Update 2026-04-22 11:19:55

This commit is contained in:
kassam 2026-04-22 11:19:59 +04:00
parent e7609b119f
commit 88e22a423b
3 changed files with 52 additions and 4 deletions

View File

@ -242,7 +242,7 @@ def _print_help():
# ── MAIN ─────────────────────────────────────────────────────────────────────
def main():
parser = argparse.ArgumentParser(description="Marcus CLI Client")
parser = argparse.ArgumentParser(description="Sanad CLI Client (talks to the Marcus brain over WebSocket)")
parser.add_argument("--ip", default=None, help="Server IP address")
parser.add_argument("--port", type=int, default=None, help="Server port")
args = parser.parse_args()
@ -250,7 +250,7 @@ def main():
eth_ip = _net.get("jetson_ip", "192.168.123.164")
wlan_ip = _net.get("jetson_wlan_ip", "10.255.254.86")
print(f"\n{C.BOLD}{C.ORANGE} MARCUS — CLI Client{C.RESET}")
print(f"\n{C.BOLD}{C.ORANGE} SANAD — CLI Client{C.RESET}")
print(f" {'' * 40}")
print(f" {C.GRAY}Connection options:{C.RESET}")
print(f" 1) eth0 — {eth_ip}:{DEFAULT_PORT}")

View File

@ -140,7 +140,7 @@ def btn(parent, text, bg_color=BG3, fg_color=WHITE, font=FONT_SM, command=None,
class MarcusGUI:
def __init__(self, root):
self.root = root
self.root.title("Marcus — Navigation Control")
self.root.title("Sanad — Navigation Control")
self.root.configure(bg=BG)
self.root.geometry("1360x860")
self.root.minsize(1000, 650)

View File

@ -29,10 +29,25 @@ from Brain.marcus_brain import init_brain, process_command, get_brain_status, sh
from API.camera_api import get_frame
_net = load_config("Network")
_cam = load_config("Camera")
HOST = "0.0.0.0"
PORT = _net.get("websocket_port", 8765)
def _camera_config_payload() -> dict:
"""Current camera parameters for the `camera_config` client event."""
return {
"type": "camera_config",
"profile": _cam.get("profile", "default"),
"width": _cam.get("width", 424),
"height": _cam.get("height", 240),
"fps": _cam.get("fps", 15),
"jpeg_quality": _cam.get("jpeg_quality", 70),
"pipeline_active": True, # camera thread is always on in this build
"timestamp": time.strftime("%H:%M:%S"),
}
connected_clients = set()
@ -76,9 +91,13 @@ async def handler(websocket):
"memory": status["memory"],
"camera": status["camera"],
"lidar": lidar_ok,
"message": "Marcus server ready (full brain)",
"message": "Sanad server ready (full brain)",
}))
# Also push current camera config so the GUI's Camera tab populates
# immediately on connect without needing an explicit get_camera round-trip.
await websocket.send(json.dumps(_camera_config_payload()))
try:
async for message in websocket:
data = json.loads(message)
@ -139,6 +158,35 @@ async def handler(websocket):
"timestamp": time.strftime("%H:%M:%S"),
}))
elif msg_type == "get_camera":
await websocket.send(json.dumps(_camera_config_payload()))
elif msg_type in ("set_camera", "set_resolution"):
# The RealSense pipeline is started once at boot with values
# from config_Camera.json; hot-switching resolution would
# require tearing down and rebuilding the camera thread.
# Not implemented today — reply with a clear error so the
# GUI surfaces it rather than hanging forever waiting for
# a camera_config event that never comes.
await websocket.send(json.dumps({
"type": "error",
"message": (f"`{msg_type}` is not supported in this build — "
"camera parameters are pinned from config_Camera.json "
"at startup."),
"echo": msg_type,
"timestamp": time.strftime("%H:%M:%S"),
}))
# Still send the current config so the GUI can refresh its UI.
await websocket.send(json.dumps(_camera_config_payload()))
else:
await websocket.send(json.dumps({
"type": "error",
"message": f"Unknown message type: {msg_type!r}",
"echo": msg_type,
"timestamp": time.strftime("%H:%M:%S"),
}))
except websockets.exceptions.ConnectionClosed:
print(f"[Server] Client disconnected: {client}")
finally: