diff --git a/HealthMonitor.Common/DateTimeUtil.cs b/HealthMonitor.Common/DateTimeUtil.cs index 423d189..8ad4590 100644 --- a/HealthMonitor.Common/DateTimeUtil.cs +++ b/HealthMonitor.Common/DateTimeUtil.cs @@ -713,7 +713,15 @@ namespace HealthMonitor.Common return sRet; } - + public static double GetTimeDifferenceInSeconds( DateTime end,DateTime start) + { + // 获取时间差 + TimeSpan timeDifference = end - start; + + // 返回时间差的总秒数 + return timeDifference.TotalSeconds; + } + ///// ///// 是否为日期型字符串 diff --git a/HealthMonitor.Service/Resolver/PregnancyHeartRateResolver.cs b/HealthMonitor.Service/Resolver/PregnancyHeartRateResolver.cs index 74c89dc..bcea08d 100644 --- a/HealthMonitor.Service/Resolver/PregnancyHeartRateResolver.cs +++ b/HealthMonitor.Service/Resolver/PregnancyHeartRateResolver.cs @@ -165,7 +165,8 @@ namespace HealthMonitor.Service.Resolver //停止高频心率采样心率连续正常次数 var stopHighFreqSampleCount = (int)watchConfig["stopHighFreqSampleCount"]!; // 高频心率采集时长 0 为持续采集,非零为高频心率的采集时长 - var highFreqSampleTimes = (int)watchConfig["highFreqSampleTimes"]!; + //var highFreqSampleTimes = (int)watchConfig["highFreqSampleTimes"]!; + var highFreqSampleTimes = 540; // 告警上限阀值 var upperAlarmThreshold = (int)watchConfig["upperAlarmThreshold"]!; // 告警下限阀值 @@ -351,8 +352,19 @@ namespace HealthMonitor.Service.Resolver if (string.IsNullOrEmpty(freqSaveAndPushFetalHeartRate)) { //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); + //await SaveAndPushFetalHeartRateEndFreqHeartRateAsync(heartRate, commonPHR, upperAlarmThreshold, lowerAlarmThreshold, DateTimeUtil.ConvertToTimeStamp(phrFreqstatus!.LastUpdate).ToString(), phrFreqstatus!.LastUpdate, FreqStatsEnd); + + var ts = DateTimeUtil.GetTimeDifferenceInSeconds((DateTime)heartRate.LastUpdate!, phrFreqstatus!.LastUpdate); + // 判断是否够highFreqSampleTimes,540s + if (ts < highFreqSampleTimes) + { + /// 不够10分钟最近12个数据的最小值生成胎心值 + heartRate.HeartRate = lastPhr.Select(i=>i.PregnancyHeartRate).Min(); + } + await SaveAndPushFetalHeartRateEndFreqHeartRateAsync(heartRate, commonPHR,highFreqSampleTimes ,upperAlarmThreshold, lowerAlarmThreshold, DateTimeUtil.ConvertToTimeStamp(phrFreqstatus!.LastUpdate).ToString(), phrFreqstatus!.LastUpdate, FreqStatsEnd); + // 删除高频状态的首条记录 await _deviceCacheMgr.DelPregnancyHeartRateFreqStatusAsync(heartRate.Serialno); // 设置15分的SaveAndPushFetalHeartRate业务间隔 @@ -382,11 +394,14 @@ namespace HealthMonitor.Service.Resolver /// 取所有值的平均值,推送胎心数据到api/v1/open/OpenIot/SetFetalHeartRateConfig if (highFreqSampleTimes == 0) { - var avgPhr = phr.OrderByDescending(i => i.LastUpdate) + var lastPhr = 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(); + .Where(i => i.PregnancyHeartRate < triggerHighFreqLow || i.PregnancyHeartRate > triggerHighFreqHigh); + //.Select(i => i.PregnancyHeartRate); + //.Average(); + + var avgPhr = lastPhr.Select(i => i.PregnancyHeartRate).Average(); // 推送胎心数据到 api/v1/open/OpenIot/SetFetalHeartRateConfig // 计算一般心率得到胎心系数 // 高频数据不建模 @@ -397,7 +412,16 @@ namespace HealthMonitor.Service.Resolver //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); + //await SaveAndPushFetalHeartRateEndFreqHeartRateAsync(heartRate, commonPHR, upperAlarmThreshold, lowerAlarmThreshold, DateTimeUtil.ConvertToTimeStamp(phrFreqstatus!.LastUpdate).ToString(), phrFreqstatus!.LastUpdate, FreqStatsEnd); + var ts = DateTimeUtil.GetTimeDifferenceInSeconds((DateTime)heartRate.LastUpdate!, phrFreqstatus!.LastUpdate); + // 判断是否够highFreqSampleTimes,540s + if (ts < highFreqSampleTimes) + { + /// 不够10分钟最近12个数据的最小值生成胎心值 + heartRate.HeartRate = lastPhr.Select(i => i.PregnancyHeartRate).Min(); + } + await SaveAndPushFetalHeartRateEndFreqHeartRateAsync(heartRate, commonPHR, highFreqSampleTimes, upperAlarmThreshold, lowerAlarmThreshold, DateTimeUtil.ConvertToTimeStamp(phrFreqstatus!.LastUpdate).ToString(), phrFreqstatus!.LastUpdate, FreqStatsEnd); + } /// 在highFreqSampleTimes>0一直异常(大于等于triggerHighFreqLow,少于等于triggerHighFreqHig), @@ -428,7 +452,20 @@ namespace HealthMonitor.Service.Resolver //计算高频 //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); + //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); + var ts = DateTimeUtil.GetTimeDifferenceInSeconds((DateTime)heartRate.LastUpdate!, phrFreqstatus!.LastUpdate); + + if (ts < highFreqSampleTimes) + { + /// 不够10分钟最近12个数据的最小值生成胎心值 + heartRate.HeartRate = lastPhr.Select(i => i.PregnancyHeartRate).Min(); + } + await SaveAndPushFetalHeartRateEndFreqHeartRateAsync(heartRate, commonPHR, highFreqSampleTimes, upperAlarmThreshold, lowerAlarmThreshold, DateTimeUtil.ConvertToTimeStamp(phrFreqstatus!.LastUpdate).ToString(), phrFreqstatus!.LastUpdate, FreqStatsEnd); } else { @@ -483,11 +520,23 @@ namespace HealthMonitor.Service.Resolver .Take(stopHighFreqSampleCount) // 计算最后12条 .Select(i => i.PregnancyHeartRate).Average(); - //await SaveAndPushFetalHeartRateAsync(heartRate, commonPHR, upperAlarmThreshold, lowerAlarmThreshold, avgPhr, DateTimeUtil.ConvertToTimeStamp(phrFreqstatus!.LastUpdate).ToString(), freqCollection.Last().LastUpdate, freqCollection.First().LastUpdate); 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}"); + // 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); + var ts = DateTimeUtil.GetTimeDifferenceInSeconds((DateTime)heartRate.LastUpdate!, phrFreqstatus!.LastUpdate); + if (ts < highFreqSampleTimes) + { + /// 不够10分钟最近12个数据的最小值生成胎心值 + heartRate.HeartRate = lastPhr.Select(i => i.PregnancyHeartRate).Min(); + } + await SaveAndPushFetalHeartRateEndFreqHeartRateAsync(heartRate, commonPHR, highFreqSampleTimes, upperAlarmThreshold, lowerAlarmThreshold, DateTimeUtil.ConvertToTimeStamp(phrFreqstatus!.LastUpdate).ToString(), phrFreqstatus!.LastUpdate, FreqStatsEnd); + + _logger.LogInformation($"{heartRate.Serialno} 高频数据个数{freqCollection.Count},触发高频开始时间{phrFreqstatus!.LastUpdate},时间倒叙"); } else { @@ -599,6 +648,7 @@ namespace HealthMonitor.Service.Resolver /// /// /// + /** private async Task CalculateNormalFetalHeartRateAsync(HisGpsHeartRate heartRate, int upperAlarmThreshold, int lowerAlarmThreshold, int intervalFHR, PregnancyCommonHeartRateModel? commonPHR) { // 上15分钟的数据 @@ -653,9 +703,13 @@ namespace HealthMonitor.Service.Resolver //await SaveAndPushFreqFetalHeartRateAsync(heartRate, commonPHR!, upperAlarmThreshold, lowerAlarmThreshold, phrValue, sampleTimeFHR); //await SaveAndPushFetalHeartRateAsync(heartRate, commonPHR!, upperAlarmThreshold, lowerAlarmThreshold, phrValue, sampleTimeFHR, normalPhrStatStartTime, normalPhrStatEndTime); heartRate.HeartRate = (int)phrValue; - await SaveAndPushFetalHeartRateEndFreqHeartRateAsync(heartRate, commonPHR!, upperAlarmThreshold, lowerAlarmThreshold, sampleTimeFHR, normalPhrStatStartTime, normalPhrStatEndTime); + //await SaveAndPushFetalHeartRateEndFreqHeartRateAsync(heartRate, commonPHR!, upperAlarmThreshold, lowerAlarmThreshold, sampleTimeFHR, normalPhrStatStartTime, normalPhrStatEndTime); + + } - + */ + + /// /// 延时计算 /// @@ -1103,6 +1157,220 @@ namespace HealthMonitor.Service.Resolver } } + 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); + + #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")}"); + } + + } + #endregion + + + var isAbnormal = fetalHeartRate > upperAlarmThreshold ? 1 : (fetalHeartRate < lowerAlarmThreshold ? 2 : 0); + if (phrFreqstatus == null) isAbnormal = 0; + var statsusDesc = (phrFreqstatus == null) ? "常规" : "高频"; + _logger.LogInformation($"{heartRate.Serialno} 在 {statsusDesc} 状态,生成胎心值:{fetalHeartRate},统计周期:{statStartTime.ToString("yyyy-MM-dd HH:mm:ss")}----{statEndTime.ToString("yyyy-MM-dd HH:mm:ss")}"); + //if (!isFreq) + //{ + // statStartTime = heartRate.LastUpdate; + // + //} + // 保存到 数据服务 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); + + // 推送到api/v1/open/OpenIot/SetFetalHeartRateConfig + // 推送最后一条常规心率计算的胎心数据到iot设备 + #region 推送最后一条常规心率计算的胎心数据到iot设备 + + // 高频(<=12)-常规 + var lastPhr = await _serviceTDengine.GetLastAsync(heartRate.Serialno); + if (lastPhr.MessageId == heartRate.MessageId && phrFreqstatus == null) + { + await _serviceIotApi.SetFetalHeartRateConfig(heartRate.Serialno, fetalHeartRate, sampleTime, isAbnormal); + _logger.LogInformation($"{heartRate.Serialno} 推送最后一条常规心率计算的胎心数据到iot设备,高频(<=12)-常规"); + } + // 高频(13)-常规-高频(13) + if (phrFreqstatus != null) + { + var phr = await _serviceTDengine.GetBySerialNoAsync(heartRate.Serialno, 1); + phr = phr.OrderByDescending(i => i.LastUpdate).ToList(); + // 获取高频数据 + var freqCollection = new List(); + PregnancyHeartRateModel? previousItem = null; + foreach (var item in phr) + { + if (previousItem != null) + { + var timeNextDiff = (previousItem!.LastUpdate - item.LastUpdate).TotalSeconds; + if (timeNextDiff <= 60) + { + freqCollection.Add(item); + } + } + // 高频最后一条 + if (lastPhr.MessageId == item.MessageId) + { + freqCollection.Add(item); + } + + previousItem = item; + } + //去除高频 + foreach (var item in freqCollection) + { + phr.Remove(item); + } + lastPhr = phr.FirstOrDefault(); + if (lastPhr?.MessageId == heartRate.MessageId) + { + await _serviceIotApi.SetFetalHeartRateConfig(heartRate.Serialno, fetalHeartRate, sampleTime, isAbnormal); + _logger.LogInformation($"{heartRate.Serialno} 推送最后一条常规心率计算的胎心数据到iot设备,高频(13)-常规-高频(13)"); + } + } + + #endregion + + #region 高频心率计算胎心数据到iot设备 + // 高频(17) ,连续12个高频正常,也不停止且数据偏高和偏低也推送到iot + if (phrFreqstatus != null && isAbnormal != 0) + { + 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) { diff --git a/HealthMonitor.WebApi/Worker.cs b/HealthMonitor.WebApi/Worker.cs index e077a00..21efeb1 100644 --- a/HealthMonitor.WebApi/Worker.cs +++ b/HealthMonitor.WebApi/Worker.cs @@ -312,6 +312,10 @@ namespace HealthMonitor.WebApi //停止高频心率采样心率连续正常次数 var stopHighFreqSampleCount = (int)watchConfig["stopHighFreqSampleCount"]!; + // 高频心率采集时长 0 为持续采集,非零为高频心率的采集时长 + //var highFreqSampleTimes = (int)watchConfig["highFreqSampleTimes"]!; + var highFreqSampleTimes = 540; + var commonPHR = await _serviceTDengine.GetLastAsync(imeiDel); @@ -337,7 +341,17 @@ namespace HealthMonitor.WebApi //await SaveAndPushFetalHeartRateAsync(triggerHeartRate, commonPHR, upperAlarmThreshold, lowerAlarmThreshold, avgPhr, DateTimeUtil.ConvertToTimeStamp(phrFreqstatus!.LastUpdate).ToString(), phrFreqstatus!.LastUpdate, FreqStatsEnd); triggerHeartRate.HeartRate = (int)avgPhr; - await SaveAndPushFetalHeartRateEndFreqHeartRateAsync(triggerHeartRate, commonPHR, upperAlarmThreshold, lowerAlarmThreshold, DateTimeUtil.ConvertToTimeStamp(phrFreqstatus!.LastUpdate).ToString(), phrFreqstatus!.LastUpdate, FreqStatsEnd); + //await SaveAndPushFetalHeartRateEndFreqHeartRateAsync(triggerHeartRate, commonPHR, upperAlarmThreshold, lowerAlarmThreshold, DateTimeUtil.ConvertToTimeStamp(phrFreqstatus!.LastUpdate).ToString(), phrFreqstatus!.LastUpdate, FreqStatsEnd); + + // 判断是否够highFreqSampleTimes,540s + var lastPhr = phrInFreqstatus; + var ts = DateTimeUtil.GetTimeDifferenceInSeconds((DateTime)triggerHeartRate.LastUpdate!, phrFreqstatus!.LastUpdate); + if (ts < highFreqSampleTimes) + { + /// 不够10分钟最近12个数据的最小值生成胎心值 + triggerHeartRate.HeartRate = lastPhr.Select(i => i.PregnancyHeartRate).Min(); + } + await SaveAndPushFetalHeartRateEndFreqHeartRateAsync(triggerHeartRate, commonPHR, highFreqSampleTimes, upperAlarmThreshold, lowerAlarmThreshold, DateTimeUtil.ConvertToTimeStamp(phrFreqstatus!.LastUpdate).ToString(), phrFreqstatus!.LastUpdate, FreqStatsEnd); } else { @@ -2339,6 +2353,220 @@ namespace HealthMonitor.WebApi } } + 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); + + #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")}"); + } + + } + #endregion + + + var isAbnormal = fetalHeartRate > upperAlarmThreshold ? 1 : (fetalHeartRate < lowerAlarmThreshold ? 2 : 0); + if (phrFreqstatus == null) isAbnormal = 0; + var statsusDesc = (phrFreqstatus == null) ? "常规" : "高频"; + _logger.LogInformation($"{heartRate.Serialno} 在 {statsusDesc} 状态,生成胎心值:{fetalHeartRate},统计周期:{statStartTime.ToString("yyyy-MM-dd HH:mm:ss")}----{statEndTime.ToString("yyyy-MM-dd HH:mm:ss")}"); + //if (!isFreq) + //{ + // statStartTime = heartRate.LastUpdate; + // + //} + // 保存到 数据服务 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); + + // 推送到api/v1/open/OpenIot/SetFetalHeartRateConfig + // 推送最后一条常规心率计算的胎心数据到iot设备 + #region 推送最后一条常规心率计算的胎心数据到iot设备 + + // 高频(<=12)-常规 + var lastPhr = await _serviceTDengine.GetLastAsync(heartRate.Serialno); + if (lastPhr.MessageId == heartRate.MessageId && phrFreqstatus == null) + { + await _serviceIotApi.SetFetalHeartRateConfig(heartRate.Serialno, fetalHeartRate, sampleTime, isAbnormal); + _logger.LogInformation($"{heartRate.Serialno} 推送最后一条常规心率计算的胎心数据到iot设备,高频(<=12)-常规"); + } + // 高频(13)-常规-高频(13) + if (phrFreqstatus != null) + { + var phr = await _serviceTDengine.GetBySerialNoAsync(heartRate.Serialno, 1); + phr = phr.OrderByDescending(i => i.LastUpdate).ToList(); + // 获取高频数据 + var freqCollection = new List(); + PregnancyHeartRateModel? previousItem = null; + foreach (var item in phr) + { + if (previousItem != null) + { + var timeNextDiff = (previousItem!.LastUpdate - item.LastUpdate).TotalSeconds; + if (timeNextDiff <= 60) + { + freqCollection.Add(item); + } + } + // 高频最后一条 + if (lastPhr.MessageId == item.MessageId) + { + freqCollection.Add(item); + } + + previousItem = item; + } + //去除高频 + foreach (var item in freqCollection) + { + phr.Remove(item); + } + lastPhr = phr.FirstOrDefault(); + if (lastPhr?.MessageId == heartRate.MessageId) + { + await _serviceIotApi.SetFetalHeartRateConfig(heartRate.Serialno, fetalHeartRate, sampleTime, isAbnormal); + _logger.LogInformation($"{heartRate.Serialno} 推送最后一条常规心率计算的胎心数据到iot设备,高频(13)-常规-高频(13)"); + } + } + + #endregion + + #region 高频心率计算胎心数据到iot设备 + // 高频(17) ,连续12个高频正常,也不停止且数据偏高和偏低也推送到iot + if (phrFreqstatus != null && isAbnormal != 0) + { + 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); + + } + }