Skip to content

Skriptumgebung

mdzio edited this page Apr 1, 2024 · 8 revisions

Skriptumgebung

Der CCU-Historian besitzt eine Skriptumgebung, die direkten Zugriff auf die Datenbank besitzt. Dadurch eröffnen sich vielfältige Anwendungen: Automatisierte Massenkonfiguration, Erstellung von Statistiken und Analysen, Manipulation von Zeitreihen, usw.. Skripte können über WerkzeugeSkriptumgebung eingegeben und ausgeführt werden. Beispielskripte sind in einem eigenen Abschnitt zu finden.

Neben Skripten mit einzelnen Anweisungen können auch umfangreiche Berechnungen, Kombinationen und Analysen von Zeitreihen mit Hilfe von Ausdrücken bzw. Formeln/Termen erfolgen. Beispielausdrücke dazu sind in einem eigenen Abschnitt zu finden.

Die Skriptumgebung verwendet die Programmiersprache Groovy für die Ausführung der Skripte. Der gesamte Sprachumfang kann genutzt werden, allerdings kann absichtlich nur auf bestimmte Klassen bzw. Objekte zugegriffen werden.

Skriptausgabe

Mit den Befehlen print und println (mit Zeilenvorschub) können Skriptausgaben erzeugt werden.

Skript:

print "Hallo "
println "Welt!"
println "Dies erscheint in der nächsten Zeile."

Ausgabe:

Hallo Welt!
Dies erscheint in der nächsten Zeile.

Wenn in einem Skript die Befehle print und println nicht verwendet werden, dann wird als Ausgabe das Ergebnis des letzten Skriptausdrucks angezeigt (ab V3.4.0). Dadurch kann beispielsweise das Ergebnis von Rechenausdrücken direkt angezeigt werden.

Skript:

5+6

Ausgabe:

11

Interessanter ist natürlich die Ausgabe von berechneten Zeitreihen (ab V3.4.0):

Skript:

dataPoint(63).average(daily()).read(parseDate("1.1.2023"), parseDate("4.1.2023"))

Ausgabe:

2023-01-01 00:00:00.000, 13.557506101983593, 2
2023-01-02 00:00:00.000, 10.666991404360811, 2
2023-01-03 00:00:00.000, 7.557838434259367, 2

Vordefinierte Variablen

version (ab V3.4.0)

In der Variablen version ist die Versionsnummer des CCU-Historians zu finden.

database

In der Variable database ist ein Datenbankobjekt zu finden, das den lesenden und schreibenden Zugriff auf die Zeitreihen-Datenbank ermöglicht. Der schreibende Zugriff muss in der Skriptumgebung explizit freigeschaltet werden. Zeitbereiche werden immer als halboffenes Intervall angegeben (begin <= Zeitstempel < end).

// Liste aller Datenpunkte
List<DataPoint> dataPoints

// Datenpunkt mit der angegebenen Historian-ID
// Rückgabe: null, wenn nicht gefunden
DataPoint getDataPoint(int idx)

// Datenpunkt mit dem angegebenen DataPointIdentifier
// Rückgabe: null, wenn nicht gefunden
DataPoint getDataPoint(DataPointIdentifier id)

// Datenpunkt anlegen
void createDataPoint(DataPoint dp)

// Geänderte Eigenschaften eines Datenpunktes speichern
void updateDataPoint(DataPoint dp)

// Datenpunkt löschen
void deleteDataPoint(DataPoint dp)

// Zeitstempel des ersten Eintrags
// Rückgabe: null, wenn nicht vorhanden
Date getFirstTimestamp(DataPoint dp)

// Prozesswert des letzten Eintrags
// Rückgabe: null, wenn nicht vorhanden
ProcessValue getLast(DataPoint dp)

// Tatsächliche Zeitreiheneinträge eines Datenpunktes
TimeSeries getTimeSeriesRaw(DataPoint dp, Date begin, Date end)

// Zeitreihe eines Datenpunktes mit interpolierten Einträgen am Beginn und
// Ende des Zeitbereichs
TimeSeries getTimeSeries(DataPoint dp, Date begin, Date end)

// Anzahl der Einträge in einem Zeitbereich
int getCount(DataPoint dp, Date startTime, Date endTime)

