Browse Source

延时计算高频状态的胎心数据,防止高频结束后与触发数据时间相隔过长。

datasub12_fetal_heart_rate
H Vs 2 months ago
parent
commit
7d31d44d47
2 changed files with 296 additions and 64 deletions
  1. +57
    -4
      HealthMonitor.Service/Resolver/PregnancyHeartRateResolver.cs
  2. +239
    -60
      HealthMonitor.WebApi/Worker.cs

+ 57
- 4
HealthMonitor.Service/Resolver/PregnancyHeartRateResolver.cs View File

@@ -173,8 +173,6 @@ namespace HealthMonitor.Service.Resolver
// 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)
@@ -236,6 +234,8 @@ namespace HealthMonitor.Service.Resolver
// 高频心率启动(在高频第二条才能判断)
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)
{
@@ -249,8 +249,23 @@ namespace HealthMonitor.Service.Resolver
await _deviceCacheMgr.SetPregnancyHeartRateFreqStatusAsync(heartRate.Serialno, freqFirstPhr);
_logger.LogInformation($"{heartRate.Serialno} 设置高频状态");
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, freqFirstHR);
}
// 续租延时计算高频心率的胎心
await SetFreqHeartRateTriggerAsync(freqHearRateHey, heartRate.Serialno, highFreqSampleInterval);

/// phr PregnancyHeartRate 连续连续正常次数个值都是正常(大于等于triggerHighFreqLow,少于等于triggerHighFreqHig),
/// 取连续正常次数正常值的平均值,推送到api/v1/open/OpenIot/SetFetalHeartRateConfig
@@ -358,7 +373,7 @@ namespace HealthMonitor.Service.Resolver
}
else
{
_logger.LogInformation($"{heartRate.Serialno} 高频心率的数据不大于{stopHighFreqSampleCount}条,不进行高频数据的胎心计算");
_logger.LogWarning($"{heartRate.Serialno} 高频心率的数据不大于{stopHighFreqSampleCount}条,不进行高频数据的胎心计算");

}

@@ -723,6 +738,14 @@ namespace HealthMonitor.Service.Resolver
}
}

#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..]}";
@@ -792,6 +815,36 @@ namespace HealthMonitor.Service.Resolver
}
}

/// <summary>
/// 高频延时计算触发器
/// </summary>
/// <param name="key"></param>
/// <param name="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);
}
else
{
await _serviceEtcd.PutValAsync(key, schedulePush, interval, false).ConfigureAwait(false);
}

}
/// <summary>
/// 去除高频数据
/// </summary>


+ 239
- 60
HealthMonitor.WebApi/Worker.cs View File

