Du kannst nicht mehr als 25 Themen auswählen Themen müssen entweder mit einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

399 Zeilen
21KB

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