您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

371 行
20KB

  1. using Google.Protobuf.WellKnownTypes;
  2. using Grpc.Core;
  3. using HealthMonitor.Common;
  4. using HealthMonitor.Common.helper;
  5. using HealthMonitor.Core.Dal;
  6. using HealthMonitor.Core.Pipeline;
  7. using HealthMonitor.Service.Biz.db;
  8. using HealthMonitor.Service.Cache;
  9. using HealthMonitor.Service.Etcd;
  10. using HealthMonitor.Service.Resolver;
  11. using HealthMonitor.Util.Entities.HealthMonitor;
  12. using HealthMonitor.WebApi.Configs;
  13. using HealthMonitor.WebApi.Model.Request;
  14. using Microsoft.AspNetCore.DataProtection.KeyManagement;
  15. using Microsoft.AspNetCore.Http;
  16. using Microsoft.AspNetCore.Mvc;
  17. using Microsoft.AspNetCore.Mvc.ViewFeatures;
  18. using Microsoft.EntityFrameworkCore.Metadata;
  19. using Microsoft.EntityFrameworkCore.Metadata.Internal;
  20. using Newtonsoft.Json;
  21. using Newtonsoft.Json.Linq;
  22. using TelpoDataService.Util.Entities.GpsCard;
  23. using TelpoDataService.Util.Clients;
  24. using System;
  25. using System.Collections.Generic;
  26. using System.ComponentModel.DataAnnotations;
  27. using TelpoDataService.Util;
  28. using TelpoDataService.Util.Models;
  29. using TelpoDataService.Util.QueryObjects;
  30. using HealthMonitor.WebApi.Controllers.Api;
  31. using HealthMonitor.Service.Biz;
  32. using HealthMonitor.Model.Service;
  33. using HealthMonitor.Model.Service.Mapper;
  34. using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http;
  35. namespace HealthMonitor.WebApi.Controllers.HealthMonitor
  36. {
  37. [ApiExplorerSettings(GroupName = AppConsts.SWAGGER_DOC_HealthMonitor)]
  38. [Produces("application/json")]
  39. [Route("api/HealthMonitor/[controller]/[action]")]
  40. [ApiController]
  41. public class HmBloodPressConfigManualCalibrationController : ControllerBase
  42. {
  43. private readonly ILogger<HmBloodPressConfigManualCalibrationController> _logger;
  44. private readonly PersonCacheManager _personCacheMgr;
  45. private readonly TDengineService _serviceTDengine;
  46. private readonly HttpHelper _httpHelper = default!;
  47. private readonly IotWebApiService _serviceIotWebApi;
  48. private readonly GpsCardAccessorClient<GpsDevice> _deviceApiClient;
  49. private readonly GpsCardAccessorClient<GpsPerson> _gpsPersonApiClient;
  50. public HmBloodPressConfigManualCalibrationController
  51. (
  52. TDengineService serviceDengine,
  53. PersonCacheManager personCacheMgr, HttpHelper httpHelper,
  54. GpsCardAccessorClient<GpsPerson> gpsPersonApiClient,
  55. GpsCardAccessorClient<GpsDevice> deviceApiClient,
  56. ILogger<HmBloodPressConfigManualCalibrationController> logger,
  57. IotWebApiService iotWebApiService
  58. )
  59. {
  60. _serviceTDengine = serviceDengine;
  61. _logger = logger;
  62. _httpHelper = httpHelper;
  63. _gpsPersonApiClient = gpsPersonApiClient;
  64. _deviceApiClient = deviceApiClient;
  65. _personCacheMgr = personCacheMgr;
  66. _serviceIotWebApi = iotWebApiService;
  67. }
  68. [HttpPost]
  69. public async Task<ApiResponse<object>> Put([FromBody] BloodPressManualCalibration model, [FromHeader] string requestId)
  70. {
  71. try
  72. {
  73. var imei = model.Imei;
  74. #region 设备合法性
  75. #if DEBUG
  76. #else
  77. var param = new GeneralParam
  78. {
  79. Filters = new List<QueryFilterCondition>
  80. {
  81. new QueryFilterCondition
  82. {
  83. Key=nameof(GpsDevice.Serialno),
  84. Value=model.Imei,
  85. ValueType=QueryValueTypeEnum.String,
  86. Operator=QueryOperatorEnum.Equal
  87. }
  88. }
  89. };
  90. var device = await _deviceApiClient.GetFirstAsync(param, new RequestHeader { RequestId = requestId }).ConfigureAwait(false);
  91. if (device == null)
  92. {
  93. _logger.LogError($"非法设备:{model.Imei}");
  94. return ApiResponse<object>.Fail(500, $"非法设备:{model.Imei}");
  95. }
  96. #endif
  97. #endregion
  98. #region 重置设备
  99. if (model.ManualSystolicRefValue.Equals(0) && model.ManualDiastolicRefValue.Equals(0))
  100. {
  101. //return await IotSetBloodPressCalibrationConfigResponseAsync(model.Imei, 0, 0, 0, 0).ConfigureAwait(false);
  102. var statNow = DateTime.Now;
  103. _logger.LogInformation($"{imei} 重置设备");
  104. return await IotSetBloodPressCalibrationConfigResponseAsync(imei, 0, 0, 0, 0, 0, 0, 0, 0, statNow, statNow).ConfigureAwait(false);
  105. //return ApiResponse<object>.Success(new
  106. //{
  107. // imei = model.Imei,
  108. // systolicCalibrationValue = 0, //收缩压标定值,值为0 表示不生效
  109. // diastolicCalibrationValue = 0, //舒张压标定值,值为0表示不生效
  110. // systolicIncValue = 0, //收缩压显示增量,值为0 表示不生效
  111. // diastolicIncValue = 0 //舒张压显示增量,值为0 表示不生效
  112. //});
  113. }
  114. #endregion
  115. #region 只下发标定值,不计算增量值,保存数据库(修改个人信息)
  116. if (model.IsPushRefOnly)
  117. {
  118. var statNow = DateTime.Now;
  119. _logger.LogInformation($"{imei}:只下发标定值,不计算增量值,保存数据库(修改个人信息)");
  120. return await IotSetBloodPressCalibrationConfigResponseAsync(imei, model.ManualSystolicRefValue, model.ManualDiastolicRefValue, 0, 0, 0, 0, 0, 0, statNow, statNow).ConfigureAwait(false);
  121. }
  122. #endregion
  123. #region 数据合法性
  124. if (model.ManualSystolicRefValue.Equals(0) || model.ManualDiastolicRefValue.Equals(0))
  125. {
  126. return ApiResponse<object>.Fail(501, $"数据非法,{JsonConvert.SerializeObject(model)}");
  127. }
  128. #endregion
  129. #region 计算增量值
  130. // 计算增量值
  131. int systolicRefValue = model.ManualSystolicRefValue;//?
  132. int diastolicRefValue = model.ManualDiastolicRefValue;//?
  133. // 没有血压数据时增量值为0
  134. int systolicInc;
  135. int diastolicInc;
  136. decimal systolicAvg;
  137. decimal diastolicAvg;
  138. // 最后一次下发值
  139. int lastPushSystolicInc = 0;
  140. int lastPushDiastolicInc = 0;
  141. // 偏移参数
  142. var avgOffset = 0.25M;
  143. var systolicAvgOffset = avgOffset;
  144. var diastolicAvgOffset = avgOffset;
  145. //int duration = 7;
  146. long duration = 7 * 24 * 3600 * 1000;
  147. TimeSpan ts = TimeSpan.FromMilliseconds(duration);
  148. DateTime endTime = DateTime.Now; //测试
  149. DateTime startTime = endTime - ts;
  150. var lastPushResponse = await _serviceTDengine.ExecuteSelectRestResponseAsync("stb_hm_bp_push_ref_inc_value", $"serialno='{imei}' order by ts desc", "last_row(*)");
  151. var lastPushParser = JsonConvert.DeserializeObject<ParseTDengineRestResponse<BloodPressurePushRefIncModel>>(lastPushResponse!);
  152. var lastPush = lastPushParser!.Select().FirstOrDefault();
  153. // 上次下推增量
  154. lastPushSystolicInc = lastPush!.SystolicIncValue;
  155. lastPushDiastolicInc = lastPush!.DiastolicIncValue;
  156. startTime = lastPush!.Timestamp;
  157. var condition = $"ts between '{startTime:yyyy-MM-dd HH:mm:ss.fff}' and '{endTime:yyyy-MM-dd HH:mm:ss.fff}'" +
  158. $" and serialno='{imei}'" +
  159. $" and is_display = true";
  160. var hmBpResponse = await _serviceTDengine.ExecuteSelectRestResponseAsync("stb_hm_bloodpress", condition);
  161. var hmBpParser = JsonConvert.DeserializeObject<ParseTDengineRestResponse<BloodPressureModel>>(hmBpResponse!);
  162. if (hmBpParser!.Rows >= 5)
  163. {
  164. //systolicAvg = (int)(hmBpParser?.AverageAfterRemovingOneMinMaxRef(i => i.SystolicValue, SafeType.SafeInt(systolicRefValue!)))!;
  165. //diastolicAvg = (int)(hmBpParser?.AverageAfterRemovingOneMinMaxRef(i => i.DiastolicValue, SafeType.SafeInt(diastolicRefValue!)))!;
  166. var avgs = _serviceTDengine.AverageAfterRemovingOneMinMaxRef(hmBpParser!);
  167. systolicAvg = avgs[0];
  168. diastolicAvg = avgs[1];
  169. // 平均值为0,说明数据不足,不能计算增量值
  170. // 数据不足等同进行初始化,只下发标定值,增量值为0;remarks恢复到未校准状态,需要基于第一个测量值生成增量值。
  171. if (systolicAvg.Equals(0))
  172. {
  173. _logger.LogInformation($"测量数据样本不足,将进行标定值初始,只下发标定值,增量值为0,更新remark (lastPushRefValue)");
  174. // 重置设备
  175. // var statNow = DateTime.Now;
  176. //await IotSetBloodPressCalibrationConfigResponseAsync(imei, 0, 0, 0, 0, 0, 0, 0, 0, statNow, statNow).ConfigureAwait(false);
  177. _logger.LogInformation($"1.测量数据样本不足,更新remark (lastPushRefValue)");
  178. var initRemarksFlag = await _serviceIotWebApi.UpdatePersonRemarksAsync(imei, 0, 0, 0, 0, "lastPushRefValue").ConfigureAwait(false);
  179. if (initRemarksFlag)
  180. {
  181. _logger.LogInformation($"2.测量数据样本不足,remarks恢复到未校准状态(remarks设置为空),成功");
  182. }
  183. else
  184. {
  185. _logger.LogInformation($"2.测量数据样本不足,remarks恢复到未校准状态(remarks设置为空),失败");
  186. }
  187. _logger.LogInformation($"2.测量数据样本不足,只下发手动标定值systolicRefValue:{systolicRefValue} -- diastolicRefValue:{diastolicRefValue},增量值为0");
  188. //statNow = statNow.AddSeconds(3);
  189. var statNow = DateTime.Now;
  190. return await IotSetBloodPressCalibrationConfigResponseAsync(imei, systolicRefValue, diastolicRefValue, 0, 0, 0, 0, 0, 0, statNow, statNow).ConfigureAwait(false);
  191. }
  192. // 除最大值和最小值后的平均值与标定值差值少于4后(当天计算出该结果则也不产生增量调整),就不再进行增量值调整了。
  193. //if (systolicRefValue-systolicAvg < 4)
  194. //{
  195. // _logger.LogInformation($"systolic 收缩压 {imei} 除最大值和最小值后的平均值:{systolicAvg}与标定值:{systolicRefValue}差值(标定值-平均值)少于4后,不再进行增量值调整");
  196. // return ApiResponse<object>.Fail(502, $"平均值与标定值差值少于4,不调整增量值");
  197. //}
  198. //if (diastolicRefValue - diastolicAvg < 4)
  199. //{
  200. // _logger.LogInformation($"diastolic 舒张压 {imei} 除最大值和最小值后的平均值:{diastolicAvg}与标定值:{diastolicRefValue}差值(标定值-平均值)少于4后,不再进行增量值调整");
  201. // return ApiResponse<object>.Fail(502, $"平均值与标定值差值少于4,不调整增量值");
  202. //}
  203. // 增量值=(标定值-平均值)* 0.25
  204. var currentSystolicInc = (int)((systolicRefValue - systolicAvg) * systolicAvgOffset)!;
  205. var currentDiastolicInc = (int)((diastolicRefValue - diastolicAvg) * diastolicAvgOffset)!;
  206. // 累计增量
  207. systolicInc = currentSystolicInc + lastPushSystolicInc;
  208. diastolicInc = currentDiastolicInc + lastPushDiastolicInc;
  209. _logger.LogInformation($"{imei}--{nameof(Put)}--计算增量值" +
  210. $"\n {imei}-- systolicAvg:{systolicAvg}--systolicInc:{systolicInc}-- currentSystolicInc:{currentSystolicInc} -- lastPushSystolicInc:{lastPushSystolicInc}" +
  211. $"\n {imei}-- diastolicAvg:{diastolicAvg}--diastolicInc:{diastolicInc} --currentDiastolicInc:{currentDiastolicInc} -- lastPushDiastolicInc:{lastPushDiastolicInc}");
  212. _logger.LogInformation($"{imei},手工校准,发给设备的绝对增量值=(上次绝对增量值+新数据的增量值)");
  213. return await IotSetBloodPressCalibrationConfigResponseAsync(imei, systolicRefValue, diastolicRefValue, systolicInc, diastolicInc, systolicAvg, diastolicAvg, systolicAvgOffset, diastolicAvgOffset, startTime, endTime).ConfigureAwait(false);
  214. }
  215. else
  216. {
  217. _logger.LogInformation($"没有符合条件的测试量数据(少于5条),将进行标定值初始,只下发标定值,增量值为0;更新remark (lastPushRefValue)");
  218. var statNow = DateTime.Now;
  219. // await IotSetBloodPressCalibrationConfigResponseAsync(imei, 0, 0, 0, 0, 0, 0, 0, 0, statNow, statNow).ConfigureAwait(false);
  220. _logger.LogInformation($"1.没有符合条件的测试量数据(少于5条),更新remark (lastPushRefValue)");
  221. var initRemarksFlag = await _serviceIotWebApi.UpdatePersonRemarksAsync(imei, 0, 0, 0, 0, "lastPushRefValue").ConfigureAwait(false);
  222. if (initRemarksFlag)
  223. {
  224. _logger.LogInformation($"2.没有符合条件的测试量数据(少于5条),remarks恢复到未校准状态(remarks设置为空),成功");
  225. }
  226. else
  227. {
  228. _logger.LogInformation($"2.没有符合条件的测试量数据(少于5条),remarks恢复到未校准状态(remarks设置为空),失败");
  229. }
  230. _logger.LogInformation($"2.没有符合条件的测试量数据(少于5条),只下发手动标定值systolicRefValue:{systolicRefValue} -- diastolicRefValue:{diastolicRefValue},增量值为0");
  231. statNow = statNow.AddSeconds(3);
  232. return await IotSetBloodPressCalibrationConfigResponseAsync(imei, systolicRefValue, diastolicRefValue, 0, 0, 0, 0, 0, 0, statNow, statNow).ConfigureAwait(false);
  233. }
  234. #endregion
  235. }
  236. catch
  237. ( Exception ex )
  238. {
  239. return ApiResponse<object>.Fail(500, $" 接口出错:{ex.Message}\n{ex.StackTrace}");
  240. }
  241. }
  242. private async Task<ApiResponse<object>> IotSetBloodPressCalibrationConfigResponseAsync(string imei, int systolicRefValue, int diastolicRefValue, int systolicInc, int diastolicInc)
  243. {
  244. BloodPressCalibrationConfigModel bpIncData = new()
  245. {
  246. Imei = imei,
  247. SystolicRefValue = systolicRefValue, //收缩压标定值,值为0 表示不生效
  248. DiastolicRefValue = diastolicRefValue, //舒张压标定值,值为0表示不生效
  249. SystolicIncValue = systolicInc, //收缩压显示增量,值为0 表示不生效
  250. DiastolicIncValue = diastolicInc //舒张压显示增量,值为0 表示不生效
  251. };
  252. // 下发 IOT 增量值
  253. // var flagIot = await _serviceIotWebApi.SetBloodPressCalibrationConfigAsync(bpIncData).ConfigureAwait(false);
  254. var response = await _serviceIotWebApi.SetBloodPressCalibrationConfig2Async(bpIncData).ConfigureAwait(false);
  255. var flagIot = response.Flag;
  256. if (flagIot)
  257. {
  258. #region 保存下推记录 stb_hm_bp_push_ref_inc_value
  259. var sql = $"INSERT INTO health_monitor.hm_bp_push_ref_inc_value_{imei.Substring(imei.Length - 2)} " +
  260. $"USING health_monitor.stb_hm_bp_push_ref_inc_value " +
  261. $"TAGS ('{imei.Substring(imei.Length - 2)}') " +
  262. $"VALUES(" +
  263. $"'{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}'," +
  264. $"'{imei}'," +
  265. $"{systolicRefValue}," +
  266. $"{diastolicRefValue}," +
  267. $"{systolicInc}," +
  268. $"{diastolicInc}," +
  269. $"{true})";
  270. _serviceTDengine.ExecuteInsertSQL(sql);
  271. #endregion
  272. // 返回结果
  273. return ApiResponse<object>.Success(bpIncData);
  274. }
  275. else
  276. {
  277. return ApiResponse<object>.Fail(500, $"业务出错!下发指令失败:{response.Message}");
  278. }
  279. }
  280. private async Task<ApiResponse<object>> IotSetBloodPressCalibrationConfigResponseAsync(string imei, int systolicRefValue, int diastolicRefValue, int systolicInc, int diastolicInc,decimal systolicAvgValue,decimal diastolicAvgValue,decimal systolicAvgOffset, decimal diastolicAvgOffset, DateTime statStartTime,DateTime statEndTime)
  281. {
  282. BloodPressCalibrationConfigModel bpIncData = new()
  283. {
  284. Imei = imei,
  285. SystolicRefValue = systolicRefValue, //收缩压标定值,值为0 表示不生效
  286. DiastolicRefValue = diastolicRefValue, //舒张压标定值,值为0表示不生效
  287. SystolicIncValue = systolicInc, //收缩压显示增量,值为0 表示不生效
  288. DiastolicIncValue = diastolicInc //舒张压显示增量,值为0 表示不生效
  289. };
  290. // 下发 IOT 增量值
  291. //var flagIot = await _serviceIotWebApi.SetBloodPressCalibrationConfigAsync(bpIncData).ConfigureAwait(false);
  292. var response = await _serviceIotWebApi.SetBloodPressCalibrationConfig2Async(bpIncData).ConfigureAwait(false);
  293. var flagIot = response.Flag;
  294. if (flagIot)
  295. {
  296. #region 保存下推记录 stb_hm_bp_push_ref_inc_value
  297. var sql = $"INSERT INTO health_monitor.hm_bp_push_ref_inc_value_{imei.Substring(imei.Length - 2)} " +
  298. $"USING health_monitor.stb_hm_bp_push_ref_inc_value " +
  299. $"TAGS ('{imei.Substring(imei.Length - 2)}') " +
  300. $"VALUES(" +
  301. $"'{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}'," +
  302. $"'{imei}'," +
  303. $"{systolicRefValue}," +
  304. $"{diastolicRefValue}," +
  305. $"{systolicInc}," +
  306. $"{diastolicInc}," +
  307. $"{true}," +
  308. $"{(float)systolicAvgValue}," +
  309. $"{(float)diastolicAvgValue}," +
  310. $"{systolicAvgOffset}," +
  311. $"{diastolicAvgOffset}," +
  312. $"'{statStartTime:yyyy-MM-dd HH:mm:ss.fff}'," +
  313. $"'{statEndTime:yyyy-MM-dd HH:mm:ss.fff}'" +
  314. $")";
  315. _serviceTDengine.ExecuteInsertSQL(sql);
  316. #endregion
  317. // 返回结果
  318. return ApiResponse<object>.Success(bpIncData);
  319. }
  320. else
  321. {
  322. return ApiResponse<object>.Fail(500, $"业务出错!下发指令失败:{response.Message}");
  323. }
  324. }
  325. }
  326. }