Skip to Content
IoT 应用开发数据后端

数据后端

IronFlock 为每个项目配置一个由 TimescaleDB 驱动的私有数据库。您的应用定义数据模式;IronFlock 在设备加入应用的那一刻就创建表并开始采集数据。

工作原理

  1. .ironflock/data-template.yml 中定义数据模式。
  2. 使用 IronFlock SDK 从边缘代码发布数据。
  3. IronFlock 在安装该应用的每个项目中自动创建数据库表。
  4. 数据从设备通过消息系统流入项目数据库。

每个项目都有自己的物理数据库——项目之间不共享数据。

用户对其项目中由您的应用采集的数据拥有完全控制权。作为开发者,您无法访问这些数据。

定义数据模式

.ironflock/ 目录中创建 data-template.yml 文件:

data: tables: - tablename: sensordata columns: - id: tsp name: Timestamp description: Timestamp of measurement path: args[0].timestamp dataType: timestamp - id: temperature name: Temperature description: Temperature reading in Celsius path: args[0].temperature dataType: numeric - id: humidity name: Humidity description: Relative humidity percentage path: args[0].humidity dataType: numeric - id: device_id name: Device ID description: Source device identifier path: args[0].device_id dataType: string

列选项

字段描述
id内部列标识符(时间戳列使用 tsp
name人类可读的列名,显示在仪表板中
description可选描述
path发布的数据对象中值的路径(例如 args[0].temperature
dataType可选值:timestampnumericstringboolean

表选项

除了 columns 之外,表还接受若干可选键,用于控制表的描述方式以及数据的留存时长:

data: tables: - tablename: sensordata description: 车间的环境测量数据 chunkTimeInterval: 1 hour dropAfter: 30 days columns: # ...
字段描述
tablename表名
description可选描述,显示在界面中,并供 AI 智能体理解该表
chunkTimeInterval表被切分成的时间分区大小。默认为 7 days
dropAfter保留窗口——早于该区间的分区会被自动删除
downsample维护一份预聚合副本,用于快速绘制长窗口图表——参见下文持续降采样
maintainLatestFlagFor标识唯一实体的列——参见下文跟踪实体的最新状态
private对其他应用隐藏该表——参见下文与其他应用共享数据

chunkTimeInterval 决定时序数据在磁盘上的分区方式。请选择让单个分区大致对应一次查询数据量的值:每秒采集的高频数据适合较小的分块(分钟到小时),变化缓慢的数据则适合较大的分块(周)。这只是应用的默认值——项目所有者之后可以在自己的数据后端中调整。

dropAfter 会把表变成一个滚动窗口。早于所给区间的分区会被整块删除,这远比逐行删除便宜。清理任务按 dropAfter / 4 的周期运行,因此一条记录在其分区被移除之前,最多可能超出有效期该区间的四分之一。省略 dropAfter 即可无限期保留数据。

两者都接受 PostgreSQL 的区间字符串——30 minutes1 hour7 days6 months

持续降采样

仪表板可以要求数据库聚合数据——每小时平均值、每天合计、每台机器的计数。用原始记录来计算这些,跨一天没问题,跨一年就代价高昂。为表添加 downsample,平台就会为它维护一份持续更新的预聚合副本,并改用该副本回答长窗口查询:

data: tables: - tablename: sensordata dropAfter: 30 days downsample: bucket: 1 minute keepFor: 2 years paths: - payload.temperature columns: # ...
字段描述
bucket预聚合副本的粒度。默认为 1 minute
keepFor降采样历史的保留时长。省略则无限期保留
paths要纳入的 JSON 字段路径,采用与仪表板相同的表示法

bucket 是图表能够从该副本获取的最细分辨率——若图表请求的时间桶远细于此值,则改为读取原始表。它接受从 1 second1 day 之间能整除一天的固定宽度区间(1 minute5 minutes1 hour)。默认的 1 minute 几乎适用于所有仪表板;更粗的桶占用更少存储、也带来更小的写入吞吐开销。

keepFor 才是让长历史数据成为可能的关键。原始记录会随 dropAfter 消失,而降采样副本有自己的保留期:原始数据保留 30 天、降采样数据保留 2 年,看板便仍可用极小的存储代价绘制两年的每小时平均值。请把它设得比 dropAfter 更长——相反的配置会被平台判定为错误配置并拒绝。

paths 把降采样扩展到 JSON 列内部的值。数值列会自动纳入;而 JSON 字段必须显式指定,因为 JSON 列没有固定的键集合。未声明的字段在仪表板中仍然可用——只是改为从原始表计算。

其余一切都是自动的。每个数值列都会维护其统计量(平均值、总和、最小值、最大值、第一个值、最后一个值以及记录条数),并按表的实体键(maintainLatestFlagFor,或发布数据的设备)分组。仪表板无需任何配置,也无需知晓其存在:小部件照常查询,平台会针对每次查询判断预聚合副本能否作答——不能时便透明地回退到原始表,例如当某个过滤条件引用了副本未按其分组的列时。

**模式变更会重建副本。**为降采样表添加、删除或更改列的类型——或编辑 downsample 块本身——都会从原始表重建预聚合副本。任何早于 dropAfter 的数据都无法重建,将会丢失。请尽可能在建表时就一并配置该块,并把长期存在的表上的后续模式变更视为一项需要慎重权衡的决定。

从边缘代码发布数据

使用 IronFlock SDK 从应用发送数据:

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

对于高频数据,请使用 publish_rows_to_table / publishRowsToTable(即发即忘)或 append_rows_to_table / appendRowsToTable(返回插入结果)在单条消息中发送多行,而非每行进行一次往返通信。每个批次都会原子地插入——全部成功或全部失败。详情请参阅 SDK 参考

转换表

您可以定义 SQL 转换,自动对原始数据进行聚合或处理:

data: tables: - tablename: sensordata columns: # ... 原始数据列 ... transforms: - tablename: hourly_averages materialize: true schedule: "0 * * * *" sql: > SELECT time_bucket('1 hour', tsp) AS hour, avg(temperature) AS avg_temp, avg(humidity) AS avg_humidity FROM sensordata GROUP BY hour columns: - id: hour name: Hour dataType: timestamp - id: avg_temp name: Average Temperature dataType: numeric - id: avg_humidity name: Average Humidity dataType: numeric
字段描述
tablename派生表的名称
materialize若为 true,结果将持久化为表
schedule转换运行的 Cron 表达式
sql计算转换的 SQL 查询
columns输出的列定义

转换表可以在仪表板和 SDK 中访问,与常规表的使用方式完全相同。

跟踪实体的最新状态

对于表示真实世界实体(机器、资产、生产订单)当前状态的表,IronFlock 支持一种名为最新状态跟踪的模式。

当数据发生变化时,您不是覆盖原有行,而是始终追加新行。您声明哪些列用于标识一个唯一实体,IronFlock 会在每次读取该表时推导出每个实体的最新行。这让您既能保留每次变更的完整历史,又能轻松查询当前状态。

通过 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 接受一组列,这些列组合起来标识一个唯一实体。行本身不会被写入任何额外内容:IronFlock 会按该实体键加时间戳为表建立索引,并在查询时选出每个实体的最新行。因此,迟到或乱序到达的行绝不会遗留下过时的标记。

仅查询当前机器状态:

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

查看特定机器的完整历史:

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

您很少需要手写这条查询。连接到此表的仪表板组件在其过滤设置中提供 latest 开关,因此用户无需任何额外操作即可始终看到当前值。在 SDK 中,只需向 filterAnd 添加 {"latest": true} 即可请求同样的模式——参见 getHistory

**从 latest_flag 升级:**IronFlock 的早期版本会存储一个名为 latest_flag 的物理布尔列。该列已不再存在——当前状态改为在 SQL 中推导得出,这样即使行乱序到达也能保持正确。现有的仪表板和按 latest_flag = true 过滤的 SDK 调用仍可继续工作:IronFlock 会识别它们并应用最新状态模式。新代码应使用 latest 开关或 {"latest": true} 过滤条目。

软删除记录

IronFlock 的仅追加模型意味着记录永远不会被物理删除。取而代之的是,使用 deleted 布尔列将记录标记为已删除。这在从仪表板中隐藏已删除记录的同时,保留了完整的审计跟踪。

在任何实体表中添加 deleted 列:

- id: deleted name: Deleted dataType: boolean

当用户删除一条记录(例如通过仪表板上的表单),您的应用会为该实体发布一个 deleted: true 的新行。结合 maintainLatestFlagFor,这个新行将成为最新状态。

仅查询活跃(未删除)的当前记录:

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

deleted 检查是在选出每台机器的最新行之后才执行的。这个顺序很重要:如果先过滤掉已删除的行,前一行未删除的记录就会重新浮现,成为当前状态。

仪表板组件和 SDK 会自动应用相同的顺序——将 latest 开关(或 {"latest": true})与 deleted 过滤条件结合使用,就能得到完全一致的行为。已删除的记录在表单提交后会立即从面板中消失,但仍保留在数据库中,用于历史记录和审计目的。

与其他应用共享数据

数据后端仅属于你的应用:项目中安装的其他应用都看不到你的表。data-template.yml 中的两个可选键可以改变这一点。

要读取其他应用的数据,请在顶层的 consumes: 部分列出想要读取的应用——它与 data: 平级,而不是嵌在其中:

consumes: - app: machine-monitor reason: "根据该监控应用的机器状态和计数器数据流计算 OEE" data: tables: - tablename: oee_results columns: # ... 照常定义你自己应用的表

app 是提供方应用的技术名称,或用 "*"(引号是必需的)表示项目中的所有应用。reason 会在授权对话框中展示给用户——仅有声明本身不会授予任何权限,必须由用户批准。

要保留某些表不共享,将其标记为 private: true。你定义的内容默认都是可共享的;私有的表或转换根本不会出现在其他应用看到的目录中。

data: tables: - tablename: measurements # 共享(默认) columns: [ ... ] - tablename: calibration_state # 内部使用——其他应用永远不可见 private: true columns: [ ... ]

访问权限是只读的,由用户按项目授予,并可随时撤销。完整模型以及读取提供方应用历史数据和实时数据流的 SDK 调用,请参见消费其他应用的数据

Last updated on