# Ausdrücke

> Die eine Sprache der Ausdrücke hinter Variablen und Alarmbedingungen, worauf sie verweisen darf, die Lesefläche jeder Art von Punkt, die Regeln, die der Auswerter erzwingt, die Meldungen der Prüfung und wie der Editor für Skripte und seine Auswahl Durchsuchen… arbeiten.

Eine Sprache der Ausdrücke bedient zwei Stellen auf der Seite Logic: das, was eine [Variable der Art Ausdruck](logic-variables) berechnet, und die Bedingung, die ein [Alarm](logic-alarms) beobachtet. Beide sind ein einzelner JavaScript-Ausdruck, im strengen Modus in einem Sandkasten ohne Zugriff auf den Rechner ausgewertet, und beide werden im selben Editor für Skripte bearbeitet. Skripte von Aktionen verwenden denselben Wortschatz, sind aber Anweisungsblöcke; ihre Unterschiede stehen auf [Aktionen](logic-actions).

## Was ein Ausdruck ist {#what-an-expression-is}

Ein Ausdruck ist genau ein JavaScript-Ausdruck: Literale, Arithmetik, Vergleiche, logische Operatoren, die Bedingung `a ? b : c`, Operationen auf Zeichenketten, Vorlagenzeichenketten und Aufrufe der eingebauten JavaScript-Objekte `Math`, `Date` und `JSON`. Anweisungen (`let`, `if`, Schleifen) gehören zu Aktionen: Eine zweite Anweisung wird abgelehnt mit "Eine Variable hält einen einzelnen Ausdruck. Anweisungen gehören in Aktionen." Zuweisungen und `++`/`--` werden überall in einem Ausdruck abgelehnt mit "Ausdrücke sind nur lesbar. Schreibvorgänge gehören in Aktionen."

Der Ausdruck verweist über ihre verwurzelte [Adresse](logic-address-space) auf Punkte. Was jede Wurzel dem Ausdruck reicht:

| Wurzel | Was in den Ausdruck übergeht |
| --- | --- |
| Tags unter `Connector`, `Variables`, `Alarms`, die `Channels` einer Einheit von Process | Ein lebendes Objekt mit der typisierten Lesefläche unten. Ein nackter Verweis (`Connector.Simulated.Pump.Flow` allein) wertet zum Wert des Punkts aus; ein nackter Alarm zu seinem `IsActive`. |
| `System`, `Server`, `Assets` sowie Zustand des Laufs, Produktivität, Befehle und Vorbereitung einer Einheit von Process | Der aktuelle Wert selbst: eine Zahl, ein Text, ein Wahrheitswert oder ein Zeitpunkt. Verwenden Sie ihn unmittelbar; es gibt kein Element zum Anhängen. Ein Element eines Live-Datenpunkts, das daran angehängt wird, wird abgelehnt: `System.StartTime.Value` ergibt "'System.StartTime' ist ein Wert: Lesen Sie ihn ohne .Value." |

Ein nackt geschriebener Live-Datenpunkt ist der Datenpunkt, nicht sein Wert, und steht nur als ganzer Ausdruck für seinen Wert. Überall, wo ein Operator ihn als Wert nimmt, lesen Sie ihn über das Element, das sagt, welcher Wert gemeint ist: `.ValueToBool()`, wo er auf wahr oder falsch geprüft wird (`Variables.Start.ValueToBool() ? 1 : 0`), `.Value`, wo er auf Gleichheit verglichen wird, `.ValueToReal()`, wo er geordnet oder verrechnet wird (`Variables.Level.ValueToReal() > 80`), und `.IsActive` bei einem Alarm. Die Prüfung lehnt den nackten Datenpunkt ab in der Bedingung von `?:`, als Operand von `!`, `&&` oder `||`, in einem Vergleich (`==`, `!=`, `===`, `!==`, `<`, `>`, `<=`, `>=`) und in der Arithmetik (`+`, `-`, `*`, `/`, `%` oder ein Vorzeichen davor), weil er dort immer als wahr gälte oder nie seinen Wert läse: `Variables.Level > 80` ergibt "'Variables.Level' ist der Datenpunkt selbst, nicht sein Wert: Ein Vergleich oder eine Berechnung liest nie seinen Wert. Lesen Sie seinen Wert mit Variables.Level.ValueToReal()." Ein Datenpunkt, der einer Funktion übergeben wird, und jedes Element eines Datenpunkts bleiben, wie sie geschrieben sind. Dieselbe Ablehnung gilt für die linke Seite von `??`, die ein Datenpunkt nie verlässt (lesen Sie `.Value`), und für einen Platzhalter `${…}` eines Template-Literals, der nie den Wert des Datenpunkts in den Text schreibt (lesen Sie `.ValueToString()`).

