Transcript for:
Vererbung in der objektorientierten Programmierung

In diesem Video geht es um eine weitere Säule der objektorientierten Programmierung, nämlich um das Thema Vererbung. Vererbung ist auch für euch als SPS-Programmierer ein ganz wichtiges Konzept, weil ihr damit nämlich auf sehr einfache Weise neue Funktionsbausteine entwickeln könnt, die die Funktionalität von schon bestehenden FBs erweitern. Ich werde das Ganze hier wieder an einem Beispiel erläutern, und zwar nämlich an einer Einzelsteuerfunktion zur Ansteuerung eines einfachen Schrittmotors.

Der Ausgangspunkt dabei ist ein FB, den ich IDF-to-Dirvar-Speed genannt habe, mit dem ich einen Schrittmotor aktivieren und deaktivieren kann. Und wenn der Antrieb aktiviert ist, kann ich eine Drehrichtung und eine Geschwindigkeit vorgeben. So, das ist auch schon alles, was der FB macht.

In einem ersten Schritt werde ich diesen Funktionsbaustein erweitern, indem ich eine weitere Funktionalität hinzufüge. Nämlich eine Möglichkeit, den laufenden Antrieb für eine gewisse Zeit zu pausieren. Dieser neue Funktionsbaustein enthält also durch die Vererbungsbeziehung zur Basisklasse deren Funktionen und auch die Eigenschaften und für die Erweiterung muss ich dann also nur die Funktion zum Pausieren ergänzen.

Mit einer Erweiterung durch Vererbung könnt ihr aber einem FB nicht nur neue Methoden und Eigenschaften hinzufügen, sondern ihr könnt auch Funktionalitäten der Basisklasse verändern. Das zeige ich dann in einer zweiten Erweiterung bei dem FB-IDF-To-Deer-Varspeed-Ramp. Und hier werde ich die Methoden zur Ansteuerung des Antriebs so verändern, dass die Zielgeschwindigkeit rammförmig angefahren wird. So, diese beiden Beispiele sollen euch also den Nutzen der Vererbung veranschaulichen. Der Nutzen liegt nämlich vor allem darin, dass ihr euch bei einer Erweiterung wirklich nur um die neuen Funktionalitäten kümmern müsst und die alten Funktionalitäten der Basisklasse alle erhalten bleiben.

Bevor ich jetzt aber in die Programmierung springe, will ich noch ein paar grundsätzliche Infos zur Vererbung in ST loswerden. Wie ihr das ja an diesem Klassendiagramm hier seht, wird durch Vererbung ein neuer Funktionsbaustein entwickelt, der typischerweise neue spezielle Funktionalitäten enthält, während die Basisklasse allgemeiner bezüglich der möglichen Verwendung gehalten ist. Deswegen nennt man die Vererbungsbeziehung auch eine Spezialisierung.

In ST können von einer Basisklasse durch Vererbung beliebig viele neue Funktionsbausteine abgeleitet werden. Und ebenfalls ist die... Tiefe der Ableitung nicht beschränkt.

Was aber in ST nicht unterstützt wird, ist die Mehrfachvererbung. Das bedeutet also, dass eine Erweiterung immer nur aus einer Basisklasse entsteht. Bei einer Vererbung kann es also nicht mehrere Basisklassen geben. So, der Ausgangspunkt für die Erklärung zur Vererbung ist der FB-IDF-To-Dear-Var-Speed, den ich hier schon geöffnet habe. Dieser Baustein ist die Basisklasse für die erste Vererbungsbeziehung.

Und den FB habe ich schon in vorherigen Videos in dieser Reihe benutzt. Für dieses Beispiel hier habe ich aber die Implementierung etwas geändert. Der FB hat ein Eingangssignal B-Log.

Wenn dieses Boolche-Signal True ist, wird der Antrieb deaktiviert. Die Ausgangssignale des FBs sind ein Freigabesignal B-Enable und die Vorgabe der Geschwindigkeit N-Val. Und dann gibt es noch eine interne Variable N-Val-Max, in der die maximal einstellbare Geschwindigkeit gespeichert wird. Damit dieser Wert von außen einstellbar ist, gibt es eine Eigenschaft pValmax mit diesen Get-und Set-Methoden. Der FB bietet zum Ausschalten und Deaktivieren des Antriebs die Methode MOff an.

