CoupledModel.java
package nl.tudelft.simulation.dsol.formalisms.devs.esdevs;
import java.rmi.RemoteException;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import org.djutils.event.EventListener;
import org.djutils.event.EventType;
import org.djutils.event.reference.Reference;
import nl.tudelft.simulation.dsol.SimRuntimeException;
import nl.tudelft.simulation.dsol.formalisms.devs.esdevs.exceptions.PortNotFoundException;
import nl.tudelft.simulation.dsol.simulators.DevsSimulatorInterface;
/**
* CoupledModel class. This class implements the classic parallel DEVS coupled model with ports conform Zeigler et al. (2000),
* section 4.3.
* <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
* @since 1.5
*/
public abstract class CoupledModel<T extends Number & Comparable<T>> extends AbstractDevsPortModel<T>
{
/** the default serialVersionUId. */
private static final long serialVersionUID = 1L;
/** the internal couplings (from internal models to internal models). */
@SuppressWarnings("checkstyle:visibilitymodifier")
protected Set<InternalCoupling<T, ?>> internalCouplingSet = new LinkedHashSet<InternalCoupling<T, ?>>();
/** the couplings from the internal models to the output of this coupled model. */
@SuppressWarnings("checkstyle:visibilitymodifier")
protected Set<ExternalOutputCoupling<T, ?>> externalOutputCouplingSet = new LinkedHashSet<ExternalOutputCoupling<T, ?>>();
/** the couplings from the outside world to the internal models of this coupled model. */
@SuppressWarnings("checkstyle:visibilitymodifier")
protected Set<ExternalInputCoupling<T, ?>> externalInputCouplingSet = new LinkedHashSet<ExternalInputCoupling<T, ?>>();
/** the models within this coupled model. */
@SuppressWarnings("checkstyle:visibilitymodifier")
protected Set<AbstractDevsModel<T>> modelComponents = new LinkedHashSet<>();
// ///////////////////////////////////////////////////////////////////////////
// CONSTRUCTORS AND INITIALIZATION
// ///////////////////////////////////////////////////////////////////////////
/**
* The constructor of the top model when the simulator is still unknown (e.g. in the constructModel() method).
* @param modelName String; the name of this component
*/
public CoupledModel(final String modelName)
{
super(modelName, null, null);
}
/**
* The constructor of a coupled model within another coupled model.
* @param modelName String; the name of this component
* @param parentModel CoupledModel<T>; the parent coupled model for this model.
*/
public CoupledModel(final String modelName, final CoupledModel<T> parentModel)
{
super(modelName, parentModel.getSimulator(), parentModel);
if (this.parentModel != null)
{
this.parentModel.addModelComponent(this);
}
}
/**
* Constructor of a high-level coupled model without a parent model.
* @param modelName String; the name of this component
* @param simulator DevsSimulatorInterface<T>; the simulator to schedule events on.
*/
public CoupledModel(final String modelName, final DevsSimulatorInterface<T> simulator)
{
super(modelName, simulator, null);
}
/**
* Add a listener recursively to the model and all its submodels. Delegate it for this coupled model to the embedded event
* producer.
* @param eli EventListener; the event listener.
* @param et EventType; the event type.
* @return success or failure of adding the listener to all submodels.
*/
public boolean addHierarchicalListener(final EventListener eli, final EventType et)
{
boolean returnBoolean = true;
returnBoolean &= super.addListener(eli, et);
for (AbstractDevsModel<T> devsmodel : this.modelComponents)
{
returnBoolean &= devsmodel.addListener(eli, et);
}
return returnBoolean;
}
// ///////////////////////////////////////////////////////////////////////////
// TRANSFER FUNCTIONS
// ///////////////////////////////////////////////////////////////////////////
/**
* The transfer function takes care of transferring a value from this coupled model to the outside world.
* @param <TYPE> the type of message / event being transferred
* @param x OutputPortInterface<T,TYPE>; the output port through which the transfer takes place
* @param y TYPE; the value being transferred
* @throws RemoteException remote exception
* @throws SimRuntimeException simulation run time exception
*/
@SuppressWarnings("unchecked")
public <TYPE> void transfer(final OutputPortInterface<T, TYPE> x, final TYPE y) throws RemoteException, SimRuntimeException
{
for (InternalCoupling<T, ?> o : this.internalCouplingSet)
{
if (o.getFromPort() == x)
{
((InternalCoupling<T, TYPE>) o).getToPort().receive(y, this.simulator.getSimulatorTime());
}
}
for (ExternalOutputCoupling<T, ?> o : this.externalOutputCouplingSet)
{
if (o.getFromPort() == x)
{
((ExternalOutputCoupling<T, TYPE>) o).getToPort().send(y);
}
}
}
// ///////////////////////////////////////////////////////////////////////////
// COUPLING: MAKING AND REMOVING IC, EOC, EIC COUPLINGS
// ///////////////////////////////////////////////////////////////////////////
/**
* @param <TYPE> the type of message / event for which the coupling is added.
* @param fromPort OutputPortInterface<T,TYPE>; the output port of an internal component that transfers the
* message / event to another internal component (start of the coupling)
* @param toPort InputPortInterface<T,TYPE>; the input port of an internal component that receives a message /
* event from the other componet (end of the coupling)
*/
public <TYPE> void addInternalCoupling(final OutputPortInterface<T, TYPE> fromPort,
final InputPortInterface<T, TYPE> toPort)
{
try
{
this.internalCouplingSet.add(new InternalCoupling<T, TYPE>(fromPort, toPort));
}
catch (Exception e)
{
this.simulator.getLogger().always().error(e);
}
}
/**
* @param <TYPE> the type of message / event for which the coupling is removed.
* @param fromPort OutputPortInterface<T,TYPE>; the output port of an internal component that transfers the
* message / event to another internal component (start of the coupling)
* @param toPort InputPortInterface<T,TYPE>; the input port of an internal component that receives a message /
* event from the other componet (end of the coupling)
*/
public <TYPE> void removeInternalCoupling(final OutputPortInterface<T, TYPE> fromPort,
final InputPortInterface<T, TYPE> toPort)
{
for (InternalCoupling<T, ?> ic : this.internalCouplingSet)
{
if (ic.getFromPort().getModel() == fromPort && ic.getToPort().getModel() == toPort)
{
this.internalCouplingSet.remove(ic);
}
}
}
/**
* Add an IOC within this coupled model.
* @param <TYPE> the type of message / event for which the coupling is added.
* @param fromPort InputPortInterface<T,TYPE>; the input port of this coupled model that transfers the message /
* event to the internal component (start of the coupling)
* @param toPort InputPortInterface<T,TYPE>; the input port of the internal component that receives a message /
* event from the overarching coupled model (end of the coupling)
*/
public <TYPE> void addExternalInputCoupling(final InputPortInterface<T, TYPE> fromPort,
final InputPortInterface<T, TYPE> toPort)
{
try
{
this.externalInputCouplingSet.add(new ExternalInputCoupling<T, TYPE>(fromPort, toPort));
}
catch (Exception e)
{
this.simulator.getLogger().always().error(e);
}
}
/**
* Remove an IOC within this coupled model.
* @param <TYPE> the type of message / event for which the coupling is removed.
* @param fromPort InputPortInterface<T,TYPE>; the input port of this coupled model that transfers the message /
* event to the internal component (start of the coupling)
* @param toPort InputPortInterface<T,TYPE>; the input port of the internal component that receives a message /
* event from the overarching coupled model (end of the coupling)
*/
public <TYPE> void removeExternalInputCoupling(final InputPortInterface<T, TYPE> fromPort,
final InputPortInterface<T, TYPE> toPort)
{
for (ExternalInputCoupling<T, ?> eic : this.externalInputCouplingSet)
{
if (eic.getFromPort() == fromPort && eic.getToPort() == toPort)
{
this.externalInputCouplingSet.remove(eic);
}
}
}
/**
* Add an EOC within this coupled model.
* @param <TYPE> the type of message / event for which the coupling is added.
* @param fromPort OutputPortInterface<T,TYPE>; the output port of the internal component that produces an event
* for the outside of the overarching coupled model (start of the coupling)
* @param toPort OutputPortInterface<T,TYPE>; the output port of this coupled model that transfers the message /
* event to the outside (end of the coupling)
*/
public <TYPE> void addExternalOutputCoupling(final OutputPortInterface<T, TYPE> fromPort,
final OutputPortInterface<T, TYPE> toPort)
{
try
{
this.externalOutputCouplingSet.add(new ExternalOutputCoupling<T, TYPE>(fromPort, toPort));
}
catch (Exception e)
{
this.simulator.getLogger().always().error(e);
}
}
/**
* Remove an EOC within this coupled model.
* @param <TYPE> the type of message / event for which the coupling is removed.
* @param fromPort OutputPortInterface<T,TYPE>; the output port of the internal component that produces an event
* for the outside of the overarching coupled model (start of the coupling)
* @param toPort OutputPortInterface<T,TYPE>; the output port of this coupled model that transfers the message /
* event to the outside (end of the coupling)
*/
public <TYPE> void removeExternalOutputCoupling(final OutputPortInterface<T, TYPE> fromPort,
final OutputPortInterface<T, TYPE> toPort)
{
for (ExternalOutputCoupling<T, ?> eoc : this.externalOutputCouplingSet)
{
if (eoc.getFromPort() == fromPort && eoc.getToPort() == toPort)
{
this.externalOutputCouplingSet.remove(eoc);
}
}
}
// ///////////////////////////////////////////////////////////////////////////
// STRUCTURE: ADDING AND REMOVING COMPONENTS AND PORTS
// ///////////////////////////////////////////////////////////////////////////
/**
* Add a model component to this coupled model.
* @param model AbstractDevsModel<T>; the component to add.
*/
public void addModelComponent(final AbstractDevsModel<T> model)
{
this.modelComponents.add(model);
List<Reference<EventListener>> elis = getListenerReferences(AbstractDevsModel.STATE_UPDATE);
if (elis == null)
{
return;
}
for (Reference<EventListener> eli : elis)
{
model.addListener(eli.get(), AbstractDevsModel.STATE_UPDATE);
}
}
/**
* Remove a model component from a coupled model, including all its couplings (internal, external in, and external out).
* @param model AbstractDevsModel<T>; the component to remove.
*/
public void removeModelComponent(final AbstractDevsModel<T> model)
{
for (ExternalOutputCoupling<T, ?> eoc : this.externalOutputCouplingSet)
{
if (eoc.getFromPort().getModel() == model || eoc.getToPort().getModel() == model)
{
this.externalOutputCouplingSet.remove(eoc);
}
}
for (ExternalInputCoupling<T, ?> eic : this.externalInputCouplingSet)
{
if (eic.getFromPort().getModel() == model || eic.getToPort().getModel() == model)
{
this.externalInputCouplingSet.remove(eic);
}
}
for (InternalCoupling<T, ?> ic : this.internalCouplingSet)
{
if (ic.getFromPort().getModel() == model || ic.getToPort().getModel() == model)
{
this.internalCouplingSet.remove(ic);
}
}
// this will also take care of the removal of the ports as they are not
// connected to anything anymore.
this.modelComponents.remove(model);
}
/**
* {@inheritDoc}
*/
@Override
public void removeInputPort(final String name) throws PortNotFoundException
{
InputPortInterface<T, ?> inputPort = this.inputPortMap.get(name);
super.removeInputPort(name); // throws exception in case nonexistent
for (ExternalInputCoupling<T, ?> eic : this.externalInputCouplingSet)
{
if (eic.getFromPort() == inputPort || eic.getToPort() == inputPort)
{
this.externalInputCouplingSet.remove(eic);
}
}
for (InternalCoupling<T, ?> ic : this.internalCouplingSet)
{
if (ic.getToPort() == inputPort)
{
this.internalCouplingSet.remove(ic);
}
}
}
/**
* {@inheritDoc}
*/
@Override
public void removeOutputPort(final String name) throws PortNotFoundException
{
OutputPortInterface<T, ?> outputPort = this.outputPortMap.get(name);
super.removeOutputPort(name); // throws exception in case nonexistent
for (ExternalOutputCoupling<T, ?> eoc : this.externalOutputCouplingSet)
{
if (eoc.getFromPort() == outputPort || eoc.getToPort() == outputPort)
{
this.externalOutputCouplingSet.remove(eoc);
}
}
for (InternalCoupling<T, ?> ic : this.internalCouplingSet)
{
if (ic.getFromPort() == outputPort)
{
this.internalCouplingSet.remove(ic);
}
}
}
// ///////////////////////////////////////////////////////////////////////////
// GETTERS FOR THE STRUCTURE
// ///////////////////////////////////////////////////////////////////////////
/**
* @return internalCouplingSet; the internal couplings (from internal models to internal models)
*/
public Set<InternalCoupling<T, ?>> getInternalCouplingSet()
{
return this.internalCouplingSet;
}
/**
* @return externalOutputCouplingSet; the couplings from the internal models to the output of this coupled model
*/
public Set<ExternalOutputCoupling<T, ?>> getExternalOutputCouplingSet()
{
return this.externalOutputCouplingSet;
}
/**
* @return externalInputCouplingSet; the couplings from the outside world to the internal models of this coupled model
*/
public Set<ExternalInputCoupling<T, ?>> getExternalInputCouplingSet()
{
return this.externalInputCouplingSet;
}
/**
* @return modelComponents; the models within the coupled model
*/
public Set<AbstractDevsModel<T>> getModelComponents()
{
return this.modelComponents;
}
// ///////////////////////////////////////////////////////////////////////////
// PRINTING THE MODEL
// ///////////////////////////////////////////////////////////////////////////
/**
* {@inheritDoc}
*/
@Override
public void printModel(final String space)
{
System.out.println(space + "================");
System.out.println(space + "coupled model name: " + this.getClass().getName());
System.out.println(space + "Externaloutputcouplings");
for (ExternalOutputCoupling<T, ?> eoc : this.externalOutputCouplingSet)
{
System.out.print(space);
System.out.print("between ");
System.out.print(eoc.getFromPort().getModel().getClass().getName());
System.out.print(" and ");
System.out.print(eoc.getToPort().getModel().getClass().getName());
System.out.println();
}
System.out.println(space + "Externalinputcouplings");
for (ExternalInputCoupling<T, ?> eic : this.externalInputCouplingSet)
{
System.out.print(space);
System.out.print("between ");
System.out.print(eic.getFromPort().getModel().getClass().getName());
System.out.print(" and ");
System.out.print(eic.getToPort().getModel().getClass().getName());
System.out.println();
}
System.out.println(space + "Externaloutputcouplings");
for (InternalCoupling<T, ?> ic : this.internalCouplingSet)
{
System.out.print(space);
System.out.print("between ");
System.out.print(ic.getFromPort().getModel().getClass().getName());
System.out.print(" and ");
System.out.print(ic.getToPort().getModel().getClass().getName());
System.out.println();
}
for (AbstractDevsModel<T> dm : this.modelComponents)
{
dm.printModel(space + " ");
}
System.out.println(space + "================");
}
}