1use serde::{Deserialize, Serialize};
2use std::{fmt, time::SystemTime};
3
4#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
6pub enum SpeedUnit {
7 Kilometers,
9 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#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
24pub enum DeviceMode {
25 Awake = 0,
27 Active = 1,
29 Idle = 2,
31 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#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
59pub enum EmergencyStopState {
60 Normal,
62 Active,
64 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#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
80pub enum AuthenticationStatus {
81 NotAuthenticated,
83 InProgress,
85 Authenticated,
87 Failed,
89}
90
91#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
93pub enum TemperatureStatus {
94 Normal = 0,
96 Stop = 1,
98 ReduceSpeed = 2,
100 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#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
128pub enum DeviceStatusCode {
129 NoError = 0,
131 HighTemperature = 1,
133 WifiScanning = 2,
135 PowerCycleRequired = 3,
137 RequestError = 4,
139 WifiNotConnected = 5,
141 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#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
175pub enum ConnectionHealth {
176 Healthy,
178 Degraded,
180 Unstable,
182 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#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
199pub struct SpeedInfo {
200 pub current: f32,
202 pub target: f32,
204 pub minimum: f32,
206 pub maximum: f32,
208 pub unit: SpeedUnit,
210}
211
212impl SpeedInfo {
213 #[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 #[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#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
260pub struct DeviceStatus {
261 pub speed: SpeedInfo,
263 pub mode: DeviceMode,
265 pub emergency_stop: EmergencyStopState,
267 pub power_on: bool,
269 pub handrail_enabled: bool,
271 pub distance: f32,
273 pub steps: u32,
275 pub temperature: f32,
277 pub temperature_status: TemperatureStatus,
279 pub device_status_code: DeviceStatusCode,
281 pub connection_health: ConnectionHealth,
283 pub authentication: AuthenticationStatus,
285 pub session_active: bool,
287 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#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
314pub struct DeviceInfo {
315 pub name: String,
317 pub mac_address: Option<String>,
319 pub rssi: i16,
321 pub priority: i8,
323 pub firmware_version: Option<String>,
325 pub hardware_version: Option<String>,
327 pub serial_number: Option<String>,
329}
330
331impl DeviceInfo {
332 #[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#[derive(Debug, Clone)]
349pub struct ConnectionParams {
350 pub timeout_ms: u64,
352 pub authenticate: bool,
354 pub retry_attempts: u32,
356 pub scan_timeout_ms: u64,
358}
359
360#[derive(Debug, Clone)]
362pub struct TimeoutConfig {
363 pub default_timeout_ms: u64,
365 pub auth_timeout_ms: u64,
367 pub status_timeout_ms: u64,
369 pub emergency_stop_timeout_ms: u64,
371 pub speed_command_timeout_ms: u64,
373 pub power_command_timeout_ms: u64,
375 pub secure_auth_timeout_ms: u64,
377 pub connection_health_timeout_ms: u64,
379 pub max_retry_attempts: u32,
381 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 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 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}