21.4.3.1. `custom_command()` 내부의 다중 분기 로직

21.4.3.1. custom_command() 내부의 다중 분기 로직

ModuleBase가 버리지 않고 넘겨준 커스텀 명령어 스위치(argv)들은 모두 custom_command(int argc, char *argv[]) 함수의 입구로 쏟아져 들어온다.

우리는 이 static 함수 안에서 문자열을 해부하여 적절한 내부 로직으로 연결하는 **분기점(Switching Hub)**을 만들어야 한다.

1. 텔레파시(Singleton)로 인스턴스 찾기

가장 먼저 짚고 넘어가야 할 거대한 제약 사항이 있다. 앞서 말했듯 custom_commandstatic 함수이기 때문에 메모리 구조상 특정 객체 모델에 귀속되지 않는다.
명령어를 처리하려면 현재 힙(Heap) 메모리에서 맹렬하게 돌아가고 있는 내 모듈의 진짜 몸체(객체 인스턴스) 주소를 먼저 찾아내야만 한다.

다행히 ModuleBase는 부모 클래스 깊숙한 곳에 자기 자식 객체의 포인터를 몰래 쥐고 있는 싱글톤(Singleton) 포인터 획득 함수get_instance()를 제공한다.

int CustomApp::custom_command(int argc, char *argv[])
{
    // 1. 커맨드가 아무것도 안 들어왔는가? (방어 코드)
    if (argc < 1) {
        return print_usage("missing command");
    }

    // 2. 현재 메모리에 떠 있는 내 진짜 객체의 포인터를 가져온다!
    CustomApp *inst = get_instance();

    // 3. 만약 모듈이 start로 켜지지도 않았는데 커스텀 명령부터 때렸다면?
    if (inst == nullptr) {
        PX4_ERR("Module is not running!");
        return -1; // 가차 없이 거부
    }

    // 4. 이제부터 본격적인 명령어 텍스트 분기(Branching)를 시작한다.
    const char *cmd = argv[0];
    // ...
}

get_instance() 호출은 마법과도 같다. 이제 우리는 inst-> 포인터를 통해 백그라운드 런루프 속에서 돌고 있는 객체의 프라이빗(Private) 멤버 변수와 멤버 함수들에 원격으로 직접(Direct) 접근할 수 있는 통제권을 획득했다.

2. strcmp 와의 극한의 눈치 게임

통제권을 얻었으니 이제 유저가 입력한 글자(argv[0])가 무엇인지 판별하여 로직을 갈라치기 할 차례다.
하지만 C++의 switch-case 문은 문자열(String)을 인자로 받지 못한다. 따라서 우리는 구시대적이고 원시적인 C 표준 라이브러리인 **strcmp(String Compare)**의 무한 if-else 사슬을 엮어낼 수밖에 없다.

    // 4. 본격적인 분기 시작
    const char *cmd = argv[0];

    // CASE 1: 센서 정렬 명령인가?
    if (!strcmp(cmd, "align_sensor")) {
        // 객체의 멤버 함수 원격 호출!
        inst->force_sensor_alignment(); 
        return 0; // 명령어 처리 성공
    }

    // CASE 2: 모터를 강제로 돌려보는 명령인가?
    if (!strcmp(cmd, "test_motor")) {
        // 하위 파라미터가 더 필요한 경우 (예: test_motor 2)
        if (argc >= 2) {
            int motor_index = atoi(argv[1]); // 위험! atoi 대신 앞서 배운 strtol을 쓸 것
            inst->test_single_motor(motor_index);
            return 0;
        } else {
            return print_usage("test_motor requires motor_index");
        }
    }

    // CASE 3: 내가 모르는 엉뚱한 명령인가?
    return print_usage("unrecognized command");

수많은 strcmp의 그물망을 짜다 보면 코드가 지저분해지기 십상이다. 하지만 저 if-else 체인이 길어지는 속도 저하에 대해서는 크게 걱정하지 않아도 된다. 왜냐하면 터미널 명령어는 비행 중에 1000Hz로 미친 듯이 들어오는 제어 루프가 아니라, 인간이 1분에 한두 번 타이핑하는 극도로 느린 이벤트 기반(Event-driven) 루틴이기 때문이다.

여기서 가장 중요한 것은 else 단락, 즉 사용자가 오타를 내거나 잘못된 옵션을 주었을 때의 피드백 처리이다. 코드 블록에 등장한 저 print_usage() 함수가 어떻게 작동하며, 터미널에 아름다운 가이드를 띄어주는지 다음 단원(21.4.3.1.1)에서 디테일을 살펴보자.