Browse Source

孕妇心率

datasub12_fetal_heart_rate_0
H Vs 4 months ago
parent
commit
643d6af19e
19 changed files with 1284 additions and 1029 deletions
  1. +4
    -12
      HealthMonitor.Model/Config/ServiceConfig.cs
  2. +79
    -0
      HealthMonitor.Model/Service/Mapper/PregnancyCommonHeartRateModel.cs
  3. +328
    -0
      HealthMonitor.Service/Biz/IotApiService.cs
  4. +0
    -626
      HealthMonitor.Service/Biz/IotWebApiService.cs
  5. +190
    -54
      HealthMonitor.Service/Biz/db/TDengineService.cs
  6. +52
    -0
      HealthMonitor.Service/Cache/DeviceCacheManager.cs
  7. +2
    -2
      HealthMonitor.Service/Resolver/BloodpressResolver.cs
  8. +0
    -1
      HealthMonitor.Service/Resolver/Factory/ResolverFactory.cs
  9. +134
    -0
      HealthMonitor.Service/Resolver/PregnancyHeartRateResolver.cs
  10. +11
    -1
      HealthMonitor.Service/Sub/MsgQueueManager.cs
  11. +70
    -31
      HealthMonitor.Service/Sub/TDengineDataSubcribe.cs
  12. +44
    -0
      HealthMonitor.Service/Sub/Topic/Model/TopicHmPregnancyHeartRate.cs
  13. +2
    -2
      HealthMonitor.WebApi/Controllers/HealthMonitor/HmBloodPressConfigManualCalibrationController.cs
  14. +18
    -1
      HealthMonitor.WebApi/Controllers/HealthMonitor/HmBloodPressController.cs
  15. +4
    -2
      HealthMonitor.WebApi/Program.cs
  16. +337
    -294
      HealthMonitor.WebApi/Worker.cs
  17. +3
    -1
      HealthMonitor.WebApi/appsettings.Development.json
  18. +3
    -1
      HealthMonitor.WebApi/appsettings.production.json
  19. +3
    -1
      HealthMonitor.WebApi/appsettings.test.json

+ 4
- 12
HealthMonitor.Model/Config/ServiceConfig.cs View File

@@ -10,17 +10,9 @@
public string EtcdServerAddress { get; set; } = default!;

public string IotWebApiUrl { get; set; } = default!;
///// <summary>
///// Kafka服务地址
///// </summary>
//public string MqServerAddress { get; set; }
///// <summary>
///// 服务守护消息kafka服务地址
///// </summary>
//public string ServiceGuardMqAddress { get; set; }
///// <summary>
///// 服务守护消息主题
///// </summary>
//public string ServiceGuardMqTopic { get; set; }

public string IotAuth { get; set; } = default!;

public string IotCore { get; set; } = default!;
}
}

+ 79
- 0
HealthMonitor.Model/Service/Mapper/PregnancyCommonHeartRateModel.cs View File

@@ -0,0 +1,79 @@
using Newtonsoft.Json;
using SqlSugar;
using SqlSugar.TDengine;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace HealthMonitor.Model.Service.Mapper
{
[STableAttribute(STableName = "stb_hm_pregnancy_common_heart_rate")]
public class PregnancyCommonHeartRateModel
{
[JsonProperty("ts")]
[SqlSugar.SugarColumn(IsPrimaryKey = true, ColumnName = "ts", SqlParameterDbType = typeof(DateTime19))]
public DateTime Timestamp { get; set; }


[JsonProperty("person_id")]
[SqlSugar.SugarColumn(ColumnName = "person_id")]
public string PersonId { get; set; } = default!;

[JsonProperty("device_key")]
[SqlSugar.SugarColumn(ColumnName = "device_key")]
public string DeviceKey { get; set; } = default!;

[JsonProperty("serialno")]
[SqlSugar.SugarColumn(ColumnName = "serialno")]
public string SerialNumber { get; set; } = default!;


[JsonProperty("mode")]
[SqlSugar.SugarColumn(ColumnName = "mode")]
public float Mode { get; set; }

[JsonProperty("percentage")]
[SqlSugar.SugarColumn(ColumnName = "percentage")]
public int Percentage { get; set; }

[JsonProperty("max_value")]
[SqlSugar.SugarColumn(ColumnName = "max_value")]
public int MaxValue { get; set; }

[JsonProperty("min_value")]
[SqlSugar.SugarColumn(ColumnName = "min_value")]
public int MinValue { get; set; }


[JsonProperty("original_max_value")]
[SqlSugar.SugarColumn(ColumnName = "original_max_value")]
public int OriginalMaxValue { get; set; }

[JsonProperty("original_min_value")]
[SqlSugar.SugarColumn(ColumnName = "original_min_value")]
public int OriginalMinValue { get; set; }

[JsonProperty("create_time")]
[SqlSugar.SugarColumn(ColumnName = "create_time")]
public DateTime CreateTime { get; set; }

[JsonProperty("stat_start_time")]
[SqlSugar.SugarColumn(ColumnName = "stat_start_time")]
public DateTime StatStartTime { get; set; }


[JsonProperty("stat_end_time")]
[SqlSugar.SugarColumn(ColumnName = "stat_end_time")]
public DateTime StatEndTime { get; set; }

[JsonProperty("remark")]
[SqlSugar.SugarColumn(ColumnName = "remark")]
public string Remark { get; set; } = default!;

[JsonProperty("serial_tail_no")]
[SqlSugar.SugarColumn(IsIgnore = true, ColumnName = "serial_tail_no")]
public string SerialTailNumber { get; set; } = default!;
}
}

+ 328
- 0
HealthMonitor.Service/Biz/IotApiService.cs View File

@@ -0,0 +1,328 @@
using HealthMonitor.Common.helper;
using HealthMonitor.Model.Config;
using HealthMonitor.Service.Resolver;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using HealthMonitor.Model.Service;
using TelpoDataService.Util.Entities.GpsCard;
using TelpoDataService.Util;
using TelpoDataService.Util.Clients;
using TelpoDataService.Util.Models;
using TelpoDataService.Util.QueryObjects;
using HealthMonitor.Service.Cache;
using HealthMonitor.Model.Cache;
using Etcdserverpb;

namespace HealthMonitor.Service.Biz
{
public class IotApiService
{
private readonly ServiceConfig _configService;
private readonly ILogger<IotApiService> _logger;
private readonly PersonCacheManager _personCacheMgr;
private readonly DeviceCacheManager _deviceCacheMgr;


private readonly HttpHelper _httpHelper = default!;

private readonly GpsCardAccessorClient<GpsPerson> _gpsPersonApiClient;

public IotApiService(ILogger<IotApiService> logger, HttpHelper httpHelper, DeviceCacheManager deviceCacheMgr, GpsCardAccessorClient<GpsPerson> gpsPersonApiClient, IOptions<ServiceConfig> optConfigService, PersonCacheManager personCacheMgr)
{
_configService = optConfigService.Value;
_httpHelper=httpHelper;
_logger = logger;
_personCacheMgr = personCacheMgr;
_gpsPersonApiClient = gpsPersonApiClient;
_deviceCacheMgr = deviceCacheMgr;
}

#region 平台下发血压标定参数
/// <summary>
/// 平台下发血压标定参数
/// </summary>
/// <param name="bpsCalibrationConfig"></param>
/// <returns></returns>
public async Task<bool> SetBloodPressCalibrationConfigAsync(BloodPressCalibrationConfigModel bpsCalibrationConfig)
{

#if DEBUG
var flag = true;
#else
//systolicCalibrationValue = 0, //收缩压标定值,值为0 表示不生效
//diastolicCalibrationValue 0, //舒张压标定值,值为0表示不生效
//systolicIncValue = 0, //收缩压显示增量,值为0 表示不生效
//diastolicIncValue = 0 //舒张压显示增量,值为0 表示不生效

var flag = false;
try
{
var url = $"{_configService.IotWebApiUrl}Command/SetBloodPressCalibrationConfig";
List<KeyValuePair<string, string>> headers = new()
{
new KeyValuePair<string, string>("AuthKey", "key1")
};
var res = await _httpHelper.HttpToPostAsync(url, bpsCalibrationConfig, headers).ConfigureAwait(false);
_logger.LogInformation($"向{bpsCalibrationConfig.Imei}下发增量值数据:{JsonConvert.SerializeObject(bpsCalibrationConfig)},响应:{res}");
var resJToken = JsonConvert.DeserializeObject(res ?? string.Empty) as JToken;
flag= resJToken?["message"]?.ToString().Equals("ok") ?? false;

}
catch (Exception ex)
{
_logger.LogError($"{nameof(SetBloodPressCalibrationConfigAsync)} 下发血压增量值异常:{ex.Message}, {ex.StackTrace}");

}

#endif

return flag;


}


public async Task<BloodPressCalibrationConfigModelReponse> SetBloodPressCalibrationConfig2Async(BloodPressCalibrationConfigModel bpsCalibrationConfig)
{
BloodPressCalibrationConfigModelReponse response = new BloodPressCalibrationConfigModelReponse();
response.Flag = false;
response.Message = string.Empty;
#if DEBUG
//var flag = true;
response.Flag=true;
#else
//systolicCalibrationValue = 0, //收缩压标定值,值为0 表示不生效
//diastolicCalibrationValue 0, //舒张压标定值,值为0表示不生效
//systolicIncValue = 0, //收缩压显示增量,值为0 表示不生效
//diastolicIncValue = 0 //舒张压显示增量,值为0 表示不生效

// var flag = false;
try
{
var url = $"{_configService.IotWebApiUrl}Command/SetBloodPressCalibrationConfig";
List<KeyValuePair<string, string>> headers = new()
{
new KeyValuePair<string, string>("AuthKey", "key1")
};
var res = await _httpHelper.HttpToPostAsync(url, bpsCalibrationConfig, headers).ConfigureAwait(false);
_logger.LogInformation($"向{bpsCalibrationConfig.Imei}下发增量值数据:{JsonConvert.SerializeObject(bpsCalibrationConfig)},响应:{res}");
var resJToken = JsonConvert.DeserializeObject(res ?? string.Empty) as JToken;
//response.Flag= resJToken?["message"]?.ToString().Equals("ok") ?? false;
response.Flag = Convert.ToBoolean(resJToken?["succeed"]?.ToString());
if (!response.Flag)
{
response.Message = resJToken?["message"]?.ToString()!;
}
}
catch (Exception ex)
{
_logger.LogError($"{nameof(SetBloodPressCalibrationConfigAsync)} 下发血压增量值异常:{ex.Message}, {ex.StackTrace}");

}

#endif

return response;
}

public async Task<bool> UpdatePersonRemarksAsync(string imei, int systolicRefValue, int diastolicRefValue, int systolicIncValue, int diastolicIncValue, string remarks = "is_blood_press")
{
var flag = false;
try
{

// 保证实时性,先更新缓存,再更新数据库
var personCache = await _personCacheMgr.GetDeviceGpsPersonCacheObjectBySerialNoAsync(new Guid().ToString(), imei).ConfigureAwait(false);

if (personCache == null)
{
_logger.LogInformation($"{imei} -- Person remarks数据异常,检查缓存和数据库");
}
else
{
var newRemarkData = new
{
imei,
time = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
commandValue = new
{
systolicCalibrationValue = systolicRefValue, //收缩压标定值,值为0 表示不生效
diastolicCalibrationValue = diastolicRefValue, //舒张压标定值,值为0表示不生效
systolicIncValue, //收缩压显示增量,值为0 表示不生效
diastolicIncValue //舒张压显示增量,值为0 表示不生效
}
};
var newRemarkStr = $"{remarks}:{JsonConvert.SerializeObject(newRemarkData)}|";

personCache["person"]!["remarks"] = newRemarkStr;
bool cacheFlag = await _personCacheMgr.UpdateDeviceGpsPersonCacheObjectBySerialNoAsync(personCache, imei);
if (cacheFlag)
{
GeneralParam condition = new()
{
Filters = new List<QueryFilterCondition> {
new QueryFilterCondition {
Key=nameof(GpsDevice.Serialno),
Value=imei,
Operator= QueryOperatorEnum.Equal,
ValueType=QueryValueTypeEnum.String
}
},
OrderBys = new List<OrderByCondition> { new OrderByCondition { Key = "serialno", IsDesc = true } }

};
_logger.LogInformation($"{imei} 更新缓存{nameof(UpdatePersonRemarksAsync)}成功,{JsonConvert.SerializeObject(personCache)}");
// 读取数据库
var person = await _gpsPersonApiClient.GetFirstAsync(condition, new RequestHeader() { RequestId = $"{imei}" }).ConfigureAwait(false);
// 更新字段
person!.Remarks = newRemarkStr;
await _gpsPersonApiClient.UpdateAsync(person, new RequestHeader() { RequestId = $"{imei}" }).ConfigureAwait(false);
_logger.LogInformation($"{imei} 更新Person remarks字段|{person.Remarks}");

}
else
{
_logger.LogInformation($"{imei} 更新缓存和数据库{nameof(UpdatePersonRemarksAsync)}失败,{JsonConvert.SerializeObject(personCache)}");
}
flag = cacheFlag;
}
// else if (string.IsNullOrWhiteSpace(personCache["person"]!["remarks"]!.ToString()))
//else if (personCache?["person"]!["remarks"]!.ToString()!=null)
//{
// var newRemarkData = new
// {
// imei,
// time = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
// commandValue = new
// {
// systolicCalibrationValue = systolicRefValue, //收缩压标定值,值为0 表示不生效
// diastolicCalibrationValue = diastolicRefValue, //舒张压标定值,值为0表示不生效
// systolicIncValue, //收缩压显示增量,值为0 表示不生效
// diastolicIncValue //舒张压显示增量,值为0 表示不生效
// }
// };
// var newRemarkStr = $"{remarks}:{JsonConvert.SerializeObject(newRemarkData)}|";

// personCache["person"]!["remarks"] = newRemarkStr;
// bool cacheFlag = await _personCacheMgr.UpdateDeviceGpsPersonCacheObjectBySerialNoAsync(personCache, imei);
// if (cacheFlag)
// {
// GeneralParam condition = new()
// {
// Filters = new List<QueryFilterCondition> {
// new QueryFilterCondition {
// Key=nameof(GpsDevice.Serialno),
// Value=imei,
// Operator= QueryOperatorEnum.Equal,
// ValueType=QueryValueTypeEnum.String
// }
// },
// OrderBys = new List<OrderByCondition> { new OrderByCondition { Key = "serialno", IsDesc = true } }

// };
// _logger.LogInformation($"{imei} 更新缓存{nameof(UpdatePersonRemarksAsync)}成功,{JsonConvert.SerializeObject(personCache)}");
// // 读取数据库
// var person = await _gpsPersonApiClient.GetFirstAsync(condition, new RequestHeader() { RequestId = $"{imei}" }).ConfigureAwait(false);
// // 更新字段
// person!.Remarks = newRemarkStr;
// await _gpsPersonApiClient.UpdateAsync(person, new RequestHeader() { RequestId = $"{imei}" }).ConfigureAwait(false);
// _logger.LogInformation($"{imei} 更新Person remarks字段|{person.Remarks}");

// }
// else
// {
// _logger.LogInformation($"{imei} 更新缓存和数据库{nameof(UpdatePersonRemarksAsync)}失败,{JsonConvert.SerializeObject(personCache)}");
// }
// flag = cacheFlag;
//}
}
catch (Exception ex)
{
_logger.LogError($"{nameof(UpdatePersonRemarksAsync)} {imei}--更新个人信息异常:{ex.Message}, {ex.StackTrace}");
}
return flag;
}

#endregion

#region 平台下发胎心监测参数
public async Task<bool> SetFetalHeartRateConfig(string serialno, int modeStatus=0, int maxValue=0, int minValue = 0)
{
try
{
#region 读取缓存
// db7.HashGet("TELPO#GPSDEVICE_WATCH_CONFIG_HASH","861281060086083_0067")
var watchConfig = await _deviceCacheMgr.GetGpsDeviceWatchConfigCacheObjectBySerialNoAsync(serialno, "0067");
if (watchConfig==null)
{
return false;
}

#endregion

#region 获取B端 Token
var getTokenUrl = $"{_configService.IotAuth}/getAccessToken2";
var tokenReq = new
{
manufactorId= "7c7c38cb-d045-41d8-b3d0-fcaaa84a8f02",
imei= serialno
};
var resToken = await _httpHelper.HttpToPostAsync(getTokenUrl, tokenReq).ConfigureAwait(false);
var tokenAuth= JsonConvert.DeserializeObject(resToken ?? string.Empty) as JToken;
var tokenAuthData = tokenAuth?["data"]?.ToString()??string.Empty;
if (tokenAuthData == null)
{
return false;
}
#endregion

#region 发送到B端
List<KeyValuePair<string, string>> headers = new()
{
new KeyValuePair<string, string>("TelpoManufactorId", tokenAuthData)
};
var data = new
{
imeis = new string[] { serialno },
enabled = (int)watchConfig["fetalParamters"]!["enabled"]!,
triggerHighFreqHigh = maxValue == 0 ? (int)watchConfig["fetalParamters"]!["triggerHighFreqHigh"]!:maxValue,
triggerLowFreqLow = minValue == 0 ? (int)watchConfig["fetalParamters"]!["triggerHighFreqLow"]! : minValue,
highFreqSampleTimes = (int)watchConfig["fetalParamters"]!["highFreqSampleTimes"]!,
highFreqSampleInterval = (int)watchConfig["fetalParamters"]!["highFreqSampleInterval"]!,
stopHighFreqSampleCount = (int)watchConfig["fetalParamters"]!["stopHighFreqSampleCount"]!,
mode = modeStatus,
edoc = watchConfig["fetalParamters"]!["EDOC"]!,
vibrateEnabled = (int)watchConfig["fetalParamters"]!["vibrateEnabled"]!,
lcdEnabled= (int)watchConfig["fetalParamters"]!["lcdEnabled"]!
};
var setUrl = $"{_configService.IotCore}/api/v1/open/OpenIot/SetFetalConfig";
var res = await _httpHelper.HttpToPostAsync(setUrl, data, headers).ConfigureAwait(false);
var resJToken = JsonConvert.DeserializeObject(res ?? string.Empty) as JToken;
return resJToken?["message"]?.ToString().Equals("ok") ?? false;
//response.Flag = resJToken?["message"]?.ToString().Equals("ok") ?? false;
//response.Flag = Convert.ToBoolean(resJToken?["succeed"]?.ToString());
//if (!response.Flag)
//{
// response.Message = resJToken?["message"]?.ToString()!;
//}
#endregion
}
catch (Exception ex)
{
_logger.LogError($"{nameof(SetFetalHeartRateConfig)} 下发胎心检测参数异常:{ex.Message}, {ex.StackTrace}");
return false;
}
}
#endregion

}
}