Eine Geschwindigkeit und Drehrichtung des Antriebs wird mit der Methode MON eingestellt, die zwei Übergabeparameter erwartet. EDir ist dabei ein Aufzählungsdatentyp, der nur die Werte FWD oder BWD für vorwärts bzw. rückwärts annehmen kann und also die Drehrichtung beschreibt.

Und Endspeed ist die gewünschte Geschwindigkeit. Wenn also der Antrieb nicht durch das Lock-Signal gesperrt ist, wird das Freigabesignal gesetzt und entsprechend der Richtungswahl die Geschwindigkeit eingestellt. Und das passiert in den Methoden M-Forward oder M-Backward. So und wenn ihr mal in diese Methoden schaut, seht ihr, dass die als Protected deklariert sind, also einen speziellen Zugriffsmodifizierer haben.

Nochmal zur Erinnerung, einen Zugriffsmodifizierer könnt ihr beim Anlegen einer Methode in diesem Dropdown-Feld festlegen. Darauf, was die Modifizierer Public und Private bewirken, bin ich schon im Video zu den Methoden eingegangen. Der Modifizierer Protected scheint nun zunächst einmal dieselbe Auswirkung zu haben wie Private, weil nämlich die als Protected deklarierten Methoden nach außen hin nicht sichtbar sind.

Das seht ihr hier in diesem Main-Programm, in dem ich eine Instanz des FBs deklariert habe. So und wenn ich im Anweisungsteil den Punktoperator auf dieser Instanz anwende, seht ihr, dass mir hier in dem Kontextmenü nur die Methoden mOn und mOff und die Eigenschaft pValMax angeboten werden, also nur die Methoden und Eigenschaften, die als Public deklariert sind. Die als Protected deklarierten Methoden mForward und mBackward tauchen hier aber nicht auf, ebenso wie die Private Methode mLimitVal und die sind also auch nach außen hin nicht sichtbar. Ihr werdet aber gleich bei der Realisierung der Vererbung sehen, dass diese Protected-Methoden im abgeleiteten FB sichtbar sind, sich also wie Public-Methoden verhalten, während Private eben dafür sorgt, dass diese Methode auch der abgeleitete FB nicht aufrufen kann.

Aber dazu gehe ich gleich nochmal ins Detail. Um jetzt die Vorstellung der Basisklasse zu vervollständigen, zeige ich euch hier noch die Methode MLimitVal. Diese Methode ist, wie gerade schon erwähnt, Private und wird in den Methoden MForward und MBackward verwendet.

um die übergebende Geschwindigkeit auf den maximalen Wert nValmax zu begrenzen. Also dieser FB realisiert eine simple Einzelsteuerfunktion zur Ansteuerung eines Schrittmotors und in diesem Main-Programm hier seht ihr jetzt die Anwendung des FBs. Zuerst wird der FB aufgerufen und das Signal B-Log Main als Eingangssignal zum Sperren übergeben. Dann kann ich hier über die Eigenschaft eine Maximalgeschwindigkeit setzen und über die Flecks B-On und B-Off rufe ich die Methoden M-On bzw.

M-Off auf. So, und hier kommt einmal die praktische Anwendung. Ich setze hier die Maximalgeschwindigkeit zunächst einmal auf 5000. Und dann starte ich den Antrieb einmal vorwärts mit der Geschwindigkeit von 3000. Und dann lasse ich den Antrieb rückwärts laufen mit einer Geschwindigkeit von 6000. Und dann seht ihr, dass der angewendete Wert auf die Maximalgeschwindigkeit eben begrenzt wird. So und wenn ich die Variable BLOCK MAIN setze, wird der Antrieb sofort gesperrt. Okay, dieser FB, den ich euch hier gezeigt habe, liefert also so etwas wie die Basisfunktionalität zur Ansteuerung des Schrittmotors.

