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