討論 #1462
是由 陳國瑋 於 17 天 前更新
> **閱讀基準:** [主需求 #1422](https://redmine.sylksoft.com/issues/1422) **一般 REST 更新規則(#1424 DEC-032)**:Live Load 完整「即時/今日/7 日」歷史 series 與 [HTML Mock v0.3/附件 #1117](https://redmine.sylksoft.com/attachments/1117)。以下沿用主票區塊編號,依畫面由上到下、由左到右說明。Mock 數字皆為假資料;功能適用所有案場,A17 不是固定規則。 Group Capacity/Slots,隨共用 Overview 在頁面首載、每 5 分鐘及手動刷新更新,不建立各自 timer。`currentPowerKw` 與 open buckets 在 WebSocket 接手後仍只由 `LIVE_LOAD_UPDATED` 更新;定時/手動 REST refresh 不得覆寫 live state,socket 斷線時也不得形成 fallback。
> **現行傳輸規則(#1424 DEC-031)**:案場即時負載是 V1 兩個 WebSocket 區塊之一。REST 提供 bootstrap 與完整「即時/今日/7 日」歷史 series;`LIVE_LOAD_UPDATED` 以 replacement 更新 `currentPowerKw` 與目前 open buckets,跨 bucket 邊界時可一併提供 finalized bucket。斷線時保留最後即時資料/時間並顯示「已停止更新」,不做 REST polling fallback、custom backoff、自動重連或 sequence/gap recovery;只有整頁 reload 才重新取得 ticket 與建立 socket。
## 04 → 05|本票涵蓋畫面中段的左右兩區 背景
**左邊看「整個案場現在用了多少功率、如何變化」;右邊看「每個群組自己的容量與供電名額」。** 兩區目的不同,不能把群組容量相加後當成案場契約容量。 主需求:#1422。
```text 原始 mockup 在「案場即時負載」中,將 A17 三個 Charge Group 的 contractCapacity(各 99 kW)直接相加為 297 kW,並據此顯示案場契約容量利用率與剩餘容量。
經現有程式碼與 A17 read-only 資料確認:
- contractCapacity 是 Charge Group 層級欄位,現有 SlotCalculator 使用它計算該群組可配置的 slots。
04 案場即時負載(左) 05 群組容量與 Slots(右) - 系統目前沒有獨立的 building/site 層級上游契約容量欄位。
目前功率(kW) 群組名稱、目前功率/自身容量 - 系統也沒有描述各 Charge Group 是否共用上游容量、是否可加總的電力拓撲資料。
即時/今日/7 日趨勢 該組占用/保留/可用名額、Queue
│ │
└─ 不推算案場契約容量、利用率或剩餘容量 ──┘
``` - 因此直接相加只得到群組排程上限合計,不能可靠宣稱為案場對台電的契約容量或剩餘容量。
這裡只新增 本票只定義 Dashboard 的唯讀資料整理與呈現,不修改 MeterValue 採集、充電、輪充或名額分配。 的唯讀顯示與 read model,不修改任何充電、輪轉或容量配置邏輯。
## 1. 04 案場即時負載:由上到下閱讀 LOAD-DEC-001:V1 不推算案場層級契約容量
| 畫面位置 | 顯示內容 | 使用者應理解的意思 | **狀態:已確認(2026-09-03,Ken)**
### 使用者需要知道的資訊
「案場即時負載」回答:
- 全案場目前合計使用多少充電功率。
|---|---|---| - 即時功率資料是否仍為最新、可以信任。
| 標題/說明 | 案場即時負載;各充電群組目前充電功率合計 | 這是充電功率,不是案場對電力公司的契約容量。 | - 功率隨時間的變化。
「群組容量與 Slots」則分別回答每一個 Charge Group 的:
- 目前功率。
| 右上三個按鈕 | 即時、今日、7 日 | 只切換圖表時間範圍,不調整設備回報或充電控制。 | - 該群組自己的 contract capacity/allocated power。
| 大數字 | 目前功率 currentPowerKw,單位 kW;Mock 21.84 | Backend 於資料時間可確認的目前總功率,與圖上「區間平均」可能不同。 | - occupied/reserved/available slots 與既有 queue 狀態。
兩者不得混成一個未經資料模型證明的案場契約容量。
### V1 顯示規則
- 保留案場即時總功率 currentPowerKw 與負載圖表。
| 資料時間/品質 | - 保留 latestMeterAt、freshness/source status | 這個目前值是否仍新鮮;未知顯示「—」。 | status,使使用者知道即時數字是否過期。
| 下方折線圖 | 一條區間平均總充電功率線 | 看各時間區間的平均功率變化,不是累計 kWh。 | - 移除案場層級的「契約容量」、「契約容量利用率」與「剩餘容量/餘裕」。
- 不得以 SUM(group.contractCapacity) 產生 buildingContractCapacityKw。
- 各 Charge Group 區塊仍可顯示該群組自己的 currentPowerKw/contractCapacityKw 與 slots。
- 群組百分比若顯示,分母只能是該群組自己的 contractCapacity;label/位置必須讓使用者清楚它是群組值。
- currentPowerKw 未知時顯示「—」與對應資料狀態,不得以 0 冒充。
- 本決策適用所有 building;不得硬編碼 A17 或固定群組數量。
### 1.1 Frontend 要開發的內容
- 「案場即時負載」主數字只顯示 Backend 回傳的 currentPowerKw。
- 說明文字改為「各充電群組目前充電功率合計」及資料 freshness 語意。
- 移除 site-level contractCapacityKw、utilizationPercent、remainingCapacityKw 的 label、數值、進度條與前端計算。
- 不從 Charge Group 陣列自行加總契約容量。
- 群組列表仍逐組顯示該組容量/slots,並適應不同群組數量。
- Loading、null、Partial、Stale、Error 不得退回假資料或把未知顯示為 0。
### Backend/API 要開發的內容
- Live Load 的 REST/WebSocket read model 回傳 currentPowerKw、latestMeterAt 與 freshness/source status。
- currentPowerKw 依本票 Live Load 公式與 #1424 現行 global per-source freshness policy,只彙整 Backend 判定未 stale、enabled Connector 的最新有效 Power value。
- V1 的 building-level Live Load DTO/event 不回傳由 Charge Group 容量相加而來的 contractCapacityKw、utilizationPercent、remainingCapacityKw。
- Group Capacity read model 仍逐組回傳既有 group contract capacity、allocated power、reserved/occupied/available slots。
- 不新增案場契約容量設定欄位、資料表、管理 API 或電力拓撲模型。
- 不修改 SlotCalculator、rotation dispatch、queue lifecycle、OCPP/Modbus 指令或任何業務資料。
### A17 驗證基線(不是跨案場規則)
查詢日期:2026-09-03。
- A17 有 B1、B2、B3 三個 Charge Group,各自 contractCapacity = 99 kW。
- 三組欄位數值相加為 297 kW,但只能視為群組排程上限的算術合計。
- A17 有 4 個 Charge Point、12 個 Connector,皆已指派群組。
- 現有資料無法證明三個群組使用三個完全獨立的上游契約,或可用 297 kW 計算案場餘裕。
A17 數字只用來揭露原 mockup 的假設與驗證通用設計,不作為其他案場的固定值。
## LOAD-DEC-002:V1 保留即時/今日/7 日三種負載檢視
**狀態:已確認(2026-09-03,Ken)**
- 「案場即時負載」保留「即時」、「今日」與「7 日」三個檢視,不移除 7 日。
- 7 日負載呈現的是功率(kW)隨時間的變化,與「30 日充電用量」呈現的能量(kWh)/Sessions 不同,兩者不互相取代。
- 三個按鈕只切換 read-only 圖表資料,不改變 MeterValue 回報頻率、充電控制、queue、rotation 或 WebSocket 連線。
- 本決策先確認三種檢視必須存在;每種檢視的 X 軸:案場當地時間 軸時間範圍、bucket 粒度、tick label,以及 Y 軸採瞬時值/平均值/峰值的定義,列為下一項需求決策。
- 在軸線定義確認前,mockup 的折線與座標文字仍屬假資料,不可當成 API contract 或驗收基準。
所有時間用 ## LOAD-DEC-003:Live Load X/Y 軸與時間 Bucket 定義
**狀態:已確認(2026-09-03,Ken)**
### 圖表與主數字的分工
- 區塊大數字標示「目前功率」,顯示 currentPowerKw,代表 Backend 在 generatedAt 時間點可確認的案場充電總功率。
- 折線圖標示「區間平均總充電功率」,不是契約容量、容量利用率,也不是 kWh。
- 所有日期與時間均使用 Backend 回傳的 building timezone,不用 timezone;Frontend 不使用 browser timezone 切日。(LOAD-DEC-002/003) 自行切日。
### X 軸正式定義
| 檢視 模式 | 時間範圍 X 軸範圍 | 每個資料點的區間(bucket) Bucket | 最多點數 最多資料點 | 軸標/Tooltip Tick/Tooltip |
|---|---|---|---|---| |---|---|---:|---:|---|
| 即時 | generatedAt 往前 60 分鐘至 generatedAt | 1 分鐘 | 60 | HH:mm;Tooltip 顯示精確起訖分鐘。 軸上顯示 HH:mm;tooltip 顯示完整起訖分鐘 |
| 今日 | 案場當日 00:00 至 generatedAt | 15 分鐘 | 96 | HH:mm;Tooltip 顯示日期及區間起訖。 軸上顯示 HH:mm;tooltip 顯示日期與 15 分鐘起訖 |
| 7 日 | 今天加前 案場今天加前 6 個案場日曆日,第一天 個日曆日,第一天 00:00 至 generatedAt | 1 小時 | 168 | 以 軸上以 M/d 分日;Tooltip 分日;tooltip 顯示 M/d HH:mm 及區間起訖。 與一小時起訖 |
- 「7 日」不是固定往回 日」是包含今天的 7 個案場日曆日,不是最近 168 小時。FE 可減少軸上文字刻度以避免擠在一起,但不改時間範圍、粒度或資料點。最後區間尚未結束時標示 OPEN(進行中)。 小時。
- rangeEndAt/最後 bucket 可以是尚未結束的進行中區間;API 必須以 bucketState = OPEN 標示,避免把時間尚未結束誤認為資料品質異常。
- FE 可依寬度減少可見 tick 數量以避免文字重疊,但不得改變資料點、時間範圍或 bucket;tooltip 必須顯示精確時間。
### 1.2 Y 軸:區間平均功率 軸正式定義
- 軸名「充電功率(kW)」,圖表說明「區間平均總充電功率」。 軸名固定為「充電功率(kW)」。
- 最小值從 最小值固定從 0 開始;最大值依所選資料的有效值自動取整,不固定成 開始。
- 最大值依目前選取 series 的有效值自動取整;不以 297 kW 或群組容量總和。 或任何群組容量合計作為固定上限。
- 每個點為 每個點的數值為該 bucket 的 time-weighted averagePowerKw,顯示小數點後 averagePowerKw,顯示到小數點後 2 位;Tooltip:「區間平均功率 位。
- Tooltip 顯示「區間平均功率 X.XX kW」。 kW」;0 表示 Backend 確認該區間平均充電功率為 0。
- 只畫一條平均線,不加峰值、最小值、契約容量線、預測線。7 日 kW 與 #1461 的 30 日 kWh/Sessions 不能互相取代。 無法可靠計算的 bucket 回 averagePowerKw = null,並以 dataStatus = PARTIAL/ERROR 表示;圖表顯示缺口及「資料無法取得」,不得用 0 補值。
- 本圖只畫一條平均功率線;V1 不同時增加 peak、min、契約容量線或預測線。
## 2. ### Backend 如何算功率:先整理時間線,再算平均 計算規則
- 原始來源為 Power.Active.Import,依 Power.Active.Import;先依 unit 正規化後統一回 kW。作用範圍依 Backend 解析的 Building 與既有 enabled Connector scope,不把 A17 數量寫死。(LOAD-DEC-001/003/005)
```text 正規化,A17 目前為 W,API 對 FE 統一回 kW。
各 - 由各 Connector 的功率讀值+充電/交易事實
↓
按時間排序、W 轉 kW、檢查數值與 依時間排序的 Power 讀值重建案場總功率時間線;每個讀值只能沿用至 Backend freshness
↓ policy 的有效期限。
重建各時間片段可確認的案場總功率
↓ - 同一時間點的 site power 是各 Connector 當時仍有效的最新讀值合計。
按片段持續時間加權 → - 每個 bucket 的 averagePowerKw 對重建後的 site power 做 time-weighted average,不可直接對 e_meter_values rows 做算術平均。
```
例如一分鐘內,前 30 秒為 10 kW、後 30 秒為 20 kW,該分鐘平均為 15 kW。不同 - 原因是不同狀態的回報頻率不同;直接平均 raw rows 會讓回報較密集的 Connector 回報頻率不同,**不能直接加總或平均 raw MeterValue rows**,否則回報較密的設備會被賦予較大權重。
### 2.1 每個時間片段的判定
| 來源事實 | 對功率的處理 | 權重過高。
|---|---|
| 新鮮 runtime/transaction 明確表示未充電 | 貢獻 0,不必額外要求 Power MeterValue。 |
| 正在充電,Power 有效、非負、單位可換算、仍在 - 若正在充電的 Connector 在 freshness cutoff 內 | 使用最新有效值,可沿用至下一筆讀值或有效期限。 | 後仍缺少有效 Power,該 bucket 依 LOAD-DEC-005 回 averagePowerKw = null、dataStatus = PARTIAL;V1 不另建 coverage 門檻。
| 正在充電但 Power 缺少、負值、無法解析或過期 | unknown,不能當成 0,也不能無限沿用舊值。 |
| 充電/交易事實無法確認,包含離線且仍有未結束交易等情況 | 不假定為 0。這是資料品質判斷,**不產生「離線充電/待 reconciliation」推測性文案**。 |
正常回報間隔內沒有每秒新資料,不會自動變成缺值。cutoff 使用 - 歷史 series 由 REST 提供;WebSocket 依 #1424 DEC-033 DEC-031 的 Backend 全系統 per-source 預設,不新增案場 override。 `LIVE_LOAD_UPDATED` 更新 currentPowerKw 與 open/跨界 finalized buckets,不傳送整段 7 日 series。
### 2.2 一整個 bucket 的結果 A17 Read-only 依據
| 情況 | averagePowerKw | dataStatus | 查詢時間:2026-09-03。
- 最近 7 日有 14,550 筆 Power.Active.Import,涵蓋 11 個曾回報 Power 的 Connector,單位皆為 W。
|---|---|---| - 最近 24 小時有 1,342 筆;其中充電中的 Sample.Clock 多數約每 30~34 秒回報,未充電的 Sample.Periodic 多數約每 900~905 秒回報。
| 所需時間片段全部可確認 | 時間加權平均 | COMPLETE | - 因回報時間與頻率不一致,不能把 raw rows 直接加總或直接平均。
| 全部可確認且全程沒充電 | 0 | COMPLETE |
| 存在超過 cutoff 後仍無法確認的充電功率片段 | null | PARTIAL |
| 查詢/計算失敗 | null | ERROR | - 目前 MeterValueRepository 沒有 Dashboard 歷史負載 aggregate query;V1 需要新增唯讀 query/service,但不新增 MeterValue 寫入或改變回報頻率。
不回「已知設備的部分功率合計」冒充完整案場功率,不做線性插值、前後平均、預測、coverage 百分比或 80%/90% 通過門檻。 ### 外部參考
## 3. 圖表狀態:時間沒結束,不等於資料不完整 - EIA 說明 kW 是功率,kWh 是一段時間使用的能量;因此 Live Load Y 軸使用 kW,不與 30 日用量的 kWh 混用:
https://www.eia.gov/energyexplained/electricity/measuring-electricity.php
- 美國能源部企業電費說明以 15 分鐘整合需求示範區間化功率;本提案只借用「區間平均」的清楚語意,不把 Dashboard 數值當成正式需量計費:
https://www.energy.gov/sites/default/files/2024-04/understanding-your-utility-bill.pdf
bucketState 與 dataStatus 是兩件事,不能互相代替。(LOAD-DEC-005) ### 已確認項目
| 欄位 | 值 | 意義 | - [x] 即時 60 分鐘/1 分鐘 bucket。
|---|---|---| - [x] 今日 00:00 至現在/15 分鐘 bucket。
| bucketState | OPEN | 時間區間尚未結束,只計已經過的 bucketStartAt~event occurredAt/rangeEndAt,不估未來。 | - [x] 7 個案場日曆日/1 小時 bucket。
| bucketState | FINALIZED | 區間已結束,不再因正常時間推進變動;結束不代表品質一定完整。 | - [x] Y 軸使用 time-weighted averagePowerKw,不使用峰值或 raw sample。
| dataStatus | COMPLETE | 已經過/可計算部分的資料完整。 |
| dataStatus | - [x] 進行中 bucket、null 與 PARTIAL | 有功率未知,無法形成完整案場平均。 |
| dataStatus | ERROR | 查詢或計算失敗。 | 的呈現。
所以 OPEN+COMPLETE 是正常「進行中」,FINALIZED+PARTIAL 也是可能結果。 ## LOAD-DEC-004:REST 提供完整 Series,WebSocket 只更新目前功率與 Bucket 邊界
FE 顯示規則: **狀態:已確認(2026-09-03,Ken)**
### 資料通道分工
- OPEN 只標「進行中」,不因 OPEN 使用警告色。 REST 在頁面首次載入、共用 5 分鐘 Overview 與手動刷新時提供 LOAD-DEC-003 定義的完整歷史 series;切換 range 只讀取同一份 Frontend cache,不另外請求。WebSocket 接手後,Overview refresh 不覆寫 currentPowerKw 與 open buckets 的 live state。
- 0+COMPLETE 畫 0 線,Tooltip 0.00 kW。 LIVE_LOAD_UPDATED WebSocket event 提供 currentPowerKw,以及 LIVE/TODAY/SEVEN_DAYS 三種範圍目前進行中的 open bucket。
- null+PARTIAL 留缺口,說明「部分充電功率無法取得」。 WebSocket 不在每次 MeterValue 更新時重送 60/96/168 個歷史 points。
- null+ERROR 留缺口,說明「負載資料計算失敗」。 三種 open buckets 一併傳送,因此 Backend 不需要知道使用者目前選了哪個 tab,也不新增 client subscribe/unsubscribe protocol。
- 不把缺口補 0、不沿用前值、不跨缺口連線;文字與螢幕閱讀器均可辨識。 currentPowerKw 與 open bucket 的 averagePowerKw 語意不同,數值可以不同;Frontend 不得互相覆寫。
## 4. 三種檢視如何取得、切換與更新
### 4.1 一次取得三組歷史,切換不發 request WebSocket Bucket Payload
Overview 首載、共用每 5 分鐘及手動刷新,**同一次**回 LIVE/TODAY/SEVEN_DAYS 三組完整歷史;不另設負載 timer、lazy-load endpoint、range request parameter 或每頁籤 API。(LOAD-DEC-004/007、#1424 DEC-032) 每個 open bucket 至少包含:
| 使用者行為/情況 | FE 行為 | - rangeCode:LIVE/TODAY/SEVEN_DAYS。
|---|---| - bucketStartAt、bucketEndAt。
| 開啟/整頁 reload | 預設 LIVE(即時)。選擇只保存在本次頁面,不存 URL/使用者偏好。 | - averagePowerKw。
| 切換即時/今日/7 日 | 切換已有 cache,同步 active/aria-pressed,不重抓 API、不動 socket。 | - bucketState = OPEN。
| 5 分鐘/手動刷新 | 更新一般歷史,保留 selectedRange;不覆寫已由 WS 接手的 currentPowerKw/open buckets。 |
| 所選 range 為 EMPTY/ERROR | 原位顯示該範圍狀態,不自動跳別頁;其他按鈕仍可操作。 |
| 某段歷史失敗、目前值正常 | 各自呈現,不因歷史失敗清空目前值或另外兩個正常 range。 | - dataStatus、updatedAt/freshness metadata。
各 range 帶 rangeCode、起訖、bucket size、buckets、dataStatus/dataState、updatedAt 及必要的 lastSuccessfulAt。 當時間跨越 bucket 邊界時:
全零+COMPLETE 仍畫 0 線,不是 Empty。EMPTY 必須由 - Backend 明確表示「成功但無可顯示歷史」;FE 不以空陣列猜 Empty。PARTIAL 保留正常點及缺口。ERROR 顯示「{範圍}負載資料暫時無法取得」;同案場有舊 series 可保留並標明更新失敗與 lastSuccessfulAt。
### 4.2 WebSocket 只送目前值與區間變動
LIVE_LOAD_UPDATED 不重送完整 60/96/168 點;帶 currentPowerKw 與三種 range 傳送剛結束 bucket 的 finalized replacement,bucketState = FINALIZED。
- 同一事件可同時帶下一個新的 open buckets,跨界時可一併帶剛結束的 bucket。
- 1 分鐘、15 分鐘與 1 小時邊界可在同一時刻各自完成,event 必須以 rangeCode + bucketStartAt 唯一識別。
- 若 finalized bucket。Backend 不必知道 FE 選哪個 Tab,不新增 subscribe/unsubscribe。(LOAD-DEC-004) bucket 因資料不完整而為 averagePowerKw = null、dataStatus = PARTIAL/ERROR,必須照實傳送,不得沿用上一個有效值或補零。
Bucket 欄位:rangeCode、bucketStartAt、bucketEndAt、averagePowerKw、bucketState、dataStatus、updatedAt/freshness。 ### Frontend 合併規則
```text - 以 buildingId + rangeCode + bucketStartAt 找到 point。
區間跨界事件
├─ 上一區間 FINALIZED replacement(可能仍為 null/PARTIAL)
└─ 下一區間 OPEN
↓ - 相同 key 的 WebSocket bucket replacement 直接取代既有 point,不做累加或重新平均。
FE 依 buildingId+rangeCode+bucketStartAt 找到原資料
↓ - 收到新的 open bucket 時,先套用 event 內的 finalized bucket,再加入新的 open bucket。
先取代已結束點,再加入新 OPEN,裁切至該 - 每個 range 的視窗 只保留 LOAD-DEC-003 規定的視窗;LIVE 最多 60、TODAY 最多 96、SEVEN_DAYS 最多 168 points。
```
相同 key 直接取代,不累加/再平均、不用 - FE 不使用 currentPowerKw 自行積分、不按抵達時間計算,也不由別的 自行積分、不依 arrival time 計算 averagePowerKw,也不從其他 range 換算。只接受相同 buildingId 與受支援 schemaVersion;不符時不套用、不猜資料,也不改抓 換算。
- Frontend 只套用 `buildingId` 相符且 `schemaVersion` 受支援的 event;不相符或不受支援時不得套用、不得猜測 buckets,也不得自行改抓 REST 做即時 作為即時資料 recovery。
- 切換 range 只改變顯示的 cached series,不重建 Dashboard WebSocket。
### 更新與中斷呈現
- Backend 可因資源保護內部 debounce/coalescing,但不承諾固定 可為資源保護做內部 debounce/coalescing,但 V1 不承諾固定 5 秒更新;沒有 秒產品更新週期;沒有 Live Load/freshness 變化不需空推。
### 4.3 斷線:保留資料與時間,提示停止更新
頁首及本區提示連線失敗/中斷;保留最後 變化時不需要空推送。
- WebSocket 中斷時保留最後 currentPowerKw/open buckets 與時間,無資料顯示「—」。共用 與時間,顯示「已停止更新」,不啟動 60 秒 polling 或其他 REST fallback。共用 5 分鐘 Overview 可繼續更新歷史及其他一般區塊,但不可冒充即時備援或覆寫最後 仍可更新 REST-owned 歷史 series,但不得冒充即時 recovery 或覆寫最後的 WS-owned live state。
不做 REST polling fallback、backoff、自動重連、sequence/gap recovery;只有整頁
- V1 不做背景自動重連。只有使用者整頁 reload 才重新取得 時,才重新取得 Overview bootstrap、短效 ticket 與建立 socket。(#1424 DEC-031/032) 並建立新的 Dashboard socket。
- 本決策不修改 MeterValue 採集頻率,不增加設備指令,也不改變充電、queue 或 rotation 邏輯。
## 5. 05 群組容量與 Slots:從上往下逐組看 ### Acceptance Criteria
| 位置 | 資訊 | 來源與界線 | - [ ] REST 可單獨重建目前選取 range 的完整正確折線。
|---|---|---| - [ ] 一般 LIVE_LOAD_UPDATED 不包含完整歷史 series,只包含 currentPowerKw 與三種 open buckets。
| 區塊標題/時段 | 群組容量與 Slots、既有時段文字 | Mock「離峰時段」僅示例。 | - [ ] bucket 跨界 event 可完成上一 bucket 並建立下一 open bucket,沒有重複或遺失。
| 每組標題與功率 | 群組名稱、currentPowerKw/contractCapacityKw | 只對應該組,Mock B1/B2/B3 各 99 kW 不可寫死。 | - [ ] FE 只以 replacement 合併,不自行計算 time-weighted average。
| 容量/占用說明 | 該組 contract capacity/allocated power、容量條 | 若顯示比例,分母只能用該組 contractCapacity,清楚標為群組值。 | - [ ] currentPowerKw 與 averagePowerKw 不會互相覆寫或被誤標。
| Slots/Queue | occupied/reserved/available slots、既有 queue 狀態 | 沿用 SlotCalculator 與現有 Queue 語意;Slots 為供電名額,不是 kWh。 | - [ ] Socket 中斷後保留最後 currentPowerKw/open buckets 與時間並標示已停止更新;不做 polling fallback、sequence/gap recovery或背景自動重連,整頁 reload 才建立新 socket。
- [ ] 不同 building 的 series、bucket 與 cache 不會混用。
支援 1、2 或多個群組,不硬編碼群組數。Group Capacity/Slots 只隨共用 REST 更新,不訂閱 ## LOAD-DEC-005:時間狀態與資料品質分離,缺值不推估
**狀態:已確認(2026-09-03,Ken)**
### 欄位與語意
每個 Live Load WS;因此可能與左側即時數字有更新時間差。 bucket 使用兩個互不替代的狀態:
**禁止推算案場容量(LOAD-DEC-001):** 現有 contractCapacity 屬於 Charge Group,用於群組 slots;沒有 authoritative - bucketState = OPEN:bucket 的時間區間尚未結束。
- bucketState = FINALIZED:bucket 的時間區間已結束,數值不再因正常時間推進而改變。
- dataStatus = COMPLETE:截至該 bucket 可計算範圍的資料完整。
- dataStatus = PARTIAL:存在無法確認的充電功率,不能產生完整案場平均值。
- dataStatus = ERROR:bucket 查詢或計算失敗。
OPEN 不等於 PARTIAL。進行中的 bucket 可以是 bucketState = OPEN + dataStatus = COMPLETE;已結束的 bucket 也可能是 bucketState = FINALIZED + dataStatus = PARTIAL。
### Connector 功率判定
在重建 site capacity 與可加總的上游配電拓撲。因此不新增/回傳/顯示群組合計推導的 buildingContractCapacityKw、utilizationPercent、remainingCapacityKw,也不新增案場容量設定、拓撲、告警門檻或管理 API。 power timeline 的每個時間片段:
## 6. 與「需留意事項」如何配合 - Connector 的 fresh runtime/transaction fact 明確顯示沒有充電時,該 Connector 對「充電功率」的貢獻為 0,不要求額外取得 Power MeterValue。
- Connector 正在充電時,必須取得仍在 Backend effective freshness cutoff 內、unit 可正規化且數值有效的 Power.Active.Import。
- 最新有效 Power 在 cutoff 內可以沿用至下一筆讀值;正常 30 秒/15 分鐘回報間隔不會因每秒沒有新 row 而自動變成缺值。
- 正在充電但 Power 缺少、無法解析、為負值或超過 cutoff,該時間片段為 unknown。
- Connector 的充電/交易狀態本身無法確認,或離線但仍可能存在 active transaction/offline charging 時,不得假定為 0,該時間片段為 unknown。
只看**目前值的品質**,不是數歷史缺口。(LOAD-DEC-006、#1463 ATT-DEC-002/008) ### Bucket 計算規則
| 狀況 | 是否成為「即時負載資料不完整」類別 | - bucket 內所有需要的時間片段均可確認時,dataStatus = COMPLETE,Backend 回傳 time-weighted averagePowerKw。
|---|---| - 全部可確認且整段沒有充電時,averagePowerKw = 0、dataStatus = COMPLETE。
| - 只要 bucket 內存在任何超過 freshness cutoff 後仍無法確認的充電功率,averagePowerKw = null、dataStatus = PARTIAL。
- bucket 查詢或計算失敗時,averagePowerKw = null、dataStatus = ERROR。
- OPEN bucket 只針對 bucketStartAt 至 event occurredAt/rangeEndAt 的已經過期間計算,不對尚未發生的時間做 0 或預測。
- finalized replacement 必須重新套用相同規則,不得因 bucket 已結束就把 PARTIAL 改成 COMPLETE。
### V1 明確不採用
- 不對缺漏 Power 做線性插值、前後平均或模型預測。
- 不把超過 freshness cutoff 的舊讀值繼續沿用。
- 不回傳「已知 Connector 的部分合計」冒充完整案場功率。
- 不設定 80%/90% 等 coverage 通過門檻。
- 不新增 coverage percentage KPI。
- 不用 0 代表 unknown、PARTIAL 或 ERROR。
### Frontend 顯示
- bucketState = OPEN:顯示「進行中」,但不使用警告色暗示資料異常。
- dataStatus = COMPLETE 且數值為 0:折線落在 0,tooltip 顯示 0.00 kW。
- dataStatus = PARTIAL:該點顯示缺口,tooltip/輔助文字顯示「部分充電功率無法取得」。
- dataStatus = ERROR:該點顯示缺口,tooltip/輔助文字顯示「負載資料計算失敗」。
- PARTIAL/ERROR 不得以前一點連線跨過缺口,也不得以 0 繪製。
- 狀態不能只靠顏色;文字及螢幕閱讀器必須可辨識。
- 正常主 mock 保留 COMPLETE 情境;最終 fixture 另增加 FINALIZED + PARTIAL 的 null gap,不把正常 mock 全部改成故障畫面。
### Acceptance Criteria
- [ ] OPEN/FINALIZED 與 COMPLETE/PARTIAL/ERROR 可自由正確組合,不互相代替。
- [ ] fresh 且確定未充電的 Connector 可貢獻 0;正在充電卻沒有 fresh Power 時不可貢獻 0。
- [ ] 正常回報間隔內可沿用最後有效值;超過 cutoff 不得沿用。
- [ ] 任一 unknown 充電功率使 bucket 回 null + PARTIAL,不回部分合計或 coverage 百分比。
- [ ] 完整零負載回 0 + COMPLETE,與 null + PARTIAL/ERROR 有不同視覺。
- [ ] 圖表不對 null 缺口做補值或跨越連線。
- [ ] 本規則只影響 Dashboard read model,不寫回 Connector、Transaction 或 MeterValue。
## LOAD-DEC-006:目前資料品質與歷史缺口的摘要範圍
**狀態:已確認(2026-09-03,Ken)**
- current Live Load freshness = STALE 或 dataStatus = PARTIAL/ERROR | 是,一個類別、affectedCount=1。 | 時,#1463「需留意事項」建立一個即時負載資料不完整 category。
| - currentPowerKw 0+COMPLETE | 否,正常零負載。 | = 0 + COMPLETE 是可確認的正常零負載,不建立摘要。
| OPEN+COMPLETE | 否,時間仍在進行而已。 | - bucketState = OPEN 本身不是異常;OPEN + COMPLETE 不建立摘要。
| - finalized 歷史 null/PARTIAL/ERROR | 否,只在圖表保留缺口與原因。 |
目前資料恢復後,本區可由 WS 更新;上方 Attention 等下一次成功 bucket 的 null + PARTIAL/ERROR 只在圖表顯示缺口與原因,不進入「需留意事項」。
- 不按歷史缺口數量建立多筆摘要或累加頁首數量。
- current source 恢復 FRESH/COMPLETE 後,Live Load 區塊可由 WebSocket 顯示最新狀態;「需留意事項」則於下一次共用 Overview 才移除該類別,不由 WS 成功更新時移除對應 category,不由 WebSocket 直接修改 Attention。不推論共同根因、離線充電或待 reconciliation。 Attention。
- 詳細摘要顯示、fixture 與 deep link 規則見 #1463 ATT-DEC-002。
## 7. 工程交付與驗收 LOAD-DEC-007:三種負載範圍一次取得,切換不重新請求
| 負責方 | 必須完成 | **狀態:已確認(2026-09-04,Ken)**
### 使用者操作規則
- 每次開啟/重新載入 Dashboard,負載圖表預設選中「即時」。目前選擇只保存於當次頁面,不新增 URL 或使用者偏好設定。
|---|---| - `GET /api/dashboard/overview` 同一次回傳「即時」、「今日」、「7 日」三組完整歷史 series。使用者切換按鈕時,只切換 Frontend 已取得的資料,不再呼叫 API。
| - 切換範圍不建立、關閉或重建 Dashboard WebSocket;WebSocket 仍可依 `rangeCode + bucketStartAt` 更新三組 cached series 的 open/finalized bucket。
- 每 5 分鐘 Overview refresh 與手動刷新完成後,保留使用者目前選擇的範圍,不強制跳回「即時」。只有整頁重新載入才回到預設「即時」。
- 選定範圍若為 EMPTY 或 ERROR,圖表原位顯示該範圍的狀態與原因,不自動切換到另一個有資料的範圍;使用者仍可自行切換其他兩個按鈕。
- currentPowerKw 主數字與歷史圖表狀態分開:某一段歷史 series 為 EMPTY/ERROR,不代表即時主數字必須清空;主數字仍依自身 WebSocket/freshness 狀態呈現。
### 狀態語意
- COMPLETE 且數值全為 0 是可確認的零負載,仍畫出 0 kW 折線,不顯示 Empty。
- EMPTY 只能由 Backend | 新增 Dashboard 歷史唯讀 query/service、Power 單位/有效性檢查、時間線加權;三 明確回傳,表示該範圍成功完成查詢但沒有可顯示的歷史 series;Frontend 不可因陣列為空或全為 0 自行猜測。
- PARTIAL 依 LOAD-DEC-005 保留可用 points 並在未知 buckets 顯示缺口,不整張改成 Empty。
- ERROR 顯示「{範圍}負載資料暫時無法取得」;若同 building 有上次成功 series,依 DEC-032 保留舊圖並標示更新失敗/lastSuccessfulAt,不得假裝最新。
### Backend 要開發的內容
- Overview 的 Live Load history 固定回傳 LIVE/TODAY/SEVEN_DAYS 三個 range,分別帶 `rangeCode`、範圍起訖、bucket size、buckets、dataStatus/dataState、updatedAt 及必要的 lastSuccessfulAt。
- V1 不新增選定 range DTO、目前值/群組資料、WS 區間 replacement。 | request parameter、lazy-load endpoint 或每個頁籤一支 API;也不因這個互動把完整歷史 series 改由 WebSocket 傳送。
| FE | 左右區塊、三按鈕、軸/Tooltip/單位、缺口/各狀態、三份 - 三組 range 各自保留資料狀態;一組 ERROR 不得使其他正常 range 一起失敗,也不改變 currentPowerKw 的 source status。
### Frontend 要開發的內容
- 使用一份 page-local selectedRange state,初始固定為 LIVE;三個按鈕同步更新 active 樣式及 `aria-pressed`。
- 切換時從同一份 Overview cache 選取對應 series 並重畫,不能觸發 Overview refetch、range API 或 WebSocket lifecycle。
- Overview 自動/手動 refresh 只替換資料 cache,不重設 selectedRange。
- 在圖表框內呈現所選 range 的 Empty/Error,保留三個切換按鈕可操作;不偷偷選擇另一組資料。
- Mock 以 `?load=empty&load-range=today` 與 key replacement、群組數量彈性、斷線提示。 |
| QA | 時區與範圍、不同頻率樣本、邊界 finalized/open、0/null、range 失敗隔離、權限/案場隔離、無 fallback 與無作業寫入。 | `?load=error&load-range=week` 提供 review fixture;此 query parameter 不屬於正式產品 contract。
### Acceptance Criteria
- [ ] 目前值與區間平均不互相覆寫;正確 kW 與兩位小數。 每次一般載入預設為「即時」,整頁 reload 後回到「即時」。
- [ ] 三組範圍、點數上限、X/Y 軸、精確 Tooltip 正確;7 日為日曆日。 一次 Overview response 同時包含三組 series;切換任一按鈕不產生額外 HTTP request 或 WebSocket session。
- [ ] 時間加權而非 raw rows 算術平均;正常回報間隔可沿用,cutoff 後不可沿用。 五分鐘與手動刷新後保留 selectedRange;切換範圍不影響 currentPowerKw 或 socket 連線。
- [ ] 任一未知充電片段使 bucket null+PARTIAL;全零為 0+COMPLETE,不做補值、跨線或 coverage 門檻。 某一 range EMPTY/ERROR 時停留原頁籤並顯示原位狀態,其他頁籤仍可手動查看。
- [ ] OPEN 與品質分開;finalized 仍套相同品質規則。 0 + COMPLETE、EMPTY、PARTIAL 與 ERROR 有不同呈現,不以空陣列或全零自行推導 Empty。
- [ ] REST 可重建完整各 range;WS 只送目前值與 open/跨界 finalized,FE 按 key replacement、視窗上限保留資料。 同 building refresh error 的 cached series 顯示 lastSuccessfulAt;未知值不轉成 0。
- [ ] 切換不發 request/動 socket;刷新保留選擇;range Empty/Error 留在原頁,不清空正常目前值。 本決策只定義查詢與畫面切換,不修改 MeterValue、功率計算、充電、Queue、rotation 或帳務邏輯。
## V1 明確不開發
- 案場層級契約容量設定/維護頁。
- Charge Group 上游配電拓撲或可加總旗標。
- 案場契約容量利用率、剩餘容量或其告警門檻。
- 因 Dashboard 顯示而調整群組容量、slots、queue 或 rotation。
- 根據 mockup 假資料執行任何營運判斷。
若未來確實需要案場契約容量 KPI,應另案定義 authoritative site capacity、共用/獨立上游拓撲、有效期間及權限;不能恢復以群組容量直接相加。
## Acceptance Criteria
- [ ] Socket 失敗/中斷保留資料與時間且提示,不啟動備援/自動重連。 案場即時負載不顯示或回傳由 group contract capacity 加總產生的案場契約容量。
- [ ] Attention 只反映目前品質,一個類別;歷史缺口不計。 UI 不顯示案場契約容量利用率或剩餘容量。
- [ ] 1/2/多組容量與 Slots 正確,沒有案場容量合計/利用率/餘裕。 currentPowerKw、latestMeterAt 與 freshness/source status 可獨立正常呈現。
- [ ] 不修改 SlotCalculator、queue lifecycle、rotation dispatch、Transaction、MeterValue 各 Charge Group 自己的 contract capacity 與 slots 仍可閱讀,且不被標示為案場總契約容量。
- [ ] 1 個、2 個或多個 Charge Group 均不需要 FE 硬編碼。
- [ ] currentPowerKw 為 null/stale/error 時有明確狀態,不顯示假 0。
- [ ] Backend 與 FE 都不自行推導 site-level capacity、utilization 或 OCPP/Modbus 行為;production 不退回假資料。 remaining capacity。
- [ ] 本需求只有唯讀查詢與呈現,不改變既有作業邏輯。
Fixture 保留正常示例,另提供 FINALIZED+PARTIAL 缺口、全零、EMPTY/ERROR 與恢復;原 Mock 的 ?load=empty&load-range=today、?load=error&load-range=week 只供 review,不是正式 API/URL 契約。
## 8. 決策追溯與歷史查核依據 Mock/文件同步
| 決策 | 本文 | - Working mock:documents/dashboard-mockup/a17-operations-dashboard.html。
|---|---|
| LOAD-DEC-001 不推算案場容量 | 第 1、5 節 |
| LOAD-DEC-002 保留三檢視;003 軸線/計算 | 第 1~3 節 |
| LOAD-DEC-004 REST/WS replacement | 第 4 節 |
| LOAD-DEC-005 時間狀態與品質分離 | 第 2~3 節 |
| LOAD-DEC-006 摘要範圍 | 第 6 節 |
| LOAD-DEC-007 一次取得/切換行為 | 第 4.1 節 |
LOAD-DEC-001~006 於 2026-09-03、007 於 2026-09-04 已確認;後續傳輸以 #1424 DEC-031/032 為準。早期「軸線待確認」已由 003 定案,不再保留為未決;原 Mock 的 - 本次確認後立即移除案場層級 297 kW/7.4%/275.16 kW、7.4% 與 275.16 kW 案場容量資訊已撤回。
**2026-09-03 A17 唯讀基線(沿用舊票記錄,本次未重新查 DB):** B1/B2/B3 各 餘裕文案。
- 各群組自己的 99 kW、4 CP/12 Connector 且皆指派群組;297 kW 只為算術合計,不能證明上游容量。近 7 日 14,550 筆 Power.Active.Import,11 個曾回報 Connector,皆為 W;近 24h 1,342 筆,充電 Sample.Clock 約 30~34 秒、非充電 Sample.Periodic 約 900~905 秒。這是使用時間加權的既有依據,不是各案場固定頻率/數量。當時 MeterValueRepository 尚無 Dashboard 歷史負載 aggregate query。 與群組內比例保留。
- Redmine attachment #1110 保留為初始設計基準;全部需求確認後再上傳整併後的最終 HTML/PDF。
原有參考保留:[EIA:功率與能量單位](https://www.eia.gov/energyexplained/electricity/measuring-electricity.php)、[DOE:電費與區間需量說明](https://www.energy.gov/sites/default/files/2024-04/understanding-your-utility-bill.pdf)。本圖不是正式需量計費,不以外部例子增加計費需求。
## 需求管理與本次編輯 需求管理狀態
- 本票仍是已確認需求的追蹤票,不代表已完成開發或通過測試;指派、狀態、進度、附件與父子關係均維持原狀。 Parent:#1422
- 全 Related:#1424(REST/WebSocket 資料分工與事件契約)
- Requirement status:**LOAD-DEC-001~007 已確認;本區塊需求已完成確認,可在全 Dashboard 完成一致性 review 後,才依 鎖版時整併,議題暫維持 New/0%**
- Final specification:全部 Dashboard 區塊確認後整併至 #1422 DOC-DEC-001 發布同版號 PDF/Markdown/HTML 套件;現有 v0.3 Draft 不覆寫,本次不發布新版附件。 v1.0 文件
- 2026-09-14:僅重整 Description、說明與排版,將已確認決策併入對應畫面/工程工作;決策編號保留供追溯。E2E impact:No catalog change(沒有修改產品行為、公式或 API 契約);功能實作時仍須遵循主票與本票驗收。 Implementation tickets:需求鎖定後才建立 FE/BE/QA child issues
返回