Files
lmxopcua/CLAUDE.md
Joseph Doherty 50b85d41bd Consolidate LDAP roles into OPC UA session roles with granular write permissions
Map LDAP groups to custom OPC UA role NodeIds on RoleBasedIdentity.GrantedRoleIds
during authentication, replacing the username-to-role side cache. Split ReadWrite
into WriteOperate/WriteTune/WriteConfigure so write access is gated per Galaxy
security classification. AnonymousCanWrite now behaves consistently regardless
of LDAP state.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-29 01:50:16 -04:00

7.9 KiB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Project Goal

Build an OPC UA server on .NET Framework 4.8 (32-bit) that exposes AVEVA System Platform (Wonderware) Galaxy tags via the MXAccess toolkit. The server mirrors the Galaxy object hierarchy as an OPC UA address space, translating between contained-name browse paths and tag-name runtime references.

Architecture Overview

Data Flow

  1. Galaxy Repository DB (ZB) — SQL Server database holding the deployed object hierarchy and attribute definitions. Queried at startup and on change detection to build/rebuild the OPC UA address space.
  2. MXAccess COM API — Runtime data access layer. Subscribes to Galaxy tag attributes for live read/write. Requires a dedicated STA thread with a Win32 message pump for COM callbacks.
  3. OPC UA Server — Exposes the hierarchy as browse nodes and attributes as variable nodes. Clients browse via contained names but reads/writes are translated to tag_name.AttributeName format for MXAccess.

Key Concept: Contained Name vs Tag Name

Galaxy objects have two names:

  • contained_name — human-readable name scoped to parent (used for OPC UA browse tree)
  • tag_name — globally unique system name (used for MXAccess read/write)

Example: browsing TestMachine_001/DelmiaReceiver/DownloadPath translates to MXAccess reference DelmiaReceiver_001.DownloadPath.

See gr/layout.md for the full mapping and target OPC UA structure.

Data Type Mapping

Galaxy mx_data_type values map to OPC UA types (Boolean, Int32, Float, Double, String, DateTime, etc.). Array attributes use ValueRank=1 with ArrayDimensions from the Galaxy attribute definition. Full mapping in gr/data_type_mapping.md.

Change Detection

Poll galaxy.time_of_last_deploy in the ZB database to detect redeployments, then rebuild the address space. See gr/build_layout_plan.md for the step-by-step plan.

Reference Implementation

An existing MXAccess client implementation is at: C:\Users\dohertj2\Desktop\scadalink-design\lmxproxy\src\ZB.MOM.WW.LmxProxy.Host

Key patterns from that codebase:

  • StaComThread — Dedicated STA thread with Win32 message pump (GetMessage/DispatchMessage loop). All MXAccess COM objects must be created and called on this thread. Uses PostThreadMessage(WM_APP) to marshal work items.
  • LMXProxyServer COM objectRegister(clientName) returns a connection handle. AddItem(handle, address) + AdviseSupervisory(handle, itemHandle) for subscriptions. OnDataChange/OnWriteComplete events for callbacks.
  • Reconnect — Stored subscriptions are replayed after reconnect. A probe tag subscription monitors connection health.
  • COM cleanupMarshal.ReleaseComObject() on disconnect. Event handlers must be unwired before unregister.

MXAccess Documentation

mxaccess_documentation.md in the project root contains the full ArchestrA MXAccess Toolkit User's Guide. Key API: ArchestrA.MxAccess namespace, LMXProxyServer class. The toolkit DLLs are in Program Files (x86)\ArchestrA\Framework\bin.

Galaxy Repository Database

Connection: sqlcmd -S localhost -d ZB -E (Windows Auth). See gr/connectioninfo.md.

The gr/ folder contains:

  • queries/ — SQL for hierarchy extraction, attribute lookup, and change detection
  • ddl/tables/ and ddl/views/ — Schema definitions
  • schema.md — Full table/view reference
  • build_layout_plan.md — Step-by-step plan for building the OPC UA address space from DB queries
  • gr/CLAUDE.md — Detailed guidance for working within the gr/ subfolder

Key tables: gobject (hierarchy/deployment), template_definition (object categories), dynamic_attribute (user-defined attributes), primitive_instance (primitive-to-attribute links), galaxy (change detection).

Build Commands

