Repository navigation
EVENT_ROBOT_FLOW_DESIGN
작성일: 2026-09-12 · 상태: 1차 구현 완료 (orchestration-server 마이그레이션 118) — 아래 §7 "구현 결정" 참고. §1~§6 은 원안. · 관련: LOCATION_AUTOMATION_DESIGN, sequence, MQTT_SCHEMA-SPOT, MQTT_SCHEMA-ROBOT_ARM
목표 플로우 (마트 적재):
LoadingsequenceDiagram oc_server-->>MQTT: mqtt.pub run_scenario market_load MQTT-->>Spot: sub robot/spot-01/command Spot->>Spot: 마트로 이동 · 자세 변경 Spot-->>MQTT: pub ack SUCCESS MQTT-->>oc_server: SPOT 작업 완료 oc_server-->>MQTT: mqtt.pub pick_basket MQTT-->>로봇팔: sub robot/arm-01/command 로봇팔->>로봇팔: 물건 집기 로봇팔-->>MQTT: pub ack SUCCESS MQTT-->>oc_server: 로봇팔 작업 완료 oc_server-->>MQTT: mqtt.pub navigate_to (다음 장소) MQTT-->>Spot: sub navigate_to Spot-->>MQTT: pub ack SUCCESS MQTT-->>oc_server: 태스크 완료 처리
새 실행 엔진을 만들지 않는다. 위 플로우는 이미 있는 자동화 규칙(arrival_automations) 의 액션 체인
그대로다 — 규칙 하나에 액션을 spot run_scenario → robot_arm pick_basket → spot navigate_to 순서로 넣으면
디바이스 큐가 앞 액션의 종결 ack(SUCCESS/FAULT)를 기다린 뒤 다음 액션을 발행한다(§4.3 디바이스 명령 큐).
sequence 정책 2·3·5(도착 완료 후 지시, 적재 중 SPOT 정지)는 이 체인 구조에서 자동으로 지켜진다.
이벤트 도메인에서 빠진 것은 네 가지뿐이고, 이 설계는 그 네 가지만 추가한다.
| # | 빠진 것 | 추가 |
|---|---|---|
| A | 이벤트 규칙의 트리거가 장소 도착뿐 — 팝업이 뜨거나 관람객이 선택지를 고른 순간에 플로우를 시작할 수 없음 | 트리거 event_published, option_selected 추가 |
| B | 플로우가 끝나도 태스크 완료로 이어지지 않음 (태스크 소유 규칙만 user_task_commands 로 추적해 자동 완료) | 규칙에 "완료 처리할 태스크" 지정 → 체인 전체를 그 태스크 명령으로 추적, 전부 종결되면 자동 completed |
| C | 앞 액션이 실패해도 다음 액션이 나감 (SPOT 이동 실패 → 로봇팔이 빈자리에서 작업, 정책 3 위반) | 액션별 stop_on_failure (실패 시 체인 중단) |
| D | 이벤트 선택지의 로봇 연동이 단일 SPOT 명령 + 반려로봇 + LG 전원뿐이라 다단계 표현 불가 | 선택지는 그대로 두고, 다단계는 option_selected 규칙으로 옮긴다 (선택지 편집기에 "플로우로 연결" 안내) |
-- 트리거 확장
ALTER TABLE arrival_automations DROP CONSTRAINT arrival_automations_trigger_check;
ALTER TABLE arrival_automations ADD CONSTRAINT arrival_automations_trigger_check
CHECK (trigger IN ('arrive','leave','wrong_item','right_item','event_published','option_selected'));
-- B. 완료 처리할 태스크
ALTER TABLE arrival_automations ADD COLUMN complete_task_mode TEXT NOT NULL DEFAULT 'none'
CHECK (complete_task_mode IN ('none','task_id','location_task'));
ALTER TABLE arrival_automations ADD COLUMN complete_task_id TEXT; -- mode='task_id' 일 때 tasks.id
-- C. 실패 시 체인 중단
ALTER TABLE arrival_automation_actions ADD COLUMN stop_on_failure BIGINT NOT NULL DEFAULT 0 CHECK (stop_on_failure IN (0,1));-
event_published/option_selected는 이벤트 소유 규칙 전용(어드민 검증).option_selected는condition_json.option_ids: []로 어느 선택지에 반응할지 고른다(비면 모든 선택지). -
complete_task_mode-
task_id: 지정 태스크(complete_task_id). 마트 이벤트 플로우가 "마트 장보기" 태스크를 완료시키는 기본형. -
location_task: 규칙이 실행되는 장소(이벤트가 매핑된 user_location)에서 이 세션의 미완료 태스크 전부. 태스크 ID 를 하드코딩하지 않아도 되지만 장소에 미완료 태스크가 여럿이면 다 완료돼 버리므로 기본값은task_id. - 태스크 소유 규칙은 지금처럼 자기 태스크를 추적한다(기존 동작 유지, 컬럼은
none).
-
- 기존 행은 전부 기본값이라 동작이 바뀌지 않는다.
stop_on_failure=0이 현재 동작(실패해도 계속).
event_timing.go publishSelectedEvent(...) 직후 → onEventPublished(userID, eventID, locationID)
event_option_select.go 선택지 처리 직후 → onOptionSelected(userID, eventID, optionID, locationID)
(locationID = GetUserLocationByUserAndEvent)
둘 다 ArrivalAutomationEngine.Run(RunArrivalParams{Trigger, TriggerEventID, TriggerOptionID, LocationID}) 를 goroutine 으로
호출한다. 후보 규칙은 owner_type='event' AND owner_id=eventID AND trigger=? (신규 쿼리 ListActiveAutomationsByEventTrigger).
option_selected 는 evaluateRule 에서 condition.option_ids 를 검사한다.
발화 정책·조건·쿨다운·실행 로그(automation_runs)는 도착 규칙과 동일. 시험 발화(백오피스)와 이벤트 테스트 패널의 "자동화 강제 실행"은 트리거와 무관하게 규칙을 지정 실행한다(기존 Manual 경로).
dispatchActions 는 지금처럼 액션을 Next 체인으로 묶는다. 목표 플로우는 규칙 하나:
| 순서 | device | action | params | stop_on_failure |
|---|---|---|---|---|
| 1 | spot | run_scenario | {scenario_id:"market_load"} |
1 |
| 2 | robot_arm | pick_basket | {basket:1} |
1 |
| 3 | spot | navigate_to | {destination_mode:"next_location"} |
0 |
- 1→2: SPOT 종결 ack(SUCCESS) 를 큐가 기다린 뒤 로봇팔 발행 — sequence 정책 2.
- 2 진행 중 SPOT 큐는 비어 있고 서버는 SPOT 에 아무것도 보내지 않는다 — 정책 5.
- 3:
next_location은 매핑 장소 기준 다음 방문 장소(GetNextUserLocationAfter). 이벤트 규칙의 LocationID 는 매핑 장소이므로 그대로 동작. - 로봇팔 타임아웃은 이미 120초 기본, SPOT run_scenario 는
REJECTED도 종결로 온다는 스키마 예외(MQTT_SCHEMA-SPOT §4)를 isTerminalSpotStatus 에 반영해야 한다 — 현재 REJECTED 는 비종결로 취급해 run_scenario 가 스텝 실패로 REJECTED 를 보내면 큐가 타임아웃까지 기다린다.ACCEPTED 이후의 REJECTED만 종결로 보도록 수정(§6-1).
device_queue handle() 종료 시 finalActionStatus(res) 가 failed/timeout 이고 job.StopOnFailure 면 enqueueNext 를 건너뛰고,
남은 체인(Next/NextGroup 재귀)의 액션 결과를 skipped / chain_stopped 로 automation_runs 에 기록, 추적 중인 태스크 명령도
failed / chain_stopped 로 닫는다(태스크가 자동 완료되지 않도록 — 아래 3.4).
- 규칙 실행 시
complete_task_mode에 따라 추적 태스크 ID 목록을 정하고, 체인의 모든 잡을recordTaskCommands로 남긴다 (태스크 소유 규칙과 같은 경로,rule_id로 어떤 규칙이 만든 명령인지 구분). - 모든 명령이 종결되면
finishTaskCommand가ProcessTaskCompletion을 호출 — 결정 필요: 지금은 실패 명령이 섞여도 "전부 종결"이면 완료 처리한다. 플로우에서는stop_on_failure로 중단된 경우 완료시키지 않는 편이 맞으므로, chain_stopped 로 닫힌 명령이 하나라도 있으면 완료 대신 상태를 유지하고 백오피스 사용자 상세에 "플로우 중단" 배지를 띄운다. - 완료되면 기존대로 대시보드 재발행 · 포인트 · 순차 실행 후속(117) · 이벤트 타이밍 판단이 이어진다.
- 선택지의 기존 단일 명령(spot_action/companion/lg)은 유지한다(호환).
- 같은 선택지에
option_selected규칙이 있으면 선택지의 spot_action 은 비워 두라고 편집기가 경고한다(이중 발행 방지). 서버도 규칙이 매칭되면 선택지의 spot_action 을 건너뛰도록 옵션화할 수 있으나, 1차는 경고만. - 반려로봇 발화(선택 직후 "실어 드릴게요")는 규칙의 첫 액션을 companion say(parallel) 로 넣으면 SPOT 과 동시에 나간다(100 병렬 그룹).
-
이벤트 상세 → 자동화 편집기: 트리거 드롭다운에
📣 팝업이 발행됐을 때,☝️ 선택지를 골랐을 때(선택지 다중 선택 칩) 추가. 규칙 폼에 "완료 처리할 태스크" (없음 / 태스크 선택 / 매핑 장소의 태스크) 추가. 액션 카드에 "실패 시 체인 중단" 체크. - 액션 순서 위에 시퀀스 다이어그램 미리보기(AutomationSequenceDiagram 재사용)로 위 mermaid 와 같은 그림을 보여준다.
-
이벤트 테스트 패널(오늘 추가한 것)에 "선택지 선택 시뮬레이션" 버튼: 태블릿의
event/option/select와 같은 경로를 태워 option_selected 플로우를 끝까지 검증한다. 결과 표에는 규칙별 액션 상태와 태스크 완료 여부가 이어서 보인다. - 실행 로그(/automations/runs)와 사용자 상세 명령 표는 그대로 쓰되
chain_stopped사유 라벨 추가.
- 마이그레이션 118 + 쿼리(수기 sqlc) + 어드민 검증(트리거/complete_task/stop_on_failure)
- 엔진: 트리거 훅 2개, option_ids 조건, complete_task 추적, stop_on_failure 중단 + chain_stopped 기록, run_scenario REJECTED 종결 처리
- 백오피스 편집기(트리거·완료 태스크·중단 체크·다이어그램) + 테스트 패널 선택지 시뮬레이션
- 위키: 이 문서를 "구현"으로 갱신, MQTT_SCHEMA-ORCHESTRATION_SERVER 24번에 트리거 추가
- run_scenario 의 REJECTED 종결 처리 — SPOT 팀 스키마대로 "ACCEPTED 뒤에 온 REJECTED 는 종결"로 바꾼다. 다른 액션에는 영향 없음(ACCEPTED 전 REJECTED 는 여전히 완료 응답 없음). 반대 의견 없으면 진행.
- 실패 섞인 플로우의 태스크 완료 — 3.4 제안(중단되면 완료 안 함)으로 갈지, 지금처럼 전부 종결이면 완료로 볼지.
-
트리거 기본값 — 마트 플로우를
option_selected(관람객이 "실어줘" 선택) 로 할지event_published(팝업 뜨자마자) 로 할지. 시연 통제 측면에서는option_selected를 권장한다(사람이 시작 시점을 쥔다). -
location_task모드가 필요한지 — 없으면task_id만 두고 단순화.
원안 A~D 중 사용자 결정에 따라 다음과 같이 구현했다.
| 원안 | 결정 | 구현 |
|---|---|---|
| A 트리거 | 팝업 버튼 클릭만 |
trigger='option_selected' (이벤트 소유 규칙 전용). event_published 는 만들지 않음 |
| B 태스크 완료 | 없음 — 이벤트 도메인에는 완료 개념이 없다 |
complete_task_* 컬럼 없음. 이벤트 규칙은 user_task_commands 로 추적하지 않는다 |
| C 실패 시 중단 | 보류 — 실패하면 최근 이력으로 확인 |
stop_on_failure 없음(실패해도 다음 단계 진행, 기존 동작). 이벤트 상세 파이프라인 카드의 "최근 이력"에서 단계별 상태·에러 확인 |
| D 선택지 다단계 | 새 이벤트 타입 pipeline |
팝업은 choice 처럼 버튼 표시. 버튼마다 파이프라인(규칙 1개 = condition.option_ids=[버튼], 액션 = 단계)을 "단계 추가"로 쌓는다. pipeline 이벤트는 선택지의 단일 SPOT/반려로봇/LG 명령을 쓰지 않는다(편집 모달에서 숨김, 서버도 건너뜀) |
- 서버:
ProcessEventOptionSelect(MQTT 핸들러·테스트 패널 공용) → pipeline 이벤트면onOptionSelected→Run(Trigger=option_selected, TriggerEventID, TriggerOptionID)→ 규칙의 액션 체인을 디바이스 큐에 적재(앞 단계 종결 ack 대기). 응답{ok:true, status:"pipeline_queued"}. - 백오피스: 이벤트 타입 "로봇 파이프라인" → "버튼(선택지)" 카드 + "🔁 파이프라인" 카드(버튼별 단계 편집, 시퀀스 다이어그램, 최근 이력). 이벤트 테스트 패널에 "버튼 선택 시뮬레이션"(가상 세션으로 종단 검증).
-
미해결(§6-1)해결(2026-09-21): SPOTREJECTED·BUSY를 종결(실패)로 처리 — 완료 응답이 오지 않는 ①단계 거절이라 즉시 실패로 끝내고 다음 명령으로 넘어간다(종전엔 30초 타임아웃을 채우며 큐가 밀렸음).fault_detail이 실패 사유(예:CHARGING)로 남는다.
§7-C 에서 보류했던 실패 정책의 첫 조각. "로봇팔 명령이 실패해도 SPOT 이 빈자리로 가 버리는" 문제를 막는다.
재시도는 자동 반복이 아니라 어드민이 백오피스에서 직접 누른다 (126 에서 잠깐 넣었던 자동 반복·automation_retry_max_attempts 는 127 에서 폐기).
| 항목 | 내용 |
|---|---|
| 기본 동작 (128, 2026-09-19) |
모든 액션이 실패 시 멈춤. 옵트인 체크(_hold_on_failure)는 실제 운영에서 로봇팔 FAULT 뒤 SPOT 이 그대로 출발한 사고로 폐기. 예외적으로 "실패해도 다음 단계 진행"이 필요한 액션(연출용 발화 등)만 카드의 "▶ 실패해도 다음 단계 진행" 체크 → params_json._continue_on_failure=true. 디바이스로 나가는 params 에서는 서버가 제거(popFailurePolicy) |
| 기다리지 않는 단계 (2026-10-04) | 카드의 "뒤 단계가 기다리지 않음" 체크 → params_json._no_wait=true. 같이 출발하되 뒤 단계가 완료를 기다리지 않는다(드론 배송 등). 체인에 매달려 있지 않으므로 실패해도 멈출 것이 없다 — hold_on_failure=false, awaiting_retry 를 달지 않는다. 자세한 규칙은 LOCATION_AUTOMATION_DESIGN 9-1
|
| 동작 | 큐 소비자(device_queue.go handle)가 종결 결과가 완료가 아니면(거절·FAULT_*·timeout·dispatch 오류) 결과를 failed/timeout 으로 남기되 Next 체인을 적재하지 않는다 — 뒤 단계(SPOT)는 queued 인 채 멈춤. 병렬 그룹의 한 잡이 멈추면 그룹 전체의 다음 단계도 나가지 않는다(heldGroups). _continue_on_failure 액션만 실패해도 다음 단계 진행 |
| 실행 로그 |
action_results[].hold_on_failure, awaiting_retry=true(멈춰서 클릭을 기다리는 단계), attempts(수동 재시도 횟수). command_id 는 최신 시도 것 |
| 수동 재시도 |
POST /api/admin/automations/runs/{runId}/retry {action_id} → RetryRunFrom: 그 단계부터 규칙의 나머지 액션 전부를 새 command_id 로 다시 체인 적재(같은 run 에 결과 덮어쓰기, attempts 이어 세기). 단, _no_wait 단계는 그 단계만 다시 보낸다 — 뒤 단계는 애초에 이 단계를 기다린 적이 없다 (2026-10-04). 실패/타임아웃 단계에만 허용(아니면 409). 태스크 추적 실행이면 되살아난 단계의 옛 user_task_commands 행을 superseded 로 닫아 자동 완료 판정이 새 행만 보게 한다 |
| 버튼 위치 | 이벤트 상세 파이프라인 카드 실행 이력(단계별 로그 옆) · 자동화 실행 로그 페이지 · 사용자 상세 태스크 카드의 실행 로그 · 태스크/이벤트 테스트 패널. 실패/타임아웃 단계에만 나타난다 |
| 종결 조건 | SPOT REJECTED·BUSY 도 종결(실패, 2026-09-21). 멈춤은 종결(timeout 포함) 뒤에 걸린다 |
- Google ADK 검토 (프레임워크·모델 비교)
- 페르소나 + 상황 카드 모델 (2차 시나리오 구동 방식)
- Mac Studio 로컬 LLM + Claude Opus 하이브리드 라우팅 전략
- 개발 일정·할 일 점검표 (단계별 진행 점검)
- 로봇 Adapter 개발 온보딩 (HA·MQTT 계정 등록, 토픽 적용, 테스트 송수신, 상태 확인)
- LG ThinQ 가전 연동 (HA 통합 · REST 데이터 취득 · 제어)
- 장소 도착 자동화 (태스크/이벤트/장소 규칙 → SPOT·로봇팔·반려로봇·Hub 자동 명령)
- 엔딩 화면 데이터 출처 점검 (차량 3필드 · 세션 종료 엔딩 5화면)