+ 0
- 626
HealthMonitor.Service/Biz/IotWebApiService.cs View File

@@ -1,626 +0,0 @@
using HealthMonitor.Common.helper;
using HealthMonitor.Model.Config;
using HealthMonitor.Service.Resolver;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using HealthMonitor.Model.Service;
using TelpoDataService.Util.Entities.GpsCard;
using TelpoDataService.Util;
using TelpoDataService.Util.Clients;
using TelpoDataService.Util.Models;
using TelpoDataService.Util.QueryObjects;
using HealthMonitor.Service.Cache;
using HealthMonitor.Model.Cache;

namespace HealthMonitor.Service.Biz
{
public class IotWebApiService
{
private readonly ServiceConfig _configService;
private readonly ILogger<IotWebApiService> _logger;
private readonly PersonCacheManager _personCacheMgr;

private readonly HttpHelper _httpHelper = default!;

private readonly GpsCardAccessorClient<GpsPerson> _gpsPersonApiClient;

public IotWebApiService(ILogger<IotWebApiService> logger, HttpHelper httpHelper, GpsCardAccessorClient<GpsPerson> gpsPersonApiClient, IOptions<ServiceConfig> optConfigService, PersonCacheManager personCacheMgr)
{
_configService = optConfigService.Value;
_httpHelper=httpHelper;
_logger = logger;
_personCacheMgr = personCacheMgr;
_gpsPersonApiClient = gpsPersonApiClient;
}
/// <summary>
/// 平台下发血压标定参数
/// </summary>
/// <param name="bpsCalibrationConfig"></param>
/// <returns></returns>
public async Task<bool> SetBloodPressCalibrationConfigAsync(BloodPressCalibrationConfigModel bpsCalibrationConfig)
{

#if DEBUG
var flag = true;
#else
//systolicCalibrationValue = 0, //收缩压标定值,值为0 表示不生效
//diastolicCalibrationValue 0, //舒张压标定值,值为0表示不生效
//systolicIncValue = 0, //收缩压显示增量,值为0 表示不生效
//diastolicIncValue = 0 //舒张压显示增量,值为0 表示不生效

var flag = false;
try
{
var url = $"{_configService.IotWebApiUrl}Command/SetBloodPressCalibrationConfig";
List<KeyValuePair<string, string>> headers = new()
{
new KeyValuePair<string, string>("AuthKey", "key1")
};
var res = await _httpHelper.HttpToPostAsync(url, bpsCalibrationConfig, headers).ConfigureAwait(false);
_logger.LogInformation($"向{bpsCalibrationConfig.Imei}下发增量值数据:{JsonConvert.SerializeObject(bpsCalibrationConfig)},响应:{res}");
var resJToken = JsonConvert.DeserializeObject(res ?? string.Empty) as JToken;
flag= resJToken?["message"]?.ToString().Equals("ok") ?? false;

}
catch (Exception ex)
{
_logger.LogError($"{nameof(SetBloodPressCalibrationConfigAsync)} 下发血压增量值异常:{ex.Message}, {ex.StackTrace}");

}

#endif

return flag;


}


public async Task<BloodPressCalibrationConfigModelReponse> SetBloodPressCalibrationConfig2Async(BloodPressCalibrationConfigModel bpsCalibrationConfig)
{
BloodPressCalibrationConfigModelReponse response = new BloodPressCalibrationConfigModelReponse();
response.Flag = false;
response.Message = string.Empty;
#if DEBUG
//var flag = true;
response.Flag=true;
#else
//systolicCalibrationValue = 0, //收缩压标定值,值为0 表示不生效
//diastolicCalibrationValue 0, //舒张压标定值,值为0表示不生效
//systolicIncValue = 0, //收缩压显示增量,值为0 表示不生效
//diastolicIncValue = 0 //舒张压显示增量,值为0 表示不生效

// var flag = false;
try
{
var url = $"{_configService.IotWebApiUrl}Command/SetBloodPressCalibrationConfig";
List<KeyValuePair<string, string>> headers = new()
{
new KeyValuePair<string, string>("AuthKey", "key1")
};
var res = await _httpHelper.HttpToPostAsync(url, bpsCalibrationConfig, headers).ConfigureAwait(false);
_logger.LogInformation($"向{bpsCalibrationConfig.Imei}下发增量值数据:{JsonConvert.SerializeObject(bpsCalibrationConfig)},响应:{res}");
var resJToken = JsonConvert.DeserializeObject(res ?? string.Empty) as JToken;
//response.Flag= resJToken?["message"]?.ToString().Equals("ok") ?? false;
response.Flag = Convert.ToBoolean(resJToken?["succeed"]?.ToString());
if (!response.Flag)
{
response.Message = resJToken?["message"]?.ToString()!;
}
}
catch (Exception ex)
{
_logger.LogError($"{nameof(SetBloodPressCalibrationConfigAsync)} 下发血压增量值异常:{ex.Message}, {ex.StackTrace}");

}

#endif

return response;
}
/** 取消
/// <summary>
/// 更新 gps_person remark和缓存
/// </summary>
/// <param name="imei"></param>
/// <param name="systolicRefValue"></param>
/// <param name="diastolicRefValue"></param>
/// <returns></returns>
public async Task<bool> UpdatePersonRemarksAsync(string imei,int systolicRefValue,int diastolicRefValue)
{
var flag = false;
try
{
GeneralParam condition = new ()
{
Filters = new List<QueryFilterCondition> {
new QueryFilterCondition {
Key=nameof(GpsDevice.Serialno),
Value=imei,
Operator= QueryOperatorEnum.Equal,
ValueType=QueryValueTypeEnum.String
}
},
OrderBys = new List<OrderByCondition> { new OrderByCondition { Key = "serialno", IsDesc = true } }

};
var person = await _gpsPersonApiClient.GetFirstAsync(condition, new RequestHeader() { RequestId = $"{imei}" }).ConfigureAwait(false);
//// 若remark为空,更新person remark字段
//if (string.IsNullOrWhiteSpace(person?.Remarks))
//{
// var newRemarkData = new
// {
// imei,
// time = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
// commandValue = new
// {
// systolicCalibrationValue = systolicRefValue, //收缩压标定值,值为0 表示不生效
// diastolicCalibrationValue = diastolicRefValue, //舒张压标定值,值为0表示不生效
// systolicIncValue = 0, //收缩压显示增量,值为0 表示不生效
// diastolicIncValue = 0 //舒张压显示增量,值为0 表示不生效
// }
// };
// person!.Remarks = $"is_blood_press:{JsonConvert.SerializeObject(newRemarkData)}|";
// await _gpsPersonApiClient.UpdateAsync(person, new RequestHeader() { RequestId = $"{imei}" }).ConfigureAwait(false);
// _logger.LogInformation($"更新Person remarks字段|{person.Remarks}");

// // 更新缓存
// var url = $"{_configService.IotWebApiUrl}Device/UpdatePersonInfoCache?imei={imei}";
// List<KeyValuePair<string, string>> headers = new()
// {
// new KeyValuePair<string, string>("AuthKey", "key1")
// };
// var res = await _httpHelper.HttpToGetAsync(url, headers).ConfigureAwait(false);
// _logger.LogInformation($"{imei} 更新缓存{nameof(UpdatePersonRemarksAsync)},响应:{res}");
// var resJToken = JsonConvert.DeserializeObject(res ?? string.Empty) as JToken;
// flag = resJToken?["message"]?.ToString().Equals("ok") ?? false;
//}

var newRemarkData = new
{
imei,
time = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
commandValue = new
{
systolicCalibrationValue = systolicRefValue, //收缩压标定值,值为0 表示不生效
diastolicCalibrationValue = diastolicRefValue, //舒张压标定值,值为0表示不生效
systolicIncValue = 0, //收缩压显示增量,值为0 表示不生效
diastolicIncValue = 0 //舒张压显示增量,值为0 表示不生效
}
};
person!.Remarks = $"is_blood_press:{JsonConvert.SerializeObject(newRemarkData)}|";
await _gpsPersonApiClient.UpdateAsync(person, new RequestHeader() { RequestId = $"{imei}" }).ConfigureAwait(false);
_logger.LogInformation($"更新Person remarks字段|{person.Remarks}");

// 更新缓存
var url = $"{_configService.IotWebApiUrl}Device/UpdatePersonInfoCache?imei={imei}";
List<KeyValuePair<string, string>> headers = new()
{
new KeyValuePair<string, string>("AuthKey", "key1")
};
var res = await _httpHelper.HttpToGetAsync(url, headers).ConfigureAwait(false);
_logger.LogInformation($"{imei} 更新缓存{nameof(UpdatePersonRemarksAsync)},响应:{res}");
var resJToken = JsonConvert.DeserializeObject(res ?? string.Empty) as JToken;
flag = resJToken?["message"]?.ToString().Equals("ok") ?? false;
}
catch (Exception ex)
{
_logger.LogError($"{nameof(UpdatePersonRemarksAsync)} 更新个人信息异常:{ex.Message}, {ex.StackTrace}");
}
return flag;
}
*/

///// <summary>
///// 初次开通更新 gps_person remark和对应的缓存
///// </summary>
///// <param name="imei"></param>
///// <param name="systolicRefValue"></param>
///// <param name="diastolicRefValue"></param>
///// <param name="systolicIncValue"></param>
///// <param name="diastolicIncValue"></param>
///// <returns></returns>
//public async Task<bool> UpdatePersonRemarksAsync0(string imei, int systolicRefValue, int diastolicRefValue,int systolicIncValue,int diastolicIncValue)
//{
// var flag = false;
// try
// {
// GeneralParam condition = new()
// {
// Filters = new List<QueryFilterCondition> {
// new QueryFilterCondition {
// Key=nameof(GpsDevice.Serialno),
// Value=imei,
// Operator= QueryOperatorEnum.Equal,
// ValueType=QueryValueTypeEnum.String
// }
// },
// OrderBys = new List<OrderByCondition> { new OrderByCondition { Key = "serialno", IsDesc = true } }

// };
// var person = await _gpsPersonApiClient.GetFirstAsync(condition, new RequestHeader() { RequestId = $"{imei}" }).ConfigureAwait(false);
// // 若remark为空,更新person remark字段
// if (string.IsNullOrWhiteSpace(person?.Remarks))
// {
// var newRemarkData = new
// {
// imei,
// time = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
// commandValue = new
// {
// systolicCalibrationValue = systolicRefValue, //收缩压标定值,值为0 表示不生效
// diastolicCalibrationValue = diastolicRefValue, //舒张压标定值,值为0表示不生效
// systolicIncValue, //收缩压显示增量,值为0 表示不生效
// diastolicIncValue //舒张压显示增量,值为0 表示不生效
// }
// };
// person!.Remarks = $"is_blood_press:{JsonConvert.SerializeObject(newRemarkData)}|";
// await _gpsPersonApiClient.UpdateAsync(person, new RequestHeader() { RequestId = $"{imei}" }).ConfigureAwait(false);
// _logger.LogInformation($"更新Person remarks字段|{person.Remarks}");

// // 更新缓存
// var personCache = await _personCacheMgr.GetDeviceGpsPersonCacheObjectBySerialNoAsync(new Guid().ToString(), imei).ConfigureAwait(false);

// if (personCache != null)
// {

// //personCache.Person.Remarks = person!.Remarks;
// personCache["person"]!["remarks"] = person!.Remarks;
// bool cacheFlag= await _personCacheMgr.UpdateDeviceGpsPersonCacheObjectBySerialNoAsync(personCache, imei);

// // flag = true;
// if (cacheFlag)
// {
// flag = true;
// _logger.LogInformation($"{imei} 更新缓存{nameof(UpdatePersonRemarksAsync)}成功,{JsonConvert.SerializeObject(personCache)}");
// }
// else
// {
// flag = false;
// _logger.LogInformation($"{imei} 更新缓存{nameof(UpdatePersonRemarksAsync)}失败,{JsonConvert.SerializeObject(personCache)}");
// }
// }


// //var url = $"{_configService.IotWebApiUrl}Device/UpdatePersonInfoCache?imei={imei}";
// //List<KeyValuePair<string, string>> headers = new()
// //{
// // new KeyValuePair<string, string>("AuthKey", "key1")
// //};
// //var res = await _httpHelper.HttpToGetAsync(url, headers).ConfigureAwait(false);
// //_logger.LogInformation($"{imei} 更新缓存{nameof(UpdatePersonRemarksAsync)},响应:{res}");
// //var resJToken = JsonConvert.DeserializeObject(res ?? string.Empty) as JToken;
// //flag = resJToken?["message"]?.ToString().Equals("ok") ?? false;
// }
// }
// catch (Exception ex)
// {
// _logger.LogError($"{nameof(UpdatePersonRemarksAsync)} 更新个人信息异常:{ex.Message}, {ex.StackTrace}");
// }
// return flag;
//}
/**
/// <summary>
/// 更新 gps_person remark缓存和数据库
/// </summary>
/// <param name="imei"></param>
/// <param name="systolicRefValue"></param>
/// <param name="diastolicRefValue"></param>
/// <param name="systolicIncValue"></param>
/// <param name="diastolicIncValue"></param>
/// <param name="isInitRemakers">是否初始化,即清空remakers</param>
/// <returns></returns>
public async Task<bool> UpdatePersonRemarksAsync(string imei, int systolicRefValue, int diastolicRefValue, int systolicIncValue, int diastolicIncValue, bool isInitRemakers=false)
{
var flag = false;
try
{
// 保证实时性,先更新缓存,再更新数据库
var personCache = await _personCacheMgr.GetDeviceGpsPersonCacheObjectBySerialNoAsync(new Guid().ToString(), imei).ConfigureAwait(false);
if (personCache == null)
{
_logger.LogInformation($"{imei} -- Person remarks数据异常,检查缓存和数据库");
}
else if (string.IsNullOrWhiteSpace(personCache["person"]!["remarks"]!.ToString()))
{
var newRemarkData = new
{
imei,
time = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
commandValue = new
{
systolicCalibrationValue = systolicRefValue, //收缩压标定值,值为0 表示不生效
diastolicCalibrationValue = diastolicRefValue, //舒张压标定值,值为0表示不生效
systolicIncValue, //收缩压显示增量,值为0 表示不生效
diastolicIncValue //舒张压显示增量,值为0 表示不生效
}
};
var newRemarkStr = isInitRemakers ? string.Empty:$"is_blood_press:{JsonConvert.SerializeObject(newRemarkData)}|";
personCache["person"]!["remarks"] = newRemarkStr;
bool cacheFlag = await _personCacheMgr.UpdateDeviceGpsPersonCacheObjectBySerialNoAsync(personCache, imei);
if (cacheFlag)
{
GeneralParam condition = new()
{
Filters = new List<QueryFilterCondition> {
new QueryFilterCondition {
Key=nameof(GpsDevice.Serialno),
Value=imei,
Operator= QueryOperatorEnum.Equal,
ValueType=QueryValueTypeEnum.String
}
},
OrderBys = new List<OrderByCondition> { new OrderByCondition { Key = "serialno", IsDesc = true } }

};
_logger.LogInformation($"{imei} 更新缓存{nameof(UpdatePersonRemarksAsync)}成功,{JsonConvert.SerializeObject(personCache)}");
// 读取数据库
var person = await _gpsPersonApiClient.GetFirstAsync(condition, new RequestHeader() { RequestId = $"{imei}" }).ConfigureAwait(false);
// 更新字段
person!.Remarks = newRemarkStr;
await _gpsPersonApiClient.UpdateAsync(person, new RequestHeader() { RequestId = $"{imei}" }).ConfigureAwait(false);
_logger.LogInformation($"{imei} 更新Person remarks字段|{person.Remarks}");

}
else
{
_logger.LogInformation($"{imei} 更新缓存和数据库{nameof(UpdatePersonRemarksAsync)}失败,{JsonConvert.SerializeObject(personCache)}");
}
flag = cacheFlag;
}
}
catch (Exception ex)
{
_logger.LogError($"{nameof(UpdatePersonRemarksAsync)} {imei}--更新个人信息异常:{ex.Message}, {ex.StackTrace}");
}
return flag;
}
*/
/// <summary>
///// 更新 gps_person remark缓存和数据库
///// </summary>
///// <param name="imei"></param>
///// <param name="systolicRefValue"></param>
///// <param name="diastolicRefValue"></param>
///// <param name="systolicIncValue"></param>
///// <param name="diastolicIncValue"></param>
///// <param name="remarks"></param>
///// <returns></returns>
//public async Task<bool> UpdatePersonRemarksAsync(string imei, int systolicRefValue, int diastolicRefValue, int systolicIncValue, int diastolicIncValue, string remarks= "is_blood_press")
//{
// var flag = false;
// try
// {

// // 保证实时性,先更新缓存,再更新数据库
// var personCache = await _personCacheMgr.GetDeviceGpsPersonCacheObjectBySerialNoAsync(new Guid().ToString(), imei).ConfigureAwait(false);

// if (personCache == null)
// {
// _logger.LogInformation($"{imei} -- Person remarks数据异常,检查缓存和数据库");
// }
// else if (string.IsNullOrWhiteSpace(personCache["person"]!["remarks"]!.ToString()))
// {
// var newRemarkData = new
// {
// imei,
// time = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
// commandValue = new
// {
// systolicCalibrationValue = systolicRefValue, //收缩压标定值,值为0 表示不生效
// diastolicCalibrationValue = diastolicRefValue, //舒张压标定值,值为0表示不生效
// systolicIncValue, //收缩压显示增量,值为0 表示不生效
// diastolicIncValue //舒张压显示增量,值为0 表示不生效
// }
// };
// var newRemarkStr = $"{remarks}:{JsonConvert.SerializeObject(newRemarkData)}|";

// personCache["person"]!["remarks"] = newRemarkStr;
// bool cacheFlag = await _personCacheMgr.UpdateDeviceGpsPersonCacheObjectBySerialNoAsync(personCache, imei);
// if (cacheFlag)
// {
// GeneralParam condition = new()
// {
// Filters = new List<QueryFilterCondition> {
// new QueryFilterCondition {
// Key=nameof(GpsDevice.Serialno),
// Value=imei,
// Operator= QueryOperatorEnum.Equal,
// ValueType=QueryValueTypeEnum.String
// }
// },
// OrderBys = new List<OrderByCondition> { new OrderByCondition { Key = "serialno", IsDesc = true } }

// };
// _logger.LogInformation($"{imei} 更新缓存{nameof(UpdatePersonRemarksAsync)}成功,{JsonConvert.SerializeObject(personCache)}");
// // 读取数据库
// var person = await _gpsPersonApiClient.GetFirstAsync(condition, new RequestHeader() { RequestId = $"{imei}" }).ConfigureAwait(false);
// // 更新字段
// person!.Remarks = newRemarkStr;
// await _gpsPersonApiClient.UpdateAsync(person, new RequestHeader() { RequestId = $"{imei}" }).ConfigureAwait(false);
// _logger.LogInformation($"{imei} 更新Person remarks字段|{person.Remarks}");

// }
// else
// {
// _logger.LogInformation($"{imei} 更新缓存和数据库{nameof(UpdatePersonRemarksAsync)}失败,{JsonConvert.SerializeObject(personCache)}");
// }
// flag = cacheFlag;
// }
// }
// catch (Exception ex)
// {
// _logger.LogError($"{nameof(UpdatePersonRemarksAsync)} {imei}--更新个人信息异常:{ex.Message}, {ex.StackTrace}");
// }
// return flag;
//}



/// 更新 gps_person remark缓存和数据库
/// </summary>
/// <param name="imei"></param>
/// <param name="systolicRefValue"></param>
/// <param name="diastolicRefValue"></param>
/// <param name="systolicIncValue"></param>
/// <param name="diastolicIncValue"></param>
/// <param name="remarks"></param>
/// <returns></returns>
public async Task<bool> UpdatePersonRemarksAsync(string imei, int systolicRefValue, int diastolicRefValue, int systolicIncValue, int diastolicIncValue, string remarks = "is_blood_press")
{
var flag = false;
try
{

// 保证实时性,先更新缓存,再更新数据库
var personCache = await _personCacheMgr.GetDeviceGpsPersonCacheObjectBySerialNoAsync(new Guid().ToString(), imei).ConfigureAwait(false);

if (personCache == null)
{
_logger.LogInformation($"{imei} -- Person remarks数据异常,检查缓存和数据库");
}
else
{
var newRemarkData = new
{
imei,
time = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
commandValue = new
{
systolicCalibrationValue = systolicRefValue, //收缩压标定值,值为0 表示不生效
diastolicCalibrationValue = diastolicRefValue, //舒张压标定值,值为0表示不生效
systolicIncValue, //收缩压显示增量,值为0 表示不生效
diastolicIncValue //舒张压显示增量,值为0 表示不生效
}
};
var newRemarkStr = $"{remarks}:{JsonConvert.SerializeObject(newRemarkData)}|";

personCache["person"]!["remarks"] = newRemarkStr;
bool cacheFlag = await _personCacheMgr.UpdateDeviceGpsPersonCacheObjectBySerialNoAsync(personCache, imei);
if (cacheFlag)
{
GeneralParam condition = new()
{
Filters = new List<QueryFilterCondition> {
new QueryFilterCondition {
Key=nameof(GpsDevice.Serialno),
Value=imei,
Operator= QueryOperatorEnum.Equal,
ValueType=QueryValueTypeEnum.String
}
},
OrderBys = new List<OrderByCondition> { new OrderByCondition { Key = "serialno", IsDesc = true } }

};
_logger.LogInformation($"{imei} 更新缓存{nameof(UpdatePersonRemarksAsync)}成功,{JsonConvert.SerializeObject(personCache)}");
// 读取数据库
var person = await _gpsPersonApiClient.GetFirstAsync(condition, new RequestHeader() { RequestId = $"{imei}" }).ConfigureAwait(false);
// 更新字段
person!.Remarks = newRemarkStr;
await _gpsPersonApiClient.UpdateAsync(person, new RequestHeader() { RequestId = $"{imei}" }).ConfigureAwait(false);
_logger.LogInformation($"{imei} 更新Person remarks字段|{person.Remarks}");

}
else
{
_logger.LogInformation($"{imei} 更新缓存和数据库{nameof(UpdatePersonRemarksAsync)}失败,{JsonConvert.SerializeObject(personCache)}");
}
flag = cacheFlag;
}
// else if (string.IsNullOrWhiteSpace(personCache["person"]!["remarks"]!.ToString()))
//else if (personCache?["person"]!["remarks"]!.ToString()!=null)
//{
// var newRemarkData = new
// {
// imei,
// time = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
// commandValue = new
// {
// systolicCalibrationValue = systolicRefValue, //收缩压标定值,值为0 表示不生效
// diastolicCalibrationValue = diastolicRefValue, //舒张压标定值,值为0表示不生效
// systolicIncValue, //收缩压显示增量,值为0 表示不生效
// diastolicIncValue //舒张压显示增量,值为0 表示不生效
// }
// };
// var newRemarkStr = $"{remarks}:{JsonConvert.SerializeObject(newRemarkData)}|";

// personCache["person"]!["remarks"] = newRemarkStr;
// bool cacheFlag = await _personCacheMgr.UpdateDeviceGpsPersonCacheObjectBySerialNoAsync(personCache, imei);
// if (cacheFlag)
// {
// GeneralParam condition = new()
// {
// Filters = new List<QueryFilterCondition> {
// new QueryFilterCondition {
// Key=nameof(GpsDevice.Serialno),
// Value=imei,
// Operator= QueryOperatorEnum.Equal,
// ValueType=QueryValueTypeEnum.String
// }
// },
// OrderBys = new List<OrderByCondition> { new OrderByCondition { Key = "serialno", IsDesc = true } }

// };
// _logger.LogInformation($"{imei} 更新缓存{nameof(UpdatePersonRemarksAsync)}成功,{JsonConvert.SerializeObject(personCache)}");
// // 读取数据库
// var person = await _gpsPersonApiClient.GetFirstAsync(condition, new RequestHeader() { RequestId = $"{imei}" }).ConfigureAwait(false);
// // 更新字段
// person!.Remarks = newRemarkStr;
// await _gpsPersonApiClient.UpdateAsync(person, new RequestHeader() { RequestId = $"{imei}" }).ConfigureAwait(false);
// _logger.LogInformation($"{imei} 更新Person remarks字段|{person.Remarks}");

// }
// else
// {
// _logger.LogInformation($"{imei} 更新缓存和数据库{nameof(UpdatePersonRemarksAsync)}失败,{JsonConvert.SerializeObject(personCache)}");
// }
// flag = cacheFlag;
//}
}
catch (Exception ex)
{
_logger.LogError($"{nameof(UpdatePersonRemarksAsync)} {imei}--更新个人信息异常:{ex.Message}, {ex.StackTrace}");
}
return flag;
}

/** 取消
public async Task<bool> UpdatePersonInfoCacheAsync(string imei)
{
var flag = false;
try
{
var url = $"{_configService.IotWebApiUrl}Device/UpdatePersonInfoCache?imei={imei}";
List<KeyValuePair<string, string>> headers = new()
{
new KeyValuePair<string, string>("AuthKey", "key1")
};
var res = await _httpHelper.HttpToGetAsync(url, headers).ConfigureAwait(false);
_logger.LogInformation($"{imei} 更新缓存{nameof(UpdatePersonInfoCacheAsync)},响应:{res}");
var resJToken = JsonConvert.DeserializeObject(res ?? string.Empty) as JToken;
flag = resJToken?["message"]?.ToString().Equals("ok") ?? false;
}
catch (Exception ex)
{
_logger.LogError($"{nameof(UpdatePersonInfoCacheAsync)} 更新缓存异常:{ex.Message}, {ex.StackTrace}");

}
return flag;


}
*/

}
}

