FBcl

FBcl

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.


1 Grafische Programmierung im Embedded Bereich - Sinn und Ziel

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.


1.1 Software soll durchschaubar sein für Kunde, Vertrieb, Management

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:

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.


1.2 Grafischen Programmierung für bestimmte Hardwareplattformen

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.


1.3 Bei beliebigen Embedded Control-Lösungen ist eine hohe Flexibilität nötig

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.


1.4 Umgebungssimulation

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.


1.5 Codegenerierung in C(++)

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:


1.6 Verallgemeinerung der Funktionsblockprogrammierung mit FBcl

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.


1.7 Rolle der UML

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: 8.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: 9 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.


1.8 Abgrenzung FBcl von Simulationstools

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.


2 FBcl-Definition

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.


2.1 Ausgehend von einem FunctionBlock Modellierungstool wie Simulink

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ßendenFunktionalität gerichtet. Beides ist mindestens gleich zu bewerten, die Umgebungssimulation sollte keinesfalls unterbewertet werden. Die Gefahr besteht diese als 'Nebensache' abgetan wird, weil Simulink hauptsächlich als Vorlage eines Modells der Software zur Codegenerierung verwendet wird, damit man nicht manuell in C-Zeilen programmieren braucht. Diese Tendenz gibt es durchaus stark, wenn die Funktionsgebung von physikalisch oder regelungstechnisch denkenden Entwicklern bestimmt wird, nicht von Softwerkern.

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 sofort 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).


2.2 Versionierung und Re-Generierung über FBcl hat Vorteile

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.


3 FBcl Datenmodel

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.


3.1 Funktionsblock und dessen Zusammenschaltung

Topic:.FBclDataModel.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

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:

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.


3.2 Aufbau der class Pin_FBcl

Topic:.FBclDataModel.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.6 Information im FBlock-Typ zu einem FBlock bzw. Interface zu einem Modul. Diese Basisklasse enthält folgende Attribute/referenzierte Elemente:

Diese Informationen sind gleichfalls in einem Pin_Type_FBcl präsent und notwendig, siehe Chapter: 3.6 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):

In der abgleiteten Din_FBcl srcJava_FBcl/org/vishia/fbcl/fblock/Din_FBcl für Date-Inputs ist zusätzlich enthalten:

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.


3.3 FBlocks im Modul

Topic:.FBclDataModel.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.


3.4 Datentypen

Topic:.FBclDataModel.dtype.

Insbesondere in frei verwendbaren Library-Modulen ist ein 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 vorgesehen. Simulink kennt für diesen Zweck den derived-Datentyp.

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).

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.


3.5 Ports im Modul, Außenschnittstellen, Interface

Topic:.FBclDataModel.Port.

Im Bild im Vorkapitel sind Pin_FBcl-Instanzen direkt vom Modul sichtbar, mit:

Diesen Pin_FBcl-Instanzen entsprechend Instanzen der Typen EvinType_FBcl usw., die vom FBlock_Type_FBcl referenziert werden. Der Zusammenhang ist mit der ixPin, der Pin-Nummer hergestellt. Es gibt also keine direkte Referenz (Association) zwischen diesen Instanzen. Die Verbindung ist notwendig für die Codegenerierung, siehe TODO.

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.6 Information im FBlock-Typ zu einem FBlock bzw. Interface zu einem Modul.

Ein FBlock-Typ kann wie folgt vorliegen:


3.6 Information im FBlock-Typ zu einem FBlock bzw. Interface zu einem Modul

Topic:.FBclDataModel.Type.

Jede Instanz eines FBlock_FBcl referenziert sein Interface, das ist die 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.

Die class FBlock_Type_FBcl srcJava_FBcl/org/vishia/fbcl/fblock/FBlock_Type_FBcl enthält folgende Attribute und Referenzen:

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

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 Chapter: 3.2 Aufbau der class Pin_FBcl:

Folgende Aggregationen (als Array implementiert) entsprechen im Typ den Pins eines FBlock:

Zusätzlich gibt es o, Bild rechts oben

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.4 Datentypen.


3.6.1 Evin mit Operation, Codegenerier-Regel für Event-Handling

Topic:.FBclDataModel.Type.evin.

Ein Event-Input eines Moduls (Composite FBlock) kann ein oder mehrere Operations 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/EvInOut_FBcl. Letztere referenziert in operations keine oder beliebig viele Operations.

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.


3.6.2 EvoutType_FBcl, Event-Output mit Bedeutung für Statemachinen

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.


3.6.3 DinType_FBcl - Dateninput, Assoziation zwischen Event- und Datenports

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.


3.6.4 DoutType_FBcl - Codegenerier-Regel für die Output

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.8 Codegenerier-Regeln in Operation und Output-Datenpins.


3.6.5 Operation_FBcl - Info welche Pins beeinflusst werden

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.7 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: 6 Codegenerierung aus FBcl) bedingt in verschienenen aufrufenden Operationen eingegliedert.

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.


