Skip to Content

Backend danych

IronFlock tworzy prywatną bazę danych dla każdego projektu, obsługiwaną przez TimescaleDB. Twoja aplikacja definiuje schemat danych; IronFlock tworzy tabele i zaczyna zbierać dane w momencie gdy urządzenie jest dodawane do aplikacji.

Jak to działa

  1. Zdefiniuj swój schemat danych w .ironflock/data-template.yml.
  2. Użyj SDK IronFlock do publikowania danych z kodu brzegowego.
  3. IronFlock automatycznie konfiguruje tabele bazy danych w każdym projekcie gdzie aplikacja jest zainstalowana.
  4. Dane przepływają z urządzeń przez system komunikatów do bazy danych projektu.

Każdy projekt otrzymuje własną fizyczną bazę danych — nie ma współdzielenia danych między projektami.

Użytkownik ma pełną kontrolę nad danymi zbieranymi przez twoją aplikację w swoim projekcie. Jako deweloper nie masz dostępu do tych danych.

Definiowanie schematu danych

Utwórz plik data-template.yml w katalogu .ironflock/:

data: tables: - tablename: sensordata columns: - id: tsp name: Timestamp description: Czas pomiaru path: args[0].timestamp dataType: timestamp - id: temperature name: Temperature description: Odczyt temperatury w Celsjuszach path: args[0].temperature dataType: numeric - id: humidity name: Humidity description: Procentowa wilgotność względna path: args[0].humidity dataType: numeric - id: device_id name: Device ID description: Identyfikator urządzenia źródłowego path: args[0].device_id dataType: string

Opcje kolumn

PoleOpis
idWewnętrzny identyfikator kolumny (użyj tsp dla kolumn znacznika czasu)
nameCzytelna dla człowieka nazwa kolumny wyświetlana w panelach
descriptionOpcjonalny opis
pathŚcieżka do wartości w opublikowanym obiekcie danych (np. args[0].temperature)
dataTypeJeden z: timestamp, numeric, string, boolean

Opcje tabel

Poza columns tabela przyjmuje kilka opcjonalnych kluczy, które określają, jak jest opisana i jak starzeją się jej dane:

data: tables: - tablename: sensordata description: Odczyty środowiskowe z hali produkcyjnej chunkTimeInterval: 1 hour dropAfter: 30 days columns: # ...
PoleOpis
tablenameNazwa tabeli
descriptionOpcjonalny opis, wyświetlany w interfejsie i wykorzystywany przez agentów AI do zrozumienia tabeli
chunkTimeIntervalRozmiar partycji czasowych, na które dzielona jest tabela. Domyślnie 7 days
dropAfterOkno retencji — starsze partycje są automatycznie usuwane
downsampleUtrzymuje wstępnie zagregowaną kopię na potrzeby szybkich wykresów z długim oknem czasowym — zobacz Ciągły downsampling poniżej
maintainLatestFlagForKolumny identyfikujące unikalną encję — zobacz Śledzenie aktualnego stanu encji poniżej
privateUkrywa tę tabelę przed innymi aplikacjami — zobacz Udostępnianie danych innym aplikacjom poniżej

chunkTimeInterval decyduje o tym, jak dane szeregów czasowych są partycjonowane na dysku. Dobierz go tak, aby jedna partycja odpowiadała mniej więcej temu, co odczytujesz w jednym zapytaniu: dane o wysokiej częstotliwości zbierane co sekundę zyskują na małych chunkach (minuty do godzin), a dane zmieniające się powoli — na dużych (tygodnie). To jedynie wartość domyślna aplikacji — właściciel projektu może ją później zmienić we własnym data backendzie.

dropAfter zamienia tabelę w przesuwane okno. Usuwane są całe partycje starsze niż podany interwał, co jest znacznie tańsze niż kasowanie pojedynczych wierszy. Zadanie czyszczące działa w cyklu dropAfter / 4, więc rekord może przetrwać swój termin ważności nawet o jedną czwartą interwału, zanim jego partycja zostanie usunięta. Pomiń dropAfter, aby przechowywać dane bezterminowo.

Oba przyjmują łańcuchy interwałów PostgreSQL — 30 minutes, 1 hour, 7 days, 6 months.

Ciągły downsampling

Panele mogą poprosić bazę danych o agregację danych — średnie godzinowe, sumy dzienne, liczbę rekordów na maszynę. Obliczanie ich z surowych rekordów jest w porządku dla jednej doby, ale kosztowne dla roku. Dodaj downsample do tabeli, a platforma będzie utrzymywać jej stale aktualizowaną, wstępnie zagregowaną kopię i odpowiadać z niej na zapytania z długim oknem czasowym:

