/* ======================================================================== * Copyright (c) 2005-2025 The OPC Foundation, Inc. All rights reserved. * * OPC Foundation MIT License 1.00 * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. * * The complete license agreement can be found here: * http://opcfoundation.org/License/MIT/1.00/ * ======================================================================*/ using System; using System.Collections.Generic; using System.Diagnostics; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; namespace Opc.Ua.Client { public partial class Session { /// /// Adapter that implements /// by delegating to the owning instance. /// private sealed class SessionEngineContext : ISubscriptionEngineContext { public SessionEngineContext(Session session) { m_session = session ?? throw new ArgumentNullException( nameof(session)); } /// public NodeId SessionId => m_session.SessionId; /// public bool Reconnecting => m_session.Reconnecting; /// public bool Connected => m_session.Connected; /// public bool Closing => m_session.Closing; /// public bool KeepAliveStopped => m_session.KeepAliveStopped; /// public DateTime LastKeepAliveTime => m_session.LastKeepAliveTime; /// public bool Disposed => m_session.Disposed; /// public bool DeleteSubscriptionsOnClose => m_session.DeleteSubscriptionsOnClose; /// public int OperationTimeout => m_session.OperationTimeout; /// public ServerState ServerState => m_session.m_serverState; /// public DiagnosticsMasks ReturnDiagnostics => m_session.ReturnDiagnostics; /// public ITelemetryContext Telemetry => m_session.m_telemetry; /// public IReadOnlyList Subscriptions { get { lock (m_session.m_lock) { return [.. m_session.m_subscriptions]; } } } /// public SemaphoreSlim ReconnectLock => m_session.m_reconnectLock; /// public int GoodPublishRequestCount => m_session.GoodPublishRequestCount; /// public ISubscriptionServiceSetClientMethods SubscriptionServiceSet => m_session; /// public IMonitoredItemServiceSetClientMethods MonitoredItemServiceSet => m_session; /// public IMethodServiceSetClientMethods MethodServiceSet => m_session; /// public ValueTask PublishAsync( RequestHeader requestHeader, ArrayOf acknowledgements, CancellationToken ct = default) { return m_session.PublishAsync( requestHeader, acknowledgements, ct); } /// public ValueTask TransferSubscriptionsAsync( RequestHeader? requestHeader, ArrayOf subscriptionIds, bool sendInitialValues, CancellationToken ct = default) { return m_session.TransferSubscriptionsAsync( requestHeader, subscriptionIds, sendInitialValues, ct); } /// public ValueTask DeleteSubscriptionsAsync( RequestHeader? requestHeader, ArrayOf subscriptionIds, CancellationToken ct = default) { return m_session.DeleteSubscriptionsAsync( requestHeader, subscriptionIds, ct); } /// public void OnKeepAlive( ServerState serverState, DateTime timestamp) { m_session.OnKeepAlive(serverState, timestamp); } /// public void OnPublishError( ServiceResult error, uint subscriptionId, uint sequenceNumber) { PublishErrorEventHandler? callback = m_session.m_PublishError; if (callback != null) { try { callback( m_session, new PublishErrorEventArgs( error, subscriptionId, sequenceNumber)); } catch (Exception e) { m_session.m_logger.LogError( e, "Session: Unexpected error invoking " + "PublishErrorCallback."); } } } /// public void OnPublishNotification( Subscription subscription, NotificationEventArgs notification) { NotificationEventHandler? publishEventHandler = m_session.m_Publish; if (publishEventHandler != null) { _ = Task.Run( () => RaisePublishNotification( publishEventHandler, notification)); } } /// public void OnKeepAliveError(ServiceResult error) { m_session.OnKeepAliveError(error); } /// public void AsyncRequestStarted( Task task, Activity? activity, uint requestHandle, uint requestTypeId) { m_session.AsyncRequestStarted( task, activity, requestHandle, requestTypeId); } /// public void AsyncRequestCompleted( Task task, uint requestHandle, uint requestTypeId) { m_session.AsyncRequestCompleted( task, requestHandle, requestTypeId); } /// public (List toSend, List updatedPending) PrepareAcknowledgementsToSend( List currentAcknowledgements) { PublishSequenceNumbersToAcknowledgeEventHandler? callback = m_session .m_PublishSequenceNumbersToAcknowledge; List? acknowledgementsToSend = null; if (callback != null) { try { var deferred = new List(); callback( m_session, new PublishSequenceNumbersToAcknowledgeEventArgs( currentAcknowledgements, deferred)); acknowledgementsToSend = currentAcknowledgements; return (acknowledgementsToSend, deferred); } catch (Exception e) { m_session.m_logger.LogError( e, "Session: Unexpected error invoking " + "PublishSequenceNumbersToAcknowledge" + "EventArgs."); } } // No callback or callback threw — send all acks. return (currentAcknowledgements, []); } /// public ValueTask DeleteOrphanedSubscriptionAsync( uint subscriptionId) { return m_session.DeleteSubscriptionAsync( subscriptionId); } private void RaisePublishNotification( NotificationEventHandler callback, NotificationEventArgs args) { try { if (args.Subscription.Id != 0) { callback(m_session, args); } } catch (Exception e) { m_session.m_logger.LogError( e, "Session: Unexpected error while " + "raising Notification event."); } } private readonly Session m_session; } } }