Skip to Content
IoT 应用开发IronFlock SDK

IronFlock SDK

IronFlock SDK 让您的边缘应用程序能够与 IronFlock 平台交互。在已注册的设备上运行时,它会自动处理身份验证,并提供用于发布数据、查询历史记录、跨设备调用远程过程以及更新设备元数据的函数。

SDK要求
Pythonironflock(PyPI)Python 3.8+
JavaScriptironflock(npm)Node.js 18+ 或现代浏览器

安装

pip install ironflock

或将 ironflock 添加到应用的 requirements.txt 中。

快速开始

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

在 IronFlock 应用容器内使用时,SDK 会自动从环境中读取连接凭据——无需手动配置。

构造函数选项

ironflock = IronFlock( mainFunc=main, # async function to run after connecting serial_number="abc123" # override device serial (optional) )
参数描述
mainFunc连接建立后执行的异步函数
serial_number覆盖设备序列号。默认使用环境变量 DEVICE_SERIAL_NUMBER

发布数据

publishToTable / publish_to_table

向舰队表发布数据记录。表名必须与应用的 data-template.yml 中定义的表匹配。SDK 会自动将数据路由到正确的项目数据库。

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

appendToTable / append_to_table

使用远程过程调用而非发布/订阅方式向舰队表追加数据。当需要确认数据已持久化时使用此方法。

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

publishRowsToTable / publish_rows_to_table

单条消息中发布多行(批量插入)到舰队表。平台会在一次操作中原子地插入整个批次(全部成功或全部失败)。对于高频数据,如果每行都进行一次往返通信开销过大,请使用此方法。与 publishToTable 一样,这是即发即忘(fire-and-forget)的——确认信息仅表示消息已送达路由器,而非确认数据库已完成插入。

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

第二个参数是一个非空的待插入行对象列表。

appendRowsToTable / append_rows_to_table

通过单次远程过程调用追加多行(批量插入)到舰队表。平台会原子地插入整个批次(全部成功或全部失败):如果任何一行无效,整个批次都将被拒绝,且不会持久化任何数据。当您需要获知插入结果时,应优先使用此方法而非 publishRowsToTable / publish_rows_to_table

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

将应用程序错误报告到舰队的 error-logs 表中。这是对 publishToTable / appendToTable 的便捷封装:它会为该行打上 source: "app" 标记、一个严重级别 level 以及一个时间戳,然后像写入任何普通表行一样将其写入。该错误会落入与 fleetdb 系统错误相同的 error-logs 表中(系统错误标记为 source: "system"),因此可以使用 getHistory 查询,使用 subscribeToTable / subscribe_to_table 流式订阅,在看板模板中使用,并通过 transformed.error-logs 实时传递——而不会触发平台的系统错误提示框。

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

参数:

参数类型描述
errorstr / string 或异常 / Error错误消息,或一个异常对象,其 traceback/stack(或消息)将被记录
levelstr / string,可选严重级别:"error""warn""info""debug"。默认为 "error"
appendbool / boolean,可选当为 true 时,使用 append 远程过程调用(返回插入结果)。默认为 false(即发即弃式发布)
tspstr / string,可选ISO-8601 时间戳覆盖值。默认为当前时间

在 Python 中,这些选项以关键字参数形式传入(report_error(error, level=..., append=..., tsp=...));在 JavaScript 中,它们通过一个选项对象传入(reportError(error, { level, append, tsp }))。

publish

向任意 WAMP 主题发布消息。用于不映射到数据库表的自定义消息传递或事件。

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

查询历史数据

getHistory

从舰队表中检索历史数据。支持过滤、时间范围和分页。

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

查询参数:

字段类型描述
limitint / number返回的最大行数(1–10,000,必填)
offsetint / number分页偏移量
timeRangedict / object{"start": "<ISO 日期时间>", "end": "<ISO 日期时间>"}
filterAndlist / arrayAND 过滤条件,以及/或 latest 标记(见下文)
columnslist / array要返回的列(可选)。tspdevice_keyauthid 始终包含在内;省略则返回所有列

过滤运算符: =, !=, >, <, >=, <=, LIKE, ILIKE, IN, NOT IN, IS, IS NOT

每个过滤条件是包含 columnoperatorvalue 键的对象。

读取当前值。 filterAnd 中的 {"latest": true} 条目并不是一个过滤条件,而是一个模式开关:数据后端只返回每个实体的最新行,该结果由表通过 maintainLatestFlagFor 声明的实体键在 SQL 中推导得出。没有实体键的表则返回其唯一的最新一行。

其他条件与该标记的组合方式符合预期:作用于实体键列的条件用于缩小返回哪些实体的范围,而所有其他条件和 timeRange 都会应用到得出的最新行上。因此,将 {"latest": true}deleted 过滤条件结合使用,会隐藏已删除的实体,而不会让它们此前的行重新浮现。

IronFlock 的早期版本会存储一个物理的 latest_flag 列。该列已不再存在——旧式的 latest_flag = true 过滤条件仍被接受并按该标记处理,但新代码应使用 {"latest": true}latest 标记在 getSeriesHistory 中不可用。

getSeriesHistory / get_series_history

从舰队表中检索降采样的时间序列数据:将数值列聚合到时间桶中(例如每小时平均值)。非常适合跨越长时间范围的图表。适用于表(不适用于 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"] })

查询参数:

字段类型描述
metricslist / array要降采样的数值列
methodstr / string每个时间桶的聚合:"AVG""SUM""COUNT""MIN""MAX""FIRST""LAST"
limitint / number桶的最大数量(1–10,000)
timeRangelist / array[start, end] — ISO 日期时间字符串或 epoch 毫秒数字;null = 开区间(必填)
groupBylist / array用于对序列分组的列(可选)
filterAndlist / arrayAND 过滤条件(可选)。仅支持过滤条件——此处不支持 latest 标记;要读取当前值请使用 getHistory

订阅数据

subscribeToTable / subscribe_to_table

订阅舰队表的实时更新。每当有新数据发布到表中时,处理函数将被调用。通过批量插入路径(publishRowsToTable / appendRowsToTable)写入的行会逐行依次传递给您的处理函数,因此无论数据以何种方式写入,处理函数的代码都保持不变。

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

subscribe

订阅任意 WAMP 主题以实现自定义实时消息传递。

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

跨应用数据访问

在同一个项目中,从你自己的应用内部读取另一个应用的舰队数据。提供方应用必须在其 data-template.ymlconsumes: 部分声明你的应用,并且项目用户必须授予访问权限。访问是只读的:你可以查询提供方共享的表和 transform 的历史数据,并实时订阅其行,但不能写入它们。对被消费应用的连接会按应用缓存,并在你的实例停止时自动关闭。

如果你的应用持有通配符授权(consumes: [{ app: "*" }]),你可以使用 listConsumableApps / list_consumable_appsconnectToAllApps / connect_to_all_apps(见下文)动态地发现并打开提供方。

connectToApp / connect_to_app

打开到另一个应用数据后端的只读连接,并返回一个句柄。该句柄提供 getHistory / get_historysubscribeToTable / subscribe_to_tablegetSeriesHistory / get_series_history(仅限表)——与你在自己的表上使用的查询和订阅完全相同——此外还提供 close 以及共享的 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)

参数:

参数类型描述
app_name / appNamestr / string提供方应用的名称,即你在 consumes: 部分中声明的名称
stagestr / string,可选提供方的阶段(stage):"dev""prod"。默认为你自己应用的阶段
on_error / onErrorcallable,可选如果在连接建立之后访问被拒绝(例如授权随后被撤销),则会以一个 CrossAppAccessError 调用它

如果访问被拒绝或使用不当,将引发(Python)/抛出(JavaScript)一个带有 code 字段的 CrossAppAccessErrorNO_GRANTPROVIDER_NOT_INSTALLEDUNKNOWN_APPPRIVATE_TABLENOT_AUTHORIZED

在 Python 中,stageon_error 是关键字参数;在 JavaScript 中,它们通过一个选项对象传入(connectToApp(appName, { stage, onError }))。

listConsumableApps / list_consumable_apps

列出项目中每一个非私有的提供方——这是为持有通配符消费授权(在你的 data-template.yml 中声明 consumes: [{ app: "*" }],并由项目用户授予)的应用提供的发现原语。它只执行一次调用,且打开任何连接:将返回的目录渲染到一个选择器中,然后对你想要的提供方调用 connectToApp / connect_to_app——或使用 connectToAllApps / connect_to_all_apps 一次性打开所有提供方。

注意:在你应用的 data-template.yml 中声明该授权,并* 加上引号——不带引号的 * 是一个 YAML 别名,无法解析:

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

每个条目描述一个提供方:

字段类型描述
appstr / string提供方应用的名称
provider_app_keyint / number提供方的应用密钥
stagesdict / object按阶段(stage)划分的目录 { dev?, prod? };只有当提供方针对该阶段有数据后端时,该阶段才会出现。每个目录包含它所共享的非私有 tablestransforms

如果你的应用没有持有通配符授权,将引发(Python)/抛出(JavaScript)一个带有 code: NO_GRANTCrossAppAccessError

connectToAllApps / connect_to_all_apps

在一次调用中打开到项目中每一个非私有提供方的只读句柄(仅限通配符消费方)。它通过 listConsumableApps / list_consumable_apps 枚举提供方并逐个打开,跳过任何针对所请求阶段没有数据后端的提供方。每个句柄都以与 connectToApp / connect_to_app 相同的键进行缓存,因此之后调用 connectToApp(name) 会返回那个已经预热好的句柄。返回的这些句柄会在你的实例停止时一起关闭。

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)

