Compare commits
4 Commits
9298a57211
...
a5207c6b28
Author | SHA1 | Date | |
---|---|---|---|
a5207c6b28 | |||
3e4aad9094 | |||
af94edae60 | |||
ad40afac9b |
@ -1,5 +1,3 @@
|
||||
version: "3.8"
|
||||
|
||||
services:
|
||||
gui-tools:
|
||||
env_file:
|
||||
@ -9,44 +7,40 @@ services:
|
||||
|
||||
environment:
|
||||
# Display settings
|
||||
- DISPLAY=${DISPLAY}
|
||||
- WAYLAND_DISPLAY=${WAYLAND_DISPLAY}
|
||||
# - QT_QPA_PLATFORM=${QT_QPA_PLATFORM:-xcb} # Default to X11
|
||||
# If running with Wayland
|
||||
- XDG_RUNTIME_DIR=${XDG_RUNTIME_DIR}
|
||||
- ROS_MASTER_URI=http://ros-master:11311
|
||||
# - NVIDIA_DRIVER_CAPABILITIES=compute,video,utility
|
||||
# - NVIDIA_VISIBLE_DEVICES=all
|
||||
DISPLAY: "${DISPLAY}"
|
||||
WAYLAND_DISPLAY: "${WAYLAND_DISPLAY}"
|
||||
# Uncomment below if using X11
|
||||
# QT_QPA_PLATFORM: "${QT_QPA_PLATFORM:-xcb}"
|
||||
XDG_RUNTIME_DIR: "${XDG_RUNTIME_DIR}"
|
||||
ROS_MASTER_URI: "http://ros-master:11311"
|
||||
|
||||
volumes:
|
||||
# X11 socket
|
||||
- /tmp/.X11-unix:/tmp/.X11-unix
|
||||
- ${XAUTHORITY:-~/.Xauthority}:/root/.Xauthority
|
||||
# Wayland socket
|
||||
#- ${XDG_RUNTIME_DIR}/wayland-0:${XDG_RUNTIME_DIR}/wayland-0
|
||||
# Allow access to the host's GPU
|
||||
# Access to GPU devices
|
||||
- /dev/dri:/dev/dri
|
||||
#Auto reload on code changes
|
||||
# Code and configuration
|
||||
- ./guiTools/spiri_sdk_guitools/:/app/spiri_sdk_guitools/
|
||||
# Enable launching the SDK from the SDK
|
||||
# - ./:/app/sdk
|
||||
- ./robots:/robots
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
|
||||
devices:
|
||||
# Provide access to GPU devices
|
||||
# Provide access to GPU devices (supports non-NVIDIA GPUs)
|
||||
- /dev/dri:/dev/dri
|
||||
|
||||
network_mode: host
|
||||
ports:
|
||||
- 8923:8923
|
||||
ipc: host
|
||||
#user: "${UID}:${GID}"
|
||||
privileged: true # Allow privileged access if necessary (e.g., for GPU access)
|
||||
# restart: unless-stopped
|
||||
# command: /bin/bash -c "source /opt/ros/foxy/setup.bash && rvis2" # Replace with the actual command to run Gazebo Ignition
|
||||
deploy:
|
||||
resources:
|
||||
reservations:
|
||||
devices:
|
||||
- driver: cdi
|
||||
device_ids:
|
||||
- nvidia.com/gpu=all
|
||||
privileged: true # Required for GPU access
|
||||
|
||||
# deploy:
|
||||
# resources:
|
||||
# reservations:
|
||||
# devices:
|
||||
# - driver: cdi # Optional for advanced resource scheduling
|
||||
# device_ids:
|
||||
# - "all" # Use "all" for all available GPUs
|
||||
|
@ -27,7 +27,7 @@ def launch_app(command):
|
||||
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
|
||||
from spiri_sdk_guitools.sim_drone import robot_types
|
||||
import aiodocker
|
||||
import asyncio
|
||||
|
||||
@ -64,30 +64,9 @@ async def main():
|
||||
|
||||
#Add a new robot
|
||||
with new_robot_widget:
|
||||
ui.label("Add new robot").classes("text-3xl")
|
||||
newRobotParams = defaultdict(binding.BindableProperty)
|
||||
ui.number(value=1, label="SysID", min=1, max=254,
|
||||
).bind_value(newRobotParams, 'sysid')
|
||||
default_robot_compose = (
|
||||
"/robots/spiri-mu/core/docker-compose.yaml\n"
|
||||
"#/robots/spiri-mu/virtual_camera/docker-compose.yaml --build"
|
||||
)
|
||||
ui.label("Compose files").classes("text-xl")
|
||||
ui.codemirror(value=default_robot_compose, language="bash", theme="basicDark").bind_value(newRobotParams, 'compose_files')
|
||||
async def new_robot():
|
||||
compose_files = []
|
||||
#Split on comma or newline, and remove comments
|
||||
for line in newRobotParams['compose_files'].split('\n'):
|
||||
line = line.split('#')[0].strip()
|
||||
if line:
|
||||
compose_files.append(line)
|
||||
current_robot = newRobotParams.copy()
|
||||
current_robot['compose_files'] = compose_files
|
||||
robot = Robot(**current_robot)
|
||||
asyncio.tasks.create_task(robot.ui(robots_widget))
|
||||
|
||||
newRobotParams['sysid'] += 1
|
||||
ui.button("Add", on_click=new_robot)
|
||||
for name, robot in robot_types.items():
|
||||
ui.label(f"Add new {name}").classes("text-3xl")
|
||||
await robot.launch_widget(robots_widget)
|
||||
|
||||
# Start the NiceGUI application
|
||||
ui.run(title="Spiri SDK Launcher", port=8923, dark=None)
|
||||
|
@ -5,13 +5,15 @@ from typing import List
|
||||
import os
|
||||
import sh
|
||||
import subprocess
|
||||
from nicegui import ui, run, app
|
||||
from nicegui import ui, run, app, binding
|
||||
import yaml
|
||||
|
||||
import docker
|
||||
import aiodocker
|
||||
import asyncio
|
||||
from spiri_sdk_guitools.video_button import EnableStreamingButton
|
||||
from collections import defaultdict
|
||||
import importlib.util
|
||||
|
||||
docker_client = docker.from_env()
|
||||
|
||||
@ -20,12 +22,15 @@ from rclpy.node import Node
|
||||
import rclpy
|
||||
import threading
|
||||
|
||||
|
||||
def ros_main() -> None:
|
||||
async def ros_loop():
|
||||
rclpy.init()
|
||||
node = rclpy.create_node('async_subscriber')
|
||||
while rclpy.ok():
|
||||
rclpy.spin_once(node, timeout_sec=0)
|
||||
await asyncio.sleep(0.1)
|
||||
|
||||
|
||||
app.on_startup(lambda: threading.Thread(target=ros_main).start())
|
||||
app.on_startup(ros_loop())
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
@ -73,7 +78,22 @@ async def container_logs(container, element):
|
||||
# ui.html(conv.convert(bytes(log,'utf-8').decode('utf-8', 'xmlcharrefreplace'), full=False))
|
||||
|
||||
|
||||
robot_types = {}
|
||||
|
||||
class Robot:
|
||||
def __init_subclass__(self):
|
||||
#Register sub-classes as plugins
|
||||
robot_types[self.robot_type] = self
|
||||
|
||||
for file in Path("/robots").glob("**/robot_plugins.py"):
|
||||
logger.info(f"Loading plugin {file}")
|
||||
spec = importlib.util.spec_from_file_location("robot_plugins", file)
|
||||
plugin = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(plugin)
|
||||
logger.info(f"Loaded plugin {file}")
|
||||
|
||||
|
||||
class Spirimu(Robot):
|
||||
robot_type = "spiri_mu"
|
||||
|
||||
def __init__(self, sysid: int, compose_files: List[Path] | str):
|
||||
@ -92,6 +112,33 @@ class Robot:
|
||||
self.robot_name = f"{self.robot_type}_{self.sysid}".replace("-","_")
|
||||
self.world_name = "citadel_hill"
|
||||
|
||||
@classmethod
|
||||
async def launch_widget(cls, robots_widget):
|
||||
newRobotParams = defaultdict(binding.BindableProperty)
|
||||
ui.number(value=1, label="SysID", min=1, max=254,
|
||||
).bind_value(newRobotParams, 'sysid')
|
||||
default_robot_compose = ""
|
||||
for compose_file in Path("/robots").glob("**/docker-compose.yaml"):
|
||||
default_robot_compose += f"{compose_file} --build \n"
|
||||
|
||||
ui.label("Compose files").classes("text-xl")
|
||||
ui.codemirror(value=default_robot_compose, language="bash", theme="basicDark").bind_value(newRobotParams, 'compose_files')
|
||||
async def new_robot():
|
||||
compose_files = []
|
||||
#Split on comma or newline, and remove comments
|
||||
for line in newRobotParams['compose_files'].split('\n'):
|
||||
line = line.split('#')[0].strip()
|
||||
if line:
|
||||
compose_files.append(line)
|
||||
current_robot = newRobotParams.copy()
|
||||
current_robot['compose_files'] = compose_files
|
||||
robot = cls(**current_robot)
|
||||
asyncio.tasks.create_task(robot.ui(robots_widget))
|
||||
|
||||
newRobotParams['sysid'] += 1
|
||||
ui.button("Add", on_click=new_robot)
|
||||
|
||||
|
||||
async def ui_containers(self, element):
|
||||
docker_elements = {}
|
||||
container_status = {}
|
||||
@ -152,6 +199,9 @@ class Robot:
|
||||
ui.label(topic[0])
|
||||
await asyncio.sleep(10)
|
||||
|
||||
async def ui_actions(self, element):
|
||||
pass
|
||||
|
||||
async def ui(self, element):
|
||||
adocker = aiodocker.Docker()
|
||||
|
||||
@ -175,11 +225,14 @@ class Robot:
|
||||
with ui.tabs() as tabs:
|
||||
tab_containers = ui.tab("Containers")
|
||||
tab_ros = ui.tab("ROS Topics")
|
||||
tab_actions = ui.tab("Actions")
|
||||
with ui.tab_panels(tabs, value=tab_containers):
|
||||
tab = ui.tab_panel(tab_containers).classes("w-full")
|
||||
asyncio.create_task(self.ui_containers(tab))
|
||||
tab = ui.tab_panel(tab_ros).classes("w-full")
|
||||
asyncio.create_task(self.ui_ros(tab))
|
||||
tab = ui.tab_panel(tab_actions).classes("w-full")
|
||||
asyncio.create_task(self.ui_actions(tab))
|
||||
|
||||
async def async_stop(self):
|
||||
return await run.io_bound(self.stop)
|
||||
|
Loading…
Reference in New Issue
Block a user