Skip to Content

SDK IronFlock

SDK IronFlock pozwala aplikacjom brzegowym wchodzić w interakcję z platformą IronFlock. Automatycznie obsługuje uwierzytelnianie podczas działania na zarejestrowanym urządzeniu i zapewnia funkcje do publikowania danych, zapytywania historii, wywoływania zdalnych procedur na urządzeniach oraz aktualizowania metadanych urządzenia.

SDKPakietWymaga
Pythonironflock na PyPIPython 3.8+
JavaScriptironflock na npmNode.js 18+ lub nowoczesna przeglądarka

Instalacja

pip install ironflock

Lub dodaj ironflock do requirements.txt swojej aplikacji.

Szybki start

import asyncio from ironflock import IronFlock async def main(): while True: await ironflock.publish_to_table("sensordata", { "temperature": 22.5, "humidity": 60 }) await asyncio.sleep(5) ironflock = IronFlock(mainFunc=main) ironflock.run()

Podczas używania wewnątrz kontenera aplikacji IronFlock, SDK automatycznie odczytuje dane uwierzytelniające połączenia ze środowiska — nie jest wymagana ręczna konfiguracja.

Opcje konstruktora

ironflock = IronFlock( mainFunc=main, # async function to run after connecting serial_number="abc123" # override device serial (optional) )
ParametrOpis
mainFuncFunkcja asynchroniczna, która uruchamia się po nawiązaniu połączenia
serial_numberNadpisuje numer seryjny urządzenia. Domyślnie zmienna środowiskowa DEVICE_SERIAL_NUMBER

Publikowanie danych

publishToTable / publish_to_table

Publikuje rekord danych do tabeli floty. Nazwa tabeli musi pasować do tabeli zdefiniowanej w data-template.yml aplikacji. SDK automatycznie kieruje dane do właściwej bazy danych projektu.

await ironflock.publish_to_table("sensordata", { "temperature": 22.5, "humidity": 60, "device_id": "sensor-001" })

appendToTable / append_to_table

Dołącza dane do tabeli floty za pomocą zdalnego wywołania procedury zamiast pub/sub. Użyj tego, gdy potrzebujesz potwierdzenia, że dane zostały zapisane.

result = await ironflock.append_to_table("sensordata", { "temperature": 22.5, "humidity": 60 })

publishRowsToTable / publish_rows_to_table

Publikuje wiele wierszy w jednej wiadomości (wstawianie zbiorcze) do tabeli floty. Platforma wstawia całą partię atomowo (wszystko albo nic) w jednej operacji. Użyj tego dla danych o wysokiej częstotliwości, gdzie jedno połączenie na wiersz byłoby zbyt kosztowne. Podobnie jak publishToTable, działa to w trybie „wyślij i zapomnij” — potwierdzenie potwierdza dostarczenie do routera, a nie wstawienie do bazy danych.

await ironflock.publish_rows_to_table("sensordata", [ {"tsp": "2024-01-15T10:30:00.000Z", "temperature": 22.5}, {"tsp": "2024-01-15T10:30:01.000Z", "temperature": 22.7}, ])

Drugim argumentem jest niepusta lista obiektów wierszy do wstawienia.

appendRowsToTable / append_rows_to_table

Dołącza wiele wierszy w jednym zdalnym wywołaniu procedury (wstawianie zbiorcze) do tabeli floty. Platforma wstawia całą partię atomowo (wszystko albo nic): jeśli którykolwiek wiersz jest nieprawidłowy, cała partia zostaje odrzucona i nic nie jest zapisywane. Preferuj to zamiast publishRowsToTable / publish_rows_to_table, gdy potrzebujesz wyniku wstawienia.

result = await ironflock.append_rows_to_table("sensordata", [ {"tsp": "2024-01-15T10:30:00.000Z", "temperature": 22.5}, {"tsp": "2024-01-15T10:30:01.000Z", "temperature": 22.7}, ]) # result -> {"success": True, "count": 2}

reportError / report_error

Zgłasza błąd aplikacji do tabeli error-logs twojej floty. Jest to wygodne opakowanie wokół publishToTable / appendToTable: oznacza wiersz wartością source: "app", poziomem ważności level oraz znacznikiem czasu, a następnie zapisuje go jak każdy normalny wiersz tabeli. Błąd trafia do tej samej tabeli error-logs, której używają błędy systemowe fleetdb (oznaczone source: "system"), więc można go odpytywać za pomocą getHistory, strumieniować za pomocą subscribeToTable / subscribe_to_table, używać w szablonach tablic oraz dostarczać w czasie rzeczywistym na transformed.error-logs — bez wyzwalania systemowego powiadomienia o błędzie platformy.

# Fire-and-forget (default): publishes to the error-logs table await ironflock.report_error("Sensor read timed out", level="warn") # Pass an exception to capture its traceback (falls back to the message) try: risky_operation() except Exception as err: await ironflock.report_error(err) # Use the append RPC when you want to await the insert outcome await ironflock.report_error("Calibration failed", level="error", append=True)

Parametry:

ParametrTypOpis
errorstr / string lub wyjątek / ErrorKomunikat błędu lub wyjątek, którego ślad stosu (traceback/stack) lub komunikat zostaje zapisany
levelstr / string, opcjonalnyPoziom ważności: "error", "warn", "info" lub "debug". Domyślnie "error"
appendbool / boolean, opcjonalnyGdy true, używa zdalnego wywołania append (zwraca wynik wstawienia). Domyślnie false (publikacja typu fire-and-forget)
tspstr / string, opcjonalnyNadpisanie znacznika czasu w formacie ISO-8601. Domyślnie bieżący czas

W Pythonie opcje są argumentami nazwanymi (report_error(error, level=..., append=..., tsp=...)); w JavaScript są przekazywane przez obiekt opcji (reportError(error, { level, append, tsp })).

publish

Publikuje wiadomość do dowolnego tematu (topic) WAMP. Użyj tego do niestandardowych wiadomości lub zdarzeń, które nie odpowiadają tabeli bazy danych.

await ironflock.publish("com.myapp.alerts", { "level": "warning", "message": "Temperature threshold exceeded" })

Zapytywanie historii

getHistory

Pobiera historyczne dane z tabeli floty. Obsługuje filtrowanie, zakresy czasu oraz stronicowanie.

# Simple query data = await ironflock.getHistory("sensordata", {"limit": 100}) # Query with time range and filters data = await ironflock.getHistory("sensordata", { "limit": 500, "offset": 0, "timeRange": { "start": "2026-01-01T00:00:00Z", "end": "2026-03-01T00:00:00Z" }, "filterAnd": [ {"column": "temperature", "operator": ">", "value": 20}, {"column": "humidity", "operator": "<=", "value": 80} ] }) # Current value(s) only: the "latest" marker returns the newest row per entity current = await ironflock.getHistory("sensordata", { "limit": 100, "filterAnd": [{"latest": True}] })

Parametry zapytania:

PoleTypOpis
limitint / numberMaksymalna liczba zwracanych wierszy (1–10 000, wymagane)
offsetint / numberPrzesunięcie dla stronicowania
timeRangedict / object{"start": "<ISO datetime>", "end": "<ISO datetime>"}
filterAndlist / arrayWarunki filtra AND i/lub znacznik latest (patrz niżej)
columnslist / arrayKolumny do zwrócenia (opcjonalne). tsp, device_key oraz authid są zawsze dołączane; pomiń, aby otrzymać wszystkie kolumny

Operatory filtra: =, !=, >, <, >=, <=, LIKE, ILIKE, IN, NOT IN, IS, IS NOT

Każdy filtr to obiekt z kluczami column, operator i value.

Odczytywanie aktualnych wartości. Wpis {"latest": true} w filterAnd nie jest warunkiem filtra, lecz przełącznikiem trybu: zaplecze danych zwraca tylko najnowszy wiersz dla każdej encji, wyznaczony w SQL na podstawie klucza encji, który tabela deklaruje za pomocą maintainLatestFlagFor. Tabela bez klucza encji zwraca swój pojedynczy, najnowszy wiersz.

Pozostałe warunki łączą się ze znacznikiem zgodnie z oczekiwaniami: warunki na kolumnach klucza encji zawężają to, które encje są zwracane, natomiast wszystkie inne warunki oraz timeRange są stosowane do wynikowych najnowszych wierszy. Dlatego połączenie {"latest": true} z filtrem na kolumnie deleted ukrywa usunięte encje, zamiast przywracać ich poprzedni wiersz.

Wcześniejsze wersje IronFlock przechowywały fizyczną kolumnę latest_flag. Kolumna ta już nie istnieje — starszy filtr latest_flag = true jest nadal akceptowany i traktowany jak ten znacznik, ale nowy kod powinien używać {"latest": true}. Znacznik latest nie jest dostępny w getSeriesHistory.

getSeriesHistory / get_series_history

Pobiera zredukowane (down-sampled) dane szeregów czasowych z tabeli floty: kolumny liczbowe agregowane w przedziały czasu (np. średnie godzinowe). Idealne do wykresów obejmujących długie zakresy czasu. Dostępne dla tabel (nie dla transformacji).

series = await ironflock.get_series_history("sensordata", { "metrics": ["temperature", "humidity"], "method": "AVG", "limit": 500, "timeRange": ["2026-01-01T00:00:00Z", "2026-03-01T00:00:00Z"], "groupBy": ["device_id"] })

Parametry zapytania:

PoleTypOpis
metricslist / arrayKolumny liczbowe do zredukowania
methodstr / stringAgregacja na przedział: "AVG", "SUM", "COUNT", "MIN", "MAX", "FIRST" lub "LAST"
limitint / numberMaksymalna liczba przedziałów (1–10 000)
timeRangelist / array[start, end] — łańcuchy ISO datetime lub liczby epoch-ms; null = otwarty koniec (wymagane)
groupBylist / arrayKolumny, według których grupować serię (opcjonalne)
filterAndlist / arrayWarunki filtra AND (opcjonalne). Wyłącznie warunki filtra — znacznik latest nie jest tutaj obsługiwany; aby odczytać aktualne wartości, użyj getHistory

Subskrybowanie danych

subscribeToTable / subscribe_to_table

Subskrybuje aktualizacje tabeli floty w czasie rzeczywistym. Handler jest wywoływany za każdym razem, gdy nowe dane zostają opublikowane do tabeli. Wiersze zapisane przez ścieżkę wstawiania zbiorczego (publishRowsToTable / appendRowsToTable) są dostarczane do twojego handlera pojedynczo, więc kod handlera pozostaje taki sam niezależnie od tego, jak dane zostały zapisane.

def on_sensor_data(*args, **kwargs): print("New reading:", args, kwargs) await ironflock.subscribe_to_table("sensordata", on_sensor_data)

subscribe

Subskrybuje dowolny temat (topic) WAMP do niestandardowej komunikacji w czasie rzeczywistym.

def on_alert(*args, **kwargs): print("Alert received:", args, kwargs) await ironflock.subscribe("com.myapp.alerts", on_alert)

Dostęp do danych między aplikacjami

Odczytuj dane floty innej aplikacji z poziomu własnej aplikacji, w obrębie tego samego projektu. Aplikacja dostawcy musi zadeklarować twoją aplikację w sekcji consumes: swojego pliku data-template.yml, a użytkownik projektu musi przyznać dostęp. Dostęp jest tylko do odczytu: możesz odpytywać historię oraz subskrybować w czasie rzeczywistym wiersze tabel i transformacji (transforms) udostępnianych przez dostawcę, ale nie możesz do nich zapisywać. Połączenia z konsumowanymi aplikacjami są buforowane per aplikacja i zamykane automatycznie, gdy twoja instancja zostaje zatrzymana.

Jeśli twoja aplikacja posiada wieloznaczne (wildcard) uprawnienie (consumes: [{ app: "*" }]), możesz dynamicznie wykrywać i otwierać dostawców za pomocą listConsumableApps / list_consumable_apps oraz connectToAllApps / connect_to_all_apps (poniżej).

connectToApp / connect_to_app

Otwiera połączenie tylko do odczytu z zapleczem danych innej aplikacji i zwraca uchwyt (handle). Uchwyt udostępnia getHistory / get_history, subscribeToTable / subscribe_to_table oraz getSeriesHistory / get_series_history (tylko tabele) — te same zapytania i subskrypcje, których używasz we własnych tabelach — a także close oraz udostępnione katalogi tables / transforms.

# Open a read-only handle on another app's data backend weather = await ironflock.connect_to_app("weather-app") # Inspect what the provider shares print([t["tablename"] for t in weather.tables]) # Query history and subscribe, just like your own tables rows = await weather.get_history("forecasts", {"limit": 100}) def on_forecast(*args, **kwargs): print("New forecast:", args) await weather.subscribe_to_table("forecasts", on_forecast)

Parametry:

ParametrTypOpis
app_name / appNamestr / stringNazwa aplikacji dostawcy, zadeklarowana w twojej sekcji consumes:
stagestr / string, opcjonalnyEtap (stage) dostawcy: "dev" lub "prod". Domyślnie etap twojej własnej aplikacji
on_error / onErrorcallable, opcjonalnyWywoływany z obiektem CrossAppAccessError, gdy dostęp zostanie odmówiony po nawiązaniu połączenia (np. gdy uprawnienie zostanie później cofnięte)

Jeśli dostęp zostanie odmówiony lub użyty nieprawidłowo, zgłaszany (Python) / rzucany (JavaScript) jest CrossAppAccessError z polem code: NO_GRANT, PROVIDER_NOT_INSTALLED, UNKNOWN_APP, PRIVATE_TABLE lub NOT_AUTHORIZED.

W Pythonie stage i on_error są argumentami nazwanymi; w JavaScript są przekazywane przez obiekt opcji (connectToApp(appName, { stage, onError })).

listConsumableApps / list_consumable_apps

Wyświetla listę wszystkich niepublicznych dostawców w projekcie — prymityw wykrywania dla aplikacji posiadających wieloznaczne uprawnienie do konsumpcji (consumes: [{ app: "*" }] w twoim pliku data-template.yml, przyznane przez użytkownika projektu). Wykonuje pojedyncze wywołanie i nie otwiera żadnych połączeń: wyrenderuj zwrócone katalogi w selektorze, a następnie wywołaj connectToApp / connect_to_app dla tych, które chcesz otworzyć — albo connectToAllApps / connect_to_all_apps, aby otworzyć je wszystkie naraz.

Uwaga: Zadeklaruj uprawnienie w pliku data-template.yml swojej aplikacji i ujmij * w cudzysłów — samo * jest aliasem YAML i nie zostanie sparsowane:

consumes: - app: "*"
providers = await ironflock.list_consumable_apps() for p in providers: print(p["app"], list(p["stages"].keys())) # e.g. "weather-app" ["dev", "prod"]

Każdy wpis opisuje jednego dostawcę:

PoleTypOpis
appstr / stringNazwa aplikacji dostawcy
provider_app_keyint / numberKlucz aplikacji dostawcy (app key)
stagesdict / objectKatalog per etap { dev?, prod? }; etap jest obecny tylko wtedy, gdy dostawca ma dla niego zaplecze danych. Każdy katalog zawiera niepubliczne tables oraz transforms, które udostępnia

Zgłaszany (Python) / rzucany (JavaScript) jest CrossAppAccessError z code: NO_GRANT, jeśli twoja aplikacja nie posiada uprawnienia wieloznacznego.

connectToAllApps / connect_to_all_apps

Otwiera uchwyty tylko do odczytu dla wszystkich niepublicznych dostawców w projekcie w jednym wywołaniu (tylko dla konsumentów wieloznacznych). Wylicza dostawców za pomocą listConsumableApps / list_consumable_apps i otwiera każdego z nich, pomijając tych, którzy nie mają zaplecza danych dla żądanego etapu. Każdy uchwyt jest buforowany pod tym samym kluczem co connectToApp / connect_to_app, więc późniejsze wywołanie connectToApp(name) zwraca już otwarty uchwyt. Zwrócone uchwyty są zamykane razem, gdy twoja instancja zostaje zatrzymana.

apps = await ironflock.connect_to_all_apps( on_error=lambda err: print("Provider skipped:", err) ) for app in apps: rows = await app.get_history(app.tables[0]["tablename"], {"limit": 10}) print(app.app, rows)

Parametry:

ParametrTypOpis
stagestr / string, opcjonalnyEtap (stage) dostawcy: "dev" lub "prod". Domyślnie etap twojej własnej aplikacji
continue_on_error / continueOnErrorbool / boolean, opcjonalnyGdy true (wartość domyślna), dostawca, którego nie udało się otworzyć, jest zgłaszany do on_error / onError i pomijany w wyniku. Gdy false, pierwszy błąd jest zgłaszany/rzucany
on_error / onErrorcallable, opcjonalnyWywoływany z każdym dostawcą, którego nie udało się otworzyć (gdy continue_on_error / continueOnError ma wartość true), oraz z obiektem CrossAppAccessError, jeśli dostęp do już otwartego połączenia zostanie później odmówiony (np. gdy uprawnienie zostanie cofnięte)

Zwraca pomyślnie otwarte uchwyty dostawców (ten sam typ uchwytu co connectToApp / connect_to_app). Zgłaszany (Python) / rzucany (JavaScript) jest CrossAppAccessError z code: NO_GRANT, jeśli twoja aplikacja nie posiada uprawnienia wieloznacznego.

W Pythonie stage, on_error i continue_on_error są argumentami nazwanymi; w JavaScript są przekazywane przez obiekt opcji (connectToAllApps({ stage, onError, continueOnError })).

Zarządzane przechowywanie plików

Każde zaplecze danych aplikacji otrzymuje obok swoich tabel prywatny magazyn obiektów, dostępny przez właściwość files. Używaj go do obrazów, plików PDF, klatek z kamery, obrazów firmware — wszystkiego, co nie należy do wiersza tabeli. Nie jest wymagana żadna konfiguracja: aplikacja bez sekcji files: w swoim szablonie danych i tak otrzymuje jedną przestrzeń nazw o nazwie default.

Kluczowa idea jest taka, że zapisanie obiektu zwraca ci od razu trwały adres URL, który możesz wpisać wprost do kolumny tabeli, dzięki czemu widget na panelu może go wyrenderować bez żadnej dodatkowej pracy:

# Store an object and get a permanent URL back in the same call info = await ironflock.files.put("part-1.jpg", jpeg_bytes, content_type="image/jpeg") # The URL is safe to store in a table column — a dashboard widget can then # render <img src="{{photo_url}}"> without any extra round trip await ironflock.publish_to_table("inspections", part_id="1", photo_url=info.url) # Read it back data = await ironflock.files.get("part-1.jpg") # Walk every object under a prefix (pages are fetched for you) async for obj in ironflock.files.iter(prefix="2026/"): print(obj.key, obj.size)

Ten adres URL nigdy nie wygasa, ale nie jest linkiem publicznym: pozostaje czytelny wyłącznie dla uwierzytelnionego żądającego, który posiada uprawnienie READ do tego zaplecza danych, a proxy uwierzytelniające sprawdza to ponownie przy każdym żądaniu. Dlatego można go bezpiecznie przechowywać w bazie danych.

Przestrzenie nazw

Przestrzeń nazw to prefiks klucza, z którym powiązane są zasady — retencja, reguły udostępniania, dozwolone typy zawartości. Nie jest to osobny bucket; wszystkie przestrzenie nazw aplikacji znajdują się w jej jednym obszarze przechowywania. Deklaruj nową tylko wtedy, gdy zbiór obiektów wymaga innych reguł; w przeciwnym razie pozostań w default i porządkuj obiekty ścieżkami kluczy w rodzaju 2026/03/part-1.jpg.

Dodatkowe przestrzenie nazw deklarujesz w data-template.yml:

files: # Storage budget the app suggests for itself. The project user can change it, # and their setting is the one that gets enforced. quotaBytes: 5368709120 namespaces: - name: frames description: Raw camera frames, one JPEG per inspected part. contentTypes: ["image/jpeg"] maxObjectBytes: 20971520 retention: { deleteAfter: 30 days }

Zwróć uwagę, że budżet deklaruje się raz dla całej aplikacji, a nie osobno dla każdej przestrzeni nazw. Przestrzeń nazw to tylko prefiks klucza wewnątrz jednego obszaru przechowywania aplikacji, więc budżet per prefiks nie miałby czego egzekwować. maxObjectBytes jest ustawiany per przestrzeń nazw — ogranicza pojedynczy obiekt, a nie sumę.

Każda z poniższych metod przyjmuje przestrzeń nazw jako argument opcjonalny, a domyślnie używa default.

Zapisywanie i odczytywanie obiektów

# Bytes in, bytes out info = await ironflock.files.put("reports/march.pdf", pdf_bytes, content_type="application/pdf") data = await ironflock.files.get("reports/march.pdf") # Or straight from/to a local file — these stream on the large-object path, # so a multi-gigabyte file never has to fit in memory await ironflock.files.put_file("firmware/v2.bin", "/data/build/v2.bin") await ironflock.files.get_to_file("firmware/v2.bin", "/tmp/v2.bin")
MetodaOpis
put(key, data, …)Zapisuje obiekt (bytes w Pythonie, Uint8Array w JavaScript). Zwraca metadane obiektu wraz z jego url
get(key, namespace?)Zwraca zawartość obiektu
put_file(key, path, …) / get_to_file(key, path, …)Tylko Python. Zapis z pliku lokalnego lub do pliku lokalnego. Strumieniuje na ścieżce dużych obiektów
delete(key, namespace?)Usuwa obiekt
copy(key, to, …)Kopiuje obiekt, opcjonalnie do innej przestrzeni nazw
move(key, to, …)Kopiowanie, a następnie usunięcie. Nie jest atomowe — usługa nie ma operacji przeniesienia, więc nieudane usunięcie pozostawia obie kopie

JavaScript nie ma pomocników operujących na ścieżkach plików, ponieważ pakiet dostarcza jedną kompilację zarówno dla Node, jak i dla przeglądarki — pliki lokalne odczytuj i zapisuj samodzielnie za pomocą fs.

put przyjmuje: content_type / contentType (typ MIME; przestrzeń nazw może ograniczać dozwolone wartości) oraz namespace. W Pythonie są to argumenty nazwane; w JavaScript trafiają do obiektu opcji.

Wyświetlanie listy i inspekcja

MetodaOpis
list(…)Jedna strona obiektów. Zwraca objects, prefixes, is_truncated / isTruncated oraz cursor, który przekazujesz z powrotem po następną stronę
iter(…) / iterate(…)Asynchroniczny iterator po każdym obiekcie pod danym prefiksem, stronicujący automatycznie. W Pythonie nazywa się iter, w JavaScript iterate
stat(key, namespace?)Metadane jednego obiektu bez przesyłania jego zawartości
exists(key, namespace?)Czy obiekt istnieje
namespaces()Przestrzenie nazw, których ta aplikacja może używać
usage(…)Ile magazynu zajmuje aplikacja — patrz niżej
catalog()Przestrzenie nazw wraz z limitami i przydziałami wystawionymi przez serwer. Buforowane po pierwszym wywołaniu

Obiekty są opisywane tymi samymi polami w obu SDK, w stylu nazewnictwa właściwym dla danego języka: namespace, key, size, etag, content_type / contentType, last_modified / lastModified, checksum_sha256 / checksumSha256 oraz url.

Wykorzystanie magazynu i przydział

usage odpowiada prosto z magazynu obiektów w jednym wywołaniu, więc sumy są dokładne, a nie doliczane przez SDK:

u = await ironflock.files.usage() print(u.size_bytes, u.object_count, u.quota_bytes, u.free_bytes) # Break the total down per namespace (costs one listing per namespace) detailed = await ironflock.files.usage(detail=True) print(detailed.per_namespace) # {"default": 1048576, "frames": 73400320}
PoleZnaczenie
size_bytes / sizeBytesAktualnie przechowywane bajty
object_count / objectCountLiczba przechowywanych obiektów
quota_bytes / quotaBytesEgzekwowany budżet. 0 oznacza brak limitu
free_bytes / freeBytesPozostałe bajty. -1 oznacza brak limitu — zgłoszenie tu 0 czytałoby się jako „pełny”
per_namespace / perNamespaceBajty w rozbiciu na przestrzenie nazw. Obecne tylko wtedy, gdy poprosisz o szczegółowe rozbicie

Rozbicie na przestrzenie nazw jest domyślnie wyłączone, ponieważ magazyn nie potrafi na nie odpowiedzieć wprost: rozlicza się per obszar przechowywania, a przestrzeń nazw to tylko prefiks, więc SDK musi wylistować każdą przestrzeń nazw i zsumować rozmiary. Proś o nie wtedy, gdy naprawdę go potrzebujesz, a nie na gorącej ścieżce.

Pojawiają się dwa różne przydziały i warto je od siebie odróżniać. catalog() raportuje oba:

PoleZnaczenie
quota_bytes / quotaBytesTo, co jest faktycznie egzekwowane, odczytane z magazynu obiektów — ustawienie użytkownika projektu
suggested_quota_bytes / suggestedQuotaBytesTo, o co poprosił szablon danych aplikacji. 0, jeśli o nic nie prosił

Różnią się zawsze wtedy, gdy użytkownik podniósł lub obniżył budżet aplikacji — i właśnie dlatego wartość egzekwowana jest odczytywana z magazynu, a nie z szablonu: ponowne wdrożenie aplikacji nie może po cichu zresetować wyboru użytkownika. Interfejs może pokazywać obie („aplikacja sugeruje X, Ty ustawiłeś Y”). Egzekwowanie zawsze korzysta z tej pierwszej.

Udostępnianie obiektów

Istnieją dwa rodzaje linków, a różnica między nimi ma znaczenie:

MetodaCzas życiaKto może go odczytać
url(key, …)TrwałyWyłącznie uwierzytelniony żądający z uprawnieniem READ do tego zaplecza danych — sprawdzanym ponownie przy każdym żądaniu. Można go bezpiecznie przechowywać w kolumnie tabeli
share_url / shareUrlWygasający (domyślnie 15 min, ograniczany przez serwer)Każdy, kto ma ten link. Nic nie sprawdza ponownie uprawnień w chwili jego użycia

share_url / shareUrl to poświadczenie na okaziciela: przekaż je osobie, która potrzebuje tymczasowego dostępu, i nie przechowuj go w bazie danych. Do wszystkiego, co renderuje panel, używaj url.

url zwraca None / undefined tam, gdzie wdrożenie nie ma krawędzi HTTP (na przykład Appliance działający po zwykłym HTTP) — to sygnał, aby sięgnąć po get. Przekazanie etag obiektu jako argumentu version pozwala przeglądarkom buforować odpowiedź jako niezmienną.

upload_url / uploadUrl tworzy wygasający adres URL, który przyjmuje bezpośrednie przesłanie danych, i zwraca url, method, headers oraz expires_in / expiresIn. Wyślij dokładnie te nagłówki, które zwróci, w przeciwnym razie podpis nie zostanie zweryfikowany.

Duże obiekty

SDK wybiera sposób transferu automatycznie, na podstawie rozmiaru — nie ma tu nic do konfigurowania:

Rozmiar obiektuSposób transferu
Do limitu inline (obecnie 6 MiB)Pojedyncze wywołanie przez router wiadomości
PowyżejBezpośrednio do magazynu obiektów przez HTTPS, z pominięciem routera

Dokładny limit serwer podaje w czasie działania jako inline_max_bytes / inlineMaxBytes w catalog(), dzięki czemu można go podnieść bez wydawania nowej wersji SDK.

Pozostają dwa górne pułapy i oba zgłaszają TOO_LARGE z przyczyną wskazującą, na który z nich trafiłeś:

  • 5 GiB — limit pojedynczego przesłania w magazynie obiektów. Przesyłanie wieloczęściowe (multipart) nie jest jeszcze zaimplementowane.
  • Limit inline tam, gdzie nie ma bezpośredniego punktu końcowego — Appliance odcięty od sieci (air-gapped) w ogóle nie może przesłać dużego obiektu. Nie pomoże tu ani ponowienie próby, ani mniejszy fragment, i komunikat wprost o tym informuje.

Ścieżka bezpośrednia wymaga, aby urządzenie miało dostęp do hosta magazynu obiektów, a nie tylko do routera. Dwa niepowodzenia częste w terenie mają własne kody, zamiast wyglądać jak problemy z autoryzacją: PRESIGN_UNREACHABLE (proxy przepuszczające wyłącznie ruch do routera) oraz CLOCK_SKEW (magazyn obiektów odrzuca żądania rozbieżne w czasie o więcej niż 15 minut — sprawdź NTP na urządzeniu).

Błędy przechowywania plików

Każda operacja na plikach zgłasza (Python) / rzuca (JavaScript) FileStoreError, który niesie stabilny code oraz czytelny dla człowieka reason. Rozgałęziaj logikę na podstawie code, nigdy na podstawie reason.

from ironflock.filestore import FileStoreError try: await ironflock.files.put("huge.bin", payload) except FileStoreError as e: if e.code == "QUOTA_EXCEEDED": print("Filestore is full:", e.reason) else: raise
KodZnaczenie
NOT_AUTHORIZEDWywołujący nie może wykonać tej operacji
NO_SUCH_NAMESPACEPrzestrzeń nazw nie jest zadeklarowana w szablonie danych
NO_SUCH_OBJECTKlucz nie istnieje
TOO_LARGEPrzekracza limit transferu w pojedynczym wywołaniu
OBJECT_TOO_LARGEPrzekracza własny maxObjectBytes przestrzeni nazw
QUOTA_EXCEEDEDMagazyn plików jest pełny
CONTENT_TYPE_NOT_ALLOWEDPrzestrzeń nazw ogranicza contentTypes
NOT_SUPPORTEDZaplecze nie potrafi tego wykonać
NOT_AVAILABLETo wdrożenie nie ma usługi plików
PRESIGN_UNREACHABLEMagazyn obiektów nie jest osiągalny bezpośrednio (proxy?)
CLOCK_SKEWZegar urządzenia jest zbyt rozbieżny
INTERNALWszystko pozostałe

Nowszy serwer może wprowadzić kody, których to wydanie SDK nie zna. Są one przekazywane dalej jako code, zamiast być sprowadzane do jednej wartości, więc traktuj nierozpoznaną wartość jako ogólne niepowodzenie.

W Pythonie FileStoreError jest importowany z ironflock.filestore; w JavaScript jest eksportowany z katalogu głównego pakietu (import { FileStoreError } from "ironflock").

Komunikacja między urządzeniami

registerDeviceFunction / register_device_function

Rejestruje procedurę, którą mogą wywoływać inne urządzenia w tym samym projekcie. SDK automatycznie przypisuje procedurę do przestrzeni nazw bieżącego urządzenia.

def add(a, b): return a + b await ironflock.register_device_function("com.myapp.add", add)

register() jest aliasem dla register_device_function().

callDeviceFunction / call_device_function

Wywołuje procedurę zarejestrowaną przez inne urządzenie. SDK automatycznie składa pełny temat (topic) WAMP, używając klucza docelowego urządzenia.

result = await ironflock.call_device_function( 42, # target device key "com.myapp.add", # procedure name args=[3, 5] # arguments ) print(result) # 8

call

Wywołuje zdalną procedurę, używając pełnego identyfikatora URI WAMP. Użyj tego do bezpośrednich wywołań, gdy znasz dokładny temat (topic).

result = await ironflock.call("some.full.wamp.topic", args=[42])

Metadane urządzenia

setDeviceLocation / set_device_location

Aktualizuje lokalizację GPS urządzenia w platformie. Zmiany są odzwierciedlane w czasie rzeczywistym na mapach IronFlock.

await ironflock.set_device_location(long=8.6821, lat=50.1109)
ParametrZakres
long-180 do 180
lat-90 do 90

Historia lokalizacji nie jest przechowywana. Aby śledzić lokalizację w czasie, utwórz dedykowaną tabelę i użyj publish_to_table / publishToTable.

getRemoteAccessUrlForPort

Zwraca publiczny adres URL zdalnego dostępu dla danego portu na urządzeniu.

url = ironflock.getRemoteAccessUrlForPort(8080) # "https://<device_key>-<app_name>-8080.app.ironflock.com"

Właściwości i cykl życia połączenia

WłaściwośćTypOpis
is_connectedboolCzy połączenie z platformą jest aktywne
connectionCrossbarConnectionBazowa instancja połączenia (zastosowania zaawansowane)
MetodaOpis
run()Uruchamia połączenie i wykonuje mainFunc (blokujące)
await start()Uruchamia połączenie asynchronicznie
await stop()Zatrzymuje połączenie i anuluje uruchomione zadania
await run_async()Uruchamia i utrzymuje połączenie działające asynchronicznie

Obsługa błędów

Każda metoda SDK zgłasza niepowodzenie głośno: przy nieprawidłowych argumentach, utraconym połączeniu lub odrzuceniu przez platformę zgłasza wyjątek (Python) lub odrzuca obietnicę (JavaScript) z komunikatem wskazującym operację, temat (topic) oraz przyczynę. Nic nie jest po cichu pomijane, więc opakuj w blok try te wywołania, które mają przetrwać błąd.

try: rows = await ironflock.getHistory("sensordata", {"limit": 100}) except ValueError as e: # Invalid parameters — e.g. limit out of range, or a malformed filter print(f"Bad query: {e}") except RuntimeError as e: # Not connected, table not in the data-template, or the platform rejected the call print(f"Query failed: {e}")

W JavaScript niepowodzenia pochodzące z platformy są instancjami WampError — zwykłej podklasy Error, która dodatkowo przenosi identyfikator URI błędu WAMP w polu error oraz ładunek błędu w polach args / kwargs. Wszystko pozostałe (nieprawidłowe parametry, brak połączenia) to zwykły Error.

Migracja: starsze wersje SDK zapisywały komunikat w logu i zwracały None / null, gdy wywołanie się nie powiodło. Teraz zamiast tego zgłaszają błąd, więc kod w rodzaju if result is None: nie wykrywa już niepowodzeń — użyj try / except (lub try / catch).

Użycie w przeglądarce (tylko JavaScript)

SDK JavaScript działa w nowoczesnych przeglądarkach. Ponieważ przeglądarki nie mają zmiennych środowiskowych, przekaż całą konfigurację przez konstruktor:

import { IronFlock } from "ironflock"; const ironflock = new IronFlock({ serialNumber: "device-serial-from-server", deviceKey: "my-device-key", appName: "MyWebApp", swarmKey: 10, appKey: 20, env: "PROD", }); await ironflock.start(); await ironflock.publishToTable("sensordata", [{ temperature: 22 }]);

Użyj IronFlock.fromServer(), aby pobrać konfigurację z twojego zaplecza (backend) zamiast wpisywać dane uwierzytelniające na stałe:

const ironflock = await IronFlock.fromServer("/api/ironflock-config"); await ironflock.start();

Twój punkt końcowy zaplecza powinien zwrócić obiekt JSON z opcjami połączenia (serialNumber, deviceKey, appName, swarmKey, appKey, env).

Rejestrowanie funkcji agentów AI

SDK może rejestrować funkcje, które mogą być wywoływane przez agentów AI. Zarejestruj procedurę i odwołaj się do jej tematu (topic) w pliku ai-template.yml:

def get_sensor_reading(sensor_id): """Returns the latest reading from a sensor.""" reading = read_from_hardware(sensor_id) return { "sensor_id": sensor_id, "temperature": reading.temp, "humidity": reading.hum, "timestamp": reading.ts } await ironflock.register_device_function("sensors.get_latest", get_sensor_reading)

Agent AI może następnie wywołać tę funkcję, gdy użytkownik zada pytanie wymagające bieżących danych z czujnika.

Aby połączyć zarejestrowany temat (topic) WAMP z agentem AI, odwołaj się do niego w pliku .ironflock/ai-template.yml swojej aplikacji:

sensor_agent: tool_description: | Delegate to this agent when the user asks about sensor readings, live device data, or current environmental conditions. system_prompt: | You are a sensor data specialist. Use get_current to retrieve the latest reading from any sensor. Always include the unit in your response. main: true max_context_tokens: 30000 max_iterations: 5 tools: get_current: description: Returns the latest reading from a sensor. topic: sensors.get_latest parameters: sensor_id: type: string description: The sensor identifier to query. required: true

Wartość topic (sensors.get_latest) musi pasować do nazwy przekazanej do register_device_function / registerDeviceFunction w twoim kodzie brzegowym. IronFlock automatycznie kieruje wywołanie do urządzenia, na którym zarejestrowana jest funkcja.

Pełną dokumentację pliku ai-template.yml znajdziesz w Definiowanie agentów i narzędzi.

Zmienne środowiskowe

Te zmienne są ustawiane automatycznie przez środowisko uruchomieniowe IronFlock wewnątrz kontenerów aplikacji:

ZmiennaOpis
DEVICE_NAMEWyświetlana nazwa urządzenia
DEVICE_SERIAL_NUMBERUnikalny, niezmienny identyfikator urządzenia
DEVICE_KEYKlucz urządzenia do uwierzytelniania
SWARM_KEYIdentyfikator projektu
APP_KEYIdentyfikator aplikacji
APP_NAMENazwa aplikacji
ENVŚrodowisko: DEV lub PROD
import os device_name = os.environ.get("DEVICE_NAME") serial = os.environ.get("DEVICE_SERIAL_NUMBER") project_key = os.environ.get("SWARM_KEY")
Last updated on