From ac1ade2fafd5b1a1cf9e11246769e07780d3ecef Mon Sep 17 00:00:00 2001 From: x12sa Date: Mon, 6 Jul 2026 08:53:15 +0200 Subject: [PATCH] webpage_generator: instrument pane shows full live measurement parameters 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. --- .../flomni/flomni_webpage_generator.py | 135 +++++++++++------- 1 file changed, 83 insertions(+), 52 deletions(-) diff --git a/csaxs_bec/bec_ipython_client/plugins/flomni/flomni_webpage_generator.py b/csaxs_bec/bec_ipython_client/plugins/flomni/flomni_webpage_generator.py index a34ad90..0802302 100644 --- a/csaxs_bec/bec_ipython_client/plugins/flomni/flomni_webpage_generator.py +++ b/csaxs_bec/bec_ipython_client/plugins/flomni/flomni_webpage_generator.py @@ -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+='

Measurement settings

'; - for(const[k,v] of Object.entries(setup.settings)) html+=''; + 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+='

Current measurement

'+esc(k)+''+esc(v)+'
'; + if(setup.sample_name) html+=''; + html+=buildParamRows(cp); html+='
Sample name'+esc(setup.sample_name)+'
'; }} 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+=''+dispLabel+''+esc(fmtTqParam(key,params[key]))+''; + }} + if(key==='tomo_angle_stepsize'){{ + const nproj=calcProjections(params); + if(nproj!=null) rows+='Projections'+nproj+''; + }} + }}); + 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+=''+dispLabel+''+esc(fmtTqParam(key,params[key]))+''; - }} - // 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+='Projections'+nproj+''; - }} - }}); + 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'}}) :'';