3.7 Event-Chain-Informationen

Topic:.FBclDataModel.Evchain.

Die Event-chain ist die Aufeinanderfolge von Funktionsblock-Teilen in einem Modell, die nacheinander abgearbeited werden, beschrieben in Evchain_FBcl srcJava_FBcl/org/vishia/fbcl/fblock/Evchain_FBcl. Die Event-chains 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.3 FBlocks im Modul über Module_FBcl#evChains referenziert.

Jeder EvChain_FBcl ist genau eine Chapter: 3.6.5 Operation_FBcl - Info welche Pins beeinflusst werden zugeordnet, die von den FBlock_Type_FBcl - Typdaten referenziert wird über die Event-Inputs.

Eine Event-chain beginnt jedenfalls an den Event-Inputs des Moduls. Die aggregierte Operation_FBcl ist die zugehörige Event-Operation.

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: 5.2 Algorithmus zur Bildung von EventChains in einem Modul und Chapter: 5.5 Abhängige Operationen beschrieben.

Eine Evchain_FBcl srcJava_FBcl/org/vishia/fbcl/fblock/Evchain_FBcl 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 Pin_FBcl):


3.8 Codegenerier-Regeln in Operation und Output-Datenpins

Topic:.FBclDataModel.codegen.

Die class ..CodeGen_FBcl srcJava_FBcl/org/vishia/fbcl/fblock/CodeGen_FBcl enthält:

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: 6 Codegenerierung aus FBcl


3.9 Konvertierung aus verschiedenen Quellen in dieses Datenmodell und zurück

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.


4 Rahmen für das Lesen von FBcl-Files und Übersetzen in eine Zielsprache

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.


4.1 Rahmen

Topic:.FBclRead.readMdl.

Für das Lesen von FBcl files nach IEC 61149 ist die class

zuständig.

/**Manage class for reading FBcl-Files and conversion to the target language.
 * @author Hartmut Schorrig
 */
 public class ReaderFBcl {

Diese class speichert gelesene Submodule eines gelesenen Moduls:

 /**Storage for all already read and converted sub modules.*/
 private final Map<String, FBlock_Type_FBcl> mapReadModules =
   new TreeMap<String, FBlock_Type_FBcl>();

Wenn Submodule mehrfach auch in sub-Ebenen auftauchen, werden diese nur einmalig gelesen. Die Codegenerierung erfolgt mit dem Lesen eines Moduls, Lesen und Codegenerieren ist also ein kompletter Ablauf. Das ist wichtig

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 Datenmodell aus den IEC 61499-Files wird erhalten über Aufruf : docuSrcJava_FBCL/org/vishia/fbcl/readFBCL/ReaderFBcl.html#readFile-java.io.File-java.util.List-, siehe todo

   IEC61499data dataRaw = readFileRawData(fileFBCL);
   //check content
   IEC61499data.Fb_type_declaration fBtype = dataRaw.get_FBType();
   IEC61499data.FBNetwork mdlRaw;
   if(fBtype ==null || ( mdlRaw = fBtype.get_FBNetwork())==null) {
     throw new IllegalArgumentException("The file should be a composite FBlock: " + fileFBCL.getAbsolutePath());
   }

Es wird vorausgesetzt, dass der FB ein Composite ist. TODO sw: Es kann aber auch ein Basic-FBlock als User-Submodul auftauchen. Wichtig ist dass der FBlock_Type_FBcl geliefert wird.

Submodul-FBlocks: Es wird nun geprüft ob das gelesene Modul Submodul-FBlocks enthält, die keine Standard-FBlocks sind:

   //it is a composite FBlock with a network inside:
   //check all call FBlock and call recursively this routine for non-standard (user space) library FBlocks:
   //
   for(IEC61499data.Fb_instance_definition fbraw: mdlRaw.get_FB()) {
     @SuppressWarnings("unused") String sNameFB = fbraw.get_Name();
     String sTypeFB = fbraw.get_Type();
     FBlock_Type_FBcl typeFB = this.prj.stdFBlocks.getType(sTypeFB);
     if(typeFB == null) {
       typeFB = this.mapReadModules.get(sTypeFB);
     }
     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
       File src = searchSubmoduleFile(sTypeFB);
       FBlock_Type_FBcl fbType = read(src, recursive +1);
       this.mapReadModules.put(fbType.name(), fbType);
     }
   }

TODO sw: Es muss auch ein FBlock in ST (Basic FBlock) in gleicher art analysiert werden.

Für Submodule, die keine Standard-FBlocks sind und die bisher nicht konvertiert wurden, wird eben diese Operation read(...) rekursiv gerufen. Dabei sucht die Routine searchModuleFile(sType) 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.


5 Bildung von Operations aus den aufeinanderfolgenden Eventverbindungen

Topic:.FBclEvChain.

