diff --git a/HealthMonitor.Service/Resolver/PregnancyHeartRateResolver.cs b/HealthMonitor.Service/Resolver/PregnancyHeartRateResolver.cs index d38c424..c0b5cb1 100644 --- a/HealthMonitor.Service/Resolver/PregnancyHeartRateResolver.cs +++ b/HealthMonitor.Service/Resolver/PregnancyHeartRateResolver.cs @@ -52,6 +52,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 }; + + private static int[] ODD_SCHEDULE_HOUR = new int[] { 1, 3, 5, 7, 9, 11,13, 15, 17, 19, 21, 23 }; // 延时胎心计算间隔周期 private static int INTERVAL_FHR=15; @@ -181,6 +183,8 @@ namespace HealthMonitor.Service.Resolver if (nonFreqPhr.Count >= 30) { #region 定时计算胎动数据触发器两小时间隔开始 + + /** var fetalMovementKey = $"health_monitor/schedule_push/cal_fetal_movement/imei/{heartRate.Serialno}"; ///// 计算 0 点秒数 var fetalMovementLastUpdate = (DateTime)heartRate.LastUpdate!; @@ -204,8 +208,54 @@ namespace HealthMonitor.Service.Resolver await SetIntervalTriggerAsync(fetalMovementKey, heartRate.Serialno, ttl, heartRate); } + */ + #endregion + + #region 定时计算胎动数据触发器两小时间隔开始(奇数小时计算) + var fetalMovementKey = $"health_monitor/schedule_push/cal_fetal_movement/imei/{heartRate.Serialno}"; + 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 fmScheduleNow = DateTime.Now; + int hour = fmScheduleNow.Hour; + int selectedScheduleHour; + + // 使用模运算来获取相应的ODD_SCHEDULE_HOUR + if (hour % 2 == 0) + { + selectedScheduleHour = ODD_SCHEDULE_HOUR[hour / 2 % ODD_SCHEDULE_HOUR.Length]; + } + else + { + selectedScheduleHour = ODD_SCHEDULE_HOUR[(hour + 1) / 2 % ODD_SCHEDULE_HOUR.Length]; + } + + + DateTime scheduledDateTime = new DateTime( + fmScheduleNow.Year, + fmScheduleNow.Month, + fmScheduleNow.Day, + selectedScheduleHour, + 0, // 分钟设为 0 + 0 // 秒设为 0 + ); + + // 如果生成的时间在当前时间之前 + // 或者 fmScheduleNow 在 23 点之后并且 selectedScheduleHour 在 1 点(即次日) + if (scheduledDateTime < fmScheduleNow || (fmScheduleNow.Hour >= 23 && selectedScheduleHour <= 1)) + { + scheduledDateTime = scheduledDateTime.AddDays(1); + } + + TimeSpan timeUntilNextRun = scheduledDateTime - fmScheduleNow; + var ttl = (long)timeUntilNextRun.TotalSeconds; + + await SetIntervalTriggerAsync(fetalMovementKey, heartRate.Serialno, ttl, heartRate); #endregion + + #region 计算胎心数据(按心率时间LastUpdate) var commonPHR = await _serviceTDengine.GetLastAsync(heartRate.Serialno); if (commonPHR == null) diff --git a/HealthMonitor.WebApi/Worker.cs b/HealthMonitor.WebApi/Worker.cs index e301d3b..df4e4f3 100644 --- a/HealthMonitor.WebApi/Worker.cs +++ b/HealthMonitor.WebApi/Worker.cs @@ -546,7 +546,7 @@ namespace HealthMonitor.WebApi { #region 胎动延时计算 - /** + /** using (_logger.BeginScope(new Dictionary { ["RequestId"] = $"FM-{imeiDel}-{DateTime.Now.ToString("yyyyMMddHHmmss")}" })) { var watchConfig = await _deviceCacheMgr.GetGpsDeviceWatchConfigCacheObjectBySerialNoAsync(imeiDel, "0067"); @@ -925,6 +925,7 @@ namespace HealthMonitor.WebApi #endregion #region 胎动延时计算(使用线程再延时) + /** using (_logger.BeginScope(new Dictionary { ["RequestId"] = $"FM-{imeiDel}-{DateTime.Now.ToString("yyyyMMddHHmmss")}" })) { var watchConfig = await _deviceCacheMgr.GetGpsDeviceWatchConfigCacheObjectBySerialNoAsync(imeiDel, "0067"); @@ -1343,6 +1344,415 @@ namespace HealthMonitor.WebApi } } + */ + #endregion + + #region 胎动延时计算(实时now是3小时,计算 lastupdate 0~2范围的数据,) + ///实时now的hour是3小时,计算 lastupdate 0~2范围的数据, + ///只在 HOUR = new int[] { 1, 3, 5, 7, 9, 11,13, 15, 17, 19, 21, 23 };是奇数 + + using (_logger.BeginScope(new Dictionary { ["RequestId"] = $"FM-{imeiDel}-{DateTime.Now.ToString("yyyyMMddHHmmss")}" })) + { + try + { + var watchConfig = await _deviceCacheMgr.GetGpsDeviceWatchConfigCacheObjectBySerialNoAsync(imeiDel, "0067"); + _logger.LogInformation($"触发胎动计算,设备配置{JsonConvert.SerializeObject(watchConfig)}"); + var isFetalHeartEnable = watchConfig != null && (int)watchConfig["enabled"]! == 1; + var highFreqSampleInterval = (int)watchConfig!["highFreqSampleInterval"]! >= 60 ? (int)watchConfig!["highFreqSampleInterval"]! : 60; + + 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} 计算胎动数据 "); + + DateTime fmNow = DateTime.Now; + int fmNowHour = fmNow.Hour; + DateTime statStartTime; + DateTime statEndTime; + + if (fmNowHour == 1) + { + // last_update 22~0 + statStartTime = fmNow.Date.AddDays(-1).AddHours(22); + statEndTime = fmNow.Date; + } + else + { + statStartTime = fmNow.Date.AddHours(fmNowHour - 3); + statEndTime = fmNow.Date.AddHours(fmNowHour - 1); + } + + _logger.LogInformation($"{imeiDel} 胎动统计周期:{statStartTime.ToString("yyyy-MM-dd HH:mm:ss")}-{statEndTime.ToString("yyyy-MM-dd HH:mm:ss")}"); + + + var fetalMovementSampleTime = DateTimeUtil.ConvertToTimeStamp(statStartTime).ToString()[..10]; + var isFetalMovementExisted = await _deviceCacheMgr.FetalMovementIsExistedAsync(imeiDel, fetalMovementSampleTime); + + 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) + { + // 读取胎心数据 + 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 time = long.Parse(fhr.SampleTime.Length < 13 ? fhr.SampleTime.PadRight(13, '0') : fhr.SampleTime); + var fhrSampleTime = DateTimeUtil.GetDateTimeFromUnixTimeMilliseconds(time); + + // 胎心数据时间与胎动时间一致,最后一条胎心数据与胎动数据的小时差不大于3 + if ((DateTime.Now.Hour - fhrSampleTime.Hour) <= 3) + { + var duringMins = Math.Abs((phrRange.First() - phrRange.Last()).TotalMinutes); + //在餐后时间段(8:00~10:00,12:00~14:00,18:00~20:00,22:00~24:00)取中间值。其他时间段取正常起始值 + bool isInTimeRanges = IsLastUpdateInTimeRanges(phrRange.First()); + + 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} "); + + #region 生理健康与胎动的关系 + /// (步数)运动步数超过1500步则加1; + /// (体温)低烧则胎动减1,高烧胎动减2;低烧是37.3~38.5,38.6以上是高烧。 + /// (血压)血压收缩压大于160则加1。 + /// (心理)心理压力高加2,压力中加1; + + var fetalMovementStepVar = 0; + var fetalMovementTempVar = 0; + var fetalMovementBpVar = 0; + var fetalMovementPpVar = 0; + var fetalMovementFhrVar = 0; + // 步数 + if (true) + { + var step = await _personCacheMgr.GetStepPeriodicityAsync(imeiDel); + if (step != null) + { + if (DateTime.Now.Hour - ((DateTime)step?.LastUpdate!).Hour <= 2) + { + if (step.Steps > 1500) fetalMovementStepVar = 1; + } + else + { + _logger.LogWarning($"{imeiDel} 周期步数 时间无效"); + } + } + else + { + _logger.LogWarning($"{imeiDel} 周期步数无数据"); + } + + } + + // 体温 + if (true) + { + var temp = await _personCacheMgr.GetTemperaturePeriodicityAsync(imeiDel); + if (temp != null) + { + if (DateTime.Now.Hour - ((DateTime)temp?.LastUpdate!).Hour <= 2) + { + // 中烧 + if (temp.Temperature >= 37.3M && temp.Temperature <= 38.5M) + { + fetalMovementTempVar = -1; + } + // 高烧 + if (temp.Temperature >= 38.6M) + { + fetalMovementTempVar = -2; + } + } + else + { + _logger.LogWarning($"{imeiDel} 周期体温 时间无效"); + } + } + else + { + _logger.LogWarning($"{imeiDel} 周期体温无数据"); + } + + } + + // 血压 + if (true) + { + var bp = await _personCacheMgr.GetBloodPressPeriodicityAsync(imeiDel); + if (bp != null) + { + if (DateTime.Now.Hour - ((DateTime)bp?.LastUpdate!).Hour <= 2) + { + if (bp.SystolicValue > 160) + { + fetalMovementBpVar = 1; + } + } + else + { + _logger.LogWarning($"{imeiDel} 周期血压 时间无效"); + } + } + else + { + _logger.LogWarning($"{imeiDel} 周期血压无数据"); + } + + } + // 心理 + if (true) + { + //-1 不处理, + //0 正常, + //1 轻度, + //2 中度; + //3 重度; + GeneralParam psychResultParam = new() + { + Filters = new List + { + new () + { + Key=nameof(HisGpsPsychResult.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(HisGpsPsychResult.Serialno) + } + } + }; + var psych = await _hisPsychResultApiClient.GetFirstAsync(psychResultParam, imeiDel[^2..], null, new RequestHeader { RequestId = Guid.NewGuid().ToString("D") }); + if (psych != null) + { + if (psych?.StressScore == 2) + { + fetalMovementPpVar = 1; + } + + if (psych?.StressScore == 3) + { + fetalMovementPpVar = 2; + } + } + else + { + _logger.LogWarning($"{imeiDel} 周期心理压力无数据"); + } + + } + + #endregion + + #region 胎心与胎动的关系 + /// 胎心值过缓时,则胎动数量减1;胎心值过速时,则胎动也加1。 + /// 此值允许在上限值上继续增加,在下限值上继续减少,最小值为0。 + /// 告警上限阀值 + + //1表示偏高;2表示偏低 + if (true) + { + if (fhr.IsAbnormal == 2) + { + fetalMovementPpVar = -1; + } + + if (fhr.IsAbnormal == 1) + { + fetalMovementPpVar = 1; + } + } + + #endregion + + _logger.LogInformation($"{imeiDel} 原始胎动值:{fetalMovementValue}, 步数参数变动值:{fetalMovementStepVar},体温参数变动值:{fetalMovementTempVar},血压参数变动值:{fetalMovementBpVar},心理压力参数变动值:{fetalMovementPpVar},胎心参数变动值:{fetalMovementFhrVar}"); + + + fetalMovementValue = fetalMovementValue + fetalMovementStepVar + fetalMovementTempVar + fetalMovementBpVar + fetalMovementPpVar + fetalMovementFhrVar; + + + _logger.LogInformation($"{imeiDel} 孕周:{pregnancyWeeks},胎动数据采样时间:{fetalMovementSampleTime}|{statStartTime.ToString("yyyy-MM-dd HH:mm:ss")}, 采样周期:{statStartTime}-{statEndTime}, 原始胎动值:{fetalMovementMapValue}, 佩戴时间 :{duringMins}|{phrRange.Last()}-{phrRange.First()}, 胎动计算值:{fetalMovementValue}, 胎动最终值:{fetalMovement} 已完成."); + + // 获取胎心数据状态与胎动数据状态一致 + //etalHeartRateIsAbnormal= fhr.IsAbnormal; + var feltalMovementIsAbnormal = fetalHeartRateIsAbnormal; + + 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); + + var device = await _deviceCacheMgr.GetDeviceBySerialNoAsync(imeiDel).ConfigureAwait(false); + var fmMsgId = $"{imeiDel}-{fetalMovementSampleTime}-{Guid.NewGuid().ToString("D")[^3..]}"; + var fmMsgTime = DateTimeUtil.GetDateTimeFromUnixTimeMilliseconds(long.Parse(fetalMovementSampleTime.Length < 13 ? fetalMovementSampleTime.PadRight(13, '0') : fetalMovementSampleTime)).ToString("yyyy-MM-dd HH:mm:ss"); + // 胎动数据推送到第三方 + var topic = "topic.push.third"; + var fmThridMsg = new + { + messageId = fmMsgId, + topic = topic, + time = fmMsgTime, + data = new + { + imei = imeiDel, + value = fetalMovement, + isAbnormal = feltalMovementIsAbnormal, + type = "fetalMovement" + } + }; + await _serviceMqProcess.ProcessIMEIEventMessageAsync(fmMsgId, topic, 31, fmThridMsg).ConfigureAwait(false); + + // 胎动数据推送到微信 + if (feltalMovementIsAbnormal != 0) + { + 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.LogWarning($"{imeiDel} 最后一条胎心数据与胎动数据的小时差大于2,不计算胎动数据"); + } + } + else + { + _logger.LogInformation($"{imeiDel} 胎动记录{isFetalMovementExisted},数据采样时间:{fetalMovementSampleTime}|{statStartTime.ToString("yyyy-MM-dd HH:mm:ss")}, 周期:{statStartTime}-{statEndTime} 不足两条,不能判断是否持续佩戴"); + + } + } + else + { + _logger.LogInformation($"{imeiDel} 胎动记录{isFetalMovementExisted},数据采样时间:{fetalMovementSampleTime}|{statStartTime.ToString("yyyy-MM-dd HH:mm:ss")}, 周期:{statStartTime}-{statEndTime} 已处理"); + } + } + else + { + _logger.LogWarning($"{imeiDel} 没有胎心建模数据"); + } + + } + else + { + _logger.LogWarning($"{imeiDel} 胎心监测功能没有开启"); + } + } + catch (Exception ex ) + { + + _logger.LogError($"{imeiDel} 计算胎动数据异常 {ex.Message}\n {ex.StackTrace}"); + } + + + } #endregion }