{"id":22189965,"url":"https://github.com/fgheysels/fg.iotedgemodule","last_synced_at":"2025-07-26T22:30:38.128Z","repository":{"id":47238610,"uuid":"319104784","full_name":"fgheysels/Fg.IoTEdgeModule","owner":"fgheysels","description":"Contains utilities and helpers to easier create better Azure IoT Edge modules.","archived":false,"fork":false,"pushed_at":"2023-02-21T16:36:59.000Z","size":50,"stargazers_count":3,"open_issues_count":1,"forks_count":1,"subscribers_count":2,"default_branch":"main","last_synced_at":"2024-12-02T03:12:22.669Z","etag":null,"topics":["azure-iot-edge","iot","iot-edge"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/fgheysels.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-12-06T18:39:57.000Z","updated_at":"2021-11-18T16:53:37.000Z","dependencies_parsed_at":"2023-01-30T19:30:27.671Z","dependency_job_id":null,"html_url":"https://github.com/fgheysels/Fg.IoTEdgeModule","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fgheysels%2FFg.IoTEdgeModule","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fgheysels%2FFg.IoTEdgeModule/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fgheysels%2FFg.IoTEdgeModule/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fgheysels%2FFg.IoTEdgeModule/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fgheysels","download_url":"https://codeload.github.com/fgheysels/Fg.IoTEdgeModule/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":227722115,"owners_count":17809871,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["azure-iot-edge","iot","iot-edge"],"created_at":"2024-12-02T11:40:53.275Z","updated_at":"2024-12-02T11:40:53.901Z","avatar_url":"https://github.com/fgheysels.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Fg.IoTEdgeModule\n\n[![Build Status](https://frederikgheysels.visualstudio.com/GitHub%20Pipelines/_apis/build/status/IoTEdgeModule/Fg.IoTEdgeModule%20CI?branchName=main)](https://frederikgheysels.visualstudio.com/GitHub%20Pipelines/_build/latest?definitionId=9\u0026branchName=main)\n[![NuGet Badge](https://buildstats.info/nuget/fg.iotedgemodule?includePreReleases=true)](https://www.nuget.org/packages/Fg.IoTEdgeModule)\n\n## Introduction\n\nThis project provides some helpful functionality to easily create better modules for Azure IoT Edge.\n\n## Installation\n\n```\nPM \u003e Install-Package Fg.IoTEdgeModule\n```\n\n## Creating IoT Edge modules as a hosted service\n\nWhen creating a new Azure IoT Edge module in Visual Studio, the VS.NET template generates a straightforward console application.  If you want to make use of dependency injection, easy integration of `ILogger`, and have a better distinction between infrastructure and the application functionality itself, it's better to setup the module as a hosted module.\n\nThe `CreateModuleClient` extension method on `IHostBuilder` allows to easily create, configure and register the `ModuleClient` as a singleton service in the host.\nUsing this method allows you to easily setup your IoT Edge module as a hosted service:\n\n```csharp\nstatic async Task Main(string[] args)\n{\n    var host = Host.CreateDefaultBuilder(args)\n                    .ConfigureIoTEdgeModuleClient(TransportType.Mqtt_Tcp_Only, configureModuleClient =\u003e\n                    {\n                        configureModuleClient.OpenAsync().Wait();\n                        configureModuleClient.SetDesiredPropertyUpdateCallbackAsync(OnDesiredPropertiesChanged, configureModuleClient).Wait();\n                    })\n                    .ConfigureLogging(logging =\u003e logging.AddConsole(consoleLogging =\u003e\n                    {\n                        consoleLogging.Format = ConsoleLoggerFormat.Systemd;\n                        consoleLogging.TimestampFormat = \"dd/MM/yyyy HH:mm:ss zz\";\n                    }))\n                    .ConfigureServices(services =\u003e\n                    {\n                        // Add other dependencies to the DI container\n                        // ...\n\n                        // \n                        services.AddHostedService\u003cApp\u003e();\n                    })\n                    .UseConsoleLifetime()\n                    .Build();\n\n    await host.RunAsync();\n}\n\nprivate static OnDesiredPropertiesChanged(TwinCollection desiredProperties, object userContext) {}\n```\n\nThe actual IoT Edge Module's functionality is in this example implemented in the `App` class, which looks like this:\n\n```csharp\ninternal class App : BackgroundService\n{\n\n    private readonly ModuleClient _iotHubModuleClient;\n    private readonly ILogger\u003cApp\u003e _logger;\n\n    /// \u003csummary\u003e\n    /// Initializes a new instance of the \u003csee cref=\"App\"/\u003e class.\n    /// \u003c/summary\u003e\n    public App(ModuleClient iotHubModuleClient, ILogger\u003cApp\u003e logger)\n    {\n        _iotHubModuleClient = iotHubModuleClient;\n        _logger = logger;\n    }\n\n    protected override async Task ExecuteAsync(CancellationToken cancellationToken)\n    {\n        while( !cancellationToken.IsCancellationRequested )\n        {\n            // Do some work here\n        }\n    }\n\n    public  Task StopAsync(CancellationToken cancellationToken)\n    {\n        // TODO: clean up\n        return Task.CompletedTask;\n    }\n}\n```\n\n## Graceful shutdown of IoT Edge modules\n\nWhen the IoT Edge runtime restarts an IoT Edge module (container), it seems that the running container instance is just killed. To be able to gracefully shutdown the module, it is required that the module is notified when a shutdown is happening.\nTo be able to do this, the `ShutdownHandler` class has been introduced.  (This class is taken from the [`EdgeUtil`](https://github.com/Azure/iotedge/issues/5274#issuecomment-885965160) codebase is a little bit modified).\n\nCreating an instance of the `ShutdownHandler` class offers you a `CancellationTokenSource` that is tied to the shutdown process of the running container.  In other words: when the container is being termined, the `CancellationTokenSource` is being canceled.\nThis means that the `CancellationToken` that is linked to it can be used in the module to determine if the module is being shut down:\n\n```csharp\nvar shutdownHandler = ShutdownHandler.Create(shutdownWaitPeriod: TimeSpan.FromSeconds(5), logger: log);\n\nwhile( !shutdownHandler.CancellationTokenSource.Token.IsCancellationRequested )\n{\n  // do work\n}\n```\n\nThe `ShutdownHandler` also offers a mechanism to make sure that everything can be cleaned up before completely shutting down the container.  The shutdown process will wait until the `SignalCleanupComplete()` method is called or until the `shutdownWaitPeriod` has been elapsed.\n\n```csharp\nvar shutdownHandler = ShutdownHandler.Create(shutdownWaitPeriod: TimeSpan.FromSeconds(5), logger: log);\n\nwhile( !shutdownHandler.CancellationTokenSource.Token.IsCancellationRequested )\n{\n  // do work\n}\n\n// Cleanup / Dispose some things\ndbConnection.Close();\nmoduleClient.Dispose();\n\nshutdownHandler.SignalCleanupComplete();\n```\n\n### Using ShutdownHandler with IHost\n\nTo use the `ShutdownHandler` in combination with `HostBuilder`/`IHost`, the following approach is adivsed:\n\n```csharp\nusing( var host = CreateHostBuilder().Build())\n{\n    var logger = host.Services.GetService\u003cILoggerFactory\u003e().GetLogger\u003cProgram\u003e()\n    var shutdownHandler = ShutdownHandler.Create(TimeSpan.FromSeconds(20), logger)\n\n    await host.StartAsync(shutdownHandler.CancellationTokenSource.Token);\n    logger.LogInformation(\"Module stopping ... \");\n    await host.WaitForShutdownAsync(shutdownHandler.CancellationTokenSource.Token);\n\n    logger.LogInformation(\"Module stopped\");\n\n    shutdownHandler.SignalCleanupComplete();\n}\n```\n\nNote that in the above code snippet we do not use `RunAsync`, but explicitly call `StartAsync` and `WaitForShutdownAsync`.  This is a [workaround](https://github.com/dotnet/runtime/issues/44086#issuecomment-811126003) for [this](https://github.com/dotnet/runtime/issues/44086) issue.\n\n### ModuleConfiguration\n\nThis library contains an abstract `ModuleConfiguration` class that allows you to abstract configuration-settings for an IoT Edge module.\nThe `ModuleConfiguration` class allows you to easily retrieve the desired properties from the module-twin and update the reported properties to the module twin as well.\n\nTo use this functionality, you need to inherit from this base-class and implement some basic functionality:\n\n```csharp\npublic class MyModuleConfiguration : ModuleConfiguration\n{\n    public int SomeIntegerProperty {get; private set;}\n    public string SomeStringProperty {get; private set;}\n\n    protected override void InitializeFromTwin(TwinCollection desiredProperties)\n    {\n        SomeIntegerProperty = Convert.ToInt32(desiredProperties[\"SomeIntegerConfigSetting\"]);\n        SomeStringProperty = Convert.ToString(desiredProperties[\"SomeStringConfigSetting\"]);\n    }\n\n    protected override void SetReportedProperties(TwinCollection reportedProperties)\n    {\n        reportedProperties[\"SomeIntegerConfigSetting\"] = SomeIntegerProperty;\n        reportedProperties[\"SomeStringConfigSetting\"] = SomeStringProperty;\n    }\n}\n```\n\nUsage:\n\n```csharp\nvar configuration = await ModuleConfiguration.CreateFromTwinAsync\u003cMyModuleConfiguration\u003e(moduleClient, logger);\n\n// Use the settings that originate from the ModuleTwin in some other classes.\nvar processor = new MyProcessor(configuration.SomeIntegerProperty);\n```\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffgheysels%2Ffg.iotedgemodule","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffgheysels%2Ffg.iotedgemodule","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffgheysels%2Ffg.iotedgemodule/lists"}