AMR 상태: 정지 요청과 실제 정지는 다르다
AMR 서비스가 요청의 진행 상태와 기기에서 관측한 상태를 따로 소유한다. STOPPING은 정지를 요청했다는 뜻이고, IDLE은 유효한 관측으로 확인해야 하는 값이다. 관측이 없거나 오래되면 UNKNOWN으로 읽는다.
이번 범위는 서버 내부의 상태 변경 계약이다. 제품 콜백 연결, 실제 정지 인증, Task 완료 처리, 자원 반환을 구현한 것은 아니다. 전체 프레임워크 구조는 Atlas, 공통 관리 서비스와 도메인의 관계는 상태·자원 소유권 지도를 함께 본다.
1. 폴더에서 찾는 책임
기준 경로: skix-framework/base/services/core/java/com/sk/airbot/frameworkservice/server/
domain/amr/
AmrManagerService.java 서비스 진입점·READY 검사·상태 소유자 조립
MobilityState.java 기존 도메인 상태 정의 — 호환 유지
state/
AmrStateAuthority.java 단일 잠금 안에서 권한·버전·전이 검사
AmrStateSnapshot.java 요청 단계 + 관측 상태의 불변 조회 결과
AmrObservation.java 상태·원본 순번·단조 시각
AmrObserverToken.java 현재 서비스 수명의 관측 권한
AmrOperationToken.java 이번 작업 시도의 상태 변경 권한
AmrStateException.java 타입이 지정된 거부 사유
execution/
MovementTaskSession.java Task 실행 permit·자원 점유 검사
ManagedMovementExecutor.java 청소 정지 → 이동 → 도착 → 구동부 정지 확인
execution/task/ TaskManagerService: 작업 실행 권한
control/resource/ ResourceManagerService: 자원 점유·재확인
control/state/ StateManagerService: 관측값 조회 사본
control/lifecycle/ 서비스 READY·세대 관리
domain/amr/state/는 AMR 내부 계약이다. 다른 도메인은 이 객체를 직접 수정하지 않고 AMR 서비스 진입점을 사용해야 한다. 이번 코드에서는 MovementTaskSession과 새 상태 소유자를 아직 자동 연결하지 않는다.
2. 서비스 API와 의미
| 진입점 | 호출자가 제공하는 것 | 성공 시 의미 |
|---|---|---|
openStateObserver() |
READY 서비스 | 현재 수명의 불투명 관측 토큰; 반복 호출은 같은 토큰 |
stateSnapshot(maxObservationAgeMs) |
양수인 관측 유효 기간 | 요청 단계와 최신 관측의 사본; 오래된 값은 UNKNOWN |
beginStateOperation(tasks, permit, expectedVersion, maxObservationAgeMs) |
같은 lifecycle의 TaskManagerService, 유효 TaskExecutionPermit, 현재 버전, 최신 IDLE | 새 작업 토큰 발급, ACCEPTED로 전이, 이전 관측 무효화 |
requestStateStop(operation, expectedVersion) |
현재 작업 토큰·버전 | STOPPING 기록, 이전 관측 무효화 |
observeState(observer, operation, expectedVersion, observation) |
현재 관측/작업 토큰, 버전, 관측값 | 관측 순서·시각·전이를 검사한 뒤 함께 갱신 |
requestStateStop은 하드웨어 명령을 보내지 않는다. 실제 중단 명령과 중단 확인은 이후 실행 경로에서 연결한다. beginStateOperation도 Task를 새로 claim하거나 자원을 예약하지 않는다.
Task가 취소된 뒤에도 이미 발급된 작업 토큰의 정지 의도와 관측을 기록할 수 있다. 이는 안전한 마무리를 위한 권한이며, 취소된 permit으로 새 작업을 시작할 수 있다는 뜻이 아니다.
3. 요청 단계와 관측 상태의 조합
| 입력 | 요청 단계 | 관측 상태 | 해석 |
|---|---|---|---|
| 초기화·재시작 | NONE | UNKNOWN | 아직 확인하지 않았음 |
| 초기 유효 관측 | NONE | IDLE 등 원본 상태 | 작업 없는 상태 확인; 작업 토큰은 null |
| 최신 IDLE에서 시작 접수 | ACCEPTED | UNKNOWN | 이전 IDLE은 새 시도의 관측이 아님 |
| 현재 작업의 이동·도킹·일시정지 관측 | ACCEPTED 또는 STOPPING 유지 | MOVING / DOCKING / PAUSED | 진행 중인 실제 상태 기록 |
| 정지 의도 접수 | STOPPING | UNKNOWN | 정지 요청만으로 IDLE을 만들지 않음 |
| 현재 작업의 유효 IDLE 관측 | QUIESCENT | IDLE | 상태 계약상 정지 관측 수신; Task 완료·자원 FREE와 별개 |
| ERROR 또는 UNKNOWN 관측 | FAULTED | ERROR / UNKNOWN | 정상 완료로 해석하지 않음 |
| QUIESCENT 뒤 비-IDLE 관측 | FAULTED | 수신한 상태 | 새 작업 시작 차단 |
| 관측 유효 기간 초과 | 기존 요청 단계 유지 | UNKNOWN | 조회만으로 버전이나 과거 요청 단계를 바꾸지 않음 |
FAULTED에서 유효 IDLE이 오면 QUIESCENT로 바뀐다. FAULTED에서 정지 의도를 기록할 수도 있다. 새 시작은 NONE 또는 QUIESCENT이면서 최신 IDLE일 때만 허용된다. QUIESCENT라는 이름은 상태 계약의 결과이지 센서나 구동부의 물리적 안전 인증이 아니다.
4. 동시에 들어오는 변경과 오래된 콜백
expectedVersion: 같은 버전을 읽은 두 변경 중 하나만 성공한다. 실패한 변경은 버전이나 관측 순번을 소비하지 않는다.- 서비스 세대와 토큰 객체: 재시작 전 관측/작업 토큰, 다른 서비스의 토큰, 이전 작업 토큰을 거부한다. 같은 문자열 ID만 맞추는 검사가 아니다.
sourceSequence: 관측자의 수명 전체에서 엄격히 증가한다. 새 작업을 시작해도 순번 기준을 초기화하지 않는다.- 관측 시각: 미래 시각, 이전에 수락한 관측보다 과거인 시각, 작업 시작/정지 의도 이전의 관측을 거부한다. 같은 시각은 더 큰 원본 순번일 때 허용한다.
- 시간 기준: 주입 가능한 단조 시계와 관측이 같은 밀리초 기준을 사용해야 한다. 제품 유효 기간은 호출자가 명시하며 임의의 기본 사양을 만들지 않는다.
- 수명: onStop/onStart와 supervisor 세대 변경은 이전 권한을 무효화하고 버전도 전진시킨다. 아직 관측이 없는 초기 상태에서도 이전 버전을 재사용하지 않는다. READY가 아닌 서비스는 새 변경을 받지 않는다.
일반 조회의 유효 기간 판정은 버전을 바꾸지 않지만, 조회 중 새 서비스 수명 경계를 처음 발견하면 권한 무효화에 따라 버전이 전진한다. 같은 수명에서 변동 없이 반복 조회하는 것은 버전을 소비하지 않는다. 거부된 이벤트 자체의 실패와 별도의 수명 변경을 구분한다.
이 검사는 프로세스 내부에서 발급한 권한을 검증한다. Binder 호출자 권한 인증, 원본 장치 이벤트의 진위, 센서 정확성을 대신하지 않는다. 단일 서비스 잠금도 Task 검사 → 자원 검사 → 물리 명령 전체의 원자성을 보장하지 않는다.
5. 기존 코드에서 무엇을 아직 연결해야 하는가
현재 DeviceAgent의 기존 이동 요청 경로는 즉시 실행 어댑터를 사용한다. 이 페이지의 상태 API로 모든 제품 요청이 전환된 상태가 아니다.
기존 AMR 상태 캐시는 초기 숫자 0을 IDLE로 표현할 수 있고, 이동 정리 경로는 하드웨어 정지 확인 없이 요청 상태를 IDLE로 초기화할 수 있다. 따라서 그 값을 그대로 새 권한 경계의 '검증된 정지'로 올리면 안 된다.
또한 기존 도착 콜백에는 작업 시도 식별자가 없다. 늦게 도착한 콜백에 수신 시점의 '현재 작업' 토큰을 붙이는 것만으로 상관관계가 증명되지 않는다. 제품 어댑터는 원본 관측의 출처·순번·시각·명령 시도 연결 방법부터 확보해야 한다.
| 후속 작업 | 필요한 근거 |
|---|---|
| 제품 콜백 → AMR 관측 어댑터 | 실제 명령 시도와 관측의 대응, 시간 기준, 재연결 세대 |
| MovementTaskSession → AMR 상태 소유자 | 실행 시작·실패·취소·중단 확인의 연결 순서와 경합 시험 |
| AMR → StateManagerService | 조회 사본의 발행 권한; 공통 StateManager의 작성자 통제는 아직 미구현 |
| 정지 확인 → 자원 재확인 | 최신 reconciliation 근거; QUIESCENT만 보고 자동 FREE 금지 |
| Cleaning·Vision 상태 소유권 | 각 도메인의 원본 상태·전이·정지/해제 의미를 별도로 분석 |
6. 검증 범위
구현 커밋은 c624e6e, 초기 상태의 재시작 버전 보강은 e30b4fa다. 최종 커밋의 서버 소스를 따로 추출해 보류 변경을 제외하고 2026-09-10 검증했다.
| 검증 | 결과 |
|---|---|
| AMR 상태·기존 AMR 서비스·이동 세션 집중 검사 | 29개 통과: 신규 상태 12 + 기존 서비스 4 + 세션 13 |
| 커밋된 서버 전체 JVM 테스트 | 397개 통과, 실패·오류·건너뜀 0 |
| SDK JVM 테스트 | 46개 통과, 실패·오류·건너뜀 0 |
| 서버 release Java 컴파일 | 통과 |
| 폴더·계층·공개 API·Bundle 경계 검사 | 4종 통과 |
이 결과는 APK 설치, 실제 구동부 정지 시험, AOSP 제품 부팅 검증을 포함하지 않는다. 보류 중인 LLM/TTS 변경도 포함하지 않는다. 독립 사양 검토에서 발견한 초기 상태의 재시작 버전 재사용 문제는 회귀 테스트로 재현하고 수정했다. 수정 후 사양 검토와 별도 품질·통합 검토를 모두 통과했다.