Construct destruct restruct

Many common programming languages have constructors, and some also have destructors. TwinCAT 3 and CODESYS 3 have the methods that map something like constructors and destructors. These are not real constructors/destructors, but this detail is not important for now. This blog post covers the basics and pitfalls.

There are always 3 methods for each function block:

  •  FB_init : This is the constructor. As you can see from the name, this is not a real constructor. It is just a method to initialize an instance of a function block
  •  FB_exit : This is the destructor. As you can see from the name, this is not a real destructor.
  •  FB_reinit : This method is a PLC peculiarity, that is only used for online change.

Furthermore, this blog article discusses a few pragmas that can and must be used to influence the behavior when creating, destroying and reinitializing.

 

Creating

noun-information-6735183-FFFFFF_edited

To create one of these methods, use only the drop-down menu.

Add method from the context menu: Right click on the class and select Add → Method from the context menu.

 

Creating

 

Add method from the drop-down menu: Use only the drop-down menu to create these special methods.

 

Creating_02

 

FB_init

noun-information-6735183-FFFFFF_edited

The FB_init method always exists and is always called, even if it is not explicitly created.
The FB_init method should never be called explicitly! This can lead to undesirable effects since the parent classes and in part also variables are reinitialized.
The FB_init method of a base class on inheritance is called implicitly a SUPER^.FB_init must therefore not be used.

The FB_initmethod is used to initialize instances in the object. It comes closest to what a constructor is, it is always present and is also always called.

If the method is not explicitly created, then the brackets are omitted in the declaration. If the FB_init method is created, then the brackets must be set during the declaration, even if the method is without parameters.

Example: For the class Foo no FB_initmethod was created. For the class Bar a method without parameters was created. The declaration looks as follows:

foo:Foo;
bar :Bar();

The method already has two input parameters: bInitRetains and bInCopyCode. These parameters cannot be set because they are set implicitly.

They can be read and eventually used to distinguish whether it is an online change or a download. In short this means that if ((bInitRetains = TRUE) AND (bInCopyCode = FALSE)) then it is a download, if ((bInitRetains = FALSE) AND (bInCopyCode = TRUE)) then it is an online change. However, this is only half the truth. Because if above the declaration of an object the pragma {attribute 'call_after_online_change_slot' := '<slotNumber>'} is placed, then it looks again as if it is a download, although a copy code takes place.

 

FB_exit

noun-information-6735183-FFFFFF_edited

The FB_exit method should never be called explicitly!  
The FB_exit method should not be extended by parameters, because these cannot be served with the implicit call.
The FB_exit method of a base class on inheritance is called implicitly. A SUPER^.FB_exit must therefore not be used.

The FB_exit method is called, if available, when an object is to be destroyed, e.g. when the controller changes to stop mode, or an object is regenerated after an online change. The method already has one input parameter: bInCopyCode. This parameter cannot be set because it is set implicitly. bInCopyCode  can be read if the parameter is True. Then the object is destroyed during an online change. The call of the FB_exit method can also be suppressed for individual objects with the pragma {attribute 'no-exit'}.

Example: FE_exit suppression
VAR

foo :Foo();
{attribute 'no-exit'}
zombieFoo :Foo();

END_VAR

For the object foo, foo.FB_exit is called and for the object zombieFoo, zombieFoo.FB_exit is not called.

 

FB_reinit

 

noun-information-6735183-FFFFFF_edited

The FB_reinit method should never be called explicitly!
The FB_reinit method should not be extended by parameters, because these cannot be served with the implicit call.
The FB_reinit method of a base class on inheritance is not called implicitly. A  SUPER^.FB_reinit must therefore be used, when it is needed.

The FB_reinit method is a strange method for which there is no equivalent in other languages. The FB_reinit method is only called in case of an online change and that as the last one. FB_reinit also behaves differently with inheritance than FB_exit and FB_init, because FB_reinit of the base class is not called implicitly. I have not thought of a use case for the FB_reinit method yet, because I cannot use parameters. It can be that the FB_reinit method is used to keep references to global data up to date, but I think this is a bad style.

 

Pragma {attribute ’no_copy‘}

The pragma {attribute 'no_copy'} is an important pragma. It prevents a variable from getting the actual values from the object after an online change. For pointers, references or interfaces it is harmful, if the values are taken from the old object. Therefore, all references, pointers and interfaces which are initialized over FB_init from outside should be provided with the pragma {attribute 'no_copy'}.

Example:
FUNCTION_BLOCK FooBar

VAR
{attribute 'no_copy'}

foo :IFoo;
END_VAR



METHOD FB_init :BOOL

VAR_INPUT
(*if TRUE, the retain variables are initialized (warm start / cold start)*)

bInitRetains :BOOL;

(*if TRUE, the instance afterwards gets moved into the copy code (online change)*)

bInCopyCode :BOOL;

foo :IFoo;
END_VAR
THIS^.foo := foo;
END_METHOD

