0

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

Cédric Moers
  • 395
  • 1
  • 10

2 Answers2

2

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

dwpessoa
  • 440
  • 4
  • 15
1

From the codesys documentation on VAR_STAT:

You can access static variables only from within the namespace where the variables are declared (like static variables in C).

I assume this means you can't access them from outside.

However, from your use case, I don't see why you are trying to use a VAR_STAT. From what I understand, they are used to declare variables inside a function that should be retained between calls, for example a counter. A function block is basically equivalent to a C# class, so all the variables declared inside it are retained withing the same instance (unless you use VAR_TEMP).

Especially, you are want constant variables, which can just be declared inside a VAR CONSTANT block inside the function block:

FUNCTION_BLOCK POU
VAR CONSTANT
    K: REAL := 0.5;
    B: REAL := 5;
END_VAR
VAR_INPUT
    in: REAL;
END_VAR
VAR_INPUT
    out: REAL;
END_VAR
VAR
END_VAR
END_FUNCTION_BLOCK

out := K * in + B;
p: POU;
k: REAL := p.K;
Guiorgy
  • 1,405
  • 9
  • 26
  • I used the `VAR_STAT` since this as the same as a static in C#: a variable scope that is shared for all instances. The idea is that the variables declared in VAR_STAT only take up memory once; even if the POU is instantiated multiple times. It could be that VAR CONSTANT has the same result, but I am not sure of this. Your answer is correct, but still requires instantiation of the POU. – Cédric Moers Mar 09 '23 at 16:26
  • [The constants are replaced with the initial value when the code is compiled](https://help.codesys.com/api-content/2/codesys/3.5.17.0/en/_cds_struct_reference_operands/), so they take no additional variables, however, if the literal is big (like long strings) and used in many places, it could bloat the source code I guess. Though, you are right about requiring an instance of the object to access the constants, however, personally I haven't run into a case where I needed to use that constant without using the object itself. Hope you find something that works for you! – Guiorgy Mar 09 '23 at 16:40