|
- using dotnet_etcd;
- using Etcdserverpb;
- using Google.Protobuf.WellKnownTypes;
- using HealthMonitor.Common;
- using HealthMonitor.Common.helper;
- using HealthMonitor.Core.Common.Extensions;
- using HealthMonitor.Core.Pipeline;
- using HealthMonitor.Model.Config;
- using HealthMonitor.Model.Service;
- 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.MessageQueue;
- using HealthMonitor.Service.Sub;
- using Microsoft.AspNetCore.Mvc.RazorPages;
- using Microsoft.EntityFrameworkCore.Metadata.Internal;
- using Microsoft.Extensions.Options;
- using Newtonsoft.Json;
- using Newtonsoft.Json.Linq;
- using System;
- using System.Reflection;
- using System.Threading.Channels;
- using TDengineDriver;
- using TDengineTMQ;
- using TelpoDataService.Util.Clients;
- using TelpoDataService.Util.Entities.GpsCard;
- using TelpoDataService.Util.Entities.GpsLocationHistory;
- using TelpoDataService.Util.Models;
- using TelpoDataService.Util.QueryObjects;
-
- namespace HealthMonitor.WebApi
- {
- public class Worker : BackgroundService
- {
- private readonly ILogger<Worker> _logger;
- private readonly IServiceProvider _services;
- private readonly TDengineDataSubcribe _tdEngineDataSubcribe;
- private readonly PackageProcess _processor;
- private readonly TDengineService _serviceTDengine;
- private readonly EtcdService _serviceEtcd;
- private readonly HttpHelper _httpHelper = default!;
- private readonly IotApiService _serviceIotApi;
- private readonly BoodPressResolverConfig _configBoodPressResolver;
- private readonly BloodPressReferenceValueCacheManager _bpRefValCacheManager;
- private readonly PersonCacheManager _personCacheMgr;
- private readonly DeviceCacheManager _deviceCacheMgr;
- private readonly FetalMovementNormalValueRangeCacheManager _mgrFetalMovementNormalValueRangeCache;
-
- private readonly GpsLocationHistoryAccessorClient<HisGpsFetalHeartRate> _hisFetalHeartApiClient;
- private readonly GpsLocationHistoryAccessorClient<HisGpsFetalMovement> _hisFetalMovementApiClient;
-
- private readonly MqProcessLogic _serviceMqProcess;
-
- private CancellationTokenSource _tokenSource = default!;
-
- public Worker(ILogger<Worker> logger, IServiceProvider services, PersonCacheManager personCacheMgr,
- BloodPressReferenceValueCacheManager bpRefValCacheManager, IotApiService IotApiService,
- IOptions<BoodPressResolverConfig> optionBoodPressResolver, PackageProcess processor,
- TDengineDataSubcribe tdEngineDataSubcribe, TDengineService serviceDengine,
- GpsLocationHistoryAccessorClient<HisGpsFetalHeartRate> hisFetalHeartApiClient,
- GpsLocationHistoryAccessorClient<HisGpsFetalMovement> hisFetalMovementApiClient,
- FetalMovementNormalValueRangeCacheManager fetalMovementNormalValueRangeCacheMgr, MqProcessLogic serviceMqProcess,
- HttpHelper httpHelper, EtcdService serviceEtcd, DeviceCacheManager deviceCacheMgr)
- {
- _logger = logger;
- _tdEngineDataSubcribe = tdEngineDataSubcribe;
- _services = services;
- _serviceIotApi = IotApiService;
- _processor = processor;
- _serviceEtcd = serviceEtcd;
- _serviceTDengine = serviceDengine;
- _httpHelper = httpHelper;
- _configBoodPressResolver = optionBoodPressResolver.Value;
- _bpRefValCacheManager = bpRefValCacheManager;
- _personCacheMgr = personCacheMgr;
- _deviceCacheMgr = deviceCacheMgr;
- _hisFetalHeartApiClient = hisFetalHeartApiClient;
- _hisFetalMovementApiClient = hisFetalMovementApiClient;
- _serviceMqProcess = serviceMqProcess;
- _mgrFetalMovementNormalValueRangeCache = fetalMovementNormalValueRangeCacheMgr;
- }
-
- public override Task StartAsync(CancellationToken cancellationToken)
- {
- //_logger.LogInformation("------StartAsync");
- _tokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
- return base.StartAsync(cancellationToken);
- }
-
- public override Task StopAsync(CancellationToken cancellationToken)
- {
- //_logger.LogInformation("------StopAsync");
- _tokenSource.Cancel(); //停止工作线程
- return base.StopAsync(cancellationToken);
- }
- protected override async Task ExecuteAsync(CancellationToken stoppingToken)
- {
- var tasks = new[]
- {
- Task.Run(async () =>
- {
- _logger.LogInformation("解析器启动");
- while (!stoppingToken.IsCancellationRequested)
- {
- await _processor.ResolveAsync().ConfigureAwait(false);
- }
- }, stoppingToken),
-
- Task.Run(() =>
- {
- _logger.LogInformation("TDengine 订阅启动");
- while (!stoppingToken.IsCancellationRequested)
- {
- _tdEngineDataSubcribe.BeginListen(stoppingToken);
- }
- }, stoppingToken),
-
- Task.Run(() =>
- _serviceEtcd.WacthKeysWithPrefixResponseAsync("health_monitor/schedule_push", WatchEvents),
- stoppingToken)
- };
-
- await Task.WhenAll(tasks);
- }
-
- private void WatchEvents(WatchResponse response)
- {
-
- response.Events.ToList<Mvccpb.Event>().ForEach(async e =>
- {
- try
- {
- switch (e.Type.ToString())
- {
- case "Put":
- // 获取时间点计算TTL
- Console.BackgroundColor = ConsoleColor.Blue;
- Console.WriteLine($"--- {e.Type}--{e.Kv.Key.ToStringUtf8()}--{e.Kv.Value.ToStringUtf8()}---{DateTime.Now}");
- Console.BackgroundColor = ConsoleColor.Black;
- break;
-
- case "Delete":
- // TTL到了重新计算TTL,下发
- //Console.BackgroundColor = ConsoleColor.Green;
- //Console.WriteLine($"--- {e.Type}--{e.Kv.Key.ToStringUtf8()}--{e.Kv.Value.ToStringUtf8()}---{DateTime.Now}");
-
- // var key = $"health_monitor/schedule_push/imei/{bp.Serialno}";
- var key = e.Kv.Key.ToStringUtf8();
- var imeiDel = key.Split('/')[^1];
- var schedule_push = await _serviceEtcd.GetValAsync(key).ConfigureAwait(false);
- if (string.IsNullOrWhiteSpace(schedule_push))
- {
- if (key.Contains("pregnancy_heart_rate"))
- {
- // 处理孕妇业务,计算一般心率并下发
- var commonPHR= await _serviceTDengine.InitPregnancyCommonHeartRateModeAsync(imeiDel);
- // 设置胎心监测参数
-
- if (commonPHR == null)
- {
- // 建模中
- var flag= await _serviceIotApi.SetFetalConfig(imeiDel);
- _logger.LogInformation($"{imeiDel} 建模建模中");
- }
- else
- {
- // 建模完成
- var flag = await _serviceIotApi.SetFetalConfig(imeiDel,1,commonPHR.MaxValue,commonPHR.MinValue);
- _logger.LogInformation($"{imeiDel} 建模完成");
- // 保存到TDengine数据库
- await _serviceTDengine.InsertAsync<PregnancyCommonHeartRateModel>("hm_pchr", commonPHR);
- _logger.LogInformation($"保存TDengine完成");
-
- //
- }
-
- #region 注册定时下发
- var startTime = DateTime.Now;
- // 注册下次下推
- var endTime = DateTime.Now;
-
- #if DEBUG
-
-
- //long ttl = (long)((60 * 1000-(endTime-startTime).TotalMilliseconds)/1000);
- //await _serviceEtcd.PutValAsync(key, imeiDel,ttl, 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 + 2, 0).AddDays(interval);
- TimeSpan timeUntilNextRun = nextRunTime - now;
-
- // 如果当前时间已经超过了8点,将等待到明天后的8点
- if (timeUntilNextRun < TimeSpan.Zero)
- {
- timeUntilNextRun = timeUntilNextRun.Add(TimeSpan.FromMinutes(1));
- nextRunTime += timeUntilNextRun;
- }
-
- //long ttl = timeUntilNextRun.Milliseconds/1000;
- long ttl = (long)((timeUntilNextRun.TotalMilliseconds - (endTime - startTime).TotalMilliseconds) / 1000);
- var data = new
- {
- imei = imeiDel,
- 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
- // 每$interval天,晚上8点
- var interval = 1;
- // 获取当前时间
- DateTime now = DateTime.Now;
- var rand = new Random();
-
- var pushSec = rand.Next(59);
- int pushMin = int.TryParse(imeiDel.AsSpan(imeiDel.Length - 1), out pushMin) ? pushMin : 10;
- // 计算距离下一个$interval天后的0点的时间间隔
- DateTime nextRunTime = new DateTime(now.Year, now.Month, now.Day, 6, pushMin, pushSec).AddDays(interval);
- TimeSpan timeUntilNextRun = nextRunTime - now;
-
- // 如果当前时间已经超过了8点,将等待到明天后的8点
- if (timeUntilNextRun < TimeSpan.Zero)
- {
- timeUntilNextRun = timeUntilNextRun.Add(TimeSpan.FromDays(1));
- nextRunTime += timeUntilNextRun;
- }
-
- // var ttl = timeUntilNextRun.TotalMilliseconds;
- long ttl = (long)((timeUntilNextRun.TotalMilliseconds-(endTime-startTime).TotalMilliseconds)/1000);
- var data = new
- {
- imei = imeiDel,
- 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
- }
- // health_monitor/schedule_push/cal_fetal_heart_rate/imei/
- else if (key.Contains("health_monitor/schedule_push/cal_fetal_heart_rate/imei/"))
- {
- /**
- var watchConfig = await _deviceCacheMgr.GetGpsDeviceWatchConfigCacheObjectBySerialNoAsync(imeiDel, "0067");
- var isFetalHeartEnable = watchConfig != null && (int)watchConfig["enabled"]! == 1;
-
- if (isFetalHeartEnable)
- {
- // 处理胎心计算业务,计算一般心率获取胎心系数
- var commonPHR = await _serviceTDengine.InitPregnancyCommonHeartRateModeAsync(imeiDel);
- if (commonPHR != null)
- {
- # region 计算上刻钟的胎心数据
- // 告警上限阀值
- var upperAlarmThreshold = (int)watchConfig!["upperAlarmThreshold"]!;
- // 告警下限阀值
- var lowerAlarmThreshold = (int)watchConfig["lowerAlarmThreshold"]!;
-
- //var lastPhr = await _serviceTDengine.GetLastAsync<PregnancyHeartRateModel>();
- // 计算胎心=孕妇心率*系数
- //var fetalHeartRate = SafeType.SafeInt(lastPhr.PregnancyHeartRate * commonPHR?.StatModeAvgFprCoefficient!);
-
- // 上15分钟的数据
- // 获取当前时间
- DateTime now = DateTime.Now;
-
- // 计算当前时间到上一刻钟的分钟数
- int minutesToSubtract = now.Minute % 15;
-
- // 计算上一刻钟的时间
- DateTime previousQuarter = now.AddMinutes(-minutesToSubtract).AddSeconds(-now.Second).AddMilliseconds(-now.Millisecond);
-
- var lastQuarterPhr = await _serviceTDengine.GetBySerialNoAsync<PregnancyHeartRateModel>(imeiDel, 1);
-
- var filteredPhr = lastQuarterPhr
- .Where(i => i.LastUpdate <= previousQuarter && i.LastUpdate >= previousQuarter.AddMinutes(-15))
- .ToList();
-
- var lastQuarterPhrValue = filteredPhr.Count == 1
- ? filteredPhr.First().PregnancyHeartRate
- : filteredPhr.Average(i => i.PregnancyHeartRate);
-
- var fetalHeartRate = SafeType.SafeInt(lastQuarterPhrValue * commonPHR?.StatModeAvgFprCoefficient!);
- var sampleTime = DateTimeUtil.ConvertToTimeStamp(DateTime.Now).ToString();
-
- var isAbnormal = fetalHeartRate > upperAlarmThreshold ? 1 : (fetalHeartRate < lowerAlarmThreshold ? 2 : 0);
-
- HisGpsFetalHeartRate gpsFetalHeartRate = new()
- {
- FetalHeartRateId = Guid.NewGuid().ToString("D"),
- PersonId = commonPHR!.PersonId,
- Serialno = imeiDel,
- HeartRate = fetalHeartRate,
- SampleTime = sampleTime,
- IsAbnormal = isAbnormal,
- StatStartTime = filteredPhr.OrderByDescending(i=>i.LastUpdate).First().LastUpdate,
- StatEndTime = filteredPhr.OrderByDescending(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(imeiDel, fetalHeartRate, sampleTime, isAbnormal);
- #endregion
-
- #region 注册定时计算胎心数据触发器 {interval} 秒后
- //await SetIntervalTriggerAsync(key, imeiDel, 60 * 15);
- #endregion
- }
- }
- */
-
- }
-
- //health_monitor/schedule_push/cal_fetal_movement/imei/
- else if (key.Contains("health_monitor/schedule_push/cal_fetal_movement/imei/"))
- {
- //// 计算胎动数据
- //var watchConfig = await _deviceCacheMgr.GetGpsDeviceWatchConfigCacheObjectBySerialNoAsync(imeiDel, "0067");
- //var isFetalHeartEnable = watchConfig != null && (int)watchConfig["enabled"]! == 1;
- //if (isFetalHeartEnable)
- //{
- // // 检查胎心建模
- // var fchr = await _serviceTDengine.GetLastAsync<PregnancyCommonHeartRateModel>(imeiDel);
- // if (fchr != null)
- // {
- // // 获取孕妇心率数据接近最近2小时的数据
- // var phr = await _serviceTDengine.GetBySerialNoAsync<PregnancyHeartRateModel>(imeiDel, 7);
- // var now = DateTime.Now;
- // var ago2hrs = now.AddHours(-2);
- // var phrRange = phr.Where(i => i.LastUpdate >= ago2hrs)
- // .OrderByDescending(i => i.LastUpdate)
- // .Select(i => i.LastUpdate)
- // .ToList();
-
- // if (phrRange.Count >= 2)
- // {
- // var duringMins = (phrRange.First() - phrRange.Last()).TotalMinutes;
- // //在餐后时间段(8:00~10:00,12:00~14:00,18:00~20:00,22:00~24:00)取中间值。其他时间段取正常起始值
- // bool isInTimeRanges = IsNowInTimeRanges();
-
- // //long.TryParse(watchConfig!["EDOC"]!.ToString(), out long edoc);
- // var edoc = DateTimeUtil.ToDateTime(watchConfig!["EDOC"]!.ToString());
-
- // 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);
-
- // var sampleTime = DateTimeUtil.ConvertToTimeStamp(DateTime.Now).ToString();
- // // 告警上限阀值
- // var upperAlarmThreshold = (int)watchConfig["upperAlarmThreshold"]!;
- // // 告警下限阀值
- // var lowerAlarmThreshold = (int)watchConfig["lowerAlarmThreshold"]!;
-
- // GeneralParam param = new()
- // {
- // Filters = new List<QueryFilterCondition>
- // {
- // new ()
- // {
- // Key=nameof(HisGpsFetalimeiDel),
- // Value=imeiDel,
- // ValueType=QueryValueTypeEnum.String,
- // Operator=QueryOperatorEnum.Equal
- // }
- // },
- // OrderBys = new List<OrderByCondition>
- // {
- // new() {
- // IsDesc=true,
- // Key=nameof(HisGpsFetalHeartRate.CreateTime)
- // }
- // }
- // };
- // var frh = await _hisFetalHeartApiClient.GetFirstAsync(param, imeiDel.Substring(imeiDel.Length - 2), null, new RequestHeader { RequestId = Guid.NewGuid().ToString("D") }).ConfigureAwait(false);
- // if (frh != null)
- // {
- // var fetalHeartRate = frh.HeartRate;
- // var isAbnormal = fetalHeartRate > upperAlarmThreshold ? 1 : (fetalHeartRate < lowerAlarmThreshold ? 2 : 0);
- // // 推送到api/v1/open/OpenIot/SetFetalMovementConfig
- // await _serviceIotApi.SetFetalMovementConfig(imeiDel, fetalMovement, sampleTime, isAbnormal);
- // // 保存到MySQL数据库
- // }
- // }
-
- // }
-
- // }
-
- // #region 定时计算胎动数据触发器 2小时后
- // await SetIntervalTriggerAsync(key, imeiDel, 60 * 60 * 2);
- // #endregion
- //}
-
- #region 胎动延时计算
- var watchConfig = await _deviceCacheMgr.GetGpsDeviceWatchConfigCacheObjectBySerialNoAsync(imeiDel, "0067");
- var isFetalHeartEnable = watchConfig != null && (int)watchConfig["enabled"]! == 1;
- if (isFetalHeartEnable)
- {
-
-
- var edoc = DateTimeUtil.ToDateTime(watchConfig!["EDOC"]!.ToString());
- // 已经建模
- var commonPHR = await _serviceTDengine.GetLastAsync<PregnancyCommonHeartRateModel>(imeiDel);
- if (commonPHR != null)
- {
- var phr = await _serviceTDengine.GetBySerialNoAsync<PregnancyHeartRateModel>(imeiDel, 7);
- _logger.LogInformation($"{imeiDel} 计算胎动数据 ");
-
- var fmNow = DateTime.Now;
- // 两小时前
- var fmNowSubtract = fmNow.AddMinutes(-fmNow.Minute).AddSeconds(-fmNow.Second).AddMilliseconds(-fmNow.Millisecond);
-
- var fetalMovementSampleTime = DateTimeUtil.ConvertToTimeStamp(fmNowSubtract).ToString()[..10];
-
- // 统计开始时间
- var statStartTime = fmNowSubtract.AddHours(-2);
- // 统计结束时间
- var statEndTime = fmNowSubtract;
-
- var isFetalMovementExisted = await _deviceCacheMgr.FetalMovementIsExistedAsync(imeiDel, fetalMovementSampleTime);
- _logger.LogInformation($"{imeiDel} 胎动记录{isFetalMovementExisted},数据采样时间:{fetalMovementSampleTime}|{fmNowSubtract.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(phrRange.First());
-
- 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($"{imeiDel} segmentCountFMIndex: {i} -- fetalMovementSampleTime:{fetalMovementSampleTime}|{midNight.AddHours(2 * i).ToString("yyyy-MM-dd HH:mm:ss")} -- statStartTime: {statStartTime} -- statEndTime: {statEndTime}-- isFetalMovementExisted: {isFetalMovementExisted} ");
-
- _logger.LogInformation($"{imeiDel} 孕周:{pregnancyWeeks},胎动数据采样时间:{fetalMovementSampleTime}|{fmNowSubtract.ToString("yyyy-MM-dd HH:mm:ss")}, 采样周期:{statStartTime}-{statEndTime}, 原始胎动值:{fetalMovementMapValue}, 佩戴时间 :{duringMins}|{statStartTime}-{phrRange.First()}, 胎动计算值:{fetalMovementValue}, 胎动最终值:{fetalMovement} 已完成.");
-
- // 读取胎心数据
- GeneralParam param = new()
- {
- Filters = new List<QueryFilterCondition>
- {
- new ()
- {
- Key=nameof(HisGpsFetalHeartRate.Serialno),
- Value=imeiDel,
- ValueType=QueryValueTypeEnum.String,
- Operator=QueryOperatorEnum.Equal
- },
- //new ()
- //{
- // Key=nameof(HisGpsFetalHeartRate.SampleTime),
- // Value=sampleTime,
- // ValueType=QueryValueTypeEnum.String,
- // Operator=QueryOperatorEnum.GreaterEqual
- //},
- },
- OrderBys=new List<OrderByCondition>
- {
- new (){
- IsDesc=true,
- Key=nameof(HisGpsFetalHeartRate.SampleTime)
- }
- }
- };
- var fetalHeartRateIsAbnormal = 0;
- var fhr = await _hisFetalHeartApiClient.GetFirstAsync(param, imeiDel[^2..], null, new RequestHeader { RequestId = Guid.NewGuid().ToString("D") });
- // 获取胎心数据状态与胎动数据状态一致
- var feltalMovementIsAbnormal = fetalHeartRateIsAbnormal;
- //var feltalMovementIsAbnormal = 0;
- // 推送到api/v1/open/OpenIot/SetFetalMovementConfig
-
- await _serviceIotApi.SetFetalMovementConfig(imeiDel, fetalMovement, fetalMovementSampleTime, feltalMovementIsAbnormal);
-
- // 保存到MySQL数据库
- HisGpsFetalMovement fm = new()
- {
- FetalMovementId = Guid.NewGuid().ToString("D"),
- PersonId = commonPHR!.PersonId,
- Serialno = imeiDel,
- 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 device = await _deviceCacheMgr.GetDeviceBySerialNoAsync(imeiDel).ConfigureAwait(false);
- var fmMsgId = $"{imeiDel}-{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 = imeiDel,
- alarmTypeId = 12,
- alarmDeviceName = imeiDel,
- 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(imeiDel, fetalMovementSampleTime, fm);
-
- }
- else
- {
- _logger.LogWarning($"{imeiDel} 孕周 {pregnancyWeeks},超出胎动计算范围");
- }
- }
- else
- {
- _logger.LogInformation($"{imeiDel} 胎动记录{isFetalMovementExisted},数据采样时间:{fetalMovementSampleTime}|{fmNowSubtract.ToString("yyyy-MM-dd HH:mm:ss")}, 周期:{statStartTime}-{statEndTime} 不足两条,不能判断是否持续佩戴");
-
- }
- }
- else
- {
- _logger.LogInformation($"{imeiDel} 胎动记录{isFetalMovementExisted},数据采样时间:{fetalMovementSampleTime}|{fmNowSubtract.ToString("yyyy-MM-dd HH:mm:ss")}, 周期:{statStartTime}-{statEndTime} 已处理");
- }
- }
-
-
- }
- #endregion
- }
- else
- {
- // 处理血压业务
- int systolicInc;
- int diastolicInc;
-
- int systolicRefValue;
- int diastolicRefValue;
-
- decimal systolicAvg;
- decimal diastolicAvg;
-
- int systolicMax = 0;
- int diastolicMax = 0;
-
- // 统计时间
- //DateTime endTime = DateTime.Now; //测试
- DateTime statStartTime = DateTime.Now;
-
-
- // 最小值
- int systolicMin = 0;
- int diastolicMin = 0;
-
- // 偏移参数
- var avgOffset = 0.25M;
- var systolicAvgOffset = avgOffset;
- var diastolicAvgOffset = avgOffset;
-
- // 最后一次下发值
- int lastPushSystolicInc = 0;
- int lastPushDiastolicInc = 0;
-
-
- var startTime = DateTime.Now;
- // 下发增量值
- #region 统计定时下发增量值
- //var last = await _serviceTDengine.GetLastAsync("stb_hm_bloodpress_stats_inc", $"serialno='{imeiDel}' order by last_update desc");
- //var ts = last?[0];
-
- // 最后一条血压数据
- var condition = $"serialno='{imeiDel}' order by last_update desc";
- var field = "last_row(*)";
- var lastHmBpResponse = await _serviceTDengine.ExecuteSelectRestResponseAsync("stb_hm_bloodpress", condition, field);
- var lastHmBpParser = JsonConvert.DeserializeObject<ParseTDengineRestResponse<BloodPressureModel>>(lastHmBpResponse!);
- var lastHmBp = lastHmBpParser?.Select().FirstOrDefault();
- //if (lastHmBpParser?.Select()?.ToList().Count < 2)
- //{
- // _logger.LogInformation($"{imeiDel} -- {nameof(Worker)} --{nameof(WatchEvents)} -- 血压数据条目不足");
- // break;
- //}
-
- // 7 天有效数据
- if (lastHmBp?.Timestamp.AddDays(7) > DateTime.Now)
- {
- // 计算增量值
- condition = $"serialno='{imeiDel}' order by ts desc";
- var lastPushResponse = await _serviceTDengine.ExecuteSelectRestResponseAsync("stb_hm_bp_push_ref_inc_value", condition, field);
- if (lastPushResponse == null)
- {
- _logger.LogInformation($"{imeiDel}--没有下发记录");
- break;
- }
- var lastPushParser = JsonConvert.DeserializeObject<ParseTDengineRestResponse<BloodPressurePushRefIncModel>>(lastPushResponse);
- var lastPush = lastPushParser!.Select().FirstOrDefault();
- // 有下推记录
- if (lastPush != null)
- {
- systolicRefValue = lastPush!.SystolicRefValue;
- diastolicRefValue = lastPush!.DiastolicRefValue;
- lastPushSystolicInc = lastPush!.SystolicIncValue;
- lastPushDiastolicInc = lastPush!.DiastolicIncValue;
- condition = $"ts between '{lastPush?.Timestamp:yyyy-MM-dd HH:mm:ss.fff}' and '{startTime:yyyy-MM-dd HH:mm:ss.fff}' " +
- $"and serialno='{imeiDel}' " +
- $"and is_display = true";
- // 使用最近一次的下推时间作为统计的开始时间
- statStartTime = lastPush!.Timestamp;
- }
- // 没有下推记录(历史遗留数据),没有初始的测量值产生的平均值(测量值=平均值)
- else
- {
- #region 获取个人信息
-
- var person = await _personCacheMgr.GetDeviceGpsPersonCacheBySerialNoAsync(Guid.NewGuid().ToString(), imeiDel).ConfigureAwait(false);
- //验证这个信息是否存在
- if (person == null || person?.Person.BornDate == null)
- {
- _logger.LogWarning($"{nameof(Worker)}--{imeiDel} 验证个人信息,找不到个人信息,跳过此消息");
- break;
- }
- // 验证年龄是否在范围 (2 - 120)
- var age = SafeType.SafeInt(DateTime.Today.Year - person?.Person.BornDate!.Value.Year!);
- if (age < 2 || age > 120)
- {
- _logger.LogWarning($"{nameof(Worker)}--{imeiDel} 验证年龄,不在范围 (2 - 120)岁,跳过此消息");
- break;
- }
-
- var gender = person?.Person.Gender == true ? 1 : 2;
- var isHypertension = SafeType.SafeBool(person?.Person.Ishypertension!);
- var height = SafeType.SafeDouble(person?.Person.Height!);
- var weight = SafeType.SafeDouble(person?.Person.Weight!);
-
- #endregion
-
- #region 初始化常规血压标定值标定值
- var bpRef = await _bpRefValCacheManager.GetBloodPressReferenceValueAsync(age, gender, isHypertension);
- //systolicRefValue = bpRef!.Systolic;//?
- //diastolicRefValue = bpRef!.Diastolic;//?
- #endregion
-
- systolicRefValue = bpRef!.Systolic;
- diastolicRefValue = bpRef!.Diastolic;
- lastPushSystolicInc = 0;
- lastPushDiastolicInc = 0;
- condition = $"ts <= '{startTime:yyyy-MM-dd HH:mm:ss.fff}' " +
- $"and serialno='{imeiDel}' " +
- $"and is_display = true";
- }
-
-
-
- var hmBpResponse = await _serviceTDengine.ExecuteSelectRestResponseAsync("stb_hm_bloodpress", condition);
- var hmBpParser = JsonConvert.DeserializeObject<ParseTDengineRestResponse<BloodPressureModel>>(hmBpResponse!);
- var hmBp = hmBpParser?.Select();
- //if (hmBp?.ToList().Count < 2)
- // 1. 判断数据样本数量
- if (hmBpParser!.Rows < 5)
- {
- _logger.LogInformation($"{imeiDel} -- {nameof(Worker)} --{nameof(WatchEvents)} -- 统计定时下发,计算增量值的数据条目不足:{hmBpParser!.Rows} < 5");
- _logger.LogInformation($"{imeiDel} -- {nameof(Worker)} --{nameof(WatchEvents)} 没有足够的数据样本,不会定时下发");
- break;
- }
- // 没有下推记录重新计算统计时间
- if (lastPush == null)
- {
- var firstHmBp = hmBpParser?.Select(i => i).OrderBy(i => i.Timestamp).FirstOrDefault();
- statStartTime = firstHmBp!.Timestamp;
- }
-
- // NewMethod(systolicRefValue, hmBpParser);
-
- // 最大值
- //systolicMax = (int)hmBpParser?.Select(i => i.SystolicValue).Max()!;
- //diastolicMax = (int)hmBpParser?.Select(i => i.DiastolicValue).Max()!;
- //// 最小值
- //systolicMin = (int)hmBpParser?.Select(i => i.SystolicValue).Min()!;
- //diastolicMin = (int)hmBpParser?.Select(i => i.DiastolicValue).Min()!;
-
- //systolicAvg = (int)(hmBpParser?.AverageAfterRemovingOneMinMaxRef(i => i.SystolicValue, SafeType.SafeInt(systolicRefValue!)))!;
- //diastolicAvg = (int)(hmBpParser?.AverageAfterRemovingOneMinMaxRef(i => i.DiastolicValue, SafeType.SafeInt(diastolicRefValue!)))!;
-
- var avgs = _serviceTDengine.AverageAfterRemovingOneMinMaxRef(hmBpParser!);
- systolicAvg = avgs[0];
- diastolicAvg = avgs[1];
-
-
- // 2. 判断能否计算增量值
- if (systolicAvg.Equals(0))
- {
- _logger.LogInformation($"{imeiDel}--{nameof(Worker)}--计算平均值" +
- $"\n currentSystolicAvg:{systolicAvg} -- lastPushSystolicInc:{lastPushSystolicInc}" +
- $"\n currentDiastolicInc:{diastolicAvg} -- lastPushDiastolicInc:{lastPushDiastolicInc}");
- _logger.LogInformation($"{imeiDel}--{nameof(Worker)} 没有足够的数据样本计算平均值,不会定时下发");
- break;
- }
- // 除最大值和最小值后的平均值与标定值差值少于4后(当天计算出该结果则也不产生增量调整),就不再进行增量值调整了。
- if (systolicRefValue - systolicAvg < 4)
- {
- _logger.LogInformation($"diastolic 舒张压 {imeiDel}除最大值和最小值后的平均值:{diastolicAvg}与标定值:{diastolicRefValue}\n systolic 收缩压 {imeiDel}除最大值和最小值后的平均值:{systolicAvg}与标定值:{systolicRefValue}的差值(标定值-平均值)少于4后,systolic 收缩压 不再进行增量值调整");
- break;
- }
-
- if (diastolicRefValue - diastolicAvg < 4)
- {
- _logger.LogInformation($"systolic 收缩压 {imeiDel}除最大值和最小值后的平均值:{systolicAvg}与标定值:{systolicRefValue}\n diastolic 舒张压 {imeiDel}除最大值和最小值后的平均值:{diastolicAvg}与标定值:{diastolicRefValue}的差值(标定值-平均值)少于4后,diastolic 舒张压 不再进行增量值调整");
- break;
- }
-
- // 增量值=(标定值-平均值)* 0.25
- var currentSystolicInc = (int)((systolicRefValue - systolicAvg) * systolicAvgOffset)!;
- var currentDiastolicInc = (int)((diastolicRefValue - diastolicAvg) * diastolicAvgOffset)!;
-
-
- // 累计增量
- systolicInc = currentSystolicInc + lastPushSystolicInc;
- diastolicInc = currentDiastolicInc + lastPushDiastolicInc;
-
- _logger.LogInformation($"{imeiDel}--{nameof(Worker)}--计算增量值" +
- $"\n {imeiDel} -- systolicAvg:{systolicAvg}-- systolicInc:{systolicInc}-- currentSystolicInc:{currentSystolicInc} -- lastPushSystolicInc:{lastPushSystolicInc}" +
- $"\n {imeiDel} -- diastolicAvg:{diastolicAvg}-- diastolicInc:{diastolicInc} --currentDiastolicInc:{currentDiastolicInc} -- lastPushDiastolicInc:{lastPushDiastolicInc}");
- _logger.LogInformation($"{imeiDel}--{nameof(Worker)}-- 定时校准,发给设备的绝对增量值=(上次绝对增量值+新数据的增量值)");
-
-
- _logger.LogInformation($"{nameof(Worker)} 开启血压标定值下发: {_configBoodPressResolver.EnableBPRefPush}");
- if (_configBoodPressResolver.EnableBPRefPush)
- // if (false) // 临时关闭
- {
- BloodPressCalibrationConfigModel bpIncData = new()
- {
-
- Imei = imeiDel,
- SystolicRefValue = SafeType.SafeInt(((int)systolicRefValue!)), //收缩压标定值,值为0 表示不生效
- DiastolicRefValue = SafeType.SafeInt(((int)diastolicRefValue!)), //舒张压标定值,值为0表示不生效
- SystolicIncValue = SafeType.SafeInt(((int)systolicInc!)), //收缩压显示增量,值为0 表示不生效
- DiastolicIncValue = SafeType.SafeInt(((int)diastolicInc!)) //舒张压显示增量,值为0 表示不生效
- };
- //var pushedBP = await _serviceIotApi.SetBloodPressCalibrationConfigAsync(bpIncData).ConfigureAwait(false);
- var response = await _serviceIotApi.SetBloodPressCalibrationConfig2Async(bpIncData).ConfigureAwait(false);
- var pushedBP = response.Flag;
- if (pushedBP)
- {
- #region 保存下推记录 stb_hm_bp_push_ref_inc_value
- var sql = $"INSERT INTO health_monitor.hm_bp_push_ref_inc_value_{imeiDel.Substring(imeiDel.Length - 2)} " +
- $"USING health_monitor.stb_hm_bp_push_ref_inc_value " +
- $"TAGS ('{imeiDel.Substring(imeiDel.Length - 2)}') " +
- $"VALUES(" +
- $"'{startTime:yyyy-MM-dd HH:mm:ss.fff}'," +
- $"'{imeiDel}'," +
- $"{bpIncData.SystolicRefValue}," +
- $"{bpIncData.DiastolicRefValue}," +
- $"{bpIncData.SystolicIncValue}," +
- $"{bpIncData.DiastolicIncValue}," +
- $"{false}," +
- $"{systolicAvg}," +
- $"{diastolicAvg}," +
- $"{systolicAvgOffset}," +
- $"{diastolicAvgOffset}," +
- $"'{statStartTime:yyyy-MM-dd HH:mm:ss.fff}'," +
- $"'{startTime:yyyy-MM-dd HH:mm:ss.fff}'" +
- $")";
- _serviceTDengine.ExecuteInsertSQL(sql);
- #endregion
-
- #region 注册定时下发
- // 注册下次下推
- var endTime = DateTime.Now;
-
- #if DEBUG
-
-
- //long ttl = (long)((60 * 1000-(endTime-startTime).TotalMilliseconds)/1000);
- //await _serviceEtcd.PutValAsync(key, imeiDel,ttl, 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 + 2, 0).AddDays(interval);
- TimeSpan timeUntilNextRun = nextRunTime - now;
-
- // 如果当前时间已经超过了8点,将等待到明天后的8点
- if (timeUntilNextRun < TimeSpan.Zero)
- {
- timeUntilNextRun = timeUntilNextRun.Add(TimeSpan.FromMinutes(1));
- nextRunTime += timeUntilNextRun;
- }
-
- // var ttl = timeUntilNextRun.TotalMilliseconds;
- long ttl = (long)((timeUntilNextRun.TotalMilliseconds - (endTime - startTime).TotalMilliseconds) / 1000);
- var data = new
- {
- imei = imeiDel,
- 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
- // 每$interval天,晚上8点
- var interval = 1;
- // 获取当前时间
- DateTime now = DateTime.Now;
-
- // 计算距离下一个$interval天后的8点的时间间隔
- DateTime nextRunTime = new DateTime(now.Year, now.Month, now.Day, 20, 0, 0).AddDays(interval);
- TimeSpan timeUntilNextRun = nextRunTime - now;
-
- // 如果当前时间已经超过了8点,将等待到明天后的8点
- if (timeUntilNextRun < TimeSpan.Zero)
- {
- timeUntilNextRun = timeUntilNextRun.Add(TimeSpan.FromDays(1));
- nextRunTime += timeUntilNextRun;
- }
-
- // var ttl = timeUntilNextRun.TotalMilliseconds;
- long ttl = (long)((timeUntilNextRun.TotalMilliseconds-(endTime-startTime).TotalMilliseconds)/1000);
- var data = new
- {
- imei = imeiDel,
- 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
-
- }
- else
- {
- _logger.LogInformation($"错误响应,没有下推数据:{response.Message}");
- }
- }
- }
- else
- {
- _logger.LogInformation($"向{imeiDel}统计数据已经失效");
- }
- #endregion
-
- }
-
- }
- break;
- }
- }
- catch (Exception ex)
- {
- _logger.LogInformation($"{nameof(WatchEvents)},出错: |{ex.Message}|{ex.StackTrace}");
- }
-
-
- });
-
- }
-
-
- 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 IsNowInTimeRanges()
- {
- var now = DateTime.Now.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);
- }
- 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);
- }
-
-
- }
- }
|