View Javadoc
1   package org.opentrafficsim.demo;
2   
3   import java.rmi.RemoteException;
4   import java.util.ArrayList;
5   import java.util.Collections;
6   import java.util.LinkedHashMap;
7   import java.util.LinkedHashSet;
8   import java.util.List;
9   import java.util.Map;
10  import java.util.Optional;
11  import java.util.Random;
12  import java.util.Set;
13  import java.util.function.Supplier;
14  
15  import org.djunits.unit.AccelerationUnit;
16  import org.djunits.unit.DurationUnit;
17  import org.djunits.unit.FrequencyUnit;
18  import org.djunits.unit.LengthUnit;
19  import org.djunits.unit.SpeedUnit;
20  import org.djunits.value.vdouble.scalar.Acceleration;
21  import org.djunits.value.vdouble.scalar.Duration;
22  import org.djunits.value.vdouble.scalar.Frequency;
23  import org.djunits.value.vdouble.scalar.Length;
24  import org.djunits.value.vdouble.scalar.Speed;
25  import org.djunits.value.vdouble.scalar.Time;
26  import org.djutils.exceptions.Try;
27  import org.opentrafficsim.animation.GraphLaneUtil;
28  import org.opentrafficsim.animation.gtu.colorer.IncentiveGtuColorer;
29  import org.opentrafficsim.animation.gtu.colorer.SynchronizationGtuColorer;
30  import org.opentrafficsim.base.OtsRuntimeException;
31  import org.opentrafficsim.base.parameters.ParameterException;
32  import org.opentrafficsim.base.parameters.ParameterTypes;
33  import org.opentrafficsim.core.definitions.DefaultsNl;
34  import org.opentrafficsim.core.distributions.ConstantSupplier;
35  import org.opentrafficsim.core.distributions.FrequencyAndObject;
36  import org.opentrafficsim.core.distributions.ObjectDistribution;
37  import org.opentrafficsim.core.dsol.OtsAnimator;
38  import org.opentrafficsim.core.dsol.OtsSimulatorInterface;
39  import org.opentrafficsim.core.gtu.Gtu;
40  import org.opentrafficsim.core.gtu.GtuException;
41  import org.opentrafficsim.core.gtu.GtuType;
42  import org.opentrafficsim.core.idgenerator.IdSupplier;
43  import org.opentrafficsim.core.network.Link;
44  import org.opentrafficsim.core.network.NetworkException;
45  import org.opentrafficsim.core.network.Node;
46  import org.opentrafficsim.core.network.route.ProbabilisticRouteGenerator;
47  import org.opentrafficsim.core.network.route.Route;
48  import org.opentrafficsim.core.parameters.ParameterFactory;
49  import org.opentrafficsim.core.parameters.ParameterFactoryByType;
50  import org.opentrafficsim.core.units.distributions.ContinuousDistDoubleScalar;
51  import org.opentrafficsim.demo.ShortMerge.ShortMergeModel;
52  import org.opentrafficsim.draw.colorer.Colorer;
53  import org.opentrafficsim.draw.colorer.trajectory.SynchronizationTrajectoryColorer;
54  import org.opentrafficsim.draw.graphs.GraphPath;
55  import org.opentrafficsim.draw.graphs.PlotScheduler;
56  import org.opentrafficsim.draw.graphs.TrajectoryPlot;
57  import org.opentrafficsim.draw.gtu.DefaultCarAnimation.GtuData.GtuMarker;
58  import org.opentrafficsim.kpi.sampling.data.ExtendedDataString;
59  import org.opentrafficsim.road.gtu.generator.GeneratorPositions;
60  import org.opentrafficsim.road.gtu.generator.LaneBasedGtuGenerator;
61  import org.opentrafficsim.road.gtu.generator.LaneBasedGtuGenerator.RoomChecker;
62  import org.opentrafficsim.road.gtu.generator.TtcRoomChecker;
63  import org.opentrafficsim.road.gtu.generator.characteristics.LaneBasedGtuTemplate;
64  import org.opentrafficsim.road.gtu.generator.characteristics.LaneBasedGtuTemplateDistribution;
65  import org.opentrafficsim.road.gtu.generator.headway.HeadwayGenerator;
66  import org.opentrafficsim.road.gtu.lane.LaneBookkeeping;
67  import org.opentrafficsim.road.gtu.lane.tactical.LaneBasedTacticalPlannerFactory;
68  import org.opentrafficsim.road.gtu.lane.tactical.Synchronizable;
69  import org.opentrafficsim.road.gtu.lane.tactical.lmrs.IncentiveCourtesy;
70  import org.opentrafficsim.road.gtu.lane.tactical.lmrs.Lmrs;
71  import org.opentrafficsim.road.gtu.lane.tactical.lmrs.LmrsFactory;
72  import org.opentrafficsim.road.gtu.lane.tactical.lmrs.LmrsFactory.Setting;
73  import org.opentrafficsim.road.gtu.lane.tactical.util.lmrs.Cooperation;
74  import org.opentrafficsim.road.gtu.lane.tactical.util.lmrs.LmrsParameters;
75  import org.opentrafficsim.road.gtu.lane.tactical.util.lmrs.Synchronization;
76  import org.opentrafficsim.road.gtu.lane.tactical.util.lmrs.Tailgating;
77  import org.opentrafficsim.road.gtu.strategical.LaneBasedStrategicalRoutePlannerFactory;
78  import org.opentrafficsim.road.network.factory.xml.OtsXmlModel;
79  import org.opentrafficsim.road.network.lane.CrossSectionLink;
80  import org.opentrafficsim.road.network.lane.Lane;
81  import org.opentrafficsim.road.network.lane.LanePosition;
82  import org.opentrafficsim.road.network.lane.object.SpeedSign;
83  import org.opentrafficsim.road.network.sampling.GtuDataRoad;
84  import org.opentrafficsim.road.network.sampling.LaneDataRoad;
85  import org.opentrafficsim.road.network.sampling.RoadSampler;
86  import org.opentrafficsim.swing.graphs.OtsPlotScheduler;
87  import org.opentrafficsim.swing.graphs.SwingTrajectoryPlot;
88  import org.opentrafficsim.swing.gui.AnimationToggles;
89  import org.opentrafficsim.swing.gui.OtsAnimationPanel;
90  import org.opentrafficsim.swing.gui.OtsSimulationApplication;
91  
92  import nl.tudelft.simulation.dsol.SimRuntimeException;
93  import nl.tudelft.simulation.jstats.distributions.DistNormal;
94  import nl.tudelft.simulation.jstats.distributions.DistUniform;
95  import nl.tudelft.simulation.jstats.streams.MersenneTwister;
96  import nl.tudelft.simulation.jstats.streams.StreamInterface;
97  import nl.tudelft.simulation.language.DsolException;
98  
99  /**
100  * <p>
101  * Copyright (c) 2013-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
102  * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
103  * </p>
104  * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
105  * @author <a href="https://github.com/peter-knoppers">Peter Knoppers</a>
106  * @author <a href="https://github.com/wjschakel">Wouter Schakel</a>
107  */
108 public class ShortMerge extends OtsSimulationApplication<ShortMergeModel>
109 {
110     /** */
111     private static final long serialVersionUID = 20170407L;
112 
113     /** Network. */
114     static final String NETWORK = "shortMerge";
115 
116     /** Truck fraction. */
117     static final double TRUCK_FRACTION = 0.15;
118 
119     /** Left traffic fraction. */
120     static final double LEFT_FRACTION = 0.3;
121 
122     /** Main demand per lane. */
123     static final Frequency MAIN_DEMAND = new Frequency(1000, FrequencyUnit.PER_HOUR);
124 
125     /** Ramp demand. */
126     static final Frequency RAMP_DEMAND = new Frequency(500, FrequencyUnit.PER_HOUR);
127 
128     /** Synchronization. */
129     static final Synchronization SYNCHRONIZATION = Synchronization.ALIGN_GAP;
130 
131     /** Cooperation. */
132     static final Cooperation COOPERATION = Cooperation.PASSIVE_MOVING;
133 
134     /** Use additional incentives. */
135     static final boolean ADDITIONAL_INCENTIVES = true;
136 
137     /** Simulation time. */
138     public static final Time SIMTIME = Time.ofSI(3600);
139 
140     /**
141      * Create a ShortMerge Swing application.
142      * @param title the title of the Frame
143      * @param panel the tabbed panel to display
144      * @param model the model
145      */
146     public ShortMerge(final String title, final OtsAnimationPanel panel, final ShortMergeModel model)
147     {
148         super(model, panel, Map.of(DefaultsNl.TRUCK, GtuMarker.SQUARE));
149     }
150 
151     @Override
152     protected void setAnimationToggles()
153     {
154         AnimationToggles.setTextAnimationTogglesStandard(getAnimationPanel());
155         getAnimationPanel().getAnimationPanel().toggleClass(Link.class);
156         getAnimationPanel().getAnimationPanel().toggleClass(Node.class);
157         getAnimationPanel().getAnimationPanel().showClass(SpeedSign.class);
158     }
159 
160     @Override
161     protected void addTabs()
162     {
163         GraphPath<LaneDataRoad> path;
164         try
165         {
166             Lane start = ((CrossSectionLink) getModel().getNetwork().getLink("AB").get()).getLanes().get(1);
167             path = GraphLaneUtil.createPath("Right lane", start);
168         }
169         catch (NetworkException exception)
170         {
171             throw new OtsRuntimeException("Could not create a path as a lane has no set speed limit.", exception);
172         }
173         ExtendedDataSync<GtuDataRoad> syncData = new ExtendedDataSync<GtuDataRoad>();
174         RoadSampler sampler = new RoadSampler(Set.of(syncData), Collections.emptySet(), getModel().getNetwork());
175         GraphPath.initRecording(sampler, path);
176         PlotScheduler scheduler = new OtsPlotScheduler(getModel().getSimulator());
177         Duration updateInterval = Duration.ofSI(10.0);
178         SwingTrajectoryPlot plot = new SwingTrajectoryPlot(
179                 new TrajectoryPlot("Trajectory right lane", updateInterval, scheduler, sampler.getSamplerData(), path), true);
180         plot.addColorer(new SynchronizationTrajectoryColorer(syncData), false);
181         getAnimationPanel().getTabbedPane().addTab(getAnimationPanel().getTabbedPane().getTabCount(), "Trajectories",
182                 plot.getContentPane());
183     }
184 
185     /**
186      * Main program.
187      * @param args the command line arguments (not used)
188      */
189     public static void main(final String[] args)
190     {
191         demo(true);
192     }
193 
194     /**
195      * Start the demo.
196      * @param exitOnClose when running stand-alone: true; when running as part of a demo: false
197      */
198     public static void demo(final boolean exitOnClose)
199     {
200         try
201         {
202             OtsAnimator simulator = new OtsAnimator("ShortMerge");
203             final ShortMergeModel otsModel = new ShortMergeModel(simulator);
204             List<Colorer<? super Gtu>> colorers = new ArrayList<>(DEFAULT_GTU_COLORERS);
205             colorers.add(new SynchronizationGtuColorer());
206             colorers.add(new IncentiveGtuColorer(IncentiveCourtesy.class, "Courtesy incentive"));
207             OtsAnimationPanel animationPanel = new OtsAnimationPanel(otsModel.getNetwork().getExtent(), simulator, otsModel,
208                     colorers, otsModel.getNetwork());
209             ShortMerge app = new ShortMerge("ShortMerge", animationPanel, otsModel);
210             app.setExitOnClose(exitOnClose);
211             animationPanel.enableSimulationControlButtons();
212         }
213         catch (SimRuntimeException | RemoteException | IndexOutOfBoundsException | DsolException exception)
214         {
215             exception.printStackTrace();
216         }
217     }
218 
219     /**
220      * <p>
221      * Copyright (c) 2013-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
222      * <br>
223      * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
224      * </p>
225      * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
226      * @author <a href="https://github.com/peter-knoppers">Peter Knoppers</a>
227      * @author <a href="https://github.com/wjschakel">Wouter Schakel</a>
228      */
229     public static class ShortMergeModel extends OtsXmlModel
230     {
231         /**
232          * Constructor.
233          * @param simulator the simulator
234          */
235         public ShortMergeModel(final OtsSimulatorInterface simulator)
236         {
237             super(simulator, "/resources/lmrs/" + NETWORK + ".xml");
238         }
239 
240         @Override
241         public void constructModel() throws SimRuntimeException
242         {
243             super.constructModel();
244             Try.execute(() -> addGenerator(), OtsRuntimeException.class, "Unable to add generator to network.");
245         }
246 
247         /**
248          * Create generators.
249          * @throws ParameterException on parameter exception
250          * @throws GtuException on GTU exception
251          * @throws NetworkException if not does not exist
252          * @throws SimRuntimeException in case of sim run time exception
253          */
254         private void addGenerator() throws ParameterException, GtuException, NetworkException, SimRuntimeException
255         {
256 
257             Random seedGenerator = new Random(1L);
258             Map<String, StreamInterface> streams = new LinkedHashMap<>();
259             StreamInterface stream = new MersenneTwister(Math.abs(seedGenerator.nextLong()) + 1);
260             streams.put("headwayGeneration", stream);
261             streams.put("gtuClass", new MersenneTwister(Math.abs(seedGenerator.nextLong()) + 1));
262             getStreamInformation().addStream("headwayGeneration", stream);
263             getStreamInformation().addStream("gtuClass", streams.get("gtuClass"));
264 
265             TtcRoomChecker roomChecker = new TtcRoomChecker(new Duration(10.0, DurationUnit.SI));
266             IdSupplier idGenerator = new IdSupplier("");
267 
268             LmrsFactory<Lmrs> tacticalFactory = new LmrsFactory<Lmrs>(Lmrs::new).setStream(stream)
269                     .set(Setting.SYNCHRONIZATION, SYNCHRONIZATION).set(Setting.COOPERATION, COOPERATION);
270             if (ADDITIONAL_INCENTIVES)
271             {
272                 tacticalFactory.set(Setting.INCENTIVE_COURTESY, true);
273             }
274 
275             GtuType car = DefaultsNl.CAR;
276             GtuType truck = DefaultsNl.TRUCK;
277             Route routeAE =
278                     getNetwork().getShortestRouteBetween(car, getNetwork().getNode("A").get(), getNetwork().getNode("E").get());
279             Route routeAG = !NETWORK.equals("shortWeave") ? null : getNetwork().getShortestRouteBetween(car,
280                     getNetwork().getNode("A").get(), getNetwork().getNode("G").get());
281             Route routeFE =
282                     getNetwork().getShortestRouteBetween(car, getNetwork().getNode("F").get(), getNetwork().getNode("E").get());
283             Route routeFG = !NETWORK.equals("shortWeave") ? null : getNetwork().getShortestRouteBetween(car,
284                     getNetwork().getNode("F").get(), getNetwork().getNode("G").get());
285 
286             double leftFraction = NETWORK.equals("shortWeave") ? LEFT_FRACTION : 0.0;
287             List<FrequencyAndObject<Route>> routesA = new ArrayList<>();
288             routesA.add(new FrequencyAndObject<>(1.0 - leftFraction, routeAE));
289             routesA.add(new FrequencyAndObject<>(leftFraction, routeAG));
290             List<FrequencyAndObject<Route>> routesF = new ArrayList<>();
291             routesF.add(new FrequencyAndObject<>(1.0 - leftFraction, routeFE));
292             routesF.add(new FrequencyAndObject<>(leftFraction, routeFG));
293             Supplier<Route> routeGeneratorA = new ProbabilisticRouteGenerator(routesA, stream);
294             Supplier<Route> routeGeneratorF = new ProbabilisticRouteGenerator(routesF, stream);
295 
296             Speed speedA = new Speed(120.0, SpeedUnit.KM_PER_HOUR);
297             Speed speedF = new Speed(20.0, SpeedUnit.KM_PER_HOUR);
298 
299             CrossSectionLink linkA = (CrossSectionLink) getNetwork().getLink("AB").get();
300             CrossSectionLink linkF = (CrossSectionLink) getNetwork().getLink("FF2").get();
301 
302             ParameterFactoryByType bcFactory = new ParameterFactoryByType();
303             bcFactory.addParameter(car, ParameterTypes.FSPEED, new DistNormal(stream, 123.7 / 120, 12.0 / 120));
304             bcFactory.addParameter(car, LmrsParameters.SOCIO, new DistNormal(stream, 0.5, 0.1));
305             bcFactory.addParameter(truck, ParameterTypes.A, new Acceleration(0.8, AccelerationUnit.SI));
306             bcFactory.addParameter(truck, LmrsParameters.SOCIO, new DistNormal(stream, 0.5, 0.1));
307             bcFactory.addParameter(Tailgating.RHO, Tailgating.RHO.getDefaultValue());
308 
309             Supplier<Duration> headwaysA1 = new HeadwayGenerator(MAIN_DEMAND, stream);
310             Supplier<Duration> headwaysA2 = new HeadwayGenerator(MAIN_DEMAND, stream);
311             Supplier<Duration> headwaysA3 = new HeadwayGenerator(MAIN_DEMAND, stream);
312             Supplier<Duration> headwaysF = new HeadwayGenerator(RAMP_DEMAND, stream);
313 
314             // speed generators
315             ContinuousDistDoubleScalar.Rel<Speed, SpeedUnit> speedCar =
316                     new ContinuousDistDoubleScalar.Rel<>(new DistUniform(stream, 160, 200), SpeedUnit.KM_PER_HOUR);
317             ContinuousDistDoubleScalar.Rel<Speed, SpeedUnit> speedTruck =
318                     new ContinuousDistDoubleScalar.Rel<>(new DistUniform(stream, 80, 95), SpeedUnit.KM_PER_HOUR);
319             // strategical planner factory
320             LaneBasedStrategicalRoutePlannerFactory strategicalFactory =
321                     new LaneBasedStrategicalRoutePlannerFactory(tacticalFactory, bcFactory);
322             // vehicle templates, with routes
323             LaneBasedGtuTemplate carA = new LaneBasedGtuTemplate(car, new ConstantSupplier<>(Length.ofSI(4.0)),
324                     new ConstantSupplier<>(Length.ofSI(2.0)), speedCar, strategicalFactory, routeGeneratorA);
325             LaneBasedGtuTemplate carF = new LaneBasedGtuTemplate(car, new ConstantSupplier<>(Length.ofSI(4.0)),
326                     new ConstantSupplier<>(Length.ofSI(2.0)), speedCar, strategicalFactory, routeGeneratorF);
327             LaneBasedGtuTemplate truckA = new LaneBasedGtuTemplate(truck, new ConstantSupplier<>(Length.ofSI(15.0)),
328                     new ConstantSupplier<>(Length.ofSI(2.5)), speedTruck, strategicalFactory, routeGeneratorA);
329             LaneBasedGtuTemplate truckF = new LaneBasedGtuTemplate(truck, new ConstantSupplier<>(Length.ofSI(15.0)),
330                     new ConstantSupplier<>(Length.ofSI(2.5)), speedTruck, strategicalFactory, routeGeneratorF);
331             //
332             ObjectDistribution<LaneBasedGtuTemplate> gtuTypeAllCarA = new ObjectDistribution<>(streams.get("gtuClass"));
333             gtuTypeAllCarA.add(new FrequencyAndObject<>(1.0, carA));
334 
335             ObjectDistribution<LaneBasedGtuTemplate> gtuType1LaneF = new ObjectDistribution<>(streams.get("gtuClass"));
336             gtuType1LaneF.add(new FrequencyAndObject<>(1.0 - 2 * TRUCK_FRACTION, carF));
337             gtuType1LaneF.add(new FrequencyAndObject<>(2 * TRUCK_FRACTION, truckF));
338 
339             ObjectDistribution<LaneBasedGtuTemplate> gtuType2ndLaneA = new ObjectDistribution<>(streams.get("gtuClass"));
340             gtuType2ndLaneA.add(new FrequencyAndObject<>(1.0 - 2 * TRUCK_FRACTION, carA));
341             gtuType2ndLaneA.add(new FrequencyAndObject<>(2 * TRUCK_FRACTION, truckA));
342 
343             ObjectDistribution<LaneBasedGtuTemplate> gtuType3rdLaneA = new ObjectDistribution<>(streams.get("gtuClass"));
344             gtuType3rdLaneA.add(new FrequencyAndObject<>(1.0 - 3 * TRUCK_FRACTION, carA));
345             gtuType3rdLaneA.add(new FrequencyAndObject<>(3 * TRUCK_FRACTION, truckA));
346 
347             makeGenerator(getLane(linkA, "FORWARD1"), speedA, "gen1", idGenerator, gtuTypeAllCarA, headwaysA1, roomChecker,
348                     bcFactory, tacticalFactory, SIMTIME, streams.get("gtuClass"));
349             if (NETWORK.equals("shortWeave"))
350             {
351                 makeGenerator(getLane(linkA, "FORWARD2"), speedA, "gen2", idGenerator, gtuTypeAllCarA, headwaysA2, roomChecker,
352                         bcFactory, tacticalFactory, SIMTIME, streams.get("gtuClass"));
353                 makeGenerator(getLane(linkA, "FORWARD3"), speedA, "gen3", idGenerator, gtuType3rdLaneA, headwaysA3, roomChecker,
354                         bcFactory, tacticalFactory, SIMTIME, streams.get("gtuClass"));
355             }
356             else
357             {
358                 makeGenerator(getLane(linkA, "FORWARD2"), speedA, "gen2", idGenerator, gtuType2ndLaneA, headwaysA2, roomChecker,
359                         bcFactory, tacticalFactory, SIMTIME, streams.get("gtuClass"));
360             }
361             makeGenerator(getLane(linkF, "FORWARD1"), speedF, "gen4", idGenerator, gtuType1LaneF, headwaysF, roomChecker,
362                     bcFactory, tacticalFactory, SIMTIME, streams.get("gtuClass"));
363 
364             new SpeedSign("sign1", getLane(linkA, "FORWARD1"), Length.ofSI(10), new Speed(130.0, SpeedUnit.KM_PER_HOUR),
365                     DefaultsNl.VEHICLE, Duration.ZERO, new Duration(24, DurationUnit.HOUR));
366 
367         }
368 
369         /**
370          * Get lane from link by id.
371          * @param link link
372          * @param id id
373          * @return lane
374          */
375         private Lane getLane(final CrossSectionLink link, final String id)
376         {
377             return (Lane) link.getCrossSectionElement(id).orElseThrow();
378         }
379 
380         /**
381          * @param lane the reference lane for this generator
382          * @param generationSpeed the speed of the GTU
383          * @param id the id of the supplier itself
384          * @param idSupplier the supplier for the ID
385          * @param distribution the type generator for the GTU
386          * @param headwaySupplier the headway generator for the GTU
387          * @param roomChecker the checker to see if there is room for the GTU
388          * @param bcFactory the factory to generate parameters for the GTU
389          * @param tacticalFactory the generator for the tactical planner
390          * @param simulationTime simulation time
391          * @param stream random numbers stream
392          * @throws SimRuntimeException in case of scheduling problems
393          * @throws GtuException in case the GTU is inconsistent
394          * @throws ParameterException in case a parameter for the perception is missing
395          * @throws NetworkException if the object could not be added to the network
396          */
397         private void makeGenerator(final Lane lane, final Speed generationSpeed, final String id, final IdSupplier idSupplier,
398                 final ObjectDistribution<LaneBasedGtuTemplate> distribution, final Supplier<Duration> headwaySupplier,
399                 final RoomChecker roomChecker, final ParameterFactory bcFactory,
400                 final LaneBasedTacticalPlannerFactory<?> tacticalFactory, final Time simulationTime,
401                 final StreamInterface stream) throws SimRuntimeException, GtuException, ParameterException, NetworkException
402         {
403 
404             Set<LanePosition> initialLongitudinalPositions = new LinkedHashSet<>();
405             initialLongitudinalPositions.add(new LanePosition(lane, new Length(5.0, LengthUnit.SI)));
406             LaneBasedGtuTemplateDistribution characteristicsGenerator = new LaneBasedGtuTemplateDistribution(distribution);
407             LaneBasedGtuGenerator generator = new LaneBasedGtuGenerator(id, headwaySupplier, characteristicsGenerator,
408                     GeneratorPositions.create(initialLongitudinalPositions, stream), getNetwork(), getSimulator(), roomChecker,
409                     idSupplier);
410             generator.setNoLaneChangeDistance(Length.ofSI(100.0));
411             generator.setBookkeeping(LaneBookkeeping.START);
412         }
413 
414     }
415 
416     /**
417      * Extended data of synchronization phase.
418      * @param <G> GTU data type
419      */
420     public static class ExtendedDataSync<G extends GtuDataRoad> extends ExtendedDataString<G>
421     {
422 
423         /**
424          * Constructor.
425          */
426         public ExtendedDataSync()
427         {
428             super("sync", "Synchronization status");
429         }
430 
431         @Override
432         public Optional<String> getValue(final GtuDataRoad gtu)
433         {
434             if (gtu.getGtu().getTacticalPlanner() instanceof Synchronizable sync)
435             {
436                 return Optional.ofNullable(sync.getSynchronizationState().toString());
437             }
438             return Optional.of("N/A");
439         }
440 
441     }
442 
443 }