You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

PregnancyHeartRateResolver.cs 71KB

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