LaneBasedGtuGenerator.java

  1. package org.opentrafficsim.road.gtu.generator;

  2. import java.util.LinkedHashMap;
  3. import java.util.LinkedHashSet;
  4. import java.util.LinkedList;
  5. import java.util.Map;
  6. import java.util.Queue;
  7. import java.util.Set;
  8. import java.util.SortedSet;
  9. import java.util.TreeSet;
  10. import java.util.UUID;
  11. import java.util.function.Supplier;

  12. import javax.naming.NamingException;

  13. import org.djunits.unit.DurationUnit;
  14. import org.djunits.value.vdouble.scalar.Duration;
  15. import org.djunits.value.vdouble.scalar.Length;
  16. import org.djunits.value.vdouble.scalar.Speed;
  17. import org.djunits.value.vdouble.scalar.Time;
  18. import org.djutils.draw.point.OrientedPoint2d;
  19. import org.djutils.draw.point.Point2d;
  20. import org.djutils.event.EventType;
  21. import org.djutils.event.LocalEventProducer;
  22. import org.djutils.exceptions.Throw;
  23. import org.djutils.metadata.MetaData;
  24. import org.djutils.metadata.ObjectDescriptor;
  25. import org.opentrafficsim.base.TimeStampedObject;
  26. import org.opentrafficsim.base.geometry.BoundingBox;
  27. import org.opentrafficsim.base.geometry.OtsBounds2d;
  28. import org.opentrafficsim.base.parameters.ParameterException;
  29. import org.opentrafficsim.core.distributions.Generator;
  30. import org.opentrafficsim.core.distributions.ProbabilityException;
  31. import org.opentrafficsim.core.dsol.OtsSimulatorInterface;
  32. import org.opentrafficsim.core.geometry.OtsGeometryException;
  33. import org.opentrafficsim.core.gtu.GtuErrorHandler;
  34. import org.opentrafficsim.core.gtu.GtuException;
  35. import org.opentrafficsim.core.gtu.GtuGenerator;
  36. import org.opentrafficsim.core.gtu.GtuType;
  37. import org.opentrafficsim.core.gtu.RelativePosition;
  38. import org.opentrafficsim.core.network.NetworkException;
  39. import org.opentrafficsim.road.gtu.generator.GeneratorPositions.GeneratorLanePosition;
  40. import org.opentrafficsim.road.gtu.generator.characteristics.LaneBasedGtuCharacteristics;
  41. import org.opentrafficsim.road.gtu.generator.characteristics.LaneBasedGtuCharacteristicsGenerator;
  42. import org.opentrafficsim.road.gtu.lane.LaneBasedGtu;
  43. import org.opentrafficsim.road.gtu.lane.perception.headway.HeadwayGtu;
  44. import org.opentrafficsim.road.gtu.lane.perception.headway.HeadwayGtuReal;
  45. import org.opentrafficsim.road.network.RoadNetwork;
  46. import org.opentrafficsim.road.network.lane.CrossSectionLink;
  47. import org.opentrafficsim.road.network.lane.Lane;
  48. import org.opentrafficsim.road.network.lane.LanePosition;

  49. import nl.tudelft.simulation.dsol.SimRuntimeException;

  50. /**
  51.  * Lane based GTU generator. This generator generates lane based GTUs using a LaneBasedTemplateGTUType. The template is used to
  52.  * generate a set of GTU characteristics at the times implied by the headway generator. These sets are queued until there is
  53.  * sufficient room to construct a GTU at the specified lane locations. The speed of a construction GTU may be reduced to ensure
  54.  * it does not run into its immediate leader GTU.
  55.  * <p>
  56.  * Copyright (c) 2013-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
  57.  * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
  58.  * </p>
  59.  * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
  60.  * @author <a href="https://tudelft.nl/staff/p.knoppers-1">Peter Knoppers</a>
  61.  * @author <a href="https://github.com/wjschakel">Wouter Schakel</a>
  62.  */
  63. public class LaneBasedGtuGenerator extends LocalEventProducer implements GtuGenerator
  64. {
  65.     /** */
  66.     private static final long serialVersionUID = 20160000L;

  67.     /**
  68.      * Event of a generated GTU. Payload: LaneBasedGtu
  69.      */
  70.     public static final EventType GTU_GENERATED_EVENT = new EventType("GENERATOR.GTU_GENERATED", new MetaData("GTU generated",
  71.             "GTU was generated", new ObjectDescriptor("GTU", "The GTU itself", LaneBasedGtu.class)));

  72.     /** FIFO for templates that have not been generated yet due to insufficient room/headway, per position, and per link. */
  73.     private final Map<CrossSectionLink,
  74.             Map<GeneratorLanePosition, Queue<TimeStampedObject<LaneBasedGtuCharacteristics>>>> unplacedTemplates =
  75.                     new LinkedHashMap<>();

  76.     /** Name of the GTU generator. */
  77.     private final String id;

  78.     /** Unique id in the network. */
  79.     private final String uniqueId;

  80.     /** Time distribution that determines the interval times between GTUs. */
  81.     private final Generator<Duration> interarrivelTimeGenerator;

  82.     /** Generates most properties of the GTUs. */
  83.     private final LaneBasedGtuCharacteristicsGenerator laneBasedGtuCharacteristicsGenerator;

  84.     /** Total number of GTUs generated so far. */
  85.     private long generatedGTUs = 0;

  86.     /** Retry interval for checking if a GTU can be placed. */
  87.     private Duration reTryInterval = new Duration(0.1, DurationUnit.SI);

  88.     /** Location provider for all generated GTUs. */
  89.     private final GeneratorPositions generatorPositions;

  90.     /** Network. */
  91.     private final RoadNetwork network;

  92.     /** Simulator. */
  93.     private final OtsSimulatorInterface simulator;

  94.     /** The way that this generator checks if it is safe to construct and place the next lane based GTU. */
  95.     private final RoomChecker roomChecker;

  96.     /** ID generator. */
  97.     private final Supplier<String> idGenerator;

  98.     /** Initial distance over which lane changes shouldn't be performed. */
  99.     private Length noLaneChangeDistance = null;

  100.     /** Whether GTUs change lane instantaneously. */
  101.     private boolean instantaneousLaneChange = false;

  102.     /** GTU error handler. */
  103.     private GtuErrorHandler errorHandler = GtuErrorHandler.THROW;

  104.     /** Vehicle generation is ignored on these lanes. */
  105.     private Set<Lane> disabled = new LinkedHashSet<>();

  106.     /** Order of GTU ids. Default is in order of successful generation. Otherwise its in order of characteristics drawing. */
  107.     private boolean idsInCharacteristicsOrder = false;

  108.     /** Map of ids drawn at time of GTU characteristics drawing, if idsInCharacteristicsOrder = true. */
  109.     private Map<LaneBasedGtuCharacteristics, String> unplacedIds = null;

  110.     /** This enables to check whether idsInCharacteristicsOrder can still be set. */
  111.     private boolean firstCharacteristicsDrawn = false;

  112.     /**
  113.      * Construct a new lane base GTU generator.
  114.      * @param id String; name of the new GTU generator
  115.      * @param interarrivelTimeGenerator Generator&lt;Duration&gt;; generator for the interval times between GTUs
  116.      * @param laneBasedGtuCharacteristicsGenerator LaneBasedGtuCharacteristicsGenerator; generator of the characteristics of
  117.      *            each GTU
  118.      * @param generatorPositions GeneratorPositions; location and initial direction provider for all generated GTUs
  119.      * @param network RoadNetwork; the OTS network that owns the generated GTUs
  120.      * @param simulator OtsSimulatorInterface; simulator
  121.      * @param roomChecker RoomChecker; the way that this generator checks that there is sufficient room to place a new GTU
  122.      * @param idGenerator Supplier&lt;String&gt;; id generator
  123.      * @throws SimRuntimeException when <cite>startTime</cite> lies before the current simulation time
  124.      * @throws ProbabilityException pe
  125.      * @throws ParameterException if drawing from the interarrival generator fails
  126.      * @throws NetworkException if the object could not be added to the network
  127.      */
  128.     @SuppressWarnings("parameternumber")
  129.     public LaneBasedGtuGenerator(final String id, final Generator<Duration> interarrivelTimeGenerator,
  130.             final LaneBasedGtuCharacteristicsGenerator laneBasedGtuCharacteristicsGenerator,
  131.             final GeneratorPositions generatorPositions, final RoadNetwork network, final OtsSimulatorInterface simulator,
  132.             final RoomChecker roomChecker, final Supplier<String> idGenerator)
  133.             throws SimRuntimeException, ProbabilityException, ParameterException, NetworkException
  134.     {
  135.         this.id = id;
  136.         this.uniqueId = UUID.randomUUID().toString() + "_" + id;
  137.         this.interarrivelTimeGenerator = interarrivelTimeGenerator;
  138.         this.laneBasedGtuCharacteristicsGenerator = laneBasedGtuCharacteristicsGenerator;
  139.         this.generatorPositions = generatorPositions;
  140.         this.network = network;
  141.         this.simulator = simulator;
  142.         this.roomChecker = roomChecker;
  143.         this.idGenerator = idGenerator;
  144.         Duration headway = this.interarrivelTimeGenerator.draw();
  145.         if (headway != null) // otherwise no demand at all
  146.         {
  147.             simulator.scheduleEventRel(headway, this, "generateCharacteristics", new Object[] {});
  148.         }
  149.         this.network.addNonLocatedObject(this);
  150.         if (this.idGenerator instanceof Injections injections && injections.hasColumn(Injections.ID_COLUMN))
  151.         {
  152.             setIdsInCharacteristicsOrder(true); // also creates the unplaced ids map
  153.         }
  154.     }

  155.     /**
  156.      * Sets the initial distance over which lane changes shouldn't be performed.
  157.      * @param noLaneChangeDistance Length; initial distance over which lane changes shouldn't be performed
  158.      */
  159.     public void setNoLaneChangeDistance(final Length noLaneChangeDistance)
  160.     {
  161.         this.noLaneChangeDistance = noLaneChangeDistance;
  162.     }

  163.     /**
  164.      * Sets whether GTUs will change lane instantaneously.
  165.      * @param instantaneous boolean; whether GTUs will change lane instantaneously
  166.      */
  167.     public void setInstantaneousLaneChange(final boolean instantaneous)
  168.     {
  169.         this.instantaneousLaneChange = instantaneous;
  170.     }

  171.     /**
  172.      * Sets the GTU error handler.
  173.      * @param gtuErrorHandler GTUErrorHandler; GTU error handler
  174.      */
  175.     public void setErrorHandler(final GtuErrorHandler gtuErrorHandler)
  176.     {
  177.         this.errorHandler = gtuErrorHandler;
  178.     }

  179.     /**
  180.      * Sets what order should be used for the ids. By default this is in the order of successful GTU generation. If however the
  181.      * id generator is an instance of {@code Injections} with an id column, it is by default in the order of characteristics
  182.      * drawing.
  183.      * @param idsInCharacteristicsOrder boolean; ids in order of drawing characteristics, or successful generation otherwise.
  184.      */
  185.     public void setIdsInCharacteristicsOrder(final boolean idsInCharacteristicsOrder)
  186.     {
  187.         Throw.when(this.firstCharacteristicsDrawn, IllegalStateException.class,
  188.                 "Id order cannot be set once GTU characteristics were drawn.");
  189.         this.unplacedIds = new LinkedHashMap<>();
  190.         this.idsInCharacteristicsOrder = idsInCharacteristicsOrder;
  191.     }

  192.     /**
  193.      * Generate the characteristics of the next GTU.
  194.      * @throws ProbabilityException when something is wrongly defined in the LaneBasedTemplateGTUType
  195.      * @throws SimRuntimeException when this method fails to re-schedule itself or the call to the method that tries to place a
  196.      *             GTU on the road
  197.      * @throws ParameterException in case of a parameter problem
  198.      * @throws GtuException if strategical planner cannot generate a plan
  199.      */
  200.     @SuppressWarnings("unused")
  201.     private void generateCharacteristics() throws ProbabilityException, SimRuntimeException, ParameterException, GtuException
  202.     {
  203.         this.firstCharacteristicsDrawn = true;
  204.         synchronized (this.unplacedTemplates)
  205.         {
  206.             LaneBasedGtuCharacteristics characteristics = this.laneBasedGtuCharacteristicsGenerator.draw();
  207.             GtuType gtuType = characteristics.getGtuType();
  208.             // gather information on number of unplaced templates per lane, and per link, for the drawing of a new position
  209.             Map<CrossSectionLink, Map<Integer, Integer>> unplaced = new LinkedHashMap<>();
  210.             for (CrossSectionLink link : this.unplacedTemplates.keySet())
  211.             {
  212.                 Map<Integer, Integer> linkMap = new LinkedHashMap<>();
  213.                 Map<GeneratorLanePosition, Queue<TimeStampedObject<LaneBasedGtuCharacteristics>>> linkTemplates =
  214.                         this.unplacedTemplates.get(link);
  215.                 for (GeneratorLanePosition lanePosition : linkTemplates.keySet())
  216.                 {
  217.                     linkMap.put(lanePosition.getLaneNumber(), linkTemplates.get(lanePosition).size());
  218.                 }
  219.                 unplaced.put(link, linkMap);
  220.             }
  221.             // position draw
  222.             GeneratorLanePosition lanePosition = this.generatorPositions.draw(gtuType, characteristics, unplaced);

  223.             // skip if disabled at this lane-direction
  224.             if (!this.disabled.contains(lanePosition.getPosition().lane()))
  225.             {
  226.                 if (this.idsInCharacteristicsOrder)
  227.                 {
  228.                     this.unplacedIds.put(characteristics, this.idGenerator.get());
  229.                 }
  230.                 queueGtu(lanePosition, characteristics);
  231.             }

  232.         }
  233.         // @docs/02-model-structure/dsol.md#event-based-simulation
  234.         Duration headway = this.interarrivelTimeGenerator.draw();
  235.         if (headway != null)
  236.         {
  237.             this.simulator.scheduleEventRel(headway, this, "generateCharacteristics", new Object[] {});
  238.         }
  239.         // @end
  240.     }

  241.     /**
  242.      * Check if the queue is non-empty and, if it is, try to place the GTUs in the queue on the road.
  243.      * @param position GeneratorLanePosition; position
  244.      * @throws SimRuntimeException should never happen
  245.      * @throws GtuException when something wrong in the definition of the GTU
  246.      * @throws OtsGeometryException when something is wrong in the definition of the GTU
  247.      * @throws NetworkException when something is wrong with the initial location of the GTU
  248.      * @throws NamingException ???
  249.      * @throws ProbabilityException pe
  250.      */
  251.     @SuppressWarnings("unused")
  252.     private void tryToPlaceGTU(final GeneratorLanePosition position) throws SimRuntimeException, GtuException, NamingException,
  253.             NetworkException, OtsGeometryException, ProbabilityException
  254.     {
  255.         TimeStampedObject<LaneBasedGtuCharacteristics> timedCharacteristics;
  256.         Queue<TimeStampedObject<LaneBasedGtuCharacteristics>> queue =
  257.                 this.unplacedTemplates.get(position.getLink()).get(position);

  258.         synchronized (queue)
  259.         {
  260.             timedCharacteristics = queue.peek();
  261.         }
  262.         if (null == timedCharacteristics)
  263.         {
  264.             return; // Do not re-schedule this method
  265.         }

  266.         LaneBasedGtuCharacteristics characteristics = timedCharacteristics.object();
  267.         SortedSet<HeadwayGtu> leaders = new TreeSet<>();
  268.         getFirstLeaders(position.getPosition().lane(),
  269.                 position.getPosition().position().neg().minus(characteristics.getFront()), position.getPosition().position(),
  270.                 leaders);
  271.         Duration since = this.simulator.getSimulatorAbsTime().minus(timedCharacteristics.timestamp());
  272.         Placement placement = this.roomChecker.canPlace(leaders, characteristics, since, position.getPosition());
  273.         if (placement.canPlace())
  274.         {
  275.             // There is enough room; remove the template from the queue and construct the new GTU
  276.             synchronized (queue)
  277.             {
  278.                 queue.remove();
  279.             }
  280.             placeGtu(characteristics, placement.getPosition(), placement.getSpeed());
  281.             if (queue.size() > 0)
  282.             {
  283.                 this.simulator.scheduleEventNow(this, "tryToPlaceGTU", new Object[] {position});
  284.             }
  285.         }
  286.         // @docs/02-model-structure/dsol.md#event-based-simulation (without the 'else')
  287.         else if (queue.size() > 0)
  288.         {
  289.             this.simulator.scheduleEventRel(this.reTryInterval, this, "tryToPlaceGTU", new Object[] {position});
  290.         }
  291.         // @end
  292.     }

  293.     /**
  294.      * Adds a GTU to the generation queue. This method ignores whether vehicle generation is enabled at the location. This
  295.      * allows an external party to govern (over some time) what vehicles are generated.
  296.      * @param characteristics LaneBasedGtuCharacteristics; characteristics of GTU to add to the queue
  297.      * @param lane Lane; position to generate the GTU at
  298.      */
  299.     public final void queueGtu(final LaneBasedGtuCharacteristics characteristics, final Lane lane)
  300.     {
  301.         // first find the correct GeneratorLanePosition
  302.         GeneratorLanePosition genPosition = null;
  303.         for (GeneratorLanePosition lanePosition : this.generatorPositions.getAllPositions())
  304.         {
  305.             if (lanePosition.getPosition().lane().equals(lane))
  306.             {
  307.                 genPosition = lanePosition;
  308.                 break;
  309.             }
  310.         }
  311.         Throw.when(genPosition == null, IllegalStateException.class, "Lane %s is not part of the generation.", lane);
  312.         try
  313.         {
  314.             queueGtu(genPosition, characteristics);
  315.         }
  316.         catch (SimRuntimeException exception)
  317.         {
  318.             throw new RuntimeException("Unexpected exception while scheduling tryToPlace event.", exception);
  319.         }
  320.     }

  321.     /**
  322.      * Places the characteristics in the queue pertaining to the position, and schedules a call to {@code tryToPlace} now if the
  323.      * queue length is 1.
  324.      * @param lanePosition GeneratorLanePosition; position to generate the GTU at
  325.      * @param characteristics LaneBasedGtuCharacteristics; characteristics of GTU to add to the queue
  326.      * @throws SimRuntimeException when an event is scheduled in the past
  327.      */
  328.     private void queueGtu(final GeneratorLanePosition lanePosition, final LaneBasedGtuCharacteristics characteristics)
  329.             throws SimRuntimeException
  330.     {
  331.         if (!this.unplacedTemplates.containsKey(lanePosition.getLink()))
  332.         {
  333.             this.unplacedTemplates.put(lanePosition.getLink(), new LinkedHashMap<>());
  334.         }
  335.         Map<GeneratorLanePosition, Queue<TimeStampedObject<LaneBasedGtuCharacteristics>>> linkMap =
  336.                 this.unplacedTemplates.get(lanePosition.getLink());
  337.         if (!linkMap.containsKey(lanePosition))
  338.         {
  339.             linkMap.put(lanePosition, new LinkedList<>());
  340.         }
  341.         Queue<TimeStampedObject<LaneBasedGtuCharacteristics>> queue = linkMap.get(lanePosition);
  342.         queue.add(new TimeStampedObject<>(characteristics, this.simulator.getSimulatorAbsTime()));
  343.         // @docs/02-model-structure/dsol.md#event-based-simulation
  344.         if (queue.size() == 1)
  345.         {
  346.             this.simulator.scheduleEventNow(this, "tryToPlaceGTU", new Object[] {lanePosition});
  347.         }
  348.         // @end
  349.     }

  350.     /**
  351.      * Places a GTU, regardless of whether it has room. The user of this method should verify this is the case.
  352.      * @param characteristics LaneBasedGtuCharacteristics; characteristics
  353.      * @param position LanePosition; position
  354.      * @param speed Speed; speed
  355.      * @throws NamingException on exception
  356.      * @throws GtuException on exception
  357.      * @throws NetworkException on exception
  358.      * @throws SimRuntimeException on exception
  359.      * @throws OtsGeometryException on exception
  360.      */
  361.     public final void placeGtu(final LaneBasedGtuCharacteristics characteristics, final LanePosition position,
  362.             final Speed speed) throws NamingException, GtuException, NetworkException, SimRuntimeException, OtsGeometryException
  363.     {
  364.         String gtuId = this.idsInCharacteristicsOrder ? this.unplacedIds.remove(characteristics) : this.idGenerator.get();
  365.         LaneBasedGtu gtu = new LaneBasedGtu(gtuId, characteristics.getGtuType(), characteristics.getLength(),
  366.                 characteristics.getWidth(), characteristics.getMaximumSpeed(), characteristics.getFront(), this.network);
  367.         gtu.setMaximumAcceleration(characteristics.getMaximumAcceleration());
  368.         gtu.setMaximumDeceleration(characteristics.getMaximumDeceleration());
  369.         gtu.setVehicleModel(characteristics.getVehicleModel());
  370.         gtu.setNoLaneChangeDistance(this.noLaneChangeDistance);
  371.         gtu.setInstantaneousLaneChange(this.instantaneousLaneChange);
  372.         gtu.setErrorHandler(this.errorHandler);
  373.         gtu.init(characteristics.getStrategicalPlannerFactory().create(gtu, characteristics.getRoute(),
  374.                 characteristics.getOrigin(), characteristics.getDestination()), position, speed);
  375.         this.generatedGTUs++;
  376.         fireEvent(GTU_GENERATED_EVENT, gtu);
  377.     }

  378.     /**
  379.      * Adds the first GTU on the lane to the set, or any number or leaders on downstream lane(s) if there is no GTU on the lane.
  380.      * @param lane Lane; lane to search on
  381.      * @param startDistance Length; distance from generator location (nose) to start of the lane
  382.      * @param beyond Length; location to search downstream of which is the generator position, or the start for downstream lanes
  383.      * @param set Set&lt;HeadwayGtu&gt;; set to add the GTU's to
  384.      * @throws GtuException if a GTU is incorrectly positioned on a lane
  385.      */
  386.     private void getFirstLeaders(final Lane lane, final Length startDistance, final Length beyond, final Set<HeadwayGtu> set)
  387.             throws GtuException
  388.     {
  389.         LaneBasedGtu next = lane.getGtuAhead(beyond, RelativePosition.FRONT, this.simulator.getSimulatorAbsTime());
  390.         if (next != null)
  391.         {
  392.             Length headway = startDistance.plus(next.position(lane, next.getRear()));
  393.             if (headway.si < 300)
  394.             {
  395.                 set.add(new HeadwayGtuReal(next, headway, true));
  396.             }
  397.             return;
  398.         }
  399.         Set<Lane> downstreamLanes = lane.nextLanes(null);
  400.         for (Lane downstreamLane : downstreamLanes)
  401.         {
  402.             Length startDistanceDownstream = startDistance.plus(lane.getLength());
  403.             if (startDistanceDownstream.si > 300)
  404.             {
  405.                 return;
  406.             }
  407.             Length beyondDownstream = Length.ZERO;
  408.             getFirstLeaders(downstreamLane, startDistanceDownstream, beyondDownstream, set);
  409.         }
  410.     }

  411.     /** {@inheritDoc} */
  412.     @Override
  413.     public final String toString()
  414.     {
  415.         return "LaneBasedGtuGenerator " + this.id + " on " + this.generatorPositions.getAllPositions();
  416.     }

  417.     /**
  418.      * @return generatedGTUs.
  419.      */
  420.     public final long getGeneratedGTUs()
  421.     {
  422.         return this.generatedGTUs;
  423.     }

  424.     /**
  425.      * Retrieve the id of this LaneBasedGtuGenerator.
  426.      * @return String; the id of this LaneBasedGtuGenerator
  427.      */
  428.     @Override
  429.     public final String getId()
  430.     {
  431.         return this.id;
  432.     }

  433.     /**
  434.      * Disable the vehicle generator during the specific time. Underlying processes such as drawing characteristics and headways
  435.      * are continued, but simply will not result in the queuing of the GTU.
  436.      * @param start Time; start time
  437.      * @param end Time; end time
  438.      * @param lane Lane; lane to disable generation on
  439.      * @throws SimRuntimeException if time is incorrect
  440.      */
  441.     public void disable(final Time start, final Time end, final Lane lane) throws SimRuntimeException
  442.     {
  443.         Throw.when(end.lt(start), SimRuntimeException.class, "End time %s is before start time %s.", end, start);
  444.         this.simulator.scheduleEventAbsTime(start, this, "disable", new Object[] {lane});
  445.         this.simulator.scheduleEventAbsTime(end, this, "enable", new Object[0]);
  446.     }

  447.     /**
  448.      * Disables the generator.
  449.      * @param lane Lane; lanes to disable generation on
  450.      */
  451.     @SuppressWarnings("unused")
  452.     private void disable(final Lane lane)
  453.     {
  454.         Throw.when(this.disabled != null && !this.disabled.isEmpty(), IllegalStateException.class,
  455.                 "Disabling a generator that is already disabled is not allowed.");
  456.         this.disabled.add(lane);
  457.     }

  458.     /**
  459.      * Enables the generator.
  460.      */
  461.     @SuppressWarnings("unused")
  462.     private void enable()
  463.     {
  464.         this.disabled = new LinkedHashSet<>();
  465.     }

  466.     /** {@inheritDoc} */
  467.     @Override
  468.     public String getFullId()
  469.     {
  470.         return this.uniqueId;
  471.     }

  472.     /** {@inheritDoc} */
  473.     @Override
  474.     public Set<GtuGeneratorPosition> getPositions()
  475.     {
  476.         Set<GtuGeneratorPosition> set = new LinkedHashSet<>();
  477.         for (GeneratorLanePosition lanePosition : this.generatorPositions.getAllPositions())
  478.         {
  479.             LanePosition pos = lanePosition.getPosition();
  480.             OrientedPoint2d p = pos.getLocation();
  481.             set.add(new GtuGeneratorPosition()
  482.             {
  483.                 /** {@inheritDoc} */
  484.                 @Override
  485.                 public Point2d getLocation()
  486.                 {
  487.                     return p;
  488.                 }

  489.                 /** {@inheritDoc} */
  490.                 @Override
  491.                 public OtsBounds2d getBounds()
  492.                 {
  493.                     return new BoundingBox(0.0, 0.0);
  494.                 }

  495.                 /** {@inheritDoc} */
  496.                 @Override
  497.                 public int getQueueCount()
  498.                 {
  499.                     return getQueueLength(lanePosition);
  500.                 }

  501.                 /** {@inheritDoc} */
  502.                 @Override
  503.                 public String getId()
  504.                 {
  505.                     return LaneBasedGtuGenerator.this.id + "@" + lanePosition.getLink().getId() + "." + pos.lane().getId();
  506.                 }
  507.             });
  508.         }
  509.         return set;
  510.     }

  511.     /**
  512.      * Returns the number of GTUs in queue at the position.
  513.      * @param position GeneratorLanePosition; position.
  514.      * @return int; number of GTUs in queue at the position.
  515.      */
  516.     private int getQueueLength(final GeneratorLanePosition position)
  517.     {
  518.         for (CrossSectionLink link : this.unplacedTemplates.keySet())
  519.         {
  520.             for (GeneratorLanePosition lanePosition : this.unplacedTemplates.get(link).keySet())
  521.             {
  522.                 if (lanePosition.equals(position))
  523.                 {
  524.                     return this.unplacedTemplates.get(link).get(lanePosition).size();
  525.                 }
  526.             }
  527.         }
  528.         return 0;
  529.     }

  530.     /**
  531.      * Interface for class that checks that there is sufficient room for a proposed new GTU and returns the maximum safe speed
  532.      * and position for the proposed new GTU.
  533.      */
  534.     public interface RoomChecker
  535.     {
  536.         /**
  537.          * Return the maximum safe speed and position for a new GTU with the specified characteristics. Returns
  538.          * {@code Placement.NO} if there is no safe speed and position. This method might be called with an empty leader set
  539.          * such that the desired speed can be implemented.
  540.          * @param leaders SortedSet&lt;HeadwayGtu&gt;; leaders, usually 1, possibly more after a branch
  541.          * @param characteristics LaneBasedGtuCharacteristics; characteristics of the proposed new GTU
  542.          * @param since Duration; time since the GTU wanted to arrive
  543.          * @param initialPosition LanePosition; initial position
  544.          * @return Speed; maximum safe speed, or null if a GTU with the specified characteristics cannot be placed at the
  545.          *         current time
  546.          * @throws NetworkException this method may throw a NetworkException if it encounters an error in the network structure
  547.          * @throws GtuException on parameter exception
  548.          */
  549.         Placement canPlace(SortedSet<HeadwayGtu> leaders, LaneBasedGtuCharacteristics characteristics, Duration since,
  550.                 LanePosition initialPosition) throws NetworkException, GtuException;
  551.     }

  552.     /**
  553.      * Placement contains the information that a {@code RoomChecker} returns.
  554.      * <p>
  555.      * Copyright (c) 2013-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
  556.      * <br>
  557.      * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
  558.      * </p>
  559.      * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
  560.      * @author <a href="https://tudelft.nl/staff/p.knoppers-1">Peter Knoppers</a>
  561.      * @author <a href="https://github.com/wjschakel">Wouter Schakel</a>
  562.      */
  563.     public static final class Placement
  564.     {

  565.         /** Value if the GTU cannot be placed. */
  566.         public static final Placement NO = new Placement();

  567.         /** Speed. */
  568.         private final Speed speed;

  569.         /** Position. */
  570.         private final LanePosition position;

  571.         /**
  572.          * Constructor for NO.
  573.          */
  574.         private Placement()
  575.         {
  576.             this.speed = null;
  577.             this.position = null;
  578.         }

  579.         /**
  580.          * Constructor.
  581.          * @param speed Speed; speed
  582.          * @param position LanePosition; position
  583.          */
  584.         public Placement(final Speed speed, final LanePosition position)
  585.         {
  586.             Throw.whenNull(speed, "Speed may not be null. Use Placement.NO if the GTU cannot be placed.");
  587.             Throw.whenNull(position, "Position may not be null. Use Placement.NO if the GTU cannot be placed.");
  588.             this.speed = speed;
  589.             this.position = position;
  590.         }

  591.         /**
  592.          * Returns whether the GTU can be placed.
  593.          * @return whether the GTU can be placed
  594.          */
  595.         public boolean canPlace()
  596.         {
  597.             return this.speed != null && this.position != null;
  598.         }

  599.         /**
  600.          * Returns the speed.
  601.          * @return Speed; speed
  602.          */
  603.         public Speed getSpeed()
  604.         {
  605.             return this.speed;
  606.         }

  607.         /**
  608.          * Returns the position.
  609.          * @return LanePosition; position
  610.          */
  611.         public LanePosition getPosition()
  612.         {
  613.             return this.position;
  614.         }

  615.         /** {@inheritDoc} */
  616.         @Override
  617.         public String toString()
  618.         {
  619.             return "Placement [speed=" + this.speed + ", position=" + this.position + "]";
  620.         }

  621.     }

  622. }