Zum Hauptinhalt springen

Metadaten im Plattform-Adapter

Liest ein Workflow im hetida designer ein Signal oder eine Signalgruppe über den Plattform-Adapter, erhält er neben den Datenpunkten auch deren Metadaten: Name, Einheit, Datentyp, die dynamischen Eigenschaften einschließlich der geerbten und die Kommentare im abgefragten Zeitraum. Diese Seite beschreibt, wie diese Metadaten aufgebaut sind, wo ein Feld der Plattform-Oberfläche darin steht – und welche Felder nicht übertragen werden.

Die Struktur folgt der Metadaten-Konvention des hetida designers (attached_metadata.md). Die Plattform verwendet ausschließlich diese Konvention; das frühere Format unter single_metric_metadata.structured_metadata wird nicht mehr geliefert.

Übertragung​

Der Adapter liefert die Metadaten im HTTP-Header Data-Attributes der Leseantwort, als Base64-kodiertes JSON. Der hetida designer dekodiert den Header und legt den Inhalt in attrs der gelesenen Daten ab:

Datentyp im WorkflowAdapter-EndpunktInhalt des HeadersIm Komponentencode
SERIES (Signal)GET /api/adapter/timeseries?id=…ein Objekt je Signal-ID, Schlüssel ist die Signal-IDseries.attrs
MULTITSFRAME (Signalgruppe)GET /api/adapter/multitsframe?id=…ein Objekt für die ganze Gruppeframe.attrs

In beiden Fällen hat ein Metadaten-Objekt dieselben zwei Teile:

attrs
├── dataset_metadata Angaben zum Datensatz: Schlüssel, Zeitraum, ggf. Signalgruppe
└── metrics[] je Signal ein Eintrag
├── id, external_id, name, display_name, description
├── default_aggregation_method
├── inherited dynamische Eigenschaften (geerbt und eigene)
├── comments[] Kommentare im Zeitraum, mit replies[]
└── value_dimensions[] genau ein Eintrag: column = "value"

Felder ohne Wert stehen mit null im JSON. Eine Komponente sollte deshalb nicht nur auf das Vorhandensein eines Schlüssels prüfen, sondern auch auf None.

Signal (SERIES)​

Der Header einer SERIES-Abfrage enthält ein Objekt je angefragtem Signal; der hetida designer gibt jeder Series den Eintrag ihrer timeseriesId.

{
"dataset_metadata": {
"metric_key": "id",
"single_metric": "0190c3a2-…",
"queried_metrics": ["0190c3a2-…"],
"signal_group": null,
"ref_interval_start_timestamp": "2026-10-01T00:00:00Z",
"ref_interval_end_timestamp": "2026-10-02T00:00:00Z",
"ref_interval_type": "closed"
},
"metrics": [
{
"id": "0190c3a2-…",
"external_id": "wasserwerk.pumpe1.druck",
"name": "Druck Pumpe 1",
"display_name": null,
"description": "Ausgangsdruck hinter Pumpe 1",
"default_aggregation_method": "AVG",
"inherited": { "grenzwert_max": 6.5, "standort": "Nord" },
"comments": [
{
"id": "…",
"start_date": "2026-10-01T08:00:00Z",
"end_date": "2026-10-01T09:30:00Z",
"created_at": "2026-10-01T10:12:00Z",
"edited_at": null,
"content": "Wartung, Werte nicht repräsentativ",
"username": "mmuster",
"tags": ["wartung"],
"replies": []
}
],
"value_dimensions": [
{
"column": "value",
"name": "Druck Pumpe 1",
"unit": "bar",
"measurement": "Druck",
"value_data_type": "float"
}
]
}
]
}

Die Felder von dataset_metadata bei einer Leseabfrage:

FeldInhalt
metric_keyimmer id: Ein Eintrag in metrics wird über sein Feld id (die Signal-ID) gefunden.
single_metricSignal-ID des einen Signals der Series.
queried_metricsangefragte Signal-IDs, auch ohne Daten im Zeitraum.
signal_groupbei SERIES immer null.
ref_interval_start_timestamp, ref_interval_end_timestampder abgefragte Zeitraum (from, to der Abfrage).
ref_interval_typederzeit immer closed. Achtung: Gelesen wird tatsächlich [from, to), ein Punkt genau bei to ist nicht enthalten. Der Header wird auf right_open umgestellt (HET-2021).

Die übrigen Felder von dataset_metadata (invalidate_dataset, invalidation_interval_* usw.) setzt der Adapter beim Lesen nicht; sie sind für das Schreiben gedacht.

Signalgruppe (MULTITSFRAME)​

