建立ModbusTCP通讯库
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Modbus.Device;
using System.Net.Sockets;
using thinger.cn.DataConvertHelper;
using System.Net;
namespace NModbusHelper
{
public class NmodbusTCPhelper
{
private TcpClient tcpClient = null;
private ModbusIpMaster master;
//private Socket tcpClient = null; //定义串口类对象
#region 打开与关闭Socket
public bool Connect(string ip, int port)
{
//tcpclient = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);
try
{
tcpClient = new TcpClient();
tcpClient.Connect(IPAddress.Parse(ip), port);
//IPEndPoint ie = new IPEndPoint(IPAddress.Parse(ip), port);
//tcpClient.Connect(ie);
master = ModbusIpMaster.CreateIp(tcpClient);
}
catch (Exception)
{
return false;
}
return true;
}
public bool Disconnect()
{
if (tcpClient != null)
{
tcpClient.Close();
return true;
}
else
{
return false;
}
}
#endregion
public ushort[] ReadKeepReg(string iAddress, int iLength)//偏移量,寄存器数量
{
try
{
ushort[] des = master.ReadHoldingRegisters(Convert.ToUInt16(iAddress), Convert.ToUInt16(iLength));
byte[] res = ByteArrayLib.GetByteArrayFromUShortArray(des);
return des;
}
catch (Exception)
{
return null;
}
}
public List<float> AnalyseData_4x(ushort[] des, string iAddress)
{
int StartByte;
StartByte = int.Parse(iAddress) * 2;
List<float> floatArray = new List<float>();
byte[] byteArray = ByteArrayLib.GetByteArrayFromUShortArray(des);
for (int i = StartByte; i < byteArray.Length; i += 4)
{
floatArray.Add(FloatLib.GetFloatFromByteArray(byteArray, i));
}
return floatArray;
}
}
}
使用ModbusTCP 读取
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Net.Sockets;
using System.Net;
using Modbus.Device;
using thinger.cn.DataConvertHelper;
using NModbusHelper;
namespace NmodbusPractice2
{
public partial class FrmMain : Form
{
public FrmMain()
{
InitializeComponent();
}
string ip = "127.0.0.1";
int port = 502;
NmodbusTCPhelper objTcp = new NmodbusTCPhelper();
private bool IsConnected = false;
private System.Windows.Forms.Timer updateTimer = new System.Windows.Forms.Timer();
ushort[] res;
List<float> floatArray;
private void Addinfo(string info)
{
this.isInfo.Items.Insert(
0, DateTime.Now.ToString("HH:mm:ss") + " " + info + Environment.NewLine);
}
private void btnConnect_Click(object sender, EventArgs e)
{
IsConnected = objTcp.Connect(ip, port);
Addinfo(IsConnected ? "连接成功" : "连接失败");
}
private void btnRead_Click(object sender, EventArgs e)
{
this.updateTimer.Interval = 1000;
this.updateTimer.Tick += UpdateTimer_Tick;
this.updateTimer.Start();
if (res != null)
{
Addinfo("读取成功");
}
}
private void UpdateTimer_Tick(object sender, EventArgs e)
{
string iAddress = this.txtVarAdd.Text;
int iLength = Convert.ToUInt16(this.numCount.Text);
res = objTcp.ReadKeepReg(iAddress, iLength);
//Uint
//this.isInfo.Items.Add(res.ToString());
//for (int i = 0; i < res.Length; i++)
//{
// this.isInfo.Items.Add(res[i].ToString());
//}
//float
floatArray = objTcp.AnalyseData_4x(res,iAddress);
for (int i = 0; i < floatArray.ToArray().Length; i++)
{
this.isInfo.Items.Add(floatArray.ToArray()[i].ToString());
}
}
private void btnDisconnect_Click(object sender, EventArgs e)
{
objTcp.Disconnect();
Addinfo("断开连接");
}
private void ChangeRTU_Click(object sender, EventArgs e)
{
FrmRTU frmRTU = new FrmRTU();
frmRTU.Show();
}
}
}
建立ModbusRTU的对象和通信库
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO.Ports;
namespace NModbusHelper
{
public class ModbusRTUEntity
{
public string Port { get; set; }
//波特率
public int Paud { get; set; }
//数据位
public int DataBit { get; set; }
//校验位
public Parity IParity { get; set; }
//停止位
public StopBits IStopBit { get; set; }
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using thinger.cn.DataConvertHelper;
using System.IO.Ports;
using Modbus.Device;
using System.Threading;
namespace NModbusHelper
{
public class NmodbusRTUhelper
{
private SerialPort MyCom = new SerialPort();
IModbusSerialMaster master;
//定义CRC校验高低位
private byte ucCRCHi = 0xFF;
private byte ucCRCLo = 0xFF;
public bool OpenMyComm(int iBaudRate, string iPortNo, int iDataBits, Parity iParity, StopBits iStopBits)
{
try
{
if (MyCom.IsOpen)
{
MyCom.Close();
}
//MyCom.BaudRate = iBaudRate;
//MyCom.PortName = iPortNo;
//MyCom.DataBits = iDataBits;
//MyCom.Parity = iParity;
//MyCom.StopBits = iStopBits;
MyCom.BaudRate = iBaudRate;
MyCom.PortName = iPortNo;
MyCom.DataBits = iDataBits;
MyCom.Parity = iParity;
MyCom.StopBits = iStopBits;
MyCom.Open();
return true;
}
catch (Exception)
{
return false;
}
}
public bool ClosePort()
{
if (MyCom.IsOpen)
{
MyCom.Close();
return true;
}
else
{
return false;
}
}
/// <summary>
///
/// </summary>
/// <param name="iDevAdd">从站地址</param>
/// <param name="iAddress">偏移量</param>
/// <param name="iLength">寄存器个数</param>
/// <returns></returns>
public ushort[] ReadKeepReg(int iDevAdd, int iAddress, int iLength)
{
byte[] SendCommand = new byte[8];
SendCommand[0] = (byte)iDevAdd;
SendCommand[1] = 0x03;
SendCommand[2] = (byte)((iAddress - iAddress % 256) / 256);
SendCommand[3] = (byte)(iAddress % 256);
SendCommand[4] = (byte)((iLength - iLength % 256) / 256);
SendCommand[5] = (byte)(iLength % 256);
Crc16(SendCommand, 6);
SendCommand[6] = ucCRCLo;
SendCommand[7] = ucCRCHi;
try
{
MyCom.Write(SendCommand, 0, 8);
}
catch (Exception)
{
return null;
}
MyCom.ReadTimeout = 500;
Thread.Sleep(500);
if (MyCom.BytesToRead>0)
{
master = ModbusSerialMaster.CreateRtu(MyCom);
}
//Convert.ToByte(iDevAdd), (ushort)iAddress, (ushort)iLength
ushort[] des = master.ReadHoldingRegisters(Convert.ToByte(iDevAdd), (ushort)iAddress, (ushort)iLength);
//byte[] res = ByteArrayLib.GetByteArrayFromUShortArray(des);
return des;
}
public List<float> AnalyseData_4x(ushort[] des, string iAddress)
{
int StartByte;
StartByte = int.Parse(iAddress) * 2;
List<float> floatArray = new List<float>();
byte[] byteArray = ByteArrayLib.GetByteArrayFromUShortArray(des);
for (int i = StartByte; i < byteArray.Length; i += 4)
{
floatArray.Add(FloatLib.GetFloatFromByteArray(byteArray, i));
}
return floatArray;
}
#region CRC校验
private static readonly byte[] aucCRCHi = {
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40
};
private static readonly byte[] aucCRCLo = {
0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06, 0x07, 0xC7,
0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD, 0x0F, 0xCF, 0xCE, 0x0E,
0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09, 0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9,
0x1B, 0xDB, 0xDA, 0x1A, 0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC,
0x14, 0xD4, 0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3,
0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3, 0xF2, 0x32,
0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4, 0x3C, 0xFC, 0xFD, 0x3D,
0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A, 0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38,
0x28, 0xE8, 0xE9, 0x29, 0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF,
0x2D, 0xED, 0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26,
0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60, 0x61, 0xA1,
0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67, 0xA5, 0x65, 0x64, 0xA4,
0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F, 0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB,
0x69, 0xA9, 0xA8, 0x68, 0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA,
0xBE, 0x7E, 0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5,
0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71, 0x70, 0xB0,
0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92, 0x96, 0x56, 0x57, 0x97,
0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C, 0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E,
0x5A, 0x9A, 0x9B, 0x5B, 0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89,
0x4B, 0x8B, 0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,
0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42, 0x43, 0x83,
0x41, 0x81, 0x80, 0x40
};
private void Crc16(byte[] pucFrame, int usLen)
{
int i = 0;
ucCRCHi = 0xFF;
ucCRCLo = 0xFF;
UInt16 iIndex = 0x0000;
while (usLen-- > 0)
{
iIndex = (UInt16)(ucCRCLo ^ pucFrame[i++]);
ucCRCLo = (byte)(ucCRCHi ^ aucCRCHi[iIndex]);
ucCRCHi = aucCRCLo[iIndex];
}
}
#endregion
}
}
读取
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Modbus.Device;
using thinger.cn.DataConvertHelper;
using NModbusHelper;
using System.IO.Ports;
using System.Threading;
namespace NmodbusPractice2
{
public partial class FrmRTU : Form
{
public FrmRTU()
{
InitializeComponent();
this.RTUPort.Text = "COM3";
this.RTUPaud.Text = "9600";
this.RTUIParity.Text = "None";
this.RTUDataBit.Text = "8";
this.RTUIStopBit.Text = "1";
this.RTUiDevAdd.Text = "1";
this.RTUiAddress.Text = "0";
this.RTUiLength.Text = "20";
}
ModbusRTUEntity rTUEntity = new ModbusRTUEntity();
NmodbusRTUhelper rTUhelper = new NmodbusRTUhelper();
private System.Windows.Forms.Timer upDateTimer = new System.Windows.Forms.Timer();
CancellationTokenSource cts = new CancellationTokenSource();
private bool IsConnected = false;
ushort[] res;
private void Addinfo(string info)
{
this.isInfo.Items.Insert(
0, DateTime.Now.ToString("HH:mm:ss") + " " + info + Environment.NewLine);
}
private void btnConnect_Click(object sender, EventArgs e)
{
rTUEntity.Port = this.RTUPort.Text.Trim();
rTUEntity.Paud = Convert.ToInt32(this.RTUPaud.Text.Trim());
rTUEntity.DataBit = Convert.ToInt32(this.RTUDataBit.Text.Trim());
switch (this.RTUIStopBit.Text.Trim())
{
case "1":
rTUEntity.IStopBit = StopBits.One;
break;
case "2":
rTUEntity.IStopBit = StopBits.Two;
break;
default:
rTUEntity.IStopBit = StopBits.One;
break;
}
switch (this.RTUIParity.Text.Trim())//效验信息
{
case "None":
rTUEntity.IParity = Parity.None;
break;
case "Even":
rTUEntity.IParity = Parity.Even;
break;
case "Odd":
rTUEntity.IParity = Parity.Odd;
break;
default:
rTUEntity.IParity = Parity.None;
break;
}
IsConnected = rTUhelper.OpenMyComm(rTUEntity.Paud, rTUEntity.Port, rTUEntity.DataBit, rTUEntity.IParity, rTUEntity.IStopBit);
Addinfo(IsConnected ? "连接成功" : "连接失败");
}
private void btnRead_Click(object sender, EventArgs e)
{
this.upDateTimer.Interval = 1000;
this.upDateTimer.Tick += UpDateTimer_Tick;
this.upDateTimer.Start();
}
private void UpDateTimer_Tick(object sender, EventArgs e)
{
string RTUiDevAdd = this.RTUiDevAdd.Text.Trim();
string RTUiAddress = this.RTUiAddress.Text.Trim();
string RTUiLength = this.RTUiLength.Text.Trim();
res = rTUhelper.ReadKeepReg(int.Parse(RTUiDevAdd), int.Parse(RTUiAddress), int.Parse(RTUiLength));
for (int i = 0; i < res.Length; i++)
{
this.isInfo.Items.Add(res[i].ToString());
}
}
private void btnDisconnect_Click(object sender, EventArgs e)
{
IsConnected = rTUhelper.ClosePort();
Addinfo(IsConnected ? "断开失败" : "断开成功");
}
private void btnTaskRead_Click(object sender, EventArgs e)
{
string RTUiDevAdd = this.RTUiDevAdd.Text.Trim();
string RTUiAddress = this.RTUiAddress.Text.Trim();
string RTUiLength = this.RTUiLength.Text.Trim();
Task.Run(() =>
{
while (true)
{
res = rTUhelper.ReadKeepReg(int.Parse(RTUiDevAdd), int.Parse(RTUiAddress), int.Parse(RTUiLength));
for (int i = 0; i < res.Length; i++)
{
this.isInfo.Invoke(new Action<ushort>(t =>
{
this.isInfo.Items.Add(t.ToString());
}), res[i]);
}
}
}, cts.Token);
}
}
}
modbus可以一主多从。
主站(modbusPoll,MB_Clent)是 客户端(client),需要指定起始值,偏移量,存储区(40001等),数据长度(上位机)。
从站(modbusSlave,MB_Server)是服务器端(server),不需要指定长度,存储区。
客户端,主站:
RTU:
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Ports;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace DAL
{
public class ModbusRtu
{
#region 对象或属性
//定义串口通信的对象
private SerialPort MyCom = new SerialPort();
//创建通信超时属性
public int ReadTimeOut { get; set; } = 2000;
public int WriteTimeOut { get; set; } = 2000;
//读取返回报文超时时间
public int ReceiveTimeOut { get; set; } = 2000;
//字节顺序
public DataFormat DataFormat { get; set; } = DataFormat.ABCD;
//创建一个互斥锁对象
private SimpleHybirdLock InteractiveLock = new SimpleHybirdLock();
//通信延时时间
public int SleepTime { get; set; } = 20;
#endregion
#region 打开关闭串口
/// <summary>
/// 打开串口
/// </summary>
/// <param name="iBaudRate">波特率</param>
/// <param name="iPortName">串口号</param>
/// <param name="iDataBits">数据位</param>
/// <param name="iParity">校验位</param>
/// <param name="iStopBits">停止位</param>
public void OpenMyCom(int iBaudRate, string iPortName, int iDataBits, Parity iParity, StopBits iStopBits)
{
//如果当前串口是打开的,先关闭一下
if (MyCom.IsOpen)
{
MyCom.Close();
}
//串口通信对象实例化
MyCom = new SerialPort(iPortName, iBaudRate, iParity, iDataBits, iStopBits);
//设置超时时间
MyCom.ReadTimeout = this.ReadTimeOut;
MyCom.WriteTimeout = this.WriteTimeOut;
//打开串口
MyCom.Open();
}
/// <summary>
/// 关闭串口
/// </summary>
public void CloseMyCom()
{
if (MyCom.IsOpen)
{
MyCom.Close();
}
}
#endregion
#region 读取输出线圈 功能码01H
/// <summary>
/// 读取输出线圈方法
/// </summary>
/// <param name="iDevAdd">从站地址</param>
/// <param name="iAddress">起始地址</param>
/// <param name="iLength">长度</param>
/// <returns></returns>
public byte[] ReadOutputStatus(int iDevAdd, int iAddress, int iLength)
{
//第一步:拼接报文
ByteArray SendCommand = new ByteArray();
SendCommand.Add(new byte[] { (byte)iDevAdd, 0x01, (byte)(iAddress / 256), (byte)(iAddress % 256) });
SendCommand.Add(new byte[] { (byte)(iLength / 256), (byte)(iLength % 256) });
SendCommand.Add(Crc16(SendCommand.array, 6));
//第二步:发送报文 接受报文
int byteLength;
if (iLength % 8 == 0)
{
byteLength = iLength / 8;
}
else
{
byteLength = iLength / 8 + 1;
}
byte[] response = new byte[5 + byteLength];
if (SendData(SendCommand.array, ref response))
{
//第三步:解析报文
//验证:功能码+字节计数
if (response[1] == 0x01 && response[2] == byteLength)
{
return GetByteArray(response, 3, byteLength);
}
else
{
return null;
}
}
else
{
return null;
}
}
#endregion
#region 读取输入线圈 功能码02H
/// <summary>
/// 读取输入线圈
/// </summary>
/// <param name="iDevAdd">从站地址</param>
/// <param name="iAddress">起始地址</param>
/// <param name="iLength">长度</param>
/// <returns></returns>
public byte[] ReadInputStatus(int iDevAdd, int iAddress, int iLength)
{
//第一步:拼接报文
ByteArray SendCommand = new ByteArray();
SendCommand.Add(new byte[] { (byte)iDevAdd, 0x02, (byte)(iAddress / 256), (byte)(iAddress % 256) });
SendCommand.Add(new byte[] { (byte)(iLength / 256), (byte)(iLength % 256) });
SendCommand.Add(Crc16(SendCommand.array, 6));
//第二步:发送报文 接受报文
int byteLength = 0;
if (iLength % 8 == 0)
{
byteLength = iLength / 8;
}
else
{
byteLength = iLength / 8 + 1;
}
byte[] response = new byte[5 + byteLength];
if (SendData(SendCommand.array, ref response))
{
//第三步:解析报文
if (response[1] == 0x02 && response[2] == byteLength)
{
return GetByteArray(response, 3, byteLength);
}
else
{
return null;
}
}
else
{
return null;
}
}
#endregion
#region 读取保持寄存器 功能码03H
public byte[] ReadKeepReg(int iDevAdd, int iAddress, int iLength)
{
//第一步:拼接报文
ByteArray SendCommand = new ByteArray();
SendCommand.Add(new byte[] { (byte)iDevAdd, 0x03, (byte)(iAddress / 256), (byte)(iAddress % 256) });
SendCommand.Add(new byte[] { (byte)(iLength / 256), (byte)(iLength % 256) });
SendCommand.Add(Crc16(SendCommand.array, 6));
//第二步:发送报文 接受报文
int byteLength = iLength * 2;
byte[] response = new byte[5 + byteLength];
if (SendData(SendCommand.array, ref response))
{
//第三步:解析报文
if (response[1] == 0x03 && response[2] == byteLength&&response.Length==byteLength+5)
{
return GetByteArray(response, 3, byteLength);
}
else
{
return null;
}
}
else
{
return null;
}
}
#endregion
#region 读取输入寄存器 功能码04H
public byte[] ReadInputReg(int iDevAdd, int iAddress, int iLength)
{
//第一步:拼接报文
ByteArray SendCommand = new ByteArray();
SendCommand.Add(new byte[] { (byte)iDevAdd, 0x04, (byte)(iAddress / 256), (byte)(iAddress % 256) });
SendCommand.Add(new byte[] { (byte)(iLength / 256), (byte)(iLength % 256) });
SendCommand.Add(Crc16(SendCommand.array, 6));
//第二步:发送报文 接受报文
int byteLength = iLength * 2;
byte[] response = new byte[5 + byteLength];
if (SendData(SendCommand.array, ref response))
{
//第三步:解析报文
if (response[1] == 0x04 && response[2] == byteLength)
{
return GetByteArray(response, 3, byteLength);
}
else
{
return null;
}
}
else
{
return null;
}
}
#endregion
#region 强制单线圈 功能码05H
public bool ForceCoil(int iDevAdd, int iAddress, bool SetValue)
{
//第一步:拼接报文
ByteArray SendCommand = new ByteArray();
SendCommand.Add(new byte[] { (byte)iDevAdd, 0x05, (byte)(iAddress / 256), (byte)(iAddress % 256) });
if (SetValue)
{
SendCommand.Add(new byte[] { 0xFF, 0x00 });
}
else
{
SendCommand.Add(new byte[] { 0x00, 0x00 });
}
SendCommand.Add(Crc16(SendCommand.array, 6));
//第二步:发送报文 接受报文
byte[] response = new byte[8];
if (SendData(SendCommand.array, ref response))
{
//第三步:验证报文
return ByteArrayEquals(SendCommand.array, response);
}
else
{
return false;
}
}
#endregion
#region 预置单个寄存器 功能码06H
public bool PreSetSingleReg(int iDevAdd, int iAddress, short SetValue)
{
//第一步:拼接报文
ByteArray SendCommand = new ByteArray();
SendCommand.Add(new byte[] { (byte)iDevAdd, 0x06, (byte)(iAddress / 256), (byte)(iAddress % 256) });
SendCommand.Add(GetByteArrayFrom16Bit(SetValue));
SendCommand.Add(Crc16(SendCommand.array, 6));
//第二步:发送报文 接受报文
byte[] response = new byte[8];
if (SendData(SendCommand.array, ref response))
{
//第三步:验证报文
return ByteArrayEquals(SendCommand.array, response);
}
else
{
return false;
}
}
public bool PreSetSingleReg(int iDevAdd, int iAddress, ushort SetValue)
{
//第一步:拼接报文
ByteArray SendCommand = new ByteArray();
SendCommand.Add(new byte[] { (byte)iDevAdd, 0x06, (byte)(iAddress / 256), (byte)(iAddress % 256) });
SendCommand.Add(GetByteArrayFrom16Bit(SetValue));
SendCommand.Add(Crc16(SendCommand.array, 6));
//第二步:发送报文 接受报文
byte[] response = new byte[8];
if (SendData(SendCommand.array, ref response))
{
//第三步:验证报文
return ByteArrayEquals(SendCommand.array, response);
}
else
{
return false;
}
}
#endregion
#region 强制多线圈 功能码0FH
public bool ForceMultiCoil(int iDevAdd, int iAddress, bool[] SetValue)
{
//第一步:拼接报文
byte[] iSetValue = BoolArrayToByteArray(SetValue);
ByteArray SendCommand = new ByteArray();
SendCommand.Add(new byte[] { (byte)iDevAdd, 0x0F, (byte)(iAddress / 256), (byte)(iAddress % 256) });
SendCommand.Add(new byte[] { (byte)(SetValue.Length / 256), (byte)(SetValue.Length % 256) });
SendCommand.Add((byte)iSetValue.Length);
SendCommand.Add(iSetValue);
SendCommand.Add(Crc16(SendCommand.array, 7 + iSetValue.Length));
//第二步:发送报文 接受报文
byte[] response = new byte[8];
if (SendData(SendCommand.array, ref response))
{
//第三步:报文验证 验证前6个字节是否正确,再验证CRC是否正确
byte[] b = GetByteArray(response, 0, 6);
byte[] crc = Crc16(b, 6);
return ByteArrayEquals(GetByteArray(SendCommand.array, 0, 6), b) && crc[0] == response[6] && crc[1] == response[7];
}
return false;
}
#endregion
#region 预置多个寄存器 功能码10H
//浮点型 Int32 UInt32 浮点型数组 Int32数组 UInt32数组 浮点型/int16/uint16
public bool PreSetMultiByteArray(int iDevAdd, int iAddress, byte[] SetValue)
{
if (SetValue == null || SetValue.Length == 0 || SetValue.Length % 2 == 1)
{
return false;
}
int RegLength = SetValue.Length / 2;
//第一步:拼接报文
ByteArray SendCommand = new ByteArray();
SendCommand.Add(new byte[] { (byte)iDevAdd, 0x10, (byte)(iAddress / 256), (byte)(iAddress % 256) });
//寄存器数量
SendCommand.Add(new byte[] { (byte)(RegLength / 256), (byte)(RegLength % 256) });
//字节数量
SendCommand.Add((byte)SetValue.Length);
//具体字节
SendCommand.Add(SetValue);
//CRC
SendCommand.Add(Crc16(SendCommand.array, 7 + SetValue.Length));
//第二步:发送报文 接受报文
byte[] response = new byte[8];
if (SendData(SendCommand.array, ref response))
{
//第三步:报文验证 验证前6个字节是否正确,再验证CRC是否正确
byte[] b = GetByteArray(response, 0, 6);
byte[] crc = Crc16(b, 6);
return ByteArrayEquals(GetByteArray(SendCommand.array, 0, 6), b) && crc[0] == response[6] && crc[1] == response[7];
}
else
{
return false;
}
}
public bool PreSetMultiReg(int iDevAdd, int iAddress, float SetValue)
{
return PreSetMultiByteArray(iDevAdd, iAddress, GetByteArrayFrom32Bit(SetValue));
}
public bool PreSetMultiReg(int iDevAdd, int iAddress, int SetValue)
{
return PreSetMultiByteArray(iDevAdd, iAddress, GetByteArrayFrom32Bit(SetValue));
}
public bool PreSetMultiReg(int iDevAdd, int iAddress, uint SetValue)
{
return PreSetMultiByteArray(iDevAdd, iAddress, GetByteArrayFrom32Bit(SetValue));
}
public bool PreSetMultiReg(int iDevAdd, int iAddress, float[] SetValue)
{
ByteArray bSetValue = new ByteArray();
foreach (float item in SetValue)
{
bSetValue.Add(GetByteArrayFrom32Bit(item));
}
return PreSetMultiByteArray(iDevAdd, iAddress, bSetValue.array);
}
public bool PreSetMultiReg(int iDevAdd, int iAddress, int[] SetValue)
{
ByteArray bSetValue = new ByteArray();
foreach (int item in SetValue)
{
bSetValue.Add(GetByteArrayFrom32Bit(item));
}
return PreSetMultiByteArray(iDevAdd, iAddress, bSetValue.array);
}
public bool PreSetMultiReg(int iDevAdd, int iAddress, short[] SetValue)
{
ByteArray bSetValue = new ByteArray();
foreach (short item in SetValue)
{
bSetValue.Add(GetByteArrayFrom16Bit(item));
}
return PreSetMultiByteArray(iDevAdd, iAddress, bSetValue.array);
}
public bool PreSetMultiReg(int iDevAdd, int iAddress, uint[] SetValue)
{
ByteArray bSetValue = new ByteArray();
foreach (uint item in SetValue)
{
bSetValue.Add(GetByteArrayFrom32Bit(item));
}
return PreSetMultiByteArray(iDevAdd, iAddress, bSetValue.array);
}
public bool PreSetMultiReg(int iDevAdd, int iAddress, ushort[] SetValue)
{
ByteArray bSetValue = new ByteArray();
foreach (ushort item in SetValue)
{
bSetValue.Add(GetByteArrayFrom16Bit(item));
}
return PreSetMultiByteArray(iDevAdd, iAddress, bSetValue.array);
}
#endregion
#region 通用发送报文并接受
/// <summary>
/// 通用发送报文并接受
/// </summary>
/// <param name="sendByte"></param>
/// <param name="response"></param>
/// <returns></returns>
private bool SendData(byte[] sendByte, ref byte[] response)
{
//上锁
InteractiveLock.Enter();
try
{
//发送报文
MyCom.Write(sendByte, 0, sendByte.Length);
//定义一个Buffer
byte[] buffer = new byte[1024];
//定义一个内存
MemoryStream ms = new MemoryStream();
//定义读取开始时间
DateTime start = DateTime.Now;
//【1】获取当前缓冲区的值,判断是否有值,如果有值,读取过来,放到内存中
//接着再去判断,如果当前缓冲区的值为0,说明读完了
//【2】如果每次读取都读不到,我们用超时时间来做判断
while (true)
{
Thread.Sleep(SleepTime);
if (MyCom.BytesToRead >= 1)
{
int spcount = MyCom.Read(buffer, 0, buffer.Length);
ms.Write(buffer, 0, spcount);
}
else
{
//判断是否超时
if ((DateTime.Now - start).TotalMilliseconds > this.ReceiveTimeOut)
{
ms.Dispose();
return false;
}
else if (ms.Length > 0)
{
break;
}
}
}
response = ms.ToArray();
ms.Dispose();
return true;
}
catch (Exception)
{
return false;
}
finally
{
//解锁
InteractiveLock.Leave();
}
}
#endregion
#region 自定义截取字节数组
/// <summary>
/// 自定义截取字节数组
/// </summary>
/// <param name="dest">目标字节数组</param>
/// <param name="offset">偏移</param>
/// <param name="count">数量</param>
/// <returns></returns>
private byte[] GetByteArray(byte[] dest, int offset, int count)
{
byte[] res = new byte[count];
if (dest != null && dest.Length >= offset + count)
{
for (int i = 0; i < count; i++)
{
res[i] = dest[offset + i];
}
return res;
}
else
{
return null;
}
}
#endregion
#region 判断两个数组是否完全一样
private bool ByteArrayEquals(byte[] b1, byte[] b2)
{
if (b1 == null || b2 == null) return false;
if (b1.Length != b2.Length) return false;
for (int i = 0; i < b1.Length; i++)
{
if (b1[i] != b2[i])
{
return false;
}
}
return true;
}
#endregion
#region 布尔数组转换成字节数组
private byte[] BoolArrayToByteArray(bool[] val)
{
if (val == null && val.Length == 0) return null;
int iByteArrLen = 0;
if (val.Length % 8 == 0)
{
iByteArrLen = val.Length / 8;
}
else
{
iByteArrLen = val.Length / 8 + 1;
}
byte[] result = new byte[iByteArrLen];
//遍历每个字节
for (int i = 0; i < iByteArrLen; i++)
{
int total = val.Length < 8 * (i + 1) ? val.Length - 8 * i : 8;
//遍历当前字节的每个位赋值
for (int j = 0; j < total; j++)
{
result[i] = SetbitValue(result[i], j + 1, val[8 * i + j]);
}
}
return result;
}
#endregion
#region 字节某个位赋值
private byte SetbitValue(byte data, int index, bool val)
{
if (index > 8 || index < 1)
return 0;
int v = index < 2 ? index : (2 << (index - 2));
return val ? (byte)(data | v) : (byte)(data & ~v);
}
#endregion
#region 16位类型转换成字节数组
private byte[] GetByteArrayFrom16Bit(short SetValue)
{
byte[] ResTemp = BitConverter.GetBytes(SetValue);
byte[] Res = new byte[2];
switch (this.DataFormat)
{
case DataFormat.ABCD:
case DataFormat.CDAB:
Res[0] = ResTemp[1];
Res[1] = ResTemp[0];
break;
case DataFormat.BADC:
case DataFormat.DCBA:
Res = ResTemp;
break;
default:
break;
}
return Res;
}
private byte[] GetByteArrayFrom16Bit(ushort SetValue)
{
byte[] ResTemp = BitConverter.GetBytes(SetValue);
byte[] Res = new byte[2];
switch (this.DataFormat)
{
case DataFormat.ABCD:
case DataFormat.CDAB:
Res[0] = ResTemp[1];
Res[1] = ResTemp[0];
break;
case DataFormat.BADC:
case DataFormat.DCBA:
Res = ResTemp;
break;
default:
break;
}
return Res;
}
#endregion
#region 32位类型转换成字节数组
private byte[] GetByteArrayFrom32Bit(float SetValue)
{
byte[] ResTemp = BitConverter.GetBytes(SetValue);
byte[] Res = new byte[4];
switch (this.DataFormat)
{
case DataFormat.ABCD:
Res[0] = ResTemp[3];
Res[1] = ResTemp[2];
Res[2] = ResTemp[1];
Res[3] = ResTemp[0];
break;
case DataFormat.CDAB:
Res[0] = ResTemp[1];
Res[1] = ResTemp[0];
Res[2] = ResTemp[3];
Res[3] = ResTemp[2];
break;
case DataFormat.BADC:
Res[0] = ResTemp[2];
Res[1] = ResTemp[3];
Res[2] = ResTemp[0];
Res[3] = ResTemp[1];
break;
case DataFormat.DCBA:
Res = ResTemp;
break;
}
return Res;
}
private byte[] GetByteArrayFrom32Bit(int SetValue)
{
byte[] ResTemp = BitConverter.GetBytes(SetValue);
byte[] Res = new byte[4];
switch (this.DataFormat)
{
case DataFormat.ABCD:
Res[0] = ResTemp[3];
Res[1] = ResTemp[2];
Res[2] = ResTemp[1];
Res[3] = ResTemp[0];
break;
case DataFormat.CDAB:
Res[0] = ResTemp[1];
Res[1] = ResTemp[0];
Res[2] = ResTemp[3];
Res[3] = ResTemp[2];
break;
case DataFormat.BADC:
Res[0] = ResTemp[2];
Res[1] = ResTemp[3];
Res[2] = ResTemp[0];
Res[3] = ResTemp[1];
break;
case DataFormat.DCBA:
Res = ResTemp;
break;
}
return Res;
}
private byte[] GetByteArrayFrom32Bit(uint SetValue)
{
byte[] ResTemp = BitConverter.GetBytes(SetValue);
byte[] Res = new byte[4];
switch (this.DataFormat)
{
case DataFormat.ABCD:
Res[0] = ResTemp[3];
Res[1] = ResTemp[2];
Res[2] = ResTemp[1];
Res[3] = ResTemp[0];
break;
case DataFormat.CDAB:
Res[0] = ResTemp[1];
Res[1] = ResTemp[0];
Res[2] = ResTemp[3];
Res[3] = ResTemp[2];
break;
case DataFormat.BADC:
Res[0] = ResTemp[2];
Res[1] = ResTemp[3];
Res[2] = ResTemp[0];
Res[3] = ResTemp[1];
break;
case DataFormat.DCBA:
Res = ResTemp;
break;
}
return Res;
}
#endregion
#region CRC校验
private static readonly byte[] aucCRCHi = {
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40
};
private static readonly byte[] aucCRCLo = {
0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06, 0x07, 0xC7,
0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD, 0x0F, 0xCF, 0xCE, 0x0E,
0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09, 0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9,
0x1B, 0xDB, 0xDA, 0x1A, 0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC,
0x14, 0xD4, 0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3,
0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3, 0xF2, 0x32,
0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4, 0x3C, 0xFC, 0xFD, 0x3D,
0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A, 0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38,
0x28, 0xE8, 0xE9, 0x29, 0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF,
0x2D, 0xED, 0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26,
0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60, 0x61, 0xA1,
0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67, 0xA5, 0x65, 0x64, 0xA4,
0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F, 0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB,
0x69, 0xA9, 0xA8, 0x68, 0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA,
0xBE, 0x7E, 0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5,
0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71, 0x70, 0xB0,
0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92, 0x96, 0x56, 0x57, 0x97,
0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C, 0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E,
0x5A, 0x9A, 0x9B, 0x5B, 0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89,
0x4B, 0x8B, 0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,
0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42, 0x43, 0x83,
0x41, 0x81, 0x80, 0x40
};
private byte[] Crc16(byte[] pucFrame, int usLen)
{
int i = 0;
byte[] res = new byte[2] { 0xFF, 0xFF };
UInt16 iIndex = 0x0000;
while (usLen-- > 0)
{
iIndex = (UInt16)(res[0] ^ pucFrame[i++]);
res[0] = (byte)(res[1] ^ aucCRCHi[iIndex]);
res[1] = aucCRCLo[iIndex];
}
return res;
}
#endregion
}
}
ByteArray:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DAL
{
public class ByteArray
{
List<byte> list = null;
//初始化
public ByteArray()
{
list = new List<byte>();
}
//添加单个字节
public void Add(byte item)
{
list.Add(item);
}
//添加数组
public void Add(byte[] item)
{
list.AddRange(item);
}
//清除
public void Clear()
{
list = new List<byte>();
}
//获取数组
public byte[] array
{
get { return list.ToArray(); }
}
}
}
TCP:
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 DAL
{
public class ModbusTcp
{
#region 属性字段
//ModbusTcp:Modbus协议在TCP/IP网络上的应用
//创建通信对象
private Socket tcpClient;
//发送超时
public int SendTimeOut { get; set; } = 2000;
public int ReceiveTimeOut { get; set; } = 2000;
//从站地址
public int SlaveAddress { get; set; } = 1;
public int MaxCycleTimer { get; set; } = 5;
//编码格式
public DataFormat DataFormat { get; set; } = DataFormat.ABCD;
//互斥锁
private SimpleHybirdLock InteractiveLock = new SimpleHybirdLock();
#endregion
#region 建立及断开连接
/// <summary>
/// 建立连接
/// </summary>
/// <param name="ip">IP地址</param>
/// <param name="port">端口号</param>
public void Connect(string ip, string port)
{
//实例化Socket
tcpClient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//设置Socket属性
tcpClient.SendTimeout = this.SendTimeOut;
tcpClient.ReceiveTimeout = this.ReceiveTimeOut;
//封装一个EndPoint对象
IPEndPoint endPoint = new IPEndPoint(IPAddress.Parse(ip), int.Parse(port));
//建立连接
tcpClient.Connect(endPoint);
}
/// <summary>
/// 断开连接
/// </summary>
public void DisConnect()
{
if (tcpClient != null)
{
tcpClient.Close();
}
}
#endregion
#region 读取输出线圈 功能码0x01
public byte[] ReadOutputStatus(int iAddress, int iLength)
{
//第一步:拼接报文
ByteArray SendCommand = new ByteArray();
SendCommand.Add(new byte[] { 0, 0, 0, 0, 0, 6, (byte)SlaveAddress, 1 });
SendCommand.Add(new byte[] { (byte)(iAddress / 256), (byte)(iAddress % 256) });
SendCommand.Add(new byte[] { (byte)(iLength / 256), (byte)(iLength % 256) });
//第二步:发送报文
//第三步:接收报文
byte[] rcv = SendAndReceive(SendCommand.array);
//第四步:验证报文
int length = 0;
if (iLength % 8 == 0)
{
length = iLength / 8;
}
else
{
length = iLength / 8 + 1;
}
if (rcv != null && rcv.Length == 9 + length)
{
if (rcv[7] == 0x01 && rcv[8] == length)
{
// 第五步:解析报文
return GetByteArray(rcv, 9, length);
}
else
{
return null;
}
}
else
{
return null;
}
}
#endregion
#region 读取输入线圈 功能码0x02
public byte[] ReadInputStatus(int iAddress, int iLength)
{
//第一步:拼接报文
ByteArray SendCommand = new ByteArray();
SendCommand.Add(new byte[] { 0, 0, 0, 0, 0, 6, (byte)SlaveAddress, 2 });
SendCommand.Add(new byte[] { (byte)(iAddress / 256), (byte)(iAddress % 256) });
SendCommand.Add(new byte[] { (byte)(iLength / 256), (byte)(iLength % 256) });
//第二步:发送报文
//第三步:接收报文
byte[] rcv = SendAndReceive(SendCommand.array);
//第四步:验证报文
int length = 0;
if (iLength % 8 == 0)
{
length = iLength / 8;
}
else
{
length = iLength / 8 + 1;
}
if (rcv != null && rcv.Length == 9 + length)
{
if (rcv[7] == 0x02 && rcv[8] == length)
{
// 第五步:解析报文
return GetByteArray(rcv, 9, length);
}
else
{
return null;
}
}
else
{
return null;
}
}
#endregion
#region 读取保持寄存器 功能码0x03
public byte[] ReadKeepReg(int iAddress, int iLength)
{
//第一步:拼接报文
ByteArray SendCommand = new ByteArray();
SendCommand.Add(new byte[] { 0, 0, 0, 0, 0, 6, (byte)SlaveAddress, 3 });
SendCommand.Add(new byte[] { (byte)(iAddress / 256), (byte)(iAddress % 256) });
SendCommand.Add(new byte[] { (byte)(iLength / 256), (byte)(iLength % 256) });
//第二步:发送报文
//第三步:接收报文
byte[] rcv = SendAndReceive(SendCommand.array);
//第四步:验证报文
if (rcv != null && rcv.Length == 9 + iLength * 2)
{
if (rcv[7] == 0x03 && rcv[8] == iLength * 2)
{
// 第五步:解析报文
return GetByteArray(rcv, 9, iLength * 2);
}
else
{
return null;
}
}
else
{
return null;
}
}
#endregion
#region 读取输入寄存器 功能码0x04
public byte[] ReadInputReg(int iAddress, int iLength)
{
//第一步:拼接报文
ByteArray SendCommand = new ByteArray();
SendCommand.Add(new byte[] { 0, 0, 0, 0, 0, 6, (byte)SlaveAddress, 4 });
SendCommand.Add(new byte[] { (byte)(iAddress / 256), (byte)(iAddress % 256) });
SendCommand.Add(new byte[] { (byte)(iLength / 256), (byte)(iLength % 256) });
//第二步:发送报文
//第三步:接收报文
byte[] rcv = SendAndReceive(SendCommand.array);
//第四步:验证报文
if (rcv != null && rcv.Length == 9 + iLength * 2)
{
if (rcv[7] == 0x03 && rcv[8] == iLength * 2)
{
// 第五步:解析报文
return GetByteArray(rcv, 9, iLength * 2);
}
else
{
return null;
}
}
else
{
return null;
}
}
#endregion
#region 预置单线圈 功能码0x05
/// <summary>
/// 强制单个线圈
/// </summary>
/// <param name="iAddress"></param>
/// <param name="value"></param>
/// <returns></returns>
public bool ForceCoil(int iAddress, bool value)
{
//第一步:拼接报文
ByteArray SendCommand = new ByteArray();
SendCommand.Add(new byte[] { 0, 0, 0, 0, 0, 6, (byte)SlaveAddress, 5 });
SendCommand.Add(new byte[] { (byte)(iAddress / 256), (byte)(iAddress % 256) });
if (value)
{
SendCommand.Add(new byte[] { (byte)(0xFF), (byte)(0x00) });
}
else
{
SendCommand.Add(new byte[] { (byte)(0x00), (byte)(0x00) });
}
//第二步:发送报文
//第三步:接收报文
byte[] rcv = SendAndReceive(SendCommand.array);
//第四步:验证报文
if (rcv != null)
{
return ByteArrayEquals(SendCommand.array, rcv);
}
else
{
return false;
}
}
#endregion
#region 预置单个寄存器 功能码0x06
public bool PreSetSingleReg(int iAddress, short SetValue)
{
//第一步:拼接报文
ByteArray SendCommand = new ByteArray();
SendCommand.Add(new byte[] { 0, 0, 0, 0, 0, 6, (byte)SlaveAddress, 6 });
SendCommand.Add(new byte[] { (byte)(iAddress / 256), (byte)(iAddress % 256) });
SendCommand.Add(GetByteArrayFrom16Bit(SetValue));
//第二步:发送报文
//第三步:接收报文
byte[] rcv = SendAndReceive(SendCommand.array);
if (rcv != null)
{
return ByteArrayEquals(SendCommand.array, rcv);
}
else
{
return false;
}
}
public bool PreSetSingleReg(int iAddress, ushort SetValue)
{
//第一步:拼接报文
ByteArray SendCommand = new ByteArray();
SendCommand.Add(new byte[] { 0, 0, 0, 0, 0, 6, (byte)SlaveAddress, 6 });
SendCommand.Add(new byte[] { (byte)(iAddress / 256), (byte)(iAddress % 256) });
SendCommand.Add(GetByteArrayFrom16Bit(SetValue));
//第二步:发送报文
//第三步:接收报文
byte[] rcv = SendAndReceive(SendCommand.array);
if (rcv != null)
{
return ByteArrayEquals(SendCommand.array, rcv);
}
else
{
return false;
}
}
#endregion
#region 预置多个线圈 功能码0x0F
public bool ForceMultiCoil(int iAddress, bool[] SetValue)
{
byte[] iSetValue = BoolArrayToByteArray(SetValue);
//第一步:拼接报文
ByteArray SendCommand = new ByteArray();
SendCommand.Add(new byte[] { 0, 0, 0, 0});
int byteLength = 7 + iSetValue.Length;
//字节长度
SendCommand.Add((byte)(byteLength / 256));
SendCommand.Add((byte)(byteLength %256));
//单元标识符和功能码
SendCommand.Add(new byte[] { (byte)SlaveAddress,0x0F});
//起始线圈
SendCommand.Add((byte)(iAddress / 256));
SendCommand.Add((byte)(iAddress % 256));
//线圈数量
SendCommand.Add((byte)(SetValue.Length / 256));
SendCommand.Add((byte)(SetValue.Length % 256));
//字节计数
SendCommand.Add((byte)(iSetValue.Length));
//具体字节
SendCommand.Add(iSetValue);
//第二步:发送报文
//第三步:接收报文
byte[] rcv = SendAndReceive(SendCommand.array);
if (rcv != null)
{
byte[] send = GetByteArray(SendCommand.array, 0, 12);
send[4] = 0x00;
send[5] = 0x06;
return ByteArrayEquals(send, rcv);
}
else
{
return false;
}
}
#endregion
#region 预置多个寄存器
//float Int32 Uint32 float[] Int32[] UInt32[] A float B int32 C Uint32
//字节数组
public bool PreSetMultiByteArray(int iAddress, byte[] SetValue)
{
//首先判断一下字节数组是否正确
if (SetValue == null || SetValue.Length == 0 || SetValue.Length % 2 == 1)
{
return false;
}
int RegLength = SetValue.Length / 2;
//第一步:拼接报文
ByteArray SendCommand = new ByteArray();
SendCommand.Add(new byte[] { 0, 0, 0, 0 });
int byteLength = 7 + SetValue.Length;
//字节长度
SendCommand.Add((byte)(byteLength / 256));
SendCommand.Add((byte)(byteLength % 256));
//单元标识符和功能码
SendCommand.Add(new byte[] { (byte)SlaveAddress, 0x10 });
//起始寄存器
SendCommand.Add((byte)(iAddress / 256));
SendCommand.Add((byte)(iAddress % 256));
//寄存器数量
SendCommand.Add((byte)(RegLength / 256));
SendCommand.Add((byte)(RegLength % 256));
//字节计数
SendCommand.Add((byte)(SetValue.Length));
//具体字节
SendCommand.Add(SetValue);
//第二步:发送报文
//第三步:接收报文
byte[] rcv = SendAndReceive(SendCommand.array);
if (rcv != null)
{
byte[] send = GetByteArray(SendCommand.array, 0, 12);
send[4] = 0x00;
send[5] = 0x06;
return ByteArrayEquals(send, rcv);
}
else
{
return false;
}
}
public bool PreSetMultiReg(int iAddress, float SetValue)
{
return PreSetMultiByteArray(iAddress, GetByteArrayFrom32Bit(SetValue));
}
public bool PreSetMultiReg(int iAddress, int SetValue)
{
return PreSetMultiByteArray(iAddress, GetByteArrayFrom32Bit(SetValue));
}
public bool PreSetMultiReg(int iAddress, uint SetValue)
{
return PreSetMultiByteArray(iAddress, GetByteArrayFrom32Bit(SetValue));
}
/// <summary>
/// 写入浮点型数组
/// </summary>
/// <param name="iAddress"></param>
/// <param name="SetValue"></param>
/// <returns></returns>
public bool PreSetMultiReg(int iAddress, float[] SetValue)
{
ByteArray bSetValue = new ByteArray();
foreach (var item in SetValue)
{
bSetValue.Add(GetByteArrayFrom32Bit(item));
}
return PreSetMultiByteArray(iAddress, bSetValue.array);
}
/// <summary>
/// 写入int数组
/// </summary>
/// <param name="iAddress"></param>
/// <param name="SetValue"></param>
/// <returns></returns>
public bool PreSetMultiReg(int iAddress, int[] SetValue)
{
ByteArray bSetValue = new ByteArray();
foreach (var item in SetValue)
{
bSetValue.Add(GetByteArrayFrom32Bit(item));
}
return PreSetMultiByteArray(iAddress, bSetValue.array);
}
/// <summary>
/// 写入uint数组
/// </summary>
/// <param name="iAddress"></param>
/// <param name="SetValue"></param>
/// <returns></returns>
public bool PreSetMultiReg(int iAddress, uint[] SetValue)
{
ByteArray bSetValue = new ByteArray();
foreach (var item in SetValue)
{
bSetValue.Add(GetByteArrayFrom32Bit(item));
}
return PreSetMultiByteArray(iAddress, bSetValue.array);
}
// short ushort short[] ushort[]
public bool PreSetMultiReg(int iAddress, short SetValue)
{
return PreSetMultiByteArray(iAddress, GetByteArrayFrom16Bit(SetValue));
}
public bool PreSetMultiReg(int iAddress, ushort SetValue)
{
return PreSetMultiByteArray(iAddress, GetByteArrayFrom16Bit(SetValue));
}
public bool PreSetMultiReg(int iAddress, short[] SetValue)
{
ByteArray bSetValue = new ByteArray();
foreach (var item in SetValue)
{
bSetValue.Add(GetByteArrayFrom16Bit(item));
}
return PreSetMultiByteArray(iAddress, bSetValue.array);
}
public bool PreSetMultiReg(int iAddress, ushort[] SetValue)
{
ByteArray bSetValue = new ByteArray();
foreach (var item in SetValue)
{
bSetValue.Add(GetByteArrayFrom16Bit(item));
}
return PreSetMultiByteArray(iAddress, bSetValue.array);
}
#endregion
#region 自定义截取字节数组
/// <summary>
/// 自定义截取字节数组
/// </summary>
/// <param name="dest"></param>
/// <param name="offset"></param>
/// <param name="count"></param>
/// <returns></returns>
private byte[] GetByteArray(byte[] dest, int offset, int count)
{
if (dest != null && dest.Length >= offset + count)
{
byte[] result = new byte[count];
Array.Copy(dest, offset, result, 0, count);
return result;
}
else
{
return null;
}
}
#endregion
#region 判断两个字节数组是否完全一致
private bool ByteArrayEquals(byte[] b1, byte[] b2)
{
if (b1 == null || b2 == null) return false;
if (b1.Length == 0 || b2.Length == 0 || b1.Length != b2.Length) return false;
for (int i = 0; i < b1.Length; i++)
{
if (b1[i] != b2[i])
{
return false;
}
}
return true;
}
#endregion
#region 布尔数组转换成字节数组
private byte[] BoolArrayToByteArray(bool[] val)
{
if (val == null || val.Length == 0) return null;
int iByteArrLen = 0;
if (val.Length % 8 == 0)
{
iByteArrLen = val.Length / 8;
}
else
{
iByteArrLen = val.Length / 8 + 1;
}
byte[] result = new byte[iByteArrLen];
for (int i = 0; i < iByteArrLen; i++)
{
//比如我们布尔的长度是20, 1-8表示第一个字节,9-16表示第二个字节,剩下的4个布尔
int total = i == iByteArrLen - 1 ? val.Length - 8 * i : 8;
for (int j = 0; j < total; j++)
{
result[i] = SetbitValue(result[i], j + 1, val[8 * i + j]);
}
}
return result;
}
#endregion
#region 给字节某个位赋值
private byte SetbitValue(byte data, int index, bool val)
{
if (index > 8 || index < 1)
return 0;
int v = index < 2 ? index : (2 << (index - 2));
return val ? (byte)(data | v) : (byte)(data & ~v);
}
#endregion
#region 16位整型转换字节数组
private byte[] GetByteArrayFrom16Bit(short SetValue)
{
byte[] ResTemp = BitConverter.GetBytes(SetValue);
byte[] Res = new byte[2];
switch (this.DataFormat)
{
case DataFormat.ABCD:
case DataFormat.CDAB:
Res[0] = ResTemp[1];
Res[1] = ResTemp[0];
break;
case DataFormat.BADC:
case DataFormat.DCBA:
Res = ResTemp;
break;
default:
break;
}
return Res;
}
private byte[] GetByteArrayFrom16Bit(ushort SetValue)
{
byte[] ResTemp = BitConverter.GetBytes(SetValue);
byte[] Res = new byte[2];
switch (this.DataFormat)
{
case DataFormat.ABCD:
case DataFormat.CDAB:
Res[0] = ResTemp[1];
Res[1] = ResTemp[0];
break;
case DataFormat.BADC:
case DataFormat.DCBA:
Res = ResTemp;
break;
default:
break;
}
return Res;
}
#endregion
#region 32位数据转换字节数组
private byte[] GetByteArrayFrom32Bit(float SetValue)
{
byte[] ResTemp = BitConverter.GetBytes(SetValue);
byte[] Res = new byte[4];
switch (this.DataFormat)
{
case DataFormat.ABCD:
Res[0] = ResTemp[3];
Res[1] = ResTemp[2];
Res[2] = ResTemp[1];
Res[3] = ResTemp[0];
break;
case DataFormat.BADC:
Res[0] = ResTemp[2];
Res[1] = ResTemp[3];
Res[2] = ResTemp[0];
Res[3] = ResTemp[1];
break;
case DataFormat.CDAB:
Res[0] = ResTemp[1];
Res[1] = ResTemp[0];
Res[2] = ResTemp[3];
Res[3] = ResTemp[2];
break;
case DataFormat.DCBA:
Res[0] = ResTemp[0];
Res[1] = ResTemp[1];
Res[2] = ResTemp[2];
Res[3] = ResTemp[3];
break;
default:
break;
}
return Res;
}
private byte[] GetByteArrayFrom32Bit(int SetValue)
{
byte[] ResTemp = BitConverter.GetBytes(SetValue);
byte[] Res = new byte[4];
switch (this.DataFormat)
{
case DataFormat.ABCD:
Res[0] = ResTemp[3];
Res[1] = ResTemp[2];
Res[2] = ResTemp[1];
Res[3] = ResTemp[0];
break;
case DataFormat.BADC:
Res[0] = ResTemp[2];
Res[1] = ResTemp[3];
Res[2] = ResTemp[0];
Res[3] = ResTemp[1];
break;
case DataFormat.CDAB:
Res[0] = ResTemp[1];
Res[1] = ResTemp[0];
Res[2] = ResTemp[3];
Res[3] = ResTemp[2];
break;
case DataFormat.DCBA:
Res[0] = ResTemp[0];
Res[1] = ResTemp[1];
Res[2] = ResTemp[2];
Res[3] = ResTemp[3];
break;
default:
break;
}
return Res;
}
private byte[] GetByteArrayFrom32Bit(uint SetValue)
{
byte[] ResTemp = BitConverter.GetBytes(SetValue);
byte[] Res = new byte[4];
switch (this.DataFormat)
{
case DataFormat.ABCD:
Res[0] = ResTemp[3];
Res[1] = ResTemp[2];
Res[2] = ResTemp[1];
Res[3] = ResTemp[0];
break;
case DataFormat.BADC:
Res[0] = ResTemp[2];
Res[1] = ResTemp[3];
Res[2] = ResTemp[0];
Res[3] = ResTemp[1];
break;
case DataFormat.CDAB:
Res[0] = ResTemp[1];
Res[1] = ResTemp[0];
Res[2] = ResTemp[3];
Res[3] = ResTemp[2];
break;
case DataFormat.DCBA:
Res[0] = ResTemp[0];
Res[1] = ResTemp[1];
Res[2] = ResTemp[2];
Res[3] = ResTemp[3];
break;
default:
break;
}
return Res;
}
#endregion
#region 读取并接受
/// <summary>
/// 发送并接受
/// </summary>
/// <param name="SendByte"></param>
/// <returns></returns>
private byte[] SendAndReceive(byte[] SendByte)
{
InteractiveLock.Enter();
try
{
tcpClient.Send(SendByte);
return ReadMessage();
}
catch (Exception)
{
return null;
}
finally
{
InteractiveLock.Leave();
}
}
/// <summary>
/// 读取缓冲区值
/// </summary>
/// <returns></returns>
private byte[] ReadMessage()
{
int count = tcpClient.Available;
int cycletimer = 0;
while (count == 0)
{
count = tcpClient.Available;
cycletimer++;
Thread.Sleep(20);
if (cycletimer > MaxCycleTimer)
{
break;
}
}
if (count == 0)
{
return null;
}
else
{
byte[] buffer = new byte[count];
tcpClient.Receive(buffer, count, SocketFlags.None);
return buffer;
}
}
#endregion
}
}