`System.Now` ist der Zeitpunkt der Auswertung: Jeder Verweis darauf innerhalb einer Auswertung liest dieselbe Zeit. Es geht wie jeder Zeitstempel als JavaScript-`Date` über, Zeitpunkte lassen sich also natürlich vergleichen und voneinander abziehen: `(System.Now - Connector.Simulated.Pump.Run.SourceTimeStamp()) / 1000` sind Sekunden.

Eine Variable der Art Ausdruck darf auf Tags, Variablen der Art Merker, Alarme, Punkte von Process, Punkte unter Assets und die Diagnosewerte von System und Server verweisen, aber **nie auf eine andere Variable der Art Ausdruck**: Der Editor lehnt das ab ("'Variables.X' ist eine Ausdrucksvariable. Ausdrücke dürfen keine anderen Ausdrucksvariablen verwenden; verwenden Sie stattdessen Merker-Variablen oder die zugrunde liegenden Datenpunkte."), damit Ergebnisse nie von der Reihenfolge der Auswertung abhängen. Eine Alarmbedingung darf auf alles außer `Alarms.*` verweisen ("Alarmbedingungen dürfen keine anderen Alarme verwenden.") und muss einen Wahrheitswert ergeben.

## Die Lesefläche eines Punkts {#the-read-surface-of-a-point}

Ein lebendes Tag bietet an:

| Element | Gibt zurück | Bedeutung |
| --- | --- | --- |
| `.Value` | typisierter Wert | Der technische Wert, nach der ganzen [Wertkette](connector-value-pipeline) des Tags |
| `.ValueRaw` | typisierter Wert | Der Rohwert, auf den erklärten Typ des Tags gebracht, vor der Skalierung |
| `.ValueObject` | beliebig | Der Wert genau so, wie der Treiber ihn erzeugt hat |
| `.ValueScaled`, `.ValueCalibrated`, `.ValueLimited`, `.ValueRounded` | Zahl | Der Wert nach den Stufen Skalierung, Kalibrierung, Grenzen und Nachkommastellen |
| `.ValueToBool()`, `.ValueToInteger()`, `.ValueToReal()`, `.ValueToString()`, `.ValueToDateTime()` | gewandelter Wert | Der technische Wert, in diese Kategorie gewandelt; kein Wert bleibt kein Wert |
| `.State()` | Wahrheitswert | Qualität: wahr, solange der Messwert gut ist |
| `.SourceTimeStamp()` | Date | Wann das Gerät den Messwert erzeugt hat, sofern sein Protokoll ihn datiert (OPC UA); sonst der Zeitpunkt, zu dem der Connector ihn empfangen hat, derselbe wie `.ApplicationTimeStamp()` |
| `.ApplicationTimeStamp()` | Date | Wann der Connector ihn empfangen hat |
| `.Name`, `.EngineeringUnit` | Text | Name und Einheit des Tags |

Eine Variable bietet `.Value`, die fünf Wandlungen `.ValueTo*()`, `.State()`, `.ApplicationTimeStamp()`, `.Name` und `.EngineeringUnit` an. Ein Alarm bietet `.IsActive`, `.IsAcked`, `.CurrentState`, `.State()` (ob seine Bedingung im letzten Zyklus ausgewertet werden konnte), `.ActiveSince()`, `.LastTransitionAt()` und `.Name` an. Ein Kanal von Process bietet die Fläche eines Tags an, wenn er an ein Tag gebunden ist, und die Fläche einer Variablen, wenn er an eine Variable gebunden ist; ein ungebundener Kanal bietet nichts an. Sonst ist an einem lebenden Objekt nichts erreichbar: Ein Element außerhalb dieser Fläche liest als undefiniert, und eines aufzurufen scheitert bei der Prüfung. Die Auswahl Durchsuchen… führt unter jedem Punkt genau diese Elemente auf, sodass keines davon aus dem Gedächtnis getippt werden muss.

## Die Regeln, die der Auswerter erzwingt {#the-rules-the-evaluator-enforces}

Sowohl im Editor beim Tippen als auch erneut in jedem Zyklus von 100 ms:

