專案

一般

配置概況

動作

Bug #1418

已結束

Bug #1343: [Branch] 華城充電樁 FINISHING 後未送 StatusNotification(Available),導致 connector 卡住無法充電 (FINISHING timeout fallback 修補)

[Backend Bug] EXPO Fortune 停止期間 PREPARING 誤觸 RemoteStart,LIFF 由 FINISHING 倒退 CHARGING

是由 陳國瑋 於 約 2 個月 前加入. 於 約 2 個月 前更新.

狀態:
Closed
優先權:
High
被分派者:
開始日期:
2026-08-10
完成日期:
預估工時:

概述

問題描述

2026-08-10 在 EXPO 案場實測 Fortune 私人樁 CBDAX50A-24L-XX-B351-001。車主按下「停止充電」後,LIFF 先顯示 FINISHING / 停止中,隨後短暫倒退為 CHARGING / 充電中,並出現「充電樁未接受啟動指令」。使用者並未再次按下開始充電。

此問題與父單 #1343 的 Fortune PREPARING -> SEND_REMOTE_START 根因相同,但本單聚焦於 RemoteStop 期間相反方向 operation 重疊,以及 LIFF 狀態倒退。

實機時序

時間 事件
14:20:27.824 車主送出 STOP_CHARGING
14:20:27.913 CP 回 RemoteStopTransaction.conf(Accepted)
14:20:30.080 CP 回 StatusNotification(PREPARING)
14:20:30.151 Branch 由 Fortune strategy 自動建立 START operation 並送出 RemoteStartTransaction
14:20:30.261 CP 回 RemoteStartTransaction.conf(Rejected),LIFF 顯示啟動失敗
14:20:32.755 CP 回 StatusNotification(FINISHING)
14:20:34.704 CP 回 StopTransaction(reason=Remote)
14:20:41.123 CP 回 StatusNotification(AVAILABLE)

DB/Log 結果

  • 原 transaction 正常 COMPLETED,未建立第二筆充電 transaction。
  • STOP operation 最終為 STOPPED。
  • 停止期間自動建立的 START operation 最終為 REJECTED / REMOTE_START_REJECTED。
  • LIFF 跳回充電中是 operation/driverState projection 錯誤,不代表實際重新供電。
  • Branch request log 只有原 START command 與本次 STOP command;14:20:30 的 RemoteStart 由 StatusNotification 流程內部觸發,不是第二次使用者請求。

根本原因

  1. FortuneConnectorTypeRotationStrategy.onPreparingWithoutRotation() 無條件回傳 SEND_REMOTE_START。
  2. RemoteChargingSupport.remoteStart() 只阻擋重複 active START,未阻擋 active STOP 期間建立 START。
  3. ConnectorDriverPresentationService 只取得最新一筆 active operation;新 START 會遮蔽仍 active 的 STOP,既有 transaction 尚未完成時 driverState 因而回到 CHARGING。

預期行為

  1. RemoteStop dispatch 後,收到同 transaction 的 StopTransaction 或 timeout/failure 前,LIFF 維持 FINISHING。
  2. Active STOP 存在期間,raw PREPARING 不得觸發 RemoteStart。
  3. 同一 connector 不得同時存在相反方向的 active START/STOP operation。
  4. 防禦性 projection 必須讓 STOP 優先,LIFF 不得從 FINISHING 倒退為 CHARGING。
  5. StopTransaction 後、Available 前不得再顯示充電中;應呈現安全的已停止/等待拔槍狀態。
  6. StatusNotification(AVAILABLE) 後回到 READY。
  7. 不得以全域禁止 raw PREPARING RemoteStart 修正,必須保留先插槍後由合法 command 啟動的流程。

影響範圍

  • Backend:ems_branch Fortune strategy、Remote operation conflict guard、driverState projection。
  • LIFF:原則上沿用 Backend driverState,不新增前端自行推導規則。
  • CloudLink:正常 START/STOP、CANCEL_START、ChangeAvailability 與輪充行為不得改變。
  • DB schema:預期無變更。