Eine Signalgruppe wird als MULTITSFRAME mit den Spalten timestamp, metric und value geliefert. Die Spalte metric enthält die Signal-ID, nicht den Namen: Namen sind nicht eindeutig, und mehrere Signale können dieselbe Eingangszeitreihe nutzen. Über die Signal-ID findet eine Komponente den passenden Eintrag in metrics.

Gegenüber der Series unterscheidet sich der Header so:

FeldInhalt
dataset_metadata.single_metricnull, der Frame enthält mehrere Signale.
dataset_metadata.queried_metricsSignal-IDs aller gelieferten Mitglieder.
dataset_metadata.signal_group{ "id", "name", "description" } der Signalgruppe.
metrics[]ein Eintrag je Mitglied, aufgebaut wie bei der Series (Eigenschaften mit Vererbung, Kommentare).
metrics[].display_namedie Bezeichnung des Signals in dieser Gruppe.
metrics[].default_aggregation_methoddie Aggregationsmethode, die in der Gruppe für das Mitglied eingestellt ist – nicht die des Signals.

Zu beachten:

  • Die Werte sind Rohdaten. Weder Aggregationsintervall noch Aggregationsmethode der Gruppe werden angewendet; default_aggregation_method sagt nur, wie die Plattform das Signal in Diagrammen und Exporten zusammenfasst.
  • Ein Mitglied ohne Eingangszeitreihe oder ein gelöschtes Signal wird übersprungen, ohne dass der Abruf scheitert. Es fehlt dann in den Daten und in metrics.
  • Nur eigenständige Signalgruppen aus dem Explorer stehen als Quelle zur Verfügung, Gruppen innerhalb eines Formulars nicht.
  • Eine Signalgruppe ist keine Senke: Ein Schreibversuch wird mit 501 Not Implemented abgelehnt.

Felder eines Eintrags in metrics​

PfadInhalt
idSignal-ID (UUID).
external_idExterne ID der Eingangszeitreihe, null bei einem Signal ohne Eingangszeitreihe.
nameName des Signals.
display_nameBezeichnung des Mitglieds in der Signalgruppe; bei SERIES null.
descriptionBeschreibung des Signals.
default_aggregation_methodAVG, MEDIAN, SUM, MIN oder MAX; null bei Keine Aggregation.
inheritedObjekt { Schlüssel: Wert } der wirksamen dynamischen Eigenschaften, siehe unten.
comments[]Kommentare, siehe unten.
value_dimensions[0].columnimmer value – die Wertspalte der Daten.
value_dimensions[0].nameName des Signals (wie name).
value_dimensions[0].unitEinheit.
value_dimensions[0].measurementMessgröße.
value_dimensions[0].value_data_typefloat, integer oder boolean aus dem Wertetyp.

Einheit, Messgröße und Datentyp stehen nur in value_dimensions, nicht direkt am Eintrag. Die Felder display_name, short_display_name und description einer Wertdimension sieht die Konvention vor, die Plattform belegt sie nicht.

Dynamische Eigenschaften (inherited)​

inherited enthält die dynamischen Eigenschaften, die für das Signal wirksam sind – trotz des Namens nicht nur die geerbten:

  • Schlüssel ist die Bezeichnung (label) der Eigenschaft, Wert ihr Wert.
  • Eigenschaften der Assets oberhalb des Signals werden geerbt; bei gleichem Schlüssel gewinnt das nächstgelegene Asset.
  • Eine eigene Eigenschaft des Signals überschreibt eine geerbte. Eine eigene Eigenschaft ohne Wert überschreibt nicht, dann bleibt der geerbte Wert wirksam.
  • Ein Schlüssel ohne wirksamen Wert fehlt im Objekt.
  • Woher ein Wert stammt (Signal oder welches Asset), wird nicht übertragen.

Dieselbe Auflösung liefert der Adapter auch über seine Metadaten-Endpunkte für Assets und Signale, eine Komponente erhält also auf beiden Wegen dieselben Werte.

Kommentare (comments)​

Übertragen werden die Kommentare des Signals, deren Zeitraum den abgefragten Zeitraum überschneidet – der zuletzt erstellte zuerst, jeweils mit seinen Antworten.

PfadInhalt
comments[].idID des Kommentars.
comments[].start_date, end_dateZeitraum, auf den sich der Kommentar bezieht.
comments[].created_at, edited_atErstellt, zuletzt bearbeitet (null, wenn nie bearbeitet).
comments[].contentText.
comments[].usernameBenutzername des Verfassers.
comments[].tagsListe der Tags.
comments[].replies[]Antworten mit id, content, username, created_at, edited_at, älteste zuerst.

Wird mit einem Betrachtungszeitpunkt (queryDate) gelesen, enthält der Header nur Kommentare und Antworten, die zu diesem Zeitpunkt schon existierten. Eine Historie der Kommentare gibt es nicht: Ein bearbeiteter Kommentar kommt mit seinem aktuellen Text, ein gelöschter gar nicht.

