Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

1454 lines
85KB

  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[] {0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22};
  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. var edoc = DateTimeUtil.ToDateTime(watchConfig!["EDOC"]!.ToString());
  100. int pregnancyWeek = (DateTime.Now - edoc.AddDays(-280)).Days / 7;
  101. // 12-45周之间
  102. if (pregnancyWeek < 12 && pregnancyWeek > 45)
  103. {
  104. _logger.LogWarning($"{heartRate.Serialno} 孕周{pregnancyWeek},不在计算范围 12-45 周内,胎心胎动都不计算");
  105. return;
  106. }
  107. //_logger.LogInformation($"{heartRate.Serialno} 计算胎心胎动启动");
  108. #region 定时下发触发器(定时建模)
  109. var key = $"health_monitor/schedule_push/pregnancy_heart_rate/imei/{heartRate.Serialno}";
  110. var schedule_push = await _serviceEtcd.GetValAsync(key).ConfigureAwait(false);
  111. if (string.IsNullOrWhiteSpace(schedule_push))
  112. {
  113. // 注册首次下推
  114. var interval = 0;
  115. // 获取当前时间
  116. DateTime now = DateTime.Now;
  117. var rand = new Random();
  118. var pushSec = rand.Next(59);
  119. int pushMin = int.TryParse(heartRate.Serialno.AsSpan(heartRate.Serialno.Length - 1), out pushMin) ? pushMin : 10;
  120. // 计算距离下一个$interval天后的8点的时间间隔
  121. DateTime nextRunTime = new DateTime(now.Year, now.Month, now.Day, 6, pushMin, pushSec).AddDays(interval);
  122. TimeSpan timeUntilNextRun = nextRunTime - now;
  123. if (timeUntilNextRun < TimeSpan.Zero)
  124. {
  125. timeUntilNextRun = timeUntilNextRun.Add(TimeSpan.FromDays(1));
  126. nextRunTime += TimeSpan.FromDays(1);
  127. }
  128. var ttl = (long)timeUntilNextRun.TotalSeconds;
  129. var data = new
  130. {
  131. imei = heartRate.Serialno,
  132. create_time = now.ToString("yyyy-MM-dd HH:mm:ss"),
  133. ttl,
  134. next_run_time = nextRunTime.ToString("yyyy-MM-dd HH:mm:ss")
  135. };
  136. var result = JsonConvert.SerializeObject(data);
  137. await _serviceEtcd.PutValAsync(key, result, ttl, false).ConfigureAwait(false);
  138. }
  139. #endregion
  140. //_logger.LogInformation($"{heartRate.Serialno} 触发定时建模");
  141. // 高频心率采样间隔 highFreqSampleInterval = highFreqSampleInterval 最小highFreqSampleInterval=60)
  142. var highFreqSampleInterval = (int)watchConfig!["highFreqSampleInterval"]! >= 60 ? (int)watchConfig!["highFreqSampleInterval"]!:60;
  143. // 触发高频监测的心率上限值
  144. var triggerHighFreqHigh = (int)watchConfig["triggerHighFreqHigh"]!;
  145. // 触发高频监测的心率下限值
  146. var triggerHighFreqLow = (int)watchConfig["triggerHighFreqLow"]!;
  147. // 暂时处理
  148. triggerHighFreqLow = triggerHighFreqLow > 60 && triggerHighFreqLow < 67 ? 60 : triggerHighFreqLow;
  149. //停止高频心率采样心率连续正常次数
  150. var stopHighFreqSampleCount = (int)watchConfig["stopHighFreqSampleCount"]!;
  151. // 高频心率采集时长 0 为持续采集,非零为高频心率的采集时长
  152. //var highFreqSampleTimes = (int)watchConfig["highFreqSampleTimes"]!;
  153. var highFreqSampleTimes = 540;
  154. // 告警上限阀值
  155. var upperAlarmThreshold = (int)watchConfig["upperAlarmThreshold"]!;
  156. // 告警下限阀值
  157. var lowerAlarmThreshold = (int)watchConfig["lowerAlarmThreshold"]!;
  158. var daysPhr = await _serviceTDengine.GetBySerialNoAsync<PregnancyHeartRateModel>(heartRate.Serialno, 7);
  159. var phr = daysPhr
  160. .Where(p => p.LastUpdate <= heartRate.LastUpdate)
  161. .ToList();
  162. _logger.LogInformation($"{heartRate.Serialno} 当前数据在TD数据库:{phr.Select(i=>i.MessageId).Contains(heartRate.MessageId)}");
  163. var nonFreqPhr = GetNonFreqPregnancyHeartRate(phr, highFreqSampleInterval);
  164. if (nonFreqPhr.Count >= 30)
  165. {
  166. #region 定时计算胎动数据触发器两小时间隔开始(奇数小时计算)
  167. /**
  168. var fetalMovementKey = $"health_monitor/schedule_push/cal_fetal_movement/imei/{heartRate.Serialno}";
  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. DateTime fmScheduleNow = DateTime.Now;
  173. int hour = fmScheduleNow.Hour;
  174. int selectedScheduleHour;
  175. // 使用模运算来获取相应的ODD_SCHEDULE_HOUR
  176. if (hour % 2 == 0)
  177. {
  178. selectedScheduleHour = ODD_SCHEDULE_HOUR[hour / 2 % ODD_SCHEDULE_HOUR.Length];
  179. }
  180. else
  181. {
  182. selectedScheduleHour = ODD_SCHEDULE_HOUR[(hour + 1) / 2 % ODD_SCHEDULE_HOUR.Length];
  183. }
  184. DateTime scheduledDateTime = new DateTime(
  185. fmScheduleNow.Year,
  186. fmScheduleNow.Month,
  187. fmScheduleNow.Day,
  188. selectedScheduleHour,
  189. pushMin,
  190. pushSec
  191. );
  192. // 如果生成的时间在当前时间之前
  193. // 或者 fmScheduleNow 在 23 点之后并且 selectedScheduleHour 在 1 点(即次日)
  194. if (scheduledDateTime < fmScheduleNow || (fmScheduleNow.Hour >= 23 && selectedScheduleHour <= 1))
  195. {
  196. scheduledDateTime = scheduledDateTime.AddDays(1);
  197. }
  198. TimeSpan timeUntilNextRun = scheduledDateTime - fmScheduleNow;
  199. var ttl = (long)timeUntilNextRun.TotalSeconds;
  200. await SetIntervalTriggerAsync(fetalMovementKey, heartRate.Serialno, ttl, heartRate);
  201. _logger.LogInformation($"{heartRate.Serialno}-{heartRate.MessageId} 创建计划统计胎动时间{scheduledDateTime.ToString("yyyy-MM-dd HH:mm:ss")}");
  202. */
  203. #endregion
  204. #region 定时计算胎动数据触发器每小时间隔开始,延时一个小时计算
  205. /*
  206. ///当前时间是的0~1小时返回2小时 ,
  207. ///当前时间是的1~2小时返回3小时 ,如此类推,
  208. ///当前时间是的21~22小时返回23小时
  209. ///当前时间是的23~0小时返回次日1小时
  210. var fetalMovementKey = $"health_monitor/schedule_push/cal_fetal_movement/imei/{heartRate.Serialno}";
  211. var rand = new Random();
  212. var pushSec = rand.Next(59);
  213. int pushMin = int.TryParse(heartRate.Serialno.AsSpan(heartRate.Serialno.Length - 1), out pushMin) ? pushMin : 10;
  214. DateTime fmScheduleNow = DateTime.Now;
  215. int hour = fmScheduleNow.Hour;
  216. int selectedScheduleHour=DateTimeUtil.GetNextHour(fmScheduleNow.Hour);
  217. DateTime scheduledDateTime = new DateTime(
  218. fmScheduleNow.Year,
  219. fmScheduleNow.Month,
  220. fmScheduleNow.Day,
  221. selectedScheduleHour,
  222. pushMin,
  223. pushSec
  224. );
  225. // 跨天
  226. if (selectedScheduleHour == 1 || selectedScheduleHour == 0)
  227. {
  228. scheduledDateTime = scheduledDateTime.AddDays(1);
  229. }
  230. TimeSpan timeUntilNextRun = scheduledDateTime - fmScheduleNow;
  231. var ttl = (long)timeUntilNextRun.TotalSeconds;
  232. await SetIntervalTriggerAsync(fetalMovementKey, heartRate.Serialno, ttl, heartRate);
  233. _logger.LogInformation($"{heartRate.Serialno}-{heartRate.MessageId} 创建计划统计胎动时间{scheduledDateTime.ToString("yyyy-MM-dd HH:mm:ss")}");
  234. */
  235. #endregion
  236. #region 定时计算胎动数据触发器每小时间隔开始,延时一个小时计算
  237. ///当前时间是的0~1小时返回2小时 ,
  238. ///当前时间是的1~2小时返回3小时 ,如此类推,
  239. ///当前时间是的21~22小时返回23小时
  240. ///当前时间是的23~0小时返回次日1小时
  241. // lastUpdate默认奇数 // lastUpdate偶数小时
  242. var fetalMovementKey = $"health_monitor/schedule_push/cal_fetal_movement/imei/{heartRate.Serialno}";
  243. var rand = new Random();
  244. var pushSec = rand.Next(59);
  245. int pushMin = int.TryParse(heartRate.Serialno.AsSpan(heartRate.Serialno.Length - 1), out pushMin) ? pushMin : 10;
  246. DateTime fmScheduleNow = DateTime.Now;
  247. int hour = fmScheduleNow.Hour;
  248. int selectedScheduleHour = DateTimeUtil.GetNextHour(fmScheduleNow.Hour);
  249. DateTime scheduledDateTime = DateTime.Now.Date
  250. .AddHours(selectedScheduleHour)
  251. .AddMinutes(pushMin)
  252. .AddSeconds(pushSec);
  253. // 当前时间与lastUpdate的时间差小于2小时,0~1->2,0~1的数据是1点的sample_time,2点计算
  254. if (DateTime.Now.Subtract((DateTime)heartRate.LastUpdate!).TotalHours <= 2)
  255. {
  256. selectedScheduleHour = DateTimeUtil.GetNextHour(((DateTime)heartRate.LastUpdate).Hour);
  257. scheduledDateTime = DateTime.Now.Date
  258. .AddHours(selectedScheduleHour)
  259. .AddMinutes(pushMin)
  260. .AddSeconds(pushSec);
  261. if (((DateTime)heartRate.LastUpdate).Hour % 2 == 0)
  262. {
  263. // lastUpdate偶数小时
  264. fetalMovementKey = $"health_monitor/schedule_push/cal_fetal_movement/imei/{heartRate.Serialno}/even_hour";
  265. }
  266. else
  267. {
  268. // lastUpdate奇数小时
  269. fetalMovementKey = $"health_monitor/schedule_push/cal_fetal_movement/imei/{heartRate.Serialno}/odd_hour";
  270. }
  271. }
  272. // sample_time采集时间与计划计算时间最长是2小时,次日上报引起
  273. else
  274. {
  275. _logger.LogWarning($"iot 上报延迟,last_update与计划计算时间超过2小时");
  276. }
  277. // 跨天
  278. if (selectedScheduleHour == 1 || selectedScheduleHour == 0)
  279. {
  280. scheduledDateTime = scheduledDateTime.AddDays(1);
  281. }
  282. TimeSpan timeUntilNextRun = scheduledDateTime - fmScheduleNow;
  283. var ttl = (long)timeUntilNextRun.TotalSeconds;
  284. await SetIntervalTriggerAsync(fetalMovementKey, heartRate.Serialno, ttl, heartRate);
  285. _logger.LogInformation($"{heartRate.Serialno}-{heartRate.MessageId} 创建计划统计胎动时间{scheduledDateTime.ToString("yyyy-MM-dd HH:mm:ss")}");
  286. #endregion
  287. #region 计算胎心数据(按心率时间LastUpdate)
  288. var commonPHR = await _serviceTDengine.GetLastAsync<PregnancyCommonHeartRateModel>(heartRate.Serialno);
  289. // 如果commonPHR为空或已建模已过期,进行建模和保存
  290. if (commonPHR == null || !commonPHR.CreateTime.Date.Equals(DateTime.Now.Date))
  291. // 获取当天 6:11 的时间点, 6:11 所有数据都已经全部建模
  292. //if (commonPHR == null || commonPHR.CreateTime < DateTime.Today.AddHours(6).AddMinutes(11))
  293. {
  294. // 处理孕妇业务,计算一般心率并下发
  295. commonPHR = await _serviceTDengine.InitPregnancyCommonHeartRateModeAsync(heartRate.Serialno, highFreqSampleInterval: highFreqSampleInterval);
  296. // 建模完成
  297. var flag = await _serviceIotApi.SetFetalConfig(heartRate.Serialno, 1, commonPHR!.MaxValue, commonPHR!.MinValue);
  298. _logger.LogInformation($"{heartRate.Serialno} 记录数量足够,建模完成");
  299. // 保存到TDengine数据库
  300. await _serviceTDengine.InsertAsync<PregnancyCommonHeartRateModel>("hm_pchr", commonPHR!);
  301. _logger.LogInformation($"保存TDengine完成");
  302. }
  303. // 获取最近的两个记录,并计算它们的 LastUpdate 时间差
  304. var firstTwoPhr = phr.OrderByDescending(i => i.LastUpdate).Take(2).Select(i => i.LastUpdate).ToList();
  305. var timeDiff = firstTwoPhr[0] - firstTwoPhr[1];
  306. // 如果需要,将时间差转换为秒
  307. var timeDiffInSeconds = timeDiff.TotalSeconds;
  308. // 高频统计结束时间
  309. var FreqStatsEnd = DateTime.Now;
  310. // 高频心率启动(在高频第二条才能判断)
  311. if (timeDiffInSeconds <= highFreqSampleInterval)
  312. {
  313. // 设置延时计算高频心率的胎心
  314. var freqHearRateHey = $"health_monitor/schedule_push/cal_freqhr_fetal_heart_rate/imei/{heartRate.Serialno}";
  315. var phrFreqstatus = await _deviceCacheMgr.GetPregnancyHeartRateFreqStatusAsync(heartRate.Serialno);
  316. if (phrFreqstatus == null)
  317. {
  318. /// 设置高频状态
  319. _logger.LogInformation($"{heartRate.Serialno} 进入高频心率启动状态,时间差 timeDiffInSeconds {timeDiffInSeconds},highFreqSampleInterval:{highFreqSampleInterval}");
  320. // 设置高频状态
  321. _logger.LogInformation($"{heartRate.Serialno} 前7天到当前记录数 {phr.Count},当前 {phr.First().MessageId}");
  322. var freqFirstPhr = phr.OrderByDescending(i => i.LastUpdate)
  323. .Skip(1) //在高频第二条才能判断,所以要去除本条记录
  324. .First();
  325. _logger.LogInformation($"首条高频附近的3条:{string.Join(',', phr.OrderByDescending(i => i.LastUpdate).Take(3).Select(i=>i.MessageId))}");
  326. await _deviceCacheMgr.SetPregnancyHeartRateFreqStatusAsync(heartRate.Serialno, freqFirstPhr);
  327. _logger.LogInformation($"{heartRate.Serialno} 设置高频状态,首条高频心率ID:{freqFirstPhr.MessageId}");
  328. phrFreqstatus = await _deviceCacheMgr.GetPregnancyHeartRateFreqStatusAsync(heartRate.Serialno);
  329. HisGpsHeartRate freqFirstHR = new()
  330. {
  331. CreateTime = freqFirstPhr.CreateTime,
  332. DeviceKey = freqFirstPhr.DeviceKey,
  333. HeartRate = freqFirstPhr.PregnancyHeartRate,
  334. HeartRateId = freqFirstPhr.PregnancyHeartRateId,
  335. IsDisplay = freqFirstPhr.IsDisplay ? 1 : 0,
  336. LastUpdate = freqFirstPhr.LastUpdate,
  337. MessageId = freqFirstPhr.MessageId,
  338. Method = freqFirstPhr.Method,
  339. PersonId = freqFirstPhr.PersonId,
  340. Serialno = freqFirstPhr.SerialNumber,
  341. };
  342. // 设置的续租周期
  343. await SetFreqHeartRateTriggerAsync(freqHearRateHey, heartRate.Serialno, highFreqSampleInterval+90, freqFirstHR);
  344. }
  345. // 续租延时计算高频心率的胎心
  346. await SetFreqHeartRateTriggerAsync(freqHearRateHey, heartRate.Serialno, highFreqSampleInterval+90);
  347. /// phr PregnancyHeartRate 连续连续正常次数个值都是正常(大于等于triggerHighFreqLow,少于等于triggerHighFreqHig),
  348. /// 取连续正常次数正常值的平均值,推送到api/v1/open/OpenIot/SetFetalHeartRateConfig
  349. #region 检查高频状态是否连续12个心率值都是正常的
  350. // 获取最近连续正常次数个心率记录
  351. //_logger.LogInformation($"{heartRate.Serialno} 设置 stopHighFreqSampleCount {stopHighFreqSampleCount}");
  352. //var lastPhr = phr.OrderByDescending(i => i.LastUpdate).Take(stopHighFreqSampleCount).ToList();
  353. var lastPhr = phr.Where(i => i.LastUpdate >= phrFreqstatus!.LastUpdate)
  354. .OrderByDescending(i => i.LastUpdate).Take(stopHighFreqSampleCount).ToList();
  355. _logger.LogInformation($"{heartRate.Serialno} 最后段记录数量:lastPhr.Count {lastPhr.Count},stopHighFreqSampleCount {stopHighFreqSampleCount}");
  356. _logger.LogInformation($"{heartRate.Serialno} 条件1-数量 Count:{lastPhr.Count >= stopHighFreqSampleCount}");
  357. _logger.LogInformation($"{heartRate.Serialno} 条件2-是否正常值 ALL{lastPhr.All(i => i.PregnancyHeartRate >= triggerHighFreqLow && i.PregnancyHeartRate <= triggerHighFreqHigh)}");
  358. // 检查是否连续12个值都是正常的
  359. if ((lastPhr.Count >= stopHighFreqSampleCount) &&
  360. lastPhr.All(i => i.PregnancyHeartRate >= triggerHighFreqLow && i.PregnancyHeartRate <= triggerHighFreqHigh)
  361. )
  362. {
  363. var avgPhr = lastPhr.Select(i => i.PregnancyHeartRate).Average();
  364. // 计算一般心率得到胎心系数
  365. //await SaveAndPushFreqFetalHeartRateAsync(heartRate, upperAlarmThreshold, lowerAlarmThreshold, avgPhr, DateTimeUtil.ConvertToTimeStamp(DateTime.Now).ToString());
  366. // 高频数据不建模
  367. FreqStatsEnd = (DateTime)heartRate.LastUpdate!;
  368. _logger.LogInformation($"{heartRate.Serialno} 高频状态已经持续{(FreqStatsEnd - phrFreqstatus!.LastUpdate).TotalSeconds} 秒,连续 {stopHighFreqSampleCount} 次采样心率正常,将下发指令");
  369. var freqSaveAndPushFetalHeartRate = await _deviceCacheMgr.GetBizIntervalAsync(heartRate.Serialno, "SaveAndPushFetalHeartRate");
  370. // 高频不停,15分钟内只下发一条
  371. if (string.IsNullOrEmpty(freqSaveAndPushFetalHeartRate))
  372. {
  373. //heartRate.HeartRate = (int)avgPhr;
  374. //// 最后一条高频心率
  375. //var lastFreqHr = lastPhr.First();
  376. //var ts = DateTimeUtil.GetTimeDifferenceInSeconds((DateTime)lastFreqHr.LastUpdate!, phrFreqstatus!.LastUpdate);
  377. //// 判断是否够highFreqSampleTimes,540s
  378. //if (ts < highFreqSampleTimes)
  379. //{
  380. // /// 不够10分钟最近12个数据生成胎心值
  381. // //int selectedHrValue = SelectValueFromFreqHeartRate(heartRate, triggerHighFreqHigh, triggerHighFreqLow, lastPhr);
  382. // int selectedHrValue = SelectValueFromFreqHeartRate(heartRate.Serialno, triggerHighFreqHigh, triggerHighFreqLow, highFreqSampleTimes, phrFreqstatus, lastPhr);
  383. // heartRate.HeartRate = selectedHrValue;
  384. // _logger.LogInformation($"{heartRate.Serialno} 不够10分钟最近12个数据中取 {heartRate.HeartRate} 生成胎心值");
  385. // heartRate.LastUpdate = lastFreqHr.LastUpdate;
  386. //}
  387. //else
  388. //{
  389. // /// 超过10分钟最近12个数据生成胎心值
  390. // //int selectedHrValue = SelectValueFromFreqHeartRate(heartRate, triggerHighFreqHigh, triggerHighFreqLow, lastPhr);
  391. // int selectedHrValue = SelectValueFromFreqHeartRate(heartRate.Serialno, triggerHighFreqHigh, triggerHighFreqLow, highFreqSampleTimes, phrFreqstatus, lastPhr);
  392. // heartRate.HeartRate = selectedHrValue;
  393. // _logger.LogInformation($"{heartRate.Serialno} 超过10分钟最近12个数据中取 {heartRate.HeartRate} 生成胎心值");
  394. //}
  395. //_logger.LogInformation($"{heartRate.Serialno} 高频数据触发连续12个值都是正常的的高频心率处理");
  396. //await SaveAndPushFetalHeartRateEndFreqHeartRateAsync(heartRate, commonPHR,highFreqSampleTimes ,upperAlarmThreshold, lowerAlarmThreshold, DateTimeUtil.ConvertToTimeStamp(phrFreqstatus!.LastUpdate).ToString(), phrFreqstatus!.LastUpdate, FreqStatsEnd);
  397. _logger.LogInformation($"{heartRate.Serialno} 高频数据触发连续12个值都是正常的的高频心率处理");
  398. await SaveAndPushFetalHeartRateEndFreqHeartRateAsync(heartRate, commonPHR, highFreqSampleTimes, upperAlarmThreshold, lowerAlarmThreshold, DateTimeUtil.ConvertToTimeStamp(phrFreqstatus!.LastUpdate).ToString(), phrFreqstatus!.LastUpdate, FreqStatsEnd, lastPhr, triggerHighFreqHigh, triggerHighFreqLow);
  399. // 删除高频状态的首条记录
  400. await _deviceCacheMgr.DelPregnancyHeartRateFreqStatusAsync(heartRate.Serialno);
  401. // 设置15分的SaveAndPushFetalHeartRate业务间隔
  402. await _deviceCacheMgr.SetBizIntervalAsync(heartRate.Serialno, "SaveAndPushFetalHeartRate");
  403. _logger.LogInformation($"{heartRate.Serialno} 连续 {stopHighFreqSampleCount} 次采样心率正常,结束高频心率状态, timeDiffInSeconds {timeDiffInSeconds},highFreqSampleInterval:{highFreqSampleInterval},高频状态持续{((DateTime)heartRate.LastUpdate - phrFreqstatus!.LastUpdate).TotalSeconds} 秒,高频心率平均值:{(int)avgPhr}");
  404. }
  405. else
  406. {
  407. _logger.LogWarning($"{heartRate.Serialno} 连续 {stopHighFreqSampleCount} 次采样心率正常,设备端应该结束高频状态,但设备端没有结束高频,平台高频15分钟内已经下发过指令");
  408. }
  409. }
  410. else
  411. {
  412. _logger.LogInformation($"{heartRate.Serialno} 处于高频状态...");
  413. }
  414. #endregion
  415. }
  416. // 高频心率结束或常规心率
  417. else
  418. {
  419. var phrFreqstatus = await _deviceCacheMgr.GetPregnancyHeartRateFreqStatusAsync(heartRate.Serialno);
  420. // 高频心率结束
  421. if (phrFreqstatus != null)
  422. {
  423. /// 在highFreqSampleTimes=0一直异常(大于等于triggerHighFreqLow,少于等于triggerHighFreqHig),
  424. /// 取所有值的平均值,推送胎心数据到api/v1/open/OpenIot/SetFetalHeartRateConfig
  425. if (highFreqSampleTimes == 0)
  426. {
  427. var lastPhr = phr.OrderByDescending(i => i.LastUpdate)
  428. .Where(i => i.LastUpdate >= phrFreqstatus?.LastUpdate)
  429. .Skip(1) // 去除首条
  430. .Where(i => i.PregnancyHeartRate < triggerHighFreqLow || i.PregnancyHeartRate > triggerHighFreqHigh)
  431. .ToList();
  432. //.Select(i => i.PregnancyHeartRate);
  433. //.Average();
  434. var avgPhr = lastPhr.Select(i => i.PregnancyHeartRate).Average();
  435. // 推送胎心数据到 api/v1/open/OpenIot/SetFetalHeartRateConfig
  436. // 计算一般心率得到胎心系数
  437. // 高频数据不建模
  438. FreqStatsEnd = firstTwoPhr[1];
  439. _logger.LogInformation($"{heartRate.Serialno} 高频状态已经持续{(FreqStatsEnd - phrFreqstatus!.LastUpdate).TotalSeconds} 秒,highFreqSampleTimes={highFreqSampleTimes}秒,即将结束高频状态,高频心率平均值:{(int)avgPhr},将下发指令");
  440. //await SaveAndPushFreqFetalHeartRateAsync(heartRate, commonPHR, upperAlarmThreshold, lowerAlarmThreshold, avgPhr, DateTimeUtil.ConvertToTimeStamp(phrFreqstatus!.LastUpdate).ToString());
  441. //await SaveAndPushFetalHeartRateAsync(heartRate, commonPHR, upperAlarmThreshold, lowerAlarmThreshold, avgPhr, DateTimeUtil.ConvertToTimeStamp(phrFreqstatus!.LastUpdate).ToString(), phrFreqstatus!.LastUpdate, FreqStatsEnd);
  442. //heartRate.HeartRate = (int)avgPhr;
  443. //// 最后一条高频心率
  444. //var lastFreqHr = lastPhr.First();
  445. //var ts = DateTimeUtil.GetTimeDifferenceInSeconds((DateTime)lastFreqHr.LastUpdate!, phrFreqstatus!.LastUpdate);
  446. //// 判断是否够highFreqSampleTimes,540s
  447. //if (ts < highFreqSampleTimes)
  448. //{
  449. // /// 不够10分钟最近12个数据生成胎心值
  450. // //int selectedHrValue = SelectValueFromFreqHeartRate(heartRate, triggerHighFreqHigh, triggerHighFreqLow, lastPhr);
  451. // int selectedHrValue = SelectValueFromFreqHeartRate(heartRate.Serialno, triggerHighFreqHigh, triggerHighFreqLow, highFreqSampleTimes, phrFreqstatus, lastPhr);
  452. // heartRate.HeartRate = selectedHrValue;
  453. // _logger.LogInformation($"{heartRate.Serialno} 不够10分钟最近12个数据中取 {heartRate.HeartRate} 生成胎心值");
  454. // heartRate.LastUpdate = lastFreqHr.LastUpdate;
  455. //}
  456. //else
  457. //{
  458. // //int selectedHrValue = SelectValueFromFreqHeartRate(heartRate, triggerHighFreqHigh, triggerHighFreqLow, lastPhr);
  459. // int selectedHrValue = SelectValueFromFreqHeartRate(heartRate.Serialno, triggerHighFreqHigh, triggerHighFreqLow, highFreqSampleTimes, phrFreqstatus, lastPhr);
  460. // heartRate.HeartRate = selectedHrValue;
  461. // /// 超过10分钟最近12个数据生成胎心值
  462. // _logger.LogInformation($"{heartRate.Serialno} 超过10分钟最近12个数据中取 {heartRate.HeartRate} 生成胎心值");
  463. //}
  464. //_logger.LogInformation($"{heartRate.Serialno} 高频结束后的highFreqSampleTimes=0的高频心率处理");
  465. //await SaveAndPushFetalHeartRateEndFreqHeartRateAsync(heartRate, commonPHR, highFreqSampleTimes, upperAlarmThreshold, lowerAlarmThreshold, DateTimeUtil.ConvertToTimeStamp(phrFreqstatus!.LastUpdate).ToString(), phrFreqstatus!.LastUpdate, FreqStatsEnd);
  466. _logger.LogInformation($"{heartRate.Serialno} 高频结束后的highFreqSampleTimes=0的高频心率处理");
  467. await SaveAndPushFetalHeartRateEndFreqHeartRateAsync(heartRate, commonPHR, highFreqSampleTimes, upperAlarmThreshold, lowerAlarmThreshold, DateTimeUtil.ConvertToTimeStamp(phrFreqstatus!.LastUpdate).ToString(), phrFreqstatus!.LastUpdate, FreqStatsEnd, lastPhr, triggerHighFreqHigh, triggerHighFreqLow);
  468. }
  469. /// 在highFreqSampleTimes>0一直异常(大于等于triggerHighFreqLow,少于等于triggerHighFreqHig),
  470. /// 取所有值的平均值,推送胎心数据到api/v1/open/OpenIot/SetFetalHeartRateConfig
  471. if (highFreqSampleTimes > 0 && heartRate.LastUpdate >= (phrFreqstatus?.LastUpdate.AddSeconds(highFreqSampleTimes)))
  472. {
  473. // 获取高频心率数据个数
  474. var filterPhr = phr
  475. .Where(i => i.LastUpdate >= phrFreqstatus?.LastUpdate)
  476. .Skip(1)
  477. .ToList();
  478. _logger.LogInformation($"{heartRate.Serialno} 高频周期 {phrFreqstatus?.LastUpdate.ToString("yyyy-MM-dd HH:mm:ss")}--{firstTwoPhr[1].ToString("yyyy-MM-dd HH:mm:ss")} 产生高频心率数量 {filterPhr.Count} 条");
  479. // 高频心率数据大于stopHighFreqSampleCount/12个才计算胎心数据
  480. //
  481. if (filterPhr.Count > stopHighFreqSampleCount)
  482. {
  483. FreqStatsEnd = firstTwoPhr[1];
  484. var avgPhr = filterPhr
  485. .OrderByDescending(i => i.LastUpdate)
  486. .Take(stopHighFreqSampleCount) // 计算最后12条
  487. .Select(i => i.PregnancyHeartRate).Average();
  488. //_logger.LogInformation($"{heartRate.Serialno} 高频状态已经持续{(FreqStatsEnd - phrFreqstatus!.LastUpdate).TotalSeconds} 秒,约定的 {highFreqSampleTimes} 秒,即将结束高频状态,高频心率平均值:{(int)avgPhr},将下发指令");
  489. //计算高频
  490. //await SaveAndPushFetalHeartRateAsync(heartRate, commonPHR, upperAlarmThreshold, lowerAlarmThreshold, avgPhr, DateTimeUtil.ConvertToTimeStamp(phrFreqstatus!.LastUpdate).ToString(), phrFreqstatus!.LastUpdate, FreqStatsEnd);
  491. heartRate.HeartRate = (int)avgPhr;
  492. // 判断是否够highFreqSampleTimes,540s
  493. var lastPhr = filterPhr
  494. .OrderByDescending(i => i.LastUpdate)
  495. .Take(stopHighFreqSampleCount).ToList();
  496. //// 最后一条高频心率
  497. //var lastFreqHr = lastPhr.First();
  498. //var ts = DateTimeUtil.GetTimeDifferenceInSeconds((DateTime)lastFreqHr.LastUpdate!, phrFreqstatus!.LastUpdate);
  499. //if (ts < highFreqSampleTimes)
  500. //{
  501. // /// 不够10分钟最近12个数据的成胎心值
  502. // //int selectedHrValue = SelectValueFromFreqHeartRate(heartRate, triggerHighFreqHigh, triggerHighFreqLow, lastPhr);
  503. // int selectedHrValue = SelectValueFromFreqHeartRate(heartRate.Serialno, triggerHighFreqHigh, triggerHighFreqLow, highFreqSampleTimes, phrFreqstatus, lastPhr);
  504. // heartRate.HeartRate = selectedHrValue;
  505. // _logger.LogInformation($"{heartRate.Serialno} 不够10分钟最近12个数据中取 {heartRate.HeartRate} 生成胎心值");
  506. // heartRate.LastUpdate = lastFreqHr.LastUpdate;
  507. //}
  508. //else
  509. //{
  510. // //int selectedHrValue = SelectValueFromFreqHeartRate(heartRate, triggerHighFreqHigh, triggerHighFreqLow, lastPhr);
  511. // int selectedHrValue = SelectValueFromFreqHeartRate(heartRate.Serialno, triggerHighFreqHigh, triggerHighFreqLow, highFreqSampleTimes, phrFreqstatus, lastPhr);
  512. // heartRate.HeartRate = selectedHrValue;
  513. // /// 超过10分钟最近12个数据的生成胎心值
  514. // _logger.LogInformation($"{heartRate.Serialno} 超过10分钟最近12个数据的中取 {heartRate.HeartRate} 生成胎心值");
  515. //}
  516. //_logger.LogInformation($"{heartRate.Serialno} 高频结束后的在highFreqSampleTimes>0 正常心率(通常情况)触发的高频心率处理");
  517. //await SaveAndPushFetalHeartRateEndFreqHeartRateAsync(heartRate, commonPHR, highFreqSampleTimes, upperAlarmThreshold, lowerAlarmThreshold, DateTimeUtil.ConvertToTimeStamp(phrFreqstatus!.LastUpdate).ToString(), phrFreqstatus!.LastUpdate, FreqStatsEnd);
  518. _logger.LogInformation($"{heartRate.Serialno} 高频结束后的在highFreqSampleTimes>0 正常心率(通常情况)触发的高频心率处理");
  519. await SaveAndPushFetalHeartRateEndFreqHeartRateAsync(heartRate, commonPHR, highFreqSampleTimes, upperAlarmThreshold, lowerAlarmThreshold, DateTimeUtil.ConvertToTimeStamp(phrFreqstatus!.LastUpdate).ToString(), phrFreqstatus!.LastUpdate, FreqStatsEnd, lastPhr, triggerHighFreqHigh, triggerHighFreqLow);
  520. }
  521. else
  522. {
  523. _logger.LogWarning($"{heartRate.Serialno} 高频心率的数据不大于{stopHighFreqSampleCount}条,不进行高频数据的胎心计算");
  524. }
  525. // 删除高频状态的首条记录
  526. await _deviceCacheMgr.DelPregnancyHeartRateFreqStatusAsync(heartRate.Serialno);
  527. // (逐条计算)计算本次常规心率的胎心数据(高频结束后,实时处理)
  528. // await CalculateNormalFetalHeartRateAsync(heartRate, upperAlarmThreshold, lowerAlarmThreshold, intervalFHR, commonPHR);
  529. await CalculateNormalFetalHeartRateScheduleAsync(heartRate);
  530. }
  531. ///不满足持续10分钟highFreqSampleTimes或出现时间倒叙
  532. ///
  533. else
  534. {
  535. // 高频结束后与常规的心率时间倒叙
  536. if ((firstTwoPhr[1] - phrFreqstatus!.LastUpdate).TotalSeconds < 0)
  537. {
  538. _logger.LogInformation($"{heartRate.Serialno} 高频结束出现时间倒叙,计算当条心率创建之前的高频心率");
  539. #region 计算当条心率创建之前的高频心率
  540. // 取得高频之后的所有数据
  541. var phrFlashBack = daysPhr.Where(p => p.LastUpdate >= phrFreqstatus!.LastUpdate)
  542. .OrderByDescending(i => i.LastUpdate);
  543. // 取得高频数据
  544. var freqCollection = phrFlashBack.ToList();
  545. _logger.LogInformation($"{heartRate.Serialno} 高频数据个数{freqCollection.Count},触发高频开始时间{phrFreqstatus!.LastUpdate}");
  546. if (freqCollection.Count > stopHighFreqSampleCount)
  547. {
  548. // 计算高频产生的胎心
  549. var avgPhr = freqCollection
  550. .OrderByDescending(i => i.LastUpdate)
  551. .Take(stopHighFreqSampleCount) // 计算最后12条
  552. .Select(i => i.PregnancyHeartRate).Average();
  553. heartRate.HeartRate = (int)avgPhr;
  554. // 判断是否够highFreqSampleTimes,540s
  555. var lastPhr = freqCollection
  556. .OrderByDescending(i => i.LastUpdate)
  557. .Take(stopHighFreqSampleCount)
  558. .ToList();
  559. //// 最后一条高频心率
  560. //var lastFreqHr = lastPhr.First();
  561. //var ts = DateTimeUtil.GetTimeDifferenceInSeconds((DateTime)lastFreqHr.LastUpdate!, phrFreqstatus!.LastUpdate);
  562. //if (ts < highFreqSampleTimes)
  563. //{
  564. // /// 不够10分钟最近12个数据生成胎心值
  565. // //int selectedHrValue = SelectValueFromFreqHeartRate(heartRate, triggerHighFreqHigh, triggerHighFreqLow, lastPhr);
  566. // int selectedHrValue = SelectValueFromFreqHeartRate(heartRate.Serialno, triggerHighFreqHigh, triggerHighFreqLow, highFreqSampleTimes, phrFreqstatus, lastPhr);
  567. // heartRate.HeartRate = selectedHrValue;
  568. // _logger.LogInformation($"{heartRate.Serialno} 不够10分钟最近12个数据中取 {heartRate.HeartRate} 生成胎心值");
  569. // heartRate.LastUpdate = lastFreqHr.LastUpdate;
  570. //}
  571. //else
  572. //{
  573. // //int selectedHrValue = SelectValueFromFreqHeartRate(heartRate, triggerHighFreqHigh, triggerHighFreqLow, lastPhr);
  574. // int selectedHrValue = SelectValueFromFreqHeartRate(heartRate.Serialno, triggerHighFreqHigh, triggerHighFreqLow, highFreqSampleTimes, phrFreqstatus, lastPhr);
  575. // heartRate.HeartRate = selectedHrValue;
  576. // /// 超过10分钟最近12个数据生成胎心值
  577. // _logger.LogInformation($"{heartRate.Serialno} 超过10分钟最近12个数据中取 {heartRate.HeartRate} 生成胎心值");
  578. //}
  579. //_logger.LogInformation($"{heartRate.Serialno} 高频结束后的时间倒序的正常心率触发的高频心率处理");
  580. //await SaveAndPushFetalHeartRateEndFreqHeartRateAsync(heartRate, commonPHR, highFreqSampleTimes, upperAlarmThreshold, lowerAlarmThreshold, DateTimeUtil.ConvertToTimeStamp(phrFreqstatus!.LastUpdate).ToString(), phrFreqstatus!.LastUpdate, FreqStatsEnd);
  581. _logger.LogInformation($"{heartRate.Serialno} 高频结束后的时间倒序的正常心率触发的高频心率处理");
  582. await SaveAndPushFetalHeartRateEndFreqHeartRateAsync(heartRate, commonPHR, highFreqSampleTimes, upperAlarmThreshold, lowerAlarmThreshold, DateTimeUtil.ConvertToTimeStamp(phrFreqstatus!.LastUpdate).ToString(), phrFreqstatus!.LastUpdate, FreqStatsEnd, lastPhr, triggerHighFreqHigh, triggerHighFreqLow);
  583. _logger.LogInformation($"{heartRate.Serialno} 高频数据个数{freqCollection.Count},触发高频开始时间{phrFreqstatus!.LastUpdate},时间倒叙");
  584. }
  585. else
  586. {
  587. _logger.LogInformation($"{heartRate.Serialno} 时间倒叙触发计算高频心率的数据不足{stopHighFreqSampleCount}条,不进行胎心计算");
  588. }
  589. #endregion
  590. }
  591. else
  592. {
  593. _logger.LogInformation($"{heartRate.Serialno} 高频持续时间不足{highFreqSampleTimes},只持续{(firstTwoPhr[1] - phrFreqstatus!.LastUpdate).TotalSeconds} 秒");
  594. }
  595. // 删除高频状态的首条记录
  596. await _deviceCacheMgr.DelPregnancyHeartRateFreqStatusAsync(heartRate.Serialno);
  597. // (逐条计算)计算本次常规心率的胎心数据(高频结束后,实时处理)
  598. //await CalculateNormalFetalHeartRateAsync(heartRate, upperAlarmThreshold, lowerAlarmThreshold, intervalFHR, commonPHR);
  599. await CalculateNormalFetalHeartRateScheduleAsync(heartRate);
  600. }
  601. // 删除高频状态的首条记录
  602. await _deviceCacheMgr.DelPregnancyHeartRateFreqStatusAsync(heartRate.Serialno);
  603. _logger.LogInformation($"{heartRate.Serialno} 超时结束高频心率状态 timeDiffInSeconds {timeDiffInSeconds},highFreqSampleInterval:{highFreqSampleInterval},高频状态持续{(firstTwoPhr[1] - phrFreqstatus!.LastUpdate).TotalSeconds} 秒");
  604. //// 使用延后计算
  605. //var fhrScheduleKey = $"health_monitor/schedule_push/cal_fetal_heart_rate/imei/{heartRate.Serialno}";
  606. //var fhrScheduleTTL = 60;
  607. //await SetIntervalTriggerAsync(fhrScheduleKey, heartRate.Serialno, fhrScheduleTTL, heartRate);
  608. }
  609. // 常规心率(本次心率可能是高频心率的首条,所以要使用延后计算胎心率)
  610. else
  611. {
  612. // 本次心率可能是高频心率的首条,所以要使用本次常规心率延后计算胎心率
  613. //var fhrScheduleKey = $"health_monitor/schedule_push/cal_fetal_heart_rate/imei/{heartRate.Serialno}";
  614. //var fhrScheduleTTL = 30;
  615. //await SetIntervalTriggerAsync(fhrScheduleKey, heartRate.Serialno, fhrScheduleTTL, heartRate);
  616. //_logger.LogInformation($"{heartRate.Serialno} 延时50秒,判断当前数据是否为高频首条");
  617. // 本次心率可能是高频心率的首条,所以要使用本次常规心率延后计算胎心率 查询30秒后是否有高频缓存
  618. Thread thread = new(async () =>
  619. {
  620. try
  621. {
  622. #region 休眠highFreqSampleInterval2秒
  623. var startTime = DateTime.Now;
  624. //var highFreqSampleInterval2 = (int)watchConfig!["highFreqSampleInterval"]!+5;
  625. var highFreqSampleInterval2 = highFreqSampleInterval;
  626. var during = TimeSpan.FromSeconds(highFreqSampleInterval2);
  627. while (true)
  628. {
  629. if (DateTime.Now - startTime > during)
  630. {
  631. break;
  632. }
  633. await Task.Delay(TimeSpan.FromSeconds(1));
  634. }
  635. #endregion
  636. var phrFreqstatus = await _deviceCacheMgr.GetPregnancyHeartRateFreqStatusAsync(heartRate.Serialno);
  637. _logger.LogInformation($"phrFreqstatus==null:{phrFreqstatus == null}");
  638. _logger.LogInformation($"phrFreqstatus.LastUpdate < heartRate.LastUpdate:{phrFreqstatus?.LastUpdate < heartRate.LastUpdate}");
  639. if (phrFreqstatus == null || phrFreqstatus.LastUpdate < heartRate.LastUpdate)
  640. {
  641. // (逐条计算)常规心率
  642. //await CalculateNormalFetalHeartRateAsync(heartRate, upperAlarmThreshold, lowerAlarmThreshold, intervalFHR, commonPHR);
  643. //_logger.LogInformation($"{heartRate.Serialno} 计算常规心率");
  644. // 延时计算常规心率
  645. await CalculateNormalFetalHeartRateScheduleAsync(heartRate);
  646. }
  647. }
  648. catch (Exception ex)
  649. {
  650. _logger.LogError($"处理延时计算异常:{ex.Message}, {ex.StackTrace}");
  651. }
  652. });
  653. thread.Start();
  654. }
  655. }
  656. #endregion
  657. }
  658. else
  659. {
  660. _logger.LogInformation($"{heartRate.Serialno} 记录不足30条,建模中");
  661. }
  662. }
  663. else
  664. {
  665. }
  666. }
  667. catch (Exception ex)
  668. {
  669. _logger.LogError($"{heartRate.Serialno} 处理孕妇心率数据异常 \n{ex.Message}\n{ex.StackTrace}");
  670. }
  671. }
  672. /// <summary>
  673. /// 延时计算
  674. /// </summary>
  675. /// <param name="heartRate"></param>
  676. /// <returns></returns>
  677. public async Task CalculateNormalFetalHeartRateScheduleAsync(HisGpsHeartRate heartRate)
  678. {
  679. var fhrScheduleKey = $"health_monitor/schedule_push/cal_normalphr_fetal_heart_rate/imei/{heartRate.Serialno}";
  680. DateTime nowInterval = DateTime.Now;
  681. if (nowInterval.Second > 0)
  682. {
  683. nowInterval = nowInterval.AddMinutes(1);
  684. }
  685. // 需要减去的分钟
  686. int minutesToSubtract = nowInterval.Minute % INTERVAL_FHR;
  687. var nextRunTime=nowInterval
  688. .AddMinutes(-minutesToSubtract)
  689. .AddMinutes(INTERVAL_FHR);
  690. TimeSpan timeUntilNextRun = nextRunTime - nowInterval;
  691. var ttl = (long)timeUntilNextRun.TotalSeconds;
  692. await SetIntervalTriggerAsync(fhrScheduleKey, heartRate.Serialno, ttl, heartRate);
  693. _logger.LogInformation($"{heartRate.Serialno}--{heartRate.MessageId} 触发延时计算常规胎心");
  694. }
  695. /// <summary>
  696. /// 从高频心率数据中取心率值计算胎心值
  697. /// </summary>
  698. /// <param name="sn"></param>
  699. /// <param name="triggerHighFreqHigh"></param>
  700. /// <param name="triggerHighFreqLow"></param>
  701. /// <param name="highFreqSampleTimes"></param>
  702. /// <param name="firstFreqPhr"></param>
  703. /// <param name="lastPhr"></param>
  704. /// <returns></returns>
  705. private int SelectValueFromFreqHeartRate(string sn, int triggerHighFreqHigh, int triggerHighFreqLow,int highFreqSampleTimes, PregnancyHeartRateModel firstFreqPhr, List<PregnancyHeartRateModel> lastPhr)
  706. {
  707. // 连续12个心率的值的最小值
  708. var selectedHrValue = lastPhr.Select(i => i.PregnancyHeartRate).Min();
  709. _logger.LogInformation($"{sn} 最近12个数据的最小值 {selectedHrValue}");
  710. /// 高频状态下,取值心率低于高频最小值阀值,则需要取最大值进行计算。刚好跟大于高频最大值阀值刚好相反
  711. if (selectedHrValue < triggerHighFreqLow)
  712. {
  713. // 低于高频最小值阀值,则需要取最大值
  714. var selectedHrMax = lastPhr.Select(i => i.PregnancyHeartRate).Max();
  715. _logger.LogInformation($"{sn} 最近12个数据的最小值 {selectedHrValue},低于高频最小值阀值 {triggerHighFreqLow},取最近12个数据的最大值{selectedHrMax}作为心率");
  716. selectedHrValue = selectedHrMax;
  717. }
  718. if (selectedHrValue > triggerHighFreqHigh)
  719. {
  720. var selectedHrMin = lastPhr.Select(i => i.PregnancyHeartRate).Min();
  721. _logger.LogInformation($"{sn} 最近12个数据的最小值 {selectedHrValue},低于高频最大值阀值 {triggerHighFreqHigh},最近12个数据的最小值{selectedHrMin}作为心率");
  722. selectedHrValue = selectedHrMin;
  723. }
  724. #region 判断是否有低于低频阀值引起的高频
  725. /// 低频阀值引起的高频情况下,选择的心率值是正常心率的最小值,之后的区间选择的心率值是正常心率的最小值,不足10分钟没有正常值取最小阀值,超过10分钟没有正常值取最大阀值
  726. if (firstFreqPhr.PregnancyHeartRate < triggerHighFreqLow)
  727. {
  728. _logger.LogInformation($"{sn} 低频阀值引起的高频,低频心率引起值 {firstFreqPhr.PregnancyHeartRate}");
  729. var phr = lastPhr.Where(i => i.PregnancyHeartRate <= triggerHighFreqHigh && i.PregnancyHeartRate >= triggerHighFreqLow);
  730. var lastFreqHr = lastPhr.First();
  731. var ts = DateTimeUtil.GetTimeDifferenceInSeconds((DateTime)lastFreqHr.LastUpdate!, (DateTime) firstFreqPhr.LastUpdate!);
  732. if (!phr.Any())
  733. {
  734. if (ts<highFreqSampleTimes)
  735. {
  736. selectedHrValue = triggerHighFreqLow;
  737. _logger.LogInformation($"{sn} 高频不足10分钟没有正常值取最小阀值");
  738. }
  739. else
  740. {
  741. selectedHrValue = triggerHighFreqHigh;
  742. _logger.LogInformation($"{sn} 高频超过10分钟没有正常值取最大阀值");
  743. }
  744. }
  745. else
  746. {
  747. selectedHrValue = lastPhr.Where(i => i.PregnancyHeartRate <= triggerHighFreqHigh && i.PregnancyHeartRate >= triggerHighFreqLow)
  748. .ToList()
  749. .Select(i => i.PregnancyHeartRate).Min();
  750. }
  751. }
  752. #endregion
  753. return selectedHrValue;
  754. }
  755. /// <summary>
  756. /// 高频胎心处理
  757. /// 1. 高频数据触发连续12个值都是正常的的高频心率处理
  758. /// 2. 高频结束后的highFreqSampleTimes=0的高频心率处理
  759. /// 3. 高频结束后的在highFreqSampleTimes>0 正常心率触发的高频心率处理(常态)
  760. /// 4. 高频结束后的时间倒序的正常心率触发的高频心率处理
  761. /// 5. 高频结束后计算胎心数据,防止结束后与常规心理的胎心处理过长,定时器时长highFreqSampleInterval触发的高频心率处理
  762. /// 高频结束后超过9分钟且不在阈值内才告警
  763. /// </summary>
  764. /// <param name="heartRate"></param>
  765. /// <param name="commonPHR"></param>
  766. /// <param name="highFreqSampleTimes"></param>
  767. /// <param name="upperAlarmThreshold"></param>
  768. /// <param name="lowerAlarmThreshold"></param>
  769. /// <param name="sampleTime"></param>
  770. /// <param name="statStartTime"></param>
  771. /// <param name="statEndTime"></param>
  772. /// <returns></returns>
  773. private async Task SaveAndPushFetalHeartRateEndFreqHeartRateAsync(HisGpsHeartRate heartRate, PregnancyCommonHeartRateModel commonPHR, int highFreqSampleTimes, int upperAlarmThreshold, int lowerAlarmThreshold, string sampleTime, DateTime statStartTime, DateTime statEndTime)
  774. {
  775. var fetalHeartRate = await _serviceTDengine.GetFetalHeartRateAsync(heartRate.Serialno, (int)heartRate.HeartRate!);
  776. var phrFreqstatus = await _deviceCacheMgr.GetPregnancyHeartRateFreqStatusAsync(heartRate.Serialno);
  777. var isAbnormal = 0;
  778. #region 判断是否够highFreqSampleTimes,540s
  779. var ts = DateTimeUtil.GetTimeDifferenceInSeconds((DateTime)heartRate.LastUpdate!, phrFreqstatus!.LastUpdate);
  780. // 判断是否够highFreqSampleTimes,540s
  781. ///高频时长不足10分钟停止高频的胎心值生成说明:最近12个数据的最小值生成胎心值,
  782. ///对于小于高频下限阀值取高频下限阀值进行转换;
  783. ///对于高于高频上限阀值取高频上限阀值进行转换。并输出相关日志后续进行数据分析。
  784. if (ts < highFreqSampleTimes)
  785. {
  786. if (fetalHeartRate > upperAlarmThreshold)
  787. {
  788. _logger.LogWarning($"{heartRate.Serialno} 高频持续不足10分钟,计算胎心值 {fetalHeartRate} 高于高频警告上限阀值{upperAlarmThreshold},最后胎心值{upperAlarmThreshold},并且不告警");
  789. fetalHeartRate = upperAlarmThreshold;
  790. }
  791. else if (fetalHeartRate < lowerAlarmThreshold)
  792. {
  793. _logger.LogWarning($"{heartRate.Serialno} 高频持续不足10分钟,计算胎心值 {fetalHeartRate} 低于高频警告下限阀值 {lowerAlarmThreshold},最后胎心值{lowerAlarmThreshold},并且不告警");
  794. fetalHeartRate = lowerAlarmThreshold;
  795. }
  796. else
  797. {
  798. _logger.LogWarning($"{heartRate.Serialno} 高频持续不足10分钟,在高频警告下限阀值 {lowerAlarmThreshold} 和 高频警告上限阀值:{upperAlarmThreshold}之间,最后胎心值{fetalHeartRate},并且不告警");
  799. }
  800. }
  801. // 超过highFreqSampleTimes,540s
  802. else
  803. {
  804. if (fetalHeartRate > 220)
  805. {
  806. fetalHeartRate = 220;
  807. _logger.LogWarning($"{heartRate.Serialno} 大于220,按220输出,计算因子:孕妇心率 {heartRate.HeartRate},周期:{statStartTime.ToString("yyyy-MM-dd HH:mm:ss")}----{statEndTime.ToString("yyyy-MM-dd HH:mm:ss")}");
  808. }
  809. // 胎心的最小值调整为90,超过都按该值90
  810. if (fetalHeartRate < 90)
  811. {
  812. fetalHeartRate = 90;
  813. _logger.LogWarning($"{heartRate.Serialno} 小于90,按90输出,计算因子:孕妇心率 {heartRate.HeartRate}, 周期:{statStartTime.ToString("yyyy-MM-dd HH:mm:ss")}----{statEndTime.ToString("yyyy-MM-dd HH:mm:ss")}");
  814. }
  815. isAbnormal = fetalHeartRate > upperAlarmThreshold ? 1 : (fetalHeartRate < lowerAlarmThreshold ? 2 : 0);
  816. }
  817. #endregion
  818. _logger.LogInformation($"{heartRate.Serialno} 在 高频 状态,生成胎心值:{fetalHeartRate},统计周期:{statStartTime.ToString("yyyy-MM-dd HH:mm:ss")}----{statEndTime.ToString("yyyy-MM-dd HH:mm:ss")}");
  819. // 保存到 数据服务 MySQL 数据库
  820. HisGpsFetalHeartRate gpsFetalHeartRate = new()
  821. {
  822. FetalHeartRateId = Guid.NewGuid().ToString("D"),
  823. PersonId = commonPHR!.PersonId,
  824. Serialno = heartRate.Serialno,
  825. HeartRate = fetalHeartRate,
  826. SampleTime = sampleTime.Length > 10 ? sampleTime.Substring(0, 10) : sampleTime,
  827. IsAbnormal = isAbnormal,
  828. StatStartTime = statStartTime,
  829. StatEndTime = statEndTime,//commonPHR.StatEndTime,
  830. CreateTime = DateTime.Now,
  831. Method = 1,
  832. IsDisplay = 1,
  833. DeviceKey = commonPHR!.DeviceKey
  834. };
  835. await _hisFetalHeartApiClient.AddAsync(gpsFetalHeartRate).ConfigureAwait(false);
  836. #region 高频心率计算胎心数据到iot设备
  837. //if (phrFreqstatus != null && isAbnormal != 0)
  838. //{
  839. // await _serviceIotApi.SetFetalHeartRateConfig(heartRate.Serialno, fetalHeartRate, sampleTime, isAbnormal);
  840. //}
  841. // 高频有数据都推送到iot
  842. await _serviceIotApi.SetFetalHeartRateConfig(heartRate.Serialno, fetalHeartRate, sampleTime, isAbnormal);
  843. #endregion
  844. var device = await _deviceCacheMgr.GetDeviceBySerialNoAsync(heartRate.Serialno).ConfigureAwait(false);
  845. var fhrMsgId = $"{heartRate.Serialno}-{sampleTime}-{Guid.NewGuid().ToString("D")[^3..]}";
  846. var fhrMsgTime = DateTimeUtil.GetDateTimeFromUnixTimeMilliseconds(long.Parse(sampleTime.Length < 13 ? sampleTime.PadRight(13, '0') : sampleTime)).ToString("yyyy-MM-dd HH:mm:ss");
  847. // 胎心数据推送到第三方
  848. var topic = "topic.push.third";
  849. var fhrThridMsg = new
  850. {
  851. messageId = fhrMsgId,
  852. topic = topic,
  853. time = fhrMsgTime,
  854. data = new
  855. {
  856. imei = heartRate.Serialno,
  857. value = fetalHeartRate,
  858. isAbnormal,
  859. type = "fetalHeart"
  860. }
  861. };
  862. await _serviceMqProcess.ProcessIMEIEventMessageAsync(fhrMsgId, topic, 31, fhrThridMsg).ConfigureAwait(false);
  863. // 胎心数据推送到微信
  864. if (isAbnormal != 0)
  865. {
  866. topic = "topic.push.wx";
  867. var fhrMsg = new
  868. {
  869. messageId = fhrMsgId,
  870. topic = topic,
  871. time = fhrMsgTime,
  872. data = new
  873. {
  874. deviceId = device?.DeviceId,
  875. imei = heartRate.Serialno,
  876. alarmTypeId = 12,
  877. alarmDeviceName = heartRate.Serialno,
  878. alarmRemarks = JsonConvert.SerializeObject(new { fetalHeartValue = fetalHeartRate, isAbnormal = isAbnormal }),
  879. address = string.Empty,
  880. deviceKey = device?.DeviceId
  881. }
  882. };
  883. await _serviceMqProcess.ProcessIMEIEventMessageAsync(fhrMsgId, topic, fhrMsg).ConfigureAwait(false);
  884. }
  885. }
  886. /// <summary>
  887. /// 高频胎心处理
  888. /// 1. 高频数据触发连续12个值都是正常的的高频心率处理
  889. /// 2. 高频结束后的highFreqSampleTimes=0的高频心率处理
  890. /// 3. 高频结束后的在highFreqSampleTimes>0 正常心率触发的高频心率处理(常态)
  891. /// 4. 高频结束后的时间倒序的正常心率触发的高频心率处理
  892. /// 5. 高频结束后计算胎心数据,防止结束后与常规心理的胎心处理过长,定时器时长highFreqSampleInterval触发的高频心率处理
  893. /// 高频结束后超过9分钟且不在阈值内才告警
  894. /// </summary>
  895. /// <param name="heartRate"></param>
  896. /// <param name="commonPHR"></param>
  897. /// <param name="highFreqSampleTimes"></param>
  898. /// <param name="upperAlarmThreshold"></param>
  899. /// <param name="lowerAlarmThreshold"></param>
  900. /// <param name="sampleTime"></param>
  901. /// <param name="statStartTime"></param>
  902. /// <param name="statEndTime"></param>
  903. /// <returns></returns>
  904. private async Task SaveAndPushFetalHeartRateEndFreqHeartRateAsync(
  905. HisGpsHeartRate heartRate,
  906. PregnancyCommonHeartRateModel commonPHR,
  907. int highFreqSampleTimes,
  908. int upperAlarmThreshold,
  909. int lowerAlarmThreshold,
  910. string sampleTime,
  911. DateTime statStartTime,
  912. DateTime statEndTime,
  913. List<PregnancyHeartRateModel> lastPhr,
  914. int triggerHighFreqHigh,
  915. int triggerHighFreqLow)
  916. {
  917. try
  918. {
  919. #region 选择心率值计算胎心
  920. /// 12个心率数据判定上限处理模式还是下限处理模式:
  921. /// 1、12个数据的最大值 > 高频上限阀值,则按上限模式进行计算。取12个值的最小值心率值转换为胎心值。
  922. ///
  923. /// 2、12个数据的最小值 < 高频下限阀值,则按下限模式进行计算。取正常范围内的最小值心率值转换为胎心值,不产生告警。
  924. /// 如果正常范围内没有值,则取异常范围最大值心率值转换为胎心值,并产生胎心过缓告警。
  925. var phrFreqstatus = await _deviceCacheMgr.GetPregnancyHeartRateFreqStatusAsync(heartRate.Serialno);
  926. var isAbnormal = 0;
  927. //取正常范围内的最小值心率值转换为胎心值,不产生告警。
  928. var lastNormalPhr = lastPhr.Where(i => i.PregnancyHeartRate > triggerHighFreqLow && i.PregnancyHeartRate < triggerHighFreqHigh);
  929. int selectedHrValue = (int)heartRate.HeartRate!;
  930. if (lastPhr.Select(i => i.PregnancyHeartRate).Max() > triggerHighFreqHigh)
  931. {
  932. // 取12个值的最小值心率值转换为胎心值。
  933. selectedHrValue = lastPhr.Select(i => i.PregnancyHeartRate).Min();
  934. _logger.LogInformation($"{heartRate.Serialno} 取12个值的最小值心率值转换为胎心值");
  935. }
  936. if (lastPhr.Select(i => i.PregnancyHeartRate).Min() < triggerHighFreqLow)
  937. {
  938. // 有正常值
  939. if (lastNormalPhr.Any())
  940. {
  941. // 12个数据的最小值 < 高频下限阀值,则按下限模式进行计算。取正常范围内的最小值心率值转换为胎心值,不产生告警
  942. selectedHrValue = lastNormalPhr
  943. .Select(i => i.PregnancyHeartRate)
  944. .Min();
  945. _logger.LogInformation($"{heartRate.Serialno} 有正常值,取正常范围内的最小值心率值转换为胎心值,不产生告警");
  946. }
  947. // 无正常值
  948. if (!lastNormalPhr.Any())
  949. {
  950. selectedHrValue = lastPhr
  951. .Select(i => i.PregnancyHeartRate)
  952. .Max();
  953. _logger.LogInformation($"{heartRate.Serialno} 无正常值,则取异常范围最大值心率值转换为胎心值,并产生胎心过缓告警");
  954. }
  955. }
  956. _logger.LogInformation($"{heartRate.Serialno} 高频选择心率值:{selectedHrValue}");
  957. var fetalHeartRate = await _serviceTDengine.GetFetalHeartRateAsync(heartRate.Serialno, selectedHrValue);
  958. _logger.LogInformation($"{heartRate.Serialno} 高频状态计算,由高频心率 {selectedHrValue} 根据胎心算法转换的胎心值 {fetalHeartRate}");
  959. #endregion
  960. #region 判断是否够highFreqSampleTimes,540s
  961. var ts = DateTimeUtil.GetTimeDifferenceInSeconds((DateTime)heartRate.LastUpdate!, phrFreqstatus!.LastUpdate);
  962. // 判断是否够highFreqSampleTimes,540s
  963. ///高频时长不足10分钟停止高频的胎心值生成说明:最近12个数据的最小值生成胎心值,
  964. ///对于小于高频下限阀值取高频下限阀值进行转换;
  965. ///对于高于高频上限阀值取高频上限阀值进行转换。并输出相关日志后续进行数据分析。
  966. if (ts < highFreqSampleTimes)
  967. {
  968. if (fetalHeartRate > upperAlarmThreshold)
  969. {
  970. _logger.LogWarning($"{heartRate.Serialno} 高频持续不足10分钟,计算胎心值 {fetalHeartRate} 高于高频警告上限阀值{upperAlarmThreshold},最后胎心值{upperAlarmThreshold},并且不告警");
  971. fetalHeartRate = upperAlarmThreshold;
  972. }
  973. else if (fetalHeartRate < lowerAlarmThreshold)
  974. {
  975. _logger.LogWarning($"{heartRate.Serialno} 高频持续不足10分钟,计算胎心值 {fetalHeartRate} 低于高频警告下限阀值 {lowerAlarmThreshold},最后胎心值{lowerAlarmThreshold},并且不告警");
  976. fetalHeartRate = lowerAlarmThreshold;
  977. }
  978. else
  979. {
  980. _logger.LogWarning($"{heartRate.Serialno} 高频持续不足10分钟,在高频警告下限阀值 {lowerAlarmThreshold} 和 高频警告上限阀值:{upperAlarmThreshold}之间,最后胎心值{fetalHeartRate},并且不告警");
  981. }
  982. isAbnormal = 0;
  983. }
  984. // 超过highFreqSampleTimes,540s
  985. else
  986. {
  987. if (fetalHeartRate > 220)
  988. {
  989. fetalHeartRate = 220;
  990. _logger.LogWarning($"{heartRate.Serialno} 大于220,按220输出,计算因子:孕妇心率 {heartRate.HeartRate},周期:{statStartTime.ToString("yyyy-MM-dd HH:mm:ss")}----{statEndTime.ToString("yyyy-MM-dd HH:mm:ss")}");
  991. }
  992. // 胎心的最小值调整为90,超过都按该值90
  993. if (fetalHeartRate < 90)
  994. {
  995. fetalHeartRate = 90;
  996. _logger.LogWarning($"{heartRate.Serialno} 小于90,按90输出,计算因子:孕妇心率 {heartRate.HeartRate}, 周期:{statStartTime.ToString("yyyy-MM-dd HH:mm:ss")}----{statEndTime.ToString("yyyy-MM-dd HH:mm:ss")}");
  997. }
  998. isAbnormal = fetalHeartRate > upperAlarmThreshold ? 1 : (fetalHeartRate < lowerAlarmThreshold ? 2 : 0);
  999. if (!lastNormalPhr.Any())
  1000. {
  1001. // 偏低(过缓)
  1002. isAbnormal = 2;
  1003. }
  1004. }
  1005. #endregion
  1006. _logger.LogInformation($"{heartRate.Serialno} 在 高频 状态,生成胎心值:{fetalHeartRate},统计周期:{statStartTime.ToString("yyyy-MM-dd HH:mm:ss")}----{statEndTime.ToString("yyyy-MM-dd HH:mm:ss")}");
  1007. // 保存到 数据服务 MySQL 数据库
  1008. HisGpsFetalHeartRate gpsFetalHeartRate = new()
  1009. {
  1010. FetalHeartRateId = Guid.NewGuid().ToString("D"),
  1011. PersonId = commonPHR!.PersonId,
  1012. Serialno = heartRate.Serialno,
  1013. HeartRate = fetalHeartRate,
  1014. SampleTime = sampleTime.Length > 10 ? sampleTime.Substring(0, 10) : sampleTime,
  1015. IsAbnormal = isAbnormal,
  1016. StatStartTime = statStartTime,
  1017. StatEndTime = statEndTime,//commonPHR.StatEndTime,
  1018. CreateTime = DateTime.Now,
  1019. Method = 1,
  1020. IsDisplay = 1,
  1021. DeviceKey = commonPHR!.DeviceKey
  1022. };
  1023. await _hisFetalHeartApiClient.AddAsync(gpsFetalHeartRate).ConfigureAwait(false);
  1024. #region 高频心率计算胎心数据到iot设备
  1025. //if (phrFreqstatus != null && isAbnormal != 0)
  1026. //{
  1027. // await _serviceIotApi.SetFetalHeartRateConfig(heartRate.Serialno, fetalHeartRate, sampleTime, isAbnormal);
  1028. //}
  1029. // 高频有数据都推送到iot
  1030. await _serviceIotApi.SetFetalHeartRateConfig(heartRate.Serialno, fetalHeartRate, sampleTime, isAbnormal);
  1031. #endregion
  1032. var device = await _deviceCacheMgr.GetDeviceBySerialNoAsync(heartRate.Serialno).ConfigureAwait(false);
  1033. var fhrMsgId = $"{heartRate.Serialno}-{sampleTime}-{Guid.NewGuid().ToString("D")[^3..]}";
  1034. var fhrMsgTime = DateTimeUtil.GetDateTimeFromUnixTimeMilliseconds(long.Parse(sampleTime.Length < 13 ? sampleTime.PadRight(13, '0') : sampleTime)).ToString("yyyy-MM-dd HH:mm:ss");
  1035. // 胎心数据推送到第三方
  1036. var topic = "topic.push.third";
  1037. var fhrThridMsg = new
  1038. {
  1039. messageId = fhrMsgId,
  1040. topic = topic,
  1041. time = fhrMsgTime,
  1042. data = new
  1043. {
  1044. imei = heartRate.Serialno,
  1045. value = fetalHeartRate,
  1046. isAbnormal,
  1047. type = "fetalHeart"
  1048. }
  1049. };
  1050. await _serviceMqProcess.ProcessIMEIEventMessageAsync(fhrMsgId, topic, 31, fhrThridMsg).ConfigureAwait(false);
  1051. // 胎心数据推送到微信
  1052. if (isAbnormal != 0)
  1053. {
  1054. topic = "topic.push.wx";
  1055. var fhrMsg = new
  1056. {
  1057. messageId = fhrMsgId,
  1058. topic = topic,
  1059. time = fhrMsgTime,
  1060. data = new
  1061. {
  1062. deviceId = device?.DeviceId,
  1063. imei = heartRate.Serialno,
  1064. alarmTypeId = 12,
  1065. alarmDeviceName = heartRate.Serialno,
  1066. alarmRemarks = JsonConvert.SerializeObject(new { fetalHeartValue = fetalHeartRate, isAbnormal = isAbnormal }),
  1067. address = string.Empty,
  1068. deviceKey = device?.DeviceId
  1069. }
  1070. };
  1071. await _serviceMqProcess.ProcessIMEIEventMessageAsync(fhrMsgId, topic, fhrMsg).ConfigureAwait(false);
  1072. }
  1073. }
  1074. catch (Exception ex)
  1075. {
  1076. _logger.LogError($"{heartRate.Serialno} 计算高频心率出错{ex.Message}\n{ex.StackTrace}");
  1077. }
  1078. }
  1079. private async Task SetIntervalTriggerAsync(string key, string imei, long interval, HisGpsHeartRate heartRate)
  1080. {
  1081. // var key = $"health_monitor/schedule_push/{type}/imei/{imei}";
  1082. var schedulePush = await _serviceEtcd.GetValAsync(key).ConfigureAwait(false);
  1083. if (string.IsNullOrWhiteSpace(schedulePush))
  1084. {
  1085. var now = DateTime.Now;
  1086. var timeNextRun = now.Add(TimeSpan.FromSeconds(interval));
  1087. var data = new
  1088. {
  1089. imei,
  1090. create_time = now.ToString("yyyy-MM-dd HH:mm:ss"),
  1091. ttl = interval,
  1092. next_run_time = timeNextRun.ToString("yyyy-MM-dd HH:mm:ss"),
  1093. trigger = heartRate,
  1094. };
  1095. var result = JsonConvert.SerializeObject(data);
  1096. await _serviceEtcd.PutValAsync(key, result, interval, false).ConfigureAwait(false);
  1097. }
  1098. }
  1099. /// <summary>
  1100. /// 高频延时计算触发器
  1101. /// </summary>
  1102. /// <param name="key">键</param>
  1103. /// <param name="imei">IMEI</param>
  1104. /// <param name="interval">高频采集间隔</param>
  1105. /// <param name="heartRate">首条高频心率</param>
  1106. /// <returns></returns>
  1107. private async Task SetFreqHeartRateTriggerAsync(string key, string imei, long interval, HisGpsHeartRate? heartRate=null)
  1108. {
  1109. var schedulePush = await _serviceEtcd.GetValAsync(key).ConfigureAwait(false);
  1110. if (string.IsNullOrWhiteSpace(schedulePush))
  1111. {
  1112. var now = DateTime.Now;
  1113. var data = new
  1114. {
  1115. imei,
  1116. create_time = now.ToString("yyyy-MM-dd HH:mm:ss"),
  1117. lease = interval,
  1118. trigger = heartRate,
  1119. };
  1120. var result = JsonConvert.SerializeObject(data);
  1121. await _serviceEtcd.PutValAsync(key, result, interval, false).ConfigureAwait(false);
  1122. _logger.LogInformation($"{imei} 心率高频状态创建首条高频心率的触发记录,创建高频延时计算触发器");
  1123. }
  1124. else
  1125. {
  1126. // 不断修改时长,直到最后的一条高频胎心
  1127. await _serviceEtcd.PutValAsync(key, schedulePush, interval, false).ConfigureAwait(false);
  1128. _logger.LogInformation($"{imei} 心率高频状态续租");
  1129. }
  1130. }
  1131. /// <summary>
  1132. /// 去除高频数据
  1133. /// </summary>
  1134. /// <param name="phr"></param>
  1135. /// <param name="highFreqSampleInterva"></param>
  1136. /// <returns></returns>
  1137. private static List<PregnancyHeartRateModel> GetNonFreqPregnancyHeartRate(List<PregnancyHeartRateModel> phr,int highFreqSampleInterval)
  1138. {
  1139. //phr = phr.OrderByDescending(i => i.LastUpdate).ToList();
  1140. //var result = new List<PregnancyHeartRateModel>();
  1141. //PregnancyHeartRateModel? previousItem = null;
  1142. //foreach (var item in phr)
  1143. //{
  1144. // if (previousItem != null)
  1145. // {
  1146. // var timeNextDiff =(previousItem!.LastUpdate - item.LastUpdate).TotalSeconds;
  1147. // if (timeNextDiff > highFreqSampleInterval)
  1148. // {
  1149. // result.Add(previousItem);
  1150. // }
  1151. // }
  1152. // previousItem = item;
  1153. //}
  1154. //// 添加上一个
  1155. //if (previousItem != null)
  1156. //{
  1157. // result.Add(previousItem);
  1158. //}
  1159. //return result;
  1160. #region 反向
  1161. var phr1 = phr.OrderByDescending(i => i.LastUpdate).ToList();
  1162. var result = new List<PregnancyHeartRateModel>();
  1163. PregnancyHeartRateModel? previousItem1 = null;
  1164. foreach (var item in phr1)
  1165. {
  1166. if (previousItem1 != null)
  1167. {
  1168. var timeNextDiff = (previousItem1!.LastUpdate - item.LastUpdate).TotalSeconds;
  1169. if (timeNextDiff > highFreqSampleInterval)
  1170. {
  1171. result.Add(previousItem1);
  1172. }
  1173. }
  1174. previousItem1 = item;
  1175. }
  1176. // 添加上一个
  1177. if (previousItem1 != null)
  1178. {
  1179. result.Add(previousItem1);
  1180. }
  1181. #endregion
  1182. #region 正向
  1183. var phr2 = phr.OrderByDescending(i => i.LastUpdate).ToList(); ;
  1184. var freqCollection = new List<PregnancyHeartRateModel>();
  1185. PregnancyHeartRateModel? previousItem = null;
  1186. foreach (var item in phr2)
  1187. {
  1188. if (previousItem != null)
  1189. {
  1190. var timeNextDiff = (previousItem!.LastUpdate - item.LastUpdate).TotalSeconds;
  1191. if (timeNextDiff <= highFreqSampleInterval)
  1192. {
  1193. freqCollection.Add(item);
  1194. }
  1195. }
  1196. previousItem = item;
  1197. }
  1198. //去除高频
  1199. foreach (var item in freqCollection)
  1200. {
  1201. phr2.Remove(item);
  1202. }
  1203. #endregion
  1204. // 交集
  1205. var commonElements = phr2.Intersect(result).ToList();
  1206. return commonElements;
  1207. }
  1208. /// <summary>
  1209. /// 获取高频数据
  1210. /// </summary>
  1211. /// <param name="phr"></param>
  1212. /// <param name="highFreqSampleInterval"></param>
  1213. /// <returns></returns>
  1214. private static List<PregnancyHeartRateModel> GetFreqPregnancyHeartRate(List<PregnancyHeartRateModel> phr, int highFreqSampleInterval)
  1215. {
  1216. phr = phr.OrderByDescending(i => i.LastUpdate).ToList();
  1217. var freqCollection = new List<PregnancyHeartRateModel>();
  1218. PregnancyHeartRateModel? previousItem = null;
  1219. foreach (var item in phr)
  1220. {
  1221. if (previousItem != null)
  1222. {
  1223. var timeNextDiff = (previousItem.LastUpdate - item.LastUpdate).TotalSeconds;
  1224. if (timeNextDiff <= highFreqSampleInterval)
  1225. {
  1226. freqCollection.Add(previousItem);
  1227. }
  1228. }
  1229. previousItem = item;
  1230. }
  1231. // 检查最后一条是否高频
  1232. if (previousItem != null && (phr.Last().LastUpdate - previousItem.LastUpdate).TotalSeconds <= highFreqSampleInterval)
  1233. {
  1234. freqCollection.Add(previousItem);
  1235. }
  1236. return freqCollection;
  1237. }
  1238. }
  1239. }