当前位置: 代码网 > it编程>编程语言>C# > 使用C#实现自己封装的Modbus工具类库

使用C#实现自己封装的Modbus工具类库

2024年05月19日 C# 我要评论
前言modbus通讯协议在工控行业的应用是很多的,并且也是上位机开发的基本技能之一。相关的类库也很多也很好用。以前只负责用,对其并没有深入学习和了解。前段时间有点空就在这块挖了挖。想做到知其然还要知其

前言

modbus通讯协议在工控行业的应用是很多的,并且也是上位机开发的基本技能之一。相关的类库也很多也很好用。以前只负责用,对其并没有深入学习和了解。前段时间有点空就在这块挖了挖。想做到知其然还要知其所以然。所以就有了自己封装的modbus工具类库的想法。一来是练练手,二来是自己封装的用的更顺手。

modbus通讯协议我在工作中目前只用到了两种一个是串口通讯modbusrtu,还有一个是网络通讯modbustcp。所以本文只有这两种通讯的实现。

设计思想

c#是高级语言有很多好用的东西,如面像对像,设计模式等。但我在工作中还是经常看到面像过程的编程。如有多个串口设备就有多个代码类似的工具类。代码重复非常严重。我认为这种事还是要发点时间总结和代码一下代码,把它封装工具类库。以便后继在其他上的使用。

本次的封装用了一点面像对像的方法,设计了一个多个modbus 基类将一些公共方法放在基类中,子类就可以继续使用。不同的子类有不同的功能。可以按需调用。使用简单方便。

调用示例        

var _serialport = new modbusrtucoil(portname, baudrate, parity, databits, stopbits);
var isok = false;
var resultmodel = _serialport.readdatacoil(1, 1, modbusfunctioncode.readinputcoil, (ushort)type.gethashcode());
if (resultmodel.resultlist != null && resultmodel.resultlist.count > 0)
{
    isok = resultmodel.resultlist.firstordefault();
}

类库项目结构

代码

modbus结果实体

using system;
using system.collections.generic;
using system.linq;
using system.text;
using system.threading.tasks;
 
namespace cjh.modbustool
{
    /// <summary>
    /// modbus结果实体
    /// </summary>
    /// <typeparam name="datetype"></typeparam>
    public class modbusresultmodel
    {
        public modbusresultmodel()
        {
            issucceed = false;
            msg = "失败(默认)";
        }
 
        private bool _issucceed = false;
        /// <summary>
        /// 是否成功
        /// </summary>
        public bool issucceed
        {
            get
            {
                return _issucceed;
            }
            set
            {
                _issucceed = value;
                if (issucceed)
                {
                    msg = "成功";
                }
            }
        }
 
        /// <summary>
        /// 返回消息
        /// </summary>
        public string msg { get; set; }
 
        /// <summary>
        /// 发送报文
        /// </summary>
        public string senddatastr { get; set; }
 
        /// <summary>
        /// 原始数据
        /// </summary>
        public byte[] datas { get; set; }
    }
 
    /// <summary>
    /// modbus结果实体
    /// </summary>
    /// <typeparam name="datetype"></typeparam>
    public class modbusresultmodel<datetype> : modbusresultmodel
    {
        public modbusresultmodel() : base()
        {
            resultlist = new list<datetype>();
        }
 
        /// <summary>
        /// 解析后的数据
        /// </summary>
        public list<datetype> resultlist { get; set; }
    }
}

modbus 基类

using system;
using system.collections;
using system.collections.generic;
using system.componentmodel;
using system.linq;
using system.text;
using system.threading.tasks;
 
namespace cjh.modbustool
{
    /// <summary>
    /// modbus 基类
    /// </summary>
    public abstract class modbusbase
    {
        /// <summary>
        /// 生成读取报文的 公共方法
        /// </summary>
        /// <param name="devaddr">从站地址</param>
        /// <param name="length">寄存器数量</param>
        /// <param name="functioncode">功能码</param>
        /// <param name="startaddr">起始寄存器地址</param>
        /// <returns>返回报文(协议格式:站地址+功能码+起始寄存器地址+寄存器数量)</returns>
        protected byte[] generatereadcommandbytes(byte devaddr, ushort length, modbusfunctioncode functioncode = modbusfunctioncode.readregister, ushort startaddr = 0)
        {
            //1.拼接报文:
            var sendcommand = new list<byte>();
 
            //协议格式:站地址+功能码+起始寄存器地址+寄存器数量
            //站地址
            sendcommand.add(devaddr);
            //功能码
            sendcommand.add((byte)functioncode.gethashcode());
            //起始寄存器地址
            sendcommand.add((byte)(startaddr / 256));
            sendcommand.add((byte)(startaddr % 256));
            //寄存器数量
            sendcommand.add((byte)(length / 256));
            sendcommand.add((byte)(length % 256));
            //crc
            //byte[] crc = crc16(sendcommand.toarray(), sendcommand.count);
            //sendcommand.addrange(crc);
 
            return sendcommand.toarray();
        }
 
