use crate::device_model::{
Command,
CustomVerseGripDevice,
CustomVerseGripMessage,
ForceRenderFullStateMsg,
Inverse3Device,
Inverse3Message,
Inverse3OutMsg,
ProbeCmd,
SdfCommand,
SdfCommandMode,
SdfHfxObject,
SdfOutputConfigure,
ServiceData,
ServiceMsg,
ServiceState,
SetAngularPositionCmd,
SetAngularTorquesCmd,
SetCursorForceCmd,
SetCursorPositionCmd,
SetExtensionDataCmd,
SetTransformCmd,
SessionInfo,
TimestampedServiceData,
VerseGripConfigure,
VerseGripDevice,
VerseGripDuplicateMode,
VerseGripMessage,
VerseGripOutMsg,
WirelessVerseGripDevice,
WirelessVerseGripMessage,
};
use log::{ error, trace };
use serde::de::DeserializeOwned;
use serde_json::Value;
use std::sync::Arc;
use std::sync::atomic::{ AtomicU64, Ordering };
use tokio::sync::Mutex;
const PARSE_ERROR_LOG_INITIAL: u64 = 3;
const PARSE_ERROR_LOG_EVERY: u64 = 1000;
const PARSE_ERROR_SAMPLE_LEN: usize = 400;
const PARSE_ERROR_AREA_CONTEXT_BYTES: usize = 160;
static PARSE_ERROR_COUNTER: AtomicU64 = AtomicU64::new(0);
fn payload_sample(payload: &str) -> String {
let mut out: String = payload.chars().take(PARSE_ERROR_SAMPLE_LEN).collect();
out = out.replace('\n', " ").replace('\r', " ");
if payload.chars().count() > PARSE_ERROR_SAMPLE_LEN {
out.push_str(" ...(truncated)");
}
out
}
fn byte_index_for_line_col(payload: &str, line: usize, column: usize) -> Option<usize> {
if line == 0 || column == 0 {
return None;
}
let mut current_line = 1usize;
let mut current_col = 1usize;
for (idx, ch) in payload.char_indices() {
if current_line == line && current_col == column {
return Some(idx);
}
if ch == '\n' {
current_line += 1;
current_col = 1;
} else {
current_col += 1;
}
}
if current_line == line && current_col == column {
return Some(payload.len());
}
None
}
fn problem_area_sample(payload: &str, err: &serde_json::Error) -> String {
let line = err.line();
let column = err.column();
let target = match byte_index_for_line_col(payload, line, column) {
Some(idx) => idx,
None => {
return payload_sample(payload);
}
};
let mut start = target.saturating_sub(PARSE_ERROR_AREA_CONTEXT_BYTES);
while start > 0 && !payload.is_char_boundary(start) {
start -= 1;
}
let mut end = (target + PARSE_ERROR_AREA_CONTEXT_BYTES).min(payload.len());
while end < payload.len() && !payload.is_char_boundary(end) {
end += 1;
}
let mut snippet = payload[start..end].replace('\n', " ").replace('\r', " ");
if start > 0 {
snippet = format!("...{}", snippet);
}
if end < payload.len() {
snippet.push_str("...");
}
format!("line:{} col:{} near: {}", line, column, snippet)
}
fn log_parse_error(context: &str, err: &serde_json::Error, payload: &str) {
let idx = PARSE_ERROR_COUNTER.fetch_add(1, Ordering::Relaxed) + 1;
let should_emit = idx <= PARSE_ERROR_LOG_INITIAL || idx % PARSE_ERROR_LOG_EVERY == 0;
if should_emit {
let detail = if idx <= PARSE_ERROR_LOG_INITIAL {
format!("payload_sample: {}", payload_sample(payload))
} else {
format!("problem_area: {}", problem_area_sample(payload, err))
};
error!("{} [count={}] | error: {} | {}", context, idx, err, detail);
} else {
trace!("Suppressed parse error log [{}]: {}", idx, context);
}
}
fn ensure_inverse3<'a>(msg: &'a mut ServiceMsg, device_id: &str) -> &'a mut Inverse3OutMsg {
if let Some(pos) = msg.inverse3.iter().position(|d| d.device_id == device_id) {
&mut msg.inverse3[pos]
} else {
msg.inverse3.push(Inverse3OutMsg {
device_id: device_id.to_string(),
commands: None,
configure: None,
});
msg.inverse3.last_mut().unwrap()
}
}
fn ensure_versegrip<'a>(
vec: &'a mut Vec<VerseGripOutMsg>,
device_id: &str
) -> &'a mut VerseGripOutMsg {
if let Some(pos) = vec.iter().position(|d| d.device_id == device_id) {
&mut vec[pos]
} else {
vec.push(VerseGripOutMsg {
device_id: device_id.to_string(),
commands: None,
configure: None,
});
vec.last_mut().unwrap()
}
}
pub fn update_entire_msg(
commands: &[Command],
msg: &mut ServiceMsg
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
#[cfg(feature = "tracy")]
let _span = tracy_client::span!("state::update_entire_msg");
for command in commands {
if let Err(e) = update_msg_from_command(command.clone(), msg) {
error!("Error while creating command message: {}", e);
return Err(e);
}
}
Ok(())
}
pub fn update_msg_from_command(
command: Command,
msg: &mut ServiceMsg
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
#[cfg(feature = "tracy")]
let _span = tracy_client::span!("state::update_msg_from_command");
match command {
Command::SetCursorForce { device_id, vector, execute } => {
let entry = ensure_inverse3(msg, &device_id);
let cmds = entry.commands.get_or_insert_with(Default::default);
cmds.set_cursor_force = Some(SetCursorForceCmd { vector, execute });
}
Command::SetCursorPosition { device_id, position, execute } => {
let entry = ensure_inverse3(msg, &device_id);
let cmds = entry.commands.get_or_insert_with(Default::default);
cmds.set_cursor_position = Some(SetCursorPositionCmd { position, execute });
}
Command::SetAngularTorques { device_id, torques, execute } => {
let entry = ensure_inverse3(msg, &device_id);
let cmds = entry.commands.get_or_insert_with(Default::default);
cmds.set_angular_torques = Some(SetAngularTorquesCmd { torques, execute });
}
Command::SetAngularPosition { device_id, angles, execute } => {
let entry = ensure_inverse3(msg, &device_id);
let cmds = entry.commands.get_or_insert_with(Default::default);
cmds.set_angular_position = Some(SetAngularPositionCmd { angles, execute });
}
Command::SetI3Transform { device_id, transform, execute } => {
let entry = ensure_inverse3(msg, &device_id);
let cmds = entry.commands.get_or_insert_with(Default::default);
cmds.set_transform = Some(SetTransformCmd { transform, execute });
}
Command::ProbePosition { device_id } => {
let entry = ensure_inverse3(msg, &device_id);
let cmds = entry.commands.get_or_insert_with(Default::default);
cmds.probe_position = Some(ProbeCmd {});
}
Command::ProbeOrientation { device_id } => {
let wvg = ensure_versegrip(&mut msg.wireless_verse_grip, &device_id);
let wvg_cmds = wvg.commands.get_or_insert_with(Default::default);
wvg_cmds.probe_orientation = Some(ProbeCmd {});
}
Command::SetExtensionData { device_id, extension_data } => {
let wvg = ensure_versegrip(&mut msg.wireless_verse_grip, &device_id);
let wvg_cmds = wvg.commands.get_or_insert_with(Default::default);
wvg_cmds.set_extension_data = Some(SetExtensionDataCmd { extension_data });
}
Command::SetVgTransform { device_id, transform, execute } => {
let wvg = ensure_versegrip(&mut msg.wireless_verse_grip, &device_id);
let wvg_cmds = wvg.commands.get_or_insert_with(Default::default);
wvg_cmds.set_transform = Some(SetTransformCmd { transform, execute });
}
Command::ConfigureInverse3 { device_id, config } => {
let entry = ensure_inverse3(msg, &device_id);
entry.configure = Some(config);
}
Command::ConfigureVerseGrip { device_id, config } => {
let wvg = ensure_versegrip(&mut msg.wireless_verse_grip, &device_id);
wvg.configure = Some(VerseGripConfigure { ..config });
}
Command::ConfigureSession(config) => {
msg.session.configure = Some(config);
}
Command::ConfigureSdfOutput { device_id, state_output } => {
let entry = ensure_inverse3(msg, &device_id);
let cfg = entry.configure.get_or_insert_with(Default::default);
cfg.sdf = Some(SdfOutputConfigure { state_output });
}
Command::SdfSet { device_id, objects, from_space } => {
let entry = ensure_inverse3(msg, &device_id);
let cmds = entry.commands.get_or_insert_with(Default::default);
cmds.sdf = Some(SdfCommand {
mode: SdfCommandMode::Set,
from_space,
objects,
});
}
Command::SdfUpdate { device_id, objects } => {
let entry = ensure_inverse3(msg, &device_id);
let cmds = entry.commands.get_or_insert_with(Default::default);
cmds.sdf = Some(SdfCommand {
mode: SdfCommandMode::Update,
from_space: None,
objects,
});
}
Command::SdfRemove { device_id, ids } => {
let entry = ensure_inverse3(msg, &device_id);
let cmds = entry.commands.get_or_insert_with(Default::default);
cmds.sdf = Some(SdfCommand {
mode: SdfCommandMode::Remove,
from_space: None,
objects: ids
.into_iter()
.map(|id| SdfHfxObject {
id,
..Default::default()
})
.collect(),
});
}
Command::ForceRenderFullState => {
msg.session.force_render_full_state = Some(ForceRenderFullStateMsg {});
}
Command::PingService => {
}
}
Ok(())
}
pub(crate) fn clear_oneshot_fields(msg: &mut ServiceMsg) {
msg.session.configure = None;
msg.session.force_render_full_state = None;
for entry in &mut msg.inverse3 {
entry.configure = None;
}
for entry in &mut msg.verse_grip {
entry.configure = None;
}
for entry in &mut msg.wireless_verse_grip {
entry.configure = None;
}
}
pub fn apply_verse_grip_dedup(data: &mut ServiceData, mode: VerseGripDuplicateMode) {
match mode {
VerseGripDuplicateMode::PreferCustom => {
let custom_ids: Vec<String> = data.custom_verse_grip
.iter()
.map(|d| d.device_id.clone())
.collect();
data.wireless_verse_grip.retain(|d| !custom_ids.contains(&d.device_id));
}
VerseGripDuplicateMode::PreferWireless => {
let wireless_ids: Vec<String> = data.wireless_verse_grip
.iter()
.map(|d| d.device_id.clone())
.collect();
data.custom_verse_grip.retain(|d| !wireless_ids.contains(&d.device_id));
}
VerseGripDuplicateMode::KeepBoth => {}
}
}
#[derive(Copy, Clone, Debug, Default)]
struct StateLanePresence {
inverse3: bool,
verse_grip: bool,
wireless_verse_grip: bool,
custom_verse_grip: bool,
session_id: bool,
session: bool,
}
impl StateLanePresence {
fn from_value(v: &Value) -> Self {
let Some(obj) = v.as_object() else {
return Self::default();
};
Self {
inverse3: obj.contains_key("inverse3"),
verse_grip: obj.contains_key("verse_grip"),
wireless_verse_grip: obj.contains_key("wireless_verse_grip"),
custom_verse_grip: obj.contains_key("custom_verse_grip"),
session_id: obj.contains_key("session_id"),
session: obj.contains_key("session"),
}
}
}
#[derive(Copy, Clone, Debug, Default)]
struct StateRetainPolicy {
inverse3: bool,
verse_grip: bool,
wireless_verse_grip: bool,
custom_verse_grip: bool,
}
#[derive(Copy, Clone, Debug, Default)]
struct SessionUpdatePolicy {
session_id: bool,
session: bool,
}
#[derive(Clone, Debug)]
struct ParsedStateUpdate {
state: ServiceState,
retain: StateRetainPolicy,
session_policy: SessionUpdatePolicy,
}
fn parse_state_array_best_effort<T: DeserializeOwned>(
root: &Value,
key: &str,
raw_payload: &str
) -> (Vec<T>, bool, bool) {
let Some(raw_field) = root.get(key) else {
return (Vec::new(), false, false);
};
let Some(items) = raw_field.as_array() else {
if let Err(e) = serde_json::from_value::<Vec<Value>>(raw_field.clone()) {
log_parse_error(
&format!("State payload field '{}' is not an array", key),
&e,
raw_payload
);
}
return (Vec::new(), true, true);
};
let mut parsed: Vec<T> = Vec::new();
let mut had_errors = false;
for (idx, item) in items.iter().enumerate() {
match serde_json::from_value::<T>(item.clone()) {
Ok(msg) => parsed.push(msg),
Err(e) => {
had_errors = true;
log_parse_error(
&format!("Failed to parse {}[{}] entry", key, idx),
&e,
&item.to_string()
);
}
}
}
(parsed, true, had_errors)
}
fn parse_session_id_best_effort(root: &Value, raw_payload: &str) -> (u64, bool, bool) {
let Some(raw_field) = root.get("session_id") else {
return (0, false, false);
};
match serde_json::from_value::<u64>(raw_field.clone()) {
Ok(id) => (id, true, false),
Err(e) => {
log_parse_error("Failed to parse session_id field", &e, raw_payload);
(0, true, true)
}
}
}
fn parse_session_best_effort(root: &Value, raw_payload: &str) -> (Option<SessionInfo>, bool, bool) {
let Some(raw_field) = root.get("session") else {
return (None, false, false);
};
match serde_json::from_value::<Option<SessionInfo>>(raw_field.clone()) {
Ok(session) => (session, true, false),
Err(e) => {
log_parse_error("Failed to parse session field", &e, raw_payload);
(None, true, true)
}
}
}
fn parse_state_update_best_effort(v: &Value, raw_payload: &str) -> Option<ParsedStateUpdate> {
if !v.is_object() {
return None;
}
let (inverse3, inverse3_present, inverse3_had_errors) =
parse_state_array_best_effort::<Inverse3Message>(v, "inverse3", raw_payload);
let (verse_grip, verse_grip_present, verse_grip_had_errors) =
parse_state_array_best_effort::<VerseGripMessage>(v, "verse_grip", raw_payload);
let (wireless_verse_grip, wireless_present, wireless_had_errors) =
parse_state_array_best_effort::<WirelessVerseGripMessage>(
v,
"wireless_verse_grip",
raw_payload
);
let (custom_verse_grip, custom_present, custom_had_errors) =
parse_state_array_best_effort::<CustomVerseGripMessage>(
v,
"custom_verse_grip",
raw_payload
);
let (session_id, session_id_present, session_id_had_errors) = parse_session_id_best_effort(
v,
raw_payload
);
let (session, session_present, session_had_errors) = parse_session_best_effort(v, raw_payload);
let has_actionable_fields =
inverse3_present ||
verse_grip_present ||
wireless_present ||
custom_present ||
session_id_present ||
session_present;
if !has_actionable_fields {
return None;
}
Some(ParsedStateUpdate {
state: ServiceState {
inverse3,
verse_grip,
wireless_verse_grip,
custom_verse_grip,
session_id,
session,
},
retain: StateRetainPolicy {
inverse3: inverse3_present && !inverse3_had_errors,
verse_grip: verse_grip_present && !verse_grip_had_errors,
wireless_verse_grip: wireless_present && !wireless_had_errors,
custom_verse_grip: custom_present && !custom_had_errors,
},
session_policy: SessionUpdatePolicy {
session_id: session_id_present && !session_id_had_errors,
session: session_present && !session_had_errors,
},
})
}
pub async fn update_state_on_message(
state: Arc<Mutex<TimestampedServiceData>>,
message: String,
first: &mut bool,
verse_grip_mode: VerseGripDuplicateMode
) {
let v: Value = match (
{
#[cfg(feature = "tracy")]
let _parse_span = tracy_client::span!("state::update_state_on_message_parse_json");
serde_json::from_str(&message)
}
) {
Ok(val) => val,
Err(e) => {
log_parse_error("Invalid JSON from WS", &e, &message);
return;
}
};
fn is_full_payload(v: &Value) -> bool {
for key in &["inverse3", "verse_grip", "wireless_verse_grip", "custom_verse_grip"] {
if let Some(arr) = v.get(key).and_then(Value::as_array) {
if arr.iter().any(|item| item.get("config").is_some()) {
return true;
}
}
}
false
}
if is_full_payload(&v) {
let state_fallback_candidate = v.clone();
match (
{
#[cfg(feature = "tracy")]
let _full_parse_span = tracy_client::span!(
"state::update_state_on_message_full_parse"
);
serde_json::from_value::<ServiceData>(v)
}
) {
Ok(sd) => {
let mut lock = state.lock().await;
let _ = update_service_struct(&mut lock.data, &sd, verse_grip_mode);
lock.timestamp = std::time::Instant::now();
if *first {
trace!("Initialized from full payload");
} else {
trace!("Merged full payload");
}
}
Err(e) => {
if *first {
log_parse_error("Failed to parse first-message full payload", &e, &message);
} else {
log_parse_error("Failed to parse full payload", &e, &message);
}
let fallback_presence = StateLanePresence::from_value(&state_fallback_candidate);
match (
{
#[cfg(feature = "tracy")]
let _fallback_state_only_parse_span = tracy_client::span!(
"state::update_state_on_message_fallback_state_only_parse"
);
serde_json::from_value::<ServiceState>(state_fallback_candidate.clone())
}
) {
Ok(ss) => {
update_state_from_service_state(
state.clone(),
ss,
verse_grip_mode,
StateRetainPolicy {
inverse3: fallback_presence.inverse3,
verse_grip: fallback_presence.verse_grip,
wireless_verse_grip: fallback_presence.wireless_verse_grip,
custom_verse_grip: fallback_presence.custom_verse_grip,
},
SessionUpdatePolicy {
session_id: fallback_presence.session_id,
session: fallback_presence.session,
}
).await;
trace!("Applied fallback state-only update from malformed full payload");
}
Err(fallback_err) => {
log_parse_error(
"Failed fallback parse to state-only payload",
&fallback_err,
&message
);
let parsed = {
#[cfg(feature = "tracy")]
let _best_effort_parse_span = tracy_client::span!(
"state::update_state_on_message_best_effort_parse"
);
parse_state_update_best_effort(&state_fallback_candidate, &message)
};
if let Some(parsed) = parsed {
update_state_from_service_state(
state.clone(),
parsed.state,
verse_grip_mode,
parsed.retain,
parsed.session_policy
).await;
trace!("Applied best-effort fallback state update");
} else {
trace!("No actionable data found in best-effort fallback parse");
}
}
}
}
}
} else {
let presence = StateLanePresence::from_value(&v);
match (
{
#[cfg(feature = "tracy")]
let _state_only_parse_span = tracy_client::span!(
"state::update_state_on_message_state_only_parse"
);
serde_json::from_value::<ServiceState>(v.clone())
}
) {
Ok(ss) => {
update_state_from_service_state(
state.clone(),
ss,
verse_grip_mode,
StateRetainPolicy {
inverse3: presence.inverse3,
verse_grip: presence.verse_grip,
wireless_verse_grip: presence.wireless_verse_grip,
custom_verse_grip: presence.custom_verse_grip,
},
SessionUpdatePolicy {
session_id: presence.session_id,
session: presence.session,
}
).await;
trace!("Applied state-only update");
}
Err(e) => {
log_parse_error("Failed to parse state-only payload", &e, &message);
let parsed = {
#[cfg(feature = "tracy")]
let _state_only_best_effort_parse_span = tracy_client::span!(
"state::update_state_on_message_state_only_best_effort_parse"
);
parse_state_update_best_effort(&v, &message)
};
if let Some(parsed) = parsed {
update_state_from_service_state(
state.clone(),
parsed.state,
verse_grip_mode,
parsed.retain,
parsed.session_policy
).await;
trace!("Applied best-effort state-only update");
} else {
trace!("No actionable data found in best-effort state parse");
}
}
}
}
if *first {
*first = false;
}
}
async fn update_state_from_service_state(
state: Arc<Mutex<TimestampedServiceData>>,
state_only: ServiceState,
verse_grip_mode: VerseGripDuplicateMode,
retain_policy: StateRetainPolicy,
session_policy: SessionUpdatePolicy
) {
trace!("Updating device state with partial data");
let mut state_lock = state.lock().await;
#[cfg(feature = "tracy")]
let _span = tracy_client::span!("state::update_state_from_service_state");
state_lock.timestamp = std::time::Instant::now();
{
#[cfg(feature = "tracy")]
let _inverse3_merge_span = tracy_client::span!(
"state::update_state_from_service_state_inverse3_merge"
);
for msg in &state_only.inverse3 {
match state_lock.data.inverse3.iter_mut().find(|d| d.device_id == msg.device_id) {
Some(dev) => {
dev.state = msg.state.clone();
dev.status = msg.status.clone();
}
None => {
state_lock.data.inverse3.push(Inverse3Device {
device_id: msg.device_id.clone(),
config: None,
state: msg.state.clone(),
status: msg.status.clone(),
});
}
}
}
}
{
#[cfg(feature = "tracy")]
let _verse_grip_merge_span = tracy_client::span!(
"state::update_state_from_service_state_verse_grip_merge"
);
for msg in &state_only.verse_grip {
match state_lock.data.verse_grip.iter_mut().find(|d| d.device_id == msg.device_id) {
Some(dev) => {
dev.state = msg.state.clone();
dev.status = msg.status.clone();
}
None => {
state_lock.data.verse_grip.push(VerseGripDevice {
device_id: msg.device_id.clone(),
config: None,
state: msg.state.clone(),
status: msg.status.clone(),
});
}
}
}
}
{
#[cfg(feature = "tracy")]
let _wireless_verse_grip_merge_span = tracy_client::span!(
"state::update_state_from_service_state_wireless_verse_grip_merge"
);
for msg in &state_only.wireless_verse_grip {
match
state_lock.data.wireless_verse_grip
.iter_mut()
.find(|d| d.device_id == msg.device_id)
{
Some(dev) => {
dev.state = msg.state.clone();
dev.status = msg.status.clone();
}
None => {
state_lock.data.wireless_verse_grip.push(WirelessVerseGripDevice {
device_id: msg.device_id.clone(),
config: None,
state: msg.state.clone(),
status: msg.status.clone(),
});
}
}
}
}
{
#[cfg(feature = "tracy")]
let _custom_verse_grip_merge_span = tracy_client::span!(
"state::update_state_from_service_state_custom_verse_grip_merge"
);
for msg in &state_only.custom_verse_grip {
match
state_lock.data.custom_verse_grip.iter_mut().find(|d| d.device_id == msg.device_id)
{
Some(dev) => {
dev.state = msg.state.clone();
dev.status = msg.status.clone();
}
None => {
state_lock.data.custom_verse_grip.push(CustomVerseGripDevice {
device_id: msg.device_id.clone(),
config: None,
state: msg.state.clone(),
status: msg.status.clone(),
});
}
}
}
}
{
#[cfg(feature = "tracy")]
let _retain_span = tracy_client::span!("state::update_state_from_service_state_retain");
if retain_policy.inverse3 {
state_lock.data.inverse3.retain(|d|
state_only.inverse3.iter().any(|msg| msg.device_id == d.device_id)
);
}
if retain_policy.verse_grip {
state_lock.data.verse_grip.retain(|d|
state_only.verse_grip.iter().any(|msg| msg.device_id == d.device_id)
);
}
if retain_policy.wireless_verse_grip {
state_lock.data.wireless_verse_grip.retain(|d| {
state_only.wireless_verse_grip.iter().any(|msg| msg.device_id == d.device_id)
});
}
if retain_policy.custom_verse_grip {
state_lock.data.custom_verse_grip.retain(|d| {
state_only.custom_verse_grip.iter().any(|msg| msg.device_id == d.device_id)
});
}
}
{
#[cfg(feature = "tracy")]
let _dedup_span = tracy_client::span!("state::update_state_from_service_state_dedup");
apply_verse_grip_dedup(&mut state_lock.data, verse_grip_mode);
}
if session_policy.session_id {
state_lock.data.session_id = state_only.session_id;
}
if session_policy.session {
state_lock.data.session = state_only.session;
}
}
pub fn update_service_struct(
service_data: &mut ServiceData,
input_data: &ServiceData,
verse_grip_mode: VerseGripDuplicateMode
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
#[cfg(feature = "tracy")]
let _span = tracy_client::span!("state::update_service_struct");
{
#[cfg(feature = "tracy")]
let _inverse3_merge_span = tracy_client::span!(
"state::update_service_struct_inverse3_merge"
);
for device in &input_data.inverse3 {
match service_data.inverse3.iter_mut().find(|d| d.device_id == device.device_id) {
Some(existing_device) => {
*existing_device = device.clone();
}
None => service_data.inverse3.push(device.clone()),
}
}
}
{
#[cfg(feature = "tracy")]
let _verse_grip_merge_span = tracy_client::span!(
"state::update_service_struct_verse_grip_merge"
);
for device in &input_data.verse_grip {
match service_data.verse_grip.iter_mut().find(|d| d.device_id == device.device_id) {
Some(existing_device) => {
*existing_device = device.clone();
}
None => service_data.verse_grip.push(device.clone()),
}
}
}
{
#[cfg(feature = "tracy")]
let _wireless_verse_grip_merge_span = tracy_client::span!(
"state::update_service_struct_wireless_verse_grip_merge"
);
for device in &input_data.wireless_verse_grip {
match
service_data.wireless_verse_grip
.iter_mut()
.find(|d| d.device_id == device.device_id)
{
Some(existing_device) => {
*existing_device = device.clone();
}
None => service_data.wireless_verse_grip.push(device.clone()),
}
}
}
{
#[cfg(feature = "tracy")]
let _custom_verse_grip_merge_span = tracy_client::span!(
"state::update_service_struct_custom_verse_grip_merge"
);
for device in &input_data.custom_verse_grip {
match
service_data.custom_verse_grip.iter_mut().find(|d| d.device_id == device.device_id)
{
Some(existing_device) => {
*existing_device = device.clone();
}
None => service_data.custom_verse_grip.push(device.clone()),
}
}
}
{
#[cfg(feature = "tracy")]
let _retain_span = tracy_client::span!("state::update_service_struct_retain");
service_data.inverse3.retain(|d|
input_data.inverse3.iter().any(|nd| nd.device_id == d.device_id)
);
service_data.verse_grip.retain(|d|
input_data.verse_grip.iter().any(|nd| nd.device_id == d.device_id)
);
service_data.wireless_verse_grip.retain(|d| {
input_data.wireless_verse_grip.iter().any(|nd| nd.device_id == d.device_id)
});
service_data.custom_verse_grip.retain(|d| {
input_data.custom_verse_grip.iter().any(|nd| nd.device_id == d.device_id)
});
}
{
#[cfg(feature = "tracy")]
let _dedup_span = tracy_client::span!("state::update_service_struct_dedup");
apply_verse_grip_dedup(service_data, verse_grip_mode);
}
if input_data.session_id != 0 {
service_data.session_id = input_data.session_id;
}
if input_data.session.is_some() {
service_data.session = input_data.session.clone();
}
Ok(())
}