ソースを参照

调整胎动计算

datasub12_fetal_heart_rate_1
H Vs 1ヶ月前
コミット
4461317826
1個のファイルの変更541行の追加4行の削除
  1. +541
    -4
      HealthMonitor.WebApi/Worker.cs

+ 541
- 4
HealthMonitor.WebApi/Worker.cs ファイルの表示

@@ -999,6 +999,7 @@ namespace HealthMonitor.WebApi
22~23->0
23~0->1
*/
/*
using (_logger.BeginScope(new Dictionary<string, object> { ["RequestId"] = $"FM-{imeiDel}-{DateTime.Now.ToString("yyyyMMddHHmmss")}" }))
{
try
@@ -1419,6 +1420,81 @@ namespace HealthMonitor.WebApi


}
*/
#endregion


#region 相隔1小时胎动延时计算(实时now是2小时,计算 lastupdate 0~1范围的数据,)
/**
0~1->2
1~2->3
2~3->4
3~4->5
4~5->6
5~6->7
7~8->9
8~9->10
9~10->11
10~11->12
11~12->13
12~13->14
14~15->16
15~16->17
16~17->18
17~18->19
18~19->20
19~20->21
20~21->22
21~22->23
22~23->0
23~0->1
*/

var triggerValue = (JObject)JsonConvert.DeserializeObject(e.PrevKv.Value.ToStringUtf8())!;
var trigger = triggerValue["trigger"]?.ToString();
if (!string.IsNullOrEmpty(trigger))
{
var triggerHeartRate = JsonConvert.DeserializeObject<HisGpsHeartRate>(trigger);
using (_logger.BeginScope(new Dictionary<string, object> { ["RequestId"] = $"FM-{imeiDel}-{DateTime.Now.ToString("yyyyMMddHHmmss")}" }))
{
try
{
var watchConfig = await _deviceCacheMgr.GetGpsDeviceWatchConfigCacheObjectBySerialNoAsync(imeiDel, "0067");
_logger.LogInformation($"触发胎动计算,设备配置{JsonConvert.SerializeObject(watchConfig)}");
var isFetalHeartEnable = watchConfig != null && (int)watchConfig["enabled"]! == 1;
var highFreqSampleInterval = (int)watchConfig!["highFreqSampleInterval"]! >= 60 ? (int)watchConfig!["highFreqSampleInterval"]! : 60;

if (isFetalHeartEnable)
{
var edoc = DateTimeUtil.ToDateTime(watchConfig!["EDOC"]!.ToString());
// 已经建模
var commonPHR = await _serviceTDengine.GetLastAsync<PregnancyCommonHeartRateModel>(imeiDel);
if (commonPHR != null)
{
await CalculateFetalMovementIntervalAsync(triggerHeartRate!, commonPHR, edoc);
}
else
{
_logger.LogWarning($"{imeiDel} 没有胎心建模数据");
}

}
else
{
_logger.LogWarning($"{imeiDel} 胎心监测功能没有开启");
}
}
catch (Exception ex)
{

_logger.LogError($"{imeiDel} 计算胎动数据异常 {ex.Message}\n {ex.StackTrace}");
}


}

}


#endregion
}
@@ -2360,7 +2436,7 @@ namespace HealthMonitor.WebApi
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}");
_logger.LogInformation($"{heartRate.Serialno} 常规胎心统计边界{boundaryStatStartTime}-{boundaryStatEndTime}");

try
{
@@ -2389,7 +2465,7 @@ namespace HealthMonitor.WebApi

if (statEndTime > boundaryStatEndTime)
{
_logger.LogInformation($"{heartRate.Serialno} 超过时间边界,迭代完成跳出循环 ");
_logger.LogInformation($"{heartRate.Serialno} 常规胎心统计超过时间边界,迭代完成跳出循环 ");
break;
}

@@ -2590,7 +2666,7 @@ namespace HealthMonitor.WebApi
}
else
{
_logger.LogInformation($"{heartRate.Serialno},统计周期:{statStartTime.ToString("yyyy-MM-dd HH:mm:ss")}----{statEndTime.ToString("yyyy-MM-dd HH:mm:ss")} ,胎心已处理");
_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));
// 跳出循环
@@ -2611,7 +2687,7 @@ namespace HealthMonitor.WebApi
}
catch (Exception ex)
{
_logger.LogError($"处理心数据时发生错误: {ex.Message}");
_logger.LogError($"处理常规胎心数据时发生错误: {ex.Message}");
}

