@sailboat-computer/health-monitoring
Version:
Comprehensive health monitoring system for sailboat computer v3 with marine-specific health checks
437 lines (378 loc) ⢠12.8 kB
text/typescript
/**
* Example usage of marine health monitoring system
* This demonstrates how to set up and use health checks for a sailboat
*/
import {
HealthCheckEngine,
defaultHealthCheckEngine,
OperationalContext,
OperationalContextType,
MarineEnvironmentStatus,
HealthMonitoringEvent,
HealthStatus,
HealthStatusType
} from '../index';
import {
GPSHealthCheck,
createGPSHealthCheck,
GPSData
} from '../marine/GPSHealthCheck';
import {
BatteryHealthCheck,
createBatteryHealthCheck,
BatteryData
} from '../marine/BatteryHealthCheck';
/**
* Example marine health monitoring setup for a sailboat
*/
export class MarineHealthMonitoringExample {
private healthEngine: HealthCheckEngine;
private gpsHealthCheck!: GPSHealthCheck;
private battery48vHealthCheck!: BatteryHealthCheck;
private battery12vHealthCheck!: BatteryHealthCheck;
constructor() {
this.healthEngine = defaultHealthCheckEngine;
// Set up event listening
this.setupEventHandling();
// Initialize health checks
this.initializeHealthChecks();
}
/**
* Initialize all marine health checks
*/
private initializeHealthChecks(): void {
// GPS Health Check
this.gpsHealthCheck = createGPSHealthCheck(
this.mockGPSDataProvider.bind(this),
{
// Custom configuration for this specific GPS unit
interval: 15000, // Check every 15 seconds instead of default 10
thresholds: {
warning: 0.7, // Slightly higher threshold for this critical system
critical: 0.4,
unit: 'health_score'
}
}
);
// 48V House Battery Health Check
this.battery48vHealthCheck = createBatteryHealthCheck(
this.mock48VBatteryDataProvider.bind(this),
{
checkId: 'house-battery-48v',
name: '48V House Battery Health',
interval: 20000, // Check every 20 seconds
thresholds: {
warning: 0.6,
critical: 0.3,
unit: 'health_score'
}
}
);
// 12V Service Battery Health Check
this.battery12vHealthCheck = createBatteryHealthCheck(
this.mock12VBatteryDataProvider.bind(this),
{
checkId: 'service-battery-12v',
name: '12V Service Battery Health',
interval: 30000, // Check every 30 seconds
thresholds: {
warning: 0.6,
critical: 0.3,
unit: 'health_score'
}
}
);
// Register all health checks with the engine
this.healthEngine.registerHealthCheck(this.gpsHealthCheck);
this.healthEngine.registerHealthCheck(this.battery48vHealthCheck);
this.healthEngine.registerHealthCheck(this.battery12vHealthCheck);
}
/**
* Set up event handling for health monitoring events
*/
private setupEventHandling(): void {
this.healthEngine.onEvent((event: HealthMonitoringEvent) => {
console.log(`[${event.timestamp.toISOString()}] Health Event: ${event.eventType}`);
console.log(` System: ${event.systemId}, Check: ${event.checkId}`);
console.log(` Status: ${event.currentStatus}`);
console.log(` Message: ${event.data.message}`);
if (event.data.score !== undefined) {
console.log(` Score: ${(event.data.score * 100).toFixed(1)}%`);
}
// Handle critical alerts
if (event.eventType === 'alert_triggered' && event.currentStatus === HealthStatus.CRITICAL) {
this.handleCriticalAlert(event);
}
// Handle system recovery
if (event.eventType === 'system_recovery') {
this.handleSystemRecovery(event);
}
});
}
/**
* Handle critical health alerts
*/
private handleCriticalAlert(event: HealthMonitoringEvent): void {
console.error(`šØ CRITICAL ALERT: ${event.data.message}`);
// In a real implementation, this might:
// - Send notifications to crew
// - Log to emergency systems
// - Trigger automatic responses
// - Update navigation displays
if (event.checkId === 'gps-health') {
console.error('ā ļø GPS CRITICAL: Switch to backup navigation methods');
} else if (event.checkId && event.checkId.includes('battery')) {
console.error('ā ļø BATTERY CRITICAL: Implement power conservation measures');
}
}
/**
* Handle system recovery events
*/
private handleSystemRecovery(event: HealthMonitoringEvent): void {
console.log(`ā
System Recovery: ${event.data.message}`);
}
/**
* Start the health monitoring system
*/
public start(): void {
console.log('š¢ Starting Marine Health Monitoring System...');
// Set initial operational context
this.healthEngine.updateOperationalContext(OperationalContext.DOCKED);
// Set initial marine environment
this.updateMarineEnvironment({
seaState: 'calm',
weather: 'clear',
powerStatus: 'normal',
criticalOperationsOnly: false
});
// Start the health check engine
this.healthEngine.start();
console.log('ā
Marine Health Monitoring System started');
}
/**
* Stop the health monitoring system
*/
public stop(): void {
console.log('š Stopping Marine Health Monitoring System...');
this.healthEngine.stop();
console.log('ā
Marine Health Monitoring System stopped');
}
/**
* Update operational context (e.g., when leaving dock, anchoring, etc.)
*/
public updateOperationalContext(context: OperationalContextType): void {
console.log(`š Updating operational context to: ${context}`);
this.healthEngine.updateOperationalContext(context);
}
/**
* Update marine environment conditions
*/
public updateMarineEnvironment(environment: Partial<MarineEnvironmentStatus>): void {
console.log('š Updating marine environment conditions');
// Get current environment and merge with updates
const currentEnv: MarineEnvironmentStatus = {
seaState: 'calm',
weather: 'clear',
windSpeed: 5,
temperature: 22,
powerStatus: 'normal',
connectivityQuality: 0.9,
expectedFailureRate: 1.0,
recommendedTimeoutMultiplier: 1.0,
criticalOperationsOnly: false,
...environment
};
this.healthEngine.updateMarineEnvironment(currentEnv);
}
/**
* Get current health status summary
*/
public async getHealthSummary(): Promise<void> {
console.log('\nš Current Health Status Summary:');
console.log('================================');
const results = this.healthEngine.getAllResults();
for (const [checkId, result] of results) {
const statusIcon = this.getStatusIcon(result.status);
const scorePercent = (result.score * 100).toFixed(1);
console.log(`${statusIcon} ${result.name}: ${result.status} (${scorePercent}%)`);
console.log(` ${result.message}`);
if (result.details) {
const importantDetails = this.extractImportantDetails(result.details);
if (importantDetails.length > 0) {
console.log(` Details: ${importantDetails.join(', ')}`);
}
}
console.log('');
}
}
/**
* Simulate different sailing scenarios
*/
public async simulateScenarios(): Promise<void> {
console.log('\nš Simulating different sailing scenarios...\n');
// Scenario 1: Leaving dock
console.log('š Scenario 1: Leaving dock');
this.updateOperationalContext(OperationalContext.MOTORING);
await this.wait(2000);
await this.getHealthSummary();
// Scenario 2: Sailing in good conditions
console.log('š Scenario 2: Sailing in good conditions');
this.updateOperationalContext(OperationalContext.SAILING);
this.updateMarineEnvironment({
seaState: 'moderate', // Valid enum value
weather: 'clear'
});
await this.wait(2000);
await this.getHealthSummary();
// Scenario 3: Rough weather
console.log('š Scenario 3: Rough weather conditions');
this.updateMarineEnvironment({
seaState: 'rough',
weather: 'storm'
});
await this.wait(2000);
await this.getHealthSummary();
// Scenario 4: Power conservation mode
console.log('š Scenario 4: Power conservation mode');
this.updateMarineEnvironment({
seaState: 'moderate',
weather: 'cloudy', // Valid enum value
powerStatus: 'conservation'
});
await this.wait(2000);
await this.getHealthSummary();
// Scenario 5: Emergency situation
console.log('š Scenario 5: Emergency situation');
this.updateOperationalContext(OperationalContext.EMERGENCY);
this.updateMarineEnvironment({
seaState: 'very_rough',
weather: 'storm',
powerStatus: 'critical',
criticalOperationsOnly: true
});
await this.wait(2000);
await this.getHealthSummary();
}
/**
* Mock GPS data provider (in real implementation, this would read from actual GPS)
*/
private async mockGPSDataProvider(): Promise<GPSData> {
// Simulate some variability in GPS data
const baseData = {
latitude: 37.7749 + (Math.random() - 0.5) * 0.001,
longitude: -122.4194 + (Math.random() - 0.5) * 0.001,
altitude: 10 + Math.random() * 5,
satelliteCount: 8 + Math.floor(Math.random() * 4),
hdop: 1.0 + Math.random() * 2.0,
vdop: 1.2 + Math.random() * 2.0,
speed: Math.random() * 10,
course: Math.random() * 360,
timestamp: new Date(),
fix: Math.random() > 0.1 ? '3d' as const : '2d' as const
};
return baseData;
}
/**
* Mock 48V battery data provider
*/
private async mock48VBatteryDataProvider(): Promise<BatteryData> {
return {
voltage: 50.4 + (Math.random() - 0.5) * 4.0,
current: (Math.random() - 0.5) * 20,
temperature: 25 + (Math.random() - 0.5) * 10,
stateOfCharge: 60 + Math.random() * 35,
capacity: 400 + (Math.random() - 0.5) * 50,
nominalCapacity: 400,
cycleCount: Math.floor(Math.random() * 500),
timestamp: new Date(),
batteryType: '48v'
};
}
/**
* Mock 12V battery data provider
*/
private async mock12VBatteryDataProvider(): Promise<BatteryData> {
return {
voltage: 12.6 + (Math.random() - 0.5) * 1.0,
current: (Math.random() - 0.5) * 10,
temperature: 25 + (Math.random() - 0.5) * 8,
stateOfCharge: 70 + Math.random() * 25,
capacity: 100 + (Math.random() - 0.5) * 20,
nominalCapacity: 100,
cycleCount: Math.floor(Math.random() * 800),
timestamp: new Date(),
batteryType: '12v'
};
}
/**
* Get status icon for display
*/
private getStatusIcon(status: HealthStatusType): string {
switch (status) {
case HealthStatus.HEALTHY:
return 'ā
';
case HealthStatus.DEGRADED:
return 'ā ļø';
case HealthStatus.UNHEALTHY:
return 'š¶';
case HealthStatus.CRITICAL:
return 'šØ';
default:
return 'ā';
}
}
/**
* Extract important details for summary display
*/
private extractImportantDetails(details: Record<string, any>): string[] {
const important: string[] = [];
if (details['voltage'] !== undefined) {
important.push(`${details['voltage'].toFixed(2)}V`);
}
if (details['stateOfCharge'] !== undefined) {
important.push(`${details['stateOfCharge']}% SoC`);
}
if (details['satelliteCount'] !== undefined) {
important.push(`${details['satelliteCount']} sats`);
}
if (details['hdop'] !== undefined) {
important.push(`HDOP ${details['hdop'].toFixed(1)}`);
}
if (details['temperature'] !== undefined) {
important.push(`${details['temperature']}°C`);
}
return important;
}
/**
* Utility function to wait
*/
private wait(ms: number): Promise<void> {
return new Promise(resolve => setTimeout(resolve, ms));
}
}
/**
* Example usage function
*/
export async function runMarineHealthMonitoringExample(): Promise<void> {
const monitor = new MarineHealthMonitoringExample();
try {
// Start the monitoring system
monitor.start();
// Wait a bit for initial health checks
await new Promise(resolve => setTimeout(resolve, 3000));
// Get initial health summary
await monitor.getHealthSummary();
// Run scenario simulations
await monitor.simulateScenarios();
// Keep running for a bit to see ongoing monitoring
console.log('\nā±ļø Running continuous monitoring for 30 seconds...');
await new Promise(resolve => setTimeout(resolve, 30000));
// Final health summary
await monitor.getHealthSummary();
} finally {
// Clean shutdown
monitor.stop();
}
}
// Export for easy testing
export default MarineHealthMonitoringExample;