estrus_warning.go 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. package crontab
  2. import (
  3. "context"
  4. "kpt-pasture/model"
  5. "time"
  6. pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
  7. "gitee.com/xuyiping_admin/pkg/logger/zaplog"
  8. "go.uber.org/zap"
  9. "gitee.com/xuyiping_admin/pkg/xerr"
  10. "github.com/hibiken/asynq"
  11. )
  12. const (
  13. MaxRuminaAdJust = 20
  14. XAdjust21 = 15
  15. XAdjust42 = 10
  16. RumtoHeat = 0.5
  17. MinCalvingAge = 20
  18. MinLact = 0
  19. NormalChangJust = 10
  20. )
  21. func (e *Entry) EntryCowEstrus(ctx context.Context, t *asynq.Task) error {
  22. activeLowValue := e.GetSystemConfigure(model.ActiveLow).Value
  23. activeMiddleValue := e.GetSystemConfigure(model.ActiveMiddle).Value
  24. activeHighValue := e.GetSystemConfigure(model.ActiveHigh).Value
  25. lastMaxEstrusId := e.GetSystemConfigure(model.MaxEstrus).Value
  26. currentMaxHabit := &model.NeckActiveHabit{}
  27. if err := e.DB.Model(new(model.NeckActiveHabit)).
  28. Order("id desc").
  29. First(currentMaxHabit).Error; err != nil {
  30. return xerr.WithStack(err)
  31. }
  32. xToday := &XToday{}
  33. if err := e.DB.Model(new(model.NeckActiveHabit)).
  34. Select(`MIN(h.heat_date) as x_beg_date, MAX(h.heat_date) as x_end_date`).
  35. Where("id BETWEEN ? AND ?", lastMaxEstrusId, currentMaxHabit).
  36. First(xToday).Error; err != nil {
  37. return xerr.WithStack(err)
  38. }
  39. // 当前Id<=上次执行的id,则不执行
  40. if currentMaxHabit.Id <= int64(lastMaxEstrusId) {
  41. return nil
  42. }
  43. xToday.LastMaxHabitId = int64(lastMaxEstrusId)
  44. xToday.CurrMaxHabitId = currentMaxHabit.Id
  45. xToday.ActiveLow = int64(activeLowValue)
  46. xToday.ActiveMiddle = int64(activeMiddleValue)
  47. xToday.ActiveHigh = int64(activeHighValue)
  48. if err := e.CowEstrusWarning(ctx, xToday); err != nil {
  49. return xerr.WithStack(err)
  50. }
  51. return nil
  52. }
  53. func (e *Entry) CowEstrusWarning(ctx context.Context, xToday *XToday) error {
  54. startDate, err := time.Parse(model.LayoutDate2, xToday.XBegDate)
  55. if err != nil {
  56. return xerr.WithStack(err)
  57. }
  58. endDate, err := time.Parse(model.LayoutDate2, xToday.XEndDate)
  59. if err != nil {
  60. return xerr.WithStack(err)
  61. }
  62. for startDate.Format(model.LayoutDate2) <= endDate.Format(model.LayoutDate2) {
  63. neckActiveHabitList := make([]*model.NeckActiveHabit, 0)
  64. if err = e.DB.Model(new(model.NeckActiveHabit)).
  65. Select("*,MAX(filter_high) as filter_high").
  66. Where("id BETWEEN ? AND ?", xToday.LastMaxHabitId, xToday.CurrMaxHabitId).
  67. Where("heat_date = ?", startDate.Format(model.LayoutDate2)).
  68. Where("cow_id > ?", 0).
  69. Where(e.DB.Where("calving_age > ?", MinCalvingAge).Or("lact = ?", MinLact)).
  70. Group("cow_id").
  71. Find(&neckActiveHabitList).
  72. Error; err != nil {
  73. return xerr.WithStack(err)
  74. }
  75. eventEstrusList := make([]*model.EventEstrus, 0)
  76. for _, v := range neckActiveHabitList {
  77. if ok := e.IsAdJustLow(ctx, xToday, v); ok {
  78. continue
  79. }
  80. cft := float32(0)
  81. if v.ChangeAdjust > 10 {
  82. cft = float32(v.ChangeFilter) - float32(v.ChangeAdjust) + 3
  83. } else {
  84. value := float32(0)
  85. switch {
  86. case v.RuminaFilter > MaxRuminaAdJust:
  87. value = float32(5)
  88. case v.RuminaFilter > 0:
  89. value = float32(v.RuminaFilter) * 0.25
  90. case v.RuminaFilter < -MaxRuminaAdJust:
  91. value = -MaxRuminaAdJust * RumtoHeat
  92. default:
  93. value = float32(v.RuminaFilter) * RumtoHeat
  94. }
  95. cft = float32(v.ChangeFilter)*float32(v.FilterCorrect)/100 - value
  96. }
  97. // 最近3天最大发情记录,小于该变化趋势的不再插入
  98. eventEstrus := e.GetBeforeThreeDaysCowEstrus(v.CowId, startDate.AddDate(0, 0, -2).Format(model.LayoutTime))
  99. if eventEstrus.CowId != v.CowId {
  100. if int32(cft) <= eventEstrus.PerTwentyFourHigh {
  101. continue
  102. }
  103. }
  104. // 判断最近50天内是否存在发情记录(发情等级>=2),如果18~25天@xadjust21,如果36~50天@xadjust42
  105. cowEstrus := e.GetTwoEstrus(v.CowId, startDate.AddDate(0, 0, -100).Format(model.LayoutTime), startDate.AddDate(0, 0, -2).Format(model.LayoutTime))
  106. if cowEstrus.CowId == v.CowId {
  107. activeDateTime, _ := time.Parse(model.LayoutTime, cowEstrus.ActiveDate)
  108. if activeDateTime.Unix() >= startDate.AddDate(0, 0, -25).Unix() && activeDateTime.Unix() <= startDate.AddDate(0, 0, -18).Unix() {
  109. cowEstrus.HadJust = XAdjust21
  110. }
  111. if activeDateTime.Unix() >= startDate.AddDate(0, 0, -50).Unix() && activeDateTime.Unix() <= startDate.AddDate(0, 0, -36).Unix() {
  112. cowEstrus.HadJust = XAdjust42
  113. }
  114. }
  115. if int32(cft)+cowEstrus.HadJust <= int32(xToday.ActiveLow) {
  116. continue
  117. }
  118. cowInfo, err := e.GetCowById(v.CowId)
  119. if err != nil {
  120. zaplog.Error("CowEstrusWarning", zap.Any("GetCowById", err), zap.Any("cowId", v.CowId))
  121. continue
  122. }
  123. level := pasturePb.EstrusLevel_High
  124. if int32(cft)+cowEstrus.HadJust < int32(xToday.ActiveMiddle) {
  125. level = pasturePb.EstrusLevel_Low
  126. }
  127. if int32(cft)+cowEstrus.HadJust >= int32(xToday.ActiveHigh) {
  128. level = pasturePb.EstrusLevel_Middle
  129. }
  130. result := pasturePb.EstrusResult_Invalid
  131. if eventEstrus.Result == pasturePb.EstrusResult_Fail && eventEstrus.PerTwentyFourHigh > int32(cft)+cowEstrus.HadJust {
  132. result = pasturePb.EstrusResult_Fail
  133. }
  134. // todo 待定
  135. if result == pasturePb.EstrusResult_Invalid {
  136. result = pasturePb.EstrusResult_Correct
  137. }
  138. isShow := pasturePb.IsShow_Ok
  139. if cowInfo.IsPregnant == pasturePb.IsShow_Ok {
  140. isShow = pasturePb.IsShow_No
  141. }
  142. eventEstrusList = append(eventEstrusList, &model.EventEstrus{
  143. CowId: v.CowId,
  144. DayAge: cowInfo.DayAge,
  145. Lact: cowInfo.Lact,
  146. CalvingAge: int32(cowInfo.CalvingAge),
  147. ExposeEstrusType: pasturePb.ExposeEstrusType_Natural_Estrus,
  148. FilterHigh: v.FilterHigh,
  149. EstrusDate: v.ActiveTime,
  150. ActiveDate: v.ActiveTime,
  151. LastEstrusDate: cowEstrus.ActiveDate,
  152. Level: level,
  153. IsPeak: pasturePb.IsShow_No,
  154. PerTwentyFourHigh: int32(cft) + cowEstrus.HadJust,
  155. Result: result,
  156. IsShow: isShow,
  157. })
  158. }
  159. if len(eventEstrusList) > 0 {
  160. if err = e.DB.Create(eventEstrusList).Error; err != nil {
  161. zaplog.Error("CowEstrusWarning", zap.Any("eventEstrusList", eventEstrusList), zap.Any("err", err))
  162. }
  163. }
  164. startDate.AddDate(0, 0, 1)
  165. }
  166. return nil
  167. }
  168. // IsAdJustLow 是否低于最低活动量
  169. func (e *Entry) IsAdJustLow(ctx context.Context, xToday *XToday, habit *model.NeckActiveHabit) bool {
  170. ruminaAdJust := float64(0)
  171. switch {
  172. case habit.RuminaFilter > MaxRuminaAdJust:
  173. ruminaAdJust = 5
  174. case habit.RuminaFilter > 0:
  175. ruminaAdJust = float64(habit.RuminaFilter) * 0.25
  176. case habit.RuminaFilter < -MaxRuminaAdJust:
  177. ruminaAdJust = -MaxRuminaAdJust * RumtoHeat
  178. default:
  179. ruminaAdJust = float64(habit.RuminaFilter) * RumtoHeat
  180. }
  181. ruminaAdJustSum := int32(float64(habit.FilterCorrect)/100 - ruminaAdJust)
  182. isContinue := int32(0)
  183. activeLow := xToday.ActiveLow - XAdjust21
  184. if habit.ChangeFilter >= NormalChangJust {
  185. isContinue = habit.ChangeFilter - habit.ChangeAdjust + 3*ruminaAdJustSum
  186. } else {
  187. isContinue = habit.ChangeFilter * ruminaAdJustSum
  188. }
  189. if isContinue < int32(activeLow) {
  190. return true
  191. }
  192. return false
  193. }