Opc.Ua.Server.SessionPublishQueue.Publish C# (CSharp) Method

Publish() public method

Returns a subscription that is ready to publish.
public Publish ( uint clientHandle, System.DateTime deadline, bool requeue, AsyncPublishOperation operation ) : Subscription
clientHandle uint
deadline System.DateTime
requeue bool
operation AsyncPublishOperation
return Subscription
        public Subscription Publish(uint clientHandle, DateTime deadline, bool requeue, AsyncPublishOperation operation)
        {
            QueuedRequest request = null;

            // DateTime queueTime = DateTime.UtcNow;
            // DateTime dequeueTime = DateTime.UtcNow;
            
            lock (m_lock)
            {
                if (m_queuedSubscriptions.Count == 0)
                {
                    // TraceState("PUBLISH ERROR (BadNoSubscription)");
                    throw new ServiceResultException(StatusCodes.BadNoSubscription);
                }

                // find the waiting subscription with the highest priority.
                List<QueuedSubscription> subscriptions = new List<QueuedSubscription>();

                for (int ii = 0; ii < m_queuedSubscriptions.Count; ii++)
                {
                    QueuedSubscription subscription = m_queuedSubscriptions[ii];
                    
                    if (subscription.ReadyToPublish && !subscription.Publishing)
                    {
                        subscriptions.Add(subscription);
                    }
                }
                
                // find waiting the subscription that has been waiting the longest.
                if (subscriptions.Count > 0)
                {
                    byte maxPriority = 0;
                    DateTime earliestTimestamp = DateTime.MaxValue;
                    QueuedSubscription subscriptionToPublish = null;

                    for (int ii = 0; ii < subscriptions.Count; ii++)
                    {
                        QueuedSubscription subscription = subscriptions[ii];

                        if (subscription.Priority > maxPriority)
                        {
                            maxPriority = subscription.Priority;
                            earliestTimestamp = DateTime.MaxValue;
                        }

                        if (subscription.Priority >= maxPriority && earliestTimestamp > subscription.Timestamp)
                        {
                            earliestTimestamp = subscription.Timestamp;
                            subscriptionToPublish = subscription;
                        }
                    }

                    // reset subscriptions flag.
                    m_subscriptionsWaiting = false;

                    for (int jj = 0; jj < m_queuedSubscriptions.Count; jj++)
                    {
                        if (m_queuedSubscriptions[jj].ReadyToPublish)
                        {
                            m_subscriptionsWaiting = true;
                            break;
                        }
                    }

                    // TraceState("REQUEST #{0} ASSIGNED TO WAITING SUBSCRIPTION", clientHandle);
                    subscriptionToPublish.Publishing = true;
                    return subscriptionToPublish.Subscription;
                }

                // queue request because there is nothing waiting.
                if (subscriptions.Count == 0)
                {
                    LinkedListNode<QueuedRequest> node = m_queuedRequests.First;
                    
                    while (node != null)
                    {
                        LinkedListNode<QueuedRequest> next = node.Next;
                        QueuedRequest queuedRequest = node.Value;
                        StatusCode requestStatus = StatusCodes.Good;

                        // check if expired.
                        if (queuedRequest.Deadline < DateTime.MaxValue && queuedRequest.Deadline.AddMilliseconds(500) < DateTime.UtcNow)
                        {
                            requestStatus = StatusCodes.BadTimeout;
                        }

                        // check secure channel.
                        else if (!m_session.IsSecureChannelValid(queuedRequest.SecureChannelId))
                        {
                            requestStatus = StatusCodes.BadSecureChannelIdInvalid;
                        }

                        // remove bad requests.
                        if (StatusCode.IsBad(requestStatus))
                        {
                            queuedRequest.Error = requestStatus;
                            queuedRequest.Set();
                            m_queuedRequests.Remove(node);
                        }

                        node = next;
                    }

                    // clear excess requests - keep the newest ones.
                    while (m_maxPublishRequests > 0 && m_queuedRequests.Count >= m_maxPublishRequests)
                    {
                        request = m_queuedRequests.First.Value;
                        request.Error = StatusCodes.BadTooManyPublishRequests;
                        request.Set();
                        m_queuedRequests.RemoveFirst();
                    }

                    request = new QueuedRequest();
                    
                    request.SecureChannelId = SecureChannelContext.Current.SecureChannelId;
                    request.Deadline = deadline;
                    request.Subscription = null;
                    request.Error = StatusCodes.Good;

                    if (operation == null)
                    {
                        request.Event = new ManualResetEvent(false);
                    }
                    else
                    {
                        request.Operation = operation;
                    }

                    if (requeue)
                    {
                        m_queuedRequests.AddFirst(request);
                        // TraceState("REQUEST #{0} RE-QUEUED", clientHandle);
                    }
                    else
                    {
                        m_queuedRequests.AddLast(request);
                        // TraceState("REQUEST #{0} QUEUED", clientHandle);
                    }
                }                 
            }

            // check for non-blocking operation.
            if (operation != null)
            {
                // TraceState("PUBLISH: #{0} Async Request Queued.", clientHandle);
                return null;
            }

            // wait for subscription.
            ServiceResult error = request.Wait(Timeout.Infinite);

            // check for error.
            if (ServiceResult.IsGood(error))
            {
                if (StatusCode.IsBad(request.Error))
                {
                    error = request.Error;
                }
            }

            // must reassign subscription on error.
            if (ServiceResult.IsBad(request.Error))
            {
                if (request.Subscription != null)
                {
                    lock (m_lock)
                    {
                        request.Subscription.Publishing = false;
                        AssignSubscriptionToRequest(request.Subscription);
                    }
                }

                // TraceState("REQUEST #{0} PUBLISH ERROR ({1})", clientHandle, error.StatusCode);
                throw new ServiceResultException(request.Error);
            }

            // must be shuting down if this is null but no error.
            if (request.Subscription == null)
            {
                throw new ServiceResultException(StatusCodes.BadNoSubscription);
            }

            // TraceState("REQUEST #{0} ASSIGNED", clientHandle);
            // return whatever was assigned.
            return request.Subscription.Subscription;
        }

