🤖 OOP in TwinCAT 3
0%
🤖 A Suspiciously Fun Lesson · Part 2

OOP in TwinCAT 3,
explained with a robot factory.

A FUNCTION_BLOCK is a blueprint, and an instance is a real robot built from it. Methods are its buttons; properties are its settings. Stop copy-pasting code like it's 1998 and build robots instead.

Power on →~4 minutes. Pure TwinCAT 3 Structured Text.
FB_Robot.Move()
01 / The problem

You have twelve conveyors. All slightly different.

The old way is to copy the conveyor logic twelve times, tweak each one, and pray you never find a bug, because fixing it means editing it twelve times. With OOP you write the logic once, as a blueprint.

😩 Copy-paste mountain

Twelve copies, one headache.

// Conveyor_01 logic ........ (200 lines)
// Conveyor_02 logic ........ (200 lines, copied)
// Conveyor_03 logic ........ (oops, typo here)
// ...
// Conveyor_12 logic ........ (who even wrote this)

// Bug found! Now fix it in 12 places. Good luck.

That's 2,400 lines for one brave soul to maintain, and good luck to them.

🌈 One blueprint

Write once. Build many.

FUNCTION_BLOCK FB_Conveyor
// the logic, written ONCE
END_FUNCTION_BLOCK

// then just stamp out instances:
fbConv : ARRAY[1..12] OF FB_Conveyor;

Fix the bug once in the blueprint and all 12 conveyors heal. Chef's kiss.

02 / Blueprint → instances

Meet the FUNCTION_BLOCK.

The blueprint defines what every robot has. Press Build a robot to create instances. Each one gets its own copy of the variables (its own battery!). Select a robot and call its methods to watch its properties change. The robots share one design, but each keeps its own memory.

📐 BLUEPRINTFB_Robot
FUNCTION_BLOCK FB_Robot
VAR
fBattery : INT := 50;
END_VAR

// settings  → PROPERTY
PROPERTY Battery : INT

// buttons   → METHODs
METHOD Charge : BOOL
METHOD Move   : BOOL
METHOD Beep   : BOOL
method = a buttonproperty = a settingvar = private memory
0 / 6 instances
Empty factory floor. Press 🏭 Build a robot to stamp out your first instance.

👆 Build a robot, then click it to call its methods.

(* live call log *)
…waiting for method calls…
03 / Inheritance · EXTENDS

Build a fancier robot without starting over.

Drag the slider to watch a child Function Block inherit everything from its parent, then add its own flair. The keyword is EXTENDS, and it's how TwinCAT 3 does inheritance.

FUNCTION_BLOCK FB_Robot
VAR
  fBattery : INT;
END_VAR

METHOD Move : BOOL
// drive forward
fBattery := fBattery - 15;
PARENT · FB_Robot
Battery : INTMove() : BOOL
Step 1 of 4
Here's our base blueprint FB_Robot. It has a Battery property and a Move() method. Plain, reliable and a bit boring.
04 / Interfaces · polymorphism

One charger. Any machine.

An INTERFACE is a promise: "I can do this." Any Function Block that IMPLEMENTS I_Chargeable promises to have a Charge() method. The charging station doesn't care whether it's a robot, a forklift or a drone. It loops over them and calls .Charge() on each one, and that's polymorphism.

// the promise
INTERFACE I_Chargeable
METHOD Charge : BOOL

// three very different machines, same promise
FUNCTION_BLOCK FB_Robot    IMPLEMENTS I_Chargeable
FUNCTION_BLOCK FB_Forklift IMPLEMENTS I_Chargeable
FUNCTION_BLOCK FB_Drone    IMPLEMENTS I_Chargeable

// now treat them all the same way
aDocked : ARRAY[0..2] OF I_Chargeable;

FOR i := 0 TO 2 DO  aDocked[i].Charge();END_FOR
🔌 Charging station
fbRobot
30%
fbForklift
15%
fbDrone
45%
Three different Function Blocks, one array, one loop. Add an FB_Submarine tomorrow? That's fine, because the loop doesn't change.
05 / Pop quiz, hotshot

Did any of that compile?

Four questions, and nothing gets downloaded to the live PLC, so your dignity survives.

Q1

In TwinCAT 3, what's the difference between a FUNCTION_BLOCK and its instance?

Q2

`FUNCTION_BLOCK FB_DancingRobot EXTENDS FB_Robot`. What does FB_DancingRobot get?

Q3

You override Move() in the child but still want the parent's original logic to run. You call…

Q4

Why loop over an `ARRAY[0..2] OF I_Chargeable` instead of the concrete types?

06 / The cheat sheet

Tape this to your second monitor.

BLUEPRINT + INSTANCE
FUNCTION_BLOCK FB_Robot
// ... members ...
END_FUNCTION_BLOCK

fbWallE : FB_Robot;  // instance
METHOD + PROPERTY
fbWallE.Move(distance := 10);
fbWallE.Battery := 80;  // SET
x := fbWallE.Battery;  // GET
EXTENDS + SUPER
FUNCTION_BLOCK FB_Dancer
EXTENDS FB_Robot
// in overridden Move():
SUPER^.Move();  // reuse parent
INTERFACE + IMPLEMENTS
INTERFACE I_Chargeable
METHOD Charge : BOOL

FUNCTION_BLOCK FB_Robot
IMPLEMENTS I_Chargeable

🙅 Don't be that automation engineer

  • Don't confuse THIS^ (me) with SUPER^ (my parent). One reuses, one loops forever.
  • Don't put logic in a PROPERTY getter that takes 3 scan cycles. Properties should be cheap.
  • Don't EXTENDS ten levels deep. If your family tree needs a scrollbar, rethink it.
  • Don't forget: an interface is a promise, not code. Implement every method or it won't compile.
  • Don't __NEW/__DELETE pointers on the PLC unless you enjoy memory leaks at 3am.