用户

发现驱动程序及其配置

qianmoQqianmoQ· 更新于 2026-10-01· 阅读 20 分钟· 0 次阅读

登录后可跨设备保存划线和私人笔记登录

本页面面向将 PLC4X 集成到其他工具中的开发者:例如连接对话框、配置表单、流水线中的校验步骤,或基于流程的编辑器中的节点。

这些内容无需硬编码。每个驱动都会附带一份对自身的描述,PLC4X 可以在运行时把这份描述交给你:classpath 上有哪些驱动、每个驱动支持哪些传输方式,以及每种组合接受哪些配置参数——包括参数类型、是否必填以及默认值。

这与本站协议页面]所使用的元数据完全相同,因此你的工具显示的内容与文档所述永远不会出现偏差。

本 API 属于 PLC4J(Java)。PLC4Go、PLC4Py 和 PLC4C 目前尚未提供等效能力——PLC4Go 的 GetMetadata() 描述的是一个连接,而非驱动。

起点在哪里

一切都挂接在 PlcDriverManager 上:

import org.apache.plc4x.java.api.PlcDriverManager;

PlcDriverManager driverManager = PlcDriverManager.getDefault();

驱动是通过 Java 的 ServiceLoader 查找的,因此“我有哪些驱动”从字面上讲就是“类路径上有哪些驱动 jar”。如果你的工具把插件加载到了自己的类加载器中,请显式地把它传过来:

import org.apache.plc4x.java.DefaultPlcDriverManager;

PlcDriverManager driverManager = new DefaultPlcDriverManager(myPluginClassLoader);

我有哪些驱动?

Set<String> protocolCodes = driverManager.getProtocolCodes();      (1)
PlcDriver driver = driverManager.getDriver("s7");                  (2)
String humanReadable = driver.getProtocolName();                   (3)
1简短代码——即 s7://10.0.0.1 中的 s7。
2如果 classpath 中没有该代码对应的驱动,将抛出 PlcConnectionException。
3显示名称,例如 Siemens S7 (Basic)。接线时用代码,给人看时用名称。
getProtocolCodes() 返回的是无序的 Set。在展示给任何人之前请先排序,否则你的下拉列表会在每次运行之间自行打乱顺序。

如果你已经有一个连接串,只是想拿到其背后的驱动,可以用 driverManager.getDriverForUrl(url),而不必自己解析 scheme。

加上 plc4j-driver-all 依赖后,上面的代码会得到 18 个驱动:

ab-ethadscanopen
eipfirmatagenericcan
iec-60870-5-104knxnet-iplogix
modbus-asciimodbus-rtumodbus-tcp
opcuaplc4xs7
simulatedslmpumas

驱动程序支持哪些传输方式?

其余内容都位于 PlcDriverMetadata:

import org.apache.plc4x.java.api.metadata.PlcDriverMetadata;

PlcDriverMetadata metadata = driver.getMetadata();

List<String> transports = metadata.getSupportedTransportCodes();   (1)
Optional<String> preferred = metadata.getDefaultTransportCode();   (2)
boolean canDiscover = metadata.isDiscoverySupported();             (3)
1例如 [tcp, tls, tls-psk, udp, test] 对应 modbus-tcp。
2当连接字符串未指定传输方式时,驱动所使用的传输方式。请预先选中此项。
3驱动是否能够在网络上搜索设备——可用它来启用“扫描”按钮。

有两点需要处理:

  • 过滤掉 test。 它是 PLC4X 自身单元测试所使用的内存传输方式。它会出现在列表中,但并非用户可以选择的选项。本站的协议页面正是出于这一原因将其过滤掉。
  • 列表可能为空。 simulated 不会报告任何传输方式,因为它从不与外界通信。不要假定至少会有一个。
List<String> selectable = metadata.getSupportedTransportCodes().stream()
    .filter(code -> !"test".equals(code))
    .toList();

驱动的配置是什么?

import org.apache.plc4x.java.api.metadata.OptionMetadata;

Optional<OptionMetadata> protocolOptions =
    metadata.getProtocolConfigurationOptionMetadata();

这是一个 Optional,因为驱动可能不声明任何自己的选项——simulated 就没有。

该驱动的某种传输的配置是什么?

请始终请求配对组合,而不要单独请求传输:

Optional<OptionMetadata> transportOptions =
    metadata.getTransportConfigurationOptionMetadata("tcp");

驱动的传输配置是按「驱动 + 传输」的组合对外暴露的,因此请始终通过你正在配置的驱动来查询该配置,而不是为每种传输代码缓存一份表。

一个配置定义了哪些参数?

OptionMetadata 会给出参数列表,Option 则会描述每一项:

import org.apache.plc4x.java.api.metadata.Option;

List<Option> all = protocolOptions.get().getOptions();
List<Option> mandatory = protocolOptions.get().getRequiredOptions();   // convenience filter
方法返回值该返回值的用途
getKey()String出现在连接字符串中的参数名称。
getType()OptionType选择合适的控件并校验输入。见下文。
isRequired()boolean将该字段标记为必填,为空时禁止提交。
getDefaultValue()Optional<Object>预填充该字段。为空表示没有默认值。
getDescription()String工具提示或帮助文本。
isSecret()boolean渲染为密码字段,并将其排除在日志之外。
getSince()Optional<String>引入该选项的 PLC4X 版本,例如 0.13.0。只有部分选项带有该信息。

OptionType 是一个小型枚举——BOOLEAN、INT、LONG、FLOAT、DOUBLE、STRING、FILE、STRUCT:

  • FILE 表示路径——提供一个文件选择器(用于密钥库和证书)。
  • STRUCT 是驱动程序会从其字符串形式解析出的复合值,例如 ADS 的 target-ams-net-id。将其视为自由文本,由驱动程序进行校验;其语法记录在驱动程序自己的协议页面上。

