use crate::device::PoKeysDevice;
use crate::error::{PoKeysError, Result};
use serde::{Deserialize, Serialize};
pub const MAX_ENCODERS: usize = 25;
pub const MAX_FAST_ENCODERS: usize = 3;
pub const ULTRA_FAST_ENCODER_INDEX: u8 = 25;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct EncoderOptions {
pub enabled: bool,
pub sampling_4x: bool,
pub sampling_2x: bool,
pub direct_key_mapping_a: bool,
pub macro_mapping_a: bool,
pub direct_key_mapping_b: bool,
pub macro_mapping_b: bool,
}
impl Default for EncoderOptions {
fn default() -> Self {
Self::new()
}
}
impl EncoderOptions {
pub fn new() -> Self {
Self {
enabled: false,
sampling_4x: false,
sampling_2x: false,
direct_key_mapping_a: false,
macro_mapping_a: false,
direct_key_mapping_b: false,
macro_mapping_b: false,
}
}
pub fn with_4x_sampling() -> Self {
Self {
enabled: true,
sampling_4x: true,
sampling_2x: false,
direct_key_mapping_a: false,
macro_mapping_a: false,
direct_key_mapping_b: false,
macro_mapping_b: false,
}
}
pub fn with_2x_sampling() -> Self {
Self {
enabled: true,
sampling_4x: false,
sampling_2x: true,
direct_key_mapping_a: false,
macro_mapping_a: false,
direct_key_mapping_b: false,
macro_mapping_b: false,
}
}
pub fn to_byte(&self) -> u8 {
let mut options = 0u8;
if self.enabled {
options |= 1 << 0;
}
if self.sampling_4x {
options |= 1 << 1;
}
if self.sampling_2x {
options |= 1 << 2;
}
if self.direct_key_mapping_a {
options |= 1 << 4;
}
if self.macro_mapping_a {
options |= 1 << 5;
}
if self.direct_key_mapping_b {
options |= 1 << 6;
}
if self.macro_mapping_b {
options |= 1 << 7;
}
options
}
pub fn from_byte(byte: u8) -> Self {
Self {
enabled: (byte & (1 << 0)) != 0,
sampling_4x: (byte & (1 << 1)) != 0,
sampling_2x: (byte & (1 << 2)) != 0,
direct_key_mapping_a: (byte & (1 << 4)) != 0,
macro_mapping_a: (byte & (1 << 5)) != 0,
direct_key_mapping_b: (byte & (1 << 6)) != 0,
macro_mapping_b: (byte & (1 << 7)) != 0,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FastEncoderConfiguration {
Disabled,
Config1,
Config2,
}
impl FastEncoderConfiguration {
pub fn to_byte(self) -> u8 {
match self {
FastEncoderConfiguration::Disabled => 0x00,
FastEncoderConfiguration::Config1 => 0x01,
FastEncoderConfiguration::Config2 => 0x10,
}
}
pub fn from_byte(byte: u8) -> Self {
match byte {
0x01 => FastEncoderConfiguration::Config1,
0x10 => FastEncoderConfiguration::Config2,
_ => FastEncoderConfiguration::Disabled,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct FastEncoderOptions {
pub disable_4x_sampling: bool,
pub invert_direction_1: bool,
pub invert_direction_2: bool,
pub invert_direction_3: bool,
}
pub(crate) const FAST_ENCODER_DISABLE_4X_SAMPLING: u8 = 0x10;
pub(crate) const FAST_ENCODER_INVERT_E1: u8 = 0x20;
pub(crate) const FAST_ENCODER_INVERT_E2: u8 = 0x40;
pub(crate) const FAST_ENCODER_INVERT_E3: u8 = 0x80;
impl FastEncoderOptions {
pub fn to_byte(self) -> u8 {
let mut b = 0u8;
if self.disable_4x_sampling {
b |= FAST_ENCODER_DISABLE_4X_SAMPLING;
}
if self.invert_direction_1 {
b |= FAST_ENCODER_INVERT_E1;
}
if self.invert_direction_2 {
b |= FAST_ENCODER_INVERT_E2;
}
if self.invert_direction_3 {
b |= FAST_ENCODER_INVERT_E3;
}
b
}
pub fn from_byte(byte: u8) -> Self {
Self {
disable_4x_sampling: (byte & FAST_ENCODER_DISABLE_4X_SAMPLING) != 0,
invert_direction_1: (byte & FAST_ENCODER_INVERT_E1) != 0,
invert_direction_2: (byte & FAST_ENCODER_INVERT_E2) != 0,
invert_direction_3: (byte & FAST_ENCODER_INVERT_E3) != 0,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct UltraFastEncoderOptions {
pub invert_direction: bool,
pub signal_mode_direction_clock: bool,
pub enable_4x_sampling: bool,
}
pub(crate) const UFENC_INVERT_DIRECTION: u8 = 0x01;
pub(crate) const UFENC_SIGNAL_MODE: u8 = 0x02;
pub(crate) const UFENC_ENABLE_4X_SAMPLING: u8 = 0x04;
impl UltraFastEncoderOptions {
pub fn to_byte(self) -> u8 {
let mut b = 0u8;
if self.invert_direction {
b |= UFENC_INVERT_DIRECTION;
}
if self.signal_mode_direction_clock {
b |= UFENC_SIGNAL_MODE;
}
if self.enable_4x_sampling {
b |= UFENC_ENABLE_4X_SAMPLING;
}
b
}
pub fn from_byte(byte: u8) -> Self {
Self {
invert_direction: (byte & UFENC_INVERT_DIRECTION) != 0,
signal_mode_direction_clock: (byte & UFENC_SIGNAL_MODE) != 0,
enable_4x_sampling: (byte & UFENC_ENABLE_4X_SAMPLING) != 0,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EncoderData {
pub encoder_value: i32,
pub encoder_options: u8,
pub channel_a_pin: u8,
pub channel_b_pin: u8,
pub dir_a_key_code: u8,
pub dir_a_key_modifier: u8,
pub dir_b_key_code: u8,
pub dir_b_key_modifier: u8,
}
impl EncoderData {
pub fn new() -> Self {
Self {
encoder_value: 0,
encoder_options: 0,
channel_a_pin: 0,
channel_b_pin: 0,
dir_a_key_code: 0,
dir_a_key_modifier: 0,
dir_b_key_code: 0,
dir_b_key_modifier: 0,
}
}
pub fn get_options(&self) -> EncoderOptions {
EncoderOptions::from_byte(self.encoder_options)
}
pub fn set_options(&mut self, options: EncoderOptions) {
self.encoder_options = options.to_byte();
}
pub fn is_enabled(&self) -> bool {
(self.encoder_options & 1) != 0
}
pub fn is_4x_sampling(&self) -> bool {
(self.encoder_options & (1 << 1)) != 0
}
pub fn is_2x_sampling(&self) -> bool {
(self.encoder_options & (1 << 2)) != 0
}
pub fn sampling_mode_str(&self) -> &'static str {
if self.is_4x_sampling() {
"4x (both edges)"
} else if self.is_2x_sampling() {
"2x (A edges only)"
} else {
"1x (disabled)"
}
}
}
impl Default for EncoderData {
fn default() -> Self {
Self::new()
}
}
impl PoKeysDevice {
pub fn configure_encoder(
&mut self,
encoder_id: u8,
channel_a_pin: u8,
channel_b_pin: u8,
options: EncoderOptions,
) -> Result<()> {
if encoder_id as usize >= self.encoders.len() {
return Err(PoKeysError::Parameter(format!(
"Invalid encoder ID: {}",
encoder_id
)));
}
let protocol_pin_a = if channel_a_pin > 0 {
channel_a_pin - 1
} else {
0
};
let protocol_pin_b = if channel_b_pin > 0 {
channel_b_pin - 1
} else {
0
};
let encoder = &mut self.encoders[encoder_id as usize];
encoder.channel_a_pin = protocol_pin_a; encoder.channel_b_pin = protocol_pin_b; encoder.set_options(options);
log::info!(
"Configuring encoder {} with pins A={}, B={} (1-based: A={}, B={}), options={:08b} using protocol 0x11",
encoder_id,
protocol_pin_a,
protocol_pin_b,
channel_a_pin,
channel_b_pin,
options.to_byte()
);
let response = self.send_request(
0x11, encoder_id, options.to_byte(), protocol_pin_a, protocol_pin_b, )?;
log::info!("Encoder configuration response: {:?}", &response[0..8]);
if response.len() > 2 {
match response[2] {
0 => {
log::info!("Encoder {} configuration successful", encoder_id);
}
1 => {
return Err(PoKeysError::Protocol(format!(
"Encoder {} configuration failed: encoder ID out of range or configuration locked",
encoder_id
)));
}
other => {
return Err(PoKeysError::Protocol(format!(
"Encoder {} configuration failed with status: {} (0x{:02X})",
encoder_id, other, other
)));
}
}
}
Ok(())
}
pub fn read_encoder_settings(&mut self, encoder_id: u8) -> Result<EncoderData> {
if encoder_id >= MAX_ENCODERS as u8 {
return Err(PoKeysError::Parameter(format!(
"Encoder ID {} exceeds maximum {}",
encoder_id, MAX_ENCODERS
)));
}
log::info!(
"Reading encoder {} settings using protocol 0x16",
encoder_id
);
let response = self.send_request(
0x16, encoder_id, 0, 0, 0, )?;
log::info!("Read encoder settings response: {:?}", &response[0..8]);
if response.len() >= 6 {
let returned_encoder_id = response[2]; let options_byte = response[3]; let channel_a_pin = response[4]; let channel_b_pin = response[5];
if returned_encoder_id != encoder_id {
log::warn!(
"Response encoder ID {} doesn't match requested {}",
returned_encoder_id,
encoder_id
);
}
let display_pin_a = channel_a_pin + 1;
let display_pin_b = channel_b_pin + 1;
let settings = EncoderData {
channel_a_pin: display_pin_a, channel_b_pin: display_pin_b, encoder_options: options_byte,
..Default::default()
};
log::info!(
"Encoder {} settings: A={}, B={} (protocol: A={}, B={}), options={:08b}",
encoder_id,
display_pin_a,
display_pin_b,
channel_a_pin,
channel_b_pin,
options_byte
);
Ok(settings)
} else {
Err(PoKeysError::Protocol(
"Invalid encoder settings response length".to_string(),
))
}
}
pub fn enable_encoder(&mut self, encoder_id: u8, enable: bool) -> Result<()> {
if encoder_id as usize >= self.encoders.len() {
return Err(PoKeysError::Parameter(format!(
"Invalid encoder ID: {}",
encoder_id
)));
}
let (channel_a_pin, channel_b_pin) = {
let encoder = &self.encoders[encoder_id as usize];
(encoder.channel_a_pin, encoder.channel_b_pin)
};
let mut options = {
let encoder = &self.encoders[encoder_id as usize];
encoder.get_options()
};
options.enabled = enable;
self.configure_encoder(encoder_id, channel_a_pin, channel_b_pin, options)
}
pub fn set_encoder_sampling(
&mut self,
encoder_id: u8,
sampling_4x: bool,
sampling_2x: bool,
) -> Result<()> {
if encoder_id as usize >= self.encoders.len() {
return Err(PoKeysError::Parameter(format!(
"Invalid encoder ID: {}",
encoder_id
)));
}
if sampling_4x && sampling_2x {
return Err(PoKeysError::Parameter(
"Cannot enable both 4x and 2x sampling simultaneously".to_string(),
));
}
let (channel_a_pin, channel_b_pin) = {
let encoder = &self.encoders[encoder_id as usize];
(encoder.channel_a_pin, encoder.channel_b_pin)
};
let mut options = {
let encoder = &self.encoders[encoder_id as usize];
encoder.get_options()
};
options.sampling_4x = sampling_4x;
options.sampling_2x = sampling_2x;
self.configure_encoder(encoder_id, channel_a_pin, channel_b_pin, options)
}
pub fn configure_encoder_key_mapping_a(
&mut self,
encoder_id: u8,
key_code: u8,
key_modifier: u8,
) -> Result<()> {
if encoder_id as usize >= self.encoders.len() {
return Err(PoKeysError::Parameter(format!(
"Invalid encoder ID: {}",
encoder_id
)));
}
let encoder = &mut self.encoders[encoder_id as usize];
encoder.dir_a_key_code = key_code;
encoder.dir_a_key_modifier = key_modifier;
let response = self.send_request(
0x12, encoder_id, 0, key_code, key_modifier, )?;
if response.len() > 3 && response[3] != 0 {
return Err(PoKeysError::Protocol(format!(
"Encoder key mapping A failed for encoder {}: status {}",
encoder_id, response[3]
)));
}
Ok(())
}
pub fn configure_encoder_key_mapping_b(
&mut self,
encoder_id: u8,
key_code: u8,
key_modifier: u8,
) -> Result<()> {
if encoder_id as usize >= self.encoders.len() {
return Err(PoKeysError::Parameter(format!(
"Invalid encoder ID: {}",
encoder_id
)));
}
let encoder = &mut self.encoders[encoder_id as usize];
encoder.dir_b_key_code = key_code;
encoder.dir_b_key_modifier = key_modifier;
let response = self.send_request(
0x13, encoder_id, 0, key_code, key_modifier, )?;
if response.len() > 3 && response[3] != 0 {
return Err(PoKeysError::Protocol(format!(
"Encoder key mapping B failed for encoder {}: status {}",
encoder_id, response[3]
)));
}
Ok(())
}
pub fn read_encoder_key_mapping_a(&mut self, encoder_id: u8) -> Result<(u8, u8)> {
if encoder_id as usize >= self.encoders.len() {
return Err(PoKeysError::Parameter(format!(
"Invalid encoder ID: {}",
encoder_id
)));
}
let response = self.send_request(
0x17, encoder_id, 0, 0, 0, )?;
if response.len() < 8 {
return Err(PoKeysError::Protocol("Invalid response length".to_string()));
}
let key_code = response[5];
let key_modifier = response[6];
self.encoders[encoder_id as usize].dir_a_key_code = key_code;
self.encoders[encoder_id as usize].dir_a_key_modifier = key_modifier;
Ok((key_code, key_modifier))
}
pub fn read_encoder_key_mapping_b(&mut self, encoder_id: u8) -> Result<(u8, u8)> {
if encoder_id as usize >= self.encoders.len() {
return Err(PoKeysError::Parameter(format!(
"Invalid encoder ID: {}",
encoder_id
)));
}
let response = self.send_request(
0x18, encoder_id, 0, 0, 0, )?;
if response.len() < 8 {
return Err(PoKeysError::Protocol("Invalid response length".to_string()));
}
let key_code = response[5];
let key_modifier = response[6];
self.encoders[encoder_id as usize].dir_b_key_code = key_code;
self.encoders[encoder_id as usize].dir_b_key_modifier = key_modifier;
Ok((key_code, key_modifier))
}
pub fn read_encoder_raw_value(&mut self, encoder_id: u8) -> Result<i32> {
if encoder_id as usize >= self.encoders.len() {
return Err(PoKeysError::Parameter(format!(
"Invalid encoder ID: {}",
encoder_id
)));
}
let response = self.send_request(
0x19, encoder_id, 0, 0, 0, )?;
if response.len() < 8 {
return Err(PoKeysError::Protocol("Invalid response length".to_string()));
}
let raw_value = response[4] as i8 as i32;
self.encoders[encoder_id as usize].encoder_value = raw_value;
Ok(raw_value)
}
pub fn reset_encoder_raw_value(&mut self, encoder_id: u8) -> Result<()> {
if encoder_id as usize >= self.encoders.len() {
return Err(PoKeysError::Parameter(format!(
"Invalid encoder ID: {}",
encoder_id
)));
}
let response = self.send_request(
0x1A, encoder_id, 0, 0, 0, )?;
if response.len() < 8 {
return Err(PoKeysError::Protocol("Invalid response length".to_string()));
}
self.encoders[encoder_id as usize].encoder_value = 0;
Ok(())
}
pub fn get_encoder_value(&mut self, encoder_id: u8) -> Result<i32> {
self.read_encoder_raw_value(encoder_id)
}
pub fn reset_encoder(&mut self, encoder_id: u8) -> Result<()> {
self.reset_encoder_raw_value(encoder_id)
}
pub fn read_encoder_long_values(&mut self, group: u8) -> Result<Vec<i32>> {
if group > 1 {
return Err(PoKeysError::Parameter(
"Group must be 0 (encoders 1-13) or 1 (encoders 14-26)".to_string(),
));
}
let response = self.send_request(
0xCD, group, 0, 0, 0, )?;
if response.len() < 64 {
return Err(PoKeysError::Protocol(
"Invalid response length for bulk encoder read".to_string(),
));
}
let mut values = Vec::new();
for i in 0..13 {
let byte_offset = 8 + (i * 4); if byte_offset + 3 < response.len() {
let value = i32::from_le_bytes([
response[byte_offset],
response[byte_offset + 1],
response[byte_offset + 2],
response[byte_offset + 3],
]);
values.push(value);
let encoder_index = if group == 0 { i } else { 13 + i };
if encoder_index < self.encoders.len() {
self.encoders[encoder_index].encoder_value = value;
}
}
}
if group == 1 && response.len() >= 60 {
let ultra_fast_value = i32::from_le_bytes([
response[56], response[57], response[58], response[59], ]);
if self.encoders.len() > 25 {
self.encoders[25].encoder_value = ultra_fast_value;
}
}
log::info!("Bulk read group {} returned {} values", group, values.len());
Ok(values)
}
pub fn set_encoder_long_values(&mut self, group: u8, values: &[i32]) -> Result<()> {
if group > 1 {
return Err(PoKeysError::Parameter(
"Group must be 0 (encoders 1-13) or 1 (encoders 14-26)".to_string(),
));
}
let expected_count = if group == 0 { 13 } else { 12 }; if values.len() < expected_count {
return Err(PoKeysError::Parameter(format!(
"Need {} values for group {}",
expected_count, group
)));
}
let mut request = vec![0u8; 64];
request[2] = 0xCD; request[3] = group + 10; request[7] = self.get_next_request_id();
for (i, &value) in values.iter().enumerate() {
let byte_offset = 9 + (i * 4);
if byte_offset + 3 < request.len() {
let bytes = value.to_le_bytes();
request[byte_offset] = bytes[0];
request[byte_offset + 1] = bytes[1];
request[byte_offset + 2] = bytes[2];
request[byte_offset + 3] = bytes[3];
}
}
let _response = self.send_raw_request(&request)?;
let start_encoder = if group == 0 { 1 } else { 14 };
for (i, &value) in values.iter().enumerate() {
let encoder_index = start_encoder + i;
if encoder_index < self.encoders.len() {
self.encoders[encoder_index].encoder_value = value;
}
}
Ok(())
}
pub fn read_all_encoder_values(&mut self) -> Result<Vec<i32>> {
let mut all_values = Vec::new();
let group1_values = self.read_encoder_long_values(0)?;
all_values.extend(group1_values);
let group2_values = self.read_encoder_long_values(1)?;
all_values.extend(group2_values);
Ok(all_values)
}
pub fn configure_encoder_options_bulk(&mut self, options: &[u8]) -> Result<Vec<u8>> {
if options.len() != 25 {
return Err(PoKeysError::Parameter(
"Need exactly 25 encoder options".to_string(),
));
}
let mut request = vec![0u8; 64];
request[2] = 0xC4; request[3] = 1; request[7] = self.get_next_request_id();
for (i, &option) in options.iter().enumerate() {
request[9 + i] = option;
}
let response = self.send_raw_request(&request)?;
let mut returned_options = Vec::new();
if response.len() >= 34 {
for i in 0..25 {
returned_options.push(response[9 + i]);
if i < self.encoders.len() {
self.encoders[i].encoder_options = response[9 + i];
}
}
}
Ok(returned_options)
}
pub fn read_encoder_options_bulk(&mut self) -> Result<Vec<u8>> {
let response = self.send_request(
0xC4, 0, 0, 0, 0, )?;
let mut options = Vec::new();
if response.len() >= 34 {
for i in 0..25 {
options.push(response[9 + i]);
if i < self.encoders.len() {
self.encoders[i].encoder_options = response[9 + i];
}
}
}
Ok(options)
}
pub fn configure_fast_encoders(
&mut self,
config: FastEncoderConfiguration,
options: FastEncoderOptions,
) -> Result<()> {
let config_byte = config.to_byte();
let options_byte = options.to_byte();
self.fast_encoders_configuration = config_byte;
self.fast_encoders_options = options_byte;
let response = self.send_request(
0xCE, config_byte, options_byte, 0, 0, )?;
if response.len() > 2 {
let status = response[2];
if status != 0 {
return Err(PoKeysError::Protocol(format!(
"Fast encoder configuration failed: status {}",
status
)));
}
}
Ok(())
}
pub fn read_fast_encoder_values(&mut self) -> Result<[i32; 3]> {
let values = self.read_encoder_long_values(0)?;
let mut fast_values = [0i32; 3];
let copy_len = 3.min(values.len());
fast_values[..copy_len].copy_from_slice(&values[..copy_len]);
Ok(fast_values)
}
pub fn configure_ultra_fast_encoder(
&mut self,
enable: bool,
options: UltraFastEncoderOptions,
reset_on_index: bool,
filter_delay: u32,
) -> Result<()> {
let options_byte = options.to_byte();
self.ultra_fast_encoder_configuration = if enable { 1 } else { 0 };
self.ultra_fast_encoder_options = options_byte;
self.ultra_fast_encoder_filter = filter_delay;
let filter_bytes = filter_delay.to_le_bytes();
let response = self.send_request_with_data(
0x1C,
if enable { 1 } else { 0 },
options_byte,
if reset_on_index { 1 } else { 0 },
0,
&filter_bytes,
)?;
if response.len() > 2 {
let status = response[2];
if status != 0 {
return Err(PoKeysError::Protocol(format!(
"Ultra-fast encoder configuration failed: status {}",
status
)));
}
}
Ok(())
}
pub fn read_ultra_fast_encoder_config(
&mut self,
) -> Result<(bool, UltraFastEncoderOptions, u32)> {
let response = self.send_request(0x1C, 0xFF, 0, 0, 0)?;
if response.len() < 12 {
return Err(PoKeysError::Protocol("Invalid response length".to_string()));
}
let enabled = response[2] != 0;
let options = UltraFastEncoderOptions::from_byte(response[3]);
let filter_delay =
u32::from_le_bytes([response[8], response[9], response[10], response[11]]);
Ok((enabled, options, filter_delay))
}
pub fn read_ultra_fast_encoder_value(&mut self) -> Result<i32> {
let values = self.read_encoder_long_values(1)?;
if let Some(&value) = values.last() {
Ok(value)
} else {
Err(PoKeysError::Protocol(
"No ultra-fast encoder value in response".to_string(),
))
}
}
pub fn set_ultra_fast_encoder_value(&mut self, value: i32) -> Result<()> {
let mut values = self.read_encoder_long_values(1)?;
if let Some(last) = values.last_mut() {
*last = value;
} else {
return Err(PoKeysError::Protocol(
"Cannot set ultra-fast encoder value".to_string(),
));
}
self.set_encoder_long_values(1, &values)
}
#[allow(clippy::too_many_arguments)]
pub fn configure_encoder_with_keys(
&mut self,
encoder_id: u8,
channel_a_pin: u8,
channel_b_pin: u8,
sampling_4x: bool,
sampling_2x: bool,
dir_a_key_code: u8,
dir_a_key_modifier: u8,
dir_b_key_code: u8,
dir_b_key_modifier: u8,
) -> Result<()> {
let mut options = EncoderOptions::new();
options.enabled = true;
options.sampling_4x = sampling_4x;
options.sampling_2x = sampling_2x;
options.direct_key_mapping_a = true;
options.direct_key_mapping_b = true;
self.configure_encoder(encoder_id, channel_a_pin, channel_b_pin, options)?;
self.configure_encoder_key_mapping_a(encoder_id, dir_a_key_code, dir_a_key_modifier)?;
self.configure_encoder_key_mapping_b(encoder_id, dir_b_key_code, dir_b_key_modifier)?;
Ok(())
}
pub fn get_encoder_sampling_mode(&self, encoder_id: u8) -> Result<String> {
if encoder_id as usize >= self.encoders.len() {
return Err(PoKeysError::Parameter(format!(
"Invalid encoder ID: {}",
encoder_id
)));
}
let encoder = &self.encoders[encoder_id as usize];
Ok(encoder.sampling_mode_str().to_string())
}
pub fn is_encoder_4x_sampling(&self, encoder_id: u8) -> Result<bool> {
if encoder_id as usize >= self.encoders.len() {
return Err(PoKeysError::Parameter(format!(
"Invalid encoder ID: {}",
encoder_id
)));
}
Ok(self.encoders[encoder_id as usize].is_4x_sampling())
}
pub fn is_encoder_2x_sampling(&self, encoder_id: u8) -> Result<bool> {
if encoder_id as usize >= self.encoders.len() {
return Err(PoKeysError::Parameter(format!(
"Invalid encoder ID: {}",
encoder_id
)));
}
Ok(self.encoders[encoder_id as usize].is_2x_sampling())
}
pub fn get_enabled_encoders(&self) -> Vec<u8> {
self.encoders
.iter()
.enumerate()
.filter_map(|(i, encoder)| {
if encoder.is_enabled() {
Some(i as u8)
} else {
None
}
})
.collect()
}
fn get_next_request_id(&mut self) -> u8 {
static mut REQUEST_ID: u8 = 0;
unsafe {
REQUEST_ID = REQUEST_ID.wrapping_add(1);
REQUEST_ID
}
}
fn send_raw_request(&mut self, request: &[u8]) -> Result<Vec<u8>> {
if request.len() >= 8 {
let response_array =
self.send_request(request[2], request[3], request[4], request[5], request[6])?;
Ok(response_array.to_vec())
} else {
Err(PoKeysError::Protocol("Invalid request format".to_string()))
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_encoder_options_4x_sampling() {
let options = EncoderOptions::with_4x_sampling();
assert!(options.enabled);
assert!(options.sampling_4x);
assert!(!options.sampling_2x);
let byte = options.to_byte();
assert_eq!(byte & 0b00000011, 0b00000011);
let options_from_byte = EncoderOptions::from_byte(byte);
assert!(options_from_byte.enabled);
assert!(options_from_byte.sampling_4x);
assert!(!options_from_byte.sampling_2x);
}
#[test]
fn test_encoder_options_2x_sampling() {
let options = EncoderOptions::with_2x_sampling();
assert!(options.enabled);
assert!(!options.sampling_4x);
assert!(options.sampling_2x);
let byte = options.to_byte();
assert_eq!(byte & 0b00000111, 0b00000101);
let options_from_byte = EncoderOptions::from_byte(byte);
assert!(options_from_byte.enabled);
assert!(!options_from_byte.sampling_4x);
assert!(options_from_byte.sampling_2x);
}
#[test]
fn test_encoder_options_key_mapping() {
let mut options = EncoderOptions::new();
options.enabled = true;
options.direct_key_mapping_a = true;
options.macro_mapping_b = true;
let byte = options.to_byte();
assert_eq!(byte & 0b11110001, 0b10010001);
let options_from_byte = EncoderOptions::from_byte(byte);
assert!(options_from_byte.enabled);
assert!(options_from_byte.direct_key_mapping_a);
assert!(options_from_byte.macro_mapping_b);
assert!(!options_from_byte.direct_key_mapping_b);
assert!(!options_from_byte.macro_mapping_a);
}
#[test]
fn test_encoder_data_sampling_modes() {
let mut encoder = EncoderData::new();
let options_4x = EncoderOptions::with_4x_sampling();
encoder.set_options(options_4x);
assert!(encoder.is_4x_sampling());
assert!(!encoder.is_2x_sampling());
assert_eq!(encoder.sampling_mode_str(), "4x (both edges)");
let options_2x = EncoderOptions::with_2x_sampling();
encoder.set_options(options_2x);
assert!(!encoder.is_4x_sampling());
assert!(encoder.is_2x_sampling());
assert_eq!(encoder.sampling_mode_str(), "2x (A edges only)");
let options_disabled = EncoderOptions::new();
encoder.set_options(options_disabled);
assert!(!encoder.is_4x_sampling());
assert!(!encoder.is_2x_sampling());
assert_eq!(encoder.sampling_mode_str(), "1x (disabled)");
}
#[test]
fn test_encoder_constants() {
assert_eq!(MAX_ENCODERS, 25);
assert_eq!(MAX_FAST_ENCODERS, 3);
assert_eq!(ULTRA_FAST_ENCODER_INDEX, 25);
}
#[test]
fn test_fast_encoder_configuration_to_byte() {
assert_eq!(FastEncoderConfiguration::Disabled.to_byte(), 0x00);
assert_eq!(FastEncoderConfiguration::Config1.to_byte(), 0x01);
assert_eq!(FastEncoderConfiguration::Config2.to_byte(), 0x10);
}
#[test]
fn test_fast_encoder_configuration_round_trip() {
for cfg in [
FastEncoderConfiguration::Disabled,
FastEncoderConfiguration::Config1,
FastEncoderConfiguration::Config2,
] {
assert_eq!(FastEncoderConfiguration::from_byte(cfg.to_byte()), cfg);
}
}
#[test]
fn test_fast_encoder_options_bit_masks() {
assert_eq!(
FastEncoderOptions {
disable_4x_sampling: true,
..Default::default()
}
.to_byte(),
0x10
);
assert_eq!(
FastEncoderOptions {
invert_direction_1: true,
..Default::default()
}
.to_byte(),
0x20
);
assert_eq!(
FastEncoderOptions {
invert_direction_2: true,
..Default::default()
}
.to_byte(),
0x40
);
assert_eq!(
FastEncoderOptions {
invert_direction_3: true,
..Default::default()
}
.to_byte(),
0x80
);
}
#[test]
fn test_fast_encoder_options_combined() {
let all = FastEncoderOptions {
disable_4x_sampling: true,
invert_direction_1: true,
invert_direction_2: true,
invert_direction_3: true,
};
assert_eq!(all.to_byte(), 0xF0);
}
#[test]
fn test_fast_encoder_options_round_trip() {
let o = FastEncoderOptions {
disable_4x_sampling: false,
invert_direction_1: true,
invert_direction_2: false,
invert_direction_3: true,
};
assert_eq!(FastEncoderOptions::from_byte(o.to_byte()), o);
}
#[test]
fn test_fast_encoder_options_ignores_reserved_low_nibble() {
let decoded = FastEncoderOptions::from_byte(0x0F);
assert_eq!(decoded, FastEncoderOptions::default());
}
#[test]
fn test_ultra_fast_encoder_options_bit_masks() {
assert_eq!(
UltraFastEncoderOptions {
invert_direction: true,
..Default::default()
}
.to_byte(),
0x01
);
assert_eq!(
UltraFastEncoderOptions {
signal_mode_direction_clock: true,
..Default::default()
}
.to_byte(),
0x02
);
assert_eq!(
UltraFastEncoderOptions {
enable_4x_sampling: true,
..Default::default()
}
.to_byte(),
0x04
);
}
#[test]
fn test_ultra_fast_encoder_options_combined() {
let all = UltraFastEncoderOptions {
invert_direction: true,
signal_mode_direction_clock: true,
enable_4x_sampling: true,
};
assert_eq!(all.to_byte(), 0x07);
}
#[test]
fn test_ultra_fast_encoder_options_round_trip() {
let o = UltraFastEncoderOptions {
invert_direction: true,
signal_mode_direction_clock: false,
enable_4x_sampling: true,
};
assert_eq!(UltraFastEncoderOptions::from_byte(o.to_byte()), o);
}
#[test]
fn test_ultra_fast_encoder_options_ignores_reserved_high_bits() {
let decoded = UltraFastEncoderOptions::from_byte(0xF8);
assert_eq!(decoded, UltraFastEncoderOptions::default());
}
}