E2E Impact

  • Add:Fortune private CHARGING -> RemoteStop -> PREPARING -> FINISHING -> StopTransaction -> AVAILABLE production regression。
  • Priority:P0。
  • Trigger:Fortune/Other connector strategy、Remote operation lifecycle、driverState 或 LIFF charging flow 變更,以及 Fortune 案場發版。
  • Release pack:Charging/OCPP + Site/Vendor。
  • CloudLink baseline:更新或執行既有 CHG-001、CHG-005、CHG-034 及相關 cancel/rotation 案例,證明無回歸。

驗收標準

  • 不產生非使用者授權的第二筆 START operation/RemoteStartTransaction。
  • 不產生相反方向 active operation 重疊。
  • LIFF 狀態順序符合預期,且成功/失敗 modal 對應真正發起的 operation。
  • Fortune 實機 P0 regression 通過。
  • CloudLink public/private baseline regression 通過。
  • E2E catalog validator 通過。

是由 陳國瑋 於 約 2 個月 前更新

  • 狀態 從 New 變更為 In Progress
  • 被分派者 設定為 陳國瑋

開始依 EXPO 實機證據進行需求確認、影響分析與實作規劃。

是由 陳國瑋 於 約 2 個月 前更新

需求確認 1:StopTransaction 後的 LIFF 文案

Ken 確認:

  • Active STOP 尚未收到同 transaction 的 StopTransaction:維持 FINISHING,title「停止中」,description「正在等待充電樁結束本次交易」。
  • 已收到 StopTransaction、raw connector 仍為 FINISHING、尚未收到 AVAILABLE:沿用 driverState.code=FINISHING,改顯示 title「充電已停止」、description「請拔槍」。
  • 收到 StatusNotification(AVAILABLE) 後才回 READY。
  • 不新增新的 driverState code,因此 LIFF 沿用 Backend title/description 與既有 FINISHING tone,不需修改 FE。

是由 陳國瑋 於 約 2 個月 前更新

需求確認 2:受影響 connector type

Ken 確認本次一併修正:

  • FORTUNE
  • DEFAULT
  • TESLA
  • UNKNOWN

上述類型目前共用/繼承 Fortune 的 PREPARING -> SEND_REMOTE_START 行為;修正後 raw PREPARING 本身不得成為未授權 RemoteStart 的來源。

CLOUDLINK 保持既有 strategy 與正常 START/STOP、CANCEL_START、ChangeAvailability、rotation 行為。合法的使用者 command、OFF_PEAK scheduler 與 rotation dispatch 仍可依持久化 request context 送出 RemoteStart。

是由 陳國瑋 於 約 2 個月 前更新

需求確認 3:StopTransaction 後未收到 AVAILABLE 的處理

Ken 確認:

  • 已收到 StopTransaction 後,若 CP 未回 StatusNotification(AVAILABLE),driverState 持續顯示 FINISHING / 充電已停止 / 請拔槍。
  • Backend 不得自行把 connector operational status 偽造為 AVAILABLE,避免實體槍或設備尚未 ready 卻開放下一次啟動。
  • Timeout 僅記錄可追蹤的 warning/audit,不改寫 connector status;收到真實 OCPP StatusNotification 後再正常轉態。

是由 陳國瑋 於 約 2 個月 前更新

需求確認 4:FINISHING timeout warning

Ken 確認:

  • Raw FINISHING 持續超過 2 分鐘時記錄 warning。
  • 同一次 FINISHING transition 只告警一次,不隨 scheduler scan 重複洗 log。
  • 以 connector 的 last_status_change 識別不同 FINISHING transition。
  • Warning 僅供維運追蹤,不改寫 connector status、不發布偽造 AVAILABLE。

是由 陳國瑋 於 約 2 個月 前更新

階段 2:實作規劃已確認

Ken 已於 2026-08-10 確認規劃可接受,進入實作階段。

實作範圍:

  • 移除 FORTUNE/DEFAULT/TESLA/UNKNOWN 由 raw PREPARING 直接觸發的未授權 RemoteStart。
  • 在 semantic command 與 RemoteChargingSupport 增加相反方向 active operation guard。
  • STOP projection 優先;StopTransaction 後至真實 AVAILABLE 前顯示 FINISHING / 充電已停止 / 請拔槍。
  • raw FINISHING 超過 120 秒時,以 connectorId + last_status_change 去重記錄 warning,不改寫狀態或偽造 event。
  • 新增 P0 E2E regression,保留 CloudLink baseline。

