Structured text

Lucask

Member
Join Date
Apr 2017
Location
Jutland
Posts
13
Hi,

I'm trying to get some good habits regarding structured text.

So that's why I'm looking for some info from others with experience in that area.

I've added a small program where the same output is controlled in each step (or where it is needed)

Would you handle it like this.

Case Istate of

0:
Motor1:=FALSE;

IF Statement_1 then
Istate:=10;
END_IF

10:
Motor1:=TRUE;

IF Statement_2 then
Istate:=20;
END_IF

20:
Motor1:=FALSE;

IF Statement_3 then
Istate:=30;
END_IF

30:
Motor1:=TRUE;

And so on...

End_Case

Or more like when making a Ladder program, where the output is only set one place, and then controlled by the steps.

Like this:

If Istate = 10 OR Istate = 30 then
Motor1:=TRUE;
ELSE Motor1:=FALSE;


Thanks!
 
1st way is easier to follow the sequence.
2nd way is easier to trace back an output.

Be consistent.

I prefer the second way.

But your if else statement is better as:
Motor1 := ( Istate = 10 ) OR ( Istate = 30 ) ;

Also, if your controller supports ENUM you should definitely use them instead of numbers.
 
Well..... Both ways will work.
Personally i prefer the first way you do it.
In case you had 50 steps where you would set Motor1 to true, the if statement would be quite a hassle to look at.

Both ways will work fine though.
Some people will tell you to be consistent with the way you choose.. some people will tell you to use the best way suited in the specific case.

Instead of choosing which of the 2 ways to do it, I would focus more on your layout of the code.

1: Use indentation

IF bStatement1 THEN
bOutput1 := TRUE;
ELSE
bOutput1 := FALSE;
END_IF;

2: Keep Keywords in either lower case or UPPER case

3: Keep consistent naming of your tags.
In the example code your example you use prefix Istate and not on Statement_1

In my case iState looks more readable to me.... but that is because i use lower camel case tag naming myself.

4: Use constants for state names in your case statement, instead of 10, use a constant with a value of 10. This makes it easier to read, and you wont end up having to change "10" a thousand places in case you end up needing to change the state number.

5: Use a have a init or reset step, where all tags set in the statemachine is reset to their initial values.

Take a look at PlcOpen's programming guidelines.
It is really good reading.
 
For simple boolean control the second way is better.

I use CASE for complicated sequences, benefits

1. You can store last active step (good for debugging in a sequence and restarting conditions)

2. Jump to a defined step when there is a global alarm
etc.

Sample as below ( I copied this from my PLC code so the formatting is messed up in this page):


IF g_xMachineAlarm THEN
g_iSequencer := 999;
END_IF

CASE g_iSequencer OF

0: // Wait for Run execute
g_iLastSequencerStep := 0;

IF g_xRunExecute THEN
g_istepNumExe := 1; // Always start from 1
g_iSequencer := 10;
END_IF

10: // Store current step of the program

g_iLastSequencerStep := 10;

g_iCurrentActiveStep := g_iStepNumExe;
g_iSequencer := 20;

20: // Check Jump

g_iLastSequencerStep := 20;

IF PRg_array[0].astSteps[g_iCurrentActiveStep].iJmpInt > 1 THEN // Jump active
g_iJmpFromStep := g_iCurrentActiveStep;
g_iJmpToStep := Prg_Array[0].astSteps[g_iCurrentActiveStep].iJmpInt;
g_dwCurrentJmpRepCtr := Prg_Array[0].astSteps[g_iJmpFromStep].iRep;
g_xJumpActive := TRUE;
g_iSequencer := 180;
ELSIF PRg_array[0].astSteps[g_iCurrentActiveStep].iJmpInt = 1 THEN
g_xJumpActive := FALSE; // Program end
g_iSequencer := 600;
ELSIF PRg_array[0].astSteps[g_iCurrentActiveStep].iJmpInt = 0 THEN // No Jump , timer disable if rep > 1, timer enable = rep =1
g_dwCurrentRepCtr := Prg_Array[0].astSteps[g_iCurrentActiveStep].iRep;
g_iSequencer := 30;
END_IF



30: // No Jump , Solenoids activation, activate timer for the first time for step changeover and then disable

g_iLastSequencerStep := 30;
g_xCallSolenoidExecute := TRUE;
g_iSequencer := 35;

....

999: // Alarm active, resets

END_CASE

Thanks.



Hi,

I'm trying to get some good habits regarding structured text.

So that's why I'm looking for some info from others with experience in that area.

I've added a small program where the same output is controlled in each step (or where it is needed)

Would you handle it like this.

Case Istate of

