Skip to Content
IoT-App-EntwicklungDaten-Backend

Daten-Backend

IronFlock stellt für jedes Projekt eine private Datenbank bereit, basierend auf TimescaleDB. Ihre App definiert das Datenschema; IronFlock erstellt die Tabellen und beginnt mit der Datenerfassung, sobald ein Gerät zur App hinzugefügt wird.

Funktionsweise

  1. Definieren Sie Ihr Datenschema in .ironflock/data-template.yml.
  2. Verwenden Sie das IronFlock SDK, um Daten aus Ihrem Edge-Code zu veröffentlichen.
  3. IronFlock richtet die Datenbanktabellen automatisch in jedem Projekt ein, in dem die App installiert ist.
  4. Daten fließen von den Geräten über das Messaging-System in die Projektdatenbank.

Jedes Projekt erhält seine eigene physische Datenbank — es gibt keinen Datenaustausch zwischen Projekten.

Der Benutzer hat die volle Kontrolle über die von Ihrer App in seinem Projekt erfassten Daten. Als Entwickler haben Sie keinen Zugriff auf diese Daten.

Datenschema definieren

Erstellen Sie eine data-template.yml-Datei im .ironflock/-Verzeichnis:

data: tables: - tablename: sensordata columns: - id: tsp name: Timestamp description: Timestamp of measurement path: args[0].timestamp dataType: timestamp - id: temperature name: Temperature description: Temperature reading in Celsius path: args[0].temperature dataType: numeric - id: humidity name: Humidity description: Relative humidity percentage path: args[0].humidity dataType: numeric - id: device_id name: Device ID description: Source device identifier path: args[0].device_id dataType: string

Spaltenoptionen

FeldBeschreibung
idInterner Spaltenbezeichner (verwenden Sie tsp für Zeitstempel-Spalten)
nameBenutzerfreundlicher Spaltenname, der in Boards angezeigt wird
descriptionOptionale Beschreibung
pathPfad zum Wert im veröffentlichten Datenobjekt (z. B. args[0].temperature)
dataTypeEiner von: timestamp, numeric, string, boolean

Tabellenoptionen

Neben columns akzeptiert eine Tabelle einige optionale Schlüssel, die steuern, wie sie beschrieben wird und wie ihre Daten altern:

data: tables: - tablename: sensordata description: Umgebungsmesswerte aus der Fertigung chunkTimeInterval: 1 hour dropAfter: 30 days columns: # ...
FeldBeschreibung
tablenameName der Tabelle
descriptionOptionale Beschreibung, wird in der UI angezeigt und von KI-Agenten zum Verständnis der Tabelle genutzt
chunkTimeIntervalGröße der Zeitpartitionen, in die die Tabelle aufgeteilt wird. Standard: 7 days
dropAfterAufbewahrungsfenster — Partitionen, die älter sind, werden automatisch verworfen
downsamplePflegt eine vor-aggregierte Kopie für schnelle Diagramme über lange Zeitfenster — siehe Kontinuierliches Downsampling weiter unten
maintainLatestFlagForSpalten, die eine eindeutige Entität identifizieren — siehe Den aktuellen Zustand einer Entität verfolgen weiter unten
privateDiese Tabelle vor anderen Apps verbergen — siehe Daten mit anderen Apps teilen weiter unten

chunkTimeInterval bestimmt, wie die Zeitreihendaten auf der Festplatte partitioniert werden. Wählen Sie den Wert so, dass eine Partition ungefähr dem entspricht, was Sie in einer Abfrage lesen: Hochfrequente Daten, die im Sekundentakt erfasst werden, profitieren von kleinen Chunks (Minuten bis Stunden), langsam veränderliche Daten von großen (Wochen). Das ist lediglich die Voreinstellung der App — der Projektinhaber kann sie später in seinem eigenen Data Backend anpassen.

dropAfter macht aus der Tabelle ein gleitendes Zeitfenster. Ganze Partitionen, die älter als das angegebene Intervall sind, werden verworfen — das ist weit günstiger als das Löschen einzelner Zeilen. Der Aufräumjob läuft im Takt von dropAfter / 4, ein Datensatz kann sein Ablaufdatum also um bis zu ein Viertel des Intervalls überleben, bevor seine Partition entfernt wird. Lassen Sie dropAfter weg, um Daten unbegrenzt aufzubewahren.

