01. Imprimer Inverse3
Se connecterau WebSocket de simulation etrecevoir en continu les données relatives à la position du curseur, à la vitesse et à la force du premier « Inverse3 » transmises par le service.
Vous apprendrez :
- Établir une connexion WebSocket et recevoir le message d'état initial complet
- Envoyer une force nulle
set_cursor_forceInstruction de maintien de la connexion, permettant à la session de rester active - Enregistrezvotre profil de sessionafin que Haply Hub puisse identifier votre simulation
- Mode dans lequel la négociation s'effectue uniquement lors du premier message — suppression de « session/configure » après l'envoi initial
- Lecture sémantique de l'espace de travail avec mise à jour par parties
transform(position, rotation, mise à l'échelle) - Limiter la fréquence des messages affichés sur la console à un niveau lisible
Processus de travail
- Ouvrir à la page
ws://localhost:10001WebSocket. Le service envoie immédiatement uneTrame d'état complète, qui répertorie les appareils connectés. - À la réception de la première image, sélectionner le premier Inverse3
device_id, puis de construire un message de requête composé de deux parties :session.configure.profile.name—— VersHub HaplyEnregistrez cette simulation.- Pour chaque appareil,
set_cursor_forceCommande : le vecteur de force est nul. Le service l'utilise comme signal de maintien de connexion : tant qu'il continue de recevoir cette commande, il continue d'envoyer des trames d'état.
- Renvoyer le message.À supprimer avant le prochain cycle
sessionChamp——Le profil de session ne sert qu’à établir une connexion initiale ; lors des cycles suivants, seules des commandes sont envoyées. - Pour chaque trame d'état suivante : imprimer le curseur à une fréquence maximale d'environ 10 Hz.
vec3les champs (position, vitesse, force), puis renvoyer une commande de maintien de la force nulle.
Paramètres
| Nom | Valeur par défaut | Utilisation |
|---|---|---|
URI | ws://localhost:10001 | URL du canal de simulation WebSocket |
PRINT_EVERY_MS | 100 | Limitation de la vitesse d'affichage de la console |
| Nom du profil de session | co.haply.inverse.tutorials:print-inverse3 | Identifier cette simulation dans le Hub « Haply » |
Champs d'état lus
Extrait de data.inverse3[0].state:
cursor_position,cursor_velocity,current_cursor_force—— tous sontvec3transform—— Changement d'espace de travail ; en tant que sous-objet, contientposition(vec3),rotation(quaternion) etscale(vec3)
sous-champ dont la valeur est égale à la valeur par défaut de son unité (position: {0,0,0},rotation: {w:1,x:0,y:0,z:0},scale: {1,1,1}) Afin d'économiser de la bande passante, le système va, à partir de la charge…(omission)。Veillez à fournir une valeur par défaut lors de la lecture (par exemple, en C++, utilisez .value("position", default_pos), utilisation en Python .get("position", default_pos)). Pour recevoir systématiquement chaque champ, activez serialization/explicit_fields。
Envoyer / Recevoir
Boucle WebSocket : réception d'une trame d'état, construction et renvoi simultanés d'une trametrame de commande. La première trame de commande contient les informations relatives à l'établissement de la session ainsi que la force nulle set_cursor_force Instruction de maintien ; à partir de là, à chaque imageSeulementContient une instruction de maintien de la connexion (les informations de session ont été supprimées).
- Python
- C++ (nlohmann)
- C++ (Glaze)
Boucle asynchrone unique ——recv() → Instructions de compilation → send() → Répétition.
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
libhv gère les connexions WebSocket sur son propre thread d'E/S — le traitement de chaque trame s'effectue dans ws.onmessage . Le thread principal n'a qu'à rester en attente jusqu'à ce que l'utilisateur appuie sur la touche ENTRÉE.
ws.onmessage = [&](const std::string &msg) {
const json data = json::parse(msg);
if (first_message) {
first_message = false;
device_id = data["inverse3"][0].at("device_id").get<std::string>();
request_msg = {
{"session", {{"configure", {{"profile",
{{"name", "co.haply.inverse.tutorials:print-inverse3"}}}}}}},
{"inverse3", json::array({
{{"device_id", device_id},
{"commands", {{"set_cursor_force",
{{"vector", {{"x", 0.0}, {"y", 0.0}, {"z", 0.0}}}}}}}},
})},
};
}
ws.send(request_msg.dump());
request_msg.erase("session"); // one-shot handshake
};
ws.open("ws://localhost:10001");
while (std::cin.get() != '\n') {} // block main thread
Modèle de rappel libhv identique à celui de la variante nlohmann — seul le corps de la fonction diffère. Glaze utilise la réflexion à la compilation : il déclare une structure dont la structure correspond à celle du JSON, puis appelle glz::read / glz::write_json。std::optional<session_cmd> Sert à contenir les informations relatives à la poignée de main unique ; s'il n'est pas défini, Glaze omet ce champ dans le JSON sérialisé.
// Struct models
struct vec3 { float x{}, y{}, z{}; };
struct inverse_state {
vec3 cursor_position{}, cursor_velocity{}, current_cursor_force{};
/* + body_orientation, angular_position, angular_velocity */
};
struct inverse_device { std::string device_id; inverse_state state; };
struct devices_message { std::vector<inverse_device> inverse3; };
struct set_cursor_force_cmd { vec3 vector; };
struct commands_message {
std::optional<session_cmd> session; // omitted from JSON when unset
std::vector<device_commands> inverse3;
};
// Send / receive
ws.onmessage = [&](const std::string &msg) {
devices_message data{};
if (glz::read<glz_settings>(data, msg)) return;
commands_message out_cmds{};
if (first_message) {
first_message = false;
out_cmds.session = session_cmd{ /* profile = print-inverse3 */ };
}
// ... populate out_cmds.inverse3 with zero-force keepalive ...
std::string out_json;
(void)glz::write_json(out_cmds, out_json);
ws.send(out_json);
};
ws.open("ws://localhost:10001");
while (std::cin.get() != '\n') {} // block main thread
Paramètres de ligne de commande (Python)
La variante Python accepte deux paramètres permettant de modifier le contenu de la sortie du tutoriel :
| Paramètres | Fonction |
|---|---|
--full | Afficher les données de charge JSON brutes de chaque trame d'état sous forme formatée, plutôt que sous forme de résumé sur une seule ligne. Cela permet de comprendre plus facilement quels champs sont renvoyés par le service. |
--query-config | Réinjecter à chaque cycle de distribution session.force_render_full_state: {}, ce qui oblige le service à générer à chaque image un instantané complet (comprenant config Bloc — type d'appareil, micrologiciel, préréglages, mode d'installation, filtres, etc.). Si ce paramètre n'est pas utilisé,config Cela n'apparaît que dans la première image ; les images suivantes ne contiennent que des données incrémentielles en continu. |
Ces deux paramètres peuvent être utilisés conjointement —python 01-haply-inverse-print-inverse3.py --full --query-config Affichera à chaque cycle un message contenant les éléments visibles config Les données de charge JSON complètes sont très utiles pour observer en temps réel les modifications des paramètres via Haply Hub ou l'API HTTP. Pour connaître les commandes WebSocket sous-jacentes, veuillez consulter session.force_render_full_state。
La variante C++ ne prend pas en charge ces paramètres : elle ne génère toujours qu'un résumé sur une seule ligne, et config Reçu uniquement dans la première image.
Le tutoriel 01 est également installé localement avec le SDK — veuillez vous rendre dans le répertoire d'installation du service, sous tutorials/01-haply-inverse-print-inverse3/ Rechercher dans.
Source : Python · C++ · C++ Glaze
Contenu associé : Protocole WebSocket · Commandes de contrôle (set_cursor_force) · Conversation · Type (vec3)