28.2.2.1. `FlightModeManager.hpp` / `FlightModeManager.cpp` 메인 클래스 해부

28.2.2.1. FlightModeManager.hpp / FlightModeManager.cpp 메인 클래스 해부

FlightModeManager 코어 클래스는 수백 개가 넘는 PX4 생태계의 복잡다단한 마이크로서비스 데몬들 중에서도, 가장 아름답고 정제된 교과서적인 객체 지향(OOP) 다이내믹 팩토리 패턴(Factory Pattern)과 비동기 시스템 스케줄링 인터페이스의 극의를 보여주는 우아한 C++ 소스 코드다.

src/modules/flight_mode_manager/ 디렉터리의 심장부이자, 조종사를 대신하여 모든 비행 궤적 통제의 법적 대리인 역할을 수행하는 이 두 거대한 메인 소스 파일의 뼈대와 내장(Anatomy)을 문법적으로 구체적으로 해부해 본다.

1. FlightModeManager.hpp: 시스템 아키텍처의 거시적 청사진(Blueprint)

클래스 헤더 파일은 FMM 데몬이 외부 RTOS 커널 스케줄러와 어떻게 소통할지, 그리고 내부적으로 램(RAM) 위에 어떤 메모리 상태 구조체 보따리를 짊어지고 다닐지 투명하게 보여주는 아키텍처 인터페이스 명세서다.

  • 다중 상속 구조(Multiple Inheritance):
    앞선 스케줄러 챕터에서 다루었듯 ModuleBase, ModuleParams, px4::WorkItem 이라는 세 개의 가장 강력하고 거대한 코어 부모 클래스들을 한 번에 다중 상속받아, NSH 셸 시스템 생명주기 제어 통신, 그라운드 컨트롤(QGC) 런타임 파라미터 동기화, 그리고 마이크로 OS 워크 큐 스케줄링 권력을 한 몸에 압축 부여받는다.
  • 핵심 커널 멤버 인터페이스(Public/Private Methods):
  • Run(): OS 스케줄러가 이벤트 인터럽트를 박고 데몬을 깨울 때 진입하는 유일무이한 절대적 오버라이딩 메인 타스크 게이트웨이 함수.
  • start(), custom_command(), print_status(): 커맨드 셸(NuttShell)에서 현재 백그라운드 핑 루프를 도는 데몬을 외부에서 감시하거나 죽이기(stop) 위한 POSIX 호환 표준 생명주기 제어 가상 인터페이스 규약 조작부 체계들.
  • switchTask(): 상부 조종사 커맨드인 nav_state 번호가 변경될 때마다 발동되어, 새로운 궤적 수학 객체를 힙(Heap) 공간에 다이내믹하게 생성해 올리고 썩은 다형성 포인터를 핫스와핑으로 갈아치워 버리는 데몬 내 가장 치명적이고 중요한 프라이빗(Private) 트랜잭션 함수.
  • 거대한 동적 다형성 포인터(Polymorphic C++ Pointer):
    객체 내부의 가장 중요한 최상급 멤버 변수로서 단 하나의 FlightTask *_current_task{nullptr}; 부모 포인터만이 달랑 선언되어 있다. 비행 모드가 수십 가지에 달하지만 FMM 뼈대는 그들을 물리적으로 포함하지 않으며, 이 단 하나의 다형성 C++ 마법 포인터 끈이 방금 막 생성된 구체적 자식 비행 모드(예: FlightTaskAutoMapper) 객체의 목줄을 잡은 채, 전체 3차원 비행 궤적 생성 연산을 무책임하고도 우아하게 위임 대리 처리한다.
  • uORB 파이프라인 통신 포트 객체:
    vehicle_status, vehicle_local_position, manual_control_setpoint 등 무려 수십 개의 센서 및 상위 상태 갱신을 귀 기울여 엿듣는 uORB::Subscription (입력 상태값) 객체 배열들과, 최종 계산된 목표 가속도 궤적을 뱉어낼 단일 출구 노드인 uORB::Publication<trajectory_setpoint_s> (출력 벡터 전송기) 래퍼 객체가 프라이빗 멤버로 견고히 자리 잡아 외부 세상과의 단단한 데이터 소통 척추 단자를 형성한다.

