diff --git a/docs/plans/2026-10-07-native-ab-transport.md.tasks.json b/docs/plans/2026-10-07-native-ab-transport.md.tasks.json index 11d2e909..6b137026 100644 --- a/docs/plans/2026-10-07-native-ab-transport.md.tasks.json +++ b/docs/plans/2026-10-07-native-ab-transport.md.tasks.json @@ -175,7 +175,7 @@ { "id": 19, "subject": "Task 19: AdminUI `Transport` dropdown (AbCip + AbLegacy) + d", - "status": "pending", + "status": "completed", "description": "**Goal:** An operator can pick the transport per driver in the `/raw` driver-config modal, the value round-trips as a string, and a live docker-dev run proves it reaches the driver.\n\n**Files:**\n- Modify: `src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Components/Shared/Drivers/Forms/AbCipDriverForm.razor` (`FormModel.Transport`, `ToOptions`/`FromOptions`, an `InputSelect` in the advanced section)\n- Modify: `src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Components/Shared/Drivers/Forms/AbLegacyDriverForm.razor` (same)\n- Modify: `tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/AbLegacyDriverPageFormSerializationTests.cs` (add Transport round-trip cases)\n- Create or modify: `tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/AbCipDriverPageFormSerializationTests.cs` (exists \u21d2 add cases; absent \u21d2 create, modelled on the AbLegacy file)\n\n**Acceptance Criteria:**\n- [ ] The dropdown has options \"libplctag (default)\" \u21d2 `Libplctag` and \"Native (managed)\" \u21d2 `Native`, bound with `@bind-Value:after=\"EmitAsync\"` the way the other fields are.\n- [ ] The serialized JSON contains `\"transport\":\"Native\"` (a string, not a number). It goes through the existing `_jsonOpts` with `JsonStringEnumConverter` (`AbCipDriverForm.razor:108-113`); verify the AbLegacy form has the same converter and add it if missing.\n- [ ] Loading a config without `transport` shows \"libplctag (default)\" and re-emits **without** a `transport` key, or with `\"Libplctag\"`; either is fine, but pick one and test it.\n- [ ] **Live `/run` on docker-dev** (login is disabled; drive it yourself with the Chrome tools against `http://localhost:9200`):\n 1. Bring up the AbCip `controllogix` fixture on `10.100.0.35`.\n 2. In `/raw`, create or edit an AbCip driver with a device `ab://10.100.0.35:44818/1,0`, set Transport = Native, and add the raw tag `TestDINT`.\n 3. Deploy.\n 4. Read the UNS/raw node with the Client CLI (`dotnet run --project src/Client/ZB.MOM.WW.OtOpcUa.Client.CLI -- read -u opc.tcp://localhost:4840 -n \"ns=;s=\"`) and get `Good`.\n 5. Confirm from the central node's logs that the native runtime served it. Add one `LogDebug(\"AbCip transport={Transport}\")` at driver init if no log line distinguishes the two.\n 6. Flip back to libplctag, redeploy, and read `Good` again.\n 7. Record the output under the gate-results doc's `## AdminUI /run (T19)` heading only.\n- [ ] **Inverted-gate check** (memory: \"an unchanged observable proves nothing\"): with Transport = Native, point the device at a port where no `ab_server` is listening. The read must go **Bad** and the log must show the *native* error text. That proves the switch changes behaviour and isn't inert.\n\n**Verify:** AdminUI unit tests pass, plus the docker-dev `/run` evidence (CLI output for both transports and the inverted case) is pasted into `docs/plans/2026-10-07-native-ab-transport-gate-results.md`.\n\nSteps + code: docs/plans/2026-10-07-native-ab-transport.md \u00a7 Task 19.\n\n```json:metadata\n{\"files\": [\"src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Components/Shared/Drivers/Forms/AbCipDriverForm.razor\", \"src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Components/Shared/Drivers/Forms/AbLegacyDriverForm.razor\", \"tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/AbCipDriverPageFormSerializationTests.cs\", \"tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/AbLegacyDriverPageFormSerializationTests.cs\"], \"verifyCommand\": \"\", \"modelTier\": \"standard\", \"model\": \"sonnet\", \"acceptanceCriteriaCount\": 5}\n```", "blockedBy": [ 17