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;
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;
byte[] response = null;
List responseData = new List();
_serialPort.Write(sendByte, 0, sendByte.Length);
while (attempt < maxRetries)
{
try
{
//使用时间去轮询查询数据
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资源
Task.Delay(20);
}
response = responseData.ToArray();
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;
List responseData = new List();
_serialPort.Write(sendByte, 0, sendByte.Length);
while (attempt < maxRetries)
{
try
{
//使用时间去轮询查询数据
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资源
Task.Delay(20);
}
response = responseData.ToArray();
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;
List responseData = new List();
byte[] response = null;
_serialPort.Write(sendByte, 0, sendByte.Length);
while (attempt < maxRetries)
{
try
{
//使用时间去轮询查询数据
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资源
Task.Delay(20);
}
response = responseData.ToArray();
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;
List responseData = new List();
_serialPort.Write(sendByte, 0, sendByte.Length);
while (attempt < maxRetries)
{
try
{
//使用时间去轮询查询数据
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资源
Task.Delay(20);
}
response = responseData.ToArray();
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();
//判断发送回来的数据是否正确
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();
}
}
}