A Discrete-Event Network Simulator
API
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
gnuplot-helper.cc
Go to the documentation of this file.
1 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2 /*
3  * Copyright (c) 2013 University of Washington
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License version 2 as
7  * published by the Free Software Foundation;
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17  *
18  * Author: Mitch Watrous (watrous@u.washington.edu)
19  */
20 
21 #include <iostream>
22 #include <fstream>
23 #include <string>
24 #include <sstream>
25 
26 #include "gnuplot-helper.h"
27 #include "ns3/abort.h"
28 #include "ns3/assert.h"
29 #include "ns3/config.h"
30 #include "ns3/log.h"
31 #include "ns3/get-wildcard-matches.h"
32 
33 namespace ns3 {
34 
35 NS_LOG_COMPONENT_DEFINE ("GnuplotHelper");
36 
38  : m_aggregator (0),
39  m_plotProbeCount (0),
40  m_outputFileNameWithoutExtension ("gnuplot-helper"),
41  m_title ("Gnuplot Helper Plot"),
42  m_xLegend ("X Values"),
43  m_yLegend ("Y Values"),
44  m_terminalType ("png")
45 {
46  NS_LOG_FUNCTION (this);
47 
48  // Note that this does not construct an aggregator. It will be
49  // constructed later when needed.
50 }
51 
52 GnuplotHelper::GnuplotHelper (const std::string &outputFileNameWithoutExtension,
53  const std::string &title,
54  const std::string &xLegend,
55  const std::string &yLegend,
56  const std::string &terminalType)
57  : m_aggregator (0),
58  m_plotProbeCount (0),
59  m_outputFileNameWithoutExtension (outputFileNameWithoutExtension),
60  m_title (title),
61  m_xLegend (xLegend),
62  m_yLegend (yLegend),
63  m_terminalType (terminalType)
64 {
65  NS_LOG_FUNCTION (this);
66 
67  // Construct the aggregator.
69 }
70 
72 {
73  NS_LOG_FUNCTION (this);
74 }
75 
76 void
77 GnuplotHelper::ConfigurePlot (const std::string &outputFileNameWithoutExtension,
78  const std::string &title,
79  const std::string &xLegend,
80  const std::string &yLegend,
81  const std::string &terminalType)
82 {
83  NS_LOG_FUNCTION (this << outputFileNameWithoutExtension << title
84  << xLegend << yLegend << terminalType);
85 
86  // See if an aggregator has already been constructed.
87  if (m_aggregator != 0)
88  {
89  NS_LOG_WARN ("An existing aggregator object " << m_aggregator <<
90  " may be destroyed if no references remain.");
91  }
92 
93  // Store these so that they can be used to construct the aggregator.
94  m_outputFileNameWithoutExtension = outputFileNameWithoutExtension;
95  m_title = title;
96  m_xLegend = xLegend;
97  m_yLegend = yLegend;
98  m_terminalType = terminalType;
99 
100  // Construct the aggregator.
102 }
103 
104 void
105 GnuplotHelper::PlotProbe (const std::string &typeId,
106  const std::string &path,
107  const std::string &probeTraceSource,
108  const std::string &title,
109  enum GnuplotAggregator::KeyLocation keyLocation)
110 {
111  NS_LOG_FUNCTION (this << typeId << path << probeTraceSource << title << keyLocation);
112 
113  // Get a pointer to the aggregator.
114  Ptr<GnuplotAggregator> aggregator = GetAggregator ();
115 
116  // Add a subtitle to the title to show the probe's path.
117  aggregator->SetTitle ( m_title + " \\n\\nProbe Path: " + path);
118 
119  // Set the default dataset plotting style for the values.
121 
122  // Set the location of the key in the plot.
123  aggregator->SetKeyLocation (keyLocation);
124 
125  std::string pathWithoutLastToken;
126  std::string lastToken;
127 
128  // See if the path has any wildcards.
129  bool pathHasNoWildcards = path.find ("*") == std::string::npos;
130 
131  // Remove the last token from the path.
132  size_t lastSlash = path.find_last_of ("/");
133  if (lastSlash == std::string::npos)
134  {
135  pathWithoutLastToken = path;
136  lastToken = "";
137  }
138  else
139  {
140  // Chop off up to last token.
141  pathWithoutLastToken = path.substr (0, lastSlash);
142 
143  // Save the last token without the last slash.
144  lastToken = path.substr (lastSlash + 1, std::string::npos);
145  }
146 
147  // See if there are any matches for the probe's path with the last
148  // token removed.
149  Config::MatchContainer matches = Config::LookupMatches (pathWithoutLastToken);
150  uint32_t matchCount = matches.GetN ();
151 
152  // This is used to make the probe's context be unique.
153  std::string matchIdentifier;
154 
155  // Hook one or more probes and the aggregator together.
156  if (matchCount == 1 && pathHasNoWildcards)
157  {
158  // Connect the probe to the aggregator only once because there
159  // is only one matching config path. There is no need to find
160  // the wildcard matches because the passed in path has none.
161  matchIdentifier = "0";
162  ConnectProbeToAggregator (typeId,
163  matchIdentifier,
164  path,
165  probeTraceSource,
166  title);
167  }
168  else if (matchCount > 0)
169  {
170  // Handle all of the matches if there are more than one.
171  for (uint32_t i = 0; i < matchCount; i++)
172  {
173  // Set the match identifier.
174  std::ostringstream matchIdentifierStream;
175  matchIdentifierStream << i;
176  matchIdentifier = matchIdentifierStream.str ();
177 
178  // Construct the matched path and get the matches for each
179  // of the wildcards.
180  std::string wildcardSeparator = " ";
181  std::string matchedPath = matches.GetMatchedPath (i) + lastToken;
182  std::string wildcardMatches = GetWildcardMatches (path,
183  matchedPath,
184  wildcardSeparator);
185 
186  // Connect the probe to the aggregator for this match.
187  ConnectProbeToAggregator (typeId,
188  matchIdentifier,
189  matchedPath,
190  probeTraceSource,
191  title + "-" + wildcardMatches);
192  }
193  }
194  else
195  {
196  // There is a problem if there are no matching config paths.
197  NS_FATAL_ERROR ("Lookup of " << path << " got no matches");
198  }
199 }
200 
201 void
202 GnuplotHelper::AddProbe (const std::string &typeId,
203  const std::string &probeName,
204  const std::string &path)
205 {
206  NS_LOG_FUNCTION (this << typeId << probeName << path);
207 
208  // See if this probe had already been added.
209  if (m_probeMap.count (probeName) > 0)
210  {
211  NS_ABORT_MSG ("That probe has already been added");
212  }
213 
214  // Prepare the factory to create an object with the requested type.
215  m_factory.SetTypeId (typeId);
216 
217  // Create a base class object in order to validate the type.
218  Ptr<Probe> probe = m_factory.Create ()->GetObject<Probe> ();
219  if (probe == 0)
220  {
221  NS_ABORT_MSG ("The requested type is not a probe");
222  }
223 
224  // Set the probe's name.
225  probe->SetName (probeName);
226 
227  // Set the path. Note that no return value is checked here.
228  probe->ConnectByPath (path);
229 
230  // Enable logging of data for the probe.
231  probe->Enable ();
232 
233  // Add this probe to the map so that its values can be used.
234  m_probeMap[probeName] = std::make_pair (probe, typeId);
235 }
236 
237 void
238 GnuplotHelper::AddTimeSeriesAdaptor (const std::string &adaptorName)
239 {
240  NS_LOG_FUNCTION (this << adaptorName);
241 
242  // See if this time series adaptor had already been added.
243  if (m_timeSeriesAdaptorMap.count (adaptorName) > 0)
244  {
245  NS_ABORT_MSG ("That time series adaptor has already been added");
246  }
247 
248  // Create the time series adaptor.
249  Ptr<TimeSeriesAdaptor> timeSeriesAdaptor = CreateObject<TimeSeriesAdaptor> ();
250 
251  // Enable logging of data for the time series adaptor.
252  timeSeriesAdaptor->Enable ();
253 
254  // Add this time series adaptor to the map so that can be used.
255  m_timeSeriesAdaptorMap[adaptorName] = timeSeriesAdaptor;
256 }
257 
259 GnuplotHelper::GetProbe (std::string probeName) const
260 {
261  // Look for the probe.
262  std::map<std::string, std::pair <Ptr<Probe>, std::string> >::const_iterator mapIterator = m_probeMap.find (probeName);
263 
264  // Return the probe if it has been added.
265  if (mapIterator != m_probeMap.end ())
266  {
267  return mapIterator->second.first;
268  }
269  else
270  {
271  NS_ABORT_MSG ("That probe has not been added");
272  }
273 }
274 
277 {
278  NS_LOG_FUNCTION (this);
279 
280  // Do a lazy construction of the aggregator if it hasn't already
281  // been constructed.
282  if (!m_aggregator)
283  {
285  }
286  return m_aggregator;
287 }
288 
289 void
291 {
292  NS_LOG_FUNCTION (this);
293 
294  // Create the aggregator.
295  m_aggregator = CreateObject<GnuplotAggregator> (m_outputFileNameWithoutExtension);
296 
297  // Set the aggregator's properties.
301 
302  // Enable logging of data for the aggregator.
303  m_aggregator->Enable ();
304 }
305 
306 void
307 GnuplotHelper::ConnectProbeToAggregator (const std::string &typeId,
308  const std::string &matchIdentifier,
309  const std::string &path,
310  const std::string &probeTraceSource,
311  const std::string &title)
312 {
313  NS_LOG_FUNCTION (this << typeId << matchIdentifier << path << probeTraceSource
314  << title);
315 
316  Ptr<GnuplotAggregator> aggregator = GetAggregator ();
317 
318  // Increment the total number of plot probes that have been created.
320 
321  // Create a unique name for this probe.
322  std::ostringstream probeNameStream;
323  probeNameStream << "PlotProbe-" << m_plotProbeCount;
324  std::string probeName = probeNameStream.str ();
325 
326  // Create a unique dataset context string for this probe.
327  std::string probeContext = probeName
328  + "/" + matchIdentifier + "/" + probeTraceSource;
329 
330  // Add the probe to the map of probes, which will keep the probe in
331  // memory after this function ends.
332  AddProbe (typeId, probeName, path);
333 
334  // Because the callbacks to the probes' trace sources don't use the
335  // probe's context, a unique adaptor needs to be created for each
336  // probe context so that information is not lost.
337  AddTimeSeriesAdaptor (probeContext);
338 
339  // Connect the probe to the adaptor.
340  if (m_probeMap[probeName].second == "ns3::DoubleProbe")
341  {
342  m_probeMap[probeName].first->TraceConnectWithoutContext
343  (probeTraceSource,
345  m_timeSeriesAdaptorMap[probeContext]));
346  }
347  else if (m_probeMap[probeName].second == "ns3::BooleanProbe")
348  {
349  m_probeMap[probeName].first->TraceConnectWithoutContext
350  (probeTraceSource,
352  m_timeSeriesAdaptorMap[probeContext]));
353  }
354  else if (m_probeMap[probeName].second == "ns3::PacketProbe")
355  {
356  m_probeMap[probeName].first->TraceConnectWithoutContext
357  (probeTraceSource,
359  m_timeSeriesAdaptorMap[probeContext]));
360  }
361  else if (m_probeMap[probeName].second == "ns3::ApplicationPacketProbe")
362  {
363  m_probeMap[probeName].first->TraceConnectWithoutContext
364  (probeTraceSource,
366  m_timeSeriesAdaptorMap[probeContext]));
367  }
368  else if (m_probeMap[probeName].second == "ns3::Ipv4PacketProbe")
369  {
370  m_probeMap[probeName].first->TraceConnectWithoutContext
371  (probeTraceSource,
373  m_timeSeriesAdaptorMap[probeContext]));
374  }
375  else if (m_probeMap[probeName].second == "ns3::Ipv6PacketProbe")
376  {
377  m_probeMap[probeName].first->TraceConnectWithoutContext
378  (probeTraceSource,
380  m_timeSeriesAdaptorMap[probeContext]));
381  }
382  else if (m_probeMap[probeName].second == "ns3::Uinteger8Probe")
383  {
384  m_probeMap[probeName].first->TraceConnectWithoutContext
385  (probeTraceSource,
387  m_timeSeriesAdaptorMap[probeContext]));
388  }
389  else if (m_probeMap[probeName].second == "ns3::Uinteger16Probe")
390  {
391  m_probeMap[probeName].first->TraceConnectWithoutContext
392  (probeTraceSource,
394  m_timeSeriesAdaptorMap[probeContext]));
395  }
396  else if (m_probeMap[probeName].second == "ns3::Uinteger32Probe")
397  {
398  m_probeMap[probeName].first->TraceConnectWithoutContext
399  (probeTraceSource,
401  m_timeSeriesAdaptorMap[probeContext]));
402  }
403  else
404  {
405  NS_FATAL_ERROR ("Unknown probe type " << m_probeMap[probeName].second << "; need to add support in the helper for this");
406  }
407 
408  // Connect the adaptor to the aggregator.
409  std::string adaptorTraceSource = "Output";
410  m_timeSeriesAdaptorMap[probeContext]->TraceConnect
411  (adaptorTraceSource,
412  probeContext,
414 
415  // Add the dataset to the plot.
416  aggregator->Add2dDataset (probeContext, title);
417 }
418 
419 } // namespace ns3
420