Exemple #1
0
        /// <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 })
            });
        }
Exemple #2
0
        /// <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
     });
 }
Exemple #6
0
        /// <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
        }
Exemple #8
0
 /// <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,
         });
     }
 }
Exemple #10
0
        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);
        }
Exemple #11
0
        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);
        }
Exemple #12
0
 /// <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);
        }
Exemple #15
0
        /// <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,
            });
        }
Exemple #16
0
        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}");
        }
Exemple #17
0
        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}");
        }