Stellt euch jetzt aber vor, dass ihr mehr Anforderungen an die Motoransteuerung habt, ihr also zusätzliche Funktionalitäten braucht. Zum Beispiel eine Methode, mit der ihr den Antrieb für eine gewisse Zeit pausieren könnt. Wenn ihr also diese Pausenmethode aufruft, Soll der laufende Antrieb angehalten werden und nach Ablauf einer vorgegebenen Zeit soll der Motor automatisch weiterlaufen.

Dabei wäre es natürlich klasse, wenn ihr auf die Basisfunktionalitäten des eben vorgestellten FBs zurückgreifen könntet und nicht den gesamten Code kopieren müsstet. Und genau das erreicht ihr mit der Vererbung. Und wie genau ihr die Vererbung anwendet, zeige ich euch am folgenden Beispiel. Zunächst einmal lege ich, wie gewohnt, einen neuen FB an.

Der Name soll jetzt EDF-to-Dir-Var-Speed-Pause sein. Und um jetzt anzugeben, dass dieser neue FB einen bestehenden Funktionsbaustein erweitert, klicke ich hier die Option Erweitert an und muss dann hier den FB angeben, der die Basisklasse sein soll. Also eben jener FB, dessen Funktionalitäten übernommen werden sollen. Und das soll jetzt der FB EDF-to-Dir-Var-Speed sein, den ich gerade vorgestellt habe.

Dann bestätige ich das Ganze mit Öffnen und lege damit also den neuen Funktionsbaustein an. Dann öffnet sich die Deklaration des Bausteins und die Auswirkung der Option Erweitert, die ich gerade angewählt habe, ist dieses Schlüsselwort Extents. Und dahinter wird der Name der Basisklasse angegeben.

So, dieser neu erzeugte FB ist also scheinbar leer. Er hat keine Eingänge, keine Ausgänge, keine internen Variablen und keine Methoden oder Eigenschaften. Aber durch die Angabe dieser Erweiterung verfügt der neue FB schon über die gesamte Funktionalität der Basisklasse.

Dass das wirklich so ist, kann ich einfach demonstrieren, indem ich hier im Main-Programm den Datentyp der Einzelsteuerfunktion ändere in idf2dirvarspeed-pause. Durch diese Änderung gibt es im Anweisungsteil schon mal keine Fehler. Und wenn ich jetzt noch den Punktoperator auf die Instanz der Einzelsteuerfunktion anwende, seht ihr auch, dass dieser neue FB dieselben Ein-und Ausgangssignale und Methoden und Eigenschaften hat, wie eben die Basisklasse EDF-to-Dir-Var-Speed.

Innerhalb des neuen FBs kann ich aber auch auf die Methoden zugreifen, die in der Basisklasse als Protected deklariert sind. Um das zu demonstrieren, gehe ich in den noch leeren Anweisungsteil und um zu zeigen, welche Methoden und Eigenschaften hier verfügbar sind, rufe ich eine Instanz der Klasse EDF-to-Dir-Var-Speed-Pause auf. So und das mache ich über den This-Pointer.

Das Schlüsselwort This ist also ein Pointer auf eine Instanz des neuen FBs und diesen Pointer kann ich mit diesem Dach dereferenzieren und dann kann ich den Punktoperator anwenden. Dann seht ihr hier in diesem Dropdown-Menü wieder die in diesem FB sichtbaren Methoden und Eigenschaften und zudem natürlich auch die internen Variablen sowie die Ein-und Ausgänge. Jetzt seht ihr hier aber, dass innerhalb des FBs eben auch die Methoden zur Verfügung stehen, die in der Basisklasse als Public oder Protected deklariert wurden, also zum Beispiel MForward und MBackward.

Die Methode MLimitVal ist in der Basisklasse als Private deklariert und daher hier nicht sichtbar. Man sagt also, Private Methoden werden nicht vererbt. Wenn ihr den Zugriffsspezifizierer von Methoden ändern wollt, Also zum Beispiel, weil ihr im Laufe der Programmierung merkt, dass auf eine in der Basisklasse ursprünglich als private deklarierte Methode in der Vererbung doch noch zugegriffen werden muss, müsst ihr einfach das Schlüsselwort in der entsprechenden Methodendeklaration ändern. Das zeige ich euch jetzt hier einmal am Beispiel der Methode mlmdVal.

