Feature #1381
進行中[BE] Branch Engineer Command API 與 OCPP/Modbus 控制
100%
概述
背景¶
Branch Admin 需要提供 Engineer Role 專用 Backend API,讓工程師對指定 Charge Point / Connector 發送受控 Modbus command 與完整 OCPP 1.6 CSMS command。主要使用情境為 Fortune(華城)充電樁測試。
完整需求與實作規劃請見附件 branch-admin-engineer-command-plan.md。
Backend Scope¶
- 新增
engineerRole,並將既有ADMIN、power_user帳號加入該 Role。 - 建立 Engineer command catalog、capability 判定及後端 payload validation。
- 建立 OCPP Engineer Command API,涵蓋全部 19 個 OCPP 1.6 CSMS -> Charge Point commands。
- HTTP request 內完成 command log 建立與 WebSocket dispatch;成功後回 HTTP 202 +
SENT,不等待 CALLRESULT/CALLERROR。 - 以 OCPP uniqueId 非同步關聯 CALLRESULT/CALLERROR;補上 Diagnostics/Firmware status timeline。
- 建立
e_engineer_command_log、event timeline 與 capability persistence,保留 365 天。 - 建立 CloudLink 受控 Modbus command path,只允許 Plan 內 12 個 operation。
-
SET_SUPPLY_STATE必須刻意使用既有controlTestMode(..., 1/0)。 - 維持既有 8 個
/api/ocppAPI 與/api/fev/connector/availabilitycontract 相容。 - 補齊 OpenAPI、unit/integration tests、migration 及 AI/技術文件。
協作票¶
FE UI:#1380,由前端依同一份 Plan 實作 React Branch Admin「工程師工具」頁面。
檔案
是由 陳國瑋 於 20 天 前更新
Backend implementation 已完成並推送 feature branch:feature/redmine-1381-engineer-command-backend
Commits:
- EMS root 16730ac feat(#1381): 實作工程師 OCPP 與稽核 API
- EMS root 6c636af docs: 更新 Redmine Agent 自行實作規範
- charge_point ffe8754 feat(#1381): 新增受控 Modbus 工程師指令
完成項目:
- Engineer Role、SS3A function/API mappings,明確授權 ADMIN 與 power_user member。
- Engineer catalog / OCPP dispatch / controlled Modbus dispatch / search / detail API。
- 全部 19 個 OCPP 1.6 CSMS→CP command 與官方 request schema validation。
- CALLRESULT/CALLERROR generic observer、GetDiagnostics/UpdateFirmware status timeline。
- e_engineer_command_log、e_engineer_command_event、e_ocpp_command_capability migration 與 365 天 retention。
- CloudLink charge_point 12 個具名 Modbus operation 雙層 allowlist;SET_SUPPLY_STATE 刻意使用 controlTestMode()。
- 舊 generic DataTransfer 禁止 EngineerModbusCommand,避免繞過 Engineer authorization。
- AI 文件、FE handoff 與 Obsidian 知識已同步。
測試:
- Engineer Branch targeted tests:全部通過(19 actions、schema、async observer、send failure audit、DataTransfer authorization bypass)。
- charge_point targeted tests:全部通過(allowlist、controlTestMode 且不呼叫 controlRelay)。
- ems_branch full suite:101 tests,100 passed;唯一既有失敗為 BillHistoricalTariffE2ETest 缺少 2025-05 測試資料,與 #1381 無關。
- charge_point full suite 仍有既有 test profile placeholder ocpp.client.charge-point-id 無法解析;本次 targeted test 與 compile 通過。
下一個 gate:請 Ken 部署兩個 feature branch 並套用 patch_20260714_redmine_1381_engineer_command.sql,之後執行 Fortune 19 OCPP commands 與 CloudLink 12 Modbus operations 實機 E2E。Issue 暫不 resolve。
是由 陳國瑋 於 20 天 前更新
依 Ken code review 意見補齊詳盡程式碼註解並推送:
補充範圍:
- Engineer Command 同步 dispatch/非同步 completion 契約。
- audit 狀態機、append-only event timeline 與快速回應競態。
- 固定 catalog、schema/capability 差異與雙層 allowlist 安全邊界。
- SS3A Engineer Role 不繼承 administrator/power_user 的原因。
- Firmware/Diagnostics status notification 配對限制。
- 原始與 masked payload 的使用界線。
- charge_point 的穩定錯誤格式、correlation tracing,以及 SET_SUPPLY_STATE 必須使用 controlTestMode() 的硬體原因。
- migration 重要欄位與 member-level role mapping 說明。
註解規範已加入 REDMINE_CODING_SOP、ai/README 及 Obsidian EMS inbox,後續實作會固定檢查。
驗證:ems_branch compile + Engineer targeted tests 通過;charge_point Engineer Modbus targeted tests 通過。
是由 陳國瑋 於 20 天 前更新
依 Ken 指示已整理 feature branch commit history,並以 force-with-lease 更新遠端:
- EMS #1381 commits 已 squash 為:16a8702 feat(#1381): 實作工程師 OCPP 與稽核 API
- charge_point #1381 commits 已 squash 為:ad6d988 feat(#1381): 新增受控 Modbus 工程師指令
EMS 中與 #1381 無關的 Update commit(settings / AGENTS / CLAUDE / deployment 文件)保留為獨立 commit 617b50e,未併入 #1381。
已用 tree diff 確認 squash 前後檔案內容完全一致;charge_point 尚未提交的 ConnectorType.java 修改仍保留且未納入 commit。
是由 陳國瑋 於 20 天 前更新
依 Ken 第二次 code review 意見,註解標準已提升並完成全範圍補充:
- Engineer API 的每個 DTO record component 均加入 OpenAPI @Schema(description=...) 欄位說明。
- 本次新增的 public/private helper、JPA lifecycle/getter、repository contract、OCPP handler、scheduler 與 test method 均補上 method-level 用途註解。
- 跨服務 dispatch、狀態機、payload masking、status 配對、雙層 allowlist 與 controlTestMode 硬體限制保留更完整的設計理由。
- REDMINE_CODING_SOP、ai/README 與 Obsidian EMS inbox 已明定「每個 method、每個 DTO 欄位都要有註解」。
依前次要求未增加零碎 commit,直接 amend 單一 #1381 commit 並 force-with-lease 更新:
驗證:ems_branch compile 與 Engineer targeted tests 通過;charge_point Engineer Modbus targeted tests 通過。未提交的 ConnectorType.java 修改仍未納入 commit。
是由 陳國瑋 於 20 天 前更新
依 code review 補強 Engineer Modbus 跨服務契約一致性:
- Branch 與 charge_point 新增 wire contractVersion=1。
- Branch 的 EngineerModbusCommand DataTransfer 內層 JSON 必須帶 contractVersion。
- edge 在 Connector 查詢及任何 Modbus I/O 前驗證版本;缺漏或不支援時回 UNSUPPORTED_CONTRACT_VERSION。
- 兩端仍各自維護 12 個 operation allowlist 與參數驗證,不因版本相同而放寬 defense-in-depth。
- 雙邊新增 V1 operation 集合鎖定、版本傳送、版本缺漏/不符拒絕測試。
- 設計文件同步校正 vendorId 為 com.sylksoft.ems,並補充升版規則。
驗證:
- ems_branch targeted tests passed
- charge_point OcppDataTransferHandlerEngineerCommandTest passed
- ems_branch / charge_point compile passed
目前變更尚未 commit,待後續依既有單一 commit 慣例 squash。
是由 陳國瑋 於 20 天 前更新
依 Ken 指示調整 SS3A database constraint 設計:
- patch 移除 e_engineer_command_log 的 correlation_id / ocpp_unique_id UNIQUE KEY,改為普通 index。
- patch 移除 e_engineer_command_event 的 (command_log_id, sequence_no) UNIQUE KEY,改為普通 index。
- patch 移除 event -> command_log FOREIGN KEY 與 ON DELETE CASCADE。
- JPA @Table 同步移除 @UniqueConstraint,避免 Hibernate 再建立限制。
- application layer 寫入前檢查 correlationId / ocppUniqueId 重複。
- operation 更新使用 pessimistic write lock,event sequence 由程式產生並再次檢查。
- 365 天 retention 改為每批 500 筆,先刪 event 再刪 command log,避免 orphan。
- PRIMARY KEY 與既有 NOT NULL 保留;普通 index 僅負責查詢效能。
- REDMINE_CODING_SOP、AI knowledge 與 Engineer Command Plan 已補上此 SS3A 慣例。
驗證:EngineerCommandAuditServiceTest 等 targeted tests passed;ems_branch compile passed。
目前變更尚未 commit,待依既有單一 commit 慣例 squash。
是由 陳國瑋 於 18 天 前更新
- 狀態 從 In Progress 變更為 Resolved
- 完成百分比 從 0 變更為 100
Backend #1381 已完成開發、測試、Jimmy 部署與文件更新,依 Ken 確認將本票標記為 Resolved。
最終交付¶
- Engineer Role、4 functions、5 API mappings;ADMIN 與 power_user member 已加入 Engineer Role。
- 19 個 OCPP 1.6 CSMS commands、JSON Schema validation 與 capability。
- 12 個 CloudLink 受控 Modbus operations;SET_SUPPLY_STATE 依硬體設計使用 controlTestMode()。
- command audit、event timeline、CALLRESULT/CALLERROR observer、Diagnostics/Firmware status tracking、365 天 retention。
- DB table 不使用 UNIQUE/FK/ON DELETE CASCADE,資料一致性由 application layer 控制。
- 時間欄位改為 DATETIME(6),並修正快速完成 operation 可能出現負 elapsedMs 的問題。
版本¶
- Jimmy 部署 runtime source:EMS Branch 25058e5;charge_point bb0a2b2。
- EMS repo 最終收斂 commit(包含部署/E2E 文件):8411ee0。
- Feature branch:feature/redmine-1381-engineer-command-backend。
- 未建立 PR/MR、未 merge。
驗證¶
- ems_branch:107 tests,0 failures/errors。
- charge_point:16 tests,0 failures/errors(MySQL Testcontainer)。
- ems_hq:4 tests,0 failures/errors。
- Jimmy real E2E:HTTP -> Branch -> OCPP -> CP001 -> Modbus -> audit 完成。
- OCPP TriggerMessage 的 Accepted/Rejected、search/detail、未登入 401、raw Modbus 400 均通過。
- 正式報告:test_report/20260714_001_redmine_1381_engineer_command_e2e.md。
已知限制¶
Fortune(華城)Charge Point 在 2026-07-14 測試時離線,因此尚未完成 Fortune 19 commands 實機 capability matrix;目前維持 UNVERIFIED,待設備恢復後另做回歸,不影響 Backend API 與 Jimmy CloudLink 路徑驗收。
FE 串接說明已更新至協作票 #1380。
是由 陳國瑋 於 18 天 前更新
#1381 已於 2026-07-16 部署至 A17,ticket 維持 Resolved。
部署內容:
- EMS Branch Engineer API / audit / SS3A Role migration。
- 3 個 CloudLink charge_point instances:CP001、CP002、CP003。
- 新增 3 tables、Engineer Role、4 functions、5 API mappings、ADMIN/power_user member mapping、19 筆 FE-EVI UNVERIFIED capability。
- 部署前 Branch 與 edge active transaction 均為 0。
- 完整 DB 與 4 份原 jar 已備份並驗證。
自動化測試:
- ems_branch 107 tests,0 failures/errors。
- charge_point 16 tests,0 failures/errors(MySQL Testcontainer)。
A17 E2E:
- ADMIN login / Engineer authorization:PASS。
- Catalog:31 commands(19 OCPP + 12 Modbus):PASS。
- CP001 READ_SUPPLY_STATE:COMPLETED / Accepted,elapsedMs=1190。
- Fortune CBDAX50A-25L-A6-A072 TriggerMessage(Heartbeat):COMPLETED / Accepted,elapsedMs=267。
- search/detail:PASS。
- 無 JWT:401。
- RAW_REGISTER_READ:400。
- negative elapsed count:0。
- Branch、CP001、CP002、CP003、Fortune 均已恢復 OCPP heartbeat。
既有環境狀態:
- CP002-001 在部署前即為 FAULTED / INTERNALERROR,部署後維持相同狀態;其餘 connectors 狀態正常。
- CP002 因該故障 meter 的 Modbus retry,完整 recovery / OCPP reconnect 約需 95 秒;container Up 不代表 recovery 已完成。
Capability 決策:
A17 Fortune 的單一 TriggerMessage 成功只驗證基本 OCPP 通訊,不代表 19 commands 全部已驗證,因此 FE-EVI capability 仍維持 UNVERIFIED,待完整實機矩陣逐項收斂。