dotnet restore ZB.MOM.WW.LmxOpcUa.slnx
dotnet build ZB.MOM.WW.LmxOpcUa.slnx
dotnet test ZB.MOM.WW.LmxOpcUa.slnx                          # all tests
dotnet test tests/ZB.MOM.WW.LmxOpcUa.Tests                    # unit tests only
dotnet test tests/ZB.MOM.WW.LmxOpcUa.IntegrationTests         # integration tests only
dotnet test --filter "FullyQualifiedName~MyTestClass.MyMethod"  # single test

Build & Runtime Constraints

  • Language: C#, .NET Framework 4.8, x86 (32-bit) platform target — required for MXAccess COM interop
  • MXAccess requires a deployed ArchestrA Platform on the machine running the server
  • COM apartment: MXAccess objects must live on an STA thread with a message pump

Transport Security

The server supports configurable OPC UA transport security via the Security section in appsettings.json. Phase 1 profiles: None (default), Basic256Sha256-Sign, Basic256Sha256-SignAndEncrypt. Security profiles are resolved by SecurityProfileResolver at startup. The server certificate is always created even for None-only deployments because UserName token encryption depends on it. See docs/security.md for the full guide.

Redundancy

The server supports non-transparent warm/hot redundancy via the Redundancy section in appsettings.json. Two instances share the same Galaxy DB and MXAccess runtime but have unique ApplicationUri values. Each exposes RedundancySupport, ServerUriArray, and a dynamic ServiceLevel based on role and runtime health. The primary advertises a higher ServiceLevel than the secondary. See docs/Redundancy.md for the full guide.

LDAP Authentication

The server uses LDAP-based user authentication via the Authentication.Ldap section in appsettings.json. When enabled, credentials are validated by LDAP bind against a GLAuth server (installed at C:\publish\glauth\), and LDAP group membership maps to OPC UA permissions: ReadOnly (browse/read), WriteOperate (write FreeAccess/Operate attributes), WriteTune (write Tune attributes), WriteConfigure (write Configure attributes), AlarmAck (alarm acknowledgment). LdapAuthenticationProvider implements both IUserAuthenticationProvider and IRoleProvider. See docs/Security.md for the full guide and C:\publish\glauth\auth.md for LDAP user/group reference.

Library Preferences

  • Logging: Serilog with rolling daily file sink
  • Unit tests: xUnit + Shouldly for assertions
  • Service hosting: TopShelf (Windows service install/uninstall/run as console)
  • OPC UA: OPC Foundation UA .NET Standard stack (https://github.com/opcfoundation/ua-.netstandard) — NuGet: OPCFoundation.NetStandard.Opc.Ua.Server

OPC UA .NET Standard Documentation

Use the DeepWiki MCP (mcp__deepwiki) to query documentation for the OPC UA .NET Standard stack: https://deepwiki.com/OPCFoundation/UA-.NETStandard. Tools: read_wiki_structure, read_wiki_contents, and ask_question with repo OPCFoundation/UA-.NETStandard.

Testing

Use the dotnet OPC UA CLI tool at tools/opcuacli-dotnet/ for manual testing against the running OPC UA server. Supports connect, read, write, subscribe, and browse commands. See tools/opcuacli-dotnet/README.md for usage details.

cd tools/opcuacli-dotnet
dotnet run -- connect -u opc.tcp://localhost:4840
dotnet run -- browse -u opc.tcp://localhost:4840 -r -d 3
dotnet run -- read -u opc.tcp://localhost:4840 -n "ns=2;s=SomeNode"
dotnet run -- subscribe -u opc.tcp://localhost:4840 -n "ns=2;s=SomeNode" -i 500

OPC PLC Sample Server

A test OPC UA server is available at tools/opcsampleserver/ (Azure IoT OPC PLC). It generates simulated data nodes (slow, fast, anomaly, GUID) on opc.tcp://localhost:50000. Must run from the publish directory or use the provided batch scripts. Requires --unsecuretransport flag for the CLI tool to connect. See tools/opcsampleserver/README.md for full details.

# Start the test server
cd tools/opcsampleserver/publish && dotnet opcplc.dll --pn=50000 --autoaccept --unsecuretransport --sn=5 --sr=10 --st=uint --fn=5 --fr=1 --ft=uint

# Or use the batch script
tools/opcsampleserver/start-server.bat