Beide erwarten PostgreSQL-Intervall-Strings — 30 minutes, 1 hour, 7 days, 6 months.

Kontinuierliches Downsampling

Dashboards können die Datenbank bitten, Daten zu aggregieren — Stundendurchschnitte, Tagessummen, Anzahlen pro Maschine. Diese aus Rohdatensätzen zu berechnen, ist für einen Tag unproblematisch und für ein Jahr teuer. Ergänzen Sie eine Tabelle um downsample, und die Plattform pflegt eine kontinuierlich aktualisierte, vor-aggregierte Kopie davon und beantwortet Abfragen über lange Zeitfenster stattdessen aus dieser Kopie:

data: tables: - tablename: sensordata dropAfter: 30 days downsample: bucket: 1 minute keepFor: 2 years paths: - payload.temperature columns: # ...
FeldBeschreibung
bucketGranularität der vor-aggregierten Kopie. Standard: 1 minute
keepForWie lange die heruntergerechnete Historie aufbewahrt wird. Weglassen, um sie unbegrenzt zu behalten
pathsEinzubeziehende JSON-Feldpfade, in derselben Notation, die Dashboards verwenden

bucket ist die feinste Auflösung, aus der ein Diagramm bedient werden kann — ein Diagramm, das deutlich feinere Intervalle anfordert, liest stattdessen die Rohtabelle. Zulässig sind feste Intervalle von 1 second bis 1 day, die einen Tag gleichmäßig teilen (1 minute, 5 minutes, 1 hour). Der Standardwert 1 minute passt praktisch für jedes Dashboard; ein gröberes Intervall kostet weniger Speicher und weniger Schreibdurchsatz.

keepFor ist das, was lange Historien überhaupt erst möglich macht. Rohdatensätze verschwinden mit dropAfter, doch die heruntergerechnete Kopie hat ihre eigene Aufbewahrung: Behalten Sie Rohdaten 30 Tage und heruntergerechnete Daten 2 Jahre, und ein Board kann weiterhin zwei Jahre an Stundendurchschnitten darstellen — mit einem Bruchteil des Speichers. Setzen Sie den Wert länger als dropAfter — den umgekehrten Fall lehnt die Plattform als Fehlkonfiguration ab.

paths erweitert das Downsampling auf Werte innerhalb von JSON-Spalten. Numerische Spalten werden automatisch einbezogen; JSON-Felder müssen ausdrücklich benannt werden, da eine JSON-Spalte keine feste Menge an Schlüsseln besitzt. Nicht deklarierte Felder funktionieren in Dashboards weiterhin — sie werden dann einfach aus der Rohtabelle berechnet.

Alles Weitere läuft automatisch. Für jede numerische Spalte werden die Kennzahlen gepflegt (Durchschnitt, Summe, Minimum, Maximum, erster Wert, letzter Wert und eine Datensatzanzahl), gruppiert nach dem Entitätsschlüssel der Tabelle (maintainLatestFlagFor, sonst nach dem publizierenden Gerät). Dashboards brauchen dafür keine Konfiguration und müssen davon nichts wissen: Ein Widget fragt wie gewohnt ab, und die Plattform entscheidet pro Abfrage, ob die vor-aggregierte Kopie sie beantworten kann — und fällt transparent auf die Rohtabelle zurück, wenn nicht, etwa wenn ein Filter eine Spalte anspricht, nach der die Kopie nicht gruppiert.

Schemaänderungen bauen die Kopie neu auf. Das Hinzufügen, Entfernen oder Umtypisieren einer Spalte einer heruntergerechneten Tabelle — oder das Ändern des downsample-Blocks selbst — baut die vor-aggregierte Kopie aus der Rohtabelle neu auf. Alles, was älter als dropAfter ist, lässt sich nicht rekonstruieren und geht verloren. Richten Sie den Block nach Möglichkeit gleich zusammen mit der Tabelle ein, und behandeln Sie spätere Schemaänderungen an langlebigen Tabellen als bewusste Entscheidung.

