1 package org.opentrafficsim.sim0mq.publisher;
2
3 import java.rmi.RemoteException;
4 import java.util.LinkedHashMap;
5 import java.util.List;
6 import java.util.Map;
7
8 import org.djunits.Throw;
9 import org.djutils.event.EventProducerInterface;
10 import org.djutils.metadata.MetaData;
11 import org.djutils.metadata.ObjectDescriptor;
12 import org.djutils.serialization.SerializationException;
13 import org.opentrafficsim.core.gtu.GTU;
14 import org.opentrafficsim.core.network.Link;
15 import org.opentrafficsim.core.network.Network;
16 import org.opentrafficsim.core.network.OTSNetwork;
17 import org.opentrafficsim.road.network.lane.CrossSectionLink;
18 import org.sim0mq.Sim0MQException;
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48 public class Publisher extends AbstractTransceiver
49 {
50
51 @SuppressWarnings("checkstyle:visibilitymodifier")
52 final Map<String, SubscriptionHandler> subscriptionHandlerMap = new LinkedHashMap<>();
53
54
55 private final Map<String, IncomingDataHandler> incomingDataHandlers = new LinkedHashMap<>();
56
57
58 @SuppressWarnings("checkstyle:visibilitymodifier")
59 final OTSNetwork network;
60
61
62
63
64
65
66 public Publisher(final OTSNetwork network) throws RemoteException
67 {
68 this(network, null, null);
69 }
70
71
72
73
74
75
76
77
78
79
80 public Publisher(final OTSNetwork network, final List<SubscriptionHandler> additionalSubscriptionHandlers,
81 final List<IncomingDataHandler> incomingDataHandlers) throws RemoteException
82 {
83 super("Publisher for " + Throw.whenNull(network, "Network may not be null").getId(),
84 new MetaData("Publisher for " + network.getId(), "Publisher",
85 new ObjectDescriptor[] {
86 new ObjectDescriptor("Name of subscription handler", "String", String.class) }),
87 new MetaData("Subscription handlers", "Subscription handlers", new ObjectDescriptor[] {
88 new ObjectDescriptor("Name of subscription handler", "String", String.class) }));
89 this.network = network;
90
91 if (incomingDataHandlers != null)
92 {
93 for (IncomingDataHandler incomingDataHandler : incomingDataHandlers)
94 {
95 this.incomingDataHandlers.put(incomingDataHandler.getKey(), incomingDataHandler);
96 }
97 }
98
99 GTUIdTransceiver gtuIdTransceiver = new GTUIdTransceiver(network);
100 GTUTransceiver gtuTransceiver = new GTUTransceiver(network, gtuIdTransceiver);
101 SubscriptionHandler gtuSubscriptionHandler =
102 new SubscriptionHandler("GTU move", gtuTransceiver, new LookupEventProducerInterface()
103 {
104 @Override
105 public EventProducerInterface lookup(final Object[] address, final ReturnWrapper returnWrapper)
106 throws Sim0MQException, SerializationException
107 {
108 String bad = AbstractTransceiver.verifyMetaData(getAddressMetaData(), address);
109 if (bad != null)
110 {
111 returnWrapper.nack(bad);
112 return null;
113 }
114 EventProducerInterface result = network.getGTU((String) address[0]);
115 if (null == result)
116 {
117 returnWrapper.nack("No GTU with id \"" + address[0] + "\" found");
118 }
119 return result;
120 }
121
122 private final MetaData metaData = new MetaData("GTU Id", "GTU Id",
123 new ObjectDescriptor[] { new ObjectDescriptor("GTU ID", "GTU Id", String.class) });
124
125 @Override
126 public MetaData getAddressMetaData()
127 {
128 return this.metaData;
129 }
130 }, null, null, GTU.MOVE_EVENT, null);
131 addSubscriptionHandler(gtuSubscriptionHandler);
132 addSubscriptionHandler(new SubscriptionHandler("GTUs in network", gtuIdTransceiver, new LookupEventProducerInterface()
133 {
134 @Override
135 public EventProducerInterface lookup(final Object[] address, final ReturnWrapper returnWrapper)
136 throws Sim0MQException, SerializationException
137 {
138 String bad = AbstractTransceiver.verifyMetaData(getAddressMetaData(), address);
139 if (bad != null)
140 {
141 returnWrapper.nack(bad);
142 return null;
143 }
144 return network;
145 }
146
147 @Override
148 public String toString()
149 {
150 return "Subscription handler for GTUs in network";
151 }
152
153 @Override
154 public MetaData getAddressMetaData()
155 {
156 return MetaData.EMPTY;
157 }
158 }, Network.GTU_ADD_EVENT, Network.GTU_REMOVE_EVENT, null, gtuSubscriptionHandler));
159 LinkIdTransceiver linkIdTransceiver = new LinkIdTransceiver(network);
160 LinkTransceiver linkTransceiver = new LinkTransceiver(network, linkIdTransceiver);
161 SubscriptionHandler linkSubscriptionHandler = new SubscriptionHandler("Link change", linkTransceiver, this.lookupLink,
162 Link.GTU_ADD_EVENT, Link.GTU_REMOVE_EVENT, null, null);
163 addSubscriptionHandler(linkSubscriptionHandler);
164 addSubscriptionHandler(new SubscriptionHandler("Links in network", linkIdTransceiver, new LookupEventProducerInterface()
165 {
166 @Override
167 public EventProducerInterface lookup(final Object[] address, final ReturnWrapper returnWrapper)
168 throws Sim0MQException, SerializationException
169 {
170 String bad = AbstractTransceiver.verifyMetaData(getAddressMetaData(), address);
171 if (bad != null)
172 {
173 returnWrapper.nack(bad);
174 return null;
175 }
176 return network;
177 }
178
179 @Override
180 public String toString()
181 {
182 return "Subscription handler for Links in network";
183 }
184
185 @Override
186 public MetaData getAddressMetaData()
187 {
188 return MetaData.EMPTY;
189 }
190 }, Network.LINK_ADD_EVENT, Network.LINK_REMOVE_EVENT, null, linkSubscriptionHandler));
191 NodeIdTransceiver nodeIdTransceiver = new NodeIdTransceiver(network);
192 NodeTransceiver nodeTransceiver = new NodeTransceiver(network, nodeIdTransceiver);
193
194
195 SubscriptionHandler nodeSubscriptionHandler =
196 new SubscriptionHandler("Node change", nodeTransceiver, new LookupEventProducerInterface()
197 {
198 @Override
199 public EventProducerInterface lookup(final Object[] address, final ReturnWrapper returnWrapper)
200 {
201 return null;
202 }
203
204 @Override
205 public String toString()
206 {
207 return "Subscription handler for Node change";
208 }
209
210 private final MetaData metaData = new MetaData("Node Id", "Node Id",
211 new ObjectDescriptor[] { new ObjectDescriptor("Node ID", "Node Id", String.class) });
212
213 @Override
214 public MetaData getAddressMetaData()
215 {
216 return this.metaData;
217 }
218 }, null, null, null, null);
219 addSubscriptionHandler(nodeSubscriptionHandler);
220 addSubscriptionHandler(new SubscriptionHandler("Nodes in network", nodeIdTransceiver, new LookupEventProducerInterface()
221 {
222 @Override
223 public EventProducerInterface lookup(final Object[] address, final ReturnWrapper returnWrapper)
224 throws Sim0MQException, SerializationException
225 {
226 String bad = AbstractTransceiver.verifyMetaData(getAddressMetaData(), address);
227 if (bad != null)
228 {
229 returnWrapper.nack(bad);
230 return null;
231 }
232 return network;
233 }
234
235 @Override
236 public String toString()
237 {
238 return "Subscription handler for Nodes in network";
239 }
240
241 @Override
242 public MetaData getAddressMetaData()
243 {
244 return MetaData.EMPTY;
245 }
246 }, Network.NODE_ADD_EVENT, Network.NODE_REMOVE_EVENT, null, nodeSubscriptionHandler));
247 SubscriptionHandler linkGTUIdSubscriptionHandler = new SubscriptionHandler("GTUs on Link",
248 new LinkGTUIdTransceiver(network), this.lookupLink, Link.GTU_ADD_EVENT, Link.GTU_REMOVE_EVENT, null, null);
249 addSubscriptionHandler(linkGTUIdSubscriptionHandler);
250 addSubscriptionHandler(new SubscriptionHandler("Cross section elements on Link",
251 new CrossSectionElementTransceiver(network), this.lookupLink, CrossSectionLink.LANE_ADD_EVENT,
252 CrossSectionLink.LANE_REMOVE_EVENT, null, linkGTUIdSubscriptionHandler));
253
254 SimulatorStateTransceiver stt = new SimulatorStateTransceiver(network.getSimulator());
255 SubscriptionHandler simulatorStateSubscriptionHandler = new SubscriptionHandler("Simulator running", stt,
256 stt.getLookupEventProducerInterface(), null, null, SimulatorStateTransceiver.SIMULATOR_STATE_CHANGED, null);
257 addSubscriptionHandler(simulatorStateSubscriptionHandler);
258
259 if (additionalSubscriptionHandlers != null)
260 {
261 for (SubscriptionHandler sh : additionalSubscriptionHandlers)
262 {
263 addSubscriptionHandler(sh);
264 }
265 }
266
267 addSubscriptionHandler(new SubscriptionHandler("", this, null, null, null, null, null));
268 }
269
270
271 private LookupEventProducerInterface lookupLink = new LookupEventProducerInterface()
272 {
273 @Override
274 public EventProducerInterface lookup(final Object[] address, final ReturnWrapper returnWrapper)
275 throws IndexOutOfBoundsException, Sim0MQException, SerializationException
276 {
277 Throw.whenNull(address, "LookupLink requires the name of a link");
278 Throw.when(address.length != 1 || !(address[1] instanceof String), IllegalArgumentException.class, "Bad address");
279 Link link = Publisher.this.network.getLink((String) address[0]);
280 if (null == link)
281 {
282 returnWrapper.nack("Network does not contain a Link with id " + address[0]);
283 return null;
284 }
285 if (!(link instanceof EventProducerInterface))
286 {
287 returnWrapper.nack("Link \"" + address[0] + "\" is not able to handle subscriptions");
288 return null;
289 }
290 return (CrossSectionLink) link;
291 }
292
293 @Override
294 public String toString()
295 {
296 return "LookupProducerInterface that looks up a Link in the network";
297 }
298
299 @Override
300 public MetaData getAddressMetaData()
301 {
302 return new MetaData("Link id", "Name of a link in the network",
303 new ObjectDescriptor[] { new ObjectDescriptor("Link id", "Name of a link in the network", String.class) });
304 }
305 };
306
307
308
309
310
311 private void addSubscriptionHandler(final SubscriptionHandler subscriptionHandler)
312 {
313 this.subscriptionHandlerMap.put(subscriptionHandler.getId(), subscriptionHandler);
314 }
315
316
317 @Override
318 public Object[] get(final Object[] address, final ReturnWrapper returnWrapper)
319 throws Sim0MQException, SerializationException
320 {
321 Throw.whenNull(returnWrapper, "returnWrapper may not be null");
322 String bad = verifyMetaData(getAddressFields(), address);
323 if (bad != null)
324 {
325 returnWrapper.nack("Bad address (should be the name of a transceiver): " + bad);
326 return null;
327 }
328 SubscriptionHandler subscriptionHandler = this.subscriptionHandlerMap.get(address[0]);
329 if (null == subscriptionHandler)
330 {
331 returnWrapper.nack("No transceiver with name \"" + address[0] + "\"");
332 return null;
333 }
334 return new Object[] { subscriptionHandler };
335 }
336
337
338 private final TransceiverInterface idSource = new TransceiverInterface()
339 {
340 @Override
341 public String getId()
342 {
343 return "Transceiver for names of available transceivers in Publisher";
344 }
345
346 @Override
347 public MetaData getAddressFields()
348 {
349 return MetaData.EMPTY;
350 }
351
352
353 private MetaData resultMetaData =
354 new MetaData("Transceiver names available in Publisher", "String array", new ObjectDescriptor[] {
355 new ObjectDescriptor("Transceiver names available in Publisher", "String array", String[].class) });
356
357 @Override
358 public MetaData getResultFields()
359 {
360 return this.resultMetaData;
361 }
362
363 @Override
364 public Object[] get(final Object[] address, final ReturnWrapper returnWrapper)
365 throws RemoteException, Sim0MQException, SerializationException
366 {
367 Object[] result = new Object[Publisher.this.subscriptionHandlerMap.size()];
368 int index = 0;
369 for (String key : Publisher.this.subscriptionHandlerMap.keySet())
370 {
371 result[index++] = key;
372 }
373 return result;
374 }
375 };
376
377
378 @Override
379 public TransceiverInterface getIdSource(final int addressLevel, final ReturnWrapper returnWrapper)
380 throws Sim0MQException, SerializationException
381 {
382 if (0 != addressLevel)
383 {
384 returnWrapper.encodeReplyAndTransmit("Address should be 0");
385 return null;
386 }
387 return this.idSource;
388 }
389
390
391 @Override
392 public boolean hasIdSource()
393 {
394 return true;
395 }
396
397
398
399
400
401
402
403
404
405
406
407 public void executeCommand(final String subscriptionHandlerName, final SubscriptionHandler.Command command,
408 final Object[] address, final ReturnWrapper returnWrapper)
409 throws RemoteException, Sim0MQException, SerializationException
410 {
411 SubscriptionHandler subscriptionHandler = this.subscriptionHandlerMap.get(subscriptionHandlerName);
412 if (null == subscriptionHandler)
413 {
414 returnWrapper.nack("No subscription handler for \"" + subscriptionHandlerName + "\"");
415 return;
416 }
417 subscriptionHandler.executeCommand(command, address, returnWrapper);
418 }
419
420
421
422
423
424
425
426
427
428
429
430 public void executeCommand(final String subscriptionHandlerName, final String commandString, final Object[] address,
431 final ReturnWrapperImpl returnWrapper) throws RemoteException, Sim0MQException, SerializationException
432 {
433 executeCommand(subscriptionHandlerName,
434 Throw.whenNull(SubscriptionHandler.lookupCommand(commandString), "Invalid command (%s)", commandString),
435 address, returnWrapper);
436 }
437
438
439
440
441
442
443 public IncomingDataHandler lookupIncomingDataHandler(final String key)
444 {
445 return this.incomingDataHandlers.get(key);
446 }
447
448 }