pokeys_lib/
device.rs

1//! Device enumeration, connection, and management
2
3use crate::communication::{CommunicationManager, NetworkInterface, UsbHidInterface};
4use crate::encoders::EncoderData;
5use crate::error::{PoKeysError, Result};
6use crate::io::PinData;
7use crate::keyboard_matrix::MatrixKeyboard;
8use crate::lcd::LcdData;
9use crate::matrix::MatrixLed;
10use crate::pulse_engine::PulseEngineV2;
11use crate::pwm::PwmData;
12use crate::sensors::EasySensor;
13use crate::types::*;
14use std::sync::{LazyLock, Mutex};
15use std::time::Duration;
16
17/// Main PoKeys device structure
18pub struct PoKeysDevice {
19    // Connection information
20    connection_type: DeviceConnectionType,
21    connection_param: ConnectionParam,
22
23    // Device information
24    pub info: DeviceInfo,
25    pub device_data: DeviceData,
26    pub network_device_data: Option<NetworkDeviceInfo>,
27
28    // Device model
29    pub model: Option<crate::models::DeviceModel>,
30
31    // Pin and I/O data
32    pub pins: Vec<PinData>,
33    pub encoders: Vec<EncoderData>,
34    pub pwm: PwmData,
35
36    // Peripheral data
37    pub matrix_keyboard: MatrixKeyboard,
38    pub matrix_led: Vec<MatrixLed>,
39    pub lcd: LcdData,
40    pub pulse_engine_v2: PulseEngineV2,
41    pub easy_sensors: Vec<EasySensor>,
42    pub rtc: RealTimeClock,
43
44    // Communication
45    pub(crate) communication: CommunicationManager,
46    usb_interface: Option<Box<dyn UsbHidInterface>>,
47    network_interface: Option<Box<dyn NetworkInterface>>,
48
49    // Configuration
50    pub fast_encoders_configuration: u8,
51    pub fast_encoders_options: u8,
52    pub ultra_fast_encoder_configuration: u8,
53    pub ultra_fast_encoder_options: u8,
54    pub ultra_fast_encoder_filter: u32,
55    pub po_ext_bus_data: Vec<u8>,
56
57    // I2C configuration and metrics
58    pub i2c_config: I2cConfig,
59    pub i2c_metrics: I2cMetrics,
60    pub validation_level: ValidationLevel,
61
62    // Internal state
63    #[allow(dead_code)]
64    request_buffer: [u8; REQUEST_BUFFER_SIZE],
65    #[allow(dead_code)]
66    response_buffer: [u8; RESPONSE_BUFFER_SIZE],
67    #[allow(dead_code)]
68    multipart_buffer: Vec<u8>,
69}
70
71impl PoKeysDevice {
72    /// Create a new device instance
73    fn new(connection_type: DeviceConnectionType) -> Self {
74        Self {
75            connection_type,
76            connection_param: ConnectionParam::Tcp,
77            info: DeviceInfo::default(),
78            device_data: DeviceData::default(),
79            network_device_data: None,
80            model: None,
81            pins: Vec::new(),
82            encoders: Vec::new(),
83            pwm: PwmData::new(),
84            matrix_keyboard: MatrixKeyboard::new(),
85            matrix_led: Vec::new(),
86            lcd: LcdData::new(),
87            pulse_engine_v2: PulseEngineV2::new(),
88            easy_sensors: Vec::new(),
89            rtc: RealTimeClock::default(),
90            communication: CommunicationManager::new(connection_type),
91            usb_interface: None,
92            network_interface: None,
93            fast_encoders_configuration: 0,
94            fast_encoders_options: 0,
95            ultra_fast_encoder_configuration: 0,
96            ultra_fast_encoder_options: 0,
97            ultra_fast_encoder_filter: 0,
98            po_ext_bus_data: Vec::new(),
99            i2c_config: I2cConfig::default(),
100            i2c_metrics: I2cMetrics::default(),
101            validation_level: ValidationLevel::None,
102            request_buffer: [0; REQUEST_BUFFER_SIZE],
103            response_buffer: [0; RESPONSE_BUFFER_SIZE],
104            multipart_buffer: Vec::new(),
105        }
106    }
107
108    /// Get device information from the connected device
109    pub fn get_device_data(&mut self) -> Result<()> {
110        // Use the comprehensive read device data command instead of the old method
111        self.read_device_data()?;
112        self.initialize_device_structures()?;
113
114        // Try to load the device model
115        self.load_device_model()?;
116
117        Ok(())
118    }
119
120    /// Load the device model based on the device type
121    fn load_device_model(&mut self) -> Result<()> {
122        use crate::models::load_model;
123
124        // Determine the model name based on the device type
125        let device_type_name = self.device_data.device_type_name();
126        let model_name = match device_type_name.as_str() {
127            "PoKeys56U" => "PoKeys56U",
128            "PoKeys56E" => "PoKeys56E",
129            "PoKeys57U" => "PoKeys57U",
130            "PoKeys57E" => "PoKeys57E",
131            "PoKeys57Ev1.1" => "PoKeys57E", // Map v1.1 to base model
132            "PoKeys57CNC" => "PoKeys57CNC",
133            _ => return Ok(()), // No model for other device types
134        };
135
136        // Try to load the model
137        match load_model(model_name, None) {
138            Ok(model) => {
139                log::info!("Loaded device model: {}", model_name);
140                self.model = Some(model);
141                Ok(())
142            }
143            Err(e) => {
144                log::warn!("Failed to load device model {}: {}", model_name, e);
145                Ok(()) // Continue without a model
146            }
147        }
148    }
149
150    /// Check if a pin supports a specific capability
151    ///
152    /// # Arguments
153    ///
154    /// * `pin` - The pin number to check
155    /// * `capability` - The capability to check for
156    ///
157    /// # Returns
158    ///
159    /// * `bool` - True if the pin supports the capability, false otherwise
160    pub fn is_pin_capability_supported(&self, pin: u32, capability: &str) -> bool {
161        if let Some(model) = &self.model {
162            model.is_pin_capability_supported(pin as u8, capability)
163        } else {
164            // If no model is loaded, assume all capabilities are supported
165            true
166        }
167    }
168
169    /// Get all capabilities for a pin
170    ///
171    /// # Arguments
172    ///
173    /// * `pin` - The pin number to get capabilities for
174    ///
175    /// # Returns
176    ///
177    /// * `Vec<String>` - List of capabilities supported by the pin
178    pub fn get_pin_capabilities(&self, pin: u32) -> Vec<String> {
179        if let Some(model) = &self.model {
180            model.get_pin_capabilities(pin as u8)
181        } else {
182            // If no model is loaded, return an empty list
183            Vec::new()
184        }
185    }
186
187    /// Validate that a pin can be configured with a specific capability
188    ///
189    /// # Arguments
190    ///
191    /// * `pin` - The pin number to check
192    /// * `capability` - The capability to check for
193    ///
194    /// # Returns
195    ///
196    /// * `Result<()>` - Ok if the capability is valid, an error otherwise
197    pub fn validate_pin_capability(&self, pin: u32, capability: &str) -> Result<()> {
198        if let Some(model) = &self.model {
199            model.validate_pin_capability(pin as u8, capability)
200        } else {
201            // If no model is loaded, assume all capabilities are valid
202            Ok(())
203        }
204    }
205
206    /// Get related capabilities for a specific capability
207    ///
208    /// # Arguments
209    ///
210    /// * `pin` - The pin number with the capability
211    /// * `capability` - The capability to find related capabilities for
212    ///
213    /// # Returns
214    ///
215    /// * `Vec<(String, u8)>` - List of related capabilities and their pin numbers
216    pub fn get_related_capabilities(&self, pin: u32, capability: &str) -> Vec<(String, u8)> {
217        if let Some(model) = &self.model {
218            model.get_related_capabilities(pin as u8, capability)
219        } else {
220            // If no model is loaded, return an empty list
221            Vec::new()
222        }
223    }
224
225    /// Save current configuration to device
226    pub fn save_configuration(&mut self) -> Result<()> {
227        // Based on official PoKeysLib source code:
228        // CreateRequest(device->request, 0x50, 0xAA, 0x55, 0, 0);
229        // Command 0x50 with parameters 0xAA, 0x55, 0, 0
230        self.send_request(0x50, 0xAA, 0x55, 0, 0)?;
231        Ok(())
232    }
233
234    /// Set device name (up to 20 bytes for long device name)
235    pub fn set_device_name(&mut self, name: &str) -> Result<()> {
236        // Based on official documentation:
237        // - byte 2: 0x06
238        // - byte 3: Bit 0 for writing device name (0x01)
239        // - byte 4: use long device name (1)
240        // - byte 5-6: 0
241        // - byte 7: request ID
242        // - bytes 36-55: long device name string (20 bytes)
243
244        // Prepare long device name (20 bytes for long name)
245        let mut name_bytes = [0u8; 20];
246        let name_str = if name.len() > 20 { &name[..20] } else { name };
247        let name_bytes_slice = name_str.as_bytes();
248        name_bytes[..name_bytes_slice.len()].copy_from_slice(name_bytes_slice);
249
250        // Create request manually according to documentation
251        let mut request = [0u8; 64];
252        request[0] = 0xBB; // header
253        request[1] = 0x06; // command (byte 2 in doc)
254        request[2] = 0x01; // bit 0 set for writing device name (byte 3 in doc)
255        request[3] = 0x01; // use long device name (byte 4 in doc)
256        request[4] = 0x00; // byte 5 in doc
257        request[5] = 0x00; // byte 6 in doc
258        request[6] = self.communication.get_next_request_id();
259
260        // Long device name at bytes 36-55 in doc = bytes 35-54 in 0-based array
261        request[35..55].copy_from_slice(&name_bytes);
262
263        // Calculate checksum exactly like official PoKeysLib getChecksum function
264        // Sum bytes 0-6 (not including byte 7 where checksum goes)
265        let mut checksum: u8 = 0;
266        for i in 0..7 {
267            checksum = checksum.wrapping_add(request[i]);
268        }
269        request[7] = checksum;
270
271        // Send the request using the appropriate interface
272        match self.connection_type {
273            DeviceConnectionType::UsbDevice | DeviceConnectionType::FastUsbDevice => {
274                if let Some(ref mut interface) = self.usb_interface {
275                    let mut hid_packet = [0u8; 65];
276                    hid_packet[0] = 0; // Report ID
277                    hid_packet[1..65].copy_from_slice(&request);
278
279                    interface.write(&hid_packet)?;
280
281                    let mut response = [0u8; 65];
282                    interface.read(&mut response)?;
283
284                    Ok(())
285                } else {
286                    Err(PoKeysError::NotConnected)
287                }
288            }
289            DeviceConnectionType::NetworkDevice => {
290                if let Some(ref mut interface) = self.network_interface {
291                    interface.send(&request)?;
292
293                    // Use timeout for network response to avoid hanging
294                    let mut response = [0u8; 64];
295                    match interface
296                        .receive_timeout(&mut response, std::time::Duration::from_millis(2000))
297                    {
298                        Ok(_) => Ok(()),
299                        Err(_) => {
300                            // Don't fail the entire operation if device name setting fails
301                            Ok(())
302                        }
303                    }
304                } else {
305                    Err(PoKeysError::NotConnected)
306                }
307            }
308        }
309    }
310
311    /// Clear device configuration
312    pub fn clear_configuration(&mut self) -> Result<()> {
313        self.send_request(0x02, 0, 0, 0, 0)?;
314        Ok(())
315    }
316
317    /// Send custom request to device
318    pub fn custom_request(
319        &mut self,
320        request_type: u8,
321        param1: u8,
322        param2: u8,
323        param3: u8,
324        param4: u8,
325    ) -> Result<[u8; RESPONSE_BUFFER_SIZE]> {
326        self.send_request(request_type, param1, param2, param3, param4)
327    }
328
329    /// Set ethernet retry count and timeout
330    pub fn set_ethernet_retry_count_and_timeout(
331        &mut self,
332        send_retries: u32,
333        read_retries: u32,
334        timeout_ms: u32,
335    ) {
336        self.communication.set_retries_and_timeout(
337            send_retries,
338            read_retries,
339            Duration::from_millis(timeout_ms as u64),
340        );
341    }
342
343    /// Get connection type
344    pub fn get_connection_type(&self) -> DeviceConnectionType {
345        self.connection_type
346    }
347
348    /// Set I2C configuration
349    pub fn set_i2c_config(&mut self, config: I2cConfig) {
350        self.i2c_config = config;
351    }
352
353    /// Get I2C configuration
354    pub fn get_i2c_config(&self) -> &I2cConfig {
355        &self.i2c_config
356    }
357
358    /// Set validation level
359    pub fn set_validation_level(&mut self, level: ValidationLevel) {
360        self.validation_level = level;
361    }
362
363    /// Get I2C metrics
364    pub fn get_i2c_metrics(&self) -> &I2cMetrics {
365        &self.i2c_metrics
366    }
367
368    /// Reset I2C metrics
369    pub fn reset_i2c_metrics(&mut self) {
370        self.i2c_metrics = I2cMetrics::default();
371    }
372
373    /// Perform device health check
374    pub fn health_check(&mut self) -> HealthStatus {
375        HealthStatus {
376            connectivity: self.test_connectivity(),
377            i2c_health: self.test_i2c_health(),
378            error_rate: self.calculate_error_rate(),
379            performance: self.get_performance_summary(),
380        }
381    }
382
383    /// Test basic connectivity
384    fn test_connectivity(&mut self) -> ConnectivityStatus {
385        match self.get_device_data() {
386            Ok(_) => ConnectivityStatus::Healthy,
387            Err(e) => ConnectivityStatus::Degraded(e.to_string()),
388        }
389    }
390
391    /// Test I2C bus health
392    fn test_i2c_health(&mut self) -> I2cHealthStatus {
393        match self.i2c_get_status() {
394            Ok(I2cStatus::Ok) => I2cHealthStatus::Healthy,
395            Ok(status) => I2cHealthStatus::Degraded(format!("I2C status: {:?}", status)),
396            Err(e) => I2cHealthStatus::Failed(e.to_string()),
397        }
398    }
399
400    /// Calculate current error rate
401    fn calculate_error_rate(&self) -> f64 {
402        if self.i2c_metrics.total_commands == 0 {
403            0.0
404        } else {
405            self.i2c_metrics.failed_commands as f64 / self.i2c_metrics.total_commands as f64
406        }
407    }
408
409    /// Get performance summary
410    fn get_performance_summary(&self) -> PerformanceSummary {
411        let success_rate = if self.i2c_metrics.total_commands == 0 {
412            1.0
413        } else {
414            self.i2c_metrics.successful_commands as f64 / self.i2c_metrics.total_commands as f64
415        };
416
417        PerformanceSummary {
418            avg_response_time_ms: self.i2c_metrics.average_response_time.as_millis() as f64,
419            success_rate,
420            throughput_commands_per_sec: 0.0, // Would need timing data to calculate
421        }
422    }
423
424    /// Validate packet according to current validation level
425    #[allow(dead_code)]
426    fn validate_packet(&self, data: &[u8]) -> Result<()> {
427        match &self.validation_level {
428            ValidationLevel::None => Ok(()),
429            ValidationLevel::Basic => self.validate_basic_structure(data),
430            ValidationLevel::Strict => self.validate_strict_protocol(data),
431            ValidationLevel::Custom(config) => self.validate_custom(data, config),
432        }
433    }
434
435    /// Validate basic packet structure
436    #[allow(dead_code)]
437    fn validate_basic_structure(&self, data: &[u8]) -> Result<()> {
438        if data.len() < 3 {
439            return Err(PoKeysError::InvalidPacketStructure(
440                "Minimum packet size is 3 bytes".to_string(),
441            ));
442        }
443        Ok(())
444    }
445
446    /// Validate strict protocol compliance
447    #[allow(dead_code)]
448    fn validate_strict_protocol(&self, data: &[u8]) -> Result<()> {
449        if data.len() < 3 {
450            return Err(PoKeysError::InvalidPacketStructure(
451                "Minimum packet size is 3 bytes".to_string(),
452            ));
453        }
454
455        let command = data[0];
456        let device_id = data[1];
457        let checksum = data[data.len() - 1];
458
459        // Validate command ID (uSPIBridge command range)
460        if !matches!(command, 0x11..=0x42) {
461            return Err(PoKeysError::InvalidCommand(command));
462        }
463
464        // Validate device ID
465        if device_id >= 16 {
466            // Reasonable max device count
467            return Err(PoKeysError::InvalidDeviceId(device_id));
468        }
469
470        // Validate checksum
471        let calculated_checksum = self.calculate_checksum(&data[..data.len() - 1]);
472        if checksum != calculated_checksum {
473            return Err(PoKeysError::InvalidChecksumDetailed {
474                expected: calculated_checksum,
475                received: checksum,
476            });
477        }
478
479        Ok(())
480    }
481
482    /// Validate with custom configuration
483    #[allow(dead_code)]
484    fn validate_custom(&self, data: &[u8], config: &crate::types::ValidationConfig) -> Result<()> {
485        if config.validate_packet_structure && data.len() < 3 {
486            return Err(PoKeysError::InvalidPacketStructure(
487                "Minimum packet size is 3 bytes".to_string(),
488            ));
489        }
490
491        if data.len() >= 3 {
492            let command = data[0];
493            let device_id = data[1];
494            let checksum = data[data.len() - 1];
495
496            if config.validate_command_ids
497                && !config.valid_commands.is_empty()
498                && !config.valid_commands.contains(&command)
499            {
500                return Err(PoKeysError::InvalidCommand(command));
501            }
502
503            if config.validate_device_ids && device_id > config.max_device_id {
504                return Err(PoKeysError::InvalidDeviceId(device_id));
505            }
506
507            if config.validate_checksums {
508                let calculated_checksum = self.calculate_checksum(&data[..data.len() - 1]);
509                if checksum != calculated_checksum {
510                    return Err(PoKeysError::InvalidChecksumDetailed {
511                        expected: calculated_checksum,
512                        received: checksum,
513                    });
514                }
515            }
516        }
517
518        Ok(())
519    }
520
521    /// Calculate XOR checksum
522    #[allow(dead_code)]
523    fn calculate_checksum(&self, data: &[u8]) -> u8 {
524        data.iter().fold(0, |acc, &byte| acc ^ byte)
525    }
526
527    /// Check if device supports a specific capability
528    pub fn check_pin_capability(&self, pin: u32, capability: crate::io::PinCapability) -> bool {
529        if pin as usize >= self.pins.len() {
530            return false;
531        }
532
533        // Implementation would check device-specific pin capabilities
534        // This is a simplified version
535        match self.device_data.device_type_id {
536            32 => check_pokeys57cnc_pin_capability(pin, capability), // PoKeys57CNC
537            _ => false, // Add other device types as needed
538        }
539    }
540
541    /// Get complete network configuration including discovery info
542    pub fn get_network_configuration(
543        &mut self,
544        timeout_ms: u32,
545    ) -> Result<(Option<NetworkDeviceSummary>, NetworkDeviceInfo)> {
546        // Get discovery info
547        let discovery_info = enumerate_network_devices(timeout_ms)?
548            .into_iter()
549            .find(|d| d.serial_number == self.device_data.serial_number);
550
551        // Get detailed config using 0xBB request
552        let response = self.send_request(0xE0, 0x00, 0x00, 0, 0)?;
553
554        // Parse network configuration from response
555        let config = NetworkDeviceInfo {
556            dhcp: response.get(8).copied().unwrap_or(0),
557            ip_address_setup: [
558                response.get(9).copied().unwrap_or(0),
559                response.get(10).copied().unwrap_or(0),
560                response.get(11).copied().unwrap_or(0),
561                response.get(12).copied().unwrap_or(0),
562            ],
563            ip_address_current: [
564                response.get(13).copied().unwrap_or(0),
565                response.get(14).copied().unwrap_or(0),
566                response.get(15).copied().unwrap_or(0),
567                response.get(16).copied().unwrap_or(0),
568            ],
569            tcp_timeout: u16::from_le_bytes([
570                response.get(17).copied().unwrap_or(0),
571                response.get(18).copied().unwrap_or(0),
572            ])
573            .saturating_mul(100),
574            gateway_ip: [
575                response.get(19).copied().unwrap_or(0),
576                response.get(20).copied().unwrap_or(0),
577                response.get(21).copied().unwrap_or(0),
578                response.get(22).copied().unwrap_or(0),
579            ],
580            subnet_mask: [
581                response.get(23).copied().unwrap_or(0),
582                response.get(24).copied().unwrap_or(0),
583                response.get(25).copied().unwrap_or(0),
584                response.get(26).copied().unwrap_or(0),
585            ],
586            additional_network_options: response.get(27).copied().unwrap_or(0),
587        };
588
589        //print the config
590        Ok((discovery_info, config))
591    }
592
593    // Internal methods
594
595    pub fn send_request(
596        &mut self,
597        request_type: u8,
598        param1: u8,
599        param2: u8,
600        param3: u8,
601        param4: u8,
602    ) -> Result<[u8; RESPONSE_BUFFER_SIZE]> {
603        match self.connection_type {
604            DeviceConnectionType::UsbDevice | DeviceConnectionType::FastUsbDevice => {
605                if let Some(ref mut interface) = self.usb_interface {
606                    self.communication.send_usb_request(
607                        interface,
608                        request_type,
609                        param1,
610                        param2,
611                        param3,
612                        param4,
613                    )
614                } else {
615                    Err(PoKeysError::NotConnected)
616                }
617            }
618            DeviceConnectionType::NetworkDevice => {
619                if let Some(ref mut interface) = self.network_interface {
620                    self.communication.send_network_request(
621                        interface,
622                        request_type,
623                        param1,
624                        param2,
625                        param3,
626                        param4,
627                    )
628                } else {
629                    Err(PoKeysError::NotConnected)
630                }
631            }
632        }
633    }
634
635    /// Send request with data payload
636    pub fn send_request_with_data(
637        &mut self,
638        request_type: u8,
639        param1: u8,
640        param2: u8,
641        param3: u8,
642        param4: u8,
643        data: &[u8],
644    ) -> Result<[u8; RESPONSE_BUFFER_SIZE]> {
645        let request = self.communication.prepare_request_with_data(
646            request_type,
647            param1,
648            param2,
649            param3,
650            param4,
651            Some(data),
652        );
653
654        match self.connection_type {
655            DeviceConnectionType::UsbDevice | DeviceConnectionType::FastUsbDevice => {
656                if let Some(ref mut interface) = self.usb_interface {
657                    self.communication.send_usb_request_raw(interface, &request)
658                } else {
659                    Err(PoKeysError::NotConnected)
660                }
661            }
662            DeviceConnectionType::NetworkDevice => {
663                if let Some(ref mut interface) = self.network_interface {
664                    self.communication
665                        .send_network_request_raw(interface, &request)
666                } else {
667                    Err(PoKeysError::NotConnected)
668                }
669            }
670        }
671    }
672
673    /// Read comprehensive device data using command 0x00
674    /// This provides accurate device information including proper firmware version and device type
675    pub fn read_device_data(&mut self) -> Result<()> {
676        // Send read device data command (byte 2: 0x00, bytes 3-6: 0)
677        let response = self.send_request(0x00, 0, 0, 0, 0)?;
678
679        if response.len() < 64 {
680            return Err(PoKeysError::Protocol(
681                "Read device data response too short".to_string(),
682            ));
683        }
684
685        self.parse_device_data_response(&response)?;
686        Ok(())
687    }
688
689    /// Parse the comprehensive device data response according to PoKeys protocol specification
690    fn parse_device_data_response(&mut self, response: &[u8]) -> Result<()> {
691        // Check for extended device signature (PK58/PKEx) at bytes 9-12 (doc says 9-12, 0-based = 8-11)
692        if response.len() >= 64 && (&response[8..12] == b"PK58" || &response[8..12] == b"PKEx") {
693            // Extended device parsing (based on official documentation)
694
695            // Basic firmware info (bytes 5-6)
696            let software_version_encoded = response[4]; // Byte 5 in doc = byte 4 in 0-based
697            let revision_number = response[5]; // Byte 6 in doc = byte 5 in 0-based
698
699            // Decode software version: v(1+[bits 4-7]).(bits [0-3])
700            let major_bits = (software_version_encoded >> 4) & 0x0F; // Extract bits 4-7
701            let minor_bits = software_version_encoded & 0x0F; // Extract bits 0-3
702            let decoded_major = 1 + major_bits;
703            let decoded_minor = minor_bits;
704
705            // 32-bit serial number (bytes 13-16 in doc = bytes 12-15 in 0-based)
706            let serial_32bit =
707                u32::from_le_bytes([response[12], response[13], response[14], response[15]]);
708
709            // Extended firmware info (bytes 17-18 in doc = bytes 16-17 in 0-based)
710            let firmware_version = response[16];
711            let firmware_revision = response[17];
712
713            // Hardware ID (byte 19 in doc = byte 18 in 0-based)
714            let hw_id = response[18];
715
716            // User ID (byte 20 in doc = byte 19 in 0-based)
717            let user_id = response[19];
718
719            // Build date (bytes 21-31 in doc = bytes 20-30 in 0-based)
720            let build_date_bytes = &response[20..31];
721
722            // Device name (bytes 32-41 in doc = bytes 31-40 in 0-based)
723            let device_name_bytes = &response[31..41];
724
725            // Firmware type (byte 42 in doc = byte 41 in 0-based)
726            let firmware_type = response[41];
727
728            // Application firmware version (bytes 43-44 in doc = bytes 42-43 in 0-based)
729            let _app_firmware_major = response[42];
730            let _app_firmware_minor = response[43];
731
732            // Product ID offset (byte 58 in doc = byte 57 in 0-based)
733            let product_id = response[57];
734
735            // Update device data structure
736            self.device_data.serial_number = serial_32bit;
737
738            // Use the decoded firmware version for display
739            self.device_data.firmware_version_major = decoded_major;
740            self.device_data.firmware_version_minor = decoded_minor;
741            self.device_data.firmware_revision = revision_number;
742
743            // Store the extended firmware info in secondary fields
744            self.device_data.secondary_firmware_version_major = firmware_version;
745            self.device_data.secondary_firmware_version_minor = firmware_revision;
746
747            self.device_data.hw_type = hw_id;
748            self.device_data.user_id = user_id;
749            self.device_data.fw_type = firmware_type;
750            self.device_data.product_id = product_id;
751
752            // Store device name (copy raw bytes)
753            self.device_data.device_name.fill(0);
754            let name_len =
755                std::cmp::min(device_name_bytes.len(), self.device_data.device_name.len());
756            self.device_data.device_name[..name_len]
757                .copy_from_slice(&device_name_bytes[..name_len]);
758
759            // Store build date (copy raw bytes)
760            self.device_data.build_date.fill(0);
761            let date_len = std::cmp::min(build_date_bytes.len(), self.device_data.build_date.len());
762            self.device_data.build_date[..date_len].copy_from_slice(&build_date_bytes[..date_len]);
763
764            log::debug!("Extended device info parsed:");
765            log::debug!("  Serial: {}", serial_32bit);
766            log::debug!(
767                "  Decoded firmware: {}.{}.{}",
768                decoded_major,
769                decoded_minor,
770                revision_number
771            );
772            log::debug!(
773                "  Raw software version byte: 0x{:02X}",
774                software_version_encoded
775            );
776            log::debug!(
777                "  Extended firmware: {}.{}",
778                firmware_version,
779                firmware_revision
780            );
781            log::debug!("  Hardware type: {}", hw_id);
782        } else {
783            // Legacy device parsing
784
785            // Serial number (bytes 3-4 in doc = bytes 2-3 in 0-based)
786            let serial_16bit = ((response[2] as u32) << 8) | (response[3] as u32);
787
788            // Software version (byte 5 in doc = byte 4 in 0-based)
789            let software_version_encoded = response[4];
790            let revision_number = response[5];
791
792            // Decode software version: v(1+[bits 4-7]).(bits [0-3])
793            let major_bits = (software_version_encoded >> 4) & 0x0F;
794            let minor_bits = software_version_encoded & 0x0F;
795            let decoded_major = 1 + major_bits;
796            let decoded_minor = minor_bits;
797
798            self.device_data.serial_number = serial_16bit;
799            self.device_data.firmware_version_major = decoded_major;
800            self.device_data.firmware_version_minor = decoded_minor;
801            self.device_data.firmware_revision = revision_number;
802            self.device_data.hw_type = 0; // Unknown for legacy
803            self.device_data.device_name.fill(0);
804            self.device_data.build_date.fill(0);
805
806            log::debug!("Legacy device info parsed:");
807            log::debug!("  Serial: {}", serial_16bit);
808            log::debug!(
809                "  Decoded firmware: {}.{}.{}",
810                decoded_major,
811                decoded_minor,
812                revision_number
813            );
814        }
815
816        Ok(())
817    }
818
819    fn initialize_device_structures(&mut self) -> Result<()> {
820        // Initialize device-specific structures based on device type
821        match self.device_data.device_type_id {
822            32 => self.initialize_pokeys57cnc(), // PoKeys57CNC
823            _ => self.initialize_generic_device(),
824        }
825    }
826
827    fn initialize_pokeys57cnc(&mut self) -> Result<()> {
828        // PoKeys57CNC specific initialization
829        self.info.pin_count = 55;
830        self.info.pwm_count = 6;
831        self.info.encoders_count = 25;
832        self.info.fast_encoders = 3;
833        self.info.ultra_fast_encoders = 1;
834        self.info.analog_inputs = 8;
835        self.info.pulse_engine_v2 = 1;
836
837        // Initialize pin array
838        self.pins = vec![PinData::new(); self.info.pin_count as usize];
839        self.encoders = vec![EncoderData::new(); self.info.encoders_count as usize];
840        // PWM data is already initialized in new()
841        self.po_ext_bus_data = vec![0; 10]; // PoKeys57CNC has 10 PoExtBus outputs
842
843        Ok(())
844    }
845
846    fn initialize_generic_device(&mut self) -> Result<()> {
847        // Generic device initialization
848        self.info.pin_count = 55; // Default
849        self.pins = vec![PinData::new(); self.info.pin_count as usize];
850        self.encoders = vec![EncoderData::new(); 25]; // Default encoder count
851        Ok(())
852    }
853
854    // LED Matrix Protocol Implementation
855
856    /// Command 0xD5: Get/Set Matrix LED Configuration
857    pub fn configure_led_matrix(
858        &mut self,
859        config: &crate::matrix::MatrixLedProtocolConfig,
860    ) -> Result<()> {
861        let enabled_flags = self.encode_matrix_enabled(config);
862        let display1_size = self.encode_display_size(
863            config.display1_characters,
864            crate::matrix::SEVEN_SEGMENT_COLUMNS,
865        );
866        let display2_size = self.encode_display_size(
867            config.display2_characters,
868            crate::matrix::SEVEN_SEGMENT_COLUMNS,
869        );
870
871        let _response =
872            self.send_request(0xD5, 0x00, enabled_flags, display1_size, display2_size)?;
873        Ok(())
874    }
875
876    /// Read Matrix LED Configuration
877    pub fn read_led_matrix_config(&mut self) -> Result<crate::matrix::MatrixLedProtocolConfig> {
878        // Read the configuration using command 0xD5 with parameter 0x01
879        let response = self.send_request(0xD5, 0x01, 0, 0, 0)?;
880
881        // Parse the response
882        let enabled_flags = response[2];
883        let display1_size = response[3];
884        let display2_size = response[4];
885
886        // Decode enabled flags
887        let display1_enabled = (enabled_flags & 0x01) != 0;
888        let display2_enabled = (enabled_flags & 0x02) != 0;
889
890        // Decode character counts (lower 4 bits)
891        // Note: The protocol response has display parameters swapped
892        let display1_characters = display2_size & 0x0F; // Use display2_size for display1
893        let display2_characters = display1_size & 0x0F; // Use display1_size for display2
894
895        Ok(crate::matrix::MatrixLedProtocolConfig {
896            display1_enabled,
897            display2_enabled,
898            display1_characters,
899            display2_characters,
900        })
901    }
902
903    /// Command 0xD6: Update Matrix Display
904    pub fn update_led_matrix(
905        &mut self,
906        matrix_id: u8,
907        action: crate::matrix::MatrixAction,
908        row: u8,
909        column: u8,
910        data: &[u8],
911    ) -> Result<()> {
912        let action_code = match (matrix_id, action) {
913            (1, crate::matrix::MatrixAction::UpdateWhole) => 1,
914            (1, crate::matrix::MatrixAction::SetPixel) => 5,
915            (1, crate::matrix::MatrixAction::ClearPixel) => 6,
916            (2, crate::matrix::MatrixAction::UpdateWhole) => 11,
917            (2, crate::matrix::MatrixAction::SetPixel) => 15,
918            (2, crate::matrix::MatrixAction::ClearPixel) => 16,
919            _ => {
920                return Err(PoKeysError::Parameter(format!(
921                    "Invalid matrix ID: {}",
922                    matrix_id
923                )));
924            }
925        };
926
927        // Prepare the request with data payload
928        let request = self.communication.prepare_request_with_data(
929            0xD6,
930            action_code,
931            row,
932            column,
933            0,
934            Some(data),
935        );
936
937        // Send the full request
938        match self.connection_type {
939            DeviceConnectionType::NetworkDevice => {
940                if let Some(ref mut interface) = self.network_interface {
941                    let request_id = request[6];
942
943                    // Send request
944                    interface.send(&request[..64])?;
945
946                    // Receive response
947                    let mut response = [0u8; RESPONSE_BUFFER_SIZE];
948                    interface.receive(&mut response)?;
949
950                    // Validate response
951                    self.communication
952                        .validate_response(&response, request_id)?;
953                }
954            }
955            _ => {
956                return Err(PoKeysError::NotSupported);
957            }
958        }
959
960        Ok(())
961    }
962
963    /// Command 0xCA: Configure Matrix Keyboard
964    ///
965    /// Configures a matrix keyboard using the official PoKeys protocol.
966    /// Uses command 0xCA with option 16 for configuration.
967    ///
968    /// # Arguments
969    /// * `width` - Number of columns (1-8)
970    /// * `height` - Number of rows (1-16)
971    /// * `column_pins` - Pin numbers for columns (1-based)
972    /// * `row_pins` - Pin numbers for rows (1-based)
973    ///
974    /// # Protocol Details
975    /// - Pins are converted to 0-based indexing for the device
976    /// - Matrix uses 8-column internal layout regardless of configured width
977    /// - Supports extended row pins for matrices with height > 8
978    pub fn configure_matrix_keyboard(
979        &mut self,
980        width: u8,
981        height: u8,
982        column_pins: &[u8],
983        row_pins: &[u8],
984    ) -> Result<()> {
985        if width > 8 || height > 16 {
986            return Err(PoKeysError::Parameter("Matrix size too large".to_string()));
987        }
988
989        // Prepare configuration data
990        let mut data = [0u8; 55];
991        data[0] = 1; // Enable matrix keyboard (bit 0)
992        data[1] = ((width - 1) << 4) | (height - 1); // Size: width-1 in upper 4 bits, height-1 in lower 4 bits
993
994        // Row pins (bytes 2-9, 0-based indexing)
995        for (i, &pin) in row_pins.iter().enumerate().take(8) {
996            data[2 + i] = if pin > 0 { pin - 1 } else { 0 };
997        }
998
999        // Column pins (bytes 10-17, 0-based indexing)
1000        for (i, &pin) in column_pins.iter().enumerate().take(8) {
1001            data[10 + i] = if pin > 0 { pin - 1 } else { 0 };
1002        }
1003
1004        // Extended row pins for height > 8 (bytes 34-41)
1005        if height > 8 {
1006            for (i, &pin) in row_pins.iter().enumerate().skip(8).take(8) {
1007                data[34 + i] = if pin > 0 { pin - 1 } else { 0 };
1008            }
1009        }
1010
1011        // Send configuration
1012        self.send_request_with_data(0xCA, 16, 0, 0, 0, &data)?;
1013
1014        // Update local state
1015        self.matrix_keyboard.configuration = 1;
1016        self.matrix_keyboard.width = width;
1017        self.matrix_keyboard.height = height;
1018
1019        // Copy pin assignments
1020        for (i, &pin) in column_pins.iter().enumerate().take(8) {
1021            self.matrix_keyboard.column_pins[i] = pin;
1022        }
1023        for (i, &pin) in row_pins.iter().enumerate().take(16) {
1024            self.matrix_keyboard.row_pins[i] = pin;
1025        }
1026
1027        Ok(())
1028    }
1029
1030    /// Command 0xCA: Read Matrix Keyboard State
1031    ///
1032    /// Reads the current state of all keys in the matrix keyboard.
1033    /// Uses command 0xCA with option 20 to read the 16x8 matrix status.
1034    ///
1035    /// # Protocol Details
1036    /// - Returns 16 bytes representing the full 16x8 matrix
1037    /// - Each byte represents 8 keys in a row (bit 0 = column 0, bit 7 = column 7)
1038    /// - Key indexing: row * 8 + column (e.g., key at row 1, col 2 = index 10)
1039    /// - Updates the internal key_values array with current state
1040    pub fn read_matrix_keyboard(&mut self) -> Result<()> {
1041        let response = self.send_request(0xCA, 20, 0, 0, 0)?;
1042
1043        // Parse keyboard state from response (bytes 8-23 contain 16x8 matrix)
1044        let data_start = 8;
1045
1046        if response.len() >= data_start + 16 {
1047            // Clear existing state
1048            self.matrix_keyboard.key_values.fill(0);
1049
1050            // Copy matrix state (16 bytes for 16x8 matrix)
1051            // Each byte represents 8 keys in a row
1052            for (row, &byte_val) in response[data_start..data_start + 16].iter().enumerate() {
1053                for col in 0..8 {
1054                    let key_index = row * 8 + col;
1055                    if key_index < 128 {
1056                        self.matrix_keyboard.key_values[key_index] =
1057                            if (byte_val & (1 << col)) != 0 { 1 } else { 0 };
1058                    }
1059                }
1060            }
1061        }
1062
1063        Ok(())
1064    }
1065
1066    /// Helper to encode display size (characters as rows, always 8 columns for 7-segment)
1067    fn encode_display_size(&self, characters: u8, columns: u8) -> u8 {
1068        // bits 0-3: number of rows (characters)
1069        // bits 4-7: number of columns (always 8 for 7-segment)
1070        (characters & 0x0F) | ((columns & 0x0F) << 4)
1071    }
1072
1073    /// Helper to encode matrix enabled flags
1074    fn encode_matrix_enabled(&self, config: &crate::matrix::MatrixLedProtocolConfig) -> u8 {
1075        let mut enabled = 0u8;
1076        if config.display1_enabled {
1077            enabled |= 1 << 0;
1078        }
1079        if config.display2_enabled {
1080            enabled |= 1 << 1;
1081        }
1082        enabled
1083    }
1084
1085    /// Configure multiple LED matrices with device model validation
1086    ///
1087    /// This method validates all configurations against the device model first,
1088    /// then applies the configurations and reserves the necessary pins.
1089    ///
1090    /// # Arguments
1091    ///
1092    /// * `configs` - Array of LED matrix configurations
1093    ///
1094    /// # Returns
1095    ///
1096    /// * `Result<()>` - Ok if all configurations were applied successfully
1097    pub fn configure_led_matrices(
1098        &mut self,
1099        configs: &[crate::matrix::LedMatrixConfig],
1100    ) -> Result<()> {
1101        // Basic validation (always performed)
1102        for config in configs {
1103            // Validate matrix ID
1104            if config.matrix_id < 1 || config.matrix_id > 2 {
1105                return Err(PoKeysError::InvalidConfiguration(
1106                    "Matrix ID must be 1 or 2".to_string(),
1107                ));
1108            }
1109
1110            // Validate character count (1-8 characters for 7-segment displays)
1111            if config.characters < 1 || config.characters > 8 {
1112                return Err(PoKeysError::InvalidConfiguration(
1113                    "Character count must be between 1 and 8 for 7-segment displays".to_string(),
1114                ));
1115            }
1116        }
1117
1118        // Device model validation (if available)
1119        for config in configs {
1120            if let Some(model) = &self.model {
1121                model.validate_led_matrix_config(config)?;
1122            }
1123        }
1124
1125        // Apply configurations
1126        let mut protocol_config = crate::matrix::MatrixLedProtocolConfig {
1127            display1_enabled: false,
1128            display2_enabled: false,
1129            display1_characters: 1,
1130            display2_characters: 1,
1131        };
1132
1133        for config in configs {
1134            match config.matrix_id {
1135                1 => {
1136                    protocol_config.display1_enabled = config.enabled;
1137                    protocol_config.display1_characters = config.characters;
1138                }
1139                2 => {
1140                    protocol_config.display2_enabled = config.enabled;
1141                    protocol_config.display2_characters = config.characters;
1142                }
1143                _ => {
1144                    return Err(PoKeysError::InvalidConfiguration(
1145                        "Invalid matrix ID".to_string(),
1146                    ));
1147                }
1148            }
1149        }
1150
1151        self.configure_led_matrix(&protocol_config)?;
1152
1153        // Reserve pins in model
1154        if let Some(model) = &mut self.model {
1155            for config in configs {
1156                if config.enabled {
1157                    model.reserve_led_matrix_pins(config.matrix_id)?;
1158                }
1159            }
1160        }
1161
1162        Ok(())
1163    }
1164
1165    /// Configure and control a servo motor
1166    pub fn configure_servo(&mut self, config: crate::pwm::ServoConfig) -> Result<()> {
1167        // Ensure PWM period is set for servo control (20ms = 500,000 cycles)
1168        if self.pwm.pwm_period == 0 {
1169            self.set_pwm_period(500000)?; // 20ms period for servo control
1170        }
1171
1172        // Enable PWM for the servo pin
1173        self.enable_pwm_for_pin(config.pin, true)?;
1174
1175        Ok(())
1176    }
1177
1178    /// Set servo angle (for position servos)
1179    pub fn set_servo_angle(&mut self, config: &crate::pwm::ServoConfig, angle: f32) -> Result<()> {
1180        config.set_angle(self, angle)
1181    }
1182
1183    /// Set servo speed (for speed servos)
1184    pub fn set_servo_speed(&mut self, config: &crate::pwm::ServoConfig, speed: f32) -> Result<()> {
1185        config.set_speed(self, speed)
1186    }
1187
1188    /// Stop servo (for speed servos)
1189    pub fn stop_servo(&mut self, config: &crate::pwm::ServoConfig) -> Result<()> {
1190        config.stop(self)
1191    }
1192}
1193
1194// Default implementations for device structures
1195impl Default for DeviceInfo {
1196    fn default() -> Self {
1197        Self {
1198            pin_count: 0,
1199            pwm_count: 0,
1200            basic_encoder_count: 0,
1201            encoders_count: 0,
1202            fast_encoders: 0,
1203            ultra_fast_encoders: 0,
1204            pwm_internal_frequency: 0,
1205            analog_inputs: 0,
1206            key_mapping: 0,
1207            triggered_key_mapping: 0,
1208            key_repeat_delay: 0,
1209            digital_counters: 0,
1210            joystick_button_axis_mapping: 0,
1211            joystick_analog_to_digital_mapping: 0,
1212            macros: 0,
1213            matrix_keyboard: 0,
1214            matrix_keyboard_triggered_mapping: 0,
1215            lcd: 0,
1216            matrix_led: 0,
1217            connection_signal: 0,
1218            po_ext_bus: 0,
1219            po_net: 0,
1220            analog_filtering: 0,
1221            init_outputs_start: 0,
1222            prot_i2c: 0,
1223            prot_1wire: 0,
1224            additional_options: 0,
1225            load_status: 0,
1226            custom_device_name: 0,
1227            po_tlog27_support: 0,
1228            sensor_list: 0,
1229            web_interface: 0,
1230            fail_safe_settings: 0,
1231            joystick_hat_switch: 0,
1232            pulse_engine: 0,
1233            pulse_engine_v2: 0,
1234            easy_sensors: 0,
1235        }
1236    }
1237}
1238
1239impl Default for RealTimeClock {
1240    fn default() -> Self {
1241        Self {
1242            sec: 0,
1243            min: 0,
1244            hour: 0,
1245            dow: 0,
1246            dom: 0,
1247            tmp: 0,
1248            doy: 0,
1249            month: 0,
1250            year: 0,
1251        }
1252    }
1253}
1254
1255// Device capability checking functions
1256fn check_pokeys57cnc_pin_capability(pin: u32, capability: crate::io::PinCapability) -> bool {
1257    use crate::io::PinCapability;
1258
1259    match pin {
1260        1..=2 => matches!(
1261            capability,
1262            PinCapability::DigitalInput
1263                | PinCapability::DigitalOutput
1264                | PinCapability::DigitalCounter
1265                | PinCapability::FastEncoder1A
1266        ),
1267        3..=6 => matches!(
1268            capability,
1269            PinCapability::DigitalInput
1270                | PinCapability::DigitalCounter
1271                | PinCapability::FastEncoder1A
1272        ),
1273        8 | 12..=13 => matches!(
1274            capability,
1275            PinCapability::DigitalInput | PinCapability::DigitalOutput
1276        ),
1277        9..=11 | 15..=16 => matches!(
1278            capability,
1279            PinCapability::DigitalInput | PinCapability::DigitalCounter
1280        ),
1281        14 => matches!(capability, PinCapability::DigitalInput),
1282        17..=18 | 20..=21 => matches!(
1283            capability,
1284            PinCapability::DigitalOutput | PinCapability::PwmOutput
1285        ),
1286        19 => matches!(
1287            capability,
1288            PinCapability::DigitalInput | PinCapability::DigitalCounter
1289        ),
1290        _ => false,
1291    }
1292}
1293
1294// Global device enumeration and connection functions
1295
1296static USB_DEVICE_LIST: LazyLock<Mutex<Vec<UsbDeviceInfo>>> =
1297    LazyLock::new(|| Mutex::new(Vec::new()));
1298#[allow(dead_code)]
1299static NETWORK_DEVICE_LIST: LazyLock<Mutex<Vec<NetworkDeviceSummary>>> =
1300    LazyLock::new(|| Mutex::new(Vec::new()));
1301
1302#[derive(Debug, Clone)]
1303#[allow(dead_code)]
1304struct UsbDeviceInfo {
1305    vendor_id: u16,
1306    product_id: u16,
1307    serial_number: Option<String>,
1308    path: String,
1309}
1310
1311/// Enumerate USB PoKeys devices
1312pub fn enumerate_usb_devices() -> Result<i32> {
1313    let mut device_list = USB_DEVICE_LIST
1314        .lock()
1315        .map_err(|_| PoKeysError::InternalError("Failed to lock USB device list".to_string()))?;
1316    device_list.clear();
1317
1318    // Platform-specific USB enumeration
1319    #[cfg(target_os = "macos")]
1320    {
1321        enumerate_usb_devices_macos(&mut device_list)
1322    }
1323    #[cfg(target_os = "linux")]
1324    {
1325        enumerate_usb_devices_linux(&mut device_list)
1326    }
1327    #[cfg(target_os = "windows")]
1328    {
1329        enumerate_usb_devices_windows(&mut device_list)
1330    }
1331    #[cfg(not(any(target_os = "macos", target_os = "linux", target_os = "windows")))]
1332    {
1333        Err(PoKeysError::NotSupported)
1334    }
1335}
1336
1337/// Enumerate network PoKeys devices
1338pub fn enumerate_network_devices(timeout_ms: u32) -> Result<Vec<NetworkDeviceSummary>> {
1339    use crate::network::discover_all_devices;
1340
1341    log::info!("Enumerating network devices with timeout {timeout_ms}ms");
1342
1343    // Use the network discovery implementation
1344    discover_all_devices(timeout_ms)
1345}
1346
1347/// Connect to USB device by index
1348pub fn connect_to_device(device_index: u32) -> Result<PoKeysDevice> {
1349    let device_list = USB_DEVICE_LIST
1350        .lock()
1351        .map_err(|_| PoKeysError::InternalError("Failed to lock USB device list".to_string()))?;
1352
1353    if device_index as usize >= device_list.len() {
1354        return Err(PoKeysError::Parameter("Invalid device index".to_string()));
1355    }
1356
1357    let device_info = device_list[device_index as usize].clone();
1358    drop(device_list); // Release the lock early
1359
1360    // Create device instance
1361    let mut device = PoKeysDevice::new(DeviceConnectionType::UsbDevice);
1362
1363    // Platform-specific USB connection
1364    #[cfg(target_os = "macos")]
1365    {
1366        device.usb_interface = Some(Box::new(connect_usb_device_macos(&device_info.path)?));
1367    }
1368    #[cfg(target_os = "linux")]
1369    {
1370        device.usb_interface = Some(Box::new(connect_usb_device_linux(&device_info.path)?));
1371    }
1372    #[cfg(target_os = "windows")]
1373    {
1374        device.usb_interface = Some(Box::new(connect_usb_device_windows(&device_info.path)?));
1375    }
1376
1377    // Get device data
1378    device.get_device_data()?;
1379
1380    Ok(device)
1381}
1382
1383/// Connect to device by serial number
1384pub fn connect_to_device_with_serial(
1385    serial_number: u32,
1386    check_network: bool,
1387    timeout_ms: u32,
1388) -> Result<PoKeysDevice> {
1389    if check_network {
1390        let network_devices = enumerate_network_devices(timeout_ms)?;
1391        for network_device in network_devices {
1392            if network_device.serial_number == serial_number {
1393                return connect_to_network_device(&network_device);
1394            }
1395        }
1396    }
1397
1398    // First try USB devices
1399    let usb_count = enumerate_usb_devices()?;
1400
1401    for i in 0..usb_count {
1402        if let Ok(device) = connect_to_device(i as u32) {
1403            if device.device_data.serial_number == serial_number {
1404                return Ok(device);
1405            }
1406        }
1407    }
1408
1409    Err(PoKeysError::CannotConnect)
1410}
1411
1412/// Connect to network device
1413pub fn connect_to_network_device(device_summary: &NetworkDeviceSummary) -> Result<PoKeysDevice> {
1414    let mut device = PoKeysDevice::new(DeviceConnectionType::NetworkDevice);
1415
1416    // Create network interface based on device settings
1417    if device_summary.use_udp != 0 {
1418        device.connection_param = ConnectionParam::Udp;
1419    } else {
1420        device.connection_param = ConnectionParam::Tcp;
1421    }
1422
1423    // Platform-specific network connection
1424    #[cfg(any(target_os = "macos", target_os = "linux", target_os = "windows"))]
1425    {
1426        device.network_interface = Some(connect_network_device(device_summary)?);
1427    }
1428
1429    // Get device data
1430    device.get_device_data()?;
1431
1432    Ok(device)
1433}
1434
1435// Stub implementations for compilation
1436struct StubUsbInterface;
1437
1438impl UsbHidInterface for StubUsbInterface {
1439    fn write(&mut self, _data: &[u8]) -> Result<usize> {
1440        Err(PoKeysError::NotSupported)
1441    }
1442
1443    fn read(&mut self, _buffer: &mut [u8]) -> Result<usize> {
1444        Err(PoKeysError::NotSupported)
1445    }
1446
1447    fn read_timeout(&mut self, _buffer: &mut [u8], _timeout: Duration) -> Result<usize> {
1448        Err(PoKeysError::NotSupported)
1449    }
1450}
1451
1452#[cfg(target_os = "macos")]
1453fn enumerate_usb_devices_macos(device_list: &mut Vec<UsbDeviceInfo>) -> Result<i32> {
1454    use std::process::Command;
1455
1456    // Use system_profiler to get USB device information
1457    let output = Command::new("system_profiler")
1458        .args(["SPUSBDataType", "-xml"])
1459        .output()
1460        .map_err(|e| PoKeysError::InternalError(format!("Failed to run system_profiler: {}", e)))?;
1461
1462    if !output.status.success() {
1463        return Err(PoKeysError::InternalError(
1464            "system_profiler command failed".to_string(),
1465        ));
1466    }
1467
1468    let output_str = String::from_utf8_lossy(&output.stdout);
1469
1470    // Look for PoKeys devices (vendor ID 0x1DC3)
1471    // This is a simple text-based search since we don't want to add XML parsing dependencies
1472    let lines: Vec<&str> = output_str.lines().collect();
1473    let mut i = 0;
1474
1475    while i < lines.len() {
1476        let line = lines[i].trim();
1477
1478        // Look for vendor_id entries
1479        if line.contains("<key>vendor_id</key>") && i + 1 < lines.len() {
1480            let next_line = lines[i + 1].trim();
1481
1482            // Check if it's a PoKeys device (vendor ID 0x1DC3 = 7619 decimal)
1483            if next_line.contains("<integer>7619</integer>")
1484                || next_line.contains("<string>0x1dc3</string>")
1485            {
1486                // Found a PoKeys device, now look for product_id and serial
1487                let mut product_id = 0u16;
1488                let mut serial_number = None;
1489                let mut location_id = String::new();
1490
1491                // Search forward for product_id and serial_number
1492                for j in (i + 2)..(i + 50).min(lines.len()) {
1493                    let search_line = lines[j].trim();
1494
1495                    if search_line.contains("<key>product_id</key>") && j + 1 < lines.len() {
1496                        let product_line = lines[j + 1].trim();
1497                        if let Some(start) = product_line.find("<integer>") {
1498                            if let Some(end) = product_line.find("</integer>") {
1499                                if let Ok(pid) = product_line[start + 9..end].parse::<u16>() {
1500                                    product_id = pid;
1501                                }
1502                            }
1503                        }
1504                    }
1505
1506                    if search_line.contains("<key>serial_num</key>") && j + 1 < lines.len() {
1507                        let serial_line = lines[j + 1].trim();
1508                        if let Some(start) = serial_line.find("<string>") {
1509                            if let Some(end) = serial_line.find("</string>") {
1510                                serial_number = Some(serial_line[start + 8..end].to_string());
1511                            }
1512                        }
1513                    }
1514
1515                    if search_line.contains("<key>location_id</key>") && j + 1 < lines.len() {
1516                        let location_line = lines[j + 1].trim();
1517                        if let Some(start) = location_line.find("<string>") {
1518                            if let Some(end) = location_line.find("</string>") {
1519                                location_id = location_line[start + 8..end].to_string();
1520                            }
1521                        }
1522                    }
1523
1524                    // Stop searching when we hit the next device or end of this device
1525                    if search_line.contains("<key>vendor_id</key>") && j > i + 2 {
1526                        break;
1527                    }
1528                }
1529
1530                // Add the device to our list
1531                device_list.push(UsbDeviceInfo {
1532                    vendor_id: 0x1DC3,
1533                    product_id,
1534                    serial_number,
1535                    path: format!("macos_usb_{}", location_id),
1536                });
1537            }
1538        }
1539        i += 1;
1540    }
1541
1542    Ok(device_list.len() as i32)
1543}
1544
1545#[cfg(target_os = "linux")]
1546fn enumerate_usb_devices_linux(device_list: &mut Vec<UsbDeviceInfo>) -> Result<i32> {
1547    // Linux-specific USB enumeration using libudev
1548    // This is a placeholder implementation
1549    // When implemented, add devices to device_list
1550    Ok(device_list.len() as i32)
1551}
1552
1553#[cfg(target_os = "windows")]
1554fn enumerate_usb_devices_windows(device_list: &mut Vec<UsbDeviceInfo>) -> Result<i32> {
1555    // Windows-specific USB enumeration using WinAPI
1556    // This is a placeholder implementation
1557    // When implemented, add devices to device_list
1558    Ok(device_list.len() as i32)
1559}
1560
1561// Network connection function
1562#[cfg(any(target_os = "macos", target_os = "linux", target_os = "windows"))]
1563fn connect_network_device(
1564    device_summary: &NetworkDeviceSummary,
1565) -> Result<Box<dyn NetworkInterface>> {
1566    use crate::network::{TcpNetworkInterface, UdpNetworkInterface};
1567
1568    if device_summary.use_udp != 0 {
1569        // Use UDP connection
1570        let interface = UdpNetworkInterface::new(device_summary.ip_address, 20055)?;
1571        Ok(Box::new(interface))
1572    } else {
1573        // Use TCP connection
1574        let interface = TcpNetworkInterface::new(device_summary.ip_address, 20055)?;
1575        Ok(Box::new(interface))
1576    }
1577}
1578
1579// Placeholder USB connection functions
1580#[cfg(target_os = "macos")]
1581fn connect_usb_device_macos(_path: &str) -> Result<StubUsbInterface> {
1582    Err(PoKeysError::NotSupported)
1583}
1584
1585#[cfg(target_os = "linux")]
1586fn connect_usb_device_linux(_path: &str) -> Result<StubUsbInterface> {
1587    Err(PoKeysError::NotSupported)
1588}
1589
1590#[cfg(target_os = "windows")]
1591fn connect_usb_device_windows(_path: &str) -> Result<StubUsbInterface> {
1592    Err(PoKeysError::NotSupported)
1593}
1594
1595#[cfg(test)]
1596mod tests {
1597    use super::*;
1598
1599    #[test]
1600    fn test_device_creation() {
1601        let device = PoKeysDevice::new(DeviceConnectionType::UsbDevice);
1602        assert_eq!(device.connection_type, DeviceConnectionType::UsbDevice);
1603        assert_eq!(device.pins.len(), 0); // Not initialized yet
1604    }
1605
1606    #[test]
1607    fn test_pin_capability_checking() {
1608        // Test PoKeys57CNC pin capabilities
1609        assert!(check_pokeys57cnc_pin_capability(
1610            1,
1611            crate::io::PinCapability::DigitalInput
1612        ));
1613        assert!(check_pokeys57cnc_pin_capability(
1614            1,
1615            crate::io::PinCapability::DigitalOutput
1616        ));
1617        assert!(!check_pokeys57cnc_pin_capability(
1618            14,
1619            crate::io::PinCapability::DigitalOutput
1620        )); // Input only
1621        assert!(!check_pokeys57cnc_pin_capability(
1622            100,
1623            crate::io::PinCapability::DigitalInput
1624        )); // Invalid pin
1625    }
1626
1627    // Hardware tests - only run when hardware-tests feature is enabled
1628    #[cfg(feature = "hardware-tests")]
1629    mod hardware_tests {
1630        use super::*;
1631        use std::thread;
1632        use std::time::Duration;
1633
1634        #[test]
1635        fn test_hardware_device_enumeration() {
1636            println!("Testing hardware device enumeration...");
1637
1638            match enumerate_usb_devices() {
1639                Ok(count) => {
1640                    println!("Found {} USB PoKeys devices", count);
1641                    if count == 0 {
1642                        println!(
1643                            "WARNING: No PoKeys devices found. Connect a device to run hardware tests."
1644                        );
1645                    }
1646                }
1647                Err(e) => {
1648                    panic!("Failed to enumerate USB devices: {}", e);
1649                }
1650            }
1651        }
1652
1653        #[test]
1654        fn test_hardware_device_connection() {
1655            println!("Testing hardware device connection...");
1656
1657            let device_count = enumerate_usb_devices().expect("Failed to enumerate devices");
1658
1659            if device_count == 0 {
1660                println!("SKIP: No PoKeys devices found for hardware test");
1661                return;
1662            }
1663
1664            match connect_to_device(0) {
1665                Ok(mut device) => {
1666                    println!("Successfully connected to device");
1667
1668                    // Test getting device data
1669                    match device.get_device_data() {
1670                        Ok(_) => {
1671                            println!("Device Serial: {}", device.device_data.serial_number);
1672                            println!(
1673                                "Firmware: {}.{}",
1674                                device.device_data.firmware_version_major,
1675                                device.device_data.firmware_version_minor
1676                            );
1677                            println!("Pin Count: {}", device.info.pin_count);
1678                        }
1679                        Err(e) => {
1680                            println!("WARNING: Could not get device data: {}", e);
1681                        }
1682                    }
1683                }
1684                Err(e) => {
1685                    panic!("Failed to connect to device: {}", e);
1686                }
1687            }
1688        }
1689
1690        #[test]
1691        fn test_hardware_digital_io() {
1692            println!("Testing hardware digital I/O...");
1693
1694            let device_count = enumerate_usb_devices().expect("Failed to enumerate devices");
1695            if device_count == 0 {
1696                println!("SKIP: No PoKeys devices found for hardware test");
1697                return;
1698            }
1699
1700            let mut device = connect_to_device(0).expect("Failed to connect to device");
1701            device.get_device_data().expect("Failed to get device data");
1702
1703            // Test setting pin as digital output
1704            match device.set_pin_function(1, crate::io::PinFunction::DigitalOutput) {
1705                Ok(_) => {
1706                    println!("Successfully configured pin 1 as digital output");
1707
1708                    // Test setting output high and low
1709                    for state in [true, false, true, false] {
1710                        match device.set_digital_output(1, state) {
1711                            Ok(_) => {
1712                                println!("Set pin 1 to {}", if state { "HIGH" } else { "LOW" })
1713                            }
1714                            Err(e) => println!("WARNING: Failed to set digital output: {}", e),
1715                        }
1716                        thread::sleep(Duration::from_millis(100));
1717                    }
1718                }
1719                Err(e) => {
1720                    println!("WARNING: Could not configure pin function: {}", e);
1721                }
1722            }
1723        }
1724
1725        #[test]
1726        fn test_hardware_analog_input() {
1727            println!("Testing hardware analog input...");
1728
1729            let device_count = enumerate_usb_devices().expect("Failed to enumerate devices");
1730            if device_count == 0 {
1731                println!("SKIP: No PoKeys devices found for hardware test");
1732                return;
1733            }
1734
1735            let mut device = connect_to_device(0).expect("Failed to connect to device");
1736            device.get_device_data().expect("Failed to get device data");
1737
1738            // Test reading analog input
1739            match device.get_analog_input(1) {
1740                Ok(value) => {
1741                    println!("Analog input 1 value: {}", value);
1742                    // Basic sanity check - value should be within ADC range
1743                    assert!(value <= 4095, "Analog value out of range for 12-bit ADC");
1744                }
1745                Err(e) => {
1746                    println!("WARNING: Could not read analog input: {}", e);
1747                }
1748            }
1749        }
1750
1751        #[test]
1752        fn test_hardware_pwm_output() {
1753            println!("Testing hardware PWM output...");
1754
1755            let device_count = enumerate_usb_devices().expect("Failed to enumerate devices");
1756            if device_count == 0 {
1757                println!("SKIP: No PoKeys devices found for hardware test");
1758                return;
1759            }
1760
1761            let mut device = connect_to_device(0).expect("Failed to connect to device");
1762            device.get_device_data().expect("Failed to get device data");
1763
1764            // Test PWM configuration
1765            match device.set_pwm_period(25000) {
1766                Ok(_) => {
1767                    println!("Set PWM period to 25000 cycles");
1768
1769                    // Test different duty cycles
1770                    for duty in [25.0, 50.0, 75.0, 0.0] {
1771                        match device.set_pwm_duty_cycle_percent_for_pin(17, duty) {
1772                            Ok(_) => {
1773                                println!("Set PWM duty cycle to {}%", duty);
1774                                thread::sleep(Duration::from_millis(200));
1775                            }
1776                            Err(e) => {
1777                                println!("WARNING: Could not set PWM duty cycle: {}", e);
1778                            }
1779                        }
1780                    }
1781                }
1782                Err(e) => {
1783                    println!("WARNING: Could not configure PWM: {}", e);
1784                }
1785            }
1786        }
1787
1788        #[test]
1789        fn test_hardware_encoder_reading() {
1790            println!("Testing hardware encoder reading...");
1791
1792            let device_count = enumerate_usb_devices().expect("Failed to enumerate devices");
1793            if device_count == 0 {
1794                println!("SKIP: No PoKeys devices found for hardware test");
1795                return;
1796            }
1797
1798            let mut device = connect_to_device(0).expect("Failed to connect to device");
1799            device.get_device_data().expect("Failed to get device data");
1800
1801            // Test encoder configuration
1802            let mut options = crate::encoders::EncoderOptions::new();
1803            options.enabled = true;
1804            options.sampling_4x = true;
1805
1806            match device.configure_encoder(0, 1, 2, options) {
1807                Ok(_) => {
1808                    println!("Configured encoder 0 on pins 1 and 2");
1809
1810                    // Read encoder value multiple times
1811                    for i in 0..5 {
1812                        match device.get_encoder_value(0) {
1813                            Ok(value) => {
1814                                println!("Encoder 0 reading {}: {}", i + 1, value);
1815                            }
1816                            Err(e) => {
1817                                println!("WARNING: Could not read encoder: {}", e);
1818                            }
1819                        }
1820                        thread::sleep(Duration::from_millis(100));
1821                    }
1822                }
1823                Err(e) => {
1824                    println!("WARNING: Could not configure encoder: {}", e);
1825                }
1826            }
1827        }
1828
1829        #[test]
1830        fn test_hardware_network_discovery() {
1831            println!("Testing hardware network device discovery...");
1832
1833            match enumerate_network_devices(2000) {
1834                Ok(devices) => {
1835                    println!("Found {} network PoKeys devices", devices.len());
1836
1837                    for (i, device) in devices.iter().enumerate() {
1838                        println!(
1839                            "Network device {}: Serial {}, IP {}.{}.{}.{}",
1840                            i + 1,
1841                            device.serial_number,
1842                            device.ip_address[0],
1843                            device.ip_address[1],
1844                            device.ip_address[2],
1845                            device.ip_address[3]
1846                        );
1847                    }
1848
1849                    if devices.is_empty() {
1850                        println!(
1851                            "No network devices found - this is normal if no network PoKeys devices are present"
1852                        );
1853                    }
1854                }
1855                Err(e) => {
1856                    println!("WARNING: Network discovery failed: {}", e);
1857                }
1858            }
1859        }
1860
1861        #[test]
1862        fn test_hardware_device_info_validation() {
1863            println!("Testing hardware device info validation...");
1864
1865            let device_count = enumerate_usb_devices().expect("Failed to enumerate devices");
1866            if device_count == 0 {
1867                println!("SKIP: No PoKeys devices found for hardware test");
1868                return;
1869            }
1870
1871            let mut device = connect_to_device(0).expect("Failed to connect to device");
1872            device.get_device_data().expect("Failed to get device data");
1873
1874            // Validate device information makes sense
1875            assert!(
1876                device.device_data.serial_number > 0,
1877                "Serial number should be non-zero"
1878            );
1879            assert!(device.info.pin_count > 0, "Pin count should be non-zero");
1880            assert!(
1881                device.info.pin_count <= 100,
1882                "Pin count should be reasonable"
1883            );
1884
1885            println!("Device validation passed:");
1886            println!("  Serial: {}", device.device_data.serial_number);
1887            println!("  Pins: {}", device.info.pin_count);
1888            println!("  PWM Channels: {}", device.info.pwm_count);
1889            println!("  Encoders: {}", device.info.encoders_count);
1890            println!("  Analog Inputs: {}", device.info.analog_inputs);
1891        }
1892    }
1893}