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>
This commit is contained in:
Joseph Doherty
2026-05-17 01:55:28 -04:00
parent 69f02fed7f
commit a25593a9c6
1044 changed files with 365 additions and 343 deletions
@@ -0,0 +1,14 @@
using Avalonia.Threading;
namespace ZB.MOM.WW.OtOpcUa.Client.UI.Services;
/// <summary>
/// Dispatches actions to the Avalonia UI thread.
/// </summary>
public sealed class AvaloniaUiDispatcher : IUiDispatcher
{
public void Post(Action action)
{
Dispatcher.UIThread.Post(action);
}
}
@@ -0,0 +1,10 @@
namespace ZB.MOM.WW.OtOpcUa.Client.UI.Services;
/// <summary>
/// Loads and saves user settings.
/// </summary>
public interface ISettingsService
{
UserSettings Load();
void Save(UserSettings settings);
}
@@ -0,0 +1,12 @@
namespace ZB.MOM.WW.OtOpcUa.Client.UI.Services;
/// <summary>
/// Abstraction for dispatching actions to the UI thread.
/// </summary>
public interface IUiDispatcher
{
/// <summary>
/// Posts an action to be executed on the UI thread.
/// </summary>
void Post(Action action);
}
@@ -0,0 +1,51 @@
using System.Text.Json;
using ZB.MOM.WW.OtOpcUa.Client.Shared;
namespace ZB.MOM.WW.OtOpcUa.Client.UI.Services;
/// <summary>
/// Persists user settings to a JSON file under LocalApplicationData.
/// </summary>
public sealed class JsonSettingsService : ISettingsService
{
// ClientStoragePaths.GetRoot runs the one-shot legacy-folder migration so pre-#208
// developer boxes pick up their existing settings.json on first launch post-rename.
private static readonly string SettingsDir = ClientStoragePaths.GetRoot();
private static readonly string SettingsPath = Path.Combine(SettingsDir, "settings.json");
private static readonly JsonSerializerOptions JsonOptions = new()
{
WriteIndented = true
};
public UserSettings Load()
{
try
{
if (!File.Exists(SettingsPath))
return new UserSettings();
var json = File.ReadAllText(SettingsPath);
return JsonSerializer.Deserialize<UserSettings>(json, JsonOptions) ?? new UserSettings();
}
catch
{
return new UserSettings();
}
}
public void Save(UserSettings settings)
{
try
{
Directory.CreateDirectory(SettingsDir);
var json = JsonSerializer.Serialize(settings, JsonOptions);
File.WriteAllText(SettingsPath, json);
}
catch
{
// Best-effort save; don't crash the app
}
}
}
@@ -0,0 +1,13 @@
namespace ZB.MOM.WW.OtOpcUa.Client.UI.Services;
/// <summary>
/// Dispatcher that executes actions synchronously on the calling thread.
/// Used for unit testing where no UI thread is available.
/// </summary>
public sealed class SynchronousUiDispatcher : IUiDispatcher
{
public void Post(Action action)
{
action();
}
}
@@ -0,0 +1,59 @@
using ZB.MOM.WW.OtOpcUa.Client.Shared.Models;
namespace ZB.MOM.WW.OtOpcUa.Client.UI.Services;
/// <summary>
/// Persisted user connection settings.
/// </summary>
public sealed class UserSettings
{
/// <summary>
/// Gets or sets the last OPC UA endpoint URL entered by the user.
/// </summary>
public string EndpointUrl { get; set; } = "opc.tcp://localhost:4840";
/// <summary>
/// Gets or sets the persisted username for authenticated sessions.
/// </summary>
public string? Username { get; set; }
/// <summary>
/// Gets or sets the persisted password for authenticated sessions.
/// </summary>
public string? Password { get; set; }
/// <summary>
/// Gets or sets the transport security mode selected by the user.
/// </summary>
public SecurityMode SecurityMode { get; set; } = SecurityMode.None;
/// <summary>
/// Gets or sets the raw failover endpoint list entered in the UI.
/// </summary>
public string? FailoverUrls { get; set; }
/// <summary>
/// Gets or sets the persisted session timeout, in seconds, for new client connections.
/// </summary>
public int SessionTimeoutSeconds { get; set; } = 60;
/// <summary>
/// Gets or sets a value indicating whether untrusted server certificates should be auto-accepted.
/// </summary>
public bool AutoAcceptCertificates { get; set; } = true;
/// <summary>
/// Gets or sets the certificate store path used for client certificates and trust lists.
/// </summary>
public string? CertificateStorePath { get; set; }
/// <summary>
/// Gets or sets the node IDs that should be re-subscribed when the UI reconnects.
/// </summary>
public List<string> SubscribedNodes { get; set; } = [];
/// <summary>
/// Gets or sets the alarm source node that should be restored when the UI reconnects.
/// </summary>
public string? AlarmSourceNodeId { get; set; }
}