From 4f0fc68d41f28d194261de8c64ce3013e471fd36 Mon Sep 17 00:00:00 2001 From: Carsten Graf Date: Thu, 11 Sep 2025 13:56:07 +0200 Subject: [PATCH] Lane difficulty added --- data/index.html | 40 +++++++++++ data/settings.css | 24 +++++++ data/settings.html | 154 ++++++++++++++++++++++++++++++++++++++++ src/databasebackend.h | 2 +- src/master.h | 7 ++ src/preferencemanager.h | 6 ++ src/webserverrouter.h | 47 ++++++++++++ 7 files changed, 279 insertions(+), 1 deletion(-) diff --git a/data/index.html b/data/index.html index 0e2d1a1..d9577d3 100644 --- a/data/index.html +++ b/data/index.html @@ -98,6 +98,11 @@ let name1 = ""; let name2 = ""; + // Lane Configuration + let laneConfigType = 0; // 0=Identical, 1=Different + let lane1DifficultyType = 0; // 0=Light, 1=Heavy + let lane2DifficultyType = 0; // 0=Light, 1=Heavy + // Batterie-Banner State let lowBatteryDevices = new Set(); let batteryBannerDismissed = false; @@ -467,6 +472,40 @@ ); } + function loadLaneConfig() { + fetch("/api/get-lane-config") + .then((response) => response.json()) + .then((data) => { + laneConfigType = data.type === "different" ? 1 : 0; + lane1DifficultyType = data.lane1Difficulty === "heavy" ? 1 : 0; + lane2DifficultyType = data.lane2Difficulty === "heavy" ? 1 : 0; + updateLaneDisplay(); + }) + .catch((error) => + console.error("Fehler beim Laden der Lane-Schwierigkeits-Konfiguration:", error) + ); + } + + function updateLaneDisplay() { + const lane1Title = document.querySelector('.lane h2'); + const lane2Title = document.querySelectorAll('.lane h2')[1]; + + if (laneConfigType === 0) { + // Identische Lanes + lane1Title.textContent = "🏊‍♀️ Bahn 1"; + lane2Title.textContent = "🏊‍♂️ Bahn 2"; + } else { + // Unterschiedliche Lanes + const lane1Icon = lane1DifficultyType === 0 ? "🟢" : "🔴"; + const lane2Icon = lane2DifficultyType === 0 ? "🟢" : "🔴"; + const lane1Difficulty = lane1DifficultyType === 0 ? "Leicht" : "Schwer"; + const lane2Difficulty = lane2DifficultyType === 0 ? "Leicht" : "Schwer"; + + lane1Title.textContent = `${lane1Icon} Bahn 1 (${lane1Difficulty})`; + lane2Title.textContent = `${lane2Icon} Bahn 2 (${lane2Difficulty})`; + } + } + // Sync with backend every 1 second setInterval(syncFromBackend, 1000); @@ -490,6 +529,7 @@ // Initial load syncFromBackend(); + loadLaneConfig(); diff --git a/data/settings.css b/data/settings.css index bf8c406..9d91852 100644 --- a/data/settings.css +++ b/data/settings.css @@ -274,6 +274,30 @@ border-right: 1px solid #e9ecef; } + /* Lane Difficulty Selection Styles */ + .lane-difficulty-selection { + margin-top: 20px; + padding: 20px; + background: rgba(73, 186, 228, 0.1); + border-radius: 12px; + border: 2px solid rgba(73, 186, 228, 0.2); + } + + .lane-difficulty-selection .form-group { + margin-bottom: 25px; + } + + .lane-difficulty-selection .form-group:last-child { + margin-bottom: 0; + } + + .lane-difficulty-selection label { + font-weight: 600; + color: #223c83; + margin-bottom: 12px; + display: block; + } + .restriction-notice { background: #fff3cd; color: #856404; diff --git a/data/settings.html b/data/settings.html index 794f355..9acf738 100644 --- a/data/settings.html +++ b/data/settings.html @@ -101,6 +101,55 @@ + +
+

🏊‍♀️ Lane-Konfiguration