data: tables: - tablename: sensordata dropAfter: 30 days downsample: bucket: 1 minute keepFor: 2 years paths: - payload.temperature columns: # ...
PoleOpis
bucketZiarnistość wstępnie zagregowanej kopii. Domyślnie 1 minute
keepForJak długo przechowywać historię po downsamplingu. Pomiń, aby przechowywać ją bezterminowo
pathsŚcieżki pól JSON do uwzględnienia, w tej samej notacji, której używają panele

bucket to najdrobniejsza rozdzielczość, z jakiej może zostać obsłużony wykres — wykres proszący o przedziały znacznie drobniejsze niż ta wartość odczyta zamiast tego tabelę surowych danych. Przyjmuje interwały o stałej szerokości od 1 second do 1 day, które dzielą dobę bez reszty (1 minute, 5 minutes, 1 hour). Domyślna wartość 1 minute odpowiada praktycznie każdemu panelowi; grubszy przedział kosztuje mniej miejsca na dysku i mniejszą przepustowość zapisu.

keepFor jest tym, co w ogóle umożliwia długie historie. Surowe rekordy znikają wraz z dropAfter, ale kopia po downsamplingu ma własną retencję: przechowuj surowe dane przez 30 dni, a dane po downsamplingu przez 2 lata, a panel nadal narysuje dwa lata średnich godzinowych, zajmując ułamek miejsca. Ustaw ją na dłuższą niż dropAfter — sytuację odwrotną platforma odrzuca jako błędną konfigurację.

paths rozszerza downsampling na wartości wewnątrz kolumn JSON. Kolumny liczbowe są uwzględniane automatycznie; pola JSON trzeba wskazać wprost, ponieważ kolumna JSON nie ma stałego zestawu kluczy. Niezadeklarowane pola nadal działają w panelach — są po prostu obliczane z tabeli surowych danych.

Cała reszta dzieje się automatycznie. Dla każdej kolumny liczbowej utrzymywane są statystyki (średnia, suma, minimum, maksimum, pierwsza i ostatnia wartość oraz liczba rekordów), pogrupowane według klucza encji tabeli (maintainLatestFlagFor albo publikujące urządzenie). Panele nie wymagają żadnej konfiguracji ani nawet wiedzy o tym mechanizmie: widżet odpytuje bazę jak zwykle, a platforma decyduje przy każdym zapytaniu, czy wstępnie zagregowana kopia jest w stanie na nie odpowiedzieć — przezroczyście wracając do tabeli surowych danych, gdy nie jest, na przykład gdy filtr odwołuje się do kolumny, według której kopia nie grupuje.

Zmiany schematu przebudowują kopię. Dodanie, usunięcie lub zmiana typu kolumny w tabeli objętej downsamplingiem — albo edycja samego bloku downsample — powoduje przebudowanie wstępnie zagregowanej kopii z tabeli surowych danych. Wszystkiego, co jest starsze niż dropAfter, nie da się odtworzyć i zostaje bezpowrotnie utracone. Tam, gdzie to możliwe, skonfiguruj ten blok razem z tabelą, a późniejsze zmiany schematu w długo żyjących tabelach traktuj jako świadomą decyzję.

Publikowanie danych z kodu brzegowego

Użyj SDK IronFlock, aby wysyłać dane z twojej aplikacji:

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" })

W przypadku danych o wysokiej częstotliwości wysyłaj wiele wierszy w jednym komunikacie zamiast jednego cyklu na wiersz, używając publish_rows_to_table / publishRowsToTable (wyślij i zapomnij) lub append_rows_to_table / appendRowsToTable (zwraca wynik wstawienia). Każda partia jest wstawiana atomowo — wszystko albo nic. Szczegóły znajdziesz w dokumentacji SDK.

Tabele transformacyjne

Możesz definiować transformacje SQL, które automatycznie agregują lub przetwarzają surowe dane:

data: tables: - tablename: sensordata columns: # ... kolumny surowych danych ... 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
PoleOpis
tablenameNazwa tabeli pochodnej
materializeJeśli true, wyniki są utrwalane jako tabela
scheduleWyrażenie cron określające kiedy uruchamiać transformację
sqlZapytanie SQL obliczające transformację
columnsDefinicje kolumn dla danych wyjściowych

Tabele transformacyjne są dostępne w panelach i przez SDK, tak jak zwykłe tabele.

Śledzenie aktualnego stanu encji

Dla tabel reprezentujących aktualny stan rzeczywistych encji — maszyn, zasobów, zleceń produkcyjnych — IronFlock obsługuje wzorzec zwany śledzeniem najnowszego stanu.

Zamiast nadpisywania wiersza gdy coś się zmienia, zawsze dołączasz nowy wiersz. Deklarujesz, które kolumny identyfikują unikalną encję, a IronFlock wyznacza najnowszy wiersz dla każdej encji przy każdym odczycie tabeli. Daje to pełną historię każdej zmiany, jednocześnie ułatwiając zapytanie tylko o aktualny stan.