+ 190
- 54
HealthMonitor.Service/Biz/db/TDengineService.cs View File

@@ -637,6 +637,7 @@ namespace HealthMonitor.Service.Biz.db
}

#region SqlSugarClient
/**
public async Task InsertFetalHeartRateAsync()
{
var tableName = typeof(FetalHeartRateModel)
@@ -680,61 +681,9 @@ namespace HealthMonitor.Service.Biz.db
.OrderByDescending(x => x.Timestamp).FirstAsync();
return first;
}
public void InsertFetalHeartRate2()
{
//var insrtSql = "";
//_clientSqlSugar.Ado.ExecuteCommand(insrtSql);
// 通过反射获取STableAttribute的STableName值
var tableName = typeof(FetalHeartRateModel)
.GetCustomAttribute<STableAttribute>()?
.STableName;

if (tableName == null)
{
throw new InvalidOperationException("STableAttribute not found on FetalHeartRateModel class.");
}

_clientSqlSugar.Ado.ExecuteCommand($"create table IF NOT EXISTS hm_fhr_00 using {tableName} tags('00')");

//_clientSqlSugar.Ado.ExecuteCommand($"create table IF NOT EXISTS hm_fhr_00 using `stb_hm_fetal_heart_rate_test` tags('00')");
_clientSqlSugar.Insertable(new FetalHeartRateModel()
{
Timestamp = DateTime.Now,
CreateTime = DateTime.Now,
FetalHeartRate = 90,
FetalHeartRateId = Guid.NewGuid().ToString("D"),
IsDisplay = false,
Method = 1,
PersonId = Guid.NewGuid().ToString("D"),
MessageId = Guid.NewGuid().ToString("D"),
SerialNumber = Guid.NewGuid().ToString("D"),
DeviceKey = Guid.NewGuid().ToString("D"),
SerialTailNumber = "00",
LastUpdate = DateTime.Now,
}).AS("hm_fhr_00").ExecuteCommand();


//List<FetalHeartRateModel> rows = new();

//rows.Add(new FetalHeartRateModel() {
// Timestamp = new DateTime(2024, 1, 1),
// CreateTime = DateTime.Now,
// FetalHeartRate = 90,
// FetalHeartRateId = Guid.NewGuid().ToString("D"),
// IsDisplay = false,
// Method = 1,
// PersonId = Guid.NewGuid().ToString("D"),
// MessageId = Guid.NewGuid().ToString("D"),
// SerialNumber = Guid.NewGuid().ToString("D"),
// DeviceKey = Guid.NewGuid().ToString("D"),
// SerialTailNumber = "00",
// LastUpdate = DateTime.Now,

//});

//_clientSqlSugar.Insertable(rows).AS("hm_fhr_00").ExecuteCommand();
}

*/
/// <summary>
/// 插入记录
/// </summary>
@@ -808,5 +757,192 @@ namespace HealthMonitor.Service.Biz.db
return res;
}
#endregion

