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#

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#region << 版 本 注 释 >>
/*--------------------------------------------------------------------
* 版权所有 (c) 2025 WenJY 保留所有权利。
* CLR版本4.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);
}
}
}
}