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

01. Inverse3の出力

シミュレーションWebSocketに接続し、サービスが報告する最初のInverse3から、カーソルの位置、速度、および力をストリーミングします。

学習内容:

  • WebSocket接続を開き、初期の完全な状態メッセージを受信すること
  • ゼロ力のset_cursor_forceキープアライブを送信し、セッションを動かし続けること
  • Haply Hubがシミュレーションを認識できるようにセッションプロファイルを登録すること
  • 最初のメッセージのみでハンドシェイクを行うパターン — 最初の送信後に session/configure を取り除くこと
  • 部分更新のセマンティクスで、ワークスペースの transform(位置、回転、スケール)を読み取ること
  • コンソール出力を読みやすい頻度に抑制すること

ワークフロー​

  1. ws://localhost:10001 にWebSocket接続を開きます。サービスは直ちに、接続されているデバイスを一覧にした完全な状態フレームを送信します。
  2. 最初のフレームで、最初のInverse3の device_id を取得し、2つの部分からなるリクエストメッセージを作成します:
    • session.configure.profile.name — シミュレーションをHaply Hubに登録します。
    • ゼロベクトルを指定した、デバイスごとのset_cursor_forceコマンド。サービスはこれをキープアライブとして使用します。コマンドが届き続けている限り、状態フレームを送信し続けます。
  3. メッセージを送り返します。次のティックの前に session フィールドを取り除いてください。セッションプロファイルは一度きりのハンドシェイクであり、以降のティックではコマンドのみを送信します。
  4. 以降の状態フレームごとに、カーソルのvec3フィールド(位置、速度、力)を約10 Hzに抑制して出力し、ゼロ力のキープアライブを再送信します。

パラメーター​

名前デフォルト用途
URIws://localhost:10001シミュレーションチャネルWebSocketのURL
PRINT_EVERY_MS100コンソール出力の抑制間隔
セッションプロファイル名co.haply.inverse.tutorials:print-inverse3Haply Hub内でこのシミュレーションを識別します

読み取る状態フィールド​

data.inverse3[0].state から以下を読み取ります:

  • cursor_position、cursor_velocity、current_cursor_force — いずれもvec3
  • transform — ワークスペースの変換。サブオブジェクトとして position(vec3)、rotation(quaternion)、scale(vec3)を持ちます
部分更新のセマンティクス

恒等値(単位元)のデフォルト値(position: {0,0,0}、rotation: {w:1,x:0,y:0,z:0}、scale: {1,1,1})と等しいサブフィールドは、帯域幅を節約するためペイロードから省略されます。読み取り時には必ずデフォルト値を指定してください(例: C++では .value("position", default_pos)、Pythonでは .get("position", default_pos))。すべてのフィールドを常に受信するには、serialization/explicit_fieldsを有効にしてください。

送受信​

WebSocketのループでは、状態フレームを受信し、コマンドフレームを作成して送り返します。最初のコマンドフレームには、セッションのハンドシェイクとゼロ力のset_cursor_forceキープアライブが含まれます。以降のフレームにはキープアライブのみが含まれます(セッションは取り除かれます)。

単一の非同期ループです — recv() → コマンドを作成 → send() → 繰り返し。

async with websockets.connect(URI) as websocket:
while True:
msg = await websocket.recv()
data = json.loads(msg)

if first_message:
first_message = False
device_id = data["inverse3"][0]["device_id"]
request_msg = {
"session": {"configure": {"profile": {
"name": "co.haply.inverse.tutorials:print-inverse3"}}},
"inverse3": [{
"device_id": device_id,
"commands": {"set_cursor_force":
{"vector": {"x": 0.0, "y": 0.0, "z": 0.0}}},
}]
}

await websocket.send(json.dumps(request_msg))
request_msg.pop("session", None) # one-shot handshake

コマンドラインフラグ(Python)​

Python バリアントでは、チュートリアルの出力を変更する2つのフラグを指定できます。

フラグ効果
--full1行の要約ではなく、各状態フレームの生のJSONペイロードを整形して出力します。サービスが送信するフィールドを確認する際に便利です。
--query-config送信する各ティックに session.force_render_full_state: {} を追加し、サービスが毎フレーム完全なスナップショット(config ブロック — デバイスタイプ、ファームウェア、プリセット、マウント、フィルターなどを含む)を再送信するようにします。このフラグがない場合、config は最初のフレームでのみ届き、以降のフレームはストリーミングの差分になります。

両方のフラグは組み合わせて使用できます。python 01-haply-inverse-print-inverse3.py --full --query-config を実行すると、毎ティック config が表示された完全なJSONペイロードが出力されます。これは、Haply Hub または HTTP API から設定の変更をリアルタイムで確認する際に便利です。基になるWebSocket コマンドについては、session.force_render_full_state を参照してください。

C++ バリアントでは、これらのフラグは提供されていません。常に1行の要約を出力し、config は最初のフレームでのみ受信します。

SDKインストーラーに同梱

チュートリアル01も、SDKと一緒にローカルにインストールされています。サービスのインストールディレクトリ配下の tutorials/01-haply-inverse-print-inverse3/ を確認してください。

ソース: Python · C++ · C++ Glaze

関連: WebSocket プロトコル · 制御コマンド(set_cursor_force) · セッション · 型(vec3)