제로웍스 연동 개발 시나리오

제로웍스 로봇이 비스캣 관제와 결합해 한 사이클을 도는 동작 흐름입니다. 토픽·페이로드 상세는 연동 API 문서를, 메인은 개발 가이드를 참조하세요.

1. 등록·인증 (최초 1회)

비스캣 관제에 robotCode(예: R-001)가 사전 등록된 뒤, 로봇은 robotCode와 공개키를 제출합니다. 관제는 robotCode로 로봇을 조회해 내부 식별자 robotId(UUID)를 회신하고 HMAC 비밀키를 발급하며, 로봇은 X.509 인증서 번들(다운로드 URL)을 내려받습니다. 이후 로봇은 인증·MQTT에 robotId를 사용합니다. 등록·인증 요청은 REST로 진행하고, 인증서를 설치한 뒤 MQTT에 연결합니다. 이후 미션·상태·알람 메시지는 모두 MQTT로 주고받습니다.

sequenceDiagram participant R as 로봇(제로웍스) participant C as 비스캣 관제 Note over R,C: robotCode는 관제에 사전 등록된 상태 R->>C: POST /v1/robot/key (robotCode, publicKey) C-->>R: robotId(UUID) + 공개키로 암호화된 HMAC 비밀키 R->>C: POST /v1/robot/cert (HMAC 서명) C-->>R: 인증서 번들 다운로드 URL 목록 Note over R: robotId·비밀키·인증서 보관 (TPM 권장)

2. 맵 등록 (REST · 로봇 UI 버튼)

로봇이 보유한 지도와 의미 거점 노드(매장·하역장·충전소·대기 등 goal.type 거점)를 siteId 기준으로 관제에 등록합니다. 운영자가 로봇 UI의 업로드 버튼을 누르면 로봇이 보유 맵을 자동 업로드합니다. 등록 개시 시 siteId·제시 mapId업로드할 파일 목록을 선언하면 관제가 파일별 업로드 URL을 발급하고, 로봇은 지도 이미지·메타(yml)·노드(json)를 각각 해당 URL로 올립니다(압축하지 않음). 흐름은 등록 개시 → 파일 업로드 → 등록 확정 3단계입니다.

sequenceDiagram participant R as 로봇(제로웍스) participant C as 비스캣 관제 participant U as 업로드 URL Note over R: 운영자가 로봇 UI 업로드 버튼 클릭 R->>C: ① POST /v1/robot/maps (siteId, mapId, files[]) Note over C: mapVersion 발급, 파일별 업로드 URL 생성 C-->>R: mapVersion, uploads[](파일별 URL), uploadExpiresIn R->>U: ② 각 파일(이미지·yml·json) 개별 업로드 U-->>R: 200 OK R->>C: ③ POST /v1/robot/maps/{mapId}/commit Note over C: 선언 파일 전부 업로드 확인 + 검증
→ 활성화(이전 버전 비활성)
누락 시 오류, 비선언 파일 미반영·삭제 C-->>R: isActive=true, activatedAt
  1. 등록 개시: 로봇이 siteId·제시 mapId와 업로드할 파일 목록을 관제에 전송. 관제가 mapVersion파일별 업로드 URL을 발급.
  2. 파일 업로드: 로봇이 지도 이미지·메타(yml)·노드(json)를 각 업로드 URL로 개별 전송(압축하지 않음). uploadExpiresIn 내 완료.
  3. 등록 확정: 로봇이 commit 호출 → 관제가 선언 파일이 모두 업로드됐는지 확인하고 검증해 통과 시 활성화(이전 버전 비활성). 누락 시 오류, 선언에 없는 파일은 미반영·삭제. 이후 MQTT location.map_id가 활성 mapId를 가리키고, 관제는 등록된 노드 좌표로 goal을 내립니다.

맵 버전·노드

재매핑 시 siteIdmapVersion이 증가하며 이력을 보관합니다(롤백·감사 가능). 노드 typegoal.type(store·unloading·charging·waiting 등)과 동일 어휘이며, 관제가 goal의 location을 구성하는 기준이 됩니다. 지도 파일 포맷은 협의로 확정합니다. 상세는 API 문서 §7.

