v1 #1
@@ -98,6 +98,11 @@
|
|||||||
let name1 = "";
|
let name1 = "";
|
||||||
let name2 = "";
|
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
|
// Batterie-Banner State
|
||||||
let lowBatteryDevices = new Set();
|
let lowBatteryDevices = new Set();
|
||||||
let batteryBannerDismissed = false;
|
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
|
// Sync with backend every 1 second
|
||||||
setInterval(syncFromBackend, 1000);
|
setInterval(syncFromBackend, 1000);
|
||||||
|
|
||||||
@@ -490,6 +529,7 @@
|
|||||||
|
|
||||||
// Initial load
|
// Initial load
|
||||||
syncFromBackend();
|
syncFromBackend();
|
||||||
|
loadLaneConfig();
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -274,6 +274,30 @@
|
|||||||
border-right: 1px solid #e9ecef;
|
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 {
|
.restriction-notice {
|
||||||
background: #fff3cd;
|
background: #fff3cd;
|
||||||
color: #856404;
|
color: #856404;
|
||||||
|
|||||||
@@ -101,6 +101,55 @@
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Lane Configuration Section -->
|
||||||
|
<div class="section">
|
||||||
|
<h2>🏊♀️ Lane-Konfiguration</h2>
|
||||||
|
<form id="laneForm">
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Lane-Typ auswählen:</label>
|
||||||
|
<div class="mode-toggle">
|
||||||
|
<button type="button" class="mode-button active" data-lane-type="identical" onclick="selectLaneType('identical')">
|
||||||
|
⚖️ Identische Lanes
|
||||||
|
</button>
|
||||||
|
<button type="button" class="mode-button" data-lane-type="different" onclick="selectLaneType('different')">
|
||||||
|
⚡ Unterschiedliche Lanes
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="laneDifficultySelection" class="lane-difficulty-selection" style="display: none;">
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Lane 1 Schwierigkeit:</label>
|
||||||
|
<div class="mode-toggle">
|
||||||
|
<button type="button" class="mode-button active" data-lane="1" data-difficulty="light" onclick="selectLaneDifficulty(1, 'light')">
|
||||||
|
🟢 Leicht
|
||||||
|
</button>
|
||||||
|
<button type="button" class="mode-button" data-lane="1" data-difficulty="heavy" onclick="selectLaneDifficulty(1, 'heavy')">
|
||||||
|
🔴 Schwer
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Lane 2 Schwierigkeit:</label>
|
||||||
|
<div class="mode-toggle">
|
||||||
|
<button type="button" class="mode-button active" data-lane="2" data-difficulty="light" onclick="selectLaneDifficulty(2, 'light')">
|
||||||
|
🟢 Leicht
|
||||||
|
</button>
|
||||||
|
<button type="button" class="mode-button" data-lane="2" data-difficulty="heavy" onclick="selectLaneDifficulty(2, 'heavy')">
|
||||||
|
🔴 Schwer
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="button-group">
|
||||||
|
<button type="submit" class="btn btn-primary">
|
||||||
|
💾 Lane-Konfiguration speichern
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Basic Settings Section -->
|
<!-- Basic Settings Section -->
|
||||||
<div class="section">
|
<div class="section">
|
||||||
@@ -357,6 +406,7 @@
|
|||||||
updateCurrentTimeDisplay();
|
updateCurrentTimeDisplay();
|
||||||
loadWifiSettings();
|
loadWifiSettings();
|
||||||
loadMode();
|
loadMode();
|
||||||
|
loadLaneConfig();
|
||||||
};
|
};
|
||||||
|
|
||||||
// Aktuelle Zeit anzeigen (Live-Update)
|
// 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
|
// Einstellungen laden
|
||||||
function loadSettings() {
|
function loadSettings() {
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ UserData checkUser(const String &uid) {
|
|||||||
userData.firstname = responseDoc["firstname"].as<String>();
|
userData.firstname = responseDoc["firstname"].as<String>();
|
||||||
userData.lastname = responseDoc["lastname"].as<String>();
|
userData.lastname = responseDoc["lastname"].as<String>();
|
||||||
userData.alter = responseDoc["alter"] | 0;
|
userData.alter = responseDoc["alter"] | 0;
|
||||||
userData.exists = true;
|
userData.exists = responseDoc["exists"] | true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Serial.printf("User check failed, HTTP code: %d\n", httpCode);
|
Serial.printf("User check failed, HTTP code: %d\n", httpCode);
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ struct TimerData1 {
|
|||||||
bool isRunning = false;
|
bool isRunning = false;
|
||||||
bool isReady = true; // Status für Bahn 1
|
bool isReady = true; // Status für Bahn 1
|
||||||
bool isArmed = false; // Status für Bahn 1 (armiert/nicht armiert)
|
bool isArmed = false; // Status für Bahn 1 (armiert/nicht armiert)
|
||||||
|
char RFIDUID = "";
|
||||||
};
|
};
|
||||||
|
|
||||||
// Timer Struktur für Bahn 2
|
// Timer Struktur für Bahn 2
|
||||||
@@ -33,6 +34,7 @@ struct TimerData2 {
|
|||||||
bool isRunning = false;
|
bool isRunning = false;
|
||||||
bool isReady = true; // Status für Bahn 2
|
bool isReady = true; // Status für Bahn 2
|
||||||
bool isArmed = false; // Status für Bahn 2 (armiert/nicht armiert)
|
bool isArmed = false; // Status für Bahn 2 (armiert/nicht armiert)
|
||||||
|
char RFIDUID = "";
|
||||||
};
|
};
|
||||||
|
|
||||||
// Button Konfiguration
|
// Button Konfiguration
|
||||||
@@ -66,6 +68,11 @@ String masterlocation;
|
|||||||
int gamemode; // 0=Individual, 1=Wettkampf
|
int gamemode; // 0=Individual, 1=Wettkampf
|
||||||
bool startCompetition = false; // Flag, ob der Timer gestartet wurde
|
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
|
// Function Declarations
|
||||||
void OnDataRecv(const uint8_t *mac, const uint8_t *incomingData, int len);
|
void OnDataRecv(const uint8_t *mac, const uint8_t *incomingData, int len);
|
||||||
void handleLearningMode(const uint8_t *mac);
|
void handleLearningMode(const uint8_t *mac);
|
||||||
|
|||||||
@@ -42,6 +42,9 @@ void saveSettings() {
|
|||||||
preferences.putULong("maxTime", maxTimeBeforeReset);
|
preferences.putULong("maxTime", maxTimeBeforeReset);
|
||||||
preferences.putULong("maxTimeDisplay", maxTimeDisplay);
|
preferences.putULong("maxTimeDisplay", maxTimeDisplay);
|
||||||
preferences.putUInt("gamemode", gamemode);
|
preferences.putUInt("gamemode", gamemode);
|
||||||
|
preferences.putUInt("laneConfigType", laneConfigType);
|
||||||
|
preferences.putUInt("lane1DifficultyType", lane1DifficultyType);
|
||||||
|
preferences.putUInt("lane2DifficultyType", lane2DifficultyType);
|
||||||
preferences.end();
|
preferences.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,6 +53,9 @@ void loadSettings() {
|
|||||||
maxTimeBeforeReset = preferences.getULong("maxTime", 300000);
|
maxTimeBeforeReset = preferences.getULong("maxTime", 300000);
|
||||||
maxTimeDisplay = preferences.getULong("maxTimeDisplay", 20000);
|
maxTimeDisplay = preferences.getULong("maxTimeDisplay", 20000);
|
||||||
gamemode = preferences.getUInt("gamemode", 0);
|
gamemode = preferences.getUInt("gamemode", 0);
|
||||||
|
laneConfigType = preferences.getUInt("laneConfigType", 0);
|
||||||
|
lane1DifficultyType = preferences.getUInt("lane1DifficultyType", 0);
|
||||||
|
lane2DifficultyType = preferences.getUInt("lane2DifficultyType", 0);
|
||||||
preferences.end();
|
preferences.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -312,6 +312,53 @@ void setupRoutes() {
|
|||||||
request->send(200, "application/json", result);
|
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
|
// Statische Dateien
|
||||||
server.serveStatic("/", SPIFFS, "/");
|
server.serveStatic("/", SPIFFS, "/");
|
||||||
server.begin();
|
server.begin();
|
||||||
|
|||||||
Reference in New Issue
Block a user