treadlers/
types.rs

1use serde::{Deserialize, Serialize};
2use std::{fmt, time::SystemTime};
3
4/// Speed unit for treadmill operations
5#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
6pub enum SpeedUnit {
7    /// Kilometers per hour
8    Kilometers,
9    /// Miles per hour
10    Miles,
11}
12
13impl fmt::Display for SpeedUnit {
14    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
15        match self {
16            Self::Kilometers => write!(f, "km/h"),
17            Self::Miles => write!(f, "mph"),
18        }
19    }
20}
21
22/// Device operational mode
23#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
24pub enum DeviceMode {
25    /// Device is powered on but not active
26    Awake = 0,
27    /// Device is actively running
28    Active = 1,
29    /// Device is idle/paused
30    Idle = 2,
31    /// Unknown state
32    Unknown = 3,
33}
34
35impl From<u8> for DeviceMode {
36    fn from(value: u8) -> Self {
37        match value {
38            0 => Self::Awake,
39            1 => Self::Active,
40            2 => Self::Idle,
41            _ => Self::Unknown,
42        }
43    }
44}
45
46impl fmt::Display for DeviceMode {
47    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
48        match self {
49            Self::Awake => write!(f, "Awake"),
50            Self::Active => write!(f, "Active"),
51            Self::Idle => write!(f, "Idle"),
52            Self::Unknown => write!(f, "Unknown"),
53        }
54    }
55}
56
57/// Emergency stop state
58#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
59pub enum EmergencyStopState {
60    /// Normal operation
61    Normal,
62    /// Emergency stop is active
63    Active,
64    /// Emergency stop reset required
65    ResetRequired,
66}
67
68impl fmt::Display for EmergencyStopState {
69    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
70        match self {
71            Self::Normal => write!(f, "Normal"),
72            Self::Active => write!(f, "Emergency Stop Active"),
73            Self::ResetRequired => write!(f, "Reset Required"),
74        }
75    }
76}
77
78/// Authentication status
79#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
80pub enum AuthenticationStatus {
81    /// Not authenticated
82    NotAuthenticated,
83    /// Authentication in progress
84    InProgress,
85    /// Successfully authenticated
86    Authenticated,
87    /// Authentication failed
88    Failed,
89}
90
91/// Temperature status codes extracted from decompiled Android app
92#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
93pub enum TemperatureStatus {
94    /// Normal temperature operation
95    Normal = 0,
96    /// Critical temperature - forces emergency stop
97    Stop = 1,
98    /// High temperature - forces speed reduction
99    ReduceSpeed = 2,
100    /// Temperature sensor error
101    Unknown = 3,
102}
103
104impl From<u8> for TemperatureStatus {
105    fn from(value: u8) -> Self {
106        match value {
107            0 => Self::Normal,
108            1 => Self::Stop,
109            2 => Self::ReduceSpeed,
110            _ => Self::Unknown,
111        }
112    }
113}
114
115impl fmt::Display for TemperatureStatus {
116    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
117        match self {
118            Self::Normal => write!(f, "Normal"),
119            Self::Stop => write!(f, "Emergency Stop - Critical Temperature"),
120            Self::ReduceSpeed => write!(f, "Warning - High Temperature"),
121            Self::Unknown => write!(f, "Temperature Sensor Error"),
122        }
123    }
124}
125
126/// Device status codes extracted from decompiled Android app
127#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
128pub enum DeviceStatusCode {
129    /// Normal operation
130    NoError = 0,
131    /// High temperature detected - safety condition
132    HighTemperature = 1,
133    /// `WiFi` scanning in progress
134    WifiScanning = 2,
135    /// Device requires power cycle - safety condition
136    PowerCycleRequired = 3,
137    /// Request processing error
138    RequestError = 4,
139    /// `WiFi` connection failed
140    WifiNotConnected = 5,
141    /// `WiFi` system error
142    WifiError = 6,
143}
144
145impl From<u8> for DeviceStatusCode {
146    fn from(value: u8) -> Self {
147        match value {
148            0 => Self::NoError,
149            1 => Self::HighTemperature,
150            2 => Self::WifiScanning,
151            3 => Self::PowerCycleRequired,
152            5 => Self::WifiNotConnected,
153            6 => Self::WifiError,
154            _ => Self::RequestError,
155        }
156    }
157}
158
159impl fmt::Display for DeviceStatusCode {
160    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
161        match self {
162            Self::NoError => write!(f, "No Error"),
163            Self::HighTemperature => write!(f, "High Temperature"),
164            Self::WifiScanning => write!(f, "WiFi Scanning"),
165            Self::PowerCycleRequired => write!(f, "Power Cycle Required"),
166            Self::RequestError => write!(f, "Request Error"),
167            Self::WifiNotConnected => write!(f, "WiFi Not Connected"),
168            Self::WifiError => write!(f, "WiFi Error"),
169        }
170    }
171}
172
173/// Connection health status for monitoring connection safety
174#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
175pub enum ConnectionHealth {
176    /// Connection is healthy
177    Healthy,
178    /// Connection is degraded but functional
179    Degraded,
180    /// Connection is unstable
181    Unstable,
182    /// Connection is lost
183    Lost,
184}
185
186impl fmt::Display for ConnectionHealth {
187    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
188        match self {
189            Self::Healthy => write!(f, "Healthy"),
190            Self::Degraded => write!(f, "Degraded"),
191            Self::Unstable => write!(f, "Unstable"),
192            Self::Lost => write!(f, "Lost"),
193        }
194    }
195}
196
197/// Speed information
198#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
199pub struct SpeedInfo {
200    /// Current speed
201    pub current: f32,
202    /// Target speed
203    pub target: f32,
204    /// Minimum speed
205    pub minimum: f32,
206    /// Maximum speed
207    pub maximum: f32,
208    /// Speed unit
209    pub unit: SpeedUnit,
210}
211
212impl SpeedInfo {
213    /// Create new speed info
214    #[must_use]
215    pub const fn new(current: f32, target: f32, min: f32, max: f32, unit: SpeedUnit) -> Self {
216        Self {
217            current,
218            target,
219            minimum: min,
220            maximum: max,
221            unit,
222        }
223    }
224
225    /// Convert speed to the specified unit
226    #[must_use]
227    pub fn convert_to(&self, unit: SpeedUnit) -> Self {
228        if self.unit == unit {
229            return *self;
230        }
231
232        let (current, target, min, max) = match (self.unit, unit) {
233            (SpeedUnit::Kilometers, SpeedUnit::Miles) => (
234                self.current * 0.6214,
235                self.target * 0.6214,
236                self.minimum * 0.6214,
237                self.maximum * 0.6214,
238            ),
239            (SpeedUnit::Miles, SpeedUnit::Kilometers) => (
240                self.current * 1.6093,
241                self.target * 1.6093,
242                self.minimum * 1.6093,
243                self.maximum * 1.6093,
244            ),
245            _ => (self.current, self.target, self.minimum, self.maximum),
246        };
247
248        Self {
249            current,
250            target,
251            minimum: min,
252            maximum: max,
253            unit,
254        }
255    }
256}
257
258/// Device status information
259#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
260pub struct DeviceStatus {
261    /// Current speed information
262    pub speed: SpeedInfo,
263    /// Device operational mode
264    pub mode: DeviceMode,
265    /// Emergency stop state
266    pub emergency_stop: EmergencyStopState,
267    /// Power state
268    pub power_on: bool,
269    /// Handrail status
270    pub handrail_enabled: bool,
271    /// Current distance traveled
272    pub distance: f32,
273    /// Step count
274    pub steps: u32,
275    /// Device temperature (Celsius)
276    pub temperature: f32,
277    /// Temperature status for safety monitoring
278    pub temperature_status: TemperatureStatus,
279    /// Device status code for error monitoring
280    pub device_status_code: DeviceStatusCode,
281    /// Connection health for safety monitoring
282    pub connection_health: ConnectionHealth,
283    /// Authentication status
284    pub authentication: AuthenticationStatus,
285    /// Session active
286    pub session_active: bool,
287    /// Last status update timestamp
288    pub timestamp: SystemTime,
289}
290
291impl Default for DeviceStatus {
292    fn default() -> Self {
293        Self {
294            speed: SpeedInfo::new(0.0, 0.0, 0.0, 20.0, SpeedUnit::Kilometers),
295            mode: DeviceMode::Unknown,
296            emergency_stop: EmergencyStopState::Normal,
297            power_on: false,
298            handrail_enabled: true,
299            distance: 0.0,
300            steps: 0,
301            temperature: 0.0,
302            temperature_status: TemperatureStatus::Normal,
303            device_status_code: DeviceStatusCode::NoError,
304            connection_health: ConnectionHealth::Healthy,
305            authentication: AuthenticationStatus::NotAuthenticated,
306            session_active: false,
307            timestamp: SystemTime::now(),
308        }
309    }
310}
311
312/// Device information
313#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
314pub struct DeviceInfo {
315    /// Device name
316    pub name: String,
317    /// Device MAC address
318    pub mac_address: Option<String>,
319    /// Signal strength (RSSI)
320    pub rssi: i16,
321    /// Device priority (from manufacturer data)
322    pub priority: i8,
323    /// Firmware version
324    pub firmware_version: Option<String>,
325    /// Hardware version
326    pub hardware_version: Option<String>,
327    /// Serial number
328    pub serial_number: Option<String>,
329}
330
331impl DeviceInfo {
332    /// Create new device info
333    #[must_use]
334    pub const fn new(name: String, rssi: i16, priority: i8) -> Self {
335        Self {
336            name,
337            mac_address: None,
338            rssi,
339            priority,
340            firmware_version: None,
341            hardware_version: None,
342            serial_number: None,
343        }
344    }
345}
346
347/// Connection parameters
348#[derive(Debug, Clone)]
349pub struct ConnectionParams {
350    /// Connection timeout in milliseconds
351    pub timeout_ms: u64,
352    /// Enable authentication
353    pub authenticate: bool,
354    /// Retry attempts
355    pub retry_attempts: u32,
356    /// Scan timeout in milliseconds
357    pub scan_timeout_ms: u64,
358}
359
360/// Protocol-compliant timeout configuration
361#[derive(Debug, Clone)]
362pub struct TimeoutConfig {
363    /// Default command timeout in milliseconds
364    pub default_timeout_ms: u64,
365    /// Authentication timeout in milliseconds
366    pub auth_timeout_ms: u64,
367    /// Status request timeout in milliseconds
368    pub status_timeout_ms: u64,
369    /// Emergency stop timeout in milliseconds
370    pub emergency_stop_timeout_ms: u64,
371    /// Speed command timeout in milliseconds
372    pub speed_command_timeout_ms: u64,
373    /// Power command timeout in milliseconds
374    pub power_command_timeout_ms: u64,
375    /// Secure authentication timeout in milliseconds
376    pub secure_auth_timeout_ms: u64,
377    /// Connection health check timeout in milliseconds
378    pub connection_health_timeout_ms: u64,
379    /// Maximum retry attempts for failed commands
380    pub max_retry_attempts: u32,
381    /// Retry delay in milliseconds
382    pub retry_delay_ms: u64,
383}
384
385impl Default for ConnectionParams {
386    fn default() -> Self {
387        Self {
388            timeout_ms: 30_000,
389            authenticate: true,
390            retry_attempts: 3,
391            scan_timeout_ms: 10_000,
392        }
393    }
394}
395
396impl Default for TimeoutConfig {
397    fn default() -> Self {
398        Self {
399            default_timeout_ms: 3_000,
400            auth_timeout_ms: 5_000,
401            status_timeout_ms: 2_000,
402            emergency_stop_timeout_ms: 5_000,
403            speed_command_timeout_ms: 3_000,
404            power_command_timeout_ms: 4_000,
405            secure_auth_timeout_ms: 8_000,
406            connection_health_timeout_ms: 30_000,
407            max_retry_attempts: 3,
408            retry_delay_ms: 500,
409        }
410    }
411}
412
413#[cfg(test)]
414mod tests {
415    use super::*;
416
417    #[test]
418    fn test_speed_conversion() {
419        let speed_km = SpeedInfo::new(10.0, 12.0, 0.0, 20.0, SpeedUnit::Kilometers);
420        let speed_mph = speed_km.convert_to(SpeedUnit::Miles);
421
422        assert!((speed_mph.current - 6.214).abs() < 0.01);
423        assert_eq!(speed_mph.unit, SpeedUnit::Miles);
424    }
425
426    #[test]
427    fn test_device_mode_from_u8() {
428        assert_eq!(DeviceMode::from(0), DeviceMode::Awake);
429        assert_eq!(DeviceMode::from(1), DeviceMode::Active);
430        assert_eq!(DeviceMode::from(2), DeviceMode::Idle);
431        assert_eq!(DeviceMode::from(99), DeviceMode::Unknown);
432    }
433
434    #[test]
435    fn test_timeout_config_defaults() {
436        let config = TimeoutConfig::default();
437
438        assert_eq!(config.default_timeout_ms, 3_000);
439        assert_eq!(config.auth_timeout_ms, 5_000);
440        assert_eq!(config.status_timeout_ms, 2_000);
441        assert_eq!(config.emergency_stop_timeout_ms, 5_000);
442        assert_eq!(config.speed_command_timeout_ms, 3_000);
443        assert_eq!(config.power_command_timeout_ms, 4_000);
444        assert_eq!(config.secure_auth_timeout_ms, 8_000);
445        assert_eq!(config.connection_health_timeout_ms, 30_000);
446        assert_eq!(config.max_retry_attempts, 3);
447        assert_eq!(config.retry_delay_ms, 500);
448    }
449
450    #[test]
451    fn test_speed_unit_conversion() {
452        let speed_km = SpeedInfo::new(10.0, 12.0, 0.0, 20.0, SpeedUnit::Kilometers);
453        let speed_mph = speed_km.convert_to(SpeedUnit::Miles);
454
455        // 10 km/h should be approximately 6.214 mph
456        assert!((speed_mph.current - 6.214).abs() < 0.01);
457        assert!((speed_mph.target - 7.457).abs() < 0.01);
458        assert_eq!(speed_mph.unit, SpeedUnit::Miles);
459
460        // Converting back should give original values
461        let speed_km_back = speed_mph.convert_to(SpeedUnit::Kilometers);
462        assert!((speed_km_back.current - 10.0).abs() < 0.01);
463        assert!((speed_km_back.target - 12.0).abs() < 0.01);
464    }
465
466    #[test]
467    fn test_device_info_creation() {
468        let info = DeviceInfo::new("Test Treadmill".to_string(), -50, 1);
469        assert_eq!(info.name, "Test Treadmill");
470        assert_eq!(info.rssi, -50);
471        assert_eq!(info.priority, 1);
472        assert!(info.mac_address.is_none());
473    }
474
475    #[test]
476    fn test_connection_params_default() {
477        let params = ConnectionParams::default();
478        assert_eq!(params.timeout_ms, 30_000);
479        assert!(params.authenticate);
480        assert_eq!(params.retry_attempts, 3);
481        assert_eq!(params.scan_timeout_ms, 10_000);
482    }
483}