本次不變更 React FE,也不進行 DB schema migration。多案例測試仍依 SOP 等待 Ken 明確下令「開始測試」。

是由 陳國瑋 於 約 2 個月 前更新

階段 3:實作完成,等待測試啟動

已完成:

  • FORTUNE/DEFAULT/TESLA/UNKNOWN 不再由 raw PREPARING 直接產生 RemoteStart。
  • Semantic command 與 RemoteChargingSupport 均加入 START/STOP 相反方向 active operation guard。
  • Driver projection 改為 STOP 優先;StopTransaction 完成而 raw status 尚為 FINISHING 時回傳「充電已停止/請拔槍」。
  • 新增 raw FINISHING 120 秒 warning,使用 connectorId + last_status_change 在 process 內去重,不改寫狀態。
  • 已新增 vendor strategy、operation conflict、driver projection、warning 與 scheduler Unit Test。

已執行安全驗證:

  • mvn -DskipTests package:PASS,main/test source 均編譯成功。
  • 單一隔離 Unit Test:ConnectorDriverPresentationServiceTest#build_whenTransactionStoppedButRawFinishing_returnsStoppedPleaseUnplug:PASS(1 test)。

依 SOP,以下多案例測試尚未執行,等待 Ken 明確下令「開始測試」:

mvn -Dtest=ConnectorTypeRotationStrategyTest,RemoteChargingSupportOperationConflictTest,ConnectorCommandServiceTest,ConnectorDriverPresentationServiceTest,FinishingStatusWarningServiceTest,RemoteOperationTimeoutTaskTest,RemoteOperationPersistenceServiceTest,RemoteOperationLifecycleServiceTest,ChargingOrchestratorQueueCorrelationTest,StatusNotificationHandlerLastStatusChangeTest test

這些案例為 Mockito/純 Unit Test,不啟動 Spring integration profile、不連接外部服務或共用 DB,也不修改共用資料。

是由 陳國瑋 於 約 2 個月 前更新

階段 5:Unit Test 報告待確認

測試報告已產出:test_report/20260810_001_redmine-1418-unit.md

結果:

  • mvn -DskipTests package:PASS。
  • 單一 isolated projection test:PASS。
  • 純 Unit Test:50 tests,0 failures,0 errors,0 skipped。
  • 初始 command 中的 StatusNotificationHandlerLastStatusChangeTest 是 @SpringBootTest integration case,因 ems_branch_e2e_unconfigured 不存在而有 4 個 environment errors;未修改程式,也未視為 PASS。
  • 該 integration case 後續必須使用 isolated integration-test wrapper 與明確 EMS_E2E_SOURCE_DB 執行。

請先確認這份測試報告;確認後才進入文件/E2E catalog 更新關卡。

是由 陳國瑋 於 約 2 個月 前更新

階段 6:文件與 E2E Catalog 待確認

已完成:

  • E2E impact:Add CHG-044、Update CHG-032、Update ROT-013。
  • CHG-044 為 P0 Conditional,trigger 包含 Fortune/Other strategy、Remote operation conflict、driverState/LIFF stop flow 與 Fortune 案場發版。
  • Fortune 條件式 P0 Core Smoke 已加入 CHG-044。
  • Inventory 統計更新為 167 cases:P0 51、P1 65、P2 46、P3 5。
  • 更新 ai/02-backend-services.md、ai/04-frontend.md、ai/06-domain-glossary.md 與本單本地紀錄。
  • Catalog validator:PASS。
PASS: documents/E2E_TEST_CASE_INVENTORY.md (167 cases; P0=51, P1=65, P2=46, P3=5)

Run-specific Unit 結果仍只保留於 test_report/20260810_001_redmine-1418-unit.md,未寫入長期 catalog。實機 CHG-044 與 CloudLink baseline 尚未部署執行,不能視為 PASS。

是由 陳國瑋 於 約 2 個月 前更新

已完成 commit、push、合併 private 與 EXPO 部署。

