在结构和功能块中使用泛型类型



我想创建一个类型泛型STRUCT和一个接受和返回泛型变量的配对Function Block(假定ANY_NUM)。

这需要将许多现有的STRUCTFB对压缩为可能属于ANY_NUM类型的泛型数字类型以相同的格式压缩为单个泛型对。

在C++中,通用结构将通过Template Class完成,但我在结构化文本中找不到类似的结构。

我在Beckhoff的ANY/ANY_(TYPE)页面上尝试了通用功能块,但是它很快就无法convert type 'LREAL' to type '__SYSTEM.AnyType'

问题:

我可以在多大程度上在结构化文本中实现此目标?

编辑:

我错误地认为ANY是唯一相关的 ST 通用。 我已被指示输入T_Arg作为潜在可行的候选人。

尝试格式示例:

结构:

TYPE Bounded_Value:
STRUCT
Value   : ANY_NUM;
Min_    : ANY_NUM;
Max_    : ANY_NUM;
END_STRUCT
END_TYPE

功能块:

FUNCTION_BLOCK Bind_Value
VAR_IN_OUT
value_struct: Bounded_Value;
END_VAR

(实现将value_struct.Value绑定到value_struct.min_value_struct.max_之间)

我最近在TwinCAT中对此进行了调查(任何类型)。你基本上需要做的是将 ANY 指针指向的每个字节转换为 LREAL(根据 IEC61131-3 您知道它始终是 8 字节)。ANY 类型包含有关它指向哪种类型的信息,因此您可以通过解析 ANY 指针指向的数据结构来了解它何时是 LREAL。请在我的博客上阅读我的完整调查:任何

奇迹

