專案

一般

配置概況

Feature #1380

是由 陳國瑋18 天 前更新

# Branch Admin 工程師工具(FE Implementation Spec) 

 ## 1. 目標與範圍 背景 

  Branch Admin 新增一個只供 Engineer 使用的「工程師工具」工作頁,讓工程師可以: 

 1. 對指定 CloudLink 需要新增一個給 `Engineer` Role 使用的「工程師工具」頁面,支援工程師在後台針對指定 Charge Point / Connector 執行受控 操作受控 Modbus 讀取與控制。 
 2. 由 Command,以及透過 Branch 向指定  Charge Point 發送 OCPP 1.6 CSMS Command。 
 3. 查詢每次操作的 request、response、錯誤、耗時與完整 event timeline。 
 4. 以 Fortune(華城)充電樁測試為主要情境,同時支援 CloudLink 與其他廠牌的 capability 顯示。 

 Backend 對應票為 #1381,目前已完成並部署至 Jimmy。FE 不需要自行設計 Backend contract;本 Description、Backend OpenAPI 與附件 Plan 為實作依據。 此需求主要用於 Fortune(華城)充電樁測試,但設計上需要保留其他廠牌的能力標示與稽核紀錄。 

 ## 2. 權限與導覽 FE 工作範圍 

 - 新路由:`/engineer-tools`。 請依附件 Plan 實作 Branch Admin UI。重點如下: 

 1. 側邊欄新增獨立頁面:「工程師工具」。 
 - 側邊欄名稱:「工程師工具」。 2. 僅 `Engineer` Role 可見與可進入。 
 - 只有取得 Backend Engineer function/menu 的使用者顯示入口並可進入。 
 - 不要在 FE hardcode `ADMIN`、`power_user` 或只判斷 role name;這兩個既有帳號已由 Backend 加入 Engineer Role。 
 - FE 隱藏入口不是安全邊界,所有 API 仍由 Backend 驗證權限。 
 - 在既有 Connector 清單或詳情頁增加工具 icon,可 deep-link: 
   `/engineer-tools?connectorId={connectorId}&tab=ocpp` 
 - 進入 deep-link 後仍須重新向 API 取得 target 與 capability,不可直接信任 query string。 

 ## 3. 頁面 Layout 

 沿用既有 dashboard shell,使用全寬工作區,不做 landing page,也不要把整個頁面包成多層 Card。 

 ### Target Bar 

 頁面頂部固定顯示: 

 - 頁面頂部提供 Charge Point searchable select 
 - / Connector searchable select 選擇器。 
 - online/offline status 4. 主區域分為三個 tabs:`Modbus`、`OCPP`、`操作紀錄`。 
 - vendor 
 - model 
 - firmware version 
 - capability badge 

 規則: 

 - 先選 Charge Point,再載入該 Charge Point 的 catalog/capability。 
 - 5. Connector 只在 connector-level command 或 Modbus command 時必填。 
 - 從 Connector 清單/詳情頁可 deep-link 進入時,自動解析並預選其 Charge Point 與 到工程師工具並預選 Connector。 

 ### Tabs 

 Target Bar 下方固定三個 tabs: 

 1. `Modbus` 
 2. `OCPP` 
 3. `操作紀錄` 

 Desktop:command list 與 form/result 使用左右兩欄。 

 Mobile:改為上下排列。 

 Command list、form、status panel 應有穩定尺寸,切換 command 或狀態時不得造成整頁寬度跳動或文字重疊。 

 ## 4. Command Catalog 與 Capability 

 畫面能力唯一來源: 

 `GET /api/engineer/commands/catalog?chargePointId={chargePointId}` 

 回傳共 31 筆:19 個 OCPP commands + 12 個 6. Modbus operations。 

 每筆主要欄位: 

 - `protocol`:`OCPP` 或 `MODBUS` 
 - `command` 
 - `category` 
 - `targetLevel`:`CHARGE_POINT` 或 `CONNECTOR` 
 - `riskLevel`:`LOW`、`MEDIUM`、`HIGH` 
 - `writeOperation` 
 - `schema`:產生表單與前端預驗證使用的 JSON Schema 
 - `capability`:`VERIFIED`、`UNVERIFIED`、`UNSUPPORTED` 
 - `capabilityNote` 

 Capability UI: 

 - `VERIFIED`:正常啟用。 
 - `UNVERIFIED`:允許執行,但在送出按鈕附近顯示非阻斷警告。 
 - `UNSUPPORTED`:禁用執行按鈕並顯示 `capabilityNote`。 
 - 不要在 FE 重複維護 vendor allowlist 或自行把 `UNVERIFIED` 判定成 supported。 
 - FE 可以維護中文標籤、欄位順序與圖示,但 command key、schema、target、risk、capability 必須來自 API。 

 ## 5. Modbus Tab 

 Modbus 只允許 CloudLink Connector。其他廠牌 catalog 會回 `UNSUPPORTED`,UI 必須禁用。 

 ### 允許的 12 個 Operations 

 Read: 

 - `CONNECTION_TEST` 
 - `READ_SUPPLY_STATE` 
 - `READ_VOLTAGE` 
 - `READ_CURRENT` 
 - `READ_ACTIVE_POWER` 
 - `READ_APPARENT_POWER` 
 - `READ_POWER_FACTOR` 
 - `READ_TOTAL_ENERGY` 
 - `READ_BALANCE` 
 - `READ_METER_ID` 

 Write: 

 - `SET_SUPPLY_STATE` 
 - `SET_METER_ID` 

 UI 分組: 

 - 連線與狀態 
 - 電力讀值 
 - 控制 

 禁止提供: 

 - tab 顯示受控指令集,不提供 raw Modbus function code code/register/payload。 
 - raw register address/value 
 - raw payload 
 - 清除歷史資料 
 - Browser 直接連 charge_point API 

 Read operation 的 `parameters` 必須是空 object。 

 `SET_SUPPLY_STATE`: 

 ```json 
 { 
   "connectorId": "CP001-001", 
   "operation": "SET_SUPPLY_STATE", 
   "parameters": { "enabled": true } 
 } 
 ``` 

 `SET_METER_ID`: 

 ```json 
 { 
   "connectorId": "CP001-001", 
   "operation": "SET_METER_ID", 
   "parameters": { "meterId": 1 } 
 } 
 ``` 

 `meterId` 範圍為 1–255。 

 文案使用「供電控制」;可以用次要說明標示底層由智慧電表 Test Mode 執行,但不要新增獨立 Test Mode command。 

 確認規則: 

 - Read command:直接執行,不顯示確認。 
 - `SET_SUPPLY_STATE`、`SET_METER_ID`:各顯示一次簡短確認。 
 - 不要求填寫「操作原因」。 
 - 不做額外第二次確認。 

 ## 6. 7. OCPP Tab 

 提供以下全部 tab 提供 19 個 OCPP 1.6 CSMS → Charge Point commands: 

 - `RemoteStartTransaction` 
 - `RemoteStopTransaction` 
 - `Reset` 
 - `ChangeAvailability` 
 - `UnlockConnector` 
 - `TriggerMessage` 
 - `DataTransfer` 
 - `GetConfiguration` 
 - `ChangeConfiguration` 
 - `ClearCache` 
 - `ReserveNow` 
 - `CancelReservation` 
 - `SetChargingProfile` 
 - `ClearChargingProfile` 
 - `GetCompositeSchedule` 
 - `GetDiagnostics` 
 - `UpdateFirmware` 
 - `GetLocalListVersion` 
 - `SendLocalList` 

 功能要求: 

 - command 依 catalog `category` 分組,並提供搜尋。 
 - 使用 segmented control 切換「表單」與「JSON」。 
 - 兩種模式共用同一份 payload state,切換模式不能丟失資料。 
 - 表單依 catalog `schema` 產生或映射。 
 - schema validation error 顯示到 JSON path/欄位。 
 - Backend 仍會執行同一份 schema validation,FE validation 只是提早提示。 
 - `Reset` 送出前顯示一次簡短確認。 
 - 其他 OCPP command 不增加確認。 
 - `GetDiagnostics`、`UpdateFirmware` 的 HTTP/HTTPS location 由 Engineer 輸入;FE 不需實作檔案上傳。 

 OCPP request: 

 ```json 
 { 
   "chargePointId": "CP001", 
   "connectorId": null, 
   "command": "TriggerMessage", 
   "payload": { 
     "requestedMessage": "Heartbeat" 
   } 
 } 
 ``` 

 若 command 為 connector-level,送出 `connectorId`;Backend 會驗證該 Connector 是否屬於選定 Charge Point,並在需要時補入 OCPP numeric connectorId。 

 ## 7. Backend API Contract 

 ### 7.1 Catalog 

 `GET /api/engineer/commands/catalog?chargePointId={chargePointId}` 

 用途:取得 31-command catalog、schema、risk、target 與 capability。 

 ### 7.2 Execute OCPP 

 `POST /api/engineer/ocpp/commands` 

 Body: 

 ```json 
 { 
   "chargePointId": "CP001", 
   "connectorId": null, 
   "command": "TriggerMessage", 
   "payload": { 
     "requestedMessage": "Heartbeat" 
   } 
 } 
 ``` 

 ### 7.3 Execute Modbus 

 `POST /api/engineer/modbus/commands` 

 Body: 

 ```json 
 { 
   "connectorId": "CP001-001", 
   "operation": "READ_SUPPLY_STATE", 
   "parameters": {} 
 } 
 ``` 

 ### 7.4 Command Detail 

 `GET /api/engineer/commands/{commandLogId}` 

 主要欄位: 

 - target snapshot:chargePointId、connectorId、connectorNo、vendor、model、firmwareVersion 
 - protocol、command、capability、operatorId 
 - correlationId、ocppUniqueId 
 - requestPayload、maskedRequestPayload、responsePayload 
 - state、protocolStatus 
 - errorCode、errorDescription、errorDetails 
 - createdAt、sentAt、respondedAt、completedAt、elapsedMs 
 - events 

 顯示規則: 

 - 預設顯示 `maskedRequestPayload`,不要預設展開 raw `requestPayload`。 
 - event timeline 依 `sequenceNo` 顯示。 
 - correlationId、ocppUniqueId、commandLogId 提供 copy icon。 
 - 長 的結構化表單與 advanced JSON 與 ID 必須 wrap/scroll,不得撐破 drawer。 

 ### 7.5 Command Search 

 `POST /api/engineer/commands/search` 

 Body 所有 filter 都可選: 

 ```json 模式。 
 { 
   "from": null, 
   "to": null, 
   "protocol": null, 
   "command": null, 
   "chargePointId": null, 
   "connectorId": null, 
   "operatorId": null, 
   "state": null, 
   "correlationId": null, 
   "ocppUniqueId": null, 
   "page": 0, 
   "size": 20 
 } 
 ``` 

 規則: 

 - `page` 從 0 起算。 
 - `size` 最大 100。 
 - response 是 Spring Page,使用 `content`、`totalElements`、`totalPages`、`number`、`size`。 

 ## 8. 202 Response 與 Polling 

 兩支 execute API 成功時回 HTTP 202: 

 ```json 已知不支援的 command 需禁用並顯示原因;未驗證能力可送出但需顯示警告。 
 { 
   "commandLogId": "uuid", 
   "correlationId": "uuid", 
   "ocppUniqueId": "uuid", 
   "state": "SENT", 
   "capability": "UNVERIFIED", 
   "createdAt": "2026-07-14T22:43:17.151298" 
 } 
 ``` 

 核心規則: 

 - HTTP 202/`SENT` 只表示 Branch 已把 OCPP CALL 送入 WebSocket。 
 - 這不代表 Charge Point 已執行完成。 
 - FE 必須用 `commandLogId` 輪詢 detail API。 
 - 建議畫面可見時每 1 秒輪詢一次。 
 - 使用者離開頁面不會取消已送出的 command。 
 - 快速設備可能在 POST 回來前已完成;FE 要先檢查 create response 的 `state`,若已是 terminal 就不需等待。 

 Non-terminal states: 

 - `CREATED` 
 - `VALIDATED` 
 - `DISPATCHING` 
 - `SENT` 
 - `RUNNING` 

 Terminal states,收到後停止 polling: 

 - `COMPLETED` 
 - `REJECTED` 
 - `CALL_ERROR` 
 - `SEND_FAILED` 
 - `FAILED` 
 - `TIMEOUT` 
 - `STATUS_UNKNOWN` 

 `GetDiagnostics`、`UpdateFirmware` 可能先進入 `RUNNING`,後續 progress 由 detail events 顯示。 

 ## 9. 操作紀錄 Tab 

 Filters: 

 - 時間範圍 Reset、供電開關、修改 Meter ID 需顯示一次簡單確認。 
 - protocol 
 - command 
 - Charge Point 
 - Connector 
 - operator 
 - state/result 
 - correlationId 
 - ocppUniqueId 

 Table columns: 

 - 執行時間 
 - protocol/command 
 - target 
 - operator 
 - state/protocol status 
 - elapsed 
 - correlationId 

 點擊 row 開啟 detail drawer,顯示: 

 - target/device snapshot 
 - masked request/response JSON 
 - CALLERROR 或 local error 
 - command event timeline 
 - commandLogId、correlationId、ocppUniqueId 

 ## 10. HTTP Error Handling 操作紀錄 tab 顯示 request、response、錯誤、耗時、操作者、目標設備、correlation id、OCPP unique id,以及 GetDiagnostics / UpdateFirmware 的後續狀態時間軸。 

 - `400`:payload/schema/target/Modbus parameters 錯誤。顯示 Backend message,schema error 儘量對應到欄位。 
 - `401`:未登入;沿用既有 refresh/sign-out middleware。 
 - `403`:沒有 Engineer function 權限。 
 - `404`:Charge Point、Connector 或 operation 不存在。 
 - `409`:同一 Charge Point 已有進行中的 `GetDiagnostics` 或 `UpdateFirmware`。 
 - `422`:capability 為 unsupported,或對非 CloudLink Connector 執行 Modbus。 
 - `503`:設備 offline 或 WebSocket dispatch 失敗。錯誤 message 會包含已建立的 `commandLogId`,UI 應提供前往 detail 的入口。 

 ## 11. React Implementation Guidance 

 先在 `react/branch` 更新 Backend types: 

 ```bash 
 # .env.local 的 NEXT_PUBLIC_API_URL 指向含 #1381 的 Branch API 
 yarn openapi:refresh 
 ``` 

 / Backend OpenAPI tag:`[B135]工程師工具`。 依賴 

 必須: Backend/API implementation 會依同一份 Plan 補上: 

 - 使用既有 `lib/query-api/openapi.ts` 的 `$api`/`fetchClient`。 `Engineer` Role、SS3A function/API mapping、初始帳號 role mapping。 
 - 沿用既有 JWT middleware、React Query、React Hook Form、Zod、shadcn、Sonner/SweetAlert2 pattern。 Engineer command API。 
 - 不要另建未帶 auth/refresh middleware 的 API client。 `e_engineer_command_log` 與 event timeline。 
 - 不要手改 `lib/data/ems.json` 或 `lib/data/emsapi.ts`。 
 - 在 `config/menus.ts` 註冊 route/menu,但顯示權限仍以 Backend function/menu 為準。 

 建議 feature 結構: 

 ```text 
 app/(dashboard)/engineer-tools/ 
 ├── page.tsx 
 ├── components/ 
 │     ├── target-bar.tsx 
 │     ├── command-list.tsx 
 │     ├── modbus-command-form.tsx 
 │     ├── ocpp-command-form.tsx 
 │     ├── command-status-panel.tsx 
 │     ├── operation-table.tsx 
 │     └── operation-detail-drawer.tsx 
 ├── hooks/ 
 │     └── engineer-command-hooks.ts 
 └── lib/ 
     ├── schema.ts 
     └── presentation.ts 
 ``` 

 這不是一般 CRUD,建議以 typed `$api.useQuery/useMutation` 建立專用 hooks,不必勉強套完整 CRUD factory。 

 ## 12. 驗收條件 

 1. 只有取得 Engineer function 的使用者看得到並能進入工程師工具。 
 2. Charge Point/Connector target 可選取,deep-link 可正確預選。 
 3. Catalog 回傳的 31 commands 都能正確分組與顯示 capability。 
 4. Modbus 不提供任何 raw register/function/payload 入口。 
 5. OCPP 19 個 OCPP commands 都可由 schema 建立 payload,表單/JSON 模式切換不丟資料。 dispatcher/response tracking。 
 6. `UNVERIFIED` 可送出且有警告;`UNSUPPORTED` 禁用並顯示原因。 
 7. Command POST 後顯示 operation panel,正確輪詢至 terminal state。 
 8. `REJECTED`、`CALL_ERROR`、`SEND_FAILED`、`TIMEOUT` 不得顯示為成功。 
 9. 操作紀錄支援 server-side filter/pagination,detail drawer 顯示 masked payload 與完整 timeline。 
 10. Reset、供電控制、Meter ID 修改各只有一次簡短確認;不要求操作原因。 
 11. Desktop/mobile 無文字重疊、截斷或切換造成的 layout shift。 
 12. 完成後通過 `yarn lint` 與 `yarn build`。 

 ## 13. Backend 現況與測試限制 

 Jimmy 已驗證: 

 - Engineer Role login 
 - 31-command catalog 
 - 真實 CP001 `READ_SUPPLY_STATE` Modbus E2E 
 - OCPP `TriggerMessage` Accepted/Rejected 
 - search/detail 
 - 未登入 401 
 - raw Modbus operation 400 controlled command path。 

 Fortune(華城)設備在 2026-07-14 測試時離線,因此其 19 個 OCPP capability 目前維持 `UNVERIFIED`。FE 必須呈現非阻斷警告,不能顯示為 `VERIFIED`。 FE 請以 Plan 中的 API contract 與 layout guidance 為準,若 backend OpenAPI 實作時欄位命名有微調,請以 regenerated types 對齊。 

 Backend E2E 報告:`test_report/20260714_001_redmine_1381_engineer_command_e2e.md`。 

 ## 14. 附件與關聯票 附件 

 - Backend:#1381(Resolved) 
 - 附件:`branch-admin-engineer-command-plan.md` 
 - 附件包含完整產品決策、資料表、OCPP schema、測試矩陣與後續 Fortune 實機驗證計畫。 請下載附件:`branch-admin-engineer-command-plan.md` 

 若本 Description 與既有 Journal 有差異,以本 Description 與最新 Backend OpenAPI 為準;Journal 只作進度歷史,不作需求拼接來源。 附件是完整 implementation plan,包含產品決策、API 設計、資料表、OCPP/Modbus command catalog、FE layout guidance、驗收標準與 phased implementation。

返回