diff --git a/src/reactor_sim/dashboard.py b/src/reactor_sim/dashboard.py index 74706c1..f3b51e1 100644 --- a/src/reactor_sim/dashboard.py +++ b/src/reactor_sim/dashboard.py @@ -43,6 +43,7 @@ class ReactorDashboard: self.quit_requested = False self.reset_requested = False self._last_state: Optional[PlantState] = None + self._trend_history: deque[tuple[float, float, float]] = deque(maxlen=120) self.log_buffer: deque[str] = deque(maxlen=4) self._log_handler: Optional[logging.Handler] = None self._previous_handlers: list[logging.Handler] = [] @@ -331,6 +332,7 @@ class ReactorDashboard: win.erase() win.box() win.addstr(0, 2, " Plant Overview ", curses.color_pair(1) | curses.A_BOLD) + self._update_trends(state) height, width = win.getmaxyx() inner_height = height - 2 inner_width = width - 2 @@ -365,6 +367,12 @@ class ReactorDashboard: ("Reactivity", f"{state.core.reactivity_margin:+.4f}"), ], ) + left_y = self._draw_section( + left_win, + left_y, + "Trends", + self._trend_lines(), + ) left_y = self._draw_section(left_win, left_y, "Key Poisons / Emitters", self._poison_lines(state)) left_y = self._draw_section( left_win, @@ -589,6 +597,24 @@ class ReactorDashboard: ] return lines + def _update_trends(self, state: PlantState) -> None: + self._trend_history.append((state.time_elapsed, state.core.fuel_temperature, state.core.power_output_mw)) + + def _trend_lines(self) -> list[tuple[str, str]]: + if len(self._trend_history) < 2: + return [("Fuel Temp Δ", "n/a"), ("Core Power Δ", "n/a")] + start_t, start_temp, start_power = self._trend_history[0] + end_t, end_temp, end_power = self._trend_history[-1] + duration = max(1.0, end_t - start_t) + temp_delta = end_temp - start_temp + power_delta = end_power - start_power + temp_rate = temp_delta / duration + power_rate = power_delta / duration + return [ + ("Fuel Temp Δ", f"{end_temp:7.1f} K (Δ{temp_delta:+6.1f} / {duration:4.0f}s, {temp_rate:+5.2f}/s)"), + ("Core Power Δ", f"{end_power:7.1f} MW (Δ{power_delta:+6.1f} / {duration:4.0f}s, {power_rate:+5.2f}/s)"), + ] + def _draw_health_bars(self, win: "curses._CursesWindow", start_y: int) -> int: height, width = win.getmaxyx() inner_width = width - 4