2021-11-22 WPF上位机 96-Modbus通信代码的封装
Posted 微软MVP Eleven
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2021-11-22 WPF上位机 96-Modbus通信代码的封装相关的知识,希望对你有一定的参考价值。
public class ModbusBase
public List<byte> GetReadCommand(byte deviceAddr, byte funcCode, ushort startAddr, ushort length)
List<byte> buffer = new List<byte>();
buffer.Add(deviceAddr);
buffer.Add(funcCode);
buffer.Add(BitConverter.GetBytes(startAddr)[1]);
buffer.Add(BitConverter.GetBytes(startAddr)[0]);
buffer.Add(BitConverter.GetBytes(length)[1]);
buffer.Add(BitConverter.GetBytes(length)[0]);
return buffer;
public class SerialPortBase : ModbusBase
public class ModbusRTU : SerialPortBase
SerialPort serialPort = null;
public ModbusRTU(string portName, int baudRate, Parity parity, int dataBits, StopBits stopBits)
serialPort = new SerialPort(portName, baudRate, parity, dataBits, stopBits);
public bool Open()
try
if (serialPort == null)
throw new Exception();
if (!serialPort.IsOpen)
serialPort.Open();
return true;
catch
return false;
// Socket 掉线重连
// 1、服务端(从站)踢掉一个连接:重新创建一个连接对象
// 2、断网,网线中断(关键服务端的处理 监听)
public bool Close()
try
if (serialPort != null && serialPort.IsOpen)
serialPort.Close();
serialPort = null;
return true;
catch
return false;
// 半双工 读时不能写 业务功能(任务队列(读、写)) List<Operation>
// 1、重复代码非常多,泛型 转换类型怎么处理 最终方案 不是Switch
// 2、字节序
// 3、掉线重连
// 发送报文 03
public List<ushort> ReadKeepRegister(byte deviceAddr, byte funcCode, ushort startAddr, ushort length)
// 获取基本报文
List<byte> bytes = this.GetReadCommand(deviceAddr, funcCode, startAddr, length);
// CRC
bytes.AddRange(CRC16(bytes));
if (serialPort.IsOpen)
serialPort.Write(bytes.ToArray(), 0, bytes.Count);
// 进行响应报文的解析
int time = 1;
while (time < 2000 && serialPort.BytesToRead <= 0)
time += 1;
//Debug.WriteLine("1");
Thread.Sleep(1);
if (serialPort.BytesToRead <= 0)
throw new Exception("响应超时");
byte[] buffer = new byte[serialPort.BytesToRead];
serialPort.Read(buffer, 0, buffer.Length);
List<byte> byteList = new List<byte>(buffer);
// CRC校验检查
List<byte> checkCRC = byteList.GetRange(byteList.Count - 2, 2);
byteList.RemoveRange(byteList.Count - 2, 2);
List<byte> validCode = CRC16(byteList);
if (validCode.SequenceEqual(checkCRC))
// 进行协议异常码的检查
// 1-检查功能码 最高位是否为1
int fc = byteList[1];
if ((fc & 0x80) == 0x80)
// 说明报文 异常
// 取异常码
throw new Exception(byteList[2].ToString());
// 数据解析
// 01 03 14 00 00 ...... (CRC)
byteList.RemoveRange(0, 3);
List<ushort> values = new List<ushort>();
List<byte> valueBytes = new List<byte>();
for (int i = 0; i < length; i++)
valueBytes.Clear();
valueBytes.Add(byteList[i * 2 + 1]);
valueBytes.Add(byteList[i * 2]);
var value = BitConverter.ToUInt16(valueBytes.ToArray(), 0);
values.Add(value);
return values;
else
Task.Run(async () =>
// 尝试次数 会有升级
while (true)
await Task.Delay(2000);
if (serialPort.IsOpen)
Console.WriteLine("连接正常");
continue;
try
Console.WriteLine("连接断开,尝试重新连接");
serialPort.Open();
catch (System.IO.IOException ex)
Console.WriteLine("设备连接断开");
continue;
);
return null;
public List<float> ReadKeepRegisterFloat(byte deviceAddr, byte funcCode, ushort startAddr, ushort length)
// 获取基本报文
List<byte> bytes = this.GetReadCommand(deviceAddr, funcCode, startAddr, length);
// CRC
bytes.AddRange(CRC16(bytes));
if (serialPort.IsOpen)
serialPort.Write(bytes.ToArray(), 0, bytes.Count);
// 进行响应报文的解析
int time = 1;
while (time < 2000 && serialPort.BytesToRead <= 0)
time += 1;
//Debug.WriteLine("1");
Thread.Sleep(1);
if (serialPort.BytesToRead <= 0)
throw new Exception("响应超时");
byte[] buffer = new byte[serialPort.BytesToRead];
serialPort.Read(buffer, 0, buffer.Length);
List<byte> byteList = new List<byte>(buffer);
// CRC校验检查
List<byte> checkCRC = byteList.GetRange(byteList.Count - 2, 2);
byteList.RemoveRange(byteList.Count - 2, 2);
List<byte> validCode = CRC16(byteList);
if (validCode.SequenceEqual(checkCRC))
// 进行协议异常码的检查
// 1-检查功能码 最高位是否为1
int fc = byteList[1];
if ((fc & 0x80) == 0x80)
// 说明报文 异常
// 取异常码
throw new Exception(byteList[2].ToString());
// 数据解析
// 01 03 14 00 00 ...... (CRC)
byteList.RemoveRange(0, 3);
List<float> values = new List<float>();
List<byte> valueBytes = new List<byte>();
for (int i = 0; i < length / 2; i++)
valueBytes.Clear();
valueBytes.Add(byteList[i * 4 + 3]);
valueBytes.Add(byteList[i * 4 + 2]);
valueBytes.Add(byteList[i * 4 + 1]);
valueBytes.Add(byteList[i * 4]);
var value = BitConverter.ToSingle(valueBytes.ToArray(), 0);
values.Add(value);
return values;
else
Task.Run(async () =>
// 尝试次数 会有升级
while (true)
await Task.Delay(2000);
if (serialPort.IsOpen)
Console.WriteLine("连接正常");
continue;
try
Console.WriteLine("连接断开,尝试重新连接");
serialPort.Open();
catch (System.IO.IOException ex)
Console.WriteLine("设备连接断开");
continue;
);
return null;
/// <summary>
/// CRC校验
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
private List<byte> CRC16(List<byte> value)
ushort poly = 0xA001;
ushort crcInit = 0xFFFF;
if (value == null || !value.Any())
throw new ArgumentException("");
//运算
ushort crc = crcInit;
for (int i = 0; i < value.Count; i++)
crc = (ushort)(crc ^ (value[i]));
for (int j = 0; j < 8; j++)
crc = (crc & 1) != 0 ? (ushort)((crc >> 1) ^ poly) : (ushort)(crc >> 1);
byte hi = (byte)((crc & 0xFF00) >> 8); //高位置
byte lo = (byte)(crc & 0x00FF); //低位置
List<byte> buffer = new List<byte>();
buffer.Add(lo);
buffer.Add(hi);
return buffer;
以上是关于2021-11-22 WPF上位机 96-Modbus通信代码的封装的主要内容,如果未能解决你的问题,请参考以下文章
2021-11-22 WPF上位机 96-Modbus通信代码的封装
2021-11-22 WPF上位机 93-ModbusTCP/IP消息帧