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
- Definieren Sie Ihr Datenschema in
.ironflock/data-template.yml. - Verwenden Sie das IronFlock SDK, um Daten aus Ihrem Edge-Code zu veröffentlichen.
- IronFlock richtet die Datenbanktabellen automatisch in jedem Projekt ein, in dem die App installiert ist.
- 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: stringSpaltenoptionen
| Feld | Beschreibung |
|---|---|
id | Interner Spaltenbezeichner (verwenden Sie tsp für Zeitstempel-Spalten) |
name | Benutzerfreundlicher Spaltenname, der in Boards angezeigt wird |
description | Optionale Beschreibung |
path | Pfad zum Wert im veröffentlichten Datenobjekt (z. B. args[0].temperature) |
dataType | Einer 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:
# ...| Feld | Beschreibung |
|---|---|
tablename | Name der Tabelle |
description | Optionale Beschreibung, wird in der UI angezeigt und von KI-Agenten zum Verständnis der Tabelle genutzt |
chunkTimeInterval | Größe der Zeitpartitionen, in die die Tabelle aufgeteilt wird. Standard: 7 days |
dropAfter | Aufbewahrungsfenster — Partitionen, die älter sind, werden automatisch verworfen |
downsample | Pflegt eine vor-aggregierte Kopie für schnelle Diagramme über lange Zeitfenster — siehe Kontinuierliches Downsampling weiter unten |
maintainLatestFlagFor | Spalten, die eine eindeutige Entität identifizieren — siehe Den aktuellen Zustand einer Entität verfolgen weiter unten |
private | Diese 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:
# ...| Feld | Beschreibung |
|---|---|
bucket | Granularität der vor-aggregierten Kopie. Standard: 1 minute |
keepFor | Wie lange die heruntergerechnete Historie aufbewahrt wird. Weglassen, um sie unbegrenzt zu behalten |
paths | Einzubeziehende 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 alsdropAfterist, 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:
Python
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| Feld | Beschreibung |
|---|---|
tablename | Name der abgeleiteten Tabelle |
materialize | Wenn true, werden die Ergebnisse als Tabelle persistiert |
schedule | Cron-Ausdruck für den Ausführungszeitpunkt der Transformation |
sql | SQL-Abfrage, die die Transformation berechnet |
columns | Spaltendefinitionen 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: stringmaintainLatestFlagFor 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 DESCUm den vollständigen Verlauf einer bestimmten Maschine anzuzeigen:
SELECT * FROM machineform WHERE machinename = 'Assembly-Line-01' ORDER BY tspDiese 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 namenslatest_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 nachlatest_flag = truefiltern, 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: booleanWenn 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 = falseDie 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 gewohntapp 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.