Home > IEC 61131-3 (english) > IEC 61131-3: Exception Handling with __TRY/__CATCH

IEC 61131-3: Exception Handling with __TRY/__CATCH

When executing a program, there is always the possibility of an unexpected runtime error occurring. These occur when a program tries to perform an illegal operation. This kind of scenario can be triggered by events such as division by 0 or a pointer which tries to reference an invalid memory address. We can significantly improve the way these exceptions are handled by using the keywords __TRY and __CATCH.

The list of possible causes for runtime errors is endless. What all these errors have in common is that they cause the program to crash. Ideally, there should at least be an error message with details of the runtime error:

Pic01

Because this leaves the program in an undefined state, runtime errors cause the system to halt. This is indicated by the yellow TwinCAT icon:

Pic02

For an operational system, an uncontrolled stop is not always the optimal response. In addition, the error message does not provide enough information about where in the program the error occurred. This makes improving the software a tricky task.

To help track down errors more quickly, you can add check functions to your program.

Pic03 

Check functions are called whenever the relevant operation is executed. The best known is probably CheckBounds(). Each time an array element is accessed, this function is implicitly called beforehand. The parameters passed to this function are the array bounds and the index of the element being accessed. This function can be configured to automatically correct attempts to access elements which are out of bounds. This approach does, however, have some disadvantages.

  1. CheckBounds() is not able to determine which array is being accessed, so error correction has to be the same for all arrays.
  2. Because CheckBounds() is called whenever an array element is accessed, it can significantly slow down program execution.

It’s a similar story with other check functions.

It is not unusual for check functions to be used during development only. Check functions include breakpoints, which stop the program when an operation throws up an error. The call stack can then be used to determine where in the program the error has occurred.

The ‘try/catch’ statement

Runtime errors in general are also known as exceptions. IEC 61131-3 includes __TRY, __CATCH and __ENDTRY statements for detecting and handling these exceptions:

__TRY
  // statements
__CATCH (exception type)
  // statements
__ENDTRY
// statements

The TRY block (the statements between __TRY and __CATCH) contains the code with the potential to throw up an exception. Assuming that no exception occurs, all of the statements in the TRY block will be executed as normal. The program will then continue from the line immediately following the __ENDTRY statement. If, however, one of the statements within the TRY block causes an exception, the program will jump straight to the CATCH block (the statements between __CATCH and __ENDTRY). All subsequent statements within the TRY block will be skipped.

The CATCH block is only executed if an exception occurs; it contains the error handling code. After processing the CATCH block, the program continues from the statement immediately following __ENDTRY.

The __CATCH statement takes the form of the keyword __CATCH followed, in brackets, by a variable of type __SYSTEM.ExceptionCode. The __SYSTEM.ExceptionCode data type contains a list of all possible exceptions. If an exception occurs, causing the CATCH block to be called, this variable can be used to query the cause of the exception.

The following example divides two elements of an array by each other. The array is passed to the function using a pointer. If the return value is negative, an error has occurred. The negative return value provides additional information on the cause of the exception:

FUNCTION F_Calc : LREAL
VAR_INPUT
  pData     : POINTER TO ARRAY [0..9] OF LREAL;
  nElementA : INT;
  nElementB : INT;
END_VAR
VAR
  exc       : __SYSTEM.ExceptionCode;
END_VAR
 
__TRY
  F_Calc := pData^[nElementA] / pData^[nElementB];
__CATCH (exc)
  IF (exc = __SYSTEM.ExceptionCode.RTSEXCPT_ARRAYBOUNDS) THEN
    F_Calc := -1;
  ELSIF ((exc = __SYSTEM.ExceptionCode.RTSEXCPT_FPU_DIVIDEBYZERO) OR
         (exc = __SYSTEM.ExceptionCode.RTSEXCPT_DIVIDEBYZERO)) THEN
    F_Calc := -2;
  ELSIF (exc = __SYSTEM.ExceptionCode.RTSEXCPT_ACCESS_VIOLATION) THEN
    F_Calc := -3;
  ELSE
    F_Calc := -4;
  END_IF
__ENDTRY

The ‘finally’ statement

The optional __FINALLY statement can be used to define a block of code that will always be called whether or not an exception has occurred. There’s only one condition: the program must step into the TRY block.

We’re going to extend our example so that a value of one is added to the result of the calculation. We’re going to do this whether or not an error has occurred.

FUNCTION F_Calc : LREAL
VAR_INPUT
  pData     : POINTER TO ARRAY [0..9] OF LREAL;
  nElementA : INT;
  nElementB : INT;
END_VAR
VAR
  exc       : __SYSTEM.ExceptionCode;
END_VAR
 
__TRY
  F_Calc := pData^[nElementA] / pData^[nElementB];
__CATCH (exc)
  IF (exc = __SYSTEM.ExceptionCode.RTSEXCPT_ARRAYBOUNDS) THEN
    F_Calc := -1;
  ELSIF ((exc = __SYSTEM.ExceptionCode.RTSEXCPT_FPU_DIVIDEBYZERO) OR
         (exc = __SYSTEM.ExceptionCode.RTSEXCPT_DIVIDEBYZERO)) THEN
    F_Calc := -2;
  ELSIF (exc = __SYSTEM.ExceptionCode.RTSEXCPT_ACCESS_VIOLATION) THEN
    F_Calc := -3;
  ELSE
    F_Calc := -4;
  END_IF
__FINALLY
  F_Calc := F_Calc + 1;
__ENDTRY

Sample 1 (TwinCAT 3.1.4024 / 32 Bit) on GitHub

The statement in the FINALLY block (line 24) will always be executed whether or not an exception has occurred.

If no exception occurs within the TRY block, the FINALLY block will be called straight after the TRY block.

If an exception does occur, the CATCH block will be executed first, followed by the FINALLY block. Only then will the program exit the function.

__FINALLY therefore enables you to perform various operations irrespective of whether or not an exception has occurred. This generally involves releasing resources, for example closing a file or dropping a network connection.

Extra care should be taken in implementing the CATCH and FINALLY blocks. If an exception occurs within these blocks, it will give rise to an unexpected runtime error, resulting in an immediate uncontrolled program stop.

The sample program runs under 32-bit TwinCAT 3.1.4024 or higher. 64-bit systems are not currently supported.

  1. CAR
    July 30, 2019 at 12:19 PM

    Sorry if its a stupid question but.
    Why doesn’t it work with 64 systems ?

    • July 30, 2019 at 4:12 PM

      In the case of an exception, quite a lot happens internally. For example, the stack must be cleaned. Especially with deeply nested method calls, this can mean a lot of work. I suspect that memory management is structured under 32 bit differently than under 64 bits. However, I assume that this will be implemented for 64 bit systems in a later build.

  1. No trackbacks yet.

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 )

Google photo

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

Twitter picture

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

Facebook photo

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

Connecting to %s

%d bloggers like this: