dc_ops_env / rendering /dashboard.py
Melikshah's picture
Upload folder using huggingface_hub
aedaf74 verified
# Copyright (c) Meta Platforms, Inc. and affiliates.
# All rights reserved.
#
# This source code is licensed under the BSD-style license found in the
# LICENSE file in the root directory of this source tree.
"""
Renders simulation state into a text-based monitoring dashboard.
The dashboard mimics what a real datacenter operator would see on their
NOC (Network Operations Center) screens. It is the primary observation
for the LLM agent.
"""
from __future__ import annotations
from ..config import ASHRAE_CLASSES, m3s_to_cfm
from ..simulation.types import (
CRACFaultType,
CRACState,
CRACStatus,
DatacenterState,
PowerState,
ZoneState,
)
def render_dashboard(
state: DatacenterState,
*,
alert: str = "",
step: int = 0,
max_steps: int = 15,
scenario_type: str = "",
) -> str:
"""Render the full monitoring dashboard as a text string.
Args:
state: Current datacenter simulation state.
alert: Active alert message to display prominently.
step: Current step number in the episode.
max_steps: Maximum steps in the episode.
scenario_type: Type of scenario being run.
Returns:
Multi-line string formatted as a monitoring dashboard.
"""
w = 68 # Inner width of the dashboard frame
lines: list[str] = []
def hline(char: str = "═") -> str:
return f"╠{char * w}╣"
def row(text: str) -> str:
return f"║ {text:<{w - 2}} ║"
# Header
lines.append(f"╔{'═' * w}╗")
title = "DC-OPS MONITORING DASHBOARD"
lines.append(f"║{title:^{w}}║")
sim_min = state.sim_time_s / 60.0
status_line = f"Sim Time: {sim_min:.1f} min Step: {step}/{max_steps}"
if scenario_type:
status_line += f" [{scenario_type}]"
lines.append(row(status_line))
# Alert section
if alert:
lines.append(hline())
# Split long alerts across lines
alert_prefix = "!! ALERT: "
remaining = w - 2 - len(alert_prefix)
if len(alert) <= remaining:
lines.append(row(f"{alert_prefix}{alert}"))
else:
lines.append(row(f"{alert_prefix}{alert[:remaining]}"))
# Continuation lines
for i in range(remaining, len(alert), w - 4):
lines.append(row(f" {alert[i:i + w - 4]}"))
# Cooling Units
lines.append(hline())
lines.append(row("COOLING UNITS"))
lines.append(row(f"{'Unit':<10} {'Status':<12} {'Setpoint':>8} {'Supply':>8} {'Fan%':>5} {'CFM':>7} {'kW':>6}"))
lines.append(row("-" * (w - 2)))
for zone in state.zones:
for crac in zone.crac_units:
lines.append(row(_format_crac_row(crac, state.outside_temp_c, zone.hot_aisle_temp_c)))
# Zone Temperatures
lines.append(hline())
lines.append(row("ZONE TEMPERATURES"))
lines.append(row(f"{'Zone':<8} {'Cold Aisle':>10} {'Hot Aisle':>10} {'Max Inlet':>10} {'IT Load':>8} {'Class':>6}"))
lines.append(row("-" * (w - 2)))
for zone in state.zones:
lines.append(row(_format_zone_row(zone)))
# Rack Detail (per zone, show max-temp racks)
lines.append(hline())
lines.append(row("RACK TEMPERATURES (top 5 hottest)"))
lines.append(row(f"{'Rack':<8} {'Inlet':>8} {'Outlet':>8} {'Load kW':>8} {'CFM':>7}"))
lines.append(row("-" * (w - 2)))
# Collect all racks, sort by inlet temp descending
all_racks = []
for zone in state.zones:
all_racks.extend(zone.racks)
all_racks.sort(key=lambda r: r.inlet_temp_c, reverse=True)
for rack in all_racks[:5]:
cfm = m3s_to_cfm(rack.airflow_m3s)
lines.append(row(
f"{rack.rack_id:<8} {rack.inlet_temp_c:>7.1f}°C {rack.outlet_temp_c:>7.1f}°C "
f"{rack.it_load_kw:>7.1f} {cfm:>7.0f}"
))
# Power Section
lines.append(hline())
lines.append(row("POWER"))
p_it = state.total_it_load_kw
p_cooling = state.total_cooling_power_kw
pue = state.pue
lines.append(row(
f"IT Load: {p_it:.1f} kW | Cooling: {p_cooling:.1f} kW | PUE: {pue:.2f}"
))
if state.power is not None:
lines.append(row(_format_power_section(state.power)))
lines.append(row(_format_ups_summary(state.power)))
else:
lines.append(row("UPS: N/A | Generator: N/A"))
# Environment
lines.append(hline())
lines.append(row("ENVIRONMENT"))
lines.append(row(
f"Outside: {state.outside_temp_c:.1f}°C | "
f"Humidity: {state.outside_humidity_rh * 100:.0f}% RH"
))
# Footer
lines.append(f"╚{'═' * w}╝")
return "\n".join(lines)
def _format_crac_row(crac: CRACState, outside_temp_c: float, hot_aisle_temp_c: float) -> str:
"""Format a single CRAC row for the dashboard."""
# Status display
if crac.status == CRACStatus.FAULT:
fault_label = crac.fault_type.value.upper() if crac.fault_type != CRACFaultType.NONE else "FAULT"
status_str = f"!! {fault_label}"
elif crac.status == CRACStatus.MAINTENANCE:
status_str = "MAINT"
elif crac.status == CRACStatus.STANDBY:
status_str = "STANDBY"
else:
status_str = "RUNNING"
# Supply temp display
if crac.status != CRACStatus.RUNNING:
supply_str = "---"
else:
supply_str = f"{crac.supply_temp_c:.1f}°C"
# CFM
cfm = m3s_to_cfm(crac.current_airflow_m3s)
# Power consumption
q_cool = crac.compute_cooling_output_kw(hot_aisle_temp_c)
p_kw = crac.compute_power_consumption_kw(q_cool, outside_temp_c)
return (
f"{crac.unit_id:<10} {status_str:<12} {crac.setpoint_c:>7.1f}°C "
f"{supply_str:>8} {crac.fan_speed_pct:>5.0f} {cfm:>7.0f} {p_kw:>6.1f}"
)
def _format_zone_row(zone: ZoneState) -> str:
"""Format a single zone row for the dashboard."""
ashrae = ASHRAE_CLASSES.get(zone.ashrae_class)
max_inlet = zone.max_inlet_temp_c
# Mark if exceeding ASHRAE recommended
inlet_marker = ""
if ashrae and max_inlet > ashrae.recommended_max_c:
inlet_marker = "*"
if ashrae and max_inlet > ashrae.allowable_max_c:
inlet_marker = "!!"
return (
f"{zone.zone_id:<8} {zone.cold_aisle_temp_c:>9.1f}°C "
f"{zone.hot_aisle_temp_c:>9.1f}°C {max_inlet:>8.1f}°C{inlet_marker:<2}"
f"{zone.total_it_load_kw:>7.1f} {zone.ashrae_class:>6}"
)
def _format_power_section(power: PowerState) -> str:
"""Format power source status line."""
parts: list[str] = []
# Utility / generator status
if power.utility_available:
parts.append("Utility: NORMAL")
else:
parts.append("Utility: DOWN")
from ..simulation.types import GeneratorState as GS
gen = power.generator
if gen.state == GS.OFF:
parts.append("Gen: OFF")
elif gen.state == GS.LOADED:
fuel_hrs = gen.fuel_remaining_hours
fuel_str = f"{fuel_hrs:.1f}h" if fuel_hrs < 100 else ">100h"
parts.append(f"Gen: LOADED {gen.load_fraction * 100:.0f}% (fuel: {fuel_str})")
elif gen.state in (GS.START_DELAY, GS.CRANKING, GS.WARMING):
parts.append(f"Gen: STARTING ({gen.state.value})")
elif gen.state == GS.READY:
parts.append("Gen: READY")
elif gen.state == GS.COOLDOWN:
parts.append("Gen: COOLDOWN")
# ATS position
from ..simulation.types import ATSPosition
ats = power.ats
if ats.position == ATSPosition.UTILITY:
parts.append("ATS: UTILITY")
elif ats.position == ATSPosition.GENERATOR:
parts.append("ATS: GENERATOR")
elif ats.position == ATSPosition.TRANSFERRING:
parts.append("ATS: TRANSFERRING")
return " | ".join(parts)
def _format_ups_summary(power: PowerState) -> str:
"""Format UPS status summary line."""
if not power.ups_units:
return "UPS: N/A"
parts: list[str] = []
for ups in power.ups_units:
soc_pct = ups.battery_soc * 100
mode_str = ups.mode.value.upper().replace("_", " ")
load_pct = ups.load_fraction * 100
eta_pct = ups.efficiency * 100
if ups.mode.value == "on_battery":
time_str = ""
if ups.battery_time_remaining_s < float("inf"):
mins = ups.battery_time_remaining_s / 60.0
time_str = f" {mins:.0f}min"
parts.append(f"{ups.unit_id}: BATTERY {soc_pct:.0f}%{time_str}")
elif ups.mode.value == "fault":
parts.append(f"{ups.unit_id}: FAULT")
else:
parts.append(f"{ups.unit_id}: {mode_str} {load_pct:.0f}% η{eta_pct:.0f}%")
return "UPS: " + " | ".join(parts)