#region 胎心算法
/// <summary>
/// 获取孕妇心率众数
/// </summary>
/// <param name="serialNo"></param>
/// <param name="days"></param>
/// <returns></returns>
public async Task<int> GetPregnancyHeartRateModeAsync(string serialNo,int days=7)
{
var tableName = typeof(PregnancyHeartRateModel)
.GetCustomAttribute<STableAttribute>()?
.STableName;

var res = await _clientSqlSugar
.Queryable<PregnancyHeartRateModel>()
.AS(tableName)
.Where(i=>i.SerialNumber.Equals(serialNo))
.Where(i => i.Timestamp > DateTime.Now.AddDays(-days))
//.OrderByDescending(i => i.PregnancyHeartRate)
.Select(i =>i.PregnancyHeartRate)
.ToListAsync();
// 心率数据量必须30个以上才进行计算
if (res.Count < 30) return 0;
// 计算众数
var mode = res.GroupBy(n => n)
.OrderByDescending(g => g.Count())
.First()
.Key;

Console.WriteLine("众数是: " + mode);

// 如果有多个众数的情况
var maxCount = res.GroupBy(n => n)
.Max(g => g.Count());

var modes = res.GroupBy(n => n)
.Where(g => g.Count() == maxCount)
.Select(g => g.Key)
.ToList();
// 多个众数,选择最接近平均数或中位数的众数
if (modes.Count>1)
{
// 计算平均值
double average = res.Average();
Console.WriteLine("平均值是: " + average);

// 计算中位数
double median;
int count = res.Count;
var sortedRes = res.OrderBy(n => n).ToList();
if (count % 2 == 0)
{
// 偶数个元素,取中间两个数的平均值
median = (sortedRes[count / 2 - 1] + sortedRes[count / 2]) / 2.0;
}
else
{
// 奇数个元素,取中间的数
median = sortedRes[count / 2];
}
Console.WriteLine("中位数是: " + median);

// 找出最接近平均值的众数
//var closestToAverage = modes.OrderBy(m => Math.Abs(m - average)).First();
//Console.WriteLine("最接近平均值的众数是: " + closestToAverage);

// 找出最接近中位数的众数
var closestToMedian = modes.OrderBy(m => Math.Abs(m - median)).First();
Console.WriteLine("最接近中位数的众数是: " + closestToMedian);
mode = closestToMedian;
}

return mode;
}
/// <summary>
/// 计算个人一般心率
/// </summary>
/// <param name="serialNo"></param>
/// <param name="days"></param>
/// <param name="percentage"></param>
/// <returns></returns>
public async Task<PregnancyCommonHeartRateModel?> InitPregnancyCommonHeartRateModeAsync(string serialNo, int days = 7,int percentage=90)
{
var tableName = typeof(PregnancyHeartRateModel)
.GetCustomAttribute<STableAttribute>()?
.STableName;

var collection = await _clientSqlSugar
.Queryable<PregnancyHeartRateModel>()
.AS(tableName)
.Where(i => i.SerialNumber.Equals(serialNo))
.Where(i => i.Timestamp > DateTime.Now.AddDays(-days))
.OrderByDescending(i => i.Timestamp)
.ToArrayAsync();

var res = collection
.Select(i => i.PregnancyHeartRate).ToList();
// 心率数据量必须30个以上才进行计算
if (res.Count < 30)
{
_logger.LogInformation($"{serialNo} 心率数据不足,无法计算其众数");
return null;
}

#region 计算众数
var mode = res.GroupBy(n => n)
.OrderByDescending(g => g.Count())
.First()
.Key;

Console.WriteLine("众数是: " + mode);

// 如果有多个众数的情况
var maxCount = res.GroupBy(n => n)
.Max(g => g.Count());

var modes = res.GroupBy(n => n)
.Where(g => g.Count() == maxCount)
.Select(g => g.Key)
.ToList();
// 多个众数,选择最接近平均数或中位数的众数
if (modes.Count > 1)
{
// 计算平均值
double average = res.Average();
Console.WriteLine("平均值是: " + average);

// 计算中位数
double median;
int count = res.Count;
var sortedRes = res.OrderBy(n => n).ToList();
if (count % 2 == 0)
{
// 偶数个元素,取中间两个数的平均值
median = (sortedRes[count / 2 - 1] + sortedRes[count / 2]) / 2.0;
}
else
{
// 奇数个元素,取中间的数
median = sortedRes[count / 2];
}
Console.WriteLine("中位数是: " + median);

// 找出最接近平均值的众数
//var closestToAverage = modes.OrderBy(m => Math.Abs(m - average)).First();
//Console.WriteLine("最接近平均值的众数是: " + closestToAverage);

// 找出最接近中位数的众数
var closestToMedian = modes.OrderBy(m => Math.Abs(m - median)).First();
Console.WriteLine("最接近中位数的众数是: " + closestToMedian);
mode = closestToMedian;
}
#endregion

// 计算需要的数量
int requiredCount = (int)(res.Count * 0.8);

// 从原始数据集中获取最接近众数的元素
var closestToModeData = res.OrderBy(n => Math.Abs(n - mode))
.Take(requiredCount)
.ToList();

// 输出新数据集
Console.WriteLine("新数据集: " + string.Join(", ", closestToModeData));
Console.WriteLine("新数据集的数量: " + closestToModeData.Count);

return new PregnancyCommonHeartRateModel()
{
Timestamp = DateTime.Now,
PersonId = collection.First().DeviceKey,
DeviceKey = collection.First().DeviceKey,
SerialNumber = collection.First().SerialNumber,
Mode = mode,
Percentage= percentage,
MaxValue= closestToModeData.Max(),
MinValue= closestToModeData.Min(),
OriginalMaxValue=res.Max(),
OriginalMinValue= res.Min(),
CreateTime = DateTime.Now,
StatStartTime = collection.OrderBy(i=>i.Timestamp).Select(i=>i.Timestamp).First(),
StatEndTime= collection.OrderBy(i => i.Timestamp).Select(i => i.Timestamp).Last(),
};

}
#endregion
}
}

+ 52
- 0
HealthMonitor.Service/Cache/DeviceCacheManager.cs View File

@@ -0,0 +1,52 @@
using Microsoft.Extensions.Logging;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace HealthMonitor.Service.Cache
{
public class DeviceCacheManager
{
private readonly ILogger<DeviceCacheManager> _logger;
private const string CACHE_KEY_GPSDEVICE_WATCH_CONFIG = "#GPSDEVICE_WATCH_CONFIG_HASH";


public DeviceCacheManager(ILogger<DeviceCacheManager> logger)
{
_logger = logger;
}


/// <summary>
///
/// </summary>
/// <param name="sn"></param>
/// <param name="bizCode">
/// 业务码
/// 0067 胎心启动配置
/// </param>
/// <returns></returns>
public async Task<JObject?> GetGpsDeviceWatchConfigCacheObjectBySerialNoAsync(string sn, string bizCode)
{
if (string.IsNullOrWhiteSpace(sn)) return null;

try
{
var config = await RedisHelperDb7.HGetAsync(CACHE_KEY_GPSDEVICE_WATCH_CONFIG, $"{sn}_{bizCode}").ConfigureAwait(false);
if (config == null) return null;
return (JObject)JsonConvert.DeserializeObject(config)!;
}
catch (Exception ex)
{
_logger.LogWarning($"Redis DB7发生异常:{ex.Message}, {ex.StackTrace}");
}

return null;
}
}
}

+ 2
- 2
HealthMonitor.Service/Resolver/BloodpressResolver.cs View File

@@ -42,7 +42,7 @@ namespace HealthMonitor.Service.Resolver
private readonly HttpHelper _httpHelper = default!;

private readonly GpsCardAccessorClient<GpsPerson> _gpsPersonApiClient;
private readonly IotWebApiService _serviceIotWebApi;
private readonly IotApiService _serviceIotWebApi;

private readonly AsyncLocal<string> _messageId = new();
private readonly AsyncLocal<HisGpsBloodPress> _msgData = new();
@@ -54,7 +54,7 @@ namespace HealthMonitor.Service.Resolver
BloodPressReferenceValueCacheManager bpRefValCacheManager,
PersonCacheManager personCacheMgr, HttpHelper httpHelper,
GpsCardAccessorClient<GpsPerson> gpsPersonApiClient,
IotWebApiService iotWebApiService,
IotApiService iotWebApiService,
EtcdService serviceEtcd,
IOptions<BoodPressResolverConfig> optionBoodPressResolver,
ILogger<BloodpressResolver> logger)


