diff --git a/ModbusDemo/App.xaml.cs b/ModbusDemo/App.xaml.cs index a4a13d8..3361c7a 100644 --- a/ModbusDemo/App.xaml.cs +++ b/ModbusDemo/App.xaml.cs @@ -77,12 +77,7 @@ namespace ModbusDemo - //private SerialPort CreateSerialPort() - //{ - // SerialPort serialPort = new SerialPort(); - - // return serialPort; - //} + } } diff --git a/ModbusDemo/Device/ModbusRTU.cs b/ModbusDemo/Device/ModbusRTU.cs index 93803dc..6b45da5 100644 --- a/ModbusDemo/Device/ModbusRTU.cs +++ b/ModbusDemo/Device/ModbusRTU.cs @@ -1,4 +1,5 @@ -using ModbusDemo.Model; +using DryIoc; +using ModbusDemo.Model; using ModbusDemo.Uitls; using System; using System.Collections.Generic; @@ -19,7 +20,7 @@ namespace ModbusDemo.Device private ModbusDbContext _modbusDbContext; //TODO,修改 private SerialPortAdapter _portAdapter; - + public ModbusRTU(SerialPort serialPort, ModbusDbContext modbusDbContext) { _serialPort = serialPort; @@ -42,17 +43,16 @@ namespace ModbusDemo.Device MessageBox.Show("串口没有链接,请先链接"); return resultValue; } - List sendByteList = new List(); - //设置从站地址 - sendByteList.Add(slaveAddress); - //设置功能码 - sendByteList.Add(0x01); + List sendByteList = new List + { + slaveAddress, // 从站地址 + 0x01, // 功能码:读线圈 + }; + //设置起始地址的高位和低位 - sendByteList.Add(BitConverter.GetBytes(startAddress)[1]); - sendByteList.Add(BitConverter.GetBytes(startAddress)[0]); + sendByteList.AddRange(BitConverter.GetBytes(startAddress).Reverse()); //设置读取几个线圈的高位和低位 - sendByteList.Add(BitConverter.GetBytes(numberOfPoints)[1]); - sendByteList.Add(BitConverter.GetBytes(numberOfPoints)[0]); + sendByteList.AddRange(BitConverter.GetBytes(numberOfPoints).Reverse()); //获取CRC校验码 byte[] getCRC = sendByteList.ToArray(); byte[] resultCRC = CRCUitl.CalculateCRC(getCRC, getCRC.Length); @@ -114,7 +114,7 @@ namespace ModbusDemo.Device MessageBox.Show("0x15:数据库插入错误"); } - + return resultValue; } @@ -144,42 +144,30 @@ namespace ModbusDemo.Device MessageBox.Show("0x15:数据库插入错误"); } - + //判断发送回来的数据是否正确 if (CheckData.CheckResponse(response)) { - List responseList = new List(response); - //移除前两位 - responseList.RemoveRange(0, 3); - //移除后两位校验码 - responseList.RemoveRange(responseList.Count - 2, 2); - responseList.Reverse(); - //数组反转之后,转为二进制字符 - List StringList = responseList.Select(m => Convert.ToString(m, 2)).ToList(); - - var result = ""; - - foreach (var item in StringList) - { - result += item.PadLeft(8, '0'); - } - //先将字符串转为数组,再反转,再转为字节数组 - char[] chars = result.ToArray().Reverse().ToArray(); - bool[] ultimately = new bool[numberOfPoints]; - for (int i = 0; i < numberOfPoints; i++) - { - if (chars[i] == '1') - { - ultimately[i] = true; - } - else - { - ultimately[i] = false; - } - } - return ultimately; + // 移除前3个字节和后2个校验码字节,并反转数组 + var processedBytes = response + .Skip(3) // 移除前3个字节 + .Take(response.Count() - 5) // 保留中间部分 (总长度-5) + .Reverse() // 反转字节顺序 + .ToList(); + + // 将每个字节转换为8位二进制字符串,不足8位时左边补0 + string binaryString = string.Concat( + processedBytes.Select(b => Convert.ToString(b, 2).PadLeft(8, '0')) + ); + + // 反转二进制字符串中的所有位,并转换为布尔数组 + return binaryString + .Reverse() // 反转所有位的顺序 + .Take(numberOfPoints) // 取指定数量的位 + .Select(c => c == '1') // 将字符'1'转换为true,其他转换为false + .ToArray(); } else { @@ -202,35 +190,25 @@ namespace ModbusDemo.Device MessageBox.Show("串口没有链接,请先链接"); return; } - List sendByteList = new List(); - //设置从站地址 - sendByteList.Add(slaveAddress); - //设置功能码 - sendByteList.Add(0x0F); - //设置起始地址的高位和低位 - sendByteList.Add(BitConverter.GetBytes(startAddress)[1]); - sendByteList.Add(BitConverter.GetBytes(startAddress)[0]); - //设置写入几个线圈 - sendByteList.Add(BitConverter.GetBytes(data.Length)[1]); - sendByteList.Add(BitConverter.GetBytes(data.Length)[0]); + List sendByteList = new List + { + slaveAddress, // 从站地址 + 0x0F, // 功能码:写多个线圈 + }; - // 计算字节数 - int byteCount = (data.Length + 7) / 8; - sendByteList.Add((byte)byteCount); + // 添加起始地址(高字节在前,低字节在后) + sendByteList.AddRange(BitConverter.GetBytes(startAddress).Reverse()); - // 初始化字节数组 - byte[] coilBytes = new byte[byteCount]; + // 添加线圈数量(高字节在前,低字节在后) + ushort coilCount = (ushort)data.Length; + sendByteList.AddRange(BitConverter.GetBytes(coilCount).Reverse()); - // 把bool数组转换为字节 - for (int i = 0; i < data.Length; i++) - { - int byteIndex = i / 8; - int bitIndex = i % 8; - if (data[i]) - { - coilBytes[byteIndex] |= (byte)(1 << bitIndex); - } - } + // 计算所需字节数(每8个线圈占1字节) + int byteCount = (coilCount + 7) / 8; + sendByteList.Add((byte)byteCount); + + // 将布尔数组转换为字节数组(每个位表示一个线圈状态) + byte[] coilBytes = ConvertBoolsToBytes(data); sendByteList.AddRange(coilBytes); //获取CRC校验码 @@ -294,12 +272,30 @@ namespace ModbusDemo.Device MessageBox.Show("0x15:数据库插入错误"); } - - //判断发送回来的数据是否正确 CheckData.CheckResponse(response); - + } + /// + /// 将布尔数组转换为字节数组(每个位表示一个线圈状态) + /// + /// + /// + private static byte[] ConvertBoolsToBytes(bool[] data) + { + int byteCount = (data.Length + 7) / 8; + byte[] result = new byte[byteCount]; + + for (int i = 0; i < data.Length; i++) + { + if (data[i]) + { + int byteIndex = i / 8; + int bitIndex = i % 8; + result[byteIndex] |= (byte)(1 << bitIndex); + } + } + return result; } /// /// 读寄存器操作 @@ -316,17 +312,17 @@ namespace ModbusDemo.Device 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); @@ -390,7 +386,7 @@ namespace ModbusDemo.Device MessageBox.Show("0x15:数据库插入错误"); } - + return resultValue; @@ -422,7 +418,7 @@ namespace ModbusDemo.Device MessageBox.Show("0x15:数据库插入错误"); } - + //判断发送回来的数据是否正确 if (CheckData.CheckResponse(response)) @@ -459,28 +455,29 @@ namespace ModbusDemo.Device MessageBox.Show("串口没有链接,请先链接"); return; } - List sendByteList = new List(); - //设置从站地址 - sendByteList.Add(slaveAddress); - //设置功能码 - sendByteList.Add(0x10); - //设置起始地址的高位和低位 - sendByteList.Add(BitConverter.GetBytes(startAddress)[1]); - sendByteList.Add(BitConverter.GetBytes(startAddress)[0]); - //设置写入几个线圈 - sendByteList.Add(BitConverter.GetBytes(data.Length)[1]); - sendByteList.Add(BitConverter.GetBytes(data.Length)[0]); + // 计算字节数 (每个寄存器占2字节) + int byteCount = (data.Length) * 2; - // 计算字节数 - int byteCount = (data.Length + 7) / 8; + // 预分配列表容量,避免多次扩容 + var sendByteList = new List(9 + byteCount) + { + slaveAddress, // 从站地址 + 0x10, // 功能码:写多个保持寄存器 + }; + + // 添加起始地址(高字节在前) + sendByteList.AddRange(BitConverter.GetBytes(startAddress).Reverse()); + + // 添加寄存器数量(高字节在前) + sendByteList.AddRange(BitConverter.GetBytes((ushort)data.Length).Reverse()); + + // 添加字节数 sendByteList.Add((byte)byteCount); + // 添加寄存器数据(每个寄存器2字节,高字节在前) foreach (ushort value in data) { - byte[] valueBytes = BitConverter.GetBytes(value); - // 大端序:高字节在前,低字节在后 - sendByteList.Add(valueBytes[1]); - sendByteList.Add(valueBytes[0]); + sendByteList.AddRange(BitConverter.GetBytes(value).Reverse()); } //获取CRC校验码 @@ -544,7 +541,7 @@ namespace ModbusDemo.Device MessageBox.Show("0x15:数据库插入错误"); } - + //判断发送回来的数据是否正确 CheckData.CheckResponse(response); diff --git a/ModbusDemo/Event/SerialPortSettingsChangedEvent .cs b/ModbusDemo/Event/SerialPortSettingsChangedEvent .cs new file mode 100644 index 0000000..9f3bcf4 --- /dev/null +++ b/ModbusDemo/Event/SerialPortSettingsChangedEvent .cs @@ -0,0 +1,14 @@ +using Prism.Events; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ModbusDemo.Event +{ + public class SerialPortSettingsChangedEvent: PubSubEvent + { + + } +} diff --git a/ModbusDemo/Model/ModbusDbContext.cs b/ModbusDemo/Model/ModbusDbContext.cs index 068adb4..08dd423 100644 --- a/ModbusDemo/Model/ModbusDbContext.cs +++ b/ModbusDemo/Model/ModbusDbContext.cs @@ -12,9 +12,7 @@ namespace ModbusDemo.Model /// public class ModbusDbContext:DbContext { - protected ModbusDbContext() - { - } + public ModbusDbContext(DbContextOptions options) :base(options) { diff --git a/ModbusDemo/Uitls/CheckData.cs b/ModbusDemo/Uitls/CheckData.cs index ec4b836..277b369 100644 --- a/ModbusDemo/Uitls/CheckData.cs +++ b/ModbusDemo/Uitls/CheckData.cs @@ -16,7 +16,7 @@ namespace ModbusDemo.Uitls /// public static bool CheckResponse(byte[] response) { - if (response.Length == 0) + if (response == null || response.Length == 0) { MessageBox.Show("返回数据为空", "error", MessageBoxButton.OK, MessageBoxImage.Error); return false; diff --git a/ModbusDemo/VIew/CoilUC.xaml b/ModbusDemo/VIew/CoilUC.xaml index a0a0489..30de8d1 100644 --- a/ModbusDemo/VIew/CoilUC.xaml +++ b/ModbusDemo/VIew/CoilUC.xaml @@ -57,6 +57,7 @@ + @@ -109,6 +110,7 @@ + @@ -149,7 +151,7 @@ Style="{StaticResource MaterialDesignRaisedDarkButton}"> - + diff --git a/ModbusDemo/VIew/RegisterUC.xaml b/ModbusDemo/VIew/RegisterUC.xaml index 57330b4..e285545 100644 --- a/ModbusDemo/VIew/RegisterUC.xaml +++ b/ModbusDemo/VIew/RegisterUC.xaml @@ -57,6 +57,7 @@ + @@ -109,6 +110,7 @@ + @@ -150,6 +152,7 @@ + diff --git a/ModbusDemo/VIew/SettingsUC.xaml b/ModbusDemo/VIew/SettingsUC.xaml index 057af00..3fa057b 100644 --- a/ModbusDemo/VIew/SettingsUC.xaml +++ b/ModbusDemo/VIew/SettingsUC.xaml @@ -61,6 +61,8 @@ + + diff --git a/ModbusDemo/VIewModel/AttachUCViewModel.cs b/ModbusDemo/VIewModel/AttachUCViewModel.cs index c5c851c..64ef628 100644 --- a/ModbusDemo/VIewModel/AttachUCViewModel.cs +++ b/ModbusDemo/VIewModel/AttachUCViewModel.cs @@ -95,7 +95,7 @@ namespace ModbusDemo.VIewModel int count = 0; for (int i = 1; i < totalRegisters; i += 2) { - result.Append(allRegisters[i].ToString()+" "); + result.Append(allRegisters[i].ToString() + " "); count++; if (count % 50 == 0) { @@ -105,7 +105,9 @@ namespace ModbusDemo.VIewModel ReadResult = result.ToString(); } - + /// + /// 自己的类 + /// private void ReadOddRegister2() { byte slaveId = 1; @@ -122,13 +124,13 @@ namespace ModbusDemo.VIewModel ushort currentChunkSize = 100; int chunkIndex = i; - tasks[i] = Task.Run(() => + tasks[i] = Task.Run(async () => { - - ushort[] registers = ReadRegisters(slaveId, startAddress, currentChunkSize); - Array.Copy(registers, 0, allRegisters, chunkIndex * chunkSize, currentChunkSize); - - + + ushort[] registers = await ReadRegisters(slaveId, startAddress, currentChunkSize); + Array.Copy(registers, 0, allRegisters, chunkIndex * chunkSize, currentChunkSize); + + }); } @@ -150,9 +152,9 @@ namespace ModbusDemo.VIewModel } - public ushort[] ReadRegisters(byte slaveAddress, ushort startAddress, ushort numberOfPoints) + public async Task ReadRegisters(byte slaveAddress, ushort startAddress, ushort numberOfPoints) { - object _lock = new object(); + ushort[] resultValue = null; if (!_serialPort.IsOpen) { @@ -186,41 +188,40 @@ namespace ModbusDemo.VIewModel { try { - lock (_lock) - { - //使用时间去轮询查询数据 - 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资源 - Task.Delay(20); - } - response = responseData.ToArray(); - if (response[0] == 0x00) + + //使用时间去轮询查询数据 + DateTime startTime = DateTime.Now; + while ((DateTime.Now - startTime).TotalMilliseconds < _serialPort.ReadTimeout) + { + int bytesToRead = _serialPort.BytesToRead; + if (bytesToRead > 0) { - continue; + byte[] buffer = new byte[bytesToRead]; + int bytesRead = _serialPort.Read(buffer, 0, bytesToRead); + responseData.AddRange(buffer.Take(bytesRead)); } - resultValue = ParseRegistersresponse(sendByte, response, numberOfPoints); - return resultValue; + //延迟20毫秒,节约cpu资源 + await Task.Delay(20); + } + response = responseData.ToArray(); + if (response[0] == 0x00) + { + continue; } + resultValue = ParseRegistersresponse(sendByte, response, numberOfPoints); + return resultValue; + } catch (Exception) { attempt++; } - + } - + return resultValue; @@ -229,7 +230,7 @@ namespace ModbusDemo.VIewModel public ushort[] ParseRegistersresponse(byte[] sendByte, byte[] response, ushort numberOfPoints) { - + //判断发送回来的数据是否正确 if (CheckData.CheckResponse(response)) diff --git a/ModbusDemo/VIewModel/CoilUCViewModel.cs b/ModbusDemo/VIewModel/CoilUCViewModel.cs index c2eee20..fb99100 100644 --- a/ModbusDemo/VIewModel/CoilUCViewModel.cs +++ b/ModbusDemo/VIewModel/CoilUCViewModel.cs @@ -1,6 +1,8 @@ using ModbusDemo.Device; +using ModbusDemo.Event; using ModbusDemo.Model; using Prism.Commands; +using Prism.Events; using Prism.Mvvm; using System; using System.Collections.Generic; @@ -17,6 +19,9 @@ namespace ModbusDemo.VIewModel /// public class CoilUCViewModel : BindableBase { + + //事件聚合器 + private readonly IEventAggregator _eventAggregator; //定义数据库操作 private ModbusDbContext _modbusDbContext; //获取读线圈的类 @@ -41,8 +46,8 @@ namespace ModbusDemo.VIewModel RaisePropertyChanged(); } } - - public string SerialPortInfo + + public string SerialPortInfo { get { @@ -50,9 +55,10 @@ namespace ModbusDemo.VIewModel + ",数据位:" + _serialPort.DataBits + ",校验位:" + _serialPort.Parity + ",停止位:" + _serialPort.StopBits; } - - } + + + #region 读取定义 //读取的从站id @@ -170,18 +176,29 @@ namespace ModbusDemo.VIewModel } - public CoilUCViewModel(SerialPort serialPort, ModbusRTU modbusRTU, ModbusDbContext modbusDbContext) + public CoilUCViewModel(SerialPort serialPort, ModbusRTU modbusRTU, ModbusDbContext modbusDbContext, IEventAggregator eventAggregator) { _serialPort = serialPort; ReadCoilCmm = new DelegateCommand(ReadCoil); _modbusRTU = modbusRTU; WriteCoilCmm = new DelegateCommand(WriteCoil); _modbusDbContext = modbusDbContext; + _eventAggregator = eventAggregator; + // 订阅事件(使用弱引用,避免内存泄漏) + _eventAggregator.GetEvent().Subscribe( + OnSerialPortSettingsChanged, + ThreadOption.UIThread, // 在UI线程执行回调 + false);// 不使用强引用 //初始化查询操作 ModbusLogList = GetOperateCoil(); IsEnable = true; } - + // 事件回调方法:当串口设置变更时触发 + private void OnSerialPortSettingsChanged() + { + // 强制刷新SerialPortInfo属性,触发UI更新 + RaisePropertyChanged(nameof(SerialPortInfo)); + } /// /// 线圈读取操作 /// diff --git a/ModbusDemo/VIewModel/RegisterUCViewModel.cs b/ModbusDemo/VIewModel/RegisterUCViewModel.cs index 95eecef..dc05eb9 100644 --- a/ModbusDemo/VIewModel/RegisterUCViewModel.cs +++ b/ModbusDemo/VIewModel/RegisterUCViewModel.cs @@ -1,6 +1,8 @@ using ModbusDemo.Device; +using ModbusDemo.Event; using ModbusDemo.Model; using Prism.Commands; +using Prism.Events; using Prism.Mvvm; using System; using System.Collections.Generic; @@ -14,6 +16,8 @@ namespace ModbusDemo.VIewModel { class RegisterUCViewModel : BindableBase { + //事件聚合器 + private readonly IEventAggregator _eventAggregator; //定义数据库操作类 private ModbusDbContext _modbusDbContext; //获取读线圈的类 @@ -163,19 +167,29 @@ namespace ModbusDemo.VIewModel { } - public RegisterUCViewModel(SerialPort serialPort, ModbusRTU modbusRTU, ModbusDbContext modbusDbContext) + public RegisterUCViewModel(SerialPort serialPort, ModbusRTU modbusRTU, ModbusDbContext modbusDbContext, IEventAggregator eventAggregator) { _serialPort = serialPort; ReadRegisterCmm = new DelegateCommand(ReadRegister); _modbusRTU = modbusRTU; WriteRegisterCmm = new DelegateCommand(WriteRegister); _modbusDbContext = modbusDbContext; - + _eventAggregator = eventAggregator; + // 订阅事件(使用弱引用,避免内存泄漏) + _eventAggregator.GetEvent().Subscribe( + OnSerialPortSettingsChanged, + ThreadOption.UIThread, // 在UI线程执行回调 + false);// 不使用强引用 ModbusLogList = GetOperateRegister(); IsEnable = true; } - + // 事件回调方法:当串口设置变更时触发 + private void OnSerialPortSettingsChanged() + { + // 强制刷新SerialPortInfo属性,触发UI更新 + RaisePropertyChanged(nameof(SerialPortInfo)); + } diff --git a/ModbusDemo/VIewModel/SettingsUCViewModel.cs b/ModbusDemo/VIewModel/SettingsUCViewModel.cs index 14bfcd7..3fc5298 100644 --- a/ModbusDemo/VIewModel/SettingsUCViewModel.cs +++ b/ModbusDemo/VIewModel/SettingsUCViewModel.cs @@ -1,5 +1,7 @@ -using ModbusDemo.Uitls; +using ModbusDemo.Event; +using ModbusDemo.Uitls; using Prism.Commands; +using Prism.Events; using Prism.Mvvm; using Prism.Regions; using System; @@ -15,6 +17,8 @@ namespace ModbusDemo.VIewModel class SettingsUCViewModel : BindableBase { + //事件聚合器 + private readonly IEventAggregator _eventAggregator; //控制页面跳转 private readonly IRegionManager _regionManager; //断开连接的命令 @@ -97,12 +101,13 @@ namespace ModbusDemo.VIewModel /// 从容器中获取创建的窗口 /// /// - public SettingsUCViewModel(SerialPort serialPort, IRegionManager regionManager) + public SettingsUCViewModel(SerialPort serialPort, IRegionManager regionManager, IEventAggregator eventAggregator) { _serialPort = serialPort; ConnectionCmm = new DelegateCommand(Connection); DisConnectionCmm = new DelegateCommand(DisConnection); _regionManager = regionManager; + _eventAggregator = eventAggregator; } private void DisConnection() @@ -110,8 +115,9 @@ namespace ModbusDemo.VIewModel if (_serialPort.IsOpen) { _serialPort.Close(); - MessageBox.Show("连接已经断开"); + } + MessageBox.Show("连接已经断开"); } /// @@ -126,8 +132,14 @@ namespace ModbusDemo.VIewModel _serialPort.PortName = GetComboBoxItemValue(this.PortName); _serialPort.BaudRate = int.Parse(GetComboBoxItemValue(this.BaudRate)); _serialPort.Parity = this.Parity; + if (this.StopBits == StopBits.None) + { + this.StopBits = StopBits.One; + MessageBox.Show("当前设备不支持无停止位(None)模式,已自动切换为 1 个停止位。"); + } _serialPort.StopBits = this.StopBits; _serialPort.DataBits = int.Parse(GetComboBoxItemValue(this.DataBits)); + //读取超时时间 _serialPort.ReadTimeout = 500; //写入超时时间 @@ -135,6 +147,8 @@ namespace ModbusDemo.VIewModel try { _serialPort.Open(); + // 发布事件通知其他ViewModel + _eventAggregator.GetEvent().Publish(); MessageBox.Show("串口链接成功"); _regionManager.Regions["ModbusRegion"].RequestNavigate("CoilUC"); @@ -142,9 +156,13 @@ namespace ModbusDemo.VIewModel catch (Exception) { - MessageBox.Show("串口已经链接,请先断开链接在尝试","warning",MessageBoxButton.OK,MessageBoxImage.Warning); + MessageBox.Show("串口已经链接或者断开,请重新检查在尝试","warning",MessageBoxButton.OK,MessageBoxImage.Warning); } } + else + { + MessageBox.Show("当前串口已经连接,请先断开,再连接"); + } } ///