TwinCAT 3 und OOP

Exilim

Level-1
Beiträge
44
Reaktionspunkte
3
Zuviel Werbung?
-> Hier kostenlos registrieren
Hallo liebe Community,

Ich bin am Anfang eines größeren Projektes.
Meine Idee ist es, zukunftsorientiert, dieses mit der IEC61131-3 3rd Edition (OOP) durchzuführen. Leider weiß ich nicht wie der Stand diesbezüglich ist (Teilweise verwirrende Doku auf der HP).
Kann mir vielleicht der ein oder andere ein wenig Feedback diesbezüglich geben?

- z.B. Wie sieht die dynamische Speicherverwaltung aus.
- variable Arraygrenzen ?!
- etc...

Vielleicht reg dieses Thema auch einige zur Diskussion an, wodurch andere widerrum profitieren können :).

Vielen Dank und viele Grüße
 
Hallo,

in TwinCAT 3.1 ist die IEC 61131-3 3rd Edition (soweit ich weiß) vollständig implementiert.
Darüber hinaus gibt es die Möglichkeit zur Laufzeit in der SPS dynamisch speicher zu allokieren. Das können "normale" Basis-Datentypen (Byte, INT, REAL, etc.), Array, Strukturen und FBs sein. Hier muss man allerdings sehr exakt und sauber arbeiten. Das heißt, man muss den Speicher der dynamisch reserviert wurde am ende auch wieder freigeben! Hier sei auf die Verwendung der speziellen FB-Methoden "FB_init", "FB_reinit" und "FB_exit" hingewiesen. Der dynamisch reservierte Speicher wird in TwinCAT von dem Router-Speicher des AMS-Messagerouters abgezweigt. Hier sind derzeit maximal 256 MB möglich! Einzustellen ist das ganze im Projektbaum unter dem Konten "System"->"Realtime" auf der Registerkarte "Settings".

Die Allokation des speichers erfolgt mit den "__new"-Operator. Das Freigeben des Speichers mit dem "__delete"-Operator
Beide Operatoren sind aktuell nicht teil der IEC 61131-3 3rd Edition und zählen zu den IEC-Erweiternden Funktionen!


Für die Initialisierung von FBs kann die oben angesprochene FB-Methode "FB_init" Verwendet werden. Die hat allerdings ein paar unschöne Eigenschaften, die sichtbar werden, wenn man Objekte verkapseln möchte und der äußer und der innere FB nicht von der selben Basisklasse sind. Denn es gilt folgende Regel: sind beide FBs von der gleichen Basis abgeleitet, dann wird beim Aufstarten der SPS von außen nach innen initialisiert. Haben die FBs keine gemeinsame Basis, dann wird von innen nach außen initialisiert. Letzteres macht es leider unmöglich von außen Initialisierungsparameter zu übergeben...
Hier gibt es allerdings eine Abhilfe, nähmlich das Attribut "call_after_init". (falls hier interesse besteht, kann ich da nochmal näher drauf eingehen)...

Die Verwendung von variablen Arraygrenzen ist mittlerweile (Tc 3.1 4020.x) auch möglich (wenn wir von dem gleichen Verhalten sprechen).
Hierzu kann man z.B. bei einem FB eine IN_OUT-Variable definieren:
Code:
VAR_IN_OUT[INDENT]nInputArray1Dim : ARRAY
[*] OF INT; // ein-dimensionales Array
nInputArray2Dim : ARRAY[*,*] OF INT; // zwei-dimensionales Array
[/INDENT]
END_VAR

In dem Code kann man dann mit den Operatoren "LOWER_BOUND" und "UPPER_BOUND" die Array-Grenzen ermitteln.
Code:
[INDENT]lBound := LOWER_BOUND(nInputArray1Dim, 1);
uBound := UPPER_BOUND(nInputArray1Dim, 1);
[/INDENT]
Das Ganze funktioniert auch mit mehrdimensionalen Arrays.

Code:
lBoundDim1 := LOWER_BOUND(nInputArray2Dim, 1);
uBoundDim1 := UPPER_BOUND(nInputArray2Dim, 1);
lBoundDim2 := LOWER_BOUND(nInputArray2Dim, 2);
uBoundDim2 := UPPER_BOUND(nInputArray2Dim, 2);


