0

在 TwinCAT 和 CodeSys IEC-61131 编程环境中,可以VAR_INPUT使用 anINTERFACE作为类型规范来声明 POU。我相信 TwinCAT 和 CoDeSys 中对接口的支持是对标准 IEC-61131 语言定义的扩展。

问题1:调用POU时,interfaceVAR_INPUT是否有pass-by-value(即输入FB的状态在每次执行被调用的FB时复制)或pass-by-reference语义?

问题 2:此行为在哪里指定或记录在案?

4

2 回答 2

2

接口类型本身是一个值,但它不携带它所引用的功能块。它被实现为指向实例的 vtable 指针。它被用作对实现接口的功能块的引用,但返回的地址不是功能块的地址(这是关键区别)。那是因为实现:

                                    FB Instance
                                         |
interface (PVOID) ------+        * PVOID vtable 1       +----> VTABLE 3
                        +------> * PVOID vtable 2  -----+        |
                                 *     ...                   * method 1
                                 * PVOID vtable n            *   ...
                                 * data fields               * method m

因此,如果您读取接口的内容,您将在功能块实例中的某处获得一个地址,该地址是实例中 vtable 指针的地址。特定的 vtable 是实现接口方法的那个(即与接口兼容)。

我们可以检查某些类型的 FB_MyFB 是否如此:

INTERFACE I_Derived EXTENDS __SYSTEM.QueryInterface
END_INTERFACE

FUNCTION_BLOCK FB_MyFB IMPLEMENTS I_Derived
...
END_FUNCTION_BLOCK

FUNCTION F_CheckInterfaceRange(fb : REFERENCE TO FB_MyFB) : BOOL
VAR
  ifc : I_Derived := fb;
  ifcval : POINTER TO PVOID := ADR(ifc);
END_VAR
ifcval := ifcval^;
F_CheckInterfaceRange := 
  ifcval >= ADR(fb) 
  AND_THEN ifcval <= (ADR(fb) + SIZEOF(FB_MyFB) - SIZEOF(PVOID)); 
END_FUNCTION

直接获取实例的地址似乎是不可能的。很可能这是一个任意的限制:所有的 vtable 指针必须是有效的并且可能属于某个内存区域,所以你可以想象从任何接口指向的开始并从它向后走,直到你停止获得有效的指针。这些是界限。该实例以 vtable 指针开头,因此您找到的这些指针之一就是它。然后检查指针在各种库 FB 类型的实例中的外观,然后查看指向的 vtable 的外观,我敢肯定会弹出一些有效的启发式方法,甚至可能不如__QUERYINTERFACE调用那么昂贵。CoDeSys 3 代码生成器非常糟糕。

相反,支持的方式是让 FB 实现接口扩展SYSTEM.__QueryInterface. 然后,__QUERYPOINTER用于访问该接口以获取THISFB 的值。

你可以想象这__QUERYPOINTER看起来有点像:

FUNCTION __QUERYPOINTER
VAR_INPUT
  ifc : __SYSTEM.QueryInterface;
  ptr : REFERENCE TO PVOID;
END_VAR
ptr := ifc.__QUERYTHIS();
END_FUNCTION

__SYSTEM.QueryInterface接口实现一个在 FB 实现的接口之间进行转换的方法,只要两个接口都派生自__SYSTEM.QueryInterface,以及一个__QUERYTHIS返回 的方法(想象它被称为 )THIS

该方法由编译器生成。

想象一下其余的实现有点像:

INTERFACE __SYSTEM.QueryInterface
PROPERTY _This_ : POINTER TO BYTE
METHOD _This__GET : POINTER TO BYTE   // that's how CoDeSys 3 implements getters/setters
END_PROPERTY
...
END_INTERFACE

FUNCTION BLOCK FB_Queryable IMPLEMENTS I_Queryable
PROPERTY _This_ : POINTER TO BYTE
METHOD _This__GET : POINTER TO BYTE
_This_GET := THIS;
END_METHOD
END_FUNCTION_BLOCK

您可以类似地实现F_QueryInterface(这不会那么容易,因为__QUERYINTERFACE从编译器获得帮助):

FUNCTION F_QueryInterface2 : BOOL
VAR_INPUT
  from : I_Queryable;
  to : REFERENCE TO I_Interface2;
END_VAR
IF from <> 0 THEN
  // the compiler would translate __QUERYINTERFACE(from, to) to something like:
  F_QueryInterface2 := from._QueryInterface_(2, ADR(to));
END_IF
END_FUNCTION

INTERFACE I_Queryable   // cont'd
...
METHOD _QueryInterface_ : BOOL
VAR_INPUT
  typeid : INT;
  to : POINTER TO PVOID; // pointer to interface
END_VAR
END_INTERFACE

INTERFACE I_Interface2 EXTENDS I_Queryable
...
END_INTERFACE

FUNCTION_BLOCK FB_MoreQueryable IMPLEMENTS I_Interface1, I_Interface2
METHOD _QueryInterface_ : BOOL
VAR_INPUT
  typeid : INT;
  to : POINTER TO U_Interfaces; // pointer to interface
END_VAR
to^.PVOID := 0;
CASE typeId OF
  1: to^.Interface1 := THIS^;
  2: to^.Interface2 := THIS^;
END_CASE
_QueryInterface_ := to^.PVOID <> 0;
END_FUNCTION_BLOCK

TYPE U_Interfaces :
UNION
   PVOID : PVOID;
   Interface1 : I_Interface1;
   Interface2 : I_Interface2;
END_UNION
END_TYPE
于 2019-10-23T06:41:10.633 回答
0

Interface在 CoDeSys 和 TwinCAT 中,变量始终被视为引用。这应该包括VAR_INPUT变量。

TwinCAT 参考链接文档摘录的屏幕截图

CoDeSys 参考链接文档摘录的屏幕截图

于 2019-05-20T01:56:08.787 回答