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 Mischung grafischer und textueller Programmierung, Kerne in C

Topic:.FBclMission.mission.GraftxtPrg.

Auch und insbesondere in der Automatisierungsgeräteprogrammierung, dort wo die grafische Programmierung immer schon verbreitet ist, ist eine Mischung aus Text- und Grafikprogrammierung zu beobachten:

In der Automatisierungsgeräteprogrammierung wird nirgends direkt in Maschinencode programmiert, außer man zählt Programme in Instruction Code oder AWL im Siemens-S7-Jargon dazu, bei denen man diverse Hardwaregegebenheiten ansprechen kann. Für das Ansprechen von Hardwaresignalen gibt es ja die fertigen System-FBlocks.

Genau deshalb braucht man bei der Programmierung im Embedded Bereich die C-Ebene:

Diese Teile sind in speziellen FBlocks realisierbar, die für eine Embedded Programmierung im Vergleich zur Automatisierungsgeräteprogrammierung die selbst erstellen System-FBlocks darstellen.

Man kann folgende drei Levels unterscheiden:


1.7 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.8 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: 9.2 Eventkopplung und Objektorientierung ist der Event-Gedanke in der Objektorientierung beleuchtet, wie er für die FBcl maßgebend ist. Auch in den UML-Tools spielen die Events die dazu passende Rolle.

Im Abschnitt Chapter: 10 Objektorientierte Referenzkopplung von Funktionsblöcken ist das Theme der Referenzierung in Funktionsblöcken angeschnitten. Der Verfasser hat dieses Prinzip schon in den 90-ger Jahren im STRUC für Simadyn realisiert und in Simulink aktuell lauffähig implementiert. Die Nutzung im Zusammenhang mit der FBcl ist geplant. In der UML tangiert dies die Klassendiagrammdarstellung. Siehe auch https://www.embedded-software-engineering.de/grafische-objektorientierte-c-programmierung-mit-simulink-a-726729/

Die Statemachine-Darstellung wie sie in der UML bekannt ist und ursprünglich von David Harel über das Tool Statemate (I-Logix) und später Rhapsody in die UML eingebracht worden ist und im UML-Standard https://www.omg.org/spec/UML/About-UML/ gut beschrieben ist, hat sich allgemein verbreitet und ist daher auch in einigen Tools der Funktionsblockprogrammierung adäquate anzutreffen. Hier hat die UML den Maßstab gesetzt.

Bezüglich der UML auf der einen Seite und der gängigen Funktionsblock-Programmierung auf der anderen Seite sollte es zukünftig Überschneidungen geben. An dieser Stelle gibt es insgesamt nützliches Verbesserungspotenzial.


1.9 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 - Ziel und 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ßenden Funktionalität gerichtet. Beides ist mindestens gleich zu bewerten.

Häufig werden Modellierungsteils (aus Managersicht) nur als bessere Werkzeuge für die Softwareerstellung des Zielsystems angesehen. Man würde Kosten sparen, weil nicht manuell in C-Zeilen programmieren werden braucht. Diese Denkweise ist zwar richig insbesondere wenn die Funktionsgebung von physikalisch oder regelungstechnisch denkenden Entwicklern bestimmt wird, nicht von Softwerkern. Jedoch schlägt eine generelle Ablehnung der Zeilenprogrammierung als Effektivitätssteigerung oft ins Gegenteil um. Beides muss Hand in Hand gehen. Die generierten Codes sind ggf. zu begutachten, nicht um manuell nachzubessern sondern um möglicherweise falsche Einstellungen der Codegenerierung zu erkennen oder die Modellgestaltung per Styleguide zweckdienlich zu verbessern. Außerdem muss für allgemeine Embedded Ablaufumgebungen eine Einbindung erfolgen. Letzlich muss man auch in Hardware- und Betriebssystemdingen Kenntnisse bewahren, solange nicht auf Standardhardware umgestellt wird.

Die Umgebungssimulation sollte keinesfalls unterbewertet werden. Die Möglichkeit, damit physikalische relalistische Test durchführen zu können, ist zwar im kommen. Die Gefahr besteht aber immernoch, dass dies als 'Nebensache' abgetan wird,

Festzustellen wäre, das Simulink deshalb sehr stark zu empfehlen ist, weil der Umgebungssimulation ein hoher Stellenwert eingeräumt werden sollte. In dieser Richtung ist die FBcl als Konzept nicht angesiedelt.

Aus den Teilen eines Simulink-Modells, die funktionsgebend für die Ziel-Software (Target-Software) sind, kann man FBcl generieren. Dabei ist es nicht das Ziel, gestandene Anwendungen der Codegenerierung in Simulink abzulösen. Es werden auch nie alle Features, die in Simulink aus verschiedenen Intensionen entwickelt wurden, von FBcl beherrscht. Vielmehr soll die FBcl als toolunabhängiges Programmierkonzept konzeptionell wirken. Beispielsweise sind Statemachines in FBcl zwar an die Harel-Statemachines der UML angelegt, haben aber zweckdienliche Sonderbedingungen. Die Simulink-Statemachines sind auch an die Harel-Statemachines angelegt, haben aber Sonderlösungen bei der Unterscheidung zwischen Message und Event. Es ist eine Schnittmenge zwischen Simulink und FBcl, die für die Funktionsgebung der Targetsoftware nutzbar ist.

Folgendes Bild zeigt rechts oben ein einfaches Simulink-Modell eines Differenzierers mit vorgelagertem User-FBlock. Wie in der Einleitung zu Chapter: 1 Grafische Programmierung im Embedded Bereich - Sinn und Ziel genannt, sollten diese wenig komplexen Dinge eher nicht grafisch programmiert werden sondern eher die Zusammenschaltung komplexer Module, aber an diesen einfachen Beispielen lassen sich die Prinzipien besser erklären.


Links ist der daraus generierte FBcl-Quelltext dargestellt. Dessen Parallelabbildung mit XML sieht dann nach Plazierung in dem IEC-61499-Grafiktool 4diac-IDE wie rechts unten gezeigt aus.

Die FBcl ist also wie hier ersichtlich eine textuelle Sprache, die eine grafische Topologie widerspiegelt. Dazu bedient sie sich der Syntax der IEC-61499-Norm.

In der textuellen Notation sind keine Grafikpositionen enthalten, diese sind auch nicht funktionsbestimmend. Inwieweit ein sogenannten Roundtrip - Rundumschlag aus FBcl-Generierung und nachfolgendes Zurückspielen in das Grafische Modell nach ggf. kleinen Änderungen unter Beibehaltung der Grafikpositionen gelinkt, ist dabei nicht ausgeschlossen, aber nicht Gegenstand jetziger Darstellungen.

Im folgenden Slide ebenfalls auf dem ASE-Kongress 2019 gezeigt, ist die Codegenerierung aus diesem FBcl-Code gezeigt:


Hinweis: Diese beiden Slides sind nur im Vortrag zusätzlich gezeigt worden und sind nicht Bestandteil der schriftlichen Unterlagen.

Zu erkennen ist links unten die 4diac-IDE-Grafik, gekennzeichnet mit Blöcken von lineraren Event-Folgen (Event-Chain). Diesen entsprechen C-Funktionen. Eine struct-Definition enthält zu merkende Daten, der Programmierstil kann als Objektorientiert bezeichnet werden (Bindung der Operationen = C-Funktionen an die Daten).


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.


2.3 FBcl als Sprache einer textuellen Modellspeicherung

Topic:.FBclDef.txtlang.

Hierzu ist definiert, dass die IEC-61499 benutzt wird. Diese enthält alle notwendigen Elemente. Die Statemachine-Möglichkeiten werden aus heutiger Sicht mit der Standardisierungsrunde in 2020 nochmals erweitert.

Die IEC-61499 ist angepasst an den Anwendungszweck Automatisierungsgeräteprogrammierung weit gefächert. Bei den Functionblocks werden grundsätzlich drei Typen unterschieden:

Damit ist die FBcl mit IEC-61499-Norm nicht nur auf die grafische Repräsentation gerichtet sondern unterstützt die in Chapter: 1.6 Mischung grafischer und textueller Programmierung, Kerne in C beschriebenen textuelle Repräsentationen.

Zusätzlich zu den IEC-61499-definierten textuellen Schreibweisen gibt es einige Report-Formate, die beispielsweise innere Verdrahtungen anders darstellen als die Verbindungslisten bei EVENT_ und DATA_CONNECTIONS der Composite-FBlocks. Diese werden mit dem Einlesen und der Auswertung der FBlocks aus IEC-61499-konformer Darstellung gewonnen, stellen also dort enthaltene aber nicht so gut sichtbare Informationen dar.


2.4 FBcl als Konzept

Topic:.FBclDef.Concept.

Dies sollte mit den weiteren Ausführungen in diesem Artikel erkennbar sein. Es geht nicht nur um eine andere Darstellungsweise von Inhalt, sondern um Herangehensweise.


2.5 FBcl als Rahmen für Konvertierungsalgorithmen

Topic:.FBclDef.intern.

Dieser Artikel beschäftigt sich maßgebend mit den internen Darstellungen von Daten und Algorithmen für die Konvertierung aus anderen grafischen Darstellungen nach FBcl (nach IEC-61499-Codierung) und für die Codegenerierung nicht nur nach C oder C++.


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

Topic:.FBclDataModel.Type.

Der FBlock_Type_FBcl srcJava_FBcl/org/vishia/fbcl/fblock/FBlock_Type_FBcl repräsentiert nach außen einen nutzbaren Funktionsblock, also entweder für die grafische Darstellung oder für den Aufruf in der textuellen Programmierung. Jede Instanz eines FBlock_FBcl referenziert sein Interface, das ist diese Typinformation. Eine gleichartige Typinstanz wird für jedes Modul aufgebaut, dass damit wieder als FBlock instanziierbar ist. Das Verhältnis zwischen FBlock_FBcl und dessen FBlock_Type_FBcl kann wie eine Instanz zu einer Klasse aus UML-Sicht der FBlock-Anwendung gesehen werden.

Zu einem FBlock_Type_FBcl gehört entweder ein Inhalt dieses Moduls, von Modul_FBcl repräsentiert, oder es sind bereits generierte Targetsystem-Quellen vorhanden.

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

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

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


3.1.1 EvinType mit Operation, Pin-Zuordnungen, Codegenerier-Regel für Event-Handling

Topic:.FBclDataModel.Type.evin.

Ein Event-Input eines FBlock_Type_FBcl kann ein oder mehrere Operation_FBcl haben. Das Event-Input wird repräsentiert von class EvinType_FBcl srcJava_FBcl/org/vishia/fbcl/fblock/EvinType_FBcl basierend auf srcJava_FBcl/org/vishia/fbcl/fblock/EvinoutType_FBcl. Letztere referenziert in operations keine oder beliebig viele Operation_FBcl srcJava_FBcl/org/vishia/fbcl/fblock/Operation_FBcl.

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


3.1.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.2.5 Event-Chain-Informationen referenziert. Sie enthalten in Bitmasken die Information, welche Output-Pins (sowohl Event als auch Data) von der jeweiligen Event-Operation beeinflusst werden. Es gibt auch innere virtuelle EvInput_FBcl die zu abhängigen Operationen gehören.

TODO Class-Diagramm hier.

Mehrere Operation_FBcl in einem FBlock-Typ entsprechen unabhängigen Teilen dieser FBlock-Gesamtfunktionaltität. Die Teile werden aus IEC-61499-klassischer Sicht als einzelne Event-Operationen zur Laufzeit abhängig von ankommenden Events unabhängig aufgerufen und werden bei der optimierten Codegenerierung (siehe Chapter: 7 Codegenerierung aus FBcl) bedingt in verschienenen aufrufenden Operationen eingegliedert.

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.2 Modul-Inhalt eines Composite-FB

Topic:.FBclDataModel.Modul.

Die class Module_FBcl srcJava_FBcl/org/vishia/fbcl/fblock/Module_FBcl repräsentiert den Inhalt eines Moduls. Diese Daten werden beim Einlesen oder Konvertieren eines Moduls (z.Bsp. aus Simulink) gebildet und sind relevant für die Codegenerierung dieses Moduls und für das Ausschreiben der FBcl-Textrepräsentation. Für die Nutzung eines FBlocks als Typ mit einer bereits erfolgten Codegenerierung sind diese Daten nicht mehr relevant.

