Object oriented programming (OOP)

Mervis IDE allows using some of the object properties used in languages such as Java, .NET, or C++. The basic type for using of these properties is a function block. Following constructs can be used:

  • Encapsulation
  • Methods, inclusive virtual methods
  • Reference to ancestors (SUPER)
  • Hierarchical structures and inheritance
  • Definition and implementation of interfaces
  • Polymorfism

All features (RETAIN, INPUT, OUTPUT, INOUT, …) are the same as in standard usage of a function block.

Motivation example - counter

The following example shows implementation of a simple counter which has maximum and minimum limits, counts up and down, and returns the current counted value:

FUNCTION_BLOCK CCounter
    VAR
        m_iCurrentValue: INT;
        m_bCountUp: BOOL:=TRUE;
    END_VAR
    
    VAR
        m_iUpperLimit: INT:=+10000;
        m_iLowerLimit: INT:=-10000;
    END_VAR

METHOD GetCount : INT
    GetCount := m_iCurrentValue;
END_METHOD

METHOD Count
    IF (m_bCountUp AND m_iCurrentValuem_iLowerLimit) THEN
        m_iCurrentValue:= m_iCurrentValue-1;
    END_IF;
END_METHOD

METHOD SetDirection
    VAR_INPUT
        bCountUp: BOOL;
    END_VAR

    m_bCountUp:=bCountUp;
END_METHOD

END_FUNCTION_BLOCK

Usage of such block is – regarding declaration – same as with any other block, inclusive explicit initialisation. The methods are then called as follows:

PROGRAM main
    VAR
        counter1 : CCounter;
        counter2 : CCounter := (m_iUpperLimit := 15000, m_iLowerLimit := -10);
        current_value : int;
    END_VAR

    counter1.SetDirection(bCountUp := true);

    counter1.Count();
    
    current_value := counter1.GetCount();

    counter2.Count();
END_PROGRAM

Function block methods

The concept of methods in Mervis IDE is the same as at other object-oriented languages. Methods define operations within instances of a function block, and operate over the data available for the function block instance.

The declaration of a method is closed between keywords METHOD and END_METHOD.

Methods are similar to functions. Name, parameters (input, output, local) and their return value are defined in the same way. Apart from their own parameters, methods can use the data common for the function block.

There may be more methods in a single function block. Methods are allowed to call other methods.

In the current version there is no support for methods in the graphic languages. Methods only can be used in ST language. Implementation of methods in Mervis IDE supports EN and ENO.

Inheritance

The heritability concept in Mervis IDE is the same as in other object-oriented languages. It is possible to define a function block hierarchy, where the ancestor usually represents a more general structure, while the descendant represents a concretisation of it. Mervis IDE does not support multiple heritability, but it supports implementation of multiple interfaces – see below. The hierarchical relation is defined using the keyword EXTENDS.

Example:

FUNCTION_BLOCK Circle EXTENDS GeomShape.

The descendant (a separated function block) has access to all variables and methods of the function block which it herits from. Other methods and data may be added to this functionality.

Virtual methods

Virtual methods are methods, implementation of which may be changed in the function block hierarchy. If such a method is called on an ancestor instance, a corresponding descendant instance will be called. For example, in a hierarchy there is a basic function block GeometryShape which defines a method called “Area” to compute an area of a shape.

A virtual method must be defined by a keyword VIRTUAL, and virtual methods in the descendants must also use this keyword. The method type can not be changed. The “virtuality” must correspond to the ancestor as far as to the level of first declaration.

As an example, see a hierarchy of geometry shapes with a rectangle and a circle, and their implementation of the corresponding formula.

FUNCTION_BLOCK GeomShape

    (* ancestor abstract method *)
    METHOD VIRTUAL Area : REAL
    END_METHOD;

END_FUNCTION_BLOCK

FUNCTION_BLOCK Rectangle EXTENDS GeomShape
    VAR_INPUT
        x, y : real;
    END_VAR

    (* descendant implementation *)
    METHOD VIRTUAL Area : REAL
        Area := x * y;
    END_METHOD;

END_FUNCTION_BLOCK

FUNCTION_BLOCK Circle EXTENDS GeomShape
    VAR_INPUT
        radius : real;
    END_VAR

    (*descendant implementation *)
    METHOD VIRTUAL Area : REAL
        Area := 3.14 * radius * radius;
    END_METHOD;

END_FUNCTION_BLOCK

The usage then may be as follows:

PROGRAM geom_shapes
    VAR
        rect1 : Rectangle := (x := 10, y := 20);
        circle1 : Circle := (radius := 10);

        shape : REF_TO GeomShape;

        rect_area, circle_area : REAL;
    END_VAR

    (* a general ancestor which can be assigned a reference to any descendant *)
    shape := REF rect1;

    (* shape now represents a Rectangle -> calculates the area of a rectangle *)
    (* rect_area will have the value of 200 after execution *)        
    rect_area := shape.Area();

    shape := REF circle1;

    (* shape now represents a Circle -> calculates the area of a circle*)
    (* circle_area will have the value of 314 after execution *)
    circle_area := shape.Area();

END_PROGRAM

Access to intrinsic items and ancestors’ items

It may be needed that a descendant accesses an ancestor’s item, or that a descendant forces access to its own (intristic) item. For this, keywords SUPER (access to ancestor) and THIS (access to intristic item) are used.

In the examle below, there is an abstract ancestor Car with a descendant StreetCar who has a descendant Bmw. In the Description method it is possible to get the description of the ancestor and connect it to intristic description.

