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.io.PrintWriter;
8 import java.net.ServerSocket;
9 import java.net.Socket;
10 import java.nio.charset.StandardCharsets;
11 import java.rmi.RemoteException;
12 import java.util.ArrayList;
13
14 import javax.naming.NamingException;
15 import javax.xml.parsers.ParserConfigurationException;
16
17 import org.djunits.unit.DurationUnit;
18 import org.djunits.unit.TimeUnit;
19 import org.djunits.value.ValueException;
20 import org.djunits.value.vdouble.scalar.Duration;
21 import org.djunits.value.vdouble.scalar.Time;
22 import org.opentrafficsim.aimsun.proto.AimsunControlProtoBuf;
23 import org.opentrafficsim.aimsun.proto.AimsunControlProtoBuf.GTUPositions;
24 import org.opentrafficsim.base.modelproperties.Property;
25 import org.opentrafficsim.base.modelproperties.PropertyException;
26 import org.opentrafficsim.base.parameters.ParameterException;
27 import org.opentrafficsim.core.dsol.OTSModelInterface;
28 import org.opentrafficsim.core.geometry.OTSGeometryException;
29 import org.opentrafficsim.core.gtu.GTU;
30 import org.opentrafficsim.core.gtu.GTUException;
31 import org.opentrafficsim.core.gtu.plan.operational.OperationalPlanException;
32 import org.opentrafficsim.core.network.NetworkException;
33 import org.opentrafficsim.core.network.OTSLink;
34 import org.opentrafficsim.core.network.OTSNetwork;
35 import org.opentrafficsim.core.network.OTSNode;
36 import org.opentrafficsim.road.animation.AnimationToggles;
37 import org.opentrafficsim.road.network.factory.xml.XmlNetworkLaneParser;
38 import org.opentrafficsim.road.network.lane.object.SpeedSign;
39 import org.opentrafficsim.simulationengine.AbstractWrappableAnimation;
40 import org.opentrafficsim.simulationengine.OTSSimulationException;
41 import org.opentrafficsim.simulationengine.SimpleAnimator;
42 import org.xml.sax.SAXException;
43
44 import nl.tudelft.simulation.dsol.SimRuntimeException;
45 import nl.tudelft.simulation.dsol.simtime.SimTimeDoubleUnit;
46 import nl.tudelft.simulation.dsol.simulators.DEVSSimulator;
47 import nl.tudelft.simulation.dsol.simulators.DEVSSimulatorInterface;
48 import nl.tudelft.simulation.dsol.simulators.SimulatorInterface;
49 import nl.tudelft.simulation.event.EventInterface;
50 import nl.tudelft.simulation.event.EventListenerInterface;
51 import nl.tudelft.simulation.event.EventProducer;
52 import nl.tudelft.simulation.language.d3.DirectedPoint;
53
54
55
56
57
58
59
60
61
62
63
64 public class AimsunControl extends AbstractWrappableAnimation
65 {
66
67
68 private static final long serialVersionUID = 20160418L;
69
70
71 private String networkXML = null;
72
73
74 private AimsunModel model = null;
75
76
77
78
79
80
81
82
83
84
85
86 public static void main(final String[] args) throws NetworkException, OTSGeometryException, NamingException, ValueException,
87 ParameterException, SimRuntimeException
88 {
89 String ip = null;
90 Integer port = null;
91
92 for (String arg : args)
93 {
94 int equalsPos = arg.indexOf("=");
95 if (equalsPos < 0)
96 {
97 System.err.println("Unhandled argument \"" + arg + "\"");
98 }
99 String key = arg.substring(0, equalsPos);
100 String value = arg.substring(equalsPos + 1);
101 switch (key.toUpperCase())
102 {
103 case "IP":
104 ip = value;
105 break;
106 case "PORT":
107 try
108 {
109 port = Integer.parseInt(value);
110 }
111 catch (NumberFormatException exception)
112 {
113 System.err.println("Bad port number \"" + value + "\"");
114 System.exit(1);
115 }
116 break;
117 default:
118 System.err.println("Unhandled argument \"" + arg + "\"");
119 }
120 }
121 if (null == ip || null == port)
122 {
123 System.err.println("Missing required argument(s) ip=<ip-number_or_hostname> port=<port-number>");
124 System.exit(1);
125 }
126 try
127 {
128 System.out.println("Creating server socket for port " + port);
129 ServerSocket serverSocket = new ServerSocket(port);
130 serverSocket.setReuseAddress(true);
131 System.out.println("Waiting for client to connect");
132 Socket clientSocket = serverSocket.accept();
133 System.out.println("Client connected; closing server socket");
134 serverSocket.close();
135 System.out.println("Socket time out is " + clientSocket.getSoTimeout());
136 clientSocket.setSoTimeout(0);
137 System.out.println("Constructing animation/simulation");
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
197
198
199 try
200 {
201 byte[] sizeBytes = new byte[4];
202
203 fillBuffer(inputStream, sizeBytes);
204 int size = ((sizeBytes[0] & 0xff) << 24) + ((sizeBytes[1] & 0xff) << 16) + ((sizeBytes[2] & 0xff) << 8)
205 + (sizeBytes[3] & 0xff);
206 System.out.println("expecting message of " + size + " bytes");
207 byte[] buffer = new byte[size];
208
209 fillBuffer(inputStream, buffer);
210 AimsunControlProtoBuf.OTSMessage message = AimsunControlProtoBuf.OTSMessage.parseFrom(buffer);
211
212
213 if (null == message)
214 {
215 System.out.println("Connection terminated; exiting");
216 break;
217 }
218 switch (message.getMsgCase())
219 {
220 case CREATESIMULATION:
221 System.out.println("Received CREATESIMULATION message");
222 AimsunControlProtoBuf.CreateSimulation createSimulation = message.getCreateSimulation();
223 this.networkXML = createSimulation.getNetworkXML();
224 try (PrintWriter pw = new PrintWriter("d:/AimsunOtsNetwork.xml"))
225 {
226 pw.print(this.networkXML);
227 }
228 Duration runDuration = new Duration(createSimulation.getRunTime(), DurationUnit.SECOND);
229 System.out.println("runDuration " + runDuration);
230 Duration warmupDuration = new Duration(createSimulation.getWarmUpTime(), DurationUnit.SECOND);
231 try
232 {
233 SimpleAnimator animator = buildAnimator(Time.ZERO, warmupDuration, runDuration,
234 new ArrayList<Property<?>>(), null, true);
235 animator.setSpeedFactor(Double.MAX_VALUE, true);
236 }
237 catch (SimRuntimeException | NamingException | OTSSimulationException | PropertyException exception1)
238 {
239 exception1.printStackTrace();
240
241 error = "XML ERROR";
242 }
243 break;
244
245 case SIMULATEUNTIL:
246 System.out.println("Received SIMULATEUNTIL message");
247 if (null == this.model)
248 {
249 System.err.println("No model active");
250 socket.close();
251 break;
252 }
253 AimsunControlProtoBuf.SimulateUntil simulateUntil = message.getSimulateUntil();
254 Time stopTime = new Time(simulateUntil.getTime(), TimeUnit.BASE_SECOND);
255 System.out.println("Simulate until " + stopTime);
256 DEVSSimulator<Time, ?, ?> simulator = (DEVSSimulator<Time, ?, ?>) this.model.getSimulator();
257 try
258 {
259 if (null != error)
260 {
261 throw new SimRuntimeException(error);
262 }
263 simulator.runUpTo(stopTime);
264 while (simulator.isRunning())
265 {
266 try
267 {
268 Thread.sleep(10);
269 }
270 catch (InterruptedException ie)
271 {
272 ie = null;
273 }
274 }
275 AimsunControlProtoBuf.GTUPositions.Builder builder =
276 AimsunControlProtoBuf.GTUPositions.newBuilder();
277 for (GTU gtu : this.model.getNetwork().getGTUs())
278 {
279 AimsunControlProtoBuf.GTUPositions.GTUPosition.Builder gpb =
280 AimsunControlProtoBuf.GTUPositions.GTUPosition.newBuilder();
281 gpb.setGtuId(gtu.getId());
282 DirectedPoint dp = gtu.getOperationalPlan().getLocation(stopTime);
283 gpb.setX(dp.x);
284 gpb.setY(dp.y);
285 gpb.setZ(dp.z);
286 gpb.setAngle(dp.getRotZ());
287 gpb.setLength(gtu.getLength().si);
288 gpb.setWidth(gtu.getWidth().si);
289 gpb.setGtuTypeId(Integer.parseInt(gtu.getGTUType().getId().split("\\.")[1]));
290 gpb.setSpeed(gtu.getSpeed().si);
291 builder.addGtuPos(gpb.build());
292 }
293 builder.setStatus("OK");
294 GTUPositions gtuPositions = builder.build();
295 AimsunControlProtoBuf.OTSMessage.Builder resultBuilder =
296 AimsunControlProtoBuf.OTSMessage.newBuilder();
297 resultBuilder.setGtuPositions(gtuPositions);
298 AimsunControlProtoBuf.OTSMessage result = resultBuilder.build();
299 transmitMessage(result, outputStream);
300
301
302
303
304
305
306
307
308 }
309 catch (SimRuntimeException | OperationalPlanException exception)
310 {
311 if (null == error)
312 {
313 error = "Error while handling SIMULATEUNTIL";
314 System.out.println(error);
315 exception.printStackTrace();
316 }
317
318 AimsunControlProtoBuf.GTUPositions.Builder builder =
319 AimsunControlProtoBuf.GTUPositions.newBuilder();
320 builder.setStatus("FAILED (" + error + ")");
321 AimsunControlProtoBuf.OTSMessage.Builder resultBuilder =
322 AimsunControlProtoBuf.OTSMessage.newBuilder();
323 resultBuilder.setGtuPositions(builder);
324 transmitMessage(resultBuilder.build(), outputStream);
325 }
326 break;
327
328 case GTUPOSITIONS:
329 System.out.println("Received GTUPOSITIONS message SHOULD NOT HAPPEN");
330 socket.close();
331 return;
332
333 case MSG_NOT_SET:
334 System.out.println("Received MSG_NOT_SET message SHOULD NOT HAPPEN");
335 socket.close();
336 return;
337
338 default:
339 System.out.println("Received unknown message SHOULD NOT HAPPEN");
340 socket.close();
341 break;
342 }
343 }
344 catch (IOException exception)
345 {
346 exception.printStackTrace();
347 break;
348 }
349 }
350 }
351
352
353
354
355
356
357
358 private void transmitMessage(final AimsunControlProtoBuf.OTSMessage message, final OutputStream outputStream)
359 throws IOException
360 {
361 int size = message.getSerializedSize();
362 System.out.print("Transmitting " + message.getGtuPositions().getGtuPosCount() + " GTU positions and status \""
363 + message.getGtuPositions().getStatus() + "\" encoded in " + size + " bytes ... ");
364 byte[] sizeBytes = new byte[4];
365 sizeBytes[0] = (byte) ((size >> 24) & 0xff);
366 sizeBytes[1] = (byte) ((size >> 16) & 0xff);
367 sizeBytes[2] = (byte) ((size >> 8) & 0xff);
368 sizeBytes[3] = (byte) (size & 0xff);
369 outputStream.write(sizeBytes);
370 byte[] buffer = new byte[size];
371 buffer = message.toByteArray();
372 outputStream.write(buffer);
373 System.out.println("Done");
374 }
375
376
377
378
379
380
381
382 static void fillBuffer(final InputStream in, final byte[] buffer) throws IOException
383 {
384 System.out.print("Need to read " + buffer.length + " bytes ... ");
385 int offset = 0;
386 while (true)
387 {
388 int bytesRead = in.read(buffer, offset, buffer.length - offset);
389 if (-1 == bytesRead)
390 {
391 break;
392 }
393 offset += bytesRead;
394 if (buffer.length == offset)
395 {
396 System.out.println("got all " + buffer.length + " requested bytes");
397 break;
398 }
399 if (buffer.length < offset)
400 {
401 System.out.println("Oops: Got more than " + buffer.length + " requested bytes");
402 break;
403 }
404 System.out.print("now got " + offset + " bytes; need to read " + (buffer.length - offset) + " more bytes ... ");
405 }
406 if (offset != buffer.length)
407 {
408 throw new IOException("Got only " + offset + " of expected " + buffer.length + " bytes");
409 }
410 }
411
412
413 @Override
414 public final String shortName()
415 {
416 return "AimsunControlledOTS";
417 }
418
419
420 @Override
421 public final String description()
422 {
423 return "Aimsun controlled OTS engine";
424 }
425
426
427 @Override
428 protected OTSModelInterface makeModel() throws OTSSimulationException
429 {
430 this.model = new AimsunModel();
431 return this.model;
432 }
433
434
435
436
437 class AimsunModel extends EventProducer implements OTSModelInterface, EventListenerInterface
438 {
439
440
441 private static final long serialVersionUID = 20170419L;
442
443
444 private OTSNetwork network;
445
446
447 private SimulatorInterface<Time, Duration, SimTimeDoubleUnit> simulator;
448
449
450 @Override
451 public void constructModel(final SimulatorInterface<Time, Duration, SimTimeDoubleUnit> theSimulator)
452 throws SimRuntimeException
453 {
454 this.simulator = theSimulator;
455
456 XmlNetworkLaneParser nlp = new XmlNetworkLaneParser((DEVSSimulatorInterface.TimeDoubleUnit) theSimulator);
457 @SuppressWarnings("synthetic-access")
458 String xml = AimsunControl.this.networkXML;
459 try
460 {
461 this.network = nlp.build(new ByteArrayInputStream(xml.getBytes(StandardCharsets.UTF_8)), true);
462 }
463 catch (NetworkException | ParserConfigurationException | SAXException | IOException | NamingException | GTUException
464 | OTSGeometryException | ValueException | ParameterException exception)
465 {
466 exception.printStackTrace();
467 throw new SimRuntimeException(exception);
468 }
469 }
470
471
472 @Override
473 public SimulatorInterface<Time, Duration, SimTimeDoubleUnit> getSimulator()
474 {
475 return this.simulator;
476 }
477
478
479 @Override
480 public void notify(final EventInterface event) throws RemoteException
481 {
482
483 }
484
485
486 @Override
487 public OTSNetwork getNetwork()
488 {
489 return this.network;
490 }
491
492 }
493
494
495 @Override
496 protected final void addAnimationToggles()
497 {
498 AnimationToggles.setTextAnimationTogglesFull(this);
499 this.toggleAnimationClass(OTSLink.class);
500 this.toggleAnimationClass(OTSNode.class);
501 showAnimationClass(SpeedSign.class);
502 }
503
504 }