数据后端
IronFlock 为每个项目配置一个由 TimescaleDB 驱动的私有数据库。您的应用定义数据模式;IronFlock 在设备加入应用的那一刻就创建表并开始采集数据。
工作原理
- 在
.ironflock/data-template.yml中定义数据模式。 - 使用 IronFlock SDK 从边缘代码发布数据。
- IronFlock 在安装该应用的每个项目中自动创建数据库表。
- 数据从设备通过消息系统流入项目数据库。
每个项目都有自己的物理数据库——项目之间不共享数据。
用户对其项目中由您的应用采集的数据拥有完全控制权。作为开发者,您无法访问这些数据。
定义数据模式
在 .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 | 可选值:timestamp、numeric、string、boolean |
表选项
除了 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 minutes、1 hour、7 days、6 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 second 到 1 day 之间能整除一天的固定宽度区间(1 minute、5 minutes、1 hour)。默认的 1 minute 几乎适用于所有仪表板;更粗的桶占用更少存储、也带来更小的写入吞吐开销。
keepFor 才是让长历史数据成为可能的关键。原始记录会随 dropAfter 消失,而降采样副本有自己的保留期:原始数据保留 30 天、降采样数据保留 2 年,看板便仍可用极小的存储代价绘制两年的每小时平均值。请把它设得比 dropAfter 更长——相反的配置会被平台判定为错误配置并拒绝。
paths 把降采样扩展到 JSON 列内部的值。数值列会自动纳入;而 JSON 字段必须显式指定,因为 JSON 列没有固定的键集合。未声明的字段在仪表板中仍然可用——只是改为从原始表计算。
其余一切都是自动的。每个数值列都会维护其统计量(平均值、总和、最小值、最大值、第一个值、最后一个值以及记录条数),并按表的实体键(maintainLatestFlagFor,或发布数据的设备)分组。仪表板无需任何配置,也无需知晓其存在:小部件照常查询,平台会针对每次查询判断预聚合副本能否作答——不能时便透明地回退到原始表,例如当某个过滤条件引用了副本未按其分组的列时。
**模式变更会重建副本。**为降采样表添加、删除或更改列的类型——或编辑
downsample块本身——都会从原始表重建预聚合副本。任何早于dropAfter的数据都无法重建,将会丢失。请尽可能在建表时就一并配置该块,并把长期存在的表上的后续模式变更视为一项需要慎重权衡的决定。
从边缘代码发布数据
使用 IronFlock SDK 从应用发送数据:
Python
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: stringmaintainLatestFlagFor 接受一组列,这些列组合起来标识一个唯一实体。行本身不会被写入任何额外内容: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 = falsedeleted 检查是在选出每台机器的最新行之后才执行的。这个顺序很重要:如果先过滤掉已删除的行,前一行未删除的记录就会重新浮现,成为当前状态。
仪表板组件和 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 调用,请参见消费其他应用的数据。