Daten aus dem Edge-Code veröffentlichen

Verwenden Sie das IronFlock SDK, um Daten aus Ihrer App zu senden:

from ironflock import IronFlock flock = IronFlock() flock.publish_to_table("sensordata", { "timestamp": "2025-01-15T10:30:00Z", "temperature": 23.5, "humidity": 62.1, "device_id": "sensor-001" })

Für hochfrequente Daten senden Sie mehrere Zeilen in einer einzigen Nachricht, anstatt einen Roundtrip pro Zeile auszuführen — mit publish_rows_to_table / publishRowsToTable (Fire-and-Forget) oder append_rows_to_table / appendRowsToTable (gibt das Ergebnis des Inserts zurück). Jeder Stapel wird atomar eingefügt — Alles-oder-nichts. Weitere Details finden Sie in der SDK-Referenz.

Transformationstabellen

Sie können SQL-Transformationen definieren, die Ihre Rohdaten automatisch aggregieren oder verarbeiten:

data: tables: - tablename: sensordata columns: # ... raw data columns ... transforms: - tablename: hourly_averages materialize: true schedule: "0 * * * *" sql: > SELECT time_bucket('1 hour', tsp) AS hour, avg(temperature) AS avg_temp, avg(humidity) AS avg_humidity FROM sensordata GROUP BY hour columns: - id: hour name: Hour dataType: timestamp - id: avg_temp name: Average Temperature dataType: numeric - id: avg_humidity name: Average Humidity dataType: numeric
FeldBeschreibung
tablenameName der abgeleiteten Tabelle
materializeWenn true, werden die Ergebnisse als Tabelle persistiert
scheduleCron-Ausdruck für den Ausführungszeitpunkt der Transformation
sqlSQL-Abfrage, die die Transformation berechnet
columnsSpaltendefinitionen für die Ausgabe

Transformationstabellen sind in Boards und über das SDK zugänglich, genau wie reguläre Tabellen.

Den aktuellen Zustand einer Entität verfolgen

Für Tabellen, die den aktuellen Zustand realer Objekte darstellen — Maschinen, Anlagen, Fertigungsaufträge — unterstützt IronFlock ein Muster namens Latest-State-Tracking.

Anstatt eine Zeile bei einer Änderung zu überschreiben, fügen Sie immer eine neue Zeile hinzu. Sie deklarieren, welche Spalten eine eindeutige Entität identifizieren, und IronFlock leitet bei jedem Lesezugriff auf die Tabelle die aktuellste Zeile pro Entität ab. So erhalten Sie einen vollständigen Verlauf aller Änderungen und können gleichzeitig einfach nur den aktuellen Zustand abfragen.

Aktivieren Sie dies auf einer Tabelle mit maintainLatestFlagFor:

- tablename: machineform maintainLatestFlagFor: ['machinename'] columns: - id: tsp dataType: timestamp - id: machinename dataType: string - id: machinetype dataType: string - id: active dataType: boolean - id: description dataType: string

maintainLatestFlagFor nimmt eine Liste von Spalten entgegen, die zusammen eine eindeutige Entität identifizieren. In die Zeile selbst wird dabei nichts geschrieben: IronFlock indiziert die Tabelle nach diesem Entitätsschlüssel plus Zeitstempel und wählt zur Abfragezeit die neueste Zeile pro Entität aus. Eine Zeile, die verspätet oder außer der Reihe eintrifft, kann daher niemals eine veraltete Markierung hinterlassen.

Um nur aktuelle Maschinenzustände abzufragen:

SELECT DISTINCT ON (machinename) * FROM machineform ORDER BY machinename, tsp DESC

Um den vollständigen Verlauf einer bestimmten Maschine anzuzeigen:

SELECT * FROM machineform WHERE machinename = 'Assembly-Line-01' ORDER BY tsp

Diese Abfrage schreiben Sie nur selten von Hand. Widgets auf einem Board, die mit dieser Tabelle verbunden sind, haben in ihren Filtereinstellungen einen latest-Schalter, sodass Benutzer immer aktuelle Werte sehen, ohne zusätzlichen Aufwand. Aus dem SDK fordern Sie denselben Modus an, indem Sie {"latest": true} zu filterAnd hinzufügen — siehe getHistory.

