IoT Edge Module stops sending data

320 views Asked by At

I have an IoT Edge module which sends data every 5 seconds, similar to the temperature sensor. It works fine, but stops sending data after some time, even though the module is still active and enabled. The logs look fine, and I do not see any cancellation.

Does edgeAgent or edgeHub does something here or why would a module stop sending data in an infinite loop?

namespace MyModule
{
    using System;
    using System.Runtime.Loader;
    using System.Threading;
    using System.Threading.Tasks;
    using Microsoft.Azure.Devices.Client;
    using Microsoft.Azure.Devices.Client.Transport.Mqtt;
    using Microsoft.Azure.Devices.Shared;
    using System.Text.Json;
    using System.Text;
    using MyModule.Dtos;
    using Geolocation;
    using System.Linq;

    class Program
    {
        private static ModuleClient ioTHubModuleClient;

        public static int Main() => MainAsync().Result;

        static async Task<int> MainAsync()
        {
            // Wait until the app unloads or is cancelled
            var cts = new CancellationTokenSource();
            AssemblyLoadContext.Default.Unloading += (ctx) => {Console.WriteLine("Unloading"); cts.Cancel();};
            Console.CancelKeyPress += (sender, cpe) => { Console.WriteLine("CancelKeyPress"); cts.Cancel();};

            MqttTransportSettings mqttSetting = new MqttTransportSettings(TransportType.Mqtt_Tcp_Only);
            ITransportSettings[] settings = { mqttSetting };

            // Open a connection to the Edge runtime
            ioTHubModuleClient = await ModuleClient.CreateFromEnvironmentAsync(settings);
            await ioTHubModuleClient.OpenAsync();
            Console.WriteLine("IoT Hub module client initialized.");
            await ioTHubModuleClient.SetMethodHandlerAsync("SetActiveServices", SetActiveServices, null);

            Console.WriteLine("Starting loop to send data");

            await SendSomeTelemetry(cts);

            WhenCancelled(cts.Token).Wait();
            return 0;
        }

        /// <summary>
        /// Handles cleanup operations when app is cancelled or unloads
        /// </summary>
        public static Task WhenCancelled(CancellationToken cancellationToken)
        {
            var tcs = new TaskCompletionSource<bool>();
            cancellationToken.Register(s => ((TaskCompletionSource<bool>)s).SetResult(true), tcs);
            return tcs.Task;
        }

        // Updating requested values should be done through twin properties, demonstrating direct calls, too, with this sample though
        private static Task<MethodResponse> SetActiveServices(MethodRequest methodRequest, object userContext)
        {
            return Task.Run(async() => {
                await SendToHub("Direct method call received");
                Console.WriteLine($"SetActiveServices method called!");
                return new MethodResponse(200);
            });
        }

        private static async Task SendSomeTelemetry(CancellationTokenSource cts) {

            while (!cts.Token.IsCancellationRequested)
            {
                if (ioTHubModuleClient == null)
                {
                    throw new InvalidOperationException("UserContext doesn't contain " + "expected values");
                }

                IoTDeviceData deviceData = new IoTDeviceData(Environment.GetEnvironmentVariable("IOTEDGE_DEVICEID"));
                var jsonString = JsonSerializer.Serialize(deviceData, options);
                await SendToHub(jsonString);
                await Task.Delay(TimeSpan.FromSeconds(5), cts.Token);
            }
        }

        private static async Task<bool> SendToHub(string message) {

            if (ioTHubModuleClient == null)
            {
                throw new InvalidOperationException("UserContext doesn't contain " + "expected values");
            }

            var messageBytes = Encoding.UTF8.GetBytes(message);

            using (var pipeMessage = new Message(messageBytes))
            {
                Console.WriteLine($"Sending message {Environment.GetEnvironmentVariable("IOTEDGE_DEVICEID")} {DateTime.Now.ToString()}");
                await ioTHubModuleClient.SendEventAsync("output1", pipeMessage);
            }

            return true;
        }

    }
}

Small note: I noticed that when the module stops sending data every 5 seconds and I call it's direct method, it resumes sending data...

Regarding the logs, it just shows all "Console.WriteLine" of the code in the output, and when it stops sending data, there is no error, just the last Console.WriteLine of the last successful message it sent.

Thanks Thomas

1

There are 1 answers

0
Indrajeet Singh On

Can you share the device name/configuration that you're using as edge device.

Because I had the same problem, and I got to know that edgeHub was using large chunks of memory (As edgeHub is optimized for performance), due to that there was no space left to run the program of sending telemetry.

So if you're using any of the resource constrained devices, you can always specify the environment variable as OptimizeForPerfomance to false.

You can go ahead and open runtime settings while deploying your temperature sensor module on IoT Hub portal and set edgeHub's environment variable as shown in the image below- enter image description here

Otherwise if you're using a deployment manifest file to deploy the temperature sensor module, add environment variable like this -

"edgeHub": {
  "type": "docker",
  "settings": {
    "image": "mcr.microsoft.com/azureiotedge-hub:1.1",
    "createOptions": <snipped>
  },
  "env": {
    "OptimizeForPerformance": {
      "value": "false"
    }
  },

That's all we have to do.

More info at - https://learn.microsoft.com/en-us/azure/iot-edge/troubleshoot-common-errors?view=iotedge-2020-11