No puede seleccionar más de 25 temas Los temas deben comenzar con una letra o número, pueden incluir guiones ('-') y pueden tener hasta 35 caracteres de largo.

Worker.cs 165KB

hace 6 meses
hace 5 meses
hace 6 meses
hace 6 meses
hace 6 meses
hace 6 meses
hace 6 meses
hace 6 meses
hace 6 meses
hace 1 año
hace 6 meses
hace 6 meses
hace 6 meses
hace 5 meses
hace 6 meses
hace 5 meses
hace 6 meses
hace 5 meses
hace 5 meses
hace 5 meses
hace 5 meses
hace 5 meses
hace 6 meses
hace 5 meses
hace 5 meses
hace 5 meses
hace 5 meses
hace 6 meses
hace 5 meses
hace 5 meses
hace 6 meses
hace 6 meses
hace 6 meses
hace 6 meses
hace 5 meses
hace 2 meses
hace 4 meses
hace 4 meses
hace 2 meses
hace 5 meses
hace 5 meses
hace 2 meses
hace 5 meses
hace 2 meses
hace 5 meses
hace 5 meses
hace 5 meses
hace 5 meses
hace 5 meses
hace 5 meses
hace 6 meses
hace 6 meses
hace 6 meses
hace 6 meses
hace 6 meses
hace 6 meses
hace 6 meses
hace 6 meses
hace 6 meses
hace 6 meses
hace 6 meses
hace 6 meses
hace 6 meses
hace 6 meses
hace 6 meses
hace 6 meses
hace 6 meses
hace 1 año
hace 6 meses
hace 6 meses
hace 6 meses
hace 6 meses
hace 6 meses
hace 6 meses
hace 6 meses
hace 6 meses
hace 6 meses
hace 6 meses
hace 6 meses
hace 6 meses
hace 1 año
hace 6 meses
hace 6 meses
hace 6 meses
hace 6 meses
hace 5 meses
hace 6 meses
hace 5 meses
hace 5 meses
hace 5 meses
hace 5 meses
hace 5 meses
hace 5 meses
hace 5 meses
hace 5 meses
hace 5 meses
hace 2 meses
hace 2 meses
hace 2 meses
hace 3 semanas
hace 3 semanas
hace 3 semanas
hace 3 semanas
hace 4 meses
hace 5 meses
hace 5 meses
hace 5 meses
hace 5 meses
hace 5 meses
hace 5 meses
hace 5 meses
hace 5 meses
hace 5 meses
hace 5 meses
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866
  1. using dotnet_etcd;
  2. using Etcdserverpb;
  3. using Google.Protobuf.WellKnownTypes;
  4. using HealthMonitor.Common;
  5. using HealthMonitor.Common.helper;
  6. using HealthMonitor.Core.Common.Extensions;
  7. using HealthMonitor.Core.Pipeline;
  8. using HealthMonitor.Model.Config;
  9. using HealthMonitor.Model.Service;
  10. using HealthMonitor.Model.Service.Mapper;
  11. using HealthMonitor.Service.Biz;
  12. using HealthMonitor.Service.Biz.db;
  13. using HealthMonitor.Service.Cache;
  14. using HealthMonitor.Service.Etcd;
  15. using HealthMonitor.Service.MessageQueue;
  16. using HealthMonitor.Service.Sub;
  17. using Microsoft.AspNetCore.Mvc.RazorPages;
  18. using Microsoft.EntityFrameworkCore.Metadata;
  19. using Microsoft.EntityFrameworkCore.Metadata.Internal;
  20. using Microsoft.Extensions.Options;
  21. using NetTaste;
  22. using Newtonsoft.Json;
  23. using Newtonsoft.Json.Linq;
  24. using System;
  25. using System.Reflection;
  26. using System.Text.RegularExpressions;
  27. using System.Threading.Channels;
  28. using TDengineDriver;
  29. using TDengineTMQ;
  30. using TelpoDataService.Util.Clients;
  31. using TelpoDataService.Util.Entities.GpsCard;
  32. using TelpoDataService.Util.Entities.GpsLocationHistory;
  33. using TelpoDataService.Util.Models;
  34. using TelpoDataService.Util.QueryObjects;
  35. namespace HealthMonitor.WebApi
  36. {
  37. public class Worker : BackgroundService
  38. {
  39. private readonly ILogger<Worker> _logger;
  40. private readonly IServiceProvider _services;
  41. private readonly TDengineDataSubcribe _tdEngineDataSubcribe;
  42. private readonly PackageProcess _processor;
  43. private readonly TDengineService _serviceTDengine;
  44. private readonly EtcdService _serviceEtcd;
  45. private readonly HttpHelper _httpHelper = default!;
  46. private readonly IotApiService _serviceIotApi;
  47. private readonly BoodPressResolverConfig _configBoodPressResolver;
  48. private readonly BloodPressReferenceValueCacheManager _bpRefValCacheManager;
  49. private readonly PersonCacheManager _personCacheMgr;
  50. private readonly DeviceCacheManager _deviceCacheMgr;
  51. private readonly FetalMovementNormalValueRangeCacheManager _mgrFetalMovementNormalValueRangeCache;
  52. private readonly GpsLocationHistoryAccessorClient<HisGpsFetalHeartRate> _hisFetalHeartApiClient;
  53. private readonly GpsLocationHistoryAccessorClient<HisGpsFetalMovement> _hisFetalMovementApiClient;
  54. private readonly GpsLocationHistoryAccessorClient<HisGpsPsychResult> _hisPsychResultApiClient;
  55. private readonly MqProcessLogic _serviceMqProcess;
  56. private CancellationTokenSource _tokenSource = default!;
  57. private static int INTERVAL_FHR = 15;
  58. public Worker(ILogger<Worker> logger, IServiceProvider services, PersonCacheManager personCacheMgr,
  59. BloodPressReferenceValueCacheManager bpRefValCacheManager, IotApiService IotApiService,
  60. IOptions<BoodPressResolverConfig> optionBoodPressResolver, PackageProcess processor,
  61. TDengineDataSubcribe tdEngineDataSubcribe, TDengineService serviceDengine,
  62. GpsLocationHistoryAccessorClient<HisGpsFetalHeartRate> hisFetalHeartApiClient,
  63. GpsLocationHistoryAccessorClient<HisGpsFetalMovement> hisFetalMovementApiClient,
  64. GpsLocationHistoryAccessorClient<HisGpsPsychResult> hisGpsPsychResultApiClient,
  65. FetalMovementNormalValueRangeCacheManager fetalMovementNormalValueRangeCacheMgr, MqProcessLogic serviceMqProcess,
  66. HttpHelper httpHelper, EtcdService serviceEtcd, DeviceCacheManager deviceCacheMgr)
  67. {
  68. _logger = logger;
  69. _tdEngineDataSubcribe = tdEngineDataSubcribe;
  70. _services = services;
  71. _serviceIotApi = IotApiService;
  72. _processor = processor;
  73. _serviceEtcd = serviceEtcd;
  74. _serviceTDengine = serviceDengine;
  75. _httpHelper = httpHelper;
  76. _configBoodPressResolver = optionBoodPressResolver.Value;
  77. _bpRefValCacheManager = bpRefValCacheManager;
  78. _personCacheMgr = personCacheMgr;
  79. _deviceCacheMgr = deviceCacheMgr;
  80. _hisFetalHeartApiClient = hisFetalHeartApiClient;
  81. _hisFetalMovementApiClient = hisFetalMovementApiClient;
  82. _hisPsychResultApiClient = hisGpsPsychResultApiClient;
  83. _serviceMqProcess = serviceMqProcess;
  84. _mgrFetalMovementNormalValueRangeCache = fetalMovementNormalValueRangeCacheMgr;
  85. }
  86. public override Task StartAsync(CancellationToken cancellationToken)
  87. {
  88. //_logger.LogInformation("------StartAsync");
  89. _tokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
  90. return base.StartAsync(cancellationToken);
  91. }
  92. public override Task StopAsync(CancellationToken cancellationToken)
  93. {
  94. //_logger.LogInformation("------StopAsync");
  95. _tokenSource.Cancel(); //停止工作线程
  96. return base.StopAsync(cancellationToken);
  97. }
  98. protected override async Task ExecuteAsync(CancellationToken stoppingToken)
  99. {
  100. var tasks = new[]
  101. {
  102. Task.Run(async () =>
  103. {
  104. _logger.LogInformation("解析器启动");
  105. while (!stoppingToken.IsCancellationRequested)
  106. {
  107. await _processor.ResolveAsync().ConfigureAwait(false);
  108. }
  109. }, stoppingToken),
  110. Task.Run(() =>
  111. {
  112. _logger.LogInformation("TDengine 订阅启动");
  113. while (!stoppingToken.IsCancellationRequested)
  114. {
  115. _tdEngineDataSubcribe.BeginListen(stoppingToken);
  116. }
  117. }, stoppingToken),
  118. Task.Run(() =>
  119. _serviceEtcd.WacthKeysWithPrefixResponseAsync("health_monitor/schedule_push", WatchEvents),
  120. stoppingToken)
  121. };
  122. await Task.WhenAll(tasks);
  123. }
  124. private void WatchEvents(WatchResponse response)
  125. {
  126. response.Events.ToList<Mvccpb.Event>().ForEach(async e =>
  127. {
  128. try
  129. {
  130. switch (e.Type.ToString())
  131. {
  132. case "Put":
  133. // 获取时间点计算TTL
  134. Console.BackgroundColor = ConsoleColor.Blue;
  135. Console.WriteLine($"--- {e.Type}--{e.Kv.Key.ToStringUtf8()}--{e.Kv.Value.ToStringUtf8()}---{DateTime.Now}");
  136. Console.BackgroundColor = ConsoleColor.Black;
  137. break;
  138. case "Delete":
  139. // TTL到了重新计算TTL,下发
  140. //Console.BackgroundColor = ConsoleColor.Green;
  141. //Console.WriteLine($"--- {e.Type}--{e.Kv.Key.ToStringUtf8()}--{e.Kv.Value.ToStringUtf8()}---{DateTime.Now}");
  142. // var key = $"health_monitor/schedule_push/imei/{bp.Serialno}";
  143. var key = e.Kv.Key.ToStringUtf8();
  144. //var imeiDel = key.Split('/')[^1];
  145. var imeiDel = ExtractImei(key);
  146. if (string.IsNullOrEmpty(imeiDel))
  147. {
  148. _logger.LogWarning("定时器不能抽取imei");
  149. return;
  150. }
  151. var schedule_push = await _serviceEtcd.GetValAsync(key).ConfigureAwait(false);
  152. if (string.IsNullOrWhiteSpace(schedule_push))
  153. {
  154. // 胎心数据建模
  155. if (key.Contains("pregnancy_heart_rate"))
  156. {
  157. using (_logger.BeginScope(new Dictionary<string, object> { ["RequestId"] = $"MODE-{imeiDel}-{DateTime.Now.ToString("yyyyMMddHHmmss")}" }))
  158. {
  159. #region 胎心数据建模
  160. var watchConfig = await _deviceCacheMgr.GetGpsDeviceWatchConfigCacheObjectBySerialNoAsync(imeiDel, "0067");
  161. var isFetalHeartEnable = watchConfig != null && (int)watchConfig["enabled"]! == 1;
  162. if (isFetalHeartEnable)
  163. {
  164. // 高频心率采样间隔 highFreqSampleInterval = highFreqSampleInterval+5,增加5秒兼容
  165. //var highFreqSampleInterval = (int)watchConfig!["highFreqSampleInterval"]! + 5;
  166. // 高频心率采样间隔 highFreqSampleInterval = highFreqSampleInterval+5,增加5秒兼容(最小highFreqSampleInterval=60)
  167. var highFreqSampleInterval = (int)watchConfig!["highFreqSampleInterval"]! >= 60 ? (int)watchConfig!["highFreqSampleInterval"]! + 5 : 60;
  168. // 处理孕妇业务,计算一般心率并下发
  169. var commonPHR = await _serviceTDengine.InitPregnancyCommonHeartRateModeAsync(imeiDel, highFreqSampleInterval: highFreqSampleInterval);
  170. if (commonPHR == null)
  171. {
  172. // 建模中
  173. var flag = await _serviceIotApi.SetFetalConfig(imeiDel);
  174. _logger.LogInformation($"{imeiDel} 建模建模中");
  175. }
  176. else
  177. {
  178. // 建模完成
  179. var flag = await _serviceIotApi.SetFetalConfig(imeiDel, 1, commonPHR.MaxValue, commonPHR.MinValue);
  180. _logger.LogInformation($"{imeiDel} 建模完成");
  181. // 保存到TDengine数据库
  182. await _serviceTDengine.InsertAsync<PregnancyCommonHeartRateModel>("hm_pchr", commonPHR);
  183. _logger.LogInformation($"保存TDengine完成");
  184. //
  185. }
  186. }
  187. #endregion
  188. #region 注册定时下发
  189. var startTime = DateTime.Now;
  190. // 注册下次下推
  191. var endTime = DateTime.Now;
  192. #if DEBUG
  193. //long ttl = (long)((60 * 1000-(endTime-startTime).TotalMilliseconds)/1000);
  194. //await _serviceEtcd.PutValAsync(key, imeiDel,ttl, false).ConfigureAwait(false);
  195. var interval = 0;
  196. // 获取当前时间
  197. DateTime now = DateTime.Now;
  198. // 计算距离下一个$interval天后的8点的时间间隔
  199. DateTime nextRunTime = new DateTime(now.Year, now.Month, now.Day, now.Hour, now.Minute + 2, 0).AddDays(interval);
  200. TimeSpan timeUntilNextRun = nextRunTime - now;
  201. // 如果当前时间已经超过了8点,将等待到明天后的8点
  202. if (timeUntilNextRun < TimeSpan.Zero)
  203. {
  204. timeUntilNextRun = timeUntilNextRun.Add(TimeSpan.FromMinutes(1));
  205. nextRunTime += timeUntilNextRun;
  206. }
  207. //long ttl = timeUntilNextRun.Milliseconds/1000;
  208. long ttl = (long)((timeUntilNextRun.TotalMilliseconds - (endTime - startTime).TotalMilliseconds) / 1000);
  209. var data = new
  210. {
  211. imei = imeiDel,
  212. create_time = now.ToString("yyyy-MM-dd HH:mm:ss"),
  213. ttl,
  214. next_run_time = nextRunTime.ToString("yyyy-MM-dd HH:mm:ss")
  215. };
  216. var result = JsonConvert.SerializeObject(data);
  217. await _serviceEtcd.PutValAsync(key, result, ttl, false).ConfigureAwait(false);
  218. #else
  219. // 每$interval天,晚上8点
  220. var interval = 1;
  221. // 获取当前时间
  222. DateTime now = DateTime.Now;
  223. var rand = new Random();
  224. var pushSec = rand.Next(59);
  225. int pushMin = int.TryParse(imeiDel.AsSpan(imeiDel.Length - 1), out pushMin) ? pushMin : 10;
  226. // 计算距离下一个$interval天后的0点的时间间隔
  227. DateTime nextRunTime = new DateTime(now.Year, now.Month, now.Day, 6, pushMin, pushSec).AddDays(interval);
  228. TimeSpan timeUntilNextRun = nextRunTime - now;
  229. // 如果当前时间已经超过了8点,将等待到明天后的8点
  230. if (timeUntilNextRun < TimeSpan.Zero)
  231. {
  232. timeUntilNextRun = timeUntilNextRun.Add(TimeSpan.FromDays(1));
  233. nextRunTime += timeUntilNextRun;
  234. }
  235. // var ttl = timeUntilNextRun.TotalMilliseconds;
  236. long ttl = (long)((timeUntilNextRun.TotalMilliseconds-(endTime-startTime).TotalMilliseconds)/1000);
  237. var data = new
  238. {
  239. imei = imeiDel,
  240. create_time = now.ToString("yyyy-MM-dd HH:mm:ss"),
  241. ttl,
  242. next_run_time = nextRunTime.ToString("yyyy-MM-dd HH:mm:ss")
  243. };
  244. var result = JsonConvert.SerializeObject(data);
  245. await _serviceEtcd.PutValAsync(key, result, ttl, false).ConfigureAwait(false);
  246. #endif
  247. #endregion
  248. }
  249. }
  250. // health_monitor/schedule_push/cal_fetal_heart_rate/imei/
  251. // 高频结束后计算胎心数据,防止结束后与常规心理的胎心处理过长
  252. if (key.Contains("health_monitor/schedule_push/cal_freqhr_fetal_heart_rate/imei/"))
  253. {
  254. /// 延时计算高频状态的胎心数据,防止高频结束后与触发数据时间相隔过长。
  255. var phrFreqstatus = await _deviceCacheMgr.GetPregnancyHeartRateFreqStatusAsync(imeiDel);
  256. // 高频不停,15分钟内只下发一条
  257. var push = await _deviceCacheMgr.GetBizIntervalAsync(imeiDel, "SaveAndPushFetalHeartRate");
  258. var triggerValue = (JObject)JsonConvert.DeserializeObject(e.PrevKv.Value.ToStringUtf8())!;
  259. var trigger = triggerValue["trigger"]?.ToString();
  260. // 验证高频首条是否还存在
  261. if (phrFreqstatus != null && string.IsNullOrEmpty(push))
  262. {
  263. if (!string.IsNullOrEmpty(trigger))
  264. {
  265. var triggerHeartRate = JsonConvert.DeserializeObject<HisGpsHeartRate>(trigger);
  266. using (_logger.BeginScope(new Dictionary<string, object> { ["RequestId"] = $"FREQ-HR-{triggerHeartRate?.MessageId!}" }))
  267. {
  268. var watchConfig = await _deviceCacheMgr.GetGpsDeviceWatchConfigCacheObjectBySerialNoAsync(imeiDel, "0067");
  269. _logger.LogInformation($"{imeiDel}延迟计算 高频 数据产生的胎心数据,高频结束后");
  270. var isFetalHeartEnable = watchConfig != null && (int)watchConfig["enabled"]! == 1;
  271. if (isFetalHeartEnable)
  272. {
  273. // 防止超过12条连续正常,高频不停止
  274. if (phrFreqstatus.MessageId == triggerHeartRate?.MessageId)
  275. {
  276. // 告警上限阀值
  277. var upperAlarmThreshold = (int)watchConfig!["upperAlarmThreshold"]!;
  278. // 告警下限阀值
  279. var lowerAlarmThreshold = (int)watchConfig["lowerAlarmThreshold"]!;
  280. // 触发高频监测的心率上限值
  281. var triggerHighFreqHigh = (int)watchConfig["triggerHighFreqHigh"]!;
  282. // 触发高频监测的心率下限值
  283. var triggerHighFreqLow = (int)watchConfig["triggerHighFreqLow"]!;
  284. // 高频心率采样间隔 highFreqSampleInterval = highFreqSampleInterval+5,增加5秒兼容(最小highFreqSampleInterval=60)
  285. var highFreqSampleInterval = (int)watchConfig!["highFreqSampleInterval"]! >= 60 ? (int)watchConfig!["highFreqSampleInterval"]! + 5 : 60;
  286. //停止高频心率采样心率连续正常次数
  287. var stopHighFreqSampleCount = (int)watchConfig["stopHighFreqSampleCount"]!;
  288. // 高频心率采集时长 0 为持续采集,非零为高频心率的采集时长
  289. //var highFreqSampleTimes = (int)watchConfig["highFreqSampleTimes"]!;
  290. var highFreqSampleTimes = 540;
  291. var commonPHR = await _serviceTDengine.GetLastAsync<PregnancyCommonHeartRateModel>(imeiDel);
  292. var phr = await _serviceTDengine.GetBySerialNoAsync<PregnancyHeartRateModel>(imeiDel, 7);
  293. var phrFromFreqstatus = phr.Where(i => i.LastUpdate >= phrFreqstatus.LastUpdate).ToList();
  294. // 高频数据
  295. var phrInFreqstatus = GetFreqPregnancyHeartRate(phrFromFreqstatus, highFreqSampleInterval)
  296. .OrderByDescending(i => i.LastUpdate).ToList();
  297. if (phrInFreqstatus.Count > stopHighFreqSampleCount)
  298. {
  299. // 取最后 stopHighFreqSampleCount 条高频数据
  300. phrInFreqstatus = phrInFreqstatus
  301. .OrderByDescending(i => i.LastUpdate)
  302. .Take(stopHighFreqSampleCount).ToList(); // 计算最后12条
  303. var avgPhr = phrInFreqstatus
  304. .Select(i => i.PregnancyHeartRate).Average();
  305. var FreqStatsEnd = phrInFreqstatus.First().LastUpdate;
  306. _logger.LogInformation($"延时计算,{imeiDel} 高频状态持续{(FreqStatsEnd - phrFreqstatus!.LastUpdate).TotalSeconds} 秒,统计周期:{phrFreqstatus!.LastUpdate.ToString("yyyy-MM-dd HH:mm:ss")}----{FreqStatsEnd.ToString("yyyy-MM-dd HH:mm:ss")},高频心率平均值:{(int)avgPhr},将下发指令");
  307. //await SaveAndPushFetalHeartRateAsync(triggerHeartRate, commonPHR, upperAlarmThreshold, lowerAlarmThreshold, avgPhr, DateTimeUtil.ConvertToTimeStamp(phrFreqstatus!.LastUpdate).ToString(), phrFreqstatus!.LastUpdate, FreqStatsEnd);
  308. //triggerHeartRate.HeartRate = (int)avgPhr;
  309. ////await SaveAndPushFetalHeartRateEndFreqHeartRateAsync(triggerHeartRate, commonPHR, upperAlarmThreshold, lowerAlarmThreshold, DateTimeUtil.ConvertToTimeStamp(phrFreqstatus!.LastUpdate).ToString(), phrFreqstatus!.LastUpdate, FreqStatsEnd);
  310. //// 判断是否够highFreqSampleTimes,540s
  311. //var lastPhr = phrInFreqstatus;
  312. //// 最后一条高频心率
  313. //var lastFreqHr = phrInFreqstatus.First();
  314. //var ts = DateTimeUtil.GetTimeDifferenceInSeconds((DateTime)lastFreqHr.LastUpdate!, phrFreqstatus!.LastUpdate);
  315. //if (ts < highFreqSampleTimes)
  316. //{
  317. // //triggerHeartRate.HeartRate = lastPhr.Select(i => i.PregnancyHeartRate).Min();
  318. // /// 不够10分钟最近12个数据生成胎心值
  319. // //int selectedHrValue = SelectValueFromFreqHeartRate(triggerHeartRate, triggerHighFreqHigh, triggerHighFreqLow, lastPhr);
  320. // int selectedHrValue = SelectValueFromFreqHeartRate(triggerHeartRate.Serialno, triggerHighFreqHigh, triggerHighFreqLow, highFreqSampleTimes, phrFreqstatus, lastPhr);
  321. // triggerHeartRate.HeartRate = selectedHrValue;
  322. // _logger.LogInformation($"{triggerHeartRate.Serialno} 不够10分钟最近12个数据数据中取 {triggerHeartRate.HeartRate} 生成胎心值");
  323. //}
  324. //else
  325. //{
  326. // //triggerHeartRate.HeartRate = lastPhr.Select(i => i.PregnancyHeartRate).Min();
  327. // //int selectedHrValue = SelectValueFromFreqHeartRate(triggerHeartRate, triggerHighFreqHigh, triggerHighFreqLow, lastPhr);
  328. // int selectedHrValue = SelectValueFromFreqHeartRate(triggerHeartRate.Serialno, triggerHighFreqHigh, triggerHighFreqLow, highFreqSampleTimes, phrFreqstatus, lastPhr);
  329. // triggerHeartRate.HeartRate = selectedHrValue;
  330. // /// 超过10分钟最近12个数据生成胎心值
  331. // _logger.LogInformation($"{triggerHeartRate.Serialno} 超过10分钟最近12个数据数据中取 {triggerHeartRate.HeartRate} 生成胎心值");
  332. //}
  333. //triggerHeartRate.LastUpdate = lastFreqHr.LastUpdate;
  334. //_logger.LogInformation($"{triggerHeartRate.Serialno} 高频结束后计算胎心数据,防止结束后与常规心理的胎心处理过长,定时器时长highFreqSampleInterval触发的高频心率处理");
  335. //await SaveAndPushFetalHeartRateEndFreqHeartRateAsync(triggerHeartRate, commonPHR, highFreqSampleTimes, upperAlarmThreshold, lowerAlarmThreshold, DateTimeUtil.ConvertToTimeStamp(phrFreqstatus!.LastUpdate).ToString(), phrFreqstatus!.LastUpdate, FreqStatsEnd);
  336. var lastPhr = phrInFreqstatus;
  337. _logger.LogInformation($"{triggerHeartRate.Serialno} 高频结束后计算胎心数据,防止结束后与常规心理的胎心处理过长,定时器时长highFreqSampleInterval触发的高频心率处理");
  338. triggerHeartRate.HeartRate= lastPhr.First().PregnancyHeartRate;
  339. await SaveAndPushFetalHeartRateEndFreqHeartRateAsync(triggerHeartRate, commonPHR, highFreqSampleTimes, upperAlarmThreshold, lowerAlarmThreshold, DateTimeUtil.ConvertToTimeStamp(phrFreqstatus!.LastUpdate).ToString(), phrFreqstatus!.LastUpdate, FreqStatsEnd, lastPhr, triggerHighFreqHigh, triggerHighFreqLow);
  340. }
  341. else
  342. {
  343. _logger.LogWarning($"{imeiDel} 高频心率的数据不大于{stopHighFreqSampleCount}条,不进行高频数据的胎心计算");
  344. }
  345. // 删除高频状态的首条记录
  346. await _deviceCacheMgr.DelPregnancyHeartRateFreqStatusAsync(imeiDel);
  347. }
  348. else
  349. {
  350. _logger.LogWarning($"{imeiDel} 超过12条连续正常,高频不停止,暂不处理" +
  351. $"\n 高频首条心率ID:{phrFreqstatus.MessageId}-时间:{phrFreqstatus.LastUpdate.ToString("yyyy-MM-dd HH:mm:ss")}" +
  352. $"\n 触发计算高频心率ID:{triggerHeartRate?.MessageId}-时间:{triggerHeartRate?.LastUpdate?.ToString("yyyy-MM-dd HH:mm:ss")}");
  353. await _deviceCacheMgr.DelPregnancyHeartRateFreqStatusAsync(imeiDel);
  354. }
  355. }
  356. else
  357. {
  358. _logger.LogWarning($"{imeiDel} 胎心监测功能没有启动");
  359. }
  360. }
  361. }
  362. }
  363. }
  364. // 延时计算常规胎心数据
  365. else if (key.Contains("health_monitor/schedule_push/cal_normalphr_fetal_heart_rate/imei/"))
  366. {
  367. var triggerValue = (JObject)JsonConvert.DeserializeObject(e.PrevKv.Value.ToStringUtf8())!;
  368. var trigger = triggerValue["trigger"]?.ToString();
  369. if (!string.IsNullOrEmpty(trigger))
  370. {
  371. var triggerHeartRate = JsonConvert.DeserializeObject<HisGpsHeartRate>(trigger);
  372. using (_logger.BeginScope(new Dictionary<string, object> { ["RequestId"] = $"NRML-HR-{triggerHeartRate?.MessageId!}" }))
  373. {
  374. var watchConfig = await _deviceCacheMgr.GetGpsDeviceWatchConfigCacheObjectBySerialNoAsync(imeiDel, "0067");
  375. _logger.LogInformation($"{imeiDel}延迟 常规 胎心数据产生的胎心数据");
  376. var isFetalHeartEnable = watchConfig != null && (int)watchConfig["enabled"]! == 1;
  377. if (isFetalHeartEnable)
  378. {
  379. var commonPHR = await _serviceTDengine.GetLastAsync<PregnancyCommonHeartRateModel>(imeiDel);
  380. if (commonPHR!=null)
  381. {
  382. var highFreqSampleInterval = (int)watchConfig!["highFreqSampleInterval"]! >= 60 ? (int)watchConfig!["highFreqSampleInterval"]! : 60;
  383. await CalculateNormalFetalHeartRateIntervalAsync(triggerHeartRate!, commonPHR, highFreqSampleInterval);
  384. }
  385. else
  386. {
  387. _logger.LogWarning($"{imeiDel} 胎心数据建模中...");
  388. }
  389. }
  390. else
  391. {
  392. _logger.LogWarning($"{imeiDel} 胎心监测功能没有启动");
  393. }
  394. }
  395. }
  396. }
  397. // 胎动计算
  398. //health_monitor/schedule_push/cal_fetal_movement/imei/
  399. else if (key.Contains("health_monitor/schedule_push/cal_fetal_movement/imei/"))
  400. {
  401. #region 相隔1小时胎动延时计算(实时now是2小时,计算 lastupdate 0~1范围的数据,)
  402. /**
  403. 0~1->2,0~1 的lastupdate数据,sample_time 是1点,计算时间2点
  404. 1~2->3
  405. 2~3->4
  406. 3~4->5
  407. 4~5->6
  408. 5~6->7
  409. 7~8->9
  410. 8~9->10
  411. 9~10->11
  412. 10~11->12
  413. 11~12->13
  414. 12~13->14
  415. 14~15->16
  416. 15~16->17
  417. 16~17->18
  418. 17~18->19
  419. 18~19->20
  420. 19~20->21
  421. 20~21->22
  422. 21~22->23
  423. 22~23->0
  424. 23~0->1
  425. */
  426. var triggerValue = (JObject)JsonConvert.DeserializeObject(e.PrevKv.Value.ToStringUtf8())!;
  427. var trigger = triggerValue["trigger"]?.ToString();
  428. if (!string.IsNullOrEmpty(trigger))
  429. {
  430. var triggerHeartRate = JsonConvert.DeserializeObject<HisGpsHeartRate>(trigger);
  431. using (_logger.BeginScope(new Dictionary<string, object> { ["RequestId"] = $"FM-{imeiDel}-{triggerHeartRate?.MessageId}" }))
  432. {
  433. try
  434. {
  435. var watchConfig = await _deviceCacheMgr.GetGpsDeviceWatchConfigCacheObjectBySerialNoAsync(imeiDel, "0067");
  436. _logger.LogInformation($"触发胎动计算,设备配置{JsonConvert.SerializeObject(watchConfig)}");
  437. var isFetalHeartEnable = watchConfig != null && (int)watchConfig["enabled"]! == 1;
  438. var highFreqSampleInterval = (int)watchConfig!["highFreqSampleInterval"]! >= 60 ? (int)watchConfig!["highFreqSampleInterval"]! : 60;
  439. if (isFetalHeartEnable)
  440. {
  441. var edoc = DateTimeUtil.ToDateTime(watchConfig!["EDOC"]!.ToString());
  442. // 已经建模
  443. var commonPHR = await _serviceTDengine.GetLastAsync<PregnancyCommonHeartRateModel>(imeiDel);
  444. if (commonPHR != null)
  445. {
  446. await CalculateFetalMovementIntervalAsync(triggerHeartRate!, commonPHR, edoc);
  447. }
  448. else
  449. {
  450. _logger.LogWarning($"{imeiDel} 没有胎心建模数据");
  451. }
  452. }
  453. else
  454. {
  455. _logger.LogWarning($"{imeiDel} 胎心监测功能没有开启");
  456. }
  457. }
  458. catch (Exception ex)
  459. {
  460. _logger.LogError($"{imeiDel} 计算胎动数据异常 {ex.Message}\n {ex.StackTrace}");
  461. }
  462. }
  463. }
  464. #endregion
  465. }
  466. else
  467. {
  468. // 处理血压业务
  469. int systolicInc;
  470. int diastolicInc;
  471. int systolicRefValue;
  472. int diastolicRefValue;
  473. decimal systolicAvg;
  474. decimal diastolicAvg;
  475. int systolicMax = 0;
  476. int diastolicMax = 0;
  477. // 统计时间
  478. //DateTime endTime = DateTime.Now; //测试
  479. DateTime statStartTime = DateTime.Now;
  480. // 最小值
  481. int systolicMin = 0;
  482. int diastolicMin = 0;
  483. // 偏移参数
  484. var avgOffset = 0.25M;
  485. var systolicAvgOffset = avgOffset;
  486. var diastolicAvgOffset = avgOffset;
  487. // 最后一次下发值
  488. int lastPushSystolicInc = 0;
  489. int lastPushDiastolicInc = 0;
  490. var startTime = DateTime.Now;
  491. // 下发增量值
  492. #region 统计定时下发增量值
  493. //var last = await _serviceTDengine.GetLastAsync("stb_hm_bloodpress_stats_inc", $"serialno='{imeiDel}' order by last_update desc");
  494. //var ts = last?[0];
  495. // 最后一条血压数据
  496. var condition = $"serialno='{imeiDel}' order by last_update desc";
  497. var field = "last_row(*)";
  498. var lastHmBpResponse = await _serviceTDengine.ExecuteSelectRestResponseAsync("stb_hm_bloodpress", condition, field);
  499. var lastHmBpParser = JsonConvert.DeserializeObject<ParseTDengineRestResponse<BloodPressureModel>>(lastHmBpResponse!);
  500. var lastHmBp = lastHmBpParser?.Select().FirstOrDefault();
  501. //if (lastHmBpParser?.Select()?.ToList().Count < 2)
  502. //{
  503. // _logger.LogInformation($"{imeiDel} -- {nameof(Worker)} --{nameof(WatchEvents)} -- 血压数据条目不足");
  504. // break;
  505. //}
  506. // 7 天有效数据
  507. if (lastHmBp?.Timestamp.AddDays(7) > DateTime.Now)
  508. {
  509. // 计算增量值
  510. condition = $"serialno='{imeiDel}' order by ts desc";
  511. var lastPushResponse = await _serviceTDengine.ExecuteSelectRestResponseAsync("stb_hm_bp_push_ref_inc_value", condition, field);
  512. if (lastPushResponse == null)
  513. {
  514. _logger.LogInformation($"{imeiDel}--没有下发记录");
  515. break;
  516. }
  517. var lastPushParser = JsonConvert.DeserializeObject<ParseTDengineRestResponse<BloodPressurePushRefIncModel>>(lastPushResponse);
  518. var lastPush = lastPushParser!.Select().FirstOrDefault();
  519. // 有下推记录
  520. if (lastPush != null)
  521. {
  522. systolicRefValue = lastPush!.SystolicRefValue;
  523. diastolicRefValue = lastPush!.DiastolicRefValue;
  524. lastPushSystolicInc = lastPush!.SystolicIncValue;
  525. lastPushDiastolicInc = lastPush!.DiastolicIncValue;
  526. condition = $"ts between '{lastPush?.Timestamp:yyyy-MM-dd HH:mm:ss.fff}' and '{startTime:yyyy-MM-dd HH:mm:ss.fff}' " +
  527. $"and serialno='{imeiDel}' " +
  528. $"and is_display = true";
  529. // 使用最近一次的下推时间作为统计的开始时间
  530. statStartTime = lastPush!.Timestamp;
  531. }
  532. // 没有下推记录(历史遗留数据),没有初始的测量值产生的平均值(测量值=平均值)
  533. else
  534. {
  535. #region 获取个人信息
  536. var person = await _personCacheMgr.GetDeviceGpsPersonCacheBySerialNoAsync(Guid.NewGuid().ToString(), imeiDel).ConfigureAwait(false);
  537. //验证这个信息是否存在
  538. if (person == null || person?.Person.BornDate == null)
  539. {
  540. _logger.LogWarning($"{nameof(Worker)}--{imeiDel} 验证个人信息,找不到个人信息,跳过此消息");
  541. break;
  542. }
  543. // 验证年龄是否在范围 (2 - 120)
  544. var age = SafeType.SafeInt(DateTime.Today.Year - person?.Person.BornDate!.Value.Year!);
  545. if (age < 2 || age > 120)
  546. {
  547. _logger.LogWarning($"{nameof(Worker)}--{imeiDel} 验证年龄,不在范围 (2 - 120)岁,跳过此消息");
  548. break;
  549. }
  550. var gender = person?.Person.Gender == true ? 1 : 2;
  551. var isHypertension = SafeType.SafeBool(person?.Person.Ishypertension!);
  552. var height = SafeType.SafeDouble(person?.Person.Height!);
  553. var weight = SafeType.SafeDouble(person?.Person.Weight!);
  554. #endregion
  555. #region 初始化常规血压标定值标定值
  556. var bpRef = await _bpRefValCacheManager.GetBloodPressReferenceValueAsync(age, gender, isHypertension);
  557. //systolicRefValue = bpRef!.Systolic;//?
  558. //diastolicRefValue = bpRef!.Diastolic;//?
  559. #endregion
  560. systolicRefValue = bpRef!.Systolic;
  561. diastolicRefValue = bpRef!.Diastolic;
  562. lastPushSystolicInc = 0;
  563. lastPushDiastolicInc = 0;
  564. condition = $"ts <= '{startTime:yyyy-MM-dd HH:mm:ss.fff}' " +
  565. $"and serialno='{imeiDel}' " +
  566. $"and is_display = true";
  567. }
  568. var hmBpResponse = await _serviceTDengine.ExecuteSelectRestResponseAsync("stb_hm_bloodpress", condition);
  569. var hmBpParser = JsonConvert.DeserializeObject<ParseTDengineRestResponse<BloodPressureModel>>(hmBpResponse!);
  570. var hmBp = hmBpParser?.Select();
  571. //if (hmBp?.ToList().Count < 2)
  572. // 1. 判断数据样本数量
  573. if (hmBpParser!.Rows < 5)
  574. {
  575. _logger.LogInformation($"{imeiDel} -- {nameof(Worker)} --{nameof(WatchEvents)} -- 统计定时下发,计算增量值的数据条目不足:{hmBpParser!.Rows} < 5");
  576. _logger.LogInformation($"{imeiDel} -- {nameof(Worker)} --{nameof(WatchEvents)} 没有足够的数据样本,不会定时下发");
  577. break;
  578. }
  579. // 没有下推记录重新计算统计时间
  580. if (lastPush == null)
  581. {
  582. var firstHmBp = hmBpParser?.Select(i => i).OrderBy(i => i.Timestamp).FirstOrDefault();
  583. statStartTime = firstHmBp!.Timestamp;
  584. }
  585. // GetSampleTime(systolicRefValue, hmBpParser);
  586. // 最大值
  587. //systolicMax = (int)hmBpParser?.Select(i => i.SystolicValue).Max()!;
  588. //diastolicMax = (int)hmBpParser?.Select(i => i.DiastolicValue).Max()!;
  589. //// 最小值
  590. //systolicMin = (int)hmBpParser?.Select(i => i.SystolicValue).Min()!;
  591. //diastolicMin = (int)hmBpParser?.Select(i => i.DiastolicValue).Min()!;
  592. //systolicAvg = (int)(hmBpParser?.AverageAfterRemovingOneMinMaxRef(i => i.SystolicValue, SafeType.SafeInt(systolicRefValue!)))!;
  593. //diastolicAvg = (int)(hmBpParser?.AverageAfterRemovingOneMinMaxRef(i => i.DiastolicValue, SafeType.SafeInt(diastolicRefValue!)))!;
  594. var avgs = _serviceTDengine.AverageAfterRemovingOneMinMaxRef(hmBpParser!);
  595. systolicAvg = avgs[0];
  596. diastolicAvg = avgs[1];
  597. // 2. 判断能否计算增量值
  598. if (systolicAvg.Equals(0))
  599. {
  600. _logger.LogInformation($"{imeiDel}--{nameof(Worker)}--计算平均值" +
  601. $"\n currentSystolicAvg:{systolicAvg} -- lastPushSystolicInc:{lastPushSystolicInc}" +
  602. $"\n currentDiastolicInc:{diastolicAvg} -- lastPushDiastolicInc:{lastPushDiastolicInc}");
  603. _logger.LogInformation($"{imeiDel}--{nameof(Worker)} 没有足够的数据样本计算平均值,不会定时下发");
  604. break;
  605. }
  606. // 除最大值和最小值后的平均值与标定值差值少于4后(当天计算出该结果则也不产生增量调整),就不再进行增量值调整了。
  607. if (systolicRefValue - systolicAvg < 4)
  608. {
  609. _logger.LogInformation($"diastolic 舒张压 {imeiDel}除最大值和最小值后的平均值:{diastolicAvg}与标定值:{diastolicRefValue}\n systolic 收缩压 {imeiDel}除最大值和最小值后的平均值:{systolicAvg}与标定值:{systolicRefValue}的差值(标定值-平均值)少于4后,systolic 收缩压 不再进行增量值调整");
  610. break;
  611. }
  612. if (diastolicRefValue - diastolicAvg < 4)
  613. {
  614. _logger.LogInformation($"systolic 收缩压 {imeiDel}除最大值和最小值后的平均值:{systolicAvg}与标定值:{systolicRefValue}\n diastolic 舒张压 {imeiDel}除最大值和最小值后的平均值:{diastolicAvg}与标定值:{diastolicRefValue}的差值(标定值-平均值)少于4后,diastolic 舒张压 不再进行增量值调整");
  615. break;
  616. }
  617. // 增量值=(标定值-平均值)* 0.25
  618. var currentSystolicInc = (int)((systolicRefValue - systolicAvg) * systolicAvgOffset)!;
  619. var currentDiastolicInc = (int)((diastolicRefValue - diastolicAvg) * diastolicAvgOffset)!;
  620. // 累计增量
  621. systolicInc = currentSystolicInc + lastPushSystolicInc;
  622. diastolicInc = currentDiastolicInc + lastPushDiastolicInc;
  623. _logger.LogInformation($"{imeiDel}--{nameof(Worker)}--计算增量值" +
  624. $"\n {imeiDel} -- systolicAvg:{systolicAvg}-- systolicInc:{systolicInc}-- currentSystolicInc:{currentSystolicInc} -- lastPushSystolicInc:{lastPushSystolicInc}" +
  625. $"\n {imeiDel} -- diastolicAvg:{diastolicAvg}-- diastolicInc:{diastolicInc} --currentDiastolicInc:{currentDiastolicInc} -- lastPushDiastolicInc:{lastPushDiastolicInc}");
  626. _logger.LogInformation($"{imeiDel}--{nameof(Worker)}-- 定时校准,发给设备的绝对增量值=(上次绝对增量值+新数据的增量值)");
  627. _logger.LogInformation($"{nameof(Worker)} 开启血压标定值下发: {_configBoodPressResolver.EnableBPRefPush}");
  628. if (_configBoodPressResolver.EnableBPRefPush)
  629. // if (false) // 临时关闭
  630. {
  631. BloodPressCalibrationConfigModel bpIncData = new()
  632. {
  633. Imei = imeiDel,
  634. SystolicRefValue = SafeType.SafeInt(((int)systolicRefValue!)), //收缩压标定值,值为0 表示不生效
  635. DiastolicRefValue = SafeType.SafeInt(((int)diastolicRefValue!)), //舒张压标定值,值为0表示不生效
  636. SystolicIncValue = SafeType.SafeInt(((int)systolicInc!)), //收缩压显示增量,值为0 表示不生效
  637. DiastolicIncValue = SafeType.SafeInt(((int)diastolicInc!)) //舒张压显示增量,值为0 表示不生效
  638. };
  639. //var pushedBP = await _serviceIotApi.SetBloodPressCalibrationConfigAsync(bpIncData).ConfigureAwait(false);
  640. var response = await _serviceIotApi.SetBloodPressCalibrationConfig2Async(bpIncData).ConfigureAwait(false);
  641. var pushedBP = response.Flag;
  642. if (pushedBP)
  643. {
  644. #region 保存下推记录 stb_hm_bp_push_ref_inc_value
  645. var sql = $"INSERT INTO health_monitor.hm_bp_push_ref_inc_value_{imeiDel.Substring(imeiDel.Length - 2)} " +
  646. $"USING health_monitor.stb_hm_bp_push_ref_inc_value " +
  647. $"TAGS ('{imeiDel.Substring(imeiDel.Length - 2)}') " +
  648. $"VALUES(" +
  649. $"'{startTime:yyyy-MM-dd HH:mm:ss.fff}'," +
  650. $"'{imeiDel}'," +
  651. $"{bpIncData.SystolicRefValue}," +
  652. $"{bpIncData.DiastolicRefValue}," +
  653. $"{bpIncData.SystolicIncValue}," +
  654. $"{bpIncData.DiastolicIncValue}," +
  655. $"{false}," +
  656. $"{systolicAvg}," +
  657. $"{diastolicAvg}," +
  658. $"{systolicAvgOffset}," +
  659. $"{diastolicAvgOffset}," +
  660. $"'{statStartTime:yyyy-MM-dd HH:mm:ss.fff}'," +
  661. $"'{startTime:yyyy-MM-dd HH:mm:ss.fff}'" +
  662. $")";
  663. _serviceTDengine.ExecuteInsertSQL(sql);
  664. #endregion
  665. #region 注册定时下发
  666. // 注册下次下推
  667. var endTime = DateTime.Now;
  668. #if DEBUG
  669. //long ttl = (long)((60 * 1000-(endTime-startTime).TotalMilliseconds)/1000);
  670. //await _serviceEtcd.PutValAsync(key, imeiDel,ttl, false).ConfigureAwait(false);
  671. var interval = 0;
  672. // 获取当前时间
  673. DateTime now = DateTime.Now;
  674. // 计算距离下一个$interval天后的8点的时间间隔
  675. DateTime nextRunTime = new DateTime(now.Year, now.Month, now.Day, now.Hour, now.Minute + 2, 0).AddDays(interval);
  676. TimeSpan timeUntilNextRun = nextRunTime - now;
  677. // 如果当前时间已经超过了8点,将等待到明天后的8点
  678. if (timeUntilNextRun < TimeSpan.Zero)
  679. {
  680. timeUntilNextRun = timeUntilNextRun.Add(TimeSpan.FromMinutes(1));
  681. nextRunTime += timeUntilNextRun;
  682. }
  683. // var ttl = timeUntilNextRun.TotalMilliseconds;
  684. long ttl = (long)((timeUntilNextRun.TotalMilliseconds - (endTime - startTime).TotalMilliseconds) / 1000);
  685. var data = new
  686. {
  687. imei = imeiDel,
  688. create_time = now.ToString("yyyy-MM-dd HH:mm:ss"),
  689. ttl,
  690. next_run_time = nextRunTime.ToString("yyyy-MM-dd HH:mm:ss")
  691. };
  692. var result = JsonConvert.SerializeObject(data);
  693. await _serviceEtcd.PutValAsync(key, result, ttl, false).ConfigureAwait(false);
  694. #else
  695. // 每$interval天,晚上8点
  696. var interval = 1;
  697. // 获取当前时间
  698. DateTime now = DateTime.Now;
  699. // 计算距离下一个$interval天后的8点的时间间隔
  700. DateTime nextRunTime = new DateTime(now.Year, now.Month, now.Day, 20, 0, 0).AddDays(interval);
  701. TimeSpan timeUntilNextRun = nextRunTime - now;
  702. // 如果当前时间已经超过了8点,将等待到明天后的8点
  703. if (timeUntilNextRun < TimeSpan.Zero)
  704. {
  705. timeUntilNextRun = timeUntilNextRun.Add(TimeSpan.FromDays(1));
  706. nextRunTime += timeUntilNextRun;
  707. }
  708. // var ttl = timeUntilNextRun.TotalMilliseconds;
  709. long ttl = (long)((timeUntilNextRun.TotalMilliseconds-(endTime-startTime).TotalMilliseconds)/1000);
  710. var data = new
  711. {
  712. imei = imeiDel,
  713. create_time = now.ToString("yyyy-MM-dd HH:mm:ss"),
  714. ttl,
  715. next_run_time = nextRunTime.ToString("yyyy-MM-dd HH:mm:ss")
  716. };
  717. var result = JsonConvert.SerializeObject(data);
  718. await _serviceEtcd.PutValAsync(key, result, ttl, false).ConfigureAwait(false);
  719. #endif
  720. #endregion
  721. }
  722. else
  723. {
  724. _logger.LogInformation($"错误响应,没有下推数据:{response.Message}");
  725. }
  726. }
  727. }
  728. else
  729. {
  730. _logger.LogInformation($"向{imeiDel}统计数据已经失效");
  731. }
  732. #endregion
  733. }
  734. }
  735. break;
  736. }
  737. }
  738. catch (Exception ex)
  739. {
  740. _logger.LogError($"{nameof(WatchEvents)},出错: |{ex.Message}|{ex.StackTrace}");
  741. }
  742. });
  743. }
  744. private async Task SetIntervalTriggerAsync(string key, string imei, long interval)
  745. {
  746. // var key = $"health_monitor/schedule_push/{type}/imei/{imei}";
  747. var schedulePush = await _serviceEtcd.GetValAsync(key).ConfigureAwait(false);
  748. if (string.IsNullOrWhiteSpace(schedulePush))
  749. {
  750. var now = DateTime.Now;
  751. var timeNextRun = now.Add(TimeSpan.FromSeconds(interval));
  752. var data = new
  753. {
  754. imei,
  755. create_time = now.ToString("yyyy-MM-dd HH:mm:ss"),
  756. ttl = interval,
  757. next_run_time = timeNextRun.ToString("yyyy-MM-dd HH:mm:ss")
  758. };
  759. var result = JsonConvert.SerializeObject(data);
  760. await _serviceEtcd.PutValAsync(key, result, interval, false).ConfigureAwait(false);
  761. }
  762. }
  763. public static bool IsNowInTimeRanges()
  764. {
  765. var now = DateTime.Now.TimeOfDay;
  766. var timeRanges = new List<(TimeSpan Start, TimeSpan End)>
  767. {
  768. // 8:00~10:00,12:00~14:00,18:00~20:00,22:00~24:00
  769. (new TimeSpan(8, 0, 0), new TimeSpan(10, 0, 0)),
  770. (new TimeSpan(12, 0, 0), new TimeSpan(14, 0, 0)),
  771. (new TimeSpan(18, 0, 0), new TimeSpan(20, 0, 0)),
  772. (new TimeSpan(22, 0, 0), new TimeSpan(24, 0, 0))
  773. };
  774. return timeRanges.Any(range => now >= range.Start && now <= range.End);
  775. }
  776. public static bool IsLastUpdateInTimeRanges(DateTime lastUpdate)
  777. {
  778. var now = lastUpdate.TimeOfDay;
  779. var timeRanges = new List<(TimeSpan Start, TimeSpan End)>
  780. {
  781. // 8:00~10:00,12:00~14:00,18:00~20:00,22:00~24:00
  782. (new TimeSpan(8, 0, 0), new TimeSpan(10, 0, 0)),
  783. (new TimeSpan(12, 0, 0), new TimeSpan(14, 0, 0)),
  784. (new TimeSpan(18, 0, 0), new TimeSpan(20, 0, 0)),
  785. (new TimeSpan(22, 0, 0), new TimeSpan(24, 0, 0))
  786. };
  787. return timeRanges.Any(range => now >= range.Start && now <= range.End);
  788. }
  789. /// <summary>
  790. /// 从高频心率数据中取心率值计算胎心值
  791. /// </summary>
  792. /// <param name="heartRate"></param>
  793. /// <param name="triggerHighFreqHigh"></param>
  794. /// <param name="triggerHighFreqLow"></param>
  795. /// <param name="lastPhr"></param>
  796. /// <returns></returns>
  797. private int SelectValueFromFreqHeartRate(HisGpsHeartRate heartRate, int triggerHighFreqHigh, int triggerHighFreqLow, List<PregnancyHeartRateModel> lastPhr)
  798. {
  799. // 连续12个心率的值的最小值
  800. var selectedHrValue = lastPhr.Select(i => i.PregnancyHeartRate).Min();
  801. _logger.LogInformation($"{heartRate.Serialno} 最近12个数据的最小值 {selectedHrValue}");
  802. /// 高频状态下,取值心率低于高频最小值阀值,则需要取最大值进行计算。刚好跟大于高频最大值阀值刚好相反
  803. if (selectedHrValue < triggerHighFreqLow)
  804. {
  805. // 低于高频最小值阀值,则需要取最大值
  806. var selectedHrMax = lastPhr.Select(i => i.PregnancyHeartRate).Max();
  807. _logger.LogInformation($"{heartRate.Serialno} 最近12个数据的最小值 {selectedHrValue},低于高频最小值阀值 {triggerHighFreqLow},取最近12个数据的最大值{selectedHrMax}作为心率");
  808. selectedHrValue = selectedHrMax;
  809. }
  810. if (selectedHrValue > triggerHighFreqHigh)
  811. {
  812. var selectedHrMin = lastPhr.Select(i => i.PregnancyHeartRate).Min();
  813. _logger.LogInformation($"{heartRate.Serialno} 最近12个数据的最小值 {selectedHrValue},低于高频最大值阀值 {triggerHighFreqHigh},最近12个数据的最小值{selectedHrMin}作为心率");
  814. selectedHrValue = selectedHrMin;
  815. }
  816. return selectedHrValue;
  817. }
  818. /// <summary>
  819. /// 从高频心率数据中取心率值计算胎心值
  820. /// </summary>
  821. /// <param name="sn"></param>
  822. /// <param name="triggerHighFreqHigh"></param>
  823. /// <param name="triggerHighFreqLow"></param>
  824. /// <param name="highFreqSampleTimes"></param>
  825. /// <param name="firstFreqPhr"></param>
  826. /// <param name="lastPhr"></param>
  827. /// <returns></returns>
  828. private int SelectValueFromFreqHeartRate(string sn, int triggerHighFreqHigh, int triggerHighFreqLow, int highFreqSampleTimes, PregnancyHeartRateModel firstFreqPhr, List<PregnancyHeartRateModel> lastPhr)
  829. {
  830. // 连续12个心率的值的最小值
  831. var selectedHrValue = lastPhr.Select(i => i.PregnancyHeartRate).Min();
  832. _logger.LogInformation($"{sn} 最近12个数据的最小值 {selectedHrValue}");
  833. /// 高频状态下,取值心率低于高频最小值阀值,则需要取最大值进行计算。刚好跟大于高频最大值阀值刚好相反
  834. if (selectedHrValue < triggerHighFreqLow)
  835. {
  836. // 低于高频最小值阀值,则需要取最大值
  837. var selectedHrMax = lastPhr.Select(i => i.PregnancyHeartRate).Max();
  838. _logger.LogInformation($"{sn} 最近12个数据的最小值 {selectedHrValue},低于高频最小值阀值 {triggerHighFreqLow},取最近12个数据的最大值{selectedHrMax}作为心率");
  839. selectedHrValue = selectedHrMax;
  840. }
  841. if (selectedHrValue > triggerHighFreqHigh)
  842. {
  843. var selectedHrMin = lastPhr.Select(i => i.PregnancyHeartRate).Min();
  844. _logger.LogInformation($"{sn} 最近12个数据的最小值 {selectedHrValue},低于高频最大值阀值 {triggerHighFreqHigh},最近12个数据的最小值{selectedHrMin}作为心率");
  845. selectedHrValue = selectedHrMin;
  846. }
  847. #region 判断是否有低于低频阀值引起的高频
  848. /// 低频阀值引起的高频情况下,选择的心率值是正常心率的最小值,之后的区间选择的心率值是正常心率的最小值,不足10分钟没有正常值取最小阀值,超过10分钟没有正常值取最大阀值
  849. if (firstFreqPhr.PregnancyHeartRate < triggerHighFreqLow)
  850. {
  851. _logger.LogInformation($"{sn} 低频阀值引起的高频,低频心率引起值 {firstFreqPhr.PregnancyHeartRate}");
  852. var phr = lastPhr.Where(i => i.PregnancyHeartRate <= triggerHighFreqHigh && i.PregnancyHeartRate >= triggerHighFreqLow);
  853. var lastFreqHr = lastPhr.First();
  854. var ts = DateTimeUtil.GetTimeDifferenceInSeconds((DateTime)lastFreqHr.LastUpdate!, (DateTime)firstFreqPhr.LastUpdate!);
  855. if (!phr.Any())
  856. {
  857. if (ts < highFreqSampleTimes)
  858. {
  859. selectedHrValue = triggerHighFreqLow;
  860. _logger.LogInformation($"{sn} 高频不足10分钟没有正常值取最小阀值");
  861. }
  862. else
  863. {
  864. selectedHrValue = triggerHighFreqHigh;
  865. _logger.LogInformation($"{sn} 高频超过10分钟没有正常值取最大阀值");
  866. }
  867. }
  868. else
  869. {
  870. selectedHrValue = lastPhr.Where(i => i.PregnancyHeartRate <= triggerHighFreqHigh && i.PregnancyHeartRate >= triggerHighFreqLow)
  871. .ToList()
  872. .Select(i => i.PregnancyHeartRate).Min();
  873. }
  874. }
  875. #endregion
  876. return selectedHrValue;
  877. }
  878. /// <summary>
  879. /// 高频胎心处理
  880. /// 1. 高频数据触发连续12个值都是正常的的高频心率处理
  881. /// 2. 高频结束后的highFreqSampleTimes=0的高频心率处理
  882. /// 3. 高频结束后的在highFreqSampleTimes>0 正常心率触发的高频心率处理(常态)
  883. /// 4. 高频结束后的时间倒序的正常心率触发的高频心率处理
  884. /// 5. 高频结束后计算胎心数据,防止结束后与常规心理的胎心处理过长,定时器时长highFreqSampleInterval触发的高频心率处理
  885. /// 高频结束后超过9分钟且不在阈值内才告警
  886. /// </summary>
  887. /// <param name="heartRate"></param>
  888. /// <param name="commonPHR"></param>
  889. /// <param name="highFreqSampleTimes"></param>
  890. /// <param name="upperAlarmThreshold"></param>
  891. /// <param name="lowerAlarmThreshold"></param>
  892. /// <param name="sampleTime"></param>
  893. /// <param name="statStartTime"></param>
  894. /// <param name="statEndTime"></param>
  895. /// <returns></returns>
  896. private async Task SaveAndPushFetalHeartRateEndFreqHeartRateAsyncOld(HisGpsHeartRate heartRate, PregnancyCommonHeartRateModel commonPHR, int highFreqSampleTimes, int upperAlarmThreshold, int lowerAlarmThreshold, string sampleTime, DateTime statStartTime, DateTime statEndTime)
  897. {
  898. // 计算胎心=孕妇心率*系数
  899. #region 胎心系数使用基于心率与众数对比
  900. var coefficient = 0f;
  901. /**
  902. // 孕妇心率少于众数,取StatMinValueFprCoefficient
  903. if (heartRate.HeartRate < commonPHR!.Mode)
  904. {
  905. coefficient = commonPHR.StatMinValueFprCoefficient!;
  906. _logger.LogInformation($"{heartRate.Serialno} 孕妇心率少于众数,使用最小值系数 {coefficient}");
  907. }
  908. // 孕妇心率大于众数,取StatMaxValueFprCoefficient与StatModeAvgFprCoefficient中少的那个
  909. else if (heartRate.HeartRate > commonPHR.Mode)
  910. {
  911. if (commonPHR.StatModeAvgFprCoefficient > commonPHR.StatMaxValueFprCoefficient)
  912. {
  913. coefficient = commonPHR.StatMaxValueFprCoefficient!;
  914. _logger.LogInformation($"{heartRate.Serialno} 孕妇心率大于众数,使用最大值系数 {coefficient}");
  915. }
  916. else
  917. {
  918. coefficient = commonPHR.StatModeAvgFprCoefficient!;
  919. _logger.LogInformation($"{heartRate.Serialno} 孕妇心率大于众数,使用均值系数 {coefficient}");
  920. }
  921. }
  922. else
  923. {
  924. coefficient = commonPHR.StatModeAvgFprCoefficient;
  925. _logger.LogInformation($"{heartRate.Serialno} 孕妇心率等于众数,使用均值系数 {coefficient}");
  926. }
  927. */
  928. ///1、高频最小值>心率,取高频最小值系数
  929. ///2、高频最小值 < 心率 < 众数,取众数系数与高频最小值系数较小值
  930. ///3、众数 < 心率 < 高频最大值,取众数系数与高频最大值系数较小值
  931. ///4、心率 > 高频最大值,取高频最大值系数
  932. // 1、高频最小值>心率,取高频最小值系数
  933. if (commonPHR.MinValue > heartRate.HeartRate)
  934. {
  935. coefficient = commonPHR.StatMinValueFprCoefficient!;
  936. _logger.LogInformation($"{heartRate.Serialno} 高频最小值>心率,取高频最小值系数 {coefficient}");
  937. }
  938. // 2、高频最小值 < 心率 < 众数,取众数系数与高频最小值系数较小值
  939. if (commonPHR.MinValue < heartRate.HeartRate && heartRate.HeartRate < commonPHR.Mode)
  940. {
  941. if (commonPHR.StatModeAvgFprCoefficient > commonPHR.StatMinValueFprCoefficient)
  942. {
  943. coefficient = commonPHR.StatMinValueFprCoefficient!;
  944. _logger.LogInformation($"{heartRate.Serialno} 高频最小值 < 心率 < 众数,平均值系数大于最小值系数,使用最小值系数 {coefficient}");
  945. }
  946. else
  947. {
  948. coefficient = commonPHR.StatModeAvgFprCoefficient!;
  949. _logger.LogInformation($"{heartRate.Serialno} 高频最小值 < 心率 < 众数,平均值系数小于最小值系数,使用均值系数 {coefficient}");
  950. }
  951. }
  952. // 3、众数 < 心率 < 高频最大值,取众数系数与高频最大值系数较小值
  953. if (commonPHR.Mode < heartRate.HeartRate && heartRate.HeartRate < commonPHR.MaxValue)
  954. {
  955. if (commonPHR.StatModeAvgFprCoefficient > commonPHR.StatMaxValueFprCoefficient)
  956. {
  957. coefficient = commonPHR.StatMaxValueFprCoefficient!;
  958. _logger.LogInformation($"{heartRate.Serialno} 众数 < 心率 < 高频最大值,平均值系数大于最最大值系数,使用最大值系数 {coefficient}");
  959. }
  960. else
  961. {
  962. coefficient = commonPHR.StatModeAvgFprCoefficient!;
  963. _logger.LogInformation($"{heartRate.Serialno} 众数 < 心率 < 高频最大值,平均值系数小于最小值系数,使用均值系数 {coefficient}");
  964. }
  965. }
  966. // 4、心率 > 高频最大值,取高频最大值系数
  967. if (heartRate.HeartRate > commonPHR.MaxValue)
  968. {
  969. coefficient = commonPHR.StatMaxValueFprCoefficient!;
  970. _logger.LogInformation($"{heartRate.Serialno} 心率 > 高频最大值,取高频最大值系数 {coefficient}");
  971. }
  972. if (heartRate.HeartRate == commonPHR.Mode)
  973. {
  974. coefficient = commonPHR.StatModeAvgFprCoefficient!;
  975. _logger.LogInformation($"{heartRate.Serialno} 心率 == 众数,取均值系数 {coefficient}");
  976. }
  977. #endregion
  978. var fetalHeartRate = SafeType.SafeInt(heartRate.HeartRate! * coefficient);
  979. // 胎心的最大值调整为220,超过都按该值220输出
  980. // fetalHeartRate = fetalHeartRate>= 220 ? 220 : fetalHeartRate;
  981. var phrFreqstatus = await _deviceCacheMgr.GetPregnancyHeartRateFreqStatusAsync(heartRate.Serialno);
  982. var isAbnormal = 0;
  983. #region 判断是否够highFreqSampleTimes,540s
  984. var ts = DateTimeUtil.GetTimeDifferenceInSeconds((DateTime)heartRate.LastUpdate!, phrFreqstatus!.LastUpdate);
  985. // 判断是否够highFreqSampleTimes,540s
  986. ///高频时长不足10分钟停止高频的胎心值生成说明:最近12个数据的最小值生成胎心值,
  987. ///对于小于高频下限阀值取高频下限阀值进行转换;
  988. ///对于高于高频上限阀值取高频上限阀值进行转换。并输出相关日志后续进行数据分析。
  989. if (ts < highFreqSampleTimes)
  990. {
  991. if (fetalHeartRate > upperAlarmThreshold)
  992. {
  993. _logger.LogWarning($"{heartRate.Serialno} 高频持续不足10分钟,计算胎心值 {fetalHeartRate} 高于高频警告上限阀值{upperAlarmThreshold},最后胎心值{upperAlarmThreshold},并且不告警");
  994. fetalHeartRate = upperAlarmThreshold;
  995. }
  996. else if (fetalHeartRate < lowerAlarmThreshold)
  997. {
  998. _logger.LogWarning($"{heartRate.Serialno} 高频持续不足10分钟,计算胎心值 {fetalHeartRate} 低于高频警告下限阀值 {lowerAlarmThreshold},最后胎心值{lowerAlarmThreshold},并且不告警");
  999. fetalHeartRate = lowerAlarmThreshold;
  1000. }
  1001. else
  1002. {
  1003. _logger.LogWarning($"{heartRate.Serialno} 高频持续不足10分钟,在高频警告下限阀值 {lowerAlarmThreshold} 和 高频警告上限阀值:{upperAlarmThreshold}之间,最后胎心值{fetalHeartRate},并且不告警");
  1004. }
  1005. }
  1006. // 超过highFreqSampleTimes,540s
  1007. else
  1008. {
  1009. if (fetalHeartRate > 220)
  1010. {
  1011. fetalHeartRate = 220;
  1012. _logger.LogWarning($"{heartRate.Serialno} 大于220,按220输出,计算因子:孕妇心率 {heartRate.HeartRate},系数 {coefficient},周期 周期:{statStartTime.ToString("yyyy-MM-dd HH:mm:ss")}----{statEndTime.ToString("yyyy-MM-dd HH:mm:ss")}");
  1013. }
  1014. // 胎心的最小值调整为90,超过都按该值90
  1015. if (fetalHeartRate < 90)
  1016. {
  1017. fetalHeartRate = 90;
  1018. _logger.LogWarning($"{heartRate.Serialno} 小于90,按90输出,计算因子:孕妇心率 {heartRate.HeartRate},系数 {coefficient},周期 周期:{statStartTime.ToString("yyyy-MM-dd HH:mm:ss")}----{statEndTime.ToString("yyyy-MM-dd HH:mm:ss")}");
  1019. }
  1020. isAbnormal = fetalHeartRate > upperAlarmThreshold ? 1 : (fetalHeartRate < lowerAlarmThreshold ? 2 : 0);
  1021. }
  1022. #endregion
  1023. //if (phrFreqstatus == null) isAbnormal = 0;
  1024. //var statsusDesc = (phrFreqstatus == null) ? "常规" : "高频";
  1025. var statsusDesc = "高频";
  1026. _logger.LogInformation($"{heartRate.Serialno} 在 {statsusDesc} 状态,生成胎心值:{fetalHeartRate},统计周期:{statStartTime.ToString("yyyy-MM-dd HH:mm:ss")}----{statEndTime.ToString("yyyy-MM-dd HH:mm:ss")}");
  1027. // 保存到 数据服务 MySQL 数据库
  1028. HisGpsFetalHeartRate gpsFetalHeartRate = new()
  1029. {
  1030. FetalHeartRateId = Guid.NewGuid().ToString("D"),
  1031. PersonId = commonPHR!.PersonId,
  1032. Serialno = heartRate.Serialno,
  1033. HeartRate = fetalHeartRate,
  1034. SampleTime = sampleTime.Length > 10 ? sampleTime.Substring(0, 10) : sampleTime,
  1035. IsAbnormal = isAbnormal,
  1036. StatStartTime = statStartTime,
  1037. StatEndTime = statEndTime,//commonPHR.StatEndTime,
  1038. CreateTime = DateTime.Now,
  1039. Method = 1,
  1040. IsDisplay = 1,
  1041. DeviceKey = commonPHR!.DeviceKey
  1042. };
  1043. await _hisFetalHeartApiClient.AddAsync(gpsFetalHeartRate).ConfigureAwait(false);
  1044. #region 高频心率计算胎心数据到iot设备
  1045. //if (phrFreqstatus != null && isAbnormal != 0)
  1046. //{
  1047. // await _serviceIotApi.SetFetalHeartRateConfig(heartRate.Serialno, fetalHeartRate, sampleTime, isAbnormal);
  1048. //}
  1049. // 高频有数据都推送到iot
  1050. await _serviceIotApi.SetFetalHeartRateConfig(heartRate.Serialno, fetalHeartRate, sampleTime, isAbnormal);
  1051. #endregion
  1052. var device = await _deviceCacheMgr.GetDeviceBySerialNoAsync(heartRate.Serialno).ConfigureAwait(false);
  1053. var fhrMsgId = $"{heartRate.Serialno}-{sampleTime}-{Guid.NewGuid().ToString("D")[^3..]}";
  1054. var fhrMsgTime = DateTimeUtil.GetDateTimeFromUnixTimeMilliseconds(long.Parse(sampleTime.Length < 13 ? sampleTime.PadRight(13, '0') : sampleTime)).ToString("yyyy-MM-dd HH:mm:ss");
  1055. // 胎心数据推送到第三方
  1056. var topic = "topic.push.third";
  1057. var fhrThridMsg = new
  1058. {
  1059. messageId = fhrMsgId,
  1060. topic = topic,
  1061. time = fhrMsgTime,
  1062. data = new
  1063. {
  1064. imei = heartRate.Serialno,
  1065. value = fetalHeartRate,
  1066. isAbnormal,
  1067. type = "fetalHeart"
  1068. }
  1069. };
  1070. await _serviceMqProcess.ProcessIMEIEventMessageAsync(fhrMsgId, topic, 31, fhrThridMsg).ConfigureAwait(false);
  1071. // 胎心数据推送到微信
  1072. if (isAbnormal != 0)
  1073. {
  1074. topic = "topic.push.wx";
  1075. var fhrMsg = new
  1076. {
  1077. messageId = fhrMsgId,
  1078. topic = topic,
  1079. time = fhrMsgTime,
  1080. data = new
  1081. {
  1082. deviceId = device?.DeviceId,
  1083. imei = heartRate.Serialno,
  1084. alarmTypeId = 12,
  1085. alarmDeviceName = heartRate.Serialno,
  1086. alarmRemarks = JsonConvert.SerializeObject(new { fetalHeartValue = fetalHeartRate, isAbnormal = isAbnormal }),
  1087. address = string.Empty,
  1088. deviceKey = device?.DeviceId
  1089. }
  1090. };
  1091. await _serviceMqProcess.ProcessIMEIEventMessageAsync(fhrMsgId, topic, fhrMsg).ConfigureAwait(false);
  1092. }
  1093. }
  1094. /// <summary>
  1095. /// 高频胎心处理
  1096. /// 1. 高频数据触发连续12个值都是正常的的高频心率处理
  1097. /// 2. 高频结束后的highFreqSampleTimes=0的高频心率处理
  1098. /// 3. 高频结束后的在highFreqSampleTimes>0 正常心率触发的高频心率处理(常态)
  1099. /// 4. 高频结束后的时间倒序的正常心率触发的高频心率处理
  1100. /// 5. 高频结束后计算胎心数据,防止结束后与常规心理的胎心处理过长,定时器时长highFreqSampleInterval触发的高频心率处理
  1101. /// 高频结束后超过9分钟且不在阈值内才告警
  1102. /// </summary>
  1103. /// <param name="heartRate"></param>
  1104. /// <param name="commonPHR"></param>
  1105. /// <param name="highFreqSampleTimes"></param>
  1106. /// <param name="upperAlarmThreshold"></param>
  1107. /// <param name="lowerAlarmThreshold"></param>
  1108. /// <param name="sampleTime"></param>
  1109. /// <param name="statStartTime"></param>
  1110. /// <param name="statEndTime"></param>
  1111. /// <returns></returns>
  1112. private async Task SaveAndPushFetalHeartRateEndFreqHeartRateAsync(HisGpsHeartRate heartRate, PregnancyCommonHeartRateModel commonPHR, int highFreqSampleTimes, int upperAlarmThreshold, int lowerAlarmThreshold, string sampleTime, DateTime statStartTime, DateTime statEndTime)
  1113. {
  1114. var fetalHeartRate = await _serviceTDengine.GetFetalHeartRateAsync(heartRate.Serialno, (int)heartRate.HeartRate!);
  1115. var phrFreqstatus = await _deviceCacheMgr.GetPregnancyHeartRateFreqStatusAsync(heartRate.Serialno);
  1116. var isAbnormal = 0;
  1117. #region 判断是否够highFreqSampleTimes,540s
  1118. var ts = DateTimeUtil.GetTimeDifferenceInSeconds((DateTime)heartRate.LastUpdate!, phrFreqstatus!.LastUpdate);
  1119. // 判断是否够highFreqSampleTimes,540s
  1120. ///高频时长不足10分钟停止高频的胎心值生成说明:最近12个数据的最小值生成胎心值,
  1121. ///对于小于高频下限阀值取高频下限阀值进行转换;
  1122. ///对于高于高频上限阀值取高频上限阀值进行转换。并输出相关日志后续进行数据分析。
  1123. if (ts < highFreqSampleTimes)
  1124. {
  1125. if (fetalHeartRate > upperAlarmThreshold)
  1126. {
  1127. _logger.LogWarning($"{heartRate.Serialno} 高频持续不足10分钟,计算胎心值 {fetalHeartRate} 高于高频警告上限阀值{upperAlarmThreshold},最后胎心值{upperAlarmThreshold},并且不告警");
  1128. fetalHeartRate = upperAlarmThreshold;
  1129. }
  1130. else if (fetalHeartRate < lowerAlarmThreshold)
  1131. {
  1132. _logger.LogWarning($"{heartRate.Serialno} 高频持续不足10分钟,计算胎心值 {fetalHeartRate} 低于高频警告下限阀值 {lowerAlarmThreshold},最后胎心值{lowerAlarmThreshold},并且不告警");
  1133. fetalHeartRate = lowerAlarmThreshold;
  1134. }
  1135. else
  1136. {
  1137. _logger.LogWarning($"{heartRate.Serialno} 高频持续不足10分钟,在高频警告下限阀值 {lowerAlarmThreshold} 和 高频警告上限阀值:{upperAlarmThreshold}之间,最后胎心值{fetalHeartRate},并且不告警");
  1138. }
  1139. }
  1140. // 超过highFreqSampleTimes,540s
  1141. else
  1142. {
  1143. if (fetalHeartRate > 220)
  1144. {
  1145. fetalHeartRate = 220;
  1146. _logger.LogWarning($"{heartRate.Serialno} 大于220,按220输出,计算因子:孕妇心率 {heartRate.HeartRate},周期:{statStartTime.ToString("yyyy-MM-dd HH:mm:ss")}----{statEndTime.ToString("yyyy-MM-dd HH:mm:ss")}");
  1147. }
  1148. // 胎心的最小值调整为90,超过都按该值90
  1149. if (fetalHeartRate < 90)
  1150. {
  1151. fetalHeartRate = 90;
  1152. _logger.LogWarning($"{heartRate.Serialno} 小于90,按90输出,计算因子:孕妇心率 {heartRate.HeartRate}, 周期:{statStartTime.ToString("yyyy-MM-dd HH:mm:ss")}----{statEndTime.ToString("yyyy-MM-dd HH:mm:ss")}");
  1153. }
  1154. isAbnormal = fetalHeartRate > upperAlarmThreshold ? 1 : (fetalHeartRate < lowerAlarmThreshold ? 2 : 0);
  1155. }
  1156. #endregion
  1157. _logger.LogInformation($"{heartRate.Serialno} 在 高频 状态,生成胎心值:{fetalHeartRate},统计周期:{statStartTime.ToString("yyyy-MM-dd HH:mm:ss")}----{statEndTime.ToString("yyyy-MM-dd HH:mm:ss")}");
  1158. // 保存到 数据服务 MySQL 数据库
  1159. HisGpsFetalHeartRate gpsFetalHeartRate = new()
  1160. {
  1161. FetalHeartRateId = Guid.NewGuid().ToString("D"),
  1162. PersonId = commonPHR!.PersonId,
  1163. Serialno = heartRate.Serialno,
  1164. HeartRate = fetalHeartRate,
  1165. SampleTime = sampleTime.Length > 10 ? sampleTime.Substring(0, 10) : sampleTime,
  1166. IsAbnormal = isAbnormal,
  1167. StatStartTime = statStartTime,
  1168. StatEndTime = statEndTime,//commonPHR.StatEndTime,
  1169. CreateTime = DateTime.Now,
  1170. Method = 1,
  1171. IsDisplay = 1,
  1172. DeviceKey = commonPHR!.DeviceKey
  1173. };
  1174. await _hisFetalHeartApiClient.AddAsync(gpsFetalHeartRate).ConfigureAwait(false);
  1175. #region 高频心率计算胎心数据到iot设备
  1176. //if (phrFreqstatus != null && isAbnormal != 0)
  1177. //{
  1178. // await _serviceIotApi.SetFetalHeartRateConfig(heartRate.Serialno, fetalHeartRate, sampleTime, isAbnormal);
  1179. //}
  1180. // 高频有数据都推送到iot
  1181. await _serviceIotApi.SetFetalHeartRateConfig(heartRate.Serialno, fetalHeartRate, sampleTime, isAbnormal);
  1182. #endregion
  1183. var device = await _deviceCacheMgr.GetDeviceBySerialNoAsync(heartRate.Serialno).ConfigureAwait(false);
  1184. var fhrMsgId = $"{heartRate.Serialno}-{sampleTime}-{Guid.NewGuid().ToString("D")[^3..]}";
  1185. var fhrMsgTime = DateTimeUtil.GetDateTimeFromUnixTimeMilliseconds(long.Parse(sampleTime.Length < 13 ? sampleTime.PadRight(13, '0') : sampleTime)).ToString("yyyy-MM-dd HH:mm:ss");
  1186. // 胎心数据推送到第三方
  1187. var topic = "topic.push.third";
  1188. var fhrThridMsg = new
  1189. {
  1190. messageId = fhrMsgId,
  1191. topic = topic,
  1192. time = fhrMsgTime,
  1193. data = new
  1194. {
  1195. imei = heartRate.Serialno,
  1196. value = fetalHeartRate,
  1197. isAbnormal,
  1198. type = "fetalHeart"
  1199. }
  1200. };
  1201. await _serviceMqProcess.ProcessIMEIEventMessageAsync(fhrMsgId, topic, 31, fhrThridMsg).ConfigureAwait(false);
  1202. // 胎心数据推送到微信
  1203. if (isAbnormal != 0)
  1204. {
  1205. topic = "topic.push.wx";
  1206. var fhrMsg = new
  1207. {
  1208. messageId = fhrMsgId,
  1209. topic = topic,
  1210. time = fhrMsgTime,
  1211. data = new
  1212. {
  1213. deviceId = device?.DeviceId,
  1214. imei = heartRate.Serialno,
  1215. alarmTypeId = 12,
  1216. alarmDeviceName = heartRate.Serialno,
  1217. alarmRemarks = JsonConvert.SerializeObject(new { fetalHeartValue = fetalHeartRate, isAbnormal = isAbnormal }),
  1218. address = string.Empty,
  1219. deviceKey = device?.DeviceId
  1220. }
  1221. };
  1222. await _serviceMqProcess.ProcessIMEIEventMessageAsync(fhrMsgId, topic, fhrMsg).ConfigureAwait(false);
  1223. }
  1224. }
  1225. /// <summary>
  1226. /// 高频胎心处理
  1227. /// 1. 高频数据触发连续12个值都是正常的的高频心率处理
  1228. /// 2. 高频结束后的highFreqSampleTimes=0的高频心率处理
  1229. /// 3. 高频结束后的在highFreqSampleTimes>0 正常心率触发的高频心率处理(常态)
  1230. /// 4. 高频结束后的时间倒序的正常心率触发的高频心率处理
  1231. /// 5. 高频结束后计算胎心数据,防止结束后与常规心理的胎心处理过长,定时器时长highFreqSampleInterval触发的高频心率处理
  1232. /// 高频结束后超过9分钟且不在阈值内才告警
  1233. /// </summary>
  1234. /// <param name="heartRate"></param>
  1235. /// <param name="commonPHR"></param>
  1236. /// <param name="highFreqSampleTimes"></param>
  1237. /// <param name="upperAlarmThreshold"></param>
  1238. /// <param name="lowerAlarmThreshold"></param>
  1239. /// <param name="sampleTime"></param>
  1240. /// <param name="statStartTime"></param>
  1241. /// <param name="statEndTime"></param>
  1242. /// <returns></returns>
  1243. private async Task SaveAndPushFetalHeartRateEndFreqHeartRateAsync(
  1244. HisGpsHeartRate heartRate,
  1245. PregnancyCommonHeartRateModel commonPHR,
  1246. int highFreqSampleTimes,
  1247. int upperAlarmThreshold,
  1248. int lowerAlarmThreshold,
  1249. string sampleTime,
  1250. DateTime statStartTime,
  1251. DateTime statEndTime,
  1252. List<PregnancyHeartRateModel> lastPhr,
  1253. int triggerHighFreqHigh,
  1254. int triggerHighFreqLow)
  1255. {
  1256. try
  1257. {
  1258. #region 选择心率值计算胎心
  1259. /// 12个心率数据判定上限处理模式还是下限处理模式:
  1260. /// 1、12个数据的最大值 > 高频上限阀值,则按上限模式进行计算。取12个值的最小值心率值转换为胎心值。
  1261. ///
  1262. /// 2、12个数据的最小值 < 高频下限阀值,则按下限模式进行计算。取正常范围内的最小值心率值转换为胎心值,不产生告警。
  1263. /// 如果正常范围内没有值,则取异常范围最大值心率值转换为胎心值,并产生胎心过缓告警。
  1264. var phrFreqstatus = await _deviceCacheMgr.GetPregnancyHeartRateFreqStatusAsync(heartRate.Serialno);
  1265. var isAbnormal = 0;
  1266. //取正常范围内的最小值心率值转换为胎心值,不产生告警。
  1267. var lastNormalPhr = lastPhr.Where(i => i.PregnancyHeartRate > triggerHighFreqLow && i.PregnancyHeartRate < triggerHighFreqHigh);
  1268. int selectedHrValue = (int)heartRate.HeartRate!;
  1269. if (lastPhr.Select(i => i.PregnancyHeartRate).Max() > triggerHighFreqHigh)
  1270. {
  1271. // 取12个值的最小值心率值转换为胎心值。
  1272. selectedHrValue = lastPhr.Select(i => i.PregnancyHeartRate).Min();
  1273. _logger.LogInformation($"{heartRate.Serialno} 取12个值的最小值心率值转换为胎心值");
  1274. }
  1275. if (lastPhr.Select(i => i.PregnancyHeartRate).Min() < triggerHighFreqLow)
  1276. {
  1277. // 有正常值
  1278. if (lastNormalPhr.Any())
  1279. {
  1280. // 12个数据的最小值 < 高频下限阀值,则按下限模式进行计算。取正常范围内的最小值心率值转换为胎心值,不产生告警
  1281. selectedHrValue = lastNormalPhr
  1282. .Select(i => i.PregnancyHeartRate)
  1283. .Min();
  1284. _logger.LogInformation($"{heartRate.Serialno} 有正常值,取正常范围内的最小值心率值转换为胎心值,不产生告警");
  1285. }
  1286. // 无正常值
  1287. if (!lastNormalPhr.Any())
  1288. {
  1289. selectedHrValue = lastPhr
  1290. .Select(i => i.PregnancyHeartRate)
  1291. .Max();
  1292. _logger.LogInformation($"{heartRate.Serialno} 无正常值,则取异常范围最大值心率值转换为胎心值,并产生胎心过缓告警");
  1293. }
  1294. }
  1295. _logger.LogInformation($"{heartRate.Serialno} 高频选择心率值:{selectedHrValue}");
  1296. var fetalHeartRate = await _serviceTDengine.GetFetalHeartRateAsync(heartRate.Serialno, selectedHrValue);
  1297. _logger.LogInformation($"{heartRate.Serialno} 高频状态计算,由高频心率 {selectedHrValue} 根据胎心算法转换的胎心值 {fetalHeartRate}");
  1298. #endregion
  1299. #region 判断是否够highFreqSampleTimes,540s
  1300. var ts = DateTimeUtil.GetTimeDifferenceInSeconds(statEndTime, statStartTime);
  1301. // 判断是否够highFreqSampleTimes,540s
  1302. ///高频时长不足10分钟停止高频的胎心值生成说明:最近12个数据的最小值生成胎心值,
  1303. ///对于小于高频下限阀值取高频下限阀值进行转换;
  1304. ///对于高于高频上限阀值取高频上限阀值进行转换。并输出相关日志后续进行数据分析。
  1305. if (ts < highFreqSampleTimes)
  1306. {
  1307. if (fetalHeartRate > upperAlarmThreshold)
  1308. {
  1309. _logger.LogWarning($"{heartRate.Serialno} 高频持续不足10分钟,计算胎心值 {fetalHeartRate} 高于高频警告上限阀值{upperAlarmThreshold},最后胎心值{upperAlarmThreshold},并且不告警");
  1310. fetalHeartRate = upperAlarmThreshold;
  1311. }
  1312. else if (fetalHeartRate < lowerAlarmThreshold)
  1313. {
  1314. _logger.LogWarning($"{heartRate.Serialno} 高频持续不足10分钟,计算胎心值 {fetalHeartRate} 低于高频警告下限阀值 {lowerAlarmThreshold},最后胎心值{lowerAlarmThreshold},并且不告警");
  1315. fetalHeartRate = lowerAlarmThreshold;
  1316. }
  1317. else
  1318. {
  1319. _logger.LogWarning($"{heartRate.Serialno} 高频持续不足10分钟,在高频警告下限阀值 {lowerAlarmThreshold} 和 高频警告上限阀值:{upperAlarmThreshold}之间,最后胎心值{fetalHeartRate},并且不告警");
  1320. }
  1321. isAbnormal = 0;
  1322. }
  1323. // 超过highFreqSampleTimes
  1324. else
  1325. {
  1326. if (fetalHeartRate > 220)
  1327. {
  1328. fetalHeartRate = 220;
  1329. _logger.LogWarning($"{heartRate.Serialno} 大于220,按220输出,计算因子:孕妇心率 {heartRate.HeartRate},周期:{statStartTime.ToString("yyyy-MM-dd HH:mm:ss")}----{statEndTime.ToString("yyyy-MM-dd HH:mm:ss")}");
  1330. }
  1331. // 胎心的最小值调整为90,超过都按该值90
  1332. if (fetalHeartRate < 90)
  1333. {
  1334. fetalHeartRate = 90;
  1335. _logger.LogWarning($"{heartRate.Serialno} 小于90,按90输出,计算因子:孕妇心率 {heartRate.HeartRate}, 周期:{statStartTime.ToString("yyyy-MM-dd HH:mm:ss")}----{statEndTime.ToString("yyyy-MM-dd HH:mm:ss")}");
  1336. }
  1337. isAbnormal = fetalHeartRate > upperAlarmThreshold ? 1 : (fetalHeartRate < lowerAlarmThreshold ? 2 : 0);
  1338. //if (!lastNormalPhr.Any())
  1339. //{
  1340. // if (selectedHrValue == lastPhr.Select(i => i.PregnancyHeartRate).Max())
  1341. // {
  1342. // // 偏低(过缓)
  1343. // isAbnormal = 2;
  1344. // _logger.LogInformation($"{heartRate.Serialno} 超过10分钟其没有正常值,则取异常范围最大值心率值转换为胎心值,并产生胎心过缓告警设置胎心过缓告警");
  1345. // }
  1346. //}
  1347. }
  1348. #endregion
  1349. _logger.LogInformation($"{heartRate.Serialno} 在 高频 状态,生成胎心值:{fetalHeartRate},统计周期:{statStartTime.ToString("yyyy-MM-dd HH:mm:ss")}----{statEndTime.ToString("yyyy-MM-dd HH:mm:ss")}");
  1350. // 保存到 数据服务 MySQL 数据库
  1351. HisGpsFetalHeartRate gpsFetalHeartRate = new()
  1352. {
  1353. FetalHeartRateId = Guid.NewGuid().ToString("D"),
  1354. PersonId = commonPHR!.PersonId,
  1355. Serialno = heartRate.Serialno,
  1356. HeartRate = fetalHeartRate,
  1357. SampleTime = sampleTime.Length > 10 ? sampleTime.Substring(0, 10) : sampleTime,
  1358. IsAbnormal = isAbnormal,
  1359. StatStartTime = statStartTime,
  1360. StatEndTime = statEndTime,//commonPHR.StatEndTime,
  1361. CreateTime = DateTime.Now,
  1362. Method = 1,
  1363. IsDisplay = 1,
  1364. DeviceKey = commonPHR!.DeviceKey
  1365. };
  1366. await _hisFetalHeartApiClient.AddAsync(gpsFetalHeartRate).ConfigureAwait(false);
  1367. #region 高频心率计算胎心数据到iot设备
  1368. //if (phrFreqstatus != null && isAbnormal != 0)
  1369. //{
  1370. // await _serviceIotApi.SetFetalHeartRateConfig(heartRate.Serialno, fetalHeartRate, sampleTime, isAbnormal);
  1371. //}
  1372. // 高频有数据都推送到iot
  1373. await _serviceIotApi.SetFetalHeartRateConfig(heartRate.Serialno, fetalHeartRate, sampleTime, isAbnormal);
  1374. #endregion
  1375. var device = await _deviceCacheMgr.GetDeviceBySerialNoAsync(heartRate.Serialno).ConfigureAwait(false);
  1376. var fhrMsgId = $"{heartRate.Serialno}-{sampleTime}-{Guid.NewGuid().ToString("D")[^3..]}";
  1377. var fhrMsgTime = DateTimeUtil.GetDateTimeFromUnixTimeMilliseconds(long.Parse(sampleTime.Length < 13 ? sampleTime.PadRight(13, '0') : sampleTime)).ToString("yyyy-MM-dd HH:mm:ss");
  1378. // 胎心数据推送到第三方
  1379. var topic = "topic.push.third";
  1380. var fhrThridMsg = new
  1381. {
  1382. messageId = fhrMsgId,
  1383. topic = topic,
  1384. time = fhrMsgTime,
  1385. data = new
  1386. {
  1387. imei = heartRate.Serialno,
  1388. value = fetalHeartRate,
  1389. isAbnormal,
  1390. type = "fetalHeart"
  1391. }
  1392. };
  1393. await _serviceMqProcess.ProcessIMEIEventMessageAsync(fhrMsgId, topic, 31, fhrThridMsg).ConfigureAwait(false);
  1394. // 胎心数据推送到微信
  1395. if (isAbnormal != 0)
  1396. {
  1397. topic = "topic.push.wx";
  1398. var fhrMsg = new
  1399. {
  1400. messageId = fhrMsgId,
  1401. topic = topic,
  1402. time = fhrMsgTime,
  1403. data = new
  1404. {
  1405. deviceId = device?.DeviceId,
  1406. imei = heartRate.Serialno,
  1407. alarmTypeId = 12,
  1408. alarmDeviceName = heartRate.Serialno,
  1409. alarmRemarks = JsonConvert.SerializeObject(new { fetalHeartValue = fetalHeartRate, isAbnormal = isAbnormal }),
  1410. address = string.Empty,
  1411. deviceKey = device?.DeviceId
  1412. }
  1413. };
  1414. await _serviceMqProcess.ProcessIMEIEventMessageAsync(fhrMsgId, topic, fhrMsg).ConfigureAwait(false);
  1415. }
  1416. }
  1417. catch (Exception ex)
  1418. {
  1419. _logger.LogError($"{heartRate.Serialno} 计算高频心率出错{ex.Message}\n{ex.StackTrace}");
  1420. }
  1421. }
  1422. /// <summary>
  1423. /// 去除高频数据
  1424. /// </summary>
  1425. /// <param name="phr"></param>
  1426. /// <param name="highFreqSampleInterva"></param>
  1427. /// <returns></returns>
  1428. private static List<PregnancyHeartRateModel> GetNonFreqPregnancyHeartRate(List<PregnancyHeartRateModel> phr, int highFreqSampleInterval)
  1429. {
  1430. #region 反向
  1431. var phr1 = phr.OrderByDescending(i => i.LastUpdate).ToList();
  1432. var result = new List<PregnancyHeartRateModel>();
  1433. PregnancyHeartRateModel? previousItem1 = null;
  1434. foreach (var item in phr1)
  1435. {
  1436. if (previousItem1 != null)
  1437. {
  1438. var timeNextDiff = (previousItem1!.LastUpdate - item.LastUpdate).TotalSeconds;
  1439. if (timeNextDiff > highFreqSampleInterval)
  1440. {
  1441. result.Add(previousItem1);
  1442. }
  1443. }
  1444. previousItem1 = item;
  1445. }
  1446. // 添加上一个
  1447. if (previousItem1 != null)
  1448. {
  1449. result.Add(previousItem1);
  1450. }
  1451. #endregion
  1452. #region 正向
  1453. var phr2 = phr.OrderByDescending(i => i.LastUpdate).ToList(); ;
  1454. var freqCollection = new List<PregnancyHeartRateModel>();
  1455. PregnancyHeartRateModel? previousItem = null;
  1456. foreach (var item in phr2)
  1457. {
  1458. if (previousItem != null)
  1459. {
  1460. var timeNextDiff = (previousItem!.LastUpdate - item.LastUpdate).TotalSeconds;
  1461. if (timeNextDiff <= highFreqSampleInterval)
  1462. {
  1463. freqCollection.Add(item);
  1464. }
  1465. }
  1466. previousItem = item;
  1467. }
  1468. //去除高频
  1469. foreach (var item in freqCollection)
  1470. {
  1471. phr2.Remove(item);
  1472. }
  1473. #endregion
  1474. // 交集
  1475. var commonElements = phr2.Intersect(result).ToList();
  1476. return commonElements;
  1477. }
  1478. /// <summary>
  1479. /// 获取高频数据
  1480. /// </summary>
  1481. /// <param name="phr"></param>
  1482. /// <param name="highFreqSampleInterval"></param>
  1483. /// <returns></returns>
  1484. private static List<PregnancyHeartRateModel> GetFreqPregnancyHeartRate(List<PregnancyHeartRateModel> phr, int highFreqSampleInterval)
  1485. {
  1486. phr = phr.OrderByDescending(i => i.LastUpdate).ToList();
  1487. var freqCollection = new List<PregnancyHeartRateModel>();
  1488. PregnancyHeartRateModel? previousItem = null;
  1489. foreach (var item in phr)
  1490. {
  1491. if (previousItem != null)
  1492. {
  1493. var timeNextDiff = (previousItem.LastUpdate - item.LastUpdate).TotalSeconds;
  1494. if (timeNextDiff <= highFreqSampleInterval)
  1495. {
  1496. freqCollection.Add(previousItem);
  1497. }
  1498. }
  1499. previousItem = item;
  1500. }
  1501. // 检查最后一条是否高频
  1502. if (previousItem != null && (phr.Last().LastUpdate - previousItem.LastUpdate).TotalSeconds <= highFreqSampleInterval)
  1503. {
  1504. freqCollection.Add(previousItem);
  1505. }
  1506. return freqCollection;
  1507. }
  1508. /// <summary>
  1509. /// 周期性常规心率计算胎心值
  1510. /// </summary>
  1511. /// <param name="heartRate"></param>
  1512. /// <param name="commonPHR"></param>
  1513. /// <param name="highFreqSampleInterval"></param>
  1514. /// <returns></returns>
  1515. public async Task CalculateNormalFetalHeartRateIntervalAsyncOld(HisGpsHeartRate heartRate, PregnancyCommonHeartRateModel commonPHR,int highFreqSampleInterval)
  1516. {
  1517. var daysPhr = await _serviceTDengine.GetBySerialNoAsync<PregnancyHeartRateModel>(heartRate.Serialno, 7);
  1518. var now = DateTime.Now;
  1519. var filteredPhr = daysPhr.Where(i => i.LastUpdate >= heartRate.LastUpdate && i.LastUpdate <= now).ToList();
  1520. // 去除高频
  1521. var normalPhr = GetNonFreqPregnancyHeartRate(filteredPhr, highFreqSampleInterval).OrderBy(i => i.LastUpdate);
  1522. if (normalPhr.ToList().Count==0)
  1523. {
  1524. _logger.LogWarning($"{heartRate.Serialno} 时间段{heartRate.LastUpdate}-{now},去除高频心率数据后,没有常规数据,不计算常规胎心数据");
  1525. return;
  1526. }
  1527. var startPhr = normalPhr.First();
  1528. var endPhr = normalPhr.Last();
  1529. //var sampleTime = GetSampleTimeFromLastUpdate((DateTime)heartRate.LastUpdate!, INTERVAL_FHR);
  1530. // 数据统计边界
  1531. var boundaryStatStartTime = GetSampleTimeFromLastUpdate((DateTime)startPhr.LastUpdate!, INTERVAL_FHR);
  1532. var boundaryStatEndTime = GetSampleTimeFromLastUpdate((DateTime)endPhr.LastUpdate!, INTERVAL_FHR).AddMinutes(INTERVAL_FHR);
  1533. _logger.LogInformation($"{heartRate.Serialno} 常规胎心统计边界{boundaryStatStartTime}-{boundaryStatEndTime}");
  1534. try
  1535. {
  1536. //var CalNow = DateTime.Now;
  1537. //var during = TimeSpan.FromSeconds(300); //5分钟
  1538. var c = 0;
  1539. while (true)
  1540. {
  1541. //if (DateTime.Now - CalNow > during)
  1542. //{
  1543. // _logger.LogInformation($"{heartRate.Serialno} 超过1分钟,迭代完成跳出循环 ");
  1544. // break;
  1545. //}
  1546. await Task.Delay(TimeSpan.FromSeconds(1));
  1547. var segmentStatStartTime = boundaryStatStartTime.AddMinutes(c * INTERVAL_FHR);
  1548. var segmentStatEndTime = segmentStatStartTime.AddMinutes(INTERVAL_FHR);
  1549. c++;
  1550. var statStartTime = segmentStatStartTime;
  1551. var statEndTime = segmentStatEndTime;
  1552. _logger.LogInformation($"{heartRate.Serialno} 当前统计周期{statStartTime.ToString("yyyy-MM-dd HH:mm:ss")}-{statEndTime.ToString("yyyy-MM-dd HH:mm:ss")}");
  1553. if (statEndTime > boundaryStatEndTime)
  1554. {
  1555. _logger.LogInformation($"{heartRate.Serialno} 常规胎心统计超过时间边界,迭代完成跳出循环 ");
  1556. break;
  1557. }
  1558. var segmentPhr = normalPhr
  1559. .Where(i => i.LastUpdate <= statEndTime && i.LastUpdate >= statStartTime)
  1560. .ToList();
  1561. if (segmentPhr.Count == 0)
  1562. {
  1563. // 跳出当次迭代,进入下次迭代
  1564. _logger.LogWarning($"{heartRate.Serialno} 统计周期:{statStartTime.ToString("yyyy-MM-dd HH:mm:ss")}-{statEndTime.ToString("yyyy-MM-dd HH:mm:ss")} 孕妇心率数据不足,{segmentPhr.Count}条记录,不处理");
  1565. continue;
  1566. }
  1567. _logger.LogInformation($"{heartRate.Serialno} 当前统计周期{statStartTime.ToString("yyyy-MM-dd HH:mm:ss")}-{statEndTime.ToString("yyyy-MM-dd HH:mm:ss")},对应的常规心率ID{string.Join(",", segmentPhr.Select(i=>i.MessageId))}");
  1568. var sampleTime = DateTimeUtil.ConvertToTimeStamp(segmentStatStartTime).ToString();
  1569. sampleTime = sampleTime.Length > 10 ? sampleTime.Substring(0, 10) : sampleTime;
  1570. //检测 是否存在,不存在则处理
  1571. GeneralParam param = new()
  1572. {
  1573. Filters = new List<QueryFilterCondition>
  1574. {
  1575. new ()
  1576. {
  1577. Key=nameof(HisGpsFetalHeartRate.Serialno),
  1578. Value=heartRate.Serialno,
  1579. ValueType=QueryValueTypeEnum.String,
  1580. Operator=QueryOperatorEnum.Equal
  1581. },
  1582. new ()
  1583. {
  1584. Key=nameof(HisGpsFetalHeartRate.SampleTime),
  1585. Value=sampleTime,
  1586. ValueType=QueryValueTypeEnum.String,
  1587. Operator=QueryOperatorEnum.Equal
  1588. },
  1589. },
  1590. OrderBys = new List<OrderByCondition>
  1591. {
  1592. new (){
  1593. IsDesc=true,
  1594. Key=nameof(HisGpsFetalHeartRate.SampleTime)
  1595. }
  1596. }
  1597. };
  1598. var fhr = await _hisFetalHeartApiClient.GetFirstAsync(param, heartRate.Serialno[^2..], null, new RequestHeader { RequestId = Guid.NewGuid().ToString("D") });
  1599. if (fhr == null)
  1600. {
  1601. var avgPhr = segmentPhr.Count == 1
  1602. ? segmentPhr.First().PregnancyHeartRate
  1603. : segmentPhr.Average(i => i.PregnancyHeartRate);
  1604. heartRate.HeartRate = (int)avgPhr;
  1605. #region 胎心系数使用基于心率与众数对比
  1606. var coefficient = 0f;
  1607. /**
  1608. // 孕妇心率少于众数,取StatMinValueFprCoefficient
  1609. if (heartRate.HeartRate < commonPHR!.Mode)
  1610. {
  1611. coefficient = commonPHR.StatMinValueFprCoefficient!;
  1612. _logger.LogInformation($"{heartRate.Serialno} 孕妇心率少于众数,使用最小值系数 {coefficient}");
  1613. }
  1614. // 孕妇心率大于众数,取StatMaxValueFprCoefficient与StatModeAvgFprCoefficient中少的那个
  1615. else if (heartRate.HeartRate > commonPHR.Mode)
  1616. {
  1617. if (commonPHR.StatModeAvgFprCoefficient > commonPHR.StatMaxValueFprCoefficient)
  1618. {
  1619. coefficient = commonPHR.StatMaxValueFprCoefficient!;
  1620. _logger.LogInformation($"{heartRate.Serialno} 孕妇心率大于众数,使用最大值系数 {coefficient}");
  1621. }
  1622. else
  1623. {
  1624. coefficient = commonPHR.StatModeAvgFprCoefficient!;
  1625. _logger.LogInformation($"{heartRate.Serialno} 孕妇心率大于众数,使用均值系数 {coefficient}");
  1626. }
  1627. }
  1628. else
  1629. {
  1630. coefficient = commonPHR.StatModeAvgFprCoefficient;
  1631. _logger.LogInformation($"{heartRate.Serialno} 孕妇心率等于众数,使用均值系数 {coefficient}");
  1632. }
  1633. */
  1634. ///1、高频最小值>心率,取高频最小值系数
  1635. ///2、高频最小值 < 心率 < 众数,取众数系数与高频最小值系数较小值
  1636. ///3、众数 < 心率 < 高频最大值,取众数系数与高频最大值系数较小值
  1637. ///4、心率 > 高频最大值,取高频最大值系数
  1638. // 1、高频最小值>心率,取高频最小值系数
  1639. if (commonPHR.MinValue > heartRate.HeartRate)
  1640. {
  1641. coefficient = commonPHR.StatMinValueFprCoefficient!;
  1642. _logger.LogInformation($"{heartRate.Serialno} 高频最小值>心率,取高频最小值系数 {coefficient}");
  1643. }
  1644. // 2、高频最小值 < 心率 < 众数,取众数系数与高频最小值系数较小值
  1645. if (commonPHR.MinValue <heartRate.HeartRate && heartRate.HeartRate<commonPHR.Mode)
  1646. {
  1647. if (commonPHR.StatModeAvgFprCoefficient > commonPHR.StatMinValueFprCoefficient)
  1648. {
  1649. coefficient = commonPHR.StatMinValueFprCoefficient!;
  1650. _logger.LogInformation($"{heartRate.Serialno} 高频最小值 < 心率 < 众数,平均值系数大于最小值系数,使用最小值系数 {coefficient}");
  1651. }
  1652. else
  1653. {
  1654. coefficient = commonPHR.StatModeAvgFprCoefficient!;
  1655. _logger.LogInformation($"{heartRate.Serialno} 高频最小值 < 心率 < 众数,平均值系数小于最小值系数,使用均值系数 {coefficient}");
  1656. }
  1657. }
  1658. // 3、众数 < 心率 < 高频最大值,取众数系数与高频最大值系数较小值
  1659. if (commonPHR.Mode < heartRate.HeartRate && heartRate.HeartRate < commonPHR.MaxValue)
  1660. {
  1661. if (commonPHR.StatModeAvgFprCoefficient > commonPHR.StatMaxValueFprCoefficient)
  1662. {
  1663. coefficient = commonPHR.StatMaxValueFprCoefficient!;
  1664. _logger.LogInformation($"{heartRate.Serialno} 众数 < 心率 < 高频最大值,平均值系数大于最最大值系数,使用最大值系数 {coefficient}");
  1665. }
  1666. else
  1667. {
  1668. coefficient = commonPHR.StatModeAvgFprCoefficient!;
  1669. _logger.LogInformation($"{heartRate.Serialno} 众数 < 心率 < 高频最大值,平均值系数小于最小值系数,使用均值系数 {coefficient}");
  1670. }
  1671. }
  1672. // 4、心率 > 高频最大值,取高频最大值系数
  1673. if (heartRate.HeartRate > commonPHR.MaxValue)
  1674. {
  1675. coefficient = commonPHR.StatMaxValueFprCoefficient!;
  1676. _logger.LogInformation($"{heartRate.Serialno} 心率 > 高频最大值,取高频最大值系数 {coefficient}");
  1677. }
  1678. if (heartRate.HeartRate == commonPHR.Mode)
  1679. {
  1680. coefficient = commonPHR.StatModeAvgFprCoefficient!;
  1681. _logger.LogInformation($"{heartRate.Serialno} 心率 == 众数,取均值系数 {coefficient}");
  1682. }
  1683. #endregion
  1684. #region 胎心阈值判断
  1685. //var fetalHeartRate = SafeType.SafeInt(heartRate.HeartRate * coefficient);
  1686. //// 胎心的最大值调整为220,超过都按该值220输出
  1687. //// fetalHeartRate = fetalHeartRate>= 220 ? 220 : fetalHeartRate;
  1688. //if (fetalHeartRate > 220)
  1689. //{
  1690. // fetalHeartRate = 220;
  1691. // _logger.LogWarning($"{heartRate.Serialno} 大于220,按220输出,计算因子:孕妇心率 {heartRate.HeartRate},系数 {coefficient},周期 周期:{statStartTime.ToString("yyyy-MM-dd HH:mm:ss")}----{statEndTime.ToString("yyyy-MM-dd HH:mm:ss")}");
  1692. //}
  1693. //// 胎心的最小值调整为90,超过都按该值90
  1694. //if (fetalHeartRate < 90)
  1695. //{
  1696. // fetalHeartRate = 90;
  1697. // _logger.LogWarning($"{heartRate.Serialno} 小于90,按90输出,计算因子:孕妇心率 {heartRate.HeartRate},系数 {coefficient},周期 周期:{statStartTime.ToString("yyyy-MM-dd HH:mm:ss")}----{statEndTime.ToString("yyyy-MM-dd HH:mm:ss")}");
  1698. //}
  1699. // 计算胎心率并进行边界检查
  1700. var fetalHeartRate = Math.Clamp((int)(heartRate.HeartRate * coefficient), 90, 220);
  1701. if (fetalHeartRate != (int)(heartRate.HeartRate * coefficient))
  1702. {
  1703. _logger.LogWarning($"{heartRate.Serialno} 胎心率超出范围, 按修正值 {fetalHeartRate} 输出, 计算因子:孕妇心率 {heartRate.HeartRate}, 系数 {coefficient}, 周期 {statStartTime} - {statEndTime}");
  1704. }
  1705. #endregion
  1706. _logger.LogInformation($"{heartRate.Serialno} 在 常规 状态,生成胎心值:{fetalHeartRate},统计周期:{statStartTime.ToString("yyyy-MM-dd HH:mm:ss")}----{statEndTime.ToString("yyyy-MM-dd HH:mm:ss")}");
  1707. var isAbnormal = 0;
  1708. #region 保存到Mysql数据库
  1709. // 保存到 数据服务 MySQL 数据库
  1710. HisGpsFetalHeartRate gpsFetalHeartRate = new()
  1711. {
  1712. FetalHeartRateId = Guid.NewGuid().ToString("D"),
  1713. PersonId = commonPHR!.PersonId,
  1714. Serialno = heartRate.Serialno,
  1715. HeartRate = fetalHeartRate,
  1716. SampleTime = sampleTime,
  1717. IsAbnormal = isAbnormal,
  1718. StatStartTime = statStartTime,
  1719. StatEndTime = statEndTime,//commonPHR.StatEndTime,
  1720. CreateTime = DateTime.Now,
  1721. Method = 1,
  1722. IsDisplay = 1,
  1723. DeviceKey = commonPHR!.DeviceKey
  1724. };
  1725. await _hisFetalHeartApiClient.AddAsync(gpsFetalHeartRate).ConfigureAwait(false);
  1726. #endregion
  1727. #region 推送最后一条常规心率计算的胎心数据到iot设备
  1728. var lastPhr = await _serviceTDengine.GetLastAsync<PregnancyHeartRateModel>(heartRate.Serialno);
  1729. if (segmentStatEndTime == boundaryStatEndTime)
  1730. {
  1731. await _serviceIotApi.SetFetalHeartRateConfig(heartRate.Serialno, fetalHeartRate, sampleTime, isAbnormal);
  1732. _logger.LogInformation($"{heartRate.Serialno} 推送最后一条常规心率计算的胎心数据到iot设备");
  1733. }
  1734. #endregion
  1735. #region 推送到第三方
  1736. var device = await _deviceCacheMgr.GetDeviceBySerialNoAsync(heartRate.Serialno).ConfigureAwait(false);
  1737. var fhrMsgId = $"{heartRate.Serialno}-{sampleTime}-{Guid.NewGuid().ToString("D")[^3..]}";
  1738. var fhrMsgTime = DateTimeUtil.GetDateTimeFromUnixTimeMilliseconds(long.Parse(sampleTime.Length < 13 ? sampleTime.PadRight(13, '0') : sampleTime)).ToString("yyyy-MM-dd HH:mm:ss");
  1739. var topic = "topic.push.third";
  1740. var fhrThridMsg = new
  1741. {
  1742. messageId = fhrMsgId,
  1743. topic = topic,
  1744. time = fhrMsgTime,
  1745. data = new
  1746. {
  1747. imei = heartRate.Serialno,
  1748. value = fetalHeartRate,
  1749. isAbnormal,
  1750. type = "fetalHeart"
  1751. }
  1752. };
  1753. await _serviceMqProcess.ProcessIMEIEventMessageAsync(fhrMsgId, topic, 31, fhrThridMsg).ConfigureAwait(false);
  1754. #endregion
  1755. #region 推送到微信
  1756. if (isAbnormal != 0)
  1757. {
  1758. topic = "topic.push.wx";
  1759. var fhrMsg = new
  1760. {
  1761. messageId = fhrMsgId,
  1762. topic = topic,
  1763. time = fhrMsgTime,
  1764. data = new
  1765. {
  1766. deviceId = device?.DeviceId,
  1767. imei = heartRate.Serialno,
  1768. alarmTypeId = 12,
  1769. alarmDeviceName = heartRate.Serialno,
  1770. alarmRemarks = JsonConvert.SerializeObject(new { fetalHeartValue = fetalHeartRate, isAbnormal = isAbnormal }),
  1771. address = string.Empty,
  1772. deviceKey = device?.DeviceId
  1773. }
  1774. };
  1775. await _serviceMqProcess.ProcessIMEIEventMessageAsync(fhrMsgId, topic, fhrMsg).ConfigureAwait(false);
  1776. }
  1777. #endregion
  1778. }
  1779. else
  1780. {
  1781. _logger.LogInformation($"{heartRate.Serialno},统计常规胎心周期:{statStartTime.ToString("yyyy-MM-dd HH:mm:ss")}----{statEndTime.ToString("yyyy-MM-dd HH:mm:ss")} ,常规胎心已处理");
  1782. }
  1783. //await Task.Delay(TimeSpan.FromSeconds(1));
  1784. // 跳出循环
  1785. if (statEndTime.ToString("yyyyMMddHHmm") == boundaryStatEndTime.ToString("yyyyMMddHHmm"))
  1786. {
  1787. _logger.LogInformation($"{heartRate.Serialno} 迭代完成跳出循环 ");
  1788. break;
  1789. }
  1790. //if (statEndTime>= boundaryStatEndTime)
  1791. //{
  1792. // _logger.LogInformation($"{heartRate.Serialno} 时间边界,迭代完成跳出循环 ");
  1793. // break;
  1794. //}
  1795. }
  1796. }
  1797. catch (Exception ex)
  1798. {
  1799. _logger.LogError($"处理常规胎心数据时发生错误: {ex.Message}");
  1800. }
  1801. //for (var segmentStatStartTime = boundaryStatStartTime;
  1802. // segmentStatStartTime < boundaryStatEndTime;
  1803. // segmentStatStartTime = segmentStatStartTime.AddMinutes(INTERVAL_FHR))
  1804. //{
  1805. // var segmentStatEndTime = segmentStatStartTime.AddMinutes(INTERVAL_FHR);
  1806. // var segmentPhr = daysPhr
  1807. // .Where(i => i.LastUpdate <= segmentStatEndTime && i.LastUpdate >= segmentStatStartTime)
  1808. // .ToList();
  1809. // if (segmentStatEndTime == boundaryStatEndTime)
  1810. // {
  1811. // break;
  1812. // }
  1813. //}
  1814. }
  1815. /// <summary>
  1816. /// 周期性常规心率计算胎心值
  1817. /// </summary>
  1818. /// <param name="heartRate"></param>
  1819. /// <param name="commonPHR"></param>
  1820. /// <param name="highFreqSampleInterval"></param>
  1821. /// <returns></returns>
  1822. public async Task CalculateNormalFetalHeartRateIntervalAsync(HisGpsHeartRate heartRate, PregnancyCommonHeartRateModel commonPHR, int highFreqSampleInterval)
  1823. {
  1824. var daysPhr = await _serviceTDengine.GetBySerialNoAsync<PregnancyHeartRateModel>(heartRate.Serialno, 7);
  1825. var now = DateTime.Now;
  1826. var filteredPhr = daysPhr.Where(i => i.LastUpdate >= heartRate.LastUpdate && i.LastUpdate <= now).ToList();
  1827. // 去除高频
  1828. var normalPhr = GetNonFreqPregnancyHeartRate(filteredPhr, highFreqSampleInterval).OrderBy(i => i.LastUpdate);
  1829. if (normalPhr.ToList().Count == 0)
  1830. {
  1831. _logger.LogWarning($"{heartRate.Serialno} 时间段{heartRate.LastUpdate}-{now},去除高频心率数据后,没有常规数据,不计算常规胎心数据");
  1832. return;
  1833. }
  1834. var startPhr = normalPhr.First();
  1835. var endPhr = normalPhr.Last();
  1836. //var sampleTime = GetSampleTimeFromLastUpdate((DateTime)heartRate.LastUpdate!, INTERVAL_FHR);
  1837. // 数据统计边界
  1838. var boundaryStatStartTime = GetSampleTimeFromLastUpdate((DateTime)startPhr.LastUpdate!, INTERVAL_FHR);
  1839. var boundaryStatEndTime = GetSampleTimeFromLastUpdate((DateTime)endPhr.LastUpdate!, INTERVAL_FHR).AddMinutes(INTERVAL_FHR);
  1840. _logger.LogInformation($"{heartRate.Serialno} 常规胎心统计边界{boundaryStatStartTime}-{boundaryStatEndTime}");
  1841. try
  1842. {
  1843. //var CalNow = DateTime.Now;
  1844. //var during = TimeSpan.FromSeconds(300); //5分钟
  1845. var c = 0;
  1846. while (true)
  1847. {
  1848. //if (DateTime.Now - CalNow > during)
  1849. //{
  1850. // _logger.LogInformation($"{heartRate.Serialno} 超过1分钟,迭代完成跳出循环 ");
  1851. // break;
  1852. //}
  1853. await Task.Delay(TimeSpan.FromSeconds(1));
  1854. var segmentStatStartTime = boundaryStatStartTime.AddMinutes(c * INTERVAL_FHR);
  1855. var segmentStatEndTime = segmentStatStartTime.AddMinutes(INTERVAL_FHR);
  1856. c++;
  1857. var statStartTime = segmentStatStartTime;
  1858. var statEndTime = segmentStatEndTime;
  1859. _logger.LogInformation($"{heartRate.Serialno} 当前统计周期{statStartTime.ToString("yyyy-MM-dd HH:mm:ss")}-{statEndTime.ToString("yyyy-MM-dd HH:mm:ss")}");
  1860. if (statEndTime > boundaryStatEndTime)
  1861. {
  1862. _logger.LogInformation($"{heartRate.Serialno} 常规胎心统计超过时间边界,迭代完成跳出循环 ");
  1863. break;
  1864. }
  1865. var segmentPhr = normalPhr
  1866. .Where(i => i.LastUpdate <= statEndTime && i.LastUpdate >= statStartTime)
  1867. .ToList();
  1868. if (segmentPhr.Count == 0)
  1869. {
  1870. // 跳出当次迭代,进入下次迭代
  1871. _logger.LogWarning($"{heartRate.Serialno} 统计周期:{statStartTime.ToString("yyyy-MM-dd HH:mm:ss")}-{statEndTime.ToString("yyyy-MM-dd HH:mm:ss")} 孕妇心率数据不足,{segmentPhr.Count}条记录,不处理");
  1872. continue;
  1873. }
  1874. _logger.LogInformation($"{heartRate.Serialno} 当前统计周期{statStartTime.ToString("yyyy-MM-dd HH:mm:ss")}-{statEndTime.ToString("yyyy-MM-dd HH:mm:ss")},对应的常规心率ID{string.Join(",", segmentPhr.Select(i => i.MessageId))}");
  1875. var sampleTime = DateTimeUtil.ConvertToTimeStamp(segmentStatStartTime).ToString();
  1876. sampleTime = sampleTime.Length > 10 ? sampleTime.Substring(0, 10) : sampleTime;
  1877. //检测 是否存在,不存在则处理
  1878. GeneralParam param = new()
  1879. {
  1880. Filters = new List<QueryFilterCondition>
  1881. {
  1882. new ()
  1883. {
  1884. Key=nameof(HisGpsFetalHeartRate.Serialno),
  1885. Value=heartRate.Serialno,
  1886. ValueType=QueryValueTypeEnum.String,
  1887. Operator=QueryOperatorEnum.Equal
  1888. },
  1889. new ()
  1890. {
  1891. Key=nameof(HisGpsFetalHeartRate.SampleTime),
  1892. Value=sampleTime,
  1893. ValueType=QueryValueTypeEnum.String,
  1894. Operator=QueryOperatorEnum.Equal
  1895. },
  1896. },
  1897. OrderBys = new List<OrderByCondition>
  1898. {
  1899. new (){
  1900. IsDesc=true,
  1901. Key=nameof(HisGpsFetalHeartRate.SampleTime)
  1902. }
  1903. }
  1904. };
  1905. var fhr = await _hisFetalHeartApiClient.GetFirstAsync(param, heartRate.Serialno[^2..], null, new RequestHeader { RequestId = Guid.NewGuid().ToString("D") });
  1906. if (fhr == null)
  1907. {
  1908. var avgPhr = segmentPhr.Count == 1
  1909. ? segmentPhr.First().PregnancyHeartRate
  1910. : segmentPhr.Average(i => i.PregnancyHeartRate);
  1911. heartRate.HeartRate = (int)avgPhr;
  1912. #region 胎心阈值判断
  1913. var fetalHeartRateFromAlgorithm = await _serviceTDengine.GetFetalHeartRateAsync(heartRate.Serialno, (int)heartRate.HeartRate);
  1914. // 计算胎心率并进行边界检查
  1915. var fetalHeartRate = Math.Clamp(fetalHeartRateFromAlgorithm, 90, 220);
  1916. if (fetalHeartRate != fetalHeartRateFromAlgorithm)
  1917. {
  1918. _logger.LogWarning($"{heartRate.Serialno} 胎心率超出范围, 按修正值 {fetalHeartRate} 输出, 孕妇心率 {heartRate.HeartRate}, 周期 {statStartTime} - {statEndTime}");
  1919. }
  1920. #endregion
  1921. _logger.LogInformation($"{heartRate.Serialno} 在 常规 状态,生成胎心值:{fetalHeartRate},统计周期:{statStartTime.ToString("yyyy-MM-dd HH:mm:ss")}----{statEndTime.ToString("yyyy-MM-dd HH:mm:ss")}");
  1922. var isAbnormal = 0;
  1923. #region 保存到Mysql数据库
  1924. // 保存到 数据服务 MySQL 数据库
  1925. HisGpsFetalHeartRate gpsFetalHeartRate = new()
  1926. {
  1927. FetalHeartRateId = Guid.NewGuid().ToString("D"),
  1928. PersonId = commonPHR!.PersonId,
  1929. Serialno = heartRate.Serialno,
  1930. HeartRate = fetalHeartRate,
  1931. SampleTime = sampleTime,
  1932. IsAbnormal = isAbnormal,
  1933. StatStartTime = statStartTime,
  1934. StatEndTime = statEndTime,//commonPHR.StatEndTime,
  1935. CreateTime = DateTime.Now,
  1936. Method = 1,
  1937. IsDisplay = 1,
  1938. DeviceKey = commonPHR!.DeviceKey
  1939. };
  1940. await _hisFetalHeartApiClient.AddAsync(gpsFetalHeartRate).ConfigureAwait(false);
  1941. #endregion
  1942. #region 推送最后一条常规心率计算的胎心数据到iot设备
  1943. var lastPhr = await _serviceTDengine.GetLastAsync<PregnancyHeartRateModel>(heartRate.Serialno);
  1944. if (segmentStatEndTime == boundaryStatEndTime)
  1945. {
  1946. await _serviceIotApi.SetFetalHeartRateConfig(heartRate.Serialno, fetalHeartRate, sampleTime, isAbnormal);
  1947. _logger.LogInformation($"{heartRate.Serialno} 推送最后一条常规心率计算的胎心数据到iot设备");
  1948. }
  1949. #endregion
  1950. #region 推送到第三方
  1951. var device = await _deviceCacheMgr.GetDeviceBySerialNoAsync(heartRate.Serialno).ConfigureAwait(false);
  1952. var fhrMsgId = $"{heartRate.Serialno}-{sampleTime}-{Guid.NewGuid().ToString("D")[^3..]}";
  1953. var fhrMsgTime = DateTimeUtil.GetDateTimeFromUnixTimeMilliseconds(long.Parse(sampleTime.Length < 13 ? sampleTime.PadRight(13, '0') : sampleTime)).ToString("yyyy-MM-dd HH:mm:ss");
  1954. var topic = "topic.push.third";
  1955. var fhrThridMsg = new
  1956. {
  1957. messageId = fhrMsgId,
  1958. topic = topic,
  1959. time = fhrMsgTime,
  1960. data = new
  1961. {
  1962. imei = heartRate.Serialno,
  1963. value = fetalHeartRate,
  1964. isAbnormal,
  1965. type = "fetalHeart"
  1966. }
  1967. };
  1968. await _serviceMqProcess.ProcessIMEIEventMessageAsync(fhrMsgId, topic, 31, fhrThridMsg).ConfigureAwait(false);
  1969. #endregion
  1970. #region 推送到微信
  1971. if (isAbnormal != 0)
  1972. {
  1973. topic = "topic.push.wx";
  1974. var fhrMsg = new
  1975. {
  1976. messageId = fhrMsgId,
  1977. topic = topic,
  1978. time = fhrMsgTime,
  1979. data = new
  1980. {
  1981. deviceId = device?.DeviceId,
  1982. imei = heartRate.Serialno,
  1983. alarmTypeId = 12,
  1984. alarmDeviceName = heartRate.Serialno,
  1985. alarmRemarks = JsonConvert.SerializeObject(new { fetalHeartValue = fetalHeartRate, isAbnormal = isAbnormal }),
  1986. address = string.Empty,
  1987. deviceKey = device?.DeviceId
  1988. }
  1989. };
  1990. await _serviceMqProcess.ProcessIMEIEventMessageAsync(fhrMsgId, topic, fhrMsg).ConfigureAwait(false);
  1991. }
  1992. #endregion
  1993. }
  1994. else
  1995. {
  1996. _logger.LogInformation($"{heartRate.Serialno},统计常规胎心周期:{statStartTime.ToString("yyyy-MM-dd HH:mm:ss")}----{statEndTime.ToString("yyyy-MM-dd HH:mm:ss")} ,常规胎心已处理");
  1997. }
  1998. //await Task.Delay(TimeSpan.FromSeconds(1));
  1999. // 跳出循环
  2000. if (statEndTime.ToString("yyyyMMddHHmm") == boundaryStatEndTime.ToString("yyyyMMddHHmm"))
  2001. {
  2002. _logger.LogInformation($"{heartRate.Serialno} 迭代完成跳出循环 ");
  2003. break;
  2004. }
  2005. //if (statEndTime>= boundaryStatEndTime)
  2006. //{
  2007. // _logger.LogInformation($"{heartRate.Serialno} 时间边界,迭代完成跳出循环 ");
  2008. // break;
  2009. //}
  2010. }
  2011. }
  2012. catch (Exception ex)
  2013. {
  2014. _logger.LogError($"处理常规胎心数据时发生错误: {ex.Message}");
  2015. }
  2016. //for (var segmentStatStartTime = boundaryStatStartTime;
  2017. // segmentStatStartTime < boundaryStatEndTime;
  2018. // segmentStatStartTime = segmentStatStartTime.AddMinutes(INTERVAL_FHR))
  2019. //{
  2020. // var segmentStatEndTime = segmentStatStartTime.AddMinutes(INTERVAL_FHR);
  2021. // var segmentPhr = daysPhr
  2022. // .Where(i => i.LastUpdate <= segmentStatEndTime && i.LastUpdate >= segmentStatStartTime)
  2023. // .ToList();
  2024. // if (segmentStatEndTime == boundaryStatEndTime)
  2025. // {
  2026. // break;
  2027. // }
  2028. //}
  2029. }
  2030. public async Task CalculateFetalMovementIntervalAsync(HisGpsHeartRate heartRate, PregnancyCommonHeartRateModel commonPHR,DateTime edoc)
  2031. {
  2032. var daysPhr = await _serviceTDengine.GetBySerialNoAsync<PregnancyHeartRateModel>(heartRate.Serialno, 7);
  2033. DateTime fmNow = DateTime.Now;
  2034. int fmNowHour = fmNow.Hour;
  2035. DateTime startHour;
  2036. DateTime endHour;
  2037. if (fmNow.Subtract((DateTime)heartRate.LastUpdate!).Hours>2)
  2038. {
  2039. _logger.LogWarning($"{heartRate.Serialno} 当前计算时间与lastUpdate 时差超过2小时,将从当前触发心率到当天的所有心率计算对应的胎动");
  2040. startHour = ((DateTime)heartRate.LastUpdate!).Date.AddHours(((DateTime)heartRate.LastUpdate!).Hour);
  2041. endHour = ((DateTime)heartRate.LastUpdate!).Date.AddDays(1);
  2042. }
  2043. else
  2044. {
  2045. // 跨天
  2046. if (fmNowHour == 1)
  2047. {
  2048. // last_update 23~0
  2049. startHour = fmNow.Date.AddDays(-1).AddHours(((DateTime)heartRate.LastUpdate!).Hour);
  2050. endHour = fmNow.Date;
  2051. }
  2052. else
  2053. {
  2054. // last_update 0~1->2,1~2->3,2~3->4...21~22->23
  2055. startHour = fmNow.Date.AddHours(((DateTime)heartRate.LastUpdate!).Hour);
  2056. endHour = fmNow.Date.AddHours(fmNowHour - 1);
  2057. }
  2058. }
  2059. try
  2060. {
  2061. var filterPhr = daysPhr
  2062. .Where(i => i.LastUpdate >= startHour && i.LastUpdate <= endHour)
  2063. .OrderBy(i => i.LastUpdate).ToList();
  2064. //if (filterPhr.Count < 2)
  2065. //{
  2066. // _logger.LogWarning($"{heartRate.Serialno} 胎动统计数据少于2条,不统计,{string.Join('-', filterPhr.Select(i=>i.LastUpdate))}");
  2067. // return;
  2068. //}
  2069. if (filterPhr.Count < 1)
  2070. {
  2071. _logger.LogWarning($"{heartRate.Serialno} 胎动统计数据少于1条,不统计,{string.Join('-', filterPhr.Select(i => i.LastUpdate))}");
  2072. return;
  2073. }
  2074. var startPhr = filterPhr.First();
  2075. var endPhr = filterPhr.Last();
  2076. // 数据统计边界
  2077. var boundaryStatStartTime = startPhr.LastUpdate.Date.AddHours(startPhr.LastUpdate.Hour);
  2078. var boundaryStatEndTime = endPhr.LastUpdate.Date.AddHours(endPhr.LastUpdate.Hour + 1);
  2079. _logger.LogInformation($"{heartRate.Serialno} 胎动统计边界{boundaryStatStartTime}-{boundaryStatEndTime}");
  2080. var c = 0;
  2081. while (true)
  2082. {
  2083. await Task.Delay(TimeSpan.FromSeconds(1));
  2084. var segmentStatStartTime = boundaryStatStartTime.AddHours(c * 1);
  2085. var segmentStatEndTime = segmentStatStartTime.AddHours(1);
  2086. c++;
  2087. var statStartTime = segmentStatStartTime;
  2088. var statEndTime = segmentStatEndTime;
  2089. _logger.LogInformation($"{heartRate.Serialno} 当前胎动统计周期{statStartTime.ToString("yyyy-MM-dd HH:mm:ss")}-{statEndTime.ToString("yyyy-MM-dd HH:mm:ss")}");
  2090. if (statEndTime > boundaryStatEndTime)
  2091. {
  2092. _logger.LogInformation($"{heartRate.Serialno} 常规胎心统计超过时间边界,迭代完成跳出循环 ");
  2093. break;
  2094. }
  2095. var segmentPhr = filterPhr
  2096. .Where(i => i.LastUpdate <= statEndTime && i.LastUpdate >= statStartTime)
  2097. .ToList();
  2098. if (segmentPhr.Count == 0)
  2099. {
  2100. // 跳出当次迭代,进入下次迭代
  2101. _logger.LogWarning($"{heartRate.Serialno} 胎动统计周期:{statStartTime.ToString("yyyy-MM-dd HH:mm:ss")}-{statEndTime.ToString("yyyy-MM-dd HH:mm:ss")} 孕妇心率数据不足,{segmentPhr.Count}条记录,不处理");
  2102. continue;
  2103. }
  2104. _logger.LogInformation($"{heartRate.Serialno} 当前胎动统计周期{statStartTime.ToString("yyyy-MM-dd HH:mm:ss")}-{statEndTime.ToString("yyyy-MM-dd HH:mm:ss")},对应的常规心率ID{string.Join(",", segmentPhr.Select(i => i.MessageId))}");
  2105. // 0~1 点的lastUpdate数据sample_time是1点,计算是2点
  2106. var fetalMovementSampleTime = DateTimeUtil.ConvertToTimeStamp(statEndTime).ToString()[..10];
  2107. var isFetalMovementExisted = await _deviceCacheMgr.FetalMovementIsExistedAsync(heartRate.Serialno, fetalMovementSampleTime);
  2108. if (!isFetalMovementExisted)
  2109. {
  2110. /// 开始计算
  2111. var phrRange = segmentPhr.OrderByDescending(i => i.LastUpdate)
  2112. .Select(i => i.LastUpdate)
  2113. .ToList();
  2114. if (phrRange.Count >= 1)
  2115. {
  2116. // 读取胎心数据
  2117. GeneralParam param = new()
  2118. {
  2119. Filters = new List<QueryFilterCondition>
  2120. {
  2121. new ()
  2122. {
  2123. Key=nameof(HisGpsFetalHeartRate.Serialno),
  2124. Value=heartRate.Serialno,
  2125. ValueType=QueryValueTypeEnum.String,
  2126. Operator=QueryOperatorEnum.Equal
  2127. },
  2128. //new ()
  2129. //{
  2130. // Key=nameof(HisGpsFetalHeartRate.SampleTime),
  2131. // Value=sampleTime,
  2132. // ValueType=QueryValueTypeEnum.String,
  2133. // Operator=QueryOperatorEnum.GreaterEqual
  2134. //},
  2135. },
  2136. OrderBys = new List<OrderByCondition>
  2137. {
  2138. new (){
  2139. IsDesc=true,
  2140. Key=nameof(HisGpsFetalHeartRate.SampleTime)
  2141. }
  2142. }
  2143. };
  2144. var fetalHeartRateIsAbnormal = 0;
  2145. var fhr = await _hisFetalHeartApiClient.GetFirstAsync(param, heartRate.Serialno[^2..], null, new RequestHeader { RequestId = Guid.NewGuid().ToString("D") });
  2146. // 胎心数据时间与胎动时间一致
  2147. var time = long.Parse(fhr.SampleTime.Length < 13 ? fhr.SampleTime.PadRight(13, '0') : fhr.SampleTime);
  2148. var fhrSampleTime = DateTimeUtil.GetDateTimeFromUnixTimeMilliseconds(time);
  2149. // 胎心数据时间与胎动时间一致,最后一条胎心数据与胎动数据的小时差不大于2
  2150. //if ((DateTime.Now.Hour - fhrSampleTime.Hour) <= 2)
  2151. if(true)
  2152. {
  2153. var duringMins = Math.Abs((phrRange.First() - phrRange.Last()).TotalMinutes);
  2154. //在餐后时间段(8:00~10:00,12:00~14:00,18:00~20:00,22:00~24:00)取中间值。其他时间段取正常起始值
  2155. bool isInTimeRanges = IsLastUpdateInTimeRanges(phrRange.First());
  2156. int pregnancyWeeks = (DateTime.Now - edoc.AddDays(-280)).Days / 7;
  2157. if (pregnancyWeeks >= 12 && pregnancyWeeks <= 50)
  2158. {
  2159. var fetalMovementMap = _mgrFetalMovementNormalValueRangeCache.GetFetalMovements();
  2160. var fetalMovementMapValue = isInTimeRanges ? fetalMovementMap
  2161. .Where(i =>
  2162. i.PregnancyPeriod![0] <= pregnancyWeeks &&
  2163. i.PregnancyPeriod[1] >= pregnancyWeeks)
  2164. .Select(i => i.MedianMovement)
  2165. .FirstOrDefault()
  2166. :
  2167. fetalMovementMap
  2168. .Where(i =>
  2169. i.PregnancyPeriod![0] <= pregnancyWeeks &&
  2170. i.PregnancyPeriod[1] >= pregnancyWeeks)
  2171. .Select(i => i.InitialMovement)
  2172. .FirstOrDefault()
  2173. ;
  2174. // var fetalMovementTimeVar = (fetalMovementMapValue * duringMins * 2) / 120;
  2175. #region 取消使用时间长度比例
  2176. //var fetalMovementTimeVar = (fetalMovementMapValue * duringMins) / 60;
  2177. //if (duringMins < 59)
  2178. //{
  2179. // // 取12小时胎动总数最小值
  2180. // fetalMovementMapValue = fetalMovementMap
  2181. // .Where(i => i.PregnancyPeriod![0] <= pregnancyWeeks && i.PregnancyPeriod[1] >= pregnancyWeeks)
  2182. // .First()
  2183. // .TwelveHourMovementRange[0];
  2184. // //取12小时胎动总数最小值的平均值
  2185. // fetalMovementTimeVar = fetalMovementMapValue / 12;
  2186. // _logger.LogWarning($"{heartRate.Serialno} 佩戴不足1小时,12小时胎动总数最小值{fetalMovementMapValue},平均值{fetalMovementTimeVar}");
  2187. //}
  2188. #endregion
  2189. var fetalMovementTimeVar = (double)fetalMovementMapValue;
  2190. #region 生理健康与胎动的关系
  2191. /// (步数)运动步数超过1500步则加1;
  2192. /// (体温)低烧则胎动减1,高烧胎动减2;低烧是37.3~38.5,38.6以上是高烧。
  2193. /// (血压)血压收缩压大于160则加1。
  2194. /// (心理)心理压力高加2,压力中加1;
  2195. var fetalMovementStepVar = 0;
  2196. var fetalMovementTempVar = 0;
  2197. var fetalMovementBpVar = 0;
  2198. var fetalMovementPpVar = 0;
  2199. var fetalMovementFhrVar = 0;
  2200. // 步数
  2201. if (false)
  2202. {
  2203. var step = await _personCacheMgr.GetStepPeriodicityAsync(heartRate.Serialno);
  2204. if (step != null)
  2205. {
  2206. if (DateTime.Now.Hour - ((DateTime)step?.LastUpdate!).Hour <= 2)
  2207. {
  2208. if (step.Steps > 1500) fetalMovementStepVar = 1;
  2209. }
  2210. else
  2211. {
  2212. _logger.LogWarning($"{heartRate.Serialno} 周期步数 时间无效");
  2213. }
  2214. }
  2215. else
  2216. {
  2217. _logger.LogWarning($"{heartRate.Serialno} 周期步数无数据");
  2218. }
  2219. }
  2220. // 体温
  2221. if (true)
  2222. {
  2223. var temp = await _personCacheMgr.GetTemperaturePeriodicityAsync(heartRate.Serialno);
  2224. if (temp != null)
  2225. {
  2226. if (DateTime.Now.Hour - ((DateTime)temp?.LastUpdate!).Hour <= 2)
  2227. {
  2228. // 中烧
  2229. if (temp.Temperature >= 37.3M && temp.Temperature <= 38.5M)
  2230. {
  2231. fetalMovementTempVar = -1;
  2232. }
  2233. // 高烧
  2234. if (temp.Temperature >= 38.6M)
  2235. {
  2236. fetalMovementTempVar = -2;
  2237. }
  2238. }
  2239. else
  2240. {
  2241. _logger.LogWarning($"{heartRate.Serialno} 周期体温 时间无效");
  2242. }
  2243. }
  2244. else
  2245. {
  2246. _logger.LogWarning($"{heartRate.Serialno} 周期体温无数据");
  2247. }
  2248. }
  2249. // 血压
  2250. if (true)
  2251. {
  2252. var bp = await _personCacheMgr.GetBloodPressPeriodicityAsync(heartRate.Serialno);
  2253. if (bp != null)
  2254. {
  2255. if (DateTime.Now.Hour - ((DateTime)bp?.LastUpdate!).Hour <= 2)
  2256. {
  2257. if (bp.SystolicValue > 160)
  2258. {
  2259. fetalMovementBpVar = 1;
  2260. }
  2261. }
  2262. else
  2263. {
  2264. _logger.LogWarning($"{heartRate.Serialno} 周期血压 时间无效");
  2265. }
  2266. }
  2267. else
  2268. {
  2269. _logger.LogWarning($"{heartRate.Serialno} 周期血压无数据");
  2270. }
  2271. }
  2272. // 心理
  2273. if (false)
  2274. {
  2275. //-1 不处理,
  2276. //0 正常,
  2277. //1 轻度,
  2278. //2 中度;
  2279. //3 重度;
  2280. GeneralParam psychResultParam = new()
  2281. {
  2282. Filters = new List<QueryFilterCondition>
  2283. {
  2284. new ()
  2285. {
  2286. Key=nameof(HisGpsPsychResult.Serialno),
  2287. Value=heartRate.Serialno,
  2288. ValueType=QueryValueTypeEnum.String,
  2289. Operator=QueryOperatorEnum.Equal
  2290. },
  2291. //new ()
  2292. //{
  2293. // Key=nameof(HisGpsFetalHeartRate.SampleTime),
  2294. // Value=sampleTime,
  2295. // ValueType=QueryValueTypeEnum.String,
  2296. // Operator=QueryOperatorEnum.GreaterEqual
  2297. //},
  2298. },
  2299. OrderBys = new List<OrderByCondition>
  2300. {
  2301. new (){
  2302. IsDesc=true,
  2303. Key=nameof(HisGpsPsychResult.Serialno)
  2304. }
  2305. }
  2306. };
  2307. var psych = await _hisPsychResultApiClient.GetFirstAsync(psychResultParam, heartRate.Serialno[^2..], null, new RequestHeader { RequestId = Guid.NewGuid().ToString("D") });
  2308. if (psych != null)
  2309. {
  2310. if (psych?.StressScore == 2)
  2311. {
  2312. fetalMovementPpVar = 1;
  2313. }
  2314. if (psych?.StressScore == 3)
  2315. {
  2316. fetalMovementPpVar = 2;
  2317. }
  2318. }
  2319. else
  2320. {
  2321. _logger.LogWarning($"{heartRate.Serialno} 周期心理压力无数据");
  2322. }
  2323. }
  2324. #endregion
  2325. #region 胎心与胎动的关系
  2326. /// 胎心值过缓时,则胎动数量减1;胎心值过速时,则胎动也加1。
  2327. /// 此值允许在上限值上继续增加,在下限值上继续减少,最小值为0。
  2328. /// 告警上限阀值
  2329. //1表示偏高;2表示偏低
  2330. if (true)
  2331. {
  2332. if (fhr.IsAbnormal == 2)
  2333. {
  2334. fetalMovementPpVar = -1;
  2335. }
  2336. if (fhr.IsAbnormal == 1)
  2337. {
  2338. fetalMovementPpVar = 1;
  2339. }
  2340. }
  2341. #endregion
  2342. _logger.LogInformation($"{heartRate.Serialno} 时间比例胎动值:{fetalMovementTimeVar}, 步数参数变动值:{fetalMovementStepVar},体温参数变动值:{fetalMovementTempVar},血压参数变动值:{fetalMovementBpVar},心理压力参数变动值:{fetalMovementPpVar},胎心参数变动值:{fetalMovementFhrVar}");
  2343. var fetalMovementValue = fetalMovementTimeVar + fetalMovementStepVar + fetalMovementTempVar + fetalMovementBpVar + fetalMovementPpVar + fetalMovementFhrVar;
  2344. // 四舍五入
  2345. var fetalMovement = (int)Math.Round(fetalMovementValue, 0, MidpointRounding.AwayFromZero);
  2346. _logger.LogInformation($"{heartRate.Serialno} 孕周:{pregnancyWeeks},胎动数据采样时间:{fetalMovementSampleTime}|{statStartTime.ToString("yyyy-MM-dd HH:mm:ss")}, 采样周期:{statStartTime}-{statEndTime}, 时间比例胎动值:{fetalMovementMapValue}, 佩戴时间 :{duringMins}|{phrRange.Last()}-{phrRange.First()}, 胎动计算值:{fetalMovementTimeVar}, 胎动四舍五入最终值:{fetalMovement} 已完成.");
  2347. // 获取胎心数据状态与胎动数据状态一致
  2348. //etalHeartRateIsAbnormal= fhr.IsAbnormal;
  2349. var feltalMovementIsAbnormal = fetalHeartRateIsAbnormal;
  2350. await _serviceIotApi.SetFetalMovementConfig(heartRate.Serialno, fetalMovement, fetalMovementSampleTime, feltalMovementIsAbnormal);
  2351. // 保存到MySQL数据库
  2352. HisGpsFetalMovement fm = new()
  2353. {
  2354. FetalMovementId = Guid.NewGuid().ToString("D"),
  2355. PersonId = commonPHR!.PersonId,
  2356. Serialno = heartRate.Serialno,
  2357. CreateTime = DateTime.Now,
  2358. IsAbnormal = feltalMovementIsAbnormal,
  2359. FetalMovementValue = fetalMovement,
  2360. SampleTime = fetalMovementSampleTime,
  2361. Method = 1,
  2362. IsDisplay = 1,
  2363. DeviceKey = commonPHR!.DeviceKey
  2364. };
  2365. await _hisFetalMovementApiClient.AddAsync(fm).ConfigureAwait(false);
  2366. var device = await _deviceCacheMgr.GetDeviceBySerialNoAsync(heartRate.Serialno).ConfigureAwait(false);
  2367. var fmMsgId = $"{heartRate.Serialno}-{fetalMovementSampleTime}-{Guid.NewGuid().ToString("D")[^3..]}";
  2368. var fmMsgTime = DateTimeUtil.GetDateTimeFromUnixTimeMilliseconds(long.Parse(fetalMovementSampleTime.Length < 13 ? fetalMovementSampleTime.PadRight(13, '0') : fetalMovementSampleTime)).ToString("yyyy-MM-dd HH:mm:ss");
  2369. // 胎动数据推送到第三方
  2370. var topic = "topic.push.third";
  2371. var fmThridMsg = new
  2372. {
  2373. messageId = fmMsgId,
  2374. topic = topic,
  2375. time = fmMsgTime,
  2376. data = new
  2377. {
  2378. imei = heartRate.Serialno,
  2379. value = fetalMovement,
  2380. isAbnormal = feltalMovementIsAbnormal,
  2381. type = "fetalMovement"
  2382. }
  2383. };
  2384. await _serviceMqProcess.ProcessIMEIEventMessageAsync(fmMsgId, topic, 31, fmThridMsg).ConfigureAwait(false);
  2385. // 胎动数据推送到微信
  2386. if (feltalMovementIsAbnormal != 0)
  2387. {
  2388. topic = "topic.push.wx";
  2389. var fmMsg = new
  2390. {
  2391. messageId = Guid.NewGuid().ToString("D"),
  2392. topic = topic,
  2393. time = DateTimeUtil.GetDateTimeFromUnixTimeMilliseconds(long.Parse(fetalMovementSampleTime.Length < 13 ? fetalMovementSampleTime.PadRight(13, '0') : fetalMovementSampleTime)).ToString("yyyy-MM-dd HH:mm:ss"),
  2394. data = new
  2395. {
  2396. deviceId = device?.DeviceId,
  2397. imei = heartRate.Serialno,
  2398. alarmTypeId = 12,
  2399. alarmDeviceName = heartRate.Serialno,
  2400. alarmRemarks = JsonConvert.SerializeObject(new { fetalMovementValue = fetalMovement, isAbnormal = feltalMovementIsAbnormal }),
  2401. address = string.Empty,
  2402. deviceKey = device?.DeviceId
  2403. }
  2404. };
  2405. await _serviceMqProcess.ProcessIMEIEventMessageAsync(fmMsgId, topic, fmMsg).ConfigureAwait(false);
  2406. }
  2407. // 设置入库缓存记录
  2408. await _deviceCacheMgr.SetFetalMovementAsync(heartRate.Serialno, fetalMovementSampleTime, fm);
  2409. }
  2410. else
  2411. {
  2412. _logger.LogWarning($"{heartRate.Serialno} 孕周 {pregnancyWeeks},超出胎动计算范围");
  2413. }
  2414. }
  2415. else
  2416. {
  2417. _logger.LogWarning($"{heartRate.Serialno} 最后一条胎心数据与胎动数据的小时差大于2,不计算胎动数据");
  2418. }
  2419. }
  2420. else
  2421. {
  2422. _logger.LogInformation($"{heartRate.Serialno} 胎动记录{isFetalMovementExisted},数据采样时间:{fetalMovementSampleTime}|{statStartTime.ToString("yyyy-MM-dd HH:mm:ss")}, 周期:{statStartTime}-{statEndTime} 不足1条,不能判断是否持续佩戴");
  2423. }
  2424. }
  2425. else
  2426. {
  2427. _logger.LogInformation($"{heartRate.Serialno} 胎动记录{isFetalMovementExisted},数据采样时间:{fetalMovementSampleTime}|{statStartTime.ToString("yyyy-MM-dd HH:mm:ss")}, 周期:{statStartTime}-{statEndTime} 已处理");
  2428. }
  2429. // 跳出循环
  2430. if (statEndTime.ToString("yyyyMMddHHmm") == boundaryStatEndTime.ToString("yyyyMMddHHmm"))
  2431. {
  2432. _logger.LogInformation($"{heartRate.Serialno} 胎动记录迭代完成跳出循环 ");
  2433. break;
  2434. }
  2435. else
  2436. {
  2437. _logger.LogInformation($"{heartRate.Serialno} 胎动周期{statStartTime.ToString("yyyy-MM-dd HH:mm:ss")}-{statEndTime.ToString("yyyy-MM-dd HH:mm:ss")}计算完成, ");
  2438. }
  2439. }
  2440. }
  2441. catch (Exception ex)
  2442. {
  2443. _logger.LogError($"处理胎动数据时发生错误: {ex.Message}");
  2444. }
  2445. }
  2446. public static DateTime GetSampleTimeFromLastUpdate(DateTime lastUpdate, int interval)
  2447. {
  2448. // 获取当前的分钟
  2449. int minute = lastUpdate.Minute;
  2450. // 按 15 分钟为单位划分时间段
  2451. int intervalStartMinute = (minute / interval) * interval;
  2452. // 返回当前时间刻度下的 DateTime,保留小时和新计算的分钟,秒和毫秒设为0
  2453. return new DateTime(lastUpdate.Year, lastUpdate.Month, lastUpdate.Day, lastUpdate.Hour, intervalStartMinute, 0, 0);
  2454. }
  2455. public static string ExtractImei(string input)
  2456. {
  2457. // 提取 'imei/' 后的数字
  2458. string pattern = @"imei/(\d+)";
  2459. Match match = Regex.Match(input, pattern);
  2460. if (match.Success)
  2461. {
  2462. return match.Groups[1].Value;
  2463. }
  2464. else
  2465. {
  2466. return string.Empty;
  2467. }
  2468. }
  2469. }
  2470. }