参数:

参数类型描述
stagestr / string,可选提供方的阶段(stage):"dev""prod"。默认为你自己应用的阶段
continue_on_error / continueOnErrorbool / boolean,可选当为 true 时(默认值),打开失败的提供方会被报告给 on_error / onError 并从结果中省略。当为 false 时,第一个失败会被引发/抛出
on_error / onErrorcallable,可选对每个无法打开的提供方调用它(当 continue_on_error / continueOnErrortrue 时),以及在一个已经打开的连接随后被拒绝(例如授权被撤销)时,以一个 CrossAppAccessError 调用它

返回成功打开的提供方句柄(与 connectToApp / connect_to_app 的句柄类型相同)。如果你的应用没有持有通配符授权,将引发(Python)/抛出(JavaScript)一个带有 code: NO_GRANTCrossAppAccessError

在 Python 中,stageon_errorcontinue_on_error 是关键字参数;在 JavaScript 中,它们通过一个选项对象传入(connectToAllApps({ stage, onError, continueOnError }))。

托管文件存储

每个应用的数据后端在表之外还会获得一块私有的对象存储,通过 files 属性访问。可以用它来存放图片、PDF、相机帧、固件二进制块——任何不适合放进表行里的内容。无需任何配置:即使应用的数据模板中没有 files: 段,它仍会获得一个名为 default 的命名空间。

