@typecad/kicad-symbols
Version:
Intelligent fuzzy search for KiCad symbols with CLI interface
326 lines • 12.5 kB
JavaScript
/**
* Local data processor for KiCad symbol files
* Orchestrates the complete pipeline from symbol discovery to data extraction
*/
import { KiCAD } from '../../kicad.js';
import { KiCadSymbolFileScanner } from './KiCadSymbolFileScanner.js';
import { KiCadSymbolExtractor } from './KiCadSymbolExtractor.js';
/**
* Default processing configuration
*/
const DEFAULT_PROCESSING_CONFIG = {
scanOptions: {
recursive: true,
maxDepth: 10,
followSymlinks: false,
excludePatterns: ['.git', '.svn', '__pycache__', 'backup', 'cache']
},
extractionOptions: {
includeWithoutDescription: true,
includeWithoutKeywords: true,
continueOnError: true,
strictValidation: false
},
enableProgress: true,
progressInterval: 1000
};
/**
* Local data processor for KiCad symbols
*/
export class KiCadLocalDataProcessor {
kicad;
config;
progressCallback;
statistics;
startTime = 0;
constructor(config = {}) {
this.kicad = new KiCAD();
this.config = { ...DEFAULT_PROCESSING_CONFIG, ...config };
this.statistics = this.initializeStatistics();
}
/**
* Process all local KiCad symbol files and extract symbol data
* @param progressCallback - Optional callback for progress updates
* @returns Promise resolving to array of extracted symbols
*/
async processLocalSymbols(progressCallback) {
this.startTime = Date.now();
this.progressCallback = progressCallback;
this.statistics = this.initializeStatistics();
try {
// Phase 1: Initialize and check KiCad installation
this.reportProgress({
phase: 'initializing',
step: 'Checking KiCad installation',
percentage: 5,
filesProcessed: 0,
totalFiles: 0,
symbolsExtracted: 0
});
const symbolsPath = await this.findSymbolsPath();
this.statistics.symbolsPath = symbolsPath;
// Phase 2: Scan for symbol files
this.reportProgress({
phase: 'scanning',
step: 'Scanning for symbol files',
percentage: 10,
filesProcessed: 0,
totalFiles: 0,
symbolsExtracted: 0
});
const symbolFiles = await this.scanForSymbolFiles(symbolsPath);
this.updateScanStatistics(symbolFiles);
// Phase 3: Extract symbols from files
this.reportProgress({
phase: 'extracting',
step: 'Extracting symbol definitions',
percentage: 20,
filesProcessed: 0,
totalFiles: symbolFiles.length,
symbolsExtracted: 0
});
const extractedSymbols = await this.extractSymbols(symbolFiles);
this.updateExtractionStatistics();
// Phase 4: Complete
this.reportProgress({
phase: 'completed',
step: 'Processing completed successfully',
percentage: 100,
filesProcessed: symbolFiles.length,
totalFiles: symbolFiles.length,
symbolsExtracted: extractedSymbols.length
});
this.statistics.totalProcessingTime = Date.now() - this.startTime;
return extractedSymbols;
}
catch (error) {
this.reportProgress({
phase: 'error',
step: `Error: ${error instanceof Error ? error.message : String(error)}`,
percentage: 0,
filesProcessed: 0,
totalFiles: 0,
symbolsExtracted: 0
});
this.statistics.totalProcessingTime = Date.now() - this.startTime;
throw error;
}
}
/**
* Get processing statistics from the last operation
* @returns Processing statistics
*/
getStatistics() {
return { ...this.statistics };
}
/**
* Check if local KiCad symbols are available
* @returns Promise resolving to true if symbols are available
*/
async areLocalSymbolsAvailable() {
try {
const symbolsPath = await this.findSymbolsPath();
return await this.kicad.hasLocalSymbols();
}
catch (error) {
return false;
}
}
/**
* Get the path to KiCad symbols directory
* @returns Promise resolving to symbols path
*/
async getSymbolsPath() {
return await this.findSymbolsPath();
}
/**
* Find the KiCad symbols directory path
* @returns Promise resolving to symbols directory path
* @private
*/
async findSymbolsPath() {
// Use custom path if provided
if (this.config.customSymbolsPath) {
return this.config.customSymbolsPath;
}
// Use auto-detected path
const symbolsPath = this.kicad.getSymbolsPath();
if (!symbolsPath) {
throw new Error('KiCad installation not found or symbols directory not accessible');
}
this.statistics.kicadPath = symbolsPath;
return symbolsPath;
}
/**
* Scan for symbol files in the symbols directory
* @param symbolsPath - Path to symbols directory
* @returns Promise resolving to array of symbol file information
* @private
*/
async scanForSymbolFiles(symbolsPath) {
const scanner = new KiCadSymbolFileScanner(symbolsPath, this.config.scanOptions);
const scanStartTime = Date.now();
const symbolFiles = await scanner.scanForSymbolFiles();
const scanStats = scanner.getStatistics();
this.reportProgress({
phase: 'scanning',
step: `Found ${symbolFiles.length} symbol files`,
percentage: 15,
filesProcessed: 0,
totalFiles: symbolFiles.length,
symbolsExtracted: 0
});
this.statistics.scanStats = {
filesFound: scanStats.totalFiles,
directoriesScanned: scanStats.directoriesScanned,
scanTime: Date.now() - scanStartTime,
errors: scanStats.errors
};
return symbolFiles;
}
/**
* Extract symbols from symbol files with progress reporting
* @param symbolFiles - Array of symbol files to process
* @returns Promise resolving to array of extracted symbols
* @private
*/
async extractSymbols(symbolFiles) {
const extractor = new KiCadSymbolExtractor(this.config.extractionOptions);
const extractedSymbols = [];
let progressInterval;
let lastProgressTime = Date.now();
// Set up progress reporting for extraction
if (this.config.enableProgress && this.progressCallback) {
progressInterval = setInterval(() => {
const progress = this.calculateExtractionProgress(extractedSymbols.length, symbolFiles.length);
this.reportProgress(progress);
}, this.config.progressInterval);
}
try {
// Process files in batches for better progress reporting
const batchSize = Math.max(1, Math.floor(symbolFiles.length / 20)); // 20 progress updates
for (let i = 0; i < symbolFiles.length; i += batchSize) {
const batch = symbolFiles.slice(i, i + batchSize);
const batchSymbols = await extractor.extractSymbolsFromFiles(batch);
extractedSymbols.push(...batchSymbols);
// Report progress
if (this.config.enableProgress && Date.now() - lastProgressTime > 500) {
const progress = this.calculateExtractionProgress(i + batch.length, symbolFiles.length, extractedSymbols.length);
this.reportProgress(progress);
lastProgressTime = Date.now();
}
}
return extractedSymbols;
}
finally {
if (progressInterval) {
clearInterval(progressInterval);
}
}
}
/**
* Calculate extraction progress information
* @param filesProcessed - Number of files processed
* @param totalFiles - Total number of files
* @param symbolsExtracted - Number of symbols extracted
* @returns Progress information
* @private
*/
calculateExtractionProgress(filesProcessed, totalFiles, symbolsExtracted = 0) {
const percentage = Math.min(95, 20 + (filesProcessed / totalFiles) * 75);
const elapsedTime = Date.now() - this.startTime;
const estimatedTotal = totalFiles > 0 ? (elapsedTime / filesProcessed) * totalFiles : 0;
const estimatedTimeRemaining = Math.max(0, estimatedTotal - elapsedTime);
return {
phase: 'extracting',
step: `Processing file ${filesProcessed} of ${totalFiles}`,
percentage,
filesProcessed,
totalFiles,
symbolsExtracted,
estimatedTimeRemaining
};
}
/**
* Report progress to callback if available
* @param progress - Progress information
* @private
*/
reportProgress(progress) {
if (this.progressCallback) {
this.progressCallback(progress);
}
}
/**
* Update scan statistics from scanner results
* @param symbolFiles - Symbol files found
* @private
*/
updateScanStatistics(symbolFiles) {
// Statistics are updated in scanForSymbolFiles method
}
/**
* Update extraction statistics from extractor results
* @private
*/
updateExtractionStatistics() {
// Statistics are updated during extraction process
}
/**
* Initialize processing statistics
* @returns Initial statistics object
* @private
*/
initializeStatistics() {
return {
totalProcessingTime: 0,
scanStats: {
filesFound: 0,
directoriesScanned: 0,
scanTime: 0,
errors: []
},
extractionStats: {
filesProcessed: 0,
filesWithErrors: 0,
symbolsExtracted: 0,
symbolsWithDescriptions: 0,
symbolsWithKeywords: 0,
extractionTime: 0,
errors: []
}
};
}
/**
* Get a human-readable summary of the processing results
* @returns Formatted summary string
*/
getProcessingSummary() {
const stats = this.statistics;
const totalTimeSeconds = (stats.totalProcessingTime / 1000).toFixed(2);
const scanTimeSeconds = (stats.scanStats.scanTime / 1000).toFixed(2);
const extractionTimeSeconds = (stats.extractionStats.extractionTime / 1000).toFixed(2);
let summary = `KiCad Local Processing Summary:\n`;
summary += ` Total processing time: ${totalTimeSeconds} seconds\n`;
summary += ` KiCad path: ${stats.kicadPath || 'Not detected'}\n`;
summary += ` Symbols path: ${stats.symbolsPath || 'Not found'}\n\n`;
summary += `Scanning Results:\n`;
summary += ` Files found: ${stats.scanStats.filesFound}\n`;
summary += ` Directories scanned: ${stats.scanStats.directoriesScanned}\n`;
summary += ` Scan time: ${scanTimeSeconds} seconds\n`;
summary += ` Scan errors: ${stats.scanStats.errors.length}\n\n`;
summary += `Extraction Results:\n`;
summary += ` Files processed: ${stats.extractionStats.filesProcessed}\n`;
summary += ` Files with errors: ${stats.extractionStats.filesWithErrors}\n`;
summary += ` Symbols extracted: ${stats.extractionStats.symbolsExtracted}\n`;
summary += ` Symbols with descriptions: ${stats.extractionStats.symbolsWithDescriptions}\n`;
summary += ` Symbols with keywords: ${stats.extractionStats.symbolsWithKeywords}\n`;
summary += ` Extraction time: ${extractionTimeSeconds} seconds\n`;
const totalErrors = stats.scanStats.errors.length + stats.extractionStats.errors.length;
if (totalErrors > 0) {
summary += `\nTotal errors encountered: ${totalErrors}\n`;
}
return summary;
}
}
//# sourceMappingURL=KiCadLocalDataProcessor.js.map