diff --git a/multivehicle/docker/Dockerfile b/multivehicle/docker/Dockerfile index ebd815d..790addb 100644 --- a/multivehicle/docker/Dockerfile +++ b/multivehicle/docker/Dockerfile @@ -116,7 +116,8 @@ RUN /opt/ros/jazzy/lib/mavros/install_geographiclib_datasets.sh # Pinned protoc 33 baked offline to /opt/protoc (supports edition = "2024"). # Installed off the system path so it never shadows /usr/bin/protoc; # setup_dashboard.sh wires it into the dashboard's third_party/protoc/. -ARG PROTOC_VERSION=33.0 +# 35.x matches the RobotX_2026 upstream generation (7.35 gencode). +ARG PROTOC_VERSION=35.1 RUN <=6.33,<8' \ + 'protobuf>=7.35,<8' \ 'tmuxp' # --------------------------------------------------------------------------- # @@ -289,6 +290,8 @@ COPY --from=ardupilot-builder /root/auv/ardupilot/build/sitl/bin/ardusub /usr/lo # mission_planner_2). Kept in `final` rather than `base` on purpose: touching this # list must not invalidate the px4-builder / ardupilot-builder caches. RUN apt-get update && apt-get install -y --no-install-recommends \ + mosquitto \ + mosquitto-clients \ python3-transforms3d \ ros-jazzy-navigation2 \ ros-jazzy-nav2-bringup \ @@ -299,16 +302,28 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ apt-get clean && \ rm -rf /var/lib/apt/lists/* -RUN pip3 install --no-cache-dir --break-system-packages mavsdk +# RoboCommand dev broker config (explicit listener; anonymous like upstream's +# RobotX_2026 reference broker). Note Debian's packaged mosquitto.conf sets +# `persistence true`, so retained messages survive broker restarts here — +# unlike upstream's compose broker. +COPY config/mosquitto-robocommand.conf /etc/mosquitto/conf.d/robocommand.conf + +RUN pip3 install --no-cache-dir --break-system-packages \ + mavsdk \ + 'paho-mqtt>=2.1,<3' COPY scripts/setup_dashboard.sh /usr/local/bin/setup_dashboard.sh RUN chmod +x /usr/local/bin/setup_dashboard.sh RUN cat >> /root/.bashrc <<'EOF' -# bb_robotx_dashboard runtime env -export ROBOCOMMAND_HOST=localhost -export ROBOCOMMAND_PORT=9000 -export ROBOCOMMAND_TEAM_ID=42 +# bb_robotx_dashboard runtime env (RobotX 2026 RoboCommand over MQTT) +export MQTT_BROKER_HOST=localhost +export MQTT_BROKER_PORT=1883 +export ROBOTX_TEAM_ID=RNRX +# JSON — pydantic-settings parses complex types as JSON, not CSV. +export ROBOTX_VEHICLE_IDS='["USV1","UUV1","UAV1"]' +export HEARTBEAT_PERIOD_S=1.0 +export AUTO_ACK_COMMANDS=true export DASHBOARD_LISTEN_HOST=0.0.0.0 export DASHBOARD_LISTEN_PORT=8080 export DASHBOARD_ADMIN_SECRET=bumblebee diff --git a/multivehicle/docker/config/mosquitto-robocommand.conf b/multivehicle/docker/config/mosquitto-robocommand.conf new file mode 100644 index 0000000..b55e3c0 --- /dev/null +++ b/multivehicle/docker/config/mosquitto-robocommand.conf @@ -0,0 +1,5 @@ +# RoboCommand dev broker (RobotX 2026). Explicit listener so the broker is +# reachable beyond loopback fallback mode, matching upstream's +# RobotX_2026/mosquitto/mosquitto.conf (anonymous, no TLS). +listener 1883 0.0.0.0 +allow_anonymous true diff --git a/multivehicle/docker/scripts/setup_dashboard.sh b/multivehicle/docker/scripts/setup_dashboard.sh index c5c7562..1b86f44 100755 --- a/multivehicle/docker/scripts/setup_dashboard.sh +++ b/multivehicle/docker/scripts/setup_dashboard.sh @@ -22,11 +22,18 @@ fi did_work=0 # 1. Clone the upstream robocommand repo (provides the .proto source). -if [ ! -d "${PKG_DIR}/third_party/robocommand/proto" ]; then - echo "[setup_dashboard] cloning robocommand proto source..." - git clone https://github.com/MonkeScripts/robocommand \ - "${PKG_DIR}/third_party/robocommand" || true - did_work=1 +# RobotX_2026/proto is the guard: a pre-RobotX (RoboBoat-era) checkout has +# only a top-level proto/ and must be deleted, not cloned into. +RC_DIR="${PKG_DIR}/third_party/robocommand" +if [ ! -d "${RC_DIR}/RobotX_2026/proto" ]; then + if [ -d "${RC_DIR}" ]; then + echo "[setup_dashboard] ${RC_DIR} is a stale pre-RobotX checkout." >&2 + echo "[setup_dashboard] delete it and re-run: rm -rf ${RC_DIR}" >&2 + else + echo "[setup_dashboard] cloning robocommand proto source..." + git clone https://github.com/robonation/robocommand "${RC_DIR}" || true + did_work=1 + fi fi # 2. Wire the image's pinned protoc 33 into third_party/protoc/ so @@ -39,14 +46,21 @@ if [ ! -x "${PKG_DIR}/third_party/protoc/bin/protoc" ] \ ln -sf /opt/protoc/bin/protoc "${PKG_DIR}/third_party/protoc/bin/protoc" fi -# 3. Generate the Python protobuf bindings if missing. -if [ -d "${PKG_DIR}/third_party/robocommand/proto" ] \ - && [ ! -f "${PKG_DIR}/bb_robotx_dashboard/proto/report_pb2.py" ]; then +# 3. Generate the Python protobuf bindings if missing. The sentinel is a +# RobotX-era module — keying on the old report_pb2.py would let a stale +# RoboBoat binding suppress regeneration forever. +if [ -d "${RC_DIR}/RobotX_2026/proto" ] \ + && [ ! -f "${PKG_DIR}/bb_robotx_dashboard/proto/robotx/rx_reports_pb2.py" ]; then echo "[setup_dashboard] compiling proto bindings..." ( cd "${PKG_DIR}" && bash scripts/compile_protos.sh ) || true did_work=1 fi +# 4. paho-mqtt 2.x fallback for containers built from a pre-MQTT image (the +# rebuilt image bakes it; apt's 1.x is not API-compatible). +python3 -c "import paho.mqtt" 2>/dev/null || \ + pip3 install --break-system-packages --no-cache-dir 'paho-mqtt>=2.1,<3' || true + if [ ! -d "${PKG_DIR}/frontend/dist" ]; then cat <ROS bridge (-> /x500/odom) -# 5) dashboard — web backend (:8080) + sim-side LED/incident bridges +# 5) dashboard — MQTT broker (:1883) + web backend (:8080) + monitor + sim-side bridges # # WS_DIR=/root/HOST/my_ws tmuxp load .../mvsim.yaml @@ -80,18 +80,25 @@ windows: - cmd: ros2 launch multivehicle_sim uav_gz.launch.py model_name:=x500_mono_cam_1 enter: false - # 5. Dashboard — fake vehicle (top, RoboCommand TCP server on :9000 the backend - # connects to), web backend (middle, http://localhost:8080), and the sim-side + # 5. Dashboard — RoboCommand MQTT broker (:1883), fake per-vehicle telemetry + # (ROS 2 topics the backend relays to MQTT), web backend + # (http://localhost:8080), a raw-frame monitor, and the sim-side # LED/incident bridges (bottom) tracking all three vehicles' /…/odom. - window_name: dashboard layout: even-vertical panes: - shell_command: - - cmd: ros2 run bb_robotx_dashboard fake_vehicle_publisher --port 9000 + - cmd: mosquitto -c /etc/mosquitto/mosquitto.conf -v + enter: false + - shell_command: + - cmd: ros2 run bb_robotx_dashboard fake_vehicle_publisher enter: false - shell_command: - cmd: ros2 launch bb_robotx_dashboard dashboard.launch.py enter: false + - shell_command: + - cmd: ros2 run bb_robotx_dashboard mqtt_spy + enter: false - shell_command: - cmd: ros2 launch bb_robotx_dashboard robotx_2026_sim.launch.py bridges_pkg:=robotx_gz models_pkg:=bb_worlds auv_name:=bluerov auv_pose_topic:=/bluerov/odom asv_name:=blueboat asv_pose_topic:=/blueboat/odom uav_name:=x500 uav_pose_topic:=/x500/odom enter: false diff --git a/multivehicle/tmuxp/mvsim_debug.yaml b/multivehicle/tmuxp/mvsim_debug.yaml index 779f397..4369d38 100644 --- a/multivehicle/tmuxp/mvsim_debug.yaml +++ b/multivehicle/tmuxp/mvsim_debug.yaml @@ -10,7 +10,7 @@ # 2) bluerov — BlueROV2 + ArduSub/MAVROS, then its square mission BT # 3) boat — BlueBoat spawn + control stack + square mission # 4) drone — XRCE agent + PX4 SITL x500 + gz bridge + offboard demo BT -# 5) dashboard — web backend (:8080) + sim-side LED/incident bridges +# 5) dashboard — MQTT broker (:1883) + web backend (:8080) + monitor + sim-side bridges # 6) debug — free shell + Foxglove bridge # # Workspace dir defaults to /root/HOST/mvsim_ws and the PX4 SITL install root to @@ -53,7 +53,7 @@ windows: - cmd: ros2 launch multivehicle_sim bluerov.launch.py enter: false - shell_command: - - cmd: ros2 launch bluerov_tasks bluerov_square_bt.launch.py + - cmd: sleep 5 & ros2 launch bluerov_tasks bluerov_square_bt.launch.py enter: false # 3. BlueBoat @@ -64,7 +64,7 @@ windows: - cmd: ros2 launch multivehicle_sim boat.launch.py enter: false - shell_command: - - cmd: ros2 launch multivehicle_examples boat_control.launch.py use_mission:=true + - cmd: sleep 5 & ros2 launch multivehicle_examples boat_control.launch.py use_mission:=true enter: false # 4. PX4 x500 drone @@ -81,19 +81,25 @@ windows: - cmd: ros2 launch multivehicle_sim uav_gz.launch.py model_name:=x500_mono_cam_1 enter: false - shell_command: - - cmd: ros2 launch multivehicle_examples px4_offboard.launch.py + - cmd: sleep 5 & ros2 launch multivehicle_examples px4_offboard.launch.py enter: false - # 5. Dashboard + # 5. Dashboard — broker, fake ROS telemetry, backend, MQTT monitor, bridges - window_name: dashboard layout: even-vertical panes: - shell_command: - - cmd: ros2 run bb_robotx_dashboard fake_vehicle_publisher --port 9000 + - cmd: mosquitto -c /etc/mosquitto/mosquitto.conf -v + enter: false + - shell_command: + - cmd: ros2 run bb_robotx_dashboard fake_vehicle_publisher enter: false - shell_command: - cmd: ros2 launch bb_robotx_dashboard dashboard.launch.py enter: false + - shell_command: + - cmd: ros2 run bb_robotx_dashboard mqtt_spy + enter: false - shell_command: - cmd: ros2 launch bb_robotx_dashboard robotx_2026_sim.launch.py bridges_pkg:=robotx_gz models_pkg:=bb_worlds auv_name:=bluerov auv_pose_topic:=/bluerov/odom asv_name:=blueboat asv_pose_topic:=/blueboat/odom uav_name:=x500 uav_pose_topic:=/x500/odom enter: false