ModbusPal模拟器使用说明

一、 工具介绍

ModbusPal是一个开源工具,用于模拟和测试 Modbus 设备和网络。它允许用户创建虚拟的 Modbus 从站(Slave),并配置这些从站的寄存器和通信参数,从而方便地测试 Modbus 主站(Master)的通信和操作。

ModbusPal主要有以下优势:

  1. 可以通过脚本方式快速部署模拟器,且支持TCP端口的自定义
  2. 从站寄存器值可通过模拟函数随机生成
  3. 开源免费

本文主要介绍如何部署ModbusPal模拟器及自行定制的方式。同时也会介绍如何在Node-Red上读取ModbusPal模拟生成的数据。

二、 工具源码及二次开发说明

ModbusPal的工具源码可从https://sourceforge.net/p/modbuspal/code/HEAD/tree/获取。需要说明的是,开源发布的最新版本是1.6c。但本文介绍的ModbusPal是在1.6c的基础上经过二次开发生成的。

之所以需要进行二次开发,主要是因为ModbusPal支持配置同一IP同一端口下配置多个Slave。但据此生成的配置文件在ModbusPal重启后会无法导入。相关调用栈如下:

6月 12, 2024 2:59:04 下午 modbuspal.main.ModbusPalPane$20 run
严重: null
java.lang.NullPointerException: Cannot invoke "modbuspal.slave.ModbusSlaveAddress.getIpAddress()" because "id" is null
        at modbuspal.main.ModbusPalProject2.getMatchingSlaveAddress(ModbusPalProject2.java:178)
        at modbuspal.main.ModbusPalProject2.getModbusSlave(ModbusPalProject2.java:204)
        at modbuspal.main.ModbusPalProject2.getModbusSlave(ModbusPalProject2.java:270)
        at modbuspal.main.ModbusPalProject.addModbusSlave(ModbusPalProject.java:1275)
        at modbuspal.main.ModbusPalProject.loadSlaves(ModbusPalProject.java:377)
        at modbuspal.main.ModbusPalProject.<init>(ModbusPalProject.java:155)
        at modbuspal.main.ModbusPalProject.load(ModbusPalProject.java:100)
        at modbuspal.main.ModbusPalPane.loadProject(ModbusPalPane.java:1355)
        at modbuspal.main.ModbusPalPane$20.run(ModbusPalPane.java:1949)
        at java.base/java.lang.Thread.run(Thread.java:1583)

为解决以上问题,对ModbusPal进行了二次开发,相关PATCH如下:

