diff --git a/docker/DOCKER.rst b/docker/DOCKER.rst index bd335af3..71811457 100644 --- a/docker/DOCKER.rst +++ b/docker/DOCKER.rst @@ -1,5 +1,5 @@ Docker build contexts -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ In the ``docker/`` folder you can find a collection of directories roughly corresponding to :term:`Docker` build contexts, and a number of @@ -8,3 +8,101 @@ case of multi-stage builds multiple service images can be created from the same build context. The ``docker/Makefile`` may define additional phony targets for commonly needed tasks such as exposing the X server to containers that have a :term:`GUI` component. + +External interfaces +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The |Docker Compose file|_ defines all services used to support GISNav deployments. +The diagram below describes the system architecture through the external +interfaces and Docker bridge networks shared between the Docker Compose services. + +The Docker bridge networks have in-built DNS which means the container names +resolve to their respective IP addresses. The container name will equal the +service name prefixed with ``gisnav-`` and suffixed with ``-1``. So for example +deploying the ``mapserver`` service using the Compose file will start a +Docker container with the hostname ``gisnav-mapserver-1``. + +.. todo:: + * Split mavlink network into mavlink and ROS networks. For ROS the intention + is to use the shared memory device instead of serializing and going through + the network stack since we will be passing a lot of images around. + * Build and expose static docs to home page - possibly no need for a server + +.. note:: + The application services have access to both ``gis`` and ``mavlink`` networks. + +.. mermaid:: + + graph TD + subgraph mavlink ["mavlink"] + mavlink_qgc[qgc] + subgraph simulation ["Simulation Services"] + simulation_px4[px4] + simulation_ardupilot[ardupilot] + end + subgraph middleware ["Middleware Services"] + middleware_mavros[mavros] + middleware_micro_ros_agent[micro-ros-agent] + middleware_gscam[gscam] + end + end + + subgraph gis_mavlink ["gis & mavlink"] + subgraph application ["Application Services"] + application_gisnav[gisnav] + application_autoheal[autoheal] + end + end + subgraph gis ["gis"] + subgraph gis_services ["GIS Services"] + gis_mapserver[mapserver] + gis_qgis[qgis] + end + subgraph data_services ["Data Services"] + gis_postgres[postgres] + end + end + + subgraph admin ["admin"] + homepage[homepage] + fileserver[fileserver] + end + + subgraph volumes ["User managed\nshared volumes"] + gscam_volume[gscam-volume] + gis_maps_volume[maps-volume] + application_gisnav_volume[gisnav-volume] + end + application_docs_volume[docs-volume] + + mavlink_qgc -->|14550/udp\nMAVLink| simulation_px4 + simulation_px4 -->|14540/udp\nMAVLink| middleware_mavros + simulation_px4 -->|8888/udp\nDDS-XRCE | middleware_micro_ros_agent + simulation_px4 -->|5600/udp\nRTP H.264 Video| middleware_gscam + middleware_mavros -->|/dev/shm\nROS 2 Fast DDS| application_gisnav + middleware_micro_ros_agent -->|/dev/shm\nROS 2 Fast DDS| application_gisnav + middleware_gscam -->|/dev/shm\nROS 2 Fast DDS| application_gisnav + application_gisnav -->|5432/tcp| gis_postgres + + application_gisnav -->|80/tcp\nHTTP WMS| gis_mapserver + gis_mapserver -->|80/tcp\nHTTP WMS| gis_qgis + gis_qgis -->|5432/tcp| gis_postgres + gis_mapserver ---|/etc/mapserver| gis_maps_volume + application_gisnav_volume ---|/etc/gisnav| application_gisnav + application_docs_volume ---|/path/to/built/docs| application_gisnav + homepage ---|3000/tcp| fileserver + fileserver ---|"/var/www/filegator/"| volumes + gscam_volume ---|/etc/gscam| middleware_gscam + + application_docs_volume ---|/path/to/docs:ro| homepage + + subgraph host ["host"] + monitoring["monitoring"] + docker_host["docker host"] + end + + monitoring ---|61208/tcp| homepage + docker_host ---|/var/run/docker.sock| monitoring + + classDef network fill:transparent,stroke-dasharray:5 5; + class mavlink,gis,gis_mavlink,admin,admin_gis,host network diff --git a/docker/apache/Dockerfile b/docker/apache/Dockerfile index 98e7954a..d86bed3b 100644 --- a/docker/apache/Dockerfile +++ b/docker/apache/Dockerfile @@ -1,5 +1,7 @@ FROM ubuntu:jammy AS apache +LABEL maintainer="Harri Makelin " + # Update apt repository and install necessary packages RUN apt-get update && apt-get install -y \ cgi-mapserver \ @@ -125,6 +127,7 @@ RUN mkdir -p /var/www/ && \ cp configuration_sample.php configuration.php && \ sed -i "s/'app_name' => 'FileGator'/'app_name' => 'GISNav'/g" configuration.php && \ sed -i "s|'logo' => 'https://filegator.io/filegator_logo.svg'|'logo' => 'img/logo-no-background.svg'|g" configuration.php && \ + sed -i "/'editable' => \[/ s/]/, '.yml', '.yaml'&/" configuration.php && \ sed -i 's/100 \* 1024 \* 1024/2 \* 1024 \* 1024 \* 1024/g' configuration.php && \ mkdir -p dist/img && \ cp /logo-no-background.svg dist/img && \ diff --git a/docker/apache/fileserver/entrypoint.sh b/docker/apache/fileserver/entrypoint.sh index 08a7426b..11dae134 100644 --- a/docker/apache/fileserver/entrypoint.sh +++ b/docker/apache/fileserver/entrypoint.sh @@ -7,4 +7,8 @@ rm -f /var/run/apache2/cgisock.* apache2ctl -D FOREGROUND & +# Make sure we can edit files on mapped volumes +chown -R www-data:www-data /var/www/filegator/repository +chmod -R 775 /var/www/filegator/repository + exec "$@" diff --git a/docker/apache/mapserver/entrypoint.sh b/docker/apache/mapserver/entrypoint.sh index cb49ab18..f45890a7 100644 --- a/docker/apache/mapserver/entrypoint.sh +++ b/docker/apache/mapserver/entrypoint.sh @@ -49,6 +49,8 @@ process_directory_change() { fi fi + sleep 0.5 + if [[ "$BASENAME" =~ \.(tif|tiff|jp2|ecw|img)$ ]]; then echo "Raster file detected, regenerating VRT." cd /etc/mapserver && gdalbuildvrt "$VRT_FILE" "$DIR_PATH"/*.tif "$DIR_PATH"/*.tiff "$DIR_PATH"/*.jp2 "$DIR_PATH"/*.ecw "$DIR_PATH"/*.img @@ -69,7 +71,7 @@ done & # Move demo imagery and DEM over to shared volume if still on image # This should trigger the inotify script -mv /etc/mapserver/$NAIP_ZIP_FILENAME $IMAGERY_DIR | echo "NAIP imagery not found on container - likely already moved to shared volume" -mv /etc/mapserver/$DEM_FILENAME $DEM_DIR | echo "USGS DEM not found on container - likely already moved to shared volume" +mv /etc/mapserver/$NAIP_ZIP_FILENAME $IMAGERY_DIR || echo "NAIP imagery not found on container - likely already moved to shared volume" +mv /etc/mapserver/$DEM_FILENAME $DEM_DIR || echo "USGS DEM not found on container - likely already moved to shared volume" exec "$@" diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml index 6e98d38d..1dc827ec 100644 --- a/docker/docker-compose.yaml +++ b/docker/docker-compose.yaml @@ -7,6 +7,9 @@ networks: mavlink: driver: bridge attachable: true + admin: + driver: bridge + attachable: true x-mavlink-net: &mavlink-net networks: @@ -16,13 +19,21 @@ x-gis-net: &gis-net networks: - gis +x-admin-net: &admin-net + networks: + - gis + - admin + x-all-net: &all-net networks: - mavlink - gis + # platforms is a sequence not a mapping so careful when merging # with something that defines its own platforms +# TODO: use a more restricted solution than exposing the docker socket +# directly for monitoring services x-base: &base build: dockerfile: Dockerfile @@ -53,7 +64,7 @@ x-x11-volumes: &x11-volumes # This is split into sub-extensions because downloader needs the environment # but also needs to extend volumes and YAML does not support merging sequences. x-x11: &x11 - <<: [*x11-environment, *x11-volumes] + <<: [ *x11-environment, *x11-volumes ] x-ros: &ros environment: @@ -74,12 +85,20 @@ services: command: apache2ctl -D FOREGROUND volumes: - maps-volume:/etc/mapserver/maps # TODO: make read only? copy files and have user delete duplicates from their own volume? + labels: + - homepage.group=Core services + - homepage.name=MapServer + - homepage.description=MapServer GIS service micro-ros-agent: <<: [*base, *ros, *mavlink-net] build: context: micro-ros-agent command: udp4 -p 8888 + labels: + - homepage.group=Core services + - homepage.name=micro-ROS agent + - homepage.description=uORB to ROS (PX4) middleware mavros: <<: [*base, *ros, *mavlink-net] @@ -89,6 +108,10 @@ services: args: ROS_VERSION: humble command: ros2 run mavros mavros_node --ros-args --param fcu_url:=udp://:14540@localhost:14557 + labels: + - homepage.group=Core services + - homepage.name=MAVROS + - homepage.description=MAVLink to ROS middleware qgc: <<: [*base, *x11, *mavlink-net] @@ -99,6 +122,10 @@ services: - /dev/shm:/dev/shm - /dev/dri:/dev/dri privileged: true + labels: + - homepage.group=Core services + - homepage.name=QGroundControl + - homepage.description=Ground control software with GUI for controlling the vehicle # Note: build context is repository root # Note 2: The "docker buildx build" command in the push_gisnav_images.yml workflow @@ -115,6 +142,11 @@ services: target: gisnav args: ROS_VERSION: humble + volumes: + - /dev/shm:/dev/shm + - /tmp/.X11-unix:/tmp/.X11-unix:ro + - /dev/dri:/dev/dri # TODO: make volumes merging work with ancors (x11, ros, etc?) + - gisnav-volume:/etc/gisnav command: ros2 launch gisnav px4.dev.launch.py depends_on: - px4 @@ -122,6 +154,10 @@ services: - micro-ros-agent - gscam - mapserver + labels: + - homepage.group=Core services + - homepage.name=GISNav + - homepage.description=Generates mock GPS messages for FCU using visual map-based navigation # The px4 service depends on mavros, mapserver and micro-ros-agent because # their IP addresses are used in the px4 service entrypoint.sh script @@ -138,6 +174,10 @@ services: - mavros - micro-ros-agent - qgc + labels: + - homepage.group=Development services + - homepage.name=PX4 + - homepage.description=PX4 simulation and firmware tool ardupilot: <<: [*base, *x11, *nvidia-gpu, *mavlink-net] @@ -149,6 +189,10 @@ services: - /dev/dri:/dev/dri privileged: True command: bash -c "cd ~ && make sim_vehicle" + labels: + - homepage.group=Development services + - homepage.name=ArduPilot + - homepage.description=ArduPilot simulation and firmware tool rviz: <<: [*base, *x11, *ros, *nvidia-gpu, *mavlink-net] @@ -157,6 +201,10 @@ services: args: ROS_VERSION: humble command: rviz2 -d gisnav_config.rviz + labels: + - homepage.group=Development services + - homepage.name=RViz + - homepage.description=ROS visualization tool gscam: <<: [*base, *ros, *mavlink-net] @@ -164,6 +212,9 @@ services: context: gscam args: ROS_VERSION: humble + volumes: + - /dev/shm:/dev/shm # todo: make work with *ros anchor + - gscam-volume:/etc/gscam command: - "ros2" - "run" @@ -171,9 +222,13 @@ services: - "gscam_node" - "--ros-args" - "--params-file" - - "gscam_params.yaml" + - "/etc/gscam/gscam_params.yaml" - "-p" - - "camera_info_url:=file:///camera_calibration.yaml" + - "camera_info_url:=file:///etc/gscam/camera_calibration.yaml" + labels: + - homepage.group=Core services + - homepage.name=gscam + - homepage.description=GStreamer camera middleware autoheal: <<: [*base, *all-net] @@ -183,6 +238,10 @@ services: AUTOHEAL_CONTAINER_LABEL: all volumes: - /var/run/docker.sock:/var/run/docker.sock + labels: + - homepage.group=Core services + - homepage.name=Autoheal + - homepage.description=Monitors and maintains health of other services. qgis: <<: [*base, *x11, *gis-net] @@ -192,6 +251,10 @@ services: depends_on: - postgres - mapserver + labels: + - homepage.group=Development services + - homepage.name=QGIS + - homepage.description=GIS client for viewing map rasters postgres: <<: [*base, *gis-net] @@ -201,16 +264,63 @@ services: POSTGRES_DB: gisnav POSTGRES_USER: gisnav POSTGRES_PASSWORD: gisnav + labels: + - homepage.group=Development services + - homepage.name=Postgres + - homepage.description=PostGIS relational database for efficiently storing geographical information + # todo: open web browser from homepage service, not from here fileserver: <<: [*base, *x11-environment, *nvidia-gpu, *gis-net] build: context: apache target: fileserver - command: firefox "http://gisnav-fileserver-1" + command: firefox "http://gisnav-homepage-1:3000" volumes: - /tmp/.X11-unix:/tmp/.X11-unix # TODO: merge x-11-volumes? - - maps-volume:/var/www/filegator/repository + - maps-volume:/var/www/filegator/repository/mapserver + - gscam-volume:/var/www/filegator/repository/gscam + - gisnav-volume:/var/www/filegator/repository/gisnav + labels: + - homepage.group=Admin services + - homepage.name=FileGator + - homepage.description=File manager for uploading and removing orthoimagery and DEMs and editing ROS configuration files + - homepage.href=http://gisnav-fileserver-1 + + # Give this thing access to all networks to see docker container status? + # extra_hosts with docker host needed for monitoring service + homepage: + <<: [*base, *x11-environment, *nvidia-gpu, *admin-net] + build: + context: homepage + labels: + - homepage.group=Admin services + - homepage.name=Homepage + - homepage.description=Administration dashboard + depends_on: + - fileserver # depends on fileserver bc currently fileserver opens the web browser + - monitoring + extra_hosts: + - "host.docker.internal:host-gateway" + volumes: + - /var/run/docker.sock:/var/run/docker.sock:ro # read-only for safety - admin dashboard uses this + + monitoring: + <<: [ *base, *x11-environment, *nvidia-gpu ] #, *admin-net ] + image: nicolargo/glances:latest-full + pid: host + network_mode: host + volumes: + - /var/run/docker.sock:/var/run/docker.sock + - /run/user/1000/podman/podman.sock:/run/user/1000/podman/podman.sock + environment: + - "GLANCES_OPT=-w" + labels: + - homepage.group=Admin services + - homepage.name=Glances + - homepage.description=System monitor volumes: maps-volume: + gscam-volume: + gisnav-volume: diff --git a/docker/gscam/entrypoint.sh b/docker/gscam/entrypoint.sh index 3f4436fb..597bc0be 100644 --- a/docker/gscam/entrypoint.sh +++ b/docker/gscam/entrypoint.sh @@ -3,4 +3,8 @@ set -e source "/opt/ros/$ROS_VERSION/setup.bash" +# Move config files to shared volume if not yet done +mv /gscam_params.yaml /etc/gscam || echo "INFO: gscam_params.yaml not found on container - likely already moved to shared volume" +mv /camera_calibration.yaml /etc/gscam || echo "INFO: camera_calibration.yaml on container - likely already moved to shared volume" + exec "$@" diff --git a/docker/homepage/Dockerfile b/docker/homepage/Dockerfile new file mode 100644 index 00000000..815ae5dd --- /dev/null +++ b/docker/homepage/Dockerfile @@ -0,0 +1,6 @@ +FROM ghcr.io/gethomepage/homepage:latest + +LABEL maintainer="Harri Makelin " + +COPY config/ /app/config/ +COPY logo-no-background.svg gisnav-website-favicon-color.png /app/public/images/ diff --git a/docker/homepage/config/bookmarks.yaml b/docker/homepage/config/bookmarks.yaml new file mode 100644 index 00000000..f4445be1 --- /dev/null +++ b/docker/homepage/config/bookmarks.yaml @@ -0,0 +1,7 @@ +- Information: + - GitHub repository: + - abbr: GH + href: https://github.com/hmakelin/gisnav + - Developer documentation: + - abbr: Docs + href: https://gisnav.org diff --git a/docker/homepage/config/docker.yaml b/docker/homepage/config/docker.yaml new file mode 100644 index 00000000..af7e9dfc --- /dev/null +++ b/docker/homepage/config/docker.yaml @@ -0,0 +1,2 @@ +docker-host: + socket: /var/run/docker.sock diff --git a/docker/homepage/config/services.yaml b/docker/homepage/config/services.yaml new file mode 100644 index 00000000..6c852790 --- /dev/null +++ b/docker/homepage/config/services.yaml @@ -0,0 +1,13 @@ +- Download maps: + - EarthExplorer: + href: https://earthexplorer.usgs.gov/ + description: Public domain high-resolution aerial imagery from United States Geological Survey (USGS) + - MapSite: + href: https://asiointi.maanmittauslaitos.fi/karttapaikka/ + description: Public domain high-resolution aerial imagery from Finnish National Land Survey (NLS) + - ArcGIS Online: + href: https://www.arcgis.com/index.html + description: Commercial imagery + - Maxar: + href: https://maxar.com/maxar-intelligence/products/satellite-imagery + description: Commercial high-resolution satellite imagery diff --git a/docker/homepage/config/settings.yaml b/docker/homepage/config/settings.yaml new file mode 100644 index 00000000..1aba1d61 --- /dev/null +++ b/docker/homepage/config/settings.yaml @@ -0,0 +1,18 @@ +title: GISNav home +language: en +#background: images/logo-no-background.svg +favicon: images/gisnav-website-favicon-color.png +showStats: true +layout: + Admin services: + icon: mdi-cog + Download maps: + icon: mdi-map-plus + Core services: + icon: mdi-shield-alert + style: row + columns: 4 + Development services: + icon: mdi-code-braces + style: row + columns: 4 diff --git a/docker/homepage/config/widgets.yaml b/docker/homepage/config/widgets.yaml new file mode 100644 index 00000000..94440373 --- /dev/null +++ b/docker/homepage/config/widgets.yaml @@ -0,0 +1,11 @@ +- greeting: + text_size: xl + text: GISNav +- glances: + url: http://host.docker.internal:61208 + cpu: true # optional, enabled by default, disable by setting to false + mem: true # optional, enabled by default, disable by setting to false + cputemp: true # disabled by default + uptime: true # disabled by default + disk: / # disabled by default, use mount point of disk(s) in glances. Can also be a list (see below) + expanded: true # show the expanded view diff --git a/docker/homepage/gisnav-website-favicon-color.png b/docker/homepage/gisnav-website-favicon-color.png new file mode 100644 index 00000000..dd4fc6bc Binary files /dev/null and b/docker/homepage/gisnav-website-favicon-color.png differ diff --git a/docker/homepage/logo-no-background.svg b/docker/homepage/logo-no-background.svg new file mode 100644 index 00000000..48a62236 --- /dev/null +++ b/docker/homepage/logo-no-background.svg @@ -0,0 +1 @@ + diff --git a/docker/mavros/Dockerfile b/docker/mavros/Dockerfile index 1ad5b685..670c7760 100644 --- a/docker/mavros/Dockerfile +++ b/docker/mavros/Dockerfile @@ -80,14 +80,15 @@ RUN git clone --branch release/1.14 https://github.com/px4/px4_msgs.git && \ colcon build --packages-select px4_msgs # Copy rest of gisnav ros2 package -COPY gisnav/ gisnav/ +COPY . gisnav/ WORKDIR /opt/colcon_ws/src/gisnav # Install python dependencies, mainly torch and related CUDA deps # Finally, build gisnav -RUN pip install . .[mock_gps_node] .[qgis_node] .[dev] && \ - cd ../.. && \ +RUN cd gisnav && \ + pip install . .[mock_gps_node] .[qgis_node] .[dev] && \ + cd /opt/colcon_ws && \ source /opt/ros/${ROS_VERSION}/setup.bash && \ source install/setup.bash && \ colcon build --packages-select gisnav diff --git a/docker/mavros/gisnav/entrypoint.sh b/docker/mavros/gisnav/entrypoint.sh index 388f0d6f..3a448930 100644 --- a/docker/mavros/gisnav/entrypoint.sh +++ b/docker/mavros/gisnav/entrypoint.sh @@ -7,4 +7,44 @@ source "/opt/colcon_ws/install/setup.bash" -- # Needed for pip installed dev tools like pre-commit and sphinx-build export PATH=/usr/lib/gisnav:$PATH +# Move .yaml param files to shared volume so that they can be edited via +# file manager: +# Source directory where the original .yaml files are located +SOURCE_DIR="/opt/colcon_ws/install/gisnav/share/gisnav/launch/params" + +# Target directory where the .yaml files should be moved to (shared volume) +TARGET_DIR="/etc/gisnav/" + +if [ ! -d "$TARGET_DIR" ]; then + echo "WARNING: Target directory $TARGET_DIR does not exist. Please use Docker Compose to create the image to ensure ROS launch parameters are moved to a shared volume." +fi + +# Check if the source directory exists and has yaml files +if [ -d "$SOURCE_DIR" ] && [ -d "$TARGET_DIR" ] && [ "$(ls -A $SOURCE_DIR/*.yaml 2>/dev/null)" ]; then + # Iterate over each .yaml file in the source directory + for file in $SOURCE_DIR/*.yaml; do + # Extract filename from the path + filename=$(basename "$file") + + # Check if the file already exists in the target directory + if [ -e "$TARGET_DIR/$filename" ]; then + echo "WARNING: $filename already exists in $TARGET_DIR. Skipping move." + else + # Move the file to the target directory + mv "$file" "$TARGET_DIR" + + # Check if move was successful before creating a symlink + if [ $? -eq 0 ]; then + echo "INFO: Moved $filename to $TARGET_DIR" + # Create a symbolic link in the source directory pointing to the new location + ln -s "${TARGET_DIR}/${filename}" "${SOURCE_DIR}/${filename}" + else + echo "ERROR: Failed to move $filename" + fi + fi + done +else + echo "INFO: Launch parameter files or target volume not found on container - likely already moved to shared volume or shared volume is not mounted." +fi + exec "$@" diff --git a/docs/pages/developer_guide/offboard/docker_compose.rst b/docs/pages/developer_guide/offboard/docker_compose.rst index 0acc5bd1..be6f1c0a 100644 --- a/docs/pages/developer_guide/offboard/docker_compose.rst +++ b/docs/pages/developer_guide/offboard/docker_compose.rst @@ -21,83 +21,6 @@ Prerequisites .. include:: ../../../../docker/DOCKER.rst - -Overview of services -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -The |Docker Compose file|_ defines all services used to support GISNav deployments. -The diagram below describes the system architecture through the external -interfaces and Docker bridge networks shared between the Docker Compose services. - -The Docker bridge networks have in-built DNS which means the container names -resolve to their respective IP addresses. The container name will equal the -service name prefixed with ``gisnav-`` and suffixed with ``-1``. So for example -deploying the ``mapserver`` service using the Compose file will start a -Docker container with the hostname ``gisnav-mapserver-1``. - -.. todo:: - Split mavlink network into mavlink and ROS networks. For ROS the intention - is to use the shared memory device instead of serializing and going through - the network stack since we will be passing a lot of images around. - -.. note:: - The application services have access to both ``gis`` and ``mavlink`` networks. - -.. mermaid:: - - graph TD - subgraph mavlink ["mavlink"] - mavlink_qgc[qgc] - subgraph simulation ["Simulation Services"] - simulation_px4[px4] - simulation_ardupilot[ardupilot] - end - subgraph middleware ["Middleware Services"] - middleware_mavros[mavros] - middleware_micro_ros_agent[micro-ros-agent] - middleware_gscam[gscam] - end - end - - subgraph gis_mavlink ["gis & mavlink"] - subgraph application ["Application Services"] - application_gisnav[gisnav] - application_autoheal[autoheal] - end - end - - subgraph gis ["gis"] - subgraph gis_services ["GIS Services"] - gis_mapserver[mapserver] - gis_qgis[qgis] - end - subgraph data_services ["Data Services"] - gis_postgres[postgres] - gis_maps_volume[maps-volume] - end - gis_fileserver[fileserver] - end - - mavlink_qgc -->|14550/udp\nMAVLink| simulation_px4 - simulation_px4 -->|14540/udp\nMAVLink| middleware_mavros - simulation_px4 -->|8888/udp\nDDS-XRCE | middleware_micro_ros_agent - simulation_px4 -->|5600/udp\nRTP H.264 Video| middleware_gscam - middleware_mavros -->|/dev/shm\nROS 2 Fast DDS| application_gisnav - middleware_micro_ros_agent -->|/dev/shm\nROS 2 Fast DDS| application_gisnav - middleware_gscam -->|/dev/shm\nROS 2 Fast DDS| application_gisnav - application_gisnav -->|5432/tcp| gis_postgres - - application_gisnav -->|80/tcp\nHTTP WMS| gis_mapserver - gis_mapserver -->|80/tcp\nHTTP WMS| gis_qgis - gis_qgis -->|5432/tcp| gis_postgres - gis_mapserver ---|/etc/mapserver| gis_maps_volume - gis_fileserver ---|/etc/mapserver| gis_maps_volume - - classDef network fill:transparent,stroke-dasharray:5 5; - class mavlink,gis,gis_mavlink network - - - Example deployments ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^