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

478 lines
20 KiB
C#

#region << 版 本 注 释 >>
/*--------------------------------------------------------------------
* (c) 2025 WenJY
* CLR4.0.30319.42000
* Mr.Wen's MacBook Pro
* Sln.Iot.Business
* 7C26094C-5352-4997-866A-FA618F2E5D27
*
* WenJY
*
* 2025-04-11 15:47:06
* V1.0.0
*
*
*--------------------------------------------------------------------
*
*
*
*
* V1.0.0
*--------------------------------------------------------------------*/
#endregion << 版 本 注 释 >>
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using Dm.util;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Sln.Iot.Business.@base;
using Sln.Iot.Common;
using Sln.Iot.Config;
using Sln.Iot.Model.dao;
using Sln.Iot.Model.dto;
using Sln.Iot.Repository.service;
using Sln.Iot.Serilog;
using Sln.Iot.Socket;
using Sln.Iot.Socket.Adapter;
using TouchSocket.Core;
using TouchSocket.Sockets;
namespace Sln.Iot.Business
{
/// <summary>
/// 物联网环境:温度、湿度、照度、噪音、振动
/// </summary>
public class IotEnvBusiness:BaseBusiness
{
private readonly IRecordIotEnvInstantService? _service;
private readonly IBaseAlarmRuleService _alarmRuleService;
private readonly WebSocketBusiness _webSocket;
private readonly TcpServer _tcpServer;
private readonly IBaseMonitorInfoService _monitorInfoService;
public IotEnvBusiness(SerilogHelper logger, AppConfig appConfig, StringChange stringChange, IRecordIotEnvInstantService? service,
IBaseAlarmRuleService alarmRuleService, WebSocketBusiness webSocket, TcpServer tcpServer
, IBaseMonitorInfoService monitorInfoService) : base(logger, appConfig, stringChange)
{
_service = service;
_alarmRuleService = alarmRuleService;
_webSocket = webSocket;
_tcpServer = tcpServer;
_monitorInfoService = monitorInfoService;
}
public override FilterResult BufferAnalysis(ISocketClient client, BufferRequestInfo requestInfo, int bodyLength)
{
ByteBlock byteBlock = new ByteBlock(requestInfo.Body);
if (byteBlock.CanReadLen < 1)
{
return FilterResult.Cache;
}
int pos = byteBlock.Pos;
try
{
List<RecordIotEnvInstant> result = new List<RecordIotEnvInstant>();
var amount = requestInfo.BufferLength / bodyLength;
_logger.Info($"收到{amount}个物联网环境数据,开始循环解析......");
for (int i = 0; i < amount; i++)
{
RecordIotEnvInstant iotEnvInstant = new RecordIotEnvInstant();
#region 表号解析 Add By Wenjy 2024-04-18
// byteBlock.Read(out byte[] b_MeterID, 2);
// var MeterID_1 = "00" + Convert.ToInt32(b_MeterID[0]).ToString();
// MeterID_1 = MeterID_1.Substring(MeterID_1.Length - 2, 2);
// var MeterID_2 = "00" + Convert.ToInt32(b_MeterID[1]).ToString();
// MeterID_2 = MeterID_2.Substring(MeterID_2.Length - 2, 2);
// var equipId = requestInfo.ColletEquipCode + "_" + MeterID_1 + MeterID_2;
#endregion
#region 表号解析
byteBlock.Read(out byte[] b_MeterID, 2);
var decParts = b_MeterID.Select(b => $"{b:D2}").ToArray();
var equipId = $"{requestInfo.ColletEquipCode}_{decParts[0]}{decParts[1]}";
#endregion
iotEnvInstant.monitorId = equipId;
#region 物联网参数解析 Edit By Wenjy 2025-05-07 修改 Nan 值过滤
do
{
byteBlock.Read(out byte[] b_UA_flag, 2);
base._stringChange.ConvertBytesToUInt16(b_UA_flag, out uint flag);
switch (flag)
{
case CommParams.TTempreture: //温度
byteBlock.Read(out byte[] tempreture, 4);
base._stringChange.SwapBytes(ref tempreture);
float f_tempreture = BitConverter.ToSingle(tempreture, 0);
ValueIsNan(ref f_tempreture);
iotEnvInstant.temperature = (decimal) f_tempreture;
break;
case CommParams.Humidity: //湿度
byteBlock.Read(out byte[] humidity, 4);
base._stringChange.SwapBytes(ref humidity);
float f_humidity = BitConverter.ToSingle(humidity, 0);
ValueIsNan(ref f_humidity);
iotEnvInstant.humidity = (decimal) f_humidity;
break;
case CommParams.Noise: //噪音
byteBlock.Read(out byte[] noise, 4);
base._stringChange.SwapBytes(ref noise);
float f_noise = BitConverter.ToSingle(noise, 0);
ValueIsNan(ref f_noise);
iotEnvInstant.noise = (decimal) f_noise;
break;
case CommParams.VibrationSpeed: //振动-速度
byteBlock.Read(out byte[] vibrationSpeed, 4);
base._stringChange.SwapBytes(ref vibrationSpeed);
float f_vibrationSpeed = BitConverter.ToSingle(vibrationSpeed, 0);
ValueIsNan(ref f_vibrationSpeed);
iotEnvInstant.vibrationSpeed = (decimal) f_vibrationSpeed;
break;
case CommParams.VibrationDisplacement: //振动-位移
byteBlock.Read(out byte[] vibrationDisplacement, 4);
base._stringChange.SwapBytes(ref vibrationDisplacement);
float f_vibrationDisplacement = BitConverter.ToSingle(vibrationDisplacement, 0);
ValueIsNan(ref f_vibrationDisplacement);
iotEnvInstant.vibrationDisplacement = (decimal)f_vibrationDisplacement;
break;
case CommParams.VibrationAcceleration: //振动-加速度
byteBlock.Read(out byte[] vibrationAcceleration, 4);
base._stringChange.SwapBytes(ref vibrationAcceleration);
float f_vibrationAcceleration = BitConverter.ToSingle(vibrationAcceleration, 0);
ValueIsNan(ref f_vibrationAcceleration);
iotEnvInstant.vibrationAcceleration = (decimal)f_vibrationAcceleration;
break;
case CommParams.VibrationTemp: //振动-温度
byteBlock.Read(out byte[] vibrationTemp, 4);
base._stringChange.SwapBytes(ref vibrationTemp);
float f_vibrationTemp = BitConverter.ToSingle(vibrationTemp, 0);
ValueIsNan(ref f_vibrationTemp);
iotEnvInstant.vibrationTemp = (decimal)f_vibrationTemp;
break;
case CommParams.CJSJ:
byteBlock.Read(out byte[] b_CJSJ, 6);
string strDateTime = "20" + b_CJSJ[5].ToString("x2")
+ "-" + b_CJSJ[4].ToString("x2")
+ "-" + b_CJSJ[3].ToString("x2")
+ " " + b_CJSJ[2].ToString("x2")
+ ":" + b_CJSJ[1].ToString("x2")
+ ":" + b_CJSJ[0].ToString("x2");
iotEnvInstant.collectTime = Convert.ToDateTime(strDateTime);
break;
}
} while (byteBlock.Pos % bodyLength != 0);
#endregion
iotEnvInstant.recordTime = DateTime.Now;
var serializeObject = JsonConvert.SerializeObject(iotEnvInstant);
_logger.Info($"第{i+1}个物联网表{iotEnvInstant.monitorId}解析完成:{serializeObject}");
result.Add(iotEnvInstant);
}
if (result.Count > 0)
{
//是否开启 FF 异常值过滤
if (_appConfig.virtualFlag)
{
ParamVerification(ref result);
}
SendData(result);
var inRes = _service.SplitInsert(result,out List<long> insertIds);
_logger.Info($"{amount}个物联网数据解析处理完成,保存{result.Count}个物联网数据,保存{(inRes ? "" : "")}");
ParamAlarmFilter(result);
}
else
{
_logger.Info($"{amount}个物联网数据解析处理完成,没有需要保存的数据");
}
return FilterResult.Success;
}
catch (Exception e)
{
base._logger.Error($"物联网数据解析异常:{e.Message}");
}
return FilterResult.Cache;
}
/// <summary>
/// 回复指令
/// </summary>
/// <param name="client"></param>
/// <param name="buffer"></param>
public override void ResponseHandle(ISocketClient client, byte[] buffer)
{
ResponsePack sendResponsePackInfo = new ResponsePack()
{
m_MessageType = 0xB5
};
base.GetMessagePack(ref sendResponsePackInfo, buffer);
base.SendMessageAsync(client,sendResponsePackInfo);
}
/// <summary>
/// FF FF参数过滤
/// </summary>
/// <param name="iotEnvInstants"></param>
/// <exception cref="ArgumentNullException"></exception>
private void ParamVerification(ref List<RecordIotEnvInstant> iotEnvInstants)
{
if (iotEnvInstants == null)
{
throw new ArgumentNullException($"过滤参数方法异常,传入参数为空");
}
for (int i = iotEnvInstants.Count - 1; i >= 0; i--)
{
var item = iotEnvInstants[i];
if (item.temperature == _appConfig.virtualValue)
{
_logger.Info($"MonitorId:{item.monitorId},温度值为 FF FF FF FF已启用过滤不保存该表数据");
iotEnvInstants.RemoveAt(i);
continue;
}
if (item.humidity == _appConfig.virtualValue)
{
_logger.Info($"MonitorId:{item.monitorId},湿度值为 FF FF FF FF已启用过滤不保存该表数据");
iotEnvInstants.RemoveAt(i);
continue;
}
if (item.noise == _appConfig.virtualValue)
{
_logger.Info($"MonitorId:{item.monitorId},噪音值为 FF FF FF FF已启用过滤不保存该表数据");
iotEnvInstants.RemoveAt(i);
continue;
}
if (item.vibrationSpeed == _appConfig.virtualValue)
{
_logger.Info($"MonitorId:{item.monitorId},振动速度值为 FF FF FF FF已启用过滤不保存该表数据");
iotEnvInstants.RemoveAt(i);
continue;
}
if (item.vibrationDisplacement == _appConfig.virtualValue)
{
_logger.Info($"MonitorId:{item.monitorId},振动位移值为 FF FF FF FF已启用过滤不保存该表数据");
iotEnvInstants.RemoveAt(i);
continue;
}
if (item.vibrationAcceleration == _appConfig.virtualValue)
{
_logger.Info($"MonitorId:{item.monitorId},振动加速度值为 FF FF FF FF已启用过滤不保存该表数据");
iotEnvInstants.RemoveAt(i);
continue;
}
if (item.vibrationTemp == _appConfig.virtualValue)
{
_logger.Info($"MonitorId:{item.monitorId},振动温度值为 FF FF FF FF已启用过滤不保存该表数据");
iotEnvInstants.RemoveAt(i);
continue;
}
}
}
/// <summary>
///
/// </summary>
/// <param name="iotEnvInstants"></param>
public void SendData(List<RecordIotEnvInstant> iotEnvInstants)
{
lock (string.Empty)
{
foreach (var iotEnvInstant in iotEnvInstants)
{
Thread.Sleep(200);
var monitorInfo = _monitorInfoService.Query(x => x.monitorId == iotEnvInstant.monitorId).FirstOrDefault();
StringBuilder sb = new StringBuilder();
if (monitorInfo != null)
{
sb.Append(iotEnvInstant.monitorId);
sb.Append("-");
sb.Append(monitorInfo.monitorType);
if (monitorInfo.monitorType == 5 || monitorInfo.monitorType == 6)
{
sb.Append("-");
sb.Append(iotEnvInstant.temperature);
}
if (monitorInfo.monitorType == 6)
{
sb.Append("-");
sb.Append(iotEnvInstant.humidity);
}
if (monitorInfo.monitorType == 7)
{
sb.Append("-");
sb.Append(iotEnvInstant.noise);
}
if (monitorInfo.monitorType == 8)
{
sb.Append("-");
sb.Append(iotEnvInstant.illuminance);
}
if (monitorInfo.monitorType == 10)
{
sb.Append("-");
sb.Append(iotEnvInstant.vibrationSpeed);
sb.Append("-");
sb.Append(iotEnvInstant.vibrationAcceleration);
sb.Append("-");
sb.Append(iotEnvInstant.vibrationDisplacement);
sb.Append("-");
sb.Append(iotEnvInstant.vibrationTemp);
}
sb.Append(";");
}
//string str = $"{iotEnvInstant.monitorId}-{iotEnvInstant.temperature}-{iotEnvInstant.humidity}-{iotEnvInstant.noise}-{iotEnvInstant.illuminance}-{iotEnvInstant.vibrationSpeed}-{iotEnvInstant.vibrationAcceleration}-{iotEnvInstant.vibrationDisplacement}-{iotEnvInstant.vibrationTemp}";
string str = sb.ToString();
byte[] bytes = StringToBytesUsingASCII(str);
_tcpServer.SendDataToRecevieDevice(bytes);
}
}
}
public static byte[] StringToBytesUsingASCII(string input)
{
if (input == null)
{
throw new ArgumentNullException(nameof(input), "输入字符串不能为 null。");
}
// 使用 ASCII 编码将字符串转换为字节数组
return Encoding.ASCII.GetBytes(input);
}
/// <summary>
/// 报警参数过滤
/// </summary>
/// <param name="iotEnvInstants"></param>
private void ParamAlarmFilter(List<RecordIotEnvInstant> iotEnvInstants)
{
if (iotEnvInstants == null)
{
throw new ArgumentNullException(nameof(iotEnvInstants), "报警参数过滤异常,传入参数为空");
}
// 预编译比较委托(避免循环内重复编译)
static Func<decimal, decimal, bool> CreateComparer(int triggerRule)
{
ParameterExpression x = Expression.Parameter(typeof(decimal), "x");
ParameterExpression y = Expression.Parameter(typeof(decimal), "y");
BinaryExpression comparison = triggerRule == 0
? Expression.GreaterThan(x, y)
: Expression.LessThan(x, y);
return Expression.Lambda<Func<decimal, decimal, bool>>(comparison, x, y).Compile();
}
var fieldAccessors = new Dictionary<int, Func<RecordIotEnvInstant, decimal>>
{
{ 0, item => item.temperature },
{ 1, item => item.humidity },
{ 2, item => item.vibrationSpeed },
{ 3, item => item.vibrationDisplacement },
{ 4, item => item.vibrationAcceleration },
{ 5, item => item.vibrationTemp },
{ 6, item => item.noise },
{ 7, item => item.illuminance }
};
foreach (var item in iotEnvInstants)
{
var alarmRules = _alarmRuleService.Query(x => x.monitorId == item.monitorId);
List<BaseAlarmRule> ruleRes = new List<BaseAlarmRule>();
List<string> alarmContents = new List<string>();
foreach (var rule in alarmRules)
{
decimal paramValue = fieldAccessors.TryGetValue(rule.monitorField, out var accessor)
? accessor(item)
: 0;
var comparer = CreateComparer(rule.triggerRule);
if (comparer(paramValue, rule.triggerValue))
{
ruleRes.Add(rule);
alarmContents.Add($"{item.monitorId}传感器数据在{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}触发{rule.ruleName}异常告警,告警规则:{(rule.triggerRule == 0 ? "" : "") },阈值:{rule.triggerValue},详细信息:{rule.cause}");
}
}
MonitorAlarmDto monitorAlarmDto = new MonitorAlarmDto()
{
monitorId = item.monitorId,
isFlag = ruleRes.Count() > 0 ? 1 : 0,
deviceParam = item,
alarmRules = ruleRes,
alarmContents = alarmContents,
recordTime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()
};
string str = JsonConvert.SerializeObject(monitorAlarmDto);
_logger.Alarm($"传感器数据推送:{str}");
_webSocket.PushMsg(str);
}
}
}
}