news 2026/3/4 19:37:07

WPF实现Modbus TCP通信客户端

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
WPF实现Modbus TCP通信客户端

一、概述:

使用:WPF、+ MVVM
Prism.DryIoc、system.IO.Ports、NMmodbus4

二、架构:

  • Views

    • MainWindow.xaml

  • Models

    • ModbusClient

  • ViewModels

    • MainWindowViewModel

  • Services

    • Interface

      • IModbusService

    • ModbusService

三、ModbusClient

public class ModbusClient { public ushort Address { get; set; } public ushort Value { get; set; } public string DisplayText => $"Addr {Address}: {Value}"; }

四、IModbusService

public interface IModbusService { Task<bool> ConnectAsync(string ipAddress, int port); Task<ushort[]> ReadHoldingRegistersAsync(ushort startAddress, ushort numberOfPoints); Task<bool> WriteSingleRegisterAsync(ushort address, ushort value); void Disconnect(); bool IsConnected { get; } }

五、ModbusService

public class ModbusService : IModbusService { private TcpClient? _tcpClient; private ModbusIpMaster? _master; public bool IsConnected => _tcpClient?.Connected == true && _master != null; public async Task<bool> ConnectAsync(string ipAddress, int port) { try { _tcpClient = new TcpClient(); await _tcpClient.ConnectAsync(ipAddress, port); if (!_tcpClient.Connected) return false; _master = ModbusIpMaster.CreateIp(_tcpClient); return true; } catch { Disconnect(); return false; } } public async Task<ushort[]> ReadHoldingRegistersAsync(ushort startAddress, ushort numberOfPoints) { if (!IsConnected || _master == null) throw new InvalidOperationException("Not connected to Modbus server."); return await Task.Run(() => _master.ReadHoldingRegisters(0, startAddress, numberOfPoints)); } public async Task<bool> WriteSingleRegisterAsync(ushort address, ushort value) { if (!IsConnected || _master == null) return false; await Task.Run(() => _master.WriteSingleRegister(0, address, value)); return true; } public void Disconnect() { _master?.Dispose(); _tcpClient?.Close(); _tcpClient?.Dispose(); _master = null; _tcpClient = null; } }

六、MainWindowViewModel

public class MainWindowViewModel : BindableBase { private readonly IModbusService _modbusService; private string _ipAddress = "127.0.0.1"; private int _port = 502; private ushort _startAddress = 0; private ushort _count = 10; private string _status = "Disconnected"; private ObservableCollection<ModbusClient> _modbusClient = new(); public string IpAddress { get => _ipAddress; set => SetProperty(ref _ipAddress, value); } public int Port { get => _port; set => SetProperty(ref _port, value); } public ushort StartAddress { get => _startAddress; set => SetProperty(ref _startAddress, value); } public ushort Count { get => _count; set => SetProperty(ref _count, value); } public string Status { get => _status; set => SetProperty(ref _status, value); } public ObservableCollection<ModbusClient> modbusClient { get => _modbusClient; set => SetProperty(ref _modbusClient, value); } public DelegateCommand ConnectCommand { get; } public DelegateCommand DisconnectCommand { get; } public DelegateCommand ReadRegistersCommand { get; } public MainWindowViewModel(IModbusService modbusService) { _modbusService = modbusService; ConnectCommand = new DelegateCommand(Connect); DisconnectCommand = new DelegateCommand(Disconnect); ReadRegistersCommand = new DelegateCommand(ReadRegisters); } private async void Connect() { var success = await _modbusService.ConnectAsync(IpAddress, Port); Status = success ? "Connected" : "Connection failed"; } private void Disconnect() { _modbusService.Disconnect(); Status = "Disconnected"; } private async void ReadRegisters() { try { var data = await _modbusService.ReadHoldingRegistersAsync(StartAddress, Count); modbusClient.Clear(); for (int i = 0; i < data.Length; i++) { modbusClient.Add(new ModbusClient { Address = (ushort)(StartAddress + i), Value = data[i], }); } } catch (Exception ex) { MessageBox.Show($"Error reading registers: {ex.Message}"); } } }

七、MainWindow.xaml

