1 package org.opentrafficsim.imb.transceiver.urbanstrategy;
2
3 import java.rmi.RemoteException;
4
5 import org.opentrafficsim.core.dsol.OTSDEVSSimulatorInterface;
6 import org.opentrafficsim.core.dsol.OTSSimTimeDouble;
7 import org.opentrafficsim.core.gtu.RelativePosition;
8 import org.opentrafficsim.core.network.Link;
9 import org.opentrafficsim.core.network.Network;
10 import org.opentrafficsim.core.network.OTSNetwork;
11 import org.opentrafficsim.imb.IMBException;
12 import org.opentrafficsim.imb.connector.Connector;
13 import org.opentrafficsim.imb.connector.Connector.IMBEventType;
14 import org.opentrafficsim.imb.transceiver.AbstractTransceiver;
15 import org.opentrafficsim.road.gtu.lane.LaneBasedGTU;
16 import org.opentrafficsim.road.network.lane.CrossSectionLink;
17 import org.opentrafficsim.road.network.lane.Lane;
18 import org.opentrafficsim.road.network.lane.object.sensor.Sensor;
19
20 import nl.tudelft.simulation.event.Event;
21 import nl.tudelft.simulation.event.EventInterface;
22 import nl.tudelft.simulation.event.EventType;
23 import nl.tudelft.simulation.event.TimedEvent;
24 import nl.tudelft.simulation.language.d3.DirectedPoint;
25
26 /**
27 * OTS publishes events about sensor triggers to IMB, e.g. to show that a vehicle has triggered a sensor for a traffic
28 * light.<br>
29 * At the start of the OTS simulation, or when a sensor is added later, a NEW message is sent to IMB to identify the sensor, the
30 * lane on which it resides, and the position on the lane. The CHANGE message is posted whenever a vehicle triggers the sensor.
31 * When a Sensor is removed from the network, a DELETE event is posted. The Sensor NEW messages are posted after the Network
32 * NEW, Node NEW, Link NEW, and Lane 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>sensorId</td>
66 * <td>String</td>
67 * <td>id of the Sensor, unique within the Lane</td>
68 * </tr>
69 * <tr>
70 * <td>longitudinalPosition</td>
71 * <td>double</td>
72 * <td>position on the center line of the lane, in meters</td>
73 * </tr>
74 * <tr>
75 * <td>length</td>
76 * <td>double</td>
77 * <td>length of the sensor, in meters</td>
78 * </tr>
79 * <tr>
80 * <td>sensorPosition.x</td>
81 * <td>double</td>
82 * <td>x-coordinate of the sensor position in (gis) coordinates</td>
83 * </tr>
84 * <tr>
85 * <td>sensorPosition.y</td>
86 * <td>double</td>
87 * <td>y-coordinate of the sensor position in (gis) coordinates</td>
88 * </tr>
89 * <tr>
90 * <td>sensorPosition.z</td>
91 * <td>double</td>
92 * <td>z-coordinate of the sensor position in (gis) coordinates</td>
93 * </tr>
94 * <tr>
95 * <td>triggerPosition</td>
96 * <td>String</td>
97 * <td>Relative position of the vehicle that triggers this sensor. One of {FRONT, REAR, REFERENCE, CONTOUR, CENTER, DRIVER}</td>
98 * </tr>
99 * </tbody>
100 * </table>
101 * </p>
102 * <p>
103 * <h2>CHANGE</h2>
104 * <table summary="" style="width:800px;">
105 * <thead>
106 * <tr>
107 * <th style="width:25%;">Variable</th>
108 * <th style="width:15%;">Type</th>
109 * <th style="width:60%;">Comments</th>
110 * </tr>
111 * </thead><tbody>
112 * <tr>
113 * <td>timestamp</td>
114 * <td>double</td>
115 * <td>time of the event, in simulation time seconds</td>
116 * </tr>
117 * <tr>
118 * <td>networkId</td>
119 * <td>String</td>
120 * <td>Id of the Network where the Lane resides</td>
121 * </tr>
122 * <tr>
123 * <td>linkId</td>
124 * <td>String</td>
125 * <td>id of the Link to which the lane belongs</td>
126 * </tr>
127 * <tr>
128 * <td>laneId</td>
129 * <td>String</td>
130 * <td>id of the Lane, unique within the Link</td>
131 * </tr>
132 * <tr>
133 * <td>sensorId</td>
134 * <td>String</td>
135 * <td>id of the Sensor, unique within the Lane</td>
136 * </tr>
137 * <tr>
138 * <td>gtuId</td>
139 * <td>String</td>
140 * <td>id of the vehicle that triggers the sensor</td>
141 * </tr>
142 * <tr>
143 * <td>speed</td>
144 * <td>double</td>
145 * <td>speed of the vehicle that triggers the sensor, in m/s</td>
146 * </tr>
147 * <tr>
148 * <td>triggerPosition</td>
149 * <td>String</td>
150 * <td>Relative position of the vehicle that triggers this sensor. One of {FRONT, REAR, REFERENCE, CONTOUR, CENTER, DRIVER}</td>
151 * </tr>
152 * </tbody>
153 * </table>
154 * </p>
155 * <p>
156 * <h2>DELETE</h2>
157 * <table summary="" style="width:800px;">
158 * <thead>
159 * <tr>
160 * <th style="width:25%;">Variable</th>
161 * <th style="width:15%;">Type</th>
162 * <th style="width:60%;">Comments</th>
163 * </tr>
164 * </thead><tbody>
165 * <tr>
166 * <td>timestamp</td>
167 * <td>double</td>
168 * <td>time of the event, in simulation time seconds</td>
169 * </tr>
170 * <tr>
171 * <td>networkId</td>
172 * <td>String</td>
173 * <td>Id of the Network where the Link resides</td>
174 * </tr>
175 * <tr>
176 * <td>linkId</td>
177 * <td>String</td>
178 * <td>id of the Link in the Network</td>
179 * </tr>
180 * <tr>
181 * <td>laneId</td>
182 * <td>String</td>
183 * <td>id of the Lane in the Link</td>
184 * </tr>
185 * <tr>
186 * <td>sensorId</td>
187 * <td>String</td>
188 * <td>id of the Sensor that is removed from the Lane</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 SensorGTUTransceiver 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 SensorGTUTransceiver.
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 SensorGTUTransceiver(final Connector connector, final OTSDEVSSimulatorInterface simulator, final OTSNetwork network)
219 throws IMBException
220 {
221 super("Sensor_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("SensorGTUTransceiver.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("SensorGTUTransceiver.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 lane.addListener(this, Lane.SENSOR_ADD_EVENT);
293 lane.addListener(this, Lane.SENSOR_REMOVE_EVENT);
294
295 // Post ourselves a SENSOR_ADD_EVENT for every Sensor currently on the lane
296 for (Sensor sensor : lane.getSensors())
297 {
298 try
299 {
300 this.notify(new TimedEvent<OTSSimTimeDouble>(Lane.SENSOR_ADD_EVENT, lane,
301 new Object[] { sensor.getId(), sensor }, getSimulator().getSimulatorTime()));
302 }
303 catch (RemoteException exception)
304 {
305 System.err.println("SensorGTUTransceiver.notify(LANE_ADD) - RemoteException: " + exception.getMessage());
306 return;
307 }
308 }
309 }
310
311 else if (type.equals(Lane.SENSOR_ADD_EVENT))
312 {
313 Object[] content = (Object[]) event.getContent();
314 Sensor sensor = (Sensor) content[1];
315 sensor.addListener(this, Sensor.SENSOR_TRIGGER_EVENT);
316
317 try
318 {
319 getConnector().postIMBMessage("Sensor_GTU", IMBEventType.NEW, transformNew(event));
320 }
321 catch (IMBException exception)
322 {
323 System.err.println("SensorGTUTransceiver.notify(SENSOR_ADD) - IMBException: " + exception.getMessage());
324 return;
325 }
326 }
327
328 else if (type.equals(Sensor.SENSOR_TRIGGER_EVENT))
329 {
330 try
331 {
332 getConnector().postIMBMessage("Sensor_GTU", IMBEventType.CHANGE, transformChange(event));
333 }
334 catch (IMBException exception)
335 {
336 System.err.println("SensorGTUTransceiver.notify(SENSOR_TRIGGER) - IMBException: " + exception.getMessage());
337 return;
338 }
339 }
340
341 else if (type.equals(Network.LINK_REMOVE_EVENT))
342 {
343 Link link = this.network.getLink((String) event.getContent());
344 if (!(link instanceof CrossSectionLink))
345 {
346 System.err
347 .println("SensorGTUTransceiver.notify(LINK_REMOVE) - Don't know how to handle a non-CrossSectionLink");
348 return;
349 }
350 CrossSectionLink csl = (CrossSectionLink) link;
351
352 csl.removeListener(this, CrossSectionLink.LANE_ADD_EVENT);
353 csl.removeListener(this, CrossSectionLink.LANE_REMOVE_EVENT);
354
355 // For already existing lanes, post ourselves a LANE_REMOVE_EVENT
356 for (Lane lane : csl.getLanes())
357 {
358 try
359 {
360 this.notify(new Event(CrossSectionLink.LANE_REMOVE_EVENT, csl,
361 new Object[] { link.getNetwork().getId(), link.getId(), lane.getId() }));
362 }
363 catch (RemoteException exception)
364 {
365 System.err.println("SensorGTUTransceiver.notify(LINK_REMOVE) - RemoteException: " + exception.getMessage());
366 return;
367 }
368 }
369 }
370
371 else if (type.equals(CrossSectionLink.LANE_REMOVE_EVENT))
372 {
373 Object[] content = (Object[]) event.getContent();
374 String laneId = (String) content[2];
375 CrossSectionLink csl = (CrossSectionLink) event.getSource();
376 Lane lane = (Lane) csl.getCrossSectionElement(laneId);
377
378 lane.removeListener(this, Lane.SENSOR_ADD_EVENT);
379 lane.removeListener(this, Lane.SENSOR_REMOVE_EVENT);
380
381 // Post ourselves a SENSOR_REMOVE_EVENT for every Sensor currently on the lane
382 for (Sensor sensor : lane.getSensors())
383 {
384 try
385 {
386 this.notify(new TimedEvent<OTSSimTimeDouble>(Lane.SENSOR_REMOVE_EVENT, lane,
387 new Object[] { sensor.getId(), sensor }, getSimulator().getSimulatorTime()));
388 }
389 catch (RemoteException exception)
390 {
391 System.err.println("SensorGTUTransceiver.notify(LANE_REMOVE) - RemoteException: " + exception.getMessage());
392 return;
393 }
394 }
395
396 // post the Node message to de-register the lane from the IMB bus
397 try
398 {
399 getConnector().postIMBMessage("Sensor_GTU", IMBEventType.DELETE, transformDelete(event));
400 }
401 catch (IMBException exception)
402 {
403 System.err.println("SensorGTUTransceiver.notify(LANE_REMOVE) - IMBException: " + exception.getMessage());
404 return;
405 }
406 }
407
408 else if (type.equals(Lane.SENSOR_REMOVE_EVENT))
409 {
410 Object[] content = (Object[]) event.getContent();
411 Sensor sensor = (Sensor) content[1];
412 sensor.removeListener(this, Sensor.SENSOR_TRIGGER_EVENT);
413
414 try
415 {
416 getConnector().postIMBMessage("Sensor_GTU", IMBEventType.DELETE, transformDelete(event));
417 }
418 catch (IMBException exception)
419 {
420 System.err.println("SensorGTUTransceiver.notify(SENSOR_REMOVE) - IMBException: " + exception.getMessage());
421 return;
422 }
423 }
424
425 else
426 {
427 System.err.println("SensorGTUTransceiver.notify - Unhandled event: " + event);
428 }
429 }
430
431 /**
432 * Transform the addition of a Sensor to a Lane to a corresponding IMB message.
433 * @param event the event to transform to a NEW message.
434 * @return the NEW payload
435 */
436 private Object[] transformNew(final EventInterface event)
437 {
438 if (Lane.SENSOR_ADD_EVENT.equals(event.getType()))
439 {
440 // Object[] {String sensorId, Sensor sensor}
441 Object[] content = (Object[]) event.getContent();
442 String sensorId = (String) content[0];
443 Sensor sensor = (Sensor) content[1];
444 Lane lane = sensor.getLane();
445 double longitudinalPosition = sensor.getLongitudinalPosition().si;
446 double length = 0.0; // sensor has zero length right now
447 DirectedPoint pos = sensor.getLocation();
448 String triggerPosition = sensor.getPositionType().toString();
449 double timestamp = getSimulator().getSimulatorTime().getTime().si;
450 return new Object[] { timestamp, this.network.getId(), lane.getParentLink().getId(), lane.getId(), sensorId,
451 longitudinalPosition, length, pos.x, pos.y, pos.z, triggerPosition };
452 }
453 System.err.println("SensorGTUTransceiver.transformNew: Don't know how to transform event " + event);
454 return new Object[] {};
455 }
456
457 /**
458 * Transform the Sensor Triggered event content to a corresponding IMB message.
459 * @param event the event to transform to a CHANGE message.
460 * @return the CHANGE payload
461 */
462 private Object[] transformChange(final EventInterface event)
463 {
464 if (Sensor.SENSOR_TRIGGER_EVENT.equals(event.getType()))
465 {
466 // Object[] {String sensorId, Sensor sensor, LaneBasedGTU gtu, RelativePosition.TYPE relativePosition}
467 Object[] content = (Object[]) event.getContent();
468 String sensorId = (String) content[0];
469 Sensor sensor = (Sensor) content[1];
470 Lane lane = sensor.getLane();
471 LaneBasedGTU gtu = (LaneBasedGTU) content[2];
472 String gtuId = gtu.getId();
473 double gtuSpeed = gtu.getSpeed().si;
474 String triggerPosition = ((RelativePosition.TYPE) content[3]).toString();
475 double timestamp = getSimulator().getSimulatorTime().getTime().si;
476 return new Object[] { timestamp, this.network.getId(), lane.getParentLink().getId(), lane.getId(), sensorId, gtuId,
477 gtuSpeed, triggerPosition };
478 }
479 System.err.println("SensorGTUTransceiver.transformChange: Don't know how to transform event " + event);
480 return new Object[] {};
481 }
482
483 /**
484 * Transform the removal of a Sensor from a Lane to a corresponding IMB message.
485 * @param event the event to transform to a DELETE message.
486 * @return the DELETE payload
487 */
488 private Object[] transformDelete(final EventInterface event)
489 {
490 if (Lane.SENSOR_REMOVE_EVENT.equals(event.getType()))
491 {
492 // Object[] {String sensorId, Sensor sensor}
493 Object[] content = (Object[]) event.getContent();
494 String sensorId = (String) content[0];
495 Sensor sensor = (Sensor) content[1];
496 Lane lane = sensor.getLane();
497 double timestamp = getSimulator().getSimulatorTime().getTime().si;
498 return new Object[] { timestamp, this.network.getId(), lane.getParentLink().getId(), lane.getId(), sensorId };
499 }
500 System.err.println("SensorGTUTransceiver.transformDelete: Don't know how to transform event " + event);
501 return new Object[] {};
502 }
503
504 }