152 lines
No EOL
4.3 KiB
TypeScript
152 lines
No EOL
4.3 KiB
TypeScript
import { SensorConfig, SensorZoneConfig } from '@/types/sensor';
|
|
|
|
// Define zone-based default configurations
|
|
export const ZONE_CONFIGS: Record<string, SensorZoneConfig> = {
|
|
head: {
|
|
zone: 'head',
|
|
defaultThresholds: {
|
|
warningThreshold: 3000,
|
|
alarmThreshold: 3500,
|
|
warningDelayMs: 30000 // 30 seconds - critical area needs fast response
|
|
},
|
|
description: 'Head area - most critical, fastest escalation'
|
|
},
|
|
shoulders: {
|
|
zone: 'shoulders',
|
|
defaultThresholds: {
|
|
warningThreshold: 2800,
|
|
alarmThreshold: 3200,
|
|
warningDelayMs: 45000 // 45 seconds
|
|
},
|
|
description: 'Shoulder area - high priority, moderate escalation'
|
|
},
|
|
back: {
|
|
zone: 'back',
|
|
defaultThresholds: {
|
|
warningThreshold: 2500,
|
|
alarmThreshold: 3000,
|
|
warningDelayMs: 60000 // 1 minute
|
|
},
|
|
description: 'Back area - moderate priority, standard escalation'
|
|
},
|
|
hips: {
|
|
zone: 'hips',
|
|
defaultThresholds: {
|
|
warningThreshold: 2200,
|
|
alarmThreshold: 2800,
|
|
warningDelayMs: 90000 // 90 seconds
|
|
},
|
|
description: 'Hip area - moderate priority, longer escalation'
|
|
},
|
|
legs: {
|
|
zone: 'legs',
|
|
defaultThresholds: {
|
|
warningThreshold: 2000,
|
|
alarmThreshold: 2500,
|
|
warningDelayMs: 120000 // 2 minutes
|
|
},
|
|
description: 'Leg area - lower priority, extended escalation'
|
|
},
|
|
feet: {
|
|
zone: 'feet',
|
|
defaultThresholds: {
|
|
warningThreshold: 1500,
|
|
alarmThreshold: 1800,
|
|
warningDelayMs: 180000 // 3 minutes
|
|
},
|
|
description: 'Feet area - lowest priority, longest escalation'
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Validates sensor configuration against zone defaults
|
|
*/
|
|
export function validateSensorConfig(config: SensorConfig): {
|
|
isValid: boolean;
|
|
warnings: string[];
|
|
errors: string[];
|
|
} {
|
|
const warnings: string[] = [];
|
|
const errors: string[] = [];
|
|
|
|
// Check if zone exists
|
|
const zoneConfig = ZONE_CONFIGS[config.zone];
|
|
if (!zoneConfig) {
|
|
errors.push(`Unknown zone: ${config.zone}`);
|
|
return { isValid: false, warnings, errors };
|
|
}
|
|
|
|
// Validate threshold values
|
|
if (config.alarmThreshold <= config.warningThreshold) {
|
|
errors.push(`Alarm threshold (${config.alarmThreshold}) must be greater than warning threshold (${config.warningThreshold})`);
|
|
}
|
|
|
|
if (config.warningThreshold <= 0 || config.alarmThreshold <= 0) {
|
|
errors.push('Thresholds must be positive values');
|
|
}
|
|
|
|
if (config.warningDelayMs <= 0) {
|
|
errors.push('Warning delay must be positive');
|
|
}
|
|
|
|
// Check against zone defaults (warnings only)
|
|
const defaults = zoneConfig.defaultThresholds;
|
|
const tolerance = 0.2; // 20% tolerance
|
|
|
|
if (Math.abs(config.warningThreshold - defaults.warningThreshold) / defaults.warningThreshold > tolerance) {
|
|
warnings.push(`Warning threshold (${config.warningThreshold}) differs significantly from zone default (${defaults.warningThreshold})`);
|
|
}
|
|
|
|
if (Math.abs(config.alarmThreshold - defaults.alarmThreshold) / defaults.alarmThreshold > tolerance) {
|
|
warnings.push(`Alarm threshold (${config.alarmThreshold}) differs significantly from zone default (${defaults.alarmThreshold})`);
|
|
}
|
|
|
|
if (Math.abs(config.warningDelayMs - defaults.warningDelayMs) / defaults.warningDelayMs > tolerance) {
|
|
warnings.push(`Warning delay (${config.warningDelayMs}ms) differs significantly from zone default (${defaults.warningDelayMs}ms)`);
|
|
}
|
|
|
|
return { isValid: errors.length === 0, warnings, errors };
|
|
}
|
|
|
|
/**
|
|
* Creates a sensor config with zone defaults as base
|
|
*/
|
|
export function createSensorConfig(
|
|
id: string,
|
|
position: { x: number; y: number },
|
|
zone: string,
|
|
label: string,
|
|
overrides: Partial<SensorConfig> = {}
|
|
): SensorConfig {
|
|
const zoneConfig = ZONE_CONFIGS[zone];
|
|
if (!zoneConfig) {
|
|
throw new Error(`Unknown zone: ${zone}`);
|
|
}
|
|
|
|
return {
|
|
id,
|
|
x: position.x,
|
|
y: position.y,
|
|
zone,
|
|
label,
|
|
...zoneConfig.defaultThresholds,
|
|
...overrides
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Gets human-readable duration string
|
|
*/
|
|
export function formatDuration(ms: number): string {
|
|
const seconds = Math.floor(ms / 1000);
|
|
const minutes = Math.floor(seconds / 60);
|
|
const hours = Math.floor(minutes / 60);
|
|
|
|
if (hours > 0) {
|
|
return `${hours}h ${minutes % 60}m`;
|
|
} else if (minutes > 0) {
|
|
return `${minutes}m ${seconds % 60}s`;
|
|
} else {
|
|
return `${seconds}s`;
|
|
}
|
|
} |