        /// <summary>
        /// 生成读取报文的 公共方法
        /// </summary>
        /// <param name="devaddr">从站地址</param>
        /// <param name="data">定入数据</param>
        /// <param name="functioncode">功能码</param>
        /// <param name="startaddr">写入地址</param>
        /// <returns>返回报文(协议格式:站地址+功能码+起始寄存器地址+寄存器数量)</returns>
        protected byte[] generatewritecommandbytes(byte devaddr, ushort data, modbusfunctioncode functioncode = modbusfunctioncode.readregister, ushort startaddr = 0)
        {
            //1.拼接报文:
            var sendcommand = new list<byte>();
 
            //协议格式:站地址+功能码+起始寄存器地址+寄存器数量
            //站地址
            sendcommand.add(devaddr);
            //功能码
            sendcommand.add((byte)functioncode.gethashcode());
            //写入地址
            sendcommand.add((byte)(startaddr / 256));
            sendcommand.add((byte)(startaddr % 256));
            //写入数据
            var temp_bytes = bitconverter.getbytes(data);
            if (bitconverter.islittleendian)
            {
                //temp_bytes.reverse();
                array.reverse(temp_bytes);
            }
            sendcommand.addrange(temp_bytes);
            //crc
            //byte[] crc = crc16(sendcommand.toarray(), sendcommand.count);
            //sendcommand.addrange(crc);
 
            return sendcommand.toarray();
        }
 
        /// <summary>
        /// 生成发送命令报文
        /// </summary>
        /// <param name="sendcommand"></param>
        /// <returns></returns>
        protected string generatesendcommandstr(byte[] sendcommand)
        {
            var sendcommandstr = string.empty;
            foreach (var item in sendcommand)
            {
                sendcommandstr += convert.tostring(item, 16) + " ";
            }
            return sendcommandstr;
        }
 
        /// <summary>
        /// 验证crc
        /// </summary>
        /// <param name="value">要验证的数据</param>
        /// <returns></returns>
        protected bool checkcrc(byte[] value)
        {
            var isok = false;
            if (value != null && value.length >= 2)
            {
                int length = value.length;
                byte[] buf = new byte[length - 2];
                array.copy(value, 0, buf, 0, buf.length);
 
                //自己验证的结果
                byte[] crcbuf = crc16(buf, buf.length);
                //把上面验证的结果和串口返回的校验码(最后两个)进行比较
                if (crcbuf[0] == value[length - 2] && crcbuf[1] == value[length - 1])
                {
                    isok = true;
                }
            }
            return isok;
        }
 
        protected byte[] crc16(byte[] pucframe, int uslen)
        {
            int i = 0;
            byte[] res = new byte[2] { 0xff, 0xff };
            ushort iindex;
            while (uslen-- > 0)
            {
                iindex = (ushort)(res[0] ^ pucframe[i++]);
                res[0] = (byte)(res[1] ^ auccrchi[iindex]);
                res[1] = auccrclo[iindex];
            }
            return res;
        }
 
        protected 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
         };
 
        protected 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
         };
 
        /// <summary>
        /// crc校验
        /// </summary>
        /// <param name="pucframe">字节数组</param>
        /// <param name="uslen">验证长度</param>
        /// <returns>2个字节</returns>
        protected byte[] calculatecrc(byte[] pucframe, int uslen)
        {
            int i = 0;
            byte[] res = new byte[2] { 0xff, 0xff };
            ushort iindex;
            while (uslen-- > 0)
            {
                iindex = (ushort)(res[0] ^ pucframe[i++]);
                res[0] = (byte)(res[1] ^ auccrchi[iindex]);
                res[1] = auccrclo[iindex];
            }
            return res;
        }
    }
 
    /// <summary>
    /// modbus 功能码
    /// </summary>
    public enum modbusfunctioncode
    {
        /// <summary>
        /// 读取输出线圈
        /// </summary>
        [description("读取输出线圈")]
        readoutcoil = 1,
 
        /// <summary>
        /// 读取输入线圈
        /// </summary>
        [description("读取输入线圈")]
        readinputcoil = 2,
 
        /// <summary>
        /// 读取保持寄存器
        /// </summary>
        [description("读取保持寄存器")]
        readregister = 3,
 
        /// <summary>
        /// 读取输入寄存器
        /// </summary>
        [description("读取输入寄存器")]
        readinputregister = 4,
 
        /// <summary>
        /// (写入)预置单线圈
        /// </summary>
        [description("(写入)预置单线圈")]
        writecoil = 5,
 
        /// <summary>
        /// (写入)预置单个寄存器
        /// </summary>
        [description("(写入)预置单个寄存器")]
        writeregister = 6,
 
        /// <summary>
        /// (写入)预置多寄存器
        /// </summary>
        [description("(写入)预置多寄存器")]
        writeregistermultiple = 16,
    }
}

rtu

串口基类 serialportbase

using system;
using system.collections.generic;
using system.componentmodel;
using system.io.ports;
using system.linq;
using system.text;
using system.threading.tasks;
 
namespace cjh.modbustool.rtu
{
    //modbus 规定4个存储区
    // 区号     名称     读写        范围
    // 0区     输出线圈  可读可写    00001-09999
    // 1区     输入线圈  只读        10001-19999
    // 2区     输入寄存器  只读      30001-39999
    // 4区     保存寄存器  可读可写  40001-19999
 
    //功能码
    //01h     读取输出线圈
    //02h     读取输入线圈
    //03h     读取保持寄存器
    //04h     读取输入寄存器
    //05h     (写入)预置单线圈
    //06h     (写入)预置寄存器
    //0fh     (写入)预置多线圈
    //10h     (写入)预置多寄存器
 
    /// <summary>
    /// 串口基类
    /// </summary>
    public abstract class serialportbase : modbusbase
    {
        protected serialport serialportobj;
 
