Commit Graph

190 Commits

Author SHA1 Message Date
Joseph Doherty
a59d4ad76c fix(lmxproxy): use raw Win32 message pump instead of WinForms Application.Run
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 23:18:09 -04:00
Joseph Doherty
b6408726bc feat(lmxproxy): add STA thread with message pump for MxAccess COM callbacks
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 23:18:09 -04:00
Joseph Doherty
c96e71c83c Revert "fix(lmxproxy): resolve subscribe/unsubscribe race condition on client reconnect"
This reverts commit 9e9efbecab399fd7dcfb3e7e14e8b08418c3c3fc.
2026-03-22 23:18:09 -04:00
Joseph Doherty
fa33e1acf1 fix(lmxproxy): resolve subscribe/unsubscribe race condition on client reconnect
Three fixes for the SubscriptionManager/MxAccessClient subscription pipeline:

1. Serialize Subscribe and UnsubscribeClient with a SemaphoreSlim gate to prevent
   race where old-session unsubscribe removes new-session COM subscriptions.
   CreateMxAccessSubscriptionsAsync is now awaited instead of fire-and-forget.

2. Fix dual VTQ delivery in MxAccessClient.OnDataChange — each update was delivered
   twice (once via stored callback, once via OnTagValueChanged property). Now uses
   stored callback as the single delivery path.

3. Store pending tag addresses when CreateMxAccessSubscriptionsAsync fails (MxAccess
   down) and retry them on reconnect via NotifyReconnection/RetryPendingSubscriptionsAsync.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 23:18:08 -04:00
Joseph Doherty
bc4fc97652 refactor(ui): extract instance bindings and overrides to dedicated Configure page
Move connection bindings, attribute overrides, and area assignment from
inline expandable rows on the Instances table to a separate page at
/deployment/instances/{id}/configure for a cleaner, less cramped UX.
2026-03-22 15:58:32 -04:00
Joseph Doherty
161dc406ed feat(scripts): add typed Parameters.Get<T>() helpers for script API
Replace raw dictionary casting with ScriptParameters wrapper that provides
Get<T>, Get<T?>, Get<T[]>, and Get<List<T>> with clear error messages,
numeric conversion, and JsonElement support for Inbound API parameters.
2026-03-22 15:47:18 -04:00
Joseph Doherty
a0e036fb6b chore(lmxproxy): switch health probe tag to DevPlatform.Scheduler.ScanTime
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 15:21:00 -04:00
Joseph Doherty
ecf4b434c2 refactor(dcl): simplify ValueFormatter now that SDK returns native .NET arrays
The LmxProxy client's ExtractArrayValue now returns proper .NET arrays
(bool[], int[], DateTime[], etc.) instead of ArrayValue objects. Removed
the reflection-based FormatArrayContainer logic — IEnumerable handling
is sufficient for all array types.
2026-03-22 15:15:38 -04:00
Joseph Doherty
af7335f9e2 docs(dcl): update protocol and type mapping docs to reflect v2 TypedValue and SDK integration 2026-03-22 15:11:58 -04:00
Joseph Doherty
ce3942990e feat(lmxproxy): add DatetimeArray proto type for DateTime[] round-trip fidelity
Added DatetimeArray message (repeated int64, UTC ticks) to proto and
code-first contracts. Host serializes DateTime[] → DatetimeArray.
Client deserializes DatetimeArray → DateTime[] (not raw long[]).
Client ExtractArrayValue now unpacks all array types including DateTime.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 15:08:15 -04:00
Joseph Doherty
b050371dd5 fix(lmxproxy): handle DateTime[] COM arrays in TypedValueConverter
DateTime[] from MxAccess was falling through to ToString() fallback,
producing "System.DateTime[]" instead of actual values. Now converts
each DateTime to UTC ticks and stores in Int64Array.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 14:56:08 -04:00
Joseph Doherty
dcdf79afdc fix(dcl): format ArrayValue objects as comma-separated strings for display
ArrayValue from LmxProxy client was showing as type name in debug views.
Added ValueFormatter utility and NormalizeValue in LmxProxyDataConnection
to convert arrays at the adapter boundary. DateTime arrays remain as
"System.DateTime[]" due to server-side v1 string serialization.
2026-03-22 14:46:15 -04:00
Joseph Doherty
ea9c2857a7 fix(docker,cli): add LmxProxy.Client to Docker build, fix set-bindings JSON parsing
Docker: include lmxproxy/src/ZB.MOM.WW.LmxProxy.Client in build context
so the project reference resolves during container image build.

