Bug #1295
是由 陳國瑋 於 23 天 前更新
## 問題描述
當充電樁連接器在資料庫中設定 `enable_off_peak = 0`(未啟用離峰充電),但在 `e_connector_priority` 表有 `charge_status = 'eligible'` 記錄時,系統會在離峰時段錯誤地將該連接器納入輪充 dispatch,導致車主在「未啟用離峰充電」的情況下被排程離峰充電。
**受影響實例:** 寶台A17(ct1-ssh.cloudlinkems.com),CP003-001(enable_off_peak=0,B3 群組)
---
## 觸發條件
1. `e_connectors.enable_off_peak = 0`(FALSE,非 NULL)
2. `e_connector_priority.charge_status = 'eligible'`
3. 離峰時段到來,`ChargingOrchestrator.rotationDispatchTick` 觸發
---
## 證據(寶台A17,2026-05-12 00:00)
### 資料庫狀態
```
connector_id = Test bug ticket for CP003-001
charge_point_id= CP003
enable_off_peak= \0 (0x00, FALSE,非 NULL)
charge_group = B3
charge_status = eligible
status_reason = NULL
priority = 15
```
### API Log(ems_branch api-2026-05-12-1.log.gz)
```
00:00:00.018 離峰起點時重新亂數排序完成 - chargeGroupId=B3, connectorIds=[CP003-005, CP003-001, CP003-004]
00:00:00.045 rotationDispatchTick 輪充調度,佔用 slot 完成 - connectorId=CP003-001, userId=U2510220002, priority=2
00:00:00.046 不經過輪充檢查的RemoteStart - connectorId=CP003-001, userId=U2510220002, chargePointId=CP003
00:00:00.047 收到遠端啟動充電請求 - connectorId=CP003-001, chargePointId=CP003, userId=U2510220002
...
00:02:14.230 開始交易,交易ID: 239, 連接器ID: CP003-001, 住戶ID: TAG20251022000000002
```
### cp_transactions 記錄
```
transaction_id = 239
connector_id = CP003-001
start_time = 2026-05-12 00:02:14
end_time = 2026-05-12 03:09:42
energy_consumed = 22870 Wh
is_offline = 0
reason = EVDisconnected
```
---
## 根本原因(Root Cause)
### 問題程式碼位置
`ChargingOrchestrator.rotationDispatchTick` → `dispatchEligibleConnectors` → `loadEligiblePriorities`
### 原因說明
**1. `OffPeakEnqueuePolicy.enqueueForOffPeak` 有正確的 `enable_off_peak` 檢查:**
```java
if (enabledOffPeak == null || enabledOffPeak.equals(Boolean.FALSE)) {
log.warn("enqueueForOffPeak skipped - connectorId={}, reason={}, enableOffPeak={}, msg=off-peak not enabled");
return OffPeakEnqueueResult.SKIPPED;
}
```
當 `enable_off_peak=0` 時,`enqueueForOffPeak` 正確 Skip,不更新 DB 記錄。
**2. `dispatchEligibleConnectors`(或 `loadEligiblePriorities`)缺少 `enable_off_peak` 過濾:**
從 `e_connector_priority` 表篩選 `charge_status = ELIGIBLE` 時,沒有 JOIN `e_connectors` 確認 `enable_off_peak` 是否為 true。
因此 `enable_off_peak=0` 的 CP003-001(當時 charge_status=eligible)仍然通過 filter。
**3. 排程繞過了 `OffPeakEnqueuePolicy` 的檢查**:`dispatchEligibleConnectors` 直接 dispatch,沒有再次確認 `enable_off_peak`。
### 修復方向
在 `dispatchEligibleConnectors` 或 `loadEligiblePriorities` 的查詢邏輯中,加入 `e_connectors.enable_off_peak = TRUE` 的過濾條件。
- **選項A(推薦):** 在 `loadEligiblePriorities` 的 SQL JOIN `e_connectors` 表時加 `WHERE c.enable_off_peak = TRUE`
- **選項B:** 在 `dispatchEligibleConnectors` 的 Predicate filter 中加 `connector.getEnableOffPeak() == Boolean.TRUE`
---
## 受影響範圍
所有 `enable_off_peak=0` 但 `e_connector_priority` 有 `charge_status=eligible` 記錄的連接器,在離峰時段都有被錯誤 dispatch 的風險。
---
## 查閱的 Log 檔案
| 檔案路徑 | 內容 |
|----------|------|
| `/opt/ems/ems_branch/api/logs/api-2026-05-12-1.log.gz` | 寶台A17 ems_branch API 全日行為(含 ChargingOrchestrator) |
| `/opt/ems/ems_branch/api/logs/scheduler-2026-05-12-1.log.gz` | ems_branch scheduler 排程 Log |
| `/opt/ems/ems_cp3/api/logs/ocpp-modbus-2026-05-12-1.log.gz` | CP003 充電樁 Modbus/OCPP Log |
## 查閱的 Database Table
| Table | 用途 |
|-------|------|
| `e_connectors` | 連接器設定(enable_off_peak, charge_group) |
| `e_connector_priority` | 排程優先順序(charge_status=eligible, priority) |
| `cp_transactions` | 充電交易記錄(is_offline, energy_consumed) |
| `cp_connectors` | 連接器狀態 |
| `e_charge_group` | 充電群組設定 |
| `s_user` | 車主帳號 |
## 相關程式碼(反編譯自 api.jar)
- `ChargingOrchestrator.rotationDispatchTick`(行 2157, 2724, 341, 374)
- `ChargingOrchestrator.dispatchEligibleConnectors`
- `OffPeakEnqueuePolicy.enqueueForOffPeak`(有正確檢查但未被 dispatchEligibleConnectors 呼叫)
- `ConnectorPriorityRepository.findByChargeGroupAndChargeStatusOrderByPriorityAsc`
返回