Files
2025-08-26 08:37:44 +08:00

235 lines
8.8 KiB
C#
Raw Permalink 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 McProtocol.Mitsubishi;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace .Utility.Network.Mitsubishi
{
internal class MC3EServer
{
public delegate void DataChanged(int address);
public delegate void LogChanged(string message);
public event DataChanged DataChangedEvent;
public event LogChanged LogChangedEvent;
private TcpListener _listener;
private bool _isRunning;
public static ushort[] _dataStorage = new ushort[65535]; // 初始化数据存储区; // 65535个地址数据类型为ushort
public MC3EServer(int port)
{
_listener = new TcpListener(IPAddress.Any, port);
}
public void Start()
{
new Thread(() =>
{
_isRunning = true;
_listener.Start();
LogChangedEvent?.Invoke("MC-3E Protocol Server started...");
while (_isRunning)
{
try
{
TcpClient client = _listener.AcceptTcpClient();
Thread clientThread = new Thread(new ParameterizedThreadStart(HandleClient));
clientThread.Start(client);
}
catch (SocketException)
{
continue;
}
catch (ThreadInterruptedException)
{
break;
}
}
}).Start();
}
public void Stop()
{
_isRunning = false;
_listener.Stop();
}
private void HandleClient(object obj)
{
TcpClient client = (TcpClient)obj;
try
{
NetworkStream stream = client.GetStream();
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = stream.Read(buffer, 0, buffer.Length)) != 0)
{
LogChangedEvent?.Invoke($"Received {bytesRead} bytes from client.");
// 解析MC-3E协议请求
byte[] response = ProcessMc3ERequest(buffer, bytesRead, out bool write, out int address);
// 发送响应
stream.Write(response, 0, response.Length);
if (write) Task.Run(() => DataChangedEvent?.Invoke(address));
}
}
catch (Exception ex)
{
LogChangedEvent?.Invoke(ex.Message);
}
client.Close();
}
private byte[] ProcessMc3ERequest(byte[] request, int length, out bool write, out int address)
{
// MC-3E协议帧格式示例
// 子头 (2字节) + 网络编号 (1字节) + PLC编号 (1字节) + 请求目标模块IO编号 (2字节) + 请求目标模块站号 (1字节)
// + 请求数据长度 (2字节) + 请求数据 (N字节)
// 解析请求数据
if (length < 8)
{
LogChangedEvent?.Invoke($"Invalid request length.");
write = false;
address = 0;
return BuildErrorResponse();
}
McFrame mcFrame = new McFrame();
mcFrame.Frame = BitConverter.ToUInt16(request, 0); // 假设前两字节为帧类型 0x0050 80
mcFrame.NetworkNumber = request[2]; // 假设第3字节为网络编号
mcFrame.PcNumber = request[3]; // 假设第4字节为PLC编号
mcFrame.IoNumber = BitConverter.ToUInt16(request, 4); // 假设第5-6字节为模块IO编号
mcFrame.ChannelNumber = request[6]; // 假设第7字节为模块站号
int dataLength = BitConverter.ToUInt16(request, 7); // 假设第8-9字节为数据长度
mcFrame.CpuTimer = BitConverter.ToUInt16(request, 9); // 假设第10-11字节为CPU监视定时器
int mainCommand = BitConverter.ToUInt16(request, 11); // 假设第12-13字节为主命令
int subCommand = BitConverter.ToUInt16(request, 13); // 假设第14-15字节为子命令
mcFrame.Address = request[17] << 16 | request[16] << 8 | request[15];
mcFrame.Type = (PlcDeviceType)request[18];
mcFrame.Size = BitConverter.ToUInt16(request, 19);
byte[] data = new byte[mcFrame.Size * 2];
for (int i = 0; i < data.Length; i++)
{
data[i] = request[21 + i];
}
mcFrame.Data = data;
// 处理读请求
if (mainCommand == 0x0401) //读命令
{
LogChangedEvent?.Invoke($"Read request: Address={mcFrame.Address},Type={mcFrame.Type} Count={mcFrame.Size}");
write = false;
address = 0;
return HandleReadRequest(mcFrame);
}
// 处理写请求
else if (mainCommand == 0x1401) // 写命令
{
LogChangedEvent?.Invoke($"Write request: Address={mcFrame.Address},Type={mcFrame.Type} Count={mcFrame.Size}");
write = true;
address = mcFrame.Address;
return HandleWriteRequest(mcFrame); ;
}
else
{
LogChangedEvent?.Invoke("Unknown command.");
write = false;
address = 0;
return BuildErrorResponse();
}
}
class McFrame
{
public ushort Frame { get; set; } // 头部
public byte NetworkNumber { get; set; } // 网络号码
public byte PcNumber { get; set; } // 电脑号码
public ushort IoNumber { get; set; } // 请求单元 I/O 编号/O番号
public byte ChannelNumber { get; set; } // 请求单位站号
public ushort CpuTimer { get; set; } // 中央处理器监控计时器
public int Address { get; set; } // 地址
public PlcDeviceType Type { get; set; } // 类型
public uint Size { get; set; } // 大小
public byte[] Data { get; set; }
}
private byte[] HandleReadRequest(McFrame mcFrame)
{
// 读取数据
List<byte> responseData = new List<byte>(); // 每个ushort占2字节
for (int i = 0; i < mcFrame.Size; i++)
{
byte[] valueBytes = BitConverter.GetBytes(_dataStorage[mcFrame.Address + i]);
responseData.AddRange(valueBytes);
}
// 构建响应帧
return BuildSuccessResponse(mcFrame, responseData.ToArray());
}
private byte[] HandleWriteRequest(McFrame mcFrame)
{
for (int i = 0; i < mcFrame.Size; i++)
{
_dataStorage[mcFrame.Address + i] = BitConverter.ToUInt16(mcFrame.Data, i * 2);
}
LogChangedEvent?.Invoke($"Write successful: Address={mcFrame.Address}, Length={mcFrame.Size}");
return BuildSuccessResponse(mcFrame, new byte[0]); // 写入成功,返回空响应
}
private byte[] BuildSuccessResponse(McFrame mcFrame, byte[] responseData)
{
// 构建成功响应帧
List<byte> response = new List<byte>();
mcFrame.Frame = 0x00D0;
response.AddRange(BitConverter.GetBytes(mcFrame.Frame));//0 1
response.Add(mcFrame.NetworkNumber); //2
response.Add(mcFrame.PcNumber); //3
response.AddRange(BitConverter.GetBytes(mcFrame.IoNumber)); //4 5
response.Add(mcFrame.ChannelNumber);//6
//response.AddRange(BitConverter.GetBytes(mcFrame.CpuTimer));
//数据长度
response.AddRange(BitConverter.GetBytes((short)(responseData.Length + 2)));//7 8
response.AddRange(BitConverter.GetBytes((short)0));//9 10结束符号
response.AddRange(responseData);
return response.ToArray();
}
private byte[] BuildErrorResponse()
{
// 构建错误响应帧
byte[] response = new byte[8];
// 子头 (2字节)
response[0] = 0xD0;
response[1] = 0x00;
// 网络编号 (1字节)
response[2] = 0x00;
// PLC编号 (1字节)
response[3] = 0xFF;
// 模块IO编号 (2字节)
response[4] = 0x00;
response[5] = 0x00;
// 模块站号 (1字节)
response[6] = 0x00;
// 响应数据长度 (2字节)
response[7] = 0x00;
response[8] = 0x00;
return response;
}
}
}