fix(driver-focas-cli): resolve Low code-review findings (Driver.FOCAS.Cli-001,002,003,004; -005 deferred)
- Driver.FOCAS.Cli-001: WriteCommand.ParseValue now wraps numeric FormatException / OverflowException as CliFx CommandException with the offending value. - Driver.FOCAS.Cli-002: SubscribeCommand's OnDataChange handler and the banner both take a writeLock so notification-callback and main-thread writes can't interleave; handler exceptions are warn-and-swallow. - Driver.FOCAS.Cli-003: FocasCommandBase.ValidateOptions rejects --cnc-port outside 1..65535, non-positive --timeout-ms, and non-positive --interval-ms; ExecuteAsync calls it first. - Driver.FOCAS.Cli-004: 'await using var driver' is the sole driver disposal path; dropped the redundant explicit await ShutdownAsync. - Driver.FOCAS.Cli-005 (Deferred): the fix lives in Driver.Cli.Common.SnapshotFormatter — explicitly naming the status-code shortlist there benefits every driver CLI. Left as a Driver.Cli.Common follow-up. - Registered the new tests/Drivers/Cli/ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Cli.Tests project in ZB.MOM.WW.OtOpcUa.slnx. 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 | 5 |
|
||||
| Open findings | 0 |
|
||||
|
||||
## Checklist coverage
|
||||
|
||||
@@ -45,7 +45,7 @@ a category produced nothing rather than leaving it blank.
|
||||
| Severity | Low |
|
||||
| Category | Error handling & resilience |
|
||||
| Location | `Commands/WriteCommand.cs:58-68` |
|
||||
| Status | Open |
|
||||
| Status | Resolved |
|
||||
|
||||
**Description:** `WriteCommand.ParseValue` parses the numeric `--value` types
|
||||
(`Byte`/`Int16`/`Int32`/`Float32`/`Float64`) with `sbyte.Parse` / `short.Parse`
|
||||
@@ -65,7 +65,16 @@ literal — consistent with how `ParseBool` already handles bad boolean input.
|
||||
The same pattern exists in the sibling S7 CLI; a shared helper in
|
||||
`Driver.Cli.Common` would fix both.
|
||||
|
||||
**Resolution:** _(open)_
|
||||
**Resolution:** Resolved 2026-05-23 — wrapped the `ParseValue` numeric switch in
|
||||
`try/catch (FormatException)` and `try/catch (OverflowException)` that rethrow as
|
||||
`CliFx.Exceptions.CommandException` with a message naming the `--type` and the
|
||||
offending value, mirroring the friendly text the `Bit` path already produced.
|
||||
Added `WriteCommandParseValueTests` with [Theory] cases covering non-numeric
|
||||
input across `Byte`/`Int16`/`Int32`/`Float32`/`Float64`, overflow edges
|
||||
(sbyte ±1, short max+1, > int.MaxValue), and an assertion that the exception
|
||||
message names both the type and the offending value. A shared `Driver.Cli.Common`
|
||||
helper is the cleaner long-term fix (cross-CLI duplication remains) but is left
|
||||
to the Driver.Cli.Common review per this module's edit scope.
|
||||
|
||||
### Driver.FOCAS.Cli-002
|
||||
|
||||
@@ -74,7 +83,7 @@ The same pattern exists in the sibling S7 CLI; a shared helper in
|
||||
| Severity | Low |
|
||||
| Category | Concurrency & thread safety |
|
||||
| Location | `Commands/SubscribeCommand.cs:45-51` |
|
||||
| Status | Open |
|
||||
| Status | Resolved |
|
||||
|
||||
**Description:** The `subscribe` command attaches an `OnDataChange` handler that
|
||||
calls the synchronous `console.Output.WriteLine`. `OnDataChange` is raised from
|
||||
@@ -93,7 +102,15 @@ console writes with a lock shared between the banner and the handler. Optionally
|
||||
detach the handler in the `finally` block before `ShutdownAsync` for symmetry
|
||||
with the `handle` teardown already present there.
|
||||
|
||||
**Resolution:** _(open)_
|
||||
**Resolution:** Resolved 2026-05-23 — introduced a `writeLock` shared between the
|
||||
`OnDataChange` handler and the banner write so the poll-engine background thread
|
||||
and the CliFx invocation thread can't interleave partial lines. Added an
|
||||
explanatory comment above the handler explaining the CliFx-`IConsole` rationale
|
||||
and the synchronous-on-background-thread design — mirroring the Modbus / S7
|
||||
copies of this command. Also added a try/catch around the handler body so a
|
||||
transient stdout error cannot tear down the poll loop, and Serilog-warn-logs the
|
||||
swallowed exception. Added `SubscribeCommandConsoleHandlerTests` to guard the
|
||||
`writeLock` + CliFx-`IConsole` rationale against future copy-paste regressions.
|
||||
|
||||
### Driver.FOCAS.Cli-003
|
||||
|
||||
@@ -102,7 +119,7 @@ with the `handle` teardown already present there.
|
||||
| Severity | Low |
|
||||
| Category | Error handling & resilience |
|
||||
| Location | `FocasCommandBase.cs:19` (`CncPort`), `FocasCommandBase.cs:27` (`TimeoutMs`), `Commands/SubscribeCommand.cs:23` (`IntervalMs`) |
|
||||
| Status | Open |
|
||||
| Status | Resolved |
|
||||
|
||||
**Description:** The numeric command options `--cnc-port`, `--timeout-ms`, and
|
||||
`--interval-ms` are accepted without range validation. A zero or negative
|
||||
@@ -120,7 +137,17 @@ timeout and interval strictly positive. The same gap exists across the sibling
|
||||
driver CLIs, so a shared validation helper in `Driver.Cli.Common` is the
|
||||
cleaner fix.
|
||||
|
||||
**Resolution:** _(open)_
|
||||
**Resolution:** Resolved 2026-05-23 — added a protected `ValidateOptions(int?
|
||||
intervalMs = null)` helper on `FocasCommandBase` that rejects `--cnc-port`
|
||||
outside `1..65535`, non-positive `--timeout-ms`, and non-positive
|
||||
`--interval-ms` (when the caller passes one) with a `CliFx.Exceptions.CommandException`
|
||||
naming the option and the rejected value. `ProbeCommand` / `ReadCommand` /
|
||||
`WriteCommand` call `ValidateOptions()` without an interval, `SubscribeCommand`
|
||||
calls `ValidateOptions(IntervalMs)`. Added `FocasCommandBaseValidationTests`
|
||||
covering accept-defaults, reject out-of-range port (0, -1, 65536), reject
|
||||
non-positive timeout / interval, and skip-interval-when-omitted. A shared
|
||||
helper in `Driver.Cli.Common` is the cleaner cross-CLI fix and is recorded
|
||||
against that module's review.
|
||||
|
||||
### Driver.FOCAS.Cli-004
|
||||
|
||||
@@ -129,7 +156,7 @@ cleaner fix.
|
||||
| Severity | Low |
|
||||
| Category | Performance & resource management |
|
||||
| Location | `Commands/ProbeCommand.cs:37,54`; `Commands/ReadCommand.cs:37,46`; `Commands/WriteCommand.cs:45,54`; `Commands/SubscribeCommand.cs:39,73` |
|
||||
| Status | Open |
|
||||
| Status | Resolved |
|
||||
|
||||
**Description:** Every command declares `await using var driver = new FocasDriver(...)`
|
||||
**and** explicitly calls `await driver.ShutdownAsync(CancellationToken.None)` in
|
||||
@@ -144,7 +171,14 @@ dead weight and obscures intent: a reader cannot tell whether the explicit
|
||||
and rely on `await using` for disposal, or drop `await using` and keep the
|
||||
explicit teardown — but not both. The same redundancy exists in the sibling CLIs.
|
||||
|
||||
**Resolution:** _(open)_
|
||||
**Resolution:** Resolved 2026-05-23 — dropped the explicit
|
||||
`await driver.ShutdownAsync(CancellationToken.None)` calls from the `finally`
|
||||
blocks of `ProbeCommand`, `ReadCommand`, `WriteCommand`, and `SubscribeCommand`;
|
||||
`await using` is now the sole driver-disposal mechanism per command
|
||||
(`FocasDriver.DisposeAsync` itself runs `ShutdownAsync`). The subscribe command
|
||||
keeps `UnsubscribeAsync` in its finally because that is a subscription-lifecycle
|
||||
concern, not driver disposal. Added `CommandDisposalConventionsTests` to guard
|
||||
the source-level convention against regression.
|
||||
|
||||
### Driver.FOCAS.Cli-005
|
||||
|
||||
@@ -153,7 +187,7 @@ explicit teardown — but not both. The same redundancy exists in the sibling CL
|
||||
| Severity | Low |
|
||||
| Category | Design-document adherence |
|
||||
| Location | `Commands/WriteCommand.cs:50`, `Commands/ProbeCommand.cs:50` (via `SnapshotFormatter.FormatStatus`) |
|
||||
| Status | Open |
|
||||
| Status | Deferred |
|
||||
|
||||
**Description:** `docs/Driver.FOCAS.Cli.md` documents `BadDeviceFailure` and
|
||||
`BadCommunicationError` as the key diagnostic signals an operator reads off
|
||||
@@ -180,4 +214,14 @@ actually emit — at minimum `BadNotWritable`, `BadOutOfRange`, `BadNotSupported
|
||||
because the gap defeats this module documented `probe`/`write` diagnostic
|
||||
workflow; cross-reference the `Driver.Cli.Common` review.
|
||||
|
||||
**Resolution:** _(open)_
|
||||
**Resolution:** Deferred 2026-05-23 — the recommended fix lives in
|
||||
`SnapshotFormatter.FormatStatus` inside the `Driver.Cli.Common` shared module,
|
||||
which is outside this module's edit scope. Driver.Cli.Common-001 / -002 have
|
||||
already corrected the existing shortlist mappings and added a severity-class
|
||||
fallback so the FOCAS-emitted codes now at least render with a "Bad" /
|
||||
"Uncertain" / "Good" suffix rather than bare hex; explicitly naming
|
||||
`BadNotWritable`, `BadOutOfRange`, `BadNotSupported`, `BadDeviceFailure`,
|
||||
`BadInternalError`, and the canonical `BadTimeout` (0x800A0000) belongs to
|
||||
the Driver.Cli.Common review's follow-up (and benefits every driver CLI, not
|
||||
just FOCAS). Re-open here only if Driver.Cli.Common declines to extend the
|
||||
shortlist.
|
||||
|
||||
Reference in New Issue
Block a user