doc/manual/attributes.texi
author Tom Henderson <tomh@tomh.org>
Fri Aug 07 15:34:45 2009 -0700 (2009-08-07)
changeset 4699 ba5d6d52be7e
parent 4654 f292d41cb943
child 4700 3d08aef4a581
permissions -rw-r--r--
fix manual for ConfigStore
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@3274
   492
@node Extending attributes
tomh@3274
   493
@section Extending attributes
tomh@3274
   494
tomh@3274
   495
The ns-3 system will place a number of internal values under the
tomh@3274
   496
attribute system, but undoubtedly users will want to extend this
tomh@3274
   497
to pick up ones we have missed, or to add their own classes to this.
tomh@3274
   498
tomh@3274
   499
@subsection Adding an existing internal variable to the metadata system 
tomh@3274
   500
tomh@3274
   501
Consider this variable in class TcpSocket:
tomh@3274
   502
@verbatim
tomh@3274
   503
 uint32_t m_cWnd;   // Congestion window
tomh@3274
   504
@end verbatim
tomh@3274
   505
tomh@3274
   506
Suppose that someone working with Tcp wanted to get or set the 
tomh@3274
   507
value of that variable using the metadata system.  If it were not
tomh@3274
   508
already provided by ns-3, the user could declare the following addition 
craigdo@4654
   509
in the runtime metadata system (to the TypeId declaration for TcpSocket):
tomh@3274
   510
@verbatim
craigdo@4654
   511
    .AddAttribute ("Congestion window", 
tomh@3274
   512
                   "Tcp congestion window (bytes)",
craigdo@4654
   513
                   UintegerValue (1),
mathieu@3320
   514
                   MakeUintegerAccessor (&TcpSocket::m_cWnd),
craigdo@4654
   515
                   MakeUintegerChecker<uint16_t> ())
tomh@3274
   516
tomh@3274
   517
@end verbatim
tomh@3274
   518
tomh@3274
   519
Now, the user with a pointer to the TcpSocket can perform operations
tomh@3274
   520
such as setting and getting the value, without having to add these
tomh@3274
   521
functions explicitly.  Furthermore, access controls can be applied, such
tomh@3274
   522
as allowing the parameter to be read and not written, or bounds
tomh@3274
   523
checking on the permissible values can be applied.
tomh@3274
   524
tomh@3274
   525
@subsection Adding a new TypeId
tomh@3274
   526
tomh@3274
   527
Here, we discuss the impact on a user who wants to add a new class to
tomh@3274
   528
ns-3; what additional things must be done to hook it into this system.
tomh@3274
   529
tomh@3274
   530
We've already introduced what a TypeId definition looks like:
tomh@3274
   531
@verbatim
tomh@3274
   532
TypeId
tomh@3274
   533
RandomWalk2dMobilityModel::GetTypeId (void)
mathieu@3320
   534
{
mathieu@3320
   535
  static TypeId tid = TypeId ("ns3::RandomWalk2dMobilityModel")
mathieu@3320
   536
    .SetParent<MobilityModel> ()
mathieu@3320
   537
    .SetGroupName ("Mobility")
tomh@3274
   538
    .AddConstructor<RandomWalk2dMobilityModel> ()
mathieu@3320
   539
    .AddAttribute ("Bounds",
mathieu@3320
   540
                   "Bounds of the area to cruise.",
mathieu@3320
   541
                   RectangleValue (Rectangle (0.0, 0.0, 100.0, 100.0)),
mathieu@3320
   542
                   MakeRectangleAccessor (&RandomWalk2dMobilityModel::m_bounds),
mathieu@3320
   543
                   MakeRectangleChecker ())
mathieu@3320
   544
    .AddAttribute ("Time",
mathieu@3320
   545
                   "Change current direction and speed after moving for this delay.",
mathieu@3320
   546
                   TimeValue (Seconds (1.0)),
mathieu@3320
   547
                   MakeTimeAccessor (&RandomWalk2dMobilityModel::m_modeTime),
mathieu@3320
   548
                   MakeTimeChecker ())
tomh@3274
   549
    // etc (more parameters).
mathieu@3320
   550
    ;
mathieu@3320
   551
  return tid;
mathieu@3320
   552
}
tomh@3274
   553
