專案

一般

配置概況

動作

Feature #1380

進行中

[FE] Branch Admin 工程師工具 UI Implementation

是由 陳國瑋20 天 前加入. 於 17 天 前更新.

狀態:
Resolved
優先權:
Normal
被分派者:
開始日期:
2026-07-14
完成日期:
完成百分比:

0%

預估工時:

概述

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

1. 目標與範圍

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

  1. 對指定 CloudLink Connector 執行受控 Modbus 讀取與控制。
  2. 由 Branch 向指定 Charge Point 發送 OCPP 1.6 CSMS Command。
  3. 查詢每次操作的 request、response、錯誤、耗時與完整 event timeline。
  4. 以 Fortune(華城)充電樁測試為主要情境,同時支援 CloudLink 與其他廠牌的 capability 顯示。

Backend 對應票為 #1381,目前已完成並部署至 Jimmy 與 A17。FE 不需要自行設計 Backend contract;本 Description、Backend OpenAPI 與附件 Plan 為實作依據。

2. 權限與導覽

  • 新路由:/engineer-tools
  • 側邊欄名稱:「工程師工具」。
  • 只有取得 Backend Engineer function/menu 的使用者顯示入口並可進入。
  • 不要在 FE hardcode ADMINpower_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
  • vendor
  • model
  • firmware version
  • capability badge

規則:

  • 先選 Charge Point,再載入該 Charge Point 的 catalog/capability。
  • 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 個 Modbus operations。

每筆主要欄位:

  • protocolOCPPMODBUS
  • command
  • category
  • targetLevelCHARGE_POINTCONNECTOR
  • riskLevelLOWMEDIUMHIGH
  • writeOperation
  • schema:產生表單與前端預驗證使用的 JSON Schema
  • capabilityVERIFIEDUNVERIFIEDUNSUPPORTED
  • 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 分組:

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

禁止提供:

  • raw Modbus function code
  • raw register address/value
  • raw payload
  • 清除歷史資料
  • Browser 直接連 charge_point API

Read operation 的 parameters 必須是空 object。

SET_SUPPLY_STATE

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

SET_METER_ID

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

meterId 範圍為 1–255。

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

確認規則:

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

6. OCPP 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 不增加確認。
  • GetDiagnosticsUpdateFirmware 的 HTTP/HTTPS location 由 Engineer 輸入;FE 不需實作檔案上傳。

OCPP request:

