using Google.Protobuf.WellKnownTypes; using Grpc.Core; using HealthMonitor.Common; using HealthMonitor.Common.helper; using HealthMonitor.Core.Dal; using HealthMonitor.Core.Pipeline; using HealthMonitor.Service.Biz.db; using HealthMonitor.Service.Cache; using HealthMonitor.Service.Etcd; using HealthMonitor.Service.Resolver; using HealthMonitor.Util.Entities.HealthMonitor; using HealthMonitor.WebApi.Configs; using HealthMonitor.WebApi.Model.Request; using Microsoft.AspNetCore.DataProtection.KeyManagement; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.ViewFeatures; using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Metadata.Internal; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using TelpoDataService.Util.Entities.GpsCard; using TelpoDataService.Util.Clients; using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using TelpoDataService.Util; using TelpoDataService.Util.Models; using TelpoDataService.Util.QueryObjects; using HealthMonitor.WebApi.Controllers.Api; using HealthMonitor.Service.Biz; using HealthMonitor.Model.Service; using HealthMonitor.Model.Service.Mapper; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http; namespace HealthMonitor.WebApi.Controllers.HealthMonitor { [ApiExplorerSettings(GroupName = AppConsts.SWAGGER_DOC_HealthMonitor)] [Produces("application/json")] [Route("api/HealthMonitor/[controller]/[action]")] [ApiController] public class HmBloodPressConfigManualCalibrationController : ControllerBase { private readonly ILogger _logger; private readonly PersonCacheManager _personCacheMgr; private readonly TDengineService _serviceTDengine; private readonly HttpHelper _httpHelper = default!; private readonly IotWebApiService _serviceIotWebApi; private readonly GpsCardAccessorClient _deviceApiClient; private readonly GpsCardAccessorClient _gpsPersonApiClient; public HmBloodPressConfigManualCalibrationController ( TDengineService serviceDengine, PersonCacheManager personCacheMgr, HttpHelper httpHelper, GpsCardAccessorClient gpsPersonApiClient, GpsCardAccessorClient deviceApiClient, ILogger logger, IotWebApiService iotWebApiService ) { _serviceTDengine = serviceDengine; _logger = logger; _httpHelper = httpHelper; _gpsPersonApiClient = gpsPersonApiClient; _deviceApiClient = deviceApiClient; _personCacheMgr = personCacheMgr; _serviceIotWebApi = iotWebApiService; } [HttpPost] public async Task> Put([FromBody] BloodPressManualCalibration model, [FromHeader] string requestId) { try { var imei = model.Imei; #region 设备合法性 #if DEBUG #else var param = new GeneralParam { Filters = new List { new QueryFilterCondition { Key=nameof(GpsDevice.Serialno), Value=model.Imei, ValueType=QueryValueTypeEnum.String, Operator=QueryOperatorEnum.Equal } } }; var device = await _deviceApiClient.GetFirstAsync(param, new RequestHeader { RequestId = requestId }).ConfigureAwait(false); if (device == null) { _logger.LogError($"非法设备:{model.Imei}"); return ApiResponse.Fail(500, $"非法设备:{model.Imei}"); } #endif #endregion #region 重置设备 if (model.ManualSystolicRefValue.Equals(0) && model.ManualDiastolicRefValue.Equals(0)) { //return await IotSetBloodPressCalibrationConfigResponseAsync(model.Imei, 0, 0, 0, 0).ConfigureAwait(false); var statNow = DateTime.Now; _logger.LogInformation($"{imei} 重置设备"); return await IotSetBloodPressCalibrationConfigResponseAsync(imei, 0, 0, 0, 0, 0, 0, 0, 0, statNow, statNow).ConfigureAwait(false); //return ApiResponse.Success(new //{ // imei = model.Imei, // systolicCalibrationValue = 0, //收缩压标定值,值为0 表示不生效 // diastolicCalibrationValue = 0, //舒张压标定值,值为0表示不生效 // systolicIncValue = 0, //收缩压显示增量,值为0 表示不生效 // diastolicIncValue = 0 //舒张压显示增量,值为0 表示不生效 //}); } #endregion #region 只下发标定值,不计算增量值,保存数据库(修改个人信息) if (model.IsPushRefOnly) { var statNow = DateTime.Now; _logger.LogInformation($"{imei}:只下发标定值,不计算增量值,保存数据库(修改个人信息)"); return await IotSetBloodPressCalibrationConfigResponseAsync(imei, model.ManualSystolicRefValue, model.ManualDiastolicRefValue, 0, 0, 0, 0, 0, 0, statNow, statNow).ConfigureAwait(false); } #endregion #region 数据合法性 if (model.ManualSystolicRefValue.Equals(0) || model.ManualDiastolicRefValue.Equals(0)) { return ApiResponse.Fail(501, $"数据非法,{JsonConvert.SerializeObject(model)}"); } #endregion #region 计算增量值 // 计算增量值 int systolicRefValue = model.ManualSystolicRefValue;//? int diastolicRefValue = model.ManualDiastolicRefValue;//? // 没有血压数据时增量值为0 int systolicInc; int diastolicInc; decimal systolicAvg; decimal diastolicAvg; // 最后一次下发值 int lastPushSystolicInc = 0; int lastPushDiastolicInc = 0; // 偏移参数 var avgOffset = 0.25M; var systolicAvgOffset = avgOffset; var diastolicAvgOffset = avgOffset; //int duration = 7; long duration = 7 * 24 * 3600 * 1000; TimeSpan ts = TimeSpan.FromMilliseconds(duration); DateTime endTime = DateTime.Now; //测试 DateTime startTime = endTime - ts; var lastPushResponse = await _serviceTDengine.ExecuteSelectRestResponseAsync("stb_hm_bp_push_ref_inc_value", $"serialno='{imei}' order by ts desc", "last_row(*)"); var lastPushParser = JsonConvert.DeserializeObject>(lastPushResponse!); var lastPush = lastPushParser!.Select().FirstOrDefault(); // 上次下推增量 lastPushSystolicInc = lastPush!.SystolicIncValue; lastPushDiastolicInc = lastPush!.DiastolicIncValue; startTime = lastPush!.Timestamp; var condition = $"ts between '{startTime:yyyy-MM-dd HH:mm:ss.fff}' and '{endTime:yyyy-MM-dd HH:mm:ss.fff}'" + $" and serialno='{imei}'" + $" and is_display = true"; var hmBpResponse = await _serviceTDengine.ExecuteSelectRestResponseAsync("stb_hm_bloodpress", condition); var hmBpParser = JsonConvert.DeserializeObject>(hmBpResponse!); if (hmBpParser!.Rows >= 5) { //systolicAvg = (int)(hmBpParser?.AverageAfterRemovingOneMinMaxRef(i => i.SystolicValue, SafeType.SafeInt(systolicRefValue!)))!; //diastolicAvg = (int)(hmBpParser?.AverageAfterRemovingOneMinMaxRef(i => i.DiastolicValue, SafeType.SafeInt(diastolicRefValue!)))!; var avgs = _serviceTDengine.AverageAfterRemovingOneMinMaxRef(SafeType.SafeInt(systolicRefValue!), hmBpParser!); systolicAvg = avgs[0]; diastolicAvg = avgs[1]; // 平均值为0,说明数据不足,不能计算增量值 // 数据不足等同进行初始化,只下发标定值,增量值为0;remarks恢复到未校准状态,需要基于第一个测量值生成增量值。 if (systolicAvg.Equals(0)) { _logger.LogInformation($"测量数据样本不足,将进行标定值初始,只下发标定值,增量值为0;remarks恢复到未校准状态。"); // 重置设备 var statNow = DateTime.Now; await IotSetBloodPressCalibrationConfigResponseAsync(imei, 0, 0, 0, 0, 0, 0, 0, 0, statNow, statNow).ConfigureAwait(false); _logger.LogInformation($"1.测量数据样本不足,重置设备"); var initRemarksFlag = await _serviceIotWebApi.UpdatePersonRemarksAsync(imei, 0, 0, 0, 0, true).ConfigureAwait(false); if (initRemarksFlag) { _logger.LogInformation($"2.测量数据样本不足,remarks恢复到未校准状态(remarks设置为空),成功"); } else { _logger.LogInformation($"2.测量数据样本不足,remarks恢复到未校准状态(remarks设置为空),失败"); } _logger.LogInformation($"3.测量数据样本不足,只下发手动标定值systolicRefValue:{systolicRefValue} -- diastolicRefValue:{diastolicRefValue},增量值为0"); statNow = DateTime.Now.AddSeconds(3); return await IotSetBloodPressCalibrationConfigResponseAsync(imei, systolicRefValue, diastolicRefValue, 0, 0, 0, 0, 0, 0, statNow, statNow).ConfigureAwait(false); } // 增量值=(标定值-平均值)* 0.25 var currentSystolicInc = (int)((systolicRefValue - systolicAvg) * systolicAvgOffset)!; var currentDiastolicInc = (int)((diastolicRefValue - diastolicAvg) * diastolicAvgOffset)!; // 累计增量 systolicInc = currentSystolicInc + lastPushSystolicInc; diastolicInc = currentDiastolicInc + lastPushDiastolicInc; _logger.LogInformation($"{imei}--{nameof(Put)}--计算增量值" + $"\n {imei}-- systolicAvg:{systolicAvg}--systolicInc:{systolicInc}-- currentSystolicInc:{currentSystolicInc} -- lastPushSystolicInc:{lastPushSystolicInc}" + $"\n {imei}-- diastolicAvg:{diastolicAvg}--diastolicInc:{diastolicInc} --currentDiastolicInc:{currentDiastolicInc} -- lastPushDiastolicInc:{lastPushDiastolicInc}"); _logger.LogInformation($"{imei},手工校准,发给设备的绝对增量值=(上次绝对增量值+新数据的增量值)"); return await IotSetBloodPressCalibrationConfigResponseAsync(imei, systolicRefValue, diastolicRefValue, systolicInc, diastolicInc, systolicAvg, diastolicAvg, systolicAvgOffset, diastolicAvgOffset, startTime, endTime).ConfigureAwait(false); } else { _logger.LogInformation($"没有符合条件的测试量数据,将进行标定值初始,只下发标定值,增量值为0;remarks恢复到未校准状态。"); var statNow = DateTime.Now; await IotSetBloodPressCalibrationConfigResponseAsync(imei, 0, 0, 0, 0, 0, 0, 0, 0, statNow, statNow).ConfigureAwait(false); _logger.LogInformation($"1.没有符合条件的测试量数据,重置设备"); var initRemarksFlag = await _serviceIotWebApi.UpdatePersonRemarksAsync(imei, 0, 0, 0, 0, true).ConfigureAwait(false); if (initRemarksFlag) { _logger.LogInformation($"2.没有符合条件的测试量数据,remarks恢复到未校准状态(remarks设置为空),成功"); } else { _logger.LogInformation($"2.没有符合条件的测试量数据,remarks恢复到未校准状态(remarks设置为空),失败"); } _logger.LogInformation($"3.没有符合条件的测试量数据,只下发手动标定值systolicRefValue:{systolicRefValue} -- diastolicRefValue:{diastolicRefValue},增量值为0"); statNow = DateTime.Now.AddSeconds(3); return await IotSetBloodPressCalibrationConfigResponseAsync(imei, systolicRefValue, diastolicRefValue, 0, 0, 0, 0, 0, 0, statNow, statNow).ConfigureAwait(false); } #endregion } catch ( Exception ex ) { return ApiResponse.Fail(500, $" 接口出错:{ex.Message}\n{ex.StackTrace}"); } } private async Task> IotSetBloodPressCalibrationConfigResponseAsync(string imei, int systolicRefValue, int diastolicRefValue, int systolicInc, int diastolicInc) { BloodPressCalibrationConfigModel bpIncData = new() { Imei = imei, SystolicRefValue = systolicRefValue, //收缩压标定值,值为0 表示不生效 DiastolicRefValue = diastolicRefValue, //舒张压标定值,值为0表示不生效 SystolicIncValue = systolicInc, //收缩压显示增量,值为0 表示不生效 DiastolicIncValue = diastolicInc //舒张压显示增量,值为0 表示不生效 }; // 下发 IOT 增量值 var flagIot = await _serviceIotWebApi.SetBloodPressCalibrationConfigAsync(bpIncData).ConfigureAwait(false); if (flagIot) { #region 保存下推记录 stb_hm_bp_push_ref_inc_value var sql = $"INSERT INTO health_monitor.hm_bp_push_ref_inc_value_{imei.Substring(imei.Length - 2)} " + $"USING health_monitor.stb_hm_bp_push_ref_inc_value " + $"TAGS ('{imei.Substring(imei.Length - 2)}') " + $"VALUES(" + $"'{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}'," + $"'{imei}'," + $"{systolicRefValue}," + $"{diastolicRefValue}," + $"{systolicInc}," + $"{diastolicInc}," + $"{true})"; _serviceTDengine.ExecuteInsertSQL(sql); #endregion // 返回结果 return ApiResponse.Success(bpIncData); } else { return ApiResponse.Fail(500, "业务出错!下发指令失败"); } } private async Task> IotSetBloodPressCalibrationConfigResponseAsync(string imei, int systolicRefValue, int diastolicRefValue, int systolicInc, int diastolicInc,int systolicAvgValue,int diastolicAvgValue,decimal systolicAvgOffset, decimal diastolicAvgOffset, DateTime statStartTime,DateTime statEndTime) { BloodPressCalibrationConfigModel bpIncData = new() { Imei = imei, SystolicRefValue = systolicRefValue, //收缩压标定值,值为0 表示不生效 DiastolicRefValue = diastolicRefValue, //舒张压标定值,值为0表示不生效 SystolicIncValue = systolicInc, //收缩压显示增量,值为0 表示不生效 DiastolicIncValue = diastolicInc //舒张压显示增量,值为0 表示不生效 }; // 下发 IOT 增量值 var flagIot = await _serviceIotWebApi.SetBloodPressCalibrationConfigAsync(bpIncData).ConfigureAwait(false); if (flagIot) { #region 保存下推记录 stb_hm_bp_push_ref_inc_value var sql = $"INSERT INTO health_monitor.hm_bp_push_ref_inc_value_{imei.Substring(imei.Length - 2)} " + $"USING health_monitor.stb_hm_bp_push_ref_inc_value " + $"TAGS ('{imei.Substring(imei.Length - 2)}') " + $"VALUES(" + $"'{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}'," + $"'{imei}'," + $"{systolicRefValue}," + $"{diastolicRefValue}," + $"{systolicInc}," + $"{diastolicInc}," + $"{true}," + $"{systolicAvgValue}," + $"{diastolicAvgValue}," + $"{systolicAvgOffset}," + $"{diastolicAvgOffset}," + $"'{statStartTime:yyyy-MM-dd HH:mm:ss.fff}'," + $"'{statEndTime:yyyy-MM-dd HH:mm:ss.fff}'" + $")"; _serviceTDengine.ExecuteInsertSQL(sql); #endregion // 返回结果 return ApiResponse.Success(bpIncData); } else { return ApiResponse.Fail(500, "业务出错!下发指令失败"); } } } }