@end verbatim
tomh@3274
   554
tomh@3274
   555
The declaration for this in the class declaration is one-line public
tomh@3274
   556
member method:
tomh@3274
   557
@verbatim
tomh@3274
   558
 public:
tomh@3274
   559
  static TypeId GetTypeId (void);
tomh@3274
   560
@end verbatim
tomh@3274
   561
mathieu@3320
   562
Typical mistakes here involve:
mathieu@3320
   563
@itemize @bullet
mathieu@3320
   564
@item Not calling the SetParent method or calling it with the wrong type
mathieu@3320
   565
@item Not calling the AddConstructor method of calling it with the wrong type
mathieu@3320
   566
@item Introducing a typographical error in the name of the TypeId in its constructor
mathieu@3320
   567
@item Not using the fully-qualified c++ typename of the enclosing c++ class as the 
mathieu@3320
   568
name of the TypeId
mathieu@3320
   569
@end itemize
mathieu@3320
   570
None of these mistakes can be detected by the ns-3 codebase so, users
mathieu@3320
   571
are advised to check carefully multiple times that they got these right.
mathieu@3320
   572
mathieu@3320
   573
tomh@3704
   574
@node Adding new class type
mathieu@3320
   575
@section Adding new class type to the attribute system
tomh@3274
   576
tomh@3274
   577
From the perspective of the user who writes a new class in the system and
tomh@3274
   578
wants to hook it in to the attribute system, there is mainly the matter 
tomh@3274
   579
of writing 
mathieu@3320
   580
the conversions to/from strings and attribute values.  Most of this can be
tomh@3274
   581
copy/pasted with macro-ized code.  For instance, consider class
craigdo@4654
   582
delcaration for Rectangle in the @code{src/mobility/} directory:
tomh@3274
   583
tomh@3274
   584
@verbatim
tomh@3274
   585
/**
tomh@3274
   586
 * \brief a 2d rectangle
tomh@3274
   587
 */
tomh@3274
   588
class Rectangle
tomh@3274
   589
{
craigdo@4654
   590
  ...
tomh@3274
   591
craigdo@4654
   592
  double xMin;
craigdo@4654
   593
  double xMax;
craigdo@4654
   594
  double yMin;
craigdo@4654
   595
  double yMax;
tomh@3274
   596
};
tomh@3274
   597
@end verbatim
tomh@3274
   598
 
craigdo@4654
   599
One macro call and two operators, must be added below the class declaration
craigdo@4654
   600
in order to turn a Rectangle into a value usable by the @code{Attribute}
craigdo@4654
   601
system:
tomh@3274
   602
tomh@3274
   603
@verbatim
tomh@3274
   604
std::ostream &operator << (std::ostream &os, const Rectangle &rectangle);
tomh@3274
   605
std::istream &operator >> (std::istream &is, Rectangle &rectangle);
tomh@3274
   606
mathieu@3320
   607
ATTRIBUTE_HELPER_HEADER (Rectangle);
tomh@3274
   608
@end verbatim
tomh@3274
   609
craigdo@4654
   610
In the class definition (@code{.cc} file), the code looks like this:
tomh@3274
   611
tomh@3274
   612
@verbatim
mathieu@3320
   613
ATTRIBUTE_HELPER_CPP (Rectangle);
tomh@3274
   614
tomh@3274
   615
std::ostream &
tomh@3274
   616
operator << (std::ostream &os, const Rectangle &rectangle)
tomh@3274
   617
{
tomh@3274
   618
  os << rectangle.xMin << "|" << rectangle.xMax << "|" << rectangle.yMin << "|" << rectangle.yMax;
tomh@3274
   619
  return os;
tomh@3274
   620
}
tomh@3274
   621
