123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332 |
- package crontab
- import (
- "fmt"
- "kpt-pasture/model"
- "kpt-pasture/util"
- "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.GetSystemNeckRingConfigure(pastureId)
- if err != nil {
- return xerr.WithStack(err)
- }
- if systemConfigureList == nil || len(systemConfigureList) == 0 {
- return nil
- }
- 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)
- }
- }
- nowTime := time.Now()
- e.CowEstrusWarning(pastureId, xToday, nowTime)
- e.UpdateNewNeckRingEstrus(pastureId, nowTime)
- return nil
- }
- // CowEstrusWarning 发情预警
- func (e *Entry) CowEstrusWarning(pastureId int64, xToday *XToday, nowTime time.Time) {
- 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天内的发情牛
- Order("cow_id").
- Find(&neckActiveHabitList).Error; err != nil {
- zaplog.Error("CowEstrusWarning", zap.Any("Find", err))
- return
- }
- 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
- }
- zaplog.Info("CowEstrusWarning", zap.Any("cft", cft), zap.Any("habit", habit))
- if neckActiveHabitMap[habit.CowId] == nil {
- 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, _ := util.TimeParseLocal(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)
- lastActiveDate := time.Time{}
- for _, habit := range cowHabitList {
- cft := calculateCFT(habit)
- if cft > maxCft {
- maxCft = cft
- }
- if habit.FilterHigh > maxHigh {
- maxHigh = habit.FilterHigh
- }
- // 获取最新的 CreateTime
- activeTimeParse, _ := util.TimeParseLocal(model.LayoutTime, habit.ActiveTime)
- if activeTimeParse.After(lastActiveDate) {
- lastActiveDate = activeTimeParse
- }
- }
- b48 := float64(0)
- t3, _ := util.TimeParseLocal(model.LayoutTime, before3Data.ActiveTime)
- b48 = t3.Sub(lastActiveDate).Hours()
- if (int32(maxCft) > before3Data.DayHigh || before3Data.CowId == 0 || b48 > B48) && int32(maxCft)+cowEstrus.HadJust > xToday.ActiveLow {
- level := calculateActiveLevel(maxCft, cowEstrus, xToday)
- cowInfo := e.FindCowInfoByCowId(cowId)
- if cowInfo == nil {
- zaplog.Error("CowEstrusWarning", zap.Any("FindCowInfoByCowId", cowId))
- continue
- }
- isShow := pasturePb.IsShow_Ok
- if cowInfo != nil && 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("b48", b48),
- zap.Any("checkResult", checkResult),
- zap.Any("isShow", isShow),
- zap.Any("isPeak", isPeak),
- zap.Any("lastEstrusDate", lastEstrusDate),
- zap.Any("activeDate", lastActiveDate),
- 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, level, checkResult, isShow)
- newNeckRingEstrus.LastTime = lastEstrusDate
- newNeckRingEstrus.ActiveTime = lastActiveDate.Format(model.LayoutTime)
- 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))
- }
- }
- }
- func (e *Entry) UpdateNewNeckRingEstrus(pastureId int64, nowTime time.Time) {
- // 更新牛只首次发情时间
- e.UpdateEstrusFirstTime1(pastureId, nowTime)
- e.UpdateEstrusIsPeak(pastureId, nowTime)
- e.UpdateEstrusFirstTime2(pastureId, nowTime)
- e.UpdateEstrusFirstTime3(pastureId, nowTime)
- }
- // UpdateEstrusFirstTime1 更新牛只首次发情时间
- func (e *Entry) UpdateEstrusFirstTime1(pastureId int64, xToday time.Time) {
- neckRingEstrusList := e.FindNeckRingEstrusByFirstTimeEmpty(pastureId)
- for _, v := range neckRingEstrusList {
- cowEstrusStartData := e.FindCowEstrusFirstTime1(pastureId, v.CowId, xToday)
- if cowEstrusStartData != nil && cowEstrusStartData.FirstTime != "" {
- if err := e.DB.Model(new(model.NeckRingEstrus)).
- Where("id = ?", v.Id).
- Update("first_time", cowEstrusStartData.FirstTime).Error; err != nil {
- zaplog.Error("UpdateEstrusFirstTime1",
- zap.Any("v", v),
- zap.Any("err", err),
- zap.Any("cowEstrusStartData", cowEstrusStartData),
- )
- }
- }
- }
- }
- func (e *Entry) UpdateEstrusFirstTime2(pastureId int64, xToday time.Time) {
- neckRingEstrusList := e.FindNeckRingEstrusByFirstTimeEmpty(pastureId)
- for _, v := range neckRingEstrusList {
- zaplog.Info("UpdateEstrusFirstTime2", zap.Any("v", v))
- }
- }
- func (e *Entry) UpdateEstrusFirstTime3(pastureId int64, xToday time.Time) {
- neckRingEstrusList := e.FindNeckRingEstrusByFirstTimeEmpty(pastureId)
- for _, v := range neckRingEstrusList {
- activeTime, _ := util.TimeParseLocal(model.LayoutTime, v.ActiveTime)
- if activeTime.After(xToday.AddDate(0, 0, -2)) {
- if err := e.DB.Model(new(model.NeckRingEstrus)).
- Where("id = ?", v.Id).
- Update("first_time", v.ActiveTime).Error; err != nil {
- zaplog.Error("UpdateEstrusFirstTime1", zap.Any("v", v), zap.Any("err", err))
- }
- }
- }
- }
- func (e *Entry) UpdateEstrusIsPeak(pastureId int64, xToday time.Time) {
- neckRingEstrusList := make([]*model.NeckRingEstrus, 0)
- if err := e.DB.Model(new(model.NeckRingEstrus)).
- Where("first_time != ?", "").
- Where("is_peak = ?", pasturePb.IsShow_Ok).
- Where("is_show = ?", pasturePb.IsShow_Ok).
- Where("pasture_id = ?", pastureId).
- Find(&neckRingEstrusList).Error; err != nil {
- zaplog.Error("UpdateEstrusIsPeak", zap.Any("Find", err))
- }
- for _, estrus := range neckRingEstrusList {
- activeTime, _ := util.TimeParseLocal(model.LayoutTime, estrus.ActiveTime)
- if activeTime.Before(xToday) || activeTime.After(xToday.AddDate(0, 0, 1)) {
- continue
- }
- var exists bool
- if err := e.DB.Model(new(model.NeckRingEstrus)).
- Where("cow_id = ?", estrus.CowId).
- Where("first_time != ?", "").
- Where("active_time BETWEEN ? AND ?", xToday, xToday.AddDate(0, 0, 1)).
- Where("active_time BETWEEN ? AND ?", estrus.FirstTime, activeTime.Add(-2*time.Hour)).
- Select("1").
- Limit(1).
- Scan(&exists).Error; err != nil {
- zaplog.Error("UpdateEstrusIsPeak", zap.Any("exists", err))
- continue
- }
- if exists {
- if err := e.DB.Model(estrus).
- Update("is_peak", pasturePb.IsShow_No).Error; err != nil {
- zaplog.Error("UpdateEstrusIsPeak", zap.Any("v", estrus), zap.Any("err", err))
- }
- }
- }
- }
- // FindCowEstrusFirstTime1 查找牛只昨天是否有发情数据
- func (e *Entry) FindCowEstrusFirstTime1(pastureId, cowId int64, xToday time.Time) *EstrusStartData {
- firstTimeEventEstrus := &EstrusStartData{}
- if err := e.DB.Model(new(model.NeckRingEstrus)).
- Select("cow_id,MIN(STR_TO_DATE(first_time, '%Y-%m-%d %H:%i:%s')) as first_time").
- Where("active_time BETWEEN ? AND ?",
- fmt.Sprintf("%s 00:00:00", xToday.AddDate(0, 0, -1).Format(model.LayoutDate2)),
- fmt.Sprintf("%s 23:59:59", xToday.Format(model.LayoutDate2)),
- ).Where("pasture_id = ?", pastureId).
- Where("cow_id = ?", cowId).
- First(&firstTimeEventEstrus).Error; err != nil {
- return nil
- }
- return firstTimeEventEstrus
- }
- // calculateCFT 计算cft值
- func calculateCFT(habit *model.NeckActiveHabit) (cft float32) {
- if habit.ChangeAdjust >= 10 {
- cft = (float32(habit.ChangeFilter) - float32(habit.ChangeAdjust) + 3) * float32(habit.FilterCorrect) / 100
- } else {
- cft = float32(habit.ChangeFilter) * float32(habit.FilterCorrect) / 100
- }
- switch {
- case habit.RuminaFilter > MaxRuminaAdJust:
- cft -= float32(5)
- case habit.RuminaFilter > 0:
- cft -= float32(habit.RuminaFilter) * 0.25
- case habit.RuminaFilter < -MaxRuminaAdJust:
- cft -= -MaxRuminaAdJust * RumtoHeat
- default:
- cft -= float32(habit.RuminaFilter) * RumtoHeat
- }
- return cft
- }
- // calculateActiveLevel 计算活动量等级
- func calculateActiveLevel(cft float32, cowEstrus *CowEstrus, xToday *XToday) pasturePb.EstrusLevel_Kind {
- if int32(cft)+cowEstrus.HadJust < xToday.ActiveMiddle {
- return pasturePb.EstrusLevel_Low
- } else if int32(cft)+cowEstrus.HadJust >= xToday.ActiveHigh {
- return pasturePb.EstrusLevel_Middle
- } else {
- return pasturePb.EstrusLevel_High
- }
- }
- // getResult 根据b3数据计算结果
- func getResult(b3 *model.NeckRingEstrus, cft float32, cowEstrus *CowEstrus) pasturePb.CheckResult_Kind {
- result := pasturePb.CheckResult_Pending
- if b3.CheckResult == pasturePb.CheckResult_Correct {
- result = pasturePb.CheckResult_Correct
- }
- if b3.CheckResult == pasturePb.CheckResult_Fail && b3.DayHigh > int32(cft)+cowEstrus.HadJust {
- result = pasturePb.CheckResult_Fail
- }
- return result
- }
|