|
@@ -3,7 +3,7 @@
|
|
|
<!-- Toggle button — always visible, also a drag handle when panel is collapsed -->
|
|
<!-- Toggle button — always visible, also a drag handle when panel is collapsed -->
|
|
|
<button class="hud-toggle" cdkDragHandle (click)="toggle()" [title]="collapsed() ? 'Expand HUD' : 'Collapse HUD'">
|
|
<button class="hud-toggle" cdkDragHandle (click)="toggle()" [title]="collapsed() ? 'Expand HUD' : 'Collapse HUD'">
|
|
|
<span class="hud-toggle-icon">{{ collapsed() ? '📊' : '✕' }}</span>
|
|
<span class="hud-toggle-icon">{{ collapsed() ? '📊' : '✕' }}</span>
|
|
|
- <span class="hud-status-dot" [class.connected]="surveillance.connected()"></span>
|
|
|
|
|
|
|
+ <span class="hud-status-dot" [class.connected]="visionSocket.connected()"></span>
|
|
|
</button>
|
|
</button>
|
|
|
|
|
|
|
|
<!-- Expanded panel -->
|
|
<!-- Expanded panel -->
|
|
@@ -12,64 +12,95 @@
|
|
|
<div class="hud-header" cdkDragHandle>
|
|
<div class="hud-header" cdkDragHandle>
|
|
|
<span class="hud-drag-grip">⠿</span>
|
|
<span class="hud-drag-grip">⠿</span>
|
|
|
<span class="hud-title">SYSTEM MONITOR</span>
|
|
<span class="hud-title">SYSTEM MONITOR</span>
|
|
|
- <!-- Status LEDs — Nest + n8n always visible in the header -->
|
|
|
|
|
<div class="hud-header-leds">
|
|
<div class="hud-header-leds">
|
|
|
- <span class="hud-led-group" title="NestJS Socket: {{ surveillance.connected() ? 'Connected' : 'Offline' }}">
|
|
|
|
|
|
|
+ <span class="hud-led-group" title="NestJS Socket: {{ visionSocket.connected() ? 'Connected' : 'Offline' }}">
|
|
|
<span class="hud-led-dot"
|
|
<span class="hud-led-dot"
|
|
|
- [class.led-green]="surveillance.connected()"
|
|
|
|
|
- [class.led-red]="!surveillance.connected()">
|
|
|
|
|
|
|
+ [class.led-green]="visionSocket.connected()"
|
|
|
|
|
+ [class.led-red]="!visionSocket.connected()">
|
|
|
</span>
|
|
</span>
|
|
|
<span class="hud-led-label">Nest</span>
|
|
<span class="hud-led-label">Nest</span>
|
|
|
</span>
|
|
</span>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
- @if (!surveillance.connected()) {
|
|
|
|
|
|
|
+ @if (!visionSocket.connected()) {
|
|
|
<div class="hud-waiting hud-offline">
|
|
<div class="hud-waiting hud-offline">
|
|
|
<span class="hud-offline-icon">⚠</span>
|
|
<span class="hud-offline-icon">⚠</span>
|
|
|
<span>Backend Offline</span>
|
|
<span>Backend Offline</span>
|
|
|
</div>
|
|
</div>
|
|
|
- } @else if (surveillance.metrics().length === 0) {
|
|
|
|
|
|
|
+ } @else if (!metrics()) {
|
|
|
<div class="hud-waiting">
|
|
<div class="hud-waiting">
|
|
|
- <span>No processes detected</span>
|
|
|
|
|
|
|
+ <span>Waiting for metrics...</span>
|
|
|
</div>
|
|
</div>
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- @for (metric of surveillance.metrics(); track trackByService($index, metric)) {
|
|
|
|
|
- <div class="hud-row">
|
|
|
|
|
- <div class="hud-service-name">{{ metric.service }}</div>
|
|
|
|
|
|
|
+ } @else {
|
|
|
|
|
|
|
|
|
|
+ <!-- Global PC stats -->
|
|
|
|
|
+ <div class="hud-row hud-global">
|
|
|
|
|
+ <div class="hud-service-name">Total CPU</div>
|
|
|
<div class="hud-metric-block">
|
|
<div class="hud-metric-block">
|
|
|
- <div class="hud-metric-label">CPU</div>
|
|
|
|
|
<div class="hud-bar-track">
|
|
<div class="hud-bar-track">
|
|
|
<div
|
|
<div
|
|
|
class="hud-bar-fill"
|
|
class="hud-bar-fill"
|
|
|
- [style.width]="cpuBarWidth(metric.cpu)"
|
|
|
|
|
- [style.background]="cpuColor(metric.cpu)">
|
|
|
|
|
|
|
+ [style.width]="cpuBarWidth(metrics()!.cpuLoad)"
|
|
|
|
|
+ [style.background]="cpuColor(metrics()!.cpuLoad)">
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
- <div class="hud-metric-value">{{ metric.cpu.toFixed(1) }}%</div>
|
|
|
|
|
|
|
+ <div class="hud-metric-value">{{ metrics()!.cpuLoad.toFixed(1) }}%</div>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
+ </div>
|
|
|
|
|
|
|
|
|
|
+ <div class="hud-row hud-global">
|
|
|
|
|
+ <div class="hud-service-name">Total RAM</div>
|
|
|
<div class="hud-metric-block">
|
|
<div class="hud-metric-block">
|
|
|
- <div class="hud-metric-label">RAM</div>
|
|
|
|
|
- <div class="hud-ram-value">{{ formatBytes(metric.memory) }}</div>
|
|
|
|
|
|
|
+ <div class="hud-bar-track">
|
|
|
|
|
+ <div
|
|
|
|
|
+ class="hud-bar-fill"
|
|
|
|
|
+ [style.width]="cpuBarWidth((metrics()!.memUsed / metrics()!.memTotal) * 100)"
|
|
|
|
|
+ [style.background]="cpuColor((metrics()!.memUsed / metrics()!.memTotal) * 100)">
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="hud-metric-value">{{ formatBytes(metrics()!.memUsed) }} / {{ formatBytes(metrics()!.memTotal) }}</div>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- Per-service rows -->
|
|
|
|
|
+ @for (svc of metrics()!.services; track trackByService($index, svc)) {
|
|
|
|
|
+ <div class="hud-row">
|
|
|
|
|
+ <div class="hud-service-name">{{ svc.service }}</div>
|
|
|
|
|
+
|
|
|
|
|
+ <div class="hud-metric-block">
|
|
|
|
|
+ <div class="hud-metric-label">CPU</div>
|
|
|
|
|
+ <div class="hud-bar-track">
|
|
|
|
|
+ <div
|
|
|
|
|
+ class="hud-bar-fill"
|
|
|
|
|
+ [style.width]="cpuBarWidth(svc.cpu)"
|
|
|
|
|
+ [style.background]="cpuColor(svc.cpu)">
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="hud-metric-value">{{ svc.cpu.toFixed(1) }}%</div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <div class="hud-metric-block">
|
|
|
|
|
+ <div class="hud-metric-label">RAM</div>
|
|
|
|
|
+ <div class="hud-ram-value">{{ svc.online ? formatBytes(svc.memory) : '—' }}</div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- <!-- Service status indicators -->
|
|
|
|
|
|
|
+ <!-- Socket connection status -->
|
|
|
<div class="hud-service-status">
|
|
<div class="hud-service-status">
|
|
|
<div class="hud-svc-row">
|
|
<div class="hud-svc-row">
|
|
|
<span class="hud-svc-label">NestJS</span>
|
|
<span class="hud-svc-label">NestJS</span>
|
|
|
<span class="hud-svc-dot"
|
|
<span class="hud-svc-dot"
|
|
|
- [class.dot-green]="surveillance.connected()"
|
|
|
|
|
- [class.dot-red]="!surveillance.connected()">
|
|
|
|
|
|
|
+ [class.dot-green]="visionSocket.connected()"
|
|
|
|
|
+ [class.dot-red]="!visionSocket.connected()">
|
|
|
</span>
|
|
</span>
|
|
|
<span class="hud-svc-state"
|
|
<span class="hud-svc-state"
|
|
|
- [class.state-online]="surveillance.connected()"
|
|
|
|
|
- [class.state-offline]="!surveillance.connected()">
|
|
|
|
|
- {{ surveillance.connected() ? 'Connected' : 'Offline — Socket features disabled' }}
|
|
|
|
|
|
|
+ [class.state-online]="visionSocket.connected()"
|
|
|
|
|
+ [class.state-offline]="!visionSocket.connected()">
|
|
|
|
|
+ {{ visionSocket.connected() ? 'Connected' : 'Offline — Socket features disabled' }}
|
|
|
</span>
|
|
</span>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|