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.

933 lines
53KB

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