diff --git a/HealthMonitor.Model/Cache/FhrPhrMap.cs b/HealthMonitor.Model/Cache/FhrPhrMap.cs
new file mode 100644
index 0000000..19e34f9
--- /dev/null
+++ b/HealthMonitor.Model/Cache/FhrPhrMap.cs
@@ -0,0 +1,34 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace HealthMonitor.Model.Cache
+{
+ ///
+ /// 胎心与孕妇心率映射
+ ///
+ 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;
+ }
+ }
+}
diff --git a/HealthMonitor.Model/Service/Mapper/PregnancyCommonHeartRateModel.cs b/HealthMonitor.Model/Service/Mapper/PregnancyCommonHeartRateModel.cs
index fec844e..bc128b6 100644
--- a/HealthMonitor.Model/Service/Mapper/PregnancyCommonHeartRateModel.cs
+++ b/HealthMonitor.Model/Service/Mapper/PregnancyCommonHeartRateModel.cs
@@ -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!;
diff --git a/HealthMonitor.Service/Biz/IotApiService.cs b/HealthMonitor.Service/Biz/IotApiService.cs
index 6803934..8b7e297 100644
--- a/HealthMonitor.Service/Biz/IotApiService.cs
+++ b/HealthMonitor.Service/Biz/IotApiService.cs
@@ -255,57 +255,63 @@ namespace HealthMonitor.Service.Biz
#endregion
#region 平台下发胎心监测参数
- public async Task SetFetalHeartRateConfig(string serialno, int modeStatus=0, int maxValue=0, int minValue = 0)
+ ///
+ /// 胎心设置
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public async Task 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> headers = new()
{
- new KeyValuePair("TelpoManufactorId", tokenAuthData)
+ new KeyValuePair("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;
+ }
+ }
+
+
+ ///
+ /// 下发胎心数据
+ ///
+ ///
+ /// 心率值
+ /// 检测时间(时间戳)
+ /// 0 //是否异常 0 表示正常;1表示偏高;2表示偏低
+ ///
+ public async Task 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> headers = new()
+ {
+ new KeyValuePair("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 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
}
diff --git a/HealthMonitor.Service/Biz/db/TDengineService.cs b/HealthMonitor.Service/Biz/db/TDengineService.cs
index c3ffcec..32ae083 100644
--- a/HealthMonitor.Service/Biz/db/TDengineService.cs
+++ b/HealthMonitor.Service/Biz/db/TDengineService.cs
@@ -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 logger,
IOptions 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> GetBySerialNoAsync(string serialNo, int days) where T : class
+ {
+ var tableName = typeof(T)
+ .GetCustomAttribute()?
+ .STableName;
+
+ // 创建表示 Timestamp 属性的表达式
+ var parameter = Expression.Parameter(typeof(T), "x");
+ var timestampProperty = Expression.Property(parameter, "Timestamp");
+ var timestampLambda = Expression.Lambda>(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>(combinedExpression, parameter);
+
+ var res = await _clientSqlSugar
+ .Queryable()
+ .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()?
.STableName;
-
+ var daysAgo = DateTime.Now.AddDays(-days);
var res = await _clientSqlSugar
.Queryable()
.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()?
.STableName;
+ var daysAgo = DateTime.Now.AddDays(-days);
var collection = await _clientSqlSugar
.Queryable()
.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)
};
}
diff --git a/HealthMonitor.Service/Cache/DeviceCacheManager.cs b/HealthMonitor.Service/Cache/DeviceCacheManager.cs
index d7b34b7..8b3c533 100644
--- a/HealthMonitor.Service/Cache/DeviceCacheManager.cs
+++ b/HealthMonitor.Service/Cache/DeviceCacheManager.cs
@@ -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 logger)
+ public DeviceCacheManager(ILogger 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 心率状态
+ ///
+ /// 读取高频状态
+ ///
+ ///
+ ///
+ public async Task 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(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
}
}
diff --git a/HealthMonitor.Service/Cache/FhrPhrMapCacheManager.cs b/HealthMonitor.Service/Cache/FhrPhrMapCacheManager.cs
new file mode 100644
index 0000000..d34a573
--- /dev/null
+++ b/HealthMonitor.Service/Cache/FhrPhrMapCacheManager.cs
@@ -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 _logger;
+ public FhrPhrMapCacheManager(ILogger logger )
+ {
+ _logger = logger;
+ }
+ public List GetHeartRatesMap()
+ {
+ return new List
+ {
+ 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})
+ };
+ }
+ }
+}
diff --git a/HealthMonitor.Service/Resolver/PregnancyHeartRateResolver.cs b/HealthMonitor.Service/Resolver/PregnancyHeartRateResolver.cs
index 2dbdf75..75436bf 100644
--- a/HealthMonitor.Service/Resolver/PregnancyHeartRateResolver.cs
+++ b/HealthMonitor.Service/Resolver/PregnancyHeartRateResolver.cs
@@ -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 _logger;
+ private readonly TDengineService _serviceTDengine;
+
+ private readonly DeviceCacheManager _deviceCacheMgr;
+ private readonly IotApiService _serviceIotApi;
+
+
private readonly AsyncLocal _messageId = new();
private readonly AsyncLocal _msgData = new();
private readonly HttpHelper _httpHelper = default!;
private readonly EtcdService _serviceEtcd;
- public PregnancyHeartRateResolver(ILogger logger, HttpHelper httpHelper, EtcdService serviceEtcd)
+ public PregnancyHeartRateResolver(ILogger 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(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("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);
+ }
+ }
}
}
diff --git a/HealthMonitor.WebApi/Controllers/HealthMonitor/HmBloodPressController.cs b/HealthMonitor.WebApi/Controllers/HealthMonitor/HmBloodPressController.cs
index affcb1e..fe00cfc 100644
--- a/HealthMonitor.WebApi/Controllers/HealthMonitor/HmBloodPressController.cs
+++ b/HealthMonitor.WebApi/Controllers/HealthMonitor/HmBloodPressController.cs
@@ -97,25 +97,27 @@ namespace HealthMonitor.WebApi.Controllers.HealthMonitor
//await _serviceTDengine.InsertAsync("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("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("hm_phr", test2);
- var first = await _serviceTDengine.GetBySerialNoAsync("864144050568123");
- return Ok(first);
+ //var first = await _serviceTDengine.GetBySerialNoAsync("864002051169655");
+ //return Ok(first);
+ await _serviceTDengine.InitPregnancyCommonHeartRateModeAsync("864002051169655");
+ return Ok("first");
}
}
}
diff --git a/HealthMonitor.WebApi/Program.cs b/HealthMonitor.WebApi/Program.cs
index b7c072a..482271e 100644
--- a/HealthMonitor.WebApi/Program.cs
+++ b/HealthMonitor.WebApi/Program.cs
@@ -173,6 +173,7 @@ namespace HealthMonitor.WebApi
builder.Services
.AddSingleton()
.AddSingleton()
+ .AddSingleton()
.AddSingleton();
#endregion
diff --git a/HealthMonitor.WebApi/Worker.cs b/HealthMonitor.WebApi/Worker.cs
index 5bd8680..83ca27a 100644
--- a/HealthMonitor.WebApi/Worker.cs
+++ b/HealthMonitor.WebApi/Worker.cs
@@ -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("hm_pchr", commonPHR);
_logger.LogInformation($"保存TDengine完成");
+
+ //
}
#region 注册定时下发