Search code examples
codesys

User defined functions with params


In codesys some functions support what in other languages is usually called 'params', i.e. a function that can take a varied amount of similarly typed variables. For example the ADD Operator (function in ladder).

My question is, if there's any way to do the same in user defined functions?

The only idea that I have so far is to take an ARRAY [*] OF SOMETHING and use LOWER_BOUND and UPPER_BOUND to do the computations. This does work, but requires the user to create an additional array variable every time they want to call my function. For example, we have the CONCAT function that concatenates 2 strings. Suppose I want a CONCAT_ALL function that takes n strings and concatenates them all:

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

(EDIT: As I was asked, I am using Schneider Electric Machine Expert 1.2, or CODESYS compiler 3.5.12.80)


Solution

  • Here is an object oriented example of a string concatenator Function Block:

    First we define an Interface with 2 methods:

    INTERFACE I_MultipleConcat
    
    METHOD concatString : I_MultipleConcat
    VAR_INPUT
        sTarget : STRING;
    END_VAR
    
    METHOD getResult 
    VAR_IN_OUT
       sRetrieveResult : STRING(1000);
    END_VAR
    

    Then the Function Block which implements the Interface:

    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;
    

    You can call it like this:

    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