diff --git a/Sln.Iot.Business/IotEnvBusiness.cs b/Sln.Iot.Business/IotEnvBusiness.cs index 06c62d4..fa0c86e 100644 --- a/Sln.Iot.Business/IotEnvBusiness.cs +++ b/Sln.Iot.Business/IotEnvBusiness.cs @@ -25,7 +25,11 @@ 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; @@ -35,6 +39,7 @@ 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; @@ -47,10 +52,26 @@ namespace Sln.Iot.Business 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) : base(logger, appConfig, stringChange) + 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) @@ -139,17 +160,16 @@ namespace Sln.Iot.Business ValueIsNan(ref f_vibrationSpeed); - iotEnvInstant.VibrationSpeed = (decimal) f_vibrationSpeed; + iotEnvInstant.vibrationSpeed = (decimal) f_vibrationSpeed; break; case CommParams.VibrationDisplacement: //振动-位移 - bodyLength = 58; 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; + iotEnvInstant.vibrationDisplacement = (decimal)f_vibrationDisplacement; break; case CommParams.VibrationAcceleration: //振动-加速度 byteBlock.Read(out byte[] vibrationAcceleration, 4); @@ -158,7 +178,7 @@ namespace Sln.Iot.Business ValueIsNan(ref f_vibrationAcceleration); - iotEnvInstant.VibrationAcceleration = (decimal)f_vibrationAcceleration; + iotEnvInstant.vibrationAcceleration = (decimal)f_vibrationAcceleration; break; case CommParams.VibrationTemp: //振动-温度 byteBlock.Read(out byte[] vibrationTemp, 4); @@ -167,7 +187,7 @@ namespace Sln.Iot.Business ValueIsNan(ref f_vibrationTemp); - iotEnvInstant.VibrationTemp = (decimal)f_vibrationTemp; + iotEnvInstant.vibrationTemp = (decimal)f_vibrationTemp; break; case CommParams.CJSJ: byteBlock.Read(out byte[] b_CJSJ, 6); @@ -203,10 +223,14 @@ namespace Sln.Iot.Business { ParamVerification(ref result); } - + + SendData(result); + var inRes = _service.SplitInsert(result,out List insertIds); _logger.Info($"{amount}个物联网数据解析处理完成,保存{result.Count}个物联网数据,保存{(inRes ? "成功" : "失败")}"); + + ParamAlarmFilter(result); } else { @@ -276,28 +300,28 @@ namespace Sln.Iot.Business continue; } - if (item.VibrationSpeed == _appConfig.virtualValue) + if (item.vibrationSpeed == _appConfig.virtualValue) { _logger.Info($"MonitorId:{item.monitorId},振动速度值为 FF FF FF FF,已启用过滤不保存该表数据"); iotEnvInstants.RemoveAt(i); continue; } - if (item.VibrationDisplacement == _appConfig.virtualValue) + if (item.vibrationDisplacement == _appConfig.virtualValue) { _logger.Info($"MonitorId:{item.monitorId},振动位移值为 FF FF FF FF,已启用过滤不保存该表数据"); iotEnvInstants.RemoveAt(i); continue; } - if (item.VibrationAcceleration == _appConfig.virtualValue) + if (item.vibrationAcceleration == _appConfig.virtualValue) { _logger.Info($"MonitorId:{item.monitorId},振动加速度值为 FF FF FF FF,已启用过滤不保存该表数据"); iotEnvInstants.RemoveAt(i); continue; } - if (item.VibrationTemp == _appConfig.virtualValue) + if (item.vibrationTemp == _appConfig.virtualValue) { _logger.Info($"MonitorId:{item.monitorId},振动温度值为 FF FF FF FF,已启用过滤不保存该表数据"); iotEnvInstants.RemoveAt(i); @@ -305,5 +329,150 @@ namespace Sln.Iot.Business } } } + + /// + /// + /// + /// + public void SendData(List 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); + } + + + /// + /// 报警参数过滤 + /// + /// + private void ParamAlarmFilter(List iotEnvInstants) + { + if (iotEnvInstants == null) + { + throw new ArgumentNullException(nameof(iotEnvInstants), "报警参数过滤异常,传入参数为空"); + } + + // 预编译比较委托(避免循环内重复编译) + static Func 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>(comparison, x, y).Compile(); + } + + var fieldAccessors = new Dictionary> + { + { 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 ruleRes = new List(); + List alarmContents = new List(); + 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); + } + } } } \ No newline at end of file diff --git a/Sln.Iot.Business/Sln.Iot.Business.csproj b/Sln.Iot.Business/Sln.Iot.Business.csproj index a1e49db..4cf19b3 100644 --- a/Sln.Iot.Business/Sln.Iot.Business.csproj +++ b/Sln.Iot.Business/Sln.Iot.Business.csproj @@ -6,6 +6,10 @@ enable + + + + diff --git a/Sln.Iot.Business/WebSocketBusiness.cs b/Sln.Iot.Business/WebSocketBusiness.cs new file mode 100644 index 0000000..18e89d2 --- /dev/null +++ b/Sln.Iot.Business/WebSocketBusiness.cs @@ -0,0 +1,63 @@ +using Fleck; +using Sln.Iot.Serilog; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Sln.Iot.Business +{ + public class WebSocketBusiness + { + private List allSockets = new List(); + + private readonly SerilogHelper _logger; + + public WebSocketBusiness(SerilogHelper serilogHelper) + { + _logger = serilogHelper; + } + + public void Init() + { + var server = new Fleck.WebSocketServer($"ws://0.0.0.0:7181"); + server.Start(socket => + { + socket.OnOpen = () => + { + var data = socket.ConnectionInfo; + _logger.Info("WebSocket Open!"); + allSockets.Add(socket); + }; + + socket.OnClose = () => + { + _logger.Info("WebSocket Close!"); + allSockets.Remove(socket); + }; + + socket.OnMessage = message => + { + //ReceivedMessageRequestInfoEvent?.Invoke(socket, message); + }; + }); + } + + public void PushMsg(string msg) + { + try + { + foreach (var socket in allSockets.ToList()) + { + socket.Send(msg); + _logger.Info($"WebSocket推送信息:{msg}"); + } + } + catch (Exception ex) + { + _logger.Info($"WebSocket推送信息异常:{ex.Message}"); + } + } + } +} diff --git a/Sln.Iot.Business/base/BaseBusiness.cs b/Sln.Iot.Business/base/BaseBusiness.cs index dcdc18c..b65f142 100644 --- a/Sln.Iot.Business/base/BaseBusiness.cs +++ b/Sln.Iot.Business/base/BaseBusiness.cs @@ -23,10 +23,13 @@ #endregion << 版 本 注 释 >> +using Fleck; +using Microsoft.Extensions.DependencyInjection; using Sln.Iot.Common; using Sln.Iot.Config; using Sln.Iot.Model.dto; using Sln.Iot.Serilog; +using Sln.Iot.Socket; using Sln.Iot.Socket.Adapter; using TouchSocket.Core; using TouchSocket.Sockets; @@ -40,12 +43,13 @@ namespace Sln.Iot.Business.@base public AppConfig _appConfig; public StringChange _stringChange; - + public BaseBusiness(SerilogHelper logger,AppConfig appConfig,StringChange stringChange) { _logger = logger; _appConfig = appConfig; _stringChange = stringChange; + } /// @@ -147,5 +151,6 @@ namespace Sln.Iot.Business.@base } } } + } } \ No newline at end of file diff --git a/Sln.Iot.Config/AppConfig.cs b/Sln.Iot.Config/AppConfig.cs index 3ea6d25..60f1c90 100644 --- a/Sln.Iot.Config/AppConfig.cs +++ b/Sln.Iot.Config/AppConfig.cs @@ -70,6 +70,11 @@ namespace Sln.Iot.Config /// public string redisConfig { get; set; } + /// + /// 串口配置 + /// + public SerialPortConfig serialPortConfig { get; set; } + public AppConfig Value => this; } } \ No newline at end of file diff --git a/Sln.Iot.Config/SerialPortConfig.cs b/Sln.Iot.Config/SerialPortConfig.cs new file mode 100644 index 0000000..a82e4f6 --- /dev/null +++ b/Sln.Iot.Config/SerialPortConfig.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Sln.Iot.Config +{ + public class SerialPortConfig + { + /// + /// 串口名称 + /// + public string PortName { get; set; } + + /// + /// 波特率 + /// + public int BaudRate { get; set; } + + /// + /// 奇偶校验 + /// + public int Parity { get; set; } + + /// + /// 停止位 + /// + public int StopBits { get; set; } + + /// + /// 数据位 + /// + public int DataBits { get; set; } + + /// + /// + /// + public int Handshake { get; set; } + } +} diff --git a/Sln.Iot.Model/dao/BaseAlarmRule.cs b/Sln.Iot.Model/dao/BaseAlarmRule.cs new file mode 100644 index 0000000..0117c5c --- /dev/null +++ b/Sln.Iot.Model/dao/BaseAlarmRule.cs @@ -0,0 +1,61 @@ +using SqlSugar; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Sln.Iot.Model.dao +{ + [SugarTable("ems_record_alarm_rule"), TenantAttribute("tao_iot")] + public class BaseAlarmRule + { + /// + /// 编号 + /// + [SugarColumn(ColumnName = "obj_id", IsPrimaryKey = true)] + public long objid { get; set; } + + /// + /// 计量设备编号 + /// + [SugarColumn(ColumnName = "monitor_id")] + public string monitorId { get; set; } + + /// + /// 规则编号 + /// + [SugarColumn(ColumnName = "rule_id")] + public string ruleId { get;set; } + + /// + /// 规则名称 + /// + [SugarColumn(ColumnName = "rule_name")] + public string ruleName { get; set; } + + /// + /// 触发规则(0大于 1小于) + /// + [SugarColumn(ColumnName = "trigger_rule")] + public int triggerRule { get; set; } + + /// + /// 监测字段(0温度,1湿度,2振动-速度(mm/s),3振动-位移(um),4振动-加速度(g),5振动-温度(℃),6-噪音,7-照度) + /// + [SugarColumn(ColumnName = "monitor_field")] + public int monitorField { get;set; } + + /// + /// 阈值 + /// + [SugarColumn(ColumnName = "trigger_value")] + public decimal triggerValue { get; set; } + + /// + /// 备注 + /// + [SugarColumn(ColumnName = "cause")] + public string cause { get; set; } + } +} diff --git a/Sln.Iot.Model/dao/RecordIotEnvInstant.cs b/Sln.Iot.Model/dao/RecordIotEnvInstant.cs index 8958735..69a4239 100644 --- a/Sln.Iot.Model/dao/RecordIotEnvInstant.cs +++ b/Sln.Iot.Model/dao/RecordIotEnvInstant.cs @@ -78,26 +78,26 @@ namespace Sln.Iot.Model.dao /// 振动-速度 /// [SugarColumn(ColumnName = "vibration_speed")] - public decimal VibrationSpeed { get; set; } + public decimal vibrationSpeed { get; set; } /// /// 振动-位移 /// [SugarColumn(ColumnName = "vibration_displacement")] - public decimal VibrationDisplacement { get; set; } + public decimal vibrationDisplacement { get; set; } /// /// 振动-加速度 /// [SugarColumn(ColumnName = "vibration_acceleration")] - public decimal VibrationAcceleration { get; set; } + public decimal vibrationAcceleration { get; set; } /// /// 振动-温度 /// [SugarColumn(ColumnName = "vibration_temp")] - public decimal VibrationTemp { get; set; } + public decimal vibrationTemp { get; set; } /// diff --git a/Sln.Iot.Model/dto/CommParams.cs b/Sln.Iot.Model/dto/CommParams.cs index 94a81a3..5c9c4a7 100644 --- a/Sln.Iot.Model/dto/CommParams.cs +++ b/Sln.Iot.Model/dto/CommParams.cs @@ -46,22 +46,23 @@ namespace Sln.Iot.Model.dto /// /// 振动-速度 /// - public const uint VibrationSpeed = 0x8E54; + public const uint VibrationSpeed = 0x8E55; /// /// 振动-位移 /// - public const uint VibrationDisplacement = 0x8E55; + public const uint VibrationDisplacement = 0x8E56; /// /// 振动-加速度 /// - public const uint VibrationAcceleration = 0x8E56; + public const uint VibrationAcceleration = 0x8E57; /// /// 振动-温度 /// - public const uint VibrationTemp = 0x8E57; + public const uint VibrationTemp = 0x8E54; + /// /// 采集时间 diff --git a/Sln.Iot.Model/dto/MonitorAlarmDto.cs b/Sln.Iot.Model/dto/MonitorAlarmDto.cs new file mode 100644 index 0000000..fa1dd05 --- /dev/null +++ b/Sln.Iot.Model/dto/MonitorAlarmDto.cs @@ -0,0 +1,43 @@ +using Sln.Iot.Model.dao; +using SqlSugar; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Sln.Iot.Model.dto +{ + public class MonitorAlarmDto + { + /// + /// 设备编号 + /// + public string monitorId { get;set; } + + /// + /// 是否报警:1-是;0-否 + /// + public int isFlag { get;set; } + + /// + /// 设备参数 + /// + public RecordIotEnvInstant deviceParam { get;set; } + + /// + /// 告警规则 + /// + public List alarmRules { get;set; } + + /// + /// 报警内容 + /// + public List alarmContents { get;set; } + + /// + /// 记录时间 + /// + public long recordTime { get;set; } + } +} diff --git a/Sln.Iot.Repository/service/IBaseAlarmRuleService.cs b/Sln.Iot.Repository/service/IBaseAlarmRuleService.cs new file mode 100644 index 0000000..4860e2c --- /dev/null +++ b/Sln.Iot.Repository/service/IBaseAlarmRuleService.cs @@ -0,0 +1,14 @@ +using Sln.Iot.Model.dao; +using Sln.Iot.Repository.service.@base; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Sln.Iot.Repository.service +{ + public interface IBaseAlarmRuleService : IBaseService + { + } +} diff --git a/Sln.Iot.Repository/service/Impl/BaseAlarmRuleServiceImpl.cs b/Sln.Iot.Repository/service/Impl/BaseAlarmRuleServiceImpl.cs new file mode 100644 index 0000000..e42d738 --- /dev/null +++ b/Sln.Iot.Repository/service/Impl/BaseAlarmRuleServiceImpl.cs @@ -0,0 +1,17 @@ +using Sln.Iot.Model.dao; +using Sln.Iot.Repository.service.@base; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Sln.Iot.Repository.service.Impl +{ + public class BaseAlarmRuleServiceImpl : BaseServiceImpl, IBaseAlarmRuleService + { + public BaseAlarmRuleServiceImpl(Repository rep) : base(rep) + { + } + } +} diff --git a/Sln.Iot.Serial/SerialExtensions.cs b/Sln.Iot.Serial/SerialExtensions.cs new file mode 100644 index 0000000..1fca02f --- /dev/null +++ b/Sln.Iot.Serial/SerialExtensions.cs @@ -0,0 +1,198 @@ +using Dm.util; +using Microsoft.Extensions.DependencyInjection; +using Newtonsoft.Json; +using RJCP.IO.Ports; +using Sln.Iot.Business; +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 SqlSugar; +using System; +using System.Linq.Expressions; +using System.Net.WebSockets; +using System.Text; +using TouchSocket.Core; +using TouchSocket.Sockets; + +namespace Sln.Iot.Serial +{ + public static class SerialExtensions + { + + public static void UseSerialPortExtensions(this IServiceProvider service) + { + IRecordIotEnvInstantService instantService = service.GetService(); + + IBaseAlarmRuleService _alarmRuleService = service.GetService(); + + WebSocketBusiness webSocket = service.GetService(); + + var logger = service.GetService(); + try + { + var appConfig = service.GetService(); + var tcpClient = service.GetService(); + var stringChange = service.GetService(); + + SerialPortStream serialPort = new SerialPortStream(appConfig.serialPortConfig.PortName); + serialPort.BaudRate = appConfig.serialPortConfig.BaudRate; + serialPort.Parity = Parity.None; + serialPort.StopBits = StopBits.One; + serialPort.DataBits = appConfig.serialPortConfig.DataBits; + serialPort.Handshake = Handshake.None; + + serialPort.DataReceived += (sender, e) => + { + lock (string.Empty) + { + try + { + byte[] receivedData = new byte[serialPort.BytesToRead]; + serialPort.Read(receivedData, 0, receivedData.Length); + + string str = Encoding.ASCII.GetString(receivedData); + logger.Info($"串口服务接收到数据:{str}"); + + List< RecordIotEnvInstant > iotEnvInstants = new List< RecordIotEnvInstant >(); + string[] monitorInfos = str.Split(';'); + + foreach ( string monitorInfo in monitorInfos ) + { + + if (monitorInfo.Length < 12) + { + continue; + } + logger.Info($"解析串口数据:{monitorInfo}"); + RecordIotEnvInstant iotEnvInstant = new RecordIotEnvInstant(); + string[] strs = monitorInfo.Replace(";","").Split('-'); + + iotEnvInstant.monitorId = strs[0]; + + if (strs[1] == "5" || strs[1] == "6") + { + iotEnvInstant.temperature = decimal.Parse(strs[2].Trim()); + } + if (strs[1] == "6") + { + iotEnvInstant.humidity = decimal.Parse(strs[3].Trim()); + } + if (strs[1] == "7") + { + iotEnvInstant.noise = decimal.Parse(strs[2].Trim()); + } + if (strs[1] == "8") + { + iotEnvInstant.illuminance = decimal.Parse(strs[2].Trim()); + } + + if (strs[1] == "10") + { + iotEnvInstant.vibrationSpeed = decimal.Parse(strs[2].Trim()); + iotEnvInstant.vibrationAcceleration = decimal.Parse(strs[3].Trim()); + iotEnvInstant.vibrationDisplacement = decimal.Parse(strs[4].Trim()); + iotEnvInstant.vibrationTemp = decimal.Parse(strs[5].Trim()); + } + iotEnvInstant.collectTime = DateTime.Now; + iotEnvInstant.recordTime = DateTime.Now; + iotEnvInstants.Add(iotEnvInstant); + + Thread.Sleep(200); + } + + + var inRes = instantService.SplitInsert(iotEnvInstants, out List insertIds); + + logger.Info($"物联网数据保存{(inRes ? "成功" : "失败")}"); + + ParamAlarmFilter(iotEnvInstants,_alarmRuleService,logger, webSocket); + } + catch (Exception ex) + { + logger.Info($"串口接收数据异常:{ex.Message}"); + } + } + }; + + serialPort.Open(); + + logger.Info($"串口服务加载启动成功"); + } + catch (Exception ex) + { + logger.Error($"串口服务加载异常:{ex.Message}"); + } + } + + private static void ParamAlarmFilter(List iotEnvInstants, IBaseAlarmRuleService _alarmRuleService, SerilogHelper _logger, WebSocketBusiness _webSocket) + { + if (iotEnvInstants == null) + { + throw new ArgumentNullException(nameof(iotEnvInstants), "报警参数过滤异常,传入参数为空"); + } + + // 预编译比较委托(避免循环内重复编译) + static Func 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>(comparison, x, y).Compile(); + } + + var fieldAccessors = new Dictionary> + { + { 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 ruleRes = new List(); + List alarmContents = new List(); + 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); + } + } + + } +} diff --git a/Sln.Iot.Serial/Sln.Iot.Serial.csproj b/Sln.Iot.Serial/Sln.Iot.Serial.csproj new file mode 100644 index 0000000..dae9b9b --- /dev/null +++ b/Sln.Iot.Serial/Sln.Iot.Serial.csproj @@ -0,0 +1,28 @@ + + + + net6.0 + enable + enable + + + + + + + + + + + + + + + + + + Always + + + + diff --git a/Sln.Iot.Serial/libnserial.so.1 b/Sln.Iot.Serial/libnserial.so.1 new file mode 100644 index 0000000..5b93c2a Binary files /dev/null and b/Sln.Iot.Serial/libnserial.so.1 differ diff --git a/Sln.Iot.Serilog/SerilogExtensions.cs b/Sln.Iot.Serilog/SerilogExtensions.cs index 48ca446..8dc9f59 100644 --- a/Sln.Iot.Serilog/SerilogExtensions.cs +++ b/Sln.Iot.Serilog/SerilogExtensions.cs @@ -48,11 +48,11 @@ namespace Sln.Iot.Serilog .Filter.ByIncludingOnly(logEvent => logEvent.Properties.ContainsKey("Module") && logEvent.Properties["Module"].ToString().Contains("Info")) .WriteTo.File(Path.Combine($"{logPath}/Info/", "Info.log"), rollingInterval: RollingInterval.Day)) .WriteTo.Logger(lc => lc - .Filter.ByIncludingOnly(logEvent => logEvent.Properties.ContainsKey("Module") && logEvent.Properties["Module"].ToString().Contains("Plc")) - .WriteTo.File(Path.Combine($"{logPath}/Plc/", "Plc.log"), rollingInterval: RollingInterval.Day)) + .Filter.ByIncludingOnly(logEvent => logEvent.Properties.ContainsKey("Module") && logEvent.Properties["Module"].ToString().Contains("Iot")) + .WriteTo.File(Path.Combine($"{logPath}/Iot/", "Iot.log"), rollingInterval: RollingInterval.Day)) .WriteTo.Logger(lc => lc - .Filter.ByIncludingOnly(logEvent => logEvent.Properties.ContainsKey("Module") && logEvent.Properties["Module"].ToString().Contains("Camera")) - .WriteTo.File(Path.Combine($"{logPath}/Camera/", "Camera.log"), rollingInterval: RollingInterval.Day)) + .Filter.ByIncludingOnly(logEvent => logEvent.Properties.ContainsKey("Module") && logEvent.Properties["Module"].ToString().Contains("Alarm")) + .WriteTo.File(Path.Combine($"{logPath}/Alarm/", "Alarm.log"), rollingInterval: RollingInterval.Day)) .WriteTo.Logger(lc => lc .Filter.ByIncludingOnly(logEvent => logEvent.Properties.ContainsKey("Module") && logEvent.Properties["Module"].ToString().Contains("Error")) .WriteTo.File(Path.Combine($"{logPath}/Error/", "Error.log"), rollingInterval: RollingInterval.Day)) diff --git a/Sln.Iot.Serilog/SerilogHelper.cs b/Sln.Iot.Serilog/SerilogHelper.cs index 7a00349..bb38ae6 100644 --- a/Sln.Iot.Serilog/SerilogHelper.cs +++ b/Sln.Iot.Serilog/SerilogHelper.cs @@ -31,9 +31,9 @@ namespace Sln.Iot.Serilog public class SerilogHelper { private readonly ILogger? Info_logger = Log.ForContext("Module", "Info"); - private readonly ILogger? Plc_logger = Log.ForContext("Module", "Plc"); + private readonly ILogger? Iot_logger = Log.ForContext("Module", "Iot"); private readonly ILogger? Error_logger = Log.ForContext("Module", "Error"); - private readonly ILogger? Camera_logger = Log.ForContext("Module", "Camera"); + private readonly ILogger? Alarm_logger = Log.ForContext("Module", "Alarm"); /// /// Info日志 @@ -48,26 +48,26 @@ namespace Sln.Iot.Serilog } /// - /// Plc日志 + /// Iot日志 /// /// - public void Plc(string msg) + public void Iot(string msg) { - if (Plc_logger != null) + if (Iot_logger != null) { - this.Plc_logger.Information(msg); + this.Iot_logger.Information(msg); } } /// - /// 相机日志 + /// 设备告警日志 /// /// - public void Camera(string msg) + public void Alarm(string msg) { - if (Camera_logger != null) + if (Alarm_logger != null) { - this.Camera_logger.Information(msg); + this.Alarm_logger.Information(msg); } } diff --git a/Sln.Iot.Socket/Class1.cs b/Sln.Iot.Socket/Class1.cs deleted file mode 100644 index 3660e6e..0000000 --- a/Sln.Iot.Socket/Class1.cs +++ /dev/null @@ -1,5 +0,0 @@ -namespace Sln.Iot.Socket; - -public class Class1 -{ -} \ No newline at end of file diff --git a/Sln.Iot.Socket/Sln.Iot.Socket.csproj b/Sln.Iot.Socket/Sln.Iot.Socket.csproj index 0990bfc..40602ca 100644 --- a/Sln.Iot.Socket/Sln.Iot.Socket.csproj +++ b/Sln.Iot.Socket/Sln.Iot.Socket.csproj @@ -11,6 +11,7 @@ + diff --git a/Sln.Iot.Socket/TcpServer.cs b/Sln.Iot.Socket/TcpServer.cs index 87e3e2a..0a9a153 100644 --- a/Sln.Iot.Socket/TcpServer.cs +++ b/Sln.Iot.Socket/TcpServer.cs @@ -24,6 +24,8 @@ #endregion << 版 本 注 释 >> using System; +using System.Text; +using Sln.Iot.Common; using Sln.Iot.Serilog; using Sln.Iot.Socket.Adapter; using TouchSocket.Core; @@ -35,11 +37,13 @@ namespace Sln.Iot.Socket { private readonly SerilogHelper _logger; private readonly TcpService _service; + private readonly StringChange _stringChange; - public TcpServer(SerilogHelper logger, TcpService service) + public TcpServer(SerilogHelper logger, TcpService service, StringChange stringChange) { _logger = logger; _service = service; + _stringChange = stringChange; } /// @@ -66,7 +70,7 @@ namespace Sln.Iot.Socket _service.Connected = (client, e) => { _logger.Info($"客户端{client.IP}接入服务成功"); RefreshClientInfoEvent?.Invoke(_service); - return EasyTask.CompletedTask; + return EasyTask.CompletedTask; }; _service.Disconnected = (client, e) => { _logger.Info($"客户端{client.IP}断开连接"); @@ -77,13 +81,13 @@ namespace Sln.Iot.Socket { if (e.RequestInfo is BufferRequestInfo request) { + _logger.Iot($"收到客户端:{client.Id};原始指令====>>>>{_stringChange.bytesToHexStr(request.buffer.Buffer, request.buffer.Len)}"); string msg = $"收到客户端:{client.Id};指令====>>>>Header:{BitConverter.ToString(request.header).Replace("-", "")};DataType:{request.DataType.ToString("X2")};BufferLength:{request.BufferLength};Body:{BitConverter.ToString(request.Body).Replace("-", "")};CheckBit:{request.CheckBit.ToString("X2")};Tail:{BitConverter.ToString(request.Tail).Replace("-", "")};"; _logger.Info($"{msg}"); ReceivedBufferRequestInfoEvent?.Invoke(client,request); - } return EasyTask.CompletedTask; @@ -123,5 +127,23 @@ namespace Sln.Iot.Socket _service.Send(item.Id,"heartbeat"); } } + + public void SendDataToRecevieDevice(byte[] bytes) + { + var clientInfos = _service.SocketClients.GetClients().Where(x => x.Id.Contains("450")).ToList(); + + if (clientInfos == null) + { + _logger.Info($"连接中不存在450设备"); + } + else + { + foreach (var item in clientInfos) + { + item.Send(bytes); + _logger.Info($"向接收设备{item.Id};发送数据:{_stringChange.bytesToHexStr(bytes, bytes.Length)}"); + } + } + } } } \ No newline at end of file diff --git a/Sln.Iot.sln b/Sln.Iot.sln index c7f584f..a491fd6 100644 --- a/Sln.Iot.sln +++ b/Sln.Iot.sln @@ -3,21 +3,23 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.9.34714.143 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sln.Iot", "Sln.Iot\Sln.Iot.csproj", "{2140AD68-D4CE-44EC-B9D3-20D18EB59F9D}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sln.Iot", "Sln.Iot\Sln.Iot.csproj", "{2140AD68-D4CE-44EC-B9D3-20D18EB59F9D}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sln.Iot.Business", "Sln.Iot.Business\Sln.Iot.Business.csproj", "{F59EB67D-66FD-43B9-B6CC-46BF25202C21}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sln.Iot.Business", "Sln.Iot.Business\Sln.Iot.Business.csproj", "{F59EB67D-66FD-43B9-B6CC-46BF25202C21}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sln.Iot.Config", "Sln.Iot.Config\Sln.Iot.Config.csproj", "{2E10DED8-5F53-4ED9-892F-B0007B85806E}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sln.Iot.Config", "Sln.Iot.Config\Sln.Iot.Config.csproj", "{2E10DED8-5F53-4ED9-892F-B0007B85806E}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sln.Iot.Common", "Sln.Iot.Common\Sln.Iot.Common.csproj", "{899D8A81-D3E3-4599-8A8C-D60280A777F3}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sln.Iot.Common", "Sln.Iot.Common\Sln.Iot.Common.csproj", "{899D8A81-D3E3-4599-8A8C-D60280A777F3}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sln.Iot.Model", "Sln.Iot.Model\Sln.Iot.Model.csproj", "{503E7EAE-6323-4CE2-AAE4-C6A7CBDFC4B2}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sln.Iot.Model", "Sln.Iot.Model\Sln.Iot.Model.csproj", "{503E7EAE-6323-4CE2-AAE4-C6A7CBDFC4B2}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sln.Iot.Repository", "Sln.Iot.Repository\Sln.Iot.Repository.csproj", "{DA193E49-8B4A-4C8D-B44E-844E22983DA8}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sln.Iot.Repository", "Sln.Iot.Repository\Sln.Iot.Repository.csproj", "{DA193E49-8B4A-4C8D-B44E-844E22983DA8}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sln.Iot.Serilog", "Sln.Iot.Serilog\Sln.Iot.Serilog.csproj", "{A9CCC9F6-BE1C-4B73-AFBF-83D363D7F64F}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sln.Iot.Serilog", "Sln.Iot.Serilog\Sln.Iot.Serilog.csproj", "{A9CCC9F6-BE1C-4B73-AFBF-83D363D7F64F}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sln.Iot.Socket", "Sln.Iot.Socket\Sln.Iot.Socket.csproj", "{5B7C6367-7B41-48A6-9A71-2F191CE14000}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sln.Iot.Socket", "Sln.Iot.Socket\Sln.Iot.Socket.csproj", "{5B7C6367-7B41-48A6-9A71-2F191CE14000}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sln.Iot.Serial", "Sln.Iot.Serial\Sln.Iot.Serial.csproj", "{A030CF57-DAA0-4662-B836-3CFC2CDCD9FE}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -57,6 +59,10 @@ Global {5B7C6367-7B41-48A6-9A71-2F191CE14000}.Debug|Any CPU.Build.0 = Debug|Any CPU {5B7C6367-7B41-48A6-9A71-2F191CE14000}.Release|Any CPU.ActiveCfg = Release|Any CPU {5B7C6367-7B41-48A6-9A71-2F191CE14000}.Release|Any CPU.Build.0 = Release|Any CPU + {A030CF57-DAA0-4662-B836-3CFC2CDCD9FE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A030CF57-DAA0-4662-B836-3CFC2CDCD9FE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A030CF57-DAA0-4662-B836-3CFC2CDCD9FE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A030CF57-DAA0-4662-B836-3CFC2CDCD9FE}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Sln.Iot/Program.cs b/Sln.Iot/Program.cs index f64080b..cd8b3fa 100644 --- a/Sln.Iot/Program.cs +++ b/Sln.Iot/Program.cs @@ -7,7 +7,10 @@ using Sln.Iot.Config; using Sln.Iot.Repository; using Sln.Iot.Serilog; using Sln.Iot.Socket; +using Sln.Iot.Serial; using TouchSocket.Sockets; +using TouchSocket.Core; +using Fleck; namespace Sln.Iot { @@ -32,6 +35,9 @@ namespace Sln.Iot var _server = ServiceProvider.GetService(); _server.Init(appConfig.listernPort); + var _webSocket = ServiceProvider.GetService(); + _webSocket.Init(); + _server.ReceivedBufferRequestInfoEvent += (client, info) => { bool isRet = false; @@ -67,12 +73,14 @@ namespace Sln.Iot if (_business != null) { Parallel.Invoke( - () => _business.ResponseHandle(client, info.buffer), + //() => _business.ResponseHandle(client, info.buffer), () => _business.BufferAnalysis(client, info, bodyLength) ); } }; + ServiceProvider.UseSerialPortExtensions(); + await Task.Delay(-1); } @@ -88,6 +96,19 @@ namespace Sln.Iot return ap; }); + //services.AddSingleton(provider => + //{ + // TcpClient tcpClient = new TcpClient(); + // tcpClient.Setup(new TouchSocketConfig() + // .SetRemoteIPHost("127.0.0.1:6000") + // .ConfigureContainer(a => + // { + // a.AddConsoleLogger(); + // })); + // tcpClient.Connect(); + // return tcpClient; + //}); + Assembly[] assemblies = { Assembly.LoadFrom("Sln.Iot.Repository.dll"), @@ -100,7 +121,7 @@ namespace Sln.Iot .AddClasses() .AsImplementedInterfaces() .AsSelf() - .WithTransientLifetime()); + .WithSingletonLifetime()); services.AddSingleton(typeof(SerilogHelper)); services.AddSingleton(typeof(TcpService)); diff --git a/Sln.Iot/Sln.Iot.csproj b/Sln.Iot/Sln.Iot.csproj index ee5f903..e953348 100644 --- a/Sln.Iot/Sln.Iot.csproj +++ b/Sln.Iot/Sln.Iot.csproj @@ -14,6 +14,7 @@ + @@ -27,6 +28,9 @@ PreserveNewest + + Always + diff --git a/Sln.Iot/appsettings.json b/Sln.Iot/appsettings.json index 8fb0454..880627a 100644 --- a/Sln.Iot/appsettings.json +++ b/Sln.Iot/appsettings.json @@ -4,16 +4,22 @@ "listernPort": 6000, "virtualFlag": true, "virtualValue": 9999999, - "electricTimeInterval": 1, //电力数据采集间隔,小于间隔数据不保存,单位:分钟 - "fluidTimeInterval": 1, //流体数据采集间隔,小于间隔数据不保存,单位:分钟 + "receiveDevice": "E0020", + "electricTimeInterval": 1, //电力数据采集间隔,小于间隔数据不保存,单位:分钟 + "fluidTimeInterval": 1, //流体数据采集间隔,小于间隔数据不保存,单位:分钟 "SqlConfig": [ { - "configId": "tao_iot", //tao:青岛胶东机场简称 - "dbType": 0, //tidb按照 mysql 去连接 + "configId": "tao_iot", //tao:青岛胶东机场简称 + "dbType": 0, //tidb按照 mysql 去连接 "isFlag": true, //"connStr": "server=127.0.0.1;Port=4000;Database=tao_iot;Uid=root;" //Pwd=haiwei@123; - "connStr": "server=1.13.177.47;Port=3306;Database=tao_iot;Uid=root;Pwd=Haiwei123456;" + "connStr": "server=1.13.177.47;Port=3306;Database=tao_iot;Uid=root;Pwd=Haiwei123456;" } ], + "serialPortConfig": { + "PortName": "COM1", + "BaudRate": 9600, + "DataBits": 8 + } } } diff --git a/Sln.Iot/libnserial.so.1 b/Sln.Iot/libnserial.so.1 new file mode 100644 index 0000000..5b93c2a Binary files /dev/null and b/Sln.Iot/libnserial.so.1 differ