ros2 env라는 기존에 없던 ROS2 CLI를 만들기!
이 과정은 앞선 포스팅에서 반복했던 내용이기에 코드는 생략합니다.

$ cd workspace/ros2_ws_new_cli/src/ros2env
$ mkdir command
$ cd command
$ touch 'env.py'


from ros2cli.command import add_subparsers_on_demand
from ros2cli.command import CommandExtension
class EnvCommand(CommandExtension):
'''add_arguments: parser에 서브 명령어를 동적으로 추가하여 확장가능한 구조 생성'''
def add_arguments(self, parser, cli_name, *, argv=None):
# 부모 클래스의 add_arguments 메서드 호출
super().add_arguments(parser, cli_name, argv=argv)
# 서브파서 추가: 'verb'라는 이름으로 서브 명령어를 구분
self._subparser = parser.add_subparsers(dest='verb')
# 서브 명령어를 추가
add_subparsers_on_demand(
self._subparser, cli_name=cli_name,
verbs=["verb1", "verb2"], # 여기에 원하는 서브 명령어를 추가
required=False # 서브 명령어가 필수가 아님
)
def main(self, *, parser, args):
# args에 '_verb' 속성이 없는 경우, 서브 명령어가 전달되지 않았을 때 도움말 출력
if not hasattr(args, "verb"):
self._subparser.print_help()
return 0
# 속성 '_verb'을 기준으로 선택된 서브 명령어(verb) 실행
extension = getattr(args, "verb")
# 해당 서브 명령어의 main 메서드 호출
return extension.main(args=args)
$ cd command
$ mkdir verb
$ echo list.py

from ros2env.api import get_all_env_list # 모든 환경 변수를 가져오는 함수
from ros2env.api import get_dds_env_list # DDS 환경 변수를 가져오는 함수
from ros2env.api import get_ros_env_list # ROS 환경 변수를 가져오는 함수
from ros2env.verb import VerbExtension # VerbExtension 클래스를 가져옴
class ListVerb(VerbExtension):
"""Output a list of ROS environment variables."""
def add_arguments(self, parser, cli_name):
"""
명령줄 인자에 대한 옵션을 추가하는 메서드.
:param parser: CLI 인자를 처리하기 위한 파서
:param cli_name: CLI의 이름
"""
# 모든 환경 변수 출력 옵션
parser.add_argument(
"-a", "--all", # -a 또는 --all로 호출 가능
action="store_true", # 이 옵션이 사용되면 True로 설정
help="Display all environment variables.", # 옵션 설명
)
# ROS 환경 변수 출력 옵션
parser.add_argument(
"-r", "--ros-env", # -r 또는 --ros-env로 호출 가능
action="store_true", # 이 옵션이 사용되면 True로 설정
help="Display the ROS environment variables." # 옵션 설명
)
# DDS 환경 변수 출력 옵션
parser.add_argument(
"-d", "--dds-env", # -d 또는 --dds-env로 호출 가능
action="store_true", # 이 옵션이 사용되면 True로 설정
help="Display the DDS environment variables." # 옵션 설명
)
def main(self, *, args):
"""
명령어의 주 기능을 처리하는 메서드.
:param args: 명령줄에서 전달된 인자
:return: 0을 반환하여 정상 종료를 나타냄
"""
# 명령줄 인자에 따라 출력할 환경 변수 목록 결정
if args.ros_env: # ROS 환경 변수가 요청된 경우
message = get_ros_env_list() # ROS 환경 변수 목록 가져오기
elif args.dds_env: # DDS 환경 변수가 요청된 경우
message = get_dds_env_list() # DDS 환경 변수 목록 가져오기
elif args.all: # -a 옵션이 사용된 경우
message = get_all_env_list() # 모든 환경 변수 목록 가져오기
else: # 어떠한 옵션도 주어지지 않은 경우
message = get_all_env_list() # 기본적으로 모든 환경 변수 목록 가져오기
# 결정된 메시지를 출력
print(message) # 최종 결과를 콘솔에 출력
$ mkdir api
$ echo __init__.py

