From 367315162cfb68da0578d2e67d928ab4a21c5f59 Mon Sep 17 00:00:00 2001 From: H Vs Date: Wed, 7 Aug 2024 10:54:50 +0800 Subject: [PATCH] =?UTF-8?q?=E8=83=8E=E5=BF=83=E5=A4=A7=E4=BA=8E220?= =?UTF-8?q?=E6=97=B6=E7=AE=97=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Resolver/PregnancyHeartRateResolver.cs | 962 ++++++++---------- 1 file changed, 432 insertions(+), 530 deletions(-) diff --git a/HealthMonitor.Service/Resolver/PregnancyHeartRateResolver.cs b/HealthMonitor.Service/Resolver/PregnancyHeartRateResolver.cs index a204867..6f50525 100644 --- a/HealthMonitor.Service/Resolver/PregnancyHeartRateResolver.cs +++ b/HealthMonitor.Service/Resolver/PregnancyHeartRateResolver.cs @@ -100,611 +100,492 @@ namespace HealthMonitor.Service.Resolver { var messageId = _messageId.Value; var heartRate = _msgData.Value!; - try + + var watchConfig = await _deviceCacheMgr.GetGpsDeviceWatchConfigCacheObjectBySerialNoAsync(heartRate.Serialno, "0067"); + var isFetalHeartEnable = watchConfig != null && (int)watchConfig["enabled"]! == 1; + + if (isFetalHeartEnable) { - + #region 定时下发触发器(定时建模) + var key = $"health_monitor/schedule_push/pregnancy_heart_rate/imei/{heartRate.Serialno}"; + var schedule_push = await _serviceEtcd.GetValAsync(key).ConfigureAwait(false); + + if (string.IsNullOrWhiteSpace(schedule_push)) + { + // 注册首次下推 + var interval = 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; + // 计算距离下一个$interval天后的8点的时间间隔 + DateTime nextRunTime = new DateTime(now.Year, now.Month, now.Day, 6, pushMin, pushSec).AddDays(interval); + TimeSpan timeUntilNextRun = nextRunTime - now; + + if (timeUntilNextRun < TimeSpan.Zero) + { + timeUntilNextRun = timeUntilNextRun.Add(TimeSpan.FromDays(1)); + nextRunTime += TimeSpan.FromDays(1); + } + + var ttl = (long)timeUntilNextRun.TotalSeconds; + var data = new + { + imei = heartRate.Serialno, + create_time = now.ToString("yyyy-MM-dd HH:mm:ss"), + ttl, + next_run_time = nextRunTime.ToString("yyyy-MM-dd HH:mm:ss") + }; + var result = JsonConvert.SerializeObject(data); + await _serviceEtcd.PutValAsync(key, result, ttl, false).ConfigureAwait(false); - var watchConfig = await _deviceCacheMgr.GetGpsDeviceWatchConfigCacheObjectBySerialNoAsync(heartRate.Serialno, "0067"); - var isFetalHeartEnable = watchConfig != null && (int)watchConfig["enabled"]! == 1; + } - if (isFetalHeartEnable) + #endregion + + + // 高频心率采样间隔 highFreqSampleInterval = highFreqSampleInterval+5,增加5秒兼容 + var highFreqSampleInterval = (int)watchConfig!["highFreqSampleInterval"]! + 5; + // 触发高频监测的心率上限值 + var triggerHighFreqHigh = (int)watchConfig["triggerHighFreqHigh"]!; + // 触发高频监测的心率下限值 + var triggerHighFreqLow = (int)watchConfig["triggerHighFreqLow"]!; + //停止高频心率采样心率连续正常次数 + var stopHighFreqSampleCount = (int)watchConfig["stopHighFreqSampleCount"]!; + // 高频心率采集时长 0 为持续采集,非零为高频心率的采集时长 + var highFreqSampleTimes = (int)watchConfig["highFreqSampleTimes"]!; + // 告警上限阀值 + var upperAlarmThreshold = (int)watchConfig["upperAlarmThreshold"]!; + // 告警下限阀值 + var lowerAlarmThreshold = (int)watchConfig["lowerAlarmThreshold"]!; + // EDOC + var edoc = DateTimeUtil.ToDateTime(watchConfig!["EDOC"]!.ToString()); + // interval (分钟) + var intervalFHR = (int)watchConfig["interval"]!; + + var fetalHeartRateIsAbnormal = 0; + + var phr = await _serviceTDengine.GetBySerialNoAsync(heartRate.Serialno, 7); + if (phr.Count >= 30) { - // 高频心率采样间隔 highFreqSampleInterval = highFreqSampleInterval+5,增加5秒兼容 - var highFreqSampleInterval = (int)watchConfig!["highFreqSampleInterval"]! + 5; - // 触发高频监测的心率上限值 - var triggerHighFreqHigh = (int)watchConfig["triggerHighFreqHigh"]!; - // 触发高频监测的心率下限值 - var triggerHighFreqLow = (int)watchConfig["triggerHighFreqLow"]!; - //停止高频心率采样心率连续正常次数 - var stopHighFreqSampleCount = (int)watchConfig["stopHighFreqSampleCount"]!; - // 高频心率采集时长 0 为持续采集,非零为高频心率的采集时长 - var highFreqSampleTimes = (int)watchConfig["highFreqSampleTimes"]!; - // 告警上限阀值 - var upperAlarmThreshold = (int)watchConfig["upperAlarmThreshold"]!; - // 告警下限阀值 - var lowerAlarmThreshold = (int)watchConfig["lowerAlarmThreshold"]!; - // EDOC - var edoc = DateTimeUtil.ToDateTime(watchConfig!["EDOC"]!.ToString()); - // interval (分钟) - var intervalFHR = (int)watchConfig["interval"]!; - - var fetalHeartRateIsAbnormal = 0; - - var phr = await _serviceTDengine.GetBySerialNoAsync(heartRate.Serialno, 7); - if (phr.Count >= 30) + #region 定时计算胎动数据触发器两小时间隔开始 + var fetalMovementKey = $"health_monitor/schedule_push/cal_fetal_movement/imei/{heartRate.Serialno}"; + ///// 计算 0 点秒数 + var fetalMovementLastUpdate = (DateTime)heartRate.LastUpdate!; + DateTime fmScheduleNow = DateTime.Now; + // 小于两小时 + if (fmScheduleNow > fetalMovementLastUpdate && (fmScheduleNow - fetalMovementLastUpdate).TotalHours <= 2) { - #region 计算胎心和胎动数据 - var commonPHR = await _serviceTDengine.GetLastAsync(heartRate.Serialno); - if (commonPHR == null) - { - // 处理孕妇业务,计算一般心率并下发 - commonPHR = await _serviceTDengine.InitPregnancyCommonHeartRateModeAsync(heartRate.Serialno, highFreqSampleInterval: highFreqSampleInterval); - // 建模完成 - var flag = await _serviceIotApi.SetFetalConfig(heartRate.Serialno, 1, commonPHR!.MaxValue, commonPHR!.MinValue); - _logger.LogInformation($"{heartRate.Serialno} 记录数量足够,建模完成"); - // 保存到TDengine数据库 - await _serviceTDengine.InsertAsync("hm_pchr", commonPHR!); - _logger.LogInformation($"保存TDengine完成"); - } - #region 计算胎心数据(按心率时间LastUpdate) + var rand = new Random(); + var pushSec = rand.Next(59); + int pushMin = int.TryParse(heartRate.Serialno.AsSpan(heartRate.Serialno.Length - 1), out pushMin) ? pushMin : 10; - // 获取最近的两个记录,并计算它们的 LastUpdate 时间差 - var firstTwoPhr = phr.OrderByDescending(i => i.LastUpdate).Take(2).Select(i => i.LastUpdate).ToList(); - var timeDiff = firstTwoPhr[0] - firstTwoPhr[1]; + var scheduleHourDiff = SCHEDULE_HOUR + .Where(h => h > fetalMovementLastUpdate.Hour) + .OrderBy(h => h - fetalMovementLastUpdate.Hour) + .FirstOrDefault() - fetalMovementLastUpdate.Hour; + var scheduleTime = fetalMovementLastUpdate.AddHours(scheduleHourDiff); - // 如果需要,将时间差转换为秒 - var timeDiffInSeconds = timeDiff.TotalSeconds; + DateTime nextRunTime = new(scheduleTime.Year, scheduleTime.Month, scheduleTime.Day, scheduleTime.Hour, pushMin, pushSec); + TimeSpan timeUntilNextRun = nextRunTime - fmScheduleNow; + var ttl = (long)timeUntilNextRun.TotalSeconds; - // 高频统计结束时间 - var FreqStatsEnd = DateTime.Now; - // 高频心率启动 - if (timeDiffInSeconds <= highFreqSampleInterval) - { + await SetIntervalTriggerAsync(fetalMovementKey, heartRate.Serialno, ttl); + } + #endregion - var phrFreqstatus = await _deviceCacheMgr.GetPregnancyHeartRateFreqStatusAsync(heartRate.Serialno); - if (phrFreqstatus == null) - { - /// 设置高频状态 - _logger.LogInformation($"{heartRate.Serialno} 进入高频心率启动状态 timeDiffInSeconds {timeDiffInSeconds},highFreqSampleInterval:{highFreqSampleInterval}"); - // 设置高频状态 - var freqFirstPhr = phr.OrderByDescending(i => i.LastUpdate).First(); - await _deviceCacheMgr.SetPregnancyHeartRateFreqStatusAsync(heartRate.Serialno, freqFirstPhr); - //phrFreqstatus = await _deviceCacheMgr.GetPregnancyHeartRateFreqStatusAsync(heartRate.Serialno); - } - /// phr PregnancyHeartRate 连续连续正常次数个值都是正常(大于等于triggerHighFreqLow,少于等于triggerHighFreqHig), - /// 取连续正常次数正常值的平均值,推送到api/v1/open/OpenIot/SetFetalHeartRateConfig - #region 检查是否连续12个值都是正常的 - // 获取最近连续正常次数个心率记录 - var lastPhr = phr.OrderByDescending(i => i.LastUpdate).Take(stopHighFreqSampleCount).ToList(); - - // 检查是否连续12个值都是正常的 - if (lastPhr.All(i => i.PregnancyHeartRate >= triggerHighFreqLow && i.PregnancyHeartRate <= triggerHighFreqHigh)) - { - var avgPhr = lastPhr.Select(i => i.PregnancyHeartRate).Average(); - // 计算一般心率得到胎心系数 - //await SaveAndPushFreqFetalHeartRateAsync(heartRate, upperAlarmThreshold, lowerAlarmThreshold, avgPhr, DateTimeUtil.ConvertToTimeStamp(DateTime.Now).ToString()); - // 高频数据不建模 - FreqStatsEnd = (DateTime)heartRate.LastUpdate!; - _logger.LogInformation($"{heartRate.Serialno} 高频状态已经持续{(FreqStatsEnd - phrFreqstatus!.LastUpdate).TotalSeconds} 秒,连续 {stopHighFreqSampleCount} 次采样心率正常,将下发指令"); - await SaveAndPushFreqFetalHeartRateAsync(heartRate, commonPHR, upperAlarmThreshold, lowerAlarmThreshold, avgPhr, DateTimeUtil.ConvertToTimeStamp(phrFreqstatus!.LastUpdate).ToString()); + #region 计算胎心和胎动数据 + var commonPHR = await _serviceTDengine.GetLastAsync(heartRate.Serialno); + if (commonPHR == null) + { + // 处理孕妇业务,计算一般心率并下发 + commonPHR = await _serviceTDengine.InitPregnancyCommonHeartRateModeAsync(heartRate.Serialno, highFreqSampleInterval: highFreqSampleInterval); + // 建模完成 + var flag = await _serviceIotApi.SetFetalConfig(heartRate.Serialno, 1, commonPHR!.MaxValue, commonPHR!.MinValue); + _logger.LogInformation($"{heartRate.Serialno} 记录数量足够,建模完成"); + // 保存到TDengine数据库 + await _serviceTDengine.InsertAsync("hm_pchr", commonPHR!); + _logger.LogInformation($"保存TDengine完成"); + } + #region 计算胎心数据(按心率时间LastUpdate) - // 删除高频状态的首条记录 - await _deviceCacheMgr.DelPregnancyHeartRateFreqStatusAsync(heartRate.Serialno); + // 获取最近的两个记录,并计算它们的 LastUpdate 时间差 + var firstTwoPhr = phr.OrderByDescending(i => i.LastUpdate).Take(2).Select(i => i.LastUpdate).ToList(); + var timeDiff = firstTwoPhr[0] - firstTwoPhr[1]; - _logger.LogInformation($"{heartRate.Serialno} 连续正常结束高频心率状态, timeDiffInSeconds {timeDiffInSeconds},highFreqSampleInterval:{highFreqSampleInterval},高频状态持续{((DateTime)heartRate.LastUpdate - phrFreqstatus!.LastUpdate).TotalSeconds} 秒"); + // 如果需要,将时间差转换为秒 + var timeDiffInSeconds = timeDiff.TotalSeconds; - } - #endregion + // 高频统计结束时间 + var FreqStatsEnd = DateTime.Now; + // 高频心率启动 + if (timeDiffInSeconds <= highFreqSampleInterval) + { + + var phrFreqstatus = await _deviceCacheMgr.GetPregnancyHeartRateFreqStatusAsync(heartRate.Serialno); + if (phrFreqstatus == null) + { + /// 设置高频状态 + _logger.LogInformation($"{heartRate.Serialno} 进入高频心率启动状态 timeDiffInSeconds {timeDiffInSeconds},highFreqSampleInterval:{highFreqSampleInterval}"); + // 设置高频状态 + var freqFirstPhr = phr.OrderByDescending(i => i.LastUpdate).First(); + await _deviceCacheMgr.SetPregnancyHeartRateFreqStatusAsync(heartRate.Serialno, freqFirstPhr); + //phrFreqstatus = await _deviceCacheMgr.GetPregnancyHeartRateFreqStatusAsync(heartRate.Serialno); } - // 高频心率结束或正常心率 - else + /// phr PregnancyHeartRate 连续连续正常次数个值都是正常(大于等于triggerHighFreqLow,少于等于triggerHighFreqHig), + /// 取连续正常次数正常值的平均值,推送到api/v1/open/OpenIot/SetFetalHeartRateConfig + #region 检查是否连续12个值都是正常的 + // 获取最近连续正常次数个心率记录 + var lastPhr = phr.OrderByDescending(i => i.LastUpdate).Take(stopHighFreqSampleCount).ToList(); + + // 检查是否连续12个值都是正常的 + if (lastPhr.All(i => i.PregnancyHeartRate >= triggerHighFreqLow && i.PregnancyHeartRate <= triggerHighFreqHigh)) { - var phrFreqstatus = await _deviceCacheMgr.GetPregnancyHeartRateFreqStatusAsync(heartRate.Serialno); - if (phrFreqstatus != null) - { - /// 在highFreqSampleTimes=0一直异常(大于等于triggerHighFreqLow,少于等于triggerHighFreqHig), - /// 取所有值的平均值,推送胎心数据到api/v1/open/OpenIot/SetFetalHeartRateConfig - if (highFreqSampleTimes == 0) - { - var avgPhr = phr.OrderByDescending(i => i.LastUpdate) - .Where(i => i.LastUpdate >= phrFreqstatus?.LastUpdate) - .Skip(1) // 去除首条 - .Where(i => i.PregnancyHeartRate < triggerHighFreqLow || i.PregnancyHeartRate > triggerHighFreqHigh) - .Select(i => i.PregnancyHeartRate).Average(); - // 推送胎心数据到 api/v1/open/OpenIot/SetFetalHeartRateConfig - // 计算一般心率得到胎心系数 - //await SaveAndPushFetalHeartRateAsync(heartRate, upperAlarmThreshold, lowerAlarmThreshold, avgPhr); - //await SaveAndPushFreqFetalHeartRateAsync(heartRate, upperAlarmThreshold, lowerAlarmThreshold, avgPhr, DateTimeUtil.ConvertToTimeStamp(DateTime.Now).ToString()); - // 高频数据不建模 - FreqStatsEnd = firstTwoPhr[1]; - _logger.LogInformation($"{heartRate.Serialno} 高频状态已经持续{(FreqStatsEnd - phrFreqstatus!.LastUpdate).TotalSeconds} 秒,highFreqSampleTimes={highFreqSampleTimes}秒,即将结束高频状态,将下发指令"); - - await SaveAndPushFreqFetalHeartRateAsync(heartRate, commonPHR, upperAlarmThreshold, lowerAlarmThreshold, avgPhr, DateTimeUtil.ConvertToTimeStamp(phrFreqstatus!.LastUpdate).ToString()); + var avgPhr = lastPhr.Select(i => i.PregnancyHeartRate).Average(); + // 计算一般心率得到胎心系数 + //await SaveAndPushFreqFetalHeartRateAsync(heartRate, upperAlarmThreshold, lowerAlarmThreshold, avgPhr, DateTimeUtil.ConvertToTimeStamp(DateTime.Now).ToString()); + // 高频数据不建模 + FreqStatsEnd = (DateTime)heartRate.LastUpdate!; + _logger.LogInformation($"{heartRate.Serialno} 高频状态已经持续{(FreqStatsEnd - phrFreqstatus!.LastUpdate).TotalSeconds} 秒,连续 {stopHighFreqSampleCount} 次采样心率正常,将下发指令"); + await SaveAndPushFreqFetalHeartRateAsync(heartRate, commonPHR, upperAlarmThreshold, lowerAlarmThreshold, avgPhr, DateTimeUtil.ConvertToTimeStamp(phrFreqstatus!.LastUpdate).ToString()); - } + // 删除高频状态的首条记录 + await _deviceCacheMgr.DelPregnancyHeartRateFreqStatusAsync(heartRate.Serialno); - /// 在highFreqSampleTimes>0一直异常(大于等于triggerHighFreqLow,少于等于triggerHighFreqHig), - /// 取所有值的平均值,推送胎心数据到api/v1/open/OpenIot/SetFetalHeartRateConfig - if (highFreqSampleTimes > 0 && heartRate.LastUpdate >= (phrFreqstatus?.LastUpdate.AddSeconds(highFreqSampleTimes))) - { - var filterPhr = phr - .Where(i => i.LastUpdate >= phrFreqstatus?.LastUpdate); - - var avgPhr = filterPhr - .OrderByDescending(i => i.LastUpdate) - .Skip(1) // 去除首条 - .Take(12) // 计算最后12条 - //.Where(i => i.PregnancyHeartRate < triggerHighFreqLow || i.PregnancyHeartRate > triggerHighFreqHigh) - .Select(i => i.PregnancyHeartRate).Average(); - // 推送胎心数据到 api/v1/open/OpenIot/SetFetalHeartRateConfig - // 计算一般心率得到胎心系数 - //await SaveAndPushFetalHeartRateAsync(heartRate, upperAlarmThreshold, lowerAlarmThreshold, avgPhr); - //await SaveAndPushFreqFetalHeartRateAsync(heartRate, upperAlarmThreshold, lowerAlarmThreshold, avgPhr, DateTimeUtil.ConvertToTimeStamp(DateTime.Now).ToString()); - //await SaveAndPushFreqFetalHeartRateAsync(heartRate, commonPHR, upperAlarmThreshold, lowerAlarmThreshold, avgPhr, DateTimeUtil.ConvertToTimeStamp(DateTime.Now).ToString()); - // 高频数据不建模 - FreqStatsEnd = firstTwoPhr[1]; - _logger.LogInformation($"{heartRate.Serialno} 高频状态已经持续{(FreqStatsEnd - phrFreqstatus!.LastUpdate).TotalSeconds} 秒,超过约定的 {highFreqSampleTimes} 秒,即将结束高频状态,将下发指令"); - - await SaveAndPushFreqFetalHeartRateAsync(heartRate, commonPHR, upperAlarmThreshold, lowerAlarmThreshold, avgPhr, DateTimeUtil.ConvertToTimeStamp(phrFreqstatus!.LastUpdate).ToString()); - } + _logger.LogInformation($"{heartRate.Serialno} 连续正常结束高频心率状态, timeDiffInSeconds {timeDiffInSeconds},highFreqSampleInterval:{highFreqSampleInterval},高频状态持续{((DateTime)heartRate.LastUpdate - phrFreqstatus!.LastUpdate).TotalSeconds} 秒"); + + } + #endregion + } + // 高频心率结束或正常心率 + else + { + var phrFreqstatus = await _deviceCacheMgr.GetPregnancyHeartRateFreqStatusAsync(heartRate.Serialno); + if (phrFreqstatus != null) + { + /// 在highFreqSampleTimes=0一直异常(大于等于triggerHighFreqLow,少于等于triggerHighFreqHig), + /// 取所有值的平均值,推送胎心数据到api/v1/open/OpenIot/SetFetalHeartRateConfig + if (highFreqSampleTimes == 0) + { + var avgPhr = phr.OrderByDescending(i => i.LastUpdate) + .Where(i => i.LastUpdate >= phrFreqstatus?.LastUpdate) + .Skip(1) // 去除首条 + .Where(i => i.PregnancyHeartRate < triggerHighFreqLow || i.PregnancyHeartRate > triggerHighFreqHigh) + .Select(i => i.PregnancyHeartRate).Average(); + // 推送胎心数据到 api/v1/open/OpenIot/SetFetalHeartRateConfig + // 计算一般心率得到胎心系数 + //await SaveAndPushFetalHeartRateAsync(heartRate, upperAlarmThreshold, lowerAlarmThreshold, avgPhr); + //await SaveAndPushFreqFetalHeartRateAsync(heartRate, upperAlarmThreshold, lowerAlarmThreshold, avgPhr, DateTimeUtil.ConvertToTimeStamp(DateTime.Now).ToString()); + // 高频数据不建模 + FreqStatsEnd = firstTwoPhr[1]; + _logger.LogInformation($"{heartRate.Serialno} 高频状态已经持续{(FreqStatsEnd - phrFreqstatus!.LastUpdate).TotalSeconds} 秒,highFreqSampleTimes={highFreqSampleTimes}秒,即将结束高频状态,将下发指令"); - // 删除高频状态的首条记录 - await _deviceCacheMgr.DelPregnancyHeartRateFreqStatusAsync(heartRate.Serialno); + await SaveAndPushFreqFetalHeartRateAsync(heartRate, commonPHR, upperAlarmThreshold, lowerAlarmThreshold, avgPhr, DateTimeUtil.ConvertToTimeStamp(phrFreqstatus!.LastUpdate).ToString()); - _logger.LogInformation($"{heartRate.Serialno} 超时结束高频心率状态 timeDiffInSeconds {timeDiffInSeconds},highFreqSampleInterval:{highFreqSampleInterval},高频状态持续{(firstTwoPhr[1] - phrFreqstatus!.LastUpdate).TotalSeconds} 秒"); } - // 正常心率 - else + + /// 在highFreqSampleTimes>0一直异常(大于等于triggerHighFreqLow,少于等于triggerHighFreqHig), + /// 取所有值的平均值,推送胎心数据到api/v1/open/OpenIot/SetFetalHeartRateConfig + if (highFreqSampleTimes > 0 && heartRate.LastUpdate >= (phrFreqstatus?.LastUpdate.AddSeconds(highFreqSampleTimes))) { + var filterPhr = phr + .Where(i => i.LastUpdate >= phrFreqstatus?.LastUpdate); + + var avgPhr = filterPhr + .OrderByDescending(i => i.LastUpdate) + .Skip(1) // 去除首条 + .Take(12) // 计算最后12条 + //.Where(i => i.PregnancyHeartRate < triggerHighFreqLow || i.PregnancyHeartRate > triggerHighFreqHigh) + .Select(i => i.PregnancyHeartRate).Average(); + // 推送胎心数据到 api/v1/open/OpenIot/SetFetalHeartRateConfig + // 计算一般心率得到胎心系数 + //await SaveAndPushFetalHeartRateAsync(heartRate, upperAlarmThreshold, lowerAlarmThreshold, avgPhr); + //await SaveAndPushFreqFetalHeartRateAsync(heartRate, upperAlarmThreshold, lowerAlarmThreshold, avgPhr, DateTimeUtil.ConvertToTimeStamp(DateTime.Now).ToString()); + //await SaveAndPushFreqFetalHeartRateAsync(heartRate, commonPHR, upperAlarmThreshold, lowerAlarmThreshold, avgPhr, DateTimeUtil.ConvertToTimeStamp(DateTime.Now).ToString()); + // 高频数据不建模 + FreqStatsEnd = firstTwoPhr[1]; + _logger.LogInformation($"{heartRate.Serialno} 高频状态已经持续{(FreqStatsEnd - phrFreqstatus!.LastUpdate).TotalSeconds} 秒,超过约定的 {highFreqSampleTimes} 秒,即将结束高频状态,将下发指令"); - // 上15分钟的数据 - // 获取当前时间 - DateTime nowInterval = (DateTime)heartRate.LastUpdate!; + await SaveAndPushFreqFetalHeartRateAsync(heartRate, commonPHR, upperAlarmThreshold, lowerAlarmThreshold, avgPhr, DateTimeUtil.ConvertToTimeStamp(phrFreqstatus!.LastUpdate).ToString()); + } - // 计算last_update到上一间隔的分钟数 - int minutesToSubtract = nowInterval.Minute % intervalFHR; + // 删除高频状态的首条记录 + await _deviceCacheMgr.DelPregnancyHeartRateFreqStatusAsync(heartRate.Serialno); - // 计算上一间隔的时间 - DateTime previousInterval = nowInterval.AddMinutes(-minutesToSubtract).AddSeconds(-nowInterval.Second).AddMilliseconds(-nowInterval.Millisecond); + _logger.LogInformation($"{heartRate.Serialno} 超时结束高频心率状态 timeDiffInSeconds {timeDiffInSeconds},highFreqSampleInterval:{highFreqSampleInterval},高频状态持续{(firstTwoPhr[1] - phrFreqstatus!.LastUpdate).TotalSeconds} 秒"); + } + // 正常心率 + else + { - // 使用 last_update 上一刻 - var sampleTimeFHR = DateTimeUtil.ConvertToTimeStamp(previousInterval).ToString(); + // 上15分钟的数据 + // 获取当前时间 + DateTime nowInterval = (DateTime)heartRate.LastUpdate!; - // 计算last_update到下一间隔的分钟数 - int minutesToAdd = intervalFHR - (nowInterval.Minute % intervalFHR); - if (minutesToAdd == intervalFHR) - { - minutesToAdd = 0; // 如果已经是间隔,则不需要增加分钟 - } + // 计算last_update到上一间隔的分钟数 + int minutesToSubtract = nowInterval.Minute % intervalFHR; - // 计算下一间隔的时间 - DateTime nextInterval = nowInterval.AddMinutes(minutesToAdd) - .AddSeconds(-nowInterval.Second) - .AddMilliseconds(-nowInterval.Millisecond); + // 计算上一间隔的时间 + DateTime previousInterval = nowInterval.AddMinutes(-minutesToSubtract).AddSeconds(-nowInterval.Second).AddMilliseconds(-nowInterval.Millisecond); + // 使用 last_update 上一刻 + var sampleTimeFHR = DateTimeUtil.ConvertToTimeStamp(previousInterval).ToString(); - var daysPhr = await _serviceTDengine.GetBySerialNoAsync(heartRate.Serialno, 7); + // 计算last_update到下一间隔的分钟数 + int minutesToAdd = intervalFHR - (nowInterval.Minute % intervalFHR); + if (minutesToAdd == intervalFHR) + { + minutesToAdd = 0; // 如果已经是间隔,则不需要增加分钟 + } - var normalPhrStatStartTime = nextInterval; + // 计算下一间隔的时间 + DateTime nextInterval = nowInterval.AddMinutes(minutesToAdd) + .AddSeconds(-nowInterval.Second) + .AddMilliseconds(-nowInterval.Millisecond); - var normalPhrStatEndTime = nextInterval.AddMinutes(-intervalFHR); - _logger.LogInformation($"{heartRate.Serialno} 计算胎心数据, 周期:{normalPhrStatStartTime}-{normalPhrStatEndTime} "); - var filteredPhr = daysPhr - // 使用 last_update 下一刻 - .Where(i => i.LastUpdate <= normalPhrStatStartTime && i.LastUpdate >= normalPhrStatEndTime) - .ToList(); - if (filteredPhr.Count == 0) - { - _logger.LogWarning($"{heartRate.Serialno} 周期:{normalPhrStatStartTime}-{normalPhrStatEndTime} 孕妇心率数据不足,{filteredPhr.Count}条记录"); - return; - } - var phrValue = filteredPhr.Count == 1 - ? filteredPhr.First().PregnancyHeartRate - : filteredPhr.Average(i => i.PregnancyHeartRate); + var daysPhr = await _serviceTDengine.GetBySerialNoAsync(heartRate.Serialno, 7); - var fetalHeartRate = SafeType.SafeInt(phrValue * commonPHR?.StatModeAvgFprCoefficient!); + var normalPhrStatStartTime = nextInterval; + var normalPhrStatEndTime = nextInterval.AddMinutes(-intervalFHR); - fetalHeartRateIsAbnormal = fetalHeartRate > upperAlarmThreshold ? 1 : (fetalHeartRate < lowerAlarmThreshold ? 2 : 0); - fetalHeartRateIsAbnormal = 0;// 不是高频数据,按正常值 - /* HisGpsFetalHeartRate gpsFetalHeartRate = new() + _logger.LogInformation($"{heartRate.Serialno} 计算胎心数据, 周期:{normalPhrStatStartTime}-{normalPhrStatEndTime} "); + var filteredPhr = daysPhr + // 使用 last_update 下一刻 + .Where(i => i.LastUpdate <= normalPhrStatStartTime && i.LastUpdate >= normalPhrStatEndTime) + .ToList(); + if (filteredPhr.Count == 0) + { + _logger.LogWarning($"{heartRate.Serialno} 周期:{normalPhrStatStartTime}-{normalPhrStatEndTime} 孕妇心率数据不足,{filteredPhr.Count}条记录"); + return; + } + var phrValue = filteredPhr.Count == 1 + ? filteredPhr.First().PregnancyHeartRate + : filteredPhr.Average(i => i.PregnancyHeartRate); + + var fetalHeartRate = SafeType.SafeInt(phrValue * commonPHR?.StatModeAvgFprCoefficient!); + + + fetalHeartRateIsAbnormal = fetalHeartRate > upperAlarmThreshold ? 1 : (fetalHeartRate < lowerAlarmThreshold ? 2 : 0); + fetalHeartRateIsAbnormal = 0;// 不是高频数据,按正常值 + /* HisGpsFetalHeartRate gpsFetalHeartRate = new() + { + FetalHeartRateId = Guid.NewGuid().ToString("D"), + PersonId = commonPHR!.PersonId, + Serialno = heartRate.Serialno, + HeartRate = fetalHeartRate, + SampleTime = sampleTimeFHR.Length > 10 ? sampleTimeFHR.Substring(0, 10) : sampleTimeFHR, + IsAbnormal = fetalHeartRateIsAbnormal, + StatStartTime = filteredPhr.OrderBy(i => i.LastUpdate).First().LastUpdate, + StatEndTime = filteredPhr.OrderBy(i => i.LastUpdate).Last().LastUpdate, + CreateTime = DateTime.Now, + Method = 1, + IsDisplay = 1, + DeviceKey = commonPHR!.DeviceKey + }; + // 保存到 数据服务 MySQL 数据库 + await _hisFetalHeartApiClient.AddAsync(gpsFetalHeartRate).ConfigureAwait(false); + + // 推送到api/v1/open/OpenIot/SetFetalHeartRateConfig + await _serviceIotApi.SetFetalHeartRateConfig(heartRate.Serialno, fetalHeartRate, sampleTimeFHR, fetalHeartRateIsAbnormal); + + // 推送送微信 + if (fetalHeartRateIsAbnormal != 0) + { + var device = await _deviceCacheMgr.GetDeviceBySerialNoAsync(heartRate.Serialno).ConfigureAwait(false); + var fhrMsgId = $"{heartRate.Serialno}-{sampleTimeFHR}-{Guid.NewGuid().ToString("D")[^3..]}"; + var topic = "topic.push.wx"; + var fhrMsg = new { - FetalHeartRateId = Guid.NewGuid().ToString("D"), - PersonId = commonPHR!.PersonId, - Serialno = heartRate.Serialno, - HeartRate = fetalHeartRate, - SampleTime = sampleTimeFHR.Length > 10 ? sampleTimeFHR.Substring(0, 10) : sampleTimeFHR, - IsAbnormal = fetalHeartRateIsAbnormal, - StatStartTime = filteredPhr.OrderBy(i => i.LastUpdate).First().LastUpdate, - StatEndTime = filteredPhr.OrderBy(i => i.LastUpdate).Last().LastUpdate, - CreateTime = DateTime.Now, - Method = 1, - IsDisplay = 1, - DeviceKey = commonPHR!.DeviceKey + messageId = fhrMsgId, + topic = topic, + time = DateTimeUtil.GetDateTimeFromUnixTimeMilliseconds(long.Parse(sampleTimeFHR.Length < 13 ? sampleTimeFHR.PadRight(13, '0') : sampleTimeFHR)).ToString("yyyy-MM-dd HH:mm:ss"), + data = new + { + deviceId = device?.DeviceId, + imei = heartRate.Serialno, + alarmTypeId = 12, + alarmDeviceName = heartRate.Serialno, + alarmRemarks = JsonConvert.SerializeObject(new { fetalHeartValue = fetalHeartRate, isAbnormal = fetalHeartRateIsAbnormal }), + address = string.Empty, + deviceKey = device?.DeviceId + } }; - // 保存到 数据服务 MySQL 数据库 - await _hisFetalHeartApiClient.AddAsync(gpsFetalHeartRate).ConfigureAwait(false); + await _serviceMqProcess.ProcessIMEIEventMessageAsync(fhrMsgId, topic, fhrMsg).ConfigureAwait(false); + } - // 推送到api/v1/open/OpenIot/SetFetalHeartRateConfig - await _serviceIotApi.SetFetalHeartRateConfig(heartRate.Serialno, fetalHeartRate, sampleTimeFHR, fetalHeartRateIsAbnormal); + */ + await SaveAndPushFreqFetalHeartRateAsync(heartRate, commonPHR!, upperAlarmThreshold, lowerAlarmThreshold, phrValue, sampleTimeFHR, false); - // 推送送微信 - if (fetalHeartRateIsAbnormal != 0) - { - var device = await _deviceCacheMgr.GetDeviceBySerialNoAsync(heartRate.Serialno).ConfigureAwait(false); - var fhrMsgId = $"{heartRate.Serialno}-{sampleTimeFHR}-{Guid.NewGuid().ToString("D")[^3..]}"; - var topic = "topic.push.wx"; - var fhrMsg = new - { - messageId = fhrMsgId, - topic = topic, - time = DateTimeUtil.GetDateTimeFromUnixTimeMilliseconds(long.Parse(sampleTimeFHR.Length < 13 ? sampleTimeFHR.PadRight(13, '0') : sampleTimeFHR)).ToString("yyyy-MM-dd HH:mm:ss"), - data = new - { - deviceId = device?.DeviceId, - imei = heartRate.Serialno, - alarmTypeId = 12, - alarmDeviceName = heartRate.Serialno, - alarmRemarks = JsonConvert.SerializeObject(new { fetalHeartValue = fetalHeartRate, isAbnormal = fetalHeartRateIsAbnormal }), - address = string.Empty, - deviceKey = device?.DeviceId - } - }; - await _serviceMqProcess.ProcessIMEIEventMessageAsync(fhrMsgId, topic, fhrMsg).ConfigureAwait(false); - } - - */ - await SaveAndPushFreqFetalHeartRateAsync(heartRate, commonPHR!, upperAlarmThreshold, lowerAlarmThreshold, phrValue, sampleTimeFHR, false); - - } } + } - #endregion + #endregion - #region 计算胎动数据(按心率时间LastUpdate) - /** - _logger.LogInformation($"{heartRate.Serialno} 计算胎动数据 "); + #region 计算胎动数据(按心率时间LastUpdate) + /** + _logger.LogInformation($"{heartRate.Serialno} 计算胎动数据 "); - var fetalMovementNow = (DateTime)heartRate.LastUpdate!; + var fetalMovementNow = (DateTime)heartRate.LastUpdate!; - var midNight = new DateTime(fetalMovementNow.Year, fetalMovementNow.Month, fetalMovementNow.Day, 0, 0, 0); + var midNight = new DateTime(fetalMovementNow.Year, fetalMovementNow.Month, fetalMovementNow.Day, 0, 0, 0); - TimeSpan fetalMovementTS = fetalMovementNow - midNight; + TimeSpan fetalMovementTS = fetalMovementNow - midNight; - // 当天每隔2小时的段数,取整数部分 - int segmentCountFM = (int)(fetalMovementTS.TotalHours / 2); + // 当天每隔2小时的段数,取整数部分 + int segmentCountFM = (int)(fetalMovementTS.TotalHours / 2); - for (int i = 0; i < segmentCountFM; i++) + for (int i = 0; i < segmentCountFM; i++) + { + // 每两小时 + var fetalMovementSampleTime = DateTimeUtil.ConvertToTimeStamp(midNight.AddHours(2 * i)).ToString()[..10]; + + // 统计开始时间 + var statStartTime = midNight.AddHours(2 * i); + // 统计结束时间 + var statEndTime = midNight.AddHours(2 * (i+1)); + + var isFetalMovementExisted = await _deviceCacheMgr.FetalMovementIsExistedAsync(heartRate.Serialno, fetalMovementSampleTime); + _logger.LogInformation($"{heartRate.Serialno} 胎动记录{isFetalMovementExisted},数据采样时间:{fetalMovementSampleTime}|{midNight.AddHours(2 * i).ToString("yyyy-MM-dd HH:mm:ss")}, 周期:{statStartTime}-{statEndTime} 开始"); + + if (!isFetalMovementExisted) { - // 每两小时 - var fetalMovementSampleTime = DateTimeUtil.ConvertToTimeStamp(midNight.AddHours(2 * i)).ToString()[..10]; - - // 统计开始时间 - var statStartTime = midNight.AddHours(2 * i); - // 统计结束时间 - var statEndTime = midNight.AddHours(2 * (i+1)); - - var isFetalMovementExisted = await _deviceCacheMgr.FetalMovementIsExistedAsync(heartRate.Serialno, fetalMovementSampleTime); - _logger.LogInformation($"{heartRate.Serialno} 胎动记录{isFetalMovementExisted},数据采样时间:{fetalMovementSampleTime}|{midNight.AddHours(2 * i).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 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(fetalMovementNow); + 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(fetalMovementNow); - int pregnancyWeeks = (DateTime.Now - edoc.AddDays(-280)).Days / 7; - if (pregnancyWeeks >= 12 && pregnancyWeeks <= 50) + 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($"{heartRate.Serialno} segmentCountFMIndex: {i} -- fetalMovementSampleTime:{fetalMovementSampleTime}|{midNight.AddHours(2 * i).ToString("yyyy-MM-dd HH:mm:ss")} -- statStartTime: {statStartTime} -- statEndTime: {statEndTime}-- isFetalMovementExisted: {isFetalMovementExisted} "); + + _logger.LogInformation($"{heartRate.Serialno} 胎动数据采样时间:{fetalMovementSampleTime}|{midNight.AddHours(2 * i).ToString("yyyy-MM-dd HH:mm:ss")}, 采样周期:{statStartTime}-{statEndTime}, 原始胎动值:{fetalMovementMapValue}, 佩戴时间 :{duringMins}|{statStartTime}-{phrRange.First()}, 胎动计算值:{fetalMovementValue}, 胎动最终值:{fetalMovement} 已完成."); + + // 获取胎心数据状态与胎动数据状态一致 + var feltalMovementIsAbnormal = fetalHeartRateIsAbnormal; + //var feltalMovementIsAbnormal = 0; + // 推送到api/v1/open/OpenIot/SetFetalMovementConfig + + await _serviceIotApi.SetFetalMovementConfig(heartRate.Serialno, fetalMovement, fetalMovementSampleTime, feltalMovementIsAbnormal); + + // 保存到MySQL数据库 + HisGpsFetalMovement fm = new() { - 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($"{heartRate.Serialno} segmentCountFMIndex: {i} -- fetalMovementSampleTime:{fetalMovementSampleTime}|{midNight.AddHours(2 * i).ToString("yyyy-MM-dd HH:mm:ss")} -- statStartTime: {statStartTime} -- statEndTime: {statEndTime}-- isFetalMovementExisted: {isFetalMovementExisted} "); - - _logger.LogInformation($"{heartRate.Serialno} 胎动数据采样时间:{fetalMovementSampleTime}|{midNight.AddHours(2 * i).ToString("yyyy-MM-dd HH:mm:ss")}, 采样周期:{statStartTime}-{statEndTime}, 原始胎动值:{fetalMovementMapValue}, 佩戴时间 :{duringMins}|{statStartTime}-{phrRange.First()}, 胎动计算值:{fetalMovementValue}, 胎动最终值:{fetalMovement} 已完成."); - - // 获取胎心数据状态与胎动数据状态一致 - var feltalMovementIsAbnormal = fetalHeartRateIsAbnormal; - //var feltalMovementIsAbnormal = 0; - // 推送到api/v1/open/OpenIot/SetFetalMovementConfig - - await _serviceIotApi.SetFetalMovementConfig(heartRate.Serialno, fetalMovement, fetalMovementSampleTime, feltalMovementIsAbnormal); - - // 保存到MySQL数据库 - HisGpsFetalMovement fm = new() - { - FetalMovementId = Guid.NewGuid().ToString("D"), - PersonId = commonPHR!.PersonId, - Serialno = heartRate.Serialno, - 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) + FetalMovementId = Guid.NewGuid().ToString("D"), + PersonId = commonPHR!.PersonId, + Serialno = heartRate.Serialno, + 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(heartRate.Serialno).ConfigureAwait(false); + var fmMsgId = $"{heartRate.Serialno}-{fetalMovementSampleTime}-{Guid.NewGuid().ToString("D")[^3..]}"; + var topic = "topic.push.wx"; + var fmMsg = new { - var device = await _deviceCacheMgr.GetDeviceBySerialNoAsync(heartRate.Serialno).ConfigureAwait(false); - var fmMsgId = $"{heartRate.Serialno}-{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 { - 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 = heartRate.Serialno, - alarmTypeId = 12, - alarmDeviceName = heartRate.Serialno, - 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(heartRate.Serialno, fetalMovementSampleTime,fm); - - } - else - { - _logger.LogWarning($"{heartRate.Serialno} 孕周 {pregnancyWeeks},超出胎动计算范围"); + deviceId = device?.DeviceId, + imei = heartRate.Serialno, + alarmTypeId = 12, + alarmDeviceName = heartRate.Serialno, + 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(heartRate.Serialno, fetalMovementSampleTime,fm); + } else { - _logger.LogInformation($"{heartRate.Serialno} 胎动记录{isFetalMovementExisted},数据采样时间:{fetalMovementSampleTime}|{midNight.AddHours(2 * i).ToString("yyyy-MM-dd HH:mm:ss")}, 周期:{statStartTime}-{statEndTime} 不足两条,不能判断是否持续佩戴"); - + _logger.LogWarning($"{heartRate.Serialno} 孕周 {pregnancyWeeks},超出胎动计算范围"); } } else { - _logger.LogInformation($"{heartRate.Serialno} 胎动记录{isFetalMovementExisted},数据采样时间:{fetalMovementSampleTime}|{midNight.AddHours(2 * i).ToString("yyyy-MM-dd HH:mm:ss")}, 周期:{statStartTime}-{statEndTime} 已处理"); - } + _logger.LogInformation($"{heartRate.Serialno} 胎动记录{isFetalMovementExisted},数据采样时间:{fetalMovementSampleTime}|{midNight.AddHours(2 * i).ToString("yyyy-MM-dd HH:mm:ss")}, 周期:{statStartTime}-{statEndTime} 不足两条,不能判断是否持续佩戴"); + } } - */ - #endregion - - #endregion - - #region 定时计算胎动数据触发器两小时间隔开始 - var fetalMovementKey = $"health_monitor/schedule_push/cal_fetal_movement/imei/{heartRate.Serialno}"; - ///// 计算 0 点秒数 - var fetalMovementLastUpdate = (DateTime)heartRate.LastUpdate!; - DateTime fmScheduleNow = DateTime.Now; - // 小于两小时 - if (fmScheduleNow > fetalMovementLastUpdate && (fmScheduleNow - fetalMovementLastUpdate).TotalHours <= 2) + else { - 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); + _logger.LogInformation($"{heartRate.Serialno} 胎动记录{isFetalMovementExisted},数据采样时间:{fetalMovementSampleTime}|{midNight.AddHours(2 * i).ToString("yyyy-MM-dd HH:mm:ss")}, 周期:{statStartTime}-{statEndTime} 已处理"); } - #endregion - - #region 定时计算胎心数据触发器 {interval} 秒后 - //var fetalKey = $"health_monitor/schedule_push/cal_fetal_heart_rate/imei/{heartRate.Serialno}"; - //await SetIntervalTriggerAsync(fetalKey, heartRate.Serialno, 60 * 15); - #endregion - - #region 定时计算胎心数据触发器下一间隔后 - //// 获取当前时间 - //DateTime nowInterval = DateTime.Now; - - //// 计算下一个15分钟的间隔 - //int minutesToAdd = 15 - (nowInterval.Minute % 15); - //if (minutesToAdd == 15) - //{ - // minutesToAdd = 0; // 如果已经是间隔,则不需要增加分钟 - //} - //// 计算下一间隔的时间 - //DateTime nextInterval = nowInterval.AddMinutes(minutesToAdd) - // .AddSeconds(-nowInterval.Second) - // .AddMilliseconds(-nowInterval.Millisecond); - - //// 计算时间差 - //TimeSpan timeDifference = nextInterval - nowInterval; - - //var fetalKey = $"health_monitor/schedule_push/cal_fetal_heart_rate/imei/{heartRate.Serialno}"; - //await SetIntervalTriggerAsync(fetalKey, heartRate.Serialno, (long)timeDifference.TotalSeconds); - #endregion - #region 高频心率计算 - - //// 获取最近的两个记录,并计算它们的 LastUpdate 时间差 - //var firstTwoPhr = phr.OrderByDescending(i => i.LastUpdate).Take(2).Select(i => i.LastUpdate).ToList(); - //var timeDiff = firstTwoPhr[0] - firstTwoPhr[1]; - - //// 如果需要,将时间差转换为秒 - //var timeDiffInSeconds = timeDiff.TotalSeconds; - - //// 高频心率启动 - //if (timeDiffInSeconds<=highFreqSampleInterval) - //{ - - // var phrFreqstatus =await _deviceCacheMgr.GetPregnancyHeartRateFreqStatusAsync(heartRate.Serialno); - // if (phrFreqstatus == null) - // { - // /// 设置高频状态 - // _logger.LogInformation($"进入高频心率启动状态 timeDiffInSeconds {timeDiffInSeconds},highFreqSampleInterval:{highFreqSampleInterval}"); - // // 设置高频状态 - // var freqFirstPhr= phr.OrderByDescending(i => i.Timestamp).First(); - // await _deviceCacheMgr.SetPregnancyHeartRateFreqStatusAsync(heartRate.Serialno, freqFirstPhr); - // phrFreqstatus = await _deviceCacheMgr.GetPregnancyHeartRateFreqStatusAsync(heartRate.Serialno); - // } - // /// phr PregnancyHeartRate 连续连续正常次数个值都是正常(大于等于triggerHighFreqLow,少于等于triggerHighFreqHig), - // /// 取连续正常次数正常值的平均值,推送到api/v1/open/OpenIot/SetFetalHeartRateConfig - // #region 检查是否连续12个值都是正常的 - // // 获取最近连续正常次数个心率记录 - // var lastPhr = phr.OrderByDescending(i => i.Timestamp).Take(stopHighFreqSampleCount).ToList(); - - // // 检查是否连续12个值都是正常的 - // if (lastPhr.All(i => i.PregnancyHeartRate >= triggerHighFreqLow && i.PregnancyHeartRate <= triggerHighFreqHigh)) - // { - // var avgPhr = lastPhr.Select(i => i.PregnancyHeartRate).Average(); - // // 计算一般心率得到胎心系数 - // await SaveAndPushFetalHeartRateAsync(heartRate, upperAlarmThreshold, lowerAlarmThreshold, avgPhr); - - // } - // #endregion - //} - //// 高频心率结束 - //else - //{ - // var phrFreqstatus = await _deviceCacheMgr.GetPregnancyHeartRateFreqStatusAsync(heartRate.Serialno); - // if (phrFreqstatus != null) - // { - // /// 在highFreqSampleTimes=0一直异常(大于等于triggerHighFreqLow,少于等于triggerHighFreqHig), - // /// 取所有值的平均值,推送胎心数据到api/v1/open/OpenIot/SetFetalHeartRateConfig - // if (highFreqSampleTimes==0) - // { - // // if (phr.OrderByDescending(i => i.Timestamp) - // //.Where(i => i.Timestamp >= phrFreqstatus?.Timestamp) - // //.Skip(1) // 去除首条 - // //.All(i => i.PregnancyHeartRate < triggerHighFreqLow || i.PregnancyHeartRate > triggerHighFreqHig)) - // // { - // // var avgPhr = phr.Select(i => i.PregnancyHeartRate).Average(); - - // // // 推送胎心数据到 api/v1/open/OpenIot/SetFetalHeartRateConfig - // // // 计算一般心率得到胎心系数 - // // } - - // var avgPhr = phr.OrderByDescending(i => i.Timestamp) - // .Where(i => i.Timestamp >= phrFreqstatus?.Timestamp) - // .Skip(1) // 去除首条 - // .Where(i => i.PregnancyHeartRate < triggerHighFreqLow || i.PregnancyHeartRate > triggerHighFreqHigh) - // .Select(i => i.PregnancyHeartRate).Average(); - // // 推送胎心数据到 api/v1/open/OpenIot/SetFetalHeartRateConfig - // // 计算一般心率得到胎心系数 - // await SaveAndPushFetalHeartRateAsync(heartRate, upperAlarmThreshold, lowerAlarmThreshold, avgPhr); - - // } - - // /// 在highFreqSampleTimes>0一直异常(大于等于triggerHighFreqLow,少于等于triggerHighFreqHig), - // /// 取所有值的平均值,推送胎心数据到api/v1/open/OpenIot/SetFetalHeartRateConfig - // if (highFreqSampleTimes > 0 && heartRate.LastUpdate >= (phrFreqstatus?.LastUpdate + TimeSpan.FromSeconds(highFreqSampleTimes))) - // { - - // var avgPhr = phr - // .Where(i => i.Timestamp >= phrFreqstatus?.Timestamp) - // .Skip(1) // 去除首条 - // .Where(i => i.PregnancyHeartRate < triggerHighFreqLow || i.PregnancyHeartRate > triggerHighFreqHigh) - // .Select(i => i.PregnancyHeartRate).Average(); - // // 推送胎心数据到 api/v1/open/OpenIot/SetFetalHeartRateConfig - // // 计算一般心率得到胎心系数 - // await SaveAndPushFetalHeartRateAsync(heartRate, upperAlarmThreshold, lowerAlarmThreshold, avgPhr); - // } - - // // 删除高频状态的首条记录 - // await _deviceCacheMgr.DelPregnancyHeartRateFreqStatusAsync(heartRate.Serialno); - - // /// 设置高频状态 - // _logger.LogInformation($"结束高频心率状态 timeDiffInSeconds {timeDiffInSeconds},highFreqSampleInterval:{highFreqSampleInterval}"); - // } - //} - - #endregion - } - else - { - // 建模中 - //var flag = await _serviceIotApi.SetFetalConfig(heartRate.Serialno); - _logger.LogInformation($"{heartRate.Serialno} 记录不足30条,建模中"); } + */ + #endregion - #region 定时下发触发器(定时建模) - var key = $"health_monitor/schedule_push/pregnancy_heart_rate/imei/{heartRate.Serialno}"; - var schedule_push = await _serviceEtcd.GetValAsync(key).ConfigureAwait(false); + #endregion + } + else + { + _logger.LogInformation($"{heartRate.Serialno} 记录不足30条,建模中"); + } - if (string.IsNullOrWhiteSpace(schedule_push)) - { - // 注册首次下推 - var interval = 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; - // 计算距离下一个$interval天后的8点的时间间隔 - DateTime nextRunTime = new DateTime(now.Year, now.Month, now.Day, 6, pushMin, pushSec).AddDays(interval); - TimeSpan timeUntilNextRun = nextRunTime - now; + + } - if (timeUntilNextRun < TimeSpan.Zero) - { - timeUntilNextRun = timeUntilNextRun.Add(TimeSpan.FromDays(1)); - nextRunTime += TimeSpan.FromDays(1); - } + //try + //{ - var ttl = (long)timeUntilNextRun.TotalSeconds; - var data = new - { - imei = heartRate.Serialno, - create_time = now.ToString("yyyy-MM-dd HH:mm:ss"), - ttl, - next_run_time = nextRunTime.ToString("yyyy-MM-dd HH:mm:ss") - }; - var result = JsonConvert.SerializeObject(data); - await _serviceEtcd.PutValAsync(key, result, ttl, false).ConfigureAwait(false); - } - #endregion - } - } - catch (Exception ex) - { + //} + //catch (Exception ex) + //{ + + // _logger.LogError($"{heartRate.Serialno} 处理孕妇心率数据异常 \n{ex.Message}\n{ex.StackTrace}"); + //} - _logger.LogError($"{heartRate.Serialno} 处理孕妇心率数据异常 \n{ex.Message}\n{ex.StackTrace}"); - } - } //private async Task SaveAndPushFetalHeartRateAsync(HisGpsHeartRate heartRate, int upperAlarmThreshold, int lowerAlarmThreshold, double avgPhr) @@ -768,6 +649,27 @@ namespace HealthMonitor.Service.Resolver { // 计算胎心=孕妇心率*系数 var fetalHeartRate = SafeType.SafeInt(phr * commonPHR?.StatModeAvgFprCoefficient!); + //fetalHeartRate = fetalHeartRate > 220 ? 220 : fetalHeartRate; // 胎心的最大值调整为220,超过都按该值220输出 + if (fetalHeartRate >= 220) + { + // 先使用最小系数计算 + var statMaxValueFprCoefficient = commonPHR?.StatMaxValueFprCoefficient!; + var statMinValueFprCoefficient = commonPHR?.StatMinValueFprCoefficient!; + var coefficient = statMaxValueFprCoefficient < statMinValueFprCoefficient ? statMaxValueFprCoefficient : statMinValueFprCoefficient; + fetalHeartRate = SafeType.SafeInt(phr * coefficient); + if (fetalHeartRate < 220) + { + // 倒推系数 + _logger.LogWarning($"{heartRate.Serialno} 使用极值系数 {coefficient} ,建模数据可能出现异常,请检查"); + } + else + { + fetalHeartRate = 220; + // 倒推系数 + _logger.LogWarning($"{heartRate.Serialno} 使用所有系数都不能放映实际,建模数据可能出现异常,请检查"); + } + } + var isAbnormal = fetalHeartRate > upperAlarmThreshold ? 1 : (fetalHeartRate < lowerAlarmThreshold ? 2 : 0); if (!isFreq) isAbnormal = 0; // 保存到 数据服务 MySQL 数据库