Files
lmxopcua/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/GalaxyDriverFactoryExtensions.cs
Joseph Doherty a25593a9c6 chore: organize solution into module folders (Core/Server/Drivers/Client/Tooling)
Group all 69 projects into category subfolders under src/ and tests/ so the
Rider Solution Explorer mirrors the module structure. Folders: Core, Server,
Drivers (with a nested Driver CLIs subfolder), Client, Tooling.

- Move every project folder on disk with git mv (history preserved as renames).
- Recompute relative paths in 57 .csproj files: cross-category ProjectReferences,
  the lib/ HintPath+None refs in Driver.Historian.Wonderware, and the external
  mxaccessgw refs in Driver.Galaxy and its test project.
- Rebuild ZB.MOM.WW.OtOpcUa.slnx with nested solution folders.
- Re-prefix project paths in functional scripts (e2e, compliance, smoke SQL,
  integration, install).

Build green (0 errors); unit tests pass. Docs left for a separate pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-17 01:55:28 -04:00

124 lines
5.6 KiB
C#

using System.Text.Json;
using System.Text.Json.Serialization;
using Microsoft.Extensions.Logging;
using ZB.MOM.WW.OtOpcUa.Core.Hosting;
using ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Config;
namespace ZB.MOM.WW.OtOpcUa.Driver.Galaxy;
/// <summary>
/// Static factory registration helper for <see cref="GalaxyDriver"/>. Mirrors
/// <c>GalaxyProxyDriverFactoryExtensions</c> / <c>ModbusDriverFactoryExtensions</c>.
/// Server's <c>Program.cs</c> calls <see cref="Register"/> once at startup; the driver
/// bootstrap pipeline materialises DriverInstance rows whose <c>DriverType</c> matches
/// <see cref="DriverTypeName"/> into live <see cref="GalaxyDriver"/> instances.
/// </summary>
/// <remarks>
/// The driver-type name <c>"GalaxyMxGateway"</c> is intentionally distinct from the
/// legacy proxy's <c>"Galaxy"</c> so both factories can be registered simultaneously
/// during parity testing (Phase 5). PR 4.W will add a server-side <c>Galaxy:Backend</c>
/// switch that materialises a Galaxy DriverInstance under one or the other type name.
/// </remarks>
public static class GalaxyDriverFactoryExtensions
{
public const string DriverTypeName = "GalaxyMxGateway";
public static void Register(DriverFactoryRegistry registry, ILoggerFactory? loggerFactory = null)
{
ArgumentNullException.ThrowIfNull(registry);
registry.Register(DriverTypeName, (id, json) => CreateInstance(id, json, loggerFactory));
}
/// <summary>Convenience for tests + standalone callers.</summary>
public static GalaxyDriver CreateInstance(string driverInstanceId, string driverConfigJson)
=> CreateInstance(driverInstanceId, driverConfigJson, loggerFactory: null);
public static GalaxyDriver CreateInstance(
string driverInstanceId, string driverConfigJson, ILoggerFactory? loggerFactory)
{
ArgumentException.ThrowIfNullOrWhiteSpace(driverInstanceId);
ArgumentException.ThrowIfNullOrWhiteSpace(driverConfigJson);
var dto = JsonSerializer.Deserialize<GalaxyDriverConfigDto>(driverConfigJson, JsonOptions)
?? throw new InvalidOperationException(
$"Galaxy driver config for '{driverInstanceId}' deserialised to null");
var options = new GalaxyDriverOptions(
Gateway: new GalaxyGatewayOptions(
Endpoint: dto.Gateway?.Endpoint
?? throw new InvalidOperationException(
$"Galaxy driver '{driverInstanceId}' missing required Gateway.Endpoint"),
ApiKeySecretRef: dto.Gateway.ApiKeySecretRef
?? throw new InvalidOperationException(
$"Galaxy driver '{driverInstanceId}' missing required Gateway.ApiKeySecretRef"),
UseTls: dto.Gateway.UseTls ?? true,
CaCertificatePath: dto.Gateway.CaCertificatePath,
ConnectTimeoutSeconds: dto.Gateway.ConnectTimeoutSeconds ?? 10,
DefaultCallTimeoutSeconds: dto.Gateway.DefaultCallTimeoutSeconds ?? 30,
StreamTimeoutSeconds: dto.Gateway.StreamTimeoutSeconds ?? 0),
MxAccess: new GalaxyMxAccessOptions(
ClientName: dto.MxAccess?.ClientName
?? throw new InvalidOperationException(
$"Galaxy driver '{driverInstanceId}' missing required MxAccess.ClientName"),
PublishingIntervalMs: dto.MxAccess.PublishingIntervalMs ?? 1000,
WriteUserId: dto.MxAccess.WriteUserId ?? 0,
EventPumpChannelCapacity: dto.MxAccess.EventPumpChannelCapacity ?? 50_000),
Repository: new GalaxyRepositoryOptions(
DiscoverPageSize: dto.Repository?.DiscoverPageSize ?? 5000,
WatchDeployEvents: dto.Repository?.WatchDeployEvents ?? true),
Reconnect: new GalaxyReconnectOptions(
InitialBackoffMs: dto.Reconnect?.InitialBackoffMs ?? 500,
MaxBackoffMs: dto.Reconnect?.MaxBackoffMs ?? 30_000,
ReplayOnSessionLost: dto.Reconnect?.ReplayOnSessionLost ?? true));
return new GalaxyDriver(driverInstanceId, options, loggerFactory?.CreateLogger<GalaxyDriver>());
}
private static readonly JsonSerializerOptions JsonOptions = new()
{
PropertyNameCaseInsensitive = true,
ReadCommentHandling = JsonCommentHandling.Skip,
AllowTrailingCommas = true,
};
internal sealed class GalaxyDriverConfigDto
{
public GatewayDto? Gateway { get; init; }
public MxAccessDto? MxAccess { get; init; }
public RepositoryDto? Repository { get; init; }
public ReconnectDto? Reconnect { get; init; }
}
internal sealed class GatewayDto
{
public string? Endpoint { get; init; }
public string? ApiKeySecretRef { get; init; }
public bool? UseTls { get; init; }
public string? CaCertificatePath { get; init; }
public int? ConnectTimeoutSeconds { get; init; }
public int? DefaultCallTimeoutSeconds { get; init; }
public int? StreamTimeoutSeconds { get; init; }
}
internal sealed class MxAccessDto
{
public string? ClientName { get; init; }
public int? PublishingIntervalMs { get; init; }
public int? WriteUserId { get; init; }
public int? EventPumpChannelCapacity { get; init; }
}
internal sealed class RepositoryDto
{
public int? DiscoverPageSize { get; init; }
public bool? WatchDeployEvents { get; init; }
}
internal sealed class ReconnectDto
{
public int? InitialBackoffMs { get; init; }
public int? MaxBackoffMs { get; init; }
public bool? ReplayOnSessionLost { get; init; }
}
}