@@ -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; | |||
} | |||
} | |||
} |
@@ -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!; | |||
@@ -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 | |||
} | |||
@@ -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) | |||
}; | |||
} | |||
@@ -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 | |||
} | |||
} |
@@ -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}) | |||
}; | |||
} | |||
} | |||
} |
@@ -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); | |||
} | |||
} | |||
} | |||
} |
@@ -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"); | |||
} | |||
} | |||
} |
@@ -173,6 +173,7 @@ namespace HealthMonitor.WebApi | |||
builder.Services | |||
.AddSingleton<PersonCacheManager>() | |||
.AddSingleton<DeviceCacheManager>() | |||
.AddSingleton<FhrPhrMapCacheManager>() | |||
.AddSingleton<BloodPressReferenceValueCacheManager>(); | |||
#endregion | |||
@@ -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 注册定时下发 | |||