With the object orientated options we’ve got available with IEC 61131-3 last years the time has come to talk about design patterns and best practices in the world of PLC programming. In this series of three posts ‘The three pillars of OOP‘ I will explain the concepts of: Encapsulation, Inheritance and Polymorphism with some examples in TwinCAT. This post is about the first pillar ‘Encapsulation’.
What is encapsulation?
Encapsulation groups properties and methods to a single object (function block). With encapsulation we also refer to a function block’s ability to hide data and behavior that are not necessary for the user. Meaning, we make a separation between a function blocks interface and its implementation.
Why is encapsulation important?
- We can specify the accessibility of a function blocks members.
- It helps to protect your data from accidental corruption.
- It helps to keep your code clean and extensible
How do we achieve encapsulation in TwinCAT?
In TwinCAT we can use a function block to build the blue print of an object (like a class in c#). With the help of properties and method we can make ‘entry ports’ to our internal fields and functionalities.
Consider the following function blocks which encapsulates a common implementation for a load cell. For more info about the used algorithm have a look at the documentation of this module.
We create a function block containing backing fields for Cn and EMax, together with a variable which can be linked with a physical input.
FUNCTION_BLOCK fbLoadCell VAR _Cn:LREAL:=1; _EMax:LREAL:=100; UBridge AT %I* : DINT; END_VAR
We can now add the public property getters and setters. Notice that the properties have protection against a zero value which turns out to be use full later on.
PROPERTY PUBLIC Cn : LREAL
IF CN > 0 THEN _Cn:=Cn; ELSE ADSLOGSTR(ADSLOG_MSGTYPE_WARN,'CN cannot be 0',''); END_IF
PROPERTY PUBLIC EMax: LREAL
IF EMax > 0 THEN _EMax:=EMax; ELSE ADSLOGSTR(ADSLOG_MSGTYPE_WARN,'EMax cannot be 0',''); END_IF
With the properties setup we can write the algorithm for for calculating the actual weight. We will do this in a get only property named ‘ActualWeight’. Notice that our properties ‘Cn’ and ‘EMax’ are used as divisors. A zero value here would cause a run time error. Good thing we protected our function block against this potential disaster 🙂 .
PROPERTY PUBLIC ActualWeight : LREAL
VAR UDiff:LREAL; UReff:LREAL :=2147483647; // 12 Volt input END_VAR
UDiff := UBridge * (20.0 / 2147483647); ActualWeight:= (UDiff/UReff)/(EMax/Cn);
And there is our encapsulated function block! In this example we only used the access specifier ‘PUBLIC’ but the IEC 61131-3 defines more:
|INTERNAL||Access from within the same namespace (library of PLC project).|
|PROTECTED||Access from the function block itself or one of its derivatives.|
|PRIVATE||Access from the function block itself only.|
Encapsulation is one of the three pillars of OOP. Encapsulation is about grouping methods and properties in one function block, and hiding and protecting data which is not necessary for the user. This helps us to write SOLID and reusable code. We have seen an example of a load cell function block which groups functionality, hides it internal fields and protects its data against corruption.
That was it for now! In the next post we will talk about the pillar of OOP : ‘Inheritance‘.