1 package org.opentrafficsim.aimsun;
2
3 import java.awt.Dimension;
4 import java.io.ByteArrayInputStream;
5 import java.io.IOException;
6 import java.io.InputStream;
7 import java.io.OutputStream;
8 import java.io.PrintWriter;
9 import java.net.ServerSocket;
10 import java.net.Socket;
11 import java.nio.charset.StandardCharsets;
12 import java.rmi.RemoteException;
13
14 import javax.naming.NamingException;
15 import javax.xml.parsers.ParserConfigurationException;
16
17 import nl.tudelft.simulation.dsol.SimRuntimeException;
18 import nl.tudelft.simulation.dsol.logger.SimLogger;
19 import nl.tudelft.simulation.dsol.simulators.DEVSRealTimeClock;
20 import nl.tudelft.simulation.dsol.simulators.SimulatorInterface;
21 import nl.tudelft.simulation.event.EventInterface;
22 import nl.tudelft.simulation.event.EventListenerInterface;
23 import nl.tudelft.simulation.language.d3.DirectedPoint;
24
25 import org.djunits.unit.DurationUnit;
26 import org.djunits.unit.TimeUnit;
27 import org.djunits.value.ValueException;
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.logger.LogCategory;
32 import org.opentrafficsim.aimsun.proto.AimsunControlProtoBuf;
33 import org.opentrafficsim.aimsun.proto.AimsunControlProtoBuf.GTUPositions;
34 import org.opentrafficsim.base.parameters.ParameterException;
35 import org.opentrafficsim.core.animation.gtu.colorer.DefaultSwitchableGTUColorer;
36 import org.opentrafficsim.core.dsol.AbstractOTSModel;
37 import org.opentrafficsim.core.dsol.OTSAnimator;
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.GTUException;
43 import org.opentrafficsim.core.gtu.GTUType;
44 import org.opentrafficsim.core.gtu.plan.operational.OperationalPlanException;
45 import org.opentrafficsim.core.network.NetworkException;
46 import org.opentrafficsim.core.network.OTSNetwork;
47 import org.opentrafficsim.draw.core.OTSDrawingException;
48 import org.opentrafficsim.draw.factory.DefaultAnimationFactory;
49 import org.opentrafficsim.road.network.factory.xml.old.XmlNetworkLaneParserOld;
50 import org.opentrafficsim.road.network.lane.conflict.ConflictBuilder;
51 import org.opentrafficsim.swing.gui.OTSAnimationPanel;
52 import org.opentrafficsim.swing.gui.OTSSimulationApplication;
53 import org.opentrafficsim.swing.gui.OTSSwingApplication;
54 import org.pmw.tinylog.Level;
55 import org.xml.sax.SAXException;
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,
86 ValueException, ParameterException, SimRuntimeException
87 {
88 SimLogger.setAllLogLevel(Level.WARNING);
89 SimLogger.setLogCategories(LogCategory.ALL);
90
91 String ip = null;
92 Integer port = null;
93
94 for (String arg : args)
95 {
96 int equalsPos = arg.indexOf("=");
97 if (equalsPos < 0)
98 {
99 System.err.println("Unhandled argument \"" + arg + "\"");
100 }
101 String key = arg.substring(0, equalsPos);
102 String value = arg.substring(equalsPos + 1);
103 switch (key.toUpperCase())
104 {
105 case "IP":
106 ip = value;
107 break;
108 case "PORT":
109 try
110 {
111 port = Integer.parseInt(value);
112 }
113 catch (NumberFormatException exception)
114 {
115 System.err.println("Bad port number \"" + value + "\"");
116 System.exit(1);
117 }
118 break;
119 default:
120 System.err.println("Unhandled argument \"" + arg + "\"");
121 }
122 }
123 if (null == ip || null == port)
124 {
125 System.err.println("Missing required argument(s) ip=<ip-number_or_hostname> port=<port-number>");
126 System.exit(1);
127 }
128 try
129 {
130 System.out.println("Creating server socket for port " + port);
131 ServerSocket serverSocket = new ServerSocket(port);
132 serverSocket.setReuseAddress(true);
133 System.out.println("Waiting for client to connect");
134 Socket clientSocket = serverSocket.accept();
135 System.out.println("Client connected; closing server socket");
136 serverSocket.close();
137 System.out.println("Socket time out is " + clientSocket.getSoTimeout());
138 clientSocket.setSoTimeout(0);
139 System.out.println("Constructing animation/simulation");
140 AimsunControl aimsunControl = new AimsunControl();
141 aimsunControl.commandLoop(clientSocket);
142 }
143 catch (IOException exception)
144 {
145 exception.printStackTrace();
146 }
147 System.exit(0);
148 }
149
150
151 private Time simulateUntil = null;
152
153
154
155
156
157
158 protected void sendGTUPositionsToAimsun(final OutputStream outputStream)
159 {
160 OTSSimulatorInterface simulator = this.model.getSimulator();
161 System.out.println("Simulator has stopped at time " + simulator.getSimulatorTime());
162 Time stopTime = simulator.getSimulatorTime();
163 AimsunControlProtoBuf.GTUPositions.Builder builder = AimsunControlProtoBuf.GTUPositions.newBuilder();
164 for (GTU gtu : this.model.getNetwork().getGTUs())
165 {
166 AimsunControlProtoBuf.GTUPositions.GTUPosition.Builder gpb =
167 AimsunControlProtoBuf.GTUPositions.GTUPosition.newBuilder();
168 gpb.setGtuId(gtu.getId());
169 DirectedPoint dp;
170 try
171 {
172 dp = gtu.getOperationalPlan().getLocation(stopTime);
173 gpb.setX(dp.x);
174 gpb.setY(dp.y);
175 gpb.setZ(dp.z);
176 gpb.setAngle(dp.getRotZ());
177 gpb.setLength(gtu.getLength().si);
178 gpb.setWidth(gtu.getWidth().si);
179 gpb.setGtuTypeId(Integer.parseInt(gtu.getGTUType().getId().split("\\.")[1]));
180 gpb.setSpeed(gtu.getSpeed().si);
181 builder.addGtuPos(gpb.build());
182 }
183 catch (OperationalPlanException exception)
184 {
185 exception.printStackTrace();
186 }
187 }
188 builder.setStatus("OK");
189 GTUPositions gtuPositions = builder.build();
190 AimsunControlProtoBuf.OTSMessage.Builder resultBuilder = AimsunControlProtoBuf.OTSMessage.newBuilder();
191 resultBuilder.setGtuPositions(gtuPositions);
192 AimsunControlProtoBuf.OTSMessage result = resultBuilder.build();
193 this.simulateUntil = null;
194 try
195 {
196 transmitMessage(result, outputStream);
197 }
198 catch (IOException exception)
199 {
200 exception.printStackTrace();
201 }
202 System.out.println("Simulator waiting for new simulateUntil value");
203 while (this.simulateUntil == null)
204 {
205 try
206 {
207 Thread.sleep(1);
208 }
209 catch (InterruptedException exception)
210 {
211
212 }
213 }
214 try
215 {
216 simulator.scheduleEventAbs(this.simulateUntil, this, this, "sendGTUPositionsToAimsun",
217 new Object[] { outputStream });
218 }
219 catch (SimRuntimeException exception)
220 {
221 exception.printStackTrace();
222 }
223 System.out.println("Simulator resuming");
224 }
225
226
227
228
229
230
231
232
233
234
235
236
237 private void commandLoop(final Socket socket) throws IOException, NetworkException, OTSGeometryException,
238 NamingException, ValueException, ParameterException, SimRuntimeException
239 {
240 System.out.println("Entering command loop");
241 InputStream inputStream = socket.getInputStream();
242 OutputStream outputStream = socket.getOutputStream();
243 String error = null;
244 boolean simulatorStarted = false;
245 while (null == error)
246 {
247 try
248 {
249 byte[] sizeBytes = new byte[4];
250 fillBuffer(inputStream, sizeBytes);
251 int size =
252 ((sizeBytes[0] & 0xff) << 24) + ((sizeBytes[1] & 0xff) << 16) + ((sizeBytes[2] & 0xff) << 8)
253 + (sizeBytes[3] & 0xff);
254 System.out.println("expecting message of " + size + " bytes");
255 byte[] buffer = new byte[size];
256 fillBuffer(inputStream, buffer);
257 AimsunControlProtoBuf.OTSMessage message = AimsunControlProtoBuf.OTSMessage.parseFrom(buffer);
258
259 if (null == message)
260 {
261 System.out.println("Connection terminated; exiting");
262 break;
263 }
264 switch (message.getMsgCase())
265 {
266 case CREATESIMULATION:
267 System.out.println("Received CREATESIMULATION message");
268 AimsunControlProtoBuf.CreateSimulation createSimulation = message.getCreateSimulation();
269 this.networkXML = createSimulation.getNetworkXML();
270 try (PrintWriter pw = new PrintWriter("d:/AimsunOtsNetwork.xml"))
271 {
272 pw.print(this.networkXML);
273 }
274 Duration runDuration = new Duration(createSimulation.getRunTime(), DurationUnit.SECOND);
275 System.out.println("runDuration " + runDuration);
276 Duration warmupDuration = new Duration(createSimulation.getWarmUpTime(), DurationUnit.SECOND);
277 try
278 {
279 OTSAnimator animator = new OTSAnimator();
280 this.model = new AimsunModel(animator, "", "");
281 animator.initialize(Time.ZERO, warmupDuration, runDuration, this.model);
282 OTSAnimationPanel animationPanel =
283 new OTSAnimationPanel(this.model.getNetwork().getExtent(), new Dimension(800, 600),
284 animator, this.model, OTSSwingApplication.DEFAULT_COLORER,
285 this.model.getNetwork());
286 DefaultAnimationFactory.animateXmlNetwork(this.model.getNetwork(), animator,
287 new DefaultSwitchableGTUColorer());
288 new AimsunSwingApplication(this.model, animationPanel);
289 animator.setSpeedFactor(Double.MAX_VALUE, true);
290 animator.setSpeedFactor(1000.0, true);
291 }
292 catch (SimRuntimeException | NamingException | OTSDrawingException exception1)
293 {
294 exception1.printStackTrace();
295
296 error = "XML ERROR";
297 }
298 break;
299
300 case SIMULATEUNTIL:
301 {
302 System.out.println("Received SIMULATEUNTIL message");
303 AimsunControlProtoBuf.SimulateUntil simulateUntilThing = message.getSimulateUntil();
304 Time stopTime = new Time(simulateUntilThing.getTime(), TimeUnit.BASE_SECOND);
305 System.out.println("Simulate until " + stopTime + " ");
306 OTSSimulatorInterface simulator = this.model.getSimulator();
307 if (!simulatorStarted)
308 {
309 simulatorStarted = true;
310 simulator.scheduleEventAbs(stopTime, this, this, "sendGTUPositionsToAimsun",
311 new Object[] { outputStream });
312 System.out.println("Starting simulator");
313 this.simulateUntil = stopTime;
314 simulator.start();
315 }
316 else if (!simulator.isRunning())
317 {
318
319 error = "HMM Simulator stopped";
320 }
321 else
322 {
323 System.out.println("Resuming simulator");
324 this.simulateUntil = stopTime;
325 }
326 break;
327 }
328
329 case GTUPOSITIONS:
330 System.out.println("Received GTUPOSITIONS message SHOULD NOT HAPPEN");
331 socket.close();
332 return;
333
334 case MSG_NOT_SET:
335 System.out.println("Received MSG_NOT_SET message SHOULD NOT HAPPEN");
336 socket.close();
337 return;
338
339 default:
340 System.out.println("Received unknown message SHOULD NOT HAPPEN");
341 socket.close();
342 break;
343 }
344 }
345 catch (IOException exception)
346 {
347 exception.printStackTrace();
348 break;
349 }
350 }
351 }
352
353
354
355
356
357
358
359 private void transmitMessage(final AimsunControlProtoBuf.OTSMessage message, final OutputStream outputStream)
360 throws IOException
361 {
362 int size = message.getSerializedSize();
363 System.out.print("Transmitting " + message.getGtuPositions().getGtuPosCount() + " GTU positions and status \""
364 + message.getGtuPositions().getStatus() + "\" encoded in " + size + " bytes ... ");
365 byte[] sizeBytes = new byte[4];
366 sizeBytes[0] = (byte) ((size >> 24) & 0xff);
367 sizeBytes[1] = (byte) ((size >> 16) & 0xff);
368 sizeBytes[2] = (byte) ((size >> 8) & 0xff);
369 sizeBytes[3] = (byte) (size & 0xff);
370 outputStream.write(sizeBytes);
371 byte[] buffer = new byte[size];
372 buffer = message.toByteArray();
373 outputStream.write(buffer);
374 System.out.println("Message sent");
375 }
376
377
378
379
380
381
382
383 static void fillBuffer(final InputStream in, final byte[] buffer) throws IOException
384 {
385 System.out.print("Need to read " + buffer.length + " bytes ... ");
386 int offset = 0;
387 while (true)
388 {
389 int bytesRead = in.read(buffer, offset, buffer.length - offset);
390 if (-1 == bytesRead)
391 {
392 break;
393 }
394 offset += bytesRead;
395 if (buffer.length == offset)
396 {
397 System.out.println("got all " + buffer.length + " requested bytes");
398 break;
399 }
400 if (buffer.length < offset)
401 {
402 System.out.println("Oops: Got more than " + buffer.length + " requested bytes");
403 break;
404 }
405 System.out.print("now got " + offset + " bytes; need to read " + (buffer.length - offset) + " more bytes ... ");
406 }
407 if (offset != buffer.length)
408 {
409 throw new IOException("Got only " + offset + " of expected " + buffer.length + " bytes");
410 }
411 }
412
413
414
415
416 class AimsunSwingApplication extends OTSSimulationApplication<OTSModelInterface>
417 {
418
419 private static final long serialVersionUID = 1L;
420
421
422
423
424
425
426 public AimsunSwingApplication(final OTSModelInterface model, final OTSAnimationPanel panel)
427 throws OTSDrawingException
428 {
429 super(model, panel);
430 }
431 }
432
433
434
435
436 class AimsunModel extends AbstractOTSModel implements EventListenerInterface
437 {
438
439
440
441
442
443 public AimsunModel(OTSSimulatorInterface simulator, String shortName, String description)
444 {
445 super(simulator, shortName, description);
446 }
447
448
449 private static final long serialVersionUID = 20170419L;
450
451
452 private OTSNetwork network;
453
454
455 @Override
456 public void constructModel() throws SimRuntimeException
457 {
458 try
459 {
460 this.simulator.addListener(this, DEVSRealTimeClock.CHANGE_SPEED_FACTOR_EVENT);
461 this.simulator.addListener(this, SimulatorInterface.TIME_CHANGED_EVENT);
462 }
463 catch (RemoteException exception1)
464 {
465 exception1.printStackTrace();
466 }
467
468 XmlNetworkLaneParserOld nlp = new XmlNetworkLaneParserOld(this.simulator);
469 @SuppressWarnings("synthetic-access")
470 String xml = AimsunControl.this.networkXML;
471 try
472 {
473 this.network = nlp.build(new ByteArrayInputStream(xml.getBytes(StandardCharsets.UTF_8)), false);
474 ConflictBuilder.buildConflicts(this.network, GTUType.VEHICLE, this.simulator,
475 new ConflictBuilder.FixedWidthGenerator(Length.createSI(2.0)));
476 }
477 catch (NetworkException | ParserConfigurationException | SAXException | IOException | NamingException
478 | GTUException | OTSGeometryException | ValueException | ParameterException | SimRuntimeException exception)
479 {
480 exception.printStackTrace();
481 throw new SimRuntimeException(exception);
482 }
483 }
484
485
486 @Override
487 public void notify(EventInterface event) throws RemoteException
488 {
489 System.out.println("Received event " + event);
490 }
491
492
493 @Override
494 public OTSNetwork getNetwork()
495 {
496 return this.network;
497 }
498
499 }
500
501 }