寻址数组
所有 PLC4X 驱动选择数组元素的方式都相同,PLC4J 和 PLC4Go 皆是如此。地址本身因协议而异——可以是 S7 数据块、Modbus 寄存器或 OPC UA 节点——但其后面的部分却完全一致:
<driver-specific address>[<selection>]:<TYPE>选择位于类型之前。当某个驱动的地址不含类型时,选择即以地址结束。
同一地址在两种语言中的含义完全相同:地址只编写一次,即可从 Java 或 Go 中使用。
形式
| 书写方式 | 含义 | 示例 |
|---|---|---|
| (省略) | 整个值——一个标量,或数组的全部元素 | myTag |
[n] | 一个元素,即位于索引 n 处的那一个 | myTag[4]——第五个元素 |
[lo..hi] | 从 lo 到 hi(含两端)各元素组成的数组 | myTag[0..7]——八个元素 |
[n;base] | 某个数组中的一个元素,该数组由 PLC 声明为起始于 base | myTag[1;1]——第一个元素 |
[lo..hi;base] | 声明为起始于 base 的数组中的一个范围 | myTag[4..7;1]——四个元素,从第四个开始 |
[…][…] | 每个维度一对方括号 | myTag[1..2][0..5] |
单个索引并非数组
myTag[4] 选择一个元素,得到的是标量。myTag[4..4] 选择一个恰好只含一个元素的范围,得到的是含一个元素的列表。这一区别是有意为之:读取 getArrayInfo() 的工具正是据此判断应当渲染一个值还是一份列表。
不从零开始的数组
IEC 61131 允许将数组声明为 ARRAY[1..10] OF BYTE,此时 PLC 程序显示给你的索引便从 1 开始。书写这些索引,并加上 ;base,让驱动知道数据真正从哪里开始:
%DB42:28.0[4..7;1]:BYTE这会从地址往后三个元素处开始读取四个字节——因为对于一个从 1 开始声明的数组来说,第 4 个元素就是第四个。
有些驱动已经能从设备中获知声明的边界:ADS、UMAS 和 OPC-UA 会从符号表或地址空间中学习这些边界。在这些情况下,你可以直接写出声明的下标,于是 ;base 就成了一种意图声明,驱动会将其与设备反馈的信息进行核对。如果基址不一致,就会被报告出来,因为这意味着所写的地址是基于与 PLC 实际布局不同的布局。
多维数组
有两种写法被接受,且含义相同——第二种是 Allen-Bradley 等使用的写法:
g_matI16_2x3[1..2][3..4]
g_matI16_2x3[1..2,3..4]getAddressString() 总是生成第一种形式,因此你从标签读回的地址始终是同一种规范写法,无论它最初是如何写入的。
范围只能是最后一个维度。a[1].b[2..5] 是允许的——从一个路径指向一个结构,然后取它的一段元素。a[1..3].b 则不行:三个不相邻元素的 b 成员不构成一次连续读取,这里的任何协议都无法在单次请求中取回它。a[1..3][2] 这种带步长的切片同理。
各驱动能表达什么
记法在各处都相同;但协议能承载的内容并不相同。
| 驱动 | 维度 | 整个数组 | 说明 |
|---|---|---|---|
| OPC-UA | 多维 | 是 | 省略选择即请求整个节点 |
| ADS | 多维 | 是 | 声明的边界来自符号表 |
| UMAS | 多维 | 是 | 选择元素尚未实现,并会给出报告 |
| EtherNet/IP | 1 | 否 | CIP 索引是 uint 8,因此不能从 255 之后开始 |
| S7、Modbus、SLMP | 1 | 否 | 内存偏移;基址会被解析进地址 |
| Simulated、Profinet | 1 | 否 | 只能从第一个元素开始选择 |
| Firmata | 1 | 否 | 没有类型后缀,因此选择即地址的结尾 |
| KNXnet/IP | 1 | 否 | 仅有设备地址;属性选择会移动起始索引 |
当协议无法表达你写下的内容时,驱动会如实说明,而不会悄悄读取别的东西。
两个绑定所携带的驱动集合并不相同,同时存在于两者中的驱动也未必实现到同一深度。对于两者都接受选择的驱动,其记法和含义完全相同;不同之处在于哪些驱动根本接受选择:
- PLC4Go 没有 Profinet 驱动。
- PLC4Go 的 OPC-UA 驱动不接受选择——它的地址由一个 NodeId 和一个类型组成。而这一记法的来源 PLC4J 的 OPC-UA 驱动则接受完整文法。
- PLC4Go 的 UMAS 驱动接受符号路径中的裸索引(
myVar[1].member[2]),但不接受范围;PLC4J 的实现会解析范围,并把选择元素报告为不支持。 - PLC4Go 的 KNXnet/IP 驱动具备上表中的设备地址形式,PLC4J 的则没有对应形式。
并非数组选择的方括号
有三种地址形式把方括号用于完全不同的用途。它们不受这一记法影响,而这一记法自身的写法在那里也不被接受:
- C-Bus 将 CAL 命令的参数写在方括号中——
recall=[param, count]。计数是命令的一个参数,而不是附加在地址后的选择标记。 - KNXnet/IP 组地址写出一组待匹配的组地址——
1/[2-3]/4以及[1,3]/2/[4-6]。这是对地址的过滤,而不是对单个值内部的选择。 - BACnet/IP 写出属性的数组索引——
ANALOG_VALUE,2/PRESENT_VALUE[3]。这就是索引,其含义与这里所说的索引一致,因此没有任何变化。
为什么是 ; 而不是别的符号
在声明的基地址之前出现的 ; 是该记法新增的唯一分隔符。之所以保留为 ;,有三个原因:
- OPC UA 地址已经使用
;作为标准 NodeId 字符串形式(ns=2;i=MyInt,来自 OPC UA Part 6)的分隔符。这不属于 PLC4X 可以重新定义的范畴——人们会从他们的工具中复制这些字符串——所以;本来就是这套词汇表的一部分。 - 它在视觉上仍与
:保持区分,后者用于分隔类型。%DB42:28.0[4..7;1]:BYTE是可读的;同一个地址用四个冒号承担三种不同的职责则不然。 :在 S7、Modbus、SLMP、EtherNet/IP 和 ADS 地址中具有结构性意义,而;只在一个驱动中具有结构性意义。在方括号内复用负载较轻的字符,可以保持语法无歧义。
升级
在多个驱动上,方括号过去位于类型之后,在那里它们表示计数:
holding-register:1:INT[4] (1)
holding-register:1[0..3]:INT (2)| 1 | 修改前——四个寄存器 |
|---|---|
| 2 | 修改后 |
旧形式的地址将不再能被解析,而错误信息会告诉你应该改写成什么。这是有意为之的:[4] 过去表示“四个元素”,现在表示“第五个”,因此如果旧地址仍能被解析,就会悄无声息地返回不同的数据。
Firmata 是例外。 它的地址不带类型(3[4]),因此其方括号并未变化,也没有什么需要拒绝的写法。3[4] 过去表示“从 3 号引脚开始的四个引脚”,现在表示一个引脚,即第五个。请将其改写为 3[0..3]。 |
|---|
PLC4Go 的 ADS 驱动是第二个例外。 它的 [n] 过去是 n 个元素的数量,这与 PLC4J 的不同,因此 MAIN.g_arr[3] 过去读取三个元素,现在只读取一个——索引 3 处的那个元素。请将其改写为 MAIN.g_arr[0..2]。它的 [a:b] “起始加数量”形式是 PLC4J 从未有过的,现已移除:请写成 [a..a+b-1]。这两处改动也正是同一个 ADS 地址如今在两种语言中含义一致的原因,而在过去并非如此。 |
|---|
零数量在任何地方都没有对应的写法了。过去有多个驱动接受 [0],随后又以“数量必须大于零”为由将其拒绝;区间要用它所覆盖的索引来书写,因此无法请求一个元素都不要,而 [0] 现在会选择第一个元素。
评论
登录后参与评论
KnowForge