package sushi.simulation;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import sushi.bpmn.element.AbstractBPMNElement;
import sushi.bpmn.element.BPMNAndGateway;
import sushi.bpmn.element.BPMNXORGateway;
import sushi.bpmn.monitoringpoint.MonitoringPoint;
import sushi.bpmn.monitoringpoint.MonitoringPointStateTransition;
import sushi.event.SushiEvent;
import sushi.event.SushiEventType;
import sushi.event.collection.SushiMapTree;
import sushi.eventhandling.Broker;
import sushi.util.SetUtil;
/**
* Represents one active path during the execution of a process
*/
public class PathSimulator {
private AbstractBPMNElement currentElement;
private InstanceSimulator instanceSimulator;
private Date currentSimulationDate;
private Boolean currentElementIsTraversed;
private List<SushiEvent> newEvents;
public PathSimulator(AbstractBPMNElement startElement, InstanceSimulator parentSimulator, Date currentSimulationDate){
this.setCurrentElement(startElement);
this.setInstanceSimulator(parentSimulator);
this.setCurrentSimulationDate(currentSimulationDate);
this.setCurrentElementIsTraversed(false);
this.newEvents = new ArrayList<SushiEvent>();
}
/**
* Traverses the current element or if it is allready traversed continues to the next - produces events
*/
public List<SushiEvent> continueSimulation() {
newEvents.clear();
if(currentElementIsTraversed){
addEventFromMonitorinPointTerminate();
AbstractBPMNElement nextElement = getNextElement();
if(getCurrentElement() instanceof BPMNXORGateway){
skipUnreachableElements(nextElement);
}
setCurrentElement(nextElement);
setCurrentElementIsTraversed(false);
if(getCurrentElement() == null){
getInstanceSimulator().unsubscribe(this);
}
else{
//Terminate eines Elements bewirkt u.U. Enable des nächsten
addEventFromMonitorinPointEnable();
}
}
else{
addEventFromMonitorinPointBegin();
setCurrentSimulationDate(new Date(getCurrentSimulationDate().getTime() + getInstanceSimulator().getSimulator().getDurationForBPMNElement(getCurrentElement())));
setCurrentElementIsTraversed(true);
}
return newEvents;
}
/**
* skips unreachable elements after an XOR-Split by marking all indirect predecessors of the XOR
* and demarking all indirect predecessors of any actualElement of a PathSimulator from the same instance
*/
private void skipUnreachableElements(AbstractBPMNElement nextElement) {
Set<AbstractBPMNElement> unreachableElements = getCurrentElement().getIndirectSuccessors();
if(nextElement != null){
unreachableElements.remove(nextElement);
setCurrentElement(nextElement);
for(PathSimulator pathSimulator : getInstanceSimulator().pathSimulators){
unreachableElements.removeAll(pathSimulator.getCurrentElement().getIndirectSuccessors());
}
}
skipElements(unreachableElements);
}
/**
* Returns true if there is exactly one path to go - creates new PathSimulators if 2 or more paths should be simulated at once
*/
private AbstractBPMNElement getNextElement() {
if(getCurrentElement().hasSuccessors()){
//Bei mehreren ausgehenden Kanten...
if(getCurrentElement().getSuccessors().size() > 1){
//Beim XOR-Split eine Kante verfolgen
//TODO: deferred choice, OR, complex?
if(getCurrentElement() instanceof BPMNXORGateway){
return getInstanceSimulator().getSimulator().choosePath(getCurrentElement());
}
//ansonsten alle Kanten verfolgen
else{
PathSimulator pathSimulator;
for(AbstractBPMNElement successor : getCurrentElement().getSuccessors()){
//ein Simulator für jede Kante erzeugen
if(successor instanceof BPMNAndGateway){
getInstanceSimulator().addJoinPredecessorToGateway(getCurrentElement(), (BPMNAndGateway) successor);
if((getInstanceSimulator().allPredecessorsOfGatewayVisited((BPMNAndGateway) successor))){
getInstanceSimulator().resetGateway((BPMNAndGateway) successor);
pathSimulator = new PathSimulator(successor, getInstanceSimulator(), getCurrentSimulationDate());
getInstanceSimulator().pathSimulators.add(pathSimulator);
}
}
pathSimulator = new PathSimulator(successor, getInstanceSimulator(), getCurrentSimulationDate());
getInstanceSimulator().pathSimulators.add(pathSimulator);
}
}
}
//Nachfolger durchlaufen (eine ausgehende Kante)
else{
AbstractBPMNElement successor = getCurrentElement().getSuccessors().iterator().next();
if(successor instanceof BPMNAndGateway){
getInstanceSimulator().addJoinPredecessorToGateway(getCurrentElement(), (BPMNAndGateway) successor);
if((getInstanceSimulator().allPredecessorsOfGatewayVisited((BPMNAndGateway) successor))){
getInstanceSimulator().resetGateway((BPMNAndGateway) successor);
}
else{
return null;
}
}
return successor;
}
}
return null;
}
private InstanceSimulator getInstanceSimulator() {
return instanceSimulator;
}
private void setInstanceSimulator(InstanceSimulator instanceSimulator) {
this.instanceSimulator = instanceSimulator;
}
public Date getCurrentSimulationDate() {
return currentSimulationDate;
}
private void setCurrentSimulationDate(Date currentSimulationDate) {
this.currentSimulationDate = currentSimulationDate;
}
private void addEventFromMonitorinPointEnable() {
addEventFromMonitorinPoint(MonitoringPointStateTransition.enable);
}
private void addEventFromMonitorinPointBegin() {
addEventFromMonitorinPoint(MonitoringPointStateTransition.begin);
}
private void addEventFromMonitorinPointTerminate() {
addEventFromMonitorinPoint(MonitoringPointStateTransition.terminate);
}
private void addEventFromMonitorinPointSkip() {
addEventFromMonitorinPoint(MonitoringPointStateTransition.skip);
}
private void addEventFromMonitorinPoint(MonitoringPointStateTransition stateTransition) {
MonitoringPoint monitoringPoint = getCurrentElement().getMonitoringPointByStateTransitionType(stateTransition);
if(monitoringPoint != null){
newEvents.add(new SushiEvent(monitoringPoint.getEventType(), getCurrentSimulationDate()));
}
}
private Boolean getCurrentElementIsTraversed() {
return currentElementIsTraversed;
}
private void setCurrentElementIsTraversed(Boolean currentElementIsTraversed) {
this.currentElementIsTraversed = currentElementIsTraversed;
}
private void skipElements(Set<AbstractBPMNElement> unreachableElements) {
Iterator<AbstractBPMNElement> iterator = unreachableElements.iterator();
while(iterator.hasNext()){
setCurrentElement(iterator.next());
addEventFromMonitorinPointSkip();
}
}
public AbstractBPMNElement getCurrentElement() {
return currentElement;
}
private void setCurrentElement(AbstractBPMNElement currentElement) {
this.currentElement = currentElement;
}
}