先上两个通用Modbus帮助类,下面这个是多线程不安全版,在多线程多电机同一端口通信下,可能造成步进电机丢步或者输出口无响应等,还有个多线程安全版,只是基于这个不安全版加上了LOCK,THIS
using Modbus.Device; using Sunny.UI; using System; using System.IO.Ports; using System.Linq; using System.Net; using System.Net.Sockets; using System.Threading; namespace SunnyUI_NuclearForm { /// <summary> /// Modbus通用帮助类 /// </summary> public class ModbusHelper : IDisposable { IModbusMaster master; SerialPort serialPort //网口-TCP TcpClient t//private static readonly object threadObj = new object();//线程安全锁 //ManualResetEvent manualEvent = new ManualResetEvent(true);//true运行,false不运行 #region connection /// <summary> /// 串口连接 /// </summary> /// <returns></returns> public bool ConnectForSerialPort(string com, int baudRate) { bool result = false; if (!string.IsNullOrWhiteSpace(com) && baudRate > 0) { serialPort = new SerialPort(); serialPort.PortName = com; serialPort.BaudRate = baudRate; //下面三个参数看情况要不要开放 serialPort.DataBits = 8; serialPort.StopBits = StopBits.One; serialPort.Parity = Parity.None; try { if (!serialPort.IsOpen) { serialPort.Open(); } master = ModbusSerialMaster.CreateRtu(serialPort); master.Transport.ReadTimeout = 3 * 1000;//读 超时时间 master.Transport.WriteTimeout = 3 * 1000;//写 超时时间 master.Transport.Retries = 20;//重试次数 master.Transport.WaitToRetryMilliseconds = 3 * 1000;//重试间隔 result = true; } catch (Exception e) { //打开失败 LogHelper.Error($"串口无法连接!COM:{com},波特率:{baudRate}。以下为详细信息:\r\n{e.Message}"); result = false; } } return result; } /// <summary> /// 网口TCP连接 /// </summary> /// <param name="ip"></param> /// <returns></returns> public bool ConnectForTCP(string ip, int port) { bool result = false; if (!string.IsNullOrWhiteSpace(ip) && port > 0) { try { tcpClient = new TcpClient(ip, port); if (tcpClient.Connected) { master = ModbusIpMaster.CreateIp(tcpClient); master.Transport.ReadTimeout = 3 * 1000;//读 超时时间 master.Transport.WriteTimeout = 3 * 1000;//写 超时时间 master.Transport.Retries = 20;//重试次数 master.Transport.WaitToRetryMilliseconds = 3 * 1000;//重试间隔 result = true; } else { LogHelper.Error($"网口无法连接!IP:{ip},端口:{port}。\r\n"); result = false; } } catch (Exception e) { LogHelper.Error($"网口无法连接!IP:{ip},端口:{port}。以下为详细信息:\r\n{e.Message}"); return false; } } return result; } /// <summary> /// Socket连接(备用) /// </summary> /// <param name="ip"></param> /// <param name="port"></param> /// <returns></returns> public bool ConnectForSocket(string ip, int port) { bool result = false; if (!string.IsNullOrWhiteSpace(ip) && port > 0) { //部分硬件,用TCP发指令没响应,但是用Socket可以,不知道是没支持好TCP还是没支持好Modbus //所以在此留下备用通信方法 socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); try { socket.Connect(IPAddress.Parse(ip), port); result = true; } catch (Exception e) { //连接失败 LogHelper.Error($"Socket(备用)无法连接!IP:{ip},端口:{port}。以下为详细信息:\r\n{e.Message}"); result = false; } } return result; } #endregion #region send /// <summary> /// 向串口发送数据 Modbus报文(字节) /// </summary> public void SendSerialByte(byte[] arr) { //this.manualEvent.WaitOne();//等待 //lock (threadObj) //{ //this.manualEvent.Reset();//红灯 if (serialPort != null && serialPort.IsOpen && arr != null && arr.Count() > 0) { serialPort.DiscardInBuffer(); serialPort.DiscardOutBuffer(); Thread.Sleep(50); serialPort.Write(arr, 0, arr.Length); Thread.Sleep(50);//写入读取命令后延时读取 或者跟下一条命令之间产生间隔 try { // 接收来自读取命令的数据 广播命令执行完毕不响应 //byte[] receiveData = new byte[1024]; //int bytesRead = serialPort.Read(receiveData, 0, receiveData.Length); if (serialPort.BytesToRead > 0) { var result = serialPort.ReadExisting(); } } catch (Exception e) { } } //this.manualEvent.Set();//绿灯 //} } /// <summary> /// 向串口发送数据 Modbus报文(字符串) /// </summary> public string SendSerialString(string str) { string result = string.Empty; //this.manualEvent.WaitOne();//等待 //lock (threadObj) //{ //this.manualEvent.Reset();//红灯 if (serialPort != null && serialPort.IsOpen && !string.IsNullOrWhiteSpace(str)) { serialPort.DiscardInBuffer(); serialPort.DiscardOutBuffer(); Thread.Sleep(50); serialPort.Write(str); Thread.Sleep(50);//写入读取命令后延时读取 或者跟下一条命令之间产生间隔 try { // 接收来自读取命令的数据 广播命令执行完毕不响应 if (serialPort.BytesToRead > 0) { result = serialPort.ReadExisting(); } } catch (Exception e) { } } //this.manualEvent.Set();//绿灯 //} return result; } /// <summary> /// 向网口发送数据 Modbus报文 /// </summary> /// <param name="arr"></param> public void SendTCPByte(byte[] arr) { if (arr != null && arr.Count() > 0 && tcpClient.Connected) { // 获取网络流 NetworkStream stream = tcpClient.GetStream(); //tcpClient.Client.Send(arr); stream.Write(arr, 0, arr.Length); //Thread.Sleep(50);//写入读取命令后延时读取 或者跟下一条命令之间产生间隔 try { // 接收来自读取命令的数据 广播命令执行完毕不响应 byte[] receiveData = new byte[1024]; int bytesRead = stream.Read(receiveData, 0, receiveData.Length); } catch (Exception) { throw; } } } /// <summary> /// 向网口发送数据 Modbus报文(Socket备用) /// </summary> /// <param name="arr"></param> public void SendSocketByte(byte[] arr) { if (arr != null && arr.Count() > 0 && socket.Connected) { socket.Send(arr); //Thread.Sleep(50);//写入读取命令后延时读取 或者跟下一条命令之间产生间隔 try { // 接收来自读取命令的数据 广播命令执行完毕不响应 byte[] buffer = new byte[1024]; int byteRead = socket.Receive(buffer); } catch (Exception) { throw; } } } #endregion #region read /// <summary> /// 读取单个线圈 Modbus 01功能码 /// </summary> /// <param name="slaveAddress">从站号</param> /// <param name="StartAddress">开始读取的寄存器地址</param> /// <param name="numberOfPoints">读取数据个数</param> /// <returns></returns> public bool[] ReadCoils_01(byte slaveAddress, ushort startAddress, ushort numberOfPoints) { return master.ReadCoils(slaveAddress, startAddress, numberOfPoints); } /// <summary> /// 读取离散/输入线圈 Modbus 02功能码 /// </summary> /// <param name="slaveAddress">从站号</param> /// <param name="StartAddress">开始读取的寄存器地址</param> /// <param name="numberOfPoints">读取数据个数</param> /// <returns></returns> public bool[] ReadInputs_02(byte slaveAddress, ushort startAddress, ushort numberOfPoints) { return master.ReadInputs(slaveAddress, startAddress, numberOfPoints); } /// <summary> /// 读取保持型寄存器 Modbus 03功能码 /// </summary> /// <param name="slaveAddress">从站号</param> /// <param name="StartAddress">开始读取的寄存器地址</param> /// <param name="numberOfPoints">读取数据个数</param> /// <returns></returns> public ushort[] ReadHoldingRegisters_03(byte slaveAddress, ushort StartAddress, ushort numberOfPoints) { try { ushort[] Uvalue = master.ReadHoldingRegisters(slaveAddress, StartAddress, numberOfPoints); //ushort 转short //short[] value = new short[Uvalue.Length]; //for (int i = 0; i < Uvalue.Length; i++) //{ // if (Uvalue[i] > short.MaxValue) // { // value[i] = (short)(Uvalue[i] - 65536); // } // else // { // value[i] = (short)Uvalue[i]; // } //} return Uvalue; } catch (Exception e) { return null; } } /// <summary> /// 读取输入寄存器 Modbus 04功能码 /// </summary> /// <param name="slaveAddress">从站号</param> /// <param name="StartAddress">开始读取的寄存器地址</param> /// <param name="numberOfPoints">读取数据个数</param> /// <returns></returns> public ushort[] ReadInputRegisters_04(byte slaveAddress, ushort startAddress, ushort numberOfPoints) { try { ushort[] Uvalue = master.ReadInputRegisters(slaveAddress, startAddress, numberOfPoints); //ushort 转short //short[] value = new short[Uvalue.Length]; //for (int i = 0; i < Uvalue.Length; i++) //{ // if (Uvalue[i] > short.MaxValue) // { // value[i] = (short)(Uvalue[i] - 65536); // } // else // { // value[i] = (short)Uvalue[i]; // } //} return Uvalue; } catch (Exception e) { return null; } } #endregion #region write /// <summary> /// 写单个线圈 Modbus 05功能码 /// </summary> /// <param name="slaveAddress"></param> /// <param name="coilAddress"></param> /// <param name="value"></param> public void WriteSingleCoil_05(byte slaveAddress, ushort coilAddress, bool value) { master.WriteSingleCoil(slaveAddress, coilAddress, value); } /// <summary> /// 写单个保持寄存器 Modbus 06功能码 /// </summary> /// <param name="slaveAddress"></param> /// <param name="registerAddress"></param> /// <param name="value"></param> public void WriteSingleRegister_06(byte slaveAddress, ushort registerAddress, ushort value) { try { //short 转 ushort //ushort Uvalue; //if (value < 0) //{ // Uvalue = (ushort)(ushort.MaxValue + value + 1); //} //else //{ // Uvalue = (ushort)value; //} master.WriteSingleRegister(slaveAddress, registerAddress, value); } catch (Exception e) { return; } } /// <summary> /// 写多个寄存器 Modbus 10功能码 /// </summary> /// <param name="slaveAddress"></param> /// <param name="startAddress"></param> /// <param name="data"></param> public void WriteMultipleRegisters_10(byte slaveAddress, ushort startAddress, ushort[] data) { try { master.WriteMultipleRegisters(slaveAddress, startAddress, data); //master.WriteMultipleRegistersAsync(slaveAddress, startAddress, data); } catch (Exception e) { } } #endregion #region 鸣志伺服-多线程不安全模式 // 设置加减速,减速度,速度 数值1-10k/分钟 public void SetAccelerationSpeedDeceleration(byte slaveAddr, ushort acceleration, ushort deceleration, ushort speed) { ushort parametersAddress = 344; ushort[] parameters = new ushort[6]; parameters[0] = 0; parameters[1] = acceleration; parameters[2] = 0; parameters[3] = deceleration; parameters[4] = 0; parameters[5] = speed; WriteMultipleRegisters_10(slaveAddr, parametersAddress, parameters); } /// <summary> /// 读取伺服电机加速度减速度速度 /// </summary> /// <param name="slaveAddr"></param> /// <returns></returns> public ushort[] ReadAccelerationSpeedDeceleration(byte slaveAddr) { ushort[] result = new ushort[3]; ushort[] speedArr = ReadHoldingRegisters_03(slaveAddr, 344, 6); if (speedArr != null && speedArr.Length >= 6) { //AC 加速度 344+1 //DC 减速度 346+1 //VE 速度 348+1 result[0] = speedArr[1]; result[1] = speedArr[3]; result[2] = speedArr[5]; } return result; } // 设置距离 数值1-100/圈 public void Distance(byte slaveAddr, long distance) { ushort parametersAddress = 350; ushort[] parameters = new ushort[1]; parameters[0] = (ushort)distance; WriteMultipleRegisters_10(slaveAddr, parametersAddress, parameters); } // 设置距离 脉冲数 public void DistanceForPulse(byte slaveAddr, int pulse) { string convert = pulse.ToString("X").PadLeft(8, '0'); //转十六进制然后八位补零 var sendByte = new byte[] { slaveAddr, 0X10, 0x01, 0x5E, 0x00, 0x02 ,0x04 , Convert.ToByte(convert.Substring(0, 2), 16), Convert.ToByte(convert.Substring(2, 2), 16), Convert.ToByte(convert.Substring(4, 2), 16), Convert.ToByte(convert.Substring(6, 2), 16)}; var crc = CRC16.CRC16Calc(sendByte); var newSend = sendByte.Concat(crc).ToArray(); SendSerialByte(newSend); } //Modbus 寄存器表中寄存器40125被定义为操作码寄存器,向40125寄存器写入相应的操作码,即执行相应操作码的动作 //FL 0x66 //SK 0xE1 //SO 0x8B /// <summary> /// 电机旋转运行到指定距离 (执行相对移动命令,移动距离和方向来自最后一个DI命令 按给定长度进至距离) /// </summary> /// <param name="slaveAddr"></param> public void Opcode_FL(byte slaveAddr) { ushort operationCodeAddress = 124; byte opCode = 0x66; WriteSingleRegister_06(slaveAddr, operationCodeAddress, opCode); } /// <summary> /// 强制停止 /// </summary> /// <param name="slaveAddr"></param> public void Opcode_SK(byte slaveAddr) { ushort operationCodeAddress = 124; byte opCode = 0xE1; WriteSingleRegister_06(slaveAddr, operationCodeAddress, opCode); } ///// <summary> ///// 强制停止-带缓冲 ///// </summary> ///// <param name="slaveAddr"></param> //public void Opcode_SKD(byte slaveAddr) //{ // ushort operationCodeAddress = 124; // byte opCode = 0xE2; // master.WriteSingleRegister(slaveAddr, operationCodeAddress, opCode); //} /// <summary> /// 报警清除 /// </summary> /// <param name="slaveAddr"></param> public void Opcode_AX(byte slaveAddr) { ushort operationCodeAddress = 124; byte opCode = 0xBA; WriteSingleRegister_06(slaveAddr, operationCodeAddress, opCode); } //1 0x31 数字量输入/输出端口1 //2 0x32 数字量输入/输出端口2 //3 0x33 数字量输入/输出端口3 //4 0x34 数字量输入/输出端口4 //5 0x35 数字量输入/输出端口5 //6 0x36 数字量输入/输出端口6 //7 0x37 数字量输出端口7 //8 0x38 数字量输出端口8 //9 0x39 数字量输出端口9 //L 0x4C 低电平(光耦导通) //H 0x48 高电平(光耦断开) /// <summary> /// Y口输出 L 0x4C H 0x48 /// </summary> /// <param name="slaveAddr"&