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>
This commit is contained in:
@@ -10,13 +10,13 @@ namespace ZB.MOM.WW.LmxProxy.Host.MxAccess
|
||||
{
|
||||
/// <summary>
|
||||
/// Wraps the ArchestrA MXAccess COM API. All COM operations
|
||||
/// execute on a dedicated STA thread via <see cref="StaDispatchThread"/>.
|
||||
/// execute via Task.Run (thread pool / MTA), relying on COM
|
||||
/// marshaling to handle cross-apartment calls.
|
||||
/// </summary>
|
||||
public sealed partial class MxAccessClient : IScadaClient
|
||||
{
|
||||
private static readonly ILogger Log = Serilog.Log.ForContext<MxAccessClient>();
|
||||
|
||||
private readonly StaDispatchThread _staThread;
|
||||
private readonly object _lock = new object();
|
||||
private readonly int _maxConcurrentOperations;
|
||||
private readonly int _readTimeoutMs;
|
||||
@@ -29,7 +29,7 @@ namespace ZB.MOM.WW.LmxProxy.Host.MxAccess
|
||||
private readonly SemaphoreSlim _readSemaphore;
|
||||
private readonly SemaphoreSlim _writeSemaphore;
|
||||
|
||||
// COM objects — only accessed on STA thread
|
||||
// COM objects
|
||||
private LMXProxyServer? _lmxProxy;
|
||||
private int _connectionHandle;
|
||||
|
||||
@@ -45,6 +45,17 @@ namespace ZB.MOM.WW.LmxProxy.Host.MxAccess
|
||||
private readonly Dictionary<string, Action<string, Vtq>> _storedSubscriptions
|
||||
= new Dictionary<string, Action<string, Vtq>>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
// Handle-to-address mapping for resolving COM callbacks
|
||||
private readonly Dictionary<int, string> _handleToAddress = new Dictionary<int, string>();
|
||||
|
||||
// Address-to-handle mapping for unsubscribe by address
|
||||
private readonly Dictionary<string, int> _addressToHandle
|
||||
= new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
// Pending write operations tracked by item handle
|
||||
private readonly Dictionary<int, TaskCompletionSource<bool>> _pendingWrites
|
||||
= new Dictionary<int, TaskCompletionSource<bool>>();
|
||||
|
||||
public MxAccessClient(
|
||||
int maxConcurrentOperations = 10,
|
||||
int readTimeoutSeconds = 5,
|
||||
@@ -64,7 +75,6 @@ namespace ZB.MOM.WW.LmxProxy.Host.MxAccess
|
||||
|
||||
_readSemaphore = new SemaphoreSlim(maxConcurrentOperations, maxConcurrentOperations);
|
||||
_writeSemaphore = new SemaphoreSlim(maxConcurrentOperations, maxConcurrentOperations);
|
||||
_staThread = new StaDispatchThread();
|
||||
}
|
||||
|
||||
public bool IsConnected
|
||||
@@ -123,7 +133,6 @@ namespace ZB.MOM.WW.LmxProxy.Host.MxAccess
|
||||
|
||||
_readSemaphore.Dispose();
|
||||
_writeSemaphore.Dispose();
|
||||
_staThread.Dispose();
|
||||
_reconnectCts?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user