Work on moving sim-drone to our user interface
This commit is contained in:
parent
15110bd830
commit
ec7a53201f
|
@ -26,7 +26,10 @@ services:
|
|||
# Allow access to the host's GPU
|
||||
- /dev/dri:/dev/dri
|
||||
#Auto reload on code changes
|
||||
- ./guiTools/spiri_sdk_guitools/:/app/spiri_sdk_guitools
|
||||
- ./guiTools/spiri_sdk_guitools/:/app/spiri_sdk_guitools/
|
||||
# Enable launching the SDK from the SDK
|
||||
- ./:/app/sdk
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
devices:
|
||||
# Provide access to GPU devices
|
||||
- /dev/dri:/dev/dri
|
||||
|
|
|
@ -4,15 +4,14 @@ RUN apt-get update
|
|||
RUN apt-get -y install qterminal mesa-utils \
|
||||
libgstreamer1.0-dev \
|
||||
libgstreamer-plugins-base1.0-dev \
|
||||
python3.12-venv \
|
||||
python3-pip \
|
||||
gstreamer1.0-libav \
|
||||
gstreamer1.0-gl \
|
||||
gstreamer1.0-plugins-good \
|
||||
gstreamer1.0-plugins-bad \
|
||||
gstreamer1.0-plugins-ugly
|
||||
|
||||
#Install poetry
|
||||
RUN curl -sSL https://install.python-poetry.org | python3 -
|
||||
ENV PATH="/root/.local/bin:$PATH"
|
||||
|
||||
COPY --from=git.spirirobotics.com/spiri/gazebo-resources:main /plugins /ardupilot_gazebo/plugins
|
||||
COPY --from=git.spirirobotics.com/spiri/gazebo-resources:main /models /ardupilot_gazebo/models
|
||||
|
@ -26,14 +25,21 @@ RUN chmod +x /spawn_drones.sh
|
|||
|
||||
WORKDIR /app
|
||||
|
||||
COPY ./pyproject.toml /app/pyproject.toml
|
||||
COPY ./poetry.lock /app/poetry.lock
|
||||
RUN python3 -m venv /opt/venv
|
||||
ENV PATH="/opt/venv/bin:$PATH"
|
||||
|
||||
RUN pip3 install poetry
|
||||
|
||||
COPY ./pyproject.toml ./poetry.lock ./README.md ./
|
||||
COPY ./spiri_sdk_guitools ./spiri_sdk_guitools
|
||||
|
||||
RUN poetry env use python3
|
||||
RUN poetry config virtualenvs.create false && poetry install --no-dev --no-interaction --no-ansi
|
||||
|
||||
RUN poetry install --no-root
|
||||
|
||||
COPY ./spiri_sdk_guitools /app/spiri_sdk_guitools
|
||||
|
||||
CMD poetry run python3 spiri_sdk_guitools/launcher.py
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -666,6 +666,24 @@ MarkupSafe = ">=2.0"
|
|||
[package.extras]
|
||||
i18n = ["Babel (>=2.7)"]
|
||||
|
||||
[[package]]
|
||||
name = "loguru"
|
||||
version = "0.7.2"
|
||||
description = "Python logging made (stupidly) simple"
|
||||
optional = false
|
||||
python-versions = ">=3.5"
|
||||
files = [
|
||||
{file = "loguru-0.7.2-py3-none-any.whl", hash = "sha256:003d71e3d3ed35f0f8984898359d65b79e5b21943f78af86aa5491210429b8eb"},
|
||||
{file = "loguru-0.7.2.tar.gz", hash = "sha256:e671a53522515f34fd406340ee968cb9ecafbc4b36c679da03c18fd8d0bd51ac"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
colorama = {version = ">=0.3.4", markers = "sys_platform == \"win32\""}
|
||||
win32-setctime = {version = ">=1.0.0", markers = "sys_platform == \"win32\""}
|
||||
|
||||
[package.extras]
|
||||
dev = ["Sphinx (==7.2.5)", "colorama (==0.4.5)", "colorama (==0.4.6)", "exceptiongroup (==1.1.3)", "freezegun (==1.1.0)", "freezegun (==1.2.2)", "mypy (==v0.910)", "mypy (==v0.971)", "mypy (==v1.4.1)", "mypy (==v1.5.1)", "pre-commit (==3.4.0)", "pytest (==6.1.2)", "pytest (==7.4.0)", "pytest-cov (==2.12.1)", "pytest-cov (==4.1.0)", "pytest-mypy-plugins (==1.9.3)", "pytest-mypy-plugins (==3.0.0)", "sphinx-autobuild (==2021.3.14)", "sphinx-rtd-theme (==1.3.0)", "tox (==3.27.1)", "tox (==4.11.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "markdown2"
|
||||
version = "2.5.1"
|
||||
|
@ -1919,6 +1937,20 @@ files = [
|
|||
{file = "websockets-13.1.tar.gz", hash = "sha256:a3b3366087c1bc0a2795111edcadddb8b3b59509d5db5d7ea3fdd69f954a8878"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "win32-setctime"
|
||||
version = "1.1.0"
|
||||
description = "A small Python utility to set file creation time on Windows"
|
||||
optional = false
|
||||
python-versions = ">=3.5"
|
||||
files = [
|
||||
{file = "win32_setctime-1.1.0-py3-none-any.whl", hash = "sha256:231db239e959c2fe7eb1d7dc129f11172354f98361c4fa2d6d2d7e278baa8aad"},
|
||||
{file = "win32_setctime-1.1.0.tar.gz", hash = "sha256:15cf5750465118d6929ae4de4eb46e8edae9a5634350c01ba582df868e932cb2"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
dev = ["black (>=19.3b0)", "pytest (>=4.6.2)"]
|
||||
|
||||
[[package]]
|
||||
name = "wsproto"
|
||||
version = "1.2.0"
|
||||
|
@ -2032,4 +2064,4 @@ propcache = ">=0.2.0"
|
|||
[metadata]
|
||||
lock-version = "2.0"
|
||||
python-versions = "^3.11"
|
||||
content-hash = "cd634ccba7b1059ce9cf2e92503f3ccce24693405aa53f78509482f944c55d5b"
|
||||
content-hash = "941bf4750c3b8a3045cfd01bb1ab4af7c1e8fd2d5fa54cd85d8ac3a0c8837238"
|
||||
|
|
|
@ -1,16 +1,17 @@
|
|||
[tool.poetry]
|
||||
name = "spiri-sdk-guitools"
|
||||
name = "spiri_sdk_guitools"
|
||||
version = "0.1.0"
|
||||
description = ""
|
||||
authors = ["Alex Davies <traverse.da@gmail.com>"]
|
||||
authors = ["Spiri Robotics"]
|
||||
readme = "README.md"
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = "^3.11"
|
||||
nicegui = "^2.5.0"
|
||||
pywebview = "^5.3.2"
|
||||
|
||||
loguru = "^0.7.2"
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry-core"]
|
||||
build-backend = "poetry.core.masonry.api"
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
from nicegui import ui
|
||||
from nicegui import ui, binding, app
|
||||
import subprocess
|
||||
from collections import defaultdict
|
||||
|
||||
# Dictionary of applications: key is the button text, value is the command to execute
|
||||
applications = {
|
||||
|
@ -21,19 +22,38 @@ def launch_app(command):
|
|||
# Create the NiceGUI interface
|
||||
ui.label("Spiri Robotics SDK").style('font-size: 40px; margin-bottom: 10px;').classes('w-full text-center')
|
||||
|
||||
robots = []
|
||||
from spiri_sdk_guitools.sim_drone import Robot
|
||||
|
||||
with ui.tabs().classes('w-full') as tabs:
|
||||
tab_tools = ui.tab('Tools')
|
||||
tab_robots = ui.tab('Robots')
|
||||
# two = ui.tab('Two')
|
||||
with ui.tab_panels(tabs, value=tab_tools).classes():
|
||||
with ui.tab_panel(tab_tools):
|
||||
# Create and place buttons dynamically based on the dictionary
|
||||
with ui.grid(columns=3):
|
||||
for app_name, command in applications.items():
|
||||
ui.button(app_name, on_click=lambda cmd=command: launch_app(cmd)).style('width: 150px; height: 50px; margin: 5px;')
|
||||
with ui.tab_panel(tab_robots):
|
||||
with ui.card().tight():
|
||||
ui.label(f"Robots SysID")
|
||||
@ui.page('/')
|
||||
def main():
|
||||
|
||||
with ui.tabs().classes('w-full') as tabs:
|
||||
tab_tools = ui.tab('Tools')
|
||||
tab_robots = ui.tab('Robots')
|
||||
# two = ui.tab('Two')
|
||||
with ui.tab_panels(tabs, value=tab_tools).classes():
|
||||
with ui.tab_panel(tab_tools):
|
||||
# Create and place buttons dynamically based on the dictionary
|
||||
with ui.grid(columns=3):
|
||||
for app_name, command in applications.items():
|
||||
ui.button(app_name, on_click=lambda cmd=command: launch_app(cmd)).style('width: 150px; height: 50px; margin: 5px;')
|
||||
with ui.tab_panel(tab_robots):
|
||||
with ui.grid(columns=3):
|
||||
for robot in robots:
|
||||
with ui.row():
|
||||
ui.label(f"Robot {robot.sys_id}")
|
||||
ui.button(f"Start Robot {robot.sys_id}", on_click=lambda: robot.start()).style('width: 150px; height: 50px; margin: 5px;')
|
||||
#Add a new robot
|
||||
with ui.element():
|
||||
ui.label("Add new robot")
|
||||
newRobotParams = defaultdict(binding.BindableProperty)
|
||||
ui.number(value=0, label="SysID").bind_value(newRobotParams, 'sysid')
|
||||
ui.input(value="./sdk/docker-compose.yml", label="Compose files (comma seperated)").bind_value(newRobotParams, 'compose_files')
|
||||
def new_robot():
|
||||
robot = Robot(**newRobotParams)
|
||||
robots.append(robot)
|
||||
ui.label(str(robots))
|
||||
ui.button("Add", on_click=new_robot)
|
||||
# Start the NiceGUI application
|
||||
ui.run(title="Spiri SDK Launcher", port=8923, dark=None)
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
from loguru import logger
|
||||
from pathlib import Path
|
||||
import contextlib
|
||||
from typing import List
|
||||
|
||||
@contextlib.contextmanager
|
||||
def modified_environ(*remove, **update):
|
||||
"""
|
||||
Temporarily updates the ``os.environ`` dictionary in-place.
|
||||
|
||||
The ``os.environ`` dictionary is updated in-place so that the modification
|
||||
is sure to work in all situations.
|
||||
|
||||
:param remove: Environment variables to remove.
|
||||
:param update: Dictionary of environment variables and values to add/update.
|
||||
"""
|
||||
env = os.environ
|
||||
update = update or {}
|
||||
remove = remove or []
|
||||
|
||||
# List of environment variables being updated or removed.
|
||||
stomped = (set(update.keys()) | set(remove)) & set(env.keys())
|
||||
# Environment variables and values to restore on exit.
|
||||
update_after = {k: env[k] for k in stomped}
|
||||
# Environment variables and values to remove on exit.
|
||||
remove_after = frozenset(k for k in update if k not in env)
|
||||
|
||||
try:
|
||||
env.update(update)
|
||||
[env.pop(k, None) for k in remove]
|
||||
yield
|
||||
finally:
|
||||
env.update(update_after)
|
||||
[env.pop(k) for k in remove_after]
|
||||
|
||||
|
||||
class Robot:
|
||||
def __init__(self, sysid: int, compose_files: List[Path]):
|
||||
if sysid > 255 or sysid < 0:
|
||||
raise ValueError("sysid must be between 0 and 255")
|
||||
self.processes = []
|
||||
|
||||
def start(self):
|
||||
"""Starts the simulated drone with a given sys_id,
|
||||
each drone must have it's own unique ID.
|
||||
"""
|
||||
instance = self.sysid-1
|
||||
with logger.contextualize(syd_id=sys_id):
|
||||
env = os.environ
|
||||
with modified_environ(
|
||||
SERIAL0_PORT=str(int(env["SERIAL0_PORT"]) + 10 * instance),
|
||||
MAVROS2_PORT=str(int(env["MAVROS2_PORT"]) + 10 * instance),
|
||||
MAVROS1_PORT=str(int(env["MAVROS1_PORT"]) + 10 * instance),
|
||||
FDM_PORT_IN=str(int(env["FDM_PORT_IN"]) + 10 * instance),
|
||||
SITL_PORT=str(int(env["SITL_PORT"]) + 10 * instance),
|
||||
INSTANCE=str(instance),
|
||||
DRONE_SYS_ID=str(self.sys_id),
|
||||
):
|
||||
logger.info("Starting drone stack, this may take some time")
|
||||
docker_stack = sh.docker.compose(
|
||||
"--profile",
|
||||
"uav-sim",
|
||||
"-p",
|
||||
f"spiri-sdk-{sys_id}",
|
||||
"up",
|
||||
_out=outputLogger("docker_stack", sys_id),
|
||||
_err=sys.stderr,
|
||||
_bg=True,
|
||||
)
|
||||
self.processes.append(docker_stack)
|
Loading…
Reference in New Issue