Exemplo n.º 1
0
        /// <summary>
        /// Add or get module identity
        /// </summary>
        private static async Task <ConnectionString> AddOrGetAsync(IIoTHubConfig config,
                                                                   string deviceId, string moduleId)
        {
            var logger   = ConsoleLogger.Create(LogEventLevel.Error);
            var registry = new IoTHubServiceHttpClient(new HttpClient(logger),
                                                       config, logger);
            await registry.CreateAsync(new DeviceTwinModel {
                Id   = deviceId,
                Tags = new Dictionary <string, JToken> {
                    [TwinProperty.Type] = IdentityType.Gateway
                },
                Capabilities = new DeviceCapabilitiesModel {
                    IotEdge = true
                }
            }, true, CancellationToken.None);

            await registry.CreateAsync(new DeviceTwinModel {
                Id       = deviceId,
                ModuleId = moduleId
            }, true, CancellationToken.None);

            var cs = await registry.GetConnectionStringAsync(deviceId, moduleId);

            return(cs);
        }
Exemplo n.º 2
0
        /// <summary>
        /// Add supervisor
        /// </summary>
        private static async Task AddAsync(IIoTHubConfig config,
                                           string deviceId, string moduleId)
        {
            var cs = await AddOrGetAsync(config, deviceId, moduleId);

            Console.WriteLine(cs);
        }
Exemplo n.º 3
0
        /// <summary>
        /// Add or get supervisor identity
        /// </summary>
        private static async Task <ConnectionString> AddOrGetAsync(IIoTHubConfig config,
                                                                   string deviceId, string moduleId, ILogger logger)
        {
            var level = LogControl.Level.MinimumLevel;

            LogControl.Level.MinimumLevel = LogEventLevel.Error;
            var registry = CreateClient(config, logger);
            await registry.CreateAsync(new DeviceTwinModel {
                Id   = deviceId,
                Tags = new Dictionary <string, JToken> {
                    [TwinProperty.Type] = IdentityType.Gateway
                },
                Capabilities = new DeviceCapabilitiesModel {
                    IotEdge = true
                }
            }, true, CancellationToken.None);

            await registry.CreateAsync(new DeviceTwinModel {
                Id       = deviceId,
                ModuleId = moduleId
            }, true, CancellationToken.None);

            var cs = await registry.GetConnectionStringAsync(deviceId, moduleId);

            LogControl.Level.MinimumLevel = level;
            return(cs);
        }
Exemplo n.º 4
0
        /// <summary>
        /// Clear registry
        /// </summary>
        private static async Task CleanupAsync(IIoTHubConfig config,
                                               bool includeSupervisors)
        {
            var logger   = ConsoleLogger.Create(LogEventLevel.Error);
            var registry = new IoTHubServiceHttpClient(new HttpClient(logger),
                                                       config, new NewtonSoftJsonSerializer(), logger);
            var result = await registry.QueryAllDeviceTwinsAsync(
                "SELECT * from devices where IS_DEFINED(tags.DeviceType)");

            foreach (var item in result)
            {
                Console.WriteLine($"Deleting {item.Id} {item.ModuleId ?? ""}");
                await registry.DeleteAsync(item.Id, item.ModuleId, null,
                                           CancellationToken.None);
            }
            if (!includeSupervisors)
            {
                return;
            }
            var query = "SELECT * FROM devices.modules WHERE " +
                        $"properties.reported.{TwinProperty.Type} = '{IdentityType.Supervisor}'";
            var supers = await registry.QueryAllDeviceTwinsAsync(query);

            foreach (var item in supers)
            {
                Console.WriteLine($"Deleting {item.Id} {item.ModuleId ?? ""}");
                await registry.DeleteAsync(item.Id, item.ModuleId, null,
                                           CancellationToken.None);
            }
        }
        /// <summary>
        /// Clear registry
        /// </summary>
        private static async Task CleanupAsync(IIoTHubConfig config,
                                               bool includeSupervisors)
        {
            var logger   = LogEx.Console(LogEventLevel.Error);
            var registry = new IoTHubServiceHttpClient(new HttpClient(logger),
                                                       config, logger);
            var result = await registry.QueryDeviceTwinsAsync(
                "SELECT * from devices where IS_DEFINED(tags.DeviceType)");

            foreach (var item in result)
            {
                Console.WriteLine($"Deleting {item.Id} {item.ModuleId ?? ""}");
                await registry.DeleteAsync(item.Id, item.ModuleId);
            }
            if (!includeSupervisors)
            {
                return;
            }
            var query = "SELECT * FROM devices.modules WHERE " +
                        $"properties.reported.{TwinProperty.kType} = 'supervisor'";
            var supers = await registry.QueryDeviceTwinsAsync(query);

            foreach (var item in supers)
            {
                Console.WriteLine($"Deleting {item.Id} {item.ModuleId ?? ""}");
                await registry.DeleteAsync(item.Id, item.ModuleId);
            }
        }
