AtomicModel.java
package nl.tudelft.simulation.dsol.formalisms.devs.esdevs;
import java.rmi.RemoteException;
import nl.tudelft.simulation.dsol.SimRuntimeException;
import nl.tudelft.simulation.dsol.formalisms.eventscheduling.SimEvent;
import nl.tudelft.simulation.dsol.logger.Cat;
import nl.tudelft.simulation.dsol.simtime.SimTime;
import nl.tudelft.simulation.dsol.simulators.DevsSimulatorInterface;
/**
* AtomicModel class. Implements the Classic Parallel DEVS Atomic Model with Ports cf Zeigler et al (2000), section 4.2.2. and
* section 4.3 (pp. 84 ff). The algorithms for parallel DEVS are explained in Chapters 6 and 7.
* <p>
* Copyright (c) 2009-2024 Delft University of Technology, Jaffalaan 5, 2628 BX Delft, the Netherlands. All rights reserved. See
* for project information <a href="https://simulation.tudelft.nl/" target="_blank"> https://simulation.tudelft.nl</a>. The DSOL
* project is distributed under a three-clause BSD-style license, which can be found at
* <a href="https://https://simulation.tudelft.nl/dsol/docs/latest/license.html" target="_blank">
* https://https://simulation.tudelft.nl/dsol/docs/latest/license.html</a>.
* </p>
* @author <a href="http://tudelft.nl/mseck">Mamadou Seck</a><br>
* @author <a href="http://tudelft.nl/averbraeck">Alexander Verbraeck</a><br>
* @param <T> the time type
*/
public abstract class AtomicModel<T extends Number & Comparable<T>> extends AbstractDevsPortModel<T>
{
/** the default serialVersionUId. */
private static final long serialVersionUID = 1L;
/** future Execution of the Internal Transition. */
private SimEvent<T> nextEvent;
/** remaining TimeAdvance. */
@SuppressWarnings("checkstyle:visibilitymodifier")
protected T sigma;
/** the current phase (if applicable). */
@SuppressWarnings("checkstyle:visibilitymodifier")
protected Phase phase = new Phase("");
/** the time of the previous event in this component. */
@SuppressWarnings("checkstyle:visibilitymodifier")
protected T timeLastEvent;
/** the time of the next scheduled event in this component. */
@SuppressWarnings("checkstyle:visibilitymodifier")
protected T timeNextEvent;
/** the time span since the last event. */
@SuppressWarnings("checkstyle:visibilitymodifier")
protected T elapsedTime;
/** the active input port that is currently processed in Parallel DEVS. */
@SuppressWarnings("checkstyle:visibilitymodifier")
protected InputPort<T, ?> activePort = null;
/** conflict handling static: first the internal event. */
public static final boolean INTERNAL_FIRST = true;
/** conflict handling static: first the external event. */
public static final boolean EXTERNAL_FIRST = false;
/** applied conflict handling strategy in this component. */
@SuppressWarnings("checkstyle:visibilitymodifier")
protected boolean conflictStrategy = AtomicModel.INTERNAL_FIRST;
/**
* conflict means that both an external event and an internal event happen at the same time; the strategy applied indicates
* what to do when this happens.
*/
@SuppressWarnings("checkstyle:visibilitymodifier")
protected boolean conflict = false;
// ///////////////////////////////////////////////////////////////////////////
// CONSTRUCTORS AND INITIALIZATION
// ///////////////////////////////////////////////////////////////////////////
/**
* Constructor for a stand-alone atomic model with explicit phases.
* @param modelName String; the name of this component
* @param simulator DevsSimulatorInterface<T>; the simulator to schedule on
* @param e T; initial elapsed time
* @param initphase Phase; the initial phase of the model
*/
public AtomicModel(final String modelName, final DevsSimulatorInterface<T> simulator, final T e, final Phase initphase)
{
this(modelName, simulator, e, initphase, AtomicModel.INTERNAL_FIRST);
}
/**
* Constructor for an atomic model within a coupled model with explicit phases.
* @param modelName String; the name of this component
* @param parentModel CoupledModel<T>; the coupled model this atomic model is part of
* @param e T; initial elapsed time
* @param initphase Phase; the initial phase of the model
*/
public AtomicModel(final String modelName, final CoupledModel<T> parentModel, final T e, final Phase initphase)
{
this(modelName, parentModel, e, initphase, AtomicModel.INTERNAL_FIRST);
}
/**
* @param modelName String; the name of this component
* @param parentModel CoupledModel<T>; the coupled model this atomic model is part of
*/
public AtomicModel(final String modelName, final CoupledModel<T> parentModel)
{
this(modelName, parentModel, SimTime.zero(parentModel.getSimulator().getSimulatorTime()), new Phase(""),
AtomicModel.INTERNAL_FIRST);
}
/**
* @param modelName String; the name of this component
* @param simulator DevsSimulatorInterface<T>; the simulator to schedule on
*/
public AtomicModel(final String modelName, final DevsSimulatorInterface<T> simulator)
{
this(modelName, simulator, SimTime.zero(simulator.getSimulatorTime()), new Phase(""), AtomicModel.INTERNAL_FIRST);
}
/**
* Constructor for a stand-alone atomic model with explicit phases and a conflict strategy.
* @param modelName String; the name of this component
* @param simulator DevsSimulatorInterface<T>; the simulator to schedule on
* @param e T; initial elapsed time
* @param initphase Phase; the initial phase of the model to use for explicit phase models
* @param conflictStrategy boolean; the conflict strategy to use when internal and external events take place at the same
* time
*/
public AtomicModel(final String modelName, final DevsSimulatorInterface<T> simulator, final T e, final Phase initphase,
final boolean conflictStrategy)
{
super(modelName, simulator, null);
this.elapsedTime = e;
this.timeLastEvent = SimTime.copy(simulator.getSimulatorTime());
this.phase = initphase;
this.conflictStrategy = conflictStrategy;
}
/**
* Constructor for an atomic model within a coupled model with explicit phases and a conflict strategy.
* @param modelName String; the name of this component
* @param parentModel CoupledModel<T>; the coupled model this atomic model is part of
* @param e T; initial elapsed time
* @param initphase Phase; the initial phase of the model to use for explicit phase models
* @param conflictStrategy boolean; the conflict strategy to use when internal and external events take place at the same
* time
*/
public AtomicModel(final String modelName, final CoupledModel<T> parentModel, final T e, final Phase initphase,
final boolean conflictStrategy)
{
super(modelName, parentModel.getSimulator(), parentModel);
this.elapsedTime = e;
this.phase = initphase;
this.timeLastEvent = SimTime.copy(parentModel.getSimulator().getSimulatorTime());
this.conflictStrategy = conflictStrategy;
// adding to the parent model's components' list
this.parentModel.addModelComponent(this);
}
/**
* Initialize the atomic model. Start the first internal event based on the time 'e'. See Zeigler's model definition for the
* definition of 'e'.
* @param e T; elapsed time since the last state transition
*/
public void initialize(final T e)
{
if (this.timeAdvance().doubleValue() != Double.POSITIVE_INFINITY)
{
try
{
this.nextEvent =
new SimEvent<T>(SimTime.minus(SimTime.plus(getSimulator().getSimulatorTime(), this.timeAdvance()), e),
this,"deltaInternalEventHandler", null);
this.timeLastEvent = this.getSimulator().getSimulatorTime();
this.simulator.scheduleEvent(this.nextEvent);
}
catch (SimRuntimeException exception)
{
this.simulator.getLogger().always().error(exception, "initialize");
}
}
else
{
this.nextEvent = null;
}
}
// ///////////////////////////////////////////////////////////////////////////
// BASIC ATOMIC MODEL FUNCTIONALITY
// ///////////////////////////////////////////////////////////////////////////
/**
* Return the elapsed time (e) since the last event.
* @param eventTime T; the time of the event for which we want to calculate the elapsed time.
* @return the elapsed time (e) since the last event.
* @throws RemoteException a remote exception occurred
*/
protected T elapsedTime(final T eventTime) throws RemoteException
{
return (SimTime.minus(eventTime, this.timeLastEvent));
}
/**
* Schedule the next event.
*/
private void schedule()
{
if (this.timeAdvance().doubleValue() != Double.POSITIVE_INFINITY && !this.conflict)
{
try
{
if (this.timeAdvance().doubleValue() != Double.POSITIVE_INFINITY)
{
this.nextEvent =
new SimEvent<T>((SimTime.plus(SimTime.minus(this.simulator.getSimulatorTime(), this.timeAdvance()),
this.elapsedTime)), this,"deltaInternalEventHandler", null);
this.timeLastEvent = this.simulator.getSimulatorTime();
this.simulator.getLogger().filter(Cat.DSOL).trace("schedule {}", this.nextEvent.toString());
this.simulator.scheduleEvent(this.nextEvent);
// this.simulator.setAuthorization(false);
}
}
catch (Exception e1)
{
this.simulator.getLogger().always().error(e1);
}
}
else
{
this.nextEvent = null;
}
}
/**
* This method handles an incoming external event. As part of its function, it calls the deltaExternal method that is
* defined in an extension of this class.
* @param e T; the elapsed time since the last state transition
* @param value Object; the value that is passed through the port, which triggered the external event
*/
protected void deltaExternalEventHandler(final T e, final Object value)
{
this.deltaExternal(e, value);
this.schedule();
this.fireUpdatedState();
}
/**
* @param e T; the elapsed time since the last state transition
* @param value Object; the value that is passed through the port, which triggered the external event
*/
protected void deltaConfluent(final T e, final Object value)
{
this.simulator.getLogger().filter(Cat.DSOL).debug("deltaConfluent: CONFLUENT");
if (this.conflictStrategy == AtomicModel.INTERNAL_FIRST)
{
this.deltaInternalEventHandler();
this.conflict = false;
this.deltaExternalEventHandler(SimTime.zero(getSimulator().getSimulatorTime()), value);
}
else
{
this.deltaExternalEventHandler(e, value);
this.conflict = false;
this.deltaInternalEventHandler();
}
}
/**
* This method handles an internal event. As part of its function, it calls the deltaInternal method that is defined in an
* extension of this class.
*/
protected void deltaInternalEventHandler()
{
this.lambda();
this.deltaInternal();
this.schedule();
this.fireUpdatedState();
}
/**
* {@inheritDoc}
*/
@Override
public void printModel(final String space)
{
System.out.println(space + "Atomicmodel: " + this.getClass().getName());
}
// ///////////////////////////////////////////////////////////////////////////
// GETTET AND SETTET METHODS
// ///////////////////////////////////////////////////////////////////////////
/**
* @return the next simulation event for this atomic model.
*/
public SimEvent<T> getNextEvent()
{
return this.nextEvent;
}
/**
* @return the timestamp of the last executed simulation event.
*/
public T getTimeLastEvent()
{
return this.timeLastEvent;
}
/**
* @return the timestamp of the simulation event to execute next.
*/
public T getTimeNextEvent()
{
return this.timeNextEvent;
}
/**
* @return if there is a conflict between an intenal event and an external event that take place at the same time.
*/
public boolean isConflict()
{
return this.conflict;
}
/**
* @param conflict boolean; indicate whether there is a conflict between an intenal event and an external event that take
* place at the same time.
*/
public void setConflict(final boolean conflict)
{
this.conflict = conflict;
}
// ///////////////////////////////////////////////////////////////////////////
// ABSTRACT METHODS TO BE DEFINED IN AN EXTENSION CLASS
// ///////////////////////////////////////////////////////////////////////////
/**
* the delta internal function that should be implemented by the extending class.
*/
protected abstract void deltaInternal();
/**
* The user defined deltaExternal method that is defined in an extension of this class.
* @param e T; the elapsed time since the last state transition
* @param value Object; the value that has been passed through the port
*/
protected abstract void deltaExternal(T e, Object value);
/**
* the lambda function that should be implemented by the extending class.
*/
protected abstract void lambda();
/**
* the time advance function that should be implemented by the extending class.
* @return the ta, which is the time advance from one state to the next.
*/
protected abstract T timeAdvance();
}