예제 #1
0
        public async Task <Message> Run(
            [BlobTrigger("car-images/cameras/{camera}/{name}", Connection = "SECCTRL_CAR_IMAGES")] CloudBlockBlob imageBlob,
            string camera,
            string name,
            ILogger log,
            [DurableClient] IDurableOrchestrationClient orchestrationClient)
        {
            log.LogInformation($"Start processing of new image {name} from camera {camera}");

            // Store timestamp when image was received in blob storage
            var timestamp = imageBlob.Properties.LastModified;

            // Read image file from blob storage and convert content to Base64
            log.LogInformation("Downloading image data");
            var file = new byte[imageBlob.Properties.Length];
            await imageBlob.DownloadToByteArrayAsync(file, 0);

            // Try to recognize the license plate
            log.LogInformation("Starting license plate recognition");
            var recognitionResult = await licensePlateRecognizer.RecognizeAsync(file, configuration);

            // Move image to archive
            log.LogInformation("Moving image to archive folder");
            var archiveImageId = Guid.NewGuid().ToString();
            var archiveBlob    = imageBlob.Container.GetBlockBlobReference($"archive/{archiveImageId}.jpg");
            await archiveBlob.StartCopyAsync(imageBlob);

            await imageBlob.DeleteAsync();

            log.LogInformation("Building plate read result");
            if (recognitionResult != null)
            {
                // We have recognized a license plate
                var read = new PlateRead
                {
                    ReadTimestamp         = timestamp.Value.Ticks,
                    CameraID              = camera,
                    LicensePlate          = recognitionResult.Plate,
                    Confidence            = recognitionResult.Confidence,
                    Nationality           = recognitionResult.Region,
                    NationalityConfidence = recognitionResult.RegionConfidence,
                    ImageID = archiveImageId
                };

                var readQuality = "low";
                if (recognitionResult.Confidence >= 93.3d && recognitionResult.RegionConfidence >= 25d)
                {
                    readQuality = "high";
                }

                if (readQuality == "low")
                {
                    var instanceId = await orchestrationClient.StartNewAsync(
                        "OrchestrateRequestApproval",
                        new PlateReadApproval
                    {
                        Read = read
                    });

                    log.LogInformation($"Durable Function Ochestration started: {instanceId}");
                }

                return(CreateMessage(read, readQuality));
            }
            else
            {
                // No license plate found
                var read = new EmptyPlateRead
                {
                    ReadTimestamp = timestamp.Value.Ticks,
                    CameraID      = camera,
                    ImageID       = archiveImageId
                };
                return(CreateMessage(read, "empty"));
            }
        }