        /// <summary>
        /// 初始化
        /// </summary>
        /// <param name="portname">com口名称</param>
        /// <param name="baudrate">波特率</param>
        /// <param name="parity">检验位</param>
        /// <param name="databits">数据位</param>
        /// <param name="stopbits">停止位</param>
        protected void init(string portname, int baudrate = 9600, parity parity = parity.none, int databits = 8, stopbits stopbits = stopbits.one)
        {
            serialportobj = new serialport(portname, baudrate, parity, databits, stopbits);
            if (serialportobj.isopen)
            {
                serialportobj.close();
            }
            serialportobj.open();
        }        
 
        /// <summary>
        /// 关闭
        /// </summary>
        public void close()
        {
            if (serialportobj.isopen)
            {
                serialportobj.close();
                serialportobj.dispose();
                serialportobj = null;
            }
        }
    }
 
    //功能码
    //01h     读取输出线圈
    //02h     读取输入线圈
    //03h     读取保持寄存器
    //04h     读取输入寄存器
    //05h     (写入)预置单线圈
    //06h     (写入)预置寄存器
    //0fh     (写入)预置多线圈
    //10h     (写入)预置多寄存器
 
    
}

modbus 串口通讯

(串口操作的所有功能这个类都能做)

using system;
using system.collections.generic;
using system.componentmodel.design;
using system.io.ports;
using system.linq;
using system.text;
using system.threading.tasks;
 
namespace cjh.modbustool.rtu
{
    /// <summary>
    /// modbus 串口通讯
    /// </summary>
    public class modbusrtu : serialportbase
    {
        private string _classname = "modbusrtu";
 
        /// <summary>
        /// modbus 串口通讯
        /// </summary>
        /// <param name="portname">com口名称</param>
        /// <param name="baudrate">波特率</param>
        /// <param name="parity">检验位</param>
        /// <param name="databits">数据位</param>
        /// <param name="stopbits">停止位</param>
        public modbusrtu(string portname, int baudrate = 9600, parity parity = parity.none, int databits = 8, stopbits stopbits = stopbits.one)
        {
            init(portname, baudrate, parity, databits, stopbits);
            //serialportobj.datareceived += new serialdatareceivedeventhandler(comdatareceived);
        }
 
        /// <summary>
        /// 读取线圈数据 ok
        /// </summary>
        /// <param name="devaddr">从站地址</param>
        /// <param name="length">寄存器数量</param>
        /// <param name="functioncode">功能码</param>
        /// <param name="startaddr">起始寄存器地址</param>
        /// <returns>线圈数据</returns>
        public modbusresultmodel readdata(byte devaddr, ushort length, modbusfunctioncode functioncode = modbusfunctioncode.readregister, ushort startaddr = 0)
        {
            return readdata(devaddr, length, (byte)functioncode, startaddr);
        }
 
        /// <summary>
        /// 读取数据 ok
        /// </summary>
        /// <param name="devaddr">从站地址</param>
        /// <param name="length">寄存器数量</param>
        /// <param name="functioncode">功能码</param>
        /// <param name="startaddr">起始寄存器地址</param>
        /// <returns>线圈数据</returns>
        public modbusresultmodel readdata(byte devaddr, ushort length, byte functioncode = 2, ushort startaddr = 0)
        {
            var resultmodel = new modbusresultmodel();
            //byte[] datas = null;
            if (functioncode >= 1 && functioncode <= 4)
            {
                try
                {
                    //1.拼接报文:
                    var sendcommand = new list<byte>();
 
                    //协议格式:站地址+功能码+起始寄存器地址+寄存器数量+crc
                    //站地址
                    sendcommand.add(devaddr);
                    //功能码
                    sendcommand.add(functioncode);
                    //起始寄存器地址
                    sendcommand.add((byte)(startaddr / 256));
                    sendcommand.add((byte)(startaddr % 256));
                    //寄存器数量
                    sendcommand.add((byte)(length / 256));
                    sendcommand.add((byte)(length % 256));
                    //crc
                    byte[] crc = crc16(sendcommand.toarray(), sendcommand.count);
                    sendcommand.addrange(crc);
 
                    resultmodel.senddatastr = generatesendcommandstr(sendcommand.toarray());
                    //2.发送报文
                    serialportobj.write(sendcommand.toarray(), 0, sendcommand.count);
                    //3.接收报文
                    thread.sleep(50);//要延时一下,才能读到数据
 
                    //读取响应报文
                    byte[] respbytes = new byte[serialportobj.bytestoread];
                    serialportobj.read(respbytes, 0, respbytes.length);
                    // respbytes -> 01 01 02 00 00 b9 fc
                    resultmodel.datas = respbytes;
                    // 检查一个校验位
                    //if (checkcrc(respbytes) && (respbytes.length == 5 + length * 2) 
                    //    && respbytes[0] == devadd && respbytes[1] == functioncode && respbytes[1] == length * 2)
                    if (checkcrc(respbytes) && respbytes[0] == devaddr && respbytes[1] == functioncode)
                    {
                        //datas = respbytes;
                        resultmodel.issucceed = true;
                    }
                    else
                    {
                        resultmodel.msg = "响应报文校验失败";
                    }
                }
                catch (exception ex)
                {
                    resultmodel.msg = "异常:" + ex.message;
                }
            }
            else
            {
                //throw new exception("功能码不正确[1-4]");
                resultmodel.msg = "功能码不正确[1-4]";
            }
            //serialportobj.close();
            return resultmodel;
        }
 
