NET工控,上位机,Modbus485网口/串口通讯(鸣志步进电机,鸣志伺服电机,松下伺服电机,华庆军继电器模块)

先上两个通用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"&
上一篇:The First项目报告:解读去中心化衍生品交易所AVEO


下一篇:乡村振兴与农村环境整治:加强农村环境治理,改善农村人居环境,打造干净整洁、生态宜居的美丽乡村