mirror of
https://github.com/Roslund/sthlm-mesh.git
synced 2026-08-06 08:53:24 +02:00
Refactor channel utilization functionality by removing LongFast channel support and associated UI elements. Update data fetching to focus solely on MediumFast channel, simplifying the gauge creation and legend display. Clean up unused code related to channel filtering.
This commit is contained in:
@@ -7,30 +7,6 @@ draft: false
|
||||
<br/>
|
||||
{{% blocks/section color="white" %}}
|
||||
|
||||
<style>
|
||||
@media (max-width: 576px) {
|
||||
#channelToolbar { top: 16px !important; }
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="channelToolbar" class="d-flex justify-content-end sticky-top" style="top: 84px;">
|
||||
<div class="btn-toolbar bg-white shadow-sm rounded-3 py-2 px-2" role="toolbar" aria-label="Channel toolbar">
|
||||
<div class="btn-group btn-group-sm me-2" role="group" aria-label="Channel filter">
|
||||
<input type="radio" class="btn-check" name="btnchannel-nodes" id="filterChannelAll" autocomplete="off" checked>
|
||||
<label class="btn btn-outline-primary" for="filterChannelAll">Alla</label>
|
||||
</div>
|
||||
<div class="btn-group btn-group-sm me-2" role="group" aria-label="Basic radio toggle button group">
|
||||
<input type="radio" class="btn-check" name="btnchannel-nodes" id="filterChannelMediumFast" data-channel="MediumFast" autocomplete="off">
|
||||
<label class="btn btn-outline-primary" for="filterChannelMediumFast">MediumFast</label>
|
||||
<input type="radio" class="btn-check" name="btnchannel-nodes" id="filterChannelLongFast" data-channel="LongFast" autocomplete="off">
|
||||
<label class="btn btn-outline-primary" for="filterChannelLongFast">LongFast</label>
|
||||
</div>
|
||||
<div data-bs-toggle="tooltip" data-bs-placement="left" title="Filtrera på kanal eller visa alla.">
|
||||
<span class="badge rounded-pill bg-dark">?</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
## Antal Enheter
|
||||
<div class="container my-3 mx-0" style="max-width: 1000px;">
|
||||
<div class="row text-center px-0">
|
||||
@@ -60,19 +36,14 @@ Antalet meddelanden per timme senaste 7 dygnen. Grafen visar antal okrypterade m
|
||||
Mätarna visar genomsnittligt kanalutnyttjande för de 10 noder som har högst kanalutnyttjande. Kanalutnyttjande (Channel Utilization) visar hur mycket av radiokanalen som är upptagen. Under 15–20% är allt normalt, över 25% får Telemetri lägre prioritet, vilket kan ge längre tid mellan uppdateringar. Runt 40% stryps positionsuppdateringar. Här i EU gäller dessutom max 10% sändtid per timme som kan pausa sändningar helt.
|
||||
|
||||
<div class="container my-3 mx-0" style="max-width: 1000px;">
|
||||
<div class="row text-center px-0">
|
||||
<div class="col-md-6 mb-4">
|
||||
<h5 class="text-muted">LongFast</h5>
|
||||
<div id="longfastGaugeContainer" class="" style="height: 300px;">
|
||||
<canvas id="longfastGauge"></canvas>
|
||||
</div>
|
||||
<div id="longfastLegend" class="" style="font-size: 0.85rem;"></div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="row px-0 align-items-start">
|
||||
<div class="col-md-6 mb-3">
|
||||
<h5 class="text-muted">MediumFast</h5>
|
||||
<div id="mediumfastGaugeContainer" class="" style="height: 300px;">
|
||||
<canvas id="mediumfastGauge"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div id="mediumfastLegend" class="" style="font-size: 0.85rem;"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -2,15 +2,13 @@ async function channelUtilizationGauges() {
|
||||
try {
|
||||
await fetchNodes();
|
||||
|
||||
// Create gauges for both channels
|
||||
await createChannelUtilizationGauge('LongFast', 'longfastGauge');
|
||||
// Create gauge for MediumFast only
|
||||
await createChannelUtilizationGauge('MediumFast', 'mediumfastGauge');
|
||||
|
||||
} catch (err) {
|
||||
console.error('Error creating channel utilization gauges:', err);
|
||||
|
||||
// Show error on both canvases
|
||||
showGaugeError('longfastGauge');
|
||||
// Show error on MediumFast canvas
|
||||
showGaugeError('mediumfastGauge');
|
||||
}
|
||||
}
|
||||
@@ -160,26 +158,28 @@ function showTop10NodesLegend(channelName, top10Nodes) {
|
||||
|
||||
if (!legendContainer || top10Nodes.length === 0) return;
|
||||
|
||||
let legendHTML = '<h6 class="text-muted mb-2">Top 10 Nodes</h6>';
|
||||
legendHTML += '<div class="row">';
|
||||
let legendHTML = '<h5 class="text-muted mb-2">Top 10 Nodes</h5>';
|
||||
legendHTML += '<ul class="list-unstyled mb-0">';
|
||||
|
||||
top10Nodes.forEach((node, index) => {
|
||||
const utilizationColor = getUtilizationColor(node.utilizationValue);
|
||||
const shortName = node.short_name || node.node_id_hex || `Node ${node.node_id}`;
|
||||
const longName = node.long_name || '';
|
||||
|
||||
legendHTML += `
|
||||
<div class="col-6 mb-1 d-flex align-items-center">
|
||||
<li class="mb-2 d-flex align-items-center">
|
||||
<span class="badge me-2" style="background-color: ${utilizationColor}; color: white; font-size: 0.7rem; min-width: 45px;">
|
||||
${node.utilizationValue.toFixed(1)}%
|
||||
</span>
|
||||
<span class="text-truncate" style="font-size: 0.75rem;" title="${node.long_name || shortName}">
|
||||
${shortName}
|
||||
<span class="me-2" style="display:inline-block;width:7ch;font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace; font-size: 0.85rem; text-align:right; white-space:nowrap; overflow:hidden; text-overflow:clip;" title="${shortName}">[${shortName}]</span>
|
||||
<span class="text-truncate" style="font-size: 0.85rem;" title="${longName || shortName}">
|
||||
${longName}
|
||||
</span>
|
||||
</div>
|
||||
</li>
|
||||
`;
|
||||
});
|
||||
|
||||
legendHTML += '</div>';
|
||||
|
||||
legendHTML += '</ul>';
|
||||
legendContainer.innerHTML = legendHTML;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,26 +9,21 @@ async function channelUtilizationHourlyAverage() {
|
||||
ctx.fillText('Loading...', canvas.width/2, canvas.height/2);
|
||||
|
||||
try {
|
||||
// Fetch data for both channels over the last 7 days
|
||||
const [mediumFastResponse, longFastResponse] = await Promise.all([
|
||||
fetch('https://map.sthlm-mesh.se/api/v1/stats/channel-utilization-stats?days=7&channel_id=MediumFast'),
|
||||
fetch('https://map.sthlm-mesh.se/api/v1/stats/channel-utilization-stats?days=7&channel_id=LongFast')
|
||||
]);
|
||||
// Fetch data for MediumFast over the last 7 days
|
||||
const response = await fetch('https://map.sthlm-mesh.se/api/v1/stats/channel-utilization-stats?days=7&channel_id=MediumFast');
|
||||
const allData = await response.json();
|
||||
|
||||
const mediumFastData = await mediumFastResponse.json();
|
||||
const longFastData = await longFastResponse.json();
|
||||
// Single dataset containing all points
|
||||
const datasets = [];
|
||||
|
||||
// Create datasets for both channels
|
||||
const datasets = [];
|
||||
|
||||
const mediumFastPoints = mediumFastData.map(entry => ({
|
||||
const points = allData.map(entry => ({
|
||||
x: entry.recorded_at,
|
||||
y: parseFloat(entry.avg_channel_utilization)
|
||||
y: entry.avg_channel_utilization
|
||||
}));
|
||||
|
||||
|
||||
datasets.push({
|
||||
label: 'MediumFast',
|
||||
data: mediumFastPoints,
|
||||
label: 'channelutilization',
|
||||
data: points,
|
||||
borderColor: 'rgb(34, 197, 94)',
|
||||
backgroundColor: 'rgba(34, 197, 94, 0.1)',
|
||||
borderWidth: 2,
|
||||
@@ -37,22 +32,6 @@ async function channelUtilizationHourlyAverage() {
|
||||
fill: false,
|
||||
});
|
||||
|
||||
const longFastPoints = longFastData.map(entry => ({
|
||||
x: entry.recorded_at,
|
||||
y: parseFloat(entry.avg_channel_utilization)
|
||||
}));
|
||||
|
||||
datasets.push({
|
||||
label: 'LongFast',
|
||||
data: longFastPoints,
|
||||
borderColor: 'rgb(15, 169, 252)',
|
||||
backgroundColor: 'rgba(15, 169, 252, 0.1)',
|
||||
borderWidth: 2,
|
||||
tension: 0.1,
|
||||
pointRadius: 0,
|
||||
fill: false,
|
||||
});
|
||||
|
||||
new Chart(ctx, {
|
||||
type: 'line',
|
||||
data: {
|
||||
@@ -78,12 +57,7 @@ async function channelUtilizationHourlyAverage() {
|
||||
},
|
||||
plugins: {
|
||||
legend: {
|
||||
display: true,
|
||||
position: 'top',
|
||||
labels: {
|
||||
usePointStyle: true,
|
||||
padding: 20
|
||||
}
|
||||
display: false
|
||||
},
|
||||
tooltip: { mode: 'index', intersect: false },
|
||||
annotation: {
|
||||
|
||||
@@ -1,20 +1,5 @@
|
||||
let nodes = [];
|
||||
let fetchPromise = null;
|
||||
const StatusFilter = {
|
||||
_channelId: null,
|
||||
_subscribers: [],
|
||||
getChannelId() {
|
||||
return this._channelId;
|
||||
},
|
||||
subscribe(callback) {
|
||||
if (typeof callback === 'function') this._subscribers.push(callback);
|
||||
},
|
||||
_notify() {
|
||||
for (const cb of this._subscribers) {
|
||||
try { cb(this._channelId); } catch (_) {}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
async function fetchNodes() {
|
||||
if (nodes.length > 0) return;
|
||||
@@ -34,16 +19,3 @@ async function fetchNodes() {
|
||||
|
||||
await fetchPromise;
|
||||
}
|
||||
|
||||
// Initialize global channel filter listeners once
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const radios = document.querySelectorAll("input[name='btnchannel-nodes']");
|
||||
if (!radios || radios.length === 0) return;
|
||||
const checked = document.querySelector("input[name='btnchannel-nodes']:checked");
|
||||
StatusFilter._channelId = checked ? (checked.getAttribute('data-channel') || null) : null;
|
||||
radios.forEach(r => r.addEventListener('change', () => {
|
||||
const channel = r.getAttribute('data-channel');
|
||||
StatusFilter._channelId = channel || null;
|
||||
StatusFilter._notify();
|
||||
}));
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user