A Discrete-Event Network Simulator
API
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 trace source's path.
117  aggregator->SetTitle ( m_title + " \\n\\nTrace Source Path: " + path);
118 
119  // Set the default dataset plotting style for the values.
120  aggregator->Set2dDatasetDefaultStyle (Gnuplot2dDataset::LINES_POINTS);
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; this should correspond to the
132  // trace source attribute.
133  size_t lastSlash = path.find_last_of ("/");
134  if (lastSlash == std::string::npos)
135  {
136  pathWithoutLastToken = path;
137  lastToken = "";
138  }
139  else
140  {
141  // Chop off up to last token.
142  pathWithoutLastToken = path.substr (0, lastSlash);
143 
144  // Save the last token without the last slash.
145  lastToken = path.substr (lastSlash + 1, std::string::npos);
146  }
147 
148  // See if there are any matches for the probe's path with the last
149  // token removed; this corresponds to the traced object itself.
150  NS_LOG_DEBUG ("Searching config database for trace source " << path);
151  Config::MatchContainer matches = Config::LookupMatches (pathWithoutLastToken);
152  uint32_t matchCount = matches.GetN ();
153  NS_LOG_DEBUG ("Found " << matchCount << " matches for trace source " << path);
154 
155  // This is used to make the probe's context be unique.
156  std::string matchIdentifier;
157 
158  // Hook one or more probes and the aggregator together.
159  if (matchCount == 1 && pathHasNoWildcards)
160  {
161  // Connect the probe to the aggregator only once because there
162  // is only one matching config path. There is no need to find
163  // the wildcard matches because the passed in path has none.
164  matchIdentifier = "0";
165  ConnectProbeToAggregator (typeId,
166  matchIdentifier,
167  path,
168  probeTraceSource,
169  title);
170  }
171  else if (matchCount > 0)
172  {
173  // Handle all of the matches if there are more than one.
174  for (uint32_t i = 0; i < matchCount; i++)
175  {
176  // Set the match identifier.
177  std::ostringstream matchIdentifierStream;
178  matchIdentifierStream << i;
179  matchIdentifier = matchIdentifierStream.str ();
180 
181  // Construct the matched path and get the matches for each
182  // of the wildcards.
183  std::string wildcardSeparator = " ";
184  std::string matchedPath = matches.GetMatchedPath (i) + lastToken;
185  std::string wildcardMatches = GetWildcardMatches (path,
186  matchedPath,
187  wildcardSeparator);
188 
189  // Connect the probe to the aggregator for this match.
190  ConnectProbeToAggregator (typeId,
191  matchIdentifier,
192  matchedPath,
193  probeTraceSource,
194  title + "-" + wildcardMatches);
195  }
196  }
197  else
198  {
199  // There is a problem if there are no matching config paths.
200  NS_FATAL_ERROR ("Lookup of " << path << " got no matches");
201  }
202 }
203 
204 void
205 GnuplotHelper::AddProbe (const std::string &typeId,
206  const std::string &probeName,
207  const std::string &path)
208 {
209  NS_LOG_FUNCTION (this << typeId << probeName << path);
210 
211  // See if this probe had already been added.
212  if (m_probeMap.count (probeName) > 0)
213  {
214  NS_ABORT_MSG ("That probe has already been added");
215  }
216 
217  // Prepare the factory to create an object with the requested type.
218  m_factory.SetTypeId (typeId);
219 
220  // Create a base class object in order to validate the type.
221  Ptr<Probe> probe = m_factory.Create ()->GetObject<Probe> ();
222  if (probe == 0)
223  {
224  NS_ABORT_MSG ("The requested type is not a probe");
225  }
226 
227  // Set the probe's name.
228  probe->SetName (probeName);
229 
230  // Set the path. Note that no return value is checked here.
231  probe->ConnectByPath (path);
232 
233  // Enable logging of data for the probe.
234  probe->Enable ();
235 
236  // Add this probe to the map so that its values can be used.
237  m_probeMap[probeName] = std::make_pair (probe, typeId);
238 }
239 
240 void
241 GnuplotHelper::AddTimeSeriesAdaptor (const std::string &adaptorName)
242 {
243  NS_LOG_FUNCTION (this << adaptorName);
244 
245  // See if this time series adaptor had already been added.
246  if (m_timeSeriesAdaptorMap.count (adaptorName) > 0)
247  {
248  NS_ABORT_MSG ("That time series adaptor has already been added");
249  }
250 
251  // Create the time series adaptor.
252  Ptr<TimeSeriesAdaptor> timeSeriesAdaptor = CreateObject<TimeSeriesAdaptor> ();
253 
254  // Enable logging of data for the time series adaptor.
255  timeSeriesAdaptor->Enable ();
256 
257  // Add this time series adaptor to the map so that can be used.
258  m_timeSeriesAdaptorMap[adaptorName] = timeSeriesAdaptor;
259 }
260 
262 GnuplotHelper::GetProbe (std::string probeName) const
263 {
264  // Look for the probe.
265  std::map<std::string, std::pair <Ptr<Probe>, std::string> >::const_iterator mapIterator = m_probeMap.find (probeName);
266 
267  // Return the probe if it has been added.
268  if (mapIterator != m_probeMap.end ())
269  {
270  return mapIterator->second.first;
271  }
272  else
273  {
274  NS_ABORT_MSG ("That probe has not been added");
275  }
276 }
277 
280 {
281  NS_LOG_FUNCTION (this);
282 
283  // Do a lazy construction of the aggregator if it hasn't already
284  // been constructed.
285  if (!m_aggregator)
286  {
288  }
289  return m_aggregator;
290 }
291 
292 void
294 {
295  NS_LOG_FUNCTION (this);
296 
297  // Create the aggregator.
298  m_aggregator = CreateObject<GnuplotAggregator> (m_outputFileNameWithoutExtension);
299 
300  // Set the aggregator's properties.
301  m_aggregator->SetTerminal (m_terminalType);
302  m_aggregator->SetTitle (m_title);
303  m_aggregator->SetLegend (m_xLegend, m_yLegend);
304 
305  // Enable logging of data for the aggregator.
306  m_aggregator->Enable ();
307 }
308 
309 void
310 GnuplotHelper::ConnectProbeToAggregator (const std::string &typeId,
311  const std::string &matchIdentifier,
312  const std::string &path,
313  const std::string &probeTraceSource,
314  const std::string &title)
315 {
316  NS_LOG_FUNCTION (this << typeId << matchIdentifier << path << probeTraceSource
317  << title);
318 
319  Ptr<GnuplotAggregator> aggregator = GetAggregator ();
320 
321  // Increment the total number of plot probes that have been created.
323 
324  // Create a unique name for this probe.
325  std::ostringstream probeNameStream;
326  probeNameStream << "PlotProbe-" << m_plotProbeCount;
327  std::string probeName = probeNameStream.str ();
328 
329  // Create a unique dataset context string for this probe.
330  std::string probeContext = probeName
331  + "/" + matchIdentifier + "/" + probeTraceSource;
332 
333  // Add the probe to the map of probes, which will keep the probe in
334  // memory after this function ends.
335  AddProbe (typeId, probeName, path);
336 
337  // Because the callbacks to the probes' trace sources don't use the
338  // probe's context, a unique adaptor needs to be created for each
339  // probe context so that information is not lost.
340  AddTimeSeriesAdaptor (probeContext);
341 
342  // Connect the probe to the adaptor.
343  if (m_probeMap[probeName].second == "ns3::DoubleProbe")
344  {
345  m_probeMap[probeName].first->TraceConnectWithoutContext
346  (probeTraceSource,
348  m_timeSeriesAdaptorMap[probeContext]));
349  }
350  else if (m_probeMap[probeName].second == "ns3::BooleanProbe")
351  {
352  m_probeMap[probeName].first->TraceConnectWithoutContext
353  (probeTraceSource,
355  m_timeSeriesAdaptorMap[probeContext]));
356  }
357  else if (m_probeMap[probeName].second == "ns3::PacketProbe")
358  {
359  m_probeMap[probeName].first->TraceConnectWithoutContext
360  (probeTraceSource,
362  m_timeSeriesAdaptorMap[probeContext]));
363  }
364  else if (m_probeMap[probeName].second == "ns3::ApplicationPacketProbe")
365  {
366  m_probeMap[probeName].first->TraceConnectWithoutContext
367  (probeTraceSource,
369  m_timeSeriesAdaptorMap[probeContext]));
370  }
371  else if (m_probeMap[probeName].second == "ns3::Ipv4PacketProbe")
372  {
373  m_probeMap[probeName].first->TraceConnectWithoutContext
374  (probeTraceSource,
376  m_timeSeriesAdaptorMap[probeContext]));
377  }
378  else if (m_probeMap[probeName].second == "ns3::Ipv6PacketProbe")
379  {
380  m_probeMap[probeName].first->TraceConnectWithoutContext
381  (probeTraceSource,
383  m_timeSeriesAdaptorMap[probeContext]));
384  }
385  else if (m_probeMap[probeName].second == "ns3::Uinteger8Probe")
386  {
387  m_probeMap[probeName].first->TraceConnectWithoutContext
388  (probeTraceSource,
390  m_timeSeriesAdaptorMap[probeContext]));
391  }
392  else if (m_probeMap[probeName].second == "ns3::Uinteger16Probe")
393  {
394  m_probeMap[probeName].first->TraceConnectWithoutContext
395  (probeTraceSource,
397  m_timeSeriesAdaptorMap[probeContext]));
398  }
399  else if (m_probeMap[probeName].second == "ns3::Uinteger32Probe")
400  {
401  m_probeMap[probeName].first->TraceConnectWithoutContext
402  (probeTraceSource,
404  m_timeSeriesAdaptorMap[probeContext]));
405  }
406  else if (m_probeMap[probeName].second == "ns3::TimeProbe")
407  {
408  m_probeMap[probeName].first->TraceConnectWithoutContext
409  (probeTraceSource,
411  m_timeSeriesAdaptorMap[probeContext]));
412  }
413  else
414  {
415  NS_FATAL_ERROR ("Unknown probe type " << m_probeMap[probeName].second << "; need to add support in the helper for this");
416  }
417 
418  // Connect the adaptor to the aggregator.
419  std::string adaptorTraceSource = "Output";
420  m_timeSeriesAdaptorMap[probeContext]->TraceConnect
421  (adaptorTraceSource,
422  probeContext,
424 
425  // Add the dataset to the plot.
426  aggregator->Add2dDataset (probeContext, title);
427 }
428 
429 } // namespace ns3
430 
std::string GetMatchedPath(uint32_t i) const
Definition: config.cc:81
void ConnectProbeToAggregator(const std::string &typeId, const std::string &matchIdentifier, const std::string &path, const std::string &probeTraceSource, const std::string &title)
Connects the probe to the aggregator.
Smart pointer class similar to boost::intrusive_ptr.
Definition: ptr.h:73
#define NS_LOG_FUNCTION(parameters)
If log level LOG_FUNCTION is enabled, this macro will output all input parameters separated by "...
Definition: second.py:1
#define NS_ABORT_MSG(msg)
Unconditional abnormal program termination with a message.
Definition: abort.h:50
void PlotProbe(const std::string &typeId, const std::string &path, const std::string &probeTraceSource, const std::string &title, enum GnuplotAggregator::KeyLocation keyLocation=GnuplotAggregator::KEY_INSIDE)
void AddTimeSeriesAdaptor(const std::string &adaptorName)
Adds a time series adaptor to be used to make the plot.
std::map< std::string, std::pair< Ptr< Probe >, std::string > > m_probeMap
Maps probe names to probes.
#define NS_LOG_COMPONENT_DEFINE(name)
Define a Log component with a specific name.
Definition: log.h:202
void SetTypeId(TypeId tid)
Set the TypeId of the Objects to be created by this factory.
void Write2d(std::string context, double x, double y)
Writes a 2D value to a 2D gnuplot dataset.
#define NS_FATAL_ERROR(msg)
Report a fatal error with a message and terminate.
Definition: fatal-error.h:162
std::string m_xLegend
Legend for the x axis.
GnuplotHelper()
Constructs a gnuplot helper that will create a space separated gnuplot data file named "gnuplot-helpe...
std::size_t GetN(void) const
Definition: config.cc:69
uint32_t m_plotProbeCount
Number of plot probes that have been created.
ObjectFactory m_factory
Used to create the probes and collectors as they are added.
virtual ~GnuplotHelper()
std::string m_outputFileNameWithoutExtension
The name of the output file to created without its extension.
void ConfigurePlot(const std::string &outputFileNameWithoutExtension, const std::string &title, const std::string &xLegend, const std::string &yLegend, const std::string &terminalType="png")
Ptr< Object > Create(void) const
Create an Object instance of the configured TypeId.
Ptr< GnuplotAggregator > m_aggregator
The aggregator used to make the plots.
void AddProbe(const std::string &typeId, const std::string &probeName, const std::string &path)
Adds a probe to be used to make the plot.
std::map< std::string, Ptr< TimeSeriesAdaptor > > m_timeSeriesAdaptorMap
Maps time series adaptor names to time series adaptors.
void ConstructAggregator()
Constructs the aggregator.
Callback< R > MakeCallback(R(T::*memPtr)(void), OBJ objPtr)
Definition: callback.h:1489
void TraceSinkUinteger32(uint32_t oldData, uint32_t newData)
Trace sink for receiving data from uint32_t valued trace sources.
Ptr< T > GetObject(void) const
Get a pointer to the requested aggregated Object.
Definition: object.h:459
Ptr< GnuplotAggregator > GetAggregator()
Gets the aggregator.
Every class exported by the ns3 library is enclosed in the ns3 namespace.
void TraceSinkUinteger8(uint8_t oldData, uint8_t newData)
Trace sink for receiving data from uint8_t valued trace sources.
hold a set of objects which match a specific search string.
Definition: config.h:151
MatchContainer LookupMatches(std::string path)
Definition: config.cc:854
KeyLocation
The location of the key in the plot.
#define NS_LOG_WARN(msg)
Use NS_LOG to output a message of level LOG_WARN.
Definition: log.h:262
Ptr< Probe > GetProbe(std::string probeName) const
Gets the specified probe.
#define NS_LOG_DEBUG(msg)
Use NS_LOG to output a message of level LOG_DEBUG.
Definition: log.h:270
void TraceSinkUinteger16(uint16_t oldData, uint16_t newData)
Trace sink for receiving data from uint16_t valued trace sources.
void TraceSinkDouble(double oldData, double newData)
Trace sink for receiving data from double valued trace sources.
void TraceSinkBoolean(bool oldData, bool newData)
Trace sink for receiving data from bool valued trace sources.
std::string m_yLegend
Legend for the y axis.
std::string GetWildcardMatches(const std::string &configPath, const std::string &matchedPath, const std::string &wildcardSeparator)
Returns the text matches from the matched path for each of the wildcards in the Config path...
Base class for probes.
Definition: probe.h:39
std::string m_title
Title string to use for this plot.
std::string m_terminalType
Terminal type for the plot.