浏览代码

重构代码

dev-2
永攀 张 1周前
父节点
当前提交
9399645edf
共有 6 个文件被更改,包括 388 次插入463 次删除
  1. +298
    -404
      ModbusDemo/Device/ModbusRTU.cs
  2. +4
    -0
      ModbusDemo/Uitls/CheckData.cs
  3. +2
    -1
      ModbusDemo/VIewModel/CoilUCViewModel.cs
  4. +5
    -3
      ModbusDemo/VIewModel/RegisterUCViewModel.cs
  5. +27
    -3
      ModbusDemo/VIewModel/SettingsUCViewModel.cs
  6. +52
    -52
      ModbusTest/ModbusRTUTest.cs

+ 298
- 404
ModbusDemo/Device/ModbusRTU.cs 查看文件

@@ -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);
}
}
}

+ 4
- 0
ModbusDemo/Uitls/CheckData.cs 查看文件

@@ -38,6 +38,10 @@ namespace ModbusDemo.Uitls
return false;
}
}
else
{
return false;
}

if (!CRCUitl.ValidateCRC(response))


+ 2
- 1
ModbusDemo/VIewModel/CoilUCViewModel.cs 查看文件

@@ -1,6 +1,7 @@
using ModbusDemo.Device;
using ModbusDemo.Event;
using ModbusDemo.Model;
using ModbusDemo.Uitls;
using Prism.Commands;
using Prism.Events;
using Prism.Mvvm;
@@ -319,7 +320,7 @@ namespace ModbusDemo.VIewModel
return _modbusDbContext.ModbusLog
.Where(log => log.OperationType == "读线圈" || log.OperationType == "写线圈")
.OrderByDescending(log => log.Time) // 按时间倒序,最新的在前
.Take(20) // 只取前20条记录
.Take(30) // 只取前20条记录
.ToList(); // 执行查询
}
}


+ 5
- 3
ModbusDemo/VIewModel/RegisterUCViewModel.cs 查看文件

@@ -191,6 +191,8 @@ namespace ModbusDemo.VIewModel
RaisePropertyChanged(nameof(SerialPortInfo));
}




/// <summary>
@@ -232,7 +234,7 @@ namespace ModbusDemo.VIewModel
return;
}
string temp = "";
int count =0;
int count = 0;
foreach (var item in result)
{
count++;
@@ -313,7 +315,7 @@ namespace ModbusDemo.VIewModel
IsEnable = true;
}
});



@@ -327,7 +329,7 @@ namespace ModbusDemo.VIewModel
return _modbusDbContext.ModbusLog
.Where(log => log.OperationType == "读寄存器" || log.OperationType == "写寄存器")
.OrderByDescending(log => log.Time) // 按时间倒序,最新的在前
.Take(20) // 只取前20条记录
.Take(30) // 只取前20条记录
.ToList(); // 执行查询
}
}


+ 27
- 3
ModbusDemo/VIewModel/SettingsUCViewModel.cs 查看文件

@@ -11,12 +11,18 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;

