C#实现Modbus TCP通讯测试软件

一、环境搭建与依赖配置

  1. 开发环境

    • Visual Studio 2022+
    • .NET 6.0+
    • NuGet包:NModbus(协议实现)、MaterialDesignThemes(UI美化)
  2. 项目结构

    ModbusTester/
    ├── Models/              # 数据模型
    ├── Views/               # WPF界面
    ├── ViewModels/          # MVVM逻辑
    ├── Services/            # 通信服务
    └── Utils/               # 工具类(CRC校验、日志)
    

二、核心代码实现

1. Modbus TCP服务类(基于NModbus)

// Services/ModbusService.cs
using Modbus.Device;
using System.Net.Sockets;

public class ModbusTcpService : IDisposable
{
    private ModbusIpMaster _master;
    private TcpClient _tcpClient;

    public async Task ConnectAsync(string ip, int port = 502)
    {
        _tcpClient = new TcpClient();
        await _tcpClient.ConnectAsync(ip, port);
        _master = ModbusIpMaster.CreateIp(_tcpClient);
    }

    // 读取保持寄存器(功能码03)
    public ushort[] ReadHoldingRegisters(ushort startAddr, ushort count)
    {
        return _master.ReadHoldingRegisters(1, startAddr, count); // 从站ID默认1
    }

    // 写入单个寄存器(功能码06)
    public void WriteSingleRegister(ushort addr, ushort value)
    {
        _master.WriteSingleRegister(1, addr, value);
    }

    // 批量写入寄存器(功能码16)
    public void WriteMultipleRegisters(ushort startAddr, ushort[] values)
    {
        _master.WriteMultipleRegisters(1, startAddr, values);
    }

    public void Dispose()
    {
        _master?.Dispose();
        _tcpClient?.Close();
    }
}

2. 数据模型设计

// Models/ModbusDataModel.cs
public class ModbusDataModel : INotifyPropertyChanged
{
    private ushort _registerValue;
    public ushort RegisterValue
    {
        get => _registerValue;
        set
        {
            _registerValue = value;
            OnPropertyChanged(nameof(RegisterValue));
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged(string propertyName) =>
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

三、WPF界面实现

1. 主界面XAML(核心功能区)

<!-- Views/MainView.xaml -->
<Window x:Class="ModbusTester.Views.MainView"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
        Title="Modbus TCP测试工具" Height="450" Width="800">
    <Grid>
        <!-- 连接配置区 -->
        <GroupBox Header="连接配置" Margin="10">
            <StackPanel Orientation="Vertical">
                <TextBox x:Name="txtIP" Width="200" materialDesign:HintAssist.Hint="IP地址"/>
                <TextBox x:Name="txtPort" Width="100" materialDesign:HintAssist.Hint="端口"/>
                <Button Content="连接" Command="{Binding ConnectCommand}"/>
            </StackPanel>
        </GroupBox>

        <!-- 数据操作区 -->
        <GroupBox Header="数据操作" Margin="10,120,10,10">
            <StackPanel Orientation="Vertical">
                <TextBox x:Name="txtStartAddr" Width="100" materialDesign:HintAssist.Hint="起始地址"/>
                <TextBox x:Name="txtCount" Width="100" materialDesign:HintAssist.Hint="数量"/>
                <Button Content="读取寄存器" Command="{Binding ReadRegistersCommand}"/>
                <DataGrid ItemsSource="{Binding Registers}" AutoGenerateColumns="False">
                    <DataGrid.Columns>
                        <DataGridTextColumn Header="地址" Binding="{Binding Address}"/>
                        <DataGridTextColumn Header="值" Binding="{Binding Value}"/>
                    </DataGrid.Columns>
                </DataGrid>
            </StackPanel>
        </GroupBox>
    </Grid>
</Window>

2. ViewModel逻辑绑定

// ViewModels/MainViewModel.cs
using MaterialDesignThemes.Wpf;
using System.Windows.Input;

public class MainViewModel : ObservableObject
{
    private readonly ModbusTcpService _modbusService = new ModbusTcpService();
    public ObservableCollection<ModbusDataModel> Registers { get; } = new();

    public ICommand ConnectCommand => new RelayCommand(async () => 
    {
        await _modbusService.ConnectAsync(txtIP.Text, int.Parse(txtPort.Text));
        ReadRegisters();
    });

    public ICommand ReadRegistersCommand => new RelayCommand(() =>
    {
        var values = _modbusService.ReadHoldingRegisters(0, 10); // 读取0-9号寄存器
        Registers.Clear();
        for (int i = 0; i < values.Length; i++)
        {
            Registers.Add(new ModbusDataModel { 
                Address = 0 + i, 
                Value = values[i] 
            });
        }
    });

    private void ReadRegisters()
    {
        // 异步读取逻辑
    }
}

四、关键调试

  1. CRC校验验证

    // 工具类方法
    public static byte[] CalculateCrc(byte[] data)
    {
        ushort crc = 0xFFFF;
        foreach (byte b in data)
        {
            crc ^= (ushort)b;
            for (int i = 0; i < 8; i++)
            {
                if ((crc & 0x0001) != 0)
                    crc >>= 1;
                else
                    crc = (ushort)((crc >> 1) ^ 0xA001);
            }
        }
        return new[] { (byte)crc, (byte)(crc >> 8) };
    }
    
  2. 异常处理策略

    try
    {
        await _modbusService.ConnectAsync(ip, port);
    }
    catch (SocketException ex)
    {
        ShowErrorMessage($"连接失败:{ex.Message}");
        Log.Error(ex, "网络连接异常");
    }
    
  3. 性能优化

    • 连接池管理:复用TcpClient实例
    • 批量操作:使用WriteMultipleRegisters替代多次单次写入
    • 异步处理:通过async/await避免界面冻结

五、扩展功能实现

  1. 数据曲线监控

    // 使用LiveCharts库
    public SeriesCollection TemperatureSeries { get; } = new();
    
    // 实时更新数据
    private void UpdateChart(double value)
    {
        TemperatureSeries.Add(new LineSeries
        {
            Values = new ChartValues<double> { value }
        });
    }
    
  2. 十六进制查看器

    // 显示原始Modbus报文
    public string HexDisplay
    {
        get
        {
            return BitConverter.ToString(data).Replace("-", "");
        }
    }
    

参考代码 C#实现Modbus TCP 通讯测试软件 www.youwenfan.com/contentcnq/111967.html

六、测试用例示例

测试场景 输入参数 预期结果
读取保持寄存器 IP=192.168.1.100, Port=502, 地址=0, 数量=10 返回10个寄存器的16进制值
写入单个寄存器 地址=40001, 值=25 寄存器值变为25(0x19)
批量写入 地址=40002, 值数组= 三个寄存器依次写入成功

七、部署建议

  1. 依赖项打包

    • 使用ILMerge合并DLL文件
    • 包含NModbus许可证文件
  2. 安装包制作

    # 使用Inno Setup创建安装程序
    [Files]
    Source: "ModbusTester.exe"; DestDir: "{app}"; Flags: ignoreversion
    Source: "NModbus.dll"; DestDir: "{app}"; Flags: ignoreversion
    
posted @ 2026-02-03 11:10  躲雨小伙  阅读(0)  评论(0)    收藏  举报