From b8fdd4e705d45d2dceb3ad96f770f23b73982e0c Mon Sep 17 00:00:00 2001 From: Alex van de Sandt Date: Mon, 24 Aug 2026 11:13:01 -0500 Subject: [PATCH] Document various traits and types --- crates/arctictis/src/command/mod.rs | 7 ++++++ crates/arctictis/src/scanner.rs | 37 +++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/crates/arctictis/src/command/mod.rs b/crates/arctictis/src/command/mod.rs index 3274431..aae781e 100644 --- a/crates/arctictis/src/command/mod.rs +++ b/crates/arctictis/src/command/mod.rs @@ -9,10 +9,16 @@ pub use no_params::NoParams; pub use ok_response::{OkResponse, OkResponseError}; pub use single_param::SingleParam; +/// A command that can be sent to the scanner to read or change settings on the scanner. +/// +/// See also [`NonProgramModeCommand`]. pub trait Command { + /// The three-letter code that defines the command const TEXT: &'static [u8]; + /// Which parameters, if any, are sent with the command type Params: Params; + /// The type the scanner is expected to respond with type Response: Response; fn params(self) -> Self::Params; @@ -41,4 +47,5 @@ pub trait Response: Sized { pub type CommandResponseError = <::Response as Response>::Error; +/// A command that can be sent to the scanner outside of "program mode". pub trait NonProgramModeCommand: Command {} diff --git a/crates/arctictis/src/scanner.rs b/crates/arctictis/src/scanner.rs index 3391e5c..2fa7456 100644 --- a/crates/arctictis/src/scanner.rs +++ b/crates/arctictis/src/scanner.rs @@ -16,6 +16,7 @@ const PRODUCT_ID: u16 = 0x0017; const TIMEOUT: Duration = Duration::from_mins(2); const BAUD_RATE: u32 = 115_200; +/// An error occured connecting to a scanner #[derive(Debug, thiserror::Error)] pub enum ScannerError { #[error("scanner not found")] @@ -28,6 +29,7 @@ pub enum ScannerError { Serial(#[from] tokio_serial::Error), } +/// An error occured sending a command to a scanner #[derive(Debug, thiserror::Error)] pub enum CommandError { #[error("port closed")] @@ -80,10 +82,14 @@ impl From> for CommandError { } } +/// A serial connection to a BC125AT Radio Scanner. #[derive(Debug)] pub struct Scanner(Framed); impl Scanner { + /// Automatically detect a plugged-in scanner and open a connection + /// + /// This will scan for avilable serial ports and match the expected vendor and product IDs. pub fn open() -> Result { let ports = tokio_serial::available_ports()?; let Some(scanner_port_path) = ports.iter().find_map(|port| { @@ -117,6 +123,11 @@ impl Scanner { Ok(response) } + /// Send a command to the scanner and parse the response. + /// + /// The serialization and deserialization of parameters and responses are handled automatically. + /// Only [`NonProgramModeCommand`]s are accepted. Use + /// [`with_program_mode`][Self::with_program_mode] to send any command. pub async fn command( &mut self, cmd: Cmd, @@ -124,6 +135,32 @@ impl Scanner { self.any_command(cmd).await } + /// Put the scanner in "program mode" to allow it to accept any command. + /// + /// This works by sending the hidden `PRG` command, then wrapping the connection with a + /// [`ProgramModeScanner`], whose [`command`][ProgramModeScanner::command] method takes any + /// command. After executing the passed-in closure, the `EPG` command is sent. + /// + /// # Example + /// ``` + /// # async { + /// use arctictis::{ + /// Scanner, + /// bc125at::{channel_info::GetChannelInfo, common::ChannelIndex}, + /// }; + /// + /// let mut scanner = Scanner::open().unwrap(); + /// scanner.with_program_mode(async |mut scanner| { + /// let idx = ChannelIndex::new(1).unwrap(); + /// let channel_info = scanner.command(GetChannelInfo(idx)).await.unwrap(); + /// + /// let name = str::from_utf8(channel_info.name.value()).unwrap(); + /// let freq = str::from_utf8(channel_info.frequency.value()).unwrap(); + /// + /// println!("{name}: {freq}") + /// }).await.unwrap(); + /// # }; + /// ``` pub async fn with_program_mode T>( &mut self, f: F, -- 2.51.2