OOP und extends

DerPaul

Level-2
Beiträge
45
Reaktionspunkte
0
Zuviel Werbung?
-> Hier kostenlos registrieren
Vererbter Code läuft nicht

Hallo,

ich versuche mir gerade diese ganze Geschichte mit OOP näher zu bringen...
Ich habe jetzt ein FB fertig programmiert und habe das jetzt ein neues FB erstellt und dieses erweitert, also so:

FUNCTION_BLOCK FB1


FUNCTION_BLOCK FB2 EXTENDS FB1

Ich habe jetzt mal in Debugger die Instanz (Also der Teil Code im FB2, der vom FB1 vererbt wurde) geöffnet, da ist folgender Code:

Code:
TON_TeachStart: TON;

[...]

TON_TeachStart(IN := bIn_Hoch AND bIn_Runter, PT := T#5S);

Jetzt läuft die Zeit aber gar nicht los, obwohl die beiden Bedingen auf TRUE sind -> der IN ist immer noch auf FALSE.
Skizze.jpg

Woran kann das liegen? Der Code im funktioniert im FB1 ohne Probleme!
 
Zuletzt bearbeitet:
Theorie: Du führst die Codestelle gar nicht aus.
Hast du den Codeteil in einer Funktion/Methode/irgendwas gekapselt? Falls ja: Du musst die Kapselung auch im Body des Kinds ausführen/aufrufen - Falls nein: Der "Body" deines (eines jeden) Funktionsbausteins wird nicht vererbt - nur seine Methoden.
 
Wenn ich jetzt aber einfach
SUPER^();
aufrufe, gibt der mir den Fehler

Code:
[FEHLER] VAR_IN_OUT 'bLichtStatus' muss in Aufruf von 'FB1' zugewiesen werden

ich habe aber doch schon die IN_OUT Variablen in der Instanzierung vom FB2 zugewiesen... Was habe ich da vergessen? Oder muss ich die IN_OUT noch mal an die Basisklasse oder den SUPER Aufruf weiterleiten?

Hast du den Codeteil in einer Funktion/Methode/irgendwas gekapselt? Falls ja: Du musst die Kapselung auch im Body des Kinds ausführen/aufrufen - Falls nein: Der "Body" deines (eines jeden) Funktionsbausteins wird nicht vererbt - nur seine Methoden.
Was meinst du mit gekapselt? Wie würde eine Kapselung aussehen?
 
Zuletzt bearbeitet:
Oder muss ich die IN_OUT noch mal an die Basisklasse oder den SUPER Aufruf weiterleiten?
Ja, leider. Also
Code:
SUPER^(bLichtStatus:=bLichtStatus);
Alternativ kannst Du den Code von FB1 in eine parameterlose Methode packen und diese Methode aus dem FB heraus aufrufen. Die Methode wird dann vererbt und steht auch im FB2 zur Verfügung. Den Methodenaufruf musst Du aber auch im FB2 noch mal hinschreiben.
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Oder muss ich die IN_OUT noch mal an die Basisklasse oder den SUPER Aufruf weiterleiten?
Ja, leider. Also
Code:
SUPER^(bLichtStatus:=bLichtStatus);
Alternativ kannst Du den Code von FB1 in eine parameterlose Methode packen und diese Methode aus dem FB heraus aufrufen. Die Methode wird dann vererbt und steht auch im FB2 zur Verfügung. Den Methodenaufruf musst Du aber auch im FB2 noch mal hinschreiben.

Wozu vererbt man eigentlich, wenn ein Teil des Bausteins nicht mit vererbt wird?
 
Wenn ich eine geerbte Methode oder Aktion überschreiben will, muss ich sie in dem erbenden FB erneut deklarieren. Wenn man den Hauptcode eines FBs auch vererben wollte, müsste man dafür einen ähnlichen Mechanismus einführen. Ich denke das wollte man vermeiden um das bisherige Look and feel für Leute beizubehalten, die die OOP nicht nutzen möchten. Aber ärgerlich ist das schon. Ich nutze die Alternative, die ich oben beschrieben habe, und vergesse bei abgeleiteten FBs oft den Aufruf der Methode. Das kostet dann jedes mal ein wenig Zeit beim Debuggen.
 
Oder muss ich die IN_OUT noch mal an die Basisklasse oder den SUPER Aufruf weiterleiten?
Ja, leider. Also
Code:
SUPER^(bLichtStatus:=bLichtStatus);
Alternativ kannst Du den Code von FB1 in eine parameterlose Methode packen und diese Methode aus dem FB heraus aufrufen. Die Methode wird dann vererbt und steht auch im FB2 zur Verfügung. Den Methodenaufruf musst Du aber auch im FB2 noch mal hinschreiben.

Verhält sich eine Methode denn genauso wie das FB? Ich hatte nämlich mal das Problem, dass ein TON innerhalb einer Methode nicht laufen wollte, hatte das ganze dann als FB umgeschrieben und schon lief es... Oder könnte es sein, dass ich vielleicht den falschen Speicherbereich genommen hatte?

Wie kann ich aber wenn ich eine Parameterlose Methode für das FB1 schreibe, die IN und Out Parameter übergeben? Oder schreibe ich dann auf die Eigenschaften im Aufruf der Methode?
 
Zuletzt bearbeitet:
Zuviel Werbung?
-> Hier kostenlos registrieren
Nein, Du schreibst eine private Methode, die im FB aufgerufen wird. Dein Programm ruft nach wie vor den FB auf und übergibt ihm dabei die VAR_IN_OUT und VAR_INPUT wie gehabt. Aus dem FB aufgerufen hat die Methode Zugriff auf alle Variablen des FBs, auch auf die VAR_IN_OUT. Wenn die Methode auf VAR_IN_OUT des FBs zugreift, gibt es eine Warnmeldung in der Art "Zugriff auf VAR_IN_OUT InOutVar von externem Kontext Methodenname", die aber nur von Belang wäre, wenn die Methode von aussen aufgerufen würde. Dies verhinderst Du, indem Du die Methode als "PROTECTED" deklarierst. Das könnte etwa so aussehen:
Code:
// Deklarationsteil des FBs
FUNCTION_BLOCK fblMyFB
VAR_IN_OUT
   refVar:BOOL;
END_VAR
VAR_INPUT
   inpVar:BOOL;
END_VAR

// Code des FBs
prvFBCall();   // Aufruf der Methode

// Deklarationsteil der Methode
METHOD PROTECTED prvFBCall
VAR_INPUT
END_VAR

// Code der Methode
refVar:=inpVar;   // oder wer weiss was
 
Zuletzt bearbeitet:
Was meinst du mit gekapselt? Wie würde eine Kapselung aussehen?
Der objektorientierte Ansatz der Kapselung wäre eine Methode (es gibt bei Codesys/Twincat3 noch irgendetwas, was darunter angesiedelt ist, aber ich habe gerade kein System um nach dem genauen Namen zu schauen - funktioniert wie eine Methode ist aber nur aus dem FB direkt aufzurufen und kann nicht public gemacht werden. Hat keine Rückgabe und keine Übergabewerte, im Prinzip nur eine Abkürzung für Code, sowas wie ein Makro)
Ich kapsle normalerweise alle Logik des Programms in so so eine Methode (mal egal welche) und rufe dann von außen her nicht nur "FBcall();" (mit oder ohne I/O) auf sondern deklariere bspw. eine "run()"-Methode mit Übergabewerten und allem drum und dran. Jetzt wird von außen dann eben "FBcall.run()" aufgerufen und die run-Methode kümmert sich um die ganzen Berechnungen.
Hat den Vorteil, dass die Vererbung komplett ausgenutzt wird (wenn ich etwas ändern möchte, überschreibe ich die run-Methode im Kind (und kann dort auch mit SUPER^.run() wieder auf die Elterklasse zugreifen) - hat aber auch den Nachteil dass die Programmierung in allem außer ST komplexer wird, weil nicht einfach nur der FB irgendwo reingezogen wird, sondern immer stattdessen seine run-Methode aufgerufen wird.

WICHTIG für dein TON: der TON-Baustein darf zwar in der Methode aufgerufen werden, deklariert werden muss er aber im FB! Sonst hast du dein geschildertes Problem, dass es mit einer Methode "nicht funktioniert" (Grund: Wenn es in der Methode deklariert wird, wird es bei jedem Aufruf erst erzeugt, dann verarbeitet und beim Verlassen wieder zerstört - deswegen ist keine Historie möglich)
 
Zuletzt bearbeitet:
Der objektorientierte Ansatz der Kapselung wäre eine Methode (es gibt bei Codesys/Twincat3 noch irgendetwas, was darunter angesiedelt ist, aber ich habe gerade kein System um nach dem genauen Namen zu schauen - funktioniert wie eine Methode ist aber nur aus dem FB direkt aufzurufen und kann nicht public gemacht werden. Hat keine Rückgabe und keine Übergabewerte, im Prinzip nur eine Abkürzung für Code, sowas wie ein Makro)
Ja, gibt es, nennt sich Aktion.
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Hallo,

vielleicht kann ich zur Klärung beitragen:
Wenn ein FB2 von einem anderen FB1 abgeleitet wird, dann erbt der FB2 alle Methoden und alle Variablen des FB1.

Man kann aber eine eine Methode auch überschreiben, FB2 bietet dann für eine bestimmte Methode eine andere Implementierung an als sein Basisklasse.
Oft greift man dabei auf die Basisimplementierung zurück indem man diese mit einer speziellen Syntax aufruft:
SUPER^.Methode();

Bis hierhin ist das ziemlich standardmässige Objektorientierung wie man sie von anderen Sprachen her auch kennt.
Wir mussten uns nun überlegen wie der Rumpf des FB in dieses Schema einzuordnen ist und wir haben uns für folgende Lösung entschieden.
Der Rumpf des FB wird wie eine Implizite Methode behandelt, die immer vorhanden ist. (Intern gibt es diese Methode sogar und die heisst __MAIN).

Dadurch dass die immer vorhanden ist, überschreibt der FB2 automatisch die Implementierung des Basis-FB1 und muss den selber aufrufen, wenn er auf die
Implementierung zurückgreifen möchte.

Der Vorteil ist, dass man alle Möglichkeiten hat, wie man mit der Basisimplementierung umgehen will: man kann sie ersetzen, man kann eine Vorverarbeitung einbauen,
man kann eine Nachbearbeitung einbauen, man kann die Parametrierung ändern. Würde der Compiler den Aufruf implizit einbauen, dann wäre man doch sehr
eingeschränkt in den Möglichkeiten.
 
Vielen Dank euch allen!
Leider sind die Tutorials und Beispiele im Internet zu dürftig, um sie als Neueinsteiger zu verstehen!

Aber dank euch habe ich das wichtigste erstmal verstanden! Das war sehr hilfreich und sehr Vorbildhaft!!!

Und dank Werner29 haben wir hier jetzt auch die Erklärung, warum das so Implementiert wurde - es wurde sich ja schon an mehreren Stellen hier im Forum darüber aufgeregt - so macht das aber Sinn!

Und btw. mein Programm läuft jetzt!!!!! Danke!
 
Zurück
Oben