From efbdaf853cc1f4801b243ea4946e191a04d8ab37 Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Fri, 26 Jun 2026 12:26:28 -0400 Subject: [PATCH] feat(otopcua): set Modbus/S7/Galaxy re-discovery policy to Once + Once-branch test (follow-up B) --- .../ZB.MOM.WW.OtOpcUa.Driver.Galaxy/GalaxyDriver.cs | 9 +++++++++ .../ZB.MOM.WW.OtOpcUa.Driver.Modbus/ModbusDriver.cs | 7 +++++++ src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.S7/S7Driver.cs | 7 +++++++ .../AbLegacyDriverTests.cs | 11 +++++++++++ 4 files changed, 34 insertions(+) diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/GalaxyDriver.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/GalaxyDriver.cs index 75f01621..65a199de 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/GalaxyDriver.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/GalaxyDriver.cs @@ -584,6 +584,15 @@ public sealed class GalaxyDriver // ===== ITagDiscovery (PR 4.1) ===== + /// + /// Run-once: fetches the full Galaxy hierarchy inline and + /// streams the complete node set within a single awaited call — there is no FOCAS-style + /// background cache that fills in after connect. Galaxy is a heavy network driver, so the + /// bounded post-connect retry loop is deliberately avoided; re-discovery on Galaxy + /// redeploy is handled separately via + the deploy-event watcher. + /// + public DiscoveryRediscoverPolicy RediscoverPolicy => DiscoveryRediscoverPolicy.Once; + /// public async Task DiscoverAsync(IAddressSpaceBuilder builder, CancellationToken cancellationToken) { diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus/ModbusDriver.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus/ModbusDriver.cs index 8f38e5f2..4d8ad138 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus/ModbusDriver.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus/ModbusDriver.cs @@ -261,6 +261,13 @@ public sealed class ModbusDriver // ---- ITagDiscovery ---- + /// + /// Run-once: emits the complete node set synchronously from + /// the configured tag table in a single pass — nothing fills in asynchronously after + /// connect, so a single discovery pass is sufficient. + /// + public DiscoveryRediscoverPolicy RediscoverPolicy => DiscoveryRediscoverPolicy.Once; + /// Discovers tags and builds the OPC UA address space. /// Address space builder. /// Cancellation token. diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.S7/S7Driver.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.S7/S7Driver.cs index a400afd0..c135b1f1 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.S7/S7Driver.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.S7/S7Driver.cs @@ -1133,6 +1133,13 @@ public sealed class S7Driver // ---- ITagDiscovery ---- + /// + /// Run-once: emits the complete node set synchronously from + /// the configured tag table in a single pass — nothing fills in asynchronously after + /// connect, so a single discovery pass is sufficient. + /// + public DiscoveryRediscoverPolicy RediscoverPolicy => DiscoveryRediscoverPolicy.Once; + /// Discovers tags and builds the OPC UA address space. /// Address space builder. /// Cancellation token. diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.Tests/AbLegacyDriverTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.Tests/AbLegacyDriverTests.cs index d684915c..ee0266ab 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.Tests/AbLegacyDriverTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.Tests/AbLegacyDriverTests.cs @@ -18,6 +18,17 @@ public sealed class AbLegacyDriverTests drv.DriverInstanceId.ShouldBe("drv-1"); } + /// + /// Verifies AbLegacy opts into run-once post-connect re-discovery — it discovers its + /// complete node set synchronously from config, with no FOCAS-style background cache fill. + /// + [Fact] + public void RediscoverPolicy_is_Once() + { + var drv = new AbLegacyDriver(new AbLegacyDriverOptions(), "drv-1"); + drv.RediscoverPolicy.ShouldBe(DiscoveryRediscoverPolicy.Once); + } + /// Verifies that InitializeAsync with devices assigns family profiles. [Fact] public async Task InitializeAsync_with_devices_assigns_family_profiles()