Haply Inverse Service
The Haply Inverse Service is a foundational component that integrates Haply's tactile devices with interactive applications, running as a background Windows service to enable real-time interactions. Its functionalities include:
- Device Discovery and Management: Identifies and configures connected Haply devices automatically.
- Ownership Management: Manages access to devices across multiple applications, ensuring smooth operation.
- Real-Time State Streaming: Provides updates on device states at high frequencies for precise control.
- Command Processing: Executes commands for force or position with high fidelity, enhancing the haptic feedback.
- Background Operation: Runs in the background, maintaining device readiness without user intervention.
Run, stop, restart
On Windows:
- Open the Services desktop app: Press
Windows
+R
to open the Run box, enterservices.msc
, and then pressEnter
or selectOK
.
On Ubuntu:
- Start
systemctl start haply-inverse-service.service
- Stop
systemctl stop haply-inverse-service.service
- Enable
systemctl enable haply-inverse-service.service
- Disable
systemctl disable haply-inverse-service.service
Important files
On Windows:
- The configuration files are located in: %PROGRAMDATA%\Haply\Inverse\*-config.json
- The logs files are located in: %PROGRAMDATA%\Haply\Inverse\*-log.log
On Ubuntu:
- The configuration files are located in: /etc/haply-inverse-service/*-config.json
- The logs files are located in: /var/log/haply-inverse-service/*-log.log
On OSX:
- The configuration files are located in: $TMPDIR/Haply/Inverse/*-config.json
- The logs files are located in: $TMPDIR/Haply/Inverse/*-log.log
Interfaces
Both the HTTP and Websocket interfaces use JSON
formatted payloads.
When reading the position of the end-effector, you must send a force value to the device (even if the force values are all zeros).
HTTP
By default the HTTP service starts on: http://localhost:10000
. Note: the port can be changed in the configuration.
Dashboard
The dashboard is available at: http://localhost:10000
Version
Method: GET
URL: http://localhost:10000/3.0/version
Response example:
{
"build_time": "2024-08-07T16:01:53Z",
"git_branch": "main",
"git_describe": "3.0.0-2-gce34c39e",
"git_hash": "ce34c39e",
"git_tag": "3.0.0",
"project_name": "haply-inverse-service",
"project_version": "3.0.0.0"
}
Devices
Method: GET
URL: http://localhost:10000/3.0/devices
Response example:
{
"inverse3": {
"049D": {
"config": {
"device_info": {
"firmware_version": 1,
"hardware_version": 7,
"id": "049D",
"model": 4,
"uuid": "5A9F3AC02E085C078AD6A4A113DE049D"
},
"extended_device_id": "5A9F3AC02E085C078AD6A4A113DE049D",
"extended_firmware_version": "77488E4644CB6057D0920B0B919C8B82",
"gravity_compensation": {
"enabled": true,
"scaling_factor": 0.75
},
"handedness": "left",
"torque_scaling": {
"enabled": true
}
},
"device_id": "049D",
"state": {
"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
},
"calibrated": false,
"cursor_position": {
"x": 0.0,
"y": 0.0,
"z": 0.0
},
"cursor_velocity": {
"x": 0.0,
"y": 0.0,
"z": 0.0
},
"in_use": false,
"mode": "idle",
"power_supply": false,
"ready": false,
"started": false
},
"status": {
"initialization_received": {
"body_orientation": true,
"device_handedness": true,
"device_info": true,
"extended_device_id": true,
"extended_firmware_version": true,
"gravity_compensation": true,
"torque_scaling": true
},
"port": "COM12"
}
}
},
"verse_grip": {
"61582": {
"button": false,
"device_id": "61582",
"error": 0,
"hall": 0,
"orientation": {
"w": 0.17022705078125,
"x": -0.24114990234375,
"y": -0.95538330078125,
"z": -0.001220703125
}
}
},
"wireless_verse_grip": {
"0": {
"battery_level": 0.6200001239776611,
"buttons": {
"a": false,
"b": false,
"c": false
},
"device_id": "0",
"hall": 15,
"orientation": {
"w": -0.906463623046875,
"x": -0.379058837890625,
"y": -0.111785888671875,
"z": -0.136688232421875
}
}
}
}
Force Scale
Method: POST
URL: http://localhost:10000/3.0/force_scale
Body example:
{
"force_scale": 0.5
}
Response example:
{
"ok": true
}
Gravity Compensation
Method: POST
URL: http://localhost:10000/3.0/gravity_compensation
Body example:
{
"device_id": "049D",
"enable": true,
"gravity_scaling_factor": 0.8
}
Response example:
{
"ok": true
}
Torque scaling
Method: POST
URL: http://localhost:10000/3.0/torque_scaling
Body example:
{
"device_id": "049D",
"enable": true
}
Response example:
{
"ok": true
}
Device Handedness
Method: POST
URL: http://localhost:10000/3.0/device_handedness
Body example:
{
"device_id": "049D",
"handedness": "right"
}
Response example:
{
"ok": true
}
Save Configuration
Only works with Inverse3 devices. Warning: Avoid saving the configuration too often, since there is a limited number of save that can be applied to any given device.
Method: POST
URL: http://localhost:10000/3.0/save_configuration
Body example:
{
"device_id": "049D"
}
Response example:
{
"ok": true
}
Websocket
The default websocket URL is ws://localhost:10000
. Note: the port can be changed in the configuration.
Initial Message
The service sends a message containing the complete device list when a websocket is connected. The initial message has the following JSON
format:
{
"inverse3": {
"049D": {
"config": {
"device_info": {
"firmware_version": 1,
"hardware_version": 7,
"id": "049D",
"model": 4,
"uuid": "5A9F3AC02E085C078AD6A4A113DE049D"
},
"extended_device_id": "5A9F3AC02E085C078AD6A4A113DE049D",
"extended_firmware_version": "77488E4644CB6057D0920B0B919C8B82",
"gravity_compensation": {
"enabled": true,
"scaling_factor": 0.75
},
"handedness": "left",
"torque_scaling": {
"enabled": true
}
},
"device_id": "049D",
"state": {
"angular_position": {
"a0": -90.40308380126953,
"a1": 3.390819549560547,
"a2": 0.8826223015785217
},
"angular_velocity": {
"a0": 0,
"a1": 0,
"a2": 0
},
"body_orientation": {
"w": 1,
"x": 0,
"y": 0,
"z": 0
},
"calibrated": false,
"cursor_position": {
"x": -0.022379431873559952,
"y": -0.011212021112442017,
"z": -0.11827600002288818
},
"cursor_velocity": {
"x": 0,
"y": 0,
"z": 0
},
"in_use": false,
"mode": "angle",
"power_supply": true,
"ready": true,
"started": true
},
"status": {
"initialization_received": {
"body_orientation": true,
"device_handedness": true,
"device_info": true,
"extended_device_id": true,
"extended_firmware_version": true,
"gravity_compensation": true,
"torque_scaling": true
},
"port": "COM12"
}
}
},
"verse_grip": {
"61582": {
"button": false,
"device_id": "61582",
"error": 0,
"hall": 0,
"orientation": {
"w": 0.156982421875,
"x": -0.24163818359375,
"y": -0.95751953125,
"z": -0.00079345703125
}
}
},
"wireless_verse_grip": {
"0": {
"battery_level": 0.5999999046325684,
"buttons": {
"a": false,
"b": false,
"c": false
},
"device_id": "0",
"hall": 15,
"orientation": {
"w": -0.91265869140625,
"x": -0.389984130859375,
"y": -0.075103759765625,
"z": -0.07635498046875
}
}
}
}
State Update Message
Whenever any of the available devices has a change, the service will send a state update message containing the state of all devices. If you wish to know the state of the machine, you must send it a message beforehand, such as a force value (even if the values are zeros). This is particularly important if using our devices as an input device, such as tracking position and not applying force. The state update message has the following JSON
format:
{
"inverse3": {
"049D": {
"state": {
"angular_position": {
"a0": 0,
"a1": 0,
"a2": 0
},
"angular_velocity": {
"a0": 0,
"a1": 0,
"a2": 0
},
"body_orientation": {
"w": 1,
"x": 0,
"y": 0,
"z": 0
},
"calibrated": true,
"cursor_position": {
"x": -0.022203030064702034,
"y": -0.027979673817753792,
"z": -0.1170499324798584
},
"cursor_velocity": {
"x": 0,
"y": 0,
"z": 0
},
"in_use": true,
"mode": "position",
"power_supply": false,
"ready": true,
"started": true
}
}
},
"verse_grip": {
"61582": {
"button": false,
"device_id": "61582",
"error": 0,
"hall": 0,
"orientation": {
"w": 0.15423583984375,
"x": -0.24176025390625,
"y": -0.95794677734375,
"z": -0.0015869140625
}
}
},
"wireless_verse_grip": {
"0": {
"battery_level": 0.5999999046325684,
"buttons": {
"a": false,
"b": false,
"c": false
},
"device_id": "0",
"hall": 15,
"orientation": {
"w": -0.3958740234375,
"x": 0.639190673828125,
"y": -0.404388427734375,
"z": -0.51739501953125
}
}
}
}
Command Message
In order to send commands to the inverse3
the client has to send a command message. Here is an example:
{
"inverse3": {
"049D": [
{
"command": "set_cursor_force",
"values": {
"x": 0,
"y": 0,
"z": 0
}
}
]
}
}
Also, it is possible to send commands to multiple devices in a single message. here is an example:
{
"inverse3": {
"049D": [
{
"command": "set_cursor_force",
"values": {
"x": 0,
"y": 0,
"z": 0
}
}
],
"049E": [
{
"command": "set_cursor_force",
"values": {
"x": 0,
"y": 0,
"z": 0
}
}
]
}
}
The following commands are currently available on the inverse3
:
Set Cursor Position
{
"command": "set_cursor_position",
"values": {
"x": 1.0,
"y": 2.0,
"z": 3.0
}
}
Set Cursor Force
{
"command": "set_cursor_force",
"values": {
"x": 0.5,
"y": 0.5,
"z": 0.5
}
}
Set Angular Position
{
"command": "set_angular_position",
"values": {
"a0": 1.0,
"a1": 2.0,
"a2": 3.0
}
}
Set Angular Torque
{
"command": "set_angular_torque",
"values": {
"a0": 0.5,
"a1": 0.5,
"a2": 0.5
}
}