+
+
+ +
+ + +
+
+ + + +
+ +
+
+
@@ -357,6 +406,7 @@ updateCurrentTimeDisplay(); loadWifiSettings(); loadMode(); + loadLaneConfig(); }; // Aktuelle Zeit anzeigen (Live-Update) @@ -554,6 +604,110 @@ }); } + // Lane Type selection function + function selectLaneType(type) { + // Remove active class from all lane type buttons + document.querySelectorAll('[data-lane-type]').forEach(button => { + button.classList.remove('active'); + }); + + // Add active class to selected button + document.querySelector(`[data-lane-type="${type}"]`).classList.add('active'); + + // Show/hide lane difficulty selection + const difficultySelection = document.getElementById('laneDifficultySelection'); + if (type === 'different') { + difficultySelection.style.display = 'block'; + } else { + difficultySelection.style.display = 'none'; + } + } + + // Lane Difficulty selection function + function selectLaneDifficulty(lane, difficulty) { + // Remove active class from all buttons for this lane + document.querySelectorAll(`[data-lane="${lane}"]`).forEach(button => { + button.classList.remove('active'); + }); + + // Add active class to selected button + document.querySelector(`[data-lane="${lane}"][data-difficulty="${difficulty}"]`).classList.add('active'); + } + + // Lane form handler + document.getElementById('laneForm').addEventListener('submit', function(e) { + e.preventDefault(); + + const activeLaneTypeButton = document.querySelector('[data-lane-type].active'); + const laneType = activeLaneTypeButton ? activeLaneTypeButton.getAttribute('data-lane-type') : 'identical'; + + let laneConfig = { + type: laneType + }; + + if (laneType === 'different') { + const lane1Difficulty = document.querySelector('[data-lane="1"].active')?.getAttribute('data-difficulty') || 'light'; + const lane2Difficulty = document.querySelector('[data-lane="2"].active')?.getAttribute('data-difficulty') || 'light'; + + laneConfig.lane1Difficulty = lane1Difficulty; + laneConfig.lane2Difficulty = lane2Difficulty; + } + + fetch('/api/set-lane-config', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(laneConfig) + }) + .then(response => response.json()) + .then(data => { + if (data.success) { + showMessage('Lane-Schwierigkeits-Konfiguration erfolgreich gespeichert!', 'success'); + } else { + showMessage('Fehler beim Speichern der Lane-Schwierigkeits-Konfiguration', 'error'); + } + }) + .catch(error => showMessage('Verbindungsfehler', 'error')); + }); + + function loadLaneConfig() { + fetch("/api/get-lane-config") + .then((response) => response.json()) + .then((data) => { + const laneType = data.type || "identical"; + const lane1Difficulty = data.lane1Difficulty || "light"; + const lane2Difficulty = data.lane2Difficulty || "light"; + + // Set lane type + document.querySelectorAll('[data-lane-type]').forEach(button => { + button.classList.remove('active'); + }); + const laneTypeBtn = document.querySelector(`[data-lane-type="${laneType}"]`); + if (laneTypeBtn) laneTypeBtn.classList.add('active'); + + // Set lane difficulties + document.querySelectorAll('[data-lane]').forEach(button => { + button.classList.remove('active'); + }); + const lane1Btn = document.querySelector(`[data-lane="1"][data-difficulty="${lane1Difficulty}"]`); + const lane2Btn = document.querySelector(`[data-lane="2"][data-difficulty="${lane2Difficulty}"]`); + if (lane1Btn) lane1Btn.classList.add('active'); + if (lane2Btn) lane2Btn.classList.add('active'); + + // Show/hide difficulty selection + const difficultySelection = document.getElementById('laneDifficultySelection'); + if (laneType === 'different') { + difficultySelection.style.display = 'block'; + } else { + difficultySelection.style.display = 'none'; + } + }) + .catch((error) => { + showMessage("Fehler beim Laden der Lane-Schwierigkeits-Konfiguration", "error"); + }); + } + // Einstellungen laden function loadSettings() { diff --git a/src/databasebackend.h b/src/databasebackend.h index f9c9eec..6435533 100644 --- a/src/databasebackend.h +++ b/src/databasebackend.h @@ -78,7 +78,7 @@ UserData checkUser(const String &uid) { userData.firstname = responseDoc["firstname"].as(); userData.lastname = responseDoc["lastname"].as(); userData.alter = responseDoc["alter"] | 0; - userData.exists = true; + userData.exists = responseDoc["exists"] | true; } } else { Serial.printf("User check failed, HTTP code: %d\n", httpCode); diff --git a/src/master.h b/src/master.h index 73fdb16..811c415 100644 --- a/src/master.h +++ b/src/master.h @@ -21,6 +21,7 @@ struct TimerData1 { bool isRunning = false; bool isReady = true; // Status für Bahn 1 bool isArmed = false; // Status für Bahn 1 (armiert/nicht armiert) + char RFIDUID = ""; }; // Timer Struktur für Bahn 2 @@ -33,6 +34,7 @@ struct TimerData2 { bool isRunning = false; bool isReady = true; // Status für Bahn 2 bool isArmed = false; // Status für Bahn 2 (armiert/nicht armiert) + char RFIDUID = ""; }; // Button Konfiguration @@ -66,6 +68,11 @@ String masterlocation; int gamemode; // 0=Individual, 1=Wettkampf bool startCompetition = false; // Flag, ob der Timer gestartet wurde +// Lane Configuration +int laneConfigType = 0; // 0=Identical, 1=Different +int lane1DifficultyType = 0; // 0=Light, 1=Heavy (difficulty) +int lane2DifficultyType = 0; // 0=Light, 1=Heavy (difficulty) + // Function Declarations void OnDataRecv(const uint8_t *mac, const uint8_t *incomingData, int len); void handleLearningMode(const uint8_t *mac); diff --git a/src/preferencemanager.h b/src/preferencemanager.h index f880f9a..bcfc7ee 100644 --- a/src/preferencemanager.h +++ b/src/preferencemanager.h @@ -42,6 +42,9 @@ void saveSettings() { preferences.putULong("maxTime", maxTimeBeforeReset); preferences.putULong("maxTimeDisplay", maxTimeDisplay); preferences.putUInt("gamemode", gamemode); + preferences.putUInt("laneConfigType", laneConfigType); + preferences.putUInt("lane1DifficultyType", lane1DifficultyType); + preferences.putUInt("lane2DifficultyType", lane2DifficultyType); preferences.end(); } @@ -50,6 +53,9 @@ void loadSettings() { maxTimeBeforeReset = preferences.getULong("maxTime", 300000); maxTimeDisplay = preferences.getULong("maxTimeDisplay", 20000); gamemode = preferences.getUInt("gamemode", 0); + laneConfigType = preferences.getUInt("laneConfigType", 0); + lane1DifficultyType = preferences.getUInt("lane1DifficultyType", 0); + lane2DifficultyType = preferences.getUInt("lane2DifficultyType", 0); preferences.end(); } diff --git a/src/webserverrouter.h b/src/webserverrouter.h index 4a20332..5c20ef2 100644 --- a/src/webserverrouter.h +++ b/src/webserverrouter.h @@ -312,6 +312,53 @@ void setupRoutes() { request->send(200, "application/json", result); }); + // Lane Configuration API Routes + server.on("/api/set-lane-config", HTTP_POST, [](AsyncWebServerRequest *request) { + Serial.println("/api/set-lane-config called"); + + String body = request->getBody(); + DynamicJsonDocument doc(256); + deserializeJson(doc, body); + + if (doc.containsKey("type")) { + String laneType = doc["type"]; + laneConfigType = (laneType == "identical") ? 0 : 1; + + if (laneConfigType == 1 && doc.containsKey("lane1Difficulty") && doc.containsKey("lane2Difficulty")) { + String lane1Difficulty = doc["lane1Difficulty"]; + String lane2Difficulty = doc["lane2Difficulty"]; + lane1DifficultyType = (lane1Difficulty == "light") ? 0 : 1; + lane2DifficultyType = (lane2Difficulty == "light") ? 0 : 1; + } + + Serial.printf("Lane configuration set - Type: %s, Lane1: %s, Lane2: %s\n", + laneType.c_str(), + (laneConfigType == 1) ? ((lane1DifficultyType == 0) ? "light" : "heavy") : "identical", + (laneConfigType == 1) ? ((lane2DifficultyType == 0) ? "light" : "heavy") : "identical"); + + DynamicJsonDocument response(64); + response["success"] = true; + String result; + serializeJson(response, result); + request->send(200, "application/json", result); + saveSettings(); + } else { + request->send(400, "application/json", "{\"success\":false,\"error\":\"Lane type missing\"}"); + } + }); + + server.on("/api/get-lane-config", HTTP_GET, [](AsyncWebServerRequest *request) { + DynamicJsonDocument doc(128); + doc["type"] = laneConfigType == 0 ? "identical" : "different"; + if (laneConfigType == 1) { + doc["lane1Difficulty"] = lane1DifficultyType == 0 ? "light" : "heavy"; + doc["lane2Difficulty"] = lane2DifficultyType == 0 ? "light" : "heavy"; + } + String result; + serializeJson(doc, result); + request->send(200, "application/json", result); + }); + // Statische Dateien server.serveStatic("/", SPIFFS, "/"); server.begin();