Task #156 — TagsTab: per-tag advanced Modbus fields (Deadband, UnitId, CoalesceProhibited)
#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.
This commit is contained in:
@@ -64,6 +64,56 @@ public sealed class TagServiceTests
|
||||
fresh.TagConfig.ShouldContain("40001:F");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task TagConfig_With_Advanced_Modbus_Fields_RoundTrips_Through_Factory()
|
||||
{
|
||||
// #156 — TagsTab serializes advanced fields (deadband / unitId / coalesceProhibited)
|
||||
// into TagConfig as a structured JSON object alongside addressString. Confirm the
|
||||
// shape survives a DB round-trip AND that ModbusDriverFactoryExtensions.BuildTag's
|
||||
// JSON consumer accepts it. If the field names drift between the UI and the factory,
|
||||
// this test catches it before users do.
|
||||
using var ctx = NewContext();
|
||||
var svc = new TagService(ctx);
|
||||
|
||||
var advancedConfig = System.Text.Json.JsonSerializer.Serialize(new
|
||||
{
|
||||
addressString = "40001:F:CDAB",
|
||||
deadband = 0.5,
|
||||
unitId = 7,
|
||||
coalesceProhibited = true,
|
||||
});
|
||||
var t = NewTag("Tank");
|
||||
t.TagConfig = advancedConfig;
|
||||
await svc.CreateAsync(1, t, TestContext.Current.CancellationToken);
|
||||
|
||||
var fresh = (await svc.ListAsync(1, ct: TestContext.Current.CancellationToken)).Single();
|
||||
fresh.TagConfig.ShouldContain("addressString");
|
||||
fresh.TagConfig.ShouldContain("deadband");
|
||||
fresh.TagConfig.ShouldContain("unitId");
|
||||
fresh.TagConfig.ShouldContain("coalesceProhibited");
|
||||
|
||||
// Build the wrapping driver-config JSON the factory consumes (one tag, the structured
|
||||
// config above as its TagConfig), then construct a driver from it. If any field name
|
||||
// doesn't match the DTO, BuildTag throws here.
|
||||
var driverConfig = System.Text.Json.JsonSerializer.Serialize(new
|
||||
{
|
||||
host = "127.0.0.1",
|
||||
tags = new[]
|
||||
{
|
||||
new
|
||||
{
|
||||
name = "Tank",
|
||||
addressString = "40001:F:CDAB",
|
||||
deadband = 0.5,
|
||||
unitId = (byte)7,
|
||||
coalesceProhibited = true,
|
||||
},
|
||||
},
|
||||
});
|
||||
Should.NotThrow(() => ZB.MOM.WW.OtOpcUa.Driver.Modbus.ModbusDriverFactoryExtensions.CreateInstance(
|
||||
"advanced-rt", driverConfig));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Delete_Removes_The_Row()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user