        /// <summary>
        /// 写入单个寄存器 ok
        /// 数据示例: 200
        /// 功能码 6
        /// </summary>
        /// <param name="devaddr">从站地址</param>
        /// <param name="value">写入的数据</param>
        /// <param name="startaddr">写入地址</param>
        /// <returns>是否成功</returns>
        public modbusresultmodel writedatashort(int devaddr, short value, short startaddr = 0)
        {
            var resultmodel = new modbusresultmodel();
            try
            {
                //bool isok = false;
                //1.拼接报文:
                //var sendcommand = getsingledatawritemessage(devadd, startaddr, value); //ok
                var sendcommand = getsingledatawritemessagelist(devaddr, startaddr, value); //ok
                                                                                           //var sendcommandstr = string.join(' ', sendcommand.toarray());
                resultmodel.senddatastr = generatesendcommandstr(sendcommand);
 
                //2.发送报文
                serialportobj.write(sendcommand.toarray(), 0, sendcommand.length);
                //3.接收报文
                thread.sleep(50);//要延时一下,才能读到数据
 
                //读取响应报文
                byte[] respbytes = new byte[serialportobj.bytestoread];
                serialportobj.read(respbytes, 0, respbytes.length);
                // respbytes -> 01 01 02 00 00 b9 fc
                // 检查一个校验位
                if (checkcrc(respbytes) && respbytes[0] == devaddr && respbytes[1] == 0x06)
                {
                    //isok = true;
                    resultmodel.issucceed = true;
                }
                else
                {
                    resultmodel.msg = "响应报文校验失败";
                }
            }
            catch (exception ex)
            {
                resultmodel.msg = "异常:" + ex.message;
            }
            //serialportobj.close();
            return resultmodel;
        }
 
        /// <summary>
        /// 写入单个寄存器 ok
        /// 数据示例: 200
        /// 功能码 6
        /// </summary>
        /// <param name="devaddr">站地址</param>
        /// <param name="datalist">写入的数据集合</param>
        /// <param name="startaddr">写入地址</param>
        /// <returns>是否成功</returns>
        public modbusresultmodel writedatashort(int devaddr, list<short> datalist, short startaddr = 0)
        {
            var resultmodel = new modbusresultmodel();
            if (datalist != null && datalist.count > 0)
            {
                foreach (var item in datalist)
                {
                    resultmodel = writedatashort(devaddr, item, startaddr);
                    startaddr++;
                }
            }
            return resultmodel;
        }
 
        /// <summary>
        /// 获取写入单个寄存器的报文
        /// </summary>
        /// <param name="slavestation">从站地址</param>
        /// <param name="startaddr">寄存器地址</param>
        /// <param name="value">写入值</param>
        /// <returns>写入单个寄存器的报文</returns>
        private byte[] getsingledatawritemessage(int slavestation, short startaddr, short value)
        {
            //从站地址
            byte station = (byte)slavestation;
            //功能码
            byte type = 0x06;//06h     (写入)预置寄存器
            //寄存器地址
            byte[] start = bitconverter.getbytes(startaddr);
            //值
            byte[] valuebytes = bitconverter.getbytes(value);
            //根据计算机大小端存储方式进行高低字节转换
            if (bitconverter.islittleendian)
            {
                array.reverse(start);
                array.reverse(valuebytes);
            }
            //拼接报文
            byte[] result = new byte[] { station, type };
            result = result.concat(start.concat(valuebytes).toarray()).toarray();
 
            //计算校验码并拼接,返回最后的报文结果
            return result.concat(crc16(result, result.length)).toarray();
        }
 
        /// <summary>
        /// 获取写入单个寄存器的报文
        /// </summary>
        /// <param name="slavestation">从站地址</param>
        /// <param name="startaddr">寄存器地址</param>
        /// <param name="data">写入值</param>
        /// <returns>写入单个寄存器的报文</returns>
        private byte[] getsingledatawritemessagelist(int slavestation, short startaddr, short data)
        {
            //1.拼接报文:
            var sendcommand = new list<byte>();
            //从站地址
            byte station = (byte)slavestation;
            //功能码
            byte type = 0x06;//06h     (写入)预置寄存器            
            //寄存器地址
            byte[] start = bitconverter.getbytes(startaddr);
            //值
            byte[] valuebytes = bitconverter.getbytes(data);
            //根据计算机大小端存储方式进行高低字节转换
            if (bitconverter.islittleendian)
            {
                array.reverse(start);
                array.reverse(valuebytes);
            }
            sendcommand.add((byte)slavestation);
            sendcommand.add(type);
            sendcommand.addrange(start);
            sendcommand.addrange(valuebytes);
 
            byte[] crc = crc16(sendcommand.toarray(), sendcommand.count);
            sendcommand.addrange(crc);
            return sendcommand.toarray();
        }
 
        /// <summary>
        /// 写入多个寄存器 ok
        /// 数据示例: 123.45f, 14.3f
        /// 功能码 10 
        /// </summary>
        /// <param name="devaddr">从站地址</param>
        /// <param name="data">写入的数据</param>
        /// <param name="startaddr">写入地址</param>
        /// <returns>是否成功</returns>
        public modbusresultmodel writedatafloat(byte devaddr, float data, ushort startaddr = 0)
        {
            return writedatafloat(devaddr, new list<float>() { data }, startaddr);
        }
 
