IEC 61131-3: Object composition with the help of interfaces

While the notion of inheritance is used readily and frequently, interfaces are rather rarely applied. However, interfaces provide a number of benefits, which increase flexibility of the PLC programs and improve maintenance. The following post will introduce the possibilities of interfaces relating to the IEC 61131-3 standard.

Inheritance appears to be the best known concept of the object-oriented programming. This is certainly because most textbooks cover it as the first topic and in this way put emphasis on it. Objects from the real world are readily represented as classes, which depict an “is-a” relationship in this manner. A dog is a mammal; a mammal is a living organism. Thus, a human or a spider can be mapped fairly simply.

FUNCTION_BLOCK PUBLIC FB_LivingOrganism
FUNCTION_BLOCK PUBLIC FB_Mammal EXTENDS FB_LivingOrganism
FUNCTION_BLOCK PUBLIC FB_Dog EXTENDS FB_Mammal
FUNCTION_BLOCK PUBLIC FB_Human EXTENDS FB_Mammal
FUNCTION_BLOCK PUBLIC FB_Insect EXTENDS FB_LivingOrganism
FUNCTION_BLOCK PUBLIC FB_Spider EXTENDS FB_Insect

Representation as a UML class diagram:

Picture01

Since the term class is more common in object-oriented programming, it is preferred to the term function block in the following.

Inheritance

If one deals with the concept of object-oriented programming, one would notice that inheritance is its best known feature. It allows an easy definition of new classes (subclasses), which are derived from the existing classes (base classes). A subclass inherits all properties, methods, actions, inputs and outputs of the base class. They should be reprogrammed only if their inherited functionalities have to be extended or changed.

While actions, inputs and outputs are always inherited to the subclasses, inheritance of properties and methods can be influenced by the keywords PUBLIC, PROTECTED, INTERNAL and PRIVATE. In the case of defining a function block, creating subclasses, i.e. possibilities of inheritance can also be constrained (PUBLIC, INTERNAL, and FINAL).

However, inheritance brings along with it several disadvantages:

  • Changes of base classes can affect the subclasses, which inherit from it. Subclasses and base classes are closely coupled to one another.
  • If base classes and subclasses are in one PLC library, the inheritance hierarchy of these blocks can not be changed by the user of the PLC library.
  • The IEC 61131-3 standard does not allow, as well as Java and C# multiple inheritance. A subclass always has a base class. Thus, it would be no longer simply be possible to create a function block for Spiderman with the abovementioned class hierarchy. Inheritance from FB_Human and(!) FB_Spider is not possible.
  • Subclasses are always dependent on their base classes. This can make it difficult to reuse a subclass.
NB:

Base classes mutate easily to a “melting pot” of methods and properties, which are required in one or the other subclass. Such “super base classes” get eventually difficult to maintain. Therefore, I try to keep base classes as small as possible. The deeper you are in the inheritance hierarchy, the smaller the classes should be. Likewise, I try to keep the number of inheritance levels small. The maximal value of 6 for the depth of inheritance can serve here as a benchmark.

Interfaces

Interfaces deal with definition of methods and properties, which are implemented in one class. The classes that implement the same interface look identical from outside and can be treated equally. The exact implementation of the required methods and properties is not described by the interface. Each class, which implements the interface, can determine the internal structure itself.

Example:

The interface I_Sample contains the method M_DoFoo() with a return value of type BYTE.

INTERFACE I_Sample
METHOD M_DoFoo : BYTE

Two function blocks implement the interface.

FUNCTION_BLOCK PUBLIC FB_A IMPLEMENTS I_Sample
METHOD M_DoFoo : BYTE
M_DoFoo := 100;
RETURN;

FUNCTION_BLOCK PUBLIC FB_B IMPLEMENTS I_Sample
METHOD M_DoFoo : BYTE
M_DoFoo := 200;
RETURN;

Both function blocks have no other binding except the common interface. Nevertheless, both of them can be equally treated.

PROGRAM MAIN
VAR
  aTest     : ARRAY[1..2] OF I_Sample;
  fbA       : FB_A;
  fbB       : FB_B;
  a         : INT;
  nIndex    : INT;
END_VAR

aTest[1] := fbA;
aTest[2] := fbB;
FOR nIndex := 1 TO 2 DO
  a := aTest[nIndex].M_DoFoo();
