【问题标题】:Using generic type in Struct & Function Block在结构和功能块中使用泛型
【发布时间】:2018-10-08 14:13:45
【问题描述】:

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

这需要使用通用数字类型probably belonging to the ANY_NUM type 将许多现有的STRUCTFB 对以相同格式压缩成一个通用对。

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

我尝试了通用功能块on Beckhoff's ANY/ANY_(TYPE) page,但很快就失败了convert type 'LREAL' to type '__SYSTEM.AnyType'

问题:

在结构化文本中我能在多大程度上实现这一目标?

编辑:

我错误地认为ANY 是唯一相关的ST 泛型。 我已被定向到 type 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_ 之间)

【问题讨论】:

  • REALLREAL 不是 ANY_NUM。那些必须被转换。从我们所知道的数学的角度来看,我们认为它是一个数字,但从数据类型的角度来看,INTREAL 是不同的类型。
  • @SergeyRomanov 我不明白你的意思。根据the Beckhoff page on ANY,ANY_NUM 包含 ANY_REAL 和 ANY_INT。
  • @SergeyRomanov 哦,您的意思是表示 ANY_NUM 和 LREAL 不是等效类型,因为 ANY_NUM 是一个包含数字的类型、指针和大小的结构,而 REAL/LREAL/INT 是数字本身。对吗?
  • 你是对的。我错过了 ANY_INT。对不起。

标签: generics struct plc st twincat


【解决方案1】:

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

【讨论】:

  • 您的博客确实解决了我对 ANY_TYPE 在函数中的使用的一些困惑,但是,您的博客和 Stefan Henneken's blog on T_Arg 都没有明确在结构中使用这些变量。
  • 我想我可以尝试使用功能块或程序将值加载到每个 STRUCT 中,但这只会产生更多的工作并消耗更多的空间,而不仅仅是为每个变量设置一个 STRUCT。
【解决方案2】:

(我从Stefan Henneken's blog post on T_Arg 收集了我的问题的解决方案。)

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

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

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

但各有利弊。为了最准确地解决这个问题,我们将使用 T_Arg

这里有区别:

ANY / ANY_NUM / ETC

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

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

缺点: 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 在实例化时会被转换 3 次,之后每次调用会被转换两次。

结构:

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);

【讨论】:

  • 您是否尝试使用别名? TYPE MyType: T_Arg; END_TYPE 然后VAR in: MyType; END_VAR?
  • 我没有,你为什么问? (我看不出替换 T_Arg 会如何压缩代码大小,因为我仍然需要执行类型转换。)
【解决方案3】:

您还可以在功能块和联合的帮助下创建泛型类型。 假设您为所有 DUT 和 POU 定义了一个 Union:

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;

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

Image of the FB

getBool 设置器的一个示例可能是:

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。 我们需要对 Generic FB 和 GenericType Union 做一些修改:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-01-29
    • 2014-10-19
    相关资源
    最近更新 更多