컴포넌트 저작도구 - 에디터 & 컴포우저 컴포넌트 저작도구 - 에디터 & 컴포우저 성진용 기자 2012-04-30 00:00:00

Open Platform for Robotic Service ②
컴포넌트 저작도구 - 에디터 & 컴포우저

 

지난 2007년 지능형로봇 개발을 위한 공통기반 플랫폼 기술개발의 과제로 시작된 오프로스(OPRoS; Open Platform for Robotic Sevices)는 로봇소프트웨어를 컴포넌트화하여 재사용과 동일기능을 하는 컴포넌트끼리 교체가 가능하도록 해 코드의 재사용성을 극대화하고, 빠른 로봇소프트웨어 개발이 가능하도록 개발도구를 개발하는 것을 목적으로 한다. 본문에서는 OPRoS 플랫폼을 사용해 컴포넌트를 쉽게 개발할 수 있는 컴포넌트 저작도구를 개략적으로 설명한다.

 

1. 컴포넌트 저작도구
오프로스(OPRoS; Open Platform for Robotic Services)는 오픈소스인 이클립스 기반으로 개발도구를 개발했다. 오프로스의 기본 개발도구들은 개발 내용에 맞도록 이클립스플러그인으로 개발되어 이클립스를 다운로드 받은 후 오프로스개발도구 플러그인들을이클립스의 플러그인폴더에 복사해 넣으면 이클립스기반의 오프로스 소프트웨어를 개발할 수 있는 도구가 준비된다. 오프로스는 개발도구들을 사용자들이 자신에 맞게 변경하여 사용할 수 있도록 자바 언어로 만들어진 소스 코드들을 제공한다.


오프로스 컴포넌트는 오프로스의 로봇 동작을 위한 최소의 소프트웨어 단위로서, 재사용성뿐만 아니라 동일기능을 하는 컴포넌트끼리 교체가 가능하도록 하여 코드의 재사용성을 극대화하고, 빠른 로봇소프트웨어개발이 가능하도록 하는 모듈이다. 즉, 컴포넌트는 독립된 단위기능의 소프트웨어 부품으로 정의할 수 있다. 이러한 컴포넌트의 개념은 현재 IT 분야에서 널리 채택하고 있는 기법이며, SW의 재활용성을 극대화하는 방법이다. 이는 컴포넌트가 컴포넌트 간 데이터 교환을 위한 인터페이스와 컴포넌트의 동작과 관련된 규격화된 방법 등이 표준화되어 있기 때문에, 어떠한 컴포넌트 간에도 레고 연결하듯이 쉽게 조립하여 동작시킬 수 있다.


그러나 로봇 SW 관련 개발자들은 아직 이러한 컴포넌트 개발 기술에 익숙하지 않아서 사용하는 것이 좀 힘들 수 있지만, 어느 정도 오프로스 컴포넌트의 동작을 이해하면 직접 프로그램하는 것보다 쉽다는 것을 알 수 있을 것이다. 특히 로봇 개발자들을 위해 개발도구에서는 컴포넌트의 기본코드를 자동생성 함으로써 컴포넌트 프로그램 개발을 쉽게 하도록 도와주고 있다. 컴포넌트의 기본 코드를 자동으로 제공하는 것은 사용자의 프로그램 개발을 쉽도록 도와주어 개발 시간을 단축하기 위한 것도 있으나, 사용자가 주의하지 않아 생길 수 있는 코드의 불안정화를 프로그래밍단계에서 방지하고자 하는 이유도 있다.

 

2. 그래픽 사용자 인터페이스(GUI)
오프로스 컴포넌트 저작도구는 사용자로부터 필요한 정보를 GUI 형태로 받고, 그 정보를 기반으로 소스를 자동 생성한다. 사용자로부터 받는 정보로는 컴포넌트의 이름, 컴포넌트의 포트의 종류와 이름, 그리고 컴포넌트의 수행 형태와 주기, 컴포넌트 수행에 필요한 초기값을 포함한 속성값 등이 있다. <그림 1>의 파란 컴포넌트 블록은 컴포넌트의 이름을 설정하면서 생성된다.


