pokeys_lib/
types.rs

1//! Core types and structures for PoKeys library
2
3use serde::{Deserialize, Serialize};
4use std::collections::{HashMap, HashSet};
5
6/// Device connection type
7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
8pub enum DeviceConnectionType {
9    UsbDevice = 0,
10    NetworkDevice = 1,
11    FastUsbDevice = 2,
12}
13
14/// Device connection parameters
15#[derive(Debug, Clone, Copy, PartialEq, Eq)]
16pub enum ConnectionParam {
17    Tcp = 0,
18    Udp = 1,
19}
20
21/// Device type IDs
22#[derive(Debug, Clone, Copy, PartialEq, Eq)]
23pub enum DeviceTypeId {
24    Bootloader55 = 3,
25    Bootloader56U = 15,
26    Bootloader56E = 16,
27    Bootloader58 = 41,
28
29    Device55v1 = 0,
30    Device55v2 = 1,
31    Device55v3 = 2,
32
33    Device56U = 10,
34    Device56E = 11,
35    Device27U = 20,
36    Device27E = 21,
37
38    Device57U = 30,
39    Device57E = 31,
40    PoKeys57CNC = 32,
41    PoKeys57CNCpro4x25 = 33,
42    PoKeys57CNCdb25 = 38,
43    PoKeys57Utest = 39,
44
45    LiniTester = 43,
46
47    Device57Uv0 = 28,
48    Device57Ev0 = 29,
49
50    Device58EU = 40,
51    PoPLC58 = 50,
52
53    OEM1 = 100,
54    SerialReader = 101,
55    X15_02_24 = 102,
56}
57
58/// Device type masks for capability checking
59#[derive(Debug, Clone, Copy)]
60pub struct DeviceTypeMask(pub u64);
61
62impl DeviceTypeMask {
63    pub const BOOTLOADER: Self = Self(1 << 0);
64    pub const BOOTLOADER55: Self = Self(1 << 1);
65    pub const BOOTLOADER56: Self = Self(1 << 2);
66    pub const BOOTLOADER56U: Self = Self(1 << 3);
67    pub const BOOTLOADER56E: Self = Self(1 << 4);
68    pub const BOOTLOADER58: Self = Self(1 << 5);
69
70    pub const DEVICE55: Self = Self(1 << 10);
71    pub const DEVICE55V1: Self = Self(1 << 11);
72    pub const DEVICE55V2: Self = Self(1 << 12);
73    pub const DEVICE55V3: Self = Self(1 << 13);
74
75    pub const DEVICE56: Self = Self(1 << 14);
76    pub const DEVICE56U: Self = Self(1 << 15);
77    pub const DEVICE56E: Self = Self(1 << 16);
78    pub const DEVICE27: Self = Self(1 << 17);
79    pub const DEVICE27U: Self = Self(1 << 18);
80    pub const DEVICE27E: Self = Self(1 << 19);
81
82    pub const DEVICE57: Self = Self(1 << 20);
83    pub const DEVICE57U: Self = Self(1 << 24);
84    pub const DEVICE57E: Self = Self(1 << 25);
85    pub const DEVICE57CNC: Self = Self(1 << 26);
86    pub const DEVICE57CNCDB25: Self = Self(1 << 27);
87    pub const DEVICE57UTEST: Self = Self(1 << 28);
88    pub const DEVICE57CNCPRO4X25: Self = Self(1 << 29);
89
90    pub const DEVICE58: Self = Self(1 << 21);
91    pub const POPLC58: Self = Self(1 << 22);
92
93    pub const POKEYS16RF: Self = Self(1 << 23);
94}
95
96/// Pulse engine state
97#[derive(Debug, Clone, Copy, PartialEq, Eq)]
98pub enum PulseEngineState {
99    Stopped = 0,
100    Internal = 1,
101    Buffer = 2,
102    Running = 3,
103
104    Jogging = 10,
105    Stopping = 11,
106
107    Home = 20,
108    Homing = 21,
109
110    ProbeComplete = 30,
111    Probe = 31,
112    ProbeError = 32,
113
114    HybridProbeStopping = 40,
115    HybridProbeComplete = 41,
116
117    StopLimit = 100,
118    StopEmergency = 101,
119}
120
121/// Pulse engine axis state
122#[derive(Debug, Clone, Copy, PartialEq, Eq)]
123pub enum PulseEngineAxisState {
124    Stopped = 0,
125    Ready = 1,
126    Running = 2,
127    Accelerating = 3,
128    Decelerating = 4,
129
130    HomingResetting = 8,
131    HomingBackingOff = 9,
132    Home = 10,
133    HomingStart = 11,
134    HomingSearch = 12,
135    HomingBack = 13,
136
137    Probed = 14,
138    ProbeStart = 15,
139    ProbeSearch = 16,
140
141    Error = 20,
142    Limit = 30,
143}
144
145/// I2C status codes - unified across PoKeys ecosystem
146#[derive(Debug, Clone, Copy, PartialEq, Eq)]
147pub enum I2cStatus {
148    Error = 0,
149    Ok = 1,
150    Complete = 2,
151    InProgress = 0x10,
152    Timeout = 0x20,
153    ChecksumError = 0x30,  // For protocol validation
154    DeviceNotFound = 0x40, // Device scan specific
155    PacketTooLarge = 0x50, // Fragmentation needed
156}
157
158/// I2C configuration options - standardized interface
159#[derive(Debug, Clone)]
160pub struct I2cConfig {
161    // Common fields
162    pub max_packet_size: usize,
163    pub timeout_ms: u64,
164    pub retry_attempts: u32,
165
166    // PoKeys specific features
167    pub auto_fragment: bool,
168    pub fragment_delay_ms: u64,
169    pub validation_level: ValidationLevel,
170    pub performance_monitoring: bool,
171}
172
173impl Default for I2cConfig {
174    fn default() -> Self {
175        Self {
176            max_packet_size: 32,
177            timeout_ms: 1000,
178            retry_attempts: 3,
179            auto_fragment: false,
180            fragment_delay_ms: 10,
181            validation_level: ValidationLevel::None,
182            performance_monitoring: false,
183        }
184    }
185}
186
187/// Retry configuration for error recovery
188#[derive(Debug, Clone)]
189pub struct RetryConfig {
190    pub max_attempts: u32,
191    pub base_delay_ms: u64,
192    pub max_delay_ms: u64,
193    pub backoff_multiplier: f64,
194    pub jitter: bool,
195}
196
197impl Default for RetryConfig {
198    fn default() -> Self {
199        Self {
200            max_attempts: 3,
201            base_delay_ms: 100,
202            max_delay_ms: 2000,
203            backoff_multiplier: 2.0,
204            jitter: true,
205        }
206    }
207}
208
209/// Validation levels for protocol validation
210#[derive(Debug, Clone, PartialEq, Eq)]
211pub enum ValidationLevel {
212    None,   // Current behavior - pass everything through
213    Basic,  // Validate packet structure only
214    Strict, // Full protocol validation
215    Custom(ValidationConfig),
216}
217
218/// Custom validation configuration
219#[derive(Debug, Clone, PartialEq, Eq)]
220pub struct ValidationConfig {
221    pub validate_checksums: bool,
222    pub validate_command_ids: bool,
223    pub validate_device_ids: bool,
224    pub validate_packet_structure: bool,
225    pub max_device_id: u8,
226    pub valid_commands: HashSet<u8>,
227}
228
229impl Default for ValidationConfig {
230    fn default() -> Self {
231        Self {
232            validate_checksums: true,
233            validate_command_ids: true,
234            validate_device_ids: true,
235            validate_packet_structure: true,
236            max_device_id: 255,
237            valid_commands: HashSet::new(),
238        }
239    }
240}
241
242/// I2C performance metrics
243#[derive(Debug, Clone, Default)]
244pub struct I2cMetrics {
245    pub total_commands: u64,
246    pub successful_commands: u64,
247    pub failed_commands: u64,
248    pub average_response_time: std::time::Duration,
249    pub max_response_time: std::time::Duration,
250    pub min_response_time: std::time::Duration,
251    pub error_counts: HashMap<String, u32>,
252}
253
254/// Health status for device diagnostics
255#[derive(Debug, Clone)]
256pub struct HealthStatus {
257    pub connectivity: ConnectivityStatus,
258    pub i2c_health: I2cHealthStatus,
259    pub error_rate: f64,
260    pub performance: PerformanceSummary,
261}
262
263/// Connectivity status
264#[derive(Debug, Clone)]
265pub enum ConnectivityStatus {
266    Healthy,
267    Degraded(String),
268    Failed(String),
269}
270
271/// I2C health status
272#[derive(Debug, Clone)]
273pub enum I2cHealthStatus {
274    Healthy,
275    Degraded(String),
276    Failed(String),
277}
278
279/// Performance summary
280#[derive(Debug, Clone, Default)]
281pub struct PerformanceSummary {
282    pub avg_response_time_ms: f64,
283    pub success_rate: f64,
284    pub throughput_commands_per_sec: f64,
285}
286
287/// LCD mode
288#[derive(Debug, Clone, Copy, PartialEq, Eq)]
289pub enum LcdMode {
290    Direct = 0,
291    Buffered = 1,
292}
293
294/// Device information structure
295#[derive(Debug, Clone, Serialize, Deserialize)]
296pub struct DeviceInfo {
297    pub pin_count: u32,
298    pub pwm_count: u32,
299    pub basic_encoder_count: u32,
300    pub encoders_count: u32,
301    pub fast_encoders: u32,
302    pub ultra_fast_encoders: u32,
303    pub pwm_internal_frequency: u32,
304    pub analog_inputs: u32,
305
306    // Feature flags
307    pub key_mapping: u32,
308    pub triggered_key_mapping: u32,
309    pub key_repeat_delay: u32,
310    pub digital_counters: u32,
311    pub joystick_button_axis_mapping: u32,
312    pub joystick_analog_to_digital_mapping: u32,
313    pub macros: u32,
314    pub matrix_keyboard: u32,
315    pub matrix_keyboard_triggered_mapping: u32,
316    pub lcd: u32,
317    pub matrix_led: u32,
318    pub connection_signal: u32,
319    pub po_ext_bus: u32,
320    pub po_net: u32,
321    pub analog_filtering: u32,
322    pub init_outputs_start: u32,
323    pub prot_i2c: u32,
324    pub prot_1wire: u32,
325    pub additional_options: u32,
326    pub load_status: u32,
327    pub custom_device_name: u32,
328    pub po_tlog27_support: u32,
329    pub sensor_list: u32,
330    pub web_interface: u32,
331    pub fail_safe_settings: u32,
332    pub joystick_hat_switch: u32,
333    pub pulse_engine: u32,
334    pub pulse_engine_v2: u32,
335    pub easy_sensors: u32,
336}
337
338/// Device-specific data
339#[derive(Debug, Clone, Serialize, Deserialize)]
340pub struct DeviceData {
341    pub device_type_id: u32,
342    pub serial_number: u32,
343    pub device_name: [u8; 30],
344    pub device_type_name: [u8; 30],
345    pub build_date: [u8; 12],
346    pub activation_code: [u8; 8],
347    pub firmware_version_major: u8,
348    pub firmware_version_minor: u8,
349    pub firmware_revision: u8,
350    pub user_id: u8,
351    pub device_type: u8,
352    pub activated_options: u8,
353    pub device_lock_status: u8,
354    pub hw_type: u8,
355    pub fw_type: u8,
356    pub product_id: u8,
357    pub secondary_firmware_version_major: u8,
358    pub secondary_firmware_version_minor: u8,
359    pub device_is_bootloader: u8,
360}
361
362/// Network device summary for enumeration
363#[derive(Debug, Clone, Serialize, Deserialize)]
364pub struct NetworkDeviceSummary {
365    pub serial_number: u32,
366    pub ip_address: [u8; 4],
367    pub host_ip: [u8; 4],
368    pub firmware_version_major: u8,
369    pub firmware_version_minor: u8,
370    pub firmware_revision: u8,
371    pub user_id: u8,
372    pub dhcp: u8,
373    pub hw_type: u8,
374    pub use_udp: u8,
375}
376
377/// Network device information
378#[derive(Debug, Clone, Serialize, Deserialize, Default)]
379pub struct NetworkDeviceInfo {
380    pub ip_address_current: [u8; 4],
381    pub ip_address_setup: [u8; 4],
382    pub subnet_mask: [u8; 4],
383    pub gateway_ip: [u8; 4],
384    pub tcp_timeout: u16,
385    pub additional_network_options: u8,
386    pub dhcp: u8,
387}
388
389/// Real-time clock structure
390#[derive(Debug, Clone, Serialize, Deserialize)]
391pub struct RealTimeClock {
392    pub sec: u8,
393    pub min: u8,
394    pub hour: u8,
395    pub dow: u8,
396    pub dom: u8,
397    pub tmp: u8,
398    pub doy: u16,
399    pub month: u16,
400    pub year: u16,
401}
402
403/// CAN message structure
404#[derive(Debug, Clone, Serialize, Deserialize)]
405pub struct CanMessage {
406    pub id: u32,
407    pub data: [u8; 8],
408    pub len: u8,
409    pub format: u8,
410    pub msg_type: u8,
411}
412
413/// Communication buffer size constants
414pub const REQUEST_BUFFER_SIZE: usize = 68;
415pub const RESPONSE_BUFFER_SIZE: usize = 68;
416pub const MULTIPART_BUFFER_SIZE: usize = 448;
417
418/// Default implementations for common types
419impl Default for DeviceData {
420    fn default() -> Self {
421        Self {
422            device_type_id: 0,
423            serial_number: 0,
424            device_name: [0; 30],
425            device_type_name: [0; 30],
426            build_date: [0; 12],
427            activation_code: [0; 8],
428            firmware_version_major: 0,
429            firmware_version_minor: 0,
430            firmware_revision: 0,
431            user_id: 0,
432            device_type: 0,
433            activated_options: 0,
434            device_lock_status: 0,
435            hw_type: 0,
436            fw_type: 0,
437            product_id: 0,
438            secondary_firmware_version_major: 0,
439            secondary_firmware_version_minor: 0,
440            device_is_bootloader: 0,
441        }
442    }
443}
444
445/// Additional types for testing compatibility
446impl DeviceData {
447    pub fn device_locked(&self) -> bool {
448        self.device_lock_status != 0
449    }
450
451    pub fn device_features(&self) -> u8 {
452        self.activated_options
453    }
454
455    /// Parse and format the software version from bytes 5 and 6 of device data response
456    /// Byte 5: bits 4-7 = major-1, bits 0-3 = minor
457    /// Byte 6: revision number
458    /// Returns formatted version string like "4.7.15"
459    pub fn software_version_string(&self) -> String {
460        // For now, using firmware_version_major as byte 5 and firmware_version_minor as byte 6
461        // This will be updated when we implement the proper read device data command
462        let software_version_byte = self.firmware_version_major;
463        let revision_byte = self.firmware_version_minor;
464
465        // Extract major version: 1 + bits 4-7
466        let major = 1 + ((software_version_byte >> 4) & 0x0F);
467
468        // Extract minor version: bits 0-3
469        let minor = software_version_byte & 0x0F;
470
471        // Revision is the full byte 6 value
472        let revision = revision_byte;
473
474        format!("{}.{}.{}", major, minor, revision)
475    }
476
477    pub fn device_name(&self) -> String {
478        String::from_utf8_lossy(&self.device_name)
479            .trim_end_matches('\0')
480            .to_string()
481    }
482
483    /// Get the device type name based on hardware ID, device_type_id, or extracted from device_name
484    pub fn device_type_name(&self) -> String {
485        // First check for device signature in device_type_name field (from read device data command)
486
487        // Try to use the hardware ID (hw_type) from read device data command (byte 19)
488        match self.hw_type {
489            1 => "PoKeys55".to_string(),
490            2 => "PoKeys55".to_string(),
491            3 => "PoKeys55".to_string(),
492            10 => "Pokeys56U".to_string(),
493            11 => "Pokeys56E".to_string(),
494            28 => "Pokeys57U".to_string(),
495            29 => "Pokeys57E".to_string(),
496            30 => "PoKeys57Uv1.1".to_string(),
497            31 => "PoKeys57Ev1.1".to_string(),
498            32 => "PoKeys57CNC".to_string(),
499            35 => "PoKeys57U OEM".to_string(),
500            36 => "PoKeys57U OEM".to_string(),
501            37 => "PoPLC57NG".to_string(),
502            38 => "PoKeys57CNCdb25 ".to_string(),
503            39 => "PoKeys57Utest ".to_string(),
504            40 => "PoKeys58EU".to_string(),
505            41 => "PoBootload (series 58)".to_string(),
506            43 => "LiniTester programmer".to_string(),
507            44 => "LiniTester calibrator  ".to_string(),
508            45 => "PoKeys57Industrial1 ".to_string(),
509            50 => "PoPLC v1.0".to_string(),
510            60 => "PoKeys16".to_string(),
511            // Add more HW ID mappings as needed
512            _ => {
513                // Fallback to showing the unknown device ID and hardware ID
514                format!(
515                    "Unknown Device (ID: {}, HW: {})",
516                    self.device_type_id, self.hw_type
517                )
518            }
519        }
520    }
521
522    /// Get the build date as a string
523    pub fn build_date_string(&self) -> String {
524        String::from_utf8_lossy(&self.build_date)
525            .trim_end_matches('\0')
526            .to_string()
527    }
528}
529
530impl NetworkDeviceInfo {
531    pub fn ip_address(&self) -> [u8; 4] {
532        self.ip_address_current
533    }
534
535    pub fn gateway(&self) -> [u8; 4] {
536        self.gateway_ip
537    }
538
539    pub fn dns_server(&self) -> [u8; 4] {
540        [0; 4] // Not stored in this structure
541    }
542
543    pub fn mac_address(&self) -> [u8; 6] {
544        [0; 6] // Not stored in this structure
545    }
546
547    pub fn device_name(&self) -> String {
548        "".to_string() // Not stored in this structure
549    }
550
551    pub fn http_port(&self) -> u16 {
552        80 // Default HTTP port
553    }
554
555    pub fn tcp_port(&self) -> u16 {
556        20055 // Default PoKeys TCP port
557    }
558
559    pub fn udp_port(&self) -> u16 {
560        20055 // Default PoKeys UDP port
561    }
562
563    pub fn dhcp_enabled(&self) -> bool {
564        self.dhcp != 0
565    }
566}
567
568/// USB vendor and product IDs
569pub const USB_VENDOR_ID: u16 = 0x1DC3;
570pub const USB_PRODUCT_ID_1: u16 = 0x1001;
571pub const USB_PRODUCT_ID_2: u16 = 0x1002;
572
573/// Protocol constants
574pub const REQUEST_HEADER: u8 = 0xBB;
575pub const RESPONSE_HEADER: u8 = 0xAA;
576pub const CHECKSUM_LENGTH: usize = 7;
577
578#[cfg(test)]
579mod tests {
580    use super::*;
581
582    #[test]
583    fn test_device_type_name() {
584        // Test known device types by hw_type
585        let mut device_data = DeviceData {
586            hw_type: 10,
587            ..Default::default()
588        };
589        assert_eq!(device_data.device_type_name(), "Pokeys56U");
590
591        device_data.hw_type = 11;
592        assert_eq!(device_data.device_type_name(), "Pokeys56E");
593
594        device_data.hw_type = 30;
595        assert_eq!(device_data.device_type_name(), "PoKeys57Uv1.1");
596
597        device_data.hw_type = 31;
598        assert_eq!(device_data.device_type_name(), "PoKeys57Ev1.1");
599
600        device_data.hw_type = 32;
601        assert_eq!(device_data.device_type_name(), "PoKeys57CNC");
602
603        // Test device type extraction from device_name field
604        device_data.hw_type = 0; // Unknown hw_type
605        device_data.device_type_id = 999999; // Unknown ID
606        device_data.device_name = [0; 30];
607        let test_name = b"Dec 11 2024PoKeys57E\0\0\0\0\0\0\0\0\0\0";
608        device_data.device_name[..test_name.len()].copy_from_slice(test_name);
609        assert_eq!(
610            device_data.device_type_name(),
611            "Unknown Device (ID: 999999, HW: 0)"
612        );
613
614        // Test another device type in name
615        let test_name2 = b"Build PoKeys56U info\0\0\0\0\0\0\0\0\0";
616        device_data.device_name[..test_name2.len()].copy_from_slice(test_name2);
617        assert_eq!(
618            device_data.device_type_name(),
619            "Unknown Device (ID: 999999, HW: 0)"
620        );
621
622        // Test unknown device type
623        device_data.device_type_id = 99;
624        device_data.hw_type = 0;
625        device_data.device_name = [0; 30];
626        let test_name3 = b"Some other text\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
627        device_data.device_name[..test_name3.len()].copy_from_slice(test_name3);
628        assert_eq!(
629            device_data.device_type_name(),
630            "Unknown Device (ID: 99, HW: 0)"
631        );
632    }
633}