Inhalt
Topic:.FBclMission.
Last changed: 2019-11-21
In diesem Artikel ist zunächst die Bedeutung der Grafischen Programmierung für Embedded Anwendungen dargestellt. Es wird dann auf ein konkretes Projekt, FBcl (Function Block Connection Language) geschwenkt und diese Entwicklung dann im Detail dargestellt.
Dieser Artikel hat informierende Bedeutung. Die FBcl ist ein Entwicklungsprojekt, an dem sich allerdings interessierte Anwender und/oder Mitentwickler beteiligen können.
Topic:.FBclMission.mission.
Das Thema der Grafischen Programmierung ist als Alternative oder besser Ergänzung zur Programmierung in den allgemeinen statischentextuellen Sprachen C, C++, Java und C# einzuordnen. Nach Meinung des Verfassers hat die textuelle Programmierung dann Nachteile, wenn es um die umfängliche Gesamtheit der Software geht. Für Details ist eine textuelle Programmierung in statischen Programmiersprache, also insbesondere C für Hardwarenähe und Betriebssystem, (zeitlos) passend. Übertragen gilt das auch für Algorithmen, die inJava oder C# sehr gut darstellbar sind.
Topic:.FBclMission.mission.nonObscure.
video: Einführung in Grafische Programierung
Bild: Kunde-Entwicklung Missverständnisse
Auf die Frage meines Vertriebs-Kollegen, was denn im neuen Update so alles geändert wurde, versuche ich zu erklären: Dies,das und jenes. Mein Gegenüber macht große Augen, weil, er versteht nicht wo von ich rede.
Software kommt dem Nicht-Softwerker oft undurchsichtig, obscure, vor. Was ist da drin in der Kiste? "Bitte schreibe konkreteFeatures auf, die ich dem Kunden weitergeben kann" .... Was habt ihr schon wieder geändert?
Bei einer Anordnung von mechanischen Teilen oder einer Hardware ist das einfacher. Da sieht man was das ist (auch wenn das Detail vielleicht nicht genau durchschaut wird). Geht das bei Software auch?
Bild: Kunde-Entwicklung - grafischer Blick
Meine Antwort "Ja" auf diese Frage besteht aus zwei Komponenten:
1) Die Software muss so in Blöcke zergliedert werden, dass dem Anwender begreiflich ist: Dieser Block ist für dies, und derandere Block für jenes zuständig. Damit wird das Ganze begreifbar (man kann den Block, wenn auch nur virtuell, greifen).
2) Wichtig ist die Sicht auf die Daten, nicht die Algorithmen und schon gar nicht die C-Zeilen. Der Programmablauf ist kaum vermittelbar, zuviel, andere Denkweise. Wenn aber jedem Block bestimmte Daten zugeordnete werden, kann der Anwender etwas damit anfangen: "Dort stehen die Parameter, die Du ja kennst. Dort kommen die Messwerte hinein, werden skaliert" ...usw.
Mit dem zweiten Punkt ist man eigentlich bei der Objektorientierung, die den Blick auf die Daten als primär gegenüber demBlick auf Abläufe (Programmstrukturen) betont. Eigentlich recht klar für den Informatiker. Interessant wird es aber, die Daten aus einer laufenden Software auf dem PC oder auf einem Gerät sofort darzustellen, visuell, bei laufender Software. Auch dieser Punkt wird dann essentiell für die grafische Programmierung.
Der erste Punkt deutet klar auf eine grafische Progarmmierung. Diese ist ein Muss, wenn Software durchschaubar sein soll.
Topic:.FBclMission.mission.now.
Es gibt für bestimmte festgelegte Hardwareplattformen häufig Tools, die entsprechend grafisch orientiert sind.
Als bedeutendsteen Vertreter möge hier die Automatisierungprojektierung genannt werden, wie sie etwa mit Simatic S7 - TIA-Portal oder den Adäquaten anderer Firmen wie Codesys bekannt sind. Diese Art der grafischen Programmierung hat direkte grafische Wurzeln: Ein Verdrahtungsplan von Relaisteuerungen wurde ebenfalls grafisch ausgeführt. Die ersten Projektierungen waren adäquate den Relaisverdrahtungen ausgeführt und haben den Namen Speicherprogrammierte Steuerung in den Anfangsjahren ab ca. 1970 und sehr deutlich ab den 80-gern geprägt. Die Standards dazu sind in den 90-gern manifestiert (IEC-61131) und noch heute gültig. Die erste Programmierung solcher Art war die den Relaisschaltungen nachempfundene Ladder-Graphic wie sie im amerikanischen Sprachraum heißt. Ladder kommt tatsächlich von 'Leiter' und spiegelt die Anordnung der jeweiligen Verbindungen zwischen dem Spannungsleiter und der Masse wieder, wie Stufen einer Leiter. Relativ schnell hat sich aber eine zweite Darstellung durchgesetzt, die ebenfalls in Hardwareverdrahtungen bekannt ist, die Verbindung von Funktionsbausteinen mit Signalen. In Hardwareschaltungen sind die Funktionsbausteine häufig Logik-Gatter (AND, OR) und Flipflops, jedoch auch komplexere Logiken wie Zähler, Anlalogsignalverarbeitung, komplexe Submodule.
Die Programmierung in der Automatisierungstechnik wird allerdings auch durch eine in der Norm enthaltene textuelle Programmierung ergänzt. Der heute wichtigster Vertreter ist ST = Structure Text oder auch SCL als S7-spezifische Ausprägung. Häufig werden die Basis-Funktionsbausteine in ST programmiert und dann mit einem Funktionsplan als Verdrahtung von Funktionsbausteinen zusammengeschaltet.
Ein Beispiel für grafische Programmierung für die Laborautomatisierung sei Labview von Texas Instruments genannt.
Auch wenn mit einem beispielsweise Simulink (Mathworks) bestimmte Hardwareplattformen vom Hersteller vorgegeben (ein Support package) vollständig grafisch programmiert werden kann, dann handelt es sich um eine ähnliche Konstellation: Bestimmte Hardware, darauf angepasstes grafisches Tool. Für Speziallösungen gibt es nun erheblich viele solche Grafische Programmierumgebungen.
Topic:.FBclMission.mission..
- und diese führt meist zu Zeilenprogrammierung in C oder C++.
Die vorhandenen Grafik-Programmiertools passen nur zu den vorgegebenen Hardwareplattformen. Wenn aber der Prozessor noch nicht feststeht, weil Anforderungen an Preis, Größe, Wärmeableitung wichtiger sind als die einfache Auswahl einer Standardhardware, dann kommt man nicht mit einer Standardhardware-Grafikprogrammierumgebung hin.
Topic:.FBclMission.mission.envSim.
Komplexe Software wird heute mehr und mehr auf dem PC auch in komplexer Form vorgetestet, wobei die Umgebung als digital twin (Digitale Abbildung, twin=Zwilling) realisiert wird. Simulationstools die physikalische Zusammenhänge gut abbilden sind geeignet. Dazu zählt das bereits erwähnte Simulink, auch Modelica und andere.
Topic:.FBclMission.mission..
Eine allgemeine Grafische Programmierung, die mit einem Codegenerator meist primär für C verbunden ist, ist an verschiedene Hardwaregegebenheiten anpassbar. Ein System, was so arbeitet und dies bietet, ist beispielsweise Simulink von Mathworks.
Verwendet man Simulink für eine vorgegebene Hardware (Raspberry Pi wird beispielsweise unterstützt), dann ist das sehr einfach. Die Adaption auf eine allgemeine Hardwarebasis erfordert aber die bekannten Kenntnisse der Systemprogrammierung in C und Betriebssystemkenntnisse. Da also das Programmierpotenzial sowieso vorhanden ist und die zu programmierenden Aufgaben überschaubar scheinen, wird also gleich alles in C oder C++ programmiert. Das ist die häufig anzutreffende Praxis. Ein Einsatz etwa von Simulink bedeutet Lizenzkosten, Einarbeitungszeit und letztlich eine gewisse Abhängigkeit von den Tools und dessen Lieferanten, denn die Codegenerierung ist zwar gut, aber durchaus komplex. Folglich wird differenziert entschieden ob man diesen Weg geht.
Um bei Simulink zu bleiben, diese Toolbasis hat allerdings noch einige weitere Features zu bieten, insbesondere die umfängliche Umgebungssimulation, die mitnichten mit etwas C(++)-Programmierung zu ersetzen ist. Setzt man an dieser Stelle auf Simulink, was durchaus empfehlenswert ist, liegt es etwas näher, dann auch gleich dessen Codegenerierung für das Zielsystem zu nutzen.
Die Nachteile verbleiben aber:
Die Codegenerierung ist Tool/Hersteller-spezifisch.
Man bindet sich langjährig an die Toolbasis, wenn man von einer kontinuierlichen Weiterentwicklung ausgeht.
Wenn der Kooperationspartner zufällig nicht auch auf Simulink gesetzt hat, dann kann man zwar für die Umgebungssimulation eine Reihe von Kompatibiltätslösungen nutzen wie Einbindung von Windows-dll, oder FMI-Datenaustausch. Jedoch bei Zielsystem-Code kommt man schlecht zueinander. Die größte gemeinsame Schnittmenge ist dann doch nur C.
Topic:.FBclMission.mission.GraftxtPrg.
Auch und insbesondere in der Automatisierungsgeräteprogrammierung, dort wo die grafische Programmierung immer schon verbreitet ist, ist eine Mischung aus Text- und Grafikprogrammierung zu beobachten:
Algorithmen, in denen häufig einfache interne Kombinatorik im Zusammenhang mit Fallunterscheidungen und Schleifen (if
und for
oder while
) vorkommen, werden oft textuell programmiert. interne Kombinatorik meint hier, dass ein Anlagenprogrammierer nicht ein Anlagensignal kombinatorisch verknüpft sondern dass es sich
um eine als Black-Bock beschriebene Funktion handelt.
Die textuelle Programmierung ist für eine einzelne Funktion überschaubar und gewohnt.
Dies trifft häufig für Kern-Funktionen zu, also den FunctionsBlocks, die dem Anwender dann letztlich für den Bau beliebiger Systeme zur Verfügung gestellt werden. Dabei ist eine Arbeitsteilung anzutreffen aber nicht notwendig: Zwischen Projektierer (kennt die Anlage, arbeitet grafisch) und Programmierer (kennt Funktionen, kennt die Analge gegebenenfalls nicht oder nur ungenau).
In der Automatisierungsgeräteprogrammierung wird nirgends direkt in Maschinencode programmiert, außer man zählt Programme in Instruction Code oder AWL im Siemens-S7-Jargon dazu, bei denen man diverse Hardwaregegebenheiten ansprechen kann. Für das Ansprechen von Hardwaresignalen gibt es ja die fertigen System-FBlocks.
Genau deshalb braucht man bei der Programmierung im Embedded Bereich die C-Ebene:
für das Ansprechen von Hardware
für den Aufruf von Betriebssystemfunktionen meist über API-Schnittstellen in C.
Diese Teile sind in speziellen FBlocks realisierbar, die für eine Embedded Programmierung im Vergleich zur Automatisierungsgeräteprogrammierung die selbst erstellen System-FBlocks darstellen.
Man kann folgende drei Levels unterscheiden:
Grafische Programmierung des Gesamtsystems
Spezielle Funktionen in den FBlocks als textuelles Programm, textuelle Sprache jeweils dem erstellungssystem angepasst (für Simulink bedeutet das beispielsweise Math-Scripts)
Systemfunktionen in der Zielsystemsprache. In Simulink gibt es dafür die S-Functions in C.
Topic:.FBclMission.mission..
Schaut man sich Funktionsblock-orientierte grafische Programmierungen an, dann ist ein gleiches Schema zu erkennen: Funktionsböcke führen Funktionen aus, können dabei Daten speichern und sind per Datenfluss verbunden. Auch wenn Statemaschinen im Spiel sind, dann sind diese aufgrund der Normung, die die UML ausstrahlt, meist ähnlich ausgeführt.
Die FBcl, Function Block connection language führt diese Grundidee zusammen zu einer einheitlichen Darstellung der Inhalte der Grafischen Programmierung. Es ist nun möglich, beispielsweise aus Simulink, das erfolgreich für die Gesamtsimulation eingesetzt wird, eine Darstellung in FBcl auszuleiten, mit anderen Grafischen Programmiertools zu mischen, und daraus einen Zielsystem-Code meist in C oder auch C++ zu erzeugen, der sich in handgeschriebene Codeteile einordnet. Die Bedeutung eines Tools wie hier am Beispiel Simulink dargestellt wird damit nicht geschmälert sondern im Gegenteil, ist für die Gesamtsimulation noch eher einsetzbar. Die Zielsystem-Codierung wird aber mehr in den Sichtbereich des Embedded Programmierers gerückt.
Als textuelle Darstellung der Zusammenhänge der Funktionsblöcke wurde eine Anleihe aus der Automatisierungsgeräteprogrammierung genommen: Es wurde in diesem Bereich mit der Norm IEC-61499 seit etwa 10 Jahren eine interessante Darstellung geschaffen, die die Funktionsblöcke wie in der basierenden Norm IEC-61131 mit Daten verbindet, aber zusätzlich noch Eventverbindungen kennt. Diese Eventverbindungen sollen aus Sicht der Automatisierungsprogrammierung die Erstellung verteilter System erleichtern, da aus den Eventbeziehungen automatisch die notwendigen Kommunikationsstrukturen (Prozess-Buskommunikation) abgeleitet werden können. Diese müssen also nicht manuell erstellt werden, damit geht eine Flexibilisierung der Aufteilung der Funktionsblöcke und damit gebaute Module auf verschiedene Hardware-Geräte einher.
Diese Eventkopplung ist nun auch sehr nutzbringend anwendbar für die Regelung der Abarbeitungsreihenfolge von Funktionsblöcken sowie auch deren Zuordnung zu verschiedenen Abtastzeiten oder Input-Events beispielsweise von Bedieneingriffen.
Damit ist die IEC-61499 eine geeignete allgemeingültige Darstellungsform von Funktionsblöcken und deren Connections und somit hier ausgewählt.
Topic:.FBclMission.mission.UML.
Man redet häufig bei Einsatz von UML-Tools von Modellgetriebener Softwareentwicklung im Gegensatz zu der manuellen Zeilenprogrammierung. Die UML als Unified-Darstellung von Zusammenhängen sollte ihre Bedeutung nicht verlieren. Jedoch sind gelten für die gängigen Tools adäquate Bemerkungen wie für bestehende Tools der Grafischen Programmierung mit Funktionsböcken.
Im Abschnitt Chapter: 9.2 Eventkopplung und Objektorientierung ist der Event-Gedanke in der Objektorientierung beleuchtet, wie er für die FBcl maßgebend ist. Auch in den UML-Tools spielen die Events die dazu passende Rolle.
Im Abschnitt Chapter: 10 Objektorientierte Referenzkopplung von Funktionsblöcken ist das Theme der Referenzierung in Funktionsblöcken angeschnitten. Der Verfasser hat dieses Prinzip schon in den 90-ger Jahren im STRUC für Simadyn realisiert und in Simulink aktuell lauffähig implementiert. Die Nutzung im Zusammenhang mit der FBcl ist geplant. In der UML tangiert dies die Klassendiagrammdarstellung. Siehe auch https://www.embedded-software-engineering.de/grafische-objektorientierte-c-programmierung-mit-simulink-a-726729/
Die Statemachine-Darstellung wie sie in der UML bekannt ist und ursprünglich von David Harel über das Tool Statemate (I-Logix) und später Rhapsody in die UML eingebracht worden ist und im UML-Standard https://www.omg.org/spec/UML/About-UML/ gut beschrieben ist, hat sich allgemein verbreitet und ist daher auch in einigen Tools der Funktionsblockprogrammierung adäquate anzutreffen. Hier hat die UML den Maßstab gesetzt.
Bezüglich der UML auf der einen Seite und der gängigen Funktionsblock-Programmierung auf der anderen Seite sollte es zukünftig Überschneidungen geben. An dieser Stelle gibt es insgesamt nützliches Verbesserungspotenzial.
Topic:.FBclMission.mission.noSim.
Betrachtet man einen Gesamtkomplex: Simulation der Physik, Realisierung beispielsweise einer Regelung die später in einem Zielsystem mit dieser realen Physik arbeiten soll, dann ist man beispielsweise in Simulink, aber auch anderen Tools wie Modelica richtig.
Simulink als auch Modelica & co produzieren ablauffähigen Code meist mit einer C/++-Zwischenebene für die Simulation auf einer Rechnerplattform. Nebst dem PC als Simulationsmaschine gibt es häufig für umfangreiche Simulationen Speziallösungen, im Simulink-Bereich etwa https://www.speedgoat.com.
Dieser Bereich wird von FBcl nicht tangiert.
Insbesondere Simulink bietet neben der Codegenerierung für das schnelle Simulieren auch die Codegenerierung für Embedded Hardware an. In diesem Bereich gibt es die Überdeckung mit FBcl: Man kann direkt aus Simulink Target-Code generieren, man kann aber auch aus Simulink FBcl generieren und daraus dann den Targetcode. Für beide Wege braucht man allerdings die Coder-Lizenz von Mathworks für Simulink. Damit tritt FBcl nicht als Konkurrenz zum Simulink-Coder auf, sondern als Alternative.
Die Leistungsfähgigkeit von FBcl ist hier (in Bezug auf Simulink) die unabhängig gestaltbare Codegenerierung aus der FBcl-Zwischensprache, wie es in Chapter: 2.2 Versionierung und Re-Generierung über FBcl hat Vorteile gezeigt ist.
Will man lediglich nur eine grafische Programmierung für das Targetsystem, dann kann man beispielsweise diese in der 4diac-IDE https://www.eclipse.org/4diac/en_ide.php realisieren. Die Speicherung der Grafik erfolgt in IEC-61499 und ist direkt mit FBcl verarbeitbar. Man kann in 4diac-IDE auch eine gewisse Umgebungssimulation aufbauen, die allerdings nicht die Physik-Möglichkeiten etwa eines SImulink oder Modelica kann. Wenn dies ausreichend ist, dann ist das ein guter Weg.
Topic:.FBclDef.
Last changed: 2019-11-21
Dieses Kapitel soll definieren, was unter FBcl (Function Block Connection Language) in der konkreten Ausprägung zu verstehen ist beziehungsweise was das Konzept ist. Es erfolgen Abgrenzungen zu anderen bekannten Programmiertools.
Topic:.FBclDef.Smlk.
Modelierungstools wie Simulink oder auch Modelica sind zu einem großen Teil auf die Umgebungssimulation und in dem Zusammenhang auf die Simulation der in Software zu gießenden Funktionalität gerichtet. Beides ist mindestens gleich zu bewerten.
Häufig werden Modellierungsteils (aus Managersicht) nur als bessere Werkzeuge für die Softwareerstellung des Zielsystems angesehen. Man würde Kosten sparen, weil nicht manuell in C-Zeilen programmieren werden braucht. Diese Denkweise ist zwar richig insbesondere wenn die Funktionsgebung von physikalisch oder regelungstechnisch denkenden Entwicklern bestimmt wird, nicht von Softwerkern. Jedoch schlägt eine generelle Ablehnung der Zeilenprogrammierung als Effektivitätssteigerung oft ins Gegenteil um. Beides muss Hand in Hand gehen. Die generierten Codes sind ggf. zu begutachten, nicht um manuell nachzubessern sondern um möglicherweise falsche Einstellungen der Codegenerierung zu erkennen oder die Modellgestaltung per Styleguide zweckdienlich zu verbessern. Außerdem muss für allgemeine Embedded Ablaufumgebungen eine Einbindung erfolgen. Letzlich muss man auch in Hardware- und Betriebssystemdingen Kenntnisse bewahren, solange nicht auf Standardhardware umgestellt wird.
Die Umgebungssimulation sollte keinesfalls unterbewertet werden. Die Möglichkeit, damit physikalische relalistische Test durchführen zu können, ist zwar im kommen. Die Gefahr besteht aber immernoch, dass dies als 'Nebensache' abgetan wird,
Festzustellen wäre, das Simulink deshalb sehr stark zu empfehlen ist, weil der Umgebungssimulation ein hoher Stellenwert eingeräumt werden sollte. In dieser Richtung ist die FBcl als Konzept nicht angesiedelt.
Aus den Teilen eines Simulink-Modells, die funktionsgebend für die Ziel-Software (Target-Software) sind, kann man FBcl generieren. Dabei ist es nicht das Ziel, gestandene Anwendungen der Codegenerierung in Simulink abzulösen. Es werden auch nie alle Features, die in Simulink aus verschiedenen Intensionen entwickelt wurden, von FBcl beherrscht. Vielmehr soll die FBcl als toolunabhängiges Programmierkonzept konzeptionell wirken. Beispielsweise sind Statemachines in FBcl zwar an die Harel-Statemachines der UML angelegt, haben aber zweckdienliche Sonderbedingungen. Die Simulink-Statemachines sind auch an die Harel-Statemachines angelegt, haben aber Sonderlösungen bei der Unterscheidung zwischen Message und Event. Es ist eine Schnittmenge zwischen Simulink und FBcl, die für die Funktionsgebung der Targetsoftware nutzbar ist.
Folgendes Bild zeigt rechts oben ein einfaches Simulink-Modell eines Differenzierers mit vorgelagertem User-FBlock. Wie in der Einleitung zu Chapter: 1 Grafische Programmierung im Embedded Bereich - Sinn und Ziel genannt, sollten diese wenig komplexen Dinge eher nicht grafisch programmiert werden sondern eher die Zusammenschaltung komplexer Module, aber an diesen einfachen Beispielen lassen sich die Prinzipien besser erklären.
Links ist der daraus generierte FBcl-Quelltext dargestellt. Dessen Parallelabbildung mit XML sieht dann nach Plazierung in dem IEC-61499-Grafiktool 4diac-IDE wie rechts unten gezeigt aus.
Die FBcl ist also wie hier ersichtlich eine textuelle Sprache, die eine grafische Topologie widerspiegelt. Dazu bedient sie sich der Syntax der IEC-61499-Norm.
In der textuellen Notation sind keine Grafikpositionen enthalten, diese sind auch nicht funktionsbestimmend. Inwieweit ein sogenannten Roundtrip - Rundumschlag aus FBcl-Generierung und nachfolgendes Zurückspielen in das Grafische Modell nach ggf. kleinen Änderungen unter Beibehaltung der Grafikpositionen gelinkt, ist dabei nicht ausgeschlossen, aber nicht Gegenstand jetziger Darstellungen.
Im folgenden Slide ebenfalls auf dem ASE-Kongress 2019 gezeigt, ist die Codegenerierung aus diesem FBcl-Code gezeigt:
Hinweis: Diese beiden Slides sind nur im Vortrag zusätzlich gezeigt worden und sind nicht Bestandteil der schriftlichen Unterlagen.
Zu erkennen ist links unten die 4diac-IDE-Grafik, gekennzeichnet mit Blöcken von lineraren Event-Folgen (Event-Chain). Diesen entsprechen C-Funktionen. Eine struct
-Definition enthält zu merkende Daten, der Programmierstil kann als Objektorientiert bezeichnet werden (Bindung der Operationen = C-Funktionen an die Daten).
Topic:.FBclDef.meta.
Folgendes Bild zeigt die Strategie, wie eine grafische Programmierung mit der textuellen Repräsentation in FBcl versioniert werden kann bzw. wie der Arbeitsablauf Modellierung, Versionierung, Code-Generierung aussehen kann:
Das Bild zeigt links oben in grün 'The user's model' in einem grafischen Editier-Tool, hier auch wieder Simulink erwähnt. Daraus wird die FBcl generiert, parallel dazu wird im Modelierungs-Tool getestet, dort auch Code generiert, zumindestens für den externen Test.
Für eine nachvollziehbare Versionierung von kundenausgelieferter Software soll aber nicht das Modell verwendet werden, sondern es wird der FBcl-Code versioniert. Aufgrund der textuellen Speicherung mit gleichbleibender Sortierung der Elemente ist es viel einfacher, einerseits eine Differenz-View-Darstellung zwischen Versionen zu bekommen, andererseits aus dem FBcl-Textcode nachvollziehbarer den Ziel-Code (in C) zu generieren oder beliebig wiederholt zu generieren. Wichtig dabei ist es, dass die wiederholte Generierung den gleichen Binärcode erzeugt, so dass eine Validierung erfolgen kann, ob denn der versionierte Softwarestand der Quellen genau dem ausgelieferten Softwarestand entspricht. Diese Binäre Vergleichbarkeit benötigt bei den meisten Target-Compilern allerdings einige Sachkenntnis-Kunst der Umgebungseinstellungen, da ansonsten in Objectfiles häufig umgebungsabhängige Metainformationen eingeschrieben werden, die letztlich binär nicht mehr vergleichbare Resultate erzeugen. Dies ist weniger bis nicht zu gebrauchen, ist aber zumeist abstellbar.
Das Slide zeigt rechts in grün auch die Einbindung von handgeschriebenen Quellen, deren Einbindbarkeit ist wesentlich.
Man kann nun bei kritischen Termin- und Änderungssituationen (beispielsweise muss sehr zeitnahe nur ein Parameter korrigiert werden) nur in den textuellen Files eingreifen und dies im nachhinein im Modell entsprechend unterbringen, im nachhinein dann verifizieren, dass der aus dem Modell generierte FBcl-Code wirklich stimmt.
Würde man nur im Modell ändern, dann muss gesichert sein, dass das Modell exakt dem damals ausgelieferten Kundenstand entspricht, dass alle Umgebungsbedingungen stimmen, dass die Generierung tatsächlich nur dieses eine Feature im Binary ändert. Das muss man gegebenenfalls auch dem Kunden nachweisen. Es sind aber auf diesem Weg einige Fallstricke vorhanden, wenn es sich um Software handelt, die nicht aktuell in Arbeit ist sondern aus dem Archiv geholt wird. Dazu gehört beispielsweise auch, dass die gleiche Toolversion benutzt werden sollte. Das wird man eigentlich nicht schaffen und muss daher umfangreiche teure Tests zur Verifizierung des gesamten neu gelieferten Binaries durchführen.
Die textuelle feingranulare Vergleichs- und Nachvollziehbarkeit hilft.
Topic:.FBclDef.txtlang.
Hierzu ist definiert, dass die IEC-61499 benutzt wird. Diese enthält alle notwendigen Elemente. Die Statemachine-Möglichkeiten werden aus heutiger Sicht mit der Standardisierungsrunde in 2020 nochmals erweitert.
Die IEC-61499 ist angepasst an den Anwendungszweck Automatisierungsgeräteprogrammierung weit gefächert. Bei den Functionblocks werden grundsätzlich drei Typen unterschieden:
Simple-FB: Dieser hat in der Regel genau einen Eventein- und Ausgang und enthält intern genau einen Algorithmus. Dieser ist nicht grafisch sondern in Structure Text notiert. Oft sind dort einfache kombinatorische Verknüpfungen der Eingänge zur Ausgangssignalbildung enthalten. Es kann auch Speichervariable geben. Folglich kann man zusätzlich zum Standard drei Simple-FB unterscheiden:
Simple-FB nur kombinatorisch Ein- auf Ausgänge: Die jeweiligen Expressions pro Ausgang (meist einer) werden direkt in den generierten Code integriert.
Simple-FB mit Zwischenspeicher und/oder Programmstruktur: Aus Structure Text wird C-Code generiert.
Simple-FB mit Sonderkennzeichnung (im ersten Comment): Es wird für die Simulation auf IEC-61499-Ebene der innere ST-Code verwendet, für die Target-Codegenerierung wird der gleichnamige C-Code manuell geschrieben vorausgesetzt (FB-Typ und Portnamen bestimmen die Namen der C-Funktionen). Das ist die Möglichkeit, bestehenden C-Code grafisch einzubinden aber in der Grafik als Black-Box zu behandeln, für den in der Grafiksimulation ein passender Ersatz geboten wird. Wichtig ist dies zum Beispiel für spezifische Harware- und Betriebssystemzugriffe im Target, die in der Simulation passend ersetzt werden. Auch in der Simulation kann abhängig vom Run-System der Simulation C-Code spezifisch eingebunden werden.
Grundsätzlich ist textuell bei den Algorithmen auch möglich weitere FunctionBlocks aufzurufen (Function call).
Basic-FB: Dieser enthält mehrere Algorithmen und eine Statemachine und meist mehrere Eventin- und output. Es ist hierüber auch möglich, einen FBlock zu haben der nur mehrere Algorithen (Operationen, Funktionen) enthält, die unabhängig möglicherweise auch in verschiedenen Abtastzeiten gerufen werden und die Funktionen nur in diesem einen FB bündeln. Auch hier gibt es die Möglichkeit, dass dieser FB im Target und/oder auch bei der Simulation von vorhandenen C-Routinen präsentiert wird.
Composite-FB: Das ist eine grafische Zusammenschaltung von Funktionblocks zu einem Modul, das sich nach außen wieder als Functionblock präsentiert.
Damit ist die FBcl mit IEC-61499-Norm nicht nur auf die grafische Repräsentation gerichtet sondern unterstützt die in Chapter: 1.6 Mischung grafischer und textueller Programmierung, Kerne in C beschriebenen textuelle Repräsentationen.
Zusätzlich zu den IEC-61499-definierten textuellen Schreibweisen gibt es einige Report-Formate, die beispielsweise innere
Verdrahtungen anders darstellen als die Verbindungslisten bei EVENT_
und DATA_CONNECTIONS
der Composite-FBlocks. Diese werden mit dem Einlesen und der Auswertung der FBlocks aus IEC-61499-konformer Darstellung gewonnen, stellen also
dort enthaltene aber nicht so gut sichtbare Informationen dar.
Topic:.FBclDef.Concept.
Dies sollte mit den weiteren Ausführungen in diesem Artikel erkennbar sein. Es geht nicht nur um eine andere Darstellungsweise von Inhalt, sondern um Herangehensweise.
Topic:.FBclDef.intern.
Dieser Artikel beschäftigt sich maßgebend mit den internen Darstellungen von Daten und Algorithmen für die Konvertierung aus anderen grafischen Darstellungen nach FBcl (nach IEC-61499-Codierung) und für die Codegenerierung nicht nur nach C oder C++.
Topic:.FBclDataModel.
Last changed: 2019-12-01
Hinweis: Diese Doku ist eine Vorab-Information. Die Konvertierung von Funktionsblockorientierten Darstellungen in die hier beschriebene Form und die Codegenerierung daraus ist ein Arbeitsstand und in Entwicklung. Die Quellen dafür sind nicht Open-Source, eine Entscheidung darüber wird zu gegebenem Zeitpunkt gefällt.
Dieses Kapitel beschreibt die Daten, wie sie als Functionblock-Connections-Abbild im Computerspeicher zur Verarbeitung vorliegen. Dieses Datenmodell ist einheitlich.
Die File-Speicherformen können unterschiedlich sein. Insbesondere erfolgt eine Konvertierung in dieses FBcl-Datenmodell aus File-Formaten verschiedener Tools wie beispielsweise Modelica-Modellfiles, Simulink-slx-Files oder Automatiserungsgeräteprojektierungen als Funktionsplan. Eine Konvertierung aus diesem Datenmodell erfolgt beispielsweise für C-Codegenerierung. Der IEC-61499 als Standard ist das offiziell erklärte Austauschformat von FBcl-Files, aus dieser Sicht jedoch auch 'nur eine' Form der Datenspeicherung.
Topic:.FBclDataModel.Type.
Der FBlock_Type_FBcl
srcJava_FBcl/org/vishia/fbcl/fblock/FBlock_Type_FBcl repräsentiert nach außen einen nutzbaren Funktionsblock, also entweder für die grafische Darstellung oder für den Aufruf
in der textuellen Programmierung. Jede Instanz eines FBlock_FBcl
referenziert sein Interface, das ist diese Typinformation. Eine gleichartige Typinstanz wird für jedes Modul aufgebaut, dass damit wieder als FBlock
instanziierbar ist. Das Verhältnis zwischen FBlock_FBcl
und dessen FBlock_Type_FBcl
kann wie eine Instanz zu einer Klasse aus UML-Sicht der FBlock-Anwendung gesehen werden.
Zu einem FBlock_Type_FBcl
gehört entweder ein Inhalt dieses Moduls, von Modul_FBcl
repräsentiert, oder es sind bereits generierte Targetsystem-Quellen vorhanden.
Die class FBlock_Type_FBcl
srcJava_FBcl/org/vishia/fbcl/fblock/FBlock_Type_FBcl enthält folgende Attribute und Referenzen:
Typename als String: Der Typename entspricht (weitgehend) dem in der Grafik verwendeten Namen. Ein Namespace gibt es nicht
(wie in vielen Grafik-Tools), was allerdings nicht zukunftsträchtig ist. Folglich muss der Typename sorgfältig gewählt sein.
Für einfache Operationen ist ein solcher Typename meist sehr kurz (F_ADD3
für ein Addierer mit 3 Eingängen) was nicht sehr sorgfältig ist, entspricht aber den Gepflogenheiten.
_kindFBlockType
dies wird nicht beständig sein, Art des Blockes, eine Klassifizierung.
mdl
: Hier wird das dem Typ zugehörige Innen-Modul Module_FBcl
referenziert. Diese Aggregation ist null
für Basistypen mit fester Funktionalität oder für bereits codegenerierte Library-Funktionspläne (Composite FBlock).
dtypes
: Dieser Container umfasst alle in den Pins vorkommenden DataTypeRef_FBcl
-Instanzen. Das Setzen mit bestimmten konkreten Datentypen erfolgt über die Pins, nach der Codegenerierung werden hierüber
wieder die originalen Datentypen (ANY_NUM
und dergleichen) wieder hergestellt.
Wesentlich für das Interface eines Moduls bzw. den FBlock_Type_FBcl
sind die Pins, die andere Informationen tragen als der Aufruf des FBlock_FBcl
. Es werden hier die Eigenschaften gespeichert
für die Codegenerierung. Diese gehören nicht zum FBlock_Type_FBcl als solches sondern zu den Pins, da ein Type mehrere unabhängige Operationen umfassen kann, auch in mehreren Event-Chains oder in mehreren Abarbeitungszeiten läuft.
für die Verbindung der Pins zu den anderen Pins des Typs, dazu zählt die Event-Datenpin-Zuordbubg, aber auch welche Dout-Pins
von Din-Pins beeinflusst werden und welche Evout zu Evin gehören. Diese Informationen sind in Operation_FBcl
srcJava_FBcl/org/vishia/fbcl/fblock/Operation_FBcl, im Bild rechts unten gespeichert.
Datentypen-Zuordnungen.
Das folgende Bild zeigt einen FBlock_Type_FBcl
aus Sicht eines instanziierten FBlock_FBcl
:
Die Instanz von FBlock_FBcl
referenziert über _typeFB
den Typ. Die Ein- und Ausgänge der FBlock-Instanz sind mit den Ein- und Ausgängen des Typs korreliert, ausgedrückt mit der
Strichellinie im ObjektModelDiagram. Die Zuordnung zwischen den Type-Pins und den Pins in einer FBlock_FBcl-Instanz ist über
den Index des jeweiligen Pins gegeben, gespeichert in PinBase_FBcl
. Es gibt keine Aggregation vom Pin auf den Type-Pin. sondern nur die Aggregation
FBlock_FBcl --> _type_FB --> Fblock_Type_FBcl
Im ObjectModelDiagram (OMD) sind die Basisklassen der Pin-KLassen mit angegeben. Hinweis: Das OMD (auch als Klassendiagramm bezeichnet) enthält die selben Klassen mehrfach gezeichnet, wenn diese verschiedene Rollen haben. Bei den Basisklassen (weiß) wurde dies aus Übersichtsgründen nicht so gekennzeichnet, es wird die eigentliche Klassenbeziehung der Vererbung oder Abstraktion gezeigt.
Für den FBlock_Type_FBcl
gibt vier Klassen der Pins, siehe folgend. Diese basieren alle auf Pin_Type_FBcl
srcJava_FBcl/org/vishia/fbcl/fblock/Pin_Type_FBcl, die lediglich die gemeinsame Basisklasse der Pins ist. Diese basiert auf PinBase_FBcl
srcJava_FBcl/org/vishia/fbcl/fblock/PinBase_FBcl, deren folgende Attribute auch für Pin_FBcl
bei der FBlock-Aufrufinstanz verwendet werden siehe Topic:.FBclDataModel.Pin.:
namePin
als Identifier-Zeichenkette.
ixPin
: Nummer des Pin beginnend ab 0.
kind
, die Art des Pins. Es wird hier zwischen Event und Daten und In/Out unterschieden. Bei Verbindungen wird dessen Korrektheit
geprüft.
Folgende Aggregationen (als Array implementiert) entsprechen im Typ den Pins eines FBlock:
EvinType_FBcl evinType
Input-Events.
EvoutType_FBcl evoutType
Output-Events.
DinType_FBcl dinType
Input-Daten.
Dout_FBcl doutType
Input-Daten.
Zusätzlich gibt es o, Bild rechts oben
EvinType_FBcl evop
: Dies sind Pseudo-Events, die dann notwendig sind, wenn entweder im Modul Verzweigungen von Events auftreten, die eigene
Operations erfordern oder sogar innerhalb gerufener FBlocks im Modul es interne Operations gibt, die auch im rufenden Modul
eine eigene Opertion erfordern. Diese evop
vom Typ EvinType_FBcl
sind so nicht direkt abgebildet von einem FBlock im Modul, (einem Pin_FBcl
eines FBlock), sondern es handelt sich um virtuelle Entsprechungen dieser internen Modul-events in der Typdarstellung.
Beide Daten-Basisklassen Dinout_FBcl
und DinoutType_FBcl
haben je eine Aggregation auf PinDtype_FBcl
(gelb im Diagramm) srcJava_FBcl/org/vishia/fbcl/fblock/PinDtype_FBcl. Diese enthält Datentypinformationen, siehe auch Chapter: 3.6 Datentypen.
Topic:.FBclDataModel.Type.evin.
Ein Event-Input eines FBlock_Type_FBcl
kann ein oder mehrere Operation_FBcl
haben. Das Event-Input wird repräsentiert von class EvinType_FBcl
srcJava_FBcl/org/vishia/fbcl/fblock/EvinType_FBcl basierend auf srcJava_FBcl/org/vishia/fbcl/fblock/EvinoutType_FBcl. Letztere referenziert in operations
keine oder beliebig viele Operation_FBcl
srcJava_FBcl/org/vishia/fbcl/fblock/Operation_FBcl.
Ein EvinType hat eine Operation, aber ohne Codegenerierung, wenn es sich um einen rein kombinatorischen FBlock handelt mit
direkter Bildung eines Outputs aus den Inputs. Dann enthält das DoutType_FBcl
die zugehörige Code-Generierregel, siehe Folgekapitel.
Ein EvinType hat eine Operation oder mehrere Operation je nach interner Eventverdrahtung als Reaktion auf ein empfangenes Event. Mit mehreren Operationen ist die Reaktion aufgeteilt. Es werden alle Operationen abgearbeitet, wenn deren Ausgänge verdrahtet sind, siehe Topic:.FBclCgen.operCond.. Dies ist Teil der Codegenerierung für das Zielsystem, die optimierend gestaltet ist: Nicht verwendete Ausgänge von gerufenen Modulen erzeugen keinen toten Code.
Die Operation_FBcl
enthält die Zuordnung aller In- und Outputs (event, data). Damit ist es möglich, vom EvinType_FBcl
auf alle beeinflussten und notwendigen Daten zu schließen. Dabei werden diejenigen Daten verzeichnet, die tatsächlich benötigt
und beeinflusst werden, nicht die formelle Zuordnung im Interface Event zu Daten. Der Unterschied ist: Letztere beschreiben,
welche Daten außen mit einem Event geliefert werden. Sie beschreiben nicht, ob sie innen entsprechend verarbeitet werden.
Wenn ein Datenpin intern verarbeitet wird, das nicht mit dem Event verbunden ist, dass die Verarbeitung anstoßt, dann wird
der Wert benutzt, der vom liefernden Event abgelegt wurde. Für die Codegenerierung wird für diesen Zweck eine Speichervariable
angelegt.
Die Operation ist mit der Codegenerier-Regel im Typ verankert. Der Code wird für das jeweiligen Event-Input der FBlock-Instanz dann nach der Regel für die Instanz generiert als Anweisung in der Operation, die der Eventchain des aufrufenden Moduls zugeordnet ist.
Topic:.FBclDataModel.Type.evout.
Einem EvoutType_FBcl
srcJava_FBcl/org/vishia/fbcl/fblock/EvoutType_FBcl eines Moduls kann ebenfalls eine Operation zugeordnet werden wenn dieses Event in einer Statemachine erzeugt wird. Die Operation
wird also bei der Generierung des Statemachine-Codes als Entry-Action in den State aufgerufen. Diese Operation wird allerdings
von der folgenden Verdrahtung bestimmt. Daher wird im Modul eine Funktion über eine Adresstabelle gerufen, in C++ oder Java
ist dies mit virtual
bzw. nicht final
Operations erledigbar, für C gibt es dafür ein extra Konstrukt. Im Code der Statemachine, der Bestandteil eines FBlocks ist
und mit den Input-Events abgearbeitet wird, wird also über den Sprungzeiger die Reaktion gerufen.
Die class EvoutType_FBcl
srcJava_FBcl/org/vishia/fbcl/fblock/EvinType_FBcl enthält eine Kennzeichnung, ob dies ein Ausgang einer Statemachine ist.
Topic:.FBclDataModel.Type.din.
Der Typ DinType_FBcl
srcJava_FBcl/org/vishia/fbcl/fblock/DinType_FBcl hat keine weiteren Datenlemente gegenüber seiner Basisklasse DInOut_FBcl
srcJava_FBcl/org/vishia/fbcl/fblock/DInOut_FBcl und ist daher eigentlich als Marker-class zu sehen. Beide Basisklassen DInOut_FBcl
und EvInOut_FBcl
enthalten referenzen gegeneinander dataPins
und associatedEventPins
. Das sind jeweils für Input und Output die nach der IEC-61499 geforderte Zuordnung zwischen Event und Daten. In dieser Norm
ist die Zuordnung in erster Linie dazu da, um bei der Aufruforganisation der FBlocks über eine EventQueue insbesondere auch
als Folge von Statemachine-Switches oder einer Eventkommunikation zwischen den Geräten einer verteilten Automatisierung die
notwendigen Daten zu den Events zu speichern. Über die Relation
EvInOut_FBcl --> dataPins --> DInOut_FBcl
wird festgestellt, welche Daten im Moment des Erzeugens eines Events (Einspeichern in die Queue oder Kommunikationsvorbereitung) zum Event gespeichert werden. Es werden die im Moment der Erzeugung des Events anstehenden Datenwerte dazu benutzt.
Diese Relation sagt nicht zwangsweise etwas darüber aus, dass die Ausgangsdaten auch gemeinsam mit dem Ausgangsevent im FBlock innen gebildet werden. Die Aussage betrifft, wie eben dargestellt, die Verwendung der Ausgangsdaten in der FBlock-Kommunikation. Häufig werden aber genau diese Daten bereitgestellt, nicht aber in jedem Fall. Es kann beispielsweise sein, dass gespeicherte Daten einer anderen Abtastzeit (folglich einer anderen Event-Kette) mit gesendet werden sollen, weil diese für die Eventverarbeitung wichtig sind.
Topic:.FBclDataModel.Type.dout.
Die class DoutType_FBcl
srcJava_FBcl/org/vishia/fbcl/fblock/DoutType_FBcl enthält mit der Assoziation genCodeAccess
die von der Codegenerierung Topic:.FBclCgen.operGen. erzeugten oder bei Standard-Fblocks vorgegebenen Zugriffscode-Generiervorschrift. Diese wird benutzt wenn der Wert des Output-Datenpins
in der Verarbeitung benötigt wird. Siehe Chapter: 3.5 Codegenerier-Regeln in Operation und Output-Datenpins.
Topic:.FBclDataModel.Type.oper.
Instanzen der class Operation_FBcl
srcJava_FBcl/org/vishia/fbcl/fblock/Operation_FBcl sind den Event-Inputs zugeordnet und werden von der zugehörigen Event-chain Chapter: 3.2.5 Event-Chain-Informationen referenziert. Sie enthalten in Bitmasken die Information, welche Output-Pins (sowohl Event als auch Data) von der jeweiligen
Event-Operation beeinflusst werden. Es gibt auch innere virtuelle EvInput_FBcl
die zu abhängigen Operationen gehören.
TODO Class-Diagramm hier.
Mehrere Operation_FBcl
in einem FBlock-Typ entsprechen unabhängigen Teilen dieser FBlock-Gesamtfunktionaltität. Die Teile werden aus IEC-61499-klassischer
Sicht als einzelne Event-Operationen zur Laufzeit abhängig von ankommenden Events unabhängig aufgerufen und werden bei der
optimierten Codegenerierung (siehe Chapter: 7 Codegenerierung aus FBcl) bedingt in verschienenen aufrufenden Operationen eingegliedert.
mTrgEvoutBits
: Unmittelbar von dieser Eventverarbeitung getriggerte Event-Outputs (typisch 1 oder kein Bit gesetzt). Das ist für kombinatorische
Logik die direkte Weiterverarbeitung des Events in der EventChain. Ist genau 1 Bit gesetzt, dann wird die EventChain Chapter: 6 Bildung von Operations aus den aufeinanderfolgenden Eventverbindungen mit der Verdrahtung des Output-Event auf einen anderen Eingang fortgesetzt. Sind mehr als 1 Bit gesetzt, dann wird ab diesem
Ausgang eine weitere Event-chain, damit eine neue Operation im aufrufenden Modul gebildet. Ist kein Bit gesetzt, ist die Event-Chain
des aufrufenden Moduls hier beendet. Die Event-Chain bestimmt die Anweisungen die für eine Operation im aufrufenden Modul
aus den Event-Operationen der Event-Inputs der Typen der FBlocks in der Chain generiert werden. Die Operation wird als Folge
von Events in der Instanziierung aufgerufen.
Ist in dieser Bitmask das höchste Bit mNecessaryEventOper
gesetzt dann wird diese Operation jedenfalls gerufen. Es handelt sich beispielsweise um eine interne Operation, die Aktionen
ausführt unabhängig von der weiteren Verdrahtung.
mTrgEvoutVirt
: Diese Events werden nicht unmittelbar getriggert sondern als Folge einer abhängigen Operation. Das gesetzte Bit bei einem
angeschossenem Event bewirkt, dass diese Operation jedenfalls aufgerufen wird, abhängig von deren Bits dann die weiteren abhängigen
Operationen.
mTrgStmEvoutBits
: Das sind die Event-Outputs, die möglicherweise aktiviert werden wenn die Operation ausgeführt wird, abhängig vom Zustand
einer damit getriggerten Statemachine. Sind alle Bits der Statemachine-Outputevents und auch alle Datenbits nicht verdrahtet,
dann wird die Statemachine in der konkreten Aufrufumgebung nicht verwendet, folglich auch nicht generiert
mDrivenDout
: Mit dieser Bitmask sind die von dieser Operation erzeugten Ausgangsdaten markiert. Im Unterschied zu der Relation des zugehörigen
Output-Events EvoutType_FBcl.dataPins
sind das die tatsächlich beeinflussten Daten, nicht diejenigen Daten die mit dem Output-Event gemeinsam gespeichert und versendet
werden. Häufig sind die mit beiden Systemen bezeichneten Data-Outputs identisch.
mDrivenDoutVirt
: Diese Outputs werden nicht unmittelbar gesetzt sondern als Folge einer abhängigen Operation. Das gesetzte Bit bei einem
angeschossenem Output bewirkt, dass diese Operation jedenfalls aufgerufen wird, abhängig von deren Bits dann die weiteren
abhängigen Operationen.
Ist keines dieser Bits gesetzt oder ist an den damit bezeichneten Ausgängen in der Verdrahtung der Instanz des FBlocks nichts angeschlossen, dann ist die Operation nicht notwendig und wird nicht generiert. Siehe Topic:.FBclCgen.operCond.. Es kann sein dass kein Event-Output verdrahtet ist, wohl aber werden die Daten benutzt. Folglich muss die zugehörige Operation aufgerufen werden. Gleiches gilt auch bei Eventverdrahtung aber nicht genutzten Daten-Outputs.
mUsedDinAll
: Diese Inputs sind insgesamt entweder von der Event-Operation direkt oder von einer Output-Access-Operation benutzt. Beim
Test der Einordnung dieser Funktionsblock-Teilfunktion in einem aufrufenden Modul wird festgestellt, ob die Inputs direkt
an Modul-Inputs hängen und damit Inputs der übergeordneten Operation der Event-chain sind, ob sie aus der selben Event-Chain
stammen entweder rein kombinatorisch oder über eine Zwischenvariable oder ob sie einer anderen Event-chain zugehören. Im letzten
Fall wird deren Wert aus einer Zwischenvariable entnommen und benötigt auch daher einen Referenz auf die Instanzdaten des
Moduls aus der aufrufenden Umgebung.
Die Bits dieser Bitmask sollten mit den zugeordneten Daten-Inputs des Event-Inputs möglichst übereinstimmen. Das ist aber nicht zwangsläufig so, siehe
mUsedDinEvOper
: Dies sind die Inputs, die unmittelbar für die Event-Operation verwendet werden. Ist diese Bitmask ==0
dann ist keine Event-Operation notwendig. Wenn lediglich das oberste Bit Operation_FBcl.mUsesThis
gesetzt ist, wird die Event-Operation mit dem Instanzzeiger ohne weitere Parameter gerufen.
Topic:.FBclDataModel.Modul.
Die class Module_FBcl
srcJava_FBcl/org/vishia/fbcl/fblock/Module_FBcl repräsentiert den Inhalt eines Moduls. Diese Daten werden beim Einlesen oder Konvertieren eines Moduls (z.Bsp. aus Simulink)
gebildet und sind relevant für die Codegenerierung dieses Moduls und für das Ausschreiben der FBcl-Textrepräsentation. Für
die Nutzung eines FBlocks als Typ mit einer bereits erfolgten Codegenerierung sind diese Daten nicht mehr relevant.
Ein Modul kann ein Composite-FB sein, also eine grafische Zusammenschaltung von FBlocks, aber auch ein Simple-FB mit einem einfachen textuellen Algorithmus in ST (Structure Text) oder ein Basic-FB mit einer Statemachine.
Die Unterscheidung ist im Datenmodell gegeben mit null
-Zeiger bzw. Besetzung des Zeigers auf:
networks
für Composite-FB
simpleFBalgm
für Simple-FB
mapAlgorithm
für Basic-FB (Map-Container mit Name als Key aller Algorithm
Topic:.FBclDataModel.Modul.FBlock.
Das obige Bild zeigt ein UML-Diagramm der Zusammenschaltung von Funktionsblöcken in einem Modul.
Hinweis zur Darstellungsart: Es handelt sich nicht um ein Objektdiagramm oder Instanzendiagramm. Die mehrfach dargestellten
selben Klassen im Klassendiagramm stellen vielmehr Rollen der instanziierbaren Klassen im Umfeld dar. So ist die class FBlock_FBcl
hier zweimal gezeichnet, um zu zeigen wie zwei mögliche Instanzen dieser Klasse über ihre auch in mehreren Rollen auftretenden
Pin_FBcl
-Instanzen verbindbar sind. Diese Art der Darstellung ist in der UML ausdrücklich zulässig, da in Diagrammen immer Ausschnitte
des Datenhaushalts den Erklärungen entsprechend enthalten sein können. Das Diagramm stellt nicht selbst den Datenhaushalt
dar (das Repository) sondern dient dem Zeigen der Zusammenhänge. Das ist grundsätzlich anders als beispielsweise ein Funktionsplan mit Funktionsblöcken
in einer grafischen Programmierung. Dort stellt jeder Funktionsblock eine Instanz dar.
Weiterer Hinweis: Es ist im folgenden Text häufig von 'Instanzen' bezüglich des FBlock_FBcl
die Rede, im Gegensatz zum Typ in FBlock_Type_FBcl
. Der Instanzbegriff ist nicht auf die Klassendiagramm-Darstellung bezogen sondern auf die Sachlage: Ein FBlock_FBcl
stellt in der Sache eine Instanziierung bzw. den Aufruf eines Funktionsblocks in der grafischen Programmierung dar, dessen
Typ in Instanzen von FBlock_Type_FBcl
beschrieben ist.
Die class FBlock_FBcl
stellt einen Aufruf eines Funktionsblocks dar. Dieser hat im wesentlichen folgende Attribute und Assoziationen, siehe auch
srcJava_FBcl/org/vishia/fbcl/fblock/FBlock_FBcl
Name der Blockinstanz im Modul (String)
Referenz zum Typ, class FBlock_Type_FBcl
siehe auch Chapter: 3.1 Information im FBlock-Typ zu einem FBlock bzw. Interface zu einem Modul.
Der FBlock_FBcl kennt insbesondere vier Arten von Verbindungen zu seinen Nachbarblöcken im Modul, die alle mit einer Instanz
von Pin_FBcl
, siehe srcJava_FBcl/org/vishia/fbcl/fblock/Pin_FBcl realisiert sind:
din
: Data-Inputs
dout
: Data-Output
evin
: Event-Inputs
evout
: Event-Outputs
Jede dieser Aggregationen kann beliebig viele Pin_FBcl
refererenzieren, wobei die maximale Anzahl auf 62 festgelegt ist. Diese Begrenzung resultiert aus Bitmask-Worten im long-Format
(64 bit) an einigen Stellen. Die Begrenzung auf 62 ist reichlich, wenn man bedenkt dass FBlocks auch grafisch dargestellt
werden sollen. (32 wäre ggf. zuwenig).
Die Datenverbindungen din
und dout
sind typisiert wobei die Typfestlegung aus Sicht eines Submoduls noch offen sein kann (nach IEC-61499-Standard beispielsweise
ANY_MAGNITUDE
) und im Gesamtverband eines Modulaufrufes dann vor der Codegenerierung festgelegt ist. Eine Datenverbindung kann auch ein
komplexer Typ sein, wobei nach IEC-61499 dann alle Daten des Typs in einer Message übertragen und im FBlock gespeichert werden.
Es ist aber auch möglich, eine Referenz zu liefern und damit direkt mit Daten anderer FBlocks zu arbeiten, wie dies bei den
Assoziationen der Objektorientierung gebräuchlich ist.
Die Eventverbindungen sind aus Sicht des FBlock-Aufrufs und der IEC-61499-Norm mit der Datenverbindung gekoppelt: Mit dem Event empfängt der FBlock die dem Event zugeordneten Daten. Diese Herangehensweise entspricht ebenfalls der Objektorientierung, die ein Paradigma der Eventkopplung der Instanzen kennt, in aktuellen objektorientierten Sprachen oft nicht nativ unterstützt, aber auch in der UML durchaus präsent. Die IEC-61499 baut direkt auf diesen Eventkopplungsgedanken auf.
Die Eventverbindung kann aber auch als Abarbeitungsreihenfolge gesehen werden. Mit einer spezifisch manuell festgelegten Eventreihenfolge kann beispielsweise eine parallele Bearbeitung von Teilalgorithmen für Multicore-Plattformen formuliert werden, besser als in zeilenorientierter Programmierung.
Die Eventkopplung ist in der IEC-61499 insbesondere auch für die Verteilung von Funktionalitäten auf verschiedene Automatisierungsgeräte oder auch Prozesse auf einem Gerät gedacht. Dann bedeutet die Eventkopplung eine Kommunikationsverbindung, wobei die Codegenerierung für die Zielplattform die Zusammenfassung von Daten passend aus einzelnen Eventkopplungen realisiert. Beispiele dafür gibt es aus der IEC-61499-Anwendungspraxis. Wenn FBlocks im Datenmodell nebeneinander stehen bzw. direkt gekoppelt sind, dann bedeutet dies für das Deployment der Funktionalität also nicht, dass diese FBlocks auch im selben Speicherbereich laufen müssen. Das ist für die Auswertung der Daten insbesondere für eine Codegenerierung zu beachten.
Topic:.FBclDataModel.Modul.Pin.
Eine Instanz von Pin_FBcl
stellt einen Anschluss dar. Das Wort 'Pin' ist aus seinem Gebrauch für einen Schaltkreis (IC) abgeleitet, denn es bedeutet
eigentlich 'Nadel'. Folglich ist ein Pin ein Connection-Point.
Die class Pin_FBcl
srcJava_FBcl/org/vishia/fbcl/fblock/Pin_FBcl ist gleichermaßen für alle vier Verbindungsarten verwendet, da eigentlich keine wesentlichen Unterschiede bestehen. Pin_FBcl
hat eine Basisklasse PinBase_FBcl
srcJava_FBcl/org/vishia/fbcl/fblock/PinBase_FBcl, die auch bei PinType_FBcl
verwendet wird, siehe Chapter: 3.1 Information im FBlock-Typ zu einem FBlock bzw. Interface zu einem Modul. Diese Basisklasse enthält folgende Attribute/referenzierte Elemente:
Name des Pin als Identifier-Zeichenkette. Falls in der grafischen Programmierung kein Identifier angegeben wurde, dann wird aus der dortigen Angabe ein Identifier gebildet und hier abgespeichert.
ixPin
: Nummer des Pin beginnend ab 0. Dieser Index ist wesentlich für die Pinzuordnung eines Ports zum äußeren Anschluss, aber
auch für Pin-Selektionen über Bitmasken, siehe Chapter: 3.1.5 Operation_FBcl - Info welche Pins beeinflusst werden. Die Nummer sollte identisch mit der Reihenfolge in der Grafikpräsentation sein.
Datentyp als Referenz zu DataTypeRef_FBcl
. Mehrere Pins referenzieren die selbe Instanz, wenn der Datentyp identisch ist (sein muss bei verbundenen Pins). Da in DataTypeRef_FBcl
der Datentyp selbst wiederum referenziert wird, ist hierüber der Austausch eines konkreten Implementierungstyps möglich,
siehe auch TODO. Der Datentyp eines Datenpins muss festgelegt sein, wobei Typen wie ANY_NUM
und dergleichen (IEC-61499 und IEC-61131) möglich ist. Die Referenz auf den Datentyp bleibt leer (null
) bei Eventpins.
stepTime
: Referenz auf eine Abarbeitungszeit. Diese Information ist nicht (mehr) notwendig bei einer bestehenden Eventverbindung.
Sie dient aber zur automatischen Erstellung von Eventverbindungen. Alle direkt in einer Event-Kette (EventChain) verbundenen
FBlocks müssen die selbe stepTime
haben.
kind
, die Art des Pins. Es wird hier zwischen Event und Daten und In/Out unterschieden. Bei Verbindungen wird dessen Korrektheit
geprüft.
Diese Informationen sind gleichfalls in einem Pin_Type_FBcl
präsent und notwendig, siehe Chapter: 3.1 Information im FBlock-Typ zu einem FBlock bzw. Interface zu einem Modul
Die eigentliche class Pin_FBcl
enthält die folgenden Elemente (Attribute und Referenzen):
connect
: Das ist eine Aggregation mit beliebig vielen Elementen (als java.util.List
, eine Containerklasse. Auch wenn ein Dateninput nur genau eine Quelle haben darf (mit einem Pin_FBcl
( mit einer Instanz eines Output-Pins verbunden), wird auch hierfür einheitlich diese Liste verwendet, dann nur mit einem
Element besetzt. Event-Inputs dürfen mehrere Quellen haben.
fb
: Aggregation auf den zugehörigen FBlock_FBcl
, notwendig um über Verbindungen zu traversieren.
In der abgleiteten Din_FBcl
srcJava_FBcl/org/vishia/fbcl/fblock/Din_FBcl für Date-Inputs ist zusätzlich enthalten:
sConstant
: Ein String. Bei Input-Pins (din
) ist dieser besetzt wenn das Pin mit einem konstantem Wert belegt ist. Die Zeichenkette ist so angegeben wie in der grafischen
bzw. zugehörigen textuellen Repräsentation geschrieben. Bei der Codegenerierung wird dann geeignet konvertiert (beispielsweise
ein 'F' angehangen wenn es eine Float-Konstante für C-Programmierung darstellt).
Inwieweit dieser Const-Wert eine Expression darstellen darf, ist eine Detailfrage. Wenn in einer grafischen Repräsentation eine Variable am Eingang vermerkt wird (Beispiel Modul-Input), dann wird dies allerdings in eine Pin-Verdrahtung aufgelöst. An dieser Stelle kann aber vermerkt sein, dass eine Repräsentation nicht die Verdrahtung zeigen soll sondern die Variable.
In der abgleiteten Dout_FBcl
srcJava_FBcl/org/vishia/fbcl/fblock/Dout_FBcl für Date-Outputs sind zusätzliche Elemente für die Bildung der Event-chain enthalten, siehe Javadoc.
Topic:.FBclDataModel.Modul.Modul.
Das Bild ist eine Wiederholung aus den Vorabschnitten:
Das obige Bild zeigt ein UML-Diagramm der Zusammenschaltung von Funktionsblöcken in einem Modul.
Die class Module_FBcl
srcJava_FBcl/org/vishia/fbcl/fblock/Module_FBcl ist die Organisationsklasse für ein Modul bestehend aus Funktionsblöcken. Andere Bezeichnungen für ein solches Modul sind
Funktionsplan, Subsystem (in Simulink gebräuchlich) oder der Inhalt eines Composite FunctionBlock (in IEC-61499 oder im 4diac-Editor).
Ein Funktionsplan ist in der IEC-61131-3 in Netzwerke eingeteilt. Daher ist unterhalb Module_FBcl
die class Network_FBcl
angeordent. Die Netzwerk-Einteilung gibt es in der IEC-61499-Norm so nicht. Aber sie ist zweckmäßig dann wenn ein komplexes
Modul etwa mehrere Seiten umfasst. Die Zuordnung der FBlocks zu den Netzwerken im Modul soll dabei so organisiert sein, dass
in der selben Abarbeitungszeitscheibe die FBlocks in den Netzwerke in der Netzreihenfolge abgearbeitet werden. Das bedeutet,
eine Eventverdrahtung darf nur vorwärts zu einem folgenden Netzwerk erfolgen, nicht rückwärts. Ein Netzwerk darf aber insbesondere
ein anderes Input-Event des Moduls verarbeiten. Diese Festlegung ist insoweit zweckmäßig, weil bei einer Seiten- (Blatt-)
-zuordnung der grafischen Darstellung zu den Netzwerken der Betrachter von dieser Abarbeitungsreihenfolge ausgehen kann. Daten-Connections
dürfen rückwärts auftreten, es handelt sich dabei aber immer um gespeicherte Daten der vorigen Abarbeitung. Der Datenfluss
selbst ist in Netzwerkreihenfolge.
Ein Netzwerk kennt nun seine FBlocks, Instanzen von FBlock_FBcl
und ihre Verdrahtung über die Pin_FBcl
.
Die class FBlock_Type_FBcl
ist der Repräsentant eines Typs eines FBlock. Alle Basistypen (AND, ADD etc) sind ebenfalls mit einem FBlock_Type_FBcl
vertreten, siehe Chapter: 3.1 Information im FBlock-Typ zu einem FBlock bzw. Interface zu einem Modul.
Ein FBlock-Typ kann wie folgt vorliegen:
1) Ein Basis-FBlock, dessen Funktionalität direkt mit Algorithmen für die Codegenerierung beschrieben ist und dessen Funktionalität allgemein bekannt ist.
2) Ein implementierungsspezifischer FBlock, dessen Funktionalität für das Zielsystem direkt mit einem Funktionsaufruf beispielsweise im C-Code belegt ist und der in der Entwicklungs-IDE passend emuliert ist (möglicherweise nicht identisch, möglicherweise leer als Dummy). Beispiele dafür sind Hardwarezugriffe auf Signale, die in der IDE simuliert werden, oder Kommunikationsschnittstellen, die für Zielsystem und IDE im Test verschieden implementiert werden.
3) Ein FBlock, der in der IDE mit der IDE-spezifischen textuellen Sprache programmiert ist. Für die IEC-61499 wäre das ST (Structure Text), für Simulink wäre das Matlab-Code. Es ist dabei abzuwägen, ob eine Konvertierung auf die für die FBcl-Speicherung verwendete IEC-61499 erfolgt.
4) Ein FBlock, der im inneren wieder mit dem im Bild gezeigten Netzwerk(en) von FBlocks programmiert ist. Wenn dieses Submodul
dann übersetzt für das Zielsystem ist, brauchen die Daten des FBcl-Datenmodells nicht weiter aufgehoben werden. Dieses Modul
mit seinem FBlock_Type_FBcl
ist dann wie 2) zu betrachten. Allerdings muss der Innenaufbau des Moduls dann aufgehoben werden, wenn die Implementierung
mit verschiedenen Datentypen, bestimmt von der äußeren Beschaltung erfolgt. Dann muss eine Neuübersetzung mit dem gewählten
Datentyp erfolgen, wenn nicht schon vorhanden. Das betrifft insbesondere Module aus einer Library.
Topic:.FBclDataModel.Modul.Port.
Im Bild im Vorkapitel sind Dout_FBcl
...usw. -Instanzen direkt vom Modul referenziert dargestellt, mit:
evinPort
: Input-Eventport
evoutPort
: Output-Eventport
dinPort
: Input-Datenport
doutPort
: Output-Datenport
Da die ...inPort
s jeweils nach einem Input eines FBlocks verdrahtet sind, müssen sie vom Typ ...out_FBcl
sein, und umgekehrt.
Diesen Port-Instanzen sind Instanzen referenziert vom Interface, FBlock_Type_FBcl
zugeordnet:
evinPin
: Input-Eventpin
evoutPin
: Output-Eventpin
dinPin
: Input-Datapin
doutPin
: Output-Datapin
Die Zuordnung ist über den ixPin, die Pin-Nummer in der Pin- und Port-Instanz geregelt.
Topic:.FBclDataModel.Modul.Evchain.
Die EventChain srcJava_FBcl/org/vishia/fbcl/fblock/Evchain_FBcl ist die fortlaufende unverzweigte Aufeinanderfolge von Event-Verbindungen von Funktionsblöcken in einem Modul. Die zugehörigen Event-Operationen sollen nacheinander abgearbeited werden. Die EventChains werden immer nach dem Einlesen des Moduls aus den inneren Moduldaten, also aus der Zusammenschaltung der FBlocks im Modul, gebildet. Sie sind vom Modul Chapter: 3.2 Modul-Inhalt eines Composite-FB über srcJava_FBcl/org/vishia/fbcl/fblock/Module_FBcl#evChains referenziert.
Eine Event-chain beginnt an den Event-Inputs des Moduls. Die aggregierte srcJava_FBcl/org/vishia/fbcl/fblock/Operation_FBcl ist die zugehörige Event-Operation, siehe Chapter: 3.1.5 Operation_FBcl - Info welche Pins beeinflusst werden. Das ist die selbe Operation, die auch dem EvinPin zugeordnet ist: srcJava_FBcl/org/vishia/fbcl/fblock/EvinType_FBcl#operations. Einem EvinPin kann mehr als eine Operation zugeordnet sein, wenn die Eventverdrahtung gleich an Eingang aufgegabelt ist. Dann gibt es entsprechend mehrere Event-Chains für dieses EvinPin.
Event-chains können aber auch innerhalb des Moduls beginnen, oder sogar innerhalb von Funktionsblöcken im Modul. Das sind Abfolgen von Funktionalitäten, die im Modul bedingt aufgerufen werden wenn eine Eventabfolge gabelt (fork) oder wenn eine Event-chain an einem Event-Output-Pin beginnt das nicht von außen sondern von einer Statemachine eines inneren FBlock getriggert wird. Wie innere Event-Chain gebildet werden ist in Chapter: 6.3 Algorithmus zur Bildung von EventChains in einem Modul und Chapter: 6.6 Abhängige Operationen beschrieben.
Die Elemente einer Evchain_FBcl
srcJava_FBcl/org/vishia/fbcl/fblock/Evchain_FBcl#evList referenziert Event-Eingänge von FBlocks, die unmittelbar ohne Verzweigung in einer Eventkette liegen und folglich nacheinander
abgearbeitet werden sollen. Die EvChain
referenziert die Event-Inputs von FBlock_FBcl
-Instanzen des Moduls (Typ Evin_FBcl
) zusammen mit der zugehörigen Operation_FBcl des Types des FBlocks. Beide Referenzen sind in je einem Element srcJava_FBcl/org/vishia/fbcl/fblock/Evchain_FBcl.EvCurr zusammengefasst. Beide Informationen sind notwendig, denn das evin des FBlocks kann im Module des FBlocks auch wieder auf
mehrere EventChain verzweigen, folglich mehrere srcJava_FBcl/org/vishia/fbcl/fblock/EvinType_FBcl#operations haben. Welche die betreffende ist, ist hier direkt aggregiert. Über das evin
ist der FBlock im Modul spezifiziert, wie im UML-Diagram erkennbar.
Zur Farbgebung im Diagramm: Grün sind die Teile, die von außen einem FBlock-Typ zugeordnet sind (links oben) und von diesem referenziert werden. Sie sind
also sichtbar, wenn von einem FBlock die Typinformation abgerufen wird. Dazu gehören selbstverständlich die Pin- Ein- und Ausgänge, aber auch die damit verbundenen Operationen. Letztere enthalten im Element srcJava_FBcl/org/vishia/fbcl/fblock/Operation_FBcl#genCodeStmnt die Generierregel für den Zugriff auf die FBlock-Operation für die Zielcodegenerierung. Dabei werden von den doutPin
über DoutType_FBcl#operDout auch Zugriffsfunktionen auf Ausgänge angeboten. EvinType_FBcl#operations sind ein oder mehrere Operationen die mit dem Event abgearbeitet (im code als Statements generiert) werden sollen.
Die türkisblauen Teile sind Objekte, die zum Inneren des Moduls gehören, also für die Codegenerierung der Operationen benutzt werden, die von außen aufgerufen werden.
Beim Aufruf trifft man wiederum auf die Typ-Informationn der FBlocks, die in einem anderen Grün (Limone) dargestellt sind. Man ist also wieder bei gleichen Objekten, aber eine Ebene tiefer, dem Submodul zugeordnet.
Die EventChain ist aufgeteilt. Der vordere Teil kann Statements erzeugen, wenn die betreffenden evin der FBlocks dies vorsehen. Insbesondere Basisfunktions-FBlocks sind allerdings nur für eine kombinatorische Verknüpfung zuständig, auch anwenderdefinierte FBlocks können rein kombinatorisch sein (Häufig als SimpleFBlock in StructureText geschrieben). Dann wird der Wert der Kombinatorik als Output-Operation berechnet, wenn auf den Ausgang zugegriffen wird. Damit besteht ein hinterer Teil der EventChain möglicherweise aus einem oder mehreren kombinatorischen Anteilen. Die EventChain ist immer in diesem Sinn zweigeteilt. Der anweisungserzeugende Teil ist eine Einheit, kann aber insgesamt entfallen, wenn nur Kombinatorik vorliegt. Die kombinatorischen Teile können aus mehreren Operationen bestehen.
Das folgende Bild zeigt solche Verhältnisse:
Es handelt sich zwar nur um eine kombinatorische Verknüpfung, die aber einen Ausgang mehrfach benutzt für mehrere Operationen. Dieser Wert darf nur einmal berechnet werden (ein anderer FB-Typ könnte Nebenwirkungen bei mehrfachem Aufruf haben). Folglich ist der Teil der EventChain über Ya1 statementerzeugend:
/**The event input operation */ inline void ev_a_Testcg_Fork1 ( Testcg_CombinSimple* thiz, float a, float b ) { thiz->Ya1 = (a + b); //setInstanceDataFromDout }
Die anderen beiden FB stellen unabhänig Verknüpfungen (Kombinatorik) dar und sind in zwei Zugriffsoperationen auf die Ausgänge C-Code-generiert. Auch der Zugriff auf y1 ist eine Zugriffsfunktion, da der FB keine Variable außen setzt:
/**get the output value */ inline float y1_Testcg_Fork1 ( Testcg_CombinSimple* thiz ) { return thiz->Ya1; } /**get the output value */ inline float y2_Testcg_Fork1 ( Testcg_CombinSimple* thiz, float c ) { return (thiz->Ya1 * c); } /**get the output value */ inline float y3_Testcg_Fork1 ( Testcg_CombinSimple* thiz ) { return (thiz->Ya1 * 1.1f); }
==>4.3 Ausgabe der Daten des gelesenen Datenmodells für Testzwecke enthält Hinweise, wie die Daten einer EventChain als Zwischenreport nach Einlesen eines FBcl-Files präsentiert werden.
==>4.3 Ausgabe der Daten des gelesenen Datenmodells für Testzwecke beschreibt wie und warum die EventChains gebildet werden.
==>7.3 Codegenerierung mit Abarbeitung der EventChain beschreibt wie die Codegenerierung aus den EventChains abläuft.
Topic:.FBclDataModel.ModuleBasicFB.
Ein Basic-FBlock ist laut IEC-61499 ein FBlock mit einer Statemaschine und mehreren zugehörigen Algorithmen, die original in ST (Structure text) notiert werden.
Topic:.FBclDataModel.ModuleBasicFB.BasicFBOnlyOps.
Mit einer einfachen Statemaschine kommt man zu einer faktisch stateless Realisierung, die aber im Basic-FB mehreren Events jeweils genau eine Operation und ein Ausgangsevent zuordnet.
Die Statemaschine ist nur dazu da, den Input-Events entsprechende Output-Events und Operations zuzuordnen. Die States sind temporär.
Diese Variante entspricht der Objektorientierten Klasse (FBlock-Typ ist eine Klasse) mit mehreren Operationen und gemeinsamen Daten, die für FBlocks aus IEC61131 so nicht bekannt ist. Dort gibt es pro FBlock immer nur genau einen Algorithmus.
Topic:.FBclDataModel.ModuleBasicFB.SFunc.
Der Begriff SFunctions =^ System Functions ist Simulink entlehnt. Sie heißen dort so, weil ihr Inhalt nicht auf Anwender (Grafik-) Ebene definiert ist sondern auf einer gedachten System-Ebene außerhalb der grafischen Anwendung. In Simulink werden solche SFunctions entweder mit MATLAB-Code gefüllt oder in der Sprache der Codegenerierung, das ist meist C, realisiert.
Der Begriff SFunctions ist nicht unbedingt stimmig. Er drückt aus, dass man eigentlich grafisch programmiert und dabei System Funktionen benutzt, die auch textuell definiert sein können. Entgegen dieser wenn man so will 'Grundhaltung' kann man aber auch bewusst einen Programmierstil der Mischung von Grafik und Text leben: Einfache überschaubare Dinge werden besser mit den bekannten textuellen Programmiersprachen beschrieben.
Häufig und zweckmäßig werden Basisfunktionen oder einfache FBlocks in diesem Sinne auch direkt in der Ziel-Programmiersprache geschrieben, also in C oder C++. Für solche FBlocks könnte man besser den Begriff Predefined FBlocks verwenden, mit dem Blick aus der dafür nicht notwendigen Codegenerierung heraus. Für eine sichere Zielsystem-Codegestaltung ist dies eine zweckdienliche Herangehensweise, da so spezifische Dinge ohne Umwege formuliert werden können.
Die Basic-FB aus IEC61499 mit einem Event ohne States 3.3.1 Basic-FB mit einer Operation pro Event ohne States eignen sich nun für solche SFunctions mit Inhalt entweder pre-defined in C/++ oder mit Algorithmen in ST. Jeder Operation, die im Statechart definiert wird, entspricht eine predefined Operation im Zielcode. Die Zuordnung der Inputs und Outputs ist mit der Eventzuordnung zu Daten gegeben.
Topic:.FBclDataModel.ModuleBasicFB.SFunc.SFuncIntern.
Aus dem Gedanken heraus, dass grafische Programmierung portabel sein soll, ist es nun zweckmäßig, C-Algorithmen so zu schreiben, dass sie sowohl in Simulink-SFunctions verpackt werden können, als auch in die Basic-FBlock ohne States von IEC-61499.
Dazu müssen Eigenheiten, die vom vorhandenen Simulink determiniert werden, berücksichtigt werden:
Mehrere Operationen: Bei Simulink kann eine SFunction mehrere Abtastzeiten haben, nur so hat sie auch mehrere Operationen. Den Abtastzeiten entsprechen in IEC-61499 die Events. Folglich sind wie bei Simulink mehrere Operationen möglich.
Mehrere Operationen in meheren FBlocks ? In Simulink wurde mit dem Generator für SFunctions aus Headerfiles ../../smlk/html/Smlk_C_ObjO.html die Möglichkeit geschaffen, aus mehreren Operationen ein Verbund von Object-FB mit Operation-FB zu schaffen. Das ist für die Gestaltung der grafischen Programmierung an sich sehr gut,. da damit Operationen in der Grafik verteilt werden können. Dies gelingt in 4diac nun nicht, da die Möglichkeit der gemeinsamen Datenhaltung zwischen mehreren FBlocks nicht besteht, jedenfalls solange auf Kompatibilität zu bestehenden Abarbeitungslösungen gesetzt wird. Dafür kann aber ein FBlock die notwendigen mehrere Input-Events haben um alle Operationen umzusetzen, was bei Simulink nicht gelingt, da dort die Unterscheidung nur aufgrund der Abtastzeit erfolgt. Damit hat man die selben Codes in C für die Basis-Operationen, aber verschiedene Realisierungen der Grafik-Programmierung.
Inputs und Outputs: In Simulink sind nur Daten-Connections vorhanden. Diese sind auf die Abtastzeiten zuordenbar, wobei auch Eingänge für alle Abtastzeiten definiert werden können. In IEC-61499 entspricht das der Datenzuordnung für die Events.
Outputs: In Simulink muss einem Output einer Operation außen eine Variable zugeordnet werden, folglich muss die Operation einen Zeiger auf die Variable als Argument bekommen. Eine Lösung der Rückgabe genau eines Wertes über return wurde bislang noch nicht realisiert, würde auch nur für einen Wert funktionieren. Das ist an sich ungünstig, da sich damit ein doppelter Speicherplatz ergibt. RAM ist häufig für Embedded Control nicht üppig. In Simulink ist dies nötig, weil die Infrastruktur eines gerufenen FBlocks in der aufrufenden Umgebung keine Rolle spielen soll, aus mehreren Gründen (Optimierungsmöglichkeit des Simulink-Coder über flatten, verschiedene Optionen bezüglich reentrance). Optimaler für den Embedded Code ist der Zugriff auf Elemente des gerufenen FBlocks, in dem berechnete Werte gespeichert sind. Das setzt voraus, dass die Codegenerierung passend zur Codegestaltung der SFunction realisiert ist. Bei FBcl-Codegenerierung aus IEC-61499 ist das möglich.
Topic:.FBclDataModel.ModulSimpleFB.
Die class Module_FBcl
srcJava_FBcl/org/vishia/fbcl/fblock/Module_FBcl repräsentiert den Inhalt eines Moduls. Diese Daten werden beim Einlesen oder Konvertieren eines Moduls (z.Bsp. aus Simulink)
gebildet und sind relevant für die Codegenerierung dieses Moduls und für das Ausschreiben der FBcl-Textrepräsentation. Für
die Nutzung eines FBlocks als Typ mit einer bereits erfolgten Codegenerierung sind diese Daten nicht mehr relevant.
Ein Modul als Simple-FB enthält einen einfachen textuellen Algorithmus in ST (Structure Text)
Die Unterscheidung ist im Datenmodell gegeben mit null
-Zeiger bzw. Besetzung des Zeigers auf:
networks
für Composite-FB
simpleFBalgm
für Simple-FB
mapAlgorithm
für Basic-FB (Map-Container mit Name als Key aller Algorithm
Die Assoziation simpleFBalgm
referenziert einen Algorithm_FBcl
srcJava_FBcl/org/vishia/fbcl/fblock/Algorithm_FBcl. Dieser enthält im wesentlichen Statement_FBcl
srcJava_FBcl/org/vishia/fbcl/fblock/Statement_FBcl, die den Algorithmus abbilden. Dieser ist hier nicht mehr als Structure Text spezifiziert, sondern in einer allgemeinen Form mit Revers Polish Notation für Expressions gespeichert. Folglich kann in diesem Datenmodell auch ein Algorithmus aus einer anderen Sprache entsprechend
geparst abgebildet werden.
Topic:.FBclDataModel.ModulSimpleFB.stmnt.
Ein Statement_FBcl
srcJava_FBcl/org/vishia/fbcl/fblock/Statement_FBcl bildet mögliche Statements allgemein ab. Wichtig ist dass die Statements nach Structure Text konvertiert werden können, auch wenn sie aus einer anderen Sprache erzeugt worden sind. Das ist wichtig, da als Datenspeicherform
der FBcl IEC-61499 gewählt wurde.
leftVariable
: Ein Statement kann eine typische Variable haben, die als leftVariable
(bei Assignments als solche verwendet) bezeichnet ist. Für eine for
-Anweisung ist das die Laufvariable. Bei if
und dergleichen wird diese nicht benutzt.
mUsedDin
, mUsedDout
, mUsedLocal
: Diese Bitmask-Werte enthalten die Information, welche Input, Output und Lokale Variable benutzt wurden. Das ist wesentlich
für die Aufrufargumente der Codegenerierung. Wie die Benutzung erfolgt, steht in den Expressions.
expr
vom Typ srcJava_Zbnf/org/vishia/execode/Execode: Ein Statement hat meist einen Ausdruck (Expression), der nach Berechnung in der Ausführung einen Wert repräsentiert. Bei
der Zuweisung ist das der rechte Teil nach dem =
. Bei if
oder while
ist dies die Bedingung. Bei for
ist dies ebenfalls die Abbruchbedingung, die Anfangswertzuweisung erfolgt mit einem extra Statement.
subStmnts
: Diese Liste ist belegt wenn es sich um ein Control-Statment handelt. Die Programmstruktur wird also als Tree (Baum) gespeichert,
wie es auch der Strukturierung des Programms entspricht. Im Gegensatz dazu ist etwa bei Java-Bytecode die Struktur nicht so
erkennbar, es werden im auszuführenden Bytecode mit Goto gearbeitet.
Topic:.FBclDataModel.ModulSimpleFB.expr.
Die class Execode
ist allgemein als Opensource in srcJava_Zbnf/org/vishia/execode/Execode definiert, da auch mehrfach verwendet. Da sie hier essentiell ist, erfolgt eine kurze Beschreibung.
Die https://de.wikipedia.org/wiki/Umgekehrte_polnische_Notation (RPN) sollte bekannt sein. Wichtig ist, dass eine 'normale' Schreibweise mit Vorrangregeln der Operatoren und Klammern sich vom Parsing-Prozess eindeutig in die RPN transformieren lassen, aber auch die Rücktransformation in die 'normale' Schreibweise, die die nötigen Klammern erzeugt, ist möglich. Damit kann ein Expression nicht nur für die Abarbeitung, die in RPN-Schreibweise schnell realisierbar ist, sondern auch für die Speicherung einer Expression für spätere Codegenerierung verwendet werden.
Eine Speicherung in einem anderen Format, etwa einem Baum aus binären Ausdrücken unterstellt, dass der vom parser abgelieferte Baum in den Vorrangregeln der Operatoren auch die Zielsprache entspricht. Das ist jedoch nicht immer gegeben. Die RPN hat keine Vorrangregeln sondern nur eine Abarbeitungsreihenfolge. Die Vorrangregeln könenn bei der Erzeugung eines 'normalen' Ausdruckes aus der RPN anders gesetzt werden als bei der Syntax des Parsers. Mit 'normaler' Ausdruck ist die Schreibweise in den gängigen Programmiersprachen gemeint.
Die Speicherung der RPN in Execode
fasst immer einen Operanden mit der nachfolgenden Operation zusammen. Die Operation wird mit dem Stack ausgeführt, auf den
in der Regel der Operand zuvor gespeichert wurde. Genauer wird in der obersten Stackebene mit dem Akkumulator gearbeitet. Dieses Verfahren hat sich bei der Abarbeitung bewährt, da der häufigste verwendete Datenwert damit direkt in
einem Register steht. Diese Herangehensweise ist auch bei der rein textuellen Konvertierung berücksichtigbar.
Folglich besteht ein Befehl (sub class Instruction
) aus zwei wesentlichen Teilen:
EInstruction instr
: der eigentliche Befehl add, sub, push etc.
EOperand operand
: einer Operandenkennzeichnung, die zusätzlich je nach Art benötigt:
id
: Nummer einer Variablen, die im userspace verwaltet wird.
constant
: Konstante verschiedener passender Art
Der Operand ist entweder
stack
: kein externer Operand, entspechend der EInstruction
Stackebenen
var
: eine Variable, die direkt benutzt wird ohne vorher auf den Stack gespeichert zu sein. Man spart sich das push Variable indem die Operation mit dem Operand gebündelt ist.
constant
: Statt einer Variable der konstante Wert.
Zusätzlich steht in einer Instruction
noch der textuelle Wert wie er beim Parsen erfasst wurde, das ist ggf. der Variablenname. Dieser kann benutzt werden für
die Textgenerierung, sollte aber identisch mit dem über id
referenzierten Namen der Variable sein.
Die Operation
a * b + c
würde in RPN direkt geschrieben werden als
a = b * c +
wobei =
für ENTER steht. In dieser Class ist das gespeichert als
a set b mult c add
Entsprechend gilt:
a + b * c //normal Expression a = b = c * + //RPN a set b set //führt zu push a in den Stack c mult @ add //@ steht für den Operand aus dem Stack
Die Rücktransformation aus dieser Form der RPN in die 'normale' Darstellung ist mit der Operation convertToNormalExpression()
ausprogrammiert, die hier nur aus Test- und Debugausgabegründen vorhanden ist. Die Konvertierung für die C-Codegenerierung
ist in srcJava_FBcl/org/vishia/fbcl/translate/Translation2Target_FBtrl in der Operation convertToOtx(Execode expr, Module_FBcl mdl)
realisiert. Diese erzeugt den für die Codegenerierung notwendigen OutTextPreparer
-Code, siehe Chapter: 3.5 Codegenerier-Regeln in Operation und Output-Datenpins
Topic:.FBclDataModel.codegen.
Die class ..CodeGen_FBcl srcJava_FBcl/org/vishia/fbcl/fblock/CodeGen_FBcl enthält:
usedInputs
: Eine long-Wert mit 62 Bits für maximal 62 Dateneingänge, die von dieser Operation in der aufsteigenden Pin-Reihenfolge als
Argument benutzt werden.
Bit mit mUsesThis
gekennzeichnet (Bit63) in usedInputs
für die Aussage, ob die Operation die Referenz auf Instanzdaten benötigt.
Bit mit mHasStatements
gekennzeichnet (Bit63) in usedInputs
als Merker während der Codegenerierung, dass Statements bereits erzeugt wurden (TODO?)
sCodeText
entweder für einen rein konstanten Code wenn genCodeAccess == null
oder als textuelle Anzeige der Codegeneriervorschrift für Debuggingzwecke.
genCodeAccess
: Dies zeigt auf eine Instanz von OutTextPreparer
srcJava_Zbnf/org/vishia/util/OutTextPreparer, die die Generierregel enthält und die Generierung ausführen kann.
Für Standard-FBlocks sieht die Generiervorschrift in Textform beispielsweise für das Output-Pin eines F_ADD
wie folgt aus:
(<&IN1> + <&IN2>)
Diese Textform ist im OutTextPreparer
als Folge von Anweisungen gespeichert, so dass die Generierung schnell abgearbeitet werden kann. Die Expression-Parts <&IN1>
usw. werden bei der konkreten Codegenerierung ersetzt mit den Werten, die der Eingangsverdrahtung der benannten Eingänge
entspricht. Sind dies ebenfalls Expressions, dann wird damit ein komplexer Ausdruck (Expression) gebildet. Daher ist diese
Expression auch in Klammern gesetzt, falls sie folgend als Teil einer Expression verwendet wird.
Für FBlocks, deren Ausgangswerte mit der Input-Event-Verarbeitung gebildet wird (mit Operations), sieht die Ausgangsexpression typischerweise wie folgt aus:
(<&THIS_OBJ>)->element
Es wird ein Element in der struct
oder class
(C++) der zugehörigen Instanz des FBlock gelesen. <&THIZ_OBJ>
ist ein Aufrufargument, das mit der Referenz auf die Instanzdaten bei der Codegenerierung belegt wird, gesteuert vom Bit
mUsesThis
siehe oben in diesem Abschnitt.
Für den Aufruf der Event-Input-Operation sieht die Codegenerier-Regel für C im allgemeinen wie folgt aus:
operName_FBType(<&THIS_OBJ>, <&IN1>, <&IN3>);
Für C++ wird dort generiert:
<&THIS_OBJ>->operName(<&IN1>, <&IN3>);
Der Typ ist für den C++-Compiler schon im THIS_OBJ
gespeichert, es wird eine Klassenmethode gerufen. Im Beispiel wird für diese Operation IN1
und IN3
benutzt. Das Element usedInputs enthält den Wert 0x80000005 falls
IN1 der erste und
IN3 der dritte Input ist. Die Inputbezeichnungen werden für die Codegenerierung selbst nicht analysiert.
Diese Codegenerier-Regeln werden selbst generiert. Siehe TODO Chapter: 7 Codegenerierung aus FBcl
Topic:.FBclDataModel.dtype.
Insbesondere in frei verwendbaren Library-Modulen ist ein konkreter numerischer Datentyp oft nicht festgelegt. Dieser ergibt
sich aufgrund der Verdrahtung. In IEC-61499 als auch in der IEC-61131-3 für Automatisierungsgeräte sind dafür Datentypen wie
ANY_MAGNITUDE
oder ANY_NUMBER
vorgesehen. Simulink kennt für diesen Zweck den derived-Datentyp.
Topic:.FBclDataModel.dtype.dtypeKind.
Es können von der Nutzung her folgende Arten unterschieden werden:
,fest (determined)
,: Wohl definierter fester Datentyp an Datenpins im FBlock_Type_FBcl
: Das kann sowohl ein FBlock-Typ-Pin sein, das nur mit bestimmten numerischen Datentypen arbeitet, nur davor vorgesehen ist,
beispielsweise nur float
oder ein spezifischer int
-Anschluss. Es kann sich aber auch um einen UserDefinedType handeln, der eben fest vorgegeben ist. An dieser Stelle soll auf
eine mögliche Vererbungsstrategie hingewiesen werden, die zulässig sein sollte. Ein abgeleiteter Typ ist auch gleichzeitig
der Basistyp, sie sind verträglich.
,egal (any)
,: Wohl definierter aber nicht konkreter numerischer Datentyp, also ANY_NUMBER
oder dergleichen. Die jenige Operation des FBlock kommt mit beliebig passenden Daten konkreter Typen zurecht, ohne dass irgendwo
bei der Codegenerierung der Typ gesondert behandelt werden muss. Das ist der Fall bei einfachen numerischen Operationen, die
eine Expression produzieren. Diese werden inline vom Compiler behandelt, der Compiler erkennt hier der Typrichtigkeit. Wenn Input- und Output-Typen
indentisch sind, werden vom Compiler die Typen erkannt und richtig im Maschinencode abgebildet. Wenn die Weiterverarbeitung
des Output-Typ höherwertig erfolgt (z.B. von int
auf int32
oder float
auf double
, dann führt der Compiler automatisch Befehle zur Konvertierung ein. Aus Sicht der Grafischen Programmierung ist dann beispielsweise
ANY_NUMBER
auf REAL
verdrahtet.
Der obige Punkt trifft auch zu, wenn die FBlock-Funktionalität in einem C-Makro enthalten ist, das letztlich Expression produziert (man kann dies so implementieren) oder eine C++-Operation gerufen wird, die für die notwendigen Typen mehrfach zur Verfügung steht. In diesem Fall entscheidet der C++-Compiler automatisch aufgrund der Argumenttypen welche genaue Operation gerufen wird. Allerdings sind könnten dann einige Kombinationen nicht zulässig sein, wenn dafür keine Definition gefunden wird. Das ist allerdings ein Problem der Abstimmung zwischen Grafischer Applikation und anzubietenden Grundfunktionalitäten. Letztere müssen dann halt ergänzt werden.
,codegen-flexible
,: Nicht letztgültig definierter Typ: Ein FBlock_Type_FBcl
hat an bestimmten DatePins nicht konkrete Datentypen, und diese sind nicht als 'wohldefiniert' gekennzeichnet. Das ist der
komplizierte Fall. Er ist notwendig eben weil ein Librarymodul mit verschiedenen Typen genutzt werden soll und die Codegenerierung
für die verschiedenen Typen unterschiedliche Implementierungen bezüglich der Typen erzeugen soll. Das darunter liegende Modul
ist dann meist grafisch definiert. Im C++-Bereich könnte man dies durch Template-Typen lösen, es gibt dann nur eine Realisierung,
die beim Compilieren für die verschiedenen Typen dupliziert wird. Das ist die C++-Template-Herangehensweise. Wenn diese jedoch
nicht erwünscht ist, dann wird quasi genau das gleiche auf Codegenerierebene ausgeführt: Für jede Typkombination wird extra
Source-Code generiert. Im Maschninencode ist das letztlich egal, denn eine Template-class erzeugt bei verschiedenen Datentypen
auch extra passende Maschinencodes, wenn dies nicht inline unauffällig wird. Es ist also nicht eine Frage der Codegrößen-Optimalität,
sondern eine Frage, wie mit verschiedenen Typen bei gleicher Funktionalität umgegangen wird. Bei Java-Codegenerierung gibt
es die Template-Möglichkeit nicht für die Primitive-Typen (numerische). Dort sieht das Konzept zwar ähnlich aus, bezieht sich
aber auf eine Typfestlegung von Referenzen, mit denen nicht gearbeitet wird sondern die nur gespeichert werden (adäquat Forward-Pointertypes
in C/++), also mehr eingeschränkt. Daher hat Java auch nicht das Problem, für verschiedene Template-(hier Generic-)Typen verschiedene
Bytecodes zu generieren. Aber für numerische Varianten muss dies auf Quellebene erfolgen.
Es gibt ein weiteres Thema: Abhängige Datentypen:
Ein Datentyp kann entweder skalar sein, oder ein Array, oder für numerische Typen ein komplexe Zahl mit Real- und Imaginäranteil. Der Array- oder Complex-Type ist ein eigener Datentyp, aber er basiert auf einem skalaren Typ. Wenn beispielsweise ein FBlock skalare Inputs, und passend zu deren Typen einen complex-Output hat, dann muss vom Typ des Complex-Output auf den skalaren Typ referenziert werden. Die Typen sind abhängig.
Eine andere aber vergleichbare Problematik tritt bei den codegen-flexible-Datentypen auf: Es kann sein dass bestimmte Datentypen an Ein- und Ausgängen zusammenhängen, diese also miteinander konform bestimmt werden müssen.
Topic:.FBclDataModel.dtype..
Die class DataType_FBcl enthält die Eigenschaften des Datentyps selbst. Dazu gehört
* Typename, char als Kürzel, Anzahl Array-Elemente, RealOrComplex.
Die class DataTypeRef_FBcl enthält DataType_FBcl
als Composite.
Ursprünglich wurde DataTypeRef_FBcl
geschaffen, um den referenzierten Datentyp auszuwechseln (Java-Codegenerierung aus Simulink in 2018). On dies noch zielführend
ist, ist zu klären.
Jedenfalls können mehrere DataTypeRef_FBcl
auf den gleichen Basis DataType_FBcl
verweisen, insbesondere bei anderen Konstellationen für Anzahl ArrayElemente und RealComplex. Das ist allerdings derzeit
nicht der Fall, da diese Infos in DataType_FBcl
gespeichert sind. Es scheint derzeit eher so, als ob es eine eineindeutige Verbindung zwischen DataTypeRef_FBcl
und DataType_FBcl
gibt (Stand 2020-08-08), in Klärung.
Der DataTypeRef_FBcl
enthält nun weitere Elemente:
connections: Liste aller DataPins mit diesem Typ, die Pins verweisen auch auf diesen Typ (gegenseitige Aggregation).
deps: DataType_FBcl.Dependency: Mit dieser Liste werden alle abhängigen DataTypeRef_FBcl
im Modul benannt.
Topic:.FBclDataModel.dtype..
Die Instanzen von DataTypeRef_FBcl
selbst im gerufenem Modul (Library-Modul) und beim Aufrufer dürfen nicht gemischt werden. Sie sind zwar im Typ abgestimmt,
es müssen aber verschiedene Instanzen sein. Ansonsten würde ..... dürfen aber passiv referenziert werden! Das passt aber nicht
weil sie ihre Dependencies wiederum haben.
Wenn bFix gesetzt, werden keine DataTypeRef_FBcl.registerConnection(...) registriert. Folglich kann der Dtyp referenziert werden, weil er fest ist!
Topic:.FBclDataModel.dtype..
Für die grafische Programmierung gilt bei den meisten Tools folgende Herangehensweise:
Die Verbindung eines allgemeinen Typs (ANY_NUMBER
) mit einem konkreten Typ ist zulässig. Bei Simulink sind dies die mit derived gekennzeichneten Typen an Pins. Die Verbindung verschiedener konkreter numerischer Typen ist aber nicht zulässig (z.B. REAL
mit INT
). Es muss also ein FBlock zur Typkonvertierung zwischengeschaltet werden. Das ist an sich auch gut so, denn die Datentypen
sind eben nicht egal. C und C++ gehen hier viel freier um, als es einer sicheren Anwendung gut tut. Die Grafischen Programmiertools
sind konsequenter.
Die srcJava_FBcl/org/vishia/fbcl/fblock/DataTypeRef_FBcl wird von den Pins referenziert, wobei es eine Organisation von abhängigen Datentypen gibt und die selben Datentypen auf verschiedenen Pins mit einer Referenz auf genau die selbe Instanz gekennzeichnet wird. Wird ein FBlock verdrahtet, dann werden vorhandene Referenzen korrigiert.
Diese Instanz ist kein fester Datentyp, sondern referenziert einen Datentyp srcJava_FBcl/org/vishia/fbcl/fblock/DataType_FBcl. Bei der Codegenerierung eines Library-Moduls werden die Referenzen auf die konkreten Typen ersetzt, damit wird generiert. Folglich gibt es mehrere Varianten der Codegenerierung für das selbige Library-Modul für verschiedene Typen, die im Filenamen und in den Operation-Namen entsprechend gekennzeichnet sind. Für C++ kann die Eigenschaft der ArgumentTypabhängigkeit der Operation (Signatur) für den Linker genutzt werden, für C ist dies nicht möglich. Daher werden die Operationen entsprechend gekennzeichnet (in 2019-11 noch TODO).
Datentyp als Referenz zu DataTypeRef_FBcl
.
stepTime
: Referenz auf eine Abarbeitungszeit, hier allerdings nicht zutreffend, es sei denn spezielle Typen sind für bestimmte Abtastzeiten
zuständig als Sonderlösung.
Die Datentypkennzeichnung erfolgt gleichermaßen als Datentyp in den Pins des FBlock_Type_FBcl
beispielsweise als allgemeiner Datentyp wie ANY_NUMBER
und im Typ in den Instanzen. Im Datentyp werden die Pins referenziert, die zum selben Typ gehören oder in einer Abhängigkeit
stehen.
Topic:.FBclDataModel.readFBcl.
Beispielsweise in Simulink, aber auch in verschiedenen Repräsentationen in der IEC61131-3 sind die Daten eines grafischen
Modells oft in XML zugänglich, in Simulink im slx-File. Häufig werden dort die einzelnen FBlocks mit ihren Eigenschaften und
einer UID (Unified Identification Number) benannt und die Verdrahtung der FBlocks in extra 'Wire'-Einträgen aufgeführt. Das
entspricht also der grafisch-topologischen Sicht. Mit diesen Informationen sind die FBlock_FBcl
-Instanzen aufbaubar. Es muss dabei ein zweckmäßiges Mapping zwischen den FBlock-Typen des jeweiligen Tools geben, und den
FBLock-Typen, die in der FBcl als Basic-Typen verfügbar sind. Letztere richten sich grundsätzlich nach der IEC-61499, sind
aber auch variabel aufbaubar (config-Files und spezifische IEC-61499-FBcl-Inputfiles). Das Mapping muss fest programmiert
im Konvertierungstool aus den speziellen Quellen verankert sein (zum Beispiel Simulink to FBcl-Conversion) oder dort geeignet
über Textfiles konfigurierbar.
Häufig liegt bei FunctionBlock-Grafiken ein Datenfluss vor, aber keine Eventverbindung. Die Eventverbindung kann aus dem Datenfluss hergeleitet werden, wobei Speicherglieder (Unit-Delay in Simulink) und Abarbeitungszeitgrenzen (Rate-Transition in Simulink oder geänderte Abtastzeitzuordnung) berücksichtigt wird.
Damit werden die EventConnections im nachhinein automatisch ermittelt und im FBcl-Datenmodel gespeichert. Die EventConnections sind wesentlich und werden genutzt für die Codegenerierung.
Erfolgt eine Transformation von IEC-61499-Files und zu IEC-61499-Files, was als FBcl-Filespeicherformat gilt, dann sind die Event-Connections mit dabei wie hier abgespeichert.
Folglich wird etwa ein Unit-Delay aus Simulink umgewandelt in eine bestimmte Art der Event-Connection. In diesem Fall ist das ein F_MOVE
-FBlock der IEC-61499, der mit einem Event getriggert wird mit der Bezeichnung Upd_<Steptime>
. (Upd
steht für Update, das Event / die Operation zum Updaten von Speicherdaten / Zuständen). Mit dieser Informationslage lässt
sich das FBcl-DatenModell grundsätzlich auch wieder als Simulink-Modell speichern.
Topic:.FBclRead.
Last changed: 2019-11-14
Dieser Dokumentationsabschnitt befasst sich mit dem Rahmenprogramm Einlesen von FBcl-Files in IEC61499-Codierung und Codegenerierung von Ablaufcode für embedded Plattformen.
Die Files in FBcl werden entweder in der IEC 61499-textuellen Notation für Composite FBlocks erwartet, oder in der entsprechenden XML-Notation beispielsweise aus einer 4diac-Grafik. Beide Inputs sind gleichwertig. Diese Files könnten beispielsweise zuvor aus Simulink erzeugt worden sein oder mit einem IEC 61499-Tool wie 4diac IDE bearbeitet und gespeichert worden sein.
Topic:.FBclRead.FBcl2c.
Last changed: 2020-07-17
Im Constructor werden die main-Aufrufargumente übergeben, ein Projekt angelegt und das Codegenerier-Templat eingelesen:
FBcl2c(CmdArgs cmdArgs, MainCmdLogging_ifc log) throws ... { this.cmdArgs = cmdArgs; this.log = log; this.prj = new Prj_FBCLrd(log, cmdArgs); this.trlScripts = ReadTranslationScripts.read(cmdArgs.codeTpl); }
Das Projekt speichert Einstellungen und alle intern gelesenen FBlocks, die dann mehrfach verwendet werden können, bzw. zur Übersetzung anstehen.
Es wird genau ein File übergeben.
void exec(File fIn) throws InstantiationException, IOException { try { Module_FBcl mdl = this.prj.readerFBcl.read(fIn, 0); Translation2Target_FBtrl.translate(mdl, this.cmdArgs.dirGenSrc, this.trlScripts, 0); } catch(IOException exc) { Debugutil.unexpected(); } }
Das Lesen dieses Modules führt auch zum Lesen aller gerufenen Sub-Module, die in einem Search-Path aufgefunden werden müssen.
Die Zielsprachgenerierung wird im Abschnitt Chapter: 7 Codegenerierung aus FBcl beschrieben.
Topic:.FBclRead.readMdl.
Last changed: 2020-07-17
Für das Lesen von FBcl files nach IEC 61149 ist die class
zuständig.
/**Working class for reading IEC61499-Files and conversion to the target language. * @author Hartmut Schorrig */ public class ReaderFBcl {
Folgende Operation realisiert das Lesen der FBcl-Files, führt die Konvertierung in die Zielsprache aus und liefert als Interface den Type des Moduls zurück.
public FBlock_Type_FBcl read(File fileFBCL, int recursive) throws NoSuchFileException, InstantiationException {
Das Raw-Datenmodell aus den IEC 61499-Files wird erhalten über Aufruf : srcJava_FBcl/org/vishia/fbcl/readFBcl/ReaderFBcl#read-java.io.File-int-, siehe todo
IEC61499data dataRaw = readFileRawData(fileFBCL); //check content IEC61499data.Fb_type_declaration fBtype = dataRaw.get_FBType();
Wenn der FBlock ein Composite-FB ist, dann enthält er intern ein Netzwerk aus weiteren FBlocks. Diese weiteren FBlocks werden
jetzt erkannt, und führen möglicherweise zum rekursiven Aufruf genau dieser Routine. FBlocks können auch Standard-FBlocks
sein, die bereits bekannt sind, oder FBlocks, die bereits übersetzt wurden und daher in dem oben genannten mapReadModules
enthlaten sind.
mdlRaw = fBtype.get_FBNetwork(); if(mdlRaw !=null) { for(IEC61499data.Fb_instance_definition fbraw: mdlRaw.get_FB()) { String sTypeFB = fbraw.get_Type(); FBlock_Type_FBcl typeFB = this.prj.stdFBlocks.getType(sTypeFB); if(typeFB == null) { typeFB = searchUserModule(sTypeFB,0); } if(typeFB == null) { //If it is not a standard FB, nor a also known submodule-FB. //It has to be found in a user's path. //Read it and store in mapReadModules throw new NoSuchFileException("src file of inner module not found: " + sTypeFB); } } }
Die Routine searchModuleFile(sType)
sucht entsprechend gegebenen Searchpath-Einstellungen in den CmdArgs die FBcl-Files der Submodule, wobei die Extension fbt
(XML-Modellfile eines Composite-FBlock), fbc
und fbcl
berücksichtigt werden. Files im Searchpath mit dieser Extension werden als Module-FBcl-File gewertet.
Die folgende Operation konvertiert das Roh-Modul nach IEC61499 in das FBcl-Datenmodell:
// //Convert from the raw (immediately parsed) data to the FBcl data model: // Module_FBcl mdl = prepareModule(fBtype);
Dabei wird erkannt, ob es sich um einen Composite-FB (mit internem Netzwerk), einen Simple-FB (mit einer in Structure Text geschriebenen Operation) oder um einen Basis-FB (mit Statemachine) handelt. Die IO-Pins werden abgelegt und der FB-Type] wird erzeugt.
Das gelesene Modul im FBcl-Datenmodell wird einem Check unterworfen, dann werden die gelesenen Informationen als Test wieder ausgeschrieben, bei Bedarf zusätzlich für das Debuggen noch ein *.fbcldata-File:
CheckModule_FBrd.check(mdl, false); // if(this.cmdArgs.dirWrTestFBcl !=null) { this.writerFBcl.writeFBCL(new File(this.cmdArgs.dirWrTestFBcl, mdl.name() + ".fbcl"), mdl); this.wrDataFBcl.writeModule(new File(this.cmdArgs.dirWrTestFBcl, mdl.name() + ".fbcldata"), mdl); if(this.cmdArgs.dirWrResults4diac !=null) { this.wrXmlFBcl.writeFBCL(new File(this.cmdArgs.dirWrResults4diac, mdl.name() + ".fbt"), mdl, this.xmlReader); } }
Als letztes wird der FBlock-Typ mit der Referenz zu seinen Interndaten in der Projekt-Map gespeichert:
FBlock_Type_FBcl fbType = mdl.ifcFB; this.prj.mapReadModules.put(fbType.name(), fbType);
Damit sind die gelesenen IEC-61499-Daten als FBcl-Datemodell gespeichert und können für die Codegenerierung verwendet werden.
Topic:.FBclRead.WrData.
Last changed: 2020-09-16
Mit der Option -dbgFBcl
beim Cmdline-Start des Übersetzers lässt sich ein Verzeichnis angeben. Ist die Option gesetzt, dann wird in diesem Verzeichnis
mehrere Files angelegt:
<&dirWrTestFBcl>/<&ModuleFile>.fbcl: Spiegel des gelesenen Files im IEC-61499-Textformat rückgeschrieben zum Vergleich.
<&dirWrTestFBcl>/<&ModuleFile>.fbt: Spiegel des gelesenen Files im IEC-61499-XML-Format zur Repäsentation im 4diac oder zum Vergleich.
<&dirWrTestFBcl>/<&ModuleFile>.html: Axusgabe aller internen Daten in einem allgemeinen Darstellungsformat als html-Ausgabe mit Links zu einzelnen Elementen (nutzt srcJava_vishiaBase/org/vishia/util/DataShow
<&dirWrTestFBcl>/<&ModuleFile>.fbcldata: Ausgabe aller internen Daten in einem speziellem Darstellungsformat, siehe Folgekapitel.
Topic:.FBclRead.WrData.fbcldata.
Last changed: 2020-09-16
Das File ist in ähnlichen Abschnitten aufgebaut wie ein fbcl-File nach IEC-61499, aber mit ergänzenden Angaben. Die Gliederung ist
==Ifc== ===EVENT_INPUT=== ...Angabe aller Event-Inputs mit zugehören Operation_FBcl ===EV_OUTPUT=== ...Angabe aller Event-Outputs ===VAR_INPUT=== ...Angabe aller Daten-Inputs ===VAR_OUTPUT=== ...Angabe aller Daten-Ouputs mit zugehören Operation_FBcl ===EV_DEP_OPER=== ...Angabe aller fiktiven Event-Inputs für abhängige innere Operationen ==EvChains== ...Angabe aller Evchain_FBcl (einzelne Eventketten) ==FBlocks== ...Angabe aller FBlocks mit Verbindungen und weiteren Details
Am Beispiel dargestellt mit Erläuterungen:
==Ifc== ===EVENT_INPUT=== ev_a, a, b, c ev_a[0] => ev_a_Ya1(#0 =$3) ev:1/1->0/1/3 D:3/7->0/3/7 ~>Ya3 ~>$Ya2[0] => Ya3(#3 .0) ev:4/5->0/2/2 d:0/7->0/4/4 <~ev_a_Ya1 ev_a[1] => ev_a_Mulac(#1 =$5) ev:1/1->0/0/c D:5/5->0/0/f8 ~>Dq1 ~>Ya4 ~>$Mulac[0] => Dq1(#4 =$0) ev:8/9->0/8/8 D:0/5->0/30/f0 <~ev_a_Mulac ~>$Mulac[1] => Ya4(#5 .0) ev:8/9->0/4/4 d:0/7->0/8/8 <~ev_a_Mulac upd upd[0] => upd(#2 =$0) ev:2/2->0/10/10 D:0/0->0/c0/c0
Es gibt ein Event-Input ev_a
dem die Datenpins a
, b
und c
zugeordent sind. Dem Event direkt zugeordnet sind zwei Operationen. Das liegt daran, dass das Event-Input im Modul auf zwei
EventChains verzweigt, also zwei unabhängige Funktionalitäten intern angesprochen werden. Nach der nochmaligen Benennung des
Events und [0]
des Index der Operation EvinType_FBcl#operations wird die Operation dargestellt (Ausgabe aus der toString()
). Diese ist in 4.3.2 Darstellung der Operationen in fbcldata erläutert.
Es sind hier gleich auch die abhängigen operationen nach ~>
dargestellt. Damit ist insgesamt erkennbar, welche Daten- und Eventpins benutzt und beeinflusst werden: Die erste Operation
setzt unmittelbar dout0 und dout1 (mask ../3/..) aber beeinflusst mittelbar auch dout2 (mask ../7). Das geschieht durch die
depending operation Ya3
, die mit mask ..4/4
nur pin dout2 setzt. Jedes Bit der hexadezimal präsentierten Maskierung stellt einen Pin dar. Die Umrechnung von Hex-Mask-Darstellung
auf die Pin-Nummer ist dem Geübten durch einfache Auszählung möglich. Jede Ziffer repräsentiert 4 pins. Die Hexa->Binär-Abbildung
für 16 Kombinationen lässt sich mit einer kleinen Tabelle verwirklichen. Es ist hier eine knappe Darstellung gewählt.
Die zweite Operation des ev_a
beeinflusst auch die Datenbits dout6 und dout7, die eigentlich zum upd
-Event gehören. Diese Information ist wichtig, denn für die Update-Funktionalität uss die ev_a[1]-Operation jedenfalls gerufen
werden.
Das zweite Event-Input-Pin ruft genau eine Operation auf.
===EV_OUTPUT=== evo_y2, y1, y2 evo_y3, y3 evo_y4, y4 setok, y5, y6 updok, q1, q2 ===VAR_INPUT=== a dataType:float#1 b dataType:float#1 c dataType:float#1
Die Event-Outpin und DinPin sind unspektakulär. Zu beachten ist, dass die zugehörigen Datenpin zu den Events mit , abgetrennt sind, nach dem Namen des Event-Pin.
Die Datenpins benennen den Datentyp, der außen vorgegeben oder intern festgestellt wurde.
===VAR_OUTPUT=== y1 dataType:float#1 y1 := y1(#6 .$0) ev:1/1->0/1/1 D:0/0->0/1/1 y2 dataType:float#1 y2 := y2(#7 .$4) ev:1/1->0/1/1 D:4/4->0/2/2
Die Daten-DoutPin benennen ebenfalls den Datentyp. Darunter wird eine zugehörige Output-Zugriffsoperation angegeben, falls vorhanden. Diese ist entweder eine kombinatorische Funktion (Codegenerierregel in Operation_FBcl#genCodeStmnt angegeben) oder eine einfache Zugriffsfunktion auf die innen gespeicherten Daten. Nur Ausgänge, die zwangweise auf eine außen anzugebende Variable schreiben, haben keine Output-Zugriffsfunktionen. Solche Outputs sind insbesondere deshalb als Speziallösung eingeführt, weil die S-Function in Simulink so arbeiten. Will man gleiche Kerne in C/++ für Simulink und IEC61499 haben, dann muss es dieses Feature geben. Die Kernfunktionen schreiben auf Variable. Es widerspricht nicht direkt den Vorgaben der IEC61499, ist als eine mögliche Variante.
===EV_DEP_OPER=== $Ya2 $Ya2[0] => Ya3=(#3 .0) ev:4/5->0/2/2 d:0/7->0/4/4 <~ev_a_Ya1
Die Ya2
-Operation ist oben bei den ===EVENT_INPUT===
bereits als abhängige Operation aufgeführt, hier nochmals in der Übersicht aller abhängigen Operationen benannt. Die Namen
der Events beginnen mit $
. Sie sind gleichwertig wie die EvinPin des FBlock (Datentyp EvinType_FBcl), sind aber nicht außen verdrahtet sondern werden stimuliert als Folge der vorigen Operation.
Abhängige Operationen entstehen durch Verzweigungen (fork) der EventChain im FBlock. Mit optimierter Codegenerierung werden sie nicht aufgerufen, wenn deren Ausgänge nicht benutzt sind und die Operation selbst nicht unbedingt ist (Operation_FBcl#isNecessaryEventOper--). An den hier aufgeführten Ausgängen (hexa-Mask) ist erkennbar, welche Ausgänge das betrifft.
Die Beispielangaben (unvollständig) entstammen dem folgenden Modul (Composite-FBlock):
==EvChains== 1: ev_a[0] => ev_a_Ya1(#0 =$3) ev:1/1->0/1/3 D:3/7->0/3/7 ~>Ya3 ==>fork... =>Ya1.REQ[0]+$ ->Ya2.REQ+ 2: ev_a[1] => ev_a_Mulac(#1 =$5) ev:1/1->0/0/c D:5/5->0/0/f8 ~>Dq1 ~>Ya4 ==>fork... =>Mulac.REQ[0]+$ 4: upd[0] => upd(#2 =$0) ev:2/2->0/10/10 D:0/0->0/c0/c0 ==>updok:4 =>Q1.REQ[0]=$ =>Q2.REQ=$ 8: $Ya2[0] => Ya3(#3 .0) ev:4/5->0/2/2 d:0/7->0/4/4 <~ev_a_Ya1 ==>evo_y3:1 ->Ya3.REQ[0]+! ->Ya31.REQ+->Y32.REQ+ 10: $Mulac[0] => Dq1(#4 =$0) ev:8/9->0/8/8 D:0/5->0/30/f0 <~ev_a_Mulac ==>setok:3 =>Dq1.REQ[0]+=>Fq1.REQ+$ =>Addq1.REQ+$ =>Dq2.REQ+=>Fq2.REQ+=>Addq2.REQ+$ ->Ya6.REQ+ 20: $Mulac[1] => Ya4(#5 .0) ev:8/9->0/4/4 d:0/7->0/8/8 <~ev_a_Mulac ==>evo_y4:2 ->Ya4.REQ[0]+
Die EventChains werden vorn mit ihrer Hexa-Mask benannt wie sie als Querverweis in den Pins der FBlocks aufgeführt sind. Die
Hexa-Mask zählen ab Index 0, repräsentiert mit 1
.
Die erste Zeile jeder EventChain zeigt folgend die gleiche Darstellung wie bei den ===EVENT_INPUT===
, das ist die toString()
-Ausgabe der Operation_FBcl#toString--). Als letztes wird nach ==>
das Output-Eventpin dargestellt. Die Angabe fork...
bedeutet, dass es eine Aufzweigung mit nachfolgenden abhängigen EventChains gibt. Das kann auch sein, wenn das OutputEvent
an einem Pin hängt aber gleichzeitig eine weitere EventChain speist. Wird das Output-EventPin benannt, dann folgt im Beispiel
:4
dessen Pin-Nummer.
Die weiteren Zeilen benennen die FBlocks und dessen angesproches evin
der Elemente aus der Evchain_FBcl#evList. Beim ersten Element wird der [index]
der zugehörigen Operation im EvinType, wie sie in Evchain_FBcl.EvCurr angegeben ist, dargestellt.
Der vordere anweisungserzeugende Teil mit einer eigenen Zeile und ==>
vor den Eventinputs dargestellt. Der nicht anweisungserzeugende Teil bzw. der Teil, der interne Variablen aufgrund Kombinatorik
setzt, ist mit -->
gekennzeichnet. Mehrere FBlock.evin
sind nacheinander in der Zeile benannt.
Ein nachfolgendes =
wie bei =>Q1.REQ[0]=$ =>Q2.REQ=$
nach dem Evin-Namen und ggf. [ixOper]
bedeutet: Die Evin-Operation des Typs des FB erzeugt Statements. Das ist nicht der Fall bei kombinatorischen FB. Im Beispiel
steht hier ein F_MOVE
der als Anweisung des Setzen der Outputvariable enthält (wirksam wenn dessen Event kommt).
Ein nachfolgendes +
wie bei den meisten Elementen bedeutet, dass ein Ausgang beim Typ eine DoutType_FBcl#operDout aufweist, also mit Kombinatorik berechnet wird. Folgt danach keine weitere Kennzeichnung, dann ist dies ein kombinatorischer
Zwischenwert, der in der Codegenerierung lediglich als Teil einer Expression in (...)
auftritt.
Ein nachfolgendes $
bedeutet, dass ein dout-Pin dieser Event-Operation mit einer Instanzvariable in der Codegenerierung versehen wird. Bei mehreren
solcher Outputs gibt es mehrere $$$
. Die Instanzvariable bedeutet, dass der Wert an dieser Stelle von mehreren EventChain verarbeitet werden kann, wie er von
der zugehörigen EventChain gesetzt wurde. Folglich darf es diese Kennzeichnung nur in dem anweisungserzeugenden Teil der EventChain
geben, mit =>
vor dem FB.evin
gekennzeichnet. Die Kennzeichnung hir im Beispiel mit =>Q1.REQ[0]=$ =>Q2.REQ=$
zeigt an, dass beide Ausgänge Instanzvariable haben und dass die evin der FBlocks Anweisungen haben. Da keine Kennzeichnung
mit +
erfolgt, setzt die Anweisung des evin die Instanzvariable. Mit einer Kennzeichnung +$
wird der Wert der Instanzvariablen in der Eventoperation mit der kombinatorischen Operation im Typ berechnet, also mit einer
Expression gesetzt.
Ein nachfolgendes !
bedeutet, dass ein dout-Pin dieser Event-Operation auf eine lokale Zwischenvariable gelegt ist. Dies soll nur im Zusammenhang
mit +!
auftreten, da die lokale Variable geeignet mit einer Expression gebildet werden muss. Im Beispiel ist das bei ->Ya3.REQ[0]+! ->Ya31.REQ+->Y32.REQ+
der Fall: Der Ausgang von Ya3
verzweigt und wird innerhalb der selben EventChain ohne Fremdverzweigung vorwärts wieder verwendet. Daher wird bei der Codegenerierung
folgendes erzeugt:
/**get the output value */ inline float y3_Testcg_CombinQ ( Testcg_CombinQ* thiz, float a ) { //===operEvin=== float Ya3 = (thiz->Ya1 * 1.1f); return ((a * Ya3) + Ya3); }
Für den Zwischenwert braucht es keine Instanzvariable, sondern nur einen lokalen temporären Speicher, mit ->Ya3.REQ[0]+!
gekennzeichnet.
==FBlocks== ..... Ya3 : F_MUL | steptime:<??null??> ->REQ := Ya2.CNF CNF -> Ya31.REQ =>IN1 := $Ya1.OUT:F | dataType:float#1 evChain:8/0 =>IN2 := <??null??> | dataType:float#1 evChain:8/0 ! OUT => Ya31.IN2:F, Y32.IN2:F oper:0/0 evChain:8/0
Die ==FBlocks==
sind nur auszugsweise mit dem oben benannten Ya3
gezeigt:
Inputs werden vorn mit ->
für Events und =>
für Daten markiert.
Die Inputbelegung wird mit :=
eingeleitete.
Outputs werden nach dem Namen mit ->
oder =>
markiert.
Vor den Datenoutputs steht ein !
oder $
je nach Präsenz mit einer lokalen oder Instanzvariablen.
Mehrere Outputs sind durch Komma getrennt.
Der Datentyp ist angegeben.
Operations die den output beeinflussen sind mit ihrer Mask angegeben. In diesem Fall ist das 0, weil dieser Output nur innerhalb einer Zugriffsoperation liegt.
Für die Outputs ist angegeben, weile EventChain dort Einfluss hat. Das ist typischerweise genau eine, kann aber mehrere sein. Daher ist hier wieder die Hexa-Mask angegeben.
Topic:.FBclRead.WrData.operdata.
Diese Darstellung wird durch Operation_FBcl#toString() erzeugt.
Ein Beispiel für eine Event-Operation ist, Output-Zugriffsoperation, ist:
$Mulac[0] => Dq1(#4 =$0) ev:8/9->0/8/8 D:0/5->0/30/f0
Dies ist im 4diac-Beispiel die abhängige Event-Operation, die am FBlock Mulac beginnt, das evout dieses FB ist aufgesplittet. Der Zweig ab dem Dq1 ist eine eigene EvChain, die mit dieser Event-Operatio vertreten ist.
Als erstes ist vor =>
das auslösende Event benannt, mit dem [Index]]
der Operation im Event. In diesem Beispiel ist das ein fiktives (Operations-) Event.
Es folgt der Name der Operation, wie sie auch in der Codegenerierung als Namensbestandteil oder Name als class-Funktion (C++, Java) verwendet wird.
Die Nummer nach (#4
ist der interne Index im Modul, in Module_FBcl#allOperations.
In der Klammer steht =
wenn die Operation Statements erzeugt. Das ist hier der Fall, da einige Instanzvariablen versorgt werden müssen. Eine Event-Operation
kann aber durchaus keine Statements erzeugen, beispielsweise bei ===EVENT_INPUT===
dargestellt bei Ya3
. Diese EventChain ist eine rein kombinatorische Verknüpfung, mit einer lokalen Zwischenvariablen, also ohne Statements.
Entweder es steht ( = )
oder ( . )
in der Klammer. Im letzten Fall hat die Operation selbst keine Statements. Das trifft für alle Event-Operationen zu, die
nur der Verwaltung der In- und Outputs dient. Die Funktionalität wird dann von Dout-Operations der Typen der FBlocks in der
Queue erledigt, mit kombinatorischem Zugriff.
In der Klammer steht $
wenn die Operation einen Instanzzeiger benötigt. Das ist der Fall, entweder wenn die Operation selbst auf Instanzvariable
zugreift oder diese setzt, oder wenn ein FBlock gerufen wird, der durch eine Instanz repräsentiert ist. Letzteres ist nur
dann nicht der Fall, wenn der FBlock lediglich eine Kombinatorik enthält, also keine Speicher braucht. Das ist allerdings
bei allen numerischen und boolschen Basis-FBlocks der Fall.
In der Klammer steht die Hexa-Mask aller Inputvariable. Die 0)
hier bedeutet, dass keine weiteren Inputvariable benötigt werden. Diese Eventchain nutzt also nicht direkt Eingänge.
Nach der Klammer werden für Event und Data die benutzten Pins angegeben, und zwar als Maske in Hexa-Darstellung so wie intern
gespeichert. Für einfache Module mit wengen I/O kann man als maschinencodeorientierter Leser sehr schnell die Ports zuordnen,
beispielsweise ist 6
das Port 1 und 2, Zählung begonnen mit 0, oder 18
das Port 3 und 4. Man wird sehr schnell auf die erwartenden Ports schließen können. Das ist eine gedrungene debugangemessene
Darstellung.
ev:
und d
können auch großgeschrieben auftreten:
Ev:
bedeutet, dass diese Operation unbedingt gerufen wird als Event-Operation, unabhängig vom Wiring am FBlock. Das entspricht
dem Bit_31 mNecessaryEventOper gesetzt in Operation_FBcl#mTrgEvoutBits. Das betrifft jedenfalls Events, die für Statemachins verwendet werden.
ev:
bedeutet, dass die Operation bei optimierter Codegenerierung nur dann gerufen wird, wenn die entsprechenden Outputs verwendet
werden (verdrahtet sind). Ausschlaggebend dafür sind die als letztes angebenen 'virtuellen' Bits für evout und dout, mindestens
eines der Bits muss wired sein. Ansonsten wird die Event-Operation nicht benötigt, sie kann aus Rechenzeiteinspargründen wegoptimiert
werden.
D:
Als Aufrufargument wird der this-Zeiger auf die Instanzdaten benötigt und generiert. Damit können auf interne Daten zugegriffen
werden. Das ist eine Wiederholung der Bedeutung des (..$..)
in der Klammer.
d:
Die Operation benötigt keinen Instanzzeiger. Es ist also eine rein kombinatorische Funktion. Es ist ein Widerspruch, wenn
diese Operation in der Klammer mit =
gekennzeichnet ist oder mit Ev
, denn sie kann keine Daten ablegen.
Bei der Inputmask für evin
und din
und für evout gibt es immer zwei Angaben:
<&direkt genutzte Input-Pins>/<&virtuelle Input-Pins>
Die virtuellen Input-Pins betreffen abhängige Operationen, die zuvor zu rufen sind (Operation_FBcl#prevDepending. Die prevDepending
Operation sind allerdings in der genannten eigenen Liste aufgeführt, für die Auswahl werden die virtuellen Input-Pins nicht
benutzt. Sie sind nur informatorisch.
Bei der Outputmask sowoh für evout
als auch dout
gibt es drei Angaben:
<&Special-Output-Pins>/<&direkt genutzte Output-Pins>/<&virtuelle Output-Pins>
Die special.Output-Pins
sind für
evout: linke Ziffer ist triggered output events for statemachine, das ist das Bitwort Operation_FBcl#mEvoutTrgStm. Hier sind alle evout aufgelistet, die abhängig von den States möglicherweise als Folge der Operation (Folge von evin) triggern könnten.
dout: linke Ziffer ist immediate set output variable, das ist das Bitwort Operation_FBcl#mDoutSet. Dieser Output muss für die angegebene Operation beim Aufruf eine zugehörige Variable (dst, left value, output variable) haben. Die Variable wird per Referenz übergeben. In ST ist das eine OUTVAR, in C/++ über einen Zeiger vermittelt und in Java über einee Array-Referenz. Diese Art der Outputgestaltung ist wichtig für bestimme Predefined FBlocks, deren Code in C/++ auch beispielsweise in Simulink als S-Function verwendet wird und dort so gestaltet wird. Die andere Anwendung ist ein FBlock, der nur kombinatorisch ist und daher keine eigenen Daten speichert. Tritt genau eine set-output-variable auf, dann ist diese über den return-Wert der Operation gesetzt. Damit kann der Output-Wert Teil einer Expression sein. Wenn es einen Set-Output gibt, der aber über eine referenzierte Variable angebunden wird, ist zusätzlich noch das Bit 63 gesetzt.
Für die nicht mit mSetDout
gekennzeichneten Outputs gilt, dass mit der Operation der Outputwert intern berechnet ist und über die output-Operation (als
getter) abholbar ist. D.h. der Wert ist innen in der Instanz des FBlock gespeichert.
In diesem Beispiel sind die Output q1 und q2 (Mask c0) die nur vom upd
-Event gesetzt werden, als virtuelle Outputs angegeben. Denn: Der Outputwert bei upd
richtet sich nach dem Setzwert vor dem F_MOVE
-Block (in Simulink ist das ein Unit-Delay). D.h. diese Operation beeinflusst die genannten Outputs, nur eben nicht direkt.
Ein Beispiel für eine OperDout, Output-Zugriffsoperation, ist:
y4 := y4(#9 .$2) ev:8/9->0/4/4 D:2/2->0/8/8
Vor der Operation steht der gesetzte Output, gefolgt von :=
.
Kombinatorische Operationen haben nie Statements, daher steht der ( . )
in der Klammer.
Diese Operation braucht aber interne Variable für ihre Kombinatorik, folglich mit ( $ )
gekennzeichnet.
Die restlichen Kennzeichnungen sind identisch wie bei Event-Operationen.
Topic:.FBclDflow2Ev.
Last changed: 2019-11-26
Dieser Dokumentationsabschnitt befasst sich mit der Umsetzung der Datenflussorientierten Abarbeitung in Eventverbindungen nach IEC-61499 für die FBcl-Abbildung. Dieser Algorithmus wird benutzt für eine Vielzahl Konvertierungen aus von Grafiktools, die eben keine Eventverbindung oder explizite Rechenzeitreihenfolge-Angaben an FBlocks kennen. Ein typischer Vertreter dafür ist Simulink. Auch die Umsetzung aus Automatisierungsgeräteprojektierungen nach IEC-61131 läuft auf diesem Weg.
Topic:.FBclDflow2Ev.prc.
Der Algorithmus ist in der class Dataflow2Eventchain_FBrd
Topic:.FBclDflow2Ev.evCluster.
Die Bildung der Event-Chain aus dem Datenfluss erfolgt in Dataflow2Eventchain_FBcl
srcJava_FBcl/org/vishia/fbcl/readSource/Dataflow2Eventchain_FBrd mit Aufruf der Operation prc(Write_Module_FBwr mdlwr)
, also mit Übergabe des fertig erstellten Moduls aus FBlocks aber noch ohne Eventverbindung.
Wesentlich ist die class EventCluster
srcJava_FBcl/org/vishia/fbcl/readSource/EventCluster_FBrd. Als Event Cluster wird eine Ansammlung von FBlocks, genauer deren Operation oder Input-Events verstanden, die in einer Eventkette hängen, deren
Reihenfolge aber bei der Bildung des EventCluster noch nicht festgelegt ist. Das Kriterium zur Zuordnung zu einem Event-Cluster
ist: Alle diese Operations sind notwendig für genau einen oder bestimmte zusammenhängende Datenausgänge.
Damit ist die Intension des EventCluster bzw. der zu bildenden Event-Chains bereits genannt: Jeweils eine Event-Chain (Kette von Eventverbindungen) ist für eine Ausgangsfunktionaltität (ein Ausgangswert oder zusammengehörige Ausgänge) bestimmt. Dahinter liegt die Idee der
Optimierung: Ausgänge die nicht verwendet werden, müssen auch nicht berechnet werden. Sie liegen in einer eigenen Event-Chain, das Event oder die Abarbeitungsreihenfolge wird dann nicht aufgerufen.
Ablaufzuordnung (Abtastzeit, Folge von Statemachine-Actions): Ein letzlich kombinatorisch gebildeterer unabhängiger Ausgang bzw. zusammengehörige Ausgänge werden in genau dieser Event-Chain aus Eingangswerten gebildet. Es werden die zueinandergehörigen Operationen herausgesucht.
Topic:.FBclDflow2Ev.mevin.
In der mapEvin
werden Instanzen für jede Event-Operation dieses Moduls gespeichert: MetaEvin_FBcl
srcJava_FBcl/org/vishia/fbcl/readSource/MetaEvin_FBrd. Diese sind namentlich sortiert nach (Fblockname+OperationName) und enthalten Informationen, die für diesen Algorithmus der
Event-Chain-Bildung temporär notwendig sind.
Wesentlich sind nicht die FBlocks selbst, sondern die Operation, die den Input-Events der FBlocks zugeordnet sind. Bei Basis-FBlocks ist dies eine 1:1-Zuordnung, es gibt ein Input-Event und genau eine Operation. Komplexe FBlocks können intern mehrere Event-Chain, mehrere unabhängige Funktionalitäten enthalten. Diesen ist je eine Operation ab dem entsprechenden Event-Input des FBlock-Typs zugeordnet, bzw. es kann noch abhängige interne Operationen geben, siehe Chapter: 3.1.5 Operation_FBcl - Info welche Pins beeinflusst werden und Chapter: 6 Bildung von Operations aus den aufeinanderfolgenden Eventverbindungen. Für einen aufgerufenen FBlock sind diese Operationen gebildet (im Typ), sie sind die Bausteine des Moduls. Die FBlocks sind nur die äußerlichen Bausteine oder Funktionsblöcke.
Topic:.FBclDflow2Ev.databack.
Um diejenigen Operationen herauszufinden, die auf einen Ausgang oder zusammengehörige Ausgänge wirken, muss von den Ausgängen ausgegangen werden, also rückwärts.
In der Operation detectEndOutpinFBlockForEvCluster()
werden allen Outports bzw. dessen treibende Outpins des vorgelagerten FBlock EventCluster zugeordnet. Dabei werden Outports
berücksichtigt, die zusammengehören: Diese werden dem selben EventCluster zugeordnet.
Topic:.FBclEvChain.
Last changed: 2020-10-19
Dieser Dokumentationsabschnitt befasst sich mit der Bildung von Operations aus den aufeinanderfolgenden Eventverbindungen.
Diese Bildung der Operations wird nach Einlesen eines FBcl-Modells nach dem Aufbau der Eventverbindungen ausgeführt. Eine Event Chain ('EventChain') ist die Verbindung von FBlocks über ihre evin und evout in einer Reihe ohne Verzweigungen. Ein einfaches Modul kann aus einer Event Chain bestehen. Verzweigungen und Vereinigungen ('E_REND') bilden mehrere EvChain.
Die Abarbeitung eines Moduls könnte nun in der Reihenfolge der EvChains ablaufen, wobei verzweigte EventChains die Möglichkeit der Parallelbearbeitung in Multicoresystemen ermöglichen. Jedoch wird im folgenden Kapitel die Herausforderung oder Anforderung nach Optimierung der Abarbeitung dargestellt. Das bedeutet, dass die EventChain nochmal aufgegliedert wird und kombinatorische Verknüpfungen extra behandelt werden. Zur Entscheidung, was bei der konkreten Nutzung eines Moduls in richtiger Reihenfolge aufgerufen werden muss, bedarf es der Ausführung der angeschlossenen Modul-Pins pro EventChain. Da EventChains bei Verzweigungen auch nur intern im Modul, nicht nur zwischen den Event-Pins auftreten, bedarf es der Speicherung von Abhängigkeiten und sogenannten fiktiver Evin-Pins. Damit entsteht zwar ein komplexes System, das aber für Optimierung der Codeabarbeitung notwendig ist.
Topic:.FBclEvChain.opt.
Folgendes Modul sei als Beispiel aus der Praxis vorgestellt. Es handelt sich um einen Teil einer Feldorientierten Regelung von Motoren oder Netzen, also elektrischen Signalen. Solche Regelungen müssen oft in einer kurzen Abtastzeit gerechnet werden, 50 µs oder 20 µs. Das Erfordernis der kurzen Abtastzeiten ergibt sich daraus, dass elektrische Signale mit niedrigen Induktivitäten entkoppelt werden, es daher bei Störungen hohe Stromanstiegsgeschwindigkeiten gibt, andererseits in Kabeln Schwingungen auftreten können bis in den kHz-Bereich, die durch die Regelung entsprechend gedämpft werden müssen. Die Stellglieder können mittlerweile diese hohen Abtastzeiten realisieren, die Prozessoren können das grundsätzlich auch, aber eine optimierte Programmierung ist Voraussetzung. Bei Embedded-Control hat man häufig die 'Nichtfunktionale Anforderung' Wärme, Stromverbrauch oder Preis des Prozessors. Man hat nicht beliebig technisch machbare Rechenleistung. Dies zur Einordnung.
Das Modul ist in Simulink gezeichnet, aus dem sich FBcl generieren lässt. Im Bild vor dem PID-Regler ist ein Block angeordnet,
der einen komplexen Wert oder Vektor mit zwei Koordinaten in einen Betrag und Winkel umrechnet. Mit dem Betrag gibt es eine
Amplitudenregelung, der Winkel wird ebenfalls weiter verarbeitet und als aAngle
ebenfalls herausgeführt. Das kann eine Null-Regelung des Winkelwertes sein. Diese kann in einigen Fällen notwendig sein,
in anderen Fällen ist der Winkelwert aufgrund anderer Vorkehrungen immer in einem 0-Bereich oder er ist nicht relevant. Folglich
wird von diesem Modul der Winkelausgang nicht immer verwendet.
Mehr noch: In einer Testphase kann der Winkelwert von hoher Bedeutung sein, beispielsweise auch nur zur Beobachtung. In der Releasephase braucht man ihn aber nicht. Es soll also weder eine Modifikation des gleichen Moduls mit und ohne Winkel geben, noch soll in der endgültigen Grafischen Software die Winkelberechnung entfallen, da man für Tests und Erweiterung den Winkelwert immer mal wieder haben möchte.
Für die Rechenzeit gilt: Die Betragsbildung erfordert 2 Multiplikationen, Addition und Wurzel. Das sind beispielsweise bei einem schnellen Prozessor ca. 50 ns. Die Winkelberechnung aus den kartesischen Koordinaten braucht eine arctan2-Funktion. Dies ist eine Reihenentwicklung nach Vorentscheidung, oder je nach Prozessorhardware ein Lookup-Tabellen-Zugriff mit Interpolation, also jedenfalls aufwändiger als die in Hardware realisierte Multiplikation und die einfache Reihenentwicklung der Quadratwurzel Erfahrungwert 200 ns. Die nachfolgende Winkelauswertung mag nochmals 100 ns benötigen.
Im Zeithorizont von 20 µs Abtastzeit mit vielen weiteren Funktionen sind 0.3 µs relativ viel. Wenn diese im Release nicht benötigt werden, sollte es jedenfalls vermieden werden, diese Werte zu berechnen.
Andererseits sollte man nicht zwischen Releases und Versionen nur aus diesem Grund die Grafik ändern, Nachteil mehrfache Datenhaltung, Versionsvielfalt.
In der C/++-Programmierung kennt man das fein gradual eingesetzte Mittel der bedingten Compilierung, das aber Quellen auch nicht gut lesbar erscheinen lässt.
Simulink geht bei seinem Codegenerator so vor, dass bei im Konkretfall ungenutzten Ausgängen die davorliegende Logik vor der Codegenerierung wegoptimiert wird. Ein im Konkretfall ungenutzter Ausgang kann ohne Änderung der Grafischen Programmierung einfach durch Austausch eines nachfolgenden Library-Moduls als ungenutzt (oder im anderen Fall genutzt) bestimmt werden. Dabei braucht ebenfalls nicht die Grafik geändert werden, nur die verwendete Library wird aufgrund anderer Suchpfadeingestellungen getauscht, oder die Testumgebung wird ausgetauscht durch die Aufrufumgebung. Das ist die Wahl des Releasemodus für das Produkt versus einer Testumgebung (Test-Libraries genutzt). Grafisch ist also die bedingte Verwendung von Ausgängen bzw. Schaltungsteilen sehr einfach modifizierbar.
Als Event Chain (EvChain) tritt uns diese Schaltung nun wie folgt entgegen:
Die EvChain für init
und param
sollen nicht weiter interessieren, sie entsprechen anderen Abtastzeiten. Das event-input step1
(=^schnelle Abtastzeit) wird am Cplx2mg gespalten. Der FBlock selbst erzeugt innen zwei Events und hat zwei EvChain, getrennt für Amplitude (magnitude) und Winkel
(angle). Eben weil die Berechnung beider Werte nicht immer notwendig ist. Die gok
EvChain wird dann weitergeleitet auf die Winkelnachbehandlung, hier nur als rad2int
dargestellt, und dann sowohl für das Event als auch den Datenausgang ausgegeben.
Sind nun beide, der angle_cnf
Event-Ausgang und der yAngle
Datenausgang nicht benutzt, dann soll der zugehörige Code auch nicht aufgerufen werden (Rechenzeitersparnis) und möglichst
auch nicht als toter Maschinencode im Embedded Target auftreren. Dabei kann es auch sein, dass die Ausgänge in der Modulaußenbeschaltung
zunächst verdrahtet sind, aber ganz außen dann unbenutzt. Dies muss auch funktionieren. Ganz außen entscheidet also die Wahl
zwischen Testumgebung und Target-Implementierung, ob von diesem Modul die entsprechenden Operationen aufgerufen werden.
Realisiert wird dies durch eigene Operationen für die einzelnen EvChain. Die Winkelverarbeitung bereits aus dem FBlock Clpx2mg ist eine eigene EvChain, der Clpx2mg hat aus diesem Grund ebenfalls bereits zwei Operationen. Es liegt nun an der aufrufenden Umgebung, also der Nutzung des Moduls, ob diese Operationen gerufen werden.
Die Operationen werden dabei immer generiert, da das Modul als solches generiert wird und in einer C/++-Umgebung fertig zur Verfügung steht, unabhängig von dessen Nutzung. Das ist anders als bei Simulink, bei dem auch die innereren Module zunächst formell aufgelöst werden und dann die Reduktion quasi auf Grafikebene vor der Codegenerierung erfolgt. Bei der FBcl-Codegenerierung ist dagegen der generierte Modulcode unabhängig von dessen Nutzung immer gleich; und kann damit einfacher als Second source-generierter Code versioniert werden.
Die Operationen werden für C/++ entweder als inline im Headerfile definiert, oder jeweils einzeln in einem Implementierungsfile. Folglich benötigten nicht gerufene Operationen keinen Platz im Maschinencode, entweder weil der inline-Code nicht genutzt wird oder weil der Linker entscheidet, dass ein Object-File nicht eingezogen wird da es nicht benutzt wird. Der Gesamt- Optimierungsprozess nutzt also noch zusätzlich die bekannten optimierenden Eigenschaften der Compiler und Linker.
Topic:.FBclEvChain..
Die EventChain-Daten Evchain_FBcl
srcJava_FBcl/org/vishia/fbcl/fblock/Evchain_FBcl sind in 3.2.5 Event-Chain-Informationen beschrieben.
Eine Operation Operation_FBcl
srcJava_FBcl/org/vishia/fbcl/fblock/Operation_FBcl ist in Chapter: 3.1.5 Operation_FBcl - Info welche Pins beeinflusst werden beschrieben. Sie enthält insbesondere Code-Generiervorschriften (Rule) für die Event-Funktionen. Die weitere wesentliche
Information sind die von der Operation betroffenen Input- und Ouput-Pins (Daten und Event). Diese Informationen werden für
die Codegenerierung und für die Bildung von Operations im übergeordneten Modulen benutzt
Operations und auch DoutType_FBcl
srcJava_FBcl/org/vishia/fbcl/fblock/DoutType_FBcl enthalten für die Generierung der Event-Operation und für den Zugriff auf das Datenport eine Generier-Vorschrift oder -Regel
(rule) für die Codegenerierung des rufenden Moduls. Die Generier-Regeln sind mit einem OutTextPreparer
srcJava_Zbnf/org/vishia/util/OutTextPreparer realisiert.
Wesentlich sind weiterhin die zugehörigen Pins des Moduls für Operations und Generierregeln.
In Module_FBcl
sind die Instanzen von Evchain_FBcl
für die Übersetzung nach C-Code und Bildung der Operations in evChains
referenziert.
Topic:.FBclEvChain.build.
Last changed: 2020-07-24
Der Algorithmus ist in srcJava_FBcl/org/vishia/fbcl/readFBcl/BuildEvchainOperation_FBcl enthalten, startend mit der Operation execute(...)
.
Für alle Event-Input (Port-Pin) des Moduls wird newEventChains(...)
gerufen und in evChainList
eingetragen.
Für alle Event-Output aller FB im Modul, die von Statemachines getriggert werden (Kennzeichnung EvoutType_FBcl.bTrgFromStm
) wird newEventChains(...)
gerufen.
In newEventChains(...)
werden alle Event-Inputs berücksichtigt, die am initial gegebenen Src-Event-Output liegen. Für alle Event-Inputs wird im
FBlock_Type_FBcl
der betreffenden FBlock_FBcl
-Instanz getestet, ob es dazu mehrere Operationen gibt. Folglich werden möglicherweise mehrere Evchain_FBcl
initial angelegt und im Arbeitscontainer evChainList
aufgenommen.
Die evChainList
wird dann vom ersten Eintrag an abgearbeitet und dabei geleert (Evchain_FBcl evChain = evChainList.remove(0);
). Es wird buildEvchain(...)
für jeden Eintrag gerufen:
In BuilldEvchainOperation_FBcl#buildEvchain(...) werden die Eventverbindungen über die FBlocks verfolgt, wobei die Zuordnung der Evout-Pins in einem FBlock zu den Evin-Pins aus der EvinType_FBcl#operations, dort der Operation_FBcl#mEvout entnommen wird. Bei einfachen FBlocks mit einem Event-In- und -Output ist die Sachlage klar. Bei komplexen FBlocks kann von außen nicht ausgesagt werden, an welchem Evout die EventChain fortgesetzt wird, nur über diese Event-Operation. Das Evout muss unmittelbar aufgrund des Evin erzeugt werden. Ist dort eine Statemachine dazwischen, dann ist das Evout nicht unmittelbar, es wird ab dem Statemachine-Evout eine eigene EventChain gebildet.
Alle entsprechenden Pins der FBlocks werden mit der EventChain markiert, eingetragen in Pin_FBcl#mEvChains. Damit kann in den Folgeschritten buildOperDout(...)
erkannt werden, welche EventChain zu den Datenpins gehört.
Wenn die Eventverbindung gabelt, also an einem Evout mehr als ein Evin verdrahtet ist, dann entstehen ab dem Gabelpunkt neue
EventChains. Da EventChains immer mit einem EvinType_FBcl starten, wird dafür intern eine Instanz angelegt und im Interface zum Modul FBlock_Type_FBcl#evinOper gespeichert. Das ist im ObjectModelDiagram oben mit nextDepending
und prevDepending
dargestellt: Die EventChains bzw. die zugeordnete Operationen sind abhängig als Folgeoperation unmittelbar.
EventChains die nicht unmittelbar folgen sondern mit einer Statemachine, sind nicht als abhängig dargestellt. Sie werden immer in die Codegenerierung aufgenommen und aus dem Statemaschinencode heraus aufgerufen. Es sei denn, die Statemachine ist nirgends mit Event-Inputs versorgt. Das kann sein, wenn Spezial-FBlocks davorgeschaltet sind, die unter bestimmten Umständen (außen vorgegeben) keine Events erzeugen. Diese Möglichkeit ist vorhanden, damit eine Grafik ein vollständiges System abbildet, von dem jedoch in einer konkreten Anwendung (konkrete Codegenerierung) nur ein Teil benutzt wird.
Nach Fertigstellung dieses ersten Schrittes, Aufstellen der EventChains als solche, wird für das Debugging ein File NAME.fbcldata_1
geschrieben. Dieser enthält die EventChains und Event-Operationen, aber noch keine kombinatorischen Operationen und noch
keine Erkennung von Outputs, die Instanzvariable benötigen wegen Nutzung der Daten in anderen EventChains. Zur Darstellung
in diesem Reportfile siehe 4.3 Ausgabe der Daten des gelesenen Datenmodells für Testzwecke.
buildOperDout(...)
Im zweiten Schritt werden in buildOperDout(...) ausgehend von allen Datenausgängen des Moduls (rückwärts im Datenfluss) die kombinatorischen Output-Operationen gebildet. Wenn diese auf dout von FBlocks treffen, die noch in anderen EventChains liegen, dann werden diese Ausgänge mit Dout_FBcl#bObjectVar bezeichnet und es werden bei der Codegenerierung Instanz- bzw. ObjectVariable erzeugt.
Wenn ein Dout aufgabelt, aber in der selben Operation verwendet wird, dann ist das eine Dout_FBcl#bLocalVar
Nach Fertigstellung dieses zweiten Schrittes, Aufstellen der EventChains als solche, wird für das Debugging ein File NAME.fbcldata_2
geschrieben. Vergleicht man beide *.fbcldata_?
files, dann kann man das Ergebnis diesen Schrittes erkennen.
buildOperationsInEvchain()
Diese Operation für jede EventChain in Evchain_FBcl.Wr#buildOperationsInEvchain--|Evchain_FBcl.Wr#buildOperationsInEvchain() ordnet nun Ein- und Ausgänge der
XXX
Wenn als Sonderfall das initial eingetragene evinStart
ein Outport ist (evinCurr.fb == null
), dann handelt es sich um eine Direktverbindung Event-Input auf Output. Dann wird dennoch dafür eine Operation gebildet und
das Eventout dort in den jeweiligen Bitmasken vermerkt (operation.setEvoutBit(...)
).
Im Normalfall startet die Untersuchung der Eventchain mit der Operation processEvchain(...)
.
In der buildEvchain(...)
gibt es folgenden Ablauf für die Traversierung durch die Event-Chain:
Das evinStart
ist das evStart
aus der evChain, das erste evin eines FBlocks im Wiring des Moduls (Composite FBlock) nach dem Input-Event. Damit startet
das evinCurr
.
Ist das erste evinCurr
bzw. dann auch evinStart == null
dann ist im Wiring das evin des Moduls direkt auf ein evout verdrahtet, oder es ist gar nicht verdratet. Dann ...TODO. Damit
ist diese Eventchain gebildet.
Ansonsten wird das evinCurr
fortlaufend abgearbeitet und in die evChain aufgenommen. Eine evChain ist nur eine Kette einer direkten Eventverbindung bis
zu einer Gabel.
while(evinCurr !=null) { evChain.add(evinCurr); EvinType_FBcl evinType = evinCurr.evinType();
Es wird das EvinType_FBcl evinType
vom evinCurr
ermittelt , vom referenzierten FB-Type im Modell. Dieses enthält die Typinformationen zum Event.
Es wird die Operation_FBcl
vom ..evinType ermittelt. Im Falle es gibt mehrere für das erste Event, wird die richtige Operation über den
Evchain_FBcl.ixOperation() geholt.
Operation_FBcl operEvinType = evinType.getOperation(ixOperationEvchainFirst); bOutputNotToPort = checkDinDout(evinCurr, evChain, operEvinType, mdlwr);
Im checkDinDout
wird jedes din, das dem Event des currFB
zugehört, auf die treibende dout-Verbindung aus einem anderen FB im Modul getestet. Kommt diese direkt vom Moduleingang,
dann wird mit
evChain.assignDin(doutIfc);
sowohl dem dinPort des Moduls die Operation zugeordnet, der evChain das Bit zugeordnet: srcJava_FBcl/org/vishia/fbcl/fblock/Evchain_FBcl#mDin als auch der Operation über operation.setDinBits(mBit);
das Bit gesetzt.
Wenn die Verbindung von einem anderen FB kommt dann wird getestet, ob der Output in der zur evChain
gehört und bereits berücksichtigt ist. Ist das nicht der Fall oder hat der Output mehrere Connections, dann wird dafür eine
Zwischenvariable benötigt. Haher wird im Dout_FBcl
bPresentedByVar
gesetzt. Ist das nicht der Fall, dann ist alles schon erledigt. Auf den Output wird dann bei der Codegenerierung direkt zugegriffen
(ohne Zwischenvariable). Das ist dann wesentlich wenn der Output eine Kombinatorik ist. Seine Output-Operation kann Bestandteil
einer Expression sein und somit optimal für den Zielcode.
dd
Im checkDinDout
werden alle Dout, die im operEvinType
der Event-Operations zugeordnet sind, getestet:
Wenn für einen Output isPresentedByVar()
gesetzt ist, dann muss dieser Output von der Event-Operation versorgt werden, folglich muss die Eventchain-Operation gerufen
werden. Mit evChain.setDinBitsThisToOperation();
werden sowohl alle bisher in mDin
aufgesammelten Input-Bits zur Operation erklärt:
/**Transfers the yet need Input Port pins to the operation. */ public final void setDinBitsThisToOperation() { this.operation.setDinBits(this.mDin | Operation_FBcl.mUsesThis); this.mDin = Operation_FBcl.mUsesThis; //the access to the Dout needs This anyway. }
als auch der Instanzzeiger wird als Inputargument erklärt (mUsesThis
).
Es werden alle Wirings vom dout zu anderen din geprüft.
Ist der Ausgang direkt an einen Modulausgang geführt, dann gibt es eeine Entscheidung: if(!mdlwr.bCombinatoricDout)
. Ist das der Fall, dann wird mit dout.setPresentedByVar()
bestimmt, dass der Output bei der Codegenerierung mit einer Variablen präsentiert wird. Im anderen Fall muss die Codegenerierung
auf die Output-Access-Routine des liefernden Output zugreifen, was für Kmobinatorik optimaleren Code liefert.
In buildEvchain(...)
wird mit checkDependingOperations(...)
getestet, ob die Operation des Types des FBlock abhängige Operationen enthält. Das ist dann der Fall, wenn in der Funktionalität
des Typs Events gabeln. Das ist nie im ersten Input-Event der Fall, denn dann gäbe es mehrere Eventchain
Wenn es innerhalb der Eventchain im Typ des aktuellen (current) FBlock eine abhängige operEvinType.iterDepNext()
Operationen gibt, dann muss auch für das abgearbeitete Modul, also das übergeordnete, eine abhängige Operation geben. Diese
wird dann gebildet, auch für mehrere abhängige Operationen an der gleichen Stelle.Es wird damit eine neue EvChain_FBcl
gebildet, die mit einem fiktivem (virtual) EvinType_FBcl
im Typ startet. Dieser Algorithmus ist ausführlich in Chapter: 6.6 Abhängige Operationen dargestellt.
Treibt das currEvin
im Typ festgestellt mehrere Event-Outputs, dann ist damit die Eventchain beendet. Es werden aber folgende EvChain_FBcl
initiiert und in evChainList
eingetragen, also später abgearbeitet. Eine Evchain_FBcl
ist immer nur eine einfache Eventkette.
Ist genau ein Event-Output im aktuellem FB dem Event-Input zugeordnet, dann wird dieses Event-Output ermittelt. Es wird die Verbindung danach geprüft.
Ist das Event-Output auf mehrere Event-Input geführt (Event-Gabelung, fork) oder hat das eine Event-Input mehrere Operation,
dann ist die Event-Chain beendet. Es werden aber ab diesem Event-Output als eventSrc
neue Event-Chains intitialisiert und in evChainList
eingetragen.
Nur wenn genau ein Event-Output dem current Event-Input folgt, dieser genau auf ein Event-Input verdrahtet ist und diesem
Event-Input nur eine Operation im FBlock-Typ zugeordnet ist, wird dieser current Event-Input in die Liste in der EvChain_FBcl
-Instanz eingetragen und die das folgende Event-Input als current weiter verfolgt.
Ist kein Event-Output vorhanden oder ist das Event-Output nicht weiter verdrahtet, dann ist die Event-Chain mit dem bereits eingetragenem current Event-Input beendet. D.h. die Operation dieses Event-Input wird als letztes berücksichtigt, eine weitere gibt es nicht. Adäquat ist die Event-Chain genauso beendet, wenn es mehrere Folge-Events oder mehrere Event-Operation im Folge-Event gibt. Der Unterschied ist: Die Folge-Events erzeugen neue Event-Chain und neue Operation.
Für das jeweilige aktuelle (current) Event-Input wird festgestellt, ob im Typ verzeichnet es dafür getriebene Output-Datenpins gibt. Das gilt auch für das letzte Event-Input, das eingetragen ist aber keinen Nachfolger hat.
Wenn ein Output-Datenpin direkt ein Out-Port treibt, dann wird in der zugehörigen Operation das zugehörige Bit gesetzt. In den abhängigen Vorgänger-Operationen wird das zugehörige virtuelle Bit gesetzt. Das bedeutet, dass die Operation im Modul dasjenige Datenbit treibt, für die Auswertung beim Aufruf.
Topic:.FBclEvChain.operPortBits.
Wie in Chapter: 3.1.1 EvinType mit Operation, Pin-Zuordnungen, Codegenerier-Regel für Event-Handling und Chapter: 3.5 Codegenerier-Regeln in Operation und Output-Datenpins dargestellt enthalten die EvinType_FBcl
-Instanzen im FBlock_Type_FBcl
in der referenzierten Operation_FBcl
Bitmasken, die die Pins der Operation kennzeichnen. Diese Information wird bei der Bildung der Event Chain erfasst und dort
gespeichert. Die Generierregel für den Zielsystem-Code für das jeweilige Event wird jedoch erst mit der Codegenerierung Chapter: 7 Codegenerierung aus FBcl eingetragen.
Die Operation checkDinDout(...)
wird für jedes Input-Event der Chain gerufen. Über den Typ des zugehörigen FBlock werden die im FBlock genutzten Pins ermittelt.
Für die Basis-FBlocks stimmen diese, für alle gerufenen FBlocks als Submodule werden diese mit eben diesem Algorithmus zuvor
ermittelt.
for(Din_FBcl din: operEvinType.iterDin(evinCurr.fb)) {
Der iterDin(...)
ist speziell gebaut und liefert die Data-Inputs von der FBlock-Instanz zurück unter Kenntnis der Operation des Typs. Es wird
die Verdrahtung des Daten-Input-Pins geprüft:
Ist das Input-Pin mit einem Port des Moduls verbunden, dann wird über evChain.operation.setDinBit(doutSrcPin.ixPin)
das entsprechende Bit in der neu gebildeten Operation zum Event-Input des Moduls gesetzt. Es ist ein notwendiges faktisches
Input-Pin, unabhängig von der Zuordnung der Input-Pins im Interface.
Kommt der Eingang von einem anderen FBlock, dann wird dort geprüft mit checkEventChain(...)
:
Ist der Datenausgang als Quelle in der selben Event-Chain bereits erfasst und hat nur einen Ausgang, dann ist das ein Ausgang zur Bildung einer Expression in der Chain.
Ist der Datenausgang noch keiner Eventchain zugeordnet oder einer anderen Eventchain, dann liegt dieser nicht in der Abarbeitungsreihenfolge
davor. Daher wird dieser Ausgang mit Dout.bPresentedByVar
markiert so dass bei der Codegenerierung der dort erzeugte Value auf einer Instanzvariable des Moduls gespeichert wird. Damit
ist der Wert jedenfalls verfügbar, abhängig vom Ablauf der Eventverarbeitung entweder als Wert aus einem anderen Event, oder
auch aus einem gespeicherten Wert der vorigen Abarbeitung.
Ist der Datenausgang an mehr als einen Dateninput angeschlossen, dann wird unabhängig davon, dass der Wert in der Reihenfolge nur in der eigenen Event-Chain davor liegt, der Wert dennoch in einer Instanzvariable gespeichert. Er wird damit in Statements der zu bildenden Event-Operation ermittelt, gespeichert und kann an den Nutzungsstellen per Variablenzugriff abgeholt werden.
Weiter werden über operEvinType.iterDout(...)
alle von der Operation des Typs gesetzten Daten-Outputs des current FBlock getestet. Es wird im Pin_FBcl
der FBlock-Instanz die Zugehörigkeit zur Event-Chain gespeichert.
Steht dort bereits eine Zugehörigkeit, meist einer anderen Event-Chain, dann muss der Output jedenfalls als Instanzvariable gespeichert werden.
Ist das der erste Event-Chain-Eintrag, dann wird bPresentedByVar
nicht verändert. Initial ist false
, also keine Instanzvariable. Es kann aber sein dass zuvor bereits ein Eintrag true
wegen Nutzung in einer anderen Event-Chain erfolgte.
Es wird getestet, ob der Datenausgang direkt mit einem Datenport des Moduls verbunden ist. Die zugehörigen Bit in der Operation
werden mit evChain.operation.setDoutBit(dinDst.ixPin)
gesetzt.
Es wird abhängig von der Argumentenvorgabe bCombinatoricDout
der Output als zu Speichern in einer Instanzvariable markiert. In diesem Fall können die Ausgangswerte jedes Moduls im Debuggingfall
auch im nachhinein aus der Variable gelesen werden, was ein wichtiger Vorteil bei einer Fehlersuche sein kann. Die Möglichkeit,
auf die Output-Instanzvariable zu verzichten, wenn der Output nur einmal dann in einer Expression verwendet wird, ist gegeben.
Dies reduziert die Anzahl der Instanzvariable und damit den Speicherplatz, was für Anwendungen mit kleinen Prozessoren im
Embedded-Bereich wichtig sein könnte.
Wenn ein Ausgang als Instanzvariable gespeichert werden soll, wird immer in der Operation zum Event des Moduls über setDinThisNecessary()
im Bit 63 des mNecessaryDin
-Bitwortes gespeichert, dass die Operation einen Instanzzeiger (this
, THIS_OBJ
) benötigt.
Die Information über mit der Operation beeinflusste Output-Pins (mDrivenDout
usw.) entscheidet im Zusammenhang mit der tatsächlichen Verdrahtung der FBlock_FBcl
-Instanz im Modul darüber, ob die Operation im Typ zur Bildung von Statements im generierten Code für die Operation bei der
Abarbeitung der EvChain_FBcl
genutzt wird, siehe Chapter: 3.1.5 Operation_FBcl - Info welche Pins beeinflusst werden.
Die Operations in den Typen der einzelnen FBlocks in der Event-Chain bestimmen folglich die Statements der Codegenerierung.
Die CodeGen_FBcl
-Infos zu den Datenausgängen eines FBlocks im Typ bestimmen die Elemente der Expressions in den generierten Statements.
Topic:.FBclEvChain.depOper.
Es geht hierbei um Optimierung der Codegenerierung. Aus Sicht der optimierten Codegenerierung für schnelle Zielhardware ist es wichtig, dass nur die Abarbeitung von notwendigem Code generiert wird. Ein Submodul, das für verschiedene Einsatzzwecke konzipiert ist, ist oft umfangreicher als in der konkreten Applikation benötigt. Beispielsweise können Berechnungen enthalten sein, die zwar im Allgemeinfall oder für Tests benötigt werden, nicht aber beim konkreten Einsatz.
Bei Programmierung von Hand (Quelltext) stehen diese Programmteile im Quelltext, möglicherweise in verschiedenen Sourcefiles. Es wird manuell ausgewählt, welche Operationen gerufen werden bzw. welche Quellen überhaupt eingebunden werden.
Wird grafisch programmiert, dann ist das Modul als solches als Funktionsblock einmalig da. Die adäquate Herangehensweise der Auswahl, welche Teilfunktionen genutzt werden, sollte damit erledigbar sein, dass nicht alle Ausgänge benutzt werden. Die Signale werden also in der Weiterverarbeitung nicht benötigt. Sie müssen dann auch nicht im Modul berechnet werden.
Das Problem wird bei der Codegenerierung im Simulink (als Beispiel einer etablierten grafischen Programmierung mit guter echtzeitfähiger Codegenerierung) so gelöst, dass vor der eigentlichen Codegenerierung das gesamte Modell abgeflacht wird, Modulgrenzen werden also aufgelöst außer für sogenannte Atomic Subsystems. In dem 'flachgeklopften' Modell kann dann besser entschieden werden, welche Teile weggelassen werden können, da deren Ausgänge nicht benutzt werden. Unterschieden wird dabei auch nach reinen sogenanten Sinks, die eine interne Funktionalität auch im Zielsystem haben müssen oder eben nicht. Diese Herangehensweise hat allerdings den kleinen Nachteil, dass im generierten Code Module nicht mehr erkennbar sind. Eine entsprechende Zuordnung wird dort über Beschreibungen in HTML-Files oder spezielle Kommentare im Code relalisiert, womit sogar die Rückverfolgung bis in die Quell-Grafik möglich ist. Das ist in dem in sich geschlossenen Tool eine durchaus probate Herangehensweise.
Für die FBcl wurde eine ähnlich leistungsfähige andere Herangehensweise genutzt, die die Modulgrenzen nicht auflöst. Dazu
werden im Modul die Operationen gebildet. Jede Operation kann nun gerufen werden oder nicht, abhängig von der Benutzung der
Ausgänge. Die Codegenerierung selbst erfolgt für alle Operationen für C/++ als inline
-Funktion im Headerfile. Damit wird auch nicht Speicherplatz für den Maschinencode belegt für nicht aufgerufene Operationen.
Der C/++-Compiler entscheidet selbst richtig, wann er umfangreiche mehrfach genutzte inline
-Funktionen nicht-inline realisiert, im Zuge der Optimierungseinstellungen für Speicherplatz und Laufzeit. Dies können moderne
Compiler.
Es ist nun diesbezüglich zu beachten, dass eine Operation eines Submoduls zwar im aufrufenden Modul verdrahtet ist, also aufgerufen werden muss. Aber es kann sein, dass eine oder mehrere Ebenen darüber dann diese Pins nicht angeschlossen sind. Um hierbei die Optimierungsfähigkeit nicht zu verlieren, muss eine bedingte Operation im Submodul ähnlich kleinteilig im aufrufenden Modul bereitgestellt werden, damit in dieser Kleinteiligkeit die Optimierung möglich ist. Im aufrufenden Modul ordnet sich eine Operation im Submodul ein in die EventChain im aufrufenden Modul. Die Information darüber, welche Event- und Datenbits die Operation im Submodul nutzt, ermöglicht es die Event-Chains unter Einbeziehung der Operationen des Submoduls als Teilfunktionalität richtig zu bilden. Wenn eine Event-Chain dann in irgendeiner rufenden Ebene letztlich nicht benutzt wird, entfällt der Aufruf dieser Operation. Wenn eine Operation eines Submoduls nicht benutzt wird, dann wird diese von vornherein in keine Event-Chain eingebunden.
Topic:.FBclEvChain.depOper.
In einem Modul kann es Operations geben, die nicht mit einem Event-Input starten sondern aufgrund einer Eventverzweigung im Inneren des Modul angeordnet sind. Auch hierbei ist es abhängig von der Modulbeschaltung, ob diese Operation bei der Codegenerierung berücksichtigt werden soll.
Das folgende Bild zeigt eine abhängige Operation im Submodul, die zu einer abhängigen Operation im aufrufenden Modul führt.
Das Bild zeigt ein Module_FBcl
dass über das Network_FBcl
einen FBlock_FBcl
referenziert (dunkelblau). Der FBlock referenziert seinen FBlock_Type_FBcl
in grün. Dieser enthält über EvinType_FBcl
eine Operation, deren Code also generiert wird in der Event-Chain des Moduls, die links in hellblau dargestellt ist. Die
Operation im Submodul mit ihrer Code-Generiervorschrift in CodeGen_FBcl
erzeugt im generiertem Code des aufrufenden Moduls für die dem Event zugeordneten Operation entsprechende Aufrufzeilen pro
FBlock.
Nun hat aber der aufgerufene FBlock in seinem FBlock-Typ verzeichnet eine abhängige (nextDepending
) Operation, die als Folge der ersten Operation links unten aufgerufen werden könnte, dargestellt in organge. Diese unabhängig
bedingte Operation wird nun auch für das aufrufende Modul (in hellblau) unabhängig realisiert. Dazu wird eine zugehörige Event-Chain
und im Interfaces des Moduls eine Operation angelegt, auch in organge gekennzeichnet. Diese startet nun nicht mit einem vorhandenen
Event-Input des gerufenen FBlock, sondern mit dem im Interface des gerufenen FBlocks vorhandenen evOper
, einer zusätzlichen EvinType_FBcl
-Instanz der kein äußeres Eventpin zugeordnet ist. Dies gehört zu der nextDepending
-Operation.
Damit die EventChain im aufrufenden Modul gebildet werden kann, werden dort zusätzliche Instanzen von Pin_FBcl
eingeführt, die nur von der EvChain_FBcl
referenziert werden als evSrc
und evStart
. Das evStart
referenziert dabei über einen negativen gekennzeichneten Index das zugehörige evOper
im Typ des gerufenen FBlocks, damit die Operation dort ausgelesen und für die Codegenerierung verwendet werden kann. Diese
EventChain startet also im Inneren des gerufenen FBlock, wie es der Typ-Operation entspricht. Die EventChain wird dann über
das zugeordnete EvoutType_FBcl
(rechts unten, grün) über das zugehörige evout
der gerufenen Block-Instanz vom Typ Pin_FBcl
(rechts, blau) im Modul außen fortgesetzt. Würden am evout
mehrere evin
hängen, oder würden mehrere Operationen im Folge-FBlock existieren, oder wäre das evout
unverdrahtet, dann enthält diese Event-Chain nur das eine Element, die nextDepending
-Operation. Die Operation könnte aber Daten-Outputs treiben und kann daher nicht entfallen.
Das gleiche Schema gilt für weitere nextDepending
-Operationen. Eine Operation kann auch mehrere nextDepending
haben. Die evOper
-Instanzen können also vielfältig sein, es funktioniert genau so wie alle anderen evin
-bestimmten Event-Chains.
Als Code-Snippet ist dieser Algorithmus wie folgt realisiert (in BuildEventChainOperation_FBcl):
private static void checkDependingOperations(Write_Module_FBwr mdlwr, Pin_FBcl evinCurr , Operation_FBcl operCurr, Operation_FBcl operEvinType, List<Evchain_FBcl> evChainList ) { // // operEvin ----> operNext ? // for(Operation_FBcl operNextType: operEvinType.iterDepNext()) { //depending Operation // // Operation --> dependingOperation with evOper as Evin_FBcl: // EvinType_FBcl evopNextType = operNextType.event; //The inner evOper of the type String nameOperNext = evinCurr.fb.name() + "_" + operNextType.name; // //Create an evOper in the module type which is associated to the operation //of the depending newly created eventChain in the current module: EvinType_FBcl evinOper = mdlwr.mdlifcCreate.addEventOper(nameOperNext); int nrPinOperNext = -1-evinOper.ixPin; //The negative nr marks an Operation event. //Create a virtual event which is associated to the Evin_FBcl of the modul's type. Pin_FBcl evsrcOpNext = new Pin_FBcl(Pin_FBcl.EPinKind.Evop, operNextType.name , nrPinOperNext, null); //ficitive inner event // //Create a virtual event Pin which is connected to evsrcOpNext, //it is the first pin of the chain and refers evopNextType via fb and ixPin int nrPinStartNext = -1-evopNextType.ixPin; //The neg nr marks an Operation event. Pin_FBcl evstartOpNext = new Pin_FBcl(Pin_FBcl.EPinKind.Evin, operNextType.name , nrPinStartNext, evinCurr.fb); //fictive inner event evstartOpNext.connectFrom(evsrcOpNext); // //The evChain (maybe more as one) has its source evin in the virtual evsrcOpNext. newEventChains(evChainList, evsrcOpNext, evinOper, null, operCurr, mdlwr); } }
Der eigentliche zugehörige Quellcode ist also kürzer als diese Erklärung, da selbstverständlich mit dem Aufruf von newEventChains(...)
auf den gleichen Algorithmus wie auch für andere Event-Chains zugegriffen wird.
Die Codegenerierung testet zusätzlich zur Verdrahtung der FBlocks auf vorhandene nextDepending
-Operations und testet wie bei allen Operations, ob die Ausgänge der Operationen benötigt werden.
Topic:.FBclCgen.
Last changed: 2020-07-17
Dieser Dokumentationsabschnitt befasst sich mit der Codegenerierung von Ablaufcode für embedded Plattformen aus dem FBcl-Datenmodell. Die Codegenerierung ist auf C und C++ bezogen dargestellt, kann jedoch adäquate auch in Java, ST oder andere adäquate Programmiersprachen erfolgen. Die Codegenerierung wird von einem Template-file gesteuert, der alle sprachrelevanten Dinge enthält. Damit kann angepasst werden.
Inwieweit sich die Codegenerierung dann auch auf ST bezieht, um beispielsweise große grafische Blöcke in FBcl in einen IEC 61499-textuellen FBlock zu generieren (Basic oder Simple FBlock), muss noch erarbeitet werden.
Die Files in FBcl werden entweder in der IEC 61499-textuellen Notation für Composite FBlocks erwartet, oder in der entsprechenden XML-Notation beispielsweise aus einer 4diac-Grafik. Beide Inputs sind gleichwertig. Diese Files könnten beispielsweise zuvor aus Simulink erzeugt worden sein oder mit einem IEC 61499-Tool wie 4diac IDE bearbeitet und gespeichert worden sein.
Die Codegenerierung ist nicht notwendig wenn eine andere Funktionsblockdarstellung beispielsweise aus Simulink gelesen und in die FBcl-Datendarstellung konvertiert werden soll. Für die notwendige Erstellung der Eventverbindungen ist die Bildung von Event-Chains und Operation ausreichend, siehe Chapter: 6 Bildung von Operations aus den aufeinanderfolgenden Eventverbindungen.
Topic:.FBclCgen.TplCgen.
Das Templatefile wird mit der class srcJava_FBcl/org/vishia/fbcl/translate/ReadTranslationScripts eingelesen. Es hat folgenden Aufbau (Beispiel C-Codegenerierung, File org/vishia/fbcl/translate/cHeader.txt
):
Einzelne Abschnitte sind ab Zeilenanfang mit ====ident====
gekennzeichnet, gehen bis ====
und werden mit diesem ident
aufgesucht. Zwischen diesen Schlüsselzeilen stehen die Regeln für die Codegenerierung. Diese liegen in einer Notation vor,
wie es für srcJava_vishiaBase/org/vishia/util/OutTextPreparer notwendig ist. Diese Klasse wird dann auch zur Generierung verwendet.
====include==== <:args:depending><: > #include <&depending>.h ====
Block, der Abhängigkeiten der Quelle enthalten soll, für Java sind das die import
statements. Für C werden pro Modul Headerfiles includiert, die entweder selbst zuvor generiert wurden, oder vorhanden sind
wenn FBlocks in vorhandenem C/++ bzw. als Quellen einer anderen Programmiersprache gerufen werden.
Die Zeile <:args:name , name2>
gibt die Namen notwendiger Argumente an. Diese im Script angegebenen Argumente werden von innen bei der Codegenerierung versorgt.
Sie sind also hier nicht bestimmbar sondern müssen wie vorgegeben heißen. In diesem Fall enthält depending
die Bezeichnung des Moduls, für das einen Abhängigkeit besteht. Für die C-Programmierung wird damit die #include ...
-Zeile gebildet. Die Codegenerierung ruft dieses Template mehrfach auf, je einmal pro abhängigem Modul.
Das Token <: >
bedeutet, das Whitespaces im Template angegeben für die Ausgabe ignoriert werden. Damit fängt die Ausgabe direkt bei #include
an und schließt mit dem Zeilenumbruch ab, der vor der ====
-Endezeile steht.
====class==== <:args: name, varList> typedef struct <&name>_T {<: > <:for:var:varList> <&var.sType> <&var.name>;<.for> } <&name>;
Block einer Typdefinition
Es gibt also 2 Argumente, name
und varList
. Letztere ist ein Container, der mit sType
und name
alle inneren Module enthält, die dieses Modell, für das der Code generiert wird, benötigt. Für C++ und Java müssen hier noch
die Operationen eingebunden werden (TODO in 2020-07). Wobei für Java die vollständigen Bodies der Funktionen hier notwendig
sind.
====operation==== <:args:docu, typeName,operName,retType,isStatic, args, statements, retValue> /**<&docu> */ inline <&retType> <&operName>_<&typeName> (<:if:isStatic==0> <&typeName>* thiz<.if><&args> ) { <:if:statements><&statements><.if><:if:retValue> return <&retValue>;<.if> }
Dieses Template beschreibt wie eine Operation, in C eine Funktion zu bauen ist. Da dieses Template für den headerfile gilt, wird also im Headerfile eine inline-Definition angegeben
Die Argumente sind etwas vielfältig. Die Namen sollten selbstdokumentierend sein.
Für C wird hier ein thiz
-Argument gebildet, der Zeiger auf die Daten des eigenen Moduls, wie oben bei ==class==
angegeben. Das ist in C ObjektOrientiert.
Die statements
sind ein Block, der mit den noch folgenden Templates bereits fertig aufbereitet als Argument übergeben wird und mit einem
'newline' abschließt. Daher ist in gleicher Zeile fortgesetzt mit der Abfrage, ob ein retValue
als !=null
übergeben wurde.
====setOutput==== <:args:out, expr><: > <: >thiz-><&out> = <&expr>; ====
Dieses Template wird verwendet, wenn im FBcl-Datenmodell ein Output des Moduls gesetzt oder verdrahtet ist und ein Wert aus 'Expression' in der Codegenerierung dort zugewiesen wird. Damit wird ein Statement erzeugt.
Es ist eine Zeile mit 2 spaces als ident. Der Output wird über das thiz
angesprochen, wie es in ==operation==
im Code erzeugt wurde.
====getOutput==== <:args:outVar>thiz-><&outVar>====
ist notwendig wenn eine Variable aus der eigenen Datenstruktur zurückgelesen wird, also etwas was auch als output ausgegeben
wird, weiter verarbeitet wird. Es wird kein Zeilenumbruch erzeugt. Daher steht das abschließende ====
hier in der selben Zeile.
====get_thisFBobj==== <:args:FBobj>thiz-><&FBobj>====
Dieses Template dient dazu auf einen FBlock zuzugreifen der im innereren verwendet wird. Er ist als Instanz (Object) in der
struct
defininiert, ide mit ==class==
generiert wurde.
====codeRuleEvinCall==== <:args:nameOp, type, THIS_OBJ, args, sep><: > ##<:<&> produces <&, <&arg> produces the value of the arg <: ><&nameOp>_<&type> ( <:<&THIS_OBJ>><:set:sep=', '><: > <:for:arg:args><&sep><:<&><&arg>><:set:sep=', '><.for> ); ====
Dieses Template (oder Regel, Rule) beschreibt wie ein Aufruf aufgrund einer Eventverbindung aussehen soll. Die Eventverbindung
ruft hier eine Operation des FBlocks auf. Das THIS_OBJ
wurde zuvor nach der oberen Regel gebildet, die aber nicht nur für diesen Aufruf verwendet wird.
Die args
sind ein Container, der über <:for:...>
abgearbeitet wird.
Mit <:set:sep=', '>
kann die Variable (als Argument übergeben) sep
geändert werden. Hier wird sie also in jedem Fall auf ", "
gesetzt.
====statement==== !assign= = ==== !ne = != ==== !eq = == ==== ====
Dieses Template enthält nur die Definition von Werten zu Variablen, die für SStatements verwendet werden. Beispiel assign
: Der Zuweisungsoperator ist in Structure Text mit :=
geschrieben, wäre hier anzugeben. In C ist es das =
.
Topic:.FBclCgen.trl.
date=2020-07-24
Die folgende Routine der class srcJava_FBcl/org/vishia/fbcl/translate/Translation2Target_FBtrl ist eingebettet in ein static public translate, die von außen gerufen wird:
private void translate(Module_FBcl mdl, File genSrcDir, int recursion) throws IOException { if(recursion >100) throw new IllegalArgumentException("more than 100..."); this.varMap = new TreeMap<String, VariableDef_FBtrl>(); // for(Map.Entry<String, FBlock_FBcl> efb: mdl.getFBlocks().entrySet()) { FBlock_FBcl fb = efb.getValue(); FBlock_Type_FBcl fbt = fb.getTypeFB(); Module_FBcl submdl = fbt.getModule(); if(submdl !=null) { //TODO check data types to translate String fbName = StringFunctions_B.toFirstLowercase(fb.name()); String fbtName = StringFunctions_B.toFirstUppercase(fbt.name()); this.varMap.put(fbName, new VariableDef_FBtrl(fbName, fbtName)); // //====> for sub modules Translation2Target_FBtrl.translate(submdl, genSrcDir, this.trlScripts , recursion+1); } }
Es werden zunächst alle im Modul enthaltenen FBlocks als Submodule komplett übersetzt. Damit wird diese Routine über das static public translate rekursiv gerufen.
Topic:.FBclCgen.evChain.
Vom eigenem Modul werden dann die im Datenmodell gebildeten Eventchain abgearbeitet. Das sind die getrennten Abläufe im zu generierendem Code, die letztlich entweder zu eigenen Operationen führen oder bei Fork und Join (E_REND) kombiniert werden.
Die Bildung der Event-Chains, wie sie in Chapter: 6 Bildung von Operations aus den aufeinanderfolgenden Eventverbindungen beschrieben ist, wird für zwei Dinge verwendet:
Feststellung welche Outputs den Inputs in einem Submodul folgen, niedergelegt in den Operation. Diese sind notwendig, um für übergeordnete (rufende) Module die EventChains zu bilden.
Codegenerierung.
Für die Codegenerierung werden alle im Modul vorhandenen Event-Chains nacheinander abgearbeitet. Jede Event-Chain ist im Interface
mit einer Operation_FBcl
vertreten (über das EvinType_FBcl
referenziert). srcJava_FBcl/org/vishia/fbcl/fblock/Operation_FBcl, srcJava_FBcl/org/vishia/fbcl/fblock/EvinType_FBcl. Es werden im generierten Code die Anweisungen der Operation gebildet.
Die Reihenfolge der Abarbeitung der Event-Chain spielt keine Rolle. Sie bestimmt aber die Reihenfolge der Operationen im Quelltext, was aus Vergleichsgründen verschiedener Stände der generierten Sources wichtig ist.
Pro Eventchain wird die Event-Operation generiert mit Aufuf von
for(Evchain_FBcl evchain: mdl.evChains) { // //====> Generates the statements and the output expression: boolean bHasStatements = genStatementsForEvChain(mdl, evchain);
Es kann sein dass damit keine Statments erzeugt worden sind weil in der Eventchain nur kombinatorische FBlocks ohne Zwischenausgänge liegen. Dann wird keine Event-Operation generiert.
if(bHasStatements) { // //====>Generates source code: genSrcOperationForEvChain(mdl, evchain, evchain.operation.usedDinEvOper()); //====> Generate the generation rule for the event input operation. evchain.operation.genCodeStmnt = generateCodeRuleForEvent(mdl , evchain.operation.name, evchain.operation.usedDinEvOper()); } }//for ...evchain
Danach werden für alle Output-Pins des Moduls sowohl die Zugriffsfunktionen im Code generiert als auch die GenerierRegeln für den Zugriff auf das Modul:
for(EvinType_FBcl evinIfc: mdl.getIfcFB().evinType()) { //====>Generates source code for the routines to the event input. genSrcOperationForEvin(mdl, evinIfc); } for(Portdout_FBcl portOut: mdl.getdoutPortFromInnerMdl()) { //====>Generates source code for the access routines to the module output. genSrcOperationForOutpin(mdl, portOut); }
Als letztes werden die Gesamtfiles generiert (bei C Header und Implementierung). Dabei werden zuvor alle aufgesammelten Instanzvariable
in eine struct
oder class
generiert.
generateFile(genSrcDir, mdl); //Output the secSourceCode file CheckModule_FBrd.check(mdl, true); this.varMap.clear(); //after generate file, clear for superior translate. }
Topic:.FBclCgen.evOper.
date=2020-07-24
class srcJava_FBcl/org/vishia/fbcl/translate/Translation2Target_FBtrl
In genStatementsForEvChain(...)
wird für jedes Input-Event der Mitglieder der Event-Chain über den FBlock-Typ die zugehörige Operation operEvin
aufgesucht. Das erste Input-Event kann mehrere Operation haben, der Index der richtigen Operation steht in den Daten der
Event-Chain: Evchain_FBcl.ixFirstOper
. Die Operation des Event-Typs kann eine Codegenerier-Regel enthalten. Diese ist bei den Basis-FBlocks fest vorgegeben und
bei gerufenen Submodulen nach eben diesem Algorithmus zuvor gebildet worden. Wenn die Operation des Event-Typs keine Codegenerier-Regel
enthält, dann handelt es sich um einen kombinatorischen FBlock. Ist eine Regel vorhanden, dann wird damit ein Statement erzeugt:
genCodeStmntOrAccess(...)
, siehe Chapter: 7.5 Generierung des Access code für ein Event Input oder Data Output. Das ist dann ein Aufruf der Event-Operation.
Danach werden in processDoutToOperEvin(...)
für den jeweiligen Event-Input der Event-Chain die Outputs aufgesucht. Diese sind in der operEvin
als Bitmaske in mDrivenDout
enthalten. Es werden die Daten-Outputs behandelt, die tatsächlich von der Event-Operation des FBlocks beeinflusst werden,
nicht die dem Output-Event im Interface zugeordneten Daten-Outputs. Diejenigen Output-Pin, die eine Variable belegen, erzeugen
ebenfalls ein Statement in der Event-Operation und müssen daher in dieser Reihenfolge generiert werden.
Diese Abarbeitungen erzeugen mit oben genannten Bedingungen die Statements für die Event-Operation. Nachdem alle Event-Inputs
der Event-Chain abgearbeitet wurden, wird geprüft, ob überhaupt Statements erzeugt worden sind, d.h. die gerufenen FBlocks
entweder Event-Operations am entsprechenden Event-Input haben oder die Daten-Outputs in Variablen gespeichert worden sind:
interne Variable bHasStatements
Das läuft konform mit der Kennzeichnung der Input-Ports in der Operation. Diese ist genau dann 0 (keine Input-Ports), wenn
hier keine Statements generiert wurden. Das wird über assert(...)
geprüft. Nur wenn Statements erzeugt wurden, wird die Event-Operation im Target-Source generiert. Auch nur in diesem Fall
wird als letztes in diesem Ablauf die Generier-Regel für diesen Event-Input bzw. der zugehörigen Operation des Moduls generiert.
Das erfolgt mit
evchain.operation.genCodeStmnt = generateCodeRuleForEvent(...)
Siehe Chapter: 7.6 Generierung der Generierregel für Event-Operation.
Folglich hat ein Event-Input in einem hier übersetzten User-Modul nicht in jedem Fall eine Generier-Regel für die Event-Operation
gespeichert in Operation_FBcl#genCodeStmnt
, nicht für rein kombinatorische Logik. Genau dann ist aber auch Operation_FBcl#mNecessaryDin ==0
.
Topic:.FBclCgen.genCodeAccess.
Beides wird einheitlich von der Operation genCodeAccess(...)
ausgeführt.
Der Aufruf benötigt Input-Daten, die aus der Verdrahtung der jeweiligen Daten-Inputs des FBlock ermittelt werden. Da im Schritt Chapter: 6.4 Belegung der Portbits in den Operations diese für den Event-Input bereits ermittelt worden sind, können sie hier als Aufrufargument genutzt werden. Für den Zugriff auf Outputs wird die Bitmask in DoutType_FBcl.mUsedInputs benutzt, die ebenfalls bereits ermittelt worden ist.
Im Aufruf von getValueDin(...)
wird entschieden:
Ist das Pin unverdrahtet, dann wird ein konstanter Wert als Vorgabe getestet: din.getConstant()
. Ist dieser null
, dann wird der Wert "0"
angenommen (TODO noch Datentyp testen, String-Input dann ""
).
Hängt der Dateninput direkt an einen Modul-Inputport, dann wird dessen Name als lokale Variable benutzt. Die Zurodnung des Modul-Inputports an diese Operation ist bereits mit Chapter: 6.4 Belegung der Portbits in den Operations erledigt. Damit wird bei der Event-Operation diese Variablen in der Aufrufargumentliste definiert.
Kommt doutSrc
. die Quelle des Inpins, von einem internen FBlock im Modul und ist bereits mit einem Access-Code versehen worden, dann wird
dieser benutzt. Das kann nur ein Zugriff auf eine Instanzvariable sein, denn nur diese wird in der EventChain zuvor generiert.
Diese sieht dann für C beispielsweise wie folgt aus: "thiz->FBname_Pinname"
.
Ist doutSrc
noch nicht mit einem Access-Code versehen, dann wird dieser jetzt generiert. Das kann entweder ein Teil einer Expression
sein, wenn der Ausgang auf keiner Variable ablegelgt wurde und in der gleichen Event-Chain liegt und daher nur an genau diesem
einen Input hängt. Der Access-Code sieht dann beispielsweise wie folgt aus: "( a + b"
wobei a
und b
Inputs dieses den Output lieferenden FBlocks sind, oder "( a * (b + c))"
wenn die Inputs dieses FBlocks ebenfalls Expressions darstellen.
Ist doutSrc
noch nicht mit einem Access-Code versehen, hängt aber nicht an der gleichen Event-Chain, dann ist der doutSrc mit isPresentedByVar()
gekennzeichnet, aber noch nicht behandelt worden. Dann wird jetzt der notwendige Access-Code auf die Variable generiert.
Das Statement, dass die Variable versorgt, wird Teil der noch zu generierenden Event-Operation sein, in der dieser FBlock
hängt. Der Zugriffscode wird mit dem Translate-Script getOutput
gebildet, dass im Ctrl-File für die Codegenerierung enthalten ist. Es kann also für die jeweilige Zielplattform angepasst
werden. Der Name der Instanzvariable wird gebildet aus dem Namen des FBlock und dem Output-Namen, wenn der FBlock mehrere
Outputs hat. Dieser wird als outVar
dem Script übergeben.
Die Expressions für die Inputs werden in den zugehörigen Argumentvariablen des Generierscripts mit Namen des inneren Inputs des FBlocks, der identisch mit dem Typnamen des Inputs ist, gespeichert. Dieser wird für das Generierscript dann verwendet.
Den Argumentvariablen des Generierscripts wird dann der Instanzname des FBlocks hinzugefügt, wenn in der Input-Bitmaske mUsesThis
, also der this-pointer verlangt wird für Zugriff auf innere Variable. Das ist nicht der Fall bei rein kombinatorischer Logik.
Dieser THIS_OBJ
genannte Pointer wird ebenfalls mit einem Generierscript generiert, da hierüber die Anpassung an verschiedene Implementierungssprachen
möglich ist. Für C ist dieses Script als thiz-><&FBobj>
textuell im Generier-Ctrlfile vorgegeben. FBobj
als Aufrufargument wird mit dem Namen des FBlocks im Modul und, falls dieses mehr als einen Ausgang besitzt, mit der Output-Variable
im FBlock bezeichnet. Das ist unabhängig von der Target-Sprache die Bezeichnung der Instanzvariable für diesen Output als
Identifier.
Mit genCodeRule.exec(dst, data);
wird dann die Expression mit den gegebenen Scriptvariablen in data
generiert und in dst
eingeschrieben. Das ist entweder der statement-Stringbuilder oder ein temporärer StringBuilder, als Referenz beim Aufruf übergeben.
Topic:.FBclCgen.genRuleEvin.
Die Generierregel für Event-Operationen ist diejenige, die von außen für das Event-Input des Moduls aufgerufen wird. Sie wird mit der Generierung des Modul-Targetcodes generiert, da sie für das Target gilt.
Ausgeführt in generateCodeRuleForEvent(...)
.
Das Template für die Generierregel wird im TargetCode-Controlfile vorgegeben und sieht für C wie folgt aus:
====codeRuleEvinCall==== <:args:nameOp, type, THIS_OBJ, args, sep><: > <: ><&nameOp>_<&type> ( <:<&THIS_OBJ>><:set:sep=', '><: > <:for:arg:args><&sep><:<&><&arg>><:set:sep=', '><.for> ); ====
Dies ist der Quelltext wie er in einem OutTextPreparer
srcJava_Zbnf/org/vishia/util/OutTextPreparer verarbeitet wird. An dieser Stelle soll die Verarbeitung auf diesen Zweck bezogen erläutert werden:
Grundsätzlich werden bestimmte Zeichenfolgen: <&
und <:
jeweils bis zum >
gesondert behandelt. Alle anderen Zeichen werden so wie geschrieben generiert. Bei diesem Script ist die Schreibweise <:<&THIS_OBJ>>
interessant, die zur Ausgabe von <&THIS_OBJ>
führt. Im erzeugten Script was ebenfalls ein OutTextPreparer-Script ist, wird damit der Wert des Argument THIS_OBJ
an diese Stelle plaziert. Ohne die Umschreibung mit <:
bis >
würde diese Zeichenfolge interpretiert, mit Umschreibung ist es ein einfacher Text.
In gleicher Weise nur noch etwas komplexer ist <:<&><&arg>>
. In diesem Fall wird in der Ausgabe zunächst <&
erzeugt, aber gefolgt von dem Wert, der in der <:for
-Schleife in arg
eingeschrieben wird. Der erzeugte Text enthält also so etwas wie <&a>
entsprechend dem Argument bei der Nutzung des Scriptes, dort wird der Name eines Inputpin übergeben. Ersetzt wird dann der
Wert bzw. die Expression, die am Input hängt.
Die Folge <: >
mit einem Leerzeichen führt zum Überlesen aller Whitespaces, insbesondere dem folgenden Zeilenumbruch. Folgt unmittelbar
danach aber nochmals ein <: >
dann wird dies nicht so interpretiert, sondern das Leerzeichen zwischen <:
und >
wird ausgegeben. In diesem Fall werden zwei Leerzeichen folgend ausgegeben, damit es eine passende Einrückung ergibt.
Nach <:args:
sind Argumentnamen benannt, die im Script benutzt werden und vor Aufruf der Generierung übergeben werden. Das ist hier der
Name der Operation usw. Diese Argumente werden fest programmiert befriedigt, man kann im Script also deren Bezeichnung nicht
ändern, welche hinzufügen oder weglassen. Aber man muss nicht alle benutzen. So kann eine Erweiterung für eine spezielle Programmiersprache
oder Schreibweise weitere Argumente erfordern, die im Programmcode dann auch berücksichtigt werden, sie müssen aber alle hier
aufgeführt werden auch wenn sie nicht benutzt werden.
In diesem Script für C ist es nun geregelt, dass der type
, das ist der FBlock-Typename, als Suffix an den nameOp
angehangen wird. Das schafft für C die notwendige Eindeutigkeit des Funktionsnamens. Für C++ ist das nicht nötig, da die
Funktion als Klassenmethode gerufen wird. Daher ist auch der nameOp
nur kurz, resultierend aus dem Namen des Input-Events das dazu gehört.
Der sep
- Separator wird hier vor der ersten Benutzung mit einem Komma gesetzt. Initial ist er eine leere Zeichenkette.
Die Versorgung der Aufrufargumente und der Aufruf der KOnvertierung selbst ist im Programmcode wie folgt realisiert:
private OutTextPreparer generateCodeRuleForEvent( Module_FBcl mdl , String nameOper , long usedInpins ) throws IOException { List<String> callArgs = new LinkedList<String>(); for(DinType_FBcl dinPin: mdl.ifcFB.dinType()) { if((usedInpins & (1<<dinPin.ixPin)) !=0) { //The pin is used by usedInpins from the operation, then it is an arg callArgs.add(dinPin.namePin); } } this.trlData.codeRuleEvinCall.setArgument("nameOp", nameOper); this.trlData.codeRuleEvinCall.setArgument("type", mdl.ifcFB.name()); this.trlData.codeRuleEvinCall.setArgument("args", callArgs); this.trlData.codeRuleEvinCall.setArgument("sep", ""); this.sTemp.setLength(0); this.trlScripts.otx_codeRuleEvinCall.exec(this.sTemp, this.trlData.codeRuleEvinCall); String sCodeRule = this.sTemp.toString(); OutTextPreparer otxPinCode = new OutTextPreparer("evinCall", null, callArgs, sCodeRule); return otxPinCode; }
Topic:.FBclCgen.trlCtrl.
Für die Feingestaltung des Target-Code in einer beliebigen Sprache wird das Translator-Controlfile benutzt. Es ist für C und
C++ per default vorgegeben (innerhalb des jar-Files, cHeader.txt
), kann aber auch per Command-Argument bestimmt werden.
Das Controlfile enthält für folgende Teile des Codes Regeln:
include
: Gestaltung einer include- oder import-Line, für C:
====include==== <:args:depending><: > #include <&depending>.h ====
class
: class oder struct für die Instanzdaten:
====class==== <:args: name, varList> typedef struct <&name>_T {<: > <:for:var:varList> <&var.sType> <&var.name>;<.for> } <&name>; ====
Eine übergebene Variable ist vom Typ srcJava_FBcl/org/vishia/fbcl/translate/VariableDef_FBtrl. Deren Attribute werden über Reflection vom Script gelesen.
operation
: Der Aufbau einer Operation im code, genutzt sowohl für die Event-Input-Operations als auch für die Access-Operations auf
outputs. Die operation
wird als inline
in den Headerfile generiert.
arg
: Schreibweise eines Arguments in einer Operation-Argumentliste
setOutput
: Bildung der Expression zum setzen einer Instanzvariable von einem Output.
getOutput
: Lesen einer Instanzvariable
get_thisFBobj
: Lesen der Instanzreferenz für eine Sub-Instanz (nicht-kombinatorischer FBlock im Modul)
codeRuleEvinCall
siehe oben, Generiervorschrift der Generierregel für Event-Operation.
codeRuleDoutAccess
: Generiervorschrift der Generierregel für Output-Zugriff
Operatoren für Expressions oder Statements: Diese sind als Zuordnung wie folgt angegeben:
====statement==== !assign= = ==== !ne = != ==== !eq = == ====
Es wird eine Map gebildet mit dem Namen als key und dem Text nach =
bis zum ausleitenden ====
als value. Bei der Bildung von Expressions wird also beispielsweise gleich so codiert wie es unter eq
aufgefunden wird. Das kann für verschiedene Programmiersprachen etwas unterschiedlich sein. Die Bildung von Expressions ist
ansonsten sehr ähnlich, so dass kein Extraaufwand für verschiedene Programmiersprachen erforderlich ist.
Die Schreibweise der einzelnen Teile der Regeln ist wie ersichtlich so organisiert, dass eine Regel mit
====Identifier====
eingeleitet wird und bis zu einem abschließenden ====
geht. Das kann auch auf der selben Zeile sein, wenn die Regel keinen Zeilenumbruch enthalten soll. Das abschließende ====
kann allerdings bereits der Beginn der neuen Regeldefinition sein.
Topic:.FBclTest4diac.
Last changed: 2019-11-18
Dieser Dokumentationsabschnitt dokumentiert Testergebnisse und zeigt damit Features der Codegenerierung
Topic:.FBclTest4diac.MdlAB.
Das folgende Bild zeigt die Innenverdrahtung des Testcg_MdlAB
:
Die Verdrahtung ist in drei bezüglich der Ausgänge vollkommen unabhängige Teile geteilt. Eingänge werden teilweise gemeinsam benutzt. Dass mit an sich unabhängigen Teilen ein Modul gebildet wird, ist Entscheidung des Anwenders. Er muss nicht funktional schneiden sondern kann beispielsweise auch nach Zuständigkeit der Entwickler oder Relevanz der Verwendung die Modulaufteilung bestimmen.
Im Modul müssen zwei Werte gespeichert werden, dass sind die Verzweigungspunkte an aby1
und aby4
. Da die jeweiligen FBlocks nur einen Ausgang haben, heißt die dem Output zugehörige Variable wie die FBlocks in der Grafik.
Es wird folgende Datenstruktur für das Modul gebildet:
typedef struct Testcg_MdlAB_T { float aby1; float aby4; } Testcg_MdlAB;
Das Event ev_a
ist an zwei unabhängigen Event-Chains präsent. Folglich gibt es zwei Event-Operation, die beide notwendig sind da Zwischenwerte
gespeichert werden müssen:
inline void ev_a_aby1_Testcg_MdlAB ( Testcg_MdlAB* thiz, float a, float b ) { thiz->aby1 = (a + b); } inline void ev_a_aby4_Testcg_MdlAB ( Testcg_MdlAB* thiz, float a, float b) { thiz->aby4 = (a * b); }
Das ev_c
hat keine Event-Operation, da es sich um eine reine Kombinatorik handelt. Es wird dann auch keine leere Funktion generiert.
Da diese weder in den Quellen (für manuellen Aufruf) noch in der Interfacebeschreibung präsent ist, wird die nicht vorhandene
Event-Operation also weder manuell noch in automatischer Codegenerierung gerufen.
Die Output-Funktion für aby1
ist folgerichtig und einfach:
/**get the output value */ inline float y1_Testcg_MdlAB ( Testcg_MdlAB* thiz ) { return thiz->aby1; }
Der Output wird mit der zugehörigen Event-Operation ev_a_aby1_Testcg_MdlAB(...)
berechnet und ist immer gespeichert. Bei der Codegenerierung des Aufruf dieser FBlock wird zuerst die Event-Funktion gerufen,
so dass dieser Wert aus den Inputs neu berechnet zur Verfügung steht. Gleichso ist bei manuellem Aufruf vorzugehen.
/**get the output value */ inline float y2_Testcg_MdlAB ( Testcg_MdlAB* thiz ) { return (thiz->aby1 - (thiz->aby1 * 2.5)); }
Der zweite Ausgang wird on demand aus dem gespeicherten Wert der Event-Operation berechnet. Es werden hier keine weiteren Inputwerte benötigt, aber die Berechnung selbst erfolgt nicht mit der Event-Operation, sondern nur 'wenn er gebraucht wird', also ein Signal angeschlossen ist. Damit wird Rechentzeit (für fast realtime) eingespart wenn eine komplexe Berechnung vorgesehen ist aber im Konkretfall nicht benötigt, obwohl andere Teile dieser Event-Operation benötigt werden.
Diese Optimierung ist nicht wirksam wenn beispielsweise dieser Ausgangswert nochmals in einer Berechnung benötigt wird und daher von der Event-Operation berechnet wird, obwohl dann diese beiden Ausgänge nicht genutzt werden. Man muss also bei der Gestaltung von Modulen doch etwas planerisch vorgehen und etwa wissen was optimiert wird.
/**get the output value */ inline float y3_Testcg_MdlAB (float c, float d ) { return ((c * d) * 2.5); }
Der Output y3
wird nur kombinatorisch aus den Eingängen berechnet. Es wird vorausgesetzt dass die Daten mit einem Event vorher geliefert
wurden bevor hier zugegriffen wird. Der Eventgedanke ist hier also genauso präsent: Das Event legt diejenigen Werte außerhalb
dieses FBlock ab, die als c
und d
dann hier als Aufrufargumente verwendet werden. Wenn die Inputwerte nicht mit dem Event ev_c
geliefert werden, dann stehen sie von einem anderen Event außen geliefert zur Verfügung.
An dieser Stelle folgender Hinweis: Bei der Abarbeitung dieses FBlocks mit dem 4diac-forte (Automatisierungsgeräte-Laufzeitsystem für IEC-61499) werden die Werte hier für c
und d
mit der Event-Operation im FBlock gespeichert. Bei der hier implementierten Codegenerierung wird vorausgesetzt, dass das
Event in der FBlock-Umgebung die Daten geeignet ablegt, die dann benutzt werden. Das kommt letztlich aus das gleiche hinaus,
lediglich die Organisation ist etwas anders.
/**get the output value */ inline float y4_Testcg_MdlAB ( Testcg_MdlAB* thiz ) { return thiz->aby4; }
Ausgang y4
wird vom gespeicherten Wert aby4
geliefert. Die Event-Operation sollte zuvor aufgerufen worden sein. Der Wert wird deshalb gespeichert, weil es eine doppelte
Nutzung gibt.
/**get the output value */ inline float y5_Testcg_MdlAB ( Testcg_MdlAB* thiz, float d ) { return (thiz->aby4 + (d * 1.25)); }
Ausgang y5
ist nun eine Kombinatorik mit dem Eingang d
mit dem gespeicherten Wert der Event-Operation. Es liegen adäquate Verhältnisse wie bei y2_Testcg_MdlAB(...)
vor, nur dass hier zusätzlich ein weiterer Input-Wert benutzt wird. Für diesen gelten die adäquaten Anmerkungen wie zu y3_Testcg_MdlAB(...)
: Der Inputwert wird mit dem entsprechenden Event außen gespeichert.
Topic:.FBclTest4diac..
Das folgende Bild zeigt die Innenverdrahtung des Testcg_CallAB
:
Es wird das Modul Testcd_MdlAB
des vorigen Abschnittes gerufen, aber nicht alle Ausgänge sind belegt.
Dieses Modul speichert eine Instanz des gerufenen Moduls, und in diesem Fall keine weiteren Zwischenvariable. Dazu wird folgende
struct
für C generiert:
typedef struct Testcg_CallAB_T { Testcg_MdlAB subM; } Testcg_CallAB;
Es gibt für dieses Modul nur 1 Input-Event, dass an beide Event des gerufenen Moduls verdrahtet ist. Folglich gibt es hier
prinzipiell mindestens zwei Event-Operation, da das Event am Eingang gegabelt ist. Da das Submodul auch mehrere Event-Operation
am pro Input-Event haben kann, könnten es in diesem Fall drei sein, wobei das Submodul zu ev_c
keine Event-Operation generiert. Es wird nur eine Event-Operation erzeugt, die andere entfällt:
inline void REQ_subM_ev_a_Testcg_CallAB ( Testcg_CallAB* thiz, float x, float x2 ) { ev_a_aby1_Testcg_MdlAB ( thiz->subM, x, x2 ); }
Gerufen wird die eine Event-Operation des Submoduls. Die andere Event-Operation ev_a_aby4_Testcg_MdlAB(...)
wird nicht gerufen, da die zugehörigen Outputs des Submodul evo_y5
, y4
und y5
allesamt unbenutzt sind.
Für alle drei Daten-Outputs gibt es die Output-Access-Operation. Für die beiden ersten werden die Output-Access-Operations des Submodul direkt gerufen:
/**get the output value */ inline float y1_Testcg_CallAB ( Testcg_CallAB* thiz ) { return y1_Testcg_MdlAB( thiz->subM ); } /**get the output value */ inline float y2_Testcg_CallAB ( ) { return y2_Testcg_MdlAB( thiz->subM ); }
Da die Operationen in C inline
sind, wird vom C-Compiler der Maschinencode der Ausführung der gerufenen Operation generiert. Das ist sehr optimal.
Die dritte Output-Access-Operation hat noch eine Verknüpfung laut Grafik:
/**get the output value */ inline float y3_Testcg_CallAB (float x2, float x3 ) { return (y3_Testcg_MdlAB( x2, x3 ) * x3); }
Folglich benutzt sie auch noch Input-Werte.
Nehmen wir an, diese grafisch programmierte Funktionalität wird letzlich in folgendem Kontext aufgerufen:
Dann verbleiben für diese Aufrufumgebung nur noch die folgenden Operationen:
typedef struct Testcg_EnvAB_T { Testcg_CallAB testcg_CallAB; } Testcg_EnvAB; inline void REQ_Testcg_EnvAB ( Testcg_EnvAB* thiz, float x1, float x2 ) { REQ_subM_ev_a_Testcg_CallAB ( thiz->testcg_CallAB, x1, x2 ); //Dout not connected: y1 } /**get the output value */ inline float y_Testcg_EnvAB ( ) { return y2_Testcg_CallAB( ); }
Wenn dies nun die oberste Ebene ist, beispielsweise ein Hardwareinterrupt, der die beiden Werte x1 und x2 von zwei A/D-Kanälen
geliefert bekommt und an y
die Ausgabe für D/A abliefert, dann kann die Interruptbehandlungsroutine in ihrem Kern genau die Statements dieser Operationen
in der richtigen Reihenfolge, erst die Eventbehandlung, dann die Outputs, erhalten. Die Daten für die Interruptbehandlung
werden mit den gespeicherten Werten noch ergänzt, was auch einem Debugging hilft. Dann sieht aus den generierten Routinen
hand-angepasst die Sache wie folgt aus:
typedef struct Testcg_EnvAB_T { float x1, x2; //Analog-Input float y; //Analog-Output Testcg_CallAB testcg_CallAB; } Testcg_EnvAB; inline void isr_core(Testcg_EnvAB* thiz){ thiz->x1 = HWaccessAD1 * (1.0f/256); //get 12 bit Value with scaling thiz->x2 = HWaccessAD2 * (1.0f/256); //and float-conversion because 1.0f REQ_subM_ev_a_Testcg_CallAB ( &thiz->testcg_CallAB, thiz->x1, thiz->x2 ); thiz->y = y_Testcg_CallAB ( ); HWaccessDA1 = (int16)(thiz->y * 256); }
Die generierten Operationen für das Testab_EnvAB
-Modul werden also genutzt und geändert, nicht gerufen, weil die Grafik dieses Moduls lediglich die Umgebung darstellen soll,
nicht nochmal als Modul gekapselt.
Die Daten können statisch angelegt sein, für einfache Fälle:
Testcg_EnvAB isrData = {0}; /here simple static data
In der eigentlichen hardwarebezogenen Interrupt-Service-Routine (ISR) wird dann aufgerufen:
isr_core(&isrData);
Diese hardwarebezogene Schale, also die grafische Zeichnung oben für Testcg_EnvAB
ist nicht funktional sondern auf den Hardwareaufbau bezogen. Die eigentliche Funktinonalität ist rein grafisch programmiert.
Siehe dazu auch Chapter: 9.3 Eventgedanke in Embedded Control Lösungen und Folgekapitel.
Topic:.FBclTest4diac.evStep.
In diesem Fall ist das Original der Module in Simulink gezeichnet.
Das rechtsstehende Bild ist das übergeordnete Modul. Es enthält eine sogenanntes Unit delay " 1/z " zur Speicherung eines Wertes mit der dort angegebenen Abtastzeit Tstep
. Das gerufene Modul besteht wie im obigen Beispiel aus mehreren unabhängigen Teilen. Wie bereits in der FBlock-Darstellung
des Aufrufes angedeutet (ein Feature von Simulink ab Version 2018a), beeinflusst x1
nur y1
und x2
nur y2
. gain
spielt eine extra Rolle.
Die rechtsstehende Innenschaltung des aufgerufenen Moduls zeigt reine Kombinatorik von jeweils von x auf y, aber die Nutzung
eines gespeicherten Wertes, wiederum mit einem Unit delay, das vom Eingang gain
gespeist wird. Damit ist die Funktionalität klar, das äußere Unit Delay wird von einem kombinatorischen Wert aus dem Input-Port x2
bestimmt, zusätzlich mit einer multiplikativen Konstante die über gain
bestimmt wird. Der Output Port dy
ist außer den gespeicherten Werten rein kombinatorisch aus x1
und x2
gebildet. Das ist lediglich ein Testbeispiel, um die Verbindung von Kombinatorik und Unit delay zu testen.
Die rechtsstehende Innenschaltung ist nun in 4diac gezeigt nach der Umsetzung von Simulink in FBcl und Plazierung der FBlocks
in 4diac. Das Unit delay ist mit einem F_MOVE
abgebildet, der mit dem Event den Input vom IN
auf OUT
schiebt. Das Event heißt upd_Tstep
und ist eigens für den Zweck des Unit delay gebildet. Upd bedeutet update, da eine update-Operation für das update der gespeicherten Werte erzeugt wird. Der Input des F_MOVE
wird wie angegeben von dem Gain-Gblock gespeist, wobei der gain
-Dateninput mit einem ev_gain
-Event verbunden ist. Eine Weiterleitung dieses Events gibt es nicht, das Update benutzt nicht etwa frisch gelieferte Werte für gain
sondern nimmt den Wert wie gespeichert. Ob der Wert am Ausgang des gainq
erneuert ist oder nicht ist also nicht eine Funktion des Update, sondern hängt von der Quelle des gain
hier eindeutig über das Event ab. Das ist in Simulink genauso, wenn der Check der korrekten Abtastzeit gemildert ist (Settings - Diagnostic - Sampletimes) und der Wert aus einer anderen Abtastzeit stammt. Der Normalfall ist allerdings, dass das ev_gain
kommt, vor upd_Tstep
.
Das rechtsstehende aufrufende Modul zeigt wie die Eventverschaltung mit der Innenfunktionalität korreliert. Für beide Inputs
x1
und x2
gibt es ein gemeinsamens ev_x1
weil beide Inputs in der gleichen Operation mindestens eines FBlocks direkt vom Eingang verwendet werden, das ist die Regel
der Zuordnung der Eingangssignale zu Events. Da das x2
aber auch an das Submodul geht, und zwar unabhängig, gabelt das ev_x1
am Eingang auf und geht parallel auf beide FBlocks. Würde der Ausgang y2
des Submoduls direkt in der selben Event-chain für dy1 -dy
genutzt werden, wäre das ev_x1
seriell auf beide verdrahtet.
Das ev_gain
geht wie zu erwarten nur an das Submodul. upd_Tstep
ist nun durchgeschleift, da die Updates hintereinander ausgeführt werden. Die Reihenfolge spielt dabei keine Rolle, die (zufällige)
Reihenfolge hängt von der Reihenfolge der Abarbeitung der FBlocks ab.
Die Funktionalität des Submoduls von x1->y1
liegt im Datenfluss hinter der Bildung von dy1
, folglich ist die Eventkette von dy1.CNF
auf testcg..ev_x1
geführt. Das evo_dy
des Gesamtmoduls wird ausgegeben, wenn über das Submodul der Wert des dy
-Ausgangs zur Verfügung steht.
Die Eventketten können als Verkomplizierung der Grafik angesehen werden, jedoch dokumentieren sie genau die Abarbeitungsfolge. Für einfache straight-forward-FBlock-Diagramme ist der Datenfluss gut ersichtlich, aber die Eventketten sind auch einfach. Für komplexe Modelle kann es ohne Eventketten gegebenenfalls Schwierigkeiten bei der Erkennung der Abarbeitungsreihenfolgen geben. Simulink kann allerdings die Abarbeitungsreihenfolge, wie es selbst festgestellt wurde, auch in der Grafik anzeigen (Display - Blocks - Sorted Execution Order). Man kann die Simulink-intern erkannte Abarbeitungsreihenfolge mit der Eventkettenfolge vergleichen. Beide werden jedoch unabhänig ermittelt, die Simulink->FBcl-Konvertierung nutzt die Simulink-intern ermittelte Execution Order nicht.
Die Codegenerierung erfolgt nicht direkt aus Simulink, sondern aus dem FBcl-Abbild, wie es auch im 4diac sichtbar ist.
Das Submodul hat eine Datenstruktur:
typedef struct Testcg_MdlTstepSmlk_T { float gainq; float q; } Testcg_MdlTstepSmlk;
Es werden neben den Wert des unit delay bzw. F_MOVE
auch der Inputwert vor dem Update-Block gespeichert, das ist immer so.
/**void */ inline void opev_gain_Testcg_MdlTstepSmlk ( Testcg_MdlTstepSmlk* thiz, float gain ) { thiz->gainq = (0.05 * gain); }
Die opev_gain_...()
wird vom äußeren Modul mit der Eventchain des ev_gain
gerufen, der gain
-Wert wird geliefert.
inline void opupd_Tstep_Testcg_MdlTstepSmlk ( Testcg_MdlTstepSmlk* thiz ) { thiz->q = thiz->gainq; }
Die update-Operation darf niemals Werte erst noch berechnen sondern muss immer den Wert vor dem Update als Speicherwert nutzen, der vorher gesetzt wurde. Ansonsten kann es passieren, dass Werte aus einem Update-FBlock je nach Aufrufreihenfolge vor oder nach dem Update für das eigene Update benutzt werden. Das würde Gleichungen in der z-Transformationsebene verfälschen, die regelungstechnisch eine wesentliche Rolle spielen. Es würde eine Divergenz zwischen dem gewollten und etwa in Simulink beobachteten Verhaltens und dem Verhalten nach der Codegenerierung geben. Dies ist ein entscheidendes Merkmal für die Qualität der Codegenerierung.
/**The event input operation */ inline void ev_x1_Testcg_MdlTstepSmlk ( Testcg_MdlTstepSmlk* thiz, float x1 ) { //no event operation for opev_x1 }
Es werden alle Eventoperationen generiert, die aber in diesem Fall leer sind da eine Kombinatorik vorliegt. Die Event-Operationen
haben nur eine Bedeutung für den manuellen Aufruf, ein generierter Aufruf aus einem Modul heraus benutzt die op...()
-Operationen.
/**The event input operation */ inline void upd_Tstep_Testcg_MdlTstepSmlk ( Testcg_MdlTstepSmlk* thiz ) { opupd_Tstep_Testcg_MdlTstepSmlk ( thiz ); }
In diesem Fall ruft die Event-Operation für upd_Tstep
die interne update-Operation auf, für die manuelle Anwendung, die die internen Operationen (können komplexer sein) nicht
kennen braucht.
/**get the output value */ inline float y1_Testcg_MdlTstepSmlk ( Testcg_MdlTstepSmlk* thiz, float x1 ) { return ((0.01 * x1) + (x1 * thiz->q)); }
Die Berechnung des y1
-Ausgangs als Kombinatorik wie sie automatisch vom aufrufenden Modul gerufen wird, y2
ist adäquat.
Das aufrufenden Modul muss diese Operationen nun richtig einordnen und sieht wie folgt aus:
typedef struct Testcg_CallTstepSmlk_T { Testcg_MdlTstepSmlk testcg_MdlTstepSmlk; float testcg_MdlTstepSmlky2; float xz1; } Testcg_CallTstepSmlk;
Die Datenstruktur umfasst die Daten des Submodul als Composite (im UML-Sinn) und zwei eigene Zwischenwerte.
inline void opev_x1_testcg_MdlTstepSmlk_ev_x2_Testcg_CallTstepSmlk ( Testcg_CallTstepSmlk* thiz, float x2 ) { thiz->testcg_MdlTstepSmlky2 = y2_Testcg_MdlTstepSmlk(thiz->testcg_MdlTstepSmlk, x2 ); }
Dies ist die eine Kette (Chain) des aufgegebelten ev_x1
für die Berechnung des Wertes vor dem unit delay oder F_MOVE
für xz1
. Es wird die kombinatorische Operation zur Bildung von y2
des Submoduls gerufen und dessen Ergebnis als Update-Input gespeichert. Würden zwischen dem y2
-Ausgang und dem xz1
-FBlock noch andere kombinatorische Elemente liegen, dann ergibt das hier eine längere Expression. Wichtig ist jedenfalls,
dass der Wert vor dem Update-FBlock gespeichert wird.
inline void opupd_Tstep_Testcg_CallTstepSmlk ( Testcg_CallTstepSmlk* thiz ) { opupd_Tstep_Testcg_MdlTstepSmlk ( thiz->testcg_MdlTstepSmlk ); thiz->xz1 = thiz->testcg_MdlTstepSmlky2; }
Die update-Operation ruft beide Updates nacheinander auf, so wie die Event-Chain verdrahtet ist.
/**The event input operation */ inline void ev_x1_Testcg_CallTstepSmlk(Testcg_CallTstepSmlk* thiz, float x1, float x2 ) { //no event operation for opev_x1_dy1 opev_x1_testcg_MdlTstepSmlk_ev_x2_Testcg_CallTstepSmlk ( thiz, x2 ); }
Man sieht hier den Zweck der zusammenfassenden Event-Operation, die nur für den manuellen Aufruf vorhanden ist: Es müssen gegebenenfalls mehrere interne Operation gerufen werden, nur hier nicht da die zweite Event-Chain keine Eventoperaiton hat wie im Comment angegeben. Gleichzeitig erkennt man hiermit in der Quelle was Sache ist. Die innere Event-Operation hat einen langen nicht mnemonischen Namen, da er zusätzlich und formell aus dem Submodul-Namen und dem Event-Input-Namen des Submoduls gebildet wird. Dies sind interne Details des Moduls, die im äußereren manuellen Aufruf keine Rolle spielen sollten. Für den generierten Aufruf ist dieser komplexe Name jedoch 'nur ein Identifier' der immerhin Aufschluss auf Internas gibt, aber vom Programmierer nicht behandelt zu werden braucht. Das ist ok.
/**The event input operation */ inline void upd_Tstep_Testcg_CallTstepSmlk ( Testcg_CallTstepSmlk* thiz ) { opupd_Tstep_Testcg_CallTstepSmlk ( thiz ); }
Dies ist der formale Wrapper um die interne update-Operation, die jedoch immer eindeutig ist. Optimierungspotenzial der Codegenerierung, dies nicht doppelt.
/**get the output value */ inline float dy_Testcg_CallTstepSmlk(Testcg_CallTstepSmlk* thiz, float x1, float x2 ) { return y1_Testcg_MdlTstepSmlk( thiz->testcg_MdlTstepSmlk, (x1 + x2 - thiz->xz1) ); }
Die interne Berechung des dy
-Ausgangs. Man könnte als Aufrufschalte für den manuellen Aufruf noch alle Ergebniswerte einer Event-Operaiton optional in
eine Datenstruktur packen und mit der Event-Operation berechnen. Der generierte Aufruf kommt mit dieser Form sehr gut zurecht.
Topic:.FBclCcall.
Last changed: 2019-11-21
Dieser Dokumentationsabschnitt befasst sich mit der Aufrufumgebung der codegenerierten Module der FBcl.
Topic:.FBclCcall.event61499.
Die Norm IEC-61499 für die Automatisierungsgeräteprogrammierung, die für die FBcl benutzt wird, weist Eventverbindungen einen entscheidenden Stellenwert zu: Daten werden immer nur mit Events von einem zum anderen FBlock übertragen. Der Datenfluss ist mit den Events geregelt.
In der IEC-61499 ist damit eine verteilte Automatisierungsprogrammierung möglich ohne die Geräteaufteilung auf einzelne Funktionalitäten von vornherein berücksichtigen zu müssen. Die notwendigige Kommunikations-Datentstrukturen ergeben sich von selbst aufgrund der Datenzuordnungen zu den Events, sobald die einzelnen FBlocks bzw. Module aus FBlocks entsprechenden Geräten zugeordnet sind. Die Events steuern die Kommunikation zwischen den Geräten genauso wie die Kommunikation zwischen den einzelnen FBlocks in Modulen.
Topic:.FBclCcall.Kay.
Dieser Event-Kopplungs-Gedanke ist eigentlich eines der Fundamente der Objektorientierung, wenn man einem Zitat von Alan Key folgt (Aus Wikipedia, Artikel "Objektorientierte Programmierung" abgerufen am 2019-11-21:
-----------------------------------------------------------------------------------
Alan Kay, der Erfinder der Programmiersprache Smalltalk und des Begriffs „object oriented“, definierte ihn im Kontext von Smalltalk folgendermaßen:
“1. Everything is an object, 2. Objects communicate by sending and receiving messages (in terms of objects), 3. Objects have their own memory (in terms of objects), 4. Every object is an instance of a class (which must be an object), 5. The class holds the shared behavior for its instances (in the form of objects in a program list), 6. To eval a program list, control is passed to the first object and the remainder is treated as its message”
„1. Alles ist ein Objekt, 2. Objekte kommunizieren durch das Senden und Empfangen von Nachrichten (welche aus Objekten bestehen), 3. Objekte haben ihren eigenen Speicher (strukturiert als Objekte), 4. Jedes Objekt ist die Instanz einer Klasse (welche ein Objekt sein muss), 5. Die Klasse beinhaltet das Verhalten aller ihrer Instanzen (in der Form von Objekten in einer Programmliste), 6. Um eine Programmliste auszuführen, wird die Ausführungskontrolle dem ersten Objekt gegeben und das Verbleibende als dessen Nachricht behandelt“
Alan Kay: The Early History of Smalltalk (1993) In: The second ACM SIGPLAN conference on History of programming languages. ACM. S. 78. 1. März 1993. doi:10.1145/155360.155364
-----------------------------------------------------------------------------------
Dieser starke Hinweis auf die Verbindung der Eventsteuerung mit der Objektorientierung erfolgt deshalb, weil aktuelle Sprachen der Objektorientierung wie C++, Java, C# und weitere zwar Event-Mechanismen selbstversttändlich in speziellen Bibliotheken anbieten jedoch keineswegs standardisiert. Lediglich QT für C++ bietet mit dem SIGNAL-SLOT-Mechanismus eine QT-spezfische aber als langjährigen Standard zu bezeichnende Lösung dafür an.
Topic:.FBclCcall.evEmbd.
Jeder Hardwareinterrupt ist ein Event. In der direkten deutschsprachigen Bedeutung ist ein Event ein Ereignis, was geplant oder auch zeitlich nicht determiniert von äußeren Bedingungen ausgelöst werden kann. Diese Bedeutung wird auch dem 'Event' in der Informatik beigemessen. Teils wird aber auch die Datenstruktur, die für das Event steht und die Daten zum Event beinhaltet, selbst als 'Event' bezeichnet, Instanzen davon werden in einer 'EventQueue' nach Empfang oder Generierung gespeichert und zur Abarbeitung weitergegeben. Eine 'Event-Übertragung' ist die Übertragung der Event-Daten oder wenn man so will die Übertragung der Tatsache, dass dieses Event ausgelöst wurde. Im kommunikationstechnischen Sinn kann man aber dabei besser von Message sprechen.
Zurück zum Hardwareinterrupt: Dieser wird zyklisch oder von Hardwarebedingungen ausgelöst und stellt programmtechnisch ein Event dar. Auch Daten können diesem Event eindeutig zugeordnet werden. Meist werden in einem Sample-Takt AD-Werte gesampelt und gespeichert, danach wird der Interrupt ausgelöst. Dann sind diese Werte die Daten zum Event.
Wenn eine Datenübertragung stattfindet, dann häufig aufgrund von zyklischen Ereignissen, beispielsweise dem Samplen von Analogwerten auf einem anderen Gerät und deren Übertragung über Spezialverbindungen (dediziertes Netzwerk, USB, etc.).
Eine Datenübertragung kann auch lansamzyklisch erfolgen, dabei zeitlich weniger determiniert oder nur bei Bedieneingriffen. Auch dann liegt ein Event vor, mit den zugehörigen Daten.
Zuletzt kann auch das Einschreiben von Daten in einem anderen Thread und das Auslösen eines Verarbeitungsthreads mit dazu synchronisierten Daten als Event aufgefasst werden.
In der Realisierungsebene der Prozessorprogrammierung etwa in C muss in allen Fällen eine Verarbeitungsroutine aufgerufen werden, die die Daten aus den Quellen einliest (vom AD-Wandler, aus dem empfangenen Telegramm etc) und in einer Datenstruktur bereitstellt.
Topic:.FBclCcall.FBcall.
Folgt man nun dem vorigen Abschnitt, dann sind für ein zu verarbeitendes Event aus IEC-61499-Sicht der Funktionsblöcke die Daten in gleichnamigen Variablen bereitgestellt, wie sie den Funktionsblocknamen und dem Inputnamen der aufzurufenden FBlocks im Toplevel entsprechen.
Alles weitere kann die codegenerierung erledigen: Aufrund der Codegeneriervorschrift zu den Event-Inputs des/der Toplevel-Moduls kann die Aufruf-Operation automatisch generiert werden und wird von den vorhandenen Variablen versorgt.
Damit bleibt als Handprogrammieraufwand, den Rahmen des Zusammenstellens der Input-Daten zu schreiben. Dieser Rahmen ist nicht funktional sondern richtet sich lediglich nach den Hardwaregegebenheiten. Ändert sich die Funktionalität bei gleicher Hardware (gleichem Interrupt-Zeitbedingungen, gleiche Signale, gleiche Threadorganisation), dann genügt zur Herstellung der neue Funktionalität die grafische Programmierung mit Codegenerierung.
Topic:.FBclOORefs.
Last changed: 2019-11-21
Dieser Dokumentationsabschnitt befasst sich mit der Kopplung von Funktionsblöcken über referenzierte Daten
Topic:.FBclOORefs.OOref.
Wogegen die in Chapter: 9.2 Eventkopplung und Objektorientierung beschriebene Eventkopplung eher strittig zur Objektorientierung gehören möge, ist die Referenzierung von Daten in der Objektorientierung
etwa mit dem UML-Klassendiagramm, die gängige Praxis. Nicht Einzeldaten sind der Referenzierung zugänglich, sondern Instanzen,
die mit Methoden oder Operations auf deren Dateninhalt zugreifen. Der public
-Zugriff auf Einzeldaten oder über deren direkte Getter- und Setter-Zugriffsroutine ist allerdings durchaus auch gängige Praxis.
Diese Möglichkeiten scheinen im System der Funktionsblockprogrammierung zu fehlen. Die IEC-61499 fügt die Eventkopplung hinzu, ansonsten ist ein Funktionsblockprogrammierungs-System vom Datenfluss geprägt:
Datenfluss bedeutet, die einzelnen Eingangsdaten werden direkt bereitgestellt, die Output-Daten sind im betreffenden Funktionsplan entweder direkt abholbar oder werden über den Event-Mechanismus der IEC-61499 selbst übertragen. Das Datenfluss-Prinzip ist bestimmend für alle Funktionsblock-Programmiersysteme.
Es gibt aber einen einfachen Weg, die Möglichkeit auf innere Daten von Funktionsblöcken zuzugreifen, ohne diese außen zu übertragen. Rein technisch ist Grundvoraussetzung, dass ich die Daten in einem gemeinsamen Speicher befinden. Es wird dann per Datenfluss die Referenz oder Datenadresse (einmalig) übertragen, dann kann sich der empfangende FBlock beliebig den Daten bedienen, wie man es landläufig aus den Objektorientierten Sprachen über Zeiger oder Referenzen kennt.
Um dies auch grafisch zu unterstützen, braucht es Datensammler-FBlocks, die intern direkt in der Zielsprache des Targets programmiert sind, deren Datenzugriffsmechanismen per Zeiger nutzen. Die Gegenstelle sind die Datenzugriffs-FBlocks.
Ein solches System wurde für Simulink als FBlock-System realisiert, siehe www.vishia.org/smlk/html/Smlk_FB_ObjOConn.html
Die gleiche Gestaltung im Rahmen der FBcl ist das folgende Projekt der Zukunft.