Files
lmxopcua/docs/Configuration.md
Joseph Doherty 965e430f48 Add component-level documentation for all 14 server subsystems
Provides technical documentation covering OPC UA server, address space,
Galaxy repository, MXAccess bridge, data types, read/write, subscriptions,
alarms, historian, incremental sync, configuration, dashboard, service
hosting, and CLI tool. Updates README with component documentation table.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 15:47:59 -04:00

8.6 KiB

Configuration

Overview

The service loads configuration from appsettings.json at startup using the Microsoft.Extensions.Configuration stack. AppConfiguration is the root holder class that aggregates five typed sections: OpcUa, MxAccess, GalaxyRepository, Dashboard, and Historian. Each section binds to a dedicated POCO class with sensible defaults, so the service runs with zero configuration on a standard deployment.

Config Binding Pattern

The production constructor in OpcUaService builds the configuration pipeline and binds each JSON section to its typed class:

var configuration = new ConfigurationBuilder()
    .AddJsonFile("appsettings.json", optional: false)
    .AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("DOTNET_ENVIRONMENT") ?? "Production"}.json", optional: true)
    .AddEnvironmentVariables()
    .Build();

_config = new AppConfiguration();
configuration.GetSection("OpcUa").Bind(_config.OpcUa);
configuration.GetSection("MxAccess").Bind(_config.MxAccess);
configuration.GetSection("GalaxyRepository").Bind(_config.GalaxyRepository);
configuration.GetSection("Dashboard").Bind(_config.Dashboard);
configuration.GetSection("Historian").Bind(_config.Historian);

This pattern uses IConfiguration.GetSection().Bind() rather than IOptions<T> because the service targets .NET Framework 4.8, where the full dependency injection container is not used.

Environment-Specific Overrides

The configuration pipeline supports three layers of override, applied in order:

  1. appsettings.json -- base configuration (required)
  2. appsettings.{DOTNET_ENVIRONMENT}.json -- environment-specific overlay (optional)
  3. Environment variables -- highest priority, useful for deployment automation

Set the DOTNET_ENVIRONMENT variable to load a named overlay file. For example, setting DOTNET_ENVIRONMENT=Staging loads appsettings.Staging.json if it exists.

Environment variables follow the standard Section__Property naming convention. For example, OpcUa__Port=5840 overrides the OPC UA port.

Configuration Sections

OpcUa

Controls the OPC UA server endpoint and session limits. Defined in OpcUaConfiguration.

Property Type Default Description
Port int 4840 TCP port the OPC UA server listens on
EndpointPath string "/LmxOpcUa" Path appended to the host URI
ServerName string "LmxOpcUa" Server name presented to OPC UA clients
GalaxyName string "ZB" Galaxy name used as the OPC UA namespace
MaxSessions int 100 Maximum simultaneous OPC UA sessions
SessionTimeoutMinutes int 30 Idle session timeout in minutes
AlarmTrackingEnabled bool false Enables AlarmConditionState nodes for alarm attributes

MxAccess

Controls the MXAccess runtime connection used for live tag reads and writes. Defined in MxAccessConfiguration.

Property Type Default Description
ClientName string "LmxOpcUa" Client name registered with MXAccess
NodeName string? null Optional Galaxy node name to target
GalaxyName string? null Optional Galaxy name for MXAccess reference resolution
ReadTimeoutSeconds int 5 Maximum wait for a live tag read
WriteTimeoutSeconds int 5 Maximum wait for a write acknowledgment
MaxConcurrentOperations int 10 Cap on concurrent MXAccess operations
MonitorIntervalSeconds int 5 Connectivity monitor probe interval
AutoReconnect bool true Automatically re-establish dropped MXAccess sessions
ProbeTag string? null Optional tag used to verify the runtime returns fresh data
ProbeStaleThresholdSeconds int 60 Seconds a probe value may remain unchanged before the connection is considered stale

GalaxyRepository

Controls the Galaxy repository database connection used to build the OPC UA address space. Defined in GalaxyRepositoryConfiguration.