예제 #2
0
        public static async Task BlobTriggerStart([BlobTrigger("original-store/{name}")] CloudBlockBlob myBlob, string name, [DurableClient] IDurableOrchestrationClient starter, ILogger log)
        {
            string instanceId = await starter.StartNewAsync(nameof(FileProcessingOrchestrator), input : name);

            log.LogInformation($"Started orchestration with ID = '{instanceId}', Blob '{name}'.");
        }
        public static async Task Run([EventHubTrigger(Constants.EventHubAddPlayers, Connection = "EVENTHUB_CONNECTION_STRING")] EventData[] events,
                                     [DurableClient] IDurableOrchestrationClient starter,
                                     ILogger log)
        {
            var exceptions = new List <Exception>();

            // Connection to the Redis database - using Event Hubs to batch requests and reduce number of connections
            IDatabase cache = Helper.Connection.GetDatabase();

            foreach (EventData eventData in events)
            {
                try
                {
                    string messageBody = Encoding.UTF8.GetString(eventData.Body.Array, eventData.Body.Offset, eventData.Body.Count);

                    log.LogInformation($"BatchAddUsers Event Hub trigger function processed a message: {messageBody}");

                    // Processing logic.
                    Player tempPlayer = JsonConvert.DeserializeObject <Player>(messageBody);

                    string playerGUID        = tempPlayer.GUID;
                    string playerMatchmaking = tempPlayer.MatchmakingSettings;

                    if (playerMatchmaking == null)
                    {
                        return;
                    }

                    // Start unique player GUID orchestrator for match request handling
                    var instanceId = await starter.StartNewAsync("MatchRequest", playerGUID, Constants.MatchmakingTimeoutInSeconds); // TODO: pass match request information

                    // Flush existing key
                    string playerKey = string.Format("{0}:{1}", Constants.PlayerHash, playerGUID);
                    cache.KeyDelete(playerKey);
                    log.LogInformation($"Player key flushed: {playerKey}");

                    // Create player Hash
                    cache.HashSet(playerKey, tempPlayer.ToHashEntryArray(), CommandFlags.FireAndForget);
                    log.LogInformation($"Hash created including the player information: {playerKey}");

                    // Add timestamp to the PlayersTimeStartedMatchmaking Sorted Set
                    cache.SortedSetAdd(Constants.PlayerTimestampsSortedSet, playerGUID, new DateTimeOffset(DateTime.UtcNow).ToUnixTimeSeconds(), CommandFlags.FireAndForget);
                    log.LogInformation($"Player matchmaking started timestamp added: {Constants.PlayerTimestampsSortedSet}");

                    // Find a suitable game session for the user and create it if doesn't exist
                    // SDIFF MatchMakingSessions and SessionsReady to find a suitable game session that is not full
                    // RedisValue[] SetCombine(SetOperation operation, RedisKey first, RedisKey second, CommandFlags flags = CommandFlags.None);
                    string keySessionsPerMatchmaking = string.Format("{0}:{1}", Constants.SessionsPerMatchmakingSet, playerMatchmaking);
                    string sessionKey  = null;
                    string sessionGUID = null;
                    int    capacity    = -1;

                    var results = cache.SetCombine(SetOperation.Difference, keySessionsPerMatchmaking, Constants.SessionsReadySet);

                    if (results != null && results.Length > 0)
                    {
                        RedisValue entry = results.First(); // Get a session from the set, optimization could be to leverage the timestamp picking the oldest one

                        sessionGUID = entry.ToString();
                        sessionKey  = string.Format("{0}:{1}", Constants.SessionHash, sessionGUID);

                        capacity = Int32.Parse(cache.HashGet(sessionKey, Constants.CapacityField));

                        log.LogInformation($"Session found for the player: {sessionGUID}; capacity: {capacity.ToString()}");
                    }
                    else // Create session
                    {
                        Guid randomGuid = Guid.NewGuid();
                        sessionGUID = randomGuid.ToString();

                        sessionKey = string.Format("{0}:{1}", Constants.SessionHash, sessionGUID);
                        cache.KeyDelete(sessionKey);

                        var tempSession = new Session
                        {
                            //Initialization
                            GUID                = sessionGUID,
                            Capacity            = Constants.CapacityMax,
                            MatchmakingSettings = playerMatchmaking
                        };

                        var outputJson = JsonConvert.SerializeObject(tempSession);

                        log.LogInformation($"Session not found for the player, a new one was created: {sessionKey}");

                        // Add new game session to the Sessions Hash
                        cache.HashSet(sessionKey, tempSession.ToHashEntryArray(), CommandFlags.FireAndForget);
                        log.LogInformation($"New session added to the Sessions Hash: {sessionKey}");

                        // Add the session to the matchmaking bucket
                        cache.SetAdd(keySessionsPerMatchmaking, sessionGUID, CommandFlags.FireAndForget);
                        log.LogInformation($"New session added to the matchmaking bucket: {keySessionsPerMatchmaking}");

                        capacity = Constants.CapacityMax;
                    }

                    // Add player to the game session
                    sessionKey = string.Format("{0}:{1}", Constants.SessionPlayersSet, sessionGUID);
                    if (!cache.SetContains(sessionKey, playerGUID)) // Is the player in this game session already
                    {
                        cache.SetAdd(sessionKey, playerGUID, CommandFlags.FireAndForget);
                    }
                    log.LogInformation($"Player added to the session: {sessionKey}");

                    // Update the capacity for the session
                    capacity--;
                    sessionKey = string.Format("{0}:{1}", Constants.SessionHash, sessionGUID);
                    cache.HashSet(sessionKey, Constants.CapacityField, capacity.ToString());

                    if (capacity == 0)
                    {
                        // Add the session to the SessionsReady Set
                        cache.SetAdd(Constants.SessionsReadySet, sessionGUID, CommandFlags.FireAndForget);
                        log.LogInformation($"Session added to the sessions ready set: {Constants.SessionsReadySet}");
                    }
                }
                catch (Exception e)
                {
                    // We need to keep processing the rest of the batch - capture this exception and continue.
                    // Also, consider capturing details of the message that failed processing so it can be processed again later.
                    exceptions.Add(e);
                }
            }

            // Once processing of the batch is complete, if any messages in the batch failed processing throw an exception so that there is a record of the failure.
            if (exceptions.Count > 1)
            {
                throw new AggregateException(exceptions);
            }

            if (exceptions.Count == 1)
            {
                throw exceptions.Single();
            }
        }
        public async Task <IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "lmi/webhook")] HttpRequest?request,
            [DurableClient] IDurableOrchestrationClient starter)
        {
            try
            {
                logger.LogInformation("Received webhook request");

                using var streamReader = new StreamReader(request?.Body !);
                var requestBody = await streamReader.ReadToEndAsync().ConfigureAwait(false);

                if (string.IsNullOrEmpty(requestBody))
                {
                    logger.LogError($"{nameof(request)} body is null");
                    return(new BadRequestResult());
                }

                string?         instanceId          = null;
                SocRequestModel?socRequest          = null;
                var             webhookRequestModel = lmiWebhookReceiverService.ExtractEvent(requestBody);
                switch (webhookRequestModel.WebhookCommand)
                {
                case WebhookCommand.SubscriptionValidation:
                    return(new OkObjectResult(webhookRequestModel.SubscriptionValidationResponse));

                case WebhookCommand.TransformAllSocToJobGroup:

                    socRequest = new SocRequestModel
                    {
                        Uri = webhookRequestModel.Url,
                    };
                    instanceId = await starter.StartNewAsync(nameof(LmiOrchestrationTrigger.RefreshOrchestrator), socRequest).ConfigureAwait(false);

                    break;

                case WebhookCommand.TransformSocToJobGroup:
                    socRequest = new SocRequestModel
                    {
                        Uri   = webhookRequestModel.Url,
                        SocId = webhookRequestModel.ContentId,
                    };
                    instanceId = await starter.StartNewAsync(nameof(LmiOrchestrationTrigger.RefreshJobGroupOrchestrator), socRequest).ConfigureAwait(false);

                    break;

                case WebhookCommand.PurgeAllJobGroups:
                    socRequest = new SocRequestModel
                    {
                        Uri = webhookRequestModel.Url,
                    };
                    instanceId = await starter.StartNewAsync(nameof(LmiOrchestrationTrigger.PurgeOrchestrator), socRequest).ConfigureAwait(false);

                    break;

                case WebhookCommand.PurgeJobGroup:
                    socRequest = new SocRequestModel
                    {
                        Uri   = webhookRequestModel.Url,
                        SocId = webhookRequestModel.ContentId,
                    };
                    instanceId = await starter.StartNewAsync(nameof(LmiOrchestrationTrigger.PurgeJobGroupOrchestrator), socRequest).ConfigureAwait(false);

                    break;

                default:
                    return(new BadRequestResult());
                }

                logger.LogInformation($"Started orchestration with ID = '{instanceId}' for SOC {socRequest?.Uri}");

                return(starter.CreateCheckStatusResponse(request, instanceId));
            }
            catch (Exception ex)
            {
                logger.LogError(ex.ToString());
                return(new StatusCodeResult((int)HttpStatusCode.InternalServerError));
            }
        }
 /// <summary>
 /// Starts a new execution of the specified orchestrator function.
 /// </summary>
 /// <param name="client">The client object.</param>
 /// <param name="orchestratorFunctionName">The name of the orchestrator function to start.</param>
 /// <returns>A task that completes when the orchestration is started. The task contains the instance id of the started
 /// orchestratation instance.</returns>
 /// <exception cref="ArgumentException">
 /// The specified function does not exist, is disabled, or is not an orchestrator function.
 /// </exception>
 public static Task <string> StartNewAsync(
     this IDurableOrchestrationClient client,
     string orchestratorFunctionName)
 {
     return(client.StartNewAsync <object>(orchestratorFunctionName, null));
 }