foo is reinitialized during online change and the value is not overwritten again after initialization.

 

 
Pragma {attribute ‚call_after_online_change_slot‘ := ‚<slotNumber>‘}

With the pragma {attribute 'call_after_online_change_slot' := '<slotNumber>'} a sequence can be defined in which the FB_init methods of the objects are called after an online change.

If an object is provided with the pragma, it is rebuilt for each online change. The sequence is defined by the slot number. The slotNumber must be an integer. The lower the slotNumber the earlier the rebuild takes place. This pragma is important for references, pointers and interfaces. At the same time, it becomes confusing quickly.

Example:
VAR

{attribute 'call_after_online_change_slot' := '1'}
foo :Foo(fooNess := 12);
{attribute 'call_after_online_change_slot' := '2'}
fooBar :FooBar(foo := foo);

END_VAR

In the example, the object foo is rebuilt first and then the object fooBar is rebuilt, so the reference on foo always remains valid if foo uses the pragma {attribute 'no_copy'} in fooBar.

 

Pragma {attribute ‚call_after_init‘}

The pragma {attribute 'call_after_init'} is used to define a method that is called after the FB_init method is called.

 

noun-information-6735183-FFFFFF_edited

This method must not have any parameters.
Classes that have a method with {attribute 'call_after_init'} must also have the pragma above the class header.
In case of inheritance, if the base class uses the {attribute 'call_after_init'} pragma, the specialized class must also have the {attribute 'call_after_init'} pragma.
In case of inheritance, if the base class uses the {attribute 'call_after_init'} pragma, then the method can be overridden with the {attribute 'call_after_init'} pragma. The method of the base class must then be called via the SUPER pointer if necessary

Example:
{attribute 'call_after_init'}
FUNCTION_BLOCK Baz
VAR

baz :INT;

END_VAR

METHOD FB_init
VAR_INPUT

(*if TRUE, the retain variables are initialized (warm start / cold start)*)
bInitRetains :BOOL;
(*if TRUE, the instance afterwards gets moved into the copy code (online change)*)
bInCopyCode :BOOL;
baz :INT;

END_VAR

THIS^.baz := baz;

END_METHOD

{attribute 'call_after_init'}
METHOD doubleBaz

THIS^.baz := (THIS^.baz * 2);

END_METHOD

In this example, you can see that both the class and the method need the {attribute 'call_after_init'} pragma.

{attribute 'call_after_init'}
FUNCTION_BLOCK ExtendedBaz EXTENDS Baz

VAR

isBazValid :BOOL;

END_VAR

METHOD FB_init
VAR_INPUT

(*if TRUE, the retain variables are initialized (warm start / cold start)*)
bInitRetains :BOOL;
(* if TRUE, the instance afterwards gets moved into the copy code (online change)*)
bInCopyCode :BOOL;
baz :INT;

END_VAR

THIS^.isBazValid := FALSE;

END_METHOD

In the example above, you can see that the ExtendedBaz class also needs the {attribute 'call_after_init'} pragma, since the base class also uses it.

 

Pragma {attribute ‚call_on_type_change‘:= ‚<ClassName1>, <ClassNameX>‘}
noun-information-6735183-FFFFFF_edited

The pragma does not work with the FB_init method. 
This method must not have any parameters.

The pragma {attribute 'call_on_type_change':= '<ClassName1>, ... <ClassNameX>'}  can be used to indicate a method, that should be called when one of the classes '<ClassName1>, ... <ClassNameX>' changes.

Personally, I think this mechanism is bad, because the class needs to know exactly about the types used, and maintaining a pragma is almost as hard as maintaining comments. Since I cannot think of an example of what to do there, other than re-reference global data, I will not show an example here.

 

Pragma {attribute ’no-exit‘}

With the pragma, {attribute 'no-exit'} you can suppress the call of FB_exit for an object instance. An example can be found in the part on FB_exit.

 

Orders

Here it will be shown very briefly what is executed when and in which order.

 

First download
  1. Instances are initialized via FB_init
  2. Explicit assignments from outside at declaration foo :Foo(foo := 3) := (fooness := 7, memberOfFookind := 2);
  3. Methods that are provided with the{attribute 'call_after_init'}

 

Re-download
  1. Instances are cleaned up via FB_exit
  2. Instances are initialized via FB_init
  3. Explicit assignments from outside at declaration foo :Foo(foo := 3) := (fooness := 7, memberOfFookind := 2);
  4. Methods that are provided with the {attribute 'call_after_init'}

 

Online change
  1. Instances are cleaned up via FB_exit
  2. New instances are initialized via FB_init
  3. Explicit assignments from outside at declaration foo :Foo(foo := 3) := (fooness := 7, memberOfFookind := 2);
  4. Methods that are provided with the{attribute 'call_after_init'}
  5. FB_reinit method is executed

 

 

Conclusion

The online change is difficult to implement when working with references, dynamic allocation, interfaces, etc. It is definitely possible, but it requires a lot of discipline. Often it is easier to work only with downloads.