diff --git a/src/modbuspal/main/ModbusPalProject.java b/src/modbuspal/main/ModbusPalProject.java
index 2dcfdb8..35d1b17 100644
--- a/src/modbuspal/main/ModbusPalProject.java
+++ b/src/modbuspal/main/ModbusPalProject.java
@@ -488,8 +488,18 @@ implements ModbusPalXML
                slaveAddress = XMLTools.getAttribute(ModbusPalXML.XML_SLAVE_ID_ATTRIBUTE, parentSlave);
                try
                {
-                       ModbusSlaveAddress msa = new ModbusSlaveAddress( InetAddress.getByName( slaveAddress ) );
-                       slave = getModbusSlave(msa);
+                    ModbusSlaveAddress msa;
+                    int openParenIndex = slaveAddress.indexOf('(');
+                    if (openParenIndex == -1) {
+                        msa = new ModbusSlaveAddress( InetAddress.getByName( slaveAddress ) );
+                    } else {
+                        String ipaddress = slaveAddress.substring(0, openParenIndex);
+                        msa = new ModbusSlaveAddress( InetAddress.getByName(ipaddress));
+                        int closeParenIndex = slaveAddress.indexOf(')');
+                        String rtu_id = slaveAddress.substring(openParenIndex + 1, closeParenIndex);
+                        msa.setRtuAddress(Integer.parseInt(rtu_id));
+                    }
+                   slave = getModbusSlave(msa);
                }
                catch (UnknownHostException exception)
                {
diff --git a/src/modbuspal/slave/ModbusSlave.java b/src/modbuspal/slave/ModbusSlave.java
index 927f76e..91680e5 100644
--- a/src/modbuspal/slave/ModbusSlave.java
+++ b/src/modbuspal/slave/ModbusSlave.java
@@ -635,7 +635,16 @@ implements ModbusPalXML, ModbusConst
                String id = XMLTools.getAttribute(XML_SLAVE_ID_ATTRIBUTE, node);
                try
                {
-                       slaveId = new ModbusSlaveAddress( InetAddress.getByName( id ) );
+                    int openParenIndex = id.indexOf('(');
+                    if (openParenIndex == -1) {
+                        slaveId = new ModbusSlaveAddress( InetAddress.getByName( id ) );
+                    } else {
+                        String ipaddress = id.substring(0, openParenIndex);
+                        slaveId = new ModbusSlaveAddress( InetAddress.getByName(ipaddress));
+                        int closeParenIndex = id.indexOf(')');
+                        String rtu_id = id.substring(openParenIndex + 1, closeParenIndex);
+                        slaveId.setRtuAddress(Integer.parseInt(rtu_id));
+                    }
                }
                catch (UnknownHostException exception)
                {

三、 ModbusPal基本使用

1. 软件依赖

1.1 OpenJDK

ModbusPal是使用Java开发,其形态为一个JAR(Java Archive)包。因此要运行ModbusPal,需要安装JDK环境。在Windows上安装JDK环境可使用微软官方提供的JDK安装包,见:https://www.microsoft.com/openjdk

1.2 RXTX

RXTX是一个开源的Java库,用于通过串行和并行端口进行通信。它提供了对串行通信的访问,使得Java应用程序能够读取和写入串口数据。ModbusPal使用该库进行串口通信。因此在安装JDK后,需要将rxtx相关的库文件拷贝到JDK目录下。假设安装的Microsoft OpenJDK的安装路径为C:\Program Files\Microsoft\jdk-21.0.3.9-hotspot,那么需要将rxtxParallel.dll和rxtxSerial.dll两个文件拷贝到C:\Program Files\Microsoft\jdk-21.0.3.9-hotspot\bin目录下。

需要注意的是,rxtxParallel.dll和rxtxSerial.dll 这两个文件在windows上有x86和x64平台之分。还需要根据使用的windows平台选择合适的代码库文件。

2. 软件启动

ModbusPal的启动指令比较简单,主要有两种情况:

  1. 无配置文件启动:

java -jar ModbusPal.jar

  • 使用配置文件启动:

java -jar ModbusPal.jar -loadFile=”<配置文件地址>”

3. 软件界面

软件的初始界面如下:

从上图可以看到,软件上主要分为五个区域:Link settings、Project、Tools、Modbus Slave、Automation。下面简单介绍界面这五个区域的作用:

3.1 Link settings

该区域我们主要关注”TCP/IP“页面的配置。”Serial”和“Replay”页面不在讨论范围,有兴趣的同事可以自行摸索。

在该区域中主要使用的功能有:

  1. 配置端口号(默认为502)
  2. 运行模拟器(Run按钮)
  3. 运行状态显示

上图中灰色图像即为模拟器运行状态指示。共有三种情况:

  1. 与Modbus Master的通信断开,图像会显示为默认灰色。
  2. 与Modbus Master通信建立,但存在通信异常(如地址错误等),图像会显示为红色。
  3. 与Modbus Master通信建立且通信正常,图像为显示为绿色。

3.2 Project

该区域我们主要关注”load”按钮和“Save”按钮。前者是导入ModbusPal的配置文件,后者是将当前在ModbusPal上的配置保存为配置文件。配置文件的后缀为xmpp。

3.3 Tools

该区域我们主要关注”Console”按钮。该按钮显示Java运行日志,常用于问题定位。

3.4 Automation

该区域主要用于配置自动生成器,即用于寄存器的数据生成。生成器有三类:线性、随机、正弦波。以配置线性生成器为例,点击 “Add”按钮添加一个生成器。我们可以在出现的生成器上修改生成器名字,如下:

点击生成器右侧的眼睛图标,会出现生成器的配置界面,我们可以根据实际情况对生成器的配置进行修改。如下图所示,我们创建一个线性生成器,然后配置最小值为1,最大值为3600,生成周期为3600秒:

其它两类生成器的配置逻辑接近,此处不再赘述。

3.5 Modbus Slave

该区域主要用于配置Slave节点,假设Master设备的IP地址为192.168.1.1,我们需要创建10个字节,Slave ID从1到10。那么点击该区域的“Add”按钮之后,会弹出一个Slave配置界面,我们可以输入“192.168.1.1(1-10)”,如下:

点击上面区域的“Add”按钮之后,将会在Modbus Slave区域生成10个从设备,如下:

我们可以点击某个设备右侧的眼睛按钮,弹出寄存器配置界面,如下:

在该界面中,我们可以点击 “Add”按钮创建寄存器,如下创建地址40001 ~ 40010共10个寄存器:

点击“Add”按钮之后,我们可以看到这个节点在Holding Registers中有10个寄存器,每个寄存器的默认值为0:

我们可以手动给这些寄存器配置不同的值,也可以将某个寄存器的值与生成器绑定。比如若需要将40001和前面创建的生成器绑定,则可以选择40001这个寄存器,然后点击 “Bind”按钮,弹出绑定界面:

绑定之后,当我们启动生成器时,可以在该界面看到寄存器的值一直在变化,如下:

4 示例:配置ModbusPal并通过Node-Red获取Modbus数据

我们通过一个简单的例子来熟悉ModbusPal的使用。

  1. 在PC(IP:192.168.1.10)上配置一个Modbus Slave节点,Master IP地址为192.168.1.1,Slave ID为1,寄存器配置为Holding Registers,地址为40001 ~ 40010,其中40001地址的值配置为2,TCP端口配置为502。如下:
  1. 点击Run启动模拟器
  1. 在设备侧(IP:192.168.1.1)启动Node-Red服务,并配置Node-Red节点。需要注意的是,如果Node-Red没有安装modbus相关节点,需要在节点管理中安装node-red-contrib-modbus,如下:
  1. Node-Red中Modbus Client节点配置如下:
  1. Node-Red中Modbus-Read节点配置如下。此处需要注意的是,ModbusPal中的寄存器地址是以1为起始,而Node-Red中是以0为起始,所以寄存器起始地址要写为40000
  1. 添加一个Modbus Response节点用于显示信息。部署之后效果如下:

完整的Node-Red Json可见附件flows.json

四、 批量部署ModbusPal模拟器

本节主要介绍如何使用已有的配置文件批量部署ModbusPal模拟器。

  1. 以前面示例中ModbusPal的配置为例,我们配置后将其保存为配置文件,命名为template.xmpp。
  2. 修改template.xmpp,生成模板文件。template.xmpp文件内容如下:
  1. 我们需要将其修改为模板文件,将端口502替换为{port},将IP地址192.168.1.1替换为{device_ip},将节点名D1替换为{device_name}。替换后的文件如下:
  1. 使用windows powershell运行脚本run.ps1,根据模板文件生成批量配置文件。run.ps1脚本可见如下文件。

需要注意的是:

  • 该脚本还需要两个参数:Master端的IP地址,需要部署的模拟器数量。
  • template.xmpp需要和脚本放置在同一目录下。

留言

您的邮箱地址不会被公开。 必填项已用 * 标注