본문으로 바로 가기

WebSocket 프로토콜

시뮬레이션 채널은 포트 10001의WebSocket 연결입니다. 이 채널은 애플리케이션과 서비스 간의 실시간 명령/상태 루프를 전달합니다.

핵심 규칙: 메시지 하나당 응답 하나​

이 서비스는 클라이언트로부터 수신한 각 메시지에대해 정확히 하나의 상태 업데이트를 전송합니다:

폴링도, 구독도, 대역 외 푸시도 없습니다. 업데이트 주기는 전적으로 클라이언트의 전송 빈도에 따라 결정됩니다.


1단계 — 초기 인벤토리(서비스의 첫 번째 메시지)​

연결되면 서비스는 감지된 모든 기기에 대해 config, state, status를 포함한 전체 스냅샷을 전송합니다:

{
"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": []
}

세 블록의 업데이트 빈도는 서로 다릅니다:

블록포함 사항변경 빈도
config펌웨어 정보, 프리셋, 베이스, 마운트, 필터드물게 — 명시적인 구성 변경 시에만
state위치, 속도, 힘, 방향, 변환매틱 (고빈도)
status준비 완료, 보정 완료, 전원 공급 중, 사용 중가끔 (낮은 빈도)

2단계 — 첫 번째 명령 전송​

초기 인벤토리를 파싱하여 장치 ID를 확인한 후, 세션 프로필, 초기 구성 및/또는 제어 명령이 포함된 메시지를 다시 전송하십시오:

{
"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 } }
}
}
]
}

3단계 — 이후 상태 업데이트​

첫 번째 교환 이후에는 구성 변경으로 인해 전체 스냅샷 푸시가 발생하지 않는 한, 서비스는 state + status만 전송합니다(config (제외):

{
"session_id": 7,
"inverse3": [
{
"device_id": "049D",
"state": { "cursor_position": { … }, "cursor_velocity": { … }, … },
"status": { "ready": true, "calibrated": true, … }
}
]
}

4단계 — 반복​

명령 전송 → 상태 업데이트 수신. 이 루프는 전송 속도에 따라 계속 진행됩니다.


configure와 commands의 차이​

보내는 메시지의 각 디바이스 항목에는 다음 두 가지 맵이 포함될 수 있습니다:

지도용도지속성
configure일회성 설정: 프리셋, 베이스, 마운트, 필터, 모듈 설정변경 전까지 유지
commands틱 단위 제어: 힘, 위치, 토크한 번 적용된 후 폐기

configure 키의 전체 목록은 세션 구성을, commands 항목은 제어 명령을 참조하십시오.

참고
set_transform이는 예외적인 경우입니다

set_transform은 commands 아래에 나와 있듯이 **지속형(persistent)**입니다. 서비스는 새로운 값이 전송될 때까지 마지막 값을 유지합니다. 이는 카메라가 움직이는 동안 변환 값을 스트리밍하면서도, 입력 간에 마지막 위치가 유지되기를 원하는 씬 탐색 용도이기 때문입니다.


execute 플래그​

configure 또는 commands 항목에 "execute": false를 포함하면, 서비스는 페이로드를 적용하지 않고 구문 분석 및 검증만 수행합니다.

{
"inverse3": [{
"device_id": "049D",
"configure": {
"preset": { "preset": "arm_front", "execute": true },
"damping": { "scalar": 0.0, "execute": false }
}
}]
}

이는 모든 필드를 항상 출력하는 리플렉션 기반 직렬화기(예: Unity의 JsonUtility)에 유용합니다. 사용하지 않는 항목은 execute: false로 설정하여 실제 구성을 덮어쓰지 않도록 하십시오. 기본값은 true또한, 플래그를 생략하면 “정상적으로 적용”됩니다.


경고​

이미 명령을 전송 중인 경우에는 프로브를 보내지 마십시오

probe_position과 probe_orientation이는 제어 명령을 전송하지 않는 세션(예: Haply Hub)을 위한 모니터링 전용 keepalive 메시지입니다. 세션이 이미 set_cursor_force, set_cursor_position 등을 보내고 있다면 프로브를 함께 보내지 마십시오. 디바이스 상태 정보는 이미 모든 응답에 포함되어 있습니다. 이 두 가지를 함께 사용하면 대역폭이 낭비되고 session-probe-dropped 이벤트가 발생할 수 있습니다.

알 수 없는 키는 오류 없이 무시됩니다

명령이 아무런 효과가 없는 것처럼 보인다면 필드 이름이 잘못되었을 가능성이 높습니다. 서비스는 현재 인식되지 않는 JSON 키를 오류 없이 무시합니다. 서비스 로그를 확인하고, API 참조 문서에서필드 이름이 올바른지 확인하십시오. 이 동작은 향후 버전에서 변경될 예정입니다.