PureEdgeSim/com/mechalikh/pureedgesim/simulationengine/PureEdgeSim.java
/**
* PureEdgeSim: A Simulation Framework for Performance Evaluation of Cloud, Edge and Mist Computing Environments
*
* This file is part of PureEdgeSim Project.
*
* PureEdgeSim is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* PureEdgeSim is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with PureEdgeSim. If not, see <http://www.gnu.org/licenses/>.
*
* @author Charafeddine Mechalikh
**/
package com.mechalikh.pureedgesim.simulationengine;
import java.util.ArrayList;
import java.util.List;
import com.mechalikh.pureedgesim.simulationmanager.DefaultSimulationManager;
/**
* The {@code PureEdgeSim} class represents the discrete event simulation (DES)
* engine. It manages all the events and the simulation entities.
* <p>
* An instance of this class is created by the
* {@link com.mechalikh.pureedgesim.simulationmanager.SimulationThread
* SimulationThread} in the
* {@link com.mechalikh.pureedgesim.simulationmanager.SimulationThread#startSimulation()
* startSimulation()} method. But it is not started until the
* {@link com.mechalikh.pureedgesim.simulationmanager.DefaultSimulationManager#startSimulation()
* startSimulation()} method of the
* {@link com.mechalikh.pureedgesim.simulationmanager.DefaultSimulationManager
* SimulationManager} is called.
* <p>
* Once the PureEdgeSim simulation engine has started, it notifies all
* simulation entities of the start of the simulation in order to schedule their
* first event. This is guaranteed by the {@link OnSimulationStartListener#onSimulationStart()} method.
*
* @see com.mechalikh.pureedgesim.simulationengine.PureEdgeSim#start()
* @see com.mechalikh.pureedgesim.simulationmanager.SimulationThread#startSimulation()
* @see OnSimulationStartListener.onSimulationStart()
* @see com.mechalikh.pureedgesim.simulationmanager.DefaultSimulationManager#startSimulation()
* @see com.mechalikh.pureedgesim.simulationmanager.DefaultSimulationManager#onSimulationStart()
*
* @author Charafeddine Mechalikh
* @since PureEdgeSim 5.0
*/
public class PureEdgeSim {
List<SimEntity> entitiesList = new ArrayList<>();
protected double time;
protected boolean isRunning = true;
protected FutureQueue<Event> events;
/**
* Gets the current simulation time in seconds.
*
* @return the simulation time in seconds.
*
* @see #clockInMinutes()
* @see #start()
*/
public double clock() {
return time;
}
/**
* Creates an instance of the PureEdgeSim discrete event simulation engine.
*
* @see PureEdgeSim
* @see #start()
*/
public PureEdgeSim() {
events = new FutureQueue<>();
}
/**
* Starts the simulation. First, it notifies all simulation entities that implement the {@link OnSimulationStartListener} interface that the
* simulation has started by calling their {@link OnSimulationStartListener#onSimulationStart()
* onSimulationStart()} method. The entities will then schedule their first events.
* Once, all the entities has scheduled their fist events, a loop will go
* through all those events to process them one by one, and the simulation time
* is updated according to the time of last processed event. With each processed
* events new events will added to the queue by the those entities. The
* simulation will stop either if the event queue is empty, or when the user
* terminates it by calling the {@link PureEdgeSim#terminate() terminate()}
* method.
*
* @see com.mechalikh.pureedgesim.simulationmanager.Simulation#launchSimulation()
* @see com.mechalikh.pureedgesim.simulationmanager.SimulationThread#startSimulation()
* @see com.mechalikh.pureedgesim.simulationmanager.DefaultSimulationManager#startSimulation()
* @see OnSimulationStartListener.onSimulationStart()
* @see #terminate()
*/
public void start() {
// Notify all entities that the simulation has started.
entitiesList.forEach(entity -> {
if (entity instanceof OnSimulationStartListener) {
((OnSimulationStartListener) entity).onSimulationStart();
}
});
while (runClockTickAndProcessFutureEvents(Double.MAX_VALUE) && isRunning) {
// All the processing happens inside the method called above
}
// Iteration finished, notify all entities and clear their list
entitiesList.forEach(entity -> {
if (entity instanceof OnSimulationEndListener) {
((OnSimulationEndListener) entity).onSimulationEnd();
}
});
entitiesList.clear();
}
/**
* Processes future events as long as the simulation end time has not reached.
*
* @param until the time when simulation should be terminated.
* @return true if an event has been processed, false if the events queue empty,
* in this case the simulation will be terminated.
*
* @see #start()
* @see #processFutureEventsHappeningAtSameTimeOfTheFirstOne(Event)
*/
protected boolean runClockTickAndProcessFutureEvents(final double until) {
if (getEventsQueue().isEmpty()) {
return false;
}
final Event first = events.first();
if (first.getTime() <= until) {
processFutureEventsHappeningAtSameTimeOfTheFirstOne(first);
return true;
}
return false;
}
/**
* Processes events happening at the same time as the first event in the queue,
* and updates the simulation time.
*
* @param firstEvent the first event in the queue at this instant of the
* simulation.
* @see #start()
* @see #runClockTickAndProcessFutureEvents(double)
* @see #processEvent(Event)
*/
protected void processFutureEventsHappeningAtSameTimeOfTheFirstOne(final Event firstEvent) {
processEvent(firstEvent);
getEventsQueue().remove(firstEvent);
while (!getEventsQueue().isEmpty()) {
final Event evt = getEventsQueue().first();
if (evt.getTime() != firstEvent.getTime())
break;
processEvent(evt);
events.remove(evt);
}
}
/**
* Processes an event and updates the simulation time.
*
* @param event the event to process.
* @see #start()
* @see #runClockTickAndProcessFutureEvents(double)
* @see #processFutureEventsHappeningAtSameTimeOfTheFirstOne(Event)
*/
protected void processEvent(final Event event) {
if (event.getTime() < time) {
final String msg = "Past event detected. Event time: %.2f Simulation clock: %.2f";
throw new IllegalArgumentException(String.format(msg, event.getTime(), time));
}
time = event.getTime();
event.getSimEntity().processEvent(event);
}
/**
* Adds an event to the queue
*
* @param event the new event.
* @see SimEntity#schedule(SimEntity, Double, int)
* @see SimEntity#schedule(SimEntity, Double, int, Object)
* @see OnSimulationStartListener#onSimulationStart()
* @see FutureQueue
* @see #start()
* @see #runClockTickAndProcessFutureEvents(double)
* @see #processFutureEventsHappeningAtSameTimeOfTheFirstOne(Event)
*/
void insert(Event event) {
events.add(event);
}
/**
* Adds an event to the head of the queue
*
* @param event the new event.
* @see SimEntity#schedule(SimEntity, Double, int)
* @see SimEntity#schedule(SimEntity, Double, int, Object)
* @see OnSimulationStartListener#onSimulationStart()
* @see FutureQueue
* @see #start()
* @see #runClockTickAndProcessFutureEvents(double)
* @see #processFutureEventsHappeningAtSameTimeOfTheFirstOne(Event)
*/
public void insertFirst(Event event) {
getEventsQueue().addFirst(event);
}
/**
* Adds a simulation entity to the entities list. The simulation entities are
* added to this list before starting the simulation. When the simulation is
* started it notifies all of them of the beginning of the simulation in order
* to schedule their first events.
*
* @param simEntity the new simulation entity.
*
* @see com.mechalikh.pureedgesim.simulationmanager.SimulationThread#loadModels(DefaultSimulationManager
* simulationManager)
* @see #start()
* @see OnSimulationStartListener#onSimulationStart()
*/
public void addEntity(SimEntity simEntity) {
entitiesList.add(simEntity);
}
/**
* Terminates the simulation.
*
* @see #start()
* @see PureEdgeSim
*/
public void terminate() {
isRunning = false;
}
/**
* Gets the current simulation time in minutes.
*
* @return simulation time in minutes.
*
* @see #clock()
* @see #start()
*/
public int clockInMinutes() {
return (int) (time / 60);
}
/**
* Gets the list of generated events.
*
* @return events queue
*/
public FutureQueue<Event> getEventsQueue() {
return events;
}
}