Attribut 'estimated-stack-usage'
Das Pragma übergibt einen Schätzwert für den Stackgrößenbedarf.
Methoden mit rekursivem Aufruf halten einer Stackprüfung nicht stand, weil der Stackverbrauch nicht ermittelt werden kann. Folglich wird eine Warnung für diese Methoden ausgegeben. Um die Warnung zu unterdrücken, können Sie der Methode mit Hilfe des Attributs einen Schätzwert in Bytes für den Stackgrößenbedarf mitgeben. Dann durchläuft die Methode die Stackprüfung erfolgreich.
Syntax: {attribute 'estimated-stack-usage' := '<estimated stack size in bytes>'}
Einfügeort: Erste Zeile über dem Deklarationsteil der Methode
Beispiel:
{attribute 'estimated-stack-usage' := '99'} // 99 bytes
METHOD SampleMethod : INT
VAR_INPUT
END_VAR
Ausgegebene Warnung:
Die Warnung, die für rekursive Methoden ohne Attribut 'estimated-stack-usage' ausgegeben wird, lautet:
„C0298: Berechnung des Stackverbrauchs aufgrund rekursiver Aufrufe unvollständig, beginnend bei '<FB.Methode>'“
In einem Projekt, das im Vergleich zum letzten Erstellen des Projekts unverändert ist, wird die Warnung nur bei Verwendung des Befehls Projekt neu erstellen ausgegeben (nicht bei Verwendung des Befehls Projekt erstellen).
Einstellung der Stackgröße:
Der Wert der maximal möglichen Stackgröße kann in den Real-Time Einstellungen des TwinCAT-Projekts konfiguriert werden (Registerkarte Settings > Global Task Config/Maximal Stack Size [KB]). Der eingestellte Maximalwert steht dabei nicht komplett dem SPS-Projekt zur Verfügung, sondern wird z. B. ein Teil davon vom TwinCAT-Laufzeitsystem gebraucht.
Keine Stackberechnung für Stand-alone SPS-Projekte Aufgrund der Abkoppelung vom System Manager ist eine Berechnung des Stackverbrauchs für ein Stand-alone SPS-Projekt nicht möglich. |
Rekursiver Methodenaufruf
Innerhalb der zugehörigen Implementierung kann sich eine Methode selbst aufrufen: entweder direkt mit Hilfe des THIS-Pointers oder mit Hilfe einer lokalen Variablen für den zugeordneten Funktionsbaustein.
Hinweis | |
Maschinenstillstand durch möglichen Stacküberlauf Bei unerwartet tiefen Rekursionen kann es zu einem Stacküberlauf und damit zu einem Maschinenstillstand kommen.
|
Beispiel: Berechnung der Fakultät
Innerhalb des Funktionsbausteins FB_Factorial
wird die Fakultät einer Zahl auf unterschiedliche Weise berechnet. Die verschiedenen Berechnungen finden jeweils in einer eigenen Methode statt.
- Methode
Iterative
: Iterativ - Methode
Pragmaed
: Rekursiv mit Warnungsunterdrückung - Methode
Recursive
: Rekursiv
Bei der Neuerstellung des Projekts erzeugt nur die Methode Recursive
die Warnung C0298.
Struktur ST_FactorialResult zur Speicherung der Werte:
// Contains the data of the factorial calculation of nNumber.
TYPE ST_FactorialResult :
STRUCT
nNumber : USINT;
nIterative : UDINT;
nRecursive : UDINT;
nPragmaed : UDINT;
END_STRUCT
END_TYPE
Funktionsbaustein FB_Factorial zur Berechnung der Fakultät:
// Factorial calculation in different ways
FUNCTION_BLOCK FB_Factorial
VAR
nNumberIterative : UINT;
END_VAR
Property nNr samt Set-Funktion, zur Übergabe des Parameters für die iterative Methode:
{attribute 'monitoring' := 'variable'}
PROPERTY nNr : UINT
nNumberIterative := nNr;
Iterative Berechnungsmethode:
// Iterative calculation
METHOD PUBLIC Iterative : UDINT
VAR
nCnt : UINT;
END_VAR
Iterative := 1;
IF nNumberIterative > 1 AND nNumberIterative <= 12 THEN
FOR nCnt := 1 TO nNumberIterative DO
Iterative := Iterative * nCnt;
END_FOR;
RETURN;
ELSE
RETURN;
END_IF
Rekursive Berechnungsmethode mit Attribut zur Unterdrückung der Warnung C0298:
// Recursive calculation with suppressed warning
{attribute 'estimated-stack-usage' := '200'}
METHOD PUBLIC Pragmaed : UDINT
VAR_INPUT
nNumber : USINT;
END_VAR
Pragmaed := 1;
IF nNumber > 1 AND nNumber <= 12 THEN
Pragmaed := nNumber * THIS^.Pragmaed(nNumber := (nNumber - 1));
RETURN;
ELSE
RETURN;
END_IF
Der Pragma-Parameter zur Angabe des geschätzten Stackgrößenbedarfs wird beispielhaft auf 200 Byte gesetzt. Dem liegt die folgende Berechnung zugrunde:
Stackverbrauch pro Methodenaufruf:
Rückgabewert UDINT + Eingangsparameter USINT + Methodenpointer = 4 Byte + 1 Byte + 8 Byte = 13 Byte
Stackverbrauch bei maximal 12 Aufrufen:
12 * 13 Byte = 156 Byte, aufgerundet auf 200 Byte
Rekursive Berechnungsmethode:
// Recursive calculation
METHOD PUBLIC Recursive : UDINT
VAR_INPUT
nNumber : USINT;
END_VAR
Recursive := 1;
IF nNumber > 1 AND nNumber <= 12 THEN
Recursive := nNumber * THIS^.Recursive(nNumber := (nNumber - 1) );
RETURN;
ELSE
RETURN;
END_IF
Hauptprogramm MAIN:
PROGRAM MAIN
VAR
fbFactorial : FB_Factorial;
stFactorial : ST_FactorialResult := (nNumber := 9);
END_VAR
// Iterativ
fbFactorial.nNr := stFactorial.nNumber;
stFactorial.nIterative := fbFactorial.Iterative();
// Recursive
stFactorial.nRecursive := fbFactorial.Recursive(nNumber := stFactorial.nNumber);
// Pragmaed
stFactorial.nPragmaed := fbFactorial.Pragmaed(nNumber := stFactorial.nNumber);