// Zeitbereich löschen
int deleteTimeSeries(DataPoint dp, Date startTime, Date endTime)

// Zeitreihe kopieren
int copyTimeSeries(DataPoint dstDp, DataPoint srcDp, Date startTime, Date endTime, Date newStartTime)

// Zeitreihe ersetzen
int replaceTimeSeries(DataPoint dstDp, Iterable<ProcessValue> srcSeries, Date startTime, Date endTime)

// Zeitreiheneintrag zur Datenbank hinzufügen
void consume(Event t)

// Mehrere Aufrufe transaktional ausführen
Object transactional(Closure cl)

// Persistente Variable lesen
String getConfig(String name)

// Persistente Variable schreiben
void setConfig(String name, String value)

// Backup der Datenbank in Datei schreiben
void createBackup(String fileName)

Vordefinierte Klassen

DataPoint

Ein DataPoint-Objekt wird vom Database-Objekt zurückgegeben und enthält alle Eigenschaften zu einem bestimmten Datenpunkt. Damit geänderte Eigenschaften vom CCU-Historian übernommen werden, müssen diese mit database.updateDataPoint() gespeichert werden. println mit einem DataPoint-Objekt zeigt alle Eigenschaften an.

// Datenpunkt erscheint nicht in der Datenpunktliste
boolean historyHidden

// Datenpunkt wird nicht aufgezeichnet
boolean historyDisabled

// Werte sind vom Typ Zeichenkette
boolean historyString

// Werte sind stetig (z.B. Temperatur im Gegensatz zu einem Schließerkontakt)
boolean continuous

// Datenpunkteigenschaften sollen nicht (erneut) aus der CCU ausgelesen werden
// (z.B. bei manuellen Anpassungen)
boolean noSynchronization

// Datenpunkteigenschaften wurde einmal aus der CCU ausgelesen
boolean synced

// abgeleiteter Anzeigename des Datenpunktes (nur lesbar)
String displayName

// Allgemeine Datenpunkteigenschaften
// Aus der ReGaHss: displayName, room, function, comment
// Vom Gerät:       paramSet, tabOrder, maximum, unit, minimum, control, operations,
//                  flags, type, defaultValue
// Vom Historian:   preprocType, preprocParam
Map attributes

DataPointIdentifier

Durch ein DataPointIdentifier-Objekt wird ein Datenpunkt eindeutig identifiziert. Es kann für den Aufruf von database.getDataPoint() verwendet werden.

Erstellen:

// Erstellen eines DataPointIdentifier's
new DataPointIdentifier(interfaceId, address, identifier)

Eigenschaften:

// Schnittstelle (z.B. "HmIP")
String interfaceId

// Adresse (z.B. "ABC000000")
String address

// Bezeichner (z.B. "TEMPERATURE")
String identifier

TimeSeries

In einem TimeSeries-Objekt wird eine Zeitreihe gespeichert. Es wird z.B. von database.getTimeSeries() zurückgegeben. Zum Einfügen von Zeitreihen kann es auch manuell erstellt werden.

Erstellen:

// Erstellen einer TimeSeries mit dem dazugehörigen Datenpunkt
new TimeSeries(dataPoint)

Eigenschaften:

// Zugehöriger Datenpunkt
DataPoint dataPoint

// Anzahl der Einträge
int size

Funktionen:

// Iterator über die Zeitreiheneinträge (dadurch kann TimeSeries z.B. für
// eine each-Schleife verwendet werden)
Iterator<ProcessValue> iterator()

// Eintrag hinzufügen
void add(ProcessValue pv)

// Eintrag an einem bestimmten Index entfernen (Index startet bei 0!).
void remove(int index)

// Eintrag an einem bestimmten Index lesen (Groovy-Kurzform: [idx])
ProcessValue getAt(int idx)

// Eintrag an einem bestimmten Index schreiben (Groovy-Kurzform: [idx])
void putAt(int idx, ProcessValue pv)

ProcessValue

Zeitreihen bestehen aus einzelnen Prozesswerten. Diese können aus Zeitreihen gelesen werden oder manuell erstellt und dann in Zeitreihen eingefügt werden.

