選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

PregnancyHeartRateResolver.cs 84KB

7ヶ月前
7ヶ月前
8ヶ月前
8ヶ月前
7ヶ月前
8ヶ月前
8ヶ月前
7ヶ月前
7ヶ月前
8ヶ月前
7ヶ月前
8ヶ月前
7ヶ月前
8ヶ月前
7ヶ月前
8ヶ月前
8ヶ月前
7ヶ月前
7ヶ月前
7ヶ月前
8ヶ月前
7ヶ月前
7ヶ月前
8ヶ月前
7ヶ月前
8ヶ月前
8ヶ月前
7ヶ月前
7ヶ月前
7ヶ月前
7ヶ月前
7ヶ月前
4ヶ月前
5ヶ月前
7ヶ月前
7ヶ月前
7ヶ月前
7ヶ月前
4ヶ月前
7ヶ月前
7ヶ月前
7ヶ月前
7ヶ月前
7ヶ月前
8ヶ月前
4ヶ月前
7ヶ月前
7ヶ月前
7ヶ月前
8ヶ月前
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433
  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. _logger.LogInformation($"{heartRate.Serialno} 高频数据触发连续12个值都是正常的的高频心率处理");
  394. await SaveAndPushFetalHeartRateEndFreqHeartRateAsync(heartRate, commonPHR, highFreqSampleTimes, upperAlarmThreshold, lowerAlarmThreshold, DateTimeUtil.ConvertToTimeStamp(phrFreqstatus!.LastUpdate).ToString(), phrFreqstatus!.LastUpdate, FreqStatsEnd, lastPhr, triggerHighFreqHigh, triggerHighFreqLow);
  395. // 删除高频状态的首条记录
  396. await _deviceCacheMgr.DelPregnancyHeartRateFreqStatusAsync(heartRate.Serialno);
  397. // 设置15分的SaveAndPushFetalHeartRate业务间隔
  398. await _deviceCacheMgr.SetBizIntervalAsync(heartRate.Serialno, "SaveAndPushFetalHeartRate");
  399. _logger.LogInformation($"{heartRate.Serialno} 连续 {stopHighFreqSampleCount} 次采样心率正常,结束高频心率状态, timeDiffInSeconds {timeDiffInSeconds},highFreqSampleInterval:{highFreqSampleInterval},高频状态持续{((DateTime)heartRate.LastUpdate - phrFreqstatus!.LastUpdate).TotalSeconds} 秒,高频心率平均值:{(int)avgPhr}");
  400. }
  401. else
  402. {
  403. _logger.LogWarning($"{heartRate.Serialno} 连续 {stopHighFreqSampleCount} 次采样心率正常,设备端应该结束高频状态,但设备端没有结束高频,平台高频15分钟内已经下发过指令");
  404. }
  405. }
  406. else
  407. {
  408. _logger.LogInformation($"{heartRate.Serialno} 处于高频状态...");
  409. }
  410. #endregion
  411. }
  412. // 高频心率结束或常规心率
  413. else
  414. {
  415. var phrFreqstatus = await _deviceCacheMgr.GetPregnancyHeartRateFreqStatusAsync(heartRate.Serialno);
  416. // 高频心率结束
  417. if (phrFreqstatus != null)
  418. {
  419. /// 在highFreqSampleTimes=0一直异常(大于等于triggerHighFreqLow,少于等于triggerHighFreqHig),
  420. /// 取所有值的平均值,推送胎心数据到api/v1/open/OpenIot/SetFetalHeartRateConfig
  421. if (highFreqSampleTimes == 0)
  422. {
  423. var lastPhr = phr.OrderByDescending(i => i.LastUpdate)
  424. .Where(i => i.LastUpdate >= phrFreqstatus?.LastUpdate)
  425. .Skip(1) // 去除首条
  426. .Where(i => i.PregnancyHeartRate < triggerHighFreqLow || i.PregnancyHeartRate > triggerHighFreqHigh)
  427. .ToList();
  428. //.Select(i => i.PregnancyHeartRate);
  429. //.Average();
  430. var avgPhr = lastPhr.Select(i => i.PregnancyHeartRate).Average();
  431. // 推送胎心数据到 api/v1/open/OpenIot/SetFetalHeartRateConfig
  432. // 计算一般心率得到胎心系数
  433. // 高频数据不建模
  434. FreqStatsEnd = firstTwoPhr[1];
  435. _logger.LogInformation($"{heartRate.Serialno} 高频状态已经持续{(FreqStatsEnd - phrFreqstatus!.LastUpdate).TotalSeconds} 秒,highFreqSampleTimes={highFreqSampleTimes}秒,即将结束高频状态,高频心率平均值:{(int)avgPhr},将下发指令");
  436. //await SaveAndPushFreqFetalHeartRateAsync(heartRate, commonPHR, upperAlarmThreshold, lowerAlarmThreshold, avgPhr, DateTimeUtil.ConvertToTimeStamp(phrFreqstatus!.LastUpdate).ToString());
  437. //await SaveAndPushFetalHeartRateAsync(heartRate, commonPHR, upperAlarmThreshold, lowerAlarmThreshold, avgPhr, DateTimeUtil.ConvertToTimeStamp(phrFreqstatus!.LastUpdate).ToString(), phrFreqstatus!.LastUpdate, FreqStatsEnd);
  438. //heartRate.HeartRate = (int)avgPhr;
  439. //// 最后一条高频心率
  440. //var lastFreqHr = lastPhr.First();
  441. //var ts = DateTimeUtil.GetTimeDifferenceInSeconds((DateTime)lastFreqHr.LastUpdate!, phrFreqstatus!.LastUpdate);
  442. //// 判断是否够highFreqSampleTimes,540s
  443. //if (ts < highFreqSampleTimes)
  444. //{
  445. // /// 不够10分钟最近12个数据生成胎心值
  446. // //int selectedHrValue = SelectValueFromFreqHeartRate(heartRate, triggerHighFreqHigh, triggerHighFreqLow, lastPhr);
  447. // int selectedHrValue = SelectValueFromFreqHeartRate(heartRate.Serialno, triggerHighFreqHigh, triggerHighFreqLow, highFreqSampleTimes, phrFreqstatus, lastPhr);
  448. // heartRate.HeartRate = selectedHrValue;
  449. // _logger.LogInformation($"{heartRate.Serialno} 不够10分钟最近12个数据中取 {heartRate.HeartRate} 生成胎心值");
  450. // heartRate.LastUpdate = lastFreqHr.LastUpdate;
  451. //}
  452. //else
  453. //{
  454. // //int selectedHrValue = SelectValueFromFreqHeartRate(heartRate, triggerHighFreqHigh, triggerHighFreqLow, lastPhr);
  455. // int selectedHrValue = SelectValueFromFreqHeartRate(heartRate.Serialno, triggerHighFreqHigh, triggerHighFreqLow, highFreqSampleTimes, phrFreqstatus, lastPhr);
  456. // heartRate.HeartRate = selectedHrValue;
  457. // /// 超过10分钟最近12个数据生成胎心值
  458. // _logger.LogInformation($"{heartRate.Serialno} 超过10分钟最近12个数据中取 {heartRate.HeartRate} 生成胎心值");
  459. //}
  460. //_logger.LogInformation($"{heartRate.Serialno} 高频结束后的highFreqSampleTimes=0的高频心率处理");
  461. //await SaveAndPushFetalHeartRateEndFreqHeartRateAsync(heartRate, commonPHR, highFreqSampleTimes, upperAlarmThreshold, lowerAlarmThreshold, DateTimeUtil.ConvertToTimeStamp(phrFreqstatus!.LastUpdate).ToString(), phrFreqstatus!.LastUpdate, FreqStatsEnd);
  462. _logger.LogInformation($"{heartRate.Serialno} 高频结束后的highFreqSampleTimes=0的高频心率处理");
  463. await SaveAndPushFetalHeartRateEndFreqHeartRateAsync(heartRate, commonPHR, highFreqSampleTimes, upperAlarmThreshold, lowerAlarmThreshold, DateTimeUtil.ConvertToTimeStamp(phrFreqstatus!.LastUpdate).ToString(), phrFreqstatus!.LastUpdate, FreqStatsEnd, lastPhr, triggerHighFreqHigh, triggerHighFreqLow);
  464. }
  465. /// 在highFreqSampleTimes>0一直异常(大于等于triggerHighFreqLow,少于等于triggerHighFreqHig),
  466. /// 取所有值的平均值,推送胎心数据到api/v1/open/OpenIot/SetFetalHeartRateConfig
  467. if (highFreqSampleTimes > 0 && heartRate.LastUpdate >= (phrFreqstatus?.LastUpdate.AddSeconds(highFreqSampleTimes)))
  468. {
  469. // 获取高频心率数据个数
  470. var filterPhr = phr
  471. .Where(i => i.LastUpdate >= phrFreqstatus?.LastUpdate)
  472. .Skip(1)
  473. .ToList();
  474. _logger.LogInformation($"{heartRate.Serialno} 高频周期 {phrFreqstatus?.LastUpdate.ToString("yyyy-MM-dd HH:mm:ss")}--{firstTwoPhr[1].ToString("yyyy-MM-dd HH:mm:ss")} 产生高频心率数量 {filterPhr.Count} 条");
  475. // 高频心率数据大于stopHighFreqSampleCount/12个才计算胎心数据
  476. //
  477. if (filterPhr.Count > stopHighFreqSampleCount)
  478. {
  479. FreqStatsEnd = firstTwoPhr[1];
  480. var avgPhr = filterPhr
  481. .OrderByDescending(i => i.LastUpdate)
  482. .Take(stopHighFreqSampleCount) // 计算最后12条
  483. .Select(i => i.PregnancyHeartRate).Average();
  484. //_logger.LogInformation($"{heartRate.Serialno} 高频状态已经持续{(FreqStatsEnd - phrFreqstatus!.LastUpdate).TotalSeconds} 秒,约定的 {highFreqSampleTimes} 秒,即将结束高频状态,高频心率平均值:{(int)avgPhr},将下发指令");
  485. //计算高频
  486. //await SaveAndPushFetalHeartRateAsync(heartRate, commonPHR, upperAlarmThreshold, lowerAlarmThreshold, avgPhr, DateTimeUtil.ConvertToTimeStamp(phrFreqstatus!.LastUpdate).ToString(), phrFreqstatus!.LastUpdate, FreqStatsEnd);
  487. heartRate.HeartRate = (int)avgPhr;
  488. // 判断是否够highFreqSampleTimes,540s
  489. var lastPhr = filterPhr
  490. .OrderByDescending(i => i.LastUpdate)
  491. .Take(stopHighFreqSampleCount).ToList();
  492. //// 最后一条高频心率
  493. //var lastFreqHr = lastPhr.First();
  494. //var ts = DateTimeUtil.GetTimeDifferenceInSeconds((DateTime)lastFreqHr.LastUpdate!, phrFreqstatus!.LastUpdate);
  495. //if (ts < highFreqSampleTimes)
  496. //{
  497. // /// 不够10分钟最近12个数据的成胎心值
  498. // //int selectedHrValue = SelectValueFromFreqHeartRate(heartRate, triggerHighFreqHigh, triggerHighFreqLow, lastPhr);
  499. // int selectedHrValue = SelectValueFromFreqHeartRate(heartRate.Serialno, triggerHighFreqHigh, triggerHighFreqLow, highFreqSampleTimes, phrFreqstatus, lastPhr);
  500. // heartRate.HeartRate = selectedHrValue;
  501. // _logger.LogInformation($"{heartRate.Serialno} 不够10分钟最近12个数据中取 {heartRate.HeartRate} 生成胎心值");
  502. // heartRate.LastUpdate = lastFreqHr.LastUpdate;
  503. //}
  504. //else
  505. //{
  506. // //int selectedHrValue = SelectValueFromFreqHeartRate(heartRate, triggerHighFreqHigh, triggerHighFreqLow, lastPhr);
  507. // int selectedHrValue = SelectValueFromFreqHeartRate(heartRate.Serialno, triggerHighFreqHigh, triggerHighFreqLow, highFreqSampleTimes, phrFreqstatus, lastPhr);
  508. // heartRate.HeartRate = selectedHrValue;
  509. // /// 超过10分钟最近12个数据的生成胎心值
  510. // _logger.LogInformation($"{heartRate.Serialno} 超过10分钟最近12个数据的中取 {heartRate.HeartRate} 生成胎心值");
  511. //}
  512. //_logger.LogInformation($"{heartRate.Serialno} 高频结束后的在highFreqSampleTimes>0 正常心率(通常情况)触发的高频心率处理");
  513. //await SaveAndPushFetalHeartRateEndFreqHeartRateAsync(heartRate, commonPHR, highFreqSampleTimes, upperAlarmThreshold, lowerAlarmThreshold, DateTimeUtil.ConvertToTimeStamp(phrFreqstatus!.LastUpdate).ToString(), phrFreqstatus!.LastUpdate, FreqStatsEnd);
  514. _logger.LogInformation($"{heartRate.Serialno} 高频结束后的在highFreqSampleTimes>0 正常心率(通常情况)触发的高频心率处理");
  515. await SaveAndPushFetalHeartRateEndFreqHeartRateAsync(heartRate, commonPHR, highFreqSampleTimes, upperAlarmThreshold, lowerAlarmThreshold, DateTimeUtil.ConvertToTimeStamp(phrFreqstatus!.LastUpdate).ToString(), phrFreqstatus!.LastUpdate, FreqStatsEnd, lastPhr, triggerHighFreqHigh, triggerHighFreqLow);
  516. }
  517. else
  518. {
  519. _logger.LogWarning($"{heartRate.Serialno} 高频心率的数据不大于{stopHighFreqSampleCount}条,不进行高频数据的胎心计算");
  520. }
  521. // 删除高频状态的首条记录
  522. await _deviceCacheMgr.DelPregnancyHeartRateFreqStatusAsync(heartRate.Serialno);
  523. // (逐条计算)计算本次常规心率的胎心数据(高频结束后,实时处理)
  524. // await CalculateNormalFetalHeartRateAsync(heartRate, upperAlarmThreshold, lowerAlarmThreshold, intervalFHR, commonPHR);
  525. await CalculateNormalFetalHeartRateScheduleAsync(heartRate);
  526. }
  527. ///不满足持续10分钟highFreqSampleTimes或出现时间倒叙
  528. ///
  529. else
  530. {
  531. // 高频结束后与常规的心率时间倒叙
  532. if ((firstTwoPhr[1] - phrFreqstatus!.LastUpdate).TotalSeconds < 0)
  533. {
  534. _logger.LogInformation($"{heartRate.Serialno} 高频结束出现时间倒叙,计算当条心率创建之前的高频心率");
  535. #region 计算当条心率创建之前的高频心率
  536. // 取得高频之后的所有数据
  537. var phrFlashBack = daysPhr.Where(p => p.LastUpdate >= phrFreqstatus!.LastUpdate)
  538. .OrderByDescending(i => i.LastUpdate);
  539. // 取得高频数据
  540. var freqCollection = phrFlashBack.ToList();
  541. _logger.LogInformation($"{heartRate.Serialno} 高频数据个数{freqCollection.Count},触发高频开始时间{phrFreqstatus!.LastUpdate}");
  542. if (freqCollection.Count > stopHighFreqSampleCount)
  543. {
  544. // 计算高频产生的胎心
  545. var avgPhr = freqCollection
  546. .OrderByDescending(i => i.LastUpdate)
  547. .Take(stopHighFreqSampleCount) // 计算最后12条
  548. .Select(i => i.PregnancyHeartRate).Average();
  549. heartRate.HeartRate = (int)avgPhr;
  550. // 判断是否够highFreqSampleTimes,540s
  551. var lastPhr = freqCollection
  552. .OrderByDescending(i => i.LastUpdate)
  553. .Take(stopHighFreqSampleCount)
  554. .ToList();
  555. //// 最后一条高频心率
  556. //var lastFreqHr = lastPhr.First();
  557. //var ts = DateTimeUtil.GetTimeDifferenceInSeconds((DateTime)lastFreqHr.LastUpdate!, phrFreqstatus!.LastUpdate);
  558. //if (ts < highFreqSampleTimes)
  559. //{
  560. // /// 不够10分钟最近12个数据生成胎心值
  561. // //int selectedHrValue = SelectValueFromFreqHeartRate(heartRate, triggerHighFreqHigh, triggerHighFreqLow, lastPhr);
  562. // int selectedHrValue = SelectValueFromFreqHeartRate(heartRate.Serialno, triggerHighFreqHigh, triggerHighFreqLow, highFreqSampleTimes, phrFreqstatus, lastPhr);
  563. // heartRate.HeartRate = selectedHrValue;
  564. // _logger.LogInformation($"{heartRate.Serialno} 不够10分钟最近12个数据中取 {heartRate.HeartRate} 生成胎心值");
  565. // heartRate.LastUpdate = lastFreqHr.LastUpdate;
  566. //}
  567. //else
  568. //{
  569. // //int selectedHrValue = SelectValueFromFreqHeartRate(heartRate, triggerHighFreqHigh, triggerHighFreqLow, lastPhr);
  570. // int selectedHrValue = SelectValueFromFreqHeartRate(heartRate.Serialno, triggerHighFreqHigh, triggerHighFreqLow, highFreqSampleTimes, phrFreqstatus, lastPhr);
  571. // heartRate.HeartRate = selectedHrValue;
  572. // /// 超过10分钟最近12个数据生成胎心值
  573. // _logger.LogInformation($"{heartRate.Serialno} 超过10分钟最近12个数据中取 {heartRate.HeartRate} 生成胎心值");
  574. //}
  575. //_logger.LogInformation($"{heartRate.Serialno} 高频结束后的时间倒序的正常心率触发的高频心率处理");
  576. //await SaveAndPushFetalHeartRateEndFreqHeartRateAsync(heartRate, commonPHR, highFreqSampleTimes, upperAlarmThreshold, lowerAlarmThreshold, DateTimeUtil.ConvertToTimeStamp(phrFreqstatus!.LastUpdate).ToString(), phrFreqstatus!.LastUpdate, FreqStatsEnd);
  577. _logger.LogInformation($"{heartRate.Serialno} 高频结束后的时间倒序的正常心率触发的高频心率处理");
  578. await SaveAndPushFetalHeartRateEndFreqHeartRateAsync(heartRate, commonPHR, highFreqSampleTimes, upperAlarmThreshold, lowerAlarmThreshold, DateTimeUtil.ConvertToTimeStamp(phrFreqstatus!.LastUpdate).ToString(), phrFreqstatus!.LastUpdate, FreqStatsEnd, lastPhr, triggerHighFreqHigh, triggerHighFreqLow);
  579. _logger.LogInformation($"{heartRate.Serialno} 高频数据个数{freqCollection.Count},触发高频开始时间{phrFreqstatus!.LastUpdate},时间倒叙");
  580. }
  581. else
  582. {
  583. _logger.LogInformation($"{heartRate.Serialno} 时间倒叙触发计算高频心率的数据不足{stopHighFreqSampleCount}条,不进行胎心计算");
  584. }
  585. #endregion
  586. }
  587. else
  588. {
  589. _logger.LogInformation($"{heartRate.Serialno} 高频持续时间不足{highFreqSampleTimes},只持续{(firstTwoPhr[1] - phrFreqstatus!.LastUpdate).TotalSeconds} 秒");
  590. }
  591. // 删除高频状态的首条记录
  592. await _deviceCacheMgr.DelPregnancyHeartRateFreqStatusAsync(heartRate.Serialno);
  593. // (逐条计算)计算本次常规心率的胎心数据(高频结束后,实时处理)
  594. //await CalculateNormalFetalHeartRateAsync(heartRate, upperAlarmThreshold, lowerAlarmThreshold, intervalFHR, commonPHR);
  595. await CalculateNormalFetalHeartRateScheduleAsync(heartRate);
  596. }
  597. // 删除高频状态的首条记录
  598. await _deviceCacheMgr.DelPregnancyHeartRateFreqStatusAsync(heartRate.Serialno);
  599. _logger.LogInformation($"{heartRate.Serialno} 超时结束高频心率状态 timeDiffInSeconds {timeDiffInSeconds},highFreqSampleInterval:{highFreqSampleInterval},高频状态持续{(firstTwoPhr[1] - phrFreqstatus!.LastUpdate).TotalSeconds} 秒");
  600. //// 使用延后计算
  601. //var fhrScheduleKey = $"health_monitor/schedule_push/cal_fetal_heart_rate/imei/{heartRate.Serialno}";
  602. //var fhrScheduleTTL = 60;
  603. //await SetIntervalTriggerAsync(fhrScheduleKey, heartRate.Serialno, fhrScheduleTTL, heartRate);
  604. }
  605. // 常规心率(本次心率可能是高频心率的首条,所以要使用延后计算胎心率)
  606. else
  607. {
  608. // 本次心率可能是高频心率的首条,所以要使用本次常规心率延后计算胎心率
  609. //var fhrScheduleKey = $"health_monitor/schedule_push/cal_fetal_heart_rate/imei/{heartRate.Serialno}";
  610. //var fhrScheduleTTL = 30;
  611. //await SetIntervalTriggerAsync(fhrScheduleKey, heartRate.Serialno, fhrScheduleTTL, heartRate);
  612. //_logger.LogInformation($"{heartRate.Serialno} 延时50秒,判断当前数据是否为高频首条");
  613. // 本次心率可能是高频心率的首条,所以要使用本次常规心率延后计算胎心率 查询30秒后是否有高频缓存
  614. Thread thread = new(async () =>
  615. {
  616. try
  617. {
  618. #region 休眠highFreqSampleInterval2秒
  619. var startTime = DateTime.Now;
  620. //var highFreqSampleInterval2 = (int)watchConfig!["highFreqSampleInterval"]!+5;
  621. var highFreqSampleInterval2 = highFreqSampleInterval;
  622. var during = TimeSpan.FromSeconds(highFreqSampleInterval2);
  623. while (true)
  624. {
  625. if (DateTime.Now - startTime > during)
  626. {
  627. break;
  628. }
  629. await Task.Delay(TimeSpan.FromSeconds(1));
  630. }
  631. #endregion
  632. var phrFreqstatus = await _deviceCacheMgr.GetPregnancyHeartRateFreqStatusAsync(heartRate.Serialno);
  633. _logger.LogInformation($"phrFreqstatus==null:{phrFreqstatus == null}");
  634. _logger.LogInformation($"phrFreqstatus.LastUpdate < heartRate.LastUpdate:{phrFreqstatus?.LastUpdate < heartRate.LastUpdate}");
  635. if (phrFreqstatus == null || phrFreqstatus.LastUpdate < heartRate.LastUpdate)
  636. {
  637. // (逐条计算)常规心率
  638. //await CalculateNormalFetalHeartRateAsync(heartRate, upperAlarmThreshold, lowerAlarmThreshold, intervalFHR, commonPHR);
  639. //_logger.LogInformation($"{heartRate.Serialno} 计算常规心率");
  640. // 延时计算常规心率
  641. await CalculateNormalFetalHeartRateScheduleAsync(heartRate);
  642. }
  643. }
  644. catch (Exception ex)
  645. {
  646. _logger.LogError($"处理延时计算异常:{ex.Message}, {ex.StackTrace}");
  647. }
  648. });
  649. thread.Start();
  650. }
  651. }
  652. #endregion
  653. }
  654. else
  655. {
  656. _logger.LogInformation($"{heartRate.Serialno} 记录不足30条,建模中");
  657. }
  658. }
  659. }
  660. catch (Exception ex)
  661. {
  662. _logger.LogError($"{heartRate.Serialno} 处理孕妇心率数据异常 \n{ex.Message}\n{ex.StackTrace}");
  663. }
  664. }
  665. /// <summary>
  666. /// 延时计算
  667. /// </summary>
  668. /// <param name="heartRate"></param>
  669. /// <returns></returns>
  670. public async Task CalculateNormalFetalHeartRateScheduleAsync(HisGpsHeartRate heartRate)
  671. {
  672. var fhrScheduleKey = $"health_monitor/schedule_push/cal_normalphr_fetal_heart_rate/imei/{heartRate.Serialno}";
  673. DateTime nowInterval = DateTime.Now;
  674. if (nowInterval.Second > 0)
  675. {
  676. nowInterval = nowInterval.AddMinutes(1);
  677. }
  678. // 需要减去的分钟
  679. int minutesToSubtract = nowInterval.Minute % INTERVAL_FHR;
  680. var nextRunTime=nowInterval
  681. .AddMinutes(-minutesToSubtract)
  682. .AddMinutes(INTERVAL_FHR);
  683. TimeSpan timeUntilNextRun = nextRunTime - nowInterval;
  684. var ttl = (long)timeUntilNextRun.TotalSeconds;
  685. await SetIntervalTriggerAsync(fhrScheduleKey, heartRate.Serialno, ttl, heartRate);
  686. _logger.LogInformation($"{heartRate.Serialno}--{heartRate.MessageId} 触发延时计算常规胎心");
  687. }
  688. /// <summary>
  689. /// 从高频心率数据中取心率值计算胎心值
  690. /// </summary>
  691. /// <param name="sn"></param>
  692. /// <param name="triggerHighFreqHigh"></param>
  693. /// <param name="triggerHighFreqLow"></param>
  694. /// <param name="highFreqSampleTimes"></param>
  695. /// <param name="firstFreqPhr"></param>
  696. /// <param name="lastPhr"></param>
  697. /// <returns></returns>
  698. private int SelectValueFromFreqHeartRate(string sn, int triggerHighFreqHigh, int triggerHighFreqLow,int highFreqSampleTimes, PregnancyHeartRateModel firstFreqPhr, List<PregnancyHeartRateModel> lastPhr)
  699. {
  700. // 连续12个心率的值的最小值
  701. var selectedHrValue = lastPhr.Select(i => i.PregnancyHeartRate).Min();
  702. _logger.LogInformation($"{sn} 最近12个数据的最小值 {selectedHrValue}");
  703. /// 高频状态下,取值心率低于高频最小值阀值,则需要取最大值进行计算。刚好跟大于高频最大值阀值刚好相反
  704. if (selectedHrValue < triggerHighFreqLow)
  705. {
  706. // 低于高频最小值阀值,则需要取最大值
  707. var selectedHrMax = lastPhr.Select(i => i.PregnancyHeartRate).Max();
  708. _logger.LogInformation($"{sn} 最近12个数据的最小值 {selectedHrValue},低于高频最小值阀值 {triggerHighFreqLow},取最近12个数据的最大值{selectedHrMax}作为心率");
  709. selectedHrValue = selectedHrMax;
  710. }
  711. if (selectedHrValue > triggerHighFreqHigh)
  712. {
  713. var selectedHrMin = lastPhr.Select(i => i.PregnancyHeartRate).Min();
  714. _logger.LogInformation($"{sn} 最近12个数据的最小值 {selectedHrValue},低于高频最大值阀值 {triggerHighFreqHigh},最近12个数据的最小值{selectedHrMin}作为心率");
  715. selectedHrValue = selectedHrMin;
  716. }
  717. #region 判断是否有低于低频阀值引起的高频
  718. /// 低频阀值引起的高频情况下,选择的心率值是正常心率的最小值,之后的区间选择的心率值是正常心率的最小值,不足10分钟没有正常值取最小阀值,超过10分钟没有正常值取最大阀值
  719. if (firstFreqPhr.PregnancyHeartRate < triggerHighFreqLow)
  720. {
  721. _logger.LogInformation($"{sn} 低频阀值引起的高频,低频心率引起值 {firstFreqPhr.PregnancyHeartRate}");
  722. var phr = lastPhr.Where(i => i.PregnancyHeartRate <= triggerHighFreqHigh && i.PregnancyHeartRate >= triggerHighFreqLow);
  723. var lastFreqHr = lastPhr.First();
  724. var ts = DateTimeUtil.GetTimeDifferenceInSeconds((DateTime)lastFreqHr.LastUpdate!, (DateTime) firstFreqPhr.LastUpdate!);
  725. if (!phr.Any())
  726. {
  727. if (ts<highFreqSampleTimes)
  728. {
  729. selectedHrValue = triggerHighFreqLow;
  730. _logger.LogInformation($"{sn} 高频不足10分钟没有正常值取最小阀值");
  731. }
  732. else
  733. {
  734. selectedHrValue = triggerHighFreqHigh;
  735. _logger.LogInformation($"{sn} 高频超过10分钟没有正常值取最大阀值");
  736. }
  737. }
  738. else
  739. {
  740. selectedHrValue = lastPhr.Where(i => i.PregnancyHeartRate <= triggerHighFreqHigh && i.PregnancyHeartRate >= triggerHighFreqLow)
  741. .ToList()
  742. .Select(i => i.PregnancyHeartRate).Min();
  743. }
  744. }
  745. #endregion
  746. return selectedHrValue;
  747. }
  748. /// <summary>
  749. /// 高频胎心处理
  750. /// 1. 高频数据触发连续12个值都是正常的的高频心率处理
  751. /// 2. 高频结束后的highFreqSampleTimes=0的高频心率处理
  752. /// 3. 高频结束后的在highFreqSampleTimes>0 正常心率触发的高频心率处理(常态)
  753. /// 4. 高频结束后的时间倒序的正常心率触发的高频心率处理
  754. /// 5. 高频结束后计算胎心数据,防止结束后与常规心理的胎心处理过长,定时器时长highFreqSampleInterval触发的高频心率处理
  755. /// 高频结束后超过9分钟且不在阈值内才告警
  756. /// </summary>
  757. /// <param name="heartRate"></param>
  758. /// <param name="commonPHR"></param>
  759. /// <param name="highFreqSampleTimes"></param>
  760. /// <param name="upperAlarmThreshold"></param>
  761. /// <param name="lowerAlarmThreshold"></param>
  762. /// <param name="sampleTime"></param>
  763. /// <param name="statStartTime"></param>
  764. /// <param name="statEndTime"></param>
  765. /// <returns></returns>
  766. private async Task SaveAndPushFetalHeartRateEndFreqHeartRateAsync(HisGpsHeartRate heartRate, PregnancyCommonHeartRateModel commonPHR, int highFreqSampleTimes, int upperAlarmThreshold, int lowerAlarmThreshold, string sampleTime, DateTime statStartTime, DateTime statEndTime)
  767. {
  768. var fetalHeartRate = await _serviceTDengine.GetFetalHeartRateAsync(heartRate.Serialno, (int)heartRate.HeartRate!);
  769. var phrFreqstatus = await _deviceCacheMgr.GetPregnancyHeartRateFreqStatusAsync(heartRate.Serialno);
  770. var isAbnormal = 0;
  771. #region 判断是否够highFreqSampleTimes,540s
  772. var ts = DateTimeUtil.GetTimeDifferenceInSeconds((DateTime)heartRate.LastUpdate!, phrFreqstatus!.LastUpdate);
  773. // 判断是否够highFreqSampleTimes,540s
  774. ///高频时长不足10分钟停止高频的胎心值生成说明:最近12个数据的最小值生成胎心值,
  775. ///对于小于高频下限阀值取高频下限阀值进行转换;
  776. ///对于高于高频上限阀值取高频上限阀值进行转换。并输出相关日志后续进行数据分析。
  777. if (ts < highFreqSampleTimes)
  778. {
  779. if (fetalHeartRate > upperAlarmThreshold)
  780. {
  781. _logger.LogWarning($"{heartRate.Serialno} 高频持续不足10分钟,计算胎心值 {fetalHeartRate} 高于高频警告上限阀值{upperAlarmThreshold},最后胎心值{upperAlarmThreshold},并且不告警");
  782. fetalHeartRate = upperAlarmThreshold;
  783. }
  784. else if (fetalHeartRate < lowerAlarmThreshold)
  785. {
  786. _logger.LogWarning($"{heartRate.Serialno} 高频持续不足10分钟,计算胎心值 {fetalHeartRate} 低于高频警告下限阀值 {lowerAlarmThreshold},最后胎心值{lowerAlarmThreshold},并且不告警");
  787. fetalHeartRate = lowerAlarmThreshold;
  788. }
  789. else
  790. {
  791. _logger.LogWarning($"{heartRate.Serialno} 高频持续不足10分钟,在高频警告下限阀值 {lowerAlarmThreshold} 和 高频警告上限阀值:{upperAlarmThreshold}之间,最后胎心值{fetalHeartRate},并且不告警");
  792. }
  793. }
  794. // 超过highFreqSampleTimes,540s
  795. else
  796. {
  797. if (fetalHeartRate > 220)
  798. {
  799. fetalHeartRate = 220;
  800. _logger.LogWarning($"{heartRate.Serialno} 大于220,按220输出,计算因子:孕妇心率 {heartRate.HeartRate},周期:{statStartTime.ToString("yyyy-MM-dd HH:mm:ss")}----{statEndTime.ToString("yyyy-MM-dd HH:mm:ss")}");
  801. }
  802. // 胎心的最小值调整为90,超过都按该值90
  803. if (fetalHeartRate < 90)
  804. {
  805. fetalHeartRate = 90;
  806. _logger.LogWarning($"{heartRate.Serialno} 小于90,按90输出,计算因子:孕妇心率 {heartRate.HeartRate}, 周期:{statStartTime.ToString("yyyy-MM-dd HH:mm:ss")}----{statEndTime.ToString("yyyy-MM-dd HH:mm:ss")}");
  807. }
  808. isAbnormal = fetalHeartRate > upperAlarmThreshold ? 1 : (fetalHeartRate < lowerAlarmThreshold ? 2 : 0);
  809. }
  810. #endregion
  811. _logger.LogInformation($"{heartRate.Serialno} 在 高频 状态,生成胎心值:{fetalHeartRate},统计周期:{statStartTime.ToString("yyyy-MM-dd HH:mm:ss")}----{statEndTime.ToString("yyyy-MM-dd HH:mm:ss")}");
  812. // 保存到 数据服务 MySQL 数据库
  813. HisGpsFetalHeartRate gpsFetalHeartRate = new()
  814. {
  815. FetalHeartRateId = Guid.NewGuid().ToString("D"),
  816. PersonId = commonPHR!.PersonId,
  817. Serialno = heartRate.Serialno,
  818. HeartRate = fetalHeartRate,
  819. SampleTime = sampleTime.Length > 10 ? sampleTime.Substring(0, 10) : sampleTime,
  820. IsAbnormal = isAbnormal,
  821. StatStartTime = statStartTime,
  822. StatEndTime = statEndTime,//commonPHR.StatEndTime,
  823. CreateTime = DateTime.Now,
  824. Method = 1,
  825. IsDisplay = 1,
  826. DeviceKey = commonPHR!.DeviceKey
  827. };
  828. await _hisFetalHeartApiClient.AddAsync(gpsFetalHeartRate).ConfigureAwait(false);
  829. #region 高频心率计算胎心数据到iot设备
  830. //if (phrFreqstatus != null && isAbnormal != 0)
  831. //{
  832. // await _serviceIotApi.SetFetalHeartRateConfig(heartRate.Serialno, fetalHeartRate, sampleTime, isAbnormal);
  833. //}
  834. // 高频有数据都推送到iot
  835. await _serviceIotApi.SetFetalHeartRateConfig(heartRate.Serialno, fetalHeartRate, sampleTime, isAbnormal);
  836. #endregion
  837. var device = await _deviceCacheMgr.GetDeviceBySerialNoAsync(heartRate.Serialno).ConfigureAwait(false);
  838. var fhrMsgId = $"{heartRate.Serialno}-{sampleTime}-{Guid.NewGuid().ToString("D")[^3..]}";
  839. var fhrMsgTime = DateTimeUtil.GetDateTimeFromUnixTimeMilliseconds(long.Parse(sampleTime.Length < 13 ? sampleTime.PadRight(13, '0') : sampleTime)).ToString("yyyy-MM-dd HH:mm:ss");
  840. // 胎心数据推送到第三方
  841. var topic = "topic.push.third";
  842. var fhrThridMsg = new
  843. {
  844. messageId = fhrMsgId,
  845. topic = topic,
  846. time = fhrMsgTime,
  847. data = new
  848. {
  849. imei = heartRate.Serialno,
  850. value = fetalHeartRate,
  851. isAbnormal,
  852. type = "fetalHeart"
  853. }
  854. };
  855. await _serviceMqProcess.ProcessIMEIEventMessageAsync(fhrMsgId, topic, 31, fhrThridMsg).ConfigureAwait(false);
  856. // 胎心数据推送到微信
  857. if (isAbnormal != 0)
  858. {
  859. topic = "topic.push.wx";
  860. var fhrMsg = new
  861. {
  862. messageId = fhrMsgId,
  863. topic = topic,
  864. time = fhrMsgTime,
  865. data = new
  866. {
  867. deviceId = device?.DeviceId,
  868. imei = heartRate.Serialno,
  869. alarmTypeId = 12,
  870. alarmDeviceName = heartRate.Serialno,
  871. alarmRemarks = JsonConvert.SerializeObject(new { fetalHeartValue = fetalHeartRate, isAbnormal = isAbnormal }),
  872. address = string.Empty,
  873. deviceKey = device?.DeviceId
  874. }
  875. };
  876. await _serviceMqProcess.ProcessIMEIEventMessageAsync(fhrMsgId, topic, fhrMsg).ConfigureAwait(false);
  877. }
  878. }
  879. /// <summary>
  880. /// 高频胎心处理
  881. /// 1. 高频数据触发连续12个值都是正常的的高频心率处理
  882. /// 2. 高频结束后的highFreqSampleTimes=0的高频心率处理
  883. /// 3. 高频结束后的在highFreqSampleTimes>0 正常心率触发的高频心率处理(常态)
  884. /// 4. 高频结束后的时间倒序的正常心率触发的高频心率处理
  885. /// 5. 高频结束后计算胎心数据,防止结束后与常规心理的胎心处理过长,定时器时长highFreqSampleInterval触发的高频心率处理
  886. /// 高频结束后超过9分钟且不在阈值内才告警
  887. /// </summary>
  888. /// <param name="heartRate"></param>
  889. /// <param name="commonPHR"></param>
  890. /// <param name="highFreqSampleTimes"></param>
  891. /// <param name="upperAlarmThreshold"></param>
  892. /// <param name="lowerAlarmThreshold"></param>
  893. /// <param name="sampleTime"></param>
  894. /// <param name="statStartTime"></param>
  895. /// <param name="statEndTime"></param>
  896. /// <returns></returns>
  897. private async Task SaveAndPushFetalHeartRateEndFreqHeartRateAsync(
  898. HisGpsHeartRate heartRate,
  899. PregnancyCommonHeartRateModel commonPHR,
  900. int highFreqSampleTimes,
  901. int upperAlarmThreshold,
  902. int lowerAlarmThreshold,
  903. string sampleTime,
  904. DateTime statStartTime,
  905. DateTime statEndTime,
  906. List<PregnancyHeartRateModel> lastPhr,
  907. int triggerHighFreqHigh,
  908. int triggerHighFreqLow)
  909. {
  910. #region 选择心率值计算胎心
  911. /// 12个心率数据判定上限处理模式还是下限处理模式:
  912. /// 1、12个数据的最大值 > 高频上限阀值,则按上限模式进行计算。取12个值的最小值心率值转换为胎心值。
  913. ///
  914. /// 2、12个数据的最小值 < 高频下限阀值,则按下限模式进行计算。取正常范围内的最小值心率值转换为胎心值,不产生告警。
  915. /// 如果正常范围内没有值,则取异常范围最大值心率值转换为胎心值,并产生胎心过缓告警。
  916. var phrFreqstatus = await _deviceCacheMgr.GetPregnancyHeartRateFreqStatusAsync(heartRate.Serialno);
  917. var isAbnormal = 0;
  918. //取正常范围内的最小值心率值转换为胎心值,不产生告警。
  919. var lastNormalPhr = lastPhr.Where(i => i.PregnancyHeartRate > triggerHighFreqLow && i.PregnancyHeartRate < triggerHighFreqHigh);
  920. int selectedHrValue = (int)heartRate.HeartRate!;
  921. if (lastPhr.Select(i => i.PregnancyHeartRate).Max() > triggerHighFreqHigh)
  922. {
  923. // 取12个值的最小值心率值转换为胎心值。
  924. selectedHrValue = lastPhr.Select(i => i.PregnancyHeartRate).Min();
  925. }
  926. if (lastPhr.Select(i => i.PregnancyHeartRate).Min() < triggerHighFreqLow)
  927. {
  928. // 有正常值
  929. if (lastNormalPhr.Any())
  930. {
  931. // 2个数据的最小值 < 高频下限阀值,则按下限模式进行计算。取正常范围内的最小值心率值转换为胎心值,不产生告警
  932. selectedHrValue = lastPhr
  933. .Where(i => i.PregnancyHeartRate > triggerHighFreqLow && i.PregnancyHeartRate < triggerHighFreqHigh)
  934. .Select(i => i.PregnancyHeartRate)
  935. .Min();
  936. }
  937. // 无正常值
  938. if (!lastNormalPhr.Any())
  939. {
  940. selectedHrValue = lastPhr
  941. .Where(i => i.PregnancyHeartRate > triggerHighFreqLow && i.PregnancyHeartRate < triggerHighFreqHigh)
  942. .Select(i => i.PregnancyHeartRate)
  943. .Max();
  944. }
  945. }
  946. _logger.LogInformation($"{heartRate.Serialno} 高频选择心率值:{selectedHrValue}");
  947. var fetalHeartRate = await _serviceTDengine.GetFetalHeartRateAsync(heartRate.Serialno, selectedHrValue);
  948. #endregion
  949. #region 判断是否够highFreqSampleTimes,540s
  950. var ts = DateTimeUtil.GetTimeDifferenceInSeconds((DateTime)heartRate.LastUpdate!, phrFreqstatus!.LastUpdate);
  951. // 判断是否够highFreqSampleTimes,540s
  952. ///高频时长不足10分钟停止高频的胎心值生成说明:最近12个数据的最小值生成胎心值,
  953. ///对于小于高频下限阀值取高频下限阀值进行转换;
  954. ///对于高于高频上限阀值取高频上限阀值进行转换。并输出相关日志后续进行数据分析。
  955. if (ts < highFreqSampleTimes)
  956. {
  957. if (fetalHeartRate > upperAlarmThreshold)
  958. {
  959. _logger.LogWarning($"{heartRate.Serialno} 高频持续不足10分钟,计算胎心值 {fetalHeartRate} 高于高频警告上限阀值{upperAlarmThreshold},最后胎心值{upperAlarmThreshold},并且不告警");
  960. fetalHeartRate = upperAlarmThreshold;
  961. }
  962. else if (fetalHeartRate < lowerAlarmThreshold)
  963. {
  964. _logger.LogWarning($"{heartRate.Serialno} 高频持续不足10分钟,计算胎心值 {fetalHeartRate} 低于高频警告下限阀值 {lowerAlarmThreshold},最后胎心值{lowerAlarmThreshold},并且不告警");
  965. fetalHeartRate = lowerAlarmThreshold;
  966. }
  967. else
  968. {
  969. _logger.LogWarning($"{heartRate.Serialno} 高频持续不足10分钟,在高频警告下限阀值 {lowerAlarmThreshold} 和 高频警告上限阀值:{upperAlarmThreshold}之间,最后胎心值{fetalHeartRate},并且不告警");
  970. }
  971. isAbnormal = 0;
  972. }
  973. // 超过highFreqSampleTimes,540s
  974. else
  975. {
  976. if (fetalHeartRate > 220)
  977. {
  978. fetalHeartRate = 220;
  979. _logger.LogWarning($"{heartRate.Serialno} 大于220,按220输出,计算因子:孕妇心率 {heartRate.HeartRate},周期:{statStartTime.ToString("yyyy-MM-dd HH:mm:ss")}----{statEndTime.ToString("yyyy-MM-dd HH:mm:ss")}");
  980. }
  981. // 胎心的最小值调整为90,超过都按该值90
  982. if (fetalHeartRate < 90)
  983. {
  984. fetalHeartRate = 90;
  985. _logger.LogWarning($"{heartRate.Serialno} 小于90,按90输出,计算因子:孕妇心率 {heartRate.HeartRate}, 周期:{statStartTime.ToString("yyyy-MM-dd HH:mm:ss")}----{statEndTime.ToString("yyyy-MM-dd HH:mm:ss")}");
  986. }
  987. isAbnormal = fetalHeartRate > upperAlarmThreshold ? 1 : (fetalHeartRate < lowerAlarmThreshold ? 2 : 0);
  988. if (!lastNormalPhr.Any())
  989. {
  990. // 偏低(过缓)
  991. isAbnormal = 2;
  992. }
  993. }
  994. #endregion
  995. _logger.LogInformation($"{heartRate.Serialno} 在 高频 状态,生成胎心值:{fetalHeartRate},统计周期:{statStartTime.ToString("yyyy-MM-dd HH:mm:ss")}----{statEndTime.ToString("yyyy-MM-dd HH:mm:ss")}");
  996. // 保存到 数据服务 MySQL 数据库
  997. HisGpsFetalHeartRate gpsFetalHeartRate = new()
  998. {
  999. FetalHeartRateId = Guid.NewGuid().ToString("D"),
  1000. PersonId = commonPHR!.PersonId,
  1001. Serialno = heartRate.Serialno,
  1002. HeartRate = fetalHeartRate,
  1003. SampleTime = sampleTime.Length > 10 ? sampleTime.Substring(0, 10) : sampleTime,
  1004. IsAbnormal = isAbnormal,
  1005. StatStartTime = statStartTime,
  1006. StatEndTime = statEndTime,//commonPHR.StatEndTime,
  1007. CreateTime = DateTime.Now,
  1008. Method = 1,
  1009. IsDisplay = 1,
  1010. DeviceKey = commonPHR!.DeviceKey
  1011. };
  1012. await _hisFetalHeartApiClient.AddAsync(gpsFetalHeartRate).ConfigureAwait(false);
  1013. #region 高频心率计算胎心数据到iot设备
  1014. //if (phrFreqstatus != null && isAbnormal != 0)
  1015. //{
  1016. // await _serviceIotApi.SetFetalHeartRateConfig(heartRate.Serialno, fetalHeartRate, sampleTime, isAbnormal);
  1017. //}
  1018. // 高频有数据都推送到iot
  1019. await _serviceIotApi.SetFetalHeartRateConfig(heartRate.Serialno, fetalHeartRate, sampleTime, isAbnormal);
  1020. #endregion
  1021. var device = await _deviceCacheMgr.GetDeviceBySerialNoAsync(heartRate.Serialno).ConfigureAwait(false);
  1022. var fhrMsgId = $"{heartRate.Serialno}-{sampleTime}-{Guid.NewGuid().ToString("D")[^3..]}";
  1023. var fhrMsgTime = DateTimeUtil.GetDateTimeFromUnixTimeMilliseconds(long.Parse(sampleTime.Length < 13 ? sampleTime.PadRight(13, '0') : sampleTime)).ToString("yyyy-MM-dd HH:mm:ss");
  1024. // 胎心数据推送到第三方
  1025. var topic = "topic.push.third";
  1026. var fhrThridMsg = new
  1027. {
  1028. messageId = fhrMsgId,
  1029. topic = topic,
  1030. time = fhrMsgTime,
  1031. data = new
  1032. {
  1033. imei = heartRate.Serialno,
  1034. value = fetalHeartRate,
  1035. isAbnormal,
  1036. type = "fetalHeart"
  1037. }
  1038. };
  1039. await _serviceMqProcess.ProcessIMEIEventMessageAsync(fhrMsgId, topic, 31, fhrThridMsg).ConfigureAwait(false);
  1040. // 胎心数据推送到微信
  1041. if (isAbnormal != 0)
  1042. {
  1043. topic = "topic.push.wx";
  1044. var fhrMsg = new
  1045. {
  1046. messageId = fhrMsgId,
  1047. topic = topic,
  1048. time = fhrMsgTime,
  1049. data = new
  1050. {
  1051. deviceId = device?.DeviceId,
  1052. imei = heartRate.Serialno,
  1053. alarmTypeId = 12,
  1054. alarmDeviceName = heartRate.Serialno,
  1055. alarmRemarks = JsonConvert.SerializeObject(new { fetalHeartValue = fetalHeartRate, isAbnormal = isAbnormal }),
  1056. address = string.Empty,
  1057. deviceKey = device?.DeviceId
  1058. }
  1059. };
  1060. await _serviceMqProcess.ProcessIMEIEventMessageAsync(fhrMsgId, topic, fhrMsg).ConfigureAwait(false);
  1061. }
  1062. }
  1063. private async Task SetIntervalTriggerAsync(string key, string imei, long interval, HisGpsHeartRate heartRate)
  1064. {
  1065. // var key = $"health_monitor/schedule_push/{type}/imei/{imei}";
  1066. var schedulePush = await _serviceEtcd.GetValAsync(key).ConfigureAwait(false);
  1067. if (string.IsNullOrWhiteSpace(schedulePush))
  1068. {
  1069. var now = DateTime.Now;
  1070. var timeNextRun = now.Add(TimeSpan.FromSeconds(interval));
  1071. var data = new
  1072. {
  1073. imei,
  1074. create_time = now.ToString("yyyy-MM-dd HH:mm:ss"),
  1075. ttl = interval,
  1076. next_run_time = timeNextRun.ToString("yyyy-MM-dd HH:mm:ss"),
  1077. trigger = heartRate,
  1078. };
  1079. var result = JsonConvert.SerializeObject(data);
  1080. await _serviceEtcd.PutValAsync(key, result, interval, false).ConfigureAwait(false);
  1081. }
  1082. }
  1083. /// <summary>
  1084. /// 高频延时计算触发器
  1085. /// </summary>
  1086. /// <param name="key">键</param>
  1087. /// <param name="imei">IMEI</param>
  1088. /// <param name="interval">高频采集间隔</param>
  1089. /// <param name="heartRate">首条高频心率</param>
  1090. /// <returns></returns>
  1091. private async Task SetFreqHeartRateTriggerAsync(string key, string imei, long interval, HisGpsHeartRate? heartRate=null)
  1092. {
  1093. var schedulePush = await _serviceEtcd.GetValAsync(key).ConfigureAwait(false);
  1094. if (string.IsNullOrWhiteSpace(schedulePush))
  1095. {
  1096. var now = DateTime.Now;
  1097. var data = new
  1098. {
  1099. imei,
  1100. create_time = now.ToString("yyyy-MM-dd HH:mm:ss"),
  1101. lease = interval,
  1102. trigger = heartRate,
  1103. };
  1104. var result = JsonConvert.SerializeObject(data);
  1105. await _serviceEtcd.PutValAsync(key, result, interval, false).ConfigureAwait(false);
  1106. _logger.LogInformation($"{imei} 心率高频状态创建首条高频心率的触发记录,创建高频延时计算触发器");
  1107. }
  1108. else
  1109. {
  1110. // 不断修改时长,直到最后的一条高频胎心
  1111. await _serviceEtcd.PutValAsync(key, schedulePush, interval, false).ConfigureAwait(false);
  1112. _logger.LogInformation($"{imei} 心率高频状态续租");
  1113. }
  1114. }
  1115. /// <summary>
  1116. /// 去除高频数据
  1117. /// </summary>
  1118. /// <param name="phr"></param>
  1119. /// <param name="highFreqSampleInterva"></param>
  1120. /// <returns></returns>
  1121. private static List<PregnancyHeartRateModel> GetNonFreqPregnancyHeartRate(List<PregnancyHeartRateModel> phr,int highFreqSampleInterval)
  1122. {
  1123. //phr = phr.OrderByDescending(i => i.LastUpdate).ToList();
  1124. //var result = new List<PregnancyHeartRateModel>();
  1125. //PregnancyHeartRateModel? previousItem = null;
  1126. //foreach (var item in phr)
  1127. //{
  1128. // if (previousItem != null)
  1129. // {
  1130. // var timeNextDiff =(previousItem!.LastUpdate - item.LastUpdate).TotalSeconds;
  1131. // if (timeNextDiff > highFreqSampleInterval)
  1132. // {
  1133. // result.Add(previousItem);
  1134. // }
  1135. // }
  1136. // previousItem = item;
  1137. //}
  1138. //// 添加上一个
  1139. //if (previousItem != null)
  1140. //{
  1141. // result.Add(previousItem);
  1142. //}
  1143. //return result;
  1144. #region 反向
  1145. var phr1 = phr.OrderByDescending(i => i.LastUpdate).ToList();
  1146. var result = new List<PregnancyHeartRateModel>();
  1147. PregnancyHeartRateModel? previousItem1 = null;
  1148. foreach (var item in phr1)
  1149. {
  1150. if (previousItem1 != null)
  1151. {
  1152. var timeNextDiff = (previousItem1!.LastUpdate - item.LastUpdate).TotalSeconds;
  1153. if (timeNextDiff > highFreqSampleInterval)
  1154. {
  1155. result.Add(previousItem1);
  1156. }
  1157. }
  1158. previousItem1 = item;
  1159. }
  1160. // 添加上一个
  1161. if (previousItem1 != null)
  1162. {
  1163. result.Add(previousItem1);
  1164. }
  1165. #endregion
  1166. #region 正向
  1167. var phr2 = phr.OrderByDescending(i => i.LastUpdate).ToList(); ;
  1168. var freqCollection = new List<PregnancyHeartRateModel>();
  1169. PregnancyHeartRateModel? previousItem = null;
  1170. foreach (var item in phr2)
  1171. {
  1172. if (previousItem != null)
  1173. {
  1174. var timeNextDiff = (previousItem!.LastUpdate - item.LastUpdate).TotalSeconds;
  1175. if (timeNextDiff <= highFreqSampleInterval)
  1176. {
  1177. freqCollection.Add(item);
  1178. }
  1179. }
  1180. previousItem = item;
  1181. }
  1182. //去除高频
  1183. foreach (var item in freqCollection)
  1184. {
  1185. phr2.Remove(item);
  1186. }
  1187. #endregion
  1188. // 交集
  1189. var commonElements = phr2.Intersect(result).ToList();
  1190. return commonElements;
  1191. }
  1192. /// <summary>
  1193. /// 获取高频数据
  1194. /// </summary>
  1195. /// <param name="phr"></param>
  1196. /// <param name="highFreqSampleInterval"></param>
  1197. /// <returns></returns>
  1198. private static List<PregnancyHeartRateModel> GetFreqPregnancyHeartRate(List<PregnancyHeartRateModel> phr, int highFreqSampleInterval)
  1199. {
  1200. phr = phr.OrderByDescending(i => i.LastUpdate).ToList();
  1201. var freqCollection = new List<PregnancyHeartRateModel>();
  1202. PregnancyHeartRateModel? previousItem = null;
  1203. foreach (var item in phr)
  1204. {
  1205. if (previousItem != null)
  1206. {
  1207. var timeNextDiff = (previousItem.LastUpdate - item.LastUpdate).TotalSeconds;
  1208. if (timeNextDiff <= highFreqSampleInterval)
  1209. {
  1210. freqCollection.Add(previousItem);
  1211. }
  1212. }
  1213. previousItem = item;
  1214. }
  1215. // 检查最后一条是否高频
  1216. if (previousItem != null && (phr.Last().LastUpdate - previousItem.LastUpdate).TotalSeconds <= highFreqSampleInterval)
  1217. {
  1218. freqCollection.Add(previousItem);
  1219. }
  1220. return freqCollection;
  1221. }
  1222. }
  1223. }