public TransitionFloat(float value, TransitionConfiguration config) { // initial value this.value = value; // initialize state this.startTime = Time.unscaledTime; this.finishTime = Time.unscaledTime; this.sourceValue = value; this.destinationValue = value; // assign properties this.duration = config.duration; this.bezier = config.bezier; this.delay = config.delay; }
public TransitionEvaluationContext(StateExecutionContext stateExecutionContext, TransitionConfiguration transitionConfiguration) { TransitionConfiguration = transitionConfiguration; StateExecutionContext = stateExecutionContext; }
public StateExecutionTransition(StateTypeConfiguration nextStateType, TransitionConfiguration transitionConfiguration) { NextStateType = nextStateType; TransitionConfiguration = transitionConfiguration; }
private async Task TransitionToState(WorkflowContext workflowContext, StateConfiguration stateConfiguration, TransitionConfiguration transitionConfiguration, CancellationToken cancellationToken = default) { // move to failed state with no additional checks in synchronous fashion if (stateConfiguration.Type == StateTypeConfiguration.Failed) { await ProcessState(workflowContext, stateConfiguration, cancellationToken).ConfigureAwait(false); return; } // in case if transition is trying to been done to the same state where workflow instance is ==> move to failed if (string.Equals(workflowContext.WorkflowInstance.CurrentStateCode, stateConfiguration.Code, StringComparison.OrdinalIgnoreCase)) { Log.Warning("{workflowInstanceId} is attempted to be moved to the same state as it is now [{state}], moving it to failed state", workflowContext.WorkflowInstance.Id, stateConfiguration.Code); await ProcessState(workflowContext, workflowContext.WorkflowConfiguration.GetFailedStateConfiguration(), cancellationToken).ConfigureAwait(false); return; } // if state has any event ==> move to awaiting event sub-state if (stateConfiguration.Events.Any()) { // [assumption] state cannot have both event and incoming async transition (this check is done during WF configuration loading) workflowContext.WorkflowInstance.CurrentStateCode = stateConfiguration.Code; workflowContext.WorkflowInstance.CurrentStateProgress = StateExecutionProgress.AwaitingEvent; await SaveWorkflowInstance(workflowContext.WorkflowInstance, cancellationToken).ConfigureAwait(false); Log.Debug("{workflowInstanceId} has been moved to [{state}::{subState}]", workflowContext.WorkflowInstance.Id, stateConfiguration.Code, StateExecutionProgress.AwaitingEvent); return; } // process synchronous / delayed transition if (transitionConfiguration.Type.In(TransitionTypeConfiguration.Synchronous, TransitionTypeConfiguration.AsynchronousWithDelay)) { // save current transition as a checkpoint workflowContext.WorkflowInstance.CurrentStateCode = stateConfiguration.Code; workflowContext.WorkflowInstance.CurrentStateProgress = StateExecutionProgress.Started; await SaveWorkflowInstance(workflowContext.WorkflowInstance, cancellationToken).ConfigureAwait(false); Log.Debug("{workflowInstanceId} is moved to [{state}::{subState}]", workflowContext.WorkflowInstance.Id, stateConfiguration.Code, StateExecutionProgress.Started); await ProcessState(workflowContext, stateConfiguration, cancellationToken).ConfigureAwait(false); return; } // process asynchronous-immediate transition if (transitionConfiguration.Type == TransitionTypeConfiguration.AsynchronousImmediate) { workflowContext.WorkflowInstance.CurrentStateCode = stateConfiguration.Code; workflowContext.WorkflowInstance.CurrentStateProgress = StateExecutionProgress.AwaitingAsyncTransition; await SaveWorkflowInstance(workflowContext.WorkflowInstance, cancellationToken).ConfigureAwait(false); var workflowMessage = new AsynchronousTransitionWorkflowMessage(workflowContext.WorkflowConfiguration.Id, workflowContext.WorkflowInstance.Id); await WorkflowEngineBuilder.WorkflowMessageTransportFactoryProvider .CreateMessageTransportFactory(workflowContext.WorkflowConfiguration.RuntimeConfiguration.EndpointConfiguration.Type) .CreateMessageTransport(workflowContext.WorkflowConfiguration.RuntimeConfiguration.EndpointConfiguration.Address) .Send(workflowContext.WorkflowConfiguration.RuntimeConfiguration.EndpointConfiguration, workflowMessage, cancellationToken) .ConfigureAwait(false); // TODO: make sure that asynchronous-immediate transition will not have race conditions with current workflow engine // ==> send them as delayed workflow messages with default delay interval (1 second) // var delay = TimeSpan.FromSeconds(1D); // var currentStateCode = workflowContext.WorkflowInstance.CurrentStateCode; // var workflowMessage = new DelayedTransitionWorkflowMessage(workflowContext.WorkflowConfiguration.Id, // workflowContext.WorkflowInstance.Id, delay, currentStateCode, transitionConfiguration.MoveToState); // await WorkflowEngineBuilder.WorkflowMessageTransportFactory // .CreateMessageTransport(workflowContext.WorkflowConfiguration.RuntimeConfiguration.EndpointConfiguration.Address) // .SendWithDelay(workflowContext.WorkflowConfiguration.RuntimeConfiguration.EndpointConfiguration, workflowMessage, cancellationToken).ConfigureAwait(false); Log.Debug("{workflowInstanceId} has been moved to [{state}::{subState}]", workflowContext.WorkflowInstance.Id, stateConfiguration.Code, StateExecutionProgress.AwaitingAsyncTransition); return; } throw new WorkflowException("Non synchronous/asynchronous-immediate transitions are not supported"); }
public async Task <TransitionEvaluationResult> Evaluate(StateExecutionContext stateExecutionContext, TransitionConfiguration transitionConfiguration, CancellationToken cancellationToken) { ITransition transition = null; if (!string.IsNullOrEmpty(transitionConfiguration.Code)) { transition = _workflowEngine.WorkflowEngineBuilder .TransitionFactory.CreateTransition(transitionConfiguration.Code); } if (null == transition && !string.IsNullOrEmpty(transitionConfiguration.ConditionScript) && transitionConfiguration.ConditionScriptType == ScriptTypeConfiguration.CSharp) { transition = new CSharpScriptTransition(stateExecutionContext.StateConfiguration.Code, transitionConfiguration.ConditionScriptType, transitionConfiguration.ConditionScript); } if (null == transition) { // there is neither code nor condition for transition // ==> assume transition always evaluates to true return(new TransitionEvaluationResult(TransitionEvaluationStatus.EvaluatedTrue)); } Log.Verbose("Starting evaluation of transition [{code}] [{condition}] for [{workflowInstanceId}]", transitionConfiguration.Code, transitionConfiguration.ConditionScriptType, stateExecutionContext.WorkflowContext.WorkflowInstance.Id); try { var transitionEvaluationContext = new TransitionEvaluationContext(stateExecutionContext, transitionConfiguration); var transitionEvaluationResult = await transition.Evaluate(transitionEvaluationContext, cancellationToken).ConfigureAwait(false); if (transitionEvaluationResult.Status == TransitionEvaluationStatus.EvaluationFailed) { throw new WorkflowException(string.Format(CultureInfo.InvariantCulture, "Evaluation of an transition [code={0}], [condition={1}] returns [EvaluationFailed] for workflow instance [{2:D}]", transitionConfiguration.Code, transitionConfiguration.ConditionScript, stateExecutionContext.WorkflowContext.WorkflowInstance.Id)); } return(transitionEvaluationResult); } catch (Exception ex) { throw new WorkflowException(string.Format(CultureInfo.InvariantCulture, "An error has occurred during evaluation of an transition [code={0}], [condition={1}] for workflow instance [{2:D}]", transitionConfiguration.Code, transitionConfiguration.ConditionScript, stateExecutionContext.WorkflowContext.WorkflowInstance.Id), ex); } }