[feat](trx-client): freeze only the disconnected rig's view in multi-rig mode
Track per-rig server connection state in `rig_server_connected` so that when one trx-server drops, only the rig(s) it serves are marked disconnected. Other rigs with active connections remain fully interactive. The SSE `server_connected` field is now resolved from the per-rig map for the session's active rig, falling back to the global flag for backward compatibility. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: Stan Grams <sjg@haxx.space>
This commit is contained in:
@@ -301,6 +301,10 @@ pub struct FrontendRuntimeContext {
|
||||
/// Whether the remote client currently has an active TCP connection to
|
||||
/// trx-server. Set to `true` on successful connect, `false` on drop.
|
||||
pub server_connected: Arc<AtomicBool>,
|
||||
/// Per-rig server connection state, keyed by short name (or rig_id in legacy mode).
|
||||
/// `true` while the rig's trx-server connection is active.
|
||||
/// Allows the UI to freeze only the rig that lost its connection.
|
||||
pub rig_server_connected: Arc<RwLock<HashMap<String, bool>>>,
|
||||
}
|
||||
|
||||
impl FrontendRuntimeContext {
|
||||
@@ -404,6 +408,7 @@ impl FrontendRuntimeContext {
|
||||
vchan_audio_cmd: Arc::new(Mutex::new(None)),
|
||||
vchan_destroyed: None,
|
||||
server_connected: Arc::new(AtomicBool::new(false)),
|
||||
rig_server_connected: Arc::new(RwLock::new(HashMap::new())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -187,7 +187,11 @@ pub async fn status_api(
|
||||
let json = serde_json::to_string(&state).map_err(actix_web::error::ErrorInternalServerError)?;
|
||||
let json = inject_frontend_meta(
|
||||
&json,
|
||||
frontend_meta_from_context(clients.load(Ordering::Relaxed), context.get_ref().as_ref()),
|
||||
frontend_meta_from_context(
|
||||
clients.load(Ordering::Relaxed),
|
||||
context.get_ref().as_ref(),
|
||||
None,
|
||||
),
|
||||
);
|
||||
Ok(HttpResponse::Ok()
|
||||
.insert_header((header::CONTENT_TYPE, "application/json"))
|
||||
@@ -214,7 +218,19 @@ fn inject_frontend_meta(json: &str, meta: FrontendMeta) -> String {
|
||||
fn frontend_meta_from_context(
|
||||
http_clients: usize,
|
||||
context: &FrontendRuntimeContext,
|
||||
rig_id: Option<&str>,
|
||||
) -> FrontendMeta {
|
||||
// Use per-rig connection state when available so that only the rig whose
|
||||
// server dropped appears disconnected, leaving other rigs unaffected.
|
||||
let server_connected = rig_id
|
||||
.and_then(|rid| {
|
||||
context
|
||||
.rig_server_connected
|
||||
.read()
|
||||
.ok()
|
||||
.and_then(|m| m.get(rid).copied())
|
||||
})
|
||||
.unwrap_or_else(|| context.server_connected.load(Ordering::Relaxed));
|
||||
FrontendMeta {
|
||||
http_clients,
|
||||
rigctl_clients: context.rigctl_clients.load(Ordering::Relaxed),
|
||||
@@ -231,7 +247,7 @@ fn frontend_meta_from_context(
|
||||
spectrum_coverage_margin_hz: spectrum_coverage_margin_hz_from_context(context),
|
||||
spectrum_usable_span_ratio: spectrum_usable_span_ratio_from_context(context),
|
||||
decode_history_retention_min: decode_history_retention_min_from_context(context),
|
||||
server_connected: context.server_connected.load(Ordering::Relaxed),
|
||||
server_connected,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -375,7 +391,7 @@ pub async fn events(
|
||||
serde_json::to_string(&initial).map_err(actix_web::error::ErrorInternalServerError)?;
|
||||
let initial_json = inject_frontend_meta(
|
||||
&initial_json,
|
||||
frontend_meta_from_context(count, context.get_ref().as_ref()),
|
||||
frontend_meta_from_context(count, context.get_ref().as_ref(), active_rig_id.as_deref()),
|
||||
);
|
||||
|
||||
let mut prefix: Vec<Result<Bytes, Error>> = Vec::new();
|
||||
@@ -418,18 +434,14 @@ pub async fn events(
|
||||
.ok()
|
||||
.and_then(|g| g.clone())
|
||||
});
|
||||
if let Some(rig_id) = rig_id_opt {
|
||||
vchan.update_primary(
|
||||
&rig_id,
|
||||
v.status.freq.hz,
|
||||
&format!("{:?}", v.status.mode),
|
||||
);
|
||||
if let Some(ref rig_id) = rig_id_opt {
|
||||
vchan.update_primary(rig_id, v.status.freq.hz, &format!("{:?}", v.status.mode));
|
||||
sync_scheduler_vchannels(
|
||||
vchan.as_ref(),
|
||||
bookmark_store_map.as_ref(),
|
||||
&scheduler_status,
|
||||
scheduler_control.as_ref(),
|
||||
&rig_id,
|
||||
rig_id,
|
||||
);
|
||||
}
|
||||
serde_json::to_string(&v).ok().map(|json| {
|
||||
@@ -438,6 +450,7 @@ pub async fn events(
|
||||
frontend_meta_from_context(
|
||||
counter.load(Ordering::Relaxed),
|
||||
context.as_ref(),
|
||||
rig_id_opt.as_deref(),
|
||||
),
|
||||
);
|
||||
Ok::<Bytes, Error>(Bytes::from(format!("data: {json}\n\n")))
|
||||
|
||||
Reference in New Issue
Block a user