メインコンテンツまでスキップ

REST API (v1)

PlantPulse Edge の v1 REST API マニュアル。画面および外部システムが OPC、タグ、監視、外部送信ヘルスチェックを扱うための標準インターフェースです。

項目値
Base URLhttp(s)://<edge-host>/api/v1
Content-Typeapplication/json; charset=utf-8
認証API Key 強制 (edge.rest.api.auth=true) — X-API-Key / Authorization: Bearer、またはログインセッションで通過。?api_key= は 401 で拒否
Audit ログApiAccessLogFilter — すべての /api/* 呼び出しを一行で記録 (メソッド/パス/ステータス/持続時間/IP/ユーザー/auth モード)
メインコントローラーplantpulse.app.edge.api.v1.* (OpcAPI, TagAPI, AppAPI, SystemAPI, MonitoringAPI)
エラーエンベロープ{data, meta, errors} — filter/advice パスも同一
HTTPSedge.rest.api.https_only=true 基本。plain HTTP client の呼び出しは拒否、または Tomcat HTTPS リダイレクトポリシーに従う

認証 (ApiAuthFilter)​

app.properties から edge.rest.api.auth=true で有効化。キーは edge.rest.api.key であり、運用環境では EDGE_REST_API_KEY_FILE または EDGE_REST_API_KEY で注入します。

次の 3 つつのうち いずれか を満たせば通過します:

A. ログインセッション通過 (ブラウザ)​

すでに /login/form でログインしたユーザー — セッションに _USER_LOGIN 属性 (JSONObject) があれば ApiAuthFilter がそのまま chain.doFilter() します。画面(JSP) から ajax で呼び出すすべての /api/* が追加キーなしで動作します。

B. X-API-Key ヘッダー (推奨)​

EDGE_REST_API_KEY="$(tr -d '\r\n' < /run/secrets/edge-rest-api-key)"
cfg="$(mktemp)"
trap 'rm -f "$cfg"' EXIT
printf 'header = "X-API-Key: %s"\n' "$EDGE_REST_API_KEY" > "$cfg"
curl --config "$cfg" https://edge.example.com/api/v1/tag/TAG_LS_XBC_0001/value

C. Authorization: Bearer <key> ヘッダー​

EDGE_REST_API_KEY="$(tr -d '\r\n' < /run/secrets/edge-rest-api-key)"
cfg="$(mktemp)"
trap 'rm -f "$cfg"' EXIT
printf 'header = "Authorization: Bearer %s"\n' "$EDGE_REST_API_KEY" > "$cfg"
curl --config "$cfg" https://edge.example.com/api/v1/tag/TAG_LS_XBC_0001/value

D. ?api_key=<key> クエリパラメータ — 拒否​

URL にキーが平文で入り、アクセスログ/プロキシキャッシュ/ブラウザ履歴に残る可能性があるため、v1 では 401 で拒否します。ヘッダー方式(B/C)を使用してください。

失敗レスポンス​

HTTP/1.1 401 Unauthorized
Content-Type: application/json; charset=utf-8

{
"data": null,
"meta": {
"timestamp": 1778069486934,
"status": "ERROR",
"http_status": 401,
"request_id": "3b89b8f1-0fc1-4ab4-b4eb-d12dc314d500"
},
"errors": [
{
"code": "UNAUTHORIZED",
"message": "Invalid or missing X-API-Key."
}
]
}

キーの比較は constantTimeEquals() (timing-attack 防御)。

edge.rest.api.auth=false (開発 / 一時的)​

認証をオフにすると ApiAuthFilter が即座に chain.doFilter() し、外部からキーなしで呼び出し可能です。運用網では常に true を推奨します。


Audit ログ (ApiAccessLogFilter)​

すべての /api/* 呼び出しを一行ログで記録 — 認証失敗(401) も含まれ、監査トレイルを残します (フィルターチェーン順序: ApiAccessLogFilter → ApiAuthFilter)。

形式​

[API_ACCESS] method=GET path=/api/v1/tag/TAG_MS_1000/value query= status=200 duration=12ms
ip=100.127.147.98 user=admin auth=session ua=Mozilla/5.0...
フィールド意味
methodHTTP メソッド
pathrequest URI (クエリ含まず)
queryクエリ文字列 — 拒否される ?api_key=... 部分も *** でマスキング
statusHTTP レスポンスコード
duration処理時間 (ms)
ip基本は RemoteAddr。edge.rest.api.trust_x_forwarded_for=true である reverse proxy 配置でのみ X-Forwarded-For の最初の IP
usersession ユーザーなら user_id / apikey なら XXXX*** (len=16) (先頭4文字 + 長さ) / 匿名なら -
authsession (ブラウザログイン) / apikey (X-API-Key/Bearer) / deprecated_query (拒否された ?api_key= 試行) / none (キー欠落または匿名)
uaUser-Agent (80文字超過時に … で切り捨て)
error例外発生時に error=ClassName 追加

ログレベル分岐​

ステータスレベル意図
5xx または throwableERRORサーバーエラー — 即時アラーム
401, 403WARN認証/権限拒否 — 監査優先
その他 (2xx, 3xx, 4xx の 4xx 一般)INFO通常の呼び出し

活用​

  • 誰がキーを誤って送信しているか — grep '\[API_ACCESS\].*status=401' で 401 トレイルを確認
  • 5xx 回帰追跡 — grep '\[API_ACCESS\].*status=5' + error= フィールド
  • 呼び出し頻度 / ユーザー別集計 — auth=apikey/session で grouping、IP 別分析
  • 応答遅延スロークエリ — duration= の長いラインを抽出

キーの平文は どこにも ログに残りません — クエリマスキング + ユーザーフィールドの先頭4文字のみで識別。侵害事故分析 に十分な識別性と平文露出防止のバランスを維持しています。

フィルターチェーンの順序が重要

web.xml のマッピング順序:ApiAccessLogFilter が先で、次に ApiAuthFilter です。AccessLog が finally ブロックでステータスを記録するため、認証に失敗した 401 呼び出しもそのまま監査されます。順序が逆になると、401 呼び出しがアクセスログから漏れます。


レスポンスエンベロープ​

v1 REST API レスポンスは共通エンベロープで包みます (RestApiSupport, ApiEnvelopeWriter)。

正常レスポンス:

{
"data": { },
"meta": {
"timestamp": 1778069486934,
"status": "OK",
"request_id": "f818abd0-eb0f-4497-9b07-7ccc94b2e2d7"
}
}

エラーレスポンス:

{
"data": null,
"meta": {
"timestamp": 1778069486934,
"status": "ERROR",
"http_status": 400,
"request_id": "3b89b8f1-0fc1-4ab4-b4eb-d12dc314d500"
},
"errors": [
{
"code": "VALIDATION_FAILED",
"message": "Validation failed"
}
]
}

旧 RPC スタイルのエンベロープ (result, _session_id, _user_id) は REST v1 では使用しません。


1. 共通規約​

エンドポイントを確認する前に、すべての /api/v1/* が共に遵守することです。これを理解していないと、個別のエンドポイントドキュメントを読んでもクライアントの実装に誤りが生じます。

1.1 運用ヘッダー​

ヘッダー方向用途
X-API-Keyリクエスト認証。?api_key= クエリは 破棄(401)
Authorization: Bearer <key>リクエストAPI キーの代替
X-Request-Idリクエスト追跡用。レスポンス meta.request_id で echo されます。指定がない場合はサーバーが生成します
Idempotency-KeyリクエストPOST 重複防止. TTL 10分。同じキーで再試行するとキャッシュされたレスポンスと共に Idempotency-Replay: true が返されます
If-Matchリクエスト楽観的同時実行制御。GET レスポンスの ETag と異なる場合は 412
ETagレスポンスweak validator(W/"<hex>")。現在は GET /api/v1/opc/{opcId} のみ返します
LocationレスポンスPOST 作成(201) 時の新リソース URI
X-RateLimit-Limit · -Remaining · -Resetレスポンスレートリミット情報
Retry-Afterレスポンス429 の時に待機すべき秒数

レートリミットは edge.rest.api.rate_limit.enabled と edge.rest.api.rate_limit.per_second (デフォルト 1000) で調整します。

再試行を実装する場合は Idempotency-Key を使用してください

現場の回線は切断されることがあります。応答を受け取らなかった POST は「失敗した」のではなく「不明」です — 単に再送すると OPC が二重に作成される可能性があります。同じキーで送信すれば、ボックスが最初の結果をそのまま返します。

1.2 ページネーション​

リストエンドポイントの共通クエリです。

クエリデフォルト値説明
page00から始まるページ番号
size00 の場合は全件を返します。 1..1000 の場合はその分だけ切り出して返し、meta.pagination を埋めます

size が 1000 を超えるか負数、または page が負数の場合は 400 INVALID_RANGE となります。

1.3 ID ルール​

対象正規表現
site_id^SITE_[A-Z0-9_]{1,60}$
opc_id^OPC_[A-Z0-9_]{1,60}$
tag_id^TAG_[A-Z0-9_]{1,60}$

data_type は Boolean · Integer · Long · Float · Double · String の 6 種類のみです (Cassandra ENUM と一致)。opc_type はドライバが持つプロトコル定数のホワイトリストであり (LS · OPCUA · MODBUS · MODBUS_RTU · HTTP · MELSEC · S7 · EIP …)、リストの正本は GET /v3/api-docs です。ホワイトリスト外の値は field_errors[].reason = "not_in_whitelist" で拒否されます。


2. エンドポイント一覧​

全 52 件です。OpenAPI 正本はボックスの GET /v3/api-docs であり、画面で確認するには /public/swagger-ui/index.html を参照してください。

2.1 OPC — 8​

MethodURL説明レスポンス
GET/api/v1/opcOPC リスト + 実行時状態 + 60分推移200 · 400
POST/api/v1/opcOPC 登録。tag_list でタグを同時に登録可能です201 · 400 · 409
GET/api/v1/opc/{opcId}詳細 + ETag。?include=tags でタグ配列を同時に取得200 · 404
PUT/api/v1/opc/{opcId}修正(If-Match 対応)200 · 404 · 412
PATCH/api/v1/opc/{opcId}部分修正 — 送信したフィールドのみ変更200 · 400 · 404 · 412
DELETE/api/v1/opc/{opcId}OPC + 下位タグまで削除200 · 404 · 412
POST/api/v1/opc/{opcId}/start収集開始200 · 404
POST/api/v1/opc/{opcId}/stop収集停止200 · 404

POST 必須 7 フィールド — site_id · opc_id · opc_name · opc_type · opc_agent_ip · opc_agent_port (1~65535) · timecycle (100ms 以上)。違反は まとめて 返されます (field_errors[])。

tag_list でタグを同時に登録します。 接続一つにタグを数十個入れるのが一般的であるため、登録を二回に分けると OPC は存在するがタグがない中間状態 が残ります。一度に送信してください — OPC のみを送信し、後で POST /api/v1/opc/{opcId}/tag を使って一つずつ追加することも可能です。

2.2 Tag — 10​

MethodURL説明レスポンス
GET/api/v1/tag全タグリスト200 · 400
GET/api/v1/opc/{opcId}/tag特定 OPC のタグリスト200 · 400
POST/api/v1/opc/{opcId}/tagタグ単一登録201 · 400 · 409
GET/api/v1/tag/{tagId}タグ詳細200 · 404
PUT/api/v1/tag/{tagId}タグ修正200 · 404
DELETE/api/v1/tag/{tagId}タグ削除200 · 404
GET/api/v1/tag/{tagId}/value最新値。?fresh=true の場合はデバイスから再読み込み200 · 404 · 503
POST/api/v1/tag/{tagId}/value値書き込み + 書き込み後再読み込み(actual_read)200 · 400 · 404 · 503
POST/api/v1/tag-values:read複数タグのキャッシュ値を一括取得(最大 100 件)200 · 400
GET/api/v1/tag/{tagId}/points時系列照会 + ダウンサンプル200 · 400 · 500

POST 必須 6 フィールド — site_id · opc_id (パスから自動設定) · tag_id · tag_name · data_type · plc_address。

points のクエリ:from·to (epoch ms, from < to) · minutes (デフォルト 10, 11440) · limit (デフォルト 600, 15000 — 超過時は 等間隔に間引いて 提供)。

書き込み失敗のコードマッピング — タグなし 404 TAG_NOT_FOUND · 接続断 503 DISCONNECTED · 収集停止 503 OPC_STOPPED · 読み取り専用タグ 400 RW_VIOLATION · タイプ不一致 400 TYPE_MISMATCH · その他 500 INTERNAL_ERROR。

2.3 アプリ(Docker コンテナ) — 9​

MethodURL説明レスポンス
GET/api/v1/appコンテナリスト200 · 400
POST/api/v1/appイメージ pull 後 run201 · 400 · 409 · 502
GET/api/v1/app/{name}docker inspect 結果200 · 404
DELETE/api/v1/app/{name}削除(?image=true の場合はイメージまで削除)200 · 404
POST/api/v1/app/{name}/start開始200 · 404
POST/api/v1/app/{name}/stop中止200 · 404
POST/api/v1/app/{name}/restart再起動200 · 404
GET/api/v1/app/{name}/logs?lines=N最後の N 行(1~5000, デフォルト 200)200 · 404
GET/api/v1/app/{name}/statsCPU · メモリ · ネットワーク · ディスク スナップショット200 · 404

name および image は正規表現 + シェルメタ文字遮断 を通過する必要があります (; & | \ $ ( ) < > \ " ' 空白 ). memory_limit_mb 는 16 이상, cpu_limit 은 0.1 이상, network 는 bridge · host · none` です。

配備失敗は段階的に通知されます — pull 失敗 502 DOCKER_PULL_FAILED, run 失敗 502 DOCKER_RUN_FAILED, 同名コンテナが存在すれば 409 CONTAINER_EXISTS。

2.4 システム — 12​

MethodURL説明認証
GET/api/v1/system/healthliveness + コンポーネント 8 種類 確認不要
GET/api/v1/system/readyreadiness(監視 + V5 クライアント)不要
GET/api/v1/system/versionバージョン · ビルド日 · イメージタグ · コンテナ有無不要
GET/api/v1/system/timeボックスの現在時刻(epoch ms)必要
POST/api/v1/system/time/syncプラットフォーム時刻に合わせます ({"platform_time":…})必要
GET/api/v1/system/logs最新ログを JSON で取得必要
GET/api/v1/system/logs/downloadログディレクトリを tar.gz で取得必要
GET/api/v1/system/eventsSSE ストリーム (text/event-stream)必要
GET/api/v1/system/upgrade/checkダウンロードサーバーの最新バージョンと比較必要
POST/api/v1/system/upgradeアップグレードトリガー必要
GET/api/v1/system/upgrade/statusアップグレード進行状態(polling)必要
POST/api/v1/system/firmwareファームウェアアップデートトリガー必要
POST/api/v1/system/reboot5秒後ホスト再起動強制
POST/api/v1/system/restart3秒後エッジエージェント再起動強制
reboot · restart は認証を«オフにできません»

edge.rest.api.auth=false で認証をオフにしていても、この 2 つだけは セッションログインまたは API キーを要求します。 誰でも呼び出せてしまうと、それ自体がサービス拒否攻撃となるためです。

2.5 モニタリング · 엣지 — 4​

MethodURL説明
GET/api/v1/monitoring稼働時間 · OPC 数 · 登録タグ · スキャン速度
GET/api/v1/edgeedge_id · site_id · バージョン · ホスト名 · モード
GET/api/v1/edge/statusエッジ状態サマリー
GET/api/v1/transfer送信先(MQTT · SparkplugB · プラットフォーム API)別ステータス

monitoring はブート直後にモニタが存在しない場合、503 MONITOR_NOT_READY を返します — これは故障ではなく「まだ準備中」であることを意味します。

2.6 二重化(HA) — 4​

MethodURL説明
GET/api/v1/ha/statusこのエッジの書き込み権限 · モード(manual/auto) · 開始時刻 · 相対ボックスの状態
PUT/api/v1/ha/write-authorityこのエッジの 書き込み権限を上げまたは下げます ({"enabled":true})
PUT/api/v1/ha/failover/override自動フェイルオーバーへの手動介入 — auto モードでのみ可能(それ以外は 409 NOT_AUTO_MODE)
POST/api/v1/ha/definitionsタグ定義を相対ボックスに複製

書き込み権限 — 読み取り前に知っておくべきこと​

何を制限しますか。 権限のないエッジは 実 PLC への書き込みを拒否します (WRITE_NOT_AUTHORIZED)。設定キーは edge.plc.write_authority であり、ない場合は true となります — 設置直後のボックスは 権限を持った状態で開始します。

HTTP 収集はこのゲートを通過しません — ただし «常に» ではありません

opc_type が HTTP である接続はデバイスへ送り出す書き込みではなく 入ってくる収集 です。そのため、権限を下げたエッジでも HTTP 収集は継続されます — 二重書き込みの危険がないためです。

⚠ ブート直後はその HTTP 書き込みも一時的に拒否される場合があります。 ゲートは opc_type をキャッシュから読み込みますが、キャッシュがまだ空であるか照会に失敗した場合、「実 PLC」と見なして遮断します (安全側に倒す意図的な動作です)。また、その照会失敗はログに残りません。

したがって、「HTTP は継続されるはずなのに拒否された」場合は 起動直後かどうかをまず確認してください。 キャッシュが埋まれば自然に解消されます — 設定を修正する問題ではありません。

🔴 この API は «転送しません» — 上げと下げの二回の独立した呼び出しです

この呼び出しは 自ボックスのみ を変更します。相手に問い合わせず、ロックもせず、譲受もしません。調停するコードは存在しません。

そのため、二つのボックスが同時に権限を持つ状態が実際に起こり得ます。 次の順序を守ってください —

  1. A を下げる — PUT /api/v1/ha/write-authority {"enabled":false}
  2. A が false であることを確認する — GET /api/v1/ha/status
  3. B を上げる

逆に行うと、その間に 両方が true となる期間 が生じ、その期間に二つのボックスが同じ PLC に書き込みます。

GET /api/v1/ha/status の peer.write_authority は相手の値を 表示 はします。しかし、それは 観測であり強制ではありません — 相手が権限を持っていても、この呼び出しは遮断されません。

persisted:false は 「失敗」ではなく「再起動すれば消える」です

トグルは二段階です。① ライブ反映 — ゲートが即座に新しい値を確認します (再起動不要)。② app.properties 永続化 — これが失敗しても ① は 維持され persisted:false と報告されます。

つまり、今は適用されたが、次回の再起動時に古い値に戻ります。 権限を下げたと信じて再起動したが、再び上がっている状態が現場で最も陥りやすいミスです。再起動前に原因を解決してください — 多くの場合 /etc/kopens/conf の書き込み権限です。診断はレスポンスではなく サーバーログ にあります:

[HA] write-authority persist to app.properties failed — applied live only

since は 「最後に制御権が移動した時刻」です。 値が 実際に変更された場合にのみ 更新されるため、同じ値を再度送信しても動きません。

監査は常に残ります — 変更があれば WARN、変更がなくても INFO に [no-op] を付与して記録し、両方とも呼び出し者の IP とアカウントを含めます。

[HA][AUDIT] write-authority false -> true by 10.0.0.5 (admin)

⚠ 推移履歴とプラットフォーム診断プッシュは 変更時のみ であり、その二つが失敗してもトグルは壊れません (ログにのみ残ります)。「履歴にない」ことが「変更されていない」ことを意味しません — 確認は status で行ってください。

エッジが自ら知らせる二つの状態。 ペアを監視する側が不一致の状態をログに残します。設定を自動で修正することはありません — 運用者が行います。

[HA] DUAL WRITE AUTHORITY detected — both edges have edge.plc.write_authority=true.
[HA] NO WRITE AUTHORITY — both edges have edge.plc.write_authority=false.

後半は どこでも PLC 書き込みができない 状態です — 「両方下げた後、上げるのを忘れた」場合に相当します。

フェイルオーバー手動介入 — PUT /api/v1/ha/failover/override​

自動フェイルオーバーが このエッジに対して判断することを停止し、役割を固定します。解除後に自動判断が再開されます。

role意味
"WRITER"ハートビートを継続的に送信し 権限を保持
"STANDBY"書き込みを停止し 権限を放棄
null介入を解除 — 自動判断に戻す
{ "data": { "override": "WRITER", "previous": null }, "meta": { "status": "OK" } }
  • auto モード専用 です。自動フェイルオーバーが動作していない場合は 409 NOT_AUTO_MODE となります — 前述の write-authority は両モードで利用可能なため、手動運用中の場合はそちらを使用してください。
  • WRITER·STANDBY は 大文字小文字を区別しません。
  • 値が三つのいずれでもない場合は 400 VALIDATION_FAILED となり field_errors:[{field:"role", reason:"invalid_value"}] が返ります。

時刻同期 — POST /api/v1/system/time/sync​

{ "platform_time": 1789780169674 }

epoch ミリ秒 (正数) であり、数値または数値形式の文字列 のいずれでも受け付けます。platform_time を省略すると ボックスに設定された NTP 原本から 同期します — 値を送信することと省略することでは動作が異なります。

レスポンスは {"applied":true,"method":"system","before":…,"after":…} の形式です。before/after を比較すると、実際にどの程度ずれていたか が分かります。

認証が«検証より先»です

システムを変更する呼び出しであるため、本文を見る前に認証から確認します。キーやセッションがなければ 401 であり、認証後に本文が正しくない場合は 400 VALIDATION_FAILED となります。順序が逆の API と混同しないでください — 誤った本文に対して 401 が返ってきた場合、それは本文の問題ではありません。

2.7 バックアップ · 復旧 — 2​

MethodURL説明
POST/api/v1/connect/backupすべての OPC + すべてのタグを 単一の JSON で出力
POST/api/v1/connect/restoreその JSON をそのまま適用(upsert)

対称的です — backup レスポンスをそのまま restore 本文として送信してください。restore レスポンスの skipped[] を必ず確認してください。個別行が失敗しても 全体が失敗せず、スキップされた後にカウントのみが減少します。

2.8 計算式 — 1​

MethodURL説明
POST/api/v1/tags/formula/preview計算式を保存する前に結果を事前に確認 — 計算式

2.9 認証なしで開かれるネット​

ApiAuthFilter を通過するのは このネットのみ です。いかなる OPC・タグ情報も出力しません。

GET /api/v1/system/health · GET /api/v1/system/ready · GET /api/v1/system/version · GET /api/health (バージョンなし別名)

«認証不要» とは、キーを検証できないという意味です

このネットは X-API-Key がなくても、あるいは 間違っていても 200 を返します。そのため、キーが正しく変更されたか確認する際にこのパスを使用してはいけません — GET /api/v1/opc のように キーが必要なパス で確認してください。

/health は 8 マス を確認します。8 マスすべてが UP であれば status=UP + 200、一つでも異なれば status=DEGRADED + 503 となります。

{
"data": {
"status": "UP",
"uptime_ms": 1720008,
"components": {
"cassandra": "UP", "redis": "UP", "mqtt": "UP", "node_red": "UP",
"opc_ua": "UP", "edge_core": "UP", "tse": "UP", "grafana": "UP"
}
},
"meta": { "timestamp": 1778925572946, "request_id": "..." }
}

🔴 503 の場合でも components がそのまま返されます。 それが診断のすべてです — ステータスコードだけを見て本文を捨てると、8 つのうち何が停止しているか知る術がありません。

三つのマスは名称から誤解しやすいため別途記載します。

マス実際に確認すること
edge_corereadiness です — 収集機・モニタが起動しているか。ブートレース中であれば DOWN となり、そのため health が 503 になります。プラットフォーム接続とは無関係 なため、STANDALONE ボックスでも意味を持ちます
opc_uaポートは 固定ではありません。 app.properties の opc.ua.server.tcp.port に従います (開発ボックスは 4840, デフォルトは 12000)。以前に 12000 でハードコーディングした結果、開発ボックスが永久に DOWN と報告された事例があります
grafana2026-07 イメージから HTTPS となります。平文 TCP でアクセスすると Grafana が 10秒ごとに TLS handshake error … EOF をログに残します — そのためボックスは ハンドシェイクを完遂するプローブ を先に使い、旧スタック向けに TCP 確認をフォールバックとして保持しています

cassandra(9042) · redis(6379) · mqtt(1883) · node_red(1880) · tse(7800) は固定ポートの TCP 確認です。

/version 例:

{
"data": {
"product_name": "PlantPulse Edge",
"version": "2026",
"build_date": "20260523",
"image_tag": "2026-20260523",
"container_mode": true
},
"meta": { "timestamp": 1778925572946, "request_id": "..." }
}

OTA upgrade.sh が /api/health を 300秒間呼び出し 自動ロールバック を判断し、Docker HEALTHCHECK もこのパスを使用します。

2.10 API ではないもの — /ui/opcua/* · /config/*​

ボックスには画面が使用するパスも存在します。新しいクライアントは使用しないでください。

  • /ui/opcua/info · /tree · /history — 内蔵 OPC UA サーバー画面専用です。製品の v1 API 正本ではこれらを 廃止(Deprecated) と宣言しました。コードはまだ応答しますが、正式な契約ではありません。
  • /config/load · /save · /restart · /reboot · /upgrade · /firmware · /backup · /temp-clean · /platform/test — 管理画面が使用するパスです。同様の機能を持つ /api/v1/system/* があるため、そちらを使用してください — エンベロープもエラーコードも v1 規約に従います。
  • /api/http(APIController) — 廃止。POST /api/v1/tag/{tagId}/value を使用してください。

3. リクエスト / レスポンス例​

以下はすべて 現行エンベロープ です。旧ドキュメントにあった {"result":"OK", ...} 形式の例は 製品と一致しないため削除しました — その形式で設計されたクライアントは現在動作しません。

3.1 OPC 登録​

curl -sk -X POST "https://<엣지>/api/v1/opc" \
-H "X-API-Key: <key>" -H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"site_id": "SITE_00001",
"opc_id": "OPC_LS_XBM_0001",
"opc_name": "1라인 LS XBM",
"opc_type": "LS",
"opc_agent_ip": "192.168.0.10",
"opc_agent_port": 2004,
"timecycle": 1000,
"tag_list": [
{ "site_id": "SITE_00001", "opc_id": "OPC_LS_XBM_0001",
"tag_id": "TAG_LS_XBM_0001", "tag_name": "1호기 토출압력",
"data_type": "Float", "plc_address": "%MW100" }
]
}'

tag_list は 省略可能 です — その場合、OPC のみが作成されます。

201 + Location: /api/v1/opc/OPC_LS_XBM_0001:

{
"data": { "info": { "opc_id": "OPC_LS_XBM_0001", "opc_name": "1라인 LS XBM" } },
"meta": { "timestamp": 1778925572946, "status": "OK", "request_id": "..." }
}
opc_agent_port と timecycle は数値で送信可能です

ストレージの該当カラムが文字列であるため、サーバーが受け取って変換します。数値で送信し文字列で返されても異常ではありません。

3.2 削除​

{
"data": { "deleted": true, "opc_id": "OPC_X" },
"meta": { "timestamp": 1778925572946, "status": "OK", "request_id": "..." }
}

DELETE も 200 + 本文 です。Swagger 注釈に 204 と記載されていても、実際の動作は 200 です — v1 ではすべてのメソッドがエンベロープを返します。

3.3 失敗のレスポンス形式​

POST /api/v1/opc/OPC_NONE/start — 404:

{
"meta": { "timestamp": 1778925572946, "status": "ERROR", "request_id": "..." },
"errors": [ { "code": "OPC_NOT_FOUND", "message": "OPC OPC_NONE 가 존재하지 않습니다." } ]
}

data が全く存在しません (null フィールドはシリアライズ時に省略されます)。data の有無で成否を判断せず、HTTP ステータスまたは meta.status で判断してください。

3.4 複数タグ値の一括取得​

curl -sk -X POST "https://<엣지>/api/v1/tag-values:read" \
-H "X-API-Key: <key>" -H "Content-Type: application/json" \
-d '{ "tag_ids": ["TAG_LS_XBM_0001", "TAG_LS_XBM_0002"] }'
{
"data": [
{ "tag_id": "TAG_LS_XBM_0001", "value": "1234", "timestamp": "2026-05-15T11:32:45.000Z", "quality": "SUCCESS" },
{ "tag_id": "TAG_LS_XBM_0002", "value": null, "timestamp": null, "quality": "PENDING" }
],
"meta": { "timestamp": 1778925572946, "status": "OK", "request_id": "..." }
}

🔴 quality を確認してください。 PENDING は「値が 0」ではなく 「まだ一度も読み取られていない」 状態です。これを 0 と読み取ると、誰も確認していない設備が「正常」として報告されます。

配列が空であるか 100 件を超える場合は 400(MISSING_FIELD / TOO_MANY) となります。

3.5 再起動​

{
"data": { "message": "reboot scheduled in 5 seconds" },
"meta": { "timestamp": 1778925572946, "status": "OK", "request_id": "..." }
}

202 です — 「完了した」のではなく 「することにした」 という意味です。レスポンスを受け取った時点ではまだ生存しています。

3.6 準備未完了​

GET /api/v1/system/ready — 503:

{
"meta": { "timestamp": 1778925572946, "status": "ERROR", "request_id": "..." },
"errors": [ { "code": "NOT_READY", "message": "엣지의 일부 구성요소가 준비되지 않았습니다." } ]
}

3.7 バックアップと復旧の対称性​

{
"data": {
"opcs": [ { "opc_id": "OPC_LS_XBM_0001", "opc_name": "..." } ],
"tags": [ { "tag_id": "TAG_LS_XBM_0001", "opc_id": "OPC_LS_XBM_0001" } ],
"count": { "opc": 1, "tag": 1 },
"exported_at": 1778925572946
},
"meta": { "timestamp": 1778925572946, "status": "OK", "request_id": "..." }
}

data をそのまま POST /api/v1/connect/restore の本文として送信してください。復旧レスポンスの skipped[] を必ず確認してください — 個別行が失敗しても全体は 200 となり、スキップされた分だけがそこに記載されます。count のみを見ると、静かに不完全な復旧となります。


4. エラーコード​

🔴 v1 では失敗を HTTP ステータスで伝えます。 旧 RPC 方式 ({result:"ERROR"} を HTTP 200 で包んでいたもの) は 廃止されました。 ステータスコードを見ずに本文の result を探すクライアントは、すべての失敗を成功として読み取ります。

判定は次の二つのいずれかで行ってください — HTTP ステータスコード、または meta.status ("OK" / "ERROR")。errors[] を探索する必要はなく、一つのキーで判別可能です。

codeHTTP意味
MISSING_BODY400リクエスト本文が空です
MISSING_FIELD400必須フィールド欠落
INVALID_JSON400JSON パース失敗
INVALID_RANGE400page·size·from~to 範囲違反
INVALID_ARGUMENT400一般引数エラー(イメージ・ボリュームホワイトリスト等)
INVALID_NAME400コンテナ名にインジェクション文字
VALIDATION_FAILED400複数フィールドが同時に 間違っています — field_errors[] を参照
TOO_MANY400一括リクエスト上限(100件)超過
RW_VIOLATION · TYPE_MISMATCH400タグ書き込み失敗 — 読み取り専用タグ / タイプ不一致
UNAUTHORIZED401認証欠落または無効
HTTPS_REQUIRED403平文 HTTP で送信されました — キーの問題ではありません
OPC_NOT_FOUND · TAG_NOT_FOUND · APP_NOT_FOUND404対象が存在しません
CONTAINER_EXISTS409同名のコンテナが既に存在します
PRECONDITION_FAILED412If-Match の ETag が異なります — その間に誰かが修正しました
START_FAILED · STOP_FAILED422OPC 収集制御失敗
DOCKER_PULL_FAILED · DOCKER_RUN_FAILED502Docker コマンド失敗
MONITOR_NOT_READY · NOT_READY · DISCONNECTED · OPC_STOPPED503未準備/切断 — リクエストが間違っているわけではありません
MISCONFIGURED500edge.rest.api.key が空です
INTERNAL_ERROR · *_FAILED500その他内部エラー
503 を 400 のように扱わないでください

DISCONNECTED · OPC_STOPPED · MONITOR_NOT_READY は リクエストが間違っているのではなく、今は実行できない という意味です。同じリクエストを 後で再送してください。 400 のように「修正して再送」として処理すると、修正すべき点がないのに修正を試みることになります。


5. 動作メモ​

  • タグ単一照会: APP_TAG.xml の R03 (WHERE TAG_ID = :tag_id ALLOW FILTERING)。
  • OPC 登録 · タグ登録 後は ConnectService.restartComponents() が動作し、キャッシュを再読み込みして OPC-UA サーバーを再起動します — 登録直後に一時的に収集が途切れるのはこのためです。
  • 値書き込み (tagWriteByTagId): HTTP は HTTPDriver.bind()、それ以外は driver.write(ProtocolAddress)。失敗時 (ok=false) に親切なヒント (EIP ファームウェア互換性等) を含みます。write 後に即座に read した結果を actual_read で共に返します (HTTP を除く)。
  • トランスファーヘルス (/api/v1/transfer): TransferRegistry に登録されたすべての outbound コンポーネントの getStatus() を収集。Sparkplug B カード (/ui/main) が 5秒周期で呼び出します。
  • OPC UA サーバー (/ui/opcua/*) は内部網ポーリング (2秒) — OPCAndTagsCache.getInstance() + LastValueMap.getInstance() のみを使用するため負荷が低いです。
  • レガシー deprecated: /api/http (APIController) — /api/v1/tag/{tagId}/value の使用を推奨。

6. コード位置​

  • コントローラー: src/plantpulse/app/edge/api/v1/ — OpcAPI · TagAPI · AppAPI · SystemAPI · SystemTimeAPI · MonitoringAPI · EdgeStatusAPI · HaAPI · ConnectIoAPI · FormulaAPI
  • MCP サーバー: src/plantpulse/app/edge/module/mcp/ — MCP サーバー
  • エンベロープ · 共通レスポンス: api/v1/RestApiSupport.java · api/v1/dto/Api*.java
  • OPC UA サーバー: src/plantpulse/app/edge/module/opcua/OPCUAViewerController.java
  • アップグレードプロキシ: src/plantpulse/app/edge/module/system/config/ConfigController.java
  • ビジネスロジック: src/plantpulse/app/edge/module/connect/ConnectService.java
  • レスポンスエンベロープヘルパー: plantpulse.app.edge.core.web.ControllerSupport (ok() / error())