[feat](trx-frontend-http): per-entry interleave time in TimeSpan scheduler
Each ScheduleEntry can now carry its own interleave_min, overriding the config-level default for that slot in the cycle. The cycle length is the sum of all active entries' effective durations (weighted), so entries with longer individual interleave times occupy proportionally more time. UI: "Interleave (min, optional)" input in the add-entry form; value shown in the entries table (displays "—" when using the config default). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: Stan Grams <sjg@haxx.space>
This commit is contained in:
@@ -724,7 +724,7 @@
|
||||
</div>
|
||||
<table class="sch-ts-table">
|
||||
<thead>
|
||||
<tr><th>Start</th><th>End</th><th>Bookmark</th><th>Label</th><th></th></tr>
|
||||
<tr><th>Start</th><th>End</th><th>Bookmark</th><th>Label</th><th>Interleave (min)</th><th></th></tr>
|
||||
</thead>
|
||||
<tbody id="scheduler-ts-tbody"></tbody>
|
||||
</table>
|
||||
@@ -741,6 +741,9 @@
|
||||
<label class="sch-label">Label (optional)
|
||||
<input type="text" id="scheduler-ts-label" class="status-input" placeholder="e.g. 40m FT8" />
|
||||
</label>
|
||||
<label class="sch-label">Interleave (min, optional)
|
||||
<input type="number" id="scheduler-ts-entry-interleave" class="status-input" min="1" max="60" placeholder="default" style="width:6rem;" />
|
||||
</label>
|
||||
<button id="scheduler-ts-add-btn" class="sch-write" type="button">+ Add</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -277,11 +277,13 @@
|
||||
: [];
|
||||
entries.forEach(function (entry, idx) {
|
||||
const tr = document.createElement("tr");
|
||||
const il = entry.interleave_min ? String(entry.interleave_min) + " min" : "—";
|
||||
tr.innerHTML =
|
||||
'<td>' + minToHHMM(entry.start_min) + '</td>' +
|
||||
'<td>' + minToHHMM(entry.end_min) + '</td>' +
|
||||
'<td>' + bmName(entry.bookmark_id) + '</td>' +
|
||||
'<td>' + escHtml(entry.label || "") + '</td>' +
|
||||
'<td>' + il + '</td>' +
|
||||
'<td><button class="sch-write sch-remove-btn" data-idx="' + idx + '" type="button">Remove</button></td>';
|
||||
tbody.appendChild(tr);
|
||||
});
|
||||
@@ -330,12 +332,15 @@
|
||||
const endEl = document.getElementById("scheduler-ts-end");
|
||||
const bmEl = document.getElementById("scheduler-ts-bookmark");
|
||||
const labelEl = document.getElementById("scheduler-ts-label");
|
||||
const ilEl = document.getElementById("scheduler-ts-entry-interleave");
|
||||
if (!startEl || !endEl || !bmEl) return;
|
||||
|
||||
const startMin = hhmmToMin(startEl.value);
|
||||
const endMin = hhmmToMin(endEl.value);
|
||||
const bmId = bmEl.value;
|
||||
const label = labelEl ? labelEl.value.trim() : "";
|
||||
const ilVal = ilEl ? parseInt(ilEl.value, 10) : NaN;
|
||||
const entryInterleave = !isNaN(ilVal) && ilVal > 0 ? ilVal : null;
|
||||
|
||||
if (!bmId) {
|
||||
alert("Please select a bookmark.");
|
||||
@@ -354,12 +359,14 @@
|
||||
end_min: endMin,
|
||||
bookmark_id: bmId,
|
||||
label: label || null,
|
||||
interleave_min: entryInterleave,
|
||||
});
|
||||
|
||||
startEl.value = "";
|
||||
endEl.value = "";
|
||||
bmEl.value = ""; // reset select to first option
|
||||
bmEl.value = "";
|
||||
if (labelEl) labelEl.value = "";
|
||||
if (ilEl) ilEl.value = "";
|
||||
|
||||
renderTimespanEntries();
|
||||
}
|
||||
|
||||
@@ -68,6 +68,11 @@ pub struct ScheduleEntry {
|
||||
pub bookmark_id: String,
|
||||
#[serde(default)]
|
||||
pub label: Option<String>,
|
||||
/// Per-entry interleave duration in minutes. Overrides the config-level
|
||||
/// `interleave_min` when set. Allows each entry to occupy a differently
|
||||
/// sized slice of the interleave cycle.
|
||||
#[serde(default)]
|
||||
pub interleave_min: Option<u32>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||
@@ -284,7 +289,7 @@ fn entry_is_active(entry: &ScheduleEntry, now_min: f64) -> bool {
|
||||
fn timespan_bookmark_id(
|
||||
entries: &[ScheduleEntry],
|
||||
now_min: f64,
|
||||
interleave_min: Option<u32>,
|
||||
default_interleave: Option<u32>,
|
||||
) -> Option<String> {
|
||||
let active: Vec<&ScheduleEntry> = entries
|
||||
.iter()
|
||||
@@ -295,11 +300,24 @@ fn timespan_bookmark_id(
|
||||
return None;
|
||||
}
|
||||
|
||||
// With interleaving and more than one active entry, pick by time slot.
|
||||
// With interleaving and more than one active entry, use a weighted cycle.
|
||||
// Each entry's effective duration is its own interleave_min, falling back
|
||||
// to the config-level default. The cycle length is the sum of all durations.
|
||||
if active.len() > 1 {
|
||||
if let Some(step) = interleave_min.filter(|&s| s > 0) {
|
||||
let slot = (now_min as u64 / step as u64) as usize % active.len();
|
||||
return Some(active[slot].bookmark_id.clone());
|
||||
let durations: Vec<u32> = active
|
||||
.iter()
|
||||
.map(|e| e.interleave_min.or(default_interleave).unwrap_or(0))
|
||||
.collect();
|
||||
let cycle: u32 = durations.iter().sum();
|
||||
if cycle > 0 {
|
||||
let pos = (now_min as u64) % (cycle as u64);
|
||||
let mut cum = 0u64;
|
||||
for (entry, &dur) in active.iter().zip(durations.iter()) {
|
||||
cum += dur as u64;
|
||||
if pos < cum {
|
||||
return Some(entry.bookmark_id.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user