@@ -2633,6 +2709,467 @@ namespace HealthMonitor.WebApi
//}
}


public async Task CalculateFetalMovementIntervalAsync(HisGpsHeartRate heartRate, PregnancyCommonHeartRateModel commonPHR,DateTime edoc)
{

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

//var start = "";
//var end = "";

DateTime fmNow = DateTime.Now;
int fmNowHour = fmNow.Hour;
DateTime startHour;
DateTime endHour;

// 跨天
if (fmNowHour == 1)
{
// last_update 23~0
startHour = fmNow.Date.AddDays(-1).AddHours(((DateTime)heartRate.LastUpdate!).Hour);
endHour = fmNow.Date;
}
else
{
// last_update 0~1->2,1~2->3,2~3->4...21~22->23
startHour = fmNow.Date.AddHours(((DateTime)heartRate.LastUpdate!).Hour);
endHour = fmNow.Date.AddHours(fmNowHour - 1);
}

var filterPhr = daysPhr
.Where(i => i.LastUpdate >= startHour && i.LastUpdate<= endHour)
.OrderBy(i => i.LastUpdate).ToList();

var startPhr = filterPhr.First();
var endPhr = filterPhr.Last();



// 数据统计边界
var boundaryStatStartTime = startPhr.LastUpdate.Date.AddHours(startPhr.LastUpdate.Hour);
var boundaryStatEndTime = endPhr.LastUpdate.Date.AddHours(endPhr.LastUpdate.Hour + 1);

_logger.LogInformation($"{heartRate.Serialno} 胎动统计边界{boundaryStatStartTime}-{boundaryStatEndTime}");



try
{
if (filterPhr.Count<2)
{
_logger.LogWarning($"{heartRate.Serialno} 胎动统计边界{boundaryStatStartTime}-{boundaryStatEndTime},数据少于2条,不统计");
return;
}
var c = 0;
while (true)
{
await Task.Delay(TimeSpan.FromSeconds(1));

var segmentStatStartTime = boundaryStatStartTime.AddHours(c * 1);
var segmentStatEndTime = segmentStatStartTime.AddHours(1);
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 = filterPhr
.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 fetalMovementSampleTime = DateTimeUtil.ConvertToTimeStamp(statStartTime).ToString()[..10];
var isFetalMovementExisted = await _deviceCacheMgr.FetalMovementIsExistedAsync(heartRate.Serialno, fetalMovementSampleTime);

if (!isFetalMovementExisted)
{
/// 开始计算
var phrRange = segmentPhr.OrderByDescending(i => i.LastUpdate)
.Select(i => i.LastUpdate)
.ToList();
if (phrRange.Count >= 2)
{
// 读取胎心数据
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.GreaterEqual
//},
},
OrderBys = new List<OrderByCondition>
{
new (){
IsDesc=true,
Key=nameof(HisGpsFetalHeartRate.SampleTime)
}
}
};
var fetalHeartRateIsAbnormal = 0;
var fhr = await _hisFetalHeartApiClient.GetFirstAsync(param, heartRate.Serialno[^2..], null, new RequestHeader { RequestId = Guid.NewGuid().ToString("D") });

// 胎心数据时间与胎动时间一致
var time = long.Parse(fhr.SampleTime.Length < 13 ? fhr.SampleTime.PadRight(13, '0') : fhr.SampleTime);
var fhrSampleTime = DateTimeUtil.GetDateTimeFromUnixTimeMilliseconds(time);
// 胎心数据时间与胎动时间一致,最后一条胎心数据与胎动数据的小时差不大于2
//if ((DateTime.Now.Hour - fhrSampleTime.Hour) <= 2)
if(true)
{
var duringMins = Math.Abs((phrRange.First() - phrRange.Last()).TotalMinutes);
//在餐后时间段(8:00~10:00,12:00~14:00,18:00~20:00,22:00~24:00)取中间值。其他时间段取正常起始值
bool isInTimeRanges = IsLastUpdateInTimeRanges(phrRange.First());

int pregnancyWeeks = (DateTime.Now - edoc.AddDays(-280)).Days / 7;
if (pregnancyWeeks >= 12 && pregnancyWeeks <= 50)
{
var fetalMovementMap = _mgrFetalMovementNormalValueRangeCache.GetFetalMovements();

var fetalMovementMapValue = isInTimeRanges ? fetalMovementMap
.Where(i =>
i.PregnancyPeriod![0] <= pregnancyWeeks &&
i.PregnancyPeriod[1] >= pregnancyWeeks)
.Select(i => i.MedianMovement)
.FirstOrDefault()
:
fetalMovementMap
.Where(i =>
i.PregnancyPeriod![0] <= pregnancyWeeks &&
i.PregnancyPeriod[1] >= pregnancyWeeks)
.Select(i => i.InitialMovement)
.FirstOrDefault()
;

// var fetalMovementTimeVar = (fetalMovementMapValue * duringMins * 2) / 120;
var fetalMovementTimeVar = (fetalMovementMapValue * duringMins) / 60;

if (duringMins < 59)
{
// 取12小时胎动总数最小值
fetalMovementMapValue = fetalMovementMap
.Where(i => i.PregnancyPeriod![0] <= pregnancyWeeks && i.PregnancyPeriod[1] >= pregnancyWeeks)
.First()
.TwelveHourMovementRange[0];
//取12小时胎动总数最小值的平均值
fetalMovementTimeVar = fetalMovementMapValue / 12;

_logger.LogWarning($"{heartRate.Serialno} 佩戴不足1小时,12小时胎动总数最小值{fetalMovementMapValue},平均值{fetalMovementTimeVar}");
}


#region 生理健康与胎动的关系
/// (步数)运动步数超过1500步则加1;
/// (体温)低烧则胎动减1,高烧胎动减2;低烧是37.3~38.5,38.6以上是高烧。
/// (血压)血压收缩压大于160则加1。
/// (心理)心理压力高加2,压力中加1;

var fetalMovementStepVar = 0;
var fetalMovementTempVar = 0;
var fetalMovementBpVar = 0;
var fetalMovementPpVar = 0;
var fetalMovementFhrVar = 0;
// 步数
if (false)
{
var step = await _personCacheMgr.GetStepPeriodicityAsync(heartRate.Serialno);
if (step != null)
{
if (DateTime.Now.Hour - ((DateTime)step?.LastUpdate!).Hour <= 2)
{
if (step.Steps > 1500) fetalMovementStepVar = 1;
}
else
{
_logger.LogWarning($"{heartRate.Serialno} 周期步数 时间无效");
}
}
else
{
_logger.LogWarning($"{heartRate.Serialno} 周期步数无数据");
}

}

// 体温
if (true)
{
var temp = await _personCacheMgr.GetTemperaturePeriodicityAsync(heartRate.Serialno);
if (temp != null)
{
if (DateTime.Now.Hour - ((DateTime)temp?.LastUpdate!).Hour <= 2)
{
// 中烧
if (temp.Temperature >= 37.3M && temp.Temperature <= 38.5M)
{
fetalMovementTempVar = -1;
}
// 高烧
if (temp.Temperature >= 38.6M)
{
fetalMovementTempVar = -2;
}
}
else
{
_logger.LogWarning($"{heartRate.Serialno} 周期体温 时间无效");
}
}
else
{
_logger.LogWarning($"{heartRate.Serialno} 周期体温无数据");
}

}

// 血压
if (true)
{
var bp = await _personCacheMgr.GetBloodPressPeriodicityAsync(heartRate.Serialno);
if (bp != null)
{
if (DateTime.Now.Hour - ((DateTime)bp?.LastUpdate!).Hour <= 2)
{
if (bp.SystolicValue > 160)
{
fetalMovementBpVar = 1;
}
}
else
{
_logger.LogWarning($"{heartRate.Serialno} 周期血压 时间无效");
}
}
else
{
_logger.LogWarning($"{heartRate.Serialno} 周期血压无数据");
}

}
// 心理
if (false)
{
//-1 不处理,
//0 正常,
//1 轻度,
//2 中度;
//3 重度;
GeneralParam psychResultParam = new()
{
Filters = new List<QueryFilterCondition>
{
new ()
{
Key=nameof(HisGpsPsychResult.Serialno),
Value=heartRate.Serialno,
ValueType=QueryValueTypeEnum.String,
Operator=QueryOperatorEnum.Equal
},
//new ()
//{
// Key=nameof(HisGpsFetalHeartRate.SampleTime),
// Value=sampleTime,
// ValueType=QueryValueTypeEnum.String,
// Operator=QueryOperatorEnum.GreaterEqual
//},
},
OrderBys = new List<OrderByCondition>
{
new (){
IsDesc=true,
Key=nameof(HisGpsPsychResult.Serialno)
}
}
};
var psych = await _hisPsychResultApiClient.GetFirstAsync(psychResultParam, heartRate.Serialno[^2..], null, new RequestHeader { RequestId = Guid.NewGuid().ToString("D") });
if (psych != null)
{
if (psych?.StressScore == 2)
{
fetalMovementPpVar = 1;
}

if (psych?.StressScore == 3)
{
fetalMovementPpVar = 2;
}
}
else
{
_logger.LogWarning($"{heartRate.Serialno} 周期心理压力无数据");
}

}

#endregion

#region 胎心与胎动的关系
/// 胎心值过缓时,则胎动数量减1;胎心值过速时,则胎动也加1。
/// 此值允许在上限值上继续增加,在下限值上继续减少,最小值为0。
/// 告警上限阀值

//1表示偏高;2表示偏低
if (true)
{
if (fhr.IsAbnormal == 2)
{
fetalMovementPpVar = -1;
}

if (fhr.IsAbnormal == 1)
{
fetalMovementPpVar = 1;
}
}

#endregion

_logger.LogInformation($"{heartRate.Serialno} 时间比例胎动值:{fetalMovementTimeVar}, 步数参数变动值:{fetalMovementStepVar},体温参数变动值:{fetalMovementTempVar},血压参数变动值:{fetalMovementBpVar},心理压力参数变动值:{fetalMovementPpVar},胎心参数变动值:{fetalMovementFhrVar}");


var fetalMovementValue = fetalMovementTimeVar + fetalMovementStepVar + fetalMovementTempVar + fetalMovementBpVar + fetalMovementPpVar + fetalMovementFhrVar;

// 四舍五入
var fetalMovement = (int)Math.Round(fetalMovementValue, 0, MidpointRounding.AwayFromZero);

_logger.LogInformation($"{heartRate.Serialno} 孕周:{pregnancyWeeks},胎动数据采样时间:{fetalMovementSampleTime}|{statStartTime.ToString("yyyy-MM-dd HH:mm:ss")}, 采样周期:{statStartTime}-{statEndTime}, 时间比例胎动值:{fetalMovementMapValue}, 佩戴时间 :{duringMins}|{phrRange.Last()}-{phrRange.First()}, 胎动计算值:{fetalMovementTimeVar}, 胎动四舍五入最终值:{fetalMovement} 已完成.");

// 获取胎心数据状态与胎动数据状态一致
//etalHeartRateIsAbnormal= fhr.IsAbnormal;
var feltalMovementIsAbnormal = fetalHeartRateIsAbnormal;

await _serviceIotApi.SetFetalMovementConfig(heartRate.Serialno, fetalMovement, fetalMovementSampleTime, feltalMovementIsAbnormal);

// 保存到MySQL数据库
HisGpsFetalMovement fm = new()
{
FetalMovementId = Guid.NewGuid().ToString("D"),
PersonId = commonPHR!.PersonId,
Serialno = heartRate.Serialno,
CreateTime = DateTime.Now,
IsAbnormal = feltalMovementIsAbnormal,
FetalMovementValue = fetalMovement,
SampleTime = fetalMovementSampleTime,
Method = 1,
IsDisplay = 1,
DeviceKey = commonPHR!.DeviceKey
};
await _hisFetalMovementApiClient.AddAsync(fm).ConfigureAwait(false);

var device = await _deviceCacheMgr.GetDeviceBySerialNoAsync(heartRate.Serialno).ConfigureAwait(false);
var fmMsgId = $"{heartRate.Serialno}-{fetalMovementSampleTime}-{Guid.NewGuid().ToString("D")[^3..]}";
var fmMsgTime = DateTimeUtil.GetDateTimeFromUnixTimeMilliseconds(long.Parse(fetalMovementSampleTime.Length < 13 ? fetalMovementSampleTime.PadRight(13, '0') : fetalMovementSampleTime)).ToString("yyyy-MM-dd HH:mm:ss");
// 胎动数据推送到第三方
var topic = "topic.push.third";
var fmThridMsg = new
{
messageId = fmMsgId,
topic = topic,
time = fmMsgTime,
data = new
{
imei = heartRate.Serialno,
value = fetalMovement,
isAbnormal = feltalMovementIsAbnormal,
type = "fetalMovement"
}
};
await _serviceMqProcess.ProcessIMEIEventMessageAsync(fmMsgId, topic, 31, fmThridMsg).ConfigureAwait(false);

// 胎动数据推送到微信
if (feltalMovementIsAbnormal != 0)
{
topic = "topic.push.wx";
var fmMsg = new
{
messageId = Guid.NewGuid().ToString("D"),
topic = topic,
time = DateTimeUtil.GetDateTimeFromUnixTimeMilliseconds(long.Parse(fetalMovementSampleTime.Length < 13 ? fetalMovementSampleTime.PadRight(13, '0') : fetalMovementSampleTime)).ToString("yyyy-MM-dd HH:mm:ss"),
data = new
{
deviceId = device?.DeviceId,
imei = heartRate.Serialno,
alarmTypeId = 12,
alarmDeviceName = heartRate.Serialno,
alarmRemarks = JsonConvert.SerializeObject(new { fetalMovementValue = fetalMovement, isAbnormal = feltalMovementIsAbnormal }),
address = string.Empty,
deviceKey = device?.DeviceId
}
};
await _serviceMqProcess.ProcessIMEIEventMessageAsync(fmMsgId, topic, fmMsg).ConfigureAwait(false);
}
// 设置入库缓存记录
await _deviceCacheMgr.SetFetalMovementAsync(heartRate.Serialno, fetalMovementSampleTime, fm);


}
else
{
_logger.LogWarning($"{heartRate.Serialno} 孕周 {pregnancyWeeks},超出胎动计算范围");
}
}
else
{
_logger.LogWarning($"{heartRate.Serialno} 最后一条胎心数据与胎动数据的小时差大于2,不计算胎动数据");
}
}
else
{
_logger.LogInformation($"{heartRate.Serialno} 胎动记录{isFetalMovementExisted},数据采样时间:{fetalMovementSampleTime}|{statStartTime.ToString("yyyy-MM-dd HH:mm:ss")}, 周期:{statStartTime}-{statEndTime} 不足两条,不能判断是否持续佩戴");

}
}
else
{
_logger.LogInformation($"{heartRate.Serialno} 胎动记录{isFetalMovementExisted},数据采样时间:{fetalMovementSampleTime}|{statStartTime.ToString("yyyy-MM-dd HH:mm:ss")}, 周期:{statStartTime}-{statEndTime} 已处理");
}

// 跳出循环
if (statEndTime.ToString("yyyyMMddHHmm") == boundaryStatEndTime.ToString("yyyyMMddHHmm"))
{
_logger.LogInformation($"{heartRate.Serialno} 胎动记录迭代完成跳出循环 ");
break;
}
else
{
_logger.LogInformation($"{heartRate.Serialno} 胎动周期{statStartTime.ToString("yyyy-MM-dd HH:mm:ss")}-{statEndTime.ToString("yyyy-MM-dd HH:mm:ss")}计算完成, ");
}
}
}
catch (Exception ex)
{
_logger.LogError($"处理胎动数据时发生错误: {ex.Message}");
}

}
//private DateTime GetSampleTimeFromLastUpdate(DateTime lastUpdate,int interval)
//{
// DateTime nowInterval = lastUpdate;


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