Phase 0 — mechanical rename ZB.MOM.WW.LmxOpcUa.* → ZB.MOM.WW.OtOpcUa.*
Renames all 11 projects (5 src + 6 tests), the .slnx solution file, all source-file namespaces, all axaml namespace references, and all v1 documentation references in CLAUDE.md and docs/*.md (excluding docs/v2/ which is already in OtOpcUa form). Also updates the TopShelf service registration name from "LmxOpcUa" to "OtOpcUa" per Phase 0 Task 0.6.
Preserves runtime identifiers per Phase 0 Out-of-Scope rules to avoid breaking v1/v2 client trust during coexistence: OPC UA `ApplicationUri` defaults (`urn:{GalaxyName}:LmxOpcUa`), server `EndpointPath` (`/LmxOpcUa`), `ServerName` default (feeds cert subject CN), `MxAccessConfiguration.ClientName` default (defensive — stays "LmxOpcUa" for MxAccess audit-trail consistency), client OPC UA identifiers (`ApplicationName = "LmxOpcUaClient"`, `ApplicationUri = "urn:localhost:LmxOpcUaClient"`, cert directory `%LocalAppData%\LmxOpcUaClient\pki\`), and the `LmxOpcUaServer` class name (class rename out of Phase 0 scope per Task 0.5 sed pattern; happens in Phase 1 alongside `LmxNodeManager → GenericDriverNodeManager` Core extraction). 23 LmxOpcUa references retained, all enumerated and justified in `docs/v2/implementation/exit-gate-phase-0.md`.
Build clean: 0 errors, 30 warnings (lower than baseline 167). Tests at strict improvement over baseline: 821 passing / 1 failing vs baseline 820 / 2 (one flaky pre-existing failure passed this run; the other still fails — both pre-existing and unrelated to the rename). `Client.UI.Tests`, `Historian.Aveva.Tests`, `Client.Shared.Tests`, `IntegrationTests` all match baseline exactly. Exit gate compliance results recorded in `docs/v2/implementation/exit-gate-phase-0.md` with all 7 checks PASS or DEFERRED-to-PR-review (#7 service install verification needs Windows service permissions on the reviewer's box).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -78,5 +78,5 @@ If no previous state is cached (first build), the full `BuildAddressSpace` path
|
||||
|
||||
## Key source files
|
||||
|
||||
- `src/ZB.MOM.WW.LmxOpcUa.Host/OpcUa/LmxNodeManager.cs` -- Node manager with `BuildAddressSpace`, `SyncAddressSpace`, and `TopologicalSort`
|
||||
- `src/ZB.MOM.WW.LmxOpcUa.Host/OpcUa/AddressSpaceBuilder.cs` -- Testable in-memory model builder
|
||||
- `src/ZB.MOM.WW.OtOpcUa.Host/OpcUa/LmxNodeManager.cs` -- Node manager with `BuildAddressSpace`, `SyncAddressSpace`, and `TopologicalSort`
|
||||
- `src/ZB.MOM.WW.OtOpcUa.Host/OpcUa/AddressSpaceBuilder.cs` -- Testable in-memory model builder
|
||||
|
||||
@@ -2,14 +2,14 @@
|
||||
|
||||
## Overview
|
||||
|
||||
`ZB.MOM.WW.LmxOpcUa.Client.CLI` is a cross-platform command-line client for the LmxOpcUa OPC UA server. It targets .NET 10 and uses the shared `IOpcUaClientService` from `Client.Shared` for all OPC UA operations. Commands are routed and parsed by [CliFx](https://github.com/Tyrrrz/CliFx).
|
||||
`ZB.MOM.WW.OtOpcUa.Client.CLI` is a cross-platform command-line client for the LmxOpcUa OPC UA server. It targets .NET 10 and uses the shared `IOpcUaClientService` from `Client.Shared` for all OPC UA operations. Commands are routed and parsed by [CliFx](https://github.com/Tyrrrz/CliFx).
|
||||
|
||||
The CLI is the primary tool for operators and developers to test and interact with the server from a terminal. It supports all core operations: connectivity testing, browsing, reading, writing, subscriptions, alarm monitoring, history reads, and redundancy queries.
|
||||
|
||||
## Build and Run
|
||||
|
||||
```bash
|
||||
cd src/ZB.MOM.WW.LmxOpcUa.Client.CLI
|
||||
cd src/ZB.MOM.WW.OtOpcUa.Client.CLI
|
||||
dotnet build
|
||||
dotnet run -- <command> [options]
|
||||
```
|
||||
@@ -240,5 +240,5 @@ Application URI: urn:localhost:LmxOpcUa:instance1
|
||||
The Client CLI has 52 unit tests covering option parsing, service invocation, output formatting, and cleanup behavior:
|
||||
|
||||
```bash
|
||||
dotnet test tests/ZB.MOM.WW.LmxOpcUa.Client.CLI.Tests
|
||||
dotnet test tests/ZB.MOM.WW.OtOpcUa.Client.CLI.Tests
|
||||
```
|
||||
|
||||
@@ -2,14 +2,14 @@
|
||||
|
||||
## Overview
|
||||
|
||||
`ZB.MOM.WW.LmxOpcUa.Client.UI` is a cross-platform Avalonia desktop application for connecting to and interacting with the LmxOpcUa OPC UA server. It targets .NET 10 and uses the shared `IOpcUaClientService` from `Client.Shared` for all OPC UA operations.
|
||||
`ZB.MOM.WW.OtOpcUa.Client.UI` is a cross-platform Avalonia desktop application for connecting to and interacting with the LmxOpcUa OPC UA server. It targets .NET 10 and uses the shared `IOpcUaClientService` from `Client.Shared` for all OPC UA operations.
|
||||
|
||||
The UI provides a single-window interface for browsing the address space, reading and writing values, monitoring live subscriptions, managing alarms, and querying historical data.
|
||||
|
||||
## Build and Run
|
||||
|
||||
```bash
|
||||
cd src/ZB.MOM.WW.LmxOpcUa.Client.UI
|
||||
cd src/ZB.MOM.WW.OtOpcUa.Client.UI
|
||||
dotnet build
|
||||
dotnet run
|
||||
```
|
||||
@@ -254,7 +254,7 @@ All service event handlers (data changes, alarm events, connection state changes
|
||||
The UI has 102 unit tests covering ViewModel logic and headless rendering:
|
||||
|
||||
```bash
|
||||
dotnet test tests/ZB.MOM.WW.LmxOpcUa.Client.UI.Tests
|
||||
dotnet test tests/ZB.MOM.WW.OtOpcUa.Client.UI.Tests
|
||||
```
|
||||
|
||||
Tests use:
|
||||
|
||||
@@ -242,7 +242,7 @@ Three boolean properties act as feature flags that control optional subsystems:
|
||||
|
||||
- **`OpcUa.AlarmTrackingEnabled`** -- When `true`, the node manager creates `AlarmConditionState` nodes for alarm attributes and monitors `InAlarm` transitions. Disabled by default because alarm tracking adds per-attribute overhead.
|
||||
- **`OpcUa.AlarmFilter.ObjectFilters`** -- List of wildcard template-name patterns that scope alarm tracking to matching objects and their descendants. An empty list preserves the current unfiltered behavior; a non-empty list includes an object only when any name in its template derivation chain matches any pattern, then propagates the inclusion to every descendant in the containment hierarchy. `*` is the only wildcard, matching is case-insensitive, and the Galaxy `$` prefix on template names is normalized so operators can write `TestMachine*` instead of `$TestMachine*`. Each list entry may itself contain comma-separated patterns (`"TestMachine*, Pump_*"`) for convenience. When the list is non-empty but `AlarmTrackingEnabled` is `false`, the validator emits a warning because the filter has no effect. See [Alarm Tracking](AlarmTracking.md#template-based-alarm-object-filter) for the full matching algorithm and telemetry.
|
||||
- **`Historian.Enabled`** -- When `true`, the service calls `HistorianPluginLoader.TryLoad(config)` to load the `ZB.MOM.WW.LmxOpcUa.Historian.Aveva` plugin from the `Historian/` subfolder next to the host exe and registers the resulting `IHistorianDataSource` with the OPC UA server host. Disabled by default because not all deployments have a Historian instance -- when disabled the plugin is not probed and the Wonderware SDK DLLs are not required on the host. If the flag is `true` but the plugin or its SDK dependencies cannot be loaded, the server still starts and every history read returns `BadHistoryOperationUnsupported` with a warning in the log.
|
||||
- **`Historian.Enabled`** -- When `true`, the service calls `HistorianPluginLoader.TryLoad(config)` to load the `ZB.MOM.WW.OtOpcUa.Historian.Aveva` plugin from the `Historian/` subfolder next to the host exe and registers the resulting `IHistorianDataSource` with the OPC UA server host. Disabled by default because not all deployments have a Historian instance -- when disabled the plugin is not probed and the Wonderware SDK DLLs are not required on the host. If the flag is `true` but the plugin or its SDK dependencies cannot be loaded, the server still starts and every history read returns `BadHistoryOperationUnsupported` with a warning in the log.
|
||||
- **`GalaxyRepository.ExtendedAttributes`** -- When `true`, the repository loads additional Galaxy attribute metadata beyond the core set needed for the address space. Disabled by default to minimize startup query time.
|
||||
- **`GalaxyRepository.Scope`** -- When set to `LocalPlatform`, the repository filters the hierarchy and attributes to only include objects hosted by the platform whose `node_name` matches this machine (or the explicit `PlatformName` override). Ancestor areas are retained to keep the browse tree connected. Default is `Galaxy` (load everything). See [Galaxy Repository — Platform Scope Filter](GalaxyRepository.md#platform-scope-filter).
|
||||
|
||||
|
||||
@@ -79,6 +79,6 @@ For historized attributes, `AccessLevels.HistoryRead` is added to the access lev
|
||||
|
||||
## Key source files
|
||||
|
||||
- `src/ZB.MOM.WW.LmxOpcUa.Host/Domain/MxDataTypeMapper.cs` -- Type and CLR mapping
|
||||
- `src/ZB.MOM.WW.LmxOpcUa.Host/Domain/SecurityClassificationMapper.cs` -- Write access mapping
|
||||
- `src/ZB.MOM.WW.OtOpcUa.Host/Domain/MxDataTypeMapper.cs` -- Type and CLR mapping
|
||||
- `src/ZB.MOM.WW.OtOpcUa.Host/Domain/SecurityClassificationMapper.cs` -- Write access mapping
|
||||
- `gr/data_type_mapping.md` -- Reference documentation for the full mapping table
|
||||
|
||||
@@ -136,8 +136,8 @@ The polling approach is used because the Galaxy Repository database does not pro
|
||||
|
||||
## Key source files
|
||||
|
||||
- `src/ZB.MOM.WW.LmxOpcUa.Host/GalaxyRepository/GalaxyRepositoryService.cs` -- SQL queries and data access
|
||||
- `src/ZB.MOM.WW.LmxOpcUa.Host/GalaxyRepository/PlatformScopeFilter.cs` -- Platform-based hierarchy and attribute filtering
|
||||
- `src/ZB.MOM.WW.LmxOpcUa.Host/GalaxyRepository/ChangeDetectionService.cs` -- Deploy timestamp polling loop
|
||||
- `src/ZB.MOM.WW.LmxOpcUa.Host/Configuration/GalaxyRepositoryConfiguration.cs` -- Connection, polling, and scope settings
|
||||
- `src/ZB.MOM.WW.LmxOpcUa.Host/Domain/PlatformInfo.cs` -- Platform-to-hostname DTO
|
||||
- `src/ZB.MOM.WW.OtOpcUa.Host/GalaxyRepository/GalaxyRepositoryService.cs` -- SQL queries and data access
|
||||
- `src/ZB.MOM.WW.OtOpcUa.Host/GalaxyRepository/PlatformScopeFilter.cs` -- Platform-based hierarchy and attribute filtering
|
||||
- `src/ZB.MOM.WW.OtOpcUa.Host/GalaxyRepository/ChangeDetectionService.cs` -- Deploy timestamp polling loop
|
||||
- `src/ZB.MOM.WW.OtOpcUa.Host/Configuration/GalaxyRepositoryConfiguration.cs` -- Connection, polling, and scope settings
|
||||
- `src/ZB.MOM.WW.OtOpcUa.Host/Domain/PlatformInfo.cs` -- Platform-to-hostname DTO
|
||||
|
||||
@@ -1,29 +1,29 @@
|
||||
# Historical Data Access
|
||||
|
||||
`LmxNodeManager` exposes OPC UA historical data access (HDA) through an abstract `IHistorianDataSource` interface (`Historian/IHistorianDataSource.cs`). The Wonderware Historian implementation lives in a separate assembly, `ZB.MOM.WW.LmxOpcUa.Historian.Aveva`, which is loaded at runtime only when `Historian.Enabled=true`. This keeps the `aahClientManaged` SDK out of the core Host so deployments that do not need history do not need the SDK installed.
|
||||
`LmxNodeManager` exposes OPC UA historical data access (HDA) through an abstract `IHistorianDataSource` interface (`Historian/IHistorianDataSource.cs`). The Wonderware Historian implementation lives in a separate assembly, `ZB.MOM.WW.OtOpcUa.Historian.Aveva`, which is loaded at runtime only when `Historian.Enabled=true`. This keeps the `aahClientManaged` SDK out of the core Host so deployments that do not need history do not need the SDK installed.
|
||||
|
||||
## Plugin Architecture
|
||||
|
||||
The historian surface is split across two assemblies:
|
||||
|
||||
- **`ZB.MOM.WW.LmxOpcUa.Host`** (core) owns only OPC UA / BCL types:
|
||||
- **`ZB.MOM.WW.OtOpcUa.Host`** (core) owns only OPC UA / BCL types:
|
||||
- `IHistorianDataSource` -- the interface `LmxNodeManager` depends on
|
||||
- `HistorianEventDto` -- SDK-free representation of a historian event record
|
||||
- `HistorianAggregateMap` -- maps OPC UA aggregate NodeIds to AnalogSummary column names
|
||||
- `HistorianPluginLoader` -- loads the plugin via `Assembly.LoadFrom` at startup
|
||||
- `HistoryContinuationPointManager` -- paginates HistoryRead results
|
||||
- **`ZB.MOM.WW.LmxOpcUa.Historian.Aveva`** (plugin) owns everything SDK-bound:
|
||||
- **`ZB.MOM.WW.OtOpcUa.Historian.Aveva`** (plugin) owns everything SDK-bound:
|
||||
- `HistorianDataSource` -- implements `IHistorianDataSource`, wraps `aahClientManaged`
|
||||
- `IHistorianConnectionFactory` / `SdkHistorianConnectionFactory` -- opens and polls `ArchestrA.HistorianAccess` connections
|
||||
- `AvevaHistorianPluginEntry.Create(HistorianConfiguration)` -- the static factory invoked by the loader
|
||||
|
||||
The plugin assembly and its SDK dependencies (`aahClientManaged.dll`, `aahClient.dll`, `aahClientCommon.dll`, `Historian.CBE.dll`, `Historian.DPAPI.dll`, `ArchestrA.CloudHistorian.Contract.dll`) deploy to a `Historian/` subfolder next to `ZB.MOM.WW.LmxOpcUa.Host.exe`. See [Service Hosting](ServiceHosting.md#required-runtime-assemblies) for the full layout and deployment matrix.
|
||||
The plugin assembly and its SDK dependencies (`aahClientManaged.dll`, `aahClient.dll`, `aahClientCommon.dll`, `Historian.CBE.dll`, `Historian.DPAPI.dll`, `ArchestrA.CloudHistorian.Contract.dll`) deploy to a `Historian/` subfolder next to `ZB.MOM.WW.OtOpcUa.Host.exe`. See [Service Hosting](ServiceHosting.md#required-runtime-assemblies) for the full layout and deployment matrix.
|
||||
|
||||
## Plugin Loading
|
||||
|
||||
When the service starts with `Historian.Enabled=true`, `OpcUaService` calls `HistorianPluginLoader.TryLoad(config)`. The loader:
|
||||
|
||||
1. Probes `AppDomain.CurrentDomain.BaseDirectory\Historian\ZB.MOM.WW.LmxOpcUa.Historian.Aveva.dll`.
|
||||
1. Probes `AppDomain.CurrentDomain.BaseDirectory\Historian\ZB.MOM.WW.OtOpcUa.Historian.Aveva.dll`.
|
||||
2. Installs a one-shot `AppDomain.AssemblyResolve` handler that redirects any `aahClientManaged`/`aahClientCommon`/`Historian.*` lookups to the same subfolder, so the CLR can resolve SDK dependencies when the plugin first JITs.
|
||||
3. Calls the plugin's `AvevaHistorianPluginEntry.Create(HistorianConfiguration)` via reflection and returns the resulting `IHistorianDataSource`.
|
||||
4. On any failure (plugin missing, entry type not found, SDK assembly unresolvable, bad image), logs a warning with the expected plugin path and returns `null`. The server starts normally and `LmxNodeManager` returns `BadHistoryOperationUnsupported` for every history call.
|
||||
@@ -35,7 +35,7 @@ The plugin uses the AVEVA Historian managed SDK (`aahClientManaged.dll`) to quer
|
||||
- **`HistoryQuery`** -- Raw historical samples with timestamp, value (numeric or string), and OPC quality.
|
||||
- **`AnalogSummaryQuery`** -- Pre-computed aggregates with properties for Average, Minimum, Maximum, ValueCount, First, Last, StdDev, and more.
|
||||
|
||||
The SDK DLLs are located in `lib/` and originate from `C:\Program Files (x86)\Wonderware\Historian\`. Only the plugin project (`src/ZB.MOM.WW.LmxOpcUa.Historian.Aveva/`) references them at build time; the core Host project does not.
|
||||
The SDK DLLs are located in `lib/` and originate from `C:\Program Files (x86)\Wonderware\Historian\`. Only the plugin project (`src/ZB.MOM.WW.OtOpcUa.Historian.Aveva/`) references them at build time; the core Host project does not.
|
||||
|
||||
## Configuration
|
||||
|
||||
|
||||
@@ -152,15 +152,15 @@ The .NET runtime's garbage collector releases COM references non-deterministical
|
||||
|
||||
## Key source files
|
||||
|
||||
- `src/ZB.MOM.WW.LmxOpcUa.Host/MxAccess/StaComThread.cs` -- STA thread and Win32 message pump
|
||||
- `src/ZB.MOM.WW.LmxOpcUa.Host/MxAccess/MxAccessClient.cs` -- Core client class (partial)
|
||||
- `src/ZB.MOM.WW.LmxOpcUa.Host/MxAccess/MxAccessClient.Connection.cs` -- Connect, disconnect, reconnect
|
||||
- `src/ZB.MOM.WW.LmxOpcUa.Host/MxAccess/MxAccessClient.Subscription.cs` -- Subscribe, unsubscribe, replay
|
||||
- `src/ZB.MOM.WW.LmxOpcUa.Host/MxAccess/MxAccessClient.ReadWrite.cs` -- Read and write operations
|
||||
- `src/ZB.MOM.WW.LmxOpcUa.Host/MxAccess/MxAccessClient.EventHandlers.cs` -- OnDataChange and OnWriteComplete handlers
|
||||
- `src/ZB.MOM.WW.LmxOpcUa.Host/MxAccess/MxAccessClient.Monitor.cs` -- Background health monitor
|
||||
- `src/ZB.MOM.WW.LmxOpcUa.Host/MxAccess/MxProxyAdapter.cs` -- COM object wrapper
|
||||
- `src/ZB.MOM.WW.LmxOpcUa.Host/MxAccess/GalaxyRuntimeProbeManager.cs` -- Per-host `ScanState` probes, state machine, `IsHostStopped` lookup
|
||||
- `src/ZB.MOM.WW.LmxOpcUa.Host/Domain/GalaxyRuntimeStatus.cs` -- Per-host DTO
|
||||
- `src/ZB.MOM.WW.LmxOpcUa.Host/Domain/GalaxyRuntimeState.cs` -- `Unknown` / `Running` / `Stopped` enum
|
||||
- `src/ZB.MOM.WW.LmxOpcUa.Host/Domain/IMxAccessClient.cs` -- Client interface
|
||||
- `src/ZB.MOM.WW.OtOpcUa.Host/MxAccess/StaComThread.cs` -- STA thread and Win32 message pump
|
||||
- `src/ZB.MOM.WW.OtOpcUa.Host/MxAccess/MxAccessClient.cs` -- Core client class (partial)
|
||||
- `src/ZB.MOM.WW.OtOpcUa.Host/MxAccess/MxAccessClient.Connection.cs` -- Connect, disconnect, reconnect
|
||||
- `src/ZB.MOM.WW.OtOpcUa.Host/MxAccess/MxAccessClient.Subscription.cs` -- Subscribe, unsubscribe, replay
|
||||
- `src/ZB.MOM.WW.OtOpcUa.Host/MxAccess/MxAccessClient.ReadWrite.cs` -- Read and write operations
|
||||
- `src/ZB.MOM.WW.OtOpcUa.Host/MxAccess/MxAccessClient.EventHandlers.cs` -- OnDataChange and OnWriteComplete handlers
|
||||
- `src/ZB.MOM.WW.OtOpcUa.Host/MxAccess/MxAccessClient.Monitor.cs` -- Background health monitor
|
||||
- `src/ZB.MOM.WW.OtOpcUa.Host/MxAccess/MxProxyAdapter.cs` -- COM object wrapper
|
||||
- `src/ZB.MOM.WW.OtOpcUa.Host/MxAccess/GalaxyRuntimeProbeManager.cs` -- Per-host `ScanState` probes, state machine, `IsHostStopped` lookup
|
||||
- `src/ZB.MOM.WW.OtOpcUa.Host/Domain/GalaxyRuntimeStatus.cs` -- Per-host DTO
|
||||
- `src/ZB.MOM.WW.OtOpcUa.Host/Domain/GalaxyRuntimeState.cs` -- `Unknown` / `Running` / `Stopped` enum
|
||||
- `src/ZB.MOM.WW.OtOpcUa.Host/Domain/IMxAccessClient.cs` -- Client interface
|
||||
|
||||
@@ -130,8 +130,8 @@ On startup, `OpcUaServerHost.StartAsync` calls `CheckApplicationInstanceCertific
|
||||
|
||||
## Key source files
|
||||
|
||||
- `src/ZB.MOM.WW.LmxOpcUa.Host/OpcUa/OpcUaServerHost.cs` -- Application lifecycle and programmatic configuration
|
||||
- `src/ZB.MOM.WW.LmxOpcUa.Host/OpcUa/LmxOpcUaServer.cs` -- StandardServer subclass and node manager creation
|
||||
- `src/ZB.MOM.WW.LmxOpcUa.Host/OpcUa/SecurityProfileResolver.cs` -- Profile-name to ServerSecurityPolicy mapping
|
||||
- `src/ZB.MOM.WW.LmxOpcUa.Host/Configuration/OpcUaConfiguration.cs` -- Configuration POCO
|
||||
- `src/ZB.MOM.WW.LmxOpcUa.Host/Configuration/SecurityProfileConfiguration.cs` -- Security configuration POCO
|
||||
- `src/ZB.MOM.WW.OtOpcUa.Host/OpcUa/OpcUaServerHost.cs` -- Application lifecycle and programmatic configuration
|
||||
- `src/ZB.MOM.WW.OtOpcUa.Host/OpcUa/LmxOpcUaServer.cs` -- StandardServer subclass and node manager creation
|
||||
- `src/ZB.MOM.WW.OtOpcUa.Host/OpcUa/SecurityProfileResolver.cs` -- Profile-name to ServerSecurityPolicy mapping
|
||||
- `src/ZB.MOM.WW.OtOpcUa.Host/Configuration/OpcUaConfiguration.cs` -- Configuration POCO
|
||||
- `src/ZB.MOM.WW.OtOpcUa.Host/Configuration/SecurityProfileConfiguration.cs` -- Security configuration POCO
|
||||
|
||||
@@ -138,8 +138,8 @@ When deploying a redundant pair, the following configuration properties must dif
|
||||
The Client CLI includes a `redundancy` command that reads the redundancy state from a running server.
|
||||
|
||||
```bash
|
||||
dotnet run --project src/ZB.MOM.WW.LmxOpcUa.Client.CLI -- redundancy -u opc.tcp://localhost:4840/LmxOpcUa
|
||||
dotnet run --project src/ZB.MOM.WW.LmxOpcUa.Client.CLI -- redundancy -u opc.tcp://localhost:4841/LmxOpcUa
|
||||
dotnet run --project src/ZB.MOM.WW.OtOpcUa.Client.CLI -- redundancy -u opc.tcp://localhost:4840/LmxOpcUa
|
||||
dotnet run --project src/ZB.MOM.WW.OtOpcUa.Client.CLI -- redundancy -u opc.tcp://localhost:4841/LmxOpcUa
|
||||
```
|
||||
|
||||
The command reads the following standard OPC UA nodes and displays their values:
|
||||
|
||||
@@ -32,11 +32,11 @@ TopShelf provides these deployment modes from the same executable:
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `LmxOpcUa.Host.exe` | Run as a console application (foreground) |
|
||||
| `LmxOpcUa.Host.exe install` | Install as a Windows service |
|
||||
| `LmxOpcUa.Host.exe uninstall` | Remove the Windows service |
|
||||
| `LmxOpcUa.Host.exe start` | Start the installed service |
|
||||
| `LmxOpcUa.Host.exe stop` | Stop the installed service |
|
||||
| `OtOpcUa.Host.exe` | Run as a console application (foreground) |
|
||||
| `OtOpcUa.Host.exe install` | Install as a Windows service |
|
||||
| `OtOpcUa.Host.exe uninstall` | Remove the Windows service |
|
||||
| `OtOpcUa.Host.exe start` | Start the installed service |
|
||||
| `OtOpcUa.Host.exe stop` | Stop the installed service |
|
||||
|
||||
The service is configured to run as `LocalSystem` and start automatically on boot.
|
||||
|
||||
@@ -146,26 +146,26 @@ Install additional instances using TopShelf's `-servicename` flag:
|
||||
|
||||
```bash
|
||||
cd C:\publish\lmxopcua\instance2
|
||||
ZB.MOM.WW.LmxOpcUa.Host.exe install -servicename "LmxOpcUa2" -displayname "LMX OPC UA Server (Instance 2)"
|
||||
ZB.MOM.WW.OtOpcUa.Host.exe install -servicename "LmxOpcUa2" -displayname "LMX OPC UA Server (Instance 2)"
|
||||
```
|
||||
|
||||
See [Redundancy Guide](Redundancy.md) for full deployment details.
|
||||
|
||||
## Required Runtime Assemblies
|
||||
|
||||
The build uses Costura.Fody to embed all NuGet dependencies into the single `ZB.MOM.WW.LmxOpcUa.Host.exe`. The only native dependency that must sit alongside the executable in every deployment is the MXAccess COM toolkit:
|
||||
The build uses Costura.Fody to embed all NuGet dependencies into the single `ZB.MOM.WW.OtOpcUa.Host.exe`. The only native dependency that must sit alongside the executable in every deployment is the MXAccess COM toolkit:
|
||||
|
||||
| Assembly | Purpose |
|
||||
|----------|---------|
|
||||
| `ArchestrA.MxAccess.dll` | MXAccess COM interop — runtime data access to Galaxy tags |
|
||||
|
||||
The Wonderware Historian SDK is packaged as a **runtime-loaded plugin** so hosts that will not use historical data access do not need the SDK installed. The plugin lives in a `Historian/` subfolder next to `ZB.MOM.WW.LmxOpcUa.Host.exe`:
|
||||
The Wonderware Historian SDK is packaged as a **runtime-loaded plugin** so hosts that will not use historical data access do not need the SDK installed. The plugin lives in a `Historian/` subfolder next to `ZB.MOM.WW.OtOpcUa.Host.exe`:
|
||||
|
||||
```
|
||||
ZB.MOM.WW.LmxOpcUa.Host.exe
|
||||
ZB.MOM.WW.OtOpcUa.Host.exe
|
||||
ArchestrA.MxAccess.dll
|
||||
Historian/
|
||||
ZB.MOM.WW.LmxOpcUa.Historian.Aveva.dll
|
||||
ZB.MOM.WW.OtOpcUa.Historian.Aveva.dll
|
||||
aahClientManaged.dll
|
||||
aahClientCommon.dll
|
||||
aahClient.dll
|
||||
@@ -174,7 +174,7 @@ Historian/
|
||||
ArchestrA.CloudHistorian.Contract.dll
|
||||
```
|
||||
|
||||
At startup, if `Historian.Enabled=true` in `appsettings.json`, `HistorianPluginLoader` probes `Historian/ZB.MOM.WW.LmxOpcUa.Historian.Aveva.dll` via `Assembly.LoadFrom` and instantiates the plugin's entry point. An `AppDomain.AssemblyResolve` handler redirects the SDK assembly lookups (`aahClientManaged`, `aahClientCommon`, …) to the same subfolder so the CLR can resolve them when the plugin first JITs. If the plugin directory is absent or any SDK dependency fails to load, the loader logs a warning and the server continues to run with history support disabled — `LmxNodeManager` returns `BadHistoryOperationUnsupported` for every history call.
|
||||
At startup, if `Historian.Enabled=true` in `appsettings.json`, `HistorianPluginLoader` probes `Historian/ZB.MOM.WW.OtOpcUa.Historian.Aveva.dll` via `Assembly.LoadFrom` and instantiates the plugin's entry point. An `AppDomain.AssemblyResolve` handler redirects the SDK assembly lookups (`aahClientManaged`, `aahClientCommon`, …) to the same subfolder so the CLR can resolve them when the plugin first JITs. If the plugin directory is absent or any SDK dependency fails to load, the loader logs a warning and the server continues to run with history support disabled — `LmxNodeManager` returns `BadHistoryOperationUnsupported` for every history call.
|
||||
|
||||
Deployment matrix:
|
||||
|
||||
|
||||
@@ -8,12 +8,12 @@ Three new .NET 10 cross-platform projects providing a shared OPC UA client libra
|
||||
|
||||
| Project | Type | Purpose |
|
||||
|---------|------|---------|
|
||||
| `ZB.MOM.WW.LmxOpcUa.Client.Shared` | Class library | Core OPC UA client, models, interfaces |
|
||||
| `ZB.MOM.WW.LmxOpcUa.Client.CLI` | Console app | Command-line interface using CliFx |
|
||||
| `ZB.MOM.WW.LmxOpcUa.Client.UI` | Avalonia app | Desktop UI with tree browser, subscriptions, alarms |
|
||||
| `ZB.MOM.WW.LmxOpcUa.Client.Shared.Tests` | Test project | Unit tests for shared library |
|
||||
| `ZB.MOM.WW.LmxOpcUa.Client.CLI.Tests` | Test project | Unit tests for CLI commands |
|
||||
| `ZB.MOM.WW.LmxOpcUa.Client.UI.Tests` | Test project | Unit tests for UI view models |
|
||||
| `ZB.MOM.WW.OtOpcUa.Client.Shared` | Class library | Core OPC UA client, models, interfaces |
|
||||
| `ZB.MOM.WW.OtOpcUa.Client.CLI` | Console app | Command-line interface using CliFx |
|
||||
| `ZB.MOM.WW.OtOpcUa.Client.UI` | Avalonia app | Desktop UI with tree browser, subscriptions, alarms |
|
||||
| `ZB.MOM.WW.OtOpcUa.Client.Shared.Tests` | Test project | Unit tests for shared library |
|
||||
| `ZB.MOM.WW.OtOpcUa.Client.CLI.Tests` | Test project | Unit tests for CLI commands |
|
||||
| `ZB.MOM.WW.OtOpcUa.Client.UI.Tests` | Test project | Unit tests for UI view models |
|
||||
|
||||
## Technology Stack
|
||||
|
||||
|
||||
@@ -9,8 +9,8 @@ The application shall use TopShelf for Windows service lifecycle (install, unins
|
||||
### Acceptance Criteria
|
||||
|
||||
- TopShelf HostFactory configures the service with name `LmxOpcUa`, display name `LMX OPC UA Server`.
|
||||
- Service installs via command line: `ZB.MOM.WW.LmxOpcUa.Host.exe install`.
|
||||
- Service uninstalls via: `ZB.MOM.WW.LmxOpcUa.Host.exe uninstall`.
|
||||
- Service installs via command line: `ZB.MOM.WW.OtOpcUa.Host.exe install`.
|
||||
- Service uninstalls via: `ZB.MOM.WW.OtOpcUa.Host.exe uninstall`.
|
||||
- Service runs as LocalSystem account (needed for MXAccess COM access and Windows Auth to SQL Server).
|
||||
- Interactive console mode (exe with no args) works for development/debugging.
|
||||
- `StartAutomatically` is set for Windows service registration.
|
||||
|
||||
@@ -110,7 +110,7 @@ The dashboard shall display a footer with last-updated time and service identifi
|
||||
|
||||
### Acceptance Criteria
|
||||
|
||||
- Format: "Last updated: {timestamp} UTC | Service: ZB.MOM.WW.LmxOpcUa.Host v{version}".
|
||||
- Format: "Last updated: {timestamp} UTC | Service: ZB.MOM.WW.OtOpcUa.Host v{version}".
|
||||
- Timestamp is the server-side UTC time when the HTML was generated.
|
||||
- Version is read from the assembly version (`Assembly.GetExecutingAssembly().GetName().Version`).
|
||||
|
||||
|
||||
@@ -211,19 +211,19 @@ The Client CLI supports the `-S` (or `--security`) flag to select the transport
|
||||
### Connect with no security
|
||||
|
||||
```bash
|
||||
dotnet run --project src/ZB.MOM.WW.LmxOpcUa.Client.CLI -- connect -u opc.tcp://localhost:4840/LmxOpcUa -S none
|
||||
dotnet run --project src/ZB.MOM.WW.OtOpcUa.Client.CLI -- connect -u opc.tcp://localhost:4840/LmxOpcUa -S none
|
||||
```
|
||||
|
||||
### Connect with signing
|
||||
|
||||
```bash
|
||||
dotnet run --project src/ZB.MOM.WW.LmxOpcUa.Client.CLI -- connect -u opc.tcp://localhost:4840/LmxOpcUa -S sign
|
||||
dotnet run --project src/ZB.MOM.WW.OtOpcUa.Client.CLI -- connect -u opc.tcp://localhost:4840/LmxOpcUa -S sign
|
||||
```
|
||||
|
||||
### Connect with signing and encryption
|
||||
|
||||
```bash
|
||||
dotnet run --project src/ZB.MOM.WW.LmxOpcUa.Client.CLI -- connect -u opc.tcp://localhost:4840/LmxOpcUa -S encrypt
|
||||
dotnet run --project src/ZB.MOM.WW.OtOpcUa.Client.CLI -- connect -u opc.tcp://localhost:4840/LmxOpcUa -S encrypt
|
||||
```
|
||||
|
||||
### Browse with encryption and authentication
|
||||
|
||||
119
docs/v2/implementation/exit-gate-phase-0.md
Normal file
119
docs/v2/implementation/exit-gate-phase-0.md
Normal file
@@ -0,0 +1,119 @@
|
||||
# Phase 0 — Exit Gate Record
|
||||
|
||||
**Phase**: 0 — Rename + .NET 10 cleanup
|
||||
**Branch**: `phase-0-rename`
|
||||
**Date**: 2026-04-17
|
||||
**Implementation lead**: Claude (executing on behalf of dohertj2)
|
||||
**Reviewer**: pending — PR review required before merge
|
||||
|
||||
## Compliance check results
|
||||
|
||||
### 1. No stale `LmxOpcUa` references (with allowlist)
|
||||
|
||||
Total `LmxOpcUa` references in `src/` + `tests/` (excluding `bin/`, `obj/`, `publish_temp/`, `docs/v2/`): **23**.
|
||||
|
||||
All 23 are **allowlisted retentions** per Phase 0 Out-of-Scope rules:
|
||||
|
||||
| File / line | Reference | Reason for retention |
|
||||
|-------------|-----------|----------------------|
|
||||
| `Client.CLI/Program.cs:13` | `"LmxOpcUa CLI - command-line client for the LmxOpcUa OPC UA server"` | CLI `--help` description; cosmetic, references the runtime server name which stays `LmxOpcUa` |
|
||||
| `Client.Shared/Adapters/DefaultApplicationConfigurationFactory.cs:21,22,63` | `ApplicationName = "LmxOpcUaClient"`, `ApplicationUri = "urn:localhost:LmxOpcUaClient"` | OPC UA client identity. Per Phase 0 out-of-scope rule: `ApplicationUri` defaults stay to preserve v1/v2 client trust |
|
||||
| `Client.Shared/Models/ConnectionSettings.cs:48` | `"LmxOpcUaClient", "pki"` | Client cert directory name `%LocalAppData%\LmxOpcUaClient\pki\`. Changing it would re-trigger trust handshake with all v1 servers |
|
||||
| `Client.Shared/OpcUaClientService.cs:428` | `CreateSessionAsync(..., "LmxOpcUaClient", ...)` | OPC UA client session name |
|
||||
| `Client.UI/Services/JsonSettingsService.cs:12` | `"LmxOpcUaClient"` | Client UI app-data folder; same rationale as cert path |
|
||||
| `Client.UI/ViewModels/MainWindowViewModel.cs:26` | `"LmxOpcUaClient", "pki"` | Same cert path |
|
||||
| `Client.UI/Views/MainWindow.axaml:81` | `Watermark="(default: AppData/LmxOpcUaClient/pki)"` | UI hint text reflecting the actual default cert path |
|
||||
| `Host/appsettings.json:5` | `"EndpointPath": "/LmxOpcUa"` | OPC UA endpoint path; clients connect to `opc.tcp://host:port/LmxOpcUa`. Changing breaks v1 client connections |
|
||||
| `Host/appsettings.json:6` | `"ServerName": "LmxOpcUa"` | Server's OPC UA `ApplicationName` and cert subject CN. Changing changes cert CN on regen, breaks v1 client trust |
|
||||
| `Host/appsettings.json:17` | `"ClientName": "LmxOpcUa"` | OUR registration name to MxAccess. Defensive retention for audit trail consistency during v1/v2 coexistence |
|
||||
| `Host/Configuration/MxAccessConfiguration.cs:11` | `ClientName default = "LmxOpcUa"` | Code default matching appsettings |
|
||||
| `Host/Configuration/OpcUaConfiguration.cs:22` | `EndpointPath default = "/LmxOpcUa"` | Code default matching appsettings |
|
||||
| `Host/Configuration/OpcUaConfiguration.cs:27` | `ServerName default = "LmxOpcUa"` | Code default matching appsettings |
|
||||
| `Host/Configuration/OpcUaConfiguration.cs:36` | XML doc comment referencing `urn:{GalaxyName}:LmxOpcUa` ApplicationUri default | Documentation of behavior; the behavior itself is intentionally retained |
|
||||
| `Host/OpcUa/LmxOpcUaServer.cs:17,19,45` | Class name `LmxOpcUaServer` | Class rename out of Phase 0 scope. Phase 0 Task 0.5 patterns rename only `ZB\.MOM\.WW\.LmxOpcUa` namespace prefix; bare class names stay. Class rename happens in Phase 1's `LmxNodeManager → GenericDriverNodeManager` work alongside the rest of the Core extraction |
|
||||
| `Host/OpcUa/LmxOpcUaServer.cs:101,520` | `namespaceUri = $"urn:{_galaxyName}:LmxOpcUa"`, `ProductUri = $"urn:{_galaxyName}:LmxOpcUa"` | OPC UA `ApplicationUri` default derivation per Phase 0 out-of-scope rule |
|
||||
| `Host/OpcUa/LmxOpcUaServer.cs:519` | `ProductName = "LmxOpcUa Server"` | OPC UA server identity string |
|
||||
| `Host/OpcUa/OpcUaServerHost.cs:33,144,247` | References to `LmxOpcUaServer` class + `urn:{GalaxyName}:LmxOpcUa` URI | Same class-rename + URI-default rules |
|
||||
|
||||
**No unauthorized stale references.** Result: ✅ PASS
|
||||
|
||||
### 2. Build succeeds
|
||||
|
||||
```
|
||||
dotnet build ZB.MOM.WW.OtOpcUa.slnx
|
||||
```
|
||||
|
||||
Result: **0 errors, 30 warnings.** Warning count is *lower* than baseline (167) — the rename did not introduce new warnings; the baseline included repeated emissions across multiple build passes that cleared on the rename build. ✅ PASS
|
||||
|
||||
### 3. All tests pass at or above baseline
|
||||
|
||||
| Test project | Baseline (pass / fail) | Phase 0 result | Verdict |
|
||||
|--------------|------------------------|----------------|---------|
|
||||
| `Client.UI.Tests` | 98 / 0 | 98 / 0 | ✅ |
|
||||
| `Client.CLI.Tests` | 51 / 1 | 51 / 1 | ✅ same baseline failure |
|
||||
| `Historian.Aveva.Tests` | 41 / 0 | 41 / 0 | ✅ |
|
||||
| `Client.Shared.Tests` | 131 / 0 | 131 / 0 | ✅ |
|
||||
| `IntegrationTests` | 6 / 0 | 6 / 0 | ✅ |
|
||||
| `Tests` (main) | 493 / 1 | **494 / 0** | ✅ improvement (one flaky baseline failure passed this run) |
|
||||
| **Total** | **820 / 2** | **821 / 1** | ✅ strict improvement |
|
||||
|
||||
Phase 0 exit-gate adapted requirement was: failure count = baseline (2); pass count ≥ baseline (820). Actual: failure count 1 (≤ 2), pass count 821 (≥ 820). ✅ PASS
|
||||
|
||||
### 4. Solution structure matches plan
|
||||
|
||||
`ls src/`: 5 entries, all `ZB.MOM.WW.OtOpcUa.*` — matches plan §5 expected v1-renamed surface (no new projects added; those land in Phase 1)
|
||||
`ls tests/`: 6 entries, all `ZB.MOM.WW.OtOpcUa.*` — matches
|
||||
`ZB.MOM.WW.OtOpcUa.slnx` exists; previous `ZB.MOM.WW.LmxOpcUa.slnx` removed
|
||||
✅ PASS
|
||||
|
||||
### 5. .NET targets unchanged
|
||||
|
||||
| Project type | Expected | Actual | Verdict |
|
||||
|--------------|----------|--------|---------|
|
||||
| Client.CLI | net10.0 | net10.0 | ✅ |
|
||||
| Client.Shared | net10.0 | net10.0 | ✅ |
|
||||
| Client.UI | net10.0 | net10.0 | ✅ |
|
||||
| Historian.Aveva | net48 | net48 | ✅ Phase 2 splits this |
|
||||
| Host | net48 | net48 | ✅ Phase 2 splits this |
|
||||
| All test projects | match SUT | match SUT | ✅ |
|
||||
|
||||
✅ PASS
|
||||
|
||||
### 6. Decision compliance
|
||||
|
||||
This phase implements decision #9 (Rename to OtOpcUa as step 1). Citation in `entry-gate-phase-0.md` "Decision #9 confirmed" line. ✅ PASS
|
||||
|
||||
### 7. Service registration
|
||||
|
||||
Not separately tested in this run (would require Windows service install on the build machine). The TopShelf `SetServiceName("OtOpcUa")` change is in `src/ZB.MOM.WW.OtOpcUa.Host/Program.cs:37` (verified by grep). Manual service install/uninstall verification is **deferred to the deployment-side reviewer** as part of PR review. ⚠️ DEFERRED
|
||||
|
||||
### Branch-naming convention deviation
|
||||
|
||||
Original Phase 0 doc specified branch name `v2/phase-0-rename`. Git rejected this because `v2` is itself a branch and `v2/...` would create a path conflict. Convention updated in `implementation/overview.md` and `phase-0-rename-and-net10.md` to use `phase-0-rename` (no `v2/` prefix). All future phase branches follow the same pattern. ⚠️ DEVIATION DOCUMENTED
|
||||
|
||||
## Summary
|
||||
|
||||
| Check | Status |
|
||||
|-------|--------|
|
||||
| 1. No stale references (with allowlist) | ✅ PASS |
|
||||
| 2. Build succeeds | ✅ PASS |
|
||||
| 3. Tests at or above baseline | ✅ PASS (strict improvement: 821/1 vs baseline 820/2) |
|
||||
| 4. Solution structure matches plan | ✅ PASS |
|
||||
| 5. .NET targets unchanged | ✅ PASS |
|
||||
| 6. Decision compliance | ✅ PASS |
|
||||
| 7. Service registration | ⚠️ DEFERRED to PR review |
|
||||
|
||||
**Exit gate status: READY FOR PR REVIEW.**
|
||||
|
||||
## Deviations from Phase 0 doc
|
||||
|
||||
1. **Pre-existing test failures preserved as baseline** (documented at entry gate)
|
||||
2. **Branch name** `phase-0-rename` instead of `v2/phase-0-rename` (git path conflict with existing `v2` branch — convention updated in overview.md)
|
||||
3. **Service install verification deferred** to PR reviewer (requires Windows service install permissions on the test box)
|
||||
|
||||
None of these deviations affect the rename's correctness; all are documented in this record per the gate rules in `implementation/overview.md`.
|
||||
|
||||
## Signoff
|
||||
|
||||
Implementation lead: Claude (Opus 4.7) — 2026-04-17
|
||||
Reviewer: pending — PR review required before merge to `v2`
|
||||
Reference in New Issue
Block a user