centralex/web/main.js
2024-05-31 20:40:57 +02:00

257 lines
6.4 KiB
JavaScript

window.onload = () => {
const table_elem = document.getElementById("table");
const last_change = document.getElementById("last_change");
const last_ping = document.getElementById("last_ping");
const connected = document.getElementById("connected");
const free_ports = document.getElementById("free_ports");
const timeout_duration = 10 * 1000;
const retry_timeout = 5 * 1000;
let reconnect_timeout;
let ping_timeout;
let evtSource;
let table = [];
let last_update = new Date();
let direction = -1;
let oldkey = "last_change";
table_elem.firstChild.firstChild.lastChild.className = "sort sort-down";
let time_ago = (ms) => {
let value = ms / 1000;
// let prev = 0;
let unit = 0;
let factors = [
[1, "Sekunde", "n"],
[60, "Minute", "n"],
[60, "Stunde", "n"],
[24, "Tag", "en"],
[7, "Woche", "n"],
[4.348214, "Monat", "en"],
[12, "Jahr", "en"],
];
for (let i in factors) {
let factor = factors[i][0];
let new_value = Math.floor(value / factor);
if (new_value == 0) break;
// prev = Math.floor(value % factor);
value = new_value;
unit = i;
}
let factor = factors[unit];
return [value, factors[unit][1] + (value == 1 ? "" : factor[2])];
};
sort = (element, key) => {
if (key == oldkey) {
direction *= -1;
} else {
oldkey = key;
direction = -1;
}
for (let child of table_elem.firstChild.firstChild.children) {
child.className = "";
}
element.className = `sort ${direction > 0 ? "sort-up" : "sort-down"}`;
do_sort(oldkey, direction);
};
let do_sort = (key, direction) => {
let is_number = !!~(["port", "number", "last_change"].indexOf(key));
table = table.sort((a, b) => (direction * (
is_number ? a[key] - b[key] : ("" + a[key]).localeCompare(b[key], "de-DE")
)));
populate_table();
};
let fmt = Intl.DateTimeFormat("de-DE", {
dateStyle: "medium",
timeStyle: "medium",
});
let format_date = (date) => fmt.format(date).replace(", ", " ");
let format_time = (date) => fmt.format(date).split(", ", 2)[1];
let populate_table = () => {
// clear everything except for the header row
while (table_elem.rows.length > 1) {
table_elem.deleteRow(-1);
}
for (let row of table) {
let tr = table_elem.insertRow(-1);
let values = [
row.name === null ? "?" : row.name,
row.number,
row.port,
row.status,
time_ago(last_update - row.last_change),
];
let names = [
"name",
"number",
"port",
"status",
"last_change",
];
for (let i in values) {
let value = values[i];
let name = names[i];
let td = tr.insertCell(-1);
td.className = name;
if (name == "last_change") {
let [number, unit] = value;
let span = document.createElement("span");
// span.className = "value";
span.innerText = number;
td.appendChild(span);
span = document.createElement("span");
span.className = "unit";
span.innerText = unit;
td.appendChild(span);
} else {
td.innerText = value;
}
}
}
};
let update_table = (data) => {
const allowed_ports = data.allowed_ports.map((x) => x.end - x.start + 1)
.reduce((a, b) => a + b, 0);
free_ports.innerText = `Freie Ports: ${
allowed_ports - Object.keys(data.allocated_ports).length -
data.errored_ports.length
}`;
// last_change.innerHTML = `Letzte Änderung: ${format_date(new Date(+data.last_update * 1000))}`;
table = [];
for (let number in data.allocated_ports) {
try {
let port = data.allocated_ports[number];
number = +number;
// allocated port has no state. This means that it is unknown and should not be displayed
if (data.port_state[port] == undefined) {
continue;
}
let { status, last_change } = data.port_state[port];
let rejector = data.rejectors[port] || null;
if (rejector && rejector instanceof Array) {
rejector = rejector.map((x) => "0x" + x.toString(16).padStart(2, 0))
.join(" ");
}
last_change = new Date(last_change * 1000);
let name = data.names[number] || null;
switch (status) {
case "disconnected":
status = rejector ? `getrennt: ${rejector}` : "getrennt";
break;
case "idle":
status = "bereit";
break;
case "in_call":
status = "anruf";
break;
}
table.push({ port, number, status, last_change, rejector, name });
} catch (error) {
console.error(error);
}
}
for (let [timestamp, port] of data.errored_ports) {
table.push({
port,
number: null,
status: "Fehler",
last_change: new Date(timestamp * 1000),
rejector: null,
name: "",
});
}
do_sort(oldkey, direction);
populate_table();
};
let format_event = (event, method) =>
(method || format_date)(new Date(+event.data * 1000));
let display_disconnected;
let connect_event_source = () => {
clearTimeout(reconnect_timeout);
clearTimeout(ping_timeout);
ping_timeout = setTimeout(connect_event_source, timeout_duration);
evtSource && evtSource.close && evtSource.close();
evtSource = new EventSource("/events");
evtSource.addEventListener("change", (event) => {
last_ping.innerText = `Stand: ${format_event(event, format_time)}`;
last_update = new Date(+event.data * 1000);
fetch("/data")
.then((res) => res.json())
.then(update_table);
});
evtSource.addEventListener("ping", (event) => {
clearTimeout(ping_timeout);
ping_timeout = setTimeout(connect_event_source, timeout_duration);
last_update = new Date(+event.data * 1000);
last_ping.innerText = `Stand: ${format_event(event, format_time)}`;
connected.className = "visible";
populate_table();
clearTimeout(display_disconnected);
display_disconnected = setTimeout(
() => connected.className = "hidden",
5000,
);
});
evtSource.onerror = () => {
clearTimeout(reconnect_timeout);
reconnect_timeout = setTimeout(connect_event_source, retry_timeout);
};
};
connect_event_source();
};