refactor(historian-gateway): retire Wonderware historian projects (gateway is sole backend)

The HistorianGateway driver is now the sole historian read/write+alarm backend, so the
Wonderware sidecar projects are dead code. Removes the 5 Wonderware projects (driver,
.Client, .Client.Contracts, + their 2 test projects) from the solution and tree, and fully
retires the vestigial 'Historian.Wonderware' driver type (UI/probe-only; it had no driver
factory): the Host probe registration, the AdminUI driver-config surface (driver page,
tag-config editor/model/validator entry, address picker/builder, driver-type catalog +
dropdown + edit-router entries), and their tests. Prunes the now-unused Wonderware
connection fields (Host/Port/UseTls/ServerCertThumbprint/SharedSecret) from
AlarmHistorianOptions (keeping Enabled + the SQLite store-and-forward knobs) and refreshes
the stale XML docs that named Wonderware as the production backend.

Claude-Session: https://claude.ai/code/session_012SDSQ3AcaXqPcBtDESBRii
This commit is contained in:
Joseph Doherty
2026-06-26 19:25:21 -04:00
parent 245db98f5e
commit 0b4b2e4cfd
84 changed files with 37 additions and 9345 deletions
@@ -1,100 +0,0 @@
using Shouldly;
using Xunit;
using ZB.MOM.WW.OtOpcUa.AdminUI.Uns.TagEditors;
namespace ZB.MOM.WW.OtOpcUa.AdminUI.Tests.Uns;
public sealed class HistorianWonderwareTagConfigModelTests
{
[Theory]
[InlineData(null)]
[InlineData("")]
[InlineData(" ")]
[InlineData("{}")]
public void FromJson_returns_defaults_for_empty_input(string? json)
{
var m = HistorianWonderwareTagConfigModel.FromJson(json);
m.FullName.ShouldBe("");
}
[Fact]
public void FromJson_reads_FullName()
{
var m = HistorianWonderwareTagConfigModel.FromJson(
"""{"FullName":"Reactor1.Temp"}""");
m.FullName.ShouldBe("Reactor1.Temp");
}
[Fact]
public void Round_trip_preserves_FullName()
{
var m = new HistorianWonderwareTagConfigModel { FullName = "Reactor1.Temp" };
var json = m.ToJson();
var m2 = HistorianWonderwareTagConfigModel.FromJson(json);
m2.FullName.ShouldBe("Reactor1.Temp");
}
[Fact]
public void ToJson_emits_PascalCase_FullName()
{
var m = new HistorianWonderwareTagConfigModel { FullName = "Reactor1.Temp" };
var json = m.ToJson();
// FullName is the composer/walker contract key — PascalCase, case-sensitive.
json.ShouldContain("\"FullName\":\"Reactor1.Temp\"");
json.ShouldNotContain("\"fullName\"", Case.Sensitive);
}
[Fact]
public void FromJson_then_ToJson_preserves_unknown_keys()
{
var json = HistorianWonderwareTagConfigModel
.FromJson("""{"FullName":"Reactor1.Temp","deadband":0.5}""")
.ToJson();
json.ShouldContain("deadband");
json.ShouldContain("0.5");
// and the exposed field still round-trips
json.ShouldContain("\"FullName\":\"Reactor1.Temp\"");
}
[Fact]
public void FromJson_then_ToJson_preserves_TagModal_merged_history_keys()
{
// The TagModal-merge seam writes isHistorized/historianTagname at the TagConfig root; this model
// does NOT model them, so they must survive a load→save untouched as preserved unknown keys.
var json = HistorianWonderwareTagConfigModel
.FromJson("""{"FullName":"Reactor1.Temp","isHistorized":true,"historianTagname":"Reactor1.Temp.Override"}""")
.ToJson();
json.ShouldContain("\"isHistorized\":true");
json.ShouldContain("\"historianTagname\":\"Reactor1.Temp.Override\"");
json.ShouldContain("\"FullName\":\"Reactor1.Temp\"");
}
[Fact]
public void ToJson_trims_FullName()
{
var json = new HistorianWonderwareTagConfigModel { FullName = " Reactor1.Temp " }.ToJson();
json.ShouldContain("\"FullName\":\"Reactor1.Temp\"");
}
[Fact]
public void Validate_returns_error_when_FullName_blank()
{
new HistorianWonderwareTagConfigModel().Validate().ShouldNotBeNullOrEmpty();
new HistorianWonderwareTagConfigModel { FullName = " " }.Validate().ShouldNotBeNullOrEmpty();
}
[Fact]
public void Validate_returns_null_when_FullName_present()
{
new HistorianWonderwareTagConfigModel { FullName = "Reactor1.Temp" }.Validate().ShouldBeNull();
}
}
@@ -31,7 +31,6 @@ public sealed class TagConfigValidatorTests
[InlineData("TwinCat")]
[InlineData("Focas")]
[InlineData("OpcUaClient")]
[InlineData("Historian.Wonderware")]
public void Required_field_blank_is_rejected(string driverType)
{
TagConfigValidator.Validate(driverType, "{}").ShouldNotBeNullOrEmpty();
@@ -42,10 +41,6 @@ public sealed class TagConfigValidatorTests
public void OpcUaClient_with_full_name_is_valid()
=> TagConfigValidator.Validate("OpcUaClient", """{"FullName":"ns=2;s=Line3.Temp"}""").ShouldBeNull();
[Fact]
public void HistorianWonderware_with_full_name_is_valid()
=> TagConfigValidator.Validate("Historian.Wonderware", """{"FullName":"Reactor1.Temp"}""").ShouldBeNull();
[Fact]
public void S7_with_address_is_valid()
=> TagConfigValidator.Validate("S7", """{"address":"DB1.DBW0"}""").ShouldBeNull();