#!/usr/bin/env bash # probe_eng.sh — read-only EngineAI PM01 discovery. # # bash tools/probe_eng.sh [user] # bash tools/probe_eng.sh 10.210.136.150 ubuntu # # Prints what the robot ACTUALLY exposes, each line labelled with the .env # variable it feeds. Installs nothing, writes nothing, publishes to no topic — # safe to run against a live robot. # # Auth: uses an ssh key if FLEET_SSH_KEY is set, otherwise sshpass with # FLEET_SSH_PASS (default "ubuntu"). Set FLEET_SSH_PASS in the environment # rather than editing this file. set -uo pipefail IP="${1:-}" USER_="${2:-ubuntu}" if [ -z "$IP" ]; then echo "usage: bash tools/probe_eng.sh [user]" >&2 exit 2 fi SSH_OPTS="-o StrictHostKeyChecking=accept-new -o ConnectTimeout=8 -o BatchMode=no" if [ -n "${FLEET_SSH_KEY:-}" ]; then RUN() { ssh $SSH_OPTS -i "$FLEET_SSH_KEY" "$USER_@$IP" "$@"; } elif command -v sshpass >/dev/null 2>&1; then RUN() { sshpass -p "${FLEET_SSH_PASS:-ubuntu}" ssh $SSH_OPTS "$USER_@$IP" "$@"; } else RUN() { ssh $SSH_OPTS "$USER_@$IP" "$@"; } fi hdr() { printf '\n\033[1m== %s\033[0m\n' "$1"; } # The robot's own environment file is the source of truth for ROS_DOMAIN_ID, # RMW_IMPLEMENTATION and CYCLONEDDS_URI. Sourcing it (rather than guessing 0) # is the difference between seeing 44 topics and seeing 2. ROSENV='set +u . /opt/ros/humble/setup.bash >/dev/null 2>&1 . /app/applications/install/bringup/ros_env.sh >/dev/null 2>&1' hdr "host (-> SOFTWARE_*, MAC_INTERFACE, STORAGE_PATH)" RUN "hostname; uname -srm; grep PRETTY_NAME /etc/os-release echo -n 'board : '; tr -d '\0' < /proc/device-tree/model 2>/dev/null; echo echo -n 'board_serial: '; tr -d '\0' < /proc/device-tree/serial-number 2>/dev/null; echo echo -n 'l4t : '; head -1 /etc/nv_tegra_release 2>/dev/null echo -n 'product : '; cat /app/applications/install/bringup/product.env 2>/dev/null echo '--- NICs (MAC_INTERFACE = the one carrying the LAN address) ---' ip -o -4 addr show | awk '{printf \" %-10s %-18s mac=\", \$2, \$4} {\"cat /sys/class/net/\" \$2 \"/address\" | getline m; print m}' echo '--- disk (STORAGE_PATH) ---'; df -h / | tail -1" hdr "ROS env (-> ROS_DOMAIN_ID; the unit must source ros_env.sh)" RUN "cat /app/applications/install/bringup/ros_env.sh 2>/dev/null echo '--- CycloneDDS interface pinning ---' cat /app/applications/install/bringup/cyclonedds.xml 2>/dev/null" hdr "ROS topics (-> ENG_TOPIC_* / ENG_TYPE_*)" RUN "$ROSENV; timeout 30 ros2 topic list -t 2>/dev/null" hdr "message definitions (-> ENG_FIELD_*)" RUN "for m in PowerInfo JointState MotorDebug MotionState; do echo \"--- \$m ---\" find /app/applications/install/interface_protocol -name \"\$m.msg\" \ -exec cat {} \; 2>/dev/null echo done" hdr "live samples (-> verify ENG_SOC_SCALE / ENG_CURRENT_SIGN / units)" RUN "$ROSENV echo '--- /hardware/power_info (battery: percentage, voltage, current) ---' timeout 12 ros2 topic echo --once /hardware/power_info 2>/dev/null echo '--- /motion/motion_state (control.mode + switchable_modes) ---' timeout 12 ros2 topic echo --once /motion/motion_state 2>/dev/null echo '--- /hardware/motor_debug (motor_temp; first lines only) ---' timeout 12 ros2 topic echo --once /hardware/motor_debug 2>/dev/null | head -12" hdr "publish rates (-> ENG_JOINT_MIN_PERIOD / ENG_MOTOR_MIN_PERIOD)" RUN "$ROSENV for t in /hardware/power_info /hardware/joint_state /hardware/motor_debug /motion/motion_state; do printf ' %-28s ' \"\$t\" timeout 8 ros2 topic hz \$t 2>/dev/null | grep -m1 average || echo 'NO DATA' done" hdr "CHARGE DIRECTION (-> ENG_CURRENT_SIGN) ~45s" echo " If percentage FALLS while current is POSITIVE, positive = discharge" echo " and ENG_CURRENT_SIGN must be -1 (otherwise the robot reports 'charging'" echo " forever while its battery drains)." RUN "$ROSENV for i in 1 2 3; do printf ' %s ' \"\$(date +%H:%M:%S)\" timeout 8 ros2 topic echo --once /hardware/power_info 2>/dev/null \ | tr '\n' ' ' | sed 's/---//' echo [ \$i -lt 3 ] && sleep 18 done" hdr "position sources (-> ENG_POSITION_SOURCE / ENG_TOPIC_ODOM)" RUN "$ROSENV echo -n ' odometry topics: ' timeout 20 ros2 topic list 2>/dev/null | grep -iE 'odom|/tf$|amcl|pose' || echo 'NONE (position reports null)' echo -n ' sanad nav status: ' curl -s -m5 http://127.0.0.1:8014/api/nav/status 2>/dev/null | head -c 220; echo" hdr "dashboards / remote (-> REMOTE_PORTS, SSH_USER)" RUN "echo '--- listening ports ---'; ss -tlnp 2>/dev/null | awk 'NR>1{print \" \" \$4}' | sort -u for p in 8014 8001 9002 9003 9004 8765; do printf ' %-6s ' \$p curl -s -m3 -o /dev/null -w 'HTTP %{http_code}' http://127.0.0.1:\$p/ 2>/dev/null || printf 'closed' curl -s -m3 http://127.0.0.1:\$p/ 2>/dev/null | grep -qi 'sanad\|dashboard' && printf ' <- dashboard page' echo done" hdr "project logs (-> PROJECT_LOG_CONTAINER; needs root)" RUN "sudo -n docker ps --format ' {{.Names}} {{.Status}}' 2>/dev/null \ || echo ' (needs sudo; the agent runs as root and can read these)'" hdr "done" echo "Set the values above in agent/.env, then: systemctl restart sanad-api-eng"