diff --git a/HealthMonitor.Service/Biz/db/TDengineService.cs b/HealthMonitor.Service/Biz/db/TDengineService.cs index e3038a4..042958e 100644 --- a/HealthMonitor.Service/Biz/db/TDengineService.cs +++ b/HealthMonitor.Service/Biz/db/TDengineService.cs @@ -28,6 +28,8 @@ using HealthMonitor.Service.Cache; using System.Text.RegularExpressions; using Etcdserverpb; using static Microsoft.EntityFrameworkCore.DbLoggerCategory; +using HealthMonitor.Core.Pipeline; +using static Dm.net.buffer.ByteArrayBuffer; namespace HealthMonitor.Service.Biz.db { @@ -1007,7 +1009,14 @@ namespace HealthMonitor.Service.Biz.db //} - + /// + /// 建模 + /// + /// + /// + /// + /// + /// public async Task InitPregnancyCommonHeartRateModeAsync(string serialNo, int days = 7, int percentage = 90, int highFreqSampleInterval=0) { var tableName = typeof(PregnancyHeartRateModel) @@ -1144,7 +1153,7 @@ namespace HealthMonitor.Service.Biz.db maxValue= maxValue < 100 ? 100 : maxValue; - // 20-45周之间 + // 12-45周之间 if (pregnancyWeek >= 12 && pregnancyWeek <= 45) { var map = fhrMap @@ -1314,82 +1323,60 @@ namespace HealthMonitor.Service.Biz.db } + + /// - /// 获取孕妇心率众数 + /// 胎心算法 + /// 1、增量系数 = (正常胎心最大值 - 正常胎心最小值) / (阀值最大值 - 阀值最小值) + /// 2、胎心值 = 正常胎心最小值 + 增量系数 * (心率值 - 阀值最小值) /// /// - /// + /// /// - //public async Task GetPregnancyHeartRateModeAsync(string serialNo,int days=7) - //{ - // 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 > daysAgo) - // //.OrderByDescending(i => i.PregnancyHeartRate) - // .Select(i =>i.PregnancyHeartRate) - // .ToListAsync(); - // // 心率数据量必须30个以上才进行计算 - // if (res.Count < 30) return 0; - - // // 计算众数 - // var mode = res.GroupBy(n => n) - // .OrderByDescending(g => g.Count()) - // .First() - // .Key; + public async Task GetFetalHeartRateAsync(string serialNo,int heartrateValue) + { + var fhrMap = _mgrFhrPhrMapCache.GetHeartRatesMap(); + var watchConfig = await _deviceCacheMgr.GetGpsDeviceWatchConfigCacheObjectBySerialNoAsync(serialNo, "0067"); + if (watchConfig == null) + { + return 0; + } - // Console.WriteLine("众数是: " + mode); + var edoc = DateTimeUtil.ToDateTime(watchConfig["EDOC"]!.ToString()); + int pregnancyWeek = (DateTime.Now - edoc.AddDays(-280)).Days / 7; - // // 如果有多个众数的情况 - // var maxCount = res.GroupBy(n => n) - // .Max(g => g.Count()); + // 12-45周之间 + if (pregnancyWeek >= 12 && pregnancyWeek <= 45) + { + var map = fhrMap + .Where(i => + i.PregnancyPeriod![0] <= pregnancyWeek && + i.PregnancyPeriod[1] >= pregnancyWeek + //&&i.PregnancyHeartRateRange![0] <= mode && i.PregnancyHeartRateRange[1] >= mode + ) + .FirstOrDefault(); - // var modes = res.GroupBy(n => n) - // .Where(g => g.Count() == maxCount) - // .Select(g => g.Key) - // .ToList(); - // // 多个众数,选择最接近平均数或中位数的众数 - // if (modes.Count>1) - // { - // // 计算平均值 - // double average = res.Average(); - // Console.WriteLine("平均值是: " + average); + var NormaletalHeartRateMin = map?.FetalHeartRateRange![0]; + var NormaletalHeartRateMax = map?.FetalHeartRateRange![1]; - // // 计算中位数 - // double median; - // int count = res.Count; - // var sortedRes = res.OrderBy(n => n).ToList(); - // if (count % 2 == 0) - // { - // // 偶数个元素,取中间两个数的平均值 - // median = (sortedRes[count / 2 - 1] + sortedRes[count / 2]) / 2.0; - // } - // else - // { - // // 奇数个元素,取中间的数 - // median = sortedRes[count / 2]; - // } - // Console.WriteLine("中位数是: " + median); - // // 找出最接近平均值的众数 - // //var closestToAverage = modes.OrderBy(m => Math.Abs(m - average)).First(); - // //Console.WriteLine("最接近平均值的众数是: " + closestToAverage); + // 触发高频监测的心率上限值 + var triggerHighFreqHigh = (int)watchConfig["triggerHighFreqHigh"]!; + // 触发高频监测的心率下限值 + var triggerHighFreqLow = (int)watchConfig["triggerHighFreqLow"]!; + //增量系数 = (正常胎心最大值 - 正常胎心最小值) / (阀值最大值 - 阀值最小值) + var coefficient = (NormaletalHeartRateMax - NormaletalHeartRateMin) / (triggerHighFreqHigh - triggerHighFreqLow); - // // 找出最接近中位数的众数 - // var closestToMedian = modes.OrderBy(m => Math.Abs(m - median)).First(); - // Console.WriteLine("最接近中位数的众数是: " + closestToMedian); - // mode = closestToMedian; - // } + //胎心值 = 正常胎心最小值 + 增量系数 * (心率值 - 阀值最小值) + var fetalHeartRate = NormaletalHeartRateMin + coefficient * (heartrateValue - triggerHighFreqLow); - // return mode; - //} + _logger.LogInformation($"{serialNo} 孕周:{pregnancyWeek}, 正常胎心最大值:{NormaletalHeartRateMax},正常胎心最小值:{NormaletalHeartRateMin},阀值最大值:{triggerHighFreqHigh} ,阀值最小值:{triggerHighFreqLow}, 增量系数:{coefficient}"); + return SafeType.SafeInt(fetalHeartRate!); + } + return 0; + } #endregion } diff --git a/HealthMonitor.Service/Resolver/PregnancyHeartRateResolver.cs b/HealthMonitor.Service/Resolver/PregnancyHeartRateResolver.cs index 8517f90..bf6ef01 100644 --- a/HealthMonitor.Service/Resolver/PregnancyHeartRateResolver.cs +++ b/HealthMonitor.Service/Resolver/PregnancyHeartRateResolver.cs @@ -829,7 +829,6 @@ namespace HealthMonitor.Service.Resolver return selectedHrValue; } - /// /// 高频胎心处理 /// 1. 高频数据触发连续12个值都是正常的的高频心率处理 @@ -848,37 +847,98 @@ namespace HealthMonitor.Service.Resolver /// /// /// - private async Task SaveAndPushFetalHeartRateEndFreqHeartRateAsync(HisGpsHeartRate heartRate, PregnancyCommonHeartRateModel commonPHR, int highFreqSampleTimes, int upperAlarmThreshold, int lowerAlarmThreshold, string sampleTime, DateTime statStartTime, DateTime statEndTime) + private async Task SaveAndPushFetalHeartRateEndFreqHeartRateAsyncOld(HisGpsHeartRate heartRate, PregnancyCommonHeartRateModel commonPHR, int highFreqSampleTimes, int upperAlarmThreshold, int lowerAlarmThreshold, string sampleTime, DateTime statStartTime, DateTime statEndTime) { // 计算胎心=孕妇心率*系数 - #region 胎心系数使用基于心率与中位数对比 + #region 胎心系数使用基于心率与众数对比 var coefficient = 0f; - // 孕妇心率少于中位数,取StatMinValueFprCoefficient + /** + // 孕妇心率少于众数,取StatMinValueFprCoefficient if (heartRate.HeartRate < commonPHR!.Mode) { coefficient = commonPHR.StatMinValueFprCoefficient!; - _logger.LogInformation($"{heartRate.Serialno} 孕妇心率少于中位数,使用最小值系数 {coefficient}"); + _logger.LogInformation($"{heartRate.Serialno} 孕妇心率少于众数,使用最小值系数 {coefficient}"); } - // 孕妇心率大于中位数,取StatMaxValueFprCoefficient与StatModeAvgFprCoefficient中少的那个 + // 孕妇心率大于众数,取StatMaxValueFprCoefficient与StatModeAvgFprCoefficient中少的那个 else if (heartRate.HeartRate > commonPHR.Mode) { if (commonPHR.StatModeAvgFprCoefficient > commonPHR.StatMaxValueFprCoefficient) { coefficient = commonPHR.StatMaxValueFprCoefficient!; - _logger.LogInformation($"{heartRate.Serialno} 孕妇心率大于中位数,使用最大值系数 {coefficient}"); + _logger.LogInformation($"{heartRate.Serialno} 孕妇心率大于众数,使用最大值系数 {coefficient}"); } else { coefficient = commonPHR.StatModeAvgFprCoefficient!; - _logger.LogInformation($"{heartRate.Serialno} 孕妇心率大于中位数,使用均值系数 {coefficient}"); + _logger.LogInformation($"{heartRate.Serialno} 孕妇心率大于众数,使用均值系数 {coefficient}"); } } else { coefficient = commonPHR.StatModeAvgFprCoefficient; - _logger.LogInformation($"{heartRate.Serialno} 孕妇心率等于中位数,使用均值系数 {coefficient}"); + _logger.LogInformation($"{heartRate.Serialno} 孕妇心率等于众数,使用均值系数 {coefficient}"); + } + */ + + + ///1、高频最小值>心率,取高频最小值系数 + ///2、高频最小值 < 心率 < 众数,取众数系数与高频最小值系数较小值 + ///3、众数 < 心率 < 高频最大值,取众数系数与高频最大值系数较小值 + ///4、心率 > 高频最大值,取高频最大值系数 + + // 1、高频最小值>心率,取高频最小值系数 + if (commonPHR.MinValue > heartRate.HeartRate) + { + coefficient = commonPHR.StatMinValueFprCoefficient!; + _logger.LogInformation($"{heartRate.Serialno} 高频最小值>心率,取高频最小值系数 {coefficient}"); } + + // 2、高频最小值 < 心率 < 众数,取众数系数与高频最小值系数较小值 + if (commonPHR.MinValue < heartRate.HeartRate && heartRate.HeartRate < commonPHR.Mode) + { + if (commonPHR.StatModeAvgFprCoefficient > commonPHR.StatMinValueFprCoefficient) + { + coefficient = commonPHR.StatMinValueFprCoefficient!; + _logger.LogInformation($"{heartRate.Serialno} 高频最小值 < 心率 < 众数,平均值系数大于最小值系数,使用最小值系数 {coefficient}"); + } + else + { + coefficient = commonPHR.StatModeAvgFprCoefficient!; + _logger.LogInformation($"{heartRate.Serialno} 高频最小值 < 心率 < 众数,平均值系数小于最小值系数,使用均值系数 {coefficient}"); + } + } + + // 3、众数 < 心率 < 高频最大值,取众数系数与高频最大值系数较小值 + if (commonPHR.Mode < heartRate.HeartRate && heartRate.HeartRate < commonPHR.MaxValue) + { + if (commonPHR.StatModeAvgFprCoefficient > commonPHR.StatMaxValueFprCoefficient) + { + coefficient = commonPHR.StatMaxValueFprCoefficient!; + _logger.LogInformation($"{heartRate.Serialno} 众数 < 心率 < 高频最大值,平均值系数大于最最大值系数,使用最大值系数 {coefficient}"); + } + else + { + coefficient = commonPHR.StatModeAvgFprCoefficient!; + _logger.LogInformation($"{heartRate.Serialno} 众数 < 心率 < 高频最大值,平均值系数小于最小值系数,使用均值系数 {coefficient}"); + } + } + + // 4、心率 > 高频最大值,取高频最大值系数 + + if (heartRate.HeartRate > commonPHR.MaxValue) + { + coefficient = commonPHR.StatMaxValueFprCoefficient!; + _logger.LogInformation($"{heartRate.Serialno} 心率 > 高频最大值,取高频最大值系数 {coefficient}"); + } + + if (heartRate.HeartRate == commonPHR.Mode) + { + coefficient = commonPHR.StatModeAvgFprCoefficient!; + + _logger.LogInformation($"{heartRate.Serialno} 心率 == 众数,取均值系数 {coefficient}"); + } + #endregion var fetalHeartRate = SafeType.SafeInt(heartRate.HeartRate! * coefficient); @@ -1012,6 +1072,153 @@ namespace HealthMonitor.Service.Resolver } } + /// + /// 高频胎心处理 + /// 1. 高频数据触发连续12个值都是正常的的高频心率处理 + /// 2. 高频结束后的highFreqSampleTimes=0的高频心率处理 + /// 3. 高频结束后的在highFreqSampleTimes>0 正常心率触发的高频心率处理(常态) + /// 4. 高频结束后的时间倒序的正常心率触发的高频心率处理 + /// 5. 高频结束后计算胎心数据,防止结束后与常规心理的胎心处理过长,定时器时长highFreqSampleInterval触发的高频心率处理 + /// 高频结束后超过9分钟且不在阈值内才告警 + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + private async Task SaveAndPushFetalHeartRateEndFreqHeartRateAsync(HisGpsHeartRate heartRate, PregnancyCommonHeartRateModel commonPHR, int highFreqSampleTimes, int upperAlarmThreshold, int lowerAlarmThreshold, string sampleTime, DateTime statStartTime, DateTime statEndTime) + { + + var fetalHeartRate = await _serviceTDengine.GetFetalHeartRateAsync(heartRate.Serialno, (int)heartRate.HeartRate!); + + var phrFreqstatus = await _deviceCacheMgr.GetPregnancyHeartRateFreqStatusAsync(heartRate.Serialno); + var isAbnormal = 0; + #region 判断是否够highFreqSampleTimes,540s + var ts = DateTimeUtil.GetTimeDifferenceInSeconds((DateTime)heartRate.LastUpdate!, phrFreqstatus!.LastUpdate); + // 判断是否够highFreqSampleTimes,540s + ///高频时长不足10分钟停止高频的胎心值生成说明:最近12个数据的最小值生成胎心值, + ///对于小于高频下限阀值取高频下限阀值进行转换; + ///对于高于高频上限阀值取高频上限阀值进行转换。并输出相关日志后续进行数据分析。 + if (ts < highFreqSampleTimes) + { + if (fetalHeartRate > upperAlarmThreshold) + { + _logger.LogWarning($"{heartRate.Serialno} 高频持续不足10分钟,计算胎心值 {fetalHeartRate} 高于高频警告上限阀值{upperAlarmThreshold},最后胎心值{upperAlarmThreshold},并且不告警"); + fetalHeartRate = upperAlarmThreshold; + + + } + else if (fetalHeartRate < lowerAlarmThreshold) + { + _logger.LogWarning($"{heartRate.Serialno} 高频持续不足10分钟,计算胎心值 {fetalHeartRate} 低于高频警告下限阀值 {lowerAlarmThreshold},最后胎心值{lowerAlarmThreshold},并且不告警"); + fetalHeartRate = lowerAlarmThreshold; + } + else + { + _logger.LogWarning($"{heartRate.Serialno} 高频持续不足10分钟,在高频警告下限阀值 {lowerAlarmThreshold} 和 高频警告上限阀值:{upperAlarmThreshold}之间,最后胎心值{fetalHeartRate},并且不告警"); + } + + } + // 超过highFreqSampleTimes,540s + else + { + if (fetalHeartRate > 220) + { + fetalHeartRate = 220; + _logger.LogWarning($"{heartRate.Serialno} 大于220,按220输出,计算因子:孕妇心率 {heartRate.HeartRate},周期:{statStartTime.ToString("yyyy-MM-dd HH:mm:ss")}----{statEndTime.ToString("yyyy-MM-dd HH:mm:ss")}"); + } + + // 胎心的最小值调整为90,超过都按该值90 + if (fetalHeartRate < 90) + { + fetalHeartRate = 90; + _logger.LogWarning($"{heartRate.Serialno} 小于90,按90输出,计算因子:孕妇心率 {heartRate.HeartRate}, 周期:{statStartTime.ToString("yyyy-MM-dd HH:mm:ss")}----{statEndTime.ToString("yyyy-MM-dd HH:mm:ss")}"); + } + isAbnormal = fetalHeartRate > upperAlarmThreshold ? 1 : (fetalHeartRate < lowerAlarmThreshold ? 2 : 0); + } + #endregion + + + + _logger.LogInformation($"{heartRate.Serialno} 在 高频 状态,生成胎心值:{fetalHeartRate},统计周期:{statStartTime.ToString("yyyy-MM-dd HH:mm:ss")}----{statEndTime.ToString("yyyy-MM-dd HH:mm:ss")}"); + // 保存到 数据服务 MySQL 数据库 + HisGpsFetalHeartRate gpsFetalHeartRate = new() + { + FetalHeartRateId = Guid.NewGuid().ToString("D"), + PersonId = commonPHR!.PersonId, + Serialno = heartRate.Serialno, + HeartRate = fetalHeartRate, + SampleTime = sampleTime.Length > 10 ? sampleTime.Substring(0, 10) : sampleTime, + IsAbnormal = isAbnormal, + StatStartTime = statStartTime, + StatEndTime = statEndTime,//commonPHR.StatEndTime, + CreateTime = DateTime.Now, + Method = 1, + IsDisplay = 1, + DeviceKey = commonPHR!.DeviceKey + }; + await _hisFetalHeartApiClient.AddAsync(gpsFetalHeartRate).ConfigureAwait(false); + + #region 高频心率计算胎心数据到iot设备 + + //if (phrFreqstatus != null && isAbnormal != 0) + //{ + // await _serviceIotApi.SetFetalHeartRateConfig(heartRate.Serialno, fetalHeartRate, sampleTime, isAbnormal); + //} + // 高频有数据都推送到iot + + await _serviceIotApi.SetFetalHeartRateConfig(heartRate.Serialno, fetalHeartRate, sampleTime, isAbnormal); + #endregion + var device = await _deviceCacheMgr.GetDeviceBySerialNoAsync(heartRate.Serialno).ConfigureAwait(false); + var fhrMsgId = $"{heartRate.Serialno}-{sampleTime}-{Guid.NewGuid().ToString("D")[^3..]}"; + var fhrMsgTime = DateTimeUtil.GetDateTimeFromUnixTimeMilliseconds(long.Parse(sampleTime.Length < 13 ? sampleTime.PadRight(13, '0') : sampleTime)).ToString("yyyy-MM-dd HH:mm:ss"); + // 胎心数据推送到第三方 + var topic = "topic.push.third"; + var fhrThridMsg = new + { + messageId = fhrMsgId, + topic = topic, + time = fhrMsgTime, + data = new + { + imei = heartRate.Serialno, + value = fetalHeartRate, + isAbnormal, + type = "fetalHeart" + } + }; + await _serviceMqProcess.ProcessIMEIEventMessageAsync(fhrMsgId, topic, 31, fhrThridMsg).ConfigureAwait(false); + + // 胎心数据推送到微信 + if (isAbnormal != 0) + { + + topic = "topic.push.wx"; + var fhrMsg = new + { + messageId = fhrMsgId, + topic = topic, + time = fhrMsgTime, + data = new + { + deviceId = device?.DeviceId, + imei = heartRate.Serialno, + alarmTypeId = 12, + alarmDeviceName = heartRate.Serialno, + alarmRemarks = JsonConvert.SerializeObject(new { fetalHeartValue = fetalHeartRate, isAbnormal = isAbnormal }), + address = string.Empty, + deviceKey = device?.DeviceId + } + }; + await _serviceMqProcess.ProcessIMEIEventMessageAsync(fhrMsgId, topic, fhrMsg).ConfigureAwait(false); + + } + } + private async Task SetIntervalTriggerAsync(string key, string imei, long interval, HisGpsHeartRate heartRate) { // var key = $"health_monitor/schedule_push/{type}/imei/{imei}"; diff --git a/HealthMonitor.WebApi/Worker.cs b/HealthMonitor.WebApi/Worker.cs index 4e9145e..2bc41a2 100644 --- a/HealthMonitor.WebApi/Worker.cs +++ b/HealthMonitor.WebApi/Worker.cs @@ -1824,7 +1824,7 @@ namespace HealthMonitor.WebApi /// /// /// - private async Task SaveAndPushFetalHeartRateEndFreqHeartRateAsync(HisGpsHeartRate heartRate, PregnancyCommonHeartRateModel commonPHR, int highFreqSampleTimes, int upperAlarmThreshold, int lowerAlarmThreshold, string sampleTime, DateTime statStartTime, DateTime statEndTime) + private async Task SaveAndPushFetalHeartRateEndFreqHeartRateAsyncOld(HisGpsHeartRate heartRate, PregnancyCommonHeartRateModel commonPHR, int highFreqSampleTimes, int upperAlarmThreshold, int lowerAlarmThreshold, string sampleTime, DateTime statStartTime, DateTime statEndTime) { // 计算胎心=孕妇心率*系数 @@ -2049,6 +2049,153 @@ namespace HealthMonitor.WebApi } } + /// + /// 高频胎心处理 + /// 1. 高频数据触发连续12个值都是正常的的高频心率处理 + /// 2. 高频结束后的highFreqSampleTimes=0的高频心率处理 + /// 3. 高频结束后的在highFreqSampleTimes>0 正常心率触发的高频心率处理(常态) + /// 4. 高频结束后的时间倒序的正常心率触发的高频心率处理 + /// 5. 高频结束后计算胎心数据,防止结束后与常规心理的胎心处理过长,定时器时长highFreqSampleInterval触发的高频心率处理 + /// 高频结束后超过9分钟且不在阈值内才告警 + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + private async Task SaveAndPushFetalHeartRateEndFreqHeartRateAsync(HisGpsHeartRate heartRate, PregnancyCommonHeartRateModel commonPHR, int highFreqSampleTimes, int upperAlarmThreshold, int lowerAlarmThreshold, string sampleTime, DateTime statStartTime, DateTime statEndTime) + { + + var fetalHeartRate = await _serviceTDengine.GetFetalHeartRateAsync(heartRate.Serialno, (int)heartRate.HeartRate!); + + var phrFreqstatus = await _deviceCacheMgr.GetPregnancyHeartRateFreqStatusAsync(heartRate.Serialno); + var isAbnormal = 0; + #region 判断是否够highFreqSampleTimes,540s + var ts = DateTimeUtil.GetTimeDifferenceInSeconds((DateTime)heartRate.LastUpdate!, phrFreqstatus!.LastUpdate); + // 判断是否够highFreqSampleTimes,540s + ///高频时长不足10分钟停止高频的胎心值生成说明:最近12个数据的最小值生成胎心值, + ///对于小于高频下限阀值取高频下限阀值进行转换; + ///对于高于高频上限阀值取高频上限阀值进行转换。并输出相关日志后续进行数据分析。 + if (ts < highFreqSampleTimes) + { + if (fetalHeartRate > upperAlarmThreshold) + { + _logger.LogWarning($"{heartRate.Serialno} 高频持续不足10分钟,计算胎心值 {fetalHeartRate} 高于高频警告上限阀值{upperAlarmThreshold},最后胎心值{upperAlarmThreshold},并且不告警"); + fetalHeartRate = upperAlarmThreshold; + + + } + else if (fetalHeartRate < lowerAlarmThreshold) + { + _logger.LogWarning($"{heartRate.Serialno} 高频持续不足10分钟,计算胎心值 {fetalHeartRate} 低于高频警告下限阀值 {lowerAlarmThreshold},最后胎心值{lowerAlarmThreshold},并且不告警"); + fetalHeartRate = lowerAlarmThreshold; + } + else + { + _logger.LogWarning($"{heartRate.Serialno} 高频持续不足10分钟,在高频警告下限阀值 {lowerAlarmThreshold} 和 高频警告上限阀值:{upperAlarmThreshold}之间,最后胎心值{fetalHeartRate},并且不告警"); + } + + } + // 超过highFreqSampleTimes,540s + else + { + if (fetalHeartRate > 220) + { + fetalHeartRate = 220; + _logger.LogWarning($"{heartRate.Serialno} 大于220,按220输出,计算因子:孕妇心率 {heartRate.HeartRate},周期:{statStartTime.ToString("yyyy-MM-dd HH:mm:ss")}----{statEndTime.ToString("yyyy-MM-dd HH:mm:ss")}"); + } + + // 胎心的最小值调整为90,超过都按该值90 + if (fetalHeartRate < 90) + { + fetalHeartRate = 90; + _logger.LogWarning($"{heartRate.Serialno} 小于90,按90输出,计算因子:孕妇心率 {heartRate.HeartRate}, 周期:{statStartTime.ToString("yyyy-MM-dd HH:mm:ss")}----{statEndTime.ToString("yyyy-MM-dd HH:mm:ss")}"); + } + isAbnormal = fetalHeartRate > upperAlarmThreshold ? 1 : (fetalHeartRate < lowerAlarmThreshold ? 2 : 0); + } + #endregion + + + + _logger.LogInformation($"{heartRate.Serialno} 在 高频 状态,生成胎心值:{fetalHeartRate},统计周期:{statStartTime.ToString("yyyy-MM-dd HH:mm:ss")}----{statEndTime.ToString("yyyy-MM-dd HH:mm:ss")}"); + // 保存到 数据服务 MySQL 数据库 + HisGpsFetalHeartRate gpsFetalHeartRate = new() + { + FetalHeartRateId = Guid.NewGuid().ToString("D"), + PersonId = commonPHR!.PersonId, + Serialno = heartRate.Serialno, + HeartRate = fetalHeartRate, + SampleTime = sampleTime.Length > 10 ? sampleTime.Substring(0, 10) : sampleTime, + IsAbnormal = isAbnormal, + StatStartTime = statStartTime, + StatEndTime = statEndTime,//commonPHR.StatEndTime, + CreateTime = DateTime.Now, + Method = 1, + IsDisplay = 1, + DeviceKey = commonPHR!.DeviceKey + }; + await _hisFetalHeartApiClient.AddAsync(gpsFetalHeartRate).ConfigureAwait(false); + + #region 高频心率计算胎心数据到iot设备 + + //if (phrFreqstatus != null && isAbnormal != 0) + //{ + // await _serviceIotApi.SetFetalHeartRateConfig(heartRate.Serialno, fetalHeartRate, sampleTime, isAbnormal); + //} + // 高频有数据都推送到iot + + await _serviceIotApi.SetFetalHeartRateConfig(heartRate.Serialno, fetalHeartRate, sampleTime, isAbnormal); + #endregion + var device = await _deviceCacheMgr.GetDeviceBySerialNoAsync(heartRate.Serialno).ConfigureAwait(false); + var fhrMsgId = $"{heartRate.Serialno}-{sampleTime}-{Guid.NewGuid().ToString("D")[^3..]}"; + var fhrMsgTime = DateTimeUtil.GetDateTimeFromUnixTimeMilliseconds(long.Parse(sampleTime.Length < 13 ? sampleTime.PadRight(13, '0') : sampleTime)).ToString("yyyy-MM-dd HH:mm:ss"); + // 胎心数据推送到第三方 + var topic = "topic.push.third"; + var fhrThridMsg = new + { + messageId = fhrMsgId, + topic = topic, + time = fhrMsgTime, + data = new + { + imei = heartRate.Serialno, + value = fetalHeartRate, + isAbnormal, + type = "fetalHeart" + } + }; + await _serviceMqProcess.ProcessIMEIEventMessageAsync(fhrMsgId, topic, 31, fhrThridMsg).ConfigureAwait(false); + + // 胎心数据推送到微信 + if (isAbnormal != 0) + { + + topic = "topic.push.wx"; + var fhrMsg = new + { + messageId = fhrMsgId, + topic = topic, + time = fhrMsgTime, + data = new + { + deviceId = device?.DeviceId, + imei = heartRate.Serialno, + alarmTypeId = 12, + alarmDeviceName = heartRate.Serialno, + alarmRemarks = JsonConvert.SerializeObject(new { fetalHeartValue = fetalHeartRate, isAbnormal = isAbnormal }), + address = string.Empty, + deviceKey = device?.DeviceId + } + }; + await _serviceMqProcess.ProcessIMEIEventMessageAsync(fhrMsgId, topic, fhrMsg).ConfigureAwait(false); + + } + } + /// /// 去除高频数据 @@ -2178,7 +2325,7 @@ namespace HealthMonitor.WebApi /// /// /// - public async Task CalculateNormalFetalHeartRateIntervalAsync(HisGpsHeartRate heartRate, PregnancyCommonHeartRateModel commonPHR,int highFreqSampleInterval) + public async Task CalculateNormalFetalHeartRateIntervalAsyncOld(HisGpsHeartRate heartRate, PregnancyCommonHeartRateModel commonPHR,int highFreqSampleInterval) { var daysPhr = await _serviceTDengine.GetBySerialNoAsync(heartRate.Serialno, 7); var now = DateTime.Now; @@ -2535,6 +2682,264 @@ namespace HealthMonitor.WebApi } + /// + /// 周期性常规心率计算胎心值 + /// + /// + /// + /// + /// + public async Task CalculateNormalFetalHeartRateIntervalAsync(HisGpsHeartRate heartRate, PregnancyCommonHeartRateModel commonPHR, int highFreqSampleInterval) + { + var daysPhr = await _serviceTDengine.GetBySerialNoAsync(heartRate.Serialno, 7); + var now = DateTime.Now; + var filteredPhr = daysPhr.Where(i => i.LastUpdate >= heartRate.LastUpdate && i.LastUpdate <= now).ToList(); + // 去除高频 + var normalPhr = GetNonFreqPregnancyHeartRate(filteredPhr, highFreqSampleInterval).OrderBy(i => i.LastUpdate); + + if (normalPhr.ToList().Count == 0) + { + _logger.LogWarning($"{heartRate.Serialno} 时间段{heartRate.LastUpdate}-{now},去除高频心率数据后,没有常规数据,不计算常规胎心数据"); + return; + } + + var startPhr = normalPhr.First(); + var endPhr = normalPhr.Last(); + + //var sampleTime = GetSampleTimeFromLastUpdate((DateTime)heartRate.LastUpdate!, INTERVAL_FHR); + // 数据统计边界 + var boundaryStatStartTime = GetSampleTimeFromLastUpdate((DateTime)startPhr.LastUpdate!, INTERVAL_FHR); + var boundaryStatEndTime = GetSampleTimeFromLastUpdate((DateTime)endPhr.LastUpdate!, INTERVAL_FHR).AddMinutes(INTERVAL_FHR); + + _logger.LogInformation($"{heartRate.Serialno} 常规胎心统计边界{boundaryStatStartTime}-{boundaryStatEndTime}"); + + try + { + //var CalNow = DateTime.Now; + //var during = TimeSpan.FromSeconds(300); //5分钟 + + var c = 0; + while (true) + { + + //if (DateTime.Now - CalNow > during) + //{ + // _logger.LogInformation($"{heartRate.Serialno} 超过1分钟,迭代完成跳出循环 "); + // break; + //} + + await Task.Delay(TimeSpan.FromSeconds(1)); + + var segmentStatStartTime = boundaryStatStartTime.AddMinutes(c * INTERVAL_FHR); + var segmentStatEndTime = segmentStatStartTime.AddMinutes(INTERVAL_FHR); + c++; + var statStartTime = segmentStatStartTime; + var statEndTime = segmentStatEndTime; + + _logger.LogInformation($"{heartRate.Serialno} 当前统计周期{statStartTime.ToString("yyyy-MM-dd HH:mm:ss")}-{statEndTime.ToString("yyyy-MM-dd HH:mm:ss")}"); + + if (statEndTime > boundaryStatEndTime) + { + _logger.LogInformation($"{heartRate.Serialno} 常规胎心统计超过时间边界,迭代完成跳出循环 "); + break; + } + + var segmentPhr = normalPhr + .Where(i => i.LastUpdate <= statEndTime && i.LastUpdate >= statStartTime) + .ToList(); + + if (segmentPhr.Count == 0) + { + // 跳出当次迭代,进入下次迭代 + _logger.LogWarning($"{heartRate.Serialno} 统计周期:{statStartTime.ToString("yyyy-MM-dd HH:mm:ss")}-{statEndTime.ToString("yyyy-MM-dd HH:mm:ss")} 孕妇心率数据不足,{segmentPhr.Count}条记录,不处理"); + continue; + } + + _logger.LogInformation($"{heartRate.Serialno} 当前统计周期{statStartTime.ToString("yyyy-MM-dd HH:mm:ss")}-{statEndTime.ToString("yyyy-MM-dd HH:mm:ss")},对应的常规心率ID{string.Join(",", segmentPhr.Select(i => i.MessageId))}"); + + + + var sampleTime = DateTimeUtil.ConvertToTimeStamp(segmentStatStartTime).ToString(); + sampleTime = sampleTime.Length > 10 ? sampleTime.Substring(0, 10) : sampleTime; + + //检测 是否存在,不存在则处理 + GeneralParam param = new() + { + Filters = new List + { + new () + { + Key=nameof(HisGpsFetalHeartRate.Serialno), + Value=heartRate.Serialno, + ValueType=QueryValueTypeEnum.String, + Operator=QueryOperatorEnum.Equal + }, + new () + { + Key=nameof(HisGpsFetalHeartRate.SampleTime), + Value=sampleTime, + ValueType=QueryValueTypeEnum.String, + Operator=QueryOperatorEnum.Equal + }, + + }, + OrderBys = new List + { + new (){ + IsDesc=true, + Key=nameof(HisGpsFetalHeartRate.SampleTime) + } + } + }; + + var fhr = await _hisFetalHeartApiClient.GetFirstAsync(param, heartRate.Serialno[^2..], null, new RequestHeader { RequestId = Guid.NewGuid().ToString("D") }); + + if (fhr == null) + { + var avgPhr = segmentPhr.Count == 1 + ? segmentPhr.First().PregnancyHeartRate + : segmentPhr.Average(i => i.PregnancyHeartRate); + + heartRate.HeartRate = (int)avgPhr; + + + + #region 胎心阈值判断 + var fetalHeartRateFromAlgorithm = await _serviceTDengine.GetFetalHeartRateAsync(heartRate.Serialno, (int)heartRate.HeartRate); + // 计算胎心率并进行边界检查 + var fetalHeartRate = Math.Clamp(fetalHeartRateFromAlgorithm, 90, 220); + if (fetalHeartRate != fetalHeartRateFromAlgorithm) + { + _logger.LogWarning($"{heartRate.Serialno} 胎心率超出范围, 按修正值 {fetalHeartRate} 输出, 孕妇心率 {heartRate.HeartRate}, 周期 {statStartTime} - {statEndTime}"); + } + #endregion + + _logger.LogInformation($"{heartRate.Serialno} 在 常规 状态,生成胎心值:{fetalHeartRate},统计周期:{statStartTime.ToString("yyyy-MM-dd HH:mm:ss")}----{statEndTime.ToString("yyyy-MM-dd HH:mm:ss")}"); + var isAbnormal = 0; + + #region 保存到Mysql数据库 + // 保存到 数据服务 MySQL 数据库 + HisGpsFetalHeartRate gpsFetalHeartRate = new() + { + FetalHeartRateId = Guid.NewGuid().ToString("D"), + PersonId = commonPHR!.PersonId, + Serialno = heartRate.Serialno, + HeartRate = fetalHeartRate, + SampleTime = sampleTime, + IsAbnormal = isAbnormal, + StatStartTime = statStartTime, + StatEndTime = statEndTime,//commonPHR.StatEndTime, + CreateTime = DateTime.Now, + Method = 1, + IsDisplay = 1, + DeviceKey = commonPHR!.DeviceKey + }; + await _hisFetalHeartApiClient.AddAsync(gpsFetalHeartRate).ConfigureAwait(false); + #endregion + + #region 推送最后一条常规心率计算的胎心数据到iot设备 + var lastPhr = await _serviceTDengine.GetLastAsync(heartRate.Serialno); + if (segmentStatEndTime == boundaryStatEndTime) + { + await _serviceIotApi.SetFetalHeartRateConfig(heartRate.Serialno, fetalHeartRate, sampleTime, isAbnormal); + _logger.LogInformation($"{heartRate.Serialno} 推送最后一条常规心率计算的胎心数据到iot设备"); + } + #endregion + + #region 推送到第三方 + var device = await _deviceCacheMgr.GetDeviceBySerialNoAsync(heartRate.Serialno).ConfigureAwait(false); + var fhrMsgId = $"{heartRate.Serialno}-{sampleTime}-{Guid.NewGuid().ToString("D")[^3..]}"; + var fhrMsgTime = DateTimeUtil.GetDateTimeFromUnixTimeMilliseconds(long.Parse(sampleTime.Length < 13 ? sampleTime.PadRight(13, '0') : sampleTime)).ToString("yyyy-MM-dd HH:mm:ss"); + var topic = "topic.push.third"; + var fhrThridMsg = new + { + messageId = fhrMsgId, + topic = topic, + time = fhrMsgTime, + data = new + { + imei = heartRate.Serialno, + value = fetalHeartRate, + isAbnormal, + type = "fetalHeart" + } + }; + await _serviceMqProcess.ProcessIMEIEventMessageAsync(fhrMsgId, topic, 31, fhrThridMsg).ConfigureAwait(false); + #endregion + + #region 推送到微信 + if (isAbnormal != 0) + { + + topic = "topic.push.wx"; + var fhrMsg = new + { + messageId = fhrMsgId, + topic = topic, + time = fhrMsgTime, + data = new + { + deviceId = device?.DeviceId, + imei = heartRate.Serialno, + alarmTypeId = 12, + alarmDeviceName = heartRate.Serialno, + alarmRemarks = JsonConvert.SerializeObject(new { fetalHeartValue = fetalHeartRate, isAbnormal = isAbnormal }), + address = string.Empty, + deviceKey = device?.DeviceId + } + }; + await _serviceMqProcess.ProcessIMEIEventMessageAsync(fhrMsgId, topic, fhrMsg).ConfigureAwait(false); + + } + #endregion + + } + else + { + _logger.LogInformation($"{heartRate.Serialno},统计常规胎心周期:{statStartTime.ToString("yyyy-MM-dd HH:mm:ss")}----{statEndTime.ToString("yyyy-MM-dd HH:mm:ss")} ,常规胎心已处理"); + } + //await Task.Delay(TimeSpan.FromSeconds(1)); + // 跳出循环 + if (statEndTime.ToString("yyyyMMddHHmm") == boundaryStatEndTime.ToString("yyyyMMddHHmm")) + { + _logger.LogInformation($"{heartRate.Serialno} 迭代完成跳出循环 "); + break; + } + + //if (statEndTime>= boundaryStatEndTime) + //{ + // _logger.LogInformation($"{heartRate.Serialno} 时间边界,迭代完成跳出循环 "); + // break; + //} + + + } + } + catch (Exception ex) + { + _logger.LogError($"处理常规胎心数据时发生错误: {ex.Message}"); + } + + + + //for (var segmentStatStartTime = boundaryStatStartTime; + // segmentStatStartTime < boundaryStatEndTime; + // segmentStatStartTime = segmentStatStartTime.AddMinutes(INTERVAL_FHR)) + //{ + // var segmentStatEndTime = segmentStatStartTime.AddMinutes(INTERVAL_FHR); + + // var segmentPhr = daysPhr + // .Where(i => i.LastUpdate <= segmentStatEndTime && i.LastUpdate >= segmentStatStartTime) + // .ToList(); + + // if (segmentStatEndTime == boundaryStatEndTime) + // { + // break; + // } + //} + } + + public async Task CalculateFetalMovementIntervalAsync(HisGpsHeartRate heartRate, PregnancyCommonHeartRateModel commonPHR,DateTime edoc) {