async void _finish(XFlowState state) { if (!_persistOnComplete) { if (_state.Contains(state)) { _state.Remove(state); } await _save(); return; } state.IsComplete = true; Debug.WriteLine("Completed State Flow with {0}: {1}", state.PreviousStageSuccess, state); if (state.PreviousStageSuccess) { state.State = XFlowStates.Success; state.IsSuccessful = true; state.Timestamp = DateTime.UtcNow; await _save(); return; } state.State = XFlowStates.Fail; state.IsSuccessful = false; state.Timestamp = DateTime.UtcNow; await _save(); }
bool _moveNextStage(XFlowState state) { if (state.StageId == null) { var firstStage = _stages.First(); state.StageId = firstStage.StageId; return(true); } var stage = _getStage(state); var index = _stages.IndexOf(stage); var newIndex = index + 1; if (newIndex >= _stages.Count) { //this is the last stage, so return false; return(false); } var newStage = _stages[newIndex]; state.StageId = newStage.StageId; state.FailureCount = 0; return(true); }
async Task _failResult(XFlowState state) { state.PreviousStageSuccess = false; var stage = _getStage(state); state.Text = stage.FailText; if (stage.Retries > 0) { if (state.FailureCount <= stage.Retries) { //let's retry state.FailureCount++; state.State = XFlowStates.WaitingForRetry; state.Timestamp = DateTime.UtcNow; await _save(); _cancelProcessWait(); return; } } await stage.Fail(state.ItemId); Debug.WriteLine("Stage Failed [{0}] {1} - {2}", state.ItemId, state.PreviousStageResult != null ? state.PreviousStageResult.ExtraText : "No Extra Text", state.PreviousStageResult != null ? state.PreviousStageResult.Exception : "No Exception"); Debug.WriteLine("Failure stage: {0}", stage.ProcessingText); _finish(state); }
public async Task <XFlowState> Start(Guid id) { if (!IsComplete) { throw new InvalidOperationException("Cannot start flows until Complete() is called"); } var flowState = new XFlowState { Id = Guid.NewGuid(), ItemId = id, State = XFlowStates.WaitingForNextStage, StageId = null, Timestamp = DateTime.UtcNow, ParentFlow = this }; using (var l = await _stateLock.LockAsync()) { var existing = _state.FirstOrDefault(_ => _.ItemId == id); if (existing != null) { //if it's not failed or completed then clear it and start gain. if (existing.State != XFlowStates.Fail && existing.State != XFlowStates.Success) { return(null); } _state.Remove(existing); } _state.Add(flowState); } await _save(); _cancelProcessWait(); return(flowState); }
async Task _successResult(XFlowState state) { //state.Text = ""; state.PreviousStageSuccess = true; if (state.PreviousStageResult != null && state.PreviousStageResult.CompleteNow) { //the result has asked for the workflow to complete early //this could be because the rest isn't needed, e.g. an entity was detected //locally so there is no need to get it form the server. _finish(state); return; } state.State = XFlowStates.WaitingForNextStage; state.Timestamp = DateTime.UtcNow; await _save(); _cancelProcessWait(); }
XStage _getStage(XFlowState state) { var stage = _stages.First(_ => _.StageId == state.StageId); return(stage); }
Task _runStage(XFlowState state) { if (state.State != XFlowStates.WaitingForNetwork && state.State != XFlowStates.WaitingForRetry && state.State != XFlowStates.WaitingToStart) { return(null); } if (_inProgressStates.Contains(state)) { return(null); } var t = Task.Run(async() => { var stage = _getStage(state); if (stage.RequiresNetwork && !_networkStatus.QuickNetworkCheck()) { state.State = XFlowStates.WaitingForNetwork; await Task.Yield(); return; } state.Text = stage.ProcessingText; state.State = XFlowStates.InProgress; state.Timestamp = DateTime.UtcNow; if (stage.IsDisconnectedProcess) { state.State = XFlowStates.DisconnectedProcessing; await _save(); Debug.WriteLine("Stopping flow for disconnected state, will resume when asked. {0}", state); return; } await _save(); XStageResult failResult = null; try { if (_inProgressStates.Contains(state)) { return; } _inProgressStates.Add(state); var result = await stage.Function(state.ItemId); if (result.Id != Guid.Empty) { state.ItemId = result.Id; } state.PreviousStageResult = result; _inProgressStates.Remove(state); if (!result.IsSuccess) { await _failResult(state); } else { await _successResult(state); } } catch (Exception ex) { _inProgressStates.Remove(state); failResult = new XStageResult(false, state.ItemId, null, exception: ex.ToString()); } if (failResult != null) { state.PreviousStageResult = failResult; var stateString = _entitySerialiser.Serialise(state); Debug.WriteLine("Caught exception process: {0}", stateString); await _failResult(state); } _cancelProcessWait(); }); return(t); }
bool _moveNextStage(XFlowState state) { if (state.StageId == null) { var firstStage = _stages.First(); state.StageId = firstStage.StageId; return true; } var stage = _getStage(state); var index = _stages.IndexOf(stage); var newIndex = index + 1; if (newIndex >= _stages.Count) { //this is the last stage, so return false; return false; } var newStage = _stages[newIndex]; state.StageId = newStage.StageId; state.FailureCount = 0; return true; }
XStage _getStage(XFlowState state) { var stage = _stages.First(_ => _.StageId == state.StageId); return stage; }
async void _finish(XFlowState state) { if (!_persistOnComplete) { if (_state.Contains(state)) { _state.Remove(state); } await _save(); return; } state.IsComplete = true; Debug.WriteLine("Completed State Flow with {0}: {1}", state.PreviousStageSuccess, state); if (state.PreviousStageSuccess) { state.State = XFlowStates.Success; state.IsSuccessful = true; state.Timestamp = DateTime.UtcNow; await _save(); return; } state.State = XFlowStates.Fail; state.IsSuccessful = false; state.Timestamp = DateTime.UtcNow; await _save(); }
async Task _successResult(XFlowState state) { //state.Text = ""; state.PreviousStageSuccess = true; if (state.PreviousStageResult != null && state.PreviousStageResult.CompleteNow) { //the result has asked for the workflow to complete early //this could be because the rest isn't needed, e.g. an entity was detected //locally so there is no need to get it form the server. _finish(state); return; } state.State = XFlowStates.WaitingForNextStage; state.Timestamp = DateTime.UtcNow; await _save(); _cancelProcessWait(); }
async Task _failResult(XFlowState state) { state.PreviousStageSuccess = false; var stage = _getStage(state); state.Text = stage.FailText; if (stage.Retries > 0) { if (state.FailureCount <= stage.Retries) { //let's retry state.FailureCount++; state.State = XFlowStates.WaitingForRetry; state.Timestamp = DateTime.UtcNow; await _save(); _cancelProcessWait(); return; } } await stage.Fail(state.ItemId); Debug.WriteLine("Stage Failed [{0}] {1} - {2}", state.ItemId, state.PreviousStageResult != null ? state.PreviousStageResult.ExtraText : "No Extra Text", state.PreviousStageResult != null ? state.PreviousStageResult.Exception : "No Exception"); Debug.WriteLine("Failure stage: {0}", stage.ProcessingText); _finish(state); }
Task _runStage(XFlowState state) { if (state.State != XFlowStates.WaitingForNetwork && state.State != XFlowStates.WaitingForRetry && state.State != XFlowStates.WaitingToStart) { return null; } if (_inProgressStates.Contains(state)) { return null; } var t = Task.Run(async () => { var stage = _getStage(state); if (stage.RequiresNetwork && !_networkStatus.QuickNetworkCheck()) { state.State = XFlowStates.WaitingForNetwork; await Task.Yield(); return; } state.Text = stage.ProcessingText; state.State = XFlowStates.InProgress; state.Timestamp = DateTime.UtcNow; if (stage.IsDisconnectedProcess) { state.State = XFlowStates.DisconnectedProcessing; await _save(); Debug.WriteLine("Stopping flow for disconnected state, will resume when asked. {0}", state); return; } await _save(); XStageResult failResult = null; try { if (_inProgressStates.Contains(state)) { return; } _inProgressStates.Add(state); var result = await stage.Function(state.ItemId); if (result.Id != Guid.Empty) { state.ItemId = result.Id; } state.PreviousStageResult = result; _inProgressStates.Remove(state); if (!result.IsSuccess) { await _failResult(state); } else { await _successResult(state); } } catch (Exception ex) { _inProgressStates.Remove(state); failResult = new XStageResult(false, state.ItemId, null, exception: ex.ToString()); } if (failResult != null) { state.PreviousStageResult = failResult; var stateString = _entitySerialiser.Serialise(state); Debug.WriteLine("Caught exception process: {0}", stateString); await _failResult(state); } _cancelProcessWait(); }); return t; }
public async Task<XFlowState> Start(Guid id) { if (!IsComplete) { throw new InvalidOperationException("Cannot start flows until Complete() is called"); } var flowState = new XFlowState { Id = Guid.NewGuid(), ItemId = id, State = XFlowStates.WaitingForNextStage, StageId = null, Timestamp = DateTime.UtcNow, ParentFlow = this }; using (var l = await _stateLock.LockAsync()) { var existing = _state.FirstOrDefault(_ => _.ItemId == id); if (existing != null) { //if it's not failed or completed then clear it and start gain. if (existing.State != XFlowStates.Fail && existing.State != XFlowStates.Success) { return null; } _state.Remove(existing); } _state.Add(flowState); } await _save(); _cancelProcessWait(); return flowState; }