Added changes for mode reply from PU
This commit is contained in:
parent
5d0a52e04b
commit
765f737f28
130
classCAN.py
130
classCAN.py
|
|
@ -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,11 +58,14 @@ 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
|
||||||
print(f"[SDO POLL] FM2: {fm2}, PS1: {ps1}")
|
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}")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"[SDO POLL ERROR] {e}")
|
print(f"[SDO POLL ERROR] {e}")
|
||||||
time.sleep(1.0)
|
time.sleep(1.0)
|
||||||
|
|
@ -57,12 +74,39 @@ class CANBackend:
|
||||||
with self.lock:
|
with self.lock:
|
||||||
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 send_state_command(self, state: str, pu_number: int, ploop_setpoint:float):
|
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):
|
||||||
if not self.connected:
|
if not self.connected:
|
||||||
raise RuntimeError("CAN not connected")
|
raise RuntimeError("CAN not 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
25
main.py
|
|
@ -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")
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user