        /// <summary>
        /// 写入多个寄存器 ok
        /// 数据示例: 123.45f, 14.3f
        /// 功能码 10 
        /// </summary>
        /// <param name="devadd">从站地址</param>
        /// <param name="datalist">写入的数据</param>
        /// <param name="startaddr">写入地址</param>
        /// <returns>是否成功</returns>
        public modbusresultmodel writedatafloat(byte devaddr, list<float> datalist, ushort startaddr = 0)
        {
            var resultmodel = new modbusresultmodel();
            if (datalist != null && datalist.count > 0)
            {
                try
                {
                    byte functioncode = (byte)modbusfunctioncode.writeregistermultiple.gethashcode();
                    int length = datalist.count * 2;
                    //1.拼接报文:
                    var sendcommand = new list<byte>();
 
                    //协议格式:站地址+功能码+起始寄存器地址+寄存器数量+crc
                    //站地址
                    sendcommand.add(devaddr);
                    //功能码
                    sendcommand.add(functioncode);
                    //写入地址
                    sendcommand.add((byte)(startaddr / 256));
                    sendcommand.add((byte)(startaddr % 256));
                    //寄存器数量
                    sendcommand.add((byte)(length / 256));
                    sendcommand.add((byte)(length % 256));
                    // 获取数值的byte[]
                    list<byte> valuebytes = new list<byte>();
                    foreach (var data in datalist)
                    {
                        list<byte> temp = new list<byte>(bitconverter.getbytes(data));
                        temp.reverse();// 调整字节序
                        valuebytes.addrange(temp);
                    }
                    // 字节数
                    sendcommand.add((byte)valuebytes.count);
                    sendcommand.addrange(valuebytes);
                    //crc
                    byte[] crc = crc16(sendcommand.toarray(), sendcommand.count);
                    sendcommand.addrange(crc);
 
                    //000004 - rx:01 10 00 02 00 04 08 42 f6 e6 66 41 64 cc cd 83 23
                    //000005 - tx:01 10 00 02 00 04 60 0a
                    //000006 - rx:01 0a 00 02 00 04 08 42 f6 e6 66 41 64 cc cd 98 f9
                    //000007 - tx:01 8a 01 86 a0 //报错了
 
                    //2.发送报文
                    serialportobj.write(sendcommand.toarray(), 0, sendcommand.count);
                    //3.接收报文
                    thread.sleep(50);//要延时一下,才能读到数据
 
                    //读取响应报文
                    byte[] respbytes = new byte[serialportobj.bytestoread];
                    serialportobj.read(respbytes, 0, respbytes.length);
                    // respbytes -> 01 01 02 00 00 b9 fc
 
                    // 检查一个校验位
                    if (checkcrc(respbytes) && respbytes[0] == devaddr && respbytes[1] == functioncode)
                    {
                        resultmodel.issucceed = true;
                    }
                    else
                    {
                        resultmodel.msg = "响应报文校验失败";
                    }
                }
                catch (exception ex)
                {
                    resultmodel.msg = "异常:" + ex.message;
                }
                //serialportobj.close();
            }
            else
            {
                resultmodel.msg = "datalis参数不能为null 且 count 要大于0";
            }
            return resultmodel;
        }
 
        /// <summary>
        /// 写单个线圈输出 ok
        /// </summary>
        /// <param name="on">开关</param>
        /// <param name="devaddr">从站地址</param>
        /// <param name="startaddr">写入地址</param>
        /// <returns></returns>
        public modbusresultmodel writesingleoutonoff(bool on, byte devaddr = 1, ushort startaddr = 0)
        {
            var resultmodel = new modbusresultmodel();
            try
            {
                //var isok = false;
                //1.拼接报文:
                var sendcommand = new list<byte>();
                //协议格式:站地址+功能码+起始寄存器地址+寄存器数量+crc
                //站地址
                sendcommand.add(devaddr);
                //功能码
                byte functioncode = 0x05;
                sendcommand.add(functioncode);
                //写入地址
                sendcommand.add((byte)(startaddr / 256));
                sendcommand.add((byte)(startaddr % 256));
                //写入数据
                sendcommand.add((byte)(on ? 0xff : 0x00));//true : 0xff 开,false : 0x00 关
                sendcommand.add(0x00);
                //crc
                byte[] crc = crc16(sendcommand.toarray(), sendcommand.count);
                sendcommand.addrange(crc);
                //2.发送报文
                serialportobj.write(sendcommand.toarray(), 0, sendcommand.count);
                //isok = true;
                resultmodel.issucceed = true;
            }
            catch (exception ex)
            {
                resultmodel.msg = "异常:" + ex.message;
            }
            return resultmodel;
        }
    }
}

modbus 串口通讯 读线圈状态

(这个类是针对线圈的 突出读取数据)

using system;
using system.collections.generic;
using system.io.ports;
using system.linq;
using system.text;
using system.threading.tasks;
 
namespace cjh.modbustool.rtu
{
    /// <summary>
    /// modbus 串口通讯 读线圈状态
    /// </summary>
    public class modbusrtucoil : modbusrtu
    {
        //modbusrtu rtu = new modbusrtu(portname);
        //var resultmodel = rtu.readdata(1, readlen, modbusfunctioncode.readoutcoil);
 
        /// <summary>
        /// modbus 串口通讯
        /// </summary>
        /// <param name="portname">com口名称</param>
        /// <param name="baudrate">波特率</param>
        /// <param name="parity">检验位</param>
        /// <param name="databits">数据位</param>
        /// <param name="stopbits">停止位</param>
        public modbusrtucoil(string portname, int baudrate = 9600, parity parity = parity.none, int databits = 8, stopbits stopbits = stopbits.one)
            : base(portname, baudrate, parity, databits, stopbits)
        {
            //init(portname, baudrate, parity, databits, stopbits);
            //serialportobj.datareceived += new serialdatareceivedeventhandler(comdatareceived);
        }
 
