IEC 61131-3: Namespaces

The third edition of IEC 61131-3 introduces the concept of namespaces. Namespaces group elements such as variables, function blocks, data types and libraries into coherent units. This means that elements are no longer identified solely using their names, but additionally using the associated namespace.

I first started looking at this issue in late 2010 in CoDeSys V3. The following examples have been created using TwinCAT 3 and illustrate the various areas in which namespaces have an effect.

Libraries

Identifiers for function blocks, functions and data types must be unambiguous. This applies equally to elements loaded into a project from a library. The use of multiple PLC libraries from different sources is a common cause of naming conflicts. To avoid this, library developers generally insert a prefix in front of each POU or data type identifier (e.g. MC_MoveAbsolute). The aim here is to minimise the likelihood of a naming conflict.

In TwinCAT 3 this problem is resolved by using namespaces. A namespace can be defined as part of the project information for each PLC project (and also therefore PLC libraries). Namespaces are not case sensitive.

Very often, a library’s namespace will simply be the name of the library (but note, however, that this is not the case in this example). If the namespace field is left blank, the namespace defaults to the library name (in the Title field).

Pic01

If libraries containing elements with identical names are added to a PLC project, namespaces can be used to resolve this naming conflict.

Example:

Two PLC libraries both containing an FB_Foo component are added to a project. Any attempt to create an instance of this component will necessarily throw up an error message. For the compiler, it is not clear which of the identically-named components from the two libraries should be used.

Pic02

Adding in the namespace for the relevant library allows this conflict to be resolved.

If no namespace is entered, the compiler will first search within the local PLC project. It is therefore possible to instance an FB_Foo POU contained within the current PLC project. In this case, whether or not a namespace has been entered in the project information panel is irrelevant.

PROGRAM MAIN
VAR
  fbFoo1 : MyNamespace01.FB_Foo;
  fbFoo2 : MyNamespace02.FB_Foo;
  fbFoo3 : FB_Foo;
END_VAR

init_namespace attribute

The init_namespace attribute is a useful tool. If a STRING or WSTRING type variable has this attribute set, the variable is initialised with the name of the current namespace.

FUNCTION_BLOCK PUBLIC FB_Foo
VAR_INPUT
END_VAR
VAR_OUTPUT
  {attribute 'init_namespace'}
  sNamespace : STRING;
END_VAR
VAR
END_VAR

This permits the relevant namespace to be determined at runtime:

Pic03

Sample 1 (TwinCAT 3.1) on GitHub

Modifying namespaces

The namespace for a library is also shown in the reference properties:

Pic04

Should a namespace be ambiguous, it can be modified in these reference properties.

This makes it possible for a project to load multiple versions of the same library.

Pic05

Modifying the namespace enables the required elements from different library versions to be addressed directly.

PROGRAM MAIN
VAR
  eAdsErr01 : Tc2_System_A.E_AdsErr;
  eAdsErr02 : Tc2_System_B.E_AdsErr;
END_VAR

Although the E_AdsErr enum has the same structure in both PLC libraries, they are two different data types. In the above example, this means that the eAdsErr01 and eAdsErr02 variables are not mutually compatible.

eAdsErr01 := eAdsErr02;

This assignment is rejected by the compiler with the following error message:

Implicit conversion from one enumeration type (E_ADSERR
(tc2_system, 3.4.14.0 (beckhoff automation gmbh))) to
another (E_ADSERR (tc2_system, 3.4.13.0 (beckhoff automation
gmbh)))

Global Variable List

A Global Variable List (GVL) is used to declare global variables. The name of the GVL must be unambiguous within the project. The name is not case sensitive. Each GVL is a self-enclosed space (namespace) in which global variables can be declared independently of other GVLs.

In the following example, we declare three variables. All three are called var01. Two are declared in separate GVLs (GVL01 and GVL02), the third locally in MAIN.

Pic06

To allow these variables to be addressed unambiguously, the name of the GVL must additionally be placed in front of the variable name, separated by a dot.

GVL01.var01 := 1; // global variable in GVL01
GVL02.var01 := 2; // global variable in GVL02
var01 := 3;       // local variable in MAIN

If the variable var01 exists only in a single GVL, there is no need to prefix the variable name with the GVL. If, however, there is also a local variable with the same name, then the GVL name does have to be included. Alternatively, you can also prefix the variable name with just a dot on its own. This designates the global namespace.

.var01 := 1; // global variable
var01 := 3;  // local variable

The dot in front of the variable name indicates that we wish to access the global variable called var01. In this case, however, the variable must again be present in just a single GVL.

qualified_only attribute

The name of the GVL should always be used, even where it is not strictly required. If the qualified_only attribute is present in the GVL, an error message will be output if an attempt is made to address a variable without prefixing it with the GVL name.

