doc/manual/attributes.texi
author Tom Henderson <tomh@tomh.org>
Fri Aug 07 16:02:06 2009 -0700 (2009-08-07)
changeset 4700 3d08aef4a581
parent 4699 ba5d6d52be7e
child 4755 04a9a7e9a624
permissions -rw-r--r--
document how to assign attributes in constructor
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