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 get_system_load(&mut self) -> Result<u8> {
240 let response = self.send_request(0x05, 0, 0, 0, 0)?;
241 Ok(parse_system_load_response(&response))
242 }
243
244 pub fn set_device_name(&mut self, name: &str) -> Result<()> {
258 let mut name_bytes = [0u8; 20];
268 let name_str = if name.len() > 20 { &name[..20] } else { name };
269 let name_bytes_slice = name_str.as_bytes();
270 name_bytes[..name_bytes_slice.len()].copy_from_slice(name_bytes_slice);
271
272 let mut request = [0u8; 64];
274 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();
281
282 request[35..55].copy_from_slice(&name_bytes);
284
285 let mut checksum: u8 = 0;
288 for i in 0..7 {
289 checksum = checksum.wrapping_add(request[i]);
290 }
291 request[7] = checksum;
292
293 match self.connection_type {
295 DeviceConnectionType::UsbDevice | DeviceConnectionType::FastUsbDevice => {
296 if let Some(ref mut interface) = self.usb_interface {
297 let mut hid_packet = [0u8; 65];
298 hid_packet[0] = 0; hid_packet[1..65].copy_from_slice(&request);
300
301 interface.write(&hid_packet)?;
302
303 let mut response = [0u8; 65];
304 interface.read(&mut response)?;
305 } else {
306 return Err(PoKeysError::NotConnected);
307 }
308 }
309 DeviceConnectionType::NetworkDevice => {
310 if let Some(ref mut interface) = self.network_interface {
311 interface.send(&request)?;
312
313 let mut response = [0u8; 64];
315 let _ = interface
316 .receive_timeout(&mut response, std::time::Duration::from_millis(2000));
317 } else {
318 return Err(PoKeysError::NotConnected);
319 }
320 }
321 }
322
323 self.save_configuration()
324 }
325
326 pub fn clear_configuration(&mut self) -> Result<()> {
330 self.send_request(0x52, 0xAA, 0x55, 0, 0)?;
331 Ok(())
332 }
333
334 pub fn reboot_device(&mut self) -> Result<()> {
345 match self.send_request(0xF3, 0, 0, 0, 0) {
346 Ok(_) => Ok(()),
347 Err(PoKeysError::Transfer(_)) => Ok(()),
350 Err(e) => Err(e),
351 }
352 }
353
354 pub fn custom_request(
356 &mut self,
357 request_type: u8,
358 param1: u8,
359 param2: u8,
360 param3: u8,
361 param4: u8,
362 ) -> Result<[u8; RESPONSE_BUFFER_SIZE]> {
363 self.send_request(request_type, param1, param2, param3, param4)
364 }
365
366 pub fn set_ethernet_retry_count_and_timeout(
368 &mut self,
369 send_retries: u32,
370 read_retries: u32,
371 timeout_ms: u32,
372 ) {
373 self.communication.set_retries_and_timeout(
374 send_retries,
375 read_retries,
376 Duration::from_millis(timeout_ms as u64),
377 );
378 }
379
380 pub fn get_connection_type(&self) -> DeviceConnectionType {
382 self.connection_type
383 }
384
385 pub fn set_i2c_config(&mut self, config: I2cConfig) {
387 self.i2c_config = config;
388 }
389
390 pub fn get_i2c_config(&self) -> &I2cConfig {
392 &self.i2c_config
393 }
394
395 pub fn set_validation_level(&mut self, level: ValidationLevel) {
397 self.validation_level = level;
398 }
399
400 pub fn get_i2c_metrics(&self) -> &I2cMetrics {
402 &self.i2c_metrics
403 }
404
405 pub fn reset_i2c_metrics(&mut self) {
407 self.i2c_metrics = I2cMetrics::default();
408 }
409
410 pub fn health_check(&mut self) -> HealthStatus {
412 HealthStatus {
413 connectivity: self.test_connectivity(),
414 i2c_health: self.test_i2c_health(),
415 error_rate: self.calculate_error_rate(),
416 performance: self.get_performance_summary(),
417 }
418 }
419
420 fn test_connectivity(&mut self) -> ConnectivityStatus {
422 match self.get_device_data() {
423 Ok(_) => ConnectivityStatus::Healthy,
424 Err(e) => ConnectivityStatus::Degraded(e.to_string()),
425 }
426 }
427
428 fn test_i2c_health(&mut self) -> I2cHealthStatus {
430 match self.i2c_get_status() {
431 Ok(I2cStatus::Ok) => I2cHealthStatus::Healthy,
432 Ok(status) => I2cHealthStatus::Degraded(format!("I2C status: {:?}", status)),
433 Err(e) => I2cHealthStatus::Failed(e.to_string()),
434 }
435 }
436
437 fn calculate_error_rate(&self) -> f64 {
439 if self.i2c_metrics.total_commands == 0 {
440 0.0
441 } else {
442 self.i2c_metrics.failed_commands as f64 / self.i2c_metrics.total_commands as f64
443 }
444 }
445
446 fn get_performance_summary(&self) -> PerformanceSummary {
448 let success_rate = if self.i2c_metrics.total_commands == 0 {
449 1.0
450 } else {
451 self.i2c_metrics.successful_commands as f64 / self.i2c_metrics.total_commands as f64
452 };
453
454 PerformanceSummary {
455 avg_response_time_ms: self.i2c_metrics.average_response_time.as_millis() as f64,
456 success_rate,
457 throughput_commands_per_sec: 0.0, }
459 }
460
461 #[allow(dead_code)]
463 fn validate_packet(&self, data: &[u8]) -> Result<()> {
464 match &self.validation_level {
465 ValidationLevel::None => Ok(()),
466 ValidationLevel::Basic => self.validate_basic_structure(data),
467 ValidationLevel::Strict => self.validate_strict_protocol(data),
468 ValidationLevel::Custom(config) => self.validate_custom(data, config),
469 }
470 }
471
472 #[allow(dead_code)]
474 fn validate_basic_structure(&self, data: &[u8]) -> Result<()> {
475 if data.len() < 3 {
476 return Err(PoKeysError::InvalidPacketStructure(
477 "Minimum packet size is 3 bytes".to_string(),
478 ));
479 }
480 Ok(())
481 }
482
483 #[allow(dead_code)]
485 fn validate_strict_protocol(&self, data: &[u8]) -> Result<()> {
486 if data.len() < 3 {
487 return Err(PoKeysError::InvalidPacketStructure(
488 "Minimum packet size is 3 bytes".to_string(),
489 ));
490 }
491
492 let command = data[0];
493 let device_id = data[1];
494 let checksum = data[data.len() - 1];
495
496 if !matches!(command, 0x11..=0x42) {
498 return Err(PoKeysError::InvalidCommand(command));
499 }
500
501 if device_id >= 16 {
503 return Err(PoKeysError::InvalidDeviceId(device_id));
505 }
506
507 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 Ok(())
517 }
518
519 #[allow(dead_code)]
521 fn validate_custom(&self, data: &[u8], config: &crate::types::ValidationConfig) -> Result<()> {
522 if config.validate_packet_structure && data.len() < 3 {
523 return Err(PoKeysError::InvalidPacketStructure(
524 "Minimum packet size is 3 bytes".to_string(),
525 ));
526 }
527
528 if data.len() >= 3 {
529 let command = data[0];
530 let device_id = data[1];
531 let checksum = data[data.len() - 1];
532
533 if config.validate_command_ids
534 && !config.valid_commands.is_empty()
535 && !config.valid_commands.contains(&command)
536 {
537 return Err(PoKeysError::InvalidCommand(command));
538 }
539
540 if config.validate_device_ids && device_id > config.max_device_id {
541 return Err(PoKeysError::InvalidDeviceId(device_id));
542 }
543
544 if config.validate_checksums {
545 let calculated_checksum = self.calculate_checksum(&data[..data.len() - 1]);
546 if checksum != calculated_checksum {
547 return Err(PoKeysError::InvalidChecksumDetailed {
548 expected: calculated_checksum,
549 received: checksum,
550 });
551 }
552 }
553 }
554
555 Ok(())
556 }
557
558 #[allow(dead_code)]
560 fn calculate_checksum(&self, data: &[u8]) -> u8 {
561 data.iter().fold(0, |acc, &byte| acc ^ byte)
562 }
563
564 pub fn check_pin_capability(&self, pin: u32, capability: crate::io::PinCapability) -> bool {
566 if pin as usize >= self.pins.len() {
567 return false;
568 }
569
570 match self.device_data.device_type_id {
573 32 => check_pokeys57cnc_pin_capability(pin, capability), _ => false, }
576 }
577
578 pub fn get_network_configuration(
580 &mut self,
581 timeout_ms: u32,
582 ) -> Result<(Option<NetworkDeviceSummary>, NetworkDeviceInfo)> {
583 let discovery_info = enumerate_network_devices(timeout_ms)?
585 .into_iter()
586 .find(|d| d.serial_number == self.device_data.serial_number);
587
588 let response = self.send_request(0xE0, 0x00, 0x00, 0, 0)?;
590
591 let config = NetworkDeviceInfo {
593 dhcp: response.get(8).copied().unwrap_or(0),
594 ip_address_setup: [
595 response.get(9).copied().unwrap_or(0),
596 response.get(10).copied().unwrap_or(0),
597 response.get(11).copied().unwrap_or(0),
598 response.get(12).copied().unwrap_or(0),
599 ],
600 ip_address_current: [
601 response.get(13).copied().unwrap_or(0),
602 response.get(14).copied().unwrap_or(0),
603 response.get(15).copied().unwrap_or(0),
604 response.get(16).copied().unwrap_or(0),
605 ],
606 tcp_timeout: u16::from_le_bytes([
607 response.get(17).copied().unwrap_or(0),
608 response.get(18).copied().unwrap_or(0),
609 ])
610 .saturating_mul(100),
611 gateway_ip: [
612 response.get(19).copied().unwrap_or(0),
613 response.get(20).copied().unwrap_or(0),
614 response.get(21).copied().unwrap_or(0),
615 response.get(22).copied().unwrap_or(0),
616 ],
617 subnet_mask: [
618 response.get(23).copied().unwrap_or(0),
619 response.get(24).copied().unwrap_or(0),
620 response.get(25).copied().unwrap_or(0),
621 response.get(26).copied().unwrap_or(0),
622 ],
623 additional_network_options: response.get(27).copied().unwrap_or(0),
624 };
625
626 Ok((discovery_info, config))
627 }
628
629 pub fn set_network_configuration(&mut self, config: &NetworkDeviceInfo) -> Result<()> {
648 let timeout_units = (config.tcp_timeout / 100).max(1);
650 let timeout_bytes = timeout_units.to_le_bytes();
651
652 let gateway_subnet_set: u8 =
654 if config.gateway_ip != [0, 0, 0, 0] || config.subnet_mask != [0, 0, 0, 0] {
655 1
656 } else {
657 0
658 };
659
660 let options = (config.additional_network_options & 0x0F) | 0xA0;
662
663 let mut data = [0u8; 21];
665 data[0] = config.dhcp; data[1..5].copy_from_slice(&config.ip_address_setup); data[9] = timeout_bytes[0]; data[10] = timeout_bytes[1];
670 data[11..15].copy_from_slice(&config.gateway_ip); data[15..19].copy_from_slice(&config.subnet_mask); data[19] = gateway_subnet_set; data[20] = options; self.send_request_with_data(0xE0, 10, 0, 0, 0, &data)?;
677 Ok(())
678 }
679
680 pub fn send_request(
683 &mut self,
684 request_type: u8,
685 param1: u8,
686 param2: u8,
687 param3: u8,
688 param4: u8,
689 ) -> Result<[u8; RESPONSE_BUFFER_SIZE]> {
690 match self.connection_type {
691 DeviceConnectionType::UsbDevice | DeviceConnectionType::FastUsbDevice => {
692 if let Some(ref mut interface) = self.usb_interface {
693 self.communication.send_usb_request(
694 interface,
695 request_type,
696 param1,
697 param2,
698 param3,
699 param4,
700 )
701 } else {
702 Err(PoKeysError::NotConnected)
703 }
704 }
705 DeviceConnectionType::NetworkDevice => {
706 if let Some(ref mut interface) = self.network_interface {
707 self.communication.send_network_request(
708 interface,
709 request_type,
710 param1,
711 param2,
712 param3,
713 param4,
714 )
715 } else {
716 Err(PoKeysError::NotConnected)
717 }
718 }
719 }
720 }
721
722 pub fn send_request_with_data(
724 &mut self,
725 request_type: u8,
726 param1: u8,
727 param2: u8,
728 param3: u8,
729 param4: u8,
730 data: &[u8],
731 ) -> Result<[u8; RESPONSE_BUFFER_SIZE]> {
732 let request = self.communication.prepare_request_with_data(
733 request_type,
734 param1,
735 param2,
736 param3,
737 param4,
738 Some(data),
739 );
740
741 match self.connection_type {
742 DeviceConnectionType::UsbDevice | DeviceConnectionType::FastUsbDevice => {
743 if let Some(ref mut interface) = self.usb_interface {
744 self.communication.send_usb_request_raw(interface, &request)
745 } else {
746 Err(PoKeysError::NotConnected)
747 }
748 }
749 DeviceConnectionType::NetworkDevice => {
750 if let Some(ref mut interface) = self.network_interface {
751 self.communication
752 .send_network_request_raw(interface, &request)
753 } else {
754 Err(PoKeysError::NotConnected)
755 }
756 }
757 }
758 }
759
760 pub fn read_device_data(&mut self) -> Result<()> {
763 let response = self.send_request(0x00, 0, 0, 0, 0)?;
765
766 if response.len() < 64 {
767 return Err(PoKeysError::Protocol(
768 "Read device data response too short".to_string(),
769 ));
770 }
771
772 self.parse_device_data_response(&response)?;
773 Ok(())
774 }
775
776 fn parse_device_data_response(&mut self, response: &[u8]) -> Result<()> {
778 if response.len() >= 64 && (&response[8..12] == b"PK58" || &response[8..12] == b"PKEx") {
780 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;
790 let decoded_minor = minor_bits;
791
792 let serial_32bit =
794 u32::from_le_bytes([response[12], response[13], response[14], response[15]]);
795
796 let firmware_version = response[16];
798 let firmware_revision = response[17];
799
800 let hw_id = response[18];
802
803 let user_id = response[19];
805
806 let build_date_bytes = &response[20..31];
808
809 let device_name_bytes = &response[31..41];
811
812 let firmware_type = response[41];
814
815 let _app_firmware_major = response[42];
817 let _app_firmware_minor = response[43];
818
819 let product_id = response[57];
821
822 self.device_data.serial_number = serial_32bit;
824
825 self.device_data.firmware_version_major = decoded_major;
827 self.device_data.firmware_version_minor = decoded_minor;
828 self.device_data.firmware_revision = revision_number;
829
830 self.device_data.secondary_firmware_version_major = firmware_version;
832 self.device_data.secondary_firmware_version_minor = firmware_revision;
833
834 self.device_data.hw_type = hw_id;
835 self.device_data.user_id = user_id;
836 self.device_data.fw_type = firmware_type;
837 self.device_data.product_id = product_id;
838
839 self.device_data.device_name.fill(0);
841 let name_len =
842 std::cmp::min(device_name_bytes.len(), self.device_data.device_name.len());
843 self.device_data.device_name[..name_len]
844 .copy_from_slice(&device_name_bytes[..name_len]);
845
846 self.device_data.build_date.fill(0);
848 let date_len = std::cmp::min(build_date_bytes.len(), self.device_data.build_date.len());
849 self.device_data.build_date[..date_len].copy_from_slice(&build_date_bytes[..date_len]);
850
851 log::debug!("Extended device info parsed:");
852 log::debug!(" Serial: {}", serial_32bit);
853 log::debug!(
854 " Decoded firmware: {}.{}.{}",
855 decoded_major,
856 decoded_minor,
857 revision_number
858 );
859 log::debug!(
860 " Raw software version byte: 0x{:02X}",
861 software_version_encoded
862 );
863 log::debug!(
864 " Extended firmware: {}.{}",
865 firmware_version,
866 firmware_revision
867 );
868 log::debug!(" Hardware type: {}", hw_id);
869 } else {
870 let serial_16bit = ((response[2] as u32) << 8) | (response[3] as u32);
874
875 let software_version_encoded = response[4];
877 let revision_number = response[5];
878
879 let major_bits = (software_version_encoded >> 4) & 0x0F;
881 let minor_bits = software_version_encoded & 0x0F;
882 let decoded_major = 1 + major_bits;
883 let decoded_minor = minor_bits;
884
885 self.device_data.serial_number = serial_16bit;
886 self.device_data.firmware_version_major = decoded_major;
887 self.device_data.firmware_version_minor = decoded_minor;
888 self.device_data.firmware_revision = revision_number;
889 self.device_data.hw_type = 0; self.device_data.device_name.fill(0);
891 self.device_data.build_date.fill(0);
892
893 log::debug!("Legacy device info parsed:");
894 log::debug!(" Serial: {}", serial_16bit);
895 log::debug!(
896 " Decoded firmware: {}.{}.{}",
897 decoded_major,
898 decoded_minor,
899 revision_number
900 );
901 }
902
903 Ok(())
904 }
905
906 fn initialize_device_structures(&mut self) -> Result<()> {
907 match self.device_data.device_type_id {
909 32 => self.initialize_pokeys57cnc(), _ => self.initialize_generic_device(),
911 }
912 }
913
914 fn initialize_pokeys57cnc(&mut self) -> Result<()> {
915 self.info.pin_count = 55;
917 self.info.pwm_count = 6;
918 self.info.encoders_count = 25;
919 self.info.fast_encoders = 3;
920 self.info.ultra_fast_encoders = 1;
921 self.info.analog_inputs = 8;
922 self.info.pulse_engine_v2 = 1;
923
924 self.pins = vec![PinData::new(); self.info.pin_count as usize];
926 self.encoders = vec![EncoderData::new(); self.info.encoders_count as usize];
927 self.po_ext_bus_data = vec![0; 10]; Ok(())
931 }
932
933 fn initialize_generic_device(&mut self) -> Result<()> {
934 self.info.pin_count = 55; self.pins = vec![PinData::new(); self.info.pin_count as usize];
937 self.encoders = vec![EncoderData::new(); 25]; Ok(())
939 }
940
941 pub fn configure_led_matrix(
945 &mut self,
946 config: &crate::matrix::MatrixLedProtocolConfig,
947 ) -> Result<()> {
948 let enabled_flags = self.encode_matrix_enabled(config);
949 let display1_size = self.encode_display_size(
950 config.display1_characters,
951 crate::matrix::SEVEN_SEGMENT_COLUMNS,
952 );
953 let display2_size = self.encode_display_size(
954 config.display2_characters,
955 crate::matrix::SEVEN_SEGMENT_COLUMNS,
956 );
957
958 let _response =
959 self.send_request(0xD5, 0x00, enabled_flags, display1_size, display2_size)?;
960 Ok(())
961 }
962
963 pub fn read_led_matrix_config(&mut self) -> Result<crate::matrix::MatrixLedProtocolConfig> {
965 let response = self.send_request(0xD5, 0x01, 0, 0, 0)?;
967
968 let enabled_flags = response[2];
970 let display1_size = response[3];
971 let display2_size = response[4];
972
973 let display1_enabled = (enabled_flags & 0x01) != 0;
975 let display2_enabled = (enabled_flags & 0x02) != 0;
976
977 let display1_characters = display2_size & 0x0F; let display2_characters = display1_size & 0x0F; Ok(crate::matrix::MatrixLedProtocolConfig {
983 display1_enabled,
984 display2_enabled,
985 display1_characters,
986 display2_characters,
987 })
988 }
989
990 pub fn update_led_matrix(
992 &mut self,
993 matrix_id: u8,
994 action: crate::matrix::MatrixAction,
995 row: u8,
996 column: u8,
997 data: &[u8],
998 ) -> Result<()> {
999 let action_code = match (matrix_id, action) {
1000 (1, crate::matrix::MatrixAction::UpdateWhole) => 1,
1001 (1, crate::matrix::MatrixAction::SetPixel) => 5,
1002 (1, crate::matrix::MatrixAction::ClearPixel) => 6,
1003 (2, crate::matrix::MatrixAction::UpdateWhole) => 11,
1004 (2, crate::matrix::MatrixAction::SetPixel) => 15,
1005 (2, crate::matrix::MatrixAction::ClearPixel) => 16,
1006 _ => {
1007 return Err(PoKeysError::Parameter(format!(
1008 "Invalid matrix ID: {}",
1009 matrix_id
1010 )));
1011 }
1012 };
1013
1014 let request = self.communication.prepare_request_with_data(
1016 0xD6,
1017 action_code,
1018 row,
1019 column,
1020 0,
1021 Some(data),
1022 );
1023
1024 match self.connection_type {
1026 DeviceConnectionType::NetworkDevice => {
1027 if let Some(ref mut interface) = self.network_interface {
1028 let request_id = request[6];
1029
1030 interface.send(&request[..64])?;
1032
1033 let mut response = [0u8; RESPONSE_BUFFER_SIZE];
1035 interface.receive(&mut response)?;
1036
1037 self.communication
1039 .validate_response(&response, request_id)?;
1040 }
1041 }
1042 _ => {
1043 return Err(PoKeysError::NotSupported);
1044 }
1045 }
1046
1047 Ok(())
1048 }
1049
1050 pub fn configure_matrix_keyboard(
1066 &mut self,
1067 width: u8,
1068 height: u8,
1069 column_pins: &[u8],
1070 row_pins: &[u8],
1071 ) -> Result<()> {
1072 if width > 8 || height > 16 {
1073 return Err(PoKeysError::Parameter("Matrix size too large".to_string()));
1074 }
1075
1076 let mut data = [0u8; 55];
1078 data[0] = 1; data[1] = ((width - 1) << 4) | (height - 1); for (i, &pin) in row_pins.iter().enumerate().take(8) {
1083 data[2 + i] = if pin > 0 { pin - 1 } else { 0 };
1084 }
1085
1086 for (i, &pin) in column_pins.iter().enumerate().take(8) {
1088 data[10 + i] = if pin > 0 { pin - 1 } else { 0 };
1089 }
1090
1091 if height > 8 {
1093 for (i, &pin) in row_pins.iter().enumerate().skip(8).take(8) {
1094 data[34 + i] = if pin > 0 { pin - 1 } else { 0 };
1095 }
1096 }
1097
1098 self.send_request_with_data(0xCA, 16, 0, 0, 0, &data)?;
1100
1101 self.matrix_keyboard.configuration = 1;
1103 self.matrix_keyboard.width = width;
1104 self.matrix_keyboard.height = height;
1105
1106 for (i, &pin) in column_pins.iter().enumerate().take(8) {
1108 self.matrix_keyboard.column_pins[i] = pin;
1109 }
1110 for (i, &pin) in row_pins.iter().enumerate().take(16) {
1111 self.matrix_keyboard.row_pins[i] = pin;
1112 }
1113
1114 Ok(())
1115 }
1116
1117 pub fn read_matrix_keyboard(&mut self) -> Result<()> {
1128 let response = self.send_request(0xCA, 20, 0, 0, 0)?;
1129
1130 let data_start = 8;
1132
1133 if response.len() >= data_start + 16 {
1134 self.matrix_keyboard.key_values.fill(0);
1136
1137 for (row, &byte_val) in response[data_start..data_start + 16].iter().enumerate() {
1140 for col in 0..8 {
1141 let key_index = row * 8 + col;
1142 if key_index < 128 {
1143 self.matrix_keyboard.key_values[key_index] =
1144 if (byte_val & (1 << col)) != 0 { 1 } else { 0 };
1145 }
1146 }
1147 }
1148 }
1149
1150 Ok(())
1151 }
1152
1153 fn encode_display_size(&self, characters: u8, columns: u8) -> u8 {
1155 (characters & 0x0F) | ((columns & 0x0F) << 4)
1158 }
1159
1160 fn encode_matrix_enabled(&self, config: &crate::matrix::MatrixLedProtocolConfig) -> u8 {
1162 let mut enabled = 0u8;
1163 if config.display1_enabled {
1164 enabled |= 1 << 0;
1165 }
1166 if config.display2_enabled {
1167 enabled |= 1 << 1;
1168 }
1169 enabled
1170 }
1171
1172 pub fn configure_led_matrices(
1185 &mut self,
1186 configs: &[crate::matrix::LedMatrixConfig],
1187 ) -> Result<()> {
1188 for config in configs {
1190 if config.matrix_id < 1 || config.matrix_id > 2 {
1192 return Err(PoKeysError::InvalidConfiguration(
1193 "Matrix ID must be 1 or 2".to_string(),
1194 ));
1195 }
1196
1197 if config.characters < 1 || config.characters > 8 {
1199 return Err(PoKeysError::InvalidConfiguration(
1200 "Character count must be between 1 and 8 for 7-segment displays".to_string(),
1201 ));
1202 }
1203 }
1204
1205 for config in configs {
1207 if let Some(model) = &self.model {
1208 model.validate_led_matrix_config(config)?;
1209 }
1210 }
1211
1212 let mut protocol_config = crate::matrix::MatrixLedProtocolConfig {
1214 display1_enabled: false,
1215 display2_enabled: false,
1216 display1_characters: 1,
1217 display2_characters: 1,
1218 };
1219
1220 for config in configs {
1221 match config.matrix_id {
1222 1 => {
1223 protocol_config.display1_enabled = config.enabled;
1224 protocol_config.display1_characters = config.characters;
1225 }
1226 2 => {
1227 protocol_config.display2_enabled = config.enabled;
1228 protocol_config.display2_characters = config.characters;
1229 }
1230 _ => {
1231 return Err(PoKeysError::InvalidConfiguration(
1232 "Invalid matrix ID".to_string(),
1233 ));
1234 }
1235 }
1236 }
1237
1238 self.configure_led_matrix(&protocol_config)?;
1239
1240 if let Some(model) = &mut self.model {
1242 for config in configs {
1243 if config.enabled {
1244 model.reserve_led_matrix_pins(config.matrix_id)?;
1245 }
1246 }
1247 }
1248
1249 Ok(())
1250 }
1251
1252 pub fn configure_servo(&mut self, config: crate::pwm::ServoConfig) -> Result<()> {
1254 if self.pwm.pwm_period == 0 {
1256 self.set_pwm_period(500000)?; }
1258
1259 self.enable_pwm_for_pin(config.pin, true)?;
1261
1262 Ok(())
1263 }
1264
1265 pub fn set_servo_angle(&mut self, config: &crate::pwm::ServoConfig, angle: f32) -> Result<()> {
1267 config.set_angle(self, angle)
1268 }
1269
1270 pub fn set_servo_speed(&mut self, config: &crate::pwm::ServoConfig, speed: f32) -> Result<()> {
1272 config.set_speed(self, speed)
1273 }
1274
1275 pub fn stop_servo(&mut self, config: &crate::pwm::ServoConfig) -> Result<()> {
1277 config.stop(self)
1278 }
1279}
1280
1281impl Default for DeviceInfo {
1283 fn default() -> Self {
1284 Self {
1285 pin_count: 0,
1286 pwm_count: 0,
1287 basic_encoder_count: 0,
1288 encoders_count: 0,
1289 fast_encoders: 0,
1290 ultra_fast_encoders: 0,
1291 pwm_internal_frequency: 0,
1292 analog_inputs: 0,
1293 key_mapping: 0,
1294 triggered_key_mapping: 0,
1295 key_repeat_delay: 0,
1296 digital_counters: 0,
1297 joystick_button_axis_mapping: 0,
1298 joystick_analog_to_digital_mapping: 0,
1299 macros: 0,
1300 matrix_keyboard: 0,
1301 matrix_keyboard_triggered_mapping: 0,
1302 lcd: 0,
1303 matrix_led: 0,
1304 connection_signal: 0,
1305 po_ext_bus: 0,
1306 po_net: 0,
1307 analog_filtering: 0,
1308 init_outputs_start: 0,
1309 prot_i2c: 0,
1310 prot_1wire: 0,
1311 additional_options: 0,
1312 load_status: 0,
1313 custom_device_name: 0,
1314 po_tlog27_support: 0,
1315 sensor_list: 0,
1316 web_interface: 0,
1317 fail_safe_settings: 0,
1318 joystick_hat_switch: 0,
1319 pulse_engine: 0,
1320 pulse_engine_v2: 0,
1321 easy_sensors: 0,
1322 }
1323 }
1324}
1325
1326impl Default for RealTimeClock {
1327 fn default() -> Self {
1328 Self {
1329 sec: 0,
1330 min: 0,
1331 hour: 0,
1332 dow: 0,
1333 dom: 0,
1334 tmp: 0,
1335 doy: 0,
1336 month: 0,
1337 year: 0,
1338 }
1339 }
1340}
1341
1342fn check_pokeys57cnc_pin_capability(pin: u32, capability: crate::io::PinCapability) -> bool {
1344 use crate::io::PinCapability;
1345
1346 match pin {
1347 1..=2 => matches!(
1348 capability,
1349 PinCapability::DigitalInput
1350 | PinCapability::DigitalOutput
1351 | PinCapability::DigitalCounter
1352 | PinCapability::FastEncoder1A
1353 ),
1354 3..=6 => matches!(
1355 capability,
1356 PinCapability::DigitalInput
1357 | PinCapability::DigitalCounter
1358 | PinCapability::FastEncoder1A
1359 ),
1360 8 | 12..=13 => matches!(
1361 capability,
1362 PinCapability::DigitalInput | PinCapability::DigitalOutput
1363 ),
1364 9..=11 | 15..=16 => matches!(
1365 capability,
1366 PinCapability::DigitalInput | PinCapability::DigitalCounter
1367 ),
1368 14 => matches!(capability, PinCapability::DigitalInput),
1369 17..=18 | 20..=21 => matches!(
1370 capability,
1371 PinCapability::DigitalOutput | PinCapability::PwmOutput
1372 ),
1373 19 => matches!(
1374 capability,
1375 PinCapability::DigitalInput | PinCapability::DigitalCounter
1376 ),
1377 _ => false,
1378 }
1379}
1380
1381static USB_DEVICE_LIST: LazyLock<Mutex<Vec<UsbDeviceInfo>>> =
1384 LazyLock::new(|| Mutex::new(Vec::new()));
1385#[allow(dead_code)]
1386static NETWORK_DEVICE_LIST: LazyLock<Mutex<Vec<NetworkDeviceSummary>>> =
1387 LazyLock::new(|| Mutex::new(Vec::new()));
1388
1389#[derive(Debug, Clone)]
1390#[allow(dead_code)]
1391struct UsbDeviceInfo {
1392 vendor_id: u16,
1393 product_id: u16,
1394 serial_number: Option<String>,
1395 path: String,
1396}
1397
1398pub fn enumerate_usb_devices() -> Result<i32> {
1400 let mut device_list = USB_DEVICE_LIST
1401 .lock()
1402 .map_err(|_| PoKeysError::InternalError("Failed to lock USB device list".to_string()))?;
1403 device_list.clear();
1404
1405 #[cfg(target_os = "macos")]
1407 {
1408 enumerate_usb_devices_macos(&mut device_list)
1409 }
1410 #[cfg(target_os = "linux")]
1411 {
1412 enumerate_usb_devices_linux(&mut device_list)
1413 }
1414 #[cfg(target_os = "windows")]
1415 {
1416 enumerate_usb_devices_windows(&mut device_list)
1417 }
1418 #[cfg(not(any(target_os = "macos", target_os = "linux", target_os = "windows")))]
1419 {
1420 Err(PoKeysError::NotSupported)
1421 }
1422}
1423
1424pub fn enumerate_network_devices(timeout_ms: u32) -> Result<Vec<NetworkDeviceSummary>> {
1426 use crate::network::discover_all_devices;
1427
1428 log::info!("Enumerating network devices with timeout {timeout_ms}ms");
1429
1430 discover_all_devices(timeout_ms)
1432}
1433
1434pub fn connect_to_device(device_index: u32) -> Result<PoKeysDevice> {
1436 let device_list = USB_DEVICE_LIST
1437 .lock()
1438 .map_err(|_| PoKeysError::InternalError("Failed to lock USB device list".to_string()))?;
1439
1440 if device_index as usize >= device_list.len() {
1441 return Err(PoKeysError::Parameter("Invalid device index".to_string()));
1442 }
1443
1444 let device_info = device_list[device_index as usize].clone();
1445 drop(device_list); let mut device = PoKeysDevice::new(DeviceConnectionType::UsbDevice);
1449
1450 #[cfg(target_os = "macos")]
1452 {
1453 device.usb_interface = Some(Box::new(connect_usb_device_macos(&device_info.path)?));
1454 }
1455 #[cfg(target_os = "linux")]
1456 {
1457 device.usb_interface = Some(Box::new(connect_usb_device_linux(&device_info.path)?));
1458 }
1459 #[cfg(target_os = "windows")]
1460 {
1461 device.usb_interface = Some(Box::new(connect_usb_device_windows(&device_info.path)?));
1462 }
1463
1464 device.get_device_data()?;
1466
1467 Ok(device)
1468}
1469
1470pub fn connect_to_device_with_serial(
1472 serial_number: u32,
1473 check_network: bool,
1474 timeout_ms: u32,
1475) -> Result<PoKeysDevice> {
1476 if check_network {
1477 let network_devices = enumerate_network_devices(timeout_ms)?;
1478 for network_device in network_devices {
1479 if network_device.serial_number == serial_number {
1480 return connect_to_network_device(&network_device);
1481 }
1482 }
1483 }
1484
1485 let usb_count = enumerate_usb_devices()?;
1487
1488 for i in 0..usb_count {
1489 if let Ok(device) = connect_to_device(i as u32) {
1490 if device.device_data.serial_number == serial_number {
1491 return Ok(device);
1492 }
1493 }
1494 }
1495
1496 Err(PoKeysError::CannotConnect)
1497}
1498
1499pub fn connect_to_network_device(device_summary: &NetworkDeviceSummary) -> Result<PoKeysDevice> {
1501 let mut device = PoKeysDevice::new(DeviceConnectionType::NetworkDevice);
1502
1503 if device_summary.use_udp != 0 {
1505 device.connection_param = ConnectionParam::Udp;
1506 } else {
1507 device.connection_param = ConnectionParam::Tcp;
1508 }
1509
1510 #[cfg(any(target_os = "macos", target_os = "linux", target_os = "windows"))]
1512 {
1513 device.network_interface = Some(connect_network_device(device_summary)?);
1514 }
1515
1516 device.get_device_data()?;
1518
1519 Ok(device)
1520}
1521
1522struct StubUsbInterface;
1524
1525impl UsbHidInterface for StubUsbInterface {
1526 fn write(&mut self, _data: &[u8]) -> Result<usize> {
1527 Err(PoKeysError::NotSupported)
1528 }
1529
1530 fn read(&mut self, _buffer: &mut [u8]) -> Result<usize> {
1531 Err(PoKeysError::NotSupported)
1532 }
1533
1534 fn read_timeout(&mut self, _buffer: &mut [u8], _timeout: Duration) -> Result<usize> {
1535 Err(PoKeysError::NotSupported)
1536 }
1537}
1538
1539#[cfg(target_os = "macos")]
1540fn enumerate_usb_devices_macos(device_list: &mut Vec<UsbDeviceInfo>) -> Result<i32> {
1541 use std::process::Command;
1542
1543 let output = Command::new("system_profiler")
1545 .args(["SPUSBDataType", "-xml"])
1546 .output()
1547 .map_err(|e| PoKeysError::InternalError(format!("Failed to run system_profiler: {}", e)))?;
1548
1549 if !output.status.success() {
1550 return Err(PoKeysError::InternalError(
1551 "system_profiler command failed".to_string(),
1552 ));
1553 }
1554
1555 let output_str = String::from_utf8_lossy(&output.stdout);
1556
1557 let lines: Vec<&str> = output_str.lines().collect();
1560 let mut i = 0;
1561
1562 while i < lines.len() {
1563 let line = lines[i].trim();
1564
1565 if line.contains("<key>vendor_id</key>") && i + 1 < lines.len() {
1567 let next_line = lines[i + 1].trim();
1568
1569 if next_line.contains("<integer>7619</integer>")
1571 || next_line.contains("<string>0x1dc3</string>")
1572 {
1573 let mut product_id = 0u16;
1575 let mut serial_number = None;
1576 let mut location_id = String::new();
1577
1578 for j in (i + 2)..(i + 50).min(lines.len()) {
1580 let search_line = lines[j].trim();
1581
1582 if search_line.contains("<key>product_id</key>") && j + 1 < lines.len() {
1583 let product_line = lines[j + 1].trim();
1584 if let Some(start) = product_line.find("<integer>") {
1585 if let Some(end) = product_line.find("</integer>") {
1586 if let Ok(pid) = product_line[start + 9..end].parse::<u16>() {
1587 product_id = pid;
1588 }
1589 }
1590 }
1591 }
1592
1593 if search_line.contains("<key>serial_num</key>") && j + 1 < lines.len() {
1594 let serial_line = lines[j + 1].trim();
1595 if let Some(start) = serial_line.find("<string>") {
1596 if let Some(end) = serial_line.find("</string>") {
1597 serial_number = Some(serial_line[start + 8..end].to_string());
1598 }
1599 }
1600 }
1601
1602 if search_line.contains("<key>location_id</key>") && j + 1 < lines.len() {
1603 let location_line = lines[j + 1].trim();
1604 if let Some(start) = location_line.find("<string>") {
1605 if let Some(end) = location_line.find("</string>") {
1606 location_id = location_line[start + 8..end].to_string();
1607 }
1608 }
1609 }
1610
1611 if search_line.contains("<key>vendor_id</key>") && j > i + 2 {
1613 break;
1614 }
1615 }
1616
1617 device_list.push(UsbDeviceInfo {
1619 vendor_id: 0x1DC3,
1620 product_id,
1621 serial_number,
1622 path: format!("macos_usb_{}", location_id),
1623 });
1624 }
1625 }
1626 i += 1;
1627 }
1628
1629 Ok(device_list.len() as i32)
1630}
1631
1632#[cfg(target_os = "linux")]
1633fn enumerate_usb_devices_linux(device_list: &mut Vec<UsbDeviceInfo>) -> Result<i32> {
1634 Ok(device_list.len() as i32)
1638}
1639
1640#[cfg(target_os = "windows")]
1641fn enumerate_usb_devices_windows(device_list: &mut Vec<UsbDeviceInfo>) -> Result<i32> {
1642 Ok(device_list.len() as i32)
1646}
1647
1648#[cfg(any(target_os = "macos", target_os = "linux", target_os = "windows"))]
1650fn connect_network_device(
1651 device_summary: &NetworkDeviceSummary,
1652) -> Result<Box<dyn NetworkInterface>> {
1653 use crate::network::{TcpNetworkInterface, UdpNetworkInterface};
1654
1655 if device_summary.use_udp != 0 {
1656 let interface = UdpNetworkInterface::new(device_summary.ip_address, 20055)?;
1658 Ok(Box::new(interface))
1659 } else {
1660 let interface = TcpNetworkInterface::new(device_summary.ip_address, 20055)?;
1662 Ok(Box::new(interface))
1663 }
1664}
1665
1666#[cfg(target_os = "macos")]
1668fn connect_usb_device_macos(_path: &str) -> Result<StubUsbInterface> {
1669 Err(PoKeysError::NotSupported)
1670}
1671
1672#[cfg(target_os = "linux")]
1673fn connect_usb_device_linux(_path: &str) -> Result<StubUsbInterface> {
1674 Err(PoKeysError::NotSupported)
1675}
1676
1677#[cfg(target_os = "windows")]
1678fn connect_usb_device_windows(_path: &str) -> Result<StubUsbInterface> {
1679 Err(PoKeysError::NotSupported)
1680}
1681
1682fn parse_system_load_response(response: &[u8]) -> u8 {
1686 response[2]
1687}
1688
1689#[cfg(test)]
1690mod tests {
1691 use super::*;
1692
1693 #[test]
1694 fn test_device_creation() {
1695 let device = PoKeysDevice::new(DeviceConnectionType::UsbDevice);
1696 assert_eq!(device.connection_type, DeviceConnectionType::UsbDevice);
1697 assert_eq!(device.pins.len(), 0); }
1699
1700 #[test]
1701 fn test_parse_system_load_response() {
1702 let mut response = [0u8; RESPONSE_BUFFER_SIZE];
1703 response[1] = 0x05; response[2] = 42; assert_eq!(parse_system_load_response(&response), 42);
1706
1707 response[2] = 0;
1708 assert_eq!(parse_system_load_response(&response), 0);
1709
1710 response[2] = 100;
1711 assert_eq!(parse_system_load_response(&response), 100);
1712 }
1713
1714 #[test]
1715 fn test_pin_capability_checking() {
1716 assert!(check_pokeys57cnc_pin_capability(
1718 1,
1719 crate::io::PinCapability::DigitalInput
1720 ));
1721 assert!(check_pokeys57cnc_pin_capability(
1722 1,
1723 crate::io::PinCapability::DigitalOutput
1724 ));
1725 assert!(!check_pokeys57cnc_pin_capability(
1726 14,
1727 crate::io::PinCapability::DigitalOutput
1728 )); assert!(!check_pokeys57cnc_pin_capability(
1730 100,
1731 crate::io::PinCapability::DigitalInput
1732 )); }
1734
1735 #[cfg(feature = "hardware-tests")]
1737 mod hardware_tests {
1738 use super::*;
1739 use std::thread;
1740 use std::time::Duration;
1741
1742 #[test]
1743 fn test_hardware_device_enumeration() {
1744 println!("Testing hardware device enumeration...");
1745
1746 match enumerate_usb_devices() {
1747 Ok(count) => {
1748 println!("Found {} USB PoKeys devices", count);
1749 if count == 0 {
1750 println!(
1751 "WARNING: No PoKeys devices found. Connect a device to run hardware tests."
1752 );
1753 }
1754 }
1755 Err(e) => {
1756 panic!("Failed to enumerate USB devices: {}", e);
1757 }
1758 }
1759 }
1760
1761 #[test]
1762 fn test_hardware_device_connection() {
1763 println!("Testing hardware device connection...");
1764
1765 let device_count = enumerate_usb_devices().expect("Failed to enumerate devices");
1766
1767 if device_count == 0 {
1768 println!("SKIP: No PoKeys devices found for hardware test");
1769 return;
1770 }
1771
1772 match connect_to_device(0) {
1773 Ok(mut device) => {
1774 println!("Successfully connected to device");
1775
1776 match device.get_device_data() {
1778 Ok(_) => {
1779 println!("Device Serial: {}", device.device_data.serial_number);
1780 println!(
1781 "Firmware: {}.{}",
1782 device.device_data.firmware_version_major,
1783 device.device_data.firmware_version_minor
1784 );
1785 println!("Pin Count: {}", device.info.pin_count);
1786 }
1787 Err(e) => {
1788 println!("WARNING: Could not get device data: {}", e);
1789 }
1790 }
1791 }
1792 Err(e) => {
1793 panic!("Failed to connect to device: {}", e);
1794 }
1795 }
1796 }
1797
1798 #[test]
1799 fn test_hardware_digital_io() {
1800 println!("Testing hardware digital I/O...");
1801
1802 let device_count = enumerate_usb_devices().expect("Failed to enumerate devices");
1803 if device_count == 0 {
1804 println!("SKIP: No PoKeys devices found for hardware test");
1805 return;
1806 }
1807
1808 let mut device = connect_to_device(0).expect("Failed to connect to device");
1809 device.get_device_data().expect("Failed to get device data");
1810
1811 match device.set_pin_function(1, crate::io::PinFunction::DigitalOutput) {
1813 Ok(_) => {
1814 println!("Successfully configured pin 1 as digital output");
1815
1816 for state in [true, false, true, false] {
1818 match device.set_digital_output(1, state) {
1819 Ok(_) => {
1820 println!("Set pin 1 to {}", if state { "HIGH" } else { "LOW" })
1821 }
1822 Err(e) => println!("WARNING: Failed to set digital output: {}", e),
1823 }
1824 thread::sleep(Duration::from_millis(100));
1825 }
1826 }
1827 Err(e) => {
1828 println!("WARNING: Could not configure pin function: {}", e);
1829 }
1830 }
1831 }
1832
1833 #[test]
1834 fn test_hardware_analog_input() {
1835 println!("Testing hardware analog input...");
1836
1837 let device_count = enumerate_usb_devices().expect("Failed to enumerate devices");
1838 if device_count == 0 {
1839 println!("SKIP: No PoKeys devices found for hardware test");
1840 return;
1841 }
1842
1843 let mut device = connect_to_device(0).expect("Failed to connect to device");
1844 device.get_device_data().expect("Failed to get device data");
1845
1846 match device.get_analog_input(1) {
1848 Ok(value) => {
1849 println!("Analog input 1 value: {}", value);
1850 assert!(value <= 4095, "Analog value out of range for 12-bit ADC");
1852 }
1853 Err(e) => {
1854 println!("WARNING: Could not read analog input: {}", e);
1855 }
1856 }
1857 }
1858
1859 #[test]
1860 fn test_hardware_pwm_output() {
1861 println!("Testing hardware PWM output...");
1862
1863 let device_count = enumerate_usb_devices().expect("Failed to enumerate devices");
1864 if device_count == 0 {
1865 println!("SKIP: No PoKeys devices found for hardware test");
1866 return;
1867 }
1868
1869 let mut device = connect_to_device(0).expect("Failed to connect to device");
1870 device.get_device_data().expect("Failed to get device data");
1871
1872 match device.set_pwm_period(25000) {
1874 Ok(_) => {
1875 println!("Set PWM period to 25000 cycles");
1876
1877 for duty in [25.0, 50.0, 75.0, 0.0] {
1879 match device.set_pwm_duty_cycle_percent_for_pin(17, duty) {
1880 Ok(_) => {
1881 println!("Set PWM duty cycle to {}%", duty);
1882 thread::sleep(Duration::from_millis(200));
1883 }
1884 Err(e) => {
1885 println!("WARNING: Could not set PWM duty cycle: {}", e);
1886 }
1887 }
1888 }
1889 }
1890 Err(e) => {
1891 println!("WARNING: Could not configure PWM: {}", e);
1892 }
1893 }
1894 }
1895
1896 #[test]
1897 fn test_hardware_encoder_reading() {
1898 println!("Testing hardware encoder reading...");
1899
1900 let device_count = enumerate_usb_devices().expect("Failed to enumerate devices");
1901 if device_count == 0 {
1902 println!("SKIP: No PoKeys devices found for hardware test");
1903 return;
1904 }
1905
1906 let mut device = connect_to_device(0).expect("Failed to connect to device");
1907 device.get_device_data().expect("Failed to get device data");
1908
1909 let mut options = crate::encoders::EncoderOptions::new();
1911 options.enabled = true;
1912 options.sampling_4x = true;
1913
1914 match device.configure_encoder(0, 1, 2, options) {
1915 Ok(_) => {
1916 println!("Configured encoder 0 on pins 1 and 2");
1917
1918 for i in 0..5 {
1920 match device.get_encoder_value(0) {
1921 Ok(value) => {
1922 println!("Encoder 0 reading {}: {}", i + 1, value);
1923 }
1924 Err(e) => {
1925 println!("WARNING: Could not read encoder: {}", e);
1926 }
1927 }
1928 thread::sleep(Duration::from_millis(100));
1929 }
1930 }
1931 Err(e) => {
1932 println!("WARNING: Could not configure encoder: {}", e);
1933 }
1934 }
1935 }
1936
1937 #[test]
1938 fn test_hardware_network_discovery() {
1939 println!("Testing hardware network device discovery...");
1940
1941 match enumerate_network_devices(2000) {
1942 Ok(devices) => {
1943 println!("Found {} network PoKeys devices", devices.len());
1944
1945 for (i, device) in devices.iter().enumerate() {
1946 println!(
1947 "Network device {}: Serial {}, IP {}.{}.{}.{}",
1948 i + 1,
1949 device.serial_number,
1950 device.ip_address[0],
1951 device.ip_address[1],
1952 device.ip_address[2],
1953 device.ip_address[3]
1954 );
1955 }
1956
1957 if devices.is_empty() {
1958 println!(
1959 "No network devices found - this is normal if no network PoKeys devices are present"
1960 );
1961 }
1962 }
1963 Err(e) => {
1964 println!("WARNING: Network discovery failed: {}", e);
1965 }
1966 }
1967 }
1968
1969 #[test]
1970 fn test_hardware_device_info_validation() {
1971 println!("Testing hardware device info validation...");
1972
1973 let device_count = enumerate_usb_devices().expect("Failed to enumerate devices");
1974 if device_count == 0 {
1975 println!("SKIP: No PoKeys devices found for hardware test");
1976 return;
1977 }
1978
1979 let mut device = connect_to_device(0).expect("Failed to connect to device");
1980 device.get_device_data().expect("Failed to get device data");
1981
1982 assert!(
1984 device.device_data.serial_number > 0,
1985 "Serial number should be non-zero"
1986 );
1987 assert!(device.info.pin_count > 0, "Pin count should be non-zero");
1988 assert!(
1989 device.info.pin_count <= 100,
1990 "Pin count should be reasonable"
1991 );
1992
1993 println!("Device validation passed:");
1994 println!(" Serial: {}", device.device_data.serial_number);
1995 println!(" Pins: {}", device.info.pin_count);
1996 println!(" PWM Channels: {}", device.info.pwm_count);
1997 println!(" Encoders: {}", device.info.encoders_count);
1998 println!(" Analog Inputs: {}", device.info.analog_inputs);
1999 }
2000 }
2001}