Bound Checks (POU CheckBounds)
Funktionen zur Überprüfung der Feldgrenzen: CheckBounds
Aufgabe dieser Überwachungsfunktion ist eine angemessene Behandlung von Verletzungen von Feldgrenzen. Eine Reaktion auf eine Verletzung kann beispielsweise das Setzen eines Error-Flags oder das Verändern des Arrayindex sein. Die Prüfung erfolgt nur bei einem variablen Arrayindex. Ein fehlerhafter konstanter Arrayindex führt zu einem Compilerfehler. TwinCAT ruft die Funktion implizit auf, sobald einer Variablen vom Typ ARRAY Werte zugewiesen werden.
Nach dem Einfügen der Funktion erhalten Sie automatisch erzeugten Code im Deklarationsteil und Implementierungsteil.
![]() | Deklarationsteil nicht verändern Um die Funktionalität der Überwachungsfunktionen zu erhalten, dürfen Sie den Deklarationsteil nicht verändern. Als einzige Ausnahme dürfen Sie lokale Variablen hinzufügen. |
Standardimplementierung
Deklarationsteil:
// Implicitly generated code : DO NOT EDIT
FUNCTION CheckBounds : DINT
VAR_INPUT
index, lower, upper: DINT;
END_VAR
Implementierungsteil:
// Implicitly generated code : Only an Implementation suggestion
{noflow}
IF index < lower THEN
CheckBounds := lower;
ELSIF index > upper THEN
CheckBounds := upper;
ELSE
CheckBounds := index;
END_IF
{flow}
Beim Aufruf erhält die Funktion folgende Eingangsparameter:
- index: Index des Arrayelementes
- lower: Untergrenze des Arraybereichs
- upper: Obergrenze des Arraybereichs
Rückgabewert ist der Index des Arrayelements, sofern sich dieser im gültigen Bereich befindet. Ansonsten gibt TwinCAT je nach Verletzung des Grenzbereichs die Ober- oder Untergrenze zurück.
Beispielimplementierung
Deklarationsteil:
// Implicitly generated code : DO NOT EDIT
FUNCTION CheckBounds : DINT
VAR_INPUT
index, lower, upper: DINT;
END_VAR
// User defined local variables
VAR
sMessageLow : STRING := 'CheckBounds: Index too low (%d)';
sMessageHigh : STRING := 'CheckBounds: Index too high (%d)';
END_VAR
Implementierungsteil:
{noflow}
// Index too low
IF index < lower THEN
CheckBounds := lower;
// Increase global counter
GVL_CheckBounds.nTooLow := GVL_CheckBounds.nTooLow + 1;
// Log message
ADSLOGDINT(msgCtrlMask := ADSLOG_MSGTYPE_WARN,
msgFmtStr := sMessageLow,
dintArg := index);
// Index too high
ELSIF index > upper THEN
CheckBounds := upper;
// Increase global counter
GVL_CheckBounds.nTooHigh := GVL_CheckBounds.nTooHigh + 1;
// Log message
ADSLOGDINT(msgCtrlMask := ADSLOG_MSGTYPE_WARN,
msgFmtStr := sMessageHigh,
dintArg := index);
// Index OK
ELSE
CheckBounds := index;
END_IF
{flow}
Wie in der Standardimplementierung wird in dieser Beispielimplementierung eine Verletzung des Arraybereichs korrigiert, indem die Ober- oder Untergrenze zurückgegeben wird. Zusätzlich dazu wird jeweils ein globaler Zähler hochgezählt, wenn außerhalb des definierten Arraybereichs auf das Array zugegriffen wird. Die globalen Zähler repräsentieren somit projektweit die Anzahl der Verletzungen der Ober- bzw. Untergrenze. Des Weiteren wird die Funktion ADSLOGDINT aus der Tc2_System-Bibliothek verwendet, um eine Verletzung der Arraygrenzen als Warnung im Meldungsfenster auszugeben.
Anwendungsbeispiel
Im unten stehenden Programm überschreitet der Index die definierte Obergrenze des Feldes aSample. Die CheckBounds-Funktion korrigiert diesen Zugriff auf das Array, welcher außerhalb der definierten Arraygrenzen stattfindet.
PROGRAM MAIN
VAR
aSample : ARRAY[0..7] OF BOOL;
nIndex : INT := 10;
END_VAR
aSample[nIndex] := TRUE;
Die Funktion CheckBounds bewirkt in diesem Beispiel, dass der Index 10 in die Obergrenze des Arraybereichs von aSample (7) abgeändert wird. Damit wird der Wert TRUE dem Element aSample[7] zugewiesen. Auf diese Weise korrigiert die Funktion Arrayzugriffe außerhalb des gültigen Feldbereichs. Es sollte aber unbedingt auch die Fehlerursache, nämlich der Zugriff auf das Element an der Stelle 10, korrigiert werden. Damit dieser automatisch korrigierte, fehlerhafte Zugriff nicht unentdeckt bleibt, wird in diesem Beispiel, wie oben beschrieben, eine Warnung im Meldungsfenster ausgegeben.
Implementierungsbeispiel zur Klassifizierung von Fehlern/Ausnahmen
Globale Variablenliste „GVL_Exc“:
VAR_GLOBAL
nCheckBounds : INT;
nCheckBounds_OutOfBounds : INT;
myException : __SYSTEM.ExceptionCode;
END_VAR
Funktion „CheckBounds“:
// Implicitly generated code : DO NOT EDIT
FUNCTION CheckBounds : DINT
VAR_INPUT
index, lower, upper : DINT;
END_VAR
// Only an implementation suggestion
{noflow}
GVL_Exc.nCheckBounds := GVL_Exc.nCheckBounds + 1;
// Index too low
IF index < lower THEN
CheckBounds := lower;
GVL_Exc.myException := __SYSTEM.ExceptionCode.RTSEXCPT_ARRAYBOUNDS;
GVL_Exc.nCheckBounds_OutOfBounds := GVL_Exc.nCheckBounds_OutOfBounds + 1;
// Index too high
ELSIF index > upper THEN
CheckBounds := upper;
GVL_Exc.myException := __SYSTEM.ExceptionCode.RTSEXCPT_ARRAYBOUNDS;
GVL_Exc.nCheckBounds_OutOfBounds := GVL_Exc.nCheckBounds_OutOfBounds + 1;
// Index OK
ELSE
CheckBounds := index;
END_IF
Beispiel: Erzeugung eines Core Dumps zur Laufzeit in CheckBounds()
Im Folgenden finden Sie ein Beispiel, bei dem die Funktion CreateCallstackCoreDump() in der Funktion Bound Checks (POU CheckBounds) für implizite Prüfungen aufgerufen wird.
Dadurch können Sie herausfinden, in welcher Codezeile ein möglicher Zugriff außerhalb der gültigen Arraygrenzen stattfindet, ohne die Laufzeit mittels Haltepunktes anzuhalten.
Durch den Aufruf von CreateCallstackCoreDump() innerhalb der entsprechenden IF-Zweige von CheckBounds() wird die Aufrufliste (Call Stack) mit der Position des fehlerhaften Arrayzugriffs während der Laufzeit in einem Core Dump gespeichert und kann für Analysezwecke zu einem passenden Zeitpunkt geladen werden.
Funktion CheckBounds:
// Implicitly generated code : DO NOT EDIT
FUNCTION CheckBounds : DINT
VAR_INPUT
index, lower, upper: DINT;
END_VAR
// Implicitly generated code: Only an implementation suggestion
{noflow}
IF index < lower THEN
CreateCallstackCoreDump('CheckBounds_IdxTooLow');
CheckBounds := lower;
ELSIF index > upper THEN
CreateCallstackCoreDump('CheckBounds_ IdxTooHigh');
CheckBounds := upper;
ELSE
CheckBounds := index;
END_IF
Dabei wird beispielsweise ein Core Dump mit dem folgenden Namen erstellt:
Port_851_CallStackDump_CheckBounds_IdxTooLow.2322085807.core
Siehe auch: