From 76ed858e5cb28db1372bfda7f071a74e38cd6596 Mon Sep 17 00:00:00 2001 From: x12sa Date: Mon, 23 Mar 2026 15:58:54 +0100 Subject: [PATCH] added heartbeat, start and remaining time to progress --- .../plugins/flomni/flomni.py | 35 ++++++++++++++++++- .../plugins/flomni/gui_tools.py | 29 ++++++++++++++- 2 files changed, 62 insertions(+), 2 deletions(-) diff --git a/csaxs_bec/bec_ipython_client/plugins/flomni/flomni.py b/csaxs_bec/bec_ipython_client/plugins/flomni/flomni.py index e300f32..5058f1e 100644 --- a/csaxs_bec/bec_ipython_client/plugins/flomni/flomni.py +++ b/csaxs_bec/bec_ipython_client/plugins/flomni/flomni.py @@ -1229,6 +1229,9 @@ class _ProgressProxy: "total_projections": 1, "angle": 0, "tomo_type": 0, + "tomo_start_time": None, + "estimated_remaining_time": None, + "heartbeat": None, } def __init__(self, client): @@ -1770,6 +1773,7 @@ class Flomni( successful = False error_caught = False if 0 <= angle < 180.05: + self.progress["heartbeat"] = datetime.datetime.now().isoformat() print(f"Starting flOMNI scan for angle {angle} in subtomo {subtomo_number}") self._print_progress() while not successful: @@ -1844,6 +1848,7 @@ class Flomni( # else: self.tomo_id = 0 self.write_pdf_report() + self.progress["tomo_start_time"] = datetime.datetime.now().isoformat() with scans.dataset_id_on_hold: if self.tomo_type == 1: @@ -1953,14 +1958,42 @@ class Flomni( self._print_progress() self.OMNYTools.printgreenbold("Tomoscan finished") + @staticmethod + def _format_duration(seconds: float) -> str: + """Format a duration in seconds as a human-readable string, e.g. '2h 03m 15s'.""" + seconds = int(seconds) + h, remainder = divmod(seconds, 3600) + m, s = divmod(remainder, 60) + if h > 0: + return f"{h}h {m:02d}m {s:02d}s" + if m > 0: + return f"{m}m {s:02d}s" + return f"{s}s" + def _print_progress(self): + # --- compute and store estimated remaining time ----------------------- + start_str = self.progress.get("tomo_start_time") + projection = self.progress["projection"] + total = self.progress["total_projections"] + if start_str is not None and total > 0 and projection > 0: + elapsed = ( + datetime.datetime.now() - datetime.datetime.fromisoformat(start_str) + ).total_seconds() + rate = projection / elapsed # projections per second + remaining_s = (total - projection) / rate + self.progress["estimated_remaining_time"] = remaining_s + eta_str = self._format_duration(remaining_s) + else: + eta_str = "N/A" + # ---------------------------------------------------------------------- print("\x1b[95mProgress report:") print(f"Tomo type: ....................... {self.progress['tomo_type']}") print(f"Projection: ...................... {self.progress['projection']:.0f}") print(f"Total projections expected ....... {self.progress['total_projections']}") print(f"Angle: ........................... {self.progress['angle']}") print(f"Current subtomo: ................. {self.progress['subtomo']}") - print(f"Current projection within subtomo: {self.progress['subtomo_projection']}\x1b[0m") + print(f"Current projection within subtomo: {self.progress['subtomo_projection']}") + print(f"Estimated remaining time: ........ {eta_str}\x1b[0m") self._flomnigui_update_progress() def add_sample_database( diff --git a/csaxs_bec/bec_ipython_client/plugins/flomni/gui_tools.py b/csaxs_bec/bec_ipython_client/plugins/flomni/gui_tools.py index 329a4d3..5d03537 100644 --- a/csaxs_bec/bec_ipython_client/plugins/flomni/gui_tools.py +++ b/csaxs_bec/bec_ipython_client/plugins/flomni/gui_tools.py @@ -243,6 +243,31 @@ class flomniGuiTools: main_progress_ring.set_value(progress) subtomo_progress_ring.set_value(subtomo_progress) + # --- format start time for display -------------------------------- + start_str = self.progress.get("tomo_start_time") + if start_str is not None: + import datetime as _dt + start_display = _dt.datetime.fromisoformat(start_str).strftime("%Y-%m-%d %H:%M:%S") + else: + start_display = "N/A" + + # --- format estimated remaining time ------------------------------ + remaining_s = self.progress.get("estimated_remaining_time") + if remaining_s is not None and remaining_s >= 0: + import datetime as _dt + remaining_s = int(remaining_s) + h, rem = divmod(remaining_s, 3600) + m, s = divmod(rem, 60) + if h > 0: + eta_display = f"{h}h {m:02d}m {s:02d}s" + elif m > 0: + eta_display = f"{m}m {s:02d}s" + else: + eta_display = f"{s}s" + else: + eta_display = "N/A" + # ------------------------------------------------------------------ + text = ( f"Progress report:\n" f" Tomo type: {self.progress['tomo_type']}\n" @@ -251,7 +276,9 @@ class flomniGuiTools: f" Angle: {self.progress['angle']:.1f}\n" f" Current subtomo: {self.progress['subtomo']}\n" f" Current projection within subtomo: {self.progress['subtomo_projection']}\n" - f" Total projections per subtomo: {int(self.progress['subtomo_total_projections'])}" + f" Total projections per subtomo: {int(self.progress['subtomo_total_projections'])}\n" + f" Scan started: {start_display}\n" + f" Est. remaining: {eta_display}" ) self.progressbar.set_center_label(text)