__TRY, __CATCH, __FINALLY, __ENDTRY

Die Operatoren sind eine Erweiterung der Norm IEC 61131-3 und dienen einem gezielten Exception-Handling im IEC-Code.

__TRY, __CATCH, __FINALLY, __ENDTRY 1:

Verfügbar ab TC3.1 Build 4024 für 32 Bit Laufzeitsysteme

Verfügbar ab TC3.1 Build 4026 für 64 Bit Laufzeitsysteme

Syntax:

__TRY
    <try_statements>
 
__CATCH(exc)
    <catch_statements>
 
__FINALLY
    <finally_statements>
 
__ENDTRY
 
<further_statements>

Wenn eine Anweisung, die unter dem Operator __TRY steht, eine Exception produziert, hält das SPS-Programm nicht an. Sie führt stattdessen die Anweisungen unter __CATCH aus und startet damit das Exception-Handling. Danach führt sie die Anweisungen unter __FINALLY aus. Das Exception-Handling endet mit __ENDTRY. Dann führt das SPS-Programm die anschließenden Anweisungen aus (Anweisungen nach __ENDTRY).

Die Anweisungen des __TRY-Blocks, die unterhalb jener Anweisung stehen, die die Exception auslöst, werden nicht mehr ausgeführt. Das bedeutet, dass, sobald die Exception geworfen wird, die weitere Ausführung des __TRY-Blocks abgebrochen wird und die Anweisungen unter __CATCH ausgeführt werden.

Die Anweisungen unter __FINALLY werden immer ausgeführt, d. h. auch dann, wenn die Anweisungen unter __TRY keine Exception werfen.

Eine IEC-Variable für eine Exception hat den Datentyp __SYSTEM.ExceptionCode.

Hinweis

Maschinenstillstand durch abgefangene Gleitkomma-Ausnahmen auf x86-Zielsystemen

Aufgrund einer technischen Einschränkung der x86-Plattform können abgefangene Ausnahmen, die durch eine Gleitkommaoperation verursacht werden, unbeabsichtigte, schwerwiegende Folgen haben:

Das System kann in einen nicht wiederherstellbaren Zustand versetzt werden oder es kann zu einem Maschinenstillstand aufgrund eines Stacküberlaufs kommen. Dabei kann es ebenso vorkommen, dass der Maschinenstillstand nicht umgehend eintritt, sondern später bei weiteren Operationen, die ebenfalls auf dem Stack ausgeführt werden.

  • Verwenden Sie implizite Prüffunktionen, wenn Gleitkommaoperationen innerhalb von Try-Catch-Blöcken auf x86-Zielen verwendet werden.

Beispiel

Der __TRY-Block des folgenden Beispiels enthält einen Zeigerzugriff und eine Division. Im ersten Zyklus wird innerhalb dieses Blocks eine „Access Violation“-Exception geworfen, da der Zeiger pSample zu diesem Zeitpunkt noch ein Null-Pointer ist. Die Verwendung eines Null-Pointers führt zu einer Exception. Im zweiten Zyklus wird innerhalb des __TRY-Blocks eine weitere Exception geworfen – dieses Mal eine Exception aufgrund einer Division durch 0.

Beide Exception-Ursachen werden innerhalb der __CATCH-Anweisungen behoben: die erste Exception durch eine Wertekorrektur des Zeigers pSample und die zweite Exception durch eine Wertekorrektur des Divisors nDivisor.

Dass bzw. wie das Exception-Handling funktioniert, kann zur Laufzeit wie folgt nachvollzogen werden:

Globale Variablenliste „GVL_Exc“:

{attribute 'qualified_only'}
VAR_GLOBAL CONSTANT
    cMaxExc            : UINT := 10;
END_VAR
VAR_GLOBAL
    nExcIndex          : UINT;
    aExceptionHistory  : ARRAY[0..cMaxExc] OF __SYSTEM.ExceptionCode;
END_VAR

Funktion „F_SaveExceptionCode“:

FUNCTION F_SaveExceptionCode
VAR_INPUT
    excInput           : __SYSTEM.ExceptionCode;
END_VAR
// Log the thrown exception into the global exception history array
IF GVL_Exc.nExcIndex <= GVL_Exc.cMaxExc THEN
    GVL_Exc.aExceptionHistory[GVL_Exc.nExcIndex] := excInput;
    GVL_Exc.nExcIndex := GVL_Exc.nExcIndex + 1;
END_IF

Programm „MAIN“:

PROGRAM MAIN
VAR
    nCounter1          : INT;
    nCounter2          : INT;
    nCounter_TRY1      : INT;
    nCounter_TRY2      : INT;
    nCounter_CATCH     : INT;
    nCounter_FINALLY   : INT;
 
    exc                : __SYSTEM.ExceptionCode;
    lastExc            : __SYSTEM.ExceptionCode;
 
    pSample            : POINTER TO BOOL;
    bVar               : BOOL;
    nSample            : INT := 100;
    nDivisor           : INT;
END_VAR
// Counter 1
nCounter1 := nCounter1 + 1;
 
// TRY-CATCH block
__TRY
    nCounter_TRY1 := nCounter_TRY1 + 1;
    pSample^      := TRUE;                // 1. cycle: null pointer access leads to "access violation" exception
    nSample       := nSample/nDivisor;    // 2. cycle: division by zero leads to "divide by zero" exception
    nCounter_TRY2 := nCounter_TRY2 + 1;
 
__CATCH(exc)
    nCounter_CATCH := nCounter_CATCH + 1;
 
    // Exception logging
    lastExc := exc;
    F_SaveExceptionCode(excInput := exc);
 
    // Correct the faulty variable values
    IF (exc = __SYSTEM.ExceptionCode.RTSEXCPT_ACCESS_VIOLATION) AND (pSample = 0) THEN
        pSample  := ADR(bVar);
    ELSIF ((exc = __SYSTEM.ExceptionCode.RTSEXCPT_DIVIDEBYZERO) OR (exc = __SYSTEM.ExceptionCode.RTSEXCPT_FPU_DIVIDEBYZERO)) AND (nDivisor = 0) THEN
        nDivisor := 1;
    END_IF
 
__FINALLY
    nCounter_FINALLY := nCounter_FINALLY + 1;
 
__ENDTRY
 
// Counter 2
nCounter2 := nCounter2 + 1;