@@ -28,6 +28,8 @@ using HealthMonitor.Service.Cache; | |||
using System.Text.RegularExpressions; | |||
using Etcdserverpb; | |||
using static Microsoft.EntityFrameworkCore.DbLoggerCategory; | |||
using HealthMonitor.Core.Pipeline; | |||
using static Dm.net.buffer.ByteArrayBuffer; | |||
namespace HealthMonitor.Service.Biz.db | |||
{ | |||
@@ -1007,7 +1009,14 @@ namespace HealthMonitor.Service.Biz.db | |||
//} | |||
/// <summary> | |||
/// 建模 | |||
/// </summary> | |||
/// <param name="serialNo"></param> | |||
/// <param name="days"></param> | |||
/// <param name="percentage"></param> | |||
/// <param name="highFreqSampleInterval"></param> | |||
/// <returns></returns> | |||
public async Task<PregnancyCommonHeartRateModel?> InitPregnancyCommonHeartRateModeAsync(string serialNo, int days = 7, int percentage = 90, int highFreqSampleInterval=0) | |||
{ | |||
var tableName = typeof(PregnancyHeartRateModel) | |||
@@ -1144,7 +1153,7 @@ namespace HealthMonitor.Service.Biz.db | |||
maxValue= maxValue < 100 ? 100 : maxValue; | |||
// 20-45周之间 | |||
// 12-45周之间 | |||
if (pregnancyWeek >= 12 && pregnancyWeek <= 45) | |||
{ | |||
var map = fhrMap | |||
@@ -1314,82 +1323,60 @@ namespace HealthMonitor.Service.Biz.db | |||
} | |||
/// <summary> | |||
/// 获取孕妇心率众数 | |||
/// 胎心算法 | |||
/// 1、增量系数 = (正常胎心最大值 - 正常胎心最小值) / (阀值最大值 - 阀值最小值) | |||
/// 2、胎心值 = 正常胎心最小值 + 增量系数 * (心率值 - 阀值最小值) | |||
/// </summary> | |||
/// <param name="serialNo"></param> | |||
/// <param name="days"></param> | |||
/// <param name="heartrateValue"></param> | |||
/// <returns></returns> | |||
//public async Task<int> GetPregnancyHeartRateModeAsync(string serialNo,int days=7) | |||
//{ | |||
// var tableName = typeof(PregnancyHeartRateModel) | |||
// .GetCustomAttribute<STableAttribute>()? | |||
// .STableName; | |||
// var daysAgo = DateTime.Now.AddDays(-days); | |||
// var res = await _clientSqlSugar | |||
// .Queryable<PregnancyHeartRateModel>() | |||
// .AS(tableName) | |||
// .Where(i=>i.SerialNumber.Equals(serialNo)) | |||
// .Where(i => i.Timestamp > daysAgo) | |||
// //.OrderByDescending(i => i.PregnancyHeartRate) | |||
// .Select(i =>i.PregnancyHeartRate) | |||
// .ToListAsync(); | |||
// // 心率数据量必须30个以上才进行计算 | |||
// if (res.Count < 30) return 0; | |||
// // 计算众数 | |||
// var mode = res.GroupBy(n => n) | |||
// .OrderByDescending(g => g.Count()) | |||
// .First() | |||
// .Key; | |||
public async Task<int> GetFetalHeartRateAsync(string serialNo,int heartrateValue) | |||
{ | |||
var fhrMap = _mgrFhrPhrMapCache.GetHeartRatesMap(); | |||
var watchConfig = await _deviceCacheMgr.GetGpsDeviceWatchConfigCacheObjectBySerialNoAsync(serialNo, "0067"); | |||
if (watchConfig == null) | |||
{ | |||
return 0; | |||
} | |||
// Console.WriteLine("众数是: " + mode); | |||
var edoc = DateTimeUtil.ToDateTime(watchConfig["EDOC"]!.ToString()); | |||
int pregnancyWeek = (DateTime.Now - edoc.AddDays(-280)).Days / 7; | |||
// // 如果有多个众数的情况 | |||
// var maxCount = res.GroupBy(n => n) | |||
// .Max(g => g.Count()); | |||
// 12-45周之间 | |||
if (pregnancyWeek >= 12 && pregnancyWeek <= 45) | |||
{ | |||
var map = fhrMap | |||
.Where(i => | |||
i.PregnancyPeriod![0] <= pregnancyWeek && | |||
i.PregnancyPeriod[1] >= pregnancyWeek | |||
//&&i.PregnancyHeartRateRange![0] <= mode && i.PregnancyHeartRateRange[1] >= mode | |||
) | |||
.FirstOrDefault(); | |||
// var modes = res.GroupBy(n => n) | |||
// .Where(g => g.Count() == maxCount) | |||
// .Select(g => g.Key) | |||
// .ToList(); | |||
// // 多个众数,选择最接近平均数或中位数的众数 | |||
// if (modes.Count>1) | |||
// { | |||
// // 计算平均值 | |||
// double average = res.Average(); | |||
// Console.WriteLine("平均值是: " + average); | |||
var NormaletalHeartRateMin = map?.FetalHeartRateRange![0]; | |||
var NormaletalHeartRateMax = map?.FetalHeartRateRange![1]; | |||
// // 计算中位数 | |||
// double median; | |||
// int count = res.Count; | |||
// var sortedRes = res.OrderBy(n => n).ToList(); | |||
// if (count % 2 == 0) | |||
// { | |||
// // 偶数个元素,取中间两个数的平均值 | |||
// median = (sortedRes[count / 2 - 1] + sortedRes[count / 2]) / 2.0; | |||
// } | |||
// else | |||
// { | |||
// // 奇数个元素,取中间的数 | |||
// median = sortedRes[count / 2]; | |||
// } | |||
// Console.WriteLine("中位数是: " + median); | |||
// // 找出最接近平均值的众数 | |||
// //var closestToAverage = modes.OrderBy(m => Math.Abs(m - average)).First(); | |||
// //Console.WriteLine("最接近平均值的众数是: " + closestToAverage); | |||
// 触发高频监测的心率上限值 | |||
var triggerHighFreqHigh = (int)watchConfig["triggerHighFreqHigh"]!; | |||
// 触发高频监测的心率下限值 | |||
var triggerHighFreqLow = (int)watchConfig["triggerHighFreqLow"]!; | |||
//增量系数 = (正常胎心最大值 - 正常胎心最小值) / (阀值最大值 - 阀值最小值) | |||
var coefficient = (NormaletalHeartRateMax - NormaletalHeartRateMin) / (triggerHighFreqHigh - triggerHighFreqLow); | |||
// // 找出最接近中位数的众数 | |||
// var closestToMedian = modes.OrderBy(m => Math.Abs(m - median)).First(); | |||
// Console.WriteLine("最接近中位数的众数是: " + closestToMedian); | |||
// mode = closestToMedian; | |||
// } | |||
//胎心值 = 正常胎心最小值 + 增量系数 * (心率值 - 阀值最小值) | |||
var fetalHeartRate = NormaletalHeartRateMin + coefficient * (heartrateValue - triggerHighFreqLow); | |||
// return mode; | |||
//} | |||
_logger.LogInformation($"{serialNo} 孕周:{pregnancyWeek}, 正常胎心最大值:{NormaletalHeartRateMax},正常胎心最小值:{NormaletalHeartRateMin},阀值最大值:{triggerHighFreqHigh} ,阀值最小值:{triggerHighFreqLow}, 增量系数:{coefficient}"); | |||
return SafeType.SafeInt(fetalHeartRate!); | |||
} | |||
return 0; | |||
} | |||
#endregion | |||
} | |||
@@ -829,7 +829,6 @@ namespace HealthMonitor.Service.Resolver | |||
return selectedHrValue; | |||
} | |||
/// <summary> | |||
/// 高频胎心处理 | |||
/// 1. 高频数据触发连续12个值都是正常的的高频心率处理 | |||
@@ -848,37 +847,98 @@ namespace HealthMonitor.Service.Resolver | |||
/// <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) | |||
private async Task SaveAndPushFetalHeartRateEndFreqHeartRateAsyncOld(HisGpsHeartRate heartRate, PregnancyCommonHeartRateModel commonPHR, int highFreqSampleTimes, int upperAlarmThreshold, int lowerAlarmThreshold, string sampleTime, DateTime statStartTime, DateTime statEndTime) | |||
{ | |||
// 计算胎心=孕妇心率*系数 | |||
#region 胎心系数使用基于心率与中位数对比 | |||
#region 胎心系数使用基于心率与众数对比 | |||
var coefficient = 0f; | |||
// 孕妇心率少于中位数,取StatMinValueFprCoefficient | |||
/** | |||
// 孕妇心率少于众数,取StatMinValueFprCoefficient | |||
if (heartRate.HeartRate < commonPHR!.Mode) | |||
{ | |||
coefficient = commonPHR.StatMinValueFprCoefficient!; | |||
_logger.LogInformation($"{heartRate.Serialno} 孕妇心率少于中位数,使用最小值系数 {coefficient}"); | |||
_logger.LogInformation($"{heartRate.Serialno} 孕妇心率少于众数,使用最小值系数 {coefficient}"); | |||
} | |||
// 孕妇心率大于中位数,取StatMaxValueFprCoefficient与StatModeAvgFprCoefficient中少的那个 | |||
// 孕妇心率大于众数,取StatMaxValueFprCoefficient与StatModeAvgFprCoefficient中少的那个 | |||
else if (heartRate.HeartRate > commonPHR.Mode) | |||
{ | |||
if (commonPHR.StatModeAvgFprCoefficient > commonPHR.StatMaxValueFprCoefficient) | |||
{ | |||
coefficient = commonPHR.StatMaxValueFprCoefficient!; | |||
_logger.LogInformation($"{heartRate.Serialno} 孕妇心率大于中位数,使用最大值系数 {coefficient}"); | |||
_logger.LogInformation($"{heartRate.Serialno} 孕妇心率大于众数,使用最大值系数 {coefficient}"); | |||
} | |||
else | |||
{ | |||
coefficient = commonPHR.StatModeAvgFprCoefficient!; | |||
_logger.LogInformation($"{heartRate.Serialno} 孕妇心率大于中位数,使用均值系数 {coefficient}"); | |||
_logger.LogInformation($"{heartRate.Serialno} 孕妇心率大于众数,使用均值系数 {coefficient}"); | |||
} | |||
} | |||
else | |||
{ | |||
coefficient = commonPHR.StatModeAvgFprCoefficient; | |||
_logger.LogInformation($"{heartRate.Serialno} 孕妇心率等于中位数,使用均值系数 {coefficient}"); | |||
_logger.LogInformation($"{heartRate.Serialno} 孕妇心率等于众数,使用均值系数 {coefficient}"); | |||
} | |||
*/ | |||
///1、高频最小值>心率,取高频最小值系数 | |||
///2、高频最小值 < 心率 < 众数,取众数系数与高频最小值系数较小值 | |||
///3、众数 < 心率 < 高频最大值,取众数系数与高频最大值系数较小值 | |||
///4、心率 > 高频最大值,取高频最大值系数 | |||
// 1、高频最小值>心率,取高频最小值系数 | |||
if (commonPHR.MinValue > heartRate.HeartRate) | |||
{ | |||
coefficient = commonPHR.StatMinValueFprCoefficient!; | |||
_logger.LogInformation($"{heartRate.Serialno} 高频最小值>心率,取高频最小值系数 {coefficient}"); | |||
} | |||
// 2、高频最小值 < 心率 < 众数,取众数系数与高频最小值系数较小值 | |||
if (commonPHR.MinValue < heartRate.HeartRate && heartRate.HeartRate < commonPHR.Mode) | |||
{ | |||
if (commonPHR.StatModeAvgFprCoefficient > commonPHR.StatMinValueFprCoefficient) | |||
{ | |||
coefficient = commonPHR.StatMinValueFprCoefficient!; | |||
_logger.LogInformation($"{heartRate.Serialno} 高频最小值 < 心率 < 众数,平均值系数大于最小值系数,使用最小值系数 {coefficient}"); | |||
} | |||
else | |||
{ | |||
coefficient = commonPHR.StatModeAvgFprCoefficient!; | |||
_logger.LogInformation($"{heartRate.Serialno} 高频最小值 < 心率 < 众数,平均值系数小于最小值系数,使用均值系数 {coefficient}"); | |||
} | |||
} | |||
// 3、众数 < 心率 < 高频最大值,取众数系数与高频最大值系数较小值 | |||
if (commonPHR.Mode < heartRate.HeartRate && heartRate.HeartRate < commonPHR.MaxValue) | |||
{ | |||
if (commonPHR.StatModeAvgFprCoefficient > commonPHR.StatMaxValueFprCoefficient) | |||
{ | |||
coefficient = commonPHR.StatMaxValueFprCoefficient!; | |||
_logger.LogInformation($"{heartRate.Serialno} 众数 < 心率 < 高频最大值,平均值系数大于最最大值系数,使用最大值系数 {coefficient}"); | |||
} | |||
else | |||
{ | |||
coefficient = commonPHR.StatModeAvgFprCoefficient!; | |||
_logger.LogInformation($"{heartRate.Serialno} 众数 < 心率 < 高频最大值,平均值系数小于最小值系数,使用均值系数 {coefficient}"); | |||
} | |||
} | |||
// 4、心率 > 高频最大值,取高频最大值系数 | |||
if (heartRate.HeartRate > commonPHR.MaxValue) | |||
{ | |||
coefficient = commonPHR.StatMaxValueFprCoefficient!; | |||
_logger.LogInformation($"{heartRate.Serialno} 心率 > 高频最大值,取高频最大值系数 {coefficient}"); | |||
} | |||
if (heartRate.HeartRate == commonPHR.Mode) | |||
{ | |||
coefficient = commonPHR.StatModeAvgFprCoefficient!; | |||
_logger.LogInformation($"{heartRate.Serialno} 心率 == 众数,取均值系数 {coefficient}"); | |||
} | |||
#endregion | |||
var fetalHeartRate = SafeType.SafeInt(heartRate.HeartRate! * coefficient); | |||
@@ -1012,6 +1072,153 @@ namespace HealthMonitor.Service.Resolver | |||
} | |||
} | |||
/// <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) | |||
{ | |||
var fetalHeartRate = await _serviceTDengine.GetFetalHeartRateAsync(heartRate.Serialno, (int)heartRate.HeartRate!); | |||
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},周期:{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}, 周期:{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 | |||
_logger.LogInformation($"{heartRate.Serialno} 在 高频 状态,生成胎心值:{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}"; | |||
@@ -1824,7 +1824,7 @@ namespace HealthMonitor.WebApi | |||
/// <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) | |||
private async Task SaveAndPushFetalHeartRateEndFreqHeartRateAsyncOld(HisGpsHeartRate heartRate, PregnancyCommonHeartRateModel commonPHR, int highFreqSampleTimes, int upperAlarmThreshold, int lowerAlarmThreshold, string sampleTime, DateTime statStartTime, DateTime statEndTime) | |||
{ | |||
// 计算胎心=孕妇心率*系数 | |||
@@ -2049,6 +2049,153 @@ namespace HealthMonitor.WebApi | |||
} | |||
} | |||
/// <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) | |||
{ | |||
var fetalHeartRate = await _serviceTDengine.GetFetalHeartRateAsync(heartRate.Serialno, (int)heartRate.HeartRate!); | |||
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},周期:{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}, 周期:{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 | |||
_logger.LogInformation($"{heartRate.Serialno} 在 高频 状态,生成胎心值:{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); | |||
} | |||
} | |||
/// <summary> | |||
/// 去除高频数据 | |||
@@ -2178,7 +2325,7 @@ namespace HealthMonitor.WebApi | |||
/// <param name="commonPHR"></param> | |||
/// <param name="highFreqSampleInterval"></param> | |||
/// <returns></returns> | |||
public async Task CalculateNormalFetalHeartRateIntervalAsync(HisGpsHeartRate heartRate, PregnancyCommonHeartRateModel commonPHR,int highFreqSampleInterval) | |||
public async Task CalculateNormalFetalHeartRateIntervalAsyncOld(HisGpsHeartRate heartRate, PregnancyCommonHeartRateModel commonPHR,int highFreqSampleInterval) | |||
{ | |||
var daysPhr = await _serviceTDengine.GetBySerialNoAsync<PregnancyHeartRateModel>(heartRate.Serialno, 7); | |||
var now = DateTime.Now; | |||
@@ -2535,6 +2682,264 @@ namespace HealthMonitor.WebApi | |||
} | |||
/// <summary> | |||
/// 周期性常规心率计算胎心值 | |||
/// </summary> | |||
/// <param name="heartRate"></param> | |||
/// <param name="commonPHR"></param> | |||
/// <param name="highFreqSampleInterval"></param> | |||
/// <returns></returns> | |||
public async Task CalculateNormalFetalHeartRateIntervalAsync(HisGpsHeartRate heartRate, PregnancyCommonHeartRateModel commonPHR, int highFreqSampleInterval) | |||
{ | |||
var daysPhr = await _serviceTDengine.GetBySerialNoAsync<PregnancyHeartRateModel>(heartRate.Serialno, 7); | |||
var now = DateTime.Now; | |||
var filteredPhr = daysPhr.Where(i => i.LastUpdate >= heartRate.LastUpdate && i.LastUpdate <= now).ToList(); | |||
// 去除高频 | |||
var normalPhr = GetNonFreqPregnancyHeartRate(filteredPhr, highFreqSampleInterval).OrderBy(i => i.LastUpdate); | |||
if (normalPhr.ToList().Count == 0) | |||
{ | |||
_logger.LogWarning($"{heartRate.Serialno} 时间段{heartRate.LastUpdate}-{now},去除高频心率数据后,没有常规数据,不计算常规胎心数据"); | |||
return; | |||
} | |||
var startPhr = normalPhr.First(); | |||
var endPhr = normalPhr.Last(); | |||
//var sampleTime = GetSampleTimeFromLastUpdate((DateTime)heartRate.LastUpdate!, INTERVAL_FHR); | |||
// 数据统计边界 | |||
var boundaryStatStartTime = GetSampleTimeFromLastUpdate((DateTime)startPhr.LastUpdate!, INTERVAL_FHR); | |||
var boundaryStatEndTime = GetSampleTimeFromLastUpdate((DateTime)endPhr.LastUpdate!, INTERVAL_FHR).AddMinutes(INTERVAL_FHR); | |||
_logger.LogInformation($"{heartRate.Serialno} 常规胎心统计边界{boundaryStatStartTime}-{boundaryStatEndTime}"); | |||
try | |||
{ | |||
//var CalNow = DateTime.Now; | |||
//var during = TimeSpan.FromSeconds(300); //5分钟 | |||
var c = 0; | |||
while (true) | |||
{ | |||
//if (DateTime.Now - CalNow > during) | |||
//{ | |||
// _logger.LogInformation($"{heartRate.Serialno} 超过1分钟,迭代完成跳出循环 "); | |||
// break; | |||
//} | |||
await Task.Delay(TimeSpan.FromSeconds(1)); | |||
var segmentStatStartTime = boundaryStatStartTime.AddMinutes(c * INTERVAL_FHR); | |||
var segmentStatEndTime = segmentStatStartTime.AddMinutes(INTERVAL_FHR); | |||
c++; | |||
var statStartTime = segmentStatStartTime; | |||
var statEndTime = segmentStatEndTime; | |||
_logger.LogInformation($"{heartRate.Serialno} 当前统计周期{statStartTime.ToString("yyyy-MM-dd HH:mm:ss")}-{statEndTime.ToString("yyyy-MM-dd HH:mm:ss")}"); | |||
if (statEndTime > boundaryStatEndTime) | |||
{ | |||
_logger.LogInformation($"{heartRate.Serialno} 常规胎心统计超过时间边界,迭代完成跳出循环 "); | |||
break; | |||
} | |||
var segmentPhr = normalPhr | |||
.Where(i => i.LastUpdate <= statEndTime && i.LastUpdate >= statStartTime) | |||
.ToList(); | |||
if (segmentPhr.Count == 0) | |||
{ | |||
// 跳出当次迭代,进入下次迭代 | |||
_logger.LogWarning($"{heartRate.Serialno} 统计周期:{statStartTime.ToString("yyyy-MM-dd HH:mm:ss")}-{statEndTime.ToString("yyyy-MM-dd HH:mm:ss")} 孕妇心率数据不足,{segmentPhr.Count}条记录,不处理"); | |||
continue; | |||
} | |||
_logger.LogInformation($"{heartRate.Serialno} 当前统计周期{statStartTime.ToString("yyyy-MM-dd HH:mm:ss")}-{statEndTime.ToString("yyyy-MM-dd HH:mm:ss")},对应的常规心率ID{string.Join(",", segmentPhr.Select(i => i.MessageId))}"); | |||
var sampleTime = DateTimeUtil.ConvertToTimeStamp(segmentStatStartTime).ToString(); | |||
sampleTime = sampleTime.Length > 10 ? sampleTime.Substring(0, 10) : sampleTime; | |||
//检测 是否存在,不存在则处理 | |||
GeneralParam param = new() | |||
{ | |||
Filters = new List<QueryFilterCondition> | |||
{ | |||
new () | |||
{ | |||
Key=nameof(HisGpsFetalHeartRate.Serialno), | |||
Value=heartRate.Serialno, | |||
ValueType=QueryValueTypeEnum.String, | |||
Operator=QueryOperatorEnum.Equal | |||
}, | |||
new () | |||
{ | |||
Key=nameof(HisGpsFetalHeartRate.SampleTime), | |||
Value=sampleTime, | |||
ValueType=QueryValueTypeEnum.String, | |||
Operator=QueryOperatorEnum.Equal | |||
}, | |||
}, | |||
OrderBys = new List<OrderByCondition> | |||
{ | |||
new (){ | |||
IsDesc=true, | |||
Key=nameof(HisGpsFetalHeartRate.SampleTime) | |||
} | |||
} | |||
}; | |||
var fhr = await _hisFetalHeartApiClient.GetFirstAsync(param, heartRate.Serialno[^2..], null, new RequestHeader { RequestId = Guid.NewGuid().ToString("D") }); | |||
if (fhr == null) | |||
{ | |||
var avgPhr = segmentPhr.Count == 1 | |||
? segmentPhr.First().PregnancyHeartRate | |||
: segmentPhr.Average(i => i.PregnancyHeartRate); | |||
heartRate.HeartRate = (int)avgPhr; | |||
#region 胎心阈值判断 | |||
var fetalHeartRateFromAlgorithm = await _serviceTDengine.GetFetalHeartRateAsync(heartRate.Serialno, (int)heartRate.HeartRate); | |||
// 计算胎心率并进行边界检查 | |||
var fetalHeartRate = Math.Clamp(fetalHeartRateFromAlgorithm, 90, 220); | |||
if (fetalHeartRate != fetalHeartRateFromAlgorithm) | |||
{ | |||
_logger.LogWarning($"{heartRate.Serialno} 胎心率超出范围, 按修正值 {fetalHeartRate} 输出, 孕妇心率 {heartRate.HeartRate}, 周期 {statStartTime} - {statEndTime}"); | |||
} | |||
#endregion | |||
_logger.LogInformation($"{heartRate.Serialno} 在 常规 状态,生成胎心值:{fetalHeartRate},统计周期:{statStartTime.ToString("yyyy-MM-dd HH:mm:ss")}----{statEndTime.ToString("yyyy-MM-dd HH:mm:ss")}"); | |||
var isAbnormal = 0; | |||
#region 保存到Mysql数据库 | |||
// 保存到 数据服务 MySQL 数据库 | |||
HisGpsFetalHeartRate gpsFetalHeartRate = new() | |||
{ | |||
FetalHeartRateId = Guid.NewGuid().ToString("D"), | |||
PersonId = commonPHR!.PersonId, | |||
Serialno = heartRate.Serialno, | |||
HeartRate = fetalHeartRate, | |||
SampleTime = 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); | |||
#endregion | |||
#region 推送最后一条常规心率计算的胎心数据到iot设备 | |||
var lastPhr = await _serviceTDengine.GetLastAsync<PregnancyHeartRateModel>(heartRate.Serialno); | |||
if (segmentStatEndTime == boundaryStatEndTime) | |||
{ | |||
await _serviceIotApi.SetFetalHeartRateConfig(heartRate.Serialno, fetalHeartRate, sampleTime, isAbnormal); | |||
_logger.LogInformation($"{heartRate.Serialno} 推送最后一条常规心率计算的胎心数据到iot设备"); | |||
} | |||
#endregion | |||
#region 推送到第三方 | |||
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); | |||
#endregion | |||
#region 推送到微信 | |||
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); | |||
} | |||
#endregion | |||
} | |||
else | |||
{ | |||
_logger.LogInformation($"{heartRate.Serialno},统计常规胎心周期:{statStartTime.ToString("yyyy-MM-dd HH:mm:ss")}----{statEndTime.ToString("yyyy-MM-dd HH:mm:ss")} ,常规胎心已处理"); | |||
} | |||
//await Task.Delay(TimeSpan.FromSeconds(1)); | |||
// 跳出循环 | |||
if (statEndTime.ToString("yyyyMMddHHmm") == boundaryStatEndTime.ToString("yyyyMMddHHmm")) | |||
{ | |||
_logger.LogInformation($"{heartRate.Serialno} 迭代完成跳出循环 "); | |||
break; | |||
} | |||
//if (statEndTime>= boundaryStatEndTime) | |||
//{ | |||
// _logger.LogInformation($"{heartRate.Serialno} 时间边界,迭代完成跳出循环 "); | |||
// break; | |||
//} | |||
} | |||
} | |||
catch (Exception ex) | |||
{ | |||
_logger.LogError($"处理常规胎心数据时发生错误: {ex.Message}"); | |||
} | |||
//for (var segmentStatStartTime = boundaryStatStartTime; | |||
// segmentStatStartTime < boundaryStatEndTime; | |||
// segmentStatStartTime = segmentStatStartTime.AddMinutes(INTERVAL_FHR)) | |||
//{ | |||
// var segmentStatEndTime = segmentStatStartTime.AddMinutes(INTERVAL_FHR); | |||
// var segmentPhr = daysPhr | |||
// .Where(i => i.LastUpdate <= segmentStatEndTime && i.LastUpdate >= segmentStatStartTime) | |||
// .ToList(); | |||
// if (segmentStatEndTime == boundaryStatEndTime) | |||
// { | |||
// break; | |||
// } | |||
//} | |||
} | |||
public async Task CalculateFetalMovementIntervalAsync(HisGpsHeartRate heartRate, PregnancyCommonHeartRateModel commonPHR,DateTime edoc) | |||
{ | |||