END_FOR

The method M_DoFoo() of the function block FB_A is accessed in the first loop pass, the method M_DoFoo of FB_B is accessed in the second loop pass. The implementation of the methods is determined individually in each function block (FB_A and FB_B).

While inheritance is a “is-a” relationship, interfaces can be described as “behaves-as” relationship or “has-a” relationship.

This kind of binding reduces dependencies considerably. Thus, the FOR loop (line 12 … 14) deals with the interface I_Sample all along. The loop works against the interface I_Sample and not against a specific implementation. The function blocks FB_A and FB_B could always be exchanged for other function blocks, as long as these implement the interface I_Sample. There would be no need in any adjustment of a FOR loop.

Since an interface represents a data type, it can be transferred through the inputs or parameters of the methods to the function blocks. The function block calls the methods of the interfaces without concrete knowledge of executed functionality (polymorphism).

Object composition

One function block can implement several interfaces. As a result, functionalities which are defined by interfaces can be combined in one function block. The class hierarchy stays very flat and the dependencies of the function blocks among themselves are minimal. By means of splitting a single assignment into several smaller, there exists no danger of covering all imaginable features in one super base class.

Example:

A PLC library makes the interfaces I_Light, I_Delayable and I_Dimmable available. Each interface has a corresponding implementation (FB_Light, FB_DelayedLight and FB_DimmingLight), which are also located in a PLC library.

InterfacesFunction blocks, with the implemented interfaces
Picture02Picture03

FB_DelayedLight and FB_DimmingLight inherit from FB_Light. FB_Light contains the methods M_On() and M_Off(), as well as the property bControlLevel. This function block serves as a base class, because this base functionality is precisely equal for all function blocks.

Representation as a UML class diagram:

Picture04
Statement of task:

On the basis of this PLC library, a programmer has to draft a function block, which contains functionalities of both FB_DelayedLight and FB_DimmingLight.

It would be appropriate to use the interfaces I_Delayable and I_Dimmable. The function block FB_Light can also be used as a base class. This brings along the advantage, that wherever I_Delayable, I_Dimmable and FB_Light are applied, their own block can also be replaced. There is a small example further on.

FUNCTION_BLOCK FB_MyLight EXTENDS FB_Light IMPLEMENTS I_Delayable, I_Dimmable

Thus, the function block has the following methods and properties:

Picture05
Tip:

The already existing function blocks can be used, so that the methods do not have to be entirely reprogrammed. For this, I have created instances of both FB_DimmingLight and FB_DelayedLight.

VAR
  fbDimmingLight     : FB_DimmingLight;
  fbDelayedLight     : FB_DelayedLight;
END_VAR

I redirect the invocations to these instances in the respective methods:

METHOD M_SetControlLevel
VAR_INPUT
  nControlLevel: BYTE;
END_VAR

fbDimmingLight.M_SetControlLevel(nControlLevel);
IF (nControlLevel = 0) THEN
  fbDelayedLight.M_Off();
ELSE
  fbDelayedLight.M_On();
END_IF

Thus, the logic for dimming or switching on/off does not have to be developed for the second time.

Representation in CFC:

Methods and properties can not be directly graphically represented with the IEC 61131-3 standard. But if it is possible, a corresponding input or output can be created for each method and for each property. Changes in the inputs are forwarded to the respective methods and the states from the properties are assigned to the outputs.

FUNCTION_BLOCK FB_MyLight EXTENDS FB_Light IMPLEMENTS I_Delayable, I_Dimmable
VAR_INPUT
  bRecallMinLevel       : BOOL;
  bSetControlLevel      : BOOL;
  nSetControlLevel      : BYTE;
END_VAR
VAR_OUTPUT
  nControl              : BYTE;
END_VAR
VAR
  rtrigRecallMinLevel   : R_TRIG;
  rtrigSetControlLevel  : R_TRIG;
  fbDimmingLight        : FB_DimmingLight;
  fbDelayedLight        : FB_DelayedLight;
  _nControlLevel        : BYTE;
END_VAR

rtrigOn(CLK := bOn);
IF (rtrigOn.Q) THEN
  M_On();
END_IF

rtrigOff(CLK := bOff);
IF (rtrigOff.Q) THEN
  M_Off();
END_IF