其核心思路在于:存储一个对象时会直接返回一个永久 URL,你可以把它原样写入表的某一列,这样仪表板组件无需任何额外工作就能把它渲染出来:

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

该 URL 永不过期,但它不是公开链接:只有在此数据后端上持有 READ 权限、且已通过身份验证的请求方才能读取它,鉴权代理会在每一次请求时重新校验这一点。因此把它存入数据库是安全的。

命名空间

命名空间是一个带有策略的键前缀——保留期、共享规则、允许的内容类型。它并不是一个独立的存储桶;一个应用的所有命名空间都位于该应用唯一的那块存储区域之内。只有当一组对象需要不同的规则时才去声明它;否则就留在 default 中,并用类似 2026/03/part-1.jpg 这样的键路径来组织你的对象。

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 }

请注意,该预算是为整个应用声明一次的,而不是按命名空间声明的。命名空间只是应用那块唯一存储区域内部的一个键前缀,因此并不存在什么东西可以让按前缀划分的预算去强制执行。maxObjectBytes确实是按命名空间生效的——它限制的是单个对象的大小,而不是总量。

下面的每个方法都接受命名空间作为可选参数,默认为 default

存储与读取对象

# 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")
方法描述
put(key, data, …)存储一个对象(Python 中为 bytes,JavaScript 中为 Uint8Array)。返回该对象的元数据,其中包含它的 url
get(key, namespace?)返回该对象的内容
put_file(key, path, …) / get_to_file(key, path, …)仅限 Python。 从本地文件存储,或写入到本地文件。在大对象路径上以流式方式传输
delete(key, namespace?)删除一个对象
copy(key, to, …)复制一个对象,也可复制到另一个命名空间
move(key, to, …)先复制再删除。非原子操作——服务端没有 move 这个动作,因此一旦删除失败,两份副本都会留存

JavaScript 没有提供文件路径辅助方法,因为该包为 Node 和浏览器发布的是同一个构建产物——请自行使用 fs 读写本地文件。

put 接受: content_type / contentType(MIME 类型;命名空间可能会限制允许哪些类型)和 namespace。在 Python 中它们是关键字参数;在 JavaScript 中它们放在一个选项对象里。

列举与查看

方法描述
list(…)返回一页对象。包含 objectsprefixesis_truncated / isTruncated,以及一个 cursor——把它传回即可获取下一页
iter(…) / iterate(…)遍历某个前缀下每一个对象的异步迭代器,自动翻页。在 Python 中名为 iter,在 JavaScript 中名为 iterate
stat(key, namespace?)获取单个对象的元数据,而不传输其内容
exists(key, namespace?)某个对象是否存在
namespaces()本应用可以使用的命名空间
usage(…)本应用占用了多少存储空间——见下文
catalog()命名空间,以及服务器下发的各项限制和配额。首次调用后会被缓存

