1 package org.opentrafficsim.aimsun;
2
3 import java.awt.Dimension;
4 import java.awt.Frame;
5 import java.awt.event.ActionEvent;
6 import java.io.ByteArrayInputStream;
7 import java.io.IOException;
8 import java.io.InputStream;
9 import java.io.OutputStream;
10 import java.io.PrintWriter;
11 import java.net.ServerSocket;
12 import java.net.Socket;
13 import java.net.URISyntaxException;
14 import java.nio.charset.StandardCharsets;
15 import java.rmi.RemoteException;
16 import java.util.LinkedHashMap;
17 import java.util.Map;
18
19 import javax.naming.NamingException;
20 import javax.swing.JFrame;
21 import javax.xml.bind.JAXBException;
22 import javax.xml.parsers.ParserConfigurationException;
23
24 import org.djunits.unit.DurationUnit;
25 import org.djunits.unit.TimeUnit;
26 import org.djunits.value.ValueRuntimeException;
27 import org.djunits.value.vdouble.scalar.Duration;
28 import org.djunits.value.vdouble.scalar.Length;
29 import org.djunits.value.vdouble.scalar.Time;
30 import org.djutils.logger.LogCategory;
31 import org.opentrafficsim.aimsun.proto.AimsunControlProtoBuf;
32 import org.opentrafficsim.aimsun.proto.AimsunControlProtoBuf.GTUPositions;
33 import org.opentrafficsim.base.parameters.ParameterException;
34 import org.opentrafficsim.core.animation.gtu.colorer.DefaultSwitchableGTUColorer;
35 import org.opentrafficsim.core.dsol.AbstractOTSModel;
36 import org.opentrafficsim.core.dsol.OTSAnimator;
37 import org.opentrafficsim.core.dsol.OTSLoggingAnimator;
38 import org.opentrafficsim.core.dsol.OTSModelInterface;
39 import org.opentrafficsim.core.dsol.OTSSimulatorInterface;
40 import org.opentrafficsim.core.geometry.OTSGeometryException;
41 import org.opentrafficsim.core.gtu.GTU;
42 import org.opentrafficsim.core.gtu.GTUDumper;
43 import org.opentrafficsim.core.gtu.GTUException;
44 import org.opentrafficsim.core.gtu.GTUType;
45 import org.opentrafficsim.core.gtu.plan.operational.OperationalPlanException;
46 import org.opentrafficsim.core.network.NetworkException;
47 import org.opentrafficsim.core.network.OTSNetwork;
48 import org.opentrafficsim.draw.core.OTSDrawingException;
49 import org.opentrafficsim.draw.factory.DefaultAnimationFactory;
50 import org.opentrafficsim.road.network.OTSRoadNetwork;
51 import org.opentrafficsim.road.network.factory.xml.XmlParserException;
52 import org.opentrafficsim.road.network.factory.xml.parser.XmlNetworkLaneParser;
53 import org.opentrafficsim.road.network.lane.CrossSectionLink;
54 import org.opentrafficsim.road.network.lane.conflict.ConflictBuilder;
55 import org.opentrafficsim.road.network.lane.conflict.LaneCombinationList;
56 import org.opentrafficsim.swing.gui.OTSAnimationPanel;
57 import org.opentrafficsim.swing.gui.OTSSimulationApplication;
58 import org.opentrafficsim.swing.gui.OTSSwingApplication;
59 import org.pmw.tinylog.Level;
60 import org.xml.sax.SAXException;
61
62 import nl.tudelft.simulation.dsol.SimRuntimeException;
63 import nl.tudelft.simulation.dsol.logger.SimLogger;
64 import nl.tudelft.simulation.dsol.simulators.DEVSRealTimeClock;
65 import nl.tudelft.simulation.dsol.simulators.SimulatorInterface;
66 import nl.tudelft.simulation.event.EventInterface;
67 import nl.tudelft.simulation.event.EventListenerInterface;
68 import nl.tudelft.simulation.jstats.streams.MersenneTwister;
69 import nl.tudelft.simulation.jstats.streams.StreamInterface;
70 import nl.tudelft.simulation.language.d3.DirectedPoint;
71
72
73
74
75
76
77
78
79
80
81
82 public class AimsunControl
83 {
84
85 private AimsunModel model = null;
86
87
88
89
90
91
92
93
94
95
96
97 public static void main(final String[] args) throws NetworkException, OTSGeometryException, NamingException, ValueRuntimeException,
98 ParameterException, SimRuntimeException
99 {
100
101 SimLogger.setAllLogLevel(Level.WARNING);
102 SimLogger.setLogCategories(LogCategory.ALL);
103
104 String ip = null;
105 Integer port = null;
106
107 for (String arg : args)
108 {
109 int equalsPos = arg.indexOf("=");
110 if (equalsPos < 0)
111 {
112 System.err.println("Unhandled argument \"" + arg + "\"");
113 }
114 String key = arg.substring(0, equalsPos);
115 String value = arg.substring(equalsPos + 1);
116 switch (key.toUpperCase())
117 {
118 case "IP":
119 ip = value;
120 break;
121 case "PORT":
122 try
123 {
124 port = Integer.parseInt(value);
125 }
126 catch (NumberFormatException exception)
127 {
128 System.err.println("Bad port number \"" + value + "\"");
129 System.exit(1);
130 }
131 break;
132 default:
133 System.err.println("Unhandled argument \"" + arg + "\"");
134 }
135 }
136 if (null == ip || null == port)
137 {
138 System.err.println("Missing required argument(s) ip=<ip-number_or_hostname> port=<port-number>");
139 System.exit(1);
140 }
141 try
142 {
143 System.out.println("Creating server socket for port " + port);
144 ServerSocket serverSocket = new ServerSocket(port);
145 serverSocket.setReuseAddress(true);
146 System.out.println("Waiting for client to connect");
147 Socket clientSocket = serverSocket.accept();
148 System.out.println("Client connected; closing server socket");
149 serverSocket.close();
150 System.out.println("Socket time out is " + clientSocket.getSoTimeout());
151 clientSocket.setSoTimeout(0);
152 System.out.println("Constructing animation/simulation");
153 AimsunControlAimsunControl">AimsunControl aimsunControl = new AimsunControl();
154 aimsunControl.commandLoop(clientSocket);
155 }
156 catch (IOException exception)
157 {
158 exception.printStackTrace();
159 }
160 System.exit(0);
161 }
162
163
164 private Time simulateUntil = null;
165
166
167 private Boolean waitingForSimulator = false;
168
169
170
171
172
173
174 protected void sendGTUPositionsToAimsun(final OutputStream outputStream)
175 {
176 OTSSimulatorInterface simulator = this.model.getSimulator();
177 Time stopTime = simulator.getSimulatorTime();
178
179 AimsunControlProtoBuf.GTUPositions.Builder builder = AimsunControlProtoBuf.GTUPositions.newBuilder();
180 for (GTU gtu : this.model.getNetwork().getGTUs())
181 {
182 AimsunControlProtoBuf.GTUPositions.GTUPosition.Builder gpb =
183 AimsunControlProtoBuf.GTUPositions.GTUPosition.newBuilder();
184 gpb.setGtuId(gtu.getId());
185 DirectedPoint dp;
186 try
187 {
188 dp = gtu.getOperationalPlan().getLocation(stopTime);
189 gpb.setX(dp.x);
190 gpb.setY(dp.y);
191 gpb.setZ(dp.z);
192 gpb.setAngle(dp.getRotZ());
193 gpb.setLength(gtu.getLength().si);
194 gpb.setWidth(gtu.getWidth().si);
195 gpb.setGtuTypeId(Integer.parseInt(gtu.getGTUType().getId().split("\\.")[1]));
196 gpb.setSpeed(gtu.getSpeed().si);
197 builder.addGtuPos(gpb.build());
198 }
199 catch (OperationalPlanException exception)
200 {
201 exception.printStackTrace();
202 }
203 }
204 builder.setStatus("OK");
205 GTUPositions gtuPositions = builder.build();
206 AimsunControlProtoBuf.OTSMessage.Builder resultBuilder = AimsunControlProtoBuf.OTSMessage.newBuilder();
207 resultBuilder.setGtuPositions(gtuPositions);
208 AimsunControlProtoBuf.OTSMessage result = resultBuilder.build();
209 this.simulateUntil = null;
210 try
211 {
212 transmitMessage(result, outputStream);
213 }
214 catch (IOException exception)
215 {
216 exception.printStackTrace();
217 }
218
219 waitingForSimulator = false;
220
221 while (this.simulateUntil == null)
222 {
223 try
224 {
225 Thread.sleep(1);
226 }
227 catch (InterruptedException exception)
228 {
229 System.err.println("Interrupted exception (sendGTUPositionsToAimsun)");
230
231 }
232 }
233 try
234 {
235 simulator.scheduleEventAbs(this.simulateUntil, this, this, "sendGTUPositionsToAimsun",
236 new Object[] { outputStream });
237 }
238 catch (SimRuntimeException exception)
239 {
240 exception.printStackTrace();
241 }
242
243 }
244
245
246
247
248
249
250
251
252
253
254
255
256 private void commandLoop(final Socket socket) throws IOException, NetworkException, OTSGeometryException, NamingException,
257 ValueRuntimeException, ParameterException, SimRuntimeException
258 {
259 System.out.println("Entering command loop");
260 InputStream inputStream = socket.getInputStream();
261 OutputStream outputStream = socket.getOutputStream();
262 String error = null;
263 boolean simulatorStarted = false;
264 while (null == error)
265 {
266 try
267 {
268 if (waitingForSimulator)
269 {
270
271
272 while (waitingForSimulator)
273 {
274 if (!this.model.getSimulator().isRunning())
275 {
276 System.out.println("Simulator has stopped; constructing GTUPositions message with error status");
277 Time stopTime = this.model.getSimulator().getSimulatorTime();
278 AimsunControlProtoBuf.GTUPositions.Builder builder =
279 AimsunControlProtoBuf.GTUPositions.newBuilder();
280 for (GTU gtu : this.model.getNetwork().getGTUs())
281 {
282 AimsunControlProtoBuf.GTUPositions.GTUPosition.Builder gpb =
283 AimsunControlProtoBuf.GTUPositions.GTUPosition.newBuilder();
284 gpb.setGtuId(gtu.getId());
285 DirectedPoint dp;
286 try
287 {
288 dp = gtu.getOperationalPlan().getLocation(stopTime);
289 gpb.setX(dp.x);
290 gpb.setY(dp.y);
291 gpb.setZ(dp.z);
292 gpb.setAngle(dp.getRotZ());
293 gpb.setLength(gtu.getLength().si);
294 gpb.setWidth(gtu.getWidth().si);
295 gpb.setGtuTypeId(Integer.parseInt(gtu.getGTUType().getId().split("\\.")[1]));
296 gpb.setSpeed(gtu.getSpeed().si);
297 builder.addGtuPos(gpb.build());
298 }
299 catch (OperationalPlanException exception)
300 {
301 exception.printStackTrace();
302 }
303 }
304 builder.setStatus("ERROR");
305 GTUPositions gtuPositions = builder.build();
306 AimsunControlProtoBuf.OTSMessage.Builder resultBuilder =
307 AimsunControlProtoBuf.OTSMessage.newBuilder();
308 resultBuilder.setGtuPositions(gtuPositions);
309 AimsunControlProtoBuf.OTSMessage result = resultBuilder.build();
310 this.simulateUntil = null;
311 try
312 {
313 transmitMessage(result, outputStream);
314 }
315 catch (IOException exception)
316 {
317 exception.printStackTrace();
318 }
319 System.out.println("Error message sent");
320
321 waitingForSimulator = false;
322 break;
323 }
324 try
325 {
326 Thread.sleep(1);
327 }
328 catch (InterruptedException exception)
329 {
330 System.err.println("Interrupted exception (command loop)");
331
332 }
333 }
334
335
336 }
337 byte[] sizeBytes = new byte[4];
338
339 fillBuffer(inputStream, sizeBytes);
340 int size = ((sizeBytes[0] & 0xff) << 24) + ((sizeBytes[1] & 0xff) << 16) + ((sizeBytes[2] & 0xff) << 8)
341 + (sizeBytes[3] & 0xff);
342
343 byte[] buffer = new byte[size];
344 fillBuffer(inputStream, buffer);
345 AimsunControlProtoBuf.OTSMessage message = AimsunControlProtoBuf.OTSMessage.parseFrom(buffer);
346
347 if (null == message)
348 {
349 System.out.println("Connection terminated; exiting");
350 break;
351 }
352 switch (message.getMsgCase())
353 {
354 case CREATESIMULATION:
355 System.out.println("Received CREATESIMULATION message");
356 AimsunControlProtoBuf.CreateSimulation createSimulation = message.getCreateSimulation();
357 String networkXML = createSimulation.getNetworkXML();
358 try (PrintWriter pw = new PrintWriter("c:/Temp/AimsunOtsNetwork.xml"))
359 {
360 pw.print(networkXML);
361 }
362 Duration runDuration = new Duration(3600, DurationUnit.SECOND);
363 System.out.println("runDuration " + runDuration);
364 Duration warmupDuration = new Duration(0, DurationUnit.SECOND);
365 try
366 {
367 OTSAnimator animator = new OTSLoggingAnimator("C:/Temp/AimsunEventlog.txt");
368 this.model = new AimsunModel(animator, "Aimsun generated model", "Aimsun model", networkXML);
369 Map<String, StreamInterface> map = new LinkedHashMap<>();
370
371 map.put("generation", new MersenneTwister(6L));
372 animator.initialize(Time.ZERO, warmupDuration, runDuration, this.model, map);
373 OTSAnimationPanel animationPanel =
374 new OTSAnimationPanel(this.model.getNetwork().getExtent(), new Dimension(1100, 1000),
375 animator, this.model, OTSSwingApplication.DEFAULT_COLORER, this.model.getNetwork());
376 DefaultAnimationFactory.animateXmlNetwork(this.model.getNetwork(), animator,
377 new DefaultSwitchableGTUColorer());
378 new AimsunSwingApplication(this.model, animationPanel);
379 JFrame frame = (JFrame) animationPanel.getParent().getParent().getParent();
380 frame.setExtendedState(Frame.NORMAL);
381 frame.setSize(new Dimension(1100, 1000));
382 frame.setBounds(0, 25, 1100, 1000);
383 animator.setSpeedFactor(Double.MAX_VALUE, true);
384 animator.setSpeedFactor(1000.0, true);
385 try
386 {
387 Thread.sleep(300);
388 }
389 catch (InterruptedException e)
390 {
391 e.printStackTrace();
392 }
393 animationPanel.actionPerformed(new ActionEvent(this, 0, "ZoomAll"));
394 }
395 catch (SimRuntimeException | NamingException | OTSDrawingException exception1)
396 {
397 exception1.printStackTrace();
398
399 error = "XML ERROR";
400 }
401 break;
402
403 case SIMULATEUNTIL:
404 {
405 AimsunControlProtoBuf.SimulateUntil simulateUntilThing = message.getSimulateUntil();
406 Time stopTime = new Time(simulateUntilThing.getTime(), TimeUnit.BASE_SECOND);
407 System.out.println("Received SIMULATEUNTIL " + stopTime + " message");
408 OTSSimulatorInterface simulator = this.model.getSimulator();
409 if (!simulatorStarted)
410 {
411 simulatorStarted = true;
412 simulator.scheduleEventAbs(stopTime, this, this, "sendGTUPositionsToAimsun",
413 new Object[] { outputStream });
414 System.out.println("Starting simulator");
415 this.simulateUntil = stopTime;
416 simulator.start();
417 }
418 else if (!simulator.isRunning())
419 {
420
421 error = "HMM Simulator stopped";
422 }
423 else
424 {
425
426 waitingForSimulator = true;
427 this.simulateUntil = stopTime;
428 }
429 break;
430 }
431
432 case GTUPOSITIONS:
433 System.out.println("Received GTUPOSITIONS message SHOULD NOT HAPPEN");
434 socket.close();
435 return;
436
437 case MSG_NOT_SET:
438 System.out.println("Received MSG_NOT_SET message SHOULD NOT HAPPEN");
439 socket.close();
440 return;
441
442 default:
443 System.out.println("Received unknown message SHOULD NOT HAPPEN");
444 socket.close();
445 break;
446 }
447 }
448 catch (IOException exception)
449 {
450 exception.printStackTrace();
451 break;
452 }
453 }
454 }
455
456
457
458
459
460
461
462 private void transmitMessage(final AimsunControlProtoBuf.OTSMessage message, final OutputStream outputStream)
463 throws IOException
464 {
465 int size = message.getSerializedSize();
466
467
468 byte[] sizeBytes = new byte[4];
469 sizeBytes[0] = (byte) ((size >> 24) & 0xff);
470 sizeBytes[1] = (byte) ((size >> 16) & 0xff);
471 sizeBytes[2] = (byte) ((size >> 8) & 0xff);
472 sizeBytes[3] = (byte) (size & 0xff);
473 outputStream.write(sizeBytes);
474 byte[] buffer = new byte[size];
475 buffer = message.toByteArray();
476 outputStream.write(buffer);
477
478 }
479
480
481
482
483
484
485
486 static void fillBuffer(final InputStream in, final byte[] buffer) throws IOException
487 {
488
489 int offset = 0;
490 while (true)
491 {
492 int bytesRead = in.read(buffer, offset, buffer.length - offset);
493 if (-1 == bytesRead)
494 {
495 break;
496 }
497 offset += bytesRead;
498 if (buffer.length == offset)
499 {
500
501 break;
502 }
503 if (buffer.length < offset)
504 {
505 System.err.println("fillBuffer: Oops: Got more than " + buffer.length + " requested bytes");
506 break;
507 }
508
509
510 }
511 if (offset != buffer.length)
512 {
513 throw new IOException("Got only " + offset + " of expected " + buffer.length + " bytes");
514 }
515 }
516
517
518
519
520 class AimsunSwingApplication extends OTSSimulationApplication<OTSModelInterface>
521 {
522
523 private static final long serialVersionUID = 1L;
524
525
526
527
528
529
530 AimsunSwingApplication(final OTSModelInterface model, final OTSAnimationPanel panel) throws OTSDrawingException
531 {
532 super(model, panel);
533 }
534 }
535
536
537
538
539 class AimsunModel extends AbstractOTSModel implements EventListenerInterface
540 {
541
542 private static final long serialVersionUID = 20170419L;
543
544
545 private OTSRoadNetwork network;
546
547
548 private final String xml;
549
550
551
552
553
554
555
556 AimsunModel(final OTSSimulatorInterface simulator, final String shortName, final String description, final String xml)
557 {
558 super(simulator, shortName, description);
559 this.xml = xml;
560 }
561
562
563 @Override
564 public void notify(final EventInterface event) throws RemoteException
565 {
566 System.err.println("Received event " + event);
567 }
568
569
570 @Override
571 public void constructModel() throws SimRuntimeException
572 {
573 try
574 {
575 this.simulator.addListener(this, DEVSRealTimeClock.CHANGE_SPEED_FACTOR_EVENT);
576 this.simulator.addListener(this, SimulatorInterface.TIME_CHANGED_EVENT);
577 }
578 catch (RemoteException exception1)
579 {
580 exception1.printStackTrace();
581 }
582 this.network = new OTSRoadNetwork(getShortName(), true);
583 try
584 {
585 XmlNetworkLaneParser.build(new ByteArrayInputStream(this.xml.getBytes(StandardCharsets.UTF_8)), this.network,
586 getSimulator());
587
588 LaneCombinationList ignoreList = new LaneCombinationList();
589 ignoreList.addLinkCombination((CrossSectionLink) this.network.getLink("928_J5"),
590 (CrossSectionLink) this.network.getLink("928_J6"));
591 ignoreList.addLinkCombination((CrossSectionLink) this.network.getLink("925_J1"),
592 (CrossSectionLink) this.network.getLink("925_J2"));
593 LaneCombinationList permittedList = new LaneCombinationList();
594 ConflictBuilder.buildConflicts(this.network, this.network.getGtuType(GTUType.DEFAULTS.VEHICLE), getSimulator(),
595 new ConflictBuilder.FixedWidthGenerator(Length.instantiateSI(2.0)), ignoreList, permittedList);
596 new GTUDumper(simulator, Time.ZERO, Duration.instantiateSI(60), network, "C:/Temp/aimsun");
597 }
598 catch (NetworkException | OTSGeometryException | JAXBException | URISyntaxException | XmlParserException
599 | SAXException | ParserConfigurationException | GTUException exception)
600 {
601 exception.printStackTrace();
602
603
604 throw new SimRuntimeException(exception);
605 }
606 }
607
608
609 @Override
610 public OTSNetwork getNetwork()
611 {
612 return this.network;
613 }
614
615 }
616
617 }