static async Task <MessageResponse> AnalyzeImage(EnvSettings.CameraDevice camera)
        {
            try
            {
                List <ImageAnalysisResult> imageAnalysisResults = new List <ImageAnalysisResult>()
                {
                };

                // Get details for AI module tags' folders
                foreach (var module in camera.AIModules)
                {
                    foreach (var tag in module.Tags)
                    {
                        // Check if it is time to analyze images for current tag
                        if (_counterValue % tag.AnalyzeTimeInterval != 0)
                        {
                            continue;
                        }

                        // Analyze each image in the tag's folder
                        string   tagFolder = Path.Combine(camera.LocalFolder, camera.FactoryId, module.Name, tag.Name);
                        string[] images    = Directory.GetFiles(tagFolder);

                        _consoleLogger.LogDebug($"Found {images.Length} images in folder {tagFolder}");

                        // Analyze image files asyncrhonously
                        Task <ImageAnalysisResult>[] tasks = new Task <ImageAnalysisResult> [images.Length];
                        for (int i = 0; i < images.Length; i++)
                        {
                            string filePath = Path.Combine(tagFolder, images[i]);
                            tasks[i] = AnalyzeImage(filePath, camera.FactoryId, camera.Id, module, tag, camera.OutputFolder);
                        }

                        // Wait for tasks to complete
                        var taskResults = await Task.WhenAll(tasks);

                        // Add to bigger list
                        imageAnalysisResults.AddRange(taskResults.Where(x => x != null));

                        /// Clean up local tag folder is not needed since
                        /// every image is deleted during the inner method.
                    }
                }

                // Send message to hub only if there are any results to report
                if (imageAnalysisResults.Count() > 0)
                {
                    // Create camera results object
                    CameraAnalysisResult cameraResults = new CameraAnalysisResult(camera.FactoryId, camera.Id, imageAnalysisResults);

                    // Send message to hub for notification purposes
                    _consoleLogger.LogTrace($"Sending notification message to hub for camera {camera.Id}. # of results: {cameraResults.ImageAnalysisResults.Length}");

                    // Create hub message and set its properties
                    Message message = new Message(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(cameraResults)));
                    message.Properties.Add("messageType", "notification");

                    // Send notification message
                    await SendMessageToHub(message);

                    // Log it
                    _consoleLogger.LogTrace($"Sent notification message for camera {camera.Id}");
                }
            }
            catch (Exception e)
            {
                _consoleLogger.LogCritical("AnalyzeImage caught an exception: {0}", e);
            }

            return(MessageResponse.Completed);
        }
        static async Task <MessageResponse> StoreImage(EnvSettings.CameraDevice camera)
        {
            try
            {
                // Get camera credentials
                string plainCredentials = $"{camera.Username}:{camera.Password}";
                string svcCredentials   = Convert.ToBase64String(ASCIIEncoding.ASCII.GetBytes(plainCredentials));
                _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", svcCredentials);

                _consoleLogger.LogDebug($"Camera URL: {camera.ImageEndpoint}");

                // Make GET request to get image
                var response = await _httpClient.GetAsync(camera.ImageEndpoint);

                if (!response.IsSuccessStatusCode)
                {
                    _consoleLogger.LogError($"Failed to make GET request to camera {camera.Id}. Response: {response.ReasonPhrase}");
                    return(MessageResponse.Abandoned);
                }
                else
                {
                    _consoleLogger.LogDebug($"Get request to camera {camera.Id} was successful");
                }

                byte[] byteArray = await response.Content.ReadAsByteArrayAsync();

                // Save the image in respective local folder for each AI module's tag
                DateTime     utcDate   = DateTime.UtcNow;
                TimeZoneInfo localZone = TimeZoneInfo.FindSystemTimeZoneById(camera.TimeZoneId);
                DateTime     localDate = TimeZoneInfo.ConvertTimeFromUtc(utcDate, localZone);

                string imageName = $"{localDate.ToString("yyyyMMddTHHmmssfff")}";
                foreach (var module in camera.AIModules)
                {
                    foreach (var tag in module.Tags)
                    {
                        // Check if it is time to capture image for the specific tag
                        if (_counterValue % tag.CaptureTimeInterval != 0)
                        {
                            continue;
                        }

                        _consoleLogger.LogTrace($"Time to capture image for camera {camera.Id} and tag {tag.Name}");

                        string tagFolder = Path.Combine(camera.LocalFolder, camera.FactoryId, module.Name, tag.Name);
                        if (!Directory.Exists(tagFolder))
                        {
                            Directory.CreateDirectory(tagFolder);
                        }

                        string imagePath = Path.Combine(tagFolder, Path.ChangeExtension(imageName, "jpg"));
                        File.WriteAllBytes(imagePath, byteArray);

                        _consoleLogger.LogDebug($"Saved image to path {imagePath}");
                    }
                }
            }
            catch (Exception e)
            {
                _consoleLogger.LogCritical("StoreImage caught an exception: {0}", e);
            }

            return(MessageResponse.Completed);
        }