0.13.1 到 1.0.0
1.0.0 是 Apache PLC4X 的第一个大版本发布,此前不断累积的不兼容变更在这一刻一次性完成,而不是分散到其后的五个版本中。本页介绍的变更无论你使用哪种语言调用 PLC4X 都适用——它们涉及连接串、标签地址和默认值,而不涉及任何一种 API。
API 的变更因语言而异,各自记录在对应的页面中:
| 请先阅读本页,然后再看你所用语言的页面。大多数升级在连接串和标签地址上花费的时间,远比在 API 上花费的多。 |
|---|
一段话概括
标签地址在所有驱动、所有语言中都使用同一种写法来选择数组元素。每个表示时长的连接串参数都以 -ms 结尾,每项 TLS 设置都位于 tls. 之下,每项传输设置都写在其传输协议的代码之下。驱动会拒绝它不支持的传输方式。OPC UA 与 TLS 传输默认校验证书,这意味着过去那种无保护的连接现在会失败,并给出失败原因。Java 需要 21,Go 需要 1.27。
标签地址:统一的数组写法
这是最可能影响你代码的变更,因为它出现在你的应用传给 addTagAddress(…) 的字符串中。
数组选择现在写在数据类型之前,单个元素使用 [n],闭区间使用 [lo..hi]:
holding-register:1:INT[4] (1)
holding-register:1[0..3]:INT (2)| 1 | 之前 —— 四个寄存器的计数 |
|---|---|
| 2 | 现在 —— 索引 0 到 3 的元素 |
这取代了四种互不兼容的写法。[4] 在七种类标签中表示“四个元素”,在两类中表示“第五个元素”;现在它在所有地方都表示一个元素。
旧形式的地址不再能被解析,错误信息会给出应当写入的地址——因此升级会显式报告这一变化,而不是悄悄返回不同的数据。恰好存在三种例外:地址仍然可以解析,只是其含义发生了变化,因此无法给出任何警告:
- Firmata 地址不携带数据类型(
3[4]),所以括号并未移动。3[4]原先从引脚 3 开始读取四个引脚,现在读取一个引脚,即第五个。请改写为3[0..3]。 - PLC4Go 的 ADS 驱动与 PLC4J 的不同,它把
[n]当作计数使用。MAIN.g_arr[3]原先读取三个元素,现在读取索引 3 处的元素。请改写为MAIN.g_arr[0..2]。 - PLC4Go 的 Firmata 驱动因为与 PLC4J 相同的原因而发生变化。
发生变化的形式,按驱动列出:
| 驱动 | 0.13.1 | 1.0.0 |
|---|---|---|
| S7 | %DB42:28.0:BYTE[4] | %DB42:28.0[0..3]:BYTE |
| S7(字符串) | %DB1:0:STRING(20)[4] | %DB1:0[0..3]:STRING(20) |
| Modbus | holding-register:1:INT[4] | holding-register:1[0..3]:INT |
| SLMP | D100:INT[4] | D100[0..3]:INT |
| ADS(直接) | 0x4020/0:DINT[4] | 0x4020/0[0..3]:DINT |
| EtherNet/IP | myArray[0]:DINT:4 | myArray[0..3]:DINT |
| Profinet | tag:INT[4] | tag[0..3]:INT |
| Profinet-NG | 1.2.INPUT.0:INT[4] | 1.2.INPUT.0[0..3]:INT |
| 模拟(Simulated) | RANDOM/foo:INT[4] | RANDOM/foo[0..3]:INT |
| KNXnet/IP(Go) | 1.2.3#4B1C:UINT[4] | 1.2.3#4B1C[0..3]:UINT |
OPC UA、C-Bus、BACnet/IP 以及 KNXnet/IP 的组地址保持不变。OPC UA 正是这套共享表示法从中提取的驱动;另外三个则用括号表示并非数组选择的含义。
即使你的地址一个都不变,也有两条规则值得了解:
- 单个索引与单元素范围不再是同一回事。
myTag[4]选择一个元素并产生标量,myTag[4..4]产生一个包含单个元素的列表。 - 不选择任何内容的地址现在表示取整个值。 对于标量这没有变化;对于数组则表示所有元素——但仅限于能够向设备查询长度的驱动(OPC UA、ADS、UMAS)。
完整语法,包括 PLC 声明数组起点不为零时使用的 ;base 后缀,见 数组寻址 页面。
连接串参数:统一的词汇
参数名称现在在 PLC4J 和 PLC4Go 中拼写一致。以毫秒为单位的时长以 -ms 结尾,TLS 设置位于 tls. 之下,而面向某种传输的参数不再重复该传输的代码,因为前缀已经提供了它。
旧名称是被移除,而非弃用。提供其中任何一个都会被报告为未知参数,并指出替代名称——而且该设置不会生效。
| 0.13.1 | 1.0.0 |
|---|---|
request-timeout | request-timeout-ms |
timeout-request (ads) | request-timeout-ms |
connect-timeout | connect-timeout-ms |
read-timeout | read-timeout-ms |
write-timeout | write-timeout-ms |
session-timeout | session-timeout-ms |
channel-lifetime | channel-lifetime-ms |
min-channel-lifetime | min-channel-lifetime-ms |
ha-heartbeat-interval | ha-heartbeat-interval-ms |
ha-failover-timeout | ha-failover-timeout-ms |
表 1. 持续时间
建立套接字连接与完成协议握手是两个设置,而不是一个,因此现在它们有两个名称。connect-timeout-ms 是套接字连接;COTP 握手和 OPC UA 协商步骤是 handshake-timeout-ms:
| 0.13.1 | 1.0.0 |
|---|---|
cotp.cotp-connection-timeout | cotp.handshake-timeout-ms |
negotiation-timeout (opcua) | handshake-timeout-ms |
| 0.13.1 | 1.0.0 |
|---|---|
tcp.tcp-no-delay | tcp.no-delay |
cotp.cotp-tpdu-size | cotp.tpdu-size |
tls.tls-version | tls.version |
表 2 传输参数不再重复其传输的代码
| 0.13.1 | 1.0.0 |
|---|---|
tls.verify-ssl | tls.verify |
key-store-file (opcua) | tls.keystore |
key-store-password | tls.keystore-password |
key-store-type | tls.keystore-type |
trust-store-file | tls.trust-store |
trust-store-password | tls.trust-store-password |
trust-store-type | tls.trust-store-type |
表 3 TLS 设置归入 tls. 之下
信任库去掉了 -file,原因与密钥库相同:这些名称中的每一个都指明了这是一个库,再说一遍毫无意义。
由协议规范固定的名称保留其原有的拼写和单位。SLMP 的 monitoring-timer 是 3E 请求帧中的一个字段,使用的是协议自身的单位,而不是毫秒,因此保持不变。
OPC UA 驱动上的 insecure-certificate-verification 改为 tls.verify,含义相反。原先设置 insecure-certificate-verification=true 的连接现在必须设置 tls.verify=false。这一项不只是改名:如果漏掉了,就会采用新的默认值,即校验证书服务器。 |
|---|
未知参数现在会被报告
PLC4X 无法识别的参数会按名称报告,如果存在相近的已知名称,也会一并给出,在 PLC4J 和 PLC4Go 中皆是如此。它仍然只是一条警告——多余的参数不会让一个本来能正常工作的连接失败——所以升级后的首次运行是查看日志的好时机。
该报告还知道连接实际使用的是哪一种传输,因此属于另一种传输的参数(在 TCP 连接上的 serial.baud-rate)会被指出为投错地方,而不是被放过。
PLC4Go 的 OPC UA 驱动过去遇到未知选项会拒绝连接。现在它会像其他所有驱动一样发出警告。
驱动会拒绝它不支持的传输
将驱动与它不讲的传输配对——例如把 S7 驱动(讲 COTP)与普通的 tcp 传输配对——现在会在连接时失败,并抛出一个异常,指明该驱动支持哪些传输。此前它会先连上,再以一种难懂得多的方式失败。
如果这种非标准配对是有意为之,请设置 allow-unsupported-transport=true。这只绕过驱动支持检查;传输本身仍必须是已注册的。
默认安全
若干默认值从“在任何地方都能工作”转变为“保护连接”。其中每一项都把过去毫无防护建立起来的连接,变成一个会失败并说明原因的连接,因此它们需要明确取舍,而不只是改名。
OPC UA
security-policy默认变为Basic256Sha256而不再是NONE。受保护的通道要求在通道打开之前就已知服务器的证书,驱动不再在服务发现时回退到比所配置通道更弱的通道——过去它会静默地这样做。可用server-certificate-file指定证书,或用tls.trust-store指定信任库;如果端点无需服务发现,可设置discovery=false;若希望像以前一样接受不受保护的通道,则请求security-policy=NONE。- 受保护的通道还需要一对客户端密钥。可用
tls.keystore(配合tls.keystore-password和tls.keystore-type)提供,否则驱动会生成一张一次性的自签名证书,而对客户端进行认证的服务器不会接受这样的证书。 - 对既不签名也不加密的通道,驱动拒绝发送用户名和密码。
allow-insecure-credentials=true仍会发送,但会给出警告。 - 端点必须同时匹配所请求的安全策略以及所请求的消息安全模式,并且会选中匹配到的最强端点,而不是最弱的。
TLS 传输
TLS 传输现在会检查服务器的证书是否是为它所连接的主机签发的。ignore-common-name 早已声明却从未被读取,因此这项检查过去完全没有发生。
如果连接的设备证书中标明的名称并非实际访问所用的地址,连接现在会失败。tls.trust-store(配合 tls.trust-store-password 和 tls.trust-store-type)用于指定要信任的证书,取代 JVM 自带的公共权威机构;ignore-common-name=true 则恢复旧行为,并记录一条警告说明这一点。
ctrlX
驱动不再信任随驱动 jar 一同发布的博世出厂默认证书,也不再接受与签发名称无关的任意主机证书。连接到仍使用出厂证书的设备现在会失败;allow-factory-default-certificate=true 可恢复旧行为,并附带警告。
plc4x 代理驱动
- 默认使用 TLS 传输而非明文 TCP。现有的明文连接必须显式指定传输方式:
plc4x:tcp://…。 - 连接时强制要求用户名/密码认证。不带凭据或凭据无效的连接请求会被拒绝,并以
ACCESS_DENIED结束握手。请使用新增的username和password参数进行配置。
日志中的凭据
携带机密的值会在声明处做出标记,无论在 PLC4J 还是 PLC4Go 中,只要配置被记录,它们都会显示为 <redacted>。这取代了以往依据参数名进行猜测的做法——那种做法永远只能跟在参数后面一步。
PLC4Go 过去会在 debug 级别原样记录连接字符串,因此密码曾在二十处调用点以明文进入日志。现在不会再这样了。嵌套在另一个连接字符串内部的连接字符串——即代理驱动的 remote-connection-string——会作为连接字符串本身被脱敏。
消息嵌套深度有上限
每个生成的解析器都会拒绝类型嵌套深度超过 1024 层的消息,在 Java、Go、C 和 Python 中皆是如此。有些类型包含自身——BACnet 的构造数据中嵌套着更多构造数据,类型 24 的 OPC UA 变体中嵌套着更多变体——因此值树的深度由发送方决定,而在线路上每深一层只需花费一个字节。
为确实嵌套更深的消息设置 PLC4X_MAX_NESTING_DEPTH。它在所有绑定中含义相同;若取值不是正数,则保留默认值并给出警告。项目自身测试套件中最深的消息嵌套了 36 层。
| 在此过程中,PLC4Go 的默认值从 255 提升到了 1024,因此无论哪个绑定读取,这个变量的同一设置值现在都代表同一深度。 |
|---|
协议层面的修正
这些改动会影响线路上传输的内容,或影响某个值被解码后的结果。它们属于修正,但已经适应旧行为的系统需要留意。
- S7
DATE_AND_TIME:星期字段的半字节现在按照 S7 的方式编号——周日为 1,周六为 7。两个绑定此前都用各自日期库中的值填充它,而该库从周一开始计数,因此写入 PLC 的每一个DATE_AND_TIME所携带的星期值都少了一天。只有IEC61131_DATE_AND_TIME受影响;DTL 变体早已使用西门子的编号方式。 - IEC 60870-5-104:S 格式确认帧现在按照标准要求,由所接收帧的发送序号推导得出。此前驱动发送的是完全不同的序号。会检查确认帧的站点将看到不同的——也是正确的——数值。
- EtherNet/IP 数据类型码:
LWORD从0x00D3移到了0x00D4,STRINGI从0x00DD移到了0x00DE。此前每个类型都与另一个类型共用同一个值,而重复的键会从生成的查找表中被静默丢弃,导致二者根本无法解析。任何硬编码旧LWORD值的代码,实际寻址的都是一个DWORD。 - EtherNet/IP 字符串写入现在发出的结构与读取路径解析的结构一致,因此驱动写入的内容读回来是同一个值。以
:STRING寻址的写入现会被拒绝——请以:STRUCTURED写入字符串。
不合理的元素数量将被拒绝
标签地址中若指定了不合理的元素数量,现在会被视为无效地址,而不再由驱动照做。元素计数是一次分配请求,过去它被照单全收。这会影响 S7、ADS、Firmata、Modbus、Profinet、Profinet-NG 以及模拟驱动。在所有这些驱动中,过大到无法表示为数字的计数,过去会以 NumberFormatException 的形式逸出,而不是标签解析器所承诺的无效标签错误。
接下来阅读
评论
登录后参与评论
KnowForge