Exemplo n.º 6
0
        /// <summary>
        /// Add or get module identity
        /// </summary>
        private static async Task <ConnectionString> AddOrGetAsync(IIoTHubConfig config,
                                                                   string deviceId, string moduleId)
        {
            var logger   = ConsoleLogger.Create(LogEventLevel.Error);
            var registry = new IoTHubServiceHttpClient(new HttpClient(logger),
                                                       config, new NewtonSoftJsonSerializer(), logger);

            try {
                await registry.CreateAsync(new DeviceTwinModel {
                    Id   = deviceId,
                    Tags = new Dictionary <string, VariantValue> {
                        [TwinProperty.Type] = IdentityType.Gateway
                    },
                    Capabilities = new DeviceCapabilitiesModel {
                        IotEdge = true
                    }
                }, false, CancellationToken.None);
            }
            catch (ConflictingResourceException) {
                logger.Information("Gateway {deviceId} exists.", deviceId);
            }
            try {
                await registry.CreateAsync(new DeviceTwinModel {
                    Id       = deviceId,
                    ModuleId = moduleId
                }, false, CancellationToken.None);
            }
            catch (ConflictingResourceException) {
                logger.Information("Module {moduleId} exists...", moduleId);
            }
            var cs = await registry.GetConnectionStringAsync(deviceId, moduleId);

            return(cs);
        }
Exemplo n.º 7
0
        /// <summary>
        /// Host the module giving it its connection string.
        /// </summary>
        private static async Task HostAsync(IIoTHubConfig config, ILogger logger,
                                            string deviceId, string moduleId, string[] args, bool verbose = false,
                                            bool acceptAll = false)
        {
            Console.WriteLine("Create or retrieve connection string...");

            var cs = await Retry.WithExponentialBackoff(logger,
                                                        () => AddOrGetAsync(config, deviceId, moduleId));

            // Hook event source
            using (var broker = new EventSourceBroker()) {
                LogControl.Level.MinimumLevel = verbose ?
                                                LogEventLevel.Verbose : LogEventLevel.Information;

                Console.WriteLine("Starting publisher module...");
                broker.Subscribe(IoTSdkLogger.EventSource, new IoTSdkLogger(logger));
                var arguments = args.ToList();
                arguments.Add($"--ec={cs}");
                arguments.Add($"--si=0");
                arguments.Add($"--ms=0");
                if (acceptAll)
                {
                    arguments.Add("--aa");
                }
                if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
                {
                    arguments.Add("--at=X509Store");
                }
                Publisher.Program.Main(arguments.ToArray());
                Console.WriteLine("Publisher module exited.");
            }
        }
Exemplo n.º 8
0
        /// <summary>
        /// List nodes on endpoint
        /// </summary>
        private static async Task ListNodesAsync(IIoTHubConfig config, ILogger logger,
                                                 string deviceId, string moduleId, string endpointUrl, CancellationToken ct)
        {
            if (string.IsNullOrEmpty(endpointUrl))
            {
                throw new ArgumentNullException(nameof(endpointUrl));
            }
            var client = new IoTHubTwinMethodClient(CreateClient(config, logger), logger);

            while (!ct.IsCancellationRequested)
            {
                await Task.Delay(TimeSpan.FromSeconds(5), ct);

                try {
                    var content = new GetNodesRequestModel {
                        EndpointUrl = endpointUrl
                    };
                    var result = await client.CallMethodAsync(deviceId, moduleId,
                                                              "GetConfiguredNodesOnEndpoint", JsonConvertEx.SerializeObject(content),
                                                              null, ct);

                    var response = JsonConvertEx.DeserializeObject <GetNodesResponseModel>(result);
                    logger.Information("Published nodes: {@response}", response);
                }
                catch (Exception ex) {
                    logger.Verbose(ex, "Failed to list published nodes.");
                }
            }
        }
