#549/#551 made every typed editor judge its driver's enum KEYS against the runtime's
own reader and stopped there. Every other reason a reader refuses a tag met no gate
until deploy, or until the driver silently skipped the tag: an absent Modbus `region`,
an `"address":"notanumber"`, an S7 `stringLength` of 300, a TwinCAT `dataType:
"Structure"` (a VALID enum name the reader refuses), a Sql blob the parser cannot
recognise as Sql at all, an MQTT `"qos":null`.
Rather than hand-mirroring N more rules into N models — the drift #551 closed on the
deploy side — each `<Driver>TagConfigModel.Validate()` now asks its driver's own parse
whether it would read the AS-LOADED blob, via the new `TagConfigParseGate`. One
authority for the verdict AND the wording.
Two cautions are load-bearing and are pinned by tests:
- The gate is the PARSE VERDICT, never "the inspector said something". FOCAS, MQTT and
Calculation inspectors emit advisories for blobs their reader ACCEPTS, so a
non-empty-inspection gate would make the modal stricter than the runtime and leave
rows nobody can save (#520/#535). Inspect is consulted only to word a refusal that
already happened.
- It judges the blob the model was LOADED FROM, not the typed fields — the lenient load
("address":"notanumber" ⇒ 0) is what hides the defect. Same capture-the-source shape
as S7's `_dataTypeMissingInSource`.
Deliberately-stricter editor rules were surveyed and KEPT, not aligned: AbLegacy's
arrayLength > 256 (reader clamps, but the un-clamped value still sets ArrayDimensions),
FOCAS's unparseable address (#540), MQTT wildcard topics + Sparkplug segment chars, and
Calculation's trigger/50 ms floors. They are now recorded fleet-wide in a table the
tests assert is still accurate.
Behaviour change: Modbus `{}` is now refused. Modbus was the one mapped editor with no
required field, and `{}` is exactly what the modal persists for a config nobody touched
— the driver skips it and it reads BadNodeIdUnknown forever with the deploy green. Its
pinned test asserted the old, weaker behaviour and is rewritten with the rationale.
Falsification: neutering only the Modbus consult turns 14 tests red, all Modbus-scoped;
every #549 enum test stays green.
Gitea #551.
1. CSV import ran no driver-typed config gate. Its typed columns were always
strictly parsed, but the free-form TagConfigJson column reached the database
unread, so a CSV could author a tag the driver rejects — one that deploys
green and reads BadNodeIdUnknown forever. RawTagCsvMapper.Parse now names the
offending row in the staged review grid, and RawTreeService enforces the same
gate on CreateTag/UpdateTag/ImportTags: a form cannot be the guarantee (the
API and any future caller reach none of them, and this repo has no bUnit).
A rejected import row is an ERROR — the batch inserts nothing and the row is
named — never a skip.
Note the brief's premise that manual entry rides ImportTagsAsync is wrong:
RawManualTagEntryModal commits row-by-row via CreateTagAsync, which is why
the gate had to land on all three write paths.
2/3. The seven pre-#549 Inspect methods ran a hand-list of DescribeInvalidEnum
checks, so every rejection cause not on the list was invisible. They are now
ACCEPT-GATE-FIRST (the MTConnect/Sql/OpcUaClient/Calculation shape): ask the
driver's own reader for the verdict, then explain the refusal, with a
catch-all so "the parse rejects but Inspect said nothing" is unreachable.
That closes S7's absent dataType, AbCip members[], Modbus stringByteOrder and
the type-dependent guards, plus causes the issue did not name (absent
region/address/tagPath/symbolPath/topic, TwinCAT Structure, MQTT's Sparkplug
tuple). FOCAS and MQTT keep advisories about ACCEPTED blobs, so they
diagnose rather than return empty.
Authoring half: ModbusTagConfigModel gains stringByteOrder;
AbCipTagConfigModel asks the new AbCipTagDefinitionFactory.DescribeInvalidMember;
S7TagConfigModel captures whether the SOURCE blob declared dataType and asks
S7TagDefinitionFactory.DataTypeKey/DeclaresDataType. One authority per rule.
4. Doc sweep: the six factory Inspect summaries, EquipmentTagConfigInspector's
class remarks and AdminOperationsActor's comment all claimed invalid enums are
"silently defaulted by the lenient runtime" — false since R2-11 Phase C made
the runtime reads strict. They describe rejections now.