rtrigRecallMinLevel(CLK := bRecallMinLevel);
IF (rtrigRecallMinLevel.Q) THEN
  M_RecallMinLevel();
END_IF

rtrigSetControlLevel(CLK := bSetControlLevel);
IF (rtrigSetControlLevel.Q) THEN
  M_SetControlLevel(nSetControlLevel);
END_IF

fbDimmingLight();
fbDelayedLight();
_bControlLevel := fbDelayedLight.bControlLevel;
IF (_bControlLevel) THEN
  _nControlLevel := fbDimmingLight.nControlLevel;
ELSE
  _nControlLevel := 0;
END_IF

bControl := bControlLevel;
nControl := nControlLevel;

Thus, the block can be easily used in CFC or the like:

Picture06

Request of an interface at runtime

Instances of function blocks can be forwarded to other function blocks as parameters. The function block has therefore unlimited access to all methods, properties, and inputs and outputs of the forwarded block.

Example:

A PLC library has to be extended by a function block, which groups three lighting blocks together. All three blocks can be controlled by the inputs. Thus, all lights should be switched on or off by bAllOn and bAllOff. The input bAllCallMinLevel has to affect only the blocks, which implement the interface I_Dimmable. The control variables are read out through the outputs.

FUNCTION_BLOCK PUBLIC FB_RoomController
VAR_INPUT
  bAllOn            : BOOL;
  bAllOff           : BOOL;
  bAllCallMinLevel  : BOOL;
  refLight01        : REFERENCE TO FB_Light;
  refLight02        : REFERENCE TO FB_Light;
  refLight03        : REFERENCE TO FB_Light;
END_VAR
VAR_OUTPUT
  aControlLevel     : ARRAY[1..3] OF BOOL;
  aDimmLevel        : ARRAY[1..3] OF BYTE;
END_VAR

It should be possible to forward all variants of the lighting blocks to the block. For this reason, FB_Light is used as a data type of the three lighting blocks. In this way, each block derived from FB_Light can be transferred at the inputs.

VAR
  fbRoomController  : FB_RoomController;
  fbLight           : FB_Light;
  fbDelayedLight    : FB_DelayedLight;
  fbDimmingLight    : FB_DimmingLight;
END_VAR
Picture07

The inputs bAllOn and bAllOff are therefore easily reassembled. If a positive edge is detected at input, the reference of the corresponding method is invoked. It has to be controlled before the invocation, whether the reference is valid.

IF (__ISVALIDREF(refLight01)) THEN
  refLight01.M_On();
END_IF
IF (__ISVALIDREF(refLight02)) THEN
  refLight02.M_On();
END_IF
IF (__ISVALIDREF(refLight03)) THEN
  refLight03.M_On();
END_IF

But how can the method M_RecallMinLevel() be called, if there exists a function block, which implemented the interface I_Dimmable? After all, the data type of the input is FB_Light, and it does not know the method M_RecallMinLevel().

The function _QUERYINTERFACE() enables type conversion from one interface reference to the other. If the conversion is successful, the function returns TRUE, and the second parameter contains a reference to the requested interface.

iLight          : I_Light;
iDimmableLight  : I_Dimmable;

IF (__ISVALIDREF(refLight01)) THEN
  iLight := refLight01;
  IF (__QUERYINTERFACE(iLight, iDimmableLight)) THEN
    iDimmableLight.M_RecallMinLevel();
  END_IF
END_IF

Thus, the implementation of an interface can be queried at runtime and afterwards can also be used.

Since the block FB_MyLight is derived from FB_Light, it can be used with FB_RoomController without adaptation. Furthermore, the method FB_RecallMinLevel() is called from FB_MyLight, because it implements the interface I_Dimmable.

Sample (TwinCAT 3.1.4020) on GitHub

Advantages

The block FB_RoomController makes it not only possible to use the blocks from the PLC library (FB_Light, FB_DimmingLight and FB_DelayedLight), but also to forward directly its own blocks derived from FB_Light. If such function blocks implement the interface I_Dimmable, FB_RoomController can access the method M_RecallMinLevel() without knowing the exact type of the lighting block. The mere fact, that I_Dimmable is implemented, is sufficient. In this case, FB_RoomController and the lighting blocks communicate through the interface I_Dimmable.

