use of desmoj.core.simulator.TimeInstant in project scylla by bptlab.
the class BatchClusterStartEvent method eventRoutine.
@Override
public void eventRoutine(BatchCluster cluster) throws SuspendExecution {
BatchActivity activity = cluster.getBatchActivity();
int nodeId = activity.getNodeId();
List<TaskBeginEvent> parentalStartEvents = cluster.getParentalStartEvents();
// Schedule all task begin events of the process instance
for (TaskBeginEvent pse : parentalStartEvents) {
ProcessInstance pi = pse.getProcessInstance();
pse.schedule(pi);
}
// schedule subprocess start events for all process instances in parent
// processInstances and parentalStartEvents are ordered the same way
// Set the responsible process instance in the batch cluster, first one by default
cluster.setResponsibleProcessInstance(parentalStartEvents.get(0).getProcessInstance());
// Go through all process instances. If it's the first one, schedule it. If not, save it to be scheduled later on
for (int j = 0; j < parentalStartEvents.size(); j++) {
TaskBeginEvent startEvent = parentalStartEvents.get(j);
ProcessInstance responsibleProcessInstance = startEvent.getProcessInstance();
int processInstanceId = responsibleProcessInstance.getId();
boolean showInTrace = responsibleProcessInstance.traceIsOn();
SimulationModel model = (SimulationModel) responsibleProcessInstance.getModel();
String source = startEvent.getSource();
TimeInstant currentSimulationTime = cluster.presentTime();
ProcessSimulationComponents pSimComponentsOfSubprocess = cluster.getProcessSimulationComponents().getChildren().get(nodeId);
ProcessModel subprocess = pSimComponentsOfSubprocess.getProcessModel();
try {
Integer startNodeId = subprocess.getStartNode();
ProcessInstance subprocessInstance = new ProcessInstance(model, subprocess, processInstanceId, showInTrace);
subprocessInstance.setParent(responsibleProcessInstance);
ScyllaEvent subprocessEvent = new BPMNStartEvent(model, source, currentSimulationTime, pSimComponentsOfSubprocess, subprocessInstance, startNodeId);
System.out.println("Created BPMNStartEvent for PI " + subprocessInstance.getId() + " / " + responsibleProcessInstance.getId() + " in Batch Cluster");
if (j == 0) {
// If it is the first process instance, schedule it...
subprocessEvent.schedule(subprocessInstance);
cluster.setStartNodeId(startNodeId);
} else {
// ...if not, save them for later
cluster.addPIEvent(startNodeId, subprocessEvent, subprocessInstance);
}
} catch (NodeNotFoundException | MultipleStartNodesException | NoStartNodeException e) {
DebugLogger.log("Start node of process model " + subprocess.getId() + " not found.");
System.err.println(e.getMessage());
e.printStackTrace();
SimulationUtils.abort(model, responsibleProcessInstance, nodeId, traceIsOn());
return;
}
}
// move batch cluster from list of not started ones to running ones
BatchPluginUtils pluginInstance = BatchPluginUtils.getInstance();
pluginInstance.setClusterToRunning(cluster);
// next node and timespan to next event determined by responsible process instance
// tasks resources only assigned to responsible subprocess instance
// only responsible subprocess instance is simulated
// other subprocess instances of batch are covered in process logs
}
use of desmoj.core.simulator.TimeInstant in project scylla by bptlab.
the class BatchLogger method writeToLog.
public void writeToLog(SimulationModel model, String outputPathWithoutExtension) throws IOException {
StringBuffer sb = new StringBuffer();
BatchPluginUtils pluginInstance = BatchPluginUtils.getInstance();
Map<String, Map<Integer, List<BatchCluster>>> clusters = pluginInstance.getBatchClusters();
TimeUnit referenceTimeUnit = DateTimeUtils.getReferenceTimeUnit();
sb.append("BATCH REPORT" + System.lineSeparator());
sb.append("Reference unit: " + referenceTimeUnit + System.lineSeparator());
sb.append("Please note: Duration and cost of batch activity execution can be found in output file of statslogger plug-in." + System.lineSeparator());
sb.append(System.lineSeparator());
for (String processId : clusters.keySet()) {
sb.append("=== PROCESS " + processId + " ===" + System.lineSeparator());
Map<Integer, List<BatchCluster>> clustersOfProcess = clusters.get(processId);
for (Integer nodeId : clustersOfProcess.keySet()) {
sb.append("== Node " + nodeId + System.lineSeparator());
double waitingTimeMaximumAllActivities = -1;
List<Double> waitingTimeAveragesAllActivities = new ArrayList<Double>();
List<BatchCluster> bcs = clustersOfProcess.get(nodeId);
for (BatchCluster bc : bcs) {
sb.append(System.lineSeparator());
sb.append("-- " + bc.getName() + System.lineSeparator());
double startTimeInRefUnit = bc.getStartTime().getTimeAsDouble(referenceTimeUnit);
double maximumWaitingTime = startTimeInRefUnit - bc.getCreationTime().getTimeAsDouble(referenceTimeUnit);
if (maximumWaitingTime > waitingTimeMaximumAllActivities) {
waitingTimeMaximumAllActivities = maximumWaitingTime;
}
sb.append("Maximum waiting time: " + maximumWaitingTime + System.lineSeparator());
List<TimeInstant> entranceTimes = bc.getProcessInstanceEntranceTimes();
List<Double> waitingTimes = new ArrayList<Double>();
for (TimeInstant et : entranceTimes) {
waitingTimes.add(startTimeInRefUnit - et.getTimeAsDouble(referenceTimeUnit));
}
double avgWaitingTime = DateTimeUtils.mean(waitingTimes);
waitingTimeAveragesAllActivities.add(avgWaitingTime);
sb.append("Average waiting time: " + avgWaitingTime + System.lineSeparator());
List<ProcessInstance> processInstances = bc.getProcessInstances();
List<Integer> processInstanceIds = new ArrayList<Integer>();
for (ProcessInstance pi : processInstances) {
processInstanceIds.add(pi.getId());
}
sb.append("Process instances: " + processInstanceIds + " (" + processInstanceIds.size() + " process instance(s))" + System.lineSeparator());
}
sb.append(System.lineSeparator());
sb.append("Maximum waiting time of all activities: " + waitingTimeMaximumAllActivities + System.lineSeparator());
sb.append("Average waiting time of all activities: " + DateTimeUtils.mean(waitingTimeAveragesAllActivities) + System.lineSeparator());
}
sb.append(System.lineSeparator());
}
if (outputPathWithoutExtension == null) {
System.out.println(sb.toString());
} else {
String resourceUtilizationFileName = outputPathWithoutExtension + model.getGlobalConfiguration().getFileNameWithoutExtension() + "_batchactivitystats.txt";
PrintWriter writer = new PrintWriter(resourceUtilizationFileName, "UTF-8");
writer.println(sb.toString());
writer.close();
System.out.println("Wrote batch activity statistics to " + resourceUtilizationFileName);
}
}
use of desmoj.core.simulator.TimeInstant in project scylla by bptlab.
the class BoundaryEventPluginUtils method createTimerBoundaryEvents.
// I did not touch this for now. Hopefully could be deleted in future.
private void createTimerBoundaryEvents(SimulationModel model, BoundaryObject bo, double startOfInterval, double endOfInterval) throws ScyllaRuntimeException {
double beginTimeOfTask = bo.getBeginTimeOfTask();
ProcessSimulationComponents desmojObjects = bo.getDesmojObjects();
ProcessModel processModel = desmojObjects.getProcessModel();
Map<Integer, EventType> eventTypes = processModel.getEventTypes();
Map<Integer, Boolean> cancelActivities = processModel.getCancelActivities();
List<Integer> referenceToBoundaryEvents = bo.getReferenceToBoundaryEvents();
for (Integer nId : referenceToBoundaryEvents) {
boolean timerEventIsInterrupting = false;
EventType eventType = eventTypes.get(nId);
if (eventType == EventType.BOUNDARY) {
Map<EventDefinitionType, Map<String, String>> eventDefinitions = processModel.getEventDefinitions().get(nId);
Map<String, String> definitionAttributes = eventDefinitions.get(EventDefinitionType.TIMER);
if (definitionAttributes != null) {
// if boundary event is timer event
double timeUntilWhenTimerEventsAreCreated = bo.getTimeUntilWhenTimerEventsAreCreated();
if (definitionAttributes.get("timeDuration") != null) {
// ISO 8601 duration
String timeDuration = definitionAttributes.get("timeDuration");
if (beginTimeOfTask != timeUntilWhenTimerEventsAreCreated) {
// timer event has already been created once, skip
continue;
}
Duration javaDuration = Duration.parse(timeDuration);
double duration = javaDuration.get(ChronoUnit.SECONDS);
if (duration == 0) {
continue;
}
double timeToSchedule = beginTimeOfTask + duration;
if (timeToSchedule < endOfInterval) {
String displayName = processModel.getDisplayNames().get(nId);
if (displayName == null) {
displayName = processModel.getIdentifiers().get(nId);
}
String source = bo.getSource();
ProcessInstance processInstance = bo.getProcessInstance();
TimeInstant timeInstant = new TimeInstant(startOfInterval, TimeUnit.SECONDS);
BPMNIntermediateEvent event = new BPMNIntermediateEvent(model, source, timeInstant, desmojObjects, processInstance, nId);
bo.getBoundaryEventsToSchedule().computeIfAbsent(timeToSchedule, k -> new ArrayList<BPMNIntermediateEvent>());
bo.getBoundaryEventsToSchedule().get(timeToSchedule).add(event);
String message = "Schedule boundary timer event: " + displayName;
bo.getMessagesOfBoundaryEventsToSchedule().computeIfAbsent(timeToSchedule, k -> new ArrayList<String>());
bo.getMessagesOfBoundaryEventsToSchedule().get(timeToSchedule).add(message);
// timeUntilWhenTimerEventsAreCreated = timeToSchedule;
}
// TODO fix boundary
timeUntilWhenTimerEventsAreCreated = timeToSchedule;
} else if (definitionAttributes.get("timeCycle") != null) {
// ISO 8601 repeating time interval:
String timeCycle = definitionAttributes.get("timeCycle");
// Rn/[ISO 8601 duration] where n
// (optional) for number of
// recurrences
// ["Rn"], "[ISO 8601 duration]"]
String[] recurrencesAndDuration = timeCycle.split("/");
String recurrencesString = recurrencesAndDuration[0];
String timeDurationString = recurrencesAndDuration[1];
Integer recurrencesMax = null;
if (recurrencesString.length() > 1) {
recurrencesMax = Integer.parseInt(recurrencesString.substring(1, recurrencesString.length()));
timerEventIsInterrupting = cancelActivities.get(nId);
if (timerEventIsInterrupting) {
recurrencesMax = 1;
}
}
Duration javaDuration = Duration.parse(timeDurationString);
double duration = javaDuration.get(ChronoUnit.SECONDS);
if (duration == 0 || recurrencesMax != null && recurrencesMax == 0) {
continue;
}
double timeToSchedule = beginTimeOfTask;
int actualNumberOfOccurrences = 0;
boolean recurrencesMaxExceeded = false;
while (timeToSchedule <= timeUntilWhenTimerEventsAreCreated) {
timeToSchedule += duration;
actualNumberOfOccurrences++;
if (recurrencesMax != null && actualNumberOfOccurrences > recurrencesMax) {
recurrencesMaxExceeded = true;
break;
}
}
if (recurrencesMaxExceeded) {
continue;
}
while (timeToSchedule <= endOfInterval) {
// add as many timer events for scheduling as possible (lots of them if timer event is
// non-interrupting,
// only one if it is interrupting
String displayName = processModel.getDisplayNames().get(nId);
if (displayName == null) {
displayName = processModel.getIdentifiers().get(nId);
}
String source = bo.getSource();
ProcessInstance processInstance = bo.getProcessInstance();
TimeInstant timeInstant = new TimeInstant(startOfInterval, TimeUnit.SECONDS);
BPMNIntermediateEvent event = new BPMNIntermediateEvent(model, source, timeInstant, desmojObjects, processInstance, nId);
bo.getBoundaryEventsToSchedule().computeIfAbsent(timeToSchedule, k -> new ArrayList<BPMNIntermediateEvent>());
bo.getBoundaryEventsToSchedule().get(timeToSchedule).add(event);
String message = "Schedule boundary timer event: " + displayName;
bo.getMessagesOfBoundaryEventsToSchedule().computeIfAbsent(timeToSchedule, k -> new ArrayList<String>());
bo.getMessagesOfBoundaryEventsToSchedule().get(timeToSchedule).add(message);
actualNumberOfOccurrences++;
if (recurrencesMax != null && actualNumberOfOccurrences == recurrencesMax) {
// recurrencesMaxExceeded = true;
break;
}
timeToSchedule += duration;
}
timeUntilWhenTimerEventsAreCreated = timeToSchedule;
} else {
// TODO support timeDate attributes?
String identifier = processModel.getIdentifiers().get(nId);
DebugLogger.log("Timer event " + identifier + " has no timer definition, skip.");
continue;
}
bo.setTimeUntilWhenTimerEventsAreCreated(timeUntilWhenTimerEventsAreCreated);
}
}
}
}
use of desmoj.core.simulator.TimeInstant in project scylla by bptlab.
the class BoundaryEventPluginUtils method createNonTimerBoundaryEvents.
private void createNonTimerBoundaryEvents(SimulationModel model, BoundaryObject bo, double startOfInterval, double endOfInterval) throws ScyllaRuntimeException {
double timeUntilWhenNonTimerEventsAreCreated = bo.getTimeUntilWhenNonTimerEventsAreCreated();
if (!bo.isGenerateMoreNonTimerBoundaryEvents() || timeUntilWhenNonTimerEventsAreCreated >= endOfInterval) {
return;
}
ProcessSimulationComponents desmojObjects = bo.getDesmojObjects();
ProcessModel processModel = desmojObjects.getProcessModel();
Map<Integer, EventType> eventTypes = processModel.getEventTypes();
Map<Integer, Boolean> cancelActivities = processModel.getCancelActivities();
int nodeId = bo.getNodeId();
while (timeUntilWhenNonTimerEventsAreCreated < endOfInterval) {
// If the parent task has not already ended...
// simulation configuration defines probability of firing boundary events
Map<Integer, Object> branchingDistributions = desmojObjects.getExtensionDistributions().get(PLUGIN_NAME);
@SuppressWarnings("unchecked") DiscreteDistEmpirical<Integer> distribution = (DiscreteDistEmpirical<Integer>) branchingDistributions.get(nodeId);
if (distribution == null) {
// There are no non-timer boundary events at this task...
bo.setGenerateMoreNonTimerBoundaryEvents(false);
return;
}
// decide on next node
model.skipTraceNote();
Integer nodeIdOfElementToSchedule = distribution.sample();
// System.out.println("Choosed: "+processModel.getIdentifiers().get(nodeIdOfElementToSchedule)+" "+processModel.getIdentifiers().get(nodeId));
if (nodeIdOfElementToSchedule == nodeId) {
// No next boundary non-timer event, finish
bo.setGenerateMoreNonTimerBoundaryEvents(false);
return;
} else {
// There are boundary events
EventType eventType = eventTypes.get(nodeIdOfElementToSchedule);
if (eventType == EventType.BOUNDARY) {
// Determine whether the boundary event to schedule is an interrupting one.
boolean eventIsInterrupting = cancelActivities.get(nodeIdOfElementToSchedule);
// Get time relative to the start of the task when this boundary event will trigger.
double relativeTimeToTrigger = desmojObjects.getDistributionSample(nodeIdOfElementToSchedule);
if (relativeTimeToTrigger == 0) {
// If this happens something is wrong anyways...
continue;
}
// Add the relative time of this boundary event, to determine when no more events are scheduled.
TimeUnit unit = desmojObjects.getDistributionTimeUnit(nodeIdOfElementToSchedule);
TimeSpan durationAsTimeSpan = new TimeSpan(relativeTimeToTrigger, unit);
timeUntilWhenNonTimerEventsAreCreated += durationAsTimeSpan.getTimeAsDouble(TimeUnit.SECONDS);
// Took this message sending part out. It was just to complicated for boundary events, fixed to their parent task.
// Furthermore it is not needed anymore.
/*String message = null;
boolean showInTrace = model.traceIsOn();
Map<EventDefinitionType, Map<String, String>> definitions = processModel.getEventDefinitions().get(nodeIdOfElementToSchedule);
String displayName = processModel.getDisplayNames().get(nodeIdOfElementToSchedule);
if (displayName == null) {
displayName = processModel.getIdentifiers().get(nodeIdOfElementToSchedule);
}
for (EventDefinitionType definition : definitions.keySet()) {
if (definition == EventDefinitionType.MESSAGE) {
message = "Schedule boundary message event: " + displayName;
}
else if (definition == EventDefinitionType.CONDITIONAL) {
message = "Schedule boundary conditional event: " + displayName;
}
else if (definition == EventDefinitionType.SIGNAL) {
message = "Schedule boundary signal event: " + displayName;
}
else if (definition == EventDefinitionType.ESCALATION) {
message = "Schedule boundary escalation event: " + displayName;
}
else {
if (eventIsInterrupting) {
if (definition == EventDefinitionType.ERROR) {
message = "Schedule boundary error event: " + displayName;
}
else if (definition == EventDefinitionType.COMPENSATION) {
message = "Schedule boundary compensation event: " + displayName;
}
else if (definition == EventDefinitionType.CANCEL) {
message = "Schedule boundary cancel event: " + displayName;
}
}
else {
SimulationUtils.sendElementNotSupportedTraceNote(model, processModel, displayName,
nodeIdOfElementToSchedule);
SimulationUtils.abort(model, processInstance, nodeId, showInTrace);
String identifier = processModel.getIdentifiers().get(nodeIdOfElementToSchedule);
throw new ScyllaRuntimeException("BPMNEvent " + identifier + " not supported.");
}
}*
bo.getMessagesOfBoundaryEventsToSchedule().computeIfAbsent(timeUntilWhenNonTimerEventsAreCreated,
k -> new ArrayList<String>());
bo.getMessagesOfBoundaryEventsToSchedule().get(timeUntilWhenNonTimerEventsAreCreated)
.add(message);
}
*/
String source = bo.getSource();
ProcessInstance processInstance = bo.getProcessInstance();
TimeInstant timeInstant = new TimeInstant(startOfInterval, TimeUnit.SECONDS);
// And create the event with the time it should trigger.
BPMNIntermediateEvent event = new BPMNIntermediateEvent(model, source, timeInstant, desmojObjects, processInstance, nodeIdOfElementToSchedule);
bo.getBoundaryEventsToSchedule().computeIfAbsent(timeUntilWhenNonTimerEventsAreCreated, k -> new ArrayList<BPMNIntermediateEvent>());
bo.getBoundaryEventsToSchedule().get(timeUntilWhenNonTimerEventsAreCreated).add(event);
if (eventIsInterrupting) {
// If the element is interrupting, finish and clean up
// boundaryObjects.values().remove(bo);
bo.setGenerateMoreNonTimerBoundaryEvents(false);
break;
}
}
}
}
bo.setTimeUntilWhenNonTimerEventsAreCreated(timeUntilWhenNonTimerEventsAreCreated);
}
use of desmoj.core.simulator.TimeInstant in project scylla by bptlab.
the class QueueManager method isAnyEventScheduledOrQueued.
// public static boolean areResourcesAvailable(SimulationModel model, Set<ResourceReference> resourceReferences,
// String displayName) {
// Map<String, ResourceQueue> resourceObjects = model.getResourceObjects();
// for (ResourceReference resourceRef : resourceReferences) {
// String resourceId = resourceRef.getResourceId();
// int amount = resourceRef.getAmount();
// ResourceQueue queue = resourceObjects.get(resourceId);
// if (queue.size() < amount) {
// model.sendTraceNote("Not enough resources of type " + resourceId + " available, task " + displayName
// + " is put in a queue.");
// return true;
// }
// }
// return false;
// }
/**
* Checks whether any event is scheduled or queued (if not, simulation may terminate).
*
* @param model
* the simulation model
* @return true if a event is either scheduled or queued
*/
public static boolean isAnyEventScheduledOrQueued(SimulationModel model) {
List<Entity> entities = model.getEntities(false);
for (Entity entity : entities) {
TimeInstant timeInstant = entity.scheduledNext();
if (timeInstant != null) {
return true;
}
}
Collection<ScyllaEventQueue> eventQueues = model.getEventQueues().values();
for (ScyllaEventQueue queue : eventQueues) {
if (!queue.isEmpty()) {
return true;
}
}
return false;
}
Aggregations