Bladeren bron

高频心率计算

datasub12_fetal_heart_rate_0
H Vs 4 maanden geleden
bovenliggende
commit
b4deb233d1
10 gewijzigde bestanden met toevoegingen van 517 en 69 verwijderingen
  1. +34
    -0
      HealthMonitor.Model/Cache/FhrPhrMap.cs
  2. +13
    -0
      HealthMonitor.Model/Service/Mapper/PregnancyCommonHeartRateModel.cs
  3. +112
    -25
      HealthMonitor.Service/Biz/IotApiService.cs
  4. +95
    -14
      HealthMonitor.Service/Biz/db/TDengineService.cs
  5. +57
    -2
      HealthMonitor.Service/Cache/DeviceCacheManager.cs
  6. +28
    -0
      HealthMonitor.Service/Cache/FhrPhrMapCacheManager.cs
  7. +153
    -8
      HealthMonitor.Service/Resolver/PregnancyHeartRateResolver.cs
  8. +20
    -18
      HealthMonitor.WebApi/Controllers/HealthMonitor/HmBloodPressController.cs
  9. +1
    -0
      HealthMonitor.WebApi/Program.cs
  10. +4
    -2
      HealthMonitor.WebApi/Worker.cs

+ 34
- 0
HealthMonitor.Model/Cache/FhrPhrMap.cs Bestand weergeven

@@ -0,0 +1,34 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace HealthMonitor.Model.Cache
{
/// <summary>
/// 胎心与孕妇心率映射
/// </summary>
public class FhrPhrMap
{
// 孕周期
public int[]? PregnancyPeriod { get; set; }
// 胎心范围
public int[]? FetalHeartRateRange { get; set; }
// 胎心平均值
public int FetalHeartRateAverage { get; set; }
// 孕妈心率
public int[]? PregnancyHeartRateRange { get; set; }
// 胎心浮动系数
public int[]? FetalHeartRateFluctuationCoefficient { get; set; }

public FhrPhrMap(int[] pregnancyPeriod, int[] fetalHeartRateRange, int fetalHeartRateAverage, int[] maternalHeartRateRange, int[] fetalHeartRateFluctuationCoefficient)
{
PregnancyPeriod = pregnancyPeriod;
FetalHeartRateRange = fetalHeartRateRange;
FetalHeartRateAverage = fetalHeartRateAverage;
PregnancyHeartRateRange = maternalHeartRateRange;
FetalHeartRateFluctuationCoefficient = fetalHeartRateFluctuationCoefficient;
}
}
}

+ 13
- 0
HealthMonitor.Model/Service/Mapper/PregnancyCommonHeartRateModel.cs Bestand weergeven

@@ -68,6 +68,19 @@ namespace HealthMonitor.Model.Service.Mapper
[SqlSugar.SugarColumn(ColumnName = "stat_end_time")]
public DateTime StatEndTime { get; set; }

[JsonProperty("stat_max_value_fpr_coefficient")]
[SqlSugar.SugarColumn(ColumnName = "stat_max_value_fpr_coefficient")]
public float StatMaxValueFprCoefficient { get; set; }

[JsonProperty("stat_min_value_fpr_coefficient")]
[SqlSugar.SugarColumn(ColumnName = "stat_min_value_fpr_coefficient")]
public float StatMinValueFprCoefficient { get; set; }


[JsonProperty("stat_mode_avg_fpr_coefficient")]
[SqlSugar.SugarColumn(ColumnName = "stat_mode_avg_fpr_coefficient")]
public float StatModeAvgFprCoefficient { get; set; }

[JsonProperty("remark")]
[SqlSugar.SugarColumn(ColumnName = "remark")]
public string Remark { get; set; } = default!;


+ 112
- 25
HealthMonitor.Service/Biz/IotApiService.cs Bestand weergeven

@@ -255,57 +255,63 @@ namespace HealthMonitor.Service.Biz
#endregion