Erstellen:

// Erstellt einen Prozesswert (value kann entweder vom Typ Double oder String
// sein, state ist eine Bitmaske)
new ProcessValue(Date timestamp, Object value, int state)

Eigenschaften:

// Zeitstempel des Prozesswetes als Java-Date-Objekt
Date timestamp

// Wert (Double oder String)
Object value

// Wert direkt als Double konvertiert
Double doubleValue

// Zustand
int state

Zustand eines Prozesswertes (State) als Bitmaske. Mit den zwei niederwertigsten Bits wird die Qualität kodiert:

STATE_QUALITY_BAD = 0x00000000
STATE_QUALITY_QUESTIONABLE = 0x00000001
STATE_QUALITY_NOT_SUPPORTED = 0x00000002
STATE_QUALITY_GOOD = 0x00000003

STATE_PREPROCESSED = 0x00000004
STATE_FIRST_ARCHIVED = 0x00000008

Date

Für Zeitstempel verwendet der CCU-Historian das Java-Date-Objekt. Es enthält die Anzahl an Millisekunden seit 1.1.1970 UTC. Es ist keiner bestimmten Zeitzone zugeordnet. Die im Betriebsystem eingestellte Zeitzone wird erst bei den Funktionen parseDate() oder formatDate() (siehe unten) berücksichtigt.

Eigenschaften:

// Anzahl an Millisekunden seit 1.1.1970 UTC
Long time

Expression (ab V3.4.0)

Ein Expression-Objekt stellt eine berechnete Zeitreihe dar. Diese können über bestimmte Funktionen (z.B. .differentiate()) oder Operatoren (z.B. +-*/) zu neuen Expression-Objekten kombiniert werden. Sie enthalten noch keine Zeitreiheneinträge wie das TimeSeries-Objekt. Die Zeitreiheneinträge werden erst beim Lesen über read() generiert.

Die Erstellung von Expression-Objekten ist im Abschnitt "Vordefinierte berechnete Zeitreihen" weiter unten beschrieben.

Eigenschaften:

// Charakteristiken/Merkmale der Zeitreihe (Bitmaske, nur lesend)
int characteristics

Die Charakteristiken/Merkmale einer Zeitreihe beeinflussen die Berechnungen und die Darstellung. Dies geschieht in der Regel automatisch:

// Der letzte Wert wird bis zur nächsten Abtastung gehalten. Dies sollte für
// binäre Sensoren (z. B. Schalter) verwendet werden.
HOLD = 0x0001

// Der Wert wird zwischen den Abtastung linear interpoliert. Dies sollte für
// stetige Sensoren (z. B. Temperatur) verwendet werden.
LINEAR = 0x0002

// Zeigt eine Ereigniszeitreihe an (z.B. für Tastendrücke). Der Zeitstempel
// ist vom primären Interesse. Der Wert ist in der Regel 1,0.
EVENT = 0x0004

// Der Wert ist ein stetig steigender Zählerstand (z. B. Energiezähler,
// Regenzähler).
COUNTER = 0x0008

Funktionen:

// Die Berechnung der Zeitreihe für ein konkretes Zeitinterval wird
// gestartet. Erst während des Lesens der Einträge über den Iterator
// werden diese berechnet.
Iterator<ProcessValue> read(Date begin, Date end)

// Filtert die Zeitreiheneinträge. Zeitreiheneinträge, die nicht das
// Filterkriterium erfüllen, werden verworfen.
Expression filter(Predicate<ProcessValue> predicate)

// Die übergebene Funktion wird für jeden Zeitzeiheneintrag aufgerufen.
// In der Funktion kann der Wert und der Zustand des Eintrags angepasst
// werden.
Expression unaryOperator(UnaryOperator<ProcessValue> operator)

// Dies ist eine Komfortfunktion für unaryOperator. Die übergebene
// Funktion arbeitet direkt mit dem Double-Wert. NaN, Infinity und
// Exceptions werden automatisch in einen ProcessValue mit Status BAD
// umgewandelt.
// Beispiel: a.map( y -> 1/y )
Expression map(DoubleUnaryOperator operator) 

