Skip to Content
Sviluppo App IoTIronFlock SDK

IronFlock SDK

L’SDK IronFlock consente alle tue applicazioni edge di interagire con la piattaforma IronFlock. Gestisce l’autenticazione automaticamente quando viene eseguito su un dispositivo registrato e fornisce funzioni per pubblicare dati, interrogare la cronologia, chiamare procedure remote tra dispositivi e aggiornare i metadati dei dispositivi.

SDKPacchettoRichiede
Pythonironflock su PyPIPython 3.8+
JavaScriptironflock su npmNode.js 18+ o browser moderno

Installazione

pip install ironflock

Oppure aggiungi ironflock al file requirements.txt della tua app.

Avvio Rapido

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()

Quando viene usato all’interno di un container dell’app IronFlock, l’SDK legge automaticamente le credenziali di connessione dall’ambiente — nessuna configurazione manuale necessaria.

Opzioni del Costruttore

ironflock = IronFlock( mainFunc=main, # async function to run after connecting serial_number="abc123" # override device serial (optional) )
ParametroDescrizione
mainFuncUna funzione asincrona che viene eseguita una volta stabilita la connessione
serial_numberSovrascrive il numero seriale del dispositivo. Predefinito: variabile d’ambiente DEVICE_SERIAL_NUMBER

Pubblicazione dei Dati

publishToTable / publish_to_table

Pubblica un record di dati in una tabella della flotta. Il nome della tabella deve corrispondere a una tabella definita nel file data-template.yml della tua app. L’SDK instrada automaticamente i dati al database del progetto corretto.

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

appendToTable / append_to_table

Aggiunge dati a una tabella della flotta tramite una chiamata di procedura remota invece di pub/sub. Usalo quando hai bisogno della conferma che i dati siano stati persistiti.

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

publishRowsToTable / publish_rows_to_table

Pubblica molte righe in un singolo messaggio (inserimento in blocco) in una tabella della flotta. La piattaforma inserisce l’intero batch in modo atomico (tutto-o-niente) in un’unica operazione. Usalo per dati ad alta frequenza dove un round-trip per riga sarebbe troppo costoso. Come publishToTable, è fire-and-forget — la conferma attesta la consegna al router, non l’inserimento nel database.

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}, ])

Il secondo argomento è una lista non vuota di oggetti riga da inserire.

appendRowsToTable / append_rows_to_table

Aggiunge molte righe in una singola chiamata di procedura remota (inserimento in blocco) a una tabella della flotta. La piattaforma inserisce l’intero batch in modo atomico (tutto-o-niente): se una qualsiasi riga non è valida, l’intero batch viene rifiutato e nulla viene persistito. Preferiscilo a publishRowsToTable / publish_rows_to_table quando hai bisogno dell’esito dell’inserimento.

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

Segnala un errore dell’applicazione nella tabella error-logs della tua flotta. Si tratta di un wrapper di comodità su publishToTable / appendToTable: aggiunge alla riga source: "app", un level di gravità e un timestamp, quindi la scrive come qualsiasi normale riga di tabella. L’errore finisce nella stessa tabella error-logs usata dagli errori di sistema di fleetdb (contrassegnati con source: "system"), perciò è interrogabile con getHistory, trasmissibile in streaming con subscribeToTable / subscribe_to_table, utilizzabile nei board-templates e consegnato in tempo reale su transformed.error-logs — senza far comparire il toast di errore di sistema della piattaforma.

# 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)

Parametri:

ParametroTipoDescrizione
errorstr / string o eccezione / ErrorIl messaggio di errore, oppure un’eccezione il cui traceback/stack (o messaggio) viene registrato
levelstr / string, opzionaleGravità: "error", "warn", "info" o "debug". Il valore predefinito è "error"
appendbool / boolean, opzionaleQuando è true, usa la procedura remota di append (restituisce l’esito dell’inserimento). Il valore predefinito è false (pubblicazione fire-and-forget)
tspstr / string, opzionaleSovrascrittura del timestamp ISO-8601. Il valore predefinito è l’ora corrente

