use crate::{
device::{
update_entrire_msg,
update_msg_from_command,
update_service_struct,
update_state_on_message,
},
device_model::{ self, * },
};
use serde_json::json;
use tokio;
use std::sync::Arc;
use tokio::sync::Mutex;
#[tokio::test]
async fn test_update_state_on_message() {
let sample_message =
json!({
"session_id": 176,
"inverse3": [
{
"device_id": "04C3",
"config": {
"type": "inverse3",
"device_info": {
"major_version": 7,
"minor_version": 1,
"id": "04C3",
"device_type": 4,
"uuid": "9AB19C4B5E655E32B4F00F8A59FD04C3"
},
"port": "COM15",
"extended_device_id": "9AB19C4B5E655E32B4F00F8A59FD04C3",
"extended_firmware_version": "8C20FDC8010AA1E15AA133CDA2534874",
"gravity_compensation": {
"enabled": true,
"scaling_factor": 0.75
},
"handedness": "right",
"streaming_mode": "USB",
"torque_scaling": {
"enabled": true
},
"cursor_offset": {
"x": 0,
"y": 0,
"z": 0
},
"coordinate_origin": "device_base",
"basis": {
"permutation": "XYZ"
}
},
"state": {
"angular_position": {
"a0": -89.66179,
"a1": 222.81319,
"a2": -22.783606
},
"angular_velocity": {
"a0": 0,
"a1": 0,
"a2": 0
},
"body_orientation": {
"x": 0.026611328,
"y": 0.7089844,
"z": -0.00018310547,
"w": 0.7047119
},
"cursor_position": {
"x": 0.12794551,
"y": 0.038903017,
"z": -0.05089721
},
"cursor_velocity": {
"x": -0.00018158932,
"y": -0.000023230032,
"z": -0.00025131978
},
"mode": "idle",
"control_domain": "undefined",
"control_mode": "idle",
"transform": {
"position": {
"x": 0,
"y": 0,
"z": 0
},
"rotation": {
"x": 0,
"y": 0,
"z": 0,
"w": 1
},
"scale": {
"x": 1,
"y": 1,
"z": 1
}
}
},
"status": {
"calibrated": true,
"in_use": false,
"power_supply": true,
"ready": true,
"started": true
}
}
],
"verse_grip": [],
"wireless_verse_grip": [
{
"device_id": "1102",
"config": {
"port": "COM12",
"type": "wireless_verse_grip",
"major_version": 0,
"minor_version": 0,
"hardware_version": 0,
"streaming_mode": "Radio",
"basis": {
"permutation": "XYZ"
}
},
"state": {
"battery_voltage": 3.695,
"battery_level": 0.1583334,
"buttons": {
"a": false,
"b": false,
"c": false
},
"hall": 16,
"orientation": {
"x": -0.0016784668,
"y": -0.037994385,
"z": -0.74890137,
"w": 0.65896606
},
"transform": {
"position": {
"x": 0,
"y": 0,
"z": 0
},
"rotation": {
"x": 0,
"y": 0,
"z": 0,
"w": 1
},
"scale": {
"x": 1,
"y": 1,
"z": 1
}
}
},
"status": {
"connected": true,
"awake": false,
"ready": true
}
}
],
"custom_verse_grip": [
{
"device_id": "1272",
"config": {
"port": "COM13",
"type": "custom_verse_grip",
"major_version": 2,
"minor_version": 1,
"hardware_version": 1,
"streaming_mode": "USB",
"basis": {
"permutation": "XYZ"
}
},
"state": {
"battery_voltage": 4.12,
"battery_level": 0.8666668,
"buttons": {
"a": false,
"b": false,
"c": false
},
"hall": 16,
"orientation": {
"x": 0.6329651,
"y": -0.32418823,
"z": 0.60165405,
"w": 0.35888672
},
"transform": {
"position": {
"x": 0,
"y": 0,
"z": 0
},
"rotation": {
"x": 0,
"y": 0,
"z": 0,
"w": 1
},
"scale": {
"x": 1,
"y": 1,
"z": 1
}
},
"extension_data": [0,0,0,0,5,6,7,8,9,10,11,12]
},
"status": {
"connected": true,
"awake": true,
"ready": true
}
}
]
}).to_string();
let timestamped_state = TimestampedServiceData {
timestamp: std::time::Instant::now(),
data: ServiceData::default(),
};
let state = Arc::new(Mutex::new(timestamped_state));
let mut first = false;
update_state_on_message(state.clone(), sample_message, &mut first).await;
let state_lock = state.lock().await.data.clone();
assert_eq!(state_lock.inverse3.len(), 1);
assert_eq!(state_lock.inverse3[0].device_id, "04C3");
assert_eq!(state_lock.inverse3[0].state.cursor_position.as_ref().unwrap().x, 0.12794551);
assert_eq!(state_lock.inverse3[0].state.cursor_position.as_ref().unwrap().y, 0.038903017);
assert_eq!(state_lock.inverse3[0].state.cursor_position.as_ref().unwrap().z, -0.05089721);
assert_eq!(state_lock.inverse3[0].state.angular_position.as_ref().unwrap().0.x, -89.66179);
assert_eq!(state_lock.inverse3[0].state.angular_position.as_ref().unwrap().0.y, 222.81319);
assert_eq!(state_lock.inverse3[0].state.angular_position.as_ref().unwrap().0.z, -22.783606);
assert_eq!(state_lock.inverse3[0].state.angular_velocity.as_ref().unwrap().0.x, 0.0);
assert_eq!(state_lock.inverse3[0].state.angular_velocity.as_ref().unwrap().0.y, 0.0);
assert_eq!(state_lock.inverse3[0].state.angular_velocity.as_ref().unwrap().0.z, 0.0);
assert_eq!(state_lock.inverse3[0].state.body_orientation.as_ref().unwrap().w, 0.7047119);
assert_eq!(state_lock.inverse3[0].state.body_orientation.as_ref().unwrap().x, 0.026611328);
assert_eq!(state_lock.inverse3[0].state.body_orientation.as_ref().unwrap().y, 0.7089844);
assert_eq!(state_lock.inverse3[0].state.body_orientation.as_ref().unwrap().z, -0.00018310547);
assert_eq!(state_lock.inverse3[0].state.cursor_velocity.as_ref().unwrap().x, -0.00018158932);
assert_eq!(state_lock.inverse3[0].state.cursor_velocity.as_ref().unwrap().y, -0.000023230032);
assert_eq!(state_lock.inverse3[0].state.cursor_velocity.as_ref().unwrap().z, -0.00025131978);
assert_eq!(state_lock.inverse3[0].state.mode, device_model::DeviceMode::Idle);
assert!(state_lock.inverse3[0].status.calibrated);
assert!(!state_lock.inverse3[0].status.in_use);
assert!(state_lock.inverse3[0].status.power_supply);
assert!(state_lock.inverse3[0].status.ready);
assert!(state_lock.inverse3[0].status.started);
assert_eq!(state_lock.verse_grip.len(), 0);
assert_eq!(state_lock.wireless_verse_grip.len(), 1);
assert_eq!(state_lock.custom_verse_grip.len(), 1);
assert_eq!(state_lock.session_id, 176);
assert_eq!(
*state_lock.inverse3[0].state.control_domain.as_ref().unwrap(),
device_model::ControlDomain::Undefined
);
assert_eq!(
*state_lock.inverse3[0].state.control_mode.as_ref().unwrap(),
device_model::ControlMode::Idle
);
let inv_config = state_lock.inverse3[0].config.clone().unwrap();
assert_eq!(inv_config.type_, device_model::DeviceType::Inverse3);
assert_eq!(inv_config.device_info.id, "04C3");
assert_eq!(inv_config.device_info.major_version, 7);
assert_eq!(inv_config.device_info.minor_version, 1);
assert_eq!(
inv_config.device_info.device_type,
device_model::DeviceType::Inverse3
);
assert_eq!(inv_config.device_info.uuid, "9AB19C4B5E655E32B4F00F8A59FD04C3");
assert_eq!(inv_config.port, "COM15");
assert_eq!(inv_config.handedness, device_model::Handedness::Right);
assert_eq!(
&inv_config.extended_device_id,
"9AB19C4B5E655E32B4F00F8A59FD04C3"
);
assert_eq!(
&inv_config.extended_firmware_version,
"8C20FDC8010AA1E15AA133CDA2534874"
);
assert_eq!(inv_config.gravity_compensation.enabled, true);
assert_eq!(inv_config.gravity_compensation.scaling_factor, 0.75);
assert_eq!(inv_config.torque_scaling.enabled, true);
assert_eq!(inv_config.cursor_offset.unwrap().x, 0.0);
assert_eq!(inv_config.cursor_offset.unwrap().y, 0.0);
assert_eq!(inv_config.cursor_offset.unwrap().z, 0.0);
assert_eq!(
inv_config.coordinate_origin.unwrap(),
device_model::CoordinateOrigin::DeviceBase
);
assert_eq!(
inv_config.coordinate_system.as_ref().unwrap().permutation,
"XYZ"
);
assert_eq!(
inv_config.streaming_mode.unwrap(),
device_model::StreamingMode::USB
);
assert_eq!(state_lock.custom_verse_grip[0].device_id, "1272");
let wvg_config_config = state_lock.custom_verse_grip[0].config.clone().unwrap();
assert_eq!(
wvg_config_config.type_,
device_model::DeviceType::CustomVerseGrip
);
assert_eq!(wvg_config_config.port, "COM13");
assert_eq!(wvg_config_config.major_version, 2);
assert_eq!(wvg_config_config.minor_version, 1);
assert_eq!(wvg_config_config.hardware_version, 1);
assert_eq!(state_lock.custom_verse_grip[0].state.battery_voltage, Some(4.12));
assert_eq!(state_lock.custom_verse_grip[0].state.battery_level, Some(0.8666668));
assert_eq!(state_lock.custom_verse_grip[0].state.hall, Some(16));
assert_eq!(state_lock.custom_verse_grip[0].state.orientation.as_ref().unwrap().x, 0.6329651);
assert_eq!(state_lock.custom_verse_grip[0].state.orientation.as_ref().unwrap().y, -0.32418823);
assert_eq!(state_lock.custom_verse_grip[0].state.orientation.as_ref().unwrap().z, 0.60165405);
assert_eq!(state_lock.custom_verse_grip[0].state.orientation.as_ref().unwrap().w, 0.35888672);
assert_eq!(state_lock.custom_verse_grip[0].state.transform.as_ref().unwrap().position.x, 0.0);
assert_eq!(state_lock.custom_verse_grip[0].state.transform.as_ref().unwrap().position.y, 0.0);
assert_eq!(state_lock.custom_verse_grip[0].state.transform.as_ref().unwrap().position.z, 0.0);
assert_eq!(state_lock.custom_verse_grip[0].state.transform.as_ref().unwrap().rotation.x, 0.0);
assert_eq!(state_lock.custom_verse_grip[0].state.transform.as_ref().unwrap().rotation.y, 0.0);
assert_eq!(state_lock.custom_verse_grip[0].state.transform.as_ref().unwrap().rotation.z, 0.0);
assert_eq!(state_lock.custom_verse_grip[0].state.transform.as_ref().unwrap().rotation.w, 1.0);
assert_eq!(state_lock.custom_verse_grip[0].state.transform.as_ref().unwrap().scale.x, 1.0);
assert_eq!(state_lock.custom_verse_grip[0].state.transform.as_ref().unwrap().scale.y, 1.0);
assert_eq!(state_lock.custom_verse_grip[0].state.transform.as_ref().unwrap().scale.z, 1.0);
assert_eq!(
state_lock.custom_verse_grip[0].state.extension_data.as_ref().unwrap(),
&[0, 0, 0, 0, 5, 6, 7, 8, 9, 10, 11, 12]
);
}
#[tokio::test]
async fn test_update_service_struct() {
let mut service_data = ServiceData::default();
service_data.inverse3.push(Inverse3Device {
device_id: "device1".to_string(),
config: None,
state: Inverse3State {
cursor_position: None,
angular_position: None,
angular_velocity: None,
body_orientation: None,
cursor_velocity: None,
mode: device_model::DeviceMode::default(),
control_domain: None,
control_mode: None,
transform: None,
transform_velocity: None,
},
status: Inverse3Status {
calibrated: false,
in_use: false,
power_supply: false,
ready: false,
started: false,
},
});
let mut input_data = ServiceData::default();
input_data.inverse3.push(Inverse3Device {
device_id: "device1".to_string(),
config: service_data.inverse3[0].config.clone(),
state: Inverse3State {
cursor_position: Some(Linear3D { x: 1.0, y: 2.0, z: 3.0 }),
angular_position: None,
angular_velocity: None,
body_orientation: None,
cursor_velocity: None,
mode: device_model::DeviceMode::default(),
control_domain: None,
control_mode: None,
transform: None,
transform_velocity: None,
},
status: Inverse3Status {
calibrated: true,
in_use: true,
power_supply: true,
ready: true,
started: true,
},
});
input_data.inverse3.push(Inverse3Device {
device_id: "device2".to_string(),
config: None,
state: Inverse3State {
cursor_position: None,
angular_position: None,
angular_velocity: None,
body_orientation: None,
cursor_velocity: None,
mode: device_model::DeviceMode::default(),
control_domain: None,
control_mode: None,
transform: None,
transform_velocity: None,
},
status: Inverse3Status {
calibrated: false,
in_use: false,
power_supply: false,
ready: false,
started: false,
},
});
input_data.verse_grip.push(VerseGripDevice {
device_id: "device3".to_string(),
config: None,
state: VerseGripState {
button: Some(true),
hall: Some(100),
orientation: Some(Orientation4D { w: 1.0, x: 0.0, y: 0.0, z: 0.0 }),
},
status: VerseGripStatus { error: Some(0), ready: Some(true) },
});
input_data.wireless_verse_grip.push(WirelessVerseGripDevice {
device_id: "device4".to_string(),
config: None,
state: WirelessVerseGripState {
battery_level: Some(75.0),
battery_voltage: None,
buttons: Some(Buttons { a: true, b: false, c: true, ..Default::default() }),
hall: Some(200),
orientation: Some(Orientation4D { w: 1.0, x: 0.0, y: 0.0, z: 0.0 }),
transform: None,
transform_velocity: None,
},
status: WirelessVerseGripStatus { ready: true, connected: true, awake: true },
});
update_service_struct(&mut service_data, &mut input_data).unwrap();
assert_eq!(service_data.inverse3.len(), 2);
assert_eq!(service_data.inverse3[0].device_id, "device1");
assert_eq!(service_data.inverse3[0].state.mode, device_model::DeviceMode::default());
assert_eq!(service_data.inverse3[1].device_id, "device2");
assert_eq!(service_data.verse_grip.len(), 1);
assert_eq!(service_data.verse_grip[0].device_id, "device3");
assert_eq!(service_data.wireless_verse_grip.len(), 1);
assert_eq!(service_data.wireless_verse_grip[0].device_id, "device4");
assert!(service_data.custom_verse_grip.is_empty());
assert_eq!(service_data.session_id, 0);
}
#[tokio::test]
async fn test_create_cmd_msgs_i3commands() {
let mut service_msg = ServiceMsg::default();
let set_cursor_force = SetCursorForce {
values: Force { x: 1.0, y: 2.0, z: 3.0 },
execute: true,
};
let inverse3_msg = Inverse3Msg {
device_id: "device1".to_string(),
command: I3Command::SetCursorForce(set_cursor_force.clone()),
};
let command = Command::I3Command(inverse3_msg.clone());
update_msg_from_command(command, &mut service_msg).unwrap();
assert_eq!(service_msg.inverse3.len(), 1);
assert_eq!(service_msg.inverse3[0].device_id, "device1");
assert!(matches!(service_msg.inverse3[0].command, I3Command::SetCursorForce(_)));
assert_eq!(service_msg.custom_verse_grip.len(), 0);
}
#[tokio::test]
async fn test_create_cmd_msgs_vgcommands() {
let mut service_msg = ServiceMsg::default();
let verse_grip_msg = VerseGripMsg {
device_id: "device2".to_string(),
command: VgCommand::ProbeOrientation(ProbeOrientation { probe_orientation: () }),
};
let command = Command::VgCommand(verse_grip_msg.clone());
update_msg_from_command(command, &mut service_msg).unwrap();
assert_eq!(service_msg.verse_grip.len(), 1);
assert_eq!(service_msg.verse_grip[0].device_id, "device2");
assert!(matches!(service_msg.verse_grip[0].command, VgCommand::ProbeOrientation(_)));
assert_eq!(service_msg.custom_verse_grip.len(), 0);
}
#[tokio::test]
async fn test_create_cmd_msgs_wvgcommands() {
let mut service_msg = ServiceMsg::default();
let wireless_verse_grip_msg = VerseGripMsg {
device_id: "device3".to_string(),
command: VgCommand::ProbeOrientation(ProbeOrientation { probe_orientation: () }),
};
let command = Command::WvgCommand(wireless_verse_grip_msg.clone());
update_msg_from_command(command, &mut service_msg).unwrap();
assert_eq!(service_msg.wireless_verse_grip.len(), 1);
assert_eq!(service_msg.wireless_verse_grip[0].device_id, "device3");
assert!(matches!(service_msg.wireless_verse_grip[0].command, VgCommand::ProbeOrientation(_)));
assert_eq!(service_msg.custom_verse_grip.len(), 1);
}
#[tokio::test]
async fn test_update_entrire_msg_multiple_commands() {
let mut service_msg = ServiceMsg::default();
let set_cursor_force = SetCursorForce {
values: Force { x: 1.0, y: 2.0, z: 3.0 },
execute: true,
};
let _set_cursor_position = SetCursorPosition { values: Linear3D { x: 4.0, y: 5.0, z: 6.0 } };
let inverse3_msg = Inverse3Msg {
device_id: "device1".to_string(),
command: I3Command::SetCursorForce(set_cursor_force.clone()),
};
let verse_grip_msg = VerseGripMsg {
device_id: "device2".to_string(),
command: VgCommand::ProbeOrientation(ProbeOrientation { probe_orientation: () }),
};
let wireless_verse_grip_msg = VerseGripMsg {
device_id: "device3".to_string(),
command: VgCommand::ProbeOrientation(ProbeOrientation { probe_orientation: () }),
};
let commands = vec![
Command::I3Command(inverse3_msg.clone()),
Command::VgCommand(verse_grip_msg.clone()),
Command::WvgCommand(wireless_verse_grip_msg.clone())
];
update_entrire_msg(&commands, &mut service_msg).unwrap();
assert_eq!(service_msg.inverse3.len(), 1);
assert_eq!(service_msg.inverse3[0].device_id, "device1");
assert!(matches!(service_msg.inverse3[0].command, I3Command::SetCursorForce(_)));
assert_eq!(service_msg.verse_grip.len(), 1);
assert_eq!(service_msg.verse_grip[0].device_id, "device2");
assert!(matches!(service_msg.verse_grip[0].command, VgCommand::ProbeOrientation(_)));
assert_eq!(service_msg.wireless_verse_grip.len(), 1);
assert_eq!(service_msg.wireless_verse_grip[0].device_id, "device3");
assert!(matches!(service_msg.wireless_verse_grip[0].command, VgCommand::ProbeOrientation(_)));
assert_eq!(service_msg.custom_verse_grip.len(), 1);
}
#[tokio::test]
async fn test_update_msg_from_command_vgcommands() {
let mut service_msg = ServiceMsg::default();
let verse_grip_msg = VerseGripMsg {
device_id: "device4".to_string(),
command: VgCommand::ProbeOrientation(ProbeOrientation { probe_orientation: () }),
};
let command = Command::VgCommand(verse_grip_msg.clone());
update_msg_from_command(command, &mut service_msg).unwrap();
assert_eq!(service_msg.verse_grip.len(), 1);
assert_eq!(service_msg.verse_grip[0].device_id, "device4");
assert!(matches!(service_msg.verse_grip[0].command, VgCommand::ProbeOrientation(_)));
assert_eq!(service_msg.custom_verse_grip.len(), 0);
}
#[tokio::test]
async fn test_update_msg_from_command_wvgcommands() {
let mut service_msg = ServiceMsg::default();
let wireless_verse_grip_msg = VerseGripMsg {
device_id: "device5".to_string(),
command: VgCommand::ProbeOrientation(ProbeOrientation { probe_orientation: () }),
};
let command = Command::WvgCommand(wireless_verse_grip_msg.clone());
update_msg_from_command(command, &mut service_msg).unwrap();
assert_eq!(service_msg.wireless_verse_grip.len(), 1);
assert_eq!(service_msg.wireless_verse_grip[0].device_id, "device5");
assert!(matches!(service_msg.wireless_verse_grip[0].command, VgCommand::ProbeOrientation(_)));
assert_eq!(service_msg.custom_verse_grip.len(), 1);
}
#[tokio::test]
async fn test_deserialize_service_data() {
let sample_message =
json!({
"inverse3": [
{
"device_id": "048D",
"config": {
"type": "inverse3",
"device_info": {
"major_version": 7,
"minor_version": 1,
"id": "048D",
"device_type": "inverse3",
"uuid": "ED78730C8173560784AB4167961D048D"
},
"port": "COM16",
"extended_device_id": "ED78730C8173560784AB4167961D048D",
"extended_firmware_version": "8C20FDC8010AA1E15AA133CDA2534874",
"gravity_compensation": {
"enabled": true,
"scaling_factor": 0.85
},
"handedness": "right",
"streaming_mode": "USB",
"torque_scaling": {
"enabled": true
},
"cursor_offset": {
"x": 0,
"y": 0,
"z": 0
},
"coordinate_origin": "device_base",
"coordinate_system": {
"permutation": "XYZ"
}
},
"state": {
"angular_position": {
"a0": -88.81024,
"a1": 118.23612,
"a2": 3.7508194
},
"angular_velocity": {
"a0": 0,
"a1": 0,
"a2": 0
},
"body_orientation": {
"x": 0.035461426,
"y": 0.6989746,
"z": 0.0025634766,
"w": 0.71429443
},
"cursor_position": {
"x": 0.025769988,
"y": -0.16684988,
"z": 0.09366201
},
"cursor_velocity": {
"x": 0,
"y": 0,
"z": 0
},
"mode": "idle",
"control_domain": "undefined",
"control_mode": "idle",
"transform": {
"position": {
"x": 0,
"y": 0,
"z": 0
},
"rotation": {
"x": 0,
"y": 0,
"z": 0,
"w": 1
},
"scale": {
"x": 1,
"y": 1,
"z": 1
}
}
},
"status": {
"calibrated": true,
"in_use": false,
"power_supply": true,
"ready": true,
"started": true
}
}
],
"verse_grip": [
{
"device_id": "61548",
"config": {
"port": "COM3",
"type": "verse_grip"
},
"state": {
"button": false,
"hall": 0,
"orientation": {
"x": -0.5019531,
"y": 0.8632202,
"z": -0.048095703,
"w": -0.022338867
}
},
"status": {
"error": 0,
"ready": true
}
}
],
"wireless_verse_grip": [
{
"device_id": "0",
"config": {
"port": "COM6",
"type": "wireless_verse_grip",
"major_version": 1,
"minor_version": 4,
"hardware_version": 1
},
"state": {
"battery_level": 0.816,
"battery_voltage": 3.77,
"buttons": {
"a": false,
"b": false,
"c": false
},
"hall": 16,
"orientation": {
"x": -0.019866943,
"y": -0.017486572,
"z": 0.05508423,
"w": -0.9963989
}
},
"status": {
"connected": true,
"awake": true,
"ready": true
}
}
],
"custom_verse_grip": [],
"session_id": 0
}).to_string();
let mut first = false;
let timestamped_state = TimestampedServiceData {
timestamp: std::time::Instant::now(),
data: ServiceData::default(),
};
let timestamped_state = Arc::new(Mutex::new(timestamped_state));
update_state_on_message(timestamped_state.clone(), sample_message, &mut first).await;
let state_lock = timestamped_state.lock().await.data.clone();
println!("Deserialized State: {:#?}", state_lock);
assert_eq!(state_lock.session_id, 0);
assert_eq!(state_lock.inverse3.len(), 1);
let inverse3 = &state_lock.inverse3[0];
assert_eq!(inverse3.device_id, "048D");
let config = &inverse3.config.clone().unwrap();
assert_eq!(config.type_, device_model::DeviceType::Inverse3);
assert_eq!(config.device_info.id, "048D");
assert_eq!(config.device_info.uuid, "ED78730C8173560784AB4167961D048D");
assert_eq!(config.port, "COM16");
assert_eq!(config.gravity_compensation.enabled, true);
assert_eq!(config.gravity_compensation.scaling_factor, 0.85);
assert_eq!(config.handedness, device_model::Handedness::Right);
assert_eq!(config.streaming_mode.unwrap(), device_model::StreamingMode::USB);
assert_eq!(config.cursor_offset.unwrap().x, 0.0);
assert_eq!(config.cursor_offset.unwrap().y, 0.0);
assert_eq!(config.cursor_offset.unwrap().z, 0.0);
assert_eq!(
config.coordinate_origin.unwrap(),
device_model::CoordinateOrigin::DeviceBase
);
assert_eq!(config.coordinate_system.as_ref().unwrap().permutation, "XYZ");
assert_eq!(config.id, "");
assert_eq!(config.extended_device_id, "ED78730C8173560784AB4167961D048D");
assert_eq!(
config.extended_firmware_version,
"8C20FDC8010AA1E15AA133CDA2534874"
);
assert_eq!(config.torque_scaling.enabled, true);
let state = &inverse3.state;
assert_eq!(state.angular_position.as_ref().unwrap().0.x, -88.81024);
assert_eq!(state.angular_position.as_ref().unwrap().0.y, 118.23612);
assert_eq!(state.angular_position.as_ref().unwrap().0.z, 3.7508194);
assert_eq!(state.angular_velocity.as_ref().unwrap().0.x, 0.0);
assert_eq!(state.angular_velocity.as_ref().unwrap().0.y, 0.0);
assert_eq!(state.angular_velocity.as_ref().unwrap().0.z, 0.0);
assert_eq!(state.body_orientation.as_ref().unwrap().w, 0.71429443);
assert_eq!(state.body_orientation.as_ref().unwrap().x, 0.035461426);
assert_eq!(state.body_orientation.as_ref().unwrap().y, 0.6989746);
assert_eq!(state.body_orientation.as_ref().unwrap().z, 0.0025634766);
assert_eq!(state.cursor_position.as_ref().unwrap().x, 0.025769988);
assert_eq!(state.cursor_position.as_ref().unwrap().y, -0.16684988);
assert_eq!(state.cursor_position.as_ref().unwrap().z, 0.09366201);
assert_eq!(state.cursor_velocity.as_ref().unwrap().x, 0.0);
assert_eq!(state.cursor_velocity.as_ref().unwrap().y, 0.0);
assert_eq!(state.cursor_velocity.as_ref().unwrap().z, 0.0);
assert_eq!(state.mode, device_model::DeviceMode::Idle);
assert_eq!(*state.control_domain.as_ref().unwrap(), device_model::ControlDomain::Undefined);
assert_eq!(*state.control_mode.as_ref().unwrap(), device_model::ControlMode::Idle);
let status = &inverse3.status;
assert_eq!(status.calibrated, true);
assert_eq!(status.in_use, false);
assert_eq!(status.power_supply, true);
assert_eq!(status.ready, true);
assert_eq!(status.started, true);
assert_eq!(state_lock.verse_grip.len(), 1);
let verse_grip = &state_lock.verse_grip[0];
assert_eq!(verse_grip.device_id, "61548");
let verse_grip_config = &verse_grip.config.clone().unwrap();
assert_eq!(verse_grip_config.port, "COM3");
assert_eq!(verse_grip_config.type_, device_model::DeviceType::VerseGrip);
let verse_grip_state = &verse_grip.state;
assert_eq!(verse_grip_state.button, Some(false));
assert_eq!(verse_grip_state.hall, Some(0));
assert_eq!(verse_grip_state.orientation.as_ref().unwrap().x, -0.5019531);
assert_eq!(verse_grip_state.orientation.as_ref().unwrap().y, 0.8632202);
assert_eq!(verse_grip_state.orientation.as_ref().unwrap().z, -0.048095703);
assert_eq!(verse_grip_state.orientation.as_ref().unwrap().w, -0.022338867);
let verse_grip_status = &verse_grip.status;
assert_eq!(verse_grip_status.error, Some(0));
assert_eq!(verse_grip_status.ready, Some(true));
let wireless_verse_grip = &state_lock.wireless_verse_grip[0];
assert_eq!(wireless_verse_grip.device_id, "0");
let wireless_verse_grip_config = &wireless_verse_grip.config.clone().unwrap();
assert_eq!(wireless_verse_grip_config.port, "COM6");
assert_eq!(wireless_verse_grip_config.type_, device_model::DeviceType::WirelessVerseGrip);
assert_eq!(wireless_verse_grip_config.major_version, 1);
assert_eq!(wireless_verse_grip_config.minor_version, 4);
assert_eq!(wireless_verse_grip_config.hardware_version, 1);
let wireless_verse_grip_state = &wireless_verse_grip.state;
assert_eq!(wireless_verse_grip_state.battery_level, Some(0.816));
assert_eq!(wireless_verse_grip_state.battery_voltage, Some(3.77));
assert_eq!(wireless_verse_grip_state.buttons.as_ref().unwrap().a, false);
assert_eq!(wireless_verse_grip_state.buttons.as_ref().unwrap().b, false);
assert_eq!(wireless_verse_grip_state.buttons.as_ref().unwrap().c, false);
assert_eq!(wireless_verse_grip_state.hall, Some(16));
assert_eq!(wireless_verse_grip_state.orientation.as_ref().unwrap().x, -0.019866943);
assert_eq!(wireless_verse_grip_state.orientation.as_ref().unwrap().y, -0.017486572);
assert_eq!(wireless_verse_grip_state.orientation.as_ref().unwrap().z, 0.05508423);
assert_eq!(wireless_verse_grip_state.orientation.as_ref().unwrap().w, -0.9963989);
let wireless_verse_grip_status = &wireless_verse_grip.status;
assert_eq!(wireless_verse_grip_status.connected, true);
assert_eq!(wireless_verse_grip_status.awake, true);
assert_eq!(wireless_verse_grip_status.ready, true);
assert!(state_lock.custom_verse_grip.is_empty());
}
#[tokio::test]
async fn test_parse_new_wireless_versegrip_types() {
let sample_message = json!({
"session_id": 114,
"session": {
"session_id": 114,
"config": {
"profile": { "name": "default" },
"started_time": "2026-04-14 21:03:41.8127122"
}
},
"inverse3": [],
"verse_grip": [],
"wireless_verse_grip": [
{
"device_id": "1412",
"config": {
"port": "COM22",
"type": "ruko",
"major_version": 2,
"minor_version": 2,
"hardware_version": 1,
"streaming_mode": "Radio",
"preset": "defaults",
"basis": { "permutation": "XYZ" },
"mount": {
"position": { "x": 0, "y": 0, "z": 0 },
"rotation": { "x": 0, "y": 0, "z": 0, "w": 1 },
"scale": { "x": 1, "y": 1, "z": 1 }
}
},
"state": {
"battery_voltage": 4.17,
"battery_level": 0.95000046,
"buttons": {
"down": false,
"up": false,
"right": false,
"left": false
},
"trigger": 0,
"wheel": 210,
"hall": 15,
"orientation": {
"x": -0.45703125,
"y": -0.48660278,
"z": -0.2996521,
"w": 0.6790161
},
"transform": {
"position": { "x": 0, "y": 0, "z": 0 },
"rotation": { "x": 0, "y": 0, "z": 0, "w": 1 },
"scale": { "x": 1, "y": 1, "z": 1 }
},
"transform_velocity": {
"position": { "x": 0, "y": 0, "z": 0 },
"rotation": { "x": 0, "y": 0, "z": 0, "w": 1 },
"scale": { "x": 0, "y": 0, "z": 0 }
}
},
"status": {
"awake": true
}
}
],
"custom_verse_grip": [
{
"device_id": "1412",
"config": {
"port": "COM22",
"type": "custom_verse_grip",
"major_version": 2,
"minor_version": 2,
"hardware_version": 1,
"streaming_mode": "Radio",
"preset": "defaults",
"basis": { "permutation": "XYZ" },
"mount": {
"position": { "x": 0, "y": 0, "z": 0 },
"rotation": { "x": 0, "y": 0, "z": 0, "w": 1 },
"scale": { "x": 1, "y": 1, "z": 1 }
}
},
"state": {
"battery_voltage": 4.17,
"battery_level": 0.95000046,
"buttons": {
"a": false,
"b": false,
"c": false
},
"hall": 15,
"orientation": {
"x": -0.45703125,
"y": -0.48660278,
"z": -0.2996521,
"w": 0.6790161
},
"transform": {
"position": { "x": 0, "y": 0, "z": 0 },
"rotation": { "x": 0, "y": 0, "z": 0, "w": 1 },
"scale": { "x": 1, "y": 1, "z": 1 }
},
"transform_velocity": {
"position": { "x": 0, "y": 0, "z": 0 },
"rotation": { "x": 0, "y": 0, "z": 0, "w": 1 },
"scale": { "x": 0, "y": 0, "z": 0 }
},
"extension_data": [0, 0, 82, 79, 5, 6, 7, 8, 9, 10, 11, 12]
},
"status": {
"connected": true,
"awake": true,
"ready": true
}
}
]
}).to_string();
let timestamped_state = TimestampedServiceData {
timestamp: std::time::Instant::now(),
data: ServiceData::default(),
};
let state = Arc::new(Mutex::new(timestamped_state));
let mut first = false;
update_state_on_message(state.clone(), sample_message, &mut first).await;
let state_lock = state.lock().await.data.clone();
assert_eq!(state_lock.session_id, 114);
assert_eq!(state_lock.wireless_verse_grip.len(), 0);
assert_eq!(state_lock.custom_verse_grip.len(), 1);
assert_eq!(state_lock.custom_verse_grip[0].device_id, "1412");
let cvg_config = state_lock.custom_verse_grip[0].config.clone().unwrap();
assert_eq!(cvg_config.type_, device_model::DeviceType::CustomVerseGrip);
assert_eq!(
state_lock.custom_verse_grip[0].state.extension_data.as_ref().unwrap(),
&[0, 0, 82, 79, 5, 6, 7, 8, 9, 10, 11, 12]
);
let cvg_buttons = state_lock.custom_verse_grip[0].state.buttons.unwrap();
assert_eq!(cvg_buttons.a, false);
assert_eq!(cvg_buttons.b, false);
assert_eq!(cvg_buttons.c, false);
assert_eq!(cvg_buttons.down, false); }
#[tokio::test]
async fn test_standalone_ruko_not_filtered() {
let sample_message = json!({
"session_id": 200,
"inverse3": [],
"verse_grip": [],
"wireless_verse_grip": [
{
"device_id": "2001",
"config": {
"port": "COM30",
"type": "ruko",
"major_version": 2,
"minor_version": 2,
"hardware_version": 1,
"streaming_mode": "Radio",
"basis": { "permutation": "XYZ" }
},
"state": {
"battery_voltage": 4.0,
"battery_level": 0.9,
"buttons": { "down": false, "up": true, "right": false, "left": false },
"hall": 10,
"orientation": { "x": 0.0, "y": 0.0, "z": 0.0, "w": 1.0 }
},
"status": { "awake": true }
}
],
"custom_verse_grip": []
}).to_string();
let timestamped_state = TimestampedServiceData {
timestamp: std::time::Instant::now(),
data: ServiceData::default(),
};
let state = Arc::new(Mutex::new(timestamped_state));
let mut first = false;
update_state_on_message(state.clone(), sample_message, &mut first).await;
let state_lock = state.lock().await.data.clone();
assert_eq!(state_lock.wireless_verse_grip.len(), 1);
assert_eq!(state_lock.wireless_verse_grip[0].device_id, "2001");
let cfg = state_lock.wireless_verse_grip[0].config.clone().unwrap();
assert_eq!(cfg.type_, device_model::DeviceType::Ruko);
assert_eq!(state_lock.wireless_verse_grip[0].state.buttons.unwrap().up, true);
assert_eq!(state_lock.custom_verse_grip.len(), 0);
}
#[tokio::test]
async fn test_parse_transformless_format() {
let sample_message = json!({
"session_id": 15,
"session": {
"session_id": 15,
"config": {
"profile": { "name": "default" },
"started_time": "2026-04-16 18:13:29.7875056"
}
},
"inverse3": [
{
"device_id": "04BA",
"config": {
"type": "inverse3",
"device_info": {
"major_version": 7,
"minor_version": 5,
"id": "04BA",
"device_type": 4,
"uuid": "2D35F80DD9005F599B68F49944CB04BA"
},
"port": "COM18",
"extended_device_id": "2D35F80DD9005F599B68F49944CB04BA",
"extended_firmware_version": "F7BACE8FF058FF8206CD336A475CF81F",
"gravity_compensation": {
"enabled": true,
"scaling_factor": 0
},
"handedness": "right",
"streaming_mode": "USB",
"torque_scaling": {
"enabled": true
},
"preset": "defaults",
"basis": {
"permutation": "XYZ"
},
"mount": {},
"filters": {
"force_gate": { "gain": 0.3 },
"damping": { "scalar": -1 }
}
},
"state": {
"angular_position": { "a0": 0, "a1": 0, "a2": 0 },
"angular_velocity": { "a0": 0, "a1": 0, "a2": 0 },
"body_orientation": {
"x": -0.049804688,
"y": 0.69537354,
"z": -0.0021972656,
"w": 0.71691895
},
"cursor_position": {
"x": 0.020575874,
"y": -0.02761598,
"z": 0.19947645
},
"cursor_velocity": { "x": 0, "y": 0, "z": 0 },
"current_cursor_force": { "x": 0, "y": 0, "z": 0 },
"current_cursor_position": { "x": 0, "y": 0, "z": 0 },
"current_angular_torques": { "a0": 0, "a1": 0, "a2": 0 },
"current_angular_position": { "a0": 0, "a1": 0, "a2": 0 },
"mode": "idle",
"control_domain": "undefined",
"control_mode": "idle",
"transform": {},
"transform_velocity": {}
},
"status": {
"calibrated": true,
"in_use": false,
"power_supply": true,
"ready": true,
"started": true
}
}
],
"verse_grip": [],
"wireless_verse_grip": [
{
"device_id": "1402",
"config": {
"port": "COM46",
"type": "ruko",
"major_version": 2,
"minor_version": 2,
"hardware_version": 1,
"streaming_mode": "Radio",
"preset": "defaults",
"basis": { "permutation": "XYZ" },
"mount": {}
},
"state": {
"battery_voltage": 4.075,
"battery_level": 0.7916666,
"buttons": {
"down": false,
"up": false,
"right": false,
"left": false
},
"trigger": 3,
"wheel": 166,
"hall": 15,
"orientation": {
"x": 0.47711182,
"y": 0.431427,
"z": -0.5709839,
"w": 0.50668335
},
"transform": {},
"transform_velocity": {}
},
"status": {
"awake": true
}
}
],
"custom_verse_grip": [
{
"device_id": "1402",
"config": {
"port": "COM46",
"type": "custom_verse_grip",
"major_version": 2,
"minor_version": 2,
"hardware_version": 1,
"streaming_mode": "Radio",
"preset": "defaults",
"basis": { "permutation": "XYZ" },
"mount": {}
},
"state": {
"battery_voltage": 4.075,
"battery_level": 0.7916666,
"buttons": {
"a": false,
"b": false,
"c": false
},
"hall": 15,
"orientation": {
"x": 0.47711182,
"y": 0.431427,
"z": -0.5709839,
"w": 0.50668335
},
"transform": {},
"transform_velocity": {},
"extension_data": [0, 3, 65, 30, 5, 6, 7, 8, 9, 10, 11, 12]
},
"status": {
"connected": true,
"awake": true,
"ready": true
}
}
]
}).to_string();
let timestamped_state = TimestampedServiceData {
timestamp: std::time::Instant::now(),
data: ServiceData::default(),
};
let state = Arc::new(Mutex::new(timestamped_state));
let mut first = false;
update_state_on_message(state.clone(), sample_message, &mut first).await;
let state_lock = state.lock().await.data.clone();
assert_eq!(state_lock.session_id, 15);
assert_eq!(state_lock.inverse3.len(), 1);
assert_eq!(state_lock.inverse3[0].device_id, "04BA");
assert_eq!(state_lock.inverse3[0].state.cursor_position.as_ref().unwrap().x, 0.020575874);
assert_eq!(state_lock.inverse3[0].state.cursor_position.as_ref().unwrap().y, -0.02761598);
assert_eq!(state_lock.inverse3[0].state.cursor_position.as_ref().unwrap().z, 0.19947645);
assert_eq!(state_lock.inverse3[0].state.body_orientation.as_ref().unwrap().w, 0.71691895);
assert_eq!(state_lock.inverse3[0].state.mode, device_model::DeviceMode::Idle);
assert!(state_lock.inverse3[0].state.transform.is_none());
assert!(state_lock.inverse3[0].state.transform_velocity.is_none());
let inv_config = state_lock.inverse3[0].config.clone().unwrap();
assert_eq!(inv_config.type_, device_model::DeviceType::Inverse3);
assert_eq!(inv_config.device_info.id, "04BA");
assert_eq!(inv_config.device_info.major_version, 7);
assert_eq!(inv_config.device_info.minor_version, 5);
assert!(state_lock.inverse3[0].status.calibrated);
assert!(state_lock.inverse3[0].status.ready);
assert_eq!(state_lock.wireless_verse_grip.len(), 0);
assert_eq!(state_lock.custom_verse_grip.len(), 1);
assert_eq!(state_lock.custom_verse_grip[0].device_id, "1402");
let cvg_config = state_lock.custom_verse_grip[0].config.clone().unwrap();
assert_eq!(cvg_config.type_, device_model::DeviceType::CustomVerseGrip);
assert_eq!(cvg_config.major_version, 2);
assert_eq!(cvg_config.minor_version, 2);
assert_eq!(state_lock.custom_verse_grip[0].state.battery_voltage, Some(4.075));
assert_eq!(state_lock.custom_verse_grip[0].state.battery_level, Some(0.7916666));
assert_eq!(state_lock.custom_verse_grip[0].state.hall, Some(15));
assert_eq!(state_lock.custom_verse_grip[0].state.orientation.as_ref().unwrap().x, 0.47711182);
assert!(state_lock.custom_verse_grip[0].state.transform.is_none());
assert!(state_lock.custom_verse_grip[0].state.transform_velocity.is_none());
assert_eq!(
state_lock.custom_verse_grip[0].state.extension_data.as_ref().unwrap(),
&[0, 3, 65, 30, 5, 6, 7, 8, 9, 10, 11, 12]
);
}
#[tokio::test]
async fn test_parse_mixed_transform_format() {
let sample_message = json!({
"session_id": 16,
"inverse3": [
{
"device_id": "04BA",
"config": {
"type": "inverse3",
"device_info": {
"major_version": 7,
"minor_version": 5,
"id": "04BA",
"device_type": 4,
"uuid": "2D35F80DD9005F599B68F49944CB04BA"
},
"port": "COM18",
"extended_device_id": "2D35F80DD9005F599B68F49944CB04BA",
"extended_firmware_version": "F7BACE8FF058FF8206CD336A475CF81F",
"gravity_compensation": { "enabled": true, "scaling_factor": 0 },
"handedness": "right",
"streaming_mode": "USB",
"torque_scaling": { "enabled": true },
"basis": { "permutation": "XYZ" }
},
"state": {
"angular_position": { "a0": 0, "a1": 0, "a2": 0 },
"angular_velocity": { "a0": 0, "a1": 0, "a2": 0 },
"body_orientation": { "x": 0, "y": 0, "z": 0, "w": 1 },
"cursor_position": { "x": 0.1, "y": 0.2, "z": 0.3 },
"cursor_velocity": { "x": 0, "y": 0, "z": 0 },
"mode": "idle",
"control_domain": "undefined",
"control_mode": "idle",
"transform": {
"position": { "x": 1.0, "y": 2.0, "z": 3.0 },
"rotation": { "x": 0, "y": 0, "z": 0, "w": 1 },
"scale": { "x": 1, "y": 1, "z": 1 }
},
"transform_velocity": {}
},
"status": {
"calibrated": true,
"in_use": false,
"power_supply": true,
"ready": true,
"started": true
}
}
],
"verse_grip": [],
"wireless_verse_grip": [],
"custom_verse_grip": []
}).to_string();
let timestamped_state = TimestampedServiceData {
timestamp: std::time::Instant::now(),
data: ServiceData::default(),
};
let state = Arc::new(Mutex::new(timestamped_state));
let mut first = false;
update_state_on_message(state.clone(), sample_message, &mut first).await;
let state_lock = state.lock().await.data.clone();
assert_eq!(state_lock.inverse3.len(), 1);
let i3_transform = state_lock.inverse3[0].state.transform.as_ref().unwrap();
assert_eq!(i3_transform.position.x, 1.0);
assert_eq!(i3_transform.position.y, 2.0);
assert_eq!(i3_transform.position.z, 3.0);
assert_eq!(i3_transform.rotation.w, 1.0);
assert_eq!(i3_transform.scale.x, 1.0);
assert!(state_lock.inverse3[0].state.transform_velocity.is_none());
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_force_render_full_state_command() {
let mut service_msg = ServiceMsg::default();
let command = vec![Command::ForceRenderFullState];
update_entrire_msg(&command, &mut service_msg).unwrap();
let serialized = serde_json::json!({"session": {"force_render_full_state": {}}});
assert_eq!(serialized, serde_json::json!({"session": {"force_render_full_state": {}}}));
}
}