@@ -47,7 +47,8 @@ namespace ModbusDemo | |||
containerRegistry.RegisterSingleton<SerialPort>(); | |||
//将读线圈注册 | |||
containerRegistry.Register<ModbusRTU>(); | |||
//附加功能如何读取单数寄存器; | |||
containerRegistry.RegisterForNavigation<AttachUC, AttachUCViewModel>(); | |||
// 1. 加载配置文件 | |||
@@ -13,7 +13,7 @@ namespace ModbusDemo.Device | |||
{ | |||
public class ModbusRTU : IModbusRTU | |||
{ | |||
//串口 | |||
private SerialPort _serialPort; | |||
//用于操作数据库 | |||
private ModbusDbContext _modbusDbContext; | |||
@@ -106,7 +106,13 @@ namespace ModbusDemo.Device | |||
return resultValue; | |||
} | |||
/// <summary> | |||
/// 用来解析返回的数据 | |||
/// </summary> | |||
/// <param name="sendByte"></param> | |||
/// <param name="response"></param> | |||
/// <param name="numberOfPoints"></param> | |||
/// <returns></returns> | |||
public bool[] ParseCoilresponse(byte[] sendByte, byte[] response, ushort numberOfPoints) | |||
{ | |||
//将操作的指令,插入数据库 | |||
@@ -266,7 +272,13 @@ namespace ModbusDemo.Device | |||
} | |||
} | |||
/// <summary> | |||
/// 读寄存器操作 | |||
/// </summary> | |||
/// <param name="slaveAddress"></param> | |||
/// <param name="startAddress"></param> | |||
/// <param name="numberOfPoints"></param> | |||
/// <returns></returns> | |||
public ushort[] ReadRegisters(byte slaveAddress, ushort startAddress, ushort numberOfPoints) | |||
{ | |||
ushort[] resultValue = null; | |||
@@ -346,6 +358,13 @@ namespace ModbusDemo.Device | |||
} | |||
/// <summary> | |||
/// 解析读取到的操作 | |||
/// </summary> | |||
/// <param name="sendByte"></param> | |||
/// <param name="response"></param> | |||
/// <param name="numberOfPoints"></param> | |||
/// <returns></returns> | |||
public ushort[] ParseRegistersresponse(byte[] sendByte, byte[] response, ushort numberOfPoints) | |||
{ | |||
//将操作的指令,插入数据库 | |||
@@ -380,7 +399,12 @@ namespace ModbusDemo.Device | |||
} | |||
/// <summary> | |||
/// 写寄存器 | |||
/// </summary> | |||
/// <param name="slaveAddress"></param> | |||
/// <param name="startAddress"></param> | |||
/// <param name="data"></param> | |||
public void WriteRegisters(byte slaveAddress, ushort startAddress, ushort[] data) | |||
{ | |||
if (!_serialPort.IsOpen) | |||
@@ -472,7 +496,11 @@ namespace ModbusDemo.Device | |||
} | |||
/// <summary> | |||
/// 将数组转换为字符串 | |||
/// </summary> | |||
/// <param name="byteArray"></param> | |||
/// <returns></returns> | |||
static string ByteArrayToString(byte[] byteArray) | |||
{ | |||
if (byteArray == null || byteArray.Length == 0) | |||
@@ -13,6 +13,7 @@ | |||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="6.0.27" /> | |||
<PackageReference Include="Microsoft.Extensions.Configuration" Version="9.0.7" /> | |||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="9.0.7" /> | |||
<PackageReference Include="NModbus4" Version="2.1.0" /> | |||
<PackageReference Include="Prism.DryIoc" Version="8.1.97" /> | |||
<PackageReference Include="System.IO.Ports" Version="9.0.7" /> | |||
</ItemGroup> | |||
@@ -0,0 +1,40 @@ | |||
<UserControl | |||
x:Class="ModbusDemo.VIew.AttachUC" | |||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" | |||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" | |||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" | |||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" | |||
xmlns:local="clr-namespace:ModbusDemo.VIew" | |||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes" | |||
xmlns:viewmodel="clr-namespace:ModbusDemo.VIewModel" | |||
d:DataContext="{d:DesignInstance Type=viewmodel:AttachUCViewModel}" | |||
d:Background="White" | |||
d:DesignHeight="450" | |||
d:DesignWidth="800" | |||
mc:Ignorable="d"> | |||
<Grid> | |||
<Grid.RowDefinitions> | |||
<RowDefinition Height="80"> | |||
</RowDefinition> | |||
<RowDefinition> | |||
</RowDefinition> | |||
<RowDefinition> | |||
</RowDefinition> | |||
</Grid.RowDefinitions> | |||
<Button | |||
Width="80" | |||
Height="40" | |||
materialDesign:ButtonAssist.CornerRadius="20" | |||
Command="{Binding ReadOddRegisterCmm}" | |||
Content="读取" | |||
Style="{StaticResource MaterialDesignRaisedDarkButton}"> | |||
</Button> | |||
<TextBox Grid.Row="1" Margin="5" Text="{Binding ReadResult}" Style="{StaticResource MaterialDesignOutlinedTextBox}"> | |||
</TextBox> | |||
<TextBlock Grid.Row="2" Text="{Binding Time}" FontSize="16"> | |||
</TextBlock> | |||
</Grid> | |||
</UserControl> |
@@ -0,0 +1,28 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
using System.Text; | |||
using System.Threading.Tasks; | |||
using System.Windows; | |||
using System.Windows.Controls; | |||
using System.Windows.Data; | |||
using System.Windows.Documents; | |||
using System.Windows.Input; | |||
using System.Windows.Media; | |||
using System.Windows.Media.Imaging; | |||
using System.Windows.Navigation; | |||
using System.Windows.Shapes; | |||
namespace ModbusDemo.VIew | |||
{ | |||
/// <summary> | |||
/// AttachUC.xaml 的交互逻辑 | |||
/// </summary> | |||
public partial class AttachUC : UserControl | |||
{ | |||
public AttachUC() | |||
{ | |||
InitializeComponent(); | |||
} | |||
} | |||
} |
@@ -88,6 +88,7 @@ | |||
Grid.Column="4" | |||
Width="80" | |||
Height="40" | |||
IsEnabled="{Binding IsEnable}" | |||
materialDesign:ButtonAssist.CornerRadius="20" | |||
Command="{Binding ReadCoilCmm}" | |||
Content="读取" | |||
@@ -142,6 +143,7 @@ | |||
Width="80" | |||
Height="40" | |||
materialDesign:ButtonAssist.CornerRadius="20" | |||
IsEnabled="{Binding IsEnable}" | |||
Command="{Binding WriteCoilCmm}" | |||
Content="写入" | |||
Style="{StaticResource MaterialDesignRaisedDarkButton}"> | |||
@@ -88,6 +88,7 @@ | |||
Grid.Column="4" | |||
Width="80" | |||
Height="40" | |||
IsEnabled="{Binding IsEnable}" | |||
materialDesign:ButtonAssist.CornerRadius="20" | |||
Command="{Binding ReadRegisterCmm}" | |||
Content="读取" | |||
@@ -141,6 +142,7 @@ | |||
Grid.Column="4" | |||
Width="80" | |||
Height="40" | |||
IsEnabled="{Binding IsEnable}" | |||
materialDesign:ButtonAssist.CornerRadius="20" | |||
Command="{Binding WriteRegisterCmm}" | |||
Content="写入" | |||
@@ -55,7 +55,7 @@ | |||
Height="30" | |||
Margin="10,0,0,5" | |||
HorizontalAlignment="Left" | |||
materialDesign:HintAssist.Hint="COM1" | |||
materialDesign:HintAssist.Hint="COM3" | |||
SelectedItem="{Binding PortName}"> | |||
<ComboBoxItem Content="COM1" /> | |||
<ComboBoxItem Content="COM2" /> | |||
@@ -0,0 +1,107 @@ | |||
using Modbus.Device; | |||
using Prism.Commands; | |||
using Prism.Mvvm; | |||
using System; | |||
using System.Collections.Generic; | |||
using System.IO.Ports; | |||
using System.Linq; | |||
using System.Text; | |||
using System.Threading.Tasks; | |||
using System.Windows; | |||
namespace ModbusDemo.VIewModel | |||
{ | |||
public class AttachUCViewModel : BindableBase | |||
{ | |||
private SerialPort _serialPort; | |||
public DelegateCommand ReadOddRegisterCmm { get; set; } | |||
//读取的结果 | |||
private string _readResult; | |||
public string ReadResult | |||
{ | |||
get { return _readResult; } | |||
set | |||
{ | |||
_readResult = value; | |||
RaisePropertyChanged(); | |||
} | |||
} | |||
//显示操作的时间 | |||
private int _time; | |||
public int Time | |||
{ | |||
get { return _time; } | |||
set | |||
{ | |||
_time = value; | |||
RaisePropertyChanged(); | |||
} | |||
} | |||
public AttachUCViewModel() | |||
{ | |||
} | |||
public AttachUCViewModel(SerialPort serialPort) | |||
{ | |||
ReadOddRegisterCmm = new DelegateCommand(ReadOddRegister); | |||
_serialPort = serialPort; | |||
} | |||
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) | |||
{ | |||
Console.WriteLine($"线程 {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(); | |||
} | |||
} | |||
} |
@@ -28,6 +28,20 @@ namespace ModbusDemo.VIewModel | |||
//获取当前使用的串口 | |||
private SerialPort _serialPort; | |||
//显示当前链接串口的信息 | |||
//按钮是否可以点击 | |||
private bool _isEnable; | |||
public bool IsEnable | |||
{ | |||
get { return _isEnable; } | |||
set | |||
{ | |||
_isEnable = value; | |||
RaisePropertyChanged(); | |||
} | |||
} | |||
public string SerialPortInfo | |||
{ | |||
get | |||
@@ -163,6 +177,7 @@ namespace ModbusDemo.VIewModel | |||
_modbusDbContext = modbusDbContext; | |||
//初始化查询操作 | |||
ModbusLogList = GetOperateCoil(); | |||
IsEnable = true; | |||
} | |||
/// <summary> | |||
@@ -170,23 +185,29 @@ namespace ModbusDemo.VIewModel | |||
/// </summary> | |||
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)) | |||
{ | |||
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(() => | |||
{ | |||
@@ -196,6 +217,7 @@ namespace ModbusDemo.VIewModel | |||
{ | |||
MessageBox.Show("读取失败", "error", MessageBoxButton.OK, MessageBoxImage.Error); | |||
ModbusLogList = GetOperateCoil(); | |||
IsEnable = true; | |||
return; | |||
} | |||
@@ -207,6 +229,7 @@ namespace ModbusDemo.VIewModel | |||
} | |||
ReadResult = temp; | |||
MessageBox.Show("读取成功"); | |||
IsEnable = true; | |||
ModbusLogList = GetOperateCoil(); | |||
}); | |||
@@ -217,19 +240,24 @@ 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)) | |||
{ | |||
MessageBox.Show("WriteData 格式无效", "warning", MessageBoxButton.OK, MessageBoxImage.Warning); | |||
IsEnable = true; | |||
return; | |||
} | |||
//去除字符串中的空格 | |||
@@ -243,14 +271,15 @@ namespace ModbusDemo.VIewModel | |||
_modbusRTU.WriteCoil(writeSlaveAddressValue, WriteStartAddressValue, data); | |||
ModbusLogList = GetOperateCoil(); | |||
ModbusLog modbusLog = ModbusLogList.FirstOrDefault(); | |||
if (modbusLog != null && modbusLog.ResponseData != string.Empty) | |||
if (modbusLog != null && modbusLog.ResponseData != string.Empty && modbusLog.ResponseData[3] !='8') | |||
{ | |||
MessageBox.Show("写入成功"); | |||
IsEnable = true; | |||
} | |||
else | |||
{ | |||
MessageBox.Show("写入失败", "error", MessageBoxButton.OK, MessageBoxImage.Error); | |||
IsEnable = true; | |||
} | |||
}); | |||
@@ -66,6 +66,7 @@ namespace ModbusDemo.VIewModel | |||
LeftMenusList.Add(new MenusInfo() { Icon = "AllInclusive", MenuName = "线圈操作", ViewName = "CoilUC" }); | |||
LeftMenusList.Add(new MenusInfo() { Icon = "BlurCircular", MenuName = "寄存器操作", ViewName = "RegisterUC" }); | |||
LeftMenusList.Add(new MenusInfo() { Icon = "Settings", MenuName = "串口设置", ViewName = "SettingsUC" }); | |||
LeftMenusList.Add(new MenusInfo() { Icon = "Settings", MenuName = "附加功能", ViewName = "AttachUC" }); | |||
} | |||
public void DefultNaigation() | |||
@@ -24,6 +24,20 @@ namespace ModbusDemo.VIewModel | |||
public DelegateCommand WriteRegisterCmm { get; set; } | |||
//获取当前使用的串口 | |||
private SerialPort _serialPort; | |||
//按钮是否可以点击 | |||
private bool _isEnable; | |||
public bool IsEnable | |||
{ | |||
get { return _isEnable; } | |||
set | |||
{ | |||
_isEnable = value; | |||
RaisePropertyChanged(); | |||
} | |||
} | |||
//显示当前链接串口的信息 | |||
public string SerialPortInfo | |||
{ | |||
@@ -159,6 +173,7 @@ namespace ModbusDemo.VIewModel | |||
ModbusLogList = GetOperateRegister(); | |||
IsEnable = true; | |||
} | |||
@@ -169,21 +184,26 @@ namespace ModbusDemo.VIewModel | |||
/// </summary> | |||
private void ReadRegister() | |||
{ | |||
//将按钮设置为不可点击状态,并解析用户输入的信息 | |||
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)) | |||
{ | |||
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线程卡顿 | |||
@@ -194,6 +214,7 @@ namespace ModbusDemo.VIewModel | |||
{ | |||
MessageBox.Show("读取失败", "error", MessageBoxButton.OK, MessageBoxImage.Error); | |||
ModbusLogList = GetOperateRegister(); | |||
IsEnable = true; | |||
return; | |||
} | |||
string temp = ""; | |||
@@ -204,6 +225,7 @@ namespace ModbusDemo.VIewModel | |||
} | |||
ReadResult = temp; | |||
MessageBox.Show("读取成功"); | |||
IsEnable = true; | |||
ModbusLogList = GetOperateRegister(); | |||
}); | |||
@@ -213,19 +235,24 @@ 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)) | |||
{ | |||
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); | |||
@@ -244,6 +271,7 @@ namespace ModbusDemo.VIewModel | |||
{ | |||
MessageBox.Show($"无法将 '{parts[i]}' 转换为 ushort 类型"); | |||
IsEnable = true; | |||
return; | |||
} | |||
} | |||
@@ -254,14 +282,15 @@ namespace ModbusDemo.VIewModel | |||
_modbusRTU.WriteRegisters(writeSlaveAddressValue, WriteStartAddressValue, data); | |||
ModbusLogList = GetOperateRegister(); | |||
ModbusLog modbusLog = ModbusLogList.FirstOrDefault(); | |||
if (modbusLog != null && modbusLog.ResponseData != string.Empty) | |||
if (modbusLog != null && modbusLog.ResponseData != string.Empty && modbusLog.ResponseData[3] != '8') | |||
{ | |||
MessageBox.Show("写入成功"); | |||
IsEnable = true; | |||
} | |||
else | |||
{ | |||
MessageBox.Show("写入失败", "error", MessageBoxButton.OK, MessageBoxImage.Error); | |||
IsEnable = true; | |||
} | |||
}); | |||
@@ -25,7 +25,7 @@ namespace ModbusDemo.VIewModel | |||
//获取串口 | |||
private SerialPort _serialPort; | |||
//设置串口名字 | |||
private string _portName = "COM1"; | |||
private string _portName = "COM3"; | |||
public string PortName | |||
{ | |||