Hier ändere ich also das Schlüsselwort private in protected und das bewirkt, dass ich in dem abgeleiteten FB jetzt auch direkt auf die Methode mLimitVal zugreifen kann, was vorher eben nicht möglich war. So, bisher habe ich in dem abgeleiteten FB ja noch gar nichts verändert. Eine Vererbung macht natürlich nur Sinn, wenn in der Erweiterung tatsächlich Änderungen oder Ergänzungen vorgenommen werden. Ich will ja jetzt den FB um eine Pausenfunktionalität erweitern und dazu brauche ich erst einmal ein paar interne Variablen. Zunächst einmal ein Pulsgeber.

der ein High-Signal mit der Dauer der gewünschten Pausenzeit erzeugen soll. tPause ist die Zeit genau dieser Pausendauer. Und dann nutze ich noch eine Flankenerkennung für die fallende Flanke, dass ich auf das Ausgangssignal des Timer-Baustands lege, um damit ein Ereignis genau nach Ablauf der Pausendauer zu erzeugen.

So, und um den Antrieb nach der Pause mit derselben Geschwindigkeit weiterlaufen zu lassen, muss ich diesen Wert ja zwischenspeichern und dafür verwende ich die Variable EnvelTemp. Um die Dauer der Pause einstellen zu können, lege ich zunächst einmal eine Eigenschaft pPause mit dem Rückgabetyp time an. In dem get-Zugriff wird der Wert von tPause einfach zurückgegeben.

Und in dem set-Zugriff mache ich eine kleine Einschränkung, einfach um diese Eigenschaften Sinn zu geben, so dass der Zugriff nämlich nicht völlig uneingeschränkt erfolgt. Ich beschränke also den Wert der Pause auf einen Bereich zwischen 0 und 3 Sekunden. So, damit kann also die Pausendauer schon mal eingestellt werden.

Und jetzt brauche ich noch eine Methode, mit der das kurzzeitige Stoppen des Antriebs aufgerufen wird. Und das ist die Methode M-Pause. Die Implementierung der Methode ist ziemlich simpel. Hier wird einfach der Pulsgeber gestartet, die aktuell eingestellte Geschwindigkeit wird zwischengespeichert in N-Val-Temp und das Ausgangssignal der Geschwindigkeit wird auf 0 gesetzt. Das heißt, der Antrieb bleibt dann also stehen.

Im Anweisungsteil des FBs rufe ich dann den Pulsgeber auf. und übergebe hier die eingestellte Pausendauer. Dann lege ich noch den Ausgang des Timers auf die Flankenerkennung und wenn also diese Flanke detektiert wird, das heißt die Dauer der Pause abgelaufen ist, dann soll die ursprünglich eingestellte Geschwindigkeit wieder ausgegeben werden und der In-Eingang des Timers zurückgesetzt werden, damit er später eben erneut gestartet werden kann. So, damit ist die Pausenfunktionalität umgesetzt. Und diesen Entwicklungsstand der erweiterten Einzelsteuerfunktion möchte ich jetzt einmal praktisch erproben.

Und dazu nehme ich im Main-Programm folgende Anpassungen vor. Erstens muss natürlich sichergestellt sein, dass der Typ meiner Einzelsteuerfunktion der gerade neu entwickelte FB ist. Das ist also der Typ IDF toDearVarSpeed-Pause. Zweitens setze ich dann die Dauer der Pause auf 2 Sekunden.

Und drittens deklariere ich dann noch das Signal B-Pause. mit dem ich viertens im Anweisungsteil die Methode Mpause aufrufe. So und hier kommt wieder die Demo am realen System.

Ihr seht also, dass ich den Antrieb wie zuvor schon starten kann, zum Beispiel in Vorwärtsrichtung mit einer Geschwindigkeit von 3000. Und wenn ich dann die Pausenmethode aufrufe, bleibt der Antrieb für die eingestellte Zeit von 2 Sekunden stehen. Das gleiche Beispiel nochmal in Rückwärtsrichtung mit einer anderen Geschwindigkeit, damit ihr seht, dass die zuvor eingestellte Geschwindigkeit auch übernommen wird. So und wenn der Antrieb steht und das Locksignal aktiv ist, dann kann ich auch den Antrieb nicht mehr starten.

