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
dataTypetimestampnumericstringbooleanのいずれか

テーブルオプション

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 minutes1 hour7 days6 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 minute5 minutes1 hourなど)を指定できます。デフォルトの1 minuteは実質的にあらゆるダッシュボードに適しています。バケットを粗くすると、ストレージと書き込みスループットのコストが下がります。

**keepFor**は、そもそも長期履歴を可能にするための設定です。生レコードはdropAfterによって消えていきますが、ダウンサンプリング済みのコピーは独自の保持期間を持ちます。生データを30日、ダウンサンプリング済みデータを2年間保持すれば、ボードはごく一部のストレージで2年分の1時間平均をチャート表示できます。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" })

高頻度データの場合は、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派生テーブルの名前
materializetrueの場合、結果がテーブルとして永続化される
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の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呼び出しについては他のアプリのデータの利用を参照してください。

Last updated on