# CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## What This Is LmxProxy is a gRPC proxy that bridges ScadaLink's Data Connection Layer to AVEVA System Platform via the ArchestrA MXAccess COM API. It has two projects: - **Host** (`ZB.MOM.WW.LmxProxy.Host`) — .NET Framework 4.8, x86-only Windows service. Hosts a gRPC server (Grpc.Core) that fronts an MxAccessClient talking to ArchestrA MXAccess. Runs as a Windows service via Topshelf. - **Client** (`ZB.MOM.WW.LmxProxy.Client`) — .NET 10, AnyCPU library. Code-first gRPC client (protobuf-net.Grpc) consumed by ScadaLink's DCL. This is a NuGet-packable library. The two projects use **different gRPC stacks**: Host uses proto-file-generated code (`Grpc.Core` + `Grpc.Tools`), Client uses code-first contracts (`protobuf-net.Grpc` with `[DataContract]`/`[ServiceContract]` attributes). They are wire-compatible because both target the same `scada.ScadaService` gRPC service. ## Build Commands ```bash dotnet build ZB.MOM.WW.LmxProxy.slnx # Build entire solution dotnet build src/ZB.MOM.WW.LmxProxy.Host # Host only (requires x86 platform) dotnet build src/ZB.MOM.WW.LmxProxy.Client # Client only ``` The Host project requires the `ArchestrA.MXAccess.dll` COM interop assembly in `lib/`. It targets x86 exclusively (MXAccess is 32-bit COM). ## Architecture ### Host Service Startup Chain `Program.Main` → Topshelf `HostFactory` → `LmxProxyService.Start()` which: 1. Validates configuration (`appsettings.json` bound to `LmxProxyConfiguration`) 2. Creates `MxAccessClient` (the `IScadaClient` impl that wraps ArchestrA.MXAccess COM) 3. Connects to MxAccess synchronously at startup 4. Starts connection monitor loop (auto-reconnect) 5. Creates `SubscriptionManager`, `SessionManager`, `PerformanceMetrics`, `ApiKeyService` 6. Creates `ScadaGrpcService` (the proto-generated service impl) with all dependencies 7. Starts Grpc.Core `Server` on configured port (default 50051) 8. Starts HTTP status web server (default port 8080) ### Key Host Components - `MxAccessClient` — Partial class split across 6 files (Connection, ReadWrite, Subscription, EventHandlers, NestedTypes, main). Wraps `LMXProxyServer` COM object. Uses semaphores for concurrency control. - `ScadaGrpcService` — Inherits proto-generated `ScadaService.ScadaServiceBase`. All RPCs validate session first, then delegate to `IScadaClient`. Values are string-serialized on the wire (v1 protocol). - `SessionManager` — Tracks client sessions by GUID. - `SubscriptionManager` — Manages MxAccess subscriptions, fans out updates via `System.Threading.Channels`. - `ApiKeyInterceptor` — gRPC server interceptor for API key validation. ### Client Architecture - `ILmxProxyClient` — Public interface for consumers. Connect/Read/Write/Subscribe/Dispose. - `LmxProxyClient` — Partial class split across multiple files (Connection, Subscription, Metrics, etc.). Uses `protobuf-net.Grpc` code-first contracts (`IScadaService` in `Domain/ScadaContracts.cs`). - `LmxProxyClientBuilder` — Fluent builder for configuring client instances. - `Domain/ScadaContracts.cs` — All gRPC message types as `[DataContract]` POCOs and the `IScadaService` interface with `[ServiceContract]`. - Value conversion: Client parses string values from wire using double → bool → string heuristic in `ConvertToVtq()`. Writes use `.ToString()` via `ConvertToString()`. ### Protocol Proto definition: `src/ZB.MOM.WW.LmxProxy.Host/Grpc/Protos/scada.proto` Currently v1 protocol (string-encoded values, string quality). A v2 protocol spec exists in `docs/lmxproxy_updates.md` that introduces `TypedValue` (protobuf oneof) and `QualityCode` (OPC UA status codes) — not yet implemented. RPCs: Connect, Disconnect, GetConnectionState, Read, ReadBatch, Write, WriteBatch, WriteBatchAndWait, Subscribe (server streaming), CheckApiKey. ### Configuration Host configured via `appsettings.json` bound to `LmxProxyConfiguration`. Key sections: GrpcPort, Connection (timeouts, auto-reconnect), Subscription (channel capacity), Tls, WebServer, Serilog, RetryPolicies, HealthCheck. ## Important Constraints - Host **must** target x86 and .NET Framework 4.8 (ArchestrA.MXAccess is 32-bit COM interop). - Host uses `Grpc.Core` (the deprecated C-core gRPC library), not `Grpc.Net`. This is required because .NET 4.8 doesn't support `Grpc.Net.Server`. - Client uses `Grpc.Net.Client` and targets .NET 10 — it runs in the ScadaLink central/site clusters. - The solution file is `.slnx` format (XML-based, not the older text format).