webpage_generator: instrument pane shows full live measurement parameters
CI for csaxs_bec / test (push) Successful in 1m34s
CI for csaxs_bec / test (push) Successful in 1m34s
The instrument-details pane showed only a hand-built 6-row settings summary, far less than the tomo queue shows per job, and nothing useful for a tomo started outside the queue. - Flomni _collect_setup_data now returns current_params: the tomo parameters read live from global vars, keyed by the same names the queue stores in job.params (_CURRENT_PARAM_KEYS, matching TQ_PARAM_DISPLAY). Values left raw; the page formats them. Replaces the old settings dict. - Extract the queue's param-table builder into a shared buildParamRows() JS helper (carries the computed Projections row), used by both the queue and the new instrument 'Current measurement' section, so they can't drift. - renderInstrument renders current_params via buildParamRows (Sample name row first); card hides only when there are neither params nor temperatures. - Base _collect_setup_data contract updated: settings -> current_params.
This commit is contained in:
@@ -1373,10 +1373,21 @@ class WebpageGeneratorBase:
|
||||
by the HTML page as the 'Instrument details' section.
|
||||
|
||||
Expected keys (all optional):
|
||||
type (str) -- setup identifier, e.g. "flomni"
|
||||
sample_name (str) -- current sample name
|
||||
temperatures (dict) -- label -> float (degrees C) or None
|
||||
settings (dict) -- label -> formatted string
|
||||
type (str) -- setup identifier, e.g. "flomni"
|
||||
sample_name (str) -- current sample name
|
||||
temperatures (dict) -- label -> float (degrees C) or None
|
||||
current_params (dict) -- raw tomo parameters read live (keyed by
|
||||
the same global-var names the tomo queue
|
||||
stores in job.params, e.g. tomo_countingtime,
|
||||
fovx, tomo_angle_stepsize, ...). Rendered by
|
||||
the page through the same param-table path
|
||||
as a queue job (buildParamRows), so the
|
||||
'Current measurement' section always matches
|
||||
the queue's level of detail -- and shows the
|
||||
details even for a scan started outside the
|
||||
queue. Values are left raw; the page formats
|
||||
them (fmtTqParam) and computes the projection
|
||||
count (calcProjections).
|
||||
"""
|
||||
return {}
|
||||
|
||||
@@ -1402,10 +1413,31 @@ class WebpageGeneratorBase:
|
||||
class FlomniWebpageGenerator(WebpageGeneratorBase):
|
||||
"""
|
||||
flOMNI-specific webpage generator.
|
||||
Adds: temperatures, sample name, measurement settings from global vars.
|
||||
Adds: temperatures, sample name, and live current-measurement parameters
|
||||
from global vars.
|
||||
Logo: flOMNI.png from the same directory as this file.
|
||||
"""
|
||||
|
||||
# Global-var names of the tomo parameters to read live for the
|
||||
# 'Current measurement' section. Kept in the same order as, and matching,
|
||||
# the tomo queue's TQ_PARAM_DISPLAY keys so the two render identically.
|
||||
# (Any name that is not actually a global var is skipped silently and just
|
||||
# does not appear as a row.)
|
||||
_CURRENT_PARAM_KEYS = [
|
||||
"tomo_type",
|
||||
"tomo_countingtime",
|
||||
"fovx",
|
||||
"fovy",
|
||||
"tomo_angle_stepsize",
|
||||
"tomo_angle_range",
|
||||
"tomo_shellstep",
|
||||
"stitch_x",
|
||||
"stitch_y",
|
||||
"frames_per_trigger",
|
||||
"single_point_instead_of_fermat_scan",
|
||||
"ptycho_reconstruct_foldername",
|
||||
]
|
||||
|
||||
# label -> dotpath under device_manager.devices
|
||||
_TEMP_MAP = {
|
||||
"Heater": "flomni_temphum.temperature_heater",
|
||||
@@ -1429,40 +1461,28 @@ class FlomniWebpageGenerator(WebpageGeneratorBase):
|
||||
for label, path in self._TEMP_MAP.items()
|
||||
}
|
||||
|
||||
# ── Settings from global vars ─────────────────────────────────
|
||||
# ── Current measurement parameters (read live) ────────────────
|
||||
# Same keys the tomo queue stores in job.params, so the 'Current
|
||||
# measurement' section on the page renders through the identical
|
||||
# param-table path (buildParamRows -> fmtTqParam / calcProjections)
|
||||
# and always matches the queue's level of detail. Reading these live
|
||||
# means a tomo started OUTSIDE the queue still shows full parameters.
|
||||
# Values are left raw; the page does the formatting.
|
||||
g = self._bec
|
||||
|
||||
def _fmt2(v):
|
||||
current_params = {}
|
||||
for key in self._CURRENT_PARAM_KEYS:
|
||||
try:
|
||||
return f"{float(v):.2f}"
|
||||
val = g.get_global_var(key)
|
||||
except Exception:
|
||||
return "N/A"
|
||||
|
||||
fovx = g.get_global_var("fovx")
|
||||
fovy = g.get_global_var("fovy")
|
||||
stx = g.get_global_var("stitch_x")
|
||||
sty = g.get_global_var("stitch_y")
|
||||
|
||||
def _fmt_int(v):
|
||||
try:
|
||||
return str(int(v))
|
||||
except (TypeError, ValueError):
|
||||
return "N/A"
|
||||
|
||||
settings = {
|
||||
"Sample name": sample_name,
|
||||
"FOV x / y": f"{_fmt2(fovx)} / {_fmt2(fovy)} \u00b5m",
|
||||
"Step size": _gvar(g, "tomo_shellstep", ".2f", " \u00b5m"),
|
||||
"Exposure time": _gvar(g, "tomo_countingtime", ".3f", " s"),
|
||||
"Angle step": _gvar(g, "tomo_angle_stepsize", ".2f", "\u00b0"),
|
||||
"Stitch x / y": f"{_fmt_int(stx)} / {_fmt_int(sty)}",
|
||||
}
|
||||
val = None
|
||||
if val is not None:
|
||||
current_params[key] = val
|
||||
|
||||
return {
|
||||
"type": "flomni",
|
||||
"sample_name": sample_name,
|
||||
"temperatures": temperatures,
|
||||
"settings": settings,
|
||||
"type": "flomni",
|
||||
"sample_name": sample_name,
|
||||
"temperatures": temperatures,
|
||||
"current_params": current_params,
|
||||
}
|
||||
|
||||
|
||||
@@ -2656,12 +2676,18 @@ function esc(s){{return String(s==null?'N/A':s).replace(/&/g,'&').replace(/<
|
||||
|
||||
function renderInstrument(setup){{
|
||||
const card=document.getElementById('instrument-card'),grid=document.getElementById('instrument-grid');
|
||||
if(!setup||(!setup.temperatures&&!setup.settings)){{card.style.display='none';return;}}
|
||||
const cp=(setup&&setup.current_params)||null;
|
||||
const hasParams=cp&&Object.keys(cp).length>0;
|
||||
if(!setup||(!setup.temperatures&&!hasParams)){{card.style.display='none';return;}}
|
||||
card.style.display='';
|
||||
let html='';
|
||||
if(setup.settings){{
|
||||
html+='<div class="instrument-section"><h3>Measurement settings</h3><table class="kv-table">';
|
||||
for(const[k,v] of Object.entries(setup.settings)) html+='<tr><td>'+esc(k)+'</td><td>'+esc(v)+'</td></tr>';
|
||||
if(hasParams){{
|
||||
// Same param-table path as a tomo queue job (buildParamRows), so the live
|
||||
// 'Current measurement' matches the queue's level of detail — and shows
|
||||
// full parameters even for a scan started outside the queue.
|
||||
html+='<div class="instrument-section"><h3>Current measurement</h3><table class="kv-table tq-params">';
|
||||
if(setup.sample_name) html+='<tr><td>Sample name</td><td>'+esc(setup.sample_name)+'</td></tr>';
|
||||
html+=buildParamRows(cp);
|
||||
html+='</table></div>';
|
||||
}}
|
||||
if(setup.temperatures){{
|
||||
@@ -2761,6 +2787,24 @@ function calcProjections(params){{
|
||||
return null;
|
||||
}}
|
||||
|
||||
// Build the tomo-parameter table rows for a params object, in TQ_PARAM_DISPLAY
|
||||
// order, injecting the computed Projections row after the angle step. Shared by
|
||||
// the tomo queue (per job) and the instrument 'Current measurement' section, so
|
||||
// both always show the same parameters, formatted the same way.
|
||||
function buildParamRows(params){{
|
||||
let rows='';
|
||||
TQ_PARAM_DISPLAY.forEach(([key,dispLabel])=>{{
|
||||
if(key in params){{
|
||||
rows+='<tr><td>'+dispLabel+'</td><td>'+esc(fmtTqParam(key,params[key]))+'</td></tr>';
|
||||
}}
|
||||
if(key==='tomo_angle_stepsize'){{
|
||||
const nproj=calcProjections(params);
|
||||
if(nproj!=null) rows+='<tr><td>Projections</td><td>'+nproj+'</td></tr>';
|
||||
}}
|
||||
}});
|
||||
return rows;
|
||||
}}
|
||||
|
||||
function renderTomoQueue(jobs){{
|
||||
const el=document.getElementById('tomo-queue-content');
|
||||
if(!el) return; // setup has no tomo-queue card (HAS_TOMO_QUEUE=False)
|
||||
@@ -2797,20 +2841,7 @@ function renderTomoQueue(jobs){{
|
||||
shouldOpen=true;
|
||||
autoOpened.add(key);
|
||||
}}
|
||||
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>';
|
||||
}}
|
||||
// After angle step, inject a computed projection count row, driven by
|
||||
// this instrument's TOMO_TYPES config (must match the corresponding
|
||||
// Python-side formula, e.g. _tomo_type1_actual_grid() for flomni's
|
||||
// "equally_spaced_grid" type).
|
||||
if(key==='tomo_angle_stepsize'){{
|
||||
const nproj=calcProjections(params);
|
||||
if(nproj!=null) paramRows+='<tr><td>Projections</td><td>'+nproj+'</td></tr>';
|
||||
}}
|
||||
}});
|
||||
const paramRows=buildParamRows(params);
|
||||
const addedAt=job.added_at
|
||||
?'Added '+new Date(job.added_at).toLocaleString([],{{month:'short',day:'numeric',hour:'2-digit',minute:'2-digit'}})
|
||||
:'';
|
||||
|
||||
Reference in New Issue
Block a user