So far, this problem could be solved with a communication structure. All lighting blocks and FB_RoomController would have as an input/output variable a structure, through which the blocks communicate with each other. But this variant has a disadvantage: what if the block FB_RoomController should be extended? Managing of scenes could serve as an example. Each lighting block has 10 scenes, which should be callable with the help of FB_RoomController. In the previous variant, FB_RoomController had to obtain other communication structures for the management of scenes, or the existing communication structure had to be extended. In both cases, FB_RoomController had to be adapted in such a way that it would be incompatible to the previous one. Development of a completely new blocks (e.g., FB_RoomControllerV2) could be one more option.

Defining a new interface (e.g., I_SceneManager) is more flexible. This interface determined the methods and the properties, which are necessary for the invocation of the respective scenes. If a lighting block has implemented this interface, FB_RoomController can invoke the requested scene. Extensions or adaptations of the existing inputs and outputs of FB_RoomController are not necessary; the block stays downward compatible.

Conclusion

Since inheritance is easy to apply, there is a great temptation to create base classes, which contain all possible functionalities. These functionalities are actually required from only one or the other subclass. After several extension cycles, dependencies arise, which are difficult to maintain. Interfaces give the opportunity to specify a particular task. These interfaces can be accessed during the development of a new function block without applying complex inheritance hierarchies.

Author: Stefan Henneken

I’m Stefan Henneken, a software developer based in Germany. This blog is just a collection of various articles I want to share, mostly related to Software Development.

9 thoughts on “IEC 61131-3: Object composition with the help of interfaces”

  1. Understandable for beginners and strengthened the assets of OOP Professionals.

    Intensive and well documented Jobs. I will forward it to my colleagues.

    All the best for you Stefan and your Blog.

    Jerome / Switzerland

  2. Thanks, I had looked at the interface mechanism before and only got the idea that it was just another kind of user defined data type. It seemed like there should have been a little more to it and you have explained what that little bit more actually is:

    iLight : I_Light;
    iDimmableLight : I_Dimmable;

    IF (__ISVALIDREF(refLight01)) THEN
    iLight := refLight01;
    IF (__QUERYINTERFACE(iLight, iDimmableLight)) THEN
    iDimmableLight.M_RecallMinLevel();
    END_IF
    END_IF

    Thaks again.

  3. Thanks for your nice article.

    It seems you are trying to say that using interfaces is a better approach rather than inheritance, but it’s not the case.
    In fact inheritance and interfaces are two different ways to implement polymorphism. Each of them is useful for particular problems and solutions.
    For example you have noticed about the temptation of creating base classes with all possible functionalities and its future problems. You’re at right and it’s not a good approach, but the solution of this problem is only keeping all classes, not only base classes, as simple as possible. This is the first law of the SOLID principles, which called “Single responsibility principle” and notices that each class (FB) should have only one responsibility. For example writing an FB that both scales analog input values and doing the weighing tasks, is a wrong design.
    So this is a problem of our design not OOP techniques. It’s important to note that using complicated interfaces can also lead us to the same problem as complicated base classes.

    I think, not only in OOP, but in all programming techniques, we should try to keep the building blocks, FBs, FCs, methods, subroutines, etc., as simple as possible.

  4. Can you explain the html tags? I want to understand why these tags are needed to redirect the method calls. Great work by the way. My Beckhoff rep gave me a link to this site and your OOP articles are great!

    1. Never mind I believe it was an error in the browser loading the site as the tags are no longer present in the code snipet. I knew I had never seen HTML inline In TwinCAT before. 😂

  5. Great work making more and more sense of this. Think I found a typo though.

    “Furthermore, the method FB_RecallMinLevel() is called from FB_MyLight, because it implements the interface I_Dimmable.”

    Where you mention FB_RecallMinLevel it should be M_RecallMinLevel.

    Other than that thanks!

  6. Hi, excellent website, I have been struggling to come to a nice OOP methodology of programming with TwinCAT. I use Java mostly along with a host of other regular programming languages and trying to apply the patterns you learn in OOP has been a struggle.

    I had a question though, how would you handle integration with existing libraries in an OOP fashion, where they use the common bDone/bBusy/bError/nErrorId pattern? I have resorted at the moment to essentially creating a function block within a method using VAR_INST parameters, but this feels very unsatisfactory.

    Thanks!

Leave a comment