diff --git a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/app.js b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/app.js
index 4487538..4d3708d 100644
--- a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/app.js
+++ b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/app.js
@@ -750,6 +750,7 @@ function applyRigList(activeRigId, rigIds, displayNames) {
populateRigPicker(headerRigSwitchSelect, lastRigIds, activeRigId, disableSwitch);
updateRigSubtitle(activeRigId);
if (typeof reloadSchedulerRigSelect === "function") reloadSchedulerRigSelect();
+ if (typeof reloadBackgroundDecodeRigSelect === "function") reloadBackgroundDecodeRigSelect();
}
async function refreshRigList() {
diff --git a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/index.html b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/index.html
index df33473..c0b541d 100644
--- a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/index.html
+++ b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/index.html
@@ -804,7 +804,7 @@
-
+
diff --git a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/plugins/background-decode.js b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/plugins/background-decode.js
index 35dc331..752933c 100644
--- a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/plugins/background-decode.js
+++ b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/plugins/background-decode.js
@@ -26,6 +26,7 @@
if (!sel) return;
const rigs = typeof getAvailableRigIds === "function" ? getAvailableRigIds() : [];
if (!rigs.length) return;
+ const prevRigId = currentRigId;
sel.innerHTML = "";
rigs.forEach(function (rigId) {
const opt = document.createElement("option");
@@ -37,6 +38,11 @@
if (!currentRigId || !rigs.includes(currentRigId)) {
currentRigId = rigs[0];
sel.value = currentRigId;
+ } else {
+ sel.value = currentRigId;
+ }
+ if (currentRigId && currentRigId !== prevRigId) {
+ loadBackgroundDecode();
}
}
@@ -158,14 +164,13 @@
ids.forEach(function (id) {
const bookmark = bookmarkList.find(function (item) { return item.id === id; });
const chip = document.createElement("div");
- chip.className = "bgd-bookmark-chip";
+ chip.className = "sch-extra-bm-tag bgd-bookmark-tag";
const decoders = bookmarkDecoderKinds(bookmark);
chip.innerHTML =
'' + escHtml(bookmark ? bookmark.name : id) + '' +
- '' + escHtml(bookmark ? (formatFreq(bookmark.freq_hz) + " " + bookmark.mode + " · " + decoders.join("/").toUpperCase()) : "Missing bookmark") + '';
- const btn = document.createElement("button");
- btn.type = "button";
- btn.className = "bgd-bookmark-chip-remove sch-write";
+ '' + escHtml(bookmark ? (formatFreq(bookmark.freq_hz) + " " + bookmark.mode + " · " + decoders.join("/").toUpperCase()) : "Missing bookmark") + '';
+ const btn = document.createElement("span");
+ btn.className = "sch-extra-bm-rm";
btn.textContent = "×";
btn.addEventListener("click", function () {
removeBookmark(id);
@@ -293,9 +298,11 @@
case "active": return "Active";
case "out_of_span": return "Out of span";
case "waiting_for_spectrum": return "Waiting";
+ case "waiting_for_user": return "No user";
case "missing_bookmark": return "Missing";
case "no_supported_decoders": return "Unsupported";
case "disabled": return "Disabled";
+ case "handled_by_scheduler": return "Scheduler";
default: return "Inactive";
}
}
@@ -333,7 +340,8 @@
function wireBackgroundDecodeEvents() {
const rigSel = document.getElementById("background-decode-rig-select");
- if (rigSel) {
+ if (rigSel && !rigSel._wired) {
+ rigSel._wired = true;
rigSel.addEventListener("change", function () {
currentRigId = rigSel.value;
loadBackgroundDecode();
@@ -341,15 +349,25 @@
}
const addBtn = document.getElementById("background-decode-bookmark-add");
- if (addBtn) addBtn.addEventListener("click", addBookmark);
+ if (addBtn && !addBtn._wired) {
+ addBtn._wired = true;
+ addBtn.addEventListener("click", addBookmark);
+ }
const saveBtn = document.getElementById("background-decode-save-btn");
- if (saveBtn) saveBtn.addEventListener("click", saveBackgroundDecode);
+ if (saveBtn && !saveBtn._wired) {
+ saveBtn._wired = true;
+ saveBtn.addEventListener("click", saveBackgroundDecode);
+ }
const resetBtn = document.getElementById("background-decode-reset-btn");
- if (resetBtn) resetBtn.addEventListener("click", resetBackgroundDecode);
+ if (resetBtn && !resetBtn._wired) {
+ resetBtn._wired = true;
+ resetBtn.addEventListener("click", resetBackgroundDecode);
+ }
}
window.initBackgroundDecode = initBackgroundDecode;
window.wireBackgroundDecodeEvents = wireBackgroundDecodeEvents;
+ window.reloadBackgroundDecodeRigSelect = renderRigSelect;
})();
diff --git a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/style.css b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/style.css
index 84f6d95..3a952c5 100644
--- a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/style.css
+++ b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/style.css
@@ -3493,6 +3493,7 @@ button:focus-visible, input:focus-visible, select:focus-visible {
.sch-extra-bm-rm:hover { opacity: 1; }
.bgd-toggle-wrap {
min-width: 18rem;
+ flex: 1 1 20rem;
}
.bgd-toggle-row {
display: inline-flex;
@@ -3506,41 +3507,25 @@ button:focus-visible, input:focus-visible, select:focus-visible {
display: flex;
gap: 0.55rem;
align-items: center;
+ width: 100%;
}
.bgd-bookmark-pick {
- min-width: min(34rem, 100%);
+ flex: 1 1 28rem;
+ min-width: 16rem;
+}
+.bgd-add-row .status-input {
+ flex: 1 1 auto;
}
.bgd-bookmark-list {
- display: flex;
- flex-wrap: wrap;
- gap: 0.55rem;
+ min-height: 1.8rem;
}
-.bgd-bookmark-chip {
- display: inline-flex;
- align-items: center;
- gap: 0.45rem;
- padding: 0.35rem 0.55rem;
- border-radius: 999px;
- border: 1px solid var(--border-light);
- background: var(--btn-bg);
- color: var(--text);
- font-size: 0.85rem;
+.bgd-bookmark-tag {
+ padding-right: 0.3rem;
}
-.bgd-bookmark-chip-meta {
+.bgd-bookmark-meta {
color: var(--text-muted);
font-size: 0.78rem;
}
-.bgd-bookmark-chip-remove {
- border: none;
- background: transparent;
- color: var(--text-muted);
- cursor: pointer;
- padding: 0;
- height: auto;
-}
-.bgd-bookmark-chip-remove:hover {
- color: var(--text);
-}
.bgd-status-list {
display: grid;
gap: 0.65rem;
@@ -3572,7 +3557,9 @@ button:focus-visible, input:focus-visible, select:focus-visible {
}
.bgd-status-state[data-state="out_of_span"],
.bgd-status-state[data-state="waiting_for_spectrum"],
-.bgd-status-state[data-state="inactive"] {
+.bgd-status-state[data-state="waiting_for_user"],
+.bgd-status-state[data-state="inactive"],
+.bgd-status-state[data-state="handled_by_scheduler"] {
color: var(--accent-yellow);
}
.bgd-status-state[data-state="missing_bookmark"],
@@ -3591,4 +3578,7 @@ button:focus-visible, input:focus-visible, select:focus-visible {
flex-direction: column;
align-items: stretch;
}
+ .bgd-add-row button {
+ width: 100%;
+ }
}
diff --git a/src/trx-client/trx-frontend/trx-frontend-http/src/background_decode.rs b/src/trx-client/trx-frontend/trx-frontend-http/src/background_decode.rs
index 363df8d..8063012 100644
--- a/src/trx-client/trx-frontend/trx-frontend-http/src/background_decode.rs
+++ b/src/trx-client/trx-frontend/trx-frontend-http/src/background_decode.rs
@@ -4,6 +4,7 @@
use std::collections::HashMap;
use std::path::{Path, PathBuf};
+use std::sync::atomic::Ordering;
use std::sync::{Arc, RwLock};
use std::time::Duration;
@@ -17,6 +18,7 @@ use trx_frontend::{FrontendRuntimeContext, SharedSpectrum, VChanAudioCmd};
use uuid::Uuid;
use crate::server::bookmarks::{Bookmark, BookmarkStore};
+use crate::server::scheduler::SchedulerStatusMap;
const SUPPORTED_DECODER_KINDS: &[&str] = &["ft8", "wspr", "hf-aprs"];
const CHANNEL_KIND_NAME: &str = "VirtualBackgroundDecodeChannel";
@@ -119,6 +121,7 @@ pub struct BackgroundDecodeManager {
store: Arc,
bookmarks: Arc,
context: Arc,
+ scheduler_status: SchedulerStatusMap,
status: Arc>>,
notify_tx: broadcast::Sender<()>,
}
@@ -128,12 +131,14 @@ impl BackgroundDecodeManager {
store: Arc,
bookmarks: Arc,
context: Arc,
+ scheduler_status: SchedulerStatusMap,
) -> Arc {
let (notify_tx, _) = broadcast::channel(16);
Arc::new(Self {
store,
bookmarks,
context,
+ scheduler_status,
status: Arc::new(RwLock::new(HashMap::new())),
notify_tx,
})
@@ -296,6 +301,12 @@ impl BackgroundDecodeManager {
let config = self.get_config(&rig_id);
let selected = dedup_ids(&config.bookmark_ids);
+ let users_connected = self.context.sse_clients.load(Ordering::Relaxed) > 0;
+ let scheduled_bookmark_ids = if users_connected {
+ Vec::new()
+ } else {
+ self.scheduler_bookmark_ids(&rig_id)
+ };
let selected_bookmarks: HashMap = self
.bookmarks
.list()
@@ -344,6 +355,18 @@ impl BackgroundDecodeManager {
continue;
}
+ if !users_connected {
+ status.state = "waiting_for_user".to_string();
+ statuses.push(status);
+ continue;
+ }
+
+ if scheduled_bookmark_ids.iter().any(|id| id == &bookmark.id) {
+ status.state = "handled_by_scheduler".to_string();
+ statuses.push(status);
+ continue;
+ }
+
let (Some(center_hz), Some(half_span_hz)) = (center_hz, half_span_hz) else {
status.state = "waiting_for_spectrum".to_string();
statuses.push(status);
@@ -409,6 +432,28 @@ impl BackgroundDecodeManager {
}
}
+ fn scheduler_bookmark_ids(&self, rig_id: &str) -> Vec {
+ let Ok(guard) = self.scheduler_status.read() else {
+ return Vec::new();
+ };
+ let Some(status) = guard.get(rig_id) else {
+ return Vec::new();
+ };
+ if !status.active {
+ return Vec::new();
+ }
+ let mut out = Vec::new();
+ if let Some(id) = status.last_bookmark_id.clone() {
+ out.push(id);
+ }
+ for id in &status.last_bookmark_ids {
+ if !out.iter().any(|existing| existing == id) {
+ out.push(id.clone());
+ }
+ }
+ out
+ }
+
async fn run(self: Arc) {
let mut runtime = BackgroundRuntimeState::default();
let mut notify_rx = self.notify_tx.subscribe();
diff --git a/src/trx-client/trx-frontend/trx-frontend-http/src/server.rs b/src/trx-client/trx-frontend/trx-frontend-http/src/server.rs
index 542a46c..d3be641 100644
--- a/src/trx-client/trx-frontend/trx-frontend-http/src/server.rs
+++ b/src/trx-client/trx-frontend/trx-frontend-http/src/server.rs
@@ -92,6 +92,7 @@ async fn serve(
background_decode_store,
bookmark_store.clone(),
context.clone(),
+ scheduler_status.clone(),
);
background_decode_mgr.spawn();