using DryIoc;
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;
using System.Threading.Tasks;
using System.Windows;
namespace ModbusDemo.Device
{
public class ModbusRTU : IModbusRTU
{
//串口
private SerialPort _serialPort;
//用于操作数据库
private ModbusDbContext _modbusDbContext;
//TODO,修改
private SerialPortAdapter _portAdapter;
public ModbusRTU(SerialPort serialPort, ModbusDbContext modbusDbContext)
{
_serialPort = serialPort;
_modbusDbContext = modbusDbContext;
//_portAdapter = new SerialPortAdapter(serialPort);
}
///
/// 用来发送读写线圈的指令
///
///
///
///
///
public bool[] ReadCoil(byte slaveAddress, ushort startAddress, ushort numberOfPoints)
{
bool[] resultValue = null;
if (!_serialPort.IsOpen)
{
MessageBox.Show("串口没有链接,请先链接");
return resultValue;
}
List sendByteList = new List
{
slaveAddress, // 从站地址
0x01, // 功能码:读线圈
};
//设置起始地址的高位和低位
sendByteList.AddRange(BitConverter.GetBytes(startAddress).Reverse());
//设置读取几个线圈的高位和低位
sendByteList.AddRange(BitConverter.GetBytes(numberOfPoints).Reverse());
//获取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;
List responseData = new List();
while (attempt < maxRetries)
{
if (_serialPort.IsOpen)
{
// 每次重试前清空缓冲区
responseData.Clear();
// 清除输入缓冲区残留数据
_serialPort.DiscardInBuffer();
}
try
{
_serialPort.Write(sendByte, 0, sendByte.Length);
//使用时间去轮询查询数据
DateTime startTime = DateTime.Now;
while ((DateTime.Now - startTime).TotalMilliseconds < _serialPort.ReadTimeout)
{
int bytesToRead = _serialPort.BytesToRead;
if (bytesToRead > 0)
{
byte[] buffer = new byte[bytesToRead];
int bytesRead = _serialPort.Read(buffer, 0, bytesToRead);
responseData.AddRange(buffer.Take(bytesRead));
}
//延迟20毫秒,节约cpu资源
Thread.Sleep(20);
}
response = responseData.ToArray();
if (response.Length == 0)
{
attempt++;
continue;
}
resultValue = ParseCoilresponse(sendByte, response, numberOfPoints);
return resultValue;
}
catch (Exception)
{
}
finally
{
attempt++;
}
}
try
{
//将操作失败的指令,插入数据库
string RequestStr = ByteArrayToString(sendByte);
ModbusLog m = new ModbusLog();
m.OperationType = "读线圈";
m.ResponseData = "";
m.RequestData = RequestStr;
_modbusDbContext.Add(m);
_modbusDbContext.SaveChanges();
}
catch (Exception)
{
MessageBox.Show(ErrorCode.ErrorCode.DatabaseException.ToString());
}
return resultValue;
}
///
/// 用来解析返回的数据
///
///
///
///
///
public bool[] ParseCoilresponse(byte[] sendByte, byte[] response, ushort numberOfPoints)
{
try
{
//将操作的指令,插入数据库
string RequestStr = ByteArrayToString(sendByte);
string responseStr = ByteArrayToString(response);
ModbusLog modbusLog = new ModbusLog();
modbusLog.OperationType = "读线圈";
modbusLog.ResponseData = responseStr;
modbusLog.RequestData = RequestStr;
_modbusDbContext.Add(modbusLog);
_modbusDbContext.SaveChanges();
}
catch (Exception)
{
MessageBox.Show(ErrorCode.ErrorCode.DatabaseException.ToString());
}
//判断发送回来的数据是否正确
if (CheckData.CheckResponse(response))
{
// 移除前3个字节和后2个校验码字节,并反转数组
var processedBytes = response
.Skip(3) // 移除前3个字节
.Take(response.Count() - 5) // 保留中间部分 (总长度-5)
.Reverse() // 反转字节顺序
.ToList();
// 将每个字节转换为8位二进制字符串,不足8位时左边补0
string binaryString = string.Concat(
processedBytes.Select(b => Convert.ToString(b, 2).PadLeft(8, '0'))
);
// 反转二进制字符串中的所有位,并转换为布尔数组
return binaryString
.Reverse() // 反转所有位的顺序
.Take(numberOfPoints) // 取指定数量的位
.Select(c => c == '1') // 将字符'1'转换为true,其他转换为false
.ToArray();
}
else
{
return null;
}
}
///
/// 写入多个线圈操作
///
/// 从站地址
/// 起始地址
/// 写入的数据
public void WriteCoil(byte slaveAddress, ushort startAddress, bool[] data)
{
if (!_serialPort.IsOpen)
{
MessageBox.Show("串口没有链接,请先链接");
return;
}
List sendByteList = new List
{
slaveAddress, // 从站地址
0x0F, // 功能码:写多个线圈
};
// 添加起始地址(高字节在前,低字节在后)
sendByteList.AddRange(BitConverter.GetBytes(startAddress).Reverse());
// 添加线圈数量(高字节在前,低字节在后)
ushort coilCount = (ushort)data.Length;
sendByteList.AddRange(BitConverter.GetBytes(coilCount).Reverse());
// 计算所需字节数(每8个线圈占1字节)
int byteCount = (coilCount + 7) / 8;
sendByteList.Add((byte)byteCount);
// 将布尔数组转换为字节数组(每个位表示一个线圈状态)
byte[] coilBytes = ConvertBoolsToBytes(data);
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;
List responseData = new List();
while (attempt < maxRetries)
{
if (_serialPort.IsOpen)
{
// 每次重试前清空缓冲区
responseData.Clear();
// 清除输入缓冲区残留数据
_serialPort.DiscardInBuffer();
}
try
{
_serialPort.Write(sendByte, 0, sendByte.Length);
//使用时间去轮询查询数据
DateTime startTime = DateTime.Now;
while ((DateTime.Now - startTime).TotalMilliseconds < _serialPort.ReadTimeout)
{
int bytesToRead = _serialPort.BytesToRead;
if (bytesToRead > 0)
{
byte[] buffer = new byte[bytesToRead];
int bytesRead = _serialPort.Read(buffer, 0, bytesToRead);
responseData.AddRange(buffer.Take(bytesRead));
}
//延迟20毫秒,节约cpu资源
Thread.Sleep(20);
}
response = responseData.ToArray();
if (response.Length != 0)
{
break;
}
}
catch (Exception)
{
}
finally
{
attempt++;
}
}
try
{
//将操作的指令,插入数据库
string RequestStr = ByteArrayToString(sendByte);
string responseStr = ByteArrayToString(response);
ModbusLog modbusLog = new ModbusLog();
modbusLog.OperationType = "写线圈";
modbusLog.ResponseData = responseStr;
modbusLog.RequestData = RequestStr;
_modbusDbContext.Add(modbusLog);
_modbusDbContext.SaveChanges();
}
catch (Exception)
{
MessageBox.Show(ErrorCode.ErrorCode.DatabaseException.ToString());
}
//判断发送回来的数据是否正确
CheckData.CheckResponse(response);
}
///
/// 将布尔数组转换为字节数组(每个位表示一个线圈状态)
///
///
///
private static byte[] ConvertBoolsToBytes(bool[] data)
{
int byteCount = (data.Length + 7) / 8;
byte[] result = new byte[byteCount];
for (int i = 0; i < data.Length; i++)
{
if (data[i])
{
int byteIndex = i / 8;
int bitIndex = i % 8;
result[byteIndex] |= (byte)(1 << bitIndex);
}
}
return result;
}
///
/// 读寄存器操作
///
///
///
///
///
public ushort[] ReadRegisters(byte slaveAddress, ushort startAddress, ushort numberOfPoints)
{
ushort[] resultValue = null;
if (!_serialPort.IsOpen)
{
MessageBox.Show("串口没有链接,请先链接");
return resultValue;
}
var sendByteList = new List(8)
{
slaveAddress, // 从站地址
0x03, // 功能码:读保持寄存器
};
// 添加起始地址(高字节在前)
sendByteList.AddRange(BitConverter.GetBytes(startAddress).Reverse());
// 添加读取点数(高字节在前)
sendByteList.AddRange(BitConverter.GetBytes(numberOfPoints).Reverse());
//获取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;
List responseData = new List();
byte[] response = null;
while (attempt < maxRetries)
{
if (_serialPort.IsOpen)
{
// 每次重试前清空缓冲区
responseData.Clear();
// 清除输入缓冲区残留数据
_serialPort.DiscardInBuffer();
}
try
{
_serialPort.Write(sendByte, 0, sendByte.Length);
//使用时间去轮询查询数据
DateTime startTime = DateTime.Now;
while ((DateTime.Now - startTime).TotalMilliseconds < _serialPort.ReadTimeout)
{
int bytesToRead = _serialPort.BytesToRead;
if (bytesToRead > 0)
{
byte[] buffer = new byte[bytesToRead];
int bytesRead = _serialPort.Read(buffer, 0, bytesToRead);
responseData.AddRange(buffer.Take(bytesRead));
}
//延迟20毫秒,节约cpu资源
Thread.Sleep(20);
}
response = responseData.ToArray();
if (response.Length == 0)
{
continue;
}
resultValue = ParseRegistersresponse(sendByte, response, numberOfPoints);
return resultValue;
}
catch (Exception)
{
}
finally
{
attempt++;
}
}
try
{
//将操作失败的指令,插入数据库
string RequestStr = ByteArrayToString(sendByte);
ModbusLog modbusLog = new ModbusLog();
modbusLog.OperationType = "读寄存器";
modbusLog.ResponseData = "";
modbusLog.RequestData = RequestStr;
_modbusDbContext.Add(modbusLog);
_modbusDbContext.SaveChanges();
}
catch (Exception)
{
MessageBox.Show(ErrorCode.ErrorCode.DatabaseException.ToString());
}
return resultValue;
}
///
/// 解析读取到的操作
///
///
///
///
///
public ushort[] ParseRegistersresponse(byte[] sendByte, byte[] response, ushort numberOfPoints)
{
try
{
//将操作的指令,插入数据库
string RequestStr = ByteArrayToString(sendByte);
string responseStr = ByteArrayToString(response);
ModbusLog modbusLog = new ModbusLog();
modbusLog.OperationType = "读寄存器";
modbusLog.ResponseData = responseStr;
modbusLog.RequestData = RequestStr;
_modbusDbContext.Add(modbusLog);
_modbusDbContext.SaveChanges();
}
catch (Exception)
{
MessageBox.Show(ErrorCode.ErrorCode.DatabaseException.ToString());
}
//判断发送回来的数据是否正确
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;
}
// 计算字节数 (每个寄存器占2字节)
int byteCount = (data.Length) * 2;
// 预分配列表容量,避免多次扩容
var sendByteList = new List(9 + byteCount)
{
slaveAddress, // 从站地址
0x10, // 功能码:写多个保持寄存器
};
// 添加起始地址(高字节在前)
sendByteList.AddRange(BitConverter.GetBytes(startAddress).Reverse());
// 添加寄存器数量(高字节在前)
sendByteList.AddRange(BitConverter.GetBytes((ushort)data.Length).Reverse());
// 添加字节数
sendByteList.Add((byte)byteCount);
// 添加寄存器数据(每个寄存器2字节,高字节在前)
foreach (ushort value in data)
{
sendByteList.AddRange(BitConverter.GetBytes(value).Reverse());
}
//获取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;
List responseData = new List();
while (attempt < maxRetries)
{
if (_serialPort.IsOpen)
{
// 每次重试前清空缓冲区
responseData.Clear();
// 清除输入缓冲区残留数据
_serialPort.DiscardInBuffer();
}
try
{
_serialPort.Write(sendByte, 0, sendByte.Length);
//使用时间去轮询查询数据
DateTime startTime = DateTime.Now;
while ((DateTime.Now - startTime).TotalMilliseconds < _serialPort.ReadTimeout)
{
int bytesToRead = _serialPort.BytesToRead;
if (bytesToRead > 0)
{
byte[] buffer = new byte[bytesToRead];
int bytesRead = _serialPort.Read(buffer, 0, bytesToRead);
responseData.AddRange(buffer.Take(bytesRead));
}
//延迟20毫秒,节约cpu资源
Thread.Sleep(20);
}
response = responseData.ToArray();
if (response.Length != 0)
{
break;
}
}
catch (Exception)
{
}
finally
{
attempt++;
}
}
try
{
//将操作的指令,插入数据库
string RequestStr = ByteArrayToString(sendByte);
string responseStr = ByteArrayToString(response);
ModbusLog modbusLog = new ModbusLog();
modbusLog.OperationType = "写寄存器";
modbusLog.ResponseData = responseStr;
modbusLog.RequestData = RequestStr;
_modbusDbContext.Add(modbusLog);
_modbusDbContext.SaveChanges();
}
catch (Exception)
{
MessageBox.Show("0x15:数据库插入错误");
}
//判断发送回来的数据是否正确
CheckData.CheckResponse(response);
}
///
/// 将数组转换为字符串
///
///
///
static string ByteArrayToString(byte[] byteArray)
{
if (byteArray == null || byteArray.Length == 0)
return string.Empty;
StringBuilder sb = new 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();
}
}
}