[fix](trx-client): preserve ft8 history on reload
Co-authored-by: OpenAI Codex <codex@openai.com> Signed-off-by: Stan Grams <sjg@haxx.space>
This commit is contained in:
+32
-21
@@ -286,6 +286,8 @@ async fn async_init() -> DynResult<AppState> {
|
||||
}));
|
||||
|
||||
// Audio streaming setup
|
||||
let mut pending_audio_client = None;
|
||||
let mut pending_audio_bridge = None;
|
||||
if cfg.frontends.audio.enabled {
|
||||
let (rx_audio_tx, _) = broadcast::channel::<Bytes>(256);
|
||||
let (tx_audio_tx, tx_audio_rx) = mpsc::channel::<Bytes>(64);
|
||||
@@ -304,7 +306,7 @@ async fn async_init() -> DynResult<AppState> {
|
||||
|
||||
let audio_rig_ports: HashMap<String, u16> = cfg.frontends.audio.rig_ports.clone();
|
||||
let audio_shutdown_rx = shutdown_rx.clone();
|
||||
task_handles.push(tokio::spawn(audio_client::run_audio_client(
|
||||
pending_audio_client = Some(tokio::spawn(audio_client::run_audio_client(
|
||||
remote_host,
|
||||
cfg.frontends.audio.server_port,
|
||||
audio_rig_ports,
|
||||
@@ -318,26 +320,7 @@ async fn async_init() -> DynResult<AppState> {
|
||||
)));
|
||||
|
||||
if cfg.frontends.audio.bridge.enabled {
|
||||
info!("Audio bridge enabled (local virtual-device integration)");
|
||||
task_handles.push(audio_bridge::spawn_audio_bridge(
|
||||
cfg.frontends.audio.bridge.clone(),
|
||||
frontend_runtime
|
||||
.audio_rx
|
||||
.as_ref()
|
||||
.expect("audio rx must be set")
|
||||
.clone(),
|
||||
frontend_runtime
|
||||
.audio_tx
|
||||
.as_ref()
|
||||
.expect("audio tx must be set")
|
||||
.clone(),
|
||||
frontend_runtime
|
||||
.audio_info
|
||||
.as_ref()
|
||||
.expect("audio info must be set")
|
||||
.clone(),
|
||||
shutdown_rx.clone(),
|
||||
));
|
||||
pending_audio_bridge = Some(cfg.frontends.audio.bridge.clone());
|
||||
}
|
||||
} else {
|
||||
info!("Audio disabled in config, decode will not be available");
|
||||
@@ -404,6 +387,34 @@ async fn async_init() -> DynResult<AppState> {
|
||||
)?;
|
||||
}
|
||||
|
||||
// Start the audio connection only after frontends are running so decode
|
||||
// subscribers can capture the server's initial history replay.
|
||||
if let Some(handle) = pending_audio_client {
|
||||
task_handles.push(handle);
|
||||
}
|
||||
if let Some(bridge_cfg) = pending_audio_bridge {
|
||||
info!("Audio bridge enabled (local virtual-device integration)");
|
||||
task_handles.push(audio_bridge::spawn_audio_bridge(
|
||||
bridge_cfg,
|
||||
frontend_runtime_ctx
|
||||
.audio_rx
|
||||
.as_ref()
|
||||
.expect("audio rx must be set")
|
||||
.clone(),
|
||||
frontend_runtime_ctx
|
||||
.audio_tx
|
||||
.as_ref()
|
||||
.expect("audio tx must be set")
|
||||
.clone(),
|
||||
frontend_runtime_ctx
|
||||
.audio_info
|
||||
.as_ref()
|
||||
.expect("audio info must be set")
|
||||
.clone(),
|
||||
shutdown_rx.clone(),
|
||||
));
|
||||
}
|
||||
|
||||
Ok(AppState {
|
||||
shutdown_tx,
|
||||
task_handles,
|
||||
|
||||
@@ -3877,8 +3877,15 @@ function initAprsMap() {
|
||||
function sizeAprsMapToViewport() {
|
||||
const mapEl = document.getElementById("aprs-map");
|
||||
if (!mapEl) return;
|
||||
const mapRect = mapEl.getBoundingClientRect();
|
||||
const stage = mapStageEl();
|
||||
if (mapIsFullscreen() && stage) {
|
||||
const stageHeight = stage.clientHeight || stage.getBoundingClientRect().height;
|
||||
const target = Math.max(260, Math.floor(stageHeight));
|
||||
mapEl.style.height = `${target}px`;
|
||||
if (aprsMap) aprsMap.invalidateSize();
|
||||
return;
|
||||
}
|
||||
const mapRect = mapEl.getBoundingClientRect();
|
||||
const width = mapEl.clientWidth || mapRect.width;
|
||||
const footer = document.querySelector(".footer");
|
||||
let bottom = mapIsFullscreen() && stage
|
||||
|
||||
@@ -10,6 +10,16 @@ const FT8_PERIOD_SECONDS = 15;
|
||||
let ft8FilterText = "";
|
||||
let ft8MessageHistory = [];
|
||||
|
||||
function normalizeFt8DisplayFreqHz(freqHz) {
|
||||
const rawHz = Number(freqHz);
|
||||
if (!Number.isFinite(rawHz)) return null;
|
||||
const baseHz = Number.isFinite(window.ft8BaseHz) ? Number(window.ft8BaseHz) : null;
|
||||
if (Number.isFinite(baseHz) && baseHz > 0 && rawHz >= 0 && rawHz < 100000) {
|
||||
return baseHz + rawHz;
|
||||
}
|
||||
return rawHz;
|
||||
}
|
||||
|
||||
function fmtTime(tsMs) {
|
||||
if (!tsMs) return "--:--:--";
|
||||
return new Date(tsMs).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit", second: "2-digit" });
|
||||
@@ -33,7 +43,8 @@ function renderFt8Row(msg) {
|
||||
row.dataset.storedFreqHz = Number.isFinite(msg.freq_hz) ? String(msg.freq_hz) : "";
|
||||
const snr = Number.isFinite(msg.snr_db) ? msg.snr_db.toFixed(1) : "--";
|
||||
const dt = Number.isFinite(msg.dt_s) ? msg.dt_s.toFixed(2) : "--";
|
||||
const freq = Number.isFinite(msg.freq_hz) ? msg.freq_hz.toFixed(0) : "--";
|
||||
const displayFreqHz = normalizeFt8DisplayFreqHz(msg.freq_hz);
|
||||
const freq = Number.isFinite(displayFreqHz) ? displayFreqHz.toFixed(0) : "--";
|
||||
const renderedMessage = renderFt8Message(rawMessage);
|
||||
row.innerHTML = `<span class="ft8-time">${fmtTime(msg.ts_ms)}</span><span class="ft8-snr">${snr}</span><span class="ft8-dt">${dt}</span><span class="ft8-freq">${freq}</span><span class="ft8-msg">${renderedMessage}</span>`;
|
||||
applyFt8FilterToRow(row);
|
||||
@@ -51,8 +62,9 @@ function addFt8Message(msg) {
|
||||
}
|
||||
|
||||
function ft8BarRfText(msg) {
|
||||
if (!Number.isFinite(msg.freq_hz)) return null;
|
||||
return `${msg.freq_hz.toFixed(0)} Hz`;
|
||||
const displayFreqHz = normalizeFt8DisplayFreqHz(msg.freq_hz);
|
||||
if (!Number.isFinite(displayFreqHz)) return null;
|
||||
return `${displayFreqHz.toFixed(0)} Hz`;
|
||||
}
|
||||
|
||||
function updateFt8Bar() {
|
||||
@@ -173,8 +185,9 @@ function updateFt8RowRf(row) {
|
||||
const freqEl = row.querySelector(".ft8-freq");
|
||||
if (!freqEl) return;
|
||||
const storedFreqHz = row.dataset.storedFreqHz ? Number(row.dataset.storedFreqHz) : NaN;
|
||||
if (Number.isFinite(storedFreqHz)) {
|
||||
freqEl.textContent = storedFreqHz.toFixed(0);
|
||||
const displayFreqHz = normalizeFt8DisplayFreqHz(storedFreqHz);
|
||||
if (Number.isFinite(displayFreqHz)) {
|
||||
freqEl.textContent = displayFreqHz.toFixed(0);
|
||||
} else {
|
||||
freqEl.textContent = "--";
|
||||
}
|
||||
|
||||
@@ -1132,12 +1132,14 @@ small { color: var(--text-muted); }
|
||||
#map-stage:fullscreen,
|
||||
#map-stage:-webkit-full-screen {
|
||||
background: var(--bg);
|
||||
padding: 0.75rem;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
#map-stage:fullscreen #aprs-map,
|
||||
#map-stage:-webkit-full-screen #aprs-map {
|
||||
border-radius: 8px;
|
||||
border-radius: 0;
|
||||
}
|
||||
.aprs-controls { display: flex; gap: 0.6rem; align-items: center; margin-bottom: 0.75rem; }
|
||||
.aprs-summary {
|
||||
|
||||
Reference in New Issue
Block a user