Ein Modul kann ein Composite-FB sein, also eine grafische Zusammenschaltung von FBlocks, aber auch ein Simple-FB mit einem einfachen textuellen Algorithmus in ST (Structure Text) oder ein Basic-FB mit einer Statemachine.

Die Unterscheidung ist im Datenmodell gegeben mit null-Zeiger bzw. Besetzung des Zeigers auf:


3.2.1 Composite-FB-Modul, Funktionsblock und dessen Zusammenschaltung

Topic:.FBclDataModel.Modul.FBlock.


Das obige Bild zeigt ein UML-Diagramm der Zusammenschaltung von Funktionsblöcken in einem Modul.

Hinweis zur Darstellungsart: Es handelt sich nicht um ein Objektdiagramm oder Instanzendiagramm. Die mehrfach dargestellten selben Klassen im Klassendiagramm stellen vielmehr Rollen der instanziierbaren Klassen im Umfeld dar. So ist die class FBlock_FBcl hier zweimal gezeichnet, um zu zeigen wie zwei mögliche Instanzen dieser Klasse über ihre auch in mehreren Rollen auftretenden Pin_FBcl-Instanzen verbindbar sind. Diese Art der Darstellung ist in der UML ausdrücklich zulässig, da in Diagrammen immer Ausschnitte des Datenhaushalts den Erklärungen entsprechend enthalten sein können. Das Diagramm stellt nicht selbst den Datenhaushalt dar (das Repository) sondern dient dem Zeigen der Zusammenhänge. Das ist grundsätzlich anders als beispielsweise ein Funktionsplan mit Funktionsblöcken in einer grafischen Programmierung. Dort stellt jeder Funktionsblock eine Instanz dar.

Weiterer Hinweis: Es ist im folgenden Text häufig von 'Instanzen' bezüglich des FBlock_FBcl die Rede, im Gegensatz zum Typ in FBlock_Type_FBcl. Der Instanzbegriff ist nicht auf die Klassendiagramm-Darstellung bezogen sondern auf die Sachlage: Ein FBlock_FBcl stellt in der Sache eine Instanziierung bzw. den Aufruf eines Funktionsblocks in der grafischen Programmierung dar, dessen Typ in Instanzen von FBlock_Type_FBcl beschrieben ist.

Die class FBlock_FBcl stellt einen Aufruf eines Funktionsblocks dar. Dieser hat im wesentlichen folgende Attribute und Assoziationen, siehe auch srcJava_FBcl/org/vishia/fbcl/fblock/FBlock_FBcl

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.2 Aufbau der class Pin_FBcl

Topic:.FBclDataModel.Modul.Pin.

Eine Instanz von Pin_FBcl stellt einen Anschluss dar. Das Wort 'Pin' ist aus seinem Gebrauch für einen Schaltkreis (IC) abgeleitet, denn es bedeutet eigentlich 'Nadel'. Folglich ist ein Pin ein Connection-Point.

Die class Pin_FBcl srcJava_FBcl/org/vishia/fbcl/fblock/Pin_FBcl ist gleichermaßen für alle vier Verbindungsarten verwendet, da eigentlich keine wesentlichen Unterschiede bestehen. Pin_FBcl hat eine Basisklasse PinBase_FBcl srcJava_FBcl/org/vishia/fbcl/fblock/PinBase_FBcl, die auch bei PinType_FBcl verwendet wird, siehe Chapter: 3.1 Information im FBlock-Typ zu einem FBlock bzw. Interface zu einem Modul. Diese Basisklasse enthält folgende Attribute/referenzierte Elemente:

Diese Informationen sind gleichfalls in einem Pin_Type_FBcl präsent und notwendig, siehe Chapter: 3.1 Information im FBlock-Typ zu einem FBlock bzw. Interface zu einem Modul

Die eigentliche class Pin_FBcl enthält die folgenden Elemente (Attribute und Referenzen):

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.2.3 FBlocks im Modul

Topic:.FBclDataModel.Modul.Modul.

Das Bild ist eine Wiederholung aus den Vorabschnitten:


Das obige Bild zeigt ein UML-Diagramm der Zusammenschaltung von Funktionsblöcken in einem Modul.

Die class Module_FBcl srcJava_FBcl/org/vishia/fbcl/fblock/Module_FBcl ist die Organisationsklasse für ein Modul bestehend aus Funktionsblöcken. Andere Bezeichnungen für ein solches Modul sind Funktionsplan, Subsystem (in Simulink gebräuchlich) oder der Inhalt eines Composite FunctionBlock (in IEC-61499 oder im 4diac-Editor).

Ein Funktionsplan ist in der IEC-61131-3 in Netzwerke eingeteilt. Daher ist unterhalb Module_FBcl die class Network_FBcl angeordent. Die Netzwerk-Einteilung gibt es in der IEC-61499-Norm so nicht. Aber sie ist zweckmäßig dann wenn ein komplexes Modul etwa mehrere Seiten umfasst. Die Zuordnung der FBlocks zu den Netzwerken im Modul soll dabei so organisiert sein, dass in der selben Abarbeitungszeitscheibe die FBlocks in den Netzwerke in der Netzreihenfolge abgearbeitet werden. Das bedeutet, eine Eventverdrahtung darf nur vorwärts zu einem folgenden Netzwerk erfolgen, nicht rückwärts. Ein Netzwerk darf aber insbesondere ein anderes Input-Event des Moduls verarbeiten. Diese Festlegung ist insoweit zweckmäßig, weil bei einer Seiten- (Blatt-) -zuordnung der grafischen Darstellung zu den Netzwerken der Betrachter von dieser Abarbeitungsreihenfolge ausgehen kann. Daten-Connections dürfen rückwärts auftreten, es handelt sich dabei aber immer um gespeicherte Daten der vorigen Abarbeitung. Der Datenfluss selbst ist in Netzwerkreihenfolge.

Ein Netzwerk kennt nun seine FBlocks, Instanzen von FBlock_FBcl und ihre Verdrahtung über die Pin_FBcl.

Die class FBlock_Type_FBcl ist der Repräsentant eines Typs eines FBlock. Alle Basistypen (AND, ADD etc) sind ebenfalls mit einem FBlock_Type_FBcl vertreten, siehe Chapter: 3.1 Information im FBlock-Typ zu einem FBlock bzw. Interface zu einem Modul.

Ein FBlock-Typ kann wie folgt vorliegen:


3.2.4 Ports im Modul, Außenschnittstellen, Interface

Topic:.FBclDataModel.Modul.Port.

Im Bild im Vorkapitel sind Dout_FBcl...usw. -Instanzen direkt vom Modul referenziert dargestellt, mit:

Da die ...inPorts jeweils nach einem Input eines FBlocks verdrahtet sind, müssen sie vom Typ ...out_FBcl sein, und umgekehrt.

Diesen Port-Instanzen sind Instanzen referenziert vom Interface, FBlock_Type_FBcl zugeordnet:

Die Zuordnung ist über den ixPin, die Pin-Nummer in der Pin- und Port-Instanz geregelt.


3.2.5 Event-Chain-Informationen

Topic:.FBclDataModel.Modul.Evchain.

Die EventChain srcJava_FBcl/org/vishia/fbcl/fblock/Evchain_FBcl ist die fortlaufende unverzweigte Aufeinanderfolge von Event-Verbindungen von Funktionsblöcken in einem Modul. Die zugehörigen Event-Operationen sollen nacheinander abgearbeited werden. Die EventChains werden immer nach dem Einlesen des Moduls aus den inneren Moduldaten, also aus der Zusammenschaltung der FBlocks im Modul, gebildet. Sie sind vom Modul Chapter: 3.2 Modul-Inhalt eines Composite-FB über srcJava_FBcl/org/vishia/fbcl/fblock/Module_FBcl#evChains referenziert.


Eine Event-chain beginnt an den Event-Inputs des Moduls. Die aggregierte srcJava_FBcl/org/vishia/fbcl/fblock/Operation_FBcl ist die zugehörige Event-Operation, siehe Chapter: 3.1.5 Operation_FBcl - Info welche Pins beeinflusst werden. Das ist die selbe Operation, die auch dem EvinPin zugeordnet ist: srcJava_FBcl/org/vishia/fbcl/fblock/EvinType_FBcl#operations. Einem EvinPin kann mehr als eine Operation zugeordnet sein, wenn die Eventverdrahtung gleich an Eingang aufgegabelt ist. Dann gibt es entsprechend mehrere Event-Chains für dieses EvinPin.

Event-chains können aber auch innerhalb des Moduls beginnen, oder sogar innerhalb von Funktionsblöcken im Modul. Das sind Abfolgen von Funktionalitäten, die im Modul bedingt aufgerufen werden wenn eine Eventabfolge gabelt (fork) oder wenn eine Event-chain an einem Event-Output-Pin beginnt das nicht von außen sondern von einer Statemachine eines inneren FBlock getriggert wird. Wie innere Event-Chain gebildet werden ist in Chapter: 6.3 Algorithmus zur Bildung von EventChains in einem Modul und Chapter: 6.6 Abhängige Operationen beschrieben.

Die Elemente einer Evchain_FBcl srcJava_FBcl/org/vishia/fbcl/fblock/Evchain_FBcl#evList referenziert Event-Eingänge von FBlocks, die unmittelbar ohne Verzweigung in einer Eventkette liegen und folglich nacheinander abgearbeitet werden sollen. Die EvChain referenziert die Event-Inputs von FBlock_FBcl-Instanzen des Moduls (Typ Evin_FBcl) zusammen mit der zugehörigen Operation_FBcl des Types des FBlocks. Beide Referenzen sind in je einem Element srcJava_FBcl/org/vishia/fbcl/fblock/Evchain_FBcl.EvCurr zusammengefasst. Beide Informationen sind notwendig, denn das evin des FBlocks kann im Module des FBlocks auch wieder auf mehrere EventChain verzweigen, folglich mehrere srcJava_FBcl/org/vishia/fbcl/fblock/EvinType_FBcl#operations haben. Welche die betreffende ist, ist hier direkt aggregiert. Über das evin ist der FBlock im Modul spezifiziert, wie im UML-Diagram erkennbar.

Zur Farbgebung im Diagramm: Grün sind die Teile, die von außen einem FBlock-Typ zugeordnet sind (links oben) und von diesem referenziert werden. Sie sind also sichtbar, wenn von einem FBlock die Typinformation abgerufen wird. Dazu gehören selbstverständlich die Pin- Ein- und Ausgänge, aber auch die damit verbundenen Operationen. Letztere enthalten im Element srcJava_FBcl/org/vishia/fbcl/fblock/Operation_FBcl#genCodeStmnt die Generierregel für den Zugriff auf die FBlock-Operation für die Zielcodegenerierung. Dabei werden von den doutPin über DoutType_FBcl#operDout auch Zugriffsfunktionen auf Ausgänge angeboten. EvinType_FBcl#operations sind ein oder mehrere Operationen die mit dem Event abgearbeitet (im code als Statements generiert) werden sollen.

Die türkisblauen Teile sind Objekte, die zum Inneren des Moduls gehören, also für die Codegenerierung der Operationen benutzt werden, die von außen aufgerufen werden.

Beim Aufruf trifft man wiederum auf die Typ-Informationn der FBlocks, die in einem anderen Grün (Limone) dargestellt sind. Man ist also wieder bei gleichen Objekten, aber eine Ebene tiefer, dem Submodul zugeordnet.

Die EventChain ist aufgeteilt. Der vordere Teil kann Statements erzeugen, wenn die betreffenden evin der FBlocks dies vorsehen. Insbesondere Basisfunktions-FBlocks sind allerdings nur für eine kombinatorische Verknüpfung zuständig, auch anwenderdefinierte FBlocks können rein kombinatorisch sein (Häufig als SimpleFBlock in StructureText geschrieben). Dann wird der Wert der Kombinatorik als Output-Operation berechnet, wenn auf den Ausgang zugegriffen wird. Damit besteht ein hinterer Teil der EventChain möglicherweise aus einem oder mehreren kombinatorischen Anteilen. Die EventChain ist immer in diesem Sinn zweigeteilt. Der anweisungserzeugende Teil ist eine Einheit, kann aber insgesamt entfallen, wenn nur Kombinatorik vorliegt. Die kombinatorischen Teile können aus mehreren Operationen bestehen.

Das folgende Bild zeigt solche Verhältnisse:


