diff --git a/graphs/data/fitHeat/calc.py b/graphs/data/fitHeat/calc.py index 305ac8f60..d5f234682 100644 --- a/graphs/data/fitHeat/calc.py +++ b/graphs/data/fitHeat/calc.py @@ -121,7 +121,12 @@ def _get_cycle_time_s(mod): return avg_time_ms / 1000.0 -def get_first_burnout_samples(fit, rack_slot, max_time_s, iterations): +def has_burnout_samples(fit, rack_slot, max_time_s, iterations): + cache_key = (getattr(fit, "ID", None), int(rack_slot), max_time_s, iterations) + return cache_key in _burnout_samples_cache + + +def get_first_burnout_samples(fit, rack_slot, max_time_s, iterations, progress_cb=None): """ Monte Carlo simulation of time until the first module in the given rack burns out. Returns a list of burnout times (seconds). If no burnout happens before max_time_s, @@ -178,7 +183,7 @@ def get_first_burnout_samples(fit, rack_slot, max_time_s, iterations): samples = [] - for _ in range(iterations): + for i in range(iterations): hp = list(base_hp) dead = [hp_val <= 0 for hp_val in hp] online_counts = dict(base_online_counts) @@ -280,6 +285,11 @@ def get_first_burnout_samples(fit, rack_slot, max_time_s, iterations): samples.append(sample_time) + if progress_cb is not None: + # progress_cb should return True to continue, False to cancel + if not progress_cb(i + 1): + break + _burnout_samples_cache[cache_key] = samples return samples diff --git a/graphs/data/fitHeat/getter.py b/graphs/data/fitHeat/getter.py index 7d68ff6d4..9bd8a2789 100644 --- a/graphs/data/fitHeat/getter.py +++ b/graphs/data/fitHeat/getter.py @@ -20,7 +20,8 @@ from eos.const import FittingSlot from graphs.data.base import SmoothPointGetter -from .calc import get_first_burnout_samples, get_rack_heat_value +import wx +from .calc import get_first_burnout_samples, get_rack_heat_value, has_burnout_samples class _BaseTime2RackHeatGetter(SmoothPointGetter): @@ -64,9 +65,41 @@ class _BaseTime2BurnoutCdfGetter(SmoothPointGetter): iterations = self._iterations if iterations <= 0: iterations = self._iterations - samples = get_first_burnout_samples( - fit=fit, rack_slot=self.rack_slot, max_time_s=max_sim_time, iterations=iterations - ) + samples = None + # Show a progress dialog only on cache miss for expensive runs + if iterations >= 1000 and not has_burnout_samples(fit, self.rack_slot, max_sim_time, iterations): + app = wx.GetApp() + parent = app.GetTopWindow() if app is not None else None + dlg = wx.ProgressDialog( + "Computing burnout CDF", + "Running overheating simulations...", + maximum=iterations, + parent=parent, + style=wx.PD_APP_MODAL | wx.PD_ELAPSED_TIME | wx.PD_AUTO_HIDE, + ) + + def progress_cb(done): + # dlg.Update returns (continue, skip) + cont, _ = dlg.Update(done) + return cont + + try: + samples = get_first_burnout_samples( + fit=fit, + rack_slot=self.rack_slot, + max_time_s=max_sim_time, + iterations=iterations, + progress_cb=progress_cb, + ) + finally: + dlg.Destroy() + else: + samples = get_first_burnout_samples( + fit=fit, + rack_slot=self.rack_slot, + max_time_s=max_sim_time, + iterations=iterations, + ) xs = [] ys = [] if not samples: @@ -98,7 +131,10 @@ class _BaseTime2BurnoutCdfGetter(SmoothPointGetter): if iterations <= 0: iterations = self._iterations samples = get_first_burnout_samples( - fit=fit, rack_slot=self.rack_slot, max_time_s=max_sim_time, iterations=iterations + fit=fit, + rack_slot=self.rack_slot, + max_time_s=max_sim_time, + iterations=iterations, ) if not samples: return 0.0