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 注册定时下发