Browse Source

analysis: 栏舍行为曲线

Yi 5 days ago
parent
commit
51d4424ebc

+ 2 - 0
config/app.develop.yaml

@@ -51,6 +51,8 @@ cron:
   neck_ring_calculate: "*/10 * * * * ?" # 计算脖环数据
   neck_ring_estrus_warning: "*/50 * * * * ?"   # 脖环预警(每50分钟执行一次
   neck_ring_health_warning: "*/50 * * * * ?"   # 脖环预警(每50分钟执行一次
+  update_pen_behavior: "0 45 * * * ?"  # 更新栏舍行行为数据
+
 
 mqtt:
   broker: "kptyun.com:1983"

+ 1 - 0
config/app.go

@@ -66,6 +66,7 @@ type CronSetting struct {
 	NeckRingCalculate       string `yaml:"neck_ring_calculate"`        //  脖环数据计算
 	NeckRingEstrusWarning   string `yaml:"neck_ring_estrus_warning"`   //  脖环发情预警
 	NeckRingHealthWarning   string `yaml:"neck_ring_health_warning"`   //  脖环健康预警
+	UpdatePenBehavior       string `yaml:"update_pen_behavior"`        //  栏舍行为数据
 }
 
 type JwtTokenKeyConfig struct {

+ 1 - 0
config/app.test.yaml

@@ -38,6 +38,7 @@ cron:
   neck_ring_calculate: "*/10 * * * * ?"  # 计算脖环数据
   neck_ring_estrus_warning: "*/50 * * * * ?"   # 脖环预警(每50分钟执行一次
   neck_ring_health_warning: "*/50 * * * * ?"   # 脖环预警(每50分钟执行一次
+  update_pen_behavior: "0 45 * * * ?"  # 更新栏舍行行为数据
 
 mqtt:
   broker: "kptyun.com:1983"

+ 6 - 0
dep/di_crontab.go

@@ -116,6 +116,12 @@ func EntryCrontab(dependency CrontabDependency) *cron.Crontab {
 		panic(err)
 	}
 
+	err = newCrontab.Bind("UpdatePenBehavior", cs.UpdatePenBehavior, dependency.CrontabHub.UpdatePenBehavior)
+	if err != nil {
+		zaplog.Error("EntryCrontab", zap.Any("UpdatePenBehavior", err))
+		panic(err)
+	}
+
 	/*err = newCrontab.Bind("GenerateWorkOrder", cs.GenerateWorkOrder, dependency.CrontabHub.GenerateAsynqWorkOrder)
 	if err != nil {
 		panic(err)

+ 4 - 0
http/handler/analysis/analysis.go

@@ -307,3 +307,7 @@ func MultiFactorInfantSurvivalRate(c *gin.Context) {
 
 	c.JSON(http.StatusOK, res)
 }
+
+func PenBehaviorAnalysis(c *gin.Context) {
+
+}

+ 1 - 1
http/route/analysis_api.go

@@ -26,6 +26,6 @@ func AnalysisAPI(opts ...func(engine *gin.Engine)) func(s *gin.Engine) {
 		analysisRoute.POST("/single/factor/pregnant/report", analysis.SingleFactorInfantSurvivalRate) // 单因素受胎率
 		analysisRoute.POST("/multi/factor/pregnant/report", analysis.MultiFactorInfantSurvivalRate)   // 多因素受胎率
 
-		//analysisRoute.POST("/pen/behavior", analysis.SingleFactorInfantSurvivalRate)
+		analysisRoute.POST("/pen/behavior", analysis.PenBehaviorAnalysis)
 	}
 }

+ 1 - 0
model/neck_ring_configure.go

@@ -14,6 +14,7 @@ const (
 	MaxHabit        = "max_habit"
 	HealthWarning   = "health_warning"
 	MinWeeklyActive = "min_weekly_active"
+	MaxPenBehavior  = "pen_behavior"
 )
 
 type NeckRingConfigure struct {

+ 2 - 0
module/crontab/interface.go

@@ -41,4 +41,6 @@ type Crontab interface {
 	UpdateCowEstrus() error       // 获取牛只疑似发情数据
 	NeckRingEstrusWarning() error // 发情预警
 	NeckRingHealthWarning() error // 健康预警
+
+	UpdatePenBehavior() error // 栏舍行为数据
 }

+ 0 - 3
module/crontab/neck_ring_calculate.go

@@ -82,9 +82,6 @@ func (e *Entry) EntryUpdateActiveHabit(pastureId int64) (err error) {
 
 	// 健康预警
 	e.HealthWarning(pastureId, processIds)
-
-	// 栏舍行为曲线
-	e.PenBehavior(pastureId, processIds)
 	return nil
 }
 

+ 1 - 1
module/crontab/neck_ring_merge.go

@@ -25,7 +25,7 @@ const (
 )
 
 var (
-	defaultLimit       = int32(1000)
+	defaultLimit       = int32(300)
 	calculateIsRunning bool
 )
 

+ 56 - 24
module/crontab/pen_behavior.go

@@ -3,23 +3,47 @@ package crontab
 import (
 	"fmt"
 	"kpt-pasture/model"
+	"sort"
 
 	"gitee.com/xuyiping_admin/pkg/logger/zaplog"
 	"go.uber.org/zap"
 )
 
+func (e *Entry) UpdatePenBehavior() error {
+	pastureList := e.FindPastureList()
+	if pastureList == nil || len(pastureList) == 0 {
+		return nil
+	}
+
+	for _, pasture := range pastureList {
+		conf, err := e.GetSystemNeckRingConfigure(pasture.Id, model.MaxPenBehavior)
+		if err != nil {
+			zaplog.Error("UpdatePenBehavior", zap.Any("pasture", pasture), zap.Any("err", err))
+			continue
+		}
+		e.PenBehavior(pasture.Id, conf.Value)
+	}
+	return nil
+}
+
 // PenBehavior 栏舍行为曲线
-func (e *Entry) PenBehavior(pastureId int64, processIds []int64) error {
+func (e *Entry) PenBehavior(pastureId, maxPenBehavior int64) {
 	// 1. 获取颈环原始数据
-	neckRingOriginalList, err := e.getNeckRingOriginalList(pastureId, processIds)
+	neckRingOriginalList, err := e.getNeckRingOriginalList(pastureId, maxPenBehavior)
 	if err != nil {
-		return fmt.Errorf("获取颈环原始数据失败: %w", err)
+		zaplog.Error("PenBehavior", zap.Any("pastureId", pastureId), zap.Any("maxPenBehavior", maxPenBehavior), zap.Any("err", err))
+		return
+	}
+
+	if len(neckRingOriginalList) <= 0 {
+		return
 	}
 
 	// 2. 获取牛只信息
 	cowMap, err := e.getCowMap(pastureId, neckRingOriginalList)
 	if err != nil {
-		return fmt.Errorf("获取牛只信息失败: %w", err)
+		zaplog.Error("PenBehavior", zap.Any("pastureId", pastureId), zap.Any("neckRingOriginalList", neckRingOriginalList), zap.Any("err", err))
+		return
 	}
 
 	// 3. 处理栏舍行为数据
@@ -29,19 +53,27 @@ func (e *Entry) PenBehavior(pastureId int64, processIds []int64) error {
 	e.calculateAveragesAndRates(penData)
 
 	// 5. 保存数据
-	if err := e.savePenBehaviorData(penData); err != nil {
-		return fmt.Errorf("保存栏舍行为数据失败: %w", err)
+	if err = e.savePenBehaviorData(penData); err != nil {
+		zaplog.Error("PenBehavior", zap.Any("penData", penData), zap.Any("err", err))
+		return
 	}
 
-	return nil
+	sort.Slice(neckRingOriginalList, func(i, j int) bool {
+		return neckRingOriginalList[i].Id > neckRingOriginalList[j].Id
+	})
+
+	if err = e.UpdateSystemNeckRingConfigure(pastureId, model.MaxPenBehavior, neckRingOriginalList[0].Id); err != nil {
+		zaplog.Error("PenBehavior", zap.Any("UpdateSystemNeckRingConfigure", err), zap.Any("neckRingOriginalList", neckRingOriginalList))
+	}
 }
 
 // getNeckRingOriginalList 获取颈环原始数据
-func (e *Entry) getNeckRingOriginalList(pastureId int64, processIds []int64) ([]*model.NeckRingOriginal, error) {
+func (e *Entry) getNeckRingOriginalList(pastureId, maxPenBehavior int64) ([]*model.NeckRingOriginal, error) {
 	var neckRingOriginalList []*model.NeckRingOriginal
 	if err := e.DB.Model(new(model.NeckRingOriginal)).
-		Where("id IN (?) AND pasture_id = ?", processIds, pastureId).
-		Order("heat_date,neck_ring_number,frameid").
+		Where("id > ? AND pasture_id = ?", maxPenBehavior, pastureId).
+		Order("active_date,neck_ring_number,frameid").
+		Limit(int(defaultLimit)).
 		Find(&neckRingOriginalList).Error; err != nil {
 		return nil, err
 	}
@@ -113,7 +145,7 @@ func (e *Entry) calculateAveragesAndRates(penData map[string]*model.PenBehaviorD
 	for _, data := range penData {
 		// 计算平均值
 		data.AvgHigh = data.AvgHigh / data.CowCount
-		
+
 		// 计算百分比
 		if data.CowCount > 0 {
 			data.RuminaRate = int32(float64(data.SumRumina) / float64(data.CowCount) * 100)
@@ -129,7 +161,7 @@ func (e *Entry) savePenBehaviorData(penData map[string]*model.PenBehaviorData) e
 	for _, data := range penData {
 		// 构建活动时间
 		activeTime := e.calculateActiveTime(data.HeatDate, data.Frameid)
-		
+
 		// 构建保存数据
 		penBehavior := &model.PenBehavior{
 			PastureId:  data.PastureId,
@@ -154,16 +186,16 @@ func (e *Entry) savePenBehaviorData(penData map[string]*model.PenBehaviorData) e
 			Where("pasture_id = ? AND heat_date = ? AND pen_id = ? AND active_time = ?",
 				penBehavior.PastureId, penBehavior.HeatDate, penBehavior.PenId, penBehavior.ActiveTime).
 			Assign(map[string]interface{}{
-				"cow_count":    penBehavior.CowCount,
-				"avg_high":     penBehavior.AvgHigh,
-				"sum_rumina":   penBehavior.SumRumina,
-				"sum_intake":   penBehavior.SumIntake,
-				"sum_rest":     penBehavior.SumRest,
-				"sum_gasp":     penBehavior.SumGasp,
-				"rumina_rate":  penBehavior.RuminaRate,
-				"intake_rate":  penBehavior.IntakeRate,
-				"rest_rate":    penBehavior.RestRate,
-				"gasp_rate":    penBehavior.GaspRate,
+				"cow_count":   penBehavior.CowCount,
+				"avg_high":    penBehavior.AvgHigh,
+				"sum_rumina":  penBehavior.SumRumina,
+				"sum_intake":  penBehavior.SumIntake,
+				"sum_rest":    penBehavior.SumRest,
+				"sum_gasp":    penBehavior.SumGasp,
+				"rumina_rate": penBehavior.RuminaRate,
+				"intake_rate": penBehavior.IntakeRate,
+				"rest_rate":   penBehavior.RestRate,
+				"gasp_rate":   penBehavior.GaspRate,
 			}).
 			FirstOrCreate(penBehavior).Error; err != nil {
 			return err
@@ -176,8 +208,8 @@ func (e *Entry) savePenBehaviorData(penData map[string]*model.PenBehaviorData) e
 func (e *Entry) calculateActiveTime(heatDate string, frameid int32) string {
 	// 计算小时和分钟
 	hour := (frameid / 10) * 2
-	minute := (frameid % 10) * 20 - 1
-	
+	minute := (frameid%10)*20 - 1
+
 	// 构建时间字符串
 	return fmt.Sprintf("%s %02d:%02d", heatDate, hour, minute)
 }

+ 11 - 0
module/crontab/sql.go

@@ -168,6 +168,17 @@ func (e *Entry) GetSystemNeckRingConfigure(pastureId int64, name string) (*model
 	return res, nil
 }
 
+func (e *Entry) UpdateSystemNeckRingConfigure(pastureId int64, name string, value int64) error {
+	if err := e.DB.Model(new(model.NeckRingConfigure)).
+		Where("pasture_id = ?", pastureId).
+		Where("is_show = ?", pasturePb.IsShow_Ok).
+		Where("name = ?", name).
+		Update("value", value).Error; err != nil {
+		return xerr.WithStack(err)
+	}
+	return nil
+}
+
 func (e *Entry) GetCowInfoByNeckRingNumber(pastureId int64, neckRingNumber string) *model.Cow {
 	res := &model.Cow{}
 	if err := e.DB.Model(new(model.Cow)).