diff --git a/README.md b/README.md index 32835fb..d9193b5 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ npm start ``` Der Server startet mit `nodemon` und ist anschließend unter Port **3000** erreichbar. -Die WebSocket-Verbindung läuft über denselben Port — es ist kein zweiter Port nötig. +Der Socket.IO-Server läuft separat auf Port **3001**. ### Port ändern @@ -65,7 +65,7 @@ scoreboard/ ├── bin/www # HTTP-Server, startet die App ├── db.json # Persistente Liste bekannter Teamnamen ├── controllers/ -│ ├── socketio.js # Socket.IO-Server (teilt sich den HTTP-Port) +│ ├── socketio.js # Socket.IO-Server (Port 3001) │ ├── timer.js # Timer-Logik (Start, Pause, Reset, IncDec) │ ├── score.js # Score-Logik (Punkte, Teams, Seitenwechsel) │ ├── db.js # Lesen/Schreiben der Teamnamen in db.json @@ -73,7 +73,7 @@ scoreboard/ │ └── cli.js # Start/Stop des Kiosk-Browsers auf dem Anzeigerechner ├── routes/ │ ├── admin.js # REST-Endpunkte für das Admin-Panel -│ └── index.js # Monitor-Ansicht (waehlt das passende Template) +│ └── index.js # Monitor-Ansicht + WebSocket-Verbindungshandler ├── views/ │ ├── admin.hbs # Admin-Panel (Handlebars-Template) │ ├── splashscreen.hbs # Monitor: Vereinslogo @@ -183,7 +183,7 @@ Admin-Panel (Browser) ├── controllers/db.js ──┐ │ └── controllers/cli.js │ │ io.sockets.emit(event, data) │ ▼ - db.json Socket.IO (am selben Port) + db.json Socket.IO-Server :3001 │ WebSocket-Verbindung │ @@ -197,12 +197,6 @@ Der gesamte Spielzustand (Timer, Score, Teams, Schalter) liegt **im Arbeitsspeicher** der Controller. Ein Serverneustart setzt ihn zurück; einzig die Teamnamen in `db.json` bleiben erhalten. -Bricht die WebSocket-Verbindung ab — auf dem Pi, der das WLAN selbst aufspannt, -kommt das vor — verbindet sich der Client automatisch neu und holt sich beim -`connect`-Event den aktuellen Stand über die REST-Endpunkte. Ein **Wechsel der -Ansicht** während der Trennung (Splashscreen, Scoreboard ein/aus) wird dabei -nicht bemerkt; dafür ist "Monitor neu laden" gedacht. - ### WebSocket-Events | Event | Richtung | Beschreibung | diff --git a/scoreboard/bin/www b/scoreboard/bin/www index a5def02..95be052 100644 --- a/scoreboard/bin/www +++ b/scoreboard/bin/www @@ -7,7 +7,6 @@ var app = require('../app'); var debug = require('debug')('scoreboard:server'); var http = require('http'); -var io = require('../controllers/socketio'); /** * Get port from environment and store in Express. @@ -22,12 +21,6 @@ app.set('port', port); var server = http.createServer(app); -/** - * Attach Socket.IO to the same server, so it shares the HTTP port. - */ - -io.attach(server); - /** * Listen on provided port, on all network interfaces. */ diff --git a/scoreboard/controllers/score.js b/scoreboard/controllers/score.js index 5a6ee10..1bdefe0 100644 --- a/scoreboard/controllers/score.js +++ b/scoreboard/controllers/score.js @@ -17,6 +17,16 @@ let teamB = { let sideswitch = false; // Attribute to switch sides after halftime let referenceMirrored = false; // If sitting behind the scoreboard, switch sides for admin board +// Funktionsübersicht (Prototypen ohne Implementierung — nur zur Übersicht) +function setEnabled(status) {}; +function configTeam(team, name, name2, isSpielgemeinschaft) {}; +function setScore(team, score) {}; +function alterScore(team, dir) {}; +function clearScore() {}; +function toggleSideswitch() {}; +function getEnabled() {}; +function getValues() {}; + // Enable or disable the scoreboard function setEnabled(status) { enabled = status; @@ -42,7 +52,6 @@ function setScore(team, score) { } else if(team == "teamB") { teamB.score = score; } - io.sockets.emit('score', print()); // Wie alterScore und clearScore den Monitor mitziehen } // Ändert den Score eines Teams um 1 — Richtung "inc" (erhöhen) oder "dec" (verringern) @@ -52,7 +61,7 @@ function alterScore(team, dir) { teamA.score++; console.log("teamA inc"); } - else if(dir == "dec" && teamA.score > 0) { // Kein negativer Spielstand + else if(dir == "dec") { teamA.score--; console.log("teamA dec"); } @@ -61,7 +70,7 @@ function alterScore(team, dir) { teamB.score++; console.log("teamB inc"); } - else if(dir == "dec" && teamB.score > 0) { // Kein negativer Spielstand + else if(dir == "dec") { teamB.score--; console.log("teamB dec"); } diff --git a/scoreboard/controllers/socketio.js b/scoreboard/controllers/socketio.js index 8647b03..ae048c3 100644 --- a/scoreboard/controllers/socketio.js +++ b/scoreboard/controllers/socketio.js @@ -1,25 +1,12 @@ -// Erstellt den zentralen Socket.IO-Server. +// Erstellt den zentralen Socket.IO-Server auf Port 3001. // Wird von timer.js, score.js und den Routen importiert, // um Events (Timer, Score, Refresh) an alle verbundenen Clients zu senden. const { Server } = require('socket.io'); -// Der Server wird ohne eigenen Port erzeugt und in bin/www per io.attach() an den -// HTTP-Server gehaengt. Dieses Modul wird beim Laden der Routen ausgewertet, also -// bevor der HTTP-Server existiert — daher die Trennung von Erzeugen und Anhaengen. -// Weil Socket.IO damit unter derselben Herkunft laeuft wie die Seite, entfaellt die -// CORS-Ausnahme. -const io = new Server(); - -// 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); - }); +// CORS auf "*" gesetzt, da Admin und Monitor auf unterschiedlichen Ports laufen können +const io = new Server(3001, { + cors: { origin: "*" } }); module.exports = io \ No newline at end of file diff --git a/scoreboard/controllers/timer.js b/scoreboard/controllers/timer.js index 4e8a02c..f029377 100644 --- a/scoreboard/controllers/timer.js +++ b/scoreboard/controllers/timer.js @@ -7,6 +7,15 @@ let durationLeft = moment.duration(7, 'minutes'); // durationLeft after the t let isPaused = true; // Status of the timer +// Function prototypes +function start() {}; +function pause() {}; +function reset() {}; +function end() {}; +function print() {}; +function incDec() {}; +function getValues() {}; + // Start timer not paused and not already finished function start() { if(isPaused == true && durationLeft.asSeconds() != 0) { // Only allow start if timer ist currently paused and durationLeft is not 0 seconds @@ -36,9 +45,7 @@ function pause() { return false } -// Reset timer to passed value, and send durationLeft to all clients. -// Ein laufender Timer wird bewusst nicht angehalten, sondern zaehlt von der -// neuen Zeit weiter herunter. +// Reset timer to passed value, and send durationLeft to all clients function reset(newDuration) { console.log("Timer Zurückgesetzt"); duration = moment.duration(newDuration, 'seconds').clone(); // Set initial duration to received duration in seconds @@ -62,8 +69,7 @@ function print() { // Increase or decrease the timer depending on the passed value in seconds function incDec(value) { - if(value < 0 && Math.abs(value) >= durationLeft.asSeconds()) { // Nur beim Verringern: faellt die Restzeit auf 0 oder darunter, Timer beenden - durationLeft = moment.duration(0, 'seconds'); // Restzeit sauber auf 00:00, sonst bliebe der Timer trotz "ENDE" startbar + if(Math.abs(value) >= durationLeft.asSeconds()) { // If abs from passed value is greater than seconds left on timer, end timer end(); } else { durationLeft.add(value, 'second'); @@ -81,9 +87,6 @@ function getValues() { }; } -// duration, durationLeft und isPaused werden bewusst nicht exportiert: Exporte -// werden beim Laden des Moduls einmal ausgewertet und wuerden dauerhaft die -// Startwerte liefern. Der aktuelle Stand kommt ueber getValues(). module.exports = { - start, pause, reset, end, print, incDec, getValues + duration, durationLeft, isPaused, start, pause, reset, end, print, incDec, getValues } \ No newline at end of file diff --git a/scoreboard/public/javascripts/admin.js b/scoreboard/public/javascripts/admin.js index ddc2ea6..1036eec 100644 --- a/scoreboard/public/javascripts/admin.js +++ b/scoreboard/public/javascripts/admin.js @@ -1,17 +1,12 @@ -// Socket.IO laeuft am selben Host und Port wie die Seite, io() ohne Argument -// verbindet sich dorthin zurueck. -const socket = io(); +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 die Werte neu -// geholt, damit das Panel nicht auf einem veralteten Stand stehen bleibt. -socket.on('connect', () => { - console.log("Verbunden"); - initialUpdate(); +socket.on('connected', (message) => { // Once client is connected to websocket + console.log("Connected"); }); socket.on('timerDurationLeft', (message) => { @@ -33,22 +28,6 @@ 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 @@ -125,19 +104,6 @@ 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); @@ -156,17 +122,27 @@ function updateDbFrontend(values) { let selectDelete = document.getElementById("dbTeamToDelete"); // Select for team deletion from db selectDelete.innerHTML = "" // Clears all existing options from select - for(const team of values.teams) { + //
  • Action
  • + + for(team of values.teams) { // Create custom Dropdowns with different function arguments for team configuration - teamAnameDropdown.appendChild(createTeamDropdownItem("teamAname", team)); - teamAname2Dropdown.appendChild(createTeamDropdownItem("teamAname2", team)); - teamBnameDropdown.appendChild(createTeamDropdownItem("teamBname", team)); - teamBname2Dropdown.appendChild(createTeamDropdownItem("teamBname2", team)); + 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); // Create select options for deletion in debugwindow let opt = document.createElement("option"); opt.value = team; - opt.textContent = team; + opt.innerHTML = team; selectDelete.appendChild(opt); } } @@ -208,14 +184,16 @@ async function killBrowser() { // Request important values for etc stuff async function etcGetValues() { - const data = await fetchJson("/admin/etcGetValues"); // Call API Endpoint /admin/etcGetValues - if(data) updateEtcFrontend(data); // Update admin frontend + 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 } // Toggle index page to also show scoreboard async function etcToggleSplashscreen() { - const data = await fetchJson("/admin/etcToggleSplashscreen");// Call API Endpoint /admin/etcToggleSplashscreen - if(data) updateEtcFrontend(data); // Update admin frontend + 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 refreshMonitor(); // Refresh main monitor to display new frontend } @@ -225,8 +203,9 @@ async function etcToggleSplashscreen() { // 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 + 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 } // Request to start the timer @@ -256,22 +235,24 @@ async function timerPause() { // Request to reset the timer with the specified time async function timerReset() { let newDuration = document.getElementById("timerSelectDuration").value; - const data = await fetchJson("/admin/timerReset", { // Call API Endpoint /admin/timerStart with selected new duration in seconds + const response = await fetch("/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}) }); - if(data) updateTimerFrontend(data); // Update admin frontend + const data = await response.json(); // Wait for asyncronous transfer to complete + updateTimerFrontend(data); // Update admin frontend } // Request to increase or decrease the timer depending on passed value in seconds async function timerIncDec(value) { - const data = await fetchJson("/admin/timerIncDec", { // Call API Endpoint /admin/timerIncDec with value in seconds to increase or decrease the timer + const response = await fetch("/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}) }); - if(data) updateTimerFrontend(data); // Update admin frontend + const data = await response.json(); // Wait for asyncronous transfer to complete + updateTimerFrontend(data); // Update admin frontend } // ###################################################################################################################################################### @@ -280,43 +261,49 @@ async function timerIncDec(value) { // Request important values for score async function scoreGetValues() { - const data = await fetchJson("/admin/scoreGetValues"); // Call API Endpoint /admin/scoreGetValues - if(data) updateScoreFrontend(data); // Update admin frontend + 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 } // Toggle index page to also show scoreboard async function scoreToggle() { - const data = await fetchJson("/admin/scoreToggle"); // Call API Endpoint /admin/scoreToggle - if(data) updateScoreFrontend(data); // Update admin frontend + 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 refreshMonitor(); // Refresh main monitor to display new frontend } // Alter the score of passed team in the specified direction async function scoreAlterScore(team, dir) { - const data = await fetchJson("/admin/scoreAlterScore", { // Call API Endpoint /admin/scoreAlterScore with the teamname to alter the score in the specified direction + const response = await fetch("/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}) }); - if(data) updateScoreFrontend(data); // Update admin frontend + const data = await response.json(); // Wait for asyncronous transfer to complete + updateScoreFrontend(data); // Update admin frontend } // Switch sides async function scoreToggleSideswitch() { - const data = await fetchJson("/admin/scoreToggleSideswitch");// Call API Endpoint /admin/scoreSwitch - if(data) updateScoreFrontend(data); // Update admin frontend + 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 } // Switch sides to mirror admin panel when sitting behind main monitor async function scoreToggleReferenceMirrored() { - const data = await fetchJson("/admin/scoreToggleReferenceMirrored");// Call API Endpoint /admin/scoreSwitch - if(data) updateScoreFrontend(data); // Update admin frontend + 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 } // Clear score async function scoreClearScore() { - const data = await fetchJson("/admin/scoreClearScore"); // Call API Endpoint /admin/scoreClearScore - if(data) updateScoreFrontend(data); // Update admin frontend + 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 } // Configure Teams @@ -333,12 +320,13 @@ async function scoreConfigTeams() { } console.log(teamA, teamB) - const data = await fetchJson("/admin/scoreConfigTeams", { // Call API Endpoint /admin/scoreAlterScore with the teamname to alter the score in the specified direction + const response = await fetch("/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}) }); - if(data) updateScoreFrontend(data); // Update admin frontend + const data = await response.json(); // Wait for asyncronous transfer to complete + updateScoreFrontend(data); // Update admin frontend refreshMonitor(); } @@ -348,28 +336,31 @@ async function scoreConfigTeams() { // Request important values for db contents async function dbGetValues() { - const data = await fetchJson("/admin/dbGetValues"); // Call API Endpoint /admin/dbGetValues - if(data) updateDbFrontend(data); // Update admin frontend + 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 } // Add team to DB async function dbAddTeam() { - const data = await fetchJson("/admin/dbAddTeam", { // Call API Endpoint /admin/scoreAlterScore with the teamname to alter the score in the specified direction + const response = await fetch("/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 - if(data) updateDbFrontend(data); + const data = await response.json(); // Wait for asyncronous transfer to complete + updateDbFrontend(data); } // Delete team from DB async function dbDeleteTeam() { let teamName = document.getElementById("dbTeamToDelete").value; - const data = await fetchJson("/admin/dbDeleteTeam", { // Call API Endpoint /admin/scoreAlterScore with the teamname to alter the score in the specified direction + const response = await fetch("/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}) }); - if(data) updateDbFrontend(data); + const data = await response.json(); // Wait for asyncronous transfer to complete + updateDbFrontend(data); } \ No newline at end of file diff --git a/scoreboard/public/javascripts/index.js b/scoreboard/public/javascripts/index.js index 9a2dc0f..1e52645 100644 --- a/scoreboard/public/javascripts/index.js +++ b/scoreboard/public/javascripts/index.js @@ -1,17 +1,12 @@ -// Socket.IO laeuft am selben Host und Port wie die Seite, io() ohne Argument -// verbindet sich dorthin zurueck. -const socket = io(); +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('connected', (message) => { + console.log("Connected"); }); socket.on('timerDurationLeft', (message) => { @@ -31,22 +26,6 @@ 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 @@ -63,6 +42,7 @@ async function initialUpdate() { // 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 + 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 } \ No newline at end of file diff --git a/scoreboard/public/javascripts/indexScore.js b/scoreboard/public/javascripts/indexScore.js index 5553845..476cec9 100644 --- a/scoreboard/public/javascripts/indexScore.js +++ b/scoreboard/public/javascripts/indexScore.js @@ -1,17 +1,12 @@ -// Socket.IO laeuft am selben Host und Port wie die Seite, io() ohne Argument -// verbindet sich dorthin zurueck. -const socket = io(); +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('connected', (message) => { + console.log("Connected"); }); socket.on('timerDurationLeft', (message) => { @@ -40,22 +35,6 @@ 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 @@ -108,8 +87,9 @@ async function initialUpdate() { // 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 + 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 } // ###################################################################################################################################################### @@ -118,7 +98,8 @@ async function timerGetValues() { // Request important values for score async function scoreGetValues() { - const data = await fetchJson("/admin/scoreGetValues"); // Call API Endpoint /admin/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) console.log(data); // Print received data - if(data) updateScoreFrontend(data); // Update admin frontend with received values for scoreboard + 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 03bcc1f..3053f07 100644 --- a/scoreboard/public/javascripts/splashscreen.js +++ b/scoreboard/public/javascripts/splashscreen.js @@ -1,11 +1,14 @@ -// Socket.IO laeuft am selben Host und Port wie die Seite, io() ohne Argument -// verbindet sich dorthin zurueck. -const socket = io(); +let wss = "ws://" + window.location.hostname + ":3001" // Build socketio endpoint from window.location +const socket = io(wss); // Connect to socketio server // ###################################################################################################################################################### // 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 059748a..70d56ab 100644 --- a/scoreboard/routes/index.js +++ b/scoreboard/routes/index.js @@ -1,5 +1,6 @@ var express = require('express'); var router = express.Router(); +let io = require('../controllers/socketio'); const score = require('../controllers/score'); const etc = require('../controllers/etc'); @@ -17,4 +18,15 @@ 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; diff --git a/scoreboard/views/admin.hbs b/scoreboard/views/admin.hbs index e16cf42..735a685 100644 --- a/scoreboard/views/admin.hbs +++ b/scoreboard/views/admin.hbs @@ -298,7 +298,7 @@

    Achtung, eine Fehlbedienung kann unerwünschtes Verhalten hervorrufen.

    - +
    @@ -306,7 +306,7 @@
    - +


    @@ -316,7 +316,7 @@

    Team Datenbank:

    - +