0:
Motor1:=FALSE;

IF Statement_1 then
Istate:=10;
END_IF

10:
Motor1:=TRUE;

IF Statement_2 then
Istate:=20;
END_IF

20:
Motor1:=FALSE;

IF Statement_3 then
Istate:=30;
END_IF

30:
Motor1:=TRUE;

And so on...

End_Case

Or more like when making a Ladder program, where the output is only set one place, and then controlled by the steps.

Like this:

If Istate = 10 OR Istate = 30 then
Motor1:=TRUE;
ELSE Motor1:=FALSE;


Thanks!
 
I tend to do it your first way in ST, especially if I have several variables that need to be set to different states throughout the CASE steps. It's just easier from a programming standpoint and also when stepping through the CASE for debugging.

I know setting something to TRUE/FALSE should be done in one place, like in LD, but from my experience, it's not always very practical in ST.
 
Welcome to the forum.

I am not sure what your application will be, but; please remember who your end user is when programming a plc. It is maintenance. you need to know what their capabilities are in regards to programming and what they are used to.

james
 
I've done a lot of state machines for a lot of different kinds of machines and processes. I've settled on setting/unsetting statuses and outputs in each state. I also check in each state if the command that got me in there is still active and if not, I bail back out to the wait state. This does make each of my states have some copied lines in overhead, but it provides maximum flexibility and readability, which has proven more important on a regular basis.

Setting outputs at the end of the state machine based on the active state is simply too hard to follow when state machines get large and it inevitably requires additional conditions besides just the state number if reaction time is a factor. There is a way around the reaction time issue that is fairly clever, but I don't particularly like it. Here is an example (I don't do this):

20..25: //This state code will work for states 20 to 25
<Some Code>
IF(MyCondition) Then
MyState:=21;
END_IF
<SomeCode>


MyOutput:= (MyState=5) OR (MyState=21);

This lets you turn on the output the same scan your condition is true instead of waiting a scan to go the next state.


Instead, I just do stuff like this:

20: //Wait to do the thing state
StatusDoingThing:= 0;
IF(DoTheThing) Then
MyState:= 25;
TheThing.DoIt:= 1;
END_IF

25: //Do the thing
StatusDoingThing:= 1;
IF(NOT(DoTheThing)) Then
MyState:= 0;
TheThing.DoIt:= 0;
ELSE
IF(TheThing.NextStepReady) Then
TheThing.DoNextThing:= 1
MyState:= 30;
END_IF
END_IF

etc...
 
Use it when it's easier to, use ladder when it's easier to.

Don't be obsessed with using ST when ladder could be better.

Also Comments Comments Comments
 
For state machines I have found it very useful to define a string variable and update it for each state with the "name" of the state. For sure it's useful when debugging and also could be brought out to an HMI for describing the sequence to the operator.

I put it on the same line as the number or constant defining the start of the step and it usually takes the place of a comment I would have put there anyway.

Code:
iState: INT;
sStateName: STRING;
-----
CASE iState OF
   0: sStateName := 'Waiting for Start';
     .
     .
   1: sStateName := 'Down @ Pickup, Gripper Open';
     .
     .
   2: sStateName := 'Close Griper @ Pickup';
     .
     .
   3: sStateName := 'Raise @ Pickup, Gripper Closed';
     .
     .
   4: sStateName := 'Extend to Drop-off';
     .
     .
   5: sStateName := 'Lower @ Drop-off';
     .
     .
     .
     .

END_CASE
 
You can write an entire IF/THEN statement on a single line if the conditions are short and it makes sense. It can sometimes encapsulate the idea of what you are trying to do in a cleaner fashion than 3 separate lines. BUT, it's not for everything.

Here are lines doing an analog conversion for some flow meters. Vibration on the machine means that when there is no flow, the pickups still get some signal and show a very small flow. Forcing these small values to zero does not effect the process and makes the HMI clearer for the operators.

Code:
InputFlow[0]     := AnalogConversion(FM3,20000,4000,15.85,0.0);
IF InputFlow[0]  < MIN_FLOW THEN InputFlow[0]  := 0.0; END_IF;

OutputFlow[0]    := AnalogConversion(FM4,20000,4000,15.85,0.0);
IF OutputFlow[0] < MIN_FLOW THEN OutputFlow[0] := 0.0; END_IF;

InputFlow[1]     := AnalogConversion(FM1,20000,4000,15.85,0.0);
IF InputFlow[1]  < MIN_FLOW THEN InputFlow[1]  := 0.0; END_IF;

OutputFlow[1]    := AnalogConversion(FM2,20000,4000,15.85,0.0);
IF OutputFlow[1] < MIN_FLOW THEN OutputFlow[1] := 0.0; END_IF;
 