两个 SDK 用相同的字段来描述对象,只是各自遵循本语言的命名风格:namespacekeysizeetagcontent_type / contentTypelast_modified / lastModifiedchecksum_sha256 / checksumSha256url

存储用量与配额

usage 由对象存储在一次调用中直接给出答案,因此这些总量是精确的,而不是由 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}
字段含义
size_bytes / sizeBytes当前已存储的字节数
object_count / objectCount已存储的对象数量
quota_bytes / quotaBytes实际强制执行的预算。0 表示不限量
free_bytes / freeBytes剩余的字节数。-1 表示不限量——在这里报告 0 会被读作“已满”
per_namespace / perNamespace按命名空间划分的字节数。只有在你请求详细分项时才会出现

按命名空间划分的分项默认是关闭的,因为对象存储无法直接回答这个问题:它是按存储区域来记账的,而命名空间只是一个前缀,所以 SDK 必须逐个列举每个命名空间才能把大小累加起来。需要的时候再去请求它,不要放在热路径上。

这里会出现两种不同的配额,把它们区分开是值得的。catalog() 会同时报告这两者:

字段含义
quota_bytes / quotaBytes实际被强制执行的值,读取自对象存储——也就是项目用户的设置
suggested_quota_bytes / suggestedQuotaBytes应用的数据模板所请求的值。如果它没有请求任何值,则为 0

只要用户调高或调低了应用的预算,这两者就会不同;这也正是为什么强制执行的值是从对象存储读取的,而不是取自模板——重新部署应用绝不能悄无声息地重置用户的选择。UI 可以把两者都显示出来(“应用建议 X,你设置的是 Y”)。强制执行时始终使用前者。

共享对象

链接有两种,二者的区别很重要:

方法有效期谁能读取它
url(key, …)永久只有在此数据后端上持有 READ 权限、且已通过身份验证的请求方——每次请求都会重新校验。可以安全地存入表的列中
share_url / shareUrl会过期(默认 15 分钟,上限由服务器强制限定)任何持有该链接的人。 使用它时不会再对授权做任何校验

share_url / shareUrl 是一种 bearer 凭据,即持有链接者即可访问:把它交给需要临时访问的人,不要把它存进数据库。凡是仪表板要渲染的内容,一律使用 url

在部署环境没有 HTTP 边缘入口时(例如一台仅有纯 HTTP 的一体机设备),url 会返回 None / undefined——这就是应当回退到 get 的信号。把对象的 etag 作为 version 参数传入,可以让浏览器以不可变(immutable)的方式缓存响应。

upload_url / uploadUrl 会签发一个会过期的 URL,它接受直接上传,并返回 urlmethodheadersexpires_in / expiresIn。请原样发送它返回的那些请求头,否则签名将无法通过校验。

大对象

SDK 会根据对象大小自动选择传输方式——没有任何需要配置的地方:

对象大小传输方式
不超过内联上限(目前为 6 MiB)经由消息路由器的一次调用
更大绕过路由器,通过 HTTPS 直接传输到对象存储

确切的上限由服务器在运行时给出,即 catalog() 中的 inline_max_bytes / inlineMaxBytes,因此无需发布新的 SDK 版本就能调高它。

仍有两个硬上限,二者都会报告 TOO_LARGE,并在原因说明中指出你触碰的是哪一个:

  • 5 GiB——对象存储的单次上传上限。分片上传尚未实现。
  • 在没有直连端点时的内联上限——物理隔离的一体机设备根本无法传输大对象。重试或改用更小的分块都无济于事,错误消息中也会这样写明。

直连路径要求设备能够访问对象存储主机,而不只是路由器。现场常见的两类故障各有专属的错误码,而不会看起来像是授权问题:PRESIGN_UNREACHABLE(代理只放行通往路由器的流量)和 CLOCK_SKEW(对象存储会拒绝时间偏差超过 15 分钟的请求——请检查设备上的 NTP)。

文件存储错误

每个文件操作都会引发(Python)/抛出(JavaScript)一个 FileStoreError,它携带一个稳定的 code 和一段人类可读的 reason请依据 code 分支判断,绝不要依据 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
错误码含义
NOT_AUTHORIZED调用方无权执行此操作
NO_SUCH_NAMESPACE该命名空间未在数据模板中声明
NO_SUCH_OBJECT该键不存在
TOO_LARGE超出单次调用的传输上限
OBJECT_TOO_LARGE超出该命名空间自身的 maxObjectBytes
QUOTA_EXCEEDED文件存储已满
CONTENT_TYPE_NOT_ALLOWED该命名空间限制了 contentTypes
NOT_SUPPORTED后端无法完成此操作
NOT_AVAILABLE此部署环境没有文件服务
PRESIGN_UNREACHABLE无法直接访问对象存储(是不是有代理?)
CLOCK_SKEW设备时钟偏差过大
INTERNAL其他所有情况

