feat: add JoeAppEngine OPC UA nodes, fix DCL auto-reconnect and quality push
- Add JoeAppEngine folder to OPC UA nodes.json (BTCS, AlarmCntsBySeverity, Scheduler/ScanTime) - Fix DataConnectionActor: capture Self in PreStart for use from non-actor threads, preventing Self.Tell failure in Disconnected event handler - Implement InstanceActor.HandleConnectionQualityChanged to mark attributes Bad on disconnect - Fix LmxFakeProxy TagMapper to serialize arrays as JSON instead of "System.Int32[]" - Allow DataType and DataSourceReference updates in TemplateService.UpdateAttributeAsync - Update test_infra_opcua.md with JoeAppEngine documentation
This commit is contained in:
@@ -14,25 +14,31 @@ internal class RealLmxProxyClient : ILmxProxyClient
|
||||
private readonly string _host;
|
||||
private readonly int _port;
|
||||
private readonly string? _apiKey;
|
||||
private readonly int _samplingIntervalMs;
|
||||
private readonly bool _useTls;
|
||||
private GrpcChannel? _channel;
|
||||
private ScadaService.ScadaServiceClient? _client;
|
||||
private string? _sessionId;
|
||||
private Metadata? _headers;
|
||||
|
||||
public RealLmxProxyClient(string host, int port, string? apiKey)
|
||||
public RealLmxProxyClient(string host, int port, string? apiKey, int samplingIntervalMs = 0, bool useTls = false)
|
||||
{
|
||||
_host = host;
|
||||
_port = port;
|
||||
_apiKey = apiKey;
|
||||
_samplingIntervalMs = samplingIntervalMs;
|
||||
_useTls = useTls;
|
||||
}
|
||||
|
||||
public bool IsConnected => _client != null && !string.IsNullOrEmpty(_sessionId);
|
||||
|
||||
public async Task ConnectAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true);
|
||||
if (!_useTls)
|
||||
AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true);
|
||||
|
||||
_channel = GrpcChannel.ForAddress($"http://{_host}:{_port}");
|
||||
var scheme = _useTls ? "https" : "http";
|
||||
_channel = GrpcChannel.ForAddress($"{scheme}://{_host}:{_port}");
|
||||
_client = new ScadaService.ScadaServiceClient(_channel);
|
||||
|
||||
_headers = new Metadata();
|
||||
@@ -111,13 +117,13 @@ internal class RealLmxProxyClient : ILmxProxyClient
|
||||
throw new InvalidOperationException($"WriteBatch failed: {response.Message}");
|
||||
}
|
||||
|
||||
public Task<ILmxSubscription> SubscribeAsync(IEnumerable<string> addresses, Action<string, LmxVtq> onUpdate, CancellationToken cancellationToken = default)
|
||||
public Task<ILmxSubscription> SubscribeAsync(IEnumerable<string> addresses, Action<string, LmxVtq> onUpdate, Action? onStreamError = null, CancellationToken cancellationToken = default)
|
||||
{
|
||||
EnsureConnected();
|
||||
var tags = addresses.ToList();
|
||||
var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
|
||||
|
||||
var request = new SubscribeRequest { SessionId = _sessionId!, SamplingMs = 0 };
|
||||
var request = new SubscribeRequest { SessionId = _sessionId!, SamplingMs = _samplingIntervalMs };
|
||||
request.Tags.AddRange(tags);
|
||||
|
||||
var call = _client!.Subscribe(request, _headers, cancellationToken: cts.Token);
|
||||
@@ -131,9 +137,18 @@ internal class RealLmxProxyClient : ILmxProxyClient
|
||||
var msg = call.ResponseStream.Current;
|
||||
onUpdate(msg.Tag, ConvertVtq(msg));
|
||||
}
|
||||
// Stream ended normally (server closed) — treat as disconnect
|
||||
_sessionId = null;
|
||||
onStreamError?.Invoke();
|
||||
}
|
||||
catch (OperationCanceledException) { }
|
||||
catch (RpcException ex) when (ex.StatusCode == StatusCode.Cancelled) { }
|
||||
catch (RpcException)
|
||||
{
|
||||
// gRPC error (server offline, network failure) — signal disconnect
|
||||
_sessionId = null;
|
||||
onStreamError?.Invoke();
|
||||
}
|
||||
}, cts.Token);
|
||||
|
||||
return Task.FromResult<ILmxSubscription>(new CtsSubscription(cts));
|
||||
@@ -191,6 +206,6 @@ internal class RealLmxProxyClient : ILmxProxyClient
|
||||
/// </summary>
|
||||
public class RealLmxProxyClientFactory : ILmxProxyClientFactory
|
||||
{
|
||||
public ILmxProxyClient Create(string host, int port, string? apiKey)
|
||||
=> new RealLmxProxyClient(host, port, apiKey);
|
||||
public ILmxProxyClient Create(string host, int port, string? apiKey, int samplingIntervalMs = 0, bool useTls = false)
|
||||
=> new RealLmxProxyClient(host, port, apiKey, samplingIntervalMs, useTls);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user