You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

Worker.cs 30KB

1 년 전
1 년 전
1 년 전
1 년 전
1 년 전
1 년 전
1 년 전
8 달 전
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496
  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.Model.Config;
  8. using HealthMonitor.Model.Service;
  9. using HealthMonitor.Model.Service.Mapper;
  10. using HealthMonitor.Service.Biz;
  11. using HealthMonitor.Service.Biz.db;
  12. using HealthMonitor.Service.Cache;
  13. using HealthMonitor.Service.Etcd;
  14. using HealthMonitor.Service.Sub;
  15. using Microsoft.AspNetCore.Mvc.RazorPages;
  16. using Microsoft.EntityFrameworkCore.Metadata.Internal;
  17. using Microsoft.Extensions.Options;
  18. using Newtonsoft.Json;
  19. using Newtonsoft.Json.Linq;
  20. using System.Reflection;
  21. using System.Threading.Channels;
  22. using TDengineDriver;
  23. using TDengineTMQ;
  24. namespace HealthMonitor.WebApi
  25. {
  26. public class Worker : BackgroundService
  27. {
  28. private readonly ILogger<Worker> _logger;
  29. private readonly IServiceProvider _services;
  30. private readonly TDengineDataSubcribe _tdEngineDataSubcribe;
  31. private readonly PackageProcess _processor;
  32. private readonly TDengineService _serviceTDengine;
  33. private readonly EtcdService _serviceEtcd;
  34. private readonly HttpHelper _httpHelper = default!;
  35. private readonly IotWebApiService _serviceIotWebApi;
  36. private readonly BoodPressResolverConfig _configBoodPressResolver;
  37. private readonly BloodPressReferenceValueCacheManager _bpRefValCacheManager;
  38. private readonly PersonCacheManager _personCacheMgr;
  39. private CancellationTokenSource _tokenSource = default!;
  40. public Worker(ILogger<Worker> logger, IServiceProvider services, PersonCacheManager personCacheMgr, BloodPressReferenceValueCacheManager bpRefValCacheManager, IotWebApiService iotWebApiService, IOptions<BoodPressResolverConfig> optionBoodPressResolver, PackageProcess processor, TDengineDataSubcribe tdEngineDataSubcribe, TDengineService serviceDengine, HttpHelper httpHelper, EtcdService serviceEtcd)
  41. {
  42. _logger = logger;
  43. _tdEngineDataSubcribe = tdEngineDataSubcribe;
  44. _services = services;
  45. _serviceIotWebApi = iotWebApiService;
  46. _processor = processor;
  47. _serviceEtcd = serviceEtcd;
  48. _serviceTDengine = serviceDengine;
  49. _httpHelper = httpHelper;
  50. _configBoodPressResolver = optionBoodPressResolver.Value;
  51. _bpRefValCacheManager = bpRefValCacheManager;
  52. _personCacheMgr = personCacheMgr;
  53. }
  54. public override Task StartAsync(CancellationToken cancellationToken)
  55. {
  56. _logger.LogInformation("------StartAsync");
  57. _tokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
  58. // 创建消费者
  59. // _tdEngineDataSubcribe.CreateConsumer();
  60. return base.StartAsync(cancellationToken);
  61. }
  62. public override Task StopAsync(CancellationToken cancellationToken)
  63. {
  64. _logger.LogInformation("------StopAsync");
  65. _tokenSource.Cancel(); //停止工作线程
  66. // 关闭消费者
  67. // _tdEngineDataSubcribe.CloseConsumer();
  68. return base.StopAsync(cancellationToken);
  69. }
  70. protected override Task ExecuteAsync(CancellationToken stoppingToken)
  71. {
  72. // var processor = _services.GetService<PackageProcess>();
  73. TaskFactory factory = new(_tokenSource.Token);
  74. factory.StartNew(async () =>
  75. {
  76. if (_tokenSource.IsCancellationRequested)
  77. _logger.LogWarning("Worker exit");
  78. _logger.LogInformation("------ResolveAsync");
  79. while (!_tokenSource.IsCancellationRequested)
  80. {
  81. //
  82. await _processor.ResolveAsync().ConfigureAwait(false);
  83. // await _tdEngineDataSubcribe.ProcessMsg();
  84. }
  85. }, TaskCreationOptions.LongRunning);
  86. factory.StartNew(() =>
  87. {
  88. _logger.LogInformation("------_tdEngineDataSubcribe");
  89. while (!_tokenSource.IsCancellationRequested)
  90. {
  91. Console.WriteLine("test");
  92. _tdEngineDataSubcribe.BeginListen(_tokenSource.Token);
  93. }
  94. }, TaskCreationOptions.LongRunning);
  95. Task.Run(() =>
  96. _serviceEtcd.WacthKeysWithPrefixResponseAsync($"health_moniter/schedule_push", WatchEvents)
  97. , stoppingToken);
  98. // watch
  99. //factory.StartNew(() =>
  100. //{
  101. // while (!_tokenSource.IsCancellationRequested)
  102. // {
  103. // //_serviceEtcd.WacthKeysWithPrefixAsync($"health_moniter/schedule_push", watchEvents => WatchEvents(watchEvents));
  104. // _serviceEtcd.WacthKeysWithPrefixResponseAsync($"health_moniter/schedule_push", WatchEvents);
  105. // }
  106. //}, TaskCreationOptions.LongRunning);
  107. // _serviceEtcd.WacthKeysWithPrefixResponseAsync($"health_moniter/schedule_push", WatchEvents);
  108. return Task.Delay(1000, _tokenSource.Token);
  109. }
  110. private void WatchEvents(WatchEvent[] response)
  111. {
  112. foreach (WatchEvent e1 in response)
  113. {
  114. // Console.WriteLine($"{nameof(WatchEventsAsync)} --- {e1.Key}:{e1.Value}:{e1.Type}");
  115. switch (e1.Type.ToString())
  116. {
  117. //case "Put":
  118. // // 获取时间点计算TTL
  119. // break;
  120. case "Delete":
  121. // TTL到了重新计算TTL,下发
  122. Console.WriteLine($"--- {e1.Key}:{e1.Value}:{e1.Type}");
  123. break;
  124. }
  125. }
  126. }
  127. private void WatchEvents(WatchResponse response)
  128. {
  129. response.Events.ToList<Mvccpb.Event>().ForEach(async e =>
  130. {
  131. try
  132. {
  133. switch (e.Type.ToString())
  134. {
  135. case "Put":
  136. // 获取时间点计算TTL
  137. Console.BackgroundColor = ConsoleColor.Blue;
  138. Console.WriteLine($"--- {e.Type}--{e.Kv.Key.ToStringUtf8()}--{e.Kv.Value.ToStringUtf8()}---{DateTime.Now}");
  139. Console.BackgroundColor = ConsoleColor.Black;
  140. break;
  141. case "Delete":
  142. // TTL到了重新计算TTL,下发
  143. Console.BackgroundColor = ConsoleColor.Green;
  144. Console.WriteLine($"--- {e.Type}--{e.Kv.Key.ToStringUtf8()}--{e.Kv.Value.ToStringUtf8()}---{DateTime.Now}");
  145. // var key = $"health_moniter/schedule_push/imei/{bp.Serialno}";
  146. var key = e.Kv.Key.ToStringUtf8();
  147. var imeiDel = key.Split('/')[3];
  148. var schedule_push = await _serviceEtcd.GetValAsync(key).ConfigureAwait(false);
  149. if (string.IsNullOrWhiteSpace(schedule_push))
  150. {
  151. int systolicInc;
  152. int diastolicInc;
  153. int systolicRefValue;
  154. int diastolicRefValue;
  155. decimal systolicAvg;
  156. decimal diastolicAvg;
  157. int systolicMax = 0;
  158. int diastolicMax = 0;
  159. // 统计时间
  160. //DateTime endTime = DateTime.Now; //测试
  161. DateTime statStartTime = DateTime.Now;
  162. // 最小值
  163. int systolicMin = 0;
  164. int diastolicMin = 0;
  165. // 偏移参数
  166. var avgOffset = 0.25M;
  167. var systolicAvgOffset = avgOffset;
  168. var diastolicAvgOffset = avgOffset;
  169. // 最后一次下发值
  170. int lastPushSystolicInc = 0;
  171. int lastPushDiastolicInc = 0;
  172. var startTime = DateTime.Now;
  173. // 下发增量值
  174. #region 统计定时下发增量值
  175. //var last = await _serviceTDengine.GetLastAsync("stb_hm_bloodpress_stats_inc", $"serialno='{imeiDel}' order by last_update desc");
  176. //var ts = last?[0];
  177. // 最后一条血压数据
  178. var condition = $"serialno='{imeiDel}' order by last_update desc";
  179. var field = "last_row(*)";
  180. var lastHmBpResponse = await _serviceTDengine.ExecuteSelectRestResponseAsync("stb_hm_bloodpress", condition, field);
  181. var lastHmBpParser = JsonConvert.DeserializeObject<ParseTDengineRestResponse<BloodPressureModel>>(lastHmBpResponse!);
  182. var lastHmBp = lastHmBpParser?.Select().FirstOrDefault();
  183. //if (lastHmBpParser?.Select()?.ToList().Count < 2)
  184. //{
  185. // _logger.LogInformation($"{imeiDel} -- {nameof(Worker)} --{nameof(WatchEvents)} -- 血压数据条目不足");
  186. // break;
  187. //}
  188. // 7 天有效数据
  189. if (lastHmBp?.Timestamp.AddDays(7) > DateTime.Now)
  190. {
  191. // 计算增量值
  192. condition = $"serialno='{imeiDel}' order by ts desc";
  193. var lastPushResponse = await _serviceTDengine.ExecuteSelectRestResponseAsync("stb_hm_bp_push_ref_inc_value", condition, field);
  194. if (lastPushResponse == null)
  195. {
  196. _logger.LogInformation($"{imeiDel}--没有下发记录");
  197. break;
  198. }
  199. var lastPushParser = JsonConvert.DeserializeObject<ParseTDengineRestResponse<BloodPressurePushRefIncModel>>(lastPushResponse);
  200. var lastPush = lastPushParser!.Select().FirstOrDefault();
  201. // 有下推记录
  202. if (lastPush != null)
  203. {
  204. systolicRefValue = lastPush!.SystolicRefValue;
  205. diastolicRefValue = lastPush!.DiastolicRefValue;
  206. lastPushSystolicInc = lastPush!.SystolicIncValue;
  207. lastPushDiastolicInc = lastPush!.DiastolicIncValue;
  208. condition = $"ts between '{lastPush?.Timestamp:yyyy-MM-dd HH:mm:ss.fff}' and '{startTime:yyyy-MM-dd HH:mm:ss.fff}' " +
  209. $"and serialno='{imeiDel}' " +
  210. $"and is_display = true";
  211. // 使用最近一次的下推时间作为统计的开始时间
  212. statStartTime= lastPush!.Timestamp;
  213. }
  214. // 没有下推记录(历史遗留数据),没有初始的测量值产生的平均值(测量值=平均值)
  215. else
  216. {
  217. #region 获取个人信息
  218. var person = await _personCacheMgr.GetDeviceGpsPersonCacheBySerialNoAsync(Guid.NewGuid().ToString(), imeiDel).ConfigureAwait(false);
  219. //验证这个信息是否存在
  220. if (person == null || person?.Person.BornDate == null)
  221. {
  222. _logger.LogWarning($"{nameof(Worker)}--{imeiDel} 验证个人信息,找不到个人信息,跳过此消息");
  223. break;
  224. }
  225. // 验证年龄是否在范围 (2 - 120)
  226. var age = SafeType.SafeInt(DateTime.Today.Year - person?.Person.BornDate!.Value.Year!);
  227. if (age < 2 || age > 120)
  228. {
  229. _logger.LogWarning($"{nameof(Worker)}--{imeiDel} 验证年龄,不在范围 (2 - 120)岁,跳过此消息");
  230. break;
  231. }
  232. var gender = person?.Person.Gender == true ? 1 : 2;
  233. var isHypertension = SafeType.SafeBool(person?.Person.Ishypertension!);
  234. var height = SafeType.SafeDouble(person?.Person.Height!);
  235. var weight = SafeType.SafeDouble(person?.Person.Weight!);
  236. #endregion
  237. #region 初始化常规血压标定值标定值
  238. var bpRef = await _bpRefValCacheManager.GetBloodPressReferenceValueAsync(age, gender, isHypertension);
  239. //systolicRefValue = bpRef!.Systolic;//?
  240. //diastolicRefValue = bpRef!.Diastolic;//?
  241. #endregion
  242. systolicRefValue = bpRef!.Systolic;
  243. diastolicRefValue = bpRef!.Diastolic;
  244. lastPushSystolicInc = 0;
  245. lastPushDiastolicInc = 0;
  246. condition = $"ts <= '{startTime:yyyy-MM-dd HH:mm:ss.fff}' " +
  247. $"and serialno='{imeiDel}' " +
  248. $"and is_display = true";
  249. }
  250. var hmBpResponse = await _serviceTDengine.ExecuteSelectRestResponseAsync("stb_hm_bloodpress", condition);
  251. var hmBpParser = JsonConvert.DeserializeObject<ParseTDengineRestResponse<BloodPressureModel>>(hmBpResponse!);
  252. var hmBp = hmBpParser?.Select();
  253. //if (hmBp?.ToList().Count < 2)
  254. // 1. 判断数据样本数量
  255. if (hmBpParser!.Rows < 5)
  256. {
  257. _logger.LogInformation($"{imeiDel} -- {nameof(Worker)} --{nameof(WatchEvents)} -- 统计定时下发,计算增量值的数据条目不足:{hmBpParser!.Rows} < 5");
  258. _logger.LogInformation($"{imeiDel} -- {nameof(Worker)} --{nameof(WatchEvents)} 没有足够的数据样本,不会定时下发");
  259. break;
  260. }
  261. // 没有下推记录重新计算统计时间
  262. if (lastPush == null)
  263. {
  264. var firstHmBp = hmBpParser?.Select(i=>i).OrderBy(i=>i.Timestamp).FirstOrDefault();
  265. statStartTime = firstHmBp!.Timestamp;
  266. }
  267. // NewMethod(systolicRefValue, hmBpParser);
  268. // 最大值
  269. //systolicMax = (int)hmBpParser?.Select(i => i.SystolicValue).Max()!;
  270. //diastolicMax = (int)hmBpParser?.Select(i => i.DiastolicValue).Max()!;
  271. //// 最小值
  272. //systolicMin = (int)hmBpParser?.Select(i => i.SystolicValue).Min()!;
  273. //diastolicMin = (int)hmBpParser?.Select(i => i.DiastolicValue).Min()!;
  274. //systolicAvg = (int)(hmBpParser?.AverageAfterRemovingOneMinMaxRef(i => i.SystolicValue, SafeType.SafeInt(systolicRefValue!)))!;
  275. //diastolicAvg = (int)(hmBpParser?.AverageAfterRemovingOneMinMaxRef(i => i.DiastolicValue, SafeType.SafeInt(diastolicRefValue!)))!;
  276. var avgs = _serviceTDengine.AverageAfterRemovingOneMinMaxRef(hmBpParser!);
  277. systolicAvg = avgs[0];
  278. diastolicAvg = avgs[1];
  279. // 2. 判断能否计算增量值
  280. if (systolicAvg.Equals(0))
  281. {
  282. _logger.LogInformation($"{imeiDel}--{nameof(Worker)}--计算平均值" +
  283. $"\n currentSystolicAvg:{systolicAvg} -- lastPushSystolicInc:{lastPushSystolicInc}" +
  284. $"\n currentDiastolicInc:{diastolicAvg} -- lastPushDiastolicInc:{lastPushDiastolicInc}");
  285. _logger.LogInformation($"{imeiDel}--{nameof(Worker)} 没有足够的数据样本计算平均值,不会定时下发");
  286. break;
  287. }
  288. // 除最大值和最小值后的平均值与标定值差值少于4后(当天计算出该结果则也不产生增量调整),就不再进行增量值调整了。
  289. if (systolicAvg-systolicRefValue < 4)
  290. {
  291. _logger.LogInformation($"除最大值和最小值后的平均值与标定值差值少于4后,不再进行增量值调整");
  292. break;
  293. }
  294. // 增量值=(标定值-平均值)* 0.25
  295. var currentSystolicInc = (int)((systolicRefValue - systolicAvg) * systolicAvgOffset)!;
  296. var currentDiastolicInc = (int)((diastolicRefValue - diastolicAvg) * diastolicAvgOffset)!;
  297. // 累计增量
  298. systolicInc = currentSystolicInc + lastPushSystolicInc;
  299. diastolicInc = currentDiastolicInc + lastPushDiastolicInc;
  300. _logger.LogInformation($"{imeiDel}--{nameof(Worker)}--计算增量值" +
  301. $"\n {imeiDel} -- systolicAvg:{systolicAvg}-- systolicInc:{systolicInc}-- currentSystolicInc:{currentSystolicInc} -- lastPushSystolicInc:{lastPushSystolicInc}" +
  302. $"\n {imeiDel} -- diastolicAvg:{diastolicAvg}-- diastolicInc:{diastolicInc} --currentDiastolicInc:{currentDiastolicInc} -- lastPushDiastolicInc:{lastPushDiastolicInc}");
  303. _logger.LogInformation($"{imeiDel}--{nameof(Worker)}-- 定时校准,发给设备的绝对增量值=(上次绝对增量值+新数据的增量值)");
  304. _logger.LogInformation($"{nameof(Worker)} 开启血压标定值下发: {_configBoodPressResolver.EnableBPRefPush}");
  305. if (_configBoodPressResolver.EnableBPRefPush)
  306. // if (false) // 临时关闭
  307. {
  308. BloodPressCalibrationConfigModel bpIncData = new()
  309. {
  310. Imei = imeiDel,
  311. SystolicRefValue = SafeType.SafeInt(((int)systolicRefValue!)), //收缩压标定值,值为0 表示不生效
  312. DiastolicRefValue = SafeType.SafeInt(((int)diastolicRefValue!)), //舒张压标定值,值为0表示不生效
  313. SystolicIncValue = SafeType.SafeInt(((int)systolicInc!)), //收缩压显示增量,值为0 表示不生效
  314. DiastolicIncValue = SafeType.SafeInt(((int)diastolicInc!)) //舒张压显示增量,值为0 表示不生效
  315. };
  316. //var pushedBP = await _serviceIotWebApi.SetBloodPressCalibrationConfigAsync(bpIncData).ConfigureAwait(false);
  317. var response = await _serviceIotWebApi.SetBloodPressCalibrationConfig2Async(bpIncData).ConfigureAwait(false);
  318. var pushedBP = response.Flag;
  319. if (pushedBP)
  320. {
  321. #region 保存下推记录 stb_hm_bp_push_ref_inc_value
  322. var sql = $"INSERT INTO health_monitor.hm_bp_push_ref_inc_value_{imeiDel.Substring(imeiDel.Length - 2)} " +
  323. $"USING health_monitor.stb_hm_bp_push_ref_inc_value " +
  324. $"TAGS ('{imeiDel.Substring(imeiDel.Length - 2)}') " +
  325. $"VALUES(" +
  326. $"'{startTime:yyyy-MM-dd HH:mm:ss.fff}'," +
  327. $"'{imeiDel}'," +
  328. $"{bpIncData.SystolicRefValue}," +
  329. $"{bpIncData.DiastolicRefValue}," +
  330. $"{bpIncData.SystolicIncValue}," +
  331. $"{bpIncData.DiastolicIncValue}," +
  332. $"{false}," +
  333. $"{systolicAvg}," +
  334. $"{diastolicAvg}," +
  335. $"{systolicAvgOffset}," +
  336. $"{diastolicAvgOffset}," +
  337. $"'{statStartTime:yyyy-MM-dd HH:mm:ss.fff}'," +
  338. $"'{startTime:yyyy-MM-dd HH:mm:ss.fff}'" +
  339. $")";
  340. _serviceTDengine.ExecuteInsertSQL(sql);
  341. #endregion
  342. #region 注册定时下发
  343. // 注册下次下推
  344. var endTime = DateTime.Now;
  345. #if DEBUG
  346. //long ttl = (long)((60 * 1000-(endTime-startTime).TotalMilliseconds)/1000);
  347. //await _serviceEtcd.PutValAsync(key, imeiDel,ttl, false).ConfigureAwait(false);
  348. var interval = 0;
  349. // 获取当前时间
  350. DateTime now = DateTime.Now;
  351. // 计算距离下一个$interval天后的8点的时间间隔
  352. DateTime nextRunTime = new DateTime(now.Year, now.Month, now.Day, now.Hour, now.Minute + 2, 0).AddDays(interval);
  353. TimeSpan timeUntilNextRun = nextRunTime - now;
  354. // 如果当前时间已经超过了8点,将等待到明天后的8点
  355. if (timeUntilNextRun < TimeSpan.Zero)
  356. {
  357. timeUntilNextRun = timeUntilNextRun.Add(TimeSpan.FromMinutes(1));
  358. nextRunTime += timeUntilNextRun;
  359. }
  360. // var ttl = timeUntilNextRun.TotalMilliseconds;
  361. long ttl = (long)((timeUntilNextRun.TotalMilliseconds - (endTime - startTime).TotalMilliseconds) / 1000);
  362. var data = new
  363. {
  364. imei = imeiDel,
  365. create_time = now.ToString("yyyy-MM-dd HH:mm:ss"),
  366. ttl,
  367. next_run_time = nextRunTime.ToString("yyyy-MM-dd HH:mm:ss")
  368. };
  369. var result = JsonConvert.SerializeObject(data);
  370. await _serviceEtcd.PutValAsync(key, result, ttl, false).ConfigureAwait(false);
  371. #else
  372. // 每$interval天,晚上8点
  373. var interval = 1;
  374. // 获取当前时间
  375. DateTime now = DateTime.Now;
  376. // 计算距离下一个$interval天后的8点的时间间隔
  377. DateTime nextRunTime = new DateTime(now.Year, now.Month, now.Day, 20, 0, 0).AddDays(interval);
  378. TimeSpan timeUntilNextRun = nextRunTime - now;
  379. // 如果当前时间已经超过了8点,将等待到明天后的8点
  380. if (timeUntilNextRun < TimeSpan.Zero)
  381. {
  382. timeUntilNextRun = timeUntilNextRun.Add(TimeSpan.FromDays(1));
  383. nextRunTime += timeUntilNextRun;
  384. }
  385. // var ttl = timeUntilNextRun.TotalMilliseconds;
  386. long ttl = (long)((timeUntilNextRun.TotalMilliseconds-(endTime-startTime).TotalMilliseconds)/1000);
  387. var data = new
  388. {
  389. imei = imeiDel,
  390. create_time = now.ToString("yyyy-MM-dd HH:mm:ss"),
  391. ttl,
  392. next_run_time = nextRunTime.ToString("yyyy-MM-dd HH:mm:ss")
  393. };
  394. var result = JsonConvert.SerializeObject(data);
  395. await _serviceEtcd.PutValAsync(key, result, ttl, false).ConfigureAwait(false);
  396. #endif
  397. #endregion
  398. }
  399. else
  400. {
  401. _logger.LogInformation($"错误响应,没有下推数据:{response.Message}");
  402. }
  403. }
  404. }
  405. else
  406. {
  407. _logger.LogInformation($"向{imeiDel}统计数据已经失效");
  408. }
  409. #endregion
  410. }
  411. break;
  412. }
  413. }
  414. catch (Exception ex)
  415. {
  416. _logger.LogInformation($"{nameof(WatchEvents)},出错: |{ex.Message}|{ex.StackTrace}");
  417. }
  418. });
  419. }
  420. }
  421. }