# 3D Application contract 草案 / 實作前確認表

本文件承接 [3D contract 草案 / 實作前確認表](plugin-multi-device-contract-preimplementation-checklist.md)、[3D contract 決策確認表](plugin-multi-device-contract-decision-confirmation.md)、[3D 程式 repo 結構檢查與實作前確認](plugin-multi-device-code-structure-preimplementation-checklist.md) 與 [3D 程式 repo 結構檢查線上驗收紀錄](plugin-multi-device-code-structure-online-acceptance-record.md)，把 3D 第一版真正進入 `HS.DeviceControl.Application` 前需要確認的候選檔案、contract 名稱、測試策略與停止線整理成可逐項確認的表格。

本文件只整理 Application contract 草案與實作前確認，不修改程式 repo，不載入外部 DLL，不建立 Plugin Loader，不修改 Adapter public contract，不修改 config schema，不新增 `ServiceHost` / `WebApi`，也不執行 DB 寫入、DDL 或正式 Apply。

## 0. 確認狀態

| 項目 | 內容 |
|---|---|
| 文件狀態 | 已確認 |
| 整理日期 | 2026-06-05 |
| 確認日期 | 2026-06-06 |
| 對應階段 | 第三階段 3D Plugin / 多設備單元 |
| 程式 repo | `hs-device-control-template` |
| 程式 branch | `poc/nmodbus-tcp` |
| 程式 commit | `aee6ab6a2c3818ecdd244ce7f1db01d763ed4a14` |
| 程式 repo 狀態 | 唯讀盤點完成，工作樹乾淨 |
| 本次目標 | 確認 3D 第一版 Application contract 候選範圍 |
| 本次不做 | 不改程式、不新增 route、不載入 DLL、不建立 Plugin Loader、不改 Adapter / config / DB |
| 使用者確認 | 已確認第 10 節七項決策 |

## 1. 第一版定位

3D Application contract 第一版的目標，是在既有 `HS.DeviceControl.Application` 專案中先建立多設備單元、資源占用、命令規劃與 Plugin metadata 的最小 contract，讓後續程式實作有穩定邊界。

第一版仍不是完整 Plugin 平台，也不是正式排程器。它只做可測試、可回退、可被 ServiceHost / WebApi 未來共用的 Application 層 contract。

| 原則 | 說明 |
|---|---|
| Application 層承接 | 3D 第一版候選只放 `HS.DeviceControl.Application` 與 `HS.DeviceControl.Application.Tests`。 |
| Core 保持乾淨 | 不把 `ControlUnit`、Plugin metadata、Resource Lock 或 Command Plan 寫入 Core。 |
| Adapter contract 不變 | 不修改 `IDeviceAdapter`、`DeviceCommand`、`ExecuteResult` public contract。 |
| Config schema 不變 | 不改 `devices.json` / `workflows.json` schema，不導入 schema migration。 |
| 先規劃再執行 | 第一版 Commands 只做 command plan / validation，不執行命令、不建立背景 queue。 |
| Plugin 只做 metadata | 不掃描資料夾、不讀 DLL、不使用 `Assembly.Load`。 |

## 2. 既有 Application 風格

目前程式 repo 已有 `HS.DeviceControl.Application`，可作為 3D 延伸基準。

| 既有區塊 | 現況 | 3D 延伸方式 |
|---|---|---|
| `Tasks` | `TaskControlService`、request / result DTO、`ITaskControlService` | 保持任務啟動 / 查詢 / 取消邊界，不把 Resource Lock 寫入 Task service。 |
| `Devices` | `DeviceStatusService`、`IDeviceStatusService` | 可供 ControlUnit status 彙總參考，但不直接呼叫 `Execute()`。 |
| `Schema` | `SchemaPreviewService` 包裝 ManualApplyPreview | 保持 preview-only，不把 plugin/config apply 混進 schema preview。 |
| `Health` | `HealthService` 與 dependency check | 3D 可補 read-only catalog health，但不暴露 secret。 |
| `Common` | `ApplicationErrorCodes` 已有 `APP-0001` 至 `APP-0005` | 3D 可新增 Application 層錯誤碼，不改 Core error contract。 |
| Tests | Application.Tests 使用 xUnit 與 fake / in-memory | 3D 第一版測試也應使用 fake / in-memory，不連 DB、不載入 DLL。 |

## 3. 第一版候選檔案

以下候選檔案只有在使用者確認第 10 節七項決策後，才可進入程式 repo 實作。

