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,
98              ValueRuntimeException, 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", new Object[] {outputStream});
236         }
237         catch (SimRuntimeException exception)
238         {
239             exception.printStackTrace();
240         }
241         
242     }
243 
244     
245 
246 
247 
248 
249 
250 
251 
252 
253 
254 
255     private void commandLoop(final Socket socket) throws IOException, NetworkException, OTSGeometryException, NamingException,
256             ValueRuntimeException, ParameterException, SimRuntimeException
257     {
258         System.out.println("Entering command loop");
259         InputStream inputStream = socket.getInputStream();
260         OutputStream outputStream = socket.getOutputStream();
261         String error = null;
262         boolean simulatorStarted = false;
263         while (null == error)
264         {
265             try
266             {
267                 if (waitingForSimulator)
268                 {
269                     
270                     
271                     while (waitingForSimulator)
272                     {
273                         if (!this.model.getSimulator().isRunning())
274                         {
275                             System.out.println("Simulator has stopped; constructing GTUPositions message with error status");
276                             Time stopTime = this.model.getSimulator().getSimulatorTime();
277                             AimsunControlProtoBuf.GTUPositions.Builder builder =
278                                     AimsunControlProtoBuf.GTUPositions.newBuilder();
279                             for (GTU gtu : this.model.getNetwork().getGTUs())
280                             {
281                                 AimsunControlProtoBuf.GTUPositions.GTUPosition.Builder gpb =
282                                         AimsunControlProtoBuf.GTUPositions.GTUPosition.newBuilder();
283                                 gpb.setGtuId(gtu.getId());
284                                 DirectedPoint dp;
285                                 try
286                                 {
287                                     dp = gtu.getOperationalPlan().getLocation(stopTime);
288                                     gpb.setX(dp.x);
289                                     gpb.setY(dp.y);
290                                     gpb.setZ(dp.z);
291                                     gpb.setAngle(dp.getRotZ());
292                                     gpb.setLength(gtu.getLength().si);
293                                     gpb.setWidth(gtu.getWidth().si);
294                                     gpb.setGtuTypeId(Integer.parseInt(gtu.getGTUType().getId().split("\\.")[1]));
295                                     gpb.setSpeed(gtu.getSpeed().si);
296                                     builder.addGtuPos(gpb.build());
297                                 }
298                                 catch (OperationalPlanException exception)
299                                 {
300                                     exception.printStackTrace();
301                                 }
302                             }
303                             builder.setStatus("ERROR");
304                             GTUPositions gtuPositions = builder.build();
305                             AimsunControlProtoBuf.OTSMessage.Builder resultBuilder =
306                                     AimsunControlProtoBuf.OTSMessage.newBuilder();
307                             resultBuilder.setGtuPositions(gtuPositions);
308                             AimsunControlProtoBuf.OTSMessage result = resultBuilder.build();
309                             this.simulateUntil = null;
310                             try
311                             {
312                                 transmitMessage(result, outputStream);
313                             }
314                             catch (IOException exception)
315                             {
316                                 exception.printStackTrace();
317                             }
318                             System.out.println("Error message sent");
319                             
320                             waitingForSimulator = false;
321                             break;
322                         }
323                         try
324                         {
325                             Thread.sleep(1);
326                         }
327                         catch (InterruptedException exception)
328                         {
329                             System.err.println("Interrupted exception (command loop)");
330                             
331                         }
332                     }
333                     
334                     
335                 }
336                 byte[] sizeBytes = new byte[4];
337                 
338                 fillBuffer(inputStream, sizeBytes);
339                 int size = ((sizeBytes[0] & 0xff) << 24) + ((sizeBytes[1] & 0xff) << 16) + ((sizeBytes[2] & 0xff) << 8)
340                         + (sizeBytes[3] & 0xff);
341                 
342                 byte[] buffer = new byte[size];
343                 fillBuffer(inputStream, buffer);
344                 AimsunControlProtoBuf.OTSMessage message = AimsunControlProtoBuf.OTSMessage.parseFrom(buffer);
345 
346                 if (null == message)
347                 {
348                     System.out.println("Connection terminated; exiting");
349                     break;
350                 }
351                 switch (message.getMsgCase())
352                 {
353                     case CREATESIMULATION:
354                         System.out.println("Received CREATESIMULATION message");
355                         AimsunControlProtoBuf.CreateSimulation createSimulation = message.getCreateSimulation();
356                         String networkXML = createSimulation.getNetworkXML();
357                         try (PrintWriter pw = new PrintWriter("c:/Temp/AimsunOtsNetwork.xml"))
358                         {
359                             pw.print(networkXML);
360                         }
361                         Duration runDuration = new Duration(3600, DurationUnit.SECOND);
362                         System.out.println("runDuration " + runDuration);
363                         Duration warmupDuration = new Duration(0, DurationUnit.SECOND);
364                         try
365                         {
366                             OTSAnimator animator = new OTSLoggingAnimator("C:/Temp/AimsunEventlog.txt");
367                             this.model = new AimsunModel(animator, "Aimsun generated model", "Aimsun model", networkXML);
368                             Map<String, StreamInterface> map = new LinkedHashMap<>();
369                             
370                             map.put("generation", new MersenneTwister(6L));
371                             animator.initialize(Time.ZERO, warmupDuration, runDuration, this.model, map);
372                             OTSAnimationPanel animationPanel =
373                                     new OTSAnimationPanel(this.model.getNetwork().getExtent(), new Dimension(1100, 1000),
374                                             animator, this.model, OTSSwingApplication.DEFAULT_COLORER, this.model.getNetwork());
375                             DefaultAnimationFactory.animateXmlNetwork(this.model.getNetwork(), animator,
376                                     new DefaultSwitchableGTUColorer());
377                             new AimsunSwingApplication(this.model, animationPanel);
378                             JFrame frame = (JFrame) animationPanel.getParent().getParent().getParent();
379                             frame.setExtendedState(Frame.NORMAL);
380                             frame.setSize(new Dimension(1100, 1000));
381                             frame.setBounds(0, 25, 1100, 1000);
382                             animator.setSpeedFactor(Double.MAX_VALUE, true);
383                             animator.setSpeedFactor(1000.0, true);
384                             try
385                             {
386                                 Thread.sleep(300);
387                             }
388                             catch (InterruptedException e)
389                             {
390                                 e.printStackTrace();
391                             }
392                             animationPanel.actionPerformed(new ActionEvent(this, 0, "ZoomAll"));
393                         }
394                         catch (SimRuntimeException | NamingException | OTSDrawingException exception1)
395                         {
396                             exception1.printStackTrace();
397                             
398                             error = "XML ERROR";
399                         }
400                         break;
401 
402                     case SIMULATEUNTIL:
403                     {
404                         AimsunControlProtoBuf.SimulateUntil simulateUntilThing = message.getSimulateUntil();
405                         Time stopTime = new Time(simulateUntilThing.getTime(), TimeUnit.BASE_SECOND);
406                         System.out.println("Received SIMULATEUNTIL " + stopTime + " message");
407                         OTSSimulatorInterface simulator = this.model.getSimulator();
408                         if (!simulatorStarted)
409                         {
410                             simulatorStarted = true;
411                             simulator.scheduleEventAbs(stopTime, this, this, "sendGTUPositionsToAimsun",
412                                     new Object[] {outputStream});
413                             System.out.println("Starting simulator");
414                             this.simulateUntil = stopTime;
415                             simulator.start();
416                         }
417                         else if (!simulator.isRunning())
418                         {
419                             
420                             error = "HMM Simulator stopped";
421                         }
422                         else
423                         {
424                             
425                             waitingForSimulator = true;
426                             this.simulateUntil = stopTime;
427                         }
428                         break;
429                     }
430 
431                     case GTUPOSITIONS:
432                         System.out.println("Received GTUPOSITIONS message SHOULD NOT HAPPEN");
433                         socket.close();
434                         return;
435 
436                     case MSG_NOT_SET:
437                         System.out.println("Received MSG_NOT_SET message SHOULD NOT HAPPEN");
438                         socket.close();
439                         return;
440 
441                     default:
442                         System.out.println("Received unknown message SHOULD NOT HAPPEN");
443                         socket.close();
444                         break;
445                 }
446             }
447             catch (IOException exception)
448             {
449                 exception.printStackTrace();
450                 break;
451             }
452         }
453     }
454 
455     
456 
457 
458 
459 
460 
461     private void transmitMessage(final AimsunControlProtoBuf.OTSMessage message, final OutputStream outputStream)
462             throws IOException
463     {
464         int size = message.getSerializedSize();
465         
466         
467         byte[] sizeBytes = new byte[4];
468         sizeBytes[0] = (byte) ((size >> 24) & 0xff);
469         sizeBytes[1] = (byte) ((size >> 16) & 0xff);
470         sizeBytes[2] = (byte) ((size >> 8) & 0xff);
471         sizeBytes[3] = (byte) (size & 0xff);
472         outputStream.write(sizeBytes);
473         byte[] buffer = new byte[size];
474         buffer = message.toByteArray();
475         outputStream.write(buffer);
476         
477     }
478 
479     
480 
481 
482 
483 
484 
485     static void fillBuffer(final InputStream in, final byte[] buffer) throws IOException
486     {
487         
488         int offset = 0;
489         while (true)
490         {
491             int bytesRead = in.read(buffer, offset, buffer.length - offset);
492             if (-1 == bytesRead)
493             {
494                 break;
495             }
496             offset += bytesRead;
497             if (buffer.length == offset)
498             {
499                 
500                 break;
501             }
502             if (buffer.length < offset)
503             {
504                 System.err.println("fillBuffer: Oops: Got more than " + buffer.length + " requested bytes");
505                 break;
506             }
507             
508             
509         }
510         if (offset != buffer.length)
511         {
512             throw new IOException("Got only " + offset + " of expected " + buffer.length + " bytes");
513         }
514     }
515 
516     
517 
518 
519     class AimsunSwingApplication extends OTSSimulationApplication<OTSModelInterface>
520     {
521         
522         private static final long serialVersionUID = 1L;
523 
524         
525 
526 
527 
528 
529         AimsunSwingApplication(final OTSModelInterface model, final OTSAnimationPanel panel) throws OTSDrawingException
530         {
531             super(model, panel);
532         }
533     }
534 
535     
536 
537 
538     class AimsunModel extends AbstractOTSModel implements EventListenerInterface
539     {
540         
541         private static final long serialVersionUID = 20170419L;
542 
543         
544         private OTSRoadNetwork network;
545 
546         
547         private final String xml;
548 
549         
550 
551 
552 
553 
554 
555         AimsunModel(final OTSSimulatorInterface simulator, final String shortName, final String description, final String xml)
556         {
557             super(simulator, shortName, description);
558             this.xml = xml;
559         }
560 
561         
562         @Override
563         public void notify(final EventInterface event) throws RemoteException
564         {
565             System.err.println("Received event " + event);
566         }
567 
568         
569         @Override
570         public void constructModel() throws SimRuntimeException
571         {
572             try
573             {
574                 this.simulator.addListener(this, DEVSRealTimeClock.CHANGE_SPEED_FACTOR_EVENT);
575                 this.simulator.addListener(this, SimulatorInterface.TIME_CHANGED_EVENT);
576             }
577             catch (RemoteException exception1)
578             {
579                 exception1.printStackTrace();
580             }
581             this.network = new OTSRoadNetwork(getShortName(), true);
582             try
583             {
584                 XmlNetworkLaneParser.build(new ByteArrayInputStream(this.xml.getBytes(StandardCharsets.UTF_8)), this.network,
585                         getSimulator(), false);
586                 LaneCombinationList ignoreList = new LaneCombinationList();
587                 try
588                 {
589                     
590                     ignoreList.addLinkCombination((CrossSectionLink) this.network.getLink("928_J5"),
591                             (CrossSectionLink) this.network.getLink("928_J6"));
592                     ignoreList.addLinkCombination((CrossSectionLink) this.network.getLink("925_J1"),
593                             (CrossSectionLink) this.network.getLink("925_J2"));
594                 }
595                 catch (NullPointerException npe)
596                 {
597                     
598                 }
599                 LaneCombinationList permittedList = new LaneCombinationList();
600                 ConflictBuilder.buildConflictsParallel(this.network, this.network.getGtuType(GTUType.DEFAULTS.VEHICLE),
601                         getSimulator(), new ConflictBuilder.FixedWidthGenerator(Length.instantiateSI(2.0)), ignoreList,
602                         permittedList);
603                 new GTUDumper(simulator, Time.ZERO, Duration.instantiateSI(60), network, "C:/Temp/aimsun");
604             }
605             catch (NetworkException | OTSGeometryException | JAXBException | URISyntaxException | XmlParserException
606                     | SAXException | ParserConfigurationException | GTUException exception)
607             {
608                 exception.printStackTrace();
609                 
610                 
611                 throw new SimRuntimeException(exception);
612             }
613         }
614 
615         
616         @Override
617         public OTSNetwork getNetwork()
618         {
619             return this.network;
620         }
621 
622     }
623 
624 }