        /// <summary>
        /// 读取线圈数据 ok
        /// </summary>
        /// <param name="devadd">从站地址</param>
        /// <param name="length">寄存器数量</param>
        /// <param name="functioncode">功能码</param>
        /// <param name="startaddr">起始寄存器地址</param>
        /// <returns>线圈数据</returns>
        public modbusresultmodel<bool> readdatacoil(byte devadd, ushort length, modbusfunctioncode functioncode = modbusfunctioncode.readregister, ushort startaddr = 0)
        {
            var resultmodel = new modbusresultmodel<bool>();
            var model = readdata(devadd, length, (byte)functioncode, startaddr);
            if (model != null && model.datas != null && model.datas.length > 5)
            {
                resultmodel.issucceed = model.issucceed;
                //报文解析
                // 检查一个校验位
                list<byte> resplist = new list<byte>(model.datas);
                resplist.removerange(0, 3);
                resplist.removerange(resplist.count - 2, 2);
                // 00 00
                //集合反转
                resplist.reverse();
                //转换成2进制
                var respstrlist = resplist.select(r => convert.tostring(r, 2)).tolist();
                var values = string.join("", respstrlist).tolist();
                values.reverse();
                //values.foreach(c => console.writeline(convert.toboolean(int.parse(c.tostring()))));
                foreach (var v in values)
                {
                    resultmodel.resultlist.add(v.tostring() == "1");
                }
            }
            return resultmodel;
        }
    }
}

tcp

modbustcp 基类

using system;
using system.collections.generic;
using system.linq;
using system.net.sockets;
using system.runtime.interopservices;
using system.security.cryptography;
using system.text;
using system.threading.tasks;
 
namespace cjh.modbustool.tcp
{
    /// <summary>
    /// modbustcp 基类
    /// </summary>
    public abstract class modbustcpbase : modbusbase
    {
        private socket _socket = null;
        ushort _tid = 0;//transactionid 最大 65535
 
        /// <summary>
        /// 异常码 字典
        /// </summary>
        protected dictionary<int, string> errors = new dictionary<int, string>() {
            { 0x01 , "非法功能码"},
            { 0x02 , "非法数据地址"},
            { 0x03 , "非法数据值"},
            { 0x04 , "从站设备故障"},
            { 0x05 , "确认,从站需要一个耗时操作"},
            { 0x06 , "从站忙"},
            { 0x08 , "存储奇偶性差错"},
            { 0x0a , "不可用网关路径"},
            { 0x0b , "网关目标设备响应失败"},
        };
 
        /// <summary>
        /// modbus tcp 通讯 初始化
        /// </summary>
        /// <param name="host">主机地址</param>
        /// <param name="port">端口</param>
        protected void init(string host, int port)
        {
            _socket = new socket(addressfamily.internetwork, sockettype.stream, protocoltype.tcp);
            _socket.connect(host, port);
        }
 
        /// <summary>
        /// 读取报文的 公共方法
        /// </summary>
        /// <param name="devaddr">从站地址</param>
        /// <param name="length">寄存器数量</param>
        /// <param name="functioncode">功能码</param>
        /// <param name="startaddr">起始寄存器地址</param>
        /// <returns>返回报文(协议格式:transactionid+协议标识+后续字节数+站地址+功能码+起始寄存器地址+寄存器数量)</returns>
        protected byte[] generatetcpcommandreadbytes(byte devaddr, ushort length, modbusfunctioncode functioncode = modbusfunctioncode.readregister, ushort startaddr = 0)
        {
            var basecommand = generatereadcommandbytes(devaddr, length, functioncode, startaddr);
 
            var sendcommand = new list<byte>();
            //transactionid
            sendcommand.add((byte)(_tid / 256));
            sendcommand.add((byte)(_tid % 256));
            //modbus 协议标识
            sendcommand.add(0x00);
            sendcommand.add(0x00);
            //后续字节数
            sendcommand.add((byte)(basecommand.length / 256));
            sendcommand.add((byte)(basecommand.length % 256));
            _tid++;
            _tid %= 65535;
            sendcommand.addrange(basecommand);
            return sendcommand.toarray();
        }
 
        /// <summary>
        /// 读取报文的 公共方法
        /// </summary>
        /// <param name="devaddr">从站地址</param>
        /// <param name="data">输入数据</param>
        /// <param name="functioncode">功能码</param>
        /// <param name="startaddr">起始寄存器地址</param>
        /// <returns>返回报文(协议格式:transactionid+协议标识+后续字节数+站地址+功能码+起始寄存器地址+寄存器数量)</returns>
        protected byte[] generatetcpcommandwritebytes(byte devaddr, ushort data, modbusfunctioncode functioncode = modbusfunctioncode.writeregister, ushort startaddr = 0)
        {
            var basecommand = generatewritecommandbytes(devaddr, data, functioncode, startaddr);
 
            var sendcommand = new list<byte>();
            //transactionid
            sendcommand.add((byte)(_tid / 256));
            sendcommand.add((byte)(_tid % 256));
            //modbus 协议标识
            sendcommand.add(0x00);
            sendcommand.add(0x00);
            //后续字节数
            sendcommand.add((byte)(basecommand.length / 256));
            sendcommand.add((byte)(basecommand.length % 256));
            _tid++;
            _tid %= 65535;
            sendcommand.addrange(basecommand);
            return sendcommand.toarray();
        }
 
