123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408 |
- package crontab
- import (
- "fmt"
- "kpt-pasture/model"
- "math"
- "sort"
- "time"
- pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
- "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)
- e.UpdatePenBehaviorWeekData(pasture.Id)
- }
- return nil
- }
- // PenBehavior 栏舍行为曲线
- func (e *Entry) PenBehavior(pastureId, maxPenBehavior int64) {
- // 1. 获取颈环原始数据
- penBehaviorModelList, err := e.getNeckRingOriginalList(pastureId, maxPenBehavior)
- if err != nil {
- zaplog.Error("PenBehavior",
- zap.Any("pastureId", pastureId),
- zap.Any("maxPenBehavior", maxPenBehavior),
- zap.Any("err", err),
- )
- return
- }
- if len(penBehaviorModelList) <= 0 {
- return
- }
- // 2. 处理栏舍行为数据
- penData := e.processPenBehaviorData(penBehaviorModelList)
- // 3. 计算平均值和百分比
- e.calculateAveragesAndRates(penData)
- // 4. 保存数据
- if err = e.savePenBehaviorData(penData); err != nil {
- zaplog.Error("PenBehavior", zap.Any("penData", penData), zap.Any("err", err))
- return
- }
- sort.Slice(penBehaviorModelList, func(i, j int) bool {
- return penBehaviorModelList[i].Id > penBehaviorModelList[j].Id
- })
- if err = e.UpdateSystemNeckRingConfigure(pastureId, model.MaxPenBehavior, penBehaviorModelList[0].Id); err != nil {
- zaplog.Error("PenBehavior", zap.Any("MaxPenBehavior", err), zap.Any("penBehaviorModelList", penBehaviorModelList))
- }
- }
- // getNeckRingOriginalList 获取颈环原始数据
- func (e *Entry) getNeckRingOriginalList(pastureId, maxPenBehavior int64) ([]*model.PenBehaviorModel, error) {
- var penBehaviorModelList []*model.PenBehaviorModel
- if err := e.DB.Table(fmt.Sprintf("%s as h", new(model.NeckRingOriginal).TableName())).
- Joins("INNER JOIN cow as c ON h.pasture_id = c.pasture_id AND h.neck_ring_number = c.neck_ring_number").
- Select("h.id,c.pasture_id, c.pen_id, c.pen_name, h.active_date, h.frameid, h.high, h.rumina, h.intake, h.inactive, h.gasp").
- Where("h.id > ? AND h.pasture_id = ?", maxPenBehavior, pastureId).
- Order("h.active_date,h.frameid").
- Limit(int(defaultLimit)).
- Find(&penBehaviorModelList).Error; err != nil {
- return nil, err
- }
- return penBehaviorModelList, nil
- }
- // processPenBehaviorData 处理栏舍行为数据
- func (e *Entry) processPenBehaviorData(penBehaviorModelList []*model.PenBehaviorModel) map[string]*model.PenBehaviorData {
- // 按active_date和frameid分组
- activeDateFrameIdMap := make(map[string][]*model.PenBehaviorModel)
- for _, v := range penBehaviorModelList {
- key := fmt.Sprintf("%s_%d", v.ActiveDate, v.Frameid)
- if activeDateFrameIdMap[key] == nil {
- activeDateFrameIdMap[key] = make([]*model.PenBehaviorModel, 0)
- }
- activeDateFrameIdMap[key] = append(activeDateFrameIdMap[key], v)
- }
- // 按pen_id分组统计
- penData := make(map[string]*model.PenBehaviorData)
- for _, v := range activeDateFrameIdMap {
- // 按pen_id分组
- penIdMap := make(map[int32]*model.PenBehaviorData)
- for _, item := range v {
- if data, exists := penIdMap[item.PenId]; exists {
- // 更新计数
- data.CowCount++
- // 更新平均值
- data.AvgHigh += item.High
- // 更新行为统计
- data.SumRumina += ifThenElse(item.Rumina >= 8, 1, 0)
- data.SumIntake += ifThenElse(item.Intake >= 8, 1, 0)
- data.SumRest += ifThenElse(item.Inactive >= 8, 1, 0)
- data.SumGasp += ifThenElse(item.Gasp >= 8, 1, 0)
- } else {
- penIdMap[item.PenId] = &model.PenBehaviorData{
- PastureId: item.PastureId,
- PenId: item.PenId,
- PenName: item.PenName,
- HeatDate: item.ActiveDate,
- Frameid: item.Frameid,
- CowCount: 1,
- AvgHigh: item.High,
- SumRumina: ifThenElse(item.Rumina >= 8, 1, 0),
- SumIntake: ifThenElse(item.Intake >= 8, 1, 0),
- SumRest: ifThenElse(item.Inactive >= 8, 1, 0),
- SumGasp: ifThenElse(item.Gasp >= 8, 1, 0),
- }
- }
- }
- // 将penIdMap的数据合并到penData中
- for penId, data := range penIdMap {
- key := fmt.Sprintf("%s_%d_%d", data.HeatDate, penId, data.Frameid)
- penData[key] = data
- }
- }
- return penData
- }
- // calculateAveragesAndRates 计算平均值和百分比
- func (e *Entry) calculateAveragesAndRates(penData map[string]*model.PenBehaviorData) {
- for _, data := range penData {
- // 计算平均值
- data.AvgHigh = data.AvgHigh / data.CowCount
- // 计算百分比
- if data.CowCount > 0 {
- data.RuminaRate = int32(float64(data.SumRumina) / float64(data.CowCount) * 100)
- data.IntakeRate = int32(float64(data.SumIntake) / float64(data.CowCount) * 100)
- data.RestRate = int32(float64(data.SumRest) / float64(data.CowCount) * 100)
- data.GaspRate = int32(float64(data.SumGasp) / float64(data.CowCount) * 100)
- }
- }
- }
- // savePenBehaviorData 保存栏舍行为数据
- func (e *Entry) savePenBehaviorData(penData map[string]*model.PenBehaviorData) error {
- for _, data := range penData {
- // 构建活动时间
- activeTime := e.calculateActiveTime(data.HeatDate, data.Frameid)
- // 构建保存数据
- penBehavior := model.NewPenBehavior(data, activeTime)
- if e.isExistByPenBehavior(data.PastureId, data.HeatDate, data.PenId, data.Frameid) {
- historyData := e.findPenBehavior(data.PastureId, data.HeatDate, data.PenId, data.Frameid)
- if historyData == nil || historyData.Id <= 0 {
- continue
- }
- // 计算新的总和和平均值
- newCowCount := historyData.CowCount + penBehavior.CowCount
- newAvgHigh := (historyData.AvgHigh*historyData.CowCount + penBehavior.AvgHigh*penBehavior.CowCount) / newCowCount
- newSumRumina := historyData.SumRumina + penBehavior.SumRumina
- newSumIntake := historyData.SumIntake + penBehavior.SumIntake
- newSumRest := historyData.SumRest + penBehavior.SumRest
- newSumGasp := historyData.SumGasp + penBehavior.SumGasp
- if err := e.DB.Model(new(model.PenBehavior)).
- Where("id = ?", historyData.Id).
- Updates(map[string]interface{}{
- "cow_count": newCowCount,
- "avg_high": newAvgHigh,
- "sum_rumina": newSumRumina,
- "sum_intake": newSumIntake,
- "sum_rest": newSumRest,
- "sum_gasp": newSumGasp,
- "rumina_rate": int32(float64(newSumRumina) / float64(newCowCount) * 100),
- "intake_rate": int32(float64(newSumIntake) / float64(newCowCount) * 100),
- "rest_rate": int32(float64(newSumRest) / float64(newCowCount) * 100),
- "gasp_rate": int32(float64(newSumGasp) / float64(newCowCount) * 100),
- }).Error; err != nil {
- zaplog.Error("savePenBehaviorData", zap.Any("penBehavior", penBehavior), zap.Any("err", err))
- }
- continue
- }
- if err := e.DB.Model(new(model.PenBehavior)).
- Create(penBehavior).Error; err != nil {
- zaplog.Error("savePenBehaviorData", zap.Any("penBehavior", penBehavior), zap.Any("err", err))
- }
- }
- return nil
- }
- func (e *Entry) UpdatePenBehaviorWeekData(pastureId int64) {
- penBehaviorList := e.findWeekPenBehaviorList(pastureId)
- if len(penBehaviorList) == 0 {
- return
- }
- // 按日期和frameid排序
- sort.Slice(penBehaviorList, func(i, j int) bool {
- if penBehaviorList[i].HeatDate == penBehaviorList[j].HeatDate {
- return penBehaviorList[i].Frameid < penBehaviorList[j].Frameid
- }
- return penBehaviorList[i].HeatDate < penBehaviorList[j].HeatDate
- })
- // 处理每个日期和frameid的数据
- for _, item := range penBehaviorList {
- currDate := item.HeatDate
- currFrameid := item.Frameid
- // 计算开始和结束日期
- currTime, err := time.Parse(model.LayoutDate2, currDate)
- if err != nil {
- zaplog.Error("UpdatePenBehaviorWeekData", zap.Error(err))
- continue
- }
- startTime := currTime.AddDate(0, 0, -7).Format(model.LayoutDate2)
- endTime := currTime.AddDate(0, 0, -1).Format(model.LayoutDate2)
- // 获取历史数据
- historyList := e.findHistoryPenBehaviorList(pastureId, startTime, endTime, currFrameid)
- if len(historyList) == 0 {
- // 如果没有历史数据,将所有记录标记为-1
- if err = e.DB.Model(new(model.PenBehavior)).
- Where("id = ?", item.Id).
- Updates(map[string]interface{}{
- "week_rumina_rate": -1,
- "week_intake_rate": -1,
- "week_rest_rate": -1,
- "week_gasp_rate": -1,
- "is_show": pasturePb.IsShow_Ok,
- }).Error; err != nil {
- zaplog.Error("UpdatePenBehaviorWeekData", zap.Error(err))
- }
- continue
- }
- // 按pen_id分组计算统计数据
- penStats := make(map[int32]*model.PenBehaviorWeekModel)
- for _, v := range historyList {
- if stats, exists := penStats[v.PenId]; exists {
- stats.CowCount++
- stats.SumRumina += v.RuminaRate
- stats.SumIntake += v.IntakeRate
- stats.SumRest += v.RestRate
- stats.SumGasp += v.GaspRate
- // 计算标准差
- stats.RuminaRate = append(stats.RuminaRate, float64(v.RuminaRate))
- stats.IntakeRate = append(stats.IntakeRate, float64(v.IntakeRate))
- stats.RestRate = append(stats.RestRate, float64(v.RestRate))
- stats.GaspRate = append(stats.GaspRate, float64(v.GaspRate))
- } else {
- penStats[v.PenId] = &model.PenBehaviorWeekModel{
- CowCount: 1,
- SumRumina: v.RuminaRate,
- SumIntake: v.IntakeRate,
- SumRest: v.RestRate,
- SumGasp: v.GaspRate,
- RuminaRate: []float64{float64(v.RuminaRate)},
- IntakeRate: []float64{float64(v.IntakeRate)},
- RestRate: []float64{float64(v.RestRate)},
- GaspRate: []float64{float64(v.GaspRate)},
- }
- }
- }
- // 更新当前记录
- if stats, exists := penStats[item.PenId]; exists {
- if err = e.DB.Model(new(model.PenBehavior)).
- Where("id = ?", item.Id).
- Updates(map[string]interface{}{
- "week_rumina_rate": int32(float64(stats.SumRumina) / float64(stats.CowCount)),
- "week_intake_rate": int32(float64(stats.SumIntake) / float64(stats.CowCount)),
- "week_rest_rate": int32(float64(stats.SumRest) / float64(stats.CowCount)),
- "week_gasp_rate": int32(float64(stats.SumGasp) / float64(stats.CowCount)),
- "rumina_std": int32(calculateStd(stats.RuminaRate)),
- "intake_std": int32(calculateStd(stats.IntakeRate)),
- "rest_std": int32(calculateStd(stats.RestRate)),
- "gasp_std": int32(calculateStd(stats.GaspRate)),
- "is_show": pasturePb.IsShow_Ok,
- }).Error; err != nil {
- zaplog.Error("UpdatePenBehaviorWeekData", zap.Error(err))
- }
- } else {
- // 如果没有历史数据,标记为-1
- if err = e.DB.Model(new(model.PenBehavior)).
- Where("id = ?", item.Id).
- Updates(map[string]interface{}{
- "week_rumina_rate": -1,
- "week_intake_rate": -1,
- "week_rest_rate": -1,
- "week_gasp_rate": -1,
- "is_show": pasturePb.IsShow_Ok,
- }).Error; err != nil {
- zaplog.Error("UpdatePenBehaviorWeekData", zap.Error(err))
- }
- }
- }
- }
- // findHistoryPenBehaviorList 获取历史数据
- func (e *Entry) findHistoryPenBehaviorList(pastureId int64, startTime, endTime string, frameid int32) []*model.PenBehavior {
- res := make([]*model.PenBehavior, 0)
- if err := e.DB.Model(new(model.PenBehavior)).
- Where("pasture_id = ?", pastureId).
- Where("heat_date BETWEEN ? AND ?", startTime, endTime).
- Where("frameid = ?", frameid).
- Find(&res).Error; err != nil {
- zaplog.Error("findHistoryPenBehaviorList", zap.Error(err))
- return nil
- }
- return res
- }
- // calculateStd 计算标准差
- func calculateStd(values []float64) float64 {
- if len(values) == 0 {
- return 0
- }
- // 计算平均值
- var sum float64
- for _, v := range values {
- sum += v
- }
- mean := sum / float64(len(values))
- // 计算方差
- var variance float64
- for _, v := range values {
- diff := v - mean
- variance += diff * diff
- }
- variance /= float64(len(values))
- // 返回标准差
- return math.Sqrt(variance)
- }
- // calculateActiveTime 计算活动时间
- func (e *Entry) calculateActiveTime(heatDate string, frameid int32) string {
- // 计算小时和分钟
- hour := (frameid / 10) * 2
- minute := (frameid%10)*20 - 1
- if minute < 0 {
- minute = 0
- }
- baseDate, err := time.Parse(model.LayoutDate2, heatDate)
- if err != nil {
- zaplog.Error("PenBehavior", zap.Any("calculateActiveTime", err))
- return ""
- }
- baseTime := time.Date(baseDate.Year(), baseDate.Month(), baseDate.Day(), int(hour), 0, 0, 0, baseDate.Location())
- finalTime := baseTime.Add(time.Duration(minute) * time.Minute)
- // 构建时间字符串
- return finalTime.Format(model.LayoutTime)
- }
- // isExistByPenBehavior 是否存在
- func (e *Entry) isExistByPenBehavior(pastureId int64, heatDate string, penId int32, frameid int32) bool {
- var count int64
- if err := e.DB.Model(new(model.PenBehavior)).
- Where("pasture_id = ? AND heat_date = ? AND frameid = ? AND pen_id = ?", pastureId, heatDate, frameid, penId).
- Count(&count).Error; err != nil {
- return false
- }
- return count > 0
- }
- func (e *Entry) findPenBehavior(pastureId int64, heatDate string, penId int32, frameid int32) *model.PenBehavior {
- res := &model.PenBehavior{}
- if err := e.DB.Model(new(model.PenBehavior)).
- Where("pasture_id = ? AND heat_date = ? AND frameid = ? AND pen_id = ?", pastureId, heatDate, penId, frameid).
- First(res).Error; err != nil {
- return nil
- }
- return res
- }
- func (e *Entry) findWeekPenBehaviorList(pastureId int64) []*model.PenBehavior {
- res := make([]*model.PenBehavior, 0)
- if err := e.DB.Model(new(model.PenBehavior)).
- Where("pasture_id = ?", pastureId).
- Where("is_show = ?", pasturePb.IsShow_No).
- Where("cow_count >= ?", model.PenBehaviorMinCowCount).
- Limit(int(defaultLimit)).
- Find(&res).Error; err != nil {
- return nil
- }
- return res
- }
- // ifThenElse 条件判断函数
- func ifThenElse(condition bool, a, b int32) int32 {
- if condition {
- return a
- }
- return b
- }
|