diff --git a/crates/muninn-daemon/src/main.rs b/crates/muninn-daemon/src/main.rs index 8661da3..ed95cd9 100644 --- a/crates/muninn-daemon/src/main.rs +++ b/crates/muninn-daemon/src/main.rs @@ -17,17 +17,18 @@ use cultnet_rs::{ CultNetRudpSocketTransportConnection, CultNetRudpSocketTransportOptions, CultNetTransportFrame, CultNetWireContract, decode_cultnet_message_from_slice, encode_cultnet_message_to_vec, }; -#[cfg(feature = "psmoveapi-tracker")] -use odin_core::MuninnMoveTrackerHealthRecord; use odin_core::{ EVE_PROVIDER_ADVERTISEMENT_SCHEMA, EveProviderAdvertisementRecord, EveSurfaceStateRecord, - IdunnDaemonHealthRecord, MUNINN_MOVE_HUE_PROGRAM_SCHEMA, MUNINN_OBS_STREAM_CATALOG_SCHEMA, + IdunnDaemonHealthRecord, + MUNINN_MOVE_HUE_PROGRAM_SCHEMA, + MUNINN_OBS_STREAM_CATALOG_SCHEMA, MuninnCaptureStreamCommandRecord, MuninnCaptureStreamRecord, MuninnCommandBoundaryCompatRecord, MuninnHidControllerStateRecord, MuninnMediaReceiverFeedbackRecord, MuninnMoveControllerStateRecord, MuninnMoveHueProgramRecord, MuninnMoveIdentityRecord, - MuninnMoveLightCommandRecord, MuninnMoveMarkerCandidateRecord, MuninnObsStreamCatalogRecord, - MuninnQuestAccessRecord, MuninnTelemetrySurfaceRecord, MuninnTransportProfileCompatRecord, - OdinDocuments, OdinEndpointQuery, discover_provider_endpoints, + MuninnMoveLightCommandRecord, + MuninnMoveMarkerCandidateRecord, MuninnObsStreamCatalogRecord, MuninnQuestAccessRecord, + MuninnTelemetrySurfaceRecord, MuninnTransportProfileCompatRecord, OdinDocuments, + OdinEndpointQuery, discover_provider_endpoints, }; use serde::{Deserialize, Serialize}; use serde_json::json; @@ -309,10 +310,7 @@ struct ActiveMoveMarkerCameraSource { frame_source: MoveMarkerFrameSource, sequence: u64, #[cfg(feature = "psmoveapi-tracker")] - psmoveapi_observations: - Option>>, - #[cfg(feature = "psmoveapi-tracker")] - psmoveapi_health: Option>, + psmoveapi_observations: Option>>, } #[derive(Clone, Debug, PartialEq, Eq)] @@ -458,7 +456,6 @@ fn serve(options: Options) -> Result<()> { &latest_move_controller_states, move_evidence_stream.as_mut(), )?; - publish_move_tracker_health(&mut node, &mut active_move_marker_cameras)?; publish_quest_access_if_requested(&mut node, &options)?; let state = if active_stream_ids.is_empty() { "idle" @@ -535,9 +532,9 @@ fn load_or_initialize_move_hue_program( ) -> Result>> { let key = move_hue_program_key(&options.host_id); let mut node = open_node(options, "muninn-move-hue-program-bootstrap")?; - let program = node - .get::(&key)? - .unwrap_or_else(|| bootstrap_move_hue_program(options)); + let program = node.get::(&key)?.unwrap_or_else(|| { + bootstrap_move_hue_program(options) + }); validate_move_hue_program(&program)?; node.put(&key, &program)?; Ok(Arc::new(Mutex::new(program))) @@ -568,10 +565,7 @@ fn validate_move_hue_program(program: &MuninnMoveHueProgramRecord) -> Result<()> program.order_mode.as_str(), "descending" | "ascending" | "bounce" | "rotating-lead" | "golden-permutation" ) { - return Err(anyhow!( - "unsupported Move hue order mode {}", - program.order_mode - )); + return Err(anyhow!("unsupported Move hue order mode {}", program.order_mode)); } Ok(()) } @@ -595,34 +589,32 @@ fn start_move_hue_program_sync_worker( .as_ref() .cloned() .unwrap_or_else(|| options.store_path.clone()); - thread::spawn(move || { - loop { - let key = move_hue_program_key(&options.host_id); - let main_program = open_node(&options, "muninn-move-hue-program-sync") - .ok() - .and_then(|node| node.get::(&key).ok().flatten()); - let activation_program = open_node( - &activation_options, - "muninn-move-hue-program-activation-sync", - ) + thread::spawn(move || loop { + let key = move_hue_program_key(&options.host_id); + let main_program = open_node(&options, "muninn-move-hue-program-sync") .ok() .and_then(|node| node.get::(&key).ok().flatten()); - let program = [main_program, activation_program] - .into_iter() - .flatten() - .filter(|program| validate_move_hue_program(program).is_ok()) - .max_by_key(|program| unix_timestamp_sort_key(&program.updated_at)); - if let Some(program) = program - && let Ok(mut current) = runtime_program.lock() - && *current != program - { - *current = program.clone(); - if let Ok(mut node) = open_node(&options, "muninn-move-hue-program-projection") { - let _ = node.put(&key, &program); - } + let activation_program = open_node( + &activation_options, + "muninn-move-hue-program-activation-sync", + ) + .ok() + .and_then(|node| node.get::(&key).ok().flatten()); + let program = [main_program, activation_program] + .into_iter() + .flatten() + .filter(|program| validate_move_hue_program(program).is_ok()) + .max_by_key(|program| unix_timestamp_sort_key(&program.updated_at)); + if let Some(program) = program + && let Ok(mut current) = runtime_program.lock() + && *current != program + { + *current = program.clone(); + if let Ok(mut node) = open_node(&options, "muninn-move-hue-program-projection") { + let _ = node.put(&key, &program); } - thread::sleep(Duration::from_millis(250)); } + thread::sleep(Duration::from_millis(250)); }); } @@ -650,8 +642,8 @@ fn run_provider_command_ingress( options: Options, runtime_program: Arc>, ) -> Result<()> { - let mut transport = - CultNetRudpSocketTransportConnection::new(CultNetRudpSocketTransportOptions { + let mut transport = CultNetRudpSocketTransportConnection::new( + CultNetRudpSocketTransportOptions { runtime_id: format!("muninn-{}-command-ingress", options.host_id), socket, mode: cultnet_rs::CultNetRudpSocketMode::Server, @@ -664,7 +656,8 @@ fn run_provider_command_ingress( max_fragment_bytes: Some(1200), max_pending_reliable_packets: Some(64), media_reliable_expire_after_ms: None, - })?; + }, + )?; loop { if let Some(frame) = transport.receive_once()? && frame.channel_id == "schema" @@ -704,10 +697,7 @@ fn apply_provider_command_document( .and_then(serde_json::Value::as_str) .unwrap_or_default(); if provider_id != muninn_provider_id(options) { - return Err(anyhow!( - "Move hue command targets provider {provider_id}, not {}", - muninn_provider_id(options) - )); + return Err(anyhow!("Move hue command targets provider {provider_id}, not {}", muninn_provider_id(options))); } let mut program = runtime_program .lock() @@ -1488,10 +1478,9 @@ fn merge_move_state_sources( continue; } } - if !sources - .iter() - .any(|discovered| discovered == source || discovered.move_id == source.move_id) - { + if !sources.iter().any(|discovered| { + discovered == source || discovered.move_id == source.move_id + }) { sources.push(source.clone()); } } @@ -2986,11 +2975,7 @@ fn publish_move_hue_eve_surface( .unwrap_or_else(|| bootstrap_move_hue_program(options)); let provider_id = muninn_provider_id(options); let surface_key = move_hue_surface_key(&options.host_id); - let action = |id: &str, - label: &str, - mode: Option<&str>, - order_mode: Option<&str>, - cycle_ms: Option| { + let action = |id: &str, label: &str, mode: Option<&str>, order_mode: Option<&str>, cycle_ms: Option| { json!({ "id": format!("{provider_id}.move-hue.{id}"), "kind": "card", @@ -4633,12 +4618,10 @@ fn active_move_marker_camera_sources( .collect::>(); move_roster.sort(); move_roster.dedup(); - #[cfg(feature = "psmoveapi-tracker")] let now_ns = SystemTime::now() .duration_since(UNIX_EPOCH) .unwrap_or_default() .as_nanos() as i128; - #[cfg(feature = "psmoveapi-tracker")] let program = move_hue_program .lock() .map(|program| program.clone()) @@ -4653,9 +4636,7 @@ fn active_move_marker_camera_sources( updated_at: "unix-0".to_string(), order_mode: "descending".to_string(), }); - #[cfg(feature = "psmoveapi-tracker")] let program_timestamp_ns = move_hue_program_timestamp_ns(&program, now_ns); - #[cfg(feature = "psmoveapi-tracker")] let move_colors = move_roster .iter() .filter_map(|identity| { @@ -4679,23 +4660,18 @@ fn active_move_marker_camera_sources( options.host_id, source.camera_id ); #[cfg(feature = "psmoveapi-tracker")] - let (psmoveapi_observations, psmoveapi_health) = if options.move_psmoveapi_tracker { - video_device_index(&source.device_path) - .and_then(|camera_index| { - Some(start_psmoveapi_tracker_worker( - options.host_id.clone(), - source.camera_id.clone(), - camera_index, - options.move_tracker_exposure_milli as f32 / 1000.0, - move_colors.clone(), - Arc::clone(&move_hue_program), - )) - }) - .map_or((None, None), |(observations, health)| { - (Some(observations), Some(health)) - }) + let psmoveapi_observations = if options.move_psmoveapi_tracker { + video_device_index(&source.device_path).and_then(|camera_index| { + Some(start_psmoveapi_tracker_worker( + source.camera_id.clone(), + camera_index, + options.move_tracker_exposure_milli as f32 / 1000.0, + move_colors.clone(), + Arc::clone(&move_hue_program), + )) + }) } else { - (None, None) + None }; ActiveMoveMarkerCameraSource { source: source.clone(), @@ -4721,8 +4697,6 @@ fn active_move_marker_camera_sources( sequence: 0, #[cfg(feature = "psmoveapi-tracker")] psmoveapi_observations, - #[cfg(feature = "psmoveapi-tracker")] - psmoveapi_health, } }) .collect() @@ -4799,58 +4773,32 @@ static PSMOVE_API_TRACKER_AUTHORITY: Mutex<()> = Mutex::new(()); #[cfg(feature = "psmoveapi-tracker")] fn start_psmoveapi_tracker_worker( - host_id: String, camera_id: String, camera_index: i32, exposure: f32, colors: Vec<(String, [u8; 3])>, move_hue_program: Arc>, -) -> ( - mpsc::Receiver>, - mpsc::Receiver, -) { +) -> mpsc::Receiver> { let (sender, receiver) = mpsc::sync_channel(1); - let (health_sender, health_receiver) = mpsc::sync_channel(1); thread::spawn(move || { let mut tracker = match PSMOVE_API_TRACKER_AUTHORITY .lock() .map_err(|_| "PSMoveAPI tracker authority lock was poisoned".to_string()) .and_then(|_authority| { - muninn_psmoveapi_tracker::PsmoveApiTracker::open(camera_index, exposure, &colors) - .map_err(|error| error.to_string()) + muninn_psmoveapi_tracker::PsmoveApiTracker::open( + camera_index, + exposure, + &colors, + ) + .map_err(|error| error.to_string()) }) { Ok(tracker) => tracker, Err(error) => { - let _ = health_sender.try_send(MuninnMoveTrackerHealthRecord { - health_id: format!("muninn:{host_id}:{camera_id}:move-tracker-health"), - host_id: host_id.clone(), - camera_id: camera_id.clone(), - camera_index, - state: "failed".to_string(), - camera_name: String::new(), - camera_api: String::new(), - width: 0, - height: 0, - exposure, - calibrated_controller_count: 0, - update_count: 0, - observation_count: 0, - latest_observation_count: 0, - last_observation_at: String::new(), - detail: error.clone(), - updated_at: timestamp().unwrap_or_else(|_| "unix-0".to_string()), - }); - eprintln!( - "Muninn PSMoveAPI tracker failed camera={camera_id} index={camera_index}: {error}" - ); + eprintln!("Muninn PSMoveAPI tracker failed camera={camera_id} index={camera_index}: {error}"); return; } }; - let camera_info = tracker.camera_info().clone(); let mut last_calibration_at = Instant::now(); - let mut update_count = 0u64; - let mut observation_count = 0u64; - let mut last_observation_at = String::new(); loop { let Ok(_authority) = PSMOVE_API_TRACKER_AUTHORITY.lock() else { eprintln!("Muninn PSMoveAPI tracker authority lock poisoned camera={camera_id}"); @@ -4860,10 +4808,7 @@ fn start_psmoveapi_tracker_worker( tracker.observe_connected(); last_calibration_at = Instant::now(); } - let mut roster = colors - .iter() - .map(|(identity, _)| identity.clone()) - .collect::>(); + let mut roster = colors.iter().map(|(identity, _)| identity.clone()).collect::>(); roster.sort(); let now_ns = SystemTime::now() .duration_since(UNIX_EPOCH) @@ -4886,78 +4831,17 @@ fn start_psmoveapi_tracker_worker( } } let observations = tracker.update(); - update_count = update_count.saturating_add(1); - observation_count = observation_count.saturating_add(observations.len() as u64); - if !observations.is_empty() { - last_observation_at = timestamp().unwrap_or_else(|_| "unix-0".to_string()); - } - let health = MuninnMoveTrackerHealthRecord { - health_id: format!("muninn:{host_id}:{camera_id}:move-tracker-health"), - host_id: host_id.clone(), - camera_id: camera_id.clone(), - camera_index, - state: "running".to_string(), - camera_name: camera_info.name.clone(), - camera_api: camera_info.api.clone(), - width: camera_info.width, - height: camera_info.height, - exposure: camera_info.exposure, - calibrated_controller_count: tracker.tracked_controller_count() as u32, - update_count, - observation_count, - latest_observation_count: observations.len() as u32, - last_observation_at: last_observation_at.clone(), - detail: String::new(), - updated_at: timestamp().unwrap_or_else(|_| "unix-0".to_string()), - }; - match health_sender.try_send(health) { - Ok(()) | Err(mpsc::TrySendError::Full(_)) => {} - Err(mpsc::TrySendError::Disconnected(_)) => return, - } match sender.try_send(observations) { Ok(()) | Err(mpsc::TrySendError::Full(_)) => {} Err(mpsc::TrySendError::Disconnected(_)) => return, } } }); - (receiver, health_receiver) + receiver } -#[cfg(feature = "psmoveapi-tracker")] -fn publish_move_tracker_health( - node: &mut cultmesh_rs::CultMeshNode, - active: &mut [ActiveMoveMarkerCameraSource], -) -> Result<()> { - for camera in active { - let Some(receiver) = camera.psmoveapi_health.as_ref() else { - continue; - }; - let mut latest = None; - while let Ok(record) = receiver.try_recv() { - latest = Some(record); - } - if let Some(record) = latest { - node.put(&record.health_id, &record)?; - } - } - Ok(()) -} - -#[cfg(not(feature = "psmoveapi-tracker"))] -fn publish_move_tracker_health( - _node: &mut cultmesh_rs::CultMeshNode, - _active: &mut [ActiveMoveMarkerCameraSource], -) -> Result<()> { - Ok(()) -} - -#[cfg(feature = "psmoveapi-tracker")] fn video_device_index(path: &Path) -> Option { - path.file_name()? - .to_str()? - .strip_prefix("video")? - .parse() - .ok() + path.file_name()?.to_str()?.strip_prefix("video")?.parse().ok() } fn stable_marker_camera_source_hash(source: &MoveMarkerFrameSource) -> u64 { @@ -6499,12 +6383,11 @@ fn scheduled_golden_move_color_with_order( let completed_cycles = completed_steps.div_euclid(roster_len); let completed_in_cycle = completed_steps.rem_euclid(roster_len); let update_order = move_hue_update_order(roster.len(), completed_cycles, order_mode); - let order_position = update_order - .iter() - .position(|index| *index == identity_index as usize)? as i128; + let order_position = update_order.iter().position(|index| *index == identity_index as usize)? as i128; let advanced_in_cycle = i128::from(order_position < completed_in_cycle); let sequence_index = identity_index + completed_cycles + advanced_in_cycle; - let hue = ((sequence_index as f64 * GOLDEN_RATIO_CONJUGATE).fract() * 360.0).rem_euclid(360.0); + let hue = ((sequence_index as f64 * GOLDEN_RATIO_CONJUGATE).fract() * 360.0) + .rem_euclid(360.0); Some(( hsv_to_rgb(hue, 1.0, 1.0), sequence_index, @@ -9871,7 +9754,8 @@ impl Options { options.move_hue_mode = Some(take_value(&mut args, "--move-hue-mode")?) } "--move-hue-order" => { - options.move_hue_order_mode = Some(take_value(&mut args, "--move-hue-order")?) + options.move_hue_order_mode = + Some(take_value(&mut args, "--move-hue-order")?) } "--command" => options.command_id = Some(take_value(&mut args, "--command")?), "--move-host" => { @@ -9953,9 +9837,7 @@ impl Options { )); } if options.move_tracker_exposure_milli > 1000 { - return Err(anyhow!( - "--move-tracker-exposure-milli must be within 0..=1000" - )); + return Err(anyhow!("--move-tracker-exposure-milli must be within 0..=1000")); } if options.move_psmoveapi_tracker && !cfg!(feature = "psmoveapi-tracker") { return Err(anyhow!( @@ -11650,8 +11532,8 @@ mod tests { client_socket .set_read_timeout(Some(Duration::from_millis(5))) .unwrap(); - let mut client = - CultNetRudpSocketTransportConnection::new(CultNetRudpSocketTransportOptions { + let mut client = CultNetRudpSocketTransportConnection::new( + CultNetRudpSocketTransportOptions { runtime_id: "muninn-provider-command-test-client".to_string(), socket: client_socket, mode: cultnet_rs::CultNetRudpSocketMode::Client, @@ -11664,8 +11546,9 @@ mod tests { max_fragment_bytes: Some(1200), max_pending_reliable_packets: Some(64), media_reliable_expire_after_ms: None, - }) - .unwrap(); + }, + ) + .unwrap(); client.connect(Vec::new()).unwrap(); let deadline = Instant::now() + Duration::from_secs(2); while !client.connected() && Instant::now() < deadline { @@ -11750,15 +11633,8 @@ mod tests { "move-000704a800d0", ]; let colors = roster.map(default_move_color_for_identity); - assert_eq!( - colors.into_iter().collect::>().len(), - roster.len() - ); - assert!( - colors - .into_iter() - .all(|color| color.0.max(color.1).max(color.2) == 255) - ); + assert_eq!(colors.into_iter().collect::>().len(), roster.len()); + assert!(colors.into_iter().all(|color| color.0.max(color.1).max(color.2) == 255)); } #[test] @@ -11774,11 +11650,7 @@ mod tests { .map(|identity| golden_move_color_for_roster(identity, &roster)) .collect::>(); assert_eq!(colors.iter().copied().collect::>().len(), 4); - assert!( - colors - .iter() - .all(|color| color.0.max(color.1).max(color.2) == 255) - ); + assert!(colors.iter().all(|color| color.0.max(color.1).max(color.2) == 255)); assert!(colors.iter().any(|color| color.0 == 255 && color.1 < 100)); assert!(colors.iter().any(|color| color.1 == 255 && color.0 < 100)); assert!(colors.iter().any(|color| color.2 == 255 && color.0 < 100)); @@ -11786,9 +11658,7 @@ mod tests { #[test] fn scheduled_golden_colors_shift_window_in_descending_move_order() { - let roster = (0..4) - .map(|index| format!("move-{index}")) - .collect::>(); + let roster = (0..4).map(|index| format!("move-{index}")).collect::>(); let state = |millis: i128| { roster .iter() @@ -11806,30 +11676,12 @@ mod tests { .collect::>() }; - assert_eq!( - state(0), - vec![(0, false), (1, false), (2, false), (3, true)] - ); - assert_eq!( - state(249), - vec![(0, false), (1, false), (2, false), (3, true)] - ); - assert_eq!( - state(250), - vec![(0, false), (1, false), (2, true), (4, false)] - ); - assert_eq!( - state(500), - vec![(0, false), (1, true), (3, false), (4, false)] - ); - assert_eq!( - state(750), - vec![(0, true), (2, false), (3, false), (4, false)] - ); - assert_eq!( - state(1_000), - vec![(1, false), (2, false), (3, false), (4, true)] - ); + assert_eq!(state(0), vec![(0, false), (1, false), (2, false), (3, true)]); + assert_eq!(state(249), vec![(0, false), (1, false), (2, false), (3, true)]); + assert_eq!(state(250), vec![(0, false), (1, false), (2, true), (4, false)]); + assert_eq!(state(500), vec![(0, false), (1, true), (3, false), (4, false)]); + assert_eq!(state(750), vec![(0, true), (2, false), (3, false), (4, false)]); + assert_eq!(state(1_000), vec![(1, false), (2, false), (3, false), (4, true)]); } #[test] @@ -11838,14 +11690,8 @@ mod tests { assert_eq!(move_hue_update_order(4, 0, "ascending"), vec![0, 1, 2, 3]); assert_eq!(move_hue_update_order(4, 0, "bounce"), vec![3, 2, 1, 0]); assert_eq!(move_hue_update_order(4, 1, "bounce"), vec![0, 1, 2, 3]); - assert_eq!( - move_hue_update_order(4, 0, "rotating-lead"), - vec![3, 2, 1, 0] - ); - assert_eq!( - move_hue_update_order(4, 1, "rotating-lead"), - vec![2, 1, 0, 3] - ); + assert_eq!(move_hue_update_order(4, 0, "rotating-lead"), vec![3, 2, 1, 0]); + assert_eq!(move_hue_update_order(4, 1, "rotating-lead"), vec![2, 1, 0, 3]); let first = move_hue_update_order(4, 23, "golden-permutation"); assert_eq!(first, move_hue_update_order(4, 23, "golden-permutation")); assert_eq!(first.iter().copied().collect::>().len(), 4); @@ -12760,12 +12606,10 @@ Device 00:07:04:A8:00:D0 (public) .value .get("routes") .and_then(|value| value.as_array()) - .and_then(|routes| { - routes.iter().find(|route| { - route.get("role").and_then(|value| value.as_str()) - == Some("muninn provider command ingress") - }) - }) + .and_then(|routes| routes.iter().find(|route| { + route.get("role").and_then(|value| value.as_str()) + == Some("muninn provider command ingress") + })) .expect("provider-owned command route"); assert_eq!( command_route.get("uri").and_then(|value| value.as_str()), @@ -13258,10 +13102,7 @@ Device 00:07:04:A8:00:D0 (public) hidraw_path: "/dev/input/js2".to_string(), }]; - assert_eq!( - merge_move_state_sources(discovered.clone(), &configured), - discovered - ); + assert_eq!(merge_move_state_sources(discovered.clone(), &configured), discovered); } #[test] diff --git a/crates/odin-core/src/documents.rs b/crates/odin-core/src/documents.rs index a55cfde..4f345b3 100644 --- a/crates/odin-core/src/documents.rs +++ b/crates/odin-core/src/documents.rs @@ -46,7 +46,6 @@ pub const MUNINN_HID_CONTROLLER_STATE_SCHEMA: &str = "muninn.hid_controller_stat pub const MUNINN_MOVE_IDENTITY_SCHEMA: &str = "muninn.move_identity.v1"; pub const MUNINN_MOVE_LIGHT_COMMAND_SCHEMA: &str = "muninn.move_light_command.v1"; pub const MUNINN_MOVE_HUE_PROGRAM_SCHEMA: &str = "muninn.move_hue_program.v1"; -pub const MUNINN_MOVE_TRACKER_HEALTH_SCHEMA: &str = "muninn.move_tracker_health.v1"; pub const MUNINN_QUEST_ACCESS_SCHEMA: &str = "muninn.quest_access.v1"; pub const MUNINN_COMMAND_BOUNDARY_SCHEMA: &str = "muninn.command_boundary.v1"; pub const MUNINN_TRANSPORT_PROFILE_SCHEMA: &str = "muninn.transport_profile.v1"; @@ -1196,48 +1195,6 @@ pub struct MuninnMoveHueProgramRecord { pub order_mode: String, } -#[derive(Clone, Debug, PartialEq, DatabaseEntry)] -#[cultcache( - type = "muninn.move_tracker_health", - schema = "muninn.move_tracker_health.v1" -)] -pub struct MuninnMoveTrackerHealthRecord { - #[cultcache(key = 0)] - pub health_id: String, - #[cultcache(key = 1)] - pub host_id: String, - #[cultcache(key = 2)] - pub camera_id: String, - #[cultcache(key = 3)] - pub camera_index: i32, - #[cultcache(key = 4)] - pub state: String, - #[cultcache(key = 5)] - pub camera_name: String, - #[cultcache(key = 6)] - pub camera_api: String, - #[cultcache(key = 7)] - pub width: u32, - #[cultcache(key = 8)] - pub height: u32, - #[cultcache(key = 9)] - pub exposure: f32, - #[cultcache(key = 10)] - pub calibrated_controller_count: u32, - #[cultcache(key = 11)] - pub update_count: u64, - #[cultcache(key = 12)] - pub observation_count: u64, - #[cultcache(key = 13)] - pub latest_observation_count: u32, - #[cultcache(key = 14)] - pub last_observation_at: String, - #[cultcache(key = 15)] - pub detail: String, - #[cultcache(key = 16)] - pub updated_at: String, -} - #[derive(Clone, Debug, PartialEq, Eq, DatabaseEntry)] #[cultcache(type = "muninn.quest_access", schema = "muninn.quest_access.v1")] pub struct MuninnQuestAccessRecord { @@ -1364,7 +1321,6 @@ cultmesh_rs::cultmesh_documents!(OdinDocuments { MuninnMoveIdentityRecord => MUNINN_MOVE_IDENTITY_SCHEMA, MuninnMoveLightCommandRecord => MUNINN_MOVE_LIGHT_COMMAND_SCHEMA, MuninnMoveHueProgramRecord => MUNINN_MOVE_HUE_PROGRAM_SCHEMA, - MuninnMoveTrackerHealthRecord => MUNINN_MOVE_TRACKER_HEALTH_SCHEMA, MuninnQuestAccessRecord => MUNINN_QUEST_ACCESS_SCHEMA, MuninnCommandBoundaryCompatRecord => MUNINN_COMMAND_BOUNDARY_SCHEMA, MuninnTransportProfileCompatRecord => MUNINN_TRANSPORT_PROFILE_SCHEMA,