带参数的用户定义函数

User defined functions with params

在 codesys 中,一些函数支持其他语言中通常称为 'params' 的函数,即可以采用不同数量的类似类型变量的函数。例如 ADD 运算符(梯形图中的函数)。

我的问题是,是否有任何方法可以在用户定义的函数中执行相同的操作?

到目前为止我唯一的想法是采用 ARRAY [*] OF SOMETHING 并使用 LOWER_BOUNDUPPER_BOUND 进行计算。这确实有效,但要求用户每次要调用我的函数时都创建一个额外的数组变量。例如,我们有连接 2 个字符串的 CONCAT 函数。假设我想要一个 CONCAT_ALL 函数,它接受 n 个字符串并将它们全部连接起来:

    STRS: ARRAY [0..9] OF STRING := [STR1, STR2, STR3, STR4, STR5, STR6, STR7, STR8, STR9, STR10];
    // This works, but I want to avoid creating an array variable!
    CONALL1: STRING := CONCAT_ALL(STRINGS := STRS);
    // This doesn't work!
    CONALL2: STRING := CONCAT_ALL(STRINGS := [STR1, STR2, STR3, STR4, STR5, STR6, STR7, STR8, STR9, STR10]);

(编辑:如我所问,我使用的是 Schneider Electric Machine Expert 1.2 或 CODESYS 编译器 3.5.12.80)

简短回答:无法将 n 个参数传递给函数。

结构化文本是一种强静态类型语言,专为硬实时要求而设计,它不像 Python 那样的脚本语言。

如果您的代码中有很多您不想在 python 中执行但在您的实时循环中执行的字符串操作(并且您应该根据您的要求评估是否真的有必要)还想做的舒服,那就得下点功夫,自己建一个字符串操作库。

之后你可以像这样进行非常舒适的函数调用:

sResult := F_Concat6(str1,str2,str3,str4,str5,str6);

我明白采用从其他编程语言学到的思想和编程模式很诱人,但与普通用户编程相比,结构化文本和实时工业控制编程确实是另一种野兽。

我的意思是,语言的设计是有特定原因的,当正确理解和应用这些原则时,坚如磐石的架构就会从中衍生出来。

总而言之,我的两点建议是:

按照您所在领域的预期思考和编写软件,不要从其他领域移植不兼容的工作方法。

不可以将 n 个参数传递给函数。

但是你可以传递一个数组,其中 none 个元素是固定的。 Codesys 2.3 的语法。

FUNCTION CONCAT_ALL : STRING(250)
    VAR_INPUT
        asParts: POINTER TO ARRAY[0..10000] OF STRING(20); (* Array of strings *)
        iNum: INT; (* Number of elements *)
    END_VAR
    VAR
        iCount: INT; (* For cycle *)
    END_VAR

    FOR iCount  := 0 TO 10000 DO
        IF iCount > iNum THEN
            EXIT;
        END_IF;

        CONCAT_ALL := CONCAT(CONCAT_ALL, asParts^[iCount]);
    END_FOR;
END_FUNCTION


PROGRAM PLC_PRG
    VAR
        (* Array 1 to test *)
        asTest1: ARRAY[1..2] OF STRING(20) := 'String 1', 'String 2';
        (* Array 2 to test *)
        asTest2: ARRAY[1..3] OF STRING(20) := 'String 1', 'String 2', 'String 3';

        s1: STRING(250);
        s2: STRING(250);
    END_VAR

    s1 := CONCAT_ALL(ADR(asTest1), 2);
    s1 := CONCAT_ALL(ADR(asTest2), 3);
END_PROGRAM

这是字符串连接器功能块的面向对象示例:

首先我们定义一个有两个方法的接口:

INTERFACE I_MultipleConcat

METHOD concatString : I_MultipleConcat
VAR_INPUT
    sTarget : STRING;
END_VAR

METHOD getResult 
VAR_IN_OUT
   sRetrieveResult : STRING(1000);
END_VAR

然后是实现接口的功能块:

FUNCTION_BLOCK FB_MultipleConcat IMPLEMENTS I_MultipleConcat

VAR_OUTPUT
    uiLen : UINT;
END_VAR
VAR
    sResult : STRING(1000);
END_VAR

//-------------------------------------------------------------------
METHOD concatString : I_MultipleConcat
VAR_INPUT
    sTarget : STRING;
END_VAR

//make sure that the length of sResult is not exceeded
IF uiLen + INT_TO_UINT(LEN(sTarget)) <= (SIZEOF(sResult)-1)
THEN   

    //add uiLen as offset to sResult memory access          
    memcpy(ADR(sResult) + uiLen,ADR(sTarget),LEN(sTarget));
    uiLen := uiLen + INT_TO_UINT(LEN(sTarget));

END_IF

//return the instance of this FuncBlock in order to concat new strings
//with concatString() or pass the result to another STRING with getResult()
concatString := THIS^;

//-------------------------------------------------------------------
METHOD getResult 
VAR_IN_OUT
    sRetrieveResult : STRING(1000);
END_VAR

sRetrieveResult := sResult;
sResult := '';
uiLen := 0;

你可以这样称呼它:

IF NOT bInit
THEN
    bInit := TRUE;
    //s1 must be a STRING(1000) otherwise compile error
    fbMultipleConcat
        .concatString('Test1 ')
        .concatString('Test2 ')
        .concatString('Test3 ')
        .getResult(s1);     
END_IF

未来有希望!

在 Codesys V3.5 SP16 中,似乎终于可以使用带有可选参数的 FUNCTION 和 METHOD。当然这会出现在非codesys的产品中,比如TwinCAT和Schneider,在以后的版本中。

这意味着您终于可以创建一个包含 100 个参数的 CONCAT 并仅使用例如 3 个参数调用它!厉害了。

https://www.codesys.com/fileadmin/data/Images/Download/features-and-improvements-V35SP16-en.pdf