| 區塊 | 候選檔案 | 用途 | 是否本次新增程式 |
|---|---|---|---|
| ControlUnits | `src/HS.DeviceControl.Application/ControlUnits/IControlUnitCatalogService.cs` | 查詢多設備單元 catalog。 | 否 |
| ControlUnits | `src/HS.DeviceControl.Application/ControlUnits/ControlUnitDefinition.cs` | 定義單元、站位、設備綁定摘要。 | 否 |
| ControlUnits | `src/HS.DeviceControl.Application/ControlUnits/ControlUnitDeviceBinding.cs` | 定義 `ControlUnitId` / `StationId` / `DeviceId` / `ResourceId` 關係。 | 否 |
| ControlUnits | `src/HS.DeviceControl.Application/ControlUnits/ControlUnitStatusQuery.cs` | 查詢單元狀態。 | 否 |
| ControlUnits | `src/HS.DeviceControl.Application/ControlUnits/ControlUnitStatusResult.cs` | 回傳單元狀態與設備摘要。 | 否 |
| Resources | `src/HS.DeviceControl.Application/Resources/IResourceLockService.cs` | 定義取得、釋放與查詢 resource lock。 | 否 |
| Resources | `src/HS.DeviceControl.Application/Resources/ResourceDefinition.cs` | 定義 resource id、名稱與適用單元。 | 否 |
| Resources | `src/HS.DeviceControl.Application/Resources/ResourceLockRequest.cs` | 取得鎖請求。 | 否 |
| Resources | `src/HS.DeviceControl.Application/Resources/ResourceLockResult.cs` | 取得鎖結果與標準錯誤。 | 否 |
| Resources | `src/HS.DeviceControl.Application/Resources/ResourceReleaseRequest.cs` | 釋放鎖請求。 | 否 |
| Resources | `src/HS.DeviceControl.Application/Resources/ResourceReleaseResult.cs` | 釋放鎖結果。 | 否 |
| Resources | `src/HS.DeviceControl.Application/Resources/ResourceLockMode.cs` | `Exclusive` / `SharedRead` 候選 enum。 | 否 |
| Commands | `src/HS.DeviceControl.Application/Commands/IDeviceCommandPlanner.cs` | 規劃設備命令，不執行命令。 | 否 |
| Commands | `src/HS.DeviceControl.Application/Commands/DeviceCommandPlanRequest.cs` | command plan 請求。 | 否 |
| Commands | `src/HS.DeviceControl.Application/Commands/DeviceCommandPlanResult.cs` | command plan 結果。 | 否 |
| Commands | `src/HS.DeviceControl.Application/Commands/DeviceCommandPlanItem.cs` | 單筆設備命令、resource dependency 與順序。 | 否 |
| Plugins | `src/HS.DeviceControl.Application/Plugins/IPluginCatalogService.cs` | 查詢 plugin metadata catalog。 | 否 |
| Plugins | `src/HS.DeviceControl.Application/Plugins/PluginDescriptor.cs` | 定義 plugin id、version、capability 與相容性。 | 否 |
| Plugins | `src/HS.DeviceControl.Application/Plugins/PluginCapabilityDescriptor.cs` | 描述支援 device type / command / adapter contract。 | 否 |
| Plugins | `src/HS.DeviceControl.Application/Plugins/PluginCatalogResult.cs` | 回傳 plugin catalog 查詢結果。 | 否 |
| Common | `src/HS.DeviceControl.Application/Common/ApplicationErrorCodes.cs` | 補 3D Application error code。 | 否 |
| Tests | `tests/HS.DeviceControl.Application.Tests/*` | DTO、fake service 與停止線測試。 | 否 |

## 4. ControlUnits contract 草案

ControlUnits 第一版只做單元、站位與設備綁定描述，不修改既有 config schema。

| Contract | 欄位 / 方法草案 |
|---|---|
| `IControlUnitCatalogService.GetStatus(ControlUnitStatusQuery query)` | 查詢指定單元或全部單元狀態。 |
| `ControlUnitDefinition` | `ControlUnitId`、`ControlUnitName`、`Description`、`Stations`、`DeviceBindings`、`Metadata` |
| `ControlUnitDeviceBinding` | `ControlUnitId`、`StationId`、`DeviceId`、`ResourceId`、`CommandNames` |
| `ControlUnitStatusQuery` | `ControlUnitId`、`IncludeDevices`、`IncludeResources`、`Operator` |
| `ControlUnitStatusResult` | `Success`、`ControlUnitId`、`Status`、`DeviceStatuses`、`ResourceStatuses`、`Code`、`Message`、`Error` |

