Feature #1400
進行中[Branch後台][BE] 充電樁狀態改由 WebSocket 推送,並補齊維運欄位與操作 API
0%
概述
背景¶
Branch Admin「連接器 → 編輯 → 充電樁狀態」分頁(react/branch/app/(dashboard)/connector/components/connector-status.tsx)目前全靠 REST 輪詢取得狀態:
-
GET /api/connector/status/{connectorId}在AVAILABLE/PREPARING/CHARGING時每 2 秒輪詢一次。 - 另外為了判斷是否可遠端操作,再打 ChargePoint API 取得
ocppConnectionStatus,離線時每 10 秒輪詢。 - 送出 start/stop 後靠
setTimeout(3000)硬等 3 秒才 refetch,用猜的方式等待後端狀態落地。
問題:
- 後端資料實際上只在 OCPP
StatusNotification/MeterValues進來時才更新(MeterValueService、TransactionService、ChargingOrchestrator、ChargePointOfflineService都會發 connector event)。2 秒輪詢絕大多數拿到同一份資料,是純浪費,卻又不保證比推送快。 - 同一份 Connector 狀態,LIFF(#1399)已改為由 WebSocket 推送的
ConnectorForHqDto取得,後台仍是輪詢。同一個真實狀態有兩條取得路徑、兩種時序,出事時無法對齊時間軸。 - 一個畫面同時掛兩條不同節奏的輪詢,
status與ocppConnectionStatus可能短暫互相矛盾,已經需要在 FE 使用prevOnlineRef補償。
注意:本案不是要後台照抄 LIFF 的顯示或操作方式。 LIFF 的 driverState/actions[] 是車主語意(OFF_PEAK_WAITING、「取消排隊」),授權綁住戶關聯與 transaction owner,管理員不適用也不需要。
本案只要求資料取得管道與來源一致:同一份 Connector 狀態、同一個推送時機,後台使用自己的維運語意呈現與操作。
目標¶
- 提供後台專用的 Connector 狀態 WebSocket 推送,讓 FE 可移除輪詢與 3 秒硬等。
- 後台需要的維運欄位由同一份 payload 一次給齊,不再為了單一欄位另打一支 API。
- 後台充電操作維持現行 operator 授權模型,但補上可追蹤性與重送防護。
A-1. 新增後台專用 Connector WebSocket endpoint(ems_branch)¶
-
ems_branch目前只有 OCPP 使用的/ws/ocpp/*,沒有供 UI 使用的 WebSocket,需新增。 -
不可沿用 HQ 的
/ws/connector/{buildingId}/{connectorId}:該 endpoint 的LineIdHandshakeInterceptor以xLineId查詢findByLineIdAndBuildingId驗證,後台管理員沒有 LINE 身分,也不是該 building 住戶,握手必定被拒。 -
握手驗證改用 Branch 既有的 ss3a 身分(JWT),並套用該管理員對此 Connector/Building 的既有權限判斷。授權失敗時的 close code 與原因請明確定義,供 FE 分辨「無權限」與「連線失敗」。
-
Branch 本身就是 connector event 的 publisher,直接由 Branch 推送即可,不需要繞 RabbitMQ 再回來。
-
推送時機請與現有
ConnectorEventPublisher的觸發點一致,包括:- 狀態變更
- MeterValues
- 交易起訖
- 離線偵測
- remote operation lifecycle
-
連線建立時需提供一份 initial snapshot,FE 不再另打 detail REST。
A-2. Payload 欄位需求¶
Base 沿用 ConnectorForHqDto(GET /api/connector/status/{connectorId} 現在回傳的就是它),後台已使用:
id/status/statusLabel/startTime/currentPower/currentVoltage/currentCurrent/energyUsage/totalEnergy/remainTime/costPredict/currentTariff
需要補進同一份 payload 的維運欄位:
| 欄位 | 用途 | 目前如何取得 |
|---|---|---|
ocppConnectionStatus(id + label) |
判斷是否可遠端操作、離線時停用按鈕 | 另打 ChargePoint API 並自行輪詢 |
chargePointId |
關聯導頁與 engineer tools | 由 FE 從 ConnectorDto 帶入 |
lastHeartbeatAt |
判斷離線多久,區分剛斷線與長期失聯 | 無 |
statusChangedAt |
判斷狀態停滯多久,例如卡在 PREPARING
|
無 |
errorCode/vendorErrorCode/vendorId
|
FAULTED 時的實際故障原因 |
無,後台目前只顯示「故障/請聯繫技術支援」 |
currentTransactionId |
對照充電記錄、engineer tools 追查 | 無 |
currentIdTag/使用者識別 |
知道現在是誰在充電,公樁停止前確認 | 無 |
activeOperation(#1398 已有) |
有未終結的 RemoteStart/RemoteStop 時,避免管理員重複送指令 | 無 |
driverState(車主語意)在後台不使用;payload 帶著無妨,但後台不得使用它決定顯示或操作。
A-3. 操作面 API¶
後台維持現行:
POST /api/tx/charge/startPOST /api/tx/charge/stop
以上 API 以 ss3a 管理員身分帶入 userId,不改用 POST /api/connector/commands。
後者的 authorizeConnector() 會檢查住戶關聯,公樁停止時還要求操作者是 transaction IdTag owner,管理員一律會被 CONNECTOR_FORBIDDEN 擋下。
但需要補齊以下項目:
-
回傳
operationIdoperationId必須與 #1398 的e_remote_transaction_log.id一致,讓後台操作能與 LIFF、Branch、OCPP log 串起同一條時間軸。目前後台送出的指令在稽核上是斷點。 -
明確的成功/失敗語意
現行回應是
isSuccess+message,FE 只能將訊息原樣顯示給使用者。請提供穩定的
errorCode,例如:- 充電樁離線
- 已有未終結 operation
- 狀態不允許
讓 FE 能依錯誤類型分流,而不是比對訊息字串。
-
operation 結果透過 A-1 的 WebSocket 推送
以 WebSocket 推送 operation 結果,取代 FE 目前的
setTimeout(3000)硬等。 -
已有未終結 operation 時明確拒絕
當
activeOperation不為null時,不得讓管理員再次送出 RemoteStart/RemoteStop。#1398 §15.2 已規定同一 Connector 不得存在重疊的未完成 operation,後台入口也必須遵守。
不在本次範圍¶
- Connector 列表頁的狀態顯示,仍走既有查詢;本案先處理單一 Connector 詳細狀態。
- Engineer tools(#1380)既有的 OCPP/Modbus 指令面板。
- LIFF 端行為(#1397/#1398/#1399)。
驗收重點¶
- 後台可透過新 WebSocket endpoint,以管理員身分連線並取得 initial snapshot;非授權身分(含 LIFF lineId)連線時會被拒絕,且 close code 定義明確。
- Payload 一次帶齊 A-2 的全部欄位,
status與ocppConnectionStatus來自同一份資料。 - 推送時機涵蓋狀態變更、MeterValues、交易起訖、離線偵測與 remote operation lifecycle。
-
FAULTED時,payload 帶出實際的errorCode/vendorErrorCode。 - start/stop 回傳
operationId與穩定的errorCode;已有未終結 operation 時,操作會被拒絕。 - 後台送出的充電操作,可使用
operationId在 Branch log 追蹤到對應的 RemoteStart/RemoteStop。
對應 FE ticket¶
FE 實作見 #1401。
是由 陳國瑋 於 約 16 小時 前更新
Backend 實作已完成,等待測試計畫核准。
已完成:
- 一次性 30 秒 connector-bound WebSocket ticket REST API
- Branch Admin Connector WebSocket initial snapshot、shared snapshot 與 initiating-admin operation result
- RabbitMQ 與 Admin WebSocket transport 隔離;無 viewer 時不查完整 snapshot
- /api/tx/charge/start|stop 改採 JWT identity,補齊 200/202/403/409 structured OpenAPI schema
- Admin Start 使用自己的 BranchUser/IdTag;私樁 queue、公樁 reserved slot、離峰 ON guard 沿用 #1398
- Admin Stop 可停止其他 user 的 ACTIVE transaction;無 ACTIVE transaction 不送 ChangeAvailability
- SS3A migration 與 E2E catalog 已補齊
- #1401 已更新完整 FE contract
目前驗證:
- mvn -DskipTests package:PASS
- 3 個隔離 unit method:PASS
- E2E catalog validator:PASS(151 cases)
是由 陳國瑋 於 約 15 小時 前更新
#1400 Backend 已 commit 並部署至 ken 測試環境。
Commit:
- 5e0b58c feat(#1400): 新增管理後台充電樁 WebSocket 控制
ken 部署:
- Branch container: ems-branch-api
- 目標: 192.168.127.3:/opt/ems/ems_branch/api
- 備份: /opt/ems/backups/20260802-230118-redmine-1400
- 使用 conf/ken 建置,JAR SHA-256: 1ff3ede95476634fbf066f55a85124337894c14dd8a41917ced5edf0aa04199f
- container 重建後狀態: Up
- /api-docs: HTTP 200
- OpenAPI 已確認包含 /api/connector/admin-websocket-ticket、/api/tx/charge/start、/api/tx/charge/stop
Database:
- 已在 ken ems_branch DB 套用 documents/sql/redmine-1400-branch-admin-connector-websocket.sql
- A1218 已建立並綁定 connector_mgmt_view/edit
- A1401/A1402 已移除 hq_function,改綁定 connector_mgmt_edit
測試與限制:
- 核准的隔離 unit test: 36/36 PASS
- E2E catalog validator: PASS(151 cases)
- 尚未執行人工 E2E;需待 FE #1401 完成後再進行完整 Branch Admin WebSocket E2E 驗證
- Issue 維持 In Progress,尚未結案。