19.2.1.3. 메시지 내 상수(Constants/Enums) 정의 및 비트마스크(Bitmask) 활용 방안

19.2.1.3. 메시지 내 상수(Constants/Enums) 정의 및 비트마스크(Bitmask) 활용 방안

uORB 구조체의 빈 캔버스를 스칼라 타입으로 채워 넣다 보면, 단순한 센서의 물리적 측정 연속값(온도, 전압, 가속도 등) 외에도 이 센서 하드웨어가 현재 정상 작동 망에 있는지, 영점 캘리브레이션(Calibration) 모드에 진입했는지, 혹은 칩셋이 타버린 치명적 에러 상태인지를 지시하는 이산적인 상태 코드(Status Code)나 모드(Mode) 플래그를 데이터 변수 속에 함께 겹쳐 실어 보내야 할 때가 빈번하고 필연적으로 발생하게 된다.

일반적인 대규모 PC 기반 C/C++ 프로젝트였다면 모듈 간의 협의를 위해 당연히 별도의 막대한 .h 시스템 공통 헤더 파일을 파고, 그곳에 고급스러운 C++11 enum class나 더러운 #define 매크로 뭉치를 때려 만들어 전역적으로 공유하려 들 것이다. 하지만 철저히 독립적이고 부품화된 uORB 메시지 생태계의 샌드박스 세계관에서는, 이런 식으로 외부 공통 헤더 파일에 상태값을 정의하여 끌어쓰는 짓은 메시지가 반드시 지녀야 할 **“자가 기술적(Self-describing) 아키텍처 독립성”**을 심각하고도 치명적으로 훼손하는 패역 행위로 간주된다.
즉, .msg 텍스트 규격 파일 하나만 인터넷을 통해 뚝 떼어 남에게 던져주더라도, 그 수신자는 어떠한 추가적인 헤더 참조 없이 오직 그 파일 안의 텍스트만 읽고서도 이 토픽 안의 상태값 0이 무엇을 뜻하고 1이 무엇을 뜻하는지 100% 암호 해독(Decode)할 수 있는 완결성을 지녀야만 한다.

이를 빌드 레벨에서 완벽하게 강제하고 보장하기 위해, PX4 genmsg 문법 생태계는 메시지 스크립트 텍스트 내부에 변수 선언과 동시에 그 변수가 취할 수 있는 상수(Constants)들을 직접 인라인(Inline)으로 욱여넣는 독자적인 메타 문법 선언 룰을 제공한다.

1. 열거형(Enumeration) 상태 코드의 자가 인라인 선언 문법

가장 널리 쓰이고 사랑받는 기법은, 특정 8비트 정수 변수에 들어갈 수 있는 후보 한계 상수(Constants)들을 그 변수 선언부 바로 밑줄에 대입 연산자 = 기호를 이용하여 폭력적으로 하드코딩(Hard-coding)해 버리는 것이다.

uint64 timestamp       # 시스템 절대 시간 타이머 앵커

# -----------------
# 센서 헬스 스위치 모드 상태 코드
uint8 system_state     # 현재 시스템 상태 분기를 담을 1바이트 그릇 변수 선언

# 바로 위 변수 그릇에 대입할 수 있는 후보군 상수값들을 바로 아래에 명세 (= 기호 활용)
uint8 STATE_UNINIT = 0     # 칩셋 전원 인가 직후, 아직 I2C 초기화 안 됨
uint8 STATE_OK = 1         # 100Hz로 정상 데이터 전송 작동 중
uint8 STATE_CALIBRATE = 2  # 오프셋 영점 튜닝(캘리브레이션) 모드 진행 중
uint8 STATE_CRITICAL = 3   # 복구 불가능한 치명적 하드웨어 화재 혹은 단선 에러

