feat(trx-client): add remote rig_id selection via config and CLI
This commit is contained in:
@@ -55,6 +55,8 @@ impl Default for GeneralConfig {
|
||||
pub struct RemoteConfig {
|
||||
/// Remote URL (host:port or tcp://host:port).
|
||||
pub url: Option<String>,
|
||||
/// Optional target rig ID on the remote multi-rig server.
|
||||
pub rig_id: Option<String>,
|
||||
/// Remote auth settings.
|
||||
pub auth: RemoteAuthConfig,
|
||||
/// Poll interval in milliseconds.
|
||||
@@ -65,6 +67,7 @@ impl Default for RemoteConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
url: None,
|
||||
rig_id: None,
|
||||
auth: RemoteAuthConfig::default(),
|
||||
poll_interval_ms: 750,
|
||||
}
|
||||
@@ -300,6 +303,11 @@ impl ClientConfig {
|
||||
return Err("[remote].url must not be empty when set".to_string());
|
||||
}
|
||||
}
|
||||
if let Some(rig_id) = &self.remote.rig_id {
|
||||
if rig_id.trim().is_empty() {
|
||||
return Err("[remote].rig_id must not be empty when set".to_string());
|
||||
}
|
||||
}
|
||||
if let Some(token) = &self.remote.auth.token {
|
||||
if token.trim().is_empty() {
|
||||
return Err("[remote.auth].token must not be empty when set".to_string());
|
||||
@@ -315,11 +323,13 @@ impl ClientConfig {
|
||||
if self.frontends.audio.enabled && self.frontends.audio.server_port == 0 {
|
||||
return Err("[frontends.audio].server_port must be > 0 when enabled".to_string());
|
||||
}
|
||||
if !self.frontends.audio.bridge.rx_gain.is_finite() || self.frontends.audio.bridge.rx_gain < 0.0
|
||||
if !self.frontends.audio.bridge.rx_gain.is_finite()
|
||||
|| self.frontends.audio.bridge.rx_gain < 0.0
|
||||
{
|
||||
return Err("[frontends.audio.bridge].rx_gain must be finite and >= 0".to_string());
|
||||
}
|
||||
if !self.frontends.audio.bridge.tx_gain.is_finite() || self.frontends.audio.bridge.tx_gain < 0.0
|
||||
if !self.frontends.audio.bridge.tx_gain.is_finite()
|
||||
|| self.frontends.audio.bridge.tx_gain < 0.0
|
||||
{
|
||||
return Err("[frontends.audio.bridge].tx_gain must be finite and >= 0".to_string());
|
||||
}
|
||||
@@ -353,6 +363,7 @@ impl ClientConfig {
|
||||
},
|
||||
remote: RemoteConfig {
|
||||
url: Some("192.168.1.100:9000".to_string()),
|
||||
rig_id: Some("hf".to_string()),
|
||||
auth: RemoteAuthConfig {
|
||||
token: Some("my-token".to_string()),
|
||||
},
|
||||
@@ -494,6 +505,7 @@ callsign = "W1AW"
|
||||
|
||||
[remote]
|
||||
url = "192.168.1.100:9000"
|
||||
rig_id = "hf"
|
||||
auth.token = "my-token"
|
||||
poll_interval_ms = 500
|
||||
|
||||
@@ -507,6 +519,7 @@ port = 8080
|
||||
let config: ClientConfig = toml::from_str(toml_str).unwrap();
|
||||
assert_eq!(config.general.callsign, Some("W1AW".to_string()));
|
||||
assert_eq!(config.remote.url, Some("192.168.1.100:9000".to_string()));
|
||||
assert_eq!(config.remote.rig_id, Some("hf".to_string()));
|
||||
assert_eq!(config.remote.auth.token, Some("my-token".to_string()));
|
||||
assert_eq!(config.remote.poll_interval_ms, 500);
|
||||
assert!(config.frontends.http.enabled);
|
||||
@@ -525,6 +538,13 @@ port = 8080
|
||||
assert!(config.validate().is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_validate_rejects_empty_remote_rig_id() {
|
||||
let mut config = ClientConfig::default();
|
||||
config.remote.rig_id = Some(" ".to_string());
|
||||
assert!(config.validate().is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_validate_rejects_empty_http_json_token() {
|
||||
let mut config = ClientConfig::default();
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
//
|
||||
// SPDX-License-Identifier: BSD-2-Clause
|
||||
|
||||
mod audio_client;
|
||||
mod audio_bridge;
|
||||
mod audio_client;
|
||||
mod config;
|
||||
mod remote_client;
|
||||
|
||||
@@ -58,6 +58,9 @@ struct Cli {
|
||||
/// Poll interval in milliseconds
|
||||
#[arg(long = "poll-interval")]
|
||||
poll_interval_ms: Option<u64>,
|
||||
/// Target rig ID on a multi-rig remote server
|
||||
#[arg(long = "rig-id")]
|
||||
rig_id: Option<String>,
|
||||
/// Frontend(s) to expose locally (e.g. http,rigctl)
|
||||
#[arg(short = 'f', long = "frontend", value_delimiter = ',', num_args = 1..)]
|
||||
frontends: Option<Vec<String>>,
|
||||
@@ -162,8 +165,10 @@ async fn async_init() -> DynResult<AppState> {
|
||||
// Set HTTP frontend authentication config
|
||||
frontend_runtime.http_auth_enabled = cfg.frontends.http.auth.enabled;
|
||||
frontend_runtime.http_auth_rx_passphrase = cfg.frontends.http.auth.rx_passphrase.clone();
|
||||
frontend_runtime.http_auth_control_passphrase = cfg.frontends.http.auth.control_passphrase.clone();
|
||||
frontend_runtime.http_auth_tx_access_control_enabled = cfg.frontends.http.auth.tx_access_control_enabled;
|
||||
frontend_runtime.http_auth_control_passphrase =
|
||||
cfg.frontends.http.auth.control_passphrase.clone();
|
||||
frontend_runtime.http_auth_tx_access_control_enabled =
|
||||
cfg.frontends.http.auth.tx_access_control_enabled;
|
||||
frontend_runtime.http_auth_session_ttl_secs = cfg.frontends.http.auth.session_ttl_min * 60;
|
||||
frontend_runtime.http_auth_cookie_secure = cfg.frontends.http.auth.cookie_secure;
|
||||
frontend_runtime.http_auth_cookie_same_site = match cfg.frontends.http.auth.cookie_same_site {
|
||||
@@ -183,6 +188,7 @@ async fn async_init() -> DynResult<AppState> {
|
||||
parse_remote_url(&remote_url).map_err(|e| format!("Invalid remote URL: {}", e))?;
|
||||
|
||||
let remote_token = cli.token.clone().or_else(|| cfg.remote.auth.token.clone());
|
||||
let remote_rig_id = cli.rig_id.clone().or_else(|| cfg.remote.rig_id.clone());
|
||||
|
||||
let poll_interval_ms = cli.poll_interval_ms.unwrap_or(cfg.remote.poll_interval_ms);
|
||||
|
||||
@@ -248,6 +254,7 @@ async fn async_init() -> DynResult<AppState> {
|
||||
let remote_cfg = RemoteClientConfig {
|
||||
addr: remote_endpoint.connect_addr(),
|
||||
token: remote_token,
|
||||
rig_id: remote_rig_id,
|
||||
poll_interval: Duration::from_millis(poll_interval_ms),
|
||||
};
|
||||
let remote_shutdown_rx = shutdown_rx.clone();
|
||||
|
||||
@@ -40,6 +40,7 @@ impl RemoteEndpoint {
|
||||
pub struct RemoteClientConfig {
|
||||
pub addr: String,
|
||||
pub token: Option<String>,
|
||||
pub rig_id: Option<String>,
|
||||
pub poll_interval: Duration,
|
||||
}
|
||||
|
||||
@@ -144,11 +145,7 @@ async fn send_command(
|
||||
cmd: ClientCommand,
|
||||
state_tx: &watch::Sender<RigState>,
|
||||
) -> RigResult<trx_core::RigSnapshot> {
|
||||
let envelope = ClientEnvelope {
|
||||
token: config.token.clone(),
|
||||
rig_id: None,
|
||||
cmd,
|
||||
};
|
||||
let envelope = build_envelope(config, cmd);
|
||||
|
||||
let payload = serde_json::to_string(&envelope)
|
||||
.map_err(|e| RigError::communication(format!("JSON serialize failed: {e}")))?;
|
||||
@@ -187,6 +184,14 @@ async fn send_command(
|
||||
))
|
||||
}
|
||||
|
||||
fn build_envelope(config: &RemoteClientConfig, cmd: ClientCommand) -> ClientEnvelope {
|
||||
ClientEnvelope {
|
||||
token: config.token.clone(),
|
||||
rig_id: config.rig_id.clone(),
|
||||
cmd,
|
||||
}
|
||||
}
|
||||
|
||||
async fn read_limited_line<R: AsyncBufRead + Unpin>(
|
||||
reader: &mut R,
|
||||
max_bytes: usize,
|
||||
@@ -476,6 +481,7 @@ mod tests {
|
||||
RemoteClientConfig {
|
||||
addr: addr.to_string(),
|
||||
token: None,
|
||||
rig_id: None,
|
||||
poll_interval: Duration::from_millis(100),
|
||||
},
|
||||
req_rx,
|
||||
@@ -503,4 +509,17 @@ mod tests {
|
||||
.expect("client shutdown timeout");
|
||||
let _ = server.await;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn build_envelope_includes_rig_id() {
|
||||
let config = RemoteClientConfig {
|
||||
addr: "127.0.0.1:4530".to_string(),
|
||||
token: Some("secret".to_string()),
|
||||
rig_id: Some("sdr".to_string()),
|
||||
poll_interval: Duration::from_millis(500),
|
||||
};
|
||||
let envelope = super::build_envelope(&config, trx_protocol::ClientCommand::GetState);
|
||||
assert_eq!(envelope.token.as_deref(), Some("secret"));
|
||||
assert_eq!(envelope.rig_id.as_deref(), Some("sdr"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,8 @@ callsign = "N0CALL"
|
||||
[remote]
|
||||
# Remote trx-server URL (host:port)
|
||||
url = "192.168.1.100:9000"
|
||||
# Optional target rig ID on a multi-rig server (omit to use server default rig)
|
||||
# rig_id = "hf"
|
||||
|
||||
# Poll interval in milliseconds
|
||||
poll_interval_ms = 750
|
||||
|
||||
Reference in New Issue
Block a user