Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

2778 lines
198KB

  1. using dotnet_etcd;
  2. using Etcdserverpb;
  3. using Google.Protobuf.WellKnownTypes;
  4. using HealthMonitor.Common;
  5. using HealthMonitor.Common.helper;
  6. using HealthMonitor.Core.Common.Extensions;
  7. using HealthMonitor.Core.Pipeline;
  8. using HealthMonitor.Model.Config;
  9. using HealthMonitor.Model.Service;
  10. using HealthMonitor.Model.Service.Mapper;
  11. using HealthMonitor.Service.Biz;
  12. using HealthMonitor.Service.Biz.db;
  13. using HealthMonitor.Service.Cache;
  14. using HealthMonitor.Service.Etcd;
  15. using HealthMonitor.Service.MessageQueue;
  16. using HealthMonitor.Service.Sub;
  17. using Microsoft.AspNetCore.Mvc.RazorPages;
  18. using Microsoft.EntityFrameworkCore.Metadata;
  19. using Microsoft.EntityFrameworkCore.Metadata.Internal;
  20. using Microsoft.Extensions.Options;
  21. using NetTaste;
  22. using Newtonsoft.Json;
  23. using Newtonsoft.Json.Linq;
  24. using System;
  25. using System.Reflection;
  26. using System.Threading.Channels;
  27. using TDengineDriver;
  28. using TDengineTMQ;
  29. using TelpoDataService.Util.Clients;
  30. using TelpoDataService.Util.Entities.GpsCard;
  31. using TelpoDataService.Util.Entities.GpsLocationHistory;
  32. using TelpoDataService.Util.Models;
  33. using TelpoDataService.Util.QueryObjects;
  34. namespace HealthMonitor.WebApi
  35. {
  36. public class Worker : BackgroundService
  37. {
  38. private readonly ILogger<Worker> _logger;
  39. private readonly IServiceProvider _services;
  40. private readonly TDengineDataSubcribe _tdEngineDataSubcribe;
  41. private readonly PackageProcess _processor;
  42. private readonly TDengineService _serviceTDengine;
  43. private readonly EtcdService _serviceEtcd;
  44. private readonly HttpHelper _httpHelper = default!;
  45. private readonly IotApiService _serviceIotApi;
  46. private readonly BoodPressResolverConfig _configBoodPressResolver;
  47. private readonly BloodPressReferenceValueCacheManager _bpRefValCacheManager;
  48. private readonly PersonCacheManager _personCacheMgr;
  49. private readonly DeviceCacheManager _deviceCacheMgr;
  50. private readonly FetalMovementNormalValueRangeCacheManager _mgrFetalMovementNormalValueRangeCache;
  51. private readonly GpsLocationHistoryAccessorClient<HisGpsFetalHeartRate> _hisFetalHeartApiClient;
  52. private readonly GpsLocationHistoryAccessorClient<HisGpsFetalMovement> _hisFetalMovementApiClient;
  53. private readonly GpsLocationHistoryAccessorClient<HisGpsPsychResult> _hisPsychResultApiClient;
  54. private readonly MqProcessLogic _serviceMqProcess;
  55. private CancellationTokenSource _tokenSource = default!;
  56. private static int INTERVAL_FHR = 15;
  57. public Worker(ILogger<Worker> logger, IServiceProvider services, PersonCacheManager personCacheMgr,
  58. BloodPressReferenceValueCacheManager bpRefValCacheManager, IotApiService IotApiService,
  59. IOptions<BoodPressResolverConfig> optionBoodPressResolver, PackageProcess processor,
  60. TDengineDataSubcribe tdEngineDataSubcribe, TDengineService serviceDengine,
  61. GpsLocationHistoryAccessorClient<HisGpsFetalHeartRate> hisFetalHeartApiClient,
  62. GpsLocationHistoryAccessorClient<HisGpsFetalMovement> hisFetalMovementApiClient,
  63. GpsLocationHistoryAccessorClient<HisGpsPsychResult> hisGpsPsychResultApiClient,
  64. FetalMovementNormalValueRangeCacheManager fetalMovementNormalValueRangeCacheMgr, MqProcessLogic serviceMqProcess,
  65. HttpHelper httpHelper, EtcdService serviceEtcd, DeviceCacheManager deviceCacheMgr)
  66. {
  67. _logger = logger;
  68. _tdEngineDataSubcribe = tdEngineDataSubcribe;
  69. _services = services;
  70. _serviceIotApi = IotApiService;
  71. _processor = processor;
  72. _serviceEtcd = serviceEtcd;
  73. _serviceTDengine = serviceDengine;
  74. _httpHelper = httpHelper;
  75. _configBoodPressResolver = optionBoodPressResolver.Value;
  76. _bpRefValCacheManager = bpRefValCacheManager;
  77. _personCacheMgr = personCacheMgr;
  78. _deviceCacheMgr = deviceCacheMgr;
  79. _hisFetalHeartApiClient = hisFetalHeartApiClient;
  80. _hisFetalMovementApiClient = hisFetalMovementApiClient;
  81. _hisPsychResultApiClient = hisGpsPsychResultApiClient;
  82. _serviceMqProcess = serviceMqProcess;
  83. _mgrFetalMovementNormalValueRangeCache = fetalMovementNormalValueRangeCacheMgr;
  84. }
  85. public override Task StartAsync(CancellationToken cancellationToken)
  86. {
  87. //_logger.LogInformation("------StartAsync");
  88. _tokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
  89. return base.StartAsync(cancellationToken);
  90. }
  91. public override Task StopAsync(CancellationToken cancellationToken)
  92. {
  93. //_logger.LogInformation("------StopAsync");
  94. _tokenSource.Cancel(); //停止工作线程
  95. return base.StopAsync(cancellationToken);
  96. }
  97. protected override async Task ExecuteAsync(CancellationToken stoppingToken)
  98. {
  99. var tasks = new[]
  100. {
  101. Task.Run(async () =>
  102. {
  103. _logger.LogInformation("解析器启动");
  104. while (!stoppingToken.IsCancellationRequested)
  105. {
  106. await _processor.ResolveAsync().ConfigureAwait(false);
  107. }
  108. }, stoppingToken),
  109. Task.Run(() =>
  110. {
  111. _logger.LogInformation("TDengine 订阅启动");
  112. while (!stoppingToken.IsCancellationRequested)
  113. {
  114. _tdEngineDataSubcribe.BeginListen(stoppingToken);
  115. }
  116. }, stoppingToken),
  117. Task.Run(() =>
  118. _serviceEtcd.WacthKeysWithPrefixResponseAsync("health_monitor/schedule_push", WatchEvents),
  119. stoppingToken)
  120. };
  121. await Task.WhenAll(tasks);
  122. }
  123. private void WatchEvents(WatchResponse response)
  124. {
  125. response.Events.ToList<Mvccpb.Event>().ForEach(async e =>
  126. {
  127. try
  128. {
  129. switch (e.Type.ToString())
  130. {
  131. case "Put":
  132. // 获取时间点计算TTL
  133. Console.BackgroundColor = ConsoleColor.Blue;
  134. Console.WriteLine($"--- {e.Type}--{e.Kv.Key.ToStringUtf8()}--{e.Kv.Value.ToStringUtf8()}---{DateTime.Now}");
  135. Console.BackgroundColor = ConsoleColor.Black;
  136. break;
  137. case "Delete":
  138. // TTL到了重新计算TTL,下发
  139. //Console.BackgroundColor = ConsoleColor.Green;
  140. //Console.WriteLine($"--- {e.Type}--{e.Kv.Key.ToStringUtf8()}--{e.Kv.Value.ToStringUtf8()}---{DateTime.Now}");
  141. // var key = $"health_monitor/schedule_push/imei/{bp.Serialno}";
  142. var key = e.Kv.Key.ToStringUtf8();
  143. var imeiDel = key.Split('/')[^1];
  144. var schedule_push = await _serviceEtcd.GetValAsync(key).ConfigureAwait(false);
  145. if (string.IsNullOrWhiteSpace(schedule_push))
  146. {
  147. // 胎心数据建模
  148. if (key.Contains("pregnancy_heart_rate"))
  149. {
  150. using (_logger.BeginScope(new Dictionary<string, object> { ["RequestId"] = $"MODE-{imeiDel}-{DateTime.Now.ToString("yyyyMMddHHmmss")}" }))
  151. {
  152. #region 胎心数据建模
  153. var watchConfig = await _deviceCacheMgr.GetGpsDeviceWatchConfigCacheObjectBySerialNoAsync(imeiDel, "0067");
  154. var isFetalHeartEnable = watchConfig != null && (int)watchConfig["enabled"]! == 1;
  155. if (isFetalHeartEnable)
  156. {
  157. // 高频心率采样间隔 highFreqSampleInterval = highFreqSampleInterval+5,增加5秒兼容
  158. //var highFreqSampleInterval = (int)watchConfig!["highFreqSampleInterval"]! + 5;
  159. // 高频心率采样间隔 highFreqSampleInterval = highFreqSampleInterval+5,增加5秒兼容(最小highFreqSampleInterval=60)
  160. var highFreqSampleInterval = (int)watchConfig!["highFreqSampleInterval"]! >= 60 ? (int)watchConfig!["highFreqSampleInterval"]! + 5 : 60;
  161. // 处理孕妇业务,计算一般心率并下发
  162. var commonPHR = await _serviceTDengine.InitPregnancyCommonHeartRateModeAsync(imeiDel, highFreqSampleInterval: highFreqSampleInterval);
  163. if (commonPHR == null)
  164. {
  165. // 建模中
  166. var flag = await _serviceIotApi.SetFetalConfig(imeiDel);
  167. _logger.LogInformation($"{imeiDel} 建模建模中");
  168. }
  169. else
  170. {
  171. // 建模完成
  172. var flag = await _serviceIotApi.SetFetalConfig(imeiDel, 1, commonPHR.MaxValue, commonPHR.MinValue);
  173. _logger.LogInformation($"{imeiDel} 建模完成");
  174. // 保存到TDengine数据库
  175. await _serviceTDengine.InsertAsync<PregnancyCommonHeartRateModel>("hm_pchr", commonPHR);
  176. _logger.LogInformation($"保存TDengine完成");
  177. //
  178. }
  179. }
  180. #endregion
  181. #region 注册定时下发
  182. var startTime = DateTime.Now;
  183. // 注册下次下推
  184. var endTime = DateTime.Now;
  185. #if DEBUG
  186. //long ttl = (long)((60 * 1000-(endTime-startTime).TotalMilliseconds)/1000);
  187. //await _serviceEtcd.PutValAsync(key, imeiDel,ttl, false).ConfigureAwait(false);
  188. var interval = 0;
  189. // 获取当前时间
  190. DateTime now = DateTime.Now;
  191. // 计算距离下一个$interval天后的8点的时间间隔
  192. DateTime nextRunTime = new DateTime(now.Year, now.Month, now.Day, now.Hour, now.Minute + 2, 0).AddDays(interval);
  193. TimeSpan timeUntilNextRun = nextRunTime - now;
  194. // 如果当前时间已经超过了8点,将等待到明天后的8点
  195. if (timeUntilNextRun < TimeSpan.Zero)
  196. {
  197. timeUntilNextRun = timeUntilNextRun.Add(TimeSpan.FromMinutes(1));
  198. nextRunTime += timeUntilNextRun;
  199. }
  200. //long ttl = timeUntilNextRun.Milliseconds/1000;
  201. long ttl = (long)((timeUntilNextRun.TotalMilliseconds - (endTime - startTime).TotalMilliseconds) / 1000);
  202. var data = new
  203. {
  204. imei = imeiDel,
  205. create_time = now.ToString("yyyy-MM-dd HH:mm:ss"),
  206. ttl,
  207. next_run_time = nextRunTime.ToString("yyyy-MM-dd HH:mm:ss")
  208. };
  209. var result = JsonConvert.SerializeObject(data);
  210. await _serviceEtcd.PutValAsync(key, result, ttl, false).ConfigureAwait(false);
  211. #else
  212. // 每$interval天,晚上8点
  213. var interval = 1;
  214. // 获取当前时间
  215. DateTime now = DateTime.Now;
  216. var rand = new Random();
  217. var pushSec = rand.Next(59);
  218. int pushMin = int.TryParse(imeiDel.AsSpan(imeiDel.Length - 1), out pushMin) ? pushMin : 10;
  219. // 计算距离下一个$interval天后的0点的时间间隔
  220. DateTime nextRunTime = new DateTime(now.Year, now.Month, now.Day, 6, pushMin, pushSec).AddDays(interval);
  221. TimeSpan timeUntilNextRun = nextRunTime - now;
  222. // 如果当前时间已经超过了8点,将等待到明天后的8点
  223. if (timeUntilNextRun < TimeSpan.Zero)
  224. {
  225. timeUntilNextRun = timeUntilNextRun.Add(TimeSpan.FromDays(1));
  226. nextRunTime += timeUntilNextRun;
  227. }
  228. // var ttl = timeUntilNextRun.TotalMilliseconds;
  229. long ttl = (long)((timeUntilNextRun.TotalMilliseconds-(endTime-startTime).TotalMilliseconds)/1000);
  230. var data = new
  231. {
  232. imei = imeiDel,
  233. create_time = now.ToString("yyyy-MM-dd HH:mm:ss"),
  234. ttl,
  235. next_run_time = nextRunTime.ToString("yyyy-MM-dd HH:mm:ss")
  236. };
  237. var result = JsonConvert.SerializeObject(data);
  238. await _serviceEtcd.PutValAsync(key, result, ttl, false).ConfigureAwait(false);
  239. #endif
  240. #endregion
  241. }
  242. }
  243. // health_monitor/schedule_push/cal_fetal_heart_rate/imei/
  244. // 高频结束后计算胎心数据,防止结束后与常规心理的胎心处理过长
  245. if (key.Contains("health_monitor/schedule_push/cal_freqhr_fetal_heart_rate/imei/"))
  246. {
  247. /// 延时计算高频状态的胎心数据,防止高频结束后与触发数据时间相隔过长。
  248. var phrFreqstatus = await _deviceCacheMgr.GetPregnancyHeartRateFreqStatusAsync(imeiDel);
  249. // 高频不停,15分钟内只下发一条
  250. var push = await _deviceCacheMgr.GetBizIntervalAsync(imeiDel, "SaveAndPushFetalHeartRate");
  251. var triggerValue = (JObject)JsonConvert.DeserializeObject(e.PrevKv.Value.ToStringUtf8())!;
  252. var trigger = triggerValue["trigger"]?.ToString();
  253. // 验证高频首条是否还存在
  254. if (phrFreqstatus != null && string.IsNullOrEmpty(push))
  255. {
  256. if (!string.IsNullOrEmpty(trigger))
  257. {
  258. var triggerHeartRate = JsonConvert.DeserializeObject<HisGpsHeartRate>(trigger);
  259. using (_logger.BeginScope(new Dictionary<string, object> { ["RequestId"] = triggerHeartRate?.MessageId! }))
  260. {
  261. var watchConfig = await _deviceCacheMgr.GetGpsDeviceWatchConfigCacheObjectBySerialNoAsync(imeiDel, "0067");
  262. _logger.LogInformation($"{imeiDel}高频结束后,延迟计算高频数据产生的胎心数据");
  263. var isFetalHeartEnable = watchConfig != null && (int)watchConfig["enabled"]! == 1;
  264. if (isFetalHeartEnable)
  265. {
  266. // 防止超过12条连续正常,高频不停止
  267. if (phrFreqstatus.MessageId == triggerHeartRate?.MessageId)
  268. {
  269. // 告警上限阀值
  270. var upperAlarmThreshold = (int)watchConfig!["upperAlarmThreshold"]!;
  271. // 告警下限阀值
  272. var lowerAlarmThreshold = (int)watchConfig["lowerAlarmThreshold"]!;
  273. // 高频心率采样间隔 highFreqSampleInterval = highFreqSampleInterval+5,增加5秒兼容(最小highFreqSampleInterval=60)
  274. var highFreqSampleInterval = (int)watchConfig!["highFreqSampleInterval"]! >= 60 ? (int)watchConfig!["highFreqSampleInterval"]! + 5 : 60;
  275. //停止高频心率采样心率连续正常次数
  276. var stopHighFreqSampleCount = (int)watchConfig["stopHighFreqSampleCount"]!;
  277. var commonPHR = await _serviceTDengine.GetLastAsync<PregnancyCommonHeartRateModel>(imeiDel);
  278. var phr = await _serviceTDengine.GetBySerialNoAsync<PregnancyHeartRateModel>(imeiDel, 7);
  279. var phrFromFreqstatus = phr.Where(i => i.LastUpdate >= phrFreqstatus.LastUpdate).ToList();
  280. // 高频数据
  281. var phrInFreqstatus = GetFreqPregnancyHeartRate(phrFromFreqstatus, highFreqSampleInterval)
  282. .OrderByDescending(i => i.LastUpdate).ToList();
  283. if (phrInFreqstatus.Count > stopHighFreqSampleCount)
  284. {
  285. // 取最后 stopHighFreqSampleCount 条高频数据
  286. phrInFreqstatus = phrInFreqstatus
  287. .OrderByDescending(i => i.LastUpdate)
  288. .Take(stopHighFreqSampleCount).ToList(); // 计算最后12条
  289. var avgPhr = phrInFreqstatus
  290. .Select(i => i.PregnancyHeartRate).Average();
  291. var FreqStatsEnd = phrInFreqstatus.First().LastUpdate;
  292. _logger.LogInformation($"延时计算,{imeiDel} 高频状态持续{(FreqStatsEnd - phrFreqstatus!.LastUpdate).TotalSeconds} 秒,统计周期:{phrFreqstatus!.LastUpdate.ToString("yyyy-MM-dd HH:mm:ss")}----{FreqStatsEnd.ToString("yyyy-MM-dd HH:mm:ss")},高频心率平均值:{(int)avgPhr},将下发指令");
  293. //await SaveAndPushFetalHeartRateAsync(triggerHeartRate, commonPHR, upperAlarmThreshold, lowerAlarmThreshold, avgPhr, DateTimeUtil.ConvertToTimeStamp(phrFreqstatus!.LastUpdate).ToString(), phrFreqstatus!.LastUpdate, FreqStatsEnd);
  294. triggerHeartRate.HeartRate = (int)avgPhr;
  295. await SaveAndPushFetalHeartRateEndFreqHeartRateAsync(triggerHeartRate, commonPHR, upperAlarmThreshold, lowerAlarmThreshold, DateTimeUtil.ConvertToTimeStamp(phrFreqstatus!.LastUpdate).ToString(), phrFreqstatus!.LastUpdate, FreqStatsEnd);
  296. }
  297. else
  298. {
  299. _logger.LogWarning($"{imeiDel} 高频心率的数据不大于{stopHighFreqSampleCount}条,不进行高频数据的胎心计算");
  300. }
  301. // 删除高频状态的首条记录
  302. await _deviceCacheMgr.DelPregnancyHeartRateFreqStatusAsync(imeiDel);
  303. }
  304. else
  305. {
  306. _logger.LogWarning($"{imeiDel} 超过12条连续正常,高频不停止,暂不处理");
  307. }
  308. }
  309. else
  310. {
  311. _logger.LogWarning($"{imeiDel} 胎心监测功能没有启动");
  312. }
  313. }
  314. }
  315. }
  316. }
  317. // 延时计算常规胎心数据
  318. else if (key.Contains("health_monitor/schedule_push/cal_normalphr_fetal_heart_rate/imei/"))
  319. {
  320. var triggerValue = (JObject)JsonConvert.DeserializeObject(e.PrevKv.Value.ToStringUtf8())!;
  321. var trigger = triggerValue["trigger"]?.ToString();
  322. if (!string.IsNullOrEmpty(trigger))
  323. {
  324. var triggerHeartRate = JsonConvert.DeserializeObject<HisGpsHeartRate>(trigger);
  325. using (_logger.BeginScope(new Dictionary<string, object> { ["RequestId"] = triggerHeartRate?.MessageId! }))
  326. {
  327. var watchConfig = await _deviceCacheMgr.GetGpsDeviceWatchConfigCacheObjectBySerialNoAsync(imeiDel, "0067");
  328. _logger.LogInformation($"{imeiDel}延迟常规胎心数据产生的胎心数据");
  329. var isFetalHeartEnable = watchConfig != null && (int)watchConfig["enabled"]! == 1;
  330. if (isFetalHeartEnable)
  331. {
  332. var commonPHR = await _serviceTDengine.GetLastAsync<PregnancyCommonHeartRateModel>(imeiDel);
  333. if (commonPHR!=null)
  334. {
  335. var highFreqSampleInterval = (int)watchConfig!["highFreqSampleInterval"]! >= 60 ? (int)watchConfig!["highFreqSampleInterval"]! : 60;
  336. await CalculateNormalFetalHeartRateIntervalAsync(triggerHeartRate!, commonPHR, highFreqSampleInterval);
  337. }
  338. else
  339. {
  340. _logger.LogWarning($"{imeiDel} 胎心数据建模中...");
  341. }
  342. }
  343. else
  344. {
  345. _logger.LogWarning($"{imeiDel} 胎心监测功能没有启动");
  346. }
  347. }
  348. }
  349. }
  350. else if (key.Contains("health_monitor/schedule_push/cal_fetal_heart_rate/imei/"))
  351. {
  352. /**
  353. var triggerValue= (JObject)JsonConvert.DeserializeObject(e.PrevKv.Value.ToStringUtf8())!;
  354. var trigger = triggerValue["trigger"]?.ToString();
  355. if (!string.IsNullOrEmpty(trigger))
  356. {
  357. var triggerHeartRate= JsonConvert.DeserializeObject<HisGpsHeartRate>(trigger);
  358. using (_logger.BeginScope(new Dictionary<string, object> { ["RequestId"] = triggerHeartRate?.MessageId! }))
  359. {
  360. var watchConfig = await _deviceCacheMgr.GetGpsDeviceWatchConfigCacheObjectBySerialNoAsync(imeiDel, "0067");
  361. _logger.LogInformation($"触发常规心率计算胎心");
  362. var isFetalHeartEnable = watchConfig != null && (int)watchConfig["enabled"]! == 1;
  363. if (isFetalHeartEnable)
  364. {
  365. // 告警上限阀值
  366. var upperAlarmThreshold = (int)watchConfig!["upperAlarmThreshold"]!;
  367. // 告警下限阀值
  368. var lowerAlarmThreshold = (int)watchConfig["lowerAlarmThreshold"]!;
  369. // 高频心率采样间隔 highFreqSampleInterval = highFreqSampleInterval+5,增加5秒兼容(最小highFreqSampleInterval=60)
  370. var highFreqSampleInterval = (int)watchConfig!["highFreqSampleInterval"]! >= 60 ? (int)watchConfig!["highFreqSampleInterval"]! + 5 : 60;
  371. var commonPHR = await _serviceTDengine.GetLastAsync<PregnancyCommonHeartRateModel>(imeiDel);
  372. // 最后一条孕妇心率
  373. var lastPhr = await _serviceTDengine.GetLastAsync<PregnancyHeartRateModel>(imeiDel);
  374. var isNormalHeartRate = triggerHeartRate?.MessageId == lastPhr.MessageId;
  375. // 判断最后一条孕妇心率与解析器的触发心率是否一致
  376. if (isNormalHeartRate)
  377. {
  378. //最后一条孕妇心率与解析器的触发心率一致
  379. HisGpsHeartRate heartRate = new()
  380. {
  381. CreateTime = lastPhr.CreateTime,
  382. DeviceKey = lastPhr.DeviceKey,
  383. HeartRate = lastPhr.PregnancyHeartRate,
  384. HeartRateId = lastPhr.PregnancyHeartRateId,
  385. IsDisplay = lastPhr.IsDisplay ? 1 : 0,
  386. MessageId = lastPhr.MessageId,
  387. LastUpdate = lastPhr.LastUpdate,
  388. Method = lastPhr.Method,
  389. PersonId = lastPhr.PersonId,
  390. Serialno = lastPhr.SerialNumber
  391. };
  392. var intervalFHR = 15;
  393. await CalculateNormalFetalHeartRateAsync(heartRate, upperAlarmThreshold, lowerAlarmThreshold, intervalFHR, commonPHR);
  394. }
  395. // 最后一条孕妇心率与解析器的触发心率不一致,触发心率是高频心率的首条
  396. else
  397. {
  398. var phr = await _serviceTDengine.GetBySerialNoAsync<PregnancyHeartRateModel>(imeiDel, 7);
  399. var phrFreqstatus = await _deviceCacheMgr.GetPregnancyHeartRateFreqStatusAsync(imeiDel);
  400. if (phrFreqstatus != null)
  401. {
  402. #region 高频缓存
  403. // 高频首条是触发心率,30秒后,高频第二条已经写入首条高频缓存
  404. var diffCount = phr.Where(i => i.CreateTime >= phrFreqstatus.CreateTime && i.CreateTime <= lastPhr.CreateTime)
  405. .OrderByDescending(i => i.CreateTime);
  406. _logger.LogInformation($"{imeiDel} 存在高频缓存,高频心率首条记录${phrFreqstatus.MessageId},高频首条到当前的MSG ID{string.Join(",",diffCount.Select(i=>i.MessageId))}");
  407. #endregion
  408. }
  409. // 不在高频状态状态,但触发孕妇心率与最后一条孕妇心率不一致,
  410. // 常规心率数据是批量上报,需要计算每条心率对应的胎心数据
  411. else
  412. {
  413. _logger.LogInformation($"{imeiDel} 常规心率数据是批量上报,需要计算每条心率对应的胎心数据");
  414. #region 计算每条心率对应的胎心数据
  415. phr = phr.OrderByDescending(i => i.LastUpdate).ToList();
  416. // 获取高频数据
  417. var freqCollection = new List<PregnancyHeartRateModel>();
  418. PregnancyHeartRateModel? previousItem = null;
  419. foreach (var item in phr)
  420. {
  421. if (previousItem != null)
  422. {
  423. var timeNextDiff = (previousItem!.LastUpdate - item.LastUpdate).TotalSeconds;
  424. if (timeNextDiff <= highFreqSampleInterval)
  425. {
  426. freqCollection.Add(item);
  427. }
  428. }
  429. previousItem = item;
  430. }
  431. //去除高频
  432. foreach (var item in freqCollection)
  433. {
  434. phr.Remove(item);
  435. }
  436. // 排序并过滤高频数据
  437. //var previousItem = phr.OrderByDescending(i => i.LastUpdate).ToList().First();
  438. //phr = phr.Skip(1)
  439. // .Where(item =>
  440. // {
  441. // var timeNextDiff = (previousItem!.LastUpdate - item.LastUpdate).TotalSeconds;
  442. // previousItem = item;
  443. // return timeNextDiff > highFreqSampleInterval;
  444. // })
  445. // .Prepend(previousItem)
  446. // .OrderByDescending(i => i.LastUpdate)
  447. // .ToList();
  448. var calFhrTasks = phr.Where(p => p.LastUpdate >= triggerHeartRate!.LastUpdate)
  449. .Select( async p =>
  450. {
  451. HisGpsHeartRate heartRate = new()
  452. {
  453. CreateTime = p.CreateTime,
  454. DeviceKey = p.DeviceKey,
  455. HeartRate = p.PregnancyHeartRate,
  456. HeartRateId = p.PregnancyHeartRateId,
  457. IsDisplay = p.IsDisplay ? 1 : 0,
  458. MessageId = p.MessageId,
  459. LastUpdate = p.LastUpdate,
  460. Method = p.Method,
  461. PersonId = p.PersonId,
  462. Serialno = p.SerialNumber
  463. };
  464. var intervalFHR = 15;
  465. await CalculateNormalFetalHeartRateAsync(heartRate, upperAlarmThreshold, lowerAlarmThreshold, intervalFHR, commonPHR);
  466. });
  467. await Task.WhenAll(calFhrTasks);
  468. #endregion
  469. }
  470. }
  471. }
  472. else
  473. {
  474. _logger.LogWarning($"{imeiDel} 胎心监测功能没有开启");
  475. }
  476. }
  477. }
  478. else
  479. {
  480. _logger.LogWarning($"{imeiDel} trigger is not set");
  481. }
  482. */
  483. }
  484. // 胎动计算
  485. //health_monitor/schedule_push/cal_fetal_movement/imei/
  486. else if (key.Contains("health_monitor/schedule_push/cal_fetal_movement/imei/"))
  487. {
  488. #region 胎动延时计算
  489. /**
  490. using (_logger.BeginScope(new Dictionary<string, object> { ["RequestId"] = $"FM-{imeiDel}-{DateTime.Now.ToString("yyyyMMddHHmmss")}" }))
  491. {
  492. var watchConfig = await _deviceCacheMgr.GetGpsDeviceWatchConfigCacheObjectBySerialNoAsync(imeiDel, "0067");
  493. _logger.LogInformation($"触发胎动计算,设备配置{JsonConvert.SerializeObject(watchConfig)}");
  494. var isFetalHeartEnable = watchConfig != null && (int)watchConfig["enabled"]! == 1;
  495. if (isFetalHeartEnable)
  496. {
  497. var edoc = DateTimeUtil.ToDateTime(watchConfig!["EDOC"]!.ToString());
  498. // 已经建模
  499. var commonPHR = await _serviceTDengine.GetLastAsync<PregnancyCommonHeartRateModel>(imeiDel);
  500. if (commonPHR != null)
  501. {
  502. var phr = await _serviceTDengine.GetBySerialNoAsync<PregnancyHeartRateModel>(imeiDel, 7);
  503. _logger.LogInformation($"{imeiDel} 计算胎动数据 ");
  504. var fmNow = DateTime.Now;
  505. // 两小时前
  506. var fmNowSubtract = fmNow.AddMinutes(-fmNow.Minute).AddSeconds(-fmNow.Second).AddMilliseconds(-fmNow.Millisecond);
  507. var fetalMovementSampleTime = DateTimeUtil.ConvertToTimeStamp(fmNowSubtract).ToString()[..10];
  508. // 统计开始时间
  509. var statStartTime = fmNowSubtract.AddHours(-2);
  510. // 统计结束时间
  511. var statEndTime = fmNowSubtract;
  512. var isFetalMovementExisted = await _deviceCacheMgr.FetalMovementIsExistedAsync(imeiDel, fetalMovementSampleTime);
  513. _logger.LogInformation($"{imeiDel} 胎动记录{isFetalMovementExisted},数据采样时间:{fetalMovementSampleTime}|{fmNowSubtract.ToString("yyyy-MM-dd HH:mm:ss")}, 周期:{statStartTime}-{statEndTime} 开始");
  514. if (!isFetalMovementExisted)
  515. {
  516. /// 开始计算
  517. var phrRange = phr.Where(i => i.LastUpdate >= statStartTime && i.LastUpdate <= statEndTime)
  518. .OrderByDescending(i => i.LastUpdate)
  519. .Select(i => i.LastUpdate)
  520. .ToList();
  521. // 判断是否有持续佩戴
  522. if (phrRange.Count >= 2)
  523. {
  524. // 读取胎心数据
  525. GeneralParam param = new()
  526. {
  527. Filters = new List<QueryFilterCondition>
  528. {
  529. new ()
  530. {
  531. Key=nameof(HisGpsFetalHeartRate.Serialno),
  532. Value=imeiDel,
  533. ValueType=QueryValueTypeEnum.String,
  534. Operator=QueryOperatorEnum.Equal
  535. },
  536. //new ()
  537. //{
  538. // Key=nameof(HisGpsFetalHeartRate.SampleTime),
  539. // Value=sampleTime,
  540. // ValueType=QueryValueTypeEnum.String,
  541. // Operator=QueryOperatorEnum.GreaterEqual
  542. //},
  543. },
  544. OrderBys = new List<OrderByCondition>
  545. {
  546. new (){
  547. IsDesc=true,
  548. Key=nameof(HisGpsFetalHeartRate.SampleTime)
  549. }
  550. }
  551. };
  552. var fetalHeartRateIsAbnormal = 0;
  553. var fhr = await _hisFetalHeartApiClient.GetFirstAsync(param, imeiDel[^2..], null, new RequestHeader { RequestId = Guid.NewGuid().ToString("D") });
  554. // 胎心数据时间与胎动时间一致
  555. var time = long.Parse(fhr.SampleTime.Length < 13 ? fhr.SampleTime.PadRight(13, '0') : fhr.SampleTime);
  556. var fhrSampleTime = DateTimeUtil.GetDateTimeFromUnixTimeMilliseconds(time);
  557. // 胎心数据时间与胎动时间一致,最后一条胎心数据与胎动数据的小时差不大于2
  558. if ((DateTime.Now.Hour - fhrSampleTime.Hour) <= 2)
  559. {
  560. var duringMins = Math.Abs((phrRange.First() - phrRange.Last()).TotalMinutes);
  561. //在餐后时间段(8:00~10:00,12:00~14:00,18:00~20:00,22:00~24:00)取中间值。其他时间段取正常起始值
  562. bool isInTimeRanges = IsLastUpdateInTimeRanges(phrRange.First());
  563. int pregnancyWeeks = (DateTime.Now - edoc.AddDays(-280)).Days / 7;
  564. if (pregnancyWeeks >= 12 && pregnancyWeeks <= 50)
  565. {
  566. var fetalMovementMap = _mgrFetalMovementNormalValueRangeCache.GetFetalMovements();
  567. var fetalMovementMapValue = isInTimeRanges ? fetalMovementMap
  568. .Where(i =>
  569. i.PregnancyPeriod![0] <= pregnancyWeeks &&
  570. i.PregnancyPeriod[1] >= pregnancyWeeks)
  571. .Select(i => i.MedianMovement)
  572. .FirstOrDefault()
  573. :
  574. fetalMovementMap
  575. .Where(i =>
  576. i.PregnancyPeriod![0] <= pregnancyWeeks &&
  577. i.PregnancyPeriod[1] >= pregnancyWeeks)
  578. .Select(i => i.InitialMovement)
  579. .FirstOrDefault()
  580. ;
  581. var fetalMovementValue = (fetalMovementMapValue * duringMins * 2) / 120;
  582. // 四舍五入
  583. var fetalMovement = (int)Math.Round(fetalMovementValue, 0, MidpointRounding.AwayFromZero);
  584. // _logger.LogInformation($"{imeiDel} segmentCountFMIndex: {i} -- fetalMovementSampleTime:{fetalMovementSampleTime}|{midNight.AddHours(2 * i).ToString("yyyy-MM-dd HH:mm:ss")} -- statStartTime: {statStartTime} -- statEndTime: {statEndTime}-- isFetalMovementExisted: {isFetalMovementExisted} ");
  585. #region 生理健康与胎动的关系
  586. /// (步数)运动步数超过1500步则加1;
  587. /// (体温)低烧则胎动减1,高烧胎动减2;低烧是37.3~38.5,38.6以上是高烧。
  588. /// (血压)血压收缩压大于160则加1。
  589. /// (心理)心理压力高加2,压力中加1;
  590. var fetalMovementStepVar = 0;
  591. var fetalMovementTempVar = 0;
  592. var fetalMovementBpVar = 0;
  593. var fetalMovementPpVar = 0;
  594. var fetalMovementFhrVar = 0;
  595. // 步数
  596. if (true)
  597. {
  598. var step = await _personCacheMgr.GetStepPeriodicityAsync(imeiDel);
  599. if (step != null)
  600. {
  601. if (DateTime.Now.Hour - ((DateTime)step?.LastUpdate!).Hour <= 2)
  602. {
  603. if (step.Steps > 1500) fetalMovementStepVar = 1;
  604. }
  605. else
  606. {
  607. _logger.LogWarning($"{imeiDel} 周期步数 时间无效");
  608. }
  609. }
  610. else
  611. {
  612. _logger.LogWarning($"{imeiDel} 周期步数无数据");
  613. }
  614. }
  615. // 体温
  616. if (true)
  617. {
  618. var temp = await _personCacheMgr.GetTemperaturePeriodicityAsync(imeiDel);
  619. if (temp != null)
  620. {
  621. if (DateTime.Now.Hour - ((DateTime)temp?.LastUpdate!).Hour <= 2)
  622. {
  623. // 中烧
  624. if (temp.Temperature >= 37.3M && temp.Temperature <= 38.5M)
  625. {
  626. fetalMovementTempVar = -1;
  627. }
  628. // 高烧
  629. if (temp.Temperature >= 38.6M)
  630. {
  631. fetalMovementTempVar = -2;
  632. }
  633. }
  634. else
  635. {
  636. _logger.LogWarning($"{imeiDel} 周期体温 时间无效");
  637. }
  638. }
  639. else
  640. {
  641. _logger.LogWarning($"{imeiDel} 周期体温无数据");
  642. }
  643. }
  644. // 血压
  645. if (true)
  646. {
  647. var bp = await _personCacheMgr.GetBloodPressPeriodicityAsync(imeiDel);
  648. if (bp != null)
  649. {
  650. if (DateTime.Now.Hour - ((DateTime)bp?.LastUpdate!).Hour <= 2)
  651. {
  652. if (bp.SystolicValue > 160)
  653. {
  654. fetalMovementBpVar = 1;
  655. }
  656. }
  657. else
  658. {
  659. _logger.LogWarning($"{imeiDel} 周期血压 时间无效");
  660. }
  661. }
  662. else
  663. {
  664. _logger.LogWarning($"{imeiDel} 周期血压无数据");
  665. }
  666. }
  667. // 心理
  668. if (true)
  669. {
  670. //-1 不处理,
  671. //0 正常,
  672. //1 轻度,
  673. //2 中度;
  674. //3 重度;
  675. GeneralParam psychResultParam = new()
  676. {
  677. Filters = new List<QueryFilterCondition>
  678. {
  679. new ()
  680. {
  681. Key=nameof(HisGpsPsychResult.Serialno),
  682. Value=imeiDel,
  683. ValueType=QueryValueTypeEnum.String,
  684. Operator=QueryOperatorEnum.Equal
  685. },
  686. //new ()
  687. //{
  688. // Key=nameof(HisGpsFetalHeartRate.SampleTime),
  689. // Value=sampleTime,
  690. // ValueType=QueryValueTypeEnum.String,
  691. // Operator=QueryOperatorEnum.GreaterEqual
  692. //},
  693. },
  694. OrderBys = new List<OrderByCondition>
  695. {
  696. new (){
  697. IsDesc=true,
  698. Key=nameof(HisGpsPsychResult.Serialno)
  699. }
  700. }
  701. };
  702. var psych = await _hisPsychResultApiClient.GetFirstAsync(psychResultParam, imeiDel[^2..], null, new RequestHeader { RequestId = Guid.NewGuid().ToString("D") });
  703. if (psych != null)
  704. {
  705. if (psych?.StressScore == 2)
  706. {
  707. fetalMovementPpVar = 1;
  708. }
  709. if (psych?.StressScore == 3)
  710. {
  711. fetalMovementPpVar = 2;
  712. }
  713. }
  714. else
  715. {
  716. _logger.LogWarning($"{imeiDel} 周期心理压力无数据");
  717. }
  718. }
  719. #endregion
  720. #region 胎心与胎动的关系
  721. /// 胎心值过缓时,则胎动数量减1;胎心值过速时,则胎动也加1。
  722. /// 此值允许在上限值上继续增加,在下限值上继续减少,最小值为0。
  723. /// 告警上限阀值
  724. //1表示偏高;2表示偏低
  725. if (true)
  726. {
  727. if (fhr.IsAbnormal == 2)
  728. {
  729. fetalMovementPpVar = -1;
  730. }
  731. if (fhr.IsAbnormal == 1)
  732. {
  733. fetalMovementPpVar = 1;
  734. }
  735. }
  736. #endregion
  737. _logger.LogInformation($"{imeiDel} 原始胎动值:{fetalMovementValue}, 步数参数变动值:{fetalMovementStepVar},体温参数变动值:{fetalMovementTempVar},血压参数变动值:{fetalMovementBpVar},心理压力参数变动值:{fetalMovementPpVar},胎心参数变动值:{fetalMovementFhrVar}");
  738. fetalMovementValue = fetalMovementValue + fetalMovementStepVar + fetalMovementTempVar + fetalMovementBpVar + fetalMovementPpVar + fetalMovementFhrVar;
  739. _logger.LogInformation($"{imeiDel} 孕周:{pregnancyWeeks},胎动数据采样时间:{fetalMovementSampleTime}|{fmNowSubtract.ToString("yyyy-MM-dd HH:mm:ss")}, 采样周期:{statStartTime}-{statEndTime}, 原始胎动值:{fetalMovementMapValue}, 佩戴时间 :{duringMins}|{phrRange.Last()}-{phrRange.First()}, 胎动计算值:{fetalMovementValue}, 胎动最终值:{fetalMovement} 已完成.");
  740. // 获取胎心数据状态与胎动数据状态一致
  741. //etalHeartRateIsAbnormal= fhr.IsAbnormal;
  742. var feltalMovementIsAbnormal = fetalHeartRateIsAbnormal;
  743. await _serviceIotApi.SetFetalMovementConfig(imeiDel, fetalMovement, fetalMovementSampleTime, feltalMovementIsAbnormal);
  744. // 保存到MySQL数据库
  745. HisGpsFetalMovement fm = new()
  746. {
  747. FetalMovementId = Guid.NewGuid().ToString("D"),
  748. PersonId = commonPHR!.PersonId,
  749. Serialno = imeiDel,
  750. CreateTime = DateTime.Now,
  751. IsAbnormal = feltalMovementIsAbnormal,
  752. FetalMovementValue = fetalMovement,
  753. SampleTime = fetalMovementSampleTime,
  754. Method = 1,
  755. IsDisplay = 1,
  756. DeviceKey = commonPHR!.DeviceKey
  757. };
  758. await _hisFetalMovementApiClient.AddAsync(fm).ConfigureAwait(false);
  759. var device = await _deviceCacheMgr.GetDeviceBySerialNoAsync(imeiDel).ConfigureAwait(false);
  760. var fmMsgId = $"{imeiDel}-{fetalMovementSampleTime}-{Guid.NewGuid().ToString("D")[^3..]}";
  761. var fmMsgTime = DateTimeUtil.GetDateTimeFromUnixTimeMilliseconds(long.Parse(fetalMovementSampleTime.Length < 13 ? fetalMovementSampleTime.PadRight(13, '0') : fetalMovementSampleTime)).ToString("yyyy-MM-dd HH:mm:ss");
  762. // 胎动数据推送到第三方
  763. var topic = "topic.push.third";
  764. var fmThridMsg = new
  765. {
  766. messageId = fmMsgId,
  767. topic = topic,
  768. time = fmMsgTime,
  769. data = new
  770. {
  771. imei = imeiDel,
  772. value = fetalMovement,
  773. isAbnormal = feltalMovementIsAbnormal,
  774. type = "fetalMovement"
  775. }
  776. };
  777. await _serviceMqProcess.ProcessIMEIEventMessageAsync(fmMsgId, topic, 31, fmThridMsg).ConfigureAwait(false);
  778. // 胎动数据推送到微信
  779. if (feltalMovementIsAbnormal != 0)
  780. {
  781. topic = "topic.push.wx";
  782. var fmMsg = new
  783. {
  784. messageId = Guid.NewGuid().ToString("D"),
  785. topic = topic,
  786. time = DateTimeUtil.GetDateTimeFromUnixTimeMilliseconds(long.Parse(fetalMovementSampleTime.Length < 13 ? fetalMovementSampleTime.PadRight(13, '0') : fetalMovementSampleTime)).ToString("yyyy-MM-dd HH:mm:ss"),
  787. data = new
  788. {
  789. deviceId = device?.DeviceId,
  790. imei = imeiDel,
  791. alarmTypeId = 12,
  792. alarmDeviceName = imeiDel,
  793. alarmRemarks = JsonConvert.SerializeObject(new { fetalMovementValue = fetalMovement, isAbnormal = feltalMovementIsAbnormal }),
  794. address = string.Empty,
  795. deviceKey = device?.DeviceId
  796. }
  797. };
  798. await _serviceMqProcess.ProcessIMEIEventMessageAsync(fmMsgId, topic, fmMsg).ConfigureAwait(false);
  799. }
  800. // 设置入库缓存记录
  801. await _deviceCacheMgr.SetFetalMovementAsync(imeiDel, fetalMovementSampleTime, fm);
  802. }
  803. else
  804. {
  805. _logger.LogWarning($"{imeiDel} 孕周 {pregnancyWeeks},超出胎动计算范围");
  806. }
  807. }
  808. else
  809. {
  810. _logger.LogWarning($"{imeiDel} 最后一条胎心数据与胎动数据的小时差大于2,不计算胎动数据");
  811. }
  812. }
  813. else
  814. {
  815. _logger.LogInformation($"{imeiDel} 胎动记录{isFetalMovementExisted},数据采样时间:{fetalMovementSampleTime}|{fmNowSubtract.ToString("yyyy-MM-dd HH:mm:ss")}, 周期:{statStartTime}-{statEndTime} 不足两条,不能判断是否持续佩戴");
  816. }
  817. }
  818. else
  819. {
  820. _logger.LogInformation($"{imeiDel} 胎动记录{isFetalMovementExisted},数据采样时间:{fetalMovementSampleTime}|{fmNowSubtract.ToString("yyyy-MM-dd HH:mm:ss")}, 周期:{statStartTime}-{statEndTime} 已处理");
  821. }
  822. }
  823. }
  824. }
  825. */
  826. #endregion
  827. #region 胎动延时计算(使用线程再延时)
  828. /**
  829. using (_logger.BeginScope(new Dictionary<string, object> { ["RequestId"] = $"FM-{imeiDel}-{DateTime.Now.ToString("yyyyMMddHHmmss")}" }))
  830. {
  831. var watchConfig = await _deviceCacheMgr.GetGpsDeviceWatchConfigCacheObjectBySerialNoAsync(imeiDel, "0067");
  832. _logger.LogInformation($"触发胎动计算,设备配置{JsonConvert.SerializeObject(watchConfig)}");
  833. var isFetalHeartEnable = watchConfig != null && (int)watchConfig["enabled"]! == 1;
  834. var highFreqSampleInterval = (int)watchConfig!["highFreqSampleInterval"]! >= 60 ? (int)watchConfig!["highFreqSampleInterval"]! : 60;
  835. if (isFetalHeartEnable)
  836. {
  837. var edoc = DateTimeUtil.ToDateTime(watchConfig!["EDOC"]!.ToString());
  838. // 已经建模
  839. var commonPHR = await _serviceTDengine.GetLastAsync<PregnancyCommonHeartRateModel>(imeiDel);
  840. if (commonPHR != null)
  841. {
  842. var phr = await _serviceTDengine.GetBySerialNoAsync<PregnancyHeartRateModel>(imeiDel, 7);
  843. _logger.LogInformation($"{imeiDel} 计算胎动数据 ");
  844. var fmNow = DateTime.Now;
  845. // 两小时前
  846. var fmNowSubtract = fmNow.AddMinutes(-fmNow.Minute).AddSeconds(-fmNow.Second).AddMilliseconds(-fmNow.Millisecond);
  847. var fetalMovementSampleTime = DateTimeUtil.ConvertToTimeStamp(fmNowSubtract).ToString()[..10];
  848. // 统计开始时间
  849. var statStartTime = fmNowSubtract.AddHours(-2);
  850. // 统计结束时间
  851. var statEndTime = fmNowSubtract;
  852. Thread thread = new(async () => {
  853. try
  854. {
  855. #region 休眠65分钟
  856. var startTime = DateTime.Now;
  857. //var highFreqSampleInterval2 = (int)watchConfig!["highFreqSampleInterval"]!+5;
  858. var highFreqSampleInterval2 = 65*60;
  859. var during = TimeSpan.FromSeconds(highFreqSampleInterval2);
  860. while (true)
  861. {
  862. if (DateTime.Now - startTime > during)
  863. {
  864. break;
  865. }
  866. await Task.Delay(TimeSpan.FromSeconds(1));
  867. }
  868. #endregion
  869. //var daysPhr = await _serviceTDengine.GetBySerialNoAsync<PregnancyHeartRateModel>(imeiDel,1);
  870. //// 去除高频
  871. //var normalPhr = GetNonFreqPregnancyHeartRate(daysPhr, highFreqSampleInterval).OrderByDescending(i => i.LastUpdate);
  872. //// 常规最后一条
  873. //var lastNormalPhr = normalPhr.First();
  874. //statEndTime = lastNormalPhr.LastUpdate;
  875. PregnancyHeartRateModel lastPhr = await _serviceTDengine.GetLastAsync<PregnancyHeartRateModel>(imeiDel);
  876. statEndTime = lastPhr.LastUpdate;
  877. #region 计算
  878. var isFetalMovementExisted = await _deviceCacheMgr.FetalMovementIsExistedAsync(imeiDel, fetalMovementSampleTime);
  879. _logger.LogInformation($"{imeiDel} 胎动记录{isFetalMovementExisted},数据采样时间:{fetalMovementSampleTime}|{fmNowSubtract.ToString("yyyy-MM-dd HH:mm:ss")}, 周期:{statStartTime}-{statEndTime} 开始");
  880. if (!isFetalMovementExisted)
  881. {
  882. /// 开始计算
  883. var phrRange = phr.Where(i => i.LastUpdate >= statStartTime && i.LastUpdate <= statEndTime)
  884. .OrderByDescending(i => i.LastUpdate)
  885. .Select(i => i.LastUpdate)
  886. .ToList();
  887. // 判断是否有持续佩戴
  888. if (phrRange.Count >= 2)
  889. {
  890. // 读取胎心数据
  891. GeneralParam param = new()
  892. {
  893. Filters = new List<QueryFilterCondition>
  894. {
  895. new ()
  896. {
  897. Key=nameof(HisGpsFetalHeartRate.Serialno),
  898. Value=imeiDel,
  899. ValueType=QueryValueTypeEnum.String,
  900. Operator=QueryOperatorEnum.Equal
  901. },
  902. //new ()
  903. //{
  904. // Key=nameof(HisGpsFetalHeartRate.SampleTime),
  905. // Value=sampleTime,
  906. // ValueType=QueryValueTypeEnum.String,
  907. // Operator=QueryOperatorEnum.GreaterEqual
  908. //},
  909. },
  910. OrderBys = new List<OrderByCondition>
  911. {
  912. new (){
  913. IsDesc=true,
  914. Key=nameof(HisGpsFetalHeartRate.SampleTime)
  915. }
  916. }
  917. };
  918. var fetalHeartRateIsAbnormal = 0;
  919. var fhr = await _hisFetalHeartApiClient.GetFirstAsync(param, imeiDel[^2..], null, new RequestHeader { RequestId = Guid.NewGuid().ToString("D") });
  920. // 胎心数据时间与胎动时间一致
  921. var time = long.Parse(fhr.SampleTime.Length < 13 ? fhr.SampleTime.PadRight(13, '0') : fhr.SampleTime);
  922. var fhrSampleTime = DateTimeUtil.GetDateTimeFromUnixTimeMilliseconds(time);
  923. // 胎心数据时间与胎动时间一致,最后一条胎心数据与胎动数据的小时差不大于2
  924. if ((DateTime.Now.Hour - fhrSampleTime.Hour) <= 4)
  925. {
  926. var duringMins = Math.Abs((phrRange.First() - phrRange.Last()).TotalMinutes);
  927. //在餐后时间段(8:00~10:00,12:00~14:00,18:00~20:00,22:00~24:00)取中间值。其他时间段取正常起始值
  928. bool isInTimeRanges = IsLastUpdateInTimeRanges(phrRange.First());
  929. int pregnancyWeeks = (DateTime.Now - edoc.AddDays(-280)).Days / 7;
  930. if (pregnancyWeeks >= 12 && pregnancyWeeks <= 50)
  931. {
  932. var fetalMovementMap = _mgrFetalMovementNormalValueRangeCache.GetFetalMovements();
  933. var fetalMovementMapValue = isInTimeRanges ? fetalMovementMap
  934. .Where(i =>
  935. i.PregnancyPeriod![0] <= pregnancyWeeks &&
  936. i.PregnancyPeriod[1] >= pregnancyWeeks)
  937. .Select(i => i.MedianMovement)
  938. .FirstOrDefault()
  939. :
  940. fetalMovementMap
  941. .Where(i =>
  942. i.PregnancyPeriod![0] <= pregnancyWeeks &&
  943. i.PregnancyPeriod[1] >= pregnancyWeeks)
  944. .Select(i => i.InitialMovement)
  945. .FirstOrDefault()
  946. ;
  947. var fetalMovementValue = (fetalMovementMapValue * duringMins * 2) / 120;
  948. // 四舍五入
  949. var fetalMovement = (int)Math.Round(fetalMovementValue, 0, MidpointRounding.AwayFromZero);
  950. // _logger.LogInformation($"{imeiDel} segmentCountFMIndex: {i} -- fetalMovementSampleTime:{fetalMovementSampleTime}|{midNight.AddHours(2 * i).ToString("yyyy-MM-dd HH:mm:ss")} -- statStartTime: {statStartTime} -- statEndTime: {statEndTime}-- isFetalMovementExisted: {isFetalMovementExisted} ");
  951. #region 生理健康与胎动的关系
  952. /// (步数)运动步数超过1500步则加1;
  953. /// (体温)低烧则胎动减1,高烧胎动减2;低烧是37.3~38.5,38.6以上是高烧。
  954. /// (血压)血压收缩压大于160则加1。
  955. /// (心理)心理压力高加2,压力中加1;
  956. var fetalMovementStepVar = 0;
  957. var fetalMovementTempVar = 0;
  958. var fetalMovementBpVar = 0;
  959. var fetalMovementPpVar = 0;
  960. var fetalMovementFhrVar = 0;
  961. // 步数
  962. if (true)
  963. {
  964. var step = await _personCacheMgr.GetStepPeriodicityAsync(imeiDel);
  965. if (step != null)
  966. {
  967. if (DateTime.Now.Hour - ((DateTime)step?.LastUpdate!).Hour <= 2)
  968. {
  969. if (step.Steps > 1500) fetalMovementStepVar = 1;
  970. }
  971. else
  972. {
  973. _logger.LogWarning($"{imeiDel} 周期步数 时间无效");
  974. }
  975. }
  976. else
  977. {
  978. _logger.LogWarning($"{imeiDel} 周期步数无数据");
  979. }
  980. }
  981. // 体温
  982. if (true)
  983. {
  984. var temp = await _personCacheMgr.GetTemperaturePeriodicityAsync(imeiDel);
  985. if (temp != null)
  986. {
  987. if (DateTime.Now.Hour - ((DateTime)temp?.LastUpdate!).Hour <= 2)
  988. {
  989. // 中烧
  990. if (temp.Temperature >= 37.3M && temp.Temperature <= 38.5M)
  991. {
  992. fetalMovementTempVar = -1;
  993. }
  994. // 高烧
  995. if (temp.Temperature >= 38.6M)
  996. {
  997. fetalMovementTempVar = -2;
  998. }
  999. }
  1000. else
  1001. {
  1002. _logger.LogWarning($"{imeiDel} 周期体温 时间无效");
  1003. }
  1004. }
  1005. else
  1006. {
  1007. _logger.LogWarning($"{imeiDel} 周期体温无数据");
  1008. }
  1009. }
  1010. // 血压
  1011. if (true)
  1012. {
  1013. var bp = await _personCacheMgr.GetBloodPressPeriodicityAsync(imeiDel);
  1014. if (bp != null)
  1015. {
  1016. if (DateTime.Now.Hour - ((DateTime)bp?.LastUpdate!).Hour <= 2)
  1017. {
  1018. if (bp.SystolicValue > 160)
  1019. {
  1020. fetalMovementBpVar = 1;
  1021. }
  1022. }
  1023. else
  1024. {
  1025. _logger.LogWarning($"{imeiDel} 周期血压 时间无效");
  1026. }
  1027. }
  1028. else
  1029. {
  1030. _logger.LogWarning($"{imeiDel} 周期血压无数据");
  1031. }
  1032. }
  1033. // 心理
  1034. if (true)
  1035. {
  1036. //-1 不处理,
  1037. //0 正常,
  1038. //1 轻度,
  1039. //2 中度;
  1040. //3 重度;
  1041. GeneralParam psychResultParam = new()
  1042. {
  1043. Filters = new List<QueryFilterCondition>
  1044. {
  1045. new ()
  1046. {
  1047. Key=nameof(HisGpsPsychResult.Serialno),
  1048. Value=imeiDel,
  1049. ValueType=QueryValueTypeEnum.String,
  1050. Operator=QueryOperatorEnum.Equal
  1051. },
  1052. //new ()
  1053. //{
  1054. // Key=nameof(HisGpsFetalHeartRate.SampleTime),
  1055. // Value=sampleTime,
  1056. // ValueType=QueryValueTypeEnum.String,
  1057. // Operator=QueryOperatorEnum.GreaterEqual
  1058. //},
  1059. },
  1060. OrderBys = new List<OrderByCondition>
  1061. {
  1062. new (){
  1063. IsDesc=true,
  1064. Key=nameof(HisGpsPsychResult.Serialno)
  1065. }
  1066. }
  1067. };
  1068. var psych = await _hisPsychResultApiClient.GetFirstAsync(psychResultParam, imeiDel[^2..], null, new RequestHeader { RequestId = Guid.NewGuid().ToString("D") });
  1069. if (psych != null)
  1070. {
  1071. if (psych?.StressScore == 2)
  1072. {
  1073. fetalMovementPpVar = 1;
  1074. }
  1075. if (psych?.StressScore == 3)
  1076. {
  1077. fetalMovementPpVar = 2;
  1078. }
  1079. }
  1080. else
  1081. {
  1082. _logger.LogWarning($"{imeiDel} 周期心理压力无数据");
  1083. }
  1084. }
  1085. #endregion
  1086. #region 胎心与胎动的关系
  1087. /// 胎心值过缓时,则胎动数量减1;胎心值过速时,则胎动也加1。
  1088. /// 此值允许在上限值上继续增加,在下限值上继续减少,最小值为0。
  1089. /// 告警上限阀值
  1090. //1表示偏高;2表示偏低
  1091. if (true)
  1092. {
  1093. if (fhr.IsAbnormal == 2)
  1094. {
  1095. fetalMovementPpVar = -1;
  1096. }
  1097. if (fhr.IsAbnormal == 1)
  1098. {
  1099. fetalMovementPpVar = 1;
  1100. }
  1101. }
  1102. #endregion
  1103. _logger.LogInformation($"{imeiDel} 原始胎动值:{fetalMovementValue}, 步数参数变动值:{fetalMovementStepVar},体温参数变动值:{fetalMovementTempVar},血压参数变动值:{fetalMovementBpVar},心理压力参数变动值:{fetalMovementPpVar},胎心参数变动值:{fetalMovementFhrVar}");
  1104. fetalMovementValue = fetalMovementValue + fetalMovementStepVar + fetalMovementTempVar + fetalMovementBpVar + fetalMovementPpVar + fetalMovementFhrVar;
  1105. _logger.LogInformation($"{imeiDel} 孕周:{pregnancyWeeks},胎动数据采样时间:{fetalMovementSampleTime}|{fmNowSubtract.ToString("yyyy-MM-dd HH:mm:ss")}, 采样周期:{statStartTime}-{statEndTime}, 原始胎动值:{fetalMovementMapValue}, 佩戴时间 :{duringMins}|{phrRange.Last()}-{phrRange.First()}, 胎动计算值:{fetalMovementValue}, 胎动最终值:{fetalMovement} 已完成.");
  1106. // 获取胎心数据状态与胎动数据状态一致
  1107. //etalHeartRateIsAbnormal= fhr.IsAbnormal;
  1108. var feltalMovementIsAbnormal = fetalHeartRateIsAbnormal;
  1109. await _serviceIotApi.SetFetalMovementConfig(imeiDel, fetalMovement, fetalMovementSampleTime, feltalMovementIsAbnormal);
  1110. // 保存到MySQL数据库
  1111. HisGpsFetalMovement fm = new()
  1112. {
  1113. FetalMovementId = Guid.NewGuid().ToString("D"),
  1114. PersonId = commonPHR!.PersonId,
  1115. Serialno = imeiDel,
  1116. CreateTime = DateTime.Now,
  1117. IsAbnormal = feltalMovementIsAbnormal,
  1118. FetalMovementValue = fetalMovement,
  1119. SampleTime = fetalMovementSampleTime,
  1120. Method = 1,
  1121. IsDisplay = 1,
  1122. DeviceKey = commonPHR!.DeviceKey
  1123. };
  1124. await _hisFetalMovementApiClient.AddAsync(fm).ConfigureAwait(false);
  1125. var device = await _deviceCacheMgr.GetDeviceBySerialNoAsync(imeiDel).ConfigureAwait(false);
  1126. var fmMsgId = $"{imeiDel}-{fetalMovementSampleTime}-{Guid.NewGuid().ToString("D")[^3..]}";
  1127. var fmMsgTime = DateTimeUtil.GetDateTimeFromUnixTimeMilliseconds(long.Parse(fetalMovementSampleTime.Length < 13 ? fetalMovementSampleTime.PadRight(13, '0') : fetalMovementSampleTime)).ToString("yyyy-MM-dd HH:mm:ss");
  1128. // 胎动数据推送到第三方
  1129. var topic = "topic.push.third";
  1130. var fmThridMsg = new
  1131. {
  1132. messageId = fmMsgId,
  1133. topic = topic,
  1134. time = fmMsgTime,
  1135. data = new
  1136. {
  1137. imei = imeiDel,
  1138. value = fetalMovement,
  1139. isAbnormal = feltalMovementIsAbnormal,
  1140. type = "fetalMovement"
  1141. }
  1142. };
  1143. await _serviceMqProcess.ProcessIMEIEventMessageAsync(fmMsgId, topic, 31, fmThridMsg).ConfigureAwait(false);
  1144. // 胎动数据推送到微信
  1145. if (feltalMovementIsAbnormal != 0)
  1146. {
  1147. topic = "topic.push.wx";
  1148. var fmMsg = new
  1149. {
  1150. messageId = Guid.NewGuid().ToString("D"),
  1151. topic = topic,
  1152. time = DateTimeUtil.GetDateTimeFromUnixTimeMilliseconds(long.Parse(fetalMovementSampleTime.Length < 13 ? fetalMovementSampleTime.PadRight(13, '0') : fetalMovementSampleTime)).ToString("yyyy-MM-dd HH:mm:ss"),
  1153. data = new
  1154. {
  1155. deviceId = device?.DeviceId,
  1156. imei = imeiDel,
  1157. alarmTypeId = 12,
  1158. alarmDeviceName = imeiDel,
  1159. alarmRemarks = JsonConvert.SerializeObject(new { fetalMovementValue = fetalMovement, isAbnormal = feltalMovementIsAbnormal }),
  1160. address = string.Empty,
  1161. deviceKey = device?.DeviceId
  1162. }
  1163. };
  1164. await _serviceMqProcess.ProcessIMEIEventMessageAsync(fmMsgId, topic, fmMsg).ConfigureAwait(false);
  1165. }
  1166. // 设置入库缓存记录
  1167. await _deviceCacheMgr.SetFetalMovementAsync(imeiDel, fetalMovementSampleTime, fm);
  1168. }
  1169. else
  1170. {
  1171. _logger.LogWarning($"{imeiDel} 孕周 {pregnancyWeeks},超出胎动计算范围");
  1172. }
  1173. }
  1174. else
  1175. {
  1176. _logger.LogWarning($"{imeiDel} 最后一条胎心数据与胎动数据的小时差大于2,不计算胎动数据");
  1177. }
  1178. }
  1179. else
  1180. {
  1181. _logger.LogInformation($"{imeiDel} 胎动记录{isFetalMovementExisted},数据采样时间:{fetalMovementSampleTime}|{fmNowSubtract.ToString("yyyy-MM-dd HH:mm:ss")}, 周期:{statStartTime}-{statEndTime} 不足两条,不能判断是否持续佩戴");
  1182. }
  1183. }
  1184. else
  1185. {
  1186. _logger.LogInformation($"{imeiDel} 胎动记录{isFetalMovementExisted},数据采样时间:{fetalMovementSampleTime}|{fmNowSubtract.ToString("yyyy-MM-dd HH:mm:ss")}, 周期:{statStartTime}-{statEndTime} 已处理");
  1187. }
  1188. #endregion
  1189. }
  1190. catch (Exception ex)
  1191. {
  1192. _logger.LogError($"处理延时计算胎动数据异常:{ex.Message}, {ex.StackTrace}");
  1193. }
  1194. });
  1195. thread.Start();
  1196. }
  1197. }
  1198. }
  1199. */
  1200. #endregion
  1201. #region 胎动延时计算(实时now是3小时,计算 lastupdate 0~2范围的数据,)
  1202. ///实时now的hour是3小时,计算 lastupdate 0~2范围的数据,
  1203. ///只在 HOUR = new int[] { 1, 3, 5, 7, 9, 11,13, 15, 17, 19, 21, 23 };是奇数
  1204. using (_logger.BeginScope(new Dictionary<string, object> { ["RequestId"] = $"FM-{imeiDel}-{DateTime.Now.ToString("yyyyMMddHHmmss")}" }))
  1205. {
  1206. try
  1207. {
  1208. var watchConfig = await _deviceCacheMgr.GetGpsDeviceWatchConfigCacheObjectBySerialNoAsync(imeiDel, "0067");
  1209. _logger.LogInformation($"触发胎动计算,设备配置{JsonConvert.SerializeObject(watchConfig)}");
  1210. var isFetalHeartEnable = watchConfig != null && (int)watchConfig["enabled"]! == 1;
  1211. var highFreqSampleInterval = (int)watchConfig!["highFreqSampleInterval"]! >= 60 ? (int)watchConfig!["highFreqSampleInterval"]! : 60;
  1212. if (isFetalHeartEnable)
  1213. {
  1214. var edoc = DateTimeUtil.ToDateTime(watchConfig!["EDOC"]!.ToString());
  1215. // 已经建模
  1216. var commonPHR = await _serviceTDengine.GetLastAsync<PregnancyCommonHeartRateModel>(imeiDel);
  1217. if (commonPHR != null)
  1218. {
  1219. var phr = await _serviceTDengine.GetBySerialNoAsync<PregnancyHeartRateModel>(imeiDel, 7);
  1220. _logger.LogInformation($"{imeiDel} 计算胎动数据 ");
  1221. DateTime fmNow = DateTime.Now;
  1222. int fmNowHour = fmNow.Hour;
  1223. DateTime statStartTime;
  1224. DateTime statEndTime;
  1225. if (fmNowHour == 1)
  1226. {
  1227. // last_update 22~0
  1228. statStartTime = fmNow.Date.AddDays(-1).AddHours(22);
  1229. statEndTime = fmNow.Date;
  1230. }
  1231. else
  1232. {
  1233. statStartTime = fmNow.Date.AddHours(fmNowHour - 3);
  1234. statEndTime = fmNow.Date.AddHours(fmNowHour - 1);
  1235. }
  1236. _logger.LogInformation($"{imeiDel} 胎动统计周期:{statStartTime.ToString("yyyy-MM-dd HH:mm:ss")}-{statEndTime.ToString("yyyy-MM-dd HH:mm:ss")}");
  1237. var fetalMovementSampleTime = DateTimeUtil.ConvertToTimeStamp(statEndTime).ToString()[..10];
  1238. var isFetalMovementExisted = await _deviceCacheMgr.FetalMovementIsExistedAsync(imeiDel, fetalMovementSampleTime);
  1239. if (!isFetalMovementExisted)
  1240. {
  1241. /// 开始计算
  1242. var phrRange = phr.Where(i => i.LastUpdate >= statStartTime && i.LastUpdate <= statEndTime)
  1243. .OrderByDescending(i => i.LastUpdate)
  1244. .Select(i => i.LastUpdate)
  1245. .ToList();
  1246. // 判断是否有持续佩戴
  1247. if (phrRange.Count >= 2)
  1248. {
  1249. // 读取胎心数据
  1250. GeneralParam param = new()
  1251. {
  1252. Filters = new List<QueryFilterCondition>
  1253. {
  1254. new ()
  1255. {
  1256. Key=nameof(HisGpsFetalHeartRate.Serialno),
  1257. Value=imeiDel,
  1258. ValueType=QueryValueTypeEnum.String,
  1259. Operator=QueryOperatorEnum.Equal
  1260. },
  1261. //new ()
  1262. //{
  1263. // Key=nameof(HisGpsFetalHeartRate.SampleTime),
  1264. // Value=sampleTime,
  1265. // ValueType=QueryValueTypeEnum.String,
  1266. // Operator=QueryOperatorEnum.GreaterEqual
  1267. //},
  1268. },
  1269. OrderBys = new List<OrderByCondition>
  1270. {
  1271. new (){
  1272. IsDesc=true,
  1273. Key=nameof(HisGpsFetalHeartRate.SampleTime)
  1274. }
  1275. }
  1276. };
  1277. var fetalHeartRateIsAbnormal = 0;
  1278. var fhr = await _hisFetalHeartApiClient.GetFirstAsync(param, imeiDel[^2..], null, new RequestHeader { RequestId = Guid.NewGuid().ToString("D") });
  1279. // 胎心数据时间与胎动时间一致
  1280. var time = long.Parse(fhr.SampleTime.Length < 13 ? fhr.SampleTime.PadRight(13, '0') : fhr.SampleTime);
  1281. var fhrSampleTime = DateTimeUtil.GetDateTimeFromUnixTimeMilliseconds(time);
  1282. // 胎心数据时间与胎动时间一致,最后一条胎心数据与胎动数据的小时差不大于3
  1283. if ((DateTime.Now.Hour - fhrSampleTime.Hour) <= 3)
  1284. {
  1285. var duringMins = Math.Abs((phrRange.First() - phrRange.Last()).TotalMinutes);
  1286. //在餐后时间段(8:00~10:00,12:00~14:00,18:00~20:00,22:00~24:00)取中间值。其他时间段取正常起始值
  1287. bool isInTimeRanges = IsLastUpdateInTimeRanges(phrRange.First());
  1288. int pregnancyWeeks = (DateTime.Now - edoc.AddDays(-280)).Days / 7;
  1289. if (pregnancyWeeks >= 12 && pregnancyWeeks <= 50)
  1290. {
  1291. var fetalMovementMap = _mgrFetalMovementNormalValueRangeCache.GetFetalMovements();
  1292. var fetalMovementMapValue = isInTimeRanges ? fetalMovementMap
  1293. .Where(i =>
  1294. i.PregnancyPeriod![0] <= pregnancyWeeks &&
  1295. i.PregnancyPeriod[1] >= pregnancyWeeks)
  1296. .Select(i => i.MedianMovement)
  1297. .FirstOrDefault()
  1298. :
  1299. fetalMovementMap
  1300. .Where(i =>
  1301. i.PregnancyPeriod![0] <= pregnancyWeeks &&
  1302. i.PregnancyPeriod[1] >= pregnancyWeeks)
  1303. .Select(i => i.InitialMovement)
  1304. .FirstOrDefault()
  1305. ;
  1306. var fetalMovementTimeVar = (fetalMovementMapValue * duringMins * 2) / 120;
  1307. //// 四舍五入
  1308. //var fetalMovement = (int)Math.Round(fetalMovementValue, 0, MidpointRounding.AwayFromZero);
  1309. // _logger.LogInformation($"{imeiDel} segmentCountFMIndex: {i} -- fetalMovementSampleTime:{fetalMovementSampleTime}|{midNight.AddHours(2 * i).ToString("yyyy-MM-dd HH:mm:ss")} -- statStartTime: {statStartTime} -- statEndTime: {statEndTime}-- isFetalMovementExisted: {isFetalMovementExisted} ");
  1310. #region 生理健康与胎动的关系
  1311. /// (步数)运动步数超过1500步则加1;
  1312. /// (体温)低烧则胎动减1,高烧胎动减2;低烧是37.3~38.5,38.6以上是高烧。
  1313. /// (血压)血压收缩压大于160则加1。
  1314. /// (心理)心理压力高加2,压力中加1;
  1315. var fetalMovementStepVar = 0;
  1316. var fetalMovementTempVar = 0;
  1317. var fetalMovementBpVar = 0;
  1318. var fetalMovementPpVar = 0;
  1319. var fetalMovementFhrVar = 0;
  1320. // 步数
  1321. if (true)
  1322. {
  1323. var step = await _personCacheMgr.GetStepPeriodicityAsync(imeiDel);
  1324. if (step != null)
  1325. {
  1326. if (DateTime.Now.Hour - ((DateTime)step?.LastUpdate!).Hour <= 2)
  1327. {
  1328. if (step.Steps > 1500) fetalMovementStepVar = 1;
  1329. }
  1330. else
  1331. {
  1332. _logger.LogWarning($"{imeiDel} 周期步数 时间无效");
  1333. }
  1334. }
  1335. else
  1336. {
  1337. _logger.LogWarning($"{imeiDel} 周期步数无数据");
  1338. }
  1339. }
  1340. // 体温
  1341. if (true)
  1342. {
  1343. var temp = await _personCacheMgr.GetTemperaturePeriodicityAsync(imeiDel);
  1344. if (temp != null)
  1345. {
  1346. if (DateTime.Now.Hour - ((DateTime)temp?.LastUpdate!).Hour <= 2)
  1347. {
  1348. // 中烧
  1349. if (temp.Temperature >= 37.3M && temp.Temperature <= 38.5M)
  1350. {
  1351. fetalMovementTempVar = -1;
  1352. }
  1353. // 高烧
  1354. if (temp.Temperature >= 38.6M)
  1355. {
  1356. fetalMovementTempVar = -2;
  1357. }
  1358. }
  1359. else
  1360. {
  1361. _logger.LogWarning($"{imeiDel} 周期体温 时间无效");
  1362. }
  1363. }
  1364. else
  1365. {
  1366. _logger.LogWarning($"{imeiDel} 周期体温无数据");
  1367. }
  1368. }
  1369. // 血压
  1370. if (true)
  1371. {
  1372. var bp = await _personCacheMgr.GetBloodPressPeriodicityAsync(imeiDel);
  1373. if (bp != null)
  1374. {
  1375. if (DateTime.Now.Hour - ((DateTime)bp?.LastUpdate!).Hour <= 2)
  1376. {
  1377. if (bp.SystolicValue > 160)
  1378. {
  1379. fetalMovementBpVar = 1;
  1380. }
  1381. }
  1382. else
  1383. {
  1384. _logger.LogWarning($"{imeiDel} 周期血压 时间无效");
  1385. }
  1386. }
  1387. else
  1388. {
  1389. _logger.LogWarning($"{imeiDel} 周期血压无数据");
  1390. }
  1391. }
  1392. // 心理
  1393. if (true)
  1394. {
  1395. //-1 不处理,
  1396. //0 正常,
  1397. //1 轻度,
  1398. //2 中度;
  1399. //3 重度;
  1400. GeneralParam psychResultParam = new()
  1401. {
  1402. Filters = new List<QueryFilterCondition>
  1403. {
  1404. new ()
  1405. {
  1406. Key=nameof(HisGpsPsychResult.Serialno),
  1407. Value=imeiDel,
  1408. ValueType=QueryValueTypeEnum.String,
  1409. Operator=QueryOperatorEnum.Equal
  1410. },
  1411. //new ()
  1412. //{
  1413. // Key=nameof(HisGpsFetalHeartRate.SampleTime),
  1414. // Value=sampleTime,
  1415. // ValueType=QueryValueTypeEnum.String,
  1416. // Operator=QueryOperatorEnum.GreaterEqual
  1417. //},
  1418. },
  1419. OrderBys = new List<OrderByCondition>
  1420. {
  1421. new (){
  1422. IsDesc=true,
  1423. Key=nameof(HisGpsPsychResult.Serialno)
  1424. }
  1425. }
  1426. };
  1427. var psych = await _hisPsychResultApiClient.GetFirstAsync(psychResultParam, imeiDel[^2..], null, new RequestHeader { RequestId = Guid.NewGuid().ToString("D") });
  1428. if (psych != null)
  1429. {
  1430. if (psych?.StressScore == 2)
  1431. {
  1432. fetalMovementPpVar = 1;
  1433. }
  1434. if (psych?.StressScore == 3)
  1435. {
  1436. fetalMovementPpVar = 2;
  1437. }
  1438. }
  1439. else
  1440. {
  1441. _logger.LogWarning($"{imeiDel} 周期心理压力无数据");
  1442. }
  1443. }
  1444. #endregion
  1445. #region 胎心与胎动的关系
  1446. /// 胎心值过缓时,则胎动数量减1;胎心值过速时,则胎动也加1。
  1447. /// 此值允许在上限值上继续增加,在下限值上继续减少,最小值为0。
  1448. /// 告警上限阀值
  1449. //1表示偏高;2表示偏低
  1450. if (true)
  1451. {
  1452. if (fhr.IsAbnormal == 2)
  1453. {
  1454. fetalMovementPpVar = -1;
  1455. }
  1456. if (fhr.IsAbnormal == 1)
  1457. {
  1458. fetalMovementPpVar = 1;
  1459. }
  1460. }
  1461. #endregion
  1462. //var fetalMovementTimeVar = fetalMovementValue;
  1463. _logger.LogInformation($"{imeiDel} 时间比例胎动值:{fetalMovementTimeVar}, 步数参数变动值:{fetalMovementStepVar},体温参数变动值:{fetalMovementTempVar},血压参数变动值:{fetalMovementBpVar},心理压力参数变动值:{fetalMovementPpVar},胎心参数变动值:{fetalMovementFhrVar}");
  1464. var fetalMovementValue = fetalMovementTimeVar + fetalMovementStepVar + fetalMovementTempVar + fetalMovementBpVar + fetalMovementPpVar + fetalMovementFhrVar;
  1465. // 四舍五入
  1466. var fetalMovement = (int)Math.Round(fetalMovementValue, 0, MidpointRounding.AwayFromZero);
  1467. _logger.LogInformation($"{imeiDel} 孕周:{pregnancyWeeks},胎动数据采样时间:{fetalMovementSampleTime}|{statStartTime.ToString("yyyy-MM-dd HH:mm:ss")}, 采样周期:{statStartTime}-{statEndTime}, 时间比例胎动值:{fetalMovementMapValue}, 佩戴时间 :{duringMins}|{phrRange.Last()}-{phrRange.First()}, 胎动计算值:{fetalMovementTimeVar}, 胎动四舍五入最终值:{fetalMovement} 已完成.");
  1468. // 获取胎心数据状态与胎动数据状态一致
  1469. //etalHeartRateIsAbnormal= fhr.IsAbnormal;
  1470. var feltalMovementIsAbnormal = fetalHeartRateIsAbnormal;
  1471. await _serviceIotApi.SetFetalMovementConfig(imeiDel, fetalMovement, fetalMovementSampleTime, feltalMovementIsAbnormal);
  1472. // 保存到MySQL数据库
  1473. HisGpsFetalMovement fm = new()
  1474. {
  1475. FetalMovementId = Guid.NewGuid().ToString("D"),
  1476. PersonId = commonPHR!.PersonId,
  1477. Serialno = imeiDel,
  1478. CreateTime = DateTime.Now,
  1479. IsAbnormal = feltalMovementIsAbnormal,
  1480. FetalMovementValue = fetalMovement,
  1481. SampleTime = fetalMovementSampleTime,
  1482. Method = 1,
  1483. IsDisplay = 1,
  1484. DeviceKey = commonPHR!.DeviceKey
  1485. };
  1486. await _hisFetalMovementApiClient.AddAsync(fm).ConfigureAwait(false);
  1487. var device = await _deviceCacheMgr.GetDeviceBySerialNoAsync(imeiDel).ConfigureAwait(false);
  1488. var fmMsgId = $"{imeiDel}-{fetalMovementSampleTime}-{Guid.NewGuid().ToString("D")[^3..]}";
  1489. var fmMsgTime = DateTimeUtil.GetDateTimeFromUnixTimeMilliseconds(long.Parse(fetalMovementSampleTime.Length < 13 ? fetalMovementSampleTime.PadRight(13, '0') : fetalMovementSampleTime)).ToString("yyyy-MM-dd HH:mm:ss");
  1490. // 胎动数据推送到第三方
  1491. var topic = "topic.push.third";
  1492. var fmThridMsg = new
  1493. {
  1494. messageId = fmMsgId,
  1495. topic = topic,
  1496. time = fmMsgTime,
  1497. data = new
  1498. {
  1499. imei = imeiDel,
  1500. value = fetalMovement,
  1501. isAbnormal = feltalMovementIsAbnormal,
  1502. type = "fetalMovement"
  1503. }
  1504. };
  1505. await _serviceMqProcess.ProcessIMEIEventMessageAsync(fmMsgId, topic, 31, fmThridMsg).ConfigureAwait(false);
  1506. // 胎动数据推送到微信
  1507. if (feltalMovementIsAbnormal != 0)
  1508. {
  1509. topic = "topic.push.wx";
  1510. var fmMsg = new
  1511. {
  1512. messageId = Guid.NewGuid().ToString("D"),
  1513. topic = topic,
  1514. time = DateTimeUtil.GetDateTimeFromUnixTimeMilliseconds(long.Parse(fetalMovementSampleTime.Length < 13 ? fetalMovementSampleTime.PadRight(13, '0') : fetalMovementSampleTime)).ToString("yyyy-MM-dd HH:mm:ss"),
  1515. data = new
  1516. {
  1517. deviceId = device?.DeviceId,
  1518. imei = imeiDel,
  1519. alarmTypeId = 12,
  1520. alarmDeviceName = imeiDel,
  1521. alarmRemarks = JsonConvert.SerializeObject(new { fetalMovementValue = fetalMovement, isAbnormal = feltalMovementIsAbnormal }),
  1522. address = string.Empty,
  1523. deviceKey = device?.DeviceId
  1524. }
  1525. };
  1526. await _serviceMqProcess.ProcessIMEIEventMessageAsync(fmMsgId, topic, fmMsg).ConfigureAwait(false);
  1527. }
  1528. // 设置入库缓存记录
  1529. await _deviceCacheMgr.SetFetalMovementAsync(imeiDel, fetalMovementSampleTime, fm);
  1530. }
  1531. else
  1532. {
  1533. _logger.LogWarning($"{imeiDel} 孕周 {pregnancyWeeks},超出胎动计算范围");
  1534. }
  1535. }
  1536. else
  1537. {
  1538. _logger.LogWarning($"{imeiDel} 最后一条胎心数据与胎动数据的小时差大于2,不计算胎动数据");
  1539. }
  1540. }
  1541. else
  1542. {
  1543. _logger.LogInformation($"{imeiDel} 胎动记录{isFetalMovementExisted},数据采样时间:{fetalMovementSampleTime}|{statStartTime.ToString("yyyy-MM-dd HH:mm:ss")}, 周期:{statStartTime}-{statEndTime} 不足两条,不能判断是否持续佩戴");
  1544. }
  1545. }
  1546. else
  1547. {
  1548. _logger.LogInformation($"{imeiDel} 胎动记录{isFetalMovementExisted},数据采样时间:{fetalMovementSampleTime}|{statStartTime.ToString("yyyy-MM-dd HH:mm:ss")}, 周期:{statStartTime}-{statEndTime} 已处理");
  1549. }
  1550. }
  1551. else
  1552. {
  1553. _logger.LogWarning($"{imeiDel} 没有胎心建模数据");
  1554. }
  1555. }
  1556. else
  1557. {
  1558. _logger.LogWarning($"{imeiDel} 胎心监测功能没有开启");
  1559. }
  1560. }
  1561. catch (Exception ex )
  1562. {
  1563. _logger.LogError($"{imeiDel} 计算胎动数据异常 {ex.Message}\n {ex.StackTrace}");
  1564. }
  1565. }
  1566. #endregion
  1567. }
  1568. else
  1569. {
  1570. // 处理血压业务
  1571. int systolicInc;
  1572. int diastolicInc;
  1573. int systolicRefValue;
  1574. int diastolicRefValue;
  1575. decimal systolicAvg;
  1576. decimal diastolicAvg;
  1577. int systolicMax = 0;
  1578. int diastolicMax = 0;
  1579. // 统计时间
  1580. //DateTime endTime = DateTime.Now; //测试
  1581. DateTime statStartTime = DateTime.Now;
  1582. // 最小值
  1583. int systolicMin = 0;
  1584. int diastolicMin = 0;
  1585. // 偏移参数
  1586. var avgOffset = 0.25M;
  1587. var systolicAvgOffset = avgOffset;
  1588. var diastolicAvgOffset = avgOffset;
  1589. // 最后一次下发值
  1590. int lastPushSystolicInc = 0;
  1591. int lastPushDiastolicInc = 0;
  1592. var startTime = DateTime.Now;
  1593. // 下发增量值
  1594. #region 统计定时下发增量值
  1595. //var last = await _serviceTDengine.GetLastAsync("stb_hm_bloodpress_stats_inc", $"serialno='{imeiDel}' order by last_update desc");
  1596. //var ts = last?[0];
  1597. // 最后一条血压数据
  1598. var condition = $"serialno='{imeiDel}' order by last_update desc";
  1599. var field = "last_row(*)";
  1600. var lastHmBpResponse = await _serviceTDengine.ExecuteSelectRestResponseAsync("stb_hm_bloodpress", condition, field);
  1601. var lastHmBpParser = JsonConvert.DeserializeObject<ParseTDengineRestResponse<BloodPressureModel>>(lastHmBpResponse!);
  1602. var lastHmBp = lastHmBpParser?.Select().FirstOrDefault();
  1603. //if (lastHmBpParser?.Select()?.ToList().Count < 2)
  1604. //{
  1605. // _logger.LogInformation($"{imeiDel} -- {nameof(Worker)} --{nameof(WatchEvents)} -- 血压数据条目不足");
  1606. // break;
  1607. //}
  1608. // 7 天有效数据
  1609. if (lastHmBp?.Timestamp.AddDays(7) > DateTime.Now)
  1610. {
  1611. // 计算增量值
  1612. condition = $"serialno='{imeiDel}' order by ts desc";
  1613. var lastPushResponse = await _serviceTDengine.ExecuteSelectRestResponseAsync("stb_hm_bp_push_ref_inc_value", condition, field);
  1614. if (lastPushResponse == null)
  1615. {
  1616. _logger.LogInformation($"{imeiDel}--没有下发记录");
  1617. break;
  1618. }
  1619. var lastPushParser = JsonConvert.DeserializeObject<ParseTDengineRestResponse<BloodPressurePushRefIncModel>>(lastPushResponse);
  1620. var lastPush = lastPushParser!.Select().FirstOrDefault();
  1621. // 有下推记录
  1622. if (lastPush != null)
  1623. {
  1624. systolicRefValue = lastPush!.SystolicRefValue;
  1625. diastolicRefValue = lastPush!.DiastolicRefValue;
  1626. lastPushSystolicInc = lastPush!.SystolicIncValue;
  1627. lastPushDiastolicInc = lastPush!.DiastolicIncValue;
  1628. condition = $"ts between '{lastPush?.Timestamp:yyyy-MM-dd HH:mm:ss.fff}' and '{startTime:yyyy-MM-dd HH:mm:ss.fff}' " +
  1629. $"and serialno='{imeiDel}' " +
  1630. $"and is_display = true";
  1631. // 使用最近一次的下推时间作为统计的开始时间
  1632. statStartTime = lastPush!.Timestamp;
  1633. }
  1634. // 没有下推记录(历史遗留数据),没有初始的测量值产生的平均值(测量值=平均值)
  1635. else
  1636. {
  1637. #region 获取个人信息
  1638. var person = await _personCacheMgr.GetDeviceGpsPersonCacheBySerialNoAsync(Guid.NewGuid().ToString(), imeiDel).ConfigureAwait(false);
  1639. //验证这个信息是否存在
  1640. if (person == null || person?.Person.BornDate == null)
  1641. {
  1642. _logger.LogWarning($"{nameof(Worker)}--{imeiDel} 验证个人信息,找不到个人信息,跳过此消息");
  1643. break;
  1644. }
  1645. // 验证年龄是否在范围 (2 - 120)
  1646. var age = SafeType.SafeInt(DateTime.Today.Year - person?.Person.BornDate!.Value.Year!);
  1647. if (age < 2 || age > 120)
  1648. {
  1649. _logger.LogWarning($"{nameof(Worker)}--{imeiDel} 验证年龄,不在范围 (2 - 120)岁,跳过此消息");
  1650. break;
  1651. }
  1652. var gender = person?.Person.Gender == true ? 1 : 2;
  1653. var isHypertension = SafeType.SafeBool(person?.Person.Ishypertension!);
  1654. var height = SafeType.SafeDouble(person?.Person.Height!);
  1655. var weight = SafeType.SafeDouble(person?.Person.Weight!);
  1656. #endregion
  1657. #region 初始化常规血压标定值标定值
  1658. var bpRef = await _bpRefValCacheManager.GetBloodPressReferenceValueAsync(age, gender, isHypertension);
  1659. //systolicRefValue = bpRef!.Systolic;//?
  1660. //diastolicRefValue = bpRef!.Diastolic;//?
  1661. #endregion
  1662. systolicRefValue = bpRef!.Systolic;
  1663. diastolicRefValue = bpRef!.Diastolic;
  1664. lastPushSystolicInc = 0;
  1665. lastPushDiastolicInc = 0;
  1666. condition = $"ts <= '{startTime:yyyy-MM-dd HH:mm:ss.fff}' " +
  1667. $"and serialno='{imeiDel}' " +
  1668. $"and is_display = true";
  1669. }
  1670. var hmBpResponse = await _serviceTDengine.ExecuteSelectRestResponseAsync("stb_hm_bloodpress", condition);
  1671. var hmBpParser = JsonConvert.DeserializeObject<ParseTDengineRestResponse<BloodPressureModel>>(hmBpResponse!);
  1672. var hmBp = hmBpParser?.Select();
  1673. //if (hmBp?.ToList().Count < 2)
  1674. // 1. 判断数据样本数量
  1675. if (hmBpParser!.Rows < 5)
  1676. {
  1677. _logger.LogInformation($"{imeiDel} -- {nameof(Worker)} --{nameof(WatchEvents)} -- 统计定时下发,计算增量值的数据条目不足:{hmBpParser!.Rows} < 5");
  1678. _logger.LogInformation($"{imeiDel} -- {nameof(Worker)} --{nameof(WatchEvents)} 没有足够的数据样本,不会定时下发");
  1679. break;
  1680. }
  1681. // 没有下推记录重新计算统计时间
  1682. if (lastPush == null)
  1683. {
  1684. var firstHmBp = hmBpParser?.Select(i => i).OrderBy(i => i.Timestamp).FirstOrDefault();
  1685. statStartTime = firstHmBp!.Timestamp;
  1686. }
  1687. // GetSampleTime(systolicRefValue, hmBpParser);
  1688. // 最大值
  1689. //systolicMax = (int)hmBpParser?.Select(i => i.SystolicValue).Max()!;
  1690. //diastolicMax = (int)hmBpParser?.Select(i => i.DiastolicValue).Max()!;
  1691. //// 最小值
  1692. //systolicMin = (int)hmBpParser?.Select(i => i.SystolicValue).Min()!;
  1693. //diastolicMin = (int)hmBpParser?.Select(i => i.DiastolicValue).Min()!;
  1694. //systolicAvg = (int)(hmBpParser?.AverageAfterRemovingOneMinMaxRef(i => i.SystolicValue, SafeType.SafeInt(systolicRefValue!)))!;
  1695. //diastolicAvg = (int)(hmBpParser?.AverageAfterRemovingOneMinMaxRef(i => i.DiastolicValue, SafeType.SafeInt(diastolicRefValue!)))!;
  1696. var avgs = _serviceTDengine.AverageAfterRemovingOneMinMaxRef(hmBpParser!);
  1697. systolicAvg = avgs[0];
  1698. diastolicAvg = avgs[1];
  1699. // 2. 判断能否计算增量值
  1700. if (systolicAvg.Equals(0))
  1701. {
  1702. _logger.LogInformation($"{imeiDel}--{nameof(Worker)}--计算平均值" +
  1703. $"\n currentSystolicAvg:{systolicAvg} -- lastPushSystolicInc:{lastPushSystolicInc}" +
  1704. $"\n currentDiastolicInc:{diastolicAvg} -- lastPushDiastolicInc:{lastPushDiastolicInc}");
  1705. _logger.LogInformation($"{imeiDel}--{nameof(Worker)} 没有足够的数据样本计算平均值,不会定时下发");
  1706. break;
  1707. }
  1708. // 除最大值和最小值后的平均值与标定值差值少于4后(当天计算出该结果则也不产生增量调整),就不再进行增量值调整了。
  1709. if (systolicRefValue - systolicAvg < 4)
  1710. {
  1711. _logger.LogInformation($"diastolic 舒张压 {imeiDel}除最大值和最小值后的平均值:{diastolicAvg}与标定值:{diastolicRefValue}\n systolic 收缩压 {imeiDel}除最大值和最小值后的平均值:{systolicAvg}与标定值:{systolicRefValue}的差值(标定值-平均值)少于4后,systolic 收缩压 不再进行增量值调整");
  1712. break;
  1713. }
  1714. if (diastolicRefValue - diastolicAvg < 4)
  1715. {
  1716. _logger.LogInformation($"systolic 收缩压 {imeiDel}除最大值和最小值后的平均值:{systolicAvg}与标定值:{systolicRefValue}\n diastolic 舒张压 {imeiDel}除最大值和最小值后的平均值:{diastolicAvg}与标定值:{diastolicRefValue}的差值(标定值-平均值)少于4后,diastolic 舒张压 不再进行增量值调整");
  1717. break;
  1718. }
  1719. // 增量值=(标定值-平均值)* 0.25
  1720. var currentSystolicInc = (int)((systolicRefValue - systolicAvg) * systolicAvgOffset)!;
  1721. var currentDiastolicInc = (int)((diastolicRefValue - diastolicAvg) * diastolicAvgOffset)!;
  1722. // 累计增量
  1723. systolicInc = currentSystolicInc + lastPushSystolicInc;
  1724. diastolicInc = currentDiastolicInc + lastPushDiastolicInc;
  1725. _logger.LogInformation($"{imeiDel}--{nameof(Worker)}--计算增量值" +
  1726. $"\n {imeiDel} -- systolicAvg:{systolicAvg}-- systolicInc:{systolicInc}-- currentSystolicInc:{currentSystolicInc} -- lastPushSystolicInc:{lastPushSystolicInc}" +
  1727. $"\n {imeiDel} -- diastolicAvg:{diastolicAvg}-- diastolicInc:{diastolicInc} --currentDiastolicInc:{currentDiastolicInc} -- lastPushDiastolicInc:{lastPushDiastolicInc}");
  1728. _logger.LogInformation($"{imeiDel}--{nameof(Worker)}-- 定时校准,发给设备的绝对增量值=(上次绝对增量值+新数据的增量值)");
  1729. _logger.LogInformation($"{nameof(Worker)} 开启血压标定值下发: {_configBoodPressResolver.EnableBPRefPush}");
  1730. if (_configBoodPressResolver.EnableBPRefPush)
  1731. // if (false) // 临时关闭
  1732. {
  1733. BloodPressCalibrationConfigModel bpIncData = new()
  1734. {
  1735. Imei = imeiDel,
  1736. SystolicRefValue = SafeType.SafeInt(((int)systolicRefValue!)), //收缩压标定值,值为0 表示不生效
  1737. DiastolicRefValue = SafeType.SafeInt(((int)diastolicRefValue!)), //舒张压标定值,值为0表示不生效
  1738. SystolicIncValue = SafeType.SafeInt(((int)systolicInc!)), //收缩压显示增量,值为0 表示不生效
  1739. DiastolicIncValue = SafeType.SafeInt(((int)diastolicInc!)) //舒张压显示增量,值为0 表示不生效
  1740. };
  1741. //var pushedBP = await _serviceIotApi.SetBloodPressCalibrationConfigAsync(bpIncData).ConfigureAwait(false);
  1742. var response = await _serviceIotApi.SetBloodPressCalibrationConfig2Async(bpIncData).ConfigureAwait(false);
  1743. var pushedBP = response.Flag;
  1744. if (pushedBP)
  1745. {
  1746. #region 保存下推记录 stb_hm_bp_push_ref_inc_value
  1747. var sql = $"INSERT INTO health_monitor.hm_bp_push_ref_inc_value_{imeiDel.Substring(imeiDel.Length - 2)} " +
  1748. $"USING health_monitor.stb_hm_bp_push_ref_inc_value " +
  1749. $"TAGS ('{imeiDel.Substring(imeiDel.Length - 2)}') " +
  1750. $"VALUES(" +
  1751. $"'{startTime:yyyy-MM-dd HH:mm:ss.fff}'," +
  1752. $"'{imeiDel}'," +
  1753. $"{bpIncData.SystolicRefValue}," +
  1754. $"{bpIncData.DiastolicRefValue}," +
  1755. $"{bpIncData.SystolicIncValue}," +
  1756. $"{bpIncData.DiastolicIncValue}," +
  1757. $"{false}," +
  1758. $"{systolicAvg}," +
  1759. $"{diastolicAvg}," +
  1760. $"{systolicAvgOffset}," +
  1761. $"{diastolicAvgOffset}," +
  1762. $"'{statStartTime:yyyy-MM-dd HH:mm:ss.fff}'," +
  1763. $"'{startTime:yyyy-MM-dd HH:mm:ss.fff}'" +
  1764. $")";
  1765. _serviceTDengine.ExecuteInsertSQL(sql);
  1766. #endregion
  1767. #region 注册定时下发
  1768. // 注册下次下推
  1769. var endTime = DateTime.Now;
  1770. #if DEBUG
  1771. //long ttl = (long)((60 * 1000-(endTime-startTime).TotalMilliseconds)/1000);
  1772. //await _serviceEtcd.PutValAsync(key, imeiDel,ttl, false).ConfigureAwait(false);
  1773. var interval = 0;
  1774. // 获取当前时间
  1775. DateTime now = DateTime.Now;
  1776. // 计算距离下一个$interval天后的8点的时间间隔
  1777. DateTime nextRunTime = new DateTime(now.Year, now.Month, now.Day, now.Hour, now.Minute + 2, 0).AddDays(interval);
  1778. TimeSpan timeUntilNextRun = nextRunTime - now;
  1779. // 如果当前时间已经超过了8点,将等待到明天后的8点
  1780. if (timeUntilNextRun < TimeSpan.Zero)
  1781. {
  1782. timeUntilNextRun = timeUntilNextRun.Add(TimeSpan.FromMinutes(1));
  1783. nextRunTime += timeUntilNextRun;
  1784. }
  1785. // var ttl = timeUntilNextRun.TotalMilliseconds;
  1786. long ttl = (long)((timeUntilNextRun.TotalMilliseconds - (endTime - startTime).TotalMilliseconds) / 1000);
  1787. var data = new
  1788. {
  1789. imei = imeiDel,
  1790. create_time = now.ToString("yyyy-MM-dd HH:mm:ss"),
  1791. ttl,
  1792. next_run_time = nextRunTime.ToString("yyyy-MM-dd HH:mm:ss")
  1793. };
  1794. var result = JsonConvert.SerializeObject(data);
  1795. await _serviceEtcd.PutValAsync(key, result, ttl, false).ConfigureAwait(false);
  1796. #else
  1797. // 每$interval天,晚上8点
  1798. var interval = 1;
  1799. // 获取当前时间
  1800. DateTime now = DateTime.Now;
  1801. // 计算距离下一个$interval天后的8点的时间间隔
  1802. DateTime nextRunTime = new DateTime(now.Year, now.Month, now.Day, 20, 0, 0).AddDays(interval);
  1803. TimeSpan timeUntilNextRun = nextRunTime - now;
  1804. // 如果当前时间已经超过了8点,将等待到明天后的8点
  1805. if (timeUntilNextRun < TimeSpan.Zero)
  1806. {
  1807. timeUntilNextRun = timeUntilNextRun.Add(TimeSpan.FromDays(1));
  1808. nextRunTime += timeUntilNextRun;
  1809. }
  1810. // var ttl = timeUntilNextRun.TotalMilliseconds;
  1811. long ttl = (long)((timeUntilNextRun.TotalMilliseconds-(endTime-startTime).TotalMilliseconds)/1000);
  1812. var data = new
  1813. {
  1814. imei = imeiDel,
  1815. create_time = now.ToString("yyyy-MM-dd HH:mm:ss"),
  1816. ttl,
  1817. next_run_time = nextRunTime.ToString("yyyy-MM-dd HH:mm:ss")
  1818. };
  1819. var result = JsonConvert.SerializeObject(data);
  1820. await _serviceEtcd.PutValAsync(key, result, ttl, false).ConfigureAwait(false);
  1821. #endif
  1822. #endregion
  1823. }
  1824. else
  1825. {
  1826. _logger.LogInformation($"错误响应,没有下推数据:{response.Message}");
  1827. }
  1828. }
  1829. }
  1830. else
  1831. {
  1832. _logger.LogInformation($"向{imeiDel}统计数据已经失效");
  1833. }
  1834. #endregion
  1835. }
  1836. }
  1837. break;
  1838. }
  1839. }
  1840. catch (Exception ex)
  1841. {
  1842. _logger.LogError($"{nameof(WatchEvents)},出错: |{ex.Message}|{ex.StackTrace}");
  1843. }
  1844. });
  1845. }
  1846. private async Task SetIntervalTriggerAsync(string key, string imei, long interval)
  1847. {
  1848. // var key = $"health_monitor/schedule_push/{type}/imei/{imei}";
  1849. var schedulePush = await _serviceEtcd.GetValAsync(key).ConfigureAwait(false);
  1850. if (string.IsNullOrWhiteSpace(schedulePush))
  1851. {
  1852. var now = DateTime.Now;
  1853. var timeNextRun = now.Add(TimeSpan.FromSeconds(interval));
  1854. var data = new
  1855. {
  1856. imei,
  1857. create_time = now.ToString("yyyy-MM-dd HH:mm:ss"),
  1858. ttl = interval,
  1859. next_run_time = timeNextRun.ToString("yyyy-MM-dd HH:mm:ss")
  1860. };
  1861. var result = JsonConvert.SerializeObject(data);
  1862. await _serviceEtcd.PutValAsync(key, result, interval, false).ConfigureAwait(false);
  1863. }
  1864. }
  1865. public static bool IsNowInTimeRanges()
  1866. {
  1867. var now = DateTime.Now.TimeOfDay;
  1868. var timeRanges = new List<(TimeSpan Start, TimeSpan End)>
  1869. {
  1870. // 8:00~10:00,12:00~14:00,18:00~20:00,22:00~24:00
  1871. (new TimeSpan(8, 0, 0), new TimeSpan(10, 0, 0)),
  1872. (new TimeSpan(12, 0, 0), new TimeSpan(14, 0, 0)),
  1873. (new TimeSpan(18, 0, 0), new TimeSpan(20, 0, 0)),
  1874. (new TimeSpan(22, 0, 0), new TimeSpan(24, 0, 0))
  1875. };
  1876. return timeRanges.Any(range => now >= range.Start && now <= range.End);
  1877. }
  1878. public static bool IsLastUpdateInTimeRanges(DateTime lastUpdate)
  1879. {
  1880. var now = lastUpdate.TimeOfDay;
  1881. var timeRanges = new List<(TimeSpan Start, TimeSpan End)>
  1882. {
  1883. // 8:00~10:00,12:00~14:00,18:00~20:00,22:00~24:00
  1884. (new TimeSpan(8, 0, 0), new TimeSpan(10, 0, 0)),
  1885. (new TimeSpan(12, 0, 0), new TimeSpan(14, 0, 0)),
  1886. (new TimeSpan(18, 0, 0), new TimeSpan(20, 0, 0)),
  1887. (new TimeSpan(22, 0, 0), new TimeSpan(24, 0, 0))
  1888. };
  1889. return timeRanges.Any(range => now >= range.Start && now <= range.End);
  1890. }
  1891. private async Task SaveAndPushFetalHeartRateEndFreqHeartRateAsync(HisGpsHeartRate heartRate, PregnancyCommonHeartRateModel commonPHR, int upperAlarmThreshold, int lowerAlarmThreshold, string sampleTime, DateTime statStartTime, DateTime statEndTime)
  1892. {
  1893. // 计算胎心=孕妇心率*系数
  1894. /**
  1895. var fetalHeartRate = SafeType.SafeInt(phrValue * commonPHR?.StatModeAvgFprCoefficient!);
  1896. fetalHeartRate = fetalHeartRate > 220 ? 220 : fetalHeartRate; // 胎心的最大值调整为220,超过都按该值220输出
  1897. if (fetalHeartRate >= 220)
  1898. {
  1899. // 先使用最小系数计算
  1900. var statMaxValueFprCoefficient = commonPHR?.StatMaxValueFprCoefficient!;
  1901. var statMinValueFprCoefficient = commonPHR?.StatMinValueFprCoefficient!;
  1902. var coefficient = statMaxValueFprCoefficient < statMinValueFprCoefficient ? statMaxValueFprCoefficient : statMinValueFprCoefficient;
  1903. fetalHeartRate = SafeType.SafeInt(phrValue * coefficient);
  1904. if (fetalHeartRate < 220)
  1905. {
  1906. _logger.LogWarning($"{heartRate.Serialno} 使用极值系数 {coefficient} ,建模数据可能出现异常,请检查");
  1907. }
  1908. else
  1909. {
  1910. fetalHeartRate = 220;
  1911. _logger.LogWarning($"{heartRate.Serialno} 使用所有系数都不能放映实际,建模数据可能出现异常,请检查");
  1912. }
  1913. }
  1914. */
  1915. #region 胎心系数使用基于心率与中位数对比
  1916. var coefficient = 0f;
  1917. // 孕妇心率少于中位数,取StatMinValueFprCoefficient
  1918. if (heartRate.HeartRate < commonPHR!.Mode)
  1919. {
  1920. coefficient = commonPHR.StatMinValueFprCoefficient!;
  1921. _logger.LogInformation($"{heartRate.Serialno} 孕妇心率少于中位数,使用最小值系数 {coefficient}");
  1922. }
  1923. // 孕妇心率大于中位数,取StatMaxValueFprCoefficient与StatModeAvgFprCoefficient中少的那个
  1924. else if (heartRate.HeartRate > commonPHR.Mode)
  1925. {
  1926. if (commonPHR.StatModeAvgFprCoefficient > commonPHR.StatMaxValueFprCoefficient)
  1927. {
  1928. coefficient = commonPHR.StatMaxValueFprCoefficient!;
  1929. _logger.LogInformation($"{heartRate.Serialno} 孕妇心率大于中位数,使用最大值系数 {coefficient}");
  1930. }
  1931. else
  1932. {
  1933. coefficient = commonPHR.StatModeAvgFprCoefficient!;
  1934. _logger.LogInformation($"{heartRate.Serialno} 孕妇心率大于中位数,使用均值系数 {coefficient}");
  1935. }
  1936. }
  1937. else
  1938. {
  1939. coefficient = commonPHR.StatModeAvgFprCoefficient;
  1940. _logger.LogInformation($"{heartRate.Serialno} 孕妇心率等于中位数,使用均值系数 {coefficient}");
  1941. }
  1942. #endregion
  1943. var fetalHeartRate = SafeType.SafeInt(heartRate.HeartRate! * coefficient);
  1944. // 胎心的最大值调整为220,超过都按该值220输出
  1945. fetalHeartRate = fetalHeartRate >= 220 ? 220 : fetalHeartRate;
  1946. var isAbnormal = fetalHeartRate > upperAlarmThreshold ? 1 : (fetalHeartRate < lowerAlarmThreshold ? 2 : 0);
  1947. var phrFreqstatus = await _deviceCacheMgr.GetPregnancyHeartRateFreqStatusAsync(heartRate.Serialno);
  1948. if (phrFreqstatus == null) isAbnormal = 0;
  1949. var statsusDesc = (phrFreqstatus == null) ? "常规" : "高频";
  1950. _logger.LogInformation($"{heartRate.Serialno} 在 {statsusDesc} 状态,生成胎心值:{fetalHeartRate},统计周期:{statStartTime.ToString("yyyy-MM-dd HH:mm:ss")}----{statEndTime.ToString("yyyy-MM-dd HH:mm:ss")}");
  1951. //if (!isFreq)
  1952. //{
  1953. // statStartTime = heartRate.LastUpdate;
  1954. //
  1955. //}
  1956. // 保存到 数据服务 MySQL 数据库
  1957. HisGpsFetalHeartRate gpsFetalHeartRate = new()
  1958. {
  1959. FetalHeartRateId = Guid.NewGuid().ToString("D"),
  1960. PersonId = commonPHR!.PersonId,
  1961. Serialno = heartRate.Serialno,
  1962. HeartRate = fetalHeartRate,
  1963. SampleTime = sampleTime.Length > 10 ? sampleTime.Substring(0, 10) : sampleTime,
  1964. IsAbnormal = isAbnormal,
  1965. StatStartTime = statStartTime,
  1966. StatEndTime = statEndTime,//commonPHR.StatEndTime,
  1967. CreateTime = DateTime.Now,
  1968. Method = 1,
  1969. IsDisplay = 1,
  1970. DeviceKey = commonPHR!.DeviceKey
  1971. };
  1972. await _hisFetalHeartApiClient.AddAsync(gpsFetalHeartRate).ConfigureAwait(false);
  1973. // 推送到api/v1/open/OpenIot/SetFetalHeartRateConfig
  1974. // 推送最后一条常规心率计算的胎心数据到iot设备
  1975. #region 推送最后一条常规心率计算的胎心数据到iot设备
  1976. // 高频(<=12)-常规
  1977. var lastPhr = await _serviceTDengine.GetLastAsync<PregnancyHeartRateModel>(heartRate.Serialno);
  1978. if (lastPhr.MessageId == heartRate.MessageId && phrFreqstatus == null)
  1979. {
  1980. await _serviceIotApi.SetFetalHeartRateConfig(heartRate.Serialno, fetalHeartRate, sampleTime, isAbnormal);
  1981. _logger.LogInformation($"{heartRate.Serialno} 推送最后一条常规心率计算的胎心数据到iot设备,高频(<=12)-常规");
  1982. }
  1983. // 高频(13)-常规-高频(13)
  1984. if (phrFreqstatus != null)
  1985. {
  1986. var phr = await _serviceTDengine.GetBySerialNoAsync<PregnancyHeartRateModel>(heartRate.Serialno, 1);
  1987. phr = phr.OrderByDescending(i => i.LastUpdate).ToList();
  1988. // 获取高频数据
  1989. var freqCollection = new List<PregnancyHeartRateModel>();
  1990. PregnancyHeartRateModel? previousItem = null;
  1991. foreach (var item in phr)
  1992. {
  1993. if (previousItem != null)
  1994. {
  1995. var timeNextDiff = (previousItem!.LastUpdate - item.LastUpdate).TotalSeconds;
  1996. if (timeNextDiff <= 60)
  1997. {
  1998. freqCollection.Add(item);
  1999. }
  2000. }
  2001. // 高频最后一条
  2002. if (lastPhr.MessageId == item.MessageId)
  2003. {
  2004. freqCollection.Add(item);
  2005. }
  2006. previousItem = item;
  2007. }
  2008. //去除高频
  2009. foreach (var item in freqCollection)
  2010. {
  2011. phr.Remove(item);
  2012. }
  2013. lastPhr = phr.FirstOrDefault();
  2014. if (lastPhr?.MessageId == heartRate.MessageId)
  2015. {
  2016. await _serviceIotApi.SetFetalHeartRateConfig(heartRate.Serialno, fetalHeartRate, sampleTime, isAbnormal);
  2017. _logger.LogInformation($"{heartRate.Serialno} 推送最后一条常规心率计算的胎心数据到iot设备,高频(13)-常规-高频(13)");
  2018. }
  2019. }
  2020. #endregion
  2021. #region 高频心率计算胎心数据到iot设备
  2022. // 高频(17) ,连续12个高频正常,也不停止且数据偏高和偏低也推送到iot
  2023. if (phrFreqstatus != null && isAbnormal != 0)
  2024. {
  2025. await _serviceIotApi.SetFetalHeartRateConfig(heartRate.Serialno, fetalHeartRate, sampleTime, isAbnormal);
  2026. }
  2027. #endregion
  2028. var device = await _deviceCacheMgr.GetDeviceBySerialNoAsync(heartRate.Serialno).ConfigureAwait(false);
  2029. var fhrMsgId = $"{heartRate.Serialno}-{sampleTime}-{Guid.NewGuid().ToString("D")[^3..]}";
  2030. var fhrMsgTime = DateTimeUtil.GetDateTimeFromUnixTimeMilliseconds(long.Parse(sampleTime.Length < 13 ? sampleTime.PadRight(13, '0') : sampleTime)).ToString("yyyy-MM-dd HH:mm:ss");
  2031. // 胎心数据推送到第三方
  2032. var topic = "topic.push.third";
  2033. var fhrThridMsg = new
  2034. {
  2035. messageId = fhrMsgId,
  2036. topic = topic,
  2037. time = fhrMsgTime,
  2038. data = new
  2039. {
  2040. imei = heartRate.Serialno,
  2041. value = fetalHeartRate,
  2042. isAbnormal,
  2043. type = "fetalHeart"
  2044. }
  2045. };
  2046. await _serviceMqProcess.ProcessIMEIEventMessageAsync(fhrMsgId, topic, 31, fhrThridMsg).ConfigureAwait(false);
  2047. // 胎心数据推送到微信
  2048. if (isAbnormal != 0)
  2049. {
  2050. topic = "topic.push.wx";
  2051. var fhrMsg = new
  2052. {
  2053. messageId = fhrMsgId,
  2054. topic = topic,
  2055. time = fhrMsgTime,
  2056. data = new
  2057. {
  2058. deviceId = device?.DeviceId,
  2059. imei = heartRate.Serialno,
  2060. alarmTypeId = 12,
  2061. alarmDeviceName = heartRate.Serialno,
  2062. alarmRemarks = JsonConvert.SerializeObject(new { fetalHeartValue = fetalHeartRate, isAbnormal = isAbnormal }),
  2063. address = string.Empty,
  2064. deviceKey = device?.DeviceId
  2065. }
  2066. };
  2067. await _serviceMqProcess.ProcessIMEIEventMessageAsync(fhrMsgId, topic, fhrMsg).ConfigureAwait(false);
  2068. }
  2069. }
  2070. /// <summary>
  2071. /// 去除高频数据
  2072. /// </summary>
  2073. /// <param name="phr"></param>
  2074. /// <param name="highFreqSampleInterva"></param>
  2075. /// <returns></returns>
  2076. private static List<PregnancyHeartRateModel> GetNonFreqPregnancyHeartRate(List<PregnancyHeartRateModel> phr, int highFreqSampleInterval)
  2077. {
  2078. //phr = phr.OrderByDescending(i => i.LastUpdate).ToList();
  2079. //var result = new List<PregnancyHeartRateModel>();
  2080. //PregnancyHeartRateModel? previousItem = null;
  2081. //foreach (var item in phr)
  2082. //{
  2083. // if (previousItem != null)
  2084. // {
  2085. // var timeNextDiff =(previousItem!.LastUpdate - item.LastUpdate).TotalSeconds;
  2086. // if (timeNextDiff > highFreqSampleInterval)
  2087. // {
  2088. // result.Add(previousItem);
  2089. // }
  2090. // }
  2091. // previousItem = item;
  2092. //}
  2093. //// 添加上一个
  2094. //if (previousItem != null)
  2095. //{
  2096. // result.Add(previousItem);
  2097. //}
  2098. //return result;
  2099. #region 反向
  2100. var phr1 = phr.OrderByDescending(i => i.LastUpdate).ToList();
  2101. var result = new List<PregnancyHeartRateModel>();
  2102. PregnancyHeartRateModel? previousItem1 = null;
  2103. foreach (var item in phr1)
  2104. {
  2105. if (previousItem1 != null)
  2106. {
  2107. var timeNextDiff = (previousItem1!.LastUpdate - item.LastUpdate).TotalSeconds;
  2108. if (timeNextDiff > highFreqSampleInterval)
  2109. {
  2110. result.Add(previousItem1);
  2111. }
  2112. }
  2113. previousItem1 = item;
  2114. }
  2115. // 添加上一个
  2116. if (previousItem1 != null)
  2117. {
  2118. result.Add(previousItem1);
  2119. }
  2120. #endregion
  2121. #region 正向
  2122. var phr2 = phr.OrderByDescending(i => i.LastUpdate).ToList(); ;
  2123. var freqCollection = new List<PregnancyHeartRateModel>();
  2124. PregnancyHeartRateModel? previousItem = null;
  2125. foreach (var item in phr2)
  2126. {
  2127. if (previousItem != null)
  2128. {
  2129. var timeNextDiff = (previousItem!.LastUpdate - item.LastUpdate).TotalSeconds;
  2130. if (timeNextDiff <= highFreqSampleInterval)
  2131. {
  2132. freqCollection.Add(item);
  2133. }
  2134. }
  2135. previousItem = item;
  2136. }
  2137. //去除高频
  2138. foreach (var item in freqCollection)
  2139. {
  2140. phr2.Remove(item);
  2141. }
  2142. #endregion
  2143. // 交集
  2144. var commonElements = phr2.Intersect(result).ToList();
  2145. return commonElements;
  2146. }
  2147. /// <summary>
  2148. /// 获取高频数据
  2149. /// </summary>
  2150. /// <param name="phr"></param>
  2151. /// <param name="highFreqSampleInterval"></param>
  2152. /// <returns></returns>
  2153. private static List<PregnancyHeartRateModel> GetFreqPregnancyHeartRate(List<PregnancyHeartRateModel> phr, int highFreqSampleInterval)
  2154. {
  2155. phr = phr.OrderByDescending(i => i.LastUpdate).ToList();
  2156. var freqCollection = new List<PregnancyHeartRateModel>();
  2157. PregnancyHeartRateModel? previousItem = null;
  2158. foreach (var item in phr)
  2159. {
  2160. if (previousItem != null)
  2161. {
  2162. var timeNextDiff = (previousItem.LastUpdate - item.LastUpdate).TotalSeconds;
  2163. if (timeNextDiff <= highFreqSampleInterval)
  2164. {
  2165. freqCollection.Add(previousItem);
  2166. }
  2167. }
  2168. previousItem = item;
  2169. }
  2170. // 检查最后一条是否高频
  2171. if (previousItem != null && (phr.Last().LastUpdate - previousItem.LastUpdate).TotalSeconds <= highFreqSampleInterval)
  2172. {
  2173. freqCollection.Add(previousItem);
  2174. }
  2175. return freqCollection;
  2176. }
  2177. public async Task CalculateNormalFetalHeartRateIntervalAsync(HisGpsHeartRate heartRate, PregnancyCommonHeartRateModel commonPHR,int highFreqSampleInterval)
  2178. {
  2179. var daysPhr = await _serviceTDengine.GetBySerialNoAsync<PregnancyHeartRateModel>(heartRate.Serialno, 7);
  2180. var now = DateTime.Now;
  2181. var filteredPhr = daysPhr.Where(i => i.LastUpdate >= heartRate.LastUpdate && i.LastUpdate <= now).ToList();
  2182. // 去除高频
  2183. var normalPhr = GetNonFreqPregnancyHeartRate(filteredPhr, highFreqSampleInterval).OrderBy(i => i.LastUpdate);
  2184. if (normalPhr.ToList().Count==0)
  2185. {
  2186. _logger.LogWarning($"{heartRate.Serialno} 时间段{heartRate.LastUpdate}-{now},去除高频心率数据后,没有常规数据,不计算常规胎心数据");
  2187. return;
  2188. }
  2189. var startPhr = normalPhr.First();
  2190. var endPhr = normalPhr.Last();
  2191. //var sampleTime = GetSampleTimeFromLastUpdate((DateTime)heartRate.LastUpdate!, INTERVAL_FHR);
  2192. // 数据统计边界
  2193. var boundaryStatStartTime = GetSampleTimeFromLastUpdate((DateTime)startPhr.LastUpdate!, INTERVAL_FHR);
  2194. var boundaryStatEndTime = GetSampleTimeFromLastUpdate((DateTime)endPhr.LastUpdate!, INTERVAL_FHR).AddMinutes(INTERVAL_FHR);
  2195. _logger.LogInformation($"{heartRate.Serialno} 统计边界{boundaryStatStartTime}-{boundaryStatEndTime}");
  2196. try
  2197. {
  2198. //var CalNow = DateTime.Now;
  2199. //var during = TimeSpan.FromSeconds(300); //5分钟
  2200. var c = 0;
  2201. while (true)
  2202. {
  2203. //if (DateTime.Now - CalNow > during)
  2204. //{
  2205. // _logger.LogInformation($"{heartRate.Serialno} 超过1分钟,迭代完成跳出循环 ");
  2206. // break;
  2207. //}
  2208. await Task.Delay(TimeSpan.FromSeconds(1));
  2209. var segmentStatStartTime = boundaryStatStartTime.AddMinutes(c * INTERVAL_FHR);
  2210. var segmentStatEndTime = segmentStatStartTime.AddMinutes(INTERVAL_FHR);
  2211. c++;
  2212. var statStartTime = segmentStatStartTime;
  2213. var statEndTime = segmentStatEndTime;
  2214. _logger.LogInformation($"{heartRate.Serialno} 当前统计周期{statStartTime.ToString("yyyy-MM-dd HH:mm:ss")}-{statEndTime.ToString("yyyy-MM-dd HH:mm:ss")}");
  2215. if (statEndTime > boundaryStatEndTime)
  2216. {
  2217. _logger.LogInformation($"{heartRate.Serialno} 超过时间边界,迭代完成跳出循环 ");
  2218. break;
  2219. }
  2220. var segmentPhr = normalPhr
  2221. .Where(i => i.LastUpdate <= statEndTime && i.LastUpdate >= statStartTime)
  2222. .ToList();
  2223. if (segmentPhr.Count == 0)
  2224. {
  2225. // 跳出当次迭代,进入下次迭代
  2226. _logger.LogWarning($"{heartRate.Serialno} 统计周期:{statStartTime.ToString("yyyy-MM-dd HH:mm:ss")}-{statEndTime.ToString("yyyy-MM-dd HH:mm:ss")} 孕妇心率数据不足,{segmentPhr.Count}条记录,不处理");
  2227. continue;
  2228. }
  2229. _logger.LogInformation($"{heartRate.Serialno} 当前统计周期{statStartTime.ToString("yyyy-MM-dd HH:mm:ss")}-{statEndTime.ToString("yyyy-MM-dd HH:mm:ss")},对应的常规心率ID{string.Join(",", segmentPhr.Select(i=>i.MessageId))}");
  2230. var sampleTime = DateTimeUtil.ConvertToTimeStamp(segmentStatStartTime).ToString();
  2231. sampleTime = sampleTime.Length > 10 ? sampleTime.Substring(0, 10) : sampleTime;
  2232. //检测 是否存在,不存在则处理
  2233. GeneralParam param = new()
  2234. {
  2235. Filters = new List<QueryFilterCondition>
  2236. {
  2237. new ()
  2238. {
  2239. Key=nameof(HisGpsFetalHeartRate.Serialno),
  2240. Value=heartRate.Serialno,
  2241. ValueType=QueryValueTypeEnum.String,
  2242. Operator=QueryOperatorEnum.Equal
  2243. },
  2244. new ()
  2245. {
  2246. Key=nameof(HisGpsFetalHeartRate.SampleTime),
  2247. Value=sampleTime,
  2248. ValueType=QueryValueTypeEnum.String,
  2249. Operator=QueryOperatorEnum.Equal
  2250. },
  2251. },
  2252. OrderBys = new List<OrderByCondition>
  2253. {
  2254. new (){
  2255. IsDesc=true,
  2256. Key=nameof(HisGpsFetalHeartRate.SampleTime)
  2257. }
  2258. }
  2259. };
  2260. var fhr = await _hisFetalHeartApiClient.GetFirstAsync(param, heartRate.Serialno[^2..], null, new RequestHeader { RequestId = Guid.NewGuid().ToString("D") });
  2261. if (fhr == null)
  2262. {
  2263. var avgPhr = segmentPhr.Count == 1
  2264. ? segmentPhr.First().PregnancyHeartRate
  2265. : segmentPhr.Average(i => i.PregnancyHeartRate);
  2266. heartRate.HeartRate = (int)avgPhr;
  2267. #region 胎心系数使用基于心率与中位数对比
  2268. var coefficient = 0f;
  2269. // 孕妇心率少于中位数,取StatMinValueFprCoefficient
  2270. if (heartRate.HeartRate < commonPHR!.Mode)
  2271. {
  2272. coefficient = commonPHR.StatMinValueFprCoefficient!;
  2273. _logger.LogInformation($"{heartRate.Serialno} 孕妇心率少于中位数,使用最小值系数 {coefficient}");
  2274. }
  2275. // 孕妇心率大于中位数,取StatMaxValueFprCoefficient与StatModeAvgFprCoefficient中少的那个
  2276. else if (heartRate.HeartRate > commonPHR.Mode)
  2277. {
  2278. if (commonPHR.StatModeAvgFprCoefficient > commonPHR.StatMaxValueFprCoefficient)
  2279. {
  2280. coefficient = commonPHR.StatMaxValueFprCoefficient!;
  2281. _logger.LogInformation($"{heartRate.Serialno} 孕妇心率大于中位数,使用最大值系数 {coefficient}");
  2282. }
  2283. else
  2284. {
  2285. coefficient = commonPHR.StatModeAvgFprCoefficient!;
  2286. _logger.LogInformation($"{heartRate.Serialno} 孕妇心率大于中位数,使用均值系数 {coefficient}");
  2287. }
  2288. }
  2289. else
  2290. {
  2291. coefficient = commonPHR.StatModeAvgFprCoefficient;
  2292. _logger.LogInformation($"{heartRate.Serialno} 孕妇心率等于中位数,使用均值系数 {coefficient}");
  2293. }
  2294. #endregion
  2295. #region 胎心阈值判断
  2296. //var fetalHeartRate = SafeType.SafeInt(heartRate.HeartRate * coefficient);
  2297. //// 胎心的最大值调整为220,超过都按该值220输出
  2298. //// fetalHeartRate = fetalHeartRate>= 220 ? 220 : fetalHeartRate;
  2299. //if (fetalHeartRate > 220)
  2300. //{
  2301. // fetalHeartRate = 220;
  2302. // _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")}");
  2303. //}
  2304. //// 胎心的最小值调整为90,超过都按该值90
  2305. //if (fetalHeartRate < 90)
  2306. //{
  2307. // fetalHeartRate = 90;
  2308. // _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")}");
  2309. //}
  2310. // 计算胎心率并进行边界检查
  2311. var fetalHeartRate = Math.Clamp((int)(heartRate.HeartRate * coefficient), 90, 220);
  2312. if (fetalHeartRate != (int)(heartRate.HeartRate * coefficient))
  2313. {
  2314. _logger.LogWarning($"{heartRate.Serialno} 胎心率超出范围, 按修正值 {fetalHeartRate} 输出, 计算因子:孕妇心率 {heartRate.HeartRate}, 系数 {coefficient}, 周期 {statStartTime} - {statEndTime}");
  2315. }
  2316. #endregion
  2317. _logger.LogInformation($"{heartRate.Serialno} 在 常规 状态,生成胎心值:{fetalHeartRate},统计周期:{statStartTime.ToString("yyyy-MM-dd HH:mm:ss")}----{statEndTime.ToString("yyyy-MM-dd HH:mm:ss")}");
  2318. var isAbnormal = 0;
  2319. #region 保存到Mysql数据库
  2320. // 保存到 数据服务 MySQL 数据库
  2321. HisGpsFetalHeartRate gpsFetalHeartRate = new()
  2322. {
  2323. FetalHeartRateId = Guid.NewGuid().ToString("D"),
  2324. PersonId = commonPHR!.PersonId,
  2325. Serialno = heartRate.Serialno,
  2326. HeartRate = fetalHeartRate,
  2327. SampleTime = sampleTime,
  2328. IsAbnormal = isAbnormal,
  2329. StatStartTime = statStartTime,
  2330. StatEndTime = statEndTime,//commonPHR.StatEndTime,
  2331. CreateTime = DateTime.Now,
  2332. Method = 1,
  2333. IsDisplay = 1,
  2334. DeviceKey = commonPHR!.DeviceKey
  2335. };
  2336. await _hisFetalHeartApiClient.AddAsync(gpsFetalHeartRate).ConfigureAwait(false);
  2337. #endregion
  2338. #region 推送最后一条常规心率计算的胎心数据到iot设备
  2339. var lastPhr = await _serviceTDengine.GetLastAsync<PregnancyHeartRateModel>(heartRate.Serialno);
  2340. if (segmentStatEndTime == boundaryStatEndTime)
  2341. {
  2342. await _serviceIotApi.SetFetalHeartRateConfig(heartRate.Serialno, fetalHeartRate, sampleTime, isAbnormal);
  2343. _logger.LogInformation($"{heartRate.Serialno} 推送最后一条常规心率计算的胎心数据到iot设备");
  2344. }
  2345. #endregion
  2346. #region 推送到第三方
  2347. var device = await _deviceCacheMgr.GetDeviceBySerialNoAsync(heartRate.Serialno).ConfigureAwait(false);
  2348. var fhrMsgId = $"{heartRate.Serialno}-{sampleTime}-{Guid.NewGuid().ToString("D")[^3..]}";
  2349. var fhrMsgTime = DateTimeUtil.GetDateTimeFromUnixTimeMilliseconds(long.Parse(sampleTime.Length < 13 ? sampleTime.PadRight(13, '0') : sampleTime)).ToString("yyyy-MM-dd HH:mm:ss");
  2350. var topic = "topic.push.third";
  2351. var fhrThridMsg = new
  2352. {
  2353. messageId = fhrMsgId,
  2354. topic = topic,
  2355. time = fhrMsgTime,
  2356. data = new
  2357. {
  2358. imei = heartRate.Serialno,
  2359. value = fetalHeartRate,
  2360. isAbnormal,
  2361. type = "fetalHeart"
  2362. }
  2363. };
  2364. await _serviceMqProcess.ProcessIMEIEventMessageAsync(fhrMsgId, topic, 31, fhrThridMsg).ConfigureAwait(false);
  2365. #endregion
  2366. #region 推送到微信
  2367. if (isAbnormal != 0)
  2368. {
  2369. topic = "topic.push.wx";
  2370. var fhrMsg = new
  2371. {
  2372. messageId = fhrMsgId,
  2373. topic = topic,
  2374. time = fhrMsgTime,
  2375. data = new
  2376. {
  2377. deviceId = device?.DeviceId,
  2378. imei = heartRate.Serialno,
  2379. alarmTypeId = 12,
  2380. alarmDeviceName = heartRate.Serialno,
  2381. alarmRemarks = JsonConvert.SerializeObject(new { fetalHeartValue = fetalHeartRate, isAbnormal = isAbnormal }),
  2382. address = string.Empty,
  2383. deviceKey = device?.DeviceId
  2384. }
  2385. };
  2386. await _serviceMqProcess.ProcessIMEIEventMessageAsync(fhrMsgId, topic, fhrMsg).ConfigureAwait(false);
  2387. }
  2388. #endregion
  2389. }
  2390. else
  2391. {
  2392. _logger.LogInformation($"{heartRate.Serialno},统计周期:{statStartTime.ToString("yyyy-MM-dd HH:mm:ss")}----{statEndTime.ToString("yyyy-MM-dd HH:mm:ss")} ,胎心已处理");
  2393. }
  2394. //await Task.Delay(TimeSpan.FromSeconds(1));
  2395. // 跳出循环
  2396. if (statEndTime.ToString("yyyyMMddHHmm") == boundaryStatEndTime.ToString("yyyyMMddHHmm"))
  2397. {
  2398. _logger.LogInformation($"{heartRate.Serialno} 迭代完成跳出循环 ");
  2399. break;
  2400. }
  2401. //if (statEndTime>= boundaryStatEndTime)
  2402. //{
  2403. // _logger.LogInformation($"{heartRate.Serialno} 时间边界,迭代完成跳出循环 ");
  2404. // break;
  2405. //}
  2406. }
  2407. }
  2408. catch (Exception ex)
  2409. {
  2410. _logger.LogError($"处理心率数据时发生错误: {ex.Message}");
  2411. }
  2412. //for (var segmentStatStartTime = boundaryStatStartTime;
  2413. // segmentStatStartTime < boundaryStatEndTime;
  2414. // segmentStatStartTime = segmentStatStartTime.AddMinutes(INTERVAL_FHR))
  2415. //{
  2416. // var segmentStatEndTime = segmentStatStartTime.AddMinutes(INTERVAL_FHR);
  2417. // var segmentPhr = daysPhr
  2418. // .Where(i => i.LastUpdate <= segmentStatEndTime && i.LastUpdate >= segmentStatStartTime)
  2419. // .ToList();
  2420. // if (segmentStatEndTime == boundaryStatEndTime)
  2421. // {
  2422. // break;
  2423. // }
  2424. //}
  2425. }
  2426. private DateTime GetSampleTimeFromLastUpdate(DateTime lastUpdate,int interval)
  2427. {
  2428. DateTime nowInterval = lastUpdate;
  2429. if (nowInterval.Second > 0)
  2430. {
  2431. nowInterval = nowInterval.AddMinutes(1);
  2432. }
  2433. // 计算last_update到上一间隔的分钟数
  2434. int minutesToSubtract = nowInterval.Minute % interval;
  2435. // 计算上一间隔的时间
  2436. DateTime previousInterval = nowInterval.AddMinutes(-minutesToSubtract).AddSeconds(-nowInterval.Second).AddMilliseconds(-nowInterval.Millisecond);
  2437. return previousInterval;
  2438. }
  2439. }
  2440. }