OOP - Debugging- / Inbetriebnahmestrategie

Zuviel Werbung?
-> Hier kostenlos registrieren
Die Klasse heiss Auto.
Ein Auto besteht aus Methoden.(Bremsen,Beschleunigungen,...
Ein Auto hat Eigenschaften.(Farbe,Anzahl der Räder,PS-Zahl,..
Eine Ausprägung der Klasse ware Auto1 vom Typ Auto.
 
Das sehe ich etwas anderst.Erst wenn sich hinter einem Objekt viele Eigenschaften und Funktionen verbergen lohnt sich dieser Aufwand.
Als Bsp. das Autorennen.Dort fahren unzählige gleiche Objekte gegeneinander mit vielen Eigenschaften und Funktionen.
Darum heisst es ja objektorintiert.Diese Objekte sollen, wie in der realen Welt ein komplexes Gebilde mit vielen Ausprägungen nachbilden.
Eine Maschine hat ja meist eben auch viele Zylinder und nicht nur einen. Von daher lohnt es sich schon mehrere Instanzen eines Zylinder-Objekts einzusetzen. Ob man das Objektorientiert machen muss ist eine andere Frage.

Interface : wurde von @mgl erklärt - das macht für mich aber in der SPS-Welt nicht wirklich Sinn - zumindestens wüßte ich jetzt gerade nicht wofür ...

Man könnte statt einem Interface Strukturen verwenden. Ich sehe da aber den Vorteil / Nachteil an der Verschachtelungstiefe.

Beispiel Zylinder:

Ich benötige für den Betrieb der Maschine in Hand und Automatik Betrieb unterschiedlich viele Status-Informationen. Beim Automatik brauche ich nur die Information ob der Zylinder in Position ist. Im Hand möchte ich aber zusätzlich auch zurück melden wie lange es gedauert hat bis die Position erreicht wurde, ein Fehler anliegt usw. Bei Interfaces kann ich Schnittmengen direkt verwenden, so dass ich Properties nicht doppelt schreiben bzw. Variablen doppelt zuweisen muss.

Code:
INTERFACE I_ZylinderAuto
--------------------------------

// Properties
bInGrundstellung
bInArbeitsstellung

=========================================

INTERFACE I_ZylinderHand

// Properties
bInGrundstellung
bInArbeitsstellung
tFahrzeitZuGrundstellung
tFahrzeitZuArbeitsstellung
....

=========================================

FUNCTION_BLOCK FB_Zylinder IMPLEMENTS I_ZylinderHand, I_ZylinderAuto

=========================================

fbHand(i_iZylinder1 := fbZylinderHand, ...);
fbAutoSchrittkette(i_iZylinder1 := fbZylinderHand, ... );

Bei Strukturen müsste ich sowohl UDT / STRUCT - ST_ZylinderAuto, als auch ST_ZylinderHand die beiden Variablen oder in ST_ZylinderHand den Datentyp ST_ZylinderAuto haben
 
Interface : wurde von @mgl erklärt - das macht für mich aber in der SPS-Welt nicht wirklich Sinn - zumindestens wüßte ich jetzt gerade nicht wofür ...
War für mich auch lange unverständlich. Einfach gesagt ist es ein Pointer/eine Referenz auf einen bestimmten Teil des FBs, der das Interface implementiert. Über diese Referenz kann dann nur auf die Inhalte des Interfaces zugegriffen werden.

Z.b. Du hast einen Zylinder FB. Es gibt binäre, analoge, digitale (z.b. IO-Link) Endschalter. Das wäre jetzt umständlich für jede Kombi ein Baustein zu programmieren. Deshalb könntest du 2 Properties definieren,
Code:
Grundstellung : I_BinärIn, Arbeitsstellung : I_BinärIn
. Dabei ist
Code:
I_BinärIn
ein Interface. Dieses hat im einfachsten Fall z.b. ein Property:
Code:
(GET) Input : BOOL
, kann aber auch zum Beispiel ein Property:
Code:
(set) DelayOn : TIME, (set) DelayOff : TIME
haben. Jetzt schreibst du Bausteine für einen Binären Eingang, einen Analogen Eingang mit Schwellwert und einen IO-Link Baustein. Diese Bausteine implementieren das Interface
Code:
I_Binär
Sie haben also mindestens die Properties und Methoden des Interfaces, können aber durchaus auch andere haben. Beim analogen Baustein wäre dies z.b. ein Schwellwert mit Toleranz.


Code:
VAR
    BinSwitch        : FB_Binär;
    AnalogSwitch    : FB_Analog;
    Cyl01            : FB_Zylinder := (Arbeitsstellung := BinSwitch, Grundstellung := AnalogSwitch);
END_VAR

Programm
    // Initialisierung wurde direkt in der Variablendeklaration gemacht, kann aber auch zur Laufzeit gemacht und geändert werden.
    Cyl01.Arbeitsstellung := BinSwitch;
    Cyl01.Grundstellung := AnalogSwitch


    // Optionaler Zyklischer Aufruf der Inputs (je nach implementierung nicht nötig)
    BinSwitch();
    AnalogSwitch();
   
    //Zyklischer Aufruf des Zylinders
    Cyl01();
   
   
End Programm


Ja klar kann man auch einfach ein Bool auf den Baustein schreiben, da ist es ja auch egal woher (Digital, Binär, Analog) der kommt. Aber man könnte auch so weit gehen, dass die Inputs nicht mehr selber aufgerufen werden müssen, sondern dass die Konfig und der Aufruf direkt über den Zylinder FB gemacht werden.

Ich hoffe es ist nun verständlicher, wir setzen dies häufig in unseren Bibliotheken um!


edit: da war jemand mit einen ähnlichen Beispiel schneller...
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Ein interface ist erstmal nichts weiter wie eine Vereinbarung: Jede Klasse, die das Interface implementiert, muss die im Interface deklarierten Methoden auch haben. Die im Interface deklarierten Methoden werden erst in der entsprechenden Klasse ausprogrammiert.
Über ein Interface lässt sich das Prinzip der polymorphie umsetzten, wenn zum Beispiel das Prinzip der Mehrfachvererbung nicht unterstützt wird.
 
Zuletzt bearbeitet:
Ein Beispiel zum Interface und polymorphie:

Du hast eine pneumatische Hubstation, mit den Methoden Auf() & Ab(). Jetzt soll diese durch eine andere, mit elektrischen Antrieb geändert werden. Eine Vererbung des FB's würde keinen Sinn ergeben, da die Ansteuerung der Ellekrischen zur pneumatischen zu unterschiedlich wäre. Also definierst du ein interfache, das die beide Methoden Auf() und Ab() enthält. Beide Hubstationsklassen nutzen das Interface. Wenn du in deinem Programm dann über den Pointer /Referenz auf die Funktionen zugreifst, kannst du jetzt beide eins zu eins austauschen.
 
Da geb ich dir recht. Man muss ja nicht alles was möglich ist nutzen aber das observer pattern (link im obigen post) ist bei mir immer in der Basis Klasse drinnen.
 
Ein interface ist erstmal nichts weiter wie eine Vereinbarung: Jede Klasse, die das Interface implementiert, muss die im Interface deklarierten Methoden auch haben. Die im Interface deklarierten Methoden werden erst in der entsprechenden Klasse ausprogrammiert.
Über ein Interface lässt sich das Prinzip der polymorphie umsetzten, wenn zum Beispiel das Prinzip der Mehrfachvererbung nicht unterstützt wird.

Ich hätte hierzu noch eine Ergänzung bzw. weitere Beispiele für ein besseres Verständnis

Der Einsatz des Vererbungsprinzips und das Prinzip der Polymorphie ist auch abhängig von der jeweils eingesetzten Entwicklungsumgebung.
Bei TwinCAT ist es, im Gegensatz zum nativen CODESYS, möglich das Vererbungsprinzip auf beliebig vielen Ebenen hinweg durchzuführe -> Stichwort "Mehrfachvererbung". Einfaches Beispiel:

Mehrfachvererbung.PNG

Der Errorhandler vererbt seine Methoden zum Erfassen von Fehlerzuständen an die Klasse (Baustein) "Maschine".
Im Baustein "Maschine" können ganz normal die grundlegenden Funktionen der Maschine implementiert werden.
Falls jetzt der Fall auftritt, dass sich die Basismaschine in 2 Ausbaustufen aufteilt, können alle Funktionen auf "Maschine A" und "Maschine B" weiter vererbt werden (Vorteil: Innerhalb von "Maschine A" und "Maschine B" müssen diese Funktionalitäten (Methoden) nicht mehr implementiert werden oder können bei Bedarf um weitere Funktionen erweitert werden können -> Vorteil: Zeitersparnis und keinen "doppelten Code").
Das Gleiche gilt für die nächste Ausbaustufe von Kundenwünschen für "Kunde A" und "Kunde B". Durch die Vererbung hat der Baustein "Kunde A" ebenfalls die Möglichkeit auf die Methoden des Errorhandlers zuzugreifen und diese nutzen zu können.
Die Gefahr von Mehrfachvererbung ist jedoch das Erstellen von sogenannten "Superklassen", die intern eine starke Abhängigkeit besitzen, wodurch sich Änderungen oder Anpassungen sehr aufwendig sind, da es jeden Baustein in der Vererbungshierarchie betrifft.


Im nativen CODESYS hingegen ist das Prinzip der Mehrfachvererbung nicht erlaubt. CODESYS erlaubt nur eine einzige Vererbungsstufe. Das oben dargestellte Konstrukt lässt sich jedoch durch das Prinzip der Polymorphie nachstellen. Siehe nachfolgendes Beispiel

Polymorphie.PNG

Im aktuellen Fall besteht nur eine Vererbung zw. "Errorhandler" und "Maschine".
Die danach folgenden Maschinenarten implementieren, im Gegensatz zu obigem Beispiel, eine Schnittstelle und werden innerhalb des Bausteins "Maschine" instanziiert. Der Vorteil (oder auch Nachteil, da die Schnittstelle sehr gut im Voraus überlegt werden muss) ist, dass jede Maschine sich über die Schnittstellenmethode "Control()" ansteuern lässt.
Der Baustein "Maschine" kennt somit nur die Schnittstelle, was dahinter steckt (Maschinentyp) ist dem Baustein in dem Fall egal, er hat nur den Auftrag über die Schnittstellenmethode die Maschine anzusteuern.
Vorteil: Maschinentypen lassen sich relativ einfach austauschen, ändern etc., da alle die gleiche Schnittstelle zum Ansteuern besitzen
Nachteil: Projekte können hierdurch sehr komplex werden

Ein weiteres typisches Beispiel für Polymorphie ist z.B. das Handling von verschiedenen Dateitypen

FileParse.PNG

Die Klasse "Fileparser" hat den einfachen Auftrag, Dateien zu zerlegen und die jeweiligen Informationen zurück zu erhalten. Im aktuellen Fall, kennt sie nur die Schnittstelle "I_FileParse" und deren Methode "parse()", was hinter der Schnittstelle folgt ist ihr nicht bekannt, sie erwartet nur die Abarbeitung der Methode und entsprechende Rückgabewerte.
Im Gegenzug muss jede "Dateityp-Klasse" die Schnittstelle "I_FileParse" mit der Schnittstellenmethode. "parse()" implementieren und diese ausprogrammieren.
Somit lässt sich im Baustein "Fileparser" die Steuerung des Parsing-Vorgang ziemlich einfach umsetzen. Ein weitere Vorteil ist, dass zukünftig weitere Dateitypklassen einfach hinzugefügt werden können.

Mit freundlichen Grüßen

Biiebs
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Vielleicht blicke ich es nicht.
Aber ich halte das alles für reichlich kompliziert.
Wie soll das einer Vorort warten und Fehler suchen?

Also wenn ich eins im Automatsierungsbereich gelernt habe, dann so einfach wie möglich...
 
Letztendlich ist OOP auch nur ein Werkzeug. Man kann es nutzen oder auch nicht. Man kann damit guten Code schreiben, oder furchtbar schlechten. Wie mit allen anderen Werkzeugen auch. Und zum Glück, führt ja bekanntlich mehr als ein Weg nach Rom ;)
 
OOP macht manchmal schon sinn zb kLogger oder Datenhanding, Man kann ja auch mal die Basic Sachen wie Methoden zum Ausführen manchen Codes verwenden.
Alles OOP machen würde ich nicht da schießt man sich dann irgendwann in den Fuß. Vor allem alles mit Designpatterns machen
OOP hat in der Informatik auch schon seinen Hype hinter sich. Da gibt es auch schon wieder andere Entwicklungen
 
funktionale Programmierung, Trennen von Datenhaltung und dehren Manipulation, In C# Records, Linq Lambda Funktionen etc. C# ist ja eine der ursprünglich OOP Sprachen.
In diese Richtung gehen viele Programmiersprachen. In Javascript hat man etwas Objektorientierung die letzten Jahre eingebracht ist aber eher eine funktionale Sprache. Aber immer das rechte Werkzeug für sein Problem verwenden.
 
@Biiebs Für mein Verständnis ist dein Twincat - Beispiel allerdings keine Mehrfachvererbung, da keine Klasse gleichzeitig von Basisklassen erbt.
Jaein, du hast recht hier fehlt die Implementierung um dies zu verdeutlichen - Ich habe zurzeit aber leider kein TwinCAT zur Hand um dies als Beispiel zu implementieren.

Da geb ich dir recht. Man muss ja nicht alles was möglich ist nutzen aber das observer pattern (link im obigen post) ist bei mir immer in der Basis Klasse drinnen.
Hast du hier einen Ansatz, Beispiel wofür du das alles einsetzt? Wird beim Debuggen doch eigentlich sehr unübersichtlich oder?
 
Zurück
Oben