[fix](trx-frontend-http): reset scheduler step timing

Track the last applied scheduler entry so previous/next\ncycles correctly across active entries and resets the\ncountdown after manual entry changes.\n\nVerification: cargo test -p trx-frontend-http scheduler\nVerification: node --check assets/web/plugins/scheduler.js\n\nCo-authored-by: OpenAI Codex <codex@openai.com>

Signed-off-by: Stan Grams <sjg@haxx.space>
This commit is contained in:
2026-03-14 13:17:45 +01:00
parent badf7c0d0f
commit dca01ead90
2 changed files with 45 additions and 3 deletions
@@ -13,6 +13,7 @@
let schedulerRole = null; // "control" | "rx" | null let schedulerRole = null; // "control" | "rx" | null
let currentRigId = null; let currentRigId = null;
let currentConfig = null; let currentConfig = null;
let currentSchedulerStatus = null;
let bookmarkList = []; // [{id, name, freq_hz, mode}, ...] let bookmarkList = []; // [{id, name, freq_hz, mode}, ...]
let statusInterval = null; let statusInterval = null;
let interleaveTicker = null; let interleaveTicker = null;
@@ -211,6 +212,30 @@
if (!(cycleMin > 0)) { if (!(cycleMin > 0)) {
return { activeEntries: active, currentIndex: 0, remainingSec: 0, cycleMin: 0 }; return { activeEntries: active, currentIndex: 0, remainingSec: 0, cycleMin: 0 };
} }
const statusEntryId = currentSchedulerStatus && currentSchedulerStatus.last_entry_id
? String(currentSchedulerStatus.last_entry_id)
: "";
const statusIndex = statusEntryId
? active.findIndex(function (entry) { return String(entry && entry.id || "") === statusEntryId; })
: -1;
const statusAppliedUtc = currentSchedulerStatus && Number.isFinite(Number(currentSchedulerStatus.last_applied_utc))
? Number(currentSchedulerStatus.last_applied_utc)
: null;
if (statusIndex >= 0 && statusAppliedUtc != null) {
const manualDurationMin = durations[statusIndex];
const elapsedSec = Math.max(0, schedulerUtcSeconds() - statusAppliedUtc);
const remainingSec = (manualDurationMin > 0)
? Math.max(1, (manualDurationMin * 60) - elapsedSec)
: 0;
if (remainingSec > 0) {
return {
activeEntries: active,
currentIndex: statusIndex,
remainingSec: remainingSec,
cycleMin: cycleMin,
};
}
}
const overlapStart = active.reduce(function (maxStart, entry) { const overlapStart = active.reduce(function (maxStart, entry) {
return Math.max(maxStart, schedulerEntryCurrentWindowStart(entry, nowMin)); return Math.max(maxStart, schedulerEntryCurrentWindowStart(entry, nowMin));
}, Number.NEGATIVE_INFINITY); }, Number.NEGATIVE_INFINITY);
@@ -285,7 +310,9 @@
if (!rig) return; if (!rig) return;
apiGetStatus(rig) apiGetStatus(rig)
.then(function (st) { .then(function (st) {
currentSchedulerStatus = st || null;
renderStatus(st); renderStatus(st);
renderSchedulerInterleaveStatus();
}) })
.catch(function () {}); .catch(function () {});
} }
@@ -297,7 +324,13 @@
el.textContent = "No activity yet."; el.textContent = "No activity yet.";
return; return;
} }
const name = st.last_bookmark_name || st.last_bookmark_id || ""; const statusEntryId = st.last_entry_id ? String(st.last_entry_id) : "";
const entry = statusEntryId && currentConfig && Array.isArray(currentConfig.entries)
? currentConfig.entries.find(function (item) { return String(item && item.id || "") === statusEntryId; })
: null;
const name = entry
? schedulerEntryDisplayName(entry)
: (st.last_bookmark_name || st.last_bookmark_id || "—");
let ts = ""; let ts = "";
if (st.last_applied_utc) { if (st.last_applied_utc) {
const d = new Date(st.last_applied_utc * 1000); const d = new Date(st.last_applied_utc * 1000);
@@ -489,6 +522,7 @@
return apiActivateSchedulerEntry(currentRigId, target.id); return apiActivateSchedulerEntry(currentRigId, target.id);
}) })
.then(function (status) { .then(function (status) {
currentSchedulerStatus = status || null;
renderStatus(status); renderStatus(status);
renderSchedulerInterleaveStatus(); renderSchedulerInterleaveStatus();
showSchedulerToast("Selected " + schedulerEntryDisplayName(target) + "."); showSchedulerToast("Selected " + schedulerEntryDisplayName(target) + ".");
@@ -398,6 +398,8 @@ fn utc_minutes_now() -> f64 {
#[derive(Debug, Clone, Serialize, Default)] #[derive(Debug, Clone, Serialize, Default)]
pub struct SchedulerStatus { pub struct SchedulerStatus {
pub active: bool, pub active: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub last_entry_id: Option<String>,
pub last_bookmark_id: Option<String>, pub last_bookmark_id: Option<String>,
pub last_bookmark_name: Option<String>, pub last_bookmark_name: Option<String>,
pub last_applied_utc: Option<i64>, pub last_applied_utc: Option<i64>,
@@ -414,6 +416,7 @@ async fn apply_scheduler_target(
rig_id: &str, rig_id: &str,
status_map: &SchedulerStatusMap, status_map: &SchedulerStatusMap,
bookmarks: &BookmarkStore, bookmarks: &BookmarkStore,
entry_id: Option<&str>,
bookmark_id: &str, bookmark_id: &str,
center_hz: Option<u64>, center_hz: Option<u64>,
extra_bm_ids: &[String], extra_bm_ids: &[String],
@@ -456,6 +459,7 @@ async fn apply_scheduler_target(
let status = SchedulerStatus { let status = SchedulerStatus {
active: true, active: true,
last_entry_id: entry_id.map(str::to_string),
last_bookmark_id: Some(bookmark_id.to_string()), last_bookmark_id: Some(bookmark_id.to_string()),
last_bookmark_name: Some(bookmark.name.clone()), last_bookmark_name: Some(bookmark.name.clone()),
last_applied_utc: Some( last_applied_utc: Some(
@@ -586,7 +590,7 @@ pub fn spawn_scheduler_task(
continue; continue;
} }
let (bm_id, center_hz, extra_bm_ids) = match &config.mode { let (entry_id, bm_id, center_hz, extra_bm_ids) = match &config.mode {
SchedulerMode::Disabled => continue, SchedulerMode::Disabled => continue,
SchedulerMode::Grayline => { SchedulerMode::Grayline => {
let Some(bm_id) = config let Some(bm_id) = config
@@ -596,7 +600,7 @@ pub fn spawn_scheduler_task(
else { else {
continue; continue;
}; };
(bm_id, None, Vec::new()) (None, bm_id, None, Vec::new())
} }
SchedulerMode::TimeSpan => { SchedulerMode::TimeSpan => {
let Some(entry) = let Some(entry) =
@@ -605,6 +609,7 @@ pub fn spawn_scheduler_task(
continue; continue;
}; };
( (
Some(entry.id.clone()),
entry.bookmark_id.clone(), entry.bookmark_id.clone(),
entry.center_hz, entry.center_hz,
entry.bookmark_ids.clone(), entry.bookmark_ids.clone(),
@@ -641,6 +646,7 @@ pub fn spawn_scheduler_task(
&config.rig_id, &config.rig_id,
&status_map, &status_map,
&bookmarks, &bookmarks,
entry_id.as_deref(),
&bm_id, &bm_id,
center_hz, center_hz,
&extra_bm_ids, &extra_bm_ids,
@@ -731,6 +737,7 @@ async fn apply_last_scheduler_cycle(
rig_id, rig_id,
status_map, status_map,
bookmarks, bookmarks,
status.last_entry_id.as_deref(),
&bookmark_id, &bookmark_id,
status.last_center_hz, status.last_center_hz,
&status.last_bookmark_ids, &status.last_bookmark_ids,
@@ -858,6 +865,7 @@ pub async fn put_scheduler_activate_entry(
&rig_id, &rig_id,
status_map.get_ref(), status_map.get_ref(),
bookmarks.get_ref().as_ref(), bookmarks.get_ref().as_ref(),
Some(&entry.id),
&entry.bookmark_id, &entry.bookmark_id,
entry.center_hz, entry.center_hz,
&entry.bookmark_ids, &entry.bookmark_ids,