• 用c# 自己封装的Modbus工具类库源码


      前言

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

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

    设计思想

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

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

    调用示例

            

    1. var _serialPort = new ModbusRTUCoil(portName, baudRate, parity, dataBits, stopBits);
    2. var isOk = false;
    3. var resultModel = _serialPort.ReadDataCoil(1, 1, ModbusFunctionCode.ReadInputCoil, (ushort)type.GetHashCode());
    4. if (resultModel.ResultList != null && resultModel.ResultList.Count > 0)
    5. {
    6. isOk = resultModel.ResultList.FirstOrDefault();
    7. }

    类库项目结构

            

    代码

            Modbus结果实体

            

    1. using System;
    2. using System.Collections.Generic;
    3. using System.Linq;
    4. using System.Text;
    5. using System.Threading.Tasks;
    6. namespace CJH.ModbusTool
    7. {
    8. ///
    9. /// Modbus结果实体
    10. ///
    11. ///
    12. public class ModbusResultModel
    13. {
    14. public ModbusResultModel()
    15. {
    16. IsSucceed = false;
    17. Msg = "失败(默认)";
    18. }
    19. private bool _isSucceed = false;
    20. ///
    21. /// 是否成功
    22. ///
    23. public bool IsSucceed
    24. {
    25. get
    26. {
    27. return _isSucceed;
    28. }
    29. set
    30. {
    31. _isSucceed = value;
    32. if (IsSucceed)
    33. {
    34. Msg = "成功";
    35. }
    36. }
    37. }
    38. ///
    39. /// 返回消息
    40. ///
    41. public string Msg { get; set; }
    42. ///
    43. /// 发送报文
    44. ///
    45. public string SendDataStr { get; set; }
    46. ///
    47. /// 原始数据
    48. ///
    49. public byte[] Datas { get; set; }
    50. }
    51. ///
    52. /// Modbus结果实体
    53. ///
    54. ///
    55. public class ModbusResultModel<DateType> : ModbusResultModel
    56. {
    57. public ModbusResultModel() : base()
    58. {
    59. ResultList = new List();
    60. }
    61. ///
    62. /// 解析后的数据
    63. ///
    64. public List ResultList { get; set; }
    65. }
    66. }

    Modbus 基类

    1. using System;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using System.ComponentModel;
    5. using System.Linq;
    6. using System.Text;
    7. using System.Threading.Tasks;
    8. namespace CJH.ModbusTool
    9. {
    10. ///
    11. /// Modbus 基类
    12. ///
    13. public abstract class ModbusBase
    14. {
    15. ///
    16. /// 生成读取报文的 公共方法
    17. ///
    18. /// 从站地址
    19. /// 寄存器数量
    20. /// 功能码
    21. /// 起始寄存器地址
    22. /// 返回报文(协议格式:站地址+功能码+起始寄存器地址+寄存器数量)
    23. protected byte[] GenerateReadCommandBytes(byte devAddr, ushort length, ModbusFunctionCode functionCode = ModbusFunctionCode.ReadRegister, ushort startAddr = 0)
    24. {
    25. //1.拼接报文:
    26. var sendCommand = new List<byte>();
    27. //协议格式:站地址+功能码+起始寄存器地址+寄存器数量
    28. //站地址
    29. sendCommand.Add(devAddr);
    30. //功能码
    31. sendCommand.Add((byte)functionCode.GetHashCode());
    32. //起始寄存器地址
    33. sendCommand.Add((byte)(startAddr / 256));
    34. sendCommand.Add((byte)(startAddr % 256));
    35. //寄存器数量
    36. sendCommand.Add((byte)(length / 256));
    37. sendCommand.Add((byte)(length % 256));
    38. //CRC
    39. //byte[] crc = Crc16(sendCommand.ToArray(), sendCommand.Count);
    40. //sendCommand.AddRange(crc);
    41. return sendCommand.ToArray();
    42. }
    43. ///
    44. /// 生成读取报文的 公共方法
    45. ///
    46. /// 从站地址
    47. /// 定入数据
    48. /// 功能码
    49. /// 写入地址
    50. /// 返回报文(协议格式:站地址+功能码+起始寄存器地址+寄存器数量)
    51. protected byte[] GenerateWriteCommandBytes(byte devAddr, ushort data, ModbusFunctionCode functionCode = ModbusFunctionCode.ReadRegister, ushort startAddr = 0)
    52. {
    53. //1.拼接报文:
    54. var sendCommand = new List<byte>();
    55. //协议格式:站地址+功能码+起始寄存器地址+寄存器数量
    56. //站地址
    57. sendCommand.Add(devAddr);
    58. //功能码
    59. sendCommand.Add((byte)functionCode.GetHashCode());
    60. //写入地址
    61. sendCommand.Add((byte)(startAddr / 256));
    62. sendCommand.Add((byte)(startAddr % 256));
    63. //写入数据
    64. var temp_bytes = BitConverter.GetBytes(data);
    65. if (BitConverter.IsLittleEndian)
    66. {
    67. //temp_bytes.Reverse();
    68. Array.Reverse(temp_bytes);
    69. }
    70. sendCommand.AddRange(temp_bytes);
    71. //CRC
    72. //byte[] crc = Crc16(sendCommand.ToArray(), sendCommand.Count);
    73. //sendCommand.AddRange(crc);
    74. return sendCommand.ToArray();
    75. }
    76. ///
    77. /// 生成发送命令报文
    78. ///
    79. ///
    80. ///
    81. protected string generateSendCommandStr(byte[] sendCommand)
    82. {
    83. var sendCommandStr = string.Empty;
    84. foreach (var item in sendCommand)
    85. {
    86. sendCommandStr += Convert.ToString(item, 16) + " ";
    87. }
    88. return sendCommandStr;
    89. }
    90. ///
    91. /// 验证CRC
    92. ///
    93. /// 要验证的数据
    94. ///
    95. protected bool CheckCRC(byte[] value)
    96. {
    97. var isOk = false;
    98. if (value != null && value.Length >= 2)
    99. {
    100. int length = value.Length;
    101. byte[] buf = new byte[length - 2];
    102. Array.Copy(value, 0, buf, 0, buf.Length);
    103. //自己验证的结果
    104. byte[] CRCbuf = Crc16(buf, buf.Length);
    105. //把上面验证的结果和串口返回的校验码(最后两个)进行比较
    106. if (CRCbuf[0] == value[length - 2] && CRCbuf[1] == value[length - 1])
    107. {
    108. isOk = true;
    109. }
    110. }
    111. return isOk;
    112. }
    113. protected byte[] Crc16(byte[] pucFrame, int usLen)
    114. {
    115. int i = 0;
    116. byte[] res = new byte[2] { 0xFF, 0xFF };
    117. ushort iIndex;
    118. while (usLen-- > 0)
    119. {
    120. iIndex = (ushort)(res[0] ^ pucFrame[i++]);
    121. res[0] = (byte)(res[1] ^ aucCRCHi[iIndex]);
    122. res[1] = aucCRCLo[iIndex];
    123. }
    124. return res;
    125. }
    126. protected readonly byte[] aucCRCHi = {
    127. 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
    128. 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
    129. 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
    130. 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
    131. 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
    132. 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
    133. 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
    134. 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
    135. 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
    136. 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
    137. 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
    138. 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
    139. 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
    140. 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
    141. 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
    142. 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
    143. 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
    144. 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
    145. 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
    146. 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
    147. 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
    148. 0x00, 0xC1, 0x81, 0x40
    149. };
    150. protected readonly byte[] aucCRCLo = {
    151. 0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06, 0x07, 0xC7,
    152. 0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD, 0x0F, 0xCF, 0xCE, 0x0E,
    153. 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09, 0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9,
    154. 0x1B, 0xDB, 0xDA, 0x1A, 0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC,
    155. 0x14, 0xD4, 0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3,
    156. 0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3, 0xF2, 0x32,
    157. 0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4, 0x3C, 0xFC, 0xFD, 0x3D,
    158. 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A, 0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38,
    159. 0x28, 0xE8, 0xE9, 0x29, 0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF,
    160. 0x2D, 0xED, 0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26,
    161. 0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60, 0x61, 0xA1,
    162. 0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67, 0xA5, 0x65, 0x64, 0xA4,
    163. 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F, 0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB,
    164. 0x69, 0xA9, 0xA8, 0x68, 0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA,
    165. 0xBE, 0x7E, 0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5,
    166. 0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71, 0x70, 0xB0,
    167. 0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92, 0x96, 0x56, 0x57, 0x97,
    168. 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C, 0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E,
    169. 0x5A, 0x9A, 0x9B, 0x5B, 0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89,
    170. 0x4B, 0x8B, 0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,
    171. 0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42, 0x43, 0x83,
    172. 0x41, 0x81, 0x80, 0x40
    173. };
    174. ///
    175. /// CRC校验
    176. ///
    177. /// 字节数组
    178. /// 验证长度
    179. /// 2个字节
    180. protected byte[] CalculateCRC(byte[] pucFrame, int usLen)
    181. {
    182. int i = 0;
    183. byte[] res = new byte[2] { 0xFF, 0xFF };
    184. ushort iIndex;
    185. while (usLen-- > 0)
    186. {
    187. iIndex = (ushort)(res[0] ^ pucFrame[i++]);
    188. res[0] = (byte)(res[1] ^ aucCRCHi[iIndex]);
    189. res[1] = aucCRCLo[iIndex];
    190. }
    191. return res;
    192. }
    193. }
    194. ///
    195. /// Modbus 功能码
    196. ///
    197. public enum ModbusFunctionCode
    198. {
    199. ///
    200. /// 读取输出线圈
    201. ///
    202. [Description("读取输出线圈")]
    203. ReadOutCoil = 1,
    204. ///
    205. /// 读取输入线圈
    206. ///
    207. [Description("读取输入线圈")]
    208. ReadInputCoil = 2,
    209. ///
    210. /// 读取保持寄存器
    211. ///
    212. [Description("读取保持寄存器")]
    213. ReadRegister = 3,
    214. ///
    215. /// 读取输入寄存器
    216. ///
    217. [Description("读取输入寄存器")]
    218. ReadInputRegister = 4,
    219. ///
    220. /// (写入)预置单线圈
    221. ///
    222. [Description("(写入)预置单线圈")]
    223. WriteCoil = 5,
    224. ///
    225. /// (写入)预置单个寄存器
    226. ///
    227. [Description("(写入)预置单个寄存器")]
    228. WriteRegister = 6,
    229. ///
    230. /// (写入)预置多寄存器
    231. ///
    232. [Description("(写入)预置多寄存器")]
    233. WriteRegisterMultiple = 16,
    234. }
    235. }

    RTU

    串口基类 SerialPortBase

    1. using System;
    2. using System.Collections.Generic;
    3. using System.ComponentModel;
    4. using System.IO.Ports;
    5. using System.Linq;
    6. using System.Text;
    7. using System.Threading.Tasks;
    8. namespace CJH.ModbusTool.RTU
    9. {
    10. //Modbus 规定4个存储区
    11. // 区号 名称 读写 范围
    12. // 0区 输出线圈 可读可写 00001-09999
    13. // 1区 输入线圈 只读 10001-19999
    14. // 2区 输入寄存器 只读 30001-39999
    15. // 4区 保存寄存器 可读可写 40001-19999
    16. //功能码
    17. //01H 读取输出线圈
    18. //02H 读取输入线圈
    19. //03H 读取保持寄存器
    20. //04H 读取输入寄存器
    21. //05H (写入)预置单线圈
    22. //06H (写入)预置寄存器
    23. //0FH (写入)预置多线圈
    24. //10H (写入)预置多寄存器
    25. ///
    26. /// 串口基类
    27. ///
    28. public abstract class SerialPortBase : ModbusBase
    29. {
    30. protected SerialPort SerialPortObj;
    31. ///
    32. /// 初始化
    33. ///
    34. /// COM口名称
    35. /// 波特率
    36. /// 检验位
    37. /// 数据位
    38. /// 停止位
    39. protected void Init(string portName, int baudRate = 9600, Parity parity = Parity.None, int dataBits = 8, StopBits stopBits = StopBits.One)
    40. {
    41. SerialPortObj = new SerialPort(portName, baudRate, parity, dataBits, stopBits);
    42. if (SerialPortObj.IsOpen)
    43. {
    44. SerialPortObj.Close();
    45. }
    46. SerialPortObj.Open();
    47. }
    48. ///
    49. /// 关闭
    50. ///
    51. public void Close()
    52. {
    53. if (SerialPortObj.IsOpen)
    54. {
    55. SerialPortObj.Close();
    56. SerialPortObj.Dispose();
    57. SerialPortObj = null;
    58. }
    59. }
    60. }
    61. //功能码
    62. //01H 读取输出线圈
    63. //02H 读取输入线圈
    64. //03H 读取保持寄存器
    65. //04H 读取输入寄存器
    66. //05H (写入)预置单线圈
    67. //06H (写入)预置寄存器
    68. //0FH (写入)预置多线圈
    69. //10H (写入)预置多寄存器
    70. }

    Modbus 串口通讯

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

    1. using System;
    2. using System.Collections.Generic;
    3. using System.ComponentModel.Design;
    4. using System.IO.Ports;
    5. using System.Linq;
    6. using System.Text;
    7. using System.Threading.Tasks;
    8. namespace CJH.ModbusTool.RTU
    9. {
    10. ///
    11. /// Modbus 串口通讯
    12. ///
    13. public class ModbusRTU : SerialPortBase
    14. {
    15. private string _className = "ModbusRTU";
    16. ///
    17. /// Modbus 串口通讯
    18. ///
    19. /// COM口名称
    20. /// 波特率
    21. /// 检验位
    22. /// 数据位
    23. /// 停止位
    24. public ModbusRTU(string portName, int baudRate = 9600, Parity parity = Parity.None, int dataBits = 8, StopBits stopBits = StopBits.One)
    25. {
    26. Init(portName, baudRate, parity, dataBits, stopBits);
    27. //SerialPortObj.DataReceived += new SerialDataReceivedEventHandler(ComDataReceived);
    28. }
    29. ///
    30. /// 读取线圈数据 ok
    31. ///
    32. /// 从站地址
    33. /// 寄存器数量
    34. /// 功能码
    35. /// 起始寄存器地址
    36. /// 线圈数据
    37. public ModbusResultModel ReadData(byte devAddr, ushort length, ModbusFunctionCode functionCode = ModbusFunctionCode.ReadRegister, ushort startAddr = 0)
    38. {
    39. return ReadData(devAddr, length, (byte)functionCode, startAddr);
    40. }
    41. ///
    42. /// 读取数据 ok
    43. ///
    44. /// 从站地址
    45. /// 寄存器数量
    46. /// 功能码
    47. /// 起始寄存器地址
    48. /// 线圈数据
    49. public ModbusResultModel ReadData(byte devAddr, ushort length, byte functionCode = 2, ushort startAddr = 0)
    50. {
    51. var resultModel = new ModbusResultModel();
    52. //byte[] datas = null;
    53. if (functionCode >= 1 && functionCode <= 4)
    54. {
    55. try
    56. {
    57. //1.拼接报文:
    58. var sendCommand = new List<byte>();
    59. //协议格式:站地址+功能码+起始寄存器地址+寄存器数量+CRC
    60. //站地址
    61. sendCommand.Add(devAddr);
    62. //功能码
    63. sendCommand.Add(functionCode);
    64. //起始寄存器地址
    65. sendCommand.Add((byte)(startAddr / 256));
    66. sendCommand.Add((byte)(startAddr % 256));
    67. //寄存器数量
    68. sendCommand.Add((byte)(length / 256));
    69. sendCommand.Add((byte)(length % 256));
    70. //CRC
    71. byte[] crc = Crc16(sendCommand.ToArray(), sendCommand.Count);
    72. sendCommand.AddRange(crc);
    73. resultModel.SendDataStr = generateSendCommandStr(sendCommand.ToArray());
    74. //2.发送报文
    75. SerialPortObj.Write(sendCommand.ToArray(), 0, sendCommand.Count);
    76. //3.接收报文
    77. Thread.Sleep(50);//要延时一下,才能读到数据
    78. //读取响应报文
    79. byte[] respBytes = new byte[SerialPortObj.BytesToRead];
    80. SerialPortObj.Read(respBytes, 0, respBytes.Length);
    81. // respBytes -> 01 01 02 00 00 B9 FC
    82. resultModel.Datas = respBytes;
    83. // 检查一个校验位
    84. //if (CheckCRC(respBytes) && (respBytes.Length == 5 + length * 2)
    85. // && respBytes[0] == devAdd && respBytes[1] == functionCode && respBytes[1] == length * 2)
    86. if (CheckCRC(respBytes) && respBytes[0] == devAddr && respBytes[1] == functionCode)
    87. {
    88. //datas = respBytes;
    89. resultModel.IsSucceed = true;
    90. }
    91. else
    92. {
    93. resultModel.Msg = "响应报文校验失败";
    94. }
    95. }
    96. catch (Exception ex)
    97. {
    98. resultModel.Msg = "异常:" + ex.Message;
    99. }
    100. }
    101. else
    102. {
    103. //throw new Exception("功能码不正确[1-4]");
    104. resultModel.Msg = "功能码不正确[1-4]";
    105. }
    106. //SerialPortObj.Close();
    107. return resultModel;
    108. }
    109. ///
    110. /// 写入单个寄存器 ok
    111. /// 数据示例: 200
    112. /// 功能码 6
    113. ///
    114. /// 从站地址
    115. /// 写入的数据
    116. /// 写入地址
    117. /// 是否成功
    118. public ModbusResultModel WriteDataShort(int devAddr, short value, short startAddr = 0)
    119. {
    120. var resultModel = new ModbusResultModel();
    121. try
    122. {
    123. //bool isOk = false;
    124. //1.拼接报文:
    125. //var sendCommand = GetSingleDataWriteMessage(devAdd, startAddr, value); //ok
    126. var sendCommand = GetSingleDataWriteMessageList(devAddr, startAddr, value); //ok
    127. //var sendCommandStr = string.Join(' ', sendCommand.ToArray());
    128. resultModel.SendDataStr = generateSendCommandStr(sendCommand);
    129. //2.发送报文
    130. SerialPortObj.Write(sendCommand.ToArray(), 0, sendCommand.Length);
    131. //3.接收报文
    132. Thread.Sleep(50);//要延时一下,才能读到数据
    133. //读取响应报文
    134. byte[] respBytes = new byte[SerialPortObj.BytesToRead];
    135. SerialPortObj.Read(respBytes, 0, respBytes.Length);
    136. // respBytes -> 01 01 02 00 00 B9 FC
    137. // 检查一个校验位
    138. if (CheckCRC(respBytes) && respBytes[0] == devAddr && respBytes[1] == 0x06)
    139. {
    140. //isOk = true;
    141. resultModel.IsSucceed = true;
    142. }
    143. else
    144. {
    145. resultModel.Msg = "响应报文校验失败";
    146. }
    147. }
    148. catch (Exception ex)
    149. {
    150. resultModel.Msg = "异常:" + ex.Message;
    151. }
    152. //SerialPortObj.Close();
    153. return resultModel;
    154. }
    155. ///
    156. /// 写入单个寄存器 ok
    157. /// 数据示例: 200
    158. /// 功能码 6
    159. ///
    160. /// 站地址
    161. /// 写入的数据集合
    162. /// 写入地址
    163. /// 是否成功
    164. public ModbusResultModel WriteDataShort(int devAddr, List<short> dataList, short startAddr = 0)
    165. {
    166. var resultModel = new ModbusResultModel();
    167. if (dataList != null && dataList.Count > 0)
    168. {
    169. foreach (var item in dataList)
    170. {
    171. resultModel = WriteDataShort(devAddr, item, startAddr);
    172. startAddr++;
    173. }
    174. }
    175. return resultModel;
    176. }
    177. ///
    178. /// 获取写入单个寄存器的报文
    179. ///
    180. /// 从站地址
    181. /// 寄存器地址
    182. /// 写入值
    183. /// 写入单个寄存器的报文
    184. private byte[] GetSingleDataWriteMessage(int slaveStation, short startAddr, short value)
    185. {
    186. //从站地址
    187. byte station = (byte)slaveStation;
    188. //功能码
    189. byte type = 0x06;//06H (写入)预置寄存器
    190. //寄存器地址
    191. byte[] start = BitConverter.GetBytes(startAddr);
    192. //值
    193. byte[] valueBytes = BitConverter.GetBytes(value);
    194. //根据计算机大小端存储方式进行高低字节转换
    195. if (BitConverter.IsLittleEndian)
    196. {
    197. Array.Reverse(start);
    198. Array.Reverse(valueBytes);
    199. }
    200. //拼接报文
    201. byte[] result = new byte[] { station, type };
    202. result = result.Concat(start.Concat(valueBytes).ToArray()).ToArray();
    203. //计算校验码并拼接,返回最后的报文结果
    204. return result.Concat(Crc16(result, result.Length)).ToArray();
    205. }
    206. ///
    207. /// 获取写入单个寄存器的报文
    208. ///
    209. /// 从站地址
    210. /// 寄存器地址
    211. /// 写入值
    212. /// 写入单个寄存器的报文
    213. private byte[] GetSingleDataWriteMessageList(int slaveStation, short startAddr, short data)
    214. {
    215. //1.拼接报文:
    216. var sendCommand = new List<byte>();
    217. //从站地址
    218. byte station = (byte)slaveStation;
    219. //功能码
    220. byte type = 0x06;//06H (写入)预置寄存器
    221. //寄存器地址
    222. byte[] start = BitConverter.GetBytes(startAddr);
    223. //值
    224. byte[] valueBytes = BitConverter.GetBytes(data);
    225. //根据计算机大小端存储方式进行高低字节转换
    226. if (BitConverter.IsLittleEndian)
    227. {
    228. Array.Reverse(start);
    229. Array.Reverse(valueBytes);
    230. }
    231. sendCommand.Add((byte)slaveStation);
    232. sendCommand.Add(type);
    233. sendCommand.AddRange(start);
    234. sendCommand.AddRange(valueBytes);
    235. byte[] crc = Crc16(sendCommand.ToArray(), sendCommand.Count);
    236. sendCommand.AddRange(crc);
    237. return sendCommand.ToArray();
    238. }
    239. ///
    240. /// 写入多个寄存器 ok
    241. /// 数据示例: 123.45f, 14.3f
    242. /// 功能码 10
    243. ///
    244. /// 从站地址
    245. /// 写入的数据
    246. /// 写入地址
    247. /// 是否成功
    248. public ModbusResultModel WriteDataFloat(byte devAddr, float data, ushort startAddr = 0)
    249. {
    250. return WriteDataFloat(devAddr, new List<float>() { data }, startAddr);
    251. }
    252. ///
    253. /// 写入多个寄存器 ok
    254. /// 数据示例: 123.45f, 14.3f
    255. /// 功能码 10
    256. ///
    257. /// 从站地址
    258. /// 写入的数据
    259. /// 写入地址
    260. /// 是否成功
    261. public ModbusResultModel WriteDataFloat(byte devAddr, List<float> dataList, ushort startAddr = 0)
    262. {
    263. var resultModel = new ModbusResultModel();
    264. if (dataList != null && dataList.Count > 0)
    265. {
    266. try
    267. {
    268. byte functionCode = (byte)ModbusFunctionCode.WriteRegisterMultiple.GetHashCode();
    269. int length = dataList.Count * 2;
    270. //1.拼接报文:
    271. var sendCommand = new List<byte>();
    272. //协议格式:站地址+功能码+起始寄存器地址+寄存器数量+CRC
    273. //站地址
    274. sendCommand.Add(devAddr);
    275. //功能码
    276. sendCommand.Add(functionCode);
    277. //写入地址
    278. sendCommand.Add((byte)(startAddr / 256));
    279. sendCommand.Add((byte)(startAddr % 256));
    280. //寄存器数量
    281. sendCommand.Add((byte)(length / 256));
    282. sendCommand.Add((byte)(length % 256));
    283. // 获取数值的byte[]
    284. List<byte> valueBytes = new List<byte>();
    285. foreach (var data in dataList)
    286. {
    287. List<byte> temp = new List<byte>(BitConverter.GetBytes(data));
    288. temp.Reverse();// 调整字节序
    289. valueBytes.AddRange(temp);
    290. }
    291. // 字节数
    292. sendCommand.Add((byte)valueBytes.Count);
    293. sendCommand.AddRange(valueBytes);
    294. //CRC
    295. byte[] crc = Crc16(sendCommand.ToArray(), sendCommand.Count);
    296. sendCommand.AddRange(crc);
    297. //000004 - Rx:01 10 00 02 00 04 08 42 F6 E6 66 41 64 CC CD 83 23
    298. //000005 - Tx:01 10 00 02 00 04 60 0A
    299. //000006 - Rx:01 0A 00 02 00 04 08 42 F6 E6 66 41 64 CC CD 98 F9
    300. //000007 - Tx:01 8A 01 86 A0 //报错了
    301. //2.发送报文
    302. SerialPortObj.Write(sendCommand.ToArray(), 0, sendCommand.Count);
    303. //3.接收报文
    304. Thread.Sleep(50);//要延时一下,才能读到数据
    305. //读取响应报文
    306. byte[] respBytes = new byte[SerialPortObj.BytesToRead];
    307. SerialPortObj.Read(respBytes, 0, respBytes.Length);
    308. // respBytes -> 01 01 02 00 00 B9 FC
    309. // 检查一个校验位
    310. if (CheckCRC(respBytes) && respBytes[0] == devAddr && respBytes[1] == functionCode)
    311. {
    312. resultModel.IsSucceed = true;
    313. }
    314. else
    315. {
    316. resultModel.Msg = "响应报文校验失败";
    317. }
    318. }
    319. catch (Exception ex)
    320. {
    321. resultModel.Msg = "异常:" + ex.Message;
    322. }
    323. //SerialPortObj.Close();
    324. }
    325. else
    326. {
    327. resultModel.Msg = "dataLis参数不能为NULL 且 Count 要大于0";
    328. }
    329. return resultModel;
    330. }
    331. ///
    332. /// 写单个线圈输出 ok
    333. ///
    334. /// 开关
    335. /// 从站地址
    336. /// 写入地址
    337. ///
    338. public ModbusResultModel WriteSingleOutOnOff(bool on, byte devAddr = 1, ushort startAddr = 0)
    339. {
    340. var resultModel = new ModbusResultModel();
    341. try
    342. {
    343. //var isOk = false;
    344. //1.拼接报文:
    345. var sendCommand = new List<byte>();
    346. //协议格式:站地址+功能码+起始寄存器地址+寄存器数量+CRC
    347. //站地址
    348. sendCommand.Add(devAddr);
    349. //功能码
    350. byte functionCode = 0x05;
    351. sendCommand.Add(functionCode);
    352. //写入地址
    353. sendCommand.Add((byte)(startAddr / 256));
    354. sendCommand.Add((byte)(startAddr % 256));
    355. //写入数据
    356. sendCommand.Add((byte)(on ? 0xFF : 0x00));//true : 0xFF 开,false : 0x00 关
    357. sendCommand.Add(0x00);
    358. //CRC
    359. byte[] crc = Crc16(sendCommand.ToArray(), sendCommand.Count);
    360. sendCommand.AddRange(crc);
    361. //2.发送报文
    362. SerialPortObj.Write(sendCommand.ToArray(), 0, sendCommand.Count);
    363. //isOk = true;
    364. resultModel.IsSucceed = true;
    365. }
    366. catch (Exception ex)
    367. {
    368. resultModel.Msg = "异常:" + ex.Message;
    369. }
    370. return resultModel;
    371. }
    372. }
    373. }

    Modbus 串口通讯 读线圈状态

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

    1. using System;
    2. using System.Collections.Generic;
    3. using System.IO.Ports;
    4. using System.Linq;
    5. using System.Text;
    6. using System.Threading.Tasks;
    7. namespace CJH.ModbusTool.RTU
    8. {
    9. ///
    10. /// Modbus 串口通讯 读线圈状态
    11. ///
    12. public class ModbusRTUCoil : ModbusRTU
    13. {
    14. //ModbusRTU rtu = new ModbusRTU(portName);
    15. //var resultModel = rtu.ReadData(1, readLen, ModbusFunctionCode.ReadOutCoil);
    16. ///
    17. /// Modbus 串口通讯
    18. ///
    19. /// COM口名称
    20. /// 波特率
    21. /// 检验位
    22. /// 数据位
    23. /// 停止位
    24. public ModbusRTUCoil(string portName, int baudRate = 9600, Parity parity = Parity.None, int dataBits = 8, StopBits stopBits = StopBits.One)
    25. : base(portName, baudRate, parity, dataBits, stopBits)
    26. {
    27. //Init(portName, baudRate, parity, dataBits, stopBits);
    28. //SerialPortObj.DataReceived += new SerialDataReceivedEventHandler(ComDataReceived);
    29. }
    30. ///
    31. /// 读取线圈数据 ok
    32. ///
    33. /// 从站地址
    34. /// 寄存器数量
    35. /// 功能码
    36. /// 起始寄存器地址
    37. /// 线圈数据
    38. public ModbusResultModel<bool> ReadDataCoil(byte devAdd, ushort length, ModbusFunctionCode functionCode = ModbusFunctionCode.ReadRegister, ushort startAddr = 0)
    39. {
    40. var resultModel = new ModbusResultModel<bool>();
    41. var model = ReadData(devAdd, length, (byte)functionCode, startAddr);
    42. if (model != null && model.Datas != null && model.Datas.Length > 5)
    43. {
    44. resultModel.IsSucceed = model.IsSucceed;
    45. //报文解析
    46. // 检查一个校验位
    47. List<byte> respList = new List<byte>(model.Datas);
    48. respList.RemoveRange(0, 3);
    49. respList.RemoveRange(respList.Count - 2, 2);
    50. // 00 00
    51. //集合反转
    52. respList.Reverse();
    53. //转换成2进制
    54. var respStrList = respList.Select(r => Convert.ToString(r, 2)).ToList();
    55. var values = string.Join("", respStrList).ToList();
    56. values.Reverse();
    57. //values.ForEach(c => Console.WriteLine(Convert.ToBoolean(int.Parse(c.ToString()))));
    58. foreach (var v in values)
    59. {
    60. resultModel.ResultList.Add(v.ToString() == "1");
    61. }
    62. }
    63. return resultModel;
    64. }
    65. }
    66. }

    TCP

    ModbusTCP 基类

    1. using System;
    2. using System.Collections.Generic;
    3. using System.Linq;
    4. using System.Net.Sockets;
    5. using System.Runtime.InteropServices;
    6. using System.Security.Cryptography;
    7. using System.Text;
    8. using System.Threading.Tasks;
    9. namespace CJH.ModbusTool.TCP
    10. {
    11. ///
    12. /// ModbusTCP 基类
    13. ///
    14. public abstract class ModbusTCPBase : ModbusBase
    15. {
    16. private Socket _socket = null;
    17. ushort _tid = 0;//TransactionId 最大 65535
    18. ///
    19. /// 异常码 字典
    20. ///
    21. protected Dictionary<int, string> Errors = new Dictionary<int, string>() {
    22. { 0x01 , "非法功能码"},
    23. { 0x02 , "非法数据地址"},
    24. { 0x03 , "非法数据值"},
    25. { 0x04 , "从站设备故障"},
    26. { 0x05 , "确认,从站需要一个耗时操作"},
    27. { 0x06 , "从站忙"},
    28. { 0x08 , "存储奇偶性差错"},
    29. { 0x0A , "不可用网关路径"},
    30. { 0x0B , "网关目标设备响应失败"},
    31. };
    32. ///
    33. /// Modbus TCP 通讯 初始化
    34. ///
    35. /// 主机地址
    36. /// 端口
    37. protected void Init(string host, int port)
    38. {
    39. _socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
    40. _socket.Connect(host, port);
    41. }
    42. ///
    43. /// 读取报文的 公共方法
    44. ///
    45. /// 从站地址
    46. /// 寄存器数量
    47. /// 功能码
    48. /// 起始寄存器地址
    49. /// 返回报文(协议格式:TransactionId+协议标识+后续字节数+站地址+功能码+起始寄存器地址+寄存器数量)
    50. protected byte[] GenerateTcpCommandReadBytes(byte devAddr, ushort length, ModbusFunctionCode functionCode = ModbusFunctionCode.ReadRegister, ushort startAddr = 0)
    51. {
    52. var baseCommand = GenerateReadCommandBytes(devAddr, length, functionCode, startAddr);
    53. var sendCommand = new List<byte>();
    54. //TransactionId
    55. sendCommand.Add((byte)(_tid / 256));
    56. sendCommand.Add((byte)(_tid % 256));
    57. //Modbus 协议标识
    58. sendCommand.Add(0x00);
    59. sendCommand.Add(0x00);
    60. //后续字节数
    61. sendCommand.Add((byte)(baseCommand.Length / 256));
    62. sendCommand.Add((byte)(baseCommand.Length % 256));
    63. _tid++;
    64. _tid %= 65535;
    65. sendCommand.AddRange(baseCommand);
    66. return sendCommand.ToArray();
    67. }
    68. ///
    69. /// 读取报文的 公共方法
    70. ///
    71. /// 从站地址
    72. /// 输入数据
    73. /// 功能码
    74. /// 起始寄存器地址
    75. /// 返回报文(协议格式:TransactionId+协议标识+后续字节数+站地址+功能码+起始寄存器地址+寄存器数量)
    76. protected byte[] GenerateTcpCommandWriteBytes(byte devAddr, ushort data, ModbusFunctionCode functionCode = ModbusFunctionCode.WriteRegister, ushort startAddr = 0)
    77. {
    78. var baseCommand = GenerateWriteCommandBytes(devAddr, data, functionCode, startAddr);
    79. var sendCommand = new List<byte>();
    80. //TransactionId
    81. sendCommand.Add((byte)(_tid / 256));
    82. sendCommand.Add((byte)(_tid % 256));
    83. //Modbus 协议标识
    84. sendCommand.Add(0x00);
    85. sendCommand.Add(0x00);
    86. //后续字节数
    87. sendCommand.Add((byte)(baseCommand.Length / 256));
    88. sendCommand.Add((byte)(baseCommand.Length % 256));
    89. _tid++;
    90. _tid %= 65535;
    91. sendCommand.AddRange(baseCommand);
    92. return sendCommand.ToArray();
    93. }
    94. protected ModbusResultModel SendCommand(byte[] sendCommand)
    95. {
    96. var resultModel = new ModbusResultModel();
    97. try
    98. {
    99. //报文
    100. //TransactionId Modbus 协议标识 后续字节数 从站地址 功能码 起始寄存器地址 寄存器数量 CRC
    101. //0x00 0x01 0x00 0x00 0x00 0x06 devAddr functionCode 0x00 0x0A
    102. resultModel.SendDataStr = generateSendCommandStr(sendCommand.ToArray());
    103. _socket.Send(sendCommand.ToArray());
    104. //000002-Rx:00 00 00 00 00 06 01 03 00 01 00 05
    105. //000003-Tx:00 00 00 00 00 0D 01 03 0A 00 00 00 00 00 00 00 00 00 00
    106. // 00 00 00 00 00 0D 前6位
    107. // 01 03 0A (0,1,2)
    108. // 0A 数据长度 (0A=10)
    109. //先取前6位,固定返回
    110. var resp_bytes = new byte[6];// 00 00 00 00 00 0D 前6位
    111. _socket.Receive(resp_bytes, 0, resp_bytes.Length, SocketFlags.None);
    112. //取出下标为 :4和5的数据[00 0D]
    113. var len_bytes = resp_bytes.ToList().GetRange(4, 2);
    114. //起始寄存器地址 的反向操作
    115. //将下标为4和5 两个字节转成10进制数
    116. int len = len_bytes[0] * 256 + len_bytes[1];
    117. //获取数据的长度
    118. //01 03 0A 00 00 00 00 00 00 00 00 00 00 [正常]
    119. //01 83 02 [异常,83, 异常代码 :02]
    120. resp_bytes = new byte[len];
    121. _socket.Receive(resp_bytes, 0, len, SocketFlags.None);
    122. //检查响应报文是否正常
    123. //0x83 1000 0011
    124. //01 83 02 [异常,83, 异常代码 :02]
    125. if (resp_bytes[1] > 0x08)//判断是否异常
    126. {
    127. //resp_bytes[2] = 异常代码 :02
    128. //说明响应是异常报文
    129. //返回异常信息,根据resp_bytes字节进行异常关联
    130. if (Errors.ContainsKey(resp_bytes[2]))
    131. {
    132. resultModel.Msg = Errors[resp_bytes[2]];//获取异常码对应的异常说明
    133. }
    134. }
    135. else
    136. {
    137. //resp_bytes[2] = 0A 数据长度 (0A=10)
    138. //正常
    139. resultModel.Datas = resp_bytes.ToList().GetRange(3, resp_bytes[2]).ToArray();
    140. resultModel.IsSucceed = true;
    141. }
    142. }
    143. catch (Exception ex)
    144. {
    145. resultModel.Msg = ex.Message;
    146. }
    147. return resultModel;
    148. }
    149. ///
    150. /// 解析数据
    151. ///
    152. ///
    153. ///
    154. ///
    155. public List<T> AnalysisDatas<T>(byte[] datas)
    156. {
    157. //data_bytes 每两个字节转成一个数字, float 4个字节转成一个数字,double 8个字节转成一个数字
    158. //2 ushort short int16 uint32 float
    159. //4 int uint int32 uint32 float
    160. //8 double
    161. //16 decimal
    162. var resultValue = new List();
    163. try
    164. {
    165. var type_len = Marshal.SizeOf(typeof(T));
    166. for (int i = 0; i < datas.Length; i += type_len)
    167. {
    168. var temp_bytes = datas.ToList().GetRange(i, type_len);
    169. if (BitConverter.IsLittleEndian)
    170. {
    171. temp_bytes.Reverse();
    172. }
    173. //反射 方法
    174. Type bitConverter_type = typeof(BitConverter);
    175. var typeMethodList = bitConverter_type.GetMethods().ToList();
    176. //找到返回类型和传入的类型一至,且方法的参数是2个的方法
    177. var method = typeMethodList.FirstOrDefault(mi => mi.ReturnType == typeof(T)
    178. && mi.GetParameters().Length == 2);
    179. if (method == null)
    180. {
    181. throw new Exception("数据转换类型出错!");
    182. }
    183. else
    184. {
    185. //由 bitConverter_type 执行找到的 method方法,注意参数数量,上面找是的两个参数的方法
    186. var value = method.Invoke(bitConverter_type, new object[] { temp_bytes.ToArray(), 0 });
    187. resultValue.Add((T)value);
    188. }
    189. }
    190. }
    191. catch (Exception ex)
    192. {
    193. }
    194. return resultValue;
    195. }
    196. }
    197. }

    Modbus TCP 通讯

    1. using System;
    2. using System.Collections.Generic;
    3. using System.Linq;
    4. using System.Text;
    5. using System.Threading.Tasks;
    6. using System.Net.Sockets;
    7. namespace CJH.ModbusTool.TCP
    8. {
    9. ///
    10. /// Modbus TCP 通讯
    11. ///
    12. public class ModbusTCP : ModbusTCPBase
    13. {
    14. ///
    15. /// Modbus TCP 通讯
    16. ///
    17. /// 主机地址
    18. /// 端口
    19. public ModbusTCP(string host, int port)
    20. {
    21. //_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
    22. //_socket.Connect(host, port);
    23. Init(host, port);
    24. }
    25. ///
    26. /// 读取保持型寄存器 03
    27. ///
    28. /// 从站地址
    29. /// 数量
    30. /// 功能码
    31. /// 起始地址
    32. //public ModbusResultModel ReadHoldingRegister(byte devAddr, ushort length, byte functionCode = 3, ushort startAddr = 0)
    33. //{
    34. // var resultModel = new ModbusResultModel();
    35. // //报文
    36. // //TransactionId Modbus 协议标识 后续字节数 从站地址 功能码 起始寄存器地址 寄存器数量 CRC
    37. // //0x00 0x01 0x00 0x00 0x00 0x06 devAddr functionCode 0x00 0x0A
    38. // try
    39. // {
    40. // ushort tid = 0;//TransactionId 最大 65535
    41. // var sendCommand = new List();
    42. // //TransactionId
    43. // sendCommand.Add((byte)(tid / 256));
    44. // sendCommand.Add((byte)(tid % 256));
    45. // //Modbus 协议标识
    46. // sendCommand.Add(0x00);
    47. // sendCommand.Add(0x00);
    48. // //后续字节数
    49. // sendCommand.Add(0x00);
    50. // sendCommand.Add(0x06);
    51. // //从站地址
    52. // sendCommand.Add(devAddr);
    53. // //功能码
    54. // sendCommand.Add(functionCode);
    55. // //起始寄存器地址
    56. // sendCommand.Add((byte)(startAddr / 256));
    57. // sendCommand.Add((byte)(startAddr % 256));
    58. // //寄存器数量
    59. // sendCommand.Add((byte)(length / 256));
    60. // sendCommand.Add((byte)(length % 256));
    61. // //CRC
    62. // //byte[] crc = Crc16(sendCommand.ToArray(), sendCommand.Count);
    63. // //sendCommand.AddRange(crc);
    64. // tid++;
    65. // tid %= 65535;
    66. // resultModel.SendDataStr = generateSendCommandStr(sendCommand.ToArray());
    67. // _socket.Send(sendCommand.ToArray());
    68. // //000002-Rx:00 00 00 00 00 06 01 03 00 01 00 05
    69. // //000003-Tx:00 00 00 00 00 0D 01 03 0A 00 00 00 00 00 00 00 00 00 00
    70. // // 00 00 00 00 00 0D 前6位
    71. // // 01 03 0A (0,1,2)
    72. // // 0A 数据长度 (0A=10)
    73. // //先取前6位,固定返回
    74. // var resp_bytes = new byte[6];// 00 00 00 00 00 0D 前6位
    75. // _socket.Receive(resp_bytes, 0, resp_bytes.Length, SocketFlags.None);
    76. // //取出下标为 :4和5的数据[00 0D]
    77. // var len_bytes = resp_bytes.ToList().GetRange(4, 2);
    78. // //起始寄存器地址 的反向操作
    79. // //将下标为4和5 两个字节转成10进制数
    80. // int len = resp_bytes[4] * 256 + resp_bytes[5];
    81. // //获取数据的长度
    82. // //01 03 0A 00 00 00 00 00 00 00 00 00 00 [正常]
    83. // //01 83 02 [异常,83, 异常代码 :02]
    84. // resp_bytes = new byte[len];
    85. // _socket.Receive(resp_bytes, 0, len, SocketFlags.None);
    86. // //检查响应报文是否正常
    87. // //0x83 1000 0011
    88. // //01 83 02 [异常,83, 异常代码 :02]
    89. // if (resp_bytes[1] > 0x08)//判断是否异常
    90. // {
    91. // //resp_bytes[2] = 异常代码 :02
    92. // //说明响应是异常报文
    93. // //返回异常信息,根据resp_bytes字节进行异常关联
    94. // if (Errors.ContainsKey(resp_bytes[2]))
    95. // {
    96. // resultModel.Msg = Errors[resp_bytes[2]];//获取异常码对应的异常说明
    97. // }
    98. // }
    99. // else
    100. // {
    101. // //resp_bytes[2] = 0A 数据长度 (0A=10)
    102. // //正常
    103. // resultModel.Datas = resp_bytes.ToList().GetRange(3, resp_bytes[2]).ToArray();
    104. // resultModel.IsSucceed = true;
    105. // }
    106. // }
    107. // catch (Exception ex)
    108. // {
    109. // resultModel.Msg = ex.Message;
    110. // }
    111. // return resultModel;
    112. //}
    113. ///
    114. /// 读取保持型寄存器 03
    115. ///
    116. /// 从站地址
    117. /// 数量
    118. /// 功能码
    119. /// 起始地址
    120. /// 返回对象
    121. public ModbusResultModel<T> ReadHoldingRegister<T>(byte devAddr, ushort length, ModbusFunctionCode functionCode = ModbusFunctionCode.ReadRegister, ushort startAddr = 0)
    122. {
    123. var resultModel = new ModbusResultModel();
    124. try
    125. {
    126. var command = GenerateTcpCommandReadBytes(devAddr, length, functionCode, startAddr);
    127. resultModel.SendDataStr = generateSendCommandStr(command.ToArray());
    128. var receptionModel = SendCommand(command.ToArray());
    129. if (receptionModel.IsSucceed
    130. && receptionModel.Datas != null && receptionModel.Datas.Length > 0)
    131. {
    132. resultModel.Datas = receptionModel.Datas;
    133. resultModel.ResultList = AnalysisDatas(receptionModel.Datas);
    134. resultModel.IsSucceed = true;
    135. }
    136. }
    137. catch (Exception ex)
    138. {
    139. resultModel.Msg = ex.Message;
    140. }
    141. return resultModel;
    142. }
    143. ///
    144. /// 写入保持型寄存器 03
    145. ///
    146. /// 从站地址
    147. /// 写入数据
    148. /// 功能码
    149. /// 起始地址
    150. /// 返回对象
    151. public ModbusResultModel WriteHoldingRegister(byte devAddr, ushort data, ModbusFunctionCode functionCode = ModbusFunctionCode.WriteRegister, ushort startAddr = 0)
    152. {
    153. var resultModel = new ModbusResultModel();
    154. try
    155. {
    156. var command = GenerateTcpCommandWriteBytes(devAddr, data, functionCode, startAddr);
    157. resultModel.SendDataStr = generateSendCommandStr(command.ToArray());
    158. var receptionModel = SendCommand(command.ToArray());
    159. if (receptionModel.IsSucceed)
    160. {
    161. resultModel.IsSucceed = true;
    162. }
    163. }
    164. catch (Exception ex)
    165. {
    166. resultModel.Msg = ex.Message;
    167. }
    168. return resultModel;
    169. }
    170. }
    171. }

    这就是全部的代码。如有使用的问题可以给我发评论。

  • 相关阅读:
    初学python(一)
    二叉搜索树--详细实现过程
    MySQL——慢查询日志分析
    年薪40w的高级网络工程师面试过程,面试过程不难,但你要真做过才能回答上来
    com.google.code:kaptcha-2.3.jar
    Python模块:hashlib模块教程
    计算机网络 --- 网络编程
    程序员面试金典 - 面试题 17.18. 最短超串
    Nginx学习(2)—— 常用命令
    TypeScript学习记录
  • 原文地址:https://blog.csdn.net/cjh16606260986/article/details/136281351