<그림 1>의 (가)요소들은 사용자가 컴포넌트에 추가하는 정보들로 컴포넌트에서 사용하는 포트들을 나열하고 있다. (가)요소들은 데이터 포트, 서비스 포트, 이벤트 포트, 이벤트 포트들이며 입력과 출력 포트로 구분된다. 데이터 포트들은 수행 주기에 따라 입력 데이터 포트로 들어온 데이터를 읽고 출력 데이터 포트로 출력할 경우에 사용하게 된다. 서비스 포트들은 비주기적으로 컴포넌트 A에서 컴포넌트 B의 서비스가 필요하면 컴포넌트 A의 출력 서비스 포트와 컴포넌트 B의 입력 서비스 포트를 사용하여 컴포넌트 B의 해당 서비스를 호출하여 결과를 받는다. 이벤트 포트는 이벤트가 발생 시 이벤트 값을 출력 이벤트 포트를 통하여 다른 컴포넌트의 입력 이벤트 포트로 전송한다.


(가)의 요소들을 파란 컴포넌트 블록으로 끌어서 오면 관련 포트들이 컴포넌트에 생성됨과 동시에 그 아래 보이는 팝업창이 생기면서 사용자가 포트에 필요한 요소들의 이름과 값을 넣을 수 있도록 한다. 이러한 방식으로 사용자가 추가한 정보를 바탕으로 해당정보를 자동으로 코드에 반영한다.

 

(나)항에는 컴포넌트 블록을 클릭했을 때는 컴포넌트에 대한 추가된 정보가 나열되고, 포트를 클릭했을 때는 포트의 정보가 나열되는 창이다.컴포넌트 저작도구의 GUI를 통하여 주요 요소들은 다음과 같다.
- 컴파일러 종류 및 실행 이름 등
- 실행 형태, 주기, 우선순위 등
- 초기값 등을 설정이 되는 속성 값 등
- 데이터/서비스/이벤트 포트의 내용 등
컴포넌트 저작도구는 사용자로부터 GUI를 통하여 정보를 입력 받아 그 컴포넌트 프로파일과 컴포넌트의 소스 코드 템플릿을 자동 생성하여, 프로그램 개발을 도와준다. 생성된 컴포넌트 템플릿에 알맞게 해당 컴포넌트의 동작 코드를 입력한 후 컴파일하면 컴포넌트의 실행파일이 생성된다. 즉 컴포넌트 저작도구로 통하여 만들어지는 파일은 컴포넌트 프로파일, 컴포넌트 실행파일과 부가적으로 포트의 정보를 명세한 서비스포트 프로파일 또는 데이터프로파일이 있다. 다음의 예는 WheelControllerTestComp의 컴포넌트 프로파일이다. 이 프로파일의 예에서 보듯이 다양한 정보가 포함되어 있다.

<그림 1>의 (나)항과 컴포넌트 프로파일의 (다)항은 같은 내용을 표시한다. 컴포넌트 프로파일 중에 의 내용은 해당 컴포넌트 혹은 요소의 위치와 크기를 나타내고, 의 내용에는 사용된 컴파일러의 종류, 실행 파일의 종류와 이름을 나타내고 있다. 를 나타내는 (다)항은 컴포넌트의 수행 형태(주기수행, 비주기 수행, 수동적), 주기 수행일 경우 주기 값(ms 단위), 우선순위, 컴포넌트의 발생 형태(하나만 생성 혹은 다수 생성), 라이프 사이클 종류(현재는 start로 고정해야 함)를 나타내고 있다. 이러한 값들은 사용자가 설정해야 한다. 또한 (마)항인 는 컴포넌트가 사용하고 있는 모든 포트를 나열하고 있다. 이 예에서는 2개의 출력 서비스 포트가 제공되고 있다.

 

3. 오프로스 컴포넌트의 주요 요소
<그림 1>에서 드래그 앤 드롭(Drag-and-Drop) 방식으로 컴포넌트의 요소를 추가하는데, 그 대표적인 정보는 속성과 포트라고 할 수 있다.

 

3-1. 컴포넌트 속성
컴포넌트 저작도구에서는 <그림 1>의 (가)항에 있는 항목 중 Property라는 항목을 파란 컴포넌트 블록으로 끌어오는 방식으로 컴포넌트의 속성값을 생성할 수 있다. 위의 컴포넌트 프로파일의 예제 안의 (라)항처럼 저작도구에 의하여 생성된다. 주로 사용자 입력 값이나 환경에 따라 달라지는 초기화 변수들을 그 이름과 값으로 프로파일에 명시해 두면 컴포넌트 소스 내부에서 getProperty(속성_name)으로 그 값을 얻어와 사용하면 컴포넌트 실행엔진에 동작할 때 동작에 반영하게 할 수 있도록 한다.

 