3. MQTT 입찰·배차 (offer → bid → result)

미션이 생기면 관제가 수행 가능한 후보 로봇에게 미션(goal 배열)을 제시합니다. 흐름은 입찰 요청 → 입찰 응답 → 배차 결과 3단계입니다. 각 로봇은 예상 도착시간을 입찰하고, 어느 로봇이 수행할지의 최종 배차 결정은 관제가 단독으로 내려(최소 도착시간 기준, 추후 적재공간 포함) result로 통보합니다.

sequenceDiagram participant C as 관제 participant A as 로봇 A participant B as 로봇 B Note over C: 미션 생성 → goals 구성
후보 산정(모드 일치 + 생존 로봇) par 입찰 요청 (offer · set과 동일 본문) C-->>A: goals[] and C-->>B: goals[] end Note over A,B: 각 로봇이 goal별 예상 도착시간 자체 계산 A-->>C: 입찰 (bid) goals[]{id, estArrival=12} B-->>C: 입찰 (bid) goals[]{id, estArrival=8} Note over C: 관제가 최소 도착시간으로 배차 결정
(무응답·타임아웃 정책 적용) par 입찰 결과 (result) C-->>A: claimed=false, reason C-->>B: claimed=true, reason=null end Note over B: 낙찰(claimed=true) 받은 B만 수행 시작
  1. 입찰 요청(offer): 관제가 후보 로봇에게 goal 배열을 제시(goals/set과 동일 본문). 입찰·낙찰은 goalId 단위로 식별합니다.
  2. 입찰 응답(bid): 각 로봇이 goal별 예상 도착시간(estArrival)을 자체 계산해 회신.
  3. 배차 결과(result): 관제가 입찰을 받아 최소 도착시간(추후 적재공간 기준 포함)으로 배차를 결정하고 goal별 claimed·reason으로 통보. claimed=true를 받은 로봇만 수행합니다.

단발 라운드 · 배차 불가 처리

일부 로봇이 무응답이어도 bid 타임아웃 시점에 관제가 모인 입찰로 배차를 결정하고, 마감 후 도착한 지각 bid는 배제합니다. 라운드는 단발이며 재오퍼하지 않습니다 — 입찰 가능한 로봇이 없으면 해당 미션은 수행 불가로 종료됩니다. mission.atomictrue면 해당 미션 goal들의 순서가 보존되어(거리 최적화 재정렬 시에도 묶음 유지) 다른 미션 goal이 사이에 끼지 못합니다. 입찰·배차 단위(goal vs 미션 묶음)는 협의 대상입니다.

3.1 배송 가능여부 견적 (quote) 비스캣 제안 — 협의 확정 대상

