Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

1060 lines
58KB

  1. using Etcdserverpb;
  2. using Google.Protobuf.WellKnownTypes;
  3. using HealthMonitor.Common;
  4. using HealthMonitor.Common.helper;
  5. using HealthMonitor.Model.Service.Mapper;
  6. using HealthMonitor.Service.Biz;
  7. using HealthMonitor.Service.Biz.db;
  8. using HealthMonitor.Service.Cache;
  9. using HealthMonitor.Service.Etcd;
  10. using HealthMonitor.Service.MessageQueue;
  11. using HealthMonitor.Service.Resolver.Interface;
  12. using HealthMonitor.Service.Sub;
  13. using HealthMonitor.Service.Sub.Topic.Model;
  14. using Microsoft.EntityFrameworkCore.Metadata;
  15. using Microsoft.Extensions.Logging;
  16. using Newtonsoft.Json;
  17. using Newtonsoft.Json.Linq;
  18. using SqlSugar;
  19. using System;
  20. using System.Collections.Generic;
  21. using System.Linq;
  22. using System.Net;
  23. using System.Text;
  24. using System.Threading.Tasks;
  25. using TelpoDataService.Util.Clients;
  26. using TelpoDataService.Util.Entities.GpsCard;
  27. using TelpoDataService.Util.Entities.GpsLocationHistory;
  28. using TelpoDataService.Util.Models;
  29. using TelpoDataService.Util.QueryObjects;
  30. namespace HealthMonitor.Service.Resolver
  31. {
  32. public class PregnancyHeartRateResolver : IResolver
  33. {
  34. private readonly ILogger<PregnancyHeartRateResolver> _logger;
  35. private readonly TDengineService _serviceTDengine;
  36. private readonly DeviceCacheManager _deviceCacheMgr;
  37. private readonly IotApiService _serviceIotApi;
  38. private readonly AsyncLocal<string> _messageId = new();
  39. private readonly AsyncLocal<HisGpsHeartRate> _msgData = new();
  40. private readonly HttpHelper _httpHelper = default!;
  41. private readonly EtcdService _serviceEtcd;
  42. private readonly GpsLocationHistoryAccessorClient<HisGpsFetalHeartRate> _hisFetalHeartApiClient;
  43. private readonly GpsLocationHistoryAccessorClient<HisGpsFetalMovement> _hisFetalMovementApiClient;
  44. private readonly FetalMovementNormalValueRangeCacheManager _mgrFetalMovementNormalValueRangeCache;
  45. private readonly MqProcessLogic _serviceMqProcess;
  46. private static int[] SCHEDULE_HOUR = new int[] { 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24 };
  47. private static int[] ODD_SCHEDULE_HOUR = new int[] { 1, 3, 5, 7, 9, 11,13, 15, 17, 19, 21, 23 };
  48. // 延时胎心计算间隔周期
  49. private static int INTERVAL_FHR=15;
  50. public PregnancyHeartRateResolver(ILogger<PregnancyHeartRateResolver> logger,
  51. HttpHelper httpHelper, EtcdService serviceEtcd, DeviceCacheManager deviceCacheMgr,
  52. MqProcessLogic serviceMqProcess,
  53. IotApiService iotApiService, TDengineService serviceDengine, FetalMovementNormalValueRangeCacheManager fetalMovementNormalValueRangeCacheMgr,
  54. GpsLocationHistoryAccessorClient<HisGpsFetalHeartRate> hisFetalHeartApiClient,
  55. GpsLocationHistoryAccessorClient<HisGpsFetalMovement> hisFetalMovementApiClient
  56. )
  57. {
  58. _logger = logger;
  59. _httpHelper = httpHelper;
  60. _serviceEtcd = serviceEtcd;
  61. _serviceTDengine = serviceDengine;
  62. _deviceCacheMgr = deviceCacheMgr;
  63. _serviceIotApi = iotApiService;
  64. _serviceMqProcess = serviceMqProcess;
  65. _hisFetalHeartApiClient = hisFetalHeartApiClient;
  66. _hisFetalMovementApiClient = hisFetalMovementApiClient;
  67. _mgrFetalMovementNormalValueRangeCache = fetalMovementNormalValueRangeCacheMgr;
  68. }
  69. public void SetResolveInfo(PackageMsgModel msg)
  70. {
  71. var topicHmPregnancyHeartRate = JsonConvert.DeserializeObject<TopicHmPregnancyHeartRate>(msg.DetailData.ToString()!);
  72. _messageId.Value = msg.MessageId;
  73. _msgData.Value = new HisGpsHeartRate()
  74. {
  75. HeartRateId = topicHmPregnancyHeartRate!.PregnancyHeartRateId,
  76. MessageId = topicHmPregnancyHeartRate!.MessageId,
  77. Serialno = topicHmPregnancyHeartRate!.Serialno,
  78. HeartRate = topicHmPregnancyHeartRate.PregnancyHeartRate,
  79. LastUpdate = DateTimeUtil.GetDateTimeFromUnixTimeMilliseconds(SafeType.SafeInt64(topicHmPregnancyHeartRate.LastUpdate) / 1000000),
  80. CreateTime = DateTimeUtil.GetDateTimeFromUnixTimeMilliseconds(SafeType.SafeInt64(topicHmPregnancyHeartRate.CreateTime) / 1000000),
  81. Method = topicHmPregnancyHeartRate!.Method,
  82. IsDisplay = topicHmPregnancyHeartRate!.IsDisplay ? 1 : 0
  83. };
  84. }
  85. public override string ToString()
  86. {
  87. return $"{nameof(PregnancyHeartRateResolver)}[{_messageId.Value}]";
  88. }
  89. public async Task ExecuteMessageAsync()
  90. {
  91. var messageId = _messageId.Value;
  92. var heartRate = _msgData.Value!;
  93. try
  94. {
  95. var watchConfig = await _deviceCacheMgr.GetGpsDeviceWatchConfigCacheObjectBySerialNoAsync(heartRate.Serialno, "0067");
  96. var isFetalHeartEnable = watchConfig != null && (int)watchConfig["enabled"]! == 1;
  97. if (isFetalHeartEnable)
  98. {
  99. //_logger.LogInformation($"{heartRate.Serialno} 计算胎心胎动启动");
  100. #region 定时下发触发器(定时建模)
  101. var key = $"health_monitor/schedule_push/pregnancy_heart_rate/imei/{heartRate.Serialno}";
  102. var schedule_push = await _serviceEtcd.GetValAsync(key).ConfigureAwait(false);
  103. if (string.IsNullOrWhiteSpace(schedule_push))
  104. {
  105. // 注册首次下推
  106. var interval = 0;
  107. // 获取当前时间
  108. DateTime now = DateTime.Now;
  109. var rand = new Random();
  110. var pushSec = rand.Next(59);
  111. int pushMin = int.TryParse(heartRate.Serialno.AsSpan(heartRate.Serialno.Length - 1), out pushMin) ? pushMin : 10;
  112. // 计算距离下一个$interval天后的8点的时间间隔
  113. DateTime nextRunTime = new DateTime(now.Year, now.Month, now.Day, 6, pushMin, pushSec).AddDays(interval);
  114. TimeSpan timeUntilNextRun = nextRunTime - now;
  115. if (timeUntilNextRun < TimeSpan.Zero)
  116. {
  117. timeUntilNextRun = timeUntilNextRun.Add(TimeSpan.FromDays(1));
  118. nextRunTime += TimeSpan.FromDays(1);
  119. }
  120. var ttl = (long)timeUntilNextRun.TotalSeconds;
  121. var data = new
  122. {
  123. imei = heartRate.Serialno,
  124. create_time = now.ToString("yyyy-MM-dd HH:mm:ss"),
  125. ttl,
  126. next_run_time = nextRunTime.ToString("yyyy-MM-dd HH:mm:ss")
  127. };
  128. var result = JsonConvert.SerializeObject(data);
  129. await _serviceEtcd.PutValAsync(key, result, ttl, false).ConfigureAwait(false);
  130. }
  131. #endregion
  132. //_logger.LogInformation($"{heartRate.Serialno} 触发定时建模");
  133. // 高频心率采样间隔 highFreqSampleInterval = highFreqSampleInterval 最小highFreqSampleInterval=60)
  134. var highFreqSampleInterval = (int)watchConfig!["highFreqSampleInterval"]! >= 60 ? (int)watchConfig!["highFreqSampleInterval"]!:60;
  135. // 触发高频监测的心率上限值
  136. var triggerHighFreqHigh = (int)watchConfig["triggerHighFreqHigh"]!;
  137. // 触发高频监测的心率下限值
  138. var triggerHighFreqLow = (int)watchConfig["triggerHighFreqLow"]!;
  139. // 暂时处理
  140. triggerHighFreqLow = triggerHighFreqLow > 60 && triggerHighFreqLow < 67 ? 60 : triggerHighFreqLow;
  141. //停止高频心率采样心率连续正常次数
  142. var stopHighFreqSampleCount = (int)watchConfig["stopHighFreqSampleCount"]!;
  143. // 高频心率采集时长 0 为持续采集,非零为高频心率的采集时长
  144. var highFreqSampleTimes = (int)watchConfig["highFreqSampleTimes"]!;
  145. // 告警上限阀值
  146. var upperAlarmThreshold = (int)watchConfig["upperAlarmThreshold"]!;
  147. // 告警下限阀值
  148. var lowerAlarmThreshold = (int)watchConfig["lowerAlarmThreshold"]!;
  149. // EDOC
  150. var edoc = DateTimeUtil.ToDateTime(watchConfig!["EDOC"]!.ToString());
  151. // interval (分钟) 固定15分钟
  152. var intervalFHR = 15;//int)watchConfig["interval"]!;
  153. var daysPhr = await _serviceTDengine.GetBySerialNoAsync<PregnancyHeartRateModel>(heartRate.Serialno, 7);
  154. var phr = daysPhr
  155. .Where(p => p.LastUpdate <= heartRate.LastUpdate)
  156. .ToList();
  157. var nonFreqPhr = GetNonFreqPregnancyHeartRate(phr, highFreqSampleInterval);
  158. if (nonFreqPhr.Count >= 30)
  159. {
  160. #region 定时计算胎动数据触发器两小时间隔开始
  161. /**
  162. var fetalMovementKey = $"health_monitor/schedule_push/cal_fetal_movement/imei/{heartRate.Serialno}";
  163. ///// 计算 0 点秒数
  164. var fetalMovementLastUpdate = (DateTime)heartRate.LastUpdate!;
  165. DateTime fmScheduleNow = DateTime.Now;
  166. // 小于两小时
  167. if (fmScheduleNow > fetalMovementLastUpdate && (fmScheduleNow - fetalMovementLastUpdate).TotalHours <= 2)
  168. {
  169. var rand = new Random();
  170. var pushSec = rand.Next(59);
  171. int pushMin = int.TryParse(heartRate.Serialno.AsSpan(heartRate.Serialno.Length - 1), out pushMin) ? pushMin : 10;
  172. var scheduleHourDiff = SCHEDULE_HOUR
  173. .Where(h => h > fetalMovementLastUpdate.Hour)
  174. .OrderBy(h => h - fetalMovementLastUpdate.Hour)
  175. .FirstOrDefault() - fetalMovementLastUpdate.Hour;
  176. var scheduleTime = fetalMovementLastUpdate.AddHours(scheduleHourDiff);
  177. DateTime nextRunTime = new(scheduleTime.Year, scheduleTime.Month, scheduleTime.Day, scheduleTime.Hour, pushMin, pushSec);
  178. TimeSpan timeUntilNextRun = nextRunTime - fmScheduleNow;
  179. var ttl = (long)timeUntilNextRun.TotalSeconds;
  180. await SetIntervalTriggerAsync(fetalMovementKey, heartRate.Serialno, ttl, heartRate);
  181. }
  182. */
  183. #endregion
  184. #region 定时计算胎动数据触发器两小时间隔开始(奇数小时计算)
  185. var fetalMovementKey = $"health_monitor/schedule_push/cal_fetal_movement/imei/{heartRate.Serialno}";
  186. var rand = new Random();
  187. var pushSec = rand.Next(59);
  188. int pushMin = int.TryParse(heartRate.Serialno.AsSpan(heartRate.Serialno.Length - 1), out pushMin) ? pushMin : 10;
  189. DateTime fmScheduleNow = DateTime.Now;
  190. int hour = fmScheduleNow.Hour;
  191. int selectedScheduleHour;
  192. // 使用模运算来获取相应的ODD_SCHEDULE_HOUR
  193. if (hour % 2 == 0)
  194. {
  195. selectedScheduleHour = ODD_SCHEDULE_HOUR[hour / 2 % ODD_SCHEDULE_HOUR.Length];
  196. }
  197. else
  198. {
  199. selectedScheduleHour = ODD_SCHEDULE_HOUR[(hour + 1) / 2 % ODD_SCHEDULE_HOUR.Length];
  200. }
  201. DateTime scheduledDateTime = new DateTime(
  202. fmScheduleNow.Year,
  203. fmScheduleNow.Month,
  204. fmScheduleNow.Day,
  205. selectedScheduleHour,
  206. 0, // 分钟设为 0
  207. 0 // 秒设为 0
  208. );
  209. // 如果生成的时间在当前时间之前
  210. // 或者 fmScheduleNow 在 23 点之后并且 selectedScheduleHour 在 1 点(即次日)
  211. if (scheduledDateTime < fmScheduleNow || (fmScheduleNow.Hour >= 23 && selectedScheduleHour <= 1))
  212. {
  213. scheduledDateTime = scheduledDateTime.AddDays(1);
  214. }
  215. TimeSpan timeUntilNextRun = scheduledDateTime - fmScheduleNow;
  216. var ttl = (long)timeUntilNextRun.TotalSeconds;
  217. await SetIntervalTriggerAsync(fetalMovementKey, heartRate.Serialno, ttl, heartRate);
  218. #endregion
  219. #region 计算胎心数据(按心率时间LastUpdate)
  220. var commonPHR = await _serviceTDengine.GetLastAsync<PregnancyCommonHeartRateModel>(heartRate.Serialno);
  221. if (commonPHR == null)
  222. {
  223. // 处理孕妇业务,计算一般心率并下发
  224. commonPHR = await _serviceTDengine.InitPregnancyCommonHeartRateModeAsync(heartRate.Serialno, highFreqSampleInterval: highFreqSampleInterval);
  225. // 建模完成
  226. var flag = await _serviceIotApi.SetFetalConfig(heartRate.Serialno, 1, commonPHR!.MaxValue, commonPHR!.MinValue);
  227. _logger.LogInformation($"{heartRate.Serialno} 记录数量足够,建模完成");
  228. // 保存到TDengine数据库
  229. await _serviceTDengine.InsertAsync<PregnancyCommonHeartRateModel>("hm_pchr", commonPHR!);
  230. _logger.LogInformation($"保存TDengine完成");
  231. }
  232. // 获取最近的两个记录,并计算它们的 LastUpdate 时间差
  233. var firstTwoPhr = phr.OrderByDescending(i => i.LastUpdate).Take(2).Select(i => i.LastUpdate).ToList();
  234. var timeDiff = firstTwoPhr[0] - firstTwoPhr[1];
  235. // 如果需要,将时间差转换为秒
  236. var timeDiffInSeconds = timeDiff.TotalSeconds;
  237. // 高频统计结束时间
  238. var FreqStatsEnd = DateTime.Now;
  239. // 高频心率启动(在高频第二条才能判断)
  240. if (timeDiffInSeconds <= highFreqSampleInterval)
  241. {
  242. // 设置延时计算高频心率的胎心
  243. var freqHearRateHey = $"health_monitor/schedule_push/cal_freqhr_fetal_heart_rate/imei/{heartRate.Serialno}";
  244. var phrFreqstatus = await _deviceCacheMgr.GetPregnancyHeartRateFreqStatusAsync(heartRate.Serialno);
  245. if (phrFreqstatus == null)
  246. {
  247. /// 设置高频状态
  248. _logger.LogInformation($"{heartRate.Serialno} 进入高频心率启动状态 timeDiffInSeconds {timeDiffInSeconds},highFreqSampleInterval:{highFreqSampleInterval}");
  249. // 设置高频状态
  250. _logger.LogInformation($"{heartRate.Serialno} phr.Count {phr.Count}");
  251. var freqFirstPhr = phr.OrderByDescending(i => i.LastUpdate)
  252. .Skip(1) //在高频第二条才能判断,所以要去除本条记录
  253. .First();
  254. await _deviceCacheMgr.SetPregnancyHeartRateFreqStatusAsync(heartRate.Serialno, freqFirstPhr);
  255. _logger.LogInformation($"{heartRate.Serialno} 设置高频状态");
  256. phrFreqstatus = await _deviceCacheMgr.GetPregnancyHeartRateFreqStatusAsync(heartRate.Serialno);
  257. HisGpsHeartRate freqFirstHR = new()
  258. {
  259. CreateTime = freqFirstPhr.CreateTime,
  260. DeviceKey = freqFirstPhr.DeviceKey,
  261. HeartRate = freqFirstPhr.PregnancyHeartRate,
  262. HeartRateId = freqFirstPhr.PregnancyHeartRateId,
  263. IsDisplay = freqFirstPhr.IsDisplay ? 1 : 0,
  264. LastUpdate = freqFirstPhr.LastUpdate,
  265. MessageId = freqFirstPhr.MessageId,
  266. Method = freqFirstPhr.Method,
  267. PersonId = freqFirstPhr.PersonId,
  268. Serialno = freqFirstPhr.SerialNumber,
  269. };
  270. await SetFreqHeartRateTriggerAsync(freqHearRateHey, heartRate.Serialno, highFreqSampleInterval, freqFirstHR);
  271. }
  272. // 续租延时计算高频心率的胎心
  273. await SetFreqHeartRateTriggerAsync(freqHearRateHey, heartRate.Serialno, highFreqSampleInterval);
  274. /// phr PregnancyHeartRate 连续连续正常次数个值都是正常(大于等于triggerHighFreqLow,少于等于triggerHighFreqHig),
  275. /// 取连续正常次数正常值的平均值,推送到api/v1/open/OpenIot/SetFetalHeartRateConfig
  276. #region 检查高频状态是否连续12个心率值都是正常的
  277. // 获取最近连续正常次数个心率记录
  278. //_logger.LogInformation($"{heartRate.Serialno} 设置 stopHighFreqSampleCount {stopHighFreqSampleCount}");
  279. //var lastPhr = phr.OrderByDescending(i => i.LastUpdate).Take(stopHighFreqSampleCount).ToList();
  280. var lastPhr = phr.Where(i => i.LastUpdate >= phrFreqstatus!.LastUpdate)
  281. .OrderByDescending(i => i.LastUpdate).Take(stopHighFreqSampleCount).ToList();
  282. _logger.LogInformation($"{heartRate.Serialno} lastPhr.Count {lastPhr.Count},stopHighFreqSampleCount {stopHighFreqSampleCount}");
  283. _logger.LogInformation($"{heartRate.Serialno} count :{lastPhr.Count >= stopHighFreqSampleCount}");
  284. _logger.LogInformation($"{heartRate.Serialno} All {lastPhr.All(i => i.PregnancyHeartRate >= triggerHighFreqLow && i.PregnancyHeartRate <= triggerHighFreqHigh)}");
  285. // 检查是否连续12个值都是正常的
  286. if ((lastPhr.Count >= stopHighFreqSampleCount) &&
  287. lastPhr.All(i => i.PregnancyHeartRate >= triggerHighFreqLow && i.PregnancyHeartRate <= triggerHighFreqHigh)
  288. )
  289. {
  290. var avgPhr = lastPhr.Select(i => i.PregnancyHeartRate).Average();
  291. // 计算一般心率得到胎心系数
  292. //await SaveAndPushFreqFetalHeartRateAsync(heartRate, upperAlarmThreshold, lowerAlarmThreshold, avgPhr, DateTimeUtil.ConvertToTimeStamp(DateTime.Now).ToString());
  293. // 高频数据不建模
  294. FreqStatsEnd = (DateTime)heartRate.LastUpdate!;
  295. _logger.LogInformation($"{heartRate.Serialno} 高频状态已经持续{(FreqStatsEnd - phrFreqstatus!.LastUpdate).TotalSeconds} 秒,连续 {stopHighFreqSampleCount} 次采样心率正常,将下发指令");
  296. var freqSaveAndPushFetalHeartRate = await _deviceCacheMgr.GetBizIntervalAsync(heartRate.Serialno, "SaveAndPushFetalHeartRate");
  297. // 高频不停,15分钟内只下发一条
  298. if (string.IsNullOrEmpty(freqSaveAndPushFetalHeartRate))
  299. {
  300. await SaveAndPushFetalHeartRateAsync(heartRate, commonPHR, upperAlarmThreshold, lowerAlarmThreshold, avgPhr, DateTimeUtil.ConvertToTimeStamp(phrFreqstatus!.LastUpdate).ToString(), phrFreqstatus!.LastUpdate, FreqStatsEnd);
  301. // 删除高频状态的首条记录
  302. await _deviceCacheMgr.DelPregnancyHeartRateFreqStatusAsync(heartRate.Serialno);
  303. // 设置15分的SaveAndPushFetalHeartRate业务间隔
  304. await _deviceCacheMgr.SetBizIntervalAsync(heartRate.Serialno, "SaveAndPushFetalHeartRate");
  305. _logger.LogInformation($"{heartRate.Serialno} 连续 {stopHighFreqSampleCount} 次采样心率正常,结束高频心率状态, timeDiffInSeconds {timeDiffInSeconds},highFreqSampleInterval:{highFreqSampleInterval},高频状态持续{((DateTime)heartRate.LastUpdate - phrFreqstatus!.LastUpdate).TotalSeconds} 秒");
  306. }
  307. else
  308. {
  309. _logger.LogWarning($"{heartRate.Serialno} 连续 {stopHighFreqSampleCount} 次采样心率正常,设备端应该结束高频状态,但设备端没有结束高频,平台高频15分钟内已经下发过指令");
  310. }
  311. }
  312. else
  313. {
  314. _logger.LogInformation($"{heartRate.Serialno} 处于高频状态...");
  315. }
  316. #endregion
  317. }
  318. // 高频心率结束或常规心率
  319. else
  320. {
  321. var phrFreqstatus = await _deviceCacheMgr.GetPregnancyHeartRateFreqStatusAsync(heartRate.Serialno);
  322. // 高频心率结束
  323. if (phrFreqstatus != null)
  324. {
  325. /// 在highFreqSampleTimes=0一直异常(大于等于triggerHighFreqLow,少于等于triggerHighFreqHig),
  326. /// 取所有值的平均值,推送胎心数据到api/v1/open/OpenIot/SetFetalHeartRateConfig
  327. if (highFreqSampleTimes == 0)
  328. {
  329. var avgPhr = phr.OrderByDescending(i => i.LastUpdate)
  330. .Where(i => i.LastUpdate >= phrFreqstatus?.LastUpdate)
  331. .Skip(1) // 去除首条
  332. .Where(i => i.PregnancyHeartRate < triggerHighFreqLow || i.PregnancyHeartRate > triggerHighFreqHigh)
  333. .Select(i => i.PregnancyHeartRate).Average();
  334. // 推送胎心数据到 api/v1/open/OpenIot/SetFetalHeartRateConfig
  335. // 计算一般心率得到胎心系数
  336. // 高频数据不建模
  337. FreqStatsEnd = firstTwoPhr[1];
  338. _logger.LogInformation($"{heartRate.Serialno} 高频状态已经持续{(FreqStatsEnd - phrFreqstatus!.LastUpdate).TotalSeconds} 秒,highFreqSampleTimes={highFreqSampleTimes}秒,即将结束高频状态,将下发指令");
  339. //await SaveAndPushFreqFetalHeartRateAsync(heartRate, commonPHR, upperAlarmThreshold, lowerAlarmThreshold, avgPhr, DateTimeUtil.ConvertToTimeStamp(phrFreqstatus!.LastUpdate).ToString());
  340. await SaveAndPushFetalHeartRateAsync(heartRate, commonPHR, upperAlarmThreshold, lowerAlarmThreshold, avgPhr, DateTimeUtil.ConvertToTimeStamp(phrFreqstatus!.LastUpdate).ToString(), phrFreqstatus!.LastUpdate, FreqStatsEnd);
  341. }
  342. /// 在highFreqSampleTimes>0一直异常(大于等于triggerHighFreqLow,少于等于triggerHighFreqHig),
  343. /// 取所有值的平均值,推送胎心数据到api/v1/open/OpenIot/SetFetalHeartRateConfig
  344. if (highFreqSampleTimes > 0 && heartRate.LastUpdate >= (phrFreqstatus?.LastUpdate.AddSeconds(highFreqSampleTimes)))
  345. {
  346. // 获取高频心率数据个数
  347. var filterPhr = phr
  348. .Where(i => i.LastUpdate >= phrFreqstatus?.LastUpdate)
  349. .Skip(1)
  350. .ToList();
  351. _logger.LogInformation($"{heartRate.Serialno} 高频周期 {phrFreqstatus?.LastUpdate.ToString("yyyy-MM-dd HH:mm:ss")}--{firstTwoPhr[1].ToString("yyyy-MM-dd HH:mm:ss")} 产生高频心率数量 {filterPhr.Count} 条");
  352. // 高频心率数据大于stopHighFreqSampleCount/12个才计算胎心数据
  353. //
  354. if (filterPhr.Count > stopHighFreqSampleCount)
  355. {
  356. FreqStatsEnd = firstTwoPhr[1];
  357. var avgPhr = filterPhr
  358. .OrderByDescending(i => i.LastUpdate)
  359. .Take(stopHighFreqSampleCount) // 计算最后12条
  360. .Select(i => i.PregnancyHeartRate).Average();
  361. _logger.LogInformation($"{heartRate.Serialno} 高频状态已经持续{(FreqStatsEnd - phrFreqstatus!.LastUpdate).TotalSeconds} 秒,超过约定的 {highFreqSampleTimes} 秒,即将结束高频状态,将下发指令");
  362. //计算高频
  363. await SaveAndPushFetalHeartRateAsync(heartRate, commonPHR, upperAlarmThreshold, lowerAlarmThreshold, avgPhr, DateTimeUtil.ConvertToTimeStamp(phrFreqstatus!.LastUpdate).ToString(), phrFreqstatus!.LastUpdate, FreqStatsEnd);
  364. }
  365. else
  366. {
  367. _logger.LogWarning($"{heartRate.Serialno} 高频心率的数据不大于{stopHighFreqSampleCount}条,不进行高频数据的胎心计算");
  368. }
  369. // 删除高频状态的首条记录
  370. await _deviceCacheMgr.DelPregnancyHeartRateFreqStatusAsync(heartRate.Serialno);
  371. // (逐条计算)计算本次常规心率的胎心数据(高频结束后,实时处理)
  372. // await CalculateNormalFetalHeartRateAsync(heartRate, upperAlarmThreshold, lowerAlarmThreshold, intervalFHR, commonPHR);
  373. await CalculateNormalFetalHeartRateScheduleAsync(heartRate);
  374. }
  375. ///不满足持续10分钟highFreqSampleTimes或出现时间倒叙
  376. ///
  377. else
  378. {
  379. // 高频结束后与常规的心率时间倒叙
  380. if ((firstTwoPhr[1] - phrFreqstatus!.LastUpdate).TotalSeconds < 0)
  381. {
  382. _logger.LogInformation($"{heartRate.Serialno} 高频结束出现时间倒叙,计算当条心率创建之前的高频心率");
  383. #region 计算当条心率创建之前的高频心率
  384. // 取得高频之后的所有数据
  385. var phrFlashBack = daysPhr.Where(p => p.LastUpdate >= phrFreqstatus!.LastUpdate)
  386. .OrderByDescending(i => i.LastUpdate);
  387. // 取得高频数据
  388. var freqCollection = phrFlashBack.ToList();
  389. //var freqCollection = new List<PregnancyHeartRateModel>();
  390. //PregnancyHeartRateModel? previousItem = null;
  391. //foreach (var item in phrFlashBack)
  392. //{
  393. // if (previousItem != null)
  394. // {
  395. // var timeNextDiff = (previousItem!.LastUpdate - item.LastUpdate).TotalSeconds;
  396. // if (timeNextDiff <= highFreqSampleInterval)
  397. // {
  398. // freqCollection.Add(item);
  399. // }
  400. // }
  401. // previousItem = item;
  402. //}
  403. _logger.LogInformation($"{heartRate.Serialno} 高频数据个数{freqCollection.Count},触发高频开始时间{phrFreqstatus!.LastUpdate}");
  404. if (freqCollection.Count > stopHighFreqSampleCount)
  405. {
  406. // 计算高频产生的胎心
  407. var avgPhr = freqCollection
  408. .OrderByDescending(i => i.LastUpdate)
  409. .Take(stopHighFreqSampleCount) // 计算最后12条
  410. .Select(i => i.PregnancyHeartRate).Average();
  411. await SaveAndPushFetalHeartRateAsync(heartRate, commonPHR, upperAlarmThreshold, lowerAlarmThreshold, avgPhr, DateTimeUtil.ConvertToTimeStamp(phrFreqstatus!.LastUpdate).ToString(), freqCollection.Last().LastUpdate, freqCollection.First().LastUpdate);
  412. }
  413. else
  414. {
  415. _logger.LogInformation($"{heartRate.Serialno} 时间倒叙触发计算高频心率的数据不足{stopHighFreqSampleCount}条,不进行胎心计算");
  416. }
  417. #endregion
  418. }
  419. else
  420. {
  421. _logger.LogInformation($"{heartRate.Serialno} 高频持续时间不足{highFreqSampleTimes},只持续{(firstTwoPhr[1] - phrFreqstatus!.LastUpdate).TotalSeconds} 秒");
  422. }
  423. // 删除高频状态的首条记录
  424. await _deviceCacheMgr.DelPregnancyHeartRateFreqStatusAsync(heartRate.Serialno);
  425. // (逐条计算)计算本次常规心率的胎心数据(高频结束后,实时处理)
  426. //await CalculateNormalFetalHeartRateAsync(heartRate, upperAlarmThreshold, lowerAlarmThreshold, intervalFHR, commonPHR);
  427. await CalculateNormalFetalHeartRateScheduleAsync(heartRate);
  428. }
  429. // 删除高频状态的首条记录
  430. await _deviceCacheMgr.DelPregnancyHeartRateFreqStatusAsync(heartRate.Serialno);
  431. _logger.LogInformation($"{heartRate.Serialno} 超时结束高频心率状态 timeDiffInSeconds {timeDiffInSeconds},highFreqSampleInterval:{highFreqSampleInterval},高频状态持续{(firstTwoPhr[1] - phrFreqstatus!.LastUpdate).TotalSeconds} 秒");
  432. //// 使用延后计算
  433. //var fhrScheduleKey = $"health_monitor/schedule_push/cal_fetal_heart_rate/imei/{heartRate.Serialno}";
  434. //var fhrScheduleTTL = 60;
  435. //await SetIntervalTriggerAsync(fhrScheduleKey, heartRate.Serialno, fhrScheduleTTL, heartRate);
  436. }
  437. // 常规心率(本次心率可能是高频心率的首条,所以要使用延后计算胎心率)
  438. else
  439. {
  440. // 本次心率可能是高频心率的首条,所以要使用本次常规心率延后计算胎心率
  441. //var fhrScheduleKey = $"health_monitor/schedule_push/cal_fetal_heart_rate/imei/{heartRate.Serialno}";
  442. //var fhrScheduleTTL = 30;
  443. //await SetIntervalTriggerAsync(fhrScheduleKey, heartRate.Serialno, fhrScheduleTTL, heartRate);
  444. //_logger.LogInformation($"{heartRate.Serialno} 延时50秒,判断当前数据是否为高频首条");
  445. // 本次心率可能是高频心率的首条,所以要使用本次常规心率延后计算胎心率 查询30秒后是否有高频缓存
  446. Thread thread = new(async () =>
  447. {
  448. try
  449. {
  450. #region 休眠highFreqSampleInterval2秒
  451. var startTime = DateTime.Now;
  452. //var highFreqSampleInterval2 = (int)watchConfig!["highFreqSampleInterval"]!+5;
  453. var highFreqSampleInterval2 = highFreqSampleInterval;
  454. var during = TimeSpan.FromSeconds(highFreqSampleInterval2);
  455. while (true)
  456. {
  457. if (DateTime.Now - startTime > during)
  458. {
  459. break;
  460. }
  461. await Task.Delay(TimeSpan.FromSeconds(1));
  462. }
  463. #endregion
  464. var phrFreqstatus = await _deviceCacheMgr.GetPregnancyHeartRateFreqStatusAsync(heartRate.Serialno);
  465. _logger.LogInformation($"phrFreqstatus==null:{phrFreqstatus == null}");
  466. _logger.LogInformation($"phrFreqstatus.LastUpdate < heartRate.LastUpdate:{phrFreqstatus?.LastUpdate < heartRate.LastUpdate}");
  467. if (phrFreqstatus == null || phrFreqstatus.LastUpdate < heartRate.LastUpdate)
  468. {
  469. // (逐条计算)常规心率
  470. //await CalculateNormalFetalHeartRateAsync(heartRate, upperAlarmThreshold, lowerAlarmThreshold, intervalFHR, commonPHR);
  471. //_logger.LogInformation($"{heartRate.Serialno} 计算常规心率");
  472. // 延时计算常规心率
  473. await CalculateNormalFetalHeartRateScheduleAsync(heartRate);
  474. }
  475. }
  476. catch (Exception ex)
  477. {
  478. _logger.LogError($"处理延时计算异常:{ex.Message}, {ex.StackTrace}");
  479. }
  480. });
  481. thread.Start();
  482. }
  483. }
  484. #endregion
  485. }
  486. else
  487. {
  488. _logger.LogInformation($"{heartRate.Serialno} 记录不足30条,建模中");
  489. }
  490. }
  491. }
  492. catch (Exception ex)
  493. {
  494. _logger.LogError($"{heartRate.Serialno} 处理孕妇心率数据异常 \n{ex.Message}\n{ex.StackTrace}");
  495. }
  496. }
  497. /// <summary>
  498. /// (逐条计算)常规心率计算胎心数据
  499. /// </summary>
  500. /// <param name="heartRate"></param>
  501. /// <param name="upperAlarmThreshold"></param>
  502. /// <param name="lowerAlarmThreshold"></param>
  503. /// <param name="intervalFHR"></param>
  504. /// <param name="commonPHR"></param>
  505. /// <returns></returns>
  506. private async Task CalculateNormalFetalHeartRateAsync(HisGpsHeartRate heartRate, int upperAlarmThreshold, int lowerAlarmThreshold, int intervalFHR, PregnancyCommonHeartRateModel? commonPHR)
  507. {
  508. // 上15分钟的数据
  509. // 获取当前时间
  510. DateTime nowInterval = (DateTime)heartRate.LastUpdate!;
  511. if (nowInterval.Second > 0)
  512. {
  513. nowInterval = nowInterval.AddMinutes(1);
  514. }
  515. // 计算last_update到上一间隔的分钟数
  516. int minutesToSubtract = nowInterval.Minute % intervalFHR;
  517. // 计算上一间隔的时间
  518. DateTime previousInterval = nowInterval.AddMinutes(-minutesToSubtract).AddSeconds(-nowInterval.Second).AddMilliseconds(-nowInterval.Millisecond);
  519. // 使用 last_update 上一刻
  520. var sampleTimeFHR = DateTimeUtil.ConvertToTimeStamp(previousInterval).ToString();
  521. // 计算last_update到下一间隔的分钟数
  522. int minutesToAdd = intervalFHR - (nowInterval.Minute % intervalFHR);
  523. if (minutesToAdd == intervalFHR)
  524. {
  525. minutesToAdd = 0; // 如果已经是间隔,则不需要增加分钟
  526. }
  527. // 计算下一间隔的时间
  528. DateTime nextInterval = nowInterval.AddMinutes(minutesToAdd)
  529. .AddSeconds(-nowInterval.Second)
  530. .AddMilliseconds(-nowInterval.Millisecond);
  531. var daysPhr = await _serviceTDengine.GetBySerialNoAsync<PregnancyHeartRateModel>(heartRate.Serialno, 7);
  532. var normalPhrStatStartTime = nextInterval.AddMinutes(-intervalFHR);
  533. var normalPhrStatEndTime = nextInterval;
  534. _logger.LogInformation($"{heartRate.Serialno} 计算胎心数据, 周期:{normalPhrStatStartTime}-{normalPhrStatEndTime} ");
  535. var filteredPhr = daysPhr
  536. // 使用 last_update 下一刻
  537. .Where(i => i.LastUpdate <= normalPhrStatEndTime && i.LastUpdate >= normalPhrStatStartTime)
  538. .ToList();
  539. if (filteredPhr.Count == 0)
  540. {
  541. _logger.LogWarning($"{heartRate.Serialno} 周期:{normalPhrStatStartTime}-{normalPhrStatEndTime} 孕妇心率数据不足,{filteredPhr.Count}条记录");
  542. return;
  543. }
  544. var phrValue = filteredPhr.Count == 1
  545. ? filteredPhr.First().PregnancyHeartRate
  546. : filteredPhr.Average(i => i.PregnancyHeartRate);
  547. //await SaveAndPushFreqFetalHeartRateAsync(heartRate, commonPHR!, upperAlarmThreshold, lowerAlarmThreshold, phrValue, sampleTimeFHR);
  548. await SaveAndPushFetalHeartRateAsync(heartRate, commonPHR!, upperAlarmThreshold, lowerAlarmThreshold, phrValue, sampleTimeFHR, normalPhrStatStartTime, normalPhrStatEndTime);
  549. }
  550. /// <summary>
  551. /// 延时计算
  552. /// </summary>
  553. /// <param name="heartRate"></param>
  554. /// <returns></returns>
  555. public async Task CalculateNormalFetalHeartRateScheduleAsync(HisGpsHeartRate heartRate)
  556. {
  557. var fhrScheduleKey = $"health_monitor/schedule_push/cal_normalphr_fetal_heart_rate/imei/{heartRate.Serialno}";
  558. DateTime nowInterval = DateTime.Now;
  559. if (nowInterval.Second > 0)
  560. {
  561. nowInterval = nowInterval.AddMinutes(1);
  562. }
  563. // 需要减去的分钟
  564. int minutesToSubtract = nowInterval.Minute % INTERVAL_FHR;
  565. var nextRunTime=nowInterval
  566. .AddMinutes(-minutesToSubtract)
  567. .AddMinutes(INTERVAL_FHR);
  568. TimeSpan timeUntilNextRun = nextRunTime - nowInterval;
  569. var ttl = (long)timeUntilNextRun.TotalSeconds;
  570. await SetIntervalTriggerAsync(fhrScheduleKey, heartRate.Serialno, ttl, heartRate);
  571. }
  572. /// <summary>
  573. ///
  574. /// </summary>
  575. /// <param name="heartRate"></param>
  576. /// <param name="commonPHR"></param>
  577. /// <param name="upperAlarmThreshold"></param>
  578. /// <param name="lowerAlarmThreshold"></param>
  579. /// <param name="phrValue"></param>
  580. /// <param name="sampleTime"></param>
  581. /// <param name="statStartTime"></param>
  582. /// <param name="statEndTime"></param>
  583. /// <returns></returns>
  584. private async Task SaveAndPushFetalHeartRateAsync(HisGpsHeartRate heartRate, PregnancyCommonHeartRateModel commonPHR, int upperAlarmThreshold, int lowerAlarmThreshold, double phrValue, string sampleTime, DateTime statStartTime, DateTime statEndTime)
  585. {
  586. // 计算胎心=孕妇心率*系数
  587. /**
  588. var fetalHeartRate = SafeType.SafeInt(phrValue * commonPHR?.StatModeAvgFprCoefficient!);
  589. fetalHeartRate = fetalHeartRate > 220 ? 220 : fetalHeartRate; // 胎心的最大值调整为220,超过都按该值220输出
  590. if (fetalHeartRate >= 220)
  591. {
  592. // 先使用最小系数计算
  593. var statMaxValueFprCoefficient = commonPHR?.StatMaxValueFprCoefficient!;
  594. var statMinValueFprCoefficient = commonPHR?.StatMinValueFprCoefficient!;
  595. var coefficient = statMaxValueFprCoefficient < statMinValueFprCoefficient ? statMaxValueFprCoefficient : statMinValueFprCoefficient;
  596. fetalHeartRate = SafeType.SafeInt(phrValue * coefficient);
  597. if (fetalHeartRate < 220)
  598. {
  599. _logger.LogWarning($"{heartRate.Serialno} 使用极值系数 {coefficient} ,建模数据可能出现异常,请检查");
  600. }
  601. else
  602. {
  603. fetalHeartRate = 220;
  604. _logger.LogWarning($"{heartRate.Serialno} 使用所有系数都不能放映实际,建模数据可能出现异常,请检查");
  605. }
  606. }
  607. */
  608. #region 胎心系数使用基于心率与中位数对比
  609. var coefficient = 0f;
  610. // 孕妇心率少于中位数,取StatMinValueFprCoefficient
  611. if (heartRate.HeartRate < commonPHR!.Mode)
  612. {
  613. coefficient = commonPHR.StatMinValueFprCoefficient!;
  614. _logger.LogInformation($"{heartRate.Serialno} 孕妇心率少于中位数,使用最小值系数 {coefficient}");
  615. }
  616. // 孕妇心率大于中位数,取StatMaxValueFprCoefficient与StatModeAvgFprCoefficient中少的那个
  617. else if (heartRate.HeartRate > commonPHR.Mode)
  618. {
  619. if (commonPHR.StatModeAvgFprCoefficient > commonPHR.StatMaxValueFprCoefficient)
  620. {
  621. coefficient = commonPHR.StatMaxValueFprCoefficient!;
  622. _logger.LogInformation($"{heartRate.Serialno} 孕妇心率大于中位数,使用最大值系数 {coefficient}");
  623. }
  624. else
  625. {
  626. coefficient = commonPHR.StatModeAvgFprCoefficient!;
  627. _logger.LogInformation($"{heartRate.Serialno} 孕妇心率大于中位数,使用均值系数 {coefficient}");
  628. }
  629. }
  630. else
  631. {
  632. coefficient = commonPHR.StatModeAvgFprCoefficient;
  633. _logger.LogInformation($"{heartRate.Serialno} 孕妇心率等于中位数,使用均值系数 {coefficient}");
  634. }
  635. #endregion
  636. var fetalHeartRate = SafeType.SafeInt(phrValue * coefficient);
  637. // 胎心的最大值调整为220,超过都按该值220输出
  638. // fetalHeartRate = fetalHeartRate>= 220 ? 220 : fetalHeartRate;
  639. if (fetalHeartRate > 220)
  640. {
  641. fetalHeartRate = 220;
  642. _logger.LogWarning($"{heartRate.Serialno} 大于220,按220输出,计算因子:孕妇心率 {heartRate.HeartRate},系数 {coefficient},周期 周期:{statStartTime.ToString("yyyy-MM-dd HH:mm:ss")}----{statEndTime.ToString("yyyy-MM-dd HH:mm:ss")}");
  643. }
  644. // 胎心的最小值调整为90,超过都按该值90
  645. if (fetalHeartRate < 90)
  646. {
  647. fetalHeartRate = 90;
  648. _logger.LogWarning($"{heartRate.Serialno} 小于90,按90输出,计算因子:孕妇心率 {heartRate.HeartRate},系数 {coefficient},周期 周期:{statStartTime.ToString("yyyy-MM-dd HH:mm:ss")}----{statEndTime.ToString("yyyy-MM-dd HH:mm:ss")}");
  649. }
  650. var isAbnormal = fetalHeartRate > upperAlarmThreshold ? 1 : (fetalHeartRate < lowerAlarmThreshold ? 2 : 0);
  651. var phrFreqstatus = await _deviceCacheMgr.GetPregnancyHeartRateFreqStatusAsync(heartRate.Serialno);
  652. if (phrFreqstatus == null) isAbnormal = 0;
  653. var statsusDesc = (phrFreqstatus == null) ? "常规" : "高频";
  654. _logger.LogInformation($"{heartRate.Serialno} 在 {statsusDesc} 状态,生成胎心值:{fetalHeartRate},统计周期:{statStartTime.ToString("yyyy-MM-dd HH:mm:ss")}----{statEndTime.ToString("yyyy-MM-dd HH:mm:ss")}");
  655. //if (!isFreq)
  656. //{
  657. // statStartTime = heartRate.LastUpdate;
  658. //
  659. //}
  660. // 保存到 数据服务 MySQL 数据库
  661. HisGpsFetalHeartRate gpsFetalHeartRate = new()
  662. {
  663. FetalHeartRateId = Guid.NewGuid().ToString("D"),
  664. PersonId = commonPHR!.PersonId,
  665. Serialno = heartRate.Serialno,
  666. HeartRate = fetalHeartRate,
  667. SampleTime = sampleTime.Length > 10 ? sampleTime.Substring(0, 10) : sampleTime,
  668. IsAbnormal = isAbnormal,
  669. StatStartTime = statStartTime,
  670. StatEndTime = statEndTime,//commonPHR.StatEndTime,
  671. CreateTime = DateTime.Now,
  672. Method = 1,
  673. IsDisplay = 1,
  674. DeviceKey = commonPHR!.DeviceKey
  675. };
  676. await _hisFetalHeartApiClient.AddAsync(gpsFetalHeartRate).ConfigureAwait(false);
  677. // 推送到api/v1/open/OpenIot/SetFetalHeartRateConfig
  678. // 推送最后一条常规心率计算的胎心数据到iot设备
  679. #region 推送最后一条常规心率计算的胎心数据到iot设备
  680. // 高频(<=12)-常规
  681. var lastPhr = await _serviceTDengine.GetLastAsync<PregnancyHeartRateModel>(heartRate.Serialno);
  682. if (lastPhr.MessageId == heartRate.MessageId && phrFreqstatus == null)
  683. {
  684. await _serviceIotApi.SetFetalHeartRateConfig(heartRate.Serialno, fetalHeartRate, sampleTime, isAbnormal);
  685. _logger.LogInformation($"{heartRate.Serialno} 推送最后一条常规心率计算的胎心数据到iot设备,高频(<=12)-常规");
  686. }
  687. // 高频(13)-常规-高频(13)
  688. if (phrFreqstatus != null)
  689. {
  690. var phr = await _serviceTDengine.GetBySerialNoAsync<PregnancyHeartRateModel>(heartRate.Serialno, 1);
  691. phr = phr.OrderByDescending(i => i.LastUpdate).ToList();
  692. // 获取高频数据
  693. var freqCollection = new List<PregnancyHeartRateModel>();
  694. PregnancyHeartRateModel? previousItem = null;
  695. foreach (var item in phr)
  696. {
  697. if (previousItem != null)
  698. {
  699. var timeNextDiff = (previousItem!.LastUpdate - item.LastUpdate).TotalSeconds;
  700. if (timeNextDiff <= 60)
  701. {
  702. freqCollection.Add(item);
  703. }
  704. }
  705. // 高频最后一条
  706. if (lastPhr.MessageId == item.MessageId)
  707. {
  708. freqCollection.Add(item);
  709. }
  710. previousItem = item;
  711. }
  712. //去除高频
  713. foreach (var item in freqCollection)
  714. {
  715. phr.Remove(item);
  716. }
  717. lastPhr = phr.FirstOrDefault();
  718. if (lastPhr?.MessageId == heartRate.MessageId)
  719. {
  720. await _serviceIotApi.SetFetalHeartRateConfig(heartRate.Serialno, fetalHeartRate, sampleTime, isAbnormal);
  721. _logger.LogInformation($"{heartRate.Serialno} 推送最后一条常规心率计算的胎心数据到iot设备,高频(13)-常规-高频(13)");
  722. }
  723. }
  724. #endregion
  725. #region 高频心率计算胎心数据到iot设备
  726. // 高频(17) ,连续12个高频正常,也不停止且数据偏高和偏低也推送到iot
  727. if (phrFreqstatus != null && isAbnormal!=0)
  728. {
  729. await _serviceIotApi.SetFetalHeartRateConfig(heartRate.Serialno, fetalHeartRate, sampleTime, isAbnormal);
  730. }
  731. #endregion
  732. var device = await _deviceCacheMgr.GetDeviceBySerialNoAsync(heartRate.Serialno).ConfigureAwait(false);
  733. var fhrMsgId = $"{heartRate.Serialno}-{sampleTime}-{Guid.NewGuid().ToString("D")[^3..]}";
  734. var fhrMsgTime = DateTimeUtil.GetDateTimeFromUnixTimeMilliseconds(long.Parse(sampleTime.Length < 13 ? sampleTime.PadRight(13, '0') : sampleTime)).ToString("yyyy-MM-dd HH:mm:ss");
  735. // 胎心数据推送到第三方
  736. var topic = "topic.push.third";
  737. var fhrThridMsg = new
  738. {
  739. messageId = fhrMsgId,
  740. topic = topic,
  741. time = fhrMsgTime,
  742. data = new
  743. {
  744. imei = heartRate.Serialno,
  745. value = fetalHeartRate,
  746. isAbnormal,
  747. type = "fetalHeart"
  748. }
  749. };
  750. await _serviceMqProcess.ProcessIMEIEventMessageAsync(fhrMsgId, topic, 31, fhrThridMsg).ConfigureAwait(false);
  751. // 胎心数据推送到微信
  752. if (isAbnormal != 0)
  753. {
  754. topic = "topic.push.wx";
  755. var fhrMsg = new
  756. {
  757. messageId = fhrMsgId,
  758. topic = topic,
  759. time = fhrMsgTime,
  760. data = new
  761. {
  762. deviceId = device?.DeviceId,
  763. imei = heartRate.Serialno,
  764. alarmTypeId = 12,
  765. alarmDeviceName = heartRate.Serialno,
  766. alarmRemarks = JsonConvert.SerializeObject(new { fetalHeartValue = fetalHeartRate, isAbnormal = isAbnormal }),
  767. address = string.Empty,
  768. deviceKey = device?.DeviceId
  769. }
  770. };
  771. await _serviceMqProcess.ProcessIMEIEventMessageAsync(fhrMsgId, topic, fhrMsg).ConfigureAwait(false);
  772. }
  773. }
  774. private async Task SetIntervalTriggerAsync(string key, string imei, long interval, HisGpsHeartRate heartRate)
  775. {
  776. // var key = $"health_monitor/schedule_push/{type}/imei/{imei}";
  777. var schedulePush = await _serviceEtcd.GetValAsync(key).ConfigureAwait(false);
  778. if (string.IsNullOrWhiteSpace(schedulePush))
  779. {
  780. var now = DateTime.Now;
  781. var timeNextRun = now.Add(TimeSpan.FromSeconds(interval));
  782. var data = new
  783. {
  784. imei,
  785. create_time = now.ToString("yyyy-MM-dd HH:mm:ss"),
  786. ttl = interval,
  787. next_run_time = timeNextRun.ToString("yyyy-MM-dd HH:mm:ss"),
  788. trigger = heartRate,
  789. };
  790. var result = JsonConvert.SerializeObject(data);
  791. await _serviceEtcd.PutValAsync(key, result, interval, false).ConfigureAwait(false);
  792. }
  793. }
  794. /// <summary>
  795. /// 高频延时计算触发器
  796. /// </summary>
  797. /// <param name="key"></param>
  798. /// <param name="imei"></param>
  799. /// <param name="interval"></param>
  800. /// <param name="heartRate"></param>
  801. /// <returns></returns>
  802. private async Task SetFreqHeartRateTriggerAsync(string key, string imei, long interval, HisGpsHeartRate? heartRate=null)
  803. {
  804. var schedulePush = await _serviceEtcd.GetValAsync(key).ConfigureAwait(false);
  805. if (string.IsNullOrWhiteSpace(schedulePush))
  806. {
  807. var now = DateTime.Now;
  808. var data = new
  809. {
  810. imei,
  811. create_time = now.ToString("yyyy-MM-dd HH:mm:ss"),
  812. lease = interval,
  813. trigger = heartRate,
  814. };
  815. var result = JsonConvert.SerializeObject(data);
  816. await _serviceEtcd.PutValAsync(key, result, interval, false).ConfigureAwait(false);
  817. }
  818. else
  819. {
  820. await _serviceEtcd.PutValAsync(key, schedulePush, interval, false).ConfigureAwait(false);
  821. }
  822. }
  823. /// <summary>
  824. /// 去除高频数据
  825. /// </summary>
  826. /// <param name="phr"></param>
  827. /// <param name="highFreqSampleInterva"></param>
  828. /// <returns></returns>
  829. private static List<PregnancyHeartRateModel> GetNonFreqPregnancyHeartRate(List<PregnancyHeartRateModel> phr,int highFreqSampleInterval)
  830. {
  831. //phr = phr.OrderByDescending(i => i.LastUpdate).ToList();
  832. //var result = new List<PregnancyHeartRateModel>();
  833. //PregnancyHeartRateModel? previousItem = null;
  834. //foreach (var item in phr)
  835. //{
  836. // if (previousItem != null)
  837. // {
  838. // var timeNextDiff =(previousItem!.LastUpdate - item.LastUpdate).TotalSeconds;
  839. // if (timeNextDiff > highFreqSampleInterval)
  840. // {
  841. // result.Add(previousItem);
  842. // }
  843. // }
  844. // previousItem = item;
  845. //}
  846. //// 添加上一个
  847. //if (previousItem != null)
  848. //{
  849. // result.Add(previousItem);
  850. //}
  851. //return result;
  852. #region 反向
  853. var phr1 = phr.OrderByDescending(i => i.LastUpdate).ToList();
  854. var result = new List<PregnancyHeartRateModel>();
  855. PregnancyHeartRateModel? previousItem1 = null;
  856. foreach (var item in phr1)
  857. {
  858. if (previousItem1 != null)
  859. {
  860. var timeNextDiff = (previousItem1!.LastUpdate - item.LastUpdate).TotalSeconds;
  861. if (timeNextDiff > highFreqSampleInterval)
  862. {
  863. result.Add(previousItem1);
  864. }
  865. }
  866. previousItem1 = item;
  867. }
  868. // 添加上一个
  869. if (previousItem1 != null)
  870. {
  871. result.Add(previousItem1);
  872. }
  873. #endregion
  874. #region 正向
  875. var phr2 = phr.OrderByDescending(i => i.LastUpdate).ToList(); ;
  876. var freqCollection = new List<PregnancyHeartRateModel>();
  877. PregnancyHeartRateModel? previousItem = null;
  878. foreach (var item in phr2)
  879. {
  880. if (previousItem != null)
  881. {
  882. var timeNextDiff = (previousItem!.LastUpdate - item.LastUpdate).TotalSeconds;
  883. if (timeNextDiff <= highFreqSampleInterval)
  884. {
  885. freqCollection.Add(item);
  886. }
  887. }
  888. previousItem = item;
  889. }
  890. //去除高频
  891. foreach (var item in freqCollection)
  892. {
  893. phr2.Remove(item);
  894. }
  895. #endregion
  896. // 交集
  897. var commonElements = phr2.Intersect(result).ToList();
  898. return commonElements;
  899. }
  900. /// <summary>
  901. /// 获取高频数据
  902. /// </summary>
  903. /// <param name="phr"></param>
  904. /// <param name="highFreqSampleInterval"></param>
  905. /// <returns></returns>
  906. private static List<PregnancyHeartRateModel> GetFreqPregnancyHeartRate(List<PregnancyHeartRateModel> phr, int highFreqSampleInterval)
  907. {
  908. phr = phr.OrderByDescending(i => i.LastUpdate).ToList();
  909. var freqCollection = new List<PregnancyHeartRateModel>();
  910. PregnancyHeartRateModel? previousItem = null;
  911. foreach (var item in phr)
  912. {
  913. if (previousItem != null)
  914. {
  915. var timeNextDiff = (previousItem.LastUpdate - item.LastUpdate).TotalSeconds;
  916. if (timeNextDiff <= highFreqSampleInterval)
  917. {
  918. freqCollection.Add(previousItem);
  919. }
  920. }
  921. previousItem = item;
  922. }
  923. // 检查最后一条是否高频
  924. if (previousItem != null && (phr.Last().LastUpdate - previousItem.LastUpdate).TotalSeconds <= highFreqSampleInterval)
  925. {
  926. freqCollection.Add(previousItem);
  927. }
  928. return freqCollection;
  929. }
  930. }
  931. }