import os # os 모듈을 임포트하여 환경 변수를 처리할 수 있도록 함
def get_ros_env_list():
"""
ROS 환경 변수를 가져와서 포맷된 문자열로 반환하는 함수.
:return: 포맷된 ROS 환경 변수 문자열
"""
# os.getenv() 함수를 사용하여 ROS와 관련된 환경 변수를 가져옴
# 변수값이 존재하지 않으면 기본값 'None'을 반환
ros_version = os.getenv('ROS_VERSION', 'None') # ROS 버전
ros_distro = os.getenv('ROS_DISTRO', 'None') # ROS 배포판
ros_python_version = os.getenv('ROS_PYTHON_VERSION', 'None') # ROS에서 사용하는 파이썬 버전
# 가져온 변수들을 포맷팅하여 문자열로 반환
ros_env_list = (
'ROS_VERSION = {0}\n'
'ROS_DISTRO = {1}\n'
'ROS_PYTHON_VERSION = {2}\n'
).format(ros_version, ros_distro, ros_python_version) # 각 변수 값을 포맷팅하여 문자열에 삽입
return ros_env_list # 최종적으로 포맷된 문자열을 반환
def get_dds_env_list():
"""
DDS 환경 변수를 가져와서 포맷된 문자열로 반환하는 함수.
:return: 포맷된 DDS 환경 변수 문자열
"""
# os.getenv() 함수를 사용하여 DDS와 관련된 환경 변수를 가져옴
# 변수값이 존재하지 않으면 기본값 'None'을 반환
ros_domain_id = os.getenv('ROS_DOMAIN_ID', 'None') # ROS 네트워크 도메인 ID
rmw_implementation = os.getenv('RMW_IMPLEMENTATION', 'None') # DDS 미들웨어 구현
# 가져온 변수들을 포맷팅하여 문자열로 반환
dds_env_list = (
'ROS_DOMAIN_ID = {0}\n'
'RMW_IMPLEMENTATION = {1}\n'
).format(ros_domain_id, rmw_implementation) # 각 변수 값을 포맷팅하여 문자열에 삽입
return dds_env_list # 최종적으로 포맷된 문자열을 반환
def get_all_env_list():
"""
모든 ROS 및 DDS 환경 변수를 가져와서 포맷된 문자열로 반환하는 함수.
:return: 포맷된 모든 환경 변수 문자열
"""
ros_env_list = get_ros_env_list() # ROS 환경 변수 가져오기
dds_env_list = get_dds_env_list() # DDS 환경 변수 가져오기
# 두 리스트를 합쳐서 하나의 문자열로 반환
all_env_list = ros_env_list + dds_env_list # ROS와 DDS 환경 변수를 합침
return all_env_list # 최종적으로 포맷된 문자열을 반환
def set_ros_env(env_name, env_value):
"""
환경 변수를 설정하는 함수.
:param env_name: 설정할 환경 변수의 이름
:param env_value: 설정할 환경 변수의 값
:return: 설정한 환경 변수의 이름과 값을 문자열로 반환
"""
# os.environ을 사용하여 환경 변수를 설정
os.environ[env_name] = env_value
# 설정한 변수의 값을 확인하여 문자열로 반환
return '{0} = {1}'.format(env_name, env_value) # env_value의 이름을 올바르게 변경

