UNPKG

@sailboat-computer/health-monitoring

Version:

Comprehensive health monitoring system for sailboat computer v3 with marine-specific health checks

445 lines 15.6 kB
"use strict"; /** * Core health check engine for marine systems */ Object.defineProperty(exports, "__esModule", { value: true }); exports.defaultHealthCheckEngine = exports.HealthCheckEngine = exports.BaseHealthCheck = void 0; const types_1 = require("../types"); const resilience_1 = require("@sailboat-computer/resilience"); /** * Abstract base class for health checks */ class BaseHealthCheck { constructor(config) { this.consecutiveFailures = 0; this.lastExecutionTime = 0; this.config = config; } /** * Execute the health check */ async execute(operationalContext, marineEnvironment) { const startTime = Date.now(); try { // Check if this health check should run in current operational context if (!this.shouldExecute(operationalContext, marineEnvironment)) { return this.createSkippedResult(operationalContext, 'Skipped due to operational context'); } // Execute the actual health check with timeout protection const timeoutContext = this.getTimeoutContext(operationalContext); const result = await (0, resilience_1.executeWithTimeout)(() => this.performCheck(operationalContext, marineEnvironment), timeoutContext); if (result.success && result.data) { this.consecutiveFailures = 0; this.lastResult = result.data; this.lastExecutionTime = Date.now() - startTime; return result.data; } else { throw result.error || new Error('Health check failed'); } } catch (error) { this.consecutiveFailures++; const failureResult = this.createFailureResult(operationalContext, error, Date.now() - startTime); this.lastResult = failureResult; this.lastExecutionTime = Date.now() - startTime; return failureResult; } } /** * Get the last health check result */ getLastResult() { return this.lastResult; } /** * Get health check configuration */ getConfig() { return { ...this.config }; } /** * Check if health check should execute in current context */ shouldExecute(operationalContext, marineEnvironment) { // Check operational context if (!this.config.marineSettings.operationalContexts.includes(operationalContext)) { return false; } // Check power awareness if (this.config.marineSettings.powerAware && marineEnvironment?.powerStatus === 'critical') { // Only run critical health checks when power is critical return this.config.marineSystemType === types_1.MarineSystemType.SAFETY || this.config.marineSystemType === types_1.MarineSystemType.NAVIGATION; } // Check environmental conditions if (marineEnvironment?.criticalOperationsOnly && !this.config.marineSettings.safetyImpact) { return false; } return true; } /** * Get timeout context for this health check */ getTimeoutContext(operationalContext) { switch (this.config.marineSystemType) { case types_1.MarineSystemType.SAFETY: return resilience_1.TimeoutContexts.safetyOperation(this.config.name, operationalContext); case types_1.MarineSystemType.NAVIGATION: return resilience_1.TimeoutContexts.navigationSensor(this.config.name, operationalContext); case types_1.MarineSystemType.COMMUNICATION: return resilience_1.TimeoutContexts.networkOperation(this.config.name, operationalContext); case types_1.MarineSystemType.MAINTENANCE: return resilience_1.TimeoutContexts.maintenanceOperation(this.config.name); default: return resilience_1.TimeoutContexts.comfortOperation(this.config.name, operationalContext); } } /** * Create a skipped result */ createSkippedResult(operationalContext, reason) { return { checkId: this.config.checkId, name: this.config.name, type: this.config.type, marineSystemType: this.config.marineSystemType, status: types_1.HealthStatus.UNKNOWN, score: 0.5, // Neutral score for skipped checks message: reason, timestamp: new Date(), executionTime: 0, marineContext: { operationalContext, environmentalImpact: false, safetyImpact: this.config.marineSettings.safetyImpact, powerImpact: 0 } }; } /** * Create a failure result */ createFailureResult(operationalContext, error, executionTime) { // Determine severity based on consecutive failures let status; let score; if (this.consecutiveFailures >= 3) { status = types_1.HealthStatus.CRITICAL; score = 0.0; } else if (this.consecutiveFailures >= 2) { status = types_1.HealthStatus.UNHEALTHY; score = 0.2; } else { status = types_1.HealthStatus.DEGRADED; score = 0.4; } return { checkId: this.config.checkId, name: this.config.name, type: this.config.type, marineSystemType: this.config.marineSystemType, status, score, message: `Health check failed: ${error.message}`, details: { error: error.message, consecutiveFailures: this.consecutiveFailures, stack: error.stack }, timestamp: new Date(), executionTime, marineContext: { operationalContext, environmentalImpact: false, safetyImpact: this.config.marineSettings.safetyImpact, powerImpact: 0 } }; } } exports.BaseHealthCheck = BaseHealthCheck; /** * Health check scheduler and executor */ class HealthCheckEngine { constructor() { this.healthChecks = new Map(); this.scheduledChecks = new Map(); this.eventListeners = []; this.isRunning = false; this.currentOperationalContext = types_1.OperationalContext.DOCKED; } /** * Register a health check */ registerHealthCheck(healthCheck) { const config = healthCheck.getConfig(); this.healthChecks.set(config.checkId, healthCheck); // Schedule the health check if engine is running if (this.isRunning) { this.scheduleHealthCheck(config.checkId); } this.emitEvent('health_check_completed', config.checkId, { message: `Health check '${config.name}' registered`, details: { action: 'registered' } }); } /** * Unregister a health check */ unregisterHealthCheck(checkId) { const healthCheck = this.healthChecks.get(checkId); if (!healthCheck) { return; } // Cancel scheduled execution const timer = this.scheduledChecks.get(checkId); if (timer) { clearTimeout(timer); this.scheduledChecks.delete(checkId); } this.healthChecks.delete(checkId); this.emitEvent('health_check_completed', checkId, { message: `Health check unregistered`, details: { action: 'unregistered' } }); } /** * Start the health check engine */ start() { if (this.isRunning) { return; } this.isRunning = true; // Schedule all registered health checks for (const checkId of this.healthChecks.keys()) { this.scheduleHealthCheck(checkId); } this.emitEvent('system_recovery', 'health-engine', { message: 'Health check engine started', details: { registeredChecks: this.healthChecks.size, operationalContext: this.currentOperationalContext } }); } /** * Stop the health check engine */ stop() { if (!this.isRunning) { return; } this.isRunning = false; // Cancel all scheduled checks for (const [checkId, timer] of this.scheduledChecks) { clearTimeout(timer); } this.scheduledChecks.clear(); this.emitEvent('system_recovery', 'health-engine', { message: 'Health check engine stopped', details: { cancelledChecks: this.scheduledChecks.size } }); } /** * Update operational context */ updateOperationalContext(context) { const previousContext = this.currentOperationalContext; this.currentOperationalContext = context; this.emitEvent('system_recovery', 'health-engine', { message: 'Operational context updated', details: { previousContext, newContext: context, affectedChecks: this.getAffectedChecks(context) } }); // Reschedule health checks if context change affects them this.rescheduleAffectedChecks(context); } /** * Update marine environment status */ updateMarineEnvironment(environment) { this.currentMarineEnvironment = environment; this.emitEvent('system_recovery', 'health-engine', { message: 'Marine environment updated', details: { seaState: environment.seaState, weather: environment.weather, powerStatus: environment.powerStatus, criticalOperationsOnly: environment.criticalOperationsOnly } }); // Adjust health check scheduling based on environment this.adjustForEnvironment(environment); } /** * Execute a specific health check immediately */ async executeHealthCheck(checkId) { const healthCheck = this.healthChecks.get(checkId); if (!healthCheck) { return null; } try { const result = await healthCheck.execute(this.currentOperationalContext, this.currentMarineEnvironment); this.emitEvent('health_check_completed', checkId, { message: `Health check completed: ${result.status}`, score: result.score, details: { status: result.status, executionTime: result.executionTime, manual: true } }); return result; } catch (error) { this.emitEvent('alert_triggered', checkId, { message: `Health check execution failed: ${error.message}`, details: { error: error.message } }); return null; } } /** * Get all health check results */ getAllResults() { const results = new Map(); for (const [checkId, healthCheck] of this.healthChecks) { const result = healthCheck.getLastResult(); if (result) { results.set(checkId, result); } } return results; } /** * Get health checks by system type */ getHealthChecksBySystem(systemType) { return Array.from(this.healthChecks.values()).filter(check => check.getConfig().marineSystemType === systemType); } /** * Add event listener */ onEvent(listener) { this.eventListeners.push(listener); } /** * Remove event listener */ removeEventListener(listener) { const index = this.eventListeners.indexOf(listener); if (index > -1) { this.eventListeners.splice(index, 1); } } /** * Schedule a health check */ scheduleHealthCheck(checkId) { const healthCheck = this.healthChecks.get(checkId); if (!healthCheck) { return; } const config = healthCheck.getConfig(); // Cancel existing timer const existingTimer = this.scheduledChecks.get(checkId); if (existingTimer) { clearTimeout(existingTimer); } // Calculate interval based on marine environment let interval = config.interval; if (this.currentMarineEnvironment) { // Adjust interval based on environmental conditions if (this.currentMarineEnvironment.criticalOperationsOnly) { interval *= 0.5; // More frequent checks in critical conditions } else if (this.currentMarineEnvironment.powerStatus === 'conservation') { interval *= 2.0; // Less frequent checks to save power } } // Schedule next execution const timer = setTimeout(async () => { await this.executeHealthCheck(checkId); // Reschedule if still running if (this.isRunning) { this.scheduleHealthCheck(checkId); } }, interval); this.scheduledChecks.set(checkId, timer); } /** * Get health checks affected by operational context change */ getAffectedChecks(context) { const affected = []; for (const [checkId, healthCheck] of this.healthChecks) { const config = healthCheck.getConfig(); if (!config.marineSettings.operationalContexts.includes(context)) { affected.push(checkId); } } return affected; } /** * Reschedule health checks affected by context change */ rescheduleAffectedChecks(context) { for (const checkId of this.healthChecks.keys()) { this.scheduleHealthCheck(checkId); } } /** * Adjust health check scheduling for marine environment */ adjustForEnvironment(environment) { // Reschedule all checks with new environmental considerations for (const checkId of this.healthChecks.keys()) { this.scheduleHealthCheck(checkId); } } /** * Emit health monitoring event */ emitEvent(eventType, checkId, data) { const event = { eventId: `${eventType}-${checkId}-${Date.now()}`, timestamp: new Date(), eventType, systemId: 'health-engine', checkId, currentStatus: types_1.HealthStatus.UNKNOWN, // Will be updated by specific implementations data: { ...data, marineContext: { operationalContext: this.currentOperationalContext, environmentalConditions: this.currentMarineEnvironment ? { ...this.currentMarineEnvironment } : {}, safetyImpact: false } } }; this.eventListeners.forEach(listener => { try { listener(event); } catch (error) { console.error('Error in health monitoring event listener:', error); } }); } } exports.HealthCheckEngine = HealthCheckEngine; /** * Default health check engine instance */ exports.defaultHealthCheckEngine = new HealthCheckEngine(); //# sourceMappingURL=HealthCheckEngine.js.map