当前位置: 首页 > news >正文

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>(); } } }
http://www.jsqmd.com/news/235722/

相关文章:

  • OpenMV识别圆形物体:Hough变换算法通俗解释
  • 基于Java+SpringBoot+SSM商场停车场管理系统(源码+LW+调试文档+讲解等)/商场停车系统/停车场管理方案/商场停车解决方案/智能停车场管理系统/商场车辆管理系统/停车场智能化管理
  • 大规模设备接入下的USB2.0主机优化策略
  • 2026年课件制作新范式:AI PPT工具深度解析
  • 扇出能力对比:TTL与CMOS驱动多个负载的表现分析
  • PCB封装基础:通俗解释引脚间距与焊盘设计
  • 基于Java+SpringBoot+SSM在线学习交流系统(源码+LW+调试文档+讲解等)/在线学习平台/学习交流系统/线上学习交流/网络学习交流/在线教育交流系统/学习互动系统
  • AD导出Gerber文件在CAM软件中的后续处理方法
  • 基于Java+SpringBoot+SSM在线网络学习平台(源码+LW+调试文档+讲解等)/在线学习平台/网络学习平台/在线教育平台/网络教育平台/线上学习平台/线上教育平台/网络课程平台
  • 理想二极管在电源管理中的应用原理深度剖析
  • [特殊字符]_Web框架性能终极对决:谁才是真正的速度王者[20260112164948]
  • 基于Java+SpringBoot+SSM在线食品安全信息平台(源码+LW+调试文档+讲解等)/在线食品监管信息平台/食品安全在线查询平台/网络食品安全信息平台/在线食品信息公示平台
  • 趋势科技:速修复这个严重的 Apex Central RCE漏洞
  • Java Web 中小型医院网站系统源码-SpringBoot2+Vue3+MyBatis-Plus+MySQL8.0【含文档】
  • 电商运营中的数据驱动的决策流程
  • 基于Java+SpringBoot+SSM在线骑行网站(源码+LW+调试文档+讲解等)/在线骑行平台/骑行在线网站/骑行网站推荐/在线骑行服务网站/骑行爱好者网站/骑行活动在线网站
  • USB接口有几种?图文详解主流类型
  • Elasticsearch搜索优化:超详细版查询性能调优指南
  • ​[特殊字符]1 概述文献来源:基于多能互补的热电联供型微网优化运行研究CHP-MG 系统供给侧多能互补模型本文主要研究包含热、电、气 3 种能源形式的CHP-MG 系统优化运行
  • 收到工资 1002415.13 元,爱你华为!!!
  • [特殊字符]_微服务架构下的性能调优实战[20260112165846]
  • vitis安装目录结构解析:深入理解集成环境布局
  • 新手教程:如何正确完成libwebkit2gtk-4.1-0安装配置
  • cart-pole 建模
  • PCBA再流焊温度曲线优化操作指南
  • 基于SpringBoot+Vue的人事系统管理系统设计与实现【Java+MySQL+MyBatis完整源码】
  • 快速理解SystemVerilog过程块:always与initial深度剖析
  • UVC协议如何简化监控开发流程:核心要点
  • 通信协议入门:rs232和rs485的区别全面讲解
  • 快速上手:AI 图像风格迁移的代码实现方法