standby·doing·done. 종전 ready·going·arrived·paused 폐지. 도착이 goal 의 done입니다(§8.3).auth:doing, 상차 완료는 pickup:done, 하차 완료는 dropoff:done입니다. 종전 "도어 closed 전이" 기준과 box/loaded·box/unloaded 토픽은 폐지되었습니다(§4).offer와 같은 본문이 아니라 축약형입니다. 반면 응답(bid) 스키마는 배차 입찰과 동일합니다(§3.1).r2s/resource로 관제에 직접 요청하는 흐름으로 정정(§6).비스캣 관제에 robotCode(예: R-001)가 사전 등록된 뒤, 로봇은 robotCode와 공개키를 제출합니다. 관제는 robotCode로 로봇을 조회해 내부 식별자 robotId(UUID)를 회신하고 HMAC 비밀키를 발급하며, 로봇은 X.509 인증서 번들(다운로드 URL)을 내려받습니다. 이후 로봇은 인증·MQTT에 robotId를 사용합니다. 등록·인증 요청은 REST로 진행하고, 인증서를 설치한 뒤 MQTT에 연결합니다. 이후 미션·상태·알람 메시지는 모두 MQTT로 주고받습니다.
/key·/cert를 다시 호출하면 409 ROBOT_ALREADY_ACTIVATED로 거절됩니다. 재발급(인증서 회전)이 필요하면 관제 관리자 초기화 후 다시 발급받습니다.X-Robot-ID, X-Robot-Ts(ISO8601, +09:00 KST), X-Robot-Signature(HMAC-SHA256). timestamp는 ±60초 윈도우.r2s/{robotId}/# publish, s2r/{robotId}/# subscribe)만 사용할 수 있습니다.로봇이 보유한 지도와 의미 거점 노드(매장·하역장·충전소·대기 등 goal.type 거점)를 siteId 기준으로 관제에 등록합니다. 운영자가 로봇 UI의 업로드 버튼을 누르면 로봇이 보유 맵을 자동 업로드합니다. 등록 개시 시 siteId·제시 mapId와 업로드할 파일 목록을 선언하면 관제가 파일별 업로드 URL을 발급하고, 로봇은 지도 이미지·메타(yml)·노드(json)를 각각 해당 URL로 올립니다(압축하지 않음). 흐름은 등록 개시 → 파일 업로드 → 등록 확정 3단계입니다.
siteId·제시 mapId와 업로드할 파일 목록을 관제에 전송. 관제가 mapVersion과 파일별 업로드 URL을 발급.uploadExpiresIn 내 완료.location.map_id가 활성 mapId를 가리키고, 관제는 등록된 노드 좌표로 goal을 내립니다.재매핑 시 (siteId, mapId)당 mapVersion이 증가하며 이력을 보관합니다(롤백·감사 가능). 노드(json)에서 Place 객체를 가진 노드가 목적지로 등록되며, 관제는 그 좌표로 goal 의 location을 구성합니다.
미션이 생기면 관제가 수행 가능한 후보 로봇에게 미션(goal 배열)을 제시합니다. 흐름은 입찰 요청 → 입찰 응답 → 배차 결과 3단계입니다. 각 로봇은 예상 도착시간을 입찰하고, 어느 로봇이 수행할지의 최종 배차 결정은 관제가 단독으로 내려(최소 도착시간 기준, 추후 적재공간 포함) result로 통보합니다.
available)와 도착 예상 시각(estArrival — occurredAt과 동일 포맷의 절대 시각, API 문서 §5.2 규약)을 자체 계산해 회신. 본문은 견적 응답(quote/bid)과 동일 스키마입니다.claimed·reason으로 통보. claimed=true 통보가 곧 주행 개시 신호이며, 관제는 별도의 출발 지시를 보내지 않습니다.일부 로봇이 무응답이어도 bid 타임아웃(초기값 3초, 견적과 공용) 시점에 관제가 모인 입찰로 배차를 결정하고, 마감 후 도착한 지각 bid는 배제합니다. 라운드는 단발이며 재오퍼하지 않습니다 — 입찰 가능한 로봇이 없으면 해당 미션은 수행 불가로 종료됩니다. mission.atomic이 true면 해당 미션 goal들의 순서가 보존되어(거리 최적화 재정렬 시에도 묶음 유지) 다른 미션 goal이 사이에 끼지 못합니다.
주문이 확정되기 전, 관제가 후보 로봇에게 "지금 이 배송을 수행할 수 있는지"를 미리 물어 가용성과 예상 도착시간을 받아 두는 사전 견적 흐름입니다. 미션(goal)을 만들지 않는 비구속 질의라 배차 입찰(goals/*)과 토픽을 분리해 quote/*로 운영하며, 낙찰(result) 없이 질의 → 견적 2단계로 끝납니다.
s2r/{robot_id}/quote/offer) — quoteId·deliveryType과 목적지 요약(type·location·boxCount)뿐입니다. 아직 주문이 확정되지 않아 goal id·PIN·주문번호가 없습니다. 라운드 상관은 토픽의 robot_id로 합니다.available)와 도착 예상 시각(estArrival, 불가 시 null·사유)을 회신(r2s/{robot_id}/quote/bid). 배차 입찰 응답(goals/bid)과 완전히 동일한 스키마이며, id는 빈 문자열로 둡니다.배차 입찰과 달리 미션을 생성하지 않고 낙찰(result) 단계가 없습니다(2단계). 결과는 배차 확정이 아니라 가용성 집계로, 관제가 available=true 로봇 중 남은 시간(estArrival 환산) 최소를 채택합니다. 단발 라운드(재질의 없음)이며 bid 타임아웃(배차와 동일, 초기값 3초) 내 무응답은 available=false로 간주합니다. 다른 것은 질의 본문뿐이고 응답 스키마는 배차 입찰과 같습니다. 토픽·페이로드 상세는 API 문서 §5.4. 타임아웃 값 협의
배차 이후의 한 사이클입니다(mode: delivery). 한 오더(mission.id)는 매장 goal(type:store, mission.type:pickup)과 고객 goal(type:customer, mission.type:dropoff)로 구성됩니다. 로봇은 goal 배열을 arrivalOrder 순으로 수행하며, 진행 상태를 r2s/{robot_id}/goals로 발행합니다. 상태값은 세 계층 모두 standby·doing·done 한 벌을 씁니다(API 문서 §1). PIN은 mission.PIN으로 선전달되어 로봇이 로컬 검증하고, 사용 결과를 관제에 감사 이벤트로 보고합니다. 열 칸은 mission.door로 관제가 지정합니다.
goal.type:store)으로 주행 — goal status가 standby → doing.mission.type:auth · status:doing) 관제가 오토메타에 매장 도착을 통지.mission.PIN으로 로컬 검증 → mission.door로 지정된 칸 개방. 개방 시점에 pickup mission 이 doing으로 전이.pickup mission 이 done으로 전이. 이 전이가 배달 출발 트리거이며, 관제가 오토메타에 배달 출발을 통지. (종전 box/loaded 토픽은 폐지 — API 문서 §6.1)goal.type:customer)로 주행(goal doing), 도착은 매장과 같이 auth:doing으로 판정. 관제가 도착 통지.mission의 autoDropOff·PIN으로 결정 — 비대면(autoDropOff=true)은 도착 즉시 하차 후 복귀, WEB 버튼 인증(기본)은 관제 → 로봇 문열림 지시로 개방, 로봇 PIN 입력(예외 fallback)은 로컬 검증 후 개방. 개방 시점에 dropoff mission 이 doing.dropoff mission 이 done, 해당 goal 도 done이 됩니다. 미션의 모든 goal·mission 이 done이면 관제가 미션을 종료하고 state.status=done. 이후 처리는 아래 갈래.auth:doing, 완료 판정은 전체 done관제가 의미를 부여하는 미션 단계는 셋뿐입니다 — 도착(auth:doing) · 박스 열림(pickup/dropoff:doing) · 박스 닫힘·지점 완료(pickup/dropoff:done). 그 밖의 단계(move·gate_*·elevatorCall·층별 상태 등)는 done으로 보고되면 이름 그대로 운행 타임라인에 기록됩니다. 어휘 목록은 API 문서 §5 참조.
전 mission이 done이 되어 state.status=done이 되면 다음 처리는 세 갈래이며, 충전 복귀만 mode가 바뀝니다.
mode=delivery를 유지한 채 새 goal을 할당(모드 전이 없음).mode를 navi로 전이하고 goal.type=charging(mission.type=charge).mode=navi·goal.type=charging으로 충전소에 복귀.적재함의 어느 칸을 쓸지는 관제가 주문 수량 기반으로 결정해 mission.door(door1~doorN)로 지정합니다. 칸 수 N은 로봇마다 다르며 관제에 로봇별로 등록되어 있습니다(하드웨어 상한 4). 점주가 임의로 칸을 고를 수 없으며, 로봇은 PIN 로컬 검증 후 지정된 도어만 개방합니다. 주문 수량이 적재 용량을 초과하면 배차 단계에서 거부됩니다.
비대면(autoDropOff:true) / WEB 버튼 인증(기본, PIN:null·autoDropOff:false) / 로봇 PIN 입력(예외 fallback, PIN:랜덤)으로 goal.mission에 전달됩니다. 비대면은 autoDropOff:true인 경우만 해당하므로 (autoDropOff, PIN) 조합으로 3방식이 모두 구분됩니다. 상세는 API 문서 §6.
mode: trash_pickup. 흐름은 배송과 동일한 MQTT 입찰·배차·주행 구조(goal 배열)를 따르되, 매장↔세대 대신 세대(goal.type:customer, mission.type:pickup) → 수거장(goal.type:unloading, mission.type:dropoff) 경로입니다.
PIN: null·autoDropOff: true로 내려갑니다(로봇의 비대면 지원 설정과 무관하게 강제).arrivalOrder 순서와 quote/offer의 deliveryType: "trash_pickup"으로 구분됩니다.로봇은 필요한 시점에 r2s/{robot_id}/resource로 관제에 요청하고, 관제가 현대엘리베이터(MIRI)·로비폰(inBASE) 연동을 대신 수행한 뒤 s2r/{robot_id}/resource/ready로 진행 신호를 돌려줍니다. 로봇은 업체 API·IP·토큰을 몰라도 됩니다.
lobbyId(예: GOCHEOK-101)를 실어 요청 → 관제가 자동 개방 → result: "opened" | "failed". opened일 때만 진입합니다.elevatorCode)와 탑승층·목적층을 실어 요청 → 관제가 콜 수행 → result: "ok". 이후 중간 상태(serviceStatus)와 배정 호기(elId)를 s2r/{robot_id}/resource/status로 중계합니다.1.5차 PoC는 "로봇 비탑승(사람 동승)" 전제로 운영하지만, 비탑승 전제여도 완료 보고는 보내야 합니다. 엘리베이터는 이 보고를 받아야 도어를 닫고 다음 단계로 넘어가며, 오지 않으면 재확인 3회 뒤 콜을 취소합니다(취소된 콜은 되살릴 수 없어 처음부터 다시 호출해야 합니다).
보고 값은 MIRI serviceStatus 어휘를 그대로 씁니다 — 탑승 완료 sourceFloorGotOn, 하차 완료 destinationFloorGotOff. 이 두 전이는 MIRI 가 push 하지 않아 로봇 보고가 유일한 소식통입니다. 상세는 API 문서 §5.5.
goals/result에서 claimed=false + 사유(reason) 수신 시 해당 입찰은 종료(재오퍼 없음). 현재 사유 값은 outbid(더 빠른 로봇이 낙찰) 한 가지입니다.canceled로 전이하면서 reason(path-blocked·robot-fault·battery-low·elevator-unavailable·door-not-opened·timeout·operator-cancel·unknown)을 함께 발행합니다. 관제는 이 사유로 재배차 여부를 판단하고 운영자 화면(보류 미션)에 올립니다 — 사유 없는 취소는 기록되지 않습니다. 상세는 로봇 발행 항목 §6.resource/ready가 failed로 오면 로봇은 대기·재시도합니다. 같은 실패가 연속되면 관제가 자동으로 보류 미션 큐에 올려 운영자에게 노출합니다.resource 에 status: "cancelByRobot" 을 보내 콜을 끊습니다. 관제는 MIRI 취소를 대신 수행하고 result: "canceled" 로 회신합니다. 재호출은 5초 이상 띄웁니다.requestId 중복 흡수로 안전). 텔레메트리(상태)는 occurredAt 기준 최신값만 반영하므로 누락에 강건.r2s/{robot_id}/alarm으로 code·level(warn|error|fatal)·msg를 발행 → 관제가 레벨별로 대응(자동/수동 리셋). 레벨별 관제 대응 매핑은 시나리오 협의로 확정.
배송 두 케이스의 토픽·메시지 흐름을 단계별로 보입니다 — ① 단건 배송과 ② 주행 중 추가 주문(묶음 배송).
토픽 규약(s2r/r2s)·메시지 envelope·필드 정의는 API 문서 §5를 따릅니다. 등장 주체는
관제·로봇 Robot1이며, 주문 인입·앱 표출(오토메타)은 맥락 노트로만 표시합니다.
mode는 로봇이 어느 서비스로 활용되는지를 나타내는 상태값입니다. 배송 로봇은 배차 전 대기·이동, 그리고 다음 배송으로의 연속 배차까지 mode: delivery를 유지하며, 일감 유무·활동 진행은 state.status·goal.status로 표현합니다(mode로 나타내지 않음). 단 미션 완료 후 충전소로 복귀할 때는 delivery가 아니라 mode: navi + goal.type=charging으로 전이합니다(관제 지시 또는 로봇 timeout 자동 회차). 완료 후 처리 갈래는 §4 참조.
아래 JSON의 requestId·좌표·시각·PIN·estArrival 등은 형식 예시입니다. 상태/진행 발행은 변동 필드만 싣는 이벤트 모델이라 단계별로 필요한 필드만 노출했습니다. 적재 칸은 주문 단위로 관제가 배정하며(mission.door), 본 예시는 주문 Order1 → door1, 주문 Order2 → door2를 쓰고 같은 주문의 픽업·하차는 동일 칸을 씁니다.
고객 Order1 주문 → 입찰 → Robot1 확정 → 매장 도착·상차 → 배송 → Order1 도착·배송 완료. 한 주문(mission_Order1)이 매장 goal(goal_s1, pickup)과 고객 goal(goal_c1, dropoff) 두 goal에 같은 mission.id로 걸칩니다.
s2r/Robot1/goals/offer · 관제 → Robot1관제가 goal 전체(location 포함)를 제시합니다. location 좌표는 로봇이 사전 등록한 맵 노드 기준으로 관제가 채웁니다(§2 맵 등록). 낙찰되면 이 본문을 그대로 수행하므로 goal 재전송은 없습니다.
{
"requestId": "req-offer-Order1",
"occurredAt": "2026-06-17T10:00:00+09:00",
"payload": {
"goals": [
{
"id": "goal_s1", "type": "store", "status": "standby", "arrivalOrder": 1,
"location": { "x": 12.4, "y": 8.1, "th": 0.0, "map_id": "apt1023-b1" },
"mission": [
{ "id": "mission_Order1", "type": "pickup", "status": "standby", "atomic": false,
"PIN": null, "door": [ { "name": "door1", "status": "closed" } ], "orderId": "Order1",
"address": "", "addressDetail": "", "phoneNumber": "", "message": "" }
]
},
{
"id": "goal_c1", "type": "customer", "status": "standby", "arrivalOrder": 2,
"location": { "x": 40.2, "y": 22.7, "th": 1.57, "map_id": "apt1023-3f" },
"mission": [
{ "id": "mission_Order1", "type": "dropoff", "status": "standby", "atomic": false,
"PIN": "1482", "door": [ { "name": "door1", "status": "closed" } ], "orderId": "Order1",
"address": "103동", "addressDetail": "302호", "phoneNumber": "010-0000-1234", "message": "문 앞" }
]
}
]
}
}
r2s/Robot1/goals/bid · Robot1 → 관제Robot1이 goal별 수행 가능 여부(available)와 도착 예상 시각(estArrival — occurredAt과 동일 포맷의 절대 시각)을 자체 계산해 회신. 식별은 관제가 발급한 goal id이며 그대로 되돌려 보냅니다. 본문 형식은 견적 응답(quote/bid)과 동일합니다.
{
"requestId": "req-bid-Order1",
"occurredAt": "2026-06-17T10:00:03+09:00",
"payload": {
"goals": [
{ "id": "goal_s1", "arrivalOrder": 1, "available": true, "estArrival": "2026-06-17T10:04:00+09:00", "reason": null },
{ "id": "goal_c1", "arrivalOrder": 2, "available": true, "estArrival": "2026-06-17T10:11:00+09:00", "reason": null }
]
}
}
s2r/Robot1/goals/result · 관제 → Robot1{
"requestId": "req-result-Order1",
"occurredAt": "2026-06-17T10:00:05+09:00",
"payload": {
"goals": [
{ "id": "goal_s1", "claimed": true, "reason": null },
{ "id": "goal_c1", "claimed": true, "reason": null }
]
}
}
r2s/Robot1/state, r2s/Robot1/goals) · Robot1 → 관제배송 로봇이므로 mode는 delivery로 계속 유지되고, 일감을 잡으면 state.status가 standby → doing으로 전이합니다. 매장 goal 은 standby → doing으로 발행되고, 도착 시 인증 대기 단계(auth)가 doing이 됩니다. (텔레메트리는 requestId 생략)
// r2s/Robot1/state — 모드 전이
{ "occurredAt": "2026-06-17T10:00:06+09:00",
"payload": { "mode": "delivery", "status": "doing",
"location": { "x": 12.5, "y": 8.0, "th": 0.0, "map_id": "apt1023-b1" } } }
// r2s/Robot1/goals — 매장 도착(auth doing) → 관제가 매장 도착으로 판정
{ "occurredAt": "2026-06-17T10:04:00+09:00",
"payload": { "goals": [
{ "id": "goal_s1", "status": "doing", "progress": 100,
"mission": [
{ "id": "…move…", "type": "move", "status": "done" },
{ "id": "…auth…", "type": "auth", "status": "doing" },
{ "id": "mission_Order1", "type": "pickup", "status": "standby" }
] }
] } }
r2s/Robot1/goals) · Robot1 → 관제점주가 PIN을 입력하면 Robot1이 mission.PIN으로 로컬 검증 후 지정 칸(door1)을 개방하고, pickup mission 을 doing으로 발행합니다 — 관제는 이 전이를 박스 열림으로 처리합니다. 상차 후 박스를 닫으면 같은 mission 이 done이 되고, 이 전이가 배달 출발 트리거입니다. 매장 goal 도 함께 done이 됩니다.
// r2s/Robot1/goals — 박스 닫힘 → 픽업 완료 · 매장 goal 완료
{ "occurredAt": "2026-06-17T10:05:11+09:00",
"payload": { "goals": [
{ "id": "goal_s1", "status": "done",
"mission": [ { "id": "mission_Order1", "type": "pickup", "status": "done" } ] }
] } }
r2s/Robot1/goals) · Robot1 → 관제세대 goal이 standby → doing, 도착 시 auth:doing, 고객 수령 후 dropoff mission이 done이 되고 goal 도 done이 됩니다. 고객 수령은 기본 WEB 버튼 인증(관제 → 로봇 문열림 지시), 예외 시 로봇 PIN 입력입니다(§4 참조). 미션의 모든 goal·mission 이 done이면 관제가 오토메타에 배송 완료를 통지합니다. 이후 연속 배차가 있으면 mode=delivery를 유지한 채 새 goal을 받고, 없으면 충전소로 복귀합니다(mode=navi·goal.type=charging — §4).
// r2s/Robot1/goals — 하차 완료 → 미션 종료
{ "occurredAt": "2026-06-17T10:10:40+09:00",
"payload": { "goals": [
{ "id": "goal_s1", "status": "done",
"mission": [ { "id": "mission_Order1", "type": "pickup", "status": "done" } ] },
{ "id": "goal_c1", "status": "done",
"mission": [ { "id": "mission_Order1", "type": "dropoff", "status": "done" } ] }
] } }
Order1 배차 후 Robot1이 매장1로 이동하는 중 고객 Order2 주문이 들어와 같은 Robot1에 추가됩니다. 낙찰 후 Robot1이 Order1·Order2 goal을 직접 통합·정렬해
매장1 상차 → 매장2 상차 → Order1 배송 → Order2 배송 순서로 수행합니다(관제의 plan 재주입 없음). Order2는 다른 매장(goal_s2)·다른 세대(goal_c2), 적재 칸은 door2를 씁니다.
Order1 주문 → offer → bid → result(claimed=true) → Robot1이 goal_s1로 이동(doing). 형식은 8.1과 같아 생략합니다.
s2r/Robot1/goals/offer · 관제 → Robot1Robot1 주행 중 Order2 주문이 인입되면, 관제가 Order2의 두 goal만 제시합니다(Order1 goal은 Robot1이 이미 보유). arrivalOrder는 단건 주문과 동일하게 주문 내 순서(픽업 1·하차 2)로 보내며, 병합 시 관제가 전체 plan 기준으로 재배정합니다(④).
{
"requestId": "req-offer-Order2",
"occurredAt": "2026-06-17T10:01:00+09:00",
"payload": {
"goals": [
{
"id": "goal_s2", "type": "store", "status": "standby", "arrivalOrder": 1,
"location": { "x": 18.9, "y": 6.4, "th": 0.0, "map_id": "apt1023-b1" },
"mission": [
{ "id": "mission_Order2", "type": "pickup", "status": "standby", "atomic": false,
"PIN": null, "door": [ { "name": "door2", "status": "closed" } ], "orderId": "Order2",
"address": "", "addressDetail": "", "phoneNumber": "", "message": "" }
]
},
{
"id": "goal_c2", "type": "customer", "status": "standby", "arrivalOrder": 2,
"location": { "x": 44.0, "y": 30.1, "th": 3.14, "map_id": "apt1023-5f" },
"mission": [
{ "id": "mission_Order2", "type": "dropoff", "status": "standby", "atomic": false,
"PIN": "7035", "door": [ { "name": "door2", "status": "closed" } ], "orderId": "Order2",
"address": "105동", "addressDetail": "501호", "phoneNumber": "010-0000-5678", "message": "" }
]
}
]
}
}
Robot1이 현재 부하(Order1 수행 중)를 반영해 입찰하고, 관제가 낙찰을 통보합니다(형식은 8.1 ②③과 동일, goalId는 goal_s2·goal_c2).
낙찰(③) 후 Robot1은 이미 보유한 Order1 goal과 새로 받은 Order2 goal을 자체적으로 통합·정렬해 수행 순서를 정합니다. goal 정의는 offer 본문으로 이미 전달됐으므로, 관제는 별도 통합 plan을 주입하지 않습니다(goals/set 미사용) — 경로 최적화·재정렬은 로봇이 담당합니다. 각 주문의 내부 순서(픽업 → 하차)와 atomic 제약은 보존되며, Order1·Order2 모두 atomic: false라 픽업끼리·하차끼리 묶는 순서(A,C,B,D)가 허용되어 Robot1은 아래 수행 순서를 잡습니다.
| 수행 순서 | goal | type | mission | door | 의미 |
|---|---|---|---|---|---|
| 1 | goal_s1 | store | mission_Order1 · pickup | door1 | 매장1 상차 (Order1) |
| 2 | goal_s2 | store | mission_Order2 · pickup | door2 | 매장2 상차 (Order2) |
| 3 | goal_c1 | customer | mission_Order1 · dropoff | door1 | Order1 배송 |
| 4 | goal_c2 | customer | mission_Order2 · dropoff | door2 | Order2 배송 |
r2s/Robot1/goals) · Robot1 → 관제Robot1이 arrivalOrder 순으로 수행하며 각 단계 완료를 mission done으로 발행합니다. 매장2 상차 완료 시점에 두 주문(door1·door2)을 모두 적재한 상태가 됩니다.
// 매장1 상차 완료
{ "occurredAt": "2026-06-17T10:04:30+09:00",
"payload": { "goals": [
{ "id": "goal_s1", "status": "done",
"mission": [ { "id": "mission_Order1", "type": "pickup", "status": "done", "door": [ { "name": "door1", "status": "closed" } ] } ] }
] } }
// 매장2 상차 완료
{ "occurredAt": "2026-06-17T10:07:20+09:00",
"payload": { "goals": [
{ "id": "goal_s2", "status": "done",
"mission": [ { "id": "mission_Order2", "type": "pickup", "status": "done", "door": [ { "name": "door2", "status": "closed" } ] } ] }
] } }
// Order1 배송 완료 → 관제가 오토메타에 통지
{ "occurredAt": "2026-06-17T10:12:10+09:00",
"payload": { "goals": [
{ "id": "goal_c1", "status": "done",
"mission": [ { "id": "mission_Order1", "type": "dropoff", "status": "done", "door": [ { "name": "door1", "status": "closed" } ] } ] }
] } }
// Order2 배송 완료 → 관제가 오토메타에 통지. 전 mission done → state.status=done (연속 배차=delivery 유지 / 충전 복귀=navi·charging)
{ "occurredAt": "2026-06-17T10:16:40+09:00",
"payload": { "goals": [
{ "id": "goal_c2", "status": "done",
"mission": [ { "id": "mission_Order2", "type": "dropoff", "status": "done", "door": [ { "name": "door2", "status": "closed" } ] } ] }
] } }
| 단계 | mode | 주요 goal.status | 주요 mission.status |
|---|---|---|---|
| 배차 전 대기 | delivery | — | — |
| 배차 대기 | delivery | — | — |
| 낙찰·수행 시작 | delivery | goal_s* standby→doing | pickup standby |
| 매장 도착 | delivery | goal_s* doing | auth doing |
| 상차(열림→닫힘)·매장 완료 | delivery | goal_s* done | pickup doing→done |
| 세대 도착 | delivery | goal_c* doing | auth doing |
| 하차(열림→닫힘)·세대 완료 | delivery | goal_c* done | dropoff doing→done |
전 goal·mission 완료 (state.status=done) | delivery | 전부 done | 전부 done |
| 완료 후 — 연속 배차 | delivery 유지 | 새 goal standby→doing | 새 mission standby |
| 완료 후 — 충전 복귀 (관제/자동 회차) | navi | charging goal doing→done | charge |
지오펜스는 관제 콘솔에서 단지 운영자가 정의하는 다각형 영역입니다. 로봇은 이 영역을 내려받아 보유하고, 경로 계획·주행 중 스스로 판정합니다. 로봇이 발행하는 항목은 로봇 발행 항목 §9를 따릅니다.
| # | 주체 | 동작 |
|---|---|---|
| 1 | 관제 | 영역 등록·수정·삭제·활성 변경 → 해당 단지의 지오펜스 버전 증가 |
| 2 | 관제 → 로봇 | s2r/{robot_id}/geofence/version 발행 (retain) |
| 3 | 로봇 | 수신 버전이 보유 버전과 다르면 GET /v1/robot/geofences 호출 |
| 4 | 로봇 | 영역 적용 후 state.geofence.version 회신 |
| 5 | 로봇 | 경로 계획·주행 중 영역 판정 (§9.4) |
| 6 | 로봇 → 관제 | 우회·차단 발생 시 r2s/{robot_id}/geofence 발행 |
s2r/{robot_id}/geofence/version{
"occurredAt": "2026-09-22T10:00:00+09:00",
"payload": {
"siteId": "APT-GOCHEOK-001",
"version": 42
}
}
GET /v1/robot/geofences인증은 맵 등록(API 문서 §7)과 동일한 로봇 HMAC 서명 방식을 사용합니다. 요청·응답·에러 코드의 정식 정의는 API 문서 §8입니다.
{
"result": "SUCCESS",
"data": {
"siteId": "APT-GOCHEOK-001",
"version": 42,
"geofences": [
{
"id": "GF-01a0c3ef",
"kind": "no-go",
"env": "indoor",
"mapId": "ground",
"vertices": [
{ "x": 46.3, "y": 193.9 },
{ "x": 52.3, "y": 193.9 },
{ "x": 52.3, "y": 199.9 },
{ "x": 46.3, "y": 199.9 }
],
"effectiveFrom": null,
"effectiveTo": null
},
{
"id": "GF-01a0c318",
"kind": "hazard",
"env": "outdoor",
"vertices": [
{ "lat": 37.4983392, "lng": 126.8599869 },
{ "lat": 37.4978070, "lng": 126.8595129 },
{ "lat": 37.4971505, "lng": 126.8605206 }
],
"effectiveFrom": "2026-09-22T09:00:00+09:00",
"effectiveTo": "2026-09-30T18:00:00+09:00"
}
]
}
}
| 필드 | 타입 | 설명 |
|---|---|---|
version | integer | 이 응답이 담은 단지 지오펜스 버전. 로봇은 이 값을 적용 버전으로 삼습니다. |
geofences[].id | string | 영역 식별자. 우회·차단 발행 시 그대로 싣습니다. |
geofences[].kind | enum | no-go · avoid · hazard. 거동은 §9.4. |
geofences[].env | enum | indoor · outdoor. 정점 형식이 갈립니다(§9.5). |
geofences[].mapId | string | indoor에만 포함. 상태 발행의 location.map_id와 같은 값입니다. |
geofences[].vertices | array | 다각형 정점. 나열 순서가 변의 순서입니다. 3개 이상. |
geofences[].effectiveFrom | string | 효력 시작. null은 제한 없음. |
geofences[].effectiveTo | string | 효력 종료. null은 무기한. |
version이 §9.2 알림의 값과 다르면 그 사이 다시 바뀐 것입니다 — 응답 값을 적용하고 다음 알림을 기다립니다.| 종류 | 로봇 거동 | 발행 |
|---|---|---|
no-go | 경로 계획에서 배제합니다. 통과하는 경로를 만들지 않습니다. 이미 내부에 있으면 최단 경로로 벗어난 뒤 정지합니다. | blocked |
avoid | 경로 비용에 가중치를 둡니다. 우회가 가능하면 우회하고, 손실이 과도하면 통과를 허용합니다. | detour |
hazard | 통과를 허용하되 내부에서 속도를 제한합니다. 제한값 협의 | 없음 |
hazard가 포함된 변경 — 즉시 적용합니다.state.geofence.version 회신은 적용을 마친 뒤에 합니다.effectiveFrom·effectiveTo는 로봇이 자체 시계로 매 판정 시점에 대조합니다. 기간이 시작·종료되어도 버전은 바뀌지 않으므로, 버전 알림만으로는 반영되지 않습니다.
env | 정점 형식 | 기준 |
|---|---|---|
indoor | x · y (미터) | 맵 프레임 좌표. 상태 발행의 location.x·location.y와 같은 좌표계·같은 단위이며, mapId가 location.map_id와 일치하는 영역만 판정 대상입니다. |
outdoor | lat · lng | WGS84. 로봇이 location.lat·location.lng를 발행하는 구간에서만 판정됩니다. |