Search code examples
codesys

Access VAR_STAT CONSTANTS of an FB externally without instantiation in CODESYS V3


I would like to have the behaviour of a public constant in C#. So:

In CODESYS V3, is it possible to externally access VAR_STAT CONSTANTS of an FB without instantiation in CODESYS V3? and If not, what would be the most proper way to do so?

The emphasis here is "on the FB", and with externally, I mean other places in the program. There are a couple of work-arounds given below, but maybe more cleaner solutions are at hand.

An application of this would be having a list of text resources that I would like to have available at other places in the code. e.g. a list of pneumatic cylinder states:

FUNCTION_BLOCK PUBLIC CylinderStates
VAR_STAT CONSTANT
    Idle : STRING := 'Idle';
    MoveToWork : STRING := 'Move to Work';
    MoveToBase : STRING := 'Move to Base';
    AtWork : STRING := 'At Base';
    AtBase : STRING := 'At Base';
    Error : STRING := 'Error';
END_VAR

So that in PRG_MAIN, I could do something like this (this does not work):

str := CylinderStates.MoveToWork;

This generates the error:

  • Function block 'CylinderStates' must be instantiated to be accessed.

Other examples are constants like PI, that I would like to contain under a Math FB (similar to how it is done in C#)

I have a couple of workarounds for this

Work-around 1: Instantiating in GVL or VARs

It is possible to define an instance in a global variable list and then call it from there (this works):

str := GVL.CylinderStatesInst.MoveToWork;

Another option is to create an instance in the VAR list of the current FB/PRG/...

Work-around 2: TO_STRING attribute

Defining an enum and adding the attribute 'to_string':

{attribute 'to_string'}
TYPE CylinderState :
(
    Idle,
    MoveToWork,
    MoveToBase ,
    AtWork,
    AtBase,
    Error
);
END_TYPE

Allows me to do the following:

str := TO_STRING(CylinderState.AtBase);

The greatest downside of this is that I cannot use spaces or special characters.

Work-around 3: flatten into GVL

This is something I do not want to do, but also provides a solution:

VAR_GLOBAL
    CylinderStates_Idle : STRING := 'Idle';
    CylinderStates_MoveToWork : STRING := 'Move to Work';
    CylinderStates_MoveToBase : STRING := 'Move to Base';
    CylinderStates_AtWork : STRING := 'At Base';
    CylinderStates_AtBase : STRING := 'At Base';
    CylinderStates_Error : STRING := 'Error';
END_VAR

Work-around 4: Dedicated GVL:

Simply creating a dedicated GVL also does the trick, and seems like the best option, but the question is if it is possible to define the constants on the FB.

{attribute 'qualified_only'}
VAR_GLOBAL
    Idle : STRING := 'Idle';
    MoveToWork : STRING := 'Move to Work';
    MoveToBase : STRING := 'Move to Base';
    AtWork : STRING := 'At Base';
    AtBase : STRING := 'At Base';
    Error : STRING := 'Error';
END_VAR


Solution

  • Guiorgy's answer is correct, but a part is missing:

    In CODESYS V3, is it possible to externally access VAR_STAT CONSTANTS of an FB without instantiation in CODESYS V3?

    As Guiorgy explained, it's not possible.

    and If not, what would be the most proper way to do so?

    This can be a little relative depending on your application and resources, specifically in your examples it seems that you want to "Categorize" status using STRINGS, usually I do this using Enumerator (Work-around 2), and only converting to STRINGS if I need to display them in a HMI for the user, you could create a "Converter" using a simple function using a CASE OF with direct strings.

    FUNCTION CylinderStateToString : STRING
    VAR_INPUT
        State : CylinderState;
    END_VAR
    VAR
    END_VAR
    
    CASE State OF
        CylinderState.Idle :
            CylinderStateToString := 'Idle';
        CylinderState.MoveToWork :
            CylinderStateToString := 'Move To Work';
        CylinderState.MoveToBase :
            CylinderStateToString := 'Move To Base';
        CylinderState.AtWork :
            CylinderStateToString := 'At Work';
        CylinderState.AtBase :
            CylinderStateToString := 'At Base';
        CylinderState.Error :
            CylinderStateToString := 'Error';
        ELSE
            CylinderStateToString := 'NA';
    END_CASE
    

    And use like that:

    MyTest := CylinderStateToString(State := CylinderState.MoveToBase);
    

    Note that in this case I would put this "Converter" in a low priority task that would only pass the information in STRING to the HMI and in the rest of the program it would only use the enumerator.


    If you need or want to use constants in STRINGS, what I recommend is to use GVL (Work-around 4), in Codesys 3.5 you can create several, so create one with the name CylinderState and declare the constants, then just use it normally:

    enter image description here

    Then you can use it like this:

    PROGRAM PRG_Main
    VAR
        MyTest : STRING;
    END_VAR
    
    MyTest := CylinderState.Idle;
    

    enter image description here

    Here, in the "Input Assistant" you can see how the structure looks like, where we can still use another GVL...

    enter image description here