/// <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); }
/// <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); }
/// <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); }
/// <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); } }
/// <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); }
/// <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."); } }
/// <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."); } } }
/// <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)); }
/// <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); }
/// <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); }
/// <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); }
/// <summary> /// Create client /// </summary> private static IoTHubServiceHttpClient CreateClient(IIoTHubConfig config, ILogger logger) { var registry = new IoTHubServiceHttpClient(new HttpClient(logger), config, logger); return(registry); }
/// <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); }
/// <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(); }
/// <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")); } }
/// <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."); }
/// <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(); }
/// <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."); }
/// <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}"); } }
/// <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}"); } }
/// <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); }
/// <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."); }
/// <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; }
/// <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); }
/// <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); } } }
/// <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"); }
/// <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"); } } }
/// <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) { } }