#region 平台下发胎心监测参数
public async Task<bool> SetFetalHeartRateConfig(string serialno, int modeStatus=0, int maxValue=0, int minValue = 0)
/// <summary>
/// 胎心设置
/// </summary>
/// <param name="serialno"></param>
/// <param name="modeStatus"></param>
/// <param name="maxValue"></param>
/// <param name="minValue"></param>
/// <returns></returns>
public async Task<bool> SetFetalConfig(string serialno, int modeStatus=0, int maxValue=0, int minValue = 0)
{
try
{
#region 读取缓存
// db7.HashGet("TELPO#GPSDEVICE_WATCH_CONFIG_HASH","861281060086083_0067")
var watchConfig = await _deviceCacheMgr.GetGpsDeviceWatchConfigCacheObjectBySerialNoAsync(serialno, "0067");
if (watchConfig==null)
if (watchConfig == null)
{
return false;
}

#endregion

#region 获取B端 Token
var getTokenUrl = $"{_configService.IotAuth}/getAccessToken2";
var tokenReq = new
{
manufactorId= "7c7c38cb-d045-41d8-b3d0-fcaaa84a8f02",
imei= serialno
};
var resToken = await _httpHelper.HttpToPostAsync(getTokenUrl, tokenReq).ConfigureAwait(false);
var tokenAuth= JsonConvert.DeserializeObject(resToken ?? string.Empty) as JToken;
var tokenAuthData = tokenAuth?["data"]?.ToString()??string.Empty;
string tokenAuthData = await getAccessToken2(serialno).ConfigureAwait(false);
if (tokenAuthData == null)
{
return false;
}
#endregion
_logger.LogInformation($" SetFetalConfig Token TelpoManufactorId: {tokenAuthData}");
#region 发送到B端
List<KeyValuePair<string, string>> headers = new()
{
new KeyValuePair<string, string>("TelpoManufactorId", tokenAuthData)
new KeyValuePair<string, string>("AccessToken", tokenAuthData)
};


var data = new
{
imeis = new string[] { serialno },
enabled = (int)watchConfig["fetalParamters"]!["enabled"]!,
triggerHighFreqHigh = maxValue == 0 ? (int)watchConfig["fetalParamters"]!["triggerHighFreqHigh"]!:maxValue,
triggerLowFreqLow = minValue == 0 ? (int)watchConfig["fetalParamters"]!["triggerHighFreqLow"]! : minValue,
highFreqSampleTimes = (int)watchConfig["fetalParamters"]!["highFreqSampleTimes"]!,
highFreqSampleInterval = (int)watchConfig["fetalParamters"]!["highFreqSampleInterval"]!,
stopHighFreqSampleCount = (int)watchConfig["fetalParamters"]!["stopHighFreqSampleCount"]!,
enabled = (int)watchConfig["enabled"]!,
interval= (int)watchConfig["interval"]!,
triggerHighFreqHigh = maxValue == 0 ? (int)watchConfig["triggerHighFreqHigh"]! : maxValue,
triggerHighFreqLow = minValue == 0 ? (int)watchConfig["triggerHighFreqLow"]! : minValue,
highFreqSampleTimes = (int)watchConfig["highFreqSampleTimes"]!,
highFreqSampleInterval = (int)watchConfig["highFreqSampleInterval"]!,
stopHighFreqSampleCount = (int)watchConfig["stopHighFreqSampleCount"]!,
mode = modeStatus,
edoc = watchConfig["fetalParamters"]!["EDOC"]!,
vibrateEnabled = (int)watchConfig["fetalParamters"]!["vibrateEnabled"]!,
lcdEnabled= (int)watchConfig["fetalParamters"]!["lcdEnabled"]!
edoc = watchConfig["EDOC"]!,
vibrateEnabled = (int)watchConfig["vibrateEnabled"]!,
lcdEnabled = (int)watchConfig["lcdEnabled"]!,
upperAlarmThreshold = (int)watchConfig["upperAlarmThreshold"]!,
lowerAlarmThreshold = (int)watchConfig["lowerAlarmThreshold"]!
};
var setUrl = $"{_configService.IotCore}/api/v1/open/OpenIot/SetFetalConfig";
var setUrl = $"{_configService.IotCore}/SetFetalConfig";
_logger.LogInformation($"{setUrl} 请求 {JsonConvert.SerializeObject(JsonConvert.SerializeObject(data))}");
var res = await _httpHelper.HttpToPostAsync(setUrl, data, headers).ConfigureAwait(false);
_logger.LogInformation($"{setUrl} 响应 {res}");
var resJToken = JsonConvert.DeserializeObject(res ?? string.Empty) as JToken;
return resJToken?["message"]?.ToString().Equals("ok") ?? false;
//response.Flag = resJToken?["message"]?.ToString().Equals("ok") ?? false;
@@ -318,10 +324,91 @@ namespace HealthMonitor.Service.Biz
}
catch (Exception ex)
{
_logger.LogError($"{nameof(SetFetalHeartRateConfig)} 下发胎心检测参数异常:{ex.Message}, {ex.StackTrace}");
_logger.LogError($"{nameof(SetFetalConfig)} 下发胎心检测参数异常:{ex.Message}, {ex.StackTrace}");
return false;
}
}