Wenn der Antrieb aber läuft und das Locksignal wird dann erst aktiviert, seht ihr, dass der Antrieb trotzdem weiterläuft. Und das entspricht natürlich nicht dem Verhalten, das die Basisklasse hat. Hier liegt also noch ein Fehler vor.

Der Fehler meiner Implementierung ist, dass der Code im Anweisungsteil der Basisklasse nicht mehr aufgerufen wird. Es erfolgt also nicht mehr die Abfrage, ob das Log-Signal gesetzt ist und wenn das der Fall ist, soll eben der Antrieb deaktiviert und die Geschwindigkeit auf 0 gesetzt werden. Ich muss also diesen Programmcode aus meinem erweiterten FB heraus noch aufrufen.

Und das mache ich so. Das Schlüsselwort super ist ein Pointer auf eine Instanz der Basisklasse. Und mit diesem Dach...

dereferenziere ich diesen Pointer wieder. SuperDach ist also wie ein Aufruf eines Objekts der Basisklasse und über die Klammer auf und zu rufe ich dann eben den Anweisungsteil der Basisklasse auf. Also genau den Code, den ich brauche um den Antrieb über das Log-Signal auch im laufenden Betrieb zu sperren. Das ist auch schon alles was ich ergänzen musste um den Anweisungsteil der Basisklasse aufzurufen.

Und das Ergebnis seht ihr jetzt hier in dieser Demo. Jetzt kann ich den Antrieb also pausieren und auch im laufenden Betrieb durch das Setzen des Log-Signals sperren. Wenn ihr euch nochmal den Code des neuen FBs anseht, stellt ihr also fest, dass ich mich mit Ausnahme dieses Aufrufs der Instanz der Basisklasse in der Erweiterung ausschließlich um die Pausen-Funktionalität kümmern musste und dabei aber sämtliche Funktionalitäten der Basisklasse erhalten geblieben sind.

Und genau das macht die Vererbung bei der Weiterentwicklung von Funktionsbausteinen ebenso interessant. weil ich mich darauf verlassen kann, dass sämtliche Funktionalitäten der Basisklasse eben erhalten bleiben und ich mich wirklich nur um das zu kümmern brauche, was in dem FB neu sein soll. Die Vererbung ermöglicht es aber nicht nur, neue Funktionalitäten in der Erweiterung zu ergänzen, sondern auch gegebene Funktionalitäten der Basisklasse zu verändern.

Und das zeige ich euch jetzt an einem zweiten Beispiel. Ich werde jetzt von der gerade entwickelten Einzelsteuerfunktion mit der Pausenfunktionalität einen neuen FB ableiten, bei dem jetzt aber die Ansteuerung des Motors so verändert wird, dass die Zielgeschwindigkeit rampenförmig angefahren wird. Und dafür muss ich einige Methoden der Basisklasse verändern. Genauer gesagt muss ich einige Methoden überschreiben. Was das genau bedeutet und wie das funktioniert, erkläre ich gleich.

Jetzt lege ich aber erst einmal den FB an. Und diesen neuen FB bezeichne ich jetzt mit IDF-to-Dir-Var-Speed-Ramp. Und der ist ab...

abgeleitet von dem FB mit der Pausenfunktionalität. So aufgrund dieser Vererbungshierarchie verfügt der jetzt gerade erstellte FB über alle Methoden und Eigenschaften, die in diesen Klassen hier entweder als Public oder als Protected deklariert sind. Jetzt geht es ja darum, die Geschwindigkeitsvorgabe so zu verändern, dass beim Anfahren die Zielgeschwindigkeit rampenförmig erreicht wird. Wie man diese Vorgabe der Rampe unter Verwendung eines Timer-Bausteins umsetzen kann, habe ich schon mal in einem Video aus dieser Reihe zum Thema Methoden im Detail erklärt.

Da findet ihr also eine ausführliche Erklärung zu der Idee dieser Implementierung und daher gehe ich hier nicht mehr im Einzelnen darauf ein. So wie gerade schon angedeutet, nutze ich also eine Einschaltverzögerung, um diese Rampe zu erzeugen. Ich brauche also als neue Variablen eine Instanz der Einschaltverzögerung. Dann noch die Dauer der Rampe, mit der ich festlegen kann, wie schnell die Zielgeschwindigkeit angefahren werden soll.

