neck_ring_habit.go 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622
  1. package mqtt
  2. import (
  3. "fmt"
  4. "kpt-pasture/model"
  5. "kpt-pasture/module/crontab"
  6. "math"
  7. "time"
  8. pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
  9. "gorm.io/gorm"
  10. "gitee.com/xuyiping_admin/pkg/logger/zaplog"
  11. "go.uber.org/zap"
  12. "gitee.com/xuyiping_admin/pkg/xerr"
  13. )
  14. const (
  15. MinChangeFilter = -99
  16. MinRuminaFilter = -99
  17. MinChewFilter = -99
  18. MinChangeHigh = -99
  19. DefaultNb = 30
  20. DefaultScore = 100
  21. )
  22. func (e *Entry) PastureUpdateActiveHabit() {
  23. pastureList := e.FindPastureList()
  24. if pastureList == nil || len(pastureList) == 0 {
  25. return
  26. }
  27. for _, pasture := range pastureList {
  28. if err := e.EntryUpdateActiveHabit(pasture.Id); err != nil {
  29. zaplog.Error("PastureUpdateActiveHabit", zap.Any("PastureUpdateActiveHabit", err), zap.Any("pasture", pasture))
  30. }
  31. zaplog.Info(fmt.Sprintf("PastureUpdateActiveHabit Success %d", pasture.Id))
  32. }
  33. }
  34. func (e *Entry) EntryUpdateActiveHabit(pastureId int64) (err error) {
  35. lastMaxHabitData, err := e.GetSystemConfigure2(pastureId, model.MaxHabit)
  36. if err != nil {
  37. return xerr.WithStack(err)
  38. }
  39. lastMaxHabitId := lastMaxHabitData.Value
  40. currMaxHabit := &model.NeckActiveHabit{}
  41. if err = e.DB.Model(new(model.NeckActiveHabit)).
  42. Where("id > ?", lastMaxHabitId).
  43. Where("pasture_id = ?", pastureId).
  44. Where(e.DB.Where("record_count = ?", model.AvgHours).Or("is_show = ?", pasturePb.IsShow_Ok)).
  45. Order("id desc").First(currMaxHabit).Error; err != nil {
  46. return xerr.WithStack(err)
  47. }
  48. if currMaxHabit.Id <= 0 || currMaxHabit.Id <= lastMaxHabitId {
  49. return nil
  50. }
  51. defer func() {
  52. // 更新最后一次执行的id值
  53. if err == nil {
  54. e.DB.Model(new(model.SystemConfigure)).
  55. Where("name = ?", model.MaxHabit).
  56. Where("pasture_id = ?", pastureId).
  57. Update("value", currMaxHabit.Id)
  58. }
  59. }()
  60. // 获取这段执行数据内最大日期和最小日期
  61. xToday := &XToday{}
  62. activeLowest, err := e.GetSystemConfigure2(pastureId, model.ActiveLowest)
  63. if err != nil {
  64. return xerr.WithStack(err)
  65. }
  66. ruminaLowest, err := e.GetSystemConfigure2(pastureId, model.RuminaLowest)
  67. if err != nil {
  68. return xerr.WithStack(err)
  69. }
  70. xRuminaDisc, err := e.GetSystemConfigure2(pastureId, model.XRuminaDisc)
  71. if err != nil {
  72. return err
  73. }
  74. xChangeDiscount, err := e.GetSystemConfigure2(pastureId, model.XChangeDiscount)
  75. if err != nil {
  76. return err
  77. }
  78. xToday.ActiveLowest = int32(activeLowest.Value)
  79. xToday.RuminaLowest = int32(ruminaLowest.Value)
  80. xToday.XRuminaDisc = int32(xRuminaDisc.Value)
  81. xToday.XChangeDiscount = int32(xChangeDiscount.Value)
  82. xToday.LastMaxHabitId = lastMaxHabitId // 上次执行的id
  83. xToday.CurrMaxHabitId = currMaxHabit.Id // 本次执行的id
  84. // 更新活动滤波
  85. if err = e.FirstFilterUpdate(pastureId, xToday); err != nil {
  86. zaplog.Error("EntryUpdateActiveHabit", zap.Any("FirstFilterUpdate", err), zap.Any("xToday", xToday))
  87. }
  88. // 更新周平均值
  89. if err = e.WeeklyActiveAvgUpdate(pastureId, xToday); err != nil {
  90. zaplog.Error("EntryUpdateActiveHabit", zap.Any("WeeklyActiveAvgUpdate", err), zap.Any("xToday", xToday))
  91. }
  92. // 二次更新滤波
  93. if err = e.SecondUpdateChangeFilter(pastureId, xToday); err != nil {
  94. zaplog.Error("EntryUpdateActiveHabit", zap.Any("SecondUpdateChangeFilter", err), zap.Any("xToday", xToday))
  95. }
  96. // 活动量校正系数和健康评分
  97. if err = e.FilterCorrectAndScoreUpdate(pastureId, xToday); err != nil {
  98. zaplog.Error("EntryUpdateActiveHabit", zap.Any("ActivityVolumeChanges", err), zap.Any("xToday", xToday))
  99. }
  100. if err = e.DB.Model(new(model.NeckActiveHabit)).
  101. Where("pasture_id = ?", pastureId).
  102. Where("id BETWEEN ? AND ?", xToday.LastMaxHabitId, xToday.CurrMaxHabitId).
  103. Where("change_filter = ?", model.InitChangeFilter).
  104. Updates(map[string]interface{}{
  105. "change_filter": model.DefaultChangeFilter,
  106. "rumina_filter": model.DefaultRuminaFilter,
  107. "chew_filter": model.DefaultChewFilter,
  108. }).Error; err != nil {
  109. zaplog.Error("EntryUpdateActiveHabit", zap.Any("change_filter", err), zap.Any("xToday", xToday))
  110. }
  111. if err = e.DB.Model(new(model.NeckActiveHabit)).
  112. Where("pasture_id = ?", pastureId).
  113. Where("id BETWEEN ? AND ?", xToday.LastMaxHabitId, xToday.CurrMaxHabitId).
  114. Where("change_filter < ?", 0).
  115. Where("filter_correct < ?", model.DefaultFilterCorrect).
  116. Updates(map[string]interface{}{
  117. "filter_correct": model.DefaultFilterCorrect,
  118. }).Error; err != nil {
  119. zaplog.Error("EntryUpdateActiveHabit", zap.Any("filter_correct", err), zap.Any("xToday", xToday))
  120. }
  121. // 插入群体校正表
  122. if err = e.UpdateChangeAdJust(pastureId, xToday); err != nil {
  123. zaplog.Error("EntryUpdateActiveHabit", zap.Any("UpdateChangeAdJust", err), zap.Any("xToday", xToday))
  124. }
  125. return nil
  126. }
  127. // FirstFilterUpdate 首次更新活动滤波
  128. func (e *Entry) FirstFilterUpdate(pastureId int64, xToDay *XToday) error {
  129. newNeckActiveHabitList := make([]*model.NeckActiveHabit, 0)
  130. if err := e.DB.Model(new(model.NeckActiveHabit)).
  131. Where("id BETWEEN ? AND ?", xToDay.LastMaxHabitId, xToDay.CurrMaxHabitId).
  132. Where("pasture_id = ?", pastureId).
  133. Where("change_filter = ?", model.InitChangeFilter).
  134. Where(e.DB.Where("high >= ?", xToDay.ActiveLowest).Or("rumina >= ?", xToDay.RuminaLowest)). // 活动量过低牛只不参与计算
  135. Order("neck_ring_number,heat_date,frameid").
  136. Limit(int(defaultLimit)).Find(&newNeckActiveHabitList).Error; err != nil {
  137. return xerr.WithStack(err)
  138. }
  139. // 活动量滤波
  140. for _, v := range newNeckActiveHabitList {
  141. firstFilterData := &FirstFilterData{}
  142. frameId := v.Frameid
  143. heatDate := v.HeatDate
  144. if v.Frameid == 0 {
  145. frameId = 11
  146. heatDateParse, _ := time.Parse(model.LayoutDate2, heatDate)
  147. heatDate = heatDateParse.AddDate(0, 0, -1).Format(model.LayoutDate2)
  148. }
  149. if err := e.DB.Model(new(model.NeckActiveHabit)).
  150. Select("neck_ring_number", "filter_high", "filter_rumina", "filter_chew").
  151. Where("neck_ring_number = ?", v.NeckRingNumber).
  152. Where("heat_date = ?", heatDate).
  153. Where("frameid = ?", frameId).
  154. First(&firstFilterData).Error; err != nil {
  155. zaplog.Error("EntryUpdateActiveHabit", zap.Any("FirstFilterUpdate", err))
  156. }
  157. if v.FilterHigh > 0 {
  158. firstFilterData.FilterHigh = v.FilterHigh
  159. } else {
  160. if v.NeckRingNumber == firstFilterData.NeckRingNumber {
  161. firstFilterData.FilterHigh = int32(computeIfPositiveElse(float64(v.High), float64(firstFilterData.FilterHigh), 0.23, 0.77))
  162. } else {
  163. firstFilterData.FilterHigh = v.High
  164. }
  165. }
  166. if v.FilterRumina > 0 {
  167. firstFilterData.FilterRumina = v.FilterRumina
  168. } else {
  169. if v.NeckRingNumber == firstFilterData.NeckRingNumber {
  170. firstFilterData.FilterRumina = int32(computeIfPositiveElse(float64(v.Rumina), float64(firstFilterData.FilterRumina), 0.33, 0.67))
  171. } else {
  172. firstFilterData.FilterRumina = v.Rumina
  173. }
  174. }
  175. if v.FilterChew > 0 {
  176. firstFilterData.FilterChew = v.FilterChew
  177. } else {
  178. if v.NeckRingNumber == firstFilterData.NeckRingNumber {
  179. firstFilterData.FilterChew = int32(computeIfPositiveElse(float64(v.Rumina+v.Intake), float64(firstFilterData.FilterChew), 0.33, 0.67))
  180. } else {
  181. firstFilterData.FilterChew = v.Rumina + v.Intake
  182. }
  183. }
  184. // 更新过滤值
  185. if err := e.DB.Model(new(model.NeckActiveHabit)).
  186. Select("filter_high", "filter_rumina", "filter_chew").
  187. Where("id = ?", v.Id).
  188. Updates(map[string]interface{}{
  189. "filter_high": firstFilterData.FilterHigh,
  190. "filter_rumina": firstFilterData.FilterRumina,
  191. "filter_chew": firstFilterData.FilterChew,
  192. }).Error; err != nil {
  193. return xerr.WithStack(err)
  194. }
  195. }
  196. zaplog.Info("EntryUpdateActiveHabit-FilterUpdate-Success")
  197. return nil
  198. }
  199. // WeeklyActiveAvgUpdate 更新周平均值
  200. func (e *Entry) WeeklyActiveAvgUpdate(pastureId int64, xToday *XToday) (err error) {
  201. weeklyActive, err := e.GetSystemConfigure2(pastureId, model.WeeklyActive)
  202. if err != nil {
  203. return xerr.WithStack(err)
  204. }
  205. if err = e.DB.Transaction(func(tx *gorm.DB) error {
  206. neckActiveHabitList := make([]*model.NeckActiveHabit, 0)
  207. if err = tx.Model(new(model.NeckActiveHabit)).
  208. Where("id BETWEEN ? AND ?", xToday.LastMaxHabitId, xToday.CurrMaxHabitId).
  209. Where("pasture_id = ?", pastureId).
  210. Where("change_filter = ?", model.InitChangeFilter).
  211. Where(e.DB.Where("high > ?", xToday.ActiveLowest).Or("rumina > ?", xToday.RuminaLowest)).
  212. Order("neck_ring_number,frameid").
  213. Limit(int(defaultLimit)).Find(&neckActiveHabitList).Error; err != nil {
  214. return xerr.WithStack(err)
  215. }
  216. zaplog.Info("WeeklyActiveAvgUpdate-0", zap.Any("neckActiveHabitList", neckActiveHabitList))
  217. for _, v := range neckActiveHabitList {
  218. beginDayDate, _ := time.Parse(model.LayoutDate2, v.HeatDate)
  219. before7DayDate := beginDayDate.AddDate(0, 0, -7).Format(model.LayoutDate2)
  220. before1DayDate := beginDayDate.AddDate(0, 0, -1).Format(model.LayoutDate2)
  221. weekHabitData := &crontab.WeekHabit{}
  222. if err = tx.Model(new(model.NeckActiveHabit)).
  223. Select("neck_ring_number").
  224. Select("IF(COUNT(1)>=3, ROUND((SUM(filter_high) -MIN(filter_high) -MAX(filter_high))/ABS(COUNT(1) -2),0), -1) as avg_high_habit").
  225. Select("IF(COUNT(1)>=3, ROUND((SUM(filter_rumina) -MIN(filter_rumina) -MAX(filter_rumina))/ABS(COUNT(1) -2),0), -1) as avg_rumina_habit").
  226. Select("IF(COUNT(1)>=3, ROUND((SUM(filter_chew) -MIN(filter_chew) -MAX(filter_chew))/ABS(COUNT(1) -2),0), -1) as avg_chew_habit").
  227. Select("ROUND(AVG(intake),0) as avg_intake_habit").
  228. Select("ROUND(AVG(inactive),0) as avg_inactive_habit").
  229. Where("heat_date BETWEEN ? AND ?", before7DayDate, before1DayDate).
  230. Where("pasture_id = ?", pastureId).
  231. Where("neck_ring_number = ? AND frameid = ?", v.NeckRingNumber, v.Frameid).
  232. Where(e.DB.Where("high > ?", xToday.ActiveLowest).Or("rumina > ?", xToday.RuminaLowest)).
  233. Group("neck_ring_number").First(weekHabitData).Error; err != nil {
  234. zaplog.Error("WeeklyActiveAvgUpdate-1", zap.Any("error", err), zap.Any("xToday", xToday))
  235. }
  236. // 累计24小时数值
  237. sumHabitData := &SumHabit{}
  238. if err = tx.Model(new(model.NeckActiveHabit)).
  239. Select("neck_ring_number").
  240. Select("IF(COUNT(1)>6, ROUND(AVG( h2.filter_rumina)*12,0), 0) as sum_rumina").
  241. Select("IF(COUNT(1)>6, ROUND(AVG( h2.intake)*12,0), 0) as sum_intake").
  242. Select("IF(COUNT(1)>6, ROUND(AVG( h2.inactive)*12,0), 0) as sum_inactive").
  243. Select("IF(COUNT(1)>6, ROUND(AVG( h2.active)*12,0), 0) as sum_active").
  244. Select("MAX(h2.change_filter) as sum_max_high").
  245. Select("MIN(IF(change_filter > ?, change_filter, ?)) as sum_min_high", model.DefaultChangeFilter, model.InitChangeFilter).
  246. Select("MIN( CASE WHEN filter_chew > ? THEN filter_chew WHEN filter_rumina >= ? THEN filter_rumina ELSE 0 END) as sum_min_chew", model.DefaultChangeFilter, model.DefaultRuminaFilter).
  247. Where("pasture_id = ?", pastureId).
  248. Where("heat_date BETWEEN ? AND ?", before1DayDate, beginDayDate).
  249. Where("active_time BETWEEN ? AND ?", beginDayDate.Add(-23*time.Hour).Format(model.LayoutTime), beginDayDate.Format(model.LayoutTime)).
  250. Where(e.DB.Where("high > ?", xToday.ActiveLowest).Or("rumina >= ?", xToday.RuminaLowest)).
  251. Where("neck_ring_number = ? AND frameid = ?", v.NeckRingNumber, v.Frameid).
  252. Group("neck_ring_number").
  253. First(sumHabitData).Error; err != nil {
  254. zaplog.Error("WeeklyActiveAvgUpdate-2", zap.Any("error", err), zap.Any("xToday", xToday))
  255. }
  256. // 变化百分比
  257. if v.WeekHighHabit > 0 {
  258. if v.FilterHigh-v.WeekHighHabit > 0 {
  259. v.ChangeHigh = (v.FilterHigh - v.WeekHighHabit) / int32(float64(v.WeekHigh)*0.6+float64(v.WeekHighHabit)*0.2+float64(weeklyActive.Value)*0.2)
  260. } else {
  261. v.ChangeHigh = v.FilterHigh - v.WeekHighHabit/v.WeekHighHabit*100
  262. }
  263. v.ChangeRumina = v.RuminaFilter - v.WeekRuminaHabit/v.WeekHighHabit*100
  264. v.ChangeChew = v.FilterChew - v.WeekChewHabit/v.WeekHighHabit*100
  265. if err = e.DB.Model(new(model.NeckActiveHabit)).
  266. Select("change_high", "change_rumina", "change_chew").
  267. Where("id = ?", v.Id).
  268. Updates(v).Error; err != nil {
  269. return xerr.WithStack(err)
  270. }
  271. }
  272. // 三天前的反刍和采食
  273. before3DaysNeckActiveHabit := &model.NeckActiveHabit{}
  274. before3DayDate := beginDayDate.AddDate(0, 0, -3).Format(model.LayoutDate2)
  275. if err = e.DB.Model(new(model.NeckActiveHabit)).
  276. Select("sum_rumina", "sum_intake").
  277. Where("pasture_id = ?", pastureId).
  278. Where("neck_ring_number = ?", v.NeckRingNumber).
  279. Where("heat_date = ?", before3DayDate).
  280. Where("frameid = ? ", v.Frameid).
  281. First(before3DaysNeckActiveHabit).Error; err != nil {
  282. zaplog.Error("WeeklyActiveAvgUpdate-3", zap.Any("error", err), zap.Any("xToday", xToday))
  283. }
  284. if err = tx.Model(new(model.NeckActiveHabit)).
  285. Select("avg_high_habit", "avg_rumina_habit", "avg_chew_habit", "avg_intake_habit", "avg_inactive_habit").
  286. Select("sum_rumina", "sum_intake", "sum_inactive", "sum_active", "sum_max_high", "sum_min_high", "sum_min_chew").
  287. Select("change_high", "change_rumina", "change_chew", "sum_rumina_before_three_day", "sum_intake_before_three_day").
  288. Where("id = ?", v.Id).
  289. Updates(map[string]interface{}{
  290. "week_high_habit": weekHabitData.AvgHighHabit,
  291. "week_rumina_habit": weekHabitData.AvgRuminaHabit,
  292. "week_chew_habit": weekHabitData.AvgChewHabit,
  293. "week_intake_habit": weekHabitData.AvgIntakeHabit,
  294. "week_inactive_habit": weekHabitData.AvgIntakeHabit,
  295. "sum_rumina": sumHabitData.SumRumina,
  296. "sum_intake": sumHabitData.SumIntake,
  297. "sum_inactive": sumHabitData.SumInactive,
  298. "sum_active": sumHabitData.SumActive,
  299. "sum_max_high": sumHabitData.SumMaxHigh,
  300. "sum_min_high": sumHabitData.SumMinHigh,
  301. "sum_min_chew": sumHabitData.SumMinChew,
  302. "change_high": v.ChangeHigh,
  303. "change_rumina": v.ChangeRumina,
  304. "change_chew": v.ChangeChew,
  305. "sum_rumina_before_three_day": before3DaysNeckActiveHabit.SumRumina,
  306. "sum_intake_before_three_day": before3DaysNeckActiveHabit.SumIntake,
  307. }).Error; err != nil {
  308. zaplog.Error("WeeklyActiveAvgUpdate-6", zap.Any("error", err), zap.Any("xToday", xToday))
  309. }
  310. zaplog.Info("WeeklyActiveAvgUpdate-7",
  311. zap.Any("v", v),
  312. zap.Any("xToday", xToday),
  313. zap.Any("before3DaysNeckActiveHabit", before3DaysNeckActiveHabit),
  314. zap.Any("sumHabitData", sumHabitData),
  315. zap.Any("weekHabitData", weekHabitData),
  316. )
  317. }
  318. return nil
  319. }); err != nil {
  320. return xerr.WithStack(err)
  321. }
  322. zaplog.Info("WeeklyActiveAvgUpdate-Success", zap.Any("xToday", xToday))
  323. return nil
  324. }
  325. // SecondUpdateChangeFilter 第二次更新变化趋势滤波
  326. func (e *Entry) SecondUpdateChangeFilter(pastureId int64, xToday *XToday) (err error) {
  327. newChangeFilterList := make([]*ChangeFilterData, 0)
  328. if err = e.DB.Model(new(model.NeckActiveHabit)).
  329. Select("id,neck_ring_number,change_high,change_filter,rumina_filter,change_rumina,chew_filter,change_chew,heat_date,frameid").
  330. Select("IF(lact = 0, 0.8, 1) as xlc_dis_count").
  331. Where("id BETWEEN ? AND ?", xToday.XMin2Id, xToday.CurrMaxHabitId).
  332. Where("pasture_id = ?", pastureId).
  333. Where("change_filter = ?", model.InitChangeFilter).
  334. Where("change_high > ?", MinChangeHigh).
  335. Order("neck_ring_number,heat_date,frameid").
  336. Find(&newChangeFilterList).Error; err != nil {
  337. return xerr.WithStack(err)
  338. }
  339. for _, v := range newChangeFilterList {
  340. secondFilterData := &SecondFilterData{}
  341. frameId := v.FrameId
  342. heatDate := v.HeatDate
  343. if v.FrameId == 0 {
  344. frameId = 11
  345. heatDateParse, _ := time.Parse(model.LayoutDate2, heatDate)
  346. heatDate = heatDateParse.AddDate(0, 0, -1).Format(model.LayoutDate2)
  347. }
  348. if err = e.DB.Model(new(model.NeckActiveHabit)).
  349. Select("neck_ring_number", "filter_high", "filter_rumina", "filter_chew").
  350. Where("neck_ring_number = ?", v.NeckRingNumber).
  351. Where("heat_date = ?", heatDate).
  352. Where("frameid = ?", frameId).
  353. First(&secondFilterData).Error; err != nil {
  354. zaplog.Error("EntryUpdateActiveHabit", zap.Any("FirstFilterUpdate", err))
  355. }
  356. if v.ChangeFilter > MinChangeFilter {
  357. secondFilterData.ChangeFilter = float64(v.ChangeFilter)
  358. } else {
  359. if v.NeckRingNumber == secondFilterData.NeckRingNumber {
  360. secondFilterData.ChangeFilter = secondFilterData.ChangeFilter*(1-(float64(xToday.XChangeDiscount)/10)*v.XlcDisCount) +
  361. math.Min(float64(v.ChangeHigh), secondFilterData.ChangeFilter+135)*(float64(xToday.XChangeDiscount)/10)*v.XlcDisCount
  362. } else {
  363. secondFilterData.ChangeFilter = 0
  364. }
  365. }
  366. if v.RuminaFilter > MinRuminaFilter {
  367. secondFilterData.RuminaFilter = float64(v.ChangeFilter)
  368. } else {
  369. if v.NeckRingNumber == secondFilterData.NeckRingNumber {
  370. discount := float64(xToday.XRuminaDisc) / 10 * v.XlcDisCount
  371. if math.Abs(float64(v.ChangeRumina)) > 60 {
  372. discount *= 0.5
  373. }
  374. secondFilterData.RuminaFilter = secondFilterData.RuminaFilter*(1-discount) + float64(v.ChangeRumina)*discount
  375. } else {
  376. secondFilterData.RuminaFilter = 0
  377. }
  378. }
  379. secondFilterData.RuminaFilter = math.Min(50, secondFilterData.RuminaFilter)
  380. if v.ChewFilter > MinChewFilter {
  381. secondFilterData.ChewFilter = float64(v.ChangeChew)
  382. } else {
  383. if v.NeckRingNumber == secondFilterData.NeckRingNumber {
  384. discount := float64(xToday.XRuminaDisc) / 10
  385. if math.Abs(float64(v.ChangeChew)) > 60 {
  386. discount *= 0.5
  387. }
  388. secondFilterData.ChewFilter = secondFilterData.ChewFilter*(1-discount) + float64(v.ChangeChew)*discount
  389. } else {
  390. secondFilterData.ChewFilter = 0
  391. }
  392. }
  393. secondFilterData.ChewFilter = math.Min(50, secondFilterData.ChewFilter)
  394. if err = e.DB.Model(new(model.NeckActiveHabit)).
  395. Select("change_filter", "rumina_filter", "chew_filter").
  396. Where("id = ?", v.Id).
  397. Where("neck_ring_number = ?", v.NeckRingNumber).
  398. Where("change_filter = ?", model.InitChangeFilter).
  399. Updates(map[string]interface{}{
  400. "change_filter": secondFilterData.ChangeFilter,
  401. "rumina_filter": secondFilterData.RuminaFilter,
  402. "chew_filter": secondFilterData.ChewFilter,
  403. }).Error; err != nil {
  404. zaplog.Error("SecondUpdateChangeFilter-1", zap.Any("error", err), zap.Any("xToday", xToday))
  405. }
  406. zaplog.Info("SecondUpdateChangeFilter",
  407. zap.Any("secondFilterData", secondFilterData),
  408. zap.Any("xToday", xToday),
  409. zap.Any("v", v),
  410. )
  411. }
  412. return nil
  413. }
  414. // FilterCorrectAndScoreUpdate 计算活动量变化趋势校正值(活跃度校正)和健康评分
  415. func (e *Entry) FilterCorrectAndScoreUpdate(pastureId int64, xToday *XToday) error {
  416. beginDayDate := time.Now()
  417. before7DayDate := beginDayDate.AddDate(0, 0, -7).Format(model.LayoutDate2)
  418. before1DayDate := beginDayDate.AddDate(0, 0, -1).Format(model.LayoutDate2)
  419. activityVolumeList := make([]*crontab.ActivityVolume, 0)
  420. activityVolumeMap := make(map[string]*crontab.ActivityVolume)
  421. if err := e.DB.Model(new(model.NeckActiveHabit)).
  422. Select("neck_ring_number").
  423. Select("AVG(IF(change_filter>=60, 60, change_filter)) as avg_filter").
  424. Select("ROUND(STD(IF(change_filter>=60, 60, change_filter))) as std_filter").
  425. Select("COUNT(1) as nb").
  426. Where("heat_date BETWEEN ? AND ?", before7DayDate, before1DayDate).
  427. Where("pasture_id = ?", pastureId).
  428. Where(e.DB.Where("high > ?", xToday.ActiveLowest).Or("rumina >= ?", xToday.RuminaLowest)).
  429. Where("active_time <= ?", beginDayDate.Add(-12*time.Hour).Format(model.LayoutTime)).
  430. Where("change_filter > ?", MinChangeFilter).
  431. Having("nb > ?", DefaultNb).
  432. Group("neck_ring_number").
  433. Find(&activityVolumeList).Error; err != nil {
  434. zaplog.Error("ActivityVolumeChanges-0", zap.Any("error", err), zap.Any("xToday", xToday))
  435. }
  436. if len(activityVolumeList) > 0 {
  437. for _, v := range activityVolumeList {
  438. activityVolumeMap[v.NeckRingNumber] = v
  439. }
  440. }
  441. neckActiveHabitList := make([]*model.NeckActiveHabit, 0)
  442. if err := e.DB.Model(new(model.NeckActiveHabit)).
  443. Where("id BETWEEN ? AND ?", xToday.XMin2Id, xToday.CurrMaxHabitId).
  444. Where("pasture_id = ?", pastureId).
  445. Where(e.DB.Where("high > ?", xToday.ActiveLowest).Or("rumina > ?", xToday.RuminaLowest)).
  446. Find(&neckActiveHabitList).Error; err != nil {
  447. zaplog.Error("ActivityVolumeChanges-1", zap.Any("error", err), zap.Any("xToday", xToday))
  448. return xerr.WithStack(err)
  449. }
  450. for _, v := range neckActiveHabitList {
  451. if filterCorrectMap, ok := activityVolumeMap[v.NeckRingNumber]; ok {
  452. filterCorrect := model.DefaultFilterCorrect - int(math.Floor(float64(filterCorrectMap.AvgFilter)/3+float64(filterCorrectMap.StdFilter)/2))
  453. // 活动量校正系数
  454. if err := e.DB.Model(new(model.NeckActiveHabit)).
  455. Where("id = ?", v.Id).
  456. Where("neck_ring_number = ?", v.NeckRingNumber).
  457. Update("filter_correct", filterCorrect).Error; err != nil {
  458. zaplog.Error("ActivityVolumeChanges-2", zap.Any("error", err), zap.Any("xToday", xToday))
  459. continue
  460. }
  461. }
  462. // 健康评分
  463. if v.Score != 0 {
  464. continue
  465. }
  466. cowScore := calculateScore(v)
  467. if err := e.DB.Model(new(model.NeckActiveHabit)).
  468. Where("id = ?", v.Id).
  469. Where("neck_ring_number = ?", v.NeckRingNumber).
  470. Update("score", cowScore).Error; err != nil {
  471. zaplog.Error("ActivityVolumeChanges-2", zap.Any("error", err), zap.Any("xToday", xToday))
  472. continue
  473. }
  474. }
  475. zaplog.Info("EntryUpdateActiveHabit-ActivityVolumeChanges-Success")
  476. return nil
  477. }
  478. // UpdateChangeAdJust 更新群体校正数据
  479. func (e *Entry) UpdateChangeAdJust(pastureId int64, xToday *XToday) error {
  480. res := make([]*model.NeckRingBarChange, 0)
  481. oneDayAgo := time.Now().AddDate(0, 0, -1).Format(model.LayoutDate2)
  482. if err := e.DB.Table(fmt.Sprintf("%s as h", new(model.NeckActiveHabit).TableName())).
  483. Select("h.neck_ring_number,h.heat_date, h.frameid, c.pen_id, c.pen_name, COUNT(*) as nb, ROUND(AVG(h.change_high)) as change_high, ROUND(AVG(h.change_filter)) as change_filter").
  484. Joins("JOIN cow as c ON h.neck_ring_number = c.neck_ring_number").
  485. Where("h.pasture_id = ?", pastureId).
  486. Where("h.heat_date >= ?", oneDayAgo).
  487. Where("h.cow_id >= ?", 0).
  488. Group("h.heat_date, h.frameid, c.pen_id").
  489. Order("h.heat_date, h.frameid, c.pen_name").
  490. Find(&res).Error; err != nil {
  491. return xerr.WithStack(err)
  492. }
  493. for _, v := range res {
  494. if err := e.DB.Model(new(model.NeckActiveHabit)).
  495. Where("id > ?", xToday.LastMaxHabitId).
  496. Where("heat_date = ?", v.HeatDate).
  497. Where("heat_date >= ?", oneDayAgo).
  498. Where("frameid = ?", v.FrameId).
  499. Where("neck_ring_number = ?", v.NeckRingNumber).
  500. Update("change_adjust", v.ChangeHigh).Error; err != nil {
  501. zaplog.Error("UpdateChangeAdJust-1", zap.Any("error", err), zap.Any("xToday", xToday))
  502. }
  503. }
  504. return nil
  505. }
  506. // 辅助函数来计算过滤值
  507. func computeIfPositiveElse(newValue, prevFilterValue float64, weightPrev, weightNew float64) float64 {
  508. return math.Ceil((prevFilterValue * weightPrev) + (weightNew * newValue))
  509. }
  510. // 计算 score 的逻辑
  511. func calculateScore(habit *model.NeckActiveHabit) int {
  512. // 第一部分逻辑
  513. var part1 float64
  514. switch {
  515. case (habit.CalvingAge <= 1 && habit.Lact >= 1) ||
  516. (habit.CalvingAge >= 2 && habit.CalvingAge <= 13 && (habit.SumRumina+habit.SumIntake) == 0) ||
  517. ((habit.Lact == 0 || habit.CalvingAge >= 14) && habit.ChangeFilter == -99):
  518. part1 = -199
  519. case habit.CalvingAge >= 2 && habit.CalvingAge <= 13:
  520. part1 = math.Min((float64(habit.SumRumina+habit.SumIntake)-(100+math.Min(7, float64(habit.CalvingAge))*60))/10*2, 0)
  521. case habit.ChangeFilter > -99:
  522. part1 = math.Min(0, math.Min(getValueOrDefault(float64(habit.ChangeFilter), 0), getValueOrDefault(float64(habit.SumMinHigh), 0)))*0.2 +
  523. math.Min(0, math.Min(getValueOrDefault(float64(habit.ChangeFilter), 0), getValueOrDefault(float64(habit.SumMinChew), 0)))*0.2 +
  524. getRuminaSumIntakeSumScore(float64(habit.SumRumina+habit.SumIntake)) + getAdditionalScore(habit)
  525. default:
  526. part1 = -299
  527. }
  528. // 第二部分逻辑
  529. var part2 float64
  530. switch {
  531. case habit.FirmwareVersion%100 >= 52:
  532. part2 = 1
  533. case habit.FirmwareVersion%100 >= 30 && habit.FirmwareVersion%100 <= 43:
  534. part2 = 0.8
  535. default:
  536. part2 = 0.6
  537. }
  538. // 最终 score
  539. return DefaultScore + int(math.Floor(part1*part2))
  540. }
  541. // 获取值或默认值
  542. func getValueOrDefault(value, defaultValue float64) float64 {
  543. if value > -99 {
  544. return value
  545. }
  546. return defaultValue
  547. }
  548. // 计算累计反刍得分
  549. func getRuminaSumIntakeSumScore(sum float64) float64 {
  550. switch {
  551. case sum < 80:
  552. return -30
  553. case sum < 180:
  554. return -20
  555. case sum < 280:
  556. return -10
  557. default:
  558. return 0
  559. }
  560. }
  561. // 计算额外得分
  562. func getAdditionalScore(habit *model.NeckActiveHabit) float64 {
  563. var score float64
  564. if (habit.SumRumina+habit.SumIntake < 280 || habit.SumMinHigh+habit.SumMinChew < -50) && habit.SumMaxHigh > 50 {
  565. score += 10
  566. }
  567. if habit.ChangeFilter < -30 && habit.ChangeFilter <= habit.SumMinHigh && habit.ChewFilter < -30 && habit.ChewFilter <= habit.SumMinChew {
  568. score -= 5
  569. }
  570. return score
  571. }