In Python le opzioni sono argomenti con parola chiave (report_error(error, level=..., append=..., tsp=...)); in JavaScript vengono passate tramite un oggetto di opzioni (reportError(error, { level, append, tsp })).

publish

Pubblica un messaggio su qualsiasi topic WAMP. Usalo per messaggi personalizzati o eventi che non si mappano su una tabella del database.

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

Interrogazione dei Dati Storici

getHistory

Recupera dati storici da una tabella della flotta. Supporta filtraggio, intervalli di tempo e paginazione.

# 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}] })

Parametri di query:

CampoTipoDescrizione
limitint / numberNumero massimo di righe da restituire (1–10.000, obbligatorio)
offsetint / numberOffset per la paginazione
timeRangedict / object{"start": "<ISO datetime>", "end": "<ISO datetime>"}
filterAndlist / arrayCondizioni di filtro AND e/o il marcatore latest (vedi sotto)
columnslist / arrayColonne da restituire (opzionale). tsp, device_key e authid sono sempre incluse; ometti per avere tutte le colonne

Operatori di filtro: =, !=, >, <, >=, <=, LIKE, ILIKE, IN, NOT IN, IS, IS NOT

Ogni filtro è un oggetto con le chiavi column, operator e value.

Leggere i valori correnti. Una voce {"latest": true} in filterAnd non è una condizione di filtro ma un cambio di modalità: il Data Backend restituisce solo la riga più recente per ogni entità, derivata in SQL dalla chiave di entità che la tabella dichiara con maintainLatestFlagFor. Una tabella senza chiave di entità restituisce la sua singola riga più recente.

Le altre condizioni si combinano con il marcatore come ci si aspetta: le condizioni sulle colonne della chiave di entità restringono quali entità vengono restituite, mentre tutte le altre condizioni e timeRange vengono applicate alle righe più recenti risultanti. Quindi combinare {"latest": true} con un filtro su deleted nasconde le entità eliminate invece di far riemergere la loro riga precedente.

Le versioni precedenti di IronFlock memorizzavano una colonna fisica latest_flag. Non esiste più — un filtro legacy latest_flag = true viene ancora accettato e trattato come il marcatore, ma il codice nuovo dovrebbe usare {"latest": true}. Il marcatore latest non è disponibile in getSeriesHistory.

getSeriesHistory / get_series_history

Recupera dati di serie temporali sotto-campionati da una tabella di flotta: colonne numeriche aggregate in intervalli di tempo (ad es. medie orarie). Ideale per grafici su intervalli di tempo lunghi. Disponibile per le tabelle (non per i transform).

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

Parametri di query:

CampoTipoDescrizione
metricslist / arrayColonne numeriche da sotto-campionare
methodstr / stringAggregazione per intervallo: "AVG", "SUM", "COUNT", "MIN", "MAX", "FIRST" o "LAST"
limitint / numberNumero massimo di intervalli (1–10.000)
timeRangelist / array[start, end] — stringhe ISO datetime o numeri epoch-ms; null = estremo aperto (obbligatorio)
groupBylist / arrayColonne per cui raggruppare la serie (opzionale)
filterAndlist / arrayCondizioni di filtro AND (opzionale). Solo condizioni di filtro — il marcatore latest non è supportato qui; usa getHistory per leggere i valori correnti

Sottoscrizione ai Dati

subscribeToTable / subscribe_to_table

Sottoscrive agli aggiornamenti in tempo reale di una tabella della flotta. Il gestore viene chiamato ogni volta che nuovi dati vengono pubblicati nella tabella. Le righe scritte tramite il percorso di inserimento in blocco (publishRowsToTable / appendRowsToTable) vengono consegnate al tuo gestore una alla volta, quindi il codice del gestore rimane lo stesso indipendentemente da come i dati sono stati scritti.

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

subscribe

Sottoscrive a qualsiasi topic WAMP per la messaggistica in tempo reale personalizzata.

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

