Updated javasccript of WebUI

This commit is contained in:
Marcel Peterkau 2024-05-30 23:55:54 +02:00
parent 5ee0a23a6d
commit ab2ab0e0c1
20 changed files with 468 additions and 113 deletions

@ -1,28 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>KTM CAN Chain Oiler</title>
<meta http-equiv="content-type" content="text/html;charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="static/css/bootstrap.min.css">
<link rel="stylesheet" href="static/css/custom.css">
<script src="static/js/jquery.min.js"></script>
<script src="static/js/bootstrap.min.js"></script>
<link rel="apple-touch-icon" sizes="180x180" href="static/img/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="static/img/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="static/img/favicon-16x16.png">
<link rel="manifest" href="static/img/site.webmanifest">
<meta http-equiv="refresh" content="3; url='index.htm'" />
</head>
<body>
<div class="container" style="display: flex; justify-content: center; align-items: center; height: 100vh">
<div class="alert alert-success">
<strong>Bitte warten!</strong> Änderungen werden übernommen.
</div>
</div>
</body>
</html>

@ -4,7 +4,7 @@
* Copyright 2011-2018 Twitter, Inc. * Copyright 2011-2018 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
*/ */
:root { :root {
--blue: #007bff; --blue: #007bff;
--indigo: #6610f2; --indigo: #6610f2;
--purple: #6f42c1; --purple: #6f42c1;

@ -25,3 +25,66 @@ hr {
padding: 10px; padding: 10px;
} }
#overlay {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.8); /* Dunkler Hintergrund mit Transparenz */
color: white; /* Textfarbe */
justify-content: center;
align-items: center;
z-index: 9999; /* Stellen Sie sicher, dass es über anderen Elementen liegt */
}
.overlay-content {
text-align: center;
font-size: 4rem;
}
.loader {
width: 96px;
height: 96px;
border: 12px solid #FFF;
border-radius: 50%;
display: inline-block;
position: relative;
box-sizing: border-box;
animation: rotation 1s linear infinite;
}
.loader::after {
content: '';
box-sizing: border-box;
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
width: 120px;
height: 120px;
border-radius: 50%;
border: 12px solid transparent;
border-bottom-color: #FF3D00;
}
@keyframes rotation {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
.notification-container {
position: fixed;
top: 30%;
left: 50%;
transform: translateX(-50%);
z-index: 1000;
}
.notification {
margin-bottom: 20px; /* Fügen Sie bei Bedarf weitere Stile hinzu */
}

@ -0,0 +1,196 @@
const jsonFilePath = "static/dtc_table.json";
var dtcState = {};
async function processDTCNotifications(dtcArray) {
if (dtcArray.length === 0 || dtcArray[0] == "0") {
dtcState = {};
return;
}
for (var i = 0; i < dtcArray.length; i++) {
var dtcInfo = dtcArray[i].split(",");
var errorCode = parseInt(dtcInfo[1]);
var activity = parseInt(dtcInfo[3]);
var severity = parseInt(dtcInfo[2]);
try {
var { title, description } = await getDescriptionForDTCNumber(errorCode);
switch (severity) {
case 1:
severity = "info";
break;
case 2:
severity = "warning";
break;
case 3:
severity = "danger";
break;
}
if (dtcState[errorCode]) {
// Überprüfen, ob sich der Zustand von "previous" auf "active" geändert hat
if (activity !== dtcState[errorCode]) {
dtcState[errorCode] = activity;
if (activity === 1) showNotification(description, severity);
}
} else {
// DTC ist neu, Zustand speichern und Benachrichtigung anzeigen
dtcState[errorCode] = activity;
showNotification(description, severity);
}
} catch (error) {
console.error("Error processing DTC:", error);
}
}
}
function getDescriptionForDTCNumber(number) {
return new Promise((resolve, reject) => {
fetch(jsonFilePath)
.then((response) => response.json())
.then((data) => {
const dtcList = data.dtc_table_data;
const foundEntry = dtcList.find((entry) => entry.num === number);
if (foundEntry) {
const description = foundEntry.description;
const title = foundEntry.title;
resolve({ title, description });
} else {
// Wenn die Nummer nicht gefunden wurde, geben Sie einen Fehler zurück
reject(`Beschreibung für Nummer ${number} nicht gefunden.`);
}
})
.catch((error) => {
// Im Fehlerfall geben Sie den Fehler zurück
reject(error);
});
});
}
async function showDTCModal(event) {
var dtc = parseInt(event.currentTarget.getAttribute("data-dtc"));
var debugval = event.currentTarget.getAttribute("data-debugval");
var modal = $("#dtcModal");
try {
var { title, description } = await getDescriptionForDTCNumber(dtc);
modal.find(".modal-title").text(title);
modal.find(".dtc-desc").text(description);
if (debugval > 0) {
modal.find(".dtc-debugval").text("Debugvalue: " + debugval);
} else {
modal.find(".dtc-debugval").remove();
}
} catch (error) {
console.error("Fehler beim Abrufen der Beschreibung:", error);
modal.find(".modal-title").text("Fehler");
modal.find(".dtc-desc").text("DTC-Beschreibung konnte nicht geladen werden");
}
// Modal anzeigen
modal.modal("show");
}
function fillDTCTable(dtcArray) {
// Referenz auf das Tabellen-Element
var table = document.getElementById("dtc_table");
var tablediv = document.getElementById("dtc_container");
// Prüfen, ob DTC vorhanden sind
if (dtcArray.length === 0 || dtcArray[0] == "0") {
// Verstecke das Tabellen-Div, wenn keine DTC vorhanden sind
tablediv.hidden = true;
table.innerHTML = "";
return;
} else {
// Zeige das Tabellen-Div, wenn DTC vorhanden sind
tablediv.hidden = false;
}
// Tabelle leeren, bevor sie neu gefüllt wird
table.innerHTML = "";
// Überschriften für die Tabelle erstellen
var headerRow = table.insertRow(0);
// Definition der Klassen und Scopes für die Spalten
var columnDefinitions = [
{ class: "col-6", scope: "Zeitstempel" },
{ class: "col-2", scope: "Fehlercode" },
{ class: "col-2", scope: "Schwere" },
{ class: "col-2", scope: "Aktiv" },
];
for (var i = 0; i < columnDefinitions.length; i++) {
var headerCell = headerRow.insertCell(i);
headerCell.className = `th ${columnDefinitions[i].class}`;
headerCell.scope = columnDefinitions[i].scope;
headerCell.innerHTML = columnDefinitions[i].scope;
}
// DTC-Daten in die Tabelle einfügen
for (var i = 0; i < dtcArray.length; i++) {
var dtcInfo = dtcArray[i].split(",");
var row = table.insertRow(i + 1); // +1 wegen der Überschriftenzeile
// Zeitstempel
var timestampCell = row.insertCell(0);
timestampCell.innerHTML = formatTimestamp(parseInt(dtcInfo[0]));
// Fehlercode
var errorCodeCell = row.insertCell(1);
errorCodeCell.innerHTML = dtcInfo[1];
// Schwere
var severityCell = row.insertCell(2);
var severity = parseInt(dtcInfo[2]);
// Schwere
switch (severity) {
case 1:
severityCell.innerHTML = '<img src="static/img/info.png" alt="Info" />';
break;
case 2:
severityCell.innerHTML =
'<img src="static/img/warn.png" alt="Warnung" />';
break;
case 3:
severityCell.innerHTML =
'<img src="static/img/critical.png" alt="Kritisch" />';
break;
default:
severityCell.innerHTML =
'<img src="static/img/none.png" alt="Unbekannt" />';
}
row.setAttribute("data-dtc", dtcInfo[1]);
row.setAttribute("data-debugval", dtcInfo[4]);
row.addEventListener("click", showDTCModal);
// Aktivität
var activityCell = row.insertCell(3);
activityCell.innerHTML = parseInt(dtcInfo[3]) === 1 ? "active" : "previous";
}
}
function formatTimestamp(milliseconds) {
const date = new Date(milliseconds);
const days = String(date.getUTCDate() - 1).padStart(2, "0");
const hours = String(date.getUTCHours()).padStart(2, "0");
const minutes = String(date.getUTCMinutes()).padStart(2, "0");
const seconds = String(date.getUTCSeconds()).padStart(2, "0");
const millisecondsFormatted = String(date.getUTCMilliseconds()).padStart(
3,
"0"
);
return `${days}-${hours}:${minutes}:${seconds}:${millisecondsFormatted}`;
}

@ -0,0 +1,27 @@
$(document).ready(function () {
$(".navbar-nav a").on("click", function () {
$(".navbar-collapse").collapse("hide");
});
$("#show_hide_password a").on("click", function (event) {
event.preventDefault();
if ($("#show_hide_password input").attr("type") == "text") {
$("#show_hide_password input").attr("type", "password");
$("#show_hide_password i").addClass("fa-eye-slash");
$("#show_hide_password i").removeClass("fa-eye");
} else if ($("#show_hide_password input").attr("type") == "password") {
$("#show_hide_password input").attr("type", "text");
$("#show_hide_password i").removeClass("fa-eye-slash");
$("#show_hide_password i").addClass("fa-eye");
}
});
});
document
.querySelector(".custom-file-input")
.addEventListener("change", function (e) {
var fileName = document.getElementById("fw-update-file").files[0].name;
var nextSibling = e.target.nextElementSibling;
nextSibling.innerText = fileName;
});

@ -1,7 +1,15 @@
var gateway = `ws://${window.location.hostname}/ws`; var gateway = `ws://${window.location.hostname}/ws`;
var websocket; var websocket;
window.addEventListener("load", onLoad); var statusMapping;
var staticMapping;
var overlay;
document.addEventListener("DOMContentLoaded", function () {
// Ihr JavaScript-Code hier, einschließlich der onLoad-Funktion
overlay = document.getElementById("overlay");
onLoad();
});
function initWebSocket() { function initWebSocket() {
console.log("Trying to open a WebSocket connection..."); console.log("Trying to open a WebSocket connection...");
@ -12,12 +20,27 @@ function initWebSocket() {
} }
function initButtons() { function initButtons() {
document var elements = document.getElementsByClassName("btn-wsevent");
.getElementById("btn-ws-stop")
.addEventListener("click", livedebug_stop); if (elements.length > 0) {
document for (var i = 0; i < elements.length; i++) {
.getElementById("btn-ws-start") let element = elements[i];
.addEventListener("click", livedebug_start); element.addEventListener("click", sendButton);
}
}
}
function initSettingInputs() {
var elements = document.getElementsByClassName("set-wsevent");
if (elements.length > 0) {
for (var i = 0; i < elements.length; i++) {
let element = elements[i];
element.addEventListener("change", function () {
websocket_sendevent("set-" + element.id, element.value);
});
}
}
} }
function onOpen(event) { function onOpen(event) {
@ -26,28 +49,95 @@ function onOpen(event) {
function onClose(event) { function onClose(event) {
console.log("Connection closed"); console.log("Connection closed");
setTimeout(initWebSocket, 2000); setTimeout(initWebSocket, 1000);
overlay.style.display = "flex";
}
function sendButton(event) {
var targetElement = event.target;
if (
targetElement.classList.contains("confirm") &&
window.confirm("Sicher?") == false
)
return;
websocket_sendevent("btn-" + targetElement.id, targetElement.value);
} }
function onMessage(event) { function onMessage(event) {
var livedebug_out = document.getElementById("livedebug-out"); var data = event.data;
var textarea_heigth = livedebug_out.scrollHeight;
livedebug_out.value += event.data; if (data.startsWith("NOTIFY:")) {
livedebug_out.scrollTop = livedebug_out.scrollHeight; var notify_data = data.slice(7).split(";")[1];
do_resize(livedebug_out); var notify_type = data.slice(7).split(";")[0];
showNotification(notify_data, notify_type);
} else if (data.startsWith("DEBUG:")) {
var addtext = data.slice(6);
var livedebug_out = document.getElementById("livedebug-out");
livedebug_out.value += addtext;
livedebug_out.scrollTop = livedebug_out.scrollHeight;
do_resize(livedebug_out);
} else if (data.startsWith("DTC:")) {
const dtcs = data.slice(4);
const dtcArray = dtcs.trim() !== "" ? dtcs.split(";").filter(Boolean) : [];
processDTCNotifications(dtcArray);
fillDTCTable(dtcArray);
} else if (data.startsWith("MAPPING_STATUS:")) {
const data_sliced = data.slice(15);
statusMapping = createMapping(data_sliced);
} else if (data.startsWith("MAPPING_STATIC:")) {
const data_sliced = data.slice(15);
staticMapping = createMapping(data_sliced);
console.log(staticMapping);
} else if (data.startsWith("STATUS:")) {
const data_sliced = data.slice(7);
const result = processDataString(data_sliced, statusMapping);
fillValuesToHTML(result);
} else if (data.startsWith("STATIC:")) {
const data_sliced = data.slice(7);
const result = processDataString(data_sliced, staticMapping);
fillValuesToHTML(result);
console.log(result);
overlay.style.display = "none";
}
}
function createMapping(mappingString) {
const mappingArray = mappingString.split(";");
const mapping = [];
mappingArray.forEach((variable) => {
if (variable !== null) mapping.push(variable.trim());
});
return mapping;
}
function processDataString(dataString, mapping) {
const valuesArray = dataString.split(";");
const dataObject = {};
valuesArray.forEach((value, index) => {
const variable = mapping[index];
if (variable) {
dataObject[variable] = value.trim();
}
});
return dataObject;
} }
function onLoad(event) { function onLoad(event) {
initWebSocket(); initWebSocket();
initButtons(); initButtons();
initSettingInputs();
overlay.style.display = "flex";
} }
function livedebug_start() { function websocket_sendevent(element_id, element_value) {
websocket.send("start"); websocket.send(element_id + ":" + element_value);
}
function livedebug_stop() {
websocket.send("stop");
} }
function do_resize(textbox) { function do_resize(textbox) {
@ -67,18 +157,77 @@ function do_resize(textbox) {
else textbox.rows = rows; else textbox.rows = rows;
} }
function notifyMe() { function fillValuesToHTML(dataset) {
if (!("Notification" in window)) { for (var key in dataset) {
alert("This browser does not support desktop notification"); var key_prefixed = "data-" + key;
} else if (Notification.permission === "granted") { var elements = document.getElementsByClassName(key_prefixed);
const notification = new Notification("Hi there!");
// … if (elements.length > 0) {
} else if (Notification.permission !== "denied") { for (var i = 0; i < elements.length; i++) {
Notification.requestPermission().then((permission) => { var element = elements[i];
if (permission === "granted") {
const notification = new Notification("Hi there!"); if (element.type === "checkbox") {
// … // Wenn das Element ein Kontrollkästchen ist
element.checked = dataset[key] == 1 ? true : false;
} else if (element.tagName === "SELECT") {
// Wenn das Element ein Dropdown ist
setDropdownValue(element, dataset[key]);
} else if (element.classList.contains("progress-bar")) {
// Wenn das Element eine Fortschrittsleiste ist
updateProgressBar(element, dataset[key]);
} else if (element.classList.contains("hideable")) {
// Wenn das Element ein Settingsabschnitt-div ist
if (dataset[key] == 0) element.style.display = "none";
else element.style.display = "";
} else {
// Standardmäßig für Textfelder und andere Elemente
element.value = dataset[key];
}
} }
}); }
} }
} }
// Funktion zum Setzen des ausgewählten Werts für Dropdowns
function setDropdownValue(selectElement, value) {
for (var i = 0; i < selectElement.options.length; i++) {
if (selectElement.options[i].value === value) {
selectElement.selectedIndex = i;
break;
}
}
}
// Funktion zum Aktualisieren der Fortschrittsleiste
function updateProgressBar(progressBar, value) {
// Wert in das aria-valuenow-Attribut einfügen
progressBar.setAttribute("aria-valuenow", value);
// Breite des Fortschrittsbalkens und inneren Text aktualisieren
progressBar.style.width = value + "%";
progressBar.textContent = value + "%";
}
function showNotification(message, type) {
// Erstellen Sie ein Bootstrap-Alert-Element
var alertElement = $(
'<div class="alert alert-' +
type +
' alert-dismissible fade show notification" role="alert">' +
"<strong>" +
message +
"</strong>" +
'<button type="button" class="close" data-dismiss="alert" aria-label="Close">' +
'<span aria-hidden="true">&times;</span>' +
"</button>" +
"</div>"
);
// Fügen Sie das Alert-Element dem Container hinzu
$("#notification-container").append(alertElement);
// Nach 5 Sekunden das Alert-Element ausblenden
setTimeout(function () {
alertElement.alert("close");
}, 5000);
}

@ -1,4 +0,0 @@
{
"title": "kein EEPROM gefunden",
"description": "Es wurde kein EEPROM gefunden. Dies lässt einen Hardware-Defekt vermuten."
}

@ -1,4 +0,0 @@
{
"title": "Power-Monitor Fehler",
"description": "Es gibt ein Problem mit dem Power-Monitoring. Die Akku-Überwachung ist ohne Funktion! Bitte Hardware prüfen"
}

@ -1,4 +0,0 @@
{
"title": "Akku-Spannung niedrig",
"description": "Die Akkuspannung ist niedrig. Bitte Akku bald aufladen!"
}

@ -1,4 +0,0 @@
{
"title": "Akku-Spannung kritisch",
"description": "Die Akkuspannung ist sehr niedrig. Bitte Akku umgehend ersetzen um eine schädliche Tiefentladung zu vermeiden!"
}

@ -1,4 +0,0 @@
{
"title": "EEPROM-Migration failed",
"description": "Migration of EEPROM-Image from an other FW-Version failed - you need to reset everything manually!"
}

@ -1,4 +0,0 @@
{
"title": "EEPROM CFG Checksumme",
"description": "Die Checksumme der Config-Partition des EEPROM ist ungültig. Setzen sie den EEPROM-Bereich 'CFG' im Menu 'Wartung' zurück"
}

@ -1,4 +0,0 @@
{
"title": "EEPROM PDS Checksumme",
"description": "Die Checksumme der Betriebsdaten-Partition des EEPROM ist ungültig. Setzen sie den EEPROM-Bereich 'PDS' im Menu 'Wartung' zurück"
}

@ -1,4 +0,0 @@
{
"title": "EEPROM PDS Adresse",
"description": "Die Adresse der Betriebsdaten-Partition im EEPROM ist ungültig. Setzen sie den EEPROM-Bereich 'PDS' im Menu 'Wartung' zurück"
}

@ -1,4 +0,0 @@
{
"title": "EEPROM Version falsch",
"description": "Die Layout-Version des EEPROM stimmt nicht mit der Firmware-Version überein. Setzen sie den EEPROM-Bereich 'CFG' im Menu 'Wartung' zurück"
}

@ -1,4 +0,0 @@
{
"title": "Flashstorage Fehler",
"description": "Der Flashstorage konnte nicht initialisiert werden. Aktualisieren sie Flash & Firmware"
}

@ -1,4 +0,0 @@
{
"title": "Flashstorage Version falsch",
"description": "Die Version des Flashstorage stimmt nicht mit der Firmware-Version überein. Aktualisieren sie den Flash mit der passenden Update-Datei"
}

@ -1,4 +0,0 @@
{
"title": "Config-Validierung",
"description": "Ein oder mehrer Einstellungswerte sind ausserhalb plausibler Werte. Prüfen Sie Ihre Einstellungen"
}

@ -1,4 +0,0 @@
{
"title": "LoRa-Modul Fehler",
"description": "Es gibt ein Problem mit dem LoRa-Modul. Es konnte keine LoRa-Verbindung aufgebaut werden. Bitte Hardware prüfen"
}

@ -1 +1 @@
1.04 1.05