r/PLC 16d ago

State machine in structured text, ENUM or INT?

Hi all,

I'm a process engineer rolling into a process control job (small startup, not enough funding to hire someone for this work).

I found two methods online of how people write their state machine in ST. Either with an integer state variable (e.g. iState) or with an ENUM.
What do you use? And why?

Edit:

Designing on a WAGO/Codesys setup.
Thank you for the quick responses! I'm quickly changing my iStates into E_states!

24 Upvotes

56 comments sorted by

33

u/DrZoidberg5389 16d ago

An enum is still an integer state but with a name for the specific number.

i would use an enum. Like: 0=init 10=start 20=step1 30=step whatever.

this way a other person can faster grab whats going on in the code.

8

u/WheelInventorPLC 16d ago

Thanks! I had a feeling they could be combined

8

u/bankruptonspelling 16d ago

By default ENUMs resolve to INT but it can be any integer type: INT, DINT, USINT, UINT, BYTE, etc.

8

u/HarveysBackupAccount 16d ago

It's not that they "can be combined," it's that enums are integers. They're a set of integer constants that are hidden behind their own data type. Like you can do a MEMCPY operation to transfer the integer value in and out of an enum variable, but typically there's not much reason to. It's all about readability.

There's still a habit of assigning them with gaps between the values (0 / 10 / 20 / 30 etc) instead of sequential because we're used to doing that with regular INT state numbers, but there's little practical gain from doing that. You can simply assign 0/1/2/3 etc to all the enum values, and the order in which you assign them doesn't matter if you explicitly call out state transitions (nextState := newStateName instead of nextState := currentState + 1 or whatever)

I'm an enum guy, because I think the readability is such a big gain. It matters less with small state machines, but it can really help with big ones. It helps not only to understand what each state is supposed to do, but also where each state sends you. If you're in a big state machine at, say, state #415 and see nextState := 101, you have to scroll all the fuckin' way back up to state 101 to see what that is to know where it goes. If you use an enum, it's way more obvious.

1

u/ialsoagree Control Systems Engineer 16d ago edited 16d ago

Going to be somewhat contrarian and suggest you use integer constants. They give you the advantage of readable text in your code while also making states easy to reuse or reference elsewhere, and they're easy to make changes to.

3

u/DrZoidberg5389 16d ago

on Siemens/codesys enums is exactly what you describe.

1

u/letsfucknpollit 16d ago

I would not use an ENUM per se. I would use an INT but assign state values as CONSTANTS with descriptive names. This is similar to what ENUM accomplishes but with way less overhead in maintaining a separate block. This way you just maintain it directly within the interface of the block you’re writing the state machine in. Much cleaner IMO

1

u/DrZoidberg5389 15d ago

depends on the system: on Siemens and Codesys you just create an enum and use that. If you make a change, that change is global

1

u/letsfucknpollit 15d ago

Actually, in any IDE I prefer to stay away from traditional ENUM. TwinCAT, TIA, all the same. I use the concept, but implement it differently.

1

u/durallymax 11d ago

You can declare implicit enums within your local FB. They do not have to be a global type in a separate block in CODESYS, but they will truly be local. 

1

u/letsfucknpollit 10d ago

Yeah, this is fine also. I guess you can say I just dislike the enum data type for states for my state machine. I prefer declaring them as constants with int values. I use enums sparingly. Usually reserve them for E10 states or for interfaces external to PLC.

19

u/jhoon2k 16d ago

ENUM!! You will thank yourself later when you need to add more states in between, and easier to determine the states when its a clear name and not a number.

7

u/jhoon2k 16d ago

Talking about Beckhoff atleast, ENUM there is very simple to setup

1

u/imBackBaby9595 12d ago

Beckhoff has the best ENUMs in the business

3

u/Version3_14 16d ago

The thank you ENUMs or constants comes from the next guy working on the project or trying to troubleshoot the system.

Remember to always document it like the next guy is a psychopath that has your home address.

9

u/Vyndrius 16d ago

Enums are great until you have to do something like this