CLI: fix set-bindings JSON parsing — use JsonElement.GetString()/GetInt32()
instead of object.ToString() which returned null for deserialized elements.
2026-03-22 14:25:09 -04:00
Joseph Doherty
847302e297 test(dcl): add failover state machine tests for DataConnectionActor 2026-03-22 08:47:44 -04:00
Joseph Doherty
5de6c8d052 docs(dcl): document primary/backup endpoint redundancy across requirements and test infra 2026-03-22 08:43:59 -04:00
Joseph Doherty
e8df71ea64 feat(cli): add --primary-config, --backup-config, --failover-retry-count to data connection commands
Thread backup data connection fields through management command messages,
ManagementActor handlers, SiteService, site-side SQLite storage, and
deployment/replication actors. The old --configuration CLI flag is kept
as a hidden alias for backwards compatibility.
2026-03-22 08:41:57 -04:00
Joseph Doherty
ab4e88f17f feat(ui): add primary/backup endpoint fields to data connection form 2026-03-22 08:36:18 -04:00
Joseph Doherty
801c0c1df2 feat(dcl): add active endpoint to health reports and log failover events
Add ActiveEndpoint field to DataConnectionHealthReport showing which
endpoint is active (Primary, Backup, or Primary with no backup configured).
Log failover transitions and connection restoration events to the site
event log via ISiteEventLogger, passed as an optional parameter through
the actor hierarchy for backwards compatibility.
2026-03-22 08:34:05 -04:00
Joseph Doherty
da290fa4f8 feat(dcl): add failover state machine to DataConnectionActor with round-robin endpoint switching 2026-03-22 08:30:03 -04:00
Joseph Doherty
46304678da feat(dcl): extend CreateConnectionCommand with backup config and failover retry count
Update CreateConnectionCommand to carry PrimaryConnectionDetails,
BackupConnectionDetails, and FailoverRetryCount. Update all callers:
DataConnectionManagerActor, DataConnectionActor, DeploymentManagerActor,
FlatteningService, and ConnectionConfig. The actor stores both configs
but continues using primary only — failover logic comes in Task 3.
2026-03-22 08:24:39 -04:00
Joseph Doherty
04af03980e feat(dcl): rename Configuration to PrimaryConfiguration, add BackupConfiguration and FailoverRetryCount 2026-03-22 08:18:31 -04:00
Joseph Doherty
5ca1be328c docs(dcl): add primary/backup data connections implementation plan
8 tasks with TDD steps, exact file paths, and code samples.
Covers entity model, failover state machine, health reporting,
UI, CLI, management API, deployment, and documentation.
2026-03-22 08:13:23 -04:00
Joseph Doherty
6267ff882c docs(dcl): add primary/backup data connection endpoints design
Covers entity model, failover state machine, health reporting,
UI/CLI changes, and deployment flow for optional backup endpoints
with automatic failover after configurable retry count.
2026-03-22 08:09:25 -04:00
Joseph Doherty
5ec7f35150 feat(dcl): replace hand-rolled LmxProxy gRPC client with real LmxProxyClient library
Switches from v1 string-based proto stubs to the production LmxProxyClient
(v2 native TypedValue protocol) via project reference. Deletes 6k+ lines of
generated proto code. Preserves ILmxProxyClient adapter interface for testability.
2026-03-22 07:55:50 -04:00
Joseph Doherty
abb7579227 chore(infra): remove LmxFakeProxy — replaced by real LmxProxy v2 instances on windev
LmxFakeProxy is no longer needed now that two real LmxProxy v2 instances
are available for testing. Added remote test infra section to test_infra.md
documenting the windev instances. Removed tagsim (never committed).
2026-03-22 07:42:13 -04:00
Joseph Doherty
efed8352c3 feat(infra): add second OPC UA server instance (opcua2) on port 50010
Enables multi-server testing with independent state. Both instances
share the same nodes.json tag config. Updated all infra documentation.
2026-03-22 07:31:56 -04:00
Joseph Doherty
ac44122bf7 docs(lmxproxy): add dual-instance configuration (A on 50100, B on 50101)
Both instances share API keys and connect to the same AVEVA platform.
Verified: 17/17 integration tests pass against both instances.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 07:26:06 -04:00
Joseph Doherty
2c99b370a0 chore(lmxproxy): switch health probe tag to DevAppEngine.Scheduler.ScanTime, remove temp prompts
AppEngine built-in tag is always present and constantly updating (~1s),
making it a more reliable probe than a user-deployed TestChildObject tag.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 07:18:39 -04:00
Joseph Doherty
ec21a9a2a0 docs(lmxproxy): mark gap 1 and gap 2 as resolved with test verification
Gap 1: Active health probing verified — 60s recovery after platform restart.
Gap 2: Address-based subscription cleanup — no stale handles.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 07:10:38 -04:00
Joseph Doherty
a6c01d73e2 feat(lmxproxy): active health probing + address-based subscription cleanup (gap 1 & 2)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 06:44:21 -04:00
Joseph Doherty
86a15c0a65 docs(lmxproxy): document reconnection gaps from platform restart testing
Tested aaBootstrap kill on windev — three gaps identified:
1. No active health probing (IsConnected stays true on dead connection)
2. Stale SubscriptionManager handles after reconnect cycle
3. AVEVA objects don't auto-start after platform crash (platform behavior)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 06:19:30 -04:00
Joseph Doherty
5a9574fb95 feat(lmxproxy): add MxAccess status detail mapping for richer error messages
- MxStatusMapper: maps all 40+ MxStatusDetail codes, MxStatusCategory,
  and MxStatusSource to human-readable names and client messages
