173 lines
4.8 KiB
HTML
173 lines
4.8 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8" />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
|
<title>Multi-PU Dashboard</title>
|
|
<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
|
|
<style>
|
|
body {
|
|
font-family: Arial, sans-serif;
|
|
margin: 0;
|
|
padding: 20px;
|
|
}
|
|
h1 {
|
|
text-align: center;
|
|
}
|
|
.plot-container {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 10px;
|
|
align-items: center;
|
|
}
|
|
.plot {
|
|
width: 90%;
|
|
height: 300px;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<h1>Multi-PU Monitoring Dashboard</h1>
|
|
<div class="plot-container">
|
|
<div id="Qperm-plot" class="plot"></div>
|
|
<div id="Pdilute-plot" class="plot"></div>
|
|
<div id="Pro-plot" class="plot"></div>
|
|
<div id="Qdilute-plot" class="plot"></div>
|
|
<div id="Qdrain-plot" class="plot"></div>
|
|
<div id="Cdilute-plot" class="plot"></div>
|
|
</div>
|
|
<script>
|
|
const time0 = [new Date()];
|
|
const zero = [0];
|
|
const maxPoints = 200;
|
|
const puList = ['PU_1', 'PU_2', 'PU_3'];
|
|
|
|
const plots = [
|
|
{ id: 'Qperm-plot', quantity: 'Qperm', title: 'Qperm per PU', ref: 1200 },
|
|
{ id: 'Qdilute-plot', quantity: 'Qdilute', title: 'Qdilute per PU' },
|
|
{ id: 'Qdrain-plot', quantity: 'Qdrain', title: 'Qdrain per PU' },
|
|
{ id: 'Pro-plot', quantity: 'Pro', title: 'Pro per PU' },
|
|
{ id: 'Pdilute-plot', quantity: 'Pdilute', title: 'Pdilute per PU', ref: 2.5 },
|
|
{ id: 'Cdilute-plot', quantity: 'Cdilute', title: 'Cdilute per PU'},
|
|
];
|
|
|
|
function makeTraces(quantity) {
|
|
return puList.map((pu, i) => ({
|
|
x: time0.slice(),
|
|
y: zero.slice(),
|
|
name: pu,
|
|
mode: 'lines',
|
|
line: { width: 2 },
|
|
legendgroup: pu
|
|
}));
|
|
}
|
|
function initAllPlots() {
|
|
plots.forEach(plot => {
|
|
const data = makeTraces(plot.quantity);
|
|
const layout = {
|
|
title: plot.title,
|
|
xaxis: { title: 'Time', type: 'date' },
|
|
yaxis: { title: plot.id.includes('P') ? 'Pressure (bar)' : 'Flow (L/h)' },
|
|
};
|
|
|
|
// Add ref line if present
|
|
if (plot.ref !== undefined) {
|
|
data.push({
|
|
x: [time0[0], time0[0]],
|
|
y: [plot.ref, plot.ref],
|
|
mode: 'lines',
|
|
line: { dash: 'dash', color: 'red' },
|
|
name: `Ref ${plot.ref}`,
|
|
showlegend: true
|
|
});
|
|
}
|
|
|
|
// Add QSkid trace only for Qperm plot
|
|
if (plot.id === 'Qperm-plot') {
|
|
data.push({
|
|
x: time0.slice(),
|
|
y: zero.slice(),
|
|
name: 'QSkid',
|
|
mode: 'lines',
|
|
line: { color: 'black', width: 2, dash: 'dot' },
|
|
legendgroup: 'PatientSkid'
|
|
});
|
|
}
|
|
|
|
if (plot.id === 'Qdrain-plot') {
|
|
data.push({
|
|
x: time0.slice(),
|
|
y: zero.slice(),
|
|
name: 'Qconso',
|
|
mode: 'lines',
|
|
line: { color: 'black', width: 2, dash: 'dot' },
|
|
legendgroup: 'Consumption'
|
|
});
|
|
}
|
|
|
|
Plotly.newPlot(plot.id, data, layout);
|
|
});
|
|
}
|
|
|
|
async function updateAllPlots() {
|
|
try {
|
|
const res = await fetch('/monitor');
|
|
if (!res.ok) throw new Error(`HTTP error! status: ${res.status}`);
|
|
const allData = await res.json();
|
|
const timestamp = new Date();
|
|
|
|
// SkidData is only fetched once
|
|
const SkidData = allData["PatientSkid"] || {};
|
|
const DSData = allData["DS"];
|
|
|
|
|
|
plots.forEach(plot => {
|
|
const xUpdates = [];
|
|
const yUpdates = [];
|
|
|
|
puList.forEach(pu => {
|
|
const puData = allData[pu] || {};
|
|
const value = puData[plot.quantity];
|
|
xUpdates.push([timestamp]);
|
|
yUpdates.push([value !== undefined ? value : null]);
|
|
});
|
|
|
|
Plotly.extendTraces(plot.id, { x: xUpdates, y: yUpdates }, puList.map((_, i) => i), maxPoints);
|
|
|
|
if (plot.ref !== undefined) {
|
|
Plotly.extendTraces(plot.id, {
|
|
x: [[timestamp]],
|
|
y: [[plot.ref]]
|
|
}, [puList.length], maxPoints); // the ref line is always the last trace
|
|
}
|
|
|
|
// Extend PatientSkid.QSkid only for Qperm plot
|
|
if (plot.id === 'Qperm-plot') {
|
|
const qSkid = SkidData["QSkid"];
|
|
const skidX = [[timestamp]];
|
|
const skidY = [[qSkid !== undefined ? qSkid : null]];
|
|
const qSkidTraceIndex = puList.length + (plot.ref !== undefined ? 1 : 0); // last trace index
|
|
Plotly.extendTraces(plot.id, { x: skidX, y: skidY }, [qSkidTraceIndex], maxPoints);
|
|
}
|
|
|
|
if (plot.id === 'Qdrain-plot') {
|
|
const Qconso = DSData["Qconso"];
|
|
const consoX = [[timestamp]];
|
|
const consoY = [[Qconso !== undefined ? Qconso : null]];
|
|
const QconsoTraceIndex = puList.length + (plot.ref !== undefined ? 1 : 0); // last trace index
|
|
Plotly.extendTraces(plot.id, { x: consoX, y: consoY }, [QconsoTraceIndex], maxPoints);
|
|
}
|
|
|
|
});
|
|
} catch (err) {
|
|
console.error("Failed to update plots:", err);
|
|
}
|
|
}
|
|
|
|
initAllPlots();
|
|
setInterval(updateAllPlots, 1000);
|
|
</script>
|
|
|
|
</body>
|
|
</html>
|