merge: G-7 closed — Contracts-side TagConfig Inspect for Sql/MTConnect/Calculation/OpcUaClient wired into the deploy gate

This commit is contained in:
Joseph Doherty
2026-08-09 21:16:16 -04:00
15 changed files with 1057 additions and 197 deletions
@@ -1,10 +1,14 @@
using ZB.MOM.WW.OtOpcUa.Core.Abstractions;
using ZB.MOM.WW.OtOpcUa.Driver.AbCip;
using ZB.MOM.WW.OtOpcUa.Driver.AbLegacy;
using ZB.MOM.WW.OtOpcUa.Driver.Calculation;
using ZB.MOM.WW.OtOpcUa.Driver.FOCAS;
using ZB.MOM.WW.OtOpcUa.Driver.Modbus;
using ZB.MOM.WW.OtOpcUa.Driver.Mqtt;
using ZB.MOM.WW.OtOpcUa.Driver.MTConnect;
using ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient;
using ZB.MOM.WW.OtOpcUa.Driver.S7;
using ZB.MOM.WW.OtOpcUa.Driver.Sql.Contracts;
using ZB.MOM.WW.OtOpcUa.Driver.TwinCAT;
namespace ZB.MOM.WW.OtOpcUa.ControlPlane.AdminOperations;
@@ -20,13 +24,19 @@ namespace ZB.MOM.WW.OtOpcUa.ControlPlane.AdminOperations;
/// <c>*.Contracts</c> assemblies — so a driver that gains an inspector cannot be silently left out. This
/// is the same guard shape the driver-form dispatch maps use, and for the same reason: the previous
/// hand-maintained list had drifted (MQTT shipped an <c>Inspect</c> and was never added).</para>
/// <para><b>Residual gap, deliberately recorded rather than papered over.</b> Galaxy, OpcUaClient, Sql,
/// MTConnect and Calculation have <b>no</b> <c>Inspect</c> in a Contracts assembly, so this deploy-time
/// gate cannot check them — while the AdminUI's <c>TagConfigValidator</c> DOES cover all but Galaxy via
/// its <c>*TagConfigModel.Validate()</c> models, which live in the AdminUI project and cannot be
/// referenced from here. The asymmetry runs the wrong way: config authored through the modal is checked,
/// but config arriving by CSV import or API is not. Closing it means promoting those models (or an
/// equivalent parser) into each driver's Contracts assembly. Tracked as deferment-register G-7.</para>
/// <para><b>The G-7 gap is CLOSED (2026-08-09).</b> Sql, MTConnect, Calculation and OpcUaClient used to
/// have no <c>Inspect</c> in a Contracts assembly, so this gate was blind to them while the AdminUI's
/// <c>TagConfigValidator</c> checked them at authoring — the asymmetry ran the wrong way, since config
/// arriving by CSV import or API meets ONLY this gate. Each now ships a Contracts-side
/// <c>*TagDefinitionFactory.Inspect</c>, wired below. Three of the four wrap the driver's own runtime
/// parse rather than mirroring the AdminUI model, which is what keeps the gate from ever being stricter
/// than the runtime: <c>SqlTagDefinitionFactory</c> wraps <c>SqlEquipmentTagParser</c>, and the
/// MTConnect / OpcUaClient / Calculation blob readers were MOVED into their Contracts assemblies (the
/// drivers now call them) rather than copied.</para>
/// <para><b>Galaxy is the sole remaining unmapped type, deliberately.</b> Its tag reference is a dotted
/// <c>tag_name.AttributeName</c> authored through the Galaxy address picker, with no typed shape to
/// check — the AdminUI has no typed editor for it either (it uses the raw-JSON textarea), so this is
/// symmetric rather than a gap. It gains an entry here if it ever gains a Contracts-side parser.</para>
/// </summary>
public static class EquipmentTagConfigInspector
{
@@ -40,6 +50,11 @@ public static class EquipmentTagConfigInspector
[DriverTypeNames.TwinCAT] = TwinCATTagDefinitionFactory.Inspect,
[DriverTypeNames.FOCAS] = FocasTagDefinitionFactory.Inspect,
[DriverTypeNames.Mqtt] = MqttTagDefinitionFactory.Inspect,
// G-7 (2026-08-09): the four the gate used to be blind to.
[DriverTypeNames.Sql] = SqlTagDefinitionFactory.Inspect,
[DriverTypeNames.MTConnect] = MTConnectTagDefinitionFactory.Inspect,
[DriverTypeNames.Calculation] = CalculationTagDefinitionFactory.Inspect,
[DriverTypeNames.OpcUaClient] = OpcUaClientTagDefinitionFactory.Inspect,
};
/// <summary>Inspects a tag's <c>TagConfig</c> for the given driver type. Returns the driver's warnings,
@@ -50,6 +50,18 @@
<ProjectReference Include="..\..\Drivers\ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.Contracts\ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.Contracts.csproj"/>
<ProjectReference Include="..\..\Drivers\ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Contracts\ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Contracts.csproj"/>
<ProjectReference Include="..\..\Drivers\ZB.MOM.WW.OtOpcUa.Driver.Mqtt.Contracts\ZB.MOM.WW.OtOpcUa.Driver.Mqtt.Contracts.csproj"/>
<!-- G-7: the four drivers the deploy gate used to be blind to, while the AdminUI's
TagConfigValidator checked them. Adding the reference is what makes
EquipmentTagConfigInspectorCoverageTests demand a dispatch-map entry for them — the guard scans
the output directory, so an unreferenced Contracts assembly is invisible to it.
Note OpcUaClient.Contracts brings OPCFoundation.NetStandard.Opc.Ua.Client transitively: its
NamespaceMap re-binds a stable nsu= reference against a live session. Every host that loads
ControlPlane (Host, AdminUI) already carries the OPC UA stack, so this adds no new deployment
surface. -->
<ProjectReference Include="..\..\Drivers\ZB.MOM.WW.OtOpcUa.Driver.Sql.Contracts\ZB.MOM.WW.OtOpcUa.Driver.Sql.Contracts.csproj"/>
<ProjectReference Include="..\..\Drivers\ZB.MOM.WW.OtOpcUa.Driver.MTConnect.Contracts\ZB.MOM.WW.OtOpcUa.Driver.MTConnect.Contracts.csproj"/>
<ProjectReference Include="..\..\Drivers\ZB.MOM.WW.OtOpcUa.Driver.Calculation.Contracts\ZB.MOM.WW.OtOpcUa.Driver.Calculation.Contracts.csproj"/>
<ProjectReference Include="..\..\Drivers\ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Contracts\ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Contracts.csproj"/>
</ItemGroup>
</Project>