1 package org.opentrafficsim.swing.script;
2
3 import java.text.SimpleDateFormat;
4 import java.util.Date;
5 import java.util.LinkedHashMap;
6 import java.util.Map;
7
8 import org.djunits.value.vdouble.scalar.Duration;
9 import org.djutils.cli.Checkable;
10 import org.djutils.cli.CliException;
11 import org.djutils.cli.CliUtil;
12 import org.djutils.event.Event;
13 import org.djutils.event.EventListener;
14 import org.djutils.exceptions.Throw;
15 import org.djutils.exceptions.Try;
16 import org.djutils.reflection.ClassUtil;
17 import org.opentrafficsim.base.OtsRuntimeException;
18 import org.opentrafficsim.base.logger.Logger;
19 import org.opentrafficsim.core.dsol.AbstractOtsModel;
20 import org.opentrafficsim.core.dsol.OtsAnimator;
21 import org.opentrafficsim.core.dsol.OtsSimulator;
22 import org.opentrafficsim.core.dsol.OtsSimulatorInterface;
23 import org.opentrafficsim.core.perception.HistoryManagerDevs;
24 import org.opentrafficsim.road.network.RoadNetwork;
25 import org.opentrafficsim.swing.gui.OtsSimulationApplication;
26 import org.opentrafficsim.swing.gui.OtsSimulationPanel;
27 import org.opentrafficsim.swing.gui.OtsSimulationPanelDecorator;
28
29 import nl.tudelft.simulation.dsol.SimRuntimeException;
30 import nl.tudelft.simulation.dsol.experiment.Replication;
31 import nl.tudelft.simulation.dsol.simulators.ReplicationState;
32 import nl.tudelft.simulation.jstats.streams.MersenneTwister;
33 import nl.tudelft.simulation.jstats.streams.StreamInterface;
34 import picocli.CommandLine.Command;
35 import picocli.CommandLine.Option;
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50 @Command(description = "Simulation script", name = "Program", mixinStandardHelpOptions = true, showDefaultValues = true)
51 public abstract class AbstractSimulationScript implements EventListener, Checkable
52 {
53
54 private final String name;
55
56
57 private final String description;
58
59
60 private OtsSimulatorInterface simulator;
61
62
63 private RoadNetwork network;
64
65
66 @Option(names = "--seed", description = "Seed", defaultValue = "1")
67 private long seed;
68
69
70 @Option(names = {"-s", "--startTime"}, description = "Start time (of day)", defaultValue = "0s")
71 private Duration startTime;
72
73
74 @Option(names = {"-w", "--warmupTime"}, description = "Warm-up time", defaultValue = "0s")
75 private Duration warmupTime;
76
77
78 @Option(names = {"-t", "--simulationTime"}, description = "Simulation time (including warm-up time)",
79 defaultValue = "3600s")
80 private Duration simulationTime;
81
82
83 @Option(names = {"-h", "--history"}, description = "Guaranteed history time", defaultValue = "0s")
84 private Duration historyTime;
85
86
87 @Option(names = {"-a", "--autorun"}, description = "Autorun", negatable = true, defaultValue = "false")
88 private boolean autorun;
89
90
91
92
93
94
95 protected AbstractSimulationScript(final String name, final String description)
96 {
97 this.name = name;
98 this.description = description;
99 try
100 {
101 CliUtil.changeCommandName(this, this.name);
102 CliUtil.changeCommandDescription(this, this.description);
103 SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
104 CliUtil.changeCommandVersion(this,
105 formatter.format(new Date(ClassUtil.classFileDescriptorForClass(this.getClass()).getLastChangedDate())));
106 }
107 catch (IllegalStateException | IllegalArgumentException | CliException exception)
108 {
109 throw new OtsRuntimeException("Exception while setting properties in @Command annotation.", exception);
110 }
111 }
112
113
114
115
116
117 public long getSeed()
118 {
119 return this.seed;
120 }
121
122
123
124
125
126 public Duration getStartTime()
127 {
128 return this.startTime;
129 }
130
131
132
133
134
135 public Duration getWarmupTime()
136 {
137 return this.warmupTime;
138 }
139
140
141
142
143
144 public Duration getSimulationTime()
145 {
146 return this.simulationTime;
147 }
148
149
150
151
152
153 public boolean isAutorun()
154 {
155 return this.autorun;
156 }
157
158 @Override
159 public void check() throws Exception
160 {
161 Throw.when(this.seed < 0, IllegalArgumentException.class, "Seed should be positive");
162 Throw.when(this.warmupTime.si < 0.0, IllegalArgumentException.class, "Warm-up time should be positive");
163 Throw.when(this.simulationTime.si < 0.0, IllegalArgumentException.class, "Simulation time should be positive");
164 Throw.when(this.simulationTime.si < this.warmupTime.si, IllegalArgumentException.class,
165 "Simulation time should be longer than warm-up time");
166 }
167
168
169
170
171
172 public void start() throws Exception
173 {
174 if (isAutorun())
175 {
176 this.simulator = new OtsSimulator(this.name);
177 final ScriptModel scriptModel = new ScriptModel(this.simulator);
178 this.simulator.initialize(this.startTime, this.warmupTime, this.simulationTime, scriptModel,
179 new HistoryManagerDevs(this.simulator, this.historyTime, Duration.ofSI(10.0)));
180 this.simulator.addListener(this, Replication.END_REPLICATION_EVENT);
181 double tReport = 60.0;
182 Duration t = this.simulator.getSimulatorTime();
183 while (t.si < this.simulationTime.si)
184 {
185 this.simulator.step();
186 t = this.simulator.getSimulatorTime();
187 if (t.si >= tReport)
188 {
189 Logger.ots().info("Simulation time is " + t);
190 tReport += 60.0;
191 }
192 }
193 if (!this.simulator.getReplicationState().equals(ReplicationState.ENDED))
194 {
195 onSimulationEnd();
196 }
197 System.exit(0);
198 }
199 else
200 {
201 this.simulator = new OtsAnimator(this.name);
202 final ScriptModel scriptModel = new ScriptModel(this.simulator);
203 this.simulator.initialize(this.startTime, this.warmupTime, this.simulationTime, scriptModel,
204 new HistoryManagerDevs(this.simulator, this.historyTime, Duration.ofSI(10.0)));
205 OtsSimulationPanel animationPanel = new OtsSimulationPanel(scriptModel.getNetwork(), getDecorator());
206 OtsSimulationApplication<ScriptModel> app = new OtsSimulationApplication<ScriptModel>(scriptModel, animationPanel);
207 app.setExitOnClose(true);
208 animationPanel.enableSimulationControlButtons();
209 }
210 }
211
212 @Override
213 public void notify(final Event event)
214 {
215 if (event.getType().equals(Replication.END_REPLICATION_EVENT))
216 {
217 onSimulationEnd();
218 }
219 }
220
221
222
223
224
225 public OtsSimulatorInterface getSimulator()
226 {
227 return AbstractSimulationScript.this.simulator;
228 }
229
230
231
232
233
234 public RoadNetwork getNetwork()
235 {
236 return AbstractSimulationScript.this.network;
237 }
238
239
240
241
242
243
244 protected void onSimulationEnd()
245 {
246
247 }
248
249
250
251
252
253 protected OtsSimulationPanelDecorator getDecorator()
254 {
255 return new OtsSimulationPanelDecorator()
256 {
257 };
258 }
259
260
261
262
263
264
265
266
267
268
269 protected abstract RoadNetwork setupSimulation(OtsSimulatorInterface sim) throws Exception;
270
271
272
273
274
275
276 private class ScriptModel extends AbstractOtsModel
277 {
278
279
280
281
282
283 ScriptModel(final OtsSimulatorInterface simulator)
284 {
285 super(simulator);
286 AbstractSimulationScript.this.simulator = simulator;
287 }
288
289 @Override
290 public void constructModel() throws SimRuntimeException
291 {
292 Map<String, StreamInterface> streams = new LinkedHashMap<>();
293 StreamInterface stream = new MersenneTwister(getSeed());
294 streams.put("generation", stream);
295 stream = new MersenneTwister(getSeed() + 1);
296 streams.put("default", stream);
297 AbstractSimulationScript.this.simulator.getModel().getStreams().putAll(streams);
298 AbstractSimulationScript.this.network =
299 Try.assign(() -> AbstractSimulationScript.this.setupSimulation(AbstractSimulationScript.this.simulator),
300 OtsRuntimeException.class, "Exception while setting up simulation.");
301 AbstractSimulationScript.this.simulator.addListener(AbstractSimulationScript.this,
302 Replication.END_REPLICATION_EVENT);
303 }
304
305 @Override
306 public RoadNetwork getNetwork()
307 {
308 return AbstractSimulationScript.this.network;
309 }
310
311 }
312
313 }