diff --git a/scoreboard/controllers/socketio.js b/scoreboard/controllers/socketio.js index ae048c3..f02ec39 100644 --- a/scoreboard/controllers/socketio.js +++ b/scoreboard/controllers/socketio.js @@ -9,4 +9,15 @@ const io = new Server(3001, { cors: { origin: "*" } }); +// Verbindungs-Handling: der Client meldet sich beim Verbinden selbst mit dem +// Socket.IO-eigenen 'connect'-Event und holt sich den aktuellen Stand ueber die +// REST-Endpunkte. Serverseitig wird die Verbindung nur protokolliert. +io.on('connection', (socket) => { + console.log("A user connected"); + + socket.on('disconnect', (reason) => { + console.log("A user disconnected: ", reason); + }); +}); + module.exports = io \ No newline at end of file diff --git a/scoreboard/public/javascripts/admin.js b/scoreboard/public/javascripts/admin.js index 1036eec..67d4ec2 100644 --- a/scoreboard/public/javascripts/admin.js +++ b/scoreboard/public/javascripts/admin.js @@ -5,8 +5,12 @@ const socket = io(wss); // Conne // Websockets event handler // ###################################################################################################################################################### -socket.on('connected', (message) => { // Once client is connected to websocket - console.log("Connected"); +// 'connect' feuert bei jeder Verbindung, auch nach einem Reconnect. Da der Pi das +// WLAN selbst aufspannt, sind kurze Abbrueche normal: danach werden die Werte neu +// geholt, damit das Panel nicht auf einem veralteten Stand stehen bleibt. +socket.on('connect', () => { + console.log("Verbunden"); + initialUpdate(); }); socket.on('timerDurationLeft', (message) => { @@ -28,6 +32,22 @@ socket.on('score', (message) => { // General functions // ###################################################################################################################################################### +// Holt JSON von einem Endpunkt. Liefert null statt zu werfen, wenn der Server nicht +// erreichbar ist oder mit einem Fehler antwortet — sonst bricht der Aufrufer still ab. +async function fetchJson(url, options) { + try { + const response = await fetch(url, options); + 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; + } +} + // Initial update gets called whenever page has been loaded async function initialUpdate() { etcGetValues(); // Request new values for etc stuff @@ -104,6 +124,19 @@ function updateScoreFrontend(values) { } +// Erzeugt einen Dropdown-Eintrag, der den Teamnamen in das angegebene Feld uebernimmt. +// Der Name wird als Text gesetzt und per Closure an den Handler gegeben, statt ihn in +// einen onclick-String zu schreiben: Namen mit Apostroph zerlegten sonst den Handler. +function createTeamDropdownItem(field, team) { + const li = document.createElement("li"); + const a = document.createElement("a"); + a.className = "dropdown-item"; + a.textContent = team; + a.addEventListener("click", () => scoreInsertChosenName(field, team)); + li.appendChild(a); + return li; +} + // Update DOM for all db contents with the passed values function updateDbFrontend(values) { console.log(values); @@ -122,27 +155,17 @@ function updateDbFrontend(values) { let selectDelete = document.getElementById("dbTeamToDelete"); // Select for team deletion from db selectDelete.innerHTML = "" // Clears all existing options from select - //
  • Action
  • - - for(team of values.teams) { + for(const team of values.teams) { // Create custom Dropdowns with different function arguments for team configuration - var li = document.createElement("li"); - li.innerHTML = ` `+ team +``; - teamAnameDropdown.appendChild(li); - var li = document.createElement("li"); - li.innerHTML = ` `+ team +``; - teamAname2Dropdown.appendChild(li); - var li = document.createElement("li"); - li.innerHTML = ` `+ team +``; - teamBnameDropdown.appendChild(li); - var li = document.createElement("li"); - li.innerHTML = ` `+ team +``; - teamBname2Dropdown.appendChild(li); + teamAnameDropdown.appendChild(createTeamDropdownItem("teamAname", team)); + teamAname2Dropdown.appendChild(createTeamDropdownItem("teamAname2", team)); + teamBnameDropdown.appendChild(createTeamDropdownItem("teamBname", team)); + teamBname2Dropdown.appendChild(createTeamDropdownItem("teamBname2", team)); // Create select options for deletion in debugwindow let opt = document.createElement("option"); opt.value = team; - opt.innerHTML = team; + opt.textContent = team; selectDelete.appendChild(opt); } } @@ -184,16 +207,14 @@ async function killBrowser() { // Request important values for etc stuff async function etcGetValues() { - const response = await fetch("/admin/etcGetValues"); // Call API Endpoint /admin/etcGetValues - const data = await response.json(); // Wait for asynchronous transfer to complete - updateEtcFrontend(data); // Update admin frontend + const data = await fetchJson("/admin/etcGetValues"); // Call API Endpoint /admin/etcGetValues + if(data) updateEtcFrontend(data); // Update admin frontend } // Toggle index page to also show scoreboard async function etcToggleSplashscreen() { - const response = await fetch("/admin/etcToggleSplashscreen");// Call API Endpoint /admin/etcToggleSplashscreen - const data = await response.json(); // Wait for asyncronous transfer to complete - updateEtcFrontend(data); // Update admin frontend + const data = await fetchJson("/admin/etcToggleSplashscreen");// Call API Endpoint /admin/etcToggleSplashscreen + if(data) updateEtcFrontend(data); // Update admin frontend refreshMonitor(); // Refresh main monitor to display new frontend } @@ -203,9 +224,8 @@ async function etcToggleSplashscreen() { // Request important timer values async function timerGetValues() { - const response = await fetch("/admin/timerGetValues"); // Call API Endpoint /admin/timerGetValues - const data = await response.json(); // Wait for asynchronous transfer to complete - updateTimerFrontend(data); // Update admin frontend + const data = await fetchJson("/admin/timerGetValues"); // Call API Endpoint /admin/timerGetValues + if(data) updateTimerFrontend(data); // Update admin frontend } // Request to start the timer @@ -235,24 +255,22 @@ async function timerPause() { // Request to reset the timer with the specified time async function timerReset() { let newDuration = document.getElementById("timerSelectDuration").value; - const response = await fetch("/admin/timerReset", { // Call API Endpoint /admin/timerStart with selected new duration in seconds + const data = await fetchJson("/admin/timerReset", { // Call API Endpoint /admin/timerStart with selected new duration in seconds method: 'POST', headers: { "Content-Type": "application/json" }, body: JSON.stringify({duration: newDuration}) }); - const data = await response.json(); // Wait for asyncronous transfer to complete - updateTimerFrontend(data); // Update admin frontend + if(data) updateTimerFrontend(data); // Update admin frontend } // Request to increase or decrease the timer depending on passed value in seconds async function timerIncDec(value) { - const response = await fetch("/admin/timerIncDec", { // Call API Endpoint /admin/timerIncDec with value in seconds to increase or decrease the timer + const data = await fetchJson("/admin/timerIncDec", { // Call API Endpoint /admin/timerIncDec with value in seconds to increase or decrease the timer method: 'POST', headers: { "Content-Type": "application/json" }, body: JSON.stringify({value: value}) }); - const data = await response.json(); // Wait for asyncronous transfer to complete - updateTimerFrontend(data); // Update admin frontend + if(data) updateTimerFrontend(data); // Update admin frontend } // ###################################################################################################################################################### @@ -261,49 +279,43 @@ async function timerIncDec(value) { // Request important values for score async function scoreGetValues() { - const response = await fetch("/admin/scoreGetValues"); // Call API Endpoint /admin/scoreGetValues - const data = await response.json(); // Wait for asynchronous transfer to complete - updateScoreFrontend(data); // Update admin frontend + const data = await fetchJson("/admin/scoreGetValues"); // Call API Endpoint /admin/scoreGetValues + if(data) updateScoreFrontend(data); // Update admin frontend } // Toggle index page to also show scoreboard async function scoreToggle() { - const response = await fetch("/admin/scoreToggle"); // Call API Endpoint /admin/scoreToggle - const data = await response.json(); // Wait for asyncronous transfer to complete - updateScoreFrontend(data); // Update admin frontend + const data = await fetchJson("/admin/scoreToggle"); // Call API Endpoint /admin/scoreToggle + if(data) updateScoreFrontend(data); // Update admin frontend refreshMonitor(); // Refresh main monitor to display new frontend } // Alter the score of passed team in the specified direction async function scoreAlterScore(team, dir) { - const response = await fetch("/admin/scoreAlterScore", { // Call API Endpoint /admin/scoreAlterScore with the teamname to alter the score in the specified direction + const data = await fetchJson("/admin/scoreAlterScore", { // Call API Endpoint /admin/scoreAlterScore with the teamname to alter the score in the specified direction method: 'POST', headers: { "Content-Type": "application/json" }, body: JSON.stringify({team: team, dir: dir}) }); - const data = await response.json(); // Wait for asyncronous transfer to complete - updateScoreFrontend(data); // Update admin frontend + if(data) updateScoreFrontend(data); // Update admin frontend } // Switch sides async function scoreToggleSideswitch() { - const response = await fetch("/admin/scoreToggleSideswitch");// Call API Endpoint /admin/scoreSwitch - const data = await response.json(); // Wait for asyncronous transfer to complete - updateScoreFrontend(data); // Update admin frontend + const data = await fetchJson("/admin/scoreToggleSideswitch");// Call API Endpoint /admin/scoreSwitch + if(data) updateScoreFrontend(data); // Update admin frontend } // Switch sides to mirror admin panel when sitting behind main monitor async function scoreToggleReferenceMirrored() { - const response = await fetch("/admin/scoreToggleReferenceMirrored");// Call API Endpoint /admin/scoreSwitch - const data = await response.json(); // Wait for asyncronous transfer to complete - updateScoreFrontend(data); // Update admin frontend + const data = await fetchJson("/admin/scoreToggleReferenceMirrored");// Call API Endpoint /admin/scoreSwitch + if(data) updateScoreFrontend(data); // Update admin frontend } // Clear score async function scoreClearScore() { - const response = await fetch("/admin/scoreClearScore"); // Call API Endpoint /admin/scoreClearScore - const data = await response.json(); // Wait for asynchronous transfer to complete - updateScoreFrontend(data); // Update admin frontend + const data = await fetchJson("/admin/scoreClearScore"); // Call API Endpoint /admin/scoreClearScore + if(data) updateScoreFrontend(data); // Update admin frontend } // Configure Teams @@ -320,13 +332,12 @@ async function scoreConfigTeams() { } console.log(teamA, teamB) - const response = await fetch("/admin/scoreConfigTeams", { // Call API Endpoint /admin/scoreAlterScore with the teamname to alter the score in the specified direction + const data = await fetchJson("/admin/scoreConfigTeams", { // Call API Endpoint /admin/scoreAlterScore with the teamname to alter the score in the specified direction method: 'POST', headers: { "Content-Type": "application/json" }, body: JSON.stringify({teamA: teamA, teamB: teamB}) }); - const data = await response.json(); // Wait for asyncronous transfer to complete - updateScoreFrontend(data); // Update admin frontend + if(data) updateScoreFrontend(data); // Update admin frontend refreshMonitor(); } @@ -336,31 +347,28 @@ async function scoreConfigTeams() { // Request important values for db contents async function dbGetValues() { - const response = await fetch("/admin/dbGetValues"); // Call API Endpoint /admin/dbGetValues - const data = await response.json(); // Wait for asynchronous transfer to complete - updateDbFrontend(data); // Update admin frontend + const data = await fetchJson("/admin/dbGetValues"); // Call API Endpoint /admin/dbGetValues + if(data) updateDbFrontend(data); // Update admin frontend } // Add team to DB async function dbAddTeam() { - const response = await fetch("/admin/dbAddTeam", { // Call API Endpoint /admin/scoreAlterScore with the teamname to alter the score in the specified direction + const data = await fetchJson("/admin/dbAddTeam", { // Call API Endpoint /admin/scoreAlterScore with the teamname to alter the score in the specified direction method: 'POST', headers: { "Content-Type": "application/json" }, body: JSON.stringify({teamName: document.getElementById("dbTeamToAdd").value}) }); document.getElementById("dbTeamToAdd").value = ""; // Empty the text field after sending - const data = await response.json(); // Wait for asyncronous transfer to complete - updateDbFrontend(data); + if(data) updateDbFrontend(data); } // Delete team from DB async function dbDeleteTeam() { let teamName = document.getElementById("dbTeamToDelete").value; - const response = await fetch("/admin/dbDeleteTeam", { // Call API Endpoint /admin/scoreAlterScore with the teamname to alter the score in the specified direction + const data = await fetchJson("/admin/dbDeleteTeam", { // Call API Endpoint /admin/scoreAlterScore with the teamname to alter the score in the specified direction method: 'POST', headers: { "Content-Type": "application/json" }, body: JSON.stringify({teamName: teamName}) }); - const data = await response.json(); // Wait for asyncronous transfer to complete - updateDbFrontend(data); + if(data) updateDbFrontend(data); } \ No newline at end of file diff --git a/scoreboard/public/javascripts/index.js b/scoreboard/public/javascripts/index.js index 1e52645..4410c9b 100644 --- a/scoreboard/public/javascripts/index.js +++ b/scoreboard/public/javascripts/index.js @@ -5,8 +5,12 @@ const socket = io(wss); // Connect to socketio s // Websockets event handler // ###################################################################################################################################################### -socket.on('connected', (message) => { - console.log("Connected"); +// '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) => { @@ -26,6 +30,22 @@ socket.on('refresh', (message) => { // 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 @@ -42,7 +62,6 @@ async function initialUpdate() { // Request important timer values async function timerGetValues() { - const response = await fetch("/admin/timerGetValues"); // Call API Endpoint /admin/timerGetValues - const data = await response.json(); // Wait for asynchronous transfer to complete - updateTimerFrontend(data); // Update admin frontend + const data = await fetchJson("/admin/timerGetValues"); // Call API Endpoint /admin/timerGetValues + if(data) updateTimerFrontend(data); // Update admin frontend } \ No newline at end of file diff --git a/scoreboard/public/javascripts/indexScore.js b/scoreboard/public/javascripts/indexScore.js index 476cec9..db99789 100644 --- a/scoreboard/public/javascripts/indexScore.js +++ b/scoreboard/public/javascripts/indexScore.js @@ -5,8 +5,12 @@ const socket = io(wss); // Connect to socketio s // Websockets event handler // ###################################################################################################################################################### -socket.on('connected', (message) => { - console.log("Connected"); +// '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) => { @@ -35,6 +39,22 @@ socket.on('scoreSideswitch', (message) => { // 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 @@ -87,9 +107,8 @@ async function initialUpdate() { // Request important timer values async function timerGetValues() { - const response = await fetch("/admin/timerGetValues"); // Call API Endpoint /admin/timerGetValues - const data = await response.json(); // Wait for asynchronous transfer to complete - updateTimerFrontend(data); // Update admin frontend + const data = await fetchJson("/admin/timerGetValues"); // Call API Endpoint /admin/timerGetValues + if(data) updateTimerFrontend(data); // Update admin frontend } // ###################################################################################################################################################### @@ -98,8 +117,7 @@ async function timerGetValues() { // Request important values for score async function scoreGetValues() { - const response = await fetch("/admin/scoreGetValues"); // Call API Endpoint /admin/scoreGetValues - const data = await response.json(); // Wait for asynchronous transfer to complete and parse json (which got received by backend) + const data = await fetchJson("/admin/scoreGetValues"); // Call API Endpoint /admin/scoreGetValues console.log(data); // Print received data - updateScoreFrontend(data); // Update admin frontend with received values for scoreboard + if(data) updateScoreFrontend(data); // Update admin frontend with received values for scoreboard } \ No newline at end of file diff --git a/scoreboard/public/javascripts/splashscreen.js b/scoreboard/public/javascripts/splashscreen.js index 3053f07..a46f235 100644 --- a/scoreboard/public/javascripts/splashscreen.js +++ b/scoreboard/public/javascripts/splashscreen.js @@ -5,10 +5,6 @@ const socket = io(wss); // Connect to socketio s // Websockets event handler // ###################################################################################################################################################### -socket.on('connected', (message) => { - console.log("Connected"); -}); - socket.on('refresh', (message) => { document.location.reload() // Reload page on received 'refresh' messagen }); \ No newline at end of file diff --git a/scoreboard/routes/index.js b/scoreboard/routes/index.js index 70d56ab..059748a 100644 --- a/scoreboard/routes/index.js +++ b/scoreboard/routes/index.js @@ -1,6 +1,5 @@ var express = require('express'); var router = express.Router(); -let io = require('../controllers/socketio'); const score = require('../controllers/score'); const etc = require('../controllers/etc'); @@ -18,15 +17,4 @@ router.get('/', function(req, res, next) { } }); -// Websocket-Verbindungshandler: wird ausgelöst, sobald ein Client (Monitor) sich verbindet -io.on('connection', (socket) => { - console.log("A user connected"); - socket.emit("Hello user from server"); // Begrüßungsnachricht an den neuen Client - - // Eingehende Nachrichten vom Client loggen (aktuell nur für Debugging) - socket.on('message', (message) => { - console.log(message) - }) -}) - module.exports = router;