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
33namespace ns3
34{
35
36NS_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
53GnuplotHelper::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
77void
78GnuplotHelper::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
105void
106GnuplotHelper::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.
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
199void
200GnuplotHelper::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.
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
235void
236GnuplotHelper::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
257GnuplotHelper::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
288void
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
305void
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
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:160
#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:691
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