Exemplo n.º 9
0
 /// <summary>
 /// Create service client
 /// </summary>
 /// <param name="httpClient"></param>
 /// <param name="config"></param>
 /// <param name="logger"></param>
 protected IoTHubHttpClientBase(IHttpClient httpClient,
                                IIoTHubConfig config, ILogger logger)
 {
     _httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
     _logger     = logger ?? throw new ArgumentNullException(nameof(logger));
     _config     = config ?? throw new ArgumentNullException(nameof(config));
 }
Exemplo n.º 10
0
        /// <summary>
        /// Create client
        /// </summary>
        private static IoTHubServiceHttpClient CreateClient(IIoTHubConfig config,
                                                            ILogger logger)
        {
            var registry = new IoTHubServiceHttpClient(new HttpClient(logger),
                                                       config, new NewtonSoftJsonSerializer(), logger);

            return(registry);
        }
Exemplo n.º 11
0
 /// <summary>
 /// Delete supervisor
 /// </summary>
 private static async Task DeleteAsync(IIoTHubConfig config,
                                       string deviceId, string moduleId)
 {
     var logger   = ConsoleLogger.Create(LogEventLevel.Error);
     var registry = new IoTHubServiceHttpClient(new HttpClient(logger),
                                                config, new NewtonSoftJsonSerializer(), logger);
     await registry.DeleteAsync(deviceId, moduleId, null, CancellationToken.None);
 }
Exemplo n.º 12
0
 /// <summary>
 /// Delete supervisor
 /// </summary>
 private static async Task DeleteAsync(IIoTHubConfig config,
                                       string deviceId, string moduleId)
 {
     var logger   = LogEx.Console(LogEventLevel.Error);
     var registry = new IoTHubServiceHttpClient(new HttpClient(logger),
                                                config, logger);
     await registry.DeleteAsync(deviceId, moduleId);
 }
Exemplo n.º 13
0
        /// <summary>
        /// Create client
        /// </summary>
        private static IoTHubServiceHttpClient CreateClient(IIoTHubConfig config,
                                                            ILogger logger)
        {
            var registry = new IoTHubServiceHttpClient(new HttpClient(logger),
                                                       config, logger);

            return(registry);
        }
Exemplo n.º 14
0
        /// <summary>
        /// Get module connection string
        /// </summary>
        private static async Task GetAsync(
            IIoTHubConfig config, string deviceId, string moduleId)
        {
            var logger   = ConsoleLogger.Create(LogEventLevel.Error);
            var registry = new IoTHubServiceHttpClient(new HttpClient(logger),
                                                       config, new NewtonSoftJsonSerializer(), logger);
            var cs = await registry.GetConnectionStringAsync(deviceId, moduleId);

            Console.WriteLine(cs);
        }
Exemplo n.º 15
0
        /// <summary>
        /// Reset supervisor
        /// </summary>
        private static async Task ResetAsync(IIoTHubConfig config,
                                             string deviceId, string moduleId)
        {
            var logger   = LogEx.Console(LogEventLevel.Error);
            var registry = new IoTHubServiceHttpClient(new HttpClient(logger),
                                                       config, logger);

            await ResetAsync(registry, await registry.GetAsync(deviceId, moduleId,
                                                               CancellationToken.None));
        }
        /// <summary>
        /// Create service client
        /// </summary>
        /// <param name="config"></param>
        /// <param name="logger"></param>
        public IoTHubConfigurationClient(IIoTHubConfig config, ILogger logger)
        {
            if (string.IsNullOrEmpty(config?.IoTHubConnString))
            {
                throw new ArgumentNullException(nameof(config.IoTHubConnString));
            }

            _logger   = logger ?? throw new ArgumentNullException(nameof(logger));
            _registry = RegistryManager.CreateFromConnectionString(config.IoTHubConnString);
            _registry.OpenAsync().Wait();
        }
