115a43dd47
Registers the MTConnect typed tag editor in TagConfigEditorMap /
TagConfigValidator (keyed off DriverTypeNames.MTConnect, never a literal) and
adds the razor shell over Task 17's MTConnectTagConfigModel.
The shell stays trivial: the /probe-sourced mtCategory/mtType/mtSubType/units
row is rendered disabled + readonly with NO binding of any kind, and no
inferred-type hint is shown — MTConnectDataTypeInference.Infer() also keys on
the DataItem `representation`, which the tag-config model does not carry, so
calling it here would agree with the browse picker for ordinary items and
silently disagree for exactly the DATA_SET/TABLE/TIME_SERIES ones.
Also fixes the picker -> editor round trip, which was broken in both
directions:
- RawBrowseCommitMapper.BuildTagConfig had no MTConnect branch, so a committed
tag landed as the generic {"address": "<dataItemId>"} while the model read
only `fullName`. The data plane kept working (the driver accepts all three id
spellings), so the only symptom was an empty DataItem-id field in the editor
— and saving from that state stamped fullName:"" over the tag.
- MTConnectTagConfigModel.FromJson now accepts dataItemId/address as fallbacks
(fixing tags committed before this change) and ToJson normalises onto
`fullName`, dropping the aliases so the two spellings cannot drift.
103 lines
4.1 KiB
C#
103 lines
4.1 KiB
C#
using System.Text.Json.Nodes;
|
|
using Shouldly;
|
|
using Xunit;
|
|
using ZB.MOM.WW.OtOpcUa.AdminUI.Uns;
|
|
using ZB.MOM.WW.OtOpcUa.AdminUI.Uns.TagEditors;
|
|
using ZB.MOM.WW.OtOpcUa.Core.Abstractions;
|
|
|
|
namespace ZB.MOM.WW.OtOpcUa.AdminUI.Tests.Uns;
|
|
|
|
/// <summary>
|
|
/// Pins the browse-picker → typed-editor round trip for MTConnect, the driver's <b>primary</b>
|
|
/// authoring path (a tag is committed from the universal browser, then confirmed/overridden in the
|
|
/// typed editor).
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// <para>
|
|
/// The defect this guards: <see cref="RawBrowseCommitMapper.BuildTagConfig"/> had no MTConnect
|
|
/// branch, so a committed tag landed as the generic <c>{"address": "<dataItemId>"}</c>
|
|
/// while <see cref="MTConnectTagConfigModel"/> read only <c>fullName</c>. The data plane kept
|
|
/// working (the driver accepts all three id spellings), so the ONLY symptom was an empty
|
|
/// DataItem-id field in the editor — and saving from that state stamped <c>fullName: ""</c>
|
|
/// over the tag. Razor is exactly what this repo cannot unit-test, so both halves of the fix
|
|
/// are pinned here in plain C#.
|
|
/// </para>
|
|
/// <para>
|
|
/// Both halves are asserted: the mapper now writes the canonical <c>fullName</c> (fixes new
|
|
/// commits), and the model accepts <c>dataItemId</c>/<c>address</c> as fallbacks (fixes tags
|
|
/// already committed before the fix).
|
|
/// </para>
|
|
/// </remarks>
|
|
[Trait("Category", "Unit")]
|
|
public sealed class MTConnectBrowseCommitRoundTripTests
|
|
{
|
|
[Fact]
|
|
public void BuildTagConfig_writes_the_canonical_fullName_key_for_MTConnect()
|
|
{
|
|
var json = RawBrowseCommitMapper.BuildTagConfig(DriverTypeNames.MTConnect, "avail");
|
|
|
|
var o = JsonNode.Parse(json)!.AsObject();
|
|
o["fullName"]!.GetValue<string>().ShouldBe("avail");
|
|
o.ContainsKey("address").ShouldBeFalse(
|
|
"the generic fallback key must not be used now that MTConnect has a typed editor");
|
|
}
|
|
|
|
[Fact]
|
|
public void A_browse_committed_tag_round_trips_through_the_typed_editor_model()
|
|
{
|
|
var committed = RawBrowseCommitMapper.BuildTagConfig(DriverTypeNames.MTConnect, "Xact");
|
|
|
|
var model = MTConnectTagConfigModel.FromJson(committed);
|
|
|
|
model.FullName.ShouldBe("Xact");
|
|
model.Validate().ShouldBeNull("a browse-committed tag must open in the editor already valid");
|
|
MTConnectTagConfigModel.FromJson(model.ToJson()).FullName.ShouldBe("Xact");
|
|
}
|
|
|
|
// ---- legacy blobs committed before the mapper fix -------------------------------------------
|
|
|
|
[Theory]
|
|
[InlineData("address")]
|
|
[InlineData("dataItemId")]
|
|
public void FromJson_accepts_the_drivers_alternate_id_spellings(string key)
|
|
{
|
|
var model = MTConnectTagConfigModel.FromJson($$"""{"{{key}}":"Ypos"}""");
|
|
|
|
model.FullName.ShouldBe("Ypos");
|
|
model.Validate().ShouldBeNull();
|
|
}
|
|
|
|
[Fact]
|
|
public void FullName_wins_over_dataItemId_which_wins_over_address()
|
|
{
|
|
MTConnectTagConfigModel
|
|
.FromJson("""{"fullName":"a","dataItemId":"b","address":"c"}""")
|
|
.FullName.ShouldBe("a");
|
|
|
|
MTConnectTagConfigModel
|
|
.FromJson("""{"dataItemId":"b","address":"c"}""")
|
|
.FullName.ShouldBe("b");
|
|
}
|
|
|
|
[Fact]
|
|
public void A_blank_alias_does_not_shadow_a_populated_one()
|
|
=> MTConnectTagConfigModel
|
|
.FromJson("""{"fullName":" ","address":"c"}""")
|
|
.FullName.ShouldBe("c");
|
|
|
|
[Fact]
|
|
public void Saving_a_legacy_blob_normalises_it_onto_fullName_and_drops_the_aliases()
|
|
{
|
|
var model = MTConnectTagConfigModel.FromJson("""{"address":"Zpos","units":"MILLIMETER"}""");
|
|
|
|
var o = JsonNode.Parse(model.ToJson())!.AsObject();
|
|
|
|
o["fullName"]!.GetValue<string>().ShouldBe("Zpos");
|
|
o.ContainsKey("address").ShouldBeFalse(
|
|
"leaving a second id spelling behind lets the two drift apart on the next edit");
|
|
o.ContainsKey("dataItemId").ShouldBeFalse();
|
|
// Unrelated keys are still preserved — normalisation is not a purge.
|
|
o["units"]!.GetValue<string>().ShouldBe("MILLIMETER");
|
|
}
|
|
}
|