pen_behavior.go 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. package crontab
  2. import (
  3. "fmt"
  4. "kpt-pasture/model"
  5. "sort"
  6. "gitee.com/xuyiping_admin/pkg/logger/zaplog"
  7. "go.uber.org/zap"
  8. )
  9. func (e *Entry) UpdatePenBehavior() error {
  10. pastureList := e.FindPastureList()
  11. if pastureList == nil || len(pastureList) == 0 {
  12. return nil
  13. }
  14. for _, pasture := range pastureList {
  15. conf, err := e.GetSystemNeckRingConfigure(pasture.Id, model.MaxPenBehavior)
  16. if err != nil {
  17. zaplog.Error("UpdatePenBehavior", zap.Any("pasture", pasture), zap.Any("err", err))
  18. continue
  19. }
  20. e.PenBehavior(pasture.Id, conf.Value)
  21. }
  22. return nil
  23. }
  24. // PenBehavior 栏舍行为曲线
  25. func (e *Entry) PenBehavior(pastureId, maxPenBehavior int64) {
  26. // 1. 获取颈环原始数据
  27. neckRingOriginalList, err := e.getNeckRingOriginalList(pastureId, maxPenBehavior)
  28. if err != nil {
  29. zaplog.Error("PenBehavior", zap.Any("pastureId", pastureId), zap.Any("maxPenBehavior", maxPenBehavior), zap.Any("err", err))
  30. return
  31. }
  32. if len(neckRingOriginalList) <= 0 {
  33. return
  34. }
  35. // 2. 获取牛只信息
  36. cowMap, err := e.getCowMap(pastureId, neckRingOriginalList)
  37. if err != nil {
  38. zaplog.Error("PenBehavior", zap.Any("pastureId", pastureId), zap.Any("neckRingOriginalList", neckRingOriginalList), zap.Any("err", err))
  39. return
  40. }
  41. // 3. 处理栏舍行为数据
  42. penData := e.processPenBehaviorData(neckRingOriginalList, cowMap)
  43. // 4. 计算平均值和百分比
  44. e.calculateAveragesAndRates(penData)
  45. // 5. 保存数据
  46. if err = e.savePenBehaviorData(penData); err != nil {
  47. zaplog.Error("PenBehavior", zap.Any("penData", penData), zap.Any("err", err))
  48. return
  49. }
  50. sort.Slice(neckRingOriginalList, func(i, j int) bool {
  51. return neckRingOriginalList[i].Id > neckRingOriginalList[j].Id
  52. })
  53. if err = e.UpdateSystemNeckRingConfigure(pastureId, model.MaxPenBehavior, neckRingOriginalList[0].Id); err != nil {
  54. zaplog.Error("PenBehavior", zap.Any("UpdateSystemNeckRingConfigure", err), zap.Any("neckRingOriginalList", neckRingOriginalList))
  55. }
  56. }
  57. // getNeckRingOriginalList 获取颈环原始数据
  58. func (e *Entry) getNeckRingOriginalList(pastureId, maxPenBehavior int64) ([]*model.NeckRingOriginal, error) {
  59. var neckRingOriginalList []*model.NeckRingOriginal
  60. if err := e.DB.Model(new(model.NeckRingOriginal)).
  61. Where("id > ? AND pasture_id = ?", maxPenBehavior, pastureId).
  62. Where("cow_id > ?", 0).
  63. Order("active_date,neck_ring_number,frameid").
  64. Limit(int(defaultLimit)).
  65. Find(&neckRingOriginalList).Error; err != nil {
  66. return nil, err
  67. }
  68. return neckRingOriginalList, nil
  69. }
  70. // getCowMap 获取牛只信息映射
  71. func (e *Entry) getCowMap(pastureId int64, neckRingOriginalList []*model.NeckRingOriginal) (map[string]*model.Cow, error) {
  72. // 提取牛只ID
  73. cowIds := make([]int64, 0, len(neckRingOriginalList))
  74. for _, v := range neckRingOriginalList {
  75. cowIds = append(cowIds, v.Id)
  76. }
  77. // 获取牛只信息
  78. cowInfoList, err := e.GetCowByIds(pastureId, cowIds)
  79. if err != nil {
  80. return nil, err
  81. }
  82. // 构建牛只信息映射
  83. cowMap := make(map[string]*model.Cow, len(cowInfoList))
  84. for _, v := range cowInfoList {
  85. if v.NeckRingNumber == "" {
  86. continue
  87. }
  88. cowMap[v.NeckRingNumber] = v
  89. }
  90. return cowMap, nil
  91. }
  92. // processPenBehaviorData 处理栏舍行为数据
  93. func (e *Entry) processPenBehaviorData(neckRingOriginalList []*model.NeckRingOriginal, cowMap map[string]*model.Cow) map[string]*model.PenBehaviorData {
  94. penData := make(map[string]*model.PenBehaviorData, len(neckRingOriginalList))
  95. for _, v := range neckRingOriginalList {
  96. cowInfo, ok := cowMap[v.NeckRingNumber]
  97. if !ok {
  98. zaplog.Error("PenBehavior", zap.Any("neckRingNumber", v.NeckRingNumber))
  99. continue
  100. }
  101. key := fmt.Sprintf("%s_%d_%d", v.ActiveDate, cowInfo.PenId, v.Frameid)
  102. if data, exists := penData[key]; exists {
  103. data.CowCount++
  104. data.AvgHigh += v.High
  105. data.SumRumina += ifThenElse(v.Rumina >= 8, 1, 0)
  106. data.SumIntake += ifThenElse(v.Intake >= 8, 1, 0)
  107. data.SumRest += ifThenElse(v.Inactive >= 8, 1, 0)
  108. data.SumGasp += ifThenElse(v.Gasp >= 8, 1, 0)
  109. } else {
  110. penData[key] = &model.PenBehaviorData{
  111. PastureId: cowInfo.PastureId,
  112. PenId: cowInfo.PenId,
  113. PenName: cowInfo.PenName,
  114. HeatDate: v.ActiveDate,
  115. Frameid: v.Frameid,
  116. CowCount: 1,
  117. AvgHigh: v.High,
  118. }
  119. }
  120. }
  121. return penData
  122. }
  123. // calculateAveragesAndRates 计算平均值和百分比
  124. func (e *Entry) calculateAveragesAndRates(penData map[string]*model.PenBehaviorData) {
  125. for _, data := range penData {
  126. // 计算平均值
  127. data.AvgHigh = data.AvgHigh / data.CowCount
  128. // 计算百分比
  129. if data.CowCount > 0 {
  130. data.RuminaRate = int32(float64(data.SumRumina) / float64(data.CowCount) * 100)
  131. data.IntakeRate = int32(float64(data.SumIntake) / float64(data.CowCount) * 100)
  132. data.RestRate = int32(float64(data.SumRest) / float64(data.CowCount) * 100)
  133. data.GaspRate = int32(float64(data.SumGasp) / float64(data.CowCount) * 100)
  134. }
  135. }
  136. }
  137. // savePenBehaviorData 保存栏舍行为数据
  138. func (e *Entry) savePenBehaviorData(penData map[string]*model.PenBehaviorData) error {
  139. for _, data := range penData {
  140. // 构建活动时间
  141. activeTime := e.calculateActiveTime(data.HeatDate, data.Frameid)
  142. // 构建保存数据
  143. penBehavior := &model.PenBehavior{
  144. PastureId: data.PastureId,
  145. HeatDate: data.HeatDate,
  146. ActiveTime: activeTime,
  147. PenId: data.PenId,
  148. PenName: data.PenName,
  149. CowCount: data.CowCount,
  150. AvgHigh: data.AvgHigh,
  151. SumRumina: data.SumRumina,
  152. SumIntake: data.SumIntake,
  153. SumRest: data.SumRest,
  154. SumGasp: data.SumGasp,
  155. RuminaRate: data.RuminaRate,
  156. IntakeRate: data.IntakeRate,
  157. RestRate: data.RestRate,
  158. GaspRate: data.GaspRate,
  159. }
  160. // 使用 Upsert 操作
  161. if err := e.DB.Model(new(model.PenBehavior)).
  162. Where("pasture_id = ? AND heat_date = ? AND pen_id = ? AND active_time = ?",
  163. penBehavior.PastureId, penBehavior.HeatDate, penBehavior.PenId, penBehavior.ActiveTime).
  164. Assign(map[string]interface{}{
  165. "cow_count": penBehavior.CowCount,
  166. "avg_high": penBehavior.AvgHigh,
  167. "sum_rumina": penBehavior.SumRumina,
  168. "sum_intake": penBehavior.SumIntake,
  169. "sum_rest": penBehavior.SumRest,
  170. "sum_gasp": penBehavior.SumGasp,
  171. "rumina_rate": penBehavior.RuminaRate,
  172. "intake_rate": penBehavior.IntakeRate,
  173. "rest_rate": penBehavior.RestRate,
  174. "gasp_rate": penBehavior.GaspRate,
  175. }).
  176. FirstOrCreate(penBehavior).Error; err != nil {
  177. return err
  178. }
  179. }
  180. return nil
  181. }
  182. // calculateActiveTime 计算活动时间
  183. func (e *Entry) calculateActiveTime(heatDate string, frameid int32) string {
  184. // 计算小时和分钟
  185. hour := (frameid / 10) * 2
  186. minute := (frameid%10)*20 - 1
  187. // 构建时间字符串
  188. return fmt.Sprintf("%s %02d:%02d", heatDate, hour, minute)
  189. }
  190. // ifThenElse 条件判断函数
  191. func ifThenElse(condition bool, a, b int32) int32 {
  192. if condition {
  193. return a
  194. }
  195. return b
  196. }