/** * Complete VIMS Demo * Demonstrates entire flow from object creation to alert generation */ const { ObjectDetectionService } = require('../src/services/objectDetection'); const { ChangeDetectionService } = require('../src/services/changeDetection'); const { RiskClassifierService } = require('../src/services/riskClassifier'); const sharp = require('sharp'); class VIMSCompleteDemo { constructor() { this.detectionService = new ObjectDetectionService(); this.changeService = new ChangeDetectionService(); this.riskService = new RiskClassifierService(); this.results = []; } log(step, data) { console.log(`\n${'='.repeat(70)}`); console.log(` ${step}`); console.log('='.repeat(70)); console.log(JSON.stringify(data, null, 2)); this.results.push({ step, data }); } async createBaselineImage() { // Create a "normal" ATM image const width = 640; const height = 640; // Gray background const image = Buffer.alloc(width * height * 3, 200); // Add components (simulated) const components = [ { name: 'card_reader', x: 220, y: 180, w: 200, h: 80, color: [100, 100, 100] }, { name: 'pin_pad', x: 240, y: 300, w: 160, h: 120, color: [80, 80, 80] }, { name: 'display', x: 200, y: 80, w: 240, h: 80, color: [50, 50, 50] }, { name: 'cash_dispenser', x: 260, y: 450, w: 120, h: 60, color: [120, 120, 120] }, { name: 'nfc_reader', x: 280, y: 530, w: 80, h: 40, color: [90, 90, 90] } ]; for (const comp of components) { for (let y = comp.y; y < comp.y + comp.h; y++) { for (let x = comp.x; x < comp.x + comp.w; x++) { const idx = (y * width + x) * 3; image[idx] = comp.color[0]; image[idx + 1] = comp.color[1]; image[idx + 2] = comp.color[2]; } } } return image; } async createModifiedImage(baseline) { // Create a modified version with a "skimmer" attached const modified = Buffer.from(baseline); const width = 640; // Add skimmer over card reader (slightly larger, different color) const skimmer = { x: 215, y: 175, w: 210, h: 90, color: [150, 50, 50] }; for (let y = skimmer.y; y < skimmer.y + skimmer.h; y++) { for (let x = skimmer.x; x < skimmer.x + skimmer.w; x++) { const idx = (y * width + x) * 3; modified[idx] = skimmer.color[0]; modified[idx + 1] = skimmer.color[1]; modified[idx + 2] = skimmer.color[2]; } } return modified; } async run() { console.log('\nšŸš€ VIMS Complete Demo'); console.log('=====================\n'); // Step 1: Create baseline console.log('šŸ“ø Step 1: Creating baseline image...'); const baselineImage = await this.createBaselineImage(); // Convert to JPEG for storage const baselineJpeg = await sharp(baselineImage, { raw: { width: 640, height: 640, channels: 3 } }).jpeg().toBuffer(); this.log('BASELINE IMAGE CREATED', { size: baselineJpeg.length, dimensions: '640x640', description: 'Normal ATM with all components' }); // Step 2: Object Detection on baseline console.log('šŸ” Step 2: Running object detection on baseline...'); const baselineResult = await this.detectionService.detect(baselineJpeg, 'atm'); this.log('BASELINE DETECTION', { modelVersion: baselineResult.modelVersion, inferenceTime: `${baselineResult.inferenceTime}ms`, detections: baselineResult.detections.map(d => ({ component: d.componentName, confidence: `${(d.confidence * 100).toFixed(1)}%`, critical: d.critical })) }); // Step 3: Create modified image (with skimmer) console.log('āš ļø Step 3: Creating modified image (with skimmer)...'); const modifiedImage = await this.createModifiedImage(baselineImage); const modifiedJpeg = await sharp(modifiedImage, { raw: { width: 640, height: 640, channels: 3 } }).jpeg().toBuffer(); this.log('MODIFIED IMAGE CREATED', { size: modifiedJpeg.length, description: 'ATM with skimmer device attached to card reader' }); // Step 4: Object Detection on modified console.log('šŸ” Step 4: Running object detection on modified image...'); const modifiedResult = await this.detectionService.detect(modifiedJpeg, 'atm'); this.log('MODIFIED DETECTION', { modelVersion: modifiedResult.modelVersion, inferenceTime: `${modifiedResult.inferenceTime}ms`, detections: modifiedResult.detections.map(d => ({ component: d.componentName, confidence: `${(d.confidence * 100).toFixed(1)}%`, critical: d.critical })) }); // Step 5: Change Detection console.log('⚔ Step 5: Running change detection...'); const changeResult = await this.changeService.compare(modifiedJpeg, baselineJpeg); this.log('CHANGE DETECTION RESULT', { isSignificant: changeResult.isSignificant, changeType: changeResult.changeType, confidence: `${(changeResult.confidence * 100).toFixed(1)}%`, metrics: { pixelDifference: `${(changeResult.metrics.pixelDifference * 100).toFixed(2)}%`, structuralDifference: `${(changeResult.metrics.structuralDifference * 100).toFixed(2)}%`, hashDifference: `${(changeResult.metrics.hashDifference * 100).toFixed(2)}%`, overallDifference: `${(changeResult.metrics.overallDifference * 100).toFixed(2)}%` } }); // Step 6: Anomaly Detection console.log('🚨 Step 6: Detecting anomalies...'); const anomalies = await this.detectionService.detectAnomalies( modifiedResult.detections, baselineResult.detections, 'atm' ); this.log('ANOMALY DETECTION', { anomalyCount: anomalies.length, anomalies: anomalies.map(a => ({ type: a.type, component: a.component, confidence: `${(a.confidence * 100).toFixed(1)}%`, severity: a.severity, description: a.description })) }); // Step 7: Risk Classification console.log('šŸŽÆ Step 7: Classifying risk...'); // Create mock observation const observation = { id: 'demo-obs-001', objectId: 'demo-atm-001', imageUrl: 'demo://modified.jpg' }; // Mark detections as anomalies const detectionsWithAnomalies = modifiedResult.detections.map(d => ({ ...d, isAnomaly: anomalies.some(a => a.component === d.componentType), anomalyType: anomalies.find(a => a.component === d.componentType)?.type || null, anomalyConfidence: anomalies.find(a => a.component === d.componentType)?.confidence || 0 })); const riskResult = this.riskService.processObservation( observation, detectionsWithAnomalies, 'atm' ); this.log('RISK CLASSIFICATION', { level: riskResult.level.toUpperCase(), reason: riskResult.reason, action: riskResult.action, details: riskResult.details?.map(d => ({ component: d.component, level: d.level, confidence: `${(d.confidence * 100).toFixed(1)}%` })) }); // Step 8: Alert Decision console.log('šŸ”” Step 8: Alert decision...'); const shouldAlert = ['orange', 'red'].includes(riskResult.level); this.log('ALERT DECISION', { shouldAlert, alertLevel: riskResult.level, title: shouldAlert ? `🚨 SECURITY ALERT: ${riskResult.reason}` : 'āœ… No action required', notificationChannels: shouldAlert ? ['email', 'sms', 'webhook'] : [], recommendedAction: riskResult.action }); // Summary console.log('\n' + '='.repeat(70)); console.log(' DEMO SUMMARY'); console.log('='.repeat(70)); console.log(`āœ… Baseline created with ${baselineResult.detections.length} components detected`); console.log(`āš ļø Modified image shows ${anomalies.length} anomalies`); console.log(`šŸ”“ Risk level: ${riskResult.level.toUpperCase()}`); console.log(`${shouldAlert ? '🚨 ALERT GENERATED' : 'āœ… No alert needed'}`); console.log(`ā±ļø Total processing time: ${baselineResult.inferenceTime + modifiedResult.inferenceTime + changeResult.processingTime}ms`); console.log('='.repeat(70) + '\n'); // Save results const fs = require('fs').promises; await fs.writeFile( 'demo/complete-demo-results.json', JSON.stringify(this.results, null, 2) ); console.log('šŸ“„ Results saved to demo/complete-demo-results.json\n'); return { baseline: baselineResult, modified: modifiedResult, changes: changeResult, anomalies, risk: riskResult, shouldAlert }; } } // Run if called directly if (require.main === module) { const demo = new VIMSCompleteDemo(); demo.run().catch(console.error); } module.exports = { VIMSCompleteDemo };