Es handelt sich zwar nur um eine kombinatorische Verknüpfung, die aber einen Ausgang mehrfach benutzt für mehrere Operationen. Dieser Wert darf nur einmal berechnet werden (ein anderer FB-Typ könnte Nebenwirkungen bei mehrfachem Aufruf haben). Folglich ist der Teil der EventChain über Ya1 statementerzeugend:

/**The event input operation
*/
inline void ev_a_Testcg_Fork1 (
  Testcg_CombinSimple* thiz, float a, float b ) {
  thiz->Ya1 = (a + b);  //setInstanceDataFromDout
}

Die anderen beiden FB stellen unabhänig Verknüpfungen (Kombinatorik) dar und sind in zwei Zugriffsoperationen auf die Ausgänge C-Code-generiert. Auch der Zugriff auf y1 ist eine Zugriffsfunktion, da der FB keine Variable außen setzt:

/**get the output value
 */
inline float y1_Testcg_Fork1 ( Testcg_CombinSimple* thiz ) {
  return thiz->Ya1;
}
/**get the output value
 */
inline float y2_Testcg_Fork1 ( Testcg_CombinSimple* thiz, float c ) {
  return (thiz->Ya1 * c);
}
/**get the output value
 */
inline float y3_Testcg_Fork1 ( Testcg_CombinSimple* thiz ) {
  return (thiz->Ya1 * 1.1f);
}

3.3 Modul-Inhalt eines Basic-FB

Topic:.FBclDataModel.ModuleBasicFB.

Ein Basic-FBlock ist laut IEC-61499 ein FBlock mit einer Statemaschine und mehreren zugehörigen Algorithmen, die original in ST (Structure text) notiert werden.


3.3.1 Basic-FB mit einer Operation pro Event ohne States

Topic:.FBclDataModel.ModuleBasicFB.BasicFBOnlyOps.

Mit einer einfachen Statemaschine kommt man zu einer faktisch stateless Realisierung, die aber im Basic-FB mehreren Events jeweils genau eine Operation und ein Ausgangsevent zuordnet.


Die Statemaschine ist nur dazu da, den Input-Events entsprechende Output-Events und Operations zuzuordnen. Die States sind temporär.

Diese Variante entspricht der Objektorientierten Klasse (FBlock-Typ ist eine Klasse) mit mehreren Operationen und gemeinsamen Daten, die für FBlocks aus IEC61131 so nicht bekannt ist. Dort gibt es pro FBlock immer nur genau einen Algorithmus.


3.3.2 Predefined FBlocks oder SFunctions

Topic:.FBclDataModel.ModuleBasicFB.SFunc.

Der Begriff SFunctions =^ System Functions ist Simulink entlehnt. Sie heißen dort so, weil ihr Inhalt nicht auf Anwender (Grafik-) Ebene definiert ist sondern auf einer gedachten System-Ebene außerhalb der grafischen Anwendung. In Simulink werden solche SFunctions entweder mit MATLAB-Code gefüllt oder in der Sprache der Codegenerierung, das ist meist C, realisiert.

Der Begriff SFunctions ist nicht unbedingt stimmig. Er drückt aus, dass man eigentlich grafisch programmiert und dabei System Funktionen benutzt, die auch textuell definiert sein können. Entgegen dieser wenn man so will 'Grundhaltung' kann man aber auch bewusst einen Programmierstil der Mischung von Grafik und Text leben: Einfache überschaubare Dinge werden besser mit den bekannten textuellen Programmiersprachen beschrieben.

Häufig und zweckmäßig werden Basisfunktionen oder einfache FBlocks in diesem Sinne auch direkt in der Ziel-Programmiersprache geschrieben, also in C oder C++. Für solche FBlocks könnte man besser den Begriff Predefined FBlocks verwenden, mit dem Blick aus der dafür nicht notwendigen Codegenerierung heraus. Für eine sichere Zielsystem-Codegestaltung ist dies eine zweckdienliche Herangehensweise, da so spezifische Dinge ohne Umwege formuliert werden können.

Die Basic-FB aus IEC61499 mit einem Event ohne States 3.3.1 Basic-FB mit einer Operation pro Event ohne States eignen sich nun für solche SFunctions mit Inhalt entweder pre-defined in C/++ oder mit Algorithmen in ST. Jeder Operation, die im Statechart definiert wird, entspricht eine predefined Operation im Zielcode. Die Zuordnung der Inputs und Outputs ist mit der Eventzuordnung zu Daten gegeben.


3.3.2.1 Simulink-kompatible C-SFunctions

Topic:.FBclDataModel.ModuleBasicFB.SFunc.SFuncIntern.

Aus dem Gedanken heraus, dass grafische Programmierung portabel sein soll, ist es nun zweckmäßig, C-Algorithmen so zu schreiben, dass sie sowohl in Simulink-SFunctions verpackt werden können, als auch in die Basic-FBlock ohne States von IEC-61499.

Dazu müssen Eigenheiten, die vom vorhandenen Simulink determiniert werden, berücksichtigt werden:


3.4 Modul-Inhalt eines Simple-FB

Topic:.FBclDataModel.ModulSimpleFB.

Die class Module_FBcl srcJava_FBcl/org/vishia/fbcl/fblock/Module_FBcl repräsentiert den Inhalt eines Moduls. Diese Daten werden beim Einlesen oder Konvertieren eines Moduls (z.Bsp. aus Simulink) gebildet und sind relevant für die Codegenerierung dieses Moduls und für das Ausschreiben der FBcl-Textrepräsentation. Für die Nutzung eines FBlocks als Typ mit einer bereits erfolgten Codegenerierung sind diese Daten nicht mehr relevant.

Ein Modul als Simple-FB enthält einen einfachen textuellen Algorithmus in ST (Structure Text)

Die Unterscheidung ist im Datenmodell gegeben mit null-Zeiger bzw. Besetzung des Zeigers auf:

Die Assoziation simpleFBalgm referenziert einen Algorithm_FBcl srcJava_FBcl/org/vishia/fbcl/fblock/Algorithm_FBcl. Dieser enthält im wesentlichen Statement_FBcl srcJava_FBcl/org/vishia/fbcl/fblock/Statement_FBcl, die den Algorithmus abbilden. Dieser ist hier nicht mehr als Structure Text spezifiziert, sondern in einer allgemeinen Form mit Revers Polish Notation für Expressions gespeichert. Folglich kann in diesem Datenmodell auch ein Algorithmus aus einer anderen Sprache entsprechend geparst abgebildet werden.


3.4.1 Aufbau der Daten eines Statement

Topic:.FBclDataModel.ModulSimpleFB.stmnt.

Ein Statement_FBcl srcJava_FBcl/org/vishia/fbcl/fblock/Statement_FBcl bildet mögliche Statements allgemein ab. Wichtig ist dass die Statements nach Structure Text konvertiert werden können, auch wenn sie aus einer anderen Sprache erzeugt worden sind. Das ist wichtig, da als Datenspeicherform der FBcl IEC-61499 gewählt wurde.


3.4.2 Expressions in Execode, allgemeine Definition in Revers Polish Notation

Topic:.FBclDataModel.ModulSimpleFB.expr.

Die class Execode ist allgemein als Opensource in srcJava_Zbnf/org/vishia/execode/Execode definiert, da auch mehrfach verwendet. Da sie hier essentiell ist, erfolgt eine kurze Beschreibung.

Die https://de.wikipedia.org/wiki/Umgekehrte_polnische_Notation (RPN) sollte bekannt sein. Wichtig ist, dass eine 'normale' Schreibweise mit Vorrangregeln der Operatoren und Klammern sich vom Parsing-Prozess eindeutig in die RPN transformieren lassen, aber auch die Rücktransformation in die 'normale' Schreibweise, die die nötigen Klammern erzeugt, ist möglich. Damit kann ein Expression nicht nur für die Abarbeitung, die in RPN-Schreibweise schnell realisierbar ist, sondern auch für die Speicherung einer Expression für spätere Codegenerierung verwendet werden.

Eine Speicherung in einem anderen Format, etwa einem Baum aus binären Ausdrücken unterstellt, dass der vom parser abgelieferte Baum in den Vorrangregeln der Operatoren auch die Zielsprache entspricht. Das ist jedoch nicht immer gegeben. Die RPN hat keine Vorrangregeln sondern nur eine Abarbeitungsreihenfolge. Die Vorrangregeln könenn bei der Erzeugung eines 'normalen' Ausdruckes aus der RPN anders gesetzt werden als bei der Syntax des Parsers. Mit 'normaler' Ausdruck ist die Schreibweise in den gängigen Programmiersprachen gemeint.

Die Speicherung der RPN in Execode fasst immer einen Operanden mit der nachfolgenden Operation zusammen. Die Operation wird mit dem Stack ausgeführt, auf den in der Regel der Operand zuvor gespeichert wurde. Genauer wird in der obersten Stackebene mit dem Akkumulator gearbeitet. Dieses Verfahren hat sich bei der Abarbeitung bewährt, da der häufigste verwendete Datenwert damit direkt in einem Register steht. Diese Herangehensweise ist auch bei der rein textuellen Konvertierung berücksichtigbar.

Folglich besteht ein Befehl (sub class Instruction) aus zwei wesentlichen Teilen:

Der Operand ist entweder

Zusätzlich steht in einer Instruction noch der textuelle Wert wie er beim Parsen erfasst wurde, das ist ggf. der Variablenname. Dieser kann benutzt werden für die Textgenerierung, sollte aber identisch mit dem über id referenzierten Namen der Variable sein.

Die Operation

a * b + c

würde in RPN direkt geschrieben werden als

a = b * c +

wobei = für ENTER steht. In dieser Class ist das gespeichert als

a set
b mult
c add

Entsprechend gilt:

a + b * c      //normal Expression
a = b = c * +  //RPN

a set
b set   //führt zu push a in den Stack
c mult
@ add   //@ steht für den Operand aus dem Stack

Die Rücktransformation aus dieser Form der RPN in die 'normale' Darstellung ist mit der Operation convertToNormalExpression() ausprogrammiert, die hier nur aus Test- und Debugausgabegründen vorhanden ist. Die Konvertierung für die C-Codegenerierung ist in srcJava_FBcl/org/vishia/fbcl/translate/Translation2Target_FBtrl in der Operation convertToOtx(Execode expr, Module_FBcl mdl) realisiert. Diese erzeugt den für die Codegenerierung notwendigen OutTextPreparer-Code, siehe Chapter: 3.5 Codegenerier-Regeln in Operation und Output-Datenpins


3.5 Codegenerier-Regeln in Operation und Output-Datenpins

Topic:.FBclDataModel.codegen.

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

Für Standard-FBlocks sieht die Generiervorschrift in Textform beispielsweise für das Output-Pin eines F_ADD wie folgt aus:

(<&IN1> + <&IN2>)

Diese Textform ist im OutTextPreparer als Folge von Anweisungen gespeichert, so dass die Generierung schnell abgearbeitet werden kann. Die Expression-Parts <&IN1> usw. werden bei der konkreten Codegenerierung ersetzt mit den Werten, die der Eingangsverdrahtung der benannten Eingänge entspricht. Sind dies ebenfalls Expressions, dann wird damit ein komplexer Ausdruck (Expression) gebildet. Daher ist diese Expression auch in Klammern gesetzt, falls sie folgend als Teil einer Expression verwendet wird.

Für FBlocks, deren Ausgangswerte mit der Input-Event-Verarbeitung gebildet wird (mit Operations), sieht die Ausgangsexpression typischerweise wie folgt aus:

(<&THIS_OBJ>)->element

Es wird ein Element in der struct oder class (C++) der zugehörigen Instanz des FBlock gelesen. <&THIZ_OBJ> ist ein Aufrufargument, das mit der Referenz auf die Instanzdaten bei der Codegenerierung belegt wird, gesteuert vom Bit mUsesThis siehe oben in diesem Abschnitt.

Für den Aufruf der Event-Input-Operation sieht die Codegenerier-Regel für C im allgemeinen wie folgt aus:

operName_FBType(<&THIS_OBJ>, <&IN1>, <&IN3>);

Für C++ wird dort generiert:

<&THIS_OBJ>->operName(<&IN1>, <&IN3>);

Der Typ ist für den C++-Compiler schon im THIS_OBJ gespeichert, es wird eine Klassenmethode gerufen. Im Beispiel wird für diese Operation IN1 und IN3 benutzt. Das Element usedInputs enthält den Wert 0x80000005 falls IN1 der erste und IN3 der dritte Input ist. Die Inputbezeichnungen werden für die Codegenerierung selbst nicht analysiert.

