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.
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.
// 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.
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.
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.
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
👆 Build a robot, then click it to call its methods.
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;
FB_Robot. It has a Battery property and a Move() method. Plain, reliable and a bit boring.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
FB_Submarine tomorrow? That's fine, because the loop doesn't change.Four questions, and nothing gets downloaded to the live PLC, so your dignity survives.
FUNCTION_BLOCK FB_Robot // ... members ... END_FUNCTION_BLOCK fbWallE : FB_Robot; // instance
fbWallE.Move(distance := 10); fbWallE.Battery := 80; // SET x := fbWallE.Battery; // GET
FUNCTION_BLOCK FB_Dancer EXTENDS FB_Robot // in overridden Move(): SUPER^.Move(); // reuse parent
INTERFACE I_Chargeable METHOD Charge : BOOL FUNCTION_BLOCK FB_Robot IMPLEMENTS I_Chargeable
THIS^ (me) with SUPER^ (my parent). One reuses, one loops forever.PROPERTY getter that takes 3 scan cycles. Properties should be cheap.EXTENDS ten levels deep. If your family tree needs a scrollbar, rethink it.__NEW/__DELETE pointers on the PLC unless you enjoy memory leaks at 3am.