Использование универсального типа в Struct & Function Block - PullRequest
0 голосов
/ 27 апреля 2018

Я хотел бы создать универсальный тип STRUCT и парные Function Block, которые принимают и возвращают переменные универсального типа (предполагается ANY_NUM).

Желательно сжать множество существующих пар STRUCT и FB в одном формате, используя общие типы чисел , вероятно, принадлежащие к типу ANY_NUM , в одну общую пару.

В C ++ общая структура будет выполнена с Template Class, но я не могу найти аналогичную структуру в структурированном тексте.

Я попробовал универсальный функциональный блок на странице БЕКХОФА ANY / ANY_ (TYPE) , однако он быстро не смог convert type 'LREAL' to type '__SYSTEM.AnyType'.

Вопрос:

Насколько я могу достичь этой цели в структурированном тексте?

EDIT:

Я ошибочно предположил, что 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_)

Ответы [ 3 ]

0 голосов
/ 30 апреля 2018

(я почерпнул решение своей проблемы из сообщения Стефана Хеннекена на T_Arg .)

Цель может быть достигнута, но не особо чисто. Есть два общих типа (которые я нашел до сих пор), которые применимы: ANY_NUM и T_Arg.

(я использую ANY_NUM, потому что это наиболее актуально для этого примера. ANY, ANY_REAL или ANY_INT также будут разумными вариантами)

Оба варианта имеют схожие структуры и функционируют одинаково. Каждый из них представляет собой структуру, содержащую информацию о хранимой переменной: ее тип, указатель на нее и ее размер .

И все же у каждого есть свои плюсы и минусы. Для наиболее точного решения этой проблемы мы будем использовать T_Arg.

Вот разница:

ANY / ANY_NUM / ETC

Преимущество: Преобразование переменной в 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, не может быть напрямую обработана. Необходимо переместить сохраненную переменную во временную переменную, чтобы использовать ее. Поэтому Value, Min_ и Max_ потребуется преобразование из типа T_Arg в тип REAL / INT / и т. Д.

Поскольку мы пытаемся использовать только один STRUCT, Value необходимо будет снова преобразовать в T_Arg, как только Bind_Value завершит манипулирование им.

В общей сложности 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);
0 голосов
/ 05 июля 2019

Вы также можете создать универсальный тип с помощью функционального блока и объединения. Допустим, вы определяете Союз со всеми своими DUT и ПМ:

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 может быть:

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 с помощью Generic 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
0 голосов
/ 27 апреля 2018

Я недавно исследовал это (ЛЮБОЙ тип) в TwinCAT. Что вам в основном нужно сделать, это преобразовать каждый байт, на который указывает ЛЮБОЙ указатель, в LREAL (который, как вы знаете в соответствии с IEC61131-3, всегда будет 8 байтов). ЛЮБОЙ тип содержит информацию о том, на какой тип он указывает, поэтому вы узнаете, когда это LREAL, проанализировав структуру данных, на которую указывает ЛЮБОЙ указатель. Пожалуйста, прочитайте мое полное расследование в моем блоге: Чудеса ЛЮБОГО

...