/** * Urban Ontology Validation * * Testar om relationerna ger nytta med verkliga observationer. * * Exempel: * - Om ett elskåp markeras som ur funktion – vilka objekt påverkas? * - Om en gatlykta försvinner – vilka vägavsnitt tappar belysning? * - Om ett träd växer – vilka skyltar riskerar att skymmas? */ const UrbanOntology = require('../urban-ontology/ontology'); class OntologyValidation { constructor() { this.ontology = new UrbanOntology(); this.results = []; } /** * ============================================================ * TEST 1: Power Failure Impact * ============================================================ */ testPowerFailure() { console.log('\n=== TEST 1: Power Failure Impact ===\n'); // Skapa scenario: Elskåp EC-001 går sönder const cabinetId = 'cabinet_sukhumvit_001'; // Lägg till relationer this.ontology.addRelation(cabinetId, 'lamp_sukhumvit_001', 'powers', { voltage: 230, capacity: 1000, }); this.ontology.addRelation(cabinetId, 'lamp_sukhumvit_002', 'powers', { voltage: 230, capacity: 1000, }); this.ontology.addRelation(cabinetId, 'lamp_sukhumvit_003', 'powers', { voltage: 230, capacity: 1000, }); this.ontology.addRelation('lamp_sukhumvit_001', 'road_sukhumvit_001', 'illuminates', { intensity: 50, coverage: 10, }); this.ontology.addRelation('lamp_sukhumvit_002', 'road_sukhumvit_002', 'illuminates', { intensity: 50, coverage: 10, }); // Kör impact analysis const impact = this.ontology.impactAnalysis(cabinetId, 'power'); console.log(`Source: ${impact.source}`); console.log(`Failure type: ${impact.failureType}`); console.log(`Impacted objects: ${impact.impactCount}`); console.log(`Objects: ${impact.impactedObjects.join(', ')}`); // Validering const expectedImpacted = 4; // 3 lamps + 2 roads (via illuminates) const passed = impact.impactCount >= 3; // Minst 3 lampor this.results.push({ test: 'Power Failure Impact', passed, impactCount: impact.impactCount, expectedMin: 3, }); console.log(`\nResult: ${passed ? 'PASS' : 'FAIL'}`); console.log(`Expected: At least 3 impacted objects`); console.log(`Actual: ${impact.impactCount} impacted objects`); return impact; } /** * ============================================================ * TEST 2: Street Lamp Outage * ============================================================ */ testStreetLampOutage() { console.log('\n=== TEST 2: Street Lamp Outage ===\n'); // Scenario: Gatlykta SL-042 släcks const lampId = 'lamp_sukhumvit_042'; // Lägg till relationer this.ontology.addRelation(lampId, 'road_sukhumvit_042', 'illuminates', { intensity: 50, coverage: 15, }); this.ontology.addRelation(lampId, 'crosswalk_sukhumvit_042', 'illuminates', { intensity: 30, coverage: 5, }); this.ontology.addRelation('traffic_light_sukhumvit_042', 'crosswalk_sukhumvit_042', 'regulates', { regulationType: 'pedestrian_crossing', }); // Kör impact analysis const impact = this.ontology.impactAnalysis(lampId, 'lighting'); console.log(`Source: ${impact.source}`); console.log(`Failure type: ${impact.failureType}`); console.log(`Impacted objects: ${impact.impactCount}`); console.log(`Objects: ${impact.impactedObjects.join(', ')}`); // Validering const hasRoad = impact.impactedObjects.some(id => id.includes('road')); const hasCrosswalk = impact.impactedObjects.some(id => id.includes('crosswalk')); const passed = hasRoad && hasCrosswalk; this.results.push({ test: 'Street Lamp Outage', passed, hasRoad, hasCrosswalk, }); console.log(`\nResult: ${passed ? 'PASS' : 'FAIL'}`); console.log(`Road impacted: ${hasRoad}`); console.log(`Crosswalk impacted: ${hasCrosswalk}`); return impact; } /** * ============================================================ * TEST 3: Tree Growth Risk * ============================================================ */ testTreeGrowthRisk() { console.log('\n=== TEST 3: Tree Growth Risk ===\n'); // Scenario: Träd växer och riskerar skymma skylt const treeId = 'tree_sukhumvit_017'; // Lägg till relationer this.ontology.addRelation(treeId, 'sign_sukhumvit_017', 'may_obscure', { currentObscuration: 0.3, growthRate: 0.5, // meters/year projectedObscuration: 0.85, }); this.ontology.addRelation(treeId, 'sign_sukhumvit_018', 'may_obscure', { currentObscuration: 0.1, growthRate: 0.3, projectedObscuration: 0.45, }); // Kör growth risk analysis const risk = this.ontology.growthRiskAnalysis(treeId, 5); console.log(`Vegetation: ${risk.vegetationId}`); console.log(`Time horizon: ${risk.timeHorizonYears} years`); console.log(`High risk count: ${risk.highRiskCount}`); console.log('\nRisks:'); risk.risks.forEach(r => { console.log(` ${r.targetId}: ${r.riskLevel}`); console.log(` Current: ${r.currentObscuration}`); console.log(` Projected: ${r.projectedObscuration}`); console.log(` Action: ${r.recommendedAction}`); }); // Validering const hasHighRisk = risk.highRiskCount > 0; const hasAction = risk.risks.some(r => r.recommendedAction.includes('Prune')); const passed = hasHighRisk && hasAction; this.results.push({ test: 'Tree Growth Risk', passed, highRiskCount: risk.highRiskCount, hasAction, }); console.log(`\nResult: ${passed ? 'PASS' : 'FAIL'}`); console.log(`High risk found: ${hasHighRisk}`); console.log(`Action recommended: ${hasAction}`); return risk; } /** * ============================================================ * TEST 4: Path Analysis * ============================================================ */ testPathAnalysis() { console.log('\n=== TEST 4: Path Analysis ===\n'); // Scenario: Hitta väg från elskåp till väg const cabinetId = 'cabinet_sukhumvit_001'; const roadId = 'road_sukhumvit_001'; // Kör path analysis const path = this.ontology.findPath(cabinetId, roadId, 5); console.log(`From: ${cabinetId}`); console.log(`To: ${roadId}`); console.log(`Found: ${path.found}`); console.log(`Length: ${path.length}`); if (path.found) { console.log('\nPath:'); path.path.forEach((step, i) => { console.log(` Step ${i + 1}: ${step.objectId} via ${step.relation?.type || 'direct'}`); }); } // Validering const passed = path.found && path.length > 0; this.results.push({ test: 'Path Analysis', passed, pathLength: path.length, found: path.found, }); console.log(`\nResult: ${passed ? 'PASS' : 'FAIL'}`); console.log(`Path found: ${path.found}`); console.log(`Path length: ${path.length}`); return path; } /** * ============================================================ * TEST 5: Network Analysis * ============================================================ */ testNetworkAnalysis() { console.log('\n=== TEST 5: Network Analysis ===\n'); // Scenario: Hitta alla objekt i nätverket runt elskåp const cabinetId = 'cabinet_sukhumvit_001'; // Kör network analysis const network = this.ontology.networkAnalysis(cabinetId, { maxDepth: 3 }); console.log(`Seed: ${network.seedObjectId}`); console.log(`Network size: ${network.networkSize}`); console.log(`Objects: ${network.objects.join(', ')}`); // Validering const hasLamps = network.objects.some(id => id.includes('lamp')); const hasRoads = network.objects.some(id => id.includes('road')); const passed = hasLamps && hasRoads; this.results.push({ test: 'Network Analysis', passed, networkSize: network.networkSize, hasLamps, hasRoads, }); console.log(`\nResult: ${passed ? 'PASS' : 'FAIL'}`); console.log(`Network size: ${network.networkSize}`); console.log(`Has lamps: ${hasLamps}`); console.log(`Has roads: ${hasRoads}`); return network; } /** * ============================================================ * SUMMARY * ============================================================ */ generateReport() { console.log('\n╔════════════════════════════════════════════════════════════╗'); console.log('║ ONTOLOGY VALIDATION REPORT ║'); console.log('╚════════════════════════════════════════════════════════════╝\n'); const passed = this.results.filter(r => r.passed).length; const total = this.results.length; console.log(`Tests run: ${total}`); console.log(`Passed: ${passed}`); console.log(`Failed: ${total - passed}`); console.log(`Success rate: ${(passed / total * 100).toFixed(1)}%\n`); console.log('Detailed Results:'); this.results.forEach((result, i) => { console.log(`\n${i + 1}. ${result.test}`); console.log(` Status: ${result.passed ? 'PASS' : 'FAIL'}`); if (result.impactCount !== undefined) { console.log(` Impact count: ${result.impactCount}`); } if (result.highRiskCount !== undefined) { console.log(` High risk count: ${result.highRiskCount}`); } if (result.pathLength !== undefined) { console.log(` Path length: ${result.pathLength}`); } if (result.networkSize !== undefined) { console.log(` Network size: ${result.networkSize}`); } }); console.log('\n' + '='.repeat(60)); console.log(`OVERALL: ${passed === total ? 'ALL TESTS PASSED' : 'SOME TESTS FAILED'}`); console.log('='.repeat(60)); return { total, passed, failed: total - passed, successRate: passed / total, results: this.results, }; } } // Run validation if (require.main === module) { const validation = new OntologyValidation(); console.log('╔════════════════════════════════════════════════════════════╗'); console.log('║ URBAN ONTOLOGY VALIDATION ║'); console.log('╚════════════════════════════════════════════════════════════╝'); validation.testPowerFailure(); validation.testStreetLampOutage(); validation.testTreeGrowthRisk(); validation.testPathAnalysis(); validation.testNetworkAnalysis(); const report = validation.generateReport(); console.log('\n✅ Ontology Validation complete!'); } module.exports = OntologyValidation;