/**********************************************************************************/ private void AddNodeForExecution(Guid nodeId) { var node = _uow.PlanRepository.GetById <PlanNodeDO>(nodeId); string nodeName = "undefined"; if (node is ActivityDO) { var activityTemplate = _uow.ActivityTemplateRepository.GetByKey(((ActivityDO)node).ActivityTemplateId); nodeName = "Activity: " + activityTemplate.Name + " Version:" + activityTemplate.Version; } if (node is SubplanDO) { nodeName = "Subplan: " + ((SubplanDO)node).Name; } var frame = new OperationalStateCM.StackFrame { NodeId = nodeId, NodeName = nodeName, LocalData = _bypassData }; _callStack.PushFrame(frame); _bypassData = null; }
/**********************************************************************************/ private bool ProcessActivityResponse(ActivityResponseDTO activityResponse, OperationalStateCM.ActivityExecutionPhase activityExecutionPhase, OperationalStateCM.StackFrame topFrame) { ActivityResponse opCode; if (activityResponse == null) { return(true); } if (!Enum.TryParse(activityResponse.Type, out opCode)) { return(true); } PlanNodeDO currentNode; PlanNodeDO targetNode; Guid id; switch (opCode) { case ActivityResponse.Error: var currentActivity = _uow.PlanRepository.GetById <ActivityDO>(topFrame.NodeId); ErrorDTO error = activityResponse.TryParseErrorDTO(out error) ? error : null; ActivityErrorCode errorCode; if (Enum.TryParse(error?.ErrorCode, out errorCode) && errorCode == ActivityErrorCode.AUTH_TOKEN_NOT_PROVIDED_OR_INVALID) { throw new InvalidTokenRuntimeException(Mapper.Map <ActivityDO, ActivityDTO>(currentActivity), Mapper.Map <ContainerDO, ContainerDTO>(_container), error?.Message ?? string.Empty); } throw new ErrorResponseException(Mapper.Map <ContainerDO, ContainerDTO>(_container), error?.Message); case ActivityResponse.ExecuteClientActivity: break; case ActivityResponse.LaunchAdditionalPlan: LoadAndRunPlan(ExtractGuidParameter(activityResponse)); break; case ActivityResponse.RequestTerminate: _callStack.Clear(); EventManager.ProcessingTerminatedPerActivityResponse(_container, ActivityResponse.RequestTerminate); return(false); case ActivityResponse.RequestSuspend: _container.State = State.Suspended; if (activityExecutionPhase == OperationalStateCM.ActivityExecutionPhase.ProcessingChildren) { _callStack.PushFrame(topFrame); } else { // reset state of currently executed activity topFrame.CurrentActivityExecutionPhase = OperationalStateCM.ActivityExecutionPhase.WasNotExecuted; } return(false); case ActivityResponse.SkipChildren: if (activityExecutionPhase == OperationalStateCM.ActivityExecutionPhase.WasNotExecuted) { _callStack.RemoveTopFrame(); } break; case ActivityResponse.JumpToSubplan: id = ExtractGuidParameter(activityResponse); targetNode = _uow.PlanRepository.GetById <PlanNodeDO>(id); if (targetNode == null) { throw new InvalidOperationException($"Unable to find node {id}"); } // @alexavrutin here: commented this block since this check broke Make a Decision in Kiosk mode // when a new plan is being created. // currentNode = _uow.PlanRepository.GetById<PlanNodeDO>(topFrame.NodeId); //if (currentNode.RootPlanNodeId != targetNode.RootPlanNodeId) //{ // throw new InvalidOperationException("Can't jump to the subplan from different plan. Instead, use Jump to Plan."); //} _callStack.Clear(); AddNodeForExecution(id); break; case ActivityResponse.JumpToActivity: id = ExtractGuidParameter(activityResponse); targetNode = _uow.PlanRepository.GetById <PlanNodeDO>(id); if (targetNode == null) { throw new InvalidOperationException($"Unable to find node {id}"); } currentNode = _uow.PlanRepository.GetById <PlanNodeDO>(topFrame.NodeId); if (currentNode.RootPlanNodeId != targetNode.RootPlanNodeId) { throw new InvalidOperationException("Can't jump to the activity from different plan. Instead, use Jump to Plan."); } if (targetNode.ParentPlanNodeId == null && currentNode.ParentPlanNodeId == null && currentNode.Id != targetNode.Id) { throw new InvalidOperationException("Can't jump from the activities that has no parent to anywhere except the activity itself."); } if (targetNode.ParentPlanNodeId != currentNode.ParentPlanNodeId) { throw new InvalidOperationException("Can't jump to activity that has parent different from activity we are jumping from."); } // we are jumping after activity's Run if (activityExecutionPhase == OperationalStateCM.ActivityExecutionPhase.WasNotExecuted) { // remove the frame representing the current activity from stack. _callStack.RemoveTopFrame(); } if (id == topFrame.NodeId) { // we want to pass current local data (from the topFrame) to the next activity we are calling. _bypassData = topFrame.LocalData; } // this is root node. Just push new frame if (_callStack.Count == 0 || currentNode.ParentPlanNode == null) { AddNodeForExecution(id); } else { // find activity that is preceeding the one we are jumping to. // so the next iteration of run cycle will exectute the activity we are jumping to var prevToJump = currentNode.ParentPlanNode.ChildNodes.OrderByDescending(x => x.Ordering).FirstOrDefault(x => x.Ordering < targetNode.Ordering); _callStack.TopFrame.CurrentChildId = prevToJump?.Id; } break; case ActivityResponse.CallAndReturn: id = ExtractGuidParameter(activityResponse); targetNode = _uow.PlanRepository.GetById <PlanNodeDO>(id); if (targetNode == null) { throw new InvalidOperationException($"Unable to find node {id}"); } currentNode = _uow.PlanRepository.GetById <PlanNodeDO>(topFrame.NodeId); if (currentNode.RootPlanNodeId != targetNode.RootPlanNodeId) { throw new InvalidOperationException("Can't call the activity from different plan. Instead, use Jump to Plan."); } AddNodeForExecution(id); break; case ActivityResponse.Break: if (activityExecutionPhase == OperationalStateCM.ActivityExecutionPhase.WasNotExecuted) { // we wan't to have an exception in case of the corrupted stack, so we don't merge this with check below _callStack.RemoveTopFrame(); } if (_callStack.Count > 0) { _callStack.RemoveTopFrame(); } break; } return(true); }