1 package org.opentrafficsim.demo.web;
2
3 import java.io.IOException;
4 import java.net.URL;
5
6 import javax.naming.NamingException;
7
8 import org.djunits.unit.DurationUnit;
9 import org.djunits.value.vdouble.scalar.Duration;
10 import org.djunits.value.vdouble.scalar.Time;
11 import org.djutils.cli.Checkable;
12 import org.djutils.cli.CliUtil;
13 import org.djutils.io.URLResource;
14 import org.djutils.serialization.SerializationException;
15 import org.opentrafficsim.core.dsol.OTSAnimator;
16 import org.opentrafficsim.core.dsol.OTSModelInterface;
17 import org.opentrafficsim.core.dsol.OTSReplication;
18 import org.opentrafficsim.core.dsol.OTSSimulatorInterface;
19 import org.opentrafficsim.demo.CircularRoadModel;
20 import org.opentrafficsim.demo.CrossingTrafficLightsModel;
21 import org.opentrafficsim.demo.NetworksModel;
22 import org.opentrafficsim.demo.ShortMerge;
23 import org.opentrafficsim.demo.StraightModel;
24 import org.opentrafficsim.demo.conflict.BusStreetDemo;
25 import org.opentrafficsim.demo.conflict.TJunctionDemo;
26 import org.opentrafficsim.demo.conflict.TurboRoundaboutDemo;
27 import org.opentrafficsim.demo.trafficcontrol.TrafCODDemo1;
28 import org.opentrafficsim.demo.trafficcontrol.TrafCODDemo2;
29 import org.sim0mq.Sim0MQException;
30 import org.sim0mq.message.MessageUtil;
31 import org.sim0mq.message.Sim0MQMessage;
32 import org.sim0mq.message.federatestarter.FS1RequestStatusMessage;
33 import org.sim0mq.message.federationmanager.FM2SimRunControlMessage;
34 import org.sim0mq.message.federationmanager.FM3SetParameterMessage;
35 import org.sim0mq.message.federationmanager.FM4SimStartMessage;
36 import org.sim0mq.message.federationmanager.FM5RequestStatus;
37 import org.sim0mq.message.federationmanager.FM6RequestStatisticsMessage;
38 import org.sim0mq.message.modelcontroller.MC1StatusMessage;
39 import org.sim0mq.message.modelcontroller.MC2AckNakMessage;
40 import org.sim0mq.message.modelcontroller.MC3StatisticsMessage;
41 import org.sim0mq.message.modelcontroller.MC4StatisticsErrorMessage;
42 import org.zeromq.SocketType;
43 import org.zeromq.ZContext;
44 import org.zeromq.ZMQ;
45
46 import nl.tudelft.simulation.dsol.SimRuntimeException;
47 import nl.tudelft.simulation.dsol.experiment.ReplicationMode;
48 import picocli.CommandLine.Option;
49
50
51
52
53
54
55
56
57
58
59
60 public class SuperDemoWebApplication implements Checkable
61 {
62
63 private OTSSimulatorInterface simulator;
64
65
66 private OTSModelInterface model;
67
68
69 private ZMQ.Socket fsSocket;
70
71
72 private ZContext fsContext;
73
74
75 private Object federationRunId;
76
77
78 private String modelId;
79
80
81 private Duration runDuration;
82
83
84 private Duration warmupDuration;
85
86
87 private long messageCount = 0;
88
89
90 @SuppressWarnings("checkstyle:visibilitymodifier")
91 @Option(names = {"-m", "--modelId"}, description = "Id of the model to run", required = true)
92 String homePage;
93
94
95 @SuppressWarnings("checkstyle:visibilitymodifier")
96 @Option(names = {"-p", "--port"}, description = "Internet port to use", defaultValue = "8081")
97 int port;
98
99
100 @Override
101 public void check() throws Exception
102 {
103 if (this.port < 1 || this.port > 65535)
104 {
105 throw new Exception("Port number should be between 1 and 65535");
106 }
107 }
108
109
110
111
112
113
114
115
116
117 protected void init() throws SimRuntimeException, NamingException, Sim0MQException, SerializationException, IOException
118 {
119 this.simulator = new OTSAnimator("SuperDemoWebApplication");
120 this.modelId = this.modelId.trim();
121 if (this.modelId.toLowerCase().contains("circularroad"))
122 {
123 this.model = new CircularRoadModel(this.simulator);
124 }
125 else if (this.modelId.toLowerCase().contains("straight"))
126 {
127 this.model = new StraightModel(this.simulator);
128 }
129 else if (this.modelId.toLowerCase().contains("shortmerge"))
130 {
131 this.model = new ShortMerge.ShortMergeModel(this.simulator);
132 }
133 else if (this.modelId.toLowerCase().contains("networksdemo"))
134 {
135 this.model = new NetworksModel(this.simulator);
136 }
137 else if (this.modelId.toLowerCase().contains("crossingtrafficlights"))
138 {
139 this.model = new CrossingTrafficLightsModel(this.simulator);
140 }
141 else if (this.modelId.toLowerCase().contains("trafcoddemosimple"))
142 {
143 URL url = URLResource.getResource("/TrafCODDemo1/TrafCODDemo1.xml");
144 String xml = TrafCODDemo2.readStringFromURL(url);
145 this.model = new TrafCODDemo1.TrafCODModel(this.simulator, "TrafCODDemo1", "TrafCODDemo1", xml);
146 }
147 else if (this.modelId.toLowerCase().contains("trafcoddemocomplex"))
148 {
149 URL url = URLResource.getResource("/TrafCODDemo2/TrafCODDemo2.xml");
150 String xml = TrafCODDemo2.readStringFromURL(url);
151 this.model = new TrafCODDemo2.TrafCODModel(this.simulator, "TrafCODDemo2", "TrafCODDemo2", xml);
152 }
153 else if (this.modelId.toLowerCase().contains("tjunction"))
154 {
155 this.model = new TJunctionDemo.TJunctionModel(this.simulator);
156 }
157 else if (this.modelId.toLowerCase().contains("busstreet"))
158 {
159 this.model = new BusStreetDemo.BusStreetModel(this.simulator);
160 }
161 else if (this.modelId.toLowerCase().contains("turboroundabout"))
162 {
163 this.model = new TurboRoundaboutDemo.TurboRoundaboutModel(this.simulator);
164 }
165
166 if (this.model == null)
167 {
168 System.err.println("Could not find model " + this.modelId);
169 }
170 else
171 {
172 startListener();
173 }
174 }
175
176
177
178
179
180
181 protected void startListener() throws Sim0MQException, SerializationException
182 {
183 this.fsContext = new ZContext(1);
184
185 this.fsSocket = this.fsContext.createSocket(SocketType.ROUTER);
186 this.fsSocket.bind("tcp://*:" + this.port);
187
188 System.out.println("Model started. Listening at port: " + this.port);
189 System.out.flush();
190
191 while (!Thread.currentThread().isInterrupted())
192 {
193
194 String identity = this.fsSocket.recvStr();
195 this.fsSocket.recvStr();
196
197 byte[] request = this.fsSocket.recv(0);
198 System.out.println(MessageUtil.printBytes(request));
199 Object[] fields = Sim0MQMessage.decodeToArray(request);
200 Object receiverId = fields[4];
201 Object messageTypeId = fields[5];
202
203 System.out.println("Received " + Sim0MQMessage.print(fields));
204 System.out.flush();
205
206 if (receiverId.equals(this.modelId))
207 {
208 switch (messageTypeId.toString())
209 {
210 case "FS.1":
211 processRequestStatus(identity, new FS1RequestStatusMessage(fields));
212 break;
213
214 case "FM.5":
215 processRequestStatus(identity, new FM5RequestStatus(fields));
216 break;
217
218 case "FM.2":
219 processSimRunControl(identity, new FM2SimRunControlMessage(fields));
220 break;
221
222 case "FM.3":
223 processSetParameter(identity, new FM3SetParameterMessage(fields));
224 break;
225
226 case "FM.4":
227 processSimStart(identity, new FM4SimStartMessage(fields));
228 break;
229
230 case "FM.6":
231 processRequestStatistics(identity, new FM6RequestStatisticsMessage(fields));
232 break;
233
234 case "FS.3":
235 processKillFederate();
236 break;
237
238 default:
239
240 System.err.println("Received unknown message -- not processed: " + messageTypeId);
241 }
242 }
243 else
244 {
245
246 System.err.println(
247 "Received message not intended for " + this.modelId + " but for " + receiverId + " -- not processed: ");
248 }
249 }
250 }
251
252
253
254
255
256
257
258
259 private void processRequestStatus(final String identity, final Sim0MQMessage message)
260 throws Sim0MQException, SerializationException
261 {
262 if (this.federationRunId == null)
263 {
264 this.federationRunId = message.getFederationId();
265 }
266 String status = "started";
267 if (this.simulator.isStartingOrRunning())
268 {
269 status = "running";
270 }
271 else if (this.simulator.getSimulatorTime() != null && this.simulator.getReplication() != null
272 && this.simulator.getReplication().getTreatment() != null)
273 {
274 if (this.simulator.getSimulatorTime().ge(this.simulator.getReplication().getTreatment().getEndTime()))
275 {
276 status = "ended";
277 }
278 else
279 {
280 status = "error";
281 }
282 }
283 this.fsSocket.sendMore(identity);
284 this.fsSocket.sendMore("");
285 byte[] mc1Message = new MC1StatusMessage(this.federationRunId, this.modelId, message.getSenderId(), ++this.messageCount,
286 message.getMessageId(), status, "").createByteArray();
287 this.fsSocket.send(mc1Message, 0);
288
289 System.out.println("Sent MC.1");
290 System.out.flush();
291 }
292
293
294
295
296
297
298
299
300 private void processSimRunControl(final String identity, final FM2SimRunControlMessage message)
301 throws Sim0MQException, SerializationException
302 {
303 boolean status = true;
304 String error = "";
305 try
306 {
307 Object runDurationField = message.getRunDuration();
308 if (runDurationField instanceof Number)
309 {
310 this.runDuration = new Duration(((Number) runDurationField).doubleValue(), DurationUnit.SI);
311 }
312 else if (runDurationField instanceof Duration)
313 {
314 this.runDuration = (Duration) runDurationField;
315 }
316 else
317 {
318 throw new Sim0MQException("runTimeField " + runDurationField + " neither Number nor Duration");
319 }
320
321 Object warmupDurationField = message.getWarmupDuration();
322 if (warmupDurationField instanceof Number)
323 {
324 this.warmupDuration = new Duration(((Number) warmupDurationField).doubleValue(), DurationUnit.SI);
325 }
326 else if (warmupDurationField instanceof Duration)
327 {
328 this.warmupDuration = (Duration) warmupDurationField;
329 }
330 else
331 {
332 throw new Sim0MQException("warmupField " + warmupDurationField + " neither Number nor Duration");
333 }
334 }
335 catch (Exception e)
336 {
337 status = false;
338 error = e.getMessage();
339 }
340 byte[] mc2Message = new MC2AckNakMessage(this.federationRunId, this.modelId, message.getSenderId(), ++this.messageCount,
341 message.getMessageId(), status, error).createByteArray();
342 this.fsSocket.sendMore(identity);
343 this.fsSocket.sendMore("");
344 this.fsSocket.send(mc2Message, 0);
345
346 System.out.println("Sent MC.2");
347 System.out.flush();
348 }
349
350
351
352
353
354
355
356
357 private void processSetParameter(final String identity, final FM3SetParameterMessage message)
358 throws Sim0MQException, SerializationException
359 {
360 boolean status = true;
361 String error = "";
362 try
363 {
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389 }
390 catch (Exception e)
391 {
392 status = false;
393 error = e.getMessage();
394 }
395
396 byte[] mc2Message = new MC2AckNakMessage(this.federationRunId, this.modelId, message.getSenderId(), ++this.messageCount,
397 message.getMessageId(), status, error).createByteArray();
398 this.fsSocket.sendMore(identity);
399 this.fsSocket.sendMore("");
400 this.fsSocket.send(mc2Message, 0);
401
402 System.out.println("Sent MC.2");
403 System.out.flush();
404 }
405
406
407
408
409
410
411
412
413 private void processSimStart(final String identity, final FM4SimStartMessage message)
414 throws Sim0MQException, SerializationException
415 {
416 boolean status = true;
417 String error = "";
418 try
419 {
420 OTSReplication replication =
421 OTSReplication.create("rep1", Time.ZERO, this.warmupDuration, this.runDuration, this.model);
422 this.simulator.initialize(replication, ReplicationMode.TERMINATING);
423
424
425 this.simulator.start();
426 }
427 catch (Exception e)
428 {
429 status = false;
430 error = e.getMessage();
431 }
432
433 byte[] mc2Message = new MC2AckNakMessage(this.federationRunId, this.modelId, message.getSenderId(), ++this.messageCount,
434 message.getMessageId(), status, error).createByteArray();
435 this.fsSocket.sendMore(identity);
436 this.fsSocket.sendMore("");
437 this.fsSocket.send(mc2Message, 0);
438
439 System.out.println("Sent MC.2");
440 System.out.flush();
441 }
442
443
444
445
446
447
448
449
450 private void processRequestStatistics(final String identity, final FM6RequestStatisticsMessage message)
451 throws Sim0MQException, SerializationException
452 {
453 boolean ok = true;
454 String error = "";
455 String variableName = message.getVariableName();
456 double variableValue = Double.NaN;
457 try
458 {
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481 }
482 catch (Exception e)
483 {
484 ok = false;
485 error = e.getMessage();
486 }
487
488 if (Double.isNaN(variableValue))
489 {
490 ok = false;
491 error = "Parameter " + variableName + " not set to a value";
492 }
493
494 if (ok)
495 {
496 byte[] mc3Message = new MC3StatisticsMessage(this.federationRunId, this.modelId, message.getSenderId(),
497 ++this.messageCount, variableName, variableValue).createByteArray();
498 this.fsSocket.sendMore(identity);
499 this.fsSocket.sendMore("");
500 this.fsSocket.send(mc3Message, 0);
501
502 System.out.println("Sent MC.3");
503 System.out.flush();
504 }
505 else
506 {
507 byte[] mc4Message = new MC4StatisticsErrorMessage(this.federationRunId, this.modelId, message.getSenderId(),
508 ++this.messageCount, variableName, error).createByteArray();
509 this.fsSocket.sendMore(identity);
510 this.fsSocket.sendMore("");
511 this.fsSocket.send(mc4Message, 0);
512
513 System.out.println("Sent MC.4");
514 System.out.flush();
515 }
516 }
517
518
519
520
521 private void processKillFederate()
522 {
523 this.fsSocket.close();
524 this.fsContext.destroy();
525 this.fsContext.close();
526 System.exit(0);
527 }
528
529
530 protected final void terminate()
531 {
532
533
534
535
536
537
538 }
539
540
541
542
543
544
545
546
547
548 public static void main(final String[] args)
549 throws SimRuntimeException, NamingException, Sim0MQException, SerializationException, IOException
550 {
551 SuperDemoWebApplicationcation.html#SuperDemoWebApplication">SuperDemoWebApplication webApp = new SuperDemoWebApplication();
552 CliUtil.execute(webApp, args);
553 webApp.init();
554 }
555
556 }