From 9399645edfa8ef1cefd5adf833acca8723333aa9 Mon Sep 17 00:00:00 2001 From: zhangyongpan Date: Thu, 31 Jul 2025 20:14:23 +0800 Subject: [PATCH] =?UTF-8?q?=E9=87=8D=E6=9E=84=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ModbusDemo/Device/ModbusRTU.cs | 702 +++++++++----------- ModbusDemo/Uitls/CheckData.cs | 4 + ModbusDemo/VIewModel/CoilUCViewModel.cs | 3 +- ModbusDemo/VIewModel/RegisterUCViewModel.cs | 8 +- ModbusDemo/VIewModel/SettingsUCViewModel.cs | 30 +- ModbusTest/ModbusRTUTest.cs | 104 +-- 6 files changed, 388 insertions(+), 463 deletions(-) diff --git a/ModbusDemo/Device/ModbusRTU.cs b/ModbusDemo/Device/ModbusRTU.cs index a1e6e6a..749341b 100644 --- a/ModbusDemo/Device/ModbusRTU.cs +++ b/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); } + /// /// 用来发送读写线圈的指令 /// @@ -43,130 +45,17 @@ namespace ModbusDemo.Device 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)) + //构建发送报文 + byte[] sendMessage = ConstructMessage(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; } + /// /// 写入多个线圈操作 /// @@ -203,328 +89,191 @@ namespace ModbusDemo.Device MessageBox.Show("串口没有链接,请先链接"); return; } - List sendByteList = new List - { - slaveAddress, // 从站地址 - 0x0F, // 功能码:写多个线圈 - }; - - // 添加起始地址(高字节在前,低字节在后) - sendByteList.AddRange(BitConverter.GetBytes(startAddress).Reverse()); + //构建报文 + byte[] sendMessage = ConstructMessage(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 responseData = new List(); + //将数据插入到数据库 + 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(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; } + /// - /// 将布尔数组转换为字节数组(每个位表示一个线圈状态) + /// 写寄存器 /// + /// + /// /// - /// - 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(0x10, slaveAddress, startAddress, data, ProcessRegisterData); + //发送报文 + byte[] responseMessage = SendMessage(sendMessage); + + //将数据插入到数据库 + InsertDatabase(sendMessage, responseMessage, "写寄存器"); + + + //判断发送回来的数据是否正确 + CheckResponse(responseMessage); + - return result; } + + /// + // 定义委托处理不同数据类型的转换 + private delegate void DataProcessor(List sendByteList, T data); /// - /// 读寄存器操作 + /// 生成发送报文 /// + /// + /// /// /// - /// + /// + /// /// - public ushort[] ReadRegisters(byte slaveAddress, ushort startAddress, ushort numberOfPoints) + private byte[] ConstructMessage(byte Function, byte slaveAddress, ushort startAddress, T data, DataProcessor processor) { - ushort[] resultValue = null; - if (!_serialPort.IsOpen) - { - MessageBox.Show("串口没有链接,请先链接"); - return resultValue; - } - var sendByteList = new List(8) + byte[] resultValue = null; + var sendByteList = new List { - 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 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()); - } - + resultValue = sendByteList.ToArray(); return resultValue; - - - } + /// - /// 解析读取到的操作 + /// 处理写入线圈数据 /// - /// - /// - /// - /// - public ushort[] ParseRegistersresponse(byte[] sendByte, byte[] response, ushort numberOfPoints) + /// + /// + private void ProcessCoilData(List 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); + } - - - + /// + /// 处理读取几个线圈或者寄存器的转换 + /// + /// + /// + private void ProcessReadData(List sendByteList, ushort data) + { + //设置读取几个线圈的高位和低位 + sendByteList.AddRange(BitConverter.GetBytes(data).Reverse()); } /// - /// 写寄存器 + /// 解析写寄存器的值 /// - /// - /// + /// /// - public void WriteRegisters(byte slaveAddress, ushort startAddress, ushort[] data) + private void ProcessRegisterData(List sendByteList, 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()); - // 添加字节数 + // 计算字节数 + 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(); - + /// + /// 读寄存器操作 + /// + /// + /// + /// + /// + /// + /// 将构建好的报文发送出去 + /// + /// + /// + 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; + } + + /// + /// 将这次报文信息插入到数据库中 + /// + /// + /// + 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); - - } - /// /// 将数组转换为字符串 /// @@ -623,5 +378,144 @@ namespace ModbusDemo.Device } + /// + /// 检测返回的数据格式是否正确 + /// + /// + /// + 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 + }; + + /// + /// CRC校验 + /// + /// 字节数组 + /// 验证长度 + /// 2个字节 + 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; + } + /// + /// 用来计算CRC是否合理 + /// + /// + /// + 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); + } } } diff --git a/ModbusDemo/Uitls/CheckData.cs b/ModbusDemo/Uitls/CheckData.cs index 2a20de3..62eed01 100644 --- a/ModbusDemo/Uitls/CheckData.cs +++ b/ModbusDemo/Uitls/CheckData.cs @@ -38,6 +38,10 @@ namespace ModbusDemo.Uitls return false; } } + else + { + return false; + } if (!CRCUitl.ValidateCRC(response)) diff --git a/ModbusDemo/VIewModel/CoilUCViewModel.cs b/ModbusDemo/VIewModel/CoilUCViewModel.cs index 29683ac..dec2f86 100644 --- a/ModbusDemo/VIewModel/CoilUCViewModel.cs +++ b/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(); // 执行查询 } } diff --git a/ModbusDemo/VIewModel/RegisterUCViewModel.cs b/ModbusDemo/VIewModel/RegisterUCViewModel.cs index 8180eb6..e513d6a 100644 --- a/ModbusDemo/VIewModel/RegisterUCViewModel.cs +++ b/ModbusDemo/VIewModel/RegisterUCViewModel.cs @@ -191,6 +191,8 @@ namespace ModbusDemo.VIewModel RaisePropertyChanged(nameof(SerialPortInfo)); } + + /// @@ -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(); // 执行查询 } } diff --git a/ModbusDemo/VIewModel/SettingsUCViewModel.cs b/ModbusDemo/VIewModel/SettingsUCViewModel.cs index a267df1..ed2e50d 100644 --- a/ModbusDemo/VIewModel/SettingsUCViewModel.cs +++ b/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 /// 从容器中获取创建的窗口 /// /// - 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().Publish(); MessageBox.Show("串口链接成功"); _regionManager.Regions["ModbusRegion"].RequestNavigate("CoilUC"); - + _checkTimer.Start(); } catch (Exception) { diff --git a/ModbusTest/ModbusRTUTest.cs b/ModbusTest/ModbusRTUTest.cs index 1d6c982..92b8eab 100644 --- a/ModbusTest/ModbusRTUTest.cs +++ b/ModbusTest/ModbusRTUTest.cs @@ -143,58 +143,58 @@ namespace ModbusTest CollectionAssert.AreEqual(data, expected); } - /// - /// 解析线圈的返回值 - /// - [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); - } - - /// - /// 解析寄存器的返回值 - /// - [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); - } + ///// + ///// 解析线圈的返回值 + ///// + //[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); + //} + + ///// + ///// 解析寄存器的返回值 + ///// + //[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); + //} } }