따라서 컴포넌트 코드를 재컴파일 없이 사용하고 싶은 변수도 컴포넌트의 속성을 활용할 수 있다. 속성의 종류와 숫자의 제한은 없으며, 오프로스가 개방형 플랫폼이라고 하는 것은 포트의 개수나 성격을 사용자가 마음대로 정할 수 있기 때문이다. 속성값은 물론 포트의 생성을 사용자가 원하는 대로 사용할 수 있다.

 

3-2. 컴포넌트 포트

로봇이 동작할 때, 여러 장치 또는 알고리즘이 그 안에서 하나의 서비스로 동작하듯이, 여러 종류의 오프로스 컴포넌트들이 서로 데이터를 주고 받으면서 하나의 서비스를 수행할 수 있다. 컴포넌트는 <그림 2>와 같이 세 가지 종류의 포트를 통하여 다른 컴포넌트와 통신을 한다. 다른 컴포넌트의 서비스를 수행하는 함수를 호출하는 서비스포트, 데이터 값을 여러 가지 형태의 큐에 저장하여 값을 전달하는 데이터포트, 그리고 컴포넌트 내의 이벤트 값을 전달하는 이벤트포트가 있다. 모든 포트들은 2가지 타입을 가진다.


서비스포트는 함수를 호출하는 Required 포트(출력포트에 해당)와 함수의 서비스를 제공하는 Provided 포트(입력포트에 해당)로 나뉜다. 사용하고자 하는 함수들은 서비스포트 프로파일을 통하여 Required 와 Provided 포트가 서비스포트를 통하여 호출될 수 있는 함수의 종류들을 공유한다. 즉, 컴포넌트A가 컴포넌트B와 함수 호출을 하고 싶으면 컴포넌트A가 가진 서비스포트 프로파일을 보면 사용자가 함수를 사용하는 컴포넌트를 작성할 수 있다. 컴포넌트가 코드의 자동생성을 하기 전에 서비스포트 프로파일이 제공되어야 컴포넌트가 포트의 성격을 반영하여 코드를 생성한다. 서비스 호출될 때는, 호출을 받는 Provided 포트를 가진 컴포넌트는 바로 해당 함수를 수행한다. 서비스포트는 서버와 클라이언트의 개념이어서, 하나의 Provided 포트는 여러 개의 Required 포트와 연결될 수 있다.

 
데이터포트는 큐(Queue)구조의 데이터 저장 공간을 가지고 있다. 두 컴포넌트가 데이터를 주기적으로 주고받는 경우에 적절한 포트타입이다. 주로 onExecute라는 호출함수(4절에서 설명)에서 주로 구현되며, 지원되는 데이터타입은 기본 데이터뿐 만 아니라 사용자 정의 데이터 타입도 지원하는데 후자의 경우는 데이터포트 프로파일을 작성하여 컴포넌트가 해당 성격의 데이터 타입의 포트를 코드생성에 반영하도록 한다.


이벤트포트는 비주기적으로 전달되어야 할 데이터를 전달할 때 사용되는데, 특히 onEvent라는 호출함수를 각 컴포넌트마다 가지고 있어서 이벤트 입력 포트로 이벤트가 도착하면 바로 처리된다. 역시 사용자 정의 데이터타입도 지원하므로 프로파일에 그 데이터타입의 구성을 명시해야 한다.포트들은 컴포넌트 저작도구의 드래그앤드롭형식으로 사용자가 입력 값을 넣으면 프로파일에 그 내용이 명시되고, 컴포넌트 소스에 그 내용이 반영되어 컴파일 된 컴포넌트 실행파일이 생성된다. 컴포넌트 간 포트의 연결은 컴포넌트 컴포우저에서 더 자세히 설명한다.


4. 컴포넌트 소스코드 개발
4-1. 컴포넌트의 소스 코드 템플릿
사용자의 설정값 입력이 끝나면 저장을 하여 수정내용이 반영되도록 한다. 특히 최근 업데이트된 컴포넌트 저작도구는 소스에서 포트를 생성하면 GUI에서 그 생성내용이 반영되도록 하여 소스와 GUI의 동기화를 적용하였다. GUI를 통하여 생성된 코드는 다음 <그림 3>과 같다.

 

