|
tomh@3314
|
1 |
@node Attributes
|
|
tomh@3314
|
2 |
@chapter Attributes
|
|
tomh@3274
|
3 |
@anchor{chap:Attributes}
|
|
tomh@3274
|
4 |
|
|
tomh@3704
|
5 |
@menu
|
|
tomh@4415
|
6 |
* Object Overview::::
|
|
tomh@3704
|
7 |
* Attribute Overview::
|
|
tomh@3704
|
8 |
* Extending attributes::
|
|
tomh@3704
|
9 |
* Adding new class type::
|
|
tomh@3704
|
10 |
* ConfigStore::
|
|
tomh@3704
|
11 |
@end menu
|
|
tomh@3704
|
12 |
|
|
tomh@3704
|
13 |
|
|
tomh@3274
|
14 |
In ns-3 simulations, there are two main aspects to configuration:
|
|
tomh@3274
|
15 |
@itemize @bullet
|
|
tomh@3274
|
16 |
@item the simulation topology and how objects are connected
|
|
tomh@3274
|
17 |
@item the values used by the models instantiated in the topology
|
|
tomh@3274
|
18 |
@end itemize
|
|
tomh@3274
|
19 |
|
|
tomh@3274
|
20 |
This chapter focuses on the second item above: how the many values
|
|
tomh@3274
|
21 |
in use in ns-3 are organized, documented, and modifiable by ns-3 users.
|
|
tomh@3274
|
22 |
The ns-3 attribute system is also the underpinning of how traces
|
|
tomh@3274
|
23 |
and statistics are gathered in the simulator.
|
|
tomh@3274
|
24 |
|
|
tomh@3274
|
25 |
Before delving into details of the attribute value system,
|
|
tomh@3274
|
26 |
it will help to review some basic properties of @code{class ns3::Object}.
|
|
tomh@3274
|
27 |
|
|
tomh@3274
|
28 |
@node Object Overview
|
|
tomh@3274
|
29 |
@section Object Overview
|
|
tomh@3274
|
30 |
|
|
tomh@3274
|
31 |
ns-3 is fundamentally a C++ object-based system. By this we mean that
|
|
tomh@3274
|
32 |
new C++ classes (types) can be declared, defined, and subclassed
|
|
tomh@3274
|
33 |
as usual.
|
|
tomh@3274
|
34 |
|
|
tomh@3274
|
35 |
Many ns-3 objects inherit from the @code{ns3::Object} base class. These
|
|
tomh@3274
|
36 |
objects have some additional properties that we exploit for
|
|
tomh@3274
|
37 |
organizing the system and improving the memory management
|
|
tomh@3274
|
38 |
of our objects:
|
|
tomh@3274
|
39 |
|
|
tomh@3274
|
40 |
@itemize @bullet
|
|
tomh@3274
|
41 |
@item a "metadata" system that links the class name to a lot of
|
|
tomh@3274
|
42 |
meta-information about the object, including the base class of the subclass,
|
|
tomh@3274
|
43 |
the set of accessible constructors in the subclass, and the set of
|
|
tomh@3274
|
44 |
"attributes" of the subclass
|
|
tomh@3274
|
45 |
@item a reference counting smart pointer implementation, for memory
|
|
tomh@3274
|
46 |
management.
|
|
tomh@3274
|
47 |
@end itemize
|
|
tomh@3274
|
48 |
|
|
tomh@3274
|
49 |
ns-3 objects that use the attribute system derive from either
|
|
tomh@3274
|
50 |
@code{ns3::Object} or @code{ns3::ObjectBase}. Most ns-3 objects
|
|
tomh@3274
|
51 |
we will discuss derive from @code{ns3::Object}, but a few that
|
|
tomh@3274
|
52 |
are outside the smart pointer memory management framework derive
|
|
tomh@3274
|
53 |
from @code{ns3::ObjectBase}.
|
|
tomh@3274
|
54 |
|
|
tomh@3274
|
55 |
Let's review a couple of properties of these objects.
|
|
tomh@3274
|
56 |
|
|
tomh@3274
|
57 |
@subsection Smart pointers
|
|
tomh@3274
|
58 |
|
|
tomh@3704
|
59 |
As introduced in the ns-3 tutorial, ns-3 objects are memory managed by a
|
|
tomh@3274
|
60 |
@uref{http://en.wikipedia.org/wiki/Smart_pointer,,reference counting smart pointer implementation}, @code{class ns3::Ptr}.
|
|
tomh@3274
|
61 |
|
|
tomh@3274
|
62 |
Smart pointers are used extensively in the ns-3 APIs, to avoid passing
|
|
tomh@3274
|
63 |
references to heap-allocated objects that may cause memory leaks.
|
|
tomh@3274
|
64 |
For most basic usage (syntax), treat a smart pointer like a regular pointer:
|
|
tomh@3274
|
65 |
@verbatim
|
|
tomh@3274
|
66 |
Ptr<WifiNetDevice> nd = ...;
|
|
tomh@3274
|
67 |
nd->CallSomeFunction ();
|
|
tomh@3274
|
68 |
// etc.
|
|
tomh@3274
|
69 |
@end verbatim
|
|
tomh@3274
|
70 |
|
|
tomh@3274
|
71 |
@subsection CreateObject
|
|
tomh@3274
|
72 |
|
|
tomh@4415
|
73 |
As we discussed above in @ref{Memory management and class Ptr},
|
|
tomh@3274
|
74 |
at the lowest-level API, objects of type @code{ns3::Object} are
|
|
tomh@3274
|
75 |
not instantiated using @code{operator new} as usual but instead by
|
|
tomh@3274
|
76 |
a templated function called @code{CreateObject()}.
|
|
tomh@3274
|
77 |
|
|
tomh@3274
|
78 |
A typical way to create such an object is as follows:
|
|
tomh@3274
|
79 |
@verbatim
|
|
tomh@3274
|
80 |
Ptr<WifiNetDevice> nd = CreateObject<WifiNetDevice> ();
|
|
tomh@3274
|
81 |
@end verbatim
|
|
tomh@3274
|
82 |
|
|
tomh@3274
|
83 |
You can think of this as being functionally equivalent to:
|
|
tomh@3274
|
84 |
@verbatim
|
|
tomh@3274
|
85 |
WifiNetDevice* nd = new WifiNetDevice ();
|
|
tomh@3274
|
86 |
@end verbatim
|
|
tomh@3274
|
87 |
|
|
tomh@3274
|
88 |
Objects that derive from @code{ns3::Object} must be allocated
|
|
tomh@3274
|
89 |
on the heap using CreateObject(). Those deriving from
|
|
tomh@3274
|
90 |
@code{ns3::ObjectBase}, such as ns-3 helper functions and packet
|
|
tomh@3274
|
91 |
headers and trailers, can be allocated on the stack.
|
|
tomh@3274
|
92 |
|
|
tomh@3274
|
93 |
In some scripts, you may not see a lot of CreateObject() calls
|
|
tomh@3274
|
94 |
in the code;
|
|
tomh@3274
|
95 |
this is because there are some helper objects in effect that
|
|
tomh@3274
|
96 |
are doing the CreateObject()s for you.
|
|
tomh@3274
|
97 |
|
|
tomh@3274
|
98 |
@subsection TypeId
|
|
tomh@3274
|
99 |
|
|
tomh@3274
|
100 |
ns-3 classes that derive from class ns3::Object can include
|
|
tomh@3274
|
101 |
a metadata class called @code{TypeId} that records meta-information
|
|
tomh@3274
|
102 |
about the class, for use in the object aggregation and component
|
|
tomh@3274
|
103 |
manager systems:
|
|
tomh@3274
|
104 |
@itemize @bullet
|
|
tomh@3274
|
105 |
@item a unique string identifying the class
|
|
tomh@3274
|
106 |
@item the base class of the subclass, within the metadata system
|
|
tomh@3274
|
107 |
@item the set of accessible constructors in the subclass
|
|
tomh@3274
|
108 |
@end itemize
|
|
tomh@3274
|
109 |
|
|
tomh@3274
|
110 |
@subsection Object Summary
|
|
tomh@3274
|
111 |
|
|
tomh@3274
|
112 |
Putting all of these concepts together, let's look at a specific
|
|
tomh@3274
|
113 |
example: @code{class ns3::Node}.
|
|
tomh@3274
|
114 |
|
|
tomh@3274
|
115 |
The public header file node.h has a declaration that includes
|
|
tomh@3274
|
116 |
a static GetTypeId function call:
|
|
craigdo@4654
|
117 |
|
|
tomh@3274
|
118 |
@verbatim
|
|
tomh@3274
|
119 |
class Node : public Object
|
|
tomh@3274
|
120 |
{
|
|
tomh@3274
|
121 |
public:
|
|
tomh@3274
|
122 |
static TypeId GetTypeId (void);
|
|
tomh@3274
|
123 |
...
|
|
tomh@3274
|
124 |
@end verbatim
|
|
tomh@3274
|
125 |
|
|
tomh@3274
|
126 |
This is defined in the node.cc file as follows:
|
|
craigdo@4654
|
127 |
|
|
tomh@3274
|
128 |
@verbatim
|
|
tomh@3274
|
129 |
TypeId
|
|
tomh@3274
|
130 |
Node::GetTypeId (void)
|
|
tomh@3274
|
131 |
{
|
|
tomh@3274
|
132 |
static TypeId tid = TypeId ("ns3::Node")
|
|
tomh@3274
|
133 |
.SetParent<Object> ()
|
|
craigdo@4654
|
134 |
.AddConstructor<Node> ()
|
|
craigdo@4654
|
135 |
.AddAttribute ("DeviceList", "The list of devices associated to this Node.",
|
|
craigdo@4654
|
136 |
ObjectVectorValue (),
|
|
craigdo@4654
|
137 |
MakeObjectVectorAccessor (&Node::m_devices),
|
|
craigdo@4654
|
138 |
MakeObjectVectorChecker<NetDevice> ())
|
|
craigdo@4654
|
139 |
.AddAttribute ("ApplicationList", "The list of applications associated to this Node.",
|
|
craigdo@4654
|
140 |
ObjectVectorValue (),
|
|
craigdo@4654
|
141 |
MakeObjectVectorAccessor (&Node::m_applications),
|
|
craigdo@4654
|
142 |
MakeObjectVectorChecker<Application> ())
|
|
craigdo@4654
|
143 |
.AddAttribute ("Id", "The id (unique integer) of this Node.",
|
|
craigdo@4654
|
144 |
TypeId::ATTR_GET, // allow only getting it.
|
|
craigdo@4654
|
145 |
UintegerValue (0),
|
|
craigdo@4654
|
146 |
MakeUintegerAccessor (&Node::m_id),
|
|
craigdo@4654
|
147 |
MakeUintegerChecker<uint32_t> ())
|
|
mathieu@3319
|
148 |
;
|
|
tomh@3274
|
149 |
return tid;
|
|
tomh@3274
|
150 |
}
|
|
tomh@3274
|
151 |
@end verbatim
|
|
craigdo@4654
|
152 |
|
|
craigdo@4654
|
153 |
Look at the TypeId of an ns-3 @code{Object} class as an extended form of run
|
|
craigdo@4654
|
154 |
time type information (RTTI). The C++ language includes simple kind of RTTI
|
|
craigdo@4654
|
155 |
in order to support @code{dynamic_cast} and @code{typeid} operators.
|
|
craigdo@4654
|
156 |
|
|
craigdo@4654
|
157 |
The ``@code{.SetParent<Object> ()}'' call in the declaration above is used in
|
|
craigdo@4654
|
158 |
conjunction with our object aggregation mechanisms to allow safe up- and
|
|
craigdo@4654
|
159 |
down-casing in inheritance trees during @code{GetObject}.
|
|
craigdo@4654
|
160 |
|
|
craigdo@4654
|
161 |
The ``@code{.AddConstructor<Node> ()}'' call is used in conjunction with our
|
|
craigdo@4654
|
162 |
abstract object factory mechanisms to allow us to construct C++ objects without
|
|
craigdo@4654
|
163 |
forcing a user to know the concrete class of the object she is building.
|
|
craigdo@4654
|
164 |
|
|
craigdo@4654
|
165 |
The three calls to ``@code{.AddAttribute}'' associate a given string with a
|
|
craigdo@4654
|
166 |
strongly typed value in the class. Notice that you must provide a help string
|
|
craigdo@4654
|
167 |
which may be displayed, for example, via command line processors. Each
|
|
craigdo@4654
|
168 |
@code{Attribute} is associated with mechanisms for accessing the underlying
|
|
craigdo@4654
|
169 |
member variable in the object (for example, @code{MakeUintegerAccessor} tells
|
|
craigdo@4654
|
170 |
the generic @code{Attribute} code how to get to the node ID above). There are
|
|
craigdo@4654
|
171 |
also ``Checker'' methods which are used to validate values.
|
|
craigdo@4654
|
172 |
|
|
craigdo@4654
|
173 |
When users want to create Nodes, they will usually call some form of
|
|
craigdo@4654
|
174 |
@code{CreateObject},
|
|
craigdo@4654
|
175 |
|
|
tomh@3274
|
176 |
@verbatim
|
|
mathieu@3319
|
177 |
Ptr<Node> n = CreateObject<Node> ();
|
|
tomh@3274
|
178 |
@end verbatim
|
|
tomh@3274
|
179 |
|
|
craigdo@4654
|
180 |
or more abstractly, using an object factory, you can create a @code{Node} object
|
|
craigdo@4654
|
181 |
without even knowing the concrete C++ type
|
|
craigdo@4654
|
182 |
|
|
craigdo@4654
|
183 |
@verbatim
|
|
craigdo@4654
|
184 |
ObjectFactory factory;
|
|
craigdo@4654
|
185 |
const std::string typeId = "ns3::Node'';
|
|
craigdo@4654
|
186 |
factory.SetTypeId(typeId);
|
|
craigdo@4654
|
187 |
Ptr<Object> node = factory.Create <Object> ();
|
|
craigdo@4654
|
188 |
@end verbatim
|
|
craigdo@4654
|
189 |
|
|
craigdo@4654
|
190 |
Both of these methods result in fully initialized attributes being available
|
|
craigdo@4654
|
191 |
in the resulting @code{Object} instances.
|
|
craigdo@4654
|
192 |
|
|
tomh@3274
|
193 |
We next discuss how attributes (values associated with member variables
|
|
tomh@3274
|
194 |
or functions of the class) are plumbed into the above TypeId.
|
|
tomh@3274
|
195 |
|
|
tomh@3274
|
196 |
@node Attribute Overview
|
|
tomh@3274
|
197 |
@section Attribute Overview
|
|
tomh@3274
|
198 |
|
|
tomh@3274
|
199 |
The goal of the attribute system is to organize the access of
|
|
tomh@3274
|
200 |
internal member objects of a simulation. This goal arises because,
|
|
tomh@3274
|
201 |
typically in simulation, users will cut and paste/modify existing
|
|
tomh@3274
|
202 |
simulation scripts, or will use higher-level simulation constructs,
|
|
tomh@3274
|
203 |
but often will be interested in studying or tracing particular
|
|
tomh@3274
|
204 |
internal variables. For instance, use cases such as:
|
|
tomh@3274
|
205 |
@itemize @bullet
|
|
tomh@3274
|
206 |
@item "I want to trace the packets on the wireless interface only on
|
|
tomh@3274
|
207 |
the first access point"
|
|
tomh@3274
|
208 |
@item "I want to trace the value of the TCP congestion window (every
|
|
tomh@3274
|
209 |
time it changes) on a particular TCP socket"
|
|
tomh@3274
|
210 |
@item "I want a dump of all values that were used in my simulation."
|
|
tomh@3274
|
211 |
@end itemize
|
|
tomh@3274
|
212 |
|
|
tomh@3274
|
213 |
Similarly, users may want fine-grained access to internal
|
|
tomh@3274
|
214 |
variables in the simulation, or may want to broadly change the
|
|
tomh@3274
|
215 |
initial value used for a particular parameter in all subsequently
|
|
tomh@3274
|
216 |
created objects. Finally, users may wish to know what variables
|
|
tomh@3274
|
217 |
are settable and retrievable in a simulation configuration. This
|
|
tomh@3274
|
218 |
is not just for direct simulation interaction on the command line;
|
|
tomh@3274
|
219 |
consider also a (future) graphical user interface
|
|
tomh@3274
|
220 |
that would like to be able to provide a feature whereby a user
|
|
tomh@3274
|
221 |
might right-click on an node on the canvas and see a hierarchical,
|
|
tomh@3274
|
222 |
organized list of parameters that are settable on the node and its
|
|
tomh@3274
|
223 |
constituent member objects, and help text and default values for
|
|
tomh@3274
|
224 |
each parameter.
|
|
tomh@3274
|
225 |
|
|
tomh@3274
|
226 |
@subsection Functional overview
|
|
tomh@3274
|
227 |
|
|
tomh@3274
|
228 |
We provide a way for users to access values deep in the system, without
|
|
tomh@3274
|
229 |
having to plumb accessors (pointers) through the system and walk
|
|
tomh@3274
|
230 |
pointer chains to get to them. Consider a class DropTailQueue that
|
|
tomh@3274
|
231 |
has a member variable that is an unsigned integer @code{m_maxPackets};
|
|
tomh@3274
|
232 |
this member variable controls the depth of the queue.
|
|
tomh@3274
|
233 |
|
|
tomh@3274
|
234 |
If we look at the declaration of DropTailQueue, we see the following:
|
|
tomh@3274
|
235 |
@verbatim
|
|
tomh@3274
|
236 |
class DropTailQueue : public Queue {
|
|
tomh@3274
|
237 |
public:
|
|
tomh@3274
|
238 |
static TypeId GetTypeId (void);
|
|
tomh@3274
|
239 |
...
|
|
tomh@3274
|
240 |
|
|
tomh@3274
|
241 |
private:
|
|
tomh@3274
|
242 |
std::queue<Ptr<Packet> > m_packets;
|
|
tomh@3274
|
243 |
uint32_t m_maxPackets;
|
|
tomh@3274
|
244 |
};
|
|
tomh@3274
|
245 |
@end verbatim
|
|
tomh@3274
|
246 |
|
|
tomh@3274
|
247 |
Let's consider things that a user may want to do with the value of
|
|
tomh@3274
|
248 |
m_maxPackets:
|
|
tomh@3274
|
249 |
|
|
tomh@3274
|
250 |
@itemize @bullet
|
|
tomh@3274
|
251 |
@item Set a default value for the system, such that whenever a new
|
|
tomh@3274
|
252 |
DropTailQueue is created, this member is initialized to that default.
|
|
tomh@3274
|
253 |
@item Set or get the value on an already instantiated queue.
|
|
tomh@3274
|
254 |
@end itemize
|
|
tomh@3274
|
255 |
|
|
tomh@3274
|
256 |
The above things typically require providing Set() and Get() functions,
|
|
tomh@3274
|
257 |
and some type of global default value.
|
|
tomh@3274
|
258 |
|
|
tomh@3274
|
259 |
In the ns-3 attribute system, these value definitions and accessor
|
|
tomh@3274
|
260 |
functions are moved into the TypeId class; e.g.:
|
|
craigdo@4654
|
261 |
|
|
tomh@3274
|
262 |
@verbatim
|
|
tomh@4478
|
263 |
NS_OBJECT_ENSURE_REGISTERED (DropTailQueue);
|
|
tomh@4478
|
264 |
|
|
tomh@3274
|
265 |
TypeId DropTailQueue::GetTypeId (void)
|
|
tomh@3274
|
266 |
{
|
|
tomh@3274
|
267 |
static TypeId tid = TypeId ("ns3::DropTailQueue")
|
|
tomh@3274
|
268 |
.SetParent<Queue> ()
|
|
tomh@3274
|
269 |
.AddConstructor<DropTailQueue> ()
|
|
mathieu@3319
|
270 |
.AddAttribute ("MaxPackets",
|
|
mathieu@3319
|
271 |
"The maximum number of packets accepted by this DropTailQueue.",
|
|
mathieu@3319
|
272 |
UintegerValue (100),
|
|
tomh@3274
|
273 |
MakeUintegerAccessor (&DropTailQueue::m_maxPackets),
|
|
tomh@3274
|
274 |
MakeUintegerChecker<uint32_t> ())
|
|
tomh@3274
|
275 |
;
|
|
tomh@3274
|
276 |
|
|
tomh@3274
|
277 |
return tid;
|
|
tomh@3274
|
278 |
}
|
|
tomh@3274
|
279 |
@end verbatim
|
|
tomh@3274
|
280 |
|
|
tomh@3274
|
281 |
The AddAttribute() method is performing a number of things with this
|
|
tomh@3274
|
282 |
value:
|
|
tomh@3274
|
283 |
@itemize @bullet
|
|
tomh@3274
|
284 |
@item Binding the variable m_maxPackets to a string "MaxPackets"
|
|
tomh@3274
|
285 |
@item Providing a default value (100 packets)
|
|
tomh@3274
|
286 |
@item Providing some help text defining the value
|
|
tomh@3274
|
287 |
@item Providing a "checker" (not used in this example) that can be used to set
|
|
tomh@3274
|
288 |
bounds on the allowable range of values
|
|
tomh@3274
|
289 |
@end itemize
|
|
tomh@3274
|
290 |
|
|
tomh@3274
|
291 |
The key point is that now the value of this variable and its default
|
|
tomh@3274
|
292 |
value are accessible in the attribute namespace, which is based on
|
|
tomh@3274
|
293 |
strings such as "MaxPackets" and TypeId strings. In the next
|
|
tomh@3274
|
294 |
section, we will provide an example script that shows how users
|
|
tomh@3274
|
295 |
may manipulate these values.
|
|
tomh@3274
|
296 |
|
|
tomh@4478
|
297 |
Note that initialization of the attribute relies on the macro
|
|
tomh@4478
|
298 |
NS_OBJECT_ENSURE_REGISTERED (DropTailQueue) being called; if you leave
|
|
tomh@4478
|
299 |
this out of your new class implementation, your attributes will not be
|
|
tomh@4478
|
300 |
initialized corretly.
|
|
tomh@4478
|
301 |
|
|
tomh@3274
|
302 |
@subsection Basic usage
|
|
tomh@3274
|
303 |
|
|
tomh@3274
|
304 |
Let's look at how a user script might access these values.
|
|
tomh@3274
|
305 |
This is based on the script found at @code{samples/main-attribute-value.cc},
|
|
tomh@3274
|
306 |
with some details stripped out.
|
|
tomh@3274
|
307 |
@verbatim
|
|
tomh@3274
|
308 |
//
|
|
tomh@3274
|
309 |
// This is a basic example of how to use the attribute system to
|
|
tomh@3274
|
310 |
// set and get a value in the underlying system; namely, an unsigned
|
|
tomh@3274
|
311 |
// integer of the maximum number of packets in a queue
|
|
tomh@3274
|
312 |
//
|
|
tomh@3274
|
313 |
|
|
tomh@3274
|
314 |
int
|
|
tomh@3274
|
315 |
main (int argc, char *argv[])
|
|
tomh@3274
|
316 |
{
|
|
tomh@3274
|
317 |
|
|
tomh@3274
|
318 |
// By default, the MaxPackets attribute has a value of 100 packets
|
|
tomh@3274
|
319 |
// (this default can be observed in the function DropTailQueue::GetTypeId)
|
|
tomh@3274
|
320 |
//
|
|
tomh@3274
|
321 |
// Here, we set it to 80 packets. We could use one of two value types:
|
|
tomh@3274
|
322 |
// a string-based value or a Uinteger value
|
|
mathieu@3319
|
323 |
Config::SetDefault ("ns3::DropTailQueue::MaxPackets", StringValue ("80"));
|
|
tomh@3274
|
324 |
// The below function call is redundant
|
|
mathieu@3319
|
325 |
Config::SetDefault ("ns3::DropTailQueue::MaxPackets", UintegerValue (80));
|
|
tomh@3274
|
326 |
|
|
tomh@3274
|
327 |
// Allow the user to override any of the defaults and the above
|
|
tomh@3274
|
328 |
// SetDefaults() at run-time, via command-line arguments
|
|
tomh@3274
|
329 |
CommandLine cmd;
|
|
tomh@3274
|
330 |
cmd.Parse (argc, argv);
|
|
tomh@3274
|
331 |
@end verbatim
|
|
tomh@3274
|
332 |
|
|
tomh@3274
|
333 |
The main thing to notice in the above are the two calls to
|
|
tomh@3274
|
334 |
@code{Config::SetDefault}. This is how we set the default value
|
|
tomh@3274
|
335 |
for all subsequently instantiated DropTailQueues. We illustrate
|
|
mathieu@3320
|
336 |
that two types of Value classes, a StringValue and a UintegerValue class,
|
|
tomh@3274
|
337 |
can be used to assign the value to the attribute named by
|
|
tomh@3274
|
338 |
"ns3::DropTailQueue::MaxPackets".
|
|
tomh@3274
|
339 |
|
|
tomh@3274
|
340 |
Now, we will create a few objects using the low-level API; here,
|
|
tomh@3274
|
341 |
our newly created queues will not have a m_maxPackets initialized to
|
|
tomh@3274
|
342 |
100 packets but to 80 packets, because of what we did above with
|
|
tomh@3274
|
343 |
default values.
|
|
tomh@3274
|
344 |
@verbatim
|
|
tomh@3274
|
345 |
Ptr<Node> n0 = CreateObject<Node> ();
|
|
tomh@3274
|
346 |
|
|
tomh@3274
|
347 |
Ptr<PointToPointNetDevice> net0 = CreateObject<PointToPointNetDevice> ();
|
|
tomh@3274
|
348 |
n0->AddDevice (net0);
|
|
tomh@3274
|
349 |
|
|
tomh@3274
|
350 |
Ptr<Queue> q = CreateObject<DropTailQueue> ();
|
|
tomh@3274
|
351 |
net0->AddQueue(q);
|
|
tomh@3274
|
352 |
@end verbatim
|
|
tomh@3274
|
353 |
|
|
tomh@3274
|
354 |
At this point, we have created a single node (Node 0) and a
|
|
tomh@3274
|
355 |
single PointToPointNetDevice (NetDevice 0) and added a
|
|
tomh@3274
|
356 |
DropTailQueue to it.
|
|
tomh@3274
|
357 |
|
|
tomh@3274
|
358 |
Now, we can manipulate the MaxPackets value of the already
|
|
tomh@3274
|
359 |
instantiated DropTailQueue. Here are various ways to do that.
|
|
tomh@3274
|
360 |
|
|
tomh@3274
|
361 |
@subsubsection Pointer-based access
|
|
tomh@3274
|
362 |
|
|
tomh@3274
|
363 |
We assume that a smart pointer (Ptr) to a relevant network device is
|
|
tomh@3274
|
364 |
in hand; here, it is the net0 pointer.
|
|
tomh@3274
|
365 |
|
|
tomh@3274
|
366 |
One way to change the value is to access a pointer to the
|
|
tomh@3274
|
367 |
underlying queue and modify its attribute.
|
|
tomh@3274
|
368 |
|
|
tomh@3274
|
369 |
First, we observe that we can get a pointer to the (base class)
|
|
tomh@3274
|
370 |
queue via the PointToPointNetDevice attributes, where it is called
|
|
tomh@3274
|
371 |
TxQueue
|
|
tomh@3274
|
372 |
@verbatim
|
|
mathieu@3319
|
373 |
PointerValue tmp;
|
|
mathieu@3319
|
374 |
net0->GetAttribute ("TxQueue", tmp);
|
|
mathieu@3319
|
375 |
Ptr<Object> txQueue = tmp.GetObject ();
|
|
tomh@3274
|
376 |
@end verbatim
|
|
tomh@3274
|
377 |
|
|
tomh@3274
|
378 |
Using the GetObject function, we can perform a safe downcast
|
|
tomh@3274
|
379 |
to a DropTailQueue, where MaxPackets is a member
|
|
tomh@3274
|
380 |
@verbatim
|
|
tomh@3274
|
381 |
Ptr<DropTailQueue> dtq = txQueue->GetObject <DropTailQueue> ();
|
|
mathieu@3319
|
382 |
NS_ASSERT (dtq != 0);
|
|
tomh@3274
|
383 |
@end verbatim
|
|
tomh@3274
|
384 |
|
|
mathieu@3319
|
385 |
Next, we can get the value of an attribute on this queue.
|
|
tomh@3274
|
386 |
We have introduced wrapper "Value" classes for the underlying
|
|
tomh@3274
|
387 |
data types, similar to Java wrappers around these types, since
|
|
tomh@3274
|
388 |
the attribute system stores values and not disparate types.
|
|
mathieu@3319
|
389 |
Here, the attribute value is assigned to a UintegerValue, and
|
|
tomh@3274
|
390 |
the Get() method on this value produces the (unwrapped) uint32_t.
|
|
tomh@3274
|
391 |
@verbatim
|
|
mathieu@3319
|
392 |
UintegerValue limit;
|
|
mathieu@3319
|
393 |
dtq->GetAttribute ("MaxPackets", limit);
|
|
tomh@3274
|
394 |
NS_LOG_INFO ("1. dtq limit: " << limit.Get () << " packets");
|
|
tomh@3274
|
395 |
@end verbatim
|
|
tomh@3274
|
396 |
|
|
tomh@3274
|
397 |
Note that the above downcast is not really needed; we could have
|
|
tomh@3274
|
398 |
done the same using the Ptr<Queue> even though the attribute
|
|
tomh@3274
|
399 |
is a member of the subclass
|
|
tomh@3274
|
400 |
@verbatim
|
|
mathieu@3319
|
401 |
txQueue->GetAttribute ("MaxPackets", limit);
|
|
tomh@3274
|
402 |
NS_LOG_INFO ("2. txQueue limit: " << limit.Get () << " packets");
|
|
tomh@3274
|
403 |
@end verbatim
|
|
tomh@3274
|
404 |
|
|
tomh@3274
|
405 |
Now, let's set it to another value (60 packets)
|
|
tomh@3274
|
406 |
@verbatim
|
|
mathieu@3319
|
407 |
txQueue->SetAttribute("MaxPackets", UintegerValue (60));
|
|
mathieu@3319
|
408 |
txQueue->GetAttribute ("MaxPackets", limit);
|
|
tomh@3274
|
409 |
NS_LOG_INFO ("3. txQueue limit changed: " << limit.Get () << " packets");
|
|
tomh@3274
|
410 |
@end verbatim
|
|
tomh@3274
|
411 |
|
|
tomh@3274
|
412 |
@subsubsection Namespace-based access
|
|
tomh@3274
|
413 |
|
|
craigdo@4654
|
414 |
An alternative way to get at the attribute is to use the configuration namespace.
|
|
craigdo@4654
|
415 |
Here, this attribute resides on a known path in this namespace; this approach
|
|
craigdo@4654
|
416 |
is useful if one doesn't have access to the underlying pointers and would like
|
|
craigdo@4654
|
417 |
to configure a specific attribute with a single statement.
|
|
craigdo@4654
|
418 |
|
|
tomh@3274
|
419 |
@verbatim
|
|
mathieu@3319
|
420 |
Config::Set ("/NodeList/0/DeviceList/0/TxQueue/MaxPackets", UintegerValue (25));
|
|
mathieu@3319
|
421 |
txQueue->GetAttribute ("MaxPackets", limit);
|
|
tomh@3274
|
422 |
NS_LOG_INFO ("4. txQueue limit changed through namespace: " <<
|
|
tomh@3274
|
423 |
limit.Get () << " packets");
|
|
tomh@3274
|
424 |
@end verbatim
|
|
tomh@3274
|
425 |
|
|
craigdo@4654
|
426 |
We could have also used wildcards to set this value for all nodes and all net
|
|
craigdo@4654
|
427 |
devices (which in this simple example has the same effect as the previous Set())
|
|
tomh@3274
|
428 |
@verbatim
|
|
mathieu@3319
|
429 |
Config::Set ("/NodeList/*/DeviceList/*/TxQueue/MaxPackets", UintegerValue (15));
|
|
mathieu@3319
|
430 |
txQueue->GetAttribute ("MaxPackets", limit);
|
|
tomh@3274
|
431 |
NS_LOG_INFO ("5. txQueue limit changed through wildcarded namespace: " <<
|
|
tomh@3274
|
432 |
limit.Get () << " packets");
|
|
tomh@3274
|
433 |
@end verbatim
|
|
tomh@3274
|
434 |
|
|
craigdo@4654
|
435 |
@subsubsection Object Name Service-based access
|
|
craigdo@4654
|
436 |
|
|
craigdo@4654
|
437 |
Another way to get at the attribute is to use the object name service facility.
|
|
craigdo@4654
|
438 |
Here, this attribute is found using a name string. This approach is useful if
|
|
craigdo@4654
|
439 |
one doesn't have access to the underlying pointers and it is difficult to
|
|
craigdo@4654
|
440 |
determine the required concrete configuration namespaced path.
|
|
craigdo@4654
|
441 |
|
|
craigdo@4654
|
442 |
@verbatim
|
|
craigdo@4654
|
443 |
Names::Add ("server", serverNode);
|
|
craigdo@4654
|
444 |
Names::Add ("server/eth0", serverDevice);
|
|
craigdo@4654
|
445 |
|
|
craigdo@4654
|
446 |
...
|
|
craigdo@4654
|
447 |
|
|
craigdo@4654
|
448 |
Config::Set ("/Names/server/eth0/TxQueue/MaxPackets", UintegerValue (25));
|
|
craigdo@4654
|
449 |
@end verbatim
|
|
craigdo@4654
|
450 |
|
|
tomh@3274
|
451 |
@subsection Setting through constructors helper classes
|
|
tomh@3274
|
452 |
|
|
tomh@3274
|
453 |
Arbitrary combinations of attributes can be set and fetched from
|
|
tomh@3274
|
454 |
the helper and low-level APIs; either from the constructors themselves:
|
|
tomh@3274
|
455 |
@verbatim
|
|
tomh@3274
|
456 |
Ptr<Object> p = CreateObject<MyNewObject> ("n1", v1, "n2", v2, ...);
|
|
tomh@3274
|
457 |
@end verbatim
|
|
tomh@3274
|
458 |
or from the higher-level helper APIs, such as:
|
|
tomh@3274
|
459 |
@verbatim
|
|
tomh@3274
|
460 |
mobility.SetPositionAllocator ("GridPositionAllocator",
|
|
mathieu@3319
|
461 |
"MinX", DoubleValue (-100.0),
|
|
mathieu@3319
|
462 |
"MinY", DoubleValue (-100.0),
|
|
mathieu@3319
|
463 |
"DeltaX", DoubleValue (5.0),
|
|
mathieu@3319
|
464 |
"DeltaY", DoubleValue (20.0),
|
|
mathieu@3319
|
465 |
"GridWidth", UintegerValue (20),
|
|
mathieu@3319
|
466 |
"LayoutType", StringValue ("RowFirst"));
|
|
tomh@3274
|
467 |
@end verbatim
|
|
tomh@3274
|
468 |
|
|
tomh@3274
|
469 |
@subsection Value classes
|
|
mathieu@3320
|
470 |
Readers will note the new FooValue classes which are subclasses of the
|
|
mathieu@3320
|
471 |
AttributeValue base class. These can be thought of as
|
|
tomh@3274
|
472 |
an intermediate class that can be used to convert from raw types to the
|
|
mathieu@3320
|
473 |
Values that are used by the attribute system. Recall that this database is holding
|
|
tomh@3274
|
474 |
objects of many types with a single generic type. Conversions to this
|
|
mathieu@3319
|
475 |
type can either be done using an intermediate class (IntegerValue, DoubleValue for
|
|
tomh@3274
|
476 |
"floating point") or via strings. Direct implicit conversion of types
|
|
tomh@3274
|
477 |
to Value is not really practical. So in the above, users have a choice
|
|
tomh@3274
|
478 |
of using strings or values:
|
|
tomh@3274
|
479 |
@verbatim
|
|
mathieu@3319
|
480 |
p->Set ("cwnd", StringValue ("100")); // string-based setter
|
|
mathieu@3319
|
481 |
p->Set ("cwnd", IntegerValue (100)); // integer-based setter
|
|
tomh@3274
|
482 |
@end verbatim
|
|
tomh@3274
|
483 |
|
|
tomh@3274
|
484 |
The system provides some macros that help users declare and define
|
|
mathieu@3320
|
485 |
new AttributeValue subclasses for new types that they want to introduce into
|
|
mathieu@3320
|
486 |
the attribute system:
|
|
mathieu@3320
|
487 |
@itemize @bullet
|
|
mathieu@3320
|
488 |
@item ATTRIBUTE_HELPER_HEADER
|
|
mathieu@3320
|
489 |
@item ATTRIBUTE_HELPER_CPP
|
|
mathieu@3320
|
490 |
@end itemize
|
|
tomh@3274
|
491 |
|
|
tomh@4700
|
492 |
@subsection Initialization order
|
|
tomh@4700
|
493 |
|
|
tomh@4700
|
494 |
In general, the attribute code to assign values to the underlying
|
|
tomh@4700
|
495 |
class member variables is executed after an object is constructed.
|
|
tomh@4700
|
496 |
But what if you need the values assigned before the constructor
|
|
tomh@4700
|
497 |
body executes, because you need them in the logic of the constructor?
|
|
tomh@4700
|
498 |
There is a way to do this, used for example in the class
|
|
tomh@4700
|
499 |
@code{ns3::ConfigStore}: call @code{ObjectBase::ConstructSelf()}
|
|
tomh@4700
|
500 |
as follows:
|
|
tomh@4700
|
501 |
|
|
tomh@4700
|
502 |
@verbatim
|
|
tomh@4700
|
503 |
ConfigStore::ConfigStore ()
|
|
tomh@4700
|
504 |
{
|
|
tomh@4700
|
505 |
ObjectBase::ConstructSelf (AttributeList ());
|
|
tomh@4700
|
506 |
// continue on with constructor.
|
|
tomh@4700
|
507 |
}
|
|
tomh@4700
|
508 |
@end verbatim
|
|
tomh@4700
|
509 |
|
|
tomh@3274
|
510 |
@node Extending attributes
|
|
tomh@3274
|
511 |
@section Extending attributes
|
|
tomh@3274
|
512 |
|
|
tomh@3274
|
513 |
The ns-3 system will place a number of internal values under the
|
|
tomh@3274
|
514 |
attribute system, but undoubtedly users will want to extend this
|
|
tomh@3274
|
515 |
to pick up ones we have missed, or to add their own classes to this.
|
|
tomh@3274
|
516 |
|
|
tomh@3274
|
517 |
@subsection Adding an existing internal variable to the metadata system
|
|
tomh@3274
|
518 |
|
|
tomh@3274
|
519 |
Consider this variable in class TcpSocket:
|
|
tomh@3274
|
520 |
@verbatim
|
|
tomh@3274
|
521 |
uint32_t m_cWnd; // Congestion window
|
|
tomh@3274
|
522 |
@end verbatim
|
|
tomh@3274
|
523 |
|
|
tomh@3274
|
524 |
Suppose that someone working with Tcp wanted to get or set the
|
|
tomh@3274
|
525 |
value of that variable using the metadata system. If it were not
|
|
tomh@3274
|
526 |
already provided by ns-3, the user could declare the following addition
|
|
craigdo@4654
|
527 |
in the runtime metadata system (to the TypeId declaration for TcpSocket):
|
|
tomh@3274
|
528 |
@verbatim
|
|
craigdo@4654
|
529 |
.AddAttribute ("Congestion window",
|
|
tomh@3274
|
530 |
"Tcp congestion window (bytes)",
|
|
craigdo@4654
|
531 |
UintegerValue (1),
|
|
mathieu@3320
|
532 |
MakeUintegerAccessor (&TcpSocket::m_cWnd),
|
|
craigdo@4654
|
533 |
MakeUintegerChecker<uint16_t> ())
|
|
tomh@3274
|
534 |
|
|
tomh@3274
|
535 |
@end verbatim
|
|
tomh@3274
|
536 |
|
|
tomh@3274
|
537 |
Now, the user with a pointer to the TcpSocket can perform operations
|
|
tomh@3274
|
538 |
such as setting and getting the value, without having to add these
|
|
tomh@3274
|
539 |
functions explicitly. Furthermore, access controls can be applied, such
|
|
tomh@3274
|
540 |
as allowing the parameter to be read and not written, or bounds
|
|
tomh@3274
|
541 |
checking on the permissible values can be applied.
|
|
tomh@3274
|
542 |
|
|
tomh@3274
|
543 |
@subsection Adding a new TypeId
|
|
tomh@3274
|
544 |
|
|
tomh@3274
|
545 |
Here, we discuss the impact on a user who wants to add a new class to
|
|
tomh@3274
|
546 |
ns-3; what additional things must be done to hook it into this system.
|
|
tomh@3274
|
547 |
|
|
tomh@3274
|
548 |
We've already introduced what a TypeId definition looks like:
|
|
tomh@3274
|
549 |
@verbatim
|
|
tomh@3274
|
550 |
TypeId
|
|
tomh@3274
|
551 |
RandomWalk2dMobilityModel::GetTypeId (void)
|
|
mathieu@3320
|
552 |
{
|
|
mathieu@3320
|
553 |
static TypeId tid = TypeId ("ns3::RandomWalk2dMobilityModel")
|
|
mathieu@3320
|
554 |
.SetParent<MobilityModel> ()
|
|
mathieu@3320
|
555 |
.SetGroupName ("Mobility")
|
|
tomh@3274
|
556 |
.AddConstructor<RandomWalk2dMobilityModel> ()
|
|
mathieu@3320
|
557 |
.AddAttribute ("Bounds",
|
|
mathieu@3320
|
558 |
"Bounds of the area to cruise.",
|
|
mathieu@3320
|
559 |
RectangleValue (Rectangle (0.0, 0.0, 100.0, 100.0)),
|
|
mathieu@3320
|
560 |
MakeRectangleAccessor (&RandomWalk2dMobilityModel::m_bounds),
|
|
mathieu@3320
|
561 |
MakeRectangleChecker ())
|
|
mathieu@3320
|
562 |
.AddAttribute ("Time",
|
|
mathieu@3320
|
563 |
"Change current direction and speed after moving for this delay.",
|
|
mathieu@3320
|
564 |
TimeValue (Seconds (1.0)),
|
|
mathieu@3320
|
565 |
MakeTimeAccessor (&RandomWalk2dMobilityModel::m_modeTime),
|
|
mathieu@3320
|
566 |
MakeTimeChecker ())
|
|
tomh@3274
|
567 |
// etc (more parameters).
|
|
mathieu@3320
|
568 |
;
|
|
mathieu@3320
|
569 |
return tid;
|
|
mathieu@3320
|
570 |
}
|
|
tomh@3274
|
571 |
@end verbatim
|
|
tomh@3274
|
572 |
|
|
tomh@3274
|
573 |
The declaration for this in the class declaration is one-line public
|
|
tomh@3274
|
574 |
member method:
|
|
tomh@3274
|
575 |
@verbatim
|
|
tomh@3274
|
576 |
public:
|
|
tomh@3274
|
577 |
static TypeId GetTypeId (void);
|
|
tomh@3274
|
578 |
@end verbatim
|
|
tomh@3274
|
579 |
|
|
mathieu@3320
|
580 |
Typical mistakes here involve:
|
|
mathieu@3320
|
581 |
@itemize @bullet
|
|
mathieu@3320
|
582 |
@item Not calling the SetParent method or calling it with the wrong type
|
|
mathieu@3320
|
583 |
@item Not calling the AddConstructor method of calling it with the wrong type
|
|
mathieu@3320
|
584 |
@item Introducing a typographical error in the name of the TypeId in its constructor
|
|
mathieu@3320
|
585 |
@item Not using the fully-qualified c++ typename of the enclosing c++ class as the
|
|
mathieu@3320
|
586 |
name of the TypeId
|
|
mathieu@3320
|
587 |
@end itemize
|
|
mathieu@3320
|
588 |
None of these mistakes can be detected by the ns-3 codebase so, users
|
|
mathieu@3320
|
589 |
are advised to check carefully multiple times that they got these right.
|
|
mathieu@3320
|
590 |
|
|
mathieu@3320
|
591 |
|
|
tomh@3704
|
592 |
@node Adding new class type
|
|
mathieu@3320
|
593 |
@section Adding new class type to the attribute system
|
|
tomh@3274
|
594 |
|
|
tomh@3274
|
595 |
From the perspective of the user who writes a new class in the system and
|
|
tomh@3274
|
596 |
wants to hook it in to the attribute system, there is mainly the matter
|
|
tomh@3274
|
597 |
of writing
|
|
mathieu@3320
|
598 |
the conversions to/from strings and attribute values. Most of this can be
|
|
tomh@3274
|
599 |
copy/pasted with macro-ized code. For instance, consider class
|
|
craigdo@4654
|
600 |
delcaration for Rectangle in the @code{src/mobility/} directory:
|
|
tomh@3274
|
601 |
|
|
tomh@3274
|
602 |
@verbatim
|
|
tomh@3274
|
603 |
/**
|
|
tomh@3274
|
604 |
* \brief a 2d rectangle
|
|
tomh@3274
|
605 |
*/
|
|
tomh@3274
|
606 |
class Rectangle
|
|
tomh@3274
|
607 |
{
|
|
craigdo@4654
|
608 |
...
|
|
tomh@3274
|
609 |
|
|
craigdo@4654
|
610 |
double xMin;
|
|
craigdo@4654
|
611 |
double xMax;
|
|
craigdo@4654
|
612 |
double yMin;
|
|
craigdo@4654
|
613 |
double yMax;
|
|
tomh@3274
|
614 |
};
|
|
tomh@3274
|
615 |
@end verbatim
|
|
tomh@3274
|
616 |
|
|
craigdo@4654
|
617 |
One macro call and two operators, must be added below the class declaration
|
|
craigdo@4654
|
618 |
in order to turn a Rectangle into a value usable by the @code{Attribute}
|
|
craigdo@4654
|
619 |
system:
|
|
tomh@3274
|
620 |
|
|
tomh@3274
|
621 |
@verbatim
|
|
tomh@3274
|
622 |
std::ostream &operator << (std::ostream &os, const Rectangle &rectangle);
|
|
tomh@3274
|
623 |
std::istream &operator >> (std::istream &is, Rectangle &rectangle);
|
|
tomh@3274
|
624 |
|
|
mathieu@3320
|
625 |
ATTRIBUTE_HELPER_HEADER (Rectangle);
|
|
tomh@3274
|
626 |
@end verbatim
|
|
tomh@3274
|
627 |
|
|
craigdo@4654
|
628 |
In the class definition (@code{.cc} file), the code looks like this:
|
|
tomh@3274
|
629 |
|
|
tomh@3274
|
630 |
@verbatim
|
|
mathieu@3320
|
631 |
ATTRIBUTE_HELPER_CPP (Rectangle);
|
|
tomh@3274
|
632 |
|
|
tomh@3274
|
633 |
std::ostream &
|
|
tomh@3274
|
634 |
operator << (std::ostream &os, const Rectangle &rectangle)
|
|
tomh@3274
|
635 |
{
|
|
tomh@3274
|
636 |
os << rectangle.xMin << "|" << rectangle.xMax << "|" << rectangle.yMin << "|" << rectangle.yMax;
|
|
tomh@3274
|
637 |
return os;
|
|
tomh@3274
|
638 |
}
|
|
tomh@3274
|
639 |
std::istream &
|
|
tomh@3274
|
640 |
operator >> (std::istream &is, Rectangle &rectangle)
|
|
tomh@3274
|
641 |
{
|
|
tomh@3274
|
642 |
char c1, c2, c3;
|
|
tomh@3274
|
643 |
is >> rectangle.xMin >> c1 >> rectangle.xMax >> c2 >> rectangle.yMin >> c3 >> rectangle.yMax;
|
|
tomh@3274
|
644 |
if (c1 != '|' ||
|
|
tomh@3274
|
645 |
c2 != '|' ||
|
|
tomh@3274
|
646 |
c3 != '|')
|
|
tomh@3274
|
647 |
{
|
|
tomh@3274
|
648 |
is.setstate (std::ios_base::failbit);
|
|
tomh@3274
|
649 |
}
|
|
tomh@3274
|
650 |
return is;
|
|
tomh@3274
|
651 |
}
|
|
tomh@3274
|
652 |
@end verbatim
|
|
tomh@3274
|
653 |
|
|
tomh@3274
|
654 |
These stream operators simply convert from a string representation of the
|
|
tomh@3274
|
655 |
Rectangle ("xMin|xMax|yMin|yMax") to the underlying Rectangle, and the
|
|
tomh@3274
|
656 |
modeler must specify these operators and the string syntactical representation
|
|
tomh@3274
|
657 |
of an instance of the new class.
|
|
tomh@3274
|
658 |
|
|
tomh@3704
|
659 |
@node ConfigStore
|
|
tomh@3704
|
660 |
@section ConfigStore
|
|
tomh@3704
|
661 |
|
|
craigdo@4654
|
662 |
@strong{Feedback requested:} This is an experimental feature of ns-3. It is
|
|
craigdo@4654
|
663 |
found in @code{src/contrib} and not in the main tree. If you like this feature
|
|
craigdo@4654
|
664 |
and would like to provide feedback on it, please email us.
|
|
tomh@3704
|
665 |
|
|
tomh@4699
|
666 |
Values for ns-3 attributes can be stored in an ascii or XML text file and
|
|
tomh@3704
|
667 |
loaded into a future simulation. This feature is known as the
|
|
tomh@3704
|
668 |
ns-3 ConfigStore.
|
|
tomh@3704
|
669 |
The ConfigStore code is in @code{src/contrib/}. It is not yet main-tree
|
|
tomh@4699
|
670 |
code, because we are seeking some user feedback and experience with this.
|
|
tomh@3704
|
671 |
|
|
tomh@3704
|
672 |
We can explore this system by using an example. Copy the @code{csma-bridge.cc}
|
|
tomh@3704
|
673 |
file to the scratch directory:
|
|
tomh@3704
|
674 |
@verbatim
|
|
tomh@3704
|
675 |
cp examples/csma-bridge.cc scratch/
|
|
tomh@3704
|
676 |
./waf
|
|
tomh@3704
|
677 |
@end verbatim
|
|
tomh@3704
|
678 |
|
|
tomh@4699
|
679 |
Let's edit it to add the ConfigStore feature. First, add an include statement
|
|
tomh@4699
|
680 |
to include the contrib module, and then add these lines:
|
|
tomh@3704
|
681 |
|
|
tomh@3704
|
682 |
@verbatim
|
|
tomh@3704
|
683 |
#include "contrib-module.h"
|
|
tomh@3704
|
684 |
...
|
|
tomh@3704
|
685 |
int main (...)
|
|
tomh@3704
|
686 |
{
|
|
tomh@3704
|
687 |
// setup topology
|
|
tomh@3704
|
688 |
|
|
tomh@3704
|
689 |
// Invoke just before entering Simulator::Run ()
|
|
tomh@3704
|
690 |
ConfigStore config;
|
|
tomh@4699
|
691 |
config.ConfigureDefaults ();
|
|
tomh@4699
|
692 |
config.ConfigureAttributes ();
|
|
tomh@3704
|
693 |
|
|
tomh@3704
|
694 |
Simulator::Run ();
|
|
tomh@3704
|
695 |
}
|
|
tomh@3704
|
696 |
@end verbatim
|
|
tomh@3704
|
697 |
|
|
tomh@4699
|
698 |
There are three attributes that govern the behavior of the ConfigStore:
|
|
tomh@4699
|
699 |
"Mode", "Filename", and "FileFormat". The Mode (default "None") configures
|
|
tomh@4699
|
700 |
whether ns-3 should load configuration from a previously saved file
|
|
tomh@4699
|
701 |
(specify "Mode=Load") or save it to a file (specify "Mode=Save").
|
|
tomh@4699
|
702 |
The Filename (default "") is where the ConfigStore should store its
|
|
tomh@4699
|
703 |
output data. The FileFormat (default "RawText") governs whether
|
|
tomh@4699
|
704 |
the ConfigStore format is Xml or RawText format.
|
|
tomh@4699
|
705 |
|
|
tomh@4699
|
706 |
So, using the above modified program, try executing the following
|
|
tomh@4699
|
707 |
waf command and
|
|
tomh@4699
|
708 |
@verbatim
|
|
tomh@4699
|
709 |
./waf --command-template="%s --ns3::ConfigStore::Filename=csma-bridge-config.xml --ns3::ConfigStore::Mode=Save --ns3::ConfigStore::FileFormat=Xml" --run scratch/csma-bridge
|
|
tomh@4699
|
710 |
@end verbatim
|
|
tomh@4699
|
711 |
After running, you can open the csma-bridge-config.xml file and it will
|
|
tomh@4699
|
712 |
display the configuration that was applied to your simulation; e.g.
|
|
tomh@4699
|
713 |
@verbatim
|
|
tomh@4699
|
714 |
<?xml version="1.0" encoding="UTF-8"?>
|
|
tomh@4699
|
715 |
<ns3>
|
|
tomh@4699
|
716 |
<default name="ns3::V4Ping::Remote" value="102.102.102.102"/>
|
|
tomh@4699
|
717 |
<default name="ns3::MsduStandardAggregator::MaxAmsduSize" value="7935"/>
|
|
tomh@4699
|
718 |
<default name="ns3::EdcaTxopN::MinCw" value="31"/>
|
|
tomh@4699
|
719 |
<default name="ns3::EdcaTxopN::MaxCw" value="1023"/>
|
|
tomh@4699
|
720 |
<default name="ns3::EdcaTxopN::Aifsn" value="3"/>
|
|
tomh@4699
|
721 |
<default name="ns3::QstaWifiMac::ProbeRequestTimeout" value="50000000ns"/>
|
|
tomh@4699
|
722 |
<default name="ns3::QstaWifiMac::AssocRequestTimeout" value="500000000ns"/>
|
|
tomh@4699
|
723 |
<default name="ns3::QstaWifiMac::MaxMissedBeacons" value="10"/>
|
|
tomh@4699
|
724 |
<default name="ns3::QstaWifiMac::ActiveProbing" value="false"/>
|
|
tomh@4699
|
725 |
...
|
|
tomh@4699
|
726 |
@end verbatim
|
|
tomh@4699
|
727 |
This file can be archived with your simulation script and output data.
|
|
tomh@3704
|
728 |
|
|
tomh@3704
|
729 |
While it is possible to generate a sample config file and lightly
|
|
tomh@3704
|
730 |
edit it to change a couple of values, there are cases where this
|
|
tomh@3704
|
731 |
process will not work because the same value on the same object
|
|
tomh@3704
|
732 |
can appear multiple times in the same automatically-generated
|
|
tomh@3704
|
733 |
configuration file under different configuration paths.
|
|
tomh@3704
|
734 |
|
|
tomh@3704
|
735 |
As such, the best way to use this class is to use it to generate
|
|
tomh@3704
|
736 |
an initial configuration file, extract from that configuration
|
|
tomh@3704
|
737 |
file only the strictly necessary elements, and move these minimal
|
|
tomh@3704
|
738 |
elements to a new configuration file which can then safely
|
|
tomh@3704
|
739 |
be edited and loaded in a subsequent simulation run.
|
|
tomh@3704
|
740 |
|
|
tomh@4699
|
741 |
When the ConfigStore object is instantiated, its attributes Filename,
|
|
tomh@4699
|
742 |
Mode, and FileFormat must be set, either via command-line or via
|
|
tomh@4699
|
743 |
program statements.
|
|
tomh@4699
|
744 |
|
|
tomh@4699
|
745 |
As a more complicated example, let's assume that we want to
|
|
tomh@4699
|
746 |
read in a configuration of defaults from an input file named
|
|
tomh@4699
|
747 |
"input-defaults.xml", and write out the resulting attributes to a
|
|
tomh@4699
|
748 |
separate file called "output-attributes.xml". (Note-- to get this
|
|
tomh@4699
|
749 |
input xml file to begin with, it is sometimes helpful to run the
|
|
tomh@4699
|
750 |
program to generate an output xml file first, then hand-edit that
|
|
tomh@4699
|
751 |
file and re-input it for the next simulation run).
|
|
tomh@3704
|
752 |
@verbatim
|
|
tomh@4699
|
753 |
#include "contrib-module.h"
|
|
tomh@4699
|
754 |
...
|
|
tomh@4699
|
755 |
int main (...)
|
|
tomh@4699
|
756 |
{
|
|
tomh@3704
|
757 |
|
|
tomh@4699
|
758 |
Config::SetDefault ("ns3::ConfigStore::Filename", StringValue ("input-defaults.xml"));
|
|
tomh@4699
|
759 |
Config::SetDefault ("ns3::ConfigStore::Mode", StringValue ("Load"));
|
|
tomh@4699
|
760 |
Config::SetDefault ("ns3::ConfigStore::FileFormat", StringValue ("Xml"));
|
|
tomh@4699
|
761 |
ConfigStore inputConfig;
|
|
tomh@4699
|
762 |
inputConfig.ConfigureDefaults ();
|
|
tomh@4699
|
763 |
|
|
tomh@4699
|
764 |
//
|
|
tomh@4699
|
765 |
// Allow the user to override any of the defaults and the above Bind() at
|
|
tomh@4699
|
766 |
// run-time, via command-line arguments
|
|
tomh@4699
|
767 |
//
|
|
tomh@4699
|
768 |
CommandLine cmd;
|
|
tomh@4699
|
769 |
cmd.Parse (argc, argv);
|
|
tomh@3704
|
770 |
|
|
tomh@4699
|
771 |
// setup topology
|
|
tomh@4699
|
772 |
...
|
|
tomh@3704
|
773 |
|
|
tomh@4699
|
774 |
// Invoke just before entering Simulator::Run ()
|
|
tomh@4699
|
775 |
Config::SetDefault ("ns3::ConfigStore::Filename", StringValue ("output-attributes.xml"));
|
|
tomh@4699
|
776 |
Config::SetDefault ("ns3::ConfigStore::Mode", StringValue ("Save"));
|
|
tomh@4699
|
777 |
ConfigStore outputConfig;
|
|
tomh@4699
|
778 |
outputConfig.ConfigureAttributes ();
|
|
tomh@4699
|
779 |
Simulator::Run ();
|
|
tomh@4699
|
780 |
}
|
|
tomh@3704
|
781 |
@end verbatim
|
|
tomh@3704
|
782 |
|
|
tomh@3704
|
783 |
@subsection GTK-based ConfigStore
|
|
tomh@3704
|
784 |
|
|
tomh@3704
|
785 |
There is a GTK-based front end for the ConfigStore. This allows users
|
|
tomh@3704
|
786 |
to use a GUI to access and change variables. Screenshots of this
|
|
tomh@3704
|
787 |
feature are available in the
|
|
tomh@3704
|
788 |
@uref{http://www.nsnam.org/docs/ns-3-overview.pdf,,ns-3 Overview} presentation.
|
|
tomh@3704
|
789 |
|
|
tomh@3704
|
790 |
To use this feature, one must install libgtk and libgtk-dev; an example
|
|
tomh@3704
|
791 |
Ubuntu installation command is:
|
|
tomh@3704
|
792 |
@verbatim
|
|
tomh@3704
|
793 |
sudo apt-get install libgtk2.0-0 libgtk2.0-dev
|
|
tomh@3704
|
794 |
@end verbatim
|
|
tomh@3704
|
795 |
To check whether it is configured or not, check the output of the
|
|
tomh@3704
|
796 |
./waf configure step:
|
|
tomh@3704
|
797 |
@verbatim
|
|
tomh@3704
|
798 |
---- Summary of optional NS-3 features:
|
|
tomh@3704
|
799 |
Threading Primitives : enabled
|
|
tomh@3704
|
800 |
Real Time Simulator : enabled
|
|
tomh@3704
|
801 |
GtkConfigStore : not enabled (library 'gtk+-2.0 >= 2.12' not found)
|
|
tomh@3704
|
802 |
@end verbatim
|
|
tomh@3704
|
803 |
|
|
tomh@3704
|
804 |
In the above example, it was not enabled, so it cannot be used until a
|
|
tomh@3704
|
805 |
suitable version is installed and ./waf configure; ./waf is rerun.
|
|
tomh@3704
|
806 |
|
|
tomh@4699
|
807 |
Usage is almost the same as the non-GTK-based version, but there
|
|
tomh@4699
|
808 |
are no ConfigStore attributes involved:
|
|
tomh@3704
|
809 |
@verbatim
|
|
tomh@3704
|
810 |
// Invoke just before entering Simulator::Run ()
|
|
tomh@3704
|
811 |
GtkConfigStore config;
|
|
tomh@4699
|
812 |
config.ConfigureDefaults ();
|
|
tomh@4699
|
813 |
config.ConfigureAttributes ();
|
|
tomh@3704
|
814 |
@end verbatim
|
|
tomh@3704
|
815 |
|
|
tomh@3704
|
816 |
Now, when you run the script, a GUI should pop up, allowing you to open
|
|
tomh@3704
|
817 |
menus of attributes on different nodes/objects, and then launch the
|
|
tomh@3704
|
818 |
simulation execution when you are done.
|
|
tomh@3704
|
819 |
|
|
tomh@3704
|
820 |
@subsection Future work
|
|
tomh@3704
|
821 |
There are a couple of possible improvements:
|
|
tomh@4020
|
822 |
@itemize @bullet
|
|
tomh@3704
|
823 |
@item save a unique version number with date and time at start of file
|
|
tomh@3704
|
824 |
@item save rng initial seed somewhere.
|
|
tomh@3704
|
825 |
@item make each RandomVariable serialize its own initial seed and re-read
|
|
tomh@3704
|
826 |
it later
|
|
tomh@3704
|
827 |
@item add the default values
|
|
tomh@3704
|
828 |
@end itemize
|
|
tomh@3704
|
829 |
|