|
- 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 SqlSugar;
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Threading.Tasks;
- using TelpoDataService.Util.Clients;
- using TelpoDataService.Util.Entities.GpsCard;
- using TelpoDataService.Util.Entities.GpsLocationHistory;
- using TelpoDataService.Util.Models;
- using TelpoDataService.Util.QueryObjects;
-
- namespace HealthMonitor.Service.Resolver
- {
- public class PregnancyHeartRateResolver : IResolver
- {
- private readonly ILogger<PregnancyHeartRateResolver> _logger;
- private readonly TDengineService _serviceTDengine;
-
- private readonly DeviceCacheManager _deviceCacheMgr;
- private readonly IotApiService _serviceIotApi;
-
-
- private readonly AsyncLocal<string> _messageId = new();
- private readonly AsyncLocal<HisGpsHeartRate> _msgData = new();
- private readonly HttpHelper _httpHelper = default!;
- private readonly EtcdService _serviceEtcd;
-
- private readonly GpsLocationHistoryAccessorClient<HisGpsFetalHeartRate> _hisFetalHeartApiClient;
- private readonly GpsLocationHistoryAccessorClient<HisGpsFetalMovement> _hisFetalMovementApiClient;
-
- private readonly FetalMovementNormalValueRangeCacheManager _mgrFetalMovementNormalValueRangeCache;
-
-
-
-
- public PregnancyHeartRateResolver(ILogger<PregnancyHeartRateResolver> logger,
- HttpHelper httpHelper, EtcdService serviceEtcd, DeviceCacheManager deviceCacheMgr,
- IotApiService iotApiService, TDengineService serviceDengine, FetalMovementNormalValueRangeCacheManager fetalMovementNormalValueRangeCacheMgr,
- GpsLocationHistoryAccessorClient<HisGpsFetalHeartRate> hisFetalHeartApiClient,
- GpsLocationHistoryAccessorClient<HisGpsFetalMovement> hisFetalMovementApiClient
- )
- {
- _logger = logger;
- _httpHelper = httpHelper;
- _serviceEtcd = serviceEtcd;
- _serviceTDengine = serviceDengine;
- _deviceCacheMgr = deviceCacheMgr;
- _serviceIotApi = iotApiService;
- _hisFetalHeartApiClient = hisFetalHeartApiClient;
- _hisFetalMovementApiClient = hisFetalMovementApiClient;
- _mgrFetalMovementNormalValueRangeCache = fetalMovementNormalValueRangeCacheMgr;
- }
-
- public void SetResolveInfo(PackageMsgModel msg)
- {
- var topicHmPregnancyHeartRate = JsonConvert.DeserializeObject<TopicHmPregnancyHeartRate>(msg.DetailData.ToString()!);
- _messageId.Value = msg.MessageId;
- _msgData.Value = new HisGpsHeartRate()
- {
- HeartRateId = topicHmPregnancyHeartRate!.PregnancyHeartRateId,
- MessageId = topicHmPregnancyHeartRate!.MessageId,
- Serialno = topicHmPregnancyHeartRate!.Serialno,
- HeartRate= topicHmPregnancyHeartRate.PregnancyHeartRate,
- LastUpdate = DateTimeUtil.GetDateTimeFromUnixTimeMilliseconds(SafeType.SafeInt64(topicHmPregnancyHeartRate.LastUpdate) / 1000000),
- CreateTime = DateTimeUtil.GetDateTimeFromUnixTimeMilliseconds(SafeType.SafeInt64(topicHmPregnancyHeartRate.CreateTime) / 1000000),
- Method = topicHmPregnancyHeartRate!.Method,
- IsDisplay = topicHmPregnancyHeartRate!.IsDisplay ? 1 : 0
- };
- }
- public override string ToString()
- {
- return $"{nameof(PregnancyHeartRateResolver)}[{_messageId.Value}]";
- }
-
- public async Task ExecuteMessageAsync()
- {
- var messageId = _messageId.Value;
- var heartRate = _msgData.Value!;
-
- var watchConfig = await _deviceCacheMgr.GetGpsDeviceWatchConfigCacheObjectBySerialNoAsync(heartRate.Serialno, "0067");
- var isFetalHeartEnable = watchConfig != null && (int)watchConfig["enabled"]! == 1;
-
- if (isFetalHeartEnable)
- {
- // 高频心率采样间隔
- 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"]!;
- // EDOC
- var edoc = DateTimeUtil.ToDateTime(watchConfig!["EDOC"]!.ToString());
- // interval (分钟)
- var intervalFHR= (int)watchConfig["interval"]!;
-
- var fetalHeartRateIsAbnormal = 0;
-
- var phr = await _serviceTDengine.GetBySerialNoAsync<PregnancyHeartRateModel>(heartRate.Serialno, 7);
- if (phr.Count >= 30)
- {
- /**
- var commonPHR = await _serviceTDengine.GetLastAsync<PregnancyCommonHeartRateModel>(heartRate.Serialno);
- if (commonPHR != null)
- {
- var lastUpdateNow = (DateTime)heartRate.LastUpdate!;
- var midNight = new DateTime(lastUpdateNow.Year, lastUpdateNow.Month, lastUpdateNow.Day, 0, 0, 0);
- #region 计算胎心数据
- _logger.LogInformation($"{heartRate.Serialno} 计算胎心数据 ");
-
- var fhrNow = lastUpdateNow;
-
- TimeSpan fhrTS = fhrNow - midNight;
- // 当天每隔intervalFHR 分钟的段数,取整数部分
- int segmentCountFHR = (int)(fhrTS.TotalMinutes / intervalFHR);
- for (int i = 0; i < segmentCountFHR; i++)
- {
- // 每intervalFHR * i分钟
- var fhrSampleTime = DateTimeUtil.ConvertToTimeStamp(midNight.AddMinutes(intervalFHR * i)).ToString()[..10];
-
- // 统计开始时间
- var statStartTimeFHR = midNight.AddMinutes(intervalFHR * i);
- // 统计结束时间
- var statEndTimeFHR = midNight.AddMinutes(intervalFHR * (i + 1));
-
- var isFHRExisted =await _mgrFetalHeartRateCache.FetalHeartRateIsExistedAsync(heartRate.Serialno, fhrSampleTime);
-
- _logger.LogInformation($"{heartRate.Serialno} 胎心数据采样时间:{fhrSampleTime}|{midNight.AddMinutes(intervalFHR * i).ToString("yyyy-MM-dd HH:mm:ss")}, 采样周期:{statStartTimeFHR}-{statEndTimeFHR},isFHRExisted:{isFHRExisted} ");
-
- if (!isFHRExisted)
- {
- // 开始计算 ,每intervalFHR分钟计算一个
- var phrRange = phr.Where(i => i.LastUpdate >= statStartTimeFHR && i.LastUpdate <= statEndTimeFHR)
- .OrderByDescending(i => i.LastUpdate)
- .ToList();
-
- // 胎心数据每intervalFHR分钟计算1个值,采集的数据多于1个取平均值再*胎心系数。
- var phrValue = phrRange.Count == 1
- ? phrRange.First().PregnancyHeartRate
- : phrRange.Average(i => i.PregnancyHeartRate);
- // 当天的系数,没有的就实时计算
- var fprCoefficient = 0f;
-
- //if (commonPHR.CreateTime.Year == lastUpdateNow.Year &&
- // commonPHR.CreateTime.Month == lastUpdateNow.Month &&
- // commonPHR.CreateTime.Day == lastUpdateNow.Day
- // )
- //{
- // fprCoefficient = (float)commonPHR?.StatModeAvgFprCoefficient!;
- //}
- //else
- //{
- // //var daysDiff = 7 - (DateTime.Now.Day - lastUpdateNow.Day);
- // //var realtimePCHR = await _serviceTDengine.InitPregnancyCommonHeartRateModeAsync(heartRate.Serialno, daysDiff);
- // //fprCoefficient = realtimePCHR!.StatModeAvgFprCoefficient;
- // var pchr = await _serviceTDengine.GetBySerialNoAsync<PregnancyCommonHeartRateModel>(heartRate.Serialno);
- // var dayPCHR = pchr.Where(i => i.CreateTime.Year == lastUpdateNow.Year && i.CreateTime.Month == lastUpdateNow.Month && i.CreateTime.Day == i.CreateTime.Day);
- // fprCoefficient=dayPCHR.First().StatModeAvgFprCoefficient;
- //}
-
- fprCoefficient = (float)commonPHR?.StatModeAvgFprCoefficient!;
-
- var fetalHeartRate = SafeType.SafeInt(phrValue * fprCoefficient);
-
- var fetalHeartRateIsAbnormal = fetalHeartRate > upperAlarmThreshold ? 1 : (fetalHeartRate < lowerAlarmThreshold ? 2 : 0);
- fetalHeartRateIsAbnormal = 0;// 不是高频数据,按正常值
- HisGpsFetalHeartRate gpsFetalHeartRate = new()
- {
- FetalHeartRateId = Guid.NewGuid().ToString("D"),
- PersonId = commonPHR!.PersonId,
- Serialno = heartRate.Serialno,
- HeartRate = fetalHeartRate,
- SampleTime = fhrSampleTime.Length > 10 ? fhrSampleTime.Substring(0, 10) : fhrSampleTime,
- IsAbnormal = fetalHeartRateIsAbnormal,
- StatStartTime = phrRange.OrderBy(i => i.LastUpdate).First().LastUpdate,
- StatEndTime = phrRange.OrderBy(i => i.LastUpdate).Last().LastUpdate,
- CreateTime = DateTime.Now,
- Method = 1,
- IsDisplay = 1,
- DeviceKey = commonPHR!.DeviceKey
- };
- // 保存到 数据服务 MySQL 数据库
- await _hisFetalHeartApiClient.AddAsync(gpsFetalHeartRate).ConfigureAwait(false);
-
- // 推送到api/v1/open/OpenIot/SetFetalHeartRateConfig
- await _serviceIotApi.SetFetalHeartRateConfig(heartRate.Serialno, fetalHeartRate, fhrSampleTime, fetalHeartRateIsAbnormal);
-
- // 推送到微信
-
-
- // 设置入库缓存记录
- await _mgrFetalHeartRateCache.SetFetalHeartRateAsync(heartRate.Serialno, fhrSampleTime);
- }
- }
-
- #endregion
-
- #region 计算胎动数据
- _logger.LogInformation($"{heartRate.Serialno} 计算胎动数据 ");
-
- var fmNow = lastUpdateNow;
-
- TimeSpan fmTS = fmNow - midNight;
-
- // 当天每隔2小时的段数,取整数部分
- int segmentCountFM = (int)(fmTS.TotalHours / 2);
-
- for (int i = 0; i < segmentCountFM; i++)
- {
- // 每两小时
- var fetalMovementSampleTime = DateTimeUtil.ConvertToTimeStamp(midNight.AddHours(2 * i)).ToString()[..10];
-
- // 统计开始时间
- var statStartTime = midNight.AddHours(2 * i);
- // 统计结束时间
- var statEndTime = midNight.AddHours(2 * (i + 1));
-
- //var isFetalMovementExist = await _hisFetalMovementApiClient.GetFirstAsync(param, heartRate.Serialno[^2..], null, new RequestHeader { RequestId = Guid.NewGuid().ToString("D") });
- var isFetalMovementExisted = await _mgrFetalMovement.FetalMovementIsExistedAsync(heartRate.Serialno, fetalMovementSampleTime);
-
- if (!isFetalMovementExisted)
- {
- /// 开始计算
- var phrRange = phr.Where(i => i.LastUpdate >= statStartTime && i.LastUpdate <= statEndTime)
- .OrderByDescending(i => i.LastUpdate)
- .Select(i => i.LastUpdate)
- .ToList();
-
- if (phrRange.Count >= 2)
- {
- var duringMins = Math.Abs((phrRange.First() - statStartTime).TotalMinutes);
- //在餐后时间段(8:00~10:00,12:00~14:00,18:00~20:00,22:00~24:00)取中间值。其他时间段取正常起始值
- bool isInTimeRanges = IsLastUpdateInTimeRanges(fmNow);
-
- int pregnancyWeeks = (DateTime.Now - edoc.AddDays(-280)).Days / 7;
- if (pregnancyWeeks >= 12 && pregnancyWeeks <= 50)
- {
- var fetalMovementMap = _mgrFetalMovementNormalValueRangeCache.GetFetalMovements();
-
- var fetalMovementMapValue = isInTimeRanges ? fetalMovementMap
- .Where(i =>
- i.PregnancyPeriod![0] <= pregnancyWeeks &&
- i.PregnancyPeriod[1] >= pregnancyWeeks)
- .Select(i => i.MedianMovement)
- .FirstOrDefault()
- :
- fetalMovementMap
- .Where(i =>
- i.PregnancyPeriod![0] <= pregnancyWeeks &&
- i.PregnancyPeriod[1] >= pregnancyWeeks)
- .Select(i => i.InitialMovement)
- .FirstOrDefault()
- ;
-
- var fetalMovementValue = (fetalMovementMapValue * duringMins * 2) / 120;
-
-
- // 四舍五入
- var fetalMovement = (int)Math.Round(fetalMovementValue, 0, MidpointRounding.AwayFromZero);
- // _logger.LogInformation($"{heartRate.Serialno} segmentCountFMIndex: {i} -- fetalMovementSampleTime:{fetalMovementSampleTime}|{midNight.AddHours(2 * i).ToString("yyyy-MM-dd HH:mm:ss")} -- statStartTime: {statStartTime} -- statEndTime: {statEndTime}-- isFetalMovementExisted: {isFetalMovementExisted} ");
-
- _logger.LogInformation($"{heartRate.Serialno} 胎动数据采样时间:{fetalMovementSampleTime}|{midNight.AddHours(2 * i).ToString("yyyy-MM-dd HH:mm:ss")}, 采样周期:{statStartTime}-{statEndTime}, 原始胎动值:{fetalMovementMapValue}, 佩戴时间 :{duringMins}|{statStartTime}-{phrRange.First()}, 胎动计算值:{fetalMovementValue}, 胎动最终值:{fetalMovement}");
-
- // 获取胎心数据状态与胎动数据状态一致
- //var feltalMovementIsAbnormal = fetalHeartRateIsAbnormal;
- var feltalMovementIsAbnormal = 0;
- // 推送到api/v1/open/OpenIot/SetFetalMovementConfig
-
- await _serviceIotApi.SetFetalMovementConfig(heartRate.Serialno, fetalMovement, fetalMovementSampleTime, feltalMovementIsAbnormal);
-
- // 保存到MySQL数据库
- HisGpsFetalMovement fm = new()
- {
- FetalMovementId = Guid.NewGuid().ToString("D"),
- PersonId = commonPHR!.PersonId,
- Serialno = heartRate.Serialno,
- CreateTime = DateTime.Now,
- IsAbnormal = feltalMovementIsAbnormal,
- FetalMovementValue = fetalMovement,
- SampleTime = fetalMovementSampleTime,
- Method = 1,
- IsDisplay = 1,
- DeviceKey = commonPHR!.DeviceKey
- };
- await _hisFetalMovementApiClient.AddAsync(fm).ConfigureAwait(false);
-
- // 设置入库缓存记录
- await _mgrFetalMovement.SetFetalMovementAsync(heartRate.Serialno, fetalMovementSampleTime);
- }
- }
- }
-
- }
- #endregion
- }
- */
-
-
- #region 高频心率计算
-
- //// 获取最近的两个记录,并计算它们的 LastUpdate 时间差
- //var firstTwoPhr = phr.OrderByDescending(i => i.LastUpdate).Take(2).Select(i => i.LastUpdate).ToList();
- //var timeDiff = firstTwoPhr[0] - firstTwoPhr[1];
-
- //// 如果需要,将时间差转换为秒
- //var timeDiffInSeconds = timeDiff.TotalSeconds;
-
- //// 高频心率启动
- //if (timeDiffInSeconds<=highFreqSampleInterval)
- //{
-
- // var phrFreqstatus =await _deviceCacheMgr.GetPregnancyHeartRateFreqStatusAsync(heartRate.Serialno);
- // if (phrFreqstatus == null)
- // {
- // /// 设置高频状态
- // _logger.LogInformation($"进入高频心率启动状态 timeDiffInSeconds {timeDiffInSeconds},highFreqSampleInterval:{highFreqSampleInterval}");
- // // 设置高频状态
- // 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);
-
- // /// 设置高频状态
- // _logger.LogInformation($"结束高频心率状态 timeDiffInSeconds {timeDiffInSeconds},highFreqSampleInterval:{highFreqSampleInterval}");
- // }
- //}
-
- #endregion
-
- #region 计算胎心和胎动数据
- var commonPHR = await _serviceTDengine.GetLastAsync<PregnancyCommonHeartRateModel>(heartRate.Serialno);
- if (commonPHR != null)
- {
-
- #region 计算胎心数据
-
- // 获取最近的两个记录,并计算它们的 LastUpdate 时间差
- var firstTwoPhr = phr.OrderByDescending(i => i.LastUpdate).Take(2).Select(i => i.LastUpdate).ToList();
- var timeDiff = firstTwoPhr[0] - firstTwoPhr[1];
-
- // 如果需要,将时间差转换为秒
- var timeDiffInSeconds = timeDiff.TotalSeconds;
-
- // 高频心率启动
- if (timeDiffInSeconds <= highFreqSampleInterval)
- {
-
- var phrFreqstatus = await _deviceCacheMgr.GetPregnancyHeartRateFreqStatusAsync(heartRate.Serialno);
- if (phrFreqstatus == null)
- {
- /// 设置高频状态
- _logger.LogInformation($"进入高频心率启动状态 timeDiffInSeconds {timeDiffInSeconds},highFreqSampleInterval:{highFreqSampleInterval}");
- // 设置高频状态
- 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)
- {
- 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);
-
- /// 设置高频状态
- _logger.LogInformation($"结束高频心率状态 timeDiffInSeconds {timeDiffInSeconds},highFreqSampleInterval:{highFreqSampleInterval}");
- }
- // 正常心率
- else
- {
- _logger.LogInformation($"{heartRate.Serialno} 计算胎心数据");
- // 上15分钟的数据
- // 获取当前时间
- DateTime nowInterval = (DateTime)heartRate.LastUpdate!;
-
- // 计算last_update到上一刻钟的分钟数
- int minutesToSubtract = nowInterval.Minute % intervalFHR;
-
- // 计算上一刻钟的时间
- DateTime previousInterval = nowInterval.AddMinutes(-minutesToSubtract).AddSeconds(-nowInterval.Second).AddMilliseconds(-nowInterval.Millisecond);
-
- // 使用 last_update 上一刻
- var sampleTimeFHR = DateTimeUtil.ConvertToTimeStamp(previousInterval).ToString();
-
- // 计算last_update到下一刻钟的分钟数
- int minutesToAdd = intervalFHR - (nowInterval.Minute % intervalFHR);
- if (minutesToAdd == intervalFHR)
- {
- minutesToAdd = 0; // 如果已经是刻钟,则不需要增加分钟
- }
-
- // 计算下一刻钟的时间
- DateTime nextInterval = nowInterval.AddMinutes(minutesToAdd)
- .AddSeconds(-nowInterval.Second)
- .AddMilliseconds(-nowInterval.Millisecond);
-
-
- var daysPhr = await _serviceTDengine.GetBySerialNoAsync<PregnancyHeartRateModel>(heartRate.Serialno, 7);
-
- var filteredPhr = daysPhr
- // 使用 last_update 下一刻
- .Where(i => i.LastUpdate <= nextInterval && i.LastUpdate >= nextInterval.AddMinutes(-intervalFHR))
- .ToList();
-
- var phrValue = filteredPhr.Count == 1
- ? filteredPhr.First().PregnancyHeartRate
- : filteredPhr.Average(i => i.PregnancyHeartRate);
-
- var fetalHeartRate = SafeType.SafeInt(phrValue * commonPHR?.StatModeAvgFprCoefficient!);
-
-
- fetalHeartRateIsAbnormal = fetalHeartRate > upperAlarmThreshold ? 1 : (fetalHeartRate < lowerAlarmThreshold ? 2 : 0);
- fetalHeartRateIsAbnormal = 0;// 不是高频数据,按正常值
- HisGpsFetalHeartRate gpsFetalHeartRate = new()
- {
- FetalHeartRateId = Guid.NewGuid().ToString("D"),
- PersonId = commonPHR!.PersonId,
- Serialno = heartRate.Serialno,
- HeartRate = fetalHeartRate,
- SampleTime = sampleTimeFHR.Length > 10 ? sampleTimeFHR.Substring(0, 10) : sampleTimeFHR,
- IsAbnormal = fetalHeartRateIsAbnormal,
- StatStartTime = filteredPhr.OrderBy(i => i.LastUpdate).First().LastUpdate,
- StatEndTime = filteredPhr.OrderBy(i => i.LastUpdate).Last().LastUpdate,
- CreateTime = DateTime.Now,
- Method = 1,
- IsDisplay = 1,
- DeviceKey = commonPHR!.DeviceKey
- };
- // 保存到 数据服务 MySQL 数据库
- await _hisFetalHeartApiClient.AddAsync(gpsFetalHeartRate).ConfigureAwait(false);
-
- // 推送到api/v1/open/OpenIot/SetFetalHeartRateConfig
- await _serviceIotApi.SetFetalHeartRateConfig(heartRate.Serialno, fetalHeartRate, sampleTimeFHR, fetalHeartRateIsAbnormal);
-
- }
- }
-
- #endregion
-
- #region 计算胎动数据
- _logger.LogInformation($"{heartRate.Serialno} 计算胎动数据 ");
-
- var fetalMovementNow = (DateTime)heartRate.LastUpdate!;
-
- var midNight = new DateTime(fetalMovementNow.Year, fetalMovementNow.Month, fetalMovementNow.Day, 0, 0, 0);
-
- TimeSpan fetalMovementTS = fetalMovementNow - midNight;
-
- // 当天每隔2小时的段数,取整数部分
- int segmentCountFM = (int)(fetalMovementTS.TotalHours / 2);
-
- for (int i = 0; i < segmentCountFM; i++)
- {
- // 每两小时
- var fetalMovementSampleTime = DateTimeUtil.ConvertToTimeStamp(midNight.AddHours(2 * i)).ToString()[..10];
-
- // 统计开始时间
- var statStartTime = midNight.AddHours(2 * i);
- // 统计结束时间
- var statEndTime = midNight.AddHours(2 * (i+1));
-
- var isFetalMovementExisted = await _deviceCacheMgr.FetalMovementIsExistedAsync(heartRate.Serialno, fetalMovementSampleTime);
- _logger.LogInformation($"{heartRate.Serialno} 胎动记录{isFetalMovementExisted},数据采样时间:{fetalMovementSampleTime}|{midNight.AddHours(2 * i).ToString("yyyy-MM-dd HH:mm:ss")}, 周期:{statStartTime}-{statEndTime} 开始");
-
- if (!isFetalMovementExisted)
- {
- /// 开始计算
- var phrRange = phr.Where(i => i.LastUpdate >= statStartTime && i.LastUpdate<= statEndTime)
- .OrderByDescending(i => i.LastUpdate)
- .Select(i => i.LastUpdate)
- .ToList();
- // 判断是否有持续佩戴
- if (phrRange.Count >= 2)
- {
- var duringMins = Math.Abs((phrRange.First()- statStartTime).TotalMinutes);
- //在餐后时间段(8:00~10:00,12:00~14:00,18:00~20:00,22:00~24:00)取中间值。其他时间段取正常起始值
- bool isInTimeRanges = IsLastUpdateInTimeRanges(fetalMovementNow);
-
- int pregnancyWeeks = (DateTime.Now - edoc.AddDays(-280)).Days / 7;
- if (pregnancyWeeks >= 12 && pregnancyWeeks <= 50)
- {
- var fetalMovementMap = _mgrFetalMovementNormalValueRangeCache.GetFetalMovements();
-
- var fetalMovementMapValue = isInTimeRanges ? fetalMovementMap
- .Where(i =>
- i.PregnancyPeriod![0] <= pregnancyWeeks &&
- i.PregnancyPeriod[1] >= pregnancyWeeks)
- .Select(i => i.MedianMovement)
- .FirstOrDefault()
- :
- fetalMovementMap
- .Where(i =>
- i.PregnancyPeriod![0] <= pregnancyWeeks &&
- i.PregnancyPeriod[1] >= pregnancyWeeks)
- .Select(i => i.InitialMovement)
- .FirstOrDefault()
- ;
-
- var fetalMovementValue = (fetalMovementMapValue * duringMins * 2) / 120;
- // 四舍五入
- var fetalMovement = (int)Math.Round(fetalMovementValue, 0, MidpointRounding.AwayFromZero);
- // _logger.LogInformation($"{heartRate.Serialno} segmentCountFMIndex: {i} -- fetalMovementSampleTime:{fetalMovementSampleTime}|{midNight.AddHours(2 * i).ToString("yyyy-MM-dd HH:mm:ss")} -- statStartTime: {statStartTime} -- statEndTime: {statEndTime}-- isFetalMovementExisted: {isFetalMovementExisted} ");
-
- _logger.LogInformation($"{heartRate.Serialno} 胎动数据采样时间:{fetalMovementSampleTime}|{midNight.AddHours(2 * i).ToString("yyyy-MM-dd HH:mm:ss")}, 采样周期:{statStartTime}-{statEndTime}, 原始胎动值:{fetalMovementMapValue}, 佩戴时间 :{duringMins}|{statStartTime}-{phrRange.First()}, 胎动计算值:{fetalMovementValue}, 胎动最终值:{fetalMovement} 已完成.");
-
- // 获取胎心数据状态与胎动数据状态一致
- var feltalMovementIsAbnormal = fetalHeartRateIsAbnormal;
- //var feltalMovementIsAbnormal = 0;
- // 推送到api/v1/open/OpenIot/SetFetalMovementConfig
-
- await _serviceIotApi.SetFetalMovementConfig(heartRate.Serialno, fetalMovement, fetalMovementSampleTime, feltalMovementIsAbnormal);
-
- // 保存到MySQL数据库
- HisGpsFetalMovement fm = new()
- {
- FetalMovementId = Guid.NewGuid().ToString("D"),
- PersonId = commonPHR!.PersonId,
- Serialno = heartRate.Serialno,
- CreateTime = DateTime.Now,
- IsAbnormal = feltalMovementIsAbnormal,
- FetalMovementValue = fetalMovement,
- SampleTime = fetalMovementSampleTime,
- Method = 1,
- IsDisplay = 1,
- DeviceKey = commonPHR!.DeviceKey
- };
- await _hisFetalMovementApiClient.AddAsync(fm).ConfigureAwait(false);
-
- // 设置入库缓存记录
- await _deviceCacheMgr.SetFetalMovementAsync(heartRate.Serialno, fetalMovementSampleTime,fm);
- }
- else
- {
- _logger.LogWarning($"{heartRate.Serialno} 孕周 {pregnancyWeeks},超出胎动计算范围");
- }
- }
- else
- {
- _logger.LogInformation($"{heartRate.Serialno} 胎动记录{isFetalMovementExisted},数据采样时间:{fetalMovementSampleTime}|{midNight.AddHours(2 * i).ToString("yyyy-MM-dd HH:mm:ss")}, 周期:{statStartTime}-{statEndTime} 不足两条,不能判断是否持续佩戴");
-
- }
- }
- else
- {
- _logger.LogInformation($"{heartRate.Serialno} 胎动记录{isFetalMovementExisted},数据采样时间:{fetalMovementSampleTime}|{midNight.AddHours(2 * i).ToString("yyyy-MM-dd HH:mm:ss")}, 周期:{statStartTime}-{statEndTime} 已处理");
- }
-
- }
- #endregion
- }
- #endregion
-
-
-
- #region 定时计算胎心数据触发器 {interval} 秒后
- //var fetalKey = $"health_monitor/schedule_push/cal_fetal_heart_rate/imei/{heartRate.Serialno}";
- //await SetIntervalTriggerAsync(fetalKey, heartRate.Serialno, 60 * 15);
- #endregion
-
- #region 定时计算胎心数据触发器下一刻钟后
- //// 获取当前时间
- //DateTime nowInterval = DateTime.Now;
-
- //// 计算下一个15分钟的刻钟
- //int minutesToAdd = 15 - (nowInterval.Minute % 15);
- //if (minutesToAdd == 15)
- //{
- // minutesToAdd = 0; // 如果已经是刻钟,则不需要增加分钟
- //}
- //// 计算下一刻钟的时间
- //DateTime nextInterval = nowInterval.AddMinutes(minutesToAdd)
- // .AddSeconds(-nowInterval.Second)
- // .AddMilliseconds(-nowInterval.Millisecond);
-
- //// 计算时间差
- //TimeSpan timeDifference = nextInterval - nowInterval;
-
- //var fetalKey = $"health_monitor/schedule_push/cal_fetal_heart_rate/imei/{heartRate.Serialno}";
- //await SetIntervalTriggerAsync(fetalKey, heartRate.Serialno, (long)timeDifference.TotalSeconds);
- #endregion
-
- #region 定时计算胎动数据触发器 0 点开始
- //var fetalMovementKey = $"health_monitor/schedule_push/cal_fetal_movement/imei/{heartRate.Serialno}";
- ///// 计算 0 点秒数
- //DateTime now = DateTime.Now;
- //var rand = new Random();
- //var pushSec = rand.Next(59);
- //int pushMin = int.TryParse(heartRate.Serialno.AsSpan(heartRate.Serialno.Length - 1), out pushMin) ? pushMin : 10;
- //DateTime nextRunTime = new (now.Year, now.Month, now.Day, 0, pushMin, pushSec);
- //TimeSpan timeUntilNextRun = nextRunTime - now;
- //if (timeUntilNextRun < TimeSpan.Zero)
- //{
- // timeUntilNextRun = timeUntilNextRun.Add(TimeSpan.FromDays(1));
- // nextRunTime += timeUntilNextRun;
- //}
- //var ttl = (long)timeUntilNextRun.TotalSeconds;
-
- //await SetIntervalTriggerAsync(fetalMovementKey, heartRate.Serialno, ttl);
- #endregion
- }
-
- #region 定时下发触发器(定时建模)
- 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))
- {
- // 注册首次下推
- #if DEBUG
- // await _serviceEtcd.PutValAsync(key, result, 60*1, false).ConfigureAwait(false);
-
- var interval = 0;
- // 获取当前时间
- DateTime now = DateTime.Now;
-
- // 计算距离下一个$interval天后的8点的时间间隔
- DateTime nextRunTime = new DateTime(now.Year, now.Month, now.Day, now.Hour, now.Minute + 1, 58).AddDays(interval);
- TimeSpan timeUntilNextRun = nextRunTime - now;
-
- // 如果当前时间已经超过了8点,将等待到明天后的8点
- if (timeUntilNextRun < TimeSpan.Zero)
- {
- timeUntilNextRun = timeUntilNextRun.Add(TimeSpan.FromMinutes(1));
- nextRunTime += timeUntilNextRun;
- }
- var ttl = (long)timeUntilNextRun.TotalSeconds;
- var data = new
- {
- 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")
- };
- var result = JsonConvert.SerializeObject(data);
- await _serviceEtcd.PutValAsync(key, result, ttl, false).ConfigureAwait(false);
-
- #else
-
- var interval = 0;
- // 获取当前时间
- DateTime now = DateTime.Now;
- var rand=new Random();
- var pushSec = rand.Next(59);
- 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, 0, pushMin, pushSec).AddDays(interval);
- TimeSpan timeUntilNextRun = nextRunTime - now;
-
-
- if (timeUntilNextRun < TimeSpan.Zero)
- {
- timeUntilNextRun = timeUntilNextRun.Add(TimeSpan.FromDays(1));
- nextRunTime += TimeSpan.FromDays(1);
- }
-
- var ttl =(long)timeUntilNextRun.TotalSeconds;
- var data = new
- {
- 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")
- };
- var result = JsonConvert.SerializeObject(data);
- await _serviceEtcd.PutValAsync(key, result,ttl, false).ConfigureAwait(false);
- #endif
-
- }
-
- #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);
-
- // 保存到 数据服务 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 = commonPHR.StatStartTime,
- StatEndTime = commonPHR.StatEndTime,
- CreateTime = DateTime.Now,
- Method = 1,
- IsDisplay = 1,
- DeviceKey = commonPHR!.DeviceKey
- };
- await _hisFetalHeartApiClient.AddAsync(gpsFetalHeartRate).ConfigureAwait(false);
-
- // 推送到api/v1/open/OpenIot/SetFetalHeartRateConfig
- await _serviceIotApi.SetFetalHeartRateConfig(heartRate.Serialno, fetalHeartRate, sampleTime, isAbnormal);
-
- // 推送到微信
- var device = await _deviceCacheMgr.GetDeviceBySerialNoAsync(heartRate.Serialno).ConfigureAwait(false);
-
- var deviceId = device?.DeviceId;
- var msg = new
- {
- messageId = "",
- topic = "topic.push.wx",
- time=""
- };
- }
- }
-
- private async Task SetIntervalTriggerAsync(string key,string imei, long interval)
- {
- // var key = $"health_monitor/schedule_push/{type}/imei/{imei}";
- var schedulePush = await _serviceEtcd.GetValAsync(key).ConfigureAwait(false);
- if (string.IsNullOrWhiteSpace(schedulePush))
- {
- var now = DateTime.Now;
- var timeNextRun = now.Add(TimeSpan.FromSeconds(interval));
- var data = new
- {
- imei,
- create_time = now.ToString("yyyy-MM-dd HH:mm:ss"),
- ttl = interval,
- next_run_time = timeNextRun.ToString("yyyy-MM-dd HH:mm:ss")
- };
- var result = JsonConvert.SerializeObject(data);
- await _serviceEtcd.PutValAsync(key, result, interval, false).ConfigureAwait(false);
- }
- }
-
- public static bool IsLastUpdateInTimeRanges(DateTime lastUpdate)
- {
- var now = lastUpdate.TimeOfDay;
-
- var timeRanges = new List<(TimeSpan Start, TimeSpan End)>
- {
- // 8:00~10:00,12:00~14:00,18:00~20:00,22:00~24:00
- (new TimeSpan(8, 0, 0), new TimeSpan(10, 0, 0)),
- (new TimeSpan(12, 0, 0), new TimeSpan(14, 0, 0)),
- (new TimeSpan(18, 0, 0), new TimeSpan(20, 0, 0)),
- (new TimeSpan(22, 0, 0), new TimeSpan(24, 0, 0))
- };
-
- return timeRanges.Any(range => now >= range.Start && now <= range.End);
- }
- }
- }
|