using System.Timers;
using Timer = System.Timers.Timer;
using ModbusDemo.Device;
namespace ModbusDemo.VIewModel
{

class SettingsUCViewModel : BindableBase
{
//
private ModbusRTU _modbusRTU;
//定时器
private readonly Timer _checkTimer;
//事件聚合器
private readonly IEventAggregator _eventAggregator;
//控制页面跳转
@@ -115,19 +121,37 @@ namespace ModbusDemo.VIewModel
/// 从容器中获取创建的窗口
/// </summary>
/// <param name="serialPort"></param>
public SettingsUCViewModel(SerialPort serialPort, IRegionManager regionManager, IEventAggregator eventAggregator)
public SettingsUCViewModel(SerialPort serialPort, IRegionManager regionManager, IEventAggregator eventAggregator, ModbusRTU modbusRTU)
{
_serialPort = serialPort;
ConnectionCmm = new DelegateCommand(Connection);
DisConnectionCmm = new DelegateCommand(DisConnection);
_regionManager = regionManager;
_eventAggregator = eventAggregator;
_modbusRTU = modbusRTU;
_checkTimer = new Timer(1000); // 每秒检查一次
_checkTimer.Elapsed += CheckPortStatus;
_checkTimer.AutoReset = true;

}
private void CheckPortStatus(object sender, ElapsedEventArgs e)
{
bool isConnected = SerialPort.GetPortNames().Contains(PortName);
byte[] bytes = new byte[] { 0x01, 0x01, 0x01, 0x2C, 0x00, 0x01, 0x3D, 0xFF };
byte[] response = _modbusRTU.SendMessage(bytes);
if (!isConnected || response ==null)
{
_checkTimer.Stop();
_serialPort.Close();
MessageBox.Show("当前连接已经断开");
}
}

private void DisConnection()
{
if (_serialPort.IsOpen)
{
_checkTimer.Stop();
_serialPort.Close();

}
@@ -165,7 +189,7 @@ namespace ModbusDemo.VIewModel
_eventAggregator.GetEvent<SerialPortSettingsChangedEvent>().Publish();
MessageBox.Show("串口链接成功");
_regionManager.Regions["ModbusRegion"].RequestNavigate("CoilUC");
_checkTimer.Start();
}
catch (Exception)
{


+ 52
- 52
ModbusTest/ModbusRTUTest.cs 查看文件

@@ -143,58 +143,58 @@ namespace ModbusTest
CollectionAssert.AreEqual(data, expected);
}

/// <summary>
/// 解析线圈的返回值
/// </summary>
[Test]
public void ParseCoilresponse1()
{
var data = new bool[5];
Array.Fill(data, true);
byte[] response = new byte[] { 0x01, 0x01, 0x01, 0x7f, 0x10, 0x68 };
bool[] bools = _modbusRtu.ParseCoilresponse(new byte[] { }, response, 5);
CollectionAssert.AreEqual(data, bools);
}
[Test]
public void ParseCoilresponse2()
{
var data = new bool[5];
Array.Fill(data, false);
byte[] response = new byte[] { 0x01, 0x01, 0x01, 0x00, 0x51, 0x88 };
bool[] result = _modbusRtu.ParseCoilresponse(new byte[] { }, response, 5);
CollectionAssert.AreEqual(data, result);
}
/// <summary>
/// 解析寄存器的返回值
/// </summary>
[Test]
public void ParseRegistersresponse1()
{
ushort[] data = new ushort[5];
Array.Fill(data, (ushort)1);
byte[] response = new byte[] {0x01, 0x03, 0x0a, 0x00, 0x01, 0x00, 0x01, 0x00,
0x01, 0x00, 0x01, 0x00, 0x01, 0x94, 0x26 };
ushort[] result = _modbusRtu.ParseRegistersresponse(new byte[] {},response, 5);
CollectionAssert.AreEqual(data, result);
}
[Test]
public void ParseRegistersresponse2()
{
ushort[] data = new ushort[2];
Array.Fill(data, (ushort)10);
byte[] response = new byte[] {0x01, 0x03, 0x04, 0x00,0x0a, 0x00, 0x0a, 0x5a, 0x36 };
ushort[] result = _modbusRtu.ParseRegistersresponse(new byte[] { }, response, 2);
CollectionAssert.AreEqual(data, result);
}
///// <summary>
///// 解析线圈的返回值
///// </summary>
//[Test]
//public void ParseCoilresponse1()
//{
// var data = new bool[5];
// Array.Fill(data, true);
// byte[] response = new byte[] { 0x01, 0x01, 0x01, 0x7f, 0x10, 0x68 };
// bool[] bools = _modbusRtu.ParseCoilresponse(new byte[] { }, response, 5);
// CollectionAssert.AreEqual(data, bools);
//}
//[Test]
//public void ParseCoilresponse2()
//{
// var data = new bool[5];
// Array.Fill(data, false);
// byte[] response = new byte[] { 0x01, 0x01, 0x01, 0x00, 0x51, 0x88 };
// bool[] result = _modbusRtu.ParseCoilresponse(new byte[] { }, response, 5);
// CollectionAssert.AreEqual(data, result);
//}
///// <summary>
///// 解析寄存器的返回值
///// </summary>
//[Test]
//public void ParseRegistersresponse1()
//{
// ushort[] data = new ushort[5];
// Array.Fill(data, (ushort)1);
// byte[] response = new byte[] {0x01, 0x03, 0x0a, 0x00, 0x01, 0x00, 0x01, 0x00,
// 0x01, 0x00, 0x01, 0x00, 0x01, 0x94, 0x26 };
// ushort[] result = _modbusRtu.ParseRegistersresponse(new byte[] {},response, 5);
// CollectionAssert.AreEqual(data, result);
//}
//[Test]
//public void ParseRegistersresponse2()
//{
// ushort[] data = new ushort[2];
// Array.Fill(data, (ushort)10);
// byte[] response = new byte[] {0x01, 0x03, 0x04, 0x00,0x0a, 0x00, 0x0a, 0x5a, 0x36 };
// ushort[] result = _modbusRtu.ParseRegistersresponse(new byte[] { }, response, 2);
// CollectionAssert.AreEqual(data, result);
//}

}
}

正在加载...
取消
保存