データバックエンド
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 | オプションの説明。UIに表示され、AIエージェントがテーブルを理解するために使用します |
chunkTimeInterval | テーブルを分割する時間パーティションのサイズ。デフォルトは7 days |
dropAfter | 保持期間 — これより古いパーティションは自動的に削除されます |
downsample | 長期ウィンドウのチャートを高速化するための事前集計済みコピーを維持します — 後述の継続的ダウンサンプリングを参照 |
maintainLatestFlagFor | 一意のエンティティを識別するカラム — 後述のエンティティの最新状態の追跡を参照 |
private | このテーブルを他のアプリから隠します — 後述の他のアプリとのデータ共有を参照 |
**chunkTimeInterval**は、時系列データをディスク上でどう分割するかを決めます。1つのパーティションが一度に問い合わせるデータ量とおおよそ一致するように選んでください。毎秒収集されるような高頻度データは小さなチャンク(分〜時間)が、変化の遅いデータは大きなチャンク(週)が適しています。これはアプリのデフォルト値にすぎず、プロジェクトの所有者は後から自身のデータバックエンドで調整できます。
**dropAfter**は、テーブルをローリングウィンドウに変えます。指定した期間より古いパーティションはまるごと削除されるため、行を1件ずつ削除するよりはるかに低コストです。クリーンアップジョブはdropAfter / 4の間隔で実行されるため、レコードはパーティションが削除されるまで、最大で期間の4分の1だけ有効期限を超えて残ることがあります。データを無期限に保持するにはdropAfterを省略してください。
いずれもPostgreSQLの間隔文字列を取ります — 30 minutes、1 hour、7 days、6 months。
継続的ダウンサンプリング
ダッシュボードはデータベースにデータの集計を依頼できます — 1時間ごとの平均、1日ごとの合計、機械ごとの件数など。これを生レコードから計算するのは、1日分であれば問題ありませんが、1年分となると高コストです。テーブルに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日を均等に分割する1 secondから1 dayまでの固定幅の間隔(1 minute、5 minutes、1 hourなど)を指定できます。デフォルトの1 minuteは実質的にあらゆるダッシュボードに適しています。バケットを粗くすると、ストレージと書き込みスループットのコストが下がります。
**keepFor**は、そもそも長期履歴を可能にするための設定です。生レコードはdropAfterによって消えていきますが、ダウンサンプリング済みのコピーは独自の保持期間を持ちます。生データを30日、ダウンサンプリング済みデータを2年間保持すれば、ボードはごく一部のストレージで2年分の1時間平均をチャート表示できます。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"
})高頻度データの場合は、1 行ごとにラウンドトリップを行う代わりに、publish_rows_to_table / publishRowsToTable(ファイア・アンド・フォーゲット)または append_rows_to_table / appendRowsToTable(挿入結果を返す)を使用して、複数の行を 1 つのメッセージで送信できます。各バッチはアトミックに挿入されます — オール・オア・ナッシングです。詳細については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の2つのオプションキーがこれを変えます。
他のアプリのデータを読み取るには、読み取り元のアプリをトップレベルの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呼び出しについては他のアプリのデータの利用を参照してください。