21.8.4.2. vehicle_command_ack_s 구독을 통한 트랜잭션 종료 판단
앞서 vehicle_command 구조체를 발행(Publish)하여 서보 밸브를 열어달라고 소리쳤다.
이 명령 데이터는 uORB 버스를 타고 액추에이터 제어 데몬(예: px4io, pwm_out_sim 등)에게 전달된다. 데몬은 명령을 치열하게 분석한 뒤, 실제로 하드웨어를 구동시켰는지 아니면 무슨 오류가 있어서 구동하지 못했는지에 대한 ’영수증’을 다시 uORB 버스에 올려놓는다.
이 영수증의 이름이 바로 vehicle_command_ack_s 토픽이다.
비행 제어 소프트웨어에서 “명령을 보냈다“는 사실은 중요하지 않다. “상대방이 명령을 정상적으로 수행했다는 영수증(ACK)을 받았다“는 사실만이 FSM 상태 천이를 위한 유일하고 완벽한 알리바이가 된다.
1. CONFIRM_ACK 상태의 사명
우리의 FSM 구조에서 STATE_TRIGGER 상태는 단지 명령서를 던지고 곧바로 STATE_CONFIRM_ACK 상태로 넘어왔다.
이제 이 CONFIRM_ACK 상태에 머무는 동안, 모듈은 귀를 쫑긋 세우고 오직 자신이 보낸 그 명령에 대한 영수증이 도착하기만을 필사적으로 기다린다.
#include <uORB/topics/vehicle_command_ack.h>
#include <uORB/Subscription.hpp>
// 헤더 파일에서 ACK 구독자 선언
uORB::Subscription _command_ack_sub{ORB_ID(vehicle_command_ack)};
2. 루프 내 영수증(ACK) 낚아채기 로직
FSM의 CONFIRM_ACK 상태 루프를 다음과 같이 설계한다. uORB 버스에는 세상 수많은 모듈들이 주고받는 수천 개의 ACK 영수증이 날아다닌다. 나는 오직 ’나의 명령어 번호(VEHICLE_CMD_DO_SET_ACTUATOR)’에 대한 영수증만 걸러서(Filter) 읽어야 한다.
void PayloadAutoDrop::run_state_confirm_ack() {
// 1. 읽지 않은 새로운 영수증(ACK)이 도착했는지 확인한다.
vehicle_command_ack_s ack{};
if (_command_ack_sub.update(&ack)) {
// 2. 이 영수증이 내가 보낸 서보 조작 명령에 대한 영수증이 맞는가?
if (ack.command == vehicle_command_s::VEHICLE_CMD_DO_SET_ACTUATOR) {
// 3. 영수증의 결과(Result) 코드를 분석한다!
switch (ack.result) {
case vehicle_command_ack_s::VEHICLE_CMD_RESULT_ACCEPTED:
// 대성공! 액추에이터가 밸브를 무사히 열었음을 100% 확신함.
PX4_INFO("Payload action CONFIRMED! Target destroyed.");
// 서보 모터 움직임이 완벽히 검증되었으므로,
// 미련 없이 다음 상태인 RTL(Return To Launch) 상태로 천이한다.
_current_state = AppState::RTL;
break;
case vehicle_command_ack_s::VEHICLE_CMD_RESULT_TEMPORARILY_REJECTED:
case vehicle_command_ack_s::VEHICLE_CMD_RESULT_DENIED:
// 실패! 하단 데몬이 바쁘거나 파라미터가 틀려서 명령이 거부됨.
PX4_ERR("Payload command REJECTED! Retrying...");
// FSM을 다시 뒷걸음질 치게 만들어 명령을 재입력(Retry)하도록 돌려보낸다.
_current_state = AppState::TRIGGER;
_state_just_entered = true;
break;
default:
// 알 수 없는 에러 상태
break;
}
}
}
// 이 상태에 머무는 동안 영수증이 올 때까지 하염없이 루프를 돌며 기다린다...
}
이 영수증 수거(ACK Handling) 로직이 추가됨으로써, 우리의 페이로드 모듈은 단순한 스크립트 쪼가리가 아니라 트랜잭션(Transaction)의 무결성을 보장하는 **클래스 1 항공 소프트웨어(Class 1 Aviation Software)**의 반열에 오르게 된다.
하지만 이 코드에는 아직 치명적인 논리적 구멍이 하나 존재한다.
만약 상대방(액추에이터 데몬)이 죽어버려서 영수증 자체가 영영 오지 않는다면 어떻게 될까? 우리의 FSM은 이 CONFIRM_ACK 구역의 늪에 영원히 빠져들어 먹통(Deadlock)이 되어 버린다.
영원한 기다림을 찢고 드론을 살려내기 위한 마지막 항공 방어술, “타임아웃(Timeout) 방어선과 GCS 에러 브로드캐스팅” 로직을 다음 장 21.8.4.2.1에서 마저 욱여넣어 보도록 하자.