+ 0
- 1
HealthMonitor.Service/Resolver/Factory/ResolverFactory.cs View File

@@ -55,7 +55,6 @@ namespace HealthMonitor.Service.Resolver.Factory
MessageId = msg.MessageId,
Topic = msg.Topic,
DetailData = msg.Body,

};
}
}


+ 134
- 0
HealthMonitor.Service/Resolver/PregnancyHeartRateResolver.cs View File

@@ -0,0 +1,134 @@
using HealthMonitor.Common;
using HealthMonitor.Common.helper;
using HealthMonitor.Service.Etcd;
using HealthMonitor.Service.Resolver.Interface;
using HealthMonitor.Service.Sub;
using HealthMonitor.Service.Sub.Topic.Model;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TelpoDataService.Util.Entities.GpsLocationHistory;

namespace HealthMonitor.Service.Resolver
{
public class PregnancyHeartRateResolver : IResolver
{
private readonly ILogger<PregnancyHeartRateResolver> _logger;
private readonly AsyncLocal<string> _messageId = new();
private readonly AsyncLocal<HisGpsHeartRate> _msgData = new();
private readonly HttpHelper _httpHelper = default!;
private readonly EtcdService _serviceEtcd;


public PregnancyHeartRateResolver(ILogger<PregnancyHeartRateResolver> logger, HttpHelper httpHelper, EtcdService serviceEtcd)
{
_logger = logger;
_httpHelper = httpHelper;
_serviceEtcd = serviceEtcd;
}

public void SetResolveInfo(PackageMsgModel msg)
{
var topicHmPregnancyHeartRate = JsonConvert.DeserializeObject<TopicHmPregnancyHeartRate>(msg.DetailData.ToString()!);
_messageId.Value = msg.MessageId;
_msgData.Value = new HisGpsHeartRate()
{
HeartRateId = topicHmPregnancyHeartRate!.PregnancyHeartRateId,
MessageId = topicHmPregnancyHeartRate!.MessageId,
Serialno = topicHmPregnancyHeartRate!.Serialno,
HeartRate= topicHmPregnancyHeartRate.PregnancyHeartRate,
LastUpdate = DateTimeUtil.GetDateTimeFromUnixTimeMilliseconds(SafeType.SafeInt64(topicHmPregnancyHeartRate.LastUpdate) / 1000000),
CreateTime = DateTimeUtil.GetDateTimeFromUnixTimeMilliseconds(SafeType.SafeInt64(topicHmPregnancyHeartRate.CreateTime) / 1000000),
Method = topicHmPregnancyHeartRate!.Method,
IsDisplay = topicHmPregnancyHeartRate!.IsDisplay ? 1 : 0
};
}
public override string ToString()
{
return $"{nameof(PregnancyHeartRateResolver)}[{_messageId.Value}]";
}

public async Task ExecuteMessageAsync()
{
//throw new NotImplementedException();
var messageId = _messageId.Value;
var phr = _msgData.Value!;
#region 定时下发触发器
var key = $"health_moniter/schedule_push/pregnancy_heart_rate/imei/{phr.Serialno}";
var schedule_push = await _serviceEtcd.GetValAsync(key).ConfigureAwait(false);

if (string.IsNullOrWhiteSpace(schedule_push))
{
// 注册首次下推
#if DEBUG
// await _serviceEtcd.PutValAsync(key, result, 60*1, false).ConfigureAwait(false);

var interval = 0;
// 获取当前时间
DateTime now = DateTime.Now;

// 计算距离下一个$interval天后的8点的时间间隔
DateTime nextRunTime = new DateTime(now.Year, now.Month, now.Day, now.Hour, now.Minute + 1, 58).AddDays(interval);
TimeSpan timeUntilNextRun = nextRunTime - now;

// 如果当前时间已经超过了8点,将等待到明天后的8点
if (timeUntilNextRun < TimeSpan.Zero)
{
timeUntilNextRun = timeUntilNextRun.Add(TimeSpan.FromMinutes(1));
nextRunTime += timeUntilNextRun;
}
var ttl = (long)timeUntilNextRun.TotalSeconds;
var data = new
{
imei = phr.Serialno,
create_time = now.ToString("yyyy-MM-dd HH:mm:ss"),
ttl,
next_run_time = nextRunTime.ToString("yyyy-MM-dd HH:mm:ss")
};
var result = JsonConvert.SerializeObject(data);
await _serviceEtcd.PutValAsync(key, result, ttl, false).ConfigureAwait(false);

#else

var interval = 0;
// 获取当前时间
DateTime now = DateTime.Now;
var rand=new Random();
var pushSec = rand.Next(59);
int pushMin= int.TryParse(phr.Serialno.AsSpan(phr.Serialno.Length - 1), out pushMin) ? pushMin : 10;
// 计算距离下一个$interval天后的8点的时间间隔
DateTime nextRunTime = new DateTime(now.Year, now.Month, now.Day, 18, pushMin, pushSec).AddDays(interval);
TimeSpan timeUntilNextRun = nextRunTime - now;

if (timeUntilNextRun < TimeSpan.Zero)
{
timeUntilNextRun = timeUntilNextRun.Add(TimeSpan.FromDays(1));
nextRunTime += TimeSpan.FromDays(1);
}

var ttl =(long)timeUntilNextRun.TotalSeconds;
var data = new
{
imei = phr.Serialno,
create_time = now.ToString("yyyy-MM-dd HH:mm:ss"),
ttl,
next_run_time = nextRunTime.ToString("yyyy-MM-dd HH:mm:ss")
};
var result = JsonConvert.SerializeObject(data);
await _serviceEtcd.PutValAsync(key, result,ttl, false).ConfigureAwait(false);
#endif

}

#endregion
}



}
}

+ 11
- 1
HealthMonitor.Service/Sub/MsgQueueManager.cs View File

@@ -15,15 +15,19 @@ namespace HealthMonitor.Service.Sub
{
private readonly ILogger<MsgQueueManager> _logger;
private readonly BloodpressResolver _resolverBloodpress;
private readonly PregnancyHeartRateResolver _resolverPregnacyHeartRate;

//private const string BP = nameof(TopicHmBloodPress).ToLower();


public MsgQueueManager(ILogger<MsgQueueManager> logger, BloodpressResolver resolverBloodpress)
public MsgQueueManager(ILogger<MsgQueueManager> logger,
BloodpressResolver resolverBloodpress,
PregnancyHeartRateResolver pregnacyHeartRateResolver)
{
_logger = logger;
_resolverBloodpress = resolverBloodpress;
_resolverPregnacyHeartRate = pregnacyHeartRateResolver;
}

//public IResolver? GetMsgResolver()
@@ -55,6 +59,12 @@ namespace HealthMonitor.Service.Sub
_resolverBloodpress.SetResolveInfo(msg);
return _resolverBloodpress;
}

if (msg.Topic.Equals(nameof(TopicHmPregnancyHeartRate).ToLower()))
{
_resolverPregnacyHeartRate.SetResolveInfo(msg);
return _resolverPregnacyHeartRate;
}
return null;



+ 70
- 31
HealthMonitor.Service/Sub/TDengineDataSubcribe.cs View File

@@ -20,6 +20,7 @@ using System.Threading.Tasks;
using TDengineDriver;
using TDengineTMQ;
using TelpoDataService.Util.Entities.GpsLocationHistory;
using static Microsoft.EntityFrameworkCore.DbLoggerCategory.Database;

namespace HealthMonitor.Service.Sub
{
@@ -69,40 +70,80 @@ namespace HealthMonitor.Service.Sub
DoReceive(conn);
}

public void DoReceive(IntPtr Connection)
public void DoReceive(IntPtr connection)
{
#region topichmbpstats 订阅
#region topic 订阅
// string topic = "topichmbpstats";
string topic = nameof(TopicHmBloodPress).ToLower();
TopicHmBloodPress fields = new();
PropertyInfo[] props = fields.GetType().GetProperties();
// 获取 fields
string attributes = "";
foreach (PropertyInfo prop in props)
//string bloodPressTopic = nameof(TopicHmBloodPress).ToLower();
//TopicHmBloodPress fields = new();
//PropertyInfo[] props = fields.GetType().GetProperties();
//// 获取 fields
//string attributes = "";
//foreach (PropertyInfo prop in props)
//{
// JsonPropertyAttribute attr = prop.GetCustomAttribute<JsonPropertyAttribute>()!;
// if (attr != null)
// {
// attributes += attr.PropertyName + ",";
// }
//}
//attributes = attributes.TrimEnd(',');

////创建 topichmbpstats
//IntPtr res = TDengine.Query(Connection, $"create topic if not exists {bloodPressTopic} as select {attributes} from health_monitor.stb_hm_bloodpress");

////创建 topichmpregnancyheartrate
//var pregnancyHeartRateTopic = nameof(TopicHmPregnancyHeartRate).ToLower();
//var pregnancyHeartateAttributes = typeof(TopicHmPregnancyHeartRate).GetType().GetProperties().Select(prop => prop.GetCustomAttribute<JsonPropertyAttribute>());
//var pregnancyHeartateAttributesStr = string.Join(", ", pregnancyHeartateAttributes);
//res = TDengine.Query(Connection, $"create topic if not exists {pregnancyHeartRateTopic} as select {pregnancyHeartateAttributesStr} from {_configTDengineService.DB}.stb_hm_pregnancy_heart_rate");

//if (TDengine.ErrorNo(res) != 0)
//{
// _logger.LogError($"create topic failed, reason:{TDengine.Error(res)}");
// throw new Exception($"create topic failed, reason:{TDengine.Error(res)}");
//}

// 获取字段属性
string GetAttributes(Type type)
{
JsonPropertyAttribute attr = prop.GetCustomAttribute<JsonPropertyAttribute>()!;
if (attr != null)
var props = type.GetProperties();
var attributeNames = props
.Select(prop => prop.GetCustomAttribute<JsonPropertyAttribute>()?.PropertyName)
.Where(attr => !string.IsNullOrEmpty(attr));

return string.Join(",", attributeNames);
}

// 创建 topic
void CreateTopic(IntPtr conn, string topicName, string attributes, string tableName)
{
string query = $"create topic if not exists {topicName} as select {attributes} from {tableName}";
IntPtr res = TDengine.Query(conn, query);

if (TDengine.ErrorNo(res) != 0)
{
attributes += attr.PropertyName + ",";
string error = TDengine.Error(res);
_logger.LogError($"Create topic {topicName} failed, reason: {error}");
throw new Exception($"Create topic {topicName} failed, reason: {error}");
}
}
attributes = attributes.TrimEnd(',');

//创建 topichmbpstats
IntPtr res = TDengine.Query(Connection, $"create topic if not exists {topic} as select {attributes} from health_monitor.stb_hm_bloodpress");
// 血压 topic
string bloodPressTopic = nameof(TopicHmBloodPress).ToLower();
string bloodPressAttributes = GetAttributes(typeof(TopicHmBloodPress));
CreateTopic(connection, bloodPressTopic, bloodPressAttributes, $"{_configTDengineService.DB}.stb_hm_bloodpress");

// 孕期心率 topic
string pregnancyHeartRateTopic = nameof(TopicHmPregnancyHeartRate).ToLower();
string pregnancyHeartRateAttributes = GetAttributes(typeof(TopicHmPregnancyHeartRate));
CreateTopic(connection, pregnancyHeartRateTopic, pregnancyHeartRateAttributes, $"{_configTDengineService.DB}.stb_hm_pregnancy_heart_rate");


//创建 topichmfetalheartrate
//var fetalHeartRateTopic = "topichmfetalheartrate";
//var fetalHeartRateFields = "ts,fetal_heart_rate_id,message_id,person_id,serialno,fetal_heart_rate,create_time,method,last_update,is_display,device_key";
//res = TDengine.Query(Connection, $"create topic if not exists {fetalHeartRateTopic} as select {fetalHeartRateFields} from health_monitor.stb_hm_fetal_heart_rate_test");
#endregion


if (TDengine.ErrorNo(res) != 0)
{
_logger.LogError($"create topic failed, reason:{TDengine.Error(res)}");
throw new Exception($"create topic failed, reason:{TDengine.Error(res)}");
}
var cfg = new ConsumerConfig
{
GourpId = "group_1",
@@ -112,12 +153,13 @@ namespace HealthMonitor.Service.Sub
TDConnectIp = _configTDengineService.Host,
};
// create consumer
var consumer = new ConsumerBuilder(cfg)
.Build();
var consumer = new ConsumerBuilder(cfg).Build();

var topics = new string[] { bloodPressTopic, pregnancyHeartRateTopic };
// subscribe
consumer.Subscribe(topic);
consumer.Subscribe(topics);
while (!_tokenSource!.IsCancellationRequested)
{
var consumeRes = consumer.Consume(300);
@@ -154,14 +196,11 @@ namespace HealthMonitor.Service.Sub
}
}
consumer.Commit(consumeRes);
//_logger.LogInformation("监听中....");
// Console.WriteLine($"{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff},监听中....");
}

// close consumer after use.Otherwise will lead memory leak.
consumer.Close();
TDengine.Close(Connection);
TDengine.Close(connection);
}

public void ParsePackage(ReceiveMessageModel model)


+ 44
- 0
HealthMonitor.Service/Sub/Topic/Model/TopicHmPregnancyHeartRate.cs View File