std::istream &
tomh@3274
   622
operator >> (std::istream &is, Rectangle &rectangle)
tomh@3274
   623
 {
tomh@3274
   624
  char c1, c2, c3;
tomh@3274
   625
  is >> rectangle.xMin >> c1 >> rectangle.xMax >> c2 >> rectangle.yMin >> c3 >> rectangle.yMax;
tomh@3274
   626
  if (c1 != '|' ||
tomh@3274
   627
      c2 != '|' ||
tomh@3274
   628
      c3 != '|')
tomh@3274
   629
    {
tomh@3274
   630
      is.setstate (std::ios_base::failbit);
tomh@3274
   631
    }
tomh@3274
   632
  return is;
tomh@3274
   633
}
tomh@3274
   634
@end verbatim
tomh@3274
   635
tomh@3274
   636
These stream operators simply convert from a string representation of the
tomh@3274
   637
Rectangle ("xMin|xMax|yMin|yMax") to the underlying Rectangle, and the
tomh@3274
   638
modeler must specify these operators and the string syntactical representation 
tomh@3274
   639
of an instance of the new class.
tomh@3274
   640
tomh@3704
   641
@node ConfigStore
tomh@3704
   642
@section ConfigStore
tomh@3704
   643
craigdo@4654
   644
@strong{Feedback requested:}  This is an experimental feature of ns-3.  It is 
craigdo@4654
   645
found in @code{src/contrib} and not in the main tree.  If you like this feature
craigdo@4654
   646
and would like to provide feedback on it, please email us.
tomh@3704
   647
tomh@4699
   648
Values for ns-3 attributes can be stored in an ascii or XML text file and
tomh@3704
   649
loaded into a future simulation.  This feature is known as the
tomh@3704
   650
ns-3 ConfigStore.  
tomh@3704
   651
The ConfigStore code is in @code{src/contrib/}.  It is not yet main-tree
tomh@4699
   652
code, because we are seeking some user feedback and experience with this. 
tomh@3704
   653
tomh@3704
   654
We can explore this system by using an example.  Copy the @code{csma-bridge.cc}
tomh@3704
   655
file to the scratch directory:
tomh@3704
   656
@verbatim
tomh@3704
   657
  cp examples/csma-bridge.cc scratch/
tomh@3704
   658
  ./waf
tomh@3704
   659
@end verbatim
tomh@3704
   660
tomh@4699
   661
Let's edit it to add the ConfigStore feature.  First, add an include statement 
tomh@4699
   662
to include the contrib module, and then add these lines:
tomh@3704
   663
tomh@3704
   664
@verbatim
tomh@3704
   665
#include "contrib-module.h"
tomh@3704
   666
...
tomh@3704
   667
int main (...)
tomh@3704
   668
{
tomh@3704
   669
  // setup topology
tomh@3704
   670
tomh@3704
   671
  // Invoke just before entering Simulator::Run ()
tomh@3704
   672
  ConfigStore config;
tomh@4699
   673
  config.ConfigureDefaults ();
tomh@4699
   674
  config.ConfigureAttributes ();
tomh@3704
   675
tomh@3704
   676
  Simulator::Run ();
tomh@3704
   677
}
tomh@3704
   678
@end verbatim
tomh@3704
   679
tomh@4699
   680
There are three attributes that govern the behavior of the ConfigStore:
tomh@4699
   681
"Mode", "Filename", and "FileFormat".  The Mode (default "None") configures
tomh@4699
   682
whether ns-3 should load configuration from a previously saved file
tomh@4699
   683
(specify "Mode=Load") or save it to a file (specify "Mode=Save").
tomh@4699
   684
The Filename (default "") is where the ConfigStore should store its
tomh@4699
   685
output data.  The FileFormat (default "RawText") governs whether
tomh@4699
   686
