From 1e3f47876c7708358ab7dc275f599215f44c6197 Mon Sep 17 00:00:00 2001 From: Etienne Chassaing <60154720+cetiennec@users.noreply.github.com> Date: Tue, 8 Jul 2025 17:02:10 +0200 Subject: [PATCH] Adds a monitor page --- MockCAN.py | 28 ++++++-- main.py | 80 +++++++++++++++++++-- static/index.html | 75 ++++++++++++++++--- static/monitor.html | 170 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 337 insertions(+), 16 deletions(-) create mode 100644 static/monitor.html diff --git a/MockCAN.py b/MockCAN.py index fdc6a71..98f403c 100644 --- a/MockCAN.py +++ b/MockCAN.py @@ -28,8 +28,28 @@ class CANBackend: # Placeholder for sending mode command PUs_states[pu_number-1] = {"PU_MODE": state, "ploop_setpoint":ploop_setpoint} - def get_latest_data(self): - # Placeholder for getting latest TPDO data - return {"FM2" : round(1000*np.random.random(),1),"PS1" : round(10*np.random.random(),2)} - + # Simulate getting the latest data with random values + return { + "FM2": round(1000 * np.random.random(), 1), + "FM3": round(1000 * np.random.random(), 1), + "FM4": round(1000 * np.random.random(), 1), + "FM5": round(1000 * np.random.random(), 1), + "PS1": round(10 * np.random.random(), 2), + "PS2": round(10 * np.random.random(), 2), + "PS3": round(10 * np.random.random(), 2), + "PS4": round(10 * np.random.random(), 2), + "Cond": 1* np.random.random(), + "MV02": round(100 * np.random.random(), 2), + "MV02_sp": round(100 * np.random.random(), 2), + "MV03": round(100 * np.random.random(), 2), + "MV03_sp": round(100 * np.random.random(), 2), + "MV05": round(100 * np.random.random(), 2), + "MV05_sp": round(100 * np.random.random(), 2), + "MV06": round(100 * np.random.random(), 2), + "MV06_sp": round(100 * np.random.random(), 2), + "MV07": round(100 * np.random.random(), 2), + "MV07_sp": round(100 * np.random.random(), 2), + "MV08": round(100 * np.random.random(), 2), + "MV08_sp": round(100 * np.random.random(), 2), + } \ No newline at end of file diff --git a/main.py b/main.py index 9d2d5dc..54e0bb1 100644 --- a/main.py +++ b/main.py @@ -41,15 +41,79 @@ def send_command(state: str, pu_number: int, ploop_setpoint: float = Query(...)) return {"status": "success", "command": state.upper(), "pu": pu_number, "ploop_setpoint": ploop_setpoint} except Exception as e: raise HTTPException(status_code=500, detail=str(e)) + @app.get("/monitor") def get_monitor_data(): + # TODO : agree on the structure data = can_backend.get_latest_data() print(f"[MONITOR] Latest SDO: {data}") return { - "Qperm": [data.get("FM2", 0.0), data.get("FM2", 0.0), 0.0], - "Pdilute": [data.get("PS1", 0.0), 0.0, 0.0], - "Conductivity": [0.0, 0.0, 0.0], - "Pro": [0.0, 0.0, 0.0], + "PU_1": { + "Qperm": data.get("FM2", 0.0), + "Qdilute": data.get("FM3", 0.0), + "Qdrain": data.get("FM4", 0.0), + "Qrecirc": data.get("FM5", 0.0), + "Pro": data.get("PS1", 0.0), + "Pdilute": data.get("PS2", 0.0), + "Prentate": data.get("PS3", 0.0), + "Conductivity": data.get("Cond", 0.0), + "MV02": data.get("MV02", 0.0), + "MV02_sp": data.get("MV02_sp", 0.0), + "MV03": data.get("MV03", 0.0), + "MV03_sp": data.get("MV03_sp", 0.0), + "MV05": data.get("MV05", 0.0), + "MV05_sp": data.get("MV05_sp", 0.0), + "MV06": data.get("MV06", 0.0), + "MV06_sp": data.get("MV06_sp", 0.0), + "MV07": data.get("MV07", 0.0), + "MV07_sp": data.get("MV07_sp", 0.0), + "MV08": data.get("MV08", 0.0), + "MV08_sp": data.get("MV08_sp", 0.0) + }, + "PU_2": { + "Qperm": data.get("FM2", 0.0), + "Qdilute": data.get("FM3", 0.0), + "Qdrain": data.get("FM4", 0.0), + "Qrecirc": data.get("FM5", 0.0), + "Pro": data.get("PS1", 0.0), + "Pdilute": data.get("PS2", 0.0), + "Prentate": data.get("PS3", 0.0), + "Conductivity": data.get("Cond", 0.0), + "MV02": data.get("MV02", 0.0), + "MV02_sp": data.get("MV02_sp", 0.0), + "MV03": data.get("MV03", 0.0), + "MV03_sp": data.get("MV03_sp", 0.0), + "MV05": data.get("MV05", 0.0), + "MV05_sp": data.get("MV05_sp", 0.0), + "MV06": data.get("MV06", 0.0), + "MV06_sp": data.get("MV06_sp", 0.0), + "MV07": data.get("MV07", 0.0), + "MV07_sp": data.get("MV07_sp", 0.0), + "MV08": data.get("MV08", 0.0), + "MV08_sp": data.get("MV08_sp", 0.0) + }, + "PU_3": { + "Qperm": data.get("FM2", 0.0), + "Qdilute": data.get("FM3", 0.0), + "Qdrain": data.get("FM4", 0.0), + "Qrecirc": data.get("FM5", 0.0), + "Pro": data.get("PS1", 0.0), + "Pdilute": data.get("PS2", 0.0), + "Prentate": data.get("PS3", 0.0), + "Conductivity": data.get("Cond", 0.0), + "MV02": data.get("MV02", 0.0), + "MV02_sp": data.get("MV02_sp", 0.0), + "MV03": data.get("MV03", 0.0), + "MV03_sp": data.get("MV03_sp", 0.0), + "MV05": data.get("MV05", 0.0), + "MV05_sp": data.get("MV05_sp", 0.0), + "MV06": data.get("MV06", 0.0), + "MV06_sp": data.get("MV06_sp", 0.0), + "MV07": data.get("MV07", 0.0), + "MV07_sp": data.get("MV07_sp", 0.0), + "MV08": data.get("MV08", 0.0), + "MV08_sp": data.get("MV08_sp", 0.0) + } } @@ -64,6 +128,14 @@ async def read_root(): with open("static/index.html", "r") as file: return HTMLResponse(content=file.read(), status_code=200) + +@app.get("/monitor-page", response_class=HTMLResponse) +async def read_monitor_page(): + """Serve monitor HTML page.""" + with open("static/monitor.html", "r") as file: + return HTMLResponse(content=file.read(), status_code=200) + + if __name__ == "__main__": import uvicorn uvicorn.run( diff --git a/static/index.html b/static/index.html index d54c0df..75387ad 100644 --- a/static/index.html +++ b/static/index.html @@ -269,14 +269,38 @@ } - async function updateMonitorData() { - const response = await fetch('/monitor'); - const data = await response.json(); - updateMonitorValues('Qperm', data.Qperm, 'L/h'); - updateMonitorValues('Pdilute', data.Pdilute, 'bar'); - updateMonitorValues('Conductivity', data.Conductivity, 'µS/cm'); - updateMonitorValues('Pro', data.Pro, 'units'); - } + async function updateMonitorData() { + const response = await fetch('/monitor'); + const data = await response.json(); // data = { PU_1: {...}, PU_2: {...}, PU_3: {...} } + + for (const [puId, puData] of Object.entries(data)) { + const container = document.getElementById(puId); + if (!container) continue; + + container.innerHTML = ` +