Diese Codegenerier-Regeln werden selbst generiert. Siehe TODO Chapter: 7 Codegenerierung aus FBcl


3.6 Datentypen

Topic:.FBclDataModel.dtype.

Insbesondere in frei verwendbaren Library-Modulen ist ein konkreter numerischer Datentyp oft nicht festgelegt. Dieser ergibt sich aufgrund der Verdrahtung. In IEC-61499 als auch in der IEC-61131-3 für Automatisierungsgeräte sind dafür Datentypen wie ANY_MAGNITUDE oder ANY_NUMBER vorgesehen. Simulink kennt für diesen Zweck den derived-Datentyp.


3.6.1 Arten von Datentypen

Topic:.FBclDataModel.dtype.dtypeKind.

Es können von der Nutzung her folgende Arten unterschieden werden:

Es gibt ein weiteres Thema: Abhängige Datentypen:

Ein Datentyp kann entweder skalar sein, oder ein Array, oder für numerische Typen ein komplexe Zahl mit Real- und Imaginäranteil. Der Array- oder Complex-Type ist ein eigener Datentyp, aber er basiert auf einem skalaren Typ. Wenn beispielsweise ein FBlock skalare Inputs, und passend zu deren Typen einen complex-Output hat, dann muss vom Typ des Complex-Output auf den skalaren Typ referenziert werden. Die Typen sind abhängig.

Eine andere aber vergleichbare Problematik tritt bei den codegen-flexible-Datentypen auf: Es kann sein dass bestimmte Datentypen an Ein- und Ausgängen zusammenhängen, diese also miteinander konform bestimmt werden müssen.


3.6.2 Definition der Datentypen und deren Abhängigkeiten

Topic:.FBclDataModel.dtype..

Die class DataType_FBcl enthält die Eigenschaften des Datentyps selbst. Dazu gehört

* Typename, char als Kürzel, Anzahl Array-Elemente, RealOrComplex.

Die class DataTypeRef_FBcl enthält DataType_FBcl als Composite.

Ursprünglich wurde DataTypeRef_FBcl geschaffen, um den referenzierten Datentyp auszuwechseln (Java-Codegenerierung aus Simulink in 2018). On dies noch zielführend ist, ist zu klären.

Jedenfalls können mehrere DataTypeRef_FBcl auf den gleichen Basis DataType_FBcl verweisen, insbesondere bei anderen Konstellationen für Anzahl ArrayElemente und RealComplex. Das ist allerdings derzeit nicht der Fall, da diese Infos in DataType_FBcl gespeichert sind. Es scheint derzeit eher so, als ob es eine eineindeutige Verbindung zwischen DataTypeRef_FBcl und DataType_FBcl gibt (Stand 2020-08-08), in Klärung.

Der DataTypeRef_FBcl enthält nun weitere Elemente:


3.6.3 Zusammenhang zwischen Datentyp im gerufenen Modul und im nutzenden Modul

Topic:.FBclDataModel.dtype..

Die Instanzen von DataTypeRef_FBcl selbst im gerufenem Modul (Library-Modul) und beim Aufrufer dürfen nicht gemischt werden. Sie sind zwar im Typ abgestimmt, es müssen aber verschiedene Instanzen sein. Ansonsten würde ..... dürfen aber passiv referenziert werden! Das passt aber nicht weil sie ihre Dependencies wiederum haben.

Wenn bFix gesetzt, werden keine DataTypeRef_FBcl.registerConnection(...) registriert. Folglich kann der Dtyp referenziert werden, weil er fest ist!


3.6.4 Bisheriger Text

Topic:.FBclDataModel.dtype..

Für die grafische Programmierung gilt bei den meisten Tools folgende Herangehensweise:

Die Verbindung eines allgemeinen Typs (ANY_NUMBER) mit einem konkreten Typ ist zulässig. Bei Simulink sind dies die mit derived gekennzeichneten Typen an Pins. Die Verbindung verschiedener konkreter numerischer Typen ist aber nicht zulässig (z.B. REAL mit INT). Es muss also ein FBlock zur Typkonvertierung zwischengeschaltet werden. Das ist an sich auch gut so, denn die Datentypen sind eben nicht egal. C und C++ gehen hier viel freier um, als es einer sicheren Anwendung gut tut. Die Grafischen Programmiertools sind konsequenter.

Die srcJava_FBcl/org/vishia/fbcl/fblock/DataTypeRef_FBcl wird von den Pins referenziert, wobei es eine Organisation von abhängigen Datentypen gibt und die selben Datentypen auf verschiedenen Pins mit einer Referenz auf genau die selbe Instanz gekennzeichnet wird. Wird ein FBlock verdrahtet, dann werden vorhandene Referenzen korrigiert.

Diese Instanz ist kein fester Datentyp, sondern referenziert einen Datentyp srcJava_FBcl/org/vishia/fbcl/fblock/DataType_FBcl. Bei der Codegenerierung eines Library-Moduls werden die Referenzen auf die konkreten Typen ersetzt, damit wird generiert. Folglich gibt es mehrere Varianten der Codegenerierung für das selbige Library-Modul für verschiedene Typen, die im Filenamen und in den Operation-Namen entsprechend gekennzeichnet sind. Für C++ kann die Eigenschaft der ArgumentTypabhängigkeit der Operation (Signatur) für den Linker genutzt werden, für C ist dies nicht möglich. Daher werden die Operationen entsprechend gekennzeichnet (in 2019-11 noch TODO).

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.7 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 Lesen von IEC61499-Files, Umsetzen in das FBcl-Datenmodell und Aufruf derZielsprachengenerierung

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

Last changed: 2020-07-17

Im Constructor werden die main-Aufrufargumente übergeben, ein Projekt angelegt und das Codegenerier-Templat eingelesen:

  FBcl2c(CmdArgs cmdArgs, MainCmdLogging_ifc log) throws ... {
   this.cmdArgs = cmdArgs;
   this.log = log;
   this.prj = new Prj_FBCLrd(log, cmdArgs);
   this.trlScripts = ReadTranslationScripts.read(cmdArgs.codeTpl);
 }

Das Projekt speichert Einstellungen und alle intern gelesenen FBlocks, die dann mehrfach verwendet werden können, bzw. zur Übersetzung anstehen.

Es wird genau ein File übergeben.

 void exec(File fIn) throws InstantiationException, IOException {
   try {
     Module_FBcl mdl = this.prj.readerFBcl.read(fIn, 0);
     Translation2Target_FBtrl.translate(mdl, this.cmdArgs.dirGenSrc, this.trlScripts, 0);
   } catch(IOException exc) {
     Debugutil.unexpected();
   }
 }

Das Lesen dieses Modules führt auch zum Lesen aller gerufenen Sub-Module, die in einem Search-Path aufgefunden werden müssen.

Die Zielsprachgenerierung wird im Abschnitt Chapter: 7 Codegenerierung aus FBcl beschrieben.


4.2 Lesen von IEC61499-Files, Konvertieren in FBcl-Datenmodell

Topic:.FBclRead.readMdl.

Last changed: 2020-07-17

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

zuständig.

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

Folgende Operation realisiert das Lesen der FBcl-Files, führt die Konvertierung in die Zielsprache aus und liefert als Interface den Type des Moduls zurück.

 public FBlock_Type_FBcl read(File fileFBCL, int recursive)
   throws NoSuchFileException, InstantiationException {

Das Raw-Datenmodell aus den IEC 61499-Files wird erhalten über Aufruf : srcJava_FBcl/org/vishia/fbcl/readFBcl/ReaderFBcl#read-java.io.File-int-, siehe todo

IEC61499data dataRaw = readFileRawData(fileFBCL);
//check content
IEC61499data.Fb_type_declaration fBtype = dataRaw.get_FBType();

Wenn der FBlock ein Composite-FB ist, dann enthält er intern ein Netzwerk aus weiteren FBlocks. Diese weiteren FBlocks werden jetzt erkannt, und führen möglicherweise zum rekursiven Aufruf genau dieser Routine. FBlocks können auch Standard-FBlocks sein, die bereits bekannt sind, oder FBlocks, die bereits übersetzt wurden und daher in dem oben genannten mapReadModules enthlaten sind.

mdlRaw = fBtype.get_FBNetwork();
if(mdlRaw !=null) {
  for(IEC61499data.Fb_instance_definition fbraw: mdlRaw.get_FB()) {
    String sTypeFB = fbraw.get_Type();
    FBlock_Type_FBcl typeFB = this.prj.stdFBlocks.getType(sTypeFB);
    if(typeFB == null) { typeFB = searchUserModule(sTypeFB,0); }
    if(typeFB == null) {
      //If it is not a standard FB, nor a also known submodule-FB.
      //It has to be found in a user's path.
      //Read it and store in mapReadModules
      throw new NoSuchFileException("src file of inner module not found: " + sTypeFB);
    }
  }
}

Die Routine searchModuleFile(sType) sucht entsprechend gegebenen Searchpath-Einstellungen in den CmdArgs die FBcl-Files der Submodule, wobei die Extension fbt (XML-Modellfile eines Composite-FBlock), fbc und fbcl berücksichtigt werden. Files im Searchpath mit dieser Extension werden als Module-FBcl-File gewertet.

Die folgende Operation konvertiert das Roh-Modul nach IEC61499 in das FBcl-Datenmodell:

 //
 //Convert from the raw (immediately parsed) data to the FBcl data model:
 //
 Module_FBcl mdl = prepareModule(fBtype);

Dabei wird erkannt, ob es sich um einen Composite-FB (mit internem Netzwerk), einen Simple-FB (mit einer in Structure Text geschriebenen Operation) oder um einen Basis-FB (mit Statemachine) handelt. Die IO-Pins werden abgelegt und der FB-Type] wird erzeugt.

Das gelesene Modul im FBcl-Datenmodell wird einem Check unterworfen, dann werden die gelesenen Informationen als Test wieder ausgeschrieben, bei Bedarf zusätzlich für das Debuggen noch ein *.fbcldata-File:

CheckModule_FBrd.check(mdl, false);
//
if(this.cmdArgs.dirWrTestFBcl !=null) {
  this.writerFBcl.writeFBCL(new File(this.cmdArgs.dirWrTestFBcl, mdl.name() + ".fbcl"), mdl);
  this.wrDataFBcl.writeModule(new File(this.cmdArgs.dirWrTestFBcl, mdl.name() + ".fbcldata"), mdl);
  if(this.cmdArgs.dirWrResults4diac !=null) {
    this.wrXmlFBcl.writeFBCL(new File(this.cmdArgs.dirWrResults4diac, mdl.name() + ".fbt"), mdl, this.xmlReader);
  }
}

Als letztes wird der FBlock-Typ mit der Referenz zu seinen Interndaten in der Projekt-Map gespeichert:

FBlock_Type_FBcl fbType = mdl.ifcFB;
this.prj.mapReadModules.put(fbType.name(), fbType);

Damit sind die gelesenen IEC-61499-Daten als FBcl-Datemodell gespeichert und können für die Codegenerierung verwendet werden.


4.3 Ausgabe der Daten des gelesenen Datenmodells für Testzwecke

Topic:.FBclRead.WrData.

Last changed: 2020-09-16

Mit der Option -dbgFBcl beim Cmdline-Start des Übersetzers lässt sich ein Verzeichnis angeben. Ist die Option gesetzt, dann wird in diesem Verzeichnis mehrere Files angelegt:


4.3.1 Präsentation in fbcldata

Topic:.FBclRead.WrData.fbcldata.

Last changed: 2020-09-16

Das File ist in ähnlichen Abschnitten aufgebaut wie ein fbcl-File nach IEC-61499, aber mit ergänzenden Angaben. Die Gliederung ist

==Ifc==
===EVENT_INPUT===
  ...Angabe aller Event-Inputs mit zugehören Operation_FBcl
===EV_OUTPUT===
  ...Angabe aller Event-Outputs
===VAR_INPUT===
  ...Angabe aller Daten-Inputs
===VAR_OUTPUT===
  ...Angabe aller Daten-Ouputs mit zugehören Operation_FBcl
===EV_DEP_OPER===
  ...Angabe aller fiktiven Event-Inputs für abhängige innere Operationen
==EvChains==
  ...Angabe aller Evchain_FBcl (einzelne Eventketten)
==FBlocks==
  ...Angabe aller FBlocks mit Verbindungen und weiteren Details

