neck_ring_merge.go 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292
  1. package crontab
  2. import (
  3. "fmt"
  4. "kpt-pasture/model"
  5. "kpt-pasture/util"
  6. "math"
  7. "sort"
  8. "strings"
  9. "time"
  10. pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
  11. "gitee.com/xuyiping_admin/pkg/logger/zaplog"
  12. "gitee.com/xuyiping_admin/pkg/xerr"
  13. "go.uber.org/zap"
  14. )
  15. const (
  16. MinChangeFilter = -99
  17. MinRuminaFilter = -99
  18. MinChewFilter = -99
  19. MinChangeHigh = -99
  20. DefaultNb = 30
  21. DefaultScore = 100
  22. )
  23. var (
  24. defaultLimit = int32(1000)
  25. )
  26. // NeckRingOriginalMerge 把脖环数据合并成2个小时的
  27. func (e *Entry) NeckRingOriginalMerge() (err error) {
  28. if ok := e.IsExistCrontabLog(NeckRingOriginal); !ok {
  29. newTime := time.Now().Local()
  30. e.CreateCrontabLog(NeckRingOriginal)
  31. // 原始数据删除15天前的
  32. e.DB.Model(new(model.NeckRingOriginal)).
  33. Where("created_at < ?", newTime.AddDate(0, -1, 0).Unix()).
  34. Delete(new(model.NeckRingOriginal))
  35. }
  36. pastureList := e.FindPastureList()
  37. if pastureList == nil || len(pastureList) == 0 {
  38. return nil
  39. }
  40. for _, pasture := range pastureList {
  41. if err = e.OriginalMergeData(pasture.Id); err != nil {
  42. zaplog.Error("NeckRingOriginalMerge", zap.Any("OriginalMergeData", err), zap.Any("pasture", pasture))
  43. }
  44. }
  45. return nil
  46. }
  47. func (e *Entry) OriginalMergeData(pastureId int64) error {
  48. limit := e.Cfg.NeckRingLimit
  49. if limit <= 0 {
  50. limit = defaultLimit
  51. }
  52. neckRingList := make([]*model.NeckRingOriginal, 0)
  53. if err := e.DB.Model(new(model.NeckRingOriginal)).
  54. Where("is_show = ?", pasturePb.IsShow_No).
  55. Where("pasture_id = ?", pastureId).
  56. Limit(int(limit)).
  57. Find(&neckRingList).Error; err != nil {
  58. return xerr.WithStack(err)
  59. }
  60. if len(neckRingList) <= 0 {
  61. return nil
  62. }
  63. // 去重
  64. neckRingList = RemoveDuplicates(neckRingList)
  65. // 计算合并
  66. neckActiveHabitList := Recalculate(neckRingList)
  67. if len(neckActiveHabitList) <= 0 {
  68. return nil
  69. }
  70. for _, habit := range neckActiveHabitList {
  71. //更新脖环牛只相关信息 新数据直接插入
  72. historyNeckActiveHabit, ct := e.IsExistNeckActiveHabit(pastureId, habit.NeckRingNumber, habit.HeatDate, habit.Frameid)
  73. if ct <= 0 {
  74. if err := e.DB.Create(habit).Error; err != nil {
  75. zaplog.Info("NeckRingOriginalMergeData-1",
  76. zap.Any("err", err),
  77. zap.Any("neckActiveHabit", habit),
  78. )
  79. }
  80. } else {
  81. // 重新计算
  82. newNeckActiveHabit := e.againRecalculate(historyNeckActiveHabit)
  83. if newNeckActiveHabit == nil {
  84. continue
  85. }
  86. if err := e.DB.Model(new(model.NeckActiveHabit)).
  87. Select("rumina", "intake", "inactive", "gasp", "other", "high", "active", "is_show", "record_count").
  88. Where("id = ?", historyNeckActiveHabit.Id).
  89. Updates(newNeckActiveHabit).Error; err != nil {
  90. zaplog.Error("NeckRingOriginalMergeData-2",
  91. zap.Any("err", err),
  92. zap.Any("ct", ct),
  93. zap.Any("historyNeckActiveHabit", historyNeckActiveHabit),
  94. zap.Any("newNeckActiveHabit", newNeckActiveHabit),
  95. )
  96. }
  97. }
  98. if err := e.UpdateNeckRingOriginalIsShow(habit); err != nil {
  99. zaplog.Error("NeckRingOriginalMergeData-4",
  100. zap.Any("err", err),
  101. zap.Any("neckActiveHabit", habit),
  102. )
  103. }
  104. }
  105. return nil
  106. }
  107. func (e *Entry) UpdateNeckRingOriginalIsShow(habit *model.NeckActiveHabit) error {
  108. if err := e.DB.Model(new(model.NeckRingOriginal)).
  109. Where("pasture_id = ?", habit.PastureId).
  110. Where("neck_ring_number = ?", habit.NeckRingNumber).
  111. Where("active_date = ?", habit.HeatDate).
  112. Where("frameid IN (?)", util.FrameIds(habit.Frameid)).
  113. Update("is_show", pasturePb.IsShow_Ok).Error; err != nil {
  114. return xerr.WithStack(err)
  115. }
  116. return nil
  117. }
  118. // RemoveDuplicates 清洗一下数据,去掉重复的,如果有重复的,取最新的一条数据
  119. func RemoveDuplicates(records []*model.NeckRingOriginal) []*model.NeckRingOriginal {
  120. uniqueRecords := make(map[string]*model.NeckRingOriginal)
  121. // 遍历原始数组
  122. for _, record := range records {
  123. mapKey := fmt.Sprintf("%s%s%s%s%d", record.NeckRingNumber, model.JoinKey, record.ActiveDate, model.JoinKey, record.Frameid) // 0001/2023-12-04/0 0001/2023-12-03/4
  124. if existing, exists := uniqueRecords[mapKey]; exists {
  125. if record.CreatedAt > existing.CreatedAt {
  126. uniqueRecords[mapKey] = record
  127. }
  128. } else {
  129. uniqueRecords[mapKey] = record
  130. }
  131. }
  132. // 将 map 中的记录转换为切片
  133. result := make([]*model.NeckRingOriginal, 0, len(uniqueRecords))
  134. for _, record := range uniqueRecords {
  135. result = append(result, record)
  136. }
  137. return result
  138. }
  139. // Recalculate 合并计算
  140. func Recalculate(neckRingList []*model.NeckRingOriginal) []*model.NeckActiveHabit {
  141. originalMapData := make(map[string]*model.NeckRingOriginalMerge)
  142. // 合并成2个小时的
  143. for _, v := range neckRingList {
  144. xframeId := util.XFrameId(v.Frameid)
  145. mapKey := fmt.Sprintf("%s%s%s%s%d", v.NeckRingNumber, model.JoinKey, v.ActiveDate, model.JoinKey, xframeId) // 0001/2023-12-04/0 0001/2023-12-03/4
  146. if originalMapData[mapKey] == nil {
  147. originalMapData[mapKey] = new(model.NeckRingOriginalMerge)
  148. }
  149. originalMapData[mapKey].IsMageData(v, xframeId)
  150. }
  151. currTime := time.Now().Local()
  152. res := make([]*model.NeckActiveHabit, 0)
  153. // 算平均值
  154. for k, v := range originalMapData {
  155. // 过滤掉合并后<6条数据,如果时间太短就晚点再算
  156. if v.RecordCount < model.DefaultRecordCount {
  157. currMaxXframeId := util.FrameIdMapReverse[int32(currTime.Hour())]
  158. activeDateString := fmt.Sprintf("%s %02d:00:00", v.ActiveDate, v.XframeId*2+1)
  159. activeDate, _ := util.TimeParseLocal(model.LayoutTime, activeDateString)
  160. if currMaxXframeId-v.XframeId <= 1 && currTime.Add(-1*time.Hour).Unix() < activeDate.Unix() {
  161. delete(originalMapData, k)
  162. continue
  163. }
  164. }
  165. v.SumAvg()
  166. }
  167. if len(originalMapData) <= 0 {
  168. return res
  169. }
  170. res = model.NeckRingOriginalMap(originalMapData).ForMatData()
  171. sort.Sort(model.NeckActiveHabitSlice(res))
  172. return res
  173. }
  174. func (e *Entry) againRecalculate(data *model.NeckActiveHabit) *model.NeckActiveHabit {
  175. originalList := make([]*model.NeckRingOriginal, 0)
  176. frameIds := util.FrameIds(data.Frameid)
  177. sql := ""
  178. for _, frameId := range frameIds {
  179. sql += fmt.Sprintf(`SELECT * FROM neck_ring_original WHERE pasture_id = %d AND neck_ring_number = '%s' AND active_date = '%s' AND frameid = %d UNION ALL `, data.PastureId, data.NeckRingNumber, data.HeatDate, frameId)
  180. }
  181. if len(sql) > 0 {
  182. sql = strings.TrimSuffix(sql, "UNION ALL ")
  183. }
  184. if err := e.DB.Raw(sql).Find(&originalList).Error; err != nil {
  185. return nil
  186. }
  187. originalList = RemoveDuplicates(originalList)
  188. newDataList := Recalculate(originalList)
  189. if len(newDataList) != 1 {
  190. return nil
  191. }
  192. res := newDataList[0]
  193. res.IsShow = pasturePb.IsShow_No
  194. return res
  195. }
  196. // computeIfPositiveElse 辅助函数来计算过滤值
  197. func computeIfPositiveElse(newValue, prevFilterValue float64, weightPrev, weightNew float64) float64 {
  198. return math.Ceil((prevFilterValue * weightPrev) + (weightNew * newValue))
  199. }
  200. // 计算 score 的逻辑
  201. func calculateScore(habit *model.NeckActiveHabit) int {
  202. // 第一部分逻辑
  203. var part1 float64
  204. switch {
  205. case (habit.CalvingAge <= 1 && habit.Lact >= 1) ||
  206. (habit.CalvingAge >= 2 && habit.CalvingAge <= 13 && (habit.SumRumina+habit.SumIntake) == 0) ||
  207. ((habit.Lact == 0 || habit.CalvingAge >= 14) && habit.ChangeFilter == -99):
  208. part1 = -199
  209. case habit.CalvingAge >= 2 && habit.CalvingAge <= 13:
  210. part1 = math.Min((float64(habit.SumRumina+habit.SumIntake)-(100+math.Min(7, float64(habit.CalvingAge))*60))/10*2, 0)
  211. case habit.ChangeFilter > -99:
  212. part1 = math.Min(0, math.Min(getValueOrDefault(float64(habit.ChangeFilter), 0), getValueOrDefault(float64(habit.SumMinHigh), 0)))*0.2 +
  213. math.Min(0, math.Min(getValueOrDefault(float64(habit.ChangeFilter), 0), getValueOrDefault(float64(habit.SumMinChew), 0)))*0.2 +
  214. getRuminaSumIntakeSumScore(float64(habit.SumRumina+habit.SumIntake)) + getAdditionalScore(habit)
  215. default:
  216. part1 = -299
  217. }
  218. // 第二部分逻辑
  219. var part2 float64
  220. versionMod := habit.FirmwareVersion % 100
  221. if versionMod >= 52 {
  222. part2 = 1
  223. } else if versionMod >= 30 && versionMod <= 43 {
  224. part2 = 0.8
  225. } else {
  226. part2 = 0.6
  227. }
  228. // 最终 score
  229. return DefaultScore + int(math.Floor(part1*part2))
  230. }
  231. // 获取值或默认值
  232. func getValueOrDefault(value, defaultValue float64) float64 {
  233. if value > -99 {
  234. return value
  235. }
  236. return defaultValue
  237. }
  238. // 计算累计反刍得分
  239. func getRuminaSumIntakeSumScore(sum float64) float64 {
  240. switch {
  241. case sum < 80:
  242. return -30
  243. case sum < 180:
  244. return -20
  245. case sum < 280:
  246. return -10
  247. default:
  248. return 0
  249. }
  250. }
  251. // 计算额外得分
  252. func getAdditionalScore(habit *model.NeckActiveHabit) float64 {
  253. var score float64
  254. if (habit.SumRumina+habit.SumIntake < 280 || habit.SumMinHigh+habit.SumMinChew < -50) && habit.SumMaxHigh > 50 {
  255. score += 10
  256. }
  257. if habit.ChangeFilter < -30 && habit.ChangeFilter <= habit.SumMinHigh && habit.ChewFilter < -30 && habit.ChewFilter <= habit.SumMinChew {
  258. score -= 5
  259. }
  260. return score
  261. }