${puId}

+
Q_perm
${puData.Qperm.toFixed(1)} L/h
+
Q_dilute
${puData.Qdilute.toFixed(1)} L/h
+
Q_drain
${puData.Qdrain.toFixed(1)} L/h
+
Q_recirc
${puData.Qrecirc.toFixed(1)} L/h
+ +
P_ro
${puData.Pro.toFixed(1)} bar
+
P_dilute
${puData.Pdilute.toFixed(1)} bar
+
P_rentate
${puData.Prentate.toFixed(1)} bar
+ +
Conductivity
${puData.Conductivity.toFixed(1)} µS/cm
+ +
MV02
${puData.MV02.toFixed(1)} % (sp: ${puData.MV02_sp.toFixed(1)})
+
MV03
${puData.MV03.toFixed(1)} % (sp: ${puData.MV03_sp.toFixed(1)})
+
MV05
${puData.MV05.toFixed(1)} % (sp: ${puData.MV05_sp.toFixed(1)})
+
MV06
${puData.MV06.toFixed(1)} % (sp: ${puData.MV06_sp.toFixed(1)})
+
MV07
${puData.MV07.toFixed(1)} % (sp: ${puData.MV07_sp.toFixed(1)})
+
MV08
${puData.MV08.toFixed(1)} % (sp: ${puData.MV08_sp.toFixed(1)})
+ `; + } + } + + function updateMonitorValues(id, values, unit) { const container = document.getElementById(id); @@ -291,5 +315,40 @@ // Update monitor data every second setInterval(updateMonitorData, 1000); + + + diff --git a/static/monitor.html b/static/monitor.html new file mode 100644 index 0000000..60b03ae --- /dev/null +++ b/static/monitor.html @@ -0,0 +1,170 @@ + + + + + + Live Monitoring Dashboard + + + + +

Live Monitoring Dashboard

+
+
+
+
+
+
+
+
+
+
+ + + + + // Initialize plots when the page loads + window.onload = initPlots; + + +