Last changed: 2019-10-16

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. Es wird damit die Abhängigkeit von Outputs von Inputs für Event und für Daten aus den tatsächlichen Gegebenheiten im Modul heraus festgestellt, unabhänig von der Event-Daten-Relation im Interface. Damit kann beim Aufruf des Moduls als Submodul im aufrufenden Modul ebenfalls die Event-Relationen auf der tatsächlichen Basis festgestellt werden. Dies ist für die Bildung der Eventverbindungen aus dem Datenfluss im übergeordneten Modul wiederum essentiell.


5.1 Datenhaushalt

Topic:.FBclEvChain..

Die Event-chain-Daten Evchain_FBcl srcJava_FBcl/org/vishia/fbcl/fblock/Evchain_FBcl sind in Chapter: 3.7 Event-Chain-Informationen beschrieben.

Eine Operation Operation_FBcl srcJava_FBcl/org/vishia/fbcl/fblock/Operation_FBcl ist in Chapter: 3.6.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.


5.2 Algorithmus zur Bildung von EventChains in einem Modul

Topic:.FBclEvChain.build.

Der Algorithmus ist in srcJava_FBcl/org/vishia/fbcl/readFBcl/BuildEvchainOperation_FBcl enthalten.

In der processEvchain(...) gibt es folgenden Ablauf für die Traversierung durch die Event-Chain:

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.


5.3 Belegung der Portbits in den Operations

Topic:.FBclEvChain.operPortBits.

Wie in Chapter: 3.6.1 Evin mit Operation, Codegenerier-Regel für Event-Handling und Chapter: 3.8 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: 6 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:

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.

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.6.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.


5.4 Bedingte Generierung von Statements aus Operation

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.


5.5 Abhängige Operationen

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.


6 Codegenerierung aus FBcl

Topic:.FBclCgen.

Last changed: 2019-10-16

Dieser Dokumentationsabschnitt befasst sich mit der Codegenerierung von Ablaufcode für embedded Plattformen aus FBcl-Files. Die Codegenerierung ist auf C bezogen, kann jedoch adäquate auch in C++, Java 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: 5 Bildung von Operations aus den aufeinanderfolgenden Eventverbindungen.


6.1 Codegenerierung mit Abarbeitung der EventChain

Topic:.FBclCgen.codegenEvchain.

Die Bildung der Event-Chains, wie sie in Chapter: 5 Bildung von Operations aus den aufeinanderfolgenden Eventverbindungen beschrieben ist, wird für zwei Dinge verwendet:

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

boolean bHasStatements = processEvChainOfModule(mdl, evchain, varMap);

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.mNecessaryDin); //usedInpins);
}

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(Din_FBcl portOut: mdl.getBondOutFromInnerMdl()) {
  //====>Generates source code for the access routines to the module output.
  genSrcOperationForOutpin(mdl, portOut);
}

Mit

generateFile(genSrcDir, mdl, varMap);  //Output the secSourceCode file

wird dann letztendlich das File auf den Datenträger geschrieben. Dabei werden zuvor alle aufgesammelten Instanzvariable in eine struct oder class generiert.


6.2 Codegenerierung für eine Event-Operation

Topic:.FBclCgen.evOper.

In processEvChainOfModule(...) 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: 6.3 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: 6.4 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.


6.3 Generierung des Access code für ein Event Input oder Data Output

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: 5.3 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:

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.


6.4 Generierung der Generierregel für Event-Operation

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;
 }

6.5 Das Translator-Controlfile, weitere Codegenerier-Regeln

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:

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.


7 Test der Codegenerierung mit 4diac-Modulen

Topic:.FBclTest4diac.

Last changed: 2019-11-18

Dieser Dokumentationsabschnitt dokumentiert Testergebnisse und zeigt damit Features der Codegenerierung


7.1 Ein Modul mit mehreren unabhängigen kombinatorischen Verknüpfungen

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.


7.2 Ein rufendes Modul, dass nicht alle Ausgänge verwendet

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: 8.3 Eventgedanke in Embedded Control Lösungen und Folgekapitel.


7.3 Modul mit Steptime-Speicher, abgetastete Systeme, weiteres Beispiel

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.


8 Aufruf der generierten Codes aus der FBcl

Topic:.FBclCcall.

Last changed: 2019-11-21

Dieser Dokumentationsabschnitt befasst sich mit der Aufrufumgebung der codegenerierten Module der FBcl.


8.1 Eventmodell der IEC-61499

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.


8.2 Eventkopplung und Objektorientierung

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.


8.3 Eventgedanke in Embedded Control Lösungen

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.


8.4 Aufruf der FBcl-generierten Operations mit den Event-Daten

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.


9 Objektorientierte Referenzkopplung von Funktionsblöcken

Topic:.FBclOORefs.

Last changed: 2019-11-21

Dieser Dokumentationsabschnitt befasst sich mit der Kopplung von Funktionsblöcken über referenzierte Daten


9.1 Referenzierung und Objektorientierung

Topic:.FBclOORefs.OOref.

Wogegen die in Chapter: 8.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.