Außerdem gibt es noch ein paar schöne Beschreibungen wie man z.B. Designpattern in der IEC 61131-3 3rd Edition umsetzen kann (allerdings nicht auf der Beckhoff Homeepage):
"Abstract Factory"-Pattern
"Command"-Pattern

Außerdem ist der Blog allgemein ein Lesezeichen Wert.


Was mir teilweise fehlt, ist die Möglichkeit Methoden überladen zu können... Das ist auch soweit ich weiß nicht angedacht, da man bei der SPS immer sicher stellen muss, das es zur Laufzeit hier nicht kracht und das ganze echtzeitfähig bleibt...

viele Grüße
 
Zuletzt bearbeitet:
Zuviel Werbung?
-> Hier kostenlos registrieren
Hallo ksc,

wow, vielen lieben dank für deine ausführliche Antwort.
Mich würde schon interessieren wie ich mit den Konstruktoren bzw. Destruktoren in Verbindung mit TwinCat umgehen kann/muss (finde die Beckhoff Doku leider furchtbar).

Was mich weiterhin interessieren würde ist deine/eure Meinung bezüglich der ganzen OOP-Sache in Verbindung mit Speicherprogrammierbaren Steuerungen.

Vielen Dank und viele Grüße
 
@Exilim

Ich persönlich bin da echt konservativ und halte gar nichts davon.
Allerdings komme ich aus dem Sondermaschinenbau und da bauen wir ohnehin ständig an den Programmen rum.
Für Bibliotheken und Standardmaschinen (>30 Stück) kann man sicher darüber nachdenken.

Größtes Problem m.E. : Mit den Steuerungsprogrammen müssen mehrere Programmierer und auch Instandhalter umgehen können, diese verstehen können. Fehlersuche in einer OOP-Steuerung stelle ich mit durchaus anspruchsvoll vor, jedenfalls mehr, als in einem "linear" geschriebenen SPS-Programm. Wenn ich sehe, was so in "normalen" Programmen, ob TIA, MSOffice oder Apple-Software alles danebengeht, dann will ich sowas nicht in meiner Produktionsmaschine haben. Die Frage stellt sich auch, was ist nun wichtiger, ein tolles Programm von einem Programmierer, der die Mechanik und Systematik einer Maschine nicht versteht oder umgekehrt. Beides ist Mist und i.d.R. muß mann einen Kompromiss dazwischewn suchen und finden. Bezieht man dann seine Kunden und deren Programmierer und Instandhalter mit in die Überlegungen ein, kommt man eher zu dem Schluß, kein OOP!

Gekapselte Bibliotheken, die ordentöich getestet sind, nehme ich einfach mal davon aus, da will ich gar nicht reinsehen.
 
Hallo Ralle,

vielen Dank für deine Meinung (die ich durchaus teile).

Jedoch kam der Kunde für dem ich das Projekt in Angriff nehme auf die Idee, während der Systemumstellung jetzt auch auf die objektorientiert Programmierung umzusteigen.
Ich bin mir da auch noch ein wenig unsicher, ob der spätere Instandhalter sich das antun möchte...

Viele Grüße
 
Zuviel Werbung?
-> Hier kostenlos registrieren
@Exilim:
Erstmal grundsätzlich : man kann OOP so und so machen ...
Ich habe, gerade für Beckhoff TC3.1, schon ein "richtig wunderschönes" SPS-Programm gesehen wo sich der Programmierer so richtig einen gegeben hat. Durch dieses Programm durchzusteigen, obwohl ich ganz genau wußte, was es macht, hatte schon erheblichen Anforderungscharakter. Das ging schon in Richtung "sich unabkömmlich machen".

Aber so muss es ja nicht grundsätzlich sein. Man kann auch übersichtliche SPS-Programme erstellen und sich dabei an ein Objekt-Modell halten.
Es ist immer eine Frage, wie man es anstellt ...

Gruß
Larry
 
Hallo Larry,

danke für deine Meinung.

Ich denke es wird definitiv Sinn machen, sich in die Thematik einzuarbeiten da doch einige Eigenschaften für die allgemeine Entwicklung von PLC-Programmen nützlich sein könnten. Oder sehe ich das falsch?
Wird das Thema OOP in der Zukunft vielleicht "noch" relevanter?