Accesso ai Dati tra App

Leggi i dati di flotta di un’altra app dall’interno della tua app, all’interno dello stesso progetto. L’app fornitrice deve dichiarare la tua app nella sezione consumes: del suo data-template.yml, e l’utente del progetto deve concedere l’accesso. L’accesso è di sola lettura: puoi interrogare lo storico e sottoscrivere in tempo reale le righe delle tabelle e dei transform condivisi dal fornitore, ma non puoi scrivervi. Le connessioni alle app consumate vengono memorizzate nella cache per app e chiuse automaticamente quando la tua istanza si arresta.

Se la tua app possiede l’autorizzazione wildcard (consumes: [{ app: "*" }]), puoi scoprire e aprire dinamicamente i fornitori con listConsumableApps / list_consumable_apps e connectToAllApps / connect_to_all_apps (di seguito).

connectToApp / connect_to_app

Apre una connessione di sola lettura al data backend di un’altra app e restituisce un handle. L’handle espone getHistory / get_history, subscribeToTable / subscribe_to_table e getSeriesHistory / get_series_history (solo tabelle) — le stesse interrogazioni e sottoscrizioni che usi sulle tue tabelle — oltre a close e ai cataloghi condivisi 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)

Parametri:

ParametroTipoDescrizione
app_name / appNamestr / stringNome dell’app fornitrice, come dichiarato nella tua sezione consumes:
stagestr / string, opzionaleStage del fornitore: "dev" o "prod". Il valore predefinito è lo stage della tua app
on_error / onErrorcallable, opzionaleViene invocato con un CrossAppAccessError se l’accesso viene negato dopo che la connessione è stata stabilita (ad es. se l’autorizzazione viene revocata in seguito)

Se l’accesso viene negato o usato in modo improprio, viene sollevato (Python) / lanciato (JavaScript) un CrossAppAccessError con un campo code: NO_GRANT, PROVIDER_NOT_INSTALLED, UNKNOWN_APP, PRIVATE_TABLE o NOT_AUTHORIZED.

In Python, stage e on_error sono argomenti con parola chiave; in JavaScript vengono passati tramite un oggetto di opzioni (connectToApp(appName, { stage, onError })).

listConsumableApps / list_consumable_apps

Elenca tutti i fornitori non privati nel progetto — la primitiva di scoperta per le app che possiedono l’autorizzazione di consumo wildcard (consumes: [{ app: "*" }] nel tuo data-template.yml, concessa dall’utente del progetto). Esegue una singola chiamata e non apre nessuna connessione: mostra i cataloghi restituiti in un selettore, quindi chiama connectToApp / connect_to_app per quelli che desideri — oppure connectToAllApps / connect_to_all_apps per aprirli tutti in una volta.

Nota: Dichiara l’autorizzazione nel data-template.yml della tua app, e metti tra virgolette il * — un * da solo è un alias YAML e non verrà interpretato:

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"]

Ogni voce descrive un fornitore:

CampoTipoDescrizione
appstr / stringNome dell’app fornitrice
provider_app_keyint / numberLa chiave dell’app del fornitore
stagesdict / objectCatalogo per stage { dev?, prod? }; uno stage è presente solo se il fornitore dispone di un data backend per esso. Ogni catalogo contiene le tables e i transforms non privati che condivide

Viene sollevato (Python) / lanciato (JavaScript) un CrossAppAccessError con code: NO_GRANT se la tua app non possiede alcuna autorizzazione wildcard.

connectToAllApps / connect_to_all_apps

Apre handle di sola lettura verso tutti i fornitori non privati nel progetto in un’unica chiamata (solo per i consumatori wildcard). Enumera i fornitori tramite listConsumableApps / list_consumable_apps e apre ciascuno di essi, saltando quelli senza un data backend per lo stage richiesto. Ogni handle viene memorizzato nella cache con la stessa chiave di connectToApp / connect_to_app, così una successiva chiamata a connectToApp(name) restituisce l’handle già pronto. Gli handle restituiti vengono chiusi insieme quando la tua istanza si arresta.

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)

