pokeys_lib/
encoders.rs

1//! Encoder support for PoKeys devices
2//!
3//! This module implements the complete PoKeys encoder protocol specification,
4//! supporting up to 25 normal encoders, 3 fast encoders, and 1 ultra-fast encoder.
5//!
6//! Features:
7//! - 4x and 2x sampling modes for precise position tracking
8//! - Key mapping for encoder directions
9//! - Bulk operations for efficient multi-encoder management
10//! - Fast and ultra-fast encoder support for high-speed applications
11
12use crate::device::PoKeysDevice;
13use crate::error::{PoKeysError, Result};
14use serde::{Deserialize, Serialize};
15
16/// Maximum number of normal encoders supported
17pub const MAX_ENCODERS: usize = 25;
18
19/// Maximum number of fast encoders supported  
20pub const MAX_FAST_ENCODERS: usize = 3;
21
22/// Ultra-fast encoder index (encoder 25)
23pub const ULTRA_FAST_ENCODER_INDEX: u8 = 25;
24
25/// Encoder configuration options
26#[derive(Debug, Clone, Copy, PartialEq, Eq)]
27pub struct EncoderOptions {
28    /// Enable encoder
29    pub enabled: bool,
30    /// 4x sampling mode (both A and B edges counted)
31    pub sampling_4x: bool,
32    /// 2x sampling mode (only A edges counted)
33    pub sampling_2x: bool,
34    /// Direct key mapping for direction A
35    pub direct_key_mapping_a: bool,
36    /// Macro mapping for direction A
37    pub macro_mapping_a: bool,
38    /// Direct key mapping for direction B
39    pub direct_key_mapping_b: bool,
40    /// Macro mapping for direction B
41    pub macro_mapping_b: bool,
42}
43
44impl Default for EncoderOptions {
45    fn default() -> Self {
46        Self::new()
47    }
48}
49
50impl EncoderOptions {
51    /// Create new encoder options with all features disabled
52    pub fn new() -> Self {
53        Self {
54            enabled: false,
55            sampling_4x: false,
56            sampling_2x: false,
57            direct_key_mapping_a: false,
58            macro_mapping_a: false,
59            direct_key_mapping_b: false,
60            macro_mapping_b: false,
61        }
62    }
63
64    /// Create encoder options with 4x sampling enabled
65    pub fn with_4x_sampling() -> Self {
66        Self {
67            enabled: true,
68            sampling_4x: true,
69            sampling_2x: false,
70            direct_key_mapping_a: false,
71            macro_mapping_a: false,
72            direct_key_mapping_b: false,
73            macro_mapping_b: false,
74        }
75    }
76
77    /// Create encoder options with 2x sampling enabled
78    pub fn with_2x_sampling() -> Self {
79        Self {
80            enabled: true,
81            sampling_4x: false,
82            sampling_2x: true,
83            direct_key_mapping_a: false,
84            macro_mapping_a: false,
85            direct_key_mapping_b: false,
86            macro_mapping_b: false,
87        }
88    }
89
90    /// Convert options to protocol byte format
91    /// Bit layout: [macro_b][key_b][macro_a][key_a][reserved][2x][4x][enable]
92    pub fn to_byte(&self) -> u8 {
93        let mut options = 0u8;
94        if self.enabled {
95            options |= 1 << 0;
96        }
97        if self.sampling_4x {
98            options |= 1 << 1;
99        }
100        if self.sampling_2x {
101            options |= 1 << 2;
102        }
103        // bit 3 is reserved
104        if self.direct_key_mapping_a {
105            options |= 1 << 4;
106        }
107        if self.macro_mapping_a {
108            options |= 1 << 5;
109        }
110        if self.direct_key_mapping_b {
111            options |= 1 << 6;
112        }
113        if self.macro_mapping_b {
114            options |= 1 << 7;
115        }
116        options
117    }
118
119    /// Create options from protocol byte format
120    pub fn from_byte(byte: u8) -> Self {
121        Self {
122            enabled: (byte & (1 << 0)) != 0,
123            sampling_4x: (byte & (1 << 1)) != 0,
124            sampling_2x: (byte & (1 << 2)) != 0,
125            direct_key_mapping_a: (byte & (1 << 4)) != 0,
126            macro_mapping_a: (byte & (1 << 5)) != 0,
127            direct_key_mapping_b: (byte & (1 << 6)) != 0,
128            macro_mapping_b: (byte & (1 << 7)) != 0,
129        }
130    }
131}
132
133/// Encoder data structure containing all encoder state and configuration
134#[derive(Debug, Clone, Serialize, Deserialize)]
135pub struct EncoderData {
136    /// Current encoder value (32-bit signed)
137    pub encoder_value: i32,
138    /// Encoder configuration options
139    pub encoder_options: u8,
140    /// Channel A input pin (0-54)
141    pub channel_a_pin: u8,
142    /// Channel B input pin (0-54)
143    pub channel_b_pin: u8,
144    /// Direction A key code for keyboard mapping
145    pub dir_a_key_code: u8,
146    /// Direction A key modifier for keyboard mapping
147    pub dir_a_key_modifier: u8,
148    /// Direction B key code for keyboard mapping
149    pub dir_b_key_code: u8,
150    /// Direction B key modifier for keyboard mapping
151    pub dir_b_key_modifier: u8,
152}
153
154impl EncoderData {
155    /// Create new encoder data with default values
156    pub fn new() -> Self {
157        Self {
158            encoder_value: 0,
159            encoder_options: 0,
160            channel_a_pin: 0,
161            channel_b_pin: 0,
162            dir_a_key_code: 0,
163            dir_a_key_modifier: 0,
164            dir_b_key_code: 0,
165            dir_b_key_modifier: 0,
166        }
167    }
168
169    /// Get encoder options as structured data
170    pub fn get_options(&self) -> EncoderOptions {
171        EncoderOptions::from_byte(self.encoder_options)
172    }
173
174    /// Set encoder options from structured data
175    pub fn set_options(&mut self, options: EncoderOptions) {
176        self.encoder_options = options.to_byte();
177    }
178
179    /// Check if encoder is enabled
180    pub fn is_enabled(&self) -> bool {
181        (self.encoder_options & 1) != 0
182    }
183
184    /// Check if 4x sampling is enabled
185    pub fn is_4x_sampling(&self) -> bool {
186        (self.encoder_options & (1 << 1)) != 0
187    }
188
189    /// Check if 2x sampling is enabled
190    pub fn is_2x_sampling(&self) -> bool {
191        (self.encoder_options & (1 << 2)) != 0
192    }
193
194    /// Get sampling mode as string for debugging
195    pub fn sampling_mode_str(&self) -> &'static str {
196        if self.is_4x_sampling() {
197            "4x (both edges)"
198        } else if self.is_2x_sampling() {
199            "2x (A edges only)"
200        } else {
201            "1x (disabled)"
202        }
203    }
204}
205
206impl Default for EncoderData {
207    fn default() -> Self {
208        Self::new()
209    }
210}
211
212impl PoKeysDevice {
213    /// Configure encoder with pins and options
214    /// Protocol: 0x11 - Individual encoder settings (per protocol spec)
215    pub fn configure_encoder(
216        &mut self,
217        encoder_id: u8,
218        channel_a_pin: u8,
219        channel_b_pin: u8,
220        options: EncoderOptions,
221    ) -> Result<()> {
222        if encoder_id as usize >= self.encoders.len() {
223            return Err(PoKeysError::Parameter(format!(
224                "Invalid encoder ID: {}",
225                encoder_id
226            )));
227        }
228
229        // Convert 1-based pin numbers to 0-based for protocol
230        // The calling code uses 1-based pin numbers, but the protocol expects 0-based
231        let protocol_pin_a = if channel_a_pin > 0 {
232            channel_a_pin - 1
233        } else {
234            0
235        };
236        let protocol_pin_b = if channel_b_pin > 0 {
237            channel_b_pin - 1
238        } else {
239            0
240        };
241
242        let encoder = &mut self.encoders[encoder_id as usize];
243        encoder.channel_a_pin = protocol_pin_a; // Store 0-based internally
244        encoder.channel_b_pin = protocol_pin_b; // Store 0-based internally
245        encoder.set_options(options);
246
247        log::info!(
248            "Configuring encoder {} with pins A={}, B={} (1-based: A={}, B={}), options={:08b} using protocol 0x11",
249            encoder_id,
250            protocol_pin_a,
251            protocol_pin_b,
252            channel_a_pin,
253            channel_b_pin,
254            options.to_byte()
255        );
256
257        // Use protocol command 0x11 for individual encoder configuration
258        // Per spec: byte 2: 0x11, byte 3: encoder ID (0-25), byte 4: option, byte 5: channel A, byte 6: channel B
259        let response = self.send_request(
260            0x11,              // Command: Encoder settings (per protocol spec)
261            encoder_id,        // Encoder ID (0-25)
262            options.to_byte(), // Options byte
263            protocol_pin_a,    // Channel A input pin (0-based)
264            protocol_pin_b,    // Channel B input pin (0-based)
265        )?;
266
267        log::info!("Encoder configuration response: {:?}", &response[0..8]);
268
269        // Check response status per spec: byte 3 (index 2) = 0 = OK, 1 = encoder ID out of range or configuration locked
270        if response.len() > 2 {
271            match response[2] {
272                // Status is at index 2 (spec byte 3)
273                0 => {
274                    log::info!("Encoder {} configuration successful", encoder_id);
275                }
276                1 => {
277                    return Err(PoKeysError::Protocol(format!(
278                        "Encoder {} configuration failed: encoder ID out of range or configuration locked",
279                        encoder_id
280                    )));
281                }
282                other => {
283                    return Err(PoKeysError::Protocol(format!(
284                        "Encoder {} configuration failed with status: {} (0x{:02X})",
285                        encoder_id, other, other
286                    )));
287                }
288            }
289        }
290
291        Ok(())
292    }
293
294    /// Read encoder settings using protocol 0x16
295    pub fn read_encoder_settings(&mut self, encoder_id: u8) -> Result<EncoderData> {
296        if encoder_id >= MAX_ENCODERS as u8 {
297            return Err(PoKeysError::Parameter(format!(
298                "Encoder ID {} exceeds maximum {}",
299                encoder_id, MAX_ENCODERS
300            )));
301        }
302
303        log::info!(
304            "Reading encoder {} settings using protocol 0x16",
305            encoder_id
306        );
307
308        // Use protocol command 0x16 for reading individual encoder settings
309        // Per spec: byte 2: 0x16, byte 3: encoder (0-25), byte 4-6: 0
310        let response = self.send_request(
311            0x16,       // Command: Read encoder settings
312            encoder_id, // Encoder ID (0-25)
313            0,          // Reserved
314            0,          // Reserved
315            0,          // Reserved
316        )?;
317
318        log::info!("Read encoder settings response: {:?}", &response[0..8]);
319
320        // Parse response per spec, accounting for header byte:
321        // Index 0: header (0xAA), Index 1: command (0x16), Index 2: encoder, Index 3: options, Index 4: channel A, Index 5: channel B
322        if response.len() >= 6 {
323            let returned_encoder_id = response[2]; // Encoder ID at index 2
324            let options_byte = response[3]; // Options at index 3
325            let channel_a_pin = response[4]; // Channel A pin at index 4
326            let channel_b_pin = response[5]; // Channel B pin at index 5
327
328            if returned_encoder_id != encoder_id {
329                log::warn!(
330                    "Response encoder ID {} doesn't match requested {}",
331                    returned_encoder_id,
332                    encoder_id
333                );
334            }
335
336            // Convert 0-based protocol pins to 1-based for display/API consistency
337            let display_pin_a = channel_a_pin + 1;
338            let display_pin_b = channel_b_pin + 1;
339
340            let settings = EncoderData {
341                channel_a_pin: display_pin_a, // Return 1-based pin numbers
342                channel_b_pin: display_pin_b, // Return 1-based pin numbers
343                encoder_options: options_byte,
344                ..Default::default()
345            };
346
347            log::info!(
348                "Encoder {} settings: A={}, B={} (protocol: A={}, B={}), options={:08b}",
349                encoder_id,
350                display_pin_a,
351                display_pin_b,
352                channel_a_pin,
353                channel_b_pin,
354                options_byte
355            );
356
357            Ok(settings)
358        } else {
359            Err(PoKeysError::Protocol(
360                "Invalid encoder settings response length".to_string(),
361            ))
362        }
363    }
364
365    /// Enable or disable encoder
366    pub fn enable_encoder(&mut self, encoder_id: u8, enable: bool) -> Result<()> {
367        if encoder_id as usize >= self.encoders.len() {
368            return Err(PoKeysError::Parameter(format!(
369                "Invalid encoder ID: {}",
370                encoder_id
371            )));
372        }
373
374        // Get current pin assignments
375        let (channel_a_pin, channel_b_pin) = {
376            let encoder = &self.encoders[encoder_id as usize];
377            (encoder.channel_a_pin, encoder.channel_b_pin)
378        };
379
380        // Update options
381        let mut options = {
382            let encoder = &self.encoders[encoder_id as usize];
383            encoder.get_options()
384        };
385        options.enabled = enable;
386
387        self.configure_encoder(encoder_id, channel_a_pin, channel_b_pin, options)
388    }
389
390    /// Set encoder sampling mode (4x or 2x)
391    pub fn set_encoder_sampling(
392        &mut self,
393        encoder_id: u8,
394        sampling_4x: bool,
395        sampling_2x: bool,
396    ) -> Result<()> {
397        if encoder_id as usize >= self.encoders.len() {
398            return Err(PoKeysError::Parameter(format!(
399                "Invalid encoder ID: {}",
400                encoder_id
401            )));
402        }
403
404        // Validate sampling mode - only one can be active
405        if sampling_4x && sampling_2x {
406            return Err(PoKeysError::Parameter(
407                "Cannot enable both 4x and 2x sampling simultaneously".to_string(),
408            ));
409        }
410
411        // Get current pin assignments
412        let (channel_a_pin, channel_b_pin) = {
413            let encoder = &self.encoders[encoder_id as usize];
414            (encoder.channel_a_pin, encoder.channel_b_pin)
415        };
416
417        // Update options
418        let mut options = {
419            let encoder = &self.encoders[encoder_id as usize];
420            encoder.get_options()
421        };
422        options.sampling_4x = sampling_4x;
423        options.sampling_2x = sampling_2x;
424
425        self.configure_encoder(encoder_id, channel_a_pin, channel_b_pin, options)
426    }
427
428    /// Configure encoder key mapping for direction A
429    /// Protocol: 0x12 - Encoder key mapping for direction A
430    pub fn configure_encoder_key_mapping_a(
431        &mut self,
432        encoder_id: u8,
433        key_code: u8,
434        key_modifier: u8,
435    ) -> Result<()> {
436        if encoder_id as usize >= self.encoders.len() {
437            return Err(PoKeysError::Parameter(format!(
438                "Invalid encoder ID: {}",
439                encoder_id
440            )));
441        }
442
443        let encoder = &mut self.encoders[encoder_id as usize];
444        encoder.dir_a_key_code = key_code;
445        encoder.dir_a_key_modifier = key_modifier;
446
447        // Send key mapping configuration using protocol command 0x12
448        let response = self.send_request(
449            0x12,         // Command: Encoder key mapping for direction A
450            encoder_id,   // Encoder ID (0-25)
451            0,            // Reserved
452            key_code,     // Key code or macro ID
453            key_modifier, // Key modifier
454        )?;
455
456        // Check response status
457        if response.len() > 3 && response[3] != 0 {
458            return Err(PoKeysError::Protocol(format!(
459                "Encoder key mapping A failed for encoder {}: status {}",
460                encoder_id, response[3]
461            )));
462        }
463
464        Ok(())
465    }
466
467    /// Configure encoder key mapping for direction B
468    /// Protocol: 0x13 - Encoder key mapping for direction B
469    pub fn configure_encoder_key_mapping_b(
470        &mut self,
471        encoder_id: u8,
472        key_code: u8,
473        key_modifier: u8,
474    ) -> Result<()> {
475        if encoder_id as usize >= self.encoders.len() {
476            return Err(PoKeysError::Parameter(format!(
477                "Invalid encoder ID: {}",
478                encoder_id
479            )));
480        }
481
482        let encoder = &mut self.encoders[encoder_id as usize];
483        encoder.dir_b_key_code = key_code;
484        encoder.dir_b_key_modifier = key_modifier;
485
486        // Send key mapping configuration using protocol command 0x13
487        let response = self.send_request(
488            0x13,         // Command: Encoder key mapping for direction B
489            encoder_id,   // Encoder ID (0-25)
490            0,            // Reserved
491            key_code,     // Key code or macro ID
492            key_modifier, // Key modifier
493        )?;
494
495        // Check response status
496        if response.len() > 3 && response[3] != 0 {
497            return Err(PoKeysError::Protocol(format!(
498                "Encoder key mapping B failed for encoder {}: status {}",
499                encoder_id, response[3]
500            )));
501        }
502
503        Ok(())
504    }
505
506    /// Read encoder key mapping for direction A
507    /// Protocol: 0x17 - Read encoder key mapping for direction A
508    pub fn read_encoder_key_mapping_a(&mut self, encoder_id: u8) -> Result<(u8, u8)> {
509        if encoder_id as usize >= self.encoders.len() {
510            return Err(PoKeysError::Parameter(format!(
511                "Invalid encoder ID: {}",
512                encoder_id
513            )));
514        }
515
516        let response = self.send_request(
517            0x17,       // Command: Read encoder key mapping for direction A
518            encoder_id, // Encoder ID (0-25)
519            0,          // Reserved
520            0,          // Reserved
521            0,          // Reserved
522        )?;
523
524        if response.len() < 8 {
525            return Err(PoKeysError::Protocol("Invalid response length".to_string()));
526        }
527
528        // Parse response: byte 5 = key code, byte 6 = key modifier
529        let key_code = response[5];
530        let key_modifier = response[6];
531
532        // Update local cache
533        self.encoders[encoder_id as usize].dir_a_key_code = key_code;
534        self.encoders[encoder_id as usize].dir_a_key_modifier = key_modifier;
535
536        Ok((key_code, key_modifier))
537    }
538
539    /// Read encoder key mapping for direction B
540    /// Protocol: 0x18 - Read encoder key mapping for direction B
541    pub fn read_encoder_key_mapping_b(&mut self, encoder_id: u8) -> Result<(u8, u8)> {
542        if encoder_id as usize >= self.encoders.len() {
543            return Err(PoKeysError::Parameter(format!(
544                "Invalid encoder ID: {}",
545                encoder_id
546            )));
547        }
548
549        let response = self.send_request(
550            0x18,       // Command: Read encoder key mapping for direction B
551            encoder_id, // Encoder ID (0-25)
552            0,          // Reserved
553            0,          // Reserved
554            0,          // Reserved
555        )?;
556
557        if response.len() < 8 {
558            return Err(PoKeysError::Protocol("Invalid response length".to_string()));
559        }
560
561        // Parse response: byte 5 = key code, byte 6 = key modifier
562        let key_code = response[5];
563        let key_modifier = response[6];
564
565        // Update local cache
566        self.encoders[encoder_id as usize].dir_b_key_code = key_code;
567        self.encoders[encoder_id as usize].dir_b_key_modifier = key_modifier;
568
569        Ok((key_code, key_modifier))
570    }
571
572    /// Read encoder RAW value
573    /// Protocol: 0x19 - Read encoder RAW value
574    pub fn read_encoder_raw_value(&mut self, encoder_id: u8) -> Result<i32> {
575        if encoder_id as usize >= self.encoders.len() {
576            return Err(PoKeysError::Parameter(format!(
577                "Invalid encoder ID: {}",
578                encoder_id
579            )));
580        }
581
582        let response = self.send_request(
583            0x19,       // Command: Read encoder RAW value
584            encoder_id, // Encoder ID (0-25)
585            0,          // Reserved
586            0,          // Reserved
587            0,          // Reserved
588        )?;
589
590        if response.len() < 8 {
591            return Err(PoKeysError::Protocol("Invalid response length".to_string()));
592        }
593
594        // Parse response: byte 4 = RAW value (8-bit for individual read)
595        // Note: For full 32-bit values, use bulk read operations
596        let raw_value = response[4] as i8 as i32; // Sign-extend 8-bit to 32-bit
597
598        // Update local cache
599        self.encoders[encoder_id as usize].encoder_value = raw_value;
600
601        Ok(raw_value)
602    }
603
604    /// Reset encoder RAW value to zero
605    /// Protocol: 0x1A - Reset encoder RAW value
606    pub fn reset_encoder_raw_value(&mut self, encoder_id: u8) -> Result<()> {
607        if encoder_id as usize >= self.encoders.len() {
608            return Err(PoKeysError::Parameter(format!(
609                "Invalid encoder ID: {}",
610                encoder_id
611            )));
612        }
613
614        let response = self.send_request(
615            0x1A,       // Command: Reset encoder RAW value
616            encoder_id, // Encoder ID (0-25)
617            0,          // Reserved
618            0,          // Reserved
619            0,          // Reserved
620        )?;
621
622        // Check if command was successful (no specific error checking in protocol)
623        if response.len() < 8 {
624            return Err(PoKeysError::Protocol("Invalid response length".to_string()));
625        }
626
627        // Update local cache
628        self.encoders[encoder_id as usize].encoder_value = 0;
629
630        Ok(())
631    }
632
633    /// Get encoder value (convenience method)
634    pub fn get_encoder_value(&mut self, encoder_id: u8) -> Result<i32> {
635        self.read_encoder_raw_value(encoder_id)
636    }
637
638    /// Reset encoder value (convenience method)
639    pub fn reset_encoder(&mut self, encoder_id: u8) -> Result<()> {
640        self.reset_encoder_raw_value(encoder_id)
641    }
642
643    /// Get encoder long RAW values (bulk operation)
644    /// Protocol: 0xCD - Get encoder long RAW values
645    /// option: 0 = encoders 1-13, 1 = encoders 14-26
646    pub fn read_encoder_long_values(&mut self, group: u8) -> Result<Vec<i32>> {
647        if group > 1 {
648            return Err(PoKeysError::Parameter(
649                "Group must be 0 (encoders 1-13) or 1 (encoders 14-26)".to_string(),
650            ));
651        }
652
653        let response = self.send_request(
654            0xCD,  // Command: Get encoder long RAW values
655            group, // Option: 0 or 1
656            0,     // Reserved
657            0,     // Reserved
658            0,     // Reserved
659        )?;
660
661        if response.len() < 64 {
662            return Err(PoKeysError::Protocol(
663                "Invalid response length for bulk encoder read".to_string(),
664            ));
665        }
666
667        let mut values = Vec::new();
668
669        // Parse 32-bit values starting from byte 8 (protocol spec says byte 9, but that's 1-based)
670        // Group 0: encoders 1-13, Group 1: encoders 14-26
671        for i in 0..13 {
672            let byte_offset = 8 + (i * 4); // Start at byte 8 in 0-based array, 4 bytes per encoder
673            if byte_offset + 3 < response.len() {
674                let value = i32::from_le_bytes([
675                    response[byte_offset],
676                    response[byte_offset + 1],
677                    response[byte_offset + 2],
678                    response[byte_offset + 3],
679                ]);
680                values.push(value);
681
682                // Update local cache - map to 0-based encoder IDs
683                let encoder_index = if group == 0 { i } else { 13 + i };
684                if encoder_index < self.encoders.len() {
685                    self.encoders[encoder_index].encoder_value = value;
686                }
687            }
688        }
689
690        // Handle ultra-fast encoder for group 1 (bytes 56-59 in 0-based array)
691        if group == 1 && response.len() >= 60 {
692            let ultra_fast_value = i32::from_le_bytes([
693                response[56], // Protocol spec byte 57 = array index 56
694                response[57], // Protocol spec byte 58 = array index 57
695                response[58], // Protocol spec byte 59 = array index 58
696                response[59], // Protocol spec byte 60 = array index 59
697            ]);
698            // Ultra-fast encoder is at index 25 (encoder 26 in 1-based numbering)
699            if self.encoders.len() > 25 {
700                self.encoders[25].encoder_value = ultra_fast_value;
701            }
702        }
703
704        log::info!("Bulk read group {} returned {} values", group, values.len());
705        Ok(values)
706    }
707
708    /// Set encoder long RAW values (bulk operation)
709    /// Protocol: 0xCD - Set encoder long RAW values
710    /// option: 10 = encoders 1-13, 11 = encoders 14-26
711    pub fn set_encoder_long_values(&mut self, group: u8, values: &[i32]) -> Result<()> {
712        if group > 1 {
713            return Err(PoKeysError::Parameter(
714                "Group must be 0 (encoders 1-13) or 1 (encoders 14-26)".to_string(),
715            ));
716        }
717
718        let expected_count = if group == 0 { 13 } else { 12 }; // Group 1 has 12 regular + ultra-fast
719        if values.len() < expected_count {
720            return Err(PoKeysError::Parameter(format!(
721                "Need {} values for group {}",
722                expected_count, group
723            )));
724        }
725
726        // Prepare request with values
727        let mut request = vec![0u8; 64];
728        request[2] = 0xCD; // Command
729        request[3] = group + 10; // Option: 10 or 11 for set operation
730        request[7] = self.get_next_request_id(); // Request ID
731
732        // Pack 32-bit values starting from byte 9
733        for (i, &value) in values.iter().enumerate() {
734            let byte_offset = 9 + (i * 4);
735            if byte_offset + 3 < request.len() {
736                let bytes = value.to_le_bytes();
737                request[byte_offset] = bytes[0];
738                request[byte_offset + 1] = bytes[1];
739                request[byte_offset + 2] = bytes[2];
740                request[byte_offset + 3] = bytes[3];
741            }
742        }
743
744        let _response = self.send_raw_request(&request)?;
745
746        // Update local cache
747        let start_encoder = if group == 0 { 1 } else { 14 };
748        for (i, &value) in values.iter().enumerate() {
749            let encoder_index = start_encoder + i;
750            if encoder_index < self.encoders.len() {
751                self.encoders[encoder_index].encoder_value = value;
752            }
753        }
754
755        Ok(())
756    }
757
758    /// Read all encoder values using bulk operations (more efficient)
759    pub fn read_all_encoder_values(&mut self) -> Result<Vec<i32>> {
760        let mut all_values = Vec::new();
761
762        // Read encoders 1-13
763        let group1_values = self.read_encoder_long_values(0)?;
764        all_values.extend(group1_values);
765
766        // Read encoders 14-25 + ultra-fast
767        let group2_values = self.read_encoder_long_values(1)?;
768        all_values.extend(group2_values);
769
770        Ok(all_values)
771    }
772
773    /// Configure encoder options (bulk operation)
774    /// Protocol: 0xC4 - Encoder option
775    pub fn configure_encoder_options_bulk(&mut self, options: &[u8]) -> Result<Vec<u8>> {
776        if options.len() != 25 {
777            return Err(PoKeysError::Parameter(
778                "Need exactly 25 encoder options".to_string(),
779            ));
780        }
781
782        let mut request = vec![0u8; 64];
783        request[2] = 0xC4; // Command
784        request[3] = 1; // Option: 1 = set
785        request[7] = self.get_next_request_id(); // Request ID
786
787        // Copy encoder options to bytes 9-33
788        for (i, &option) in options.iter().enumerate() {
789            request[9 + i] = option;
790        }
791
792        let response = self.send_raw_request(&request)?;
793
794        // Parse returned options from bytes 9-33
795        let mut returned_options = Vec::new();
796        if response.len() >= 34 {
797            for i in 0..25 {
798                returned_options.push(response[9 + i]);
799                // Update local cache
800                if i < self.encoders.len() {
801                    self.encoders[i].encoder_options = response[9 + i];
802                }
803            }
804        }
805
806        Ok(returned_options)
807    }
808
809    /// Read encoder options (bulk operation)
810    /// Protocol: 0xC4 - Encoder option
811    pub fn read_encoder_options_bulk(&mut self) -> Result<Vec<u8>> {
812        let response = self.send_request(
813            0xC4, // Command: Encoder option
814            0,    // Option: 0 = get
815            0,    // Reserved
816            0,    // Reserved
817            0,    // Reserved
818        )?;
819
820        let mut options = Vec::new();
821        if response.len() >= 34 {
822            for i in 0..25 {
823                options.push(response[9 + i]);
824                // Update local cache
825                if i < self.encoders.len() {
826                    self.encoders[i].encoder_options = response[9 + i];
827                }
828            }
829        }
830
831        Ok(options)
832    }
833
834    /// Configure fast encoders
835    /// Protocol: 0xCE - Enable/disable fast encoders on pins 1-2, 3-4/5-6 and 15-16
836    /// Configuration 1: pins 1-2 as encoder 1, pins 3-4 as encoder 2, pins 15-16 as encoder 3
837    /// Configuration 2: pins 1-2 as encoder 1, pins 5-6 as encoder 2, pins 15-16 as encoder 3
838    pub fn configure_fast_encoders(&mut self, options: u8, enable_index: bool) -> Result<()> {
839        self.fast_encoders_options = options;
840
841        let response = self.send_request(
842            0xCE,                             // Command: Enable/disable fast encoders
843            options,                          // Options byte with encoder configuration
844            if enable_index { 1 } else { 0 }, // Enable index signal
845            0,                                // Reserved
846            0,                                // Reserved
847        )?;
848
849        // Check response status
850        if response.len() > 3 {
851            let status = response[3];
852            if status != 0 {
853                return Err(PoKeysError::Protocol(format!(
854                    "Fast encoder configuration failed: status {}",
855                    status
856                )));
857            }
858        }
859
860        Ok(())
861    }
862
863    /// Read fast encoder values
864    pub fn read_fast_encoder_values(&mut self) -> Result<[i32; 3]> {
865        // Fast encoders use the bulk read operation for encoders 0, 1, 2
866        let values = self.read_encoder_long_values(0)?;
867
868        let mut fast_values = [0i32; 3];
869        let copy_len = 3.min(values.len());
870        fast_values[..copy_len].copy_from_slice(&values[..copy_len]);
871
872        Ok(fast_values)
873    }
874
875    /// Configure ultra-fast encoder (PoKeys56E only)
876    /// Protocol: 0x1C - Enable/disable ultra fast encoder
877    /// Pins: Pin 8 (Phase A), Pin 12 (Phase B), Pin 13 (Index)
878    pub fn configure_ultra_fast_encoder(
879        &mut self,
880        enable: bool,
881        enable_4x_sampling: bool,
882        signal_mode_direction_clock: bool,
883        invert_direction: bool,
884        reset_on_index: bool,
885        filter_delay: u32,
886    ) -> Result<()> {
887        // Build options byte
888        let mut options = 0u8;
889        if enable_4x_sampling {
890            options |= 1 << 1; // Enable 4x sampling
891        }
892        if signal_mode_direction_clock {
893            options |= 1 << 2; // Signal mode: A=direction, B=clock
894        }
895        if invert_direction {
896            options |= 1 << 3; // Invert direction
897        }
898
899        self.ultra_fast_encoder_configuration = if enable { 1 } else { 0 };
900        self.ultra_fast_encoder_options = options;
901        self.ultra_fast_encoder_filter = filter_delay;
902
903        // Prepare request with filter delay in bytes 9-12
904        let mut request = vec![0u8; 64];
905        request[2] = 0x1C; // Command
906        request[3] = if enable { 1 } else { 0 }; // Enable/disable
907        request[4] = options; // Additional options
908        request[5] = if reset_on_index { 1 } else { 0 }; // Reset on index
909        request[6] = 0; // Reserved
910        request[7] = self.get_next_request_id(); // Request ID
911
912        // Pack filter delay as 32-bit little-endian in bytes 9-12
913        let filter_bytes = filter_delay.to_le_bytes();
914        request[9] = filter_bytes[0];
915        request[10] = filter_bytes[1];
916        request[11] = filter_bytes[2];
917        request[12] = filter_bytes[3];
918
919        let response = self.send_raw_request(&request)?;
920
921        // Check response status
922        if response.len() > 3 {
923            let status = response[3];
924            if status != 0 {
925                return Err(PoKeysError::Protocol(format!(
926                    "Ultra-fast encoder configuration failed: status {}",
927                    status
928                )));
929            }
930        }
931
932        Ok(())
933    }
934
935    /// Read ultra-fast encoder configuration
936    /// Protocol: 0x1C with enable = 0xFF to read configuration
937    pub fn read_ultra_fast_encoder_config(&mut self) -> Result<(bool, u8, u32)> {
938        let mut request = vec![0u8; 64];
939        request[2] = 0x1C; // Command
940        request[3] = 0xFF; // Read configuration
941        request[7] = self.get_next_request_id(); // Request ID
942
943        let response = self.send_raw_request(&request)?;
944
945        if response.len() < 13 {
946            return Err(PoKeysError::Protocol("Invalid response length".to_string()));
947        }
948
949        let enabled = response[3] != 0;
950        let options = response[4];
951        let filter_delay =
952            u32::from_le_bytes([response[9], response[10], response[11], response[12]]);
953
954        Ok((enabled, options, filter_delay))
955    }
956
957    /// Read ultra-fast encoder value
958    pub fn read_ultra_fast_encoder_value(&mut self) -> Result<i32> {
959        // Ultra-fast encoder is included in bulk read group 1
960        let values = self.read_encoder_long_values(1)?;
961
962        // Ultra-fast encoder is the last value in group 1
963        if let Some(&value) = values.last() {
964            Ok(value)
965        } else {
966            Err(PoKeysError::Protocol(
967                "No ultra-fast encoder value in response".to_string(),
968            ))
969        }
970    }
971
972    /// Set ultra-fast encoder value
973    pub fn set_ultra_fast_encoder_value(&mut self, value: i32) -> Result<()> {
974        // Use bulk set operation for group 1 with only the ultra-fast encoder value
975        // We need to read current values first, then set only the ultra-fast one
976        let mut values = self.read_encoder_long_values(1)?;
977
978        // Set the ultra-fast encoder value (last in the array)
979        if let Some(last) = values.last_mut() {
980            *last = value;
981        } else {
982            return Err(PoKeysError::Protocol(
983                "Cannot set ultra-fast encoder value".to_string(),
984            ));
985        }
986
987        self.set_encoder_long_values(1, &values)
988    }
989    /// Configure encoder with keyboard mapping (convenience method)
990    #[allow(clippy::too_many_arguments)]
991    pub fn configure_encoder_with_keys(
992        &mut self,
993        encoder_id: u8,
994        channel_a_pin: u8,
995        channel_b_pin: u8,
996        sampling_4x: bool,
997        sampling_2x: bool,
998        dir_a_key_code: u8,
999        dir_a_key_modifier: u8,
1000        dir_b_key_code: u8,
1001        dir_b_key_modifier: u8,
1002    ) -> Result<()> {
1003        // Configure basic encoder settings
1004        let mut options = EncoderOptions::new();
1005        options.enabled = true;
1006        options.sampling_4x = sampling_4x;
1007        options.sampling_2x = sampling_2x;
1008        options.direct_key_mapping_a = true;
1009        options.direct_key_mapping_b = true;
1010
1011        self.configure_encoder(encoder_id, channel_a_pin, channel_b_pin, options)?;
1012
1013        // Configure key mappings
1014        self.configure_encoder_key_mapping_a(encoder_id, dir_a_key_code, dir_a_key_modifier)?;
1015        self.configure_encoder_key_mapping_b(encoder_id, dir_b_key_code, dir_b_key_modifier)?;
1016
1017        Ok(())
1018    }
1019
1020    /// Get encoder sampling mode as string (for debugging/display)
1021    pub fn get_encoder_sampling_mode(&self, encoder_id: u8) -> Result<String> {
1022        if encoder_id as usize >= self.encoders.len() {
1023            return Err(PoKeysError::Parameter(format!(
1024                "Invalid encoder ID: {}",
1025                encoder_id
1026            )));
1027        }
1028
1029        let encoder = &self.encoders[encoder_id as usize];
1030        Ok(encoder.sampling_mode_str().to_string())
1031    }
1032
1033    /// Check if encoder is configured for 4x sampling
1034    pub fn is_encoder_4x_sampling(&self, encoder_id: u8) -> Result<bool> {
1035        if encoder_id as usize >= self.encoders.len() {
1036            return Err(PoKeysError::Parameter(format!(
1037                "Invalid encoder ID: {}",
1038                encoder_id
1039            )));
1040        }
1041
1042        Ok(self.encoders[encoder_id as usize].is_4x_sampling())
1043    }
1044
1045    /// Check if encoder is configured for 2x sampling
1046    pub fn is_encoder_2x_sampling(&self, encoder_id: u8) -> Result<bool> {
1047        if encoder_id as usize >= self.encoders.len() {
1048            return Err(PoKeysError::Parameter(format!(
1049                "Invalid encoder ID: {}",
1050                encoder_id
1051            )));
1052        }
1053
1054        Ok(self.encoders[encoder_id as usize].is_2x_sampling())
1055    }
1056
1057    /// Get all enabled encoders
1058    pub fn get_enabled_encoders(&self) -> Vec<u8> {
1059        self.encoders
1060            .iter()
1061            .enumerate()
1062            .filter_map(|(i, encoder)| {
1063                if encoder.is_enabled() {
1064                    Some(i as u8)
1065                } else {
1066                    None
1067                }
1068            })
1069            .collect()
1070    }
1071
1072    /// Helper method to get next request ID (implement in device)
1073    fn get_next_request_id(&mut self) -> u8 {
1074        // This should be implemented in the device structure
1075        // For now, return a simple incrementing counter
1076        static mut REQUEST_ID: u8 = 0;
1077        unsafe {
1078            REQUEST_ID = REQUEST_ID.wrapping_add(1);
1079            REQUEST_ID
1080        }
1081    }
1082
1083    /// Helper method to send raw request (implement in device)
1084    fn send_raw_request(&mut self, request: &[u8]) -> Result<Vec<u8>> {
1085        // This should use the actual communication interface
1086        // For now, delegate to the existing send_request method and convert array to Vec
1087        if request.len() >= 8 {
1088            let response_array =
1089                self.send_request(request[2], request[3], request[4], request[5], request[6])?;
1090            Ok(response_array.to_vec())
1091        } else {
1092            Err(PoKeysError::Protocol("Invalid request format".to_string()))
1093        }
1094    }
1095}
1096
1097#[cfg(test)]
1098mod tests {
1099    use super::*;
1100
1101    #[test]
1102    fn test_encoder_options_4x_sampling() {
1103        let options = EncoderOptions::with_4x_sampling();
1104        assert!(options.enabled);
1105        assert!(options.sampling_4x);
1106        assert!(!options.sampling_2x);
1107
1108        let byte = options.to_byte();
1109        assert_eq!(byte & 0b00000011, 0b00000011); // enabled + 4x sampling
1110
1111        let options_from_byte = EncoderOptions::from_byte(byte);
1112        assert!(options_from_byte.enabled);
1113        assert!(options_from_byte.sampling_4x);
1114        assert!(!options_from_byte.sampling_2x);
1115    }
1116
1117    #[test]
1118    fn test_encoder_options_2x_sampling() {
1119        let options = EncoderOptions::with_2x_sampling();
1120        assert!(options.enabled);
1121        assert!(!options.sampling_4x);
1122        assert!(options.sampling_2x);
1123
1124        let byte = options.to_byte();
1125        assert_eq!(byte & 0b00000111, 0b00000101); // enabled + 2x sampling
1126
1127        let options_from_byte = EncoderOptions::from_byte(byte);
1128        assert!(options_from_byte.enabled);
1129        assert!(!options_from_byte.sampling_4x);
1130        assert!(options_from_byte.sampling_2x);
1131    }
1132
1133    #[test]
1134    fn test_encoder_options_key_mapping() {
1135        let mut options = EncoderOptions::new();
1136        options.enabled = true;
1137        options.direct_key_mapping_a = true;
1138        options.macro_mapping_b = true;
1139
1140        let byte = options.to_byte();
1141        assert_eq!(byte & 0b11110001, 0b10010001); // enabled + key_a + macro_b
1142
1143        let options_from_byte = EncoderOptions::from_byte(byte);
1144        assert!(options_from_byte.enabled);
1145        assert!(options_from_byte.direct_key_mapping_a);
1146        assert!(options_from_byte.macro_mapping_b);
1147        assert!(!options_from_byte.direct_key_mapping_b);
1148        assert!(!options_from_byte.macro_mapping_a);
1149    }
1150
1151    #[test]
1152    fn test_encoder_data_sampling_modes() {
1153        let mut encoder = EncoderData::new();
1154
1155        // Test 4x sampling
1156        let options_4x = EncoderOptions::with_4x_sampling();
1157        encoder.set_options(options_4x);
1158        assert!(encoder.is_4x_sampling());
1159        assert!(!encoder.is_2x_sampling());
1160        assert_eq!(encoder.sampling_mode_str(), "4x (both edges)");
1161
1162        // Test 2x sampling
1163        let options_2x = EncoderOptions::with_2x_sampling();
1164        encoder.set_options(options_2x);
1165        assert!(!encoder.is_4x_sampling());
1166        assert!(encoder.is_2x_sampling());
1167        assert_eq!(encoder.sampling_mode_str(), "2x (A edges only)");
1168
1169        // Test disabled
1170        let options_disabled = EncoderOptions::new();
1171        encoder.set_options(options_disabled);
1172        assert!(!encoder.is_4x_sampling());
1173        assert!(!encoder.is_2x_sampling());
1174        assert_eq!(encoder.sampling_mode_str(), "1x (disabled)");
1175    }
1176
1177    #[test]
1178    fn test_encoder_constants() {
1179        assert_eq!(MAX_ENCODERS, 25);
1180        assert_eq!(MAX_FAST_ENCODERS, 3);
1181        assert_eq!(ULTRA_FAST_ENCODER_INDEX, 25);
1182    }
1183}