#155 wired the basic tag form (Name / Driver / Equipment / DataType / Access /
WriteIdempotent + ModbusAddressEditor for the address). The per-tag knobs added
across #141 / #142 / #143 still required operators to hand-edit TagConfig JSON.
This commit exposes them through an "Advanced" expander.
UI changes (TagsTab.razor):
- Collapsible "▶ Advanced (Deadband / UnitId override / CoalesceProhibited)"
button below the address editor, visible only when the selected driver is
Modbus. Collapsed by default — basic form covers the typical edit workflow.
- Three numeric / checkbox inputs with inline help text explaining each knob's
purpose and when to use it.
- _showAdvanced auto-opens on Edit when any of the advanced fields are present
in the existing TagConfig — operators see immediately what's been configured.
Save-side serialization:
- New RefreshTagConfigJson serializes the address + advanced fields into a
structured JSON object using a Dictionary<string, object?>. Fields with
default / empty values are omitted to keep diffs in the existing draft-diff
viewer minimal — a tag with only an address still produces
`{"addressString":"40001:F"}` and not a full superset object with nulls.
- OnAddressChanged + OnAdvancedChanged both delegate to RefreshTagConfigJson
so any input change keeps TagConfig in sync.
Read-side hydration:
- New HydrateModbusFromTagConfig parses an existing TagConfig JSON and
populates _modbusAddress + the three advanced fields. Falls back to empty
defaults on malformed JSON. ResetAdvanced is called before hydration on
every form open so leftover state from a previous edit doesn't leak.
ResetAdvanced helper introduced + called from StartAdd so a fresh "New tag"
form starts with everything cleared.
Tests (1 new in TagServiceTests):
- TagConfig_With_Advanced_Modbus_Fields_RoundTrips_Through_Factory — creates a
tag whose TagConfig carries addressString + deadband + unitId +
coalesceProhibited, persists via TagService, reloads, asserts every field
survives. Then constructs a wrapping driver-config JSON and feeds it to
ModbusDriverFactoryExtensions.CreateInstance — confirms the field NAMES the
UI emits match what BuildTag's DTO consumes. If the UI's JSON shape ever
drifts from the factory's expected DTO, this test catches it before users do.
119 + 1 = 120 Admin tests green. Solution build clean.