Parametri:

ParametroTipoDescrizione
stagestr / string, opzionaleStage del fornitore: "dev" o "prod". Il valore predefinito è lo stage della tua app
continue_on_error / continueOnErrorbool / boolean, opzionaleQuando è true (il valore predefinito), un fornitore che non riesce ad aprirsi viene segnalato a on_error / onError e omesso dal risultato. Quando è false, il primo errore viene sollevato/lanciato
on_error / onErrorcallable, opzionaleViene invocato con ogni fornitore che non è stato possibile aprire (mentre continue_on_error / continueOnError è true), e con un CrossAppAccessError se una connessione già aperta viene successivamente negata (ad es. se l’autorizzazione viene revocata)

Restituisce gli handle dei fornitori aperti con successo (stesso tipo di handle di connectToApp / connect_to_app). Viene sollevato (Python) / lanciato (JavaScript) un CrossAppAccessError con code: NO_GRANT se la tua app non possiede alcuna autorizzazione wildcard.

In Python, stage, on_error e continue_on_error sono argomenti con parola chiave; in JavaScript vengono passati tramite un oggetto di opzioni (connectToAllApps({ stage, onError, continueOnError })).

Archiviazione Gestita dei File

Ogni data backend di un’app dispone di un’archiviazione a oggetti privata accanto alle sue tabelle, raggiungibile tramite la proprietà files. Usala per immagini, PDF, fotogrammi di telecamere, blob di firmware — tutto ciò che non appartiene alla riga di una tabella. Non è richiesta alcuna configurazione: un’app senza una sezione files: nel suo data template ottiene comunque un namespace chiamato default.

L’idea chiave è che memorizzare un oggetto ti restituisce un URL permanente che puoi scrivere direttamente in una colonna di una tabella, così che un widget di una dashboard possa renderizzarlo senza ulteriore lavoro:

# 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)

Quell’URL non scade mai, ma non è un link pubblico: resta leggibile soltanto a un richiedente autenticato che possiede l’accesso READ su questo data backend, e un proxy di autenticazione lo riverifica a ogni richiesta. È quindi sicuro memorizzarlo nel database.

Namespace

Un namespace è un prefisso di chiave che porta con sé delle policy — conservazione, regole di condivisione, tipi di contenuto ammessi. Non è un bucket separato: ogni namespace di un’app risiede all’interno dell’unica area di archiviazione di quell’app. Dichiarane uno solo quando un insieme di oggetti ha bisogno di regole diverse; altrimenti resta in default e organizza i tuoi oggetti con percorsi di chiave come 2026/03/part-1.jpg.

Dichiara i namespace aggiuntivi nel 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 }

Nota che il budget viene dichiarato una sola volta per l’intera app, non per singolo namespace. Un namespace è soltanto un prefisso di chiave all’interno dell’unica area di archiviazione dell’app, quindi non ci sarebbe nulla su cui far valere un budget per prefisso. maxObjectBytes è invece per namespace — limita un singolo oggetto, non un totale.

Ogni metodo elencato di seguito accetta il namespace come argomento opzionale e usa default come valore predefinito.

Memorizzare e leggere gli oggetti

# 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")
MetodoDescrizione
put(key, data, …)Memorizza un oggetto (bytes in Python, Uint8Array in JavaScript). Restituisce i metadati dell’oggetto, incluso il suo url
get(key, namespace?)Restituisce il contenuto dell’oggetto
put_file(key, path, …) / get_to_file(key, path, …)Solo Python. Memorizza a partire da un file locale, o scrive su di esso. Trasmette in streaming sul percorso per oggetti di grandi dimensioni
delete(key, namespace?)Elimina un oggetto
copy(key, to, …)Copia un oggetto, opzionalmente in un altro namespace
move(key, to, …)Copia-poi-elimina. Non atomico — il servizio non dispone di un verbo di spostamento, quindi un’eliminazione fallita lascia entrambe le copie

