Files
wifi-densepose/ui/services/health.service.js
ruv b7e0f07e6e feat: Sensing-only UI mode with Gaussian splat visualization and Rust migration ADR
- Add Python WebSocket sensing server (ws_server.py) with ESP32 UDP CSI
  and Windows RSSI auto-detect collectors on port 8765
- Add Three.js Gaussian splat renderer with custom GLSL shaders for
  real-time WiFi signal field visualization (blue→green→red gradient)
- Add SensingTab component with RSSI sparkline, feature meters, and
  motion classification badge
- Add sensing.service.js WebSocket client with reconnect and simulation fallback
- Implement sensing-only mode: suppress all DensePose API calls when
  FastAPI backend (port 8000) is not running, clean console output
- ADR-019: Document sensing-only UI architecture and data flow
- ADR-020: Migrate AI/model inference to Rust with RuVector ONNX Runtime,
  replacing ~2.7GB Python stack with ~50MB static binary
- Add ruvnet/ruvector as upstream remote for RuVector crate ecosystem

Co-Authored-By: claude-flow <ruv@ruv.net>
2026-02-28 14:37:29 -05:00

139 lines
3.4 KiB
JavaScript

// Health Service for WiFi-DensePose UI
import { API_CONFIG } from '../config/api.config.js';
import { apiService } from './api.service.js';
export class HealthService {
constructor() {
this.healthCheckInterval = null;
this.healthSubscribers = [];
this.lastHealthStatus = null;
}
// Get system health
async getSystemHealth() {
const health = await apiService.get(API_CONFIG.ENDPOINTS.HEALTH.SYSTEM);
this.lastHealthStatus = health;
this.notifySubscribers(health);
return health;
}
// Check readiness
async checkReadiness() {
return apiService.get(API_CONFIG.ENDPOINTS.HEALTH.READY);
}
// Check liveness
async checkLiveness() {
return apiService.get(API_CONFIG.ENDPOINTS.HEALTH.LIVE);
}
// Get system metrics
async getSystemMetrics() {
return apiService.get(API_CONFIG.ENDPOINTS.HEALTH.METRICS);
}
// Get version info
async getVersion() {
return apiService.get(API_CONFIG.ENDPOINTS.HEALTH.VERSION);
}
// Get API info
async getApiInfo() {
return apiService.get(API_CONFIG.ENDPOINTS.INFO);
}
// Get API status
async getApiStatus() {
return apiService.get(API_CONFIG.ENDPOINTS.STATUS);
}
// Start periodic health checks
startHealthMonitoring(intervalMs = 30000) {
if (this.healthCheckInterval) {
console.warn('Health monitoring already active');
return;
}
// Initial check (silent on failure — DensePose API may not be running)
this.getSystemHealth().catch(() => {
// DensePose API not running — sensing-only mode, skip polling
this._backendUnavailable = true;
});
// Set up periodic checks only if backend was reachable
this.healthCheckInterval = setInterval(() => {
if (this._backendUnavailable) return;
this.getSystemHealth().catch(error => {
this.notifySubscribers({
status: 'error',
error: error.message,
timestamp: new Date().toISOString()
});
});
}, intervalMs);
}
// Stop health monitoring
stopHealthMonitoring() {
if (this.healthCheckInterval) {
clearInterval(this.healthCheckInterval);
this.healthCheckInterval = null;
}
}
// Subscribe to health updates
subscribeToHealth(callback) {
this.healthSubscribers.push(callback);
// Send last known status if available
if (this.lastHealthStatus) {
callback(this.lastHealthStatus);
}
// Return unsubscribe function
return () => {
const index = this.healthSubscribers.indexOf(callback);
if (index > -1) {
this.healthSubscribers.splice(index, 1);
}
};
}
// Notify subscribers
notifySubscribers(health) {
this.healthSubscribers.forEach(callback => {
try {
callback(health);
} catch (error) {
console.error('Error in health subscriber:', error);
}
});
}
// Check if system is healthy
isSystemHealthy() {
if (!this.lastHealthStatus) {
return null;
}
return this.lastHealthStatus.status === 'healthy';
}
// Get component status
getComponentStatus(componentName) {
if (!this.lastHealthStatus?.components) {
return null;
}
return this.lastHealthStatus.components[componentName];
}
// Clean up
dispose() {
this.stopHealthMonitoring();
this.healthSubscribers = [];
this.lastHealthStatus = null;
}
}
// Create singleton instance
export const healthService = new HealthService();