@@ -0,0 +1,44 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace HealthMonitor.Service.Sub.Topic.Model
{
public class TopicHmPregnancyHeartRate
{
[JsonProperty("ts")]
public long Ts { get; set; } = default!;
[JsonProperty("pregnancy_heart_rate_id")]
public string PregnancyHeartRateId { get; set; } = default!;

[JsonProperty("message_id")]
public string MessageId { get; set; } = default!;

[JsonProperty("person_id")]
public string PersonId { get; set; } = default!;

[JsonProperty("serialno")]
public string Serialno { get; set; } = default!;

[JsonProperty("pregnancy_heart_rate")]
public int PregnancyHeartRate { get; set; }

[JsonProperty("last_update")]
public long LastUpdate { get; set; }

[JsonProperty("create_time")]
public long CreateTime { get; set; }

[JsonProperty("method")]
public int Method { get; set; }

[JsonProperty("is_display")]
public bool IsDisplay { get; set; }

[JsonProperty("device_key")]
public string DeviceKey { get; set; } = default!;
}
}

+ 2
- 2
HealthMonitor.WebApi/Controllers/HealthMonitor/HmBloodPressConfigManualCalibrationController.cs View File

@@ -46,7 +46,7 @@ namespace HealthMonitor.WebApi.Controllers.HealthMonitor
private readonly TDengineService _serviceTDengine;
private readonly HttpHelper _httpHelper = default!;

private readonly IotWebApiService _serviceIotWebApi;
private readonly IotApiService _serviceIotWebApi;

private readonly GpsCardAccessorClient<GpsDevice> _deviceApiClient;
private readonly GpsCardAccessorClient<GpsPerson> _gpsPersonApiClient;
@@ -58,7 +58,7 @@ namespace HealthMonitor.WebApi.Controllers.HealthMonitor
GpsCardAccessorClient<GpsPerson> gpsPersonApiClient,
GpsCardAccessorClient<GpsDevice> deviceApiClient,
ILogger<HmBloodPressConfigManualCalibrationController> logger,
IotWebApiService iotWebApiService
IotApiService iotWebApiService
)
{



+ 18
- 1
HealthMonitor.WebApi/Controllers/HealthMonitor/HmBloodPressController.cs View File

@@ -97,7 +97,24 @@ namespace HealthMonitor.WebApi.Controllers.HealthMonitor
//await _serviceTDengine.InsertAsync<FetalHeartRateModel>("hm_fhr", test);
//var first = _serviceTDengine.GetFirst();

var first = await _serviceTDengine.GetBySerialNoAsync<FetalHeartRateModel>("864144050568123");
var test2 = new PregnancyHeartRateModel()
{
Timestamp = DateTime.Now,
CreateTime = DateTime.Now,
PregnancyHeartRate = 120,
PregnancyHeartRateId= Guid.NewGuid().ToString("D"),
IsDisplay = false,
Method = 1,
PersonId = Guid.NewGuid().ToString("D"),
MessageId = Guid.NewGuid().ToString("D"),
SerialNumber = "864144050568123",
DeviceKey = Guid.NewGuid().ToString("D"),
SerialTailNumber = "23",
LastUpdate = DateTime.Now,
};
await _serviceTDengine.InsertAsync<PregnancyHeartRateModel>("hm_phr", test2);

var first = await _serviceTDengine.GetBySerialNoAsync<PregnancyHeartRateModel>("864144050568123");
return Ok(first);
}
}


+ 4
- 2
HealthMonitor.WebApi/Program.cs View File

@@ -93,7 +93,7 @@ namespace HealthMonitor.WebApi
var serverVersion = ServerVersion.AutoDetect(mySqlCon);
options.UseMySql(mySqlCon, serverVersion)
.UseLoggerFactory(loggerFactory);
}, poolSize: 64); ;
}, poolSize: 64);

