動作
Bug #1345
已結束[Branch] StatusNotificationHandler 沒更新 Connector.last_status_change,導致 last_status_change 欄位不可靠 (修補並修 #1343 方案 A 前置)
開始日期:
2026-06-18
完成日期:
預估工時:
概述
問題描述¶
StatusNotificationHandler 處理充電樁送過來的 StatusNotification 訊息時,不會更新 Connector.last_status_change 欄位。只有 DataTransferHandler(處理華城樁的 ChargeErrorNotification)才會透過 Connector.updateStatus() 同步更新 last_status_change。
結果:e_connectors.last_status_change 欄位不可靠,無法作為「status 真正變更時間」的判斷依據。
影響範圍¶
- 影響所有 vendor 的 connector(不限華城)
- 影響所有 StatusNotification 事件(每個狀態變更都會被 OCPP 送過來)
- 直接影響 Redmine #1343 方案 A(Finishing timeout fallback)— timeout 判斷邏輯會誤判
- 影響任何依賴
last_status_change欄位的業務邏輯(例如:卡 Finishing 超時、卡 PREPARING 超時等的偵測)
復現¶
- 部署任意版本的 ems_branch
- 任一充電樁送
StatusNotification: <新狀態>給 EMS - 觀察 DB:
SELECT id, status, last_status_change, update_time FROM e_connectors WHERE id = '<受影響 connector>'; - 預期:
last_status_change應該要被更新成「StatusNotification 處理當下的時間」 - 實際:
last_status_change沒被更新,停在 DataTransfer 上次觸發的時間(或 NULL)
實際案例(2026-06-18 ct1 觀察)¶
connector CBDAX50A-25L-A6-A072-001:
| 時間 | 事件 | status | last_status_change | update_time |
|---|---|---|---|---|
| 14:28:21 | StatusNotification: Finishing | FINISHING | 14:28:21 (DataTransfer 觸發) | 14:28:21 |
| 14:31:18 | StatusNotification: Finishing (重送) | FINISHING | (不變) | 14:31:18 |
| 14:56:13 | StatusNotification: Finishing (重開機後) | FINISHING | (不變) | 14:56:13 |
| 15:04:26 | StatusNotification: Available | AVAILABLE | (不變) | 15:04:26 |
| 15:05:00 | DataTransfer: ChargeErrorNotification | SUSPENDED_EV | 15:05:00 (DataTransfer 觸發) | 15:05:00 |
| 15:05:18 | StatusNotification: Finishing | FINISHING | (不變) | 15:05:18 |
| 15:05:26 | StatusNotification: Available | AVAILABLE | (不變) | 15:05:26 |
| 15:13:15 | DataTransfer: ChargeErrorNotification (txId=509) | SUSPENDED_EV | 15:13:15 (DataTransfer 觸發) | 15:13:15 |
| 15:14:01 | StatusNotification: Available | AVAILABLE | (不變) ← 應該要是 15:14:01 | 15:14:01 |
根本原因¶
StatusNotificationHandler.handle() (line 190-194) 使用 6 個 setter 來更新 connector:
connector.setStatus(ConnectorStatus.getEnum(status)); // ← 只改 status
connector.setErrorCode(ErrorCode.getEnum(errorCode));
connector.setInfo(info);
connector.setVendorErrorCode(vendorErrorCode);
connector.setUpdateTime(...); // ← 用 OCPP 訊息 timestamp 或現在時間
connector.setUpdater(Constants.DEFAULT_UPDATER);
// ❌ 沒呼叫 updateStatus(),所以 last_status_change 不更新
Connector.updateStatus() (line 256-262) 才是統一更新 status + last_status_change 的 method:
public void updateStatus(ConnectorStatus status, ErrorCode errorCode, String info, String vendorErrorCode, String updater) {
this.status = status;
this.errorCode = errorCode;
this.info = info;
this.vendorErrorCode = vendorErrorCode;
this.lastStatusChange = LocalDateTime.now(); // ← 統一更新
this.setUpdater(updater);
this.setUpdateTime(new Date());
}
DataTransferHandler (line 184) 跟 ConnectorService (line 651) 都正確使用 updateStatus(),只有 StatusNotificationHandler 漏了。
修法方向¶
方案 A(推薦):把 StatusNotificationHandler 改成呼叫 Connector.updateStatus()
- 改動量:1 個 method call 取代 6 個 setter call(2 處:line 144 跟 line 190 附近)
-
影響範圍:所有 StatusNotification 事件都會更新
last_status_change -
時機一致性:
updateStatus()用new Date()(現在時間),原本的setUpdateTime用 OCPP 訊息 timestamp — 差異是「OCPP 訊息產生時間」vs「EMS 處理時間」,差異通常 < 1 秒,業務上可接受 -
風險:低。
updateStatus()內部行為跟目前 6 個 setter 組合等價,只是多更新last_status_change
方案 B:在 Connector entity 加 helper method¶
- 加
setStatusAndLastChangeTime(ConnectorStatus status)一行 method - StatusNotificationHandler 改成呼叫這個 helper
- 比方案 A 多了 entity method,但保留「OCPP 訊息 timestamp 用於 updateTime」的彈性
方案 C:把 DataTransferHandler 改成不要動 status¶
- 讓 DataTransfer 只記 log,不主動改 status
- 由後續的 StatusNotification 統一改
- 改動量較大,且會影響華城樁的 vendor-specific 行為
建議採用:方案 A¶
理由:
- 改動最小
- 跟現有 DataTransferHandler 跟 ConnectorService 行為一致
- 修完後
last_status_change欄位成為可靠的 status 變更時間依據 - 連帶讓 #1343 方案 A(Finishing timeout fallback)能正確運作
測試計劃¶
新增 StatusNotificationHandlerLastStatusChangeTest:
- 驗證 StatusNotification 處理後
last_status_change被更新成「處理當下時間」 - 驗證連續多次 StatusNotification 都能正確更新
last_status_change - 驗證 OCPP 訊息 timestamp 不會影響
last_status_change(用new Date())
參考資訊¶
- 受影響檔案:
java/ems_branch/src/main/java/com/sylksoft/ems/branch/ocpp/handler/StatusNotificationHandler.java(line 144-149, 190-194) - 現有正確實作:
DataTransferHandler.java(line 184),ConnectorService.java(line 651) - Connector entity method:
Connector.java(line 256-262) - 相關 ticket:#1343 (Finishing 卡住,方案 A 依賴 last_status_change 判斷 timeout)
- 觀察案例:2026-06-18 ct1 connector CBDAX50A-25L-A6-A072-001
- Branch:待建立
feature/redmine-1345-status-notification-last-status-change
動作