Reformatting

This commit is contained in:
Etienne Chassaing 2025-08-06 11:13:29 +02:00
parent 400fe40bcd
commit 9ad18a17c8
2 changed files with 67 additions and 39 deletions

98
main.py
View File

@ -50,11 +50,11 @@ latest_data: Dict[str, Any] = {
"PU_1": None,
"PU_2": None,
"PU_3": None,
"DS" : None,
"DS": None,
"PatientSkid": {"QSkid": 0.0},
}
active_PUs : list[int] = []
active_PUs: list[int] = []
DEFAULT_FEED_VALVE = 0.0
@ -78,7 +78,8 @@ def format_PU_data(data):
"Qdilute": np.round(data.get("FM1", 0.0), 1),
"Qdrain": np.round(data.get("FM4", 0.0), 1),
"Qrecirc": np.round(data.get("FM3", 0.0), 1),
"QdrainEDI": np.round(data.get("FM2", 0.0), 1)- np.round(data.get("FM1", 0.0), 1),
"QdrainEDI": np.round(data.get("FM2", 0.0), 1)
- np.round(data.get("FM1", 0.0), 1),
"Pro": np.round(data.get("PS2", 0.0), 2),
"Pdilute": np.round(data.get("PS3", 0.0), 2),
"Pretentate": np.round(data.get("PS1", 0.0), 2),
@ -109,11 +110,9 @@ def format_DS_data(data):
"TankLevel": np.round(data.get("TankLevel", 0.0), 2),
"Qinlet": np.round(data.get("Inlet_flow", 0.0), 1),
"Qoutlet": np.round(data.get("Outlet_flow", 0.0), 1),
"Q_conso_filt": np.round(data.get("Qdrain_sp", 0.0), 1),
"Q_conso_filt": np.round(data.get("Qdrain_sp", 0.0), 1),
"Q_conso_filt": np.round(data.get("Qdrain_sp", 0.0), 1),
}
@ -164,6 +163,12 @@ def logout(request: Request):
# ======== PROTECTED INTERFACE ========
@app.on_event("startup")
async def startup_event():
asyncio.create_task(update_latest_data())
asyncio.create_task(update_latest_flow())
@app.get("/control", response_class=HTMLResponse)
def control_page(request: Request):
can_backend.connect()
@ -177,12 +182,14 @@ def monitor_page(request: Request):
with open("static/monitor_DS.html") as f:
return HTMLResponse(f.read())
@app.get("/monitor-page", response_class=HTMLResponse)
@app.get("/monitor-PU", response_class=HTMLResponse)
def monitor_page(request: Request):
with open("static/monitor_PU.html") as f:
return HTMLResponse(f.read())
@app.get("/multi-monitor-page", response_class=HTMLResponse)
@app.get("/multi-monitor-PU", response_class=HTMLResponse)
def monitor_page(request: Request):
with open("static/multi_pu_dashboard.html") as f:
return HTMLResponse(f.read())
@ -190,6 +197,7 @@ def monitor_page(request: Request):
# ======== CAN + BACKEND ROUTES ========
@app.post("/connect_toggle")
def connect_toggle():
logging.info(f"Toggling CAN connection, CAN is {can_backend.connected}")
@ -204,11 +212,15 @@ def connect_toggle():
raise HTTPException(status_code=500, detail="Connection failed.")
return {"connected": can_backend.connected}
@app.get("/is_connected")
def is_connected():
return {"connected": can_backend.connected}
# PU CONTROL
@app.post("/command/{state}/pu/{pu_number}")
def send_command(state: str, pu_number: int, ploop_setpoint: float = Query(...)):
global DEFAULT_FEED_VALVE
@ -243,6 +255,9 @@ def send_command(state: str, pu_number: int, ploop_setpoint: float = Query(...))
raise HTTPException(status_code=500, detail=str(e))
## MONITORING
@app.get("/api/pu_status")
def get_pu_status():
global active_PUs
@ -253,7 +268,11 @@ def get_pu_status():
}
logging.info(f"[PU STATUS] {states}")
active_PUs = [index + 1 for index, (pu, status) in enumerate(states.items()) if status != 'Offline']
active_PUs = [
index + 1
for index, (pu, status) in enumerate(states.items())
if status != "Offline"
]
logging.info(f"[ACTIVE PU] {active_PUs}")
return JSONResponse(content=states)
@ -262,7 +281,7 @@ def get_pu_status():
async def update_latest_data():
global active_PUs
while True:
#DS
# DS
data = can_backend.get_latest_data(pu_number=0)
latest_data[f"DS"] = format_DS_data(data)
@ -271,7 +290,6 @@ async def update_latest_data():
data = can_backend.get_latest_data(pu_number=pu)
latest_data[f"PU_{pu}"] = format_PU_data(data)
logging.debug(f"[MONITOR DS BUFFER] latest_data: {latest_data}")
await asyncio.sleep(0.05)
@ -281,18 +299,6 @@ async def get_monitor_data():
return JSONResponse(content=latest_data)
@app.on_event("startup")
async def startup_event():
asyncio.create_task(update_latest_data())
asyncio.create_task(update_latest_flow())
@app.get("/can_status")
def can_status():
"""Return current CAN connection status."""
return {"connected": can_backend.connected}
# LOCAL RECORDER
@app.post("/start_recording")
async def start_recording():
@ -344,11 +350,7 @@ async def record_data_loop():
timestamp = datetime.datetime.now().isoformat()
for pu, data in latest_data.items():
if data:
row = {
"timestamp": timestamp,
"pu": pu,
**data
}
row = {"timestamp": timestamp, "pu": pu, **data}
recording_writer.writerow(row)
# Flush every flush_interval seconds
@ -360,18 +362,26 @@ async def record_data_loop():
await asyncio.sleep(0.05) # 10 Hz
## AUTOMATIC TESTING
async def send_command_with_delay(state: str, pu: int, delay_s: int = 0, ploop_setpoint: float = 0.0):
async def send_command_with_delay(
state: str, pu: int, delay_s: int = 0, ploop_setpoint: float = 0.0
):
await asyncio.sleep(delay_s)
logging.info(f"[AUTO TEST] Sending {state} to PU{pu} after {delay_s}s")
can_backend.send_state_command(state, pu, ploop_setpoint)
async def set_patients_with_delay(count: int, delay_s: int):
await asyncio.sleep(delay_s)
logging.info(f"[AUTO TEST] Sending {count} patients to patient skid after {delay_s}s")
logging.info(
f"[AUTO TEST] Sending {count} patients to patient skid after {delay_s}s"
)
set_patient_skid_users(count)
@router.post("/test/auto/1")
async def auto_test_pu1(ploop_setpoint: float = Query(0.0)):
pu = 1
@ -379,39 +389,52 @@ async def auto_test_pu1(ploop_setpoint: float = Query(0.0)):
asyncio.create_task(run_auto_test_pu1(pu, ploop_setpoint))
return {"status": "started", "pu": pu}
@router.post("/test/auto/2")
async def auto_test_pu2(ploop_setpoint: float = Query(0.0)):
logging.info("[AUTO TEST] Starting automatic test for 2 PUs")
asyncio.create_task(run_auto_test_pu2(ploop_setpoint))
return {"status": "started", "pu": [1, 2]}
async def run_auto_test_pu1(pu: int, ploop_setpoint: float):
await send_command_with_delay("PRE-PRODUCTION", pu, delay_s=0, ploop_setpoint=ploop_setpoint)
await send_command_with_delay("PRODUCTION", pu, delay_s=180, ploop_setpoint=ploop_setpoint)
await send_command_with_delay(
"PRE-PRODUCTION", pu, delay_s=0, ploop_setpoint=ploop_setpoint
)
await send_command_with_delay(
"PRODUCTION", pu, delay_s=180, ploop_setpoint=ploop_setpoint
)
await set_patients_with_delay(5, delay_s=60)
await set_patients_with_delay(10, delay_s=60)
await send_command_with_delay("IDLE", pu, delay_s=60, ploop_setpoint=ploop_setpoint)
logging.info("[AUTO TEST] Finished PU1 test")
async def run_auto_test_pu2(ploop_setpoint: float):
# Step 1: Run PU1 test
await run_auto_test_pu1(1, ploop_setpoint)
# Step 2: PU2 sequence
await send_command_with_delay("PRE-PRODUCTION", 2, delay_s=0, ploop_setpoint=ploop_setpoint)
await send_command_with_delay("PRODUCTION", 2, delay_s=180, ploop_setpoint=ploop_setpoint)
await send_command_with_delay(
"PRE-PRODUCTION", 2, delay_s=0, ploop_setpoint=ploop_setpoint
)
await send_command_with_delay(
"PRODUCTION", 2, delay_s=180, ploop_setpoint=ploop_setpoint
)
await set_patients_with_delay(15, delay_s=60)
await set_patients_with_delay(0, delay_s=60)
await send_command_with_delay("IDLE", 2, delay_s=60, ploop_setpoint=ploop_setpoint)
await send_command_with_delay("IDLE", 1, delay_s=60, ploop_setpoint=ploop_setpoint)
logging.info("[AUTO TEST] Finished PU1 + PU2 test")
@router.post("/test/auto/3")
async def auto_test_pu3():
# Call the function for PU3 auto test
logging.info("Start auto test of 3 PU")
return {"status": "started", "pu": 3}
# PATIENT SKID HELPERS
async def update_latest_flow():
global active_PUs
@ -431,6 +454,7 @@ async def update_latest_flow():
logging.error(f"Error fetching flow: {e}")
await asyncio.sleep(1.0)
def set_patient_skid_users(count: int = 1):
try:
url = f"http://192.168.1.28:8000/set_users/{count}"
@ -439,9 +463,13 @@ def set_patient_skid_users(count: int = 1):
if response.status_code == 200:
return {"status": "success", "detail": response.json()}
else:
raise HTTPException(status_code=502, detail=f"Remote server error: {response.text}")
raise HTTPException(
status_code=502, detail=f"Remote server error: {response.text}"
)
except httpx.RequestError as e:
raise HTTPException(status_code=500, detail=f"Request to external server failed: {str(e)}")
raise HTTPException(
status_code=500, detail=f"Request to external server failed: {str(e)}"
)
app.include_router(router)

View File

@ -241,16 +241,16 @@
<h1>Hydraulic Machine Control</h1>
<div class="monitor-pu-buttons">
<!-- New multi-monitor button -->
<a href="/multi-monitor-page" target="_blank" class="monitor-link">
<a href="/multi-monitor-PU" target="_blank" class="monitor-link">
<i class="fas fa-chart-bar"></i> Monitor All PUs
</a>
<a href="/monitor-page?pu_number=1" target="_blank" class="monitor-link">
<a href="/monitor-PU?pu_number=1" target="_blank" class="monitor-link">
<i class="fas fa-chart-line"></i> Monitor PU 1
</a>
<a href="/monitor-page?pu_number=2" target="_blank" class="monitor-link">
<a href="/monitor-PU?pu_number=2" target="_blank" class="monitor-link">
<i class="fas fa-chart-line"></i> Monitor PU 2
</a>
<a href="/monitor-page?pu_number=3" target="_blank" class="monitor-link">
<a href="/monitor-PU?pu_number=3" target="_blank" class="monitor-link">
<i class="fas fa-chart-line"></i> Monitor PU 3
</a>
<a href="/monitor-DS" target="_blank" class="monitor-link">