neck_ring_handle.go 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782
  1. package crontab
  2. import (
  3. "fmt"
  4. "kpt-pasture/model"
  5. "kpt-pasture/util"
  6. "math"
  7. "sort"
  8. "time"
  9. pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
  10. "gitee.com/xuyiping_admin/pkg/logger/zaplog"
  11. "gitee.com/xuyiping_admin/pkg/xerr"
  12. "go.uber.org/zap"
  13. )
  14. const (
  15. MinChangeFilter = -99
  16. MinRuminaFilter = -99
  17. MinChewFilter = -99
  18. MinChangeHigh = -99
  19. DefaultNb = 30
  20. DefaultScore = 100
  21. )
  22. var (
  23. defaultLimit = int32(1000)
  24. calculateIsRunning bool
  25. )
  26. // NeckRingOriginalMergeData 把脖环数据合并成2个小时的
  27. func (e *Entry) NeckRingOriginalMergeData() (err error) {
  28. if ok := e.IsExistCrontabLog(NeckRingOriginal); !ok {
  29. newTime := time.Now()
  30. e.CreateCrontabLog(NeckRingOriginal)
  31. // 原始数据删除15天前的
  32. e.DB.Model(new(model.NeckRingOriginal)).
  33. Where("created_at < ?", newTime.AddDate(0, 0, -7).Unix()).
  34. Delete(new(model.NeckRingOriginal))
  35. // 活动数据删除6个月前的数据
  36. e.DB.Model(new(model.NeckActiveHabit)).
  37. Where("created_at < ?", newTime.AddDate(0, -2, 0).Unix()).
  38. Delete(new(model.NeckActiveHabit))
  39. }
  40. limit := e.Cfg.NeckRingLimit
  41. if limit <= 0 {
  42. limit = defaultLimit
  43. }
  44. //createdAt := newTime.Add(-1 * time.Hour)
  45. neckRingNumber := []string{
  46. "10026", "10027", "10028", "10029", "10030",
  47. "10031", "10032", "10033", "10034", "10035",
  48. "10036", "10037", "10038", "10039", "10040",
  49. "10041", "10042", "10043", "10044", "10046",
  50. "10047", "10048", "10049", "10050", "10051",
  51. "10052", "10053", "10054", "10055", "10056",
  52. }
  53. neckRingList := make([]*model.NeckRingOriginal, 0)
  54. if err = e.DB.Model(new(model.NeckRingOriginal)).
  55. Where("is_show = ?", pasturePb.IsShow_No).
  56. Where("neck_ring_number IN (?)", neckRingNumber).
  57. //Where("created_at <= ?", createdAt.Unix()).
  58. Order("active_date,frameid,neck_ring_number").
  59. Limit(int(limit)).Find(&neckRingList).Error; err != nil {
  60. return xerr.WithStack(err)
  61. }
  62. if len(neckRingList) <= 0 {
  63. return nil
  64. }
  65. // 计算合并
  66. neckActiveHabitList := e.recalculate(neckRingList)
  67. if len(neckActiveHabitList) <= 0 {
  68. return nil
  69. }
  70. for _, habit := range neckActiveHabitList {
  71. //更新脖环牛只相关信息 新数据直接插入
  72. historyNeckActiveHabit, ct := e.IsExistNeckActiveHabit(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-3",
  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-2",
  100. zap.Any("err", err),
  101. zap.Any("neckActiveHabit", habit),
  102. )
  103. }
  104. }
  105. return nil
  106. }
  107. func (e *Entry) NeckRingCalculate() error {
  108. pastureList := e.FindPastureList()
  109. if pastureList == nil || len(pastureList) == 0 {
  110. return nil
  111. }
  112. if calculateIsRunning {
  113. return nil
  114. }
  115. defer func() {
  116. calculateIsRunning = false
  117. }()
  118. calculateIsRunning = true
  119. for _, pasture := range pastureList {
  120. if err := e.EntryUpdateActiveHabit(pasture.Id); err != nil {
  121. zaplog.Error("NeckRingCalculate", zap.Any("err", err), zap.Any("pasture", pasture))
  122. }
  123. zaplog.Info(fmt.Sprintf("NeckRingCalculate Success %d", pasture.Id))
  124. }
  125. return nil
  126. }
  127. // recalculate 合并计算
  128. func (e *Entry) recalculate(neckRingList []*model.NeckRingOriginal) []*model.NeckActiveHabit {
  129. originalMapData := make(map[string]*model.NeckRingOriginalMerge)
  130. // 合并成2个小时的
  131. for _, v := range neckRingList {
  132. xframeId := util.XFrameId(v.Frameid)
  133. 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
  134. if originalMapData[mapKey] == nil {
  135. originalMapData[mapKey] = new(model.NeckRingOriginalMerge)
  136. }
  137. originalMapData[mapKey].IsMageData(v, xframeId)
  138. }
  139. currTime := time.Now()
  140. res := make([]*model.NeckActiveHabit, 0)
  141. // 算平均值
  142. for k, v := range originalMapData {
  143. // 过滤掉合并后不等于6条数据
  144. if v.RecordCount > 6 {
  145. delete(originalMapData, k)
  146. continue
  147. } else if v.RecordCount < 6 {
  148. currMaxXframeId := util.FrameIdMapReverse[int32(currTime.Hour())]
  149. activeDateString := fmt.Sprintf("%s %02d:00:00", v.ActiveDate, v.XframeId*2+1)
  150. activeDate, _ := time.Parse(model.LayoutTime, activeDateString)
  151. if currMaxXframeId-v.XframeId <= 1 && currTime.Add(-1*time.Hour).Unix() < activeDate.Unix() {
  152. delete(originalMapData, k)
  153. continue
  154. }
  155. }
  156. v.SumAvg()
  157. }
  158. if len(originalMapData) <= 0 {
  159. return res
  160. }
  161. res = model.NeckRingOriginalMap(originalMapData).ForMatData()
  162. sort.Sort(model.NeckActiveHabitSlice(res))
  163. return res
  164. }
  165. func (e *Entry) againRecalculate(data *model.NeckActiveHabit) *model.NeckActiveHabit {
  166. originalList := make([]*model.NeckRingOriginal, 0)
  167. frameIds := util.FrameIds(data.Frameid)
  168. if err := e.DB.Model(new(model.NeckRingOriginal)).
  169. Where("pasture_id = ?", data.PastureId).
  170. Where("neck_ring_number = ?", data.NeckRingNumber).
  171. Where("active_date = ?", data.HeatDate).
  172. Where("frameid IN (?)", frameIds).
  173. Find(&originalList).Error; err != nil {
  174. return nil
  175. }
  176. newDataList := e.recalculate(originalList)
  177. if len(newDataList) != 1 {
  178. return nil
  179. }
  180. res := newDataList[0]
  181. res.IsShow = pasturePb.IsShow_No
  182. return res
  183. }
  184. func (e *Entry) EntryUpdateActiveHabit(pastureId int64) (err error) {
  185. // 获取这段执行数据内最大日期和最小日期
  186. xToday := &XToday{}
  187. systemConfigureList, err := e.GetSystemConfigure(pastureId)
  188. if err != nil {
  189. return xerr.WithStack(err)
  190. }
  191. for _, v := range systemConfigureList {
  192. switch v.Name {
  193. case model.MaxHabit:
  194. xToday.LastMaxHabitId = v.Value
  195. case model.High:
  196. xToday.High = int32(v.Value)
  197. case model.Rumina:
  198. xToday.Rumina = int32(v.Value)
  199. case model.XRuminaDisc:
  200. xToday.XRuminaDisc = int32(v.Value)
  201. case model.XChangeDiscount:
  202. xToday.XChangeDiscount = int32(v.Value)
  203. case model.WeeklyActive:
  204. xToday.WeeklyActive = int32(v.Value)
  205. }
  206. }
  207. currMaxHabit := &model.NeckActiveHabit{}
  208. if err = e.DB.Model(new(model.NeckActiveHabit)).
  209. Where("id > ?", xToday.LastMaxHabitId).
  210. Where("pasture_id = ?", pastureId).
  211. Where("is_show = ?", pasturePb.IsShow_No).
  212. Order("id desc").First(currMaxHabit).Error; err != nil {
  213. return xerr.WithStack(err)
  214. }
  215. if currMaxHabit.Id <= 0 || currMaxHabit.Id <= xToday.LastMaxHabitId {
  216. return nil
  217. }
  218. xToday.CurrMaxHabitId = currMaxHabit.Id
  219. defer func() {
  220. // 更新最后一次执行的id值
  221. if err == nil {
  222. e.DB.Model(new(model.NeckRingConfigure)).
  223. Where("name = ?", model.MaxHabit).
  224. Where("pasture_id = ?", pastureId).
  225. Update("value", currMaxHabit.Id)
  226. }
  227. }()
  228. var processIds []int64
  229. // 更新活动滤波
  230. processIds, err = e.FirstFilterUpdate(pastureId, xToday)
  231. if err != nil {
  232. zaplog.Error("NeckRingCalculate", zap.Any("FirstFilterUpdate", err), zap.Any("xToday", xToday))
  233. }
  234. if len(processIds) > 0 {
  235. if err = e.WeeklyUpdateActiveHabit(pastureId, processIds, xToday); err != nil {
  236. zaplog.Error("NeckRingCalculate", zap.Any("WeeklyUpdateActiveHabit", err), zap.Any("xToday", xToday))
  237. }
  238. if err = e.Before3DaysNeckActiveHabit(pastureId, processIds, xToday); err != nil {
  239. zaplog.Error("NeckRingCalculate", zap.Any("Before3DaysNeckActiveHabit", err), zap.Any("xToday", xToday))
  240. }
  241. // 二次更新滤波
  242. if err = e.SecondUpdateChangeFilter(pastureId, xToday); err != nil {
  243. zaplog.Error("NeckRingCalculate", zap.Any("SecondUpdateChangeFilter", err), zap.Any("xToday", xToday))
  244. }
  245. }
  246. // 活动量校正系数和健康评分
  247. if err = e.FilterCorrectAndScoreUpdate(pastureId, xToday); err != nil {
  248. zaplog.Error("NeckRingCalculate", zap.Any("ActivityVolumeChanges", err), zap.Any("xToday", xToday))
  249. }
  250. if err = e.DB.Model(new(model.NeckActiveHabit)).
  251. Where("id BETWEEN ? AND ?", xToday.LastMaxHabitId, xToday.CurrMaxHabitId).
  252. Where("pasture_id = ?", pastureId).
  253. Where("is_show = ?", pasturePb.IsShow_No).
  254. Where("change_filter = ?", model.InitChangeFilter).
  255. Updates(map[string]interface{}{
  256. "change_filter": model.DefaultChangeFilter,
  257. "rumina_filter": model.DefaultRuminaFilter,
  258. "chew_filter": model.DefaultChewFilter,
  259. }).Error; err != nil {
  260. zaplog.Error("EntryUpdateActiveHabit", zap.Any("change_filter", err), zap.Any("xToday", xToday))
  261. }
  262. if err = e.DB.Model(new(model.NeckActiveHabit)).
  263. Where("id BETWEEN ? AND ?", xToday.LastMaxHabitId, xToday.CurrMaxHabitId).
  264. Where("pasture_id = ?", pastureId).
  265. Where("change_filter < ?", 0).
  266. Where("filter_correct < ?", model.DefaultFilterCorrect).
  267. Updates(map[string]interface{}{
  268. "filter_correct": model.DefaultFilterCorrect,
  269. }).Error; err != nil {
  270. zaplog.Error("EntryUpdateActiveHabit", zap.Any("filter_correct", err), zap.Any("xToday", xToday))
  271. }
  272. // 插入群体校正表
  273. if err = e.UpdateChangeAdJust(pastureId, xToday); err != nil {
  274. zaplog.Error("EntryUpdateActiveHabit", zap.Any("UpdateChangeAdJust", err), zap.Any("xToday", xToday))
  275. }
  276. // 健康预警
  277. if len(processIds) > 0 {
  278. if err = e.HealthWarning(pastureId, processIds); err != nil {
  279. zaplog.Error("EntryUpdateActiveHabit", zap.Any("HealthWarning", err))
  280. }
  281. }
  282. return nil
  283. }
  284. // FirstFilterUpdate 首次更新活动滤波
  285. func (e *Entry) FirstFilterUpdate(pastureId int64, xToDay *XToday) (processIds []int64, err error) {
  286. newNeckActiveHabitList := make([]*model.NeckActiveHabit, 0)
  287. if err = e.DB.Model(new(model.NeckActiveHabit)).
  288. Where("id BETWEEN ? AND ?", xToDay.LastMaxHabitId, xToDay.CurrMaxHabitId).
  289. Where("pasture_id = ?", pastureId).
  290. Where("is_show = ?", pasturePb.IsShow_No).
  291. Where(e.DB.Where("high >= ?", xToDay.High).Or("rumina >= ?", xToDay.Rumina)).
  292. Order("heat_date,neck_ring_number,frameid").
  293. Limit(int(defaultLimit)).Find(&newNeckActiveHabitList).Error; err != nil {
  294. return nil, xerr.WithStack(err)
  295. }
  296. // 活动量滤波
  297. for _, v := range newNeckActiveHabitList {
  298. // 过滤牛只未绑定的脖环的数据
  299. cowInfo := e.GetCowInfoByNeckRingNumber(v.PastureId, v.NeckRingNumber)
  300. if cowInfo == nil || cowInfo.Id <= 0 {
  301. continue
  302. }
  303. frameId := v.Frameid
  304. heatDate := v.HeatDate
  305. if v.Frameid == 0 {
  306. frameId = 11
  307. heatDateParse, _ := time.Parse(model.LayoutDate2, heatDate)
  308. heatDate = heatDateParse.AddDate(0, 0, -1).Format(model.LayoutDate2)
  309. }
  310. firstFilterData := e.FindFirstFilter(pastureId, v.NeckRingNumber, heatDate, frameId)
  311. if v.FilterHigh > 0 {
  312. firstFilterData.FilterHigh = v.FilterHigh
  313. } else {
  314. if v.NeckRingNumber == firstFilterData.NeckRingNumber {
  315. firstFilterData.FilterHigh = int32(computeIfPositiveElse(float64(v.High), float64(firstFilterData.FilterHigh), 0.23, 0.77))
  316. } else {
  317. firstFilterData.FilterHigh = v.High
  318. }
  319. }
  320. if v.FilterRumina > 0 {
  321. firstFilterData.FilterRumina = v.FilterRumina
  322. } else {
  323. if v.NeckRingNumber == firstFilterData.NeckRingNumber {
  324. firstFilterData.FilterRumina = int32(computeIfPositiveElse(float64(v.Rumina), float64(firstFilterData.FilterRumina), 0.33, 0.67))
  325. } else {
  326. firstFilterData.FilterRumina = v.Rumina
  327. }
  328. }
  329. if v.FilterChew > 0 {
  330. firstFilterData.FilterChew = v.FilterChew
  331. } else {
  332. if v.NeckRingNumber == firstFilterData.NeckRingNumber {
  333. firstFilterData.FilterChew = int32(computeIfPositiveElse(float64(v.Rumina+v.Intake), float64(firstFilterData.FilterChew), 0.33, 0.67))
  334. } else {
  335. firstFilterData.FilterChew = v.Rumina + v.Intake
  336. }
  337. }
  338. processIds = append(processIds, v.Id)
  339. // 更新过滤值
  340. if err = e.DB.Model(new(model.NeckActiveHabit)).
  341. Select(
  342. "filter_high", "filter_rumina", "filter_chew", "cow_id", "lact", "calving_age",
  343. ).Where("id = ?", v.Id).
  344. Updates(map[string]interface{}{
  345. "filter_high": firstFilterData.FilterHigh,
  346. "filter_rumina": firstFilterData.FilterRumina,
  347. "filter_chew": firstFilterData.FilterChew,
  348. "cow_id": cowInfo.Id,
  349. "lact": cowInfo.Lact,
  350. "calving_age": cowInfo.CalvingAge,
  351. }).Error; err != nil {
  352. zaplog.Error("FirstFilterUpdate",
  353. zap.Any("error", err),
  354. zap.Any("firstFilterData", firstFilterData),
  355. zap.Any("NeckActiveHabit", v),
  356. zap.Any("cowInfo", cowInfo),
  357. zap.Any("xToday", xToDay),
  358. )
  359. }
  360. }
  361. return processIds, nil
  362. }
  363. func (e *Entry) WeeklyUpdateActiveHabit(pastureId int64, processIds []int64, xToDay *XToday) (err error) {
  364. newNeckActiveHabitList := make([]*model.NeckActiveHabit, 0)
  365. if err = e.DB.Model(new(model.NeckActiveHabit)).
  366. Where("id IN (?)", processIds).
  367. Order("heat_date,neck_ring_number,frameid").
  368. Find(&newNeckActiveHabitList).Error; err != nil {
  369. return xerr.WithStack(err)
  370. }
  371. for _, v := range newNeckActiveHabitList {
  372. // 前七天的
  373. weekHabitData := e.FindWeekHabitData(pastureId, v.NeckRingNumber, v.HeatDate, v.Frameid, xToDay)
  374. highDiff := v.FilterHigh - weekHabitData.WeekHighHabit
  375. denominator := float64(v.WeekHigh)*0.6 + float64(weekHabitData.WeekHighHabit)*0.2 + float64(xToDay.WeeklyActive)*0.2
  376. if highDiff > 0 {
  377. v.ChangeHigh = int32(math.Round((float64(highDiff) / denominator) * 100))
  378. } else {
  379. v.ChangeHigh = int32(math.Round(float64(highDiff) / denominator * 100))
  380. }
  381. if weekHabitData.WeekRuminaHabit != 0 {
  382. v.ChangeRumina = int32(math.Round(float64(v.FilterRumina-weekHabitData.WeekRuminaHabit) / float64(weekHabitData.WeekRuminaHabit) * 100))
  383. } else {
  384. v.ChangeRumina = 0
  385. }
  386. if weekHabitData.WeekChewHabit != 0 {
  387. v.ChangeChew = int32(math.Round(float64(v.FilterChew-weekHabitData.WeekChewHabit) / float64(weekHabitData.WeekChewHabit) * 100))
  388. } else {
  389. v.ChangeChew = 0
  390. }
  391. sumHabitData := e.FindSumHabitData(pastureId, v.NeckRingNumber, v.HeatDate, v.Frameid, xToDay)
  392. // 更新过滤值
  393. if err = e.DB.Model(new(model.NeckActiveHabit)).
  394. Select(
  395. "week_high_habit", "week_rumina_habit", "week_chew_habit", "week_intake_habit", "week_inactive_habit",
  396. "sum_rumina", "sum_intake", "sum_inactive", "sum_active", "sum_max_high", "sum_min_high", "sum_min_chew",
  397. "change_high", "change_rumina", "change_chew", "before_three_sum_rumina", "before_three_sum_intake",
  398. ).Where("id = ?", v.Id).
  399. Updates(map[string]interface{}{
  400. "week_high_habit": weekHabitData.WeekHighHabit,
  401. "week_rumina_habit": weekHabitData.WeekRuminaHabit,
  402. "week_chew_habit": weekHabitData.WeekChewHabit,
  403. "week_intake_habit": weekHabitData.WeekIntakeHabit,
  404. "week_inactive_habit": weekHabitData.WeekIntakeHabit,
  405. "sum_rumina": sumHabitData.SumRumina,
  406. "sum_intake": sumHabitData.SumIntake,
  407. "sum_inactive": sumHabitData.SumInactive,
  408. "sum_active": sumHabitData.SumActive,
  409. "sum_max_high": sumHabitData.SumMaxHigh,
  410. "sum_min_high": sumHabitData.SumMinHigh,
  411. "sum_min_chew": sumHabitData.SumMinChew,
  412. "change_high": v.ChangeHigh,
  413. "change_rumina": v.ChangeRumina,
  414. "change_chew": v.ChangeChew,
  415. }).Error; err != nil {
  416. zaplog.Error("WeeklyUpdateActiveHabit",
  417. zap.Error(err),
  418. zap.Any("NeckActiveHabit", v),
  419. zap.Any("pastureId", pastureId),
  420. )
  421. }
  422. }
  423. return err
  424. }
  425. func (e *Entry) Before3DaysNeckActiveHabit(pastureId int64, processIds []int64, xToDay *XToday) (err error) {
  426. newNeckActiveHabitList := make([]*model.NeckActiveHabit, 0)
  427. if err = e.DB.Model(new(model.NeckActiveHabit)).
  428. Where("id IN (?)", processIds).
  429. Order("heat_date,neck_ring_number,frameid").
  430. Find(&newNeckActiveHabitList).Error; err != nil {
  431. return xerr.WithStack(err)
  432. }
  433. for _, v := range newNeckActiveHabitList {
  434. before3DaysNeckActiveHabit := e.FindBefore3DaysNeckActiveHabit(pastureId, v.NeckRingNumber, v.HeatDate, v.Frameid)
  435. // 更新过滤值
  436. if err = e.DB.Model(new(model.NeckActiveHabit)).
  437. Select("before_three_sum_rumina", "before_three_sum_intake").
  438. Where("id = ?", v.Id).
  439. Updates(map[string]interface{}{
  440. "before_three_sum_rumina": before3DaysNeckActiveHabit.SumRumina,
  441. "before_three_sum_intake": before3DaysNeckActiveHabit.SumIntake,
  442. }).Error; err != nil {
  443. zaplog.Error("Before3DaysNeckActiveHabit",
  444. zap.Error(err),
  445. zap.Any("NeckActiveHabit", v),
  446. zap.Any("pastureId", pastureId),
  447. )
  448. }
  449. }
  450. return nil
  451. }
  452. // SecondUpdateChangeFilter 第二次更新变化趋势滤波
  453. func (e *Entry) SecondUpdateChangeFilter(pastureId int64, xToday *XToday) (err error) {
  454. newChangeFilterList := make([]*ChangeFilterData, 0)
  455. if err = e.DB.Model(new(model.NeckActiveHabit)).
  456. Select("id", "neck_ring_number", "change_high", "change_filter", "rumina_filter", "change_rumina",
  457. "chew_filter", "change_chew", "heat_date", "frameid", "IF(lact = 0, 0.8, 1) as xlc_dis_count").
  458. Where("heat_date >= ?", time.Now().AddDate(0, 0, -2).Format(model.LayoutDate2)).
  459. Where("pasture_id = ?", pastureId).
  460. Where("change_filter = ?", model.InitChangeFilter).
  461. Where("change_high > ?", MinChangeHigh).
  462. Order("neck_ring_number,heat_date,frameid").
  463. Find(&newChangeFilterList).Error; err != nil {
  464. return xerr.WithStack(err)
  465. }
  466. for _, v := range newChangeFilterList {
  467. secondFilterData := &SecondFilterData{}
  468. frameId := v.FrameId
  469. heatDate := v.HeatDate
  470. if v.FrameId == 0 {
  471. frameId = 11
  472. heatDateParse, _ := time.Parse(model.LayoutDate2, heatDate)
  473. heatDate = heatDateParse.AddDate(0, 0, -1).Format(model.LayoutDate2)
  474. }
  475. if err = e.DB.Model(new(model.NeckActiveHabit)).
  476. Select("neck_ring_number", "filter_high", "filter_rumina", "filter_chew").
  477. Where("neck_ring_number = ?", v.NeckRingNumber).
  478. Where("heat_date = ?", heatDate).
  479. Where("frameid = ?", frameId).
  480. First(&secondFilterData).Error; err != nil {
  481. zaplog.Error("EntryUpdateActiveHabit", zap.Any("FirstFilterUpdate", err))
  482. }
  483. if v.ChangeFilter > MinChangeFilter {
  484. secondFilterData.ChangeFilter = float64(v.ChangeFilter)
  485. } else {
  486. if v.NeckRingNumber == secondFilterData.NeckRingNumber {
  487. secondFilterData.ChangeFilter = secondFilterData.ChangeFilter*(1-(float64(xToday.XChangeDiscount)/10)*v.XlcDisCount) +
  488. math.Min(float64(v.ChangeHigh), secondFilterData.ChangeFilter+135)*(float64(xToday.XChangeDiscount)/10)*v.XlcDisCount
  489. } else {
  490. secondFilterData.ChangeFilter = 0
  491. }
  492. }
  493. if v.RuminaFilter > MinRuminaFilter {
  494. secondFilterData.RuminaFilter = float64(v.ChangeFilter)
  495. } else {
  496. if v.NeckRingNumber == secondFilterData.NeckRingNumber {
  497. discount := float64(xToday.XRuminaDisc) / 10 * v.XlcDisCount
  498. if math.Abs(float64(v.ChangeRumina)) > 60 {
  499. discount *= 0.5
  500. }
  501. secondFilterData.RuminaFilter = secondFilterData.RuminaFilter*(1-discount) + float64(v.ChangeRumina)*discount
  502. } else {
  503. secondFilterData.RuminaFilter = 0
  504. }
  505. }
  506. secondFilterData.RuminaFilter = math.Min(50, secondFilterData.RuminaFilter)
  507. if v.ChewFilter > MinChewFilter {
  508. secondFilterData.ChewFilter = float64(v.ChangeChew)
  509. } else {
  510. if v.NeckRingNumber == secondFilterData.NeckRingNumber {
  511. discount := float64(xToday.XRuminaDisc) / 10
  512. if math.Abs(float64(v.ChangeChew)) > 60 {
  513. discount *= 0.5
  514. }
  515. secondFilterData.ChewFilter = secondFilterData.ChewFilter*(1-discount) + float64(v.ChangeChew)*discount
  516. } else {
  517. secondFilterData.ChewFilter = 0
  518. }
  519. }
  520. secondFilterData.ChewFilter = math.Min(50, secondFilterData.ChewFilter)
  521. if err = e.DB.Model(new(model.NeckActiveHabit)).
  522. Select("change_filter", "rumina_filter", "chew_filter").
  523. Where("id = ?", v.Id).
  524. Updates(map[string]interface{}{
  525. "change_filter": secondFilterData.ChangeFilter,
  526. "rumina_filter": secondFilterData.RuminaFilter,
  527. "chew_filter": secondFilterData.ChewFilter,
  528. }).Error; err != nil {
  529. zaplog.Error("SecondUpdateChangeFilter-1", zap.Any("error", err), zap.Any("xToday", xToday))
  530. }
  531. }
  532. return nil
  533. }
  534. // FilterCorrectAndScoreUpdate 计算活动量变化趋势校正值(活跃度校正)和健康评分
  535. func (e *Entry) FilterCorrectAndScoreUpdate(pastureId int64, xToday *XToday) error {
  536. beginDayDate := time.Now()
  537. before7DayDate := beginDayDate.AddDate(0, 0, -7).Format(model.LayoutDate2)
  538. before1DayDate := beginDayDate.AddDate(0, 0, -1).Format(model.LayoutDate2)
  539. activityVolumeList := make([]*ActivityVolume, 0)
  540. activityVolumeMap := make(map[string]*ActivityVolume)
  541. if err := e.DB.Model(new(model.NeckActiveHabit)).
  542. Select("neck_ring_number", "AVG(IF(change_filter>=60, 60, change_filter)) as avg_filter",
  543. "ROUND(STD(IF(change_filter>=60, 60, change_filter))) as std_filter", "COUNT(1) as nb").
  544. Where("heat_date BETWEEN ? AND ?", before7DayDate, before1DayDate).
  545. Where("pasture_id = ?", pastureId).
  546. Where(e.DB.Where("high > ?", xToday.High).Or("rumina >= ?", xToday.Rumina)).
  547. Where("active_time <= ?", beginDayDate.Add(-12*time.Hour).Format(model.LayoutTime)).
  548. Where("change_filter > ?", MinChangeFilter).
  549. Having("nb > ?", DefaultNb).
  550. Group("neck_ring_number").
  551. Find(&activityVolumeList).Error; err != nil {
  552. zaplog.Error("ActivityVolumeChanges-0", zap.Any("error", err), zap.Any("xToday", xToday))
  553. }
  554. if len(activityVolumeList) > 0 {
  555. for _, v := range activityVolumeList {
  556. activityVolumeMap[v.NeckRingNumber] = v
  557. }
  558. }
  559. neckActiveHabitList := make([]*model.NeckActiveHabit, 0)
  560. if err := e.DB.Model(new(model.NeckActiveHabit)).
  561. Where("id <= ?", xToday.CurrMaxHabitId).
  562. Where("heat_date >= ?", before1DayDate).
  563. Where("pasture_id = ?", pastureId).
  564. Where(e.DB.Where("high > ?", xToday.High).Or("rumina > ?", xToday.Rumina)).
  565. Find(&neckActiveHabitList).Error; err != nil {
  566. zaplog.Error("ActivityVolumeChanges-1", zap.Any("error", err), zap.Any("xToday", xToday))
  567. return xerr.WithStack(err)
  568. }
  569. for _, v := range neckActiveHabitList {
  570. if filterCorrectMap, ok := activityVolumeMap[v.NeckRingNumber]; ok {
  571. filterCorrect := model.DefaultFilterCorrect - int(math.Floor(filterCorrectMap.AvgFilter/3+float64(filterCorrectMap.StdFilter)/2))
  572. // 活动量校正系数
  573. if err := e.DB.Model(new(model.NeckActiveHabit)).
  574. Where("id = ?", v.Id).
  575. Where("neck_ring_number = ?", v.NeckRingNumber).
  576. Update("filter_correct", filterCorrect).Error; err != nil {
  577. zaplog.Error("ActivityVolumeChanges-2", zap.Any("error", err), zap.Any("xToday", xToday))
  578. continue
  579. }
  580. }
  581. cowScore := calculateScore(v)
  582. if err := e.DB.Model(new(model.NeckActiveHabit)).
  583. Where("id = ?", v.Id).
  584. Update("score", cowScore).Error; err != nil {
  585. zaplog.Error("ActivityVolumeChanges-2", zap.Any("error", err), zap.Any("xToday", xToday))
  586. continue
  587. }
  588. }
  589. return nil
  590. }
  591. // UpdateChangeAdJust 更新群体校正数据
  592. func (e *Entry) UpdateChangeAdJust(pastureId int64, xToday *XToday) error {
  593. /*-- 插入群体校正表
  594. INSERT INTO data_bar_change(heatdate, frameid, intCurBar, intCurBarName, nb, highchange, changefilter)
  595. SELECT h.heatdate, h.frameid, c.intCurBar, c.intCurBarName, COUNT(*) nb, ROUND(AVG(h.highchange)) highchange, ROUND(AVG(h.changefilter) ) changefilter
  596. FROM h_activehabit h JOIN cow c ON h.intCowId=c.intCowId
  597. WHERE h.heatdate>=(CURDATE() -INTERVAL 1 DAY )
  598. GROUP BY h.heatdate, h.frameid, c.intCurBar
  599. ORDER BY h.heatdate, h.frameid, c.intCurBarName
  600. ON DUPLICATE KEY UPDATE nb = VALUES(nb), highchange = VALUES(highchange), changefilter = VALUES(changefilter);
  601. UPDATE h_activehabit h
  602. JOIN cow c ON h.intCowId=c.intCowId
  603. JOIN data_bar_change cg ON h.heatdate=cg.heatdate AND h.frameid=cg.frameid AND c.intCurBar=cg.intCurBar
  604. SET h.changeadjust=cg.changefilter
  605. WHERE h.id>xBeg_update_act_Id AND h.heatdate>=CURRENT_DATE() - INTERVAL 1 DAY AND ABS(cg.changefilter)>=10;
  606. */
  607. res := make([]*model.NeckRingBarChange, 0)
  608. oneDayAgo := time.Now().AddDate(0, 0, -1).Format(model.LayoutDate2)
  609. if err := e.DB.Table(fmt.Sprintf("%s as h", new(model.NeckActiveHabit).TableName())).
  610. 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").
  611. Joins("JOIN cow as c ON h.neck_ring_number = c.neck_ring_number").
  612. Where("h.pasture_id = ?", pastureId).
  613. Where("h.heat_date >= ?", oneDayAgo).
  614. Where("h.cow_id >= ?", 0).
  615. Where("h.cow_id >= ?", 0).
  616. Group("h.heat_date, h.frameid, c.pen_id").
  617. Order("h.heat_date, h.frameid, c.pen_name").
  618. Find(&res).Error; err != nil {
  619. return xerr.WithStack(err)
  620. }
  621. // todo ABS(cg.changefilter)>=10;
  622. for _, v := range res {
  623. if err := e.DB.Model(new(model.NeckActiveHabit)).
  624. Where("id > ?", xToday.LastMaxHabitId).
  625. Where("heat_date = ?", v.HeatDate).
  626. Where("frameid = ?", v.FrameId).
  627. Where("neck_ring_number = ?", v.NeckRingNumber).
  628. Update("change_adjust", v.ChangeHigh).Error; err != nil {
  629. zaplog.Error("UpdateChangeAdJust-1", zap.Any("error", err), zap.Any("xToday", xToday))
  630. }
  631. }
  632. // 更新所有的显示状态为否的记录为是
  633. if err := e.DB.Model(new(model.NeckActiveHabit)).
  634. Where("id BETWEEN ? AND ?", xToday.LastMaxHabitId, xToday.CurrMaxHabitId).
  635. Where("pasture_id = ?", pastureId).
  636. Update("is_show", pasturePb.IsShow_Ok).Error; err != nil {
  637. zaplog.Error("UpdateChangeAdJust-2", zap.Any("error", err), zap.Any("xToday", xToday))
  638. }
  639. return nil
  640. }
  641. func (e *Entry) UpdateNeckRingOriginalIsShow(habit *model.NeckActiveHabit) error {
  642. if err := e.DB.Model(new(model.NeckRingOriginal)).
  643. Where("pasture_id = ?", habit.PastureId).
  644. Where("neck_ring_number = ?", habit.NeckRingNumber).
  645. Where("active_date = ?", habit.HeatDate).
  646. Where("frameid IN (?)", util.FrameIds(habit.Frameid)).
  647. Update("is_show", pasturePb.IsShow_Ok).Error; err != nil {
  648. return xerr.WithStack(err)
  649. }
  650. return nil
  651. }
  652. // computeIfPositiveElse 辅助函数来计算过滤值
  653. func computeIfPositiveElse(newValue, prevFilterValue float64, weightPrev, weightNew float64) float64 {
  654. return math.Ceil((prevFilterValue * weightPrev) + (weightNew * newValue))
  655. }
  656. // 计算 score 的逻辑
  657. func calculateScore(habit *model.NeckActiveHabit) int {
  658. // 第一部分逻辑
  659. var part1 float64
  660. switch {
  661. case (habit.CalvingAge <= 1 && habit.Lact >= 1) ||
  662. (habit.CalvingAge >= 2 && habit.CalvingAge <= 13 && (habit.SumRumina+habit.SumIntake) == 0) ||
  663. ((habit.Lact == 0 || habit.CalvingAge >= 14) && habit.ChangeFilter == -99):
  664. part1 = -199
  665. case habit.CalvingAge >= 2 && habit.CalvingAge <= 13:
  666. part1 = math.Min((float64(habit.SumRumina+habit.SumIntake)-(100+math.Min(7, float64(habit.CalvingAge))*60))/10*2, 0)
  667. case habit.ChangeFilter > -99:
  668. part1 = math.Min(0, math.Min(getValueOrDefault(float64(habit.ChangeFilter), 0), getValueOrDefault(float64(habit.SumMinHigh), 0)))*0.2 +
  669. math.Min(0, math.Min(getValueOrDefault(float64(habit.ChangeFilter), 0), getValueOrDefault(float64(habit.SumMinChew), 0)))*0.2 +
  670. getRuminaSumIntakeSumScore(float64(habit.SumRumina+habit.SumIntake)) + getAdditionalScore(habit)
  671. default:
  672. part1 = -299
  673. }
  674. // 第二部分逻辑
  675. var part2 float64
  676. switch {
  677. case habit.FirmwareVersion%100 >= 52:
  678. part2 = 1
  679. case habit.FirmwareVersion%100 >= 30 && habit.FirmwareVersion%100 <= 43:
  680. part2 = 0.8
  681. default:
  682. part2 = 0.6
  683. }
  684. // 最终 score
  685. return DefaultScore + int(math.Floor(part1*part2))
  686. }
  687. // 获取值或默认值
  688. func getValueOrDefault(value, defaultValue float64) float64 {
  689. if value > -99 {
  690. return value
  691. }
  692. return defaultValue
  693. }
  694. // 计算累计反刍得分
  695. func getRuminaSumIntakeSumScore(sum float64) float64 {
  696. switch {
  697. case sum < 80:
  698. return -30
  699. case sum < 180:
  700. return -20
  701. case sum < 280:
  702. return -10
  703. default:
  704. return 0
  705. }
  706. }
  707. // 计算额外得分
  708. func getAdditionalScore(habit *model.NeckActiveHabit) float64 {
  709. var score float64
  710. if (habit.SumRumina+habit.SumIntake < 280 || habit.SumMinHigh+habit.SumMinChew < -50) && habit.SumMaxHigh > 50 {
  711. score += 10
  712. }
  713. if habit.ChangeFilter < -30 && habit.ChangeFilter <= habit.SumMinHigh && habit.ChewFilter < -30 && habit.ChewFilter <= habit.SumMinChew {
  714. score -= 5
  715. }
  716. return score
  717. }