View Javadoc
1   package org.opentrafficsim.aimsun;
2   
3   import java.io.ByteArrayInputStream;
4   import java.io.IOException;
5   import java.io.InputStream;
6   import java.io.OutputStream;
7   import java.net.ServerSocket;
8   import java.net.Socket;
9   import java.nio.charset.StandardCharsets;
10  import java.rmi.RemoteException;
11  import java.util.ArrayList;
12  
13  import javax.naming.NamingException;
14  
15  import org.djunits.unit.DurationUnit;
16  import org.djunits.unit.TimeUnit;
17  import org.djunits.value.vdouble.scalar.Duration;
18  import org.djunits.value.vdouble.scalar.Time;
19  import org.opentrafficsim.aimsun.proto.AimsunControlProtoBuf;
20  import org.opentrafficsim.base.modelproperties.Property;
21  import org.opentrafficsim.base.modelproperties.PropertyException;
22  import org.opentrafficsim.core.dsol.OTSDEVSSimulatorInterface;
23  import org.opentrafficsim.core.dsol.OTSModelInterface;
24  import org.opentrafficsim.core.dsol.OTSSimTimeDouble;
25  import org.opentrafficsim.core.gtu.GTU;
26  import org.opentrafficsim.core.gtu.animation.GTUColorer;
27  import org.opentrafficsim.core.gtu.plan.operational.OperationalPlanException;
28  import org.opentrafficsim.core.network.OTSNetwork;
29  import org.opentrafficsim.road.network.factory.xml.XmlNetworkLaneParser;
30  import org.opentrafficsim.simulationengine.AbstractWrappableAnimation;
31  import org.opentrafficsim.simulationengine.OTSSimulationException;
32  import org.opentrafficsim.simulationengine.SimpleAnimator;
33  
34  import nl.tudelft.simulation.dsol.SimRuntimeException;
35  import nl.tudelft.simulation.dsol.simulators.DEVSSimulator;
36  import nl.tudelft.simulation.dsol.simulators.SimulatorInterface;
37  import nl.tudelft.simulation.event.EventInterface;
38  import nl.tudelft.simulation.event.EventListenerInterface;
39  import nl.tudelft.simulation.event.EventProducer;
40  import nl.tudelft.simulation.language.d3.DirectedPoint;
41  
42  /**
43   * <p>
44   * Copyright (c) 2013-2016 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
45   * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
46   * <p>
47   * @version $Revision$, $LastChangedDate$, by $Author$, initial version Apr 18, 2017 <br>
48   * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
49   * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
50   * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
51   */
52  public class AimsunControl extends AbstractWrappableAnimation
53  {
54  
55      /** */
56      private static final long serialVersionUID = 20160418L;
57  
58      /** XML description of the network. */
59      private String networkXML = null;
60  
61      /** Currently active Aimsun model. */
62      private AimsunModel model = null;
63  
64      /**
65       * Program entry point.
66       * @param args String[]; the command line arguments
67       */
68      public static void main(final String[] args)
69      {
70          String ip = null;
71          Integer port = null;
72  
73          for (String arg : args)
74          {
75              int equalsPos = arg.indexOf("=");
76              if (equalsPos < 0)
77              {
78                  System.err.println("Unhandled argument \"" + arg + "\"");
79              }
80              String key = arg.substring(0, equalsPos);
81              String value = arg.substring(equalsPos + 1);
82              switch (key.toUpperCase())
83              {
84                  case "IP":
85                      ip = value;
86                      break;
87                  case "PORT":
88                      try
89                      {
90                          port = Integer.parseInt(value);
91                      }
92                      catch (NumberFormatException exception)
93                      {
94                          System.err.println("Bad port number \"" + value + "\"");
95                          System.exit(1);
96                      }
97                      break;
98                  default:
99                      System.err.println("Unhandled argument \"" + arg + "\"");
100             }
101         }
102         if (null == ip || null == port)
103         {
104             System.err.println("Missing required argument(s) ip=<ip-number_or_hostname> port=<port-number>");
105             System.exit(1);
106         }
107         try
108         {
109             System.out.println("Creating server socket for port " + port);
110             ServerSocket serverSocket = new ServerSocket(port);
111             serverSocket.setReuseAddress(true); // Ensure we can be restarted without the normal delay
112             System.out.println("Waiting for client to connect");
113             Socket clientSocket = serverSocket.accept();
114             System.out.println("Client connected; closing server socket");
115             serverSocket.close(); // don't accept any other connections
116             System.out.println("Socket time out is " + clientSocket.getSoTimeout());
117             clientSocket.setSoTimeout(0);
118             System.out.println("Entering command loop");
119             AimsunControl aimsunControl = new AimsunControl();
120             aimsunControl.commandLoop(clientSocket);
121         }
122         catch (IOException exception)
123         {
124             exception.printStackTrace();
125         }
126         System.exit(0);
127     }
128     
129     /**
130      * Create a nice hex dump of a bunch of bytes.
131      * @param bytes byte[]; the bytes
132      * @return String; the hex dump
133      */
134     public static String hexDump(final byte[] bytes)
135     {
136         StringBuilder result = new StringBuilder();
137         int pos = 0;
138         for (byte b : bytes)
139         {
140             result.append(String.format("%02x", b));
141             if (pos % 16 == 0)
142             {
143                 result.append("\r\n");
144             }
145             else if (pos % 8 == 0)
146             {
147                 result.append("  ");
148             }
149             else
150             {
151                 result.append(" ");
152             }
153         }
154         return result.toString();
155     }
156 
157     /**
158      * Process incoming commands.
159      * @param socket Socket; the communications channel to Aimsun
160      * @throws IOException when communication with Aimsun fails
161      */
162     private void commandLoop(final Socket socket) throws IOException
163     {
164         InputStream inputStream = socket.getInputStream();
165         OutputStream outputStream = socket.getOutputStream();
166         while (true)
167         {
168             // byte[] in = new byte[1];
169             // inputStream.read(in);
170             // System.out.println(String.format("Got byte %02x", in[0]));
171             try
172             {
173                 byte[] sizeBytes = new byte[4];
174                 // inputStream.read(sizeBytes);
175                 fillBuffer(inputStream, sizeBytes);
176                 int size =
177                         ((sizeBytes[0] & 0xff) << 24) + ((sizeBytes[1] & 0xff) << 16) + ((sizeBytes[2] & 0xff) << 8)
178                                 + (sizeBytes[3] & 0xff);
179                 System.out.println("expecting " + size + " bytes");
180                 byte[] buffer = new byte[size];
181                 // inputStream.read(buffer);
182                 fillBuffer(inputStream, buffer);
183                 AimsunControlProtoBuf.OTSMessage message = AimsunControlProtoBuf.OTSMessage.parseFrom(buffer);
184 
185                 // AimsunControlProtoBuf.OTSMessage message = AimsunControlProtoBuf.OTSMessage.parseDelimitedFrom(inputStream);
186                 if (null == message)
187                 {
188                     System.out.println("Connection terminated; exiting");
189                     break;
190                 }
191                 switch (message.getMsgCase())
192                 {
193                     case CREATESIMULATION:
194                         System.out.println("Received CREATESIMULATION message");
195                         AimsunControlProtoBuf.CreateSimulation createSimulation = message.getCreateSimulation();
196                         this.networkXML = createSimulation.getNetworkXML();
197                         // String network = null; // IOUtils.toString(URLResource.getResource("/aimsun/singleRoad.xml"));
198                         // URLConnection conn = URLResource.getResource("/aimsun/singleRoad.xml").openConnection();
199                         // try (BufferedReader reader =
200                         // new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8)))
201                         // {
202                         // network = reader.lines().collect(Collectors.joining("\n"));
203                         // }
204                         // this.networkXML = network;
205                         Duration runDuration = new Duration(createSimulation.getRunTime(), DurationUnit.SECOND);
206                         Duration warmupDuration = new Duration(createSimulation.getWarmUpTime(), DurationUnit.SECOND);
207                         try
208                         {
209                             SimpleAnimator animator =
210                                     buildAnimator(Time.ZERO, warmupDuration, runDuration, new ArrayList<Property<?>>(), null,
211                                             true);
212                             animator.setSpeedFactor(Double.MAX_VALUE, true);
213                         }
214                         catch (SimRuntimeException | NamingException | OTSSimulationException | PropertyException exception1)
215                         {
216                             exception1.printStackTrace();
217                         }
218                         break;
219 
220                     case SIMULATEUNTIL:
221                         System.out.println("Received SIMULATEUNTIL message");
222                         if (null == this.model)
223                         {
224                             System.err.println("No model active");
225                             socket.close();
226                             break;
227                         }
228                         AimsunControlProtoBuf.SimulateUntil simulateUntil = message.getSimulateUntil();
229                         Time stopTime = new Time(simulateUntil.getTime(), TimeUnit.BASE_SECOND);
230                         System.out.println("Simulate until " + stopTime);
231                         DEVSSimulator<Time, ?, ?> simulator = (DEVSSimulator<Time, ?, ?>) this.model.getSimulator();
232                         try
233                         {
234                             simulator.runUpTo(stopTime);
235                             while (simulator.isRunning())
236                             {
237                                 try
238                                 {
239                                     Thread.sleep(10);
240                                 }
241                                 catch (InterruptedException ie)
242                                 {
243                                     ie = null; // ignore
244                                 }
245                             }
246                             AimsunControlProtoBuf.GTUPositions.Builder builder =
247                                     AimsunControlProtoBuf.GTUPositions.newBuilder();
248                             for (GTU gtu : this.model.getNetwork().getGTUs())
249                             {
250                                 AimsunControlProtoBuf.GTUPositions.GTUPosition.Builder gpb =
251                                         AimsunControlProtoBuf.GTUPositions.GTUPosition.newBuilder();
252                                 gpb.setGtuId(gtu.getId());
253                                 DirectedPoint dp = gtu.getOperationalPlan().getLocation(stopTime);
254                                 gpb.setX(dp.x);
255                                 gpb.setY(dp.y);
256                                 gpb.setZ(dp.z);
257                                 gpb.setAngle(dp.getRotZ());
258                                 builder.addGtuPos(gpb.build());
259                             }
260                             AimsunControlProtoBuf.GTUPositions gtuPositions = builder.build();
261                             AimsunControlProtoBuf.OTSMessage.Builder resultBuilder =
262                                     AimsunControlProtoBuf.OTSMessage.newBuilder();
263                             resultBuilder.setGtuPositions(gtuPositions);
264                             AimsunControlProtoBuf.OTSMessage result = resultBuilder.build();
265                             System.out.println("About to transmit " + result.toString());
266                             size = result.getSerializedSize();
267                             sizeBytes[0] = (byte) ((size >> 24) & 0xff);
268                             sizeBytes[1] = (byte) ((size >> 16) & 0xff);
269                             sizeBytes[2] = (byte) ((size >> 8) & 0xff);
270                             sizeBytes[3] = (byte) (size & 0xff);
271                             outputStream.write(sizeBytes);
272                             buffer = new byte[size];
273                             buffer = result.toByteArray();
274                             outputStream.write(buffer);
275                             // result.writeDelimitedTo(outputStream);
276                         }
277                         catch (SimRuntimeException | OperationalPlanException exception)
278                         {
279                             System.out.println("Error in runUpTo");
280                             exception.printStackTrace();
281                         }
282                         break;
283 
284                     case GTUPOSITIONS:
285                         System.out.println("Received GTUPOSITIONS message SHOULD NOT HAPPEN");
286                         socket.close();
287                         return;
288 
289                     case MSG_NOT_SET:
290                         System.out.println("Received MSG_NOT_SET message SHOULD NOT HAPPEN");
291                         socket.close();
292                         return;
293 
294                     default:
295                         System.out.println("Received unknown message SHOULD NOT HAPPEN");
296                         socket.close();
297                         break;
298                 }
299             }
300             catch (IOException exception)
301             {
302                 exception.printStackTrace();
303                 break;
304             }
305         }
306     }
307 
308     /**
309      * Fill a buffer from a stream; retry until the buffer is entirely filled.
310      * @param in InputStream; the input stream for the data
311      * @param buffer byte[]; the buffer
312      */
313     static void fillBuffer(final InputStream in, final byte[] buffer)
314     {
315         System.out.println("Need to read " + buffer.length + " bytes");
316         int offset = 0;
317         while (true)
318         {
319             try
320             {
321                 int bytesRead = in.read(buffer, offset, buffer.length - offset);
322                 if (-1 == bytesRead)
323                 {
324                     break;
325                 }
326                 offset += bytesRead;
327                 if (offset >= buffer.length)
328                 {
329                     System.out.println("Got all " + buffer.length + " requested bytes");
330                     break;
331                 }
332                 System.out.println("Now got " + offset + " bytes; need to read " + (buffer.length - offset) + " more bytes");
333             }
334             catch (Exception exception)
335             {
336                 exception.printStackTrace();
337             }
338         }
339     }
340 
341     /** {@inheritDoc} */
342     @Override
343     public final String shortName()
344     {
345         return "AimsunControlledOTS";
346     }
347 
348     /** {@inheritDoc} */
349     @Override
350     public final String description()
351     {
352         return "Aimsun controlled OTS engine";
353     }
354 
355     /** {@inheritDoc} */
356     @Override
357     protected final OTSModelInterface makeModel(final GTUColorer colorer) throws OTSSimulationException
358     {
359         this.model = new AimsunModel();
360         return this.model;
361     }
362 
363     /**
364      * The network.
365      */
366     class AimsunModel extends EventProducer implements OTSModelInterface, EventListenerInterface
367     {
368 
369         /** */
370         private static final long serialVersionUID = 20170419L;
371 
372         /** The network. */
373         private OTSNetwork network;
374 
375         /** The simulator. */
376         private SimulatorInterface<Time, Duration, OTSSimTimeDouble> simulator;
377 
378         /** {@inheritDoc} */
379         @Override
380         public void constructModel(final SimulatorInterface<Time, Duration, OTSSimTimeDouble> theSimulator)
381                 throws SimRuntimeException, RemoteException
382         {
383             try
384             {
385                 this.simulator = theSimulator;
386                 // URL url = URLResource.getResource("/aimsun/singleRoad.xml");
387                 XmlNetworkLaneParser nlp = new XmlNetworkLaneParser((OTSDEVSSimulatorInterface) theSimulator);
388                 String xml = AimsunControl.this.networkXML;
389                 this.network = nlp.build(new ByteArrayInputStream(xml.getBytes(StandardCharsets.UTF_8)));
390             }
391             catch (Exception exception)
392             {
393                 exception.printStackTrace();
394             }
395         }
396 
397         /** {@inheritDoc} */
398         @Override
399         public SimulatorInterface<Time, Duration, OTSSimTimeDouble> getSimulator() throws RemoteException
400         {
401             return this.simulator;
402         }
403 
404         /** {@inheritDoc} */
405         @Override
406         public void notify(final EventInterface event) throws RemoteException
407         {
408             // WIP
409         }
410 
411         /** {@inheritDoc} */
412         @Override
413         public OTSNetwork getNetwork()
414         {
415             return this.network;
416         }
417 
418     }
419 
420 }