// Vereinfachung von unaryOperator() zur Erzeugung einer Zeitreihe über
// eine Funktion, die einen Prozesswert in Abhängigkeit vom Zeitstempel
// berechnet.
//
// Beispiel für eine Sinuskurve über einen Tag:
// hourly().generate(t -> sin(t.time/1000/60/60/24*2*PI))
Expression generate(Function<Date, Double> operator)

// Die Werte der Zeitreihe werden negiert.
Expression negative()

// Ruft einen binären Operator auf, um zwei Zeitreihen zu kombinieren.
// Die beiden Zeitreihen sollten das Merkmal LINEAR haben. Allerdings
// werden auch Zeitreihen mit dem Merkmal HOLD automatisch mit linear()
// umgewandelt. Die neue Zeitreihe hat Einträge zu Zeitpunkten aus
// beiden Zeitreihen.
// Die Merkmale der ersten Zeitreihe werden mit dem Merkmal LINEAR
// gesetzt und HOLD zurückgesetzt zurückgegeben.
Expression binaryOperator(Expression other, BinaryOperator<ProcessValue> operator)

// Dies ist eine Komfortfunktion für binaryOperator. Die übergebene
// Funktion arbeitet direkt auf den Double-Werten. NaN, Infinity und
// Ausnahmen werden automatisch in ein ProcessValue mit Status BAD
// umgewandelt.
// Beispiel: a.combine(b, (xa, xb) -> xa+1/xb )
Expression combine(Expression other, DoubleBinaryOperator operator)

Expression plus(Expression other) // Kurzform: +
Expression plus(Number constant) // Kurzform: +

Expression minus(Expression other) // Kurzform: -
Expression minus(Number constant) // Kurzform: -

Expression multiply(Expression other) // Kurzform: *
Expression multiply(Number constant) // Kurzform: *

Expression div(Expression other) // Kurzform: /
Expression div(Number constant) // Kurzform: /

// Die Ergebniszeitreihe hat den Wert 1,0, wenn der Wert dieser Zeitreihe
// größer als 0,0 bzw. eine andere Zeitreihe bzw. eine Konstante ist,
// sonst den Wert 0.0. Der Status wird kopiert.
Expression greaterThanZero()
Expression greaterThan(Expression other)
Expression greaterThan(Number constant)

// Die Ergebniszeitreihe hat den Wert 1,0, wenn der Wert dieser Zeitreihe
// kleiner als 0,0 bzw. eine andere Zeitreihe bzw. eine Konstante ist,
// sonst den Wert 0.0. Der Status wird kopiert.
Expression lessThanZero()
Expression lessThan(Expression other)
Expression lessThan(Number constant)

// Alle negativen Werte der Zeitreihe werden bei Null abgeschnitten.
// Die Zeitreihe sollte das Merkmal LINEAR haben. Allerdings wird
// auch Zeitreihen mit dem Merkmal HOLD automatisch mit linear()
// umgewandelt.
// Die Merkmale der ersten Zeitreihe werden mit dem Merkmal LINEAR
// gesetzt und HOLD zurückgesetzt zurückgegeben.
Expression clipZero()

// Die Werte der Zeitreihe werden auf den angegebenen Bereich begrenzt.
// Ansonsten ist das Verhalten wie bei clipZero().
Expression clip(Number limitLow, Number limitHigh)

// Die Zeitreihe wird in Intervalle unterteilt und diese dann aggregiert. Die
// Zeitstempel für die Intervalle werden der Zeitreihe 'intervals' entnommen.
// Für jedes Intervall wird eine Funktion aufgerufen, die die Zeitreihen
// innerhalb des Intervalls zu einem Prozesswert aggregiert. Vor der
// Verarbeitung wird linear() angewendet. Für die resultierende Zeitreihe
// wird das Merkmal HOLD gesetzt und LINEAR zurückgesetzt.
Expression aggregate(Expression intervals, Function<Interval, ProcessValue> function)

// Berechnet das Minimum eines jeden Intervalls. Ansonsten ist das Verhalten
// wie bei aggregate().
Expression minimum(Expression intervals)
// Berechnet das Minimum über den gesamten Abfragezeitraum.
Expression minimum()

