#region ModbusTCP客户端
public class ModbusTCP : IDisposable
{
#region 字段与属性
private TcpClient tcpClient; //----------------------------------------TCP 客户端实例
private NetworkStream stream; //---------------------------------------网络流,用于收发数据
private Thread receiveTh; //-------------------------------------------接收数据线程
private bool connected = false; //-------------------------------------当前连接状态
private readonly object socketLock = new object(); //------------------锁,保证线程安全
private ManualResetEvent receiveEvent = new ManualResetEvent(false);
private byte[] receiveBuffer = new byte[256]; //-----------------------接收缓冲区
private byte[] responseBytes; //---------------------------------------存储完整响应报文
private byte[] currentResponse; //-------------------------------------当前接收到的原始数据
private static ushort transId = 1; //----------------------------------Modbus 事务ID,每次递增
string nameClient; //--------------------------------------------------客户端名称
private string currentIp; //-------------------------------------------当前连接IP
private int currentPort; //--------------------------------------------当前连接端口
private volatile bool disposed = false; //-----------------------------是否已释放资源
private readonly object responseLock = new object(); //----------------响应锁
public del LogInfoDisplay; //-------------------------------------------委托变量-信息显示
public InfoMsg info = new InfoMsg("", ""); //---------------------------显示信息
public event Action<string> returnReceiveEvent;//-----------------------数据接收事件
//自动重连
private bool autoReconnect = true; //----------------------------------自动重连,默认开启
private int reconnectInterval = 5000; //-------------------------------重连间隔
private int maxReconnectAttempts = 5; //-------------------------------重连次数
public bool AutoReconnect
{
get => autoReconnect;
set { lock (socketLock) autoReconnect = value; }
}
public int ReconnectInterval { get; set; } = 3000; //------------------重连间隔
public int Timeout { get; set; } = 5000; //----------------------------超时时间(毫秒)
public int AddressOffset { get; set; } = 0; //-------------------------地址偏移,默认M0对应0(如 D100 实际地址为100)
private static readonly object idLock = new object();
private static ushort GenerateTransactionId()
{
lock (idLock)
{
return transId++;
}
}
private System.Timers.Timer heartbeatTimer; //-------------------------心跳定时器
#endregion
#region 数据类型枚举
/// <summary>
/// 读写数据类型
/// 字 = 16位(short),双字 = 32位(int)
/// </summary>
public enum DataType : short
{
字 = 1,
双字 = 2
}
/// <summary>
/// 寄存器内字节顺序(用于16位数据)
/// </summary>
public enum ByteOrder
{
BigEndian, // 高字节在前(AB)
LittleEndian // 低字节在前(BA)
}
/// <summary>
/// 定义32位数据在两个16位寄存器中的存储顺序
/// </summary>
public enum WordOrder
{
/// <summary>
/// 高字在前,低字在后(如西门子、标准 Modbus)
/// 示例:0x12345678 → [0x1234][0x5678]
/// </summary>
HighFirst = 1, // 高位寄存器在前(ABCD)
/// <summary>
/// 低字在前,高字在后(如三菱、欧姆龙常见)
/// 示例:0x12345678 → [0x5678][0x1234]
/// </summary>
LowFirst = 2 // 低位寄存器在前(CDAB)
}
#endregion
#region 客户端初始化
/// <summary>
/// 客户端初始化
/// </summary>
/// <param name="name">客户端名称</param>
/// <param name="receiveData">接收数据事件</param>
/// <param name="info">消息显示事件</param>
/// <param name="addressOffset">地址偏移量(如M0对应0,则为0;若PLC从1开始则设为-1)</param>
public void Initialize(string name = "客户端", Action<string> receiveData = null, del info = null,int addressOffset = 0)
{
nameClient = name; //-----------------------客户端名称
returnReceiveEvent += receiveData; //-------接收数据事件
LogInfoDisplay += info; //------------------消息事件
this.info.msgType = nameClient; //----------log消息类型
this.AddressOffset = addressOffset; //------地址偏移设置
}
/// <summary>
/// 配置自动重连
/// </summary>
/// <param name="enabled">开启重连true/false</param>
/// <param name="intervalMs">重连间隔</param>
/// <param name="maxAttempts">重连次数</param>
public void ConfigureAutoReconnect(bool enabled, int intervalMs = 5000, int maxAttempts = 5)
{
autoReconnect = enabled;
reconnectInterval = intervalMs;
maxReconnectAttempts = maxAttempts;
AutoReconnect = autoReconnect;
ReconnectInterval = reconnectInterval;
}
/// <summary>
/// 配置并启用心跳功能
/// 通过定时器周期性向指定地址写入0/1交替信号,用于PLC监控连接状态
/// </summary>
/// <param name="enabled">是否启用心跳功能,true=启用,false=禁用</param>
/// <param name="address">心跳信号写入的PLC地址,如"D750"</param>
/// <param name="intervalMs">心跳间隔时间,单位毫秒,默认500ms</param>
/// <example>
/// // 启用心跳,每500ms向D750地址写入0/1交替信号
/// modbusTCP.ConfigureHeartbeat(true, "D750", 500);
///
/// // 禁用心跳功能
/// modbusTCP.ConfigureHeartbeat(false);
/// </example>
public void ConfigureHeartbeat(bool enabled = true, string address = "D700", int intervalMs = 500)
{
// 停止并释放之前的心跳定时器,避免多个定时器同时运行
heartbeatTimer?.Stop(); // 停止定时器
heartbeatTimer?.Dispose(); // 释放定时器资源
heartbeatTimer = null;
// 如果禁用心跳,直接返回
if (!enabled) return;
// 创建新的心跳定时器
heartbeatTimer = new System.Timers.Timer(intervalMs);
bool state = false; // 心跳状态标志,用于0/1交替
heartbeatTimer.Elapsed += async (s, e) =>
{
try
{
if (!connected) return;
state = !state;
await WriteSingleRegistersAsync(1, address, state ? 1 : 0);
}
catch (Exception ex)
{
// 异常必须在 async void 中捕获,否则会崩溃整个程序
Console.WriteLine("[Heartbeat] Exception: " + ex.Message);
}
};
heartbeatTimer.Start();
}
#endregion
#region 连接与断开
/// <summary>
/// 连接到 Modbus TCP 服务器
/// </summary>
/// <param name="ip">PLC IP 地址</param>
/// <param name="port">端口,默认 502</param>
/// <returns>是否连接成功</returns>
public bool Connect(string ip, int port = 502)
{
lock (socketLock)
{
if (connected) return true; // 已连接则直接返回
try
{
tcpClient = new TcpClient();
tcpClient.SendTimeout = Timeout;
tcpClient.ReceiveTimeout = Timeout;
var result = tcpClient.BeginConnect(IPAddress.Parse(ip), port, null, null);
var success = result.AsyncWaitHandle.WaitOne(TimeSpan.FromMilliseconds(Timeout));
if (!success)
{
info.msg = $"连接超时 ({ip}:{port})";
LogInfoDisplay?.Invoke(info);
return false;
}
tcpClient.EndConnect(result);
stream = tcpClient.GetStream();
connected = true;
currentIp = ip;
currentPort = port;
StartReceiveThread(); // 启动接收线程
info.msg = $"连接成功 {ip}:{port}";
LogInfoDisplay?.Invoke(info);
return true;
}
catch (Exception ex)
{
info.msg = $"连接失败: {ex.Message}";
LogInfoDisplay?.Invoke(info);
Disconnect();
HandleReconnection();
return false;
}
}
}
/// <summary>
/// 断开连接
/// </summary>
public void Disconnect()
{
lock (socketLock)
{
if (!connected) return;
try
{
connected = false;
receiveEvent.Set();
receiveTh?.Interrupt();
receiveTh = null;
stream?.Close();
tcpClient?.Close();
stream = null;
tcpClient = null;
info.msg = "已断开连接";
LogInfoDisplay?.Invoke(info);
}
catch (Exception ex)
{
info.msg = $"断开异常: {ex.Message}";
LogInfoDisplay?.Invoke(info);
}
}
}
/// <summary>
/// 处理自动重连逻辑(避免阻塞主线程)
/// </summary>
private void HandleReconnection()
{
if (!AutoReconnect || string.IsNullOrEmpty(currentIp)) return;
Task.Run(() =>
{
int attempts = 0;
while (!connected && !disposed && attempts < maxReconnectAttempts)
{
Thread.Sleep(ReconnectInterval);
try
{
if (Connect(currentIp, currentPort))
{
info.msg = "自动重连成功";
LogInfoDisplay?.Invoke(info);
break;
}
attempts++;
}
catch { }
}
});
}
#endregion
#region 接收线程
/// <summary>
/// 启动后台接收线程
/// </summary>
private void StartReceiveThread()
{
receiveTh = new Thread(ReceiveThreadProc)
{
IsBackground = true,
Name = "ModbusTCP接收线程"
};
receiveTh.Start();
}
/// <summary>
/// 接收线程主体:持续监听数据
/// </summary>
private void ReceiveThreadProc()
{
while (connected && !disposed)
{
try
{
if (stream.DataAvailable)
{
// 先读取 MBAP 头(前6字节)
int len = stream.Read(receiveBuffer, 0, 6);
if (len < 6) continue;
// 解析协议数据单元长度(大端)
int pduLength = (receiveBuffer[4] << 8) | receiveBuffer[5];
int totalLength = 6 + pduLength;
// 读取剩余部分
int read = 0;
while (read < pduLength)
{
int n = stream.Read(receiveBuffer, 6 + read, pduLength - read);
if (n <= 0) throw new IOException("连接中断");
read += n;
}
ProcessReceivedData(receiveBuffer, totalLength);
}
else
{
Thread.Sleep(10); // 小延时避免CPU空转
}
}
catch (SocketException ex)
{
// 连接被对端关闭或中断
if (ex.SocketErrorCode == SocketError.ConnectionReset ||
ex.SocketErrorCode == SocketError.ConnectionAborted)
{
info.msg = "连接被远程关闭或异常断开";
LogInfoDisplay?.Invoke(info);
connected = false;
HandleReconnection();
}
else
{
info.msg = $"Socket异常: {ex.Message}";
LogInfoDisplay?.Invoke(info);
}
}
catch (Exception ex)
{
if (connected)
{
info.msg = $"接收数据异常: {ex.Message}";
LogInfoDisplay?.Invoke(info);
connected = false;
HandleReconnection();
}
break;
}
}
}
/// <summary>
/// 处理接收到的数据包
/// </summary>
private void ProcessReceivedData(byte[] data, int length)
{
lock (responseLock)
{
currentResponse = new byte[length];
Array.Copy(data, 0, currentResponse, 0, length);
responseBytes = currentResponse;
receiveEvent.Set(); // 通知等待响应的线程
// 触发接收事件(Hex字符串格式)
if (returnReceiveEvent != null)
{
string hexString = BitConverter.ToString(data, 0, length).Replace("-", " ");
returnReceiveEvent(hexString);
}
}
}
#endregion
#region 发送请求
/// <summary>
/// 发送 Modbus 请求并等待响应
/// </summary>
/// <param name="request">请求报文</param>
/// <param name="expectedResponseLength">期望响应长度(用于校验)</param>
/// <returns>响应字节数组,失败返回 null</returns>
private async Task<byte[]> SendRequestAsync(byte[] request, int expectedResponseLength)
{
try
{
// 假设 tcpClient 已连接,networkStream = tcpClient.GetStream()
if (stream == null || !stream.CanWrite) return null;
// 设置事务ID(大端字节序)
ushort transactionId = GenerateTransactionId(); // 自增或随机生成
request[0] = (byte)(transactionId >> 8);
request[1] = (byte)(transactionId & 0xFF);
// 发送请求
await stream.WriteAsync(request, 0, request.Length).ConfigureAwait(false);
// 读取响应头(至少 9 字节 MBAP + PDU header)
byte[] header = new byte[9];
int read = await ReadExactAsync(stream, header, 0, 9).ConfigureAwait(false);
if (read != 9) return null;
// 解析后续长度(第5-6字节)
int pduLength = (header[4] << 8) | header[5]; // 后续数据长度
int totalResponseLength = 6 + pduLength; // MBAP(6) + PDU
if (totalResponseLength > expectedResponseLength)
{
// 扩展缓冲区以接收完整响应
byte[] fullResponse = new byte[totalResponseLength];
Array.Copy(header, 0, fullResponse, 0, 9);
int remaining = totalResponseLength - 9;
read = await ReadExactAsync(stream, fullResponse, 9, remaining).ConfigureAwait(false);
if (read != remaining) return null;
return fullResponse;
}
return header; // 如果已足够
}
catch (Exception ex)
{
Console.WriteLine("通信异常: " + ex.Message);
connected = false;
return null;
}
}
/// <summary>
/// 确保异步读取指定数量的字节
/// </summary>
private async Task<int> ReadExactAsync(NetworkStream stream, byte[] buffer, int offset, int count)
{
int totalRead = 0;
while (totalRead < count)
{
int read = await stream.ReadAsync(buffer, offset + totalRead, count - totalRead).ConfigureAwait(false);
if (read == 0) break; // 连接关闭
totalRead += read;
}
return totalRead;
}
#endregion
#region 地址解析
/// <summary>
/// 解析地址字符串如 "D100" -> 实际地址 99(若偏移-1)
/// </summary>
private ushort ParseAddress(string address)
{
if (string.IsNullOrEmpty(address) || address.Length < 2)
throw new ArgumentException("地址格式错误");
char type = char.ToUpper(address[0]);
string numPart = address.Substring(1).Split('.')[0]; // 忽略位索引
if (!int.TryParse(numPart, out int rawAddr))
throw new ArgumentException("地址数值无效");
int baseAddress;
switch (type)
{
case 'D':
case 'M':
case 'X':
case 'Y':
baseAddress = 0x0000;
break;
default:
throw new ArgumentException($"不支持的地址类型: {type}");
}
int finalAddr = baseAddress + rawAddr + AddressOffset;
if (finalAddr < 0 || finalAddr > 65535)
throw new ArgumentException("地址超出范围");
return (ushort)finalAddr;
}
#endregion
#region 读取线圈寄存器(M寄存器)
/// <summary>
/// 读取线圈寄存器(功能码01)
/// </summary>
/// <param name="ID">客户端ID</param>
/// <param name="address">地址</param>
/// <param name="length">长度</param>
/// <returns>值</returns>
public async Task<bool[]> ReadCoilsAsync(byte ID, string address, int length)
{
if (length <= 0) length = 1;
if (length > 2048) length = 2048;
ushort addr = ParseAddress(address);
int byteCount = (length + 7) / 8;
byte[] request = new byte[12];
// 构建 Modbus TCP 请求报文
request[0] = 0; request[1] = 0; //----------事务ID(由 SendRequestAsync 填充或自动生成)
request[2] = 0x00; request[3] = 0x00; //----协议ID
request[4] = 0x00; request[5] = 0x06; //----后续长度:6字节(PDU)
request[6] = ID; //-------------------------单元标识符ID
request[7] = 0x01; //-----------------------功能码(读线圈)
request[8] = (byte)(addr >> 8); //----------起始地址高字节
request[9] = (byte)(addr & 0xFF); //--------起始地址低字节
request[10] = (byte)(length >> 8); //-------数量高字节
request[11] = (byte)(length & 0xFF); //-----数量低字节
byte[] response = await SendRequestAsync(request, 9 + byteCount).ConfigureAwait(false);
if (response == null || response.Length < 9 + byteCount) return null;
bool[] result = new bool[length];
for (int i = 0; i < length; i++)
{
int byteIdx = i / 8;
int bitIdx = i % 8;
result[i] = (response[9 + byteIdx] & (1 << bitIdx)) != 0;
}
return result;
}
#endregion
#region 读取输入状态
/// <summary>
/// 读取输入状态(功能码02)
/// </summary>
/// <param name="ID">客户端ID</param>
/// <param name="address">地址</param>
/// <param name="length">长度</param>
/// <returns>值</returns>
public async Task<bool[]> ReadDiscreteInputsAsync(byte ID, string address, int length)
{
if (length <= 0) length = 1;
if (length > 2048) length = 2048;
ushort addr = ParseAddress(address);
int byteCount = (length + 7) / 8;
byte[] request = new byte[12];
// 构建 Modbus TCP 请求报文
request[0] = 0; request[1] = 0; //----------事务ID(由 SendRequestAsync 填充或自动生成)
request[2] = 0x00; request[3] = 0x00; //----协议ID
request[4] = 0x00; request[5] = 0x06; //----后续长度:6字节(PDU)
request[6] = ID; //-------------------------单元标识符ID
request[7] = 0x02; //-----------------------功能码(读离散输入)
request[8] = (byte)(addr >> 8); //----------起始地址高字节
request[9] = (byte)(addr & 0xFF); //--------起始地址低字节
request[10] = (byte)(length >> 8); //-------数量高字节
request[11] = (byte)(length & 0xFF); //-----数量低字节
byte[] response = await SendRequestAsync(request, 9 + byteCount).ConfigureAwait(false);
if (response == null || response.Length < 9 + byteCount) return null;
bool[] result = new bool[length];
for (int i = 0; i < length; i++)
{
int byteIdx = i / 8;
int bitIdx = i % 8;
result[i] = (response[9 + byteIdx] & (1 << bitIdx)) != 0;
}
return result;
}
#endregion
#region 读取保持寄存器(D寄存器)
/// <summary>
/// 读取保持寄存器(功能码03)
/// </summary>
/// <param name="ID">客户端ID</param>
/// <param name="address">地址</param>
/// <param name="length">长度</param>
/// <param name="type">类型</param>
/// <returns>值</returns>
public async Task<int[]> ReadHoldingRegistersAsync(byte ID, string address, int length, DataType type = DataType.字)
{
if (length <= 0) length = 1;
if (length > 125) length = 125;
ushort addr = ParseAddress(address);
ushort regCount = (ushort)(length * (ushort)type);
byte[] request = new byte[12];
// 构建 Modbus TCP 请求报文
request[0] = 0; request[1] = 0; //----------事务ID(由 SendRequestAsync 填充或自动生成)
request[2] = 0x00; request[3] = 0x00; //----协议ID
request[4] = 0x00; request[5] = 0x06; //----后续长度:6字节(PDU)
request[6] = ID; //-------------------------单元标识符ID
request[7] = 0x03; //-----------------------功能码(读保持寄存器)
request[8] = (byte)(addr >> 8); //----------起始地址高字节
request[9] = (byte)(addr & 0xFF); //--------起始地址低字节
request[10] = (byte)(regCount >> 8); //-----数量高字节
request[11] = (byte)(regCount & 0xFF); //---数量低字节
byte[] response = await SendRequestAsync(request, 9 + regCount * 2).ConfigureAwait(false);
if (response == null || response.Length < 9 + regCount * 2) return null;
return ParseRegisterResponse(response, length, type);
}
/// <summary>
/// 解析寄存器响应(16位和32位)
/// </summary>
private int[] ParseRegisterResponse(byte[] response, int length, DataType type, ByteOrder byteOrder = ByteOrder.BigEndian, WordOrder wordOrder = WordOrder.HighFirst)
{
int[] result = new int[length];
if (type == DataType.字)
{
for (int i = 0; i < length; i++)
{
int dataIndex = 9 + i * 2;
if (dataIndex + 1 < response.Length)
{
ushort value;
if (byteOrder == ByteOrder.BigEndian)
{
value = (ushort)(response[dataIndex] << 8 | response[dataIndex + 1]); // AB
}
else
{
value = (ushort)(response[dataIndex] | response[dataIndex + 1] << 8); // BA
}
result[i] = (short)value; // 直接转换为有符号short
}
}
}
else //双字
{
for (int i = 0; i < length; i++)
{
int dataIndex = 9 + i * 4;
if (dataIndex + 3 < response.Length)
{
// Step 1: 先按字节序读两个16位寄存器
ushort reg1, reg2;
if (byteOrder == ByteOrder.BigEndian)
{
reg1 = (ushort)(response[dataIndex] << 8 | response[dataIndex + 1]);
reg2 = (ushort)(response[dataIndex + 2] << 8 | response[dataIndex + 3]);
}
else
{
reg1 = (ushort)(response[dataIndex + 1] << 8 | response[dataIndex]);
reg2 = (ushort)(response[dataIndex + 3] << 8 | response[dataIndex + 2]);
}
// Step 2: 再按字序组合
uint value;
if (wordOrder == WordOrder.HighFirst)
{
value = ((uint)reg1 << 16) | reg2;
}
else
{
value = ((uint)reg2 << 16) | reg1;
}
result[i] = (int)value; // 自动处理补码
}
}
}
return result;
}
#endregion
#region 读取输入寄存器
/// <summary>
/// 读取输入寄存器(功能码04)
/// </summary>
/// <param name="ID">客户端ID</param>
/// <param name="address">地址</param>
/// <param name="length">长度</param>
/// <param name="type">类型</param>
/// <returns>值</returns>
public async Task<int[]> ReadInputRegistersAsync(byte ID, string address, int length, DataType type = DataType.字)
{
if (length <= 0) length = 1;
if (length > 125) length = 125;
ushort addr = ParseAddress(address);
ushort regCount = (ushort)(length * (ushort)type);
byte[] request = new byte[12];
// 构建 Modbus TCP 请求报文
request[0] = 0; request[1] = 0; //----------事务ID(由 SendRequestAsync 填充或自动生成)
request[2] = 0x00; request[3] = 0x00; //----协议ID
request[4] = 0x00; request[5] = 0x06; //----后续长度:6字节(PDU)
request[6] = ID; //-------------------------单元标识符ID
request[7] = 0x04; //-----------------------功能码(读输入寄存器)
request[8] = (byte)(addr >> 8); //----------起始地址高字节
request[9] = (byte)(addr & 0xFF); //--------起始地址低字节
request[10] = (byte)(regCount >> 8); //-----数量高字节
request[11] = (byte)(regCount & 0xFF); //---数量低字节
byte[] response = await SendRequestAsync(request, 9 + regCount * 2).ConfigureAwait(false);
if (response == null || response.Length < 9 + regCount * 2) return null;
return ParseRegisterResponse(response, length, type);
}
#endregion
#region 写入单个线圈寄存器值(M寄存器)
/// <summary>
/// 写入单个线圈寄存器值(功能码05)
/// </summary>
/// <param name="ID">客户端ID</param>
/// <param name="address">地址</param>
/// <param name="value">值</param>
public async Task<bool> WriteSingleCoilsAsync(byte ID, string address, bool value)
{
if (!connected) return false;
ushort addr = ParseAddress(address);
byte[] request = new byte[12];
// 构建 Modbus TCP 请求报文
request[0] = 0; request[1] = 0; //----------事务ID(由 SendRequestAsync 填充或自动生成)
request[2] = 0x00; request[3] = 0x00; //----协议ID
request[4] = 0x00; request[5] = 0x06; //----后续长度:6字节(PDU)
request[6] = ID; //-------------------------单元标识符ID
request[7] = 0x05; //-----------------------功能码(写单个线圈)
request[8] = (byte)(addr >> 8); //----------起始地址高字节
request[9] = (byte)(addr & 0xFF); //--------起始地址低字节
request[10] = (byte)(value ? 0xFF : 0x00); //数据(FF表示ON,00表示OFF)
request[11] = 0x00; //----------------------数据(固定0x00)
try
{
byte[] response = await SendRequestAsync(request, 12).ConfigureAwait(false);
return response != null && response.Length >= 12 &&
response[7] == 0x05 && (response[8] << 8 | response[9]) == addr;
}
catch { return false; }
}
#endregion
#region 写入单个保持寄存器值(D寄存器)
/// <summary>
/// 写入单个保持寄存器值(功能码06)(功能码16)
/// </summary>
/// <param name="ID">设备ID</param>
/// <param name="address">地址</param>
/// <param name="value">值(16位或32位)</param>
/// <param name="type">数据类型(字=16位,双字=32位)</param>
/// <returns>是否写入成功</returns>
public async Task<bool> WriteSingleRegistersAsync(byte ID, string address, int value, DataType type = DataType.字)
{
if (!connected) return false;
if (type == DataType.字)
{
return await WriteSingleRegister16(ID, address, (short)value);
}
else
{
return await WriteDoubleRegister32(ID, address, (int)value);
}
}
/// <summary>
/// 16位写入实现(功能码06)
/// </summary>
/// <param name="ID"></param>
/// <param name="address"></param>
/// <param name="value"></param>
/// <returns></returns>
private async Task<bool> WriteSingleRegister16(byte ID, string address, short value)
{
ushort addr = ParseAddress(address);
ushort uvalue = (ushort)value;
byte[] request = new byte[12];
// 构建 Modbus TCP 请求报文
request[0] = 0; request[1] = 0; //----------事务ID(由 SendRequestAsync 填充或自动生成)
request[2] = 0x00; request[3] = 0x00; //----协议ID
request[4] = 0x00; request[5] = 0x06; //----后续长度:6字节(PDU)
request[6] = ID; //-------------------------单元标识符ID
request[7] = 0x06; //-----------------------功能码(写16位保存寄存器)
request[8] = (byte)(addr >> 8); //----------起始地址高字节
request[9] = (byte)(addr & 0xFF); //--------起始地址低字节
request[10] = (byte)(uvalue >> 8); //-------数量高字节
request[11] = (byte)(uvalue & 0xFF); //-----数量低字节
byte[] response = await SendRequestAsync(request, 12).ConfigureAwait(false);
return response != null && response.Length >= 12 &&
response[7] == 0x06 && (response[8] << 8 | response[9]) == addr;
}
/// <summary>
/// 32位写入实现(功能码16)
/// </summary>
/// <param name="ID"></param>
/// <param name="address"></param>
/// <param name="value"></param>
/// <returns></returns>
private async Task<bool> WriteDoubleRegister32(byte ID, string address, int value, WordOrder wordOrder = WordOrder.HighFirst)
{
short hiWord = (short)(value >> 16);
short loWord = (short)(value & 0xFFFF);
short[] words = wordOrder == WordOrder.HighFirst ?
new[] { hiWord, loWord } :
new[] { loWord, hiWord };
return await WriteMultipleRegistersAsync(ID, address, words);
}
#endregion
#region 写入多个线圈寄存器值(M寄存器)
/// <summary>
/// 写入多个线圈寄存器值(功能码15)- 所有线圈写入相同值
/// </summary>
/// <param name="ID">设备ID</param>
/// <param name="address">起始地址(如"M100")</param>
/// <param name="length">写入的线圈数量</param>
/// <param name="value">所有线圈的状态(true=ON, false=OFF)</param>
/// <returns>是否写入成功</returns>
/// <example>
/// WriteMultipleCoils(1, "M100", 2, true);
/// // 写入M100=true, M101=true
/// WriteMultipleCoils(1, "M100", 5, false);
/// // 写入M100~M104全部为false
/// </example>
public async Task<bool> WriteMultipleCoilsAsync(byte ID, string address, int length, bool value)
{
if (!connected) return false;
bool[] values = new bool[length];
for (int i = 0; i < length; i++) values[i] = value;
return await WriteMultipleCoilsAsync(ID, address, values);
}
/// <summary>
/// 写入多个线圈寄存器值(bool数组版本)- 每个线圈独立控制
/// </summary>
/// <param name="ID">设备ID</param>
/// <param name="address">起始地址</param>
/// <param name="values">每个线圈的状态数组</param>
/// <returns>是否写入成功</returns>
/// <example>
/// WriteMultipleCoils(1, "M100", new bool[] { true, false, true });
/// // 写入M100=true, M101=false, M102=true
/// </example>
public async Task<bool> WriteMultipleCoilsAsync(byte ID, string address, bool[] values)
{
if (!connected) return false;
ushort addr = ParseAddress(address);
int byteCount = (values.Length + 7) / 8;
byte[] coilBytes = new byte[byteCount];
for (int i = 0; i < values.Length; i++)
{
if (values[i])
coilBytes[i / 8] |= (byte)(1 << (i % 8));
}
byte[] request = new byte[13 + byteCount];
// 构建 Modbus TCP 请求报文
request[0] = 0; request[1] = 0; //----------事务ID(由 SendRequestAsync 填充或自动生成)
request[2] = 0x00; request[3] = 0x00; //----协议ID
request[4] = (byte)((request.Length - 6) >> 8);
request[5] = (byte)((request.Length - 6) & 0xFF);
request[6] = ID; //-------------------------单元标识符ID
request[7] = 0x0F; //-----------------------功能码(写多线圈寄存器)
request[8] = (byte)(addr >> 8); //----------起始地址高字节
request[9] = (byte)(addr & 0xFF); //--------起始地址低字节
request[10] = (byte)(values.Length >> 8); //数量高字节
request[11] = (byte)(values.Length & 0xFF);//数量低字节
request[12] = (byte)(values.Length * 2);
request[12] = (byte)byteCount;
Array.Copy(coilBytes, 0, request, 13, byteCount);
byte[] response = await SendRequestAsync(request, 12).ConfigureAwait(false);
return response != null && response.Length >= 12;
}
#endregion
#region 写入多个保持寄存器值(D寄存器)
/// <summary>
/// 写入多个保持寄存器值(功能码16) - 所有寄存器写入相同值
/// </summary>
/// <param name="ID">设备ID</param>
/// <param name="address">起始地址(如"D100")</param>
/// <param name="length">写入的寄存器数量</param>
/// <param name="value">所有寄存器的值</param>
/// <param name="type">数据类型(字=16位,双字=32位)</param>
/// <returns>是否写入成功</returns>
/// <example>
/// WriteMultipleRegister(1, "D100", 3, 100);
/// // 写入D100=100, D101=100, D102=100
/// WriteMultipleRegister(1, "D200", 2, 123456789, ReadTypeEnum.双字);
/// // 写入D200-D201=123456789, D202-D203=123456789
/// </example>
public async Task<bool> WriteMultipleRegistersAsync(byte ID, string address, int length, int value, DataType type = DataType.字)
{
if (!connected) return false;
if (type == DataType.字)
{
short[] arr = new short[length];
for (int i = 0; i < length; i++) arr[i] = (short)value;
return await WriteMultipleRegistersAsync(ID, address, arr);
}
else
{
short[] arr = new short[length * 2];
for (int i = 0; i < length; i++)
{
arr[i * 2] = (short)(value >> 16);
arr[i * 2 + 1] = (short)(value & 0xFFFF);
}
return await WriteMultipleRegistersAsync(ID, address, arr);
}
}
/// <summary>
/// 写入多个保持寄存器值(功能码16)
/// </summary>
/// <param name="ID"></param>
/// <param name="address"></param>
/// <param name="values"></param>
/// <returns></returns>
private async Task<bool> WriteMultipleRegistersAsync(byte ID, string address, short[] values)
{
ushort addr = ParseAddress(address);
byte[] request = new byte[13 + values.Length * 2];
// 构建 Modbus TCP 请求报文
request[0] = 0; request[1] = 0; //----------事务ID(由 SendRequestAsync 填充或自动生成)
request[2] = 0x00; request[3] = 0x00; //----协议ID
request[4] = (byte)((request.Length - 6) >> 8);
request[5] = (byte)((request.Length - 6) & 0xFF);
request[6] = ID; //-------------------------单元标识符ID
request[7] = 0x10; //-----------------------功能码(写多保存寄存器)
request[8] = (byte)(addr >> 8); //----------起始地址高字节
request[9] = (byte)(addr & 0xFF); //--------起始地址低字节
request[10] = (byte)(values.Length >> 8); //数量高字节
request[11] = (byte)(values.Length & 0xFF);//数量低字节
request[12] = (byte)(values.Length * 2);
for (int i = 0; i < values.Length; i++)
{
request[13 + i * 2] = (byte)(values[i] >> 8);
request[13 + i * 2 + 1] = (byte)(values[i] & 0xFF);
}
byte[] response = await SendRequestAsync(request, 12).ConfigureAwait(false);
return response != null && response.Length >= 12 && response[7] == 0x10 &&
(response[8] << 8 | response[9]) == addr &&
(response[10] << 8 | response[11]) == values.Length;
}
#endregion
#region 客户端关闭
/// <summary>
/// 释放所有资源
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposed) return;
disposed = true;
if (disposing)
{
// 释放托管资源
heartbeatTimer?.Dispose();
receiveEvent?.Dispose();
stream?.Dispose();
tcpClient?.Dispose();
}
}
#endregion
}
#endregion这是优化后的代码,检查下更改的正确完全吗
最新发布