builder.Services
.AddScoped<IGpsCardDataAccessor, EfCoreDataAccessor>(sp =>
@@ -172,6 +172,7 @@ namespace HealthMonitor.WebApi
#region Cache
builder.Services
.AddSingleton<PersonCacheManager>()
.AddSingleton<DeviceCacheManager>()
.AddSingleton<BloodPressReferenceValueCacheManager>();

#endregion
@@ -189,11 +190,12 @@ namespace HealthMonitor.WebApi
builder.Services.AddSingleton<MsgQueueManager>();
builder.Services.AddSingleton<IResolverFactory, ResolverFactory>();
builder.Services.AddSingleton<BloodpressResolver>();
builder.Services.AddSingleton<PregnancyHeartRateResolver>();
builder.Services.AddSingleton<PackageProcess>();

builder.Services
.AddSingleton<TDengineDataSubcribe>()
.AddSingleton<IotWebApiService>()
.AddSingleton<IotApiService>()
.AddSingleton<EtcdService>()
.AddHostedService<Worker>();
#endregion


+ 337
- 294
HealthMonitor.WebApi/Worker.cs View File

@@ -33,19 +33,19 @@ namespace HealthMonitor.WebApi
private readonly TDengineService _serviceTDengine;
private readonly EtcdService _serviceEtcd;
private readonly HttpHelper _httpHelper = default!;
private readonly IotWebApiService _serviceIotWebApi;
private readonly IotApiService _serviceIotApi;
private readonly BoodPressResolverConfig _configBoodPressResolver;
private readonly BloodPressReferenceValueCacheManager _bpRefValCacheManager;
private readonly PersonCacheManager _personCacheMgr;

private CancellationTokenSource _tokenSource = default!;

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)
public Worker(ILogger<Worker> logger, IServiceProvider services, PersonCacheManager personCacheMgr, BloodPressReferenceValueCacheManager bpRefValCacheManager, IotApiService IotApiService, IOptions<BoodPressResolverConfig> optionBoodPressResolver, PackageProcess processor, TDengineDataSubcribe tdEngineDataSubcribe, TDengineService serviceDengine, HttpHelper httpHelper, EtcdService serviceEtcd)
{
_logger = logger;
_tdEngineDataSubcribe = tdEngineDataSubcribe;
_services = services;
_serviceIotWebApi = iotWebApiService;
_serviceIotApi = IotApiService;
_processor = processor;
_serviceEtcd = serviceEtcd;
_serviceTDengine = serviceDengine;
@@ -116,283 +116,382 @@ namespace HealthMonitor.WebApi

case "Delete":
// TTL到了重新计算TTL,下发
Console.BackgroundColor = ConsoleColor.Green;
Console.WriteLine($"--- {e.Type}--{e.Kv.Key.ToStringUtf8()}--{e.Kv.Value.ToStringUtf8()}---{DateTime.Now}");
//Console.BackgroundColor = ConsoleColor.Green;
//Console.WriteLine($"--- {e.Type}--{e.Kv.Key.ToStringUtf8()}--{e.Kv.Value.ToStringUtf8()}---{DateTime.Now}");

// var key = $"health_moniter/schedule_push/imei/{bp.Serialno}";
var key = e.Kv.Key.ToStringUtf8();
var imeiDel = key.Split('/')[3];
var imeiDel = key.Split('/')[^1];
var schedule_push = await _serviceEtcd.GetValAsync(key).ConfigureAwait(false);
if (string.IsNullOrWhiteSpace(schedule_push))
{
int systolicInc;
int diastolicInc;
if (key.Contains("pregnancy_heart_rate"))
{
// 处理孕妇业务,计算一般心率并下发
var commonPHR= await _serviceTDengine.InitPregnancyCommonHeartRateModeAsync(imeiDel);
// 设置胎心监测参数

int systolicRefValue;
int diastolicRefValue;
if (commonPHR == null)
{
// 建模中
var flag= await _serviceIotApi.SetFetalHeartRateConfig(imeiDel);
_logger.LogInformation($"{imeiDel} 建模建模中");
}
else
{
// 建模完成
var flag = await _serviceIotApi.SetFetalHeartRateConfig(imeiDel,1,commonPHR.MaxValue,commonPHR.MinValue);
_logger.LogInformation($"{imeiDel} 建模完成");
// 保存到TDengine数据库
await _serviceTDengine.InsertAsync<PregnancyCommonHeartRateModel>("hm_pchr", commonPHR);
_logger.LogInformation($"保存TDengine完成");
}

decimal systolicAvg;
decimal diastolicAvg;
#region 注册定时下发
var startTime = DateTime.Now;
// 注册下次下推
var endTime = DateTime.Now;

int systolicMax = 0;
int diastolicMax = 0;
#if DEBUG

// 统计时间
//DateTime endTime = DateTime.Now; //测试
DateTime statStartTime = DateTime.Now;

//long ttl = (long)((60 * 1000-(endTime-startTime).TotalMilliseconds)/1000);
//await _serviceEtcd.PutValAsync(key, imeiDel,ttl, false).ConfigureAwait(false);

// 最小值
int systolicMin = 0;
int diastolicMin = 0;
var interval = 0;
// 获取当前时间
DateTime now = DateTime.Now;

// 偏移参数
var avgOffset = 0.25M;
var systolicAvgOffset = avgOffset;
var diastolicAvgOffset = avgOffset;
// 计算距离下一个$interval天后的8点的时间间隔
DateTime nextRunTime = new DateTime(now.Year, now.Month, now.Day, now.Hour, now.Minute + 2, 0).AddDays(interval);
TimeSpan timeUntilNextRun = nextRunTime - now;

// 最后一次下发值
int lastPushSystolicInc = 0;
int lastPushDiastolicInc = 0;
// 如果当前时间已经超过了8点,将等待到明天后的8点
if (timeUntilNextRun < TimeSpan.Zero)
{
timeUntilNextRun = timeUntilNextRun.Add(TimeSpan.FromMinutes(1));
nextRunTime += timeUntilNextRun;
}

//long ttl = timeUntilNextRun.Milliseconds/1000;
long ttl = (long)((timeUntilNextRun.TotalMilliseconds - (endTime - startTime).TotalMilliseconds) / 1000);
var data = new
{
imei = imeiDel,
create_time = now.ToString("yyyy-MM-dd HH:mm:ss"),
ttl,
next_run_time = nextRunTime.ToString("yyyy-MM-dd HH:mm:ss")
};
var result = JsonConvert.SerializeObject(data);

await _serviceEtcd.PutValAsync(key, result, ttl, false).ConfigureAwait(false);

var startTime = DateTime.Now;
// 下发增量值
#region 统计定时下发增量值
//var last = await _serviceTDengine.GetLastAsync("stb_hm_bloodpress_stats_inc", $"serialno='{imeiDel}' order by last_update desc");
//var ts = last?[0];

// 最后一条血压数据
var condition = $"serialno='{imeiDel}' order by last_update desc";
var field = "last_row(*)";
var lastHmBpResponse = await _serviceTDengine.ExecuteSelectRestResponseAsync("stb_hm_bloodpress", condition, field);
var lastHmBpParser = JsonConvert.DeserializeObject<ParseTDengineRestResponse<BloodPressureModel>>(lastHmBpResponse!);
var lastHmBp = lastHmBpParser?.Select().FirstOrDefault();
//if (lastHmBpParser?.Select()?.ToList().Count < 2)
//{
// _logger.LogInformation($"{imeiDel} -- {nameof(Worker)} --{nameof(WatchEvents)} -- 血压数据条目不足");
// break;
//}
#else
// 每$interval天,晚上8点
var interval = 1;
// 获取当前时间
DateTime now = DateTime.Now;
var rand = new Random();

// 7 天有效数据
if (lastHmBp?.Timestamp.AddDays(7) > DateTime.Now)
var pushSec = rand.Next(59);
int pushMin = int.TryParse(imeiDel.AsSpan(imeiDel.Length - 1), out pushMin) ? pushMin : 10;
// 计算距离下一个$interval天后的8点的时间间隔
DateTime nextRunTime = new DateTime(now.Year, now.Month, now.Day, 18, pushMin, pushSec).AddDays(interval);
TimeSpan timeUntilNextRun = nextRunTime - now;

// 如果当前时间已经超过了8点,将等待到明天后的8点
if (timeUntilNextRun < TimeSpan.Zero)
{
timeUntilNextRun = timeUntilNextRun.Add(TimeSpan.FromDays(1));
nextRunTime += timeUntilNextRun;
}

// var ttl = timeUntilNextRun.TotalMilliseconds;
long ttl = (long)((timeUntilNextRun.TotalMilliseconds-(endTime-startTime).TotalMilliseconds)/1000);
var data = new
{
imei = imeiDel,
create_time = now.ToString("yyyy-MM-dd HH:mm:ss"),
ttl,
next_run_time = nextRunTime.ToString("yyyy-MM-dd HH:mm:ss")
};
var result = JsonConvert.SerializeObject(data);
await _serviceEtcd.PutValAsync(key, result, ttl, false).ConfigureAwait(false);
#endif
#endregion
}
else
{
// 计算增量值
condition = $"serialno='{imeiDel}' order by ts desc";
var lastPushResponse = await _serviceTDengine.ExecuteSelectRestResponseAsync("stb_hm_bp_push_ref_inc_value", condition, field);
if (lastPushResponse == null)
// 处理血压业务
int systolicInc;
int diastolicInc;

int systolicRefValue;
int diastolicRefValue;

decimal systolicAvg;
decimal diastolicAvg;

int systolicMax = 0;
int diastolicMax = 0;

// 统计时间
//DateTime endTime = DateTime.Now; //测试
DateTime statStartTime = DateTime.Now;


// 最小值
int systolicMin = 0;
int diastolicMin = 0;

// 偏移参数
var avgOffset = 0.25M;
var systolicAvgOffset = avgOffset;
var diastolicAvgOffset = avgOffset;

// 最后一次下发值
int lastPushSystolicInc = 0;
int lastPushDiastolicInc = 0;


var startTime = DateTime.Now;
// 下发增量值
#region 统计定时下发增量值
//var last = await _serviceTDengine.GetLastAsync("stb_hm_bloodpress_stats_inc", $"serialno='{imeiDel}' order by last_update desc");
//var ts = last?[0];

// 最后一条血压数据
var condition = $"serialno='{imeiDel}' order by last_update desc";
var field = "last_row(*)";
var lastHmBpResponse = await _serviceTDengine.ExecuteSelectRestResponseAsync("stb_hm_bloodpress", condition, field);
var lastHmBpParser = JsonConvert.DeserializeObject<ParseTDengineRestResponse<BloodPressureModel>>(lastHmBpResponse!);
var lastHmBp = lastHmBpParser?.Select().FirstOrDefault();
//if (lastHmBpParser?.Select()?.ToList().Count < 2)
//{
// _logger.LogInformation($"{imeiDel} -- {nameof(Worker)} --{nameof(WatchEvents)} -- 血压数据条目不足");
// break;
//}

// 7 天有效数据
if (lastHmBp?.Timestamp.AddDays(7) > DateTime.Now)
{
_logger.LogInformation($"{imeiDel}--没有下发记录");
break;
}
var lastPushParser = JsonConvert.DeserializeObject<ParseTDengineRestResponse<BloodPressurePushRefIncModel>>(lastPushResponse);
var lastPush = lastPushParser!.Select().FirstOrDefault();
// 有下推记录
if (lastPush != null)
{
systolicRefValue = lastPush!.SystolicRefValue;
diastolicRefValue = lastPush!.DiastolicRefValue;
lastPushSystolicInc = lastPush!.SystolicIncValue;
lastPushDiastolicInc = lastPush!.DiastolicIncValue;
condition = $"ts between '{lastPush?.Timestamp:yyyy-MM-dd HH:mm:ss.fff}' and '{startTime:yyyy-MM-dd HH:mm:ss.fff}' " +
$"and serialno='{imeiDel}' " +
$"and is_display = true";
// 使用最近一次的下推时间作为统计的开始时间
statStartTime = lastPush!.Timestamp;
}
// 没有下推记录(历史遗留数据),没有初始的测量值产生的平均值(测量值=平均值)
else
{
#region 获取个人信息

var person = await _personCacheMgr.GetDeviceGpsPersonCacheBySerialNoAsync(Guid.NewGuid().ToString(), imeiDel).ConfigureAwait(false);
//验证这个信息是否存在
if (person == null || person?.Person.BornDate == null)
// 计算增量值
condition = $"serialno='{imeiDel}' order by ts desc";
var lastPushResponse = await _serviceTDengine.ExecuteSelectRestResponseAsync("stb_hm_bp_push_ref_inc_value", condition, field);
if (lastPushResponse == null)
{
_logger.LogWarning($"{nameof(Worker)}--{imeiDel} 验证个人信息,找不到个人信息,跳过此消息");
_logger.LogInformation($"{imeiDel}--没有下发记录");
break;
}
// 验证年龄是否在范围 (2 - 120)
var age = SafeType.SafeInt(DateTime.Today.Year - person?.Person.BornDate!.Value.Year!);
if (age < 2 || age > 120)
var lastPushParser = JsonConvert.DeserializeObject<ParseTDengineRestResponse<BloodPressurePushRefIncModel>>(lastPushResponse);
var lastPush = lastPushParser!.Select().FirstOrDefault();
// 有下推记录
if (lastPush != null)
{
_logger.LogWarning($"{nameof(Worker)}--{imeiDel} 验证年龄,不在范围 (2 - 120)岁,跳过此消息");
break;
systolicRefValue = lastPush!.SystolicRefValue;
diastolicRefValue = lastPush!.DiastolicRefValue;
lastPushSystolicInc = lastPush!.SystolicIncValue;
lastPushDiastolicInc = lastPush!.DiastolicIncValue;
condition = $"ts between '{lastPush?.Timestamp:yyyy-MM-dd HH:mm:ss.fff}' and '{startTime:yyyy-MM-dd HH:mm:ss.fff}' " +
$"and serialno='{imeiDel}' " +
$"and is_display = true";
// 使用最近一次的下推时间作为统计的开始时间
statStartTime = lastPush!.Timestamp;
}
// 没有下推记录(历史遗留数据),没有初始的测量值产生的平均值(测量值=平均值)
else
{
#region 获取个人信息

var gender = person?.Person.Gender == true ? 1 : 2;
var isHypertension = SafeType.SafeBool(person?.Person.Ishypertension!);
var height = SafeType.SafeDouble(person?.Person.Height!);
var weight = SafeType.SafeDouble(person?.Person.Weight!);

#endregion

#region 初始化常规血压标定值标定值
var bpRef = await _bpRefValCacheManager.GetBloodPressReferenceValueAsync(age, gender, isHypertension);
//systolicRefValue = bpRef!.Systolic;//?
//diastolicRefValue = bpRef!.Diastolic;//?
#endregion

systolicRefValue = bpRef!.Systolic;
diastolicRefValue = bpRef!.Diastolic;
lastPushSystolicInc = 0;
lastPushDiastolicInc = 0;
condition = $"ts <= '{startTime:yyyy-MM-dd HH:mm:ss.fff}' " +
$"and serialno='{imeiDel}' " +
$"and is_display = true";
}
var person = await _personCacheMgr.GetDeviceGpsPersonCacheBySerialNoAsync(Guid.NewGuid().ToString(), imeiDel).ConfigureAwait(false);
//验证这个信息是否存在
if (person == null || person?.Person.BornDate == null)
{
_logger.LogWarning($"{nameof(Worker)}--{imeiDel} 验证个人信息,找不到个人信息,跳过此消息");
break;
}
// 验证年龄是否在范围 (2 - 120)
var age = SafeType.SafeInt(DateTime.Today.Year - person?.Person.BornDate!.Value.Year!);
if (age < 2 || age > 120)
{
_logger.LogWarning($"{nameof(Worker)}--{imeiDel} 验证年龄,不在范围 (2 - 120)岁,跳过此消息");
break;
}

var gender = person?.Person.Gender == true ? 1 : 2;
var isHypertension = SafeType.SafeBool(person?.Person.Ishypertension!);
var height = SafeType.SafeDouble(person?.Person.Height!);
var weight = SafeType.SafeDouble(person?.Person.Weight!);

#endregion

var hmBpResponse = await _serviceTDengine.ExecuteSelectRestResponseAsync("stb_hm_bloodpress", condition);
var hmBpParser = JsonConvert.DeserializeObject<ParseTDengineRestResponse<BloodPressureModel>>(hmBpResponse!);
var hmBp = hmBpParser?.Select();
//if (hmBp?.ToList().Count < 2)
// 1. 判断数据样本数量
if (hmBpParser!.Rows < 5)
{
_logger.LogInformation($"{imeiDel} -- {nameof(Worker)} --{nameof(WatchEvents)} -- 统计定时下发,计算增量值的数据条目不足:{hmBpParser!.Rows} < 5");
_logger.LogInformation($"{imeiDel} -- {nameof(Worker)} --{nameof(WatchEvents)} 没有足够的数据样本,不会定时下发");
break;
}
// 没有下推记录重新计算统计时间
if (lastPush == null)
{
var firstHmBp = hmBpParser?.Select(i => i).OrderBy(i => i.Timestamp).FirstOrDefault();
statStartTime = firstHmBp!.Timestamp;
}
#region 初始化常规血压标定值标定值
var bpRef = await _bpRefValCacheManager.GetBloodPressReferenceValueAsync(age, gender, isHypertension);
//systolicRefValue = bpRef!.Systolic;//?
//diastolicRefValue = bpRef!.Diastolic;//?
#endregion

// NewMethod(systolicRefValue, hmBpParser);
systolicRefValue = bpRef!.Systolic;
diastolicRefValue = bpRef!.Diastolic;
lastPushSystolicInc = 0;
lastPushDiastolicInc = 0;
condition = $"ts <= '{startTime:yyyy-MM-dd HH:mm:ss.fff}' " +
$"and serialno='{imeiDel}' " +
$"and is_display = true";
}

// 最大值
//systolicMax = (int)hmBpParser?.Select(i => i.SystolicValue).Max()!;
//diastolicMax = (int)hmBpParser?.Select(i => i.DiastolicValue).Max()!;
//// 最小值
//systolicMin = (int)hmBpParser?.Select(i => i.SystolicValue).Min()!;
//diastolicMin = (int)hmBpParser?.Select(i => i.DiastolicValue).Min()!;

//systolicAvg = (int)(hmBpParser?.AverageAfterRemovingOneMinMaxRef(i => i.SystolicValue, SafeType.SafeInt(systolicRefValue!)))!;
//diastolicAvg = (int)(hmBpParser?.AverageAfterRemovingOneMinMaxRef(i => i.DiastolicValue, SafeType.SafeInt(diastolicRefValue!)))!;

var avgs = _serviceTDengine.AverageAfterRemovingOneMinMaxRef(hmBpParser!);
systolicAvg = avgs[0];
diastolicAvg = avgs[1];
var hmBpResponse = await _serviceTDengine.ExecuteSelectRestResponseAsync("stb_hm_bloodpress", condition);
var hmBpParser = JsonConvert.DeserializeObject<ParseTDengineRestResponse<BloodPressureModel>>(hmBpResponse!);
var hmBp = hmBpParser?.Select();
//if (hmBp?.ToList().Count < 2)
// 1. 判断数据样本数量
if (hmBpParser!.Rows < 5)
{
_logger.LogInformation($"{imeiDel} -- {nameof(Worker)} --{nameof(WatchEvents)} -- 统计定时下发,计算增量值的数据条目不足:{hmBpParser!.Rows} < 5");
_logger.LogInformation($"{imeiDel} -- {nameof(Worker)} --{nameof(WatchEvents)} 没有足够的数据样本,不会定时下发");
break;
}
// 没有下推记录重新计算统计时间
if (lastPush == null)
{
var firstHmBp = hmBpParser?.Select(i => i).OrderBy(i => i.Timestamp).FirstOrDefault();
statStartTime = firstHmBp!.Timestamp;
}

// NewMethod(systolicRefValue, hmBpParser);

// 2. 判断能否计算增量值
if (systolicAvg.Equals(0))
{
_logger.LogInformation($"{imeiDel}--{nameof(Worker)}--计算平均值" +
$"\n currentSystolicAvg:{systolicAvg} -- lastPushSystolicInc:{lastPushSystolicInc}" +
$"\n currentDiastolicInc:{diastolicAvg} -- lastPushDiastolicInc:{lastPushDiastolicInc}");
_logger.LogInformation($"{imeiDel}--{nameof(Worker)} 没有足够的数据样本计算平均值,不会定时下发");
break;
}
// 除最大值和最小值后的平均值与标定值差值少于4后(当天计算出该结果则也不产生增量调整),就不再进行增量值调整了。
if (systolicRefValue - systolicAvg < 4)
{
_logger.LogInformation($"diastolic 舒张压 {imeiDel}除最大值和最小值后的平均值:{diastolicAvg}与标定值:{diastolicRefValue}\n systolic 收缩压 {imeiDel}除最大值和最小值后的平均值:{systolicAvg}与标定值:{systolicRefValue}的差值(标定值-平均值)少于4后,systolic 收缩压 不再进行增量值调整");
break;
}
// 最大值
//systolicMax = (int)hmBpParser?.Select(i => i.SystolicValue).Max()!;
//diastolicMax = (int)hmBpParser?.Select(i => i.DiastolicValue).Max()!;
//// 最小值
//systolicMin = (int)hmBpParser?.Select(i => i.SystolicValue).Min()!;
//diastolicMin = (int)hmBpParser?.Select(i => i.DiastolicValue).Min()!;

if (diastolicRefValue - diastolicAvg < 4)
{
_logger.LogInformation($"systolic 收缩压 {imeiDel}除最大值和最小值后的平均值:{systolicAvg}与标定值:{systolicRefValue}\n diastolic 舒张压 {imeiDel}除最大值和最小值后的平均值:{diastolicAvg}与标定值:{diastolicRefValue}的差值(标定值-平均值)少于4后,diastolic 舒张压 不再进行增量值调整");
break;
}
//systolicAvg = (int)(hmBpParser?.AverageAfterRemovingOneMinMaxRef(i => i.SystolicValue, SafeType.SafeInt(systolicRefValue!)))!;
//diastolicAvg = (int)(hmBpParser?.AverageAfterRemovingOneMinMaxRef(i => i.DiastolicValue, SafeType.SafeInt(diastolicRefValue!)))!;

// 增量值=(标定值-平均值)* 0.25
var currentSystolicInc = (int)((systolicRefValue - systolicAvg) * systolicAvgOffset)!;
var currentDiastolicInc = (int)((diastolicRefValue - diastolicAvg) * diastolicAvgOffset)!;
var avgs = _serviceTDengine.AverageAfterRemovingOneMinMaxRef(hmBpParser!);
systolicAvg = avgs[0];
diastolicAvg = avgs[1];


// 累计增量
systolicInc = currentSystolicInc + lastPushSystolicInc;
diastolicInc = currentDiastolicInc + lastPushDiastolicInc;
// 2. 判断能否计算增量值
if (systolicAvg.Equals(0))
{
_logger.LogInformation($"{imeiDel}--{nameof(Worker)}--计算平均值" +
$"\n currentSystolicAvg:{systolicAvg} -- lastPushSystolicInc:{lastPushSystolicInc}" +
$"\n currentDiastolicInc:{diastolicAvg} -- lastPushDiastolicInc:{lastPushDiastolicInc}");
_logger.LogInformation($"{imeiDel}--{nameof(Worker)} 没有足够的数据样本计算平均值,不会定时下发");
break;
}
// 除最大值和最小值后的平均值与标定值差值少于4后(当天计算出该结果则也不产生增量调整),就不再进行增量值调整了。
if (systolicRefValue - systolicAvg < 4)
{
_logger.LogInformation($"diastolic 舒张压 {imeiDel}除最大值和最小值后的平均值:{diastolicAvg}与标定值:{diastolicRefValue}\n systolic 收缩压 {imeiDel}除最大值和最小值后的平均值:{systolicAvg}与标定值:{systolicRefValue}的差值(标定值-平均值)少于4后,systolic 收缩压 不再进行增量值调整");
break;
}

_logger.LogInformation($"{imeiDel}--{nameof(Worker)}--计算增量值" +
$"\n {imeiDel} -- systolicAvg:{systolicAvg}-- systolicInc:{systolicInc}-- currentSystolicInc:{currentSystolicInc} -- lastPushSystolicInc:{lastPushSystolicInc}" +
$"\n {imeiDel} -- diastolicAvg:{diastolicAvg}-- diastolicInc:{diastolicInc} --currentDiastolicInc:{currentDiastolicInc} -- lastPushDiastolicInc:{lastPushDiastolicInc}");
_logger.LogInformation($"{imeiDel}--{nameof(Worker)}-- 定时校准,发给设备的绝对增量值=(上次绝对增量值+新数据的增量值)");
if (diastolicRefValue - diastolicAvg < 4)
{
_logger.LogInformation($"systolic 收缩压 {imeiDel}除最大值和最小值后的平均值:{systolicAvg}与标定值:{systolicRefValue}\n diastolic 舒张压 {imeiDel}除最大值和最小值后的平均值:{diastolicAvg}与标定值:{diastolicRefValue}的差值(标定值-平均值)少于4后,diastolic 舒张压 不再进行增量值调整");
break;
}

// 增量值=(标定值-平均值)* 0.25
var currentSystolicInc = (int)((systolicRefValue - systolicAvg) * systolicAvgOffset)!;
var currentDiastolicInc = (int)((diastolicRefValue - diastolicAvg) * diastolicAvgOffset)!;

_logger.LogInformation($"{nameof(Worker)} 开启血压标定值下发: {_configBoodPressResolver.EnableBPRefPush}");
if (_configBoodPressResolver.EnableBPRefPush)
// if (false) // 临时关闭
{
BloodPressCalibrationConfigModel bpIncData = new()
{

Imei = imeiDel,
SystolicRefValue = SafeType.SafeInt(((int)systolicRefValue!)), //收缩压标定值,值为0 表示不生效
DiastolicRefValue = SafeType.SafeInt(((int)diastolicRefValue!)), //舒张压标定值,值为0表示不生效
SystolicIncValue = SafeType.SafeInt(((int)systolicInc!)), //收缩压显示增量,值为0 表示不生效
DiastolicIncValue = SafeType.SafeInt(((int)diastolicInc!)) //舒张压显示增量,值为0 表示不生效
};
//var pushedBP = await _serviceIotWebApi.SetBloodPressCalibrationConfigAsync(bpIncData).ConfigureAwait(false);
var response = await _serviceIotWebApi.SetBloodPressCalibrationConfig2Async(bpIncData).ConfigureAwait(false);
var pushedBP = response.Flag;
if (pushedBP)
// 累计增量
systolicInc = currentSystolicInc + lastPushSystolicInc;
diastolicInc = currentDiastolicInc + lastPushDiastolicInc;

_logger.LogInformation($"{imeiDel}--{nameof(Worker)}--计算增量值" +
$"\n {imeiDel} -- systolicAvg:{systolicAvg}-- systolicInc:{systolicInc}-- currentSystolicInc:{currentSystolicInc} -- lastPushSystolicInc:{lastPushSystolicInc}" +
$"\n {imeiDel} -- diastolicAvg:{diastolicAvg}-- diastolicInc:{diastolicInc} --currentDiastolicInc:{currentDiastolicInc} -- lastPushDiastolicInc:{lastPushDiastolicInc}");
_logger.LogInformation($"{imeiDel}--{nameof(Worker)}-- 定时校准,发给设备的绝对增量值=(上次绝对增量值+新数据的增量值)");


_logger.LogInformation($"{nameof(Worker)} 开启血压标定值下发: {_configBoodPressResolver.EnableBPRefPush}");
if (_configBoodPressResolver.EnableBPRefPush)
// if (false) // 临时关闭
{
#region 保存下推记录 stb_hm_bp_push_ref_inc_value
var sql = $"INSERT INTO health_monitor.hm_bp_push_ref_inc_value_{imeiDel.Substring(imeiDel.Length - 2)} " +
$"USING health_monitor.stb_hm_bp_push_ref_inc_value " +
$"TAGS ('{imeiDel.Substring(imeiDel.Length - 2)}') " +
$"VALUES(" +
$"'{startTime:yyyy-MM-dd HH:mm:ss.fff}'," +
$"'{imeiDel}'," +
$"{bpIncData.SystolicRefValue}," +
$"{bpIncData.DiastolicRefValue}," +
$"{bpIncData.SystolicIncValue}," +
$"{bpIncData.DiastolicIncValue}," +
$"{false}," +
$"{systolicAvg}," +
$"{diastolicAvg}," +
$"{systolicAvgOffset}," +
$"{diastolicAvgOffset}," +
$"'{statStartTime:yyyy-MM-dd HH:mm:ss.fff}'," +
$"'{startTime:yyyy-MM-dd HH:mm:ss.fff}'" +
$")";
_serviceTDengine.ExecuteInsertSQL(sql);
#endregion
BloodPressCalibrationConfigModel bpIncData = new()
{

#region 注册定时下发
// 注册下次下推
var endTime = DateTime.Now;
Imei = imeiDel,
SystolicRefValue = SafeType.SafeInt(((int)systolicRefValue!)), //收缩压标定值,值为0 表示不生效
DiastolicRefValue = SafeType.SafeInt(((int)diastolicRefValue!)), //舒张压标定值,值为0表示不生效
SystolicIncValue = SafeType.SafeInt(((int)systolicInc!)), //收缩压显示增量,值为0 表示不生效
DiastolicIncValue = SafeType.SafeInt(((int)diastolicInc!)) //舒张压显示增量,值为0 表示不生效
};
//var pushedBP = await _serviceIotApi.SetBloodPressCalibrationConfigAsync(bpIncData).ConfigureAwait(false);
var response = await _serviceIotApi.SetBloodPressCalibrationConfig2Async(bpIncData).ConfigureAwait(false);
var pushedBP = response.Flag;
if (pushedBP)
{
#region 保存下推记录 stb_hm_bp_push_ref_inc_value
var sql = $"INSERT INTO health_monitor.hm_bp_push_ref_inc_value_{imeiDel.Substring(imeiDel.Length - 2)} " +
$"USING health_monitor.stb_hm_bp_push_ref_inc_value " +
$"TAGS ('{imeiDel.Substring(imeiDel.Length - 2)}') " +
$"VALUES(" +
$"'{startTime:yyyy-MM-dd HH:mm:ss.fff}'," +
$"'{imeiDel}'," +
$"{bpIncData.SystolicRefValue}," +
$"{bpIncData.DiastolicRefValue}," +
$"{bpIncData.SystolicIncValue}," +
$"{bpIncData.DiastolicIncValue}," +
$"{false}," +
$"{systolicAvg}," +
$"{diastolicAvg}," +
$"{systolicAvgOffset}," +
$"{diastolicAvgOffset}," +
$"'{statStartTime:yyyy-MM-dd HH:mm:ss.fff}'," +
$"'{startTime:yyyy-MM-dd HH:mm:ss.fff}'" +
$")";
_serviceTDengine.ExecuteInsertSQL(sql);
#endregion

#region 注册定时下发
// 注册下次下推
var endTime = DateTime.Now;

#if DEBUG


//long ttl = (long)((60 * 1000-(endTime-startTime).TotalMilliseconds)/1000);
//await _serviceEtcd.PutValAsync(key, imeiDel,ttl, false).ConfigureAwait(false);
//long ttl = (long)((60 * 1000-(endTime-startTime).TotalMilliseconds)/1000);
//await _serviceEtcd.PutValAsync(key, imeiDel,ttl, false).ConfigureAwait(false);

var interval = 0;
// 获取当前时间
DateTime now = DateTime.Now;
var interval = 0;
// 获取当前时间
DateTime now = DateTime.Now;

// 计算距离下一个$interval天后的8点的时间间隔
DateTime nextRunTime = new DateTime(now.Year, now.Month, now.Day, now.Hour, now.Minute + 2, 0).AddDays(interval);
TimeSpan timeUntilNextRun = nextRunTime - now;
// 计算距离下一个$interval天后的8点的时间间隔
DateTime nextRunTime = new DateTime(now.Year, now.Month, now.Day, now.Hour, now.Minute + 2, 0).AddDays(interval);
TimeSpan timeUntilNextRun = nextRunTime - now;

// 如果当前时间已经超过了8点,将等待到明天后的8点
if (timeUntilNextRun < TimeSpan.Zero)
{
timeUntilNextRun = timeUntilNextRun.Add(TimeSpan.FromMinutes(1));
nextRunTime += timeUntilNextRun;
}
// 如果当前时间已经超过了8点,将等待到明天后的8点
if (timeUntilNextRun < TimeSpan.Zero)
{
timeUntilNextRun = timeUntilNextRun.Add(TimeSpan.FromMinutes(1));
nextRunTime += timeUntilNextRun;
}

// var ttl = timeUntilNextRun.TotalMilliseconds;
long ttl = (long)((timeUntilNextRun.TotalMilliseconds - (endTime - startTime).TotalMilliseconds) / 1000);
var data = new
{
imei = imeiDel,
create_time = now.ToString("yyyy-MM-dd HH:mm:ss"),
ttl,
next_run_time = nextRunTime.ToString("yyyy-MM-dd HH:mm:ss")
};
var result = JsonConvert.SerializeObject(data);
// var ttl = timeUntilNextRun.TotalMilliseconds;
long ttl = (long)((timeUntilNextRun.TotalMilliseconds - (endTime - startTime).TotalMilliseconds) / 1000);
var data = new
{
imei = imeiDel,
create_time = now.ToString("yyyy-MM-dd HH:mm:ss"),
ttl,
next_run_time = nextRunTime.ToString("yyyy-MM-dd HH:mm:ss")
};
var result = JsonConvert.SerializeObject(data);

await _serviceEtcd.PutValAsync(key, result, ttl, false).ConfigureAwait(false);
await _serviceEtcd.PutValAsync(key, result, ttl, false).ConfigureAwait(false);


#else
@@ -424,24 +523,24 @@ namespace HealthMonitor.WebApi
var result = JsonConvert.SerializeObject(data);
await _serviceEtcd.PutValAsync(key, result, ttl, false).ConfigureAwait(false);
#endif
#endregion
#endregion

}
else
{
_logger.LogInformation($"错误响应,没有下推数据:{response.Message}");
}
else
{
_logger.LogInformation($"错误响应,没有下推数据:{response.Message}");
}
}
}
}
else
{
_logger.LogInformation($"向{imeiDel}统计数据已经失效");
}
#endregion
else
{
_logger.LogInformation($"向{imeiDel}统计数据已经失效");
}
#endregion

}

}