위 초보적인 텍스트 블록이 파이썬 컴파일러 템플릿을 무사히 통과하여 C++ 헤더로 융합 변환될 때, 파서는 = 0, = 1 구문들을 기가 막히게 스캐닝 캐치해 내어 해당 마스터 구조체 네임스페이스 내부에 static constexpr uint8_t STATE_OK = 1; 형태의 우아한 정적 상수 메모리로 완벽하게 치환시켜 놓는다. 이를 통해 코어 혈관의 퍼블리셔(Publisher)와 서브스크라이버(Subscriber) 모듈들은 서로 #include <system_states_common.h> 따위의 더럽고 무거운 외부 링킹 의존성 부하 없이, 오직 자동 생성된 이 메시지 헤더 단 하나만을 깔끔하게 인클루드하여 sensor_data_s::STATE_OK 형태로 명료하게 전역 상수표를 빼내어 쓸 수 있는 신비로운 특권을 얻게 되는 것이다.

2. 극한의 메모리 압축 해킹: 비트마스크(Bitmask) 병렬 선언

비행 제어기의 임베디드 램(RAM) 칩 지형도에서는, 방대한 데스크탑과 달리 고작 1바이트(8비트)의 숨쉴 구멍조차도 너무나 소중한 광활한 운동장이다. 만약 조명 LED 스위치 온/오프, 랜딩 기어 락, 낙하산 사출 락 등 서로 간섭 없이 완전히 독립적인 여러 개의 참/거짓(Boolean True/False) 상태 플래그를 한 번에 실어 보내야 한다고 가정해 보자. 이 단순한 플래그들을 일일이 무식하게 bool light_on, bool gear_on 식으로 밑으로 8개를 내리 선언하면, 아까운 8바이트 메모리 덩어리가 허무하게 증발해 버려 캐시 메모리 패킹의 최적화가 박살난다.

최상급 시스템 코어 해커들은 이런 멍청한 설계를 혐오하며, 이를 단 1바이트(uint8) 크기의 구멍 하나에 8개의 상태 플래그를 정교한 비트 연산(Bitwise) 자리 밀어넣기로 강제 압축해 쑤셔 넣는 비트마스크(Bitmask) 파킹 기법을 편집증적으로 사랑한다.

uint8 failure_flags    # 치명적 하드웨어 에러 상태를 무려 8개까지 한 번에 담을 수 있는 1바이트 비트 컨테이너 망

# 비트 자리수(Bit Shift)를 물리수학 상수로 1, 2, 4, 8 명세하여 마스킹 프로토콜 강제 체결
uint8 ERROR_GPS_LOST     = 1   # 이진수 0000 0001 (0번째 Bit 타격)
uint8 ERROR_MAG_ANOMALY  = 2   # 이진수 0000 0010 (1번째 Bit 타격)
uint8 ERROR_BATT_LOW     = 4   # 이진수 0000 0100 (2번째 Bit 타격)
uint8 ERROR_RC_LOST      = 8   # 이진수 0000 1000 (3번째 Bit 타격)

이 압도적이고 소름 돋는 극한의 압축 포맷을 수신한 mc_pos_control 구독자(Subscriber) 데몬은 스레드가 폴링(Polling)에서 깨어나자마자, 번잡한 if-else 분기 트리 필요 없이 오직 if (msg.failure_flags & msg::ERROR_GPS_LOST)라는 단 한 줄의 미친듯한 초고속 비트 마스킹 논리곱(Bitwise AND 연산) 판별식 어셈블리만을 수행한다. 이로써 GPS 모듈이 죽었는지 살았는지를 O(1) CPU 클럭 속도로 찰나에 판단해 내고, 즉각 생존(Failsafe) 궤도로 조종면 제어 방향을 무자비하게 틀어버리게 된다.

결국, 사용자 커스텀 메시지 문법 내부 깊숙한 곳의 인라인 상수 강제 명세 및 비트마스크 압축 정의 설계는, 초보자들을 편하게 해주는 단순 가독성 확보의 차원을 아득히 넘어서는 위대한 설계 유산이다. 전체 임베디드 1차 캐시(L1 Cache) 코어 메모리 패킹 사이즈를 극적으로 최소화시키고, 수십 개의 분산 스레드 모듈 간의 컴파일 통신 의존성(Coupling)을 이 세상에서 가장 아름답고 무자비하게 단절 결별시켜버리는 PX4 최상위 수준의 아키텍처 생존 철학의 정수인 것이다.