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 };