ROS #17 ROS2 CLI 작성법 실습

남생이·2024년 10월 24일

ROS

목록 보기
18/28

ros2 env라는 기존에 없던 ROS2 CLI를 만들기!

1. pkg 생성

이 과정은 앞선 포스팅에서 반복했던 내용이기에 코드는 생략합니다.

2. env.py

2.1 env.py 생성

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

2.2 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)

3. list.py

3.1 list.py 생성

$ cd command
$ mkdir verb
$ echo list.py

3.2 list.py 편집

  • list.py: ros2env 패키지의 일부로, ROS2 환경 변수들을 출력하는 ListVerb 클래스 정의
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)  # 최종 결과를 콘솔에 출력

4. api

4.1 api/init.py 생성

$ mkdir api
$ echo __init__.py

4.2 api/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의 이름을 올바르게 변경

5. set.py

5.1 set.py 생성

  • verb 폴더에 set.py 생성
  • set.py: ros2env 패키지를 사용하여 ROS2 Humble에서 환경 변수를 설정하고 출력하는 기능을 제공

5.2 set.py 편집

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() : 필수 인자 추가

6. init.py

  • init.py: ROS2 Humble에서 env 명령어에 대한 확장 포인트를 정의하기 위한 파일, ROS2 CLI 확장을 위한 기본 템플릿으로 사용
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 : 각 명령어 확장에서 반드시 구현하는 메서드로 , 명령어의 주요 로직을 수행하는 함수


7. setup.py

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로 연결
     ],
 },
)

profile
공부하는 거북이

0개의 댓글