from ros2env.api import get_all_env_list # 모든 ROS 환경 변수를 가져오는 함수 임포트
from ros2env.api import set_ros_env # ROS 환경 변수를 설정하는 함수 임포트
from ros2env.verb import VerbExtension # VerbExtension 클래스를 임포트하여 명령어 확장
class SetVerb(VerbExtension):
"""ROS 환경 변수를 설정하는 명령어 클래스"""
def add_arguments(self, parser, cli_name):
# 명령어에 필요한 인자를 추가
parser.add_argument("env_name", help="설정할 환경 변수의 이름") # 환경 변수 이름
parser.add_argument("value", help="설정할 환경 변수의 값") # 환경 변수 값
def main(self, *, args):
# 인자가 제공된 경우 환경 변수를 설정
if args.env_name and args.value: # env_name과 value가 모두 제공된 경우
message = set_ros_env(args.env_name, args.value) # 환경 변수 설정 함수 호출
print("[ROS 환경 변수를 변경했습니다]:")
print(message) # 변경된 내용 출력
# 현재의 모든 ROS 환경 변수 목록 가져오기
current_env_vars = get_all_env_list() # 모든 환경 변수 목록 가져오기
print("\n[현재 ROS 환경 변수 목록]:")
print(current_env_vars) # 현재 환경 변수 목록 출력
add_arguments: ROS2 명령어에 필요한 옵션과 인수를 정의parser.add_argument() : 필수 인자 추가from ros2cli.plugin_system import PLUGIN_SYSTEM_VERSION # 현재 CLI 플러그인 시스템 버전
from ros2cli.plugin_system import satisfies_version # 현재 버전이 호환되는지 확인
class VerbExtension:
'''ROS2 CLI 명령어 확장을 정의하는 기본 클래스'''
NAME= "MyVerb" # 확장하는 verb의 이름
EXTENSION_POINT_VERSION = "0.1"# 확장이 지원하는 인터페이스의 버전
def __init__(self):
super(VerbExtension, self).__init__()
satisfies_version(PLUGIN_SYSTEM_VERSION, "^0.1")
def add_argments(self, parser, cli_name): # CLI 명령어가 인자를 받을 수 있도록 설정
parser.add_argument(
'--greet', type=str, help='Name to greet'
)
# pass
def main(self, *, args):
print(f"Hello, {args.greet}!")
# raise NotImplentedError()
__init__ : satisfies_version 함수를 사용해 현재 플러그인 시스템 버전이 호환되는지 확인
add_arguments: ROS2 명령어에 필요한 인자들을 정의하기 위한 메서드
main : 각 명령어 확장에서 반드시 구현하는 메서드로 , 명령어의 주요 로직을 수행하는 함수
from setuptools import find_packages, setup # setuptools에서 필요한 모듈을 임포트
# 패키지 이름 정의
package_name = 'ros2env'
# setup() 함수 호출하여 패키지 설정
setup(
name=package_name, # 패키지 이름
version='0.0.0', # 패키지 버전
packages=find_packages(exclude=['test']), # 패키지에 포함할 서브패키지 찾기, 'test' 폴더는 제외
data_files=[ # 설치 시 추가할 데이터 파일 목록
('share/ament_index/resource_index/packages',
['resource/' + package_name]), # 패키지 리소스 인덱스
('share/' + package_name, ['package.xml']), # 패키지 메타데이터
],
install_requires=['setuptools', 'rclpy'], # 패키지 설치에 필요한 의존성
zip_safe=True, # 패키지를 zip으로 설치할 수 있는지 여부
maintainer='namsang', # 패키지 유지 보수자 이름
maintainer_email='namsang@todo.todo', # 유지 보수자 이메일
description='Package for managing ROS2 environment variables', # 패키지 설명
license='MIT', # 라이센스 정보
tests_require=['pytest'], # 테스트 실행에 필요한 패키지
entry_points={
'ros2cli.command': [
# CLI에서 사용할 명령어 정의
'env = ros2env.command.env:EnvCommand', # 'env' 명령어를 EnvCommand로 연결
],
'ros2cli.extension_point': [
# ROS 2 CLI 확장을 위한 엔트리 포인트
'ros2env.verb = ros2env.verb:VerbExtension', # VerbExtension 연결
],
'ros2env.verb': [
# 사용자 정의 명령어 등록
'list = ros2env.verb.list:ListVerb', # 'list' 명령어를 ListVerb로 연결
'set = ros2env.verb.set:SetVerb', # 'set' 명령어를 SetVerb로 연결
],
},
)
