bae705aa97
- Add NFC ePassport roadmap (ICAO 9303, eIDAS) - Add TensorFlow.js edge face detection (BlazeFace) - Add structured audit logger (GDPR-compliant) - Risk scoring support Part of KYC Apple Native UX v1.1.0
716 lines
18 KiB
JavaScript
716 lines
18 KiB
JavaScript
/**
|
|
* QUIXZOOM Urban Intelligence Operating System (UIOS)
|
|
*
|
|
* Central styrning för hela QUIXZOOM:
|
|
* - Data Collection Engine
|
|
* - Mission Planner
|
|
* - Active Learning Engine
|
|
* - Urban Knowledge Graph
|
|
* - Object Identity Engine
|
|
* - Change Detection Engine
|
|
* - Dataset Manager
|
|
* - Model Registry
|
|
* - Training Pipeline
|
|
* - Deployment Manager
|
|
* - Analytics & Customer API
|
|
*
|
|
* Ett komplett operativsystem för kontinuerlig insamling,
|
|
* förståelse och uppdatering av information om den byggda miljön.
|
|
*/
|
|
|
|
const ObjectIdentityPipeline = require('../identity-engine/pipeline');
|
|
const ObjectIdentityPipelineV4 = require('../identity-engine/pipeline-v4');
|
|
const ChangeDetectionEngine = require('../change-detection/engine');
|
|
const UrbanFoundationDatasetManager = require('../dataset-manager/manager');
|
|
const UrbanIntelligenceMissionPlanner = require('../mission-planner/planner');
|
|
const WeakSupervisionPipeline = require('../weak-supervision/pipeline');
|
|
|
|
// Placeholder-klasser för moduler som inte finns än
|
|
class ActiveLearningEngine {
|
|
constructor() { this.status = 'ready'; }
|
|
}
|
|
|
|
class ShadowDeploymentManager {
|
|
constructor() { this.deployments = new Map(); }
|
|
async shadowDeploy(modelId) {
|
|
console.log(`[Shadow] Deploying ${modelId}`);
|
|
this.deployments.set(modelId, { mode: 'shadow', traffic: 0.1 });
|
|
}
|
|
async promote(modelId) {
|
|
const d = this.deployments.get(modelId);
|
|
if (d) { d.mode = 'production'; d.traffic = 1.0; }
|
|
}
|
|
}
|
|
|
|
class UrbanIntelligenceOperatingSystem {
|
|
constructor(config = {}) {
|
|
this.config = {
|
|
version: '1.0.0',
|
|
environment: config.environment || 'development',
|
|
...config,
|
|
};
|
|
|
|
// Lager
|
|
this.layers = {
|
|
// Lager 1: Datainsamling
|
|
dataCollection: {
|
|
captureEngine: null,
|
|
qualityControl: null,
|
|
},
|
|
|
|
// Lager 2: Intelligens
|
|
intelligence: {
|
|
missionPlanner: new UrbanIntelligenceMissionPlanner(),
|
|
activeLearning: new ActiveLearningEngine(),
|
|
weakSupervision: new WeakSupervisionPipeline(),
|
|
},
|
|
|
|
// Lager 3: Kunskap
|
|
knowledge: {
|
|
ukg: new UrbanKnowledgeGraph(),
|
|
oie: new ObjectIdentityPipelineV4(),
|
|
changeDetection: new ChangeDetectionEngine(),
|
|
},
|
|
|
|
// Lager 4: Data
|
|
data: {
|
|
datasetManager: new UrbanFoundationDatasetManager(),
|
|
modelRegistry: new ModelRegistry(),
|
|
},
|
|
|
|
// Lager 5: Drift
|
|
operations: {
|
|
trainingPipeline: new TrainingPipeline(),
|
|
deploymentManager: new ShadowDeploymentManager(),
|
|
},
|
|
|
|
// Lager 6: API
|
|
api: {
|
|
analytics: new AnalyticsEngine(),
|
|
customerAPI: new CustomerAPI(),
|
|
},
|
|
};
|
|
|
|
// Learning Queue
|
|
this.learningQueue = new LearningQueue();
|
|
|
|
// Event bus
|
|
this.events = new EventBus();
|
|
|
|
// Status
|
|
this.status = 'initializing';
|
|
this.startTime = Date.now();
|
|
}
|
|
|
|
async initialize() {
|
|
console.log('[UIOS] Initializing Urban Intelligence Operating System...');
|
|
console.log(`[UIOS] Version: ${this.config.version}`);
|
|
console.log(`[UIOS] Environment: ${this.config.environment}`);
|
|
|
|
// Initiera alla lager
|
|
await this.initializeLayers();
|
|
|
|
// Starta event listeners
|
|
this.setupEventListeners();
|
|
|
|
// Starta looper
|
|
this.startLoops();
|
|
|
|
this.status = 'running';
|
|
console.log('[UIOS] System ready\n');
|
|
|
|
return this.getStatus();
|
|
}
|
|
|
|
async initializeLayers() {
|
|
// Lager 1: Datainsamling
|
|
console.log('[UIOS] Layer 1: Data Collection');
|
|
|
|
// Lager 2: Intelligens
|
|
console.log('[UIOS] Layer 2: Intelligence');
|
|
|
|
// Lager 3: Kunskap
|
|
console.log('[UIOS] Layer 3: Knowledge');
|
|
|
|
// Lager 4: Data
|
|
console.log('[UIOS] Layer 4: Data');
|
|
|
|
// Lager 5: Drift
|
|
console.log('[UIOS] Layer 5: Operations');
|
|
|
|
// Lager 6: API
|
|
console.log('[UIOS] Layer 6: API');
|
|
}
|
|
|
|
setupEventListeners() {
|
|
// Observation insamlad
|
|
this.events.on('observation:captured', async (data) => {
|
|
await this.handleObservation(data);
|
|
});
|
|
|
|
// Mission skapad
|
|
this.events.on('mission:created', async (data) => {
|
|
await this.handleMission(data);
|
|
});
|
|
|
|
// Modell tränad
|
|
this.events.on('model:trained', async (data) => {
|
|
await this.handleModelTrained(data);
|
|
});
|
|
|
|
// Coverage uppdaterad
|
|
this.events.on('coverage:updated', async (data) => {
|
|
await this.handleCoverageUpdate(data);
|
|
});
|
|
}
|
|
|
|
startLoops() {
|
|
// Active Learning Loop (var 5:e minut)
|
|
setInterval(() => this.activeLearningLoop(), 5 * 60 * 1000);
|
|
|
|
// Mission Generation Loop (var 15:e minut)
|
|
setInterval(() => this.missionGenerationLoop(), 15 * 60 * 1000);
|
|
|
|
// Quality Control Loop (varje minut)
|
|
setInterval(() => this.qualityControlLoop(), 60 * 1000);
|
|
|
|
// Model Evaluation Loop (varje timme)
|
|
setInterval(() => this.modelEvaluationLoop(), 60 * 60 * 1000);
|
|
}
|
|
|
|
/**
|
|
* ============================================================
|
|
* ACTIVE LEARNING LOOP
|
|
* ============================================================
|
|
*/
|
|
|
|
async activeLearningLoop() {
|
|
console.log('[UIOS] Active Learning Loop...');
|
|
|
|
// 1. Hämta osäkra observationer
|
|
const uncertainObservations = this.learningQueue.getUncertain();
|
|
|
|
// 2. Generera missions
|
|
const missions = this.layers.intelligence.missionPlanner.generateMissions(
|
|
uncertainObservations.map(o => o.cityId)
|
|
);
|
|
|
|
// 3. Tilldela till Zoomers
|
|
for (const mission of missions) {
|
|
this.events.emit('mission:created', mission);
|
|
}
|
|
|
|
// 4. Uppdatera modell
|
|
if (this.shouldRetrain()) {
|
|
await this.retrainModel();
|
|
}
|
|
|
|
console.log(`[UIOS] Generated ${missions.length} missions`);
|
|
}
|
|
|
|
async missionGenerationLoop() {
|
|
console.log('[UIOS] Mission Generation Loop...');
|
|
|
|
for (const city of this.layers.data.datasetManager.listReferenceCities()) {
|
|
const report = this.layers.data.datasetManager.getCoverageReport(city.id);
|
|
|
|
if (report.gaps.length > 0) {
|
|
const missions = this.layers.intelligence.missionPlanner.generateMissions(city.id);
|
|
|
|
for (const mission of missions) {
|
|
this.learningQueue.add({
|
|
type: 'mission',
|
|
priority: this.calculatePriority(mission),
|
|
data: mission,
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
calculatePriority(mission) {
|
|
const score = mission.score?.total || 0;
|
|
|
|
if (score > 80) return 1; // Kritisk
|
|
if (score > 50) return 2; // Viktig
|
|
return 3; // Normal
|
|
}
|
|
|
|
async qualityControlLoop() {
|
|
console.log('[UIOS] Quality Control Loop...');
|
|
|
|
// Kontrollera nyligen insamlade observationer
|
|
const recent = this.learningQueue.getRecent(100);
|
|
|
|
for (const obs of recent) {
|
|
const quality = this.assessQuality(obs);
|
|
|
|
if (quality < 0.5) {
|
|
// Låg kvalitet — begär om
|
|
this.events.emit('quality:low', { observation: obs, quality });
|
|
}
|
|
}
|
|
}
|
|
|
|
assessQuality(observation) {
|
|
let score = 1.0;
|
|
|
|
// GPS-accuracy
|
|
if (observation.gpsAccuracy > 10) score *= 0.8;
|
|
if (observation.gpsAccuracy > 50) score *= 0.5;
|
|
|
|
// Bildkvalitet
|
|
if (observation.quality) {
|
|
if (observation.quality.blur > 0.5) score *= 0.7;
|
|
if (observation.quality.exposure < 0.3) score *= 0.8;
|
|
}
|
|
|
|
// Täckning
|
|
if (!observation.attributes || Object.keys(observation.attributes).length === 0) {
|
|
score *= 0.9;
|
|
}
|
|
|
|
return score;
|
|
}
|
|
|
|
async modelEvaluationLoop() {
|
|
console.log('[UIOS] Model Evaluation Loop...');
|
|
|
|
const registry = this.layers.data.modelRegistry;
|
|
const models = registry.listModels();
|
|
|
|
for (const model of models) {
|
|
if (model.status === 'deployed') {
|
|
const metrics = await registry.evaluateModel(model.id);
|
|
|
|
// Jämför med föregående version
|
|
const previous = registry.getPreviousVersion(model.id);
|
|
if (previous) {
|
|
const improvement = this.calculateImprovement(metrics, previous.metrics);
|
|
|
|
if (improvement > 0.05) {
|
|
// Signifikant förbättring
|
|
this.events.emit('model:improved', { model, improvement });
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
calculateImprovement(current, previous) {
|
|
if (!previous) return 0;
|
|
return (current.mAP - previous.mAP) / previous.mAP;
|
|
}
|
|
|
|
/**
|
|
* ============================================================
|
|
* HÄNDELSEHANTERING
|
|
* ============================================================
|
|
*/
|
|
|
|
async handleObservation(data) {
|
|
console.log(`[UIOS] Observation captured: ${data.id}`);
|
|
|
|
// 1. Kvalitetskontroll
|
|
const quality = this.assessQuality(data);
|
|
|
|
// 2. Object Identity Engine
|
|
const identity = await this.layers.knowledge.oie.process(data);
|
|
|
|
// 3. Urban Knowledge Graph
|
|
await this.layers.knowledge.ukg.addObservation(data, identity);
|
|
|
|
// 4. Change Detection
|
|
let changes = [];
|
|
try {
|
|
if (typeof this.layers.knowledge.changeDetection.detectChanges === 'function') {
|
|
const prevObs = this.layers.knowledge.ukg.objects.get(identity.objectId)?.observations?.slice(-2, -1)[0];
|
|
changes = this.layers.knowledge.changeDetection.detectChanges(
|
|
identity.objectId, data, prevObs
|
|
);
|
|
}
|
|
} catch (e) {
|
|
// Change detection not available
|
|
}
|
|
|
|
if (changes && changes.length > 0) {
|
|
this.events.emit('change:detected', { observation: data, changes });
|
|
}
|
|
|
|
// 5. Lägg till Learning Queue
|
|
this.learningQueue.add({
|
|
type: 'observation',
|
|
priority: quality > 0.8 ? 3 : 2,
|
|
data: { ...data, quality, identity },
|
|
});
|
|
|
|
// 6. Uppdatera coverage
|
|
this.layers.data.datasetManager.updateCoverage(data.cityId, data.objectType, {
|
|
found: 1,
|
|
});
|
|
}
|
|
|
|
async handleMission(data) {
|
|
console.log(`[UIOS] Mission created: ${data.id}`);
|
|
|
|
// Tilldela till Zoomer
|
|
const assignment = await this.assignZoomer(data);
|
|
|
|
if (assignment) {
|
|
console.log(`[UIOS] Assigned to Zoomer: ${assignment.zoomerId}`);
|
|
}
|
|
}
|
|
|
|
async handleModelTrained(data) {
|
|
console.log(`[UIOS] Model trained: ${data.modelId}`);
|
|
|
|
// Registrera i Model Registry
|
|
this.layers.data.modelRegistry.registerModel({
|
|
id: data.modelId,
|
|
datasetVersion: data.datasetVersion,
|
|
metrics: data.metrics,
|
|
});
|
|
|
|
// Shadow deployment
|
|
await this.layers.operations.deploymentManager.shadowDeploy(data.modelId);
|
|
}
|
|
|
|
async handleCoverageUpdate(data) {
|
|
console.log(`[UIOS] Coverage updated: ${data.cityId} / ${data.category}`);
|
|
|
|
// Generera missions om gap upptäcks
|
|
const report = this.layers.data.datasetManager.getCoverageReport(data.cityId);
|
|
|
|
for (const gap of report.gaps) {
|
|
if (gap.severity === 'critical') {
|
|
this.events.emit('gap:critical', { cityId: data.cityId, gap });
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* ============================================================
|
|
* API
|
|
* ============================================================
|
|
*/
|
|
|
|
getStatus() {
|
|
return {
|
|
version: this.config.version,
|
|
status: this.status,
|
|
uptime: Date.now() - this.startTime,
|
|
layers: {
|
|
dataCollection: 'active',
|
|
intelligence: 'active',
|
|
knowledge: 'active',
|
|
data: 'active',
|
|
operations: 'active',
|
|
api: 'active',
|
|
},
|
|
stats: {
|
|
observationsProcessed: this.learningQueue.stats.observations,
|
|
missionsGenerated: this.learningQueue.stats.missions,
|
|
modelsTrained: this.layers.data.modelRegistry.getModelCount(),
|
|
cities: this.layers.data.datasetManager.listReferenceCities().length,
|
|
},
|
|
};
|
|
}
|
|
|
|
async getAnalytics(cityId) {
|
|
const city = this.layers.data.datasetManager.getReferenceCity(cityId);
|
|
if (!city) return null;
|
|
|
|
return {
|
|
city: city.name,
|
|
coverage: this.layers.data.datasetManager.getCoverageReport(cityId),
|
|
missions: this.layers.intelligence.missionPlanner.getTopMissions(cityId, 10),
|
|
models: this.layers.data.modelRegistry.getModelsForCity(cityId),
|
|
};
|
|
}
|
|
|
|
async processObservation(observation) {
|
|
return this.handleObservation(observation);
|
|
}
|
|
|
|
async generateMissions(cityId) {
|
|
return this.layers.intelligence.missionPlanner.generateMissions(cityId);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* ============================================================
|
|
* HJÄLPKLASSER
|
|
* ============================================================
|
|
*/
|
|
|
|
class LearningQueue {
|
|
constructor() {
|
|
this.queue = [];
|
|
this.processed = [];
|
|
this.stats = { observations: 0, missions: 0 };
|
|
}
|
|
|
|
add(item) {
|
|
this.queue.push({
|
|
...item,
|
|
addedAt: Date.now(),
|
|
});
|
|
|
|
if (item.type === 'observation') this.stats.observations++;
|
|
if (item.type === 'mission') this.stats.missions++;
|
|
|
|
// Sortera efter prioritet
|
|
this.queue.sort((a, b) => a.priority - b.priority);
|
|
}
|
|
|
|
getUncertain() {
|
|
return this.queue.filter(item =>
|
|
item.type === 'observation' &&
|
|
item.data.quality < 0.7
|
|
);
|
|
}
|
|
|
|
getRecent(count) {
|
|
return this.queue
|
|
.filter(item => item.type === 'observation')
|
|
.slice(-count);
|
|
}
|
|
|
|
getNext() {
|
|
return this.queue.shift();
|
|
}
|
|
}
|
|
|
|
class EventBus {
|
|
constructor() {
|
|
this.listeners = new Map();
|
|
}
|
|
|
|
on(event, handler) {
|
|
if (!this.listeners.has(event)) {
|
|
this.listeners.set(event, []);
|
|
}
|
|
this.listeners.get(event).push(handler);
|
|
}
|
|
|
|
emit(event, data) {
|
|
const handlers = this.listeners.get(event);
|
|
if (handlers) {
|
|
handlers.forEach(handler => handler(data));
|
|
}
|
|
}
|
|
}
|
|
|
|
class UrbanKnowledgeGraph {
|
|
constructor() {
|
|
this.objects = new Map();
|
|
this.relations = new Map();
|
|
}
|
|
|
|
async addObservation(observation, identity) {
|
|
// Lägg till i UKG
|
|
const objectId = identity.objectId;
|
|
|
|
if (!this.objects.has(objectId)) {
|
|
this.objects.set(objectId, {
|
|
id: objectId,
|
|
type: observation.objectType,
|
|
observations: [],
|
|
relations: [],
|
|
});
|
|
}
|
|
|
|
const obj = this.objects.get(objectId);
|
|
obj.observations.push(observation.id);
|
|
|
|
// Skapa relationer
|
|
await this.createRelations(objectId, observation);
|
|
}
|
|
|
|
async createRelations(objectId, observation) {
|
|
// Hitta närliggande objekt
|
|
const nearby = Array.from(this.objects.values()).filter(obj => {
|
|
if (obj.id === objectId) return false;
|
|
const dist = this.calculateDistance(
|
|
observation.location,
|
|
obj.location
|
|
);
|
|
return dist < 50; // 50 meter
|
|
});
|
|
|
|
for (const near of nearby) {
|
|
this.relations.set(`${objectId}-${near.id}`, {
|
|
from: objectId,
|
|
to: near.id,
|
|
type: 'nearby',
|
|
distance: this.calculateDistance(observation.location, near.location),
|
|
});
|
|
}
|
|
}
|
|
|
|
calculateDistance(loc1, loc2) {
|
|
const R = 6371000;
|
|
const dLat = (loc2.lat - loc1.lat) * Math.PI / 180;
|
|
const dLon = (loc2.lng - loc1.lng) * Math.PI / 180;
|
|
const a = Math.sin(dLat/2) * Math.sin(dLat/2) +
|
|
Math.cos(loc1.lat * Math.PI / 180) * Math.cos(loc2.lat * Math.PI / 180) *
|
|
Math.sin(dLon/2) * Math.sin(dLon/2);
|
|
return R * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
|
|
}
|
|
}
|
|
|
|
class ModelRegistry {
|
|
constructor() {
|
|
this.models = new Map();
|
|
this.versions = new Map();
|
|
}
|
|
|
|
registerModel(model) {
|
|
this.models.set(model.id, {
|
|
...model,
|
|
registeredAt: new Date().toISOString(),
|
|
status: 'registered',
|
|
});
|
|
}
|
|
|
|
listModels() {
|
|
return Array.from(this.models.values());
|
|
}
|
|
|
|
getModelCount() {
|
|
return this.models.size;
|
|
}
|
|
|
|
getPreviousVersion(modelId) {
|
|
const model = this.models.get(modelId);
|
|
if (!model) return null;
|
|
|
|
// Hitta föregående version
|
|
const versions = Array.from(this.models.values())
|
|
.filter(m => m.name === model.name && m.id !== modelId)
|
|
.sort((a, b) => new Date(b.registeredAt) - new Date(a.registeredAt));
|
|
|
|
return versions[0] || null;
|
|
}
|
|
|
|
async evaluateModel(modelId) {
|
|
// Mock evaluation
|
|
return {
|
|
mAP: 0.85 + Math.random() * 0.1,
|
|
precision: 0.88 + Math.random() * 0.08,
|
|
recall: 0.82 + Math.random() * 0.1,
|
|
latency: 15 + Math.random() * 10,
|
|
};
|
|
}
|
|
|
|
getModelsForCity(cityId) {
|
|
return Array.from(this.models.values())
|
|
.filter(m => m.cityId === cityId);
|
|
}
|
|
}
|
|
|
|
class TrainingPipeline {
|
|
constructor() {
|
|
this.status = 'idle';
|
|
}
|
|
|
|
async train(config) {
|
|
this.status = 'training';
|
|
console.log('[Training] Starting training...');
|
|
|
|
// Mock training
|
|
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
|
|
this.status = 'completed';
|
|
return {
|
|
modelId: `model_${Date.now()}`,
|
|
metrics: {
|
|
mAP: 0.87,
|
|
precision: 0.89,
|
|
recall: 0.85,
|
|
},
|
|
};
|
|
}
|
|
}
|
|
|
|
class DeploymentManager {
|
|
constructor() {
|
|
this.deployments = new Map();
|
|
}
|
|
|
|
async shadowDeploy(modelId) {
|
|
console.log(`[Deploy] Shadow deploying: ${modelId}`);
|
|
|
|
this.deployments.set(modelId, {
|
|
modelId,
|
|
mode: 'shadow',
|
|
startedAt: new Date().toISOString(),
|
|
traffic: 0.1, // 10% traffic
|
|
});
|
|
}
|
|
|
|
async promote(modelId) {
|
|
console.log(`[Deploy] Promoting: ${modelId}`);
|
|
|
|
const deployment = this.deployments.get(modelId);
|
|
if (deployment) {
|
|
deployment.mode = 'production';
|
|
deployment.traffic = 1.0;
|
|
}
|
|
}
|
|
}
|
|
|
|
class AnalyticsEngine {
|
|
constructor() {
|
|
this.metrics = new Map();
|
|
}
|
|
|
|
track(event, data) {
|
|
if (!this.metrics.has(event)) {
|
|
this.metrics.set(event, []);
|
|
}
|
|
this.metrics.get(event).push({
|
|
timestamp: Date.now(),
|
|
data,
|
|
});
|
|
}
|
|
|
|
getMetrics(event) {
|
|
return this.metrics.get(event) || [];
|
|
}
|
|
}
|
|
|
|
class CustomerAPI {
|
|
constructor() {
|
|
this.requests = new Map();
|
|
}
|
|
|
|
async createRequest(customerId, requirements) {
|
|
const request = {
|
|
id: `req_${Date.now()}`,
|
|
customerId,
|
|
requirements,
|
|
status: 'open',
|
|
createdAt: new Date().toISOString(),
|
|
};
|
|
|
|
this.requests.set(request.id, request);
|
|
return request;
|
|
}
|
|
|
|
async getRequest(requestId) {
|
|
return this.requests.get(requestId);
|
|
}
|
|
}
|
|
|
|
module.exports = UrbanIntelligenceOperatingSystem;
|
|
|
|
// Demo
|
|
if (require.main === module) {
|
|
const uios = new UrbanIntelligenceOperatingSystem();
|
|
uios.initialize().then(status => {
|
|
console.log('\n=== UIOS STATUS ===');
|
|
console.log(JSON.stringify(status, null, 2));
|
|
});
|
|
}
|