package org.opentrafficsim.demo;
import java.io.Serializable;
import java.rmi.RemoteException;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import javax.naming.NamingException;
import org.djunits.unit.DirectionUnit;
import org.djunits.unit.DurationUnit;
import org.djunits.unit.LengthUnit;
import org.djunits.unit.SpeedUnit;
import org.djunits.unit.util.UNITS;
import org.djunits.value.vdouble.scalar.Direction;
import org.djunits.value.vdouble.scalar.Duration;
import org.djunits.value.vdouble.scalar.Length;
import org.djunits.value.vdouble.scalar.Speed;
import org.djunits.value.vdouble.scalar.base.DoubleScalar;
import org.opentrafficsim.base.parameters.ParameterException;
import org.opentrafficsim.core.compatibility.Compatible;
import org.opentrafficsim.core.distributions.Distribution;
import org.opentrafficsim.core.distributions.Distribution.FrequencyAndObject;
import org.opentrafficsim.core.distributions.Generator;
import org.opentrafficsim.core.distributions.ProbabilityException;
import org.opentrafficsim.core.dsol.AbstractOTSModel;
import org.opentrafficsim.core.dsol.OTSSimulatorInterface;
import org.opentrafficsim.core.geometry.OTSGeometryException;
import org.opentrafficsim.core.geometry.OTSPoint3D;
import org.opentrafficsim.core.gtu.GTUDirectionality;
import org.opentrafficsim.core.gtu.GTUException;
import org.opentrafficsim.core.gtu.GTUType;
import org.opentrafficsim.core.idgenerator.IdGenerator;
import org.opentrafficsim.core.network.NetworkException;
import org.opentrafficsim.core.network.Node;
import org.opentrafficsim.core.network.route.FixedRouteGenerator;
import org.opentrafficsim.core.network.route.Route;
import org.opentrafficsim.core.parameters.ParameterFactory;
import org.opentrafficsim.core.units.distributions.ContinuousDistDoubleScalar;
import org.opentrafficsim.draw.road.TrafficLightAnimation;
import org.opentrafficsim.road.gtu.generator.CFRoomChecker;
import org.opentrafficsim.road.gtu.generator.GeneratorPositions;
import org.opentrafficsim.road.gtu.generator.LaneBasedGTUGenerator;
import org.opentrafficsim.road.gtu.generator.characteristics.LaneBasedTemplateGTUType;
import org.opentrafficsim.road.gtu.generator.characteristics.LaneBasedTemplateGTUTypeDistribution;
import org.opentrafficsim.road.gtu.lane.tactical.following.IDMPlusFactory;
import org.opentrafficsim.road.gtu.lane.tactical.lmrs.DefaultLMRSPerceptionFactory;
import org.opentrafficsim.road.gtu.lane.tactical.lmrs.LMRSFactory;
import org.opentrafficsim.road.gtu.strategical.LaneBasedStrategicalPlanner;
import org.opentrafficsim.road.gtu.strategical.LaneBasedStrategicalPlannerFactory;
import org.opentrafficsim.road.gtu.strategical.route.LaneBasedStrategicalRoutePlannerFactory;
import org.opentrafficsim.road.network.OTSRoadNetwork;
import org.opentrafficsim.road.network.factory.LaneFactory;
import org.opentrafficsim.road.network.lane.DirectedLanePosition;
import org.opentrafficsim.road.network.lane.Lane;
import org.opentrafficsim.road.network.lane.LaneType;
import org.opentrafficsim.road.network.lane.OTSRoadNode;
import org.opentrafficsim.road.network.lane.object.sensor.SinkSensor;
import org.opentrafficsim.road.network.lane.object.trafficlight.SimpleTrafficLight;
import org.opentrafficsim.road.network.lane.object.trafficlight.TrafficLight;
import org.opentrafficsim.road.network.lane.object.trafficlight.TrafficLightColor;
import nl.tudelft.simulation.dsol.SimRuntimeException;
import nl.tudelft.simulation.dsol.model.inputparameters.InputParameterDouble;
import nl.tudelft.simulation.dsol.model.inputparameters.InputParameterException;
import nl.tudelft.simulation.dsol.model.inputparameters.InputParameterMap;
import nl.tudelft.simulation.jstats.distributions.DistContinuous;
import nl.tudelft.simulation.jstats.distributions.DistErlang;
import nl.tudelft.simulation.jstats.distributions.DistUniform;
import nl.tudelft.simulation.jstats.streams.MersenneTwister;
import nl.tudelft.simulation.jstats.streams.StreamInterface;
/**
* Simulate four double lane roads with a crossing in the middle.
*
* Copyright (c) 2013-2022 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
* BSD-style license. See OpenTrafficSim License.
*
* $LastChangedDate: 2016-10-28 16:34:11 +0200 (Fri, 28 Oct 2016) $, @version $Revision: 2429 $, by $Author: pknoppers $,
* initial version ug 1, 2014
* @author Peter Knoppers
*/
public class CrossingTrafficLightsModel extends AbstractOTSModel implements UNITS
{
/** */
private static final long serialVersionUID = 20140815L;
/** The network. */
private final OTSRoadNetwork network;
/** the random stream for this demo. */
private StreamInterface stream = new MersenneTwister(555);
/** Id generator (used by all generators). */
private IdGenerator idGenerator = new IdGenerator("");
/** The probability distribution for the variable part of the headway. */
@SuppressWarnings("checkstyle:visibilitymodifier")
protected DistContinuous headwayGenerator;
/** The probability that the next generated GTU is a passenger car. */
private double carProbability;
/** the strategical planner factory for cars. */
private LaneBasedStrategicalPlannerFactory strategicalPlannerFactoryCar;
/** the strategical planner factory for trucks. */
private LaneBasedStrategicalPlannerFactory strategicalPlannerFactoryTruck;
/** The speed limit on all Lanes. */
private Speed speedLimit = new Speed(80, KM_PER_HOUR);
/** Fixed green time. */
private static final Duration TGREEN = new Duration(39.0, DurationUnit.SI);
/** Fixed yellow time. */
private static final Duration TYELLOW = new Duration(6.0, DurationUnit.SI);
/** Fixed red time. */
private static final Duration TRED = new Duration(45.0, DurationUnit.SI);
/**
* @param simulator OTSSimulatorInterface; the simulator for this model
*/
public CrossingTrafficLightsModel(final OTSSimulatorInterface simulator)
{
super(simulator);
this.network = new OTSRoadNetwork("network", true, simulator);
createInputParameters();
}
/**
* Create input parameters for the networks demo.
*/
private void createInputParameters()
{
try
{
InputParameterHelper.makeInputParameterMapCarTruck(this.inputParameterMap, 1.0);
InputParameterMap genericMap = (InputParameterMap) this.inputParameterMap.get("generic");
genericMap.add(new InputParameterDouble("flow", "Flow per input lane", "Traffic flow per input lane", 250d, 0d,
400d, true, true, "%.0f veh/h", 1.5));
}
catch (InputParameterException e)
{
e.printStackTrace();
}
}
/** {@inheritDoc} */
@Override
public final void constructModel() throws SimRuntimeException
{
try
{
OTSRoadNode[][] nodes = new OTSRoadNode[4][4];
nodes[0][0] = new OTSRoadNode(this.network, "sn1", new OTSPoint3D(10, -500),
new Direction(90, DirectionUnit.EAST_DEGREE));
nodes[0][1] =
new OTSRoadNode(this.network, "sn2", new OTSPoint3D(10, -20), new Direction(90, DirectionUnit.EAST_DEGREE));
nodes[0][2] =
new OTSRoadNode(this.network, "sn3", new OTSPoint3D(10, +20), new Direction(90, DirectionUnit.EAST_DEGREE));
nodes[0][3] = new OTSRoadNode(this.network, "sn4", new OTSPoint3D(10, +600),
new Direction(90, DirectionUnit.EAST_DEGREE));
nodes[1][0] = new OTSRoadNode(this.network, "we1", new OTSPoint3D(-500, -10), Direction.ZERO);
nodes[1][1] = new OTSRoadNode(this.network, "we2", new OTSPoint3D(-20, -10), Direction.ZERO);
nodes[1][2] = new OTSRoadNode(this.network, "we3", new OTSPoint3D(+20, -10), Direction.ZERO);
nodes[1][3] = new OTSRoadNode(this.network, "we4", new OTSPoint3D(+600, -10), Direction.ZERO);
nodes[2][0] = new OTSRoadNode(this.network, "ns1", new OTSPoint3D(-10, +500),
new Direction(270, DirectionUnit.EAST_DEGREE));
nodes[2][1] = new OTSRoadNode(this.network, "ns2", new OTSPoint3D(-10, +20),
new Direction(270, DirectionUnit.EAST_DEGREE));
nodes[2][2] = new OTSRoadNode(this.network, "ns3", new OTSPoint3D(-10, -20),
new Direction(270, DirectionUnit.EAST_DEGREE));
nodes[2][3] = new OTSRoadNode(this.network, "ns4", new OTSPoint3D(-10, -600),
new Direction(270, DirectionUnit.EAST_DEGREE));
nodes[3][0] = new OTSRoadNode(this.network, "ew1", new OTSPoint3D(+500, 10),
new Direction(180, DirectionUnit.EAST_DEGREE));
nodes[3][1] = new OTSRoadNode(this.network, "ew2", new OTSPoint3D(+20, 10),
new Direction(180, DirectionUnit.EAST_DEGREE));
nodes[3][2] = new OTSRoadNode(this.network, "ew3", new OTSPoint3D(-20, 10),
new Direction(180, DirectionUnit.EAST_DEGREE));
nodes[3][3] = new OTSRoadNode(this.network, "ew4", new OTSPoint3D(-600, 10),
new Direction(180, DirectionUnit.EAST_DEGREE));
LaneType laneType = this.network.getLaneType(LaneType.DEFAULTS.TWO_WAY_LANE);
Map trafficLights = new LinkedHashMap<>();
this.carProbability = (double) getInputParameter("generic.carProbability");
ParameterFactory params = new InputParameterHelper(getInputParameterMap());
this.strategicalPlannerFactoryCar = new LaneBasedStrategicalRoutePlannerFactory(
new LMRSFactory(new IDMPlusFactory(this.stream), new DefaultLMRSPerceptionFactory()), params);
this.strategicalPlannerFactoryTruck = new LaneBasedStrategicalRoutePlannerFactory(
new LMRSFactory(new IDMPlusFactory(this.stream), new DefaultLMRSPerceptionFactory()), params);
this.idGenerator = new IdGenerator("");
double contP = (double) getInputParameter("generic.flow");
Duration averageHeadway = new Duration(3600.0 / contP, SECOND);
Duration minimumHeadway = new Duration(3, SECOND);
this.headwayGenerator =
new DistErlang(new MersenneTwister(1234), DoubleScalar.minus(averageHeadway, minimumHeadway).getSI(), 4);
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 3; j++)
{
Lane[] lanes = LaneFactory.makeMultiLane(this.network,
"Lane_" + nodes[i][j].getId() + "-" + nodes[i][j + 1].getId(), nodes[i][j], nodes[i][j + 1], null,
2, laneType, this.speedLimit, this.simulator);
if (j == 0)
{
for (Lane lane : lanes)
{
// make a generator for the lane
Generator routeGenerator = new FixedRouteGenerator(new Route("main",
Arrays.asList(new Node[] {nodes[i][0], nodes[i][1], nodes[i][2], nodes[i][3]})));
makeGenerator(lane, routeGenerator);
// add the traffic light
SimpleTrafficLight tl = new SimpleTrafficLight(lane.getId() + "_TL", lane,
new Length(lane.getLength().minus(new Length(10.0, LengthUnit.METER))), this.simulator);
trafficLights.put(lane, tl);
try
{
new TrafficLightAnimation(tl, this.simulator);
}
catch (RemoteException | NamingException exception)
{
throw new NetworkException(exception);
}
if (i == 0 || i == 2)
{
this.simulator.scheduleEventRel(Duration.ZERO, this, this, "changeTL", new Object[] {tl});
}
else
{
this.simulator.scheduleEventRel(TRED, this, this, "changeTL", new Object[] {tl});
}
}
}
if (j == 2)
{
for (Lane lane : lanes)
{
new SinkSensor(lane, new Length(500.0, METER), Compatible.EVERYTHING, this.simulator);
}
}
}
}
}
catch (SimRuntimeException | NamingException | NetworkException | OTSGeometryException | GTUException
| InputParameterException | ProbabilityException | ParameterException exception)
{
exception.printStackTrace();
}
}
/**
* Build a generator.
* @param lane Lane; the lane on which the generated GTUs are placed
* @param routeGenerator the fixed route for this lane
* @return LaneBasedGTUGenerator
* @throws GTUException when lane position out of bounds
* @throws SimRuntimeException when generation scheduling fails
* @throws ProbabilityException when probability distribution is wrong
* @throws ParameterException when a parameter is missing for the perception of the GTU
* @throws InputParameterException when a parameter is missing for the perception of the GTU
*/
private LaneBasedGTUGenerator makeGenerator(final Lane lane, final Generator routeGenerator)
throws GTUException, SimRuntimeException, ProbabilityException, ParameterException, InputParameterException
{
Distribution distribution = new Distribution<>(this.stream);
Length initialPosition = new Length(16, METER);
Set initialPositions = new LinkedHashSet<>(1);
initialPositions.add(new DirectedLanePosition(lane, initialPosition, GTUDirectionality.DIR_PLUS));
LaneBasedTemplateGTUType template = makeTemplate(this.stream, lane,
new ContinuousDistDoubleScalar.Rel(new DistUniform(this.stream, 3, 6), METER),
new ContinuousDistDoubleScalar.Rel(new DistUniform(this.stream, 1.6, 2.0), METER),
new ContinuousDistDoubleScalar.Rel(new DistUniform(this.stream, 140, 180), KM_PER_HOUR),
initialPositions, this.strategicalPlannerFactoryCar, routeGenerator);
// System.out.println("Constructed template " + template);
distribution.add(new FrequencyAndObject<>(this.carProbability, template));
template = makeTemplate(this.stream, lane,
new ContinuousDistDoubleScalar.Rel(new DistUniform(this.stream, 8, 14), METER),
new ContinuousDistDoubleScalar.Rel(new DistUniform(this.stream, 2.0, 2.5), METER),
new ContinuousDistDoubleScalar.Rel(new DistUniform(this.stream, 100, 140), KM_PER_HOUR),
initialPositions, this.strategicalPlannerFactoryTruck, routeGenerator);
// System.out.println("Constructed template " + template);
distribution.add(new FrequencyAndObject<>(1.0 - this.carProbability, template));
LaneBasedTemplateGTUTypeDistribution templateDistribution = new LaneBasedTemplateGTUTypeDistribution(distribution);
LaneBasedGTUGenerator.RoomChecker roomChecker = new CFRoomChecker();
return new LaneBasedGTUGenerator(lane.getId(), new Generator()
{
@Override
public Duration draw()
{
return new Duration(CrossingTrafficLightsModel.this.headwayGenerator.draw(), DurationUnit.SI);
}
}, templateDistribution, GeneratorPositions.create(initialPositions, this.stream), this.network, this.simulator,
roomChecker, this.idGenerator);
}
/**
* @param randStream StreamInterface; the random stream to use
* @param lane Lane; reference lane to generate GTUs on
* @param lengthDistribution ContinuousDistDoubleScalar.Rel<Length,LengthUnit>; distribution of the GTU length
* @param widthDistribution ContinuousDistDoubleScalar.Rel<Length,LengthUnit>; distribution of the GTU width
* @param maximumSpeedDistribution ContinuousDistDoubleScalar.Rel<Speed,SpeedUnit>; distribution of the GTU's maximum
* speed
* @param initialPositions Set<DirectedLanePosition>; initial position(s) of the GTU on the Lane(s)
* @param strategicalPlannerFactory LaneBasedStrategicalPlannerFactory<LaneBasedStrategicalPlanner>; factory to
* generate the strategical planner for the GTU
* @param routeGenerator the route generator
* @return template for a GTU
* @throws GTUException when characteristics cannot be initialized
*/
@SuppressWarnings("checkstyle:parameternumber")
LaneBasedTemplateGTUType makeTemplate(final StreamInterface randStream, final Lane lane,
final ContinuousDistDoubleScalar.Rel lengthDistribution,
final ContinuousDistDoubleScalar.Rel widthDistribution,
final ContinuousDistDoubleScalar.Rel maximumSpeedDistribution,
final Set initialPositions,
final LaneBasedStrategicalPlannerFactory strategicalPlannerFactory,
final Generator routeGenerator) throws GTUException
{
return new LaneBasedTemplateGTUType(this.network.getGtuType(GTUType.DEFAULTS.CAR), new Generator()
{
@Override
public Length draw()
{
return lengthDistribution.draw();
}
}, new Generator()
{
@Override
public Length draw()
{
return widthDistribution.draw();
}
}, new Generator()
{
@Override
public Speed draw()
{
return maximumSpeedDistribution.draw();
}
}, strategicalPlannerFactory, routeGenerator);
}
/**
* Change the traffic light to a new color.
* @param tl TrafficLight; the traffic light
* @throws SimRuntimeException when scheduling fails
*/
protected final void changeTL(final TrafficLight tl) throws SimRuntimeException
{
if (tl.getTrafficLightColor().isRed())
{
tl.setTrafficLightColor(TrafficLightColor.GREEN);
this.simulator.scheduleEventRel(TGREEN, this, this, "changeTL", new Object[] {tl});
}
else if (tl.getTrafficLightColor().isGreen())
{
tl.setTrafficLightColor(TrafficLightColor.YELLOW);
this.simulator.scheduleEventRel(TYELLOW, this, this, "changeTL", new Object[] {tl});
}
else if (tl.getTrafficLightColor().isYellow())
{
tl.setTrafficLightColor(TrafficLightColor.RED);
this.simulator.scheduleEventRel(TRED, this, this, "changeTL", new Object[] {tl});
}
}
/** {@inheritDoc} */
@Override
public OTSRoadNetwork getNetwork()
{
return this.network;
}
/** {@inheritDoc} */
@Override
public Serializable getSourceId()
{
return "CrossingTrafficLightsModel";
}
}