1 package org.opentrafficsim.imb.transceiver.urbanstrategy;
2
3 import java.io.IOException;
4 import java.util.ArrayList;
5 import java.util.List;
6
7 import org.djunits.value.vdouble.scalar.Duration;
8 import org.opentrafficsim.core.network.Network;
9 import org.opentrafficsim.graphs.AbstractOTSPlot;
10 import org.opentrafficsim.graphs.ContourPlot;
11 import org.opentrafficsim.graphs.TrajectoryPlot;
12 import org.opentrafficsim.imb.IMBException;
13 import org.opentrafficsim.imb.connector.Connector;
14 import org.opentrafficsim.imb.connector.Connector.IMBEventType;
15 import org.opentrafficsim.imb.transceiver.AbstractTransceiver;
16 import org.opentrafficsim.road.network.lane.Lane;
17 import org.opentrafficsim.simulationengine.SimpleSimulatorInterface;
18
19 import nl.tudelft.simulation.dsol.SimRuntimeException;
20
21 /**
22 * OTS can publish graphs to IMB, e.g. trajectory graphs, flow graphs and density graphs.<br>
23 * When a graph is published for the first time, a NEW message is sent to IMB to identify the graph, the image resolution, and
24 * the lane(s) for which the graph is created. The CHANGE message is posted whenever an updated graph is posted. When a Graph is
25 * no longer published, a DELETE event is posted. The Graph NEW messages are posted after the Network NEW, Node NEW, Link NEW,
26 * and Lane NEW messages are posted, as it has to be able to identify Lanes.
27 * <p>
28 * <style>table,th,td {border:1px solid grey; border-style:solid; text-align:left; border-collapse: collapse;}</style>
29 * <h2>NEW</h2>
30 * <table summary="" style="width:800px;">
31 * <thead>
32 * <tr>
33 * <th style="width:25%;">Variable</th>
34 * <th style="width:15%;">Type</th>
35 * <th style="width:60%;">Comments</th>
36 * </tr>
37 * </thead><tbody>
38 * <tr>
39 * <td>timestamp</td>
40 * <td>double</td>
41 * <td>time of the event, in simulation time seconds</td>
42 * </tr>
43 * <tr>
44 * <td>graphId</td>
45 * <td>String</td>
46 * <td>a unique id for the graph, e.g. a UUID string</td>
47 * </tr>
48 * <tr>
49 * <td>width</td>
50 * <td>int</td>
51 * <td>width of the graph in pixels</td>
52 * </tr>
53 * <tr>
54 * <td>height</td>
55 * <td>int</td>
56 * <td>height of the graph in pixels</td>
57 * </tr>
58 * <tr>
59 * <td>description</td>
60 * <td>String</td>
61 * <td>textual description of the graph</td>
62 * </tr>
63 * <tr>
64 * <td>graphType</td>
65 * <td>String</td>
66 * <td>type of graph; one of TRAJECTORY, SPEED_CONTOUR, ACCELERATION_CONTOUR, DENSITY_CONTOUR, FLOW_CONTOUR,
67 * FUNDAMENTAL_DIAGRAM</td>
68 * </tr>
69 * <tr>
70 * <td>time_resolution</td>
71 * <td>double</td>
72 * <td>For the four types of contour graphs, and the trajectory graph, it provides the aggregation in seconds. 0.0 for other
73 * types of graphs. When the value is 0.0 for the trajectory graph, the graph is updated on the basis of events from the
74 * GTU.</td>
75 * </tr>
76 * <tr>
77 * <td>value_resolution</td>
78 * <td>double</td>
79 * <td>For the four types of contour graphs, it provides the aggregation in the SI unit of the value. 0.0 for other types of
80 * graphs</td>
81 * </tr>
82 * <tr>
83 * <td>networkId</td>
84 * <td>String</td>
85 * <td>id of the Network for which the Graph is made</td>
86 * </tr>
87 * <tr>
88 * <td>numberOfLanes</td>
89 * <td>int</td>
90 * <td>number of Link-Lane combinations for this Graph</td>
91 * </tr>
92 * <tr>
93 * <td>linkId_1</td>
94 * <td>String</td>
95 * <td>id of the first Link; unique within the Network</td>
96 * </tr>
97 * <tr>
98 * <td>laneId_1</td>
99 * <td>String</td>
100 * <td>id of the first Lane, unique within the Link</td>
101 * </tr>
102 * <tr>
103 * <td>...</td>
104 * <td> </td>
105 * <td> </td>
106 * </tr>
107 * <tr>
108 * <td>linkId_n</td>
109 * <td>String</td>
110 * <td>id of the last Link; unique within the Network</td>
111 * </tr>
112 * <tr>
113 * <td>laneId_n</td>
114 * <td>String</td>
115 * <td>id of the last Lane, unique within the Link</td>
116 * </tr>
117 * <tr>
118 * <td>transmissionInterval</td>
119 * <td>double</td>
120 * <td>transmission interval of the graph in seconds</td>
121 * </tr>
122 * </tbody>
123 * </table>
124 * </p>
125 * <p>
126 * <h2>CHANGE</h2>
127 * <table summary="" style="width:800px;">
128 * <thead>
129 * <tr>
130 * <th style="width:25%;">Variable</th>
131 * <th style="width:15%;">Type</th>
132 * <th style="width:60%;">Comments</th>
133 * </tr>
134 * </thead><tbody>
135 * <tr>
136 * <td>timestamp</td>
137 * <td>double</td>
138 * <td>time of the event, in simulation time seconds</td>
139 * </tr>
140 * <tr>
141 * <td>graphId</td>
142 * <td>String</td>
143 * <td>the unique id for the graph, e.g. a UUID string</td>
144 * </tr>
145 * <tr>
146 * <td>width</td>
147 * <td>int</td>
148 * <td>width of the graph in pixels</td>
149 * </tr>
150 * <tr>
151 * <td>height</td>
152 * <td>int</td>
153 * <td>height of the graph in pixels</td>
154 * </tr>
155 * <tr>
156 * <td>image data</td>
157 * <td>byte[]</td>
158 * <td>image in PNG format; starts with the standard 8-byte signature 89 50 4E 47 0D 0A 1A 0A.</td>
159 * </tr>
160 * </tbody>
161 * </table>
162 * </p>
163 * <p>
164 * <h2>DELETE</h2>
165 * <table summary="" style="width:800px;">
166 * <thead>
167 * <tr>
168 * <th style="width:25%;">Variable</th>
169 * <th style="width:15%;">Type</th>
170 * <th style="width:60%;">Comments</th>
171 * </tr>
172 * </thead><tbody>
173 * <tr>
174 * <td>timestamp</td>
175 * <td>double</td>
176 * <td>time of the event, in simulation time seconds</td>
177 * </tr>
178 * <tr>
179 * <td>graphId</td>
180 * <td>String</td>
181 * <td>the unique id for the graph that is removed</td>
182 * </tr>
183 * <tr>
184 * <td>width</td>
185 * <td>int</td>
186 * <td>width of the graph in pixels</td>
187 * </tr>
188 * <tr>
189 * <td>height</td>
190 * <td>int</td>
191 * <td>height of the graph in pixels</td>
192 * </tr>
193 * </tbody>
194 * </table>
195 * <br>
196 * Note: when two resolutions for the same graph are sent over the network, they have the same graphId but a different width
197 * and/or height. This means that the combination of graphId, width, and height creates a unique key for the graph.
198 * </p>
199 * <p>
200 * Copyright (c) 2013-2016 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
201 * BSD-style license. See <a href="http://opentrafficsim.org/docs/current/license.html">OpenTrafficSim License</a>.
202 * <p>
203 * @version $Revision$, $LastChangedDate$, by $Author$, initial version Sep 16, 2016 <br>
204 * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
205 * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
206 */
207 public class GraphTransceiver extends AbstractTransceiver
208 {
209 /** */
210 private static final long serialVersionUID = 20160919L;
211
212 /** The Network for which the graph is made. */
213 private final Network network;
214
215 /** The width of the graph, in pixels. */
216 private final int width;
217
218 /** The height of the graph, in pixels. */
219 private final int height;
220
221 /** The interval between generation of graphs. */
222 private final Duration transmissionInterval;
223
224 // TODO handle the DELETE message
225
226 /**
227 * Construct a new GraphTransceiver.
228 * @param connector Connector; the IMB connector
229 * @param simulator SimpleSimulatorInterface; the simulator
230 * @param network Network; the network
231 * @param width int; the width of the graph, in pixels
232 * @param height int; the height of the graph, in pixels
233 * @param plot AbstractOTSPlot; the graph
234 * @param transmissionInterval Duration; the interval between generation of graphs
235 * @throws IMBException when the message cannot be posted, or the scheduling of the publish event fails
236 */
237 public GraphTransceiver(final Connector connector, SimpleSimulatorInterface simulator, Network network, final int width,
238 final int height, final AbstractOTSPlot plot, final Duration transmissionInterval) throws IMBException
239 {
240 super("Graph", connector, simulator);
241 this.network = network;
242 this.width = width;
243 this.height = height;
244 this.transmissionInterval = transmissionInterval;
245
246 List<Object> newMessage = new ArrayList<>();
247 newMessage.add(getSimulator().getSimulatorTime().getTime().si);
248 newMessage.add(plot.getId());
249 newMessage.add(width);
250 newMessage.add(height);
251 newMessage.add(plot.getCaption());
252 newMessage.add(plot.getGraphType().toString());
253 if (plot instanceof TrajectoryPlot)
254 {
255 Duration interval = ((TrajectoryPlot) plot).getSampleInterval();
256 newMessage.add(interval == null ? 0.0d : interval.si);
257 }
258 else if (plot instanceof ContourPlot)
259 {
260 newMessage.add(((ContourPlot) plot).getXAxis().getCurrentGranularity());
261 }
262 else
263 {
264 newMessage.add(0.0d);
265 }
266 newMessage.add(plot instanceof ContourPlot ? ((ContourPlot) plot).getYAxis().getCurrentGranularity() : 0.0d);
267 newMessage.add(this.network.getId());
268 newMessage.add(plot.getPath().size());
269 for (Lane lane : plot.getPath())
270 {
271 newMessage.add(lane.getParentLink().getId());
272 newMessage.add(lane.getId());
273 }
274 newMessage.add(transmissionInterval.si);
275
276 getConnector().postIMBMessage("Graph", IMBEventType.NEW, newMessage.toArray());
277
278 try
279 {
280 simulator.scheduleEventRel(this.transmissionInterval, this, this, "makePNG", new Object[] { plot });
281 }
282 catch (SimRuntimeException exception)
283 {
284 throw new IMBException(exception);
285 }
286 }
287
288 /**
289 * @param plot the plot to generate the PNG for
290 * @throws IOException when the creation of the PNG has failed
291 * @throws IMBException when the transmission of the IMB message fails
292 * @throws SimRuntimeException when the scheduling of the next publish event fails
293 */
294 public void makePNG(final AbstractOTSPlot plot) throws IOException, IMBException, SimRuntimeException
295 {
296 byte[] png = plot.generatePNG(this.width, this.height);
297 getConnector().postIMBMessage("Graph", IMBEventType.CHANGE,
298 new Object[] { getSimulator().getSimulatorTime().getTime().si, plot.getId(), this.width, this.height, png });
299 getSimulator().scheduleEventRel(this.transmissionInterval, this, this, "makePNG", new Object[] { plot });
300 }
301 }