Alle vier Frontends horchten auf ein Event 'connected', das nie gesendet wurde; der Server verschickte stattdessen ein Event mit dem Namen "Hello user from server" und leerem Payload. Beides ist entfernt. An seine Stelle tritt das Socket.IO-eigene 'connect', das auch nach einem Reconnect feuert: Adminpanel und Monitor holen sich dort den aktuellen Stand. Da der Pi das WLAN selbst aufspannt, sind kurze Abbrueche im Betrieb normal — bisher blieb die Anzeige danach auf einem veralteten Stand stehen, bis das naechste Ereignis eintraf. Die Teamnamen-Dropdowns bauten ihre Eintraege als HTML-String zusammen und interpolierten den Namen in einen onclick-Aufruf. Ein Name mit Apostroph, etwa "SG D'Horn", zerlegte den Handler und machte den Eintrag unbrauchbar. Die Eintraege entstehen jetzt ueber createTeamDropdownItem() mit textContent und addEventListener, der Name kommt per Closure statt als String. Die Schleifenvariable ist zudem korrekt deklariert, vorher war sie global. fetchJson() kapselt Anfragen und liefert null, wenn der Server nicht erreichbar ist oder mit einem Fehler antwortet, statt still in der Konsole zu scheitern. timerStart und timerPause behalten ihre eigene Auswertung des 406-Status. Das Verbindungs-Handling ist von routes/index.js nach controllers/socketio.js gewandert, wo der Socket.IO-Server erzeugt wird, und protokolliert jetzt auch Verbindungsabbrueche. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
123 lines
No EOL
5.9 KiB
JavaScript
123 lines
No EOL
5.9 KiB
JavaScript
let wss = "ws://" + window.location.hostname + ":3001" // Build socketio endpoint from window.location
|
|
const socket = io(wss); // Connect to socketio server
|
|
|
|
// ######################################################################################################################################################
|
|
// Websockets event handler
|
|
// ######################################################################################################################################################
|
|
|
|
// 'connect' feuert bei jeder Verbindung, auch nach einem Reconnect. Da der Pi das WLAN
|
|
// selbst aufspannt, sind kurze Abbrueche normal: danach werden Timer und Spielstand neu
|
|
// geholt, damit die Anzeige nicht auf einem veralteten Stand haengen bleibt.
|
|
socket.on('connect', () => {
|
|
console.log("Verbunden");
|
|
initialUpdate();
|
|
});
|
|
|
|
socket.on('timerDurationLeft', (message) => {
|
|
console.log(message) // Log durationLeft in client console
|
|
document.getElementById("durationLeft").innerHTML = message; // Display durationLeft in corresponding html Element
|
|
});
|
|
|
|
socket.on('timerEnded', (message) => {
|
|
document.getElementById("durationLeft").innerHTML = "ENDE"; // Display "STOP" in same html element as the duration
|
|
});
|
|
|
|
socket.on('refresh', (message) => {
|
|
document.location.reload() // Reload page on received 'refresh' messagen
|
|
});
|
|
|
|
socket.on('score', (message) => {
|
|
console.log(message) // Log score in client console
|
|
document.getElementById("score").innerHTML = message; // Display score in corresponding html Element
|
|
});
|
|
|
|
socket.on('scoreSideswitch', (message) => {
|
|
scoreGetValues(); // Update frontend when sides have been switch (switches team name sides)
|
|
});
|
|
|
|
// ######################################################################################################################################################
|
|
// General functions
|
|
// ######################################################################################################################################################
|
|
|
|
// Holt JSON von einem Endpunkt. Liefert null statt zu werfen, wenn der Server nicht
|
|
// erreichbar ist oder mit einem Fehler antwortet.
|
|
async function fetchJson(url) {
|
|
try {
|
|
const response = await fetch(url);
|
|
if(!response.ok) {
|
|
console.error("Anfrage fehlgeschlagen:", url, response.status);
|
|
return null;
|
|
}
|
|
return await response.json();
|
|
} catch (err) {
|
|
console.error("Server nicht erreichbar:", url, err);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
// Update DOM timer elements from passed values
|
|
function updateTimerFrontend(values) {
|
|
document.getElementById("durationLeft").innerHTML = values.print; // Display durationLeft as prettyfied string
|
|
}
|
|
|
|
// Update DOM score elements from passed values
|
|
function updateScoreFrontend(values) {
|
|
document.getElementById("score").innerHTML = values.print; // Set score on admin interface
|
|
|
|
if(!values.sideswitch) {
|
|
// Normale Seite: teamA links, teamB rechts
|
|
if(values.teamA.isSpielgemeinschaft) {
|
|
document.getElementById("teamA").innerHTML = values.teamA.name + '<br>' + values.teamA.name2;
|
|
} else {
|
|
document.getElementById("teamA").innerHTML = values.teamA.name
|
|
}
|
|
|
|
if(values.teamB.isSpielgemeinschaft) {
|
|
document.getElementById("teamB").innerHTML = values.teamB.name + '<br>' + values.teamB.name2;
|
|
} else {
|
|
document.getElementById("teamB").innerHTML = values.teamB.name
|
|
}
|
|
} else {
|
|
// Seitenwechsel nach Halbzeit: teamA und teamB tauschen ihre Anzeigeseite
|
|
if(values.teamA.isSpielgemeinschaft) {
|
|
document.getElementById("teamB").innerHTML = values.teamA.name + '<br>' + values.teamA.name2;
|
|
} else {
|
|
document.getElementById("teamB").innerHTML = values.teamA.name
|
|
}
|
|
|
|
if(values.teamB.isSpielgemeinschaft) {
|
|
document.getElementById("teamA").innerHTML = values.teamB.name + '<br>' + values.teamB.name2;
|
|
} else {
|
|
document.getElementById("teamA").innerHTML = values.teamB.name
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
// Wird beim Laden der Seite aufgerufen, um Timer und Score sofort mit dem aktuellen
|
|
// Serverstand zu synchronisieren (verhindert leere Anzeige nach Seiten-Reload)
|
|
async function initialUpdate() {
|
|
timerGetValues(); // Request new values for timer
|
|
scoreGetValues(); // Request new values for score
|
|
}
|
|
|
|
// ######################################################################################################################################################
|
|
// Timerfunctions
|
|
// ######################################################################################################################################################
|
|
|
|
// Request important timer values
|
|
async function timerGetValues() {
|
|
const data = await fetchJson("/admin/timerGetValues"); // Call API Endpoint /admin/timerGetValues
|
|
if(data) updateTimerFrontend(data); // Update admin frontend
|
|
}
|
|
|
|
// ######################################################################################################################################################
|
|
// Scoreboardfunctions
|
|
// ######################################################################################################################################################
|
|
|
|
// Request important values for score
|
|
async function scoreGetValues() {
|
|
const data = await fetchJson("/admin/scoreGetValues"); // Call API Endpoint /admin/scoreGetValues
|
|
console.log(data); // Print received data
|
|
if(data) updateScoreFrontend(data); // Update admin frontend with received values for scoreboard
|
|
} |