the ConfigStore format is Xml or RawText format.
tomh@4699
   687
tomh@4699
   688
So, using the above modified program, try executing the following
tomh@4699
   689
waf command and 
tomh@4699
   690
@verbatim
tomh@4699
   691
./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
   692
@end verbatim
tomh@4699
   693
After running, you can open the csma-bridge-config.xml file and it will
tomh@4699
   694
display the configuration that was applied to your simulation; e.g.
tomh@4699
   695
@verbatim
tomh@4699
   696
<?xml version="1.0" encoding="UTF-8"?>
tomh@4699
   697
<ns3>
tomh@4699
   698
 <default name="ns3::V4Ping::Remote" value="102.102.102.102"/>
tomh@4699
   699
 <default name="ns3::MsduStandardAggregator::MaxAmsduSize" value="7935"/>
tomh@4699
   700
 <default name="ns3::EdcaTxopN::MinCw" value="31"/>
tomh@4699
   701
 <default name="ns3::EdcaTxopN::MaxCw" value="1023"/>
tomh@4699
   702
 <default name="ns3::EdcaTxopN::Aifsn" value="3"/>
tomh@4699
   703
 <default name="ns3::QstaWifiMac::ProbeRequestTimeout" value="50000000ns"/>
tomh@4699
   704
 <default name="ns3::QstaWifiMac::AssocRequestTimeout" value="500000000ns"/>
tomh@4699
   705
 <default name="ns3::QstaWifiMac::MaxMissedBeacons" value="10"/>
tomh@4699
   706
 <default name="ns3::QstaWifiMac::ActiveProbing" value="false"/>
tomh@4699
   707
...
tomh@4699
   708
@end verbatim
tomh@4699
   709
This file can be archived with your simulation script and output data.
tomh@3704
   710
tomh@3704
   711
While it is possible to generate a sample config file and lightly
tomh@3704
   712
edit it to change a couple of values, there are cases where this
tomh@3704
   713
process will not work because the same value on the same object
tomh@3704
   714
can appear multiple times in the same automatically-generated 
tomh@3704
   715
configuration file under different configuration paths.
tomh@3704
   716
tomh@3704
   717
As such, the best way to use this class is to use it to generate
tomh@3704
   718
an initial configuration file, extract from that configuration
tomh@3704
   719
file only the strictly necessary elements, and move these minimal
tomh@3704
   720
elements to a new configuration file which can then safely
tomh@3704
   721
be edited and loaded in a subsequent simulation run. 
tomh@3704
   722
tomh@4699
   723
When the ConfigStore object is instantiated, its attributes Filename,
tomh@4699
   724
Mode, and FileFormat must be set, either via command-line or via
tomh@4699
   725
program statements.  
tomh@4699
   726
tomh@4699
   727
As a more complicated example, let's assume that we want to 
tomh@4699
   728
read in a configuration of defaults from an input file named
tomh@4699
   729
"input-defaults.xml", and write out the resulting attributes to a
tomh@4699
   730
separate file called "output-attributes.xml".  (Note-- to get this
tomh@4699
   731
input xml file to begin with, it is sometimes helpful to run the
tomh@4699
   732
program to generate an output xml file first, then hand-edit that
tomh@4699
   733
file and re-input it for the next simulation run).
tomh@3704
   734
@verbatim
tomh@4699
   735
#include "contrib-module.h"
tomh@4699
   736
...
tomh@4699
   737
