[fix](trx-frontend-http): refresh background decode settings UI
Co-authored-by: OpenAI Codex <codex@openai.com> Signed-off-by: Stan Grams <sjg@haxx.space>
This commit is contained in:
@@ -750,6 +750,7 @@ function applyRigList(activeRigId, rigIds, displayNames) {
|
|||||||
populateRigPicker(headerRigSwitchSelect, lastRigIds, activeRigId, disableSwitch);
|
populateRigPicker(headerRigSwitchSelect, lastRigIds, activeRigId, disableSwitch);
|
||||||
updateRigSubtitle(activeRigId);
|
updateRigSubtitle(activeRigId);
|
||||||
if (typeof reloadSchedulerRigSelect === "function") reloadSchedulerRigSelect();
|
if (typeof reloadSchedulerRigSelect === "function") reloadSchedulerRigSelect();
|
||||||
|
if (typeof reloadBackgroundDecodeRigSelect === "function") reloadBackgroundDecodeRigSelect();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function refreshRigList() {
|
async function refreshRigList() {
|
||||||
|
|||||||
@@ -804,7 +804,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div id="background-decode-bookmark-list" class="bgd-bookmark-list"></div>
|
<div id="background-decode-bookmark-list" class="sch-extra-bm-list bgd-bookmark-list"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="sch-actions">
|
<div class="sch-actions">
|
||||||
|
|||||||
+27
-9
@@ -26,6 +26,7 @@
|
|||||||
if (!sel) return;
|
if (!sel) return;
|
||||||
const rigs = typeof getAvailableRigIds === "function" ? getAvailableRigIds() : [];
|
const rigs = typeof getAvailableRigIds === "function" ? getAvailableRigIds() : [];
|
||||||
if (!rigs.length) return;
|
if (!rigs.length) return;
|
||||||
|
const prevRigId = currentRigId;
|
||||||
sel.innerHTML = "";
|
sel.innerHTML = "";
|
||||||
rigs.forEach(function (rigId) {
|
rigs.forEach(function (rigId) {
|
||||||
const opt = document.createElement("option");
|
const opt = document.createElement("option");
|
||||||
@@ -37,6 +38,11 @@
|
|||||||
if (!currentRigId || !rigs.includes(currentRigId)) {
|
if (!currentRigId || !rigs.includes(currentRigId)) {
|
||||||
currentRigId = rigs[0];
|
currentRigId = rigs[0];
|
||||||
sel.value = currentRigId;
|
sel.value = currentRigId;
|
||||||
|
} else {
|
||||||
|
sel.value = currentRigId;
|
||||||
|
}
|
||||||
|
if (currentRigId && currentRigId !== prevRigId) {
|
||||||
|
loadBackgroundDecode();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -158,14 +164,13 @@
|
|||||||
ids.forEach(function (id) {
|
ids.forEach(function (id) {
|
||||||
const bookmark = bookmarkList.find(function (item) { return item.id === id; });
|
const bookmark = bookmarkList.find(function (item) { return item.id === id; });
|
||||||
const chip = document.createElement("div");
|
const chip = document.createElement("div");
|
||||||
chip.className = "bgd-bookmark-chip";
|
chip.className = "sch-extra-bm-tag bgd-bookmark-tag";
|
||||||
const decoders = bookmarkDecoderKinds(bookmark);
|
const decoders = bookmarkDecoderKinds(bookmark);
|
||||||
chip.innerHTML =
|
chip.innerHTML =
|
||||||
'<span>' + escHtml(bookmark ? bookmark.name : id) + '</span>' +
|
'<span>' + escHtml(bookmark ? bookmark.name : id) + '</span>' +
|
||||||
'<span class="bgd-bookmark-chip-meta">' + escHtml(bookmark ? (formatFreq(bookmark.freq_hz) + " " + bookmark.mode + " · " + decoders.join("/").toUpperCase()) : "Missing bookmark") + '</span>';
|
'<span class="bgd-bookmark-meta">' + escHtml(bookmark ? (formatFreq(bookmark.freq_hz) + " " + bookmark.mode + " · " + decoders.join("/").toUpperCase()) : "Missing bookmark") + '</span>';
|
||||||
const btn = document.createElement("button");
|
const btn = document.createElement("span");
|
||||||
btn.type = "button";
|
btn.className = "sch-extra-bm-rm";
|
||||||
btn.className = "bgd-bookmark-chip-remove sch-write";
|
|
||||||
btn.textContent = "×";
|
btn.textContent = "×";
|
||||||
btn.addEventListener("click", function () {
|
btn.addEventListener("click", function () {
|
||||||
removeBookmark(id);
|
removeBookmark(id);
|
||||||
@@ -293,9 +298,11 @@
|
|||||||
case "active": return "Active";
|
case "active": return "Active";
|
||||||
case "out_of_span": return "Out of span";
|
case "out_of_span": return "Out of span";
|
||||||
case "waiting_for_spectrum": return "Waiting";
|
case "waiting_for_spectrum": return "Waiting";
|
||||||
|
case "waiting_for_user": return "No user";
|
||||||
case "missing_bookmark": return "Missing";
|
case "missing_bookmark": return "Missing";
|
||||||
case "no_supported_decoders": return "Unsupported";
|
case "no_supported_decoders": return "Unsupported";
|
||||||
case "disabled": return "Disabled";
|
case "disabled": return "Disabled";
|
||||||
|
case "handled_by_scheduler": return "Scheduler";
|
||||||
default: return "Inactive";
|
default: return "Inactive";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -333,7 +340,8 @@
|
|||||||
|
|
||||||
function wireBackgroundDecodeEvents() {
|
function wireBackgroundDecodeEvents() {
|
||||||
const rigSel = document.getElementById("background-decode-rig-select");
|
const rigSel = document.getElementById("background-decode-rig-select");
|
||||||
if (rigSel) {
|
if (rigSel && !rigSel._wired) {
|
||||||
|
rigSel._wired = true;
|
||||||
rigSel.addEventListener("change", function () {
|
rigSel.addEventListener("change", function () {
|
||||||
currentRigId = rigSel.value;
|
currentRigId = rigSel.value;
|
||||||
loadBackgroundDecode();
|
loadBackgroundDecode();
|
||||||
@@ -341,15 +349,25 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
const addBtn = document.getElementById("background-decode-bookmark-add");
|
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");
|
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");
|
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.initBackgroundDecode = initBackgroundDecode;
|
||||||
window.wireBackgroundDecodeEvents = wireBackgroundDecodeEvents;
|
window.wireBackgroundDecodeEvents = wireBackgroundDecodeEvents;
|
||||||
|
window.reloadBackgroundDecodeRigSelect = renderRigSelect;
|
||||||
})();
|
})();
|
||||||
|
|||||||
@@ -3493,6 +3493,7 @@ button:focus-visible, input:focus-visible, select:focus-visible {
|
|||||||
.sch-extra-bm-rm:hover { opacity: 1; }
|
.sch-extra-bm-rm:hover { opacity: 1; }
|
||||||
.bgd-toggle-wrap {
|
.bgd-toggle-wrap {
|
||||||
min-width: 18rem;
|
min-width: 18rem;
|
||||||
|
flex: 1 1 20rem;
|
||||||
}
|
}
|
||||||
.bgd-toggle-row {
|
.bgd-toggle-row {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
@@ -3506,41 +3507,25 @@ button:focus-visible, input:focus-visible, select:focus-visible {
|
|||||||
display: flex;
|
display: flex;
|
||||||
gap: 0.55rem;
|
gap: 0.55rem;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
}
|
}
|
||||||
.bgd-bookmark-pick {
|
.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 {
|
.bgd-bookmark-list {
|
||||||
display: flex;
|
min-height: 1.8rem;
|
||||||
flex-wrap: wrap;
|
|
||||||
gap: 0.55rem;
|
|
||||||
}
|
}
|
||||||
.bgd-bookmark-chip {
|
.bgd-bookmark-tag {
|
||||||
display: inline-flex;
|
padding-right: 0.3rem;
|
||||||
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-chip-meta {
|
.bgd-bookmark-meta {
|
||||||
color: var(--text-muted);
|
color: var(--text-muted);
|
||||||
font-size: 0.78rem;
|
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 {
|
.bgd-status-list {
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: 0.65rem;
|
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="out_of_span"],
|
||||||
.bgd-status-state[data-state="waiting_for_spectrum"],
|
.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);
|
color: var(--accent-yellow);
|
||||||
}
|
}
|
||||||
.bgd-status-state[data-state="missing_bookmark"],
|
.bgd-status-state[data-state="missing_bookmark"],
|
||||||
@@ -3591,4 +3578,7 @@ button:focus-visible, input:focus-visible, select:focus-visible {
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
}
|
}
|
||||||
|
.bgd-add-row button {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
use std::sync::atomic::Ordering;
|
||||||
use std::sync::{Arc, RwLock};
|
use std::sync::{Arc, RwLock};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
@@ -17,6 +18,7 @@ use trx_frontend::{FrontendRuntimeContext, SharedSpectrum, VChanAudioCmd};
|
|||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::server::bookmarks::{Bookmark, BookmarkStore};
|
use crate::server::bookmarks::{Bookmark, BookmarkStore};
|
||||||
|
use crate::server::scheduler::SchedulerStatusMap;
|
||||||
|
|
||||||
const SUPPORTED_DECODER_KINDS: &[&str] = &["ft8", "wspr", "hf-aprs"];
|
const SUPPORTED_DECODER_KINDS: &[&str] = &["ft8", "wspr", "hf-aprs"];
|
||||||
const CHANNEL_KIND_NAME: &str = "VirtualBackgroundDecodeChannel";
|
const CHANNEL_KIND_NAME: &str = "VirtualBackgroundDecodeChannel";
|
||||||
@@ -119,6 +121,7 @@ pub struct BackgroundDecodeManager {
|
|||||||
store: Arc<BackgroundDecodeStore>,
|
store: Arc<BackgroundDecodeStore>,
|
||||||
bookmarks: Arc<BookmarkStore>,
|
bookmarks: Arc<BookmarkStore>,
|
||||||
context: Arc<FrontendRuntimeContext>,
|
context: Arc<FrontendRuntimeContext>,
|
||||||
|
scheduler_status: SchedulerStatusMap,
|
||||||
status: Arc<RwLock<HashMap<String, BackgroundDecodeStatus>>>,
|
status: Arc<RwLock<HashMap<String, BackgroundDecodeStatus>>>,
|
||||||
notify_tx: broadcast::Sender<()>,
|
notify_tx: broadcast::Sender<()>,
|
||||||
}
|
}
|
||||||
@@ -128,12 +131,14 @@ impl BackgroundDecodeManager {
|
|||||||
store: Arc<BackgroundDecodeStore>,
|
store: Arc<BackgroundDecodeStore>,
|
||||||
bookmarks: Arc<BookmarkStore>,
|
bookmarks: Arc<BookmarkStore>,
|
||||||
context: Arc<FrontendRuntimeContext>,
|
context: Arc<FrontendRuntimeContext>,
|
||||||
|
scheduler_status: SchedulerStatusMap,
|
||||||
) -> Arc<Self> {
|
) -> Arc<Self> {
|
||||||
let (notify_tx, _) = broadcast::channel(16);
|
let (notify_tx, _) = broadcast::channel(16);
|
||||||
Arc::new(Self {
|
Arc::new(Self {
|
||||||
store,
|
store,
|
||||||
bookmarks,
|
bookmarks,
|
||||||
context,
|
context,
|
||||||
|
scheduler_status,
|
||||||
status: Arc::new(RwLock::new(HashMap::new())),
|
status: Arc::new(RwLock::new(HashMap::new())),
|
||||||
notify_tx,
|
notify_tx,
|
||||||
})
|
})
|
||||||
@@ -296,6 +301,12 @@ impl BackgroundDecodeManager {
|
|||||||
|
|
||||||
let config = self.get_config(&rig_id);
|
let config = self.get_config(&rig_id);
|
||||||
let selected = dedup_ids(&config.bookmark_ids);
|
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<String, Bookmark> = self
|
let selected_bookmarks: HashMap<String, Bookmark> = self
|
||||||
.bookmarks
|
.bookmarks
|
||||||
.list()
|
.list()
|
||||||
@@ -344,6 +355,18 @@ impl BackgroundDecodeManager {
|
|||||||
continue;
|
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 {
|
let (Some(center_hz), Some(half_span_hz)) = (center_hz, half_span_hz) else {
|
||||||
status.state = "waiting_for_spectrum".to_string();
|
status.state = "waiting_for_spectrum".to_string();
|
||||||
statuses.push(status);
|
statuses.push(status);
|
||||||
@@ -409,6 +432,28 @@ impl BackgroundDecodeManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn scheduler_bookmark_ids(&self, rig_id: &str) -> Vec<String> {
|
||||||
|
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<Self>) {
|
async fn run(self: Arc<Self>) {
|
||||||
let mut runtime = BackgroundRuntimeState::default();
|
let mut runtime = BackgroundRuntimeState::default();
|
||||||
let mut notify_rx = self.notify_tx.subscribe();
|
let mut notify_rx = self.notify_tx.subscribe();
|
||||||
|
|||||||
@@ -92,6 +92,7 @@ async fn serve(
|
|||||||
background_decode_store,
|
background_decode_store,
|
||||||
bookmark_store.clone(),
|
bookmark_store.clone(),
|
||||||
context.clone(),
|
context.clone(),
|
||||||
|
scheduler_status.clone(),
|
||||||
);
|
);
|
||||||
background_decode_mgr.spawn();
|
background_decode_mgr.spawn();
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user