Browse Source

bug修改,添加附加功能高效读取寄存器

master
永攀 张 1 week ago
parent
commit
ef86df764e
13 changed files with 280 additions and 12 deletions
  1. +2
    -1
      ModbusDemo/App.xaml.cs
  2. +33
    -5
      ModbusDemo/Device/ModbusRTU.cs
  3. +1
    -0
      ModbusDemo/ModbusDemo.csproj
  4. +40
    -0
      ModbusDemo/VIew/AttachUC.xaml
  5. +28
    -0
      ModbusDemo/VIew/AttachUC.xaml.cs
  6. +2
    -0
      ModbusDemo/VIew/CoilUC.xaml
  7. +2
    -0
      ModbusDemo/VIew/RegisterUC.xaml
  8. +1
    -1
      ModbusDemo/VIew/SettingsUC.xaml
  9. +107
    -0
      ModbusDemo/VIewModel/AttachUCViewModel.cs
  10. +31
    -2
      ModbusDemo/VIewModel/CoilUCViewModel.cs
  11. +1
    -0
      ModbusDemo/VIewModel/MainWindowViewModel.cs
  12. +31
    -2
      ModbusDemo/VIewModel/RegisterUCViewModel.cs
  13. +1
    -1
      ModbusDemo/VIewModel/SettingsUCViewModel.cs

+ 2
- 1
ModbusDemo/App.xaml.cs View File

@@ -47,7 +47,8 @@ namespace ModbusDemo
containerRegistry.RegisterSingleton<SerialPort>();
//将读线圈注册
containerRegistry.Register<ModbusRTU>();

//附加功能如何读取单数寄存器;
containerRegistry.RegisterForNavigation<AttachUC, AttachUCViewModel>();


// 1. 加载配置文件


+ 33
- 5
ModbusDemo/Device/ModbusRTU.cs View File

@@ -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)


+ 1
- 0
ModbusDemo/ModbusDemo.csproj View File

@@ -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>


+ 40
- 0
ModbusDemo/VIew/AttachUC.xaml View File

@@ -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>

+ 28
- 0
ModbusDemo/VIew/AttachUC.xaml.cs View File

@@ -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();
}
}
}

+ 2
- 0
ModbusDemo/VIew/CoilUC.xaml View File

@@ -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}">


+ 2
- 0
ModbusDemo/VIew/RegisterUC.xaml View File

@@ -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="写入"


+ 1
- 1
ModbusDemo/VIew/SettingsUC.xaml View File

@@ -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" />


+ 107
- 0
ModbusDemo/VIewModel/AttachUCViewModel.cs View File

@@ -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();

}
}

}

+ 31
- 2
ModbusDemo/VIewModel/CoilUCViewModel.cs View File

@@ -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;
}
});



+ 1
- 0
ModbusDemo/VIewModel/MainWindowViewModel.cs View File

@@ -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()


+ 31
- 2
ModbusDemo/VIewModel/RegisterUCViewModel.cs View File

@@ -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;
}
});


+ 1
- 1
ModbusDemo/VIewModel/SettingsUCViewModel.cs View File

@@ -25,7 +25,7 @@ namespace ModbusDemo.VIewModel
//获取串口
private SerialPort _serialPort;
//设置串口名字
private string _portName = "COM1";
private string _portName = "COM3";

public string PortName
{


Loading…
Cancel
Save