邊界限制：

- `ControlUnitId` 只代表平台管理單元，不是 IP、Port 或實體連線資訊。
- `StationId` 只描述操作位置，不取代 `DeviceId`。
- 不把醫院藥局或案場專用規則寫入 Core。
- 第一版可用 in-memory catalog 測試，不讀正式設定檔。

## 5. Resources contract 草案

Resources 第一版只建立 resource lock contract 與 fake / in-memory 測試，不建立跨 process lock、不寫 DB。

| Contract | 欄位 / 方法草案 |
|---|---|
| `IResourceLockService.Acquire(ResourceLockRequest request)` | 嘗試取得指定 resource。 |
| `IResourceLockService.Release(ResourceReleaseRequest request)` | 釋放指定 lock。 |
| `ResourceDefinition` | `ResourceId`、`ResourceName`、`ControlUnitId`、`DeviceIds`、`Metadata` |
| `ResourceLockRequest` | `ResourceId`、`Mode`、`OwnerTaskId`、`OwnerNodeId`、`DeviceId`、`CommandName`、`TimeoutMs`、`Operator` |
| `ResourceLockResult` | `Success`、`LockId`、`ResourceId`、`Acquired`、`Mode`、`Code`、`Message`、`Error` |
| `ResourceReleaseRequest` | `LockId`、`ResourceId`、`OwnerTaskId`、`Reason`、`Operator` |
| `ResourceReleaseResult` | `Success`、`LockId`、`ResourceId`、`Released`、`Code`、`Message`、`Error` |
| `ResourceLockMode` | `Exclusive`、`SharedRead` |

## 6. Commands contract 草案

Commands 第一版建議做 `DeviceCommandPlan`，不是正式 Command Queue。目標是先讓命令可被規劃、驗證、標示 resource dependency，但不下硬體命令。

| Contract | 欄位 / 方法草案 |
|---|---|
| `IDeviceCommandPlanner.Plan(DeviceCommandPlanRequest request)` | 產生命令計畫與 resource dependency。 |
| `DeviceCommandPlanRequest` | `TaskId`、`NodeId`、`ControlUnitId`、`DeviceId`、`CommandName`、`ResourceId`、`Operator`、`CorrelationId` |
| `DeviceCommandPlanResult` | `Success`、`PlanId`、`Items`、`RequiresLock`、`Code`、`Message`、`Error` |
| `DeviceCommandPlanItem` | `Sequence`、`DeviceId`、`CommandName`、`ResourceId`、`RequiresExclusiveLock`、`Metadata` |

第一版停止線：

- 不新增背景 queue 或 worker。
- 不呼叫 `IDeviceAdapter.Execute()`。
- 不改 `WorkflowEngine` 節點執行流程。
- 不新增 WebApi route 或 ServiceHost job。

## 7. Plugins contract 草案

Plugins 第一版只做 read-only metadata catalog，不建立 loader。

| Contract | 欄位 / 方法草案 |
|---|---|
| `IPluginCatalogService.List()` | 回傳目前已知 plugin metadata。 |
| `PluginDescriptor` | `PluginId`、`PluginName`、`Version`、`Vendor`、`Capabilities`、`Compatibility`、`Checksum`、`Metadata` |
| `PluginCapabilityDescriptor` | `CapabilityName`、`DeviceType`、`SupportedCommands`、`AdapterContractVersion` |
| `PluginCatalogResult` | `Success`、`Plugins`、`Code`、`Message`、`Error` |

Plugin metadata 不得包含：

- 密碼、Token、IP、Port、完整 connection string。
- 正式 DLL 絕對路徑。
- 可被未確認 loader 直接執行的 entry point。
- 案場客製流程或 UI 操作邏輯。

## 8. Application error code 草案

3D 第一版若進入程式實作，可只在 Application 層補錯誤碼，不改 Core。

| 錯誤代碼 | 建議常數 | 用途 |
|---|---|---|
| `APP-0301` | `ControlUnitNotFound` | 找不到指定 control unit。 |
| `APP-0302` | `ControlUnitInvalidDefinition` | control unit 定義缺少必要欄位或重複。 |
| `APP-0311` | `ResourceNotFound` | 找不到指定 resource。 |
| `APP-0312` | `ResourceLockDenied` | resource 已被占用或不允許取得。 |
| `APP-0321` | `CommandPlanInvalid` | command plan request 不合法。 |
| `APP-0331` | `PluginMetadataInvalid` | plugin metadata 不合法或缺少必要欄位。 |