Zuordnung zur Oberfläche​

Signal​

Oberfläche (Signal-Maske)Feld in den Metadaten
Allgemeines › Namemetrics[].name und metrics[].value_dimensions[0].name
Allgemeines › Beschreibungmetrics[].description
Eigenschaften (dynamische Eigenschaften)metrics[].inherited, zusammen mit den geerbten
Metadata › Wertetypmetrics[].value_dimensions[0].value_data_type
Metadata › Messgrößemetrics[].value_dimensions[0].measurement
Metadata › Einheitmetrics[].value_dimensions[0].unit
Aggregation › Standard-Aggregationsmethodemetrics[].default_aggregation_method (nur bei SERIES)
Eingangszeitreihen › Externe IDmetrics[].external_id
ID des Signals (nicht in der Maske)metrics[].id, single_metric, queried_metrics
Kommentare am Signalmetrics[].comments

Nicht übertragen werden:

OberflächeHinweis
Anzeigeeinstellungen (Zeitbereich, Aktualisierungsintervall)nur für die Darstellung in der Plattform.
Ausgänge (Berechnungen und ihre Namen)Die Ausgänge eines Signals sind eigene Werte der Plattform und gehören nicht zur Zeitreihe.
OPC-UA-Abruf und VerdichtungKonfiguration der Datenerfassung; welcher Weg die Daten lieferte, steht nicht in den Metadaten.
Position im Explorer (übergeordnete Assets, Pfad)Nur die Eigenschaften der Assets kommen über inherited an, nicht die Assets selbst.
Herkunft einer Eigenschaftinherited enthält nur den wirksamen Wert.
Berechtigungen, Änderungshistorie–

Signalgruppe​

Oberfläche (Signalgruppen-Maske)Feld in den Metadaten
Allgemeines › Namedataset_metadata.signal_group.name
Allgemeines › Beschreibungdataset_metadata.signal_group.description
ID der Signalgruppedataset_metadata.signal_group.id
Signale auswählen (Mitglieder)ein Eintrag je Mitglied in metrics, IDs in queried_metrics
Bezeichnung eines Signals in der Gruppemetrics[].display_name
Aggregationsmethode eines Signals in der Gruppemetrics[].default_aggregation_method
Felder der einzelnen Signalewie bei der Series, siehe oben

Nicht übertragen wird das Aggregationsintervall der Gruppe; der Frame enthält die Rohdaten. Eine Komponente sollte Mitglieder über die Signal-ID ansprechen, nicht über ihre Position in metrics – ein übersprungenes Mitglied verschiebt die Positionen.

Schreiben​

Schreibt ein Workflow eine Series in ein Signal (POST /api/adapter/timeseries?timeseriesId=…), schickt der hetida designer die attrs der Ausgabe wieder als Data-Attributes-Header mit. Die Plattform wertet daraus nur Felder in dataset_metadata aus, die festlegen, ob vorhandene Werte vor dem Schreiben ungültig gemacht werden:

FeldWirkung
invalidate_datasettrue: Vorhandene Werte im Intervall werden ungültig gemacht, bevor die neuen geschrieben werden. Ohne das Feld werden die Werte nur hinzugefügt.
only_invalidatetrue: Es wird nur ungültig gemacht, keine neuen Werte geschrieben. Wirkt nur zusammen mit invalidate_dataset; allein gesetzt werden die Werte geschrieben.
invalidation_interval_start, invalidation_interval_endIntervall des Ungültigmachens.
ref_interval_start_timestamp, ref_interval_end_timestampErsatz, wenn kein invalidation_interval_* gesetzt ist. Fehlen beide, gilt der Bereich vom ersten bis zum letzten geschriebenen Zeitpunkt.
ref_dataset_discretetrue: Statt eines Intervalls werden nur die Zeitpunkte der neuen Werte ungültig gemacht. Wird ignoriert, sobald ein invalidation_interval_* gesetzt ist.

Das Intervall wird als links geschlossen, rechts offen behandelt; ref_interval_type und invalidation_interval_type werden nicht ausgewertet. Ebenfalls nicht ausgewertet werden delete_invalidated, invalidation_timestamp und new_data_invalidation_date. Alle übrigen Inhalte des Headers, also auch metrics, ändern am Signal nichts – Name, Einheit und Eigenschaften lassen sich über den Adapter nicht schreiben.

Ein Header, der weder gültiges Base64 noch gültiges JSON ist, wird mit 400 Bad Request abgelehnt.

Zeigt die Plattform die Ausgabe eines Workflows direkt an, etwa in einem Dashboard, liest sie Name und Einheit der Series aus metrics[] (Eintrag zu single_metric, sonst der einzige Eintrag) und dort aus der Wertdimension value.