1use 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
17pub struct PoKeysDevice {
19 connection_type: DeviceConnectionType,
21 connection_param: ConnectionParam,
22
23 pub info: DeviceInfo,
25 pub device_data: DeviceData,
26 pub network_device_data: Option<NetworkDeviceInfo>,
27
28 pub model: Option<crate::models::DeviceModel>,
30
31 pub pins: Vec<PinData>,
33 pub encoders: Vec<EncoderData>,
34 pub pwm: PwmData,
35
36 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 pub(crate) communication: CommunicationManager,
46 usb_interface: Option<Box<dyn UsbHidInterface>>,
47 network_interface: Option<Box<dyn NetworkInterface>>,
48
49 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 pub i2c_config: I2cConfig,
59 pub i2c_metrics: I2cMetrics,
60 pub validation_level: ValidationLevel,
61
62 #[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 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 pub fn get_device_data(&mut self) -> Result<()> {
110 self.read_device_data()?;
112 self.initialize_device_structures()?;
113
114 self.load_device_model()?;
116
117 Ok(())
118 }
119
120 fn load_device_model(&mut self) -> Result<()> {
122 use crate::models::load_model;
123
124 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", "PoKeys57CNC" => "PoKeys57CNC",
133 _ => return Ok(()), };
135
136 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(()) }
147 }
148 }
149
150 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 true
166 }
167 }
168
169 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 Vec::new()
184 }
185 }
186
187 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 Ok(())
203 }
204 }
205
206 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 Vec::new()
222 }
223 }
224
225 pub fn save_configuration(&mut self) -> Result<()> {
227 self.send_request(0x50, 0xAA, 0x55, 0, 0)?;
231 Ok(())
232 }
233
234 pub fn set_device_name(&mut self, name: &str) -> Result<()> {
236 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 let mut request = [0u8; 64];
252 request[0] = 0xBB; request[1] = 0x06; request[2] = 0x01; request[3] = 0x01; request[4] = 0x00; request[5] = 0x00; request[6] = self.communication.get_next_request_id();
259
260 request[35..55].copy_from_slice(&name_bytes);
262
263 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 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; 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 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 Ok(())
302 }
303 }
304 } else {
305 Err(PoKeysError::NotConnected)
306 }
307 }
308 }
309 }
310
311 pub fn clear_configuration(&mut self) -> Result<()> {
313 self.send_request(0x02, 0, 0, 0, 0)?;
314 Ok(())
315 }
316
317 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 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 pub fn get_connection_type(&self) -> DeviceConnectionType {
345 self.connection_type
346 }
347
348 pub fn set_i2c_config(&mut self, config: I2cConfig) {
350 self.i2c_config = config;
351 }
352
353 pub fn get_i2c_config(&self) -> &I2cConfig {
355 &self.i2c_config
356 }
357
358 pub fn set_validation_level(&mut self, level: ValidationLevel) {
360 self.validation_level = level;
361 }
362
363 pub fn get_i2c_metrics(&self) -> &I2cMetrics {
365 &self.i2c_metrics
366 }
367
368 pub fn reset_i2c_metrics(&mut self) {
370 self.i2c_metrics = I2cMetrics::default();
371 }
372
373 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 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 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 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 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, }
422 }
423
424 #[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 #[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 #[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 if !matches!(command, 0x11..=0x42) {
461 return Err(PoKeysError::InvalidCommand(command));
462 }
463
464 if device_id >= 16 {
466 return Err(PoKeysError::InvalidDeviceId(device_id));
468 }
469
470 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 #[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 #[allow(dead_code)]
523 fn calculate_checksum(&self, data: &[u8]) -> u8 {
524 data.iter().fold(0, |acc, &byte| acc ^ byte)
525 }
526
527 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 match self.device_data.device_type_id {
536 32 => check_pokeys57cnc_pin_capability(pin, capability), _ => false, }
539 }
540
541 pub fn get_network_configuration(
543 &mut self,
544 timeout_ms: u32,
545 ) -> Result<(Option<NetworkDeviceSummary>, NetworkDeviceInfo)> {
546 let discovery_info = enumerate_network_devices(timeout_ms)?
548 .into_iter()
549 .find(|d| d.serial_number == self.device_data.serial_number);
550
551 let response = self.send_request(0xE0, 0x00, 0x00, 0, 0)?;
553
554 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 Ok((discovery_info, config))
591 }
592
593 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 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 pub fn read_device_data(&mut self) -> Result<()> {
676 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 fn parse_device_data_response(&mut self, response: &[u8]) -> Result<()> {
691 if response.len() >= 64 && (&response[8..12] == b"PK58" || &response[8..12] == b"PKEx") {
693 let software_version_encoded = response[4]; let revision_number = response[5]; let major_bits = (software_version_encoded >> 4) & 0x0F; let minor_bits = software_version_encoded & 0x0F; let decoded_major = 1 + major_bits;
703 let decoded_minor = minor_bits;
704
705 let serial_32bit =
707 u32::from_le_bytes([response[12], response[13], response[14], response[15]]);
708
709 let firmware_version = response[16];
711 let firmware_revision = response[17];
712
713 let hw_id = response[18];
715
716 let user_id = response[19];
718
719 let build_date_bytes = &response[20..31];
721
722 let device_name_bytes = &response[31..41];
724
725 let firmware_type = response[41];
727
728 let _app_firmware_major = response[42];
730 let _app_firmware_minor = response[43];
731
732 let product_id = response[57];
734
735 self.device_data.serial_number = serial_32bit;
737
738 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 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 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 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 let serial_16bit = ((response[2] as u32) << 8) | (response[3] as u32);
787
788 let software_version_encoded = response[4];
790 let revision_number = response[5];
791
792 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; 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 match self.device_data.device_type_id {
822 32 => self.initialize_pokeys57cnc(), _ => self.initialize_generic_device(),
824 }
825 }
826
827 fn initialize_pokeys57cnc(&mut self) -> Result<()> {
828 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 self.pins = vec![PinData::new(); self.info.pin_count as usize];
839 self.encoders = vec![EncoderData::new(); self.info.encoders_count as usize];
840 self.po_ext_bus_data = vec![0; 10]; Ok(())
844 }
845
846 fn initialize_generic_device(&mut self) -> Result<()> {
847 self.info.pin_count = 55; self.pins = vec![PinData::new(); self.info.pin_count as usize];
850 self.encoders = vec![EncoderData::new(); 25]; Ok(())
852 }
853
854 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 pub fn read_led_matrix_config(&mut self) -> Result<crate::matrix::MatrixLedProtocolConfig> {
878 let response = self.send_request(0xD5, 0x01, 0, 0, 0)?;
880
881 let enabled_flags = response[2];
883 let display1_size = response[3];
884 let display2_size = response[4];
885
886 let display1_enabled = (enabled_flags & 0x01) != 0;
888 let display2_enabled = (enabled_flags & 0x02) != 0;
889
890 let display1_characters = display2_size & 0x0F; let display2_characters = display1_size & 0x0F; Ok(crate::matrix::MatrixLedProtocolConfig {
896 display1_enabled,
897 display2_enabled,
898 display1_characters,
899 display2_characters,
900 })
901 }
902
903 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 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 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 interface.send(&request[..64])?;
945
946 let mut response = [0u8; RESPONSE_BUFFER_SIZE];
948 interface.receive(&mut response)?;
949
950 self.communication
952 .validate_response(&response, request_id)?;
953 }
954 }
955 _ => {
956 return Err(PoKeysError::NotSupported);
957 }
958 }
959
960 Ok(())
961 }
962
963 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 let mut data = [0u8; 55];
991 data[0] = 1; data[1] = ((width - 1) << 4) | (height - 1); for (i, &pin) in row_pins.iter().enumerate().take(8) {
996 data[2 + i] = if pin > 0 { pin - 1 } else { 0 };
997 }
998
999 for (i, &pin) in column_pins.iter().enumerate().take(8) {
1001 data[10 + i] = if pin > 0 { pin - 1 } else { 0 };
1002 }
1003
1004 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 self.send_request_with_data(0xCA, 16, 0, 0, 0, &data)?;
1013
1014 self.matrix_keyboard.configuration = 1;
1016 self.matrix_keyboard.width = width;
1017 self.matrix_keyboard.height = height;
1018
1019 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 pub fn read_matrix_keyboard(&mut self) -> Result<()> {
1041 let response = self.send_request(0xCA, 20, 0, 0, 0)?;
1042
1043 let data_start = 8;
1045
1046 if response.len() >= data_start + 16 {
1047 self.matrix_keyboard.key_values.fill(0);
1049
1050 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 fn encode_display_size(&self, characters: u8, columns: u8) -> u8 {
1068 (characters & 0x0F) | ((columns & 0x0F) << 4)
1071 }
1072
1073 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 pub fn configure_led_matrices(
1098 &mut self,
1099 configs: &[crate::matrix::LedMatrixConfig],
1100 ) -> Result<()> {
1101 for config in configs {
1103 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 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 for config in configs {
1120 if let Some(model) = &self.model {
1121 model.validate_led_matrix_config(config)?;
1122 }
1123 }
1124
1125 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 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 pub fn configure_servo(&mut self, config: crate::pwm::ServoConfig) -> Result<()> {
1167 if self.pwm.pwm_period == 0 {
1169 self.set_pwm_period(500000)?; }
1171
1172 self.enable_pwm_for_pin(config.pin, true)?;
1174
1175 Ok(())
1176 }
1177
1178 pub fn set_servo_angle(&mut self, config: &crate::pwm::ServoConfig, angle: f32) -> Result<()> {
1180 config.set_angle(self, angle)
1181 }
1182
1183 pub fn set_servo_speed(&mut self, config: &crate::pwm::ServoConfig, speed: f32) -> Result<()> {
1185 config.set_speed(self, speed)
1186 }
1187
1188 pub fn stop_servo(&mut self, config: &crate::pwm::ServoConfig) -> Result<()> {
1190 config.stop(self)
1191 }
1192}
1193
1194impl 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
1255fn 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
1294static 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
1311pub 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 #[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
1337pub 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 discover_all_devices(timeout_ms)
1345}
1346
1347pub 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); let mut device = PoKeysDevice::new(DeviceConnectionType::UsbDevice);
1362
1363 #[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 device.get_device_data()?;
1379
1380 Ok(device)
1381}
1382
1383pub 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 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
1412pub fn connect_to_network_device(device_summary: &NetworkDeviceSummary) -> Result<PoKeysDevice> {
1414 let mut device = PoKeysDevice::new(DeviceConnectionType::NetworkDevice);
1415
1416 if device_summary.use_udp != 0 {
1418 device.connection_param = ConnectionParam::Udp;
1419 } else {
1420 device.connection_param = ConnectionParam::Tcp;
1421 }
1422
1423 #[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 device.get_device_data()?;
1431
1432 Ok(device)
1433}
1434
1435struct 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 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 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 if line.contains("<key>vendor_id</key>") && i + 1 < lines.len() {
1480 let next_line = lines[i + 1].trim();
1481
1482 if next_line.contains("<integer>7619</integer>")
1484 || next_line.contains("<string>0x1dc3</string>")
1485 {
1486 let mut product_id = 0u16;
1488 let mut serial_number = None;
1489 let mut location_id = String::new();
1490
1491 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 if search_line.contains("<key>vendor_id</key>") && j > i + 2 {
1526 break;
1527 }
1528 }
1529
1530 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 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 Ok(device_list.len() as i32)
1559}
1560
1561#[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 let interface = UdpNetworkInterface::new(device_summary.ip_address, 20055)?;
1571 Ok(Box::new(interface))
1572 } else {
1573 let interface = TcpNetworkInterface::new(device_summary.ip_address, 20055)?;
1575 Ok(Box::new(interface))
1576 }
1577}
1578
1579#[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); }
1605
1606 #[test]
1607 fn test_pin_capability_checking() {
1608 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 )); assert!(!check_pokeys57cnc_pin_capability(
1622 100,
1623 crate::io::PinCapability::DigitalInput
1624 )); }
1626
1627 #[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 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 match device.set_pin_function(1, crate::io::PinFunction::DigitalOutput) {
1705 Ok(_) => {
1706 println!("Successfully configured pin 1 as digital output");
1707
1708 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 match device.get_analog_input(1) {
1740 Ok(value) => {
1741 println!("Analog input 1 value: {}", value);
1742 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 match device.set_pwm_period(25000) {
1766 Ok(_) => {
1767 println!("Set PWM period to 25000 cycles");
1768
1769 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 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 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 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}