Włącz to na tabeli za pomocą 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 przyjmuje listę kolumn, które razem identyfikują unikalną encję. Do samego wiersza nie jest przy tym nic zapisywane: IronFlock indeksuje tabelę według tego klucza encji oraz znacznika czasu i przy wykonywaniu zapytania wybiera najnowszy wiersz dla każdej encji. Wiersz, który przyjdzie z opóźnieniem lub poza kolejnością, nigdy nie może więc pozostawić po sobie nieaktualnego oznaczenia.

Aby zapytać tylko o aktualne stany maszyn:

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

Aby wyświetlić pełną historię konkretnej maszyny:

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

Rzadko piszesz to zapytanie ręcznie. Widgety na panelu połączone z tą tabelą mają w ustawieniach filtrów przełącznik latest, dzięki czemu użytkownicy zawsze widzą aktualne wartości bez dodatkowej pracy. Z poziomu SDK zażądasz tego samego trybu, dodając {"latest": true} do filterAnd — zobacz getHistory.

Migracja z latest_flag: wcześniejsze wersje IronFlock przechowywały fizyczną kolumnę boolean o nazwie latest_flag. Ta kolumna już nie istnieje — aktualny stan jest zamiast tego wyznaczany w SQL, co utrzymuje jego poprawność, gdy wiersze przychodzą poza kolejnością. Istniejące panele i wywołania SDK, które filtrują po latest_flag = true, działają nadal: IronFlock je rozpoznaje i stosuje tryb najnowszego stanu. Nowy kod powinien używać przełącznika latest lub wpisu filtra {"latest": true}.

Miękkie usuwanie rekordów

Model tylko-do-dołączania IronFlock oznacza, że rekordy nigdy nie są fizycznie usuwane. Zamiast tego użyj kolumny boolean deleted, aby oznaczyć rekord jako usunięty. Zachowuje to pełny ślad audytu, jednocześnie ukrywając usunięte rekordy przed panelami.

Dodaj kolumnę deleted do dowolnej tabeli encji:

- id: deleted name: Deleted dataType: boolean

Aby zapytać tylko o aktywne (nie usunięte) aktualne rekordy:

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

Sprawdzenie kolumny deleted jest wykonywane po wybraniu najnowszego wiersza dla każdej maszyny. Ta kolejność ma znaczenie: odfiltrowanie usuniętych wierszy w pierwszej kolejności sprawiłoby, że poprzedni, nieusunięty wiersz pojawiłby się ponownie jako aktualny stan.

Widgety na panelach oraz SDK stosują tę samą kolejność automatycznie — połącz przełącznik latest (lub {"latest": true}) z filtrem na kolumnie deleted, a otrzymasz dokładnie takie zachowanie. Usunięte rekordy znikają z panelu natychmiast po wysłaniu formularza, ale pozostają w bazie danych na potrzeby historii i audytu.

Udostępnianie danych innym aplikacjom

Twój data backend należy wyłącznie do Twojej aplikacji: żadna inna aplikacja zainstalowana w projekcie nie widzi Twoich tabel. Zmieniają to dwa opcjonalne klucze w data-template.yml.

Aby czytać dane innej aplikacji, wypisz aplikacje, z których chcesz czytać, w sekcji consumes: na najwyższym poziomie — obok data:, a nie w jej wnętrzu:

consumes: - app: machine-monitor reason: "Oblicza OEE na podstawie strumieni stanu maszyn i liczników z monitora" data: tables: - tablename: oee_results columns: # ... własne tabele Twojej aplikacji, jak zwykle

app to techniczna nazwa aplikacji udostępniającej albo "*" (cudzysłowy są wymagane) dla wszystkich aplikacji w projekcie. reason jest pokazywany użytkownikowi w oknie zgody — sama deklaracja niczego nie przyznaje, dopóki użytkownik jej nie zatwierdzi.

Aby zachować wybrane tabele dla siebie, oznacz je private: true. Wszystko, co zdefiniujesz, jest domyślnie udostępnialne; prywatna tabela lub transformacja w ogóle nie pojawia się w katalogu widzianym przez inne aplikacje.

data: tables: - tablename: measurements # udostępniana (domyślnie) columns: [ ... ] - tablename: calibration_state # wewnętrzna — nigdy niewidoczna dla innych aplikacji private: true columns: [ ... ]

Dostęp jest wyłącznie do odczytu, przyznawany przez użytkownika w obrębie projektu i w każdej chwili odwoływalny. Pełny model oraz wywołania SDK odczytujące historię i strumienie na żywo aplikacji udostępniającej opisano w Korzystanie z danych innych aplikacji.

Last updated on