|
- using Etcdserverpb;
- using Google.Protobuf.WellKnownTypes;
- using HealthMonitor.Common;
- using HealthMonitor.Common.helper;
- using HealthMonitor.Model.Service.Mapper;
- using HealthMonitor.Service.Biz;
- using HealthMonitor.Service.Biz.db;
- using HealthMonitor.Service.Cache;
- using HealthMonitor.Service.Etcd;
- using HealthMonitor.Service.MessageQueue;
- using HealthMonitor.Service.Resolver.Interface;
- using HealthMonitor.Service.Sub;
- using HealthMonitor.Service.Sub.Topic.Model;
- using Microsoft.EntityFrameworkCore.Metadata;
- using Microsoft.Extensions.Logging;
- using Newtonsoft.Json;
- using Newtonsoft.Json.Linq;
- using SqlSugar;
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Net;
- using System.Text;
- using System.Threading.Tasks;
- using TelpoDataService.Util.Clients;
- using TelpoDataService.Util.Entities.GpsCard;
- using TelpoDataService.Util.Entities.GpsLocationHistory;
- using TelpoDataService.Util.Models;
- using TelpoDataService.Util.QueryObjects;
-
- namespace HealthMonitor.Service.Resolver
- {
- public class PregnancyHeartRateResolver : IResolver
- {
- private readonly ILogger<PregnancyHeartRateResolver> _logger;
- private readonly TDengineService _serviceTDengine;
-
- private readonly DeviceCacheManager _deviceCacheMgr;
- private readonly IotApiService _serviceIotApi;
-
-
- private readonly AsyncLocal<string> _messageId = new();
- private readonly AsyncLocal<HisGpsHeartRate> _msgData = new();
- private readonly HttpHelper _httpHelper = default!;
- private readonly EtcdService _serviceEtcd;
-
- private readonly GpsLocationHistoryAccessorClient<HisGpsFetalHeartRate> _hisFetalHeartApiClient;
- private readonly GpsLocationHistoryAccessorClient<HisGpsFetalMovement> _hisFetalMovementApiClient;
-
- private readonly FetalMovementNormalValueRangeCacheManager _mgrFetalMovementNormalValueRangeCache;
-
- private readonly MqProcessLogic _serviceMqProcess;
-
- private static int[] SCHEDULE_HOUR = new int[] {0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22};
-
- 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;
-
-
-
-
- public PregnancyHeartRateResolver(ILogger<PregnancyHeartRateResolver> logger,
- HttpHelper httpHelper, EtcdService serviceEtcd, DeviceCacheManager deviceCacheMgr,
- MqProcessLogic serviceMqProcess,
- IotApiService iotApiService, TDengineService serviceDengine, FetalMovementNormalValueRangeCacheManager fetalMovementNormalValueRangeCacheMgr,
- GpsLocationHistoryAccessorClient<HisGpsFetalHeartRate> hisFetalHeartApiClient,
- GpsLocationHistoryAccessorClient<HisGpsFetalMovement> hisFetalMovementApiClient
- )
- {
- _logger = logger;
- _httpHelper = httpHelper;
- _serviceEtcd = serviceEtcd;
- _serviceTDengine = serviceDengine;
- _deviceCacheMgr = deviceCacheMgr;
- _serviceIotApi = iotApiService;
- _serviceMqProcess = serviceMqProcess;
- _hisFetalHeartApiClient = hisFetalHeartApiClient;
- _hisFetalMovementApiClient = hisFetalMovementApiClient;
- _mgrFetalMovementNormalValueRangeCache = fetalMovementNormalValueRangeCacheMgr;
- }
-
- public void SetResolveInfo(PackageMsgModel msg)
- {
- var topicHmPregnancyHeartRate = JsonConvert.DeserializeObject<TopicHmPregnancyHeartRate>(msg.DetailData.ToString()!);
- _messageId.Value = msg.MessageId;
- _msgData.Value = new HisGpsHeartRate()
- {
- HeartRateId = topicHmPregnancyHeartRate!.PregnancyHeartRateId,
- MessageId = topicHmPregnancyHeartRate!.MessageId,
- Serialno = topicHmPregnancyHeartRate!.Serialno,
- HeartRate = topicHmPregnancyHeartRate.PregnancyHeartRate,
- LastUpdate = DateTimeUtil.GetDateTimeFromUnixTimeMilliseconds(SafeType.SafeInt64(topicHmPregnancyHeartRate.LastUpdate) / 1000000),
- CreateTime = DateTimeUtil.GetDateTimeFromUnixTimeMilliseconds(SafeType.SafeInt64(topicHmPregnancyHeartRate.CreateTime) / 1000000),
- Method = topicHmPregnancyHeartRate!.Method,
- IsDisplay = topicHmPregnancyHeartRate!.IsDisplay ? 1 : 0
- };
- }
- public override string ToString()
- {
- return $"{nameof(PregnancyHeartRateResolver)}[{_messageId.Value}]";
- }
-
- public async Task ExecuteMessageAsync()
- {
- 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)
- {
- //_logger.LogInformation($"{heartRate.Serialno} 计算胎心胎动启动");
- #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);
-
- }
-
- #endregion
- //_logger.LogInformation($"{heartRate.Serialno} 触发定时建模");
-
- // 高频心率采样间隔 highFreqSampleInterval = highFreqSampleInterval 最小highFreqSampleInterval=60)
- var highFreqSampleInterval = (int)watchConfig!["highFreqSampleInterval"]! >= 60 ? (int)watchConfig!["highFreqSampleInterval"]!:60;
- // 触发高频监测的心率上限值
- var triggerHighFreqHigh = (int)watchConfig["triggerHighFreqHigh"]!;
- // 触发高频监测的心率下限值
- var triggerHighFreqLow = (int)watchConfig["triggerHighFreqLow"]!;
- // 暂时处理
- triggerHighFreqLow = triggerHighFreqLow > 60 && triggerHighFreqLow < 67 ? 60 : triggerHighFreqLow;
- //停止高频心率采样心率连续正常次数
- var stopHighFreqSampleCount = (int)watchConfig["stopHighFreqSampleCount"]!;
- // 高频心率采集时长 0 为持续采集,非零为高频心率的采集时长
- //var highFreqSampleTimes = (int)watchConfig["highFreqSampleTimes"]!;
- var highFreqSampleTimes = 540;
- // 告警上限阀值
- var upperAlarmThreshold = (int)watchConfig["upperAlarmThreshold"]!;
- // 告警下限阀值
- var lowerAlarmThreshold = (int)watchConfig["lowerAlarmThreshold"]!;
- // EDOC
- var edoc = DateTimeUtil.ToDateTime(watchConfig!["EDOC"]!.ToString());
- // interval (分钟) 固定15分钟
- var intervalFHR = 15;//int)watchConfig["interval"]!;
-
- var daysPhr = await _serviceTDengine.GetBySerialNoAsync<PregnancyHeartRateModel>(heartRate.Serialno, 7);
- var phr = daysPhr
- .Where(p => p.LastUpdate <= heartRate.LastUpdate)
- .ToList();
- _logger.LogInformation($"{heartRate.Serialno} 当前数据在TD数据库:{phr.Select(i=>i.MessageId).Contains(heartRate.MessageId)}");
- var nonFreqPhr = GetNonFreqPregnancyHeartRate(phr, highFreqSampleInterval);
- if (nonFreqPhr.Count >= 30)
- {
- #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,
- pushMin,
- pushSec
- );
-
- // 如果生成的时间在当前时间之前
- // 或者 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);
- _logger.LogInformation($"{heartRate.Serialno}-{heartRate.MessageId} 创建计划统计胎动时间{scheduledDateTime.ToString("yyyy-MM-dd HH:mm:ss")}");
- */
- #endregion
-
-
- #region 定时计算胎动数据触发器每小时间隔开始,延时一个小时计算
- /*
- ///当前时间是的0~1小时返回2小时 ,
- ///当前时间是的1~2小时返回3小时 ,如此类推,
- ///当前时间是的21~22小时返回23小时
- ///当前时间是的23~0小时返回次日1小时
-
- 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=DateTimeUtil.GetNextHour(fmScheduleNow.Hour);
-
- DateTime scheduledDateTime = new DateTime(
- fmScheduleNow.Year,
- fmScheduleNow.Month,
- fmScheduleNow.Day,
- selectedScheduleHour,
- pushMin,
- pushSec
- );
-
- // 跨天
- if (selectedScheduleHour == 1 || selectedScheduleHour == 0)
- {
- scheduledDateTime = scheduledDateTime.AddDays(1);
- }
-
- TimeSpan timeUntilNextRun = scheduledDateTime - fmScheduleNow;
- var ttl = (long)timeUntilNextRun.TotalSeconds;
-
- await SetIntervalTriggerAsync(fetalMovementKey, heartRate.Serialno, ttl, heartRate);
- _logger.LogInformation($"{heartRate.Serialno}-{heartRate.MessageId} 创建计划统计胎动时间{scheduledDateTime.ToString("yyyy-MM-dd HH:mm:ss")}");
- */
-
- #endregion
-
-
- #region 定时计算胎动数据触发器每小时间隔开始,延时一个小时计算
- ///当前时间是的0~1小时返回2小时 ,
- ///当前时间是的1~2小时返回3小时 ,如此类推,
- ///当前时间是的21~22小时返回23小时
- ///当前时间是的23~0小时返回次日1小时
- // lastUpdate默认奇数 // lastUpdate偶数小时
- 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 = DateTimeUtil.GetNextHour(fmScheduleNow.Hour);
-
- DateTime scheduledDateTime = DateTime.Now.Date
- .AddHours(selectedScheduleHour)
- .AddMinutes(pushMin)
- .AddSeconds(pushSec);
-
- // 当前时间与lastUpdate的时间差小于2小时,0~1->2,0~1的数据是1点的sample_time,2点计算
- if (DateTime.Now.Subtract((DateTime)heartRate.LastUpdate!).TotalHours <= 2)
- {
- selectedScheduleHour = DateTimeUtil.GetNextHour(((DateTime)heartRate.LastUpdate).Hour);
- scheduledDateTime = DateTime.Now.Date
- .AddHours(selectedScheduleHour)
- .AddMinutes(pushMin)
- .AddSeconds(pushSec);
-
-
- if (((DateTime)heartRate.LastUpdate).Hour % 2 == 0)
- {
- // lastUpdate偶数小时
- fetalMovementKey = $"health_monitor/schedule_push/cal_fetal_movement/imei/{heartRate.Serialno}/even_hour";
- }
- else
- {
- // lastUpdate奇数小时
- fetalMovementKey = $"health_monitor/schedule_push/cal_fetal_movement/imei/{heartRate.Serialno}/odd_hour";
- }
- }
- // sample_time采集时间与计划计算时间最长是2小时,次日上报引起
- else
- {
- _logger.LogWarning($"iot 上报延迟,last_update与计划计算时间超过2小时");
- }
-
-
- // 跨天
- if (selectedScheduleHour == 1 || selectedScheduleHour == 0)
- {
- scheduledDateTime = scheduledDateTime.AddDays(1);
- }
-
- TimeSpan timeUntilNextRun = scheduledDateTime - fmScheduleNow;
- var ttl = (long)timeUntilNextRun.TotalSeconds;
-
- await SetIntervalTriggerAsync(fetalMovementKey, heartRate.Serialno, ttl, heartRate);
- _logger.LogInformation($"{heartRate.Serialno}-{heartRate.MessageId} 创建计划统计胎动时间{scheduledDateTime.ToString("yyyy-MM-dd HH:mm:ss")}");
-
-
- #endregion
-
-
- #region 计算胎心数据(按心率时间LastUpdate)
- var commonPHR = await _serviceTDengine.GetLastAsync<PregnancyCommonHeartRateModel>(heartRate.Serialno);
- // 如果commonPHR为空或已建模已过期,进行建模和保存
- if (commonPHR == null || !commonPHR.CreateTime.Date.Equals(DateTime.Now.Date))
- // 获取当天 6:11 的时间点, 6:11 所有数据都已经全部建模
- //if (commonPHR == null || commonPHR.CreateTime < DateTime.Today.AddHours(6).AddMinutes(11))
- {
- // 处理孕妇业务,计算一般心率并下发
- 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<PregnancyCommonHeartRateModel>("hm_pchr", commonPHR!);
- _logger.LogInformation($"保存TDengine完成");
- }
-
- // 获取最近的两个记录,并计算它们的 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;
-
- // 高频统计结束时间
- var FreqStatsEnd = DateTime.Now;
-
-
- // 高频心率启动(在高频第二条才能判断)
- if (timeDiffInSeconds <= highFreqSampleInterval)
- {
- // 设置延时计算高频心率的胎心
- var freqHearRateHey = $"health_monitor/schedule_push/cal_freqhr_fetal_heart_rate/imei/{heartRate.Serialno}";
- var phrFreqstatus = await _deviceCacheMgr.GetPregnancyHeartRateFreqStatusAsync(heartRate.Serialno);
- if (phrFreqstatus == null)
- {
- /// 设置高频状态
- _logger.LogInformation($"{heartRate.Serialno} 进入高频心率启动状态,时间差 timeDiffInSeconds {timeDiffInSeconds},highFreqSampleInterval:{highFreqSampleInterval}");
- // 设置高频状态
- _logger.LogInformation($"{heartRate.Serialno} 前7天到当前记录数 {phr.Count},当前 {phr.First().MessageId}");
- var freqFirstPhr = phr.OrderByDescending(i => i.LastUpdate)
- .Skip(1) //在高频第二条才能判断,所以要去除本条记录
- .First();
- _logger.LogInformation($"首条高频附近的3条:{string.Join(',', phr.OrderByDescending(i => i.LastUpdate).Take(3).Select(i=>i.MessageId))}");
- await _deviceCacheMgr.SetPregnancyHeartRateFreqStatusAsync(heartRate.Serialno, freqFirstPhr);
- _logger.LogInformation($"{heartRate.Serialno} 设置高频状态,首条高频心率ID:{freqFirstPhr.MessageId}");
- phrFreqstatus = await _deviceCacheMgr.GetPregnancyHeartRateFreqStatusAsync(heartRate.Serialno);
- HisGpsHeartRate freqFirstHR = new()
- {
- CreateTime = freqFirstPhr.CreateTime,
- DeviceKey = freqFirstPhr.DeviceKey,
- HeartRate = freqFirstPhr.PregnancyHeartRate,
- HeartRateId = freqFirstPhr.PregnancyHeartRateId,
- IsDisplay = freqFirstPhr.IsDisplay ? 1 : 0,
- LastUpdate = freqFirstPhr.LastUpdate,
- MessageId = freqFirstPhr.MessageId,
- Method = freqFirstPhr.Method,
- PersonId = freqFirstPhr.PersonId,
- Serialno = freqFirstPhr.SerialNumber,
- };
- // 设置的续租周期
- await SetFreqHeartRateTriggerAsync(freqHearRateHey, heartRate.Serialno, highFreqSampleInterval+90, freqFirstHR);
- }
- // 续租延时计算高频心率的胎心
- await SetFreqHeartRateTriggerAsync(freqHearRateHey, heartRate.Serialno, highFreqSampleInterval+90);
-
- /// phr PregnancyHeartRate 连续连续正常次数个值都是正常(大于等于triggerHighFreqLow,少于等于triggerHighFreqHig),
- /// 取连续正常次数正常值的平均值,推送到api/v1/open/OpenIot/SetFetalHeartRateConfig
- #region 检查高频状态是否连续12个心率值都是正常的
- // 获取最近连续正常次数个心率记录
- //_logger.LogInformation($"{heartRate.Serialno} 设置 stopHighFreqSampleCount {stopHighFreqSampleCount}");
-
- //var lastPhr = phr.OrderByDescending(i => i.LastUpdate).Take(stopHighFreqSampleCount).ToList();
- var lastPhr = phr.Where(i => i.LastUpdate >= phrFreqstatus!.LastUpdate)
- .OrderByDescending(i => i.LastUpdate).Take(stopHighFreqSampleCount).ToList();
- _logger.LogInformation($"{heartRate.Serialno} 最后段记录数量:lastPhr.Count {lastPhr.Count},stopHighFreqSampleCount {stopHighFreqSampleCount}");
- _logger.LogInformation($"{heartRate.Serialno} 条件1-数量 Count:{lastPhr.Count >= stopHighFreqSampleCount}");
- _logger.LogInformation($"{heartRate.Serialno} 条件2-是否正常值 ALL{lastPhr.All(i => i.PregnancyHeartRate >= triggerHighFreqLow && i.PregnancyHeartRate <= triggerHighFreqHigh)}");
-
- // 检查是否连续12个值都是正常的
- if ((lastPhr.Count >= stopHighFreqSampleCount) &&
- 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} 次采样心率正常,将下发指令");
-
- var freqSaveAndPushFetalHeartRate = await _deviceCacheMgr.GetBizIntervalAsync(heartRate.Serialno, "SaveAndPushFetalHeartRate");
- // 高频不停,15分钟内只下发一条
- if (string.IsNullOrEmpty(freqSaveAndPushFetalHeartRate))
- {
- heartRate.HeartRate = (int)avgPhr;
- // 最后一条高频心率
- var lastFreqHr = lastPhr.First();
- var ts = DateTimeUtil.GetTimeDifferenceInSeconds((DateTime)lastFreqHr.LastUpdate!, phrFreqstatus!.LastUpdate);
- // 判断是否够highFreqSampleTimes,540s
- if (ts < highFreqSampleTimes)
- {
- /// 不够10分钟最近12个数据生成胎心值
- int selectedHrValue = SelectValueFromFreqHeartRate(heartRate, triggerHighFreqHigh, triggerHighFreqLow, lastPhr);
-
- heartRate.HeartRate = selectedHrValue;
- _logger.LogInformation($"{heartRate.Serialno} 不够10分钟最近12个数据中取 {heartRate.HeartRate} 生成胎心值");
-
- heartRate.LastUpdate = lastFreqHr.LastUpdate;
- }
- else
- {
-
- /// 超过10分钟最近12个数据生成胎心值
- int selectedHrValue = SelectValueFromFreqHeartRate(heartRate, triggerHighFreqHigh, triggerHighFreqLow, lastPhr);
- heartRate.HeartRate = selectedHrValue;
- _logger.LogInformation($"{heartRate.Serialno} 超过10分钟最近12个数据中取 {heartRate.HeartRate} 生成胎心值");
- }
- _logger.LogInformation($"{heartRate.Serialno} 高频数据触发连续12个值都是正常的的高频心率处理");
- await SaveAndPushFetalHeartRateEndFreqHeartRateAsync(heartRate, commonPHR,highFreqSampleTimes ,upperAlarmThreshold, lowerAlarmThreshold, DateTimeUtil.ConvertToTimeStamp(phrFreqstatus!.LastUpdate).ToString(), phrFreqstatus!.LastUpdate, FreqStatsEnd);
-
- // 删除高频状态的首条记录
- await _deviceCacheMgr.DelPregnancyHeartRateFreqStatusAsync(heartRate.Serialno);
- // 设置15分的SaveAndPushFetalHeartRate业务间隔
- await _deviceCacheMgr.SetBizIntervalAsync(heartRate.Serialno, "SaveAndPushFetalHeartRate");
- _logger.LogInformation($"{heartRate.Serialno} 连续 {stopHighFreqSampleCount} 次采样心率正常,结束高频心率状态, timeDiffInSeconds {timeDiffInSeconds},highFreqSampleInterval:{highFreqSampleInterval},高频状态持续{((DateTime)heartRate.LastUpdate - phrFreqstatus!.LastUpdate).TotalSeconds} 秒,高频心率平均值:{(int)avgPhr}");
- }
- else
- {
- _logger.LogWarning($"{heartRate.Serialno} 连续 {stopHighFreqSampleCount} 次采样心率正常,设备端应该结束高频状态,但设备端没有结束高频,平台高频15分钟内已经下发过指令");
- }
-
- }
- else
- {
- _logger.LogInformation($"{heartRate.Serialno} 处于高频状态...");
- }
- #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 lastPhr = phr.OrderByDescending(i => i.LastUpdate)
- .Where(i => i.LastUpdate >= phrFreqstatus?.LastUpdate)
- .Skip(1) // 去除首条
- .Where(i => i.PregnancyHeartRate < triggerHighFreqLow || i.PregnancyHeartRate > triggerHighFreqHigh)
- .ToList();
- //.Select(i => i.PregnancyHeartRate);
- //.Average();
-
- var avgPhr = lastPhr.Select(i => i.PregnancyHeartRate).Average();
- // 推送胎心数据到 api/v1/open/OpenIot/SetFetalHeartRateConfig
- // 计算一般心率得到胎心系数
- // 高频数据不建模
- FreqStatsEnd = firstTwoPhr[1];
- _logger.LogInformation($"{heartRate.Serialno} 高频状态已经持续{(FreqStatsEnd - phrFreqstatus!.LastUpdate).TotalSeconds} 秒,highFreqSampleTimes={highFreqSampleTimes}秒,即将结束高频状态,高频心率平均值:{(int)avgPhr},将下发指令");
-
- //await SaveAndPushFreqFetalHeartRateAsync(heartRate, commonPHR, upperAlarmThreshold, lowerAlarmThreshold, avgPhr, DateTimeUtil.ConvertToTimeStamp(phrFreqstatus!.LastUpdate).ToString());
- //await SaveAndPushFetalHeartRateAsync(heartRate, commonPHR, upperAlarmThreshold, lowerAlarmThreshold, avgPhr, DateTimeUtil.ConvertToTimeStamp(phrFreqstatus!.LastUpdate).ToString(), phrFreqstatus!.LastUpdate, FreqStatsEnd);
-
- heartRate.HeartRate = (int)avgPhr;
- //await SaveAndPushFetalHeartRateEndFreqHeartRateAsync(heartRate, commonPHR, upperAlarmThreshold, lowerAlarmThreshold, DateTimeUtil.ConvertToTimeStamp(phrFreqstatus!.LastUpdate).ToString(), phrFreqstatus!.LastUpdate, FreqStatsEnd);
- // 最后一条高频心率
- var lastFreqHr = lastPhr.First();
-
- var ts = DateTimeUtil.GetTimeDifferenceInSeconds((DateTime)lastFreqHr.LastUpdate!, phrFreqstatus!.LastUpdate);
- // 判断是否够highFreqSampleTimes,540s
- if (ts < highFreqSampleTimes)
- {
- /// 不够10分钟最近12个数据生成胎心值
- int selectedHrValue = SelectValueFromFreqHeartRate(heartRate, triggerHighFreqHigh, triggerHighFreqLow, lastPhr);
- heartRate.HeartRate = selectedHrValue;
- _logger.LogInformation($"{heartRate.Serialno} 不够10分钟最近12个数据中取 {heartRate.HeartRate} 生成胎心值");
-
- heartRate.LastUpdate = lastFreqHr.LastUpdate;
- }
- else
- {
- int selectedHrValue = SelectValueFromFreqHeartRate(heartRate, triggerHighFreqHigh, triggerHighFreqLow, lastPhr);
- heartRate.HeartRate = selectedHrValue;
- /// 超过10分钟最近12个数据生成胎心值
- _logger.LogInformation($"{heartRate.Serialno} 超过10分钟最近12个数据中取 {heartRate.HeartRate} 生成胎心值");
- }
- _logger.LogInformation($"{heartRate.Serialno} 高频结束后的highFreqSampleTimes=0的高频心率处理");
- await SaveAndPushFetalHeartRateEndFreqHeartRateAsync(heartRate, commonPHR, highFreqSampleTimes, upperAlarmThreshold, lowerAlarmThreshold, DateTimeUtil.ConvertToTimeStamp(phrFreqstatus!.LastUpdate).ToString(), phrFreqstatus!.LastUpdate, FreqStatsEnd);
-
- }
-
- /// 在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)
- .Skip(1)
- .ToList();
-
- _logger.LogInformation($"{heartRate.Serialno} 高频周期 {phrFreqstatus?.LastUpdate.ToString("yyyy-MM-dd HH:mm:ss")}--{firstTwoPhr[1].ToString("yyyy-MM-dd HH:mm:ss")} 产生高频心率数量 {filterPhr.Count} 条");
-
- // 高频心率数据大于stopHighFreqSampleCount/12个才计算胎心数据
- //
- if (filterPhr.Count > stopHighFreqSampleCount)
- {
- FreqStatsEnd = firstTwoPhr[1];
-
- var avgPhr = filterPhr
- .OrderByDescending(i => i.LastUpdate)
- .Take(stopHighFreqSampleCount) // 计算最后12条
- .Select(i => i.PregnancyHeartRate).Average();
-
- //_logger.LogInformation($"{heartRate.Serialno} 高频状态已经持续{(FreqStatsEnd - phrFreqstatus!.LastUpdate).TotalSeconds} 秒,约定的 {highFreqSampleTimes} 秒,即将结束高频状态,高频心率平均值:{(int)avgPhr},将下发指令");
- //计算高频
- //await SaveAndPushFetalHeartRateAsync(heartRate, commonPHR, upperAlarmThreshold, lowerAlarmThreshold, avgPhr, DateTimeUtil.ConvertToTimeStamp(phrFreqstatus!.LastUpdate).ToString(), phrFreqstatus!.LastUpdate, FreqStatsEnd);
- heartRate.HeartRate = (int)avgPhr;
- //await SaveAndPushFetalHeartRateEndFreqHeartRateAsync(heartRate, commonPHR, upperAlarmThreshold, lowerAlarmThreshold, DateTimeUtil.ConvertToTimeStamp(phrFreqstatus!.LastUpdate).ToString(), phrFreqstatus!.LastUpdate, FreqStatsEnd);
-
- // 判断是否够highFreqSampleTimes,540s
- var lastPhr = filterPhr
- .OrderByDescending(i => i.LastUpdate)
- .Take(stopHighFreqSampleCount).ToList();
-
- // 最后一条高频心率
- var lastFreqHr = lastPhr.First();
-
- var ts = DateTimeUtil.GetTimeDifferenceInSeconds((DateTime)lastFreqHr.LastUpdate!, phrFreqstatus!.LastUpdate);
-
- if (ts < highFreqSampleTimes)
- {
- /// 不够10分钟最近12个数据的成胎心值
- int selectedHrValue = SelectValueFromFreqHeartRate(heartRate, triggerHighFreqHigh, triggerHighFreqLow, lastPhr);
- heartRate.HeartRate = selectedHrValue;
- _logger.LogInformation($"{heartRate.Serialno} 不够10分钟最近12个数据中取 {heartRate.HeartRate} 生成胎心值");
-
- heartRate.LastUpdate = lastFreqHr.LastUpdate;
- }
- else
- {
- int selectedHrValue = SelectValueFromFreqHeartRate(heartRate, triggerHighFreqHigh, triggerHighFreqLow, lastPhr);
- heartRate.HeartRate = selectedHrValue;
- /// 超过10分钟最近12个数据的生成胎心值
- _logger.LogInformation($"{heartRate.Serialno} 超过10分钟最近12个数据的中取 {heartRate.HeartRate} 生成胎心值");
- }
- _logger.LogInformation($"{heartRate.Serialno} 高频结束后的在highFreqSampleTimes>0 正常心率(通常情况)触发的高频心率处理");
- await SaveAndPushFetalHeartRateEndFreqHeartRateAsync(heartRate, commonPHR, highFreqSampleTimes, upperAlarmThreshold, lowerAlarmThreshold, DateTimeUtil.ConvertToTimeStamp(phrFreqstatus!.LastUpdate).ToString(), phrFreqstatus!.LastUpdate, FreqStatsEnd);
- }
- else
- {
- _logger.LogWarning($"{heartRate.Serialno} 高频心率的数据不大于{stopHighFreqSampleCount}条,不进行高频数据的胎心计算");
-
- }
-
- // 删除高频状态的首条记录
- await _deviceCacheMgr.DelPregnancyHeartRateFreqStatusAsync(heartRate.Serialno);
- // (逐条计算)计算本次常规心率的胎心数据(高频结束后,实时处理)
- // await CalculateNormalFetalHeartRateAsync(heartRate, upperAlarmThreshold, lowerAlarmThreshold, intervalFHR, commonPHR);
- await CalculateNormalFetalHeartRateScheduleAsync(heartRate);
-
- }
- ///不满足持续10分钟highFreqSampleTimes或出现时间倒叙
- ///
- else
- {
-
- // 高频结束后与常规的心率时间倒叙
- if ((firstTwoPhr[1] - phrFreqstatus!.LastUpdate).TotalSeconds < 0)
- {
- _logger.LogInformation($"{heartRate.Serialno} 高频结束出现时间倒叙,计算当条心率创建之前的高频心率");
- #region 计算当条心率创建之前的高频心率
- // 取得高频之后的所有数据
- var phrFlashBack = daysPhr.Where(p => p.LastUpdate >= phrFreqstatus!.LastUpdate)
- .OrderByDescending(i => i.LastUpdate);
- // 取得高频数据
- var freqCollection = phrFlashBack.ToList();
-
-
- _logger.LogInformation($"{heartRate.Serialno} 高频数据个数{freqCollection.Count},触发高频开始时间{phrFreqstatus!.LastUpdate}");
- if (freqCollection.Count > stopHighFreqSampleCount)
- {
- // 计算高频产生的胎心
- var avgPhr = freqCollection
- .OrderByDescending(i => i.LastUpdate)
- .Take(stopHighFreqSampleCount) // 计算最后12条
- .Select(i => i.PregnancyHeartRate).Average();
-
- heartRate.HeartRate = (int)avgPhr;
- // await SaveAndPushFetalHeartRateEndFreqHeartRateAsync(heartRate, commonPHR, upperAlarmThreshold, lowerAlarmThreshold, DateTimeUtil.ConvertToTimeStamp(phrFreqstatus!.LastUpdate).ToString(), phrFreqstatus!.LastUpdate, FreqStatsEnd);
- //_logger.LogInformation($"{heartRate.Serialno} 高频数据个数{freqCollection.Count},触发高频开始时间{phrFreqstatus!.LastUpdate},高频心率平均值:{(int)avgPhr}");
-
- // 判断是否够highFreqSampleTimes,540s
- var lastPhr = freqCollection
- .OrderByDescending(i => i.LastUpdate)
- .Take(stopHighFreqSampleCount)
- .ToList();
- // 最后一条高频心率
- var lastFreqHr = lastPhr.First();
-
- var ts = DateTimeUtil.GetTimeDifferenceInSeconds((DateTime)lastFreqHr.LastUpdate!, phrFreqstatus!.LastUpdate);
- if (ts < highFreqSampleTimes)
- {
- /// 不够10分钟最近12个数据生成胎心值
- int selectedHrValue = SelectValueFromFreqHeartRate(heartRate, triggerHighFreqHigh, triggerHighFreqLow, lastPhr);
- heartRate.HeartRate = selectedHrValue;
- _logger.LogInformation($"{heartRate.Serialno} 不够10分钟最近12个数据中取 {heartRate.HeartRate} 生成胎心值");
-
- heartRate.LastUpdate = lastFreqHr.LastUpdate;
- }
- else
- {
- int selectedHrValue = SelectValueFromFreqHeartRate(heartRate, triggerHighFreqHigh, triggerHighFreqLow, lastPhr);
- heartRate.HeartRate = selectedHrValue;
- /// 超过10分钟最近12个数据生成胎心值
- _logger.LogInformation($"{heartRate.Serialno} 超过10分钟最近12个数据中取 {heartRate.HeartRate} 生成胎心值");
- }
- _logger.LogInformation($"{heartRate.Serialno} 高频结束后的时间倒序的正常心率触发的高频心率处理");
- await SaveAndPushFetalHeartRateEndFreqHeartRateAsync(heartRate, commonPHR, highFreqSampleTimes, upperAlarmThreshold, lowerAlarmThreshold, DateTimeUtil.ConvertToTimeStamp(phrFreqstatus!.LastUpdate).ToString(), phrFreqstatus!.LastUpdate, FreqStatsEnd);
-
- _logger.LogInformation($"{heartRate.Serialno} 高频数据个数{freqCollection.Count},触发高频开始时间{phrFreqstatus!.LastUpdate},时间倒叙");
- }
- else
- {
- _logger.LogInformation($"{heartRate.Serialno} 时间倒叙触发计算高频心率的数据不足{stopHighFreqSampleCount}条,不进行胎心计算");
-
- }
- #endregion
-
- }
- else
- {
- _logger.LogInformation($"{heartRate.Serialno} 高频持续时间不足{highFreqSampleTimes},只持续{(firstTwoPhr[1] - phrFreqstatus!.LastUpdate).TotalSeconds} 秒");
- }
- // 删除高频状态的首条记录
- await _deviceCacheMgr.DelPregnancyHeartRateFreqStatusAsync(heartRate.Serialno);
- // (逐条计算)计算本次常规心率的胎心数据(高频结束后,实时处理)
- //await CalculateNormalFetalHeartRateAsync(heartRate, upperAlarmThreshold, lowerAlarmThreshold, intervalFHR, commonPHR);
- await CalculateNormalFetalHeartRateScheduleAsync(heartRate);
- }
-
- // 删除高频状态的首条记录
- await _deviceCacheMgr.DelPregnancyHeartRateFreqStatusAsync(heartRate.Serialno);
-
- _logger.LogInformation($"{heartRate.Serialno} 超时结束高频心率状态 timeDiffInSeconds {timeDiffInSeconds},highFreqSampleInterval:{highFreqSampleInterval},高频状态持续{(firstTwoPhr[1] - phrFreqstatus!.LastUpdate).TotalSeconds} 秒");
-
-
- //// 使用延后计算
- //var fhrScheduleKey = $"health_monitor/schedule_push/cal_fetal_heart_rate/imei/{heartRate.Serialno}";
- //var fhrScheduleTTL = 60;
- //await SetIntervalTriggerAsync(fhrScheduleKey, heartRate.Serialno, fhrScheduleTTL, heartRate);
- }
- // 常规心率(本次心率可能是高频心率的首条,所以要使用延后计算胎心率)
- else
- {
-
- // 本次心率可能是高频心率的首条,所以要使用本次常规心率延后计算胎心率
- //var fhrScheduleKey = $"health_monitor/schedule_push/cal_fetal_heart_rate/imei/{heartRate.Serialno}";
- //var fhrScheduleTTL = 30;
- //await SetIntervalTriggerAsync(fhrScheduleKey, heartRate.Serialno, fhrScheduleTTL, heartRate);
- //_logger.LogInformation($"{heartRate.Serialno} 延时50秒,判断当前数据是否为高频首条");
-
- // 本次心率可能是高频心率的首条,所以要使用本次常规心率延后计算胎心率 查询30秒后是否有高频缓存
- Thread thread = new(async () =>
- {
- try
- {
- #region 休眠highFreqSampleInterval2秒
- var startTime = DateTime.Now;
- //var highFreqSampleInterval2 = (int)watchConfig!["highFreqSampleInterval"]!+5;
- var highFreqSampleInterval2 = highFreqSampleInterval;
- var during = TimeSpan.FromSeconds(highFreqSampleInterval2);
- while (true)
- {
- if (DateTime.Now - startTime > during)
- {
- break;
- }
- await Task.Delay(TimeSpan.FromSeconds(1));
- }
- #endregion
- var phrFreqstatus = await _deviceCacheMgr.GetPregnancyHeartRateFreqStatusAsync(heartRate.Serialno);
- _logger.LogInformation($"phrFreqstatus==null:{phrFreqstatus == null}");
- _logger.LogInformation($"phrFreqstatus.LastUpdate < heartRate.LastUpdate:{phrFreqstatus?.LastUpdate < heartRate.LastUpdate}");
- if (phrFreqstatus == null || phrFreqstatus.LastUpdate < heartRate.LastUpdate)
- {
- // (逐条计算)常规心率
- //await CalculateNormalFetalHeartRateAsync(heartRate, upperAlarmThreshold, lowerAlarmThreshold, intervalFHR, commonPHR);
- //_logger.LogInformation($"{heartRate.Serialno} 计算常规心率");
-
- // 延时计算常规心率
- await CalculateNormalFetalHeartRateScheduleAsync(heartRate);
- }
-
- }
- catch (Exception ex)
- {
-
- _logger.LogError($"处理延时计算异常:{ex.Message}, {ex.StackTrace}");
- }
-
- });
- thread.Start();
- }
- }
-
- #endregion
- }
- else
- {
- _logger.LogInformation($"{heartRate.Serialno} 记录不足30条,建模中");
- }
- }
-
-
- }
- catch (Exception ex)
- {
-
- _logger.LogError($"{heartRate.Serialno} 处理孕妇心率数据异常 \n{ex.Message}\n{ex.StackTrace}");
- }
-
- }
-
-
-
-
- /// <summary>
- /// 延时计算
- /// </summary>
- /// <param name="heartRate"></param>
- /// <returns></returns>
- public async Task CalculateNormalFetalHeartRateScheduleAsync(HisGpsHeartRate heartRate)
- {
- var fhrScheduleKey = $"health_monitor/schedule_push/cal_normalphr_fetal_heart_rate/imei/{heartRate.Serialno}";
-
- DateTime nowInterval = DateTime.Now;
- if (nowInterval.Second > 0)
- {
- nowInterval = nowInterval.AddMinutes(1);
- }
- // 需要减去的分钟
- int minutesToSubtract = nowInterval.Minute % INTERVAL_FHR;
- var nextRunTime=nowInterval
- .AddMinutes(-minutesToSubtract)
- .AddMinutes(INTERVAL_FHR);
- TimeSpan timeUntilNextRun = nextRunTime - nowInterval;
- var ttl = (long)timeUntilNextRun.TotalSeconds;
- await SetIntervalTriggerAsync(fhrScheduleKey, heartRate.Serialno, ttl, heartRate);
- _logger.LogInformation($"{heartRate.Serialno}--{heartRate.MessageId} 触发延时计算常规胎心");
- }
-
-
- /// <summary>
- /// 从高频心率数据中取心率值计算胎心值
- /// </summary>
- /// <param name="heartRate"></param>
- /// <param name="triggerHighFreqHigh"></param>
- /// <param name="triggerHighFreqLow"></param>
- /// <param name="lastPhr"></param>
- /// <returns></returns>
- private int SelectValueFromFreqHeartRate(HisGpsHeartRate heartRate, int triggerHighFreqHigh, int triggerHighFreqLow, List<PregnancyHeartRateModel> lastPhr)
- {
- // 连续12个心率的值的最小值
- var selectedHrValue = lastPhr.Select(i => i.PregnancyHeartRate).Min();
- _logger.LogInformation($"{heartRate.Serialno} 最近12个数据的最小值 {selectedHrValue}");
- /// 高频状态下,取值心率低于高频最小值阀值,则需要取最大值进行计算。刚好跟大于高频最大值阀值刚好相反
- if (selectedHrValue < triggerHighFreqLow)
- {
- // 低于高频最小值阀值,则需要取最大值
- var selectedHrMax = lastPhr.Select(i => i.PregnancyHeartRate).Max();
- _logger.LogInformation($"{heartRate.Serialno} 最近12个数据的最小值 {selectedHrValue},低于高频最小值阀值 {triggerHighFreqLow},取最近12个数据的最大值{selectedHrMax}作为心率");
- selectedHrValue = selectedHrMax;
- }
-
- if (selectedHrValue > triggerHighFreqHigh)
- {
- var selectedHrMin = lastPhr.Select(i => i.PregnancyHeartRate).Min();
- _logger.LogInformation($"{heartRate.Serialno} 最近12个数据的最小值 {selectedHrValue},低于高频最大值阀值 {triggerHighFreqHigh},最近12个数据的最小值{selectedHrMin}作为心率");
- selectedHrValue = selectedHrMin;
- }
-
- return selectedHrValue;
- }
-
-
- /// <summary>
- /// 高频胎心处理
- /// 1. 高频数据触发连续12个值都是正常的的高频心率处理
- /// 2. 高频结束后的highFreqSampleTimes=0的高频心率处理
- /// 3. 高频结束后的在highFreqSampleTimes>0 正常心率触发的高频心率处理(常态)
- /// 4. 高频结束后的时间倒序的正常心率触发的高频心率处理
- /// 5. 高频结束后计算胎心数据,防止结束后与常规心理的胎心处理过长,定时器时长highFreqSampleInterval触发的高频心率处理
- /// 高频结束后超过9分钟且不在阈值内才告警
- /// </summary>
- /// <param name="heartRate"></param>
- /// <param name="commonPHR"></param>
- /// <param name="highFreqSampleTimes"></param>
- /// <param name="upperAlarmThreshold"></param>
- /// <param name="lowerAlarmThreshold"></param>
- /// <param name="sampleTime"></param>
- /// <param name="statStartTime"></param>
- /// <param name="statEndTime"></param>
- /// <returns></returns>
- private async Task SaveAndPushFetalHeartRateEndFreqHeartRateAsync(HisGpsHeartRate heartRate, PregnancyCommonHeartRateModel commonPHR, int highFreqSampleTimes, int upperAlarmThreshold, int lowerAlarmThreshold, string sampleTime, DateTime statStartTime, DateTime statEndTime)
- {
- // 计算胎心=孕妇心率*系数
-
- #region 胎心系数使用基于心率与中位数对比
- var coefficient = 0f;
- // 孕妇心率少于中位数,取StatMinValueFprCoefficient
- if (heartRate.HeartRate < commonPHR!.Mode)
- {
- coefficient = commonPHR.StatMinValueFprCoefficient!;
- _logger.LogInformation($"{heartRate.Serialno} 孕妇心率少于中位数,使用最小值系数 {coefficient}");
- }
- // 孕妇心率大于中位数,取StatMaxValueFprCoefficient与StatModeAvgFprCoefficient中少的那个
- else if (heartRate.HeartRate > commonPHR.Mode)
- {
- if (commonPHR.StatModeAvgFprCoefficient > commonPHR.StatMaxValueFprCoefficient)
- {
- coefficient = commonPHR.StatMaxValueFprCoefficient!;
- _logger.LogInformation($"{heartRate.Serialno} 孕妇心率大于中位数,使用最大值系数 {coefficient}");
- }
- else
- {
- coefficient = commonPHR.StatModeAvgFprCoefficient!;
- _logger.LogInformation($"{heartRate.Serialno} 孕妇心率大于中位数,使用均值系数 {coefficient}");
- }
- }
- else
- {
- coefficient = commonPHR.StatModeAvgFprCoefficient;
- _logger.LogInformation($"{heartRate.Serialno} 孕妇心率等于中位数,使用均值系数 {coefficient}");
- }
- #endregion
-
- var fetalHeartRate = SafeType.SafeInt(heartRate.HeartRate! * coefficient);
- // 胎心的最大值调整为220,超过都按该值220输出
- // fetalHeartRate = fetalHeartRate>= 220 ? 220 : fetalHeartRate;
-
- var phrFreqstatus = await _deviceCacheMgr.GetPregnancyHeartRateFreqStatusAsync(heartRate.Serialno);
- var isAbnormal = 0;
- #region 判断是否够highFreqSampleTimes,540s
- var ts = DateTimeUtil.GetTimeDifferenceInSeconds((DateTime)heartRate.LastUpdate!, phrFreqstatus!.LastUpdate);
- // 判断是否够highFreqSampleTimes,540s
- ///高频时长不足10分钟停止高频的胎心值生成说明:最近12个数据的最小值生成胎心值,
- ///对于小于高频下限阀值取高频下限阀值进行转换;
- ///对于高于高频上限阀值取高频上限阀值进行转换。并输出相关日志后续进行数据分析。
- if (ts < highFreqSampleTimes)
- {
- if (fetalHeartRate > upperAlarmThreshold)
- {
- _logger.LogWarning($"{heartRate.Serialno} 高频持续不足10分钟,计算胎心值 {fetalHeartRate} 高于高频警告上限阀值{upperAlarmThreshold},最后胎心值{upperAlarmThreshold},并且不告警");
- fetalHeartRate = upperAlarmThreshold;
-
-
- }
- else if (fetalHeartRate < lowerAlarmThreshold)
- {
- _logger.LogWarning($"{heartRate.Serialno} 高频持续不足10分钟,计算胎心值 {fetalHeartRate} 低于高频警告下限阀值 {lowerAlarmThreshold},最后胎心值{lowerAlarmThreshold},并且不告警");
- fetalHeartRate = lowerAlarmThreshold;
- }
- else
- {
- _logger.LogWarning($"{heartRate.Serialno} 高频持续不足10分钟,在高频警告下限阀值 {lowerAlarmThreshold} 和 高频警告上限阀值:{upperAlarmThreshold}之间,最后胎心值{fetalHeartRate},并且不告警");
- }
-
- }
- // 超过highFreqSampleTimes,540s
- else
- {
- if (fetalHeartRate > 220)
- {
- fetalHeartRate = 220;
- _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")}");
- }
-
- // 胎心的最小值调整为90,超过都按该值90
- if (fetalHeartRate < 90)
- {
- fetalHeartRate = 90;
- _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")}");
- }
- isAbnormal = fetalHeartRate > upperAlarmThreshold ? 1 : (fetalHeartRate < lowerAlarmThreshold ? 2 : 0);
- }
- #endregion
-
-
-
- //if (phrFreqstatus == null) isAbnormal = 0;
- //var statsusDesc = (phrFreqstatus == null) ? "常规" : "高频";
- var statsusDesc = "高频";
- _logger.LogInformation($"{heartRate.Serialno} 在 {statsusDesc} 状态,生成胎心值:{fetalHeartRate},统计周期:{statStartTime.ToString("yyyy-MM-dd HH:mm:ss")}----{statEndTime.ToString("yyyy-MM-dd HH:mm:ss")}");
- // 保存到 数据服务 MySQL 数据库
- HisGpsFetalHeartRate gpsFetalHeartRate = new()
- {
- FetalHeartRateId = Guid.NewGuid().ToString("D"),
- PersonId = commonPHR!.PersonId,
- Serialno = heartRate.Serialno,
- HeartRate = fetalHeartRate,
- SampleTime = sampleTime.Length > 10 ? sampleTime.Substring(0, 10) : sampleTime,
- IsAbnormal = isAbnormal,
- StatStartTime = statStartTime,
- StatEndTime = statEndTime,//commonPHR.StatEndTime,
- CreateTime = DateTime.Now,
- Method = 1,
- IsDisplay = 1,
- DeviceKey = commonPHR!.DeviceKey
- };
- await _hisFetalHeartApiClient.AddAsync(gpsFetalHeartRate).ConfigureAwait(false);
-
- #region 高频心率计算胎心数据到iot设备
-
- //if (phrFreqstatus != null && isAbnormal != 0)
- //{
- // await _serviceIotApi.SetFetalHeartRateConfig(heartRate.Serialno, fetalHeartRate, sampleTime, isAbnormal);
- //}
- // 高频有数据都推送到iot
-
- await _serviceIotApi.SetFetalHeartRateConfig(heartRate.Serialno, fetalHeartRate, sampleTime, isAbnormal);
- #endregion
- var device = await _deviceCacheMgr.GetDeviceBySerialNoAsync(heartRate.Serialno).ConfigureAwait(false);
- var fhrMsgId = $"{heartRate.Serialno}-{sampleTime}-{Guid.NewGuid().ToString("D")[^3..]}";
- var fhrMsgTime = DateTimeUtil.GetDateTimeFromUnixTimeMilliseconds(long.Parse(sampleTime.Length < 13 ? sampleTime.PadRight(13, '0') : sampleTime)).ToString("yyyy-MM-dd HH:mm:ss");
- // 胎心数据推送到第三方
- var topic = "topic.push.third";
- var fhrThridMsg = new
- {
- messageId = fhrMsgId,
- topic = topic,
- time = fhrMsgTime,
- data = new
- {
- imei = heartRate.Serialno,
- value = fetalHeartRate,
- isAbnormal,
- type = "fetalHeart"
- }
- };
- await _serviceMqProcess.ProcessIMEIEventMessageAsync(fhrMsgId, topic, 31, fhrThridMsg).ConfigureAwait(false);
-
- // 胎心数据推送到微信
- if (isAbnormal != 0)
- {
-
- topic = "topic.push.wx";
- var fhrMsg = new
- {
- messageId = fhrMsgId,
- topic = topic,
- time = fhrMsgTime,
- data = new
- {
- deviceId = device?.DeviceId,
- imei = heartRate.Serialno,
- alarmTypeId = 12,
- alarmDeviceName = heartRate.Serialno,
- alarmRemarks = JsonConvert.SerializeObject(new { fetalHeartValue = fetalHeartRate, isAbnormal = isAbnormal }),
- address = string.Empty,
- deviceKey = device?.DeviceId
- }
- };
- await _serviceMqProcess.ProcessIMEIEventMessageAsync(fhrMsgId, topic, fhrMsg).ConfigureAwait(false);
-
- }
- }
-
- private async Task SetIntervalTriggerAsync(string key, string imei, long interval, HisGpsHeartRate heartRate)
- {
- // var key = $"health_monitor/schedule_push/{type}/imei/{imei}";
- var schedulePush = await _serviceEtcd.GetValAsync(key).ConfigureAwait(false);
- if (string.IsNullOrWhiteSpace(schedulePush))
- {
- var now = DateTime.Now;
- var timeNextRun = now.Add(TimeSpan.FromSeconds(interval));
- var data = new
- {
- imei,
- create_time = now.ToString("yyyy-MM-dd HH:mm:ss"),
- ttl = interval,
- next_run_time = timeNextRun.ToString("yyyy-MM-dd HH:mm:ss"),
- trigger = heartRate,
-
- };
- var result = JsonConvert.SerializeObject(data);
- await _serviceEtcd.PutValAsync(key, result, interval, false).ConfigureAwait(false);
- }
- }
-
- /// <summary>
- /// 高频延时计算触发器
- /// </summary>
- /// <param name="key">键</param>
- /// <param name="imei">IMEI</param>
- /// <param name="interval">高频采集间隔</param>
- /// <param name="heartRate">首条高频心率</param>
- /// <returns></returns>
- private async Task SetFreqHeartRateTriggerAsync(string key, string imei, long interval, HisGpsHeartRate? heartRate=null)
- {
- var schedulePush = await _serviceEtcd.GetValAsync(key).ConfigureAwait(false);
- if (string.IsNullOrWhiteSpace(schedulePush))
- {
- var now = DateTime.Now;
- var data = new
- {
- imei,
- create_time = now.ToString("yyyy-MM-dd HH:mm:ss"),
- lease = interval,
- trigger = heartRate,
- };
- var result = JsonConvert.SerializeObject(data);
- await _serviceEtcd.PutValAsync(key, result, interval, false).ConfigureAwait(false);
- _logger.LogInformation($"{imei} 心率高频状态创建首条高频心率的触发记录,创建高频延时计算触发器");
- }
- else
- {
- // 不断修改时长,直到最后的一条高频胎心
- await _serviceEtcd.PutValAsync(key, schedulePush, interval, false).ConfigureAwait(false);
- _logger.LogInformation($"{imei} 心率高频状态续租");
- }
-
- }
-
- /// <summary>
- /// 去除高频数据
- /// </summary>
- /// <param name="phr"></param>
- /// <param name="highFreqSampleInterva"></param>
- /// <returns></returns>
- private static List<PregnancyHeartRateModel> GetNonFreqPregnancyHeartRate(List<PregnancyHeartRateModel> phr,int highFreqSampleInterval)
- {
- //phr = phr.OrderByDescending(i => i.LastUpdate).ToList();
- //var result = new List<PregnancyHeartRateModel>();
- //PregnancyHeartRateModel? previousItem = null;
-
- //foreach (var item in phr)
- //{
- // if (previousItem != null)
- // {
- // var timeNextDiff =(previousItem!.LastUpdate - item.LastUpdate).TotalSeconds;
- // if (timeNextDiff > highFreqSampleInterval)
- // {
- // result.Add(previousItem);
- // }
- // }
- // previousItem = item;
- //}
-
- //// 添加上一个
- //if (previousItem != null)
- //{
- // result.Add(previousItem);
- //}
-
- //return result;
-
- #region 反向
- var phr1 = phr.OrderByDescending(i => i.LastUpdate).ToList();
- var result = new List<PregnancyHeartRateModel>();
- PregnancyHeartRateModel? previousItem1 = null;
-
- foreach (var item in phr1)
- {
- if (previousItem1 != null)
- {
- var timeNextDiff = (previousItem1!.LastUpdate - item.LastUpdate).TotalSeconds;
- if (timeNextDiff > highFreqSampleInterval)
- {
- result.Add(previousItem1);
- }
- }
- previousItem1 = item;
- }
-
- // 添加上一个
- if (previousItem1 != null)
- {
- result.Add(previousItem1);
- }
- #endregion
-
- #region 正向
- var phr2 = phr.OrderByDescending(i => i.LastUpdate).ToList(); ;
- var freqCollection = new List<PregnancyHeartRateModel>();
- PregnancyHeartRateModel? previousItem = null;
- foreach (var item in phr2)
- {
- if (previousItem != null)
- {
- var timeNextDiff = (previousItem!.LastUpdate - item.LastUpdate).TotalSeconds;
- if (timeNextDiff <= highFreqSampleInterval)
- {
- freqCollection.Add(item);
- }
- }
- previousItem = item;
- }
- //去除高频
- foreach (var item in freqCollection)
- {
- phr2.Remove(item);
- }
- #endregion
-
- // 交集
- var commonElements = phr2.Intersect(result).ToList();
- return commonElements;
- }
-
- /// <summary>
- /// 获取高频数据
- /// </summary>
- /// <param name="phr"></param>
- /// <param name="highFreqSampleInterval"></param>
- /// <returns></returns>
- private static List<PregnancyHeartRateModel> GetFreqPregnancyHeartRate(List<PregnancyHeartRateModel> phr, int highFreqSampleInterval)
- {
- phr = phr.OrderByDescending(i => i.LastUpdate).ToList();
- var freqCollection = new List<PregnancyHeartRateModel>();
- PregnancyHeartRateModel? previousItem = null;
-
- foreach (var item in phr)
- {
- if (previousItem != null)
- {
- var timeNextDiff = (previousItem.LastUpdate - item.LastUpdate).TotalSeconds;
- if (timeNextDiff <= highFreqSampleInterval)
- {
- freqCollection.Add(previousItem);
- }
- }
- previousItem = item;
- }
-
- // 检查最后一条是否高频
- if (previousItem != null && (phr.Last().LastUpdate - previousItem.LastUpdate).TotalSeconds <= highFreqSampleInterval)
- {
- freqCollection.Add(previousItem);
- }
-
- return freqCollection;
- }
-
- }
- }
|