Property Type Default Description
ConnectionString string "Server=localhost;Database=ZB;Integrated Security=true;" SQL Server connection string for the Galaxy database
ChangeDetectionIntervalSeconds int 30 How often the service polls for Galaxy deploy changes
CommandTimeoutSeconds int 30 SQL command timeout for repository queries
ExtendedAttributes bool false Load extended Galaxy attribute metadata into the OPC UA model

Dashboard

Controls the embedded HTTP status dashboard. Defined in DashboardConfiguration.

Property Type Default Description
Enabled bool true Whether the status dashboard is hosted
Port int 8081 HTTP port for the dashboard endpoint
RefreshIntervalSeconds int 10 HTML auto-refresh interval in seconds

Historian

Controls the Wonderware Historian connection for OPC UA historical data access. Defined in HistorianConfiguration.

Property Type Default Description
Enabled bool false Enables OPC UA historical data access
ConnectionString string "Server=localhost;Database=Runtime;Integrated Security=true;" Connection string for the Historian Runtime database
CommandTimeoutSeconds int 30 SQL command timeout for historian queries
MaxValuesPerRead int 10000 Maximum values returned per HistoryRead request

Feature Flags

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.
  • Historian.Enabled -- When true, the service creates a HistorianDataSource connected to the Wonderware Historian Runtime database and registers it with the OPC UA server host. Disabled by default because not all deployments have a Historian instance.
  • 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.

Configuration Validation

ConfigurationValidator.ValidateAndLog() runs at the start of OpcUaService.Start(). It logs every resolved configuration value at Information level and validates required constraints:

  • OpcUa.Port must be between 1 and 65535
  • OpcUa.GalaxyName must not be empty
  • MxAccess.ClientName must not be empty
  • GalaxyRepository.ConnectionString must not be empty

If validation fails, the service throws InvalidOperationException and does not start.

Test Constructor Pattern

OpcUaService provides an internal constructor that accepts pre-built dependencies instead of loading appsettings.json:

internal OpcUaService(
    AppConfiguration config,
    IMxProxy? mxProxy,
    IGalaxyRepository? galaxyRepository,
    IMxAccessClient? mxAccessClientOverride = null,
    bool hasMxAccessClientOverride = false)

Integration tests use this constructor to inject substitute implementations of IMxProxy, IGalaxyRepository, and IMxAccessClient, bypassing the STA thread, COM interop, and SQL Server dependencies. The hasMxAccessClientOverride flag tells the service to use the injected IMxAccessClient directly instead of creating one from the IMxProxy on the STA thread.

Example appsettings.json

{
  "OpcUa": {
    "Port": 4840,
    "EndpointPath": "/LmxOpcUa",
    "ServerName": "LmxOpcUa",
    "GalaxyName": "ZB",
    "MaxSessions": 100,
    "SessionTimeoutMinutes": 30,
    "AlarmTrackingEnabled": false
  },
  "MxAccess": {
    "ClientName": "LmxOpcUa",
    "NodeName": null,
    "GalaxyName": null,
    "ReadTimeoutSeconds": 5,
    "WriteTimeoutSeconds": 5,
    "MaxConcurrentOperations": 10,
    "MonitorIntervalSeconds": 5,
    "AutoReconnect": true,
    "ProbeTag": null,
    "ProbeStaleThresholdSeconds": 60
  },
  "GalaxyRepository": {
    "ConnectionString": "Server=localhost;Database=ZB;Integrated Security=true;",
    "ChangeDetectionIntervalSeconds": 30,
    "CommandTimeoutSeconds": 30,
    "ExtendedAttributes": false
  },
  "Dashboard": {
    "Enabled": true,
    "Port": 8081,
    "RefreshIntervalSeconds": 10
  },
  "Historian": {
    "Enabled": false,
    "ConnectionString": "Server=localhost;Database=Runtime;Integrated Security=true;",
    "CommandTimeoutSeconds": 30,
    "MaxValuesPerRead": 10000
  }
}