JavaScript non offre helper per i percorsi dei file perché il pacchetto viene distribuito con un’unica build sia per Node sia per il browser — leggi e scrivi i file locali autonomamente con fs.

put accetta: content_type / contentType (il tipo MIME; il namespace può limitare quali sono ammessi) e namespace. In Python sono argomenti con parola chiave; in JavaScript vengono passati in un oggetto di opzioni.

Elencare e ispezionare

MetodoDescrizione
list(…)Una pagina di oggetti. Restituisce objects, prefixes, is_truncated / isTruncated e un cursor da ripassare per la pagina successiva
iter(…) / iterate(…)Iteratore asincrono su tutti gli oggetti sotto un prefisso, con paginazione automatica. Si chiama iter in Python e iterate in JavaScript
stat(key, namespace?)Metadati di un singolo oggetto senza trasferirne il contenuto
exists(key, namespace?)Indica se un oggetto esiste
namespaces()I namespace che questa app può usare
usage(…)Quanto spazio di archiviazione sta usando l’app — vedi sotto
catalog()I namespace più i limiti e le quote emessi dal server. Memorizzato nella cache dopo la prima chiamata

Gli oggetti sono descritti dagli stessi campi in entrambi gli SDK, secondo lo stile di denominazione di ciascun linguaggio: namespace, key, size, etag, content_type / contentType, last_modified / lastModified, checksum_sha256 / checksumSha256 e url.

Utilizzo dello spazio e quota

usage risponde interrogando l’archiviazione a oggetti con una sola chiamata, quindi i totali sono esatti anziché sommati dall’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}
CampoSignificato
size_bytes / sizeBytesByte attualmente memorizzati
object_count / objectCountNumero di oggetti memorizzati
quota_bytes / quotaBytesIl budget applicato. 0 significa illimitato
free_bytes / freeBytesByte rimanenti. -1 significa illimitato — riportare 0 verrebbe letto come “pieno”
per_namespace / perNamespaceByte per namespace. Presente solo se richiedi il dettaglio

Il dettaglio per namespace è disattivato per impostazione predefinita perché l’archiviazione a oggetti non è in grado di fornirlo direttamente: conteggia per area di archiviazione, e un namespace è soltanto un prefisso, quindi l’SDK deve elencare ogni namespace per sommarne le dimensioni. Richiedilo quando ti serve, non su un percorso critico.

Compaiono due quote diverse, ed è utile tenerle distinte. catalog() riporta entrambe:

CampoSignificato
quota_bytes / quotaBytesCiò che viene effettivamente applicato, letto dall’archiviazione a oggetti — l’impostazione dell’utente del progetto
suggested_quota_bytes / suggestedQuotaBytesCiò che il data template dell’app ha richiesto. 0 se non ha richiesto nulla

Differiscono ogni volta che un utente ha aumentato o ridotto il budget dell’app, ed è per questo che il valore applicato viene letto dall’archiviazione anziché dal template — ridistribuire l’app non deve azzerare silenziosamente la scelta di un utente. Un’interfaccia può mostrare entrambi (“l’app suggerisce X, tu hai impostato Y”). L’applicazione della quota usa sempre il primo.

Condividere gli oggetti

Esistono due tipi di link, e la differenza è importante:

MetodoDurataChi può leggerlo
url(key, …)PermanenteSolo un richiedente autenticato con accesso READ su questo data backend — riverificato a ogni richiesta. Sicuro da memorizzare in una colonna di una tabella
share_url / shareUrlA scadenza (15 minuti per impostazione predefinita, limitati dal server)Chiunque possieda il link. Nulla riverifica l’autorizzazione nel momento in cui viene usato

share_url / shareUrl è una capability al portatore: consegnalo a una persona che ha bisogno di accesso temporaneo, e non memorizzarlo nel database. Usa url per tutto ciò che viene renderizzato da una dashboard.