{
  "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:

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

7.3 Execute Modbus

POST /api/engineer/modbus/commands

Body:

{
  "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。
  • 長 JSON 與 ID 必須 wrap/scroll,不得撐破 drawer。

7.5 Command Search

POST /api/engineer/commands/search

Body 所有 filter 都可選:

{
  "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,使用 contenttotalElementstotalPagesnumbersize

8. 202 Response 與 Polling

兩支 execute API 成功時回 HTTP 202:

{
  "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

GetDiagnosticsUpdateFirmware 可能先進入 RUNNING,後續 progress 由 detail events 顯示。

9. 操作紀錄 Tab

Filters:

  • 時間範圍
  • 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

  • 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 已有進行中的 GetDiagnosticsUpdateFirmware
  • 422:capability 為 unsupported,或對非 CloudLink Connector 執行 Modbus。
  • 503:設備 offline 或 WebSocket dispatch 失敗。錯誤 message 會包含已建立的 commandLogId,UI 應提供前往 detail 的入口。

11. React Implementation Guidance

先在 react/branch 更新 Backend types:

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

Backend OpenAPI tag:[B135]工程師工具

必須:

  • 使用既有 lib/query-api/openapi.ts$apifetchClient
  • 沿用既有 JWT middleware、React Query、React Hook Form、Zod、shadcn、Sonner/SweetAlert2 pattern。
  • 不要另建未帶 auth/refresh middleware 的 API client。
  • 不要手改 lib/data/ems.jsonlib/data/emsapi.ts
  • config/menus.ts 註冊 route/menu,但顯示權限仍以 Backend function/menu 為準。

建議 feature 結構:

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. 19 個 OCPP commands 都可由 schema 建立 payload,表單/JSON 模式切換不丟資料。
  6. UNVERIFIED 可送出且有警告;UNSUPPORTED 禁用並顯示原因。
  7. Command POST 後顯示 operation panel,正確輪詢至 terminal state。
  8. REJECTEDCALL_ERRORSEND_FAILEDTIMEOUT 不得顯示為成功。
  9. 操作紀錄支援 server-side filter/pagination,detail drawer 顯示 masked payload 與完整 timeline。
  10. Reset、供電控制、Meter ID 修改各只有一次簡短確認;不要求操作原因。
  11. Desktop/mobile 無文字重疊、截斷或切換造成的 layout shift。
  12. 完成後通過 yarn lintyarn 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

Fortune(華城)設備在 Jimmy 2026-07-14 測試時離線;A17 於 2026-07-16 已通過 TriggerMessage(Heartbeat) 低風險實機 E2E。這只證明基礎 OCPP 通訊路徑可用,尚未完成 19-command capability matrix,因此 FE 仍必須將其 capability 呈現為 UNVERIFIED 與非阻斷警告,不能顯示為 VERIFIED

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 實機驗證計畫。

若本 Description 與既有 Journal 有差異,以本 Description 與最新 Backend OpenAPI 為準;Journal 只作進度歷史,不作需求拼接來源。


檔案

branch-admin-engineer-command-plan.md (26.9 KB) branch-admin-engineer-command-plan.md Branch Admin Engineer Command Center implementation plan 陳國瑋, 2026-07-14 05:14

是由 陳國瑋20 天 前更新

Backend 對應票已建立:#1381(Branch Engineer Command API 與 OCPP/Modbus 控制)。FE 請以 #1380 附件 Plan 為 UI contract,Backend API/OpenAPI 由 #1381 實作。

是由 陳國瑋18 天 前更新

Backend #1381 已完成並部署至 Jimmy,FE 可開始串接。請以本票附件 Plan 與 Backend OpenAPI 為準。

1. 先更新 OpenAPI types

react/branch

# .env.local 的 NEXT_PUBLIC_API_URL 指向含 #1381 的 Branch API(目前 Jimmy 已部署)
yarn openapi:refresh

請使用既有 lib/query-api/openapi.ts$apifetchClient,不要另建未帶既有 JWT middleware 的 axios client。OpenAPI tag 為 [B135]工程師工具

2. 五支 API

  1. GET /api/engineer/commands/catalog?chargePointId={chargePointId}

    • 回傳 19 個 OCPP + 12 個 Modbus command。
    • 每筆欄位:protocol, command, category, targetLevel, riskLevel, writeOperation, schema, capability, capabilityNote
    • 選定 Charge Point 後再取 catalog,並以回傳的 JSON Schema 建表單;不要在 FE 自建 vendor allowlist。
  2. POST /api/engineer/ocpp/commands

{
  "chargePointId": "CP001",
  "connectorId": null,
  "command": "TriggerMessage",
  "payload": { "requestedMessage": "Heartbeat" }
}
  1. POST /api/engineer/modbus/commands

Read:

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

Write:

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

SET_METER_ID 使用 { "meterId": 1 },範圍 1–255。其餘 read operation 的 parameters 必須是空 object。不要提供 raw register/function code UI。

  1. GET /api/engineer/commands/{commandLogId}

    • 查 operation 最終結果與 event timeline。
    • 顯示 JSON 時預設使用 maskedRequestPayloadrequestPayload 只在明確需要且具權限的 detail view 顯示。
    • timeline 已依 sequenceNo 排序。
  2. POST /api/engineer/commands/search

    • filters:from, to, protocol, command, chargePointId, connectorId, operatorId, state, correlationId, ocppUniqueId
    • page 從 0 起算,size 最大 100。
    • response 是 Spring Page:使用 content, totalElements, totalPages, number, size

3. 202 與 polling(重要)

Command POST 成功回 HTTP 202:

{
  "commandLogId": "uuid",
  "correlationId": "uuid",
  "ocppUniqueId": "uuid",
  "state": "SENT",
  "capability": "UNVERIFIED",
  "createdAt": "..."
}

202/SENT 只表示 Branch 已把 OCPP CALL 送入 WebSocket,不代表設備已執行成功。FE 要用 commandLogId 輪詢 detail(建議畫面可見時每 1 秒一次)。

非 terminal:CREATED, VALIDATED, DISPATCHING, SENT, RUNNING

Terminal,收到後停止 polling:
COMPLETED, REJECTED, CALL_ERROR, SEND_FAILED, FAILED, TIMEOUT, STATUS_UNKNOWN

快速設備可能在 POST 回來前已完成,因此 FE 應直接檢查 create response 的 state;若已是 terminal,不必再等待。離開頁面不會取消已送出的 command。

4. Capability 與錯誤處理

  • VERIFIED:正常啟用。
  • UNVERIFIED:可送出,顯示非阻斷警告。
  • UNSUPPORTED:停用按鈕並顯示 capabilityNote;Backend 仍會以 422 阻擋。
  • 400:payload/schema/target/Modbus parameters 錯誤。
  • 401/403:登入或 Engineer function 權限不足。
  • 404:Charge Point、Connector 或 operation 不存在。
  • 409:同一設備已有進行中的 GetDiagnostics/UpdateFirmware。
  • 422:設備 capability 不支援,或非 CloudLink Connector 呼叫 Modbus。
  • 503:設備 offline/WebSocket dispatch 失敗;錯誤 message 會包含已建立的 commandLogId,UI 應提供查明細入口。

5. UI implementation

  • 新增 /engineer-tools,沿用 dashboard shell。
  • target bar:Charge Point、Connector、在線狀態、vendor/model/firmware、capability。
  • 三個 tabs:ModbusOCPP操作紀錄
  • OCPP 用「表單 / JSON」segmented control,共用同一份 payload state;schema error 顯示 JSON path。
  • ResetSET_SUPPLY_STATESET_METER_ID 各做一次簡短確認;其餘不增加確認。
  • Connector 頁加工具 icon deep-link:/engineer-tools?connectorId={id}&tab=ocpp
  • 選單/入口顯示依 Backend Engineer function,不要只 hardcode role 名稱;後端仍會做最後授權。
  • Detail drawer 顯示 target/operator/state/protocol status/elapsed/correlationId/ocppUniqueId、masked request/response、錯誤與 timeline。
  • 完成後執行 yarn lintyarn build,檢查 desktop/mobile。

6. 已驗證狀態

Jimmy 已通過:登入 Engineer Role、31-command catalog、真實 CP001 Modbus READ_SUPPLY_STATE、OCPP TriggerMessage Accepted/Rejected、search/detail、401、raw Modbus 400。

Fortune(華城)設備測試時離線,因此其 OCPP capability 目前仍是 UNVERIFIED;FE 必須保留警告狀態,不能顯示成 VERIFIED。Backend E2E 報告位於 repo:test_report/20260714_001_redmine_1381_engineer_command_e2e.md

是由 陳國瑋18 天 前更新

是由 陳國瑋18 天 前更新

是由 鍾正剛17 天 前更新

已完成 FE 開發,branch: feature/issue-1380-branch-admin-engineer-tools-ui

實作內容:

  • 新增 /engineer-tools 頁面,master-detail 排版(左側指令清單 + 右側表單/結果),Target Bar 支援 Charge Point 搜尋選擇與依 chargePointId 過濾的 Connector 選擇。
  • Modbus tab:12 個受控 operation,依連線/讀值/控制分組,CloudLink-only 判斷生效。
  • OCPP tab:19 個 command,表單/JSON 雙模式共用同一份 payload state,schema-driven 通用表單引擎(無需為每個 command 手動維護 UI,新增/移除 command 免改前端碼)。
  • 操作紀錄 tab:server-side filter + pagination + detail drawer(masked payload、event timeline、copy id)。
  • Connector 列表新增工具 icon deep-link,權限依 Backend 動態選單判斷(不 hardcode role)。
  • Reset/供電控制/Meter ID 修改皆有一次簡短確認;202 polling 依 terminal state 自動停止。

已於 Jimmy 環境以真實帳號逐項操作驗證:Modbus CONNECTION_TEST/SET_SUPPLY_STATE 完整跑通 COMPLETED,OCPP ChangeConfiguration 表單/JSON 切換與 CALL_ERROR(NotSupported)顯示正確,操作紀錄查詢與明細 drawer 正常。過程中發現並修正一個真實 bug:表單初始值未給 boolean 欄位明確 default,導致 Switch 顯示合法的 false 卻被 zod 判定為必填缺漏,已修正為欄位種類感知的預設值產生器。

yarn lint/yarn build/tsc --noEmit 均通過。

是由 鍾正剛17 天 前更新

  • 狀態New 變更為 Resolved
動作

匯出至 Atom PDF