Protocole WebSocket
Le canal de simulation est une connexion WebSocket sur le port 10001. Il assure la boucle commande/état en temps réel entre votre application et le service.
Règle fondamentale : une seule réponse par message
Le service envoie exactement une mise à jour d'état pour chaque message qu'il reçoit de votre client :
Il n'y a ni interrogation, ni abonnement, ni notification push hors bande. La fréquence dépend entièrement de votre cadence d'envoi.
Étape 1 — État des lieux initial (premier message du service)
À la connexion, le service envoie un aperçu complet contenant config, state,
et status pour chaque appareil détecté :
{
"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": []
}
Ces trois blocs ont des fréquences de mise à jour différentes :
| Bloc | Contient | Modifications |
|---|---|---|
config | Informations sur le micrologiciel, préréglages, base, montage, filtres | Rarement — uniquement en cas de modification explicite de la configuration |
state | Position, vitesse, force, orientation, transformations | À chaque impulsion (haute fréquence) |
status | Prêt, calibré, alimenté, en cours d'utilisation | De temps en temps (rarement) |
Étape 2 — Envoyez votre première commande
Analysez l'inventaire initial pour trouver l'identifiant de votre appareil, puis renvoyez un message contenant votre profil de session, toute configuration initiale et/ou une commande de contrôle :
{
"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 } }
}
}
]
}
Étape 3 — Mises à jour ultérieures de l'état
Après le premier échange, le service envoie uniquement state + status (non
config) à moins qu'une modification de configuration ne déclenche la transmission d'un instantané complet :
{
"session_id": 7,
"inverse3": [
{
"device_id": "049D",
"state": { "cursor_position": { … }, "cursor_velocity": { … }, … },
"status": { "ready": true, "calibrated": true, … }
}
]
}
Étape 4 — Répétez l'opération
Envoyez une commande → recevez une mise à jour de l'état. La boucle se poursuit à votre fréquence d'envoi.
configure contre commands
Chaque entrée de périphérique dans votre message sortant peut contenir deux mappages :
| Carte | Objectif | Persévérance |
|---|---|---|
configure | Paramètres ponctuels : préréglage, base, montage, filtres, configuration du module | On s'en souviendra jusqu'à ce que cela change |
commands | Contrôle par impulsion : force, position, couples | On l'applique une fois, puis on l'oublie |
Voir Configuration de la session pour consulter la
liste complète des configure touches, et Commandes de contrôle pour
le commands entrées.
set_transform est un cas particulierset_transform vit sous commands mais est persistant — le service
conserve la dernière valeur jusqu'à ce que vous en envoyiez une nouvelle. En effet, il est destiné à la
navigation dans les scènes, où l'on transmet en continu les transformations pendant le déplacement de la caméra, mais où l'on souhaite que la
dernière position soit conservée entre deux pressions.
Les execute drapeau
N'importe quel configure ou commands une entrée peut inclure "execute": false pour mettre en place le
service analyser et valider la charge utile sans l'appliquer.
{
"inverse3": [{
"device_id": "049D",
"configure": {
"preset": { "preset": "arm_front", "execute": true },
"damping": { "scalar": 0.0, "execute": false }
}
}]
}
Cela est utile pour sérialiseurs basés sur la réflexion (par exemple, Unity's JsonUtility)
qui transmettent toujours tous les champs : définir les entrées inutilisées sur execute: false afin qu'ils
ne remplacent pas la configuration réelle. La valeur par défaut est true — l'absence de drapeau signifie
« appliquer normalement ».
Avertissements
probe_position et probe_orientation sont des messages de maintien de connexion à des fins de surveillance uniquement pour les
sessions qui n'envoient pas de commandes de contrôle (par exemple, Haply ). Si votre session
envoie déjà set_cursor_force, set_cursor_position, etc., n'envoyez pas non plus
de sondes — l'état de l'appareil figure déjà dans chaque réponse. Combiner les deux
gaspille de la bande passante et peut entraîner session-probe-dropped événements.
Si une commande semble ne produire aucun effet, le nom du champ est probablement incorrect. Le service ignore actuellement les clés JSON non reconnues sans générer d'erreur. Consultez les journaux du service et vérifiez les noms de champs par rapport à la référence de l'API. Il est prévu de modifier ce comportement dans une prochaine version.