Und dann benötige ich noch eine Variable zum Speichern der Zielgeschwindigkeit. Die brauche ich, weil ja die vom Anwender vorgegebene Geschwindigkeit nicht mehr wie vorher direkt auf den Ausgang geschrieben wird, sondern nur als Zielvorgabe dient. Um die Pausenfunktionalität und das Sperren des Antriebs im laufenden Betrieb zu erhalten, muss ich zunächst wieder den Anweisungsteil der Basisklassen aufrufen. Der rampenförmige Verlauf der Geschwindigkeit wird dann durch den Timer erzeugt, den ich hier aufrufe und dabei das In-Signal auf True setze. Das Signal nVal soll durch die Methode mRamp berechnet werden, aber nur unter der Bedingung, dass die Pausenfunktionalität, die von der Basisklasse geerbt wurde, nicht aktiv ist.

Die Methode mRamp gibt es noch nicht, also muss ich die jetzt erzeugen. Hiermit soll ja eine Geschwindigkeit vom Typ int berechnet werden, also ist das auch der Rückgabetyp der Methode. Und dann deklariere ich die Methode noch als private, weil die nach außen nicht sichtbar sein soll.

In dieser Methode passiert dann folgendes. Hier wird die abgelaufene Zeit des Timers in Bezug gesetzt zur gesamten Dauer für das Anfahren des Antriebs. Dieser Quotient ist also eine Normierung auf 1. Und der wird durch Multiplikation mit dem Zielwert der Geschwindigkeit noch in der Amplitude angepasst. So auf diese Weise wird also ein rampenförmiger Geschwindigkeitsverlauf erzeugt. So, damit gehe ich nochmal zurück zum Anweisungsteil des neuen FBs.

Hier seht ihr ja, dass das IN-Signal des Timers auf TRUE ist. Aber damit allein wird ja der Timer noch nicht gestartet, denn das Startsignal ist ja eine steigende Flanke an IN. Das heißt, in einer anderen Methode muss also vorher das IN-Signal einmal auf FALSE gesetzt werden, damit beim nächsten Aufruf dieses Anweisungsteils IN wieder auf TRUE gesetzt wird und damit die steigende Flanke kommt und dann das Anfahren der Rampe beginnt.

Das In-Signal des Timers auf False zu setzen, ist natürlich die Methode mOn zum Starten des Antriebs. Jetzt ist es aber so, dass die Methode mOn der Basisklasse EDF-to-DiAVAR-Speed den Timer gar nicht kennt, denn schließlich ist er ja nur in dem abgeleiteten FB deklariert und folglich kann dort das Eingangssignal in auch nicht verändert werden. Das bedeutet also, dass ich in dieser Erweiterung eine neue Methode mOn brauche, in der das IN-Signal des Timers zurückgesetzt wird. Die lege ich jetzt zunächst einmal an. Hierbei muss ich darauf achten, dass diese Methode genauso heißt wie die Methode der Basisklasse und auch denselben Rückgabetyp liefert und dieselben Übergabeparameter aufnimmt.

Das heißt, der Deklarationsteil meiner neuen Methode MON muss... mit Ausnahme lokaler Variablen, die es hier aber noch nicht gibt, identisch zum Deklarationsteil der Methode der Basisklasse sein. Auf diese Weise überschreibe ich also die Methode mOn.

Okay, warum habe ich die Methode mOn überhaupt überschrieben? Hier soll ja jetzt der Timer zurückgesetzt werden. Aber das alleine würde den Antrieb nicht starten, denn dazu muss ich noch das Enable-Signal setzen.

Wenn ihr nochmal in die Methode mOn der Basisklasse schaut, stellt ihr fest, dass genau das dort schon gemacht wird. Es wäre also wieder gut, wenn ich diesen Code nicht kopieren müsste, sondern diese Befehle aus meiner neuen Methode mOn aufrufen könnte. Und das funktioniert wieder mit dem Superzeiger. Den dereferenziere ich wieder und wenn ich dann den Punktoperator anwende, bekomme ich in diesem Kontextmenü wieder die Auswahl aller Methoden und Eigenschaften angezeigt, unter anderem eben auch die Methode mOn der Basisklasse. Die rufe ich auf und übergebe dann noch die Übergabeparameter zur Drehrichtung und der vorgegebenen Geschwindigkeit.