2. FlightModeManager.cpp: 상태 렌더링 전환과 팩토리 생산의 용광로

클래스의 실제 구현부인 CPP 파일 본문은 수식보다 철저하게 비동기 데이터 이벤트 폴링(Event Polling) 조건문과 다이내믹 C++ 포인터 핫스와핑(Hot-swapping) 행정 로직만으로 여백 없이 가득 차 있다.

2.1 Run() 루프의 극단적 절차적 단순함 (Stateless Execution Flow)

모든 권력이 집중된 Run() 메인 함수의 C++ 실제 내부는 놀라울 만큼 빈약하고 극단적으로 짧다.

  1. uORB 이벤트 데이터 검문 루프: _vehicle_status_sub.updated() API 등을 분기문으로 가볍게 계속 체크하여 외부 최고 사령부(Commander)로부터 비행 모드 변경 인텐트 데이터가 내려왔는지 스캐닝 감시한다.
  2. 동적 태스크 스위칭 발동: 만약 상태 번호 트리거가 바뀌었으면 무거운 트랜잭션 함수인 switchTask()를 즉각 발동시켜, 램 메모리 영역 위에서 돌아가던 수학 객체(FlightTask) 덩어리 본체를 통째로 파괴하고 새 객체로 다이내믹 갈아치워 치기 한다.
  3. 다형성 위임 궤적 산출 통보: 새로 갈아 끼운 포인터 끈을 쥐고 _current_task->update() 다형성 가상 함수를 묻지도 따지지도 않고 무지성으로 후려쳐 호출함으로써, 그 서브 객체가 자체적으로 조종기 스틱을 parse 하든 경로 매트릭스를 연산하든 알아서 아름다운 다음 궤적을 쥐어짜 내도록 방관한다.
  4. 최종 결과 출판 발행 및 수면(Sleep): 얻어낸 순수 3차원 목표 가속도 궤적(trajectory_setpoint)을 uORB 우체통에 시크하게 최종 퍼블리시(Publish)하고, ScheduleDelayed()를 다시 무의식 호출해 50ms 기상 알람을 커널 스케줄러 보드에 맞춰놓은 뒤, 리턴값 없이 깊은 스레드 반환 수면(return;)에 빠져든다.

2.2 switchTask() 동적 메모리 팩토리의 극한의 냉혹함과 자가 치유

과거 틱(Tick)까지 하늘을 쌩쌩하게 잘 날던 옛 수학 객체(old _current_task)는 포인터가 분리되는 순간 얄짤없이 가차 없이 delete 키워드로 소멸(Destruction C++ 호출)되어 힙 파편화 램 메모리를 깨끗하게 시스템에 즉각 반납당한다.
이후 C++ 내부의 거대한 자동 생성 switch-case 매크로 팩토리가 굉음을 내며 동작하여, 상부에서 새롭게 요구한 글로벌 상태(예: GCS 화면을 터치하여 내린 RTL 복귀 명령)에 딱 맞는 new FlightTaskAutoRtl() 객체 인스턴스 덩어리를 램 빈공간에 다이내믹 생성하여 포인터에 무자비하게 꽂아 넣는다.

만약 가장 끔찍한 시나리오로, 이때 새롭게 램에 생성된 모드 컴포넌트가 _current_task->activate() 필수 초기화 과정을 도는 중, GPS 안테나 통신 불량 에러나 비전 센서 패닉 등으로 인해 초기화가 펑크나 false 불허를 뿜어내며 뻗어버리면, 거대한 FMM 엔진은 즉각 콘솔 빨간 에러 로깅을 남긴다. 동시에 이 치명적 공중 데드락(Deadlock)에 당황하여 펌웨어 시스템 패닉 크래시로 빠지는 대신, 찰나에 똑똑하게 방향을 틀어 switchTask() 루틴 재귀 루프를 고도 보위 태스크(FlightTaskManualAltitude 등) 안전 샌드박스로 변경 재호출하여 록인(Lock-in)하고, 드론이 하늘에서 스스로 추락할 뻔한 절체절명의 수렁에서 우아하고 매끄럽게 시스템 차원의 방어 복구 매핑(Fallback Mapping Isolation) 처리를 극적으로 해낸다.