From 136aa07898d77bf43c00bf627365d6e143c63f33 Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Fri, 24 Jul 2026 17:43:27 -0400 Subject: [PATCH] feat(mtconnect): host registration + DriverTypeNames const + guard parity (Task 16) Register the MTConnect factory + probe with the Host, add the DriverTypeNames.MTConnect constant, and keep DriverTypeNamesGuardTests green. - DriverTypeNames: new MTConnect const + appended to All. - DriverFactoryBootstrap: MTConnectDriverFactoryExtensions.Register in Register(...) at the default Tier A (managed HttpClient/XML, in-process; Tier C is the only tier arming process-recycle, wrong here), and TryAddEnumerable(IDriverProbe -> MTConnectDriverProbe) in AddOtOpcUaDriverProbes so the probe reaches admin-only nodes (the admin-pinned Test-Connect singleton) without double-registering on a fused admin,driver node. - Host.csproj: ProjectReference to Driver.MTConnect (required to compile the Register call). - Core.Abstractions.Tests.csproj: ProjectReference to Driver.MTConnect so the guard's bin scan discovers the factory. This and the constant MUST land together -- verified RED (2/4 facts) with the constant alone. - DriverProbeRegistrationTests: MTConnect added to AdminUiDriverTypeKeys; its idempotency fact asserts distinct-probe-count and goes RED without this (and RED if TryAddEnumerable is downgraded to AddSingleton). Reconciles all four "MTConnect" literals onto the one constant: the factory's DriverTypeName, MTConnectDriver.DriverType, and MTConnectDriverProbe.DriverType now all alias DriverTypeNames.MTConnect (no circular reference -- Core.Abstractions is a leaf both driver projects already reference). This is the repo's TwinCat/Focas anti-drift convention. --- .../DriverTypeNames.cs | 4 ++++ .../MTConnectDriver.cs | 7 ++++++- .../MTConnectDriverFactoryExtensions.cs | 15 +++++++++------ .../MTConnectDriverProbe.cs | 7 ++++++- .../Drivers/DriverFactoryBootstrap.cs | 9 +++++++++ .../ZB.MOM.WW.OtOpcUa.Host.csproj | 1 + ....MOM.WW.OtOpcUa.Core.Abstractions.Tests.csproj | 1 + .../DriverProbeRegistrationTests.cs | 1 + 8 files changed, 37 insertions(+), 8 deletions(-) diff --git a/src/Core/ZB.MOM.WW.OtOpcUa.Core.Abstractions/DriverTypeNames.cs b/src/Core/ZB.MOM.WW.OtOpcUa.Core.Abstractions/DriverTypeNames.cs index bbebd080..7e521839 100644 --- a/src/Core/ZB.MOM.WW.OtOpcUa.Core.Abstractions/DriverTypeNames.cs +++ b/src/Core/ZB.MOM.WW.OtOpcUa.Core.Abstractions/DriverTypeNames.cs @@ -56,6 +56,9 @@ public static class DriverTypeNames /// Read-only SQL Server table/view poller — publishes columns as OPC UA variables. public const string Sql = "Sql"; + /// MTConnect Agent driver — read-only HTTP/XML over an Agent's probe/current/sample surface. + public const string MTConnect = "MTConnect"; + /// /// Every driver-type string declared above, for callers that need to enumerate /// the full set (e.g. validation of an authored DriverType). @@ -72,5 +75,6 @@ public static class DriverTypeNames Galaxy, Calculation, Sql, + MTConnect, ]; } diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.MTConnect/MTConnectDriver.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.MTConnect/MTConnectDriver.cs index 6b6c4f77..babf65b2 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.MTConnect/MTConnectDriver.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.MTConnect/MTConnectDriver.cs @@ -386,7 +386,12 @@ public sealed class MTConnectDriver : IDriver, IReadable, ISubscribable, ITagDis public string DriverInstanceId => _driverInstanceId; /// - public string DriverType => "MTConnect"; + /// + /// Sourced from the constant rather than a literal — the + /// repo-wide fix for the historical TwinCat/Focas drift, where a driver's own + /// spelling diverged from the constant every dispatch map keys off. + /// + public string DriverType => DriverTypeNames.MTConnect; /// /// The live dataItemId → snapshot map that backs the subscription surface: diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.MTConnect/MTConnectDriverFactoryExtensions.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.MTConnect/MTConnectDriverFactoryExtensions.cs index 34797fa7..40faa2bd 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.MTConnect/MTConnectDriverFactoryExtensions.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.MTConnect/MTConnectDriverFactoryExtensions.cs @@ -45,13 +45,16 @@ public static class MTConnectDriverFactoryExtensions /// The DriverInstance.DriverType value this factory answers to. /// /// - /// Kept identical to — the registry key and the - /// instance's self-reported type must agree or the runtime would look the driver up under a - /// name it never registered. Task 16 adds the matching DriverTypeNames.MTConnect - /// constant (the repo's convention is that dispatch maps key off those constants) and wires - /// the Host registration + guard test; this literal is what that constant must equal. + /// Aliased to , not a literal. The registry key, + /// the instance's self-reported , the probe's + /// DriverType, and the constant every dispatch map keys off are therefore the same + /// symbol — the repo-wide fix for the historical TwinCat/Focas drift, where a + /// driver's own spelling silently diverged from the constant and dispatch maps missed it. + /// Retained as a named const (rather than deleted in favour of the constant) so the existing + /// factory callers and the sibling *DriverFactoryExtensions.DriverTypeName convention + /// keep working; it is now an alias with no independent value. /// - public const string DriverTypeName = "MTConnect"; + public const string DriverTypeName = DriverTypeNames.MTConnect; /// /// Register the MTConnect factory with the driver registry. The optional diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.MTConnect/MTConnectDriverProbe.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.MTConnect/MTConnectDriverProbe.cs index a6afb152..7768ec7b 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.MTConnect/MTConnectDriverProbe.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.MTConnect/MTConnectDriverProbe.cs @@ -86,7 +86,12 @@ public sealed class MTConnectDriverProbe : IDriverProbe } /// - public string DriverType => "MTConnect"; + /// + /// Sourced from the constant, not a literal — the probe is + /// looked up by AdminOperationsActor under this key, so a drift from the constant + /// silently disables the Test Connect button rather than failing anything at build time. + /// + public string DriverType => DriverTypeNames.MTConnect; /// Minimal probe-only config shape. Deliberately independent of the factory DTO — see the type remarks. private sealed class MTConnectProbeConfigDto diff --git a/src/Server/ZB.MOM.WW.OtOpcUa.Host/Drivers/DriverFactoryBootstrap.cs b/src/Server/ZB.MOM.WW.OtOpcUa.Host/Drivers/DriverFactoryBootstrap.cs index 2687366e..83cf2631 100644 --- a/src/Server/ZB.MOM.WW.OtOpcUa.Host/Drivers/DriverFactoryBootstrap.cs +++ b/src/Server/ZB.MOM.WW.OtOpcUa.Host/Drivers/DriverFactoryBootstrap.cs @@ -19,6 +19,7 @@ using OpcUaProbe = Driver.OpcUaClient.OpcUaClientDriverProbe; using GalaxyProbe = Driver.Galaxy.GalaxyDriverProbe; using CalculationProbe = Driver.Calculation.CalculationDriverProbe; using SqlProbe = Driver.Sql.SqlDriverProbe; +using MTConnectProbe = Driver.MTConnect.MTConnectDriverProbe; /// /// Wires every cross-platform driver assembly's Register(registry, loggerFactory) @@ -124,6 +125,7 @@ public static class DriverFactoryBootstrap services.TryAddEnumerable(ServiceDescriptor.Singleton()); services.TryAddEnumerable(ServiceDescriptor.Singleton()); services.TryAddEnumerable(ServiceDescriptor.Singleton()); + services.TryAddEnumerable(ServiceDescriptor.Singleton()); return services; } @@ -146,6 +148,13 @@ public static class DriverFactoryBootstrap Driver.FOCAS.FocasDriverFactoryExtensions.Register(registry); Driver.Galaxy.GalaxyDriverFactoryExtensions.Register(registry, secretResolver, loggerFactory); Driver.Modbus.ModbusDriverFactoryExtensions.Register(registry, loggerFactory); + // Tier A (the Register default): fully managed — HttpClient + System.Xml.Linq, no native SDK, + // no COM. Tier C is the only tier that arms process-level recycle (MemoryRecycle hard-breach / + // ScheduledRecycleScheduler), which would be wrong here: the driver is in-process and killing + // it kills every OPC UA session. The long-lived /sample stream does not argue for a slower + // tier either — SubscribeAsync returns synchronously after starting the pump on a background + // task, so the Tier A 5s Subscribe budget never covers the stream's lifetime. + Driver.MTConnect.MTConnectDriverFactoryExtensions.Register(registry, loggerFactory); Driver.OpcUaClient.OpcUaClientDriverFactoryExtensions.Register(registry, loggerFactory, secretResolver); Driver.S7.S7DriverFactoryExtensions.Register(registry); Driver.Sql.SqlDriverFactoryExtensions.Register(registry, loggerFactory); diff --git a/src/Server/ZB.MOM.WW.OtOpcUa.Host/ZB.MOM.WW.OtOpcUa.Host.csproj b/src/Server/ZB.MOM.WW.OtOpcUa.Host/ZB.MOM.WW.OtOpcUa.Host.csproj index b3fc5bad..cfb242b3 100644 --- a/src/Server/ZB.MOM.WW.OtOpcUa.Host/ZB.MOM.WW.OtOpcUa.Host.csproj +++ b/src/Server/ZB.MOM.WW.OtOpcUa.Host/ZB.MOM.WW.OtOpcUa.Host.csproj @@ -74,6 +74,7 @@ + diff --git a/tests/Core/ZB.MOM.WW.OtOpcUa.Core.Abstractions.Tests/ZB.MOM.WW.OtOpcUa.Core.Abstractions.Tests.csproj b/tests/Core/ZB.MOM.WW.OtOpcUa.Core.Abstractions.Tests/ZB.MOM.WW.OtOpcUa.Core.Abstractions.Tests.csproj index 182518eb..c98479f4 100644 --- a/tests/Core/ZB.MOM.WW.OtOpcUa.Core.Abstractions.Tests/ZB.MOM.WW.OtOpcUa.Core.Abstractions.Tests.csproj +++ b/tests/Core/ZB.MOM.WW.OtOpcUa.Core.Abstractions.Tests/ZB.MOM.WW.OtOpcUa.Core.Abstractions.Tests.csproj @@ -37,6 +37,7 @@ + diff --git a/tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests/DriverProbeRegistrationTests.cs b/tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests/DriverProbeRegistrationTests.cs index c2e74697..61d4a148 100644 --- a/tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests/DriverProbeRegistrationTests.cs +++ b/tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests/DriverProbeRegistrationTests.cs @@ -32,6 +32,7 @@ public sealed class DriverProbeRegistrationTests "OpcUaClient", "GalaxyMxGateway", "Calculation", // v3 Batch 2 pseudo-driver; CalculationProbe registered in DriverFactoryBootstrap + "MTConnect", // MTConnectProbe — read-only Agent driver; probe issues a bare GET /probe ]; [Fact]