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
59
60
61
62
63
64
65
66
67 public class AimsunControl
68 {
69
70 private String networkXML = null;
71
72
73 private AimsunModel model = null;
74
75
76
77
78
79
80
81
82
83
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);
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();
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
150
151
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
178
179
180
181
182
183
184
185
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
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
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
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
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
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
375
376
377
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
399
400
401
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
435
436 class AimsunSwingApplication extends OTSSimulationApplication<OTSModelInterface>
437 {
438
439 private static final long serialVersionUID = 1L;
440
441
442
443
444
445
446 public AimsunSwingApplication(final OTSModelInterface model, final OTSAnimationPanel panel) throws OTSDrawingException
447 {
448 super(model, panel);
449 }
450 }
451
452
453
454
455 class AimsunModel extends AbstractOTSModel implements EventListenerInterface
456 {
457
458
459
460
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
471 private OTSNetwork network;
472
473
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
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
505 @Override
506 public void notify(EventInterface event) throws RemoteException
507 {
508 System.out.println("Received event " + event);
509 }
510
511
512 @Override
513 public OTSNetwork getNetwork()
514 {
515 return this.network;
516 }
517
518 }
519
520 }