/** * QUIXZOOM Replay Engine + UKG Integration * * Kopplar Replay Engine till Urban Knowledge Graph för: * - Spela upp historiska händelser * - Jämföra algoritm-versioner * - Debugga i efterhand */ const ReplayEngine = require('./engine'); const UKG = require('../urban-knowledge-graph/ukg'); const ObjectIdentityPipeline = require('../identity-engine/pipeline'); const ChangeDetectionEngine = require('../change-detection/engine'); class UKGReplayIntegration { constructor() { this.ukg = new UKG(); this.oie = new ObjectIdentityPipeline(); this.changeDetection = new ChangeDetectionEngine(); this.replay = new ReplayEngine({ speed: 100 }); this.setupHandlers(); } setupHandlers() { // Zoomer startar this.replay.registerHandler('zoomer_started', async (payload) => { console.log(` → Zoomer ${payload.zoomerId} started`); return { status: 'recording' }; }); // Observation skapad this.replay.registerHandler('observation_created', async (payload, context) => { console.log(` → Observation: ${payload.objectType}`); // Kör genom OIE const identityResult = await this.oie.process({ id: payload.observationId, objectType: payload.objectType, location: payload.location, timestamp: payload.timestamp, attributes: payload.attributes, }); return { observationId: payload.observationId, identityAction: identityResult.action, objectId: identityResult.objectId, }; }); // Identity matchad this.replay.registerHandler('identity_matched', async (payload, context) => { console.log(` → Identity: ${payload.objectId} (${(payload.confidence * 100).toFixed(0)}%)`); // Hämta föregående observation const previousObs = this.findPreviousObservation(payload.objectId); const currentObs = this.findObservation(payload.observationId); if (previousObs && currentObs) { // Kör change detection const changes = this.changeDetection.detectChanges( payload.objectId, currentObs, previousObs ); return { objectId: payload.objectId, confidence: payload.confidence, changes: changes.changes, severity: changes.severity, }; } return { objectId: payload.objectId, confidence: payload.confidence }; }); // Evidence tillagd this.replay.registerHandler('evidence_added', async (payload) => { console.log(` → Evidence: ${payload.evidenceType}`); // Lägg till i UKG this.ukg.addEvidence(payload.objectId, { id: payload.evidenceId, type: payload.evidenceType, source: payload.source, timestamp: payload.timestamp, }); return { evidenceId: payload.evidenceId }; }); // Kunskapsgap upptäckt this.replay.registerHandler('knowledge_gap_detected', async (payload) => { console.log(` → Gap: ${payload.gapType} (priority: ${payload.priority})`); // Skapa uppdrag const mission = { id: `mission_${Date.now()}`, type: 'verify', target: payload.objectId, gapType: payload.gapType, priority: payload.priority, }; return { mission }; }); // Uppdrag skapat this.replay.registerHandler('mission_created', async (payload) => { console.log(` → Mission: ${payload.missionType}`); return { missionId: payload.missionId }; }); // Instruktion skickad this.replay.registerHandler('instruction_sent', async (payload) => { console.log(` → Instruction: "${payload.instruction}"`); return { sent: true }; }); } findPreviousObservation(objectId) { // Hämta senaste observation för objektet // Förenklad implementation return null; } findObservation(observationId) { // Hämta observation // Förenklad implementation return null; } async replayEvents(events) { console.log('=== UKG REPLAY INTEGRATION ===\n'); this.replay.loadEvents(events); await this.replay.play(); console.log('\n=== REPLAY COMPLETE ==='); console.log('Stats:', this.replay.getStats()); return this.replay.results; } async compareAlgorithms(events, versionA, versionB) { console.log('=== ALGORITHM COMPARISON ===\n'); const comparison = await this.replay.compareVersions( events, versionA.handlers, versionB.handlers ); console.log('\nComparison:'); console.log(` Total events: ${comparison.comparison.totalEvents}`); console.log(` Differences: ${comparison.comparison.differences}`); console.log(` Difference rate: ${comparison.comparison.differenceRate}`); if (comparison.comparison.differences > 0) { console.log('\n Details:'); for (const diff of comparison.comparison.details.slice(0, 5)) { console.log(` [${diff.index}] ${diff.type}:`); console.log(` A: ${JSON.stringify(diff.versionA)}`); console.log(` B: ${JSON.stringify(diff.versionB)}`); } } return comparison; } } // Exportera module.exports = UKGReplayIntegration; // Demo if (require.main === module) { const integration = new UKGReplayIntegration(); // Skapa scenario const baseTime = Date.now(); const events = [ { id: 'evt_001', type: 'zoomer_started', timestamp: baseTime, payload: { zoomerId: 'zoomer_00001' } }, { id: 'evt_002', type: 'observation_created', timestamp: baseTime + 6000, payload: { observationId: 'obs_001', objectType: 'street_lamp', location: { lat: 13.725, lng: 100.555 }, timestamp: baseTime + 6000, attributes: { material: 'steel', height: 7.8, rust: false } } }, { id: 'evt_003', type: 'identity_matched', timestamp: baseTime + 7000, payload: { observationId: 'obs_001', objectId: 'street_lamp_000001', confidence: 0.94 } }, { id: 'evt_004', type: 'evidence_added', timestamp: baseTime + 7000, payload: { evidenceType: 'image', evidenceId: 'ev_001', objectId: 'street_lamp_000001', source: 'zoomer_00001' } }, { id: 'evt_005', type: 'knowledge_gap_detected', timestamp: baseTime + 8000, payload: { gapType: 'angle', gapId: 'gap_001', objectId: 'street_lamp_000001', priority: 'medium' } }, { id: 'evt_006', type: 'mission_created', timestamp: baseTime + 9000, payload: { missionType: 'verify_angle', missionId: 'mission_001' } }, { id: 'evt_007', type: 'instruction_sent', timestamp: baseTime + 10000, payload: { instruction: 'Gå två meter närmare' } }, ]; integration.replayEvents(events).then(() => { console.log('\n=== UKG STATE ==='); console.log(`Objects: ${integration.ukg.objects.size}`); console.log(`Observations: ${integration.ukg.observations.size}`); console.log(`Evidence: ${integration.ukg.evidence.size}`); }); }