/// <summary>
/// 下发胎心数据
/// </summary>
/// <param name="serialno"></param>
/// <param name="fhr">心率值</param>
/// <param name="sampeTime">检测时间(时间戳)</param>
/// <param name="isAbnormal">0 //是否异常 0 表示正常;1表示偏高;2表示偏低</param>
/// <returns></returns>
public async Task<bool> SetFetalHeartRateConfig(string serialno, int fhr, string sampeTime, int isAbnormal)
{
try
{
#region 读取缓存
// db7.HashGet("TELPO#GPSDEVICE_WATCH_CONFIG_HASH","861281060086083_0067")
var watchConfig = await _deviceCacheMgr.GetGpsDeviceWatchConfigCacheObjectBySerialNoAsync(serialno, "0067");
if (watchConfig == null)
{
return false;
}

#endregion

#region 获取B端 Token
string tokenAuthData = await getAccessToken2(serialno).ConfigureAwait(false);
if (tokenAuthData == null)
{
return false;
}
#endregion

#region 下发数据
List<KeyValuePair<string, string>> headers = new()
{
new KeyValuePair<string, string>("AccessToken", tokenAuthData)
};
_logger.LogInformation($" SetFetalHeartRateConfig Token TelpoManufactorId: {tokenAuthData}");
var data = new
{
imei= serialno,
heartValue= fhr,
sampeTime,
isAbnormal
};

var setUrl = $"{_configService.IotCore}/SetFetalHeartRateConfig";
_logger.LogInformation($"{setUrl} 请求 {JsonConvert.SerializeObject(JsonConvert.SerializeObject(data))}");
var res = await _httpHelper.HttpToPostAsync(setUrl, data, headers).ConfigureAwait(false);
_logger.LogInformation($"{setUrl} 响应 {res}");
var resJToken = JsonConvert.DeserializeObject(res ?? string.Empty) as JToken;
return resJToken?["message"]?.ToString().Equals("ok") ?? false;

#endregion
}
catch (Exception ex)
{
_logger.LogError($"{nameof(SetFetalHeartRateConfig)} 下发胎心数据异常:{ex.Message}, {ex.StackTrace}");
return false;
}
}

private async Task<string> getAccessToken2(string serialno)
{
var getTokenUrl = $"{_configService.IotAuth}/getAccessToken2";
var tokenReq = new
{
manufactorId = "7c7c38cb-d045-41d8-b3d0-fcaaa84a8f02",
imei = serialno
};
_logger.LogInformation($"{getTokenUrl} 请求 {JsonConvert.SerializeObject(tokenReq)}");
var resToken = await _httpHelper.HttpToPostAsync(getTokenUrl, tokenReq).ConfigureAwait(false);
_logger.LogInformation($"{getTokenUrl} 响应 {resToken}");
var tokenAuth = JsonConvert.DeserializeObject(resToken ?? string.Empty) as JToken;
var tokenAuthData = tokenAuth?["data"]?.ToString() ?? string.Empty;
return tokenAuthData;
}

#endregion

}


+ 95
- 14
HealthMonitor.Service/Biz/db/TDengineService.cs Bestand weergeven

@@ -24,6 +24,7 @@ using System.Xml.Linq;
using TDengineDriver;
using TDengineDriver.Impl;
using TDengineTMQ;
using HealthMonitor.Service.Cache;

