您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

1543 行
90KB

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