FUNCTION_BLOCK Car

 METHOD VIRTUAL Description : STRING
 END_METHOD

END_FUNCTION_BLOCK


FUNCTION_BLOCK StreetCar EXTENDS Car

 METHOD VIRTUAL Description : STRING
  Description := "STREET CAR";
 END_METHOD

END_FUNCTION_BLOCK


FUNCTION_BLOCK Bmw EXTENDS StreetCar

 METHOD VIRTUAL Description : STRING
  (* returns the ancestor text and adds the intristic text *)
  (* result of this method will thus be "STREET CAR BMW" *)
  Description := CONCAT(SUPER.Description(), " BMW");
 END_METHOD

END_FUNCTION_BLOCK

Another example:

FUNCTION_BLOCK Base

    METHOD VIRTUAL Description : STRING

        Description := "Base";

    END_METHOD

END_FUNCTION_BLOCK

FUNCTION_BLOCK Descendant EXTENDS Base

    METHOD VIRTUAL Description : STRING

        Description := "Descendant";

    END_METHOD

    METHOD SuperDescription : STRING

        (* Přesně specifikujeme, že jde o konkrétní metodu na předkovi. *)
        SuperDescription := SUPER.Description();

    END_METHOD

    METHOD ThisDescription : STRING

        (* THIS se nemusí uvádět. Implicitně se předpokládá jako nejbližší scope. *)
        ThisDescription := THIS.Description();

    END_METHOD

END_FUNCTION_BLOCK
 
PROGRAM this_super_prg
    VAR
        fb : Descendant;

        desc, this_desc, super_desc : STRING;
    END_VAR

    desc := fb.Description();                // returns "Descendant"
    this_desc := fb.ThisDescription();        // returns "Descendant"
    super_desc := fb.SuperDescription();    // returns "Base"

END_PROGRAM

Intrinsic function block calculation code

A standard calculation (i.e. without object extension) of a function block can be understood as a “main” method. It has no input or output parameters and uses data available in the calculation code.

This code can also be understood as virtual by marking the whole function block as virtual.

Example:

FUNCTION_BLOCK VIRTUAL ScheduleBase

Interface

The interface concept allows to separate the implementation and specification of properties which the implementation shall comply with. An interface contains a set of method prototypes and does not contain any data items nor variables. The interface definition is bordered by keywords INTERFACE and END_INTERFACE.

An interface may be used in a function block definiton, which means that a function block shall implement all methods defined by the interface. For this, the IMPLEMENTS keyword is used. Another usage is to specify the variable type: In an interface- type variable, function blocks may be assigned, which – be it on their own or due to some of their ancestor – implement an interface.

A function block may implement any number of interfaces.

In the example below, there is an interface named Room, which defines methods to be called in the day time and in the night time. This interface is implemented by a function block LightRoom, which represents a room with a light bulb. The bulb is switched on at night time, and off at day time.

INTERFACE Room
    METHOD DayTime END_METHOD // Called in day-time
    METHOD NightTime END_METHOD // in night-time
END_INTERFACE

FUNCTION_BLOCK LightRoom IMPLEMENTS Room
    VAR
        Light: BOOL;
    END_VAR

    METHOD DayTime
        Light:= FALSE;
    END_METHOD
    
    METHOD NightTime
        Light:= TRUE;
    END_METHOD

END_FUNCTION_BLOCK

Využití v programu:

PROGRAM light_rooms
    VAR
        kitchen : LightRoom;
        
        room_interf : REF_TO Room;
    END_VAR; // class instantiation

    room_interf := REF kitchen;
    
    room_interf.DayTime();
    
END_PROGRAM

Interface inheritance

Interface inheritance is similar to function block inheritance. It also uses the EXTENDS keyword. In the same way, an interface can not inherit more interfaces.

A function block which implements one or more inherited interfaces must implement a union of methods from all interfaces.

Example:

INTERFACE BaseA

    METHOD A END_METHOD

END_INTERFACE


INTERFACE BaseB

    METHOD B END_METHOD

END_INTERFACE

INTERFACE BaseC EXTENDS BaseA

    METHOD C END_METHOD

END_INTERFACE

INTERFACE BaseD

    METHOD D END_METHOD

END_INTERFACE


FUNCTION_BLOCK C IMPLEMENTS BaseC, BaseD

    METHOD A END_METHOD
    METHOD B END_METHOD
    METHOD C END_METHOD
    METHOD D END_METHOD

END_FUNCTION_BLOCK

Polymorphism

From the above text and examples it is apparent that, in various situations, there may exist a type which is not known in advance. Only when the program is running, a particular instance is applied. This can be used in abstractization of a problem. A general algorithm then solves all tasks over the abstract types, and concrete implementations may be supplied by another subject than the library author.

Polymorphism can be used in following situations:

  • Interafce - because an interface does not specify implementation, but only the contact with the environment, it is useful for separation of algorithms and implementations. A good example are I/O transforms. GUI and the whole system supposes that there is an interface which defines transforms. Users may then create a customized implementation for his or her devices (for various temperature sensor types etc.).
  • Function block hierarchy – a very similar usage of an interface. The ancestors may serve in more general calls and libraries similarly to interfaces.
  • Virtual methods - in case of calls at THIS, even in case that the intrinsic instance is an ancestor, the method on the last descendant is called.
  • REF_TO - it is possible to assign a derived function block or interface into variables – see examples.
  • © Energocentrum Plus, s.r.o. 2017 - 2021