위의 소스 창에 있는 코드를 자세히 살펴보면, 아래의 코드는 WheelControllerTestComp의 자동으로 생성된 코드의 일부분이다. GUI를 통하여 포트의 설정부분이 portsetup() 함수에 반영된 것을 알 수 있다. <그림 1>의 파란 컴포넌트 블록에 연결된 빨간 2개의 Required 포트가 자동생성된 코드는 다음과 같다. 즉 2개의 제공된 서비스는 LaserScannerService와 WheelControllerService임을 알 수 있다.


그 이외에도 컴포넌트 저작도구가 생성하는 코드들은 다음과 같이 11개의 호출함수가 있다. 오프로스 컴포넌트들은 이 호출함수들은 컴포넌트 실행엔진이 컴포넌트의 특성에 맞도록 운영하기 위해서 필요한 함수들인데, 로봇의 프로그래밍에서 매우 주요한 초기화나 정상적인 종료, 주기적으로 하드웨어장치 값 획득, 에러발생시 처리사항 등을 호출함수로 명시하여 사용자가 누락하지 않고 작성하게 하며, 또 실행엔진은 컴포넌트의 상태에 따라 해당 호출함수를 수행한다. 즉 사용자가 주기를 수행하기 위한 루프함수들을 구현할 것 없이 onExecute함수에 그 루틴을 구현하면 실행엔진이 주기적으로 해당 내용을 수행할 수 있다.
각 함수에서 하는 기능은 다음과 같다. 이러한 기능은 <그림 4>와 연계하여 알맞게 각 함수를 구현해야 한다. 즉, 이러한 11개의 함수는 <그림 4>의 생명주기를 동작시킨다.
onInitilaize(): 컴포넌트가 초기화 될 때
onStart(): 컴포넌트가 활성화될 때
onStop(): 컴포넌트가 비활성화될 때
onDestroy(): 컴포넌트가 종료될 때
onReset(): 컴포넌트가 리셋 될 때
onError(): 컴포넌트의 에러가 발생했을 때
onRecover(): 컴포넌트의 에러복구 할 때
onEvent(): 컴포넌트가 이벤트를 받았을 때
onExecute(): 컴포넌트의 주기적 동작 호출
onUpdate(): onExecute가 호출된 후처리
onPeriodChange(): 주기를 변경 (미사용중) 
몇 가지 호출 함수들의 용도를 하드웨어인 경우로 설명하면, onInitialize()에는 변수의 장치 핸들러의 인스턴스생성 등에 관한 코드가 들어가고, onStart에는 장치를 활성화 하고 그 초기  값 등의 설정에 관련된 코드들이 삽입되면 된다. onExecute는 하드웨어로부터 감지된 값들을 읽어 오거나, 다른 컴포넌트로 전달하는 일등 주기적으로 수행해야 할 일, 그리고 onUpdate은 onExecute에서 다른 컴포넌트로부터 데이터를 받아오거나 다른 컴포넌트로 데이터를 보내고 난 후, 내부적으로 변동되어야 할 값들을 처리한다. onEvent는 다른 컴포넌트로부터 이벤트를 받았을 때 처리해야 할 루틴을 입력한다.


추가적으로 사용자들이 추가해야 할 함수들은 서비스 포트들로 제공해야할 함수 등이다. 예를 들면, 앞에서 설명한 포트 부분으로부터 LaserScannerService와 WheelControllerService함수들에 대해 템플릿이 자동으로 생성되는데 이러한 템플릿 코드에 알맞게 관련 코드들을 추가하면 된다. 즉, 컴포넌트 저작도구는 GUI에 의해 사용할 포트들과 속성값 등을 설정하면, 관련 소스 코드들을 템플릿으로 제공하고 사용자는 그 템플릿을 활용하여 알맞게 관련 코드를 넣어 주기만 하면 된다.


4.2. 컴포넌트의 생명주기
이 컴포넌트 호출함수들은 컴포넌트 실행엔진에 의해서 <그림 4>와 같이 컴포넌트의 생명주기에 따라 컴포넌트를 수행시킨다. 컴포넌트는 6개의 상태를 가지며, 그 상태는 11개의 호출함수들 중 해당 함수를 수행함으로써 다음 상태로 전이하게 된다. 컴포넌트의 시작인 생성(Created) 상태에서는 onInitialize함수를 수행하여 컴포넌트가 메모리에 로딩될 때 필요한 조건들 즉 인스턴스 생성 등에 관한 코드들을 수행한다. 반대로 컴포넌트가 메모리에서 지워질 때는 onDestroy함수가 호출되어 생명주기를 끝낸다. 컴포넌트의 시작(Ready) 상태에서는 onStart 함수를 호출하여 초기값 설정에 필요한 코드들을 수행하는데, 생성과 시작이라는 상태를 구분함으로써 컴포넌트가 메모리에 있는 한 새로이 동작이 가능하도록 한다. 반대로 onStop 함수를 호출하면 컴포넌트는 비활성 상태가 되어 모든 동작이 정지하게 된다.


