using System;
|
using System.Net.Sockets;
|
using System.Net;
|
using Microsoft.Owin.BuilderProperties;
|
using System.Web.Services.Description;
|
using Org.BouncyCastle.Utilities.Net;
|
using System.Web.UI.WebControls.WebParts;
|
using System.Linq;
|
|
using TcpClient = System.Net.Sockets.TcpClient;
|
using System.IO;
|
|
namespace HH.WCS.Mobox3.DSZSH.device {
|
// 定义Modbus通讯接口
|
public interface IModbusCommunicator : IDisposable {
|
bool IsConnected { get; }
|
|
void Link(string ipAddress, int port);
|
void Disconnect();
|
void ReconnectAll();
|
|
// 常用的Modbus功能码方法
|
/// <summary>
|
/// 读一个或多个线圈,返回一个bit真假数组
|
/// </summary>
|
/// <param name="startingAddress"></param>
|
/// <param name="quantity"></param>
|
/// <returns></returns>
|
bool[] ReadCoils(int startingAddress, int quantity);
|
/// <summary>
|
/// 读一个或多个离散输入,返回一个bit真假数组
|
/// </summary>
|
/// <param name="startingAddress"></param>
|
/// <param name="quantity"></param>
|
/// <returns></returns>
|
bool[] ReadDiscreteInputs(int startingAddress, int quantity);
|
/// <summary>
|
/// 批量读取或单独读取保持寄存器,返回的是32位int数组
|
/// </summary>
|
/// <param name="startingAddress"></param>
|
/// <param name="quantity"></param>
|
/// <returns></returns>
|
int[] ReadHoldingRegisters(int startingAddress, int quantity);
|
/// <summary>
|
/// 读一个或多个输入寄存器,返回一个int32位数组
|
/// </summary>
|
/// <param name="startingAddress"></param>
|
/// <param name="quantity"></param>
|
/// <returns></returns>
|
int[] ReadInputRegisters(int startingAddress, int quantity);
|
/// <summary>
|
/// 写入单个线圈
|
/// </summary>
|
/// <param name="coilAddress"></param>
|
/// <param name="value"></param>
|
bool WriteSingleCoil(int coilAddress, bool value);
|
/// <summary>
|
/// 写入单个寄存器数据
|
/// </summary>
|
/// <param name="registerAddress"></param>
|
/// <param name="value"></param>
|
/// <returns></returns>
|
bool WriteSingleRegister(int registerAddress, ushort value);
|
/// <summary>
|
/// 写入多个线圈
|
/// </summary>
|
/// <param name="startingAddress"></param>
|
/// <param name="values"></param>
|
/// <returns></returns>
|
bool WriteMultipleCoils(int startingAddress, bool[] values);
|
/// <summary>
|
/// 写入多个寄存器数据
|
/// </summary>
|
/// <param name="startingAddress"></param>
|
/// <param name="values"></param>
|
/// <returns></returns>
|
bool WriteMultipleRegisters(int startingAddress, int[] values);
|
}
|
|
// Modbus通讯方式枚举
|
public enum ModbusCommunicationType {
|
EasyModbus,
|
TcpSocket
|
}
|
|
// Modbus通讯工厂类
|
public static class ModbusFactory {
|
public static IModbusCommunicator CreateCommunicator(ModbusCommunicationType type) {
|
switch (type) {
|
case ModbusCommunicationType.EasyModbus:
|
return new EasyModbusCommunicator();
|
case ModbusCommunicationType.TcpSocket:
|
return new TcpSocketCommunicator();
|
default:
|
//throw new ArgumentException("不支持的Modbus通信方式:请选择EasyModbus或TcpSocket");
|
LogHelper.Info("不支持的Modbus通信方式:请选择EasyModbus或TcpSocket");
|
return null;
|
}
|
}
|
|
static void Test() {
|
try {
|
var communicator = ModbusFactory.CreateCommunicator(ModbusCommunicationType.TcpSocket);
|
|
communicator.Link("127.0.0.1", 502);
|
var read = communicator.ReadInputRegisters(0, 5);
|
}
|
catch (Exception ex) {
|
LogHelper.InfoEx(ex);
|
}
|
}
|
}
|
|
// EasyModbus实现
|
public class EasyModbusCommunicator : IModbusCommunicator {
|
private EasyModbus.ModbusClient _modbusClient;
|
|
public bool IsConnected => _modbusClient?.Connected ?? false;
|
|
public void Link(string ipAddress, int port) {
|
if (IsConnected) {
|
LogHelper.Info($"(ip:{ipAddress}, port:{port})已经连接");
|
return;
|
}
|
|
_modbusClient = new EasyModbus.ModbusClient(ipAddress, port);
|
_modbusClient.Connect();
|
|
if (IsConnected) {
|
LogHelper.Info($"连接成功:{ipAddress}:{port}");
|
}
|
else {
|
LogHelper.Info($"连接失败:{ipAddress}:{port}");
|
}
|
}
|
|
public void Disconnect() {
|
if (!IsConnected) {
|
LogHelper.Info($"当前没有要断开的连接");
|
return;
|
}
|
|
_modbusClient?.Disconnect();
|
|
if (IsConnected) {
|
LogHelper.Info($"连接成功:{_modbusClient.IPAddress}:{_modbusClient.Port}");
|
}
|
else {
|
LogHelper.Info($"连接失败:{_modbusClient.IPAddress} : {_modbusClient.Port}");
|
}
|
}
|
|
public void ReconnectAll() {
|
if (_modbusClient != null) {
|
var ip = _modbusClient.IPAddress;
|
var port = _modbusClient.Port;
|
Disconnect();
|
Link(ip, port);
|
}
|
}
|
|
public bool[] ReadCoils(int startingAddress, int quantity) {
|
bool[] res = new bool[0];
|
var client = _modbusClient;
|
if (_modbusClient != null && IsConnected) {
|
var ip = client.IPAddress;
|
var port = client.Port;
|
try {
|
res = _modbusClient.ReadCoils(startingAddress, quantity);
|
if (res.Length != 0) {
|
//读取成功
|
LogHelper.Info($"读取成功:ReadCoils:IP:{ip},Port:{port}");
|
}
|
else {
|
//读取失败
|
LogHelper.Info($"读取失败:ReadCoils:IP:{ip},Port:{port}");
|
}
|
}
|
catch (Exception ex) {
|
LogHelper.InfoEx(ex);
|
}
|
}
|
else {
|
LogHelper.Info($"未找到Modbus设备实例对象,或对象未成功连接");
|
}
|
return res;
|
}
|
|
public bool[] ReadDiscreteInputs(int startingAddress, int quantity) {
|
bool[] res = new bool[0];
|
var client = _modbusClient;
|
if (_modbusClient != null && IsConnected) {
|
var ip = client.IPAddress;
|
var port = client.Port;
|
try {
|
res = _modbusClient.ReadDiscreteInputs(startingAddress, quantity);
|
if (res.Length != 0) {
|
//读取成功
|
LogHelper.Info($"读取成功:ReadDiscreteInputs:IP:{ip},Port:{port}");
|
}
|
else {
|
//读取失败
|
LogHelper.Info($"读取失败:ReadDiscreteInputs:IP:{ip},Port:{port}");
|
}
|
}
|
catch (Exception ex) {
|
LogHelper.InfoEx(ex);
|
}
|
}
|
else {
|
LogHelper.Info($"未找到Modbus设备实例对象,或对象未成功连接");
|
}
|
return res;
|
}
|
|
public int[] ReadHoldingRegisters(int startingAddress, int quantity) {
|
int[] res = new int[0];
|
var client = _modbusClient;
|
if (client != null && client.Connected) {
|
var ip = client.IPAddress;
|
var port = client.Port;
|
try {
|
//一个寄存器是16位,返回2个int类型
|
res = client.ReadHoldingRegisters(startingAddress, quantity);
|
if (res.Length != 0) {
|
//读取成功
|
LogHelper.Info($"读取成功:ReadHoldingRegisters:IP:{ip},Port:{port}");
|
}
|
else {
|
//读取失败
|
LogHelper.Info($"读取失败:ReadHoldingRegisters:IP:{ip},Port:{port}");
|
}
|
}
|
catch (Exception ex) {
|
//如果请求数量超出保持寄存器的最大数据行数,会报错
|
LogHelper.InfoEx(ex);
|
}
|
}
|
else {
|
LogHelper.Info($"未找到Modbus设备实例对象,或对象未成功连接");
|
}
|
return res;
|
}
|
|
public int[] ReadInputRegisters(int startingAddress, int quantity) {
|
int[] res = new int[0];
|
var client = _modbusClient;
|
if (client != null && client.Connected) {
|
var ip = client.IPAddress;
|
var port = client.Port;
|
try {
|
res = client.ReadInputRegisters(startingAddress, quantity);
|
if (res.Length != 0) {
|
//读取成功
|
LogHelper.Info($"读取成功:ReadInputRegisters:IP:{ip},Port:{port}");
|
}
|
else {
|
//读取失败
|
LogHelper.Info($"读取失败:ReadInputRegisters:IP:{ip},Port:{port}");
|
}
|
}
|
catch (Exception ex) {
|
LogHelper.InfoEx(ex);
|
}
|
}
|
else {
|
LogHelper.Info($"未找到Modbus设备实例对象,或对象未成功连接");
|
}
|
return res;
|
}
|
|
public bool WriteSingleCoil(int coilAddress, bool value) {
|
var res = false;
|
var client = _modbusClient;
|
if (client != null && client.Connected) {
|
var ip = client.IPAddress;
|
var port = client.Port;
|
try {
|
client.WriteSingleCoil(coilAddress, value);
|
res = value == client.ReadCoils(coilAddress, 1)[0];
|
if (res) {
|
//写入成功
|
LogHelper.Info($"写入成功:WriteSingleCoil:IP:{ip},Port:{port}");
|
}
|
else {
|
//写入失败
|
LogHelper.Info($"写入失败:WriteSingleCoil:IP:{ip},Port:{port}");
|
}
|
}
|
catch (Exception ex) {
|
LogHelper.InfoEx(ex);
|
}
|
}
|
else {
|
LogHelper.Info($"未找到Modbus设备实例对象,或对象未成功连接");
|
}
|
return res;
|
}
|
|
public bool WriteSingleRegister(int registerAddress, ushort value) {
|
var res = false;
|
var client = _modbusClient;
|
if (client != null && client.Connected) {
|
var ip = client.IPAddress;
|
var port = client.Port;
|
try {
|
client.WriteSingleRegister(registerAddress, value);
|
res = value == client.ReadHoldingRegisters(registerAddress, 1)[0];
|
if (res) {
|
//写入成功
|
LogHelper.Info($"写入成功:WriteSingleRegister:IP:{ip},Port:{port}");
|
}
|
else {
|
//写入失败
|
LogHelper.Info($"写入失败:WriteSingleRegister:IP:{ip},Port:{port}");
|
}
|
}
|
catch (Exception ex) {
|
LogHelper.InfoEx(ex);
|
}
|
}
|
else {
|
LogHelper.Info($"未找到Modbus设备实例对象,或对象未成功连接");
|
}
|
return res;
|
}
|
|
public bool WriteMultipleCoils(int startingAddress, bool[] values) {
|
var res = false;
|
var client = _modbusClient;
|
if (client != null && client.Connected) {
|
var ip = client.IPAddress;
|
var port = client.Port;
|
try {
|
client.WriteMultipleCoils(startingAddress, values);
|
var dataRead = client.ReadCoils(startingAddress, values.Length);
|
res = values.SequenceEqual(dataRead);
|
if (res) {
|
//写入成功
|
LogHelper.Info($"写入成功:WriteMultipleCoils:IP:{ip},Port:{port}");
|
}
|
else {
|
//写入失败
|
LogHelper.Info($"写入失败:WriteMultipleCoils:IP:{ip},Port:{port}");
|
}
|
}
|
catch (Exception ex) {
|
LogHelper.InfoEx(ex);
|
}
|
}
|
else {
|
LogHelper.Info($"未找到Modbus设备实例对象,或对象未成功连接");
|
}
|
return res;
|
}
|
|
public bool WriteMultipleRegisters(int startingAddress, int[] values) {
|
var res = false;
|
var client = _modbusClient;
|
if (client != null && client.Connected) {
|
var ip = client.IPAddress;
|
var port = client.Port;
|
var log = string.Join(",", values.Select(x => x.ToString()));
|
try {
|
client.WriteMultipleRegisters(startingAddress, values);
|
var dataRead = client.ReadHoldingRegisters(startingAddress, values.Length);
|
res = values.SequenceEqual(dataRead);
|
if (res) {
|
LogHelper.Info($"写入成功,WriteMultipleRegisters:IP:{ip},Port:{port},{log}");
|
}
|
else {
|
LogHelper.Info($"写入失败,WriteMultipleRegisters:IP:{ip},Port:{port},{log}");
|
}
|
}
|
catch (Exception ex) {
|
LogHelper.InfoEx(ex);
|
}
|
}
|
else {
|
LogHelper.Info($"未找到Modbus设备实例对象,或对象未成功连接");
|
}
|
return res;
|
}
|
|
public void Dispose() {
|
//_modbusClient?.Dispose();
|
GC.SuppressFinalize(_modbusClient);
|
}
|
}
|
|
// TCPSocket实现
|
public class TcpSocketCommunicator : IModbusCommunicator {
|
private System.Net.Sockets.TcpClient _tcpClient;
|
private NetworkStream _networkStream;
|
private byte unitIdentifier = 1; // 默认单元标识符
|
|
public bool IsConnected => _tcpClient?.Connected ?? false;
|
//public bool IsConnected { get { if (_tcpClient != null) { return _tcpClient.Connected; } else { return false; } } }
|
|
public void Link(string ipAddress, int port) {
|
_tcpClient = new System.Net.Sockets.TcpClient();
|
_tcpClient.Connect(ipAddress, port);
|
_networkStream = _tcpClient.GetStream();
|
}
|
|
public void Disconnect() {
|
_networkStream?.Close();
|
_tcpClient?.Close();
|
}
|
|
public void ReconnectAll() {
|
if (_tcpClient != null) {
|
var ip = ((IPEndPoint)_tcpClient.Client.RemoteEndPoint).Address.ToString();
|
var port = ((IPEndPoint)_tcpClient.Client.RemoteEndPoint).Port;
|
Disconnect();
|
Link(ip, port);
|
}
|
}
|
|
public bool[] ReadCoils(int startingAddress, int quantity) {
|
if (quantity < 1 || quantity > 2000)
|
throw new ArgumentException("Quantity must be between 1 and 2000");
|
|
// 构建请求报文(12字节)
|
byte[] request = new byte[12];
|
request[0] = 0x00; // 事务ID高字节
|
request[1] = 0x01; // 事务ID低字节(示例值)
|
request[2] = 0x00; // 协议ID高字节(固定0)
|
request[3] = 0x00; // 协议ID低字节(固定0)
|
request[4] = 0x00; // 长度高字节(后续6字节)
|
request[5] = 0x06; // 长度低字节
|
request[6] = unitIdentifier; // 单元标识符
|
request[7] = 0x01; // 功能码(读线圈)
|
request[8] = (byte)(startingAddress >> 8); // 起始地址高字节
|
request[9] = (byte)startingAddress; // 起始地址低字节
|
request[10] = (byte)(quantity >> 8); // 数量高字节
|
request[11] = (byte)quantity; // 数量低字节
|
|
// 发送请求
|
_networkStream.Write(request, 0, request.Length);
|
|
// 读取响应头(8字节)
|
byte[] responseHeader = new byte[8];
|
int bytesRead = _networkStream.Read(responseHeader, 0, 8);
|
if (bytesRead != 8)
|
throw new Exception("Invalid response header length");
|
|
// 校验事务ID、协议ID、单元标识符
|
if (responseHeader[0] != request[0] || responseHeader[1] != request[1] ||
|
responseHeader[2] != 0x00 || responseHeader[3] != 0x00 ||
|
responseHeader[6] != unitIdentifier)
|
throw new Exception("Invalid response header");
|
|
// 检查异常响应(功能码 + 0x80)
|
if (responseHeader[7] == 0x81) {
|
int errorCode = _networkStream.ReadByte();
|
throw new Exception($"Modbus error. Code: {errorCode}");
|
}
|
else if (responseHeader[7] != 0x01)
|
throw new Exception("Invalid function code in response");
|
|
// 读取数据部分(字节数 + 实际数据)
|
byte byteCount = responseHeader[8];
|
byte[] responseData = new byte[byteCount];
|
bytesRead = _networkStream.Read(responseData, 0, byteCount);
|
if (bytesRead != byteCount)
|
throw new Exception("Invalid response data length");
|
|
// 解析线圈状态(每个位表示一个线圈)
|
bool[] coils = new bool[quantity];
|
for (int i = 0; i < quantity; i++) {
|
int byteIndex = i / 8;
|
int bitIndex = i % 8;
|
coils[i] = (responseData[byteIndex] & (1 << bitIndex)) != 0;
|
}
|
|
return coils;
|
}
|
|
public bool[] ReadDiscreteInputs(int startingAddress, int quantity) {
|
// 报文结构与ReadCoils几乎相同,仅功能码改为0x02
|
byte[] request = new byte[12];
|
request[0] = 0x00; // 事务ID高字节
|
request[1] = 0x01; // 事务ID低字节(示例值)
|
request[2] = 0x00; // 协议ID高字节(固定0)
|
request[3] = 0x00; // 协议ID低字节(固定0)
|
request[4] = 0x00; // 长度高字节(后续6字节)
|
request[5] = 0x06; // 长度低字节
|
request[6] = unitIdentifier;
|
request[7] = 0x02; // 功能码:读离散输入
|
request[8] = (byte)(startingAddress >> 8); // 起始地址高字节
|
request[9] = (byte)startingAddress; // 起始地址低字节
|
request[10] = (byte)(quantity >> 8); // 数量高字节
|
request[11] = (byte)quantity; // 数量低字节
|
|
_networkStream.Write(request, 0, request.Length);
|
|
// 响应处理逻辑与ReadCoils一致
|
byte[] responseHeader = new byte[8];
|
_networkStream.Read(responseHeader, 0, 8);
|
|
// 校验和异常处理(参考ReadCoils)
|
if (responseHeader[7] == 0x82) // 异常响应
|
{
|
int errorCode = _networkStream.ReadByte();
|
throw new Exception($"Modbus error. Code: {errorCode}");
|
}
|
|
byte byteCount = responseHeader[8];
|
byte[] responseData = new byte[byteCount];
|
_networkStream.Read(responseData, 0, byteCount);
|
|
// 解析离散输入状态
|
bool[] inputs = new bool[quantity];
|
for (int i = 0; i < quantity; i++) {
|
int byteIndex = i / 8;
|
int bitIndex = i % 8;
|
inputs[i] = (responseData[byteIndex] & (1 << bitIndex)) != 0;
|
}
|
|
return inputs;
|
}
|
|
public int[] ReadHoldingRegisters(int startingAddress, int quantity) {
|
if (quantity < 1 || quantity > 125)
|
throw new ArgumentException("Quantity must be between 1 and 125");
|
|
// 构建请求报文(12字节)
|
byte[] request = new byte[12];
|
request[0] = 0x00; // 事务ID高字节(示例值)
|
request[1] = 0x01; // 事务ID低字节
|
request[2] = 0x00; // 协议ID高字节(固定0)
|
request[3] = 0x00; // 协议ID低字节(固定0)
|
request[4] = 0x00; // 长度高字节(后续6字节)
|
request[5] = 0x06; // 长度低字节
|
request[6] = unitIdentifier; // 单元标识符
|
request[7] = 0x03; // 功能码(读保持寄存器)
|
request[8] = (byte)(startingAddress >> 8); // 起始地址高字节
|
request[9] = (byte)startingAddress; // 起始地址低字节
|
request[10] = (byte)(quantity >> 8); // 数量高字节
|
request[11] = (byte)quantity; // 数量低字节
|
|
// 发送请求
|
_networkStream.Write(request, 0, request.Length);
|
|
// 读取响应头(8字节)
|
byte[] responseHeader = new byte[8];
|
int bytesRead = _networkStream.Read(responseHeader, 0, 8);
|
if (bytesRead != 8)
|
throw new Exception("Invalid response header length");
|
|
// 校验事务ID、协议ID、单元标识符
|
if (responseHeader[0] != request[0] || responseHeader[1] != request[1] ||
|
responseHeader[2] != 0x00 || responseHeader[3] != 0x00 ||
|
responseHeader[6] != unitIdentifier)
|
throw new Exception("Invalid response header");
|
|
// 检查异常响应(功能码 + 0x80)
|
if (responseHeader[7] == 0x83) {
|
int errorCode = _networkStream.ReadByte();
|
throw new Exception($"Modbus error. Code: {errorCode}");
|
}
|
else if (responseHeader[7] != 0x03)
|
throw new Exception("Invalid function code in response");
|
|
// 读取数据部分(字节数 + 实际数据)
|
byte byteCount = responseHeader[8];
|
byte[] responseData = new byte[byteCount];
|
bytesRead = _networkStream.Read(responseData, 0, byteCount);
|
if (bytesRead != byteCount)
|
throw new Exception("Invalid response data length");
|
|
// 解析寄存器值(大端序)
|
int[] registers = new int[quantity];
|
for (int i = 0; i < quantity; i++) {
|
int offset = i * 2;
|
registers[i] = (ushort)((responseData[offset] << 8) | responseData[offset + 1]);
|
}
|
|
return registers;
|
}
|
|
public int[] ReadInputRegisters(int startingAddress, int quantity) {
|
if (quantity < 1 || quantity > 125)
|
throw new ArgumentException("Quantity must be between 1 and 125");
|
|
// 构建Modbus TCP请求报文
|
byte[] request = new byte[12];
|
|
// 事务标识符(可以简单递增)
|
request[0] = 0x00;
|
request[1] = 0x01;
|
|
// 协议标识符(Modbus固定为0)
|
request[2] = 0x00;
|
request[3] = 0x00;
|
|
// 长度字段(后面还有6字节)
|
request[4] = 0x00;
|
request[5] = 0x06;
|
|
// 单元标识符
|
request[6] = unitIdentifier;
|
|
// 功能码(4表示读输入寄存器)
|
request[7] = 0x04;
|
|
// 起始地址
|
request[8] = (byte)(startingAddress >> 8);
|
request[9] = (byte)startingAddress;
|
|
// 寄存器数量
|
request[10] = (byte)(quantity >> 8);
|
request[11] = (byte)quantity;
|
|
// 发送请求
|
_networkStream.Write(request, 0, request.Length);
|
|
// 接收响应
|
byte[] responseHeader = new byte[8];
|
int bytesRead = _networkStream.Read(responseHeader, 0, 8);
|
|
if (bytesRead != 8)
|
throw new Exception("Invalid response header length");
|
|
// 检查事务ID和协议ID是否匹配
|
if (responseHeader[0] != request[0] || responseHeader[1] != request[1] ||
|
responseHeader[2] != 0x00 || responseHeader[3] != 0x00)
|
throw new Exception("Invalid response header");
|
|
// 检查单元标识符和功能码
|
if (responseHeader[6] != unitIdentifier)
|
throw new Exception("Unit identifier mismatch");
|
|
if (responseHeader[7] != 0x04 && responseHeader[7] != 0x84)
|
throw new Exception("Invalid function code in response");
|
|
// 检查异常响应
|
if (responseHeader[7] == 0x84) {
|
byte errorCode = responseHeader[8];
|
throw new Exception($"Modbus error response. Error code: {errorCode}");
|
}
|
|
// 读取剩余响应数据
|
byte byteCount = responseHeader[8];
|
byte[] responseData = new byte[byteCount];
|
bytesRead = _networkStream.Read(responseData, 0, byteCount);
|
|
if (bytesRead != byteCount)
|
throw new Exception("Invalid response data length");
|
|
// 解析寄存器值
|
int[] registers = new int[quantity];
|
for (int i = 0; i < quantity; i++) {
|
int offset = i * 2;
|
registers[i] = (ushort)((responseData[offset] << 8) | responseData[offset + 1]);
|
}
|
|
return registers;
|
}
|
|
public bool WriteSingleCoil(int address, bool value) {
|
// 请求报文固定长度12字节
|
byte[] request = new byte[12];
|
request[0] = 0x00; // 事务ID
|
request[1] = 0x01;
|
request[2] = 0x00; // 协议ID
|
request[3] = 0x00;
|
request[4] = 0x00; // 长度
|
request[5] = 0x06;
|
request[6] = unitIdentifier;
|
request[7] = 0x05; // 功能码
|
request[8] = (byte)(address >> 8); // 地址高字节
|
request[9] = (byte)address; // 地址低字节
|
request[10] = (byte)(value ? 0xFF : 0x00); // ON=0xFF00, OFF=0x0000
|
request[11] = 0x00;
|
|
_networkStream.Write(request, 0, request.Length);
|
|
// 响应应与请求完全一致(回显)
|
byte[] response = new byte[12];
|
int bytesRead = _networkStream.Read(response, 0, 12);
|
if (bytesRead != 12)
|
throw new Exception("Invalid response length");
|
|
// 校验回显报文
|
for (int i = 0; i < 12; i++) {
|
if (response[i] != request[i])
|
throw new Exception("Response does not match request");
|
}
|
return true;
|
}
|
|
public bool WriteSingleRegister(int address, ushort value) {
|
byte[] request = new byte[12];
|
request[0] = 0x00; // 事务ID
|
request[1] = 0x01;
|
request[2] = 0x00; // 协议ID
|
request[3] = 0x00;
|
request[4] = 0x00; // 长度
|
request[5] = 0x06;
|
request[6] = unitIdentifier;
|
request[7] = 0x06; // 功能码
|
request[8] = (byte)(address >> 8); // 地址高字节
|
request[9] = (byte)address;
|
request[10] = (byte)(value >> 8); // 值高字节
|
request[11] = (byte)value; // 值低字节
|
|
_networkStream.Write(request, 0, request.Length);
|
|
// 检查回显响应(同WriteSingleCoil)
|
byte[] response = new byte[12];
|
_networkStream.Read(response, 0, 12);
|
if (!response.SequenceEqual(request))
|
throw new Exception("Response mismatch");
|
|
return true;
|
}
|
|
public bool WriteMultipleCoils(int startingAddress, bool[] values) {
|
int quantity = values.Length;
|
if (quantity < 1 || quantity > 1968)
|
throw new ArgumentException("Quantity must be between 1 and 1968");
|
|
// 计算需要的字节数(每个字节存储8个线圈状态)
|
int byteCount = (quantity + 7) / 8;
|
byte[] coilBytes = new byte[byteCount];
|
|
// 将bool数组压缩为字节数组
|
for (int i = 0; i < quantity; i++) {
|
if (values[i]) {
|
int byteIndex = i / 8;
|
int bitIndex = i % 8;
|
coilBytes[byteIndex] |= (byte)(1 << bitIndex);
|
}
|
}
|
|
// 构建请求报文(13 + 线圈字节数)
|
byte[] request = new byte[13 + coilBytes.Length];
|
request[0] = 0x00; // 事务ID高字节
|
request[1] = 0x01; // 事务ID低字节
|
request[2] = 0x00; // 协议ID高字节
|
request[3] = 0x00; // 协议ID低字节
|
request[4] = (byte)((7 + coilBytes.Length) >> 8); // 长度高字节
|
request[5] = (byte)(7 + coilBytes.Length); // 长度低字节
|
request[6] = unitIdentifier;
|
request[7] = 0x0F; // 功能码(写多个线圈)
|
request[8] = (byte)(startingAddress >> 8); // 起始地址高字节
|
request[9] = (byte)startingAddress; // 起始地址低字节
|
request[10] = (byte)(quantity >> 8); // 数量高字节
|
request[11] = (byte)quantity; // 数量低字节
|
request[12] = (byte)byteCount; // 字节数
|
Buffer.BlockCopy(coilBytes, 0, request, 13, coilBytes.Length);
|
|
// 发送请求
|
_networkStream.Write(request, 0, request.Length);
|
|
// 读取响应(固定12字节)
|
byte[] response = new byte[12];
|
int bytesRead = _networkStream.Read(response, 0, 12);
|
if (bytesRead != 12)
|
throw new Exception("Invalid response length");
|
|
// 校验回显的地址和数量
|
if (response[8] != request[8] || response[9] != request[9] ||
|
response[10] != request[10] || response[11] != request[11])
|
throw new Exception("Response address/quantity mismatch");
|
|
return true;
|
}
|
|
public bool WriteMultipleRegisters(int startingAddress, int[] values) {
|
int quantity = values.Length;
|
if (quantity < 1 || quantity > 123)
|
throw new ArgumentException("Quantity must be between 1 and 123");
|
|
// 将ushort数组转换为字节数组(大端序)
|
byte[] valueBytes = new byte[quantity * 2];
|
for (int i = 0; i < quantity; i++) {
|
valueBytes[i * 2] = (byte)(values[i] >> 8);
|
valueBytes[i * 2 + 1] = (byte)values[i];
|
}
|
|
// 构建请求报文(13 + 值字节数)
|
byte[] request = new byte[13 + valueBytes.Length];
|
request[0] = 0x00; // 事务ID高字节
|
request[1] = 0x01; // 事务ID低字节
|
request[2] = 0x00; // 协议ID高字节
|
request[3] = 0x00; // 协议ID低字节
|
request[4] = (byte)((7 + valueBytes.Length) >> 8); // 长度高字节
|
request[5] = (byte)(7 + valueBytes.Length); // 长度低字节
|
request[6] = unitIdentifier;
|
request[7] = 0x10; // 功能码(写多个寄存器)
|
request[8] = (byte)(startingAddress >> 8); // 起始地址高字节
|
request[9] = (byte)startingAddress; // 起始地址低字节
|
request[10] = (byte)(quantity >> 8); // 数量高字节
|
request[11] = (byte)quantity; // 数量低字节
|
request[12] = (byte)(quantity * 2); // 字节数
|
Buffer.BlockCopy(valueBytes, 0, request, 13, valueBytes.Length);
|
|
// 发送请求
|
_networkStream.Write(request, 0, request.Length);
|
|
// 读取响应(固定12字节)
|
byte[] response = new byte[12];
|
int bytesRead = _networkStream.Read(response, 0, 12);
|
if (bytesRead != 12)
|
throw new Exception("Invalid response length");
|
|
// 校验回显的地址和数量
|
if (response[8] != request[8] || response[9] != request[9] ||
|
response[10] != request[10] || response[11] != request[11])
|
throw new Exception("Response address/quantity mismatch");
|
|
return true;
|
}
|
|
public void Dispose() {
|
_networkStream?.Dispose();
|
_tcpClient?.Dispose();
|
}
|
}
|
}
|