Viele Grüße
 
Ich würde dir da generell zustimmen.
Wie schon geschrieben ... es ist erstmal eine Frage, wie man es aufzieht (Vererbung, Instanzierung und Einbindung von Instanzen, Zugriffe auf Instanzen, Strukturierung).
Für mich ist es so, dass ich mich von der PC-Seite her (also .Net) für die Visualisierung damit beschäftigt habe. Es hat dann nicht sehr lange gedauert bis sich dies so erworbene Wissen unmittelbar auf meine SPS-Programme und deren Machart ausgewirkt hat. Das Ganze ist auch unabhängig vom Entwicklungssystem - das geht mit Siemens genauso (wobei alle CodeSys-Derivate da schon ein wenig mehr drauf haben).
Ob ich jetzt natürlich OOP-Programme für die SPS schreibe will ich mal dahingestellt sein lassen - ich würde eher sagen, dass sich die Art und Weise des Aufbaus, der Strukturierung und des Daten-Handlings zwischen Bausteinen gewandelt hat (ich meine positiv).

Gruß
Larry
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Hallo,

allgemein finde ich die OOP-Erweiterungen der IEC sehr hilfreich. ich habe mir zum Beispiel einen Logger gebaut der aus einer C#-Komponente (GUI) und einer SPS-Komponente (Lib mit mehreren FBs, alles mit OOP) besteht.
Unter anderem habe ich in der Lib auch einen Basis-FB definiert der die gewünschten Logger-Funktionen zur verfügung stellt. Wenn ich jetzt in der eigentlichen Applikation einen FB schreiben möchte, der loggen können soll, so muss ich den nur noch von dem Basis-FB ableiten (erweitern) und der FB hat automatisch die Logger-Funktionen mit drinnen.

Hinzu kommen bei der OOP nette Funktionen, wie z.B. dass man mit Properties einen Schreibschutz herstellen kann und direkte Skalierungen und Bereichsüberwachungen realisieren kann.

Wichtig ist, dass man versteht, dass man nicht die gesammte Applikation in OOP umsetzten muss. Hinzu kommt, dass man die OOP-Erweiterung in allen Sprachen verwenden kann (KOP, FUP, AWL, ...).
Ich kenne es von eineigen Firmen so, da werden die Kernfunktionen in ST (teilweise schon mit OOP) erstellt und in eine Lib gekapselt. In den höheren Ebenen wird weiterhin KOP oder FUP verwendet, damit die Instandhaltung und Inbetriebnahme leichter arbeiten kann (und das meine ich jetzt nicht negativ oder abwertend!!!).

Klar kann ich durch die Verwendung der OOP-Erweiterungen sehr unübersichtliche und verkopfte Programme erstellen. Hier muss man einfach aufpassen, dass man das ganze nicht zu weit treibt. Zum Beispiel halte ich auch nichts davon, dass man von einem FB mehr als 3-4 Vererbungsstufen erstellt. Aber auch mit der konvenstionellen Progammierung kann ich einen grausamen Programmierstil pflegen ;-).

Die Pflege des Programmcodes finde ich bei der Verwendung von OOP deutlich einfacher, da die Funktionen besser gekapselt werden können und man so schneller den Ort findet, wo etwas zu verbessern/berichtigen ist.

Es gibt aber auch deutliche Nachteile. So ist das Debuggen eines Programms welches mit OOP erstellt wurde (Verwendung von Interfaces, Ableitung, etc.) mit TwinCAT viel aufwändiger. Wenn man da mit dem Debugger schrittweise durch das Programm steppt, hat man rucki zucki mehr als 20 Fenster offen. Bei Interfaces kommt hinzu, dass man hier ja quasi immer mit Pointern arbeietet, was die Sache weiter erschweren kann (nach dem Motto: ist das jetzt überhaupt die richtige Instanz die ich da sehe :) ).

Um es kurz zu machen:
Ich finde die OOP-Erweiterung der IEC durchaus lobenswert. Es muss halt zum eigenen (und dem der Firma) Programmiersilt und der Aufgabenstellung passen. Man muss sich bei der Einarbeitung in das Thema selbst ein bisschen mehr Zeit zugestehen (auch wenn die wie immer Mangelware ist). Wenn man auf OOP umsteigen möchte muss man es nicht sofort allumfänglich machen, sondern man kann partiel anfangen. Und was auch sehr wichtig ist, man sollte alle Programmierer mitziehen (und unterstüzen, aller Anfang ist schwer)!