Migration von latest_flag: Frühere Versionen von IronFlock speicherten eine physische boolesche Spalte namens latest_flag. Diese Spalte existiert nicht mehr — der aktuelle Zustand wird stattdessen in SQL abgeleitet, was ihn auch dann korrekt hält, wenn Zeilen außer der Reihe eintreffen. Bestehende Boards und SDK-Aufrufe, die nach latest_flag = true filtern, funktionieren weiterhin: IronFlock erkennt sie und wendet den Latest-State-Modus an. Neuer Code sollte den latest-Schalter oder den Filtereintrag {"latest": true} verwenden.

Soft Delete von Datensätzen

Das Append-Only-Modell von IronFlock bedeutet, dass Datensätze niemals physisch gelöscht werden. Verwenden Sie stattdessen eine boolesche Spalte deleted, um einen Datensatz als entfernt zu markieren. Dies bewahrt den vollständigen Audit-Trail und blendet gleichzeitig gelöschte Datensätze in Dashboards aus.

Fügen Sie jeder Entitätstabelle eine deleted-Spalte hinzu:

- id: deleted name: Deleted dataType: boolean

Wenn ein Benutzer einen Datensatz löscht (z. B. über ein Formular auf dem Board), veröffentlicht Ihre App eine neue Zeile für diese Entität mit deleted: true. In Kombination mit maintainLatestFlagFor wird diese neue Zeile zum aktuellen Zustand.

Um nur aktive (nicht gelöschte) aktuelle Datensätze abzufragen:

SELECT * FROM ( SELECT DISTINCT ON (machinename) * FROM machineform ORDER BY machinename, tsp DESC ) latest WHERE deleted IS NULL OR deleted = false

Die deleted-Prüfung läuft, nachdem die neueste Zeile pro Maschine ausgewählt wurde. Diese Reihenfolge ist entscheidend: Würden die gelöschten Zeilen zuerst herausgefiltert, würde die vorherige, nicht gelöschte Zeile wieder als aktueller Zustand auftauchen.

Board-Widgets und das SDK wenden dieselbe Reihenfolge automatisch an — kombinieren Sie den latest-Schalter (oder {"latest": true}) mit einem deleted-Filter, und Sie erhalten genau dieses Verhalten. Gelöschte Datensätze verschwinden sofort nach dem Absenden des Formulars aus dem Dashboard, verbleiben aber für Verlaufs- und Prüfzwecke in der Datenbank.

Daten mit anderen Apps teilen

Ihr Data Backend gehört allein Ihrer App: Keine andere im Projekt installierte App sieht Ihre Tabellen. Zwei optionale Schlüssel in data-template.yml ändern das.

Um Daten einer anderen App zu lesen, listen Sie die gewünschten Apps in einem Abschnitt consumes: auf oberster Ebene auf — neben data:, nicht darin:

consumes: - app: machine-monitor reason: "Berechnet die OEE aus den Maschinenzustands- und Zählerströmen des Monitors" data: tables: - tablename: oee_results columns: # ... die eigenen Tabellen Ihrer App, wie gewohnt

app ist der technische Name der bereitstellenden App oder "*" (Anführungszeichen erforderlich) für alle Apps im Projekt. reason wird dem Nutzer im Zustimmungsdialog angezeigt — die Deklaration allein gewährt nichts, solange er nicht zustimmt.

Um einzelne Tabellen zurückzuhalten, markieren Sie sie mit private: true. Alles, was Sie definieren, ist standardmäßig teilbar; eine private Tabelle oder Transformation taucht im Katalog, den andere Apps sehen, überhaupt nicht auf.

data: tables: - tablename: measurements # geteilt (Standard) columns: [ ... ] - tablename: calibration_state # intern — für andere Apps nie sichtbar private: true columns: [ ... ]

Der Zugriff ist ausschließlich lesend, wird vom Nutzer pro Projekt erteilt und ist jederzeit widerrufbar. Das vollständige Modell und die SDK-Aufrufe zum Lesen von Verlauf und Live-Streams einer bereitstellenden App finden Sie unter Daten anderer Apps nutzen.

Last updated on