OOP in grafischen Programmiersprachen. Teil 2 MOS und OOP

Im ersten Teil habe ich versucht zu zeigen, dass eine schwarze OOP-Katze in einem dunklen Raum grafischer Sprachen existiert, auch wenn es sich nicht um eine Katze handelt, sondern um eine halbtote Katze des Schrödinger-Flayers, das heißt, es ist keine. Ein Beispiel für die Implementierung der Methodik der objektorientierten Programmierung wurde gezeigt, wenn ein Programm kein C ++ - oder Java-Code ist, sondern ein Simulink-, SimInTech-, SimulationX- oder SCADE-Esterel-Diagramm - eine beliebige grafische Notation der Algorithmusbeschreibung.


In Werbematerialien verwendet Matlab Simulink häufig den Begriff MOS - Model-based Design. In vielen Texten betonen sie, dass das grafische Diagramm des Algorithmus ein Modell ist, was natürlich wahr ist. In der anfänglichen Definition von MOS ist das Modell jedoch hauptsächlich das Modell des Objekts, für das das Steuerungssystem entwickelt wurde, einschließlich der Steuerungssoftware. Weitere MOS-Methoden werden hier beschrieben. Daher ist es bei der Entwicklung eines Steuerungssystems gemäß der MOS-Methodik möglich und notwendig, die OOP-Methodik für die Entwicklung von Verwaltungssoftware zu verwenden. Und um das Problem mit den Modellen vollständig zu schließen, hier ein Bild mit den Unterschieden zwischen den beiden. Wenn alles klar ist, können Sie nicht mehr lesen.




Kehren wir zu den Grafiksprachen und OOP zurück, die für Steuerungsprogramme für die Industrie gelten. Die grafische Programmiersprache liefert einen Programmdatensatz in Form eines Diagramms aus einer Reihe von Blöcken, die durch Kommunikationsleitungen verbunden sind. In der industriellen Programmierung wird in der Regel von einem automatischen Codegenerator aus einer Grafikschaltung ein Code für den Compiler und das anschließende Laden in das Zielgerät erstellt.


Als Teil meiner Spezialität musste ich mich mit der Verwaltung von Software für Kernkraftwerke befassen, bei der es verboten ist, C ++ mit Sicherheitsstandards zu verwenden, nur reines C und in einigen Fällen nicht einmal C, sondern „Eisen“ -Logik, bei der die Algorithmen als elektronische Schaltungen auf Transistoren und Transistoren implementiert sind Relais. Und die Einhaltung von Standards wird von strengen Aufsichtspersonen überwacht.



Egal wie überraschend es auch klingen mag, die Entwicklung verwendet immer noch die OOP-Methodik. Denn wenn Sie OOP nicht verwenden können, es aber wirklich wollen, können Sie es. Richtig, dann muss alles zurückgegeben werden, und das wäre aus Sicherheitsgründen und über alles Standards kein C ++ und der endgültige Code. Wie sie sagen, einen Fisch zu essen und sich nicht wegen Verstößen hinzusetzen.


Um ein reales Objekt durch die Definition von OOP zu erstellen, binden wir Datenstrukturen und Verarbeitungsschemata in ein einziges Objekt. Dies wird als Kapselung bezeichnet. Und da wir C ++ nicht für zuverlässige KKW-Systeme verwenden können, müssen wir dies alles beim Generieren des Codes analysieren. Wie in den Kommentaren zum vorherigen Artikel erläutert, funktionierte der erste C ++ Front-Compiler auf die gleiche Weise und übersetzte OOP C ++ - Code in reines C.


In der ersten Version der Implementierung von OOP in einer grafischen Sprache haben wir einen speziellen Block erstellt, der ein grafisches Berechnungsschema enthält. Während der Initialisierung band dieser Block das Klassenschema (die Methode) an eine bestimmte „Klasseninstanz“ - eine Reihe von Variablen, die auf besondere Weise benannt wurden.


Betrachten Sie die zweite Ausführungsform der OOP-Methodik in grafischen Programmiersprachen. Abbildung 1 zeigt die Funktionsweise des Sensorverarbeitungsalgorithmus.



Abbildung 1. Sensorverarbeitungsprogramm.


Dies ist eine Klassenmethode. Es entspricht seiner eigenen Kategorie "Sensoren" in der Datenbank - einer abstrakten Klasse mit einem bestimmten Satz von Feldern und einer Instanz des klassenspezifischen Sensors KBA31CFO1 . Für diesen Sensor haben die Felder bestimmte Werte, einige der Felder werden vom Benutzer festgelegt, einige der Felder werden während der Ausführung des Programms berechnet. siehe Bild 2