Einen Aspekt sollte man auch nicht vernachlässigen:
Bei der Verwendung von OOP geht es eigentlich immer um eine bessere Wiederverwendbarkeit des Codes. Dabei wird hüfig außer acht gelassen, dass man die anderen Gewerke (Mechanik, Elektrik, ...) außen vor lässt.
Dabei wäre es Sinnvoll mit diesen Gewerken zusammen die Einteilung in Objekte vorzunehmen und darauf zu achten, dass diese Objekte auf allen Ebenen/Gewerken die gleichen Schnittstellen verwenden/beschreiben!



bzgl. Konstruktoren und Destruktoren:
In TwinCAT gibt es die Möglichkeit an einem FB Methoden hinzuzufügen. Hier kann man bei dem Dialog statt dem Namen der Methode über das Dropdown Menü auch die vordefinierten "FB_init", "FB_reinit" und "FB_exit" -Methoden auswählen.

Alle drei Methoden werden vom System sowieso immer verwendet, auch wenn sie nicht explizit hinzugefügt wurden (dann sind sie halt leer). Durch hinzufügen dieser Methonden hat man dann nun die Möglichkeit selbst zu entscheiden, was den bei den jeweiligen Schritten unternommen werden soll. Z.B. lassen sich so Initialisierungsparameter an eine FB-Instanz übergeben, die für dem ersten wirklichen Aufruf des FBs schon gesetzt sind.

Beispiel:

ein FB hat eine lokale Variable die zu beginn einen Wert haben soll:

Code:
FUNCTIONBLOCK FB_Test
VAR[INDENT]nInitValue : int;
[/INDENT]
END_VAR

bei der FB_init-Methode kann man jetzt für diese Variable eine Input-Variable erstellen (hier "nInitParam") der den Wert auf die Variable im FB schreibt:
Code:
METHOD FB_init : BOOL
VAR_INPUT
    bInitRetains : BOOL; // if TRUE, the retain variables are initialized (warm start / cold start)
    bInCopyCode : BOOL;  // if TRUE, the instance afterwards gets moved into the copy code (online change)
    nInitParam : INT;
END_VAR

//---------
nInitValue := nInitParam;


Wenn man jetzt im Programm eine Instanz des FBs anlegt muss man auch die Initialisierungsparameter mit übernehmen:

Code:
PROGRAMM MAIN
VAR[INDENT]fbTest : FB_Test(nInitParam := 5);
[/INDENT]
END_VAR

Wichtig: die Klammer mit den Initialisierungsparameter kommt dierekt nach dem FB-Namen, es handelt sich hierbei nicht um die Angabe von klassichen Initialisierungswerten die ja mit dem Zuweisungsoperator :)=) erfolgen würden!


Nachteil hierbei ist zum einen, dass ich den Parameter immer angeben muss (sonst schimpft der Kompiler) und zum Andern ist eine Kapselung schwiriger wie ich im Post oben beschrieben habe. Außerdem ist ein Debuggen der Methoden so nicht möglich.

Hier gibt es als Alternative die Verwendung von dem Attribut "call_after_init" bei dem ich den Zeitpukt selbst definieren kann.

Anbei ein Beispiel, dass das ganz gut verdeutlicht (die Dateierweiterung muss von .zip einmal in .tszip (TwinCAT) umbenannt werden)...
Anhang anzeigen Init_Sample.zip

Viele Grüße
 
allgemein finde ich die OOP-Erweiterungen der IEC sehr hilfreich.
...
Hinzu kommen bei der OOP nette Funktionen, wie z.B. dass man mit Properties einen Schreibschutz herstellen kann und direkte Skalierungen und Bereichsüberwachungen realisieren kann.

schöner Beitrag (nur mal nebenbei -> Properties sind nicht Bestandteil der IEC 61131)
 
Hallo KGU,

vielen Dank für die Richtigstellung!
Ich könnte mir allerdings vorstellen, dass die Properties über kurz oder lang auch in die IEC einfließen werden (ich würde es zumindest begrüßen)...

Gruß
 
Zurück
Oben