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.io.Serializable;
12 import java.net.ServerSocket;
13 import java.net.Socket;
14 import java.net.URISyntaxException;
15 import java.nio.charset.StandardCharsets;
16 import java.rmi.RemoteException;
17 import java.util.LinkedHashMap;
18 import java.util.Map;
19
20 import javax.naming.NamingException;
21 import javax.swing.JFrame;
22 import javax.xml.bind.JAXBException;
23 import javax.xml.parsers.ParserConfigurationException;
24
25 import org.djunits.unit.DurationUnit;
26 import org.djunits.unit.TimeUnit;
27 import org.djunits.value.ValueRuntimeException;
28 import org.djunits.value.vdouble.scalar.Duration;
29 import org.djunits.value.vdouble.scalar.Length;
30 import org.djunits.value.vdouble.scalar.Time;
31 import org.djutils.event.EventInterface;
32 import org.djutils.event.EventListenerInterface;
33 import org.djutils.logger.CategoryLogger;
34 import org.djutils.logger.LogCategory;
35 import org.opentrafficsim.aimsun.proto.AimsunControlProtoBuf;
36 import org.opentrafficsim.aimsun.proto.AimsunControlProtoBuf.GTUPositions;
37 import org.opentrafficsim.base.parameters.ParameterException;
38 import org.opentrafficsim.core.animation.gtu.colorer.DefaultSwitchableGTUColorer;
39 import org.opentrafficsim.core.dsol.AbstractOTSModel;
40 import org.opentrafficsim.core.dsol.OTSAnimator;
41 import org.opentrafficsim.core.dsol.OTSLoggingAnimator;
42 import org.opentrafficsim.core.dsol.OTSModelInterface;
43 import org.opentrafficsim.core.dsol.OTSSimulatorInterface;
44 import org.opentrafficsim.core.geometry.OTSGeometryException;
45 import org.opentrafficsim.core.gtu.GTU;
46 import org.opentrafficsim.core.gtu.GTUException;
47 import org.opentrafficsim.core.gtu.GTUType;
48 import org.opentrafficsim.core.gtu.plan.operational.OperationalPlanException;
49 import org.opentrafficsim.core.network.NetworkException;
50 import org.opentrafficsim.core.network.OTSNetwork;
51 import org.opentrafficsim.draw.core.OTSDrawingException;
52 import org.opentrafficsim.draw.factory.DefaultAnimationFactory;
53 import org.opentrafficsim.road.network.OTSRoadNetwork;
54 import org.opentrafficsim.road.network.factory.xml.XmlParserException;
55 import org.opentrafficsim.road.network.factory.xml.parser.XmlNetworkLaneParser;
56 import org.opentrafficsim.road.network.lane.CrossSectionLink;
57 import org.opentrafficsim.road.network.lane.conflict.ConflictBuilder;
58 import org.opentrafficsim.road.network.lane.conflict.LaneCombinationList;
59 import org.opentrafficsim.swing.gui.OTSAnimationPanel;
60 import org.opentrafficsim.swing.gui.OTSSimulationApplication;
61 import org.opentrafficsim.swing.gui.OTSSwingApplication;
62 import org.opentrafficsim.trafficcontrol.TrafficControlException;
63 import org.pmw.tinylog.Level;
64 import org.xml.sax.SAXException;
65
66 import nl.tudelft.simulation.dsol.SimRuntimeException;
67 import nl.tudelft.simulation.dsol.simulators.DEVSRealTimeClock;
68 import nl.tudelft.simulation.dsol.simulators.SimulatorInterface;
69 import nl.tudelft.simulation.jstats.streams.MersenneTwister;
70 import nl.tudelft.simulation.jstats.streams.StreamInterface;
71 import nl.tudelft.simulation.language.DSOLException;
72 import nl.tudelft.simulation.language.d3.DirectedPoint;
73
74
75
76
77
78
79
80
81
82
83
84 public class AimsunControl
85 {
86
87 private AimsunModel model = null;
88
89
90
91
92
93
94
95
96
97
98
99 public static void main(final String[] args) throws NetworkException, OTSGeometryException, NamingException,
100 ValueRuntimeException, ParameterException, SimRuntimeException
101 {
102
103 CategoryLogger.setAllLogLevel(Level.WARNING);
104 CategoryLogger.setLogCategories(LogCategory.ALL);
105
106 String ip = null;
107 Integer port = null;
108
109 for (String arg : args)
110 {
111 int equalsPos = arg.indexOf("=");
112 if (equalsPos < 0)
113 {
114 System.err.println("Unhandled argument \"" + arg + "\"");
115 }
116 String key = arg.substring(0, equalsPos);
117 String value = arg.substring(equalsPos + 1);
118 switch (key.toUpperCase())
119 {
120 case "IP":
121 ip = value;
122 break;
123 case "PORT":
124 try
125 {
126 port = Integer.parseInt(value);
127 }
128 catch (NumberFormatException exception)
129 {
130 System.err.println("Bad port number \"" + value + "\"");
131 System.exit(1);
132 }
133 break;
134 default:
135 System.err.println("Unhandled argument \"" + arg + "\"");
136 }
137 }
138 if (null == ip || null == port)
139 {
140 System.err.println("Missing required argument(s) ip=<ip-number_or_hostname> port=<port-number>");
141 System.exit(1);
142 }
143 try
144 {
145 System.out.println("Creating server socket for port " + port);
146 ServerSocket serverSocket = new ServerSocket(port);
147 serverSocket.setReuseAddress(true);
148 System.out.println("Waiting for client to connect");
149 Socket clientSocket = serverSocket.accept();
150 System.out.println("Client connected; closing server socket");
151 serverSocket.close();
152 System.out.println("Socket time out is " + clientSocket.getSoTimeout());
153 clientSocket.setSoTimeout(0);
154 System.out.println("Constructing animation/simulation");
155 AimsunControlAimsunControl">AimsunControl aimsunControl = new AimsunControl();
156 aimsunControl.commandLoop(clientSocket);
157 }
158 catch (IOException exception)
159 {
160 exception.printStackTrace();
161 }
162 System.exit(0);
163 }
164
165
166 private Time simulateUntil = null;
167
168
169 private Boolean waitingForSimulator = false;
170
171
172
173
174
175
176 protected void sendGTUPositionsToAimsun(final OutputStream outputStream)
177 {
178 OTSSimulatorInterface simulator = this.model.getSimulator();
179 Time stopTime = simulator.getSimulatorTime();
180
181 AimsunControlProtoBuf.GTUPositions.Builder builder = AimsunControlProtoBuf.GTUPositions.newBuilder();
182 for (GTU gtu : this.model.getNetwork().getGTUs())
183 {
184 AimsunControlProtoBuf.GTUPositions.GTUPosition.Builder gpb =
185 AimsunControlProtoBuf.GTUPositions.GTUPosition.newBuilder();
186 gpb.setGtuId(gtu.getId());
187 DirectedPoint dp;
188 try
189 {
190 dp = gtu.getOperationalPlan().getLocation(stopTime);
191 gpb.setX(dp.x);
192 gpb.setY(dp.y);
193 gpb.setZ(dp.z);
194 gpb.setAngle(dp.getRotZ());
195 gpb.setLength(gtu.getLength().si);
196 gpb.setWidth(gtu.getWidth().si);
197 gpb.setGtuTypeId(Integer.parseInt(gtu.getGTUType().getId().split("\\.")[1]));
198 gpb.setSpeed(gtu.getSpeed().si);
199 builder.addGtuPos(gpb.build());
200 }
201 catch (OperationalPlanException exception)
202 {
203 exception.printStackTrace();
204 }
205 }
206 builder.setStatus("OK");
207 GTUPositions gtuPositions = builder.build();
208 AimsunControlProtoBuf.OTSMessage.Builder resultBuilder = AimsunControlProtoBuf.OTSMessage.newBuilder();
209 resultBuilder.setGtuPositions(gtuPositions);
210 AimsunControlProtoBuf.OTSMessage result = resultBuilder.build();
211 this.simulateUntil = null;
212 try
213 {
214 transmitMessage(result, outputStream);
215 }
216 catch (IOException exception)
217 {
218 exception.printStackTrace();
219 }
220
221 waitingForSimulator = false;
222
223 while (this.simulateUntil == null)
224 {
225 try
226 {
227 Thread.sleep(1);
228 }
229 catch (InterruptedException exception)
230 {
231 System.err.println("Interrupted exception (sendGTUPositionsToAimsun)");
232
233 }
234 }
235 try
236 {
237 simulator.scheduleEventAbs(this.simulateUntil, this, this, "sendGTUPositionsToAimsun", new Object[] {outputStream});
238 }
239 catch (SimRuntimeException exception)
240 {
241 exception.printStackTrace();
242 }
243
244 }
245
246
247
248
249
250
251
252
253
254
255
256
257 private void commandLoop(final Socket socket) throws IOException, NetworkException, OTSGeometryException, NamingException,
258 ValueRuntimeException, ParameterException, SimRuntimeException
259 {
260 System.out.println("Entering command loop");
261 InputStream inputStream = socket.getInputStream();
262 OutputStream outputStream = socket.getOutputStream();
263 String error = null;
264 boolean simulatorStarted = false;
265 while (null == error)
266 {
267 try
268 {
269 if (waitingForSimulator)
270 {
271
272
273 while (waitingForSimulator)
274 {
275 if (!this.model.getSimulator().isStartingOrRunning())
276 {
277 System.out.println("Simulator has stopped; constructing GTUPositions message with error status");
278 Time stopTime = this.model.getSimulator().getSimulatorTime();
279 AimsunControlProtoBuf.GTUPositions.Builder builder =
280 AimsunControlProtoBuf.GTUPositions.newBuilder();
281 for (GTU gtu : this.model.getNetwork().getGTUs())
282 {
283 AimsunControlProtoBuf.GTUPositions.GTUPosition.Builder gpb =
284 AimsunControlProtoBuf.GTUPositions.GTUPosition.newBuilder();
285 gpb.setGtuId(gtu.getId());
286 DirectedPoint dp;
287 try
288 {
289 dp = gtu.getOperationalPlan().getLocation(stopTime);
290 gpb.setX(dp.x);
291 gpb.setY(dp.y);
292 gpb.setZ(dp.z);
293 gpb.setAngle(dp.getRotZ());
294 gpb.setLength(gtu.getLength().si);
295 gpb.setWidth(gtu.getWidth().si);
296 gpb.setGtuTypeId(Integer.parseInt(gtu.getGTUType().getId().split("\\.")[1]));
297 gpb.setSpeed(gtu.getSpeed().si);
298 builder.addGtuPos(gpb.build());
299 }
300 catch (OperationalPlanException exception)
301 {
302 exception.printStackTrace();
303 }
304 }
305 builder.setStatus("ERROR");
306 GTUPositions gtuPositions = builder.build();
307 AimsunControlProtoBuf.OTSMessage.Builder resultBuilder =
308 AimsunControlProtoBuf.OTSMessage.newBuilder();
309 resultBuilder.setGtuPositions(gtuPositions);
310 AimsunControlProtoBuf.OTSMessage result = resultBuilder.build();
311 this.simulateUntil = null;
312 try
313 {
314 transmitMessage(result, outputStream);
315 }
316 catch (IOException exception)
317 {
318 exception.printStackTrace();
319 }
320 System.out.println("Error message sent");
321
322 waitingForSimulator = false;
323 break;
324 }
325 try
326 {
327 Thread.sleep(1);
328 }
329 catch (InterruptedException exception)
330 {
331 System.err.println("Interrupted exception (command loop)");
332
333 }
334 }
335
336
337 }
338 byte[] sizeBytes = new byte[4];
339
340 fillBuffer(inputStream, sizeBytes);
341 int size = ((sizeBytes[0] & 0xff) << 24) + ((sizeBytes[1] & 0xff) << 16) + ((sizeBytes[2] & 0xff) << 8)
342 + (sizeBytes[3] & 0xff);
343
344 byte[] buffer = new byte[size];
345 fillBuffer(inputStream, buffer);
346 AimsunControlProtoBuf.OTSMessage message = AimsunControlProtoBuf.OTSMessage.parseFrom(buffer);
347
348 if (null == message)
349 {
350 System.out.println("Connection terminated; exiting");
351 break;
352 }
353 switch (message.getMsgCase())
354 {
355 case CREATESIMULATION:
356 System.out.println("Received CREATESIMULATION message");
357 AimsunControlProtoBuf.CreateSimulation createSimulation = message.getCreateSimulation();
358 String networkXML = createSimulation.getNetworkXML();
359 try (PrintWriter pw = new PrintWriter("c:/Temp/AimsunOtsNetwork.xml"))
360 {
361 pw.print(networkXML);
362 }
363 Duration runDuration = new Duration(3600, DurationUnit.SECOND);
364 System.out.println("runDuration " + runDuration);
365 Duration warmupDuration = new Duration(0, DurationUnit.SECOND);
366 try
367 {
368 OTSAnimator animator = new OTSLoggingAnimator("C:/Temp/AimsunEventlog.txt", "AimsunControl");
369 this.model = new AimsunModel(animator, "Aimsun generated model", "Aimsun model", networkXML);
370 Map<String, StreamInterface> map = new LinkedHashMap<>();
371
372 map.put("generation", new MersenneTwister(6L));
373 animator.initialize(Time.ZERO, warmupDuration, runDuration, this.model, map);
374 OTSAnimationPanel animationPanel =
375 new OTSAnimationPanel(this.model.getNetwork().getExtent(), new Dimension(1100, 1000),
376 animator, this.model, OTSSwingApplication.DEFAULT_COLORER, this.model.getNetwork());
377 DefaultAnimationFactory.animateXmlNetwork(this.model.getNetwork(), 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 | DSOLException 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.isStartingOrRunning())
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, getSimulator());
583 try
584 {
585 XmlNetworkLaneParser.build(new ByteArrayInputStream(this.xml.getBytes(StandardCharsets.UTF_8)), this.network,
586 false);
587 LaneCombinationList ignoreList = new LaneCombinationList();
588 try
589 {
590
591 ignoreList.addLinkCombination((CrossSectionLink) this.network.getLink("928_J5"),
592 (CrossSectionLink) this.network.getLink("928_J6"));
593 ignoreList.addLinkCombination((CrossSectionLink) this.network.getLink("925_J1"),
594 (CrossSectionLink) this.network.getLink("925_J2"));
595 }
596 catch (NullPointerException npe)
597 {
598
599 }
600 LaneCombinationList permittedList = new LaneCombinationList();
601 ConflictBuilder.buildConflictsParallel(this.network, this.network.getGtuType(GTUType.DEFAULTS.VEHICLE),
602 getSimulator(), new ConflictBuilder.FixedWidthGenerator(Length.instantiateSI(2.0)), ignoreList,
603 permittedList);
604
605 }
606 catch (NetworkException | OTSGeometryException | JAXBException | URISyntaxException | XmlParserException
607 | SAXException | ParserConfigurationException | GTUException | IOException
608 | TrafficControlException exception)
609 {
610 exception.printStackTrace();
611
612
613 throw new SimRuntimeException(exception);
614 }
615 }
616
617
618 @Override
619 public OTSNetwork getNetwork()
620 {
621 return this.network;
622 }
623
624
625 @Override
626 public Serializable getSourceId()
627 {
628 return "AimsunModel";
629 }
630
631 }
632
633 }