url restituisce None / undefined quando il deployment non dispone di un edge HTTP (ad esempio un’appliance in solo HTTP) — è il segnale per ripiegare su get. Passando l’etag di un oggetto come argomento version, i browser possono memorizzare la risposta nella cache in modo immutabile.

upload_url / uploadUrl genera un URL a scadenza che accetta un upload diretto, restituendo url, method, headers ed expires_in / expiresIn. Invia esattamente gli header che restituisce, altrimenti la firma non verrà verificata.

Oggetti di grandi dimensioni

L’SDK sceglie il trasporto in base alla dimensione, automaticamente — non c’è nulla da configurare:

Dimensione dell’oggettoCome viaggia
Fino al limite inline (attualmente 6 MiB)Una singola chiamata attraverso il router dei messaggi
SuperioreDirettamente verso l’archiviazione a oggetti tramite HTTPS, bypassando il router

Il limite esatto viene comunicato dal server a runtime come inline_max_bytes / inlineMaxBytes in catalog(), così può essere alzato senza una nuova release dell’SDK.

Restano due tetti massimi, ed entrambi segnalano TOO_LARGE con un motivo che indica quale dei due hai raggiunto:

  • 5 GiB — il limite di upload singolo dell’archiviazione a oggetti. L’upload multipart non è ancora implementato.
  • Il limite inline, dove non esiste un endpoint diretto — un’appliance air-gapped non può trasferire affatto un oggetto di grandi dimensioni. Nessun nuovo tentativo né chunk più piccolo servirà a qualcosa, e il messaggio lo dice esplicitamente.

Il percorso diretto richiede che il dispositivo raggiunga l’host dell’archiviazione a oggetti, non solo il router. Due fallimenti comuni sul campo hanno codici propri invece di apparire come problemi di autorizzazione: PRESIGN_UNREACHABLE (un proxy che consente soltanto il router) e CLOCK_SKEW (l’archiviazione a oggetti rifiuta le richieste sfasate di più di 15 minuti — controlla l’NTP sul dispositivo).

Errori di archiviazione dei file

Ogni operazione sui file solleva (Python) / lancia (JavaScript) un FileStoreError che trasporta un code stabile e un reason leggibile. Basa le tue condizioni sul code, mai sul 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
CodiceSignificato
NOT_AUTHORIZEDIl chiamante non può eseguire questa operazione
NO_SUCH_NAMESPACEIl namespace non è dichiarato nel data template
NO_SUCH_OBJECTLa chiave non esiste
TOO_LARGESupera il limite di trasferimento in una singola chiamata
OBJECT_TOO_LARGESupera il maxObjectBytes proprio del namespace
QUOTA_EXCEEDEDIl filestore è pieno
CONTENT_TYPE_NOT_ALLOWEDIl namespace limita i contentTypes
NOT_SUPPORTEDIl backend non può farlo
NOT_AVAILABLEQuesto deployment non dispone di un servizio file
PRESIGN_UNREACHABLEL’archiviazione a oggetti non è raggiungibile direttamente (un proxy?)
CLOCK_SKEWL’orologio del dispositivo è troppo sfasato
INTERNALQualsiasi altra cosa

Un server più recente può introdurre codici che questa release dell’SDK non conosce. Vengono propagati così come sono in code invece di essere accorpati, quindi tratta un valore non riconosciuto come un fallimento generico.

In Python, FileStoreError viene importato da ironflock.filestore; in JavaScript viene esportato dalla radice del pacchetto (import { FileStoreError } from "ironflock").

Comunicazione tra Dispositivi

registerDeviceFunction / register_device_function

Registra una procedura che altri dispositivi nello stesso progetto possono chiamare. L’SDK assegna automaticamente il namespace della procedura al dispositivo corrente.

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

register() è un alias di register_device_function().

callDeviceFunction / call_device_function

Chiama una procedura registrata da un altro dispositivo. L’SDK assembla automaticamente il topic WAMP completo usando la chiave del dispositivo di destinazione.

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

call

Chiama una procedura remota usando un URI WAMP completo. Usalo per chiamate dirette quando conosci il topic esatto.

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

