//! UPP protocol frame structures use serde::{Deserialize, Serialize}; use std::mem; /// UPP USB frame header (5 bytes) #[derive(Debug, Clone, Copy, Serialize, Deserialize)] #[repr(C, packed)] pub struct UPPUsbFrame { pub magic: u16, // 0xBBAA pub cid: u8, // Camera ID pub length: u16, // Data length (excluding header) } /// UPP camera frame header (7 bytes) #[derive(Debug, Clone, Copy, Serialize, Deserialize, Default)] #[repr(C, packed)] pub struct UPPFrameHeader { pub frame_id: u8, // Frame ID pub camera_number: u8, // Camera number pub flags: UPPFlags, // Various flags pub g_sensor: u32, // G-sensor data } /// UPP frame flags #[derive(Debug, Clone, Copy, Serialize, Deserialize, Default)] #[repr(C, packed)] pub struct UPPFlags { pub has_g: bool, // Has G-sensor data (bit 0) pub button_press: bool, // Button press detected (bit 1) pub other: u8, // Other flags (bits 2-7, 6 bits total) } /// Complete UPP frame #[derive(Debug, Clone, Serialize, Deserialize, Default)] pub struct UPPFrame { pub header: UPPFrameHeader, pub data: Vec, } impl UPPUsbFrame { /// Create a new UPP USB frame pub fn new(cid: u8, length: u16) -> Self { Self { magic: super::UPP_USB_MAGIC, cid, length, } } /// Get the total frame size including header /// Based on C++ POC: length field includes camera header + data, but not USB header pub fn total_size(&self) -> usize { mem::size_of::() + self.length as usize } /// Validate the frame magic number pub fn is_valid(&self) -> bool { self.magic == super::UPP_USB_MAGIC } /// Get the expected payload size (camera header + data) /// Based on C++ POC: this is what the length field represents pub fn expected_payload_size(&self) -> usize { self.length as usize } /// Get the expected data size (excluding camera header) /// Camera header is 7 bytes, so data size is payload - 7 pub fn expected_data_size(&self) -> usize { if self.length >= 7 { self.length as usize - 7 } else { 0 } } } impl UPPFrameHeader { /// Create a new UPP frame header pub fn new( frame_id: u8, camera_number: u8, has_g: bool, button_press: bool, g_sensor: u32, ) -> Self { Self { frame_id, camera_number, flags: UPPFlags { has_g, button_press, other: 0, }, g_sensor, } } /// Check if this frame has G-sensor data pub fn has_g_sensor(&self) -> bool { self.flags.has_g } /// Check if button press was detected pub fn button_pressed(&self) -> bool { self.flags.button_press } /// Get other flags pub fn other_flags(&self) -> u8 { self.flags.other } /// Set other flags pub fn set_other_flags(&mut self, flags: u8) { self.flags.other = flags & 0x3F; // Only 6 bits } /// Get G-sensor data pub fn g_sensor_data(&self) -> Option { if self.has_g_sensor() { Some(self.g_sensor) } else { None } } } impl UPPFrame { /// Create a new UPP frame pub fn new(header: UPPFrameHeader, data: Vec) -> Self { Self { header, data } } /// Get the total frame size pub fn total_size(&self) -> usize { mem::size_of::() + self.data.len() } /// Get the frame ID pub fn frame_id(&self) -> u8 { self.header.frame_id } /// Get the camera number pub fn camera_number(&self) -> u8 { self.header.camera_number } /// Check if button was pressed pub fn button_pressed(&self) -> bool { self.header.button_pressed() } /// Get G-sensor data if available pub fn g_sensor_data(&self) -> Option { if self.header.has_g_sensor() { Some(self.header.g_sensor) } else { None } } } impl Default for UPPUsbFrame { fn default() -> Self { Self { magic: super::UPP_USB_MAGIC, cid: super::UPP_CAMID_7, length: 0, } } } #[cfg(test)] mod tests { use super::*; #[test] fn test_upp_usb_frame_creation() { let frame = UPPUsbFrame::new(7, 1024); let magic = frame.magic; let cid = frame.cid; let length = frame.length; assert_eq!(magic, super::super::UPP_USB_MAGIC); assert_eq!(cid, 7); assert_eq!(length, 1024); assert!(frame.is_valid()); } #[test] fn test_upp_usb_frame_validation() { let mut frame = UPPUsbFrame::new(7, 1024); assert!(frame.is_valid()); frame.magic = 0x1234; assert!(!frame.is_valid()); } #[test] fn test_upp_frame_header_creation() { let header = UPPFrameHeader::new(1, 0, true, false, 12345); assert_eq!(header.frame_id, 1); assert_eq!(header.camera_number, 0); assert!(header.has_g_sensor()); assert!(!header.button_pressed()); assert_eq!(header.g_sensor_data(), Some(12345)); } #[test] fn test_upp_frame_header_flags() { let mut header = UPPFrameHeader::default(); assert!(!header.has_g_sensor()); assert!(!header.button_pressed()); header.flags.has_g = true; header.flags.button_press = true; assert!(header.has_g_sensor()); assert!(header.button_pressed()); } #[test] fn test_upp_frame_creation() { let header = UPPFrameHeader::new(1, 0, false, false, 0); let data = vec![1, 2, 3, 4, 5]; let frame = UPPFrame::new(header, data.clone()); assert_eq!(frame.frame_id(), 1); assert_eq!(frame.camera_number(), 0); assert_eq!(frame.data, data); assert_eq!(frame.total_size(), mem::size_of::() + 5); } #[test] fn test_upp_frame_defaults() { let frame = UPPFrame::default(); assert_eq!(frame.frame_id(), 0); assert_eq!(frame.camera_number(), 0); assert!(frame.data.is_empty()); assert!(!frame.button_pressed()); assert!(frame.g_sensor_data().is_none()); } #[test] fn test_upp_flags_other_bits() { let mut header = UPPFrameHeader::default(); header.set_other_flags(0xFF); assert_eq!(header.other_flags(), 0x3F); // Only 6 bits should be set } #[test] fn test_memory_layout() { // Ensure packed structs have correct sizes assert_eq!(mem::size_of::(), 5); // UPPFrameHeader: frame_id(1) + camera_number(1) + flags(3) + g_sensor(4) = 9 bytes assert_eq!(mem::size_of::(), 9); } }