namespace HealthMonitor.Service.Biz.db
{
@@ -34,15 +35,20 @@ namespace HealthMonitor.Service.Biz.db
private readonly HttpHelper _httpHelper=default!;
private readonly TDengineServiceConfig _configTDengineService;
private readonly SqlSugarClient _clientSqlSugar;
private readonly FhrPhrMapCacheManager _mgrFhrPhrMapCache;
private readonly DeviceCacheManager _deviceCacheMgr;

public TDengineService(ILogger<TDengineService> logger,
IOptions<TDengineServiceConfig> configTDengineService,
HttpHelper httpHelper
HttpHelper httpHelper,
FhrPhrMapCacheManager fhrPhrMapCacheManager, DeviceCacheManager deviceCacheMgr
)
{
_logger = logger;
_configTDengineService = configTDengineService.Value;
_httpHelper = httpHelper;
_mgrFhrPhrMapCache = fhrPhrMapCacheManager;
_deviceCacheMgr = deviceCacheMgr;
_clientSqlSugar = new SqlSugarClient(new ConnectionConfig()
{
DbType = SqlSugar.DbType.TDengine,
@@ -756,6 +762,40 @@ namespace HealthMonitor.Service.Biz.db
.OrderByDescending(timestampLambda).ToListAsync();
return res;
}

public async Task<List<T>> GetBySerialNoAsync<T>(string serialNo, int days) where T : class
{
var tableName = typeof(T)
.GetCustomAttribute<STableAttribute>()?
.STableName;

// 创建表示 Timestamp 属性的表达式
var parameter = Expression.Parameter(typeof(T), "x");
var timestampProperty = Expression.Property(parameter, "Timestamp");
var timestampLambda = Expression.Lambda<Func<T, object>>(Expression.Convert(timestampProperty, typeof(object)), parameter);

// 创建表示 SerialNo 属性的表达式
var serialNoProperty = Expression.Property(parameter, "SerialNumber");
var serialNoConstant = Expression.Constant(serialNo);
var equalExpression = Expression.Equal(serialNoProperty, serialNoConstant);

// 创建表示 Timestamp 大于指定天数的表达式
var daysAgo = DateTime.Now.AddDays(-days);
var daysAgoConstant = Expression.Constant(daysAgo, typeof(DateTime));
var greaterThanExpression = Expression.GreaterThan(timestampProperty, daysAgoConstant);

// 合并 SerialNo 和 Timestamp 条件
var combinedExpression = Expression.AndAlso(equalExpression, greaterThanExpression);
var combinedLambda = Expression.Lambda<Func<T, bool>>(combinedExpression, parameter);

var res = await _clientSqlSugar
.Queryable<T>()
.AS(tableName)
.Where(combinedLambda)
.OrderByDescending(timestampLambda).ToListAsync();

return res;
}
#endregion

#region 胎心算法
@@ -770,12 +810,12 @@ namespace HealthMonitor.Service.Biz.db
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 > DateTime.Now.AddDays(-days))
.Where(i => i.Timestamp > daysAgo)
//.OrderByDescending(i => i.PregnancyHeartRate)
.Select(i =>i.PregnancyHeartRate)
.ToListAsync();
@@ -846,11 +886,12 @@ namespace HealthMonitor.Service.Biz.db
.GetCustomAttribute<STableAttribute>()?
.STableName;

var daysAgo = DateTime.Now.AddDays(-days);
var collection = await _clientSqlSugar
.Queryable<PregnancyHeartRateModel>()
.AS(tableName)
.Where(i => i.SerialNumber.Equals(serialNo))
.Where(i => i.Timestamp > DateTime.Now.AddDays(-days))
.Where(i => i.Timestamp > daysAgo)
.OrderByDescending(i => i.Timestamp)
.ToArrayAsync();

@@ -925,21 +966,61 @@ namespace HealthMonitor.Service.Biz.db
Console.WriteLine("新数据集: " + string.Join(", ", closestToModeData));
Console.WriteLine("新数据集的数量: " + closestToModeData.Count);

return new PregnancyCommonHeartRateModel()
var fhrMap = _mgrFhrPhrMapCache.GetHeartRatesMap();
var watchConfig = await _deviceCacheMgr.GetGpsDeviceWatchConfigCacheObjectBySerialNoAsync(serialNo, "0067");
if (watchConfig == null )
{
return null;
}
long.TryParse(watchConfig["EDOC"]!.ToString(), out long edoc);
// "EDOC": "1720860180652",EDOC -280 days =怀孕时间

int pregnancyWeek = (DateTime.Now-DateTimeUtil.GetDateTimeFromUnixTimeMilliseconds(edoc).AddDays(-280)).Days / 7;

_logger.LogInformation($"怀孕周数 {pregnancyWeek}");

var statMaxValueFprCoefficient = 0;
var statMinValueFprCoefficient = 0;
var StatModeAvgFprCoefficient = 0;
// 20-40周之间
if (pregnancyWeek >= 12 && pregnancyWeek <= 40)
{
var map= fhrMap
.Where(i =>
i.PregnancyPeriod![0] <= pregnancyWeek &&
i.PregnancyPeriod[1] >= pregnancyWeek &&
i.PregnancyHeartRateRange![0] <=mode &&
i.PregnancyHeartRateRange[1]>=mode)
.FirstOrDefault();

if (map != null)
{
statMaxValueFprCoefficient = map.FetalHeartRateRange![1] / res.Max();
statMinValueFprCoefficient = map.FetalHeartRateRange[0]/res.Min();
StatModeAvgFprCoefficient = map.FetalHeartRateAverage / mode;
}
}

return new PregnancyCommonHeartRateModel()
{
Timestamp = DateTime.Now,
PersonId = collection.First().DeviceKey,
DeviceKey = collection.First().DeviceKey,
SerialNumber = collection.First().SerialNumber,
Mode = mode,
Percentage= percentage,
MaxValue= closestToModeData.Max(),
MinValue= closestToModeData.Min(),
OriginalMaxValue=res.Max(),
OriginalMinValue= res.Min(),
Mode = mode,
Percentage = percentage,
MaxValue = closestToModeData.Max(),
MinValue = closestToModeData.Min(),
OriginalMaxValue = res.Max(),
OriginalMinValue = res.Min(),
CreateTime = DateTime.Now,
StatStartTime = collection.OrderBy(i=>i.Timestamp).Select(i=>i.Timestamp).First(),
StatEndTime= collection.OrderBy(i => i.Timestamp).Select(i => i.Timestamp).Last(),
StatStartTime = collection.OrderBy(i => i.Timestamp).Select(i => i.Timestamp).First(),
StatEndTime = collection.OrderBy(i => i.Timestamp).Select(i => i.Timestamp).Last(),
StatMaxValueFprCoefficient = statMaxValueFprCoefficient,
StatMinValueFprCoefficient = statMinValueFprCoefficient,
StatModeAvgFprCoefficient = StatModeAvgFprCoefficient,
Remark = string.Empty,
SerialTailNumber = serialNo.Substring(serialNo.Length - 2)
};

}


+ 57
- 2
HealthMonitor.Service/Cache/DeviceCacheManager.cs Bestand weergeven

@@ -6,6 +6,9 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TDengineTMQ;
using System.Web;
using HealthMonitor.Model.Service.Mapper;

namespace HealthMonitor.Service.Cache
{
@@ -15,7 +18,7 @@ namespace HealthMonitor.Service.Cache
private const string CACHE_KEY_GPSDEVICE_WATCH_CONFIG = "#GPSDEVICE_WATCH_CONFIG_HASH";


public DeviceCacheManager(ILogger<DeviceCacheManager> logger)
public DeviceCacheManager(ILogger<DeviceCacheManager> logger)
{
_logger = logger;
}
@@ -37,7 +40,7 @@ namespace HealthMonitor.Service.Cache
try
{
var config = await RedisHelperDb7.HGetAsync(CACHE_KEY_GPSDEVICE_WATCH_CONFIG, $"{sn}_{bizCode}").ConfigureAwait(false);
if (config == null) return null;
return (JObject)JsonConvert.DeserializeObject(config)!;
}
@@ -48,5 +51,57 @@ namespace HealthMonitor.Service.Cache

return null;
}


#region 心率状态
/// <summary>
/// 读取高频状态
/// </summary>
/// <param name="sn"></param>
/// <returns></returns>
public async Task<PregnancyHeartRateModel?> GetPregnancyHeartRateFreqStatusAsync(string sn)
{
try
{
var key = $"PregnancyHeartRateFreqStatus_{sn}";
var status = await RedisHelper.GetAsync(key).ConfigureAwait(false);
if (string.IsNullOrEmpty(status)) return null;
return JsonConvert.DeserializeObject<PregnancyHeartRateModel>(status);
}
catch (Exception ex)
{
_logger.LogWarning($"Redis 发生异常:{ex.Message}, {ex.StackTrace}");
}
return null;
}

public async Task SetPregnancyHeartRateFreqStatusAsync(string sn, PregnancyHeartRateModel status,int expire=-1)
{
try
{
var key = $"PregnancyHeartRateFreqStatus_{sn}";
var data=JsonConvert.SerializeObject(status);
await RedisHelper.SetAsync(key, data, expire).ConfigureAwait(false);
}
catch (Exception ex)
{
_logger.LogWarning($"Redis 发生异常:{ex.Message}, {ex.StackTrace}");
}
}

public async Task DelPregnancyHeartRateFreqStatusAsync(string sn)
{
try
{
var key = $"PregnancyHeartRateFreqStatus_{sn}";
await RedisHelper.DelAsync(key).ConfigureAwait(false);
}
catch (Exception ex)
{
_logger.LogWarning($"Redis 发生异常:{ex.Message}, {ex.StackTrace}");
}
}
#endregion
}
}

