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

WebSocket プロトコル

シミュレーションチャネルは、ポート 10001 上の WebSocket 接続です。アプリケーションとサービスの間で、リアルタイムのコマンド/状態ループを伝送します。

基本ルール:メッセージごとに1つのレスポンス​

サービスは、クライアントから受信した 各メッセージ に対して、ちょうど1つの状態更新 を送信します:

ポーリングも、サブスクリプションも、帯域外(アウトオブバンド)のプッシュもありません。更新の間隔は、完全にアプリケーションの送信レートによって決まります。


ステップ 1 — 初期インベントリ(サービスからの最初のメッセージ)​

接続時に、サービスは検出されたすべてのデバイスについて、config、state、status を含む 完全なスナップショット を送信します:

{
"session_id": 7,
"inverse3": [
{
"device_id": "049D",
"config": {
"type": "inverse3",
"port": "COM12",
"device_info": { "major_version": 7, "minor_version": 4, "id": "049D", "device_type": 4, "uuid": "…" },
"extended_device_id": "…",
"extended_firmware_version": "…",
"gravity_compensation": { "enabled": true, "scaling_factor": 1.0 },
"handedness": "right",
"streaming_mode": "USB",
"torque_scaling": { "enabled": true },
"home_return": { "enabled": false },
"preset": "defaults",
"basis": { "permutation": "XYZ" },
"mount": { "position": { "x": 0, "y": 0, "z": 0 }, "rotation": { "w": 1, "x": 0, "y": 0, "z": 0 }, "scale": { "x": 1, "y": 1, "z": 1 } },
"filters": {
"force_gate": { "gain": 0.3 },
"damping": { "scalar": 0.0 }
}
},
"state": {
"cursor_position": { "x": 0.0, "y": -0.12, "z": 0.15 },
"cursor_velocity": { "x": 0.0, "y": 0.0, "z": 0.0 },
"angular_position": { "a0": 0.0, "a1": 0.0, "a2": 0.0 },
"angular_velocity": { "a0": 0.0, "a1": 0.0, "a2": 0.0 },
"body_orientation": { "w": 1.0, "x": 0.0, "y": 0.0, "z": 0.0 },
"current_cursor_force": { "x": 0.0, "y": 0.0, "z": 0.0 },
"current_cursor_position": { "x": 0.0, "y": 0.0, "z": 0.0 },
"current_angular_torques": { "a0": 0.0, "a1": 0.0, "a2": 0.0 },
"current_angular_position": { "a0": 0.0, "a1": 0.0, "a2": 0.0 },
"control_domain": "cartesian",
"control_mode": "idle",
"transform": { "position": { "x": 0, "y": 0, "z": 0 }, "rotation": { "w": 1, "x": 0, "y": 0, "z": 0 }, "scale": { "x": 1, "y": 1, "z": 1 } },
"transform_velocity": { "position": { "x": 0, "y": 0, "z": 0 }, "rotation": { "w": 1, "x": 0, "y": 0, "z": 0 }, "scale": { "x": 0, "y": 0, "z": 0 } }
},
"status": {
"calibrated": true,
"in_use": false,
"power_supply": true,
"ready": true,
"started": true
}
}
],
"verse_grip": [],
"wireless_verse_grip": []
}

3つのブロックは、それぞれ異なる頻度で更新されます:

ブロック含まれる内容変更頻度
configファームウェア情報、プリセット、basis、マウント、フィルターめったに変更されない — 明示的な設定変更時のみ
state位置、速度、力、姿勢、変換毎ティック(高頻度)
status準備完了、キャリブレーション済み、電源供給、in_use時々(低頻度)

ステップ 2 — 最初のコマンドを送信​

初期インベントリを解析してデバイス ID を見つけ、セッションプロファイル、初期設定(ある場合)、および/または制御コマンドを含むメッセージを送り返します:

{
"session": {
"configure": {
"profile": { "name": "co.haply.inverse.tutorials:hello-floor" }
}
},
"inverse3": [
{
"device_id": "049D",
"configure": {
"preset": { "preset": "arm_front_centered" }
},
"commands": {
"set_cursor_force": { "vector": { "x": 0.0, "y": 0.0, "z": 0.0 } }
}
}
]
}

ステップ 3 — 以降の状態更新​

最初のやり取りの後、サービスは、設定変更によって完全なスナップショットのプッシュがトリガーされない限り、state と status のみを送信します(config は含まれません):

{
"session_id": 7,
"inverse3": [
{
"device_id": "049D",
"state": { "cursor_position": { … }, "cursor_velocity": { … }, … },
"status": { "ready": true, "calibrated": true, … }
}
]
}

ステップ4 — 繰り返し​

コマンドを送信すると、状態の更新を受信します。ループは送信レートに従って継続します。


configure と commands​

送信するメッセージ内の各デバイスエントリには、2つのマップを含めることができます。

マップ目的永続性
configure一度限りの設定:プリセット、基準、マウント、フィルター、モジュール設定変更されるまで保持
commandsティックごとの制御:力、位置、トルク一度適用されると破棄

configure キーの完全な一覧は セッション設定 を、commands のエントリについては 制御コマンド を参照してください。

注記
set_transform は特殊なケースです

set_transform は commands の下にありますが、永続的です。サービスは新しい値を送信されるまで最後の値を保持します。これは、その目的がシーンのナビゲーションにあるためです。カメラの移動中にトランスフォームをストリーミングしつつ、ボタンを押すたびに最後の位置を維持したい場合に使用されます。


execute フラグ​

configure または commands のエントリには "execute": false を含めることができます。これにより、サービスはペイロードを適用せずに解析および検証のみを行います。

{
"inverse3": [{
"device_id": "049D",
"configure": {
"preset": { "preset": "arm_front", "execute": true },
"damping": { "scalar": 0.0, "execute": false }
}
}]
}

これは、常にすべてのフィールドを出力するリフレクションベースのシリアライザー(例:Unity の JsonUtility)で役立ちます。未使用のエントリには execute: false を設定して、実際の設定を上書きしないようにしてください。デフォルトは true です。フラグを省略すると「通常どおり適用」を意味します。


警告​

すでにコマンドを送信している場合はプローブを送信しないでください

probe_position と probe_orientation は、制御コマンドを送信しないセッション(例:Haply Hub)向けの、監視専用のキープアライブです。セッションがすでに set_cursor_force、set_cursor_position などを送信している場合は、プローブを追加で送信しないでください。デバイスの状態はすでにすべてのレスポンスに含まれています。両方を混在させると帯域幅を無駄にし、session-probe-dropped イベントが発生する可能性があります。

不明なキーは警告なく無視されます

コマンドが効果を持たないように見える場合は、フィールド名が誤っている可能性があります。サービスは現在、認識されない JSON キーをエラーなしで無視します。サービスのログを確認し、フィールド名が APIリファレンス と一致しているか検証してください。この動作は将来のバージョンで変更される予定です。