Exemplo n.º 17
0
        /// <summary>
        /// setup publishing from sample server
        /// </summary>
        private static async Task PublishAsync(IIoTHubConfig config, ILogger logger,
                                               string deviceId, string moduleId, bool listNodes, string[] args)
        {
            try {
                using (var cts = new CancellationTokenSource())
                    using (var server = new ServerWrapper(logger)) { // Start test server
                        // Start publisher module
                        var host = Task.Run(() => HostAsync(config, logger, deviceId,
                                                            moduleId, args, true), cts.Token);

                        // Wait a bit
                        await Task.Delay(TimeSpan.FromSeconds(5), cts.Token);

                        // Nodes to publish
                        var nodes = new string[] {
                            "i=2258", // Server time
                            "ns=12;s=0:Boiler #1?Drum/Level/Measurement"
                            // ...
                        };

                        foreach (var node in nodes)
                        {
                            await PublishNodesAsync(config, logger, deviceId, moduleId,
                                                    server.EndpointUrl, node, true, cts.Token);
                        }

                        var lister = Task.CompletedTask;
                        if (listNodes)
                        {
                            lister = Task.Run(() => ListNodesAsync(config, logger,
                                                                   deviceId, moduleId, server.EndpointUrl, cts.Token), cts.Token);
                        }

                        Console.WriteLine("Press key to cancel...");
                        Console.ReadKey();

                        foreach (var node in nodes)
                        {
                            await PublishNodesAsync(config, logger, deviceId, moduleId,
                                                    server.EndpointUrl, node, false, CancellationToken.None);
                        }

                        logger.Information("Server exiting - tear down publisher...");
                        cts.Cancel();

                        await lister;
                        await host;
                    }
            }
            catch (OperationCanceledException) { }
            finally {
                Try.Op(() => File.Delete("publishednodes.json"));
            }
        }
Exemplo n.º 18
0
        /// <summary>
        /// Host the supervisor module giving it its connection string.
        /// </summary>
        private static async Task HostAsync(IIoTHubConfig config,
                                            string deviceId, string moduleId)
        {
            Console.WriteLine("Create or retrieve connection string...");
            var logger = LogEx.Console(LogEventLevel.Error);
            var cs     = await Retry.WithExponentialBackoff(logger,
                                                            () => AddOrGetAsync(config, deviceId, moduleId));

            Console.WriteLine("Starting twin module...");
            Twin.Program.Main(new string[] { $"EdgeHubConnectionString={cs}" });
            Console.WriteLine("Twin module exited.");
        }
Exemplo n.º 19
0
        /// <summary>
        /// Create service client
        /// </summary>
        /// <param name="config"></param>
        /// <param name="handlers"></param>
        /// <param name="logger"></param>
        public IoTHubFileNotificationHost(IIoTHubConfig config,
                                          IEnumerable <IBlobUploadHandler> handlers, ILogger logger)
        {
            if (string.IsNullOrEmpty(config.IoTHubConnString))
            {
                throw new ArgumentException(nameof(config));
            }

            _handlers = handlers ?? throw new ArgumentNullException(nameof(handlers));
            _logger   = logger ?? throw new ArgumentNullException(nameof(logger));
            _client   = ServiceClient.CreateFromConnectionString(config.IoTHubConnString);
            _cts      = new CancellationTokenSource();
        }
Exemplo n.º 20
0
        /// <summary>
        /// Host the module giving it its connection string.
        /// </summary>
        private static async Task HostAsync(IIoTHubConfig config,
                                            string deviceId, string moduleId, string[] args)
        {
            Console.WriteLine("Create or retrieve connection string...");
            var logger = LogEx.Console(LogEventLevel.Error);
            var cs     = await Retry.WithExponentialBackoff(logger,
                                                            () => AddOrGetAsync(config, deviceId, moduleId));

            Console.WriteLine("Starting discovery module...");
            var arguments = args.ToList();

            arguments.Add($"EdgeHubConnectionString={cs}");
            Discovery.Program.Main(arguments.ToArray());
            Console.WriteLine("Discovery module exited.");
        }
Exemplo n.º 21
0
        /// <summary>
        /// List all twin module identities
        /// </summary>
        private static async Task ListAsync(IIoTHubConfig config)
        {
            var logger   = ConsoleLogger.Create(LogEventLevel.Error);
            var registry = new IoTHubServiceHttpClient(new HttpClient(logger),
                                                       config, new NewtonSoftJsonSerializer(), logger);

            var query = "SELECT * FROM devices.modules WHERE " +
                        $"properties.reported.{TwinProperty.Type} = '{IdentityType.Supervisor}'";
            var supers = await registry.QueryAllDeviceTwinsAsync(query);

            foreach (var item in supers)
            {
                Console.WriteLine($"{item.Id} {item.ModuleId}");
            }
        }
