You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

305 satır
16KB

  1. using HealthMonitor.Common;
  2. using HealthMonitor.Common.helper;
  3. using HealthMonitor.Model.Service.Mapper;
  4. using HealthMonitor.Service.Biz;
  5. using HealthMonitor.Service.Biz.db;
  6. using HealthMonitor.Service.Cache;
  7. using HealthMonitor.Service.Etcd;
  8. using HealthMonitor.Service.Resolver.Interface;
  9. using HealthMonitor.Service.Sub;
  10. using HealthMonitor.Service.Sub.Topic.Model;
  11. using Microsoft.EntityFrameworkCore.Metadata;
  12. using Microsoft.Extensions.Logging;
  13. using Newtonsoft.Json;
  14. using System;
  15. using System.Collections.Generic;
  16. using System.Linq;
  17. using System.Text;
  18. using System.Threading.Tasks;
  19. using TelpoDataService.Util.Clients;
  20. using TelpoDataService.Util.Entities.GpsLocationHistory;
  21. namespace HealthMonitor.Service.Resolver
  22. {
  23. public class PregnancyHeartRateResolver : IResolver
  24. {
  25. private readonly ILogger<PregnancyHeartRateResolver> _logger;
  26. private readonly TDengineService _serviceTDengine;
  27. private readonly DeviceCacheManager _deviceCacheMgr;
  28. private readonly IotApiService _serviceIotApi;
  29. private readonly AsyncLocal<string> _messageId = new();
  30. private readonly AsyncLocal<HisGpsHeartRate> _msgData = new();
  31. private readonly HttpHelper _httpHelper = default!;
  32. private readonly EtcdService _serviceEtcd;
  33. private readonly GpsLocationHistoryAccessorClient<HisGpsFetalHeartRate> _hisFetalHeartApiClient;
  34. public PregnancyHeartRateResolver(ILogger<PregnancyHeartRateResolver> logger,
  35. HttpHelper httpHelper, EtcdService serviceEtcd, DeviceCacheManager deviceCacheMgr,
  36. IotApiService iotApiService, TDengineService serviceDengine, GpsLocationHistoryAccessorClient<HisGpsFetalHeartRate> hisFetalHeartApiClient)
  37. {
  38. _logger = logger;
  39. _httpHelper = httpHelper;
  40. _serviceEtcd = serviceEtcd;
  41. _serviceTDengine = serviceDengine;
  42. _deviceCacheMgr = deviceCacheMgr;
  43. _serviceIotApi = iotApiService;
  44. _hisFetalHeartApiClient = hisFetalHeartApiClient;
  45. }
  46. public void SetResolveInfo(PackageMsgModel msg)
  47. {
  48. var topicHmPregnancyHeartRate = JsonConvert.DeserializeObject<TopicHmPregnancyHeartRate>(msg.DetailData.ToString()!);
  49. _messageId.Value = msg.MessageId;
  50. _msgData.Value = new HisGpsHeartRate()
  51. {
  52. HeartRateId = topicHmPregnancyHeartRate!.PregnancyHeartRateId,
  53. MessageId = topicHmPregnancyHeartRate!.MessageId,
  54. Serialno = topicHmPregnancyHeartRate!.Serialno,
  55. HeartRate= topicHmPregnancyHeartRate.PregnancyHeartRate,
  56. LastUpdate = DateTimeUtil.GetDateTimeFromUnixTimeMilliseconds(SafeType.SafeInt64(topicHmPregnancyHeartRate.LastUpdate) / 1000000),
  57. CreateTime = DateTimeUtil.GetDateTimeFromUnixTimeMilliseconds(SafeType.SafeInt64(topicHmPregnancyHeartRate.CreateTime) / 1000000),
  58. Method = topicHmPregnancyHeartRate!.Method,
  59. IsDisplay = topicHmPregnancyHeartRate!.IsDisplay ? 1 : 0
  60. };
  61. }
  62. public override string ToString()
  63. {
  64. return $"{nameof(PregnancyHeartRateResolver)}[{_messageId.Value}]";
  65. }
  66. public async Task ExecuteMessageAsync()
  67. {
  68. //throw new NotImplementedException();
  69. var messageId = _messageId.Value;
  70. var heartRate = _msgData.Value!;
  71. var watchConfig = await _deviceCacheMgr.GetGpsDeviceWatchConfigCacheObjectBySerialNoAsync(heartRate.Serialno, "0067");
  72. var isFetalHeartEnable = watchConfig != null && (int)watchConfig["enabled"]! == 1;
  73. if (isFetalHeartEnable)
  74. {
  75. #region 高频心率计算
  76. var phr = await _serviceTDengine.GetBySerialNoAsync<PregnancyHeartRateModel>(heartRate.Serialno, 7);
  77. if (phr.Count >= 30)
  78. {
  79. // 获取最近的两个记录,并计算它们的 LastUpdate 时间差
  80. var firstTwoPhr = phr.OrderByDescending(i => i.Timestamp).Take(2).Select(i => i.LastUpdate).ToList();
  81. var timeDiff = firstTwoPhr[0] - firstTwoPhr[1];
  82. // 如果需要,将时间差转换为秒
  83. var timeDiffInSeconds = timeDiff.TotalSeconds;
  84. // 高频心率采样间隔
  85. var highFreqSampleInterval = (int)watchConfig!["highFreqSampleInterval"]!;
  86. // 触发高频监测的心率上限值
  87. var triggerHighFreqHigh = (int)watchConfig["triggerHighFreqHigh"]!;
  88. // 触发高频监测的心率下限值
  89. var triggerHighFreqLow = (int)watchConfig["triggerHighFreqLow"]!;
  90. //停止高频心率采样心率连续正常次数
  91. var stopHighFreqSampleCount = (int)watchConfig["stopHighFreqSampleCount"]!;
  92. // 高频心率采集时长 0 为持续采集,非零为高频心率的采集时长
  93. var highFreqSampleTimes = (int)watchConfig["stopHighFreqSampleCount"]!;
  94. // 告警上限阀值
  95. var upperAlarmThreshold = (int)watchConfig["upperAlarmThreshold"]!;
  96. // 告警下限阀值
  97. var lowerAlarmThreshold= (int)watchConfig["lowerAlarmThreshold"]!;
  98. // 高频心率启动
  99. if (timeDiffInSeconds<=highFreqSampleInterval)
  100. {
  101. /// 设置高频状态
  102. var phrFreqstatus =await _deviceCacheMgr.GetPregnancyHeartRateFreqStatusAsync(heartRate.Serialno);
  103. if (phrFreqstatus == null)
  104. {
  105. // 设置高频状态
  106. var freqFirstPhr= phr.OrderByDescending(i => i.Timestamp).First();
  107. await _deviceCacheMgr.SetPregnancyHeartRateFreqStatusAsync(heartRate.Serialno, freqFirstPhr);
  108. phrFreqstatus = await _deviceCacheMgr.GetPregnancyHeartRateFreqStatusAsync(heartRate.Serialno);
  109. }
  110. /// phr PregnancyHeartRate 连续连续正常次数个值都是正常(大于等于triggerHighFreqLow,少于等于triggerHighFreqHig),
  111. /// 取连续正常次数正常值的平均值,推送到api/v1/open/OpenIot/SetFetalHeartRateConfig
  112. #region 检查是否连续12个值都是正常的
  113. // 获取最近连续正常次数个心率记录
  114. var lastPhr = phr.OrderByDescending(i => i.Timestamp).Take(stopHighFreqSampleCount).ToList();
  115. // 检查是否连续12个值都是正常的
  116. if (lastPhr.All(i => i.PregnancyHeartRate >= triggerHighFreqLow && i.PregnancyHeartRate <= triggerHighFreqHigh))
  117. {
  118. var avgPhr = lastPhr.Select(i => i.PregnancyHeartRate).Average();
  119. // 计算一般心率得到胎心系数
  120. await SaveAndPushFetalHeartRateAsync(heartRate, upperAlarmThreshold, lowerAlarmThreshold, avgPhr);
  121. }
  122. #endregion
  123. }
  124. // 高频心率结束
  125. else
  126. {
  127. var phrFreqstatus = await _deviceCacheMgr.GetPregnancyHeartRateFreqStatusAsync(heartRate.Serialno);
  128. if (phrFreqstatus != null)
  129. {
  130. /// 在highFreqSampleTimes=0一直异常(大于等于triggerHighFreqLow,少于等于triggerHighFreqHig),
  131. /// 取所有值的平均值,推送胎心数据到api/v1/open/OpenIot/SetFetalHeartRateConfig
  132. if (highFreqSampleTimes==0)
  133. {
  134. // if (phr.OrderByDescending(i => i.Timestamp)
  135. //.Where(i => i.Timestamp >= phrFreqstatus?.Timestamp)
  136. //.Skip(1) // 去除首条
  137. //.All(i => i.PregnancyHeartRate < triggerHighFreqLow || i.PregnancyHeartRate > triggerHighFreqHig))
  138. // {
  139. // var avgPhr = phr.Select(i => i.PregnancyHeartRate).Average();
  140. // // 推送胎心数据到 api/v1/open/OpenIot/SetFetalHeartRateConfig
  141. // // 计算一般心率得到胎心系数
  142. // }
  143. var avgPhr = phr.OrderByDescending(i => i.Timestamp)
  144. .Where(i => i.Timestamp >= phrFreqstatus?.Timestamp)
  145. .Skip(1) // 去除首条
  146. .Where(i => i.PregnancyHeartRate < triggerHighFreqLow || i.PregnancyHeartRate > triggerHighFreqHigh)
  147. .Select(i => i.PregnancyHeartRate).Average();
  148. // 推送胎心数据到 api/v1/open/OpenIot/SetFetalHeartRateConfig
  149. // 计算一般心率得到胎心系数
  150. await SaveAndPushFetalHeartRateAsync(heartRate, upperAlarmThreshold, lowerAlarmThreshold, avgPhr);
  151. }
  152. /// 在highFreqSampleTimes>0一直异常(大于等于triggerHighFreqLow,少于等于triggerHighFreqHig),
  153. /// 取所有值的平均值,推送胎心数据到api/v1/open/OpenIot/SetFetalHeartRateConfig
  154. if (highFreqSampleTimes > 0 && heartRate.LastUpdate >= (phrFreqstatus?.LastUpdate + TimeSpan.FromSeconds(highFreqSampleTimes)))
  155. {
  156. var avgPhr = phr
  157. .Where(i => i.Timestamp >= phrFreqstatus?.Timestamp)
  158. .Skip(1) // 去除首条
  159. .Where(i => i.PregnancyHeartRate < triggerHighFreqLow || i.PregnancyHeartRate > triggerHighFreqHigh)
  160. .Select(i => i.PregnancyHeartRate).Average();
  161. // 推送胎心数据到 api/v1/open/OpenIot/SetFetalHeartRateConfig
  162. // 计算一般心率得到胎心系数
  163. await SaveAndPushFetalHeartRateAsync(heartRate, upperAlarmThreshold, lowerAlarmThreshold, avgPhr);
  164. }
  165. // 删除高频状态的首条记录
  166. await _deviceCacheMgr.DelPregnancyHeartRateFreqStatusAsync(heartRate.Serialno);
  167. }
  168. }
  169. }
  170. #endregion
  171. }
  172. #region 定时下发触发器
  173. var key = $"health_monitor/schedule_push/pregnancy_heart_rate/imei/{heartRate.Serialno}";
  174. var schedule_push = await _serviceEtcd.GetValAsync(key).ConfigureAwait(false);
  175. if (string.IsNullOrWhiteSpace(schedule_push))
  176. {
  177. // 注册首次下推
  178. #if DEBUG
  179. // await _serviceEtcd.PutValAsync(key, result, 60*1, false).ConfigureAwait(false);
  180. var interval = 0;
  181. // 获取当前时间
  182. DateTime now = DateTime.Now;
  183. // 计算距离下一个$interval天后的8点的时间间隔
  184. DateTime nextRunTime = new DateTime(now.Year, now.Month, now.Day, now.Hour, now.Minute + 1, 58).AddDays(interval);
  185. TimeSpan timeUntilNextRun = nextRunTime - now;
  186. // 如果当前时间已经超过了8点,将等待到明天后的8点
  187. if (timeUntilNextRun < TimeSpan.Zero)
  188. {
  189. timeUntilNextRun = timeUntilNextRun.Add(TimeSpan.FromMinutes(1));
  190. nextRunTime += timeUntilNextRun;
  191. }
  192. var ttl = (long)timeUntilNextRun.TotalSeconds;
  193. var data = new
  194. {
  195. imei = heartRate.Serialno,
  196. create_time = now.ToString("yyyy-MM-dd HH:mm:ss"),
  197. ttl,
  198. next_run_time = nextRunTime.ToString("yyyy-MM-dd HH:mm:ss")
  199. };
  200. var result = JsonConvert.SerializeObject(data);
  201. await _serviceEtcd.PutValAsync(key, result, ttl, false).ConfigureAwait(false);
  202. #else
  203. var interval = 0;
  204. // 获取当前时间
  205. DateTime now = DateTime.Now;
  206. var rand=new Random();
  207. var pushSec = rand.Next(59);
  208. int pushMin= int.TryParse(heartRate.Serialno.AsSpan(heartRate.Serialno.Length - 1), out pushMin) ? pushMin : 10;
  209. // 计算距离下一个$interval天后的8点的时间间隔
  210. DateTime nextRunTime = new DateTime(now.Year, now.Month, now.Day, 18, pushMin, pushSec).AddDays(interval);
  211. TimeSpan timeUntilNextRun = nextRunTime - now;
  212. if (timeUntilNextRun < TimeSpan.Zero)
  213. {
  214. timeUntilNextRun = timeUntilNextRun.Add(TimeSpan.FromDays(1));
  215. nextRunTime += TimeSpan.FromDays(1);
  216. }
  217. var ttl =(long)timeUntilNextRun.TotalSeconds;
  218. var data = new
  219. {
  220. imei = heartRate.Serialno,
  221. create_time = now.ToString("yyyy-MM-dd HH:mm:ss"),
  222. ttl,
  223. next_run_time = nextRunTime.ToString("yyyy-MM-dd HH:mm:ss")
  224. };
  225. var result = JsonConvert.SerializeObject(data);
  226. await _serviceEtcd.PutValAsync(key, result,ttl, false).ConfigureAwait(false);
  227. #endif
  228. }
  229. #endregion
  230. }
  231. private async Task SaveAndPushFetalHeartRateAsync(HisGpsHeartRate heartRate, int upperAlarmThreshold, int lowerAlarmThreshold, double avgPhr)
  232. {
  233. var commonPHR = await _serviceTDengine.InitPregnancyCommonHeartRateModeAsync(heartRate.Serialno);
  234. if (commonPHR != null)
  235. {
  236. // 保存到TDengine数据库
  237. await _serviceTDengine.InsertAsync<PregnancyCommonHeartRateModel>("hm_pchr", commonPHR);
  238. // 计算胎心=孕妇心率*系数
  239. var fetalHeartRate = SafeType.SafeInt(avgPhr * commonPHR?.StatModeAvgFprCoefficient!);
  240. var sampleTime = DateTimeUtil.ConvertToTimeStamp(DateTime.Now).ToString();
  241. var isAbnormal = fetalHeartRate > upperAlarmThreshold ? 1 : (fetalHeartRate < lowerAlarmThreshold ? 2 : 0);
  242. // 保存到 数据服务 MySQL 数据库
  243. HisGpsFetalHeartRate gpsFetalHeartRate = new ()
  244. {
  245. FetalHeartRateId = Guid.NewGuid().ToString("D"),
  246. PersonId = commonPHR!.PersonId,
  247. Serialno = heartRate.Serialno,
  248. HeartRate = fetalHeartRate,
  249. SampleTime = sampleTime,
  250. IsAbnormal = isAbnormal,
  251. StatStartTime = commonPHR.StatStartTime,
  252. StatEndTime = commonPHR.StatEndTime,
  253. CreateTime = DateTime.Now,
  254. Method = 1,
  255. IsDisplay = 1,
  256. DeviceKey = commonPHR!.DeviceKey
  257. };
  258. await _hisFetalHeartApiClient.AddAsync(gpsFetalHeartRate).ConfigureAwait(false);
  259. // 推送到api/v1/open/OpenIot/SetFetalHeartRateConfig
  260. await _serviceIotApi.SetFetalHeartRateConfig(heartRate.Serialno, fetalHeartRate, sampleTime, isAbnormal);
  261. }
  262. }
  263. }
  264. }