372 lines
14 KiB
C#
372 lines
14 KiB
C#
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}");
|
||
}
|
||
|
||
}
|
||
}
|
||
|
||
|
||
}
|
||
|
||
|
||
|