说明 VARIANT 数据类型的参数是一个指针或引用,可指向各种不同数据类型的变量。VARIANT 指针无法指向实例,因此不能指向多重实例或多重实例的 ARRAY。VARIANT 指针可以是基本数据类型(例如,INT 或 REAL)的对象。还可以是 STRING、DTL、STRUCT 类型的 ARRAY、UDT、UDT 类型的 ARRAY。VARIANT 指针可以识别结构,并指向各个结构元素。VARIANT 数据类型的操作数不占用背景数据块或工作存储器中的空间。但是,将占用 CPU 上的存储空间。 VARIANT 类型的变量不是一个对象,而是对另一个对象的引用。在函数的块接口中的 VAR_IN、VAR_IN_OUT 和 VAR_TEMP 部分中,VARIANT 类型的单个元素只能声明为形参。因此,不能在数据块或函数块的块接口静态部分中声明,例如,因为各元素的大小未知。所引用对象的大小可以更改。 使用 VARIANT 数据类型时,可为各种数据类型创建通用的标准函数块 (FB) 或函数 (FC)。为此,可使用所有编程语言中的各种指令。在程序创建过程中,可指定该块可处理数据类型。在此,可使用 VARIANT 数据类型对各种变量进行互连。之后,再根据这些变量在块中的数据类型进行响应。调用某个块时,可以将该块的参数连接到任何数据类型的变量。调用某个块时,除了传递变量的指针外,还会传递变量的类型信息。块代码随后可以根据运行期间传递的变量类型来执行。 例如,如果函数的块参数为 VARIANT 数据类型,那么整数数据类型的变量可以在程序中的一个点处传递,而 PLC 数据类型的变量可以在程序中的另一个点处传递。借助 VARIANT 指令,函数随后可以正确响应这种情况,而不会出错。 说明 如果一个数据块最初的数据类型为用户自定义的数据类型 (UDT),那么只能指向完整的数据块。 |
说明 访问 I/O 只有 S7-1500 模块的 CPU 才能直接读写 I/O 输出或输出中的信号。(<操作数>:P) |
下表列出了 VARIANT 指针的属性: 长度(字节) | 表示法 | 格式 | 输入值示例 | 0 | 符号 | 操作数 | 'TagResult' | 数据块名称.操作数名称.元素 | 'Data_TIA_Portal'.StructVariable.FirstComponent | 绝对 | 操作数 | %MW10 | 数据块编号.操作数 类型长度 | P#DB10.DBX10.0 INT 12 1) | NULL 指针 | NULL | 1) 如果使用前缀 P#,则只能指向“标准”访问模式的存储区。 |
数据类型的编码 如果通过 P# 使用绝对寻址,则允许使用以下数据类型: - BOOL
- BYTE
- CHAR
- WORD
- INT
- DWORD
- DINT
- REAL
- TIME
- S5TIME
- DATE
- TOD
- DT
示例 以下示例说明了 VARIANT 使用 STL 指令“MOVE:移动值”的工作原理: STL | 说明 | CALL MOVE | // 调用指令。 | value_type := VARIANT | // 参数 IN 和 OUT 的数据类型 | IN := 'Data_TIA_Portal'.StructVariable.FirstComponent | // 从“Data_TIA_Portal”数据块移动操作数“FirstComponent”中的数据。 | OUT := 'MotorDB'.StructResult.TagResult | // 从“MotorDB”数据块传送到“TagResult”操作数中。 |
S7-1200/1500 CPU 中的指针用例(与 S7-300/400 相比) 下表简要列出了 S7-300/400 系列 CPU(ANY 指针)与 S7-1200/1500 系列 CPU 中指针的各种应用及解决方案。 在大多数应用中,S7-1200/1500 系列 CPU 无需使用指针。取而代之的是更为简单的语言资源。 在程序运行时期间只需确定数据类型时,才建议使用 VARIANT 数据类型进行间接寻址。 ANY 指针的用途 | 在 TIA Portal 中应用时的建议 (S7-1200/S7-1500) | 使用指令“BLKMOV:块移动”,在程序中移动任意源和目标数据类型的数据。 | 在 PLC 数据类型中变量定义。使用指令“Serialize”和“Deserialize”,移动变量。 | 初始化 ARRAY 结构 | 使用指令“FILL_BLK:填充块”,初始化或填充 ARRAY 结构。 | 移动 ARRAY 元素 | 使用指令“MOVE_BLK:块移动”,将一个 ARRAY 结构中的多个元素内容移到另一个 ARRAY 结构中。 | 使用结构化数据,优化存储器和性能 | 使用块接口中的 InOut 部分优化存储器和性能。 更新信息,敬请访问“S7-1200/1500 编程指南”中的《S7-1200/1500 编程指南》 | 访问 WORD 的各个位/字节 | 使用“片段访问” 更多信息,请参见“片段访问示例” | 确定结构或数据块的长度 | 使用 ARRAY 并读取其长度:使用指令“CountofElements:获取 ARRAY 元素个数”。该指令只能与数据类型 VARIANT 结合使用。 | 间接寻址 | 使用 VARIANT 指针,可对仅在运行过程中已知的数据类型进行间接寻址。使用数据类型 DB_ANY,可对数据块进行间接访问。 |
VARIANT 指令 TIA Portal 中提供了以下用于处理 VARIANT 的指令: 基本指令 | 类别 | 指令 | 说明 | 比较器运算 | EQ_Type | 比较数据类型与变量数据类型是否“相等” | NE_Type | 比较数据类型与变量数据类型是否“不相等” | EQ_ElemType | 比较 ARRAY 元素数据类型与变量数据类型是否“相等” | NE_ElemType | 比较 ARRAY 元素数据类型与变量数据类型是否“不相等” | IS_NULL | EQUALS ZERO 指针查询 | NOT_NULL | UNEQUALS ZERO 指针查询 | IS_ARRAY | 检查 ARRAY | TypeOf | 检查 VARIANT 变量的数据类型 | TypeOfElements | 检查 VARIANT 变量的元素数据类型 | 移动操作 | MOVE_BLK_VARIANT | 块移动 | VariantGet | 读取 VARIANT 变量值 | VariantPut | 写入 VARIANT 变量值 | CountOfElements | 获取 ARRAY 元素个数 | 转换操作 | VARIANT_TO_DB_ANY | 将 VARIANT 转换为 DB_ANY | DB_ANY_TO_VARIANT | 将 DB_ANY 转换为 VARIANT |
说明 MOVE、MOVE_BLK 和 MOVE_BLK_VARIANT 之间的区别 - 可使用“MOVE”指令来复制完整的结构。
- 可使用“MOVE_BLK”指令来移动具有已知数据类型的 ARRAY 的部分。
- 仅在您想要移动其数据类型仅在程序运行时期间已知的 ARRAY 的部分时,才需要使用 MOVE_BLK_VARIANT 指令。
|
可以在信息系统的“基本指令 > 对应编程语言”(Basic instructions > Respective programming language) 下找到有关各种指令的其它信息。 也可以在“扩展指令”(Extended instructions) 下找到同样用于处理 VARIANT 数据类型的其它指令。 简介 在下一章中,您将了解可用于 VARIANT 指令的应用选项。 对 VARIANT 指向的变量的数据类型求值 在下表中,您将看到可使用哪些指令来对 VARIANT 指向的变量的数据类型求值: 函数 | 指令 | 说明 | 确定数据类型 | TypeOf():检查 VARIANT 变量的数据类型 (该指令仅适用于 SCL,且只能与 IF 或 CASE 指令一起使用。) | 可使用该指令将 VARIANT 变量指向的数据类型与任何其它变量的数据类型进行比较。也可以与 PLC 数据类型作比较。 | TypeOfElements():扫描 VARIANT 变量 ARRAY 元素的数据类型 (该指令仅适用于 SCL,且只能与 IF 或 CASE 指令一起使用。) | 可使用该指令将 VARIANT 变量指向的数据类型与任何其它变量的数据类型进行比较。也可以与 PLC 数据类型作比较。如果 VARIANT 变量的数据类型为 ARRAY,则将比较 ARRAY 元素的数据类型。 | EQ_Type:比较数据类型与变量数据类型是否“相等” NE_Type:比较数据类型与变量数据类型是否“不相等” | 可使用该指令将 VARIANT 变量指向的数据类型与任何其它变量的数据类型进行比较。也可以与 PLC 数据类型作比较。 | EQ_ElemType:比较 ARRAY 元素数据类型与变量数据类型是否“相等” NE_ElemType:比较 ARRAY 元素数据类型与变量数据类型是否“不相等” | 可使用该指令将 VARIANT 变量指向的数据类型与任何其它变量的数据类型进行比较。也可以与 PLC 数据类型作比较。如果 VARIANT 变量的数据类型为 ARRAY,则将比较 ARRAY 元素的数据类型。 | 对 ARRAY 元素求值 | IS_ARRAY:检查 ARRAY | 可使用该指令检查 VARIANT 变量指向的数据类型是否为 ARRAY。 | CountOfElements:获取 ARRAY 元素个数 | 可使用该指令读出该变量中 VARIANT 变量指向了多少个 ARRAY 元素。 |
可以在信息系统的“基本指令 > 对应编程语言”(Basic instructions > Respective programming language) 下找到有关各种指令的其它信息。 读取 VARIANT 指向的数据 为了能够使用数据,您必须在一个中间步骤中将该数据移到变量中,因为无法直接处理该数据。 指令 | 说明 | 示例 | 结果 | VARIANT 指向 | 目标数据类型 | VariantGet:读取 VARIANT 变量值 | 可使用该指令将单个变量的值移到另一个变量中。这两个变量的数据类型必须匹配。 | UDT_1 | UDT_1 | 已执行该指令。 | REAL | REAL | DINT | DWORD | 该指令未执行。 |
将数据分配给 VARIANT 变量 无法使用该指令来初始化 VARIANT 变量。因此,在将数据返回到变量时必须已经初始化 VARIANT 变量。请勿使用未初始化的临时 VARIANT 变量。 指令 | 说明 | 示例 | 结果 | 源数据类型 | VARIANT 指向: | VariantPut:写入 VARIANT 变量值 | 可使用该指令将单个变量的值移到另一个变量中。这两个变量的数据类型必须匹配。 | UDT_1 | UDT_1 | 已执行该指令。 | REAL | REAL | DINT | DWORD | 将不执行该指令,因为数据类型不同。 |
处理动态 ARRAY 结构 对 ARRAY 元素求值 | TypeOfElements():扫描 VARIANT 变量 ARRAY 元素的数据类型 (该指令仅适用于 SCL,且只能与 IF 或 CASE 指令一起使用。) | 可使用该指令将 VARIANT 变量指向的数据类型与任何其它变量的数据类型进行比较。也可以与 PLC 数据类型作比较。如果 VARIANT 变量的数据类型为 ARRAY,则将比较 ARRAY 元素的数据类型。 | IS_ARRAY:检查 ARRAY | 可使用该指令检查 VARIANT 变量指向的数据类型是否为 ARRAY。 | CountOfElements:获取 ARRAY 元素个数 | 可使用该指令读出该变量中 VARIANT 变量指向了多少个 ARRAY 元素。 | MOVE_BLK_VARIANT:块移动 | 可使用该指令移动动态和类型安全(集成类型测试)ARRAY。可以为源和目标 ARRAY 自由选择限值。ARRAY 元素的数据类型必须匹配。 |
说明 MOVE、MOVE_BLK 和 MOVE_BLK_VARIANT 之间的区别 - 可使用“MOVE”指令来复制完整的结构。
- 可使用“MOVE_BLK”指令来移动具有已知数据类型的 ARRAY 的部分。
- 仅在您想要移动其数据类型仅在程序运行时期间已知的 ARRAY 的部分时,才需要使用 MOVE_BLK_VARIANT 指令。
|
可以在“移动数据”(Moving data) 编程示例中找到有关使用 MOVE_BLK_VARIANT 指令的其它信息。 说明 通过在块调用时为 VARIANT 块参数指定一个特定变量,对 VARIANT 数据类型进行初始化。这将形成对所传递变量地址的引用。为此,需在块接口中创建一个 VARIANT 数据类型的块参数。在以下示例中,在 SourceArray 部分中包含两个块参数 DestinationArray 和 InOut。 该示例显示了“移动数据示例”中的部分编程示例。详细的程序代码,请参见“另请参见”。 说明 系统不支持将变量直接传递到 VARIANT 变量中。如,myVARIANT: = #Variable |
该示例显示了“移动数据示例”中的部分编程示例。详细的程序代码,请参见“另请参见”。 说明 VARIANT 作为形参 如果将 VARIANT 声明为一个形参,则未写保护的数据将作为实参进行传递。 |
传递各种数据类型 在以下示例中,将显示多次调用通用的标准函数时如何使用不同变量对 VARIANT 块参数进行初始化: “FC_PartialArrayCopy”函数将调用两次。通过左侧的调用,将 VARIANT 参数 SourceArray 与“my_struct”数据类型的 ARRAY 互连。通过右侧的调用,将 VARIANT 参数 SourceArray 与 REAL 数据类型的 ARRAY 互连。 读出并检查数据类型 系统目前支持各种不同的比较指令,可读取变量或元素的数据类型,并将其与其它变量或元素的数据类型进行比较。 在下图中,将显示如何使用多个比较指令检查 ARRAY 的元素是否具有相同的数据类型: 仅当 ARRAY 元素的数据类型相同时,才执行 MOVE_BLK_VARIANT 指令。 编程示例 在此编程示例中,将移动在生产班次期间为示例收集的数据值以作进一步处理。 收集的数据放在 ARRAY 中。 通过“MOVE_BLK_VARIANT: 移动块”(Move block) 指令,可以动态或以类型安全方式移动整个 ARRAY 或个别 ARRAY 元素。 可以为源和目标 ARRAY 自由选择 ARRAY 限值,这些限值不必匹配。 但是,要移动的数据值的数据类型必须匹配。 该指令在所有编程语言中都可用。 通过 VARIANT 数据类型,也可以使用已创建的程序代码并通过在块调用中指定不同的源和目标区域来移动另一个生产班次的数据。 步骤 - 使用 SCL 编程语言创建函数并将其命名为“FC_PartialArrayCopy”。
- 按如下方式声明块接口:
3.按如下方式创建 SCL 程序代码:可以找到以下程序代码作为模板。 4.创建 PLC 数据类型“UDT_MyStruct”: 5.创建全局数据块“DB_WithArrays”:
6.在组织块(例如 OB1)中调用“FC_PartialArrayCopy”函数,并使用 DB_WithArrays 数据块初始化参数。 输入指定的常量: 7.也可以使用第三个和第四个 ARRAY(数据类型为 REAL)来代替使用前两个 ARRAY (数据类型为 UDT_MyStruct)。 结果 在程序周期中调用“FC_PartialArrayCopy”块后,会将从第四个元素开始的两个数据值立即从“DB_WithArrays”全局数据块的第一个 ARRAY 复制到该数据块的第二个 ARRAY 中。 复制的数据值将插入到第二个 ARRAY 中(从第四个元素开始)。 用于复制的 SCL 程序代码: SCL | IF IS_ARRAY(#SourceArray) AND TypeOfElements(#SourceArray) = TypeOfElements(#DestinationArray) THEN | #Error := MOVE_BLK_VARIANT(COUNT := #Count, SRC := #SourceArray, SRC_INDEX := #SourceIndex, | DEST => #DestinationArray, DEST_INDEX := #DestinationIndex); | END_IF; | #FC_PartialArrayCopy := #Error; |
编程示例 在以下示例中,您编写了一个环形缓冲区,该缓冲区包含一个 ARRAY,并且根据 FIFO 原理进行读写。该程序代码有一个读取 VARIANT 指针和一个写入 VARIANT 指针。通过 VARIANT 指令,可以编写稳定的程序代码并确保可靠地进行复制或删除。 通过 VARIANT 数据类型,程序部分在运行时期间可能会受影响。VARIANT 指针是类型安全指针,即在运行时期间执行类型测试。对于使用块属性“optimized”创建的块,先前使用 ANY 指针编写的子函数现在可以使用 VARIANT 指针进行解析。可使用 VARIANT 数据类型将结构传送到系统函数块。 步骤 - 创建 SCL 函数块并将其命名为“FIFOQueue”。
- 按如下方式声明块接口:
- 声明
- 参数
- 数据类型
- 注释
- Input
- request
- BOOL
- 当“request”参数中检测到信号上升沿时,将执行该指令。
- mode
- BOOL
- 0 = 返回环形缓冲区的第一个条目。
- 1 = 条目被写入环形缓冲区的最后一个位置。
- initialValue
- VARIANT
- 环形缓冲区的 ARRAY 被初始化的值。
- Output
- error
- INT
- 错误信息
- InOut
- item
- VARIANT
- 从环形缓冲区中返回或写入到环形缓冲区的条目。
- buffer
- VARIANT
- 用于环形缓冲区的 ARRAY。
- Static
- edgeupm
- BOOL
- 保存上一次查询的 RLO 的边沿存储位。
- firstItemIndex
- INT
- 环形缓冲区中最旧条目的索引
- nextEmptyItemIndex
- INT
- 环形缓冲区中下一个空闲条目的索引
- Temp
- edgeup
- BOOL
- 边沿检测的结果
- internalError
- INT
- 错误信息
- newFirstItemIndex
- INT
- 可变下标
- newNextEmptyItemIndex
- INT
- 可变下标
- bufferSize
- UDINT
- 环形缓冲区中 ARRAY 元素的数量
- 在“FIFOQueue”函数块中创建以下程序代码:
| (* 该程序代码部分仅在信号上升沿后执行一次。如果逻辑运算结果的信号状态没有改变,则将“FIFOQueue”FB 的程序执行将终止。 *) | #edgeup := #request & NOT #edgeupm; | #edgeupm := #request; | IF NOT (#edgeup) THEN | RETURN; | END_IF; | | | // ------验证所有参数输入是否均有效。---- | (* 该程序代码部分检查环形缓冲区是否为 ARRAY。如果是的话,将读取 ARRAY 元素的数量。如果不是 ARRAY,程序执行将在该点终止,并输出错误代码“-10”。*) | IF NOT (IS_ARRAY(#buffer)) THEN | #error := -10; | RETURN; | ELSE | #bufferSize := CountofElements(#buffer); | END_IF; | | (* 该程序代码部分将检查 ARRAY 元素的数据类型是否匹配条目的数据类型(变量 #项目)。如果数据类型不匹配,则程序执行将在该点终止,并输出错误代码“-11”。*) | IF NOT (TypeOf(#item) = TypeOfElements(#buffer)) THEN | #error := -11; | RETURN; | END_IF; | | (* 该程序代码部分将检查环形缓冲区的初始值是否匹配条目(变量 #项目)。如果数据类型不匹配,则程序执行将在该点终止,并输出错误代码“-12”。*) | IF NOT (TypeOf(#item) = TypeOf(#initialValue)) THEN | #error := -12; | RETURN; | END_IF; | | (* 该程序代码部分将检查变量索引是否在 ARRAY 限值之内。如果不是,程序执行将在该点终止,并且输出错误代码“-20”或“-21”,具体取决于索引。*) | IF (#nextEmptyItemIndex >= #bufferSize) THEN | #error := -20; | RETURN; | END_IF; | IF (#firstItemIndex >= #bufferSize) THEN | #error := -21; | RETURN; | END_IF; | | | //-----------程序代码执行,具体取决于模式参数------------- | // 指令的执行取决于模式参数的信号状态。 | IF #mode = 0 THEN | | // 如果模式参数的信号状态为“0”,将返回传递的环形缓冲区的第一个条目。 | (* 该程序代码部分检查环形缓冲区是否为空。如果是,程序执行将在该点终止,并输出错误代码“-40”。*) | IF (#firstItemIndex = -1) THEN | #error := -40; | RETURN; | END_IF; | | // 该程序代码部分将返回环形缓冲区的第一个条目。 | #internalError := MOVE_BLK_VARIANT(SRC := #buffer, | COUNT := 1, | SRC_INDEX := #firstItemIndex, | DEST_INDEX := 0, | DEST => #item); | | IF (#internalError = 0) THEN | (* 该程序代码部分检查环形缓冲区是否包含 ARRAY 元素。如果是,则传递第一个条目,且索引递增 1。*) | #internalError := MOVE_BLK_VARIANT(SRC := #initialValue, | COUNT := 1, | SRC_INDEX := 0, | DEST_INDEX := #firstItemIndex, | DEST => #buffer); | | // 该程序代码部分将计算第一个条目的新索引。 | #newFirstItemIndex := #firstItemIndex +1; | #newFirstItemIndex := #newFirstItemIndex MOD UDINT_TO_INT(#bufferSize); | | // 该程序部分将检查环形缓冲区是否为空。 | IF (#nextEmptyItemIndex = #newFirstItemIndex) THEN | // 如果环形缓冲区为空,索引将被设置为 0。 | #firstItemIndex := -1; | #nextEmptyItemIndex := 0; | ELSE | // 第一个条目的索引已更改。 | #firstItemIndex := #newFirstItemIndex; | END_IF; | END_IF; | ELSE | | | // 如果模式参数的信号状态为“1”,条目将写入到传递的环形缓冲区中。 | (* 该程序代码部分检查环形缓冲区是否已满。如果是,程序执行将在该点终止,并输出错误代码“-50”。*) | IF (#nextEmptyItemIndex = #firstItemIndex) THEN | #error := -50; | RETURN; | END_IF; | | // 该程序代码部分会将条目写入环形缓冲区中。 | #internalError := MOVE_BLK_VARIANT(SRC := #item, | COUNT := 1, | SRC_INDEX := 0, | DEST_INDEX := #nextEmptyItemIndex, | DEST => #buffer); | | IF (#internalError = 0) THEN | // 该程序代码部分会使索引加 1,并计算新的空条目索引。 | #newNextEmptyItemIndex := #nextEmptyItemIndex +1; | #newNextEmptyItemIndex := #newNextEmptyItemIndex MOD #bufferSize; | #nextEmptyItemIndex := #newNextEmptyItemIndex; | | (* 该程序代码部分会检查“#firstItemIndex”变量有哪些索引。如果数字为 -1,环形缓冲区将初始化,条目将写入到环形缓冲区中。这也正是必须为变量分配“0”的原因所在。*) | IF (#firstItemIndex = -1) THEN | #firstItemIndex := 0; | END_IF; | END_IF; | END_IF; | | //-------------------------局部错误处理 ----------------------------
| (* 该程序代码部分将检查是否出现局部错误。如果是,程序执行将在该点终止,并输出错误代码“-100”。*) | IF (#internalError > 0) THEN | #error := -100; | RETURN; | END_IF; | | // 如果在程序执行期间没有出错,将输出错误代码“0”。 | #error := 0; |
结果在程序中运行 FIFO 队列的位置调用 SCL 函数块。
|