ERROR (ENUM)

TYPE ERROR :

Every function block in this document has a boolean output xError to indicate that there is an error condition reached. In this case a more detailed information will be signaled with the value of the output iErrorID of the functionblock BehaviourModel. The iErrorID represents an Integer value, indicating the reason for the error. It is neccecary to map the integer value of iErrorID to an domain specific enum data type ERROR.

Error Domains and Error Codes (ERROR (Enum) and eErrorID (output)) and their organisation in different domains:

In many cases this integer value of eErrorID is used as input for an additional FB which converts the number to a related localized string in an applicable language. The set of values for a specific eErrorID is are application dependent. In case several libraries are combined (several domains), there can be an overlap in the numbers of the eErrorID, meaning that the same number identify a different error condition in a different domain. For this reason an value range definition for eErrorID per library must be done.

The error handling of a function block should be designed in a way that only error codes are returned, which are documented in the affected library. It is very convenient but not recommended simply to return untreated error codes from sub libraries. This would result in a bad user experience. It is recommended to map foreign error codes to the error range of the affected library.

In the following example we take a closer look to the relationship between two libraries. Each with a specific domain of error codes. The first library may be called the “Memory Block Manager library” and is build in the namespace MBM. The second library my be called the “Function Block Factory” and is build in the namespace FBF. Each library defines its own ERROR Enum data type.

The ERROR Enum of the library “Memory Block Manager” (MBM)

{attribute 'qualified_only'}
TYPE ERROR : (
    NO_ERROR := 0, // The defined operation was executed successfully
    NO_MEMORY := 10 // The memory pool has no further capacity
    HANDLE_INVALID := 20, // The object was not created properly or has been already released
    WRONG_ALIGNMENT := 30, // The structure description aligns not properly to the block specification
    (*...*)
END_TYPE

The ERROR Enum of the library “Function Block Factory” (FBF)

{attribute 'qualified_only'}
TYPE ERROR : (
    NO_ERROR := 0, // The defined operation was executed successfully
    TIMEOUT := 1, // The specified operation time was exceeded
    INVALID_PARAM := 10, // One or more function parameters have no valid value
    NO_MEMORY := 20, // The extension of memory pool is not possible
    (*...*)
END_TYPE
  • Two libraries are isolated with a namespace (in this example FBF and MBM).

  • Each ERROR Enum declaration should respect two predefined error codes.

    • NO_ERROR ⇒ 0 (Zero)

    • TIME_OUT ⇒ 1 (One)

  • If the TIME_OUT error code has no usage in a specific domain the value should not reused for an other error code.

  • Any error code need a short description about the background of its error condition.

  • A Enum data types should be isolated from other Enum data types with its own namespace ({attribute 'qualified_only'}). FBF.ERROR.NO_MEMORY has a completely different meaning as MBM.ERROR.NO_MEMORY.

Working together with sub libraries brings up the need for mapping the different error domains to the one local domain. The next example demonstrates the possible design of an error code mapping function. It handles the error codes (from CS.ERROR and CO.ERROR) of two sub libraries and tries to map these to the one local Error Enum (CANOPEN_KERNEL_ERROR) (All enum data types in this example have the base type INT).

Example of an error code mapping function “MapError”

FUNCTION MapError : CANOPEN_KERNEL_ERROR
VAR_INPUT
       iErrorID : INT;
END_VAR

MapError := CANOPEN_KERNEL_ERROR.CANOPEN_KERNEL_UNKNOWN_ERROR;
IF iErrorID = CS.ERROR.NO_ERROR THEN
    MapError := CANOPEN_KERNEL_ERROR.CANOPEN_KERNEL_NO_ERROR;
ELSIF iErrorID > CS.ERROR.FIRST_ERROR AND iErrorID < CS.ERROR.LAST_ERROR THEN
    CASE iErrorID OF
        CS.ERROR.TIME_OUT           : MapError := CANOPEN_KERNEL_ERROR.CANOPEN_KERNEL_TIMEOUT;
        CS.ERROR.REQUEST_ERROR      : MapError := CANOPEN_KERNEL_ERROR.CANOPEN_REQUEST_ERROR;
        CS.ERROR.WRONG_PARAMETER    : MapError := CANOPEN_KERNEL_ERROR.CANOPEN_WRONG_PARAMETER;
        CS.ERROR.NODEID_UNKNOWN     : MapError := CANOPEN_KERNEL_ERROR.CANOPEN_NODEID_UNKNOWN;
        CS.ERROR.SDOCHANNEL_UNKNOWN : MapError := CANOPEN_KERNEL_ERROR.CANOPEN_SDOCHANNEL_UNKNOWN;
    ELSE
        MapError := CANOPEN_KERNEL_ERROR.CANOPEN_KERNEL_OTHER_ERROR;
    END_CASE
ELSIF iErrorID > CO.ERROR.FIRST_ERROR AND iErrorID < CO.ERROR.LAST_ERROR THEN
    CASE iErrorID OF
        CO.ERROR.TIME_OUT       : MapError := CANOPEN_KERNEL_ERROR.CANOPEN_KERNEL_TIMEOUT;
        CO.ERROR.NO_MORE_MEMORY : MapError := CANOPEN_KERNEL_ERROR.CANOPEN_NO_MORE_MEMORY;
        CO.ERROR.WRONG_PARAMETER: MapError := CANOPEN_KERNEL_ERROR.CANOPEN_WRONG_PARAMETER;
        CO.ERROR.NODEID_UNKNOWN : MapError := CANOPEN_KERNEL_ERROR.CANOPEN_NODEID_UNKNOWN;
        CO.ERROR.NETID_UNKNOWN  : MapError := CANOPEN_KERNEL_ERROR.CANOPEN_NETID_UNKNOWN;
    ELSE
        MapError := CANOPEN_KERNEL_ERROR.CANOPEN_KERNEL_OTHER_ERROR;
    END_CASE
END_IF

This design assumes CS.ERROR.NO_ERROR has the same value as CO.ERROR.NO_ERROR and the rest of the value range of CS.ERROR and CO.ERROR is disjunct.

Attributes:

qualified_only

InOut:

Name

Initial

NO_ERROR

0

TIME_OUT

1

WRONG_TRANSITION

-1

WRONG_CONFIGURATION

-2