WebSocket-Protokoll
Der Simulationskanal ist eine WebSocket-Verbindung auf Port 10001. Er überträgt den Echtzeit-Befehls-/Zustandsaustausch zwischen Ihrer Anwendung und dem Dienst.
Grundregel: eine Antwort pro Nachricht
Der Dienst sendet genau eine Statusaktualisierung für jede Nachricht, die er von Ihrem Client erhält:
Es gibt keine Abfrage, kein Abonnement und keine Push-Benachrichtigungen außerhalb des Datenkanals. Der Rhythmus richtet sich voll und ganz nach Ihrer Sendefrequenz.
Schritt 1 – Erstbestandsaufnahme (erste Nachricht vom Dienst)
Bei der Verbindung sendet der Dienst eine vollständiger Überblick 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 | Änderungen |
|---|---|---|
config | Firmware-Informationen, Voreinstellungen, Basis, Halterung, Filter | Selten – nur bei einer expliziten Konfigurationsänderung |
state | Position, Geschwindigkeit, Kraft, Ausrichtung, Transformationen | Bei jedem Tick (Hochfrequenz) |
status | Bereit, kalibriert, Stromversorgung, in Gebrauch | Gelegentlich (selten) |
Schritt 2 – Senden Sie Ihren ersten Befehl
Analysieren Sie die anfängliche Bestandsaufnahme, um Ihre Geräte-ID zu ermitteln, und senden Sie anschließend eine Nachricht zurück, die Ihr Sitzungsprofil, etwaige 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 Statusaktualisierungen
Nach dem ersten Austausch sendet der Dienst nur state + status (nein
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
Befehl senden → Statusaktualisierung empfangen. Der Zyklus läuft mit Ihrer Sendefrequenz weiter.
configure vs commands
Jeder Geräteeintrag in Ihrer ausgehenden Nachricht kann zwei Zuordnungen enthalten:
| Karte | Zweck | Beharrlichkeit |
|---|---|---|
configure | Einmalige Einstellungen: Voreinstellung, Basis, Halterung, Filter, Modulkonfiguration | So lange gespeichert, bis es geändert wird |
commands | Steuerung pro Takt: Kraft, Position, Drehmomente | Einmal auftragen, dann vergessen |
Siehe Sitzungskonfiguration die
vollständige Liste der configure Tasten und Steuerbefehle für
die commands Einträge.
set_transform ist ein Sonderfallset_transform lebt in commands aber ist anhaltend — Der Dienst
behält den letzten Wert bei, bis Sie einen neuen senden. Der Grund dafür ist, dass er für die
Szenennavigation gedacht ist, bei der Sie während der Kamerabewegung Transformationen streamen, aber möchten, dass die
letzte Position zwischen den Tastendrücken beibehalten wird.
Die execute Flagge
Beliebig configure oder commands Ein Eintrag kann Folgendes enthalten "execute": false um den
Dienst analysieren und validieren die Nutzlast, 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. Unity's JsonUtility)
die immer alle Felder ausgeben: Unbenutzte Einträge auf execute: false damit sie
die eigentliche Konfiguration nicht überschreiben. Die Standardeinstellung ist true — Wenn die Option weggelassen wird, bedeutet dies
„normal anwenden“.
Warnhinweise
probe_position und probe_orientation sind reine Überwachungs-Keepalives für
Sitzungen, die keine Steuerbefehle senden (z. B. Haply ). Wenn Ihre Sitzung
bereits set_cursor_force, set_cursor_positionusw., schick nicht auch noch
Sonden — Der Gerätestatus ist bereits in jeder Antwort enthalten. Beides zu vermischen
verschwendet Bandbreite und kann dazu führen, dass session-probe-dropped Veranstaltungen.
Wenn ein Befehl keine Wirkung zu zeigen scheint, ist der Feldname wahrscheinlich falsch. Der Dienst ignoriert derzeit unbekannte JSON-Schlüssel ohne Fehlermeldung. Überprüfen Sie die Dienstprotokolle und vergleichen Sie die Feldnamen mit der API-Referenz. Dieses Verhalten soll in einer zukünftigen Version geändert werden.