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