Abbildung 2. Die Signaldatenbank mit der offenen Kategorie „Sensor“.


Bisher ist alles wie in der ersten Ausführungsform, in der wir die Bindung des Entwurfsschemas an einen bestimmten Sensor bei der Installation der Einheit in der Schaltung hergestellt haben. "Wo ist der Unterschied?" - Du fragst. Und der Unterschied liegt im Block. Wenn in der ersten Version ein Entwurfsdiagramm vorhanden war, das bei jeder Installation des Blocks kopiert wurde, sieht das Innere in dieser Version ungefähr so ​​aus:



Abbildung 3. Die Innenräume einer Blockinstanz einer Klasseninstanz.


Anstelle des Entwurfsschemas werden nur Datenübertragung und -empfang innerhalb des Blocks "angezeigt".
Die Berechnung selbst findet an einer anderen Stelle statt, im Diagramm aus Abbildung 1. In einigen Fällen ist es möglich, im Berechnungsdiagramm überhaupt keine Blöcke zu verwenden. Es reicht aus, Instanzen der Sensorklasse in der Singles-Datenbank zu haben. Dies ist die zweite Möglichkeit, die Kapselung in Grafiksprachen zu implementieren. Der Trick besteht darin, dass alle Blöcke in dem Diagramm von 1 vektoriell sind und nicht ein Signal verarbeiten, sondern einen Vektor von Signalen von allen Sensoren dieses Typs im Berechnungsschema. Wenn Sie den Modus zum Anzeigen der Ergebnisse auf der Kommunikationsleitung aktivieren, sehen wir, dass jede Kommunikationsleitung nicht eine Ziffer, sondern einen Vektor mit 4 Zahlen enthält (entsprechend der Anzahl der Sensoren in der Datenbank).



Abbildung 4. Diagramm zur Verarbeitung des Signals von Sensoren im Modus zum Anzeigen von Werten.


Somit implementiert ein Verarbeitungsschema die Verarbeitung aller Sensoren im Projekt, und jeder Sensor wird mit seinen eigenen Parametern verarbeitet, die in der Datenbank als Merkmale einer bestimmten Instanz der Klasse angegeben sind. Beispielsweise nimmt der Begrenzer den Maximalwert aus der Datenbank, der für die ersten drei Sensoren gleich eingestellt ist und sich für den vierten unterscheidet. (siehe Abb. 5)



Abbildung 5. Parameter des Begrenzerblocks im Berechnungsschema.


Und was ist mit dem resultierenden Code, der automatisch nach diesem wunderbaren Schema generiert wird? Wie schaffen Sie es, OOP-Artefakte zu vermeiden? Es ist ganz einfach: kein Schummeln und kein OOP, reines C im Code. Für jeden Block des Vektorverarbeitungsschemas wird ein Zyklus gebildet, der so viele Berechnungen liefert, wie Klasseninstanzen im Projekt vorhanden sind. In unserem Fall gibt es 4 Sensoren, daher bilden wir zuerst Arrays der Dimension „4“, indem wir die Signale von den Sensoren lesen:


/* Index=104 UID=104 GeneratorClassName=TSignalReader Name=buz1.sensor.Macro6.Macro3.Macro157.SignalReader3 Type=    */ }; state_vars->kbastdv104_out_0_[0] = kba31cf001_mf_type; state_vars->kbastdv104_out_0_[1] = kba32cf001_mf_type; state_vars->kbastdv104_out_0_[2] = kba33cf001_mf_type; state_vars->kbastdv104_out_0_[3] = uf40y329084320_mf_type 

Dann sortieren wir alle Blöcke der Reihe nach und führen sie in einer Schleife aus. Für jedes Element eines Array vom Typ wird jeder Berechnungsblock für alle Sensoren durchgeführt.


 /* Index=211 UID=211 GeneratorClassName=TAndSrc Name=buz1.sensor.Macro6.And61 Type=  */ for(i=0;i<4;i++){ locals->v211_out_0_[i] = state_vars->kbastdv125_out_0_[i] && (!(locals->v191_out_7_[i] > 0.5)); /* Index=212 UID=212 GeneratorClassName=TMulDbl Name=buz1.sensor.Macro6.Mul_oper1 Type= */ locals->v209_out_2_[i] = consts->kbastdv121_a_[i]*state_vars->kbastdv127_out_0_[i]; /* Index=213 UID=213 GeneratorClassName=TSumSrc Name=buz1.sensor.Macro6.Add_oper1 Type= */ locals->v209_out_3_[i] = (1)*consts->kbastdv122_a_[i]+(1)*locals->v209_out_2_[i]; … } 

