deprecate(lmxproxy): move all LmxProxy code, tests, and docs to deprecated/

LmxProxy is no longer needed. Moved the entire lmxproxy/ workspace, DCL
adapter files, and related docs to deprecated/. Removed LmxProxy registration
from DataConnectionFactory, project reference from DCL, protocol option from
UI, and cleaned up all requirement docs.
This commit is contained in:
Joseph Doherty
2026-04-08 15:56:23 -04:00
parent 8423915ba1
commit 9dccf8e72f
220 changed files with 25 additions and 132 deletions

View File

@@ -0,0 +1,156 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using ZB.MOM.WW.LmxProxy.Client.Domain;
namespace ZB.MOM.WW.LmxProxy.Client
{
public partial class LmxProxyClient
{
private class CodeFirstSubscription : ISubscription
{
private readonly IScadaService _client;
private readonly string _sessionId;
private readonly List<string> _tags;
private readonly Action<string, Vtq> _onUpdate;
private readonly ILogger<LmxProxyClient> _logger;
private readonly Action<ISubscription>? _onDispose;
private readonly CancellationTokenSource _cts = new();
private Task? _processingTask;
private bool _disposed;
/// <summary>
/// Initializes a new instance of the CodeFirstSubscription class.
/// </summary>
/// <param name="client">The gRPC ScadaService client.</param>
/// <param name="sessionId">The session identifier.</param>
/// <param name="tags">The list of tag addresses to subscribe to.</param>
/// <param name="onUpdate">Callback invoked when tag values change.</param>
/// <param name="logger">Logger for diagnostic information.</param>
/// <param name="onDispose">Optional callback invoked when the subscription is disposed.</param>
public CodeFirstSubscription(
IScadaService client,
string sessionId,
List<string> tags,
Action<string, Vtq> onUpdate,
ILogger<LmxProxyClient> logger,
Action<ISubscription>? onDispose = null)
{
_client = client;
_sessionId = sessionId;
_tags = tags;
_onUpdate = onUpdate;
_logger = logger;
_onDispose = onDispose;
}
/// <summary>
/// Starts the subscription asynchronously and begins processing tag value updates.
/// </summary>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>A task that completes when the subscription processing has started.</returns>
public Task StartAsync(CancellationToken cancellationToken = default)
{
_processingTask = ProcessUpdatesAsync(cancellationToken);
return Task.CompletedTask;
}
private async Task ProcessUpdatesAsync(CancellationToken cancellationToken)
{
try
{
var request = new SubscribeRequest
{
SessionId = _sessionId,
Tags = _tags,
SamplingMs = 1000
};
using var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, _cts.Token);
await foreach (VtqMessage vtq in _client.SubscribeAsync(request, linkedCts.Token))
{
try
{
Vtq convertedVtq = ConvertToVtq(vtq.Tag, vtq);
_onUpdate(vtq.Tag, convertedVtq);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error processing subscription update for {Tag}", vtq.Tag);
}
}
}
catch (OperationCanceledException) when (_cts.Token.IsCancellationRequested || cancellationToken.IsCancellationRequested)
{
_logger.LogDebug("Subscription cancelled");
}
catch (Exception ex)
{
_logger.LogError(ex, "Error in subscription processing");
try { await _cts.CancelAsync(); } catch { /* ignore */ }
}
finally
{
if (!_disposed)
{
_disposed = true;
_onDispose?.Invoke(this);
}
}
}
/// <summary>
/// Asynchronously disposes the subscription and stops processing tag updates.
/// </summary>
/// <returns>A task representing the asynchronous disposal operation.</returns>
public async Task DisposeAsync()
{
if (_disposed) return;
_disposed = true;
await _cts.CancelAsync();
try
{
if (_processingTask != null)
{
await _processingTask;
}
}
catch (Exception ex)
{
_logger.LogError(ex, "Error disposing subscription");
}
finally
{
_cts.Dispose();
_onDispose?.Invoke(this);
}
}
/// <summary>
/// Synchronously disposes the subscription and stops processing tag updates.
/// </summary>
public void Dispose()
{
if (_disposed) return;
try
{
Task task = DisposeAsync();
if (!task.Wait(TimeSpan.FromSeconds(5)))
{
_logger.LogWarning("Subscription disposal timed out");
}
}
catch (Exception ex)
{
_logger.LogError(ex, "Error during synchronous disposal");
}
}
}
}
}