Git:

  • feature commit: f32e13a fix(#1418): 修正 Fortune 停止競態與 FINISHING 顯示
  • private merge commit: 898c795 merge(#1418): 整併 Fortune 停止競態修正
  • feature 與 private 均已推送 origin

部署:

  • 環境:EXPO
  • 服務:ems-branch-api
  • 部署時間:2026-08-10 15:28 (Asia/Taipei)
  • openclaw JAR SHA-256: 129fa8ee9917624fe393302e2834e3b4fd16f205b2cfc20da068a8da1dd325d9
  • 備份:/opt/ems/backups/20260810-1526-redmine-1418
  • 上傳檔:/opt/ems/deployments/20260810-1526-redmine-1418/api.jar

部署前安全檢查:

  • CBDAX50A-24L-XX-B351-001 = AVAILABLE,current_transaction_id=NULL
  • Branch ACTIVE transaction=0
  • CP ACTIVE transaction=0
  • active remote operation=0

部署後健康檢查:

  • ems-branch-api running,RestartCount=0
  • /api-docs HTTP 200
  • Spring Boot Started Application(14.216 秒)
  • CBDAX50A-24L-XX-B351 OCPP WebSocket 已重連,DB 為 ONLINE
  • 目標 connector 仍為 AVAILABLE,Branch/CP ACTIVE transaction=0,active remote operation=0
  • 無 APPLICATION FAILED / OOM / BeanCreation / SQL syntax 嚴重啟動錯誤

已知既有環境警告:

  • RabbitMQ 使用 guest 驗證失敗;舊版部署前 log 已存在相同錯誤,因此非 #1418 變更造成。Branch API 與 OCPP 上線不受阻,但 connector.events 發布可能失敗,建議另案追蹤環境 MQ 認證。

尚未執行真實充電 E2E;依 SOP 等待明確「開始 E2E 測試」後再執行 CHG-044。

是由 陳國瑋 於 約 2 個月 前更新

狀態更新:

  • #1419 EXPO 獨立 profile/RabbitMQ 認證修正已部署、MQ transport smoke 通過,並已合併 private(merge commit 698aba6)。
  • #1418 程式修正仍在 EXPO 現行 JAR 中,MQ 推播阻礙已排除。
  • 本單完成度更新為 90%,維持 In Progress。
  • 唯一未完成 release gate:Fortune 實機 P0 CHG-044,需確認 STOP 後 LIFF 不倒退 CHARGING、沒有額外 RemoteStart/相反 active operation,StopTransaction 後顯示「充電已停止/請拔槍」,真實 AVAILABLE 後回 READY。
  • 未取得上述實機證據前不宣稱 CHG-044 PASS,也不將 #1418 設為 Resolved。

是由 陳國瑋 於 約 2 個月 前更新

  • 狀態 從 In Progress 變更為 Resolved

EXPO Fortune 實機回歸完成

  • 修正 commit:f32e13a
  • private merge:898c795
  • E2E 報告 commit:cba4bf7
  • 文件結果 commit:e3e47d1
  • 測試報告:test_report/20260810_002_redmine-1418-expo-fortune-e2e.md

實機結果

  • 2026-08-10 17:58:09~18:01:46(Asia/Taipei)於 EXPO Fortune PRIVATE connector CBDAX50A-24L-XX-B351-001 執行 CHG-044/CHG-032。
  • Ken 手動確認 LIFF 未再出現 FINISHING -> CHARGING 倒退或錯誤 RemoteStart 失敗訊息。
  • 刻意維持插槍超過 120 秒;第 123 秒只記錄一次 FINISHING warning,第 169 秒仍顯示「充電已停止/請拔槍」,收到真實 StatusNotification(AVAILABLE) 後才解除 FINISHING。
  • STOP operation 324:SUCCESS / STOPPED;transaction 33:COMPLETED / Remote。
  • 停止區間只有一筆 RemoteStopTransaction 與一筆 StopTransaction,沒有 RemoteStartTransaction、第二筆 START operation或新 transaction。
  • E2E Catalog validator PASS:167 cases;P0=51、P1=65、P2=46、P3=5。

結論:P0 CHG-044 實機回歸 PASS,#1418 驗收完成。

是由 陳國瑋 於 約 2 個月 前更新

  • 狀態 從 Resolved 變更為 Closed
動作

匯出至 Atom PDF