Ein Funktionsplan ist Logik aus Bausteinen, so wie eine SPS in CFC programmiert wird: Timer, Flipflops, Vergleiche und Arithmetik, verbunden mit ioBroker-Datenpunkten. Er ist für Logik gedacht, die dauerhaft läuft - Verzögerungen, Selbsthaltungen, Schwellwerte - und für Nutzer, die diese Art aus der SPS-Welt kennen. Wer auf ein einzelnes Ereignis reagieren will, ist mit Blockly oder Rules meist einfacher unterwegs.
Beim Speichern wird der Plan in JavaScript übersetzt und läuft wie jedes andere Skript dieses Adapters.
Ein neues Skript hinzufügen und Funktionsplan wählen. Der Editor hat drei Bereiche:
- Palette (links), mit einer Suche und drei Reitern:
- Blöcke: die Bausteine nach Kategorien - auf die Fläche ziehen oder anklicken. Eine Kategorie klappt mit einem Klick auf ihren Namen auf und zu.
- Variablen: die Datenpunkte, die der Plan liest und schreibt; ein Klick zeigt den Baustein.
- Favoriten: die Bausteine, die mit dem Stern markiert sind - er erscheint, wenn die Maus über einem Baustein steht.
- Fläche: einen Ausgang (rechts am Baustein) mit einem Eingang (links) verbinden. Nur passende Typen lassen
sich verbinden - die Farbe eines Anschlusses zeigt seinen Typ; wird eine Verbindung auf einem unpassenden
Anschluss losgelassen, steht oben, warum. Ein Eingang nimmt genau eine Verbindung.
Entflöscht das Ausgewählte. - Eigenschaften (rechts): Eigenschaften zeigt die Einstellungen des gewählten Bausteins in Abschnitten, die auf- und zuklappen - Allgemein (Instanzname, Typ, ein Kommentar), Parameter, Eingänge und bei Timern, Flipflops, Flanken und Regelungsbausteinen eine Vorschau: ein Zeitdiagramm des Bausteins mit seinen Werten. Dokumentation beschreibt den Baustein und seine Anschlüsse. Ist nichts gewählt: die Einstellungen des Plans und die Tastenkürzel.
Ein Baustein zeigt das Wichtige direkt in sich: den Datenpunkt, den er liest oder schreibt, den Wert eines CONST
und die Werte der Eingänge, die nicht verbunden sind - sie lassen sich gleich dort ändern: Ein Klick schaltet ein
BOOL um, alles andere wird eingetippt und mit Enter übernommen. Im Kopf eines Bausteins zeigt das Zahnrad seine
Eigenschaften, das Menü ⋮ dupliziert oder löscht ihn (online auch: Haltepunkt; bei einem eigenen Baustein:
Innenansicht).
Probleme stehen unten auf der Fläche; ein Klick wählt den Baustein aus. Ein Plan mit Fehlern lässt sich speichern, sein Skript läuft aber nicht, sondern meldet die Fehler im Protokoll.
Der Knopf FBD → JS zeigt den erzeugten Code.
Bearbeiten geht wie gewohnt: Strg+Z / Strg+Y (oder die Knöpfe der Werkzeugleiste) machen rückgängig und
wiederholen, Strg+C, Strg+X und Strg+V kopieren, schneiden aus und fügen die ausgewählten Bausteine samt der
Verbindungen zwischen ihnen ein - auch in einen anderen Plan. Eingefügte Bausteine bekommen neue IDs und, wo der
alte Name vergeben ist, neue Namen.
Die Leiste über der Fläche zeigt den Plan (und den Pfad in eine Instanz hinein) und hat die Werkzeuge:
- den Zoom: kleiner, der Zoom (ein Klick: 100 %), größer
- Automatisch anordnen stellt die Bausteine in Spalten entlang des Datenflusses auf: Eingänge links, Ausgänge
rechts, jeder Baustein auf der Höhe der Bausteine, mit denen er verbunden ist. Kommentare bleiben, wo sie sind.
Strg+Znimmt es zurück. - Im Plan suchen (
Strg+F) findet Bausteine nach Instanzname, Typ, Datenpunkt oder anderem Parameter, Kommentare nach ihrem Text.Entergeht zum nächsten Treffer,Umschalt+Enterzum vorigen,Escschließt die Suche. - Raster an oder aus (mit Raster rasten die Bausteine ein), und Verbindungen als Kurven oder rechtwinklig wie in CFC - beides merkt sich der Browser, nicht der Plan
- rechts die Onlineansicht, siehe unten
Verbindungsmarken: Eine Verbindung quer durch den Plan lässt sich statt als Linie als Marke an beiden Enden zeigen - Verbindung auswählen und in ihren Eigenschaften Als Verbindungsmarke zeigen einschalten. Beide Marken zeigen den Namen des Bausteins, von dem die Verbindung kommt, oder einen eigenen. Ein Doppelklick auf eine Marke springt zum anderen Ende. Nur die Darstellung ändert sich, die Verbindung wirkt wie zuvor.
Online (rechts in der Leiste über der Fläche) zeigt die Werte des laufenden Plans: an jedem Ausgang,
BOOL-Leitungen grün (TRUE) oder grau (FALSE), andere Leitungen mit ihrem Wert in der Mitte. Neben oder unter dem
Knopf stehen:
- der Zykluszähler - er bleibt stehen, wenn der Plan nicht mehr rechnet
- eine Warnung, wenn keine Daten kommen (das Skript läuft nicht, oder seine Instanz ist gestoppt)
- ein Hinweis, wenn der Plan nach dem Speichern geändert wurde: Die Werte gehören zur gespeicherten Fassung
Schlägt ein Zyklus fehl, wird der Baustein, dessen Code fehlschlug, rot umrandet und der Fehler unten links
aufgeführt; auch das Protokoll nennt den Baustein (Error in the function block diagram in block b7: ...).
Die Werte fließen nur, solange ein Editor den Plan online zeigt: Der Editor fragt alle 10 s beim Adapter an, nach 30 s ohne Anfrage hört der Plan auf zu senden. Es kommen höchstens vier Aktualisierungen pro Sekunde, jede nur mit den geänderten Werten. Ein Plan, der vor dieser Version gespeichert wurde, zeigt den Zykluszähler, aber keine Werte - einmal speichern.
Ein Doppelklick auf eine Instanz eines eigenen Bausteins (oder Innenansicht in ihren Eigenschaften) zeigt den
Plan des Bausteins - so, wie dieser Plan ihn mitführt, nur lesbar - und online mit den Werten genau dieser Instanz.
Eine Instanz darin öffnet sich genauso; der Pfad oben links führt zurück, ebenso Esc. Werte im Inneren lassen sich
erzwingen; Haltepunkte werden im Plan selbst gesetzt. Die Werte im Inneren brauchen einen Plan, der mit dieser
Version gespeichert wurde.
Solange der Plan online gezeigt wird, lässt er sich steuern:
- Erzwingen: einen Baustein (oder eine Verbindung) auswählen - die Eigenschaften zeigen seine Ausgänge mit ihren Werten. TRUE / FALSE oder ein Wert und Erzwingen halten den Ausgang auf diesem Wert, gleich was der Baustein rechnet; die Bausteine dahinter und die Datenpunkte, die der Plan schreibt, bekommen den erzwungenen Wert. Ein erzwungener Wert ist orange umrandet; Freigeben lässt ihn los.
- Haltepunkte: der Punkt oben links an einem Baustein,
F9oder Haltepunkt in seinen Eigenschaften. Der Plan hält vor diesem Baustein an; der Baustein, der als nächster läuft, ist gelb umrandet, und die Werte zeigen, wie weit der Zyklus kam. - Die Knöpfe links von Online: Anhalten / Fortsetzen (
F8), Ein Zyklus und Ein Baustein (F10). Solange der Plan wartet, warten auch die Änderungen seiner Eingänge. Ein Zyklus von Hand rechnet mit einer Zykluszeit als vergangener Zeit - Timer verhalten sich so, wie sie es täten, egal wie lange der Plan wartete.
Das alles endet mit der Onlineansicht: Schaltet der Editor sie aus, wird er geschlossen oder verschwindet er (30 s ohne Anfrage), gibt die Laufzeit die erzwungenen Werte frei, entfernt die Haltepunkte und läuft weiter - und protokolliert das. Beim Speichern des Plans bleiben erzwungene Werte und Haltepunkte erhalten, solange die Onlineansicht offen ist. Haltepunkte und Einzelschritte brauchen einen Plan, der mit dieser Version gespeichert wurde; ein älterer lässt sich schon erzwingen und anhalten.
Vorsicht: Ein erzwungener Wert wirkt auf echte Geräte - der Plan schreibt seine Datenpunkte damit.
| Kategorie | Bausteine |
|---|---|
| ioBroker | STATE_IN, STATE_OUT, CONST |
| Logik | AND, OR, XOR, NOT |
| Speicher | RS, SR, R_TRIG, F_TRIG |
| Zeit | TON, TOF, TP, BLINK |
| Zähler | CTU, CTD, CTUD |
| Vergleich | GT, GE, LT, LE, EQ, NE |
| Arithmetik | ADD, SUB, MUL, DIV, MIN, MAX, ROUND |
| Umwandlung | TO_BOOL, TO_INT, TO_REAL, TO_TIME, TO_STRING, CONCAT |
| Regelung | HYST, RAMP, PT1, PID |
| Kalender | CLOCK, TIMEWINDOW, SCHEDULE, ASTRO |
| Meldungen | LOG, NOTIFY, SENDTO |
| Experte | JS |
Namen und Verhalten folgen IEC 61131-3. AND, OR, XOR, ADD, MUL, MIN, MAX und CONCAT haben 2 bis 16
Eingänge. Zeiten werden wie 500ms, 2s, 1m30s oder T#2s eingegeben, Uhrzeiten wie 08:30.
Die Regelungsbausteine:
HYST- Zweipunktschalter:Qgeht an, wennINüberHIGHsteigt, und aus, wennINunterLOWfällt; dazwischen bleibtQ, wie es ist. Eine Heizung, die unterLOWeinschaltet, nimmtQinvertiert (NOToder ein invertierter Eingang).RAMP-OUTfolgtIN, ändert sich aber um höchstensUPpro Sekunde aufwärts undDOWNpro Sekunde abwärts;0begrenzt nicht. Sie beginnt beiIN.PT1- Tiefpass erster Ordnung:OUTfolgtINmit der ZeitkonstanteT(nachTsind 63 % eines Sprungs erreicht). Glättet einen unruhigen Wert; er beginnt beiIN.PID- PID-Regler: SollwertSP, IstwertPV, StellgrößeYzwischenYMINundYMAX.KPist die Verstärkung,TNdie Nachstellzeit des I-Anteils,TVdie Vorhaltzeit;TNoderTVgleich0lässt den Anteil weg. StehtYan einer Grenze, wächst der I-Anteil nicht weiter (Anti-Windup); der D-Anteil wird ausPVgebildet, ein neuer Sollwert gibt also keinen Stoß.RSTlöscht den I-Anteil.
RAMP, PT1 und PID hängen von der Zeit ab: Ein Plan mit ihnen läuft zyklisch.
Blinker und Zähler:
BLINK- solangeENwahr ist, istQfürTHwahr und fürTLfalsch, beginnend mit wahr.CTUzählt die steigenden Flanken anCU;RsetztCVauf 0 zurück,Qist wahr, sobaldCVPVerreicht hat.CTDzählt beiCDherunter,LDlädtPV,Qist bei 0 und darunter wahr.CTUDkann beides;Rgeht vorLD, und Flanken anCUundCDim selben Zyklus heben sich auf. Ein Eingang, der beim Start des Skripts schon wahr ist, ist keine Flanke. Der Zählerstand bleibt über einen Neustart des Skripts nicht erhalten.
Umwandlung: TO_BOOL, TO_INT, TO_REAL, TO_TIME und TO_STRING nehmen jeden Typ. TO_INT rundet, ein
Dezimalkomma gilt als Punkt, und was keine Zahl ist, ergibt 0; TO_TIME liest eine Zahl als ms und einen Text wie
2s oder 08:30. CONCAT fügt seine Eingänge zu einem Text zusammen - Zahlen so, wie sie sind, also vorher mit
ROUND runden (DIGITS Nachkommastellen, 0 bis 10).
Der Kalender:
CLOCK- die Ortszeit:TOD(Uhrzeit),HOUR,MIN,WDAY(1 Montag ... 7 Sonntag),DAY,MONTH,YEAR.TIMEWINDOW-Qist vonSTARTbisENDan den gewählten Tagen wahr (jeden Tag, Montag bis Freitag, Samstag und Sonntag). Ein Fenster über Mitternacht wie22:00bis06:00gehört zu dem Tag, an dem es beginnt: Bei Montag bis Freitag reicht es von Freitagnacht bis Samstagmorgen, aber nicht von Sonntagnacht in den Montag.SCHEDULE-Qist einen Zyklus lang wahr, wenn die Zeit des Cron-Musters kommt, etwa0 8 * * 1-5für 8:00 von Montag bis Freitag, oder mit Sekunden vorne*/5 * * * * *für alle 5 Sekunden. Der Knopf neben dem Feld öffnet den Dialog für das Muster.ASTRO-Qist von einem Sonnenereignis bis zu einem anderen wahr, etwa vonsunsetbissunrise, jeweils mit einem Versatz in Minuten.STARTundENDsind die Zeiten der beiden Ereignisse heute. Die Position ist die aus den Einstellungen des Adapters; ohne sie gibt es keine Zeiten, undQbleibt falsch.
CLOCK, TIMEWINDOW und ASTRO lassen einen Plan zyklisch laufen. SCHEDULE nicht: Wenn seine Zeit kommt, läuft
der Plan sofort, und gleich danach noch einmal, um den Impuls zu beenden.
STATE_INliest einen Datenpunkt und wandelt ihn in den gewählten Typ (BOOL,INT,REAL,STRING). Ein Wert, der sich nicht wandeln lässt, wird einmal im Protokoll gemeldet und als 0 gelesen. Ändert sich die ID des Datenpunkts - im Dialog gewählt oder eingetippt -, wird der Typ aus seinem Objekt übernommen, und ein Baustein, der noch seinen ersten Namen hat, bekommt den Namen des Datenpunkts. Den Typ kann man danach anders setzen; neu übernommen wird er erst mit der nächsten ID.STATE_OUTschreibt einen Datenpunkt nur, wenn sich der Wert ändert.Mindestzeit zwischen zwei Schreibvorgängenbegrenzt, wie oft er schreibt; der letzte Wert wird geschrieben, wenn die Zeit um ist.CONSTist ein Wert des gewählten Typs. Mit einem Eingang anderen Typs verbunden, übernimmt er dessen Typ und behält seinen Wert, soweit es geht ("0"wird für eineTIMEzu 0 ms) - außer er speist schon andere Eingänge, die den neuen Typ nicht nehmen.
Die Meldungen:
LOGschreibt eine Zeile ins ioBroker-Protokoll, mit der gewählten Stufe (info,warn,error,debug): bei steigender Flanke anTRIGoder wann immer sichINändert.%sim Text ist der Wert vonIN. Höchstens 20 Zeilen pro Minute - ein Plan, der alle 200 ms läuft, würde das Protokoll sonst fluten; die 20. Zeile sagt das.NOTIFYsendet den Text als ioBroker-Benachrichtigung - als Meldung oder als Alarm -,SENDTOan einen Adapter wietelegram.0,pushover.0oderemail.0, als{ text, message, title, subject }mit dem gewählten Befehl (send). Beide senden, wennLOGschreiben würde, höchstens 5-mal pro Minute; das Protokoll sagt, wenn mehr ausgelassen wurden.
Der Expertenbaustein JS führt eigenen Code aus. Der Code ist der Rumpf einer Funktion, die die Eingänge IN1 ...
INn (0 bis 8, beliebiger Typ) bekommt, dazu dt (ms seit dem letzten Zyklus), firstScan und state - ein Objekt,
das von einem Zyklus zum nächsten erhalten bleibt. Er gibt den Wert von OUT1 zurück oder ein Feld
[OUT1, OUT2, ...] für mehr Ausgänge (1 bis 8); wo er undefined liefert, behält ein Ausgang seinen Wert. Die
Ausgänge dürfen an Eingänge jedes Typs, der Code muss also liefern, was diese erwarten.
// zählt, wie oft IN1 über IN2 gestiegen ist
if (IN1 > IN2 && !state.above) {
state.count = (state.count || 0) + 1;
}
state.above = IN1 > IN2;
return state.count || 0;Der Code läuft im Skript des Plans, er kann also Funktionen der Skripte wie log() aufrufen - er sollte aber auf
nichts warten: Der nächste Baustein braucht seine Ausgänge sofort. Ein Syntaxfehler wird schon vor dem Speichern
angezeigt, ein Fehler beim Ausführen am Baustein.
Signaltypen: BOOL, INT, REAL, TIME, STRING. Ein INT darf an einen REAL-Eingang; andere Umwandlungen
geschehen nicht von selbst.
Ein Plan kann selbst ein Baustein sein, den andere Pläne beliebig oft einsetzen - wie ein Funktionsbaustein in der SPS. Dazu in den Einstellungen des Plans Dieser Plan ist ein Baustein einschalten und dem Baustein einen Namen und eine Beschreibung geben.
- Die Eingänge des Bausteins sind
FB_IN-Bausteine, seine AusgängeFB_OUT-Bausteine (Kategorie Schnittstelle, nur im Plan eines Bausteins angeboten). Der Anschlussname ist der Name des Ein- oder Ausgangs am Baustein;FB_INhat außerdem einen Typ und den Wert, den der Eingang hat, wenn er nicht verbunden ist. - Ein Baustein greift nicht auf Datenpunkte zu:
STATE_INundSTATE_OUTsind in ihm nicht erlaubt,CONSTschon. Er darf andere eigene Bausteine verwenden, aber nicht sich selbst. - Jede Instanz hat ihren eigenen Zustand - zwei Instanzen eines Bausteins mit einem Timer laufen unabhängig.
- Das Skript eines Bausteins tut nichts, wenn es läuft; der Baustein arbeitet nur in den Plänen, die ihn einsetzen.
Die Bausteine aller Pläne stehen in der Palette unter Eigene Bausteine. Der Plan, der einen Baustein einsetzt, speichert eine Kopie davon und damit die Version: Jede gespeicherte Änderung eines Bausteins ist eine neue Version. Pläne mit einer älteren arbeiten mit ihr weiter und zeigen einen Hinweis - Aktualisieren in den Eigenschaften einer Instanz übernimmt die neue Version in den Plan, Baustein öffnen öffnet den Plan des Bausteins. Verbindungen zu Ein- oder Ausgängen, die es nicht mehr gibt, entfernt das Aktualisieren.
Weil die Kopie im Plan steht, läuft ein Plan weiter, wenn der Baustein gelöscht wird, und lässt sich allein exportieren und importieren. Beim Kopieren von Instanzen in einen anderen Plan kommt der Baustein mit.
Ein Fehler in einem Baustein wird an der Instanz gemeldet, die ihn ausgeführt hat, im Editor und im Protokoll.
Die Bausteine laufen in der Reihenfolge des Datenflusses; die Zahl in der Ecke jedes Bausteins ist seine Position. Was gleichzeitig laufen könnte, läuft von links nach rechts, dann von oben nach unten.
- Ereignisgesteuert: Hängt kein Baustein von der Zeit ab, läuft der Plan, sobald sich ein Eingang ändert. Änderungen innerhalb von 20 ms laufen gemeinsam in einem Zyklus.
- Zyklisch: Sobald der Plan einen Timer enthält, läuft er alle 200 ms (50 ms bis 10 s, siehe Einstellungen des Plans) und zusätzlich direkt nach der Änderung eines Eingangs.
Die Betriebsart wird automatisch gewählt oder in den Einstellungen des Plans festgelegt.
Eine Rückkopplung ist nur über einen speichernden Baustein erlaubt (Timer, Zähler, Flipflop, Flanke, Regelungsbaustein,
JS, eigener Baustein): Die zurückführende
Verbindung liest, was dieser Baustein im vorigen Zyklus ausgegeben hat, und wird als Doppelstrich gezeichnet. Eine
Rückkopplung ohne einen solchen Baustein ist ein Fehler.
Ein Timer zählt ab dem Zyklus, in dem er die Änderung seines Eingangs sieht. Er läuft also nie vor seiner Zeit ab, höchstens einen Zyklus später.
- Nicht für sicherheitsgerichtete Funktionen. Ein Plan läuft in einem ioBroker-Skript, ohne Echtzeitgarantien.
- Der Zustand der Bausteine bleibt über einen Neustart des Skripts nicht erhalten.
- Noch nicht vorhanden: Haltepunkte in einem eigenen Baustein, eine manuelle Ausführungsreihenfolge, Verbindungen, die um Bausteine herum geführt werden.
Der Plan steht im Skript selbst, als letzte Zeile von common.source (//#fbd:{...}); darüber steht der erzeugte
Code. Der Plan enthält die Kopien der eigenen Bausteine, die er einsetzt. Die Bausteine der Bibliothek sind im Modul @iobroker/fb-runtime umgesetzt, das mit dem Adapter kommt, und werden nicht in
jedes Skript kopiert.