using ModbusDemo.Model;
using ModbusDemo.Uitls;
using System;
using System.Collections.Generic;
using System.IO.Ports;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
namespace ModbusDemo.Device
{
public class ModbusRTU : IModbusRTU
{
private SerialPort _serialPort;
//用于操作数据库
private ModbusDbContext _modbusDbContext;
public ModbusRTU(SerialPort serialPort, ModbusDbContext modbusDbContext)
{
_serialPort = serialPort;
_modbusDbContext = modbusDbContext;
}
///
/// 用来发送读写线圈的指令
///
///
///
///
///
public bool[] ReadCoil(byte slaveAddress, ushort startAddress, ushort numberOfPoints)
{
bool[] resultValue = null;
if (!_serialPort.IsOpen)
{
MessageBox.Show("串口没有链接,请先链接");
return resultValue;
}
List sendByteList = new List();
//设置从站地址
sendByteList.Add(slaveAddress);
//设置功能码
sendByteList.Add(0x01);
//设置起始地址的高位和低位
sendByteList.Add(BitConverter.GetBytes(startAddress)[1]);
sendByteList.Add(BitConverter.GetBytes(startAddress)[0]);
//设置读取几个线圈的高位和低位
sendByteList.Add(BitConverter.GetBytes(numberOfPoints)[1]);
sendByteList.Add(BitConverter.GetBytes(numberOfPoints)[0]);
//获取CRC校验码
byte[] getCRC = sendByteList.ToArray();
byte[] resultCRC = CRCUitl.CalculateCRC(getCRC, getCRC.Length);
sendByteList.Add(resultCRC[0]);
sendByteList.Add(resultCRC[1]);
byte[] sendByte = sendByteList.ToArray();
int maxRetries = 3; // 最大重试次数
int attempt = 0;
while (attempt < maxRetries)
{
try
{
// 清空接收缓冲区(避免残留数据干扰)
_serialPort.DiscardInBuffer();
_serialPort.Write(sendByte, 0, sendByte.Length);
Thread.Sleep(300);
byte[] response = new byte[_serialPort.BytesToRead];
_serialPort.Read(response, 0, _serialPort.BytesToRead);
if (response[0] == 0x00)
{
continue;
}
resultValue = ParseCoilresponse(sendByte, response, numberOfPoints);
return resultValue;
}
catch (Exception)
{
attempt++;
}
}
//将操作失败的指令,插入数据库
string RequestStr = ByteArrayToString(sendByte);
ModbusLog m = new ModbusLog();
m.OperationType = "读线圈";
m.ResponseData = "";
m.RequestData = RequestStr;
_modbusDbContext.Add(m);
_modbusDbContext.SaveChanges();
return resultValue;
}
public bool[] ParseCoilresponse(byte[] sendByte, byte[] response, ushort numberOfPoints)
{
//将操作的指令,插入数据库
string RequestStr = ByteArrayToString(sendByte);
string responseStr = ByteArrayToString(response);
ModbusLog m = new ModbusLog();
m.OperationType = "读线圈";
m.ResponseData = responseStr;
m.RequestData = RequestStr;
_modbusDbContext.Add(m);
_modbusDbContext.SaveChanges();
//判断发送回来的数据是否正确
if (CheckData.CheckResponse(response))
{
List responseList = new List(response);
//移除前两位
responseList.RemoveRange(0, 3);
//移除后两位校验码
responseList.RemoveRange(responseList.Count - 2, 2);
responseList.Reverse();
//数组反转之后,转为二进制字符
List StringList = responseList.Select(m => Convert.ToString(m, 2)).ToList();
var result = "";
foreach (var item in StringList)
{
result += item.PadLeft(8, '0');
}
//先将字符串转为数组,再反转,再转为字节数组
char[] chars = result.ToArray().Reverse().ToArray();
bool[] ultimately = new bool[numberOfPoints];
for (int i = 0; i < numberOfPoints; i++)
{
if (chars[i] == '1')
{
ultimately[i] = true;
}
else
{
ultimately[i] = false;
}
}
return ultimately;
}
else
{
return null;
}
}
///
/// 写入多个线圈操作
///
/// 从站地址
/// 起始地址
/// 写入的数据
public void WriteCoil(byte slaveAddress, ushort startAddress, bool[] data)
{
if (!_serialPort.IsOpen)
{
MessageBox.Show("串口没有链接,请先链接");
return;
}
List sendByteList = new List();
//设置从站地址
sendByteList.Add(slaveAddress);
//设置功能码
sendByteList.Add(0x0F);
//设置起始地址的高位和低位
sendByteList.Add(BitConverter.GetBytes(startAddress)[1]);
sendByteList.Add(BitConverter.GetBytes(startAddress)[0]);
//设置写入几个线圈
sendByteList.Add(BitConverter.GetBytes(data.Length)[1]);
sendByteList.Add(BitConverter.GetBytes(data.Length)[0]);
// 计算字节数
int byteCount = (data.Length + 7) / 8;
sendByteList.Add((byte)byteCount);
// 初始化字节数组
byte[] coilBytes = new byte[byteCount];
// 把bool数组转换为字节
for (int i = 0; i < data.Length; i++)
{
int byteIndex = i / 8;
int bitIndex = i % 8;
if (data[i])
{
coilBytes[byteIndex] |= (byte)(1 << bitIndex);
}
}
sendByteList.AddRange(coilBytes);
//获取CRC校验码
byte[] getCRC = sendByteList.ToArray();
byte[] resultCRC = CRCUitl.CalculateCRC(getCRC, getCRC.Length);
sendByteList.Add(resultCRC[0]);
sendByteList.Add(resultCRC[1]);
byte[] sendByte = sendByteList.ToArray();
int maxRetries = 3; // 最大重试次数
int attempt = 0;
byte[] response = null;
while (attempt < maxRetries)
{
try
{
// 清空接收缓冲区(避免残留数据干扰)
_serialPort.DiscardInBuffer();
_serialPort.Write(sendByte, 0, sendByte.Length);
Thread.Sleep(300);
response = new byte[_serialPort.BytesToRead];
_serialPort.Read(response, 0, _serialPort.BytesToRead);
if (response[0] != 0x00)
{
break;
}
}
catch (Exception)
{
attempt++;
}
}
//将操作的指令,插入数据库
string RequestStr = ByteArrayToString(sendByte);
string responseStr = ByteArrayToString(response);
ModbusLog m = new ModbusLog();
m.OperationType = "写线圈";
m.ResponseData = responseStr;
m.RequestData = RequestStr;
_modbusDbContext.Add(m);
_modbusDbContext.SaveChanges();
//判断发送回来的数据是否正确
if (CheckData.CheckResponse(response))
{
}
}
public ushort[] ReadRegisters(byte slaveAddress, ushort startAddress, ushort numberOfPoints)
{
ushort[] resultValue = null;
if (!_serialPort.IsOpen)
{
MessageBox.Show("串口没有链接,请先链接");
return resultValue;
}
List sendByteList = new List();
//设置从站地址
sendByteList.Add(slaveAddress);
//设置功能码
sendByteList.Add(0x03);
//设置起始地址的高位和低位
sendByteList.Add(BitConverter.GetBytes(startAddress)[1]);
sendByteList.Add(BitConverter.GetBytes(startAddress)[0]);
//设置读取几个线圈的高位和低位
sendByteList.Add(BitConverter.GetBytes(numberOfPoints)[1]);
sendByteList.Add(BitConverter.GetBytes(numberOfPoints)[0]);
//获取CRC校验码
byte[] getCRC = sendByteList.ToArray();
byte[] resultCRC = CRCUitl.CalculateCRC(getCRC, getCRC.Length);
sendByteList.Add(resultCRC[0]);
sendByteList.Add(resultCRC[1]);
byte[] sendByte = sendByteList.ToArray();
int maxRetries = 3; // 最大重试次数
int attempt = 0;
while (attempt < maxRetries)
{
try
{
// 清空接收缓冲区(避免残留数据干扰)
_serialPort.DiscardInBuffer();
_serialPort.Write(sendByte, 0, sendByte.Length);
Thread.Sleep(300);
byte[] response = new byte[_serialPort.BytesToRead];
_serialPort.Read(response, 0, _serialPort.BytesToRead);
if (response[0] == 0x00)
{
continue;
}
resultValue = ParseRegistersresponse(sendByte, response, numberOfPoints);
return resultValue;
}
catch (Exception)
{
attempt++;
}
}
//将操作失败的指令,插入数据库
string RequestStr = ByteArrayToString(sendByte);
ModbusLog m = new ModbusLog();
m.OperationType = "读寄存器";
m.ResponseData = "";
m.RequestData = RequestStr;
_modbusDbContext.Add(m);
_modbusDbContext.SaveChanges();
return resultValue;
}
public ushort[] ParseRegistersresponse(byte[] sendByte, byte[] response, ushort numberOfPoints)
{
//将操作的指令,插入数据库
string RequestStr = ByteArrayToString(sendByte);
string responseStr = ByteArrayToString(response);
ModbusLog m = new ModbusLog();
m.OperationType = "读寄存器";
m.ResponseData = responseStr;
m.RequestData = responseStr;
_modbusDbContext.Add(m);
_modbusDbContext.SaveChanges();
//判断发送回来的数据是否正确
if (CheckData.CheckResponse(response))
{
ushort[] registers = new ushort[numberOfPoints];
for (int i = 0; i < numberOfPoints; i++)
{
int pos = 3 + i * 2;
// 大端序转换 (高位在前)
registers[i] = (ushort)((response[pos] << 8) | response[pos + 1]);
}
return registers;
}
else
{
return null;
}
}
public void WriteRegisters(byte slaveAddress, ushort startAddress, ushort[] data)
{
if (!_serialPort.IsOpen)
{
MessageBox.Show("串口没有链接,请先链接");
return;
}
List sendByteList = new List();
//设置从站地址
sendByteList.Add(slaveAddress);
//设置功能码
sendByteList.Add(0x10);
//设置起始地址的高位和低位
sendByteList.Add(BitConverter.GetBytes(startAddress)[1]);
sendByteList.Add(BitConverter.GetBytes(startAddress)[0]);
//设置写入几个线圈
sendByteList.Add(BitConverter.GetBytes(data.Length)[1]);
sendByteList.Add(BitConverter.GetBytes(data.Length)[0]);
// 计算字节数
int byteCount = (data.Length + 7) / 8;
sendByteList.Add((byte)byteCount);
foreach (ushort value in data)
{
byte[] valueBytes = BitConverter.GetBytes(value);
// 大端序:高字节在前,低字节在后
sendByteList.Add(valueBytes[1]);
sendByteList.Add(valueBytes[0]);
}
//获取CRC校验码
byte[] getCRC = sendByteList.ToArray();
byte[] resultCRC = CRCUitl.CalculateCRC(getCRC, getCRC.Length);
sendByteList.Add(resultCRC[0]);
sendByteList.Add(resultCRC[1]);
byte[] sendByte = sendByteList.ToArray();
int maxRetries = 3; // 最大重试次数
int attempt = 0;
byte[] response = null;
while (attempt< maxRetries)
{
try
{
// 清空接收缓冲区(避免残留数据干扰)
_serialPort.DiscardInBuffer();
_serialPort.Write(sendByte, 0, sendByte.Length);
Thread.Sleep(1000);
response = new byte[_serialPort.BytesToRead];
_serialPort.Read(response, 0, _serialPort.BytesToRead);
if (response[0] != 0x00)
{
break;
}
}
catch (Exception)
{
attempt++;
}
}
//将操作的指令,插入数据库
string RequestStr = ByteArrayToString(sendByte);
string responseStr = ByteArrayToString(response);
ModbusLog m = new ModbusLog();
m.OperationType = "写寄存器";
m.ResponseData = responseStr;
m.RequestData = responseStr;
_modbusDbContext.Add(m);
_modbusDbContext.SaveChanges();
//判断发送回来的数据是否正确
CheckData.CheckResponse(response);
}
static string ByteArrayToString(byte[] byteArray)
{
if (byteArray == null || byteArray.Length == 0)
return string.Empty;
System.Text.StringBuilder sb = new System.Text.StringBuilder();
// 处理第一个元素(无前导空格)
sb.Append(byteArray[0].ToString("X2"));
// 处理后续元素(每个前面加空格)
for (int i = 1; i < byteArray.Length; i++)
{
sb.Append(' ');
sb.Append(byteArray[i].ToString("X2"));
}
return sb.ToString();
}
}
}