Exemplo n.º 22
0
        /// <summary>
        /// List all twin module identities
        /// </summary>
        private static async Task ListAsync(IIoTHubConfig config)
        {
            var logger   = LogEx.Console(LogEventLevel.Error);
            var registry = new IoTHubServiceHttpClient(new HttpClient(logger),
                                                       config, logger);

            var query = "SELECT * FROM devices.modules WHERE " +
                        $"properties.reported.{TwinProperty.kType} = 'supervisor'";
            var supers = await registry.QueryDeviceTwinsAsync(query);

            foreach (var item in supers)
            {
                Console.WriteLine($"{item.Id} {item.ModuleId}");
            }
        }
Exemplo n.º 23
0
 /// <summary>
 /// Create service client
 /// </summary>
 /// <param name="httpClient"></param>
 /// <param name="config"></param>
 /// <param name="logger"></param>
 protected IoTHubHttpClientBase(IHttpClient httpClient,
                                IIoTHubConfig config, ILogger logger)
 {
     _httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
     _logger     = logger ?? throw new ArgumentNullException(nameof(logger));
     if (config == null)
     {
         throw new ArgumentNullException(nameof(config));
     }
     if (string.IsNullOrEmpty(config.IoTHubConnString))
     {
         throw new ArgumentException(nameof(config.IoTHubConnString));
     }
     _resourceId          = config.IoTHubResourceId;
     _hubConnectionString = ConnectionString.Parse(config.IoTHubConnString);
 }
Exemplo n.º 24
0
        /// <summary>
        /// Host the diagnostic module giving it its connection string.
        /// </summary>
        private static async Task HostAsync(IIoTHubConfig config, ILogger logger,
                                            string deviceId, string moduleId, string[] args)
        {
            logger.Information("Create or retrieve connection string...");

            var cs = await Retry.WithExponentialBackoff(logger,
                                                        () => AddOrGetAsync(config, deviceId, moduleId, logger));

            logger.Information("Starting diagnostic module...");
            var arguments = args.ToList();

            arguments.Add($"EdgeHubConnectionString={cs}");
            arguments.Add($"LogLevel={LogControl.Level.MinimumLevel}");
            Diagnostic.Program.Main(arguments.ToArray());
            logger.Information("Diagnostic module exited.");
        }
Exemplo n.º 25
0
        /// <summary>
        /// Create service client
        /// </summary>
        /// <param name="config"></param>
        /// <param name="logger"></param>
        public IoTHubServiceClient(IIoTHubConfig config, ILogger logger)
        {
            if (string.IsNullOrEmpty(config?.IoTHubConnString))
            {
                throw new ArgumentNullException(nameof(config.IoTHubConnString));
            }

            _logger = logger ?? throw new ArgumentNullException(nameof(logger));

            _client   = ServiceClient.CreateFromConnectionString(config.IoTHubConnString);
            _registry = RegistryManager.CreateFromConnectionString(config.IoTHubConnString);
            _jobs     = JobClient.CreateFromConnectionString(config.IoTHubConnString);

            Task.WaitAll(_client.OpenAsync(), _registry.OpenAsync(), _jobs.OpenAsync());

            HostName = ConnectionString.Parse(config.IoTHubConnString).HostName;
        }
Exemplo n.º 26
0
        /// <summary>
        /// Add or get module identity
        /// </summary>
        private static async Task <ConnectionString> AddOrGetAsync(IIoTHubConfig config,
                                                                   string deviceId, string moduleId)
        {
            var logger   = LogEx.Console(LogEventLevel.Error);
            var registry = CreateClient(config, logger);
            await registry.CreateAsync(new DeviceTwinModel {
                Id           = deviceId,
                ModuleId     = moduleId,
                Capabilities = new DeviceCapabilitiesModel {
                    IotEdge = true
                }
            }, true, CancellationToken.None);

            var cs = await registry.GetConnectionStringAsync(deviceId, moduleId);

            return(cs);
        }
