feature/multiple-compose #11

Merged
unsalted_salt merged 7 commits from feature/multiple-compose into master 2024-11-18 14:27:31 -04:00
4 changed files with 60 additions and 37 deletions

View File

@ -70,7 +70,7 @@ async def main():
).bind_value(newRobotParams, 'sysid') ).bind_value(newRobotParams, 'sysid')
default_robot_compose = ( default_robot_compose = (
"/robots/spiri-mu/core/docker-compose.yaml\n" "/robots/spiri-mu/core/docker-compose.yaml\n"
"#/robots/spiri-mu/virtual_camera/docker-compose.yaml" "#/robots/spiri-mu/virtual_camera/docker-compose.yaml --build"
) )
ui.label("Compose files").classes("text-xl") ui.label("Compose files").classes("text-xl")
ui.codemirror(value=default_robot_compose, language="bash", theme="basicDark").bind_value(newRobotParams, 'compose_files') ui.codemirror(value=default_robot_compose, language="bash", theme="basicDark").bind_value(newRobotParams, 'compose_files')

View File

@ -6,6 +6,7 @@ import os
import sh import sh
import subprocess import subprocess
from nicegui import ui, run, app from nicegui import ui, run, app
import yaml
import docker import docker
import aiodocker import aiodocker
@ -96,38 +97,47 @@ class Robot:
container_status = {} container_status = {}
with element: with element:
while True: while True:
# Poll for data that changes with logger.catch():
for container in self.containers(): # Poll for data that changes
try: for container in self.containers():
health = container.attrs["State"]["Health"]["Status"] try:
except KeyError: health = container.attrs["State"]["Health"]["Status"]
health = "Unknown" except KeyError:
health = "Unknown"
container_status[container] = ( container_status[container] = (
f"{container.name} {container.status} {health}" f"{container.name} {container.status} {health}"
) )
if container not in docker_elements: if container not in docker_elements:
docker_elements[container] = ui.element().classes("w-full") docker_elements[container] = ui.element().classes("w-full")
with docker_elements[container]: with docker_elements[container]:
ui.label().bind_text(container_status, container).classes( ui.label().bind_text(container_status, container).classes(
"text-lg" "text-2xl"
) )
#Show the command the container is running #Show the command the container is running
# ui.label(container.attrs["Config"]["Cmd"]) # ui.label(container.attrs["Config"]["Cmd"])
cmd_widget = ui.codemirror(" ".join(container.attrs["Config"]["Cmd"]), language="bash",theme="basicDark").classes('h-auto max-h-32') cmd_widget = ui.codemirror(" ".join(container.attrs["Config"]["Cmd"]), language="bash",theme="basicDark").classes('h-auto max-h-32')
cmd_widget.enabled = False cmd_widget.enabled = False
logelement = (
ui.expansion("Logs") with ui.expansion("Env Variables").classes("w-full outline outline-1").style("margin: 10px;"):
.style("margin: 10px;") env_widget = ui.codemirror("\n".join(container.attrs["Config"]["Env"]), language="bash",theme="basicDark")
.classes("w-full outline outline-1") env_widget.enabled = False
)
asyncio.create_task(container_logs(container, logelement)) logelement = (
# Check for containers that have been removed ui.expansion("Logs")
removed = set(docker_elements.keys()) - set(self.containers()) .style("margin: 10px;")
for container in removed: .classes("w-full outline outline-1")
self.robot_ui.remove(docker_elements[container]) )
docker_elements.pop(container) asyncio.create_task(container_logs(container, logelement))
await asyncio.sleep(1) with ui.expansion("Full details").classes("w-full outline outline-1").style("margin: 10px;"):
details_widget = ui.codemirror(yaml.dump(container.attrs), language="yaml",theme="basicDark")
details_widget.enabled = False
# Check for containers that have been removed
removed = set(docker_elements.keys()) - set(self.containers())
for container in removed:
self.robot_ui.remove(docker_elements[container])
docker_elements.pop(container)
await asyncio.sleep(1)
async def ui_ros(self, element): async def ui_ros(self, element):
with element: with element:
@ -138,7 +148,7 @@ class Robot:
scroll_area.clear() scroll_area.clear()
#Filter for topics that start with self.robot_name #Filter for topics that start with self.robot_name
for topic in node_dummy.get_topic_names_and_types(): for topic in node_dummy.get_topic_names_and_types():
if topic[0].startswith(f"/{self.robot_name}/") or topic[0].startswith(f"/world/{self.world_name}/model/{self.robot_name}/"): if self.robot_name in topic[0]:
ui.label(topic[0]) ui.label(topic[0])
await asyncio.sleep(10) await asyncio.sleep(10)
@ -221,6 +231,11 @@ class Robot:
self.spawn_gz_model() self.spawn_gz_model()
logger.info("Starting drone stack, this may take some time") logger.info("Starting drone stack, this may take some time")
for compose_file in self.compose_files: for compose_file in self.compose_files:
arguments = compose_file.split(" ")
arguments = [arg.strip() for arg in arguments]
compose_file = arguments[0]
arguments = arguments[1:]
if not isinstance(compose_file, Path): if not isinstance(compose_file, Path):
compose_file = Path(compose_file) compose_file = Path(compose_file)
if not compose_file.exists(): if not compose_file.exists():
@ -236,15 +251,17 @@ class Robot:
"-f", "-f",
compose_file.as_posix(), compose_file.as_posix(),
"up", "up",
*arguments,
] ]
command = " ".join(args) command = " ".join(args)
logger.info(f"Starting drone stack with command: {command}") logger.info(f"Starting drone stack with command: {command}")
docker_stack = subprocess.Popen( docker_stack = subprocess.Popen(
args, args,
stdout=subprocess.PIPE, # stdout=subprocess.PIPE,
stderr=subprocess.PIPE, # stderr=subprocess.PIPE,
) )
logger.info(f"Started drone stack with PID: {docker_stack.pid}")
@logger.catch @logger.catch
def spawn_gz_model(self): def spawn_gz_model(self):

View File

@ -1,5 +1,8 @@
FROM git.spirirobotics.com/spiri/services-ros2-mavros:main FROM git.spirirobotics.com/spiri/services-ros2-mavros:main
RUN apt-get update RUN apt-get update
RUN apt-get --yes install ros-${ROS_DISTRO}-ros-gz-bridge ros-${ROS_DISTRO}-compressed-image-transport ros-${ROS_DISTRO}-rmw-cyclonedds-cpp RUN apt-get --yes install ros-${ROS_DISTRO}-ros-gz-bridge \
ros-${ROS_DISTRO}-ros-gz-image \
ros-${ROS_DISTRO}-compressed-image-transport \
ros-${ROS_DISTRO}-rmw-cyclonedds-cpp

View File

@ -4,7 +4,10 @@ services:
ipc: host ipc: host
network_mode: host network_mode: host
# image: git.spirirobotics.com/spiri/services-ros2-mavros:main # image: git.spirirobotics.com/spiri/services-ros2-mavros:main
#Build the iamge, give it a name, don't try to pull the image
build: ./ build: ./
image: spirisdk-virtual_camera
pull_policy: never
environment: environment:
- RMW_IMPLEMENTATION=rmw_cyclonedds_cpp - RMW_IMPLEMENTATION=rmw_cyclonedds_cpp
command: ros2 run ros_gz_image image_bridge /world/${WORLD_NAME}/model/${ROBOT_NAME}/link/pitch_link/sensor/camera/image command: ros2 run ros_gz_image image_bridge /world/${WORLD_NAME}/model/${ROBOT_NAME}/link/pitch_link/sensor/camera/image