Aller au contenu principal

Protocole WebSocket

Le canal de simulation est une connexion WebSocket sur le port 10001. Il achemine la boucle de commande et d'état en temps réel entre votre application et le service.

Règle fondamentale : une 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 sondage (polling), ni abonnement, ni envoi spontané hors bande. La cadence est entièrement déterminée par votre fréquence d'envoi.


Étape 1 — État des lieux initial (premier message du service)​

Lors de la connexion, le service envoie un instantané 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": []
}

Les trois blocs ont des fréquences de mise à jour différentes :

BlocContenuModifications
configInformations sur le micrologiciel, les préréglages, la base, la monture et les filtresRarement — uniquement en cas de modification explicite de la configuration
statePosition, vitesse, force, orientation, transformationsÀ chaque cycle (haute fréquence)
statusPrêt, calibré, sous tension, en cours d'utilisationDe 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 d'état suivantes​

Après le premier échange, le service n'envoie que state + status (sans config), sauf si une modification de la configuration déclenche l'envoi 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​

Envoyez une commande → recevez une mise à jour sur son statut. Le cycle se répète à la fréquence à laquelle vous envoyez vos commandes.


configure ou commands​

Chaque entrée d'appareil de votre message sortant peut contenir deux dictionnaires :

DictionnaireRôlePersévérance
configureRéglages ponctuels : préréglage, base, montage, filtres, configuration des modulesEnregistrés jusqu'à modification
commandsContrôle à chaque cycle : force, position, couplesAppliqués une fois, puis oubliés

Voir Configuration de session pour consulter la liste complète des clés de configure, et Commandes de contrôle pour les entrées de commands.

remarque
set_transform fait exception

set_transform se trouve sous commands, mais elle est persistante — Le service conserve la dernière valeur jusqu'à ce qu'une nouvelle soit envoyée. Cela s'explique par le fait que sa fonction est la navigation dans la scène : vous transmettez les transformations en continu pendant les déplacements de la caméra, mais vous souhaitez que la dernière position reste inchangée entre deux pressions.


L'indicateur execute​

Toute entrée configure ou commands peut inclure "execute": false pour que 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 les sérialiseurs à réflexion (par exemple, JsonUtility (de Unity) qui émettent toujours tous les champs : définissez les entrées inutilisées comme execute: false afin qu'elles ne remplacent pas la configuration actuelle. La valeur par défaut est true — omettre l'indicateur signifie « appliquer normalement ».


Avertissements​

N'envoyez pas de sondes si vous transmettez déjà des commandes

probe_position et probe_orientation sont des messages de maintien de connexion (keepalive) destinés à la surveillance, pour les sessions qui n'envoient pas de commandes de contrôle (par exemple, Haply Hub). 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 est déjà indiqué dans chaque réponse. Combiner les deux gaspille de la bande passante et peut déclencher des événements session-probe-dropped.

Les clés inconnues sont ignorées sans message d'erreur.

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 des champs dans la référence de l'API. Ce comportement est appelé à changer dans une prochaine version.