pen_behavior.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408
  1. package crontab
  2. import (
  3. "fmt"
  4. "kpt-pasture/model"
  5. "math"
  6. "sort"
  7. "time"
  8. pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
  9. "gitee.com/xuyiping_admin/pkg/logger/zaplog"
  10. "go.uber.org/zap"
  11. )
  12. func (e *Entry) UpdatePenBehavior() error {
  13. pastureList := e.FindPastureList()
  14. if pastureList == nil || len(pastureList) == 0 {
  15. return nil
  16. }
  17. for _, pasture := range pastureList {
  18. conf, err := e.GetSystemNeckRingConfigure(pasture.Id, model.MaxPenBehavior)
  19. if err != nil {
  20. zaplog.Error("UpdatePenBehavior", zap.Any("pasture", pasture), zap.Any("err", err))
  21. continue
  22. }
  23. e.PenBehavior(pasture.Id, conf.Value)
  24. e.UpdatePenBehaviorWeekData(pasture.Id)
  25. }
  26. return nil
  27. }
  28. // PenBehavior 栏舍行为曲线
  29. func (e *Entry) PenBehavior(pastureId, maxPenBehavior int64) {
  30. // 1. 获取颈环原始数据
  31. penBehaviorModelList, err := e.getNeckRingOriginalList(pastureId, maxPenBehavior)
  32. if err != nil {
  33. zaplog.Error("PenBehavior",
  34. zap.Any("pastureId", pastureId),
  35. zap.Any("maxPenBehavior", maxPenBehavior),
  36. zap.Any("err", err),
  37. )
  38. return
  39. }
  40. if len(penBehaviorModelList) <= 0 {
  41. return
  42. }
  43. // 2. 处理栏舍行为数据
  44. penData := e.processPenBehaviorData(penBehaviorModelList)
  45. // 3. 计算平均值和百分比
  46. e.calculateAveragesAndRates(penData)
  47. // 4. 保存数据
  48. if err = e.savePenBehaviorData(penData); err != nil {
  49. zaplog.Error("PenBehavior", zap.Any("penData", penData), zap.Any("err", err))
  50. return
  51. }
  52. sort.Slice(penBehaviorModelList, func(i, j int) bool {
  53. return penBehaviorModelList[i].Id > penBehaviorModelList[j].Id
  54. })
  55. if err = e.UpdateSystemNeckRingConfigure(pastureId, model.MaxPenBehavior, penBehaviorModelList[0].Id); err != nil {
  56. zaplog.Error("PenBehavior", zap.Any("MaxPenBehavior", err), zap.Any("penBehaviorModelList", penBehaviorModelList))
  57. }
  58. }
  59. // getNeckRingOriginalList 获取颈环原始数据
  60. func (e *Entry) getNeckRingOriginalList(pastureId, maxPenBehavior int64) ([]*model.PenBehaviorModel, error) {
  61. var penBehaviorModelList []*model.PenBehaviorModel
  62. if err := e.DB.Table(fmt.Sprintf("%s as h", new(model.NeckRingOriginal).TableName())).
  63. Joins("INNER JOIN cow as c ON h.pasture_id = c.pasture_id AND h.neck_ring_number = c.neck_ring_number").
  64. Select("h.id,c.pasture_id, c.pen_id, c.pen_name, h.active_date, h.frameid, h.high, h.rumina, h.intake, h.inactive, h.gasp").
  65. Where("h.id > ? AND h.pasture_id = ?", maxPenBehavior, pastureId).
  66. Order("h.active_date,h.frameid").
  67. Limit(int(defaultLimit)).
  68. Find(&penBehaviorModelList).Error; err != nil {
  69. return nil, err
  70. }
  71. return penBehaviorModelList, nil
  72. }
  73. // processPenBehaviorData 处理栏舍行为数据
  74. func (e *Entry) processPenBehaviorData(penBehaviorModelList []*model.PenBehaviorModel) map[string]*model.PenBehaviorData {
  75. // 按active_date和frameid分组
  76. activeDateFrameIdMap := make(map[string][]*model.PenBehaviorModel)
  77. for _, v := range penBehaviorModelList {
  78. key := fmt.Sprintf("%s_%d", v.ActiveDate, v.Frameid)
  79. if activeDateFrameIdMap[key] == nil {
  80. activeDateFrameIdMap[key] = make([]*model.PenBehaviorModel, 0)
  81. }
  82. activeDateFrameIdMap[key] = append(activeDateFrameIdMap[key], v)
  83. }
  84. // 按pen_id分组统计
  85. penData := make(map[string]*model.PenBehaviorData)
  86. for _, v := range activeDateFrameIdMap {
  87. // 按pen_id分组
  88. penIdMap := make(map[int32]*model.PenBehaviorData)
  89. for _, item := range v {
  90. if data, exists := penIdMap[item.PenId]; exists {
  91. // 更新计数
  92. data.CowCount++
  93. // 更新平均值
  94. data.AvgHigh += item.High
  95. // 更新行为统计
  96. data.SumRumina += ifThenElse(item.Rumina >= 8, 1, 0)
  97. data.SumIntake += ifThenElse(item.Intake >= 8, 1, 0)
  98. data.SumRest += ifThenElse(item.Inactive >= 8, 1, 0)
  99. data.SumGasp += ifThenElse(item.Gasp >= 8, 1, 0)
  100. } else {
  101. penIdMap[item.PenId] = &model.PenBehaviorData{
  102. PastureId: item.PastureId,
  103. PenId: item.PenId,
  104. PenName: item.PenName,
  105. HeatDate: item.ActiveDate,
  106. Frameid: item.Frameid,
  107. CowCount: 1,
  108. AvgHigh: item.High,
  109. SumRumina: ifThenElse(item.Rumina >= 8, 1, 0),
  110. SumIntake: ifThenElse(item.Intake >= 8, 1, 0),
  111. SumRest: ifThenElse(item.Inactive >= 8, 1, 0),
  112. SumGasp: ifThenElse(item.Gasp >= 8, 1, 0),
  113. }
  114. }
  115. }
  116. // 将penIdMap的数据合并到penData中
  117. for penId, data := range penIdMap {
  118. key := fmt.Sprintf("%s_%d_%d", data.HeatDate, penId, data.Frameid)
  119. penData[key] = data
  120. }
  121. }
  122. return penData
  123. }
  124. // calculateAveragesAndRates 计算平均值和百分比
  125. func (e *Entry) calculateAveragesAndRates(penData map[string]*model.PenBehaviorData) {
  126. for _, data := range penData {
  127. // 计算平均值
  128. data.AvgHigh = data.AvgHigh / data.CowCount
  129. // 计算百分比
  130. if data.CowCount > 0 {
  131. data.RuminaRate = int32(float64(data.SumRumina) / float64(data.CowCount) * 100)
  132. data.IntakeRate = int32(float64(data.SumIntake) / float64(data.CowCount) * 100)
  133. data.RestRate = int32(float64(data.SumRest) / float64(data.CowCount) * 100)
  134. data.GaspRate = int32(float64(data.SumGasp) / float64(data.CowCount) * 100)
  135. }
  136. }
  137. }
  138. // savePenBehaviorData 保存栏舍行为数据
  139. func (e *Entry) savePenBehaviorData(penData map[string]*model.PenBehaviorData) error {
  140. for _, data := range penData {
  141. // 构建活动时间
  142. activeTime := e.calculateActiveTime(data.HeatDate, data.Frameid)
  143. // 构建保存数据
  144. penBehavior := model.NewPenBehavior(data, activeTime)
  145. if e.isExistByPenBehavior(data.PastureId, data.HeatDate, data.PenId, data.Frameid) {
  146. historyData := e.findPenBehavior(data.PastureId, data.HeatDate, data.PenId, data.Frameid)
  147. if historyData == nil || historyData.Id <= 0 {
  148. continue
  149. }
  150. // 计算新的总和和平均值
  151. newCowCount := historyData.CowCount + penBehavior.CowCount
  152. newAvgHigh := (historyData.AvgHigh*historyData.CowCount + penBehavior.AvgHigh*penBehavior.CowCount) / newCowCount
  153. newSumRumina := historyData.SumRumina + penBehavior.SumRumina
  154. newSumIntake := historyData.SumIntake + penBehavior.SumIntake
  155. newSumRest := historyData.SumRest + penBehavior.SumRest
  156. newSumGasp := historyData.SumGasp + penBehavior.SumGasp
  157. if err := e.DB.Model(new(model.PenBehavior)).
  158. Where("id = ?", historyData.Id).
  159. Updates(map[string]interface{}{
  160. "cow_count": newCowCount,
  161. "avg_high": newAvgHigh,
  162. "sum_rumina": newSumRumina,
  163. "sum_intake": newSumIntake,
  164. "sum_rest": newSumRest,
  165. "sum_gasp": newSumGasp,
  166. "rumina_rate": int32(float64(newSumRumina) / float64(newCowCount) * 100),
  167. "intake_rate": int32(float64(newSumIntake) / float64(newCowCount) * 100),
  168. "rest_rate": int32(float64(newSumRest) / float64(newCowCount) * 100),
  169. "gasp_rate": int32(float64(newSumGasp) / float64(newCowCount) * 100),
  170. }).Error; err != nil {
  171. zaplog.Error("savePenBehaviorData", zap.Any("penBehavior", penBehavior), zap.Any("err", err))
  172. }
  173. continue
  174. }
  175. if err := e.DB.Model(new(model.PenBehavior)).
  176. Create(penBehavior).Error; err != nil {
  177. zaplog.Error("savePenBehaviorData", zap.Any("penBehavior", penBehavior), zap.Any("err", err))
  178. }
  179. }
  180. return nil
  181. }
  182. func (e *Entry) UpdatePenBehaviorWeekData(pastureId int64) {
  183. penBehaviorList := e.findWeekPenBehaviorList(pastureId)
  184. if len(penBehaviorList) == 0 {
  185. return
  186. }
  187. // 按日期和frameid排序
  188. sort.Slice(penBehaviorList, func(i, j int) bool {
  189. if penBehaviorList[i].HeatDate == penBehaviorList[j].HeatDate {
  190. return penBehaviorList[i].Frameid < penBehaviorList[j].Frameid
  191. }
  192. return penBehaviorList[i].HeatDate < penBehaviorList[j].HeatDate
  193. })
  194. // 处理每个日期和frameid的数据
  195. for _, item := range penBehaviorList {
  196. currDate := item.HeatDate
  197. currFrameid := item.Frameid
  198. // 计算开始和结束日期
  199. currTime, err := time.Parse(model.LayoutDate2, currDate)
  200. if err != nil {
  201. zaplog.Error("UpdatePenBehaviorWeekData", zap.Error(err))
  202. continue
  203. }
  204. startTime := currTime.AddDate(0, 0, -7).Format(model.LayoutDate2)
  205. endTime := currTime.AddDate(0, 0, -1).Format(model.LayoutDate2)
  206. // 获取历史数据
  207. historyList := e.findHistoryPenBehaviorList(pastureId, startTime, endTime, currFrameid)
  208. if len(historyList) == 0 {
  209. // 如果没有历史数据,将所有记录标记为-1
  210. if err = e.DB.Model(new(model.PenBehavior)).
  211. Where("id = ?", item.Id).
  212. Updates(map[string]interface{}{
  213. "week_rumina_rate": -1,
  214. "week_intake_rate": -1,
  215. "week_rest_rate": -1,
  216. "week_gasp_rate": -1,
  217. "is_show": pasturePb.IsShow_Ok,
  218. }).Error; err != nil {
  219. zaplog.Error("UpdatePenBehaviorWeekData", zap.Error(err))
  220. }
  221. continue
  222. }
  223. // 按pen_id分组计算统计数据
  224. penStats := make(map[int32]*model.PenBehaviorWeekModel)
  225. for _, v := range historyList {
  226. if stats, exists := penStats[v.PenId]; exists {
  227. stats.CowCount++
  228. stats.SumRumina += v.RuminaRate
  229. stats.SumIntake += v.IntakeRate
  230. stats.SumRest += v.RestRate
  231. stats.SumGasp += v.GaspRate
  232. // 计算标准差
  233. stats.RuminaRate = append(stats.RuminaRate, float64(v.RuminaRate))
  234. stats.IntakeRate = append(stats.IntakeRate, float64(v.IntakeRate))
  235. stats.RestRate = append(stats.RestRate, float64(v.RestRate))
  236. stats.GaspRate = append(stats.GaspRate, float64(v.GaspRate))
  237. } else {
  238. penStats[v.PenId] = &model.PenBehaviorWeekModel{
  239. CowCount: 1,
  240. SumRumina: v.RuminaRate,
  241. SumIntake: v.IntakeRate,
  242. SumRest: v.RestRate,
  243. SumGasp: v.GaspRate,
  244. RuminaRate: []float64{float64(v.RuminaRate)},
  245. IntakeRate: []float64{float64(v.IntakeRate)},
  246. RestRate: []float64{float64(v.RestRate)},
  247. GaspRate: []float64{float64(v.GaspRate)},
  248. }
  249. }
  250. }
  251. // 更新当前记录
  252. if stats, exists := penStats[item.PenId]; exists {
  253. if err = e.DB.Model(new(model.PenBehavior)).
  254. Where("id = ?", item.Id).
  255. Updates(map[string]interface{}{
  256. "week_rumina_rate": int32(float64(stats.SumRumina) / float64(stats.CowCount)),
  257. "week_intake_rate": int32(float64(stats.SumIntake) / float64(stats.CowCount)),
  258. "week_rest_rate": int32(float64(stats.SumRest) / float64(stats.CowCount)),
  259. "week_gasp_rate": int32(float64(stats.SumGasp) / float64(stats.CowCount)),
  260. "rumina_std": int32(calculateStd(stats.RuminaRate)),
  261. "intake_std": int32(calculateStd(stats.IntakeRate)),
  262. "rest_std": int32(calculateStd(stats.RestRate)),
  263. "gasp_std": int32(calculateStd(stats.GaspRate)),
  264. "is_show": pasturePb.IsShow_Ok,
  265. }).Error; err != nil {
  266. zaplog.Error("UpdatePenBehaviorWeekData", zap.Error(err))
  267. }
  268. } else {
  269. // 如果没有历史数据,标记为-1
  270. if err = e.DB.Model(new(model.PenBehavior)).
  271. Where("id = ?", item.Id).
  272. Updates(map[string]interface{}{
  273. "week_rumina_rate": -1,
  274. "week_intake_rate": -1,
  275. "week_rest_rate": -1,
  276. "week_gasp_rate": -1,
  277. "is_show": pasturePb.IsShow_Ok,
  278. }).Error; err != nil {
  279. zaplog.Error("UpdatePenBehaviorWeekData", zap.Error(err))
  280. }
  281. }
  282. }
  283. }
  284. // findHistoryPenBehaviorList 获取历史数据
  285. func (e *Entry) findHistoryPenBehaviorList(pastureId int64, startTime, endTime string, frameid int32) []*model.PenBehavior {
  286. res := make([]*model.PenBehavior, 0)
  287. if err := e.DB.Model(new(model.PenBehavior)).
  288. Where("pasture_id = ?", pastureId).
  289. Where("heat_date BETWEEN ? AND ?", startTime, endTime).
  290. Where("frameid = ?", frameid).
  291. Find(&res).Error; err != nil {
  292. zaplog.Error("findHistoryPenBehaviorList", zap.Error(err))
  293. return nil
  294. }
  295. return res
  296. }
  297. // calculateStd 计算标准差
  298. func calculateStd(values []float64) float64 {
  299. if len(values) == 0 {
  300. return 0
  301. }
  302. // 计算平均值
  303. var sum float64
  304. for _, v := range values {
  305. sum += v
  306. }
  307. mean := sum / float64(len(values))
  308. // 计算方差
  309. var variance float64
  310. for _, v := range values {
  311. diff := v - mean
  312. variance += diff * diff
  313. }
  314. variance /= float64(len(values))
  315. // 返回标准差
  316. return math.Sqrt(variance)
  317. }
  318. // calculateActiveTime 计算活动时间
  319. func (e *Entry) calculateActiveTime(heatDate string, frameid int32) string {
  320. // 计算小时和分钟
  321. hour := (frameid / 10) * 2
  322. minute := (frameid%10)*20 - 1
  323. if minute < 0 {
  324. minute = 0
  325. }
  326. baseDate, err := time.Parse(model.LayoutDate2, heatDate)
  327. if err != nil {
  328. zaplog.Error("PenBehavior", zap.Any("calculateActiveTime", err))
  329. return ""
  330. }
  331. baseTime := time.Date(baseDate.Year(), baseDate.Month(), baseDate.Day(), int(hour), 0, 0, 0, baseDate.Location())
  332. finalTime := baseTime.Add(time.Duration(minute) * time.Minute)
  333. // 构建时间字符串
  334. return finalTime.Format(model.LayoutTime)
  335. }
  336. // isExistByPenBehavior 是否存在
  337. func (e *Entry) isExistByPenBehavior(pastureId int64, heatDate string, penId int32, frameid int32) bool {
  338. var count int64
  339. if err := e.DB.Model(new(model.PenBehavior)).
  340. Where("pasture_id = ? AND heat_date = ? AND frameid = ? AND pen_id = ?", pastureId, heatDate, frameid, penId).
  341. Count(&count).Error; err != nil {
  342. return false
  343. }
  344. return count > 0
  345. }
  346. func (e *Entry) findPenBehavior(pastureId int64, heatDate string, penId int32, frameid int32) *model.PenBehavior {
  347. res := &model.PenBehavior{}
  348. if err := e.DB.Model(new(model.PenBehavior)).
  349. Where("pasture_id = ? AND heat_date = ? AND frameid = ? AND pen_id = ?", pastureId, heatDate, penId, frameid).
  350. First(res).Error; err != nil {
  351. return nil
  352. }
  353. return res
  354. }
  355. func (e *Entry) findWeekPenBehaviorList(pastureId int64) []*model.PenBehavior {
  356. res := make([]*model.PenBehavior, 0)
  357. if err := e.DB.Model(new(model.PenBehavior)).
  358. Where("pasture_id = ?", pastureId).
  359. Where("is_show = ?", pasturePb.IsShow_No).
  360. Where("cow_count >= ?", model.PenBehaviorMinCowCount).
  361. Limit(int(defaultLimit)).
  362. Find(&res).Error; err != nil {
  363. return nil
  364. }
  365. return res
  366. }
  367. // ifThenElse 条件判断函数
  368. func ifThenElse(condition bool, a, b int32) int32 {
  369. if condition {
  370. return a
  371. }
  372. return b
  373. }