컴포넌트의 실행엔진은 컴포넌트 프로파일에 명시된 컴포넌트 타입에 따라 컴포넌트를 동작시키는데 그 타입으로는 주기적(Periodic), 비주기(Aperiodic), 그리고 수동적(Passive)타입이 있다. 컴포넌트가 주기적 성격을 가지면 컴포넌트 프로파일에 명시된 주기시간에 따라 한번씩 onExecute와onUpdate를 수행하면서 계속적으로 활성화(active)상태에 머물게 된다. 컴포넌트가 비주기적 성격이면 onExecute를 1회만 수행하고 활성화 상태로 있게 되며, 컴포넌트가 수동적 성격이면 다른 컴포넌트가 시작 상태에서 활성화상태로 onExecute 또는 onUpdate를 호출하여 상태가 전이되는 컴포넌트들을 말한다.


컴포넌트저작도구는 사용자가 GUI를 통하여 컴포넌트의 성격 및 초기값을 설정하면 이것을 기반으로 템플릿을 자동 생성하여 사용자가 11개 호출함수의 성격에 맞는 코드를 구현하여 컴파일을 하면 컴포넌트 실행파일과 컴포넌트의 정보를 제공하는 프로파일을 생성한다. 현재 오프로스가 지원하는 컴포넌트 저작도구는 코드형식으로는 C 프로그래밍 언어와 프로파일의 형식으로는 명세로는 xml형식을 지원하고 있다.


<그림 4>와 앞에서 설명하였듯이 사용자들은 11개의 함수와 제공하는 서비스 메쏘드들의 주요 내용(알고리즘)만 개발하면 통신 부분과 오류 처리 부분은 컴포넌트의 생명주기에 따라 실행엔진에서 해결하여 준다. 따라서 사용자들은 컴포넌트의 오류가 생겼을 때 고민할 필요가 없고, 동작 순서 등도 고민할 필요가 없기 때문에 신뢰성 있는 컴포넌트 개발을 보다 쉽게 할 수 있다. 또한 속성값을 파일 이름을 통하여 제공하고 속성값을 읽는 상태를 변경하면 같은 컴포넌트이지만 매우 다양한 동작을 하는 컴포넌트도 쉽게 만들 수 있는 등의 장점도 있다.


4.3. 추가 도움 기능
컴포넌트 저작도구가 더 많은 자동생성코드를 지원하고자 컴포넌트를 분류하여 초기화에 필요한 함수들을 추가한다. 즉, 오프로스 컴포넌트를 표준화 하여 표준화된 컴포넌트에서 사용되는 인터페이스들을 미리 생성하도록 하여 사용자가 필수 함수들을 누락하는 일이 없도록 지원한다. 그림 5는 컴포넌트 저작도구가 컴포넌트 생성 초기단계에서 컴포넌트의 세부분류를 설정하게 하여 코드생성에 반영하는 옵션이다.


오프로스 저작도구는 다른 로봇 플랫폼이 일반프로그램일 저작도구를 사용하는 것에서 앞서서GUI를 적용하여 보다 쉬운 로봇 프로그래밍이 될 수 있도록 노력하였다. 또한 컴포넌트에 대한 GUI로 추가 삭제된 코드가 소스코드의 해당부분에 동시에 업데이트 되도록 포트에 관해서만 동기화를 적용하였다.

 

5. 맺은 말
현재 로봇 개발자는 컴포넌트 개념에 익숙하지 않아서 힘들 수는 있지만 하나의 부품으로써 컴포넌트를 개발하고 컴포넌트들을 조립하여 응용 콘텐츠를 쉽게 개발할 수 있고 재활용을 극대화한다는 점에서 컴포넌트를 이해할 필요가 있다. 프로그램이란 것은 익숙해지면 모든 것이 쉽기 때문에 이러한 컴포넌트 개발 방법에 익숙해지면, 하나의 로봇에 들어가는 모든 SW들을 개발하는데 시간이 적게 소요됨을 알 수 있다.