ソースを参照

调整胎心算法

datasub12_fetal_heart_rate_1
H Vs 2日前
コミット
74377a2ab2
3個のファイルの変更675行の追加76行の削除
  1. +52
    -65
      HealthMonitor.Service/Biz/db/TDengineService.cs
  2. +216
    -9
      HealthMonitor.Service/Resolver/PregnancyHeartRateResolver.cs
  3. +407
    -2
      HealthMonitor.WebApi/Worker.cs

+ 52
- 65
HealthMonitor.Service/Biz/db/TDengineService.cs ファイルの表示

@@ -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
}


+ 216
- 9
HealthMonitor.Service/Resolver/PregnancyHeartRateResolver.cs ファイルの表示

@@ -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}";


+ 407
- 2
HealthMonitor.WebApi/Worker.cs ファイルの表示

@@ -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)
{



読み込み中…
キャンセル
保存