fix(adminui): authoring must not accept a TagConfig enum the runtime rejects (#549)
MTConnectTagConfigModel read `dataType` with TagConfigJson.GetEnum, which falls
back to String on ANY unrecognised value. A misspelled name ("Strng") from a
hand-authored blob, a CSV import or the API was therefore accepted at authoring
and silently rewritten, while the driver's strict parse rejected it and the tag
died with BadNodeIdUnknown. Found by the G-7 parity tests' falsifiability
control, which had to use the digits-only shape to have a case that failed.
The rule is not restated in the AdminUI. MTConnectTagDefinitionFactory exposes
its parse as `IsAcceptedDataTypeName`, documented as the one authority the
driver, the deploy gate and the authoring model all consult; the model's
Validate() asks it about the raw value the blob carries. The old digits-only
message is NOT retained as a special case: the runtime draws no distinction
between a bare number and a typo — one predicate, one Inspect warning — so a
second message here would be a second statement of a rule this model does not
own. The raw value is captured as the JSON TOKEN, because the runtime
deserialises the key into a `string?` DTO field and an unquoted number throws
there too.
The survey found the same asymmetry at six more editors, contrary to the
assumption that those runtimes silently default: R2-11 Phase C moved every
driver's TagConfig enum read to TagConfigJson.TryReadEnumStrict, which REJECTS
the tag on a present-but-invalid string, while the editors kept loading them
with a silent fallback. Modbus (region/dataType/byteOrder), S7, AbCip, AbLegacy,
TwinCAT and FOCAS now check their enums in Validate() through the runtime's own
TagConfigJson.DescribeInvalidEnumText — added alongside a public
ClassifyEnumText that TryReadEnum now delegates to, so the JSON-token door and
the string door reach one implementation. Mqtt and Sql already carried
equivalent strict checks and are untouched.
Care taken NOT to become stricter than the runtime: a present-but-non-string
token stays accepted for those six (TryReadEnum calls it absent), as does an
absent key, each pinned by a test.
Also adds a direct ProjectReference to Core.Abstractions from the AdminUI — the
save gate now leans on a type it was consuming only transitively.
This commit is contained in:
+22
@@ -154,6 +154,28 @@ public static class MTConnectTagDefinitionFactory
|
||||
return warnings;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <b>The single authority on which coercion-type spellings an MTConnect tag may carry</b>, consulted
|
||||
/// by the runtime + deploy gate (through <see cref="TryFromTagConfig"/> / <see cref="Inspect"/>) and
|
||||
/// by the AdminUI's authoring model (<c>MTConnectTagConfigModel.Validate</c>) alike.
|
||||
/// <para>
|
||||
/// It is public for exactly that second caller. The authoring model used to read the type with a
|
||||
/// lenient enum parse that fell back to <c>String</c> on ANY unrecognised value, so a misspelled
|
||||
/// name ("Strng") from a hand-authored blob, a CSV import or the API was accepted at authoring —
|
||||
/// and silently rewritten — while this parse rejected it and the tag died at the driver
|
||||
/// (Gitea #549). A second copy of the rule in the AdminUI would have reopened that gap the
|
||||
/// moment either side moved, so there is one rule and both sides ask it.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// A blank/absent value is ACCEPTED: it declares no override, and <see cref="TryFromTagConfig"/>
|
||||
/// then falls back to the Agent's <c>/probe</c> declaration rather than rejecting the tag.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
/// <param name="raw">The authored type text, or <see langword="null"/>/blank when none was authored.</param>
|
||||
/// <returns><see langword="true"/> when the driver would accept this authored type.</returns>
|
||||
public static bool IsAcceptedDataTypeName(string? raw)
|
||||
=> string.IsNullOrWhiteSpace(raw) || TryParseDataType(raw.Trim(), out _);
|
||||
|
||||
/// <summary>
|
||||
/// Parses an authored coercion type by NAME. A digits-only value is rejected outright, BEFORE the
|
||||
/// parse: <see cref="Enum.TryParse{TEnum}(string, bool, out TEnum)"/> accepts <c>"8"</c> as readily
|
||||
|
||||
Reference in New Issue
Block a user