Nach der Berechnung zeichnen wir die Signale für jede Instanz der Klasse auf:

 /* Index=776 UID=776 GeneratorClassName=TSignalWriter Name=buz1.sensor.Macro6.Macro3.SignalWriter4 Type=    */ kba31cf001_mf_xb01 = state_vars->kbastdv207_out_0_[0]; kba32cf001_mf_xb01 = state_vars->kbastdv207_out_0_[1]; kba33cf001_mf_xb01 = state_vars->kbastdv207_out_0_[2]; uf40y329084320_mf_xb01 = state_vars->kbastdv207_out_0_[3]; 

Wie Sie sehen können, enthält der endgültige Code keine Objekte. Sauber, unschuldig und sicher SI. Im obigen Beispiel, der Implementierung von OOP in einer Grafiksprache, berechnet eine Vektorschaltung alle Sensoren des gleichen Typs. Mit dieser Technik können Sie ein Schema ändern, um die Verarbeitung aller Sensoren zu ändern.


Ein weiterer zusätzlicher Vorteil dieses Ansatzes ist die Fehlerversicherung. Stellen Sie sich vor: Sie fügen manuell einen Sensor hinzu und haben an einer Stelle vergessen, die Anzahl der Wiederholungen während der Verarbeitung in einem Zyklus zu erhöhen. Kein statischer Code-Analysator kann diesen Fehler erkennen, der Code ist korrekt. Und selbst bei der Arbeit wirkt sich dies möglicherweise nicht sofort und auf offensichtliche Weise aus.


Nun, am Ende der versprochene Polymorphismus und Vererbung. Bei der ersten Methode erhielt der Benutzer viele identische Schemata, die er nach der Installation des Untermodellblocks bearbeiten und dadurch einen Polymorphismus ausführen konnte, wodurch das Verhalten einer bestimmten Instanz der Klasse geändert wurde. Ich denke, jeder hat vermutet, dass es möglich ist, das Verarbeitungsschema für einen bestimmten Sensor zu ändern, und wir werden eine neue Klasse erhalten, die dieselben Felder hat, aber die Methoden sind unterschiedlich. Sie können auch neue Felder hinzufügen und eine neue Klasse mit verschiedenen Feldern abrufen, die alle Felder des übergeordneten Elements und die Methoden des übergeordneten Elements enthalten.


Abbildung 6 zeigt ein Beispiel für zwei Blöcke der Klassen „Eltern“ und „Erbe“. Innerhalb des Blocks wird das Berechnungsschema der übergeordneten Klasse gespeichert. Alle Daten gehen zu einem gemeinsamen Vektorblock, ähnlich dem Block in Fig. 1. 4. Die übergeordnete Klassenmethode wird vollständig wiederholt. Und dann hat die Nachfolgeklasse ein zusätzliches Feld Yatm und eine zusätzliche Neuberechnung des Wertes unter Verwendung des linearen Interpolationsblocks.


Somit bleibt es möglich, die Verarbeitungsmethoden des Elternteils zu ändern, die sich in allen Klassenerben ändern, sowie das Verhalten des Erben individuell anzupassen.



Abbildung 6. Polymorphismus bei Klassenerben.


Zusammenfassend können wir argumentieren, dass die OOP-Methodik verwendet werden kann, um Software in grafischen Programmiersprachen zu erstellen. Abstraktion, Kapselung, Vererbung, Polymorphismus - all diese Prinzipien lassen sich mit den richtigen Entwicklungswerkzeugen einfach und natürlich umsetzen. Es ist wichtig zu beachten, dass der endgültige Code nach der automatischen Generierung aus einer Grafiksprache rein sicheres C ohne OOP bleibt


In einigen Fällen ist das Ergebnis der Entwicklung von Steuerungssoftware in grafischer Form nicht der C-Code zum Laden in die Steuerungen, sondern der Schaltplan "Eisenlogik", aber die oben beschriebene Technik OOP funktioniert auch einwandfrei.

Source: https://habr.com/ru/post/de454232/


All Articles