tomo queue added

This commit is contained in:
x12sa
2026-07-03 06:48:28 +02:00
parent bf3ca21535
commit e2b9fbeb28
@@ -103,10 +103,8 @@ VERBOSITY_VERBOSE = 2
VERBOSITY_DEBUG = 3
_PHONE_NUMBERS = [
("Beamline", "+41 56 310 5845"),
("Beamline mobile", "+41 56 310 5844"),
("Local contact", "+41 79 343 9230"),
("Control room", "+41 56 310 5503"),
("Beamline", "+41 56 310 5845"),
("Control room", "+41 56 310 5503"),
]
@@ -1030,6 +1028,12 @@ class WebpageGeneratorBase:
else max(0.0, _epoch() - self._last_active_time)
)
# ── Tomo queue ───────────────────────────────────────────────
# Persisted list of parameter-snapshot jobs (global var "tomo_queue").
# Each job: {"label": str, "params": {...},
# "status": pending|running|incomplete|done, "added_at": ISO str}
tomo_queue = self._bec.get_global_var("tomo_queue") or []
# ── Reconstruction queue ──────────────────────────────────────
recon = self._collect_recon_data()
@@ -1061,6 +1065,7 @@ class WebpageGeneratorBase:
"idle_for_human": _format_duration(idle_for_s),
"queue_locks": queue_locks,
"beamline_states": beamline_states_info,
"tomo_queue": tomo_queue,
"progress": {
"tomo_type": progress.get("tomo_type", "N/A"),
"projection": progress.get("projection", 0),
@@ -1806,6 +1811,52 @@ def _render_html(phone_numbers: list) -> str:
.bl-badge.bl-watched-no {{ background: var(--surface2); color: var(--text-dim); }}
.blstates-none {{ font-family: var(--mono); font-size: 0.75rem; color: var(--text-dim); padding: 0.5rem 0; }}
/* ── Tomo queue card ── */
.tq-empty {{
font-family: var(--mono); font-size: 0.78rem; color: var(--text-dim);
padding: 0.25rem 0;
}}
.tq-job {{ border-bottom: 1px solid var(--border); }}
.tq-job:last-child {{ border-bottom: none; }}
.tq-summary {{
display: flex; align-items: center; gap: 0.75rem;
padding: 0.55rem 0; cursor: pointer; list-style: none;
font-size: 0.85rem;
}}
.tq-summary::-webkit-details-marker {{ display: none; }}
.tq-summary::before {{
content: ''; font-family: var(--mono); font-size: 0.55rem;
color: var(--text-dim); transition: transform 0.2s; flex-shrink: 0;
}}
details.tq-job[open] > .tq-summary::before {{ transform: rotate(90deg); }}
.tq-num {{
font-family: var(--mono); font-size: 0.65rem; color: var(--text-dim);
min-width: 1.8rem; flex-shrink: 0;
}}
.tq-label {{ flex: 1; font-weight: 600; color: var(--text); }}
.tq-badge {{
font-family: var(--mono); font-size: 0.58rem; font-weight: 700;
letter-spacing: 0.06em; text-transform: uppercase;
padding: 0.12rem 0.45rem; border-radius: 100px; white-space: nowrap;
flex-shrink: 0;
}}
.tq-badge.tq-pending {{ background: var(--surface2); color: var(--text-dim); }}
.tq-badge.tq-running {{ background: color-mix(in srgb, var(--c-scanning) 20%, transparent); color: var(--c-scanning); }}
.tq-badge.tq-incomplete {{ background: color-mix(in srgb, var(--c-blocked) 20%, transparent); color: var(--c-blocked); }}
.tq-badge.tq-done {{ background: color-mix(in srgb, var(--c-running) 20%, transparent); color: var(--c-running); }}
.tq-details {{ padding: 0.4rem 0 0.7rem 2.4rem; }}
.tq-params {{ width: 100%; border-collapse: collapse; }}
.tq-params td {{ padding: 0.18rem 0; font-size: 0.78rem; vertical-align: top; }}
.tq-params td:first-child {{
font-family: var(--mono); font-size: 0.65rem; color: var(--text-dim);
padding-right: 1rem; white-space: nowrap; width: 40%;
}}
.tq-params td:last-child {{ color: var(--text); font-weight: 600; }}
.tq-added {{
font-family: var(--mono); font-size: 0.6rem; color: var(--text-dim);
margin-top: 0.4rem;
}}
/* ── Audio card ── */
.audio-card {{
display: flex; align-items: center;
@@ -1964,6 +2015,7 @@ def _render_html(phone_numbers: list) -> str:
<div class="info-item"><span class="label">Remaining</span><span class="value" id="pi-remaining">-</span></div>
<div class="info-item"><span class="label">Finish time</span><span class="value" id="pi-finish">-</span></div>
<div class="info-item"><span class="label">Started</span><span class="value" id="pi-start">-</span></div>
<div class="info-item" id="pi-queue-item" style="display:none"><span class="label">Queue job</span><span class="value" id="pi-queue-label">-</span></div>
</div>
</div>
@@ -2081,7 +2133,14 @@ def _render_html(phone_numbers: list) -> str:
</table>
</div>
<!-- 5. Contacts -->
<!-- 5. Tomo queue -->
<div class="card draggable-card" data-card-id="tomo-queue">
<div class="drag-handle" title="Drag to reorder">&#8942;&#8942;</div>
<div class="card-title">Tomo queue</div>
<div id="tomo-queue-content"></div>
</div>
<!-- 6. Contacts -->
<div class="card draggable-card" data-card-id="contacts">
<div class="drag-handle" title="Drag to reorder">&#8942;&#8942;</div>
<div class="card-title">Contacts</div>
@@ -2119,7 +2178,7 @@ function setTheme(t) {{
// ── Drag-and-drop card ordering ──────────────────────────────────────────
const CARD_ORDER_KEY = 'cardOrder';
const DEFAULT_ORDER = ['audio','recon-queue','ptycho','instrument','blstates','contacts'];
const DEFAULT_ORDER = ['audio','recon-queue','ptycho','instrument','blstates','tomo-queue','contacts'];
let _dragSrc = null;
function savedOrder() {{
@@ -2555,6 +2614,61 @@ function renderBeamlineStates(bl){{
tbody.innerHTML=html;
}}
// Key params to show in expanded job detail, in display order.
const TQ_PARAM_DISPLAY = [
['tomo_type', 'Type'],
['tomo_countingtime', 'Exposure (s)'],
['fovx', 'FOV x (\u00b5m)'],
['fovy', 'FOV y (\u00b5m)'],
['tomo_angle_stepsize', 'Angle step (\u00b0)'],
['tomo_angle_range', 'Angle range (\u00b0)'],
['tomo_shellstep', 'Shell step (\u00b5m)'],
['stitch_x', 'Stitch x'],
['stitch_y', 'Stitch y'],
['ptycho_reconstruct_foldername','Recon folder'],
];
function fmtTqParam(key, val){{
if(val==null) return '-';
if(typeof val==='number') return Number.isInteger(val)?String(val):val.toFixed(3).replace(/\\.?0+$/,'');
return String(val);
}}
function renderTomoQueue(jobs){{
const el=document.getElementById('tomo-queue-content');
if(!jobs||jobs.length===0){{
el.innerHTML='<div class="tq-empty">No jobs queued</div>';
return;
}}
let html='';
jobs.forEach((job,i)=>{{
const status=job.status||'pending';
const label=job.label||('Job '+(i+1));
const params=job.params||{{}};
let paramRows='';
TQ_PARAM_DISPLAY.forEach(([key,dispLabel])=>{{
if(key in params){{
paramRows+='<tr><td>'+dispLabel+'</td><td>'+esc(fmtTqParam(key,params[key]))+'</td></tr>';
}}
}});
const addedAt=job.added_at
?'Added '+new Date(job.added_at).toLocaleString([],{{month:'short',day:'numeric',hour:'2-digit',minute:'2-digit'}})
:'';
html+='<details class="tq-job"'+(status==='running'?' open':'')+'>'
+'<summary class="tq-summary">'
+'<span class="tq-num">#'+(i+1)+'</span>'
+'<span class="tq-label">'+esc(label)+'</span>'
+'<span class="tq-badge tq-'+esc(status)+'">'+esc(status)+'</span>'
+'</summary>'
+'<div class="tq-details">'
+(paramRows?'<table class="tq-params">'+paramRows+'</table>':'')
+(addedAt?'<div class="tq-added">'+esc(addedAt)+'</div>':'')
+'</div>'
+'</details>';
}});
el.innerHTML=html;
}}
function render(d){{
const s=d.experiment_status||'unknown',p=d.progress||{{}};
document.body.className=s;
@@ -2576,6 +2690,14 @@ function render(d){{
document.getElementById('pi-remaining').textContent=p.estimated_remaining_human||'-';
document.getElementById('pi-finish').textContent=fmtTime(p.estimated_finish_time);
document.getElementById('pi-start').textContent=fmtTime(p.tomo_start_time);
const runningJob=(d.tomo_queue||[]).find(j=>j.status==='running');
const queueItem=document.getElementById('pi-queue-item');
if(runningJob){{
document.getElementById('pi-queue-label').textContent=runningJob.label||'-';
queueItem.style.display='';
}}else{{
queueItem.style.display='none';
}}
if(d.recon){{
document.getElementById('recon-waiting').textContent=d.recon.waiting;
@@ -2586,6 +2708,7 @@ function render(d){{
}}
renderInstrument(d.setup);
renderBeamlineStates(d.beamline_states);
renderTomoQueue(d.tomo_queue);
renderPtycho(d.ptycho);
document.getElementById('last-update').textContent='updated '+new Date(d.generated_at).toLocaleTimeString();
const ageS=(Date.now()/1000)-d.generated_at_epoch;