#!/usr/bin/env bash if grep -qU $'\r' "$0" 2>/dev/null; then sed -i 's/\r$//' "$0" && exec bash "$0" "$@"; fi # Windows-Zeilenumbrüche selbst reparieren # ===================================================================== # 2_start_ursim_v2.sh (reduzierte Modellliste) # # Startet den Universal Robots Simulator (URSim PolyScope X) richtig: # - fragt, welches Robotermodell gestartet werden soll (Auswahlmenü) # - privilegierter Container mit host.docker.internal (wie von UR verlangt) # - Programme werden im Home-Ordner gespeichert (~/ursimx//applications) # - RTDE / Primary / Secondary / Realtime / Interpreter nach außen freigegeben # - wartet, bis PolyScope X wirklich fertig ist, und öffnet den Browser # # Aufruf: # bash 2_start_ursim.sh Auswahlmenü, Web-Port 8000 # bash 2_start_ursim.sh UR20 UR20 direkt starten # bash 2_start_ursim.sh UR10e 8001 UR10e auf Web-Port 8001 # bash 2_start_ursim.sh stop alle URSim-Container beenden # bash 2_start_ursim.sh status laufende URSim-Container anzeigen # # Optionale Umgebungsvariablen: # CB_TYPE=CB7 bash 2_start_ursim.sh UR18g Control-Box-Typ setzen # RTDE=0 bash 2_start_ursim.sh Roboter-Ports nicht freigeben # KEEP_OTHERS=1 bash 2_start_ursim.sh UR3e andere URSim nicht beenden # WEB_PORT=8001 bash 2_start_ursim.sh Web-Port fürs Auswahlmenü # ===================================================================== set -euo pipefail IMAGE="universalrobots/ursim_polyscopex:latest" # Roboter-Schnittstellen: Primary, Secondary, Realtime, RTDE, Interpreter ROBOT_PORTS=(30001 30002 30003 30004 30020) TIMEOUT_S=600 # Einträge für das Auswahlmenü: Typ Beschreibung MENU_ITEMS=( UR3e "e-Series" UR5e "e-Series" UR7e "e-Series" UR10e "e-Series" UR12e "e-Series" UR16e "e-Series" UR8LONG "UR8 Long" UR15 "UR-Serie" UR18 "UR-Serie" UR20 "UR-Serie" UR30 "UR-Serie" UR10g "g-Serie" UR17g "g-Serie" UR18g "g-Serie" STOP "Laufenden Simulator beenden" ) KNOWN_TYPES="UR3 UR5 UR10 UR16" # ältere Namen, vom Simulator weiter akzeptiert for (( i = 0; i < ${#MENU_ITEMS[@]} - 2; i += 2 )); do KNOWN_TYPES+=" ${MENU_ITEMS[i]}"; done step() { echo; echo -e "\e[1;34m==> $*\e[0m"; } ok() { echo -e "\e[1;32m [OK] $*\e[0m"; } warn() { echo -e "\e[1;33m [!] $*\e[0m"; } fail() { echo -e "\e[1;31m [X] $*\e[0m"; exit 1; } # --pause: Fenster am Ende offen lassen (für das Desktop-Symbol) PAUSE=0 if [[ "${1:-}" == "--pause" ]]; then PAUSE=1; shift fi finish() { if (( PAUSE )); then echo; read -r -p "Enter drücken zum Schließen ..." _; fi } trap finish EXIT # docker ohne sudo, falls der Benutzer schon in der Gruppe docker ist if docker info >/dev/null 2>&1; then DOCKER=(docker) elif command -v docker >/dev/null 2>&1; then warn "docker braucht noch sudo (nach der Installation einmal ab-/anmelden)." DOCKER=(sudo docker) sudo -v else fail "Docker ist nicht installiert - zuerst: bash 1_install_ursim_docker_v2.sh" fi d() { "${DOCKER[@]}" "$@"; } # Alle URSim-Container (laufend oder gestoppt) ursim_containers() { { d ps -aq --filter "name=^ursimx_"; d ps -aq --filter "ancestor=${IMAGE}"; } | sort -u } port_busy() { ss -ltnH "sport = :$1" 2>/dev/null | grep -q .; } stop_all() { step "URSim-Container beenden" local ids id name ids=$(ursim_containers) if [[ -z "$ids" ]]; then ok "Es läuft kein URSim-Container." else for id in $ids; do name=$(d inspect -f '{{.Name}}' "$id" | tr -d /) d rm -f -v "$id" >/dev/null ok "beendet: ${name}" done echo " Gespeicherte Programme bleiben in ~/ursimx erhalten." fi } # Auswahlmenü - gibt den gewählten Typ aus (leer = abgebrochen) choose_robot() { if command -v whiptail >/dev/null 2>&1; then whiptail --title "URSim PolyScope X" \ --ok-button "Starten" --cancel-button "Abbrechen" --default-item UR5e \ --menu "\nWelches Robotermodell soll gestartet werden?\n(Pfeiltasten + Enter)" \ 24 60 15 "${MENU_ITEMS[@]}" 3>&1 1>&2 2>&3 || true else local types=() i for (( i = 0; i < ${#MENU_ITEMS[@]}; i += 2 )); do types+=("${MENU_ITEMS[i]}"); done PS3="Nummer des Robotermodells: " select t in "${types[@]}"; do [[ -n "$t" ]] && { echo "$t"; break; }; done fi } # --------------------------------------------------------------------- # Unterbefehle: stop / status / help # --------------------------------------------------------------------- case "${1:-}" in stop) stop_all; exit 0 ;; status) d ps -a --filter "name=^ursimx_" --format 'table {{.Names}}\t{{.Status}}\t{{.Ports}}' exit 0 ;; -h|--help|help) PAUSE=0; sed -n '2,24p' "$0" | sed 's/^# \{0,1\}//' exit 0 ;; esac ROBOT_TYPE="${1:-}" WEB_PORT="${2:-${WEB_PORT:-8000}}" if [[ -z "$ROBOT_TYPE" ]]; then ROBOT_TYPE=$(choose_robot) clear 2>/dev/null || true if [[ -z "$ROBOT_TYPE" ]]; then PAUSE=0; echo "Abgebrochen."; exit 0 fi if [[ "$ROBOT_TYPE" == "STOP" ]]; then stop_all; exit 0 fi fi [[ "$ROBOT_TYPE" == "UR8L" ]] && ROBOT_TYPE="UR8LONG" # Kurzname aus der UR-Doku NAME="${NAME:-ursimx_${ROBOT_TYPE,,}}" APP_DIR="${HOME}/ursimx/${ROBOT_TYPE}/applications" case "$(uname -m)" in aarch64|arm64) HOST_ARCH=arm64 ;; *) HOST_ARCH=amd64 ;; esac echo -e "\e[1m" echo " ============================================================" echo " URSim PolyScope X starten: ${ROBOT_TYPE} (Web-Port ${WEB_PORT})" echo " ============================================================" echo -e "\e[0m" # --------------------------------------------------------------------- step "1/5 Prüfen" # --------------------------------------------------------------------- if ! grep -qw -- "$ROBOT_TYPE" <<<"$KNOWN_TYPES"; then warn "Unbekannter Robotertyp '${ROBOT_TYPE}'. Bekannt sind:" echo "$KNOWN_TYPES" | fold -s -w 64 | sed 's/^/ /' read -r -p " Trotzdem versuchen? [j/N] " a [[ "$a" =~ ^[jJyY]$ ]] || exit 1 fi ok "Robotertyp: ${ROBOT_TYPE}${CB_TYPE:+ (Control Box ${CB_TYPE})}" if ! d image inspect "$IMAGE" >/dev/null 2>&1; then warn "Image fehlt - wird jetzt heruntergeladen ..." d pull "$IMAGE" fi ok "Image: ${IMAGE}" # --------------------------------------------------------------------- step "2/5 Alte Container aufräumen" # --------------------------------------------------------------------- # Gleichnamigen Container immer neu anlegen (sauberer Start) if d inspect "$NAME" >/dev/null 2>&1; then d rm -f -v "$NAME" >/dev/null ok "alten Container '${NAME}' entfernt" fi # Andere Simulatoren beenden - sie belegen RAM und die Roboter-Ports if [[ "${KEEP_OTHERS:-0}" != "1" ]]; then for id in $(ursim_containers); do other=$(d inspect -f '{{.Name}}' "$id" | tr -d /) d rm -f -v "$id" >/dev/null ok "anderen Simulator '${other}' beendet" done fi ok "bereit" # --------------------------------------------------------------------- step "3/5 Ports prüfen" # --------------------------------------------------------------------- port_busy "$WEB_PORT" && fail "Web-Port ${WEB_PORT} ist belegt - anderen Port angeben, z.B.: bash $0 ${ROBOT_TYPE} 8001" ok "Web-Oberfläche: Port ${WEB_PORT}" PORT_ARGS=(-p "${WEB_PORT}:80") if [[ "${RTDE:-1}" == "1" ]]; then busy=() for p in "${ROBOT_PORTS[@]}"; do port_busy "$p" && busy+=("$p"); done if (( ${#busy[@]} )); then warn "Roboter-Ports belegt (${busy[*]}) - werden NICHT freigegeben." warn "Läuft noch ein anderer Simulator oder eine Port-Weiterleitung?" else for p in "${ROBOT_PORTS[@]}"; do PORT_ARGS+=(-p "${p}:${p}"); done ok "Roboter-Ports: ${ROBOT_PORTS[*]} (30004 = RTDE)" fi fi # --------------------------------------------------------------------- step "4/5 Container starten" # --------------------------------------------------------------------- mkdir -p "$APP_DIR" ENV_ARGS=(-e "HOST_ARCH=${HOST_ARCH}" -e "ROBOT_TYPE=${ROBOT_TYPE}") [[ -n "${CB_TYPE:-}" ]] && ENV_ARGS+=(-e "CB_TYPE=${CB_TYPE}") set -x "${DOCKER[@]}" run -d \ --name "$NAME" \ --privileged \ --network bridge \ --add-host "host.docker.internal:host-gateway" \ "${ENV_ARGS[@]}" \ "${PORT_ARGS[@]}" \ -v "${APP_DIR}:/ur/bin/backend/applications" \ "$IMAGE" >/dev/null { set +x; } 2>/dev/null ok "Container '${NAME}' gestartet" ok "Programme werden gespeichert in: ${APP_DIR}" # --------------------------------------------------------------------- step "5/5 Warten, bis PolyScope X bereit ist (dauert 1-3 Minuten)" # --------------------------------------------------------------------- # nginx antwortet schon lange bevor PolyScope X fertig ist - deshalb den # Bootstrapper fragen, ob alle System-URCaps installiert sind. STATUS_URL="http://localhost:${WEB_PORT}/universal-robots/bootstrapper/status" start=$SECONDS while :; do if [[ "$(d inspect -f '{{.State.Running}}' "$NAME" 2>/dev/null)" != "true" ]]; then echo d logs --tail 30 "$NAME" 2>&1 | sed 's/^/ | /' fail "Container wurde beendet - siehe Log oben." fi json=$(curl -s -m 5 "$STATUS_URL" || true) if grep -q '"isFinished":true' <<<"$json"; then break fi done_n=$(grep -o '"numberProcessedUrcaps":[0-9]*' <<<"$json" | tr -dc '0-9' || true) total_n=$(grep -o '"totalNumberOfUrcaps":[0-9]*' <<<"$json" | tr -dc '0-9' || true) el=$(( SECONDS - start )) if [[ -n "$total_n" && "$total_n" != "0" ]]; then msg="URCaps installiert: ${done_n:-0}/${total_n}" else msg="Simulator startet ..." fi printf "\r [%3ds] %-40s" "$el" "$msg" (( el > TIMEOUT_S )) && { echo; fail "Zeitüberschreitung - Log: docker logs ${NAME}"; } sleep 3 done printf "\r%-60s\r" " " ok "PolyScope X ist bereit ($(( SECONDS - start )) s)" # --------------------------------------------------------------------- VM_IP=$(hostname -I 2>/dev/null | awk '{print $1}') URL="http://localhost:${WEB_PORT}" if [[ -n "${DISPLAY:-}${WAYLAND_DISPLAY:-}" ]]; then for b in google-chrome chromium chromium-browser xdg-open; do if command -v "$b" >/dev/null 2>&1; then nohup "$b" "$URL" >/dev/null 2>&1 & break fi done fi echo echo -e "\e[1;32m ============================================================" echo " URSim PolyScope X (${ROBOT_TYPE}) läuft!" echo -e " ============================================================\e[0m" echo echo " PolyScope X in der VM: ${URL}" [[ -n "$VM_IP" ]] && echo " PolyScope X von außen: http://${VM_IP}:${WEB_PORT}" if [[ " ${PORT_ARGS[*]} " == *" 30004:30004 "* && -n "$VM_IP" ]]; then echo " RTDE: ${VM_IP}:30004" fi echo echo " Status anzeigen: bash $0 status" echo " Log ansehen: docker logs -f ${NAME}" echo " Beenden: bash $0 stop" echo