fix(trx-server): remove unused RigSdr import in rig_task.rs

Also run cargo fmt to fix formatting issues across trx-server,
trx-frontend-http, and trx-configurator.

https://claude.ai/code/session_01RsHUyVz2wjQjsEsxJo5owt
Signed-off-by: Claude <noreply@anthropic.com>
This commit is contained in:
Claude
2026-03-26 12:59:16 +00:00
committed by Stan Grams
parent c8f33b8939
commit a63f27971d
10 changed files with 413 additions and 130 deletions
@@ -527,7 +527,9 @@ 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_store_map
.get_for_rig(rig_id, bookmark_id)
.map(|bookmark| {
(
bookmark_id.clone(),
bookmark.freq_hz,
@@ -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<BookmarkWithScope> = 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() {
@@ -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<String>, CwEvent)>) {
fn prune_cw_history(
context: &FrontendRuntimeContext,
history: &mut VecDeque<(Instant, Option<String>, 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<AprsPacket> {
pub fn snapshot_aprs_history(
context: &FrontendRuntimeContext,
rig_filter: Option<&str>,
) -> Vec<AprsPacket> {
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<AprsPacket> {
pub fn snapshot_hf_aprs_history(
context: &FrontendRuntimeContext,
rig_filter: Option<&str>,
) -> Vec<AprsPacket> {
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<AisMessage> {
pub fn snapshot_ais_history(
context: &FrontendRuntimeContext,
rig_filter: Option<&str>,
) -> Vec<AisMessage> {
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<VdesMessage> {
pub fn snapshot_vdes_history(
context: &FrontendRuntimeContext,
rig_filter: Option<&str>,
) -> Vec<VdesMessage> {
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<CwEvent> {
pub fn snapshot_cw_history(
context: &FrontendRuntimeContext,
rig_filter: Option<&str>,
) -> Vec<CwEvent> {
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<Ft8Message> {
pub fn snapshot_ft8_history(
context: &FrontendRuntimeContext,
rig_filter: Option<&str>,
) -> Vec<Ft8Message> {
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<Ft8Message> {
pub fn snapshot_ft4_history(
context: &FrontendRuntimeContext,
rig_filter: Option<&str>,
) -> Vec<Ft8Message> {
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<Ft8Message> {
pub fn snapshot_ft2_history(
context: &FrontendRuntimeContext,
rig_filter: Option<&str>,
) -> Vec<Ft8Message> {
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<WsprMessage> {
pub fn snapshot_wspr_history(
context: &FrontendRuntimeContext,
rig_filter: Option<&str>,
) -> Vec<WsprMessage> {
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()
@@ -202,10 +202,7 @@ impl SchedulerStoreMap {
/// List configs from all known per-rig stores.
pub fn list_all(&self) -> Vec<SchedulerConfig> {
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,7 +884,10 @@ pub async fn get_scheduler(
store_map: web::Data<Arc<SchedulerStoreMap>>,
) -> impl Responder {
let remote = path.into_inner();
let config = store_map.store_for(&remote).get_config().unwrap_or(SchedulerConfig {
let config = store_map
.store_for(&remote)
.get_config()
.unwrap_or(SchedulerConfig {
remote: remote.clone(),
mode: SchedulerMode::Disabled,
grayline: None,
@@ -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.
+61 -17
View File
@@ -217,7 +217,12 @@ fn check_server_sections(
errors: &mut Vec<String>,
) {
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<String>,
) {
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<String>) {
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<String>,
) {
fn validate_port(table: &toml_edit::Table, key: &str, context: &str, errors: &mut Vec<String>) {
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 {
+8 -2
View File
@@ -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);
}
}
+5 -1
View File
@@ -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)
+182 -45
View File
@@ -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(())
}
+9 -6
View File
@@ -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(());
+28 -10
View File
@@ -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);
}