View Javadoc
1   package org.opentrafficsim.aimsun;
2   
3   import java.awt.Dimension;
4   import java.awt.geom.Rectangle2D;
5   import java.io.ByteArrayInputStream;
6   import java.io.IOException;
7   import java.io.InputStream;
8   import java.io.OutputStream;
9   import java.io.PrintWriter;
10  import java.lang.management.ManagementFactory;
11  import java.lang.management.ThreadMXBean;
12  import java.net.ServerSocket;
13  import java.net.Socket;
14  import java.nio.charset.StandardCharsets;
15  import java.rmi.RemoteException;
16  
17  import javax.naming.NamingException;
18  import javax.xml.parsers.ParserConfigurationException;
19  
20  import org.djunits.unit.DurationUnit;
21  import org.djunits.unit.TimeUnit;
22  import org.djunits.value.ValueException;
23  import org.djunits.value.vdouble.scalar.Duration;
24  import org.djunits.value.vdouble.scalar.Length;
25  import org.djunits.value.vdouble.scalar.Time;
26  import org.opentrafficsim.aimsun.proto.AimsunControlProtoBuf;
27  import org.opentrafficsim.aimsun.proto.AimsunControlProtoBuf.GTUPositions;
28  import org.opentrafficsim.base.parameters.ParameterException;
29  import org.opentrafficsim.core.dsol.AbstractOTSModel;
30  import org.opentrafficsim.core.dsol.OTSAnimator;
31  import org.opentrafficsim.core.dsol.OTSModelInterface;
32  import org.opentrafficsim.core.dsol.OTSSimulatorInterface;
33  import org.opentrafficsim.core.geometry.OTSGeometryException;
34  import org.opentrafficsim.core.gtu.GTU;
35  import org.opentrafficsim.core.gtu.GTUException;
36  import org.opentrafficsim.core.gtu.GTUType;
37  import org.opentrafficsim.core.gtu.plan.operational.OperationalPlanException;
38  import org.opentrafficsim.core.network.NetworkException;
39  import org.opentrafficsim.core.network.OTSNetwork;
40  import org.opentrafficsim.draw.core.OTSDrawingException;
41  import org.opentrafficsim.draw.factory.DefaultAnimationFactory;
42  import org.opentrafficsim.road.network.factory.xml.XmlNetworkLaneParser;
43  import org.opentrafficsim.road.network.lane.conflict.ConflictBuilder;
44  import org.opentrafficsim.swing.gui.AnimationToggles;
45  import org.opentrafficsim.swing.gui.OTSAnimationPanel;
46  import org.opentrafficsim.swing.gui.OTSSimulationApplication;
47  import org.opentrafficsim.swing.gui.OTSSwingApplication;
48  import org.xml.sax.SAXException;
49  
50  import nl.tudelft.simulation.dsol.SimRuntimeException;
51  import nl.tudelft.simulation.dsol.simulators.DEVSRealTimeClock;
52  import nl.tudelft.simulation.dsol.simulators.SimulatorInterface;
53  import nl.tudelft.simulation.event.EventInterface;
54  import nl.tudelft.simulation.event.EventListenerInterface;
55  import nl.tudelft.simulation.language.d3.DirectedPoint;
56  
57  /**
58   * <p>
59   * Copyright (c) 2013-2019 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
60   * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
61   * <p>
62   * @version $Revision$, $LastChangedDate$, by $Author$, initial version Apr 18, 2017 <br>
63   * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
64   * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
65   * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
66   */
67  public class AimsunControl
68  {
69      /** XML description of the network. */
70      private String networkXML = null;
71  
72      /** Currently active Aimsun model. */
73      private AimsunModel model = null;
74  
75      /**
76       * Program entry point.
77       * @param args String[]; the command line arguments
78       * @throws OTSGeometryException on error
79       * @throws NetworkException on error
80       * @throws NamingException on error
81       * @throws ValueException on error
82       * @throws SimRuntimeException on error
83       * @throws ParameterException on error
84       */
85      public static void main(final String[] args) throws NetworkException, OTSGeometryException, NamingException, ValueException,
86              ParameterException, SimRuntimeException
87      {
88          String ip = null;
89          Integer port = null;
90  
91          for (String arg : args)
92          {
93              int equalsPos = arg.indexOf("=");
94              if (equalsPos < 0)
95              {
96                  System.err.println("Unhandled argument \"" + arg + "\"");
97              }
98              String key = arg.substring(0, equalsPos);
99              String value = arg.substring(equalsPos + 1);
100             switch (key.toUpperCase())
101             {
102                 case "IP":
103                     ip = value;
104                     break;
105                 case "PORT":
106                     try
107                     {
108                         port = Integer.parseInt(value);
109                     }
110                     catch (NumberFormatException exception)
111                     {
112                         System.err.println("Bad port number \"" + value + "\"");
113                         System.exit(1);
114                     }
115                     break;
116                 default:
117                     System.err.println("Unhandled argument \"" + arg + "\"");
118             }
119         }
120         if (null == ip || null == port)
121         {
122             System.err.println("Missing required argument(s) ip=<ip-number_or_hostname> port=<port-number>");
123             System.exit(1);
124         }
125         try
126         {
127             System.out.println("Creating server socket for port " + port);
128             ServerSocket serverSocket = new ServerSocket(port);
129             serverSocket.setReuseAddress(true); // Ensure we can be restarted without the normal delay
130             System.out.println("Waiting for client to connect");
131             Socket clientSocket = serverSocket.accept();
132             System.out.println("Client connected; closing server socket");
133             serverSocket.close(); // don't accept any other connections
134             System.out.println("Socket time out is " + clientSocket.getSoTimeout());
135             clientSocket.setSoTimeout(0);
136             System.out.println("Constructing animation/simulation");
137 
138             AimsunControl aimsunControl = new AimsunControl();
139             aimsunControl.commandLoop(clientSocket);
140         }
141         catch (IOException exception)
142         {
143             exception.printStackTrace();
144         }
145         System.exit(0);
146     }
147 
148     /**
149      * Create a nice hex dump of a bunch of bytes.
150      * @param bytes byte[]; the bytes
151      * @return String; the hex dump
152      */
153     public static String hexDump(final byte[] bytes)
154     {
155         StringBuilder result = new StringBuilder();
156         int pos = 0;
157         for (byte b : bytes)
158         {
159             result.append(String.format("%02x", b));
160             if (pos % 16 == 0)
161             {
162                 result.append("\r\n");
163             }
164             else if (pos % 8 == 0)
165             {
166                 result.append("  ");
167             }
168             else
169             {
170                 result.append(" ");
171             }
172         }
173         return result.toString();
174     }
175 
176     /**
177      * Process incoming commands.
178      * @param socket Socket; the communications channel to Aimsun
179      * @throws IOException when communication with Aimsun fails
180      * @throws OTSGeometryException on error
181      * @throws NetworkException on error
182      * @throws NamingException on error
183      * @throws ValueException on error
184      * @throws SimRuntimeException on error
185      * @throws ParameterException on error
186      */
187     private void commandLoop(final Socket socket) throws IOException, NetworkException, OTSGeometryException, NamingException,
188             ValueException, ParameterException, SimRuntimeException
189     {
190         System.out.println("Entering command loop");
191         InputStream inputStream = socket.getInputStream();
192         OutputStream outputStream = socket.getOutputStream();
193         String error = null;
194         while (true)
195         {
196             try
197             {
198                 byte[] sizeBytes = new byte[4];
199                 fillBuffer(inputStream, sizeBytes);
200                 int size = ((sizeBytes[0] & 0xff) << 24) + ((sizeBytes[1] & 0xff) << 16) + ((sizeBytes[2] & 0xff) << 8)
201                         + (sizeBytes[3] & 0xff);
202                 System.out.println("expecting message of " + size + " bytes");
203                 byte[] buffer = new byte[size];
204                 fillBuffer(inputStream, buffer);
205                 AimsunControlProtoBuf.OTSMessage message = AimsunControlProtoBuf.OTSMessage.parseFrom(buffer);
206 
207                 if (null == message)
208                 {
209                     System.out.println("Connection terminated; exiting");
210                     break;
211                 }
212                 switch (message.getMsgCase())
213                 {
214                     case CREATESIMULATION:
215                         System.out.println("Received CREATESIMULATION message");
216                         AimsunControlProtoBuf.CreateSimulation createSimulation = message.getCreateSimulation();
217                         this.networkXML = createSimulation.getNetworkXML();
218                         try (PrintWriter pw = new PrintWriter("d:/AimsunOtsNetwork.xml"))
219                         {
220                             pw.print(this.networkXML);
221                         }
222                         Duration runDuration = new Duration(createSimulation.getRunTime(), DurationUnit.SECOND);
223                         System.out.println("runDuration " + runDuration);
224                         Duration warmupDuration = new Duration(createSimulation.getWarmUpTime(), DurationUnit.SECOND);
225                         try
226                         {
227                             OTSAnimator animator = new OTSAnimator();
228                             animator.initialize(Time.ZERO, warmupDuration, runDuration, this.model);
229                             OTSAnimationPanel animationPanel = new OTSAnimationPanel(
230                                     new Rectangle2D.Double(-500, -500, 1000, 1000), new Dimension(200, 200), animator,
231                                     this.model, OTSSwingApplication.DEFAULT_COLORER, this.model.getNetwork());
232                             new AimsunSwingApplication(this.model, animationPanel);
233                             animator.setSpeedFactor(Double.MAX_VALUE, true);
234                             animator.setSpeedFactor(1000.0, true);
235                         }
236                         catch (SimRuntimeException | NamingException | OTSDrawingException exception1)
237                         {
238                             exception1.printStackTrace();
239                             // Stop the simulation
240                             error = "XML ERROR";
241                         }
242                         break;
243 
244                     case SIMULATEUNTIL:
245                         System.out.println("Received SIMULATEUNTIL message");
246                         if (null == this.model)
247                         {
248                             System.err.println("No model active");
249                             socket.close();
250                             break;
251                         }
252                         AimsunControlProtoBuf.SimulateUntil simulateUntil = message.getSimulateUntil();
253                         Time stopTime = new Time(simulateUntil.getTime(), TimeUnit.BASE_SECOND);
254                         System.out.println("Simulate until " + stopTime + " ");
255                         OTSSimulatorInterface simulator = this.model.getSimulator();
256                         try
257                         {
258                             if (null != error)
259                             {
260                                 throw new SimRuntimeException(error);
261                             }
262                             simulator.runUpTo(stopTime);
263                             int attempt = 0;
264                             while (simulator.isRunning())
265                             {
266                                 attempt++;
267                                 try
268                                 {
269                                     Thread.sleep(10);
270                                     // System.out.println("Simulation time is now " + simulator.getSimulatorTime());
271                                     if (Integer.toBinaryString(attempt).matches("10*"))
272                                     {
273                                         System.out.print(".");
274                                     }
275                                     if (attempt % 1000 == 0)
276                                     {
277                                         ThreadMXBean mxBean = ManagementFactory.getThreadMXBean();
278                                         long[] threadIds = mxBean.findDeadlockedThreads();
279                                         if (null == threadIds)
280                                         {
281                                             System.out.println("Hmmm... There are no dead locked threads");
282                                         }
283                                         else
284                                         {
285                                             for (long threadId : threadIds)
286                                             {
287                                                 System.out.println("Thread id of dead locked thread is " + threadId);
288                                             }
289                                         }
290                                         // System.out.println(mxBean);
291                                         System.out.println("Simulator time is " + simulator.getSimulatorTime());
292                                         System.out.println("Kicking simulator...");
293                                         simulator.stop();
294                                         simulator.runUpTo(stopTime);
295                                     }
296                                 }
297                                 catch (InterruptedException ie)
298                                 {
299                                     ie.printStackTrace();
300                                     // ie = null; // ignore
301                                 }
302                             }
303                             System.out.println("Simulator has stopped at time " + simulator.getSimulatorTime());
304                             AimsunControlProtoBuf.GTUPositions.Builder builder =
305                                     AimsunControlProtoBuf.GTUPositions.newBuilder();
306                             for (GTU gtu : this.model.getNetwork().getGTUs())
307                             {
308                                 AimsunControlProtoBuf.GTUPositions.GTUPosition.Builder gpb =
309                                         AimsunControlProtoBuf.GTUPositions.GTUPosition.newBuilder();
310                                 gpb.setGtuId(gtu.getId());
311                                 DirectedPoint dp = gtu.getOperationalPlan().getLocation(stopTime);
312                                 gpb.setX(dp.x);
313                                 gpb.setY(dp.y);
314                                 gpb.setZ(dp.z);
315                                 gpb.setAngle(dp.getRotZ());
316                                 gpb.setLength(gtu.getLength().si);
317                                 gpb.setWidth(gtu.getWidth().si);
318                                 gpb.setGtuTypeId(Integer.parseInt(gtu.getGTUType().getId().split("\\.")[1]));
319                                 gpb.setSpeed(gtu.getSpeed().si);
320                                 builder.addGtuPos(gpb.build());
321                             }
322                             builder.setStatus("OK");
323                             GTUPositions gtuPositions = builder.build();
324                             AimsunControlProtoBuf.OTSMessage.Builder resultBuilder =
325                                     AimsunControlProtoBuf.OTSMessage.newBuilder();
326                             resultBuilder.setGtuPositions(gtuPositions);
327                             AimsunControlProtoBuf.OTSMessage result = resultBuilder.build();
328                             transmitMessage(result, outputStream);
329                         }
330                         catch (SimRuntimeException | OperationalPlanException exception)
331                         {
332                             if (null == error)
333                             {
334                                 error = "Error while handling SIMULATEUNTIL";
335                                 System.out.println(error);
336                                 exception.printStackTrace();
337                             }
338                             // Stop the simulation
339                             AimsunControlProtoBuf.GTUPositions.Builder builder =
340                                     AimsunControlProtoBuf.GTUPositions.newBuilder();
341                             builder.setStatus("FAILED (" + error + ")");
342                             AimsunControlProtoBuf.OTSMessage.Builder resultBuilder =
343                                     AimsunControlProtoBuf.OTSMessage.newBuilder();
344                             resultBuilder.setGtuPositions(builder);
345                             transmitMessage(resultBuilder.build(), outputStream);
346                         }
347                         break;
348 
349                     case GTUPOSITIONS:
350                         System.out.println("Received GTUPOSITIONS message SHOULD NOT HAPPEN");
351                         socket.close();
352                         return;
353 
354                     case MSG_NOT_SET:
355                         System.out.println("Received MSG_NOT_SET message SHOULD NOT HAPPEN");
356                         socket.close();
357                         return;
358 
359                     default:
360                         System.out.println("Received unknown message SHOULD NOT HAPPEN");
361                         socket.close();
362                         break;
363                 }
364             }
365             catch (IOException exception)
366             {
367                 exception.printStackTrace();
368                 break;
369             }
370         }
371     }
372 
373     /**
374      * Transmit a message
375      * @param message AimsunControlProtoBuf.OTSMessage; the message
376      * @param outputStream OutputStream; the output stream
377      * @throws IOException when transmission fails
378      */
379     private void transmitMessage(final AimsunControlProtoBuf.OTSMessage message, final OutputStream outputStream)
380             throws IOException
381     {
382         int size = message.getSerializedSize();
383         System.out.print("Transmitting " + message.getGtuPositions().getGtuPosCount() + " GTU positions and status \""
384                 + message.getGtuPositions().getStatus() + "\" encoded in " + size + " bytes ... ");
385         byte[] sizeBytes = new byte[4];
386         sizeBytes[0] = (byte) ((size >> 24) & 0xff);
387         sizeBytes[1] = (byte) ((size >> 16) & 0xff);
388         sizeBytes[2] = (byte) ((size >> 8) & 0xff);
389         sizeBytes[3] = (byte) (size & 0xff);
390         outputStream.write(sizeBytes);
391         byte[] buffer = new byte[size];
392         buffer = message.toByteArray();
393         outputStream.write(buffer);
394         System.out.println("Done");
395     }
396 
397     /**
398      * Fill a buffer from a stream; retry until the buffer is entirely filled.
399      * @param in InputStream; the input stream for the data
400      * @param buffer byte[]; the buffer
401      * @throws IOException when it is not possible to fill the entire buffer
402      */
403     static void fillBuffer(final InputStream in, final byte[] buffer) throws IOException
404     {
405         System.out.print("Need to read " + buffer.length + " bytes ... ");
406         int offset = 0;
407         while (true)
408         {
409             int bytesRead = in.read(buffer, offset, buffer.length - offset);
410             if (-1 == bytesRead)
411             {
412                 break;
413             }
414             offset += bytesRead;
415             if (buffer.length == offset)
416             {
417                 System.out.println("got all " + buffer.length + " requested bytes");
418                 break;
419             }
420             if (buffer.length < offset)
421             {
422                 System.out.println("Oops: Got more than " + buffer.length + " requested bytes");
423                 break;
424             }
425             System.out.print("now got " + offset + " bytes; need to read " + (buffer.length - offset) + " more bytes ... ");
426         }
427         if (offset != buffer.length)
428         {
429             throw new IOException("Got only " + offset + " of expected " + buffer.length + " bytes");
430         }
431     }
432 
433     /**
434      * The application.
435      */
436     class AimsunSwingApplication extends OTSSimulationApplication<OTSModelInterface>
437     {
438         /** */
439         private static final long serialVersionUID = 1L;
440 
441         /**
442          * @param model OTSModelInterface; the model
443          * @param panel OTSAnimationPanel; the panel of the main screen
444          * @throws OTSDrawingException on animation error
445          */
446         public AimsunSwingApplication(final OTSModelInterface model, final OTSAnimationPanel panel) throws OTSDrawingException
447         {
448             super(model, panel);
449         }
450     }
451 
452     /**
453      * The network.
454      */
455     class AimsunModel extends AbstractOTSModel implements EventListenerInterface
456     {
457         /**
458          * @param simulator OTSSimulatorInterface; the simulator
459          * @param shortName String; the model name
460          * @param description String; the model description
461          */
462         public AimsunModel(OTSSimulatorInterface simulator, String shortName, String description)
463         {
464             super(simulator, shortName, description);
465         }
466 
467         /** */
468         private static final long serialVersionUID = 20170419L;
469 
470         /** The network. */
471         private OTSNetwork network;
472 
473         /** {@inheritDoc} */
474         @Override
475         public void constructModel() throws SimRuntimeException
476         {
477             try
478             {
479                 this.simulator.addListener(this, DEVSRealTimeClock.CHANGE_SPEED_FACTOR_EVENT);
480                 this.simulator.addListener(this, SimulatorInterface.TIME_CHANGED_EVENT);
481             }
482             catch (RemoteException exception1)
483             {
484                 exception1.printStackTrace();
485             }
486             // URL url = URLResource.getResource("/aimsun/singleRoad.xml");
487             XmlNetworkLaneParser nlp = new XmlNetworkLaneParser(this.simulator);
488             @SuppressWarnings("synthetic-access")
489             String xml = AimsunControl.this.networkXML;
490             try
491             {
492                 this.network = nlp.build(new ByteArrayInputStream(xml.getBytes(StandardCharsets.UTF_8)), false);
493                 ConflictBuilder.buildConflicts(this.network, GTUType.VEHICLE, this.simulator,
494                         new ConflictBuilder.FixedWidthGenerator(Length.createSI(2.0)));
495             }
496             catch (NetworkException | ParserConfigurationException | SAXException | IOException | NamingException | GTUException
497                     | OTSGeometryException | ValueException | ParameterException | SimRuntimeException exception)
498             {
499                 exception.printStackTrace();
500                 throw new SimRuntimeException(exception);
501             }
502         }
503 
504         /** {@inheritDoc} */
505         @Override
506         public void notify(EventInterface event) throws RemoteException
507         {
508             System.out.println("Received event " + event);
509         }
510 
511         /** {@inheritDoc} */
512         @Override
513         public OTSNetwork getNetwork()
514         {
515             return this.network;
516         }
517 
518     }
519 
520 }