diff --git a/examples/version.rs b/examples/version.rs index 9a88619..92a53c2 100644 --- a/examples/version.rs +++ b/examples/version.rs @@ -2,13 +2,17 @@ use std::fmt::Debug; -use arctictis::{NonProgramModeCommand, Scanner, bc125at::firmware_version::GetFirmwareVersion}; +use arctictis::{ + NonProgramModeCommand, Scanner, + bc125at::{firmware_version::GetFirmwareVersion, model::GetModel}, +}; #[tokio::main] async fn main() { let mut scanner = Scanner::open().unwrap(); println!("{scanner:#?}"); print_response(&mut scanner, GetFirmwareVersion).await; + print_response(&mut scanner, GetModel).await; } async fn print_response(scanner: &mut Scanner, cmd: Cmd) diff --git a/src/command/bc125at/band_plan.rs b/src/command/bc125at/band_plan.rs new file mode 100644 index 0000000..e996966 --- /dev/null +++ b/src/command/bc125at/band_plan.rs @@ -0,0 +1,9 @@ +get_set_command!( + text: b"BPL", + get: GetBandPlan, + set: SetBandPlan, + single_field: enum BandPlan { + Usa => b"0", + Canada => b"1", + } BandPlanError, +); diff --git a/src/command/bc125at/channel_info.rs b/src/command/bc125at/channel_info.rs index c18698b..1c04e32 100644 --- a/src/command/bc125at/channel_info.rs +++ b/src/command/bc125at/channel_info.rs @@ -2,7 +2,10 @@ use tokio_util::bytes::Bytes; -use crate::command::IntoParam; +use crate::{ + Command, OkResponse, + command::{IntoParam, SingleParam}, +}; get_set_command!( text: b"CIN", @@ -11,7 +14,8 @@ get_set_command!( type: ChannelInfo(ChannelInfoError) ( index: range ChannelIndex(1..=500 => usize), name: str Name(16), - frequency: str Frequency(16), + // TODO: frequency validation + frequency: str Frequency(8), modulaton: enum Modulation { Auto => b"AUTO", Am => b"AM", @@ -39,3 +43,18 @@ get_set_command!( }, ), ); + +// TODO: move ChannelIndex to a shared module +pub struct DeleteChannel(pub ChannelIndex); + +impl Command for DeleteChannel { + const TEXT: &'static [u8] = b"DCH"; + + type Params = SingleParam; + + type Response = OkResponse; + + fn params(self) -> Self::Params { + SingleParam(self.0) + } +} diff --git a/src/command/bc125at/clear.rs b/src/command/bc125at/clear.rs new file mode 100644 index 0000000..67d9cd4 --- /dev/null +++ b/src/command/bc125at/clear.rs @@ -0,0 +1,16 @@ +use crate::{Command, OkResponse, command::NoParams}; + +#[derive(Debug, Clone, Copy)] +pub struct ClearAllMemory; + +impl Command for ClearAllMemory { + const TEXT: &'static [u8] = b"CLR"; + + type Params = NoParams; + + type Response = OkResponse; + + fn params(self) -> Self::Params { + NoParams + } +} diff --git a/src/command/bc125at/close_call.rs b/src/command/bc125at/close_call.rs new file mode 100644 index 0000000..f8aa7e2 --- /dev/null +++ b/src/command/bc125at/close_call.rs @@ -0,0 +1,51 @@ +get_set_command! { + text: b"SCO", + get: GetSearchCloseCallSettings, + set: SetSearchCloseCallSettings, + type: SearchCloseCallSettings(SearchCloseCallError) ( + // TODO: move these to a shared module + delay: enum Delay { + Neg10 => b"-10", + Neg5 => b"-5", + Zero => b"0", + One => b"1", + Two => b"2", + Three => b"3", + Four => b"4", + Five => b"5", + }, + lockout: enum Lockout { + Unlocked => b"0", + Lockout => b"1", + }, + ), + non_program_mode: true, +} + +get_set_command!( + text: b"CLC", + get: GetCloseCallSettings, + set: SetCloseCallSettings, + type: CloseCallSettings(CloseCallSettingsError) ( + cc_mode: enum CcMode { + Off => b"0", + Priority => b"1", + DoNotDisturb => b"2", + CloseCallOnly => b"3", + }, + alert_beep: enum AlertBeep { + Off => b"0", + On => b"1", + }, + alert_light: enum AlertLight { + Off => b"0", + On => b"1", + }, + // TODO: close call band field validation + cc_band: str CloseCallBand(5), + lockout_cc_hits_with_scan: enum LockoutCcHitsWithScan { + Lockout => b"0", + Unlocked => b"1", + } + ), +); diff --git a/src/command/bc125at/custom_search.rs b/src/command/bc125at/custom_search.rs new file mode 100644 index 0000000..93ba4c9 --- /dev/null +++ b/src/command/bc125at/custom_search.rs @@ -0,0 +1,18 @@ +get_set_command!( + text: b"CSG", + get: GetCustomSearchGroups, + set: SetCustomSearchGroups, + single_field: str CustomSearchGroups(10) CustomSearchGroupError, +); + +get_set_command!( + text: b"CSP", + get: GetCustomSearchSettings(SearchIndex), + set: SetCustomSearchSettings, + type: CustomSearchSettings(CustomSearchSettingsError) ( + search_index: range SearchIndex(1..=10 => u8), + // TODO: custom param types + lower_limit: range LowerLimitFrequency(250000..=5120000 => u32), + upper_limit: range UpperLimitFrequency(250000..=5120000 => u32), + ), +); diff --git a/src/command/bc125at/key_beep.rs b/src/command/bc125at/key_beep.rs deleted file mode 100644 index 4345792..0000000 --- a/src/command/bc125at/key_beep.rs +++ /dev/null @@ -1,16 +0,0 @@ -get_set_command! { - text: b"KBP", - get: GetKeyBeepSetting, - set: SetKeyBeep, - type: KeyBeepSetting(KeyBeepSettingError) ( - beep_level: enum BeepLevel { - Auto => b"0", - Off => b"99", - }, - key_lock_status: enum KeyLockStatus { - Off => b"0", - On => b"1", - }, - ), - non_program_mode: true, -} diff --git a/src/command/bc125at/lockout.rs b/src/command/bc125at/lockout.rs new file mode 100644 index 0000000..f52bc82 --- /dev/null +++ b/src/command/bc125at/lockout.rs @@ -0,0 +1,16 @@ +// TODO: move Frequency to shared module +use crate::{Command, OkResponse, bc125at::channel_info::Frequency, command::SingleParam}; + +pub struct LockoutFrequency(pub Frequency); + +impl Command for LockoutFrequency { + const TEXT: &'static [u8] = b"LOF"; + + type Params = SingleParam; + + type Response = OkResponse; + + fn params(self) -> Self::Params { + SingleParam(self.0) + } +} diff --git a/src/command/bc125at/mod.rs b/src/command/bc125at/mod.rs index 1f9c0dc..0362362 100644 --- a/src/command/bc125at/mod.rs +++ b/src/command/bc125at/mod.rs @@ -1,5 +1,15 @@ +pub mod band_plan; pub mod channel_info; +pub mod clear; +pub mod close_call; +pub mod custom_search; pub mod firmware_version; -pub mod key_beep; -pub mod program_mode; -pub mod volume; +pub mod lockout; +pub mod model; +pub mod priority; +pub mod settings; + +pub(crate) mod program_mode; + +// TODO: scan channel group +// TODO: global lockout frequency diff --git a/src/command/bc125at/model.rs b/src/command/bc125at/model.rs new file mode 100644 index 0000000..a1eea2b --- /dev/null +++ b/src/command/bc125at/model.rs @@ -0,0 +1,54 @@ +use std::string::FromUtf8Error; + +use crate::{ + Command, NonProgramModeCommand, + command::{NoParams, Response}, +}; + +#[derive(Debug, thiserror::Error)] +pub enum ModelError { + #[error(transparent)] + Utf8(#[from] FromUtf8Error), + #[error("expected one field")] + Malformed, +} + +#[derive(Debug)] +pub struct Model(pub String); + +impl Response for Model { + type Error = ModelError; + + fn deserialize<'i, I: Iterator>( + mut raw_values: I, + ) -> Result { + if let Some(val) = raw_values.next() + && raw_values.next().is_none() + { + let utf8 = String::from_utf8(val.to_vec())?; + Ok(Self(utf8)) + } else { + Err(ModelError::Malformed) + } + } + + fn expected_field_count() -> usize { + 1 + } +} + +#[derive(Debug, Clone, Copy)] +pub struct GetModel; + +impl Command for GetModel { + const TEXT: &'static [u8] = b"VER"; + + type Params = NoParams; + + type Response = Model; + + fn params(self) -> Self::Params { + NoParams + } +} +impl NonProgramModeCommand for GetModel {} diff --git a/src/command/bc125at/priority.rs b/src/command/bc125at/priority.rs new file mode 100644 index 0000000..9d9dbd4 --- /dev/null +++ b/src/command/bc125at/priority.rs @@ -0,0 +1,11 @@ +get_set_command!( + text: b"PRI", + get: GetPriorityMode, + set: SetPriorityMode, + single_field: enum PriorityMode { + Off => b"0", + On => b"1", + PlusOn => b"2", + DoNotDisturb => b"3", + } PriorityModeError, +); diff --git a/src/command/bc125at/settings.rs b/src/command/bc125at/settings.rs new file mode 100644 index 0000000..21c1023 --- /dev/null +++ b/src/command/bc125at/settings.rs @@ -0,0 +1,69 @@ +get_set_command!( + text: b"BLT", + get: GetBacklight, + set: SetBacklight, + single_field: enum Backlight { + AlwaysOn => b"AO", + Keypress => b"KY", + Squelch => b"SQ", + KeyOrSquelch => b"KS", + AlwaysOff => b"AF", + } BacklightError, +); + +get_set_command!( + text: b"BSV", + get: GetBatteryInfo, + set: SetBatteryInfo, + single_field: range BatteryChargeTime(1..=16 => u8) BatteryInfoError, +); + +get_set_command!( + text: b"CNT", + get: GetLcdContrast, + set: SetLcdContrast, + single_field: range LcdContrast(1..=15 => u8) LcdContrastError, +); + +get_set_command! { + text: b"KBP", + get: GetKeyBeepSetting, + set: SetKeyBeep, + type: KeyBeepSetting(KeyBeepSettingError) ( + beep_level: enum BeepLevel { + Auto => b"0", + Off => b"99", + }, + key_lock_status: enum KeyLockStatus { + Off => b"0", + On => b"1", + }, + ), + non_program_mode: true, +} + +get_set_command!( + text: b"SQL", + get: GetSquelch, + set: SetSquelch, + single_field: range Squelch(0..=15 => u8) SquelchError, + non_program_mode: true, +); + +get_set_command!( + text: b"VOL", + get: GetVolume, + set: SetVolume, + single_field: range Volume(0..=15 => u8) VolumeError, + non_program_mode: true, +); + +get_set_command!( + text: b"WXS", + get: GetWeatherSettings, + set: SetWeatherSettings, + single_field: enum WeatherAlertPriority { + Off => b"0", + On => b"1", + } WeatherSettingsError, +); diff --git a/src/command/bc125at/volume.rs b/src/command/bc125at/volume.rs deleted file mode 100644 index bd477cb..0000000 --- a/src/command/bc125at/volume.rs +++ /dev/null @@ -1,7 +0,0 @@ -get_set_command!( - text: b"VOL", - get: GetVolume, - set: SetVolume, - single_field: range Volume(0..=15 => u8) VolumeError, - non_program_mode: true, -); diff --git a/src/macros.rs b/src/macros.rs index 0781be9..2d3355e 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -46,7 +46,7 @@ macro_rules! get_set_command { // Param set ( @params $set_name:ident ($set_error_name:ident) ( - $($(#[$field_doc:meta])? $field_name:ident: $kind:tt $type_name:ident $body:tt),* $(,)? + $($(#[$field_doc:meta])? $field_name:ident: $kind:tt $type_name:ident $body:tt),+ $(,)? ) ) => { $(get_set_command!(@param $kind $type_name $body $($field_doc)?);)*