(我从Stefan Henneken的博客文章中收集了我的问题的解决方案T_Arg。

目标可以实现,但不是特别干净。有两种通用类型(到目前为止我找到的)是适用的:ANY_NUMT_Arg

(我使用ANY_NUM因为它与此示例最相关。ANYANY_REALANY_INT也是合理的选择)

这两个选项具有相似的结构和功能。每个都是一个结构,其中包含有关存储变量的信息:其类型、指向它的指针及其大小

然而,每种方法都有优点和缺点。为了最准确地解决这个问题,我们将使用T_Arg.

这是区别:

任何/ANY_NUM/等

优点:变量转换为ANY_NUM是在分配变量时隐式完成的。输入变量在输入到函数之前不需要预转换,从而减少了代码的大小。

此外,它只接受属于其域的变量,因此字符串不会被意外使用。

缺点:ANY_NUM不能在VAR_INPUT块之外声明,实际上,在尝试时会提供以下错误消息:

Variables of type 'ANY_NUM' only allowed as input of functions.

因此,ANY_NUM不能用作STRUCT变量,即使该STRUCT声明为函数的输入也是如此。这就是为什么它不能用于解决这个特定问题的原因。

T_Arg

优点:T_Arg可以在任何地方声明和使用。

缺点:T_Arg需要任何预期变量类型的转换函数,例如:F_INT()F_REAL()F_DINT()

因此,需要在输入之前和之后执行类型检查。

示例解决方案

不幸的是,存储在T_Arg中的变量不能直接操作。有必要将存储的变量移动到临时变量中才能使用它。所以ValueMin_Max_都需要从T_Arg型转换为REAL/INT/等。

由于我们尝试只使用一个STRUCT,一旦Bind_Value完成操作,Value将需要再次转换为T_Arg。

总的来说,Value在实例化时将转换三次,之后每次调用将转换两次。

结构:

TYPE Bounded_Value:
STRUCT
Value   : T_Arg;
Min_    : T_Arg;
Max_    : T_Arg;
END_STRUCT
END_TYPE

功能块:

FUNCTION_BLOCK Bind_Value
VAR_IN_OUT
value_struct: Bounded_Value;
// Other variable type declarations
END_VAR
VAR
val_int     :   INT;
max_int     :   INT;
min_int     :   INT;
END_VAR  
<小时 />
CASE (value_struct.Value.eType) OF
E_ArgType.ARGTYPE_INT: // If the struct's Value's type is INT
// Copy generic pointer information into typed pointer 
MEMCPY(ADR(val_int), value_struct.Value.pData, value_struct.Value.cbLen);
MEMCPY(ADR(max_int), value_struct.Max_.pData,  value_struct.Max_.cbLen);
MEMCPY(ADR(min_int), value_struct.Min_.pData,  value_struct.Min_.cbLen);
IF val_int > max_int THEN
value_struct.Value.pData := value_struct.Max_.pData;
ELSIF val_int < min_int THEN
value_struct.Value.pData := value_struct.Min_.pData;
END_IF
// Other variable type handlings
END_CASE

主要:

PROGRAM MAIN
VAR
val     : INT := -1; //Change this to test
minim   : INT :=  0;
maxim   : INT :=  5;
newVal  : INT;
bv      : Bounded_Value;
bind    : Bind_Value;
END_VAR
<小时 />
// Convert INT variables to T_Arg in structure
bv.Value:= F_INT(val);
bv.Max_ := F_INT(maxim);
bv.Min_ := F_INT(minim);
// Bind_Value.value_struct := bv;
bind(value_struct := bv);
// Copy result to newVal
MEMCPY(ADR(newVal), bv.Value.pData, bv.Value.cbLen);

您还可以借助功能块和联合创建泛型类型。 假设您定义了一个包含所有 DUT 和 POU 的联合:

TYPE GenericType :
UNION
generic : PVOID;
bBool   : REFERENCE TO BOOL;
nInt    : REFERENCE TO INT;
nUint   : REFERENCE TO UINT;
nUdint  : REFERENCE TO UDINT;
fReal   : REFERENCE TO REAL;
fLreal  : REFERENCE TO LREAL;
fbTest  : REFERENCE TO FB_Test;
END_UNION
END_TYPE

然后创建一个特殊的功能块:

FUNCTION_BLOCK Generic
VAR_INPUT
END_VAR
VAR_OUTPUT
END_VAR
VAR
uGenericType : GenericType;
END_VAR

以及获取和设置 PVOID 的属性:

PROPERTY PUBLIC generic : PVOID

吸气者:

generic := uGenericType.generic;

二传手:

uGenericType.generic := generic;

您还需要属性以便稍后检索您的类型:

FB的图片

getBool setter 的一个例子可能是:

IF uGenericType.generic = 0 
THEN
RETURN;
ELSE
getBool := uGenericType.bBool;
END_IF

现在,您创建将使用泛型类型的 FB

FUNCTION_BLOCK FB_Container
VAR_INPUT
myGenericType       : Generic;
nContainerOption    : INT;
END_VAR
VAR_OUTPUT
END_VAR
VAR
testInt             : INT;
testBool            : BOOL;
testFB              : FB_Test;
END_VAR

CASE nContainerOption OF
1:
testInt := myGenericType.getInt;
2:
testFB := myGenericType.getFbTest;
3:
testBool := myGenericType.getBool;
END_CASE

调用可能是这样的:

fbContainer.myGenericType.generic := ADR(testInteger);
...
fbContainer(nContainerOption := 1);

另一种方法是使用通用FB扩展我们的FB。 不过,我们需要对泛型FB和泛型联合进行一些修改:

FUNCTION_BLOCK Generic
VAR_INPUT
END_VAR
VAR_OUTPUT
END_VAR
VAR
uGenericType    : GenericType;
bInit           : BOOL;
END_VAR

IF NOT bInit THEN
uGenericType.generic := ADR(THIS^);
bInit := TRUE;
END_IF

TYPE GenericType :
UNION
generic : PVOID;
//Add the pointer of the FB you want to extend
pAxis   : POINTER TO FB_Axis;
bBool   : REFERENCE TO BOOL;
nInt    : REFERENCE TO INT;
nUint   : REFERENCE TO UINT;
nUdint  : REFERENCE TO UDINT;
fReal   : REFERENCE TO REAL;
fLreal  : REFERENCE TO LREAL;
fbTest  : REFERENCE TO FB_Test;
END_UNION
END_TYPE

扩展的FB:

FUNCTION_BLOCK FB_Axis EXTENDS Generic
VAR_INPUT
END_VAR
VAR_OUTPUT
END_VAR
VAR
fPosition : LREAL;
END_VAR

现在像以前一样调用我们的容器:

fbContainer.myGenericType := fbAxis;

在FB_Container中,您可以按如下方式调用轴:

IF myGenericType.getPointerFbAxis <> 0 
THEN
position := myGenericType.getPointerFbAxis^.fPosition;
END_IF

最新更新