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