大多数选项都是可选的并带有默认值。ads 是默认集中唯一真正要求输入的驱动程序——四个必填参数,其中两个是 STRUCT:

target-ams-net-id    STRUCT   required
target-ams-port      INT      required
source-ams-net-id    STRUCT   required
source-ams-port      INT      required

将选项转换回连接字符串

这些键将原样用作查询参数,只有一条规则:传输层选项使用传输层代码作为命名空间,协议选项则不加命名空间。

s7://10.0.0.1?pdu-size=2048&cotp.local-rack=1&cotp.remote-slot=2
     ^                      ^                 ^
     |                      |                 └── transport option, prefixed with "cotp."
     |                      └── transport option
     └── protocol option, no prefix

因此,在构建该字符串时,请恰好为来自 getTransportConfigurationOptionMetadata(…​) 的值加上前缀:

String key = (transportCode == null) ? option.getKey()
                                     : transportCode + "." + option.getKey();
标记为 isSecret() 的任何内容都会被合并到同一个字符串中。PLC4X 在自身的日志输出中会对这些参数进行脱敏处理——在记录、显示或持久化连接字符串之前,你也应当进行同样的脱敏处理。

完整示例

本程序会打印完整的目录——包括每个驱动程序、其传输方式,以及每个选项的类型、是否必填和默认值:

import org.apache.plc4x.java.api.PlcDriver;
import org.apache.plc4x.java.api.PlcDriverManager;
import org.apache.plc4x.java.api.metadata.Option;
import org.apache.plc4x.java.api.metadata.OptionMetadata;
import org.apache.plc4x.java.api.metadata.PlcDriverMetadata;

import java.util.List;
import java.util.TreeSet;

public class DriverCatalog {

    public static void main(String[] args) throws Exception {
        PlcDriverManager driverManager = PlcDriverManager.getDefault();

        for (String protocolCode : new TreeSet<>(driverManager.getProtocolCodes())) {
            PlcDriver driver = driverManager.getDriver(protocolCode);
            PlcDriverMetadata metadata = driver.getMetadata();

            System.out.println("== " + protocolCode + " (" + driver.getProtocolName() + ")");
            System.out.println("   discovery supported : " + metadata.isDiscoverySupported());
            System.out.println("   default transport   : "
                + metadata.getDefaultTransportCode().orElse("<none>"));

            // "test" is an in-memory transport used by PLC4X's own unit tests.
            List<String> transports = metadata.getSupportedTransportCodes().stream()
                .filter(code -> !"test".equals(code))
                .toList();
            System.out.println("   transports          : " + transports);

            metadata.getProtocolConfigurationOptionMetadata()
                .ifPresent(options -> print("   protocol options", options, null));

            for (String transportCode : transports) {
                metadata.getTransportConfigurationOptionMetadata(transportCode)
                    .ifPresent(options ->
                        print("   transport options (" + transportCode + ")", options, transportCode));
            }
            System.out.println();
        }
    }

    private static void print(String heading, OptionMetadata metadata, String prefix) {
        System.out.println(heading + ":");
        for (Option option : metadata.getOptions()) {
            // In a connection string, transport options are namespaced with the transport code.
            String key = (prefix == null) ? option.getKey() : prefix + "." + option.getKey();
            System.out.printf("     %-34s %-8s %-9s %-7s %s%n",
                key,
                option.getType(),
                option.isRequired() ? "required" : "optional",
                option.isSecret() ? "secret" : "",
                option.getDefaultValue().map(v -> "default=" + v).orElse(""));
        }
    }
}

ADS 驱动的输出如下:

== ads (Beckhoff TwinCat ADS)
   discovery supported : true
   default transport   : tcp
   transports          : [tcp]
   protocol options:
     target-ams-net-id                  STRUCT   required
     target-ams-port                    INT      required
     source-ams-net-id                  STRUCT   required
     source-ams-port                    INT      required
     request-timeout-ms                 INT      optional          default=4000
     max-data-type-table-depth          INT      optional          default=20
     load-symbol-and-data-type-tables   BOOLEAN  optional          default=true
   transport options (tcp):
     tcp.connect-timeout-ms             INT      optional          default=5000
     tcp.read-timeout-ms                INT      optional          default=0
     tcp.write-timeout-ms               INT      optional          default=0
     tcp.no-delay                       BOOLEAN  optional          default=true
     tcp.keep-alive                     BOOLEAN  optional          default=false
     tcp.send-buffer-size               INT      optional          default=81920
     tcp.receive-buffer-size            INT      optional          default=81920
     tcp.local-address                  STRING   optional
     tcp.local-port                     INT      optional          default=0

要运行它,你需要在类路径(classpath)中包含一个驱动程序。plc4j-driver-all 会列出所有驱动程序:

<dependency>
  <groupId>org.apache.plc4x</groupId>
  <artifactId>plc4j-driver-all</artifactId>
  <version>1.0.0</version>
</dependency>

需要注意的事项

  • getProtocolCodes() 是一个无序的 Set——显示前请先排序。
  • 将 test 传输方式从用户可见的任何内容中过滤掉。
  • 一个驱动可能报告没有任何传输方式(simulated),也没有任何选项(simulated 再次出现)。每一个可能缺失的元数据访问器都会返回一个 Optional 或一个空列表——它们都不会返回 null,但同样也都不保证有内容。
  • 请求传输选项时应按“驱动与传输方式”的组合来查询,而不是按传输方式代码查询。
  • 在任何地方都要将 isSecret() 选项当作凭据处理:在 UI 中遮蔽显示,在日志中脱敏。

评论

登录后参与评论

正在加载评论…