/** * QUIXZOOM WebSocket Test Client * * Simulerar en Zoomer som ansluter och skickar observationer. */ const WebSocket = require('ws'); class TestClient { constructor(zoomerId, options = {}) { this.zoomerId = zoomerId; this.url = options.url || 'ws://localhost:8080'; this.ws = null; this.connected = false; this.messagesReceived = 0; this.messagesSent = 0; } connect() { return new Promise((resolve, reject) => { this.ws = new WebSocket(this.url); this.ws.on('open', () => { console.log(`[CLIENT ${this.zoomerId}] Connected`); this.connected = true; // Autentisera this.send({ type: 'auth', payload: { zoomerId: this.zoomerId }, }); resolve(); }); this.ws.on('message', (data) => { this.messagesReceived++; const message = JSON.parse(data); this.handleMessage(message); }); this.ws.on('close', () => { console.log(`[CLIENT ${this.zoomerId}] Disconnected`); this.connected = false; }); this.ws.on('error', (error) => { console.error(`[CLIENT ${this.zoomerId}] Error:`, error.message); reject(error); }); }); } handleMessage(message) { switch (message.type) { case 'connected': console.log(`[CLIENT ${this.zoomerId}] Server says: connected`); break; case 'auth_success': console.log(`[CLIENT ${this.zoomerId}] Authenticated as ${message.zoomerId}`); break; case 'observation_ack': console.log(`[CLIENT ${this.zoomerId}] Observation ${message.observationId} acknowledged`); break; case 'identity_result': console.log(`[CLIENT ${this.zoomerId}] Identity: ${message.objectId} (${(message.confidence * 100).toFixed(0)}% confidence)`); break; case 'instruction': console.log(`[CLIENT ${this.zoomerId}] INSTRUCTION: "${message.instruction}" (priority: ${message.priority})`); break; default: console.log(`[CLIENT ${this.zoomerId}] Received:`, message.type); } } send(message) { if (!this.connected) return; this.ws.send(JSON.stringify(message)); this.messagesSent++; } sendObservation(objectType, location) { this.send({ type: 'observation', payload: { observationId: `obs_${Date.now()}`, objectType, location, timestamp: Date.now(), attributes: { material: 'steel', height: 7.8, condition: 'good', }, }, }); } sendLocation(location) { this.send({ type: 'location_update', payload: { location }, }); } disconnect() { if (this.ws) { this.ws.close(); } } } // Demo if (require.main === module) { async function runDemo() { console.log('=== WEBSOCKET CLIENT DEMO ===\n'); // Skapa två Zoomers const zoomerA = new TestClient('zoomer_A'); const zoomerB = new TestClient('zoomer_B'); // Anslut await zoomerA.connect(); await zoomerB.connect(); // Vänta på autentisering await new Promise(resolve => setTimeout(resolve, 500)); // Zoomer A skickar observation console.log('\n--- Zoomer A sends observation ---'); zoomerA.sendObservation('street_lamp', { lat: 59.3293, lng: 18.0686 }); // Vänta på svar await new Promise(resolve => setTimeout(resolve, 500)); // Zoomer B skickar observation (samma område) console.log('\n--- Zoomer B sends observation ---'); zoomerB.sendObservation('street_lamp', { lat: 59.3294, lng: 18.0687 }); // Vänta på svar await new Promise(resolve => setTimeout(resolve, 1000)); // Skicka fler observationer console.log('\n--- Zoomer A sends more observations ---'); for (let i = 0; i < 3; i++) { zoomerA.sendObservation('traffic_sign', { lat: 59.3293 + Math.random() * 0.001, lng: 18.0686 + Math.random() * 0.001, }); await new Promise(resolve => setTimeout(resolve, 300)); } // Vänta await new Promise(resolve => setTimeout(resolve, 1000)); // Statistik console.log('\n=== STATISTICS ==='); console.log(`Zoomer A: sent=${zoomerA.messagesSent}, received=${zoomerA.messagesReceived}`); console.log(`Zoomer B: sent=${zoomerB.messagesSent}, received=${zoomerB.messagesReceived}`); // Koppla ner zoomerA.disconnect(); zoomerB.disconnect(); console.log('\nDemo complete'); } runDemo().catch(console.error); } module.exports = TestClient;