use std::{
fmt::Debug,
io,
ops::RangeInclusive,
sync::{Arc, Condvar, Mutex, MutexGuard, WaitTimeoutResult},
thread,
time::Duration,
};
use tracing::{error, info, trace, warn};
use super::{
CalcMode, Command, Config, DspMode, InputStage, Mode, Model, Sweep, TrackingStatus, WifiBand,
};
use crate::rf_explorer::{
COMMAND_RESPONSE_TIMEOUT, ConfigCallback, NEXT_SCREEN_DATA_TIMEOUT,
RECEIVE_INITIAL_DEVICE_INFO_TIMEOUT, ScreenData, SerialNumber, SetupInfo, impl_rf_explorer,
};
use crate::{ConnectionError, ConnectionResult, Device, Error, Frequency, Result};
#[derive(Debug)]
pub struct SpectrumAnalyzer {
rfe: Device<MessageContainer>,
}
impl_rf_explorer!(SpectrumAnalyzer, MessageContainer);
impl SpectrumAnalyzer {
const MIN_MAX_AMP_RANGE_DBM: RangeInclusive<i16> = -120..=35;
const MIN_SWEEP_LEN: u16 = 112;
const NEXT_SWEEP_TIMEOUT: Duration = Duration::from_secs(2);
pub fn serial_number(&self) -> Option<String> {
if let Some(ref serial_number) = *self.messages().serial_number.0.lock().unwrap() {
return Some(serial_number.to_string());
}
self.send_command(crate::rf_explorer::Command::RequestSerialNumber)
.ok()?;
let (lock, cvar) = &self.messages().serial_number;
tracing::trace!("Waiting to receive SerialNumber from RF Explorer");
let _ = cvar
.wait_timeout_while(
lock.lock().unwrap(),
std::time::Duration::from_secs(2),
|serial_number| serial_number.is_none(),
)
.unwrap();
(*self.messages().serial_number.0.lock().unwrap())
.as_ref()
.map(|sn| sn.to_string())
}
pub fn firmware_version(&self) -> String {
self.messages()
.setup_info
.0
.lock()
.unwrap()
.as_ref()
.map(|setup_info| setup_info.firmware_version.clone())
.unwrap_or_default()
}
fn config(&'_ self) -> MutexGuard<'_, Option<Config>> {
self.messages().config.0.lock().unwrap()
}
pub fn start_freq(&self) -> Frequency {
self.config()
.as_ref()
.map(|config| config.start_freq)
.unwrap_or_default()
}
pub fn step_size(&self) -> Frequency {
self.config()
.as_ref()
.map(|config| config.step_size)
.unwrap_or_default()
}
pub fn stop_freq(&self) -> Frequency {
self.config()
.as_ref()
.map(|config| config.stop_freq)
.unwrap_or_default()
}
pub fn center_freq(&self) -> Frequency {
self.config()
.as_ref()
.map(|config| config.center_freq)
.unwrap_or_default()
}
pub fn span(&self) -> Frequency {
self.config()
.as_ref()
.map(|config| config.span)
.unwrap_or_default()
}
pub fn min_freq(&self) -> Frequency {
self.config()
.as_ref()
.map(|config| config.min_freq)
.unwrap_or_default()
}
pub fn max_freq(&self) -> Frequency {
self.config()
.as_ref()
.map(|config| config.max_freq)
.unwrap_or_default()
}
pub fn max_span(&self) -> Frequency {
self.config()
.as_ref()
.map(|config| config.max_span)
.unwrap_or_default()
}
pub fn rbw(&self) -> Option<Frequency> {
self.config()
.as_ref()
.map(|config| config.rbw)
.unwrap_or_default()
}
pub fn min_amp_dbm(&self) -> i16 {
self.config()
.as_ref()
.map(|config| config.min_amp_dbm)
.unwrap_or_default()
}
pub fn max_amp_dbm(&self) -> i16 {
self.config()
.as_ref()
.map(|config| config.max_amp_dbm)
.unwrap_or_default()
}
pub fn amp_offset_db(&self) -> Option<i8> {
self.config()
.as_ref()
.map(|config| config.amp_offset_db)
.unwrap_or_default()
}
pub fn sweep_len(&self) -> u16 {
self.config()
.as_ref()
.map(|config| config.sweep_len)
.unwrap_or_default()
}
fn is_expansion_radio_module_active(&self) -> bool {
self.config()
.as_ref()
.map(|config| config.is_expansion_radio_module_active)
.unwrap_or_default()
}
pub fn mode(&self) -> Mode {
self.config()
.as_ref()
.map(|config| config.mode)
.unwrap_or_default()
}
pub fn calc_mode(&self) -> Option<CalcMode> {
self.config()
.as_ref()
.map(|config| config.calc_mode)
.unwrap_or_default()
}
pub fn sweep(&self) -> Option<Vec<f32>> {
self.rfe
.messages()
.sweep
.0
.lock()
.unwrap()
.as_ref()
.map(|sweep| sweep.amplitudes_dbm.clone())
}
pub fn fill_buf_with_sweep(&self, buf: &mut [f32]) -> Result<usize> {
let sweep = self.messages().sweep.0.lock().unwrap();
let Some(sweep) = sweep.as_ref() else {
return Err(Error::InvalidOperation(
"No sweeps have been measured by the RF Explorer".to_string(),
));
};
let sweep_len = sweep.amplitudes_dbm.len();
if buf.len() >= sweep_len {
buf[0..sweep_len].copy_from_slice(sweep.amplitudes_dbm.as_slice());
Ok(sweep_len)
} else {
Err(Error::InvalidInput(
"The buffer is too small to fit the sweep".to_string(),
))
}
}
pub fn wait_for_next_sweep(&self) -> Result<Vec<f32>> {
self.wait_for_next_sweep_with_timeout(Self::NEXT_SWEEP_TIMEOUT)
}
pub fn wait_for_next_sweep_and_fill_buf(&self, buf: &mut [f32]) -> Result<usize> {
self.wait_for_next_sweep_with_timeout_and_fill_buf(Self::NEXT_SWEEP_TIMEOUT, buf)
}
pub fn wait_for_next_sweep_with_timeout(&self, timeout: Duration) -> Result<Vec<f32>> {
let previous_sweep_timestamp = self
.rfe
.messages()
.sweep
.0
.lock()
.unwrap()
.as_ref()
.map(|sweep| sweep.timestamp);
let (sweep, cond_var) = &self.messages().sweep;
let (sweep, wait_result) = cond_var
.wait_timeout_while(sweep.lock().unwrap(), timeout, |sweep| {
sweep.as_ref().map(|sweep| sweep.timestamp) == previous_sweep_timestamp
|| sweep.is_none()
})
.unwrap();
match &*sweep {
Some(sweep) if !wait_result.timed_out() => Ok(sweep.amplitudes_dbm.clone()),
_ => Err(Error::TimedOut(timeout)),
}
}
pub fn wait_for_next_sweep_with_timeout_and_fill_buf(
&self,
timeout: Duration,
buf: &mut [f32],
) -> Result<usize> {
let previous_sweep_timestamp = self
.rfe
.messages()
.sweep
.0
.lock()
.unwrap()
.as_ref()
.map(|sweep| sweep.timestamp);
let (sweep, cond_var) = &self.messages().sweep;
let (sweep, wait_result) = cond_var
.wait_timeout_while(sweep.lock().unwrap(), timeout, |sweep| {
sweep.as_ref().map(|sweep| sweep.timestamp) == previous_sweep_timestamp
|| sweep.is_none()
})
.unwrap();
drop(sweep);
if !wait_result.timed_out() {
self.fill_buf_with_sweep(buf)
} else {
Err(Error::TimedOut(timeout))
}
}
pub fn screen_data(&self) -> Option<ScreenData> {
self.messages().screen_data.0.lock().unwrap().clone()
}
pub fn wait_for_next_screen_data(&self) -> Result<ScreenData> {
self.wait_for_next_screen_data_with_timeout(NEXT_SCREEN_DATA_TIMEOUT)
}
pub fn wait_for_next_screen_data_with_timeout(&self, timeout: Duration) -> Result<ScreenData> {
let previous_screen_data = self.screen_data();
let (screen_data, condvar) = &self.messages().screen_data;
let (screen_data, wait_result) = condvar
.wait_timeout_while(screen_data.lock().unwrap(), timeout, |screen_data| {
*screen_data == previous_screen_data || screen_data.is_none()
})
.unwrap();
match &*screen_data {
Some(screen_data) if !wait_result.timed_out() => Ok(screen_data.clone()),
_ => Err(Error::TimedOut(timeout)),
}
}
pub fn dsp_mode(&self) -> Option<DspMode> {
*self.messages().dsp_mode.0.lock().unwrap()
}
pub fn tracking_status(&self) -> Option<TrackingStatus> {
*self.messages().tracking_status.0.lock().unwrap()
}
pub fn input_stage(&self) -> Option<InputStage> {
*self.messages().input_stage.0.lock().unwrap()
}
pub fn main_radio_model(&self) -> Option<Model> {
self.messages()
.setup_info
.0
.lock()
.unwrap()
.as_ref()
.unwrap()
.main_radio_model
}
pub fn expansion_radio_model(&self) -> Option<Model> {
self.rfe
.messages()
.setup_info
.0
.lock()
.unwrap()
.as_ref()
.unwrap()
.expansion_radio_model
}
pub fn active_radio_model(&self) -> Model {
if self.is_expansion_radio_module_active() {
self.expansion_radio_model().unwrap_or_default()
} else {
self.main_radio_model().unwrap_or_default()
}
}
pub fn inactive_radio_model(&self) -> Option<Model> {
let expansion_radio_model = self.expansion_radio_model();
if expansion_radio_model.is_some() {
if self.is_expansion_radio_module_active() {
self.main_radio_model()
} else {
expansion_radio_model
}
} else {
None
}
}
#[tracing::instrument]
pub fn start_wifi_analyzer(&self, wifi_band: WifiBand) -> io::Result<()> {
self.send_command(Command::StartWifiAnalyzer(wifi_band))
}
#[tracing::instrument(skip(self))]
pub fn stop_wifi_analyzer(&self) -> io::Result<()> {
self.send_command(Command::StopWifiAnalyzer)
}
#[tracing::instrument(skip(self))]
pub fn request_tracking(&self, start_hz: u64, step_hz: u64) -> Result<TrackingStatus> {
*self.messages().tracking_status.0.lock().unwrap() = None;
self.send_command(Command::StartTracking {
start: Frequency::from_hz(start_hz),
step: Frequency::from_hz(step_hz),
})?;
let (lock, condvar) = &self.messages().tracking_status;
let (tracking_status, wait_result) = condvar
.wait_timeout_while(
lock.lock().unwrap(),
COMMAND_RESPONSE_TIMEOUT,
|tracking_status| tracking_status.is_none(),
)
.unwrap();
if !wait_result.timed_out() {
Ok(tracking_status.unwrap_or_default())
} else {
Err(Error::TimedOut(COMMAND_RESPONSE_TIMEOUT))
}
}
#[tracing::instrument(skip(self))]
pub fn tracking_step(&self, step: u16) -> io::Result<()> {
self.send_command(Command::TrackingStep(step))
}
pub fn activate_main_radio(&self) -> Result<()> {
if !self.is_expansion_radio_module_active() {
return Err(Error::InvalidOperation(
"Main radio module is already active.".to_string(),
));
}
self.send_command(Command::SwitchModuleMain)?;
let _ = self.wait_for_config_while(|config| {
config
.as_ref()
.filter(|config| !config.is_expansion_radio_module_active)
.is_none()
});
if !self.is_expansion_radio_module_active() {
Ok(())
} else {
Err(Error::TimedOut(COMMAND_RESPONSE_TIMEOUT))
}
}
pub fn activate_expansion_radio(&self) -> Result<()> {
if self.expansion_radio_model().is_none() {
return Err(Error::InvalidOperation(
"This RF Explorer does not contain an expansion radio module.".to_string(),
));
}
if self.is_expansion_radio_module_active() {
return Err(Error::InvalidOperation(
"Expansion radio module is already active.".to_string(),
));
}
self.send_command(Command::SwitchModuleExp)?;
let _ = self.wait_for_config_while(|config| {
config
.as_ref()
.filter(|config| config.is_expansion_radio_module_active)
.is_none()
});
if self.is_expansion_radio_module_active() {
Ok(())
} else {
Err(Error::TimedOut(COMMAND_RESPONSE_TIMEOUT))
}
}
pub fn set_start_stop(
&self,
start: impl Into<Frequency>,
stop: impl Into<Frequency>,
) -> Result<()> {
self.set_config(
start.into(),
stop.into(),
self.min_amp_dbm(),
self.max_amp_dbm(),
)
}
pub fn set_start_stop_sweep_len(
&self,
start: impl Into<Frequency>,
stop: impl Into<Frequency>,
sweep_len: u16,
) -> Result<()> {
self.set_sweep_len(sweep_len)?;
self.set_start_stop(start, stop)
}
pub fn set_center_span(
&self,
center: impl Into<Frequency>,
span: impl Into<Frequency>,
) -> Result<()> {
let (start, stop) = self.start_stop_from_center_span(center.into(), span.into())?;
self.set_start_stop(start, stop)
}
pub fn set_center_span_sweep_len(
&self,
center: impl Into<Frequency>,
span: impl Into<Frequency>,
sweep_len: u16,
) -> Result<()> {
let (start, stop) = self.start_stop_from_center_span(center.into(), span.into())?;
self.set_start_stop_sweep_len(start, stop, sweep_len)
}
#[tracing::instrument(skip(self))]
pub fn set_min_max_amps(&self, min_amp_dbm: i16, max_amp_dbm: i16) -> Result<()> {
self.set_config(
self.start_freq(),
self.stop_freq(),
min_amp_dbm,
max_amp_dbm,
)
}
#[tracing::instrument(skip(self), ret, err)]
fn set_config(
&self,
start: Frequency,
stop: Frequency,
min_amp_dbm: i16,
max_amp_dbm: i16,
) -> Result<()> {
self.validate_start_stop(start, stop)?;
self.validate_min_max_amps(min_amp_dbm, max_amp_dbm)?;
self.send_command(Command::SetConfig {
start,
stop,
min_amp_dbm,
max_amp_dbm,
})?;
if self
.config()
.as_ref()
.unwrap_or(&Config::default())
.contains_start_stop_amp_range(start, stop, min_amp_dbm, max_amp_dbm)
{
return Ok(());
}
trace!("Waiting to receive updated 'Config'");
let (config, wait_result) = self.wait_for_config_while(|config| {
let Some(config) = config else {
return true;
};
!config.contains_start_stop_amp_range(start, stop, min_amp_dbm, max_amp_dbm)
});
drop(config);
if !wait_result.timed_out() {
Ok(())
} else {
Err(Error::TimedOut(COMMAND_RESPONSE_TIMEOUT))
}
}
pub fn set_sweep_callback(
&self,
cb: impl Fn(&[f32], Frequency, Frequency) + Send + Sync + 'static,
) {
*self.messages().sweep_callback.lock().unwrap() = Some(Arc::new(Box::new(cb)));
}
pub fn remove_sweep_callback(&self) {
*self.messages().sweep_callback.lock().unwrap() = None;
}
pub fn set_config_callback(&self, cb: impl Fn(Config) + Send + Sync + 'static) {
*self.messages().config_callback.lock().unwrap() = Some(Arc::new(Box::new(cb)));
}
pub fn remove_config_callback(&self) {
*self.messages().config_callback.lock().unwrap() = None;
}
#[tracing::instrument(skip(self))]
pub fn set_sweep_len(&self, sweep_len: u16) -> Result<()> {
if !self.active_radio_model().is_plus_model() {
return Err(Error::InvalidOperation(
"Only RF Explorer 'Plus' models support setting the number of sweep points"
.to_string(),
));
}
if sweep_len < Self::MIN_SWEEP_LEN {
return Err(Error::InvalidInput(format!(
"The sweep length must be at least {}",
Self::MIN_SWEEP_LEN
)));
}
if sweep_len <= 4096 {
self.send_command(Command::SetSweepPointsExt(sweep_len))?;
} else {
self.send_command(Command::SetSweepPointsLarge(sweep_len))?;
}
let expected_sweep_len = (sweep_len / 16) * 16;
if self.sweep_len() == expected_sweep_len {
return Ok(());
}
info!("Waiting to receive updated config");
let (config, wait_result) = self.wait_for_config_while(|config| {
config
.as_ref()
.filter(|config| config.sweep_len == expected_sweep_len)
.is_none()
});
drop(config);
if !wait_result.timed_out() {
Ok(())
} else {
warn!("Failed to receive updated config");
Err(Error::TimedOut(COMMAND_RESPONSE_TIMEOUT))
}
}
#[tracing::instrument(skip(self))]
pub fn set_calc_mode(&self, calc_mode: CalcMode) -> io::Result<()> {
self.send_command(Command::SetCalcMode(calc_mode))
}
#[tracing::instrument(skip(self))]
pub fn set_input_stage(&self, input_stage: InputStage) -> io::Result<()> {
self.send_command(Command::SetInputStage(input_stage))
}
#[tracing::instrument(skip(self))]
pub fn set_offset_db(&self, offset_db: i8) -> io::Result<()> {
self.send_command(Command::SetOffsetDB(offset_db))
}
#[tracing::instrument(skip(self))]
pub fn set_dsp_mode(&self, dsp_mode: DspMode) -> Result<()> {
if *self.messages().dsp_mode.0.lock().unwrap() == Some(dsp_mode) {
return Ok(());
}
self.send_command(Command::SetDsp(dsp_mode))?;
let (lock, condvar) = &self.messages().dsp_mode;
let (dsp_mode, wait_result) = condvar
.wait_timeout_while(
lock.lock().unwrap(),
COMMAND_RESPONSE_TIMEOUT,
|new_dsp_mode| *new_dsp_mode != Some(dsp_mode),
)
.unwrap();
drop(dsp_mode);
if !wait_result.timed_out() {
Ok(())
} else {
Err(Error::TimedOut(COMMAND_RESPONSE_TIMEOUT))
}
}
fn wait_for_config_while(
&'_ self,
condition: impl FnMut(&mut Option<Config>) -> bool,
) -> (MutexGuard<'_, Option<Config>>, WaitTimeoutResult) {
let (lock, condvar) = &self.messages().config;
condvar
.wait_timeout_while(lock.lock().unwrap(), COMMAND_RESPONSE_TIMEOUT, condition)
.unwrap()
}
fn start_stop_from_center_span(
&self,
center: Frequency,
span: Frequency,
) -> Result<(Frequency, Frequency)> {
let half_span_hz = span.as_hz() / 2;
if center.as_hz() < half_span_hz {
return Err(Error::InvalidInput(
"The span is too large for the center frequency".to_string(),
));
}
let Some(stop_hz) = center.as_hz().checked_add(half_span_hz) else {
return Err(Error::InvalidInput(
"The center frequency and span exceed the maximum representable frequency"
.to_string(),
));
};
let start = Frequency::from_hz(center.as_hz() - half_span_hz);
let stop = Frequency::from_hz(stop_hz);
self.validate_start_stop(start, stop)?;
Ok((start, stop))
}
#[tracing::instrument(skip(self), ret, err)]
fn validate_start_stop(&self, start: Frequency, stop: Frequency) -> Result<()> {
if start >= stop {
return Err(Error::InvalidInput(
"The start frequency must be less than the stop frequency".to_string(),
));
}
let active_model = self.active_radio_model();
let min_max_freq = active_model.min_freq()..=active_model.max_freq();
if !min_max_freq.contains(&start) {
return Err(Error::InvalidInput(format!(
"The start frequency {} MHz is not within the RF Explorer's frequency range of {}-{} MHz",
start.as_mhz_f64(),
min_max_freq.start().as_mhz_f64(),
min_max_freq.end().as_mhz_f64()
)));
} else if !min_max_freq.contains(&stop) {
return Err(Error::InvalidInput(format!(
"The stop frequency {} MHz is not within the RF Explorer's frequency range of {}-{} MHz",
stop.as_mhz(),
min_max_freq.start().as_mhz_f64(),
min_max_freq.end().as_mhz_f64()
)));
}
let min_max_span = active_model.min_span()..=active_model.max_span();
if !min_max_span.contains(&(stop - start)) {
return Err(Error::InvalidInput(format!(
"The span {} MHz is not within the RF Explorer's span range of {}-{} MHz",
(stop - start).as_mhz_f64(),
min_max_span.start().as_mhz_f64(),
min_max_span.end().as_mhz_f64()
)));
}
Ok(())
}
#[tracing::instrument(skip(self), ret, err)]
fn validate_min_max_amps(&self, min_amp_dbm: i16, max_amp_dbm: i16) -> Result<()> {
if min_amp_dbm >= max_amp_dbm {
error!("");
return Err(Error::InvalidInput(
"The minimum amplitude must be less than the maximum amplitude".to_string(),
));
}
if !Self::MIN_MAX_AMP_RANGE_DBM.contains(&min_amp_dbm) {
return Err(Error::InvalidInput(format!(
"The amplitude {} dBm is not within the RF Explorer's amplitude range of {}-{} dBm",
min_amp_dbm,
Self::MIN_MAX_AMP_RANGE_DBM.start(),
Self::MIN_MAX_AMP_RANGE_DBM.end()
)));
} else if !Self::MIN_MAX_AMP_RANGE_DBM.contains(&max_amp_dbm) {
return Err(Error::InvalidInput(format!(
"The amplitude {} dBm is not within the RF Explorer's amplitude range of {}-{} dBm",
max_amp_dbm,
Self::MIN_MAX_AMP_RANGE_DBM.start(),
Self::MIN_MAX_AMP_RANGE_DBM.end()
)));
}
Ok(())
}
}
#[derive(Default)]
struct MessageContainer {
pub(crate) config: (Mutex<Option<Config>>, Condvar),
pub(crate) config_callback: Mutex<ConfigCallback<Config>>,
pub(crate) sweep: (Mutex<Option<Sweep>>, Condvar),
pub(crate) sweep_callback: Mutex<Option<SweepCallback>>,
pub(crate) screen_data: (Mutex<Option<ScreenData>>, Condvar),
pub(crate) dsp_mode: (Mutex<Option<DspMode>>, Condvar),
pub(crate) tracking_status: (Mutex<Option<TrackingStatus>>, Condvar),
pub(crate) input_stage: (Mutex<Option<InputStage>>, Condvar),
pub(crate) setup_info: (Mutex<Option<SetupInfo>>, Condvar),
pub(crate) serial_number: (Mutex<Option<SerialNumber>>, Condvar),
}
type SweepCallback = Arc<Box<dyn Fn(&[f32], Frequency, Frequency) + Send + Sync + 'static>>;
impl crate::common::MessageContainer for MessageContainer {
type Message = super::Message;
fn cache_message(&self, message: Self::Message) {
match message {
Self::Message::Config(config) => {
*self.config.0.lock().unwrap() = Some(config);
self.config.1.notify_one();
if let Some(cb) = self.config_callback.lock().unwrap().clone()
&& let Some(config) = self.config.0.lock().unwrap().clone()
{
thread::spawn(move || {
cb(config);
});
}
}
Self::Message::Sweep(sweep) => {
*self.sweep.0.lock().unwrap() = Some(sweep);
self.sweep.1.notify_one();
if let Some(cb) = self.sweep_callback.lock().unwrap().clone() {
let (start_freq, stop_freq) = {
let config = self.config.0.lock().unwrap();
(
config
.as_ref()
.map(|config| config.start_freq)
.unwrap_or_default(),
config
.as_ref()
.map(|config| config.stop_freq)
.unwrap_or_default(),
)
};
if let Some(sweep) = self.sweep.0.lock().unwrap().clone() {
thread::spawn(move || {
cb(sweep.amplitudes_dbm.as_slice(), start_freq, stop_freq);
});
}
}
}
Self::Message::ScreenData(screen_data) => {
*self.screen_data.0.lock().unwrap() = Some(screen_data);
self.screen_data.1.notify_one();
}
Self::Message::DspMode(dsp_mode) => {
*self.dsp_mode.0.lock().unwrap() = Some(dsp_mode);
self.dsp_mode.1.notify_one();
}
Self::Message::InputStage(input_stage) => {
*self.input_stage.0.lock().unwrap() = Some(input_stage);
self.input_stage.1.notify_one();
}
Self::Message::TrackingStatus(tracking_status) => {
*self.tracking_status.0.lock().unwrap() = Some(tracking_status);
self.tracking_status.1.notify_one();
}
Self::Message::SerialNumber(serial_number) => {
*self.serial_number.0.lock().unwrap() = Some(serial_number);
self.serial_number.1.notify_one();
}
Self::Message::SetupInfo(setup_info) => {
*self.setup_info.0.lock().unwrap() = Some(setup_info);
self.setup_info.1.notify_one();
}
}
}
fn wait_for_device_info(&self) -> ConnectionResult<()> {
let (config_lock, config_cvar) = &self.config;
let (setup_info_lock, setup_info_cvar) = &self.setup_info;
if config_lock.lock().unwrap().is_some() && setup_info_lock.lock().unwrap().is_some() {
return Ok(());
}
if config_cvar
.wait_timeout_while(
config_lock.lock().unwrap(),
RECEIVE_INITIAL_DEVICE_INFO_TIMEOUT,
|config| config.is_none(),
)
.unwrap()
.0
.is_some()
&& setup_info_cvar
.wait_timeout_while(
setup_info_lock.lock().unwrap(),
RECEIVE_INITIAL_DEVICE_INFO_TIMEOUT,
|setup_info| setup_info.is_none(),
)
.unwrap()
.0
.is_some()
{
Ok(())
} else {
Err(ConnectionError::DeviceInfoNotReceived)
}
}
}
impl Debug for MessageContainer {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("MessageContainer")
.field("config", &self.config.0.lock().unwrap())
.field("sweep", &self.sweep.0.lock().unwrap())
.field("screen_data", &self.screen_data.0.lock().unwrap())
.field("dsp_mode", &self.dsp_mode.0.lock().unwrap())
.field("tracking_status", &self.tracking_status.0.lock().unwrap())
.field("input_stage", &self.input_stage.0.lock().unwrap())
.field("setup_info", &self.setup_info.0.lock().unwrap())
.field("serial_number", &self.serial_number.0.lock().unwrap())
.finish()
}
}