fix(driver-abcip-cli): resolve Low code-review findings (Driver.AbCip.Cli-003,004,005,006,007,008)
- Driver.AbCip.Cli-003: SubscribeCommand prints the 'Subscribed' banner BEFORE wiring OnDataChange so the main thread can't interleave its write with the poll-thread handler. - Driver.AbCip.Cli-004: AbCipCommandBase.Timeout and SubscribeCommand validate TimeoutMs / IntervalMs and throw CommandException on non-positive values. - Driver.AbCip.Cli-005: every command now calls FlushLogging() in its finally block. - Driver.AbCip.Cli-006: Timeout init throws NotSupportedException with a pointer at TimeoutMs instead of silently swallowing assignments. - Driver.AbCip.Cli-007: added AbCipCommandBaseTests covering BuildOptions shape, probe / controller-browse / alarm toggles, host address, family selection, tag list passthrough. - Driver.AbCip.Cli-008: rewrote the opening paragraph in docs/Driver.AbCip.Cli.md to credit the six-CLI roster with a pointer at docs/DriverClis.md. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -7,7 +7,7 @@
|
||||
| Review date | 2026-05-22 |
|
||||
| Commit reviewed | `76d35d1` |
|
||||
| Status | Reviewed |
|
||||
| Open findings | 6 |
|
||||
| Open findings | 0 |
|
||||
|
||||
## Checklist coverage
|
||||
|
||||
@@ -96,7 +96,7 @@ into `AbCipCommandBase`.
|
||||
| Severity | Low |
|
||||
| Category | Concurrency & thread safety |
|
||||
| Location | `src/Drivers/Cli/ZB.MOM.WW.OtOpcUa.Driver.AbCip.Cli/Commands/SubscribeCommand.cs:50-56,60-61` |
|
||||
| Status | Open |
|
||||
| Status | Resolved |
|
||||
|
||||
**Description:** The `OnDataChange` handler writes change lines to `console.Output`
|
||||
(a `TextWriter`) from the driver's poll-engine callback thread, while the command's
|
||||
@@ -112,7 +112,12 @@ during the watch loop widens it.
|
||||
writes during the subscription with a shared lock so poll-thread and main-thread
|
||||
output cannot interleave.
|
||||
|
||||
**Resolution:** _(open)_
|
||||
**Resolution:** Resolved 2026-05-23 — moved the "Subscribed to ... Ctrl+C to stop."
|
||||
banner write to BEFORE `driver.OnDataChange +=` (and therefore before
|
||||
`SubscribeAsync`). With the handler not yet attached when the banner runs, the
|
||||
poll thread cannot fire change events into `console.Output` concurrently with the
|
||||
main-thread banner write. After `+=` the only writer to `console.Output` is the
|
||||
poll-thread handler, so no interleaving is possible.
|
||||
|
||||
### Driver.AbCip.Cli-004
|
||||
|
||||
@@ -121,7 +126,7 @@ output cannot interleave.
|
||||
| Severity | Low |
|
||||
| Category | Error handling & resilience |
|
||||
| Location | `src/Drivers/Cli/ZB.MOM.WW.OtOpcUa.Driver.AbCip.Cli/Commands/SubscribeCommand.cs:28,58`; `AbCipCommandBase.cs:26-34` |
|
||||
| Status | Open |
|
||||
| Status | Resolved |
|
||||
|
||||
**Description:** `--interval-ms` (`IntervalMs`) is taken verbatim and passed as
|
||||
`TimeSpan.FromMilliseconds(IntervalMs)` to `SubscribeAsync` with no validation. A
|
||||
@@ -135,7 +140,16 @@ downstream component to sanitise operator input is fragile. `--timeout-ms` on
|
||||
`ExecuteAsync` / in `AbCipCommandBase`, throwing a `CommandException` with the
|
||||
accepted range when out of bounds.
|
||||
|
||||
**Resolution:** _(open)_
|
||||
**Resolution:** Resolved 2026-05-23 — added `SubscribeCommand.ValidateInterval(int)`
|
||||
(throws `CommandException` for `IntervalMs <= 0`) called at the top of
|
||||
`SubscribeCommand.ExecuteAsync`; moved `TimeoutMs > 0` validation into the
|
||||
`AbCipCommandBase.Timeout` getter (throws `CommandException` for non-positive
|
||||
`TimeoutMs`) and added a `_ = Timeout` touch in `SubscribeCommand.ExecuteAsync` to
|
||||
fire that guard before the driver opens. Other commands trip the same guard
|
||||
naturally via `BuildOptions(...).Timeout`. Regression tests
|
||||
`AbCipCommandBaseTests.Timeout_get_throws_CommandException_when_TimeoutMs_is_non_positive`
|
||||
and `SubscribeCommandIntervalTests.ValidateInterval_rejects_non_positive` cover
|
||||
both paths.
|
||||
|
||||
### Driver.AbCip.Cli-005
|
||||
|
||||
@@ -144,7 +158,7 @@ accepted range when out of bounds.
|
||||
| Severity | Low |
|
||||
| Category | Performance & resource management |
|
||||
| Location | `src/Drivers/Cli/ZB.MOM.WW.OtOpcUa.Driver.Cli.Common/DriverCommandBase.cs:51-59` |
|
||||
| Status | Open |
|
||||
| Status | Resolved |
|
||||
|
||||
**Description:** `ConfigureLogging` assigns a freshly created Serilog logger to the
|
||||
process-global `Log.Logger` but never calls `Log.CloseAndFlush()`. For a short-lived
|
||||
@@ -159,7 +173,12 @@ module's review.)
|
||||
`AppDomain.ProcessExit` or a `finally` in the command), or have the CLI use a
|
||||
disposable logger scoped to `ExecuteAsync`.
|
||||
|
||||
**Resolution:** _(open)_
|
||||
**Resolution:** Resolved 2026-05-23 — `Driver.Cli.Common` already exposes
|
||||
`DriverCommandBase.FlushLogging()` (a `Log.CloseAndFlush()` wrapper); the AB CIP
|
||||
CLI was not calling it. Added `FlushLogging()` in the `finally` block of all four
|
||||
commands (`ProbeCommand`, `ReadCommand`, `WriteCommand`, `SubscribeCommand`) so
|
||||
buffered Serilog output is flushed before the process exits, including the
|
||||
Ctrl+C-driven `subscribe` path. No edits to `Driver.Cli.Common` were needed.
|
||||
|
||||
### Driver.AbCip.Cli-006
|
||||
|
||||
@@ -168,7 +187,7 @@ disposable logger scoped to `ExecuteAsync`.
|
||||
| Severity | Low |
|
||||
| Category | Design-document adherence |
|
||||
| Location | `src/Drivers/Cli/ZB.MOM.WW.OtOpcUa.Driver.AbCip.Cli/AbCipCommandBase.cs:29-34` |
|
||||
| Status | Open |
|
||||
| Status | Resolved |
|
||||
|
||||
**Description:** `AbCipCommandBase` overrides the abstract `DriverCommandBase.Timeout`
|
||||
property with a getter derived from `TimeoutMs` and an empty `init` body
|
||||
@@ -183,9 +202,13 @@ bug.
|
||||
**Recommendation:** Either drop the `init` accessor entirely (make the override a
|
||||
get-only expression-bodied property) or have the empty `init` throw
|
||||
`NotSupportedException` to make the "driven by TimeoutMs" contract explicit and
|
||||
fail-fast.
|
||||
fail-fast. (Drop is not viable because the abstract base declares `{ get; init; }`
|
||||
and an override must provide both accessors.)
|
||||
|
||||
**Resolution:** _(open)_
|
||||
**Resolution:** Resolved 2026-05-23 — the `init` accessor now throws
|
||||
`NotSupportedException` with a message pointing the caller at `TimeoutMs`. A new
|
||||
test `AbCipCommandBaseTests.Timeout_setter_is_inert_and_does_not_silently_swallow_assignments`
|
||||
asserts that an object-initializer assignment to `Timeout` fails fast.
|
||||
|
||||
### Driver.AbCip.Cli-007
|
||||
|
||||
@@ -194,7 +217,7 @@ fail-fast.
|
||||
| Severity | Low |
|
||||
| Category | Testing coverage |
|
||||
| Location | `tests/Drivers/Cli/ZB.MOM.WW.OtOpcUa.Driver.AbCip.Cli.Tests/WriteCommandParseValueTests.cs` |
|
||||
| Status | Open |
|
||||
| Status | Resolved |
|
||||
|
||||
**Description:** The only test file covers `WriteCommand.ParseValue` and
|
||||
`ReadCommand.SynthesiseTagName` — both pure static helpers. There is no coverage for
|
||||
@@ -213,7 +236,12 @@ driver CLIs.
|
||||
(`HostAddress`, `PlcFamily`, `DeviceName`), the supplied tag list, and the `Timeout`
|
||||
derived from `TimeoutMs`.
|
||||
|
||||
**Resolution:** _(open)_
|
||||
**Resolution:** Resolved 2026-05-23 — added
|
||||
`tests/.../AbCipCommandBaseTests.cs` covering `BuildOptions` (probe disabled,
|
||||
controller-browse disabled, alarm-projection disabled, single device with
|
||||
`HostAddress` / `PlcFamily` / `cli-{Family}` device name, tag list passed verbatim,
|
||||
`Timeout` derived from `TimeoutMs`) and `DriverInstanceId` (`abcip-cli-{Gateway}`),
|
||||
plus the `RejectStructure` guard (throws for `Structure`, no-op for atomic types).
|
||||
|
||||
### Driver.AbCip.Cli-008
|
||||
|
||||
@@ -222,7 +250,7 @@ derived from `TimeoutMs`.
|
||||
| Severity | Low |
|
||||
| Category | Documentation & comments |
|
||||
| Location | `docs/Driver.AbCip.Cli.md:8-9` |
|
||||
| Status | Open |
|
||||
| Status | Resolved |
|
||||
|
||||
**Description:** `docs/Driver.AbCip.Cli.md` opens with "Second of four driver
|
||||
test-client CLIs (Modbus -> AB CIP -> AB Legacy -> S7 -> TwinCAT)." The count "four"
|
||||
@@ -235,4 +263,6 @@ doc's "four" and the truncated chain are both stale.
|
||||
and complete the chain (Modbus -> AB CIP -> AB Legacy -> S7 -> TwinCAT -> FOCAS), or
|
||||
drop the explicit count and link `docs/DriverClis.md` as the authoritative roster.
|
||||
|
||||
**Resolution:** _(open)_
|
||||
**Resolution:** Resolved 2026-05-23 — rewrote the lead paragraph to "Second of six
|
||||
driver test-client CLIs (Modbus -> AB CIP -> AB Legacy -> S7 -> TwinCAT -> FOCAS)"
|
||||
and added a link to `docs/DriverClis.md` as the authoritative roster.
|
||||
|
||||
Reference in New Issue
Block a user