/// <summary> /// Worker method to test Transfer of subscriptions to new session. /// </summary> public static void TransferSubscriptionTest( IServerTestServices services, RequestHeader requestHeader, UInt32Collection subscriptionIds, bool sendInitialData, bool expectAccessDenied) { Assert.AreEqual(1, subscriptionIds.Count); requestHeader.Timestamp = DateTime.UtcNow; var response = services.TransferSubscriptions(requestHeader, subscriptionIds, sendInitialData, out TransferResultCollection transferResults, out DiagnosticInfoCollection diagnosticInfos); Assert.AreEqual(StatusCodes.Good, response.ServiceResult.Code); Assert.AreEqual(subscriptionIds.Count, transferResults.Count); ServerFixtureUtils.ValidateDiagnosticInfos(diagnosticInfos, subscriptionIds); foreach (var transferResult in transferResults) { TestContext.Out.WriteLine("TransferResult: {0}", transferResult.StatusCode); if (expectAccessDenied) { Assert.AreEqual(StatusCodes.BadUserAccessDenied, transferResult.StatusCode.Code); } else { Assert.IsTrue(StatusCode.IsGood(transferResult.StatusCode)); Assert.AreEqual(1, transferResult.AvailableSequenceNumbers.Count); } } if (expectAccessDenied) { return; } requestHeader.Timestamp = DateTime.UtcNow; var acknoledgements = new SubscriptionAcknowledgementCollection(); response = services.Publish(requestHeader, acknoledgements, out uint publishedId, out UInt32Collection availableSequenceNumbers, out bool moreNotifications, out NotificationMessage notificationMessage, out StatusCodeCollection _, out diagnosticInfos); Assert.AreEqual(StatusCodes.Good, response.ServiceResult.Code); ServerFixtureUtils.ValidateResponse(response); ServerFixtureUtils.ValidateDiagnosticInfos(diagnosticInfos, acknoledgements); Assert.AreEqual(subscriptionIds[0], publishedId); Assert.AreEqual(sendInitialData ? 1 : 0, notificationMessage.NotificationData.Count); //Assert.AreEqual(0, availableSequenceNumbers.Count); requestHeader.Timestamp = DateTime.UtcNow; response = services.DeleteSubscriptions(requestHeader, subscriptionIds, out StatusCodeCollection statusResults, out diagnosticInfos); Assert.AreEqual(StatusCodes.Good, response.ServiceResult.Code); }
public ResponseHeader Publish( RequestHeader requestHeader, SubscriptionAcknowledgementCollection subscriptionAcknowledgements, out uint subscriptionId, out UInt32Collection availableSequenceNumbers, out bool moreNotifications, out NotificationMessage notificationMessage, out StatusCodeCollection results, out DiagnosticInfoCollection diagnosticInfos) { return(m_session.Publish(requestHeader, subscriptionAcknowledgements, out subscriptionId, out availableSequenceNumbers, out moreNotifications, out notificationMessage, out results, out diagnosticInfos)); }
/// <summary> /// Worker method to test TransferSubscriptions of a server. /// </summary> public static UInt32Collection CreateSubscriptionForTransfer( IServerTestServices services, RequestHeader requestHeader, NodeId[] testNodes, uint queueSize = DefaultMonitoredItemsQueueSize, int samplingInterval = DefaultMonitoredItemsSamplingInterval) { // start time requestHeader.Timestamp = DateTime.UtcNow; uint subscriptionId = CreateSubscription(services, requestHeader); uint clientHandle = 1; foreach (NodeId testNode in testNodes) { CreateMonitoredItem(services, requestHeader, subscriptionId, testNode, clientHandle++, queueSize, samplingInterval); } var subscriptionIds = new UInt32Collection(); subscriptionIds.Add(subscriptionId); // enable publishing var response = services.SetPublishingMode(requestHeader, true, subscriptionIds, out var statuses, out var diagnosticInfos); ServerFixtureUtils.ValidateResponse(response); ServerFixtureUtils.ValidateDiagnosticInfos(diagnosticInfos, subscriptionIds); // wait some time to settle Thread.Sleep(1000); // publish request var acknoledgements = new SubscriptionAcknowledgementCollection(); response = services.Publish(requestHeader, acknoledgements, out uint publishedId, out UInt32Collection availableSequenceNumbers, out bool moreNotifications, out NotificationMessage notificationMessage, out StatusCodeCollection _, out diagnosticInfos); ServerFixtureUtils.ValidateResponse(response); ServerFixtureUtils.ValidateDiagnosticInfos(diagnosticInfos, acknoledgements); Assert.AreEqual(subscriptionId, publishedId); // static node, do not acknoledge Assert.AreEqual(1, availableSequenceNumbers.Count); return(subscriptionIds); }
/// <summary> /// Creates the test object. /// </summary> public SubscribeTest( Session session, ServerTestConfiguration configuration, ReportMessageEventHandler reportMessage, ReportProgressEventHandler reportProgress, TestBase template) : base("Subscribe", session, configuration, reportMessage, reportProgress, template) { m_subscriptions = new List<Subscription>(); m_errorEvent = new ManualResetEvent(false); m_acknowledgements = new SubscriptionAcknowledgementCollection(); m_publishPipelineDepth = 10; m_idealTimingError = 400; m_maximumTimingError = 500; }
public static Entry For(SubscriptionAcknowledgementCollection collection) { if (collection == null) { return(new NullEntry()); } else { ArrayEntry array = new ArrayEntry(); SubscriptionAcknowledgementCollection.Enumerator e = collection.GetEnumerator(); while (e.MoveNext()) { ObjectEntry oe = new ObjectEntry(); oe.Add("SubscriptionId", For(e.Current.SubscriptionId)); oe.Add("SequenceNo", For(e.Current.SequenceNumber)); array.Add(oe); } return(array); } }
/// <summary> /// Worker method to verify the SubscriptionTransferred message of a server. /// </summary> public static void VerifySubscriptionTransferred( IServerTestServices services, RequestHeader requestHeader, UInt32Collection subscriptionIds, bool deleteSubscriptions) { // start time requestHeader.Timestamp = DateTime.UtcNow; // wait some time to settle Thread.Sleep(100); // publish request var acknoledgements = new SubscriptionAcknowledgementCollection(); var response = services.Publish(requestHeader, acknoledgements, out uint publishedId, out UInt32Collection availableSequenceNumbers, out bool moreNotifications, out NotificationMessage notificationMessage, out StatusCodeCollection _, out var diagnosticInfos); ServerFixtureUtils.ValidateResponse(response); ServerFixtureUtils.ValidateDiagnosticInfos(diagnosticInfos, acknoledgements); Assert.IsFalse(moreNotifications); Assert.IsTrue(subscriptionIds.Contains(publishedId)); Assert.AreEqual(1, notificationMessage.NotificationData.Count); var statusMessage = notificationMessage.NotificationData[0].ToString(); Assert.IsTrue(statusMessage.Contains("GoodSubscriptionTransferred")); // static node, do not acknoledge if (availableSequenceNumbers != null) { Assert.AreEqual(0, availableSequenceNumbers.Count); } if (deleteSubscriptions) { response = services.DeleteSubscriptions(requestHeader, subscriptionIds, out var _, out diagnosticInfos); ServerFixtureUtils.ValidateResponse(response); ServerFixtureUtils.ValidateDiagnosticInfos(diagnosticInfos, subscriptionIds); } }
/// <summary> /// Invokes the Publish service. /// </summary> public virtual ResponseHeader Publish( RequestHeader requestHeader, SubscriptionAcknowledgementCollection subscriptionAcknowledgements, out uint subscriptionId, out UInt32Collection availableSequenceNumbers, out bool moreNotifications, out NotificationMessage notificationMessage, out StatusCodeCollection results, out DiagnosticInfoCollection diagnosticInfos) { subscriptionId = 0; availableSequenceNumbers = null; moreNotifications = false; notificationMessage = null; results = null; diagnosticInfos = null; ValidateRequest(requestHeader); // Insert implementation. return(CreateResponse(requestHeader, StatusCodes.BadServiceUnsupported)); }
/// <summary> /// Processes acknowledgements for previously published messages. /// </summary> public void Acknowledge( OperationContext context, SubscriptionAcknowledgementCollection subscriptionAcknowledgements, out StatusCodeCollection acknowledgeResults, out DiagnosticInfoCollection acknowledgeDiagnosticInfos) { if (context == null) { throw new ArgumentNullException("context"); } if (subscriptionAcknowledgements == null) { throw new ArgumentNullException("subscriptionAcknowledgements"); } lock (m_lock) { bool diagnosticsExist = false; acknowledgeResults = new StatusCodeCollection(subscriptionAcknowledgements.Count); acknowledgeDiagnosticInfos = new DiagnosticInfoCollection(subscriptionAcknowledgements.Count); for (int ii = 0; ii < subscriptionAcknowledgements.Count; ii++) { SubscriptionAcknowledgement acknowledgement = subscriptionAcknowledgements[ii]; bool found = false; for (int jj = 0; jj < m_queuedSubscriptions.Count; jj++) { QueuedSubscription subscription = m_queuedSubscriptions[jj]; if (subscription.Subscription.Id == acknowledgement.SubscriptionId) { ServiceResult result = subscription.Subscription.Acknowledge(context, acknowledgement.SequenceNumber); if (ServiceResult.IsGood(result)) { acknowledgeResults.Add(StatusCodes.Good); if ((context.DiagnosticsMask & DiagnosticsMasks.OperationAll) != 0) { acknowledgeDiagnosticInfos.Add(null); } } else { acknowledgeResults.Add(result.Code); if ((context.DiagnosticsMask & DiagnosticsMasks.OperationAll) != 0) { DiagnosticInfo diagnosticInfo = ServerUtils.CreateDiagnosticInfo(m_server, context, result); acknowledgeDiagnosticInfos.Add(diagnosticInfo); diagnosticsExist = true; } } found = true; break; } } if (!found) { ServiceResult result = new ServiceResult(StatusCodes.BadSubscriptionIdInvalid); acknowledgeResults.Add(result.Code); if ((context.DiagnosticsMask & DiagnosticsMasks.OperationAll) != 0) { DiagnosticInfo diagnosticInfo = ServerUtils.CreateDiagnosticInfo(m_server, context, result); acknowledgeDiagnosticInfos.Add(diagnosticInfo); diagnosticsExist = true; } } } if (!diagnosticsExist) { acknowledgeDiagnosticInfos.Clear(); } } }
/// <summary> /// Invokes the Publish service. /// </summary> public virtual ResponseHeader Publish( RequestHeader requestHeader, SubscriptionAcknowledgementCollection subscriptionAcknowledgements, out uint subscriptionId, out UInt32Collection availableSequenceNumbers, out bool moreNotifications, out NotificationMessage notificationMessage, out StatusCodeCollection results, out DiagnosticInfoCollection diagnosticInfos) { subscriptionId = 0; availableSequenceNumbers = null; moreNotifications = false; notificationMessage = null; results = null; diagnosticInfos = null; ValidateRequest(requestHeader); // Insert implementation. return CreateResponse(requestHeader, StatusCodes.BadServiceUnsupported); }
/// <summary> /// Invokes the Publish service. /// </summary> /// <param name="requestHeader">The request header.</param> /// <param name="subscriptionAcknowledgements">The list of acknowledgements for one or more Subscriptions.</param> /// <param name="subscriptionId">The subscription identifier.</param> /// <param name="availableSequenceNumbers">The available sequence numbers.</param> /// <param name="moreNotifications">If set to <c>true</c> the number of Notifications that were ready to be sent could not be sent in a single response.</param> /// <param name="notificationMessage">The NotificationMessage that contains the list of Notifications.</param> /// <param name="results">The list of results for the acknowledgements.</param> /// <param name="diagnosticInfos">The diagnostic information for the results.</param> /// <returns> /// Returns a <see cref="ResponseHeader"/> object /// </returns> public override ResponseHeader Publish( RequestHeader requestHeader, SubscriptionAcknowledgementCollection subscriptionAcknowledgements, out uint subscriptionId, out UInt32Collection availableSequenceNumbers, out bool moreNotifications, out NotificationMessage notificationMessage, out StatusCodeCollection results, out DiagnosticInfoCollection diagnosticInfos) { OperationContext context = ValidateRequest(requestHeader, RequestType.Publish); try { /* // check if there is an odd delay. if (DateTime.UtcNow > requestHeader.Timestamp.AddMilliseconds(100)) { Utils.Trace( "WARNING. Unexpected delay receiving Publish request. Time={0:hh:mm:ss.fff}, ReceiveTime={1:hh:mm:ss.fff}", DateTime.UtcNow, requestHeader.Timestamp); } */ Utils.Trace("PUBLISH #{0} RECIEVED. TIME={1:hh:hh:ss.fff}", requestHeader.RequestHandle, requestHeader.Timestamp); notificationMessage = ServerInternal.SubscriptionManager.Publish( context, subscriptionAcknowledgements, null, out subscriptionId, out availableSequenceNumbers, out moreNotifications, out results, out diagnosticInfos); /* if (notificationMessage != null) { Utils.Trace( "PublishResponse: SubId={0} SeqNo={1}, PublishTime={2:mm:ss.fff}, Time={3:mm:ss.fff}", subscriptionId, notificationMessage.SequenceNumber, notificationMessage.PublishTime, DateTime.UtcNow); } */ return CreateResponse(requestHeader, context.StringTable); } catch (ServiceResultException e) { lock (ServerInternal.DiagnosticsLock) { ServerInternal.ServerDiagnostics.RejectedRequestsCount++; if (IsSecurityError(e.StatusCode)) { ServerInternal.ServerDiagnostics.SecurityRejectedRequestsCount++; } } throw TranslateException(context, e); } finally { OnRequestComplete(context); } }
/// <summary> /// Processes the response from a publish request. /// </summary> private void ProcessPublishResponse( ResponseHeader responseHeader, uint subscriptionId, UInt32Collection availableSequenceNumbers, bool moreNotifications, NotificationMessage notificationMessage) { Subscription subscription = null; // send notification that the server is alive. OnKeepAlive(m_serverState, responseHeader.Timestamp); // collect the current set if acknowledgements. lock (SyncRoot) { // clear out acknowledgements for messages that the server does not have any more. SubscriptionAcknowledgementCollection acknowledgementsToSend = new SubscriptionAcknowledgementCollection(); for (int ii = 0; ii < m_acknowledgementsToSend.Count; ii++) { SubscriptionAcknowledgement acknowledgement = m_acknowledgementsToSend[ii]; if (acknowledgement.SubscriptionId != subscriptionId) { acknowledgementsToSend.Add(acknowledgement); } else { if (availableSequenceNumbers == null || availableSequenceNumbers.Contains(acknowledgement.SequenceNumber)) { acknowledgementsToSend.Add(acknowledgement); } } } // create an acknowledgement to be sent back to the server. if (notificationMessage.NotificationData.Count > 0) { SubscriptionAcknowledgement acknowledgement = new SubscriptionAcknowledgement(); acknowledgement.SubscriptionId = subscriptionId; acknowledgement.SequenceNumber = notificationMessage.SequenceNumber; acknowledgementsToSend.Add(acknowledgement); } uint lastSentSequenceNumber = 0; if (availableSequenceNumbers != null) { foreach (uint availableSequenceNumber in availableSequenceNumbers) { if (m_latestAcknowledgementsSent.ContainsKey(subscriptionId)) { lastSentSequenceNumber = m_latestAcknowledgementsSent[subscriptionId]; // If the last sent sequence number is uint.Max do not display the warning; the counter rolled over // If the last sent sequence number is greater or equal to the available sequence number (returned by the publish), a warning must be logged. if (((lastSentSequenceNumber >= availableSequenceNumber) && (lastSentSequenceNumber != uint.MaxValue)) || (lastSentSequenceNumber == availableSequenceNumber) && (lastSentSequenceNumber == uint.MaxValue)) { Utils.Trace("Received sequence number which was already acknowledged={0}", availableSequenceNumber); } } } } if (m_latestAcknowledgementsSent.ContainsKey(subscriptionId)) { lastSentSequenceNumber = m_latestAcknowledgementsSent[subscriptionId]; // If the last sent sequence number is uint.Max do not display the warning; the counter rolled over // If the last sent sequence number is greater or equal to the notificationMessage's sequence number (returned by the publish), a warning must be logged. if (((lastSentSequenceNumber >= notificationMessage.SequenceNumber) && (lastSentSequenceNumber != uint.MaxValue)) || (lastSentSequenceNumber == notificationMessage.SequenceNumber) && (lastSentSequenceNumber == uint.MaxValue)) { Utils.Trace("Received sequence number which was already acknowledged={0}", notificationMessage.SequenceNumber); } } if (availableSequenceNumbers != null) { foreach (var acknowledgement in acknowledgementsToSend) { if (acknowledgement.SubscriptionId == subscriptionId && !availableSequenceNumbers.Contains(acknowledgement.SequenceNumber)) { Utils.Trace("Sequence number={0} was not received in the available sequence numbers.", acknowledgement.SequenceNumber); } } } m_acknowledgementsToSend = acknowledgementsToSend; if (notificationMessage.IsEmpty) { Utils.Trace("Empty notification message received for SessionId {0} with PublishTime {1}", SessionId, notificationMessage.PublishTime.ToLocalTime()); } // find the subscription. foreach (Subscription current in m_subscriptions) { if (current.Id == subscriptionId) { subscription = current; break; } } } // ignore messages with a subscription that has been deleted. if (subscription != null) { // Validate publish time and reject old values. if (notificationMessage.PublishTime.AddMilliseconds(subscription.CurrentPublishingInterval * subscription.CurrentLifetimeCount) < DateTime.UtcNow) { Utils.Trace("PublishTime {0} in publish response is too old for SubscriptionId {1}.", notificationMessage.PublishTime.ToLocalTime(), subscription.Id); } // Validate publish time and reject old values. if (notificationMessage.PublishTime > DateTime.UtcNow.AddMilliseconds(subscription.CurrentPublishingInterval * subscription.CurrentLifetimeCount)) { Utils.Trace("PublishTime {0} in publish response is newer than actual time for SubscriptionId {1}.", notificationMessage.PublishTime.ToLocalTime(), subscription.Id); } // update subscription cache. subscription.SaveMessageInCache( availableSequenceNumbers, notificationMessage, responseHeader.StringTable); // raise the notification. lock (m_eventLock) { NotificationEventArgs args = new NotificationEventArgs(subscription, notificationMessage, responseHeader.StringTable); if (m_Publish != null) { ThreadPool.QueueUserWorkItem(OnRaisePublishNotification, args); } } } else { Utils.Trace("Received Publish Response for Unknown SubscriptionId={0}", subscriptionId); } }
/// <summary> /// Sends an additional publish request. /// </summary> public IAsyncResult BeginPublish(int timeout) { // do not publish if reconnecting. if (m_reconnecting) { Utils.Trace("Published skipped due to reconnect"); return null; } SubscriptionAcknowledgementCollection acknowledgementsToSend = null; // collect the current set if acknowledgements. lock (SyncRoot) { acknowledgementsToSend = m_acknowledgementsToSend; m_acknowledgementsToSend = new SubscriptionAcknowledgementCollection(); foreach (var toSend in acknowledgementsToSend) { if (m_latestAcknowledgementsSent.ContainsKey(toSend.SubscriptionId)) { m_latestAcknowledgementsSent[toSend.SubscriptionId] = toSend.SequenceNumber; } else { m_latestAcknowledgementsSent.Add(toSend.SubscriptionId, toSend.SequenceNumber); } } } // send publish request. RequestHeader requestHeader = new RequestHeader(); // ensure the publish request is discarded before the timeout occurs to ensure the channel is dropped. requestHeader.TimeoutHint = (uint)OperationTimeout/2; requestHeader.ReturnDiagnostics = (uint)(int)ReturnDiagnostics; requestHeader.RequestHandle = Utils.IncrementIdentifier(ref m_publishCounter); AsyncRequestState state = new AsyncRequestState(); state.RequestTypeId = DataTypes.PublishRequest; state.RequestId = requestHeader.RequestHandle; state.Timestamp = DateTime.UtcNow; try { IAsyncResult result = BeginPublish( requestHeader, acknowledgementsToSend, OnPublishComplete, new object[] { SessionId, acknowledgementsToSend, requestHeader }); AsyncRequestStarted(result, requestHeader.RequestHandle, DataTypes.PublishRequest); // Utils.Trace("PUBLISH #{0} SENT", requestHeader.RequestHandle); return result; } catch (Exception e) { Utils.Trace(e, "Unexpected error sending publish request."); return null; } }
/// <summary> /// Sets the object members to default values. /// </summary> private void Initialize() { m_sessionTimeout = 0; m_namespaceUris = new NamespaceTable(); m_serverUris = new StringTable(); m_factory = EncodeableFactory.GlobalFactory; m_nodeCache = new NodeCache(this); m_configuration = null; m_instanceCertificate = null; m_endpoint = null; m_subscriptions = new List<Subscription>(); m_dictionaries = new Dictionary<NodeId,DataDictionary>(); m_acknowledgementsToSend = new SubscriptionAcknowledgementCollection(); m_latestAcknowledgementsSent = new Dictionary<uint, uint>(); m_identityHistory = new List<IUserIdentity>(); m_outstandingRequests = new LinkedList<AsyncRequestState>(); m_keepAliveInterval = 5000; m_defaultSubscription = new Subscription(); m_defaultSubscription.DisplayName = "Subscription"; m_defaultSubscription.PublishingInterval = 1000; m_defaultSubscription.KeepAliveCount = 10; m_defaultSubscription.LifetimeCount = 1000; m_defaultSubscription.Priority = 255; m_defaultSubscription.PublishingEnabled = true; }
/// <summary> /// Processes the response from a publish request. /// </summary> private void ProcessPublishResponse( ResponseHeader responseHeader, uint subscriptionId, UInt32Collection availableSequenceNumbers, bool moreNotifications, NotificationMessage notificationMessage) { Subscription subscription = null; // send notification that the server is alive. OnKeepAlive(m_serverState, responseHeader.Timestamp); // collect the current set if acknowledgements. lock (SyncRoot) { // clear out acknowledgements for messages that the server does not have any more. SubscriptionAcknowledgementCollection acknowledgementsToSend = new SubscriptionAcknowledgementCollection(); for (int ii = 0; ii < m_acknowledgementsToSend.Count; ii++) { SubscriptionAcknowledgement acknowledgement = m_acknowledgementsToSend[ii]; if (acknowledgement.SubscriptionId != subscriptionId) { acknowledgementsToSend.Add(acknowledgement); } else { if (availableSequenceNumbers == null || availableSequenceNumbers.Contains(acknowledgement.SequenceNumber)) { acknowledgementsToSend.Add(acknowledgement); } } } // create an acknowledgement to be sent back to the server. if (notificationMessage.NotificationData.Count > 0) { SubscriptionAcknowledgement acknowledgement = new SubscriptionAcknowledgement(); acknowledgement.SubscriptionId = subscriptionId; acknowledgement.SequenceNumber = notificationMessage.SequenceNumber; acknowledgementsToSend.Add(acknowledgement); } m_acknowledgementsToSend = acknowledgementsToSend; // find the subscription. foreach (Subscription current in m_subscriptions) { if (current.Id == subscriptionId) { subscription = current; break; } } } // ignore messages with a subscription that has been deleted. if (subscription != null) { // update subscription cache. subscription.SaveMessageInCache( availableSequenceNumbers, notificationMessage, responseHeader.StringTable); // raise the notification. lock (m_eventLock) { NotificationEventArgs args = new NotificationEventArgs(subscription, notificationMessage, responseHeader.StringTable); if (m_Publish != null) { Task.Run(() => { OnRaisePublishNotification(args); }); } } } }
/// <summary> /// Returns the acknowledgements to send to the server with the publish. /// </summary> private SubscriptionAcknowledgementCollection GetAcknowledgements() { lock (m_acknowledgements) { SubscriptionAcknowledgementCollection acknowledgements = new SubscriptionAcknowledgementCollection(); acknowledgements.AddRange(m_acknowledgements); m_acknowledgements.Clear(); return acknowledgements; } }
public void ResendData() { var serverTestServices = new ServerTestServices(m_server); // save old security context, test fixture can only work with one session var securityContext = SecureChannelContext.Current; try { var namespaceUris = m_server.CurrentInstance.NamespaceUris; NodeId[] testSet = CommonTestWorkers.NodeIdTestSetStatic.Select(n => ExpandedNodeId.ToNodeId(n, namespaceUris)).ToArray(); //Re-use method CreateSubscriptionForTransfer to create a subscription CommonTestWorkers.CreateSubscriptionForTransfer(serverTestServices, m_requestHeader, testSet, out var subscriptionIds); RequestHeader resendDataRequestHeader = m_server.CreateAndActivateSession("ResendData"); var resendDataSecurityContext = SecureChannelContext.Current; // After the ResendData call there will be data to publish again MethodState methodStateInstance = (MethodState)m_server.CurrentInstance. DiagnosticsNodeManager.FindPredefinedNode(MethodIds.Server_ResendData, typeof(MethodState)); var nodesToCall = new CallMethodRequestCollection(); nodesToCall.Add(new CallMethodRequest() { ObjectId = ObjectIds.Server, MethodId = MethodIds.Server_ResendData, InputArguments = new VariantCollection() { new Variant(subscriptionIds.Last()) } }); //call ResendData method from the same session context m_requestHeader.Timestamp = DateTime.UtcNow; var response = m_server.Call(m_requestHeader, nodesToCall, out var results, out var diagnosticInfos); Assert.IsTrue(StatusCode.IsGood(results[0].StatusCode)); ServerFixtureUtils.ValidateResponse(response); Thread.Sleep(1000); // Make sure publish queue becomes empty by consuming it Assert.AreEqual(1, subscriptionIds.Count); // Issue a Publish request m_requestHeader.Timestamp = DateTime.UtcNow; var acknoledgements = new SubscriptionAcknowledgementCollection(); response = serverTestServices.Publish(m_requestHeader, acknoledgements, out uint publishedId, out UInt32Collection availableSequenceNumbers, out bool moreNotifications, out NotificationMessage notificationMessage, out StatusCodeCollection _, out diagnosticInfos); Assert.AreEqual(StatusCodes.Good, response.ServiceResult.Code); ServerFixtureUtils.ValidateResponse(response); ServerFixtureUtils.ValidateDiagnosticInfos(diagnosticInfos, acknoledgements); Assert.AreEqual(subscriptionIds[0], publishedId); Assert.AreEqual(1, notificationMessage.NotificationData.Count); // Validate nothing to publish a few times const int timesToCallPublish = 3; for (int i = 0; i < timesToCallPublish; i++) { m_requestHeader.Timestamp = DateTime.UtcNow; response = serverTestServices.Publish(m_requestHeader, acknoledgements, out publishedId, out availableSequenceNumbers, out moreNotifications, out notificationMessage, out StatusCodeCollection _, out diagnosticInfos); Assert.AreEqual(StatusCodes.Good, response.ServiceResult.Code); ServerFixtureUtils.ValidateResponse(response); ServerFixtureUtils.ValidateDiagnosticInfos(diagnosticInfos, acknoledgements); Assert.AreEqual(subscriptionIds[0], publishedId); Assert.AreEqual(0, notificationMessage.NotificationData.Count); } // Validate ResendData method call from same and different session contexts // call ResendData method from different session context resendDataRequestHeader.Timestamp = DateTime.UtcNow; response = m_server.Call(resendDataRequestHeader, nodesToCall, out results, out diagnosticInfos); Assert.AreEqual(StatusCodes.BadUserAccessDenied, results[0].StatusCode.Code); ServerFixtureUtils.ValidateResponse(response); // Still nothing to publish since previous ResendData call did not execute m_requestHeader.Timestamp = DateTime.UtcNow; response = serverTestServices.Publish(m_requestHeader, acknoledgements, out publishedId, out availableSequenceNumbers, out moreNotifications, out notificationMessage, out StatusCodeCollection _, out diagnosticInfos); Assert.AreEqual(StatusCodes.Good, response.ServiceResult.Code); ServerFixtureUtils.ValidateResponse(response); ServerFixtureUtils.ValidateDiagnosticInfos(diagnosticInfos, acknoledgements); Assert.AreEqual(subscriptionIds[0], publishedId); Assert.AreEqual(0, notificationMessage.NotificationData.Count); //call ResendData method from the same session context m_requestHeader.Timestamp = DateTime.UtcNow; response = m_server.Call(m_requestHeader, nodesToCall, out results, out diagnosticInfos); Assert.IsTrue(StatusCode.IsGood(results[0].StatusCode)); ServerFixtureUtils.ValidateResponse(response); // Data should be available for publishing now m_requestHeader.Timestamp = DateTime.UtcNow; response = serverTestServices.Publish(m_requestHeader, acknoledgements, out publishedId, out availableSequenceNumbers, out moreNotifications, out notificationMessage, out StatusCodeCollection _, out diagnosticInfos); Assert.AreEqual(StatusCodes.Good, response.ServiceResult.Code); ServerFixtureUtils.ValidateResponse(response); ServerFixtureUtils.ValidateDiagnosticInfos(diagnosticInfos, acknoledgements); Assert.AreEqual(subscriptionIds[0], publishedId); Assert.AreEqual(1, notificationMessage.NotificationData.Count); // Call ResendData method with invalid subscription Id nodesToCall = new CallMethodRequestCollection(); nodesToCall.Add(new CallMethodRequest() { ObjectId = ObjectIds.Server, MethodId = MethodIds.Server_ResendData, InputArguments = new VariantCollection() { new Variant(subscriptionIds.Last() + 20) } }); m_requestHeader.Timestamp = DateTime.UtcNow; response = m_server.Call(m_requestHeader, nodesToCall, out results, out diagnosticInfos); Assert.AreEqual(StatusCodes.BadSubscriptionIdInvalid, results[0].StatusCode.Code); ServerFixtureUtils.ValidateResponse(response); // Nothing to publish since previous ResendData call did not execute m_requestHeader.Timestamp = DateTime.UtcNow; response = serverTestServices.Publish(m_requestHeader, acknoledgements, out publishedId, out availableSequenceNumbers, out moreNotifications, out notificationMessage, out StatusCodeCollection _, out diagnosticInfos); Assert.AreEqual(StatusCodes.Good, response.ServiceResult.Code); ServerFixtureUtils.ValidateResponse(response); ServerFixtureUtils.ValidateDiagnosticInfos(diagnosticInfos, acknoledgements); Assert.AreEqual(subscriptionIds[0], publishedId); Assert.AreEqual(0, notificationMessage.NotificationData.Count); resendDataRequestHeader.Timestamp = DateTime.UtcNow; SecureChannelContext.Current = resendDataSecurityContext; m_server.CloseSession(resendDataRequestHeader); } finally { //restore security context, that close connection can work SecureChannelContext.Current = securityContext; } }
/// <summary> /// Worker method to test subscriptions of a server. /// </summary> /// <param name="services"></param> /// <param name="requestHeader"></param> public static void SubscriptionTest( IServerTestServices services, RequestHeader requestHeader) { // start time requestHeader.Timestamp = DateTime.UtcNow; // create subscription double publishingInterval = 1000.0; uint lifetimeCount = 60; uint maxKeepAliveCount = 2; uint maxNotificationPerPublish = 0; byte priority = 128; bool enabled = false; uint queueSize = 5; var response = services.CreateSubscription(requestHeader, publishingInterval, lifetimeCount, maxKeepAliveCount, maxNotificationPerPublish, enabled, priority, out uint id, out double revisedPublishingInterval, out uint revisedLifetimeCount, out uint revisedMaxKeepAliveCount); Assert.AreEqual(publishingInterval, revisedPublishingInterval); Assert.AreEqual(lifetimeCount, revisedLifetimeCount); Assert.AreEqual(maxKeepAliveCount, revisedMaxKeepAliveCount); ServerFixtureUtils.ValidateResponse(response); MonitoredItemCreateRequestCollection itemsToCreate = new MonitoredItemCreateRequestCollection(); // check badnothingtodo var sre = Assert.Throws <ServiceResultException>(() => services.CreateMonitoredItems(requestHeader, id, TimestampsToReturn.Neither, itemsToCreate, out MonitoredItemCreateResultCollection mockResults, out DiagnosticInfoCollection mockInfos)); Assert.AreEqual(StatusCodes.BadNothingToDo, sre.StatusCode); // add item uint handleCounter = 1; itemsToCreate.Add(new MonitoredItemCreateRequest() { ItemToMonitor = new ReadValueId() { AttributeId = Attributes.Value, NodeId = VariableIds.Server_ServerStatus_CurrentTime }, MonitoringMode = MonitoringMode.Reporting, RequestedParameters = new MonitoringParameters() { ClientHandle = ++handleCounter, SamplingInterval = -1, Filter = null, DiscardOldest = true, QueueSize = queueSize } }); response = services.CreateMonitoredItems(requestHeader, id, TimestampsToReturn.Neither, itemsToCreate, out MonitoredItemCreateResultCollection itemCreateResults, out DiagnosticInfoCollection diagnosticInfos); ServerFixtureUtils.ValidateResponse(response); ServerFixtureUtils.ValidateDiagnosticInfos(diagnosticInfos, itemsToCreate); // modify subscription response = services.ModifySubscription(requestHeader, id, publishingInterval, lifetimeCount, maxKeepAliveCount, maxNotificationPerPublish, priority, out revisedPublishingInterval, out revisedLifetimeCount, out revisedMaxKeepAliveCount); Assert.AreEqual(publishingInterval, revisedPublishingInterval); Assert.AreEqual(lifetimeCount, revisedLifetimeCount); Assert.AreEqual(maxKeepAliveCount, revisedMaxKeepAliveCount); ServerFixtureUtils.ValidateResponse(response); // modify monitored item, just timestamps to return var itemsToModify = new MonitoredItemModifyRequestCollection(); foreach (var itemCreated in itemCreateResults) { itemsToModify.Add( new MonitoredItemModifyRequest() { MonitoredItemId = itemCreated.MonitoredItemId }); } ; response = services.ModifyMonitoredItems(requestHeader, id, TimestampsToReturn.Both, itemsToModify, out MonitoredItemModifyResultCollection modifyResults, out diagnosticInfos); ServerFixtureUtils.ValidateResponse(response); ServerFixtureUtils.ValidateDiagnosticInfos(diagnosticInfos, itemsToModify); // publish request var acknoledgements = new SubscriptionAcknowledgementCollection(); response = services.Publish(requestHeader, acknoledgements, out uint subscriptionId, out UInt32Collection availableSequenceNumbers, out bool moreNotifications, out NotificationMessage notificationMessage, out StatusCodeCollection statuses, out diagnosticInfos); ServerFixtureUtils.ValidateResponse(response); ServerFixtureUtils.ValidateDiagnosticInfos(diagnosticInfos, acknoledgements); Assert.AreEqual(id, subscriptionId); Assert.AreEqual(0, availableSequenceNumbers.Count); // enable publishing enabled = true; var subscriptions = new UInt32Collection() { id }; response = services.SetPublishingMode(requestHeader, enabled, subscriptions, out statuses, out diagnosticInfos); ServerFixtureUtils.ValidateResponse(response); ServerFixtureUtils.ValidateDiagnosticInfos(diagnosticInfos, subscriptions); // wait some time to fill queue int loopCounter = (int)queueSize; Thread.Sleep(loopCounter * 1000); acknoledgements = new SubscriptionAcknowledgementCollection(); do { // get publish responses response = services.Publish(requestHeader, acknoledgements, out subscriptionId, out availableSequenceNumbers, out moreNotifications, out notificationMessage, out statuses, out diagnosticInfos); ServerFixtureUtils.ValidateResponse(response); ServerFixtureUtils.ValidateDiagnosticInfos(diagnosticInfos, acknoledgements); Assert.AreEqual(id, subscriptionId); var dataChangeNotification = notificationMessage.NotificationData[0].Body as DataChangeNotification; TestContext.Out.WriteLine("Notification: {0} {1} {2}", notificationMessage.SequenceNumber, dataChangeNotification?.MonitoredItems[0].Value.ToString(), notificationMessage.PublishTime); acknoledgements.Clear(); acknoledgements.Add(new SubscriptionAcknowledgement() { SubscriptionId = id, SequenceNumber = notificationMessage.SequenceNumber }); } while (acknoledgements.Count > 0 && --loopCounter > 0); // republish response = services.Republish(requestHeader, subscriptionId, notificationMessage.SequenceNumber, out notificationMessage); ServerFixtureUtils.ValidateResponse(response); // disable publishing enabled = false; response = services.SetPublishingMode(requestHeader, enabled, subscriptions, out statuses, out diagnosticInfos); ServerFixtureUtils.ValidateResponse(response); ServerFixtureUtils.ValidateDiagnosticInfos(diagnosticInfos, subscriptions); // delete subscription response = services.DeleteSubscriptions(requestHeader, subscriptions, out statuses, out diagnosticInfos); ServerFixtureUtils.ValidateResponse(response); ServerFixtureUtils.ValidateDiagnosticInfos(diagnosticInfos, subscriptions); }
/// <summary> /// Publishes a subscription. /// </summary> public NotificationMessage Publish( OperationContext context, SubscriptionAcknowledgementCollection subscriptionAcknowledgements, AsyncPublishOperation operation, out uint subscriptionId, out UInt32Collection availableSequenceNumbers, out bool moreNotifications, out StatusCodeCollection acknowledgeResults, out DiagnosticInfoCollection acknowledgeDiagnosticInfos) { availableSequenceNumbers = null; moreNotifications = false; // get publish queue for session. SessionPublishQueue queue = null; lock (m_lock) { if (!m_publishQueues.TryGetValue(context.Session.Id, out queue)) { if (m_subscriptions.Count == 0) { throw new ServiceResultException(StatusCodes.BadNoSubscription); } throw new ServiceResultException(StatusCodes.BadSessionClosed); } } // acknowlege previous messages. queue.Acknowledge( context, subscriptionAcknowledgements, out acknowledgeResults, out acknowledgeDiagnosticInfos); // update diagnostics. if (context.Session != null) { lock (context.Session.DiagnosticsLock) { SessionDiagnosticsDataType diagnostics = context.Session.SessionDiagnostics; diagnostics.CurrentPublishRequestsInQueue++; } } // save results for asynchrounous operation. if (operation != null) { operation.Response.Results = acknowledgeResults; operation.Response.DiagnosticInfos = acknowledgeDiagnosticInfos; } // gets the next message that is ready to publish. NotificationMessage message = GetNextMessage( context, queue, operation, out subscriptionId, out availableSequenceNumbers, out moreNotifications); // if no message and no async operation then a timeout occurred. if (message == null && operation == null) { throw new ServiceResultException(StatusCodes.BadTimeout); } // return message. return message; }
/// <summary> /// Begins an asynchronous invocation of the Publish service. /// </summary> public IAsyncResult BeginPublish( RequestHeader requestHeader, SubscriptionAcknowledgementCollection subscriptionAcknowledgements, AsyncCallback callback, object asyncState) { PublishRequest request = new PublishRequest(); request.RequestHeader = requestHeader; request.SubscriptionAcknowledgements = subscriptionAcknowledgements; UpdateRequestHeader(request, requestHeader == null, "Publish"); if (UseTransportChannel) { return TransportChannel.BeginSendRequest(request, callback, asyncState); } return InnerChannel.BeginPublish(new PublishMessage(request), callback, asyncState); }
/// <summary> /// Invokes the Publish service. /// </summary> public virtual ResponseHeader Publish( RequestHeader requestHeader, SubscriptionAcknowledgementCollection subscriptionAcknowledgements, out uint subscriptionId, out UInt32Collection availableSequenceNumbers, out bool moreNotifications, out NotificationMessage notificationMessage, out StatusCodeCollection results, out DiagnosticInfoCollection diagnosticInfos) { PublishRequest request = new PublishRequest(); PublishResponse response = null; request.RequestHeader = requestHeader; request.SubscriptionAcknowledgements = subscriptionAcknowledgements; UpdateRequestHeader(request, requestHeader == null, "Publish"); try { if (UseTransportChannel) { IServiceResponse genericResponse = TransportChannel.SendRequest(request); if (genericResponse == null) { throw new ServiceResultException(StatusCodes.BadUnknownResponse); } ValidateResponse(genericResponse.ResponseHeader); response = (PublishResponse)genericResponse; } else { PublishResponseMessage responseMessage = InnerChannel.Publish(new PublishMessage(request)); if (responseMessage == null || responseMessage.PublishResponse == null) { throw new ServiceResultException(StatusCodes.BadUnknownResponse); } response = responseMessage.PublishResponse; ValidateResponse(response.ResponseHeader); } subscriptionId = response.SubscriptionId; availableSequenceNumbers = response.AvailableSequenceNumbers; moreNotifications = response.MoreNotifications; notificationMessage = response.NotificationMessage; results = response.Results; diagnosticInfos = response.DiagnosticInfos; } finally { RequestCompleted(request, response, "Publish"); } return response.ResponseHeader; }
/// <summary> /// Processes acknowledgements for previously published messages. /// </summary> public void Acknowledge( OperationContext context, SubscriptionAcknowledgementCollection subscriptionAcknowledgements, out StatusCodeCollection acknowledgeResults, out DiagnosticInfoCollection acknowledgeDiagnosticInfos) { if (context == null) throw new ArgumentNullException("context"); if (subscriptionAcknowledgements == null) throw new ArgumentNullException("subscriptionAcknowledgements"); lock (m_lock) { bool diagnosticsExist = false; acknowledgeResults = new StatusCodeCollection(subscriptionAcknowledgements.Count); acknowledgeDiagnosticInfos = new DiagnosticInfoCollection(subscriptionAcknowledgements.Count); for (int ii = 0; ii < subscriptionAcknowledgements.Count; ii++) { SubscriptionAcknowledgement acknowledgement = subscriptionAcknowledgements[ii]; bool found = false; for (int jj = 0; jj < m_queuedSubscriptions.Count; jj++) { QueuedSubscription subscription = m_queuedSubscriptions[jj]; if (subscription.Subscription.Id == acknowledgement.SubscriptionId) { ServiceResult result = subscription.Subscription.Acknowledge(context, acknowledgement.SequenceNumber); if (ServiceResult.IsGood(result)) { acknowledgeResults.Add(StatusCodes.Good); if ((context.DiagnosticsMask & DiagnosticsMasks.OperationAll) != 0) { acknowledgeDiagnosticInfos.Add(null); } } else { acknowledgeResults.Add(result.Code); if ((context.DiagnosticsMask & DiagnosticsMasks.OperationAll) != 0) { DiagnosticInfo diagnosticInfo = ServerUtils.CreateDiagnosticInfo(m_server, context, result); acknowledgeDiagnosticInfos.Add(diagnosticInfo); diagnosticsExist = true; } } found = true; break; } } if (!found) { ServiceResult result = new ServiceResult(StatusCodes.BadSubscriptionIdInvalid); acknowledgeResults.Add(result.Code); if ((context.DiagnosticsMask & DiagnosticsMasks.OperationAll) != 0) { DiagnosticInfo diagnosticInfo = ServerUtils.CreateDiagnosticInfo(m_server, context, result); acknowledgeDiagnosticInfos.Add(diagnosticInfo); diagnosticsExist = true; } } } if (!diagnosticsExist) { acknowledgeDiagnosticInfos.Clear(); } } }
public void ResendData(bool updateValues, uint queueSize) { var serverTestServices = new ServerTestServices(m_server); // save old security context, test fixture can only work with one session var securityContext = SecureChannelContext.Current; try { var namespaceUris = m_server.CurrentInstance.NamespaceUris; NodeIdCollection testSetCollection = CommonTestWorkers.NodeIdTestSetStatic.Select(n => ExpandedNodeId.ToNodeId(n, namespaceUris)).ToArray(); testSetCollection.AddRange(CommonTestWorkers.NodeIdTestDataSetStatic.Select(n => ExpandedNodeId.ToNodeId(n, namespaceUris)).ToArray()); NodeId[] testSet = testSetCollection.ToArray(); //Re-use method CreateSubscriptionForTransfer to create a subscription var subscriptionIds = CommonTestWorkers.CreateSubscriptionForTransfer(serverTestServices, m_requestHeader, testSet, queueSize, 0); RequestHeader resendDataRequestHeader = m_server.CreateAndActivateSession("ResendData"); var resendDataSecurityContext = SecureChannelContext.Current; SecureChannelContext.Current = securityContext; // After the ResendData call there will be data to publish again var nodesToCall = ResendDataCall(StatusCodes.Good, subscriptionIds); Thread.Sleep(1000); // Make sure publish queue becomes empty by consuming it Assert.AreEqual(1, subscriptionIds.Count); // Issue a Publish request m_requestHeader.Timestamp = DateTime.UtcNow; var acknoledgements = new SubscriptionAcknowledgementCollection(); var response = serverTestServices.Publish(m_requestHeader, acknoledgements, out uint publishedId, out UInt32Collection availableSequenceNumbers, out bool moreNotifications, out NotificationMessage notificationMessage, out StatusCodeCollection _, out DiagnosticInfoCollection diagnosticInfos); Assert.AreEqual(StatusCodes.Good, response.ServiceResult.Code); ServerFixtureUtils.ValidateResponse(response); ServerFixtureUtils.ValidateDiagnosticInfos(diagnosticInfos, acknoledgements); Assert.AreEqual(subscriptionIds[0], publishedId); Assert.AreEqual(1, notificationMessage.NotificationData.Count); // Validate nothing to publish a few times const int timesToCallPublish = 3; for (int i = 0; i < timesToCallPublish; i++) { m_requestHeader.Timestamp = DateTime.UtcNow; response = serverTestServices.Publish(m_requestHeader, acknoledgements, out publishedId, out availableSequenceNumbers, out moreNotifications, out notificationMessage, out StatusCodeCollection _, out diagnosticInfos); Assert.AreEqual(StatusCodes.Good, response.ServiceResult.Code); ServerFixtureUtils.ValidateResponse(response); ServerFixtureUtils.ValidateDiagnosticInfos(diagnosticInfos, acknoledgements); Assert.AreEqual(subscriptionIds[0], publishedId); Assert.AreEqual(0, notificationMessage.NotificationData.Count); } // Validate ResendData method call returns error from different session contexts // call ResendData method from different session context SecureChannelContext.Current = resendDataSecurityContext; resendDataRequestHeader.Timestamp = DateTime.UtcNow; response = m_server.Call(resendDataRequestHeader, nodesToCall, out var results, out diagnosticInfos); SecureChannelContext.Current = securityContext; Assert.AreEqual(StatusCodes.BadUserAccessDenied, results[0].StatusCode.Code); ServerFixtureUtils.ValidateResponse(response); // Still nothing to publish since previous ResendData call did not execute m_requestHeader.Timestamp = DateTime.UtcNow; response = serverTestServices.Publish(m_requestHeader, acknoledgements, out publishedId, out availableSequenceNumbers, out moreNotifications, out notificationMessage, out StatusCodeCollection _, out diagnosticInfos); Assert.AreEqual(StatusCodes.Good, response.ServiceResult.Code); ServerFixtureUtils.ValidateResponse(response); ServerFixtureUtils.ValidateDiagnosticInfos(diagnosticInfos, acknoledgements); Assert.AreEqual(subscriptionIds[0], publishedId); Assert.AreEqual(0, notificationMessage.NotificationData.Count); if (updateValues) { UpdateValues(testSet); // fill queues, but only a single value per resend publish shall be returned for (int i = 1; i < queueSize; i++) { UpdateValues(testSet); } } // call ResendData method from the same session context ResendDataCall(StatusCodes.Good, subscriptionIds); // Data should be available for publishing now m_requestHeader.Timestamp = DateTime.UtcNow; response = serverTestServices.Publish(m_requestHeader, acknoledgements, out publishedId, out availableSequenceNumbers, out moreNotifications, out notificationMessage, out StatusCodeCollection _, out diagnosticInfos); Assert.AreEqual(StatusCodes.Good, response.ServiceResult.Code); ServerFixtureUtils.ValidateResponse(response); ServerFixtureUtils.ValidateDiagnosticInfos(diagnosticInfos, acknoledgements); Assert.AreEqual(subscriptionIds[0], publishedId); Assert.AreEqual(1, notificationMessage.NotificationData.Count); var items = notificationMessage.NotificationData.FirstOrDefault(); Assert.IsTrue(items.Body is Opc.Ua.DataChangeNotification); var monitoredItemsCollection = ((Opc.Ua.DataChangeNotification)items.Body).MonitoredItems; Assert.AreEqual(testSet.Length, monitoredItemsCollection.Count); Thread.Sleep(1000); if (updateValues && queueSize > 1) { // remaining queue Data should be sent in this publish m_requestHeader.Timestamp = DateTime.UtcNow; response = serverTestServices.Publish(m_requestHeader, acknoledgements, out publishedId, out availableSequenceNumbers, out moreNotifications, out notificationMessage, out StatusCodeCollection _, out diagnosticInfos); Assert.AreEqual(StatusCodes.Good, response.ServiceResult.Code); ServerFixtureUtils.ValidateResponse(response); ServerFixtureUtils.ValidateDiagnosticInfos(diagnosticInfos, acknoledgements); Assert.AreEqual(subscriptionIds[0], publishedId); Assert.AreEqual(1, notificationMessage.NotificationData.Count); items = notificationMessage.NotificationData.FirstOrDefault(); Assert.IsTrue(items.Body is Opc.Ua.DataChangeNotification); monitoredItemsCollection = ((Opc.Ua.DataChangeNotification)items.Body).MonitoredItems; Assert.AreEqual(testSet.Length * (queueSize - 1), monitoredItemsCollection.Count, testSet.Length); } // Call ResendData method with invalid subscription Id ResendDataCall(StatusCodes.BadSubscriptionIdInvalid, new UInt32Collection() { subscriptionIds.Last() + 20 }); // Nothing to publish since previous ResendData call did not execute m_requestHeader.Timestamp = DateTime.UtcNow; response = serverTestServices.Publish(m_requestHeader, acknoledgements, out publishedId, out availableSequenceNumbers, out moreNotifications, out notificationMessage, out StatusCodeCollection _, out diagnosticInfos); Assert.AreEqual(StatusCodes.Good, response.ServiceResult.Code); ServerFixtureUtils.ValidateResponse(response); ServerFixtureUtils.ValidateDiagnosticInfos(diagnosticInfos, acknoledgements); Assert.AreEqual(subscriptionIds[0], publishedId); Assert.AreEqual(0, notificationMessage.NotificationData.Count); resendDataRequestHeader.Timestamp = DateTime.UtcNow; SecureChannelContext.Current = resendDataSecurityContext; m_server.CloseSession(resendDataRequestHeader); } finally { //restore security context, that close connection can work SecureChannelContext.Current = securityContext; } }