<Window x:Class="ModbusDemo.Views.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:ModbusDemo" mc:Ignorable="d" Title="Modbus TCP Client" Height="450" Width="800"> <Grid Margin="10"> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/> <RowDefinition Height="*"/> </Grid.RowDefinitions> <!-- Connection Panel --> <StackPanel Orientation="Horizontal" Margin="0,0,0,10"> <TextBox Text="{Binding IpAddress}" Width="120" Margin="0,0,5,0"/> <TextBox Text="{Binding Port}" Width="60" Margin="0,0,10,0"/> <Button Content="Connect" Command="{Binding ConnectCommand}" Width="80" Margin="0,0,5,0"/> <Button Content="Disconnect" Command="{Binding DisconnectCommand}" Width="80"/> </StackPanel> <!-- Status --> <TextBlock Grid.Row="1" Text="{Binding Status}" Margin="0,0,0,10"/> <!-- Read Panel --> <StackPanel Grid.Row="2" Orientation="Horizontal" VerticalAlignment="Top"> <TextBox Text="{Binding StartAddress}" Width="60" Margin="0,0,5,0"/> <TextBox Text="{Binding Count}" Width="50" Margin="0,0,10,0"/> <Button Content="Read Holding Registers" Command="{Binding ReadRegistersCommand}"/> </StackPanel> <!-- Register List --> <ListBox Grid.Row="2" Margin="0,40,0,0" ItemsSource="{Binding modbusClient}" > <ListBox.ItemTemplate> <DataTemplate> <StackPanel Orientation="Horizontal"> <TextBlock Text="{Binding Address, StringFormat='Addr {0}: '}" FontWeight="Bold"/> <TextBlock Text="{Binding Value}"/> <TextBlock Text="{Binding DisplayText}" Margin="30 0"/> </StackPanel> </DataTemplate> </ListBox.ItemTemplate> </ListBox> </Grid> </Window>

八、MainWindow.xaml.cs

namespace ModbusDemo.Views { /// <summary> /// Interaction logic for MainWindow.xaml /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } } }

九、App.xaml

<prism:PrismApplication x:Class="ModbusDemo.App" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:ModbusDemo" xmlns:prism="http://prismlibrary.com/"> <Application.Resources> </Application.Resources> </prism:PrismApplication>

十、App.xaml.cs

using ModbusDemo.Services.Interface; using ModbusDemo.Services; using ModbusDemo.Views; using System.Windows; using ModbusDemo.ViewModels; namespace ModbusDemo { /// <summary> /// Interaction logic for App.xaml /// </summary> public partial class App : PrismApplication { protected override Window CreateShell() { return Container.Resolve<MainWindow>(); } protected override void RegisterTypes(IContainerRegistry containerRegistry) { containerRegistry.RegisterSingleton<IModbusService, ModbusService>(); containerRegistry.RegisterForNavigation<MainWindow, MainWindowViewModel>(); } } }
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/2/20 23:56:28

理想二极管在电源管理中的应用原理深度剖析

理想二极管&#xff1a;如何用MOSFET“伪装”成零压降二极管&#xff0c;彻底告别发热与效率瓶颈&#xff1f;你有没有遇到过这样的场景&#xff1a;一个看似简单的电源切换电路&#xff0c;却因为用了几个肖特基二极管&#xff0c;导致板子烫得不敢摸&#xff1f;或者在做电池…

作者头像 李华
网站建设 2026/2/28 13:55:32

UVC协议如何简化监控开发流程:核心要点

UVC协议如何让监控开发“开箱即用”&#xff1a;从原理到实战的深度解析你有没有遇到过这样的场景&#xff1f;新买了一个USB摄像头&#xff0c;插上电脑后还没来得及安装驱动&#xff0c;系统就已经弹出提示&#xff1a;“已检测到新的视频设备”——打开会议软件&#xff0c;…

作者头像 李华
网站建设 2026/3/3 7:32:20

新手进阶Python:给办公看板加权限管理,多角色安全协作

大家好&#xff01;我是CSDN的Python新手博主&#xff5e; 上一篇我们用Flask搭建了办公数据看板&#xff0c;实现了局域网内数据共享&#xff0c;但很多小伙伴反馈“所有人都能看所有数据&#xff0c;比如销售员工能看到其他部门的业绩&#xff0c;不太安全”。今天就带来超落…

作者头像 李华
网站建设 2026/3/2 20:50:26

GPU驱动卸载失败?display driver uninstaller超详细版解决方案

GPU驱动卸载失败&#xff1f;一招彻底解决&#xff01;DDU实战全解析 你有没有遇到过这样的情况&#xff1a;想升级显卡驱动&#xff0c;结果安装程序弹出“Error 1”&#xff1b;或者刚换了一块新显卡&#xff0c;系统却死活识别不了&#xff1b;甚至重装系统后屏幕黑屏、分辨…

作者头像 李华
网站建设 2026/3/2 22:10:29

HID设备在Linux下的USB驱动实现详解

Linux下HID设备的USB驱动实现&#xff1a;从插入到事件上报的完整链路解析 你有没有想过&#xff0c;当你把一个USB鼠标插进电脑时&#xff0c;光标为什么能立刻动起来&#xff1f;不需要安装任何驱动&#xff0c;系统仿佛“天生”就认识它。这背后&#xff0c;正是 HID&…

作者头像 李华
网站建设 2026/2/27 1:50:45

手把手教你嘉立创PCB布线:EasyEDA自动布线功能详解

嘉立创EDA自动布线实战&#xff1a;从零开始搞定PCB设计&#xff0c;小白也能一天出板你是不是也经历过这样的时刻&#xff1f;画好了原理图&#xff0c;信心满满地转入PCB界面&#xff0c;结果面对一堆飞线和密密麻麻的焊盘&#xff0c;瞬间懵了——“这线到底该怎么走&#x…

作者头像 李华