diff --git a/.github/workflows/build-docker-image.yaml b/.github/workflows/build-docker-image.yaml index 6d36f6a..3a835d9 100644 --- a/.github/workflows/build-docker-image.yaml +++ b/.github/workflows/build-docker-image.yaml @@ -1,64 +1,65 @@ -name: Build/Publish Docker Image +name: Build a Docker Image -on: +on: + push: workflow_dispatch: - inputs: - build_type: - description: "Is it a \"development\" or a \"stable\" release?" - required: true - default: 'development' - type: choice - options: - - development - - stable - target_distro: - description: "In case of \"stable\" release specify the ROS distro of the existing docker image (eg. humble)" - type: string - default: "ardent" - target_release: - description: "In case of \"stable\" release specify the version of the existing docker image (eg. 1.0.12)" - type: string - default: "0.0.0" - target_date: - description: "In case of \"stable\" release specify the date of the existing docker image in format YYYYMMDD (eg. 20220124)" - type: string - default: "20131206" - repository_dispatch: - types: [rebuild] - pull_request: - types: - - closed - - opened - + jobs: build: runs-on: ubuntu-20.04 - strategy: - fail-fast: false - matrix: - - include: - - dockerfile: Dockerfile - platforms: "linux/amd64, linux/arm64" - ros_distro: "humble" - repo_name: foxglove steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Set FOXGLOVE_VERSION env + run: echo "FOXGLOVE_VERSION=v1.72.0" >> $GITHUB_ENV + shell: bash + + - name: Set SHORT_DATE env + run: echo "SHORT_DATE=$(date +%Y%m%d)" >> $GITHUB_ENV + shell: bash - - name: Checkout - uses: actions/checkout@v2 + - name: Set up QEMU + uses: docker/setup-qemu-action@v2 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + with: + version: latest + + - name: Login to Docker Registry + uses: docker/login-action@v2 + with: + registry: docker.io + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Build and push (production) + if: github.ref_name == 'main' + uses: docker/build-push-action@v3 + with: + platforms: linux/amd64, linux/arm64 + push: true + build-args: | + FOXGLOVE_VERSION=${{ env.FOXGLOVE_VERSION }} + tags: | + husarion/foxglove:${{ env.FOXGLOVE_VERSION }}-${{ env.SHORT_DATE }} + husarion/foxglove:${{ env.FOXGLOVE_VERSION }} + husarion/foxglove:nightly + # cache-from: type=registry,ref=husarnet/dds-router + cache-to: type=inline - - name: Build Docker Image - uses: husarion-ci/ros-docker-img-action@v0.2 - with: - dockerhub_username: ${{ secrets.DOCKERHUB_USERNAME }} - dockerhub_token: ${{ secrets.DOCKERHUB_TOKEN }} - dockerfile: ${{ matrix.dockerfile }} - repo_name: ${{ matrix.repo_name }} - build_type: ${{ inputs.build_type }} - ros_distro: ${{ matrix.ros_distro }} - platforms: ${{ matrix.platforms }} - # variables important only for stable release - target_distro: ${{ inputs.target_distro }} - target_release: ${{ inputs.target_release }} - target_date: ${{ inputs.target_date }} + - name: Build and push (feature branch) + if: github.ref_name != 'main' + uses: docker/build-push-action@v3 + with: + platforms: linux/amd64, linux/arm64 + push: true + build-args: | + FOXGLOVE_VERSION=${{ env.FOXGLOVE_VERSION }} + tags: | + husarion/foxglove:${{ github.head_ref || github.ref_name }}-${{ env.FOXGLOVE_VERSION }} + husarion/foxglove:nightly + # cache-from: type=registry,ref=husarnet/dds-router-${{ github.head_ref || github.ref_name }} + cache-to: type=inline \ No newline at end of file diff --git a/Caddyfile b/Caddyfile index d27b3e0..d78235c 100644 --- a/Caddyfile +++ b/Caddyfile @@ -1,31 +1,20 @@ # Listen on localhost on the specified port -:{$FOXGLOVE_PORT} { - root * /src +:{$UI_PORT} { + tls internal + + root * /src + + redir /ui /?ds={$DS_TYPE}&ds.url=ws%3A%2F%2F{http.request.host}%3A{$DS_PORT} templates { - mime "application/json" "text/plain" + mime "application/json" "text/plain" "text/html" } - file_server browse + file_server browse @urdf path /*.urdf - header @urdf { - Content-Type "text/plain" - } - - # respond /get-default-ws "{{.Host}}{{env 'FOXGLOVE_PORT'}}" 200 { - # close - # } - - header { - # get rid of CORS error - Access-Control-Allow-Origin * - Access-Control-Allow-Credentials true - Access-Control-Allow-Methods * - Access-Control-Allow-Headers * - defer - # Disable caching - Cache-Control "no-cache, no-store, must-revalidate" - } + header @urdf { + Content-Type "text/plain" + } } diff --git a/Dockerfile b/Dockerfile index 24010e9..8fc7195 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,11 +1,29 @@ -ARG FOXGLOVE_VERSION=v1.39.1 +ARG FOXGLOVE_VERSION=v1.72.0 # URDF stage FROM husarion/rviz2:humble-nightly as urdf_builder + +RUN apt-get update && apt-get install -y \ + python3-pip \ + python3-colcon-common-extensions \ + python3-rosdep \ + python3-vcstool \ + git && \ + git clone -b ros2-control https://github.com/husarion/panther_ros.git /ros2_ws/src/panther_ros && \ + rm -rf /ros2_ws/src/panther_ros/panther && \ + rm -rf /ros2_ws/src/panther_ros/panther_battery && \ + rm -rf /ros2_ws/src/panther_ros/panther_controller && \ + rm -rf /ros2_ws/src/panther_ros/panther_hardware_interfaces && \ + rm -rf /ros2_ws/src/panther_ros/panther_utils && \ + cd /ros2_ws && \ + source install/setup.bash && \ + colcon build + RUN apt-get update -y && apt-get install -y ros-$ROS_DISTRO-xacro && \ source install/setup.bash && \ xacro /ros2_ws/src/rosbot_ros/rosbot_description/urdf/rosbot.urdf.xacro > /rosbot.urdf && \ - xacro /ros2_ws/src/rosbot_xl_ros/rosbot_xl_description/urdf/rosbot_xl.urdf.xacro > /rosbot_xl.urdf && \ + xacro /ros2_ws/src/rosbot_xl_ros/rosbot_xl_description/urdf/rosbot_xl.urdf.xacro use_sim:=false simulation_controllers_config_file:=None > /rosbot_xl.urdf && \ + xacro /ros2_ws/src/panther_ros/panther_description/urdf/panther.urdf.xacro > /panther.urdf && \ # Changing rotation is cause by .stl files in rosbot_ros/rosbot_description. We will change it to the .dae files. sed -i 's/rpy=\"1.5707963267948966 0.0 1.5707963267948966\"/rpy=\"0.0 0.0 1.5707963267948966\"/g' /rosbot.urdf @@ -21,13 +39,6 @@ WORKDIR /src RUN git clone --branch $FOXGLOVE_VERSION https://github.com/foxglove/studio WORKDIR /src/studio -COPY studio-files/defaultLayout.ts ./packages/studio-base/src/providers/CurrentLayoutProvider/defaultLayout.ts -COPY studio-files/Start.tsx ./packages/studio-base/src/components/OpenDialog/Start.tsx -COPY studio-files/Root.tsx ./web/src/Root.tsx -COPY studio-files/RosbridgeDataSourceFactory.ts ./packages/studio-base/src/dataSources/RosbridgeDataSourceFactory.ts -RUN grep -rl 'showOpenDialogOnStartup = true' ./packages/studio-base/src/Workspace.tsx \ - | xargs sed -i 's/showOpenDialogOnStartup = true/showOpenDialogOnStartup = false/g' - RUN corepack enable RUN yarn install --immutable @@ -39,37 +50,35 @@ FROM caddy:2.6.2-alpine ARG FOXGLOVE_VERSION -RUN apk update && apk add bash +RUN apk update && apk add \ + bash \ + nss-tools SHELL ["/bin/bash", "-c"] WORKDIR /src COPY --from=build /src/studio/web/.webpack . -COPY FoxgloveDefaultLayout.json . -COPY default-url.txt . +COPY FoxgloveDefaultLayout.json /foxglove/default-layout.json COPY Caddyfile /etc/caddy/ -COPY caddy_entrypoint.sh / +COPY entrypoint.sh / # copy URDFs COPY --from=urdf_builder /ros2_ws ./ros2_ws COPY --from=urdf_builder /rosbot.urdf . COPY --from=urdf_builder /rosbot_xl.urdf . - -# replace file:///ros2_ws with http://{{.Host}}:FOXGLOVE_PORT/ros2_ws in /src/rosbot_xl.urdf and /src/rosbot.urdf files -RUN sed -i 's|file:///ros2_ws|http://{{.Host}}:{{env "FOXGLOVE_PORT"}}/ros2_ws|g' /src/rosbot_xl.urdf /src/rosbot.urdf - -# replace localhost:8080 with {{.Host}}:FOXGLOVE_PORT in /src/FoxgloveDefaultLayout.json file -RUN sed -i 's|localhost:8080|{{.Host}}:{{env "FOXGLOVE_PORT"}}|g' /src/FoxgloveDefaultLayout.json +COPY --from=urdf_builder /panther.urdf . EXPOSE 8080 -# CMD ["caddy", "file-server", "--listen", ":8080"] -RUN echo $(echo $FOXGLOVE_VERSION | sed -r 's/v([0-9]+.[0-9]+.[0-9]+)/\1/g') > /version.txt +ENV DS_TYPE=rosbridge-websocket +# ENV DS_TYPE=foxglove-websocket +ENV DS_PORT=9090 +ENV UI_PORT=8080 -ENV FOXGLOVE_PORT=8080 -ENV ROSBRIDGE_PORT=9090 +# replace file:///ros2_ws with http://{{.Host}}:UI_PORT/ros2_ws in /src/rosbot_xl.urdf and /src/rosbot.urdf files +RUN sed -i 's|file:///ros2_ws|http://{{.Host}}:{{env "UI_PORT"}}/ros2_ws|g' /src/rosbot_xl.urdf /src/rosbot.urdf /src/panther.urdf -# ENTRYPOINT [ "/bin/bash", "/caddy_entrypoint.sh" ] +ENTRYPOINT ["/bin/sh", "/entrypoint.sh"] CMD ["caddy", "run", "--config", "/etc/caddy/Caddyfile", "--adapter", "caddyfile"] diff --git a/FoxgloveDefaultLayout.json b/FoxgloveDefaultLayout.json index e520737..e82443c 100644 --- a/FoxgloveDefaultLayout.json +++ b/FoxgloveDefaultLayout.json @@ -1,85 +1,75 @@ { "configById": { - "3D!40jejke": { + "3D!18i6zy7": { + "layers": { + "845139cb-26bc-40b3-8161-8ab60af4baf5": { + "visible": true, + "frameLocked": true, + "label": "Grid", + "instanceId": "845139cb-26bc-40b3-8161-8ab60af4baf5", + "layerId": "foxglove.Grid", + "size": 10, + "divisions": 10, + "lineWidth": 1, + "color": "#248eff", + "position": [ + 0, + 0, + 0 + ], + "rotation": [ + 0, + 0, + 0 + ], + "order": 1 + }, + "12a9ec87-514c-4118-861f-d8ffd5947834": { + "visible": true, + "frameLocked": true, + "label": "URDF", + "instanceId": "12a9ec87-514c-4118-861f-d8ffd5947834", + "layerId": "foxglove.Urdf", + "sourceType": "url", + "url": "http://localhost:8080/panther.urdf", + "filePath": "", + "parameter": "", + "topic": "", + "framePrefix": "", + "displayMode": "auto", + "fallbackColor": "#ffffff", + "order": 2 + } + }, "cameraState": { - "distance": 0.7504827842222612, "perspective": true, - "phi": 60.34220532319342, - "target": [0, 0, 0], - "targetOffset": [0, 0, 0], - "targetOrientation": [0, 0, 0, 1], - "thetaOffset": 44.9999999999999, + "distance": 3.1555842957637816, + "phi": 59.99999999999901, + "thetaOffset": 45.00000000000005, + "targetOffset": [ + 0.3015677915671571, + 0.3762082426349962, + 2.3927830801494895e-17 + ], + "target": [ + 0, + 0, + 0 + ], + "targetOrientation": [ + 0, + 0, + 0, + 1 + ], "fovy": 45, "near": 0.5, "far": 5000 }, "followMode": "follow-pose", "scene": {}, - "transforms": { - "frame:base_link": { - "visible": false - }, - "frame:body_link": { - "visible": false - }, - "frame:cover_link": { - "visible": false - }, - "frame:imu_link": { - "visible": false - }, - "frame:camera_link": { - "visible": false - }, - "frame:fl_range": { - "visible": false - }, - "frame:fr_range": { - "visible": false - }, - "frame:rl_range": { - "visible": false - }, - "frame:rr_range": { - "visible": false - }, - "frame:fl_wheel_link": { - "visible": false - }, - "frame:fr_wheel_link": { - "visible": false - }, - "frame:rl_wheel_link": { - "visible": false - }, - "frame:rr_wheel_link": { - "visible": false - }, - "frame:slamtec_rplidar_a2_link": { - "visible": false - }, - "frame:laser": { - "visible": false - }, - "frame:orbbec_astra_link": { - "visible": false - }, - "frame:depth": { - "visible": false - } - }, + "transforms": {}, "topics": {}, - "layers": { - "e827a6dc-875b-448a-8475-5497577c2e1b": { - "visible": true, - "frameLocked": true, - "label": "URDF", - "instanceId": "e827a6dc-875b-448a-8475-5497577c2e1b", - "layerId": "foxglove.Urdf", - "url": "http://localhost:8080/rosbot.urdf", - "order": 1 - } - }, "publish": { "type": "point", "poseTopic": "/move_base_simple/goal", @@ -88,43 +78,57 @@ "poseEstimateXDeviation": 0.5, "poseEstimateYDeviation": 0.5, "poseEstimateThetaDeviation": 0.26179939 - } - }, - "ImageViewPanel!4091812": { - "cameraTopic": "", - "enabledMarkerTopics": [], - "mode": "fit", - "pan": { - "x": 0, - "y": 0 }, - "rotation": 0, - "synchronize": false, - "transformMarkers": false, - "zoom": 1 + "imageMode": {} }, - "Teleop!jo9z4f": { - "publishRate": 1, - "upButton": { - "field": "linear-x", - "value": 1 - }, - "downButton": { - "field": "linear-x", - "value": -1 + "RawMessages!os6rgs": { + "diffEnabled": false, + "diffMethod": "custom", + "diffTopicPath": "", + "showFullMessageForDiff": false, + "topicPath": "" + }, + "Image!3mnp456": { + "cameraState": { + "distance": 20, + "perspective": true, + "phi": 60, + "target": [ + 0, + 0, + 0 + ], + "targetOffset": [ + 0, + 0, + 0 + ], + "targetOrientation": [ + 0, + 0, + 0, + 1 + ], + "thetaOffset": 45, + "fovy": 45, + "near": 0.5, + "far": 5000 }, - "leftButton": { - "field": "angular-z", - "value": 1 + "followMode": "follow-pose", + "scene": {}, + "transforms": {}, + "topics": {}, + "layers": {}, + "publish": { + "type": "point", + "poseTopic": "/move_base_simple/goal", + "pointTopic": "/clicked_point", + "poseEstimateTopic": "/initialpose", + "poseEstimateXDeviation": 0.5, + "poseEstimateYDeviation": 0.5, + "poseEstimateThetaDeviation": 0.26179939 }, - "rightButton": { - "field": "angular-z", - "value": -1 - } - }, - "RosOut!b0toow": { - "searchTerms": [], - "minLogLevel": 1 + "imageMode": {} } }, "globalVariables": {}, @@ -133,18 +137,14 @@ "speed": 1 }, "layout": { - "first": { - "first": "3D!40jejke", - "second": { - "first": "ImageViewPanel!4091812", - "second": "RosOut!b0toow", - "direction": "column" - }, - "direction": "row", - "splitPercentage": 72.58448357924799 + "first": "3D!18i6zy7", + "second": { + "first": "Image!3mnp456", + "second": "RawMessages!os6rgs", + "direction": "column", + "splitPercentage": 30 }, - "second": "Teleop!jo9z4f", - "direction": "column", - "splitPercentage": 79.54319761668322 + "direction": "row", + "splitPercentage": 70 } } diff --git a/README.md b/README.md index 15a06c2..d09dcfb 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,50 @@ # foxglove-docker + Foxglove docker images customized for running directly on the robot + +## Environment Variables + +| Environment Variable | Default Value | Description | +| - | - | - | +| `DS_TYPE` | `rosbridge-websocket` | Data source type. Possible values: `rosbridge-websocket` or `foxglove-websocket` | +| `DS_PORT` | `9090` | Data source port | +| `UI_PORT` | `8080` | User interface port | + +## Quick Start + +Create the `compose.yaml` file + +```yaml +services: + foxglove: + image: husarion/foxglove:v1.72.0 + ports: + - 8080:8080 + # optionaly override the default config + # volumes: + # - ./custom-ui-config.json:/foxglove/default-layout.json + environment: + - DS_TYPE=rosbridge-websocket + - DS_PORT=9090 + - UI_PORT=8080 + + rosbridge: + image: husarion/rosbridge-server:humble + ports: + - 9090:9090 + command: ros2 launch rosbridge_server rosbridge_websocket_launch.xml +``` + +And execute in the terminal: + +``` +docker compose up +``` + +Access the robot user interface at: + +http://localhost:8080/ui + +This address automatically redirects to the URL using `DS_PORT` and `DS_TYPE` envs to connect to the right data source without setting it manually in the Foxglove UI. + + diff --git a/caddy_entrypoint.sh b/caddy_entrypoint.sh deleted file mode 100755 index 07f5ca3..0000000 --- a/caddy_entrypoint.sh +++ /dev/null @@ -1,36 +0,0 @@ -#!/bin/sh -set -e - -# # check if ROSBOT_ADDR is an IPv4 address -# if [[ $ROSBOT_ADDR =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then -# ROSBOT_IP=$ROSBOT_ADDR -# # check if ROSBOT_ADDR is an IPv6 address -# elif [[ $ROSBOT_ADDR =~ ^[0-9a-fA-F:]+$ ]]; then -# ROSBOT_IP="[$ROSBOT_ADDR]" -# # ROSBOT_ADDR is not an IP address, look it up in /etc/hosts file -# else -# # try to find the IP address associated with ROSBOT_ADDR and the "# managed by Husarnet" comment -# ROSBOT_IP="["$(grep -E "$ROSBOT_ADDR.*# managed by Husarnet$" /etc/hosts | awk '{ print $1 }')"]" - -# # if no match is found, try to find any IP address associated with ROSBOT_ADDR -# if [[ "$ROSBOT_IP" == "[]" ]]; then -# ROSBOT_IP=""$(grep -m 1 -E "$ROSBOT_ADDR" /etc/hosts | awk '{ print $1 }')"" -# fi -# fi - -# # check if ROSBOT_IP is empty -# if [ -z "$ROSBOT_IP" ]; then -# echo "ERROR: ROSBOT_ADDR not found in /etc/hosts" -# exit 1 -# fi - -# echo "rosbot IP is: ${ROSBOT_IP}" - -# # replace file:///ros2_ws with http://ROSBOT_IP:FOXGLOVE_PORT/ros2_ws in /src/rosbot_xl.urdf and /src/rosbot.urdf files -# sed -i "s|file:///ros2_ws|http://$ROSBOT_IP:$FOXGLOVE_PORT/ros2_ws|g" /src/rosbot_xl.urdf /src/rosbot.urdf - -# # replace localhost:8080 with ROSBOT_IP:FOXGLOVE_PORT in /src/FoxgloveDefaultLayout.json file -# sed -i "s|localhost:8080|$ROSBOT_IP:$FOXGLOVE_PORT|g" /src/FoxgloveDefaultLayout.json - -# run the command passed as arguments to the script -exec "$@" diff --git a/compose.yaml b/compose.yaml index 274356e..1a45ec4 100644 --- a/compose.yaml +++ b/compose.yaml @@ -1,17 +1,17 @@ services: foxglove: - build: - dockerfile: Dockerfile.foxglove + build: . network_mode: host - environment: - - FOXGLOVE_PORT=8080 - - ROSBRIDGE_PORT=9090 # ports: # - 8080:8080 + environment: + - DS_TYPE=rosbridge-websocket + - DS_PORT=9090 + - UI_PORT=8080 rosbridge: image: husarion/rosbridge-server:humble network_mode: host # ports: # - 9090:9090 - command: ros2 launch rosbridge_server rosbridge_websocket_launch.xml \ No newline at end of file + command: ros2 launch rosbridge_server rosbridge_websocket_launch.xml diff --git a/default-url.txt b/default-url.txt deleted file mode 100644 index ccc212c..0000000 --- a/default-url.txt +++ /dev/null @@ -1 +0,0 @@ -ws://{{.Host}}:{{env "ROSBRIDGE_PORT"}} \ No newline at end of file diff --git a/demo/compose.yaml b/demo/compose.yaml index 213c08e..04d985d 100644 --- a/demo/compose.yaml +++ b/demo/compose.yaml @@ -1,15 +1,17 @@ services: foxglove: - image: husarion/foxglove:humble - volumes: - - ./RosbotTeleop.json:/src/FoxgloveDefaultLayout.json - environment: - - FOXGLOVE_PORT=8080 + image: husarion/foxglove:v1.72.0 ports: - 8080:8080 + volumes: + - ./RosbotTeleop.json:/foxglove/default-layout.json + environment: + - DS_TYPE=rosbridge-websocket + - DS_PORT=9090 + - UI_PORT=8080 rosbridge: image: husarion/rosbridge-server:humble ports: - 9090:9090 - command: ros2 launch rosbridge_server rosbridge_websocket_launch.xml \ No newline at end of file + command: ros2 launch rosbridge_server rosbridge_websocket_launch.xml diff --git a/entrypoint.sh b/entrypoint.sh new file mode 100755 index 0000000..ef7e582 --- /dev/null +++ b/entrypoint.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +# replace localhost:8080 with {{.Host}}:UI_PORT in /foxglove/default-layout.json file +sed -i 's|localhost:8080|{{.Host}}:{{env "UI_PORT"}}|g' /foxglove/default-layout.json + +# Optionally override the default layout with one provided via bind mount +index_html=$(cat index.html) +replace_pattern='/*FOXGLOVE_STUDIO_DEFAULT_LAYOUT_PLACEHOLDER*/' +replace_value=$(cat /foxglove/default-layout.json) +echo "${index_html/"$replace_pattern"/$replace_value}" > index.html + +# Continue executing the CMD +exec "$@" \ No newline at end of file diff --git a/studio-files/Default.json b/studio-files/Default.json deleted file mode 100644 index 2c774f9..0000000 --- a/studio-files/Default.json +++ /dev/null @@ -1,104 +0,0 @@ -{ - "configById": { - "3D!18i6zy7": { - "layers": { - "845139cb-26bc-40b3-8161-8ab60af4baf5": { - "visible": true, - "frameLocked": true, - "label": "Grid", - "instanceId": "845139cb-26bc-40b3-8161-8ab60af4baf5", - "layerId": "foxglove.Grid", - "size": 10, - "divisions": 10, - "lineWidth": 1, - "color": "#248eff", - "position": [ - 0, - 0, - 0 - ], - "rotation": [ - 0, - 0, - 0 - ], - "order": 1 - } - }, - "cameraState": { - "distance": 20, - "perspective": true, - "phi": 60, - "target": [ - 0, - 0, - 0 - ], - "targetOffset": [ - 0, - 0, - 0 - ], - "targetOrientation": [ - 0, - 0, - 0, - 1 - ], - "thetaOffset": 45, - "fovy": 45, - "near": 0.5, - "far": 5000 - }, - "followMode": "follow-pose", - "scene": {}, - "transforms": {}, - "topics": {}, - "publish": { - "type": "point", - "poseTopic": "/move_base_simple/goal", - "pointTopic": "/clicked_point", - "poseEstimateTopic": "/initialpose", - "poseEstimateXDeviation": 0.5, - "poseEstimateYDeviation": 0.5, - "poseEstimateThetaDeviation": 0.26179939 - } - }, - "RawMessages!os6rgs": { - "diffEnabled": false, - "diffMethod": "custom", - "diffTopicPath": "", - "showFullMessageForDiff": false, - "topicPath": "" - }, - "ImageViewPanel!3mnp456": { - "cameraTopic": "", - "enabledMarkerTopics": [], - "mode": "fit", - "pan": { - "x": 0, - "y": 0 - }, - "rotation": 0, - "synchronize": false, - "transformMarkers": false, - "zoom": 1 - } - }, - "globalVariables": {}, - "userNodes": {}, - "playbackConfig": { - "speed": 1 - }, - "layout": { - "first": "3D!18i6zy7", - "second": { - "first": "ImageViewPanel!3mnp456", - "second": "RawMessages!os6rgs", - "direction": "column", - "splitPercentage": 30 - }, - "direction": "row", - "splitPercentage": 70 - } -} diff --git a/studio-files/Root.tsx b/studio-files/Root.tsx deleted file mode 100644 index b63e3d7..0000000 --- a/studio-files/Root.tsx +++ /dev/null @@ -1,79 +0,0 @@ -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at http://mozilla.org/MPL/2.0/ - -import { useMemo, useState } from "react"; - -import { - IDataSourceFactory, - // Ros1LocalBagDataSourceFactory, - // Ros2LocalBagDataSourceFactory, - RosbridgeDataSourceFactory, - // RemoteDataSourceFactory, - // FoxgloveDataPlatformDataSourceFactory, - // FoxgloveWebSocketDataSourceFactory, - // UlogLocalDataSourceFactory, - // McapLocalDataSourceFactory, - // SampleNuscenesDataSourceFactory, - IAppConfiguration, - IdbExtensionLoader, - App, - ConsoleApi, -} from "@foxglove/studio-base"; - -// import Ros1UnavailableDataSourceFactory from "./dataSources/Ros1UnavailableDataSourceFactory"; -// import Ros2UnavailableDataSourceFactory from "./dataSources/Ros2UnavailableDataSourceFactory"; -// import VelodyneUnavailableDataSourceFactory from "./dataSources/VelodyneUnavailableDataSourceFactory"; -import { IdbLayoutStorage } from "./services/IdbLayoutStorage"; - -export function Root({ appConfiguration }: { appConfiguration: IAppConfiguration }): JSX.Element { - const layoutStorage = useMemo(() => new IdbLayoutStorage(), []); - const [extensionLoaders] = useState(() => [ - new IdbExtensionLoader("org"), - new IdbExtensionLoader("local"), - ]); - const consoleApi = useMemo(() => new ConsoleApi(process.env.FOXGLOVE_API_URL ?? ""), []); - - const dataSources: IDataSourceFactory[] = useMemo(() => { - const sources = [ - // new Ros1UnavailableDataSourceFactory(), - // new Ros1LocalBagDataSourceFactory(), - // new Ros2UnavailableDataSourceFactory(), - // new Ros2LocalBagDataSourceFactory(), - // new FoxgloveWebSocketDataSourceFactory(), - new RosbridgeDataSourceFactory(), - // new UlogLocalDataSourceFactory(), - // new VelodyneUnavailableDataSourceFactory(), - // new FoxgloveDataPlatformDataSourceFactory(consoleApi), - // new SampleNuscenesDataSourceFactory(), - // new McapLocalDataSourceFactory(), - // new RemoteDataSourceFactory(), - ]; - - return sources; - }, [consoleApi]); - - // Enable dialog auth in development since using cookie auth does not work between - // localhost and the hosted dev deployment due to browser cookie/host security. - const enableDialogAuth = - process.env.NODE_ENV === "development" || process.env.FOXGLOVE_ENABLE_DIALOG_AUTH != undefined; - - const disableSignin = process.env.FOXGLOVE_DISABLE_SIGN_IN != undefined; - - return ( - <> - - - ); -} diff --git a/studio-files/RosbridgeDataSourceFactory.ts b/studio-files/RosbridgeDataSourceFactory.ts deleted file mode 100644 index e5caf2c..0000000 --- a/studio-files/RosbridgeDataSourceFactory.ts +++ /dev/null @@ -1,64 +0,0 @@ -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at http://mozilla.org/MPL/2.0/ - -import { - IDataSourceFactory, - DataSourceFactoryInitializeArgs, -} from "@foxglove/studio-base/context/PlayerSelectionContext"; -import RosbridgePlayer from "@foxglove/studio-base/players/RosbridgePlayer"; -import { Player } from "@foxglove/studio-base/players/types"; - -let myurl: string; - -fetch("/default-url.txt") - .then(async (response) => await response.text()) - .then((defaultUrl) => { - if (defaultUrl) { - myurl = defaultUrl; - } - }) - .catch((error) => { - console.error("Error fetching default WebSocket URL:", error); - }); - -class RosbridgeDataSourceFactory implements IDataSourceFactory { - public id = "rosbridge-websocket"; - public type: IDataSourceFactory["type"] = "connection"; - public displayName = "Rosbridge"; - public iconName: IDataSourceFactory["iconName"] = "Flow"; - public docsLinks = [{ url: "https://foxglove.dev/docs/studio/connection/rosbridge" }]; - public description = "Connect to a ROS 1 or ROS 2 system using the Rosbridge WebSocket protocol."; - - public formConfig = { - fields: [ - { - id: "url", - label: "WebSocket URL", - defaultValue: myurl, - validate: (newValue: string): Error | undefined => { - try { - const url = new URL(newValue); - if (url.protocol !== "ws:" && url.protocol !== "wss:") { - return new Error(`Invalid protocol: ${url.protocol}`); - } - return undefined; - } catch (err) { - return new Error("Enter a valid url"); - } - }, - }, - ], - }; - - public initialize(args: DataSourceFactoryInitializeArgs): Player | undefined { - const url = args.params?.url; - if (!url) { - return; - } - - return new RosbridgePlayer({ url, metricsCollector: args.metricsCollector, sourceId: this.id }); - } -} - -export default RosbridgeDataSourceFactory; diff --git a/studio-files/Start.tsx b/studio-files/Start.tsx deleted file mode 100644 index 2794574..0000000 --- a/studio-files/Start.tsx +++ /dev/null @@ -1,229 +0,0 @@ -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at http://mozilla.org/MPL/2.0/ - -import { Button, Link, List, ListItem, ListItemButton, SvgIcon, Typography } from "@mui/material"; -import { useMemo } from "react"; -import tinycolor from "tinycolor2"; -import { makeStyles } from "tss-react/mui"; - -import FoxgloveLogoText from "@foxglove/studio-base/components/FoxgloveLogoText"; -import Stack from "@foxglove/studio-base/components/Stack"; -import TextMiddleTruncate from "@foxglove/studio-base/components/TextMiddleTruncate"; -import { useAnalytics } from "@foxglove/studio-base/context/AnalyticsContext"; -import { usePlayerSelection } from "@foxglove/studio-base/context/PlayerSelectionContext"; -import { AppEvent } from "@foxglove/studio-base/services/IAnalytics"; - -import { OpenDialogViews } from "./types"; - -export type IStartProps = { - onSelectView: (newValue: OpenDialogViews) => void; -}; - -const useStyles = makeStyles()((theme) => ({ - logo: { - width: 212, - height: "auto", - marginLeft: theme.spacing(-1), - }, - grid: { - [theme.breakpoints.up("md")]: { - display: "grid", - gridTemplateAreas: ` - "header spacer" - "content sidebar" - `, - gridTemplateRows: `content auto`, - gridTemplateColumns: `1fr 375px`, - }, - }, - header: { - padding: theme.spacing(6), - gridArea: "header", - - [theme.breakpoints.down("md")]: { - padding: theme.spacing(4), - }, - [`@media (max-height: ${theme.breakpoints.values.sm})`]: { - display: "none", - }, - }, - content: { - padding: theme.spacing(0, 6, 6), - overflow: "hidden", - gridArea: "content", - - [theme.breakpoints.down("md")]: { - padding: theme.spacing(0, 4, 4), - }, - [`@media (max-height: ${theme.breakpoints.values.sm})`]: { - paddingTop: theme.spacing(6), - }, - }, - spacer: { - gridArea: "spacer", - backgroundColor: tinycolor(theme.palette.text.primary).setAlpha(0.04).toRgbString(), - - [`@media (max-height: ${theme.breakpoints.values.sm})`]: { - display: "none", - }, - }, - sidebar: { - gridArea: "sidebar", - backgroundColor: tinycolor(theme.palette.text.primary).setAlpha(0.04).toRgbString(), - padding: theme.spacing(0, 5, 5), - - [theme.breakpoints.down("md")]: { - padding: theme.spacing(4), - }, - [`@media (max-height: ${theme.breakpoints.values.sm})`]: { - paddingTop: theme.spacing(6), - }, - }, - button: { - whiteSpace: "nowrap", - textOverflow: "ellipsis", - overflow: "hidden", - }, - connectionButton: { - textAlign: "left", - justifyContent: "flex-start", - padding: theme.spacing(2, 3), - gap: theme.spacing(1.5), - borderColor: theme.palette.divider, - - ".MuiButton-startIcon .MuiSvgIcon-fontSizeLarge": { - fontSize: 28, - }, - }, - recentListItemButton: { - overflow: "hidden", - color: theme.palette.primary.main, - - "&:hover": { - backgroundColor: "transparent", - color: theme.palette.primary[theme.palette.mode === "dark" ? "light" : "dark"], - }, - }, - recentSourceSecondary: { - color: "inherit", - }, - accountList: { padding: "0px" }, - accountListItem: { margin: "0px 0px 5px 10px" }, -})); - -type DataSourceOptionProps = { - text: string; - secondaryText: string; - icon: JSX.Element; - onClick: () => void; - href?: string; -}; - -function DataSourceOption(props: DataSourceOptionProps): JSX.Element { - const { icon, onClick, text, secondaryText, href } = props; - const { classes } = useStyles(); - const button = ( - - ); - - return href ? ( - - {button} - - ) : ( - button - ); -} - -export default function Start(props: IStartProps): JSX.Element { - const { onSelectView } = props; - const { recentSources, selectRecent } = usePlayerSelection(); - const { classes } = useStyles(); - const analytics = useAnalytics(); - - const startItems = useMemo(() => { - return [ - { - key: "open-connection", - text: "Open connection", - secondaryText: "Connect to a live robot or server.", - icon: ( - - - - ), - onClick: () => { - onSelectView("connection"); - void analytics.logEvent(AppEvent.DIALOG_SELECT_VIEW, { type: "live" }); - }, - }, - ]; - }, [analytics, onSelectView]); - - return ( - -
- -
- - - - - Open data source - - {startItems.map((item) => ( - - ))} - - {recentSources.length > 0 && ( - - - Recent data sources - - - {recentSources.slice(0, 5).map((recent) => ( - - selectRecent(recent.id)} - className={classes.recentListItemButton} - > - - - - ))} - - - )} - - -
- ); -} diff --git a/studio-files/defaultLayout.ts b/studio-files/defaultLayout.ts deleted file mode 100644 index 5fd5031..0000000 --- a/studio-files/defaultLayout.ts +++ /dev/null @@ -1,28 +0,0 @@ -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at http://mozilla.org/MPL/2.0/ - -// import { LayoutData } from "@foxglove/studio-base/context/CurrentLayoutContext/actions"; -// import { defaultPlaybackConfig } from "@foxglove/studio-base/providers/CurrentLayoutProvider/reducers"; - -// /** -// * This is loaded when the user has no layout selected on application launch -// * to avoid presenting the user with a blank layout. -// */ - -import { LayoutData } from "@foxglove/studio-base/context/CurrentLayoutContext/actions"; - -const fetchJSON = (url: string): Promise => { - return fetch(url, { mode: 'no-cors'}) - .then(response => response.json()) - .catch(error => error); -}; - -let url = "/FoxgloveDefaultLayout.json?timestamp=" + Date.now(); -let defaultLayout: LayoutData; -fetchJSON(url) - .then(data => { - defaultLayout = data; - }); - -export { defaultLayout };