blob: c4f0e20dee160ea5de79b920c7e0a9ebaee0bf8f [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2010-2015, Andras Szabolcs Nagy, Abel Hegedus, Akos Horvath, Zoltan Ujhelyi and Daniel Varro
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-v20.html.
*
* SPDX-License-Identifier: EPL-2.0
*******************************************************************************/
package org.eclipse.viatra.dse.examples.bpmn.simulator;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import org.apache.log4j.Logger;
import org.eclipse.viatra.dse.examples.simplifiedbpmn.BaseElement;
import org.eclipse.viatra.dse.examples.simplifiedbpmn.EndEvent;
import org.eclipse.viatra.dse.examples.simplifiedbpmn.Gateway;
import org.eclipse.viatra.dse.examples.simplifiedbpmn.ParallelGateway;
import org.eclipse.viatra.dse.examples.simplifiedbpmn.ResourceInstance;
import org.eclipse.viatra.dse.examples.simplifiedbpmn.ResourceType;
import org.eclipse.viatra.dse.examples.simplifiedbpmn.ResourceTypeVariant;
import org.eclipse.viatra.dse.examples.simplifiedbpmn.SimplifiedBPMN;
import org.eclipse.viatra.dse.examples.simplifiedbpmn.SequenceFlow;
import org.eclipse.viatra.dse.examples.simplifiedbpmn.Task;
public class Simulator {
public static class Token {
public int startTime = 0;
public int endTime = 0;
public Task task;
}
public static class MultiToken extends Token {
public Token originalToken;
}
public static class ResourceInstanceData {
public int timeUsed = 0;
public int timeRemaining = 0;
public Token tokenProcessing;
public ResourceInstance resource;
public ResourceInstanceData(ResourceInstance resource) {
this.resource = resource;
}
}
// Configuration
private SimplifiedBPMN model;
private int numberOfTokens;
private int timeBetweenTokens;
// Inner use
private int currentTime = 0;
private int timeToNextToken;
private Random random = new Random();
private Map<ParallelGateway, Map<Token, Integer>> arrivedTokens = new HashMap<ParallelGateway, Map<Token, Integer>>();
private int tokensFinished = 0;
private Logger logger = Logger.getLogger(getClass().getSimpleName());
private Map<ResourceInstance,ResourceInstanceData> resourceDatas = new HashMap<ResourceInstance, Simulator.ResourceInstanceData>();
private Map<ResourceTypeVariant,List<Token>> tokensWaiting = new HashMap<ResourceTypeVariant, List<Token>>();
private List<Token> tokens = new ArrayList<Simulator.Token>();
public Simulator(SimplifiedBPMN model, int numberOfTokens, int timeBetweenTokens) {
this.model = model;
this.numberOfTokens = numberOfTokens;
this.timeBetweenTokens = timeBetweenTokens;
timeToNextToken = timeBetweenTokens;
for (ResourceType resourceType : model.getResourceTypes()) {
for (ResourceTypeVariant rtv : resourceType.getVariants()) {
tokensWaiting.put(rtv, new ArrayList<Simulator.Token>());
for (ResourceInstance resource : rtv.getInstances()) {
resourceDatas.put(resource, new ResourceInstanceData(resource));
}
}
}
for (ParallelGateway pGateways : model.getParallelGateways()) {
arrivedTokens.put(pGateways, new HashMap<Token, Integer>());
}
}
public boolean canSimulate() {
for (Task task : model.getTasks()) {
ResourceTypeVariant variant = task.getVariant();
if (variant == null || variant.getInstances().isEmpty()) {
return false;
}
}
return true;
}
public void simulate() {
newToken(model, currentTime);
startProcessingTokens();
boolean simulationEnded = false;
while (!simulationEnded) {
logger.debug("------- Next step -------");
simulateStep();
simulationEnded = checkIfSimulationEnded();
}
logger.debug("------- Ended -------");
}
private boolean checkIfSimulationEnded() {
if (tokens.size() == numberOfTokens && tokensFinished == tokens.size()) {
return true;
}
return false;
}
private void simulateStep() {
ResourceInstanceData resource = getFastestResource();
int timeInterval;
boolean newToken = false;
if (tokens.size() < numberOfTokens && (
(resource != null && timeToNextToken < resource.timeRemaining) ||
resource == null)) {
timeInterval = timeToNextToken;
timeToNextToken = this.timeBetweenTokens;
newToken = true;
}
else if (resource == null) {
throw new RuntimeException("The simulation should have stopped by now.");
} else {
timeInterval = resource.timeRemaining;
timeToNextToken -= timeInterval;
}
currentTime += timeInterval;
for (ResourceInstanceData resourceData : resourceDatas.values()) {
if (resourceData.tokenProcessing != null) {
resourceData.timeRemaining -= timeInterval;
resourceData.timeUsed += timeInterval;
if (resourceData.timeRemaining == 0) {
Token token = resourceData.tokenProcessing;
logger.debug(token.task.getName() + " finished processing a token.");
resourceData.tokenProcessing = null;
chooseTokenFlow(token.task, token);
}
}
}
if (newToken) {
newToken(model, currentTime);
}
startProcessingTokens();
}
private void startProcessingTokens() {
for (ResourceType resourceType : model.getResourceTypes()) {
for (ResourceTypeVariant rtv : resourceType.getVariants()) {
List<Token> t = tokensWaiting.get(rtv);
if (!t.isEmpty()) {
for (ResourceInstance resource : rtv.getInstances()) {
ResourceInstanceData resourceData = resourceDatas.get(resource);
if (resourceData.tokenProcessing == null && !t.isEmpty()) {
Token token = t.remove(0);
resourceData.tokenProcessing = token;
resourceData.timeRemaining = (int) Math.round(token.task.getExecutionTime() * rtv.getEfficiency());
logger.debug(token.task.getName() + " started processing a token.");
}
}
}
}
}
}
private BaseElement nextElement;
private void chooseTokenFlow(BaseElement element, Token token) {
chooseTokenFlow(element, token, false);
}
private void chooseTokenFlow(BaseElement element, Token token, boolean processElementItself) {
if (processElementItself) {
nextElement = element;
}
else {
if (element.getOutFlows().size() > 1) {
chooseFlowBasedOnPropablity(element, new IChoosenFlow() {
@Override
public void process(SequenceFlow flow) {
nextElement = flow.getTarget();
}
});
} else {
nextElement = element.getOutFlows().get(0).getTarget();
}
}
logger.debug("Chosen flow: " + nextElement.getName());
if (nextElement instanceof Task) {
Task targetTask = (Task) nextElement;
token.task = targetTask;
ResourceTypeVariant resourceTypeVariant = targetTask.getVariant();
tokensWaiting.get(resourceTypeVariant).add(token);
} else if (nextElement instanceof Gateway) {
chooseTokenFlow(nextElement, token);
} else if (nextElement instanceof ParallelGateway) {
ParallelGateway parallelGateway = (ParallelGateway) nextElement;
if (parallelGateway.isDiverging()) {
for (SequenceFlow flow : parallelGateway.getOutFlows()) {
MultiToken subToken = new MultiToken();
subToken.originalToken = token;
chooseTokenFlow(flow.getTarget(), subToken, true);
}
} else {
Map<Token, Integer> arrivedTokensMap = arrivedTokens.get(parallelGateway);
Token originalToken = ((MultiToken)token).originalToken;
Integer arrivedTokensNumber = arrivedTokensMap.get(originalToken);
if (arrivedTokensNumber == null) {
arrivedTokensNumber = Integer.valueOf(0);
}
if (arrivedTokensNumber == parallelGateway.getInFlows().size() - 1) {
chooseTokenFlow(parallelGateway, originalToken);
} else {
arrivedTokensMap.put(originalToken, arrivedTokensNumber + 1);
}
}
} else if (nextElement instanceof EndEvent) {
token.endTime = currentTime;
tokensFinished++;
logger.debug(tokensFinished + ". token finished at " + currentTime + " in "
+ (token.endTime - token.startTime));
}
}
private void chooseFlowBasedOnPropablity(BaseElement baseElement, IChoosenFlow choosenFlowImpl) {
int sum = 0;
for (SequenceFlow flow : baseElement.getOutFlows()) {
sum += flow.getPropability();
}
int rnd = random.nextInt(sum);
sum = 0;
for (SequenceFlow flow : baseElement.getOutFlows()) {
sum += flow.getPropability();
if (rnd < sum) {
choosenFlowImpl.process(flow);
return;
}
}
}
private ResourceInstanceData getFastestResource() {
int minTimeRemaining = Integer.MAX_VALUE;
ResourceInstanceData result = null;
for (ResourceInstanceData resourceData : resourceDatas.values()) {
if (resourceData.tokenProcessing != null && resourceData.timeRemaining < minTimeRemaining) {
result = resourceData;
minTimeRemaining = result.timeRemaining;
}
}
return result;
}
private void newToken(SimplifiedBPMN model, int currentTime) {
Token token = new Token();
token.startTime = currentTime;
tokens.add(token);
logger.debug("New token " + tokens.size());
BaseElement firstStartEvent = model.getStartEvents().get(0);
chooseTokenFlow(firstStartEvent, token);
}
public int getElapsedTime() {
return currentTime;
}
public List<Token> getTokens() {
return tokens;
}
public Map<ResourceInstance, ResourceInstanceData> getResourceDatas() {
return resourceDatas;
}
}