- OnDataChange: checks MXSTATUS_PROXY.success and overrides quality with
  specific OPC UA code when MxAccess reports a failure (e.g., CommFailure,
  ConfigError, WaitingForInitialData)
- OnWriteComplete: uses MxStatusMapper.FormatStatus for structured logging
- Write errors: catches COMException separately with HRESULT in message
- Read errors: distinguishes COM, timeout, and generic failures in logging

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 05:10:50 -04:00
Joseph Doherty
73b2b2f6d7 docs(lmxproxy): add STA message pump gap analysis with implementation guide
Documents when the full STA+Application.Run() approach is needed
(secured/verified writes), why our first attempt failed, the correct
pattern using Form.BeginInvoke(), and tradeoffs vs fire-and-forget.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 05:02:15 -04:00
Joseph Doherty
467fdc34d8 docs(lmxproxy): correct deviation #7 — OnWriteComplete is a COM threading issue, not MxAccess behavior
The MxAccess docs explicitly state OnWriteComplete always fires after Write().
The real cause is no Windows message pump in the headless service process to
marshal the COM callback. Fire-and-forget is safe for supervisory writes but
would miss secured/verified write rejections (errors 1012/1013).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 04:53:54 -04:00
Joseph Doherty
866c73dcd4 docs(lmxproxy): add deviation #8 — SubscriptionManager COM subscription wiring
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 04:47:23 -04:00
Joseph Doherty
7bed4b901a fix(lmxproxy): wire MxAccess COM subscriptions in SubscriptionManager
SubscriptionManager tracked client-to-tag routing but never called
MxAccessClient.SubscribeAsync to create the actual COM subscriptions,
so OnDataChange never fired. Now creates MxAccess subscriptions for
new tags and disposes them when the last client unsubscribes.

All 17 integration tests pass.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 04:46:15 -04:00
Joseph Doherty
c5d4849bd3 fix(lmxproxy): resolve write timeout — bypass OnWriteComplete callback for supervisory writes
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 04:39:14 -04:00
Joseph Doherty
e2c204b62b docs(lmxproxy): add execution prompt to fix failing write integration tests 2026-03-22 04:38:30 -04:00
Joseph Doherty
7079f6eed4 docs(lmxproxy): add ArchestrA MXAccess Toolkit reference documentation 2026-03-22 04:30:39 -04:00
Joseph Doherty
f4386bc518 docs(lmxproxy): record v2 rebuild deviations and key technical decisions
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 04:21:36 -04:00
Joseph Doherty
779598d962 feat(lmxproxy): phase 7 — integration tests, deployment to windev, v1 cutover
- Replaced STA dispatch thread with Task.Run pattern for COM interop
- Fixed TypedValue oneof tracking with property-level _setCase field
- Added x-api-key DelegatingHandler for gRPC metadata authentication
- Fixed CheckApiKey RPC to validate request body key (not header)
- Integration tests: 15/17 pass (reads, subscribes, API keys, connections)
- 2 write tests pending (OnWriteComplete callback timing issue)
- v2 service deployed on windev port 50100

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 01:11:44 -04:00
Joseph Doherty
6d9bf594ec feat(lmxproxy): phase 7 — integration test project and test scenarios
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 00:31:26 -04:00
Joseph Doherty
215cfa29f3 feat(lmxproxy): phase 6 — client extras (builder, factory, DI, streaming extensions)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 00:29:16 -04:00
Joseph Doherty
8ba75b50e8 feat(lmxproxy): phase 5 — client core (ILmxProxyClient, connection, read/write/subscribe)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 00:22:29 -04:00
Joseph Doherty
9eb81180c0 feat(lmxproxy): phase 4 — host health monitoring, metrics, status web server
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 00:14:40 -04:00
Joseph Doherty
16d1b95e9a feat(lmxproxy): phase 3 — host gRPC server, security, configuration, service hosting
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 00:05:36 -04:00
Joseph Doherty
64c92c63e5 feat(lmxproxy): phase 2 — host core (MxAccessClient, SessionManager, SubscriptionManager)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 23:58:17 -04:00
Joseph Doherty
0d63fb1105 feat(lmxproxy): phase 1 — v2 protocol types and domain model
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 23:41:56 -04:00
Joseph Doherty
08d2a07d8b docs(lmxproxy): update test tags to TestChildObject namespace for v2 type coverage
Replace JoeAppEngine tags with TestChildObject tags (TestBool, TestInt, TestFloat,
TestDouble, TestString, TestDateTime, and array variants) in Phase 4 and Phase 7
plans. These tags cover all TypedValue oneof cases for comprehensive v2 testing.
2026-03-21 23:35:15 -04:00
Joseph Doherty
4303f06fc3 docs(lmxproxy): add v2 rebuild design, 7-phase implementation plans, and execution prompt
Design doc covers architecture, v2 protocol (TypedValue/QualityCode), COM threading
model, session lifecycle, subscription semantics, error model, and guardrails.
Implementation plans are detailed enough for autonomous Claude Code execution.
Verified all dev tooling on windev (Grpc.Tools, protobuf-net.Grpc, Polly v8, xUnit).
2026-03-21 23:29:42 -04:00