Update public/scripts/trends.js
This commit is contained in:
parent
ab5f5d2dfc
commit
8348d1a3fa
@ -3,9 +3,11 @@ function average(arr) {
|
|||||||
return arr.reduce((a, b) => a + b, 0) / arr.length;
|
return arr.reduce((a, b) => a + b, 0) / arr.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
// period definitions
|
// interval mappings
|
||||||
const periodKeys = ['hourly','daily','weekly','monthly','yearly'];
|
const periodKeys = ['hourly','daily','weekly','monthly','yearly'];
|
||||||
const periodLabels = ['Hourly','Daily','Weekly','Monthly','Yearly'];
|
const periodLabels = ['Hourly','Daily','Weekly','Monthly','Yearly'];
|
||||||
|
|
||||||
|
// bucket definitions
|
||||||
const periodConfig = {
|
const periodConfig = {
|
||||||
hourly: { keyFn: r => r.timestamp.slice(0,13), labelFn: k => k.replace('T',' ') },
|
hourly: { keyFn: r => r.timestamp.slice(0,13), labelFn: k => k.replace('T',' ') },
|
||||||
daily: { keyFn: r => r.timestamp.slice(0,10), labelFn: k => k },
|
daily: { keyFn: r => r.timestamp.slice(0,10), labelFn: k => k },
|
||||||
@ -19,22 +21,62 @@ const periodConfig = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// global Chart.js instance
|
// global Chart.js instance
|
||||||
let trendChart;
|
let trendChart, dataTable;
|
||||||
|
|
||||||
// fetch all readings
|
// fetch readings from server
|
||||||
async function fetchReadings() {
|
async function fetchReadings() {
|
||||||
const res = await fetch('/api/readings');
|
const res = await fetch('/api/readings');
|
||||||
return res.ok ? res.json() : [];
|
return res.ok ? res.json() : [];
|
||||||
}
|
}
|
||||||
|
|
||||||
// build or update chart
|
// determine direction based on dock door #
|
||||||
|
function getDirection(dock) {
|
||||||
|
return ( (dock>=124 && dock<=138) || (dock>=202 && dock<=209) )
|
||||||
|
? 'Inbound'
|
||||||
|
: 'Outbound';
|
||||||
|
}
|
||||||
|
|
||||||
|
// render DataTable
|
||||||
|
function renderTable(allReadings) {
|
||||||
|
const tbody = $('#trendTable tbody').empty();
|
||||||
|
allReadings.forEach(r => {
|
||||||
|
const ts = new Date(r.timestamp).toLocaleString('en-US', { timeZone:'America/New_York' });
|
||||||
|
const dir = getDirection(r.dockDoor);
|
||||||
|
tbody.append(`
|
||||||
|
<tr>
|
||||||
|
<td>${ts}</td>
|
||||||
|
<td>${r.temperature.toFixed(1)}</td>
|
||||||
|
<td>${r.humidity.toFixed(1)}</td>
|
||||||
|
<td>${r.heatIndex.toFixed(1)}</td>
|
||||||
|
<td>${r.dockDoor}</td>
|
||||||
|
<td>${dir}</td>
|
||||||
|
</tr>
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
|
// initialize or redraw DataTable
|
||||||
|
if ($.fn.DataTable.isDataTable('#trendTable')) {
|
||||||
|
dataTable.clear().rows.add($('#trendTable tbody tr')).draw();
|
||||||
|
} else {
|
||||||
|
dataTable = $('#trendTable').DataTable({
|
||||||
|
paging: true,
|
||||||
|
pageLength: 25,
|
||||||
|
ordering: true,
|
||||||
|
order: [[0,'desc']],
|
||||||
|
autoWidth: false,
|
||||||
|
scrollX: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// draw or update chart
|
||||||
async function drawTrend() {
|
async function drawTrend() {
|
||||||
const all = await fetchReadings();
|
const all = await fetchReadings();
|
||||||
const slider = document.getElementById('periodSlider');
|
const slider = document.getElementById('periodSlider');
|
||||||
const periodKey = periodKeys[slider.value];
|
const periodKey = periodKeys[slider.value];
|
||||||
const cfg = periodConfig[periodKey];
|
const cfg = periodConfig[periodKey];
|
||||||
|
|
||||||
// group and compute stats
|
// group & stats
|
||||||
const groups = {};
|
const groups = {};
|
||||||
all.forEach(r => {
|
all.forEach(r => {
|
||||||
const key = cfg.keyFn(r);
|
const key = cfg.keyFn(r);
|
||||||
@ -74,10 +116,13 @@ async function drawTrend() {
|
|||||||
} else {
|
} else {
|
||||||
trendChart = new Chart(ctx, {
|
trendChart = new Chart(ctx, {
|
||||||
type: 'line',
|
type: 'line',
|
||||||
data: { labels: labels.map(cfg.labelFn), datasets },
|
data: {
|
||||||
|
labels: labels.map(cfg.labelFn),
|
||||||
|
datasets
|
||||||
|
},
|
||||||
options: {
|
options: {
|
||||||
responsive: true,
|
responsive: true,
|
||||||
maintainAspectRatio: false, // <<— ensure the canvas fills its container
|
maintainAspectRatio: false,
|
||||||
plugins: {
|
plugins: {
|
||||||
legend: { display: true },
|
legend: { display: true },
|
||||||
tooltip: { mode:'index', intersect:false },
|
tooltip: { mode:'index', intersect:false },
|
||||||
@ -93,16 +138,23 @@ async function drawTrend() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// always update table below
|
||||||
|
renderTable(all);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// wire up controls
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
// initial draw
|
||||||
drawTrend();
|
drawTrend();
|
||||||
|
|
||||||
// slider
|
// slider
|
||||||
document.getElementById('periodSlider')
|
document.getElementById('periodSlider')
|
||||||
.addEventListener('input', e => {
|
.addEventListener('input', e => {
|
||||||
document.getElementById('periodLabel').textContent = periodLabels[e.target.value];
|
document.getElementById('periodLabel').textContent = periodLabels[e.target.value];
|
||||||
drawTrend();
|
drawTrend();
|
||||||
});
|
});
|
||||||
|
|
||||||
// toggles
|
// toggles
|
||||||
document.querySelectorAll('#metricToggles input')
|
document.querySelectorAll('#metricToggles input')
|
||||||
.forEach(chk => chk.addEventListener('change', drawTrend));
|
.forEach(chk => chk.addEventListener('change', drawTrend));
|
||||||
|
Loading…
x
Reference in New Issue
Block a user