+ 28
- 0
HealthMonitor.Service/Cache/FhrPhrMapCacheManager.cs Bestand weergeven

@@ -0,0 +1,28 @@
using HealthMonitor.Model.Cache;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace HealthMonitor.Service.Cache
{
public class FhrPhrMapCacheManager
{
private readonly ILogger<FhrPhrMapCacheManager> _logger;
public FhrPhrMapCacheManager(ILogger<FhrPhrMapCacheManager> logger )
{
_logger = logger;
}
public List<FhrPhrMap> GetHeartRatesMap()
{
return new List<FhrPhrMap>
{
new FhrPhrMap(new int[] {12, 20}, new int[] {120, 170}, 162, new int[] {60, 100}, new int[] {50, 80}),
new FhrPhrMap(new int[] {21, 30}, new int[] {110, 160}, 147, new int[] {60, 100}, new int[] {40, 70}),
new FhrPhrMap(new int[] {31, 40}, new int[] {110, 160}, 139, new int[] {60, 100}, new int[] {40, 70})
};
}
}
}

+ 153
- 8
HealthMonitor.Service/Resolver/PregnancyHeartRateResolver.cs Bestand weergeven

@@ -1,9 +1,14 @@
using HealthMonitor.Common;
using HealthMonitor.Common.helper;
using HealthMonitor.Model.Service.Mapper;
using HealthMonitor.Service.Biz;
using HealthMonitor.Service.Biz.db;
using HealthMonitor.Service.Cache;
using HealthMonitor.Service.Etcd;
using HealthMonitor.Service.Resolver.Interface;
using HealthMonitor.Service.Sub;
using HealthMonitor.Service.Sub.Topic.Model;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using System;
@@ -18,17 +23,27 @@ namespace HealthMonitor.Service.Resolver
public class PregnancyHeartRateResolver : IResolver
{
private readonly ILogger<PregnancyHeartRateResolver> _logger;
private readonly TDengineService _serviceTDengine;

private readonly DeviceCacheManager _deviceCacheMgr;
private readonly IotApiService _serviceIotApi;


private readonly AsyncLocal<string> _messageId = new();
private readonly AsyncLocal<HisGpsHeartRate> _msgData = new();
private readonly HttpHelper _httpHelper = default!;
private readonly EtcdService _serviceEtcd;


public PregnancyHeartRateResolver(ILogger<PregnancyHeartRateResolver> logger, HttpHelper httpHelper, EtcdService serviceEtcd)
public PregnancyHeartRateResolver(ILogger<PregnancyHeartRateResolver> logger,
HttpHelper httpHelper, EtcdService serviceEtcd, DeviceCacheManager deviceCacheMgr, IotApiService iotApiService, TDengineService serviceDengine)
{
_logger = logger;
_httpHelper = httpHelper;
_serviceEtcd = serviceEtcd;
_serviceTDengine = serviceDengine;
_deviceCacheMgr = deviceCacheMgr;
_serviceIotApi = iotApiService;
}

public void SetResolveInfo(PackageMsgModel msg)
@@ -56,9 +71,126 @@ namespace HealthMonitor.Service.Resolver
{
//throw new NotImplementedException();
var messageId = _messageId.Value;
var phr = _msgData.Value!;
var heartRate = _msgData.Value!;


var watchConfig = await _deviceCacheMgr.GetGpsDeviceWatchConfigCacheObjectBySerialNoAsync(heartRate.Serialno, "0067");
var isFetalHeartEnable = watchConfig != null && (int)watchConfig["enabled"]! == 1;

if (isFetalHeartEnable)
{
#region 高频心率计算
var phr = await _serviceTDengine.GetBySerialNoAsync<PregnancyHeartRateModel>(heartRate.Serialno, 7);
if (phr.Count >= 30)
{
// 获取最近的两个记录,并计算它们的 LastUpdate 时间差
var firstTwoPhr = phr.OrderByDescending(i => i.Timestamp).Take(2).Select(i => i.LastUpdate).ToList();
var timeDiff = firstTwoPhr[0] - firstTwoPhr[1];

// 如果需要,将时间差转换为秒
var timeDiffInSeconds = timeDiff.TotalSeconds;
// 高频心率采样间隔
var highFreqSampleInterval = (int)watchConfig!["highFreqSampleInterval"]!;
// 触发高频监测的心率上限值
var triggerHighFreqHigh = (int)watchConfig["triggerHighFreqHigh"]!;
// 触发高频监测的心率下限值
var triggerHighFreqLow = (int)watchConfig["triggerHighFreqLow"]!;
//停止高频心率采样心率连续正常次数
var stopHighFreqSampleCount = (int)watchConfig["stopHighFreqSampleCount"]!;
// 高频心率采集时长 0 为持续采集,非零为高频心率的采集时长
var highFreqSampleTimes = (int)watchConfig["stopHighFreqSampleCount"]!;
// 告警上限阀值
var upperAlarmThreshold = (int)watchConfig["upperAlarmThreshold"]!;
// 告警下限阀值
var lowerAlarmThreshold= (int)watchConfig["lowerAlarmThreshold"]!;

// 高频心率启动
if (timeDiffInSeconds<=highFreqSampleInterval)
{
/// 设置高频状态
var phrFreqstatus =await _deviceCacheMgr.GetPregnancyHeartRateFreqStatusAsync(heartRate.Serialno);
if (phrFreqstatus == null)
{
// 设置高频状态
var freqFirstPhr= phr.OrderByDescending(i => i.Timestamp).First();
await _deviceCacheMgr.SetPregnancyHeartRateFreqStatusAsync(heartRate.Serialno, freqFirstPhr);
phrFreqstatus = await _deviceCacheMgr.GetPregnancyHeartRateFreqStatusAsync(heartRate.Serialno);
}
/// phr PregnancyHeartRate 连续连续正常次数个值都是正常(大于等于triggerHighFreqLow,少于等于triggerHighFreqHig),
/// 取连续正常次数正常值的平均值,推送到api/v1/open/OpenIot/SetFetalHeartRateConfig
#region 检查是否连续12个值都是正常的
// 获取最近连续正常次数个心率记录
var lastPhr = phr.OrderByDescending(i => i.Timestamp).Take(stopHighFreqSampleCount).ToList();

// 检查是否连续12个值都是正常的
if (lastPhr.All(i => i.PregnancyHeartRate >= triggerHighFreqLow && i.PregnancyHeartRate <= triggerHighFreqHigh))
{
var avgPhr = lastPhr.Select(i => i.PregnancyHeartRate).Average();

// 计算一般心率得到胎心系数
await SaveAndPushFetalHeartRateAsync(heartRate, upperAlarmThreshold, lowerAlarmThreshold, avgPhr);


}
#endregion
}
// 高频心率结束
else
{
var phrFreqstatus = await _deviceCacheMgr.GetPregnancyHeartRateFreqStatusAsync(heartRate.Serialno);
if (phrFreqstatus != null)
{
/// 在highFreqSampleTimes=0一直异常(大于等于triggerHighFreqLow,少于等于triggerHighFreqHig),
/// 取所有值的平均值,推送胎心数据到api/v1/open/OpenIot/SetFetalHeartRateConfig
if (highFreqSampleTimes==0)
{
// if (phr.OrderByDescending(i => i.Timestamp)
//.Where(i => i.Timestamp >= phrFreqstatus?.Timestamp)
//.Skip(1) // 去除首条
//.All(i => i.PregnancyHeartRate < triggerHighFreqLow || i.PregnancyHeartRate > triggerHighFreqHig))
// {
// var avgPhr = phr.Select(i => i.PregnancyHeartRate).Average();

// // 推送胎心数据到 api/v1/open/OpenIot/SetFetalHeartRateConfig
// // 计算一般心率得到胎心系数
// }

var avgPhr = phr.OrderByDescending(i => i.Timestamp)
.Where(i => i.Timestamp >= phrFreqstatus?.Timestamp)
.Skip(1) // 去除首条
.Where(i => i.PregnancyHeartRate < triggerHighFreqLow || i.PregnancyHeartRate > triggerHighFreqHigh)
.Select(i => i.PregnancyHeartRate).Average();
// 推送胎心数据到 api/v1/open/OpenIot/SetFetalHeartRateConfig
// 计算一般心率得到胎心系数
await SaveAndPushFetalHeartRateAsync(heartRate, upperAlarmThreshold, lowerAlarmThreshold, avgPhr);

}

/// 在highFreqSampleTimes>0一直异常(大于等于triggerHighFreqLow,少于等于triggerHighFreqHig),
/// 取所有值的平均值,推送胎心数据到api/v1/open/OpenIot/SetFetalHeartRateConfig
if (highFreqSampleTimes > 0 && heartRate.LastUpdate >= (phrFreqstatus?.LastUpdate + TimeSpan.FromSeconds(highFreqSampleTimes)))
{

var avgPhr = phr
.Where(i => i.Timestamp >= phrFreqstatus?.Timestamp)
.Skip(1) // 去除首条
.Where(i => i.PregnancyHeartRate < triggerHighFreqLow || i.PregnancyHeartRate > triggerHighFreqHigh)
.Select(i => i.PregnancyHeartRate).Average();
// 推送胎心数据到 api/v1/open/OpenIot/SetFetalHeartRateConfig
// 计算一般心率得到胎心系数
await SaveAndPushFetalHeartRateAsync(heartRate, upperAlarmThreshold, lowerAlarmThreshold, avgPhr);
}

// 删除高频状态的首条记录
await _deviceCacheMgr.DelPregnancyHeartRateFreqStatusAsync(heartRate.Serialno);
}
}
}
#endregion
}

#region 定时下发触发器
var key = $"health_monitor/schedule_push/pregnancy_heart_rate/imei/{phr.Serialno}";
var key = $"health_monitor/schedule_push/pregnancy_heart_rate/imei/{heartRate.Serialno}";
var schedule_push = await _serviceEtcd.GetValAsync(key).ConfigureAwait(false);

if (string.IsNullOrWhiteSpace(schedule_push))
@@ -84,7 +216,7 @@ namespace HealthMonitor.Service.Resolver
var ttl = (long)timeUntilNextRun.TotalSeconds;
var data = new
{
imei = phr.Serialno,
imei = heartRate.Serialno,
create_time = now.ToString("yyyy-MM-dd HH:mm:ss"),
ttl,
next_run_time = nextRunTime.ToString("yyyy-MM-dd HH:mm:ss")
@@ -99,7 +231,7 @@ namespace HealthMonitor.Service.Resolver
DateTime now = DateTime.Now;
var rand=new Random();
var pushSec = rand.Next(59);
int pushMin= int.TryParse(phr.Serialno.AsSpan(phr.Serialno.Length - 1), out pushMin) ? pushMin : 10;
int pushMin= int.TryParse(heartRate.Serialno.AsSpan(heartRate.Serialno.Length - 1), out pushMin) ? pushMin : 10;
// 计算距离下一个$interval天后的8点的时间间隔
DateTime nextRunTime = new DateTime(now.Year, now.Month, now.Day, 18, pushMin, pushSec).AddDays(interval);
TimeSpan timeUntilNextRun = nextRunTime - now;
@@ -114,7 +246,7 @@ namespace HealthMonitor.Service.Resolver
var ttl =(long)timeUntilNextRun.TotalSeconds;
var data = new
{
imei = phr.Serialno,
imei = heartRate.Serialno,
create_time = now.ToString("yyyy-MM-dd HH:mm:ss"),
ttl,
next_run_time = nextRunTime.ToString("yyyy-MM-dd HH:mm:ss")
@@ -128,7 +260,20 @@ namespace HealthMonitor.Service.Resolver
#endregion
}



private async Task SaveAndPushFetalHeartRateAsync(HisGpsHeartRate heartRate, int upperAlarmThreshold, int lowerAlarmThreshold, double avgPhr)
{
var commonPHR = await _serviceTDengine.InitPregnancyCommonHeartRateModeAsync(heartRate.Serialno);
if (commonPHR != null)
{
// 保存到TDengine数据库
await _serviceTDengine.InsertAsync<PregnancyCommonHeartRateModel>("hm_pchr", commonPHR);
// 计算胎心=孕妇心率*系数
var fetalHeartRate = SafeType.SafeInt(avgPhr * commonPHR?.StatModeAvgFprCoefficient!);
var sampleTime = DateTimeUtil.ConvertToTimeStamp(DateTime.Now).ToString();
var isAbnormal = fetalHeartRate > upperAlarmThreshold ? 1 : (fetalHeartRate < lowerAlarmThreshold ? 2 : 0);
// 推送到api/v1/open/OpenIot/SetFetalHeartRateConfig
await _serviceIotApi.SetFetalHeartRateConfig(heartRate.Serialno, fetalHeartRate, sampleTime, isAbnormal);
}
}
}
}

+ 20
- 18
HealthMonitor.WebApi/Controllers/HealthMonitor/HmBloodPressController.cs Bestand weergeven

@@ -97,25 +97,27 @@ namespace HealthMonitor.WebApi.Controllers.HealthMonitor
//await _serviceTDengine.InsertAsync<FetalHeartRateModel>("hm_fhr", test);
//var first = _serviceTDengine.GetFirst();

var test2 = new PregnancyHeartRateModel()
{
Timestamp = DateTime.Now,
CreateTime = DateTime.Now,
PregnancyHeartRate = 120,
PregnancyHeartRateId= Guid.NewGuid().ToString("D"),
IsDisplay = false,
Method = 1,
PersonId = Guid.NewGuid().ToString("D"),
MessageId = Guid.NewGuid().ToString("D"),
SerialNumber = "864144050568123",
DeviceKey = Guid.NewGuid().ToString("D"),
SerialTailNumber = "23",
LastUpdate = DateTime.Now,
};
await _serviceTDengine.InsertAsync<PregnancyHeartRateModel>("hm_phr", test2);
//var test2 = new PregnancyHeartRateModel()
//{
// Timestamp = DateTime.Now,
// CreateTime = DateTime.Now,
// PregnancyHeartRate = 120,
// PregnancyHeartRateId= Guid.NewGuid().ToString("D"),
// IsDisplay = false,
// Method = 1,
// PersonId = Guid.NewGuid().ToString("D"),
// MessageId = Guid.NewGuid().ToString("D"),
// SerialNumber = "864144050568123",
// DeviceKey = Guid.NewGuid().ToString("D"),
// SerialTailNumber = "23",
// LastUpdate = DateTime.Now,
//};
//await _serviceTDengine.InsertAsync<PregnancyHeartRateModel>("hm_phr", test2);

var first = await _serviceTDengine.GetBySerialNoAsync<PregnancyHeartRateModel>("864144050568123");
return Ok(first);
//var first = await _serviceTDengine.GetBySerialNoAsync<PregnancyHeartRateModel>("864002051169655");
//return Ok(first);
await _serviceTDengine.InitPregnancyCommonHeartRateModeAsync("864002051169655");
return Ok("first");
}
}
}

