From e85eb4cdd5fd8eee45387c002300fa7cc3227cd7 Mon Sep 17 00:00:00 2001 From: H Vs Date: Mon, 5 Aug 2024 18:22:30 +0800 Subject: [PATCH] =?UTF-8?q?=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Biz/db/TDengineService.cs | 12 + .../Resolver/PregnancyHeartRateResolver.cs | 1025 +++++++++-------- .../HealthMonitor/HmFetalController.cs | 25 + HealthMonitor.WebApi/Model/Request/BaseReq.cs | 10 + HealthMonitor.WebApi/Worker.cs | 3 +- 5 files changed, 567 insertions(+), 508 deletions(-) create mode 100644 HealthMonitor.WebApi/Model/Request/BaseReq.cs diff --git a/HealthMonitor.Service/Biz/db/TDengineService.cs b/HealthMonitor.Service/Biz/db/TDengineService.cs index f0dd7dc..3bf16af 100644 --- a/HealthMonitor.Service/Biz/db/TDengineService.cs +++ b/HealthMonitor.Service/Biz/db/TDengineService.cs @@ -820,6 +820,18 @@ namespace HealthMonitor.Service.Biz.db return res; } + + + public async Task DeleteAllBySerialNoCMDAsync(string serialNo) where T : class + { + var stbName = typeof(T) + .GetCustomAttribute()? + .STableName; + var endTime=DateTime.Now; + var startTime = DateTime.Now.AddYears(-1); + var sql = $"DELETE FROM {stbName} WHERE ts >= '{startTime:yyyy-MM-dd HH:mm:ss}' AND ts <= '{endTime:yyyy-MM-dd HH:mm:ss}' AND serialno='{serialNo}'"; + return await _clientSqlSugar.Ado.ExecuteCommandAsync(sql); + } #endregion #region 胎心算法 diff --git a/HealthMonitor.Service/Resolver/PregnancyHeartRateResolver.cs b/HealthMonitor.Service/Resolver/PregnancyHeartRateResolver.cs index 8cc894e..1835b18 100644 --- a/HealthMonitor.Service/Resolver/PregnancyHeartRateResolver.cs +++ b/HealthMonitor.Service/Resolver/PregnancyHeartRateResolver.cs @@ -100,593 +100,606 @@ namespace HealthMonitor.Service.Resolver { var messageId = _messageId.Value; var heartRate = _msgData.Value!; + try + { + - var watchConfig = await _deviceCacheMgr.GetGpsDeviceWatchConfigCacheObjectBySerialNoAsync(heartRate.Serialno, "0067"); - var isFetalHeartEnable = watchConfig != null && (int)watchConfig["enabled"]! == 1; + var watchConfig = await _deviceCacheMgr.GetGpsDeviceWatchConfigCacheObjectBySerialNoAsync(heartRate.Serialno, "0067"); + var isFetalHeartEnable = watchConfig != null && (int)watchConfig["enabled"]! == 1; - if (isFetalHeartEnable) - { - // 高频心率采样间隔 highFreqSampleInterval = highFreqSampleInterval+5,增加5秒兼容 - var highFreqSampleInterval = (int)watchConfig!["highFreqSampleInterval"]!+5; - // 触发高频监测的心率上限值 - 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(heartRate.Serialno, 7); - if (phr.Count >= 30) + if (isFetalHeartEnable) { - #region 计算胎心和胎动数据 - var commonPHR = await _serviceTDengine.GetLastAsync(heartRate.Serialno); - if (commonPHR == null) - { - // 处理孕妇业务,计算一般心率并下发 - commonPHR = await _serviceTDengine.InitPregnancyCommonHeartRateModeAsync(heartRate.Serialno,highFreqSampleInterval: highFreqSampleInterval); - // 建模完成 - var flag = await _serviceIotApi.SetFetalConfig(heartRate.Serialno, 1, commonPHR!.MaxValue, commonPHR!.MinValue); - _logger.LogInformation($"{heartRate.Serialno} 记录数量足够,建模完成"); - // 保存到TDengine数据库 - await _serviceTDengine.InsertAsync("hm_pchr", commonPHR!); - _logger.LogInformation($"保存TDengine完成"); - } - #region 计算胎心数据(按心率时间LastUpdate) - - // 获取最近的两个记录,并计算它们的 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; - - // 高频统计结束时间 - var FreqStatsEnd = DateTime.Now; - // 高频心率启动 - if (timeDiffInSeconds <= highFreqSampleInterval) + // 高频心率采样间隔 highFreqSampleInterval = highFreqSampleInterval+5,增加5秒兼容 + var highFreqSampleInterval = (int)watchConfig!["highFreqSampleInterval"]! + 5; + // 触发高频监测的心率上限值 + 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(heartRate.Serialno, 7); + if (phr.Count >= 30) { - - var phrFreqstatus = await _deviceCacheMgr.GetPregnancyHeartRateFreqStatusAsync(heartRate.Serialno); - if (phrFreqstatus == null) - { - /// 设置高频状态 - _logger.LogInformation($"{heartRate.Serialno} 进入高频心率启动状态 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)) + #region 计算胎心和胎动数据 + var commonPHR = await _serviceTDengine.GetLastAsync(heartRate.Serialno); + if (commonPHR == null) { - var avgPhr = lastPhr.Select(i => i.PregnancyHeartRate).Average(); - // 计算一般心率得到胎心系数 - //await SaveAndPushFreqFetalHeartRateAsync(heartRate, upperAlarmThreshold, lowerAlarmThreshold, avgPhr, DateTimeUtil.ConvertToTimeStamp(DateTime.Now).ToString()); - // 高频数据不建模 - FreqStatsEnd = (DateTime)heartRate.LastUpdate!; - _logger.LogInformation($"{heartRate.Serialno} 高频状态已经持续{(FreqStatsEnd- phrFreqstatus!.LastUpdate).TotalSeconds} 秒,连续 {stopHighFreqSampleCount} 次采样心率正常,将下发指令"); - await SaveAndPushFreqFetalHeartRateAsync(heartRate, commonPHR, upperAlarmThreshold, lowerAlarmThreshold, avgPhr, DateTimeUtil.ConvertToTimeStamp(phrFreqstatus!.LastUpdate).ToString()); - + // 处理孕妇业务,计算一般心率并下发 + commonPHR = await _serviceTDengine.InitPregnancyCommonHeartRateModeAsync(heartRate.Serialno, highFreqSampleInterval: highFreqSampleInterval); + // 建模完成 + var flag = await _serviceIotApi.SetFetalConfig(heartRate.Serialno, 1, commonPHR!.MaxValue, commonPHR!.MinValue); + _logger.LogInformation($"{heartRate.Serialno} 记录数量足够,建模完成"); + // 保存到TDengine数据库 + await _serviceTDengine.InsertAsync("hm_pchr", commonPHR!); + _logger.LogInformation($"保存TDengine完成"); } - #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); - //await SaveAndPushFreqFetalHeartRateAsync(heartRate, upperAlarmThreshold, lowerAlarmThreshold, avgPhr, DateTimeUtil.ConvertToTimeStamp(DateTime.Now).ToString()); - // 高频数据不建模 - FreqStatsEnd = firstTwoPhr[1]; - _logger.LogInformation($"{heartRate.Serialno} 高频状态已经持续{(FreqStatsEnd - phrFreqstatus!.LastUpdate).TotalSeconds} 秒,highFreqSampleTimes={highFreqSampleTimes}秒,即将结束高频状态,将下发指令"); + #region 计算胎心数据(按心率时间LastUpdate) - await SaveAndPushFreqFetalHeartRateAsync(heartRate, commonPHR, upperAlarmThreshold, lowerAlarmThreshold, avgPhr, DateTimeUtil.ConvertToTimeStamp(phrFreqstatus!.LastUpdate).ToString()); + // 获取最近的两个记录,并计算它们的 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; - /// 在highFreqSampleTimes>0一直异常(大于等于triggerHighFreqLow,少于等于triggerHighFreqHig), - /// 取所有值的平均值,推送胎心数据到api/v1/open/OpenIot/SetFetalHeartRateConfig - if (highFreqSampleTimes > 0 && heartRate.LastUpdate >= (phrFreqstatus?.LastUpdate + TimeSpan.FromSeconds(highFreqSampleTimes))) - { + // 高频统计结束时间 + var FreqStatsEnd = DateTime.Now; + // 高频心率启动 + if (timeDiffInSeconds <= highFreqSampleInterval) + { - 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 + var phrFreqstatus = await _deviceCacheMgr.GetPregnancyHeartRateFreqStatusAsync(heartRate.Serialno); + if (phrFreqstatus == null) + { + /// 设置高频状态 + _logger.LogInformation($"{heartRate.Serialno} 进入高频心率启动状态 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); //await SaveAndPushFreqFetalHeartRateAsync(heartRate, upperAlarmThreshold, lowerAlarmThreshold, avgPhr, DateTimeUtil.ConvertToTimeStamp(DateTime.Now).ToString()); - //await SaveAndPushFreqFetalHeartRateAsync(heartRate, commonPHR, upperAlarmThreshold, lowerAlarmThreshold, avgPhr, DateTimeUtil.ConvertToTimeStamp(DateTime.Now).ToString()); // 高频数据不建模 - FreqStatsEnd = firstTwoPhr[1]; - _logger.LogInformation($"{heartRate.Serialno} 高频状态已经持续{(FreqStatsEnd - phrFreqstatus!.LastUpdate).TotalSeconds} 秒,超过规定的 {highFreqSampleTimes} 秒,即将结束高频状态,将下发指令"); - + FreqStatsEnd = (DateTime)heartRate.LastUpdate!; + _logger.LogInformation($"{heartRate.Serialno} 高频状态已经持续{(FreqStatsEnd - phrFreqstatus!.LastUpdate).TotalSeconds} 秒,连续 {stopHighFreqSampleCount} 次采样心率正常,将下发指令"); await SaveAndPushFreqFetalHeartRateAsync(heartRate, commonPHR, upperAlarmThreshold, lowerAlarmThreshold, avgPhr, DateTimeUtil.ConvertToTimeStamp(phrFreqstatus!.LastUpdate).ToString()); - } - - // 删除高频状态的首条记录 - await _deviceCacheMgr.DelPregnancyHeartRateFreqStatusAsync(heartRate.Serialno); - _logger.LogInformation($"{heartRate.Serialno} 结束高频心率状态 timeDiffInSeconds {timeDiffInSeconds},highFreqSampleInterval:{highFreqSampleInterval},高频状态持续{(firstTwoPhr[1] - phrFreqstatus!.LastUpdate).TotalSeconds} 秒"); + } + #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); + //await SaveAndPushFreqFetalHeartRateAsync(heartRate, upperAlarmThreshold, lowerAlarmThreshold, avgPhr, DateTimeUtil.ConvertToTimeStamp(DateTime.Now).ToString()); + // 高频数据不建模 + FreqStatsEnd = firstTwoPhr[1]; + _logger.LogInformation($"{heartRate.Serialno} 高频状态已经持续{(FreqStatsEnd - phrFreqstatus!.LastUpdate).TotalSeconds} 秒,highFreqSampleTimes={highFreqSampleTimes}秒,即将结束高频状态,将下发指令"); + + await SaveAndPushFreqFetalHeartRateAsync(heartRate, commonPHR, upperAlarmThreshold, lowerAlarmThreshold, avgPhr, DateTimeUtil.ConvertToTimeStamp(phrFreqstatus!.LastUpdate).ToString()); - // 上15分钟的数据 - // 获取当前时间 - DateTime nowInterval = (DateTime)heartRate.LastUpdate!; + } + + /// 在highFreqSampleTimes>0一直异常(大于等于triggerHighFreqLow,少于等于triggerHighFreqHig), + /// 取所有值的平均值,推送胎心数据到api/v1/open/OpenIot/SetFetalHeartRateConfig + if (highFreqSampleTimes > 0 && heartRate.LastUpdate >= (phrFreqstatus?.LastUpdate + TimeSpan.FromSeconds(highFreqSampleTimes))) + { + var filterPhr = phr + .Where(i => i.Timestamp >= phrFreqstatus?.Timestamp); - // 计算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(); + var avgPhr = filterPhr + .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 SaveAndPushFreqFetalHeartRateAsync(heartRate, upperAlarmThreshold, lowerAlarmThreshold, avgPhr, DateTimeUtil.ConvertToTimeStamp(DateTime.Now).ToString()); + //await SaveAndPushFreqFetalHeartRateAsync(heartRate, commonPHR, upperAlarmThreshold, lowerAlarmThreshold, avgPhr, DateTimeUtil.ConvertToTimeStamp(DateTime.Now).ToString()); + // 高频数据不建模 + FreqStatsEnd = firstTwoPhr[1]; + _logger.LogInformation($"{heartRate.Serialno} 高频状态已经持续{(FreqStatsEnd - phrFreqstatus!.LastUpdate).TotalSeconds} 秒,超过规定的 {highFreqSampleTimes} 秒,即将结束高频状态,将下发指令"); - // 计算last_update到下一间隔的分钟数 - int minutesToAdd = intervalFHR - (nowInterval.Minute % intervalFHR); - if (minutesToAdd == intervalFHR) - { - minutesToAdd = 0; // 如果已经是间隔,则不需要增加分钟 + await SaveAndPushFreqFetalHeartRateAsync(heartRate, commonPHR, upperAlarmThreshold, lowerAlarmThreshold, avgPhr, DateTimeUtil.ConvertToTimeStamp(phrFreqstatus!.LastUpdate).ToString()); + } + + // 删除高频状态的首条记录 + await _deviceCacheMgr.DelPregnancyHeartRateFreqStatusAsync(heartRate.Serialno); + + _logger.LogInformation($"{heartRate.Serialno} 结束高频心率状态 timeDiffInSeconds {timeDiffInSeconds},highFreqSampleInterval:{highFreqSampleInterval},高频状态持续{(firstTwoPhr[1] - phrFreqstatus!.LastUpdate).TotalSeconds} 秒"); } + // 正常心率 + else + { - // 计算下一间隔的时间 - DateTime nextInterval = nowInterval.AddMinutes(minutesToAdd) - .AddSeconds(-nowInterval.Second) - .AddMilliseconds(-nowInterval.Millisecond); + // 上15分钟的数据 + // 获取当前时间 + DateTime nowInterval = (DateTime)heartRate.LastUpdate!; + // 计算last_update到上一间隔的分钟数 + int minutesToSubtract = nowInterval.Minute % intervalFHR; - var daysPhr = await _serviceTDengine.GetBySerialNoAsync(heartRate.Serialno, 7); + // 计算上一间隔的时间 + DateTime previousInterval = nowInterval.AddMinutes(-minutesToSubtract).AddSeconds(-nowInterval.Second).AddMilliseconds(-nowInterval.Millisecond); - var normalPhrStatStartTime = nextInterval; + // 使用 last_update 上一刻 + var sampleTimeFHR = DateTimeUtil.ConvertToTimeStamp(previousInterval).ToString(); - var normalPhrStatEndTime = nextInterval.AddMinutes(-intervalFHR); + // 计算last_update到下一间隔的分钟数 + int minutesToAdd = intervalFHR - (nowInterval.Minute % intervalFHR); + if (minutesToAdd == intervalFHR) + { + minutesToAdd = 0; // 如果已经是间隔,则不需要增加分钟 + } - _logger.LogInformation($"{heartRate.Serialno} 计算胎心数据, 周期:{normalPhrStatStartTime}-{normalPhrStatEndTime} "); - var filteredPhr = daysPhr - // 使用 last_update 下一刻 - .Where(i => i.LastUpdate <= normalPhrStatStartTime && i.LastUpdate >= normalPhrStatEndTime) - .ToList(); - if (filteredPhr.Count == 0) - { - _logger.LogWarning($"{heartRate.Serialno} 周期:{normalPhrStatStartTime}-{normalPhrStatEndTime} 孕妇心率数据不足,{filteredPhr.Count}条记录"); - return; - } - var phrValue = filteredPhr.Count == 1 - ? filteredPhr.First().PregnancyHeartRate - : filteredPhr.Average(i => i.PregnancyHeartRate); + // 计算下一间隔的时间 + DateTime nextInterval = nowInterval.AddMinutes(minutesToAdd) + .AddSeconds(-nowInterval.Second) + .AddMilliseconds(-nowInterval.Millisecond); - var fetalHeartRate = SafeType.SafeInt(phrValue * commonPHR?.StatModeAvgFprCoefficient!); + var daysPhr = await _serviceTDengine.GetBySerialNoAsync(heartRate.Serialno, 7); - 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); - - // 推送送微信 - if (fetalHeartRateIsAbnormal != 0) - { - var device = await _deviceCacheMgr.GetDeviceBySerialNoAsync(heartRate.Serialno).ConfigureAwait(false); - var fhrMsgId = $"{heartRate.Serialno}-{sampleTimeFHR}-{Guid.NewGuid().ToString("D")[^3..]}"; - var topic = "topic.push.wx"; - var fhrMsg = new - { - messageId = fhrMsgId, - topic = topic, - time = DateTimeUtil.GetDateTimeFromUnixTimeMilliseconds(long.Parse(sampleTimeFHR.Length < 13 ? sampleTimeFHR.PadRight(13, '0') : sampleTimeFHR)).ToString("yyyy-MM-dd HH:mm:ss"), - data = new - { - deviceId = device?.DeviceId, - imei = heartRate.Serialno, - alarmTypeId = 12, - alarmDeviceName = heartRate.Serialno, - alarmRemarks = JsonConvert.SerializeObject(new { fetalHeartValue = fetalHeartRate, isAbnormal = fetalHeartRateIsAbnormal }), - address = string.Empty, - deviceKey = device?.DeviceId - } - }; - await _serviceMqProcess.ProcessIMEIEventMessageAsync(fhrMsgId, topic, fhrMsg).ConfigureAwait(false); - } + var normalPhrStatStartTime = nextInterval; + + var normalPhrStatEndTime = nextInterval.AddMinutes(-intervalFHR); - */ - await SaveAndPushFreqFetalHeartRateAsync(heartRate, commonPHR!, upperAlarmThreshold, lowerAlarmThreshold, phrValue, sampleTimeFHR,false); + _logger.LogInformation($"{heartRate.Serialno} 计算胎心数据, 周期:{normalPhrStatStartTime}-{normalPhrStatEndTime} "); + var filteredPhr = daysPhr + // 使用 last_update 下一刻 + .Where(i => i.LastUpdate <= normalPhrStatStartTime && i.LastUpdate >= normalPhrStatEndTime) + .ToList(); + if (filteredPhr.Count == 0) + { + _logger.LogWarning($"{heartRate.Serialno} 周期:{normalPhrStatStartTime}-{normalPhrStatEndTime} 孕妇心率数据不足,{filteredPhr.Count}条记录"); + return; + } + 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); + + // 推送送微信 + if (fetalHeartRateIsAbnormal != 0) + { + var device = await _deviceCacheMgr.GetDeviceBySerialNoAsync(heartRate.Serialno).ConfigureAwait(false); + var fhrMsgId = $"{heartRate.Serialno}-{sampleTimeFHR}-{Guid.NewGuid().ToString("D")[^3..]}"; + var topic = "topic.push.wx"; + var fhrMsg = new + { + messageId = fhrMsgId, + topic = topic, + time = DateTimeUtil.GetDateTimeFromUnixTimeMilliseconds(long.Parse(sampleTimeFHR.Length < 13 ? sampleTimeFHR.PadRight(13, '0') : sampleTimeFHR)).ToString("yyyy-MM-dd HH:mm:ss"), + data = new + { + deviceId = device?.DeviceId, + imei = heartRate.Serialno, + alarmTypeId = 12, + alarmDeviceName = heartRate.Serialno, + alarmRemarks = JsonConvert.SerializeObject(new { fetalHeartValue = fetalHeartRate, isAbnormal = fetalHeartRateIsAbnormal }), + address = string.Empty, + deviceKey = device?.DeviceId + } + }; + await _serviceMqProcess.ProcessIMEIEventMessageAsync(fhrMsgId, topic, fhrMsg).ConfigureAwait(false); + } + + */ + await SaveAndPushFreqFetalHeartRateAsync(heartRate, commonPHR!, upperAlarmThreshold, lowerAlarmThreshold, phrValue, sampleTimeFHR, false); + } } - } - #endregion + #endregion - #region 计算胎动数据(按心率时间LastUpdate) - /** - _logger.LogInformation($"{heartRate.Serialno} 计算胎动数据 "); + #region 计算胎动数据(按心率时间LastUpdate) + /** + _logger.LogInformation($"{heartRate.Serialno} 计算胎动数据 "); - var fetalMovementNow = (DateTime)heartRate.LastUpdate!; + var fetalMovementNow = (DateTime)heartRate.LastUpdate!; - var midNight = new DateTime(fetalMovementNow.Year, fetalMovementNow.Month, fetalMovementNow.Day, 0, 0, 0); + var midNight = new DateTime(fetalMovementNow.Year, fetalMovementNow.Month, fetalMovementNow.Day, 0, 0, 0); - TimeSpan fetalMovementTS = fetalMovementNow - midNight; + TimeSpan fetalMovementTS = fetalMovementNow - midNight; - // 当天每隔2小时的段数,取整数部分 - int segmentCountFM = (int)(fetalMovementTS.TotalHours / 2); + // 当天每隔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) + for (int i = 0; i < segmentCountFM; i++) { - /// 开始计算 - 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 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 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 phrRange = phr.Where(i => i.LastUpdate >= statStartTime && i.LastUpdate<= statEndTime) + .OrderByDescending(i => i.LastUpdate) + .Select(i => i.LastUpdate) + .ToList(); + // 判断是否有持续佩戴 + if (phrRange.Count >= 2) { - 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); - - // 发送到微信 - if (feltalMovementIsAbnormal != 0) + 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 device = await _deviceCacheMgr.GetDeviceBySerialNoAsync(heartRate.Serialno).ConfigureAwait(false); - var fmMsgId = $"{heartRate.Serialno}-{fetalMovementSampleTime}-{Guid.NewGuid().ToString("D")[^3..]}"; - var topic = "topic.push.wx"; - var fmMsg = new + 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() { - messageId = Guid.NewGuid().ToString("D"), - topic = topic, - time = DateTimeUtil.GetDateTimeFromUnixTimeMilliseconds(long.Parse(fetalMovementSampleTime.Length < 13 ? fetalMovementSampleTime.PadRight(13, '0') : fetalMovementSampleTime)).ToString("yyyy-MM-dd HH:mm:ss"), - data = new - { - deviceId = device?.DeviceId, - imei = heartRate.Serialno, - alarmTypeId = 12, - alarmDeviceName = heartRate.Serialno, - alarmRemarks = JsonConvert.SerializeObject(new { fetalMovementValue = fetalMovement, isAbnormal = feltalMovementIsAbnormal }), - address = string.Empty, - deviceKey = device?.DeviceId - } + 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 _serviceMqProcess.ProcessIMEIEventMessageAsync(fmMsgId, topic, fmMsg).ConfigureAwait(false); + await _hisFetalMovementApiClient.AddAsync(fm).ConfigureAwait(false); + + // 发送到微信 + if (feltalMovementIsAbnormal != 0) + { + var device = await _deviceCacheMgr.GetDeviceBySerialNoAsync(heartRate.Serialno).ConfigureAwait(false); + var fmMsgId = $"{heartRate.Serialno}-{fetalMovementSampleTime}-{Guid.NewGuid().ToString("D")[^3..]}"; + var topic = "topic.push.wx"; + var fmMsg = new + { + messageId = Guid.NewGuid().ToString("D"), + topic = topic, + time = DateTimeUtil.GetDateTimeFromUnixTimeMilliseconds(long.Parse(fetalMovementSampleTime.Length < 13 ? fetalMovementSampleTime.PadRight(13, '0') : fetalMovementSampleTime)).ToString("yyyy-MM-dd HH:mm:ss"), + data = new + { + deviceId = device?.DeviceId, + imei = heartRate.Serialno, + alarmTypeId = 12, + alarmDeviceName = heartRate.Serialno, + alarmRemarks = JsonConvert.SerializeObject(new { fetalMovementValue = fetalMovement, isAbnormal = feltalMovementIsAbnormal }), + address = string.Empty, + deviceKey = device?.DeviceId + } + }; + await _serviceMqProcess.ProcessIMEIEventMessageAsync(fmMsgId, topic, fmMsg).ConfigureAwait(false); + } + // 设置入库缓存记录 + await _deviceCacheMgr.SetFetalMovementAsync(heartRate.Serialno, fetalMovementSampleTime,fm); + + } + else + { + _logger.LogWarning($"{heartRate.Serialno} 孕周 {pregnancyWeeks},超出胎动计算范围"); } - // 设置入库缓存记录 - await _deviceCacheMgr.SetFetalMovementAsync(heartRate.Serialno, fetalMovementSampleTime,fm); - } else { - _logger.LogWarning($"{heartRate.Serialno} 孕周 {pregnancyWeeks},超出胎动计算范围"); + _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} 不足两条,不能判断是否持续佩戴"); - + _logger.LogInformation($"{heartRate.Serialno} 胎动记录{isFetalMovementExisted},数据采样时间:{fetalMovementSampleTime}|{midNight.AddHours(2 * i).ToString("yyyy-MM-dd HH:mm:ss")}, 周期:{statStartTime}-{statEndTime} 已处理"); } + } - else + */ + #endregion + + #endregion + + #region 定时计算胎动数据触发器两小时间隔开始 + var fetalMovementKey = $"health_monitor/schedule_push/cal_fetal_movement/imei/{heartRate.Serialno}"; + ///// 计算 0 点秒数 + var fetalMovementLastUpdate = (DateTime)heartRate.LastUpdate!; + DateTime fmScheduleNow = DateTime.Now; + // 小于两小时 + if (fmScheduleNow > fetalMovementLastUpdate && (fmScheduleNow - fetalMovementLastUpdate).TotalHours <= 2) { - _logger.LogInformation($"{heartRate.Serialno} 胎动记录{isFetalMovementExisted},数据采样时间:{fetalMovementSampleTime}|{midNight.AddHours(2 * i).ToString("yyyy-MM-dd HH:mm:ss")}, 周期:{statStartTime}-{statEndTime} 已处理"); + var rand = new Random(); + var pushSec = rand.Next(59); + int pushMin = int.TryParse(heartRate.Serialno.AsSpan(heartRate.Serialno.Length - 1), out pushMin) ? pushMin : 10; + + var scheduleHourDiff = SCHEDULE_HOUR + .Where(h => h > fetalMovementLastUpdate.Hour) + .OrderBy(h => h - fetalMovementLastUpdate.Hour) + .FirstOrDefault() - fetalMovementLastUpdate.Hour; + var scheduleTime = fetalMovementLastUpdate.AddHours(scheduleHourDiff); + + DateTime nextRunTime = new(scheduleTime.Year, scheduleTime.Month, scheduleTime.Day, scheduleTime.Hour, pushMin, pushSec); + TimeSpan timeUntilNextRun = nextRunTime - fmScheduleNow; + var ttl = (long)timeUntilNextRun.TotalSeconds; + + await SetIntervalTriggerAsync(fetalMovementKey, heartRate.Serialno, ttl); } + #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 高频心率计算 + + //// 获取最近的两个记录,并计算它们的 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 + } + else + { + // 建模中 + //var flag = await _serviceIotApi.SetFetalConfig(heartRate.Serialno); + _logger.LogInformation($"{heartRate.Serialno} 记录不足30条,建模中"); } - */ - #endregion - #endregion + #region 定时下发触发器(定时建模) + var key = $"health_monitor/schedule_push/pregnancy_heart_rate/imei/{heartRate.Serialno}"; + var schedule_push = await _serviceEtcd.GetValAsync(key).ConfigureAwait(false); - #region 定时计算胎动数据触发器两小时间隔开始 - var fetalMovementKey = $"health_monitor/schedule_push/cal_fetal_movement/imei/{heartRate.Serialno}"; - ///// 计算 0 点秒数 - var fetalMovementLastUpdate = (DateTime)heartRate.LastUpdate!; - DateTime fmScheduleNow = DateTime.Now; - // 小于两小时 - if (fmScheduleNow > fetalMovementLastUpdate && (fmScheduleNow - fetalMovementLastUpdate).TotalHours <= 2) + if (string.IsNullOrWhiteSpace(schedule_push)) { + // 注册首次下推 + 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, 6, pushMin, pushSec).AddDays(interval); + TimeSpan timeUntilNextRun = nextRunTime - now; - var scheduleHourDiff = SCHEDULE_HOUR - .Where(h => h > fetalMovementLastUpdate.Hour) - .OrderBy(h => h - fetalMovementLastUpdate.Hour) - .FirstOrDefault() - fetalMovementLastUpdate.Hour; - var scheduleTime = fetalMovementLastUpdate.AddHours(scheduleHourDiff); + if (timeUntilNextRun < TimeSpan.Zero) + { + timeUntilNextRun = timeUntilNextRun.Add(TimeSpan.FromDays(1)); + nextRunTime += TimeSpan.FromDays(1); + } - DateTime nextRunTime = new(scheduleTime.Year, scheduleTime.Month, scheduleTime.Day, scheduleTime.Hour, pushMin, pushSec); - TimeSpan timeUntilNextRun = nextRunTime - fmScheduleNow; 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); - await SetIntervalTriggerAsync(fetalMovementKey, heartRate.Serialno, ttl); } - #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 高频心率计算 - - //// 获取最近的两个记录,并计算它们的 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 - } - else - { - // 建模中 - //var flag = await _serviceIotApi.SetFetalConfig(heartRate.Serialno); - _logger.LogInformation($"{heartRate.Serialno} 记录不足30条,建模中"); - } - - #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)) - { - // 注册首次下推 - 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, 6, 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); - } - #endregion } + catch (Exception ex) + { + _logger.LogError($"{heartRate.Serialno} 处理孕妇心率数据异常 \n{ex.Message}\n{ex.StackTrace}"); + } + } //private async Task SaveAndPushFetalHeartRateAsync(HisGpsHeartRate heartRate, int upperAlarmThreshold, int lowerAlarmThreshold, double avgPhr) diff --git a/HealthMonitor.WebApi/Controllers/HealthMonitor/HmFetalController.cs b/HealthMonitor.WebApi/Controllers/HealthMonitor/HmFetalController.cs index f62f5a8..b020f29 100644 --- a/HealthMonitor.WebApi/Controllers/HealthMonitor/HmFetalController.cs +++ b/HealthMonitor.WebApi/Controllers/HealthMonitor/HmFetalController.cs @@ -2,8 +2,11 @@ using HealthMonitor.Service.Biz; using HealthMonitor.Service.Biz.db; using HealthMonitor.WebApi.Configs; +using HealthMonitor.WebApi.Controllers.Api; +using HealthMonitor.WebApi.Model.Request; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore.Metadata; +using SqlSugar.TDengine; namespace HealthMonitor.WebApi.Controllers.HealthMonitor { @@ -99,5 +102,27 @@ namespace HealthMonitor.WebApi.Controllers.HealthMonitor }; } + [HttpPost] + public async Task> CleanFetal([FromHeader] string requestId, [FromBody] BaseReq req) + { + try + { + // 删除孕妇心率 + await _serviceTDengine.DeleteAllBySerialNoCMDAsync(req.Serialno); + // 删除一般心率(建模数据) + await _serviceTDengine.DeleteAllBySerialNoCMDAsync(req.Serialno); + + var data = $"{req.Serialno} 清理胎心胎动建模数据成功"; + return ApiResponse.Success(data); + } + catch (Exception ex) + { + _logger.LogError($"{req.Serialno} 清理胎心胎动数据异常; {ex.Message}\n {ex.InnerException} \n{ex.StackTrace}"); + + } + + return ApiResponse.Fail(500, $"{req.Serialno} 清理胎心胎动数据异常"); + + } } } diff --git a/HealthMonitor.WebApi/Model/Request/BaseReq.cs b/HealthMonitor.WebApi/Model/Request/BaseReq.cs new file mode 100644 index 0000000..b067407 --- /dev/null +++ b/HealthMonitor.WebApi/Model/Request/BaseReq.cs @@ -0,0 +1,10 @@ +using Newtonsoft.Json; + +namespace HealthMonitor.WebApi.Model.Request +{ + public class BaseReq + { + [JsonProperty("serialno")] + public string Serialno { get; set; } = default!; + } +} diff --git a/HealthMonitor.WebApi/Worker.cs b/HealthMonitor.WebApi/Worker.cs index 69c99b7..24017b2 100644 --- a/HealthMonitor.WebApi/Worker.cs +++ b/HealthMonitor.WebApi/Worker.cs @@ -441,11 +441,10 @@ namespace HealthMonitor.WebApi #region 胎动延时计算 var watchConfig = await _deviceCacheMgr.GetGpsDeviceWatchConfigCacheObjectBySerialNoAsync(imeiDel, "0067"); + _logger.LogInformation($"触发胎动计算,设备配置{JsonConvert.SerializeObject(watchConfig)}"); var isFetalHeartEnable = watchConfig != null && (int)watchConfig["enabled"]! == 1; if (isFetalHeartEnable) { - - var edoc = DateTimeUtil.ToDateTime(watchConfig!["EDOC"]!.ToString()); // 已经建模 var commonPHR = await _serviceTDengine.GetLastAsync(imeiDel);