较新的服务器可能会引入本次 SDK 发布尚不认识的错误码。它们会作为 code 原样透传,而不会被归并,因此请把无法识别的值当作一般性失败来处理。

在 Python 中,FileStoreErrorironflock.filestore 导入;在 JavaScript 中,它由包的根导出(import { FileStoreError } from "ironflock")。

跨设备通信

registerDeviceFunction / register_device_function

注册一个同项目中其他设备可以调用的过程。SDK 会自动为当前设备的过程添加命名空间。

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

register()register_device_function() 的别名。

callDeviceFunction / call_device_function

调用另一台设备注册的过程。SDK 使用目标设备的密钥自动组装完整的 WAMP 主题。

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

call

使用完整的 WAMP URI 调用远程过程。当您知道确切的主题时,可使用此方法进行直接调用。

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

设备元数据

setDeviceLocation / set_device_location

更新设备在平台上的 GPS 位置。更改会实时反映在 IronFlock 地图上。

await ironflock.set_device_location(long=8.6821, lat=50.1109)
参数范围
long-180 到 180
lat-90 到 90

位置历史不会被存储。要跟踪位置随时间的变化,请创建专用表并使用 publish_to_table / publishToTable

getRemoteAccessUrlForPort

返回设备上指定端口的公共远程访问 URL。

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

连接属性与生命周期

属性类型描述
is_connectedbool平台连接是否处于活动状态
connectionCrossbarConnection底层连接实例(高级用法)
方法描述
run()启动连接并运行 mainFunc(阻塞式)
await start()异步启动连接
await stop()停止连接并取消正在运行的任务
await run_async()异步启动并保持连接

错误处理

每个 SDK 方法在失败时都会明确报错:遇到无效参数、连接丢失或平台拒绝时,它会引发异常(Python)或拒绝(JavaScript),并给出一条包含操作名称、主题和原因的消息。没有任何错误会被悄悄吞掉,因此请将您希望能够继续运行的调用包裹在 try 块中。

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

在 JavaScript 中,来自平台的失败是 WampError 实例——它是一个普通的 Error 子类,并额外在 error 中携带 WAMP 错误 URI,在 args / kwargs 中携带错误负载。其他所有情况(参数错误、未连接)都是普通的 Error

**升级说明:**较早版本的 SDK 在调用失败时会记录一条日志并返回 None / null。现在它们会改为引发错误,因此形如 if result is None: 的代码已无法再检测到失败——请改用 try / except(或 try / catch)。

浏览器使用(仅 JavaScript)

JavaScript SDK 可在现代浏览器中使用。由于浏览器没有环境变量,需要通过构造函数传入所有配置:

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

使用 IronFlock.fromServer() 从后端获取配置,而不是硬编码凭据:

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

后端接口应返回包含连接选项(serialNumberdeviceKeyappNameswarmKeyappKeyenv)的 JSON 对象。

注册 AI Agent 函数

SDK 可以注册供 AI Agent 调用的函数。注册一个过程并在 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)

当用户提出需要实时传感器数据的问题时,AI Agent 可以调用此函数。

要将注册的 WAMP 主题与 AI 代理连接,请在应用的 .ironflock/ai-template.yml 中引用它:

sensor_agent: tool_description: | 当用户询问传感器读数、设备实时数据或当前环境条件时, 请委托给此代理。 system_prompt: | 你是传感器数据专家。使用 get_current 获取任意传感器的最新读数。 在回答中始终包含单位。 main: true max_context_tokens: 30000 max_iterations: 5 tools: get_current: description: 返回传感器的最新读数。 topic: sensors.get_latest parameters: sensor_id: type: string description: 要查询的传感器标识符。 required: true

topic 的值(sensors.get_latest)必须与边缘代码中传递给 register_device_function / registerDeviceFunction 的名称一致。IronFlock 会自动将调用路由到注册了该函数的设备。

有关 ai-template.yml 的完整参考,请参阅定义代理与工具

环境变量

以下变量由 IronFlock 运行时在应用容器内自动设置:

变量描述
DEVICE_NAME设备显示名称
DEVICE_SERIAL_NUMBER唯一且不可变的设备标识符
DEVICE_KEY用于认证的设备密钥
SWARM_KEY项目标识符
APP_KEY应用标识符
APP_NAME应用名称
ENV环境:DEVPROD
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