|
|
@@ -9,6 +9,7 @@ using System.Text; |
|
|
|
using System.Threading; |
|
|
|
using System.Threading.Tasks; |
|
|
|
using System.Windows; |
|
|
|
using static System.Runtime.InteropServices.JavaScript.JSType; |
|
|
|
|
|
|
|
namespace ModbusDemo.Device |
|
|
|
{ |
|
|
@@ -27,6 +28,7 @@ namespace ModbusDemo.Device |
|
|
|
_modbusDbContext = modbusDbContext; |
|
|
|
//_portAdapter = new SerialPortAdapter(serialPort); |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary> |
|
|
|
/// 用来发送读写线圈的指令 |
|
|
|
/// </summary> |
|
|
@@ -43,130 +45,17 @@ namespace ModbusDemo.Device |
|
|
|
MessageBox.Show("串口没有链接,请先链接"); |
|
|
|
return resultValue; |
|
|
|
} |
|
|
|
List<byte> sendByteList = new List<byte> |
|
|
|
{ |
|
|
|
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<byte> responseData = new List<byte>(); |
|
|
|
|
|
|
|
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; |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary> |
|
|
|
/// 用来解析返回的数据 |
|
|
|
/// </summary> |
|
|
|
/// <param name="sendByte"></param> |
|
|
|
/// <param name="response"></param> |
|
|
|
/// <param name="numberOfPoints"></param> |
|
|
|
/// <returns></returns> |
|
|
|
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)) |
|
|
|
//构建发送报文 |
|
|
|
byte[] sendMessage = ConstructMessage<ushort>(0x01, slaveAddress, startAddress, numberOfPoints,ProcessReadData); |
|
|
|
//发送报文 |
|
|
|
byte[] responseMessage = SendMessage(sendMessage); |
|
|
|
//判断发送过来的字节是否正确 |
|
|
|
if (CheckResponse(responseMessage)) |
|
|
|
{ |
|
|
|
// 移除前3个字节和后2个校验码字节,并反转数组 |
|
|
|
var processedBytes = response |
|
|
|
var processedBytes = responseMessage |
|
|
|
.Skip(3) // 移除前3个字节 |
|
|
|
.Take(response.Count() - 5) // 保留中间部分 (总长度-5) |
|
|
|
.Take(responseMessage.Count() - 5) // 保留中间部分 (总长度-5) |
|
|
|
.Reverse() // 反转字节顺序 |
|
|
|
.ToList(); |
|
|
|
|
|
|
@@ -176,20 +65,17 @@ namespace ModbusDemo.Device |
|
|
|
); |
|
|
|
|
|
|
|
// 反转二进制字符串中的所有位,并转换为布尔数组 |
|
|
|
return binaryString |
|
|
|
.Reverse() // 反转所有位的顺序 |
|
|
|
.Take(numberOfPoints) // 取指定数量的位 |
|
|
|
.Select(c => c == '1') // 将字符'1'转换为true,其他转换为false |
|
|
|
.ToArray(); |
|
|
|
resultValue = binaryString |
|
|
|
.Reverse() // 反转所有位的顺序 |
|
|
|
.Take(numberOfPoints) // 取指定数量的位 |
|
|
|
.Select(c => c == '1') // 将字符'1'转换为true,其他转换为false |
|
|
|
.ToArray(); |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
return null; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//将数据插入到数据库 |
|
|
|
InsertDatabase(sendMessage, responseMessage,"读线圈"); |
|
|
|
return resultValue; |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary> |
|
|
|
/// 写入多个线圈操作 |
|
|
|
/// </summary> |
|
|
@@ -203,328 +89,191 @@ namespace ModbusDemo.Device |
|
|
|
MessageBox.Show("串口没有链接,请先链接"); |
|
|
|
return; |
|
|
|
} |
|
|
|
List<byte> sendByteList = new List<byte> |
|
|
|
{ |
|
|
|
slaveAddress, // 从站地址 |
|
|
|
0x0F, // 功能码:写多个线圈 |
|
|
|
}; |
|
|
|
|
|
|
|
// 添加起始地址(高字节在前,低字节在后) |
|
|
|
sendByteList.AddRange(BitConverter.GetBytes(startAddress).Reverse()); |
|
|
|
//构建报文 |
|
|
|
byte[] sendMessage = ConstructMessage<bool[]>(0x0F, slaveAddress, startAddress, data, ProcessCoilData); |
|
|
|
//发送报文 |
|
|
|
byte[] responseMessage = SendMessage(sendMessage); |
|
|
|
|
|
|
|
// 添加线圈数量(高字节在前,低字节在后) |
|
|
|
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<byte> responseData = new List<byte>(); |
|
|
|
//将数据插入到数据库 |
|
|
|
InsertDatabase(sendMessage, responseMessage,"写线圈"); |
|
|
|
//判断发送回来的数据是否正确 |
|
|
|
CheckResponse(responseMessage); |
|
|
|
} |
|
|
|
|
|
|
|
while (attempt < maxRetries) |
|
|
|
|
|
|
|
public ushort[] ReadRegisters(byte slaveAddress, ushort startAddress, ushort numberOfPoints) |
|
|
|
{ |
|
|
|
ushort[] resultValue = null; |
|
|
|
if (!_serialPort.IsOpen) |
|
|
|
{ |
|
|
|
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) |
|
|
|
{ |
|
|
|
|
|
|
|
MessageBox.Show("串口没有链接,请先链接"); |
|
|
|
return resultValue; |
|
|
|
} |
|
|
|
//构建发送报文 |
|
|
|
byte[] sendMessage = ConstructMessage<ushort>(0x03, slaveAddress, startAddress, numberOfPoints, ProcessReadData); |
|
|
|
//发送报文 |
|
|
|
byte[] responseMessage = SendMessage(sendMessage); |
|
|
|
|
|
|
|
} |
|
|
|
finally |
|
|
|
//将数据插入到数据库 |
|
|
|
InsertDatabase(sendMessage, responseMessage,"读寄存器"); |
|
|
|
//判断发送回来的数据是否正确 |
|
|
|
if (CheckResponse(responseMessage)) |
|
|
|
{ |
|
|
|
ushort[] registers = new ushort[numberOfPoints]; |
|
|
|
for (int i = 0; i < numberOfPoints; i++) |
|
|
|
{ |
|
|
|
attempt++; |
|
|
|
int pos = 3 + i * 2; |
|
|
|
// 大端序转换 (高位在前) |
|
|
|
registers[i] = (ushort)((responseMessage[pos] << 8) | responseMessage[pos + 1]); |
|
|
|
} |
|
|
|
|
|
|
|
resultValue = registers; |
|
|
|
} |
|
|
|
|
|
|
|
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); |
|
|
|
return resultValue; |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary> |
|
|
|
/// 将布尔数组转换为字节数组(每个位表示一个线圈状态) |
|
|
|
/// 写寄存器 |
|
|
|
/// </summary> |
|
|
|
/// <param name="slaveAddress"></param> |
|
|
|
/// <param name="startAddress"></param> |
|
|
|
/// <param name="data"></param> |
|
|
|
/// <returns></returns> |
|
|
|
private static byte[] ConvertBoolsToBytes(bool[] data) |
|
|
|
public void WriteRegisters(byte slaveAddress, ushort startAddress, ushort[] data) |
|
|
|
{ |
|
|
|
int byteCount = (data.Length + 7) / 8; |
|
|
|
byte[] result = new byte[byteCount]; |
|
|
|
|
|
|
|
for (int i = 0; i < data.Length; i++) |
|
|
|
if (!_serialPort.IsOpen) |
|
|
|
{ |
|
|
|
if (data[i]) |
|
|
|
{ |
|
|
|
int byteIndex = i / 8; |
|
|
|
int bitIndex = i % 8; |
|
|
|
result[byteIndex] |= (byte)(1 << bitIndex); |
|
|
|
} |
|
|
|
MessageBox.Show("串口没有链接,请先链接"); |
|
|
|
return; |
|
|
|
} |
|
|
|
//构建报文 |
|
|
|
byte[] sendMessage = ConstructMessage<ushort[]>(0x10, slaveAddress, startAddress, data, ProcessRegisterData); |
|
|
|
//发送报文 |
|
|
|
byte[] responseMessage = SendMessage(sendMessage); |
|
|
|
|
|
|
|
//将数据插入到数据库 |
|
|
|
InsertDatabase(sendMessage, responseMessage, "写寄存器"); |
|
|
|
|
|
|
|
|
|
|
|
//判断发送回来的数据是否正确 |
|
|
|
CheckResponse(responseMessage); |
|
|
|
|
|
|
|
|
|
|
|
return result; |
|
|
|
} |
|
|
|
|
|
|
|
/// |
|
|
|
// 定义委托处理不同数据类型的转换 |
|
|
|
private delegate void DataProcessor<T>(List<byte> sendByteList, T data); |
|
|
|
/// <summary> |
|
|
|
/// 读寄存器操作 |
|
|
|
/// 生成发送报文 |
|
|
|
/// </summary> |
|
|
|
/// <typeparam name="T"></typeparam> |
|
|
|
/// <param name="Function"></param> |
|
|
|
/// <param name="slaveAddress"></param> |
|
|
|
/// <param name="startAddress"></param> |
|
|
|
/// <param name="numberOfPoints"></param> |
|
|
|
/// <param name="data"></param> |
|
|
|
/// <param name="processor"></param> |
|
|
|
/// <returns></returns> |
|
|
|
public ushort[] ReadRegisters(byte slaveAddress, ushort startAddress, ushort numberOfPoints) |
|
|
|
private byte[] ConstructMessage<T>(byte Function, byte slaveAddress, ushort startAddress, T data, DataProcessor<T> processor) |
|
|
|
{ |
|
|
|
ushort[] resultValue = null; |
|
|
|
if (!_serialPort.IsOpen) |
|
|
|
{ |
|
|
|
MessageBox.Show("串口没有链接,请先链接"); |
|
|
|
return resultValue; |
|
|
|
} |
|
|
|
var sendByteList = new List<byte>(8) |
|
|
|
byte[] resultValue = null; |
|
|
|
var sendByteList = new List<byte> |
|
|
|
{ |
|
|
|
slaveAddress, // 从站地址 |
|
|
|
0x03, // 功能码:读保持寄存器 |
|
|
|
slaveAddress, // 从站地址 |
|
|
|
Function // 功能码 |
|
|
|
}; |
|
|
|
|
|
|
|
// 添加起始地址(高字节在前) |
|
|
|
// 添加起始地址(高字节在前,低字节在后) |
|
|
|
sendByteList.AddRange(BitConverter.GetBytes(startAddress).Reverse()); |
|
|
|
// 调用处理器处理具体数据 |
|
|
|
processor(sendByteList, data); |
|
|
|
|
|
|
|
// 添加读取点数(高字节在前) |
|
|
|
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<byte> responseData = new List<byte>(); |
|
|
|
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()); |
|
|
|
} |
|
|
|
|
|
|
|
resultValue = sendByteList.ToArray(); |
|
|
|
return resultValue; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
/// <summary> |
|
|
|
/// 解析读取到的操作 |
|
|
|
/// 处理写入线圈数据 |
|
|
|
/// </summary> |
|
|
|
/// <param name="sendByte"></param> |
|
|
|
/// <param name="response"></param> |
|
|
|
/// <param name="numberOfPoints"></param> |
|
|
|
/// <returns></returns> |
|
|
|
public ushort[] ParseRegistersresponse(byte[] sendByte, byte[] response, ushort numberOfPoints) |
|
|
|
/// <param name="sendByteList"></param> |
|
|
|
/// <param name="data"></param> |
|
|
|
private void ProcessCoilData(List<byte> sendByteList, bool[] data) |
|
|
|
{ |
|
|
|
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()); |
|
|
|
// 添加线圈数量(高字节在前,低字节在后) |
|
|
|
ushort coilCount = (ushort)data.Length; |
|
|
|
sendByteList.AddRange(BitConverter.GetBytes(coilCount).Reverse()); |
|
|
|
|
|
|
|
} |
|
|
|
// 计算所需字节数(每8个线圈占1字节) |
|
|
|
int byteCount = (coilCount + 7) / 8; |
|
|
|
sendByteList.Add((byte)byteCount); |
|
|
|
|
|
|
|
// 将布尔数组转换为字节数组(每个位表示一个线圈状态) |
|
|
|
byte[] result = new byte[byteCount]; |
|
|
|
|
|
|
|
//判断发送回来的数据是否正确 |
|
|
|
if (CheckData.CheckResponse(response)) |
|
|
|
for (int i = 0; i < data.Length; i++) |
|
|
|
{ |
|
|
|
ushort[] registers = new ushort[numberOfPoints]; |
|
|
|
for (int i = 0; i < numberOfPoints; i++) |
|
|
|
if (data[i]) |
|
|
|
{ |
|
|
|
int pos = 3 + i * 2; |
|
|
|
// 大端序转换 (高位在前) |
|
|
|
registers[i] = (ushort)((response[pos] << 8) | response[pos + 1]); |
|
|
|
int byteIndex = i / 8; |
|
|
|
int bitIndex = i % 8; |
|
|
|
result[byteIndex] |= (byte)(1 << bitIndex); |
|
|
|
} |
|
|
|
|
|
|
|
return registers; |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
return null; |
|
|
|
} |
|
|
|
sendByteList.AddRange(result); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary> |
|
|
|
/// 处理读取几个线圈或者寄存器的转换 |
|
|
|
/// </summary> |
|
|
|
/// <param name="sendByteList"></param> |
|
|
|
/// <param name="data"></param> |
|
|
|
private void ProcessReadData(List<byte> sendByteList, ushort data) |
|
|
|
{ |
|
|
|
//设置读取几个线圈的高位和低位 |
|
|
|
sendByteList.AddRange(BitConverter.GetBytes(data).Reverse()); |
|
|
|
} |
|
|
|
/// <summary> |
|
|
|
/// 写寄存器 |
|
|
|
/// 解析写寄存器的值 |
|
|
|
/// </summary> |
|
|
|
/// <param name="slaveAddress"></param> |
|
|
|
/// <param name="startAddress"></param> |
|
|
|
/// <param name="sendByteList"></param> |
|
|
|
/// <param name="data"></param> |
|
|
|
public void WriteRegisters(byte slaveAddress, ushort startAddress, ushort[] data) |
|
|
|
private void ProcessRegisterData(List<byte> sendByteList, ushort[] data) |
|
|
|
{ |
|
|
|
if (!_serialPort.IsOpen) |
|
|
|
{ |
|
|
|
MessageBox.Show("串口没有链接,请先链接"); |
|
|
|
return; |
|
|
|
} |
|
|
|
// 计算字节数 (每个寄存器占2字节) |
|
|
|
int byteCount = (data.Length) * 2; |
|
|
|
|
|
|
|
// 预分配列表容量,避免多次扩容 |
|
|
|
var sendByteList = new List<byte>(9 + byteCount) |
|
|
|
{ |
|
|
|
slaveAddress, // 从站地址 |
|
|
|
0x10, // 功能码:写多个保持寄存器 |
|
|
|
}; |
|
|
|
|
|
|
|
// 添加起始地址(高字节在前) |
|
|
|
sendByteList.AddRange(BitConverter.GetBytes(startAddress).Reverse()); |
|
|
|
|
|
|
|
// 添加寄存器数量(高字节在前) |
|
|
|
// 添加寄存器数量 |
|
|
|
sendByteList.AddRange(BitConverter.GetBytes((ushort)data.Length).Reverse()); |
|
|
|
|
|
|
|
// 添加字节数 |
|
|
|
// 计算字节数 |
|
|
|
int byteCount = data.Length * 2; |
|
|
|
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(); |
|
|
|
|
|
|
|
/// <summary> |
|
|
|
/// 读寄存器操作 |
|
|
|
/// </summary> |
|
|
|
/// <param name="slaveAddress"></param> |
|
|
|
/// <param name="startAddress"></param> |
|
|
|
/// <param name="numberOfPoints"></param> |
|
|
|
/// <returns></returns> |
|
|
|
|
|
|
|
/// <summary> |
|
|
|
/// 将构建好的报文发送出去 |
|
|
|
/// </summary> |
|
|
|
/// <param name="sendByte"></param> |
|
|
|
/// <returns></returns> |
|
|
|
public byte[] SendMessage(byte[] sendByte) |
|
|
|
{ |
|
|
|
int maxRetries = 3; // 最大重试次数 |
|
|
|
int attempt = 0; |
|
|
|
byte[] response = null; |
|
|
@@ -557,27 +306,41 @@ namespace ModbusDemo.Device |
|
|
|
Thread.Sleep(20); |
|
|
|
} |
|
|
|
response = responseData.ToArray(); |
|
|
|
if (response.Length != 0) |
|
|
|
if (response.Length == 0) |
|
|
|
{ |
|
|
|
break; |
|
|
|
attempt++; |
|
|
|
continue; |
|
|
|
} |
|
|
|
return response; |
|
|
|
} |
|
|
|
catch (Exception) |
|
|
|
{ |
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
finally |
|
|
|
{ |
|
|
|
attempt++; |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
return null; |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary> |
|
|
|
/// 将这次报文信息插入到数据库中 |
|
|
|
/// </summary> |
|
|
|
/// <param name="sendByte"></param> |
|
|
|
/// <param name="response"></param> |
|
|
|
private void InsertDatabase(byte[] sendByte, byte[] response, string operationType) |
|
|
|
{ |
|
|
|
try |
|
|
|
{ |
|
|
|
//将操作的指令,插入数据库 |
|
|
|
string RequestStr = ByteArrayToString(sendByte); |
|
|
|
string responseStr = ByteArrayToString(response); |
|
|
|
ModbusLog modbusLog = new ModbusLog(); |
|
|
|
modbusLog.OperationType = "写寄存器"; |
|
|
|
modbusLog.OperationType = operationType; |
|
|
|
modbusLog.ResponseData = responseStr; |
|
|
|
modbusLog.RequestData = RequestStr; |
|
|
|
_modbusDbContext.Add(modbusLog); |
|
|
@@ -586,17 +349,9 @@ namespace ModbusDemo.Device |
|
|
|
catch (Exception) |
|
|
|
{ |
|
|
|
|
|
|
|
MessageBox.Show("0x15:数据库插入错误"); |
|
|
|
MessageBox.Show(ErrorCode.ErrorCode.DatabaseException.ToString()); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//判断发送回来的数据是否正确 |
|
|
|
CheckData.CheckResponse(response); |
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
/// <summary> |
|
|
|
/// 将数组转换为字符串 |
|
|
|
/// </summary> |
|
|
@@ -623,5 +378,144 @@ namespace ModbusDemo.Device |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// <summary> |
|
|
|
/// 检测返回的数据格式是否正确 |
|
|
|
/// </summary> |
|
|
|
/// <param name="response"></param> |
|
|
|
/// <returns></returns> |
|
|
|
public static bool CheckResponse(byte[] response) |
|
|
|
{ |
|
|
|
if (response == null || response.Length == 0) |
|
|
|
{ |
|
|
|
MessageBox.Show("返回数据为空", "error", MessageBoxButton.OK, MessageBoxImage.Error); |
|
|
|
return false; |
|
|
|
} |
|
|
|
// 检查数组长度是否足够 |
|
|
|
if (response.Length > 3) |
|
|
|
{ |
|
|
|
byte secondByte = response[1]; // 获取第二个字节(索引为1) |
|
|
|
|
|
|
|
// 使用掩码0xF0获取高4位,并右移4位 |
|
|
|
int highNibble = (secondByte & 0xF0) >> 4; |
|
|
|
|
|
|
|
// 判断高4位是否等于8 |
|
|
|
if (highNibble == 8) |
|
|
|
{ |
|
|
|
var error = ErrorCode.ErrorCode.FromByte(response[2]); |
|
|
|
|
|
|
|
MessageBox.Show(error.ToString()); |
|
|
|
return false; |
|
|
|
} |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
return false; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (!ValidateCRC(response)) |
|
|
|
{ |
|
|
|
MessageBox.Show(ErrorCode.ErrorCode.CrcCheckError.ToString()); |
|
|
|
return false; |
|
|
|
} |
|
|
|
|
|
|
|
return true; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
//查表法计算CRC |
|
|
|
private static readonly byte[] aucCRCHi = { |
|
|
|
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, |
|
|
|
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, |
|
|
|
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, |
|
|
|
0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, |
|
|
|
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, |
|
|
|
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, |
|
|
|
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, |
|
|
|
0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, |
|
|
|
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, |
|
|
|
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, |
|
|
|
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, |
|
|
|
0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, |
|
|
|
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, |
|
|
|
0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, |
|
|
|
0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, |
|
|
|
0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, |
|
|
|
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, |
|
|
|
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, |
|
|
|
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, |
|
|
|
0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, |
|
|
|
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, |
|
|
|
0x00, 0xC1, 0x81, 0x40 |
|
|
|
}; |
|
|
|
private static readonly byte[] aucCRCLo = { |
|
|
|
0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06, 0x07, 0xC7, |
|
|
|
0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD, 0x0F, 0xCF, 0xCE, 0x0E, |
|
|
|
0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09, 0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, |
|
|
|
0x1B, 0xDB, 0xDA, 0x1A, 0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC, |
|
|
|
0x14, 0xD4, 0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3, |
|
|
|
0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3, 0xF2, 0x32, |
|
|
|
0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4, 0x3C, 0xFC, 0xFD, 0x3D, |
|
|
|
0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A, 0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, |
|
|
|
0x28, 0xE8, 0xE9, 0x29, 0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF, |
|
|
|
0x2D, 0xED, 0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26, |
|
|
|
0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60, 0x61, 0xA1, |
|
|
|
0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67, 0xA5, 0x65, 0x64, 0xA4, |
|
|
|
0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F, 0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, |
|
|
|
0x69, 0xA9, 0xA8, 0x68, 0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA, |
|
|
|
0xBE, 0x7E, 0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5, |
|
|
|
0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71, 0x70, 0xB0, |
|
|
|
0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92, 0x96, 0x56, 0x57, 0x97, |
|
|
|
0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C, 0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, |
|
|
|
0x5A, 0x9A, 0x9B, 0x5B, 0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89, |
|
|
|
0x4B, 0x8B, 0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C, |
|
|
|
0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42, 0x43, 0x83, |
|
|
|
0x41, 0x81, 0x80, 0x40 |
|
|
|
}; |
|
|
|
|
|
|
|
/// <summary> |
|
|
|
/// CRC校验 |
|
|
|
/// </summary> |
|
|
|
/// <param name="pucFrame">字节数组</param> |
|
|
|
/// <param name="usLen">验证长度</param> |
|
|
|
/// <returns>2个字节</returns> |
|
|
|
public static byte[] CalculateCRC(byte[] pucFrame, int usLen) |
|
|
|
{ |
|
|
|
int i = 0; |
|
|
|
byte[] res = new byte[2] { 0xFF, 0xFF }; |
|
|
|
ushort iIndex; |
|
|
|
while (usLen-- > 0) |
|
|
|
{ |
|
|
|
iIndex = (ushort)(res[0] ^ pucFrame[i++]); |
|
|
|
res[0] = (byte)(res[1] ^ aucCRCHi[iIndex]); |
|
|
|
res[1] = aucCRCLo[iIndex]; |
|
|
|
} |
|
|
|
return res; |
|
|
|
} |
|
|
|
/// <summary> |
|
|
|
/// 用来计算CRC是否合理 |
|
|
|
/// </summary> |
|
|
|
/// <param name="fullFrame"></param> |
|
|
|
/// <returns></returns> |
|
|
|
public static bool ValidateCRC(byte[] fullFrame) |
|
|
|
{ |
|
|
|
// 报文必须至少包含2个字节的CRC校验码 |
|
|
|
if (fullFrame.Length < 2) |
|
|
|
return false; |
|
|
|
|
|
|
|
// 计算数据部分的长度(排除最后2个CRC字节) |
|
|
|
int dataLength = fullFrame.Length - 2; |
|
|
|
|
|
|
|
// 提取报文中包含的CRC校验码(传输顺序:低字节在前,高字节在后) |
|
|
|
byte receivedCrcLo = fullFrame[fullFrame.Length - 2]; |
|
|
|
byte receivedCrcHi = fullFrame[fullFrame.Length - 1]; |
|
|
|
|
|
|
|
// 计算数据部分的CRC(返回顺序:高字节在前,低字节在后) |
|
|
|
byte[] calculatedCrc = CalculateCRC(fullFrame, dataLength); |
|
|
|
|
|
|
|
// 比较计算出的CRC和接收到的CRC |
|
|
|
return (calculatedCrc[calculatedCrc.Length - 1] == receivedCrcHi) && |
|
|
|
(calculatedCrc[calculatedCrc.Length - 2] == receivedCrcLo); |
|
|
|
} |
|
|
|
} |
|
|
|
} |