Am Beispiel dargestellt mit Erläuterungen:

==Ifc==
===EVENT_INPUT===
ev_a, a, b, c
  ev_a[0] => ev_a_Ya1(#0 =$3) ev:1/1->0/1/3 D:3/7->0/3/7 ~>Ya3
    ~>$Ya2[0] => Ya3(#3 .0) ev:4/5->0/2/2 d:0/7->0/4/4 <~ev_a_Ya1
  ev_a[1] => ev_a_Mulac(#1 =$5) ev:1/1->0/0/c D:5/5->0/0/f8 ~>Dq1 ~>Ya4
    ~>$Mulac[0] => Dq1(#4 =$0) ev:8/9->0/8/8 D:0/5->0/30/f0 <~ev_a_Mulac
    ~>$Mulac[1] => Ya4(#5 .0) ev:8/9->0/4/4 d:0/7->0/8/8 <~ev_a_Mulac
upd
  upd[0] => upd(#2 =$0) ev:2/2->0/10/10 D:0/0->0/c0/c0

Es gibt ein Event-Input ev_a dem die Datenpins a, b und c zugeordent sind. Dem Event direkt zugeordnet sind zwei Operationen. Das liegt daran, dass das Event-Input im Modul auf zwei EventChains verzweigt, also zwei unabhängige Funktionalitäten intern angesprochen werden. Nach der nochmaligen Benennung des Events und [0] des Index der Operation EvinType_FBcl#operations wird die Operation dargestellt (Ausgabe aus der toString()). Diese ist in 4.3.2 Darstellung der Operationen in fbcldata erläutert.

Es sind hier gleich auch die abhängigen operationen nach ~> dargestellt. Damit ist insgesamt erkennbar, welche Daten- und Eventpins benutzt und beeinflusst werden: Die erste Operation setzt unmittelbar dout0 und dout1 (mask ../3/..) aber beeinflusst mittelbar auch dout2 (mask ../7). Das geschieht durch die depending operation Ya3, die mit mask ..4/4 nur pin dout2 setzt. Jedes Bit der hexadezimal präsentierten Maskierung stellt einen Pin dar. Die Umrechnung von Hex-Mask-Darstellung auf die Pin-Nummer ist dem Geübten durch einfache Auszählung möglich. Jede Ziffer repräsentiert 4 pins. Die Hexa->Binär-Abbildung für 16 Kombinationen lässt sich mit einer kleinen Tabelle verwirklichen. Es ist hier eine knappe Darstellung gewählt.

Die zweite Operation des ev_a beeinflusst auch die Datenbits dout6 und dout7, die eigentlich zum upd-Event gehören. Diese Information ist wichtig, denn für die Update-Funktionalität uss die ev_a[1]-Operation jedenfalls gerufen werden.

Das zweite Event-Input-Pin ruft genau eine Operation auf.

===EV_OUTPUT===
evo_y2, y1, y2
evo_y3, y3
evo_y4, y4
setok, y5, y6
updok, q1, q2
===VAR_INPUT===
a dataType:float#1
b dataType:float#1
c dataType:float#1

Die Event-Outpin und DinPin sind unspektakulär. Zu beachten ist, dass die zugehörigen Datenpin zu den Events mit , abgetrennt sind, nach dem Namen des Event-Pin.

Die Datenpins benennen den Datentyp, der außen vorgegeben oder intern festgestellt wurde.

===VAR_OUTPUT===
y1 dataType:float#1
   y1 := y1(#6 .$0) ev:1/1->0/1/1 D:0/0->0/1/1
y2 dataType:float#1
   y2 := y2(#7 .$4) ev:1/1->0/1/1 D:4/4->0/2/2

Die Daten-DoutPin benennen ebenfalls den Datentyp. Darunter wird eine zugehörige Output-Zugriffsoperation angegeben, falls vorhanden. Diese ist entweder eine kombinatorische Funktion (Codegenerierregel in Operation_FBcl#genCodeStmnt angegeben) oder eine einfache Zugriffsfunktion auf die innen gespeicherten Daten. Nur Ausgänge, die zwangweise auf eine außen anzugebende Variable schreiben, haben keine Output-Zugriffsfunktionen. Solche Outputs sind insbesondere deshalb als Speziallösung eingeführt, weil die S-Function in Simulink so arbeiten. Will man gleiche Kerne in C/++ für Simulink und IEC61499 haben, dann muss es dieses Feature geben. Die Kernfunktionen schreiben auf Variable. Es widerspricht nicht direkt den Vorgaben der IEC61499, ist als eine mögliche Variante.

===EV_DEP_OPER===
$Ya2
  $Ya2[0] => Ya3=(#3 .0) ev:4/5->0/2/2 d:0/7->0/4/4 <~ev_a_Ya1

Die Ya2-Operation ist oben bei den ===EVENT_INPUT=== bereits als abhängige Operation aufgeführt, hier nochmals in der Übersicht aller abhängigen Operationen benannt. Die Namen der Events beginnen mit $. Sie sind gleichwertig wie die EvinPin des FBlock (Datentyp EvinType_FBcl), sind aber nicht außen verdrahtet sondern werden stimuliert als Folge der vorigen Operation.

Abhängige Operationen entstehen durch Verzweigungen (fork) der EventChain im FBlock. Mit optimierter Codegenerierung werden sie nicht aufgerufen, wenn deren Ausgänge nicht benutzt sind und die Operation selbst nicht unbedingt ist (Operation_FBcl#isNecessaryEventOper--). An den hier aufgeführten Ausgängen (hexa-Mask) ist erkennbar, welche Ausgänge das betrifft.

Die Beispielangaben (unvollständig) entstammen dem folgenden Modul (Composite-FBlock):


.

==EvChains==
 1: ev_a[0] => ev_a_Ya1(#0 =$3) ev:1/1->0/1/3 D:3/7->0/3/7 ~>Ya3 ==>fork...
    =>Ya1.REQ[0]+$
    ->Ya2.REQ+
 2: ev_a[1] => ev_a_Mulac(#1 =$5) ev:1/1->0/0/c D:5/5->0/0/f8 ~>Dq1 ~>Ya4 ==>fork...
    =>Mulac.REQ[0]+$
 4: upd[0] => upd(#2 =$0) ev:2/2->0/10/10 D:0/0->0/c0/c0 ==>updok:4
    =>Q1.REQ[0]=$ =>Q2.REQ=$
 8: $Ya2[0] => Ya3(#3 .0) ev:4/5->0/2/2 d:0/7->0/4/4 <~ev_a_Ya1 ==>evo_y3:1
    ->Ya3.REQ[0]+! ->Ya31.REQ+->Y32.REQ+
10: $Mulac[0] => Dq1(#4 =$0) ev:8/9->0/8/8 D:0/5->0/30/f0 <~ev_a_Mulac ==>setok:3
    =>Dq1.REQ[0]+=>Fq1.REQ+$ =>Addq1.REQ+$ =>Dq2.REQ+=>Fq2.REQ+=>Addq2.REQ+$
    ->Ya6.REQ+
20: $Mulac[1] => Ya4(#5 .0) ev:8/9->0/4/4 d:0/7->0/8/8 <~ev_a_Mulac ==>evo_y4:2
    ->Ya4.REQ[0]+

Die EventChains werden vorn mit ihrer Hexa-Mask benannt wie sie als Querverweis in den Pins der FBlocks aufgeführt sind. Die Hexa-Mask zählen ab Index 0, repräsentiert mit 1.

Die erste Zeile jeder EventChain zeigt folgend die gleiche Darstellung wie bei den ===EVENT_INPUT===, das ist die toString()-Ausgabe der Operation_FBcl#toString--). Als letztes wird nach ==> das Output-Eventpin dargestellt. Die Angabe fork... bedeutet, dass es eine Aufzweigung mit nachfolgenden abhängigen EventChains gibt. Das kann auch sein, wenn das OutputEvent an einem Pin hängt aber gleichzeitig eine weitere EventChain speist. Wird das Output-EventPin benannt, dann folgt im Beispiel :4 dessen Pin-Nummer.

Die weiteren Zeilen benennen die FBlocks und dessen angesproches evin der Elemente aus der Evchain_FBcl#evList. Beim ersten Element wird der [index] der zugehörigen Operation im EvinType, wie sie in Evchain_FBcl.EvCurr angegeben ist, dargestellt.

Der vordere anweisungserzeugende Teil mit einer eigenen Zeile und ==> vor den Eventinputs dargestellt. Der nicht anweisungserzeugende Teil bzw. der Teil, der interne Variablen aufgrund Kombinatorik setzt, ist mit --> gekennzeichnet. Mehrere FBlock.evin sind nacheinander in der Zeile benannt.

Ein nachfolgendes = wie bei =>Q1.REQ[0]=$ =>Q2.REQ=$ nach dem Evin-Namen und ggf. [ixOper] bedeutet: Die Evin-Operation des Typs des FB erzeugt Statements. Das ist nicht der Fall bei kombinatorischen FB. Im Beispiel steht hier ein F_MOVE der als Anweisung des Setzen der Outputvariable enthält (wirksam wenn dessen Event kommt).

Ein nachfolgendes + wie bei den meisten Elementen bedeutet, dass ein Ausgang beim Typ eine DoutType_FBcl#operDout aufweist, also mit Kombinatorik berechnet wird. Folgt danach keine weitere Kennzeichnung, dann ist dies ein kombinatorischer Zwischenwert, der in der Codegenerierung lediglich als Teil einer Expression in (...) auftritt.

Ein nachfolgendes $ bedeutet, dass ein dout-Pin dieser Event-Operation mit einer Instanzvariable in der Codegenerierung versehen wird. Bei mehreren solcher Outputs gibt es mehrere $$$. Die Instanzvariable bedeutet, dass der Wert an dieser Stelle von mehreren EventChain verarbeitet werden kann, wie er von der zugehörigen EventChain gesetzt wurde. Folglich darf es diese Kennzeichnung nur in dem anweisungserzeugenden Teil der EventChain geben, mit => vor dem FB.evin gekennzeichnet. Die Kennzeichnung hir im Beispiel mit =>Q1.REQ[0]=$ =>Q2.REQ=$ zeigt an, dass beide Ausgänge Instanzvariable haben und dass die evin der FBlocks Anweisungen haben. Da keine Kennzeichnung mit + erfolgt, setzt die Anweisung des evin die Instanzvariable. Mit einer Kennzeichnung +$ wird der Wert der Instanzvariablen in der Eventoperation mit der kombinatorischen Operation im Typ berechnet, also mit einer Expression gesetzt.

Ein nachfolgendes ! bedeutet, dass ein dout-Pin dieser Event-Operation auf eine lokale Zwischenvariable gelegt ist. Dies soll nur im Zusammenhang mit +! auftreten, da die lokale Variable geeignet mit einer Expression gebildet werden muss. Im Beispiel ist das bei ->Ya3.REQ[0]+! ->Ya31.REQ+->Y32.REQ+ der Fall: Der Ausgang von Ya3 verzweigt und wird innerhalb der selben EventChain ohne Fremdverzweigung vorwärts wieder verwendet. Daher wird bei der Codegenerierung folgendes erzeugt:

/**get the output value
 */
inline float y3_Testcg_CombinQ ( Testcg_CombinQ* thiz, float a ) { //===operEvin===
  float Ya3 = (thiz->Ya1 * 1.1f);
  return ((a * Ya3) + Ya3);
}

Für den Zwischenwert braucht es keine Instanzvariable, sondern nur einen lokalen temporären Speicher, mit ->Ya3.REQ[0]+! gekennzeichnet.

==FBlocks==
.....
Ya3 : F_MUL | steptime:<??null??>
  ->REQ := Ya2.CNF
    CNF -> Ya31.REQ
  =>IN1 := $Ya1.OUT:F | dataType:float#1 evChain:8/0
  =>IN2 := <??null??> | dataType:float#1 evChain:8/0
  ! OUT => Ya31.IN2:F, Y32.IN2:F oper:0/0 evChain:8/0

Die ==FBlocks== sind nur auszugsweise mit dem oben benannten Ya3 gezeigt:


4.3.2 Darstellung der Operationen in fbcldata

Topic:.FBclRead.WrData.operdata.

Diese Darstellung wird durch Operation_FBcl#toString() erzeugt.

Ein Beispiel für eine Event-Operation ist, Output-Zugriffsoperation, ist:

$Mulac[0] => Dq1(#4 =$0) ev:8/9->0/8/8 D:0/5->0/30/f0

Dies ist im 4diac-Beispiel die abhängige Event-Operation, die am FBlock Mulac beginnt, das evout dieses FB ist aufgesplittet. Der Zweig ab dem Dq1 ist eine eigene EvChain, die mit dieser Event-Operatio vertreten ist.

Nach der Klammer werden für Event und Data die benutzten Pins angegeben, und zwar als Maske in Hexa-Darstellung so wie intern gespeichert. Für einfache Module mit wengen I/O kann man als maschinencodeorientierter Leser sehr schnell die Ports zuordnen, beispielsweise ist 6 das Port 1 und 2, Zählung begonnen mit 0, oder 18 das Port 3 und 4. Man wird sehr schnell auf die erwartenden Ports schließen können. Das ist eine gedrungene debugangemessene Darstellung.

ev: und d können auch großgeschrieben auftreten:

Bei der Inputmask für evin und din und für evout gibt es immer zwei Angaben:

<&direkt genutzte Input-Pins>/<&virtuelle Input-Pins>

Die virtuellen Input-Pins betreffen abhängige Operationen, die zuvor zu rufen sind (Operation_FBcl#prevDepending. Die prevDepending Operation sind allerdings in der genannten eigenen Liste aufgeführt, für die Auswahl werden die virtuellen Input-Pins nicht benutzt. Sie sind nur informatorisch.

Bei der Outputmask sowoh für evout als auch dout gibt es drei Angaben:

<&Special-Output-Pins>/<&direkt genutzte Output-Pins>/<&virtuelle Output-Pins>

Die special.Output-Pins sind für

In diesem Beispiel sind die Output q1 und q2 (Mask c0) die nur vom upd-Event gesetzt werden, als virtuelle Outputs angegeben. Denn: Der Outputwert bei upd richtet sich nach dem Setzwert vor dem F_MOVE-Block (in Simulink ist das ein Unit-Delay). D.h. diese Operation beeinflusst die genannten Outputs, nur eben nicht direkt.

Ein Beispiel für eine OperDout, Output-Zugriffsoperation, ist:

y4 := y4(#9 .$2) ev:8/9->0/4/4 D:2/2->0/8/8

Die restlichen Kennzeichnungen sind identisch wie bei Event-Operationen.


5 Umsetzung der Datenflussreihenfolge auf Eventkopplung

Topic:.FBclDflow2Ev.

Last changed: 2019-11-26

Dieser Dokumentationsabschnitt befasst sich mit der Umsetzung der Datenflussorientierten Abarbeitung in Eventverbindungen nach IEC-61499 für die FBcl-Abbildung. Dieser Algorithmus wird benutzt für eine Vielzahl Konvertierungen aus von Grafiktools, die eben keine Eventverbindung oder explizite Rechenzeitreihenfolge-Angaben an FBlocks kennen. Ein typischer Vertreter dafür ist Simulink. Auch die Umsetzung aus Automatisierungsgeräteprojektierungen nach IEC-61131 läuft auf diesem Weg.


5.1 Algorithmus

Topic:.FBclDflow2Ev.prc.

Der Algorithmus ist in der class Dataflow2Eventchain_FBrd


5.2 Operationen zusammenfassen: Event Cluster

Topic:.FBclDflow2Ev.evCluster.

Die Bildung der Event-Chain aus dem Datenfluss erfolgt in Dataflow2Eventchain_FBcl srcJava_FBcl/org/vishia/fbcl/readSource/Dataflow2Eventchain_FBrd mit Aufruf der Operation prc(Write_Module_FBwr mdlwr), also mit Übergabe des fertig erstellten Moduls aus FBlocks aber noch ohne Eventverbindung.

Wesentlich ist die class EventCluster srcJava_FBcl/org/vishia/fbcl/readSource/EventCluster_FBrd. Als Event Cluster wird eine Ansammlung von FBlocks, genauer deren Operation oder Input-Events verstanden, die in einer Eventkette hängen, deren Reihenfolge aber bei der Bildung des EventCluster noch nicht festgelegt ist. Das Kriterium zur Zuordnung zu einem Event-Cluster ist: Alle diese Operations sind notwendig für genau einen oder bestimmte zusammenhängende Datenausgänge.

Damit ist die Intension des EventCluster bzw. der zu bildenden Event-Chains bereits genannt: Jeweils eine Event-Chain (Kette von Eventverbindungen) ist für eine Ausgangsfunktionaltität (ein Ausgangswert oder zusammengehörige Ausgänge) bestimmt. Dahinter liegt die Idee der


5.3 Temporäre Instanzen für Event-Operationen: MetaEvin_FBrd

Topic:.FBclDflow2Ev.mevin.

In der mapEvin werden Instanzen für jede Event-Operation dieses Moduls gespeichert: MetaEvin_FBcl srcJava_FBcl/org/vishia/fbcl/readSource/MetaEvin_FBrd. Diese sind namentlich sortiert nach (Fblockname+OperationName) und enthalten Informationen, die für diesen Algorithmus der Event-Chain-Bildung temporär notwendig sind.

Wesentlich sind nicht die FBlocks selbst, sondern die Operation, die den Input-Events der FBlocks zugeordnet sind. Bei Basis-FBlocks ist dies eine 1:1-Zuordnung, es gibt ein Input-Event und genau eine Operation. Komplexe FBlocks können intern mehrere Event-Chain, mehrere unabhängige Funktionalitäten enthalten. Diesen ist je eine Operation ab dem entsprechenden Event-Input des FBlock-Typs zugeordnet, bzw. es kann noch abhängige interne Operationen geben, siehe Chapter: 3.1.5 Operation_FBcl - Info welche Pins beeinflusst werden und Chapter: 6 Bildung von Operations aus den aufeinanderfolgenden Eventverbindungen. Für einen aufgerufenen FBlock sind diese Operationen gebildet (im Typ), sie sind die Bausteine des Moduls. Die FBlocks sind nur die äußerlichen Bausteine oder Funktionsblöcke.


5.4 Backward-Analyse

Topic:.FBclDflow2Ev.databack.

Um diejenigen Operationen herauszufinden, die auf einen Ausgang oder zusammengehörige Ausgänge wirken, muss von den Ausgängen ausgegangen werden, also rückwärts.

In der Operation detectEndOutpinFBlockForEvCluster() werden allen Outports bzw. dessen treibende Outpins des vorgelagerten FBlock EventCluster zugeordnet. Dabei werden Outports berücksichtigt, die zusammengehören: Diese werden dem selben EventCluster zugeordnet.


6 Bildung von Operations aus den aufeinanderfolgenden Eventverbindungen

Topic:.FBclEvChain.

Last changed: 2020-10-19

Dieser Dokumentationsabschnitt befasst sich mit der Bildung von Operations aus den aufeinanderfolgenden Eventverbindungen.

Diese Bildung der Operations wird nach Einlesen eines FBcl-Modells nach dem Aufbau der Eventverbindungen ausgeführt. Eine Event Chain ('EventChain') ist die Verbindung von FBlocks über ihre evin und evout in einer Reihe ohne Verzweigungen. Ein einfaches Modul kann aus einer Event Chain bestehen. Verzweigungen und Vereinigungen ('E_REND') bilden mehrere EvChain.

Die Abarbeitung eines Moduls könnte nun in der Reihenfolge der EvChains ablaufen, wobei verzweigte EventChains die Möglichkeit der Parallelbearbeitung in Multicoresystemen ermöglichen. Jedoch wird im folgenden Kapitel die Herausforderung oder Anforderung nach Optimierung der Abarbeitung dargestellt. Das bedeutet, dass die EventChain nochmal aufgegliedert wird und kombinatorische Verknüpfungen extra behandelt werden. Zur Entscheidung, was bei der konkreten Nutzung eines Moduls in richtiger Reihenfolge aufgerufen werden muss, bedarf es der Ausführung der angeschlossenen Modul-Pins pro EventChain. Da EventChains bei Verzweigungen auch nur intern im Modul, nicht nur zwischen den Event-Pins auftreten, bedarf es der Speicherung von Abhängigkeiten und sogenannten fiktiver Evin-Pins. Damit entsteht zwar ein komplexes System, das aber für Optimierung der Codeabarbeitung notwendig ist.


6.1 Optimierungsherausforderung

Topic:.FBclEvChain.opt.

Folgendes Modul sei als Beispiel aus der Praxis vorgestellt. Es handelt sich um einen Teil einer Feldorientierten Regelung von Motoren oder Netzen, also elektrischen Signalen. Solche Regelungen müssen oft in einer kurzen Abtastzeit gerechnet werden, 50 µs oder 20 µs. Das Erfordernis der kurzen Abtastzeiten ergibt sich daraus, dass elektrische Signale mit niedrigen Induktivitäten entkoppelt werden, es daher bei Störungen hohe Stromanstiegsgeschwindigkeiten gibt, andererseits in Kabeln Schwingungen auftreten können bis in den kHz-Bereich, die durch die Regelung entsprechend gedämpft werden müssen. Die Stellglieder können mittlerweile diese hohen Abtastzeiten realisieren, die Prozessoren können das grundsätzlich auch, aber eine optimierte Programmierung ist Voraussetzung. Bei Embedded-Control hat man häufig die 'Nichtfunktionale Anforderung' Wärme, Stromverbrauch oder Preis des Prozessors. Man hat nicht beliebig technisch machbare Rechenleistung. Dies zur Einordnung.


Das Modul ist in Simulink gezeichnet, aus dem sich FBcl generieren lässt. Im Bild vor dem PID-Regler ist ein Block angeordnet, der einen komplexen Wert oder Vektor mit zwei Koordinaten in einen Betrag und Winkel umrechnet. Mit dem Betrag gibt es eine Amplitudenregelung, der Winkel wird ebenfalls weiter verarbeitet und als aAngle ebenfalls herausgeführt. Das kann eine Null-Regelung des Winkelwertes sein. Diese kann in einigen Fällen notwendig sein, in anderen Fällen ist der Winkelwert aufgrund anderer Vorkehrungen immer in einem 0-Bereich oder er ist nicht relevant. Folglich wird von diesem Modul der Winkelausgang nicht immer verwendet.

Mehr noch: In einer Testphase kann der Winkelwert von hoher Bedeutung sein, beispielsweise auch nur zur Beobachtung. In der Releasephase braucht man ihn aber nicht. Es soll also weder eine Modifikation des gleichen Moduls mit und ohne Winkel geben, noch soll in der endgültigen Grafischen Software die Winkelberechnung entfallen, da man für Tests und Erweiterung den Winkelwert immer mal wieder haben möchte.

Für die Rechenzeit gilt: Die Betragsbildung erfordert 2 Multiplikationen, Addition und Wurzel. Das sind beispielsweise bei einem schnellen Prozessor ca. 50 ns. Die Winkelberechnung aus den kartesischen Koordinaten braucht eine arctan2-Funktion. Dies ist eine Reihenentwicklung nach Vorentscheidung, oder je nach Prozessorhardware ein Lookup-Tabellen-Zugriff mit Interpolation, also jedenfalls aufwändiger als die in Hardware realisierte Multiplikation und die einfache Reihenentwicklung der Quadratwurzel Erfahrungwert 200 ns. Die nachfolgende Winkelauswertung mag nochmals 100 ns benötigen.

Im Zeithorizont von 20 µs Abtastzeit mit vielen weiteren Funktionen sind 0.3 µs relativ viel. Wenn diese im Release nicht benötigt werden, sollte es jedenfalls vermieden werden, diese Werte zu berechnen.

Andererseits sollte man nicht zwischen Releases und Versionen nur aus diesem Grund die Grafik ändern, Nachteil mehrfache Datenhaltung, Versionsvielfalt.

In der C/++-Programmierung kennt man das fein gradual eingesetzte Mittel der bedingten Compilierung, das aber Quellen auch nicht gut lesbar erscheinen lässt.

Simulink geht bei seinem Codegenerator so vor, dass bei im Konkretfall ungenutzten Ausgängen die davorliegende Logik vor der Codegenerierung wegoptimiert wird. Ein im Konkretfall ungenutzter Ausgang kann ohne Änderung der Grafischen Programmierung einfach durch Austausch eines nachfolgenden Library-Moduls als ungenutzt (oder im anderen Fall genutzt) bestimmt werden. Dabei braucht ebenfalls nicht die Grafik geändert werden, nur die verwendete Library wird aufgrund anderer Suchpfadeingestellungen getauscht, oder die Testumgebung wird ausgetauscht durch die Aufrufumgebung. Das ist die Wahl des Releasemodus für das Produkt versus einer Testumgebung (Test-Libraries genutzt). Grafisch ist also die bedingte Verwendung von Ausgängen bzw. Schaltungsteilen sehr einfach modifizierbar.

Als Event Chain (EvChain) tritt uns diese Schaltung nun wie folgt entgegen:


Die EvChain für init und param sollen nicht weiter interessieren, sie entsprechen anderen Abtastzeiten. Das event-input step1 (=^schnelle Abtastzeit) wird am Cplx2mg gespalten. Der FBlock selbst erzeugt innen zwei Events und hat zwei EvChain, getrennt für Amplitude (magnitude) und Winkel (angle). Eben weil die Berechnung beider Werte nicht immer notwendig ist. Die gok EvChain wird dann weitergeleitet auf die Winkelnachbehandlung, hier nur als rad2int dargestellt, und dann sowohl für das Event als auch den Datenausgang ausgegeben.

Sind nun beide, der angle_cnf Event-Ausgang und der yAngle Datenausgang nicht benutzt, dann soll der zugehörige Code auch nicht aufgerufen werden (Rechenzeitersparnis) und möglichst auch nicht als toter Maschinencode im Embedded Target auftreren. Dabei kann es auch sein, dass die Ausgänge in der Modulaußenbeschaltung zunächst verdrahtet sind, aber ganz außen dann unbenutzt. Dies muss auch funktionieren. Ganz außen entscheidet also die Wahl zwischen Testumgebung und Target-Implementierung, ob von diesem Modul die entsprechenden Operationen aufgerufen werden.

Realisiert wird dies durch eigene Operationen für die einzelnen EvChain. Die Winkelverarbeitung bereits aus dem FBlock Clpx2mg ist eine eigene EvChain, der Clpx2mg hat aus diesem Grund ebenfalls bereits zwei Operationen. Es liegt nun an der aufrufenden Umgebung, also der Nutzung des Moduls, ob diese Operationen gerufen werden.

Die Operationen werden dabei immer generiert, da das Modul als solches generiert wird und in einer C/++-Umgebung fertig zur Verfügung steht, unabhängig von dessen Nutzung. Das ist anders als bei Simulink, bei dem auch die innereren Module zunächst formell aufgelöst werden und dann die Reduktion quasi auf Grafikebene vor der Codegenerierung erfolgt. Bei der FBcl-Codegenerierung ist dagegen der generierte Modulcode unabhängig von dessen Nutzung immer gleich; und kann damit einfacher als Second source-generierter Code versioniert werden.

Die Operationen werden für C/++ entweder als inline im Headerfile definiert, oder jeweils einzeln in einem Implementierungsfile. Folglich benötigten nicht gerufene Operationen keinen Platz im Maschinencode, entweder weil der inline-Code nicht genutzt wird oder weil der Linker entscheidet, dass ein Object-File nicht eingezogen wird da es nicht benutzt wird. Der Gesamt- Optimierungsprozess nutzt also noch zusätzlich die bekannten optimierenden Eigenschaften der Compiler und Linker.


6.2 Datenhaushalt

Topic:.FBclEvChain..

Die EventChain-Daten Evchain_FBcl srcJava_FBcl/org/vishia/fbcl/fblock/Evchain_FBcl sind in 3.2.5 Event-Chain-Informationen beschrieben.


Eine Operation Operation_FBcl srcJava_FBcl/org/vishia/fbcl/fblock/Operation_FBcl ist in Chapter: 3.1.5 Operation_FBcl - Info welche Pins beeinflusst werden beschrieben. Sie enthält insbesondere Code-Generiervorschriften (Rule) für die Event-Funktionen. Die weitere wesentliche Information sind die von der Operation betroffenen Input- und Ouput-Pins (Daten und Event). Diese Informationen werden für die Codegenerierung und für die Bildung von Operations im übergeordneten Modulen benutzt

Operations und auch DoutType_FBcl srcJava_FBcl/org/vishia/fbcl/fblock/DoutType_FBcl enthalten für die Generierung der Event-Operation und für den Zugriff auf das Datenport eine Generier-Vorschrift oder -Regel (rule) für die Codegenerierung des rufenden Moduls. Die Generier-Regeln sind mit einem OutTextPreparer srcJava_Zbnf/org/vishia/util/OutTextPreparer realisiert.

Wesentlich sind weiterhin die zugehörigen Pins des Moduls für Operations und Generierregeln.

In Module_FBcl sind die Instanzen von Evchain_FBcl für die Übersetzung nach C-Code und Bildung der Operations in evChains referenziert.


6.3 Algorithmus zur Bildung von EventChains in einem Modul

Topic:.FBclEvChain.build.

Last changed: 2020-07-24

Der Algorithmus ist in srcJava_FBcl/org/vishia/fbcl/readFBcl/BuildEvchainOperation_FBcl enthalten, startend mit der Operation execute(...).

Nach Fertigstellung dieses ersten Schrittes, Aufstellen der EventChains als solche, wird für das Debugging ein File NAME.fbcldata_1 geschrieben. Dieser enthält die EventChains und Event-Operationen, aber noch keine kombinatorischen Operationen und noch keine Erkennung von Outputs, die Instanzvariable benötigen wegen Nutzung der Daten in anderen EventChains. Zur Darstellung in diesem Reportfile siehe 4.3 Ausgabe der Daten des gelesenen Datenmodells für Testzwecke.

buildOperDout(...)

Im zweiten Schritt werden in buildOperDout(...) ausgehend von allen Datenausgängen des Moduls (rückwärts im Datenfluss) die kombinatorischen Output-Operationen gebildet. Wenn diese auf dout von FBlocks treffen, die noch in anderen EventChains liegen, dann werden diese Ausgänge mit Dout_FBcl#bObjectVar bezeichnet und es werden bei der Codegenerierung Instanz- bzw. ObjectVariable erzeugt.

Wenn ein Dout aufgabelt, aber in der selben Operation verwendet wird, dann ist das eine Dout_FBcl#bLocalVar

Nach Fertigstellung dieses zweiten Schrittes, Aufstellen der EventChains als solche, wird für das Debugging ein File NAME.fbcldata_2 geschrieben. Vergleicht man beide *.fbcldata_? files, dann kann man das Ergebnis diesen Schrittes erkennen.

buildOperationsInEvchain()

Diese Operation für jede EventChain in Evchain_FBcl.Wr#buildOperationsInEvchain--|Evchain_FBcl.Wr#buildOperationsInEvchain() ordnet nun Ein- und Ausgänge der

XXX

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

In buildEvchain(...) wird mit checkDependingOperations(...) getestet, ob die Operation des Types des FBlock abhängige Operationen enthält. Das ist dann der Fall, wenn in der Funktionalität des Typs Events gabeln. Das ist nie im ersten Input-Event der Fall, denn dann gäbe es mehrere Eventchain

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.


6.4 Belegung der Portbits in den Operations

Topic:.FBclEvChain.operPortBits.

Wie in Chapter: 3.1.1 EvinType mit Operation, Pin-Zuordnungen, Codegenerier-Regel für Event-Handling und Chapter: 3.5 Codegenerier-Regeln in Operation und Output-Datenpins dargestellt enthalten die EvinType_FBcl-Instanzen im FBlock_Type_FBcl in der referenzierten Operation_FBcl Bitmasken, die die Pins der Operation kennzeichnen. Diese Information wird bei der Bildung der Event Chain erfasst und dort gespeichert. Die Generierregel für den Zielsystem-Code für das jeweilige Event wird jedoch erst mit der Codegenerierung Chapter: 7 Codegenerierung aus FBcl eingetragen.

Die Operation checkDinDout(...) wird für jedes Input-Event der Chain gerufen. Über den Typ des zugehörigen FBlock werden die im FBlock genutzten Pins ermittelt. Für die Basis-FBlocks stimmen diese, für alle gerufenen FBlocks als Submodule werden diese mit eben diesem Algorithmus zuvor ermittelt.

 for(Din_FBcl din: operEvinType.iterDin(evinCurr.fb)) {

Der iterDin(...) ist speziell gebaut und liefert die Data-Inputs von der FBlock-Instanz zurück unter Kenntnis der Operation des Typs. Es wird die Verdrahtung des Daten-Input-Pins geprüft:

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.1.5 Operation_FBcl - Info welche Pins beeinflusst werden.

Die Operations in den Typen der einzelnen FBlocks in der Event-Chain bestimmen folglich die Statements der Codegenerierung.

Die CodeGen_FBcl-Infos zu den Datenausgängen eines FBlocks im Typ bestimmen die Elemente der Expressions in den generierten Statements.


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


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


7 Codegenerierung aus FBcl

Topic:.FBclCgen.

Last changed: 2020-07-17

Dieser Dokumentationsabschnitt befasst sich mit der Codegenerierung von Ablaufcode für embedded Plattformen aus dem FBcl-Datenmodell. Die Codegenerierung ist auf C und C++ bezogen dargestellt, kann jedoch adäquate auch in Java, ST oder andere adäquate Programmiersprachen erfolgen. Die Codegenerierung wird von einem Template-file gesteuert, der alle sprachrelevanten Dinge enthält. Damit kann angepasst werden.

Inwieweit sich die Codegenerierung dann auch auf ST bezieht, um beispielsweise große grafische Blöcke in FBcl in einen IEC 61499-textuellen FBlock zu generieren (Basic oder Simple FBlock), muss noch erarbeitet werden.

Die Files in FBcl werden entweder in der IEC 61499-textuellen Notation für Composite FBlocks erwartet, oder in der entsprechenden XML-Notation beispielsweise aus einer 4diac-Grafik. Beide Inputs sind gleichwertig. Diese Files könnten beispielsweise zuvor aus Simulink erzeugt worden sein oder mit einem IEC 61499-Tool wie 4diac IDE bearbeitet und gespeichert worden sein.

Die Codegenerierung ist nicht notwendig wenn eine andere Funktionsblockdarstellung beispielsweise aus Simulink gelesen und in die FBcl-Datendarstellung konvertiert werden soll. Für die notwendige Erstellung der Eventverbindungen ist die Bildung von Event-Chains und Operation ausreichend, siehe Chapter: 6 Bildung von Operations aus den aufeinanderfolgenden Eventverbindungen.


7.1 Template-File für die Codegenerierung

Topic:.FBclCgen.TplCgen.

Das Templatefile wird mit der class srcJava_FBcl/org/vishia/fbcl/translate/ReadTranslationScripts eingelesen. Es hat folgenden Aufbau (Beispiel C-Codegenerierung, File org/vishia/fbcl/translate/cHeader.txt):

Einzelne Abschnitte sind ab Zeilenanfang mit ====ident==== gekennzeichnet, gehen bis ==== und werden mit diesem ident aufgesucht. Zwischen diesen Schlüsselzeilen stehen die Regeln für die Codegenerierung. Diese liegen in einer Notation vor, wie es für srcJava_vishiaBase/org/vishia/util/OutTextPreparer notwendig ist. Diese Klasse wird dann auch zur Generierung verwendet.

====include====
<:args:depending><: >
#include <&depending>.h
====

Block, der Abhängigkeiten der Quelle enthalten soll, für Java sind das die import statements. Für C werden pro Modul Headerfiles includiert, die entweder selbst zuvor generiert wurden, oder vorhanden sind wenn FBlocks in vorhandenem C/++ bzw. als Quellen einer anderen Programmiersprache gerufen werden.

====class====
<:args: name, varList>
typedef struct <&name>_T {<: >
  <:for:var:varList>
  <&var.sType> <&var.name>;<.for>
} <&name>;

Block einer Typdefinition

====operation====
<:args:docu, typeName,operName,retType,isStatic, args, statements, retValue>
/**<&docu> */
inline <&retType> <&operName>_<&typeName> (<:if:isStatic==0> <&typeName>* thiz<.if><&args> ) {
  <:if:statements><&statements><.if><:if:retValue>
  return <&retValue>;<.if>
}

Dieses Template beschreibt wie eine Operation, in C eine Funktion zu bauen ist. Da dieses Template für den headerfile gilt, wird also im Headerfile eine inline-Definition angegeben

====setOutput====
<:args:out, expr><: >
  <:  >thiz-><&out> = <&expr>;
====

Dieses Template wird verwendet, wenn im FBcl-Datenmodell ein Output des Moduls gesetzt oder verdrahtet ist und ein Wert aus 'Expression' in der Codegenerierung dort zugewiesen wird. Damit wird ein Statement erzeugt.

Es ist eine Zeile mit 2 spaces als ident. Der Output wird über das thiz angesprochen, wie es in ==operation== im Code erzeugt wurde.

====getOutput====
<:args:outVar>thiz-><&outVar>====

ist notwendig wenn eine Variable aus der eigenen Datenstruktur zurückgelesen wird, also etwas was auch als output ausgegeben wird, weiter verarbeitet wird. Es wird kein Zeilenumbruch erzeugt. Daher steht das abschließende ==== hier in der selben Zeile.

====get_thisFBobj====
<:args:FBobj>thiz-><&FBobj>====

Dieses Template dient dazu auf einen FBlock zuzugreifen der im innereren verwendet wird. Er ist als Instanz (Object) in der struct defininiert, ide mit ==class== generiert wurde.

====codeRuleEvinCall====
<:args:nameOp, type, THIS_OBJ, args, sep><: >  ##<:<&> produces <&, <&arg> produces the value of the arg
<:  ><&nameOp>_<&type> ( <:<&THIS_OBJ>><:set:sep=', '><: >
<:for:arg:args><&sep><:<&><&arg>><:set:sep=', '><.for> );
====

Dieses Template (oder Regel, Rule) beschreibt wie ein Aufruf aufgrund einer Eventverbindung aussehen soll. Die Eventverbindung ruft hier eine Operation des FBlocks auf. Das THIS_OBJ wurde zuvor nach der oberen Regel gebildet, die aber nicht nur für diesen Aufruf verwendet wird.

====statement====
!assign= = ====
!ne = != ====
!eq = == ====
====

Dieses Template enthält nur die Definition von Werten zu Variablen, die für SStatements verwendet werden. Beispiel assign: Der Zuweisungsoperator ist in Structure Text mit := geschrieben, wäre hier anzugeben. In C ist es das =.


7.2 Ablauf für Übersetzung in Zielcode

Topic:.FBclCgen.trl.

date=2020-07-24

Die folgende Routine der class srcJava_FBcl/org/vishia/fbcl/translate/Translation2Target_FBtrl ist eingebettet in ein static public translate, die von außen gerufen wird:

 private void translate(Module_FBcl mdl, File genSrcDir, int recursion)
 throws IOException {
   if(recursion  >100) throw new IllegalArgumentException("more than 100...");
   this.varMap = new TreeMap<String, VariableDef_FBtrl>();
   //
   for(Map.Entry<String, FBlock_FBcl> efb: mdl.getFBlocks().entrySet()) {
     FBlock_FBcl fb = efb.getValue();
     FBlock_Type_FBcl fbt = fb.getTypeFB();
     Module_FBcl submdl = fbt.getModule();
     if(submdl !=null) {
       //TODO check data types to translate
       String fbName = StringFunctions_B.toFirstLowercase(fb.name());
       String fbtName = StringFunctions_B.toFirstUppercase(fbt.name());
       this.varMap.put(fbName, new VariableDef_FBtrl(fbName, fbtName));
       //
       //====> for sub modules
       Translation2Target_FBtrl.translate(submdl, genSrcDir, this.trlScripts
         , recursion+1);
     }
   }

Es werden zunächst alle im Modul enthaltenen FBlocks als Submodule komplett übersetzt. Damit wird diese Routine über das static public translate rekursiv gerufen.


7.3 Codegenerierung mit Abarbeitung der EventChain

Topic:.FBclCgen.evChain.

Vom eigenem Modul werden dann die im Datenmodell gebildeten Eventchain abgearbeitet. Das sind die getrennten Abläufe im zu generierendem Code, die letztlich entweder zu eigenen Operationen führen oder bei Fork und Join (E_REND) kombiniert werden.

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

Für die Codegenerierung werden alle im Modul vorhandenen Event-Chains nacheinander abgearbeitet. Jede Event-Chain ist im Interface mit einer Operation_FBcl vertreten (über das EvinType_FBcl referenziert). srcJava_FBcl/org/vishia/fbcl/fblock/Operation_FBcl, srcJava_FBcl/org/vishia/fbcl/fblock/EvinType_FBcl. Es werden im generierten Code die Anweisungen der Operation gebildet.

Die Reihenfolge der Abarbeitung der Event-Chain spielt keine Rolle. Sie bestimmt aber die Reihenfolge der Operationen im Quelltext, was aus Vergleichsgründen verschiedener Stände der generierten Sources wichtig ist.

Pro Eventchain wird die Event-Operation generiert mit Aufuf von

  for(Evchain_FBcl evchain: mdl.evChains) {
    //
    //====> Generates the statements and the output expression:
    boolean bHasStatements = genStatementsForEvChain(mdl, evchain);

Es kann sein dass damit keine Statments erzeugt worden sind weil in der Eventchain nur kombinatorische FBlocks ohne Zwischenausgänge liegen. Dann wird keine Event-Operation generiert.

    if(bHasStatements) {
      //
      //====>Generates source code:
      genSrcOperationForEvChain(mdl, evchain, evchain.operation.usedDinEvOper());
      //====> Generate the generation rule for the event input operation.
      evchain.operation.genCodeStmnt = generateCodeRuleForEvent(mdl
          , evchain.operation.name, evchain.operation.usedDinEvOper());
    }
  }//for ...evchain

Danach werden für alle Output-Pins des Moduls sowohl die Zugriffsfunktionen im Code generiert als auch die GenerierRegeln für den Zugriff auf das Modul:

  for(EvinType_FBcl evinIfc: mdl.getIfcFB().evinType()) {
    //====>Generates source code for the routines to the event input.
    genSrcOperationForEvin(mdl, evinIfc);
  }
  for(Portdout_FBcl portOut: mdl.getdoutPortFromInnerMdl()) {
    //====>Generates source code for the access routines to the module output.
    genSrcOperationForOutpin(mdl, portOut);
  }

Als letztes werden die Gesamtfiles generiert (bei C Header und Implementierung). Dabei werden zuvor alle aufgesammelten Instanzvariable in eine struct oder class generiert.

  generateFile(genSrcDir, mdl);  //Output the secSourceCode file
  CheckModule_FBrd.check(mdl, true);
  this.varMap.clear();  //after generate file, clear for superior translate.
}

7.4 Codegenerierung für eine Event-Operation

Topic:.FBclCgen.evOper.

date=2020-07-24

class srcJava_FBcl/org/vishia/fbcl/translate/Translation2Target_FBtrl

In genStatementsForEvChain(...) wird für jedes Input-Event der Mitglieder der Event-Chain über den FBlock-Typ die zugehörige Operation operEvin aufgesucht. Das erste Input-Event kann mehrere Operation haben, der Index der richtigen Operation steht in den Daten der Event-Chain: Evchain_FBcl.ixFirstOper. Die Operation des Event-Typs kann eine Codegenerier-Regel enthalten. Diese ist bei den Basis-FBlocks fest vorgegeben und bei gerufenen Submodulen nach eben diesem Algorithmus zuvor gebildet worden. Wenn die Operation des Event-Typs keine Codegenerier-Regel enthält, dann handelt es sich um einen kombinatorischen FBlock. Ist eine Regel vorhanden, dann wird damit ein Statement erzeugt: genCodeStmntOrAccess(...), siehe Chapter: 7.5 Generierung des Access code für ein Event Input oder Data Output. Das ist dann ein Aufruf der Event-Operation.

Danach werden in processDoutToOperEvin(...) für den jeweiligen Event-Input der Event-Chain die Outputs aufgesucht. Diese sind in der operEvin als Bitmaske in mDrivenDout enthalten. Es werden die Daten-Outputs behandelt, die tatsächlich von der Event-Operation des FBlocks beeinflusst werden, nicht die dem Output-Event im Interface zugeordneten Daten-Outputs. Diejenigen Output-Pin, die eine Variable belegen, erzeugen ebenfalls ein Statement in der Event-Operation und müssen daher in dieser Reihenfolge generiert werden.

Diese Abarbeitungen erzeugen mit oben genannten Bedingungen die Statements für die Event-Operation. Nachdem alle Event-Inputs der Event-Chain abgearbeitet wurden, wird geprüft, ob überhaupt Statements erzeugt worden sind, d.h. die gerufenen FBlocks entweder Event-Operations am entsprechenden Event-Input haben oder die Daten-Outputs in Variablen gespeichert worden sind: interne Variable bHasStatements Das läuft konform mit der Kennzeichnung der Input-Ports in der Operation. Diese ist genau dann 0 (keine Input-Ports), wenn hier keine Statements generiert wurden. Das wird über assert(...) geprüft. Nur wenn Statements erzeugt wurden, wird die Event-Operation im Target-Source generiert. Auch nur in diesem Fall wird als letztes in diesem Ablauf die Generier-Regel für diesen Event-Input bzw. der zugehörigen Operation des Moduls generiert. Das erfolgt mit


evchain.operation.genCodeStmnt = generateCodeRuleForEvent(...)

Siehe Chapter: 7.6 Generierung der Generierregel für Event-Operation.

Folglich hat ein Event-Input in einem hier übersetzten User-Modul nicht in jedem Fall eine Generier-Regel für die Event-Operation gespeichert in Operation_FBcl#genCodeStmnt, nicht für rein kombinatorische Logik. Genau dann ist aber auch Operation_FBcl#mNecessaryDin ==0.


7.5 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: 6.4 Belegung der Portbits in den Operations diese für den Event-Input bereits ermittelt worden sind, können sie hier als Aufrufargument genutzt werden. Für den Zugriff auf Outputs wird die Bitmask in DoutType_FBcl.mUsedInputs benutzt, die ebenfalls bereits ermittelt worden ist.

Im Aufruf von getValueDin(...) wird entschieden:

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.


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

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


8 Test der Codegenerierung mit 4diac-Modulen

Topic:.FBclTest4diac.

Last changed: 2019-11-18

Dieser Dokumentationsabschnitt dokumentiert Testergebnisse und zeigt damit Features der Codegenerierung


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


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


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


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


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


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


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


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


10 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


10.1 Referenzierung und Objektorientierung

Topic:.FBclOORefs.OOref.

Wogegen die in Chapter: 9.2 Eventkopplung und Objektorientierung beschriebene Eventkopplung eher strittig zur Objektorientierung gehören möge, ist die Referenzierung von Daten in der Objektorientierung etwa mit dem UML-Klassendiagramm, die gängige Praxis. Nicht Einzeldaten sind der Referenzierung zugänglich, sondern Instanzen, die mit Methoden oder Operations auf deren Dateninhalt zugreifen. Der public-Zugriff auf Einzeldaten oder über deren direkte Getter- und Setter-Zugriffsroutine ist allerdings durchaus auch gängige Praxis.

Diese Möglichkeiten scheinen im System der Funktionsblockprogrammierung zu fehlen. Die IEC-61499 fügt die Eventkopplung hinzu, ansonsten ist ein Funktionsblockprogrammierungs-System vom Datenfluss geprägt:

Datenfluss bedeutet, die einzelnen Eingangsdaten werden direkt bereitgestellt, die Output-Daten sind im betreffenden Funktionsplan entweder direkt abholbar oder werden über den Event-Mechanismus der IEC-61499 selbst übertragen. Das Datenfluss-Prinzip ist bestimmend für alle Funktionsblock-Programmiersysteme.

Es gibt aber einen einfachen Weg, die Möglichkeit auf innere Daten von Funktionsblöcken zuzugreifen, ohne diese außen zu übertragen. Rein technisch ist Grundvoraussetzung, dass ich die Daten in einem gemeinsamen Speicher befinden. Es wird dann per Datenfluss die Referenz oder Datenadresse (einmalig) übertragen, dann kann sich der empfangende FBlock beliebig den Daten bedienen, wie man es landläufig aus den Objektorientierten Sprachen über Zeiger oder Referenzen kennt.

Um dies auch grafisch zu unterstützen, braucht es Datensammler-FBlocks, die intern direkt in der Zielsprache des Targets programmiert sind, deren Datenzugriffsmechanismen per Zeiger nutzen. Die Gegenstelle sind die Datenzugriffs-FBlocks.

Ein solches System wurde für Simulink als FBlock-System realisiert, siehe www.vishia.org/smlk/html/Smlk_FB_ObjOConn.html

Die gleiche Gestaltung im Rahmen der FBcl ist das folgende Projekt der Zukunft.