// Berechnet das Maximum eines jeden Intervalls. Ansonsten ist das Verhalten
// wie bei aggregate().
Expression maximum(Expression intervals)
// Berechnet das Maximum über den gesamten Abfragezeitraum.
Expression maximum()

// Berechnet den Mittelwert eines jeden Intervalls. Ansonsten ist das Verhalten
// wie bei aggregate().
Expression average(Expression intervals)
// Berechnet den Mittelwert über den gesamten Abfragezeitraum.
Expression average()

// Die Zeitreihe wird neu abgetastet. Zwischenwerte werden linear interpoliert.
// Ansonsten ist das Verhalten wie bei aggregate().
Expression resample(Expression intervals)

// Berechnet die Anzahl an Zeitreiheneinträgen je Interval. Ansonsten ist
// das Verhalten wie bei aggregate().
Expression count(Expression intervals)

// Berechnet die Anzahl an Zeitreiheneinträgen für den gesamten
// Abfragezeitraum.
Expression count()

// Gibt die Ableitung der Zeitreihe zurück. Die Zeiteinheit ist eine Stunde.
// Die zurückgegebene Zeitreihe hat einen Eintrag weniger. Das Merkmal HOLD
// wird gesetzt und LINEAR wird zurückgesetzt.
Expression differentiate()

// Gibt das Integral der Zeitreihe zurück. Die Zeiteinheit ist eine Stunde.
// Vor der Verarbeitung wird linear() angewendet. Das Merkmal LINEAR wird
// gesetzt und HOLD wird zurückgesetzt.
Expression integrate()

// Die Zeitreihe wird zeitlich verschoben. Bei einer positiven Zeitdauer
// wird ein älterer Zeitbereich angefragt, und die Zeitstempel auf den
// aktuellen Zeitbereich verschoben. (Die Kurve wird beispielweise in einer
// Trend-Darstellung nach rechts verschoben). Die Zeitdauer wird in Stunden
// angegeben.
Expression shift(Number duration)

// Heizgradtage (heating degree days) sind ein Maß zur Quantifizierung des
// Energiebedarfs für die Beheizung eines Gebäudes. Diese Funktion sollte in
// Verbindung mit der Außenlufttemperatur verwendet werden. Für jeden Tag
// wird ein Wert berechnet. Der Heizgrenzwert muss angegeben werden (z.B. 15°C).
Expression hdd(Number heatingLimit)

// Heizgradtage mit einem Heizgrenzwert von 15°C.
Expression hdd()

// Korrigiert Rückstellungen bei Zählern. Das Merkmal COUNTER wird gesetzt.
Expression counter()

// Wandelt eine Zeitreihe mit dem Merkmal HOLD (und nur dann) in eine linear
// interpolierte um. Unmittelbar vor jeder Wertänderung und am Ende des
// Abfragezeitraums wird ein zusätzlicher Eintrag mit dem Wert des
// vorhergehenden Eintrags eingefügt. Das Merkmal LINEAR wird gesetzt und
// HOLD wird zurückgesetzt.
Expression linear()

// Löscht ungültige Zeitstempel. Die Zeitstempel müssen innerhalb des
// abgefragten Zeitbereichs liegen und streng monoton aufsteigend sein. Alle
// anderen werden entfernt.
Expression sanitize()

// Setzt die Qualität von Prozesswerten außerhalb des angegebenen Bereichs
// auf BAD.
Expression validate(Number minimum, Number maximum)

// Der Zustand der Zeitreihe wird abhängig von einer zweiten Zeitreihe auf BAD
// gesetzt. Wenn die zweite Zeitreihe den Zustand BAD besitzt oder einen Wert
// größer als 0, dann wird der Zustand in der ursprünglichen Zeitreihe
// ebenfalls auf BAD gesetzt. Der Wert wird nicht verändert.
Expression badIf(Expression condition)

// Ersetzt Prozesswerte mit Qualität BAD durch den angegebenen Ersatzwert
// und Qualität QUESTIONABLE.
Expression replaceBad(Number replacement)

// Entfernt Prozesswerte mit der Qualität BAD aus der Zeitreihe.
Expression removeBad()

// Ändert die Merkmale einer Zeitreihe.
Expression characteristics(int setMask, int resetMask)

