merge wt/t0-3 into v3/batch2-raw-ui (Track 0)
This commit is contained in:
@@ -0,0 +1,68 @@
|
|||||||
|
namespace ZB.MOM.WW.OtOpcUa.Core.Abstractions;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Single source of truth for the driver-type identifier strings the runtime
|
||||||
|
/// dispatches on. Each constant's <b>value</b> is the exact <c>DriverTypeName</c>
|
||||||
|
/// that the corresponding driver factory registers under in the process
|
||||||
|
/// <c>DriverFactoryRegistry</c> — the string the deploy pipeline stores in
|
||||||
|
/// <c>DriverInstance.DriverType</c> and every dispatch map keys by.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// <para>
|
||||||
|
/// Historically several dispatch maps hand-authored these strings and drifted from
|
||||||
|
/// the authoritative factory names (e.g. <c>"TwinCat"</c> vs the real
|
||||||
|
/// <c>"TwinCAT"</c>, <c>"Focas"</c> vs <c>"FOCAS"</c>). Consumers should reference
|
||||||
|
/// these constants instead of literals so the drift can never recur.
|
||||||
|
/// </para>
|
||||||
|
/// <para>
|
||||||
|
/// The reflection guard test
|
||||||
|
/// (<c>DriverTypeNamesGuardTests</c>) asserts bidirectional parity between these
|
||||||
|
/// constants and the set the driver factories actually register, so a rename on
|
||||||
|
/// either side breaks the build's test gate. Only constants for
|
||||||
|
/// <b>currently-registered</b> factories belong here — a not-yet-registered driver
|
||||||
|
/// (e.g. the Calculation driver landing in a later Batch 2 package) adds its own
|
||||||
|
/// constant when its factory is wired in.
|
||||||
|
/// </para>
|
||||||
|
/// </remarks>
|
||||||
|
public static class DriverTypeNames
|
||||||
|
{
|
||||||
|
/// <summary>Modbus TCP / RTU-over-TCP driver.</summary>
|
||||||
|
public const string Modbus = "Modbus";
|
||||||
|
|
||||||
|
/// <summary>Siemens S7 driver.</summary>
|
||||||
|
public const string S7 = "S7";
|
||||||
|
|
||||||
|
/// <summary>Allen-Bradley CIP (ControlLogix / CompactLogix) driver.</summary>
|
||||||
|
public const string AbCip = "AbCip";
|
||||||
|
|
||||||
|
/// <summary>Allen-Bradley legacy (PCCC / DF1-over-TCP) driver.</summary>
|
||||||
|
public const string AbLegacy = "AbLegacy";
|
||||||
|
|
||||||
|
/// <summary>Beckhoff TwinCAT ADS driver.</summary>
|
||||||
|
public const string TwinCAT = "TwinCAT";
|
||||||
|
|
||||||
|
/// <summary>FANUC FOCAS CNC driver.</summary>
|
||||||
|
public const string FOCAS = "FOCAS";
|
||||||
|
|
||||||
|
/// <summary>OPC UA client (upstream-server) driver.</summary>
|
||||||
|
public const string OpcUaClient = "OpcUaClient";
|
||||||
|
|
||||||
|
/// <summary>AVEVA System Platform (Wonderware) Galaxy driver, via the mxaccessgw gateway.</summary>
|
||||||
|
public const string Galaxy = "GalaxyMxGateway";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Every driver-type string declared above, for callers that need to enumerate
|
||||||
|
/// the full set (e.g. validation of an authored <c>DriverType</c>).
|
||||||
|
/// </summary>
|
||||||
|
public static IReadOnlyCollection<string> All { get; } =
|
||||||
|
[
|
||||||
|
Modbus,
|
||||||
|
S7,
|
||||||
|
AbCip,
|
||||||
|
AbLegacy,
|
||||||
|
TwinCAT,
|
||||||
|
FOCAS,
|
||||||
|
OpcUaClient,
|
||||||
|
Galaxy,
|
||||||
|
];
|
||||||
|
}
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
using System.Reflection;
|
||||||
|
using Shouldly;
|
||||||
|
using Xunit;
|
||||||
|
using ZB.MOM.WW.OtOpcUa.Core.Abstractions;
|
||||||
|
using ZB.MOM.WW.OtOpcUa.Core.Hosting;
|
||||||
|
|
||||||
|
namespace ZB.MOM.WW.OtOpcUa.Core.Abstractions.Tests;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Guards <see cref="DriverTypeNames"/> against drift from the driver factories that are the
|
||||||
|
/// authority for these strings. The test builds the <b>real</b> registered-factory set the way
|
||||||
|
/// production does — it drives each driver assembly's <c>*DriverFactoryExtensions.Register</c>
|
||||||
|
/// entry point into a live <see cref="DriverFactoryRegistry"/>, exactly as the Host's
|
||||||
|
/// <c>DriverFactoryBootstrap.Register</c> does — then asserts bidirectional parity with the
|
||||||
|
/// constants. If a factory renames its <c>DriverTypeName</c> (or a constant is added/removed
|
||||||
|
/// without a matching factory), this fails, forcing the constant and the factory back in sync.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class DriverTypeNamesGuardTests
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Build the authoritative registered-factory set by invoking every driver assembly's
|
||||||
|
/// registration extension against a fresh registry — the same set of calls, in the same
|
||||||
|
/// spirit, as <c>DriverFactoryBootstrap.Register</c> on a driver-role node.
|
||||||
|
/// </summary>
|
||||||
|
private static IReadOnlyCollection<string> RegisteredDriverTypeNames()
|
||||||
|
{
|
||||||
|
var registry = new DriverFactoryRegistry();
|
||||||
|
|
||||||
|
// loggerFactory is optional on every extension; the factory func is never invoked here,
|
||||||
|
// so null is fine — we only need the type-name key each Register call installs.
|
||||||
|
Driver.AbCip.AbCipDriverFactoryExtensions.Register(registry);
|
||||||
|
Driver.AbLegacy.AbLegacyDriverFactoryExtensions.Register(registry, loggerFactory: null);
|
||||||
|
Driver.FOCAS.FocasDriverFactoryExtensions.Register(registry);
|
||||||
|
Driver.Galaxy.GalaxyDriverFactoryExtensions.Register(registry, loggerFactory: null);
|
||||||
|
Driver.Modbus.ModbusDriverFactoryExtensions.Register(registry, loggerFactory: null);
|
||||||
|
Driver.OpcUaClient.OpcUaClientDriverFactoryExtensions.Register(registry, loggerFactory: null);
|
||||||
|
Driver.S7.S7DriverFactoryExtensions.Register(registry);
|
||||||
|
Driver.TwinCAT.TwinCATDriverFactoryExtensions.Register(registry);
|
||||||
|
|
||||||
|
return registry.RegisteredTypes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Reflect every <c>public const string</c> declared on <see cref="DriverTypeNames"/>.</summary>
|
||||||
|
private static IReadOnlyCollection<string> DeclaredConstantValues() =>
|
||||||
|
typeof(DriverTypeNames)
|
||||||
|
.GetFields(BindingFlags.Public | BindingFlags.Static)
|
||||||
|
.Where(f => f.IsLiteral && !f.IsInitOnly && f.FieldType == typeof(string))
|
||||||
|
.Select(f => (string)f.GetRawConstantValue()!)
|
||||||
|
.ToArray();
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Every_registered_factory_has_a_matching_constant()
|
||||||
|
{
|
||||||
|
var constants = DeclaredConstantValues();
|
||||||
|
|
||||||
|
foreach (var registered in RegisteredDriverTypeNames())
|
||||||
|
{
|
||||||
|
constants.ShouldContain(registered,
|
||||||
|
$"driver factory registers '{registered}' but no DriverTypeNames constant has that value — " +
|
||||||
|
"add/rename the constant so dispatch maps can reference it instead of a drifting literal");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Every_constant_matches_a_registered_factory()
|
||||||
|
{
|
||||||
|
var registered = RegisteredDriverTypeNames();
|
||||||
|
|
||||||
|
foreach (var constant in DeclaredConstantValues())
|
||||||
|
{
|
||||||
|
registered.ShouldContain(constant,
|
||||||
|
$"DriverTypeNames has '{constant}' but no registered driver factory uses it — " +
|
||||||
|
"remove the constant or wire the factory (do not add constants for unregistered drivers)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Constants_and_registered_factories_are_exactly_the_same_set()
|
||||||
|
{
|
||||||
|
DeclaredConstantValues()
|
||||||
|
.OrderBy(s => s, StringComparer.Ordinal)
|
||||||
|
.ShouldBe(
|
||||||
|
RegisteredDriverTypeNames().OrderBy(s => s, StringComparer.Ordinal),
|
||||||
|
"DriverTypeNames must be exactly the set of registered driver-type strings — no drift, no extras, no gaps");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void All_collection_equals_the_declared_constants()
|
||||||
|
{
|
||||||
|
// Keeps the convenience `All` collection honest against the individual consts.
|
||||||
|
DriverTypeNames.All
|
||||||
|
.OrderBy(s => s, StringComparer.Ordinal)
|
||||||
|
.ShouldBe(
|
||||||
|
DeclaredConstantValues().OrderBy(s => s, StringComparer.Ordinal),
|
||||||
|
"DriverTypeNames.All must list every declared constant exactly once");
|
||||||
|
}
|
||||||
|
}
|
||||||
+12
@@ -21,6 +21,18 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\..\..\src\Core\ZB.MOM.WW.OtOpcUa.Core.Abstractions\ZB.MOM.WW.OtOpcUa.Core.Abstractions.csproj"/>
|
<ProjectReference Include="..\..\..\src\Core\ZB.MOM.WW.OtOpcUa.Core.Abstractions\ZB.MOM.WW.OtOpcUa.Core.Abstractions.csproj"/>
|
||||||
|
<!-- Core hosts DriverFactoryRegistry; the eight driver assemblies each ship the
|
||||||
|
*DriverFactoryExtensions.Register entry point the guard test drives to build the
|
||||||
|
authoritative registered-factory set (mirrors the Host's DriverFactoryBootstrap). -->
|
||||||
|
<ProjectReference Include="..\..\..\src\Core\ZB.MOM.WW.OtOpcUa.Core\ZB.MOM.WW.OtOpcUa.Core.csproj"/>
|
||||||
|
<ProjectReference Include="..\..\..\src\Drivers\ZB.MOM.WW.OtOpcUa.Driver.Modbus\ZB.MOM.WW.OtOpcUa.Driver.Modbus.csproj"/>
|
||||||
|
<ProjectReference Include="..\..\..\src\Drivers\ZB.MOM.WW.OtOpcUa.Driver.S7\ZB.MOM.WW.OtOpcUa.Driver.S7.csproj"/>
|
||||||
|
<ProjectReference Include="..\..\..\src\Drivers\ZB.MOM.WW.OtOpcUa.Driver.AbCip\ZB.MOM.WW.OtOpcUa.Driver.AbCip.csproj"/>
|
||||||
|
<ProjectReference Include="..\..\..\src\Drivers\ZB.MOM.WW.OtOpcUa.Driver.AbLegacy\ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.csproj"/>
|
||||||
|
<ProjectReference Include="..\..\..\src\Drivers\ZB.MOM.WW.OtOpcUa.Driver.TwinCAT\ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.csproj"/>
|
||||||
|
<ProjectReference Include="..\..\..\src\Drivers\ZB.MOM.WW.OtOpcUa.Driver.FOCAS\ZB.MOM.WW.OtOpcUa.Driver.FOCAS.csproj"/>
|
||||||
|
<ProjectReference Include="..\..\..\src\Drivers\ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient\ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.csproj"/>
|
||||||
|
<ProjectReference Include="..\..\..\src\Drivers\ZB.MOM.WW.OtOpcUa.Driver.Galaxy\ZB.MOM.WW.OtOpcUa.Driver.Galaxy.csproj"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
Reference in New Issue
Block a user