28.7.3.2. `FlightModeManager.cpp`의 Switch-case 구문 내 `_flight_tasks.switchTask(FlightTaskIndex::Custom)` 인스턴스화

28.7.3.2. FlightModeManager.cpp의 Switch-case 구문 내 _flight_tasks.switchTask(FlightTaskIndex::Custom) 인스턴스화

Commander 모듈에서 커스텀 모드로 진입해도 좋다는 최종 허가(Transition Allowed)가 떨어지면, 기체의 시스템 뼈대를 관장하는 메인 루프는 새로운 비행 상태(NAVIGATION_STATE)로 완전히 전환된다.

이제 남은 마지막 퍼즐은, 이 바뀐 상태 번호를 감지하고 실제로 우리가 작성한 C++ 클래스(FlightTaskCustom)를 컴퓨터 메모리에 올려(인스턴스화) 실행시켜 줄 스위칭 로직(Switching Logic) 을 구현하는 것이다. 이 거대한 분배기 역할을 하는 코어 파일이 바로 FlightModeManager.cpp 이다. 본 절에서는 이 파일 내부의 스위칭 메커니즘을 낱낱이 파헤친다.


1. FlightModeManager의 역할과 구조

src/modules/flight_mode_manager/FlightModeManager.cpp는 위치 제어의 최전선(Front-line)에 있는 모듈이다. 이 모듈은 내부적으로 FlightTasks 라는 강력한 제어 래퍼(Wrapper) 클래스를 멤버 객체(_flight_tasks)로 들고 있으며, 기체의 현재 nav_state를 실시간으로 감시한다.

조종기 스위치 조작 등으로 인해 vehicle_status.nav_state가 변경되는 순간, FlightModeManager의 메인 루프(Run)는 즉시 하드웨어 인터럽트 수준의 반응 속도로 이를 낚아채어 내부의 거대한 switch-case 제어 블록으로 던져버린다.


2. 의존성(Dependency) 주입: 헤더 파일 포함

가장 먼저 해야 할 일은 FlightModeManager.cpp 파일의 상단 #include 영역에 우리가 심어놓은 커스텀 뇌(Brain)의 설계도를 가져오는 것이다.

// src/modules/flight_mode_manager/FlightModeManager.cpp 내부 (상단)

#include "tasks/Auto/FlightTaskAuto.hpp"
#include "tasks/AutoMapper/FlightTaskAutoMapper.hpp"
#include "tasks/ManualPosition/FlightTaskManualPosition.hpp"
// ... 기존 모드 헤더들 ...

// [NEW] 방금 2단계에서 만든 커스텀 Task의 헤더를 시스템 전역 스위치보드에 꽂아준다.
#include "tasks/Custom/FlightTaskCustom.hpp" 

이 한 줄이 있어야만 아래의 스위치 구문에서 FlightTaskIndex::Custom을 생성할 때 참조할 메모리의 크기와 함수들의 위치를 컴파일러가 알아차릴 수 있다.


3. 핵심 스위칭 블록: switchTask() 인스턴스화

FlightModeManager.cpp 소스 코드를 내리다 보면, 모드를 켜고 끄는 핵심 엔진인 FlightModeManager::Run() 또는 별도로 분리된 상태 처리기(State Handler) 내에 switch-case 문이 장황하게 나열되어 있는 것을 볼 수 있다.

이곳에 앞서 1단계에서 추가했던 통신용 상수(NAVIGATION_STATE_CUSTOM_FOO)를 매개변수로 받는 case를 하나 뚫고, _flight_tasks 매니저 객체에게 스위칭 명령을 하달한다.

// FlightModeManager.cpp 내부 스위치 구문 예시

void FlightModeManager::Run()
{
	// ... (uORB 데이터 폴링 및 사전 방어 로직) ...

	// 기체의 현재 상태(nav_state)를 기준으로 분기
	switch (_vehicle_status.nav_state) {
		case vehicle_status_s::NAVIGATION_STATE_MANUAL:
		case vehicle_status_s::NAVIGATION_STATE_ACRO:
		case vehicle_status_s::NAVIGATION_STATE_STAB:
			// 수동 모드들은 Flight Task를 사용하지 않고 바로 Attitude Control로직으로 뽑아낸다.
			_flight_tasks.switchTask(FlightTaskIndex::None);
			break;

		case vehicle_status_s::NAVIGATION_STATE_POSCTL:
			_flight_tasks.switchTask(FlightTaskIndex::ManualPosition);
			break;

		case vehicle_status_s::NAVIGATION_STATE_AUTO_MISSION:
			_flight_tasks.switchTask(FlightTaskIndex::AutoMapper);
			break;

		// ... (기존 자동 비행 모드들 생략) ...

		// [NEW] 우리의 커스텀 비행 모드 진입 브랜치 추가
		case vehicle_status_s::NAVIGATION_STATE_CUSTOM_FOO:
			// FlightTaskIndex::Custom 은 앞서 헤더에 뚫어놓은 고유 번호표이다.
			// 이 명령어가 실행되는 순간, 기존에 램(RAM)에 떠있던 구형 Task 객체의 empty()가 불리며 파괴되고,
			// FlightTaskCustom 객체의 생성자(Constructor)와 initialize()가 메모리에 로드(Load)된다.
			_flight_tasks.switchTask(FlightTaskIndex::Custom);
			break;

		default:
			// 정의되지 않은 알 수 없는 모드가 들어오면 Task를 죽여 기체를 긴급 수동 상태로 폴백(Fallback)시킨다.
			_flight_tasks.switchTask(FlightTaskIndex::None);
			break;
	}

	// ... (하위 로직: _flight_tasks.update() 를 호출하여 실제 궤적을 뿜어냄) ...
}

3.1 switchTask()의 이면에서 벌어지는 일 (Polymorphism)

_flight_tasks.switchTask(FlightTaskIndex::Custom)가 호출될 때 시스템 내부적으로는 C++의 다형성(Polymorphism)을 활용한 ’팩토리 패턴(Factory Pattern)’이 가동된다.

  1. 기존에 실행 중이던 객체 메모리 해제(소멸자 호출).
  2. new FlightTaskCustom() 을 통해 힙(Heap) 메모리에 우리의 커스텀 클래스를 동적 할당.
  3. 할당된 객체의 initialize() 멤버 함수 실행.
  4. 부모 클래스 포인터(FlightTask *)에 자식 클래스(FlightTaskCustom) 주소 바인딩.

이 과정을 거친 후 FlightModeManager의 메인 루프가 도는 내내 _flight_tasks.update() 만 단순히 호출하더라도, 가상 함수 테이블(vtable)에 의해 우리가 짜놓은 커스텀 로직의 update() 함수가 자동으로 매 주기마다 꼬박꼬박 불리게 되는 혁신적인 객체 지향의 마법이 완성된다.

이 지점까지 에러 없이 바이너리가 엮였다면, 펌웨어 단(C++)에서 커스텀 모드가 완벽히 동작할 수 있는 내부 신경망은 100% 개통된 것이다. 이제 남은 것은 외부 세계(GCS)와 소통하기 위한 4단계 MAVLink UI 반영뿐이다.