| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <meta name="description" content="OpenGrid — Multi-Agent POMDP Power Grid Control Room with Safe RL"> |
| <title>OpenGrid | Control Room</title> |
| <link href="https://api.fontshare.com/v2/css?f[]=bespoke-stencil@400,700&display=swap" rel="stylesheet"> |
| <link rel="stylesheet" href="/static/style.css?v=16"> |
| <link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" /> |
| <script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script> |
| <link rel="icon" href="/static/logo.png" type="image/png"> |
| </head> |
| <body> |
|
|
| |
| <div class="loading-overlay" id="loading"> |
| <div class="loading-spinner"></div> |
| <div class="loading-text">OpenGrid — Initializing Control Room</div> |
| </div> |
|
|
| |
| <div class="alert-banner" id="alertBanner"> |
| <span id="alertText"></span> |
| <button class="dismiss" onclick="dismissAlert()">Dismiss</button> |
| </div> |
|
|
| |
| <div class="control-room"> |
|
|
| |
| <header class="header"> |
| <div class="header-brand"> |
| <img src="/static/logo.png" alt="OpenGrid" class="logo-img" style="width:32px;height:32px;border-radius:6px;"> |
| <div> |
| <h1>OpenGrid</h1> |
| <div class="sub">Multi-Agent Power Grid Control Room</div> |
| </div> |
| </div> |
|
|
| <div class="sim-badge"> |
| <span class="dot"></span> |
| <span id="simStatus">READY</span> |
| </div> |
|
|
| <div class="header-stats"> |
| <div class="header-stat"> |
| <span class="label">Episode</span> |
| <span class="value normal" id="headerEpisode">--</span> |
| </div> |
| <div class="header-stat"> |
| <span class="label">Step</span> |
| <span class="value" id="headerStep">0 / 50</span> |
| </div> |
| <div class="header-stat"> |
| <span class="label">Frequency</span> |
| <span class="value normal" id="headerFreq">50.00 Hz</span> |
| </div> |
| <div class="header-stat"> |
| <span class="label">Agents</span> |
| <span class="value normal" id="headerAgents">--</span> |
| </div> |
| <div class="header-stat"> |
| <span class="label">Team Reward</span> |
| <span class="value" id="headerReward">0.00</span> |
| </div> |
| </div> |
| </header> |
|
|
| |
| <div class="toolbar"> |
| <div class="toolbar-section"> |
| <span class="toolbar-label">PROCEDURAL</span> |
| <div class="task-group" id="proceduralTasks"> |
| |
| </div> |
| </div> |
| <div class="toolbar-divider"></div> |
| <div class="toolbar-section"> |
| <span class="toolbar-label">KARNATAKA</span> |
| <div class="task-group" id="karnatakaTasks"> |
| |
| </div> |
| </div> |
| <div class="toolbar-divider"></div> |
| <div class="toolbar-section"> |
| <span class="toolbar-label">CONTROLS</span> |
| <div class="controls-row"> |
| <button class="ctrl-btn accent" id="btnReset" onclick="resetEpisode()">⟳ Reset</button> |
| <button class="ctrl-btn" id="btnStep" onclick="stepEpisode()">▶ Step</button> |
| <button class="ctrl-btn" id="btnAutoRun" onclick="toggleAutoRun()">⏩ Auto</button> |
| <button class="ctrl-btn" onclick="getGrade()">★ Grade</button> |
| </div> |
| </div> |
| <div class="toolbar-divider"></div> |
| <div class="toolbar-section"> |
| <span class="toolbar-label">SCORE</span> |
| <div class="toolbar-score" id="episodeScore">--</div> |
| </div> |
| <div class="toolbar-section" style="margin-left:auto;"> |
| <span class="toolbar-label">STEPS</span> |
| <span class="toolbar-value" id="totalSteps">0</span> |
| </div> |
| <div class="toolbar-section"> |
| <span class="toolbar-label">BLACKOUT</span> |
| <span class="toolbar-value" id="blackoutStatus" style="color: var(--status-normal);">No</span> |
| </div> |
| </div> |
|
|
| |
| <aside class="left-panel"> |
|
|
| |
| <div class="card freq-card"> |
| <div class="card-title"> |
| <span>Grid Frequency</span> |
| <span class="freq-nominal-tag">Nom 50.00 Hz</span> |
| </div> |
| <div class="freq-display"> |
| <div class="freq-arc-container" id="freqArc"></div> |
| <div class="freq-readout"> |
| <div class="freq-value-big" id="freqValueBig">50.00</div> |
| <div class="freq-unit">Hz</div> |
| </div> |
| <div class="freq-delta-row"> |
| <div class="freq-delta-chip" id="freqDeltaChip"> |
| <span class="freq-delta-arrow" id="freqDeltaArrow">●</span> |
| <span id="freqDeltaText">Δ 0.000 Hz</span> |
| </div> |
| <div class="grid-condition normal" id="gridCondition"> |
| <span class="cond-dot"></span> |
| <span id="gridConditionLabel">NORMAL</span> |
| </div> |
| </div> |
| </div> |
| </div> |
|
|
| |
| <div class="card"> |
| <div class="card-title">System Summary</div> |
| <div class="stat-row highlight"> |
| <span class="label">Total Generation</span> |
| <span class="value" id="totalGen">-- MW</span> |
| </div> |
| <div class="stat-row"> |
| <span class="label">Total Load</span> |
| <span class="value" id="totalLoad">-- MW</span> |
| </div> |
| <div class="stat-row"> |
| <span class="label">Net Balance</span> |
| <span class="value" id="netBalance">-- MW</span> |
| </div> |
| <div class="stat-row"> |
| <span class="label">Lines Connected</span> |
| <span class="value" id="linesConnected">--</span> |
| </div> |
| <div class="stat-row"> |
| <span class="label">Lines Overloaded</span> |
| <span class="value" id="linesOverloaded" style="color: var(--status-normal);">0</span> |
| </div> |
| </div> |
|
|
| |
| <div class="card"> |
| <div class="card-title">Oversight Agent</div> |
| <div class="coord-score"> |
| <div class="big-value" id="coordScore" style="color: var(--status-normal);">1.00</div> |
| <div style="font-size:10px; color: var(--text-secondary); margin-top:4px;">Coordination Score</div> |
| </div> |
| <div class="stat-row"> |
| <span class="label">Conflicts</span> |
| <span class="value" id="conflicts">0</span> |
| </div> |
| <div class="stat-row"> |
| <span class="label">Safety Corrections</span> |
| <span class="value" id="safetyCorrTotal">0</span> |
| </div> |
| <div class="stat-row"> |
| <span class="label">Selfish Actions</span> |
| <span class="value" id="selfishActions">0</span> |
| </div> |
| </div> |
|
|
| |
| <div class="card" style="flex:1; display:flex; flex-direction:column; overflow:hidden;"> |
| <div class="card-title" style="color: var(--status-warning);">Exception Log</div> |
| <div class="alarm-log" id="alarmLog"></div> |
| </div> |
|
|
| </aside> |
|
|
| |
| <main class="center-panel" id="centerPanel"> |
| <div class="grid-map" id="gridMap"></div> |
| |
| <div class="map-legend"> |
| <div class="legend-title">Legend</div> |
| <div class="legend-item"><span class="legend-dot" style="background:#00e5a0;"></span> Slack</div> |
| <div class="legend-item"><span class="legend-dot" style="background:#f5a623;"></span> Generator</div> |
| <div class="legend-item"><span class="legend-dot" style="background:#e94560;"></span> Load</div> |
| <div class="legend-item"><span class="legend-dot" style="background:#000000;"></span> Battery</div> |
| <div class="legend-item"><span class="legend-dot" style="background:#ffeb3b;"></span> Solar</div> |
| <div class="legend-item"><span class="legend-dot" style="background:#64ffda;"></span> Wind</div> |
| <div class="legend-line"><span class="legend-line-sample normal"></span> Normal</div> |
| <div class="legend-line"><span class="legend-line-sample warn"></span> Congested</div> |
| <div class="legend-line"><span class="legend-line-sample crit"></span> Overloaded</div> |
| <div id="agentLegendContainer" style="margin-top: 8px; border-top: 1px solid rgba(255,255,255,0.1); padding-top: 8px;"></div> |
| <div style="margin-top: 8px; font-size: 8px; color: var(--text-muted); font-style: italic;"> |
| * Scroll to zoom for a clearer view |
| </div> |
| </div> |
| <div class="bus-tooltip" id="busTooltip"> |
| <div class="tt-title" id="ttTitle">Bus 0</div> |
| <div class="tt-row"><span>Type</span><span class="tt-val" id="ttType">--</span></div> |
| <div class="tt-row"><span>Injection</span><span class="tt-val" id="ttInj">-- MW</span></div> |
| <div class="tt-row"><span>Zone</span><span class="tt-val" id="ttZone">--</span></div> |
| </div> |
| </main> |
|
|
| |
| <aside class="right-panel"> |
| <div class="card"> |
| <div class="card-title">Agent Leaderboard</div> |
| <ul class="leaderboard" id="leaderboard"></ul> |
| </div> |
| <div id="agentCards"></div> |
| </aside> |
|
|
| |
| <footer class="bottom-panel"> |
| <div class="bottom-card"> |
| <div class="card-title">Reward History</div> |
| <div class="chart-area" id="rewardChart"></div> |
| </div> |
| <div class="bottom-card"> |
| <div class="card-title">Frequency Trend</div> |
| <div class="chart-area" id="freqChart"></div> |
| </div> |
| <div class="bottom-card"> |
| <div class="card-title">Generation Mix</div> |
| <div class="chart-area" id="genMixChart"></div> |
| </div> |
| </footer> |
|
|
| </div> |
|
|
| <script src="/static/app.js?v=20"></script> |
| </body> |
| </html> |
|
|