public async Task <IActionResult> Post([FromBody] PayloadV5 payload)
        {
            string payloadFieldsUnpacked = string.Empty;

            // Check that the post data is good
            if (!this.ModelState.IsValid)
            {
                log.WarnFormat("QueuedController validation failed {0}", this.ModelState.Messages());

                return(this.BadRequest(this.ModelState));
            }

            try
            {
                QueueClient queueClient = new QueueClient(storageConnectionString, queueName);

                await queueClient.CreateIfNotExistsAsync();

                await queueClient.SendMessageAsync(Convert.ToBase64String(JsonSerializer.SerializeToUtf8Bytes(payload)));
            }
            catch (Exception ex)
            {
                log.Error("Unable to open/create queue or send message", ex);

                return(this.Problem("Unable to open queue (creating if it doesn't exist) or send message", statusCode: 500, title: "Uplink payload not sent"));
            }

            return(this.Ok());
        }
        public static async Task Run(
            [QueueTrigger("%UplinkQueueName%", Connection = "AzureStorageConnectionString")]
            PayloadV5 payloadObject,
            ILogger log)
        {
            DeviceClient deviceClient = null;

            // I worry about threading for this and configuration
            if (Log == null)
            {
                Log = log;
            }

            // Quick n dirty hack to see what difference (if any) not processing retries makes
            if (payloadObject.is_retry)
            {
                Log.LogInformation("DevID:{dev_id} AppID:{app_id} Counter:{counter} Uplink message retry", payloadObject.dev_id, payloadObject.app_id, payloadObject.counter);
                return;
            }

            // Check that KeyVault URI is configured in environment variables. Not a lot we can do if it isn't....
            if (Configuration == null)
            {
                string keyVaultUri = Environment.GetEnvironmentVariable("KeyVaultURI");
                if (string.IsNullOrEmpty(keyVaultUri))
                {
                    Log.LogError("KeyVaultURI environment variable not set");
                    throw new ApplicationException("KeyVaultURI environment variable not set");
                }

                // Load configuration from KeyVault
                try
                {
                    Configuration = new ConfigurationBuilder()
                                    .AddEnvironmentVariables()
                                    .AddAzureKeyVault(keyVaultUri)
                                    .Build();
                }
                catch (Exception ex)
                {
                    Log.LogError(ex, $"Configuration loading failed");
                    throw;
                }
            }

            Log.LogInformation("DevID:{dev_id} AppID:{app_id} Counter:{counter} Uplink message device processing start", payloadObject.dev_id, payloadObject.app_id, payloadObject.counter);

            deviceClient = await DeviceClientCreate(
                Configuration.GetSection("DPSGlobaDeviceEndpoint").Value,
                Configuration.GetSection("DPSIDScope").Value,
                payloadObject.app_id,
                payloadObject.dev_id);

            await DeviceTelemetrySend(deviceClient, payloadObject);

            Log.LogInformation("DevID:{dev_id} AppID:{app_id} Counter:{counter} Uplink message device processing completed", payloadObject.dev_id, payloadObject.app_id, payloadObject.counter);
        }
        static async Task DeviceTelemetrySend(DeviceClient deviceClient, PayloadV5 payloadObject)
        {
            // Assemble the JSON payload to send to Azure IoT Hub/Central.
            Log.LogInformation("DevID:{dev_id} AppID:{app_id} Payload assembly start", payloadObject.dev_id, payloadObject.app_id);

            JObject telemetryEvent = new JObject();

            try
            {
                JObject payloadFields = (JObject)payloadObject.payload_fields;
                telemetryEvent.Add("HardwareSerial", payloadObject.hardware_serial);
                telemetryEvent.Add("Retry", payloadObject.is_retry);
                telemetryEvent.Add("Counter", payloadObject.counter);
                telemetryEvent.Add("DeviceID", payloadObject.dev_id);
                telemetryEvent.Add("ApplicationID", payloadObject.app_id);
                telemetryEvent.Add("Port", payloadObject.port);
                telemetryEvent.Add("PayloadRaw", payloadObject.payload_raw);
                telemetryEvent.Add("ReceivedAtUTC", payloadObject.metadata.time);

                // If the payload has been unpacked in TTN backend add fields to telemetry event payload
                if (payloadFields != null)
                {
                    foreach (JProperty child in payloadFields.Children())
                    {
                        EnumerateChildren(telemetryEvent, child);
                    }
                }
            }
            catch (Exception ex)
            {
                Log.LogError(ex, "DevID:{dev_id} AppID:{app_id} Payload processing or Telemetry event assembly failed", payloadObject.dev_id, payloadObject.app_id);
                throw;
            }

            // Send the message to Azure IoT Hub/Azure IoT Central
            Log.LogInformation("DevID:{dev_id} AppID:{app_id} Payload SendEventAsync start", payloadObject.dev_id, payloadObject.app_id);
            try
            {
                using (Message ioTHubmessage = new Message(Encoding.ASCII.GetBytes(JsonConvert.SerializeObject(telemetryEvent))))
                {
                    // Ensure the displayed time is the acquired time rather than the uploaded time. esp. important for when messages that ended up
                    // in poison queue are returned to the processing queue.
                    ioTHubmessage.Properties.Add("iothub-creation-time-utc", payloadObject.metadata.time.ToString("s", CultureInfo.InvariantCulture));
                    await deviceClient.SendEventAsync(ioTHubmessage);
                }
            }
            catch (Exception ex)
            {
                if (!DeviceClients.TryRemove(payloadObject.dev_id, out deviceClient))
                {
                    Log.LogWarning("DevID:{dev_id} AppID:{app_id} Payload SendEventAsync TryRemove failed", payloadObject.dev_id, payloadObject.app_id);
                }

                Log.LogError(ex, "DevID:{dev_id} AppID:{app_id} SendEventAsync failed", payloadObject.dev_id, payloadObject.app_id);
                throw;
            }
        }