{attribute 'qualified_only'}
VAR_GLOBAL
  var01 : INT;
  var02 : INT;
END_VAR

It is therefore always necessary to include the name of the GVL when addressing a global variable. This makes the program easier to read and makes it easier to avoid side effects between local and global variables.

Sample 2 (TwinCAT 3.1) on GitHub

GVLs in libraries

If a GVL is in a library, in addition to the GVL name it is also possible to use the namespace of the library.

In the following example, three variables, all called var01, are declared in different places:

  • – in GVL01 in a library with the namespace MyNamespace01
  • – in GVL01 in the local PLC project
  • – in the local POU

    To address the required variable, the library’s namespace can be used in addition to the GVL name:

    MyNamespace01.GVL01.var01 := 1; // global variable of the GVL inside the library
    GVL01.var01 := 2;               // global variable of the local GVL
    var01 := 3;                     // local variable
    

    If the qualified_only attribute is not used in the local GVL, the GVL can be omitted:

    MyNamespace01.GVL01.var01 := 1; // global variable of the GVL inside the library
    .var01 := 2;                    // global variable of the local GVL
    var01 := 3;                     // local variable
    

    Similarly, the GVL name for the library can also be omitted if the qualified_only attribute is not used in that GVL.

    MyNamespace01.var01 := 1; // global variable of the GVL inside the library
    .var01 := 2;              // global variable of the local GVL
    var01 := 3;               // local variable
    

    If the GVL names are unambiguous, the namespace can also be omitted:

    GVL01.var01 := 1; // global variable of the GVL of the library
    .var01 := 2;      // global variable of the local GVL
    var01 := 3;       // local variable
    

    The final example illustrates the point that omitting namespaces reduces readability of the program. It is harder to identify where the referenced variables are declared. Consequently, it is best to include the qualified_only attribute in every GVL and to use the namespace when using elements from libraries.

    The namespace should also be entered when defining function blocks derived from function blocks from a library and when using interfaces from a library:

    FUNCTION_BLOCK FB_MyFoo EXTENDS MyNamespace01.FB_Foo IMPLEMENTS MyNamespace01.I_Foo
    VAR_INPUT
    END_VAR
    VAR_OUTPUT
    END_VAR
    VAR
    END_VAR
    

    The dialog for creating new FBs supports this process:

    Pic07

    Sample 3 (TwinCAT 3.1) on GitHub

    Enumerations

    When accessing an enumerator from an enumeration, the relevant enumerator can be used directly. To enable enumerated constants to be addressed unambiguously, the type name can also be added.

    TYPE E_Foo01 :
    (
      eNoError := 0,
      eErrorA := 1,
      eErrorB := 2
    );
    END_TYPE
    
    PROGRAM MAIN
    VAR
      eFoo01 : E_Foo01;
    END_VAR
    eFoo01 := eErrorA;
    eFoo01 := E_Foo01.eErrorA;
    

    If the identifier for an enumerated constant is found in multiple type definitions, the type name must be used when addressing it. This allows constants with the same name to be used in multiple enumerations. The result is that it is no longer necessary to add a prefix to every enumerated constant to avoid naming conflicts.

    TYPE E_Foo01 :
    (
      eNoError := 0,
      eErrorA := 1,
      eErrorB := 2
    );
    END_TYPE
    
    TYPE E_Foo02 :
    (
      eNoError := 0,
      eError1 := 1,
      eError2 := 2
    );
    END_TYPE
    
    PROGRAM MAIN
    VAR
      eFoo01 : E_Foo01;
      eFoo02 : E_Foo02;
    END_VAR
    eFoo01 := E_Foo01.eNoError;
    eFoo02 := E_Foo02.eNoError;
    

    If the type definition is in a library, you can also additionally use the library namespace:

    eFoo01 := MyNamespace01.E_Foo01.eNoError;
    

    qualified_only attribute

    Similarly to GVLs, the qualified_only attribute can be used to force the use of the type name:

    {attribute 'qualified_only'}
    TYPE E_Foo01 :
    (
      eNoError := 0,
      eErrorA := 1,
      eErrorB := 2
    );
    END_TYPE
    

    Sample 4 (TwinCAT 3.1) on GitHub

    strict attribute

    The strict attribute is not directly relevant to namespaces, but can be used to improve program readability. We will therefore consider it briefly here.

    A variable with an enumerated type is treated internally as an INT. Consequently, arithmetic operations can be carried out on enumerations and values can be assigned which are not defined by the enumeration. The following example assigns the variable eFoo01 the value 3. The enumeration E_Foo01 does not, however, include this value.

    eFoo01 := E_Foo01.eErrorA + 2;
    

    If the strict attribute is used in the definition of an enumerated type, variables of this type are only able to take values specified by the enumerators.

    {attribute 'strict'}
    TYPE E_Foo01 :
    (
      eNoError := 0,
      eErrorA := 1,
      eErrorB := 2
    );
    END_TYPE
    

    The strict attribute also means that it is no longer possible to perform arithmetic operations which would result in values which are not specified by the enumerators. This affects the following operations:

    Assignment of constants which are not specified by the enumerators
    eFoo01 := 5;
    
    Arithmetic operations with enumerators
    eFoo01 := E_Foo01.eNoError + E_Foo01.eErrorA;
    
    Assignment of variables which are not of the enumerated type
    nVar := 1;      // variable nVar is a INT
    eFoo01 := nVar;
    

    Summary

    Namespaces represent a useful mechanism for avoiding naming conflicts. They remove the need to add prefixes to function and data type identifiers. This makes element names more compact and makes code more readable.

  • 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.

    5 thoughts on “IEC 61131-3: Namespaces”

    1. Hi, thank you very much for this post !

      I have created function blocks that work properly and are very useful to me. So I decided to create a library. I have a problem with a function block that uses a structure that must be adaptable to the project. I give you an example (this is not my real function block):

      FUNCTION_BLOCK FB_xxx
      VAR_INPUT
      xTrigger : BOOL;
      Empty : ST_xxx;
      END_VAR
      VAR_IN_OUT
      aMemory : ARRAY [1..2] OF ST_xxx;
      END_VAR

      Inside the FB, I transfer aMemory[1] to aMemory[2], but the structure ST_xxx could be different dependid the project

      If ST_xxx is declared in the library, iit is not possible to declare the variables inside the structure.

      Can you help me ?
      David

      1. Hi David,

        Take a look to the datatype T_Arg. This will help you to handle the pointer.
        Use ARRAY OF T_Arg instead of ARRAY OF ST_xxx. With the F_ARGCPY you can copy the data from one array to the other.

        FUNCTION_BLOCK FB_Foo
        VAR_INPUT
        END_VAR
        VAR_IN_OUT
        aMemory : ARRAY [1..2] OF T_Arg;
        END_VAR

        IF (aMemory[1].eType = E_ArgType.ARGTYPE_BIGTYPE) THEN
        F_ARGCPY(FALSE, aMemory[2], aMemory[1]);
        END_IF

        Now it’s possible to call FB_Foo with different types of structures.

        Example: Define two structures:

        TYPE ST_A :
        STRUCT
        a : BYTE;
        b : INT;
        END_STRUCT
        END_TYPE

        TYPE ST_B :
        STRUCT
        c : REAL;
        d : UDINT;
        END_STRUCT
        END_TYPE

        Declare in MAIN the FB and an array of ST_A and an array of ST_B.

        PROGRAM MAIN
        VAR
        fbFoo : FB_Foo();
        aMemoryA : ARRAY [1..2] OF ST_A := [(a := 1, b := 300), (a := 0, b := 0)];
        aMemoryB : ARRAY [1..2] OF ST_B := [(c := 1.23, d := 70000), (c := 0, d := 0)];
        aMemory : ARRAY [1..2] OF T_Arg;
        END_VAR

        This will call the FB with the data from ST_A:
        aMemory[1] := F_BIGTYPE(ADR(aMemoryA[1]), SIZEOF(aMemoryA[1]));
        aMemory[2] := F_BIGTYPE(ADR(aMemoryA[2]), SIZEOF(aMemoryA[2]));
        fbFoo(aMemory := aMemory);
        The variable aMemoryA[2] has now the data of aMemoryA[1].

        And this calls the FB with the data from ST_B:
        aMemory[1] := F_BIGTYPE(ADR(aMemoryB[1]), SIZEOF(aMemoryB[1]));
        aMemory[2] := F_BIGTYPE(ADR(aMemoryB[2]), SIZEOF(aMemoryB[2]));
        fbFoo(aMemory := aMemory);
        The variable aMemoryB[2] has now the data of aMemoryB[1].

        An other solution could be the datatype PVOID. PVOID is a pointer an independent data type. In that case you must working with MEMCPY inside the FB. T_ARG will help you to handle pointer, because contains the pointer to the data and the length of the data. You will find more information about T_Arg in:
        https://stefanhenneken.wordpress.com/2018/07/04/iec-61131-3-the-generic-data-type-t_arg

        I hope, this will help you to find a solution for you problem.

        Regards
        Stefan

    Leave a Reply

    Fill in your details below or click an icon to log in:

    WordPress.com Logo

    You are commenting using your WordPress.com account. Log Out /  Change )

    Facebook photo

    You are commenting using your Facebook account. Log Out /  Change )

    Connecting to %s

    %d bloggers like this: