A Discrete-Event Network Simulator
API
Loading...
Searching...
No Matches
gnuplot-helper.cc
Go to the documentation of this file.
1/*
2 * Copyright (c) 2013 University of Washington
3 *
4 * SPDX-License-Identifier: GPL-2.0-only
5 *
6 * Author: Mitch Watrous (watrous@u.washington.edu)
7 */
8
9#include "gnuplot-helper.h"
10
11#include "ns3/abort.h"
12#include "ns3/assert.h"
13#include "ns3/config.h"
14#include "ns3/get-wildcard-matches.h"
15#include "ns3/log.h"
16
17#include <fstream>
18#include <iostream>
19#include <sstream>
20#include <string>
21
22namespace ns3
23{
24
25NS_LOG_COMPONENT_DEFINE("GnuplotHelper");
26
28 : m_aggregator(nullptr),
29 m_plotProbeCount(0),
30 m_outputFileNameWithoutExtension("gnuplot-helper"),
31 m_title("Gnuplot Helper Plot"),
32 m_xLegend("X Values"),
33 m_yLegend("Y Values"),
34 m_terminalType("png")
35{
36 NS_LOG_FUNCTION(this);
37
38 // Note that this does not construct an aggregator. It will be
39 // constructed later when needed.
40}
41
42GnuplotHelper::GnuplotHelper(const std::string& outputFileNameWithoutExtension,
43 const std::string& title,
44 const std::string& xLegend,
45 const std::string& yLegend,
46 const std::string& terminalType)
47 : m_aggregator(nullptr),
48 m_plotProbeCount(0),
49 m_outputFileNameWithoutExtension(outputFileNameWithoutExtension),
50 m_title(title),
51 m_xLegend(xLegend),
52 m_yLegend(yLegend),
53 m_terminalType(terminalType)
54{
55 NS_LOG_FUNCTION(this);
56
57 // Construct the aggregator.
59}
60
65
66void
67GnuplotHelper::ConfigurePlot(const std::string& outputFileNameWithoutExtension,
68 const std::string& title,
69 const std::string& xLegend,
70 const std::string& yLegend,
71 const std::string& terminalType)
72{
73 NS_LOG_FUNCTION(this << outputFileNameWithoutExtension << title << xLegend << yLegend
74 << terminalType);
75
76 // See if an aggregator has already been constructed.
77 if (m_aggregator)
78 {
79 NS_LOG_WARN("An existing aggregator object "
80 << m_aggregator << " may be destroyed if no references remain.");
81 }
82
83 // Store these so that they can be used to construct the aggregator.
84 m_outputFileNameWithoutExtension = outputFileNameWithoutExtension;
85 m_title = title;
86 m_xLegend = xLegend;
87 m_yLegend = yLegend;
88 m_terminalType = terminalType;
89
90 // Construct the aggregator.
92}
93
94void
95GnuplotHelper::PlotProbe(const std::string& typeId,
96 const std::string& path,
97 const std::string& probeTraceSource,
98 const std::string& title,
100{
101 NS_LOG_FUNCTION(this << typeId << path << probeTraceSource << title << keyLocation);
102
103 // Get a pointer to the aggregator.
105
106 // Add a subtitle to the title to show the trace source's path.
107 aggregator->SetTitle(m_title + " \\n\\nTrace Source Path: " + path);
108
109 // Set the default dataset plotting style for the values.
110 aggregator->Set2dDatasetDefaultStyle(Gnuplot2dDataset::LINES_POINTS);
111
112 // Set the location of the key in the plot.
113 aggregator->SetKeyLocation(keyLocation);
114
115 std::string pathWithoutLastToken;
116 std::string lastToken;
117
118 // See if the path has any wildcards.
119 bool pathHasNoWildcards = path.find('*') == std::string::npos;
120
121 // Remove the last token from the path; this should correspond to the
122 // trace source attribute.
123 size_t lastSlash = path.find_last_of('/');
124 if (lastSlash == std::string::npos)
125 {
126 pathWithoutLastToken = path;
127 lastToken = "";
128 }
129 else
130 {
131 // Chop off up to last token.
132 pathWithoutLastToken = path.substr(0, lastSlash);
133
134 // Save the last token without the last slash.
135 lastToken = path.substr(lastSlash + 1, std::string::npos);
136 }
137
138 // See if there are any matches for the probe's path with the last
139 // token removed; this corresponds to the traced object itself.
140 NS_LOG_DEBUG("Searching config database for trace source " << path);
141 Config::MatchContainer matches = Config::LookupMatches(pathWithoutLastToken);
142 uint32_t matchCount = matches.GetN();
143 NS_LOG_DEBUG("Found " << matchCount << " matches for trace source " << path);
144
145 // This is used to make the probe's context be unique.
146 std::string matchIdentifier;
147
148 // Hook one or more probes and the aggregator together.
149 if (matchCount == 1 && pathHasNoWildcards)
150 {
151 // Connect the probe to the aggregator only once because there
152 // is only one matching config path. There is no need to find
153 // the wildcard matches because the passed in path has none.
154 matchIdentifier = "0";
155 ConnectProbeToAggregator(typeId, matchIdentifier, path, probeTraceSource, title);
156 }
157 else if (matchCount > 0)
158 {
159 // Handle all of the matches if there are more than one.
160 for (uint32_t i = 0; i < matchCount; i++)
161 {
162 // Set the match identifier.
163 std::ostringstream matchIdentifierStream;
164 matchIdentifierStream << i;
165 matchIdentifier = matchIdentifierStream.str();
166
167 // Construct the matched path and get the matches for each
168 // of the wildcards.
169 std::string wildcardSeparator = " ";
170 std::string matchedPath = matches.GetMatchedPath(i) + lastToken;
171 std::string wildcardMatches = GetWildcardMatches(path, matchedPath, wildcardSeparator);
172
173 // Connect the probe to the aggregator for this match.
175 matchIdentifier,
176 matchedPath,
177 probeTraceSource,
178 title + "-" + wildcardMatches);
179 }
180 }
181 else
182 {
183 // There is a problem if there are no matching config paths.
184 NS_FATAL_ERROR("Lookup of " << path << " got no matches");
185 }
186}
187
188void
189GnuplotHelper::AddProbe(const std::string& typeId,
190 const std::string& probeName,
191 const std::string& path)
192{
193 NS_LOG_FUNCTION(this << typeId << probeName << path);
194
195 // See if this probe had already been added.
196 if (m_probeMap.count(probeName) > 0)
197 {
198 NS_ABORT_MSG("That probe has already been added");
199 }
200
201 // Prepare the factory to create an object with the requested type.
202 m_factory.SetTypeId(typeId);
203
204 // Create a base class object in order to validate the type.
206 if (!probe)
207 {
208 NS_ABORT_MSG("The requested type is not a probe");
209 }
210
211 // Set the probe's name.
212 probe->SetName(probeName);
213
214 // Set the path. Note that no return value is checked here.
215 probe->ConnectByPath(path);
216
217 // Enable logging of data for the probe.
218 probe->Enable();
219
220 // Add this probe to the map so that its values can be used.
221 m_probeMap[probeName] = std::make_pair(probe, typeId);
222}
223
224void
225GnuplotHelper::AddTimeSeriesAdaptor(const std::string& adaptorName)
226{
227 NS_LOG_FUNCTION(this << adaptorName);
228
229 // See if this time series adaptor had already been added.
230 if (m_timeSeriesAdaptorMap.count(adaptorName) > 0)
231 {
232 NS_ABORT_MSG("That time series adaptor has already been added");
233 }
234
235 // Create the time series adaptor.
237
238 // Enable logging of data for the time series adaptor.
239 timeSeriesAdaptor->Enable();
240
241 // Add this time series adaptor to the map so that can be used.
242 m_timeSeriesAdaptorMap[adaptorName] = timeSeriesAdaptor;
243}
244
246GnuplotHelper::GetProbe(std::string probeName) const
247{
248 // Look for the probe.
249 auto mapIterator = m_probeMap.find(probeName);
250
251 // Return the probe if it has been added.
252 if (mapIterator != m_probeMap.end())
253 {
254 return mapIterator->second.first;
255 }
256 else
257 {
258 NS_ABORT_MSG("That probe has not been added");
259 }
260}
261
264{
265 NS_LOG_FUNCTION(this);
266
267 // Do a lazy construction of the aggregator if it hasn't already
268 // been constructed.
269 if (!m_aggregator)
270 {
272 }
273 return m_aggregator;
274}
275
276void
278{
279 NS_LOG_FUNCTION(this);
280
281 // Create the aggregator.
283
284 // Set the aggregator's properties.
285 m_aggregator->SetTerminal(m_terminalType);
286 m_aggregator->SetTitle(m_title);
287 m_aggregator->SetLegend(m_xLegend, m_yLegend);
288
289 // Enable logging of data for the aggregator.
290 m_aggregator->Enable();
291}
292
293void
295 const std::string& matchIdentifier,
296 const std::string& path,
297 const std::string& probeTraceSource,
298 const std::string& title)
299{
300 NS_LOG_FUNCTION(this << typeId << matchIdentifier << path << probeTraceSource << title);
301
303
304 // Increment the total number of plot probes that have been created.
306
307 // Create a unique name for this probe.
308 std::ostringstream probeNameStream;
309 probeNameStream << "PlotProbe-" << m_plotProbeCount;
310 std::string probeName = probeNameStream.str();
311
312 // Create a unique dataset context string for this probe.
313 std::string probeContext = probeName + "/" + matchIdentifier + "/" + probeTraceSource;
314
315 // Add the probe to the map of probes, which will keep the probe in
316 // memory after this function ends.
317 AddProbe(typeId, probeName, path);
318
319 // Because the callbacks to the probes' trace sources don't use the
320 // probe's context, a unique adaptor needs to be created for each
321 // probe context so that information is not lost.
322 AddTimeSeriesAdaptor(probeContext);
323
324 // Connect the probe to the adaptor.
325 if (m_probeMap[probeName].second == "ns3::DoubleProbe" ||
326 m_probeMap[probeName].second == "ns3::TimeProbe")
327 {
328 m_probeMap[probeName].first->TraceConnectWithoutContext(
329 probeTraceSource,
331 m_timeSeriesAdaptorMap[probeContext]));
332 }
333 else if (m_probeMap[probeName].second == "ns3::BooleanProbe")
334 {
335 m_probeMap[probeName].first->TraceConnectWithoutContext(
336 probeTraceSource,
338 m_timeSeriesAdaptorMap[probeContext]));
339 }
340 else if (m_probeMap[probeName].second == "ns3::Uinteger32Probe" ||
341 m_probeMap[probeName].second == "ns3::PacketProbe" ||
342 m_probeMap[probeName].second == "ns3::ApplicationPacketProbe" ||
343 m_probeMap[probeName].second == "ns3::Ipv4PacketProbe" ||
344 m_probeMap[probeName].second == "ns3::Ipv6PacketProbe")
345 {
346 m_probeMap[probeName].first->TraceConnectWithoutContext(
347 probeTraceSource,
349 m_timeSeriesAdaptorMap[probeContext]));
350 }
351 else if (m_probeMap[probeName].second == "ns3::Uinteger8Probe")
352 {
353 m_probeMap[probeName].first->TraceConnectWithoutContext(
354 probeTraceSource,
356 m_timeSeriesAdaptorMap[probeContext]));
357 }
358 else if (m_probeMap[probeName].second == "ns3::Uinteger16Probe")
359 {
360 m_probeMap[probeName].first->TraceConnectWithoutContext(
361 probeTraceSource,
363 m_timeSeriesAdaptorMap[probeContext]));
364 }
365 else
366 {
367 NS_FATAL_ERROR("Unknown probe type " << m_probeMap[probeName].second
368 << "; need to add support in the helper for this");
369 }
370
371 // Connect the adaptor to the aggregator.
372 std::string adaptorTraceSource = "Output";
373 m_timeSeriesAdaptorMap[probeContext]->TraceConnect(
374 adaptorTraceSource,
375 probeContext,
377
378 // Add the dataset to the plot.
379 aggregator->Add2dDataset(probeContext, title);
380}
381
382} // namespace ns3
hold a set of objects which match a specific search string.
Definition config.h:184
std::string GetMatchedPath(uint32_t i) const
Definition config.cc:82
std::size_t GetN() const
Definition config.cc:68
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.
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.
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 PlotProbe(const std::string &typeId, const std::string &path, const std::string &probeTraceSource, const std::string &title, GnuplotAggregator::KeyLocation keyLocation=GnuplotAggregator::KEY_INSIDE)
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:511
Base class for probes.
Definition probe.h:30
Smart pointer class similar to boost::intrusive_ptr.
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:991
#define NS_FATAL_ERROR(msg)
Report a fatal error with a message and terminate.
#define NS_ABORT_MSG(msg)
Unconditional abnormal program termination with a message.
Definition abort.h:38
#define NS_LOG_COMPONENT_DEFINE(name)
Define a Log component with a specific name.
Definition log.h:191
#define NS_LOG_DEBUG(msg)
Use NS_LOG to output a message of level LOG_DEBUG.
Definition log.h:257
#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:250
Ptr< T > CreateObject(Args &&... args)
Create an object by type, with varying number of constructor parameters.
Definition object.h:619
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:684
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,...