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:
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
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/...
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.
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
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
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:
Then you can use it like this:
PROGRAM PRG_Main
VAR
MyTest : STRING;
END_VAR
MyTest := CylinderState.Idle;
Here, in the "Input Assistant" you can see how the structure looks like, where we can still use another GVL...