Added changes for mode reply from PU

This commit is contained in:
AzureAD\AniketSaha 2025-07-10 14:19:05 +02:00
parent 5d0a52e04b
commit 765f737f28
2 changed files with 102 additions and 57 deletions

View File

@ -1,41 +1,55 @@
import threading import threading
import canopen import canopen
import time import time
import os
class CANBackend: class CANBackend:
def __init__(self): def __init__(self):
self.network = None self.network = None
self.node = None self.nodes = {} # {1: RemoteNode(0x02), 2: RemoteNode(0x03), ...}
self.connected = False self.connected = False
self.latest_data = {}
self.lock = threading.Lock() self.lock = threading.Lock()
# Initialize thread-related attributes
self.polling_thread = None self.polling_thread = None
self.polling_active = False self.polling_active = False
self.latest_data = {}
self.eds_path = os.path.join(os.path.dirname(__file__), "eds_file", "processBoard_0.eds")
def connect(self, node_id, eds_path): def connect(self):
try: try:
self.network = canopen.Network() self.network = canopen.Network()
self.network.connect(bustype='socketcan', channel='can0', bitrate=250000) self.network.connect(channel='can0', bustype='socketcan')
self.node = canopen.RemoteNode(node_id, eds_path)
self.network.add_node(self.node) # PU mapping: PU1->0x02, PU2->0x03, PU3->0x04
self.node.nmt.state = 'PRE-OPERATIONAL' node_map = {
1: 0x02,
2: 0x03,
3: 0x04
}
for pu_number, node_id in node_map.items():
node = canopen.RemoteNode(node_id, self.eds_path)
self.network.add_node(node)
self.nodes[pu_number] = node
self.connected = True self.connected = True
self.latest_data = {}
# Start polling thread
self._start_sdo_polling() self._start_sdo_polling()
return True return True
except Exception as e: except Exception as e:
print(f"[CAN ERROR] {e}") print(f"[CONNECT ERROR] {e}")
self.connected = False
return False return False
def shutdown(self):
self.polling_active = False
if self.network:
self.network.disconnect()
self.nodes.clear()
self.connected = False
def _start_sdo_polling(self): def _start_sdo_polling(self):
if self.polling_thread and self.polling_thread.is_alive(): if self.polling_thread and self.polling_thread.is_alive():
return # Already running return
self.polling_active = True self.polling_active = True
self.polling_thread = threading.Thread(target=self._sdo_polling_loop, daemon=True) self.polling_thread = threading.Thread(target=self._sdo_polling_loop, daemon=True)
self.polling_thread.start() self.polling_thread.start()
@ -44,10 +58,13 @@ class CANBackend:
while self.polling_active: while self.polling_active:
with self.lock: with self.lock:
try: try:
fm2 = self.node.sdo[0x2004][2].raw # Poll only PU1 (node ID 0x02) for live monitor values
ps1 = self.node.sdo[0x2005][1].raw node = self.nodes.get(1)
self.latest_data["FM2"] = fm2/100 if node:
self.latest_data["PS1"] = ps1/1000 fm2 = node.sdo[0x2004][2].raw
ps1 = node.sdo[0x2005][1].raw
self.latest_data["FM2"] = fm2 / 100.0
self.latest_data["PS1"] = ps1 / 1000.0
print(f"[SDO POLL] FM2: {fm2}, PS1: {ps1}") print(f"[SDO POLL] FM2: {fm2}, PS1: {ps1}")
except Exception as e: except Exception as e:
print(f"[SDO POLL ERROR] {e}") print(f"[SDO POLL ERROR] {e}")
@ -58,9 +75,36 @@ class CANBackend:
return self.latest_data.copy() return self.latest_data.copy()
def read_current_state(self, pu_number: int): def read_current_state(self, pu_number: int):
# Placeholder for reading mode command try:
#TODO : CODE IT node = self.nodes.get(pu_number)
pass if node is None:
return "Offline"
state_raw = node.sdo[0x2000].raw
return self.decode_state(state_raw)
except Exception as e:
print(f"[PU{pu_number} READ ERROR] {e}")
return "Offline"
def decode_state(self, state_val: int) -> str:
state_map = {
0: "SYSTEM_MODE_INIT",
1: "SYSTEM_MODE_OFF",
2: "SYSTEM_MODE_READY",
3: "SYSTEM_MODE_PRODUCTION",
4: "SYSTEM_MODE_LOW_LOOP_PRESSURE",
5: "SYSTEM_MODE_LOOP_CLEANING",
6: "SYSTEM_MODE_HEATING_RO",
7: "SYSTEM_MODE_RINSING_RO",
8: "SYSTEM_MODE_HEATING_EDI",
9: "SYSTEM_MODE_COOLING_EDI",
10: "SYSTEM_MODE_RO_FLUSH",
11: "SYSTEM_MODE_RO_RINSE",
12: "SYSTEM_MODE_EDI_RINSE",
15: "SYSTEM_MODE_FAIL_SAFE",
16: "SYSTEM_MODE_FIRST_FLUSH",
255: "SYSTEM_MODE_DEFAULT"
}
return state_map.get(state_val, f"UNKNOWN({state_val})")
def send_state_command(self, state: str, pu_number: int, ploop_setpoint: float): def send_state_command(self, state: str, pu_number: int, ploop_setpoint: float):
if not self.connected: if not self.connected:
@ -78,21 +122,23 @@ class CANBackend:
raise ValueError(f"Invalid state: {state}") raise ValueError(f"Invalid state: {state}")
try: try:
print(f"[DEBUG] Writing state {state_map[state]} to index 0x2024, subindex {pu_number}") node = self.nodes.get(pu_number)
self.node.sdo[0x2024][pu_number].raw = state_map[state] if node is None:
raise ValueError(f"PU{pu_number} not connected")
# Write the ploop setpoint to its own index/subindex print(f"[DEBUG] Writing state {state_map[state]} to 0x2024:{pu_number}")
print(f"[DEBUG] Writing ploop_setpoint {ploop_setpoint} to index 0x2007") node.sdo[0x2024][pu_number].raw = state_map[state]
self.node.sdo[0x2007].raw = int(ploop_setpoint * 100)
print(f"[DEBUG] Writing ploop_setpoint {ploop_setpoint} to 0x2007")
node.sdo[0x2007].raw = int(ploop_setpoint * 100)
except Exception as e: except Exception as e:
print(f"[SDO WRITE ERROR] Failed to write to 0x2007:{e}") print(f"[SDO WRITE ERROR] PU{pu_number}: {e}")
raise raise
def send_thermal_loop_cleaning(self, mode: str, pu_number: int):
def send_thermal_loop_cleaning(self, mode: str):
if not self.connected: if not self.connected:
raise RuntimeError("Not connected to CAN") raise RuntimeError("CAN not connected")
mode_map = { mode_map = {
"IDLE": 0, "IDLE": 0,
@ -102,10 +148,14 @@ class CANBackend:
if mode not in mode_map: if mode not in mode_map:
raise ValueError(f"Invalid thermal loop mode: {mode}") raise ValueError(f"Invalid thermal loop mode: {mode}")
self.node.sdo[0x2024][0x01].raw = mode_map[mode] try:
node = self.nodes.get(pu_number)
if node is None:
raise ValueError(f"PU{pu_number} not connected")
def shutdown(self): print(f"[DEBUG] Sending thermal loop mode {mode} to 0x2024:{pu_number}")
self.polling_active = False node.sdo[0x2024][pu_number].raw = mode_map[mode]
if self.network:
self.network.disconnect() except Exception as e:
self.connected = False print(f"[THERMAL LOOP ERROR] PU{pu_number}: {e}")
raise

25
main.py
View File

@ -17,6 +17,7 @@ if platform.system() in ['Darwin']: # macOS or Windows
from MockCAN import CANBackend from MockCAN import CANBackend
else : else :
from classCAN import CANBackend # Your real backend from classCAN import CANBackend # Your real backend
app = FastAPI() app = FastAPI()
app.add_middleware(SessionMiddleware, secret_key="your_super_secret_key") app.add_middleware(SessionMiddleware, secret_key="your_super_secret_key")
router = APIRouter() router = APIRouter()
@ -94,21 +95,17 @@ def monitor_page(request: Request):
@app.post("/connect_toggle") @app.post("/connect_toggle")
def connect_toggle(): def connect_toggle():
"""Toggle CAN connection."""
logging.info("Toggling CAN connection...") logging.info("Toggling CAN connection...")
if can_backend.connected: if can_backend.connected:
can_backend.shutdown() can_backend.shutdown()
return {"connected": False} return {"connected": False}
else: else:
success = can_backend.connect( success = can_backend.connect()
node_id=0x02,
eds_path = os.path.join(os.path.dirname(__file__), "eds_file", "processBoard_0.eds")
)
if not success: if not success:
raise HTTPException(status_code=500, detail="Connection failed.") raise HTTPException(status_code=500, detail="Connection failed.")
return {"connected": True} return {"connected": True}
@app.post("/command/{state}/pu/{pu_number}") @app.post("/command/{state}/pu/{pu_number}")
def send_command(state: str, pu_number: int, ploop_setpoint: float = Query(...)): def send_command(state: str, pu_number: int, ploop_setpoint: float = Query(...)):
"""Send a state command to a specific PU.""" """Send a state command to a specific PU."""
@ -122,15 +119,13 @@ def send_command(state: str, pu_number: int, ploop_setpoint: float = Query(...))
@app.get("/api/pu_status") @app.get("/api/pu_status")
def get_pu_status(): def get_pu_status():
states = [can_backend.read_current_state(1),can_backend.read_current_state(2),can_backend.read_current_state(3)] states = {
logging.info(f"Reading state '{states}' from PUs") "PU1": can_backend.read_current_state(1),
"PU2": can_backend.read_current_state(2),
# Replace this with real machine status "PU3": can_backend.read_current_state(3),
return JSONResponse(content={ }
"PU1": states[0], logging.info(f"[PU STATUS] {states}")
"PU2": states[1], return JSONResponse(content=states)
"PU3": states[2]
})
@app.get("/monitor") @app.get("/monitor")