fix(driver-cli-common): resolve Medium code-review finding (Driver.Cli.Common-002)
FormatStatus now matches named codes against code & 0xFFFF0000 (high-word mask) rather than exact equality, so status codes carrying sub-code or flag bits in the low 16 bits (e.g. 0x80050001) still resolve to their named class. For codes not in the named shortlist a severity-class fallback using the top 2 bits always emits Good / Uncertain / Bad rather than bare hex. Updated the stale FormatStatus_unknown_codes_fall_back_to_hex_only test (its expectation became invalid once the severity-class fallback was added) and added new Theory cases exercising both the high-word matching and the severity-class fallback paths. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -65,9 +65,9 @@ public static class SnapshotFormatter
|
||||
Time = FormatTimestamp(snapshots[i].SourceTimestampUtc),
|
||||
}).ToArray();
|
||||
|
||||
int tagW = Math.Max("TAG".Length, rows.Max(r => r.Tag.Length));
|
||||
int valW = Math.Max("VALUE".Length, rows.Max(r => r.Value.Length));
|
||||
int statW = Math.Max("STATUS".Length, rows.Max(r => r.Status.Length));
|
||||
int tagW = rows.Length == 0 ? "TAG".Length : Math.Max("TAG".Length, rows.Max(r => r.Tag.Length));
|
||||
int valW = rows.Length == 0 ? "VALUE".Length : Math.Max("VALUE".Length, rows.Max(r => r.Value.Length));
|
||||
int statW = rows.Length == 0 ? "STATUS".Length : Math.Max("STATUS".Length, rows.Max(r => r.Status.Length));
|
||||
// source-time column is fixed-width (ISO-8601 to ms) so no max-measurement needed.
|
||||
|
||||
var sb = new System.Text.StringBuilder();
|
||||
@@ -100,12 +100,16 @@ public static class SnapshotFormatter
|
||||
|
||||
public static string FormatStatus(uint statusCode)
|
||||
{
|
||||
// Match the OPC UA shorthand for the statuses most-likely to land in a CLI run.
|
||||
// Anything outside this short-list surfaces as hex — operators can cross-reference
|
||||
// against OPC UA Part 6 § 7.34 (StatusCode tables) or Core.Abstractions status mappers.
|
||||
// OPC UA status codes carry sub-code and flag bits in the low 16 bits (info type,
|
||||
// structure-changed, semantics-changed, limit bits, overflow, etc.). To ensure
|
||||
// that e.g. 0x80050001 still reads as "BadCommunicationError" rather than bare hex,
|
||||
// named codes are matched against the high-word mask (code & 0xFFFF0000). When no
|
||||
// named match is found the severity class (top 2 bits) provides a meaningful fallback
|
||||
// so operators always see at least Good / Uncertain / Bad rather than raw hex.
|
||||
// Numeric codes are the canonical values from the OPC Foundation Opc.Ua.StatusCodes
|
||||
// table; keep them in sync with that table if this list is extended.
|
||||
var name = statusCode switch
|
||||
var masked = statusCode & 0xFFFF0000u;
|
||||
var name = masked switch
|
||||
{
|
||||
0x00000000u => "Good",
|
||||
0x80000000u => "Bad",
|
||||
@@ -119,6 +123,19 @@ public static class SnapshotFormatter
|
||||
0x40000000u => "Uncertain",
|
||||
_ => null,
|
||||
};
|
||||
|
||||
if (name is null)
|
||||
{
|
||||
// Severity fallback: top 2 bits identify the quality class even for unknown
|
||||
// sub-codes. 0x80000000 and 0xC0000000 (reserved quality) both map to "Bad".
|
||||
name = (statusCode & 0xC0000000u) switch
|
||||
{
|
||||
0x00000000u => "Good",
|
||||
0x40000000u => "Uncertain",
|
||||
_ => "Bad",
|
||||
};
|
||||
}
|
||||
|
||||
return name is null
|
||||
? $"0x{statusCode:X8}"
|
||||
: $"0x{statusCode:X8} ({name})";
|
||||
|
||||
Reference in New Issue
Block a user