|
@@ -0,0 +1,261 @@
|
|
|
|
+package crontab
|
|
|
|
+
|
|
|
|
+import (
|
|
|
|
+ "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"
|
|
|
|
+)
|
|
|
|
+
|
|
|
|
+func (e *Entry) CowNeckRingErrorEnter() (err error) {
|
|
|
|
+ pastureList := e.FindPastureList()
|
|
|
|
+ if pastureList == nil || len(pastureList) == 0 {
|
|
|
|
+ return nil
|
|
|
|
+ }
|
|
|
|
+ for _, pasture := range pastureList {
|
|
|
|
+ e.DB.Model(new(model.NeckRingError)).Delete(new(model.NeckRingError)).Where("pasture_id = ?", pasture.Id)
|
|
|
|
+ e.CowNeckRingError(pasture.Id)
|
|
|
|
+ zaplog.Error("CowNeckRingErrorEnter-Success", zap.Any("pasture", pasture))
|
|
|
|
+ }
|
|
|
|
+ return nil
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+func (e *Entry) CowNeckRingError(pastureId int64) {
|
|
|
|
+ yesterday := time.Now().Local().AddDate(0, 0, -1).Format(model.LayoutDate2)
|
|
|
|
+ habitMinId, originalMinId := 0, 0
|
|
|
|
+ if err := e.DB.Model(new(model.NeckActiveHabit)).
|
|
|
|
+ Select("MIN(id) as id").
|
|
|
|
+ Where("pasture_id = ?", pastureId).
|
|
|
|
+ Where("heat_date = ?", yesterday).
|
|
|
|
+ Scan(&habitMinId).Error; err != nil {
|
|
|
|
+ zaplog.Error("CowNeckRingError-Error", zap.Any("pastureId", pastureId), zap.Any("err", err))
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if err := e.DB.Model(new(model.NeckRingOriginal)).
|
|
|
|
+ Select("MIN(id) as id").
|
|
|
|
+ Where("pasture_id = ?", pastureId).
|
|
|
|
+ Where("heat_date = ?", yesterday).
|
|
|
|
+ Scan(&originalMinId).Error; err != nil {
|
|
|
|
+ zaplog.Error("CowNeckRingError-Error", zap.Any("pastureId", pastureId), zap.Any("err", err))
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ minIsBindDate := util.TimeParseLocalUnix(yesterday)
|
|
|
|
+ neckRingList := make([]*model.NeckRing, 0)
|
|
|
|
+ if err := e.DB.Model(new(model.NeckRingError)).
|
|
|
|
+ Where("pasture_id = ?", pastureId).
|
|
|
|
+ Where("is_bind = ?", pasturePb.IsShow_Ok).
|
|
|
|
+ Where("wear_at <= ?", minIsBindDate).
|
|
|
|
+ Find(&neckRingList).Error; err != nil {
|
|
|
|
+ zaplog.Error("NeckRingErrorOfNoSignal", zap.Any("err", err), zap.Any("pastureId", pastureId))
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ updateNeckRingMap := make(map[int64]*model.NeckRingStats)
|
|
|
|
+ errorMap := e.NeckRingErrorMap()
|
|
|
|
+ for _, neckRing := range neckRingList {
|
|
|
|
+ c1 := e.NeckRingErrorOfNoSignal(pastureId, neckRing, int64(habitMinId), yesterday)
|
|
|
|
+ if c1 > 0 {
|
|
|
|
+ updateNeckRingMap[neckRing.Id] = &model.NeckRingStats{
|
|
|
|
+ ErrorKind: c1,
|
|
|
|
+ ErrorReason: errorMap[c1],
|
|
|
|
+ Describe: "",
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ c2 := e.NeckRingErrorOfSuspectedFallOffAndLowBattery(pastureId, neckRing, int64(habitMinId), yesterday)
|
|
|
|
+ if c2 > 0 {
|
|
|
|
+ updateNeckRingMap[neckRing.Id] = &model.NeckRingStats{
|
|
|
|
+ ErrorKind: c2,
|
|
|
|
+ ErrorReason: errorMap[c2],
|
|
|
|
+ Describe: "",
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ c3 := e.NeckRingErrorOfReceivingLess(pastureId, neckRing, int64(habitMinId), int64(originalMinId), yesterday)
|
|
|
|
+ if c3 > 0 {
|
|
|
|
+ updateNeckRingMap[neckRing.Id] = &model.NeckRingStats{
|
|
|
|
+ ErrorKind: c3,
|
|
|
|
+ ErrorReason: errorMap[c3],
|
|
|
|
+ Describe: "",
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ c4 := e.NeckRingErrorOfDataLatency(pastureId, neckRing, int64(habitMinId), yesterday)
|
|
|
|
+ if c4 > 0 {
|
|
|
|
+ updateNeckRingMap[neckRing.Id] = &model.NeckRingStats{
|
|
|
|
+ ErrorKind: c4,
|
|
|
|
+ ErrorReason: errorMap[c4],
|
|
|
|
+ Describe: "",
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if len(updateNeckRingMap) > 0 {
|
|
|
|
+ for id, v := range updateNeckRingMap {
|
|
|
|
+ if err := e.DB.Model(new(model.NeckRing)).
|
|
|
|
+ Where("id = ?", id).
|
|
|
|
+ Updates(map[string]interface{}{
|
|
|
|
+ "status": pasturePb.IsShow_No,
|
|
|
|
+ "error_kind": v.ErrorKind,
|
|
|
|
+ "error_reason": v.ErrorReason,
|
|
|
|
+ "describe": v.Describe,
|
|
|
|
+ }).Error; err != nil {
|
|
|
|
+ zaplog.Error("CowNeckRingError", zap.Any("err", err), zap.Any("id", id), zap.Any("v", v))
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// NeckRingErrorOfNoSignal 佩戴后无信号
|
|
|
|
+func (e *Entry) NeckRingErrorOfNoSignal(pastureId int64, neckRing *model.NeckRing, minId int64, dateTime string) pasturePb.NeckRingNumberError_Kind {
|
|
|
|
+ var count int64
|
|
|
|
+ if err := e.DB.Model(new(model.NeckActiveHabit)).
|
|
|
|
+ Where("id >= ?", minId).
|
|
|
|
+ Where("neck_ring_number = ?", neckRing.NeckRingNumber).
|
|
|
|
+ Where("pasture_id = ?", pastureId).
|
|
|
|
+ Where("heat_date >= ?", dateTime).
|
|
|
|
+ Count(&count).Error; err != nil {
|
|
|
|
+ zaplog.Error("NeckRingErrorOfNoSignal", zap.Any("err", err), zap.Any("pastureId", pastureId))
|
|
|
|
+ }
|
|
|
|
+ if count <= 0 {
|
|
|
|
+ return pasturePb.NeckRingNumberError_No_Signal
|
|
|
|
+ }
|
|
|
|
+ return pasturePb.NeckRingNumberError_Invalid
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// NeckRingErrorOfSuspectedFallOffAndLowBattery 插入异常脖环 '疑似脱落', '电量低','接收少'
|
|
|
|
+func (e *Entry) NeckRingErrorOfSuspectedFallOffAndLowBattery(pastureId int64, neckRing *model.NeckRing, minId int64, dateTime string) pasturePb.NeckRingNumberError_Kind {
|
|
|
|
+ nowTime := time.Now().Local()
|
|
|
|
+
|
|
|
|
+ neckRingHabitList := make([]*model.NeckActiveHabit, 0)
|
|
|
|
+ if err := e.DB.Model(new(model.NeckActiveHabit)).
|
|
|
|
+ Select(``).
|
|
|
|
+ Where("id >= ?", minId).
|
|
|
|
+ Where("neck_ring_number = ?", neckRing.NeckRingNumber).
|
|
|
|
+ Where("pasture_id = ?", pastureId).
|
|
|
|
+ Where("heat_date >= ?", dateTime).
|
|
|
|
+ Find(&neckRingHabitList).Error; err != nil {
|
|
|
|
+ zaplog.Error("suspectedFallOffAndLowBattery", zap.Any("err", err), zap.Any("pastureId", pastureId))
|
|
|
|
+ return pasturePb.NeckRingNumberError_Invalid
|
|
|
|
+ }
|
|
|
|
+ nba, nb1, nbHh, nbh, voltage := 0, 0, 0, 0, int32(0)
|
|
|
|
+ for _, v := range neckRingHabitList {
|
|
|
|
+ nba++
|
|
|
|
+ if v.High <= 50 && v.Rumina <= 5 {
|
|
|
|
+ nb1 += 1
|
|
|
|
+ }
|
|
|
|
+ at := util.DateTimeParseLocalUnix(v.ActiveTime)
|
|
|
|
+ nowTimeUnix := nowTime.Unix()
|
|
|
|
+ hoursDiff := (nowTimeUnix - at) / 3600
|
|
|
|
+ sumRuminaIntake := v.SumRumina + v.SumIntake
|
|
|
|
+ if hoursDiff <= 8 && (v.High > 0 || sumRuminaIntake > 20) {
|
|
|
|
+ nbHh++
|
|
|
|
+ }
|
|
|
|
+ if v.High > 100 || v.Rumina > 5 {
|
|
|
|
+ nbh++
|
|
|
|
+ }
|
|
|
|
+ voltage += v.Voltage
|
|
|
|
+ }
|
|
|
|
+ svgVoltage := int32(float32(voltage) / float32(nba))
|
|
|
|
+ errorKind := pasturePb.NeckRingNumberError_Invalid
|
|
|
|
+ switch {
|
|
|
|
+ case svgVoltage <= 275:
|
|
|
|
+ errorKind = pasturePb.NeckRingNumberError_Low_Battery
|
|
|
|
+ case nb1 >= 4:
|
|
|
|
+ errorKind = pasturePb.NeckRingNumberError_Suspected_Fall_Off
|
|
|
|
+ default:
|
|
|
|
+ errorKind = pasturePb.NeckRingNumberError_Receiving_Less
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (nb1 >= 4 && nbHh <= 1) || svgVoltage <= 275 || nba <= (5+nowTime.Hour()/4-1) {
|
|
|
|
+ return errorKind
|
|
|
|
+ }
|
|
|
|
+ return pasturePb.NeckRingNumberError_Invalid
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// NeckRingErrorOfReceivingLess '接收少'
|
|
|
|
+func (e *Entry) NeckRingErrorOfReceivingLess(pastureId int64, neckRing *model.NeckRing, habitMinId, originalMinId int64, dateTime string) pasturePb.NeckRingNumberError_Kind {
|
|
|
|
+ var count int64
|
|
|
|
+ if err := e.DB.Model(new(model.NeckRing)).
|
|
|
|
+ Where("pasture_id = ?", pastureId).
|
|
|
|
+ Where("neck_ring_number = ?", neckRing.NeckRingNumber).
|
|
|
|
+ Where("status = ?", pasturePb.IsShow_No).
|
|
|
|
+ Count(&count).Error; err != nil {
|
|
|
|
+ zaplog.Error("NeckRingErrorOfReceivingLess", zap.Any("err", err), zap.Any("pastureId", pastureId))
|
|
|
|
+ return pasturePb.NeckRingNumberError_Invalid
|
|
|
|
+ }
|
|
|
|
+ // 已存在就不再处理
|
|
|
|
+ if count > 0 {
|
|
|
|
+ return pasturePb.NeckRingNumberError_Invalid
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if err := e.DB.Model(new(model.NeckActiveHabit)).
|
|
|
|
+ Where("id >= ?", habitMinId).
|
|
|
|
+ Where("neck_ring_number = ?", neckRing.NeckRingNumber).
|
|
|
|
+ Where("pasture_id = ?", pastureId).
|
|
|
|
+ Where("heat_date >= ?", dateTime).
|
|
|
|
+ Count(&count).Error; err != nil {
|
|
|
|
+ zaplog.Error("NeckRingErrorOfReceivingLess", zap.Any("err", err), zap.Any("pastureId", pastureId))
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 已存在就不再处理
|
|
|
|
+ if count > 0 {
|
|
|
|
+ return pasturePb.NeckRingNumberError_Invalid
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 查询原始活动数据统计信息
|
|
|
|
+ var originalStats struct {
|
|
|
|
+ Count int64
|
|
|
|
+ AvgVoltage float64
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if err := e.DB.Model(new(model.NeckRingOriginal)).
|
|
|
|
+ Select("COUNT(*) as count, ROUND(AVG(voltage)) as avg_voltage").
|
|
|
|
+ Where("id >= ?", originalMinId).
|
|
|
|
+ Where("neck_ring_number = ?", neckRing.NeckRingNumber).
|
|
|
|
+ Where("pasture_id = ?", pastureId).
|
|
|
|
+ Where("heat_date >= ?", dateTime).
|
|
|
|
+ First(&originalStats).Error; err != nil {
|
|
|
|
+ zaplog.Error("NeckRingOriginal", zap.Any("err", err), zap.Any("pasture_id", pastureId))
|
|
|
|
+ return pasturePb.NeckRingNumberError_Invalid
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 创建错误记录
|
|
|
|
+ errorKind := pasturePb.NeckRingNumberError_Receiving_Less
|
|
|
|
+ if originalStats.AvgVoltage <= 285 {
|
|
|
|
+ errorKind = pasturePb.NeckRingNumberError_Low_Battery
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 限制最大显示数量为99
|
|
|
|
+ displayCount := originalStats.Count
|
|
|
|
+ if displayCount > 99 {
|
|
|
|
+ displayCount = 99
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if displayCount > 0 {
|
|
|
|
+ return errorKind
|
|
|
|
+ }
|
|
|
|
+ return pasturePb.NeckRingNumberError_Invalid
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// NeckRingErrorOfDataLatency 数据延迟
|
|
|
|
+func (e *Entry) NeckRingErrorOfDataLatency(pastureId int64, neckRing *model.NeckRing, habitMinId int64, dateTime string) pasturePb.NeckRingNumberError_Kind {
|
|
|
|
+ var count int64
|
|
|
|
+ if err := e.DB.Model(new(model.NeckActiveHabit)).
|
|
|
|
+ Select(`h.cow_id, h.ear_number, SUM(IF(TIMESTAMPDIFF(HOUR, UNIX_TIMESTAMP(h.active_time), h.created_at)>9, 1, 0)) AS nb, COUNT(1) AS nba, ROUND(AVG(h.voltage), 0) AS voltage`).
|
|
|
|
+ Where("id >= ?", habitMinId).
|
|
|
|
+ Where("cow_id = ?", neckRing.CowId).
|
|
|
|
+ Where("filter_high > ?", 200).
|
|
|
|
+ Where("heat_date >= ?", dateTime).
|
|
|
|
+ Having("nb/nba >= ?", 0.7).
|
|
|
|
+ Count(&count).Error; err != nil {
|
|
|
|
+ zaplog.Error("NeckRingErrorOfDataLatency", zap.Any("err", err), zap.Any("pastureId", pastureId))
|
|
|
|
+ }
|
|
|
|
+ if count > 0 {
|
|
|
|
+ return pasturePb.NeckRingNumberError_Data_Latency
|
|
|
|
+ }
|
|
|
|
+ return pasturePb.NeckRingNumberError_Invalid
|
|
|
|
+}
|