const path = require('path'); const http = require('http'); const express = require('express'); const dgram = require('dgram'); const { WebSocketServer } = require('ws'); const HTTP_PORT = process.env.PORT || 3000; const UDP_BROADCAST_PORT = Number(process.env.UDP_PORT) || 4210; const UDP_BROADCAST_ADDR = process.env.UDP_ADDR || '255.255.255.255'; const MATRIX_WIDTH = Number(process.env.MATRIX_WIDTH) || 16; const MATRIX_HEIGHT = Number(process.env.MATRIX_HEIGHT) || 16; const BYTES_PER_PIXEL = 3; const app = express(); app.use(express.static(path.join(__dirname, 'public'))); const server = http.createServer(app); const wss = new WebSocketServer({ server, path: '/ws' }); const udpSocket = dgram.createSocket('udp4'); udpSocket.on('error', (err) => { console.error('[UDP] error:', err); }); udpSocket.bind(() => { try { udpSocket.setBroadcast(true); console.log(`[UDP] Ready to broadcast on ${UDP_BROADCAST_ADDR}:${UDP_BROADCAST_PORT}`); } catch (e) { console.error('[UDP] setBroadcast failed:', e); } }); wss.on('connection', (ws, req) => { const clientAddress = req?.socket?.remoteAddress || 'unknown'; console.log(`[WS] Client connected: ${clientAddress}`); ws.on('message', (data) => { const bufferToSend = Buffer.isBuffer(data) ? data : (data instanceof ArrayBuffer ? Buffer.from(data) : Buffer.from(String(data))); const expectedSize = MATRIX_WIDTH * MATRIX_HEIGHT * BYTES_PER_PIXEL; if (bufferToSend.length !== expectedSize) { console.warn(`[WS] Unexpected frame size: ${bufferToSend.length} bytes (expected ${expectedSize}).`); } const hexPayload = bufferToSend.toString('hex'); const udpPayload = Buffer.from(`RAW:${hexPayload}`, 'ascii'); udpSocket.send( udpPayload, UDP_BROADCAST_PORT, UDP_BROADCAST_ADDR, (err) => { if (err) { console.error('[UDP] send error:', err.message); } } ); }); ws.on('close', () => { console.log('[WS] Client disconnected'); }); ws.on('error', (err) => { console.error('[WS] error:', err.message); }); }); server.listen(HTTP_PORT, () => { console.log(`Server listening on http://localhost:${HTTP_PORT}`); }); process.on('SIGINT', () => { console.log('Shutting down...'); try { udpSocket.close(); } catch {} try { server.close(() => process.exit(0)); } catch {} });