99 lines
3.5 KiB
Python
99 lines
3.5 KiB
Python
"""
|
|
Entry point: ``python -m backend``
|
|
|
|
Binds 0.0.0.0 and prints every address the dashboard is reachable on, so you can
|
|
open it from a phone or tablet on the same Wi-Fi without knowing this machine's
|
|
IP in advance.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import sys
|
|
|
|
from . import netinfo, settings
|
|
|
|
|
|
def main() -> int:
|
|
parser = argparse.ArgumentParser(prog="python -m backend",
|
|
description="AGIBOT X2 dashboard server")
|
|
parser.add_argument("--port", type=int, help="HTTP port (default from config.json)")
|
|
parser.add_argument("--host", default=None, help="Bind address (default 0.0.0.0 - all interfaces)")
|
|
parser.add_argument("--robot", default=None, help="Robot host or IP; saved to config.json")
|
|
parser.add_argument("--domain", type=int, default=None, help="ROS_DOMAIN_ID")
|
|
parser.add_argument("--mode", choices=["auto", "ros2", "mock"], default=None,
|
|
help="Bridge mode: auto (default), ros2 (require rclpy), mock (simulate)")
|
|
parser.add_argument("--reload", action="store_true", help="Auto-reload on source changes")
|
|
args = parser.parse_args()
|
|
|
|
updates = {}
|
|
if args.port is not None:
|
|
updates["port"] = args.port
|
|
if args.robot is not None:
|
|
updates["robot_host"] = args.robot
|
|
if args.domain is not None:
|
|
updates["ros_domain_id"] = args.domain
|
|
if args.mode is not None:
|
|
updates["bridge_mode"] = args.mode
|
|
if updates:
|
|
settings.save(updates)
|
|
|
|
config = settings.load()
|
|
host = args.host or config["host"]
|
|
port = config["port"]
|
|
|
|
try:
|
|
import uvicorn
|
|
except ImportError:
|
|
print("uvicorn is not installed. Run: pip install -r requirements.txt", file=sys.stderr)
|
|
return 1
|
|
|
|
name = config.get("dashboard_name") if config.get("advertise_name") else None
|
|
detail = netinfo.address_detail()
|
|
|
|
print()
|
|
print(" AGIBOT X2 Dashboard")
|
|
print(" " + "-" * 52)
|
|
if detail["ip"]:
|
|
print(" Open this from a phone, tablet or laptop:")
|
|
print(f" http://{detail['ip']}:{port}")
|
|
print(f" (live {detail['kind'] or 'network'} address on '{detail['adapter']}')")
|
|
else:
|
|
print(" No usable network address right now:")
|
|
print(f" {detail['reason_text'] or detail['reason']}")
|
|
print(" The dashboard is still running; it will pick the address up")
|
|
print(" automatically when the network comes back.")
|
|
|
|
others = [u for u in netinfo.dashboard_urls(port, name)
|
|
if u != f"http://{detail['ip']}:{port}"]
|
|
if others:
|
|
print()
|
|
print(" Also reachable at:")
|
|
for url in others:
|
|
print(f" {url}")
|
|
print(" " + "-" * 52)
|
|
print(f" robot host {config['robot_host'] or '(not set - use the Settings tab)'}")
|
|
print(f" bridge mode {config['bridge_mode']}")
|
|
print(f" ROS_DOMAIN_ID {config['ros_domain_id']}")
|
|
print(" Open any address above from any device on this network.")
|
|
print(flush=True)
|
|
|
|
uvicorn.run(
|
|
"backend.server:app",
|
|
host=host,
|
|
port=port,
|
|
reload=args.reload,
|
|
log_level="info",
|
|
access_log=False,
|
|
# websockets 16.x crashes uvicorn's default WS impl (starlette/anyio
|
|
# "cancel scope" error) -> stuck "Connecting". wsproto is a stable,
|
|
# isolated WS engine that avoids it without touching the shared
|
|
# `websockets` package the voice pipeline depends on.
|
|
ws="wsproto",
|
|
)
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|