neck_ring_estrus.go 8.2 KB

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