123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275 |
- package crontab
- import (
- "kpt-pasture/model"
- "time"
- pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
- "gitee.com/xuyiping_admin/pkg/logger/zaplog"
- "go.uber.org/zap"
- "gitee.com/xuyiping_admin/pkg/xerr"
- )
- const (
- MaxRuminaAdJust = 20
- XAdjust21 = 15
- XAdjust42 = 10
- RumtoHeat = 0.5
- MinCalvingAge = 20
- MinLact = 0
- NormalChangJust = 10
- B48 = 48
- )
- func (e *Entry) UpdateCowEstrus() (err error) {
- pastureList := e.FindPastureList()
- if pastureList == nil || len(pastureList) == 0 {
- return nil
- }
- for _, pasture := range pastureList {
- if err = e.EntryCowEstrus(pasture.Id); err != nil {
- zaplog.Error("EntryCrontab", zap.Any("PastureUpdateCowEstrus", err), zap.Any("pasture", pasture))
- }
- zaplog.Info("PastureUpdateCowEstrus-success", zap.Any("pasture", pasture.Id))
- }
- return nil
- }
- func (e *Entry) EntryCowEstrus(pastureId int64) (err error) {
- xToday := &XToday{}
- systemConfigureList, err := e.GetSystemConfigure(pastureId)
- for _, v := range systemConfigureList {
- switch v.Name {
- case model.ActiveLow:
- xToday.ActiveLow = int32(v.Value)
- case model.ActiveMiddle:
- xToday.ActiveMiddle = int32(v.Value)
- case model.ActiveHigh:
- xToday.ActiveHigh = int32(v.Value)
- }
- }
- if err = e.CowEstrusWarning(pastureId, xToday); err != nil {
- zaplog.Error("EntryCowEstrus", zap.Any("CowEstrusWarning", err), zap.Any("xToday", xToday))
- }
- // 将历史发情预警数据更新为已过期
- if err = e.DB.Model(new(model.NeckRingEstrus)).
- Where("estrus_start_date <= ?", time.Now().AddDate(0, 0, -4).Format(model.LayoutTime)).
- Update("is_show", pasturePb.IsShow_No).Error; err != nil {
- zaplog.Error("EntryCowEstrus", zap.Any("UpdateEventEstrus", err))
- }
- return nil
- }
- // CowEstrusWarning 发情预警
- func (e *Entry) CowEstrusWarning(pastureId int64, xToday *XToday) (err error) {
- nowTime := time.Now()
- neckActiveHabitList := make([]*model.NeckActiveHabit, 0) // todo 需要考虑到数据量太大的情况
- if err = e.DB.Model(new(model.NeckActiveHabit)).
- Where("heat_date = ?", nowTime.Format(model.LayoutDate2)).
- Where("pasture_id = ?", pastureId).
- Where("filter_high > 0 AND change_filter > ?", model.DefaultChangeFilter).
- Where("cow_id > ?", 0).
- Where(e.DB.Where("calving_age > ?", MinCalvingAge).Or("lact = ?", MinLact)). // 排除产后20天内的发情牛
- Find(&neckActiveHabitList).Error; err != nil {
- return xerr.WithStack(err)
- }
- zaplog.Info("CowEstrusWarning", zap.Any("neckActiveHabitList", neckActiveHabitList))
- neckActiveHabitMap := make(map[int64][]*model.NeckActiveHabit)
- for _, habit := range neckActiveHabitList {
- cft := calculateCFT(habit)
- if cft < float32(xToday.ActiveLow-XAdjust21) {
- continue
- }
- if _, ok := neckActiveHabitMap[habit.CowId]; !ok {
- neckActiveHabitMap[habit.CowId] = make([]*model.NeckActiveHabit, 0)
- }
- neckActiveHabitMap[habit.CowId] = append(neckActiveHabitMap[habit.CowId], habit)
- }
- zaplog.Info("CowEstrusWarning", zap.Any("neckActiveHabitMap", neckActiveHabitMap))
- neckRingEstrusList := make([]*model.NeckRingEstrus, 0)
- for cowId, cowHabitList := range neckActiveHabitMap {
- // 最近3天最大发情记录,小于该变化趋势的不再插入
- before3Data := e.GetBeforeThreeDaysCowEstrus(cowId, nowTime.AddDate(0, 0, -2).Format(model.LayoutTime))
- // 判断最近50天内是否存在发情记录(发情等级>=2),如果18~25天@xadjust21,如果36~50天@xadjust42
- cowEstrus := e.GetTwoEstrus(pastureId, cowId, nowTime.AddDate(0, 0, -100).Format(model.LayoutTime), nowTime.AddDate(0, 0, -2).Format(model.LayoutTime))
- activeDateTime, _ := time.Parse(model.LayoutTime, cowEstrus.ActiveDate)
- if activeDateTime.Unix() >= nowTime.AddDate(0, 0, -25).Unix() && activeDateTime.Unix() <= nowTime.AddDate(0, 0, -18).Unix() {
- cowEstrus.HadJust = XAdjust21
- }
- if activeDateTime.Unix() >= nowTime.AddDate(0, 0, -50).Unix() && activeDateTime.Unix() <= nowTime.AddDate(0, 0, -36).Unix() {
- cowEstrus.HadJust = XAdjust42
- }
- maxCft := float32(0)
- maxHigh := int32(0)
- for _, habit := range cowHabitList {
- cft := calculateCFT(habit)
- if cft > maxCft {
- maxCft = cft
- }
- if habit.FilterHigh > maxHigh {
- maxHigh = habit.FilterHigh
- }
- }
- activeDate := ""
- if len(cowHabitList) > 0 {
- sortHabits := sortHabitsByChangeFilter(cowHabitList)
- activeDate = sortHabits[0].ActiveTime
- }
- b48 := float64(0)
- t1, _ := time.Parse(model.LayoutTime, activeDate)
- t3, err := time.Parse(model.LayoutTime, before3Data.ActiveDate)
- if err == nil {
- b48 = t3.Sub(t1).Hours()
- }
- if (int32(maxCft) > before3Data.DayHigh || b48 > B48) && int32(maxCft)+cowEstrus.HadJust > xToday.ActiveLow {
- level := calculateLevel(maxCft, cowEstrus, xToday)
- cowInfo := e.FindCowInfoByNeckRingNumber(cowHabitList[0].NeckRingNumber)
- isShow := pasturePb.IsShow_Ok
- if cowInfo.IsPregnant == pasturePb.IsShow_Ok && level == pasturePb.EstrusLevel_Low {
- isShow = pasturePb.IsShow_No
- }
- dayHigh := int32(maxCft) + cowEstrus.HadJust
- lastEstrusDate := cowEstrus.ActiveDate
- checkResult := getResult(before3Data, maxCft, cowEstrus)
- isPeak := pasturePb.IsShow_Ok
- zaplog.Info("CowEstrusWarning",
- zap.Any("level", level),
- zap.Any("checkResult", checkResult),
- zap.Any("isShow", isShow),
- zap.Any("isPeak", isPeak),
- zap.Any("lastEstrusDate", lastEstrusDate),
- zap.Any("activeDate", activeDate),
- zap.Any("dayHigh", dayHigh),
- zap.Any("cft", maxCft),
- zap.Any("before3Data", before3Data),
- zap.Any("cowEstrus", cowEstrus),
- zap.Any("cowInfo", cowInfo),
- zap.Any("cowHabitList", cowHabitList),
- )
- newNeckRingEstrus := model.NewNeckRingEstrus(pastureId, cowInfo, pasturePb.ExposeEstrusType_Neck_Ring, level, checkResult, isShow)
- newNeckRingEstrus.LastEstrusDate = lastEstrusDate
- newNeckRingEstrus.ActiveDate = activeDate
- newNeckRingEstrus.DayHigh = dayHigh
- newNeckRingEstrus.MaxHigh = maxHigh
- newNeckRingEstrus.IsPeak = isPeak
- neckRingEstrusList = append(neckRingEstrusList, newNeckRingEstrus)
- }
- }
- zaplog.Info("CowEstrusWarning", zap.Any("neckRingEstrusList", neckRingEstrusList))
- if len(neckRingEstrusList) > 0 {
- if err = e.DB.Model(new(model.NeckRingEstrus)).Create(neckRingEstrusList).Error; err != nil {
- zaplog.Error("CowEstrusWarningNew", zap.Any("eventEstrusList", neckRingEstrusList), zap.Any("err", err))
- }
- }
- if err = e.UpdateEstrusStartDate(pastureId, nowTime); err != nil {
- zaplog.Error("UpdateEstrusStartDate", zap.Any("err", err))
- }
- return err
- }
- // UpdateEstrusStartDate 更新发情开始时间数据
- func (e *Entry) UpdateEstrusStartDate(pastureId int64, xToday time.Time) (err error) {
- beforeEventEstrus := make([]*EstrusStartData, 0)
- if err = e.DB.Model(new(model.NeckRingEstrus)).
- Select("cow_id,MIN(estrus_start_date) as estrus_start_date").
- Where("active_date BETWEEN ? AND ?", xToday.Add(-24*time.Hour).Format(model.LayoutTime), xToday.Format(model.LayoutTime)).
- Where("estrus_start_date != ?", "").
- Where("expose_estrus_type = ?", pasturePb.ExposeEstrusType_Neck_Ring).
- Where("pasture_id = ?", pastureId).
- Group("cow_id").Find(&beforeEventEstrus).Error; err != nil {
- return xerr.WithStack(err)
- }
- if len(beforeEventEstrus) > 0 {
- for _, v := range beforeEventEstrus {
- if err = e.DB.Model(new(model.NeckRingEstrus)).
- Where("cow_id = ?", v.CowId).
- Where("active_date >= ? AND <= ?", xToday.Add(-1*time.Hour).Format(model.LayoutTime), xToday.Add(24*time.Hour).Format(model.LayoutTime)).
- Update("estrus_start_date", v.EstrusStartDate).Error; err != nil {
- zaplog.Error("UpdateEstrusStartDate", zap.Any("err", err))
- }
- }
- }
- return nil
- }
- // UpdateIsPeak 更新IsPeak是否是高峰字段 1 是 0 否
- func (e *Entry) UpdateIsPeak(pastureId int64, xToday time.Time) (err error) {
- return nil
- }
- // calculateCFT 计算cft值
- func calculateCFT(habit *model.NeckActiveHabit) (cft float32) {
- cft = float32(habit.ChangeFilter) - float32(habit.ChangeAdjust) + 3
- if habit.ChangeAdjust < 10 {
- cft = float32(habit.ChangeFilter)
- }
- cft = cft * float32(habit.FilterCorrect) / 100
- ruminaAdjust := float32(0)
- switch {
- case habit.RuminaFilter > MaxRuminaAdJust:
- ruminaAdjust = float32(5)
- case habit.RuminaFilter > 0:
- ruminaAdjust = float32(habit.RuminaFilter) * 0.25
- case habit.RuminaFilter < -MaxRuminaAdJust:
- ruminaAdjust = -MaxRuminaAdJust * RumtoHeat
- default:
- ruminaAdjust = float32(habit.RuminaFilter) * RumtoHeat
- }
- cft -= ruminaAdjust
- return cft
- }
- // calculateLevel 计算发情等级
- func calculateLevel(cft float32, cowEstrus *CowEstrus, xToday *XToday) pasturePb.EstrusLevel_Kind {
- level := pasturePb.EstrusLevel_High
- if int32(cft)+cowEstrus.HadJust < xToday.ActiveMiddle {
- level = pasturePb.EstrusLevel_Low
- }
- if int32(cft)+cowEstrus.HadJust >= xToday.ActiveHigh {
- level = pasturePb.EstrusLevel_Middle
- }
- return level
- }
- // getResult 根据b3数据计算结果 0 1 2 3 -1 -2
- func getResult(b3 *model.NeckRingEstrus, cft float32, cowEstrus *CowEstrus) pasturePb.CheckResult_Kind {
- result := pasturePb.CheckResult_Invalid
- if b3.CheckResult == pasturePb.CheckResult_Fail && b3.DayHigh > int32(cft)+cowEstrus.HadJust {
- result = pasturePb.CheckResult_Fail
- }
- if b3.CheckResult == pasturePb.CheckResult_Overdue {
- result = pasturePb.CheckResult_Correct
- }
- return result
- }
- // sortHabitsByChangeFilter 根据change_filter排序
- func sortHabitsByChangeFilter(habits []*model.NeckActiveHabit) []*model.NeckActiveHabit {
- sorted := make([]*model.NeckActiveHabit, len(habits))
- copy(sorted, habits)
- for i := range sorted {
- for j := i + 1; j < len(sorted); j++ {
- if sorted[i].ChangeFilter < sorted[j].ChangeFilter {
- sorted[i], sorted[j] = sorted[j], sorted[i]
- }
- }
- }
- return sorted
- }
|