// server.js const { createServer } = require("http"); const next = require("next"); const { Server } = require("socket.io"); const path = require("path"); const fs = require("fs"); const fetch = (...args) => import("node-fetch").then(({ default: fetch }) => fetch(...args)); const dev = process.env.NODE_ENV !== "production"; const app = next({ dev }); const handle = app.getRequestHandler(); const PORT = 3000; // Hilfsfunktion zum Schreiben von JSON-Dateien bei Änderung const writeJsonFile = (filename, data) => { const dir = path.join(process.cwd(), "websocketDump"); if (!fs.existsSync(dir)) fs.mkdirSync(dir); const fullPath = path.join(dir, filename); fs.writeFileSync(fullPath, JSON.stringify(data, null, 2), "utf-8"); }; // Extrahiert relevante Datenstruktur aus Antwort const extractData = (json, name) => { return ( json?.Statis || json?.Points || json?.Systems || json?.Rights || json?.[name] || json || [] ); }; app.prepare().then(() => { const server = createServer((req, res) => { handle(req, res); }); const io = new Server(server); // ✅ Globaler Cache für alle aktuellen Daten const globalDataCache = new Map(); io.on("connection", socket => { const { m: idMap, u: idUser } = socket.handshake.query; console.log(`🔌 WebSocket verbunden (idMap=${idMap}, idUser=${idUser})`); const cacheKey = `${idMap}_${idUser}`; const endpoints = [ { name: "GisLinesStatus", getUrl: () => `WebServiceMap.asmx/GisLinesStatus?idMap=${idMap}`, mock: "GisLinesStatus.json", }, { name: "GisStationsMeasurements", getUrl: () => `WebServiceMap.asmx/GisStationsMeasurements?idMap=${idMap}`, mock: "GisStationsMeasurements.json", }, { name: "GisStationsStaticDistrict", getUrl: () => `WebServiceMap.asmx/GisStationsStaticDistrict?idMap=${idMap}&idUser=${idUser}`, mock: "GisStationsStaticDistrict.json", }, { name: "GisStationsStatusDistrict", getUrl: () => `WebServiceMap.asmx/GisStationsStatusDistrict?idMap=${idMap}&idUser=${idUser}`, mock: "GisStationsStatusDistrict.json", }, { name: "GisSystemStatic", getUrl: () => `WebServiceMap.asmx/GisSystemStatic?idMap=${idMap}&idUser=${idUser}`, mock: "GisSystemStatic.json", }, ]; const lastDataMap = {}; // ✅ Funktion um sofort alle verfügbaren Daten zu senden (für Browser-Reload) const sendAllCurrentData = () => { const cachedData = globalDataCache.get(cacheKey); if (cachedData && Object.keys(cachedData).length > 0) { console.log( `📦 Sending all current data to client (${Object.keys(cachedData).length} endpoints)` ); Object.entries(cachedData).forEach(([name, data]) => { socket.emit(`${name}Updated`, data); console.log(`🔄 Browser-Reload: ${name} data sent`); }); } else { console.log(`📭 No cached data available for ${cacheKey}, will fetch fresh data`); } }; const fetchData = async () => { for (const { name, getUrl, mock } of endpoints) { try { let statis; if (dev) { const mockPath = path.join(process.cwd(), "mockData", mock); const jsonStr = fs.readFileSync(mockPath, "utf-8"); const json = JSON.parse(jsonStr); statis = extractData(json, name); console.log(`🧪 [Mock] ${name}`); } else { const fetchUrl = `http://localhost/talas5/ClientData/${getUrl()}`; const res = await fetch(fetchUrl); const text = await res.text(); let json; try { json = JSON.parse(text); } catch (err) { console.error(`❌ ${name}: JSON Parsing fehlgeschlagen:`, err.message); console.error(`🔍 Antwort war:`, text.slice(0, 300)); continue; } statis = extractData(json, name); console.log(`📡 Webservice-Daten empfangen für ${name}`); } const newDataStr = JSON.stringify(statis); // ✅ Cache aktualisieren if (!globalDataCache.has(cacheKey)) { globalDataCache.set(cacheKey, {}); } globalDataCache.get(cacheKey)[name] = statis; // ✅ Nur bei Änderungen senden (setInterval-Logik) if (newDataStr !== lastDataMap[name]) { lastDataMap[name] = newDataStr; socket.emit(`${name}Updated`, statis); console.log(`✅ Änderung bei ${name} erkannt → gesendet`); writeJsonFile(`${name}.json`, statis); } else { // console.log(`🔁 ${name}: Keine Änderung`); } } catch (error) { console.error(`❌ Fehler bei ${name}:`, error.message); } } }; // ✅ Beim Connect: Sofort alle aktuellen Daten senden (für Browser-Reload) sendAllCurrentData(); // ✅ Dann erste Datenabfrage durchführen fetchData(); // ✅ setInterval für regelmäßige Updates (nur bei Änderungen) const interval = setInterval(fetchData, 5000); // 5 Sekunden ,TALAS.web nutzt 12 Sekunden socket.on("disconnect", () => { clearInterval(interval); console.log("❌ WebSocket getrennt"); }); }); server.listen(PORT, () => { console.log(`🚀 App + WebSocket läuft auf http://localhost:${PORT}`); }); });