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.

1629 lines
90KB

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