private EventMatch FindGroupMatch(EventGroup group, ICollection <IEvent> events, StateMachineContext context, JToken data) { Debug.Assert(group != null); Debug.Assert(events != null); Debug.Assert(context != null); Debug.Assert(data != null); foreach (var evt in events) { Debug.Assert(evt != null); bool isDefinedAndNotAMatch(string eventDefAttribute, string incomingEventAttribute) { return(!(string.IsNullOrWhiteSpace(eventDefAttribute)) && !(eventDefAttribute.IsEqualTo(incomingEventAttribute))); } var matchedEventName = group.Events.FirstOrDefault(e => e.IsEqualTo(evt.EventName)); if (string.IsNullOrWhiteSpace(matchedEventName)) { continue; } var targetEvent = context.Workflow.Events.Single(e => e.Name.IsEqualTo(evt.EventName)); if (isDefinedAndNotAMatch(targetEvent.Source, evt.EventSource)) { continue; } if (isDefinedAndNotAMatch(targetEvent.Type, evt.EventType)) { continue; } if (!string.IsNullOrWhiteSpace(group.Condition) && !group.Condition.EvalPredicateExpr(data, context)) { continue; } return(new EventMatch { EventDefinition = targetEvent, EventInstance = evt, Transition = this.TargetTransition, Group = group }); } return(null); }
private EventMatch[] FindMatches(ICollection <IEvent> events, StateMachineContext context, JToken data) { var matches = new List <EventMatch>(); foreach (var group in this.TargetTransition.EventGroups) { var match = FindGroupMatch(group, events, context, data); if (match == null) { return(null); } matches.Add(match); } return(matches.ToArray()); }
private static async Task <JToken> RunAsync(StateMachineContext context) { Debug.Assert(context != null); var state = context.Workflow.States.SingleOrDefault(s => s.Start); if (state == null) { throw new InvalidOperationException("Unable to resolve single start state in workflow."); } while (state != null) { state = await state.ExecuteAsync(context); } return(context.Data); }
public static async Task <EventMatch[]> WaitForFirstMatchedSubscriptionAsync( IEnumerable <EventSubscription> subscriptions, StateMachineContext context, JToken data, CancellationToken cancelToken) { subscriptions.CheckArgNull(nameof(subscriptions)); context.CheckArgNull(nameof(context)); Func <ICollection <IEvent>, EventMatch[]> matchFinder = accumulated => { foreach (var subscription in subscriptions) { EventMatch[] matches = subscription.FindMatches(accumulated, context, data); if (matches?.Length > 0) { return(matches); } } return(null); }; var accumulated = new List <IEvent>(); EventMatch[] matches = null; while (matches == null && !cancelToken.IsCancellationRequested) { var evt = await context.Host.WaitForEventAsync(cancelToken); Debug.Assert(evt != null); accumulated.Add(evt); matches = matchFinder(accumulated); } return(matches); }
public static async Task <JToken> RunAsync(StateMachine workflow, IStateMachineHost host, JObject?input = null, ObservableAction[]?targetActions = null, CancellationToken cancelToken = default) { workflow.CheckArgNull(nameof(workflow)); host.CheckArgNull(nameof(host)); StateMachineContext?context = null; Func <CancellationToken, Task <JToken> > runTask = async token => { context = new StateMachineContext(workflow, host, input, targetActions, token); await context.RecordObservableActionAsync(ObservableAction.EnterStateMachine); try { return(await RunAsync(context)); } finally { await context.RecordObservableActionAsync(ObservableAction.ExitStateMachine); } }; JToken output; if (workflow.Timeout != null) { using var localTimeoutCancelTokenSource = new CancellationTokenSource(); using var combined = CancellationTokenSource.CreateLinkedTokenSource( localTimeoutCancelTokenSource.Token, cancelToken); Task <JToken> timeoutTask = host.DelayAsync(workflow.Timeout.Duration, combined.Token) .ContinueWith(_ => { return((JToken)JValue.CreateNull()); }); Debug.Assert(timeoutTask != null); output = await Task.WhenAny(timeoutTask, runTask(combined.Token)).Unwrap(); if (!timeoutTask.IsCompleted) { localTimeoutCancelTokenSource.Cancel(); } else if (workflow.Timeout.Action != null) { Debug.Assert(context != null); await workflow.Timeout.Action.ExecuteAsync(context, context.Data); } } else { output = await runTask(cancelToken); } Debug.Assert(output != null); return(output); }