|
@@ -1,6 +1,7 @@
|
|
|
package crontab
|
|
|
|
|
|
import (
|
|
|
+ "fmt"
|
|
|
"kpt-pasture/model"
|
|
|
"time"
|
|
|
|
|
@@ -56,12 +57,14 @@ func (e *Entry) EntryCowEstrus(pastureId int64) (err error) {
|
|
|
zaplog.Error("EntryCowEstrus", zap.Any("CowEstrusWarning", err), zap.Any("xToday", xToday))
|
|
|
}
|
|
|
|
|
|
- // 将历史发情预警数据更新为已过期
|
|
|
+ // 将3天前历史发情预警数据更新为已过期
|
|
|
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 {
|
|
|
+ Where("first_time != ?", "").
|
|
|
+ Where("first_time <= ?", time.Now().AddDate(0, 0, -3).Format(model.LayoutTime)).
|
|
|
+ Update("check_result", pasturePb.CheckResult_Overdue).Error; err != nil {
|
|
|
zaplog.Error("EntryCowEstrus", zap.Any("UpdateEventEstrus", err))
|
|
|
}
|
|
|
+
|
|
|
return nil
|
|
|
}
|
|
|
|
|
@@ -74,7 +77,8 @@ func (e *Entry) CowEstrusWarning(pastureId int64, xToday *XToday) (err error) {
|
|
|
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天内的发情牛
|
|
|
+ Where(e.DB.Where("calving_age >= ?", MinCalvingAge).Or("lact = ?", MinLact)). // 排除产后20天内的发情牛
|
|
|
+ Order("cow_id").
|
|
|
Find(&neckActiveHabitList).Error; err != nil {
|
|
|
return xerr.WithStack(err)
|
|
|
}
|
|
@@ -86,9 +90,6 @@ func (e *Entry) CowEstrusWarning(pastureId int64, xToday *XToday) (err error) {
|
|
|
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)
|
|
|
}
|
|
|
|
|
@@ -110,6 +111,7 @@ func (e *Entry) CowEstrusWarning(pastureId int64, xToday *XToday) (err error) {
|
|
|
|
|
|
maxCft := float32(0)
|
|
|
maxHigh := int32(0)
|
|
|
+ lastActiveDate := time.Time{}
|
|
|
for _, habit := range cowHabitList {
|
|
|
cft := calculateCFT(habit)
|
|
|
if cft > maxCft {
|
|
@@ -118,49 +120,59 @@ func (e *Entry) CowEstrusWarning(pastureId int64, xToday *XToday) (err error) {
|
|
|
if habit.FilterHigh > maxHigh {
|
|
|
maxHigh = habit.FilterHigh
|
|
|
}
|
|
|
- }
|
|
|
-
|
|
|
- activeDate := ""
|
|
|
- if len(cowHabitList) > 0 {
|
|
|
- sortHabits := sortHabitsByChangeFilter(cowHabitList)
|
|
|
- activeDate = sortHabits[0].ActiveTime
|
|
|
+ // 获取最新的 CreateTime
|
|
|
+ activeTimeParse, _ := time.Parse(habit.ActiveTime, model.LayoutTime)
|
|
|
+ if activeTimeParse.After(lastActiveDate) {
|
|
|
+ lastActiveDate = activeTimeParse
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
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()
|
|
|
- }
|
|
|
+ t3, _ := time.Parse(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 {
|
|
|
|
|
|
- if (int32(maxCft) > before3Data.DayHigh || b48 > B48) && int32(maxCft)+cowEstrus.HadJust > xToday.ActiveLow {
|
|
|
level := calculateActiveLevel(maxCft, cowEstrus, xToday)
|
|
|
- cowInfo := e.FindCowInfoByNeckRingNumber(cowHabitList[0].NeckRingNumber)
|
|
|
+ cowInfo := e.FindCowInfoByCowId(cowId)
|
|
|
+ if cowInfo == nil {
|
|
|
+ zaplog.Error("CowEstrusWarning", zap.Any("FindCowInfoByCowId", cowId))
|
|
|
+ continue
|
|
|
+ }
|
|
|
isShow := pasturePb.IsShow_Ok
|
|
|
- if cowInfo.IsPregnant == pasturePb.IsShow_Ok && level == pasturePb.EstrusLevel_Low {
|
|
|
+ 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
|
|
|
+
|
|
|
+ cowEstrusStartData := e.FindCowEstrusFirstTime(pastureId, cowId, nowTime)
|
|
|
+ firstTime := ""
|
|
|
+ if cowEstrusStartData != nil {
|
|
|
+ firstTime = cowEstrusStartData.FirstTime
|
|
|
+ }
|
|
|
+
|
|
|
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", activeDate),
|
|
|
+ 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("firstTime", firstTime),
|
|
|
zap.Any("cowHabitList", cowHabitList),
|
|
|
)
|
|
|
- newNeckRingEstrus := model.NewNeckRingEstrus(pastureId, cowInfo, level, checkResult, isShow)
|
|
|
- newNeckRingEstrus.LastEstrusDate = lastEstrusDate
|
|
|
- newNeckRingEstrus.ActiveDate = activeDate
|
|
|
+ newNeckRingEstrus := model.NewNeckRingEstrus(pastureId, cowInfo, level, checkResult, isShow, firstTime)
|
|
|
+ newNeckRingEstrus.LastTime = lastEstrusDate
|
|
|
+ newNeckRingEstrus.ActiveTime = lastActiveDate.Format(model.LayoutTime)
|
|
|
newNeckRingEstrus.DayHigh = dayHigh
|
|
|
newNeckRingEstrus.MaxHigh = maxHigh
|
|
|
newNeckRingEstrus.IsPeak = isPeak
|
|
@@ -175,99 +187,68 @@ func (e *Entry) CowEstrusWarning(pastureId int64, xToday *XToday) (err error) {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- 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("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))
|
|
|
- }
|
|
|
- }
|
|
|
+// FindCowEstrusFirstTime 查找牛只昨天是否有发情数据
|
|
|
+func (e *Entry) FindCowEstrusFirstTime(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 nil
|
|
|
-}
|
|
|
-
|
|
|
-// UpdateIsPeak 更新IsPeak是否是高峰字段 1 是 0 否
|
|
|
-func (e *Entry) UpdateIsPeak(pastureId int64, xToday time.Time) (err error) {
|
|
|
- return nil
|
|
|
+ return firstTimeEventEstrus
|
|
|
}
|
|
|
|
|
|
// 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)
|
|
|
+ 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
|
|
|
}
|
|
|
- cft = cft * float32(habit.FilterCorrect) / 100
|
|
|
- ruminaAdjust := float32(0)
|
|
|
+
|
|
|
switch {
|
|
|
case habit.RuminaFilter > MaxRuminaAdJust:
|
|
|
- ruminaAdjust = float32(5)
|
|
|
+ cft -= float32(5)
|
|
|
case habit.RuminaFilter > 0:
|
|
|
- ruminaAdjust = float32(habit.RuminaFilter) * 0.25
|
|
|
+ cft -= float32(habit.RuminaFilter) * 0.25
|
|
|
case habit.RuminaFilter < -MaxRuminaAdJust:
|
|
|
- ruminaAdjust = -MaxRuminaAdJust * RumtoHeat
|
|
|
+ cft -= -MaxRuminaAdJust * RumtoHeat
|
|
|
default:
|
|
|
- ruminaAdjust = float32(habit.RuminaFilter) * RumtoHeat
|
|
|
+ cft -= float32(habit.RuminaFilter) * RumtoHeat
|
|
|
}
|
|
|
- cft -= ruminaAdjust
|
|
|
return cft
|
|
|
}
|
|
|
|
|
|
// calculateActiveLevel 计算活动量等级
|
|
|
func calculateActiveLevel(cft float32, cowEstrus *CowEstrus, xToday *XToday) pasturePb.EstrusLevel_Kind {
|
|
|
- activeLevel := pasturePb.EstrusLevel_High
|
|
|
if int32(cft)+cowEstrus.HadJust < xToday.ActiveMiddle {
|
|
|
- activeLevel = pasturePb.EstrusLevel_Low
|
|
|
+ return pasturePb.EstrusLevel_Low
|
|
|
+ } else if int32(cft)+cowEstrus.HadJust >= xToday.ActiveHigh {
|
|
|
+ return pasturePb.EstrusLevel_Middle
|
|
|
+ } else {
|
|
|
+ return pasturePb.EstrusLevel_High
|
|
|
}
|
|
|
-
|
|
|
- if int32(cft)+cowEstrus.HadJust >= xToday.ActiveHigh {
|
|
|
- activeLevel = pasturePb.EstrusLevel_Middle
|
|
|
- }
|
|
|
- return activeLevel
|
|
|
}
|
|
|
|
|
|
-// getResult 根据b3数据计算结果 0 1 2 3 -1 -2
|
|
|
+// getResult 根据b3数据计算结果
|
|
|
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
|
|
|
- }
|
|
|
+ result := pasturePb.CheckResult_Pending
|
|
|
|
|
|
- if b3.CheckResult == pasturePb.CheckResult_Overdue {
|
|
|
+ if b3.CheckResult == pasturePb.CheckResult_Correct {
|
|
|
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]
|
|
|
- }
|
|
|
- }
|
|
|
+ if b3.CheckResult == pasturePb.CheckResult_Fail && b3.DayHigh > int32(cft)+cowEstrus.HadJust {
|
|
|
+ result = pasturePb.CheckResult_Fail
|
|
|
}
|
|
|
- return sorted
|
|
|
+
|
|
|
+ return result
|
|
|
}
|