Vordefinierte Funktionen

Textformatierung

// Zahl als Zeichenkette formatieren (Ländereinstellung wird beachtet)
String formatNumber(Integer number)

// Zeichenkette in eine Zahl konvertieren (Ländereinstellung wird beachtet)
Number parseNumber(String str)

// Zeitstempel formatieren (dd.MM.yyyy HH:mm:ss)
String formatDate(Date date)

// Zeichenkette in ein Zeitstempel konvertieren
// Unterstützte Formate: dd.MM.yyyy HH:mm:ss, dd.MM.yyyy HH:mm, dd.MM.yyyy HH, dd.MM.yyyy, MM.yyyy, yyyyMMddHHmmss, yyyyMMddHHmm, yyyyMMddHH, yyyyMMdd, yyyyMM, yyyy
Date parseDate(String str)

// Zahl, Datum oder anderes Objekt als Zeichenkette ausgeben
String format(value)

// Zeitdauer in Millisekunden formatieren
String formatDuration(Long dur)

Vordefinierte berechnete Zeitreihen (ab V3.4.0)

Die Folgende Funktionen geben Expression-Objekte zurück.

CCU-Historian-Datenpunkte müssen als erstes in ein Expression-Objekt umgewandelt werden, damit sie in Berechnungen verwendet werden können:

// Ein CCU-Historian-Datenpunkt anhand seiner ID als Expression zurückgeben.
Expression dataPoint(int historianID)
Expression DP(int historianID)

// Ein Datenpunkt anhand des Interfaces, der Adresse und des Parameternamens
// (z.B. "BidCos-RF.GEQ0000000:1.BRIGHTNESS") auswählen.
Expression dataPoint(String itfAddrIdent)
Expression DP(String itfAddrIdent)

// Wenn ein DataPoint-Objekt (siehe oben) vorliegt, kann auch dieses verwendet
// werden.
Expression dataPoint(DataPoint dp)
Expression DP(DataPoint dp)

Folgende Zeitreihen sind in erste Linie für die Aggregat-Funktionen (aggregate(), minimum(), maximum(), average()) als Intervallquelle bestimmt. Der Wert der Einträge ist immer 1,0.

// Diese Zeitreihe enthält jährlich einen Eintrag am 1. Januar, 00:00 Uhr
// (Lokalzeit).
Expression yearly()

// Diese Zeitreihe enthält monatlich einen Eintrag am 1. Tag des Monats,
// 00:00 Uhr (Lokalzeit).
Expression monthly()

// Diese Zeitreihe enthält wöchentlich einen Eintrag am Sonntag, 00:00 Uhr
// (Lokalzeit).
Expression weekly()

// Diese Zeitreihe enthält täglich einen Eintrag um 00:00 Uhr (Lokalzeit).
Expression daily()

// Diese Zeitreihe enthält stündlich einen Eintrag zur vollen Stunde
// (Lokalzeit).
Expression hourly()

// Diese Zeitreihe enthält menütlich einen Eintrag zur vollen Minute
// (Lokalzeit).
Expression everyMinute()

// Diese Zeitreihe enthält Einträge zu Zeitpunkten, die durch den übergebenen
// Cron-Ausdruck definiert werden (Lokalzeit).
Expression cron(String cronText)

// Die Zeitreihe besteht immer aus einem Eintrag am Anfang und am Ende des
// Abfragezeitraums.
Expression entire()

Weitere Hilfszeitreihen:

// Die Zeitreihe besitzt den Wert firstValue bis zum Zeitpunkt stepTimestamp.
// Ab dem Zeitpunkt besitzt sie den Wert secondValue.
Expression step(Number firstValue, Number secondValue, Date stepTimestamp)

// Die Zeitreihe besitzt den Wert 0,0 bis zum Zeitpunkt edgeTimestamp.
// Ab dem Zeitpunkt besitzt sie den Wert 1,0.
Expression positiveEdge(Date edgeTimestamp)

// Die Zeitreihe besitzt den Wert 1,0 bis zum Zeitpunkt edgeTimestamp.
// Ab dem Zeitpunkt besitzt sie den Wert 0,0.
Expression negativeEdge(Date edgeTimestamp)