diff --git a/ModbusDemo/App.xaml.cs b/ModbusDemo/App.xaml.cs index 6dd09c6..b40db92 100644 --- a/ModbusDemo/App.xaml.cs +++ b/ModbusDemo/App.xaml.cs @@ -50,7 +50,7 @@ namespace ModbusDemo //将读线圈注册 containerRegistry.Register(); //附加功能如何读取单数寄存器; - //containerRegistry.RegisterForNavigation(); + containerRegistry.RegisterForNavigation(); // 1. 加载配置文件 diff --git a/ModbusDemo/Device/IModbusRTU.cs b/ModbusDemo/Device/IModbusRTU.cs index 7d5e750..adc20cc 100644 --- a/ModbusDemo/Device/IModbusRTU.cs +++ b/ModbusDemo/Device/IModbusRTU.cs @@ -6,17 +6,17 @@ using System.Threading.Tasks; namespace ModbusDemo.Device { - public interface IModbusRTU + public interface IModbusRTU:IDisposable { public bool[] ReadCoil(byte slaveAddress, ushort startAddress, ushort numberOfPoints); - public void WriteCoil(byte slaveAddress, ushort startAddress, bool[] data); + public bool WriteCoil(byte slaveAddress, ushort startAddress, bool[] data); - public ushort[] ReadRegisters(byte slaveAddress, ushort startAddress, ushort numberOfPoints); + public ushort[] ReadRegister(byte slaveAddress, ushort startAddress, ushort numberOfPoints); - public void WriteRegisters(byte slaveAddress, ushort startAddress, ushort[] data); + public bool WriteRegister(byte slaveAddress, ushort startAddress, ushort[] data); } } diff --git a/ModbusDemo/Device/ModbusRTU.cs b/ModbusDemo/Device/ModbusRTU.cs index 749341b..00aa0b7 100644 --- a/ModbusDemo/Device/ModbusRTU.cs +++ b/ModbusDemo/Device/ModbusRTU.cs @@ -5,16 +5,19 @@ using System; using System.Collections.Generic; using System.IO.Ports; using System.Linq; +using System.Runtime.InteropServices; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows; -using static System.Runtime.InteropServices.JavaScript.JSType; + namespace ModbusDemo.Device { public class ModbusRTU : IModbusRTU { + // 标记是否已释放资源 + private bool _disposed = false; //串口 private SerialPort _serialPort; //用于操作数据库 @@ -46,7 +49,7 @@ namespace ModbusDemo.Device return resultValue; } //构建发送报文 - byte[] sendMessage = ConstructMessage(0x01, slaveAddress, startAddress, numberOfPoints,ProcessReadData); + byte[] sendMessage = ConstructMessage(0x01, slaveAddress, startAddress, numberOfPoints, ProcessReadData); //发送报文 byte[] responseMessage = SendMessage(sendMessage); //判断发送过来的字节是否正确 @@ -72,7 +75,7 @@ namespace ModbusDemo.Device .ToArray(); } //将数据插入到数据库 - InsertDatabase(sendMessage, responseMessage,"读线圈"); + InsertDatabase(sendMessage, responseMessage, "读线圈"); return resultValue; } @@ -82,12 +85,12 @@ namespace ModbusDemo.Device /// 从站地址 /// 起始地址 /// 写入的数据 - public void WriteCoil(byte slaveAddress, ushort startAddress, bool[] data) + public bool WriteCoil(byte slaveAddress, ushort startAddress, bool[] data) { if (!_serialPort.IsOpen) { MessageBox.Show("串口没有链接,请先链接"); - return; + return false; } //构建报文 byte[] sendMessage = ConstructMessage(0x0F, slaveAddress, startAddress, data, ProcessCoilData); @@ -95,13 +98,13 @@ namespace ModbusDemo.Device byte[] responseMessage = SendMessage(sendMessage); //将数据插入到数据库 - InsertDatabase(sendMessage, responseMessage,"写线圈"); + InsertDatabase(sendMessage, responseMessage, "写线圈"); //判断发送回来的数据是否正确 - CheckResponse(responseMessage); + return CheckResponse(responseMessage); } - - public ushort[] ReadRegisters(byte slaveAddress, ushort startAddress, ushort numberOfPoints) + + public ushort[] ReadRegister(byte slaveAddress, ushort startAddress, ushort numberOfPoints) { ushort[] resultValue = null; if (!_serialPort.IsOpen) @@ -115,7 +118,7 @@ namespace ModbusDemo.Device byte[] responseMessage = SendMessage(sendMessage); //将数据插入到数据库 - InsertDatabase(sendMessage, responseMessage,"读寄存器"); + InsertDatabase(sendMessage, responseMessage, "读寄存器"); //判断发送回来的数据是否正确 if (CheckResponse(responseMessage)) { @@ -139,12 +142,12 @@ namespace ModbusDemo.Device /// /// /// - public void WriteRegisters(byte slaveAddress, ushort startAddress, ushort[] data) + public bool WriteRegister(byte slaveAddress, ushort startAddress, ushort[] data) { if (!_serialPort.IsOpen) { MessageBox.Show("串口没有链接,请先链接"); - return; + return false; } //构建报文 byte[] sendMessage = ConstructMessage(0x10, slaveAddress, startAddress, data, ProcessRegisterData); @@ -154,9 +157,9 @@ namespace ModbusDemo.Device //将数据插入到数据库 InsertDatabase(sendMessage, responseMessage, "写寄存器"); - + //判断发送回来的数据是否正确 - CheckResponse(responseMessage); + return CheckResponse(responseMessage); } @@ -190,7 +193,7 @@ namespace ModbusDemo.Device //获取CRC校验码 byte[] getCRC = sendByteList.ToArray(); - byte[] resultCRC = CRCUitl.CalculateCRC(getCRC, getCRC.Length); + byte[] resultCRC = CalculateCRC(getCRC, getCRC.Length); sendByteList.Add(resultCRC[0]); sendByteList.Add(resultCRC[1]); @@ -259,13 +262,8 @@ namespace ModbusDemo.Device } } - /// - /// 读寄存器操作 - /// - /// - /// - /// - /// + // 自定义锁 + private static readonly object _Lock = new object(); /// /// 将构建好的报文发送出去 @@ -274,6 +272,7 @@ namespace ModbusDemo.Device /// public byte[] SendMessage(byte[] sendByte) { + int maxRetries = 3; // 最大重试次数 int attempt = 0; byte[] response = null; @@ -281,48 +280,49 @@ namespace ModbusDemo.Device while (attempt < maxRetries) { - if (_serialPort.IsOpen) + lock (_Lock) { - // 每次重试前清空缓冲区 - responseData.Clear(); - // 清除输入缓冲区残留数据 - _serialPort.DiscardInBuffer(); - } - try - { - _serialPort.Write(sendByte, 0, sendByte.Length); - //使用时间去轮询查询数据 - DateTime startTime = DateTime.Now; - while ((DateTime.Now - startTime).TotalMilliseconds < _serialPort.ReadTimeout) + if (_serialPort.IsOpen) + { + // 每次重试前清空缓冲区 + responseData.Clear(); + // 清除输入缓冲区残留数据 + _serialPort.DiscardInBuffer(); + } + try { - int bytesToRead = _serialPort.BytesToRead; - if (bytesToRead > 0) + _serialPort.Write(sendByte, 0, sendByte.Length); + //使用时间去轮询查询数据 + DateTime startTime = DateTime.Now; + while ((DateTime.Now - startTime).TotalMilliseconds < _serialPort.ReadTimeout) { - byte[] buffer = new byte[bytesToRead]; - int bytesRead = _serialPort.Read(buffer, 0, bytesToRead); - responseData.AddRange(buffer.Take(bytesRead)); + int bytesToRead = _serialPort.BytesToRead; + if (bytesToRead > 0) + { + byte[] buffer = new byte[bytesToRead]; + int bytesRead = _serialPort.Read(buffer, 0, bytesToRead); + responseData.AddRange(buffer.Take(bytesRead)); + } + //延迟20毫秒,节约cpu资源 + Task.Delay(20).Wait(); } - //延迟20毫秒,节约cpu资源 - Thread.Sleep(20); + response = responseData.ToArray(); + if (response.Length == 0) + { + attempt++; + continue; + } + return response; } - response = responseData.ToArray(); - if (response.Length == 0) + catch (Exception) { - attempt++; - continue; } - return response; - } - catch (Exception) - { - + finally + { + attempt++; + } } - finally - { - attempt++; - } - } return null; } @@ -517,5 +517,44 @@ namespace ModbusDemo.Device return (calculatedCrc[calculatedCrc.Length - 1] == receivedCrcHi) && (calculatedCrc[calculatedCrc.Length - 2] == receivedCrcLo); } + + + /// + /// 释放资源 + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + /// + /// 真实调用的方法 + /// + /// + protected virtual void Dispose(bool disposing) + { + if (_disposed) + return; // 避免重复释放 + + // 释放托管资源(仅在手动调用Dispose时执行) + if (disposing) + { + + //_serialPort是单例的,不要手动释放 + + // 释放数据库上下文是瞬态的需要手动释放 + if (_modbusDbContext != null) + { + _modbusDbContext.Dispose(); + _modbusDbContext = null; + } + } + + + + + _disposed = true; // 标记为已释放 + } + } } diff --git a/ModbusDemo/Extension/EnumBindingSourceExtension.cs b/ModbusDemo/Extension/EnumBindingSourceExtension.cs index fde4db8..90ef204 100644 --- a/ModbusDemo/Extension/EnumBindingSourceExtension.cs +++ b/ModbusDemo/Extension/EnumBindingSourceExtension.cs @@ -13,6 +13,7 @@ namespace ModbusDemo.Extension /// class EnumBindingSourceExtension : MarkupExtension { + //枚举字段 private Type _enumType; public Type EnumType @@ -40,7 +41,13 @@ namespace ModbusDemo.Extension { EnumType = enumType; } - + /// + /// 将枚举转换为数组,返回出去 + /// 可以绑定可空枚举 + /// + /// xmal上下文 + /// + /// public override object ProvideValue(IServiceProvider serviceProvider) { if (_enumType == null) diff --git a/ModbusDemo/ModbusDemo.csproj b/ModbusDemo/ModbusDemo.csproj index 2db4493..9a89d30 100644 --- a/ModbusDemo/ModbusDemo.csproj +++ b/ModbusDemo/ModbusDemo.csproj @@ -14,6 +14,8 @@ + + diff --git a/ModbusDemo/Model/ModbusDbContext.cs b/ModbusDemo/Model/ModbusDbContext.cs index 08dd423..ede44ef 100644 --- a/ModbusDemo/Model/ModbusDbContext.cs +++ b/ModbusDemo/Model/ModbusDbContext.cs @@ -12,7 +12,12 @@ namespace ModbusDemo.Model /// public class ModbusDbContext:DbContext { - + + public ModbusDbContext() + { + + } + public ModbusDbContext(DbContextOptions options) :base(options) { diff --git a/ModbusDemo/Uitls/CheckInputUitl.cs b/ModbusDemo/Uitls/CheckInputUitl.cs new file mode 100644 index 0000000..8f2b5e0 --- /dev/null +++ b/ModbusDemo/Uitls/CheckInputUitl.cs @@ -0,0 +1,82 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; + +namespace ModbusDemo.Uitls +{ + public class CheckInputUitl + { + /// + /// 检测输入的数据是否合理 + /// + /// + /// + /// + /// + public static (bool success, byte slaveAddress, ushort startAddress, ushort numberOfPoints) ValidateReadParameters( + string slaveAddressStr, string startAddressStr, string numberOfPointsStr) + { + if (!byte.TryParse(slaveAddressStr, out byte slaveAddress)) + { + ShowMessage("SlaveAddress 格式无效"); + return (false, 0, 0, 0); + } + + if (!ushort.TryParse(startAddressStr, out ushort startAddress)) + { + ShowMessage("StartAddress 格式无效"); + return (false, 0, 0, 0); + } + + if (!ushort.TryParse(numberOfPointsStr, out ushort numberOfPoints)) + { + ShowMessage("NumberOfPoints 格式无效"); + return (false, 0, 0, 0); + } + + return (true, slaveAddress, startAddress, numberOfPoints); + } + /// + /// 检测写的数据是否合理 + /// + /// + /// + /// + /// + public static (bool success, byte slaveAddress, ushort startAddress) ValidateWriteParameters( + string slaveAddressStr, string startAddressStr, string WriteData) + { + + if (!byte.TryParse(slaveAddressStr, out byte slaveAddress)) + { + ShowMessage("SlaveAddress 格式无效"); + return (false, 0, 0); + } + + if (!ushort.TryParse(startAddressStr, out ushort startAddress)) + { + ShowMessage("StartAddress 格式无效"); + return (false, 0, 0); + } + + if (string.IsNullOrEmpty(WriteData)) + { + ShowMessage("WriteData 格式无效"); + return (false, 0, 0); + } + + return (true, slaveAddress, startAddress); + } + + + + private static void ShowMessage(string message, string title = "warn", + MessageBoxImage image = MessageBoxImage.Warning) + { + MessageBox.Show(message, title, MessageBoxButton.OK, image); + } + } +} diff --git a/ModbusDemo/Uitls/DatabaseHelper.cs b/ModbusDemo/Uitls/DatabaseHelper.cs new file mode 100644 index 0000000..b4a68e8 --- /dev/null +++ b/ModbusDemo/Uitls/DatabaseHelper.cs @@ -0,0 +1,27 @@ +using ModbusDemo.Model; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ModbusDemo.Uitls +{ + public class DatabaseHelper + { + + //private readonly ModbusDbContext _modbusDbContext; + //public DatabaseHelper(ModbusDbContext modbusDbContext) + //{ + // _modbusDbContext = modbusDbContext; + //} + //public static List GetOperateCoil() + //{ + // return _modbusDbContext.ModbusLog + // .Where(log => log.OperationType == "读线圈" || log.OperationType == "写线圈") + // .OrderByDescending(log => log.Time) // 按时间倒序,最新的在前 + // .Take(30) // 只取前30条记录 + // .ToList(); // 执行查询 + //} + } +} diff --git a/ModbusDemo/VIew/AttachUC.xaml b/ModbusDemo/VIew/AttachUC.xaml index 30c8ae1..e5c0340 100644 --- a/ModbusDemo/VIew/AttachUC.xaml +++ b/ModbusDemo/VIew/AttachUC.xaml @@ -12,15 +12,22 @@ d:DesignHeight="450" d:DesignWidth="800" mc:Ignorable="d"> + + + + + - + + + + diff --git a/ModbusDemo/VIew/CoilUC.xaml b/ModbusDemo/VIew/CoilUC.xaml index 6ac9787..b7a7a11 100644 --- a/ModbusDemo/VIew/CoilUC.xaml +++ b/ModbusDemo/VIew/CoilUC.xaml @@ -143,7 +143,7 @@ Margin="0,-50,0,0" HorizontalAlignment="Center" VerticalAlignment="Center" - Text="1代表Ture,0代表flase"> + Text="1为true,0为flase"> diff --git a/ModbusDemo/VIewModel/AttachUCViewModel.cs b/ModbusDemo/VIewModel/AttachUCViewModel.cs index 38b993d..38ded53 100644 --- a/ModbusDemo/VIewModel/AttachUCViewModel.cs +++ b/ModbusDemo/VIewModel/AttachUCViewModel.cs @@ -17,7 +17,7 @@ namespace ModbusDemo.VIewModel { private SerialPort _serialPort; - public DelegateCommand ReadOddRegisterCmm { get; set; } + public DelegateCommand WriteMoreRegisterCmm { get; set; } //读取的结果 private string _readResult; @@ -30,18 +30,7 @@ namespace ModbusDemo.VIewModel RaisePropertyChanged(); } } - //显示操作的时间 - private int _time; - - public int Time - { - get { return _time; } - set - { - _time = value; - RaisePropertyChanged(); - } - } + public AttachUCViewModel() @@ -50,219 +39,14 @@ namespace ModbusDemo.VIewModel } public AttachUCViewModel(SerialPort serialPort) { - //ReadOddRegisterCmm = new DelegateCommand(ReadOddRegister); + WriteMoreRegisterCmm = new DelegateCommand(WriteMoreRegister); _serialPort = serialPort; } - /// - /// 读1000个寄存器 - /// - //private void ReadOddRegister() - //{ - // // 创建 Modbus RTU 主站 - // IModbusSerialMaster master = ModbusSerialMaster.CreateRtu(_serialPort); - // byte slaveId = 1; - // ushort totalRegisters = 1000; - // ushort chunkSize = 100; - // int numChunks = (int)Math.Ceiling((double)totalRegisters / chunkSize); - // ushort[] allRegisters = new ushort[totalRegisters]; - // Task[] tasks = new Task[numChunks]; - // DateTime startTime = DateTime.Now; - // for (int i = 0; i < numChunks; i++) - // { - // ushort startAddress = (ushort)(10300 + i * chunkSize); - // ushort currentChunkSize = 100; - - // int chunkIndex = i; - // tasks[i] = Task.Run(() => - // { - // try - // { - // ushort[] registers = master.ReadHoldingRegisters(slaveId, startAddress, currentChunkSize); - // Array.Copy(registers, 0, allRegisters, chunkIndex * chunkSize, currentChunkSize); - // } - // catch (Exception ex) - // { - // MessageBox.Show($"线程 {chunkIndex} 读取寄存器时出错: {ex.Message}"); - // } - // }); - // } - - // // 等待所有任务完成 - // Task.WaitAll(tasks); - // Time = (int)(DateTime.Now - startTime).TotalMilliseconds; - // StringBuilder result = new StringBuilder(); - // int count = 0; - // for (int i = 1; i < totalRegisters; i += 2) - // { - // result.Append(allRegisters[i].ToString() + " "); - // count++; - // if (count % 50 == 0) - // { - // result.AppendLine(); - // } - // } - // ReadResult = result.ToString(); - - //} - /// - /// 自己的类 - /// - private void ReadOddRegister2() + private void WriteMoreRegister() { - byte slaveId = 1; - ushort totalRegisters = 1000; - ushort chunkSize = 100; - int numChunks = (int)Math.Ceiling((double)totalRegisters / chunkSize); - - ushort[] allRegisters = new ushort[totalRegisters]; - Task[] tasks = new Task[numChunks]; - DateTime startTime = DateTime.Now; - for (int i = 0; i < numChunks; i++) - { - ushort startAddress = (ushort)(10300 + i * chunkSize); - ushort currentChunkSize = 100; - - int chunkIndex = i; - - - - ushort[] registers = ReadRegisters(slaveId, startAddress, currentChunkSize); - Array.Copy(registers, 0, allRegisters, chunkIndex * chunkSize, currentChunkSize); - - - - } - - // 等待所有任务完成 - Time = (int)(DateTime.Now - startTime).TotalMilliseconds; - StringBuilder result = new StringBuilder(); - int count = 0; - for (int i = 1; i < totalRegisters; i += 2) - { - result.Append(allRegisters[i].ToString() + " "); - count++; - if (count % 50 == 0) - { - result.AppendLine(); - } - } - ReadResult = result.ToString(); } - - public ushort[] ReadRegisters(byte slaveAddress, ushort startAddress, ushort numberOfPoints) - { - ushort[] resultValue = null; - if (!_serialPort.IsOpen) - { - MessageBox.Show("串口没有链接,请先链接"); - return resultValue; - } - 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); - 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) - { - // 每次重试前清空缓冲区 - 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++; - } - - } - return resultValue; - - - - } - /// - /// 解析读取到的操作 - /// - /// - /// - /// - /// - public ushort[] ParseRegistersresponse(byte[] sendByte, byte[] response, ushort numberOfPoints) - { - - - - //判断发送回来的数据是否正确 - if (CheckData.CheckResponse(response)) - { - ushort[] registers = new ushort[numberOfPoints]; - for (int i = 0; i < numberOfPoints; i++) - { - int pos = 3 + i * 2; - // 大端序转换 (高位在前) - registers[i] = (ushort)((response[pos] << 8) | response[pos + 1]); - } - - return registers; - } - else - { - return null; - } - - - - - } - } } diff --git a/ModbusDemo/VIewModel/CoilUCViewModel.cs b/ModbusDemo/VIewModel/CoilUCViewModel.cs index dec2f86..552b624 100644 --- a/ModbusDemo/VIewModel/CoilUCViewModel.cs +++ b/ModbusDemo/VIewModel/CoilUCViewModel.cs @@ -18,9 +18,10 @@ namespace ModbusDemo.VIewModel /// /// 这是CoilUC的VM用来支持数据绑定 /// - public class CoilUCViewModel : BindableBase + public class CoilUCViewModel : BindableBase,IDisposable { - + // 标记是否已释放资源 + private bool _disposed = false; //事件聚合器 private readonly IEventAggregator _eventAggregator; //定义数据库操作 @@ -205,33 +206,18 @@ namespace ModbusDemo.VIewModel /// private void ReadCoil() { - //将按钮设置为不可点击状态,并解析用户输入的信息 - IsEnable = false; - if (!byte.TryParse(SlaveAddress, out byte slaveAddressValue)) - { - MessageBox.Show("SlaveAddress 格式无效", "warning", MessageBoxButton.OK, MessageBoxImage.Warning); - IsEnable = true; - return; - } - - if (!ushort.TryParse(StartAddress, out ushort startAddressValue)) + //检测当前输入的信息是否正确 + var validation = CheckInputUitl.ValidateReadParameters(SlaveAddress, StartAddress, NumberOfPoints); + if (!validation.success) { - MessageBox.Show("StartAddress 格式无效", "warning", MessageBoxButton.OK, MessageBoxImage.Warning); - IsEnable = true; - return; - } - - if (!ushort.TryParse(NumberOfPoints, out ushort numberOfPointsValue)) - { - MessageBox.Show("NumberOfPoints 格式无效", "warning", MessageBoxButton.OK, MessageBoxImage.Warning); - IsEnable = true; return; } //使用子线程,防止ui线程卡顿 Task.Run(() => { - bool[] result = _modbusRTU.ReadCoil(slaveAddressValue, startAddressValue, numberOfPointsValue); + IsEnable = false; + bool[] result = _modbusRTU.ReadCoil(validation.slaveAddress, validation.startAddress, validation.numberOfPoints); if (result == null) { @@ -240,7 +226,6 @@ namespace ModbusDemo.VIewModel IsEnable = true; return; } - string temp = ""; int count = 0; foreach (var item in result) @@ -266,24 +251,10 @@ namespace ModbusDemo.VIewModel private void WriteCoil() { - //将按钮设置为不可点击状态,并解析用户输入的信息 - IsEnable = false; - if (!byte.TryParse(WriteSlaveAddress, out byte writeSlaveAddressValue)) - { - MessageBox.Show("WriteSlaveAddress 格式无效", "warning", MessageBoxButton.OK, MessageBoxImage.Warning); - IsEnable = true; - return; - } - if (!ushort.TryParse(WriteStartAddress, out ushort WriteStartAddressValue)) - { - MessageBox.Show("WriteStartAddress 格式无效", "warning", MessageBoxButton.OK, MessageBoxImage.Warning); - IsEnable = true; - return; - } - if (string.IsNullOrEmpty(WriteData)) + //检测当前输入的信息是否正确 + var validation = CheckInputUitl.ValidateWriteParameters(WriteSlaveAddress, WriteStartAddress,WriteData); + if (!validation.success) { - MessageBox.Show("WriteData 格式无效", "warning", MessageBoxButton.OK, MessageBoxImage.Warning); - IsEnable = true; return; } //去除字符串中的空格 @@ -294,19 +265,20 @@ namespace ModbusDemo.VIewModel //使用子线程执行,避免ui线程卡顿 Task.Run(() => { - _modbusRTU.WriteCoil(writeSlaveAddressValue, WriteStartAddressValue, data); + IsEnable = false; + bool issucceed = _modbusRTU.WriteCoil(validation.slaveAddress, validation.startAddress, data); ModbusLogList = GetOperateCoil(); - ModbusLog modbusLog = ModbusLogList.FirstOrDefault(); - if (modbusLog != null && modbusLog.ResponseData != string.Empty && modbusLog.ResponseData[3] !='8') + if (issucceed) { MessageBox.Show("写入成功"); - IsEnable = true; + } else { MessageBox.Show("写入失败", "error", MessageBoxButton.OK, MessageBoxImage.Error); - IsEnable = true; + } + IsEnable = true; }); } @@ -323,5 +295,48 @@ namespace ModbusDemo.VIewModel .Take(30) // 只取前20条记录 .ToList(); // 执行查询 } + + + /// + /// 释放资源 + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + /// + /// 真实调用的方法 + /// + /// + protected virtual void Dispose(bool disposing) + { + if (_disposed) + return; // 避免重复释放 + + // 释放托管资源(仅在手动调用Dispose时执行) + if (disposing) + { + + //_serialPort是单例的,不要手动释放 + + // 释放数据库上下文是瞬态的需要手动释放 + if (_modbusDbContext != null) + { + _modbusDbContext.Dispose(); + _modbusDbContext = null; + } + if (_modbusRTU != null) + { + _modbusRTU.Dispose(); + _modbusRTU = null; + } + } + + + + + _disposed = true; // 标记为已释放 + } } } diff --git a/ModbusDemo/VIewModel/RegisterUCViewModel.cs b/ModbusDemo/VIewModel/RegisterUCViewModel.cs index e513d6a..98c0c14 100644 --- a/ModbusDemo/VIewModel/RegisterUCViewModel.cs +++ b/ModbusDemo/VIewModel/RegisterUCViewModel.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; @@ -14,8 +15,10 @@ using System.Windows; namespace ModbusDemo.VIewModel { - class RegisterUCViewModel : BindableBase + class RegisterUCViewModel : BindableBase,IDisposable { + // 标记是否已释放资源 + private bool _disposed = false; //事件聚合器 private readonly IEventAggregator _eventAggregator; //定义数据库操作类 @@ -200,32 +203,16 @@ namespace ModbusDemo.VIewModel /// private void ReadRegister() { - //将按钮设置为不可点击状态,并解析用户输入的信息 - IsEnable = false; - if (!byte.TryParse(SlaveAddress, out byte slaveAddressValue)) + //检测当前输入的信息是否正确 + var validation = CheckInputUitl.ValidateReadParameters(SlaveAddress, StartAddress, NumberOfPoints); + if (!validation.success) { - MessageBox.Show("SlaveAddress 格式无效", "warning", MessageBoxButton.OK, MessageBoxImage.Warning); - IsEnable = true; - return; - } - - if (!ushort.TryParse(StartAddress, out ushort startAddressValue)) - { - MessageBox.Show("StartAddress 格式无效", "warning", MessageBoxButton.OK, MessageBoxImage.Warning); - IsEnable = true; - return; - } - - if (!ushort.TryParse(NumberOfPoints, out ushort numberOfPointsValue)) - { - MessageBox.Show("NumberOfPoints 格式无效", "warning", MessageBoxButton.OK, MessageBoxImage.Warning); - IsEnable = true; return; } //使用子线程进行读取,防止ui线程卡顿 Task.Run(() => - { - ushort[] result = _modbusRTU.ReadRegisters(slaveAddressValue, startAddressValue, numberOfPointsValue); + { IsEnable = false; + ushort[] result = _modbusRTU.ReadRegister(validation.slaveAddress, validation.startAddress, validation.numberOfPoints); if (result == null) { MessageBox.Show("读取失败", "error", MessageBoxButton.OK, MessageBoxImage.Error); @@ -257,31 +244,17 @@ namespace ModbusDemo.VIewModel private void WriteRegister() { - //将按钮设置为不可点击状态,并解析用户输入的信息 - IsEnable = false; - if (!byte.TryParse(WriteSlaveAddress, out byte writeSlaveAddressValue)) - { - MessageBox.Show("WriteSlaveAddress 格式无效", "warning", MessageBoxButton.OK, MessageBoxImage.Warning); - IsEnable = true; - return; - } - if (!ushort.TryParse(WriteStartAddress, out ushort WriteStartAddressValue)) + //检测当前输入的信息是否正确 + var validation = CheckInputUitl.ValidateWriteParameters(WriteSlaveAddress, WriteStartAddress, WriteData); + if (!validation.success) { - MessageBox.Show("WriteStartAddress 格式无效", "warning", MessageBoxButton.OK, MessageBoxImage.Warning); - IsEnable = true; - return; - } - if (string.IsNullOrEmpty(WriteData)) - { - MessageBox.Show("WriteData 格式无效", "warning", MessageBoxButton.OK, MessageBoxImage.Warning); - IsEnable = true; return; } string[] parts = WriteData.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); - // 3. 创建结果数组 + // 创建结果数组 ushort[] data = new ushort[parts.Length]; - // 4. 遍历并转换每个部分 + // 遍历并转换每个部分 for (int i = 0; i < parts.Length; i++) { // 尝试解析为ushort @@ -293,7 +266,7 @@ namespace ModbusDemo.VIewModel { MessageBox.Show($"无法将 '{parts[i]}' 转换为 ushort 类型"); - IsEnable = true; + return; } } @@ -301,10 +274,11 @@ namespace ModbusDemo.VIewModel //使用子线程来去执行操作,防止ui线程卡顿 Task.Run(() => { - _modbusRTU.WriteRegisters(writeSlaveAddressValue, WriteStartAddressValue, data); + IsEnable = false; + bool issucceed = _modbusRTU.WriteRegister(validation.slaveAddress, validation.startAddress, data); ModbusLogList = GetOperateRegister(); - ModbusLog modbusLog = ModbusLogList.FirstOrDefault(); - if (modbusLog != null && modbusLog.ResponseData != string.Empty && modbusLog.ResponseData[3] != '8') + + if (issucceed) { MessageBox.Show("写入成功"); IsEnable = true; @@ -332,5 +306,47 @@ namespace ModbusDemo.VIewModel .Take(30) // 只取前20条记录 .ToList(); // 执行查询 } + + /// + /// 释放资源 + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + /// + /// 真实调用的方法 + /// + /// + protected virtual void Dispose(bool disposing) + { + if (_disposed) + return; // 避免重复释放 + + // 释放托管资源(仅在手动调用Dispose时执行) + if (disposing) + { + + //_serialPort是单例的,不要手动释放 + + // 释放数据库上下文是瞬态的需要手动释放 + if (_modbusDbContext != null) + { + _modbusDbContext.Dispose(); + _modbusDbContext = null; + } + if (_modbusRTU != null) + { + _modbusRTU.Dispose(); + _modbusRTU = null; + } + } + + + + + _disposed = true; // 标记为已释放 + } } } diff --git a/ModbusDemo/VIewModel/SettingsUCViewModel.cs b/ModbusDemo/VIewModel/SettingsUCViewModel.cs index ed2e50d..14aa7bd 100644 --- a/ModbusDemo/VIewModel/SettingsUCViewModel.cs +++ b/ModbusDemo/VIewModel/SettingsUCViewModel.cs @@ -129,17 +129,17 @@ namespace ModbusDemo.VIewModel _regionManager = regionManager; _eventAggregator = eventAggregator; _modbusRTU = modbusRTU; - _checkTimer = new Timer(1000); // 每秒检查一次 + _checkTimer = new Timer(5000); // 每秒检查一次 _checkTimer.Elapsed += CheckPortStatus; _checkTimer.AutoReset = true; } private void CheckPortStatus(object sender, ElapsedEventArgs e) { - bool isConnected = SerialPort.GetPortNames().Contains(PortName); + //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) + if (response ==null) { _checkTimer.Stop(); _serialPort.Close(); diff --git a/ModbusTest/ModbusRTUTest.cs b/ModbusTest/ModbusRTUTest.cs index 92b8eab..44340ad 100644 --- a/ModbusTest/ModbusRTUTest.cs +++ b/ModbusTest/ModbusRTUTest.cs @@ -14,7 +14,7 @@ namespace ModbusTest { public class ModbusRTUTest { - //设计缺陷,应该使用适配器模式重写SerialPort + private SerialPort _serialport; private Mock _mockDbContext; private ModbusRTU _modbusRtu; @@ -38,6 +38,7 @@ namespace ModbusTest { // 确保在测试结束后释放资源 _serialport.Dispose(); + _modbusRtu.Dispose(); } /// /// 测试线圈读取 @@ -100,7 +101,7 @@ namespace ModbusTest { ushort[] data = new ushort[5]; Array.Fill(data, (ushort)0); - var expected = _modbusRtu.ReadRegisters(1, 300, 5); + var expected = _modbusRtu.ReadRegister(1, 300, 5); CollectionAssert.AreEqual(data, expected); } @@ -112,7 +113,7 @@ namespace ModbusTest ushort[] data = new ushort[5]; Array.Fill(data, (ushort)1); - var expected = _modbusRtu.ReadRegisters(1, 305, 5); + var expected = _modbusRtu.ReadRegister(1, 305, 5); CollectionAssert.AreEqual(data, expected); } @@ -126,8 +127,8 @@ namespace ModbusTest ushort[] data = new ushort[5]; Array.Fill(data, (ushort)1); - _modbusRtu.WriteRegisters(1, 310, data); - var expected = _modbusRtu.ReadRegisters(1, 310, 5); + _modbusRtu.WriteRegister(1, 310, data); + var expected = _modbusRtu.ReadRegister(1, 310, 5); CollectionAssert.AreEqual(data, expected); } @@ -138,11 +139,58 @@ namespace ModbusTest ushort[] data = new ushort[5]; Array.Fill(data, (ushort)2); - _modbusRtu.WriteRegisters(1, 315, data); - var expected = _modbusRtu.ReadRegisters(1, 315, 5); + _modbusRtu.WriteRegister(1, 315, data); + var expected = _modbusRtu.ReadRegister(1, 315, 5); CollectionAssert.AreEqual(data, expected); } + + [Test] + public void CRC1() + { + byte[] data = { 0x48, 0x65, 0x6C, 0x6C, 0x6F }; + int length = data.Length; + + byte[] crc = ModbusRTU.CalculateCRC(data, length); + // Assert + Assert.That(crc[0], Is.EqualTo(0x77)); + Assert.That(crc[1], Is.EqualTo(0xF3)); + } + + [Test] + public void CRC2() + { + byte[] data = { 0x49, 0x66, 0x51, 0x6C, 0x7F }; + int length = data.Length; + + byte[] crc = ModbusRTU.CalculateCRC(data, length); + // Assert + Assert.That(crc[0], Is.EqualTo(0xDA)); + Assert.That(crc[1], Is.EqualTo(0x77)); + } + + + [Test] + public void CRC3() + { + byte[] data = { 0x01, 0x90, 0x03, 0x0C, 0x01 }; + + + bool isTrue = ModbusRTU.ValidateCRC(data); + // Assert + Assert.IsTrue(isTrue); + } + [Test] + public void CRC4() + { + byte[] data = { 0x01, 0x90, 0x03, 0x0C, 0x00 }; + + + bool isFalse = ModbusRTU.ValidateCRC(data); + // Assert + Assert.IsFalse(isFalse); + } + } ///// ///// 解析线圈的返回值 ///// @@ -197,4 +245,4 @@ namespace ModbusTest //} } -} +