2024-11-26 13:45:28 +08:00

372 lines
14 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using DotNetty.Buffers;
using DotNetty.Transport.Channels;
using Newtonsoft.Json.Linq;
using Serilog;
using System.Collections;
using System.Diagnostics;
using System.Net;
using System.Text;
using Yunda.SOMS.OperationsMainSiteGatewayServer.TcpSocket.Models;
using Yunda.SOMS.OperationsMainSiteGatewayServer.TcpSocket.TestData;
namespace Yunda.SOMS.OperationsMainSiteGatewayServer.TcpSocket.Server
{
public class DotNettyServerHandler : SimpleChannelInboundHandler<IByteBuffer>
{
// 定义设备信息类
private class DeviceInfo
{
public bool Status { get; set; } // 设备状态(运行或离线)
public DateTime LastUpdate { get; set; } // 最后更新时间
public int OfflineCount { get; set; } // 连续离线计数
}
private IChannelHandlerContext _context;
// 定义事件,事件处理程序包含消息和功能描述
private readonly Dictionary<byte, IChannelHandlerContext> _connections;
private readonly Action<byte, byte[], byte> _onMessageReceived;
private readonly Action<byte> _onDeviceConnection;
Dictionary<byte, DateTime> _deviceRunStates = new Dictionary<byte, DateTime>();
byte _serverAddr;
public DotNettyServerHandler(Dictionary<byte, IChannelHandlerContext> connections,
Action<byte, byte[], byte> onMessageReceived, Action<Dictionary<byte, int[]>> deviceBoardStatesAction, byte addr)
{
_serverAddr = addr;
_connections = connections;
_onMessageReceived = onMessageReceived;
//_onDeviceConnection = onDeviceConnection;
Task.Factory.StartNew(async () =>
{
while (true)
{
try
{
CheckAndUpdateDeviceStates();
SendTestData();
await Task.Delay(1000);
}
catch (Exception ex)
{
//MonitoringEventBus.LogHandler($"客户端连接错误:{ex.StackTrace}", "定值错误信息");
}
}
}, TaskCreationOptions.LongRunning);
}
byte heartbeatDelayCount = 0;
byte heartbeatCount = 0;
async void CheckAndUpdateDeviceStates()
{
try
{
if (heartbeatDelayCount ==0)
{
await HandleHeartbeat(_context, _serverAddr, heartbeatCount);
foreach (var entry in _deviceRunStates)
{
var deviceAddress = entry.Key;
var date = entry.Value;
// 检查是否超过10秒未更新
if ((DateTime.Now - date).TotalSeconds > 65)
{
Log.Warning($"[{DateTime.Now}] {deviceAddress} 判定为离线超过65秒未更新");
await _context.CloseAsync();
_connections.Remove(deviceAddress);
}
}
}
else
{
heartbeatDelayCount++;
}
if (heartbeatDelayCount == 30)
{
heartbeatDelayCount = 0;
}
}
catch (Exception ex)
{
Log.Error(ex,$"状态检测");
}
}
int sequence = 31;
void SendTestData()
{
var data = SimulateData.SendProductionInformation();
// 创建一个应用控制字实例
ApplicationControlWord controlWord = new ApplicationControlWord(fir: true, fin: true, con: true, sequence: sequence);
// 将控制字转换为字节
byte controlByte = controlWord.ToByte();
Log.Information($"Control Byte: 0x{controlByte:X2}"); // 输出控制字的十六进制表示
SendCustomMessageAsync(_context, _serverAddr, controlByte, 1,data);
sequence--;
if (sequence == 15)
{
sequence = 31;
}
}
public async override void ChannelActive(IChannelHandlerContext context)
{
try
{
string clientIp = context.Channel.RemoteAddress.ToString();
Log.Information($"客户端连接:{clientIp}", "103客户端发送消息");
await HandleHeartbeat(context, _serverAddr, heartbeatCount);
base.ChannelActive(context);
}
catch (Exception ex)
{
Log.Information($"{ex.StackTrace}", "103客户端错误信息");
}
}
public override void ChannelInactive(IChannelHandlerContext context)
{
try
{
string clientIp = context.Channel.RemoteAddress.ToString();
Log.Information($"客户端断开连接:{clientIp}", "103客户端发送消息");
base.ChannelInactive(context);
}
catch (Exception ex)
{
Log.Information($"{ex.StackTrace}", "103客户端错误信息");
}
}
protected async override void ChannelRead0(IChannelHandlerContext ctx, IByteBuffer msg)
{
_context = ctx;
try
{
byte startByte = msg.ReadByte();
ushort length = msg.ReadUnsignedShort();
byte address = msg.ReadByte();
if (length == 6) //心跳
{
HandleHeatbeatInfo(msg, address, ctx);
return;
}
byte controlWord = msg.ReadByte();
byte functionType = msg.ReadByte();
// 计算数据长度
int dataLength = length - 6;
if (msg.ReadableBytes < dataLength)
{
// 数据不足,等待更多数据
return;
}
byte[] data = new byte[dataLength];
msg.ReadBytes(data);
string clientIp = ctx.Channel.RemoteAddress.ToString();
Log.Information($"客户端ip地址{clientIp}");
string description = FunctionCodeDescriptions.GetDescription(functionType);
Log.Information($"地址 {address} 功能码:{functionType} 数据长度:{dataLength}", "103客户端消息");
Log.Information($"地址 {address} 功能码:{functionType} 数据长度:{dataLength}", "103客户端消息");
Task.Run(() => _onMessageReceived?.Invoke(address, data, functionType));
HandleFunctionCodeAsync(functionType, address, controlWord, data, ctx);
}
catch (Exception ex)
{
Log.Information($"Error in ChannelRead0: {ex.StackTrace}", "103客户端发送消息");
}
}
private void HandleHeatbeatInfo(IByteBuffer msg, byte address, IChannelHandlerContext ctx)
{
byte handleHearfunctionType = msg.ReadByte();
byte handleHeardata = msg.ReadByte();
if (handleHearfunctionType == 7)
{
if (_connections.ContainsKey(address))
{
_connections[address] = ctx;
}
else
{
_connections.Add(address, ctx);
}
Log.Information($"接受到心跳信息:地址:{address} 序列号:{handleHeardata}");
if (_deviceRunStates.ContainsKey(address))
{
_deviceRunStates[address] = DateTime.Now;
}
else
{
_deviceRunStates.Add(address, DateTime.Now);
}
//await HandleHeartbeat(ctx, _serverAddr, handleHeardata);
}
}
private async Task HandleFunctionCodeAsync(byte functionType, byte address, byte controlWord, byte[] data, IChannelHandlerContext ctx)
{
switch (functionType)
{
case 0:
break;
case 1: //装置生产信息报文
Log.Information($"收到装置生产信息报文: {BitConverter.ToString(data)}");
break;
case 2:
// 装置版本信息报文
break;
case 3: //装置运行信息报文
Log.Information($"版本信息: {Encoding.ASCII.GetString(data)}", "103客户端信息");
break;
case 4:
// 板件状态自诊断信息报文
break;
case 5:
//装置运行状态自诊断信息报文
//MonitoringEventBus.LogHandler($"开入开出信息: {Encoding.ASCII.GetString(data)}", "103客户端信息");
break;
case 6:
//二次回路智能诊断信息报文
//await UpdateDeviceCommunicationStateAsync(address, data, ctx);
break;
case 10:
// 信息召唤
Log.Information("信息召唤");
break;
default:
//MonitoringEventBus.LogHandler("未知的功能码", "103客户端信息");
break;
}
}
/// <summary>
/// 心跳信息
/// </summary>
/// <param name="context"></param>
/// <param name="address"></param>
/// <param name="data"></param>
/// <returns></returns>
private async Task HandleHeartbeat(IChannelHandlerContext context, byte address, byte data)
{
if (context == null)
{
return;
}
try
{
// 构建数据帧
byte[] buffer = new byte[6];
// 填入启动字符
buffer[0] = 0x68;
// 填入长度假设数据部分为50字节2字节长度字段+1字节地址+1字节标志位
byte dataLength = 6;
buffer[1] = 0; // 长度低字节
buffer[2] = dataLength; // 长度高字节
// 填入地址、应用控制字、功能类型
buffer[3] = address; // 示例地址
buffer[4] = 7; // 示例功能类型
buffer[5] = data;
// 填入数据部分
IByteBuffer wrappedBuffer = Unpooled.WrappedBuffer(buffer);
// 发送数据
await context.WriteAndFlushAsync(wrappedBuffer);
Log.Information($"发送心跳信息:序号:{data} 地址:{address}");
}
catch (Exception ex)
{
Log.Error(ex, "心跳信息");
}
heartbeatCount++;
// 防止溢出
if (heartbeatCount == 0xFF) // 如果超过 255则重置为 0
{
heartbeatCount = 0;
}
}
public override async void ExceptionCaught(IChannelHandlerContext context, Exception ex)
{
try
{
//MonitoringEventBus.LogHandler(ex.Message, "103客户端错误消息");
Log.Information(ex, "错误消息");
await context.CloseAsync();
}
catch (Exception ex1)
{
//MonitoringEventBus.LogHandler($"Error: {ex1.StackTrace}", "关闭tcp客户端");
}
}
// 发送自定义消息
public async Task SendCustomByteMessageAsync(IChannelHandlerContext context, byte address, byte controlWord, byte functionType, byte data)
{
try
{
await SendCustomMessageAsync(context, address, controlWord, functionType, new byte[] { data });
}
catch (Exception ex)
{
Log.Information($"Error: {ex.StackTrace}");
}
}
// 发送自定义消息
public async Task SendCustomMessageAsync(IChannelHandlerContext context, byte address, byte controlWord, byte functionType, byte[] data)
{
try
{
if (context==null|| data == null|| data.Length==0)
{
return;
}
ushort dataLength = (ushort)(6 + data.Length);
// 构建数据帧
byte[] buffer = new byte[dataLength];
// 填入启动字符
buffer[0] = 0x68;
// 填入长度假设数据部分为50字节2字节长度字段+1字节地址+1字节标志位
byte[] bytes = BitConverter.GetBytes(dataLength);
buffer[1] = bytes[0]; // 长度低字节
buffer[2] = bytes[1]; // 长度高字节
// 填入地址、应用控制字、功能类型
buffer[3] = address; // 示例地址
buffer[4] = controlWord; // 示例应用控制字
buffer[5] = functionType; // 示例功能类型
for (ushort i = 6; i < dataLength; i++)
{
buffer[i] = data[i-6];
}
// 填入数据部分
IByteBuffer wrappedBuffer = Unpooled.WrappedBuffer(buffer);
// 发送数据
await context.WriteAndFlushAsync(wrappedBuffer);
string hexString = BitConverter.ToString(buffer).Replace("-", " ");
Log.Information($"地址:{address} 功能码:{functionType} 数据:{hexString}");
await Task.Delay(100);
}
catch (Exception ex)
{
Log.Information($"Error: {ex.StackTrace}");
}
}
}
}