int main (...)
tomh@4699
   738
{
tomh@3704
   739
tomh@4699
   740
  Config::SetDefault ("ns3::ConfigStore::Filename", StringValue ("input-defaults.xml"));
tomh@4699
   741
  Config::SetDefault ("ns3::ConfigStore::Mode", StringValue ("Load"));
tomh@4699
   742
  Config::SetDefault ("ns3::ConfigStore::FileFormat", StringValue ("Xml"));
tomh@4699
   743
  ConfigStore inputConfig;
tomh@4699
   744
  inputConfig.ConfigureDefaults ();
tomh@4699
   745
  
tomh@4699
   746
  //
tomh@4699
   747
  // Allow the user to override any of the defaults and the above Bind() at
tomh@4699
   748
  // run-time, via command-line arguments
tomh@4699
   749
  //
tomh@4699
   750
  CommandLine cmd;
tomh@4699
   751
  cmd.Parse (argc, argv);
tomh@3704
   752
tomh@4699
   753
  // setup topology
tomh@4699
   754
  ...
tomh@3704
   755
tomh@4699
   756
  // Invoke just before entering Simulator::Run ()
tomh@4699
   757
  Config::SetDefault ("ns3::ConfigStore::Filename", StringValue ("output-attributes.xml"));
tomh@4699
   758
  Config::SetDefault ("ns3::ConfigStore::Mode", StringValue ("Save"));
tomh@4699
   759
  ConfigStore outputConfig;
tomh@4699
   760
  outputConfig.ConfigureAttributes ();
tomh@4699
   761
  Simulator::Run ();
tomh@4699
   762
}
tomh@3704
   763
@end verbatim
tomh@3704
   764
tomh@3704
   765
@subsection GTK-based ConfigStore
tomh@3704
   766
tomh@3704
   767
There is a GTK-based front end for the ConfigStore.  This allows users
tomh@3704
   768
to use a GUI to access and change variables.  Screenshots of this
tomh@3704
   769
feature are available in the 
tomh@3704
   770
@uref{http://www.nsnam.org/docs/ns-3-overview.pdf,,ns-3 Overview} presentation.
tomh@3704
   771
tomh@3704
   772
To use this feature, one must install libgtk and libgtk-dev; an example
tomh@3704
   773
Ubuntu installation command is:
tomh@3704
   774
@verbatim
tomh@3704
   775
sudo apt-get install libgtk2.0-0 libgtk2.0-dev
tomh@3704
   776
@end verbatim
tomh@3704
   777
To check whether it is configured or not, check the output of the
tomh@3704
   778
./waf configure step:
tomh@3704
   779
@verbatim
tomh@3704
   780
---- Summary of optional NS-3 features:
tomh@3704
   781
Threading Primitives          : enabled
tomh@3704
   782
Real Time Simulator           : enabled
tomh@3704
   783
GtkConfigStore                : not enabled (library 'gtk+-2.0 >= 2.12' not found)
tomh@3704
   784
@end verbatim
tomh@3704
   785
tomh@3704
   786
In the above example, it was not enabled, so it cannot be used until a
tomh@3704
   787
suitable version is installed and ./waf configure; ./waf is rerun.
tomh@3704
   788
tomh@4699
   789
Usage is almost the same as the non-GTK-based version, but there
tomh@4699
   790
are no ConfigStore attributes involved:
tomh@3704
   791
@verbatim
tomh@3704
   792
  // Invoke just before entering Simulator::Run ()
tomh@3704
   793
  GtkConfigStore config;
tomh@4699
   794
  config.ConfigureDefaults ();
tomh@4699
   795
  config.ConfigureAttributes ();
tomh@3704
   796
@end verbatim
tomh@3704
   797
tomh@3704
   798
Now, when you run the script, a GUI should pop up, allowing you to open
tomh@3704
   799
menus of attributes on different nodes/objects, and then launch the
tomh@3704
   800
simulation execution when you are done.  
tomh@3704
   801
tomh@3704
   802
@subsection Future work
tomh@3704
   803
There are a couple of possible improvements:
tomh@4020
   804
@itemize @bullet
tomh@3704
   805
@item save a unique version number with date and time at start of file
tomh@3704
   806
@item save rng initial seed somewhere.
tomh@3704
   807
@item make each RandomVariable serialize its own initial seed and re-read
tomh@3704
   808
it later
tomh@3704
   809
@item add the default values
tomh@3704
   810
@end itemize
tomh@3704
   811