diff --git a/src/trx-client/trx-frontend/trx-frontend-http/src/api.rs b/src/trx-client/trx-frontend/trx-frontend-http/src/api.rs index 4eed79c..9805a30 100644 --- a/src/trx-client/trx-frontend/trx-frontend-http/src/api.rs +++ b/src/trx-client/trx-frontend/trx-frontend-http/src/api.rs @@ -527,15 +527,17 @@ fn sync_scheduler_vchannels( .last_bookmark_ids .iter() .filter_map(|bookmark_id| { - bookmark_store_map.get_for_rig(rig_id, bookmark_id).map(|bookmark| { - ( - bookmark_id.clone(), - bookmark.freq_hz, - bookmark.mode.clone(), - bookmark.bandwidth_hz.unwrap_or(0) as u32, - bookmark_decoder_kinds(&bookmark), - ) - }) + bookmark_store_map + .get_for_rig(rig_id, bookmark_id) + .map(|bookmark| { + ( + bookmark_id.clone(), + bookmark.freq_hz, + bookmark.mode.clone(), + bookmark.bandwidth_hz.unwrap_or(0) as u32, + bookmark_decoder_kinds(&bookmark), + ) + }) }) .collect::>() }) @@ -573,7 +575,10 @@ impl DecodeHistoryPayload { /// Build the grouped decode history payload from all per-decoder ring-buffers. /// When `rig_filter` is `Some`, only entries recorded for that rig are included. -fn collect_decode_history(context: &FrontendRuntimeContext, rig_filter: Option<&str>) -> DecodeHistoryPayload { +fn collect_decode_history( + context: &FrontendRuntimeContext, + rig_filter: Option<&str>, +) -> DecodeHistoryPayload { DecodeHistoryPayload { ais: crate::server::audio::snapshot_ais_history(context, rig_filter), vdes: crate::server::audio::snapshot_vdes_history(context, rig_filter), @@ -1497,7 +1502,10 @@ pub async fn list_bookmarks( status::index_html(), )); } - let scope = query.scope.as_deref().filter(|s| !s.is_empty() && *s != "general"); + let scope = query + .scope + .as_deref() + .filter(|s| !s.is_empty() && *s != "general"); let mut list: Vec = match scope { Some(remote) => { // Rig selected: merge general + rig-specific (rig wins on duplicate IDs). @@ -1507,20 +1515,36 @@ pub async fn list_bookmarks( .into_iter() .map(|bm| { let id = bm.id.clone(); - (id, BookmarkWithScope { bm, scope: "general".into() }) + ( + id, + BookmarkWithScope { + bm, + scope: "general".into(), + }, + ) }) .collect(); for bm in store_map.store_for(remote).list() { let id = bm.id.clone(); - map.insert(id, BookmarkWithScope { bm, scope: remote.to_owned() }); + map.insert( + id, + BookmarkWithScope { + bm, + scope: remote.to_owned(), + }, + ); } map.into_values().collect() } - None => { - store_map.general().list().into_iter() - .map(|bm| BookmarkWithScope { bm, scope: "general".into() }) - .collect() - } + None => store_map + .general() + .list() + .into_iter() + .map(|bm| BookmarkWithScope { + bm, + scope: "general".into(), + }) + .collect(), }; if let Some(ref cat) = query.category { if !cat.is_empty() { diff --git a/src/trx-client/trx-frontend/trx-frontend-http/src/audio.rs b/src/trx-client/trx-frontend/trx-frontend-http/src/audio.rs index a510226..bb83f99 100644 --- a/src/trx-client/trx-frontend/trx-frontend-http/src/audio.rs +++ b/src/trx-client/trx-frontend/trx-frontend-http/src/audio.rs @@ -141,7 +141,10 @@ fn record_vdes(context: &FrontendRuntimeContext, mut msg: VdesMessage) { prune_vdes_history(context, &mut history); } -fn prune_cw_history(context: &FrontendRuntimeContext, history: &mut VecDeque<(Instant, Option, CwEvent)>) { +fn prune_cw_history( + context: &FrontendRuntimeContext, + history: &mut VecDeque<(Instant, Option, CwEvent)>, +) { let cutoff = decode_history_cutoff(context); while let Some((ts, _, _)) = history.front() { if *ts >= cutoff { @@ -281,25 +284,33 @@ fn matches_rig_filter(entry_rig: Option<&str>, filter: Option<&str>) -> bool { } } -pub fn snapshot_aprs_history(context: &FrontendRuntimeContext, rig_filter: Option<&str>) -> Vec { +pub fn snapshot_aprs_history( + context: &FrontendRuntimeContext, + rig_filter: Option<&str>, +) -> Vec { let mut history = context .aprs_history .lock() .expect("aprs history mutex poisoned"); prune_aprs_history(context, &mut history); - history.iter() + history + .iter() .filter(|(_, rid, _)| matches_rig_filter(rid.as_deref(), rig_filter)) .map(|(_, _, pkt)| pkt.clone()) .collect() } -pub fn snapshot_hf_aprs_history(context: &FrontendRuntimeContext, rig_filter: Option<&str>) -> Vec { +pub fn snapshot_hf_aprs_history( + context: &FrontendRuntimeContext, + rig_filter: Option<&str>, +) -> Vec { let mut history = context .hf_aprs_history .lock() .expect("hf_aprs history mutex poisoned"); prune_hf_aprs_history(context, &mut history); - history.iter() + history + .iter() .filter(|(_, rid, _)| matches_rig_filter(rid.as_deref(), rig_filter)) .map(|(_, _, pkt)| pkt.clone()) .collect() @@ -312,7 +323,10 @@ pub fn snapshot_hf_aprs_history(context: &FrontendRuntimeContext, rig_filter: Op /// what the map shows (current position/state) and keeps the response compact. /// The returned vec is sorted ascending by `ts_ms` so the client can replay /// in chronological order. -pub fn snapshot_ais_history(context: &FrontendRuntimeContext, rig_filter: Option<&str>) -> Vec { +pub fn snapshot_ais_history( + context: &FrontendRuntimeContext, + rig_filter: Option<&str>, +) -> Vec { let mut history = context .ais_history .lock() @@ -331,73 +345,97 @@ pub fn snapshot_ais_history(context: &FrontendRuntimeContext, rig_filter: Option out } -pub fn snapshot_vdes_history(context: &FrontendRuntimeContext, rig_filter: Option<&str>) -> Vec { +pub fn snapshot_vdes_history( + context: &FrontendRuntimeContext, + rig_filter: Option<&str>, +) -> Vec { let mut history = context .vdes_history .lock() .expect("vdes history mutex poisoned"); prune_vdes_history(context, &mut history); - history.iter() + history + .iter() .filter(|(_, rid, _)| matches_rig_filter(rid.as_deref(), rig_filter)) .map(|(_, _, msg)| msg.clone()) .collect() } -pub fn snapshot_cw_history(context: &FrontendRuntimeContext, rig_filter: Option<&str>) -> Vec { +pub fn snapshot_cw_history( + context: &FrontendRuntimeContext, + rig_filter: Option<&str>, +) -> Vec { let mut history = context .cw_history .lock() .expect("cw history mutex poisoned"); prune_cw_history(context, &mut history); - history.iter() + history + .iter() .filter(|(_, rid, _)| matches_rig_filter(rid.as_deref(), rig_filter)) .map(|(_, _, evt)| evt.clone()) .collect() } -pub fn snapshot_ft8_history(context: &FrontendRuntimeContext, rig_filter: Option<&str>) -> Vec { +pub fn snapshot_ft8_history( + context: &FrontendRuntimeContext, + rig_filter: Option<&str>, +) -> Vec { let mut history = context .ft8_history .lock() .expect("ft8 history mutex poisoned"); prune_ft8_history(context, &mut history); - history.iter() + history + .iter() .filter(|(_, rid, _)| matches_rig_filter(rid.as_deref(), rig_filter)) .map(|(_, _, msg)| msg.clone()) .collect() } -pub fn snapshot_ft4_history(context: &FrontendRuntimeContext, rig_filter: Option<&str>) -> Vec { +pub fn snapshot_ft4_history( + context: &FrontendRuntimeContext, + rig_filter: Option<&str>, +) -> Vec { let mut history = context .ft4_history .lock() .expect("ft4 history mutex poisoned"); prune_ft4_history(context, &mut history); - history.iter() + history + .iter() .filter(|(_, rid, _)| matches_rig_filter(rid.as_deref(), rig_filter)) .map(|(_, _, msg)| msg.clone()) .collect() } -pub fn snapshot_ft2_history(context: &FrontendRuntimeContext, rig_filter: Option<&str>) -> Vec { +pub fn snapshot_ft2_history( + context: &FrontendRuntimeContext, + rig_filter: Option<&str>, +) -> Vec { let mut history = context .ft2_history .lock() .expect("ft2 history mutex poisoned"); prune_ft2_history(context, &mut history); - history.iter() + history + .iter() .filter(|(_, rid, _)| matches_rig_filter(rid.as_deref(), rig_filter)) .map(|(_, _, msg)| msg.clone()) .collect() } -pub fn snapshot_wspr_history(context: &FrontendRuntimeContext, rig_filter: Option<&str>) -> Vec { +pub fn snapshot_wspr_history( + context: &FrontendRuntimeContext, + rig_filter: Option<&str>, +) -> Vec { let mut history = context .wspr_history .lock() .expect("wspr history mutex poisoned"); prune_wspr_history(context, &mut history); - history.iter() + history + .iter() .filter(|(_, rid, _)| matches_rig_filter(rid.as_deref(), rig_filter)) .map(|(_, _, msg)| msg.clone()) .collect() diff --git a/src/trx-client/trx-frontend/trx-frontend-http/src/scheduler.rs b/src/trx-client/trx-frontend/trx-frontend-http/src/scheduler.rs index b9899e9..8dc9d87 100644 --- a/src/trx-client/trx-frontend/trx-frontend-http/src/scheduler.rs +++ b/src/trx-client/trx-frontend/trx-frontend-http/src/scheduler.rs @@ -202,10 +202,7 @@ impl SchedulerStoreMap { /// List configs from all known per-rig stores. pub fn list_all(&self) -> Vec { let stores = self.stores.lock().unwrap_or_else(|e| e.into_inner()); - stores - .values() - .filter_map(|s| s.get_config()) - .collect() + stores.values().filter_map(|s| s.get_config()).collect() } /// One-time migration: extract `sch:{remote}` entries from legacy @@ -887,13 +884,16 @@ pub async fn get_scheduler( store_map: web::Data>, ) -> impl Responder { let remote = path.into_inner(); - let config = store_map.store_for(&remote).get_config().unwrap_or(SchedulerConfig { - remote: remote.clone(), - mode: SchedulerMode::Disabled, - grayline: None, - entries: vec![], - interleave_min: None, - }); + let config = store_map + .store_for(&remote) + .get_config() + .unwrap_or(SchedulerConfig { + remote: remote.clone(), + mode: SchedulerMode::Disabled, + grayline: None, + entries: vec![], + interleave_min: None, + }); HttpResponse::Ok().json(config) } diff --git a/src/trx-client/trx-frontend/trx-frontend-http/src/vchan.rs b/src/trx-client/trx-frontend/trx-frontend-http/src/vchan.rs index ad5fb5e..1d45b1b 100644 --- a/src/trx-client/trx-frontend/trx-frontend-http/src/vchan.rs +++ b/src/trx-client/trx-frontend/trx-frontend-http/src/vchan.rs @@ -160,7 +160,12 @@ impl ClientChannelManager { } } // Fall back to global sender. - if let Some(tx) = self.audio_cmd.lock().unwrap_or_else(|e| e.into_inner()).as_ref() { + if let Some(tx) = self + .audio_cmd + .lock() + .unwrap_or_else(|e| e.into_inner()) + .as_ref() + { let _ = tx.try_send(cmd); } } @@ -540,7 +545,11 @@ impl ClientChannelManager { /// Return the channel a session is currently subscribed to. pub fn session_channel(&self, session_id: Uuid) -> Option<(String, Uuid)> { - self.sessions.read().unwrap_or_else(|e| e.into_inner()).get(&session_id).cloned() + self.sessions + .read() + .unwrap_or_else(|e| e.into_inner()) + .get(&session_id) + .cloned() } /// Return the selected channel's tune metadata. diff --git a/src/trx-configurator/src/check.rs b/src/trx-configurator/src/check.rs index b45bcd2..b4039c8 100644 --- a/src/trx-configurator/src/check.rs +++ b/src/trx-configurator/src/check.rs @@ -217,7 +217,12 @@ fn check_server_sections( errors: &mut Vec, ) { if let Some(general) = table.get("general").and_then(|v| v.as_table()) { - check_unknown_keys(general, SERVER_GENERAL_KEYS, &format!("{}[general].", prefix), warnings); + check_unknown_keys( + general, + SERVER_GENERAL_KEYS, + &format!("{}[general].", prefix), + warnings, + ); validate_log_level(general, &format!("{}[general]", prefix), errors); validate_coordinates(general, &format!("{}[general]", prefix), errors); } @@ -225,13 +230,23 @@ fn check_server_sections( if let Some(rig) = table.get("rig").and_then(|v| v.as_table()) { check_unknown_keys(rig, RIG_KEYS, &format!("{}[rig].", prefix), warnings); if let Some(access) = rig.get("access").and_then(|v| v.as_table()) { - check_unknown_keys(access, ACCESS_KEYS, &format!("{}[rig.access].", prefix), warnings); + check_unknown_keys( + access, + ACCESS_KEYS, + &format!("{}[rig.access].", prefix), + warnings, + ); validate_access(access, &format!("{}[rig.access]", prefix), errors); } } if let Some(listen) = table.get("listen").and_then(|v| v.as_table()) { - check_unknown_keys(listen, LISTEN_KEYS, &format!("{}[listen].", prefix), warnings); + check_unknown_keys( + listen, + LISTEN_KEYS, + &format!("{}[listen].", prefix), + warnings, + ); validate_port(listen, "port", &format!("{}[listen]", prefix), errors); } @@ -241,7 +256,12 @@ fn check_server_sections( } if let Some(behavior) = table.get("behavior").and_then(|v| v.as_table()) { - check_unknown_keys(behavior, BEHAVIOR_KEYS, &format!("{}[behavior].", prefix), warnings); + check_unknown_keys( + behavior, + BEHAVIOR_KEYS, + &format!("{}[behavior].", prefix), + warnings, + ); } } @@ -252,21 +272,41 @@ fn check_client_sections( errors: &mut Vec, ) { if let Some(general) = table.get("general").and_then(|v| v.as_table()) { - check_unknown_keys(general, CLIENT_GENERAL_KEYS, &format!("{}[general].", prefix), warnings); + check_unknown_keys( + general, + CLIENT_GENERAL_KEYS, + &format!("{}[general].", prefix), + warnings, + ); validate_log_level(general, &format!("{}[general]", prefix), errors); } if let Some(remote) = table.get("remote").and_then(|v| v.as_table()) { - check_unknown_keys(remote, REMOTE_KEYS, &format!("{}[remote].", prefix), warnings); + check_unknown_keys( + remote, + REMOTE_KEYS, + &format!("{}[remote].", prefix), + warnings, + ); } if let Some(frontends) = table.get("frontends").and_then(|v| v.as_table()) { - check_unknown_keys(frontends, FRONTENDS_KEYS, &format!("{}[frontends].", prefix), warnings); + check_unknown_keys( + frontends, + FRONTENDS_KEYS, + &format!("{}[frontends].", prefix), + warnings, + ); if let Some(http) = frontends.get("http").and_then(|v| v.as_table()) { validate_port(http, "port", &format!("{}[frontends.http]", prefix), errors); } if let Some(rigctl) = frontends.get("rigctl").and_then(|v| v.as_table()) { - validate_port(rigctl, "port", &format!("{}[frontends.rigctl]", prefix), errors); + validate_port( + rigctl, + "port", + &format!("{}[frontends.rigctl]", prefix), + errors, + ); } } } @@ -285,12 +325,21 @@ fn validate_log_level(table: &toml_edit::Table, context: &str, errors: &mut Vec< } fn validate_coordinates(table: &toml_edit::Table, context: &str, errors: &mut Vec) { - if let Some(lat) = table.get("latitude").and_then(|v| v.as_float().or_else(|| v.as_integer().map(|i| i as f64))) { + if let Some(lat) = table + .get("latitude") + .and_then(|v| v.as_float().or_else(|| v.as_integer().map(|i| i as f64))) + { if !(-90.0..=90.0).contains(&lat) { - errors.push(format!("{}.latitude {} is out of range (-90..90)", context, lat)); + errors.push(format!( + "{}.latitude {} is out of range (-90..90)", + context, lat + )); } } - if let Some(lon) = table.get("longitude").and_then(|v| v.as_float().or_else(|| v.as_integer().map(|i| i as f64))) { + if let Some(lon) = table + .get("longitude") + .and_then(|v| v.as_float().or_else(|| v.as_integer().map(|i| i as f64))) + { if !(-180.0..=180.0).contains(&lon) { errors.push(format!( "{}.longitude {} is out of range (-180..180)", @@ -309,12 +358,7 @@ fn validate_coordinates(table: &toml_edit::Table, context: &str, errors: &mut Ve } } -fn validate_port( - table: &toml_edit::Table, - key: &str, - context: &str, - errors: &mut Vec, -) { +fn validate_port(table: &toml_edit::Table, key: &str, context: &str, errors: &mut Vec) { if let Some(port) = table.get(key).and_then(|v| v.as_integer()) { if let Some(enabled) = table.get("enabled").and_then(|v| v.as_bool()) { if enabled && port <= 0 { diff --git a/src/trx-configurator/src/main.rs b/src/trx-configurator/src/main.rs index a08322c..3768418 100644 --- a/src/trx-configurator/src/main.rs +++ b/src/trx-configurator/src/main.rs @@ -12,7 +12,10 @@ use std::path::PathBuf; use clap::Parser; #[derive(Parser)] -#[command(name = "trx-configurator", about = "Interactive configuration generator for trx-rs")] +#[command( + name = "trx-configurator", + about = "Interactive configuration generator for trx-rs" +)] struct Cli { /// Generate a default config without interactive prompts #[arg(long)] @@ -70,7 +73,10 @@ fn main() { "client" => ConfigType::Client, "combined" => ConfigType::Combined, other => { - eprintln!("Unknown config type '{}'. Use: server, client, combined", other); + eprintln!( + "Unknown config type '{}'. Use: server, client, combined", + other + ); std::process::exit(1); } } diff --git a/src/trx-configurator/src/prompts.rs b/src/trx-configurator/src/prompts.rs index 297db72..fca61fa 100644 --- a/src/trx-configurator/src/prompts.rs +++ b/src/trx-configurator/src/prompts.rs @@ -51,7 +51,11 @@ pub struct FrontendsSetup { // ── Prompt functions ──────────────────────────────────────────────────── pub fn prompt_config_type() -> ConfigType { - let items = &["Server (trx-server.toml)", "Client (trx-client.toml)", "Combined (trx-rs.toml)"]; + let items = &[ + "Server (trx-server.toml)", + "Client (trx-client.toml)", + "Combined (trx-rs.toml)", + ]; let sel = Select::new() .with_prompt("What configuration would you like to generate?") .items(items) diff --git a/src/trx-configurator/src/writer.rs b/src/trx-configurator/src/writer.rs index 0b25e87..ed660b4 100644 --- a/src/trx-configurator/src/writer.rs +++ b/src/trx-configurator/src/writer.rs @@ -16,8 +16,7 @@ use crate::ConfigType; fn commented(table: &mut Table, key: &str, val: Item, comment: &str) { table.insert(key, val); if let Some(mut kv) = table.key_mut(key) { - kv.leaf_decor_mut() - .set_prefix(format!("# {}\n", comment)); + kv.leaf_decor_mut().set_prefix(format!("# {}\n", comment)); } } @@ -27,24 +26,40 @@ fn header_comment(table: &mut Table, comment: &str) { // ── Server document builder ───────────────────────────────────────────── -fn build_server_tables( - general: ServerGeneral, - rig: RigSetup, - listen: ListenSetup, -) -> Table { +fn build_server_tables(general: ServerGeneral, rig: RigSetup, listen: ListenSetup) -> Table { let mut root = Table::new(); // [general] { let mut t = Table::new(); header_comment(&mut t, "General settings"); - commented(&mut t, "callsign", value(&general.callsign), "Station callsign"); - commented(&mut t, "log_level", value(&general.log_level), "Log level (trace, debug, info, warn, error)"); + commented( + &mut t, + "callsign", + value(&general.callsign), + "Station callsign", + ); + commented( + &mut t, + "log_level", + value(&general.log_level), + "Log level (trace, debug, info, warn, error)", + ); if let Some(lat) = general.latitude { - commented(&mut t, "latitude", value(lat), "Station latitude (decimal degrees, WGS84)"); + commented( + &mut t, + "latitude", + value(lat), + "Station latitude (decimal degrees, WGS84)", + ); } if let Some(lon) = general.longitude { - commented(&mut t, "longitude", value(lon), "Station longitude (decimal degrees, WGS84)"); + commented( + &mut t, + "longitude", + value(lon), + "Station longitude (decimal degrees, WGS84)", + ); } root.insert("general", Item::Table(t)); } @@ -53,18 +68,38 @@ fn build_server_tables( { let mut t = Table::new(); header_comment(&mut t, "Rig backend configuration"); - commented(&mut t, "model", value(&rig.model), "Rig model (ft817, ft450d, soapysdr)"); - commented(&mut t, "initial_freq_hz", value(144_300_000i64), "Initial frequency in Hz"); + commented( + &mut t, + "model", + value(&rig.model), + "Rig model (ft817, ft450d, soapysdr)", + ); + commented( + &mut t, + "initial_freq_hz", + value(144_300_000i64), + "Initial frequency in Hz", + ); commented(&mut t, "initial_mode", value("USB"), "Initial mode"); // [rig.access] let mut access = Table::new(); header_comment(&mut access, "Rig access method"); - commented(&mut access, "type", value(&rig.access_type), "Access type: serial, tcp, or sdr"); + commented( + &mut access, + "type", + value(&rig.access_type), + "Access type: serial, tcp, or sdr", + ); match rig.access_type.as_str() { "serial" => { if let Some(port) = &rig.serial_port { - commented(&mut access, "port", value(port.as_str()), "Serial port path"); + commented( + &mut access, + "port", + value(port.as_str()), + "Serial port path", + ); } if let Some(baud) = rig.serial_baud { commented(&mut access, "baud", value(baud as i64), "Baud rate"); @@ -80,7 +115,12 @@ fn build_server_tables( } "sdr" => { if let Some(args) = &rig.sdr_args { - commented(&mut access, "args", value(args.as_str()), "SoapySDR device args string"); + commented( + &mut access, + "args", + value(args.as_str()), + "SoapySDR device args string", + ); } } _ => {} @@ -93,10 +133,25 @@ fn build_server_tables( { let mut t = Table::new(); header_comment(&mut t, "Polling and retry behavior"); - commented(&mut t, "poll_interval_ms", value(500i64), "Rig polling interval (ms)"); - commented(&mut t, "poll_interval_tx_ms", value(100i64), "Polling interval during TX (ms)"); + commented( + &mut t, + "poll_interval_ms", + value(500i64), + "Rig polling interval (ms)", + ); + commented( + &mut t, + "poll_interval_tx_ms", + value(100i64), + "Polling interval during TX (ms)", + ); commented(&mut t, "max_retries", value(3i64), "Maximum retry attempts"); - commented(&mut t, "retry_base_delay_ms", value(100i64), "Base retry delay (ms)"); + commented( + &mut t, + "retry_base_delay_ms", + value(100i64), + "Base retry delay (ms)", + ); root.insert("behavior", Item::Table(t)); } @@ -105,8 +160,18 @@ fn build_server_tables( let mut t = Table::new(); header_comment(&mut t, "JSON TCP listener for client connections"); commented(&mut t, "enabled", value(true), "Enable the TCP listener"); - commented(&mut t, "listen", value(&listen.listen), "IP address to listen on"); - commented(&mut t, "port", value(listen.port as i64), "TCP port for client connections"); + commented( + &mut t, + "listen", + value(&listen.listen), + "IP address to listen on", + ); + commented( + &mut t, + "port", + value(listen.port as i64), + "TCP port for client connections", + ); root.insert("listen", Item::Table(t)); } @@ -118,22 +183,34 @@ fn build_server_tables( commented(&mut t, "listen", value("127.0.0.1"), "Audio listen address"); commented(&mut t, "port", value(4531i64), "Audio TCP port"); commented(&mut t, "sample_rate", value(48000i64), "Sample rate in Hz"); - commented(&mut t, "channels", value(2i64), "Channel count (1 = mono, 2 = stereo)"); - commented(&mut t, "frame_duration_ms", value(20i64), "Opus frame duration (ms)"); - commented(&mut t, "bitrate_bps", value(256000i64), "Opus bitrate (bps)"); + commented( + &mut t, + "channels", + value(2i64), + "Channel count (1 = mono, 2 = stereo)", + ); + commented( + &mut t, + "frame_duration_ms", + value(20i64), + "Opus frame duration (ms)", + ); + commented( + &mut t, + "bitrate_bps", + value(256000i64), + "Opus bitrate (bps)", + ); root.insert("audio", Item::Table(t)); } root } -pub fn build_server( - general: ServerGeneral, - rig: RigSetup, - listen: ListenSetup, -) -> DocumentMut { +pub fn build_server(general: ServerGeneral, rig: RigSetup, listen: ListenSetup) -> DocumentMut { let mut doc = DocumentMut::new(); - doc.decor_mut().set_prefix("# trx-server configuration\n# Generated by trx-configurator\n"); + doc.decor_mut() + .set_prefix("# trx-server configuration\n# Generated by trx-configurator\n"); let tables = build_server_tables(general, rig, listen); for (key, item) in tables.iter() { doc.insert(key, item.clone()); @@ -154,8 +231,18 @@ fn build_client_tables( { let mut t = Table::new(); header_comment(&mut t, "General settings"); - commented(&mut t, "callsign", value(&general.callsign), "Station callsign"); - commented(&mut t, "log_level", value(&general.log_level), "Log level (trace, debug, info, warn, error)"); + commented( + &mut t, + "callsign", + value(&general.callsign), + "Station callsign", + ); + commented( + &mut t, + "log_level", + value(&general.log_level), + "Log level (trace, debug, info, warn, error)", + ); root.insert("general", Item::Table(t)); } @@ -163,8 +250,18 @@ fn build_client_tables( { let mut t = Table::new(); header_comment(&mut t, "Remote server connection"); - commented(&mut t, "url", value(&remote.url), "Server address (host:port)"); - commented(&mut t, "poll_interval_ms", value(750i64), "State poll interval (ms)"); + commented( + &mut t, + "url", + value(&remote.url), + "Server address (host:port)", + ); + commented( + &mut t, + "poll_interval_ms", + value(750i64), + "State poll interval (ms)", + ); let mut auth = Table::new(); if let Some(token) = &remote.token { @@ -182,26 +279,61 @@ fn build_client_tables( header_comment(&mut frontends_table, "Frontend configuration"); let mut http = Table::new(); - commented(&mut http, "enabled", value(frontends.http_enabled), "Enable HTTP web frontend"); + commented( + &mut http, + "enabled", + value(frontends.http_enabled), + "Enable HTTP web frontend", + ); commented(&mut http, "listen", value("127.0.0.1"), "Listen address"); - commented(&mut http, "port", value(frontends.http_port as i64), "HTTP port"); + commented( + &mut http, + "port", + value(frontends.http_port as i64), + "HTTP port", + ); frontends_table.insert("http", Item::Table(http)); let mut rigctl = Table::new(); - commented(&mut rigctl, "enabled", value(frontends.rigctl_enabled), "Enable Hamlib rigctl frontend"); + commented( + &mut rigctl, + "enabled", + value(frontends.rigctl_enabled), + "Enable Hamlib rigctl frontend", + ); commented(&mut rigctl, "listen", value("127.0.0.1"), "Listen address"); - commented(&mut rigctl, "port", value(frontends.rigctl_port as i64), "rigctl port"); + commented( + &mut rigctl, + "port", + value(frontends.rigctl_port as i64), + "rigctl port", + ); frontends_table.insert("rigctl", Item::Table(rigctl)); let mut http_json = Table::new(); - commented(&mut http_json, "enabled", value(true), "Enable JSON-over-TCP frontend"); - commented(&mut http_json, "listen", value("127.0.0.1"), "Listen address"); + commented( + &mut http_json, + "enabled", + value(true), + "Enable JSON-over-TCP frontend", + ); + commented( + &mut http_json, + "listen", + value("127.0.0.1"), + "Listen address", + ); commented(&mut http_json, "port", value(0i64), "Port (0 = ephemeral)"); frontends_table.insert("http_json", Item::Table(http_json)); let mut audio = Table::new(); commented(&mut audio, "enabled", value(true), "Enable audio client"); - commented(&mut audio, "server_port", value(4531i64), "Server audio port"); + commented( + &mut audio, + "server_port", + value(4531i64), + "Server audio port", + ); frontends_table.insert("audio", Item::Table(audio)); root.insert("frontends", Item::Table(frontends_table)); @@ -216,7 +348,8 @@ pub fn build_client( frontends: FrontendsSetup, ) -> DocumentMut { let mut doc = DocumentMut::new(); - doc.decor_mut().set_prefix("# trx-client configuration\n# Generated by trx-configurator\n"); + doc.decor_mut() + .set_prefix("# trx-client configuration\n# Generated by trx-configurator\n"); let tables = build_client_tables(general, remote, frontends); for (key, item) in tables.iter() { doc.insert(key, item.clone()); @@ -235,7 +368,8 @@ pub fn build_combined( frontends: FrontendsSetup, ) -> DocumentMut { let mut doc = DocumentMut::new(); - doc.decor_mut().set_prefix("# trx-rs combined configuration\n# Generated by trx-configurator\n"); + doc.decor_mut() + .set_prefix("# trx-rs combined configuration\n# Generated by trx-configurator\n"); let server = build_server_tables(s_general, rig, listen); let mut server_item = Item::Table(server); @@ -294,7 +428,9 @@ pub fn build_default(config_type: ConfigType) -> DocumentMut { match config_type { ConfigType::Server => build_server(s_general, rig, listen), ConfigType::Client => build_client(c_general, remote, frontends), - ConfigType::Combined => build_combined(s_general, rig, listen, c_general, remote, frontends), + ConfigType::Combined => { + build_combined(s_general, rig, listen, c_general, remote, frontends) + } } } @@ -312,7 +448,8 @@ pub fn write_file(doc: &DocumentMut, path: &Path) -> Result<(), String> { } } - std::fs::write(path, doc.to_string()).map_err(|e| format!("Failed to write {}: {}", path.display(), e))?; + std::fs::write(path, doc.to_string()) + .map_err(|e| format!("Failed to write {}: {}", path.display(), e))?; println!("Wrote {}", path.display()); Ok(()) } diff --git a/src/trx-server/src/audio.rs b/src/trx-server/src/audio.rs index f848786..d6f2c24 100644 --- a/src/trx-server/src/audio.rs +++ b/src/trx-server/src/audio.rs @@ -138,10 +138,7 @@ impl StreamErrorLogger { fn log(&self, err: &str) { let now = Instant::now(); let kind = classify_stream_error(err); - let mut state = self - .state - .lock() - .unwrap_or_else(|e| e.into_inner()); + let mut state = self.state.lock().unwrap_or_else(|e| e.into_inner()); // First occurrence or changed error class: log as error once. if state.last_kind != Some(kind) { @@ -1023,7 +1020,10 @@ fn run_playback( if rx.is_empty() { let _ = stream.pause(); playing = false; - ring_writer.lock().unwrap_or_else(|e| e.into_inner()).clear(); + ring_writer + .lock() + .unwrap_or_else(|e| e.into_inner()) + .clear(); info!("Audio playback: paused (idle)"); if channel_closed { return Ok(()); @@ -1051,7 +1051,10 @@ fn run_playback( let _ = stream.pause(); playing = false; } - ring_writer.lock().unwrap_or_else(|e| e.into_inner()).clear(); + ring_writer + .lock() + .unwrap_or_else(|e| e.into_inner()) + .clear(); if channel_closed { return Ok(()); diff --git a/src/trx-server/src/rig_task.rs b/src/trx-server/src/rig_task.rs index b2c84d5..fea520b 100644 --- a/src/trx-server/src/rig_task.rs +++ b/src/trx-server/src/rig_task.rs @@ -21,7 +21,7 @@ use trx_core::rig::controller::{ }; use trx_core::rig::request::RigRequest; use trx_core::rig::state::{RigMode, RigSnapshot, RigState}; -use trx_core::rig::{RigCat, RigRxStatus, RigSdr, RigTxStatus}; +use trx_core::rig::{RigCat, RigRxStatus, RigTxStatus}; use trx_core::{DynResult, RigError, RigResult}; use crate::audio::DecoderHistories; @@ -564,7 +564,9 @@ async fn process_command( if let Err(e) = sdr.set_sdr_gain(gain_db).await { return Err(RigError::communication(format!("set_sdr_gain: {e}"))); } - } else { return Err(RigError::not_supported("set_sdr_gain")); } + } else { + return Err(RigError::not_supported("set_sdr_gain")); + } ctx.state.filter = ctx.rig.as_sdr_ref().and_then(|s| s.filter_state()); let _ = ctx.state_tx.send(ctx.state.clone()); return snapshot_from(ctx.state); @@ -574,7 +576,9 @@ async fn process_command( if let Err(e) = sdr.set_sdr_lna_gain(gain_db).await { return Err(RigError::communication(format!("set_sdr_lna_gain: {e}"))); } - } else { return Err(RigError::not_supported("set_sdr_lna_gain")); } + } else { + return Err(RigError::not_supported("set_sdr_lna_gain")); + } ctx.state.filter = ctx.rig.as_sdr_ref().and_then(|s| s.filter_state()); let _ = ctx.state_tx.send(ctx.state.clone()); return snapshot_from(ctx.state); @@ -584,7 +588,9 @@ async fn process_command( if let Err(e) = sdr.set_sdr_agc(enabled).await { return Err(RigError::communication(format!("set_sdr_agc: {e}"))); } - } else { return Err(RigError::not_supported("set_sdr_agc")); } + } else { + return Err(RigError::not_supported("set_sdr_agc")); + } ctx.state.filter = ctx.rig.as_sdr_ref().and_then(|s| s.filter_state()); let _ = ctx.state_tx.send(ctx.state.clone()); return snapshot_from(ctx.state); @@ -597,7 +603,9 @@ async fn process_command( if let Err(e) = sdr.set_sdr_squelch(enabled, threshold_db).await { return Err(RigError::communication(format!("set_sdr_squelch: {e}"))); } - } else { return Err(RigError::not_supported("set_sdr_squelch")); } + } else { + return Err(RigError::not_supported("set_sdr_squelch")); + } ctx.state.filter = ctx.rig.as_sdr_ref().and_then(|s| s.filter_state()); let _ = ctx.state_tx.send(ctx.state.clone()); return snapshot_from(ctx.state); @@ -609,7 +617,9 @@ async fn process_command( "set_sdr_noise_blanker: {e}" ))); } - } else { return Err(RigError::not_supported("set_sdr_noise_blanker")); } + } else { + return Err(RigError::not_supported("set_sdr_noise_blanker")); + } ctx.state.filter = ctx.rig.as_sdr_ref().and_then(|s| s.filter_state()); let _ = ctx.state_tx.send(ctx.state.clone()); return snapshot_from(ctx.state); @@ -619,7 +629,9 @@ async fn process_command( if let Err(e) = sdr.set_wfm_deemphasis(deemphasis_us).await { return Err(RigError::communication(format!("set_wfm_deemphasis: {e}"))); } - } else { return Err(RigError::not_supported("set_wfm_deemphasis")); } + } else { + return Err(RigError::not_supported("set_wfm_deemphasis")); + } if let Some(f) = ctx.state.filter.as_mut() { f.wfm_deemphasis_us = deemphasis_us; } @@ -631,7 +643,9 @@ async fn process_command( if let Err(e) = sdr.set_wfm_stereo(enabled).await { return Err(RigError::communication(format!("set_wfm_stereo: {e}"))); } - } else { return Err(RigError::not_supported("set_wfm_stereo")); } + } else { + return Err(RigError::not_supported("set_wfm_stereo")); + } if let Some(f) = ctx.state.filter.as_mut() { f.wfm_stereo = enabled; } @@ -643,7 +657,9 @@ async fn process_command( if let Err(e) = sdr.set_wfm_denoise(level).await { return Err(RigError::communication(format!("set_wfm_denoise: {e}"))); } - } else { return Err(RigError::not_supported("set_wfm_denoise")); } + } else { + return Err(RigError::not_supported("set_wfm_denoise")); + } if let Some(f) = ctx.state.filter.as_mut() { f.wfm_denoise = level; } @@ -655,7 +671,9 @@ async fn process_command( if let Err(e) = sdr.set_center_freq(freq).await { return Err(RigError::communication(format!("set_center_freq: {e}"))); } - } else { return Err(RigError::not_supported("set_center_freq")); } + } else { + return Err(RigError::not_supported("set_center_freq")); + } *ctx.poll_pause_until = Some(Instant::now() + Duration::from_millis(200)); return snapshot_from(ctx.state); }