Duplicate.java
package nl.tudelft.simulation.dsol.formalisms.flow;
import java.util.function.Consumer;
import org.djutils.event.EventType;
import org.djutils.exceptions.Throw;
import org.djutils.metadata.MetaData;
import org.djutils.metadata.ObjectDescriptor;
import nl.tudelft.simulation.dsol.simulators.DevsSimulatorInterface;
import nl.tudelft.simulation.dsol.statistics.SimCounter;
import nl.tudelft.simulation.jstats.distributions.DistDiscrete;
import nl.tudelft.simulation.jstats.distributions.DistDiscreteConstant;
/**
* The Duplicate flow block makes a number of copies of incoming entities and sends them to a destination.
* <p>
* Copyright (c) 2002-2025 Delft University of Technology, Jaffalaan 5, 2628 BX Delft, the Netherlands. All rights reserved. See
* for project information <a href="https://simulation.tudelft.nl/dsol/manual/" target="_blank">DSOL Manual</a>. The DSOL
* project is distributed under a three-clause BSD-style license, which can be found at
* <a href="https://simulation.tudelft.nl/dsol/docs/latest/license.html" target="_blank">DSOL License</a>.
* </p>
* @author <a href="https://www.linkedin.com/in/peterhmjacobs">Peter Jacobs </a>
* @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
* @param <T> the time type
*/
public class Duplicate<T extends Number & Comparable<T>> extends FlowBlock<T, Duplicate<T>>
{
/** The destination of the duplicates. */
private FlowBlock<T, ?> duplicateDestination;
/** The distribution of the number of duplicates to generate. */
private DistDiscrete numberCopiesDist = null;
/** the function to apply just before releasing a duplicate entity. */
private Consumer<Entity<T>> duplicateReleaseFunction = null;
/** a potential count statistic for the number of released duplicate entities; when null, no statistic is calculated. */
private SimCounter<T> countDuplicateReleasedStatistic;
/** DUPLICATE_RELEASE_EVENT is fired whenever a duplicate entity leaves the flow object. */
public static final EventType DUPLICATE_RELEASE_EVENT = new EventType(new MetaData("DUPLICATE_RELEASE_EVENT",
"Duplicate entity released",
new ObjectDescriptor("releasedDuplicateEntity", "number of released duplicate entities (1)", Integer.class)));
/**
* Create a new Duplicate flow block.
* @param id the id of the FlowObject
* @param simulator on which is scheduled
*/
public Duplicate(final String id, final DevsSimulatorInterface<T> simulator)
{
super(id, simulator);
}
/**
* Set the destination of the duplicate entity / entities. The destination can be null, in that case the duplicates are
* destroyed after creation.
* @param duplicateDestination the new destination of the duplicate entities
* @return this object for method chaining
*/
public Duplicate<T> setDuplicateDestination(final FlowBlock<T, ?> duplicateDestination)
{
this.duplicateDestination = duplicateDestination;
return this;
}
/**
* Set the distribution of the number of copies to generate.
* @param numberCopiesDist set numberCopiesDist
* @return this object for method chaining
*/
public Duplicate<T> setNumberCopiesDist(final DistDiscrete numberCopiesDist)
{
Throw.whenNull(numberCopiesDist, "distribution for number copies cannot be null");
this.numberCopiesDist = numberCopiesDist;
return this;
}
/**
* Set a fixed number of copies to generate.
* @param numberCopies set the fixed number of copies to generate
* @return this object for method chaining
*/
public Duplicate<T> setNumberCopies(final int numberCopies)
{
Throw.when(numberCopies < 0, IllegalArgumentException.class, "number of copies cannot be negative");
this.numberCopiesDist = new DistDiscreteConstant(getSimulator().getModel().getDefaultStream(), numberCopies);
return this;
}
/**
* Set the function to apply just before releasing a duplicate entity.
* @param duplicateReleaseFunction the function to apply just before releasing a duplicate entity, can be null to remove an
* existing duplicate release function
* @return this object for method chaining
*/
public Duplicate<T> setDuplicateReleaseFunction(final Consumer<Entity<T>> duplicateReleaseFunction)
{
this.duplicateReleaseFunction = duplicateReleaseFunction;
return this;
}
@Override
public synchronized void receiveEntity(final Entity<T> entity)
{
Throw.whenNull(this.numberCopiesDist, "distribution for number copies cannot be null when running");
super.receiveEntity(entity);
this.releaseEntity(entity);
long nr = this.numberCopiesDist.draw();
Throw.when(nr < 0, IllegalArgumentException.class, "number of copies cannot be negative");
for (int i = 0; i < nr; i++)
{
Entity<T> clone = entity.clone();
this.fireTimedEvent(DUPLICATE_RELEASE_EVENT, 1, getSimulator().getSimulatorTime());
if (this.duplicateReleaseFunction != null)
this.duplicateReleaseFunction.accept(clone);
if (this.duplicateDestination != null)
this.duplicateDestination.receiveEntity(clone);
}
}
/**
* Turn on the default statistics for this flow block.
* @return the Duplicate instance for method chaining
*/
public Duplicate<T> setDefaultStatistics()
{
if (!hasDefaultStatistics())
{
super.setDefaultFlowBlockStatistics();
this.countDuplicateReleasedStatistic = new SimCounter<>("Duplicate.DuplicateReleased:" + getBlockNumber(),
getId() + " # of released duplicate entities", getSimulator().getModel(), this, DUPLICATE_RELEASE_EVENT);
this.countDuplicateReleasedStatistic.initialize();
}
return this;
}
/**
* Return whether statistics are turned on for this Duplicate block.
* @return whether statistics are turned on for this Duplicate block.
*/
public boolean hasDefaultStatistics()
{
return this.countDuplicateReleasedStatistic != null;
}
/**
* Return the count statistic for the number of released objects
* @return the count statistic for the number of released objects, can be null if no statistics are calculated.
*/
public SimCounter<T> getCountDuplicateReleasedStatistic()
{
return this.countDuplicateReleasedStatistic;
}
/**
* Return the destination flow block where the duplicates go.
* @return the destination flow block where the duplicates go, can be null if not yet set
*/
public FlowBlock<T, ?> getDuplicateDestination()
{
return this.duplicateDestination;
}
/**
* Return the distribution of the number of copies.
* @return the distribution of the number of copies, can be null if not yet set
*/
public DistDiscrete getNumberCopiesDist()
{
return this.numberCopiesDist;
}
}