Exemplo n.º 27
0
        /// <summary>
        /// Configure publishing of a particular node
        /// </summary>
        private static async Task PublishNodesAsync(IIoTHubConfig config, ILogger logger,
                                                    string deviceId, string moduleId, string endpointUrl, string nodeId,
                                                    bool publish, CancellationToken ct)
        {
            if (string.IsNullOrEmpty(endpointUrl))
            {
                throw new ArgumentNullException(nameof(endpointUrl));
            }
            if (string.IsNullOrEmpty(nodeId))
            {
                throw new ArgumentNullException(nameof(nodeId));
            }
            var client = new IoTHubTwinMethodClient(CreateClient(config, logger), logger);

            while (!ct.IsCancellationRequested)
            {
                try {
                    logger.Information("Start publishing {nodeId}...", nodeId);
                    var content = new PublishNodesRequestModel {
                        EndpointUrl = endpointUrl,
                        UseSecurity = true,
                        OpcNodes    = new List <PublisherNodeModel> {
                            new PublisherNodeModel {
                                Id = nodeId,
                                OpcPublishingInterval = 1000,
                                OpcSamplingInterval   = 1000
                            }
                        }
                    };
                    var result = await client.CallMethodAsync(deviceId, moduleId,
                                                              publish? "PublishNodes" : "UnpublishNodes",
                                                              JsonConvertEx.SerializeObject(content), null, ct);

                    logger.Information("... started");
                    break;
                }
                catch (Exception ex) {
                    logger.Verbose(ex, "Failed to configure publishing.");
                    // Wait a bit
                    await Task.Delay(TimeSpan.FromSeconds(2), ct);
                }
            }
        }
Exemplo n.º 28
0
        /// <summary>
        /// Ping continously
        /// </summary>
        private static async Task PingAsync(IIoTHubConfig config, ILogger logger, string deviceId,
                                            string moduleId, CancellationToken ct)
        {
            var serializer = new NewtonSoftJsonSerializer();
            var client     = new IoTHubTwinMethodClient(CreateClient(config, logger), logger);

            logger.Information("Starting echo thread");
            var found = false;

            for (var index = 0; !ct.IsCancellationRequested; index++)
            {
                try {
                    var message = serializer.SerializePretty(new {
                        Index   = index,
                        Started = DateTime.UtcNow
                    });
                    logger.Debug("Sending ECHO {Index}... ", index);
                    var result = await client.CallMethodAsync(deviceId, moduleId,
                                                              "Echo_V1", message, null, ct);

                    found = true;
                    try {
                        var returned = serializer.Parse(result);
                        logger.Debug("... received back ECHO {Index} - took {Passed}.",
                                     returned["Index"], DateTime.UtcNow - ((DateTime)returned["Started"]));
                    }
                    catch (Exception e) {
                        logger.Error(e, "Bad result for ECHO {Index}: {result} ",
                                     index, result);
                    }
                }
                catch (Exception ex) {
                    if (!found && (ex is ResourceNotFoundException))
                    {
                        logger.Debug("Waiting for module to connect...");
                        continue; // Initial startup ...
                    }
                    logger.Information(ex, "Failed to send ECHO {Index}.", index);
                }
            }
            logger.Information("Echo thread completed");
        }
Exemplo n.º 29
0
        /// <summary>
        /// Stop publishing
        /// </summary>
        private static async Task StopPublishAsync(IIoTHubConfig config, ILogger logger,
                                                   string deviceId, string moduleId, CancellationToken ct)
        {
            var client = new IoTHubTwinMethodClient(CreateClient(config, logger), logger);

            while (!ct.IsCancellationRequested)
            {
                try {
                    logger.Information("Stop publishing...");
                    var result = await client.CallMethodAsync(deviceId, moduleId,
                                                              "StopPublish_V1", "", null, ct);

                    logger.Information("... stopped");
                    break;
                }
                catch (Exception ex) {
                    logger.Verbose(ex, "Failed to stop publishing");
                }
            }
        }
Exemplo n.º 30
0
        /// <summary>
        /// setup publishing from sample server
        /// </summary>
        private static async Task WithServerAsync(IIoTHubConfig config, ILogger logger,
                                                  string deviceId, string moduleId, string[] args, bool verbose = false)
        {
            try {
                using (var cts = new CancellationTokenSource())
                    using (var server = new ServerWrapper(logger)) { // Start test server
                        // Start publisher module
                        var host = Task.Run(() => HostAsync(config, logger, deviceId,
                                                            moduleId, args, verbose, true), cts.Token);

                        Console.WriteLine("Press key to cancel...");
                        Console.ReadKey();

                        logger.Information("Server exiting - tear down publisher...");
                        cts.Cancel();

                        await host;
                    }
            }
            catch (OperationCanceledException) { }
        }