Metadati del Dispositivo

setDeviceLocation / set_device_location

Aggiorna la posizione GPS del dispositivo nella piattaforma. Le modifiche vengono riflesse in tempo reale sulle mappe IronFlock.

await ironflock.set_device_location(long=8.6821, lat=50.1109)
ParametroIntervallo
longda -180 a 180
latda -90 a 90

La cronologia della posizione non viene memorizzata. Per tracciare la posizione nel tempo, crea una tabella dedicata e usa publish_to_table / publishToTable.

getRemoteAccessUrlForPort

Restituisce l’URL pubblico di accesso remoto per una determinata porta sul dispositivo.

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

Proprietà della Connessione e Ciclo di Vita

ProprietàTipoDescrizione
is_connectedboolIndica se la connessione alla piattaforma è attiva
connectionCrossbarConnectionL’istanza di connessione sottostante (uso avanzato)
MetodoDescrizione
run()Avvia la connessione ed esegue mainFunc (bloccante)
await start()Avvia la connessione in modo asincrono
await stop()Interrompe la connessione e annulla i task in esecuzione
await run_async()Avvia e mantiene la connessione in esecuzione in modo asincrono

Gestione degli Errori

Ogni metodo dell’SDK fallisce in modo esplicito: in caso di argomenti non validi, di connessione persa o di rifiuto da parte della piattaforma, solleva un’eccezione (Python) o va in reject (JavaScript) con un messaggio che indica l’operazione, il topic e il motivo. Nulla viene silenziosamente ignorato, quindi racchiudi in un blocco try le chiamate che vuoi far sopravvivere agli errori.

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

In JavaScript, i fallimenti provenienti dalla piattaforma sono istanze di WampError — una normale sottoclasse di Error che trasporta in più l’URI dell’errore WAMP in error e il payload dell’errore in args / kwargs. Tutto il resto (parametri non validi, nessuna connessione) è un semplice Error.

Migrazione: le versioni precedenti dell’SDK registravano un messaggio nei log e restituivano None / null quando una chiamata falliva. Ora sollevano invece un errore, quindi il codice scritto come if result is None: non rileva più i fallimenti — usa try / except (o try / catch).

Utilizzo nel Browser (solo JavaScript)

L’SDK JavaScript funziona nei browser moderni. Poiché i browser non hanno variabili d’ambiente, passa tutta la configurazione tramite il costruttore:

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 }]);

Usa IronFlock.fromServer() per recuperare la configurazione dal tuo backend invece di inserire le credenziali nel codice:

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

L’endpoint del tuo backend deve restituire un oggetto JSON con le opzioni di connessione (serialNumber, deviceKey, appName, swarmKey, appKey, env).

Registrazione delle Funzioni per Agenti AI

L’SDK può registrare funzioni chiamabili dagli agenti AI. Registra una procedura e fai riferimento al suo topic nel tuo 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)

L’agente AI può quindi chiamare questa funzione quando un utente fa una domanda che richiede dati live dai sensori.

Per collegare il topic WAMP registrato a un agente AI, fai riferimento ad esso nel file .ironflock/ai-template.yml della tua app:

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

Il valore di topic (sensors.get_latest) deve corrispondere al nome passato a register_device_function / registerDeviceFunction nel codice edge. IronFlock instrada automaticamente la chiamata al dispositivo in cui la funzione è registrata.

Per il riferimento completo di ai-template.yml, consulta Definire Agenti e Strumenti.

Variabili d’Ambiente

Queste variabili vengono impostate automaticamente dal runtime IronFlock all’interno dei container dell’app:

VariabileDescrizione
DEVICE_NAMENome visualizzato del dispositivo
DEVICE_SERIAL_NUMBERIdentificatore univoco e immutabile del dispositivo
DEVICE_KEYChiave del dispositivo per l’autenticazione
SWARM_KEYIdentificatore del progetto
APP_KEYIdentificatore dell’app
APP_NAMENome dell’app
ENVAmbiente: DEV o 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