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.

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