So, an diesem Beispiel mit der Rampe hier solltet ihr bisher folgendes verstanden haben. Bei der Vererbungsbeziehung können geerbte Methoden der Basisklasse verändert werden. Dazu müsst ihr in dem erweiterten FB einfach eine Methode anlegen, die denselben Namen, denselben Typ des Übergabeparameters und dieselben Rückgabeparameter hat.

Wenn es dann also in einer Vererbungsbeziehung zwei Methoden gibt, die in diesem Sinne gleich sind, dann hat immer die lokale Methode den Vorrang. Das heißt in meinem Beispiel also, dass wenn ich die Methode mOn auf ein Objekt vom Typ EDF2DirVarSpeed anwende, wird diese Methode ausgeführt. Und wenn ich die Methode mOn auf ein Objekt vom Typ EDF2DirVarSpeed-Ramp anwende, wird diese Methode ausgeführt. So, ich hoffe ihr habt noch ein bisschen Ausdauer, denn dieses Beispiel geht noch einen Schritt weiter. Wenn ihr jetzt mal in die Methode mOn der Basisklasse schaut, seht ihr, dass die vorgegebene Geschwindigkeit in den Methoden mForward bzw.

mBackward gesetzt und direkt auf den Ausgang geschrieben wird. In meinem veränderten Rampenbeispiel soll ja jetzt aber nicht der Ausgang direkt beschrieben werden, sondern der Rückgabewert von mLimitVal soll ja auf die Zielgeschwindigkeit nValSet geschrieben werden. Damit das also so passiert, muss ich diese Methoden MForward und MBackward wieder überschreiben. Ich lege diese also nochmal an, wieder mit denselben Bezeichnern, denselben Übergabeparametern und übrigens auch mit demselben Zugriffsspezifizierer Protected. Und die Anweisungsteile sehen dann fast so aus wie bei den Methoden der Basisklasse, mit dem feinen Unterschied, dass die Geschwindigkeit eben auf den Zielwert und nicht direkt auf den Ausgang geschrieben wird.

So warum ist diese Ergänzung jetzt interessant und etwas anderes als das, was ich euch vorher zum Überschreiben von Methoden gezeigt habe? Auf den ersten Blick scheint hier ja nichts anders zu sein, aber wenn ihr mal genauer drauf schaut, wie die Methoden aufgerufen werden, ist doch eine Sache bemerkenswert. Wenn auf eine Instanz der Klasse IDF toDirVaSpeedRamp die Methode mon angewendet wird, wird hier eben dieser Code hier ausgeführt. Und dabei wird also die Methode mOn der Basisklasse aufgerufen und es wird dann also dieser Code hier ausgeführt. So und hierin werden die Methoden mForward und mBackward aufgerufen.

Und jetzt ist es ja so, dass die Basisklasse auch genau diese Methoden hat. Man könnte jetzt also denken, dass beim Aufruf von mOn der Basisklasse auch deren Methoden mForward und mBackward ausgeführt werden. Das ist aber nicht so, denn wenn in der Klasse über die der Methodenaufruf initiiert wird, also hier die Einzelsteuerfunktion mit der Rampe, genau diese Methoden überschrieben sind, dann werden auch immer die lokalen Implementierungen bevorzugt. Das heißt, hier werden dann also die Methoden M-Forward und M-Backward aus dem FB-IDF-To-Dir-Var-Speed-Ramp ausgeführt. So, und hier kommt noch einmal der Nachweis am praktischen Beispiel, dass diese Implementierung funktioniert.

In meinem MADE-Programm ändere ich dazu nur den Typ der Einzelsteuerfunktion. So und dann werden die Geschwindigkeitsvorgaben also immer rampenförmig angefahren und zusätzlich bleiben natürlich die Pausenfunktionalität und die Möglichkeit zum Sperren des Antriebs im laufenden Betrieb weiter erhalten.