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 }