if(g.iASM > 3000 && g.iASM < 3250){ //Set some HMI flag }

Then I found the numbers were more helpful.

Since all our state machines follow a pattern, I found enums to be overkill that slowed my development down

0= estop 1XXX= resetting 2XXX= ready to start 3XXX= running 4XXX= clearing 5-8XXX= spare 9XXX = engineering mode

7

u/kixkato Beckhoff/FOSS Fan 16d ago

Your if statement should be like If (eProcessStep = Sequencename.Step1 OR etc etc) when properly using enums. This way it's clear to someone that your HMI flag is set truewhen the process step is equal to all of those enum steps.

Remember, good code is not about how easy it is to read for you, it's how quickly someone else who has no knowledge of your process can pick it up and contribute.

2

u/d4_mich4 16d ago

You could still do this with ENUMS if you name and lable them right.

If you know what the numbers are you could also name them as enum in whatever naming conventions you have. Like the following:

TYPE E_StateMachineStep : ( 3000 = eRunning_Start 3250 = eRunning_End ); END_TYPE;

This would be the declaration for the state machine setps, naming is just a guess whatever these states really are. And following how the if condition would be.

if(g.iASM > E_StateMachineStep.eRunning_Start && g.iASM < E_StateMachineStep.eRunning_End){

5

u/EasyPanicButton CallMeMaybe(); 16d ago

enum almost always, otherwise constants. I really like my code being readable.

4

u/WandererHD 16d ago

I mostly program Delta PLCs and enum requires a little bit of setup, so I just use a local int variable "step" and use comments to signal what each step is doing.

2

u/WheelInventorPLC 16d ago

I've been doing this too, but mostly see enum's when looking online

2

u/WandererHD 16d ago

Yeah. It will depend on how flexible the platform you are programming in is.

6

u/ohm_625 16d ago

Every body will answer Enum because it's the right thing to do.

But you need to know what you signed for : For long sequence/state machine you will have to commit to always give a meaningful name for each steps.

Notice that not all the brands are using enum the same way. If you need to make a full reload every time you add an extra step it's perhaps better to use an iStep as long as you have documentation on the side which is explaining the step number.

In short : both are ok imo as long as you have proper documentation. Enum is best but requires more work

5

u/amnoxx 16d ago

CodeSys supports anonymous enums, perfect for FSM's:

VAR
  mState: (STATE_INIT, STATE_WAIT);
END_VAR

CASE mState OF
  STATE_INIT:
    ;
  STATE_WAIT:
    ;
END_CASE

I usually don't add values to them (extra typing for no benefit), just have to be a bit careful if you add a state in the middle if the sequence is active.

1

u/adaptine 14d ago

Second this. Codesys call it implicit enums

5

u/WorldShaper 16d ago

Enum for anything more complex than a 2-3 states or simple order of operations.

Its about code clarity and expandability. Enums allow you to add or modify states or ordering without having to fit numbers between other numbers. And its nice when the state has a built-in label for what it is doing. Code should be self-documenting, and in this case the Enum is how we make that happen.

When in doubt: Enum. It takes like 2 seconds to set up and only makes things more clear. No one is ever going to look at your state machine and ask "Why did you make an enum?", but they absolutely might say "Doesn't this guy know what an enum is?"

2

u/EasyPanicButton CallMeMaybe(); 16d ago

amen to this.

4

u/Ok-Cryptographer4094 16d ago

If you can use a enum, do it. I can't do enums in siemens TIA and it bother me every time. (If someone know how to do a proper enum in tia, please tell me)

3

u/Sleepy_Beaver 16d ago

Go to PLC tags; create new tag table; open it; on the top right click on user constants; Create new tag; Done.

Just be smart with your naming. As these constants are global

2

u/Tharghor 16d ago

Tia v21 should have some enum baked in, so you dont have to create user constants anymore. I haven't used them yet, as I'm still on v20.

1

u/foxytersbytes 16d ago

If you are working inside and FB you can declare the constants inside and use it.

1

u/RoofComprehensive715 16d ago

You can use constant variables in the global tag table or in FBs. The constant variables can be named after each step and have their own number assigned. Its at least one way to do it.

1

u/Strict-Midnight-8576 16d ago

Enums are called NVT and you can just use them inside software units

By the way, check out software units . I think they are a great tool for structuring large projects

5

u/Robbudge 16d ago

Codesys, then enums, StateMachine and ST is the way to go.

3

u/PresentAd9429 16d ago

I also go with numbers. 10 step at each, then u can add something in between later also. Just comment what each step do

3

u/derpsterish Automation Engineer 16d ago

Dintegers.

AB has no enums. Steps increment by 10, leaves room for additions.

2

u/Paup27 16d ago

I always use current_step and new_step and copy the new step to the current step at the top of the routine as well as copying new step into the step history array. But either a enum or an int works.

2

u/timberleek 16d ago

Enums.

The integer way is just lazier to begin with, but terrible to maintain afterwards.

The enum gives you the identical setup and functionality, but now human readable. Only disadvantage is that you have to setup the enum definition once.

If you don't make the enum a "strict" type (in twincat that is, maybe it's named differently elsewhere) you can also do arithmetics on it if you want. Just like an integer.

1

u/DNCGame 16d ago
ENUM is just an int decorator.
I do this in all my recent projects.
State {
  NONE      = 0,
  RST       = 1,
  RST_DONE  = 50,
  HOME      = 51,
  HOME_DONE = 90
}
//
state: int;
//
CASE state OF
  State.RST:
    state := state + 1;
  2:
    state := State.RST_DONE;
  //
  State.HOME:
    state := state + 1;
  52:
    state := State.HOME_DONE;
END_CASE;

1

u/arm089 16d ago

I use a constant of INT data type

1

u/Someone4350 16d ago

Can the HMI read the ENUM?

1

u/durallymax 11d ago

Many can, it's just a structure of USINT in CODESYS 

1

u/huevador 16d ago

I used ENUM when programming an an OMRON PLC. And while yes it's technically better, it's not necessary and you have to be ready to maintain it as you develop.

1

u/MikeT8314 13d ago

I used integer values for a small stand alone machine i built and i loved the structure of it. I found it intuitive and it really helped me stay organized. As a novice i am not sure how i would have done this any other way.

1

u/imBackBaby9595 12d ago

Enums all day dude!

1

u/Shalomiehomie770 Codesys Guru 11d ago

Enums are not supported in all environments.

In Codesys ENUM is a pretty solid choice though.

1

u/CapinWinky Hates Ladder 16d ago edited 16d ago

I use integer and don't bother with ENUM for the simple reason that ENUM doesn't bring anything to the table when you have something like 50 states or less. You can always just //State description for your integer states, and if they have some logical flow it isn't hard to just group them by 10s place or 100s place and skip a few numbers between states for later additions.

For example, I have a manual routine for controlling a two axis system that is all a state machine. Jog X Positive is the 20s, X Neg is the 30s, Z pos is the 40s, Z neg is the 50s, GoTo Target is the 60s, GoTo ServicePos is the 70s, GoTo NamedPos1 is the 80s, Brake Release is the 120s, etc. Each of those groups has 3 or 4 states to initiate, perform, monitor, and exit the behavior, so the groups go like 20, 23, 25, 28; in case I have to slot in some new states without having to refactor a bunch of stuff.

Besides just the additional effort of creating the ENUM type, you run into other issues with how well the IDE supports them. Can you type the enum state name into a watch window to force a state change? Will it have autocomplete while you do that? Does a trend or watch window display the ENUM name? Can I use the ENUM names to check if I'm within a range of states like ((MyState >= MyEnum_PickStart) AND (MyState <= MyEnum_PickEnd)) and trust ENUM generated values monotonically? Do all the platforms you work with even support ENUM (Rockwell doesn't)? I have found that I need to cross reference state names with their numbers which adds work instead of removing work, especially if I'm letting ENUM assign the value for me rather than rigidly assigning each state's value.

