07 — METHOD
Behaviour that belongs to the data — called on demand, not every scan.
Estimated 24 minutes.
The command that must not repeat
FB_Pump has a body, and the body runs on every scan — ten times a second, forever, because that is what a scan cycle is. Now add a start command. Written into the body it is not a command at all; it is a condition being re-evaluated six hundred times a minute, and every one of those evaluations can re-latch, re-count and re-arm something. What you want is a piece of the block that runs once, on the scan the operator asks for it, and then stops existing until asked again. That piece is a METHOD.
One block, two kinds of code
Everything between METHOD and END_METHOD executes only when somebody writes P101.Start(Rate := 60). Everything after the last END_METHOD is the body, and it executes on every P101(...) call. Same instance, same variables, two completely different schedules.
Place the calls on the scan timeline
Twelve scans of a 100 ms cycle. The top lane is the block body — it is not clickable, because you do not get to choose when it runs. The three lanes below are method calls: click any cell to call that method on that scan, click again to take it back. Then watch where the instance state actually moves.
One call, one hundred scans
The program calls P101(DryRun := PS_Suction); on every scan, and calls P101.Start(Rate := 60); exactly once, on scan 14. Over 100 scans, how many times does the code inside METHOD Start execute?
- 100 — it is part of the block, so it is part of the scan
- Once — on scan 14, and never again
- 87 — every scan from 14 onward
- Twice — once for the call and once for the body
A function holding the instance keys
Inside a METHOD, VAR_INPUT holds the arguments and plain VAR is scratch that is created on entry and destroyed on return — the same deal a FUNCTION gets, no drawer of its own. You return a value by assigning the method's own name, again exactly like a FUNCTION. What is different is the reach: a method can see the instance's variables. Running, Faulted and Starts belong to P101 and survive the call; Legal is a temporary and is gone the moment the method returns. Access is declared on the header line — PUBLIC for anything the plant may call, PRIVATE for helpers only the block calls, PROTECTED for blocks that extend it.
How a method looks on a rung
Note the R_TRIG in the ST. A method call is an event, so it wants an edge in front of it — a held pushbutton on a bare IF would call Start on every scan, which is the very thing methods exist to avoid.
What this engine says about METHOD
TryPLC runs the IEC 61131-3 second-edition core in your browser, and it stops at the METHOD keyword rather than pretending. That is also honest about the wider world: a large share of installed controllers cannot do methods either, and what those plants use instead is the command word — an INT input that the block decodes in a CASE, one branch per command. Everything you have just learned about scheduling still applies, so the next stage runs the command-word form live and you can watch on-demand behaviour actually execute.
Run the commands into a real skid
P-101, a 7.5 kW end-suction pump, executing on the real engine. HMI_Cmd is the command word: 1 = Start, 2 = Stop, 3 = Reset. Watch two things at once — each command branch fires on exactly one scan, and the ramp and the dry-run protection keep working on every scan in between with nobody calling them.
Two designs for the same pump
Both designs put the command logic inside the block, which is the point. The difference is whether the call site says what it wants in words or in a magic number — and whether the block has to invent its own edge detection.
The command word nobody clears
A colleague deletes the IF Cmd <> Prev guard, so the CASE runs unconditionally. The HMI writes 1 into HMI_Cmd when the operator presses Start and leaves it there. What happens?
- Nothing new — the Start branch already ran once
- The Start branch runs every scan, so Starts counts up ten a second and no other source can ever stop the pump
- The CASE fires on a rising edge automatically
- The block faults on the duplicate command
Four jobs worth a method
Commands first: Start, Stop, Reset, Home, Jog, Acknowledge — a discrete order arriving at a discrete moment, with a BOOL return saying whether the block accepted it. Then calculations against instance state: RuntimeHours(), TotalLitres(), an efficiency figure derived from data the block already holds, computed when somebody asks instead of every 100 ms. Then initialisation: CODESYS calls FB_init once before the first scan with the arguments you wrote in the declaration, which is how a block learns its own tag number and pipe size. And validation: CanStart(SealWater := ...) keeps the rule that decides whether starting is legal in the same object as the data that rule reads.
Sort six jobs into the right half of the block
Pick a job from the tray, then click the deck it belongs in. Nothing is punished and nothing resets — a wrong placement stays where you put it and the scope shows you what P-101 does about it, so pick the job back up and move it when you have seen enough.
FB_init, RuntimeHours, CanStart
FB_init has a signature the runtime dictates: bInitRetains and bInCopyCode first, then your own arguments. THIS^ is how you separate the instance member Tag from the argument also called Tag — the one place in ST where the pointer syntax earns its keep.
The method nobody calls
The failure mode is specific and it always has the same shape: cyclic work moved into a method, and then a call site that does not run every scan. A ramp that steps only when the faceplate is open. A debounce timer that never advances. A dry-run trip that protects the pump only while a human is watching. Nothing errors and nothing logs — the machine just behaves slightly wrong in a way that is very hard to see from the source, because the source reads perfectly. Before you move a line into a method, ask exactly who calls it and how often, and write the answer in a comment above it.
The ramp that arrived in steps
A colleague moves FB_Pump's speed-ramp logic out of the body and into METHOD Ramp, then calls P101.Ramp() from the rung that refreshes the HMI faceplate — a rung that only runs while the faceplate is open. Commissioning goes fine. Six weeks later the operators report that the pump jumps and the discharge check valve hammers. What happened?
- The method is too slow to be called on every scan
- The ramp only advances on the scans the faceplate rung runs, so the reference arrives in steps
- The method's VAR loses its value between calls, so the ramp restarts from zero
- The instance memory was never marked RETAIN
What you learned
- A METHOD is declared inside a FUNCTION_BLOCK with its own VAR_INPUT, its own VAR and a return type, and it returns by assigning its own name — the same shape as a FUNCTION.
- The block body runs on every P101(...) call, which is every scan; a method runs only on the scan somebody writes P101.Start(...). They share the instance memory, not the schedule.
- A method's own VARs are temporary. The state it changes belongs to the instance, which is exactly why the change survives the call.
- Good method work: commands (Start / Stop / Reset / Home), calculations against instance state, FB_init-style initialisation, and validation kept beside the data it validates.
- Bad method work: anything that measures time or protects the machine, because a method only runs when called and the caller will eventually stop calling.
- In ladder a method is a box titled Instance.Method with EN as the call; in ST it is Instance.Method(arg := value), and it wants an edge in front of it.
- This engine rejects METHOD, and so do plenty of installed controllers — the command word decoded in a CASE is the same design without the keyword.
METHOD — done
- You can write a METHOD with arguments, temporaries and a return value, and call it from a rung or a line of ST.
- You can look at any piece of logic and say whether it needs a slice of every scan or a single call — and name what breaks if you get it backwards.
- You watched a command word decoded in a CASE drive a real pump on the engine, one branch per scan, while the ramp underneath it never stopped.