Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

1563 lines
86KB

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