diff --git a/realtime/client-example.js b/realtime/client-example.js new file mode 100644 index 0000000..455f884 --- /dev/null +++ b/realtime/client-example.js @@ -0,0 +1,78 @@ +const WebSocket = require('ws'); +const { encode } = require('@msgpack/msgpack'); + +const SERVER_URL = process.env.SERVER_URL || 'http://localhost:3000'; +const WS_URL = process.env.WS_URL || 'ws://localhost:3000'; +const SENSOR_NAME = process.env.SENSOR_NAME || 'sensor-01'; +const SENSOR_CODE = process.env.SENSOR_CODE || 'password123'; + +async function authenticate() { + const res = await fetch(`${SERVER_URL}/connect/`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ name: SENSOR_NAME, code: SENSOR_CODE }), + }); + + if (!res.ok) { + const err = await res.json(); + throw new Error(`Auth failed: ${err.error}`); + } + + const data = await res.json(); + console.log('Authenticated, token received'); + return data.t; +} + +function connectWebSocket(token) { + const ws = new WebSocket(`${WS_URL}?token=${token}`); + + ws.on('open', () => { + console.log('WebSocket connected'); + startSendingData(ws); + }); + + ws.on('pong', () => { + // Keepalive pong received + }); + + ws.on('close', (code, reason) => { + console.log(`WebSocket closed: ${code} ${reason}`); + process.exit(1); + }); + + ws.on('error', (err) => { + console.error('WebSocket error:', err.message); + }); +} + +function startSendingData(ws) { + setInterval(() => { + if (ws.readyState !== WebSocket.OPEN) return; + + const packet = { + ts: Date.now(), + t: 20 + Math.random() * 10, // temperature °C + h: 50 + Math.random() * 30, // humidity % + spd: Math.random() * 30, // speed + cog: Math.random() * 360, // course over ground + sog: 5 + Math.random() * 10, // speed over ground kn + hdg: Math.random() * 360, // heading true + lat: 43.7230 + Math.random() * 0.01, // latitude + lon: 10.3966 + Math.random() * 0.01, // longitude + }; + + ws.send(Buffer.from(encode(packet))); + }, 1000); +} + +async function main() { + try { + const token = await authenticate(); + connectWebSocket(token); + } catch (err) { + console.error(err.message); + process.exit(1); + } +} + +main(); diff --git a/realtime/src/index.js b/realtime/src/index.js index e28c824..78bb823 100644 --- a/realtime/src/index.js +++ b/realtime/src/index.js @@ -8,6 +8,15 @@ const wsHandler = require('./ws/handler'); app.use(express.json()); +// CORS — consenti richieste dalla console e altri client browser +app.use((req, res, next) => { + res.header('Access-Control-Allow-Origin', '*'); + res.header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS'); + res.header('Access-Control-Allow-Headers', 'Content-Type'); + if (req.method === 'OPTIONS') return res.sendStatus(204); + next(); +}); + // DATABASE POSTGRESQL app.get('/', (req, res) => { diff --git a/realtime/src/routes/sessions.js b/realtime/src/routes/sessions.js index 8b1182e..9298488 100644 --- a/realtime/src/routes/sessions.js +++ b/realtime/src/routes/sessions.js @@ -1,7 +1,10 @@ const router = require('express').Router(); -const db = require('../store/db'); const { queryAll, query } = require('../store/redis'); +/** + * GET /sessions — Lista tutte le sessioni attive dei sensori. + * Legge da Redis le chiavi sensors:* (scritte da handler.js alla connessione) + */ router.get('/', async (req, res) => { try { const keys = await queryAll('sensors'); @@ -23,38 +26,57 @@ router.get('/', async (req, res) => { } }); -router.get('/pending', (req, res) => { +/** + * GET /sessions/pending — Lista token di connessione pendenti. + * Legge da Redis le chiavi sensors_pending:* (create da createConnectionToken) + */ +router.get('/pending', async (req, res) => { try { - const pendingTokens = queryAll('snsr_pending_token'); - res.json(pendingTokens); + const keys = await queryAll('sensors_pending'); + res.json(keys); } catch (err) { console.error('Error fetching pending tokens', err); res.status(500).json({ error: `Error fetching pending tokens, ${err}` }); } }); -router.get('/connected', (req, res) => { +/** + * GET /sessions/connected — Lista sensori attualmente connessi. + * Legge da Redis le chiavi sensor:* (scritte da appendAsConnection in handler.js) + */ +router.get('/connected', async (req, res) => { try { - const connectedSensors = queryAll('snsr_connected'); - res.json(connectedSensors); + const keys = await queryAll('sensor'); + const connected = []; + for (const key of keys) { + const name = key.replace('sensor:', ''); + const info = await query(name, 'sensor'); + if (info.status === 'connected') { + connected.push({ name, connectedAt: info.timestamp }); + } + } + res.json(connected); } catch (err) { console.error('Error fetching connected sensors', err); res.status(500).json({ error: `Error fetching connected sensors, ${err}` }); } }); +/** + * GET /sessions/connected/:id — Verifica se un sensore specifico è connesso. + */ router.get('/connected/:id', async (req, res) => { const { id } = req.params; try { - const sensor = await query(`snsr_connected:${id}`); - if (!sensor) { + const info = await query(id, 'sensor'); + if (!info || info.status !== 'connected') { return res.status(404).json({ error: 'sensor not connected' }); } - res.json({ id, name: sensor }); + res.json({ name: id, connectedAt: info.timestamp }); } catch (err) { console.error('Error fetching sensor connection status', err); res.status(500).json({ error: `Error fetching sensor connection status, ${err}` }); } }); -module.exports = router; \ No newline at end of file +module.exports = router; diff --git a/realtime/src/ws/handler.js b/realtime/src/ws/handler.js index 6b497ca..364e2d7 100644 --- a/realtime/src/ws/handler.js +++ b/realtime/src/ws/handler.js @@ -103,7 +103,7 @@ function handleSensorConnection(ws) { ws.on('message', (data) => { try { const packet = decode(data); - const { ts, ...fields } = packet; + const { ts, _m, ...fields } = packet; writeSensorData(fields, sensorName, sessionId, ts);