No puede seleccionar más de 25 temas Los temas deben comenzar con una letra o número, pueden incluir guiones ('-') y pueden tener hasta 35 caracteres de largo.

PregnancyHeartRateResolver.cs 56KB

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