private async void StartDevices()
        {
            // Create namespace manager
            var namespaceUri = ServiceBusEnvironment.CreateServiceUri("sb", txtNamespace.Text, string.Empty);
            var tokenProvider = TokenProvider.CreateSharedAccessSignatureTokenProvider(txtKeyName.Text, txtKeyValue.Text);
            var namespaceManager = new NamespaceManager(namespaceUri, tokenProvider);

            // Check if the event hub already exists, if not, create the event hub.
            if (!await namespaceManager.EventHubExistsAsync(cboEventHub.Text))
            {
                WriteToLog(string.Format(EventHubDoesNotExists, cboEventHub.Text));
                return;
            }
            var eventHubDescription = await namespaceManager.GetEventHubAsync(cboEventHub.Text);

            WriteToLog(string.Format(EventHubCreatedOrRetrieved, cboEventHub.Text));

            // Check if the SAS authorization rule used by devices to send events to the event hub already exists, if not, create the rule.
            var authorizationRule = eventHubDescription.
                                    Authorization.
                                    FirstOrDefault(r => string.Compare(r.KeyName,
                                                                        SenderSharedAccessKey,
                                                                        StringComparison.InvariantCultureIgnoreCase)
                                                                        == 0) as SharedAccessAuthorizationRule;

            if (authorizationRule == null)
            {
                authorizationRule = new SharedAccessAuthorizationRule(SenderSharedAccessKey,
                                                                         SharedAccessAuthorizationRule.GenerateRandomKey(),
                                                                         new[]
                                                                         {
                                                                                     AccessRights.Send
                                                                         });
                eventHubDescription.Authorization.Add(authorizationRule);
                await namespaceManager.UpdateEventHubAsync(eventHubDescription);
            }

            cancellationTokenSource = new CancellationTokenSource();
            var serviceBusNamespace = txtNamespace.Text;
            var eventHubName = cboEventHub.Text;
            var senderKey = authorizationRule.PrimaryKey;
            var status = DefaultStatus;
            var eventInterval = txtEventIntervalInMilliseconds.IntegerValue;
            var minValue = txtMinValue.IntegerValue;
            var maxValue = txtMaxValue.IntegerValue;
            var minOffset = txtMinOffset.IntegerValue;
            var maxOffset = txtMaxOffset.IntegerValue;
            var spikePercentage = trackbarSpikePercentage.Value;
            var cancellationToken = cancellationTokenSource.Token;

            // Create one task for each device
            for (var i = 1; i <= txtDeviceCount.IntegerValue; i++)
            {
                var deviceId = i;
#pragma warning disable 4014
#pragma warning disable 4014
                Task.Run(async () =>
#pragma warning restore 4014
                {
                    var deviceName = $"device{deviceId:000}";

                    if (radioButtonAmqp.Checked)
                    {
                        // The token has the following format: 
                        // SharedAccessSignature sr={URI}&sig={HMAC_SHA256_SIGNATURE}&se={EXPIRATION_TIME}&skn={KEY_NAME}
                        var token = CreateSasTokenForAmqpSender(SenderSharedAccessKey,
                                                                senderKey,
                                                                serviceBusNamespace,
                                                                eventHubName,
                                                                deviceName,
                                                                TimeSpan.FromDays(1));
                        WriteToLog(string.Format(SasToken, deviceId));

                        var messagingFactory = MessagingFactory.Create(ServiceBusEnvironment.CreateServiceUri("sb", serviceBusNamespace, ""), new MessagingFactorySettings
                        {
                            TokenProvider = TokenProvider.CreateSharedAccessSignatureTokenProvider(token),
                            TransportType = TransportType.Amqp
                        });
                        WriteToLog(string.Format(MessagingFactoryCreated, deviceId));

                        // Each device uses a different publisher endpoint: [EventHub]/publishers/[PublisherName]
                        var eventHubClient = messagingFactory.CreateEventHubClient($"{eventHubName}/publishers/{deviceName}");
                        WriteToLog(string.Format(EventHubClientCreated, deviceId, eventHubClient.Path));

                        while (!cancellationToken.IsCancellationRequested)
                        {
                            // Create random value
                            var value = GetValue(minValue, maxValue, minOffset, maxOffset, spikePercentage);
                            var timestamp = DateTime.Now;

                            // Create EventData object with the payload serialized in JSON format 
                            var payload = new Payload
                            {
                                DeviceId = deviceId,
                                Name = deviceName,
                                Status = status,
                                Value = value,
                                Timestamp = timestamp
                            };
                            var json = JsonConvert.SerializeObject(payload);
                            using (var eventData = new EventData(Encoding.UTF8.GetBytes(json))
                            {
                                PartitionKey = deviceName
                            })
                            {
                                // Create custom properties
                                eventData.Properties.Add(DeviceId, deviceId);
                                eventData.Properties.Add(DeviceName, deviceName);
                                eventData.Properties.Add(DeviceStatus, status);
                                eventData.Properties.Add(Value, value);
                                eventData.Properties.Add(Timestamp, timestamp);

                                // Send the event to the event hub
                                await eventHubClient.SendAsync(eventData);
                                WriteToLog($"[Event] DeviceId=[{payload.DeviceId:000}] " +
                                           $"Value=[{payload.Value:000}] " +
                                           $"Timestamp=[{payload.Timestamp}]");
                            }

                            // Wait for the event time interval
                            Thread.Sleep(eventInterval);
                        }
                    }
                    else
                    {
                        // The token has the following format: 
                        // SharedAccessSignature sr={URI}&sig={HMAC_SHA256_SIGNATURE}&se={EXPIRATION_TIME}&skn={KEY_NAME}
                        var token = CreateSasTokenForHttpsSender(SenderSharedAccessKey,
                            senderKey,
                            serviceBusNamespace,
                            eventHubName,
                            deviceName,
                            TimeSpan.FromDays(1));
                        WriteToLog(string.Format(SasToken, deviceId));

                        // Create HttpClient object used to send events to the event hub.
                        var httpClient = new HttpClient
                        {
                            BaseAddress =
                                new Uri(string.Format(EventHubUrl,
                                    serviceBusNamespace,
                                    eventHubName,
                                    deviceName).ToLower())
                        };
                        httpClient.DefaultRequestHeaders.TryAddWithoutValidation("Authorization", token);
                        httpClient.DefaultRequestHeaders.Add("ContentType",
                            "application/json;type=entry;charset=utf-8");
                        WriteToLog(string.Format(HttpClientCreated, deviceId, httpClient.BaseAddress));

                        while (!cancellationToken.IsCancellationRequested)
                        {
                            // Create random value
                            var value = GetValue(minValue, maxValue, minOffset, maxOffset, spikePercentage);
                            var timestamp = DateTime.Now;

                            // Create EventData object with the payload serialized in JSON format 
                            var payload = new Payload
                            {
                                DeviceId = deviceId,
                                Name = deviceName,
                                Status = status,
                                Value = value,
                                Timestamp = timestamp
                            };
                            var json = JsonConvert.SerializeObject(payload);

                            // Create HttpContent
                            var postContent = new ByteArrayContent(Encoding.UTF8.GetBytes(json));

                            // Create custom properties
                            postContent.Headers.Add(DeviceId, deviceId.ToString(CultureInfo.InvariantCulture));
                            postContent.Headers.Add(DeviceName, deviceName);
                            //postContent.Headers.Add(DeviceStatus, location);
                            postContent.Headers.Add(Value, value.ToString(CultureInfo.InvariantCulture));
                            postContent.Headers.Add(Timestamp, timestamp.ToString(CultureInfo.InvariantCulture));

                            try
                            {
                                var response =
                                    await
                                        httpClient.PostAsync(
                                            httpClient.BaseAddress + "/messages" + "?timeout=60" + ApiVersion,
                                            postContent, cancellationToken);
                                response.EnsureSuccessStatusCode();
                                WriteToLog($"[Event] DeviceId=[{payload.DeviceId:000}] " +
                                           $"Value=[{payload.Value:000}] " +
                                           $"Timestamp=[{payload.Timestamp}]");
                            }
                            catch (HttpRequestException ex)
                            {
                                WriteToLog(string.Format(SendFailed, deviceId, ex.Message));
                            }

                            // Wait for the event time interval
                            Thread.Sleep(eventInterval);
                        }
                    }
                },
                cancellationToken).ContinueWith(t =>
#pragma warning restore 4014
#pragma warning restore 4014
                        {
                    if (t.IsFaulted && t.Exception != null)
                    {
                        HandleException(t.Exception);
                    }
                }, cancellationToken);
            }
        }
        public async Task ProcessEventAsync(Payload payload)
        {
            try
            {
                // Validate payload
                if (payload == null)
                {
                    return;
                }

                // Enqueue the new payload
                var queueResult = await StateManager.TryGetStateAsync<Queue<Payload>>(QueueState);
                if (queueResult.HasValue)
                {
                    var queue = queueResult.Value;
                    queue.Enqueue(payload);

                    // The actor keeps the latest n payloads in a queue, where n is  
                    // defined by the QueueLength parameter in the Settings.xml file.
                    if (queue.Count > ((DeviceActorService)ActorService).QueueLength)
                    {
                        queue.Dequeue();
                    }
                }

                // Retrieve Metadata from the Actor state
                var metadataResult = await StateManager.TryGetStateAsync<Device>(MetadataState);
                var metadata = metadataResult.HasValue ? 
                               metadataResult.Value :
                               new Device
                               {
                                   DeviceId = payload.DeviceId,
                                   Name = payload.Name,
                                   MinThreshold = MinThresholdDefault,
                                   MaxThreshold = MaxThresholdDefault,
                                   Model = Unknown,
                                   Type = Unknown,
                                   Manufacturer = Unknown,
                                   City = Unknown,
                                   Country = Unknown
                               };

                // Trace ETW event
                ActorEventSource.Current.Message($"Id=[{payload.DeviceId}] Value=[{payload.Value}] Timestamp=[{payload.Timestamp}]");

                // This ETW event is traced to a separate table with respect to the message
                ActorEventSource.Current.Telemetry(metadata, payload);

                // Track Application Insights event
                Program.TelemetryClient.TrackEvent(new EventTelemetry
                {
                    Name = "Telemetry",
                    Properties =
                            {
                                {"DeviceId", metadata.DeviceId.ToString(CultureInfo.InvariantCulture)},
                                {"Name", metadata.Name},
                                {"City", metadata.City},
                                {"Country", metadata.Country},
                                {"Manufacturer", metadata.Manufacturer},
                                {"Model", metadata.Model},
                                {"Type", metadata.Type},
                                {"MinThreshold", metadata.MinThreshold.ToString(CultureInfo.InvariantCulture)},
                                {"MaxThreshold", metadata.MaxThreshold.ToString(CultureInfo.InvariantCulture)},
                                {"Value", payload.Value.ToString(CultureInfo.InvariantCulture)},
                                {"Status", payload.Status},
                                {"Timestamp", payload.Timestamp.ToString(CultureInfo.InvariantCulture)},
                                {"ActorType", "DeviceActor"},
                                {"ActorId", Id.ToString()},
                                {"ServiceName", ActorService.Context.ServiceName.ToString()},
                                {"Partition", ActorService.Context.PartitionId.ToString()},
                                {"Node", ActorService.Context.NodeContext.NodeName}
                            },
                    Metrics =
                            {
                                {"TelemetryValue", payload.Value},
                                {"TelemetryCount", 1}
                            },
                });

                // Real spikes happen when both Spike1 and Spike2 are equal to 1. By the way, you can change the logic
                if (payload.Value < metadata.MinThreshold || payload.Value > metadata.MaxThreshold)
                {
                    // Create EventData object with the payload serialized in JSON format 
                    var alert = new Alert
                    {
                        DeviceId = metadata.DeviceId,
                        Name = metadata.Name,
                        MinThreshold = metadata.MinThreshold,
                        MaxThreshold = metadata.MaxThreshold,
                        Model = metadata.Model,
                        Type = metadata.Type,
                        Manufacturer = metadata.Manufacturer,
                        City = metadata.City,
                        Country = metadata.Country,
                        Status = payload.Status,
                        Value = payload.Value,
                        Timestamp = payload.Timestamp
                    };
                    var json = JsonConvert.SerializeObject(alert);
                    using (var eventData = new EventData(Encoding.UTF8.GetBytes(json))
                    {
                        PartitionKey = payload.Name
                    })
                    {
                        // Create custom properties
                        eventData.Properties.Add(DeviceId, payload.DeviceId);
                        eventData.Properties.Add(Value, payload.Value);
                        eventData.Properties.Add(Timestamp, payload.Timestamp);

                        // Send the event to the event hub
                        await eventHubClient.SendAsync(eventData);

                        // Trace ETW event
                        ActorEventSource.Current.Message($"[Alert] Id=[{payload.DeviceId}] Value=[{payload.Value}] Timestamp=[{payload.Timestamp}]");

                        // This ETW event is traced to a separate table
                        ActorEventSource.Current.Alert(metadata, payload);

                        // Track Application Insights event
                        Program.TelemetryClient.TrackEvent(new EventTelemetry
                        {
                            Name = "Alert",
                            Properties =
                            {
                                {"DeviceId", metadata.DeviceId.ToString(CultureInfo.InvariantCulture)},
                                {"Name", metadata.Name},
                                {"City", metadata.City},
                                {"Country", metadata.Country},
                                {"Manufacturer", metadata.Manufacturer},
                                {"Model", metadata.Model},
                                {"Type", metadata.Type},
                                {"MinThreshold", metadata.MinThreshold.ToString(CultureInfo.InvariantCulture)},
                                {"MaxThreshold", metadata.MaxThreshold.ToString(CultureInfo.InvariantCulture)},
                                {"Value", payload.Value.ToString(CultureInfo.InvariantCulture)},
                                {"Status", payload.Status},
                                {"Timestamp", payload.Timestamp.ToString(CultureInfo.InvariantCulture)},
                                {"ActorType", "DeviceActor"},
                                {"ActorId", Id.ToString()},
                                {"ServiceName", ActorService.Context.ServiceName.ToString()},
                                {"Partition", ActorService.Context.PartitionId.ToString()},
                                {"Node", ActorService.Context.NodeContext.NodeName}
                            },
                            Metrics =
                            {
                                {"AlertValue", payload.Value},
                                {"AlertCount", 1}
                            },
                        });
                    }
                }
            }
            catch (Exception ex)
            {
                // Trace exception as ETW event
                ActorEventSource.Current.Error(ex);

                // Trace exception using Application Insights
                Program.TelemetryClient.TrackException(ex, new Dictionary<string, string>
                {
                    { "ActorType", "DeviceActor"},
                    { "ActorId", Id.ToString()},
                    { "ServiceName", ActorService.Context.ServiceName.ToString()},
                    { "Partition", ActorService.Context.PartitionId.ToString()},
                    { "Node", ActorService.Context.NodeContext.NodeName}
                });
            }
        }
 public void Alert(Device device, Payload payload)
 {
     if (device != null && payload != null && IsEnabled())
     {
         Alert(device.DeviceId,
               device.Name,
               device.City,
               device.Country,
               device.Manufacturer,
               device.Model,
               device.Type,
               device.MinThreshold,
               device.MaxThreshold,
               payload.Value,
               payload.Status,
               payload.Timestamp);
     }
 }