A Discrete-Event Network Simulator
API
gnuplot-helper.cc
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2013 University of Washington
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License version 2 as
6  * published by the Free Software Foundation;
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program; if not, write to the Free Software
15  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16  *
17  * Author: Mitch Watrous (watrous@u.washington.edu)
18  */
19 
20 #include "gnuplot-helper.h"
21 
22 #include "ns3/abort.h"
23 #include "ns3/assert.h"
24 #include "ns3/config.h"
25 #include "ns3/get-wildcard-matches.h"
26 #include "ns3/log.h"
27 
28 #include <fstream>
29 #include <iostream>
30 #include <sstream>
31 #include <string>
32 
33 namespace ns3
34 {
35 
36 NS_LOG_COMPONENT_DEFINE("GnuplotHelper");
37 
39  : m_aggregator(nullptr),
40  m_plotProbeCount(0),
41  m_outputFileNameWithoutExtension("gnuplot-helper"),
42  m_title("Gnuplot Helper Plot"),
43  m_xLegend("X Values"),
44  m_yLegend("Y Values"),
45  m_terminalType("png")
46 {
47  NS_LOG_FUNCTION(this);
48 
49  // Note that this does not construct an aggregator. It will be
50  // constructed later when needed.
51 }
52 
53 GnuplotHelper::GnuplotHelper(const std::string& outputFileNameWithoutExtension,
54  const std::string& title,
55  const std::string& xLegend,
56  const std::string& yLegend,
57  const std::string& terminalType)
58  : m_aggregator(nullptr),
59  m_plotProbeCount(0),
60  m_outputFileNameWithoutExtension(outputFileNameWithoutExtension),
61  m_title(title),
62  m_xLegend(xLegend),
63  m_yLegend(yLegend),
64  m_terminalType(terminalType)
65 {
66  NS_LOG_FUNCTION(this);
67 
68  // Construct the aggregator.
70 }
71 
73 {
74  NS_LOG_FUNCTION(this);
75 }
76 
77 void
78 GnuplotHelper::ConfigurePlot(const std::string& outputFileNameWithoutExtension,
79  const std::string& title,
80  const std::string& xLegend,
81  const std::string& yLegend,
82  const std::string& terminalType)
83 {
84  NS_LOG_FUNCTION(this << outputFileNameWithoutExtension << title << xLegend << yLegend
85  << terminalType);
86 
87  // See if an aggregator has already been constructed.
88  if (m_aggregator)
89  {
90  NS_LOG_WARN("An existing aggregator object "
91  << m_aggregator << " may be destroyed if no references remain.");
92  }
93 
94  // Store these so that they can be used to construct the aggregator.
95  m_outputFileNameWithoutExtension = outputFileNameWithoutExtension;
96  m_title = title;
97  m_xLegend = xLegend;
98  m_yLegend = yLegend;
99  m_terminalType = terminalType;
100 
101  // Construct the aggregator.
103 }
104 
105 void
106 GnuplotHelper::PlotProbe(const std::string& typeId,
107  const std::string& path,
108  const std::string& probeTraceSource,
109  const std::string& title,
110  enum GnuplotAggregator::KeyLocation keyLocation)
111 {
112  NS_LOG_FUNCTION(this << typeId << path << probeTraceSource << title << keyLocation);
113 
114  // Get a pointer to the aggregator.
115  Ptr<GnuplotAggregator> aggregator = GetAggregator();
116 
117  // Add a subtitle to the title to show the trace source's path.
118  aggregator->SetTitle(m_title + " \\n\\nTrace Source Path: " + path);
119 
120  // Set the default dataset plotting style for the values.
121  aggregator->Set2dDatasetDefaultStyle(Gnuplot2dDataset::LINES_POINTS);
122 
123  // Set the location of the key in the plot.
124  aggregator->SetKeyLocation(keyLocation);
125 
126  std::string pathWithoutLastToken;
127  std::string lastToken;
128 
129  // See if the path has any wildcards.
130  bool pathHasNoWildcards = path.find('*') == std::string::npos;
131 
132  // Remove the last token from the path; this should correspond to the
133  // trace source attribute.
134  size_t lastSlash = path.find_last_of('/');
135  if (lastSlash == std::string::npos)
136  {
137  pathWithoutLastToken = path;
138  lastToken = "";
139  }
140  else
141  {
142  // Chop off up to last token.
143  pathWithoutLastToken = path.substr(0, lastSlash);
144 
145  // Save the last token without the last slash.
146  lastToken = path.substr(lastSlash + 1, std::string::npos);
147  }
148 
149  // See if there are any matches for the probe's path with the last
150  // token removed; this corresponds to the traced object itself.
151  NS_LOG_DEBUG("Searching config database for trace source " << path);
152  Config::MatchContainer matches = Config::LookupMatches(pathWithoutLastToken);
153  uint32_t matchCount = matches.GetN();
154  NS_LOG_DEBUG("Found " << matchCount << " matches for trace source " << path);
155 
156  // This is used to make the probe's context be unique.
157  std::string matchIdentifier;
158 
159  // Hook one or more probes and the aggregator together.
160  if (matchCount == 1 && pathHasNoWildcards)
161  {
162  // Connect the probe to the aggregator only once because there
163  // is only one matching config path. There is no need to find
164  // the wildcard matches because the passed in path has none.
165  matchIdentifier = "0";
166  ConnectProbeToAggregator(typeId, matchIdentifier, path, probeTraceSource, 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, matchedPath, wildcardSeparator);
183 
184  // Connect the probe to the aggregator for this match.
186  matchIdentifier,
187  matchedPath,
188  probeTraceSource,
189  title + "-" + wildcardMatches);
190  }
191  }
192  else
193  {
194  // There is a problem if there are no matching config paths.
195  NS_FATAL_ERROR("Lookup of " << path << " got no matches");
196  }
197 }
198 
199 void
200 GnuplotHelper::AddProbe(const std::string& typeId,
201  const std::string& probeName,
202  const std::string& path)
203 {
204  NS_LOG_FUNCTION(this << typeId << probeName << path);
205 
206  // See if this probe had already been added.
207  if (m_probeMap.count(probeName) > 0)
208  {
209  NS_ABORT_MSG("That probe has already been added");
210  }
211 
212  // Prepare the factory to create an object with the requested type.
213  m_factory.SetTypeId(typeId);
214 
215  // Create a base class object in order to validate the type.
216  Ptr<Probe> probe = m_factory.Create()->GetObject<Probe>();
217  if (!probe)
218  {
219  NS_ABORT_MSG("The requested type is not a probe");
220  }
221 
222  // Set the probe's name.
223  probe->SetName(probeName);
224 
225  // Set the path. Note that no return value is checked here.
226  probe->ConnectByPath(path);
227 
228  // Enable logging of data for the probe.
229  probe->Enable();
230 
231  // Add this probe to the map so that its values can be used.
232  m_probeMap[probeName] = std::make_pair(probe, typeId);
233 }
234 
235 void
236 GnuplotHelper::AddTimeSeriesAdaptor(const std::string& adaptorName)
237 {
238  NS_LOG_FUNCTION(this << adaptorName);
239 
240  // See if this time series adaptor had already been added.
241  if (m_timeSeriesAdaptorMap.count(adaptorName) > 0)
242  {
243  NS_ABORT_MSG("That time series adaptor has already been added");
244  }
245 
246  // Create the time series adaptor.
247  Ptr<TimeSeriesAdaptor> timeSeriesAdaptor = CreateObject<TimeSeriesAdaptor>();
248 
249  // Enable logging of data for the time series adaptor.
250  timeSeriesAdaptor->Enable();
251 
252  // Add this time series adaptor to the map so that can be used.
253  m_timeSeriesAdaptorMap[adaptorName] = timeSeriesAdaptor;
254 }
255 
257 GnuplotHelper::GetProbe(std::string probeName) const
258 {
259  // Look for the probe.
260  std::map<std::string, std::pair<Ptr<Probe>, std::string>>::const_iterator mapIterator =
261  m_probeMap.find(probeName);
262 
263  // Return the probe if it has been added.
264  if (mapIterator != m_probeMap.end())
265  {
266  return mapIterator->second.first;
267  }
268  else
269  {
270  NS_ABORT_MSG("That probe has not been added");
271  }
272 }
273 
276 {
277  NS_LOG_FUNCTION(this);
278 
279  // Do a lazy construction of the aggregator if it hasn't already
280  // been constructed.
281  if (!m_aggregator)
282  {
284  }
285  return m_aggregator;
286 }
287 
288 void
290 {
291  NS_LOG_FUNCTION(this);
292 
293  // Create the aggregator.
294  m_aggregator = CreateObject<GnuplotAggregator>(m_outputFileNameWithoutExtension);
295 
296  // Set the aggregator's properties.
297  m_aggregator->SetTerminal(m_terminalType);
298  m_aggregator->SetTitle(m_title);
299  m_aggregator->SetLegend(m_xLegend, m_yLegend);
300 
301  // Enable logging of data for the aggregator.
302  m_aggregator->Enable();
303 }
304 
305 void
306 GnuplotHelper::ConnectProbeToAggregator(const std::string& typeId,
307  const std::string& matchIdentifier,
308  const std::string& path,
309  const std::string& probeTraceSource,
310  const std::string& title)
311 {
312  NS_LOG_FUNCTION(this << typeId << matchIdentifier << path << probeTraceSource << title);
313 
314  Ptr<GnuplotAggregator> aggregator = GetAggregator();
315 
316  // Increment the total number of plot probes that have been created.
318 
319  // Create a unique name for this probe.
320  std::ostringstream probeNameStream;
321  probeNameStream << "PlotProbe-" << m_plotProbeCount;
322  std::string probeName = probeNameStream.str();
323 
324  // Create a unique dataset context string for this probe.
325  std::string probeContext = probeName + "/" + matchIdentifier + "/" + probeTraceSource;
326 
327  // Add the probe to the map of probes, which will keep the probe in
328  // memory after this function ends.
329  AddProbe(typeId, probeName, path);
330 
331  // Because the callbacks to the probes' trace sources don't use the
332  // probe's context, a unique adaptor needs to be created for each
333  // probe context so that information is not lost.
334  AddTimeSeriesAdaptor(probeContext);
335 
336  // Connect the probe to the adaptor.
337  if (m_probeMap[probeName].second == "ns3::DoubleProbe")
338  {
339  m_probeMap[probeName].first->TraceConnectWithoutContext(
340  probeTraceSource,
342  m_timeSeriesAdaptorMap[probeContext]));
343  }
344  else if (m_probeMap[probeName].second == "ns3::BooleanProbe")
345  {
346  m_probeMap[probeName].first->TraceConnectWithoutContext(
347  probeTraceSource,
349  m_timeSeriesAdaptorMap[probeContext]));
350  }
351  else if (m_probeMap[probeName].second == "ns3::PacketProbe")
352  {
353  m_probeMap[probeName].first->TraceConnectWithoutContext(
354  probeTraceSource,
356  m_timeSeriesAdaptorMap[probeContext]));
357  }
358  else if (m_probeMap[probeName].second == "ns3::ApplicationPacketProbe")
359  {
360  m_probeMap[probeName].first->TraceConnectWithoutContext(
361  probeTraceSource,
363  m_timeSeriesAdaptorMap[probeContext]));
364  }
365  else if (m_probeMap[probeName].second == "ns3::Ipv4PacketProbe")
366  {
367  m_probeMap[probeName].first->TraceConnectWithoutContext(
368  probeTraceSource,
370  m_timeSeriesAdaptorMap[probeContext]));
371  }
372  else if (m_probeMap[probeName].second == "ns3::Ipv6PacketProbe")
373  {
374  m_probeMap[probeName].first->TraceConnectWithoutContext(
375  probeTraceSource,
377  m_timeSeriesAdaptorMap[probeContext]));
378  }
379  else if (m_probeMap[probeName].second == "ns3::Uinteger8Probe")
380  {
381  m_probeMap[probeName].first->TraceConnectWithoutContext(
382  probeTraceSource,
384  m_timeSeriesAdaptorMap[probeContext]));
385  }
386  else if (m_probeMap[probeName].second == "ns3::Uinteger16Probe")
387  {
388  m_probeMap[probeName].first->TraceConnectWithoutContext(
389  probeTraceSource,
391  m_timeSeriesAdaptorMap[probeContext]));
392  }
393  else if (m_probeMap[probeName].second == "ns3::Uinteger32Probe")
394  {
395  m_probeMap[probeName].first->TraceConnectWithoutContext(
396  probeTraceSource,
398  m_timeSeriesAdaptorMap[probeContext]));
399  }
400  else if (m_probeMap[probeName].second == "ns3::TimeProbe")
401  {
402  m_probeMap[probeName].first->TraceConnectWithoutContext(
403  probeTraceSource,
405  m_timeSeriesAdaptorMap[probeContext]));
406  }
407  else
408  {
409  NS_FATAL_ERROR("Unknown probe type " << m_probeMap[probeName].second
410  << "; need to add support in the helper for this");
411  }
412 
413  // Connect the adaptor to the aggregator.
414  std::string adaptorTraceSource = "Output";
415  m_timeSeriesAdaptorMap[probeContext]->TraceConnect(
416  adaptorTraceSource,
417  probeContext,
419 
420  // Add the dataset to the plot.
421  aggregator->Add2dDataset(probeContext, title);
422 }
423 
424 } // namespace ns3
hold a set of objects which match a specific search string.
Definition: config.h:195
std::string GetMatchedPath(uint32_t i) const
Definition: config.cc:89
std::size_t GetN() const
Definition: config.cc:75
void Write2d(std::string context, double x, double y)
Writes a 2D value to a 2D gnuplot dataset.
KeyLocation
The location of the key in the plot.
void ConfigurePlot(const std::string &outputFileNameWithoutExtension, const std::string &title, const std::string &xLegend, const std::string &yLegend, const std::string &terminalType="png")
std::string m_title
Title string to use for this plot.
GnuplotHelper()
Constructs a gnuplot helper that will create a space separated gnuplot data file named "gnuplot-helpe...
void AddTimeSeriesAdaptor(const std::string &adaptorName)
Adds a time series adaptor to be used to make the plot.
Ptr< GnuplotAggregator > m_aggregator
The aggregator used to make the plots.
Ptr< Probe > GetProbe(std::string probeName) const
Gets the specified probe.
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)
std::string m_terminalType
Terminal type for the plot.
std::string m_outputFileNameWithoutExtension
The name of the output file to created without its extension.
uint32_t m_plotProbeCount
Number of plot probes that have been created.
virtual ~GnuplotHelper()
ObjectFactory m_factory
Used to create the probes and collectors as they are added.
std::string m_yLegend
Legend for the y axis.
std::map< std::string, Ptr< TimeSeriesAdaptor > > m_timeSeriesAdaptorMap
Maps time series adaptor names to time series adaptors.
void AddProbe(const std::string &typeId, const std::string &probeName, const std::string &path)
Adds a probe to be used to make the plot.
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.
std::map< std::string, std::pair< Ptr< Probe >, std::string > > m_probeMap
Maps probe names to probes.
void ConstructAggregator()
Constructs the aggregator.
std::string m_xLegend
Legend for the x axis.
Ptr< GnuplotAggregator > GetAggregator()
Gets the aggregator.
Ptr< Object > Create() const
Create an Object instance of the configured TypeId.
void SetTypeId(TypeId tid)
Set the TypeId of the Objects to be created by this factory.
Ptr< T > GetObject() const
Get a pointer to the requested aggregated Object.
Definition: object.h:471
Base class for probes.
Definition: probe.h:40
Smart pointer class similar to boost::intrusive_ptr.
Definition: ptr.h:78
void TraceSinkUinteger8(uint8_t oldData, uint8_t newData)
Trace sink for receiving data from uint8_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.
void TraceSinkUinteger32(uint32_t oldData, uint32_t newData)
Trace sink for receiving data from uint32_t valued trace sources.
void TraceSinkUinteger16(uint16_t oldData, uint16_t newData)
Trace sink for receiving data from uint16_t valued trace sources.
MatchContainer LookupMatches(std::string path)
Definition: config.cc:999
#define NS_FATAL_ERROR(msg)
Report a fatal error with a message and terminate.
Definition: fatal-error.h:179
#define NS_ABORT_MSG(msg)
Unconditional abnormal program termination with a message.
Definition: abort.h:49
#define NS_LOG_COMPONENT_DEFINE(name)
Define a Log component with a specific name.
Definition: log.h:202
#define NS_LOG_DEBUG(msg)
Use NS_LOG to output a message of level LOG_DEBUG.
Definition: log.h:268
#define NS_LOG_FUNCTION(parameters)
If log level LOG_FUNCTION is enabled, this macro will output all input parameters separated by ",...
#define NS_LOG_WARN(msg)
Use NS_LOG to output a message of level LOG_WARN.
Definition: log.h:261
Every class exported by the ns3 library is enclosed in the ns3 namespace.
Callback< R, Args... > MakeCallback(R(T::*memPtr)(Args...), OBJ objPtr)
Build Callbacks for class method members which take varying numbers of arguments and potentially retu...
Definition: callback.h:707
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,...
Definition: second.py:1