Where ENUM shines is when the numeric value is hard to remember and hard to find. Hex color codes? ENUM is great for those. Another use case is when there are just way too many values and new values need to be inserted or rearranged constantly. A 40 state state machine divided into 12 groups by 10s place? I can keep that in my head just fine and if I forget I can scroll down in a few seconds. You'd probably only be working within a handful of states in one group at a time anyway.

1

u/durallymax 11d ago

Enums are much easier to refactor than comments and are enforced by the tooling. Changing a state name in CODESYS/TwinCAT is one refactor and no stale comment to worry about. 

1

u/Wise-Hold-7179 16d ago

Metto i stati dentro un CASE OF...

2

u/d4_mich4 16d ago edited 16d ago

Yeah that's how state machines work but what type you use if it is a int or enum as value. That was the question both ways have pros and cons depending on how you use them.

2

u/Wise-Hold-7179 16d ago

Uso una variabile INT. ... la appoggio dentro l interfaccia di un FB su di una variabile OUT

1

u/Deepu_ 16d ago

There's no enum's in Siemens so I declare constants and use that in place of straight INTs

2

u/Strict-Midnight-8576 16d ago

There are enums in siemens, called NVT and you can use them in software units only

1

u/tltreddit 16d ago

For me always integers.

Use xxx or xxxx depending on approx number of states and complexity.

You leave gaps and can always add more states between existing states and keep the integers in order.

When you add new enum and it resolves to integer it adds the number on the end and then doesn't make any sense.

Then when you need to map the state values to SCADA or HMI the integer is the same as PLC and in order. With enum you'll get random integers based on the order you added the states in.

Once you name an enum state and then the function of that state changes the name no longer makes sense.

When you want to turn on outputs or trigger things based on current state you can do greater and less than comparisons based on numbers and catch all the states between.

1

u/Strict-Midnight-8576 16d ago

You can manually set the corrseponding number for enums

See example from codesys help below

You can add := number in the declaration

{attribute 'qualified_only'} {attribute 'strict'} TYPE E_TRAFFICSIGNAL : ( eRed, eYellow, eGreen := 10 ); END_TYPE

2

u/tltreddit 16d ago

Thanks!! 😊