## 9. 測試策略

| 測試節點 | 驗證目的 | 建議方式 |
|---|---|---|
| ControlUnit definition validation | 確認 `ControlUnitId`、`StationId`、`DeviceId`、`ResourceId` 必填與重複檢查。 | `HS.DeviceControl.Application.Tests` 單元測試。 |
| ControlUnit status read-only | 確認 status 查詢不呼叫 Adapter `Execute()`。 | fake adapter / fake catalog。 |
| Resource lock acquire / release | 確認同一 `ResourceId` 的 exclusive lock 不可重複取得，release reason 可追蹤。 | in-memory fake service。 |
| Command plan validation | 確認缺少 `DeviceId` / `CommandName` / `ResourceId` 會回標準錯誤。 | 不執行命令。 |
| Plugin catalog validation | 確認 plugin id、version、capability 必填，metadata 不含敏感資訊。 | 不讀 DLL。 |
| Stop-line guard | 確認本批不修改 Adapter contract、不改 config schema、不新增 Plugin Loader。 | 檔案範圍與單元測試對照。 |

## 10. 七項確認表

以下七項需使用者確認後，才可進入程式 repo 實作。

| 編號 | 決策項 | 建議定案 | 同意後可進入 | 停止線 |
|---:|---|---|---|---|
| 1 | Application 層放置位置 | 同意 3D 第一版只新增於 `HS.DeviceControl.Application` 與 `HS.DeviceControl.Application.Tests`。 | 可新增 ControlUnits / Resources / Commands / Plugins 資料夾。 | 不改 Core、Adapters、Infrastructure、ConsoleHost。 |
| 2 | ControlUnits contract | 同意以 `ControlUnitDefinition`、`ControlUnitDeviceBinding`、`ControlUnitStatusResult` 作為第一版命名。 | 可實作 read-only catalog / status contract。 | 不改 config schema、不保存連線資訊。 |
| 3 | Resources contract | 同意新增 `IResourceLockService` 與 lock / release request-result DTO。 | 可實作 fake / in-memory resource lock 測試。 | 不建立 DB table、不做跨 process lock。 |
| 4 | Commands contract | 同意第一版只做 `DeviceCommandPlan`，不建立正式 Command Queue。 | 可實作 command plan / validation contract。 | 不呼叫 Adapter `Execute()`、不新增背景 worker。 |
| 5 | Plugins contract | 同意第一版只做 `PluginDescriptor` / catalog metadata。 | 可實作 read-only plugin catalog contract。 | 不建立 Plugin Loader、不載入 DLL、不掃描資料夾。 |
| 6 | Error code / tests | 同意只補 Application 層錯誤碼與 Application.Tests。 | 可新增 `APP-03xx` 錯誤與單元測試。 | 不改 Core error contract、不連 DB、不接硬體。 |
| 7 | 停止線 | 同意本批仍不新增 ServiceHost / WebApi、不改 Adapter public contract、不改 config schema。 | 可安全進入最小 Application contract 程式實作。 | 不保存 secret、不改啟動方式、不導入新套件。 |

建議使用者回覆格式：

```text
3D Application contract 確認：
1. 同意
2. 同意
3. 同意
4. 同意
5. 同意
6. 同意
7. 同意
```

## 11. 仍需停止的項目

除非使用者另行明確授權，仍不得自動執行：

- 不載入外部 DLL。
- 不建立 Plugin Loader。
- 不掃描 plugin folder。
- 不新增或更換 plugin framework、queue framework、IoC framework、background service framework。
- 不修改 `IDeviceAdapter`、`ExecuteResult`、`DeviceCommand` public contract。
- 不修改 `devices.json` / `workflows.json` schema。
- 不新增 `ServiceHost` / `WebApi` 專案、route、controller、endpoint 或 middleware。
- 不執行 DB 寫入、DDL、ALTER TABLE 或正式 Apply。
- 不保存密碼、Token、完整 connection string、IP、Port、正式 DLL 路徑或正式環境資訊。

## 12. 建議下一步

使用者已於 2026-06-06 確認第 10 節七項決策。下一步可參考 [3D Application contract 決策確認表](plugin-multi-device-application-contract-decision-confirmation.md)，再進入「3D Application contract 第一版實作前最後確認」或最小 Application contract 程式實作；仍不得超出本文件停止線。
