@@ -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")] | [SqlSugar.SugarColumn(ColumnName = "stat_end_time")] | ||||
public DateTime StatEndTime { get; set; } | 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")] | [JsonProperty("remark")] | ||||
[SqlSugar.SugarColumn(ColumnName = "remark")] | [SqlSugar.SugarColumn(ColumnName = "remark")] | ||||
public string Remark { get; set; } = default!; | public string Remark { get; set; } = default!; | ||||
@@ -255,57 +255,63 @@ namespace HealthMonitor.Service.Biz | |||||
#endregion | #endregion | ||||
#region 平台下发胎心监测参数 | #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 | try | ||||
{ | { | ||||
#region 读取缓存 | #region 读取缓存 | ||||
// db7.HashGet("TELPO#GPSDEVICE_WATCH_CONFIG_HASH","861281060086083_0067") | // db7.HashGet("TELPO#GPSDEVICE_WATCH_CONFIG_HASH","861281060086083_0067") | ||||
var watchConfig = await _deviceCacheMgr.GetGpsDeviceWatchConfigCacheObjectBySerialNoAsync(serialno, "0067"); | var watchConfig = await _deviceCacheMgr.GetGpsDeviceWatchConfigCacheObjectBySerialNoAsync(serialno, "0067"); | ||||
if (watchConfig==null) | if (watchConfig == null) | ||||
{ | { | ||||
return false; | return false; | ||||
} | } | ||||
#endregion | #endregion | ||||
#region 获取B端 Token | #region 获取B端 Token | ||||
var getTokenUrl = $"{_configService.IotAuth}/getAccessToken2"; | string tokenAuthData = await getAccessToken2(serialno).ConfigureAwait(false); | ||||
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; | |||||
if (tokenAuthData == null) | if (tokenAuthData == null) | ||||
{ | { | ||||
return false; | return false; | ||||
} | } | ||||
#endregion | #endregion | ||||
_logger.LogInformation($" SetFetalConfig Token TelpoManufactorId: {tokenAuthData}"); | |||||
#region 发送到B端 | #region 发送到B端 | ||||
List<KeyValuePair<string, string>> headers = new() | List<KeyValuePair<string, string>> headers = new() | ||||
{ | { | ||||
new KeyValuePair<string, string>("TelpoManufactorId", tokenAuthData) | new KeyValuePair<string, string>("AccessToken", tokenAuthData) | ||||
}; | }; | ||||
var data = new | var data = new | ||||
{ | { | ||||
imeis = new string[] { serialno }, | imeis = new string[] { serialno }, | ||||
enabled = (int)watchConfig["fetalParamters"]!["enabled"]!, | enabled = (int)watchConfig["enabled"]!, | ||||
triggerHighFreqHigh = maxValue == 0 ? (int)watchConfig["fetalParamters"]!["triggerHighFreqHigh"]!:maxValue, | interval= (int)watchConfig["interval"]!, | ||||
triggerLowFreqLow = minValue == 0 ? (int)watchConfig["fetalParamters"]!["triggerHighFreqLow"]! : minValue, | triggerHighFreqHigh = maxValue == 0 ? (int)watchConfig["triggerHighFreqHigh"]! : maxValue, | ||||
highFreqSampleTimes = (int)watchConfig["fetalParamters"]!["highFreqSampleTimes"]!, | triggerHighFreqLow = minValue == 0 ? (int)watchConfig["triggerHighFreqLow"]! : minValue, | ||||
highFreqSampleInterval = (int)watchConfig["fetalParamters"]!["highFreqSampleInterval"]!, | highFreqSampleTimes = (int)watchConfig["highFreqSampleTimes"]!, | ||||
stopHighFreqSampleCount = (int)watchConfig["fetalParamters"]!["stopHighFreqSampleCount"]!, | highFreqSampleInterval = (int)watchConfig["highFreqSampleInterval"]!, | ||||
stopHighFreqSampleCount = (int)watchConfig["stopHighFreqSampleCount"]!, | |||||
mode = modeStatus, | mode = modeStatus, | ||||
edoc = watchConfig["fetalParamters"]!["EDOC"]!, | edoc = watchConfig["EDOC"]!, | ||||
vibrateEnabled = (int)watchConfig["fetalParamters"]!["vibrateEnabled"]!, | vibrateEnabled = (int)watchConfig["vibrateEnabled"]!, | ||||
lcdEnabled= (int)watchConfig["fetalParamters"]!["lcdEnabled"]! | 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); | var res = await _httpHelper.HttpToPostAsync(setUrl, data, headers).ConfigureAwait(false); | ||||
_logger.LogInformation($"{setUrl} 响应 {res}"); | |||||
var resJToken = JsonConvert.DeserializeObject(res ?? string.Empty) as JToken; | var resJToken = JsonConvert.DeserializeObject(res ?? string.Empty) as JToken; | ||||
return resJToken?["message"]?.ToString().Equals("ok") ?? false; | return resJToken?["message"]?.ToString().Equals("ok") ?? false; | ||||
//response.Flag = 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) | 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; | 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 | #endregion | ||||
} | } | ||||
@@ -24,6 +24,7 @@ using System.Xml.Linq; | |||||
using TDengineDriver; | using TDengineDriver; | ||||
using TDengineDriver.Impl; | using TDengineDriver.Impl; | ||||
using TDengineTMQ; | using TDengineTMQ; | ||||
using HealthMonitor.Service.Cache; | |||||
namespace HealthMonitor.Service.Biz.db | namespace HealthMonitor.Service.Biz.db | ||||
{ | { | ||||
@@ -34,15 +35,20 @@ namespace HealthMonitor.Service.Biz.db | |||||
private readonly HttpHelper _httpHelper=default!; | private readonly HttpHelper _httpHelper=default!; | ||||
private readonly TDengineServiceConfig _configTDengineService; | private readonly TDengineServiceConfig _configTDengineService; | ||||
private readonly SqlSugarClient _clientSqlSugar; | private readonly SqlSugarClient _clientSqlSugar; | ||||
private readonly FhrPhrMapCacheManager _mgrFhrPhrMapCache; | |||||
private readonly DeviceCacheManager _deviceCacheMgr; | |||||
public TDengineService(ILogger<TDengineService> logger, | public TDengineService(ILogger<TDengineService> logger, | ||||
IOptions<TDengineServiceConfig> configTDengineService, | IOptions<TDengineServiceConfig> configTDengineService, | ||||
HttpHelper httpHelper | HttpHelper httpHelper, | ||||
FhrPhrMapCacheManager fhrPhrMapCacheManager, DeviceCacheManager deviceCacheMgr | |||||
) | ) | ||||
{ | { | ||||
_logger = logger; | _logger = logger; | ||||
_configTDengineService = configTDengineService.Value; | _configTDengineService = configTDengineService.Value; | ||||
_httpHelper = httpHelper; | _httpHelper = httpHelper; | ||||
_mgrFhrPhrMapCache = fhrPhrMapCacheManager; | |||||
_deviceCacheMgr = deviceCacheMgr; | |||||
_clientSqlSugar = new SqlSugarClient(new ConnectionConfig() | _clientSqlSugar = new SqlSugarClient(new ConnectionConfig() | ||||
{ | { | ||||
DbType = SqlSugar.DbType.TDengine, | DbType = SqlSugar.DbType.TDengine, | ||||
@@ -756,6 +762,40 @@ namespace HealthMonitor.Service.Biz.db | |||||
.OrderByDescending(timestampLambda).ToListAsync(); | .OrderByDescending(timestampLambda).ToListAsync(); | ||||
return res; | 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 | #endregion | ||||
#region 胎心算法 | #region 胎心算法 | ||||
@@ -770,12 +810,12 @@ namespace HealthMonitor.Service.Biz.db | |||||
var tableName = typeof(PregnancyHeartRateModel) | var tableName = typeof(PregnancyHeartRateModel) | ||||
.GetCustomAttribute<STableAttribute>()? | .GetCustomAttribute<STableAttribute>()? | ||||
.STableName; | .STableName; | ||||
var daysAgo = DateTime.Now.AddDays(-days); | |||||
var res = await _clientSqlSugar | var res = await _clientSqlSugar | ||||
.Queryable<PregnancyHeartRateModel>() | .Queryable<PregnancyHeartRateModel>() | ||||
.AS(tableName) | .AS(tableName) | ||||
.Where(i=>i.SerialNumber.Equals(serialNo)) | .Where(i=>i.SerialNumber.Equals(serialNo)) | ||||
.Where(i => i.Timestamp > DateTime.Now.AddDays(-days)) | .Where(i => i.Timestamp > daysAgo) | ||||
//.OrderByDescending(i => i.PregnancyHeartRate) | //.OrderByDescending(i => i.PregnancyHeartRate) | ||||
.Select(i =>i.PregnancyHeartRate) | .Select(i =>i.PregnancyHeartRate) | ||||
.ToListAsync(); | .ToListAsync(); | ||||
@@ -846,11 +886,12 @@ namespace HealthMonitor.Service.Biz.db | |||||
.GetCustomAttribute<STableAttribute>()? | .GetCustomAttribute<STableAttribute>()? | ||||
.STableName; | .STableName; | ||||
var daysAgo = DateTime.Now.AddDays(-days); | |||||
var collection = await _clientSqlSugar | var collection = await _clientSqlSugar | ||||
.Queryable<PregnancyHeartRateModel>() | .Queryable<PregnancyHeartRateModel>() | ||||
.AS(tableName) | .AS(tableName) | ||||
.Where(i => i.SerialNumber.Equals(serialNo)) | .Where(i => i.SerialNumber.Equals(serialNo)) | ||||
.Where(i => i.Timestamp > DateTime.Now.AddDays(-days)) | .Where(i => i.Timestamp > daysAgo) | ||||
.OrderByDescending(i => i.Timestamp) | .OrderByDescending(i => i.Timestamp) | ||||
.ToArrayAsync(); | .ToArrayAsync(); | ||||
@@ -925,21 +966,61 @@ namespace HealthMonitor.Service.Biz.db | |||||
Console.WriteLine("新数据集: " + string.Join(", ", closestToModeData)); | Console.WriteLine("新数据集: " + string.Join(", ", closestToModeData)); | ||||
Console.WriteLine("新数据集的数量: " + closestToModeData.Count); | 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, | Timestamp = DateTime.Now, | ||||
PersonId = collection.First().DeviceKey, | PersonId = collection.First().DeviceKey, | ||||
DeviceKey = collection.First().DeviceKey, | DeviceKey = collection.First().DeviceKey, | ||||
SerialNumber = collection.First().SerialNumber, | SerialNumber = collection.First().SerialNumber, | ||||
Mode = mode, | Mode = mode, | ||||
Percentage= percentage, | Percentage = percentage, | ||||
MaxValue= closestToModeData.Max(), | MaxValue = closestToModeData.Max(), | ||||
MinValue= closestToModeData.Min(), | MinValue = closestToModeData.Min(), | ||||
OriginalMaxValue=res.Max(), | OriginalMaxValue = res.Max(), | ||||
OriginalMinValue= res.Min(), | OriginalMinValue = res.Min(), | ||||
CreateTime = DateTime.Now, | CreateTime = DateTime.Now, | ||||
StatStartTime = collection.OrderBy(i=>i.Timestamp).Select(i=>i.Timestamp).First(), | StatStartTime = collection.OrderBy(i => i.Timestamp).Select(i => i.Timestamp).First(), | ||||
StatEndTime= collection.OrderBy(i => i.Timestamp).Select(i => i.Timestamp).Last(), | 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.Linq; | ||||
using System.Text; | using System.Text; | ||||
using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
using TDengineTMQ; | |||||
using System.Web; | |||||
using HealthMonitor.Model.Service.Mapper; | |||||
namespace HealthMonitor.Service.Cache | namespace HealthMonitor.Service.Cache | ||||
{ | { | ||||
@@ -15,7 +18,7 @@ namespace HealthMonitor.Service.Cache | |||||
private const string CACHE_KEY_GPSDEVICE_WATCH_CONFIG = "#GPSDEVICE_WATCH_CONFIG_HASH"; | private const string CACHE_KEY_GPSDEVICE_WATCH_CONFIG = "#GPSDEVICE_WATCH_CONFIG_HASH"; | ||||
public DeviceCacheManager(ILogger<DeviceCacheManager> logger) | public DeviceCacheManager(ILogger<DeviceCacheManager> logger) | ||||
{ | { | ||||
_logger = logger; | _logger = logger; | ||||
} | } | ||||
@@ -37,7 +40,7 @@ namespace HealthMonitor.Service.Cache | |||||
try | try | ||||
{ | { | ||||
var config = await RedisHelperDb7.HGetAsync(CACHE_KEY_GPSDEVICE_WATCH_CONFIG, $"{sn}_{bizCode}").ConfigureAwait(false); | var config = await RedisHelperDb7.HGetAsync(CACHE_KEY_GPSDEVICE_WATCH_CONFIG, $"{sn}_{bizCode}").ConfigureAwait(false); | ||||
if (config == null) return null; | if (config == null) return null; | ||||
return (JObject)JsonConvert.DeserializeObject(config)!; | return (JObject)JsonConvert.DeserializeObject(config)!; | ||||
} | } | ||||
@@ -48,5 +51,57 @@ namespace HealthMonitor.Service.Cache | |||||
return null; | 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; | ||||
using HealthMonitor.Common.helper; | 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.Etcd; | ||||
using HealthMonitor.Service.Resolver.Interface; | using HealthMonitor.Service.Resolver.Interface; | ||||
using HealthMonitor.Service.Sub; | using HealthMonitor.Service.Sub; | ||||
using HealthMonitor.Service.Sub.Topic.Model; | using HealthMonitor.Service.Sub.Topic.Model; | ||||
using Microsoft.EntityFrameworkCore.Metadata; | |||||
using Microsoft.Extensions.Logging; | using Microsoft.Extensions.Logging; | ||||
using Newtonsoft.Json; | using Newtonsoft.Json; | ||||
using System; | using System; | ||||
@@ -18,17 +23,27 @@ namespace HealthMonitor.Service.Resolver | |||||
public class PregnancyHeartRateResolver : IResolver | public class PregnancyHeartRateResolver : IResolver | ||||
{ | { | ||||
private readonly ILogger<PregnancyHeartRateResolver> _logger; | 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<string> _messageId = new(); | ||||
private readonly AsyncLocal<HisGpsHeartRate> _msgData = new(); | private readonly AsyncLocal<HisGpsHeartRate> _msgData = new(); | ||||
private readonly HttpHelper _httpHelper = default!; | private readonly HttpHelper _httpHelper = default!; | ||||
private readonly EtcdService _serviceEtcd; | 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; | _logger = logger; | ||||
_httpHelper = httpHelper; | _httpHelper = httpHelper; | ||||
_serviceEtcd = serviceEtcd; | _serviceEtcd = serviceEtcd; | ||||
_serviceTDengine = serviceDengine; | |||||
_deviceCacheMgr = deviceCacheMgr; | |||||
_serviceIotApi = iotApiService; | |||||
} | } | ||||
public void SetResolveInfo(PackageMsgModel msg) | public void SetResolveInfo(PackageMsgModel msg) | ||||
@@ -56,9 +71,126 @@ namespace HealthMonitor.Service.Resolver | |||||
{ | { | ||||
//throw new NotImplementedException(); | //throw new NotImplementedException(); | ||||
var messageId = _messageId.Value; | 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 定时下发触发器 | #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); | var schedule_push = await _serviceEtcd.GetValAsync(key).ConfigureAwait(false); | ||||
if (string.IsNullOrWhiteSpace(schedule_push)) | if (string.IsNullOrWhiteSpace(schedule_push)) | ||||
@@ -84,7 +216,7 @@ namespace HealthMonitor.Service.Resolver | |||||
var ttl = (long)timeUntilNextRun.TotalSeconds; | var ttl = (long)timeUntilNextRun.TotalSeconds; | ||||
var data = new | var data = new | ||||
{ | { | ||||
imei = phr.Serialno, | imei = heartRate.Serialno, | ||||
create_time = now.ToString("yyyy-MM-dd HH:mm:ss"), | create_time = now.ToString("yyyy-MM-dd HH:mm:ss"), | ||||
ttl, | ttl, | ||||
next_run_time = nextRunTime.ToString("yyyy-MM-dd HH:mm:ss") | next_run_time = nextRunTime.ToString("yyyy-MM-dd HH:mm:ss") | ||||
@@ -99,7 +231,7 @@ namespace HealthMonitor.Service.Resolver | |||||
DateTime now = DateTime.Now; | DateTime now = DateTime.Now; | ||||
var rand=new Random(); | var rand=new Random(); | ||||
var pushSec = rand.Next(59); | 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点的时间间隔 | // 计算距离下一个$interval天后的8点的时间间隔 | ||||
DateTime nextRunTime = new DateTime(now.Year, now.Month, now.Day, 18, pushMin, pushSec).AddDays(interval); | DateTime nextRunTime = new DateTime(now.Year, now.Month, now.Day, 18, pushMin, pushSec).AddDays(interval); | ||||
TimeSpan timeUntilNextRun = nextRunTime - now; | TimeSpan timeUntilNextRun = nextRunTime - now; | ||||
@@ -114,7 +246,7 @@ namespace HealthMonitor.Service.Resolver | |||||
var ttl =(long)timeUntilNextRun.TotalSeconds; | var ttl =(long)timeUntilNextRun.TotalSeconds; | ||||
var data = new | var data = new | ||||
{ | { | ||||
imei = phr.Serialno, | imei = heartRate.Serialno, | ||||
create_time = now.ToString("yyyy-MM-dd HH:mm:ss"), | create_time = now.ToString("yyyy-MM-dd HH:mm:ss"), | ||||
ttl, | ttl, | ||||
next_run_time = nextRunTime.ToString("yyyy-MM-dd HH:mm:ss") | next_run_time = nextRunTime.ToString("yyyy-MM-dd HH:mm:ss") | ||||
@@ -128,7 +260,20 @@ namespace HealthMonitor.Service.Resolver | |||||
#endregion | #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); | //await _serviceTDengine.InsertAsync<FetalHeartRateModel>("hm_fhr", test); | ||||
//var first = _serviceTDengine.GetFirst(); | //var first = _serviceTDengine.GetFirst(); | ||||
var test2 = new PregnancyHeartRateModel() | //var test2 = new PregnancyHeartRateModel() | ||||
{ | //{ | ||||
Timestamp = DateTime.Now, | // Timestamp = DateTime.Now, | ||||
CreateTime = DateTime.Now, | // CreateTime = DateTime.Now, | ||||
PregnancyHeartRate = 120, | // PregnancyHeartRate = 120, | ||||
PregnancyHeartRateId= Guid.NewGuid().ToString("D"), | // PregnancyHeartRateId= Guid.NewGuid().ToString("D"), | ||||
IsDisplay = false, | // IsDisplay = false, | ||||
Method = 1, | // Method = 1, | ||||
PersonId = Guid.NewGuid().ToString("D"), | // PersonId = Guid.NewGuid().ToString("D"), | ||||
MessageId = Guid.NewGuid().ToString("D"), | // MessageId = Guid.NewGuid().ToString("D"), | ||||
SerialNumber = "864144050568123", | // SerialNumber = "864144050568123", | ||||
DeviceKey = Guid.NewGuid().ToString("D"), | // DeviceKey = Guid.NewGuid().ToString("D"), | ||||
SerialTailNumber = "23", | // SerialTailNumber = "23", | ||||
LastUpdate = DateTime.Now, | // LastUpdate = DateTime.Now, | ||||
}; | //}; | ||||
await _serviceTDengine.InsertAsync<PregnancyHeartRateModel>("hm_phr", test2); | //await _serviceTDengine.InsertAsync<PregnancyHeartRateModel>("hm_phr", test2); | ||||
var first = await _serviceTDengine.GetBySerialNoAsync<PregnancyHeartRateModel>("864144050568123"); | //var first = await _serviceTDengine.GetBySerialNoAsync<PregnancyHeartRateModel>("864002051169655"); | ||||
return Ok(first); | //return Ok(first); | ||||
await _serviceTDengine.InitPregnancyCommonHeartRateModeAsync("864002051169655"); | |||||
return Ok("first"); | |||||
} | } | ||||
} | } | ||||
} | } |
@@ -173,6 +173,7 @@ namespace HealthMonitor.WebApi | |||||
builder.Services | builder.Services | ||||
.AddSingleton<PersonCacheManager>() | .AddSingleton<PersonCacheManager>() | ||||
.AddSingleton<DeviceCacheManager>() | .AddSingleton<DeviceCacheManager>() | ||||
.AddSingleton<FhrPhrMapCacheManager>() | |||||
.AddSingleton<BloodPressReferenceValueCacheManager>(); | .AddSingleton<BloodPressReferenceValueCacheManager>(); | ||||
#endregion | #endregion | ||||
@@ -134,17 +134,19 @@ namespace HealthMonitor.WebApi | |||||
if (commonPHR == null) | if (commonPHR == null) | ||||
{ | { | ||||
// 建模中 | // 建模中 | ||||
var flag= await _serviceIotApi.SetFetalHeartRateConfig(imeiDel); | var flag= await _serviceIotApi.SetFetalConfig(imeiDel); | ||||
_logger.LogInformation($"{imeiDel} 建模建模中"); | _logger.LogInformation($"{imeiDel} 建模建模中"); | ||||
} | } | ||||
else | 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} 建模完成"); | _logger.LogInformation($"{imeiDel} 建模完成"); | ||||
// 保存到TDengine数据库 | // 保存到TDengine数据库 | ||||
await _serviceTDengine.InsertAsync<PregnancyCommonHeartRateModel>("hm_pchr", commonPHR); | await _serviceTDengine.InsertAsync<PregnancyCommonHeartRateModel>("hm_pchr", commonPHR); | ||||
_logger.LogInformation($"保存TDengine完成"); | _logger.LogInformation($"保存TDengine完成"); | ||||
// | |||||
} | } | ||||
#region 注册定时下发 | #region 注册定时下发 | ||||