# RBS Web Interface — Tài liệu tham chiếu > Ứng dụng `robot_app` — giao diện web quản lý robot, mission, map và tích hợp. **Chú thích trạng thái:** ✅ đầy đủ · 🔶 một phần · ⏳ placeholder · ❌ chưa có --- ## 1. Giới thiệu Tài liệu mô tả giao diện web RBS cho **administrator**, kỹ sư cấu hình và operator: đăng nhập, dashboard, mission, map, user, Modbus/REST tích hợp. **Stack:** frontend `www/` (vanilla JS), backend C++ (`src/server/`, `src/mission/`, …), dữ liệu SQLite `data/RBS.db` + `data/auth.json`. --- ## 2. Đăng nhập và phân quyền ### 2.1 Luồng đăng nhập ``` Trình duyệt → / (trang Sign in) → POST /api/auth/login (password hoặc PIN) → Cookie lm_session + shell app (Dashboard / Setup / Monitoring / System / Help) → Menu user (góc phải): đổi mật khẩu, đăng xuất ``` - Toàn bộ API (trừ health/login/logout) yêu cầu session; UI bị chặn cho đến khi đăng nhập. - `auth.js` gọi `GET /api/auth/me` khi mở trang; session hợp lệ thì vào app ngay. - Các module JS (`app.js`, `missions.js`, `dashboard.js`, …) khởi động sau sự kiện `lm:auth-ready`. ### 2.2 Giao diện Sign in | Thành phần | Mô tả | |------------|--------| | Tab **Tên đăng nhập và mật khẩu** | Form username + password | | Tab **Mã PIN** | Keypad 4 chữ số; tự đăng nhập khi đủ 4 số | | Sau login | Menu user topbar; ẩn/vô hiệu hóa theo quyền read-only | File: `www/index.html`, `www/auth.js`, `www/style.css`. ### 2.3 Tài khoản mặc định Tự tạo lần đầu trong `data/auth.json`: | Username | Password | User group | |----------|----------|------------| | `Admin` | `admin` | Administrators | | `User` | `user` | Users | | `Distributor` | `distributor` | Distributors | - Username **không phân biệt hoa thường**. - **PIN:** không có mã mặc định; chỉ nhóm có `allow_pin: true`; admin gán qua API/UI. ### 2.4 User groups và permissions Credentials gắn **user**; quyền gắn **group**. Mỗi user thuộc một nhóm. **7 resource:** `dashboard`, `config`, `maps`, `missions`, `sounds`, `integrations`, `users` — giá trị `none` | `read` | `write`. | Group | PIN | dashboard | config | maps | missions | sounds | integrations | users | |-------|-----|-----------|--------|------|----------|--------|--------------|-------| | Distributors | Không | write | write | write | write | write | write | write | | Administrators | Không | write | write | write | write | write | write | write | | Users | Sau khi gán | write | read | read | read | read | read | none | Trang không có quyền write: **vẫn hiển thị**, nút tạo/sửa bị vô hiệu (`body.auth-readonly-*`). **Quyền trang** (`www/auth.js`): | Trang | Resource | |-------|----------| | `config` | Chỉ nhóm **Distributors** | | `maps`, `transitions`, `paths`, `path-guides` | `maps` | | `missions` | `missions` | | `sounds` | `sounds` | | `io-modules`, `integrations` | `integrations` | | `users`, `user-groups` | `users` | | `monitoring`, `help` | Mọi user đã đăng nhập | ### 2.5 Session và API auth | Cơ chế | Chi tiết | |--------|----------| | Session | Server-side; mất khi restart process | | Cookie | `lm_session=; HttpOnly; SameSite=Lax` | | Header | `Authorization: Bearer ` | | Middleware | `AuthService::preRoute` trên `/api/*` | | Public | `GET /api/health`, `POST /api/auth/login`, `POST /api/auth/logout`, `OPTIONS` | | Dev | `LM_AUTH_DISABLED=1` tắt auth | **API → module:** | Module | Prefix | |--------|--------| | config | `/api/lidars`, `/api/imus`, `/api/layouts`, `/api/state`, … | | maps | `/api/maps`, `/api/sites`, `/api/paths`, `/api/path_guides`, `/api/transitions` | | missions | `/api/missions`, `/api/mission_queue` | | sounds | `/api/sounds` | | integrations | `/api/triggers`, `/api/schedules`, `/api/fleet`, `/api/modbus`, `/api/io_modules`, `/api/v2.0.0/` | | users | `/api/users`, `/api/user_groups` | **Endpoint auth chính:** | Method | Endpoint | Mô tả | |--------|----------|--------| | POST | `/api/auth/login` | `{ username, password }` hoặc `{ pin }` | | POST | `/api/auth/logout` | Xóa session | | GET | `/api/auth/me` | User, group, permissions | | PUT | `/api/auth/password` | Đổi mật khẩu | | GET/POST/PUT/DELETE | `/api/users`, `/api/user_groups` | CRUD user và nhóm | **Mã nguồn:** `src/auth/auth_service.cpp`, `www/auth.js`, `www/users.js`, `www/user-groups.js`. **Chưa có:** auto sign-out theo nhóm, OAuth. --- ## 3. Điều hướng và topbar ### 3.1 Shell 3 cột Rail trái → flyout submenu → content (`www/nav.js`). | Module | Flyout (section → page) | File JS | Trạng thái | |--------|-------------------------|---------|------------| | **Dashboards** | Danh sách động → `dashboard` | `dashboard.js` | ✅ | | **Setup** | missions, maps, sounds, transitions, users, user-groups, io-modules, paths, path-guides | `*.js` | 🔶 | | **Robot+** | robot → `config`, simulation → `simulation` | `app.js` (config), — | 🔶 | | **Monitoring** | analytics (mặc định), system log, error logs, hardware, safety, mission log → `monitoring` | — | ✅ | | **System** | integrations → `integrations` | `integrations.js` | 🔶 | | **Help** | help-api → `help` | — | ⏳ | ### 3.2 Topbar (`www/topbar.js`) | Thành phần | API | |------------|-----| | Start / Pause robot | `POST /api/robot/start`, `POST /api/robot/pause` | | Strip mission, queue hint | `GET /api/robot/status` (~1.5s) | | Panel lỗi + RESET | `POST /api/robot/errors/reset` | | Pin, locale vi/en | từ `/api/robot/status` | | Joystick overlay | `POST /api/robot/joystick`, `POST /api/robot/cmd_vel` | | Menu user | `GET /api/auth/me` | Ẩn topbar nếu user không có quyền trang `missions`. --- ## 4. Mục lục tính năng ### 4.1 Dashboards — ✅ / 🔶 - **Quản lý:** list, filter, phân trang, tạo, xóa, designer (kéo thả grid, resize), **Permissions** theo user group, chế độ edit/operate. - API: `GET/PUT /api/dashboards`. **Widget:** | Widget | Trạng thái | Ghi chú | |--------|------------|---------| | `mission_button` | ✅ | Queue mission cố định | | `mission_group` | ✅ | Nút theo nhóm mission | | `mission_queue` | ✅ | Xem + xóa queue | | `pause_continue` | ✅ | Pause/continue (+ hủy mission) | | `mission_action_log` | 🔶 | Log mission đang chạy (~10 dòng) | | `map_locked` | 🔶 | Robot-centered, không pan/zoom | | `map` | 🔶 | Pose marker | | `io_connect` | ✅ | Connect/disconnect I/O | | `io_status` | ✅ | LED cổng digital | | `io_configuration` | ✅ | Bật/tắt output | | `logout_button` | ✅ | | | `robot_summary` | ⏳ | Chỉ tên robot | | PLC tab | ⏳ | «Coming soon» | | Hook / Joystick / Distributor widget | ❌ | Joystick chỉ trên topbar | File: `www/dashboard.js`. ### 4.2 Setup — Missions — 🔶 | UI | API | |----|-----| | Danh sách, tạo, sửa, xóa, Save, Save as | `GET/PUT /api/missions` | | Editor kéo-thả, If/While/Loop, embed mission con | | | Queue từ list (dialog biến) | `POST /api/mission_queue` | | Panel queue: sắp xếp, xóa, clear, pause/continue/cancel | `/api/mission_queue/*` | **Action types (22):** | Nhóm | Types | |------|-------| | Move | `move_to_position`, `move_to_marker`, `adjust_localization`, `switch_map`, `wait`, `set_speed` | | Logic | `if`, `while`, `loop`, `break`, `continue`, `pause` | | I/O | `set_digital_output`, `wait_digital_input`, `connect_bluetooth`, `disconnect_bluetooth`, `set_plc_register` | | Cart | `pick_cart`, `drop_cart` | | Misc | `user_log`, `play_sound` | **Ba cách chạy mission:** | Cách | Mô tả | |------|--------| | Dashboard | Widget mission button | | Setup → Missions | Icon queue → thêm vào queue | | Tích hợp ngoài | Modbus trigger, `POST /api/v2.0.0/mission_queue` | Queue chạy tuần tự; biến mission (`*_var`) hỏi operator khi enqueue. File: `www/missions.js`, `src/mission/mission_queue.cpp`. **Chưa có:** Prompt User, Try/Catch, Email, shelf actions, điều kiện If đầy đủ. ### 4.3 Setup — Maps — 🔶 | UI | API | |----|-----| | List theo site, tạo, xóa, import site bundle | `/api/maps`, `/api/sites`, `…/import` | | Editor: pan/zoom, wall/floor/position, zones | `PUT /api/maps` | | Upload ảnh, YAML, occupancy, undo | `/api/maps/…/image`, `…/yaml` | | Path preview (từ Paths → View) | overlay trên map editor | | Cảnh báo preferred zone khi có path cache | confirm trước save | **Zone types:** wall, floor, position, forbidden, preferred, unpreferred, speed, sound, directional, planner, I/O. **Chưa có:** markers, evacuation/limit-robots zones; mapping live đầy đủ. File: `www/maps.js`, `www/map-editor.js`. ### 4.4 Setup — Sounds — ✅ List, upload, nghe, play trên robot, CRUD (không xóa system sound). API: `/api/sounds`. File: `www/sounds.js`. ### 4.5 Setup — Transitions — ✅ > **Tham chiếu MiR** (xem [SOURCES_MiR.md](SOURCES_MiR.md)): > - MiR robot Reference Guide **rev 1.9** (03/2019, SW 2.6.0) — §4.1.4.3 *Switch Map*, §4.4 *Transitions* > - MiR robot Reference Guide **v2.2** (02/2021) — bản sửa cách diễn đạt, tr. 43 & 90–92 > - **MiRFleet** Reference Guide SW 2.5.0 (01/2019) — §4.3 *Elevators* > - **MiR Fleet Enterprise** v1.2 (01/2025) — tr. 107–108 *Transitions*, tr. 325–329 *Create transitions / Ramps / Elevators* List, filter, pager, CRUD dialog (site, from/to map, start/goal position, mission). API: `/api/transitions`. File: `www/transitions.js`. #### Nguyên lý hoạt động **Mục đích.** Transition xử lý việc robot chuyển từ map này sang map khác trong **cùng một site** (ví dụ hai nhà xưởng kề nhau, mỗi nơi một map). Khi đã cấu hình, mission chỉ cần `move_to_position` tới đích trên map khác; hệ thống tự chèn chuỗi chuyển map — người lập mission không phải gọi `switch_map` thủ công ở mọi chỗ. **Thành phần của một transition** | Trường | Ý nghĩa | |--------|---------| | Site | Cả hai map phải thuộc cùng site | | From map / To map | Map nguồn → map đích (chiều A→B và B→A là hai bản ghi / hai mission riêng) | | Start position | Position kiểu robot trên **map nguồn**, tại điểm hai map chồng lên nhau | | Goal position | Position kiểu robot trên **map đích**, **cùng vị trí vật lý** với Start (tên khác nhau, tọa độ thế giới trùng) | | Mission | Mission chuyển map (thường chứa action `switch_map` và các bước liên quan cửa/PLC nếu cần) | #### Phân tích chức năng lệnh Switch map (MiR §4.1 / dùng trong §4.4.1) **Định nghĩa (MiR).** `Switch map` đổi bản đồ navigation đang dùng trong một mission khi site có nhiều map. Hai map phải có vùng overlap để robot định vị lại trên map mới. Action này là **nền tảng** của Transition: khi Transition đã cấu hình, hệ thống tự chèn switch khi mission chỉ lệnh đi sang position thuộc map khác. > **Đính chính thuật ngữ (v1.9 → v2.2).** Bản 1.9 viết móc nối nhầm sang *Goal position*; bản **v2.2 sửa lại** cho đúng: điểm overlap phải khớp với **Entry position** của chính action Switch map. > > - v1.9: *"…physically overlaps the **Goal position** you select here"* > - v2.2: *"The Switch map action must be preceded by a Move action to the position in the current map that physically overlaps the **Entry position in the other map** that the robot is switching to."* > > → Khi đối chiếu tài liệu cũ, hiểu **Goal (transition) ≡ Entry (action Switch map)** — cùng một điểm vật lý. **Việc Switch map làm (ý nghĩa chức năng)** | Khía cạnh | Nội dung | |-----------|----------| | Input ngữ cảnh | Robot đang đứng tại điểm chồng trên map **from** (đã `move` tới Start trước đó) | | Thay đổi trạng thái | Đặt map **to** làm map active (bản đồ planner / localization) | | Định vị lại sau đổi map | Dùng Entry/Goal position trên map **to** (cùng chỗ vật lý với Start trên map from) để khớp pose trên map mới | | Đầu ra | Các lệnh `move_to_position` tiếp theo resolve theo map mới | Không di chuyển cơ học xuyên tường/tầng: robot **đứng yên (hoặc gần điểm chồng)**, đổi “hệ tọa độ map”, rồi tiếp tục mission trên map đích. **Mission Transition cần *mấy* Switch map? — MiR đổi quan điểm theo thời gian** Đây là điểm **hai thế hệ tài liệu MiR nói khác nhau**, cần biết để không cấu hình thừa: | Nguồn | Yêu cầu | Nguyên văn | |-------|---------|------------| | Reference Guide 1.9 (2019) | **Hai** Switch map | *"must include two Switch map actions: the first… the 'from' map and the defined start position, and the second… the 'to' map and the defined goal position"* | | Reference Guide v2.2 (2021) | **Hai** Switch map | giữ nguyên diễn đạt trên | | **Fleet Enterprise v1.2 (2025)** | **Một** Switch map | *"A transition mission that contains **a** Switch map action"* (tr. 107) · *"A transition mission must consist of **a** Switch map action that specifies which map the robot must switch to and which position it starts from"* (tr. 328) | Mô hình **hai** action (đời cũ): | # | Switch map | Ý nghĩa | |---|------------|---------| | 1 | From map + Start position | Định vị lại / khẳng định đang ở điểm chồng trên map nguồn trước khi đổi | | 2 | To map + Entry (= Goal) position | Đổi sang map đích và định vị lại tại cùng điểm vật lý | → **Kết luận cho robot_app:** thiết kế hiện tại (**một** `switch_map` → map đích, Start/Goal do bản ghi Transition + runtime lo) **trùng khớp với hướng dẫn mới nhất của MiR (2025)**, không phải sai lệch so với chuẩn. Action đầu trong mô hình cũ chỉ mang tính "khẳng định vị trí" — runtime robot_app đã đảm bảo bằng bước *đi tới Start* trước khi chạy mission TR. **Trường trên MiR (action editor)** | Trường | Nội dung | |--------|----------| | **Entry Position** | Position trên map **to** — điểm robot "bắt đầu" sau khi chuyển map (tương đương Goal của Transition) | | Biến | v2.2 cho phép chọn **XYZ icon** để đặt Entry Position thành **biến mission** (robot_app: chưa có) | **Trên robot_app hiện tại** | | MiR | robot_app | |--|-----|-----------| | Tham số UI | Entry position (+ ngữ cảnh from/to trong transition mission) | Chủ yếu `map_id` (`switch_map`) | | Runtime | Đổi map nav + localize tại entry | `setActiveMap` / ghi `robot_runtime.active_map_id` | | Vai trò với Transition | Nằm trong mission TR; auto-transition chèn quanh Start/Goal | Mission TR chạy `switch_map`; sau đó queue vẫn `setActiveMap(to)` và move Goal | | Số `switch_map` trong mission TR | 1.9 / v2.2: **2** · Fleet Enterprise 2025: **1** | **1** `switch_map` → map đích; Start/Goal do Transition + runtime lo → **khớp chuẩn 2025** | Triển khai: `MissionQueue` xử lý `type == "switch_map"` → `setActiveMapOnRobotRuntimeDoc`; auto-transition thêm một lần set map active sau mission TR. **Khi nào dùng Switch map** 1. **Trong mission Transition** (khuyến nghị) — cùng I/O thang / cửa nếu cần. 2. **Trong mission vận hành thủ công** — ít dùng nếu đã có Transition (trùng / dễ lệch so với auto). 3. **Không thay** `move_to_position`: Switch map không đi từ A đến B; chỉ đổi map tại điểm chồng. **Điều kiện thành công** - Đã tới vùng overlap (Start) trước khi đổi map. - Start/Goal cùng điểm vật lý, tên khác, map khác, cùng site. - Map đích tồn tại; sau switch, localization khớp Goal/Entry. **Điều kiện vật lý.** Start và Goal phải nằm đúng một điểm chồng (overlap) giữa hai map — robot dừng ngắn ở đó để định vị lại trên map mới trước khi tiếp tục đi. **Luồng runtime (robot_app)** — khi mission đang chạy gặp `move_to_position` / `adjust_localization` mà position thuộc map khác map đang active: ``` Map A (đang active) Map B (đích) ───────────────── ───────────── … đi bình thường Position đích ∈ Map B? │ ▼ Tìm transition A → B (TransitionStore.findBetween) │ không có → lỗi "No transition configured" ▼ 1. Di chuyển tới Start (trên Map A) 2. Chạy Transition mission (không cho auto-transition lồng nhau) 3. switch_map → đặt Map B active 4. Định vị / di chuyển tới Goal (trên Map B) 5. Tiếp tục move_to_position tới đích gốc trên Map B ``` Triển khai: `MissionQueue::runAutoTransitionIfNeeded` trong `src/mission/mission_queue.cpp`. Transition mission chạy với `allow_auto_transition = false` để tránh vòng lặp. **Tóm tắt hành vi người dùng.** Sau khi tạo transition trong UI (MiR §4.4.1): thiết kế mission như môi trường một map; khi queue chạy tới lệnh đi sang map khác, robot tự dừng tại điểm chồng, chạy mission chuyển map, đổi map, rồi tiếp tục — chỉ “nhìn thấy” khoảng dừng ngắn khi chuyển. #### Trường hợp đặc biệt: khu chuyển map là thang máy Hai tầng không “chồng sàn” như hai nhà xưởng. Điểm giao là **khu thang máy**. Với **thứ tự runtime hiện tại** (`đi tới Start` → chạy mission TR → `switch_map` → đi Goal), không nên đặt Start ngay trong cabin nếu cửa còn đóng — robot sẽ cố đi vào cabin **trước** khi gọi thang. ##### MiR chính thức nói gì về thang máy Hai sản phẩm Fleet của MiR xử lý thang máy **ngược nhau** — đây là căn cứ quan trọng cho lựa chọn thiết kế của robot_app: | Sản phẩm | Hỗ trợ thang máy | Cách làm | |----------|------------------|----------| | **MiRFleet SW 2.5.0** (2019) | ✅ **Có sẵn** (§4.3 *Elevators*) | Fleet tự "takes over", đưa robot vào/ra cabin đúng tầng và **tự đổi map nội bộ** | | **MiR Fleet Enterprise v1.2** (2025) | ❌ **Đã bỏ** | *"MiR Fleet Enterprise **does not support elevators**. Use MiR Fleet Integration API to define your own elevator control."* | Hướng dẫn của bản 2025 khi cần thang máy (tr. 311): > *"For elevators, you must make a **custom elevator control system**. You can create **transition positions inside the elevator** on the map for each floor and **set up transitions for each floor shift**."* → **Cách robot_app đang làm — mission TR tự viết logic I/O gọi thang + `switch_map`, transition riêng cho mỗi chiều/tầng — chính là cách MiR khuyến nghị ở bản mới nhất.** Không phải giải pháp chắp vá. **Mô hình elevator của MiRFleet 2.5.0** (tham khảo để đặt tên/bố trí position — xác nhận bố cục bên dưới là đúng chuẩn): | Trường MiRFleet | Ý nghĩa | Tương ứng robot_app | |-----------------|---------|---------------------| | **Position in the elevator** | Position robot **bên trong cabin** | `Cabin_A` / `Cabin_B` | | **Position in front of the elevator** | Position **ngoài cửa**, nơi robot **chờ thang tới và cửa mở** | `Tr_Start_AB` / `Tr_Goal_AB` | | **Entry mission** / **Exit mission** | MiR tách **hai** mission: vào thang và ra thang | robot_app gộp trong **một** mission TR | | **Floor** | Số tầng — **bắt buộc là số** (tầng trệt = `0`, hầm = `-1`) | Quy ước đặt tên map | | **IP address** | IP hệ thống thang (do nhà tích hợp thang cung cấp) | Cấu hình I/O module | | **Turn in place** | Cho phép robot xoay trong cabin hay không | Nới collision trong cabin | | **Door** | 1 = xoay 180° ra cùng cửa vào · 2 = không xoay, ra cửa đối diện | Thiết kế Cabin/Goal theo hướng | Lưu ý MiRFleet: map mỗi tầng **phải chứa hai elevator position** (trong cabin + trước cửa) thì mới chuyển map được — trùng với bố trí 4 position của robot_app. **Bố trí position khuyến nghị (thang máy + robot_app)** | Position | Map | Vị trí | Vai trò | |----------|-----|--------|---------| | Pos_A | Map_A | Điểm làm việc | Đích/điểm xuất phát vận hành | | **Tr_Start_AB** | Map_A | **Trước cửa thang** (chờ) | Start của Transition — runtime đi tới đây trước | | Cabin_A | Map_A | **Trong cabin** | Dùng trong mission TR (vào thang) | | Cabin_B | Map_B | **Trong cabin** (cùng XY Cabin_A) | Định vị lại sau `switch_map` | | **Tr_Goal_AB** | Map_B | **Trước cửa thang** tầng B | Goal của Transition — ra thang / chờ cửa | | Pos_B | Map_B | Điểm làm việc | Đích vận hành | **Cách viết mission** 1) Mission vận hành `Go_A_to_B` — chỉ việc “đi làm”, **không** viết cửa/cabin: | # | Action | Position | |---|--------|----------| | 1 | `move_to_position` | Pos_A *(tuỳ chọn)* | | 2 | `move_to_position` | **Pos_B** | Khi tới bước 2, runtime thấy Pos_B ∈ Map_B → tự chạy Transition A→B rồi mới tới Pos_B. 2) Mission Transition `TR_MapA_to_MapB` — **toàn bộ** cửa → vào → đi tầng → ra: | # | Action | Nội dung | |---|--------|----------| | 1 | `set_digital_output` / PLC | Gọi thang / chọn tầng A (nếu cần) | | 2 | `wait_digital_input` | Chờ thang tới, **cửa mở** | | 3 | `move_to_position` | **Cabin_A** (vào thang) | | 4 | I/O | Đóng cửa, lệnh đi **tầng B** | | 5 | `wait_digital_input` | Chờ tới tầng B, cửa mở | | 6 | `switch_map` | **Map_B** | | 7 | `adjust_localization` hoặc `move_to_position` | **Cabin_B** (định vị lại trên map mới) | | 8 | `move_to_position` | Ra gần cửa / **Tr_Goal_AB** *(tuỳ chọn — runtime cũng đi Goal sau)* | 3) Transition record: Start = `Tr_Start_AB`, Goal = `Tr_Goal_AB`, Mission = `TR_MapA_to_MapB`. **Chuỗi thực tế khi bấm Dashboard** ``` Pos_A → (ops) move Pos_B kích hoạt Transition → runtime: tới Tr_Start_AB (cửa tầng A) → TR: gọi thang → vào Cabin_A → đi tầng → switch_map → Cabin_B → ra cửa → runtime: tới Tr_Goal_AB (cửa tầng B) → (ops) tiếp tục tới Pos_B ``` **Lưu ý** - Cabin_A / Cabin_B vẫn là điểm **định vị lại** an toàn trong cabin; Tr_Start/Goal đặt **ngoài cửa** cho khớp thứ tự “đi Start trước khi chạy TR”. - Chiều B→A: mission TR + transition riêng. - I/O module thang phải cấu hình trước (Setup → I/O modules). - Cabin hẹp: nới collision / speed zone trong cabin. **Lưu ý vận hành** - Cần **hai** transition (và thường hai mission) nếu muốn đi hai chiều A↔B. - Chưa có transition A→B mà mission yêu cầu position trên B → mission **fail**. - Transition được export/import theo site bundle cùng maps. - Quyền: resource `maps` (xem ma trận auth §2). - Qua thang: lỗi I/O / tín hiệu cửa không tới → mission dừng ở bước Wait; không tự retry tầng khác trừ khi viết Try/Catch (chưa đủ trên robot_app). **Nguyên tắc thiết kế map & transition** (MiR Fleet Enterprise 2025, tr. 325) | Nguyên tắc | Lý do | |------------|-------| | Cắt map tại nơi có **một lối vào duy nhất** | Robot buộc phải đi qua transition position để đổi map → cắt ở chỗ thắt cổ chai là tự nhiên nhất | | Được phép tạo **nhiều transition** giữa cùng cặp map | Nhưng mỗi cái tốn công cấu hình + bảo trì → chỉ thêm khi thật cần | | **Bắt buộc** transition cho **cả hai chiều** | Hai chiều có thể **cùng** một chỗ hoặc **khác** chỗ | | Transition hướng **theo chiều luồng giao thông** | Tránh robot đi ngược dòng tại điểm giao | | Muốn kiểm soát chỗ robot chuyển vùng → **chia nhỏ map** | Kết hợp map zone để điều tiết planner/traffic | **Ramp (dốc nối tầng).** MiR xếp ramp cùng nhóm với thang máy — *"Transitions are often also used when changing floors with ramps or elevators."* Nếu robot không tự plan qua dốc, MiR khuyên thêm **Access zone** phủ lên vạch đỏ phát hiện sàn ở chân dốc. robot_app: chưa có zone loại Access. #### Hướng dẫn cài đặt: gọi robot từ điểm A (map A) → điểm B (map B) Mục tiêu: operator bấm một nút trên Dashboard → robot đi từ **Pos_A** trên **Map_A** tới **Pos_B** trên **Map_B**. Transition A→B chạy tự động khi mission yêu cầu position sang map khác. ##### Bước 1 — Thiết kế map 1. **Setup → Maps**: tạo (hoặc chọn) **một Site** chứa cả Map_A và Map_B. 2. Tạo **Map_A** và **Map_B**; upload ảnh / occupancy / YAML nếu có; vẽ floor, tường, forbidden… 3. Trên **Map_A** tạo các robot position: - **Pos_A** — điểm xuất phát (điểm A). - **Tr_Start_AB** — điểm chồng / chờ chuyển map (sàn kề: vùng overlap; **thang máy: trước cửa thang**). - *(Thang máy)* **Cabin_A** — trong cabin, dùng trong mission TR. 4. Trên **Map_B** tạo: - **Tr_Goal_AB** — điểm chồng tương ứng (thang máy: trước cửa tầng B). - *(Thang máy)* **Cabin_B** — trong cabin, cùng XY với Cabin_A. - **Pos_B** — điểm đích (điểm B). 5. Lưu map. Đảm bảo map active ban đầu là Map_A khi robot đang đứng ở Pos_A (định vị / Adjust localization nếu cần). > **Cách tạo cặp Start/Goal cho *đúng cùng một điểm vật lý*** — quy trình MiR Fleet Enterprise 2025 (tr. 327). Đây là bước dễ sai nhất: gõ tay toạ độ hai map hầu như luôn lệch. > > | # | Thao tác | > |---|----------| > | 1 | Định vị robot chuẩn trên **Map_A** (Adjust localization) | > | 2 | **Lái robot tới đúng điểm overlap** giữa hai map | > | 3 | Xoay robot **hướng đúng chiều đi vào khu vực mới** | > | 4 | ⚠️ **Từ đây KHÔNG di chuyển robot nữa** cho tới hết bước 7 | > | 5 | Trên Map_A: tạo Robot position **từ vị trí hiện tại của robot** → `Tr_Start_AB` | > | 6 | **Kích hoạt Map_B**, định vị lại robot **mà không dời robot** | > | 7 | Trên Map_B: tạo Robot position **từ vị trí hiện tại** → `Tr_Goal_AB` | > > Đặt tên position thể hiện rõ nó thuộc transition nào. MiR: *"The transitions must point in the same direction as the traffic flow."* > Thang máy: logic gọi thang / vào–ra cabin viết trong mission TR (Bước 2), không viết trong mission vận hành. ##### Bước 2 — Tạo mission chuyển map (dùng cho Transition) **Setup → Missions** → tạo mission riêng, ví dụ `TR_MapA_to_MapB` (không phải mission vận hành hàng ngày). Nội dung tối thiểu (sàn kề nhau): | # | Action | Tham số gợi ý | |---|--------|----------------| | 1 | `switch_map` | Map = Map_B | | 2 | (tuỳ chọn) `adjust_localization` / `move_to_position` | Tr_Goal_AB | Nội dung kiểu thang máy — xem bảng đầy đủ ở mục *Khu chuyển map là thang máy*; tóm tắt: `gọi thang → chờ cửa → move Cabin_A → đi tầng → chờ cửa → switch_map Map_B → Cabin_B → (ra cửa)` Lưu mission. Chiều ngược B→A cần mission + transition riêng nếu muốn đi hai chiều. ##### Bước 3 — Tạo Transition **Setup → Transitions → Create transition:** | Trường | Giá trị | |--------|---------| | Site | Site chứa Map_A, Map_B | | From map | Map_A | | To map | Map_B | | Start position | Tr_Start_AB | | Goal position | Tr_Goal_AB | | Mission | `TR_MapA_to_MapB` | Lưu. Không có bản ghi này → mission vận hành sẽ lỗi *No transition configured* khi đi sang Map_B. ##### Bước 4 — Tạo mission vận hành A → B **Setup → Missions** → tạo ví dụ `Go_A_to_B`: | # | Action | Tham số | |---|--------|---------| | 1 | `move_to_position` | Pos_A (tuỳ chọn — đảm bảo đứng đúng điểm A) | | 2 | `move_to_position` | **Pos_B** | Chỉ cần gọi Pos_B trên Map_B. Runtime tự: `… → tới Tr_Start_AB → chạy TR_MapA_to_MapB → switch map → Tr_Goal_AB → tới Pos_B`. Không nhúng toàn bộ logic chuyển map vào `Go_A_to_B` (trừ khi cố ý không dùng Transition). ##### Bước 5 — Dashboard gọi robot 1. **Dashboards** → tạo / mở dashboard (vd. *Operator*). 2. Chế độ **Edit** → **Add widget**: - `mission_button` — chọn mission `Go_A_to_B`, đặt nhãn nút (vd. «A → B»). - Nên thêm `mission_queue`, `pause_continue`, (tuỳ chọn) `map` / `mission_action_log`. 3. Gán **Permissions** user group được phép xem/dùng dashboard. 4. Lưu → chuyển **Operate**. Operator bấm nút → `POST /api/mission_queue` → robot_app chạy queue. Có thể kiểm tra tại Setup → Missions (panel queue) hoặc Monitoring → Mission log. ##### Sơ đồ tổng thể ``` [Map design] Pos_A, Tr_Start_AB trên Map_A Tr_Goal_AB, Pos_B trên Map_B │ ▼ [Mission TR] TR_MapA_to_MapB (switch_map ± I/O thang) │ ▼ [Transition] Map_A → Map_B, Start/Goal, gắn mission TR │ ▼ [Mission ops] Go_A_to_B: move → Pos_A?, move → Pos_B │ ▼ [Dashboard] mission_button → queue → robot chạy A→B (auto transition) ``` ##### Kiểm tra nhanh | Kiểm tra | Kỳ vọng | |----------|---------| | Bấm nút Dashboard | Entry xuất hiện trên mission queue | | Robot đang Map_A, đích Pos_B | Log: Auto transition Map_A → Map_B | | Thiếu Transition | Lỗi *No transition configured* | | Sai I/O thang | Dừng ở Wait for input trong mission TR | ##### Logic vận hành (runtime) Thứ tự khi operator gọi robot đi **Pos_A (Map_A) → Pos_B (Map_B)**: ``` Operator bấm mission_button (Go_A_to_B) │ ▼ POST /api/mission_queue → entry status = pending │ ▼ MissionQueue worker (FIFO) lấy entry pending đầu tiên → status = executing runner.state = running │ ▼ Thực thi từng action của Go_A_to_B ``` **Action 1 — `move_to_position` Pos_A** (nếu có) 1. Resolve Pos_A; so sánh `map_id` với map đang active. 2. Cùng Map_A → đi thẳng tới Pos_A; cập nhật `last_position_id`. 3. Khác map → kích hoạt auto-transition trước (xem dưới), rồi mới hoàn tất move. **Action 2 — `move_to_position` Pos_B** (điểm mấu chốt) 1. Resolve Pos_B → thuộc **Map_B**; active đang là **Map_A** → khác map. 2. Gọi `runAutoTransitionIfNeeded(Map_A, Map_B)`: - Không tìm thấy transition A→B → **fail** (`No transition configured`). - Có transition: 1. Di chuyển tới **Start** (`Tr_Start_AB`) trên Map_A 2. Chạy mission gắn transition (`TR_MapA_to_MapB`) với `allow_auto_transition=false` (không lồng transition) — trong đó: I/O thang (nếu có), `switch_map`, … 3. Runtime đặt map active = **Map_B** (sau mission TR) 4. Di chuyển tới / định vị lại tại **Goal** (`Tr_Goal_AB`) trên Map_B 3. Sau transition: hoàn tất `move_to_position` tới **Pos_B** trên Map_B. 4. `last_position_id` = Pos_B. **Kết thúc mission** | Kết quả | Hành vi | |---------|---------| | Thành công | entry = finished / done; runner → idle (nếu hết queue); ghi Mission log (persisted) | | Cancel (topbar / pause_continue) | dừng giữa action; entry hủy | | Lỗi (thiếu transition, I/O, switch_map…) | runner.state = error; ghi error log | **Pause / Continue / Cancel** - Queue chỉ lấy entry `pending` khi không `paused`. - Pause: dừng nhận / tạm dừng runner; Continue: tiếp tục. - Cancel: hủy mission đang `executing`. **Song song nhiều lệnh** - Nhiều lần bấm nút → nhiều entry **pending** xếp hàng; chỉ **một** mission chạy tại một thời điểm (FIFO). **Topbar / Dashboard phản ánh** - Strip mission + state: từ `GET /api/robot/status` / runner. - Widget `mission_queue`: danh sách pending/executing. - Widget `mission_action_log`: vài dòng log action đang chạy. - Monitoring → Mission log: lịch sử sau khi chạy xong. ### 4.6 Setup — Users — ✅ List, filter, CRUD, PIN 4 số. API: `/api/users`. File: `www/users.js`. ### 4.7 Setup — User groups — ✅ List, CRUD, ma trận quyền 7 resource, `allow_pin`. API: `/api/user_groups`. File: `www/user-groups.js`. ### 4.8 Setup — I/O modules — 🔶 List, filter, CRUD; loại Bluetooth / WISE; test TCP từ dialog. API: `/api/io_modules`, `…/test`. Connect runtime qua dashboard widgets. File: `www/io-modules.js`. ### 4.9 Setup — Paths — 🔶 **Không có Create** — path cache tự tạo khi robot chạy giữa hai position. | UI | API | |----|-----| | List, filter, pager | `GET /api/paths` | | View trên map | `MapsApp.openEditorWithPath` | | Delete | `DELETE /api/paths/:id` | Backend: `PathStore`, `PathPlanner`, `PathService`; invalidate khi đổi position; export/import trong site bundle. File: `www/paths.js`. ### 4.10 Setup — Path guides — ✅ List, wizard tạo, sửa starts/vias/goals, xóa. API: `/api/path_guides`. File: `www/path-guides.js`. ### 4.11 Setup — Build robot — 🔶 Chỉ **Distributor**. Layout đa cấu hình, LiDAR/IMU CRUD, canvas robot diff. API: `/api/layouts`, `/api/lidars`, `/api/imus`, `/api/state`. File: `www/app.js` (`pageConfig`). ### 4.12 Chưa triển khai (Setup) | Mục | Trạng thái | |-----|------------| | Shelf types | ❌ | --- ## 5. Monitoring — ✅ Flyout 6 mục (giống MiR): **Analytics**, **System log**, **Error logs**, **Hardware health**, **Safety system**, **Mission log**. Frontend: `www/monitoring.js` (poll ~2.5s), layout `www/index.html`, style `www/style.css`, i18n `www/i18n.js`. Backend: `src/monitoring/monitoring_service.*`, routes `src/server/api_monitoring_routes.cpp`, mission persist trong `src/storage/mission_run_store.*`. ### 5.1 Analytics — ✅ - **UI**: chọn `Start date` / `End date`; preset (current week/7d/30d/365d); `Grouping` day/month; chart mode **Bar graph** / **Accumulated**. - **API**: `GET /api/monitoring/analytics?start=YYYY-MM-DD&end=YYYY-MM-DD&grouping=day|month` - **Data**: document SQLite `monitoring_analytics` (tích lũy khi robot di chuyển; tick từ `RobotRuntime`). ### 5.2 System log — ✅ - **UI**: bảng `State` (dot màu), `Module`, `Message`, `Time`; filter + pager; refresh. - **API**: `GET /api/monitoring/system_log` - **Data**: document SQLite `monitoring_system_log` (sự kiện hệ thống + một phần sự kiện error). ### 5.3 Error logs — ✅ - **UI**: bảng `Description`, `Module`, `Time`; nút **Generate log** (user log); **Delete all**; xóa từng dòng; **Download** từng log. - **API**: - `GET /api/monitoring/error_logs` - `POST /api/monitoring/error_logs/generate` - `DELETE /api/monitoring/error_logs` - `DELETE /api/monitoring/error_logs/` - `GET /api/monitoring/error_logs//download` - **Data**: document SQLite `monitoring_error_logs` (+ action snapshot ring `monitoring_action_ring`). ### 5.4 Hardware health — ✅ - **UI**: accordion nhóm (Computer, Motors, Power system, Safety system, Sensors, Modbus) → expand xem component. - **API**: `GET /api/monitoring/hardware_health` - **Data**: ghép từ `robot_status` + `StateRepository` (Build robot: LiDAR/IMU). ### 5.5 Safety system — ✅ - **UI**: live cards (E-stop, Front scanner, Rear scanner) với trạng thái Free/Blocked, Released/Activated. - **API**: `GET /api/monitoring/safety` - **Data**: từ `robot_status.safety` (hiện default/placeholder nếu chưa có phần cứng thật). ### 5.6 Mission log — ✅ (MiR-like, có lịch sử bền vững) - **UI**: bảng Mission/State/Message/Start time/Ran for/Started by; filter + pager; **View action log** (icon mắt); Phase 3/4: **Delete history**, xóa từng run, **Download run bundle**. - **API**: - Queue hiện tại: `GET /api/mission_queue` - Lịch sử persist: - `GET /api/monitoring/mission_runs?limit=N` - `GET /api/monitoring/mission_runs//actions` - `DELETE /api/monitoring/mission_runs` (clear history) - `DELETE /api/monitoring/mission_runs/` - `GET /api/monitoring/mission_runs//download` (Phase 4 bundle JSON: run + actions + robot snapshot) - **Data**: - Queue: document SQLite `mission_queue` (mất khi clear queue) - History: tables `mission_runs`, `mission_run_actions` (không mất khi clear queue) - **Retention** (Phase 4): giữ tối đa ~2000 runs gần nhất (DB bounded history). **Chưa có (so với MiR)**: error log file mã hóa gửi Support; retention cấu hình qua UI; safety/hardware đọc từ phần cứng thật (thay vì stub/snapshot). --- ## 6. System — Integrations — 🔶 File: `www/integrations.js`. | UI | API | |----|-----| | Modbus mission triggers (tạo, xóa, fire coil) | `/api/triggers`, `/api/modbus/coils` | | REST v2 — ví dụ enqueue | `POST /api/v2.0.0/mission_queue` | | Fleet schedules (tạo, xóa, run now) | `/api/fleet/schedules` | **Modbus (tương thích tích hợp):** - Mission triggers: coil 1001–2000 → enqueue mission. - Action commands: coil 1–6 (continue, pause, cancel mission, clear queue, clear error). - Server Modbus TCP `:5502`. **Chưa có:** Settings, Backups, PLC registers UI, Processes, Software versions; sửa trigger/schedule (chỉ add/delete). ### 6.1 System — Settings — ✅ Trang Settings theo style MiR (card/section + Apply). Frontend: `www/settings.js`. **Nhóm Logging & retention:** - `retention_mission_runs`: số mission runs lịch sử tối đa (bounded history; ảnh hưởng Mission log Phase 3/4). - `retention_system_log`: số dòng system log tối đa. - `retention_error_logs`: số dòng error log tối đa. - `log_level`: `error|warn|info|debug` (hiện mới dùng làm setting; chưa ảnh hưởng sâu tới backend logging). **API:** - `GET /api/settings` - `PUT /api/settings` **Data:** document SQLite `settings`. Retention được áp dụng khi ghi log/runs mới (trim tự động). --- ## 7. Help — ⏳ Trang `pageHelp`: text trỏ tới System → Integrations và tài liệu API. **Chưa có:** API browser nhúng, service book. --- ## 8. REST API robot Base tương thích: `http:///api/v2.0.0/` | Endpoint | Mô tả | |----------|--------| | `GET /status` | Trạng thái robot | | `GET /missions` | Danh sách mission | | `GET /mission_queue` | Queue hiện tại | | `POST /mission_queue` | Enqueue (`mission_id`) | | `DELETE /mission_queue` | Xóa queue | Xác thực: session cookie hoặc Bearer (ưu tiên session web); tích hợp ngoài có thể dùng Basic tùy cấu hình. **Robot runtime:** `GET /api/robot/status`, start/pause, joystick, active map, sound play. --- ## 9. Tóm tắt triển khai ``` ✅ Hoàn thiện tốt: Sign in, Users, User groups, Sounds, Transitions, Path guides, Monitoring 🔶 Đang dùng được: Dashboard, Missions, Maps, I/O, Paths, Integrations, Topbar, Build robot ⏳ Placeholder: Help, Robot summary widget, PLC tab ❌ Chưa bắt đầu: Shelf types, Analytics/Error logs/Hardware/Safety, System Settings/Backups ``` **File frontend chính:** `index.html`, `nav.js`, `topbar.js`, `auth.js`, `app.js`, `dashboard.js`, `missions.js`, `maps.js`, `map-editor.js`, `sounds.js`, `transitions.js`, `users.js`, `user-groups.js`, `io-modules.js`, `paths.js`, `path-guides.js`, `integrations.js`, `monitoring.js`, `style.css`, `i18n.js`. --- ## 10. Vận hành và kiểm thử ```bash ./scripts/lm.sh test run # smoke + pytest docker compose up --build -d # sau sửa www/ ``` - Dữ liệu: `data/RBS.db`, `data/auth.json`, `data/state.json`. - Hard refresh (`Ctrl+Shift+R`) nếu cache JS/CSS.