Leitfaden zur Verwendung von Formula (Formel, fomula)
Wenn Sie bei der Tag-Registrierung im Feld fomula einen arithmetischen Ausdruck hinterlegen, verarbeitet der Treiber den vom PLC gelesenen Rohwert (raw value) nach und verwendet den umgerechneten Wert für Cache / Sparkplug / REST-Antworten.
| Punkt | Wert |
|---|---|
| Implementierungsklasse | plantpulse.app.edge.component.collector.plc.PLCValueFomula |
| Aufrufstelle | Schritt 3 von PLCValueReader.read() (raw → format → fomula → Cache-Aktualisierung) |
| Bibliothek | plantpulse-edge-fomula.jar (plantpulse.edge.fomula.FormulaEngine) |
| Variablenersetzung | ${VALUE} (aktueller Tag-Wert) / ${tag_id} (letzter Wert eines anderen Tags, LastValueMap-Cache) |
Geltungsbereich data_type | Nur Float / Double / Integer / Long. Andere Typen (String, Boolean, …) werden unverändert durchgereicht |
| Ergebnis | BigDecimal von Formula.evaluate(...) → typabhängiges Rendering (ganzzahlige Typen als Ganzzahl, Float/Double als Dezimalzahl) |
Ablauf
-
Der Treiber empfängt den Roh-String-Wert (z. B.
"16384"). -
PLCValueFomula.fomulaValue(address, value)wird aufgerufen. -
Ist
address.getFomula()leer, wird der Wert unverändert zurückgegeben. -
Ist
data_typenicht Float / Double / Integer / Long, wird der Wert unverändert zurückgegeben. -
Die in der Formel referenzierten Namen werden ermittelt —
VALUEist der soeben gelesene Wert, alle übrigen sind die letzten Werte anderer Tags ausLastValueMap. -
Wurde ein referenzierter Tag noch nie erfasst, tritt eine Ausnahme auf:
Variable value referenced by the formula is not yet in cache : formula=[`<fomula>`], variable=[`<name>`] -
Nur die in der Formel referenzierten Namen werden mit Werten gebunden und ausgewertet →
BigDecimal→ Rückgabe als typabhängiger String.
Variablenarten
| Notation | Bedeutung | Quelle |
|---|---|---|
${VALUE} | Rohwert des aktuellen Tags | Der soeben vom Treiber gelesene Wert |
${TAG_ID_X} | Letzter Wert eines anderen Tags | LastValueMap (gemeinsamer Cache aller Tags) |
LastValueMap hält die letzten Roh- bzw. nachverarbeiteten Werte aller Tags. Referenziert werden können nicht nur andere Tags desselben OPC, sondern auch Tags anderer OPC (sofern der betreffende Tag mindestens einmal erfasst wurde und im Cache vorliegt).
Wird ${TAG_ZERO} referenziert, TAG_ZERO aber noch nie erfasst, kann kein Wert gebunden werden und es tritt eine Ausnahme auf. Wählen Sie für den referenzierten Tag eine kürzere timecycle oder registrieren Sie ihn unter auto_collect=true, damit er zuerst erfasst wird. (Die Syntaxprüfung beim Speichern lässt diesen Fall durch — es handelt sich um ein Problem der Erfassungsreihenfolge, nicht um einen Formelfehler.)
Ausdruckssyntax
Die Schreibweise entspricht der von Excel. Funktionsnamen und Bedeutung sind identisch mit Excel, sodass Sie in Excel verwendete Formeln nahezu unverändert übernehmen können. Groß-/Kleinschreibung spielt keine Rolle (IF = if).
| Kategorie | Token | Anmerkung |
|---|---|---|
| Arithmetik | +, -, *, /, ^ | ^ ist die Potenzierung |
| Vergleich | >, <, >=, <=, =, <> | = ist gleich, <> ist ungleich (wie in Excel) |
| Gruppierung | (, ) | Klammern haben Vorrang |
| Konstanten | PI, E | Kreiszahl, eulersche Zahl |
| Bedingung | IF, SWITCH, AND, OR, NOT | IF(조건, 참일때, 거짓일때) |
| Rundung | ROUND, ROUNDUP, ROUNDDOWN, CEILING, FLOOR, INT, TRUNC | ROUND(값, 자리수) |
| Numerik | ABS, SIGN, MOD, POWER, SQRT, CBRT, EXP, FACT | Das Vorzeichen des Rests bei MOD folgt dem Divisor |
| Aggregation | MIN, MAX, SUM, AVERAGE, COALESCE | Mehrere Argumente |
| Logarithmus | LOG, LOG10, LN | LOG = dekadischer Logarithmus (Basis 10), LN = natürlicher Logarithmus |
| Trigonometrie | SIN, COS, TAN, ASIN, ACOS, ATAN, ATAN2 | Basis Radiant (wie in Excel) |
| Hyperbelfunktionen | SINH, COSH, TANH, ASINH, ACOSH, ATANH | |
| Winkelumrechnung | DEGREES, RADIANS | Radiant ↔ Grad |
- Bitoperationen (
&,|) - Benutzerdefinierte Funktionen
- String- / Datumsoperationen Falls Sie dies benötigen, behandeln Sie es in einer nachgelagerten Stufe (externe Funktion / Nachverarbeitungs-Node).
Syntaktisch fehlerhafte Formeln werden bereits beim Speichern abgelehnt (UI-Speicherung / CSV-Upload / Backup-Wiederherstellung gleichermaßen). Im Tag-Konfigurationsdialog können Sie einen Beispielwert eingeben und das Ergebnis vorab prüfen.
Fehler während der Berechnung (Division durch 0, referenzierter Wert ist keine Zahl usw.) werden als Erfassungsfehler protokolliert und mindern die Qualität des betreffenden Tags. Es wird kein plausibel wirkender Ersatzwert gespeichert.
Variablennotation
Sowohl ${VALUE} als auch der bloße Name VALUE sind zulässig. Gleiches gilt für andere Tags —
${TAG_ZERO} = TAG_ZERO. Bereits gespeicherte ${...}-Formeln funktionieren unverändert weiter.
${VALUE}*0.1 기존 표기
VALUE*0.1 같은 뜻
IF(VALUE>100, 100, VALUE*0.1) 엑셀식
Umfangreiche Beispielsammlung
1. Einfache Skalierung (×0,1)
| Punkt | Wert |
|---|---|
data_type | Float |
fomula | ${VALUE}*0.1 |
| Eingabe → Ausgabe | 16384 → 1638.4 |
Verwendung: Ganzzahliger Rohwert → Gleitkommazahl mit einer Nachkommastelle. Wenn Temperatur-, Druck- oder Durchflusssensoren ihre Werte als Ganzzahlen senden.
2. Einheitenumrechnung (mV → V)
| Punkt | Wert |
|---|---|
data_type | Float |
fomula | ${VALUE}/1000 |
| Eingabe → Ausgabe | 3300 → 3.3 |
3. Offset-Korrektur (Celsius → Kelvin)
| Punkt | Wert |
|---|---|
data_type | Float |
fomula | ${VALUE}+273.15 |
| Eingabe → Ausgabe | 25 → 298.15 |
4. Referenz auf anderen Tag (Nullpunktkorrektur)
| Punkt | Wert |
|---|---|
data_type | Float |
fomula | ${VALUE}-${TAG_ZERO} |
| Eingabe → Ausgabe | VALUE=1024, TAG_ZERO=24 → 1000 |
TAG_ZERO ist die ID eines anderen Tags. Wird für Nullpunkt-/Tara-Korrektur verwendet.
5. Polynom (Quadrat)
| Punkt | Wert |
|---|---|
data_type | Float |
fomula | ${VALUE}*${VALUE}*0.001 |
| Eingabe → Ausgabe | 100 → 10.0 |
6. Funktion — Quadratwurzel
| Punkt | Wert |
|---|---|
data_type | Float |
fomula | sqrt(${VALUE}) |
| Eingabe → Ausgabe | 144 → 12.0 |
7. Funktion — Trigonometrie (sin, Radiant)
| Punkt | Wert |
|---|---|
data_type | Float |
fomula | sin(${VALUE}) |
| Eingabe → Ausgabe | 1.5708 (≈π/2) → 1.0 |
Bei Eingabe in Grad: sin(${VALUE}*pi/180).
8. Mehrere Tags — Kalibrierung (gain × x + offset)
| Punkt | Wert |
|---|---|
data_type | Float |
fomula | ${VALUE}*${TAG_GAIN}+${TAG_OFFSET} |
| Eingabe → Ausgabe | VALUE=100, GAIN=0.05, OFFSET=2 → 7.0 |
Muster, bei dem gerätespezifische Korrekturkoeffizienten als eigene Tags (oder HTTP-bind-Tags) verwaltet werden.
9. Typumwandlung / erzwungene Gleitkommadarstellung
| Punkt | Wert |
|---|---|
data_type | Float |
fomula | ${VALUE}*1.0 |
| Eingabe → Ausgabe | 123 → 123.0 |
Wenn ein als Integer eingehender Wert lediglich nach Float gecastet werden soll.
10. Potenz / Exponent
| Punkt | Wert |
|---|---|
data_type | Float |
fomula | ${VALUE}^2 |
| Eingabe → Ausgabe | 5 → 25.0 |
11. Logarithmus
| Punkt | Wert |
|---|---|
data_type | Float |
fomula | log(${VALUE}) |
| Eingabe → Ausgabe | 100 → 2.0 |
ln(...) ist ebenfalls verwendbar (natürlicher Logarithmus).
12. Kombiniert — dB-Umrechnung des RMS
| Punkt | Wert |
|---|---|
data_type | Float |
fomula | 20*log(${VALUE}) |
| Eingabe → Ausgabe | 1000 → 60.0 |
REST-Registrierungsbeispiel (curl)
Registrierung eines einzelnen Tags:
curl -X POST http://<edge-host>/api/v1/opc/OPC_LS_XBM_0001/tag \
-H "Content-Type: application/json" \
-d '{
"tag_id": "OPC_LS_XBM_0001_TAG_PRESS",
"tag_name": "Pressure (kPa)",
"plc_address": "D00100",
"data_type": "Float",
"format": "REAL",
"fomula": "${VALUE}*0.1",
"description": "스케일 ×0.1 적용"
}'
Bei der gebündelten Registrierung von OPC + Tags fügen Sie fomula einfach innerhalb von tag_list[] ein.
Wert lesen (es wird der nachverarbeitete Wert zurückgegeben):
curl -s http://<edge-host>/api/v1/tag/OPC_LS_XBM_0001_TAG_PRESS/value | jq
# {
# "result": "OK",
# "data": {
# "tag_id": "OPC_LS_XBM_0001_TAG_PRESS",
# "value": "1638.4",
# ...
# }
# }
Häufige Fehler + Lösungen
| Meldung / Symptom | Ursache | Lösung |
|---|---|---|
계산식에 해당하는 변수값이 아직 캐시에 없습니다 : 계산식=[${VALUE}-${TAG_ZERO}] | Der referenzierte Tag (TAG_ZERO) wurde noch nie erfasst | Referenzierten Tag zuerst registrieren + mit auto_collect=true einen Zyklus durchlaufen lassen |
계산식이 참조하는 값이 숫자가 아닙니다 | Der Wert des referenzierten Tags ist keine Zahl (z. B. Referenz auf einen Tag mit data_type=String) | So anpassen, dass ein Tag mit numerischem Typ referenziert wird |
Closing brace not found / Missing second operand | Klammern nicht paarig oder Operand nach einem Operator fehlt | Wird beim Speichern abgelehnt; die Fehlermeldung im Dialog nennt auch die Position |
| Nachverarbeitung wird nicht angewendet (Rohwert wird unverändert zurückgegeben) | data_type ist String/Boolean | Auf Float/Double/Integer/Long ändern oder bei Bedarf eine separate Verarbeitungsstufe verwenden |
Ergebnis ist immer 0 | Der Rohwert ist tatsächlich 0 | Zunächst den gelesenen Rohwert mit GET /api/v1/tag/.../value prüfen. (Der alte Parser berechnete unvollständige Ausdrücke wie ${VALUE}* stillschweigend als 0; heute werden solche Ausdrücke bereits beim Speichern abgelehnt.) |
Erfassungsfehler FORMAT_FAILED + Qualitätsminderung | Berechnungsfehler, z. B. Division durch 0 oder negativer Wert bei sqrt | Infinity/NaN werden nicht gespeichert, sondern als Fehler ausgewiesen. Bauen Sie eine Absicherung in die Formel ein — z. B.: IF(TAG_ZERO=0, 0, VALUE/TAG_ZERO) |
Betriebshinweise
LastValueMapist ein Singleton (in-memory). Beim Neustart des Gateway wird er initialisiert → im ersten Zyklus können Referenzen auf andere Tags fehlschlagen.- Die Berechnung erfolgt durchgängig mit
BigDecimal, sodass auch große Ganzzahlen ausLong/QWordbis zur letzten Stelle erhalten bleiben (kein Genauigkeitsverlust wie bei der altendouble-Auswertung). - Das Ergebnis wird als
Double.toString()→ String-Cache abgelegt. Für Anzeige / Sparkplug-Veröffentlichung wird es erneut nachdata_typegecastet (siehe SparkplugDataTypeMapper). - Referenzieren sich innerhalb desselben OPC die fomula mehrerer Tags gegenseitig, kann der erste Zyklus je nach Erfassungsreihenfolge teilweise fehlschlagen. Betrieblich ist das unkritisch (ab dem nächsten Zyklus normal); für eine saubere Lösung legen Sie die referenzierten Tags in ein separates OPC mit kürzerer
timecycle.