+ 1
- 0
HealthMonitor.WebApi/Program.cs Bestand weergeven

@@ -173,6 +173,7 @@ namespace HealthMonitor.WebApi
builder.Services
.AddSingleton<PersonCacheManager>()
.AddSingleton<DeviceCacheManager>()
.AddSingleton<FhrPhrMapCacheManager>()
.AddSingleton<BloodPressReferenceValueCacheManager>();

#endregion


+ 4
- 2
HealthMonitor.WebApi/Worker.cs Bestand weergeven

@@ -134,17 +134,19 @@ namespace HealthMonitor.WebApi
if (commonPHR == null)
{
// 建模中
var flag= await _serviceIotApi.SetFetalHeartRateConfig(imeiDel);
var flag= await _serviceIotApi.SetFetalConfig(imeiDel);
_logger.LogInformation($"{imeiDel} 建模建模中");
}
else
{
// 建模完成
var flag = await _serviceIotApi.SetFetalHeartRateConfig(imeiDel,1,commonPHR.MaxValue,commonPHR.MinValue);
var flag = await _serviceIotApi.SetFetalConfig(imeiDel,1,commonPHR.MaxValue,commonPHR.MinValue);
_logger.LogInformation($"{imeiDel} 建模完成");
// 保存到TDengine数据库
await _serviceTDengine.InsertAsync<PregnancyCommonHeartRateModel>("hm_pchr", commonPHR);
_logger.LogInformation($"保存TDengine完成");

//
}

#region 注册定时下发


Laden…
Annuleren
Opslaan