UNPKG

@flexabrain/mcp-server

Version:

Advanced electrical schematic analysis MCP server with rail engineering expertise

362 lines 16.1 kB
/** * FlexaBrain MCP Server - Electrical Component Classification Engine * * Advanced component classification system leveraging rail electrical engineering expertise. * Identifies and classifies electrical components from OCR results using pattern matching, * context analysis, and domain knowledge. */ import { ComponentType, ComponentCategory, SafetyLevel } from '../types/electrical.js'; import { RAIL_COMPONENT_PATTERNS, RAIL_COMPONENT_SPECIFICATIONS } from '../data/rail-components.js'; export class ElectricalComponentClassifier { MINIMUM_CONFIDENCE = 0.3; CONTEXT_BOOST_FACTOR = 0.15; PATTERN_CONFIDENCE_WEIGHT = 0.7; OCR_CONFIDENCE_WEIGHT = 0.3; /** * Classify electrical components from OCR results */ async classifyComponents(ocrResult, context) { console.error(`FlexaBrain Classifier: Processing ${ocrResult.words.length} OCR words`); const components = []; const potentialMatches = []; // First pass: Find potential component matches for (const word of ocrResult.words) { const matches = this.findComponentMatches(word, context); potentialMatches.push(...matches); } // Second pass: Apply context analysis and filtering const filteredMatches = this.applyContextAnalysis(potentialMatches, ocrResult, context); // Third pass: Create final component objects for (const match of filteredMatches) { if (match.confidence >= this.MINIMUM_CONFIDENCE) { const component = this.createElectricalComponent(match, context); components.push(component); } } console.error(`FlexaBrain Classifier: Identified ${components.length} electrical components`); return this.deduplicateComponents(components); } /** * Find potential component matches for a single OCR word */ findComponentMatches(word, context) { const matches = []; const cleanText = word.text.trim().toUpperCase(); if (cleanText.length < 2) return matches; // Skip very short text // Test against all component patterns for (const [componentType, pattern] of Object.entries(RAIL_COMPONENT_PATTERNS)) { const typeKey = componentType; for (const regex of pattern.patterns) { if (regex.test(cleanText)) { const baseConfidence = this.calculatePatternConfidence(cleanText, regex, pattern); const contextScore = this.calculateContextScore(cleanText, pattern, context); const match = { component_id: cleanText, type: typeKey, pattern_match: regex, confidence: this.combineConfidences(baseConfidence, word.confidence, contextScore), context_score: contextScore, location: word.bbox, ocr_word: word }; matches.push(match); } } } return matches; } /** * Calculate confidence score for pattern matching */ calculatePatternConfidence(text, pattern, componentPattern) { // Base confidence from pattern boost let confidence = componentPattern.confidence_boost; // Boost for exact prefix matches const hasExactPrefix = componentPattern.prefixes.some(prefix => text.startsWith(prefix.toUpperCase())); if (hasExactPrefix) { confidence = Math.min(confidence + 0.1, 1.0); } // Special handling for signal references (8-digit numbers) if (pattern.source === '^\\d{8}$' && text.length === 8) { confidence = 0.98; // Very high confidence for 8-digit signal references } // Penalize very short or very long identifiers if (text.length < 3) confidence *= 0.8; if (text.length > 12) confidence *= 0.9; return Math.max(0.1, Math.min(confidence, 1.0)); } /** * Calculate context-based confidence boost */ calculateContextScore(componentId, pattern, context) { let contextScore = 0; if (!context) return contextScore; // Schematic type context if (context.schematic_type) { const schematicBoosts = { 'traction_power': ['CONVERTER', 'TRANSFORMER', 'CIRCUIT_BREAKER'], 'signaling': ['SIGNAL_REFERENCE', 'SIGNAL_MODULE', 'RELAY'], 'control': ['CONTACTOR', 'RELAY', 'SWITCH'], 'auxiliary': ['TRANSFORMER', 'CIRCUIT_BREAKER', 'FUSE'] }; const relevantTypes = schematicBoosts[context.schematic_type] || []; if (relevantTypes.some(type => pattern.description.toUpperCase().includes(type))) { contextScore += 0.1; } } // Rail system type context if (context.rail_system_type) { const railSystemBoosts = { 'METRO': ['converter', 'circuit_breaker', 'contactor'], 'HIGH_SPEED': ['transformer', 'converter', 'signal'], 'FREIGHT': ['transformer', 'circuit_breaker', 'junction'] }; const relevantComponents = railSystemBoosts[context.rail_system_type] || []; if (relevantComponents.some(comp => pattern.description.toLowerCase().includes(comp))) { contextScore += 0.08; } } // Voltage level context if (context.voltage_level) { if (context.voltage_level > 1000 && pattern.description.includes('transformer')) { contextScore += 0.05; // High voltage systems likely have transformers } if (context.voltage_level < 1000 && pattern.description.includes('contactor')) { contextScore += 0.05; // Low voltage systems likely have contactors } } // Surrounding text context if (context.surrounding_text) { const contextClues = pattern.context_clues || []; const surroundingText = context.surrounding_text.join(' ').toLowerCase(); const matchingClues = contextClues.filter(clue => surroundingText.includes(clue.toLowerCase())); contextScore += matchingClues.length * 0.02; } return Math.min(contextScore, 0.3); // Cap context boost at 30% } /** * Combine different confidence scores */ combineConfidences(patternConfidence, ocrConfidence, contextScore) { const baseConfidence = (patternConfidence * this.PATTERN_CONFIDENCE_WEIGHT) + (ocrConfidence * this.OCR_CONFIDENCE_WEIGHT); return Math.min(baseConfidence + contextScore, 1.0); } /** * Apply context analysis to filter and improve matches */ applyContextAnalysis(matches, ocrResult, context) { // Group matches by component ID to handle duplicates const matchGroups = new Map(); for (const match of matches) { const key = match.component_id; if (!matchGroups.has(key)) { matchGroups.set(key, []); } matchGroups.get(key).push(match); } const filteredMatches = []; // For each group, select the best match for (const [componentId, groupMatches] of matchGroups) { if (groupMatches.length === 0) continue; // Sort by confidence (highest first) groupMatches.sort((a, b) => b.confidence - a.confidence); const bestMatch = groupMatches[0]; // Apply additional filtering if (bestMatch && this.validateComponentMatch(bestMatch, ocrResult, context)) { filteredMatches.push(bestMatch); } } return filteredMatches; } /** * Validate that a component match makes sense in context */ validateComponentMatch(match, ocrResult, context) { // Basic validation: minimum OCR confidence if (match.ocr_word.confidence < 0.5) { return false; } // Validate component ID format if (!this.isValidComponentId(match.component_id, match.type)) { return false; } // Context-specific validation if (context?.schematic_type) { return this.isComponentAppropriateForSchematic(match.type, context.schematic_type); } return true; } /** * Check if component ID format is valid for the component type */ isValidComponentId(componentId, type) { const id = componentId.toUpperCase(); // Rail-specific validation rules switch (type) { case ComponentType.SIGNAL_REFERENCE: return /^\d{6,8}$/.test(id); // Signal references must be 6-8 digits case ComponentType.CONVERTER: return /^(A|CONV|INV|REC)\d+[A-Z]?$/.test(id); case ComponentType.CIRCUIT_BREAKER: return /^(CB|Q|BR|MCB)\d+[A-Z]?$/.test(id); case ComponentType.TRANSFORMER: return /^(TR?|TRANS|TX)\d+[A-Z]?$/.test(id); default: return id.length >= 2 && id.length <= 12; } } /** * Check if component type is appropriate for schematic type */ isComponentAppropriateForSchematic(componentType, schematicType) { const appropriateComponents = { 'traction_power': [ ComponentType.CONVERTER, ComponentType.TRANSFORMER, ComponentType.CIRCUIT_BREAKER, ComponentType.CONTACTOR, ComponentType.FUSE, ComponentType.AMMETER, ComponentType.VOLTMETER ], 'signaling': [ ComponentType.SIGNAL_REFERENCE, ComponentType.SIGNAL_MODULE, ComponentType.RELAY, ComponentType.COMMUNICATION_MODULE, ComponentType.TERMINAL_BLOCK ], 'control': [ ComponentType.CONTACTOR, ComponentType.RELAY, ComponentType.SWITCH, ComponentType.PUSHBUTTON, ComponentType.INDICATOR, ComponentType.TERMINAL_BLOCK ], 'auxiliary': [ ComponentType.TRANSFORMER, ComponentType.CIRCUIT_BREAKER, ComponentType.FUSE, ComponentType.CONTACTOR, ComponentType.JUNCTION_BOX ] }; const allowedTypes = appropriateComponents[schematicType]; return !allowedTypes || allowedTypes.includes(componentType); } /** * Create ElectricalComponent object from match */ createElectricalComponent(match, context) { const specification = RAIL_COMPONENT_SPECIFICATIONS[match.type]; const category = this.determineComponentCategory(match.type); const safetyLevel = this.determineSafetyLevel(match.type, specification); const component = { id: match.component_id, type: match.type, category, location: match.location, confidence: match.confidence, classification: { type: match.type, category, confidence: match.confidence, specifications: specification, safety_level: safetyLevel }, specifications: specification, safety_level: safetyLevel, compliance_status: 'REQUIRES_REVIEW' }; // Add context-specific properties if (context?.rail_system_type) { component.rail_system_type = context.rail_system_type; } return component; } /** * Determine component category based on type */ determineComponentCategory(type) { const categoryMap = { [ComponentType.CONVERTER]: ComponentCategory.TRACTION_POWER, [ComponentType.TRANSFORMER]: ComponentCategory.TRACTION_POWER, [ComponentType.INVERTER]: ComponentCategory.TRACTION_POWER, [ComponentType.RECTIFIER]: ComponentCategory.TRACTION_POWER, [ComponentType.CIRCUIT_BREAKER]: ComponentCategory.PROTECTION, [ComponentType.FUSE]: ComponentCategory.PROTECTION, [ComponentType.SURGE_PROTECTOR]: ComponentCategory.PROTECTION, [ComponentType.RELAY]: ComponentCategory.CONTROL, [ComponentType.CONTACTOR]: ComponentCategory.CONTROL, [ComponentType.SWITCH]: ComponentCategory.CONTROL, [ComponentType.PUSHBUTTON]: ComponentCategory.CONTROL, [ComponentType.INDICATOR]: ComponentCategory.CONTROL, [ComponentType.SIGNAL_MODULE]: ComponentCategory.SIGNALING, [ComponentType.SIGNAL_REFERENCE]: ComponentCategory.SIGNALING, [ComponentType.COMMUNICATION_MODULE]: ComponentCategory.COMMUNICATION, [ComponentType.AMMETER]: ComponentCategory.MEASUREMENT, [ComponentType.VOLTMETER]: ComponentCategory.MEASUREMENT, [ComponentType.FREQUENCY_METER]: ComponentCategory.MEASUREMENT, [ComponentType.POWER_METER]: ComponentCategory.MEASUREMENT, [ComponentType.JUNCTION_BOX]: ComponentCategory.AUXILIARY_POWER, [ComponentType.TERMINAL_BLOCK]: ComponentCategory.AUXILIARY_POWER, [ComponentType.CONNECTOR]: ComponentCategory.AUXILIARY_POWER, [ComponentType.CABLE]: ComponentCategory.AUXILIARY_POWER, [ComponentType.UNKNOWN]: ComponentCategory.UNCLASSIFIED, [ComponentType.OTHER]: ComponentCategory.UNCLASSIFIED }; return categoryMap[type] || ComponentCategory.UNCLASSIFIED; } /** * Determine safety level based on component type and specifications */ determineSafetyLevel(type, spec) { // High-risk components if ([ComponentType.CONVERTER, ComponentType.TRANSFORMER].includes(type)) { return SafetyLevel.CRITICAL; } if (type === ComponentType.CIRCUIT_BREAKER) { return SafetyLevel.HIGH; } // Check voltage rating if (spec.voltage_rating && spec.voltage_rating.max > 1000) { return SafetyLevel.HIGH; } if (spec.voltage_rating && spec.voltage_rating.max > 230) { return SafetyLevel.MEDIUM; } return SafetyLevel.LOW; } /** * Remove duplicate components (same ID, overlapping locations) */ deduplicateComponents(components) { const unique = new Map(); for (const component of components) { const existing = unique.get(component.id); if (!existing || component.confidence > existing.confidence) { unique.set(component.id, component); } } return Array.from(unique.values()).sort((a, b) => b.confidence - a.confidence); } /** * Get classification statistics */ getClassificationStats(components) { const stats = { total: components.length, by_type: {}, by_category: {}, by_safety_level: {}, average_confidence: 0 }; let totalConfidence = 0; for (const component of components) { // Count by type stats.by_type[component.type] = (stats.by_type[component.type] || 0) + 1; // Count by category stats.by_category[component.category] = (stats.by_category[component.category] || 0) + 1; // Count by safety level stats.by_safety_level[component.safety_level] = (stats.by_safety_level[component.safety_level] || 0) + 1; totalConfidence += component.confidence; } stats.average_confidence = components.length > 0 ? totalConfidence / components.length : 0; return stats; } } // Export singleton instance export const electricalComponentClassifier = new ElectricalComponentClassifier(); //# sourceMappingURL=component-classifier.js.map