        protected modbusresultmodel sendcommand(byte[] sendcommand)
        {
            var resultmodel = new modbusresultmodel();
            try
            {
                //报文
                //transactionid modbus 协议标识 后续字节数  从站地址  功能码         起始寄存器地址  寄存器数量  crc
                //0x00 0x01     0x00   0x00     0x00  0x06  devaddr   functioncode   0x00  0x0a      
 
                resultmodel.senddatastr = generatesendcommandstr(sendcommand.toarray());
                _socket.send(sendcommand.toarray());
                //000002-rx:00 00 00 00 00 06 01 03 00 01 00 05
                //000003-tx:00 00 00 00 00 0d 01 03 0a 00 00 00 00 00 00 00 00 00 00
                // 00 00 00 00 00 0d 前6位
                // 01 03 0a (0,1,2)
                // 0a 数据长度 (0a=10)
 
                //先取前6位,固定返回
                var resp_bytes = new byte[6];// 00 00 00 00 00 0d 前6位
                _socket.receive(resp_bytes, 0, resp_bytes.length, socketflags.none);
 
                //取出下标为 :4和5的数据[00 0d]
                var len_bytes = resp_bytes.tolist().getrange(4, 2);
                //起始寄存器地址 的反向操作
                //将下标为4和5 两个字节转成10进制数
                int len = len_bytes[0] * 256 + len_bytes[1];
 
                //获取数据的长度
                //01 03 0a 00 00 00 00 00 00 00 00 00 00 [正常]
                //01 83 02 [异常,83, 异常代码 :02]
                resp_bytes = new byte[len];
                _socket.receive(resp_bytes, 0, len, socketflags.none);
 
                //检查响应报文是否正常
                //0x83 1000 0011
                //01 83 02 [异常,83, 异常代码 :02]
                if (resp_bytes[1] > 0x08)//判断是否异常
                {
                    //resp_bytes[2] = 异常代码 :02
                    //说明响应是异常报文
                    //返回异常信息,根据resp_bytes字节进行异常关联
                    if (errors.containskey(resp_bytes[2]))
                    {
                        resultmodel.msg = errors[resp_bytes[2]];//获取异常码对应的异常说明
                    }
                }
                else
                {
                    //resp_bytes[2] = 0a 数据长度 (0a=10)
                    //正常
                    resultmodel.datas = resp_bytes.tolist().getrange(3, resp_bytes[2]).toarray();
                    resultmodel.issucceed = true;
                }
            }
            catch (exception ex)
            {
                resultmodel.msg = ex.message;
            }
            return resultmodel;
        }
 
        /// <summary>
        /// 解析数据
        /// </summary>
        /// <typeparam name="t"></typeparam>
        /// <param name="datas"></param>
        /// <returns></returns>
        public list<t> analysisdatas<t>(byte[] datas)
        {
            //data_bytes 每两个字节转成一个数字, float 4个字节转成一个数字,double 8个字节转成一个数字
            //2  ushort short int16 uint32 float
            //4  int uint int32 uint32 float
            //8  double
            //16 decimal
            var resultvalue = new list<t>();
            try
            {
                var type_len = marshal.sizeof(typeof(t));
                for (int i = 0; i < datas.length; i += type_len)
                {
                    var temp_bytes = datas.tolist().getrange(i, type_len);
                    if (bitconverter.islittleendian)
                    {
                        temp_bytes.reverse();
                    }
                    //反射 方法
                    type bitconverter_type = typeof(bitconverter);
                    var typemethodlist = bitconverter_type.getmethods().tolist();
                    //找到返回类型和传入的类型一至,且方法的参数是2个的方法
                    var method = typemethodlist.firstordefault(mi => mi.returntype == typeof(t)
                                        && mi.getparameters().length == 2);
                    if (method == null)
                    {
                        throw new exception("数据转换类型出错!");
                    }
                    else
                    {
                        //由 bitconverter_type 执行找到的 method方法,注意参数数量,上面找是的两个参数的方法
                        var value = method.invoke(bitconverter_type, new object[] { temp_bytes.toarray(), 0 });
                        resultvalue.add((t)value);
                    }
                }
            }
            catch (exception ex)
            {
 
            }
            return resultvalue;
        }
    }
}

modbus tcp 通讯

using system;
using system.collections.generic;
using system.linq;
using system.text;
using system.threading.tasks;
using system.net.sockets;
 
namespace cjh.modbustool.tcp
{
    /// <summary>
    /// modbus tcp 通讯
    /// </summary>
    public class modbustcp : modbustcpbase
    {
 
        /// <summary>
        /// modbus tcp 通讯
        /// </summary>
        /// <param name="host">主机地址</param>
        /// <param name="port">端口</param>
        public modbustcp(string host, int port)
        {
            //_socket = new socket(addressfamily.internetwork, sockettype.stream, protocoltype.tcp);
            //_socket.connect(host, port);
            init(host, port);
        }
 
        /// <summary>
        /// 读取保持型寄存器 03
        /// </summary>
        /// <param name="devaddr">从站地址</param>
        /// <param name="count">数量</param>
        /// <param name="functioncode">功能码</param>
        /// <param name="startaddr">起始地址</param>
        //public modbusresultmodel readholdingregister(byte devaddr, ushort length, byte functioncode = 3, ushort startaddr = 0)
        //{
        //    var resultmodel = new modbusresultmodel();
        //    //报文
        //    //transactionid modbus 协议标识 后续字节数  从站地址  功能码         起始寄存器地址  寄存器数量  crc
        //    //0x00 0x01     0x00   0x00     0x00  0x06  devaddr   functioncode   0x00  0x0a      
        //    try
        //    {
        //        ushort tid = 0;//transactionid 最大 65535
 