- **Das Tor der Qualität.** Vor der Auswertung wird jeder genannte Punkt aufgelöst und aufgefrischt. Jeder Punkt, der fehlt, schlecht ist oder keinen Wert hält, macht das Ergebnis zu einer Lücke schlechter Qualität, ohne den Ausdruck auszuführen, ein Tag, das offline ist, kann also nie einen Abtastwert erfinden, und `null` erreicht die Maschine nie, um dort zu `0` zu werden. Ein Tag, dessen Messwert verstummt ist, ohne neuen Messwert während seines Abfrageintervalls plus 10 Sekunden, zählt hier als fehlgeschlagener Lesevorgang: schlecht, es sei denn, seine Regel für [ein Gerät, das nicht geantwortet hat](connector-value-pipeline#the-device-did-not-answer) stellt einen Ersatzwert dar.
- **Nicht endliche Ergebnisse sind Lücken.** NaN oder unendlich (Teilen durch null, undefinierte Arithmetik) wird nie zu einem Abtastwert.
- **Schutz vor Ausreißern.** Eine Auswertung wird jenseits von 250 ms, 5.000 Anweisungen oder 4 MB Skriptspeicher abgebrochen und zählt als Lücke schlechter Qualität. Je Text eines Ausdrucks wird eine Maschine gehalten, aus einem anderen Ausdruck dringt also nichts herüber.
- **Wandlung.** Ein erfolgreiches Ergebnis wird in den [Ausgabetyp](logic-variables#output-types) der Variablen gewandelt; eine Alarmbedingung wird in einen Wahrheitswert gewandelt, und ein Ergebnis, das sich nicht wandeln lässt, hält den Zustand des Alarms.
- **Fehler sind Lücken, keine Ausnahmen.** Ein Fehler zur Laufzeit im Zyklus ergibt eine Lücke und einen Eintrag im Journal (den ersten Fehlschlag, Zusammenfassungen, solange er sich wiederholt, eine Zeile bei der Erholung); der Zyklus macht mit der nächsten Variablen weiter.

## Was die Prüfung ablehnt {#what-validation-refuses}

Der Editor prüft den Entwurf jede halbe Sekunde und zeigt das erste Problem unter dem Wert oder der Bedingung:

| Meldung | Ursache |
| --- | --- |
| Ein Ausdruck ist erforderlich. / Eine Bedingung ist erforderlich. | Der Editor ist leer. |
| Fehler im Ausdruck: … | Der Text lässt sich nicht lesen. |
| Eine Variable hält einen einzelnen Ausdruck. Anweisungen gehören in Aktionen. | Mehr als eine Anweisung oder eine Anweisung, die kein Ausdruck ist. |
| Ausdrücke sind nur lesbar. Schreibvorgänge gehören in Aktionen. | Eine Zuweisung oder `++`/`--`. |
| Adressen müssen fest sein: Auf 'Root' wird mit einem berechneten Segment zugegriffen. | `Variables[name]` oder irgendein Segment, das kein Literal ist. |
| Unvollständige Adresse 'Connector.Simulated'. | Eine Kette, die zu kurz ist, um unter ihrer Wurzel einen Punkt zu benennen. |
| 'X' wurde nicht gefunden. | Die Adresse löst auf nichts auf. |
| 'X' ist eine Ausdrucksvariable. … | Ein Verweis auf eine andere Ausdrucksvariable. |
| 'X' ist ein Wert: Lesen Sie ihn ohne .Value. | Ein Element eines Live-Datenpunkts (`.Value`, `.State()`, …) an einem Punkt, der als sein Wert eingeht (`System.StartTime.Value`). |
| 'X' ist der Datenpunkt selbst, nicht sein Wert: Als Bedingung gilt er immer als wahr. Lesen Sie seinen Wert mit X.ValueToBool(). | Ein nackt geschriebener Live-Datenpunkt, wo er auf wahr oder falsch geprüft wird: die Bedingung von `?:` oder ein Operand von `!`, `&&` oder `\|\|` (`!Variables.Start`). Bei einem Alarm nennt die Meldung `.IsActive`. |
| 'X' ist der Datenpunkt selbst, nicht sein Wert: Ein Vergleich oder eine Berechnung liest nie seinen Wert. Lesen Sie seinen Wert mit X.ValueToReal(). | Ein nackt geschriebener Live-Datenpunkt in einem Vergleich oder in der Arithmetik (`Variables.Level > 80`, `Variables.Level * 2`). Bei `==`, `!=`, `===` und `!==` nennt die Meldung `.Value`, bei einem Alarm `.IsActive`. |
| 'X' ist der Datenpunkt selbst, nicht sein Wert: Er ist nie leer, also behält ?? immer ihn. Lesen Sie seinen Wert mit X.Value. | Ein nackt geschriebener Live-Datenpunkt links von `??` (`Variables.Level ?? 0`). Bei einem Alarm nennt die Meldung `.IsActive`. |
| 'X' ist der Datenpunkt selbst, nicht sein Wert: In einen Text geschrieben, zeigt er nie seinen Wert. Lesen Sie seinen Wert mit X.ValueToString(). | Ein nackt geschriebener Live-Datenpunkt in einem Platzhalter `${…}` eines Template-Literals. Bei einem Alarm nennt die Meldung `.IsActive`. |
| Alarmbedingungen dürfen keine anderen Alarme verwenden. | Ein Verweis auf `Alarms.*` in einer Bedingung. |
| Die Bedingung muss einen Wahrheitswert ergeben, sie hat aber 'x' ergeben. | Die Probeauswertung einer Bedingung ergab einen vorhandenen Wert, der sich nicht in einen Wahrheitswert wandeln lässt. |
| Fehler im Ausdruck: Ein Live-Datenpunkt lässt sich hier nicht unmittelbar verwenden. Lesen Sie ihn über sein Element .Value (z. B. Connector.Driver.Device.Tag.Value). | Ein lebendes Objekt dort verwendet, wo ein einfacher Wert nötig ist (`Math.round(Variables.Level)`). |
| Fehler im Ausdruck: `<Meldung der Maschine>` | Die Probeauswertung hat geworfen: ein unbekannter Bezeichner, ein gesperrtes Element als Funktion aufgerufen. |

Die Probeauswertung läuft mit den aktuellen Werten, ohne Rücksicht auf die Qualität, eine Meldung spiegelt also den Ausdruck und keine schlechte Eingabe; eine schlechte Eingabe zeigt sich stattdessen in der Karte GELESENE PUNKTE.

## Der Editor für Skripte {#the-script-editor}

Jeder Ausdruck, jede Bedingung und jedes Skript auf der Seite wird in derselben Karte bearbeitet: eine Augenbraue (AUSDRUCK, BEDINGUNG oder SKRIPT), eine Schaltfläche **Durchsuchen…**, der Editor, ein Hinweis und die Prüfmeldung. Der Editor ist CodeMirror mit Hervorhebung für JavaScript, einer Geschichte zum Rückgängigmachen und Vervollständigung für die Zeichen einer Adresse: Wurzelnamen bei einem nackten Zeichen, dann die Kinder des bisher getippten Pfades, Elemente eingeschlossen, sobald das Zeichen einen Punkt nennt ([eine Adresse eingeben](logic-address-space#entering-an-address)). Der Text wird verzögert in den Entwurf geschoben und automatisch gespeichert. Die Felder für Ausdruck und Bedingung sind mindestens 56 px hoch, das Feld für das Skript einer Aktion 160 px. Kann die Maschine des Editors nicht starten, fällt das Feld auf ein einfaches Textfeld zurück, das weiterhin liest und schreibt, ohne Hervorhebung, Vervollständigung und Geschichte; Durchsuchen… hängt die gewählte Adresse dann am Ende an statt an der Einfügemarke.

**Durchsuchen…** öffnet einen Dialog ("Durchsuchen: in Ausdruck einfügen") mit dem Baum des ganzen Adressraums bis auf die Ebene der Elemente; wählen Sie eine Zeile und drücken Sie **Einfügen** oder klicken Sie doppelt darauf, um ihre Adresse an die Einfügemarke zu setzen. Jeder Editor besitzt seinen eigenen Dialog, zwei Editoren auf einer Seite teilen sich also nie eine Auswahl.

## Was Ausdrücke nicht tun {#what-expressions-do-not-do}

Sie schreiben nichts, rufen keine Befehle für Läufe von Process auf, halten zwischen zwei Auswertungen keinen Zustand, können keine Funktionen bilden, die sich mehrere Ausdrücke teilen, und werden nicht auf Anforderung ausgewertet: Der Zyklus veröffentlicht sie alle 100 ms, und der Editor zeigt den Entwurf zweimal je Sekunde vorab.