Usage Example

コード例 #1
0
		/// <summary>
		/// Publishes a subscription.
		/// </summary>
        public NotificationMessage GetNextMessage(
            OperationContext context,
            SessionPublishQueue queue,
            AsyncPublishOperation operation,
            out uint subscriptionId, 
            out UInt32Collection availableSequenceNumbers, 
            out bool moreNotifications)
        {
            subscriptionId = 0;
            availableSequenceNumbers = null;
            moreNotifications = false;

            NotificationMessage message = null;

            try
            {
                Utils.Trace("Publish #{0} ReceivedFromClient", context.ClientHandle);

                // check for status messages.
                lock (m_statusMessages)
                {
                    Queue<StatusMessage> statusQueue = null;

                    if (m_statusMessages.TryGetValue(context.SessionId, out statusQueue))
                    {
                        if (statusQueue.Count > 0)
                        {
                            StatusMessage status = statusQueue.Dequeue();
                            subscriptionId = status.SubscriptionId;
                            return status.Message;
                        }
                    }
                }

                bool requeue = false;

                do
                {
                    // wait for a subscription to publish.
                    Subscription subscription = queue.Publish(
                        context.ClientHandle,
                        context.OperationDeadline,
                        requeue,
                        operation);

                    if (subscription == null)
                    {
                        Utils.Trace("Publish #{0} Timeout", context.ClientHandle);
                        return null;
                    }

                    subscriptionId = subscription.Id;
                    moreNotifications = false;

                    // publish notifications.
                    try
                    {
                        requeue = false;

                        message = subscription.Publish(
                            context,
                            out availableSequenceNumbers,
                            out moreNotifications);

                        // a null message indicates a false alarm and that there were no notifications
                        // to publish and that the request needs to be requeued.
                        if (message != null)
                        {
                            break;
                        }

                        Utils.Trace("Publish False Alarm - Request #{0} Requeued.", context.ClientHandle);
                        requeue = true;
                    }
                    finally
                    {
                        queue.PublishCompleted(subscription, moreNotifications);
                    }
                }
                while (requeue);
            }
            finally
            {            
                // update diagnostics.
                if (context.Session != null)
                {
                    lock (context.Session.DiagnosticsLock)
                    {
                        SessionDiagnosticsDataType diagnostics = context.Session.SessionDiagnostics;
                        diagnostics.CurrentPublishRequestsInQueue--;
                    }
                }
            }

            return message;
        }