From b4426c9b3d43dbeb26e3ec2dc3eddb013e56d9ae Mon Sep 17 00:00:00 2001 From: Chris Iverach-Brereton Date: Thu, 25 Sep 2025 14:52:37 -0400 Subject: [PATCH 1/3] Add `system.bash` section to robot.yaml with `source` and `env` sub-fields for adding additional bash sources and envars --- clearpath_config/system/bash.py | 98 +++++++++++++++++++++++++++++++ clearpath_config/system/system.py | 31 +++++++++- 2 files changed, 126 insertions(+), 3 deletions(-) create mode 100644 clearpath_config/system/bash.py diff --git a/clearpath_config/system/bash.py b/clearpath_config/system/bash.py new file mode 100644 index 0000000..14f7716 --- /dev/null +++ b/clearpath_config/system/bash.py @@ -0,0 +1,98 @@ +# Software License Agreement (BSD) +# +# @author Chris Iverach-Brereton +# @copyright (c) 2025, Clearpath Robotics, Inc., All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of Clearpath Robotics nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +import os +from typing import List + +from clearpath_config.common.types.config import BaseConfig +from clearpath_config.common.types.file import File +from clearpath_config.common.utils.dictionary import flip_dict + + +class BashConfig(BaseConfig): + """ + Contains additional BASH sources and envars. + + These are passed to the generator for inclusion in setup.bash. + """ + + SOURCE = 'source' + ENV = 'env' + + TEMPLATE = { + SOURCE: SOURCE, + ENV: ENV, + } + + KEYS = flip_dict(TEMPLATE) + + DEFAULTS = { + SOURCE: [], + ENV: {}, + } + + def __init__( + self, + config: dict = {}, + source: List[str] = DEFAULTS[SOURCE], + env: dict = DEFAULTS[ENV], + ) -> None: + self._env = {} + self._source = [] + + setters = { + self.KEYS[self.SOURCE]: BashConfig.additional_sources, + self.KEYS[self.ENV]: BashConfig.additional_envars, + } + + super().__init__(setters, config) + + self.additional_envars = env + self.additional_sources = source + + @property + def additional_sources(self) -> List[str]: + return self._source + + @additional_sources.setter + def additional_sources(self, sources: List[str]) -> None: + self._source.clear() + for f in sources: + assert isinstance(f, str), f'Bash source {f} must be a string' + assert os.path.exists(f), f'Bash source {f} does not exist' + self._source.append(f) + + @property + def additional_envars(self) -> dict: + return self._env + + @additional_envars.setter + def additional_envars(self, envars: dict) -> None: + self._env.clear() + for env in envars: + assert isinstance(env, str), f'Environment variable {env} must be a string' + self._env[env] = str(envars[env]) diff --git a/clearpath_config/system/system.py b/clearpath_config/system/system.py index 13987ec..4fca185 100644 --- a/clearpath_config/system/system.py +++ b/clearpath_config/system/system.py @@ -35,6 +35,7 @@ from clearpath_config.common.types.namespace import Namespace from clearpath_config.common.types.username import Username from clearpath_config.common.utils.dictionary import flip_dict +from clearpath_config.system.bash import BashConfig from clearpath_config.system.hosts import HostConfig, HostListConfig from clearpath_config.system.middleware import MiddlewareConfig @@ -51,6 +52,7 @@ class SystemConfig(BaseConfig): DOMAIN_ID = 'domain_id' MIDDLEWARE = MiddlewareConfig.MIDDLEWARE WORKSPACES = 'workspaces' + BASH = 'bash' TEMPLATE = { SYSTEM: { @@ -63,7 +65,8 @@ class SystemConfig(BaseConfig): DOMAIN_ID: DOMAIN_ID, MIDDLEWARE: MIDDLEWARE, WORKSPACES: WORKSPACES - } + }, + BASH: BASH, } } @@ -83,7 +86,9 @@ class SystemConfig(BaseConfig): # Discovery Server Disabled MIDDLEWARE: MiddlewareConfig.DEFAULTS, # Workpaces: empty list - WORKSPACES: [] + WORKSPACES: [], + # additional bash environment + BASH: BashConfig.DEFAULTS, } def __init__( @@ -95,7 +100,8 @@ def __init__( namespace: str | Namespace = DEFAULTS[NAMESPACE], domain_id: int | DomainID = DEFAULTS[DOMAIN_ID], middleware: dict | MiddlewareConfig = DEFAULTS[MIDDLEWARE], - workspaces: list = DEFAULTS[WORKSPACES] + workspaces: list = DEFAULTS[WORKSPACES], + bash: dict | BashConfig = DEFAULTS[BASH], ) -> None: # Initialization self._config = {} @@ -106,6 +112,7 @@ def __init__( self.domain_id = domain_id self.middleware = middleware self.workspaces = workspaces + self.bash = bash # Setter Template setters = { self.KEYS[self.HOSTS]: SystemConfig.hosts, @@ -115,6 +122,7 @@ def __init__( self.KEYS[self.DOMAIN_ID]: SystemConfig.domain_id, self.KEYS[self.MIDDLEWARE]: SystemConfig.middleware, self.KEYS[self.WORKSPACES]: SystemConfig.workspaces, + self.KEYS[self.BASH]: SystemConfig.bash, } # Set from Config super().__init__(setters, config, self.SYSTEM) @@ -272,3 +280,20 @@ def workspaces(self, value: list) -> None: assert all([isinstance(i, str) for i in value]), ( # noqa:C419 'Workspaces must be "list" of "str"') self._workspaces = value + + @property + def bash(self) -> BashConfig: + return self._bash + + @bash.setter + def bash(self, bash: dict | BashConfig) -> None: + if isinstance(bash, dict): + self._bash = BashConfig( + source=bash.get('source', []), + env=bash.get('env', {}), + ) + elif isinstance(bash, BashConfig): + self._bash = bash + else: + assert isinstance(bash, dict) or isinstance(bash, BashConfig), \ + 'Bash configuration must be of type "dict" or "BashConfig"' From 34c2410b3a0688ed1defc4af99f8390611f8cb0b Mon Sep 17 00:00:00 2001 From: Chris Iverach-Brereton Date: Thu, 25 Sep 2025 15:05:03 -0400 Subject: [PATCH 2/3] Remove unused import --- clearpath_config/system/bash.py | 1 - 1 file changed, 1 deletion(-) diff --git a/clearpath_config/system/bash.py b/clearpath_config/system/bash.py index 14f7716..6eaaed7 100644 --- a/clearpath_config/system/bash.py +++ b/clearpath_config/system/bash.py @@ -29,7 +29,6 @@ from typing import List from clearpath_config.common.types.config import BaseConfig -from clearpath_config.common.types.file import File from clearpath_config.common.utils.dictionary import flip_dict From cee57701008e54e8131c5b49890307aa4242e37a Mon Sep 17 00:00:00 2001 From: Chris Iverach-Brereton <59611394+civerachb-cpr@users.noreply.github.com> Date: Fri, 26 Sep 2025 12:53:59 -0400 Subject: [PATCH 3/3] Add new envar to Flir camera sample --- clearpath_config/sample/sensors/flir_blackfly.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/clearpath_config/sample/sensors/flir_blackfly.yaml b/clearpath_config/sample/sensors/flir_blackfly.yaml index 36a3fe0..feac81c 100644 --- a/clearpath_config/sample/sensors/flir_blackfly.yaml +++ b/clearpath_config/sample/sensors/flir_blackfly.yaml @@ -1,5 +1,9 @@ serial_number: a300-0000 version: 0 +system: + bash: + env: + SPINNAKER_GENTL64_CTI: /opt/ros/jazzy/lib/spinnaker-gentl/Spinnaker_GenTL.cti sensors: camera: - model: flir_blackfly