实现定时采集

This commit is contained in:
2025-10-26 15:10:38 +08:00
parent 5c83c19bce
commit 6a8e8f1f7d
7 changed files with 176 additions and 14 deletions

View File

@@ -11,6 +11,7 @@ import (
"git.huangwc.com/pig/pig-farm-controller/internal/app/service"
"git.huangwc.com/pig/pig-farm-controller/internal/app/webhook"
"git.huangwc.com/pig/pig-farm-controller/internal/domain/audit"
"git.huangwc.com/pig/pig-farm-controller/internal/domain/collection"
"git.huangwc.com/pig/pig-farm-controller/internal/domain/device"
domain_notify "git.huangwc.com/pig/pig-farm-controller/internal/domain/notify"
"git.huangwc.com/pig/pig-farm-controller/internal/domain/pig"
@@ -28,11 +29,12 @@ import (
// Application 是整个应用的核心,封装了所有组件和生命周期。
type Application struct {
Config *config.Config
Logger *logs.Logger
Storage database.Storage
Executor *task.Scheduler
API *api.API // 添加 API 对象
Config *config.Config
Logger *logs.Logger
Storage database.Storage
Executor *task.Scheduler
API *api.API
Collector collection.Collector
// 新增的仓库和管理器字段,以便在 initializePendingTasks 中访问
planRepo repository.PlanRepository
@@ -177,6 +179,14 @@ func NewApplication(configPath string) (*Application, error) {
cfg.Task.NumWorkers,
)
// --- 初始化定时采集器 ---
timedCollector := collection.NewTimedCollector(
deviceRepo,
generalDeviceService,
logger,
time.Duration(cfg.Collection.Interval)*time.Second,
)
// 初始化 API 服务器
apiServer := api.NewAPI(
cfg.Server,
@@ -204,6 +214,7 @@ func NewApplication(configPath string) (*Application, error) {
Storage: storage,
Executor: executor,
API: apiServer,
Collector: timedCollector,
planRepo: planRepo,
pendingTaskRepo: pendingTaskRepo,
executionLogRepo: executionLogRepo,
@@ -327,6 +338,9 @@ func (app *Application) Start() error {
// 启动任务执行器
app.Executor.Start()
// 启动定时采集器
app.Collector.Start()
// 启动 API 服务器
app.API.Start()
@@ -349,6 +363,9 @@ func (app *Application) Stop() error {
// 关闭任务执行器
app.Executor.Stop()
// 关闭定时采集器
app.Collector.Stop()
// 断开数据库连接
if err := app.Storage.Disconnect(); err != nil {
app.Logger.Errorw("数据库连接断开失败", "error", err)

View File

@@ -0,0 +1,6 @@
package collection
type Collector interface {
Start()
Stop()
}

View File

@@ -0,0 +1,89 @@
package collection
import (
"time"
"git.huangwc.com/pig/pig-farm-controller/internal/domain/device"
"git.huangwc.com/pig/pig-farm-controller/internal/infra/logs"
"git.huangwc.com/pig/pig-farm-controller/internal/infra/models"
"git.huangwc.com/pig/pig-farm-controller/internal/infra/repository"
)
// TimedCollector 实现了 Collector 接口,用于定时从数据库获取设备信息并下发采集指令
type TimedCollector struct {
deviceRepo repository.DeviceRepository
deviceService device.Service
logger *logs.Logger
interval time.Duration
ticker *time.Ticker
done chan bool
}
// NewTimedCollector 创建一个定时采集器实例
func NewTimedCollector(
deviceRepo repository.DeviceRepository,
deviceService device.Service,
logger *logs.Logger,
interval time.Duration,
) Collector {
return &TimedCollector{
deviceRepo: deviceRepo,
deviceService: deviceService,
logger: logger,
interval: interval,
done: make(chan bool),
}
}
// Start 开始定时采集
func (c *TimedCollector) Start() {
c.logger.Infof("定时采集器启动,采集间隔: %s", c.interval)
c.ticker = time.NewTicker(c.interval)
go func() {
for {
select {
case <-c.done:
return
case <-c.ticker.C:
c.collect()
}
}
}()
}
// Stop 停止定时采集
func (c *TimedCollector) Stop() {
c.logger.Info("定时采集器停止")
c.ticker.Stop()
c.done <- true
}
// collect 是核心的采集逻辑
func (c *TimedCollector) collect() {
c.logger.Info("开始新一轮的设备数据采集")
sensors, err := c.deviceRepo.ListAllSensors()
if err != nil {
c.logger.Errorf("采集周期: 从数据库获取所有传感器失败: %v", err)
return
}
if len(sensors) == 0 {
c.logger.Info("采集周期: 未发现任何传感器设备,跳过本次采集")
return
}
sensorsByController := make(map[uint][]*models.Device)
for _, sensor := range sensors {
sensorsByController[sensor.AreaControllerID] = append(sensorsByController[sensor.AreaControllerID], sensor)
}
for controllerID, controllerSensors := range sensorsByController {
c.logger.Infof("采集周期: 准备为区域主控 %d 下的 %d 个传感器下发采集指令", controllerID, len(controllerSensors))
if err := c.deviceService.Collect(controllerID, controllerSensors); err != nil {
c.logger.Errorf("采集周期: 为区域主控 %d 下发采集指令失败: %v", controllerID, err)
}
}
c.logger.Info("本轮设备数据采集完成")
}

View File

@@ -44,6 +44,9 @@ type Config struct {
// Notify 通知服务配置
Notify NotifyConfig `yaml:"notify"`
// Collection 定时采集配置
Collection CollectionConfig `yaml:"collection"`
}
// AppConfig 代表应用基础配置
@@ -195,6 +198,11 @@ type LarkConfig struct {
AppSecret string `yaml:"appSecret"`
}
// CollectionConfig 代表定时采集配置
type CollectionConfig struct {
Interval int `yaml:"interval"`
}
// NewConfig 创建并返回一个新的配置实例
func NewConfig() *Config {
// 默认值可以在这里设置,但我们优先使用配置文件中的值

View File

@@ -23,6 +23,9 @@ type DeviceRepository interface {
// ListAll 获取所有设备的列表
ListAll() ([]*models.Device, error)
// ListAllSensors 获取所有传感器类型的设备列表
ListAllSensors() ([]*models.Device, error)
// ListByAreaControllerID 根据区域主控 ID 列出所有子设备。
ListByAreaControllerID(areaControllerID uint) ([]*models.Device, error)
@@ -84,6 +87,19 @@ func (r *gormDeviceRepository) ListAll() ([]*models.Device, error) {
return devices, nil
}
// ListAllSensors 检索归类为传感器的所有设备
func (r *gormDeviceRepository) ListAllSensors() ([]*models.Device, error) {
var sensors []*models.Device
err := r.db.Preload("AreaController").Preload("DeviceTemplate").
Joins("JOIN device_templates ON device_templates.id = devices.device_template_id").
Where("device_templates.category = ?", models.CategorySensor).
Find(&sensors).Error
if err != nil {
return nil, fmt.Errorf("查询所有传感器失败: %w", err)
}
return sensors, nil
}
// ListByAreaControllerID 根据区域主控 ID 列出所有子设备
func (r *gormDeviceRepository) ListByAreaControllerID(areaControllerID uint) ([]*models.Device, error) {
var devices []*models.Device