WebSocket-Protokoll
Der Simulationskanal ist eine WebSocket-Verbindung auf Port 10001. Er überträgt die Echtzeit-Schleife aus Befehlen und Zuständen zwischen Ihrer Anwendung und dem Dienst.
Grundregel: eine Antwort pro Nachricht
Der Dienst sendet genau eine Zustandsaktualisierung für jede Nachricht, die er von Ihrem Client empfängt:
Es gibt kein Polling, kein Abonnement und keinen Out-of-Band-Push. Der Takt wird vollständig durch Ihre Sendefrequenz bestimmt.
Schritt 1 — Initiale Bestandsaufnahme (erste Nachricht vom Dienst)
Beim Verbindungsaufbau sendet der Dienst einen vollständigen Snapshot mit config, state und status für jedes erkannte Gerät:
{
"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": []
}
Die drei Blöcke haben unterschiedliche Aktualisierungsfrequenzen:
| Block | Enthält | Änderungshäufigkeit |
|---|---|---|
config | Firmware-Info, Preset, Basis, Montage, Filter | Selten, nur bei einer expliziten Konfigurationsänderung |
state | Position, Geschwindigkeit, Kraft, Orientierung, Transformationen | Bei jedem Tick (hohe Frequenz) |
status | Bereit, kalibriert, Stromversorgung, in_use | Gelegentlich (niedrige Frequenz) |
Schritt 2 — Ersten Befehl senden
Parsen Sie die initiale Bestandsaufnahme, um Ihre Geräte-ID zu finden. Senden Sie dann eine Nachricht, die Ihr Sitzungsprofil, eventuelle Anfangskonfigurationen und/oder einen Steuerbefehl enthält:
{
"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 } }
}
}
]
}
Schritt 3 — Nachfolgende Zustandsaktualisierungen
Nach dem ersten Austausch sendet der Dienst nur noch state + status (ohne config), es sei denn, eine Konfigurationsänderung löst einen vollständigen Snapshot-Push aus:
{
"session_id": 7,
"inverse3": [
{
"device_id": "049D",
"state": { "cursor_position": { … }, "cursor_velocity": { … }, … },
"status": { "ready": true, "calibrated": true, … }
}
]
}
Schritt 4 — Wiederholen
Senden Sie einen Befehl → Sie erhalten eine Zustandsaktualisierung. Die Schleife läuft mit Ihrer Sendefrequenz weiter.
configure vs. commands
Jeder Geräteeintrag in Ihrer ausgehenden Nachricht kann zwei Maps enthalten:
| Map | Zweck | Persistenz |
|---|---|---|
configure | Einmalige Einstellungen: Preset, Basis, Montage, Filter, Modulkonfiguration | Wird bis zur Änderung gespeichert |
commands | Steuerung pro Tick: Kraft, Position, Drehmomente | Wird einmal angewendet und danach vergessen |
Die vollständige Liste der configure-Schlüssel finden Sie unter Session-Konfiguration, und die commands-Einträge unter Steuerbefehle.
set_transform ist ein Sonderfallset_transform gehört zu commands, ist aber persistent — der Dienst behält den letzten Wert, bis Sie einen neuen senden. Das liegt daran, dass sein Zweck die Szenennavigation ist: Sie streamen Transformationen während einer Kamerabewegung, möchten aber, dass die letzte Position zwischen den Eingaben erhalten bleibt.
Das Flag execute
Jeder Eintrag configure oder commands kann "execute": false enthalten, damit der Dienst die Nutzlast parst und validiert, ohne sie anzuwenden.
{
"inverse3": [{
"device_id": "049D",
"configure": {
"preset": { "preset": "arm_front", "execute": true },
"damping": { "scalar": 0.0, "execute": false }
}
}]
}
Dies ist nützlich für reflexionsbasierte Serializer (z. B. JsonUtility in Unity), die immer alle Felder ausgeben: Setzen Sie ungenutzte Einträge auf execute: false, damit sie keine echte Konfiguration überschreiben. Der Standardwert ist true — wird das Flag weggelassen, bedeutet das „normal anwenden“.
Warnungen
probe_position und probe_orientation sind reine Überwachungs-Keepalives für Sitzungen, die keine Steuerbefehle senden (z. B. Haply Hub). Wenn Ihre Sitzung bereits set_cursor_force, set_cursor_position usw. sendet, senden Sie keine zusätzlichen Probes — der Gerätezustand ist ohnehin in jeder Antwort enthalten. Beides gleichzeitig verschwendet Bandbreite und kann session-probe-dropped-Ereignisse auslösen.
Wenn ein Befehl keine Wirkung zu haben scheint, ist der Feldname wahrscheinlich falsch. Der Dienst ignoriert unbekannte JSON-Schlüssel derzeit ohne Fehlermeldung. Prüfen Sie die Dienst-Logs und vergleichen Sie die Feldnamen mit der API-Referenz. Dieses Verhalten soll in einer zukünftigen Version geändert werden.