1st way is easier to follow the sequence.
2nd way is easier to trace back an output.

Be consistent.

I prefer the second way.

But your if else statement is better as:
Motor1 := ( Istate = 10 ) OR ( Istate = 30 ) ;

Also, if your controller supports ENUM you should definitely use them instead of numbers.
Yes be Consistent
ENUM +1000

Well..... Both ways will work.
Personally i prefer the first way you do it.
In case you had 50 steps where you would set Motor1 to true, the if statement would be quite a hassle to look at.

Both ways will work fine though.
Some people will tell you to be consistent with the way you choose.. some people will tell you to use the best way suited in the specific case.

Instead of choosing which of the 2 ways to do it, I would focus more on your layout of the code.

1: Use indentation

IF bStatement1 THEN
bOutput1 := TRUE;
ELSE
bOutput1 := FALSE;
END_IF;

2: Keep Keywords in either lower case or UPPER case

3: Keep consistent naming of your tags.
In the example code your example you use prefix Istate and not on Statement_1

In my case iState looks more readable to me.... but that is because i use lower camel case tag naming myself.

4: Use constants for state names in your case statement, instead of 10, use a constant with a value of 10. This makes it easier to read, and you wont end up having to change "10" a thousand places in case you end up needing to change the state number.

5: Use a have a init or reset step, where all tags set in the statemachine is reset to their initial values.

Take a look at PlcOpen's programming guidelines.
It is really good reading.
(y)

Welcome to the forum.

I am not sure what your application will be, but; please remember who your end user is when programming a plc. It is maintenance. you need to know what their capabilities are in regards to programming and what they are used to.

james
Wrong, this kind of mind set is how we end up with such terribly written PLC code in the US. You should write code for other competent PLC programmers to understand. That doesn't mean make an inconsistent mess with a whole lot of "clever" logic, but it does mean take full advantage of the Programming tools that the environment provides you and try to maintain good DRY (Don't Repeat Yourself) and SOC (Separation of Concerns) practices. I see so much code that is written by Controls "Engineers" that is a giant knot of spaghetti with glue sauce, when I try to explain DRY and SOC practices to them I might as well be speaking Klingon. Anything other than Copy/Paste throw a NO there, put a NC there is to scary to try to grasp.



I'm not a degree-ed Engineer (no BS in Engineering that is), but I do consider myself to be an engineer. Engineering is a mind set and a way of thinking. I have encounters controls engineers that have real Engineering Degrees and I don't consider them engineers because they create messes.


This topic may need it's own thread.


Use it when it's easier to, use ladder when it's easier to.

Don't be obsessed with using ST when ladder could be better.

Also Comments Comments Comments
Yes

The opposite is true too :)
and yes

You can write an entire IF/THEN statement on a single line if the conditions are short and it makes sense.
IMO that makes it harder to read, especially if you go back and forth between styles in the same project.
 
Wrong, this kind of mind set is how we end up with such terribly written PLC code in the US. You should write code for other competent PLC programmers to understand. That doesn't mean make an inconsistent mess with a whole lot of "clever" logic, but it does mean take full advantage of the Programming tools that the environment provides you

With the risk of high-jacking this thread, I whole-heartily agree with the above. Take advantage of the tools that are provided, including the programming languages. Use the language that is best for the routine you're coding.

"Keep the maintenance people in mind when writing PLC code" is essentially implying to write it all in LD so the maintenance people can understand. If one cannot understand IF_THEN_ELSE, then that person should be nowhere near the PLC in my opinion. It's written in plain English (IF_THEN_ELSE). How much easier does it need to be? Okay, so you might have to learn what a CASE statement is and a FOR loop. No different than the numerous instructions outside of a XIC, XIO, and OTE in a RSLogix ladder program that one had to learn!
 

Similar Topics

Good morning. I'm doing a rehab and I need to recycle some part of the old code that won't change and that I need. This is a calculation that...
Replies
22
Views
1,164
I'm writing some structured text that's handling a data structure that comes from a PC. The PC structure is in the "new" LREAL 64-bit floating...
Replies
3
Views
438
Trying to put these equations in structured text program in a Micro 830. when I run the verify I get this error. ;:expected before this new...
Replies
4
Views
394
Hey all, Studio v20.05 Background. We are piggy backing another system to record attributes for product travelling down the line and several...
Replies
21
Views
3,418
Hi, In our company (programming mainly Beckhoff PLCs) we are establishing internal coding guidelines for ST. Until now, the conventions were...
Replies
5
Views
1,017
Back
Top Bottom