주문이 확정되기 전, 관제가 후보 로봇에게 "지금 이 배송을 수행할 수 있는지"를 미리 물어 가용성과 예상 도착시간을 받아 두는 사전 견적 흐름입니다. 미션(goal)을 만들지 않는 비구속 질의라 배차 입찰(goals/*)과 토픽을 분리해 quote/*로 운영하며, 낙찰(result) 없이 질의 → 견적 2단계로 끝납니다.

sequenceDiagram participant C as 관제 participant A as 로봇 A participant B as 로봇 B Note over C: 배송 가능여부 질의 (주문 전 사전 견적) par 견적 질의 (quote/offer · goals/set과 동일 본문) C-->>A: goals[] (비구속) and C-->>B: goals[] end Note over A,B: 각 로봇이 현재 상태로 가용성·ETA 산정 A-->>C: quote/bid goals[]{available=true, estArrival=9} B-->>C: quote/bid goals[]{available=true, estArrival=6} Note over C: available=true 중 최소 estArrival 채택
(무응답·타임아웃 = available false)
  1. 견적 질의(offer): 관제가 후보 로봇에게 goal 배열을 제시(s2r/{robot_id}/quote/offer) — goals/set(입찰 offer)과 동일한 본문 구조. 미션을 만들지 않는 비구속 질의이며, 입찰과 동일하게 goalId 단위로 식별합니다.
  2. 견적 응답(bid): 각 로봇이 goal별 수행 가능 여부(available)와 예상 도착시간(estArrival, 불가 시 null·사유)을 회신(r2s/{robot_id}/quote/bid). 입찰 bidavailable만 더한 형태입니다.

입찰과의 차이 · 라운드 마감

배차 입찰과 달리 미션을 생성하지 않고 낙찰(result) 단계가 없습니다(2단계). 결과는 배차 확정이 아니라 가용성 집계로, 관제가 available=true 로봇 중 최소 estArrival을 채택합니다. 입찰과 동일하게 goalId 단위 단발 라운드이며 bid 타임아웃 내 무응답은 available=false로 간주합니다. 토픽·페이로드 상세는 API 문서 §5.4. 타임아웃 값 협의

4. 배송 미션 전체 시나리오

배차 이후의 한 사이클입니다(mode: delivery). 한 오더(mission.id)는 매장 goal(type:store, mission.type:pickup)과 고객 goal(type:customer, mission.type:dropoff)로 구성됩니다. 로봇은 goal 배열을 arrivalOrder 순으로 수행하며, 진행 상태를 r2s/{robot_id}/goals로 발행합니다. PIN은 mission.PIN으로 선전달되어 로봇이 로컬 검증하고, 사용 결과를 관제에 감사 이벤트로 보고합니다. 열 칸은 mission.door로 관제가 지정합니다.

sequenceDiagram participant R as 로봇 participant C as 관제 participant O as 오토메타(앱 표출) Note over R: 배차 완료 (delivery) · mission.PIN 보유 R->>C: 매장(store/pickup) 이동 → goals status=going→arrived C->>O: 매장 도착 통지 Note over R: 점주 PIN 입력 → 로봇 로컬 검증
mission.door 칸 개방 R->>C: PIN 사용·검증 결과 감사 이벤트 (r2s) Note over R: 점주 상차 → 박스 닫힘 (mission.door status=closed) R->>C: goals(mission.door closed) / 출발 트리거 C->>O: 배달 출발 알림 R->>C: 세대(customer/dropoff) 이동 → goals status=going→arrived C->>O: 도착 통지 Note over R: 고객 수령 (방식별) R->>C: PIN 사용·검증 결과 감사 이벤트 (r2s) Note over R: 하차 완료 → 미션 종료 R->>C: mission status=done (goal은 arrived 유지) · state.status=done C->>O: 하차 완료 통지 Note over R,C: 완료 후 — 연속 배차=mode delivery 유지 / 충전 복귀=mode navi·goal.type charging
  1. 매장 이동·도착: 배차 후 매장(goal.type:store · mission.type:pickup)으로 주행. 도착 시 goal statusgoing → arrived로 발행되고, 관제가 오토메타에 매장 도착을 통지.
  2. 점주 PIN 검증(로컬): 점주가 PIN 입력 → 로봇이 mission.PIN으로 로컬 검증 → mission.door로 지정된 칸 개방. 사용·검증 결과를 관제에 감사 이벤트(r2s)로 보고.
  3. 상차·박스 닫힘: 점주 상차 후 박스를 닫으면 도어 상태가 closed로 전이. 이 시점이 배달 출발 트리거이며, 관제가 오토메타에 배달 출발을 통지.
  4. 세대 이동·도착: 세대(goal.type:customer · mission.type:dropoff)로 주행, 도착 발행. 관제가 도착 통지.
  5. 고객 수령: 방식은 missionautoDropOff·PIN으로 결정 — 비대면(autoDropOff=true)은 도착 즉시 하차 후 복귀, WEB 버튼 인증(기본)은 관제 → 로봇 문열림 지시로 개방, 로봇 PIN 입력(예외 fallback)은 로컬 검증 후 개방. 사용 결과는 감사 이벤트로 보고.
  6. 완료·복귀: 하차 완료 시 dropoff mission이 done이 되고(goal은 arrived 유지 — goal엔 종료값 없음) 관제가 미션을 종료. 전 mission이 done이면 state.status=done. 이후 처리는 아래 갈래.

미션 완료 후 — 연속 배차 · 충전 복귀 · 자동 회차

전 mission이 done이 되어 state.status=done이 되면 다음 처리는 세 갈래이며, 충전 복귀만 mode가 바뀝니다.

복귀=충전delivery가 아니라 navi 모드입니다(goal.type=charging). 다음 일감을 바로 받는 경우에만 delivery를 유지합니다. timeout 값은 협의로 확정합니다. timeout 값 협의

열 칸은 관제가 결정

4칸 적재함의 어느 칸을 쓸지는 관제가 주문 수량 기반으로 결정해 mission.door로 지정합니다. 점주가 임의로 칸을 고를 수 없으며, 로봇은 PIN 로컬 검증 후 지정된 도어만 개방합니다. 주문 수량이 적재 용량을 초과하면 배차 단계에서 거부됩니다.

고객 수령 3방식

비대면(autoDropOff:true) / WEB 버튼 인증(기본, PIN:null·autoDropOff:false) / 로봇 PIN 입력(예외 fallback, PIN:랜덤)으로 goal.mission에 전달됩니다. 비대면은 autoDropOff:true인 경우만 해당하므로 (autoDropOff, PIN) 조합으로 3방식이 모두 구분됩니다. 상세는 API 문서 §6.

5. 쓰레기 수거 시나리오

mode: trash_pickup. 흐름은 배송과 동일한 MQTT 입찰·배차·주행 구조(goal 배열)를 따르되, 매장↔세대 대신 세대·수거 요청 지점(goal.type:customer, mission.type:pickup) → 하역장(goal.type:unloading, mission.type:dropoff) 경로이고 점주 PIN 단계가 없습니다. trash_pickup의 goal/mission 매핑과 세부 적재·개폐 동작은 제로웍스 펌웨어 capability에 맞춰 협의 시 확정합니다. 매핑 협의 대기

6. 단지 인프라(E/V·로비폰) 처리

로봇은 엘리베이터·로비폰을 직접 호출하지 않습니다

엘리베이터 호출·문 열림은 관제가 엘리베이터·공동현관 연동을 통해 처리합니다. 로봇은 자신의 위치·상태·도착(goal status)만 발행하면 되고, 인프라 제어 책임은 지지 않습니다. 관제가 로봇 위치를 기준으로 적절한 시점에 엘리베이터·공동현관을 제어합니다.

단, 1.5차 PoC는 "로봇 비탑승(사람 동승)" 전제로 운영하며, 실패 시나리오(호출 실패·문 안 열림·층 오인식) 대응을 별도 협의합니다.

7. 알람·에러·재시도

8. 배송 통신 플로우 예시 (메시지 레벨)

배송 두 케이스의 토픽·메시지 흐름을 단계별로 보입니다 — ① 단건 배송② 주행 중 추가 주문(묶음 배송). 토픽 규약(s2r/r2s)·메시지 봉투·필드 정의는 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를 쓰고 같은 주문의 픽업·하차는 동일 칸을 씁니다.

8.1 단건 배송 (Order1)

고객 Order1 주문 → 입찰 → Robot1 확정 → 매장 도착·상차 → 배송 → Order1 도착·배송 완료. 한 주문(mission_Order1)이 매장 goal(goal_s1, pickup)과 고객 goal(goal_c1, dropoff) 두 goal에 같은 mission.id로 걸칩니다.

sequenceDiagram autonumber participant O as 오토메타 participant C as 관제 participant Robot1 as 로봇 Robot1 O->>C: 고객 Order1 주문 인입 Note over C: 미션 생성(mission_Order1)
goal_s1(store/pickup) + goal_c1(customer/dropoff) C-->>Robot1: s2r/Robot1/goals/offer (goals[]) Robot1-->>C: r2s/Robot1/goals/bid (estArrival) Note over C: 최소 도착시간으로 배차 결정 C-->>Robot1: s2r/Robot1/goals/result (claimed=true) Robot1->>C: r2s/Robot1/state (mode=delivery) Robot1->>C: r2s/Robot1/goals (goal_s1 going) — 매장 이동 Robot1->>C: r2s/Robot1/goals (goal_s1 arrived · pickup doing) Note over Robot1: 점주 PIN 로컬 검증 → door1 개방 Robot1->>C: r2s/Robot1/goals (mission.door1 closed) — 상차·박스 닫힘 Robot1->>C: r2s/Robot1/goals (pickup done) C->>O: 배달 출발 통지 Robot1->>C: r2s/Robot1/goals (goal_c1 going) — 세대 이동 Robot1->>C: r2s/Robot1/goals (goal_c1 arrived · dropoff doing) Note over Robot1: 고객 수령 (WEB 버튼/PIN) Robot1->>C: r2s/Robot1/goals (dropoff done) — 배송 완료 C->>O: 배송 완료 통지 Robot1->>C: r2s/Robot1/state (status=done) — 전 미션 완료 Note over C,Robot1: 완료 후 — 연속 배차=mode delivery 유지 / 충전 복귀=mode navi·goal.type charging

① 입찰 요청 (offer) — s2r/Robot1/goals/offer · 관제 → Robot1

관제가 goal 전체(location 포함)를 제시합니다. location 좌표는 로봇이 사전 등록한 맵 노드 기준으로 관제가 채웁니다(§2 맵 등록).

{
  "requestId": "req-offer-Order1",
  "occurredAt": "2026-06-17T10:00:00+09:00",
  "payload": {
    "goals": [
      {
        "id": "goal_s1", "type": "store", "status": "ready", "arrivalOrder": 1,
        "location": { "x": 12.4, "y": 8.1, "th": 0.0, "map_id": "apt1023-b1" },
        "mission": [
          { "id": "mission_Order1", "type": "pickup", "status": "ready", "atomic": false,
            "PIN": null, "door": [ { "name": "door1", "status": "closed" } ], "orderId": "Order1",
            "address": "", "addressDetail": "", "phoneNumber": "", "message": "" }
        ]
      },
      {
        "id": "goal_c1", "type": "customer", "status": "ready", "arrivalOrder": 2,
        "location": { "x": 40.2, "y": 22.7, "th": 1.57, "map_id": "apt1023-3f" },
        "mission": [
          { "id": "mission_Order1", "type": "dropoff", "status": "ready", "atomic": false,
            "PIN": "1482", "door": [ { "name": "door1", "status": "closed" } ], "orderId": "Order1",
            "address": "103동", "addressDetail": "302호", "phoneNumber": "010-0000-1234", "message": "문 앞" }
        ]
      }
    ]
  }
}

② 입찰 응답 (bid) — r2s/Robot1/goals/bid · Robot1 → 관제

Robot1이 goal별 예상 도착시간(estArrival, min)을 자체 계산해 회신. 식별은 goalId 단위.

{
  "requestId": "req-bid-Order1",
  "occurredAt": "2026-06-17T10:00:03+09:00",
  "payload": {
    "goals": [
      { "id": "goal_s1", "arrivalOrder": 1, "estArrival": 4 },
      { "id": "goal_c1", "arrivalOrder": 2, "estArrival": 11 }
    ]
  }
}

③ 배차 결과 (result) — 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 }
    ]
  }
}

④ 수행 — 모드·goal 진행 발행 (r2s/Robot1/state, r2s/Robot1/goals) · Robot1 → 관제

배송 로봇이므로 modedelivery로 계속 유지되고, 일감을 잡으면 state.statusdoing으로 전이합니다. 매장 goal이 going → arrived로 발행되고 pickup mission이 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 — 매장 도착·픽업 시작
{ "occurredAt": "2026-06-17T10:04:00+09:00",
  "payload": { "goals": [
    { "id": "goal_s1", "status": "arrived", "progress": 100,
      "mission": [ { "id": "mission_Order1", "type": "pickup", "status": "doing", "door": [ { "name": "door1", "status": "opened" } ] } ] }
  ] } }

⑤ 상차·박스 닫힘 → 픽업 완료 (r2s/Robot1/goals) · Robot1 → 관제

점주가 PIN을 입력하면 Robot1이 mission.PIN으로 로컬 검증 후 지정 칸(door1)을 개방하고, 사용·결과를 감사 이벤트(r2s)로 보고합니다(토픽·페이로드는 API 문서 §6 협의 대상). 상차 후 박스를 닫으면 mission.door의 해당 도어가 closed로 전이(=배달 출발 트리거)하고 pickup mission이 done으로 완료됩니다. 도어 상태는 미션 컨텍스트이므로 state가 아니라 goals 발행의 mission.door[]에 실립니다.

// r2s/Robot1/goals — 도어 닫힘(door1) → 픽업 완료 (goal은 arrived 유지, 완료는 mission.status=done)
{ "occurredAt": "2026-06-17T10:05:11+09:00",
  "payload": { "goals": [
    { "id": "goal_s1", "status": "arrived",
      "mission": [ { "id": "mission_Order1", "type": "pickup", "status": "done",
        "door": [ { "name": "door1", "status": "closed" } ] } ] }
  ] } }

⑥ 배송·완료 (r2s/Robot1/goals) · Robot1 → 관제

세대 goal이 going → arrived, 고객 수령 후 dropoff mission이 done. 고객 수령은 기본 WEB 버튼 인증(관제 → 로봇 문열림 지시), 예외 시 로봇 PIN 입력입니다(§4 참조). 관제가 오토메타에 배송 완료를 통지합니다. 전 mission이 done이 되면 state.status=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_c1", "status": "arrived",
      "mission": [ { "id": "mission_Order1", "type": "dropoff", "status": "done", "door": [ { "name": "door1", "status": "closed" } ] } ] }
  ] } }

8.2 주행 중 추가 주문 — 묶음 배송 (Order1 → Order2)

Order1 배차 후 Robot1이 매장1로 이동하는 중 고객 Order2 주문이 들어와 같은 Robot1에 추가됩니다. 낙찰 후 Robot1이 Order1·Order2 goal을 직접 통합·정렬매장1 상차 → 매장2 상차 → Order1 배송 → Order2 배송 순서로 수행합니다(관제의 plan 재주입 없음). Order2는 다른 매장(goal_s2)·다른 세대(goal_c2), 적재 칸은 door2를 씁니다.

sequenceDiagram autonumber participant O as 오토메타 participant C as 관제 participant Robot1 as 로봇 Robot1 O->>C: 고객 Order1 주문 C-->>Robot1: offer (mission_Order1 · goal_s1, goal_c1) Robot1-->>C: bid C-->>Robot1: result claimed=true Robot1->>C: r2s/Robot1/goals (goal_s1 going) — 매장1 이동 중 O->>C: 고객 Order2 주문 (Robot1 주행 중) Note over C: 후보 산정 → Robot1 포함 C-->>Robot1: offer (mission_Order2 · goal_s2, goal_c2) Robot1-->>C: bid (현재 부하 반영) C-->>Robot1: result claimed=true Note over Robot1: Robot1이 Order1+Order2 goal 통합·정렬
(atomic·주문 내 순서 보존)
수행 순서: s1 → s2 → c1 → c2 Robot1->>C: r2s/Robot1/goals (goal_s1 arrived · pickup_Order1 done) — 매장1 상차 Robot1->>C: r2s/Robot1/goals (goal_s2 going→arrived · pickup_Order2 done) — 매장2 상차 Robot1->>C: r2s/Robot1/goals (goal_c1 going→arrived · dropoff_Order1 done) C->>O: Order1 배송 완료 Robot1->>C: r2s/Robot1/goals (goal_c2 going→arrived · dropoff_Order2 done) C->>O: Order2 배송 완료 Robot1->>C: r2s/Robot1/state (status=done) — 전 미션 완료 Note over C,Robot1: 완료 후 — 연속 배차=mode delivery 유지 / 충전 복귀=mode navi·goal.type charging

① Order1 배차·매장1 이동 — 8.1의 ①~④와 동일

Order1 주문 → offerbidresult(claimed=true) → Robot1이 goal_s1로 이동(going). 형식은 8.1과 같아 생략합니다.

② Order2 입찰 요청 (offer) — s2r/Robot1/goals/offer · 관제 → Robot1

Robot1 주행 중 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": "ready", "arrivalOrder": 1,
        "location": { "x": 18.9, "y": 6.4, "th": 0.0, "map_id": "apt1023-b1" },
        "mission": [
          { "id": "mission_Order2", "type": "pickup", "status": "ready", "atomic": false,
            "PIN": null, "door": [ { "name": "door2", "status": "closed" } ], "orderId": "Order2",
            "address": "", "addressDetail": "", "phoneNumber": "", "message": "" }
        ]
      },
      {
        "id": "goal_c2", "type": "customer", "status": "ready", "arrivalOrder": 2,
        "location": { "x": 44.0, "y": 30.1, "th": 3.14, "map_id": "apt1023-5f" },
        "mission": [
          { "id": "mission_Order2", "type": "dropoff", "status": "ready", "atomic": false,
            "PIN": "7035", "door": [ { "name": "door2", "status": "closed" } ], "orderId": "Order2",
            "address": "105동", "addressDetail": "501호", "phoneNumber": "010-0000-5678", "message": "" }
        ]
      }
    ]
  }
}

③ Order2 입찰 응답·배차 결과 (bid, result)

Robot1이 현재 부하(Order1 수행 중)를 반영해 입찰하고, 관제가 낙찰을 통보합니다(형식은 8.1 ②③과 동일, goalIdgoal_s2·goal_c2).

④ Robot1 통합·정렬 (로봇 자체 재계획)

낙찰(③) 후 Robot1은 이미 보유한 Order1 goal과 새로 받은 Order2 goal을 자체적으로 통합·정렬해 수행 순서를 정합니다. goal 정의는 offer 본문으로 이미 전달됐으므로, 관제는 별도 통합 plan을 주입하지 않습니다(goals/set 미사용) — 경로 최적화·재정렬은 로봇이 담당합니다. 각 주문의 내부 순서(픽업 → 하차)와 atomic 제약은 보존되며, Order1·Order2 모두 atomic: false라 픽업끼리·하차끼리 묶는 순서(A,C,B,D)가 허용되어 Robot1은 아래 수행 순서를 잡습니다.

수행 순서goaltypemissiondoor의미
1goal_s1storemission_Order1 · pickupdoor1매장1 상차 (Order1)
2goal_s2storemission_Order2 · pickupdoor2매장2 상차 (Order2)
3goal_c1customermission_Order1 · dropoffdoor1Order1 배송
4goal_c2customermission_Order2 · dropoffdoor2Order2 배송

Robot1이 정한 수행 순서는 r2s/Robot1/goals 진행 발행의 arrivalOrder(1~4)로 반영되어, 관제가 Robot1의 계획을 파악합니다.

⑤ 수행 — 상차 2회 → 배송 2회 (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": "arrived",
      "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": "arrived",
      "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": "arrived",
      "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": "arrived",
      "mission": [ { "id": "mission_Order2", "type": "dropoff", "status": "done", "door": [ { "name": "door2", "status": "closed" } ] } ] }
  ] } }

8.3 상태 전이 요약

단계mode주요 goal.status주요 mission.status
배차 전 대기delivery
낙찰·수행 시작deliverygoal_s* ready→goingpickup ready
매장 도착·상차deliverygoal_s* arrivedpickup doing→done
세대 도착·하차deliverygoal_c* going→arriveddropoff doing→done
전 mission 완료 (state.status=done)deliverygoal arrived 유지전부 done
완료 후 — 연속 배차delivery 유지새 goal ready→going새 mission ready
완료 후 — 충전 복귀 (관제/자동 회차)navicharging goal going→arrivedcharge

goal.status에는 종료값이 없습니다(ready·going·arrived·canceled). 지점 작업 완료는 mission.status = done으로 판정하고 goal은 arrived에 머뭅니다. 중도 취소는 goal/mission canceled, 로봇 일시정지는 별도 축 state.status = paused. 복귀=충전은 delivery가 아니라 navi 모드이며, 다음 일감을 바로 받는 연속 배차에서만 delivery를 유지합니다.