專案

一般

配置概況

Bug #1343

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

## 問題描述 

 ct1 (寶台 A17) 案場的華城充電樁 (CBDAX50A-25L-A6-A072-001) 在「先插充電槍、再執行開始充電」的流程中,connector status 會卡在 FINISHING 狀態,導致後續完全無法充電。問題持續超過 10 分鐘無人值守恢復,必須手動 SQL 干預。 

 ## 復現時序 (2026-06-18 14:28~14:38, ct1 log) 

 | 時間 | 方向 | 事件 | 說明 | 
 |------|------|------|------| 
 | 14:21:51 | ←樁 | StatusNotification: Preparing | 前次充電開始 (tx #507) | 
 | 14:21:56 | ←樁 | StartTransaction (tx #507, meter=3362) | | 
 | 14:26:52 | ←樁 | StatusNotification: SuspendedEV (vendorErrorCode=1000) | | 
 | 14:26:54 | 樁→ | DataTransfer | `車端要求暫停充電,220 斷開,停止發送電錶值,交易尚未結束,車端隨時會再要求充電` | 
 | 14:26:54 | ←樁 | StatusNotification: Finishing | | 
 | 14:26:57 | ←樁 | StopTransaction (tx #507, reason=EVDisconnected, meterStop=3938) | | 
 | 14:27:19 | ←樁 | StatusNotification: **Available** | EV 拔槍後正常 ✓ | 
 | **14:28:15** | ←樁 | StatusNotification: **Preparing** | **用戶插槍** | 
 | 14:28:17 | EMS→ | RemoteStartTransaction | 用戶點開始充電 | 
 | 14:28:18 | ←樁 | RemoteStartTransaction Response: Accepted | | 
 | 14:28:20 | 樁→ | DataTransfer | `transactionId=0, errorCode=1000` ← 根本沒開新 transaction | 
 | 14:28:21 | ←樁 | StatusNotification: **SuspendedEV + Finishing** | 1 秒內連送兩個 | 
 | 14:28:21 ~ 14:44:09 | | 完全沒有 StatusNotification,只有 Heartbeat | 充電樁送完 Finishing 後就斷了 | 
 | 14:38:16 | EMS | GET /api/cp/hq/user/connectors | 用戶查 connector (Ken 給的 log 起點) | 
 | 14:38:17 | EMS | GET /api/cp/hq/connector/info | status 仍為 FINISHING | 

 ## 根本原因 

 ### Bug 1(主因):華城 (FE-EVI) 充電樁在「先插槍再開機」流程下的 OCPP 行為異常 

 從 vendorErrorCode=1000 + DataTransfer 的中文訊息看,**充電樁判定 EV 端要求暫停充電**: 
 1. 收到用戶「插槍」→ 送 Preparing 
 2. EMS 送 RemoteStart → 充電樁回 Accepted 
 3. **但充電樁 1 秒後立刻送 SuspendedEV → Finishing**,且 DataTransfer 帶 `transactionId=0` 表示根本沒開始交易 
 4. 送完 Finishing 之後**再也沒有送 StatusNotification(Available)**,EMS 不知道它已經「準備好」 

 OCPP 1.6 規範下 StatusNotification 是單向通知,**充電樁不送 EMS 也沒轍**。這是華城充電樁 firmware 的問題。 

 ### Bug 2:EMS 對 FINISHING 狀態沒有 timeout fallback 

 `RemoteChargingSupport.isConnectorAvailableForCharging` (line 145-156) 只接受 AVAILABLE 或 PREPARING: 

 ```java 
 boolean available = status == ConnectorStatus.AVAILABLE  
                  || status == ConnectorStatus.PREPARING; 
 ``` 

 所以 FINISHING 的 connector 永久被擋。即使 EMS 再收到 RemoteStart,POST 進來會被擋掉回 400。 
 而且就算用戶重插槍觸發新的 Preparing StatusNotification,由於 connector 還卡在 FINISHING,新事件進來後雖然 StatusNotification handler 會 update status=Preparing,但中間可能會有 race condition / 排程 race。 

 ## 影響範圍 

 - **影響案場**:ct1 (寶台 A17),所有華城 (FORTUNE, FE-EVI) 充電樁 
 - **影響場景**: 
   - 先插槍再點開始充電 (主要) 
   - 任何 EV 拔槍後充電樁不送 StatusNotification(Available) 的情境 
 - **使用者體驗**:車主完全無法充電,必須人工介入 
 - **錯誤代碼**:`isConnectorAvailableForCharging=false` → 400 Bad Request 

 ## 修法方向(待 Ken 確認) 

 ### 方案 A(推薦):EMS 加 FINISHING 狀態 timeout fallback 
 - 監測 FINISHING 狀態超過 N 分鐘(例如 5 分鐘)自動 reset AVAILABLE 
 - 並釋放對應 slot 
 - 觸發 `ConnectorForHqDto` 重新 query 
 - 同時 log 警告 + 觸發事件供後續追蹤 

 ### 方案 B:被動等待 + 加重試 
 - 當用戶點開始充電時,若 connector 是 FINISHING 狀態超過 1 分鐘,強制 reset AVAILABLE 
 - 適用於「使用者已經想用」的情境 

 ### 方案 C:華城充電樁 firmware 修 
 - 聯絡華城 (FORTUNE) 修正 firmware,確保 EV 拔槍後一定送 StatusNotification(Available) 
 - 中長期建議 

 ## 建議採用:A + B 並行 + 開案追 C 

 ## 暫時補救(已記錄於此 ticket) 

 ```sql 
 -- 手動把卡在 FINISHING 的 connector 救回 AVAILABLE 
 UPDATE e_connectors  
 SET status='AVAILABLE', last_status_change=NOW(), update_time=NOW() 
 WHERE id='CBDAX50A-25L-A6-A072-001'; 
 ``` 

 ## 參考資訊 

 - 受影響 connector:CBDAX50A-25L-A6-A072-001 (FORTUNE 華城,B1 群組,private) 
 - 案場:ct1 
 - Charge Point:CBDAX50A-25L-A6-A072 (FE-EVI, FEEVC-3-32A-CNS) 
 - 案場 log 檔名:a17_0618_3---48ce5901-8336-4d6b-a7de-016b61287a66.txt 
 - 相關 ticket:#1342 (華城 TYPE_B/D stop 完全沒作用,FINISHING 後卡 PREPARING) 
 - Branch:private 


 --- 

 ## 2026-06-18 20:35 重新整理 Bug 1(5 小時觀察 + 程式碼分析) 

 ### 重大發現:Bug 1 真正本質不是「卡 Finishing」,而是「先插槍 EMS 自動送電」 

 從 5 小時的 6 次充電觀察(tx #507~#512),卡 Finishing 只發生 1 次(#507,28 分鐘),其他 5 次都是 8~24 秒正常補送。所以「卡 Finishing」是衍生症狀(低機率),不是核心 bug。 

 **真正 100% 重現的主症狀**:Fortune / Tesla / Unknown 充電樁在「先插槍、未按開始充電」時,EMS 會自動送電。 

 ### 程式碼根本原因 

 **`FortuneConnectorTypeRotationStrategy.onPreparingWithoutRotation()` (line 25-27) 永遠回傳 `SEND_REMOTE_START`**: 

 ```java 
 @Override 
 public ConnectorTypeActionResult onPreparingWithoutRotation(ConnectorTypeActionContext ctx) { 
     return ConnectorTypeActionResult.of(ConnectorTypeAction.SEND_REMOTE_START);    // ← 自動送電 
 } 
 ``` 

 **對照組 `CloudLinkConnectorTypeRotationStrategy.onPreparingWithoutRotation()` (line 25-27) 回傳 `noAction()`** — 不自動送電。 

 **觸發鏈**: 
 - 樁送 `StatusNotification: PREPARING` 
 - → `ChargingOrchestrator.onStatusNotification()` (line 598) 
 - → `handleStatusNotificationForRotation()` (line 776) 
 - → `strategy.onPreparingWithoutRotation(ctx)` (line 851) 
 - → Fortune 回傳 `SEND_REMOTE_START` 
 - → 自動送 `RemoteStartTransaction` → 樁充電 

 **影響範圍**: 
 - `OtherConnectorTypeRotationStrategy extends FortuneConnectorTypeRotationStrategy` (line 6) — DEFAULT/TESLA/UNKNOWN 都受影響 
 - `ChargingOrchestrator.buildConnectorTypeRotationStrategies()` (line 252-254) 註冊:DEFAULT/TESLA/UNKNOWN → OtherConnectorTypeRotationStrategy 

 ### 觀察資料(5 小時內,6 次充電完整記錄) 

 | tx | 觸發模式 | Finishing → Available | reason | 備註 | 
 |----|---------|----------------------|--------|------| 
 | #507 | (前次,先插再開) | **28 分鐘**才補送 | EVDisconnected | 14:28 異常,DataTransfer `txId=0, errorCode=1000` | 
 | #509 | (前次,正常) | 24 秒 | EVDisconnected | 用戶手動按 Start → 插槍 → 充 → Stop → 拔槍 | 
 | #510 | **先插槍 EMS 自動送電** | 14 秒 | Remote | 15:23 測試 | 
 | #511 | **先插槍 EMS 自動送電** | 8 秒 | Remote | 15:33 測試 | 
 | #512 | **先插槍 EMS 自動送電** | 9 秒 | Remote | 15:55 測試 | 

 **正常 Finishing 持續時間:8~24 秒**(平均 ~14 秒) 

 ### 「卡 Finishing」只發生在 txId=0 的特殊情境 

 從 5 小時觀察: 
 - 6 次充電中只有 #507 那次卡 Finishing 28 分鐘 
 - 原因:DataTransfer `transactionId=0, errorCode=1000`(EV 不配合充電) 
 - 其他 5 次都是 8~24 秒正常補送 Available 
 - 重現條件:EV 端要求暫停充電(華城 vendor-specific) 

 ### 樁重開機行為 

 華城樁在以下時間點都自己重開機(BootNotification): 
 - 14:56:04(卡 Finishing 28 分鐘後) 
 - 15:25:03(充電中) 
 - 15:31:47(充電中) 
 - 15:49:08, 15:53:48, 16:03:02(空檔重連) 

 **這是 vendor-specific 行為**,EMS 端目前沒對應處理(但目前沒造成壞處)。 

 ### Bug 1 重新分層(取代原本 ticket 描述) 

 ``` 
 #1343 Bug 1 
 ├── 主症狀 (重現率 100%) 
 │     └── Fortune / Tesla / Unknown 充電樁「先插槍、未按開始充電」時 EMS 自動送電 
 │         ├── 觸發:StatusNotification: PREPARING → onPreparingWithoutRotation → SEND_REMOTE_START 
 │         ├── 對照組:CloudLink 是 noAction()(行為不一致) 
 │         └── 影響:用戶未授權就被充電(體驗差 + 安全疑慮) 
 │ 
 ├── 衍生症狀 (低機率,特定條件才觸發) 
 │     └── Finishing 狀態卡住不送 Available 
 │         ├── 觸發:txId=0(華城樁 EV 不配合充電 → vendorErrorCode=1000) 
 │         ├── 正常 Finishing:8~24 秒補送 Available 
 │         └── 異常 Finishing:8~28+ 分鐘才補送(甚至完全不送,要靠樁 BootNotification 觸發) 
 │ 
 └── 旁支發現 (與 Bug 1 共因) 
     └── StopTransaction 帶 transactionId=0 → EMS 拋 ERROR 
         └── TransactionService.stopTransaction() 沒容錯 txId=0 
 ``` 

 ### 修法方向(已大幅簡化) 

 **原本 ticket 列的 A (timeout fallback) 跟 B (user-triggered reset) 主要處理「卡 Finishing」這個衍生症狀。但從 5 小時觀察,「卡 Finishing」重現率極低(1/6),且 8 分鐘以上 timeout 仍會自動恢復。** 

 **真正要修的是「自動送電」這個主症狀**: 

 | 方案 | 改動 | 影響範圍 | 適用情境 | 
 |------|------|---------|---------| 
 | **方案 X(推薦)**:把 Fortune 的 `onPreparingWithoutRotation` 改成 `noAction()` | 1 個 class,3 行 | 改變 Fortune/Tesla/Unknown 行為,跟 CloudLink 一致 | 如果用戶體驗(不該自動送電)是首選 | 
 | 方案 Y:加 flag `enableAutoStartOnPlugin`,default false | 多個 class(config + strategy + ChargingOrchestrator) | 較複雜但保留彈性 | 如果部分場景需要自動送電(例如離峰自動充電) | 
 | 方案 Z:保留自動送電,但加「需要 user 授權」檢查 | 多個 class | 最複雜 | 如果要兼顧自動 + 授權 | 

 **對於衍生症狀(卡 Finishing)**: 
 - 觀察顯示實際上 8~24 秒就恢復,影響很小 
 - 如果仍要做 fallback,建議 timeout 設 **2 分鐘**(遠大於正常 8~24 秒) 
 - 觸發條件:`status=FINISHING AND current_transaction_id IS NULL AND last_status_change < NOW() - 2min` 

 ### 影響範圍重新評估 

 | 案場 / 樁類型 | 主症狀(自動送電) | 衍生症狀(卡 Finishing) | 修法方案 X 影響 | 
 |--------------|------------------|--------------------|---------------| 
 | ct1 華城 (FORTUNE) | ✅ 100% 重現 | ✅ 1/6 機率 | 改 noAction(),行為跟 CloudLink 一致 | 
 | a17 華城 (FORTUNE) | ✅ 100% 重現 | ✅ 1/6 機率 | 同上 | 
 | a17 CloudLink (CLOUDLINK) | ❌ 已是 noAction() | ⚠️ 見 #1342 | 不受影響 | 
 | 任何 TESLA / UNKNOWN | ✅ 100% 重現(extends Fortune)| 機率同華城 | 同 Fortune 修法 | 
 | 其他公版 (DEFAULT) | ✅ 100% 重現 | 機率同華城 | 同 Fortune 修法 | 

 ### 待釐清問題(重新評估) 

 - ✅ Q1: 華城樁 txId=0 的 Finishing 是不是 100% 重現?→ **否**,5 小時觀察只 1/6 機率 
 - ⚠️ Q2: EMS retry 機制對「FINISHING 狀態」會不會自動重試?→ 從程式碼看不會 
 - ✅ Q3: 「txId=0 → Finishing 卡住」是不是華城所有樁都這樣?→ 從 ct1 + a17 log 都有類似行為,但重現率低 
 - ✅ Q4: 華城樁 24 秒 Finishing 是否所有情境都 24 秒?→ **8~24 秒範圍** 
 - ❌ Q5: EV 不同狀態會不會影響 Finishing 持續時間?→ 還沒測過 
 - ⚠️ Q6: StopTransaction 帶 txId=0 的 ERROR 會不會影響 connector 狀態?→ 從 #1342 ticket 看會 

 ### 實作進度更新 

 - 2026-06-18 14:55 建立 Feature Branch `feature/redmine-1343-finishing-timeout-fallback` 
 - 2026-06-18 15:21 Ken 指示 **暫停實作,進入純觀察模式** 
 - 2026-06-18 20:35 Ken 要求 **重新整理 Bug 1** 
 - 重大發現:Bug 1 真正本質是「自動送電」不是「卡 Finishing」 

 ### 旁支發現詳細記錄 

 - **2026-06-18 15:05:20、15:06:57**:兩次 `StopTransaction` 帶 `transactionId=0` → EMS 拋 ERROR 
   - 推測:EM 對 transactionId=0 的 StopTransaction 沒容錯 
   - 與 #1343 共因(華城樁 EV 不配合充電導致 txId=0) 
   - **建議併案處理**(同個 onStatusNotification / StatusNotification 流程) 
 - **華城樁反覆重開機**:14:56, 15:25, 15:31, 15:49, 15:53, 16:03 
   - vendor-specific 行為 
   - 目前無實質影響,但建議追蹤 

 ### 觀察 log 時間區間 

 - 觀察 log 來源:`ems_branch.e_ocpp_message_log` table, charge_point_id=`CBDAX50A-25L-A6-A072` 
 - 觀察時間:2026-06-18 14:21 ~ 20:25(將近 6 小時) 
 - 觀察 connector:CBDAX50A-25L-A6-A072-001(華城 FORTUNE, B1 群組, private) 

 --- 

 ## 2026-06-18 21:41 重大更新(#1345 已修前置 bug) 

 **Redmine #1345 已經實作完成並 push**: 
 - Commit: `27e7d76 fix(#1345): StatusNotificationHandler 改用 Connector.updateStatus(),同步更新 last_status_change` 
 - Branch: `feature/redmine-1345-status-notification-last-status-change` (pushed to origin) 
 - 改動:StatusNotificationHandler 改用 `Connector.updateStatus()`,所有 StatusNotification 事件都會更新 `e_connectors.last_status_change` 

 ### 對 #1343 修法方向(方案 A)的影響 

 **之前**: 
 - 方案 A 的 Finishing timeout 判斷依賴 `e_connectors.last_status_change` 欄位 
 - 但欄位不可靠(只有 DataTransfer 觸發才會更新),會誤判 

 **現在(#1345 修好後)**: 
 - `e_connectors.last_status_change` 變成可靠的「status 真正變更時間」依據 
 - 方案 A 的 trigger 條件 `status=FINISHING AND last_status_change < NOW() - 2min` 可以正確運作 
 - 任何依賴 `last_status_change` 判斷 connector 卡死情境的邏輯都會更可靠 

 ### 對修法方向的影響 

 | 方案 | 之前狀態 | 現在狀態 | 
 |------|---------|---------| 
 | 方案 A (Finishing timeout fallback 2min) | 判斷邏輯不可靠 | 判斷邏輯可靠,可實作 | 
 | 方案 X (Fortune onPreparingWithoutRotation 改 noAction) | 仍是主症狀修法 | 仍是主症狀修法(不受 #1345 影響)| 

 ### 觀察資料補充 

 從 5 小時觀察(6 次充電 tx #507~#512),正常 Finishing 持續時間 8~24 秒。**8 分鐘以上的卡 Finishing 重現率只有 1/6,且該次由 OCPP StatusNotification 自然恢復(15:04:26)**。 

 **重要發現**:14:28 那次卡 Finishing 28 分鐘後,**確實是樁送 OCPP StatusNotification 訊息自然恢復**(e_ocpp_message_log 有完整 REQUEST + RESPONSE 記錄,CP timestamp=15:04:24.977Z),不是 Ken 手動改 DB。但 Ken 14:28~15:04 期間可能手動介入過(無法驗證因手動改 DB 不留痕跡)。 

 ### 重現率評估(修訂) 

 - 「卡 Finishing」重現率:5 小時觀察 1/6 ≈ 17% 
 - 但**每次都有 OCPP 訊息證據**支持「自然恢復」(最長 33 分鐘) 
 - 加上 #1345 修好後,方案 A 的 2 分鐘 timeout 仍可作為「保險」 
 - 仍建議實作方案 A(修 #1345 後變可行) 

 ### Ticket 狀態 

 - #1345: **In Progress** (commit 跟 test 已完成,等 Ken merge + 部署) 
 - #1343: 持續觀察中(主症狀修法方案 X 仍在評估) 

 ### 參考連結 
 - 相關 ticket: #1345 (StatusNotificationHandler last_status_change bug) 
 - Branch: `feature/redmine-1345-status-notification-last-status-change` 
 - Commit: `27e7d76` 

 --- 

 ## 2026-06-19 00:06 重大更新(SS3A framework 治本影響 #1343 修法) 

 **`sylksoft/sylksoft-framework` 6.9.5-SNAPSHOT build 45 已 deploy。** 

 ### Framework 改動 

 `PrefixDateSequenceIdGenerator.java` build 45 完全重寫: 
 - 移除所有 `IdGeneratorUtil.nextIdFromSequence()` 跟 `currentIdFromSequence()` 呼叫 
 - 新邏輯:JVM 內部 `ConcurrentHashMap counter + DB max(id)` 
 - 完全不需要 `hibernate_sequence` table 

 ### 對 #1343 修法的影響 

 | 修法 | 之前評估 | 現在評估(framework 治本後)| 
 |------|---------|----------------------------| 
 | 方案 A (Finishing timeout fallback) | 方案 A 判斷依賴 `last_status_change`,需 #1345 修好 | 仍可做(#1345 已修)但**需求降低**(卡 Finishing 重現率 1/6)| 
 | 方案 B (user-triggered reset) | 仍可做 | 仍可做(不受 framework 影響)| 
 | 方案 X (Fortune.onPreparingWithoutRotation 改 noAction) | **治本修法**,跟 framework 無關 | **仍推薦**(主症狀 Fortune 自動送電)| 

 ### #1343 跟 #1344 的關係釐清 

 - **#1343** (華城充電樁 Finishing 卡住 + Fortune 自動送電) — 跟 framework **無關**,仍需繼續處理 
 - **#1344** (hibernate_sequence 缺 e_connector_priority) — **由 framework build 45 治本**,Status 改為 Resolved 

 ### 重申 #1343 仍需做的核心工作 

 1. **方案 X** (改 `Fortune.onPreparingWithoutRotation` 為 `noAction()`) — Fortune 自動送電主症狀治本修法 
 2. **方案 A** (ScheduledTask 自動 reset Finishing connector) — 防禦性 fallback,可選 
 3. **方案 B** (user-triggered reset) — 可選 

 ### Ticket 狀態 

 - #1343: **New** (仍需處理 Fortune 自動送電 + Finishing 卡住) 
 - #1344: **Resolved** (framework 已治本) 
 - #1345: **In Progress** (commit 跟 test 已完成,等 Ken merge to private) 

返回