        //        var sendcommand = new list<byte>();
        //        //transactionid
        //        sendcommand.add((byte)(tid / 256));
        //        sendcommand.add((byte)(tid % 256));
        //        //modbus 协议标识
        //        sendcommand.add(0x00);
        //        sendcommand.add(0x00);
        //        //后续字节数
        //        sendcommand.add(0x00);
        //        sendcommand.add(0x06);
        //        //从站地址
        //        sendcommand.add(devaddr);
        //        //功能码
        //        sendcommand.add(functioncode);
        //        //起始寄存器地址
        //        sendcommand.add((byte)(startaddr / 256));
        //        sendcommand.add((byte)(startaddr % 256));
        //        //寄存器数量
        //        sendcommand.add((byte)(length / 256));
        //        sendcommand.add((byte)(length % 256));
        //        //crc
        //        //byte[] crc = crc16(sendcommand.toarray(), sendcommand.count);
        //        //sendcommand.addrange(crc);
        //        tid++;
        //        tid %= 65535;
        //        resultmodel.senddatastr = generatesendcommandstr(sendcommand.toarray());
        //        _socket.send(sendcommand.toarray());
        //        //000002-rx:00 00 00 00 00 06 01 03 00 01 00 05
        //        //000003-tx:00 00 00 00 00 0d 01 03 0a 00 00 00 00 00 00 00 00 00 00
        //        // 00 00 00 00 00 0d 前6位
        //        // 01 03 0a (0,1,2)
        //        // 0a 数据长度 (0a=10)
 
        //        //先取前6位,固定返回
        //        var resp_bytes = new byte[6];// 00 00 00 00 00 0d 前6位
        //        _socket.receive(resp_bytes, 0, resp_bytes.length, socketflags.none);
 
        //        //取出下标为 :4和5的数据[00 0d]
        //        var len_bytes = resp_bytes.tolist().getrange(4, 2);
        //        //起始寄存器地址 的反向操作
        //        //将下标为4和5 两个字节转成10进制数
        //        int len = resp_bytes[4] * 256 + resp_bytes[5];
 
        //        //获取数据的长度
        //        //01 03 0a 00 00 00 00 00 00 00 00 00 00 [正常]
        //        //01 83 02 [异常,83, 异常代码 :02]
        //        resp_bytes = new byte[len];
        //        _socket.receive(resp_bytes, 0, len, socketflags.none);
 
        //        //检查响应报文是否正常
        //        //0x83 1000 0011
        //        //01 83 02 [异常,83, 异常代码 :02]
        //        if (resp_bytes[1] > 0x08)//判断是否异常
        //        {
        //            //resp_bytes[2] = 异常代码 :02
        //            //说明响应是异常报文
        //            //返回异常信息,根据resp_bytes字节进行异常关联
        //            if (errors.containskey(resp_bytes[2]))
        //            {
        //                resultmodel.msg = errors[resp_bytes[2]];//获取异常码对应的异常说明
        //            }
        //        }
        //        else
        //        {
        //            //resp_bytes[2] = 0a 数据长度 (0a=10)
        //            //正常
        //            resultmodel.datas = resp_bytes.tolist().getrange(3, resp_bytes[2]).toarray();
        //            resultmodel.issucceed = true;
        //        }
        //    }
        //    catch (exception ex)
        //    {
        //        resultmodel.msg = ex.message;
        //    }
        //    return resultmodel;
        //}
 
        /// <summary>
        /// 读取保持型寄存器 03
        /// </summary>
        /// <param name="devaddr">从站地址</param>
        /// <param name="length">数量</param>
        /// <param name="functioncode">功能码</param>
        /// <param name="startaddr">起始地址</param>
        /// <returns>返回对象</returns>
        public modbusresultmodel<t> readholdingregister<t>(byte devaddr, ushort length, modbusfunctioncode functioncode = modbusfunctioncode.readregister, ushort startaddr = 0)
        {
            var resultmodel = new modbusresultmodel<t>();
            try
            {
                var command = generatetcpcommandreadbytes(devaddr, length, functioncode, startaddr);
 
                resultmodel.senddatastr = generatesendcommandstr(command.toarray());
                var receptionmodel = sendcommand(command.toarray());
                if (receptionmodel.issucceed
                    && receptionmodel.datas != null && receptionmodel.datas.length > 0)
                {
                    resultmodel.datas = receptionmodel.datas;
                    resultmodel.resultlist = analysisdatas<t>(receptionmodel.datas);
                    resultmodel.issucceed = true;
                }
            }
            catch (exception ex)
            {
                resultmodel.msg = ex.message;
            }
            return resultmodel;
        }
 
        /// <summary>
        /// 写入保持型寄存器 03
        /// </summary>
        /// <param name="devaddr">从站地址</param>
        /// <param name="datas">写入数据</param>
        /// <param name="functioncode">功能码</param>
        /// <param name="startaddr">起始地址</param>
        /// <returns>返回对象</returns>
        public modbusresultmodel writeholdingregister(byte devaddr, ushort data, modbusfunctioncode functioncode = modbusfunctioncode.writeregister, ushort startaddr = 0)
        {
            var resultmodel = new modbusresultmodel();
            try
            {
                var command = generatetcpcommandwritebytes(devaddr, data, functioncode, startaddr);
 
                resultmodel.senddatastr = generatesendcommandstr(command.toarray());
                var receptionmodel = sendcommand(command.toarray());
                if (receptionmodel.issucceed)
                {
                    resultmodel.issucceed = true;
                }
            }
            catch (exception ex)
            {
                resultmodel.msg = ex.message;
            }
            return resultmodel;
        }
    }
}

这就是全部的代码。

以上就是使用c#实现自己封装的modbus工具类库的详细内容,更多关于c# modbus的资料请关注代码网其它相关文章!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com