1 package org.opentrafficsim.imb.transceiver.urbanstrategy;
2
3 import java.rmi.RemoteException;
4 import java.util.ArrayList;
5 import java.util.List;
6
7 import org.opentrafficsim.core.dsol.OTSDEVSSimulatorInterface;
8 import org.opentrafficsim.core.dsol.OTSSimTimeDouble;
9 import org.opentrafficsim.core.geometry.OTSGeometryException;
10 import org.opentrafficsim.core.geometry.OTSPoint3D;
11 import org.opentrafficsim.core.network.Link;
12 import org.opentrafficsim.core.network.Network;
13 import org.opentrafficsim.core.network.OTSNetwork;
14 import org.opentrafficsim.imb.IMBException;
15 import org.opentrafficsim.imb.connector.Connector;
16 import org.opentrafficsim.imb.connector.Connector.IMBEventType;
17 import org.opentrafficsim.imb.transceiver.AbstractTransceiver;
18 import org.opentrafficsim.road.gtu.lane.LaneBasedGTU;
19 import org.opentrafficsim.road.network.lane.CrossSectionLink;
20 import org.opentrafficsim.road.network.lane.Lane;
21
22 import nl.tudelft.simulation.event.Event;
23 import nl.tudelft.simulation.event.EventInterface;
24 import nl.tudelft.simulation.event.EventType;
25 import nl.tudelft.simulation.event.TimedEvent;
26
27 /**
28 * OTS publishes events about the lanes to IMB, e.g. to know about the number of vehicles on a particular lane.<br>
29 * At the start of the OTS simulation, or when a Lane is added later, a NEW message is sent to IMB to identify the lane, the
30 * coordinates of its center line, and the start and end nodes of the link. The CHANGE message is posted whenever a vehicle
31 * enters or leaves a link. When a Link is removed from the network, a DELETE event is posted. The Link NEW messages are posted
32 * after the Network NEW, Node NEW and Link NEW messages are posted.
33 * <p>
34 * <style>table,th,td {border:1px solid grey; border-style:solid; text-align:left; border-collapse: collapse;}</style>
35 * <h2>NEW</h2>
36 * <table summary="" style="width:800px;">
37 * <thead>
38 * <tr>
39 * <th style="width:25%;">Variable</th>
40 * <th style="width:15%;">Type</th>
41 * <th style="width:60%;">Comments</th>
42 * </tr>
43 * </thead><tbody>
44 * <tr>
45 * <td>timestamp</td>
46 * <td>double</td>
47 * <td>time of the event, in simulation time seconds</td>
48 * </tr>
49 * <tr>
50 * <td>networkId</td>
51 * <td>String</td>
52 * <td>Id of the Network where the Link resides</td>
53 * </tr>
54 * <tr>
55 * <td>linkId</td>
56 * <td>String</td>
57 * <td>id of the Link; unique within the Network</td>
58 * </tr>
59 * <tr>
60 * <td>laneId</td>
61 * <td>String</td>
62 * <td>id of the Lane, unique within the Link</td>
63 * </tr>
64 * <tr>
65 * <td>laneNumber</td>
66 * <td>int</td>
67 * <td>identification of the lane number within the Link.</td>
68 * </tr>
69 * <tr>
70 * <td>centerLine.numberOfPoints</td>
71 * <td>int</td>
72 * <td>number of points for the center line of the Lane. The number of doubles that follow is 3 times this number</td>
73 * </tr>
74 * <tr>
75 * <td>centerLine.x1</td>
76 * <td>double</td>
77 * <td>x-coordinate of the first point of the center line</td>
78 * </tr>
79 * <tr>
80 * <td>centerLine.y1</td>
81 * <td>double</td>
82 * <td>y-coordinate of the first point of the center line</td>
83 * </tr>
84 * <tr>
85 * <td>centerLine.z1</td>
86 * <td>double</td>
87 * <td>z-coordinate of the first point of the center line</td>
88 * </tr>
89 * <tr>
90 * <td>...</td>
91 * <td></td>
92 * <td></td>
93 * </tr>
94 * <tr>
95 * <td>centerLine.xn</td>
96 * <td>double</td>
97 * <td>x-coordinate of the last point of the center line</td>
98 * </tr>
99 * <tr>
100 * <td>centerLine.yn</td>
101 * <td>double</td>
102 * <td>y-coordinate of the last point of the center line</td>
103 * </tr>
104 * <tr>
105 * <td>centerLine.zn</td>
106 * <td>double</td>
107 * <td>z-coordinate of the last point of the center line</td>
108 * </tr>
109 * </tbody>
110 * </table>
111 * </p>
112 * <p>
113 * <h2>CHANGE</h2>
114 * <table summary="" style="width:800px;">
115 * <thead>
116 * <tr>
117 * <th style="width:25%;">Variable</th>
118 * <th style="width:15%;">Type</th>
119 * <th style="width:60%;">Comments</th>
120 * </tr>
121 * </thead><tbody>
122 * <tr>
123 * <td>timestamp</td>
124 * <td>double</td>
125 * <td>time of the event, in simulation time seconds</td>
126 * </tr>
127 * <tr>
128 * <td>networkId</td>
129 * <td>String</td>
130 * <td>Id of the Network where the Lane resides</td>
131 * </tr>
132 * <tr>
133 * <td>linkId</td>
134 * <td>String</td>
135 * <td>id of the Link to which the lane belongs</td>
136 * </tr>
137 * <tr>
138 * <td>laneId</td>
139 * <td>String</td>
140 * <td>id of the Lane, unique within the Link</td>
141 * </tr>
142 * <tr>
143 * <td>isVehicleAdded</td>
144 * <td>boolean</td>
145 * <td>true if vehicle added, false if vehicle removed</td>
146 * </tr>
147 * <tr>
148 * <td>gtuId</td>
149 * <td>String</td>
150 * <td>id of the gtu that was added or removed from the Lane</td>
151 * </tr>
152 * <tr>
153 * <td>countAfterEvent</td>
154 * <td>int</td>
155 * <td>the number of vehicles on the Lane after the event</td>
156 * </tr>
157 * </tbody>
158 * </table>
159 * </p>
160 * <p>
161 * <h2>DELETE</h2>
162 * <table summary="" style="width:800px;">
163 * <thead>
164 * <tr>
165 * <th style="width:25%;">Variable</th>
166 * <th style="width:15%;">Type</th>
167 * <th style="width:60%;">Comments</th>
168 * </tr>
169 * </thead><tbody>
170 * <tr>
171 * <td>timestamp</td>
172 * <td>double</td>
173 * <td>time of the event, in simulation time seconds</td>
174 * </tr>
175 * <tr>
176 * <td>networkId</td>
177 * <td>String</td>
178 * <td>Id of the Network where the Link resides</td>
179 * </tr>
180 * <tr>
181 * <td>linkId</td>
182 * <td>String</td>
183 * <td>id of the Link</td>
184 * </tr>
185 * <tr>
186 * <td>laneId</td>
187 * <td>String</td>
188 * <td>id of the Lane that is removed from the Network</td>
189 * </tr>
190 * </tbody>
191 * </table>
192 * </p>
193 * <p>
194 * Copyright (c) 2013-2016 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
195 * BSD-style license. See <a href="http://opentrafficsim.org/docs/current/license.html">OpenTrafficSim License</a>.
196 * </p>
197 * @version $Revision$, $LastChangedDate$, by $Author$, initial version Sep 13, 2016 <br>
198 * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
199 * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
200 * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
201 */
202 public class LaneGTUTransceiver extends AbstractTransceiver
203 {
204 /** */
205 private static final long serialVersionUID = 20160918L;
206
207 /** the OTS network on which Links / Lanes are registered. */
208 private final OTSNetwork network;
209
210 /**
211 * Construct a new LaneGTUTransceiver.
212 * @param connector Connector; the IMB connector through which this transceiver communicates
213 * @param simulator OTSDEVSSimulatorInterface; the simulator to schedule the incoming notifications on
214 * @param network OTSNetwork; the OTS network on which Links for Lanes are registered
215 * @throws IMBException when the registration of one of the channels fails
216 * @throws NullPointerException in case one of the arguments is null.
217 */
218 public LaneGTUTransceiver(final Connector connector, final OTSDEVSSimulatorInterface simulator, final OTSNetwork network)
219 throws IMBException
220 {
221 super("Lane_GTU", connector, simulator);
222 this.network = network;
223
224 // listen on network changes and register the listener to all the Links
225 addListeners();
226 }
227
228 /**
229 * Ensure that we get notified about newly created and destroyed Links instrument all currently existing Links.
230 * @throws IMBException in case notification of existing Lanes fails
231 */
232 private void addListeners() throws IMBException
233 {
234 // Subscribe to all future link creation and removal events.
235 this.network.addListener(this, Network.LINK_ADD_EVENT);
236 this.network.addListener(this, Network.LINK_REMOVE_EVENT);
237
238 // For already existing links, post ourselves a LINK_ADD_EVENT
239 for (Link link : this.network.getLinkMap().values())
240 {
241 try
242 {
243 this.notify(new TimedEvent<OTSSimTimeDouble>(Network.LINK_ADD_EVENT, this.network, link.getId(),
244 getSimulator().getSimulatorTime()));
245 }
246 catch (RemoteException exception)
247 {
248 throw new IMBException(exception);
249 }
250 }
251 }
252
253 /** {@inheritDoc} */
254 @Override
255 public void notify(final EventInterface event) throws RemoteException
256 {
257 EventType type = event.getType();
258 if (type.equals(Network.LINK_ADD_EVENT))
259 {
260 Link link = this.network.getLink((String) event.getContent());
261 if (!(link instanceof CrossSectionLink))
262 {
263 System.err.println("LaneGTUTransceiver.notify(LINK_ADD) - Don't know how to handle a non-CrossSectionLink");
264 return;
265 }
266 CrossSectionLink csl = (CrossSectionLink) link;
267
268 csl.addListener(this, CrossSectionLink.LANE_ADD_EVENT);
269 csl.addListener(this, CrossSectionLink.LANE_REMOVE_EVENT);
270
271 // For already existing lanes, post ourselves a LANE_ADD_EVENT
272 for (Lane lane : csl.getLanes())
273 {
274 try
275 {
276 this.notify(new Event(CrossSectionLink.LANE_ADD_EVENT, csl, new Object[] { link.getNetwork().getId(),
277 link.getId(), lane.getId(), lane, csl.getLanes().indexOf(lane) }));
278 }
279 catch (RemoteException exception)
280 {
281 System.err.println("LaneGTUTransceiver.notify(LINK_ADD) - RemoteException: " + exception.getMessage());
282 return;
283 }
284 }
285 }
286
287 else if (type.equals(CrossSectionLink.LANE_ADD_EVENT))
288 {
289 Object[] content = (Object[]) event.getContent();
290 Lane lane = (Lane) content[3];
291
292 // post the Lane_GTU message to register the lane on the IMB bus
293 try
294 {
295 getConnector().postIMBMessage("Lane_GTU", IMBEventType.NEW, transformNew(event));
296 }
297 catch (IMBException exception)
298 {
299 System.err.println("LaneGTUTransceiver.notify(LANE_ADD) - IMBException: " + exception.getMessage());
300 return;
301 }
302
303 lane.addListener(this, Lane.GTU_ADD_EVENT);
304 lane.addListener(this, Lane.GTU_REMOVE_EVENT);
305
306 // Post ourselves a GTU_ADD_EVENT for every GTU currently on the lane
307 int gtuCount = lane.getGtuList().size();
308 for (LaneBasedGTU gtu : lane.getGtuList())
309 {
310 try
311 {
312 this.notify(new TimedEvent<OTSSimTimeDouble>(Lane.GTU_ADD_EVENT, lane,
313 new Object[] { gtu.getId(), gtu, gtuCount }, getSimulator().getSimulatorTime()));
314 }
315 catch (RemoteException exception)
316 {
317 System.err.println("LaneGTUTransceiver.notify(LANE_ADD) - RemoteException: " + exception.getMessage());
318 return;
319 }
320 }
321 }
322
323 else if (type.equals(Network.LINK_REMOVE_EVENT))
324 {
325 Link link = this.network.getLink((String) event.getContent());
326 if (!(link instanceof CrossSectionLink))
327 {
328 System.err.println("LaneGTUTransceiver.notify(LINK_REMOVE) - Don't know how to handle a non-CrossSectionLink");
329 return;
330 }
331 CrossSectionLink csl = (CrossSectionLink) link;
332
333 csl.removeListener(this, CrossSectionLink.LANE_ADD_EVENT);
334 csl.removeListener(this, CrossSectionLink.LANE_REMOVE_EVENT);
335
336 // For already existing lanes, post ourselves a LANE_REMOVE_EVENT
337 for (Lane lane : csl.getLanes())
338 {
339 try
340 {
341 this.notify(new Event(CrossSectionLink.LANE_REMOVE_EVENT, csl,
342 new Object[] { link.getNetwork().getId(), link.getId(), lane.getId() }));
343 }
344 catch (RemoteException exception)
345 {
346 System.err.println("LaneGTUTransceiver.notify(LINK_REMOVE) - RemoteException: " + exception.getMessage());
347 return;
348 }
349 }
350 }
351
352 else if (type.equals(CrossSectionLink.LANE_REMOVE_EVENT))
353 {
354 Object[] content = (Object[]) event.getContent();
355 String laneId = (String) content[2];
356 CrossSectionLink csl = (CrossSectionLink) event.getSource();
357 Lane lane = (Lane) csl.getCrossSectionElement(laneId);
358
359 lane.removeListener(this, Lane.GTU_ADD_EVENT);
360 lane.removeListener(this, Lane.GTU_REMOVE_EVENT);
361
362 // post the Node message to de-register the lane from the IMB bus
363 try
364 {
365 getConnector().postIMBMessage("Lane_GTU", IMBEventType.DELETE, transformDelete(event));
366 }
367 catch (IMBException exception)
368 {
369 System.err.println("LaneGTUTransceiver.notify(LANE_REMOVE) - IMBException: " + exception.getMessage());
370 return;
371 }
372 }
373
374 else if (type.equals(Lane.GTU_ADD_EVENT) || type.equals(Lane.GTU_REMOVE_EVENT))
375 {
376 try
377 {
378 getConnector().postIMBMessage("Lane_GTU", IMBEventType.CHANGE, transformChange(event));
379 }
380 catch (IMBException exception)
381 {
382 System.err.println("LaneGTUTransceiver.notify CHANGE - IMBException: " + exception.getMessage());
383 return;
384 }
385 }
386
387 else
388 {
389 System.err.println("LaneGTUTransceiver.notify - Unhandled event: " + event);
390 }
391 }
392
393 /**
394 * Transform the addition of a Lane to the network to a corresponding IMB message.
395 * @param event the event to transform to a NEW message.
396 * @return the NEW payload
397 */
398 public Object[] transformNew(final EventInterface event)
399 {
400 if (CrossSectionLink.LANE_ADD_EVENT.equals(event.getType()))
401 {
402 Object[] content = (Object[]) event.getContent();
403 Lane lane = (Lane) content[3];
404 int laneNumber = (Integer) content[4];
405 double timestamp = getSimulator().getSimulatorTime().getTime().si;
406 List<Object> resultList = new ArrayList<>();
407 resultList.add(timestamp);
408 resultList.add(this.network.getId());
409 resultList.add(lane.getParentLink().getId());
410 resultList.add(lane.getId());
411 resultList.add(laneNumber);
412 resultList.add(lane.getCenterLine().size());
413 for (int i = 0; i < lane.getCenterLine().size(); i++)
414 {
415 try
416 {
417 OTSPoint3D p = lane.getCenterLine().get(i);
418 resultList.add(p.x);
419 resultList.add(p.y);
420 resultList.add(p.z);
421 }
422 catch (OTSGeometryException exception)
423 {
424 exception.printStackTrace();
425 resultList.add(0.0d);
426 resultList.add(0.0d);
427 resultList.add(0.0d);
428 }
429 }
430 return resultList.toArray();
431 }
432 System.err.println("LaneGTUTransceiver.transformNew: Don't know how to transform event " + event);
433 return new Object[] {};
434 }
435
436 /**
437 * Transform the GTU added or removed event content to a corresponding IMB message.
438 * @param event the event to transform to a CHANGE message.
439 * @return the CHANGE payload
440 */
441 public Object[] transformChange(final EventInterface event)
442 {
443 Object[] gtuInfo = (Object[]) event.getContent();
444 String gtuId = (String) gtuInfo[0];
445 int countAfterEvent = (Integer) gtuInfo[2];
446 Lane lane = (Lane) event.getSource();
447 double timestamp = getSimulator().getSimulatorTime().getTime().si;
448 if (Lane.GTU_ADD_EVENT.equals(event.getType()))
449 {
450 return new Object[] { timestamp, this.network.getId(), lane.getParentLink().getId(), lane.getId(), true, gtuId,
451 countAfterEvent };
452 }
453 else if (Lane.GTU_REMOVE_EVENT.equals(event.getType()))
454 {
455 return new Object[] { timestamp, this.network.getId(), lane.getParentLink().getId(), lane.getId(), false, gtuId,
456 countAfterEvent };
457 }
458 System.err.println("LaneGTUTransceiver.transformChange: Don't know how to transform event " + event);
459 return new Object[] {};
460 }
461
462 /**
463 * Transform the removal of a Lane from the network to a corresponding IMB message.
464 * @param event the event to transform to a DELETE message.
465 * @return the DELETE payload
466 */
467 public Object[] transformDelete(final EventInterface event)
468 {
469 if (CrossSectionLink.LANE_REMOVE_EVENT.equals(event.getType()))
470 {
471 Object[] content = (Object[]) event.getContent();
472 String networkId = (String) content[0];
473 String linkId = (String) content[1];
474 String laneId = (String) content[2];
475 double timestamp = getSimulator().getSimulatorTime().getTime().si;
476 return new Object[] { timestamp, networkId, linkId, laneId };
477 }
478 System.err.println("LaneGTUTransceiver.transformDelete: Don't know how to transform event " + event);
479 return new Object[] {};
480 }
481
482 }