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:
@@ -527,15 +527,17 @@ fn sync_scheduler_vchannels(
|
|||||||
.last_bookmark_ids
|
.last_bookmark_ids
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|bookmark_id| {
|
.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)
|
||||||
bookmark_id.clone(),
|
.map(|bookmark| {
|
||||||
bookmark.freq_hz,
|
(
|
||||||
bookmark.mode.clone(),
|
bookmark_id.clone(),
|
||||||
bookmark.bandwidth_hz.unwrap_or(0) as u32,
|
bookmark.freq_hz,
|
||||||
bookmark_decoder_kinds(&bookmark),
|
bookmark.mode.clone(),
|
||||||
)
|
bookmark.bandwidth_hz.unwrap_or(0) as u32,
|
||||||
})
|
bookmark_decoder_kinds(&bookmark),
|
||||||
|
)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
})
|
})
|
||||||
@@ -573,7 +575,10 @@ impl DecodeHistoryPayload {
|
|||||||
|
|
||||||
/// Build the grouped decode history payload from all per-decoder ring-buffers.
|
/// 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.
|
/// 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 {
|
DecodeHistoryPayload {
|
||||||
ais: crate::server::audio::snapshot_ais_history(context, rig_filter),
|
ais: crate::server::audio::snapshot_ais_history(context, rig_filter),
|
||||||
vdes: crate::server::audio::snapshot_vdes_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(),
|
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 {
|
let mut list: Vec<BookmarkWithScope> = match scope {
|
||||||
Some(remote) => {
|
Some(remote) => {
|
||||||
// Rig selected: merge general + rig-specific (rig wins on duplicate IDs).
|
// Rig selected: merge general + rig-specific (rig wins on duplicate IDs).
|
||||||
@@ -1507,20 +1515,36 @@ pub async fn list_bookmarks(
|
|||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|bm| {
|
.map(|bm| {
|
||||||
let id = bm.id.clone();
|
let id = bm.id.clone();
|
||||||
(id, BookmarkWithScope { bm, scope: "general".into() })
|
(
|
||||||
|
id,
|
||||||
|
BookmarkWithScope {
|
||||||
|
bm,
|
||||||
|
scope: "general".into(),
|
||||||
|
},
|
||||||
|
)
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
for bm in store_map.store_for(remote).list() {
|
for bm in store_map.store_for(remote).list() {
|
||||||
let id = bm.id.clone();
|
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()
|
map.into_values().collect()
|
||||||
}
|
}
|
||||||
None => {
|
None => store_map
|
||||||
store_map.general().list().into_iter()
|
.general()
|
||||||
.map(|bm| BookmarkWithScope { bm, scope: "general".into() })
|
.list()
|
||||||
.collect()
|
.into_iter()
|
||||||
}
|
.map(|bm| BookmarkWithScope {
|
||||||
|
bm,
|
||||||
|
scope: "general".into(),
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
};
|
};
|
||||||
if let Some(ref cat) = query.category {
|
if let Some(ref cat) = query.category {
|
||||||
if !cat.is_empty() {
|
if !cat.is_empty() {
|
||||||
|
|||||||
@@ -141,7 +141,10 @@ fn record_vdes(context: &FrontendRuntimeContext, mut msg: VdesMessage) {
|
|||||||
prune_vdes_history(context, &mut history);
|
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);
|
let cutoff = decode_history_cutoff(context);
|
||||||
while let Some((ts, _, _)) = history.front() {
|
while let Some((ts, _, _)) = history.front() {
|
||||||
if *ts >= cutoff {
|
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
|
let mut history = context
|
||||||
.aprs_history
|
.aprs_history
|
||||||
.lock()
|
.lock()
|
||||||
.expect("aprs history mutex poisoned");
|
.expect("aprs history mutex poisoned");
|
||||||
prune_aprs_history(context, &mut history);
|
prune_aprs_history(context, &mut history);
|
||||||
history.iter()
|
history
|
||||||
|
.iter()
|
||||||
.filter(|(_, rid, _)| matches_rig_filter(rid.as_deref(), rig_filter))
|
.filter(|(_, rid, _)| matches_rig_filter(rid.as_deref(), rig_filter))
|
||||||
.map(|(_, _, pkt)| pkt.clone())
|
.map(|(_, _, pkt)| pkt.clone())
|
||||||
.collect()
|
.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
|
let mut history = context
|
||||||
.hf_aprs_history
|
.hf_aprs_history
|
||||||
.lock()
|
.lock()
|
||||||
.expect("hf_aprs history mutex poisoned");
|
.expect("hf_aprs history mutex poisoned");
|
||||||
prune_hf_aprs_history(context, &mut history);
|
prune_hf_aprs_history(context, &mut history);
|
||||||
history.iter()
|
history
|
||||||
|
.iter()
|
||||||
.filter(|(_, rid, _)| matches_rig_filter(rid.as_deref(), rig_filter))
|
.filter(|(_, rid, _)| matches_rig_filter(rid.as_deref(), rig_filter))
|
||||||
.map(|(_, _, pkt)| pkt.clone())
|
.map(|(_, _, pkt)| pkt.clone())
|
||||||
.collect()
|
.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.
|
/// 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
|
/// The returned vec is sorted ascending by `ts_ms` so the client can replay
|
||||||
/// in chronological order.
|
/// 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
|
let mut history = context
|
||||||
.ais_history
|
.ais_history
|
||||||
.lock()
|
.lock()
|
||||||
@@ -331,73 +345,97 @@ pub fn snapshot_ais_history(context: &FrontendRuntimeContext, rig_filter: Option
|
|||||||
out
|
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
|
let mut history = context
|
||||||
.vdes_history
|
.vdes_history
|
||||||
.lock()
|
.lock()
|
||||||
.expect("vdes history mutex poisoned");
|
.expect("vdes history mutex poisoned");
|
||||||
prune_vdes_history(context, &mut history);
|
prune_vdes_history(context, &mut history);
|
||||||
history.iter()
|
history
|
||||||
|
.iter()
|
||||||
.filter(|(_, rid, _)| matches_rig_filter(rid.as_deref(), rig_filter))
|
.filter(|(_, rid, _)| matches_rig_filter(rid.as_deref(), rig_filter))
|
||||||
.map(|(_, _, msg)| msg.clone())
|
.map(|(_, _, msg)| msg.clone())
|
||||||
.collect()
|
.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
|
let mut history = context
|
||||||
.cw_history
|
.cw_history
|
||||||
.lock()
|
.lock()
|
||||||
.expect("cw history mutex poisoned");
|
.expect("cw history mutex poisoned");
|
||||||
prune_cw_history(context, &mut history);
|
prune_cw_history(context, &mut history);
|
||||||
history.iter()
|
history
|
||||||
|
.iter()
|
||||||
.filter(|(_, rid, _)| matches_rig_filter(rid.as_deref(), rig_filter))
|
.filter(|(_, rid, _)| matches_rig_filter(rid.as_deref(), rig_filter))
|
||||||
.map(|(_, _, evt)| evt.clone())
|
.map(|(_, _, evt)| evt.clone())
|
||||||
.collect()
|
.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
|
let mut history = context
|
||||||
.ft8_history
|
.ft8_history
|
||||||
.lock()
|
.lock()
|
||||||
.expect("ft8 history mutex poisoned");
|
.expect("ft8 history mutex poisoned");
|
||||||
prune_ft8_history(context, &mut history);
|
prune_ft8_history(context, &mut history);
|
||||||
history.iter()
|
history
|
||||||
|
.iter()
|
||||||
.filter(|(_, rid, _)| matches_rig_filter(rid.as_deref(), rig_filter))
|
.filter(|(_, rid, _)| matches_rig_filter(rid.as_deref(), rig_filter))
|
||||||
.map(|(_, _, msg)| msg.clone())
|
.map(|(_, _, msg)| msg.clone())
|
||||||
.collect()
|
.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
|
let mut history = context
|
||||||
.ft4_history
|
.ft4_history
|
||||||
.lock()
|
.lock()
|
||||||
.expect("ft4 history mutex poisoned");
|
.expect("ft4 history mutex poisoned");
|
||||||
prune_ft4_history(context, &mut history);
|
prune_ft4_history(context, &mut history);
|
||||||
history.iter()
|
history
|
||||||
|
.iter()
|
||||||
.filter(|(_, rid, _)| matches_rig_filter(rid.as_deref(), rig_filter))
|
.filter(|(_, rid, _)| matches_rig_filter(rid.as_deref(), rig_filter))
|
||||||
.map(|(_, _, msg)| msg.clone())
|
.map(|(_, _, msg)| msg.clone())
|
||||||
.collect()
|
.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
|
let mut history = context
|
||||||
.ft2_history
|
.ft2_history
|
||||||
.lock()
|
.lock()
|
||||||
.expect("ft2 history mutex poisoned");
|
.expect("ft2 history mutex poisoned");
|
||||||
prune_ft2_history(context, &mut history);
|
prune_ft2_history(context, &mut history);
|
||||||
history.iter()
|
history
|
||||||
|
.iter()
|
||||||
.filter(|(_, rid, _)| matches_rig_filter(rid.as_deref(), rig_filter))
|
.filter(|(_, rid, _)| matches_rig_filter(rid.as_deref(), rig_filter))
|
||||||
.map(|(_, _, msg)| msg.clone())
|
.map(|(_, _, msg)| msg.clone())
|
||||||
.collect()
|
.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
|
let mut history = context
|
||||||
.wspr_history
|
.wspr_history
|
||||||
.lock()
|
.lock()
|
||||||
.expect("wspr history mutex poisoned");
|
.expect("wspr history mutex poisoned");
|
||||||
prune_wspr_history(context, &mut history);
|
prune_wspr_history(context, &mut history);
|
||||||
history.iter()
|
history
|
||||||
|
.iter()
|
||||||
.filter(|(_, rid, _)| matches_rig_filter(rid.as_deref(), rig_filter))
|
.filter(|(_, rid, _)| matches_rig_filter(rid.as_deref(), rig_filter))
|
||||||
.map(|(_, _, msg)| msg.clone())
|
.map(|(_, _, msg)| msg.clone())
|
||||||
.collect()
|
.collect()
|
||||||
|
|||||||
@@ -202,10 +202,7 @@ impl SchedulerStoreMap {
|
|||||||
/// List configs from all known per-rig stores.
|
/// List configs from all known per-rig stores.
|
||||||
pub fn list_all(&self) -> Vec<SchedulerConfig> {
|
pub fn list_all(&self) -> Vec<SchedulerConfig> {
|
||||||
let stores = self.stores.lock().unwrap_or_else(|e| e.into_inner());
|
let stores = self.stores.lock().unwrap_or_else(|e| e.into_inner());
|
||||||
stores
|
stores.values().filter_map(|s| s.get_config()).collect()
|
||||||
.values()
|
|
||||||
.filter_map(|s| s.get_config())
|
|
||||||
.collect()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// One-time migration: extract `sch:{remote}` entries from legacy
|
/// One-time migration: extract `sch:{remote}` entries from legacy
|
||||||
@@ -887,13 +884,16 @@ pub async fn get_scheduler(
|
|||||||
store_map: web::Data<Arc<SchedulerStoreMap>>,
|
store_map: web::Data<Arc<SchedulerStoreMap>>,
|
||||||
) -> impl Responder {
|
) -> impl Responder {
|
||||||
let remote = path.into_inner();
|
let remote = path.into_inner();
|
||||||
let config = store_map.store_for(&remote).get_config().unwrap_or(SchedulerConfig {
|
let config = store_map
|
||||||
remote: remote.clone(),
|
.store_for(&remote)
|
||||||
mode: SchedulerMode::Disabled,
|
.get_config()
|
||||||
grayline: None,
|
.unwrap_or(SchedulerConfig {
|
||||||
entries: vec![],
|
remote: remote.clone(),
|
||||||
interleave_min: None,
|
mode: SchedulerMode::Disabled,
|
||||||
});
|
grayline: None,
|
||||||
|
entries: vec![],
|
||||||
|
interleave_min: None,
|
||||||
|
});
|
||||||
HttpResponse::Ok().json(config)
|
HttpResponse::Ok().json(config)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -160,7 +160,12 @@ impl ClientChannelManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Fall back to global sender.
|
// 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);
|
let _ = tx.try_send(cmd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -540,7 +545,11 @@ impl ClientChannelManager {
|
|||||||
|
|
||||||
/// Return the channel a session is currently subscribed to.
|
/// Return the channel a session is currently subscribed to.
|
||||||
pub fn session_channel(&self, session_id: Uuid) -> Option<(String, Uuid)> {
|
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.
|
/// Return the selected channel's tune metadata.
|
||||||
|
|||||||
@@ -217,7 +217,12 @@ fn check_server_sections(
|
|||||||
errors: &mut Vec<String>,
|
errors: &mut Vec<String>,
|
||||||
) {
|
) {
|
||||||
if let Some(general) = table.get("general").and_then(|v| v.as_table()) {
|
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_log_level(general, &format!("{}[general]", prefix), errors);
|
||||||
validate_coordinates(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()) {
|
if let Some(rig) = table.get("rig").and_then(|v| v.as_table()) {
|
||||||
check_unknown_keys(rig, RIG_KEYS, &format!("{}[rig].", prefix), warnings);
|
check_unknown_keys(rig, RIG_KEYS, &format!("{}[rig].", prefix), warnings);
|
||||||
if let Some(access) = rig.get("access").and_then(|v| v.as_table()) {
|
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);
|
validate_access(access, &format!("{}[rig.access]", prefix), errors);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(listen) = table.get("listen").and_then(|v| v.as_table()) {
|
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);
|
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()) {
|
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>,
|
errors: &mut Vec<String>,
|
||||||
) {
|
) {
|
||||||
if let Some(general) = table.get("general").and_then(|v| v.as_table()) {
|
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);
|
validate_log_level(general, &format!("{}[general]", prefix), errors);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(remote) = table.get("remote").and_then(|v| v.as_table()) {
|
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()) {
|
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()) {
|
if let Some(http) = frontends.get("http").and_then(|v| v.as_table()) {
|
||||||
validate_port(http, "port", &format!("{}[frontends.http]", prefix), errors);
|
validate_port(http, "port", &format!("{}[frontends.http]", prefix), errors);
|
||||||
}
|
}
|
||||||
if let Some(rigctl) = frontends.get("rigctl").and_then(|v| v.as_table()) {
|
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>) {
|
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) {
|
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) {
|
if !(-180.0..=180.0).contains(&lon) {
|
||||||
errors.push(format!(
|
errors.push(format!(
|
||||||
"{}.longitude {} is out of range (-180..180)",
|
"{}.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(
|
fn validate_port(table: &toml_edit::Table, key: &str, context: &str, errors: &mut Vec<String>) {
|
||||||
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(port) = table.get(key).and_then(|v| v.as_integer()) {
|
||||||
if let Some(enabled) = table.get("enabled").and_then(|v| v.as_bool()) {
|
if let Some(enabled) = table.get("enabled").and_then(|v| v.as_bool()) {
|
||||||
if enabled && port <= 0 {
|
if enabled && port <= 0 {
|
||||||
|
|||||||
@@ -12,7 +12,10 @@ use std::path::PathBuf;
|
|||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
|
|
||||||
#[derive(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 {
|
struct Cli {
|
||||||
/// Generate a default config without interactive prompts
|
/// Generate a default config without interactive prompts
|
||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
@@ -70,7 +73,10 @@ fn main() {
|
|||||||
"client" => ConfigType::Client,
|
"client" => ConfigType::Client,
|
||||||
"combined" => ConfigType::Combined,
|
"combined" => ConfigType::Combined,
|
||||||
other => {
|
other => {
|
||||||
eprintln!("Unknown config type '{}'. Use: server, client, combined", other);
|
eprintln!(
|
||||||
|
"Unknown config type '{}'. Use: server, client, combined",
|
||||||
|
other
|
||||||
|
);
|
||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,7 +51,11 @@ pub struct FrontendsSetup {
|
|||||||
// ── Prompt functions ────────────────────────────────────────────────────
|
// ── Prompt functions ────────────────────────────────────────────────────
|
||||||
|
|
||||||
pub fn prompt_config_type() -> ConfigType {
|
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()
|
let sel = Select::new()
|
||||||
.with_prompt("What configuration would you like to generate?")
|
.with_prompt("What configuration would you like to generate?")
|
||||||
.items(items)
|
.items(items)
|
||||||
|
|||||||
@@ -16,8 +16,7 @@ use crate::ConfigType;
|
|||||||
fn commented(table: &mut Table, key: &str, val: Item, comment: &str) {
|
fn commented(table: &mut Table, key: &str, val: Item, comment: &str) {
|
||||||
table.insert(key, val);
|
table.insert(key, val);
|
||||||
if let Some(mut kv) = table.key_mut(key) {
|
if let Some(mut kv) = table.key_mut(key) {
|
||||||
kv.leaf_decor_mut()
|
kv.leaf_decor_mut().set_prefix(format!("# {}\n", comment));
|
||||||
.set_prefix(format!("# {}\n", comment));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -27,24 +26,40 @@ fn header_comment(table: &mut Table, comment: &str) {
|
|||||||
|
|
||||||
// ── Server document builder ─────────────────────────────────────────────
|
// ── Server document builder ─────────────────────────────────────────────
|
||||||
|
|
||||||
fn build_server_tables(
|
fn build_server_tables(general: ServerGeneral, rig: RigSetup, listen: ListenSetup) -> Table {
|
||||||
general: ServerGeneral,
|
|
||||||
rig: RigSetup,
|
|
||||||
listen: ListenSetup,
|
|
||||||
) -> Table {
|
|
||||||
let mut root = Table::new();
|
let mut root = Table::new();
|
||||||
|
|
||||||
// [general]
|
// [general]
|
||||||
{
|
{
|
||||||
let mut t = Table::new();
|
let mut t = Table::new();
|
||||||
header_comment(&mut t, "General settings");
|
header_comment(&mut t, "General settings");
|
||||||
commented(&mut t, "callsign", value(&general.callsign), "Station callsign");
|
commented(
|
||||||
commented(&mut t, "log_level", value(&general.log_level), "Log level (trace, debug, info, warn, error)");
|
&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 {
|
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 {
|
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));
|
root.insert("general", Item::Table(t));
|
||||||
}
|
}
|
||||||
@@ -53,18 +68,38 @@ fn build_server_tables(
|
|||||||
{
|
{
|
||||||
let mut t = Table::new();
|
let mut t = Table::new();
|
||||||
header_comment(&mut t, "Rig backend configuration");
|
header_comment(&mut t, "Rig backend configuration");
|
||||||
commented(&mut t, "model", value(&rig.model), "Rig model (ft817, ft450d, soapysdr)");
|
commented(
|
||||||
commented(&mut t, "initial_freq_hz", value(144_300_000i64), "Initial frequency in Hz");
|
&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");
|
commented(&mut t, "initial_mode", value("USB"), "Initial mode");
|
||||||
|
|
||||||
// [rig.access]
|
// [rig.access]
|
||||||
let mut access = Table::new();
|
let mut access = Table::new();
|
||||||
header_comment(&mut access, "Rig access method");
|
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() {
|
match rig.access_type.as_str() {
|
||||||
"serial" => {
|
"serial" => {
|
||||||
if let Some(port) = &rig.serial_port {
|
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 {
|
if let Some(baud) = rig.serial_baud {
|
||||||
commented(&mut access, "baud", value(baud as i64), "Baud rate");
|
commented(&mut access, "baud", value(baud as i64), "Baud rate");
|
||||||
@@ -80,7 +115,12 @@ fn build_server_tables(
|
|||||||
}
|
}
|
||||||
"sdr" => {
|
"sdr" => {
|
||||||
if let Some(args) = &rig.sdr_args {
|
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();
|
let mut t = Table::new();
|
||||||
header_comment(&mut t, "Polling and retry behavior");
|
header_comment(&mut t, "Polling and retry behavior");
|
||||||
commented(&mut t, "poll_interval_ms", value(500i64), "Rig polling interval (ms)");
|
commented(
|
||||||
commented(&mut t, "poll_interval_tx_ms", value(100i64), "Polling interval during TX (ms)");
|
&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, "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));
|
root.insert("behavior", Item::Table(t));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,8 +160,18 @@ fn build_server_tables(
|
|||||||
let mut t = Table::new();
|
let mut t = Table::new();
|
||||||
header_comment(&mut t, "JSON TCP listener for client connections");
|
header_comment(&mut t, "JSON TCP listener for client connections");
|
||||||
commented(&mut t, "enabled", value(true), "Enable the TCP listener");
|
commented(&mut t, "enabled", value(true), "Enable the TCP listener");
|
||||||
commented(&mut t, "listen", value(&listen.listen), "IP address to listen on");
|
commented(
|
||||||
commented(&mut t, "port", value(listen.port as i64), "TCP port for client connections");
|
&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));
|
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, "listen", value("127.0.0.1"), "Audio listen address");
|
||||||
commented(&mut t, "port", value(4531i64), "Audio TCP port");
|
commented(&mut t, "port", value(4531i64), "Audio TCP port");
|
||||||
commented(&mut t, "sample_rate", value(48000i64), "Sample rate in Hz");
|
commented(&mut t, "sample_rate", value(48000i64), "Sample rate in Hz");
|
||||||
commented(&mut t, "channels", value(2i64), "Channel count (1 = mono, 2 = stereo)");
|
commented(
|
||||||
commented(&mut t, "frame_duration_ms", value(20i64), "Opus frame duration (ms)");
|
&mut t,
|
||||||
commented(&mut t, "bitrate_bps", value(256000i64), "Opus bitrate (bps)");
|
"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.insert("audio", Item::Table(t));
|
||||||
}
|
}
|
||||||
|
|
||||||
root
|
root
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build_server(
|
pub fn build_server(general: ServerGeneral, rig: RigSetup, listen: ListenSetup) -> DocumentMut {
|
||||||
general: ServerGeneral,
|
|
||||||
rig: RigSetup,
|
|
||||||
listen: ListenSetup,
|
|
||||||
) -> DocumentMut {
|
|
||||||
let mut doc = DocumentMut::new();
|
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);
|
let tables = build_server_tables(general, rig, listen);
|
||||||
for (key, item) in tables.iter() {
|
for (key, item) in tables.iter() {
|
||||||
doc.insert(key, item.clone());
|
doc.insert(key, item.clone());
|
||||||
@@ -154,8 +231,18 @@ fn build_client_tables(
|
|||||||
{
|
{
|
||||||
let mut t = Table::new();
|
let mut t = Table::new();
|
||||||
header_comment(&mut t, "General settings");
|
header_comment(&mut t, "General settings");
|
||||||
commented(&mut t, "callsign", value(&general.callsign), "Station callsign");
|
commented(
|
||||||
commented(&mut t, "log_level", value(&general.log_level), "Log level (trace, debug, info, warn, error)");
|
&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));
|
root.insert("general", Item::Table(t));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -163,8 +250,18 @@ fn build_client_tables(
|
|||||||
{
|
{
|
||||||
let mut t = Table::new();
|
let mut t = Table::new();
|
||||||
header_comment(&mut t, "Remote server connection");
|
header_comment(&mut t, "Remote server connection");
|
||||||
commented(&mut t, "url", value(&remote.url), "Server address (host:port)");
|
commented(
|
||||||
commented(&mut t, "poll_interval_ms", value(750i64), "State poll interval (ms)");
|
&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();
|
let mut auth = Table::new();
|
||||||
if let Some(token) = &remote.token {
|
if let Some(token) = &remote.token {
|
||||||
@@ -182,26 +279,61 @@ fn build_client_tables(
|
|||||||
header_comment(&mut frontends_table, "Frontend configuration");
|
header_comment(&mut frontends_table, "Frontend configuration");
|
||||||
|
|
||||||
let mut http = Table::new();
|
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, "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));
|
frontends_table.insert("http", Item::Table(http));
|
||||||
|
|
||||||
let mut rigctl = Table::new();
|
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, "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));
|
frontends_table.insert("rigctl", Item::Table(rigctl));
|
||||||
|
|
||||||
let mut http_json = Table::new();
|
let mut http_json = Table::new();
|
||||||
commented(&mut http_json, "enabled", value(true), "Enable JSON-over-TCP frontend");
|
commented(
|
||||||
commented(&mut http_json, "listen", value("127.0.0.1"), "Listen address");
|
&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)");
|
commented(&mut http_json, "port", value(0i64), "Port (0 = ephemeral)");
|
||||||
frontends_table.insert("http_json", Item::Table(http_json));
|
frontends_table.insert("http_json", Item::Table(http_json));
|
||||||
|
|
||||||
let mut audio = Table::new();
|
let mut audio = Table::new();
|
||||||
commented(&mut audio, "enabled", value(true), "Enable audio client");
|
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));
|
frontends_table.insert("audio", Item::Table(audio));
|
||||||
|
|
||||||
root.insert("frontends", Item::Table(frontends_table));
|
root.insert("frontends", Item::Table(frontends_table));
|
||||||
@@ -216,7 +348,8 @@ pub fn build_client(
|
|||||||
frontends: FrontendsSetup,
|
frontends: FrontendsSetup,
|
||||||
) -> DocumentMut {
|
) -> DocumentMut {
|
||||||
let mut doc = DocumentMut::new();
|
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);
|
let tables = build_client_tables(general, remote, frontends);
|
||||||
for (key, item) in tables.iter() {
|
for (key, item) in tables.iter() {
|
||||||
doc.insert(key, item.clone());
|
doc.insert(key, item.clone());
|
||||||
@@ -235,7 +368,8 @@ pub fn build_combined(
|
|||||||
frontends: FrontendsSetup,
|
frontends: FrontendsSetup,
|
||||||
) -> DocumentMut {
|
) -> DocumentMut {
|
||||||
let mut doc = DocumentMut::new();
|
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 server = build_server_tables(s_general, rig, listen);
|
||||||
let mut server_item = Item::Table(server);
|
let mut server_item = Item::Table(server);
|
||||||
@@ -294,7 +428,9 @@ pub fn build_default(config_type: ConfigType) -> DocumentMut {
|
|||||||
match config_type {
|
match config_type {
|
||||||
ConfigType::Server => build_server(s_general, rig, listen),
|
ConfigType::Server => build_server(s_general, rig, listen),
|
||||||
ConfigType::Client => build_client(c_general, remote, frontends),
|
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());
|
println!("Wrote {}", path.display());
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -138,10 +138,7 @@ impl StreamErrorLogger {
|
|||||||
fn log(&self, err: &str) {
|
fn log(&self, err: &str) {
|
||||||
let now = Instant::now();
|
let now = Instant::now();
|
||||||
let kind = classify_stream_error(err);
|
let kind = classify_stream_error(err);
|
||||||
let mut state = self
|
let mut state = self.state.lock().unwrap_or_else(|e| e.into_inner());
|
||||||
.state
|
|
||||||
.lock()
|
|
||||||
.unwrap_or_else(|e| e.into_inner());
|
|
||||||
|
|
||||||
// First occurrence or changed error class: log as error once.
|
// First occurrence or changed error class: log as error once.
|
||||||
if state.last_kind != Some(kind) {
|
if state.last_kind != Some(kind) {
|
||||||
@@ -1023,7 +1020,10 @@ fn run_playback(
|
|||||||
if rx.is_empty() {
|
if rx.is_empty() {
|
||||||
let _ = stream.pause();
|
let _ = stream.pause();
|
||||||
playing = false;
|
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)");
|
info!("Audio playback: paused (idle)");
|
||||||
if channel_closed {
|
if channel_closed {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
@@ -1051,7 +1051,10 @@ fn run_playback(
|
|||||||
let _ = stream.pause();
|
let _ = stream.pause();
|
||||||
playing = false;
|
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 {
|
if channel_closed {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ use trx_core::rig::controller::{
|
|||||||
};
|
};
|
||||||
use trx_core::rig::request::RigRequest;
|
use trx_core::rig::request::RigRequest;
|
||||||
use trx_core::rig::state::{RigMode, RigSnapshot, RigState};
|
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 trx_core::{DynResult, RigError, RigResult};
|
||||||
|
|
||||||
use crate::audio::DecoderHistories;
|
use crate::audio::DecoderHistories;
|
||||||
@@ -564,7 +564,9 @@ async fn process_command(
|
|||||||
if let Err(e) = sdr.set_sdr_gain(gain_db).await {
|
if let Err(e) = sdr.set_sdr_gain(gain_db).await {
|
||||||
return Err(RigError::communication(format!("set_sdr_gain: {e}")));
|
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());
|
ctx.state.filter = ctx.rig.as_sdr_ref().and_then(|s| s.filter_state());
|
||||||
let _ = ctx.state_tx.send(ctx.state.clone());
|
let _ = ctx.state_tx.send(ctx.state.clone());
|
||||||
return snapshot_from(ctx.state);
|
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 {
|
if let Err(e) = sdr.set_sdr_lna_gain(gain_db).await {
|
||||||
return Err(RigError::communication(format!("set_sdr_lna_gain: {e}")));
|
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());
|
ctx.state.filter = ctx.rig.as_sdr_ref().and_then(|s| s.filter_state());
|
||||||
let _ = ctx.state_tx.send(ctx.state.clone());
|
let _ = ctx.state_tx.send(ctx.state.clone());
|
||||||
return snapshot_from(ctx.state);
|
return snapshot_from(ctx.state);
|
||||||
@@ -584,7 +588,9 @@ async fn process_command(
|
|||||||
if let Err(e) = sdr.set_sdr_agc(enabled).await {
|
if let Err(e) = sdr.set_sdr_agc(enabled).await {
|
||||||
return Err(RigError::communication(format!("set_sdr_agc: {e}")));
|
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());
|
ctx.state.filter = ctx.rig.as_sdr_ref().and_then(|s| s.filter_state());
|
||||||
let _ = ctx.state_tx.send(ctx.state.clone());
|
let _ = ctx.state_tx.send(ctx.state.clone());
|
||||||
return snapshot_from(ctx.state);
|
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 {
|
if let Err(e) = sdr.set_sdr_squelch(enabled, threshold_db).await {
|
||||||
return Err(RigError::communication(format!("set_sdr_squelch: {e}")));
|
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());
|
ctx.state.filter = ctx.rig.as_sdr_ref().and_then(|s| s.filter_state());
|
||||||
let _ = ctx.state_tx.send(ctx.state.clone());
|
let _ = ctx.state_tx.send(ctx.state.clone());
|
||||||
return snapshot_from(ctx.state);
|
return snapshot_from(ctx.state);
|
||||||
@@ -609,7 +617,9 @@ async fn process_command(
|
|||||||
"set_sdr_noise_blanker: {e}"
|
"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());
|
ctx.state.filter = ctx.rig.as_sdr_ref().and_then(|s| s.filter_state());
|
||||||
let _ = ctx.state_tx.send(ctx.state.clone());
|
let _ = ctx.state_tx.send(ctx.state.clone());
|
||||||
return snapshot_from(ctx.state);
|
return snapshot_from(ctx.state);
|
||||||
@@ -619,7 +629,9 @@ async fn process_command(
|
|||||||
if let Err(e) = sdr.set_wfm_deemphasis(deemphasis_us).await {
|
if let Err(e) = sdr.set_wfm_deemphasis(deemphasis_us).await {
|
||||||
return Err(RigError::communication(format!("set_wfm_deemphasis: {e}")));
|
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() {
|
if let Some(f) = ctx.state.filter.as_mut() {
|
||||||
f.wfm_deemphasis_us = deemphasis_us;
|
f.wfm_deemphasis_us = deemphasis_us;
|
||||||
}
|
}
|
||||||
@@ -631,7 +643,9 @@ async fn process_command(
|
|||||||
if let Err(e) = sdr.set_wfm_stereo(enabled).await {
|
if let Err(e) = sdr.set_wfm_stereo(enabled).await {
|
||||||
return Err(RigError::communication(format!("set_wfm_stereo: {e}")));
|
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() {
|
if let Some(f) = ctx.state.filter.as_mut() {
|
||||||
f.wfm_stereo = enabled;
|
f.wfm_stereo = enabled;
|
||||||
}
|
}
|
||||||
@@ -643,7 +657,9 @@ async fn process_command(
|
|||||||
if let Err(e) = sdr.set_wfm_denoise(level).await {
|
if let Err(e) = sdr.set_wfm_denoise(level).await {
|
||||||
return Err(RigError::communication(format!("set_wfm_denoise: {e}")));
|
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() {
|
if let Some(f) = ctx.state.filter.as_mut() {
|
||||||
f.wfm_denoise = level;
|
f.wfm_denoise = level;
|
||||||
}
|
}
|
||||||
@@ -655,7 +671,9 @@ async fn process_command(
|
|||||||
if let Err(e) = sdr.set_center_freq(freq).await {
|
if let Err(e) = sdr.set_center_freq(freq).await {
|
||||||
return Err(RigError::communication(format!("set_center_freq: {e}")));
|
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));
|
*ctx.poll_pause_until = Some(Instant::now() + Duration::from_millis(200));
|
||||||
return snapshot_from(ctx.state);
|
return snapshot_from(ctx.state);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user