@@ -274,6 +274,87 @@ namespace HealthMonitor.WebApi
}
}
// health_monitor/schedule_push/cal_fetal_heart_rate/imei/
if (key.Contains("health_monitor/schedule_push/cal_freqhr_fetal_heart_rate/imei/"))
{
/// 延时计算高频状态的胎心数据,防止高频结束后与触发数据时间相隔过长。
var phrFreqstatus = await _deviceCacheMgr.GetPregnancyHeartRateFreqStatusAsync(imeiDel);
// 高频不停,15分钟内只下发一条
var push =await _deviceCacheMgr.GetBizIntervalAsync(imeiDel, "SaveAndPushFetalHeartRate");
var triggerValue = (JObject)JsonConvert.DeserializeObject(e.PrevKv.Value.ToStringUtf8())!;
var trigger = triggerValue["trigger"]?.ToString();
// 验证高频首条是否还存在
if (phrFreqstatus!=null && string.IsNullOrEmpty(push))
{
if (!string.IsNullOrEmpty(trigger))
{
var triggerHeartRate = JsonConvert.DeserializeObject<HisGpsHeartRate>(trigger);
using (_logger.BeginScope(new Dictionary<string, object> { ["RequestId"] = triggerHeartRate?.MessageId! }))
{
var watchConfig = await _deviceCacheMgr.GetGpsDeviceWatchConfigCacheObjectBySerialNoAsync(imeiDel, "0067");
_logger.LogInformation($"{imeiDel}高频结束后,延迟计算高频数据产生的胎心数据");
var isFetalHeartEnable = watchConfig != null && (int)watchConfig["enabled"]! == 1;
if (isFetalHeartEnable)
{
// 防止超过12条连续正常,高频不停止
if (phrFreqstatus.MessageId == triggerHeartRate?.MessageId)
{
// 告警上限阀值
var upperAlarmThreshold = (int)watchConfig!["upperAlarmThreshold"]!;
// 告警下限阀值
var lowerAlarmThreshold = (int)watchConfig["lowerAlarmThreshold"]!;

// 高频心率采样间隔 highFreqSampleInterval = highFreqSampleInterval+5,增加5秒兼容(最小highFreqSampleInterval=60)
var highFreqSampleInterval = (int)watchConfig!["highFreqSampleInterval"]! >= 60 ? (int)watchConfig!["highFreqSampleInterval"]! + 5 : 60;

//停止高频心率采样心率连续正常次数
var stopHighFreqSampleCount = (int)watchConfig["stopHighFreqSampleCount"]!;

var commonPHR = await _serviceTDengine.GetLastAsync<PregnancyCommonHeartRateModel>(imeiDel);


var phr = await _serviceTDengine.GetBySerialNoAsync<PregnancyHeartRateModel>(imeiDel, 7);
var phrFromFreqstatus = phr.Where(i => i.LastUpdate >= phrFreqstatus.LastUpdate).ToList();
// 高频数据
var phrInFreqstatus = GetFreqPregnancyHeartRate(phrFromFreqstatus, highFreqSampleInterval)
.OrderByDescending(i=>i.LastUpdate).ToList();

if (phrInFreqstatus.Count > stopHighFreqSampleCount)
{
// 取最后 stopHighFreqSampleCount 条高频数据
phrInFreqstatus = phrInFreqstatus
.OrderByDescending(i => i.LastUpdate)
.Take(stopHighFreqSampleCount).ToList(); // 计算最后12条

var avgPhr = phrInFreqstatus
.Select(i => i.PregnancyHeartRate).Average();

var FreqStatsEnd = phrInFreqstatus.First().LastUpdate;

_logger.LogInformation($"延时计算,{imeiDel} 高频状态持续{(FreqStatsEnd - phrFreqstatus!.LastUpdate).TotalSeconds} 秒,统计周期:{phrFreqstatus!.LastUpdate.ToString("yyyy-MM-dd HH:mm:ss")}----{FreqStatsEnd.ToString("yyyy-MM-dd HH:mm:ss")},将下发指令");

await SaveAndPushFetalHeartRateAsync(triggerHeartRate, commonPHR, upperAlarmThreshold, lowerAlarmThreshold, avgPhr, DateTimeUtil.ConvertToTimeStamp(phrFreqstatus!.LastUpdate).ToString(), phrFreqstatus!.LastUpdate, FreqStatsEnd);
}
else
{
_logger.LogWarning($"{imeiDel} 高频心率的数据不大于{stopHighFreqSampleCount}条,不进行高频数据的胎心计算");

}
// 删除高频状态的首条记录
await _deviceCacheMgr.DelPregnancyHeartRateFreqStatusAsync(imeiDel);
}
else
{
_logger.LogWarning($"{imeiDel} 超过12条连续正常,高频不停止,暂不处理");
}
}
else
{
_logger.LogWarning($"{imeiDel} 胎心监测功能没有启动");
}
}
}
}
}
else if (key.Contains("health_monitor/schedule_push/cal_fetal_heart_rate/imei/"))
{
/**
@@ -1191,67 +1272,13 @@ namespace HealthMonitor.WebApi
return timeRanges.Any(range => now >= range.Start && now <= range.End);
}

private async Task CalculateNormalFetalHeartRateAsync(HisGpsHeartRate heartRate, int upperAlarmThreshold, int lowerAlarmThreshold, int intervalFHR, PregnancyCommonHeartRateModel? commonPHR)
{
// 上15分钟的数据
// 获取当前时间
DateTime nowInterval = (DateTime)heartRate.LastUpdate!;

if (nowInterval.Second > 0)
{
nowInterval = nowInterval.AddMinutes(1);
}
// 计算last_update到上一间隔的分钟数
int minutesToSubtract = nowInterval.Minute % intervalFHR;

// 计算上一间隔的时间
DateTime previousInterval = nowInterval.AddMinutes(-minutesToSubtract).AddSeconds(-nowInterval.Second).AddMilliseconds(-nowInterval.Millisecond);

// 使用 last_update 上一刻
var sampleTimeFHR = DateTimeUtil.ConvertToTimeStamp(previousInterval).ToString();

// 计算last_update到下一间隔的分钟数
int minutesToAdd = intervalFHR - (nowInterval.Minute % intervalFHR);
if (minutesToAdd == intervalFHR)
{
minutesToAdd = 0; // 如果已经是间隔,则不需要增加分钟
}

// 计算下一间隔的时间
DateTime nextInterval = nowInterval.AddMinutes(minutesToAdd)
.AddSeconds(-nowInterval.Second)
.AddMilliseconds(-nowInterval.Millisecond);


var daysPhr = await _serviceTDengine.GetBySerialNoAsync<PregnancyHeartRateModel>(heartRate.Serialno, 7);

var normalPhrStatStartTime = nextInterval.AddMinutes(-intervalFHR);

var normalPhrStatEndTime = nextInterval;

_logger.LogInformation($" {heartRate.MessageId} -- {heartRate.Serialno} 计算胎心数据, 周期:{normalPhrStatStartTime}-{normalPhrStatEndTime} ");
var filteredPhr = daysPhr
// 使用 last_update 下一刻
.Where(i => i.LastUpdate <= normalPhrStatEndTime && i.LastUpdate >= normalPhrStatStartTime)
.ToList();
if (filteredPhr.Count == 0)
{
_logger.LogWarning($"{heartRate.MessageId} -- {heartRate.Serialno} 周期:{normalPhrStatStartTime}-{normalPhrStatEndTime} 孕妇心率数据不足,{filteredPhr.Count}条记录");
return;
}
var phrValue = filteredPhr.Count == 1
? filteredPhr.First().PregnancyHeartRate
: filteredPhr.Average(i => i.PregnancyHeartRate);
//await SaveAndPushFreqFetalHeartRateAsync(heartRate, commonPHR!, upperAlarmThreshold, lowerAlarmThreshold, phrValue, sampleTimeFHR);
await SaveAndPushFetalHeartRateAsync(heartRate, commonPHR!, upperAlarmThreshold, lowerAlarmThreshold, phrValue, sampleTimeFHR, normalPhrStatStartTime, normalPhrStatEndTime);
}

private async Task SaveAndPushFetalHeartRateAsync(HisGpsHeartRate heartRate, PregnancyCommonHeartRateModel commonPHR, int upperAlarmThreshold, int lowerAlarmThreshold, double phrValue, string sampleTime, DateTime statStartTime, DateTime statEndTime)
{
// 计算胎心=孕妇心率*系数
/**
var fetalHeartRate = SafeType.SafeInt(phrValue * commonPHR?.StatModeAvgFprCoefficient!);
//fetalHeartRate = fetalHeartRate > 220 ? 220 : fetalHeartRate; // 胎心的最大值调整为220,超过都按该值220输出
fetalHeartRate = fetalHeartRate > 220 ? 220 : fetalHeartRate; // 胎心的最大值调整为220,超过都按该值220输出
if (fetalHeartRate >= 220)
{
// 先使用最小系数计算
@@ -1269,12 +1296,46 @@ namespace HealthMonitor.WebApi
_logger.LogWarning($"{heartRate.Serialno} 使用所有系数都不能放映实际,建模数据可能出现异常,请检查");
}
}
*/

#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(phrValue * coefficient);
// 胎心的最大值调整为220,超过都按该值220输出
fetalHeartRate = fetalHeartRate >= 220 ? 220 : fetalHeartRate;

var isAbnormal = fetalHeartRate > upperAlarmThreshold ? 1 : (fetalHeartRate < lowerAlarmThreshold ? 2 : 0);
var phrFreqstatus = await _deviceCacheMgr.GetPregnancyHeartRateFreqStatusAsync(heartRate.Serialno);
if (phrFreqstatus == null) isAbnormal = 0;
var statsusDesc = (phrFreqstatus == null) ? $"MSG ID: {heartRate.MessageId} 常规" : "高频";
_logger.LogInformation($"{heartRate.Serialno} 在 {statsusDesc} 状态,心率值{heartRate.HeartRate},生成胎心值:{fetalHeartRate},统计周期:{statStartTime.ToString("yyyy-MM-dd HH:mm:ss")}----{statEndTime.ToString("yyyy-MM-dd HH:mm:ss")}");
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;
@@ -1300,13 +1361,63 @@ namespace HealthMonitor.WebApi

// 推送到api/v1/open/OpenIot/SetFetalHeartRateConfig
// 推送最后一条常规心率计算的胎心数据到iot设备
#region 推送最后一条常规心率计算的胎心数据到iot设备

// 高频(<=12)-常规
var lastPhr = await _serviceTDengine.GetLastAsync<PregnancyHeartRateModel>(heartRate.Serialno);
if (lastPhr.MessageId== heartRate.MessageId)
if (lastPhr.MessageId == heartRate.MessageId && phrFreqstatus == null)
{
await _serviceIotApi.SetFetalHeartRateConfig(heartRate.Serialno, fetalHeartRate, sampleTime, isAbnormal);
_logger.LogInformation($"{heartRate.Serialno} 推送最后一条常规心率计算的胎心数据到iot设备");
_logger.LogInformation($"{heartRate.Serialno} 推送最后一条常规心率计算的胎心数据到iot设备,高频(<=12)-常规");
}
// 高频(13)-常规-高频(13)
if (phrFreqstatus != null)
{
var phr = await _serviceTDengine.GetBySerialNoAsync<PregnancyHeartRateModel>(heartRate.Serialno, 1);
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 <= 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");
@@ -1353,5 +1464,73 @@ namespace HealthMonitor.WebApi
}
}


/// <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;
}

/// <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;
}
}
}

Loading…
Cancel
Save