예제 #6
0
 public static async Task Run([TimerTrigger("0 0 1 * * *")]
                              TimerInfo timerInfo,
                              [DurableClient] IDurableOrchestrationClient orchestrationClient)
 {
     await orchestrationClient.StartNewAsync(nameof(DemoOrchestration), null, Guid.NewGuid());
 }
예제 #7
0
        public async Task <HttpResponseMessage> ProcessWebHook([HttpTrigger(AuthorizationLevel.Function, "POST")] HttpRequestMessage request,
                                                               [DurableClient] IDurableOrchestrationClient orchestrationClient,
                                                               ExecutionContext executionContext)
        {
            DateTime startTime   = DateTime.UtcNow;
            string   requestBody = await request.Content.ReadAsStringAsync().ConfigureAwait(false);

            request.Headers.TryGetValues("X-GitHub-Delivery", out IEnumerable <string> deliveryGuidValues);
            if (deliveryGuidValues == null || !deliveryGuidValues.Any())
            {
                string errorMessage = $"WebHookProcessor requires 'X-GitHub-Event' header.";
                this.telemetryClient.TrackTrace(errorMessage, SeverityLevel.Error);
                throw new FatalException(errorMessage);
            }
            string sessionId = deliveryGuidValues.First();

            request.Headers.TryGetValues("X-GitHub-Event", out IEnumerable <string> eventTypeValues);
            if (eventTypeValues == null || !eventTypeValues.Any())
            {
                string errorMessage = $"WebHookProcessor endpoint requires 'X-GitHub-Event' header.";
                this.telemetryClient.TrackTrace(errorMessage, SeverityLevel.Error);
                throw new FatalException(errorMessage);
            }
            string eventType = eventTypeValues.First();

            // The request does not have to go through the LogicApp. This might happen e.g., for local testing. Therefore, assume that Logic-App-related headers are optional:
            // X-LogicApp-Timestamp
            // X-Ms-Workflow-Run-Id
            request.Headers.TryGetValues("X-LogicApp-Timestamp", out IEnumerable <string> logicAppStartDateValues);
            string logicAppStartDate = string.Empty;

            if (logicAppStartDateValues != null && logicAppStartDateValues.Any())
            {
                logicAppStartDate = logicAppStartDateValues.First();
            }

            request.Headers.TryGetValues("X-Ms-Workflow-Run-Id", out IEnumerable <string> logicAppRunIdValues);
            string logicAppRunId = string.Empty;

            if (logicAppRunIdValues != null && logicAppRunIdValues.Any())
            {
                logicAppRunId = logicAppRunIdValues.First();
            }

            OrchestrationContext context = new OrchestrationContext()
            {
                RequestBody       = requestBody,
                CollectorType     = CollectorType.Main.ToString(),
                SessionId         = sessionId,
                EventType         = eventType,
                FunctionStartDate = startTime,
                LogicAppStartDate = logicAppStartDate,
                LogicAppRunId     = logicAppRunId,
                InvocationId      = executionContext.InvocationId.ToString(),
            };

            ITelemetryClient telemetryClient = new GitHubApplicationInsightsTelemetryClient(this.telemetryClient, context);

            telemetryClient.TrackEvent("SessionStart", GetMainCollectorSessionStartEventProperties(context, identifier: eventType, logicAppRunId));

            string instanceId = await orchestrationClient.StartNewAsync("ProcessWebHookOrchestration", context).ConfigureAwait(false);

            return(orchestrationClient.CreateCheckStatusResponse(request, instanceId, returnInternalServerErrorOnFailure: true));
        }
예제 #8
0
 public static async void Run([BlobTrigger("csvimports/{name}", Connection = "StorageConnection")] Stream myBlob, string name, [DurableClient] IDurableOrchestrationClient starter, ILogger log)
 {
     string instanceId = await starter.StartNewAsync("CSVImport_Orchestrator", name); log.LogInformation($"C# Blob trigger function Processed blob\n Name:{name} \n Size: {myBlob.Length} Bytes");
 }