【问题标题】:User defined functions with params带参数的用户定义函数
【发布时间】:2020-07-07 13:36:53
【问题描述】:

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

【问题讨论】:

    标签: codesys


    【解决方案1】:

    未来有希望!

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

    这意味着您最终可以创建一个包含 100 个参数的 CONCAT,并使用例如 3 来调用它!太棒了。

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

    【讨论】:

    • 惊人的发现!迫不及待想尝试一下!
    【解决方案2】:

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

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

    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
    

    【讨论】:

    • 所以本质上是一个字符串生成器!不完全是我所希望的,而是一个非常接近和优雅的解决方案。
    • 只是好奇,但是您为此制作了一个界面有什么原因吗? 没关系,我尝试在没有界面的情况下这样做并得到 "C0185: It无法对函数调用的结果执行组件访问'.'、索引访问'[]'或调用'()'。首先将结果分配给帮助变量。".但是为什么?
    • 正如你所说,接口是对方法调用结果进行组件访问所必需的。我们也可以使用指向 FB_MultipleConcat 的指针作为 concatString 的返回值,但这有缺点:第一,这是违反直觉的,因为第一个 .concatString('Test1') 之后的后续调用具有不同的语法 ^.concatString('Test2 ').2nd,因为接口是一个契约,我们可以用它增加代码封装。你只能访问一组方法或属性。通过指向Function Block的指针你可以访问完整的对象。
    【解决方案3】:

    简答:没有办法将 n 个参数传递给函数。

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

    如果您的代码中有很多不想在 python 中但在实时循环中执行的字符串操作(并且您应该根据您的要求评估它是否真的有必要)并且仍然想要进行以一种舒适的方式,那么您必须付出一些努力并自己构建一个字符串操作库。

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

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

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

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

    总结一下,我的两分钱建议:

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

    【讨论】:

      【解决方案4】:

      不,你不能将 n 个参数传递给函数。

      但是你可以传递一个数组,没有固定数量的元素。 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
      

      【讨论】:

      • 不幸的是,但最终得到了类似的结果。我特别改变的一件事是我的输入是STRINGS: ARRAY [*] OF STRING。您为什么选择使用恒定长度的数组?
      • 因为他在这个例子中使用了codesys 2.3编译器。
      • 顺便说一句,这段代码非常危险,可能会导致半夜打电话询问为什么整个生产线突然以不可恢复的方式停止。
      • @FilippoBoido 我想你会喜欢这个例子的 :) 我想对你的陈述有更多的解释。这样的代码有多危险?
      • @Guiorgy 因为你用 Codesys 标签标记了这个问题,但没有提到版本。所以我的例子基于 2.3。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-11-25
      • 1970-01-01
      • 1970-01-01
      • 2011-12-28
      • 2020-06-06
      • 2011-03-21
      • 2011-08-07
      相关资源
      最近更新 更多