From aa5c68bb744c6e4e8e13421a15b754a026fd9e87 Mon Sep 17 00:00:00 2001 From: H Vs Date: Mon, 29 Jul 2024 02:15:06 +0800 Subject: [PATCH] =?UTF-8?q?=E8=B0=83=E6=95=B4=E8=83=8E=E5=8A=A8=E8=AE=A1?= =?UTF-8?q?=E7=AE=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Resolver/PregnancyHeartRateResolver.cs | 63 +++--- HealthMonitor.WebApi/Worker.cs | 200 +++++++++++++++++- 2 files changed, 234 insertions(+), 29 deletions(-) diff --git a/HealthMonitor.Service/Resolver/PregnancyHeartRateResolver.cs b/HealthMonitor.Service/Resolver/PregnancyHeartRateResolver.cs index 29bcd69..42ea17f 100644 --- a/HealthMonitor.Service/Resolver/PregnancyHeartRateResolver.cs +++ b/HealthMonitor.Service/Resolver/PregnancyHeartRateResolver.cs @@ -50,6 +50,8 @@ namespace HealthMonitor.Service.Resolver private readonly MqProcessLogic _serviceMqProcess; + private static int[] SCHEDULE_HOUR = new int[] { 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24 }; + @@ -323,10 +325,10 @@ namespace HealthMonitor.Service.Resolver #endregion } */ - - + + #region 高频心率计算 - + //// 获取最近的两个记录,并计算它们的 LastUpdate 时间差 //var firstTwoPhr = phr.OrderByDescending(i => i.LastUpdate).Take(2).Select(i => i.LastUpdate).ToList(); //var timeDiff = firstTwoPhr[0] - firstTwoPhr[1]; @@ -337,7 +339,7 @@ namespace HealthMonitor.Service.Resolver //// 高频心率启动 //if (timeDiffInSeconds<=highFreqSampleInterval) //{ - + // var phrFreqstatus =await _deviceCacheMgr.GetPregnancyHeartRateFreqStatusAsync(heartRate.Serialno); // if (phrFreqstatus == null) // { @@ -418,7 +420,7 @@ namespace HealthMonitor.Service.Resolver // _logger.LogInformation($"结束高频心率状态 timeDiffInSeconds {timeDiffInSeconds},highFreqSampleInterval:{highFreqSampleInterval}"); // } //} - + #endregion #region 计算胎心和胎动数据 @@ -513,7 +515,7 @@ namespace HealthMonitor.Service.Resolver // 正常心率 else { - + // 上15分钟的数据 // 获取当前时间 DateTime nowInterval = (DateTime)heartRate.LastUpdate!; @@ -587,7 +589,7 @@ namespace HealthMonitor.Service.Resolver await _serviceIotApi.SetFetalHeartRateConfig(heartRate.Serialno, fetalHeartRate, sampleTimeFHR, fetalHeartRateIsAbnormal); // 推送送微信 - if (fetalHeartRateIsAbnormal!=0) + if (fetalHeartRateIsAbnormal != 0) { var device = await _deviceCacheMgr.GetDeviceBySerialNoAsync(heartRate.Serialno).ConfigureAwait(false); var fhrMsgId = $"{heartRate.Serialno}-{sampleTimeFHR}-{Guid.NewGuid().ToString("D")[^3..]}"; @@ -610,14 +612,15 @@ namespace HealthMonitor.Service.Resolver }; await _serviceMqProcess.ProcessIMEIEventMessageAsync(fhrMsgId, topic, fhrMsg).ConfigureAwait(false); } - + } } #endregion - #region 计算胎动数据 + #region 计算胎动数据(按心率时间LastUpdate) + /** _logger.LogInformation($"{heartRate.Serialno} 计算胎动数据 "); var fetalMovementNow = (DateTime)heartRate.LastUpdate!; @@ -751,10 +754,11 @@ namespace HealthMonitor.Service.Resolver } } + */ #endregion } #endregion - + #region 定时计算胎心数据触发器 {interval} 秒后 @@ -784,23 +788,30 @@ namespace HealthMonitor.Service.Resolver //await SetIntervalTriggerAsync(fetalKey, heartRate.Serialno, (long)timeDifference.TotalSeconds); #endregion - #region 定时计算胎动数据触发器 0 点开始 - //var fetalMovementKey = $"health_monitor/schedule_push/cal_fetal_movement/imei/{heartRate.Serialno}"; + #region 定时计算胎动数据触发器两小时间隔开始 + var fetalMovementKey = $"health_monitor/schedule_push/cal_fetal_movement/imei/{heartRate.Serialno}"; ///// 计算 0 点秒数 - //DateTime now = DateTime.Now; - //var rand = new Random(); - //var pushSec = rand.Next(59); - //int pushMin = int.TryParse(heartRate.Serialno.AsSpan(heartRate.Serialno.Length - 1), out pushMin) ? pushMin : 10; - //DateTime nextRunTime = new (now.Year, now.Month, now.Day, 0, pushMin, pushSec); - //TimeSpan timeUntilNextRun = nextRunTime - now; - //if (timeUntilNextRun < TimeSpan.Zero) - //{ - // timeUntilNextRun = timeUntilNextRun.Add(TimeSpan.FromDays(1)); - // nextRunTime += timeUntilNextRun; - //} - //var ttl = (long)timeUntilNextRun.TotalSeconds; - - //await SetIntervalTriggerAsync(fetalMovementKey, heartRate.Serialno, ttl); + var fetalMovementLastUpdate = (DateTime)heartRate.LastUpdate!; + DateTime fmScheduleNow = DateTime.Now; + // 小于两小时 + if (fmScheduleNow > fetalMovementLastUpdate && (fmScheduleNow - fetalMovementLastUpdate).TotalHours <= 2) + { + var rand = new Random(); + var pushSec = rand.Next(59); + int pushMin = int.TryParse(heartRate.Serialno.AsSpan(heartRate.Serialno.Length - 1), out pushMin) ? pushMin : 10; + + var scheduleHourDiff = SCHEDULE_HOUR + .Where(h => h > fetalMovementLastUpdate.Hour) + .OrderBy(h => h - fetalMovementLastUpdate.Hour) + .FirstOrDefault() - fetalMovementLastUpdate.Hour; + var scheduleTime = fetalMovementLastUpdate.AddHours(scheduleHourDiff); + + DateTime nextRunTime = new(scheduleTime.Year, scheduleTime.Month, scheduleTime.Day, scheduleTime.Hour, pushMin, pushSec); + TimeSpan timeUntilNextRun = nextRunTime - fmScheduleNow; + var ttl = (long)timeUntilNextRun.TotalSeconds; + + await SetIntervalTriggerAsync(fetalMovementKey, heartRate.Serialno, ttl); + } #endregion } diff --git a/HealthMonitor.WebApi/Worker.cs b/HealthMonitor.WebApi/Worker.cs index 68b885e..c6f4972 100644 --- a/HealthMonitor.WebApi/Worker.cs +++ b/HealthMonitor.WebApi/Worker.cs @@ -12,6 +12,7 @@ using HealthMonitor.Service.Biz; using HealthMonitor.Service.Biz.db; using HealthMonitor.Service.Cache; using HealthMonitor.Service.Etcd; +using HealthMonitor.Service.MessageQueue; using HealthMonitor.Service.Sub; using Microsoft.AspNetCore.Mvc.RazorPages; using Microsoft.EntityFrameworkCore.Metadata.Internal; @@ -48,6 +49,9 @@ namespace HealthMonitor.WebApi private readonly FetalMovementNormalValueRangeCacheManager _mgrFetalMovementNormalValueRangeCache; private readonly GpsLocationHistoryAccessorClient _hisFetalHeartApiClient; + private readonly GpsLocationHistoryAccessorClient _hisFetalMovementApiClient; + + private readonly MqProcessLogic _serviceMqProcess; private CancellationTokenSource _tokenSource = default!; @@ -56,7 +60,8 @@ namespace HealthMonitor.WebApi IOptions optionBoodPressResolver, PackageProcess processor, TDengineDataSubcribe tdEngineDataSubcribe, TDengineService serviceDengine, GpsLocationHistoryAccessorClient hisFetalHeartApiClient, - FetalMovementNormalValueRangeCacheManager fetalMovementNormalValueRangeCacheMgr, + GpsLocationHistoryAccessorClient hisFetalMovementApiClient, + FetalMovementNormalValueRangeCacheManager fetalMovementNormalValueRangeCacheMgr, MqProcessLogic serviceMqProcess, HttpHelper httpHelper, EtcdService serviceEtcd, DeviceCacheManager deviceCacheMgr) { _logger = logger; @@ -72,6 +77,8 @@ namespace HealthMonitor.WebApi _personCacheMgr = personCacheMgr; _deviceCacheMgr = deviceCacheMgr; _hisFetalHeartApiClient = hisFetalHeartApiClient; + _hisFetalMovementApiClient = hisFetalMovementApiClient; + _serviceMqProcess = serviceMqProcess; _mgrFetalMovementNormalValueRangeCache = fetalMovementNormalValueRangeCacheMgr; } @@ -371,7 +378,7 @@ namespace HealthMonitor.WebApi // .Select(i => i.InitialMovement) // .FirstOrDefault() // ; - + // var fetalMovementValue = fetalMovementMapValue * duringMins * 2 / 120; // // 四舍五入 // var fetalMovement= (int)Math.Round(fetalMovementValue, 0, MidpointRounding.AwayFromZero); @@ -388,7 +395,7 @@ namespace HealthMonitor.WebApi // { // new () // { - // Key=nameof(HisGpsFetalHeartRate.Serialno), + // Key=nameof(HisGpsFetalimeiDel), // Value=imeiDel, // ValueType=QueryValueTypeEnum.String, // Operator=QueryOperatorEnum.Equal @@ -422,7 +429,177 @@ namespace HealthMonitor.WebApi // #endregion //} + #region 胎动延时计算 + var watchConfig = await _deviceCacheMgr.GetGpsDeviceWatchConfigCacheObjectBySerialNoAsync(imeiDel, "0067"); + var isFetalHeartEnable = watchConfig != null && (int)watchConfig["enabled"]! == 1; + if (isFetalHeartEnable) + { + + + var edoc = DateTimeUtil.ToDateTime(watchConfig!["EDOC"]!.ToString()); + // 已经建模 + var commonPHR = await _serviceTDengine.GetLastAsync(imeiDel); + if (commonPHR != null) + { + var phr = await _serviceTDengine.GetBySerialNoAsync(imeiDel, 7); + _logger.LogInformation($"{imeiDel} 计算胎动数据 "); + + var fmNow = DateTime.Now; + // 两小时前 + var fmNowSubtract = fmNow.AddMinutes(-fmNow.Minute).AddSeconds(-fmNow.Second).AddMilliseconds(-fmNow.Millisecond); + + var fetalMovementSampleTime = DateTimeUtil.ConvertToTimeStamp(fmNowSubtract).ToString()[..10]; + + // 统计开始时间 + var statStartTime = fmNowSubtract.AddHours(-2); + // 统计结束时间 + var statEndTime = fmNowSubtract; + + var isFetalMovementExisted = await _deviceCacheMgr.FetalMovementIsExistedAsync(imeiDel, fetalMovementSampleTime); + _logger.LogInformation($"{imeiDel} 胎动记录{isFetalMovementExisted},数据采样时间:{fetalMovementSampleTime}|{fmNowSubtract.ToString("yyyy-MM-dd HH:mm:ss")}, 周期:{statStartTime}-{statEndTime} 开始"); + if (!isFetalMovementExisted) + { + /// 开始计算 + var phrRange = phr.Where(i => i.LastUpdate >= statStartTime && i.LastUpdate <= statEndTime) + .OrderByDescending(i => i.LastUpdate) + .Select(i => i.LastUpdate) + .ToList(); + // 判断是否有持续佩戴 + if (phrRange.Count >= 2) + { + var duringMins = Math.Abs((phrRange.First() - statStartTime).TotalMinutes); + //在餐后时间段(8:00~10:00,12:00~14:00,18:00~20:00,22:00~24:00)取中间值。其他时间段取正常起始值 + bool isInTimeRanges = IsLastUpdateInTimeRanges(statEndTime); + + int pregnancyWeeks = (DateTime.Now - edoc.AddDays(-280)).Days / 7; + if (pregnancyWeeks >= 12 && pregnancyWeeks <= 50) + { + var fetalMovementMap = _mgrFetalMovementNormalValueRangeCache.GetFetalMovements(); + + var fetalMovementMapValue = isInTimeRanges ? fetalMovementMap + .Where(i => + i.PregnancyPeriod![0] <= pregnancyWeeks && + i.PregnancyPeriod[1] >= pregnancyWeeks) + .Select(i => i.MedianMovement) + .FirstOrDefault() + : + fetalMovementMap + .Where(i => + i.PregnancyPeriod![0] <= pregnancyWeeks && + i.PregnancyPeriod[1] >= pregnancyWeeks) + .Select(i => i.InitialMovement) + .FirstOrDefault() + ; + + var fetalMovementValue = (fetalMovementMapValue * duringMins * 2) / 120; + // 四舍五入 + var fetalMovement = (int)Math.Round(fetalMovementValue, 0, MidpointRounding.AwayFromZero); + // _logger.LogInformation($"{imeiDel} segmentCountFMIndex: {i} -- fetalMovementSampleTime:{fetalMovementSampleTime}|{midNight.AddHours(2 * i).ToString("yyyy-MM-dd HH:mm:ss")} -- statStartTime: {statStartTime} -- statEndTime: {statEndTime}-- isFetalMovementExisted: {isFetalMovementExisted} "); + + _logger.LogInformation($"{imeiDel} 胎动数据采样时间:{fetalMovementSampleTime}|{fmNowSubtract.ToString("yyyy-MM-dd HH:mm:ss")}, 采样周期:{statStartTime}-{statEndTime}, 原始胎动值:{fetalMovementMapValue}, 佩戴时间 :{duringMins}|{statStartTime}-{phrRange.First()}, 胎动计算值:{fetalMovementValue}, 胎动最终值:{fetalMovement} 已完成."); + + // 读取胎心数据 + GeneralParam param = new() + { + Filters = new List + { + new () + { + Key=nameof(HisGpsFetalHeartRate.Serialno), + Value=imeiDel, + ValueType=QueryValueTypeEnum.String, + Operator=QueryOperatorEnum.Equal + }, + //new () + //{ + // Key=nameof(HisGpsFetalHeartRate.SampleTime), + // Value=sampleTime, + // ValueType=QueryValueTypeEnum.String, + // Operator=QueryOperatorEnum.GreaterEqual + //}, + }, + OrderBys=new List + { + new (){ + IsDesc=true, + Key=nameof(HisGpsFetalHeartRate.SampleTime) + } + } + }; + var fetalHeartRateIsAbnormal = 0; + var fhr = await _hisFetalHeartApiClient.GetFirstAsync(param, imeiDel[^2..], null, new RequestHeader { RequestId = Guid.NewGuid().ToString("D") }); + // 获取胎心数据状态与胎动数据状态一致 + var feltalMovementIsAbnormal = fetalHeartRateIsAbnormal; + //var feltalMovementIsAbnormal = 0; + // 推送到api/v1/open/OpenIot/SetFetalMovementConfig + + await _serviceIotApi.SetFetalMovementConfig(imeiDel, fetalMovement, fetalMovementSampleTime, feltalMovementIsAbnormal); + + // 保存到MySQL数据库 + HisGpsFetalMovement fm = new() + { + FetalMovementId = Guid.NewGuid().ToString("D"), + PersonId = commonPHR!.PersonId, + Serialno = imeiDel, + CreateTime = DateTime.Now, + IsAbnormal = feltalMovementIsAbnormal, + FetalMovementValue = fetalMovement, + SampleTime = fetalMovementSampleTime, + Method = 1, + IsDisplay = 1, + DeviceKey = commonPHR!.DeviceKey + }; + await _hisFetalMovementApiClient.AddAsync(fm).ConfigureAwait(false); + + // 发送到微信 + if (feltalMovementIsAbnormal != 0) + { + var device = await _deviceCacheMgr.GetDeviceBySerialNoAsync(imeiDel).ConfigureAwait(false); + var fmMsgId = $"{imeiDel}-{fetalMovementSampleTime}-{Guid.NewGuid().ToString("D")[^3..]}"; + var topic = "topic.push.wx"; + var fmMsg = new + { + messageId = Guid.NewGuid().ToString("D"), + topic = topic, + time = DateTimeUtil.GetDateTimeFromUnixTimeMilliseconds(long.Parse(fetalMovementSampleTime.Length < 13 ? fetalMovementSampleTime.PadRight(13, '0') : fetalMovementSampleTime)).ToString("yyyy-MM-dd HH:mm:ss"), + data = new + { + deviceId = device?.DeviceId, + imei = imeiDel, + alarmTypeId = 12, + alarmDeviceName = imeiDel, + alarmRemarks = JsonConvert.SerializeObject(new { fetalMovementValue = fetalMovement, isAbnormal = feltalMovementIsAbnormal }), + address = string.Empty, + deviceKey = device?.DeviceId + } + }; + await _serviceMqProcess.ProcessIMEIEventMessageAsync(fmMsgId, topic, fmMsg).ConfigureAwait(false); + } + // 设置入库缓存记录 + await _deviceCacheMgr.SetFetalMovementAsync(imeiDel, fetalMovementSampleTime, fm); + + } + else + { + _logger.LogWarning($"{imeiDel} 孕周 {pregnancyWeeks},超出胎动计算范围"); + } + } + else + { + _logger.LogInformation($"{imeiDel} 胎动记录{isFetalMovementExisted},数据采样时间:{fetalMovementSampleTime}|{fmNowSubtract.ToString("yyyy-MM-dd HH:mm:ss")}, 周期:{statStartTime}-{statEndTime} 不足两条,不能判断是否持续佩戴"); + + } + } + else + { + _logger.LogInformation($"{imeiDel} 胎动记录{isFetalMovementExisted},数据采样时间:{fetalMovementSampleTime}|{fmNowSubtract.ToString("yyyy-MM-dd HH:mm:ss")}, 周期:{statStartTime}-{statEndTime} 已处理"); + } + } + + + } + #endregion } else { @@ -794,5 +971,22 @@ namespace HealthMonitor.WebApi return timeRanges.Any(range => now >= range.Start && now <= range.End); } + public static bool IsLastUpdateInTimeRanges(DateTime lastUpdate) + { + var now = lastUpdate.TimeOfDay; + + var timeRanges = new List<(TimeSpan Start, TimeSpan End)> + { + // 8:00~10:00,12:00~14:00,18:00~20:00,22:00~24:00 + (new TimeSpan(8, 0, 0), new TimeSpan(10, 0, 0)), + (new TimeSpan(12, 0, 0), new TimeSpan(14, 0, 0)), + (new TimeSpan(18, 0, 0), new TimeSpan(20, 0, 0)), + (new TimeSpan(22, 0, 0), new TimeSpan(24, 0, 0)) + }; + + return timeRanges.Any(range => now >= range.Start && now <= range.End); + } + + } }