/// <summary> /// Convert to credential model /// </summary> /// <param name="entry"></param> /// <returns></returns> private async Task <CredentialModel> ToUserNamePasswordCredentialAsync( PublishedNodesEntryModel entry) { var user = entry.OpcAuthenticationUsername; var password = entry.OpcAuthenticationPassword; if (string.IsNullOrEmpty(user)) { if (_cryptoProvider == null || string.IsNullOrEmpty(entry.EncryptedAuthUsername)) { return(null); } const string kInitializationVector = "alKGJdfsgidfasdO"; // See previous publisher var userBytes = await _cryptoProvider.DecryptAsync(kInitializationVector, Convert.FromBase64String(entry.EncryptedAuthUsername)); user = Encoding.UTF8.GetString(userBytes); if (entry.EncryptedAuthPassword != null) { var passwordBytes = await _cryptoProvider.DecryptAsync(kInitializationVector, Convert.FromBase64String(entry.EncryptedAuthPassword)); password = Encoding.UTF8.GetString(passwordBytes); } } return(new CredentialModel { Type = CredentialType.UserName, Value = _serializer.FromObject(new { user, password }) }); }
/// <summary> /// Create a multiple nodes model with opcplc nodes /// </summary> /// <param name="IIoTMultipleNodesTestContext">context</param> /// <param name="CancellationTokenSource">cancellation token</param> public static async Task <PublishedNodesEntryModel> CreateMultipleNodesModelAsync( IIoTMultipleNodesTestContext context, CancellationToken ct, int endpointIndex = 2, int numberOfNodes = 250) { await context.LoadSimulatedPublishedNodes(ct); PublishedNodesEntryModel nodesToPublish; if (context.SimulatedPublishedNodes.Count > 1) { var testPlc = context.SimulatedPublishedNodes.Skip(endpointIndex).First().Value; nodesToPublish = context.GetEntryModelWithoutNodes(testPlc); // We want to take several slow and fast nodes. // To make sure that we will not have missing values because of timing issues, // we will set publishing and sampling intervals to a lower value than the publishing // interval of the simulated OPC PLC. This will eliminate false-positives. nodesToPublish.OpcNodes = testPlc.OpcNodes .Where(node => !node.Id.Contains("bad", StringComparison.OrdinalIgnoreCase)) .Where(node => node.Id.Contains("slow", StringComparison.OrdinalIgnoreCase) || node.Id.Contains("fast", StringComparison.OrdinalIgnoreCase)) .Take(numberOfNodes) .Select(opcNode => { var opcPlcPublishingInterval = opcNode.OpcPublishingInterval; opcNode.OpcPublishingInterval = opcPlcPublishingInterval / 2; opcNode.OpcSamplingInterval = opcPlcPublishingInterval / 4; opcNode.QueueSize = 4; return(opcNode); }) .ToArray(); context.ConsumedOpcUaNodes.AddOrUpdate(testPlc.EndpointUrl, nodesToPublish); } else { var opcPlcIp = context.OpcPlcConfig.Urls.Split(TestConstants.SimulationUrlsSeparator)[endpointIndex]; nodesToPublish = new PublishedNodesEntryModel { EndpointUrl = $"opc.tcp://{opcPlcIp}:50000", UseSecurity = false }; var nodes = new List <OpcUaNodesModel>(); for (int i = 0; i < numberOfNodes; i++) { nodes.Add(new OpcUaNodesModel { Id = $"ns=2;s=SlowUInt{i + 1}", OpcPublishingInterval = 10000 / 2, OpcSamplingInterval = 10000 / 4, QueueSize = 4, }); } nodesToPublish.OpcNodes = nodes.ToArray(); context.ConsumedOpcUaNodes.Add(opcPlcIp, nodesToPublish); } return(nodesToPublish); }
private static void AssertSameNodes(PublishedNodesEntryModel endpoint, List <OpcNodeModel> nodes) { Assert.Equal(endpoint.OpcNodes.Count, nodes.Count); for (var k = 0; k < endpoint.OpcNodes.Count; k++) { Assert.True(endpoint.OpcNodes[k].IsSame(nodes[k])); } }
public void Test_StartPublishing250Nodes_Expect_Success() { var cts = new CancellationTokenSource(TimeSpan.FromMinutes(1)); _context.LoadSimulatedPublishedNodes(cts.Token).GetAwaiter().GetResult(); PublishedNodesEntryModel nodesToPublish; if (_context.SimulatedPublishedNodes.Count > 1) { var testPlc = _context.SimulatedPublishedNodes.Skip(2).First().Value; nodesToPublish = _context.GetEntryModelWithoutNodes(testPlc); // We want to take one of the slow nodes that updates each 10 seconds. // To make sure that we will not have missing values because of timing issues, // we will set publishing and sampling intervals to a lower value than the publishing // interval of the simulated OPC PLC. This will eliminate false-positives. nodesToPublish.OpcNodes = testPlc.OpcNodes .Take(250) .Select(opcNode => { var opcPlcPublishingInterval = opcNode.OpcPublishingInterval; opcNode.OpcPublishingInterval = opcPlcPublishingInterval / 2; opcNode.OpcSamplingInterval = opcPlcPublishingInterval / 4; return(opcNode); }) .ToArray(); _context.ConsumedOpcUaNodes.Add(testPlc.EndpointUrl, nodesToPublish); } else { var opcPlcIp = _context.OpcPlcConfig.Urls.Split(TestConstants.SimulationUrlsSeparator)[2]; nodesToPublish = new PublishedNodesEntryModel { EndpointUrl = $"opc.tcp://{opcPlcIp}:50000", UseSecurity = false }; var nodes = new List <OpcUaNodesModel>(); for (int i = 0; i < 250; i++) { nodes.Add(new OpcUaNodesModel { Id = $"ns=2;s=SlowUInt{i+1}", OpcPublishingInterval = 10000 / 2, OpcSamplingInterval = 10000 / 4 }); } nodesToPublish.OpcNodes = nodes.ToArray(); _context.ConsumedOpcUaNodes.Add(opcPlcIp, nodesToPublish); } cts = new CancellationTokenSource(TimeSpan.FromMinutes(5)); TestHelper.SwitchToStandaloneModeAndPublishNodesAsync(new[] { nodesToPublish }, _context, cts.Token).GetAwaiter().GetResult(); }
/// <summary> /// Create a Copy except the OpcNodes array /// </summary> /// <param name="testPlc">Source object</param> /// <returns>Copy</returns> public PublishedNodesEntryModel GetEntryModelWithoutNodes(PublishedNodesEntryModel testPlc) { return(new PublishedNodesEntryModel { EncryptedAuthPassword = testPlc.EncryptedAuthPassword, EncryptedAuthUsername = testPlc.EncryptedAuthUsername, EndpointUrl = testPlc.EndpointUrl, OpcAuthenticationPassword = testPlc.OpcAuthenticationPassword, OpcAuthenticationUsername = testPlc.OpcAuthenticationUsername, UseSecurity = testPlc.UseSecurity, OpcNodes = null }); }
/// <summary> /// Create a single node model with opcplc node /// </summary> /// <param name="IIoTMultipleNodesTestContext">context</param> /// <param name="CancellationTokenSource">cancellation token</param> public static async Task <PublishedNodesEntryModel> CreateSingleNodeModelAsync(IIoTMultipleNodesTestContext context, CancellationToken ct) { IDictionary <string, PublishedNodesEntryModel> simulatedPublishedNodesConfiguration = new Dictionary <string, PublishedNodesEntryModel>(0); // With the nested edge test servers don't have public IP addresses and cannot be accessed in this way if (context.IoTEdgeConfig.NestedEdgeFlag != "Enable") { simulatedPublishedNodesConfiguration = await GetSimulatedPublishedNodesConfigurationAsync(context, ct); } PublishedNodesEntryModel model; if (simulatedPublishedNodesConfiguration.Count > 0) { model = simulatedPublishedNodesConfiguration[simulatedPublishedNodesConfiguration.Keys.First()]; } else { var opcPlcIp = context.OpcPlcConfig.Urls.Split(TestConstants.SimulationUrlsSeparator)[0]; model = new PublishedNodesEntryModel { EndpointUrl = $"opc.tcp://{opcPlcIp}:50000", UseSecurity = false, OpcNodes = new OpcUaNodesModel[] { new OpcUaNodesModel { Id = "ns=2;s=SlowUInt1", OpcPublishingInterval = 10000, } } }; } // We want to take one of the slow nodes that updates each 10 seconds. // To make sure that we will not have missing values because of timing issues, // we will set publishing and sampling intervals to a lower value than the publishing // interval of the simulated OPC PLC. This will eliminate false-positives. model.OpcNodes = model.OpcNodes .Where(node => !node.Id.Contains("bad", StringComparison.OrdinalIgnoreCase)) .Where(opcNode => opcNode.Id.Contains("SlowUInt")) .Take(1).Select(opcNode => { var opcPlcPublishingInterval = opcNode.OpcPublishingInterval; opcNode.OpcPublishingInterval = opcPlcPublishingInterval / 2; opcNode.OpcSamplingInterval = opcPlcPublishingInterval / 4; opcNode.QueueSize = 4; return(opcNode); }) .ToArray(); return(model); }
public void Test_StartPublishingSingleNode_Expect_Success() { var cts = new CancellationTokenSource(TestConstants.MaxTestTimeoutMilliseconds); var simulatedPublishedNodesConfiguration = new Dictionary <string, PublishedNodesEntryModel>(0); // With the nested edge test servers don't have public IP addresses and cannot be accessed in this way if (_context.IoTEdgeConfig.NestedEdgeFlag != "Enable") { TestHelper.GetSimulatedPublishedNodesConfigurationAsync(_context, cts.Token).GetAwaiter().GetResult(); } PublishedNodesEntryModel model; if (simulatedPublishedNodesConfiguration.Count > 0) { model = simulatedPublishedNodesConfiguration[simulatedPublishedNodesConfiguration.Keys.First()]; } else { var opcPlcIp = _context.OpcPlcConfig.Urls.Split(TestConstants.SimulationUrlsSeparator)[0]; model = new PublishedNodesEntryModel { EndpointUrl = $"opc.tcp://{opcPlcIp}:50000", UseSecurity = false, OpcNodes = new OpcUaNodesModel[] { new OpcUaNodesModel { Id = "ns=2;s=SlowUInt1", OpcPublishingInterval = 10000 } } }; } // We want to take one of the slow nodes that updates each 10 seconds. // To make sure that we will not have missing values because of timing issues, // we will set publishing and sampling intervals to a lower value than the publishing // interval of the simulated OPC PLC. This will eliminate false-positives. model.OpcNodes = model.OpcNodes .Take(1).Select(opcNode => { var opcPlcPublishingInterval = opcNode.OpcPublishingInterval; opcNode.OpcPublishingInterval = opcPlcPublishingInterval / 2; opcNode.OpcSamplingInterval = opcPlcPublishingInterval / 4; return(opcNode); }) .ToArray(); TestHelper.SwitchToStandaloneModeAndPublishNodesAsync(new[] { model }, _context, cts.Token).GetAwaiter().GetResult(); Task.Delay(TestConstants.DefaultTimeoutInMilliseconds).GetAwaiter().GetResult(); //wait some time till the updated pn.json is reflected }
/// <summary> /// Transforms a published nodes model connection header to a Connection Model object /// </summary> public ConnectionModel ToConnectionModel(PublishedNodesEntryModel model, StandaloneCliModel standaloneCliModel) { return(new ConnectionModel { Group = model.DataSetWriterGroup, // Exclude the DataSetWriterId since it is not part of the connection model Endpoint = new EndpointModel { Url = model.EndpointUrl?.OriginalString, SecurityMode = model.UseSecurity ? SecurityMode.Best : SecurityMode.None, }, User = model.OpcAuthenticationMode != OpcAuthenticationMode.UsernamePassword ? null : ToUserNamePasswordCredentialAsync(model).GetAwaiter().GetResult(), }); }
/// <summary> /// Get the node models from entry /// </summary> /// <param name="item"></param> /// <param name="scaleTestCount"></param> /// <returns></returns> private IEnumerable <OpcNodeModel> GetNodeModels(PublishedNodesEntryModel item, int scaleTestCount = 1) { if (item.OpcNodes != null) { foreach (var node in item.OpcNodes) { if (string.IsNullOrEmpty(node.Id)) { node.Id = node.ExpandedNodeId; } if (string.IsNullOrEmpty(node.DisplayName)) { node.DisplayName = node.Id; } if (scaleTestCount == 1) { yield return(node); } else { for (var i = 0; i < scaleTestCount; i++) { yield return(new OpcNodeModel { Id = node.Id, DisplayName = $"{node.DisplayName}_{i}", ExpandedNodeId = node.ExpandedNodeId, HeartbeatInterval = node.HeartbeatInterval, HeartbeatIntervalTimespan = node.HeartbeatIntervalTimespan, OpcPublishingInterval = node.OpcPublishingInterval, OpcPublishingIntervalTimespan = node.OpcPublishingIntervalTimespan, OpcSamplingInterval = node.OpcSamplingInterval, OpcSamplingIntervalTimespan = node.OpcSamplingIntervalTimespan, SkipFirst = node.SkipFirst }); } } } } if (item.NodeId?.Identifier != null) { yield return(new OpcNodeModel { Id = item.NodeId.Identifier, }); } }
public void UseSecuritySerializationTest() { var newtonSoftJsonSerializer = new NewtonSoftJsonSerializer(); var model = new PublishedNodesEntryModel { EndpointUrl = new Uri("opc.tcp://localhost:50000"), OpcNodes = new List <OpcNodeModel> { new OpcNodeModel { Id = "i=2258" } } }; var modeJson = newtonSoftJsonSerializer.SerializeToString(model); Assert.Contains("\"UseSecurity\":false", modeJson); model = new PublishedNodesEntryModel { EndpointUrl = new Uri("opc.tcp://localhost:50000"), UseSecurity = false, OpcNodes = new List <OpcNodeModel> { new OpcNodeModel { Id = "i=2258" } } }; modeJson = newtonSoftJsonSerializer.SerializeToString(model); Assert.Contains("\"UseSecurity\":false", modeJson); model = new PublishedNodesEntryModel { EndpointUrl = new Uri("opc.tcp://localhost:50000"), UseSecurity = true, OpcNodes = new List <OpcNodeModel> { new OpcNodeModel { Id = "i=2258" } } }; modeJson = newtonSoftJsonSerializer.SerializeToString(model); Assert.Contains("\"UseSecurity\":true", modeJson); }
public void OpcAuthenticationModeSerializationTest() { var newtonSoftJsonSerializer = new NewtonSoftJsonSerializer(); var model = new PublishedNodesEntryModel { EndpointUrl = new Uri("opc.tcp://localhost:50000"), OpcNodes = new List <OpcNodeModel> { new OpcNodeModel { Id = "i=2258" } } }; var modeJson = newtonSoftJsonSerializer.SerializeToString(model); Assert.Contains("\"OpcAuthenticationMode\":\"anonymous\"", modeJson); model = new PublishedNodesEntryModel { EndpointUrl = new Uri("opc.tcp://localhost:50000"), OpcAuthenticationMode = OpcAuthenticationMode.Anonymous, OpcNodes = new List <OpcNodeModel> { new OpcNodeModel { Id = "i=2258" } } }; modeJson = newtonSoftJsonSerializer.SerializeToString(model); Assert.Contains("\"OpcAuthenticationMode\":\"anonymous\"", modeJson); model = new PublishedNodesEntryModel { EndpointUrl = new Uri("opc.tcp://localhost:50000"), OpcAuthenticationMode = OpcAuthenticationMode.UsernamePassword, OpcNodes = new List <OpcNodeModel> { new OpcNodeModel { Id = "i=2258" } } }; modeJson = newtonSoftJsonSerializer.SerializeToString(model); Assert.Contains("\"OpcAuthenticationMode\":\"usernamePassword\"", modeJson); }
/// <summary> /// Get the node models from entry /// </summary> /// <param name="item"></param> /// <returns></returns> private IEnumerable <OpcNodeModel> GetNodeModels(PublishedNodesEntryModel item) { if (item.OpcNodes != null) { foreach (var node in item.OpcNodes) { if (string.IsNullOrEmpty(node.Id)) { node.Id = node.ExpandedNodeId; } yield return(node); } } if (item.NodeId?.Identifier != null) { yield return(new OpcNodeModel { Id = item.NodeId.Identifier, }); } }
/// <summary> /// Create an api model /// </summary> public static PublishNodesEndpointApiModel ToApiModel( this PublishedNodesEntryModel model) { if (model == null) { return(null); } return(new PublishNodesEndpointApiModel { DataSetWriterGroup = model.DataSetWriterGroup, DataSetWriterId = model.DataSetWriterId, DataSetPublishingInterval = model.DataSetPublishingInterval, EndpointUrl = model.EndpointUrl, UseSecurity = model.UseSecurity, Password = model.OpcAuthenticationPassword, UserName = model.OpcAuthenticationUsername, OpcNodes = model.OpcNodes != null ? model.OpcNodes.Select(n => n.ToApiModel()).ToList() : null, }); }
public async Task Test_PublishNodes_NullOrEmpty() { InitStandaloneJobOrchestrator(); // Check null request. await FluentActions .Invoking(async() => await _standaloneJobOrchestrator .PublishNodesAsync(null) .ConfigureAwait(false)) .Should() .ThrowAsync <MethodCallStatusException>() .WithMessage($"{{\"Message\":\"Response 400 null request is provided\",\"Details\":{{}}}}") .ConfigureAwait(false); var request = new PublishedNodesEntryModel { EndpointUrl = new Uri("opc.tcp://opcplc:50000"), }; // Check null OpcNodes in request. await FluentActions .Invoking(async() => await _standaloneJobOrchestrator .PublishNodesAsync(request) .ConfigureAwait(false)) .Should() .ThrowAsync <MethodCallStatusException>() .WithMessage($"{{\"Message\":\"Response 400 null or empty OpcNodes is provided in request\",\"Details\":{{}}}}") .ConfigureAwait(false); request.OpcNodes = new List <OpcNodeModel>(); // Check empty OpcNodes in request. await FluentActions .Invoking(async() => await _standaloneJobOrchestrator .PublishNodesAsync(request) .ConfigureAwait(false)) .Should() .ThrowAsync <MethodCallStatusException>() .WithMessage($"{{\"Message\":\"Response 400 null or empty OpcNodes is provided in request\",\"Details\":{{}}}}") .ConfigureAwait(false); }
/// <summary> /// Create an api model from service model ignoring the password /// </summary> public static PublishNodesEndpointApiModel ToApiModel( this PublishedNodesEntryModel endpoint) { if (endpoint == null) { return(null); } return(new PublishNodesEndpointApiModel { EndpointUrl = endpoint.EndpointUrl.OriginalString, UseSecurity = endpoint.UseSecurity, OpcAuthenticationMode = (AuthenticationMode)endpoint.OpcAuthenticationMode, UserName = endpoint.OpcAuthenticationUsername, DataSetWriterGroup = endpoint.DataSetWriterGroup, DataSetWriterId = endpoint.DataSetWriterId, Tag = endpoint.Tag, DataSetPublishingIntervalTimespan = endpoint.DataSetPublishingIntervalTimespan, // only fill the DataSetPublishingInterval if the DataSetPublishingIntervalTimespan // was not provided. DataSetPublishingInterval = !endpoint.DataSetPublishingIntervalTimespan.HasValue ? endpoint.DataSetPublishingInterval : null, }); }
async Task SubscribeUnsubscribeDirectMethodTest(MessagingMode messagingMode, bool incremental) { var ioTHubEdgeBaseDeployment = new IoTHubEdgeBaseDeployment(_context); var ioTHubPublisherDeployment = new IoTHubPublisherDeployment(_context, messagingMode); _iotHubPublisherModuleName = ioTHubPublisherDeployment.ModuleName; var cts = new CancellationTokenSource(TestConstants.MaxTestTimeoutMilliseconds); // Make sure that there is no active monitoring. await TestHelper.StopMonitoringIncomingMessagesAsync(_context, cts.Token).ConfigureAwait(false); // Clean publishednodes.json. await TestHelper.CleanPublishedNodesJsonFilesAsync(_context).ConfigureAwait(false); // Create base edge deployment. var baseDeploymentResult = await ioTHubEdgeBaseDeployment.CreateOrUpdateLayeredDeploymentAsync(cts.Token).ConfigureAwait(false); Assert.True(baseDeploymentResult, "Failed to create/update new edge base deployment."); _output.WriteLine("Created/Updated new edge base deployment."); // Create layered edge deployment. var layeredDeploymentResult = await ioTHubPublisherDeployment.CreateOrUpdateLayeredDeploymentAsync(cts.Token).ConfigureAwait(false); Assert.True(layeredDeploymentResult, "Failed to create/update layered deployment for publisher module."); _output.WriteLine("Created/Updated layered deployment for publisher module."); await TestHelper.SwitchToStandaloneModeAsync(_context, cts.Token).ConfigureAwait(false); // We will wait for module to be deployed. await _context.RegistryHelper.WaitForSuccessfulDeploymentAsync( ioTHubPublisherDeployment.GetDeploymentConfiguration(), cts.Token ).ConfigureAwait(false); await _context.RegistryHelper.WaitForIIoTModulesConnectedAsync( _context.DeviceConfig.DeviceId, cts.Token, new string[] { ioTHubPublisherDeployment.ModuleName } ).ConfigureAwait(false); // We've observed situations when even after the above waits the module did not yet restart. // That leads to situations where the publishing of nodes happens just before the restart to apply // new container creation options. After restart persisted nodes are picked up, but on the telemetry side // the restart causes dropped messages to be detected. That happens because just before the restart OPC Publisher // manages to send some telemetry. This wait makes sure that we do not run the test while restart is happening. await Task.Delay(TestConstants.AwaitInitInMilliseconds, cts.Token).ConfigureAwait(false); // Call GetConfiguredEndpoints direct method, initially there should be no endpoints var responseGetConfiguredEndpoints = await CallMethodAsync( new MethodParameterModel { Name = TestConstants.DirectMethodNames.GetConfiguredEndpoints }, cts.Token ).ConfigureAwait(false); Assert.Equal((int)HttpStatusCode.OK, responseGetConfiguredEndpoints.Status); var configuredEndpointsResponse = _serializer.Deserialize <GetConfiguredEndpointsResponseApiModel>(responseGetConfiguredEndpoints.JsonPayload); Assert.Equal(0, configuredEndpointsResponse.Endpoints.Count); // Use test event processor to verify data send to IoT Hub (expected* set to zero // as data gap analysis is not part of this test case). await TestHelper.StartMonitoringIncomingMessagesAsync(_context, 0, 0, 0, cts.Token).ConfigureAwait(false); var model = await TestHelper.CreateSingleNodeModelAsync(_context, cts.Token).ConfigureAwait(false); var expectedModel = model.ToApiModel(); expectedModel.OpcNodes = new List <PublishedNodeApiModel>(); var initialOpcPublishingInterval = model.OpcNodes[0].OpcPublishingInterval; for (var i = 0; i < 4; i++) { model.OpcNodes[0].Id = $"nsu=http://microsoft.com/Opc/OpcPlc/;s=SlowUInt{i + 1}"; if (incremental) { model.OpcNodes[0].OpcPublishingInterval = (uint)(initialOpcPublishingInterval + i * 1000); model.OpcNodes[0].OpcSamplingInterval = model.OpcNodes[0].OpcPublishingInterval / 2; } var request = model.ToApiModel(); expectedModel.OpcNodes.Add(new PublishedNodeApiModel { Id = request.OpcNodes[0].Id, DataSetFieldId = request.OpcNodes[0].DataSetFieldId, DisplayName = request.OpcNodes[0].DisplayName, ExpandedNodeId = model.OpcNodes[0].ExpandedNodeId, HeartbeatInterval = request.OpcNodes[0].HeartbeatInterval, OpcPublishingInterval = request.OpcNodes[0].OpcSamplingInterval, OpcSamplingInterval = request.OpcNodes[0].OpcSamplingInterval, QueueSize = request.OpcNodes[0].QueueSize, SkipFirst = request.OpcNodes[0].SkipFirst, }); // Call Publish direct method var response = await CallMethodAsync( new MethodParameterModel { Name = TestConstants.DirectMethodNames.PublishNodes, JsonPayload = _serializer.SerializeToString(request) }, cts.Token ).ConfigureAwait(false); Assert.Equal((int)HttpStatusCode.OK, response.Status); // Wait some time to generate events to process. await Task.Delay(TestConstants.AwaitInitInMilliseconds, cts.Token).ConfigureAwait(false); // Create request for GetConfiguredNodesOnEndpoint method call var nodesOnEndpoint = new PublishedNodesEntryModel { EndpointUrl = request.EndpointUrl, }; var requestGetConfiguredNodesOnEndpoint = nodesOnEndpoint.ToApiModel(); // Call GetConfiguredEndpoints direct method responseGetConfiguredEndpoints = await CallMethodAsync( new MethodParameterModel { Name = TestConstants.DirectMethodNames.GetConfiguredEndpoints }, cts.Token ).ConfigureAwait(false); Assert.Equal((int)HttpStatusCode.OK, responseGetConfiguredEndpoints.Status); configuredEndpointsResponse = _serializer.Deserialize <GetConfiguredEndpointsResponseApiModel>(responseGetConfiguredEndpoints.JsonPayload); Assert.Equal(1, configuredEndpointsResponse.Endpoints.Count); TestHelper.Publisher.AssertEndpointModel(configuredEndpointsResponse.Endpoints[0], request); // Call GetConfiguredNodesOnEndpoint direct method var responseGetConfiguredNodesOnEndpoint = await CallMethodAsync( new MethodParameterModel { Name = TestConstants.DirectMethodNames.GetConfiguredNodesOnEndpoint, JsonPayload = _serializer.SerializeToString(requestGetConfiguredNodesOnEndpoint) }, cts.Token ).ConfigureAwait(false); Assert.Equal((int)HttpStatusCode.OK, responseGetConfiguredNodesOnEndpoint.Status); var jsonResponse = _serializer.Deserialize <GetConfiguredNodesOnEndpointResponseApiModel>(responseGetConfiguredNodesOnEndpoint.JsonPayload); Assert.Equal(jsonResponse.OpcNodes.Count, i + 1); Assert.Equal(jsonResponse.OpcNodes[i].Id, $"nsu=http://microsoft.com/Opc/OpcPlc/;s=SlowUInt{i + 1}"); } // Call GetDiagnosticInfo direct method var responseGetDiagnosticInfo = await CallMethodAsync( new MethodParameterModel { Name = TestConstants.DirectMethodNames.GetDiagnosticInfo, }, cts.Token ).ConfigureAwait(false); Assert.Equal((int)HttpStatusCode.OK, responseGetDiagnosticInfo.Status); var diagInfo = _serializer.Deserialize <List <DiagnosticInfoApiModel> >(responseGetDiagnosticInfo.JsonPayload); Assert.Equal(diagInfo.Count, 1); TestHelper.Publisher.AssertEndpointDiagnosticInfoModel(expectedModel, diagInfo[0]); // Stop monitoring and get the result. var publishingMonitoringResultJson = await TestHelper.StopMonitoringIncomingMessagesAsync(_context, cts.Token).ConfigureAwait(false); Assert.True(publishingMonitoringResultJson.TotalValueChangesCount > 0, "No messages received at IoT Hub"); Assert.True(publishingMonitoringResultJson.DroppedValueCount == 0, $"Dropped messages detected: {publishingMonitoringResultJson.DroppedValueCount}"); Assert.True(publishingMonitoringResultJson.DuplicateValueCount == 0, $"Duplicate values detected: {publishingMonitoringResultJson.DuplicateValueCount}"); Assert.Equal(0U, publishingMonitoringResultJson.DroppedSequenceCount); // Uncomment once bug generating duplicate sequence numbers is resolved. //Assert.Equal(0U, publishingMonitoringResultJson.DuplicateSequenceCount); Assert.Equal(0U, publishingMonitoringResultJson.ResetSequenceCount); model.OpcNodes = null; // Call Unpublish direct method var responseUnpublish = await CallMethodAsync( new MethodParameterModel { Name = TestConstants.DirectMethodNames.UnpublishAllNodes, JsonPayload = _serializer.SerializeToString(model.ToApiModel()) }, cts.Token ).ConfigureAwait(false); Assert.Equal((int)HttpStatusCode.OK, responseUnpublish.Status); // Wait till the publishing has stopped. await Task.Delay(TestConstants.AwaitCleanupInMilliseconds, cts.Token).ConfigureAwait(false); // Call GetDiagnosticInfo direct method var responseGetDiagnosticInfoFinal = await CallMethodAsync( new MethodParameterModel { Name = TestConstants.DirectMethodNames.GetDiagnosticInfo, }, cts.Token ).ConfigureAwait(false); Assert.Equal((int)HttpStatusCode.OK, responseGetDiagnosticInfoFinal.Status); var diagInfoList = _serializer.Deserialize <List <DiagnosticInfoApiModel> >(responseGetDiagnosticInfoFinal.JsonPayload); Assert.Equal(diagInfoList.Count, 0); // Use test event processor to verify data send to IoT Hub (expected* set to zero // as data gap analysis is not part of this test case). await TestHelper.StartMonitoringIncomingMessagesAsync(_context, 0, 0, 0, cts.Token).ConfigureAwait(false); // Wait some time to generate events to process await Task.Delay(TestConstants.AwaitCleanupInMilliseconds, cts.Token).ConfigureAwait(false); // Stop monitoring and get the result. var unpublishingMonitoringResultJson = await TestHelper.StopMonitoringIncomingMessagesAsync(_context, cts.Token).ConfigureAwait(false); Assert.True(unpublishingMonitoringResultJson.TotalValueChangesCount == 0, $"Messages received at IoT Hub: {unpublishingMonitoringResultJson.TotalValueChangesCount}"); }
async Task SubscribeUnsubscribeDirectMethodLegacyPublisherTest() { var ioTHubEdgeBaseDeployment = new IoTHubEdgeBaseDeployment(_context); var ioTHubLegacyPublisherDeployment = new IoTHubLegacyPublisherDeployments(_context); _iotHubPublisherModuleName = ioTHubLegacyPublisherDeployment.ModuleName; var cts = new CancellationTokenSource(TestConstants.MaxTestTimeoutMilliseconds); // Make sure that there is no active monitoring. await TestHelper.StopMonitoringIncomingMessagesAsync(_context, cts.Token).ConfigureAwait(false); // Clean publishednodes.json. await TestHelper.CleanPublishedNodesJsonFilesAsync(_context).ConfigureAwait(false); // Create base edge deployment. var baseDeploymentResult = await ioTHubEdgeBaseDeployment.CreateOrUpdateLayeredDeploymentAsync(cts.Token).ConfigureAwait(false); Assert.True(baseDeploymentResult, "Failed to create/update new edge base deployment."); _output.WriteLine("Created/Updated new edge base deployment."); // Create layered edge deployment. var layeredDeploymentResult1 = await ioTHubLegacyPublisherDeployment.CreateOrUpdateLayeredDeploymentAsync(cts.Token).ConfigureAwait(false); Assert.True(layeredDeploymentResult1, "Failed to create/update layered deployment for legacy publisher module."); _output.WriteLine("Created/Updated layered deployment for legacy publisher module."); var model = await TestHelper.CreateSingleNodeModelAsync(_context, cts.Token).ConfigureAwait(false); await TestHelper.SwitchToStandaloneModeAsync(_context, cts.Token).ConfigureAwait(false); // We will wait for module to be deployed. await _context.RegistryHelper.WaitForSuccessfulDeploymentAsync( ioTHubLegacyPublisherDeployment.GetDeploymentConfiguration(), cts.Token ).ConfigureAwait(false); await _context.RegistryHelper.WaitForIIoTModulesConnectedAsync( _context.DeviceConfig.DeviceId, cts.Token, new string[] { ioTHubLegacyPublisherDeployment.ModuleName } ).ConfigureAwait(false); // Call GetConfiguredEndpoints direct method, initially there should be no endpoints var responseGetConfiguredEndpoints = await CallMethodAsync( new MethodParameterModel { Name = TestConstants.DirectMethodLegacyNames.GetConfiguredEndpoints }, cts.Token ).ConfigureAwait(false); Assert.Equal((int)HttpStatusCode.OK, responseGetConfiguredEndpoints.Status); var configuredEndpointsResponse = _serializer.Deserialize <GetConfiguredEndpointsResponseApiModel>(responseGetConfiguredEndpoints.JsonPayload); Assert.Equal(0, configuredEndpointsResponse.Endpoints.Count); var request = model.ToApiModel(); // Call Publish direct method var response = await CallMethodAsync( new MethodParameterModel { Name = TestConstants.DirectMethodLegacyNames.PublishNodes, JsonPayload = _serializer.SerializeToString(request) }, cts.Token ).ConfigureAwait(false); Assert.Equal((int)HttpStatusCode.OK, response.Status); // Use test event processor to verify data send to IoT Hub (expected* set to zero // as data gap analysis is not part of this test case). await TestHelper.StartMonitoringIncomingMessagesAsync(_context, 0, 0, 0, cts.Token).ConfigureAwait(false); // Wait some time to generate events to process. await Task.Delay(TestConstants.AwaitDataInMilliseconds, cts.Token).ConfigureAwait(false); // Call GetConfiguredEndpoints direct method responseGetConfiguredEndpoints = await CallMethodAsync( new MethodParameterModel { Name = TestConstants.DirectMethodLegacyNames.GetConfiguredEndpoints }, cts.Token ).ConfigureAwait(false); Assert.Equal((int)HttpStatusCode.OK, responseGetConfiguredEndpoints.Status); configuredEndpointsResponse = _serializer.Deserialize <GetConfiguredEndpointsResponseApiModel>(responseGetConfiguredEndpoints.JsonPayload); Assert.Equal(configuredEndpointsResponse.Endpoints.Count, 1); TestHelper.Publisher.AssertEndpointModel(configuredEndpointsResponse.Endpoints[0], request); // Create request for GetConfiguredNodesOnEndpoint method call var nodesOnEndpoint = new PublishedNodesEntryModel { EndpointUrl = request.EndpointUrl, }; var requestGetConfiguredNodesOnEndpoint = nodesOnEndpoint.ToApiModel(); // Call GetConfiguredNodesOnEndpoint direct method var responseGetConfiguredNodesOnEndpoint = await CallMethodAsync( new MethodParameterModel { Name = TestConstants.DirectMethodLegacyNames.GetConfiguredNodesOnEndpoint, JsonPayload = _serializer.SerializeToString(requestGetConfiguredNodesOnEndpoint) }, cts.Token ).ConfigureAwait(false); Assert.Equal((int)HttpStatusCode.OK, responseGetConfiguredNodesOnEndpoint.Status); var jsonResponse = _serializer.Deserialize <GetConfiguredNodesOnEndpointResponseApiModel>( responseGetConfiguredNodesOnEndpoint.JsonPayload); Assert.Equal(1, jsonResponse.OpcNodes.Count); Assert.Equal(request.OpcNodes[0].Id, jsonResponse.OpcNodes[0].Id); // Call GetDiagnosticInfo direct method var responseGetDiagnosticInfo = await CallMethodAsync( new MethodParameterModel { Name = TestConstants.DirectMethodLegacyNames.GetDiagnosticInfo, }, cts.Token ).ConfigureAwait(false); Assert.Equal((int)HttpStatusCode.OK, responseGetDiagnosticInfo.Status); var diagInfo = _serializer.Deserialize <DiagnosticInfoLegacyModel>(responseGetDiagnosticInfo.JsonPayload); Assert.Equal(diagInfo.NumberOfOpcSessionsConfigured, 1); Assert.Equal(diagInfo.NumberOfOpcSessionsConnected, 1); Assert.Equal(diagInfo.NumberOfOpcMonitoredItemsConfigured, 1); Assert.Equal(diagInfo.NumberOfOpcMonitoredItemsMonitored, 1); Assert.True(diagInfo.SentMessages > 0); Assert.Equal(diagInfo.FailedMessages, 0); Assert.Equal(diagInfo.TooLargeCount, 0); // Stop monitoring and get the result. var publishingMonitoringResultJson = await TestHelper.StopMonitoringIncomingMessagesAsync(_context, cts.Token).ConfigureAwait(false); Assert.True(publishingMonitoringResultJson.TotalValueChangesCount > 0, "No messages received at IoT Hub"); Assert.True(publishingMonitoringResultJson.DroppedValueCount == 0, $"Dropped messages detected: {publishingMonitoringResultJson.DroppedValueCount}"); Assert.True(publishingMonitoringResultJson.DuplicateValueCount == 0, $"Duplicate values detected: {publishingMonitoringResultJson.DuplicateValueCount}"); Assert.Equal(0U, publishingMonitoringResultJson.DroppedSequenceCount); // Uncomment once bug generating duplicate sequence numbers is resolved. //Assert.Equal(0U, publishingMonitoringResultJson.DuplicateSequenceCount); Assert.Equal(0U, publishingMonitoringResultJson.ResetSequenceCount); // Call Unpublish direct method response = await CallMethodAsync( new MethodParameterModel { Name = TestConstants.DirectMethodLegacyNames.UnpublishNodes, JsonPayload = _serializer.SerializeToString(request) }, cts.Token ).ConfigureAwait(false); Assert.Equal((int)HttpStatusCode.OK, response.Status); // Wait till the publishing has stopped. await Task.Delay(TestConstants.AwaitCleanupInMilliseconds, cts.Token).ConfigureAwait(false); // Call GetDiagnosticInfo direct method responseGetDiagnosticInfo = await CallMethodAsync( new MethodParameterModel { Name = TestConstants.DirectMethodLegacyNames.GetDiagnosticInfo, }, cts.Token ).ConfigureAwait(false); Assert.Equal((int)HttpStatusCode.OK, responseGetDiagnosticInfo.Status); diagInfo = _serializer.Deserialize <DiagnosticInfoLegacyModel>(responseGetDiagnosticInfo.JsonPayload); Assert.Equal(diagInfo.NumberOfOpcSessionsConfigured, 0); // Use test event processor to verify data send to IoT Hub (expected* set to zero // as data gap analysis is not part of this test case). await TestHelper.StartMonitoringIncomingMessagesAsync(_context, 0, 0, 0, cts.Token).ConfigureAwait(false); // Wait some time to generate events to process await Task.Delay(TestConstants.AwaitCleanupInMilliseconds, cts.Token).ConfigureAwait(false); // Stop monitoring and get the result. var unpublishingMonitoringResultJson = await TestHelper.StopMonitoringIncomingMessagesAsync(_context, cts.Token); Assert.True(unpublishingMonitoringResultJson.TotalValueChangesCount == 0, $"Messages received at IoT Hub: {unpublishingMonitoringResultJson.TotalValueChangesCount}"); }
public async Task PublishNodesStressTest() { var standaloneCliModelProviderMock = new Mock <IStandaloneCliModelProvider>(); var agentConfigProviderMock = new Mock <IAgentConfigProvider>(); var engineConfigMock = new Mock <IEngineConfiguration>(); var clientConfignMock = new Mock <IClientServicesConfig>(); var newtonSoftJsonSerializer = new NewtonSoftJsonSerializer(); var jobSerializer = new PublisherJobSerializer(newtonSoftJsonSerializer); var logger = TraceLogger.Create(); var publishedNodesJobConverter = new PublishedNodesJobConverter(logger, newtonSoftJsonSerializer, engineConfigMock.Object, clientConfignMock.Object); using (var fileStream = new FileStream(_tempFile, FileMode.Open, FileAccess.Write)) { fileStream.Write(Encoding.UTF8.GetBytes("[]")); } var standaloneCliModel = new StandaloneCliModel { PublishedNodesFile = _tempFile, PublishedNodesSchemaFile = "Storage/publishednodesschema.json" }; standaloneCliModelProviderMock.Setup(p => p.StandaloneCliModel).Returns(standaloneCliModel); agentConfigProviderMock.Setup(p => p.Config).Returns(new AgentConfigModel()); var publishedNodesProvider = new PublishedNodesProvider(standaloneCliModelProviderMock.Object, logger); var orchestrator = new StandaloneJobOrchestrator( publishedNodesJobConverter, standaloneCliModelProviderMock.Object, agentConfigProviderMock.Object, jobSerializer, logger, publishedNodesProvider, newtonSoftJsonSerializer ); var numberOfEndpoints = 100; var numberOfNodes = 1000; var payload = new List <PublishedNodesEntryModel>(); for (int endpointIndex = 0; endpointIndex < numberOfEndpoints; ++endpointIndex) { var model = new PublishedNodesEntryModel { EndpointUrl = new Uri($"opc.tcp://server{endpointIndex}:49580"), }; model.OpcNodes = new List <OpcNodeModel>(); for (var nodeIndex = 0; nodeIndex < numberOfNodes; ++nodeIndex) { model.OpcNodes.Add(new OpcNodeModel { Id = $"ns=2;s=Node-Server-{nodeIndex}", }); } payload.Add(model); } // Publish all nodes. foreach (var request in payload) { await FluentActions .Invoking(async() => await orchestrator.PublishNodesAsync(request).ConfigureAwait(false)) .Should() .NotThrowAsync() .ConfigureAwait(false); } async Task CheckEndpointsAndNodes( int expectedNumberOfEndpoints, int expectedNumberOfNodes ) { var tasks = new List <Task <JobProcessingInstructionModel> >(); for (var i = 0; i < expectedNumberOfEndpoints + 1; i++) { tasks.Add(orchestrator.GetAvailableJobAsync(i.ToString(), new JobRequestModel())); } await Task.WhenAll(tasks).ConfigureAwait(false); tasks.Count(t => t.Result != null) .Should() .Be(expectedNumberOfEndpoints); var distinctConfigurations = tasks .Where(t => t.Result != null) .Select(t => t.Result.Job.JobConfiguration) .Distinct(); distinctConfigurations.Count() .Should() .Be(expectedNumberOfEndpoints); var writerGroups = tasks .Where(t => t.Result != null) .Select(t => jobSerializer.DeserializeJobConfiguration( t.Result.Job.JobConfiguration, t.Result.Job.JobConfigurationType) as WriterGroupJobModel); writerGroups.Select( jobModel => jobModel.WriterGroup.DataSetWriters .Select(writer => writer.DataSet.DataSetSource.PublishedVariables.PublishedData.Count()) .Sum() ).Count(v => v == expectedNumberOfNodes) .Should() .Be(expectedNumberOfEndpoints); } // Check await CheckEndpointsAndNodes(numberOfEndpoints, numberOfNodes).ConfigureAwait(false); // Publish one more node for each endpoint. var payloadDiff = new List <PublishedNodesEntryModel>(); for (int endpointIndex = 0; endpointIndex < numberOfEndpoints; ++endpointIndex) { var model = new PublishedNodesEntryModel { EndpointUrl = new Uri($"opc.tcp://server{endpointIndex}:49580"), OpcNodes = new List <OpcNodeModel> { new OpcNodeModel { Id = $"ns=2;s=Node-Server-{numberOfNodes}", } } }; payloadDiff.Add(model); } foreach (var request in payloadDiff) { await FluentActions .Invoking(async() => await orchestrator.PublishNodesAsync(request).ConfigureAwait(false)) .Should() .NotThrowAsync() .ConfigureAwait(false); } // Check await CheckEndpointsAndNodes(numberOfEndpoints, numberOfNodes + 1).ConfigureAwait(false); // Unpublish new nodes for each endpoint. foreach (var request in payloadDiff) { await FluentActions .Invoking(async() => await orchestrator.UnpublishNodesAsync(request).ConfigureAwait(false)) .Should() .NotThrowAsync() .ConfigureAwait(false); } // Check await CheckEndpointsAndNodes(numberOfEndpoints, numberOfNodes).ConfigureAwait(false); }
async Task SubscribeUnsubscribeDirectMethodTest(MessagingMode messagingMode, bool useAddOrUpdate) { // When useAddOrUpdate is true, all publishing and unpublishing operations // will be performed through AddOrUpdateEndpoints direct method. var ioTHubEdgeBaseDeployment = new IoTHubEdgeBaseDeployment(_context); var ioTHubPublisherDeployment = new IoTHubPublisherDeployment(_context, messagingMode); _iotHubPublisherModuleName = ioTHubPublisherDeployment.ModuleName; // Clear context. _context.Reset(); // Create base edge deployment. var baseDeploymentResult = await ioTHubEdgeBaseDeployment.CreateOrUpdateLayeredDeploymentAsync(_cts.Token).ConfigureAwait(false); Assert.True(baseDeploymentResult, "Failed to create/update new edge base deployment."); _output.WriteLine("Created/Updated new edge base deployment."); // Create layered edge deployment. var layeredDeploymentResult = await ioTHubPublisherDeployment.CreateOrUpdateLayeredDeploymentAsync(_cts.Token).ConfigureAwait(false); Assert.True(layeredDeploymentResult, "Failed to create/update layered deployment for publisher module."); _output.WriteLine("Created/Updated layered deployment for publisher module."); await TestHelper.SwitchToStandaloneModeAsync(_context, _cts.Token).ConfigureAwait(false); // We will wait for module to be deployed. await _context.RegistryHelper.WaitForSuccessfulDeploymentAsync( ioTHubPublisherDeployment.GetDeploymentConfiguration(), _cts.Token ).ConfigureAwait(false); await _context.RegistryHelper.WaitForIIoTModulesConnectedAsync( _context.DeviceConfig.DeviceId, _cts.Token, new string[] { ioTHubPublisherDeployment.ModuleName } ).ConfigureAwait(false); // We've observed situations when even after the above waits the module did not yet restart. // That leads to situations where the publishing of nodes happens just before the restart to apply // new container creation options. After restart persisted nodes are picked up, but on the telemetry side // the restart causes dropped messages to be detected. That happens because just before the restart OPC Publisher // manages to send some telemetry. This wait makes sure that we do not run the test while restart is happening. await Task.Delay(TestConstants.AwaitInitInMilliseconds, _cts.Token).ConfigureAwait(false); _output.WriteLine("OPC Publisher module is up and running."); // Call GetConfiguredEndpoints direct method, initially there should be no endpoints var responseGetConfiguredEndpoints = await CallMethodAsync( new MethodParameterModel { Name = TestConstants.DirectMethodNames.GetConfiguredEndpoints }, _cts.Token ).ConfigureAwait(false); Assert.Equal((int)HttpStatusCode.OK, responseGetConfiguredEndpoints.Status); var configuredEndpointsResponse = _serializer.Deserialize <GetConfiguredEndpointsResponseApiModel>(responseGetConfiguredEndpoints.JsonPayload); Assert.Equal(configuredEndpointsResponse.Endpoints.Count, 0); var numberOfNodes = 250; var nodesToPublish = await TestHelper .CreateMultipleNodesModelAsync(_context, _cts.Token, numberOfNodes : numberOfNodes) .ConfigureAwait(false); var request = nodesToPublish.ToApiModel(); MethodResultModel response = null; // Publish nodes for the endpoint if (useAddOrUpdate) { // Call AddOrUpdateEndpoints direct method response = await CallMethodAsync( new MethodParameterModel { Name = TestConstants.DirectMethodNames.AddOrUpdateEndpoints, JsonPayload = _serializer.SerializeToString(new List <PublishNodesEndpointApiModel> { request }) }, _cts.Token ).ConfigureAwait(false); } else { // Call PublishNodes direct method response = await CallMethodAsync( new MethodParameterModel { Name = TestConstants.DirectMethodNames.PublishNodes, JsonPayload = _serializer.SerializeToString(request) }, _cts.Token ).ConfigureAwait(false); } Assert.Equal((int)HttpStatusCode.OK, response.Status); // Use test event processor to verify data send to IoT Hub (expected* set to zero // as data gap analysis is not part of this test case) await TestHelper.StartMonitoringIncomingMessagesAsync(_context, numberOfNodes, 0, 90_000_000, _cts.Token).ConfigureAwait(false); // Wait some time to generate events to process. await Task.Delay(TestConstants.AwaitDataInMilliseconds, _cts.Token).ConfigureAwait(false); // Call GetConfiguredEndpoints direct method responseGetConfiguredEndpoints = await CallMethodAsync( new MethodParameterModel { Name = TestConstants.DirectMethodNames.GetConfiguredEndpoints }, _cts.Token ).ConfigureAwait(false); Assert.Equal((int)HttpStatusCode.OK, responseGetConfiguredEndpoints.Status); configuredEndpointsResponse = _serializer.Deserialize <GetConfiguredEndpointsResponseApiModel>(responseGetConfiguredEndpoints.JsonPayload); Assert.Equal(1, configuredEndpointsResponse.Endpoints.Count); TestHelper.Publisher.AssertEndpointModel(configuredEndpointsResponse.Endpoints[0], request); // Create request for GetConfiguredNodesOnEndpoint method call var nodesOnEndpoint = new PublishedNodesEntryModel(); nodesOnEndpoint.EndpointUrl = request.EndpointUrl; var requestGetConfiguredNodesOnEndpoint = nodesOnEndpoint.ToApiModel(); // Call GetConfiguredNodesOnEndpoint direct method var responseGetConfiguredNodesOnEndpoint = await CallMethodAsync( new MethodParameterModel { Name = TestConstants.DirectMethodNames.GetConfiguredNodesOnEndpoint, JsonPayload = _serializer.SerializeToString(requestGetConfiguredNodesOnEndpoint) }, _cts.Token ).ConfigureAwait(false); Assert.Equal((int)HttpStatusCode.OK, responseGetConfiguredNodesOnEndpoint.Status); var jsonResponse = _serializer.Deserialize <GetConfiguredNodesOnEndpointResponseApiModel>(responseGetConfiguredNodesOnEndpoint.JsonPayload); Assert.Equal(numberOfNodes, jsonResponse.OpcNodes.Count); // Call GetDiagnosticInfo direct method var responseGetDiagnosticInfo = await CallMethodAsync( new MethodParameterModel { Name = TestConstants.DirectMethodNames.GetDiagnosticInfo, }, _cts.Token ).ConfigureAwait(false); Assert.Equal((int)HttpStatusCode.OK, responseGetDiagnosticInfo.Status); var diagInfoList = _serializer.Deserialize <List <DiagnosticInfoApiModel> >(responseGetDiagnosticInfo.JsonPayload); Assert.Equal(diagInfoList.Count, 1); TestHelper.Publisher.AssertEndpointDiagnosticInfoModel(request, diagInfoList[0]); // Stop monitoring and get the result. var publishingMonitoringResultJson = await TestHelper.StopMonitoringIncomingMessagesAsync(_context, _cts.Token).ConfigureAwait(false); Assert.True(publishingMonitoringResultJson.TotalValueChangesCount > 0, "No messages received at IoT Hub"); Assert.Equal(publishingMonitoringResultJson.ValueChangesByNodeId.Count, request.OpcNodes.Count); Assert.True(publishingMonitoringResultJson.DroppedValueCount == 0, $"Dropped messages detected: {publishingMonitoringResultJson.DroppedValueCount}"); Assert.True(publishingMonitoringResultJson.DuplicateValueCount == 0, $"Duplicate values detected: {publishingMonitoringResultJson.DuplicateValueCount}"); Assert.Equal(0U, publishingMonitoringResultJson.DroppedSequenceCount); // Uncomment once bug generating duplicate sequence numbers is resolved. //Assert.Equal(0U, publishingMonitoringResultJson.DuplicateSequenceCount); Assert.Equal(0U, publishingMonitoringResultJson.ResetSequenceCount); // Check that every published node is sending data. if (_context.ConsumedOpcUaNodes != null) { var expectedNodes = _context.ConsumedOpcUaNodes.First().Value.OpcNodes.Select(n => n.Id).ToList(); foreach (var property in publishingMonitoringResultJson.ValueChangesByNodeId) { var propertyName = property.Key; var nodeId = propertyName.Split('#').Last(); var expected = expectedNodes.FirstOrDefault(n => n.EndsWith(nodeId)); Assert.True(expected != null, $"Publishing from unexpected node: {propertyName}"); expectedNodes.Remove(expected); } expectedNodes.ForEach(n => _context.OutputHelper.WriteLine(n)); Assert.Empty(expectedNodes); } // Unpublish all nodes for the endpoint if (useAddOrUpdate) { // Call AddOrUpdateEndpoints direct method request.OpcNodes = null; response = await CallMethodAsync( new MethodParameterModel { Name = TestConstants.DirectMethodNames.AddOrUpdateEndpoints, JsonPayload = _serializer.SerializeToString(new List <PublishNodesEndpointApiModel> { request }) }, _cts.Token ).ConfigureAwait(false); } else { // Call UnPublishNodes direct method response = await CallMethodAsync( new MethodParameterModel { Name = TestConstants.DirectMethodNames.UnpublishNodes, JsonPayload = _serializer.SerializeToString(request) }, _cts.Token ).ConfigureAwait(false); } Assert.Equal((int)HttpStatusCode.OK, response.Status); // Wait till the publishing has stopped. await Task.Delay(TestConstants.AwaitCleanupInMilliseconds, _cts.Token).ConfigureAwait(false); // Call GetDiagnosticInfo direct method responseGetDiagnosticInfo = await CallMethodAsync( new MethodParameterModel { Name = TestConstants.DirectMethodNames.GetDiagnosticInfo, }, _cts.Token ).ConfigureAwait(false); Assert.Equal((int)HttpStatusCode.OK, responseGetDiagnosticInfo.Status); diagInfoList = _serializer.Deserialize <List <DiagnosticInfoApiModel> >(responseGetDiagnosticInfo.JsonPayload); Assert.Equal(diagInfoList.Count, 0); // Use test event processor to verify data send to IoT Hub (expected* set to zero // as data gap analysis is not part of this test case) await TestHelper.StartMonitoringIncomingMessagesAsync(_context, 0, 0, 0, _cts.Token).ConfigureAwait(false); // Wait some time to generate events to process. await Task.Delay(TestConstants.AwaitCleanupInMilliseconds, _cts.Token).ConfigureAwait(false); // Stop monitoring and get the result. var unpublishingMonitoringResultJson = await TestHelper.StopMonitoringIncomingMessagesAsync(_context, _cts.Token); Assert.True(unpublishingMonitoringResultJson.TotalValueChangesCount == 0, $"Messages received at IoT Hub: {unpublishingMonitoringResultJson.TotalValueChangesCount}"); }
async Task SubscribeUnsubscribeDirectMethodLegacyPublisherTest() { var ioTHubEdgeBaseDeployment = new IoTHubEdgeBaseDeployment(_context); var ioTHubLegacyPublisherDeployment = new IoTHubLegacyPublisherDeployments(_context); _iotHubPublisherModuleName = ioTHubLegacyPublisherDeployment.ModuleName; // Create base edge deployment. var baseDeploymentResult = await ioTHubEdgeBaseDeployment.CreateOrUpdateLayeredDeploymentAsync(_cts.Token).ConfigureAwait(false); Assert.True(baseDeploymentResult, "Failed to create/update new edge base deployment."); _output.WriteLine("Created/Updated new edge base deployment."); // Create layered edge deployment. var layeredDeploymentResult1 = await ioTHubLegacyPublisherDeployment.CreateOrUpdateLayeredDeploymentAsync(_cts.Token).ConfigureAwait(false); Assert.True(layeredDeploymentResult1, "Failed to create/update layered deployment for legacy publisher module."); _output.WriteLine("Created/Updated layered deployment for legacy publisher module."); await TestHelper.SwitchToStandaloneModeAsync(_context, _cts.Token).ConfigureAwait(false); var nodesToPublish = await TestHelper.CreateMultipleNodesModelAsync(_context, _cts.Token).ConfigureAwait(false); // We will wait for module to be deployed. await _context.RegistryHelper.WaitForSuccessfulDeploymentAsync( ioTHubLegacyPublisherDeployment.GetDeploymentConfiguration(), _cts.Token ).ConfigureAwait(false); await _context.RegistryHelper.WaitForIIoTModulesConnectedAsync( _context.DeviceConfig.DeviceId, _cts.Token, new string[] { ioTHubLegacyPublisherDeployment.ModuleName } ).ConfigureAwait(false); // Call GetConfiguredEndpoints direct method, initially there should be no endpoints var responseGetConfiguredEndpoints = await CallMethodAsync( new MethodParameterModel { Name = TestConstants.DirectMethodLegacyNames.GetConfiguredEndpoints }, _cts.Token ).ConfigureAwait(false); Assert.Equal((int)HttpStatusCode.OK, responseGetConfiguredEndpoints.Status); var configuredEndpointsResponse = _serializer.Deserialize <GetConfiguredEndpointsResponseApiModel>(responseGetConfiguredEndpoints.JsonPayload); Assert.Equal(configuredEndpointsResponse.Endpoints.Count, 0); var request = nodesToPublish.ToApiModel(); // Call Publish direct method var response = await CallMethodAsync( new MethodParameterModel { Name = TestConstants.DirectMethodLegacyNames.PublishNodes, JsonPayload = _serializer.SerializeToString(request) }, _cts.Token ).ConfigureAwait(false); Assert.Equal((int)HttpStatusCode.OK, response.Status); // Use test event processor to verify data send to IoT Hub (expected* set to zero // as data gap analysis is not part of this test case) await TestHelper.StartMonitoringIncomingMessagesAsync(_context, 250, 10_000, 90_000_000, _cts.Token).ConfigureAwait(false); // Wait some time to generate events to process. await Task.Delay(TestConstants.AwaitDataInMilliseconds, _cts.Token).ConfigureAwait(false); // Call GetConfiguredEndpoints direct method responseGetConfiguredEndpoints = await CallMethodAsync( new MethodParameterModel { Name = TestConstants.DirectMethodLegacyNames.GetConfiguredEndpoints }, _cts.Token ).ConfigureAwait(false); Assert.Equal((int)HttpStatusCode.OK, responseGetConfiguredEndpoints.Status); configuredEndpointsResponse = _serializer.Deserialize <GetConfiguredEndpointsResponseApiModel>(responseGetConfiguredEndpoints.JsonPayload); Assert.Equal(1, configuredEndpointsResponse.Endpoints.Count); TestHelper.Publisher.AssertEndpointModel(configuredEndpointsResponse.Endpoints[0], request); // Create request for GetConfiguredNodesOnEndpoint method call var nodesOnEndpoint = new PublishedNodesEntryModel(); nodesOnEndpoint.EndpointUrl = request.EndpointUrl; var requestGetConfiguredNodesOnEndpoint = nodesOnEndpoint.ToApiModel(); // Call GetConfiguredNodesOnEndpoint direct method var responseGetConfiguredNodesOnEndpoint = await CallMethodAsync( new MethodParameterModel { Name = TestConstants.DirectMethodLegacyNames.GetConfiguredNodesOnEndpoint, JsonPayload = _serializer.SerializeToString(requestGetConfiguredNodesOnEndpoint) }, _cts.Token ).ConfigureAwait(false); Assert.Equal((int)HttpStatusCode.OK, responseGetConfiguredNodesOnEndpoint.Status); var jsonResponse = _serializer.Deserialize <GetConfiguredNodesOnEndpointResponseApiModel>( responseGetConfiguredNodesOnEndpoint.JsonPayload); Assert.Equal(jsonResponse.OpcNodes.Count, 250); // Call GetDiagnosticInfo direct method var responseGetDiagnosticInfo = await CallMethodAsync( new MethodParameterModel { Name = TestConstants.DirectMethodLegacyNames.GetDiagnosticInfo, }, _cts.Token ).ConfigureAwait(false); Assert.Equal((int)HttpStatusCode.OK, responseGetDiagnosticInfo.Status); var diagInfo = _serializer.Deserialize <DiagnosticInfoLegacyModel>(responseGetDiagnosticInfo.JsonPayload); Assert.Equal(diagInfo.NumberOfOpcSessionsConfigured, 1); Assert.Equal(diagInfo.NumberOfOpcSessionsConfigured, 1); Assert.Equal(diagInfo.NumberOfOpcSessionsConnected, 1); Assert.Equal(diagInfo.NumberOfOpcMonitoredItemsConfigured, 250); Assert.Equal(diagInfo.NumberOfOpcMonitoredItemsMonitored, 250); Assert.True(diagInfo.SentMessages > 0); Assert.Equal(diagInfo.FailedMessages, 0); Assert.Equal(diagInfo.TooLargeCount, 0); // Stop monitoring and get the result. var publishingMonitoringResultJson = await TestHelper.StopMonitoringIncomingMessagesAsync(_context, _cts.Token).ConfigureAwait(false); Assert.True(publishingMonitoringResultJson.TotalValueChangesCount > 0, "No messages received at IoT Hub"); Assert.Equal(publishingMonitoringResultJson.ValueChangesByNodeId.Count, request.OpcNodes.Count); Assert.True(publishingMonitoringResultJson.DroppedValueCount == 0, $"Dropped messages detected: {publishingMonitoringResultJson.DroppedValueCount}"); Assert.True(publishingMonitoringResultJson.DuplicateValueCount == 0, $"Duplicate values detected: {publishingMonitoringResultJson.DuplicateValueCount}"); Assert.Equal(0U, publishingMonitoringResultJson.DroppedSequenceCount); // Uncomment once bug generating duplicate sequence numbers is resolved. //Assert.Equal(0U, publishingMonitoringResultJson.DuplicateSequenceCount); Assert.Equal(0U, publishingMonitoringResultJson.ResetSequenceCount); // Check that every published node is sending data. if (_context.ConsumedOpcUaNodes != null) { var expectedNodes = _context.ConsumedOpcUaNodes.First().Value.OpcNodes.Select(n => n.Id).ToList(); foreach (var property in publishingMonitoringResultJson.ValueChangesByNodeId) { var propertyName = property.Key; var nodeId = propertyName.Split('#').Last(); var expected = expectedNodes.FirstOrDefault(n => n.EndsWith(nodeId)); Assert.True(expected != null, $"Publishing from unexpected node: {propertyName}"); expectedNodes.Remove(expected); } expectedNodes.ForEach(n => _context.OutputHelper.WriteLine(n)); Assert.Empty(expectedNodes); } // Call Unpublish direct method response = await CallMethodAsync( new MethodParameterModel { Name = TestConstants.DirectMethodLegacyNames.UnpublishNodes, JsonPayload = _serializer.SerializeToString(request) }, _cts.Token ).ConfigureAwait(false); Assert.Equal((int)HttpStatusCode.OK, response.Status); // Wait till the publishing has stopped. await Task.Delay(TestConstants.AwaitCleanupInMilliseconds, _cts.Token).ConfigureAwait(false); // Call GetDiagnosticInfo direct method responseGetDiagnosticInfo = await CallMethodAsync( new MethodParameterModel { Name = TestConstants.DirectMethodLegacyNames.GetDiagnosticInfo, }, _cts.Token ).ConfigureAwait(false); Assert.Equal((int)HttpStatusCode.OK, responseGetDiagnosticInfo.Status); diagInfo = _serializer.Deserialize <DiagnosticInfoLegacyModel>(responseGetDiagnosticInfo.JsonPayload); Assert.Equal(diagInfo.NumberOfOpcSessionsConfigured, 0); // Use test event processor to verify data send to IoT Hub (expected* set to zero // as data gap analysis is not part of this test case) await TestHelper.StartMonitoringIncomingMessagesAsync(_context, 0, 0, 0, _cts.Token).ConfigureAwait(false); // Wait some time to generate events to process. await Task.Delay(TestConstants.AwaitCleanupInMilliseconds, _cts.Token).ConfigureAwait(false); // Stop monitoring and get the result. var unpublishingMonitoringResultJson = await TestHelper.StopMonitoringIncomingMessagesAsync(_context, _cts.Token); Assert.True(unpublishingMonitoringResultJson.TotalValueChangesCount == 0, $"Messages received at IoT Hub: {unpublishingMonitoringResultJson.TotalValueChangesCount}"); }