diff --git a/src-tauri/src/commands.rs b/src-tauri/src/commands.rs new file mode 100644 index 0000000..ff6d781 --- /dev/null +++ b/src-tauri/src/commands.rs @@ -0,0 +1,194 @@ + + +use std::io::Write; + +use crate::{Preprocessing, model::Filters, Codec}; + + +pub trait Command { + fn as_buf(&self) -> Vec; +} + +#[repr(u16)] +#[allow(dead_code)] +pub enum StructureTypes { + // Commands/Responses, these are container TLVs. The Value will be a set of TLV structures. + OK = 0, // Standard response when a command was successful + NOK, // Standard error response + FlashHeader, // A special container for the config stored in flash. Hopefully there is some useful + // metadata in here to allow us to migrate an old config to a new version. + GetVersion, // Returns the current config version, and the minimum supported version so clients + // can decide if they can talk to us or not. + SetConfiguration, // Updates the active configuration with the supplied TLVs + GetActiveConfiguration, // Retrieves the current active configuration TLVs from RAM + GetStoredConfiguration, // Retrieves the current stored configuration TLVs from Flash + SaveConfiguration, // Writes the active configuration to Flash + FactoryReset, // Invalidates the flash memory + + // Configuration structures, these are returned in the body of a command/response + PreProcessingConfiguration = 0x200, + FilterConfiguration, + Pcm3060Configuration, + + // Status structures, these are returned in the body of a command/response but they are + // not persisted as part of the configuration + VersionStatus = 0x400, +} + +pub struct GetVersion(); + +impl GetVersion { + pub fn new() -> Self { + Self() + } +} + +impl Command for GetVersion { + fn as_buf(&self) -> Vec { + let mut buf = Vec::new(); + buf.extend_from_slice(&(StructureTypes::GetVersion as u16).to_le_bytes()); + buf.extend_from_slice(&(4u16).to_le_bytes()); + buf + } +} + +pub struct SetPreprocessingConfiguration<'a>(&'a Preprocessing); + +impl<'a> SetPreprocessingConfiguration<'a> { + pub fn new(preprocessing: &'a Preprocessing) -> Self { + Self(preprocessing) + } +} + +impl Command for SetPreprocessingConfiguration<'_> { + fn as_buf(&self) -> Vec { + let payload = self.0.to_payload(); + let mut buf = Vec::new(); + buf.extend_from_slice(&(StructureTypes::PreProcessingConfiguration as u16).to_le_bytes()); + buf.extend_from_slice(&((4 + payload.len()) as u16).to_le_bytes()); + buf.extend_from_slice(&payload); + buf + } +} + +pub struct SetFilterConfiguration<'a>(&'a Filters); + +impl<'a> SetFilterConfiguration<'a> { + pub fn new(filters: &'a Filters) -> Self { + Self(filters) + } +} + +impl Command for SetFilterConfiguration<'_> { + fn as_buf(&self) -> Vec { + let payload = self.0.to_payload(); + let mut buf = Vec::new(); + buf.extend_from_slice(&(StructureTypes::FilterConfiguration as u16).to_le_bytes()); + buf.extend_from_slice(&((4 + payload.len()) as u16).to_le_bytes()); + buf.extend_from_slice(&payload); + buf + } +} + +pub struct SetPcm3060Configuration<'a>(&'a Codec); + +impl<'a> SetPcm3060Configuration<'a> { + pub fn new(codec: &'a Codec) -> Self { + Self(codec) + } +} + +impl Command for SetPcm3060Configuration<'_> { + fn as_buf(&self) -> Vec { + let payload = self.0.to_payload(); + let mut buf = Vec::new(); + buf.extend_from_slice(&(StructureTypes::Pcm3060Configuration as u16).to_le_bytes()); + buf.extend_from_slice(&((4 + payload.len()) as u16).to_le_bytes()); + buf.extend_from_slice(&payload); + buf + } +} + +pub struct SetConfiguration<'a, 'b, 'c>{ + preprocessing: SetPreprocessingConfiguration<'a>, + filter: SetFilterConfiguration<'b>, + codec: SetPcm3060Configuration<'c> +} + +impl<'a, 'b, 'c> SetConfiguration<'a, 'b, 'c> { + pub fn new(preprocessing: SetPreprocessingConfiguration<'a>, filter: SetFilterConfiguration<'b>, codec: SetPcm3060Configuration<'c>) -> Self { + Self { + preprocessing, + filter, + codec + } + } +} + +impl Command for SetConfiguration<'_, '_, '_> { + fn as_buf(&self) -> Vec { + let mut buf: Vec = Vec::new(); + buf.extend_from_slice(&(StructureTypes::SetConfiguration as u16).to_le_bytes()); + buf.extend_from_slice( + &((16 + self.filter.0.to_payload().len() + + self.preprocessing.0.to_payload().len() + + self.codec.0.to_payload().len()) as u16) + .to_le_bytes(), + ); + buf.extend_from_slice(&self.preprocessing.as_buf()); + buf.extend_from_slice(&self.filter.as_buf()); + buf.extend_from_slice(&self.codec.as_buf()); + buf + } +} + +pub struct FactoryReset(); + +impl FactoryReset { + pub fn new() -> Self { + Self() + } +} + +impl Command for FactoryReset { + fn as_buf(&self) -> Vec { + let mut buf: Vec = Vec::new(); + buf.extend_from_slice(&(StructureTypes::FactoryReset as u16).to_le_bytes()); + buf.extend_from_slice(&(4u16).to_le_bytes()); + buf + } +} + +pub struct SaveConfiguration(); + +impl SaveConfiguration { + pub fn new() -> Self { + Self() + } +} + +impl Command for SaveConfiguration { + fn as_buf(&self) -> Vec { + let mut buf: Vec = Vec::new(); + buf.extend_from_slice(&(StructureTypes::SaveConfiguration as u16).to_le_bytes()); + buf.extend_from_slice(&(4u16).to_le_bytes()); + buf + } +} + +pub struct GetStoredConfiguration(); + +impl GetStoredConfiguration { + pub fn new() -> Self { + Self() + } +} + +impl Command for GetStoredConfiguration { + fn as_buf(&self) -> Vec { + let mut buf: Vec = Vec::new(); + buf.extend_from_slice(&(StructureTypes::GetStoredConfiguration as u16).to_le_bytes()); + buf.extend_from_slice(&(4u16).to_le_bytes()); + buf + } +} diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index abe76e5..70d8cb7 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -1,6 +1,15 @@ // Prevents additional console window on Windows in release, DO NOT REMOVE!! #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] use byteorder::{LittleEndian, ReadBytesExt}; +use commands::Command; +use commands::FactoryReset; +use commands::GetStoredConfiguration; +use commands::GetVersion; +use commands::SaveConfiguration; +use commands::SetConfiguration; +use commands::SetFilterConfiguration; +use commands::SetPcm3060Configuration; +use commands::SetPreprocessingConfiguration; use model::Filter; use model::Filters; use model::StructureTypes; @@ -30,6 +39,7 @@ use std::fs::File; use tauri::PathResolver; mod model; +mod commands; pub const LIBUSB_RECIPIENT_DEVICE: u8 = 0x00; pub const LIBUSB_REQUEST_TYPE_VENDOR: u8 = 0x02 << 5; @@ -115,7 +125,7 @@ fn find_configuration_endpoints( } #[derive(Serialize, Deserialize, Default, Debug)] -struct Preprocessing { +pub struct Preprocessing { preamp: f32, postEQGain: f32, reverse_stereo: bool, @@ -149,7 +159,7 @@ impl Preprocessing { } #[derive(Serialize, Deserialize, Default, Debug)] -struct Codec { +pub struct Codec { oversampling: bool, phase: bool, rolloff: bool, @@ -229,8 +239,9 @@ impl VersionInfo { fn send_cmd( connection_state: State<'_, Mutex>, - buf: &[u8], + cmd: impl Command, ) -> Result<[u8; MAX_CFG_LEN], String> { + let buf = cmd.as_buf(); let mut connection = connection_state.lock().unwrap(); let device = match &connection.connected { @@ -289,37 +300,16 @@ fn write_config( config: Config, connection_state: State<'_, Mutex>, ) -> Result { - info!("write_config"); - println!("write_config"); - let filter_payload: Vec = config.filters.to_payload(); - let preprocessing_payload: Vec = config.preprocessing.to_payload(); - let codec_payload = config.codec.to_payload(); - - let mut buf: Vec = Vec::new(); - buf.extend_from_slice(&(StructureTypes::SetConfiguration as u16).to_le_bytes()); - buf.extend_from_slice( - &((16 + filter_payload.len() + preprocessing_payload.len() + codec_payload.len()) as u16) - .to_le_bytes(), - ); - buf.extend_from_slice(&(StructureTypes::PreProcessingConfiguration as u16).to_le_bytes()); - buf.extend_from_slice(&((4 + preprocessing_payload.len()) as u16).to_le_bytes()); - buf.extend_from_slice(&preprocessing_payload); - buf.extend_from_slice(&(StructureTypes::FilterConfiguration as u16).to_le_bytes()); - buf.extend_from_slice(&((4 + filter_payload.len()) as u16).to_le_bytes()); - buf.extend_from_slice(&filter_payload); - buf.extend_from_slice(&(StructureTypes::Pcm3060Configuration as u16).to_le_bytes()); - buf.extend_from_slice(&((4 + codec_payload.len()) as u16).to_le_bytes()); - buf.extend_from_slice(&codec_payload); - - send_cmd(connection_state, &buf).map(|_| true) + let prep = SetPreprocessingConfiguration::new(&config.preprocessing); + let filters = SetFilterConfiguration::new(&config.filters); + let codec = SetPcm3060Configuration::new(&config.codec); + let cmd = SetConfiguration::new(prep, filters, codec); + send_cmd(connection_state, cmd).map(|_| true) } #[tauri::command] fn save_config(connection_state: State<'_, Mutex>) -> Result { - let mut buf: Vec = Vec::new(); - buf.extend_from_slice(&(StructureTypes::SaveConfiguration as u16).to_le_bytes()); - buf.extend_from_slice(&(4u16).to_le_bytes()); - send_cmd(connection_state, &buf).map(|_| true) + send_cmd(connection_state, SaveConfiguration::new()).map(|_| true) } #[tauri::command] @@ -328,7 +318,7 @@ fn load_config(connection_state: State<'_, Mutex>) -> Result x, Err(e) => { @@ -385,11 +375,7 @@ fn load_config(connection_state: State<'_, Mutex>) -> Result>) -> Result { - let mut buf: Vec = Vec::new(); - buf.extend_from_slice(&(StructureTypes::FactoryReset as u16).to_le_bytes()); - buf.extend_from_slice(&(4u16).to_le_bytes()); - - send_cmd(connection_state, &buf).map(|_| true) + send_cmd(connection_state, FactoryReset::new()).map(|_| true) } #[tauri::command] @@ -415,11 +401,7 @@ fn reboot_bootloader(connection_state: State>) -> Result< #[tauri::command] fn read_version_info(connection_state: State<'_, Mutex>) -> Result { - let mut buf: Vec = Vec::new(); - buf.extend_from_slice(&(StructureTypes::GetVersion as u16).to_le_bytes()); - buf.extend_from_slice(&(4u16).to_le_bytes()); - - let v = send_cmd(connection_state, &buf)?; + let v = send_cmd(connection_state, GetVersion::new())?; let version = VersionInfo::from_buf(&v)?; Ok(version) } diff --git a/src-tauri/src/model.rs b/src-tauri/src/model.rs index b69b971..c05ced4 100644 --- a/src-tauri/src/model.rs +++ b/src-tauri/src/model.rs @@ -5,7 +5,7 @@ use byteorder::{LittleEndian, ReadBytesExt}; use serde::{Deserialize, Serialize}; #[repr(u16)] -#[allow(dead_code)] +// #[allow(dead_code)] pub enum StructureTypes { // Commands/Responses, these are container TLVs. The Value will be a set of TLV structures. OK = 0, // Standard response when a command was successful