RoadSampler.java

  1. package org.opentrafficsim.road.network.sampling;

  2. import java.rmi.RemoteException;
  3. import java.util.HashMap;
  4. import java.util.HashSet;
  5. import java.util.Map;
  6. import java.util.Set;

  7. import org.djunits.unit.DurationUnit;
  8. import org.djunits.value.vdouble.scalar.Acceleration;
  9. import org.djunits.value.vdouble.scalar.Duration;
  10. import org.djunits.value.vdouble.scalar.Frequency;
  11. import org.djunits.value.vdouble.scalar.Length;
  12. import org.djunits.value.vdouble.scalar.Speed;
  13. import org.djunits.value.vdouble.scalar.Time;
  14. import org.opentrafficsim.core.gtu.GTUDirectionality;
  15. import org.opentrafficsim.core.gtu.GTUException;
  16. import org.opentrafficsim.core.gtu.RelativePosition;
  17. import org.opentrafficsim.kpi.sampling.KpiGtuDirectionality;
  18. import org.opentrafficsim.kpi.sampling.KpiLaneDirection;
  19. import org.opentrafficsim.kpi.sampling.Sampler;
  20. import org.opentrafficsim.road.gtu.lane.LaneBasedGTU;
  21. import org.opentrafficsim.road.network.lane.Lane;
  22. import org.opentrafficsim.road.network.lane.LaneDirection;

  23. import nl.tudelft.simulation.dsol.SimRuntimeException;
  24. import nl.tudelft.simulation.dsol.formalisms.eventscheduling.SimEventInterface;
  25. import nl.tudelft.simulation.dsol.simtime.SimTimeDoubleUnit;
  26. import nl.tudelft.simulation.dsol.simulators.DEVSSimulatorInterface;
  27. import nl.tudelft.simulation.event.EventInterface;
  28. import nl.tudelft.simulation.event.EventListenerInterface;
  29. import nl.tudelft.simulation.event.TimedEvent;
  30. import nl.tudelft.simulation.language.Throw;

  31. /**
  32.  * Implementation of kpi sampler for OTS.
  33.  * <p>
  34.  * Copyright (c) 2013-2018 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
  35.  * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
  36.  * <p>
  37.  * @version $Revision$, $LastChangedDate$, by $Author$, initial version 12 okt. 2016 <br>
  38.  * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
  39.  * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
  40.  * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
  41.  */
  42. public class RoadSampler extends Sampler<GtuData> implements EventListenerInterface
  43. {

  44.     /** Simulator. */
  45.     private final DEVSSimulatorInterface.TimeDoubleUnit simulator;

  46.     /** Sampling interval. */
  47.     private final Duration samplingInterval;

  48.     /** Registration of sampling events of each GTU per lane, if interval based. */
  49.     private final Map<String, Map<LaneDirection, SimEventInterface<SimTimeDoubleUnit>>> eventPerGtu = new HashMap<>();

  50.     /** List of lane the sampler is listening to for each GTU. Usually 1, could be 2 during a trajectory transition. */
  51.     private final Map<String, Set<LaneDirection>> listenersPerGtu = new HashMap<>();

  52.     /**
  53.      * Constructor which uses the operational plan updates of GTU's as sampling interval.
  54.      * @param simulator simulator
  55.      * @throws NullPointerException if the simulator is {@code null}
  56.      */
  57.     public RoadSampler(final DEVSSimulatorInterface.TimeDoubleUnit simulator)
  58.     {
  59.         Throw.whenNull(simulator, "Simulator may not be null.");
  60.         this.simulator = simulator;
  61.         this.samplingInterval = null;
  62.     }

  63.     /**
  64.      * Constructor which uses the given frequency to determine the sampling interval.
  65.      * @param simulator simulator
  66.      * @param frequency sampling frequency
  67.      * @throws NullPointerException if an input is {@code null}
  68.      * @throws IllegalArgumentException if frequency is negative or zero
  69.      */
  70.     public RoadSampler(final DEVSSimulatorInterface.TimeDoubleUnit simulator, final Frequency frequency)
  71.     {
  72.         Throw.whenNull(simulator, "Simulator may not be null.");
  73.         Throw.whenNull(frequency, "Frequency may not be null.");
  74.         Throw.when(frequency.le(Frequency.ZERO), IllegalArgumentException.class,
  75.                 "Negative or zero sampling frequency is not permitted.");
  76.         this.simulator = simulator;
  77.         this.samplingInterval = new Duration(1.0 / frequency.si, DurationUnit.SI);
  78.     }

  79.     /** {@inheritDoc} */
  80.     @Override
  81.     public final Time now()
  82.     {
  83.         return this.simulator.getSimulatorTime();
  84.     }

  85.     /** {@inheritDoc} */
  86.     @Override
  87.     public final void scheduleStartRecording(final Time time, final KpiLaneDirection kpiLaneDirection)
  88.     {
  89.         try
  90.         {
  91.             this.simulator.scheduleEventAbs(time, this, this, "startRecording", new Object[] { kpiLaneDirection });
  92.         }
  93.         catch (SimRuntimeException exception)
  94.         {
  95.             throw new RuntimeException("Cannot start recording.", exception);
  96.         }
  97.     }

  98.     /** {@inheritDoc} */
  99.     @Override
  100.     public final void scheduleStopRecording(final Time time, final KpiLaneDirection kpiLaneDirection)
  101.     {
  102.         try
  103.         {
  104.             this.simulator.scheduleEventAbs(time, this, this, "stopRecording", new Object[] { kpiLaneDirection });
  105.         }
  106.         catch (SimRuntimeException exception)
  107.         {
  108.             throw new RuntimeException("Cannot stop recording.", exception);
  109.         }
  110.     }

  111.     /** {@inheritDoc} */
  112.     @Override
  113.     public final void initRecording(final KpiLaneDirection kpiLaneDirection)
  114.     {
  115.         Lane lane = ((LaneData) kpiLaneDirection.getLaneData()).getLane();
  116.         lane.addListener(this, Lane.GTU_ADD_EVENT, true);
  117.         lane.addListener(this, Lane.GTU_REMOVE_EVENT, true);
  118.         int count = 1;
  119.         for (LaneBasedGTU gtu : lane.getGtuList())
  120.         {
  121.             try
  122.             {
  123.                 if (sameDirection(kpiLaneDirection.getKpiDirection(), gtu.getDirection(lane)))
  124.                 {
  125.                     // Payload: Object[] {String gtuId, LaneBasedGTU gtu, int count_after_addition}
  126.                     notify(new TimedEvent<>(Lane.GTU_ADD_EVENT, lane, new Object[] { gtu.getId(), gtu, count },
  127.                             gtu.getSimulator().getSimulatorTime()));
  128.                 }
  129.                 count++;
  130.             }
  131.             catch (RemoteException | GTUException exception)
  132.             {
  133.                 throw new RuntimeException("Position cannot be obtained for GTU that is registered on a lane", exception);
  134.             }
  135.         }
  136.     }

  137.     /** {@inheritDoc} */
  138.     @Override
  139.     public final void finalizeRecording(final KpiLaneDirection kpiLaneDirection)
  140.     {
  141.         Lane lane = ((LaneData) kpiLaneDirection.getLaneData()).getLane();
  142.         lane.removeListener(this, Lane.GTU_ADD_EVENT);
  143.         lane.removeListener(this, Lane.GTU_REMOVE_EVENT);
  144.         // Lane lane = ((LaneData) kpiLaneDirection.getLaneData()).getLane();
  145.         // int count = 0;
  146.         // List<LaneBasedGTU> currentGtus = new ArrayList<>();
  147.         // try
  148.         // {
  149.         // for (LaneBasedGTU gtu : lane.getGtuList())
  150.         // {
  151.         // DirectedLanePosition dlp = gtu.getReferencePosition();
  152.         // if (dlp.getLane().equals(lane) && sameDirection(kpiLaneDirection.getKpiDirection(), dlp.getGtuDirection()))
  153.         // {
  154.         // currentGtus.add(gtu);
  155.         // count++;
  156.         // }
  157.         // }
  158.         // for (LaneBasedGTU gtu : currentGtus)
  159.         // {
  160.         // // Payload: Object[] {String gtuId, LaneBasedGTU gtu, int count_after_removal, Length position}
  161.         // notify(new TimedEvent<>(Lane.GTU_REMOVE_EVENT, lane, new Object[] { gtu.getId(), gtu, count },
  162.         // gtu.getSimulator().getSimulatorTime()));
  163.         // count--;
  164.         // }
  165.         // }
  166.         // catch (RemoteException | GTUException exception)
  167.         // {
  168.         // throw new RuntimeException("Position cannot be obtained for GTU that is registered on a lane", exception);
  169.         // }
  170.     }

  171.     /**
  172.      * Compares a {@link KpiGtuDirectionality} and a {@link GTUDirectionality}.
  173.      * @param kpiGtuDirectionality kpi gtu direction
  174.      * @param gtuDirectionality gtu direction
  175.      * @return whether both are in the same direction
  176.      */
  177.     private boolean sameDirection(final KpiGtuDirectionality kpiGtuDirectionality, final GTUDirectionality gtuDirectionality)
  178.     {
  179.         if (kpiGtuDirectionality.equals(KpiGtuDirectionality.DIR_PLUS))
  180.         {
  181.             return gtuDirectionality.equals(GTUDirectionality.DIR_PLUS);
  182.         }
  183.         return gtuDirectionality.equals(GTUDirectionality.DIR_MINUS);
  184.     }

  185.     /** {@inheritDoc} */
  186.     @Override
  187.     public final void notify(final EventInterface event) throws RemoteException
  188.     {
  189.         if (event.getType().equals(LaneBasedGTU.LANEBASED_MOVE_EVENT))
  190.         {
  191.             // Payload: [String gtuId, DirectedPoint position, Speed speed, Acceleration acceleration, TurnIndicatorStatus
  192.             // turnIndicatorStatus, Length odometer, Lane referenceLane, Length positionOnReferenceLane]
  193.             Object[] payload = (Object[]) event.getContent();
  194.             KpiLaneDirection laneDirection =
  195.                     new KpiLaneDirection(new LaneData((Lane) payload[6]), KpiGtuDirectionality.DIR_PLUS);
  196.             processGtuMoveEvent(laneDirection, (Length) payload[7], (Speed) payload[2], (Acceleration) payload[3], now(),
  197.                     new GtuData((LaneBasedGTU) event.getSource()));
  198.         }
  199.         else if (event.getType().equals(Lane.GTU_ADD_EVENT))
  200.         {
  201.             // Payload: Object[] {String gtuId, LaneBasedGTU gtu, int count_after_addition}
  202.             Lane lane = (Lane) event.getSource();
  203.             // TODO GTUDirectionality from Lane.GTU_ADD_EVENT
  204.             KpiLaneDirection laneDirection = new KpiLaneDirection(new LaneData(lane), KpiGtuDirectionality.DIR_PLUS);
  205.             if (!contains(laneDirection))
  206.             {
  207.                 // we are not sampling this LaneDirection
  208.                 return;
  209.             }
  210.             Object[] payload = (Object[]) event.getContent();
  211.             LaneBasedGTU gtu = (LaneBasedGTU) payload[1];
  212.             Length position;
  213.             try
  214.             {
  215.                 // TODO Length from Lane.GTU_ADD_EVENT
  216.                 position = gtu.position(lane, RelativePosition.REFERENCE_POSITION);
  217.             }
  218.             catch (GTUException exception)
  219.             {
  220.                 throw new RuntimeException(exception);
  221.             }
  222.             Speed speed = gtu.getSpeed();
  223.             Acceleration acceleration = gtu.getAcceleration();
  224.             processGtuAddEvent(laneDirection, position, speed, acceleration, now(), new GtuData(gtu));
  225.             LaneDirection lDirection = new LaneDirection(lane, GTUDirectionality.DIR_PLUS);
  226.             if (isIntervalBased())
  227.             {
  228.                 scheduleSamplingEvent(gtu, lDirection);
  229.             }
  230.             else
  231.             {
  232.                 if (!this.listenersPerGtu.containsKey(gtu.getId()))
  233.                 {
  234.                     this.listenersPerGtu.put(gtu.getId(), new HashSet<>());
  235.                 }
  236.                 this.listenersPerGtu.get(gtu.getId()).add(lDirection);
  237.                 gtu.addListener(this, LaneBasedGTU.LANEBASED_MOVE_EVENT, true);
  238.             }
  239.         }
  240.         else if (event.getType().equals(Lane.GTU_REMOVE_EVENT))
  241.         {
  242.             // Payload: Object[] {String gtuId, LaneBasedGTU gtu, int count_after_removal, Length position}
  243.             Lane lane = (Lane) event.getSource();
  244.             // TODO GTUDirectionality from Lane.GTU_ADD_EVENT
  245.             KpiLaneDirection kpiLaneDirection = new KpiLaneDirection(new LaneData(lane), KpiGtuDirectionality.DIR_PLUS);
  246.             Object[] payload = (Object[]) event.getContent();
  247.             LaneBasedGTU gtu = (LaneBasedGTU) payload[1];
  248.             Length position = (Length) payload[3];
  249.             Speed speed = gtu.getSpeed();
  250.             Acceleration acceleration = gtu.getAcceleration();
  251.             processGtuRemoveEvent(kpiLaneDirection, position, speed, acceleration, now(), new GtuData(gtu));
  252.             LaneDirection lDirection = new LaneDirection(lane, GTUDirectionality.DIR_PLUS);
  253.             if (isIntervalBased())
  254.             {
  255.                 String gtuId = (String) payload[0];

  256.                 if (this.eventPerGtu.get(gtuId) != null)
  257.                 {
  258.                     if (this.eventPerGtu.get(gtuId).containsKey(lDirection))
  259.                     {
  260.                         this.simulator.cancelEvent(this.eventPerGtu.get(gtuId).get(lDirection));
  261.                     }
  262.                     this.eventPerGtu.get(gtuId).remove(lDirection);
  263.                     if (this.eventPerGtu.get(gtuId).isEmpty())
  264.                     {
  265.                         this.eventPerGtu.remove(gtuId);
  266.                     }
  267.                 }
  268.             }
  269.             else
  270.             {
  271.                 // Should not remove if just added on other lane
  272.                 this.listenersPerGtu.get(gtu.getId()).remove(lDirection);
  273.                 if (this.listenersPerGtu.get(gtu.getId()).isEmpty())
  274.                 {
  275.                     this.listenersPerGtu.remove(gtu.getId());
  276.                     gtu.removeListener(this, LaneBasedGTU.LANEBASED_MOVE_EVENT);
  277.                 }
  278.             }
  279.         }

  280.     }

  281.     /**
  282.      * @return whether sampling is interval based
  283.      */
  284.     private boolean isIntervalBased()
  285.     {
  286.         return this.samplingInterval != null;
  287.     }

  288.     /**
  289.      * Schedules a sampling event for the given gtu on the given lane for the sampling interval from the current time.
  290.      * @param gtu gtu to sample
  291.      * @param laneDirection lane direction where the gtu is at
  292.      */
  293.     private void scheduleSamplingEvent(final LaneBasedGTU gtu, final LaneDirection laneDirection)
  294.     {
  295.         SimEventInterface<SimTimeDoubleUnit> simEvent;
  296.         try
  297.         {
  298.             // this.simulator.scheduleEvent(simEvent);
  299.             simEvent = this.simulator.scheduleEventRel(this.samplingInterval, this, this, "notifySample",
  300.                     new Object[] { gtu, laneDirection });
  301.         }
  302.         catch (SimRuntimeException exception)
  303.         {
  304.             // should not happen with getSimulatorTime.add()
  305.             throw new RuntimeException("Scheduling sampling in the past.", exception);
  306.         }
  307.         String gtuId = gtu.getId();
  308.         if (!this.eventPerGtu.containsKey(gtuId))
  309.         {
  310.             Map<LaneDirection, SimEventInterface<SimTimeDoubleUnit>> map = new HashMap<>();
  311.             this.eventPerGtu.put(gtuId, map);
  312.         }
  313.         this.eventPerGtu.get(gtuId).put(laneDirection, simEvent);
  314.     }

  315.     /**
  316.      * Samples a gtu and schedules the next sampling event.
  317.      * @param gtu gtu to sample
  318.      * @param laneDirection lane direction where the gtu is at
  319.      */
  320.     public final void notifySample(final LaneBasedGTU gtu, final LaneDirection laneDirection)
  321.     {
  322.         KpiLaneDirection kpiLaneDirection = new KpiLaneDirection(new LaneData(laneDirection.getLane()),
  323.                 laneDirection.getDirection().isPlus() ? KpiGtuDirectionality.DIR_PLUS : KpiGtuDirectionality.DIR_MINUS);
  324.         try
  325.         {
  326.             this.processGtuMoveEvent(kpiLaneDirection,
  327.                     gtu.position(laneDirection.getLane(), RelativePosition.REFERENCE_POSITION), gtu.getSpeed(),
  328.                     gtu.getAcceleration(), now(), new GtuData(gtu));
  329.         }
  330.         catch (GTUException exception)
  331.         {
  332.             throw new RuntimeException("Requesting position on lane, but the GTU is not on the lane.", exception);
  333.         }
  334.         scheduleSamplingEvent(gtu, laneDirection);
  335.     }

  336.     /** {@inheritDoc} */
  337.     @Override
  338.     public final int hashCode()
  339.     {
  340.         final int prime = 31;
  341.         int result = super.hashCode();
  342.         result = prime * result + ((this.eventPerGtu == null) ? 0 : this.eventPerGtu.hashCode());
  343.         result = prime * result + ((this.samplingInterval == null) ? 0 : this.samplingInterval.hashCode());
  344.         result = prime * result + ((this.simulator == null) ? 0 : this.simulator.hashCode());
  345.         return result;
  346.     }

  347.     /** {@inheritDoc} */
  348.     @Override
  349.     public final boolean equals(final Object obj)
  350.     {
  351.         if (this == obj)
  352.         {
  353.             return true;
  354.         }
  355.         if (!super.equals(obj))
  356.         {
  357.             return false;
  358.         }
  359.         if (getClass() != obj.getClass())
  360.         {
  361.             return false;
  362.         }
  363.         RoadSampler other = (RoadSampler) obj;
  364.         if (this.eventPerGtu == null)
  365.         {
  366.             if (other.eventPerGtu != null)
  367.             {
  368.                 return false;
  369.             }
  370.         }
  371.         else if (!this.eventPerGtu.equals(other.eventPerGtu))
  372.         {
  373.             return false;
  374.         }
  375.         if (this.samplingInterval == null)
  376.         {
  377.             if (other.samplingInterval != null)
  378.             {
  379.                 return false;
  380.             }
  381.         }
  382.         else if (!this.samplingInterval.equals(other.samplingInterval))
  383.         {
  384.             return false;
  385.         }
  386.         if (this.simulator == null)
  387.         {
  388.             if (other.simulator != null)
  389.             {
  390.                 return false;
  391.             }
  392.         }
  393.         else if (!this.simulator.equals(other.simulator))
  394.         {
  395.             return false;
  396.         }
  397.         return true;
  398.     }

  399.     /** {@inheritDoc} */
  400.     @Override
  401.     public final String toString()
  402.     {
  403.         return "RoadSampler [samplingInterval=" + this.samplingInterval + ", eventPerGtu=" + this.eventPerGtu + "]";
  404.     }

  405. }