break;
}
}
@@ -454,62 +553,6 @@ namespace HealthMonitor.WebApi
});

}
/**
protected override Task ExecuteAsync(CancellationToken stoppingToken)
{
TaskFactory factory = new(_tokenSource.Token);
factory.StartNew(async () =>
{
if (_tokenSource.IsCancellationRequested)
_logger.LogWarning("Worker exit");

_logger.LogInformation("------ResolveAsync");
while (!_tokenSource.IsCancellationRequested)
{
await _processor.ResolveAsync().ConfigureAwait(false);
}

}, TaskCreationOptions.LongRunning);

factory.StartNew(() =>
{
_logger.LogInformation("------_tdEngineDataSubcribe");
while (!_tokenSource.IsCancellationRequested)
{
_tdEngineDataSubcribe.BeginListen(_tokenSource.Token);
}
}, TaskCreationOptions.LongRunning);
Task.Run(() =>
_serviceEtcd.WacthKeysWithPrefixResponseAsync($"health_moniter/schedule_push", WatchEvents)
, stoppingToken);
return Task.Delay(1000, _tokenSource.Token);

}
**/

/**
private void WatchEvents(WatchEvent[] response)
{
foreach (WatchEvent e1 in response)
{
// Console.WriteLine($"{nameof(WatchEventsAsync)} --- {e1.Key}:{e1.Value}:{e1.Type}");

switch (e1.Type.ToString())
{
//case "Put":
// // 获取时间点计算TTL
// break;

case "Delete":
// TTL到了重新计算TTL,下发
Console.WriteLine($"--- {e1.Key}:{e1.Value}:{e1.Type}");
break;
}
}
}
*/


}


+ 3
- 1
HealthMonitor.WebApi/appsettings.Development.json View File

@@ -27,7 +27,9 @@
"ServiceConfig": {
"TelpoDataUrl": "https://id.gdssjl.com/data/",
"EtcdServerAddress": "http://192.168.2.121:2379",
"IotWebApiUrl": "http://id.gdssjl.com/webapi/api/"
"IotWebApiUrl": "http://id.gdssjl.com/webapi/api/",
"IotAuth": "http://id.ssjlai.com/auth/identityController",
"IotCore": "https://id.ssjlai.com/gateway/core/api/v1/open/OpenIot"
},
"BoodPressResolverConfig": {
"EnableBPRefPush": true


+ 3
- 1
HealthMonitor.WebApi/appsettings.production.json View File

@@ -24,7 +24,9 @@
"ServiceConfig": {
"TelpoDataUrl": "https://ai.gdssjl.com/data/",
"EtcdServerAddress": "http://172.19.42.40:2379",
"IotWebApiUrl": "http://ai.gdssjl.com/webapi/api/"
"IotWebApiUrl": "http://ai.gdssjl.com/webapi/api/",
"IotAuth": "http://ai.ssjlai.com/auth/identityController",
"IotCore": "https://ai.ssjlai.com/gateway/core/api/v1/open/OpenIot"
},
"BoodPressResolverConfig": {
"EnableBPRefPush": true


+ 3
- 1
HealthMonitor.WebApi/appsettings.test.json View File

@@ -32,7 +32,9 @@
"ServiceConfig": {
"TelpoDataUrl": "https://id.gdssjl.com/data/",
"EtcdServerAddress": "http://172.19.42.44:2379",
"IotWebApiUrl": "http://id.gdssjl.com/webapi/api/"
"IotWebApiUrl": "http://id.gdssjl.com/webapi/api/",
"IotAuth": "http://id.ssjlai.com/auth/identityController",
"IotCore": "https://id.ssjlai.com/gateway/core/api/v1/open/OpenIot"
},
"BoodPressResolverConfig": {
"EnableBPRefPush": true


Loading…
Cancel
Save