Files
nodeMap/server.js

189 lines
6.2 KiB
JavaScript

// 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 express = require("express");
const expressApp = express();
// Proxy-Route für Karten-Tiles
expressApp.get("/tiles/:z/:x/:y.png", async (req, res) => {
const { z, x, y } = req.params;
// OSM-Subdomain (a, b, c) zufällig wählen
const subdomains = ["a", "b", "c"];
const s = subdomains[Math.floor(Math.random() * subdomains.length)];
const tileUrl = `https://${s}.tile.openstreetmap.org/${z}/${x}/${y}.png`;
try {
const response = await fetch(tileUrl);
if (!response.ok) {
res.status(response.status).send("Tile not found");
return;
}
res.set("Content-Type", "image/png");
response.body.pipe(res);
} catch (err) {
res.status(500).send("Error fetching tile");
}
});
// Alle anderen Routen an Next.js
expressApp.all("*", (req, res) => {
handle(req, res);
});
const server = createServer(expressApp);
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}`);
});
});