From 85f81b9817dd5ea90e3b90024da1d8a495bf2fd5 Mon Sep 17 00:00:00 2001 From: zhangyongpan Date: Wed, 30 Jul 2025 20:13:13 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=8A=9F=E8=83=BD=E7=A0=81?= =?UTF-8?q?=EF=BC=8C=E5=B9=B6=E6=B7=BB=E5=8A=A0=E5=8F=AF=E4=BB=A5=E9=80=89?= =?UTF-8?q?=E6=8B=A9=E8=AF=BB=E5=86=99=E8=80=97=E6=97=B6=E6=97=B6=E9=97=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ModbusDemo/Device/ModbusRTU.cs | 141 ++++++++++++------ ModbusDemo/ErrorCode/ErrorCode.cs | 52 +++++++ .../Extension/EnumBindingSourceExtension.cs | 3 +- ModbusDemo/ModbusDemo.csproj | 5 + ModbusDemo/Model/ErrorCode.cs | 10 ++ ModbusDemo/Uitls/CheckData.cs | 8 +- ModbusDemo/VIew/SettingsUC.xaml | 30 +++- ModbusDemo/VIewModel/AttachUCViewModel.cs | 71 +++++---- ModbusDemo/VIewModel/SettingsUCViewModel.cs | 36 +++-- ModbusTest/UnitTest1.cs | 7 +- 10 files changed, 270 insertions(+), 93 deletions(-) create mode 100644 ModbusDemo/ErrorCode/ErrorCode.cs create mode 100644 ModbusDemo/Model/ErrorCode.cs diff --git a/ModbusDemo/Device/ModbusRTU.cs b/ModbusDemo/Device/ModbusRTU.cs index 6b45da5..ede4987 100644 --- a/ModbusDemo/Device/ModbusRTU.cs +++ b/ModbusDemo/Device/ModbusRTU.cs @@ -25,7 +25,7 @@ namespace ModbusDemo.Device { _serialPort = serialPort; _modbusDbContext = modbusDbContext; - _portAdapter = new SerialPortAdapter(serialPort); + //_portAdapter = new SerialPortAdapter(serialPort); } /// /// 用来发送读写线圈的指令 @@ -63,11 +63,20 @@ namespace ModbusDemo.Device int attempt = 0; byte[] response = null; List responseData = new List(); - _serialPort.Write(sendByte, 0, sendByte.Length); + 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) @@ -80,11 +89,12 @@ namespace ModbusDemo.Device responseData.AddRange(buffer.Take(bytesRead)); } //延迟20毫秒,节约cpu资源 - Task.Delay(20); + Thread.Sleep(20); } response = responseData.ToArray(); - if (response[0] == 0x00) + if (response.Length == 0) { + attempt++; continue; } resultValue = ParseCoilresponse(sendByte, response, numberOfPoints); @@ -93,6 +103,10 @@ namespace ModbusDemo.Device catch (Exception) { + + } + finally + { attempt++; } @@ -112,7 +126,7 @@ namespace ModbusDemo.Device catch (Exception) { - MessageBox.Show("0x15:数据库插入错误"); + MessageBox.Show(ErrorCode.ErrorCode.DatabaseException.ToString()); } return resultValue; @@ -132,17 +146,17 @@ namespace ModbusDemo.Device //将操作的指令,插入数据库 string RequestStr = ByteArrayToString(sendByte); string responseStr = ByteArrayToString(response); - ModbusLog m = new ModbusLog(); - m.OperationType = "读线圈"; - m.ResponseData = responseStr; - m.RequestData = RequestStr; - _modbusDbContext.Add(m); + ModbusLog modbusLog = new ModbusLog(); + modbusLog.OperationType = "读线圈"; + modbusLog.ResponseData = responseStr; + modbusLog.RequestData = RequestStr; + _modbusDbContext.Add(modbusLog); _modbusDbContext.SaveChanges(); } catch (Exception) { - MessageBox.Show("0x15:数据库插入错误"); + MessageBox.Show(ErrorCode.ErrorCode.DatabaseException.ToString()); } @@ -222,11 +236,19 @@ namespace ModbusDemo.Device int attempt = 0; byte[] response = null; List responseData = new List(); - _serialPort.Write(sendByte, 0, sendByte.Length); + 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) @@ -239,10 +261,10 @@ namespace ModbusDemo.Device responseData.AddRange(buffer.Take(bytesRead)); } //延迟20毫秒,节约cpu资源 - Task.Delay(20); + Thread.Sleep(20); } response = responseData.ToArray(); - if (response[0] != 0x00) + if (response.Length != 0) { break; } @@ -250,6 +272,10 @@ namespace ModbusDemo.Device catch (Exception) { + + } + finally + { attempt++; } @@ -260,16 +286,16 @@ namespace ModbusDemo.Device //将操作的指令,插入数据库 string RequestStr = ByteArrayToString(sendByte); string responseStr = ByteArrayToString(response); - ModbusLog m = new ModbusLog(); - m.OperationType = "写线圈"; - m.ResponseData = responseStr; - m.RequestData = RequestStr; - _modbusDbContext.Add(m); + ModbusLog modbusLog = new ModbusLog(); + modbusLog.OperationType = "写线圈"; + modbusLog.ResponseData = responseStr; + modbusLog.RequestData = RequestStr; + _modbusDbContext.Add(modbusLog); _modbusDbContext.SaveChanges(); } catch (Exception) { - MessageBox.Show("0x15:数据库插入错误"); + MessageBox.Show(ErrorCode.ErrorCode.DatabaseException.ToString()); } //判断发送回来的数据是否正确 @@ -334,12 +360,18 @@ namespace ModbusDemo.Device int attempt = 0; List responseData = new List(); byte[] response = null; - _serialPort.Write(sendByte, 0, sendByte.Length); 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) @@ -352,10 +384,10 @@ namespace ModbusDemo.Device responseData.AddRange(buffer.Take(bytesRead)); } //延迟20毫秒,节约cpu资源 - Task.Delay(20); + Thread.Sleep(20); } response = responseData.ToArray(); - if (response[0] == 0x00) + if (response.Length == 0) { continue; } @@ -365,26 +397,31 @@ namespace ModbusDemo.Device catch (Exception) { + + } + finally + { attempt++; } } - //将操作失败的指令,插入数据库 - string RequestStr = ByteArrayToString(sendByte); + try { - ModbusLog m = new ModbusLog(); - m.OperationType = "读寄存器"; - m.ResponseData = ""; - m.RequestData = RequestStr; - _modbusDbContext.Add(m); + //将操作失败的指令,插入数据库 + string RequestStr = ByteArrayToString(sendByte); + ModbusLog modbusLog = new ModbusLog(); + modbusLog.OperationType = "读寄存器"; + modbusLog.ResponseData = ""; + modbusLog.RequestData = RequestStr; + _modbusDbContext.Add(modbusLog); _modbusDbContext.SaveChanges(); } catch (Exception) { - MessageBox.Show("0x15:数据库插入错误"); + MessageBox.Show(ErrorCode.ErrorCode.DatabaseException.ToString()); } return resultValue; @@ -406,16 +443,16 @@ namespace ModbusDemo.Device //将操作的指令,插入数据库 string RequestStr = ByteArrayToString(sendByte); string responseStr = ByteArrayToString(response); - ModbusLog m = new ModbusLog(); - m.OperationType = "读寄存器"; - m.ResponseData = responseStr; - m.RequestData = RequestStr; - _modbusDbContext.Add(m); + ModbusLog modbusLog = new ModbusLog(); + modbusLog.OperationType = "读寄存器"; + modbusLog.ResponseData = responseStr; + modbusLog.RequestData = RequestStr; + _modbusDbContext.Add(modbusLog); _modbusDbContext.SaveChanges(); } catch (Exception) { - MessageBox.Show("0x15:数据库插入错误"); + MessageBox.Show(ErrorCode.ErrorCode.DatabaseException.ToString()); } @@ -493,11 +530,19 @@ namespace ModbusDemo.Device int attempt = 0; byte[] response = null; List responseData = new List(); - _serialPort.Write(sendByte, 0, sendByte.Length); + 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) @@ -510,15 +555,19 @@ namespace ModbusDemo.Device responseData.AddRange(buffer.Take(bytesRead)); } //延迟20毫秒,节约cpu资源 - Task.Delay(20); + Thread.Sleep(20); } response = responseData.ToArray(); - if (response[0] != 0x00) + if (response.Length != 0) { break; } } catch (Exception) + { + + } + finally { attempt++; } @@ -528,11 +577,11 @@ namespace ModbusDemo.Device //将操作的指令,插入数据库 string RequestStr = ByteArrayToString(sendByte); string responseStr = ByteArrayToString(response); - ModbusLog m = new ModbusLog(); - m.OperationType = "写寄存器"; - m.ResponseData = responseStr; - m.RequestData = RequestStr; - _modbusDbContext.Add(m); + ModbusLog modbusLog = new ModbusLog(); + modbusLog.OperationType = "写寄存器"; + modbusLog.ResponseData = responseStr; + modbusLog.RequestData = RequestStr; + _modbusDbContext.Add(modbusLog); _modbusDbContext.SaveChanges(); } catch (Exception) diff --git a/ModbusDemo/ErrorCode/ErrorCode.cs b/ModbusDemo/ErrorCode/ErrorCode.cs new file mode 100644 index 0000000..0018412 --- /dev/null +++ b/ModbusDemo/ErrorCode/ErrorCode.cs @@ -0,0 +1,52 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ModbusDemo.ErrorCode +{ + public class ErrorCode + { + + // 静态列表:存储所有错误码 + public static readonly List AllErrors = new List(); + + // 错误码值 + public byte Code { get; } + + // 错误描述 + public string Description { get; } + + // 私有构造函数,只能通过静态方法创建错误码 + private ErrorCode(byte code, string description) + { + Code = code; + Description = description; + + // 自动添加到错误码列表 + AllErrors.Add(this); + } + + // 重写 ToString 方法 + public override string ToString() => $"Error 0x{Code:X2}: {Description}"; + + // 隐式转换为 byte + public static implicit operator byte(ErrorCode error) => error.Code; + + // 根据错误码值查找错误码 + public static ErrorCode FromByte(byte code) => AllErrors.FirstOrDefault(e => e.Code == code); + + // 定义所有错误码 + public static readonly ErrorCode FunctionCodeError = new ErrorCode(0x01, "功能码错误"); + public static readonly ErrorCode DomainStartAddressError = new ErrorCode(0x02, "域数据首地址错误"); + public static readonly ErrorCode DataContentError = new ErrorCode(0x03, "数据内容错误"); + public static readonly ErrorCode DataTypeError = new ErrorCode(0x10, "数据类型错误"); + public static readonly ErrorCode DomainDataCountError = new ErrorCode(0x11, "域数据个数错误"); + public static readonly ErrorCode DataCountMismatch = new ErrorCode(0x12, "数据个数与内部区域数据个数不符"); + public static readonly ErrorCode WriteOperationDenied = new ErrorCode(0x13, "不能进行写操作"); + public static readonly ErrorCode CrcCheckError = new ErrorCode(0x14, "CRC校验错误"); + public static readonly ErrorCode DatabaseException = new ErrorCode(0x15, "数据库异常"); + } + +} diff --git a/ModbusDemo/Extension/EnumBindingSourceExtension.cs b/ModbusDemo/Extension/EnumBindingSourceExtension.cs index 29320aa..fde4db8 100644 --- a/ModbusDemo/Extension/EnumBindingSourceExtension.cs +++ b/ModbusDemo/Extension/EnumBindingSourceExtension.cs @@ -4,7 +4,8 @@ using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Markup; - +using System; +using System.Reflection; namespace ModbusDemo.Extension { /// diff --git a/ModbusDemo/ModbusDemo.csproj b/ModbusDemo/ModbusDemo.csproj index 02d9860..841bdf7 100644 --- a/ModbusDemo/ModbusDemo.csproj +++ b/ModbusDemo/ModbusDemo.csproj @@ -9,6 +9,11 @@ ModbusRTU_64x64.ico + + + + + Always diff --git a/ModbusDemo/Model/ErrorCode.cs b/ModbusDemo/Model/ErrorCode.cs new file mode 100644 index 0000000..340c416 --- /dev/null +++ b/ModbusDemo/Model/ErrorCode.cs @@ -0,0 +1,10 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ModbusDemo.Model +{ + +} diff --git a/ModbusDemo/Uitls/CheckData.cs b/ModbusDemo/Uitls/CheckData.cs index 277b369..8c46307 100644 --- a/ModbusDemo/Uitls/CheckData.cs +++ b/ModbusDemo/Uitls/CheckData.cs @@ -4,7 +4,7 @@ using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; - +using ModbusDemo.ErrorCode; namespace ModbusDemo.Uitls { public static class CheckData @@ -32,7 +32,9 @@ namespace ModbusDemo.Uitls // 判断高4位是否等于8 if (highNibble == 8) { - MessageBox.Show($"数据错误:错误码为:0x{response[2]:X2}"); + var error = ErrorCode.ErrorCode.FromByte(response[2]); + + MessageBox.Show(error.ToString()); return false; } } @@ -40,7 +42,7 @@ namespace ModbusDemo.Uitls if (!CRCUitl.ValidateCRC(response)) { - MessageBox.Show("0x14:CRC校验错误"); + MessageBox.Show(ErrorCode.ErrorCode.CrcCheckError.ToString()); return false; } diff --git a/ModbusDemo/VIew/SettingsUC.xaml b/ModbusDemo/VIew/SettingsUC.xaml index 3fa057b..e00da87 100644 --- a/ModbusDemo/VIew/SettingsUC.xaml +++ b/ModbusDemo/VIew/SettingsUC.xaml @@ -31,7 +31,6 @@ - @@ -44,7 +43,8 @@ - + + @@ -131,6 +131,32 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/ModbusDemo/VIewModel/AttachUCViewModel.cs b/ModbusDemo/VIewModel/AttachUCViewModel.cs index 64ef628..c7ef208 100644 --- a/ModbusDemo/VIewModel/AttachUCViewModel.cs +++ b/ModbusDemo/VIewModel/AttachUCViewModel.cs @@ -50,7 +50,7 @@ namespace ModbusDemo.VIewModel } public AttachUCViewModel(SerialPort serialPort) { - ReadOddRegisterCmm = new DelegateCommand(ReadOddRegister); + ReadOddRegisterCmm = new DelegateCommand(ReadOddRegister2); _serialPort = serialPort; } /// @@ -124,18 +124,18 @@ namespace ModbusDemo.VIewModel ushort currentChunkSize = 100; int chunkIndex = i; - tasks[i] = Task.Run(async () => - { + + - ushort[] registers = await ReadRegisters(slaveId, startAddress, currentChunkSize); + ushort[] registers = ReadRegisters(slaveId, startAddress, currentChunkSize); Array.Copy(registers, 0, allRegisters, chunkIndex * chunkSize, currentChunkSize); - }); + } // 等待所有任务完成 - Task.WaitAll(tasks); + Time = (int)(DateTime.Now - startTime).TotalMilliseconds; StringBuilder result = new StringBuilder(); int count = 0; @@ -151,27 +151,25 @@ namespace ModbusDemo.VIewModel ReadResult = result.ToString(); } - - public async Task ReadRegisters(byte slaveAddress, ushort startAddress, ushort numberOfPoints) + 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]); + var sendByteList = new List(8) + { + slaveAddress, // 从站地址 + 0x03, // 功能码:读保持寄存器 + }; + + // 添加起始地址(高字节在前) + sendByteList.AddRange(BitConverter.GetBytes(startAddress).Reverse()); + + // 添加读取点数(高字节在前) + sendByteList.AddRange(BitConverter.GetBytes(numberOfPoints).Reverse()); //获取CRC校验码 byte[] getCRC = sendByteList.ToArray(); byte[] resultCRC = CRCUitl.CalculateCRC(getCRC, getCRC.Length); @@ -183,14 +181,15 @@ namespace ModbusDemo.VIewModel int attempt = 0; List responseData = new List(); byte[] response = null; - _serialPort.Write(sendByte, 0, sendByte.Length); while (attempt < maxRetries) { + // 每次重试前清空缓冲区 + responseData.Clear(); + // 清除输入缓冲区残留数据 + _serialPort.DiscardInBuffer(); try { - - - + _serialPort.Write(sendByte, 0, sendByte.Length); //使用时间去轮询查询数据 DateTime startTime = DateTime.Now; while ((DateTime.Now - startTime).TotalMilliseconds < _serialPort.ReadTimeout) @@ -203,33 +202,42 @@ namespace ModbusDemo.VIewModel responseData.AddRange(buffer.Take(bytesRead)); } //延迟20毫秒,节约cpu资源 - await Task.Delay(20); + Thread.Sleep(20); } response = responseData.ToArray(); - if (response[0] == 0x00) + if (response.Length == 0) { continue; } resultValue = ParseRegistersresponse(sendByte, response, numberOfPoints); return resultValue; - } catch (Exception) { + + } + finally + { attempt++; } } - return resultValue; } - + /// + /// 解析读取到的操作 + /// + /// + /// + /// + /// public ushort[] ParseRegistersresponse(byte[] sendByte, byte[] response, ushort numberOfPoints) { + //判断发送回来的数据是否正确 @@ -249,7 +257,12 @@ namespace ModbusDemo.VIewModel { return null; } + + + + } + } } diff --git a/ModbusDemo/VIewModel/SettingsUCViewModel.cs b/ModbusDemo/VIewModel/SettingsUCViewModel.cs index 3fc5298..a267df1 100644 --- a/ModbusDemo/VIewModel/SettingsUCViewModel.cs +++ b/ModbusDemo/VIewModel/SettingsUCViewModel.cs @@ -14,7 +14,7 @@ using System.Windows; namespace ModbusDemo.VIewModel { - + class SettingsUCViewModel : BindableBase { //事件聚合器 @@ -22,9 +22,9 @@ namespace ModbusDemo.VIewModel //控制页面跳转 private readonly IRegionManager _regionManager; //断开连接的命令 - public DelegateCommand DisConnectionCmm { get; set; } + public DelegateCommand DisConnectionCmm { get; set; } //链接窗口的命令 - public DelegateCommand ConnectionCmm { get; set; } + public DelegateCommand ConnectionCmm { get; set; } //获取串口 private SerialPort _serialPort; @@ -93,9 +93,23 @@ namespace ModbusDemo.VIewModel } } + //设置读取和写入时间 + private string _timeOut = "200"; + + public string TimeOut + { + get { return _timeOut; } + set + { + _timeOut = value; + RaisePropertyChanged(); + } + } + + public SettingsUCViewModel() { - + } /// /// 从容器中获取创建的窗口 @@ -115,7 +129,7 @@ namespace ModbusDemo.VIewModel if (_serialPort.IsOpen) { _serialPort.Close(); - + } MessageBox.Show("连接已经断开"); } @@ -139,11 +153,11 @@ namespace ModbusDemo.VIewModel } _serialPort.StopBits = this.StopBits; _serialPort.DataBits = int.Parse(GetComboBoxItemValue(this.DataBits)); - + //读取超时时间 - _serialPort.ReadTimeout = 500; + _serialPort.ReadTimeout = int.Parse(GetComboBoxItemValue(this.TimeOut)); //写入超时时间 - _serialPort.WriteTimeout = 500; + _serialPort.WriteTimeout = int.Parse(GetComboBoxItemValue(this.TimeOut)); try { _serialPort.Open(); @@ -156,14 +170,14 @@ namespace ModbusDemo.VIewModel catch (Exception) { - MessageBox.Show("串口已经链接或者断开,请重新检查在尝试","warning",MessageBoxButton.OK,MessageBoxImage.Warning); + MessageBox.Show("串口已经链接或者断开,请重新检查在尝试", "warning", MessageBoxButton.OK, MessageBoxImage.Warning); } } else { MessageBox.Show("当前串口已经连接,请先断开,再连接"); } - + } /// /// 处理下拉框的选择信息 @@ -177,7 +191,7 @@ namespace ModbusDemo.VIewModel string cleanText = displayText.Replace("System.Windows.Controls.ComboBoxItem: ", ""); return cleanText; } - + } } diff --git a/ModbusTest/UnitTest1.cs b/ModbusTest/UnitTest1.cs index ac0286d..66b1e27 100644 --- a/ModbusTest/UnitTest1.cs +++ b/ModbusTest/UnitTest1.cs @@ -1,3 +1,5 @@ +//using ModbusDemo.Enum; + namespace ModbusTest { public class Tests @@ -10,7 +12,10 @@ namespace ModbusTest [Test] public void Test1() { - Assert.Pass(); + byte[] byteStream = new byte[] { 0x01, 0x81, 0x01, 0x10, 0x00, 0x00, 0xAB, 0x17 }; + byte errorCodeByte = byteStream[2]; // 0x01 + //ErrorCode errorCode = (ErrorCode)errorCodeByte; // תΪ ErrorCode.FunctionCodeError + //string message = errorCode.GetMessage(); } } } \ No newline at end of file