Rebranded to console tester.

This commit is contained in:
2025-12-23 11:21:02 -05:00
parent a0403eef57
commit 7d8c835fee
7 changed files with 309 additions and 207 deletions

View File

@@ -1,11 +1,12 @@
# HDMI Tester Enterprise # HDMI Console Diagnostics (Enterprise)
A high-performance orchestration and telemetry platform designed for real-time monitoring and lifecycle management of HDMI testing hardware. This platform provides a "single pane of glass" view for large fleets of test nodes, offering sub-ms edge latency tracking and remote hardware orchestration. A high-performance orchestration platform designed for real-time monitoring and repair diagnostics of **Console HDMI Ports** (PS5, Xbox, Switch). This platform provides a "single pane of glass" view for large fleets of test nodes, offering sub-ms edge latency tracking and remote hardware orchestration for front-desk and technical staff.
## 🚀 Key Features ## 🚀 Key Features
- **Operational Telemetry**: Real-time monitoring of active nodes, test results, and system health. - **Port Telemetry**: Real-time monitoring of active nodes, port diagnostic results, and system health.
- **Node Insights**: Tactical spectral analysis of HDMI bus metrics including DDC, CEC, and TMDS voltage verification. - **Port Insights**: Tactical spectral analysis of HDMI pins (DDC, CEC, TMDS) for board-level repair verification.
- **Retail & Enterprise Views**: Simplified "Good/Bad" UI for front-desk staff and detailed telemetry for technicians.
- **Command Center**: Enterprise-grade fleet management for device enrollment and environment orchestration. - **Command Center**: Enterprise-grade fleet management for device enrollment and environment orchestration.
- **Device Configuration**: - **Device Configuration**:
- Remote renaming and identification. - Remote renaming and identification.
@@ -15,6 +16,7 @@ A high-performance orchestration and telemetry platform designed for real-time m
- **Premium UI/UX**: - **Premium UI/UX**:
- High-contrast Light and Dark modes. - High-contrast Light and Dark modes.
- Safari-optimized rendering and backdrop filters. - Safari-optimized rendering and backdrop filters.
- **Virtual Hardware Simulator**: A web-based tactile OLED emulator for immediate testing.
- Fluid micro-animations and responsive layouts. - Fluid micro-animations and responsive layouts.
## 🛠 Tech Stack ## 🛠 Tech Stack
@@ -53,8 +55,13 @@ Open [http://localhost:3000](http://localhost:3000) to access the dashboard.
## 🤖 Hardware Simulation ## 🤖 Hardware Simulation
To test the platform without physical hardware, use the provided simulator. It mimics a head unit with active mesh capabilities. To test the platform without physical hardware, you can use either the interactive web emulator or the Python-based CLI simulator.
### Web Simulator (Recommended)
Access the tactile OLED emulator directly in your browser: [http://localhost:3000/simulator](http://localhost:3000/simulator)
- **Features**: Real-time OLED display, keyboard shortcuts (R/T/S/B), and live system logs.
### Python CLI Simulator
```bash ```bash
# Register a simulated device # Register a simulated device
python3 firmware/simulator.py register python3 firmware/simulator.py register

View File

@@ -5,7 +5,7 @@ import requests
import uuid import uuid
import socket import socket
class HeadUnit: class PortTester:
def __init__(self, server_url="http://localhost:3000", config_path="firmware/config/device.json"): def __init__(self, server_url="http://localhost:3000", config_path="firmware/config/device.json"):
self.server_url = server_url self.server_url = server_url
self.config_path = config_path self.config_path = config_path
@@ -109,42 +109,39 @@ class HeadUnit:
self.save_config() self.save_config()
print(f"[*] Switched to Slot {self.active_slot}.") print(f"[*] Switched to Slot {self.active_slot}.")
def generate_mock_hdmi_test(): def generate_console_port_test():
"""Generates mock spectral data for a console HDMI port."""
pins = ["DDC_SCL", "DDC_SDA", "CEC", "HPD", "TMDS_CLK+", "TMDS_CLK-"]
results = {pin: round(0.400 + (random.random() * 0.2), 3) for pin in pins}
return { return {
"type": "HDMI_OFFLINE", "type": "PORT_DIAGNOSTIC",
"status": "PASS", "status": "PASS",
"hdmi5v": False, "hdmi5v": True,
"summary": "Offline diode check completed. All lines within tolerance.", "summary": "Port handshake confirmed. Pin continuity within spec.",
"diodeResults": { "diodeResults": results
"DDC_SCL": 0.542,
"DDC_SDA": 0.544,
"CEC": 0.612,
"HPD": 0.589,
"TMDS_CLK+": 0.498,
"TMDS_CLK-": 0.497
}
} }
if __name__ == "__main__": if __name__ == "__main__":
hu = HeadUnit() hu = PortTester()
def print_menu(): def print_menu():
print("\n" + "="*45) print("\n" + "="*45)
print(f" HDMI TESTER - HARDWARE SIMULATOR (v1.2.0)") print(f" HDMI PORT DIAGNOSTICS - HARDWARE SIMULATOR")
print("="*45) print("="*45)
print(f" Status: [{hu.status}]") print(f" Status: [{hu.status}]")
print(f" Mode: [{hu.mode}]") print(f" Mode: [{hu.mode}]")
print(f" Active: [Slot {hu.active_slot}]") print(f" Active: [Slot {hu.active_slot}]")
print(f" Serial: [{hu.serial_number}]") print(f" Node: [{hu.serial_number}]")
print("-" * 45) print("-" * 45)
print(" 1. Register / Sync Device") print(" 1. Register / Sync Port Diagnostics")
print(" 2. Report Mock HDMI Test") print(" 2. Run Console Port Sweep")
print(" 3. Switch to DEV Mode") print(" 3. Switch to DEV Mode")
print(" 4. Switch to PROD Mode") print(" 4. Switch to PROD Mode")
print(" 5. Swap Active Slot (A/B)") print(" 5. Swap Active Slot (A/B)")
print(" 6. Start Automated Loop") print(" 6. Start Automated Loop")
print(" 7. Request Re-enlistment") print(" 7. Request Re-enlistment")
print(" 0. Exit Orchestrator") print(" 0. Power Down Node")
print("-" * 45) print("-" * 45)
while True: while True:
@@ -154,7 +151,7 @@ if __name__ == "__main__":
if choice == "1": if choice == "1":
hu.register() hu.register()
elif choice == "2": elif choice == "2":
hu.report_test(generate_mock_hdmi_test()) hu.report_test(generate_console_port_test())
elif choice == "3": elif choice == "3":
hu.switch_mode("DEV") hu.switch_mode("DEV")
elif choice == "4": elif choice == "4":
@@ -171,7 +168,7 @@ if __name__ == "__main__":
break break
if hu.status == "ENROLLED": if hu.status == "ENROLLED":
hu.report_test(generate_mock_hdmi_test()) hu.report_test(generate_console_port_test())
time.sleep(5) time.sleep(5)
if hu.status == "OFFLINE": if hu.status == "OFFLINE":

Binary file not shown.

View File

@@ -1,7 +1,7 @@
"use client"; "use client";
import { useState, useEffect } from "react"; import { useState, useEffect } from "react";
import { Shield, ArrowRight, Settings2, PlusCircle, CheckCircle2, XCircle, Clock, ChevronRight, RefreshCw } from "lucide-react"; import { Shield, ArrowRight, Settings2, PlusCircle, CheckCircle2, XCircle, Clock, ChevronRight, RefreshCw, Activity } from "lucide-react";
import Link from "next/link"; import Link from "next/link";
interface Device { interface Device {
@@ -73,11 +73,25 @@ export default function AdminPage() {
</h1> </h1>
<p className="text-secondary font-medium text-sm tracking-wide">Manage device fleet, enrollment, and firmware orchestration.</p> <p className="text-secondary font-medium text-sm tracking-wide">Manage device fleet, enrollment, and firmware orchestration.</p>
</div> </div>
<div className="flex items-center gap-4">
<Link
href="/simulator"
className="group flex items-center gap-3 bg-secondary/5 border border-border px-5 py-2.5 rounded-2xl hover:bg-sky-500/10 hover:border-sky-500/30 transition-all active:scale-95"
>
<div className="w-8 h-8 rounded-lg bg-sky-500/10 flex items-center justify-center text-sky-500 group-hover:bg-sky-500 group-hover:text-white transition-all">
<Activity className="w-4 h-4" />
</div>
<div className="text-left">
<p className="text-[10px] font-black uppercase tracking-widest text-foreground">Launch Virtual Node</p>
<p className="text-[8px] font-bold text-secondary uppercase tracking-tight">OLED Hardware Emulator</p>
</div>
</Link>
<button className="bg-sky-500 hover:bg-sky-400 text-white px-6 py-2.5 rounded-2xl text-[10px] font-black uppercase tracking-widest flex items-center gap-3 transition-all shadow-[0_0_25px_rgba(56,189,248,0.2)] hover:scale-105 active:scale-95"> <button className="bg-sky-500 hover:bg-sky-400 text-white px-6 py-2.5 rounded-2xl text-[10px] font-black uppercase tracking-widest flex items-center gap-3 transition-all shadow-[0_0_25px_rgba(56,189,248,0.2)] hover:scale-105 active:scale-95">
<PlusCircle className="w-4 h-4" /> <PlusCircle className="w-4 h-4" />
Proactive Provisioning Proactive Provisioning
</button> </button>
</div> </div>
</div>
<div className="grid grid-cols-1 gap-6"> <div className="grid grid-cols-1 gap-6">
<div className="bg-background border border-border rounded-[2rem] overflow-hidden backdrop-blur-xl"> <div className="bg-background border border-border rounded-[2rem] overflow-hidden backdrop-blur-xl">

View File

@@ -1,7 +1,7 @@
"use client"; "use client";
import { useState, useEffect } from "react"; import { useState, useEffect } from "react";
import { Activity, Cpu, CheckCircle2, AlertCircle, Clock, Zap } from "lucide-react"; import { Activity, Cpu, CheckCircle2, AlertCircle, Clock, Zap, RefreshCw } from "lucide-react";
import Link from "next/link"; import Link from "next/link";
import { clsx, type ClassValue } from "clsx"; import { clsx, type ClassValue } from "clsx";
import { twMerge } from "tailwind-merge"; import { twMerge } from "tailwind-merge";
@@ -41,6 +41,7 @@ export default function DashboardPage() {
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const [lastUpdate, setLastUpdate] = useState<Date | null>(null); const [lastUpdate, setLastUpdate] = useState<Date | null>(null);
const [isUpdating, setIsUpdating] = useState(false); const [isUpdating, setIsUpdating] = useState(false);
const [viewMode, setViewMode] = useState<"ENTERPRISE" | "RETAIL">("ENTERPRISE");
const fetchData = async () => { const fetchData = async () => {
try { try {
@@ -97,6 +98,27 @@ export default function DashboardPage() {
Operational Telemetry Operational Telemetry
</h1> </h1>
</div> </div>
<div className="flex items-center gap-4">
<div className="flex items-center gap-2 bg-secondary/5 p-1 rounded-xl border border-border">
<button
onClick={() => setViewMode("RETAIL")}
className={cn(
"px-4 py-1.5 rounded-lg text-[9px] font-black uppercase tracking-widest transition-all",
viewMode === "RETAIL" ? "bg-background text-foreground shadow-sm" : "text-secondary hover:text-foreground"
)}
>
Retail
</button>
<button
onClick={() => setViewMode("ENTERPRISE")}
className={cn(
"px-4 py-1.5 rounded-lg text-[9px] font-black uppercase tracking-widest transition-all",
viewMode === "ENTERPRISE" ? "bg-background text-foreground shadow-sm" : "text-secondary hover:text-foreground"
)}
>
Enterprise
</button>
</div>
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<div className="text-[10px] font-mono text-secondary uppercase tracking-tighter"> <div className="text-[10px] font-mono text-secondary uppercase tracking-tighter">
Last sync: <span className="text-foreground">{lastUpdate ? lastUpdate.toLocaleTimeString() : "--:--:--"}</span> Last sync: <span className="text-foreground">{lastUpdate ? lastUpdate.toLocaleTimeString() : "--:--:--"}</span>
@@ -107,7 +129,67 @@ export default function DashboardPage() {
)} /> )} />
</div> </div>
</div> </div>
</div>
{viewMode === "RETAIL" ? (
/* Retail Mode UI - Ultra Simplified */
<div className="flex flex-col items-center justify-center py-20 animate-in zoom-in-95 duration-700">
<div className={cn(
"w-full max-w-2xl overflow-hidden rounded-[3rem] p-16 text-center border-2 transition-all duration-1000",
data.recentTests[0]?.status === "PASS"
? "bg-emerald-500/5 border-emerald-500/20 text-emerald-500 shadow-[0_20px_100px_rgba(16,185,129,0.1)]"
: "bg-rose-500/5 border-rose-500/20 text-rose-500 shadow-[0_20px_100px_rgba(244,63,94,0.1)]"
)}>
<div className="space-y-8">
<div className="flex justify-center">
<div className={cn(
"w-32 h-32 rounded-full flex items-center justify-center border-8 transition-all duration-1000 shadow-xl",
data.recentTests[0]?.status === "PASS" ? "bg-emerald-500/10 border-emerald-500" : "bg-rose-500/10 border-rose-500"
)}>
{data.recentTests[0]?.status === "PASS" ? (
<CheckCircle2 className="w-16 h-16" />
) : (
<AlertCircle className="w-16 h-16" />
)}
</div>
</div>
<div className="space-y-4">
<h2 className="text-8xl font-black tracking-tighter uppercase">
{data.recentTests[0]?.status === "PASS" ? "Port OK" : "Port Fail"}
</h2>
<div className="h-px w-24 bg-current/20 mx-auto" />
<p className="text-foreground/70 font-bold text-2xl uppercase tracking-widest italic">
{data.recentTests[0]?.status === "PASS" ? "HDMI Port is Functional" : "Console Port Requires Repair"}
</p>
</div>
<div className="pt-10 flex flex-col items-center gap-4">
<div className="px-6 py-2 bg-foreground/5 border border-foreground/10 rounded-full flex items-center gap-3">
<div className="w-2 h-2 rounded-full bg-foreground animate-pulse" />
<span className="text-[10px] font-black uppercase tracking-[0.2em] text-foreground">Waiting for Next Console</span>
</div>
<p className="text-secondary font-medium text-sm">
Last test: {data.recentTests[0]?.summary}
</p>
</div>
</div>
</div>
<div className="mt-12 flex items-center gap-8">
<div className="text-center">
<p className="text-[10px] font-black text-secondary uppercase tracking-widest mb-1">Active Station</p>
<p className="text-sm font-black text-foreground">{data.enrolledDevices[0]?.name || "Front Desk 01"}</p>
</div>
<div className="w-px h-8 bg-border" />
<div className="text-center">
<p className="text-[10px] font-black text-secondary uppercase tracking-widest mb-1">Today's Volume</p>
<p className="text-sm font-black text-foreground">{data.stats.testCount} Tests</p>
</div>
</div>
</div>
) : (
<div className="space-y-10">
{/* Hero Stats */} {/* Hero Stats */}
<div className="grid grid-cols-1 md:grid-cols-3 gap-6"> <div className="grid grid-cols-1 md:grid-cols-3 gap-6">
{[ {[
@@ -243,7 +325,7 @@ export default function DashboardPage() {
</h4> </h4>
<div className="space-y-3 relative z-10"> <div className="space-y-3 relative z-10">
<p className="text-xs text-secondary leading-relaxed font-medium"> <p className="text-xs text-secondary leading-relaxed font-medium">
Real-time spectral analysis of HDMI bus metrics. Processing orchestrated with <span className="text-sky-400 font-bold">sub-ms</span> edge latency. Real-time spectral analysis of <span className="text-foreground">Console HDMI Ports</span>. Processing orchestrated with <span className="text-sky-400 font-bold">sub-ms</span> edge latency.
</p> </p>
<div className="flex items-center gap-2 py-3 border-t border-sky-500/10"> <div className="flex items-center gap-2 py-3 border-t border-sky-500/10">
<span className="relative flex h-2 w-2"> <span className="relative flex h-2 w-2">
@@ -286,5 +368,7 @@ export default function DashboardPage() {
</div> </div>
</div> </div>
</div> </div>
)}
</div>
); );
} }

View File

@@ -102,7 +102,7 @@ export default function WebSimulatorPage() {
return; return;
} }
setState(s => ({ ...s, isTesting: true })); setState(s => ({ ...s, isTesting: true }));
addLog("[OP] Initiating HDMI spectral sweep..."); addLog("[OP] Initiating HDMI Port Diagnostic sweep...");
await new Promise(r => setTimeout(r, 1500)); await new Promise(r => setTimeout(r, 1500));
@@ -111,10 +111,10 @@ export default function WebSimulatorPage() {
method: "POST", method: "POST",
body: JSON.stringify({ body: JSON.stringify({
serialNumber: state.serialNumber, serialNumber: state.serialNumber,
type: "HDMI_OFFLINE", type: "PORT_DIAGNOSTIC",
status: "PASS", status: "PASS",
hdmi5v: false, hdmi5v: true,
summary: "Virtual sweep completed within threshold.", summary: "Port handshake confirmed. Pin continuity within spec.",
diodeResults: { diodeResults: {
"DDC_SCL": 0.500 + Math.random() * 0.1, "DDC_SCL": 0.500 + Math.random() * 0.1,
"DDC_SDA": 0.500 + Math.random() * 0.1, "DDC_SDA": 0.500 + Math.random() * 0.1,
@@ -215,9 +215,9 @@ export default function WebSimulatorPage() {
{/* Main Content */} {/* Main Content */}
<div className="flex flex-col items-center justify-center flex-1"> <div className="flex flex-col items-center justify-center flex-1">
<div className="text-white text-lg font-black tracking-tighter leading-none mb-1"> <div className="text-white text-lg font-black tracking-tighter leading-none mb-1">
{state.isTesting ? "RUNNING SWEEP" : state.serialNumber} {state.isTesting ? "PORT SWEEP" : state.serialNumber}
</div> </div>
<div className="text-[8px] text-sky-400/60 font-black uppercase tracking-[0.2em]">Validated Path v1.0</div> <div className="text-[8px] text-sky-400/60 font-black uppercase tracking-[0.2em]">Console Interface v1.0</div>
</div> </div>
{/* Bottom Console */} {/* Bottom Console */}

View File

@@ -28,7 +28,7 @@ export default async function TestDetailsPage({ params }: { params: Promise<{ id
<Activity className="w-3 h-3" /> Dashboard <Activity className="w-3 h-3" /> Dashboard
</Link> </Link>
<span className="opacity-20">/</span> <span className="opacity-20">/</span>
<span className="text-secondary/70">Node Insights</span> <span className="text-secondary/70">Port Diagnostic Insights</span>
</div> </div>
<div className="flex flex-col md:flex-row md:items-end justify-between gap-8 pb-8 border-b border-border"> <div className="flex flex-col md:flex-row md:items-end justify-between gap-8 pb-8 border-b border-border">
@@ -85,7 +85,7 @@ export default async function TestDetailsPage({ params }: { params: Promise<{ id
<div className="w-10 h-10 rounded-xl bg-sky-500/10 flex items-center justify-center"> <div className="w-10 h-10 rounded-xl bg-sky-500/10 flex items-center justify-center">
<Info className="w-5 h-5 text-sky-400" /> <Info className="w-5 h-5 text-sky-400" />
</div> </div>
<h2 className="text-[10px] font-black uppercase tracking-[0.3em] text-secondary">Response Spectrum Analysis</h2> <h2 className="text-[10px] font-black uppercase tracking-[0.3em] text-secondary">HDMI Port Pinout Analysis</h2>
</div> </div>
<div className="px-4 py-1.5 bg-sky-500/10 border border-sky-500/20 rounded-full text-[9px] font-black text-sky-400 uppercase tracking-widest"> <div className="px-4 py-1.5 bg-sky-500/10 border border-sky-500/20 rounded-full text-[9px] font-black text-sky-400 uppercase tracking-widest">
Calibration: 0.8V VDD Calibration: 0.8V VDD