doc/tutorial/attributes.texi
changeset 3332 da67e8efa347
parent 3331 7305032b6100
child 3333 76188c437e2e
equal deleted inserted replaced
3331:7305032b6100 3332:da67e8efa347
     1 @node ns-3 Attributes
       
     2 @chapter ns-3 Attributes 
       
     3 @anchor{chap:Attributes}
       
     4 
       
     5 In ns-3 simulations, there are two main aspects to configuration:
       
     6 @itemize @bullet
       
     7 @item the simulation topology and how objects are connected 
       
     8 @item the values used by the models instantiated in the topology
       
     9 @end itemize
       
    10 
       
    11 This chapter focuses on the second item above: how the many values
       
    12 in use in ns-3 are organized, documented, and modifiable by ns-3 users.
       
    13 The ns-3 attribute system is also the underpinning of how traces
       
    14 and statistics are gathered in the simulator. 
       
    15 
       
    16 Before delving into details of the attribute value system,
       
    17 it will help to review some basic properties of @code{class ns3::Object}.
       
    18 
       
    19 @node Object Overview
       
    20 @section Object Overview
       
    21 
       
    22 ns-3 is fundamentally a C++ object-based system.  By this we mean that
       
    23 new C++ classes (types) can be declared, defined, and subclassed
       
    24 as usual.
       
    25 
       
    26 Many ns-3 objects inherit from the @code{ns3::Object} base class.  These
       
    27 objects have some additional properties that we exploit for 
       
    28 organizing the system and improving the memory management
       
    29 of our objects:
       
    30 
       
    31 @itemize @bullet
       
    32 @item a "metadata" system that links the class name to a lot of 
       
    33 meta-information about the object, including the base class of the subclass,
       
    34 the set of accessible constructors in the subclass, and the set of 
       
    35 "attributes" of the subclass
       
    36 @item a reference counting smart pointer implementation, for memory
       
    37 management.
       
    38 @end itemize
       
    39 
       
    40 ns-3 objects that use the attribute system derive from either
       
    41 @code{ns3::Object} or @code{ns3::ObjectBase}.  Most ns-3 objects
       
    42 we will discuss derive from @code{ns3::Object}, but a few that
       
    43 are outside the smart pointer memory management framework derive
       
    44 from @code{ns3::ObjectBase}.
       
    45 
       
    46 Let's review a couple of properties of these objects.
       
    47 
       
    48 @node Smart pointers
       
    49 @subsection Smart pointers
       
    50 
       
    51 As introduced above in @ref{Smart Pointers 101}, ns-3 objects 
       
    52 are memory managed by a 
       
    53 @uref{http://en.wikipedia.org/wiki/Smart_pointer,,reference counting smart pointer implementation}, @code{class ns3::Ptr}. 
       
    54 
       
    55 Smart pointers are used extensively in the ns-3 APIs, to avoid passing
       
    56 references to heap-allocated objects that may cause memory leaks.  
       
    57 For most basic usage (syntax), treat a smart pointer like a regular pointer:
       
    58 @verbatim
       
    59   Ptr<WifiNetDevice> nd = ...;
       
    60   nd->CallSomeFunction ();
       
    61   // etc.
       
    62 @end verbatim
       
    63 
       
    64 @node CreateObject
       
    65 @subsection CreateObject
       
    66 
       
    67 As we discussed above in @ref{Object Creation}, 
       
    68 at the lowest-level API, objects of type @code{ns3::Object} are 
       
    69 not instantiated using @code{operator new} as usual but instead by
       
    70 a templated function called @code{CreateObject()}.
       
    71 
       
    72 A typical way to create such an object is as follows:
       
    73 @verbatim
       
    74   Ptr<WifiNetDevice> nd = CreateObject<WifiNetDevice> ();
       
    75 @end verbatim
       
    76 
       
    77 You can think of this as being functionally equivalent to:
       
    78 @verbatim
       
    79   WifiNetDevice* nd = new WifiNetDevice ();
       
    80 @end verbatim
       
    81 
       
    82 Objects that derive from @code{ns3::Object} must be allocated
       
    83 on the heap using CreateObject().  Those deriving from 
       
    84 @code{ns3::ObjectBase}, such as ns-3 helper functions and packet
       
    85 headers and trailers, can be allocated on the stack.  
       
    86 
       
    87 In some scripts, you may not see a lot of CreateObject() calls
       
    88 in the code;
       
    89 this is because there are some helper objects in effect that 
       
    90 are doing the CreateObject()s for you.
       
    91 
       
    92 @node TypeId
       
    93 @subsection TypeId
       
    94 
       
    95 ns-3 classes that derive from class ns3::Object can include
       
    96 a metadata class called @code{TypeId} that records meta-information
       
    97 about the class, for use in the object aggregation and component
       
    98 manager systems:
       
    99 @itemize @bullet
       
   100  @item a unique string identifying the class
       
   101  @item the base class of the subclass, within the metadata system
       
   102  @item the set of accessible constructors in the subclass
       
   103 @end itemize
       
   104 
       
   105 @node Object Summary
       
   106 @subsection Object Summary
       
   107 
       
   108 Putting all of these concepts together, let's look at a specific
       
   109 example:  @code{class ns3::Node}.
       
   110 
       
   111 The public header file node.h has a declaration that includes
       
   112 a static GetTypeId function call:
       
   113 @verbatim
       
   114 class Node : public Object
       
   115 {
       
   116 public:
       
   117   static TypeId GetTypeId (void);
       
   118   ...
       
   119 @end verbatim
       
   120 
       
   121 This is defined in the node.cc file as follows:
       
   122 @verbatim
       
   123 TypeId 
       
   124 Node::GetTypeId (void)
       
   125 {
       
   126   static TypeId tid = TypeId ("ns3::Node")
       
   127     .SetParent<Object> ()
       
   128   return tid;
       
   129 }
       
   130 @end verbatim
       
   131 Finally, when users want to create Nodes, they call:
       
   132 @verbatim
       
   133   Ptr<Node> n = CreateObject<Node> n;
       
   134 @end verbatim
       
   135 
       
   136 We next discuss how attributes (values associated with member variables
       
   137 or functions of the class) are plumbed into the above TypeId.
       
   138 
       
   139 @node Attribute Overview
       
   140 @section Attribute Overview
       
   141 
       
   142 The goal of the attribute system is to organize the access of
       
   143 internal member objects of a simulation.  This goal arises because,
       
   144 typically in simulation, users will cut and paste/modify existing
       
   145 simulation scripts, or will use higher-level simulation constructs,
       
   146 but often will be interested in studying or tracing particular 
       
   147 internal variables.  For instance, use cases such as:
       
   148 @itemize @bullet
       
   149 @item "I want to trace the packets on the wireless interface only on
       
   150 the first access point"
       
   151 @item "I want to trace the value of the TCP congestion window (every
       
   152 time it changes) on a particular TCP socket"
       
   153 @item "I want a dump of all values that were used in my simulation."
       
   154 @end itemize 
       
   155 
       
   156 Similarly, users may want fine-grained access to internal
       
   157 variables in the simulation, or may want to broadly change the
       
   158 initial value used for a particular parameter in all subsequently
       
   159 created objects.  Finally, users may wish to know what variables
       
   160 are settable and retrievable in a simulation configuration.  This
       
   161 is not just for direct simulation interaction on the command line; 
       
   162 consider also a (future) graphical user interface
       
   163 that would like to be able to provide a feature whereby a user
       
   164 might right-click on an node on the canvas and see a hierarchical,
       
   165 organized list of parameters that are settable on the node and its 
       
   166 constituent member objects, and help text and default values for
       
   167 each parameter.
       
   168 
       
   169 @node Functional overview
       
   170 @subsection Functional overview
       
   171 
       
   172 We provide a way for users to access values deep in the system, without
       
   173 having to plumb accessors (pointers) through the system and walk 
       
   174 pointer chains to get to them.  Consider a class DropTailQueue that
       
   175 has a member variable that is an unsigned integer @code{m_maxPackets};
       
   176 this member variable controls the depth of the queue.  
       
   177 
       
   178 If we look at the declaration of DropTailQueue, we see the following:
       
   179 @verbatim
       
   180 class DropTailQueue : public Queue {
       
   181 public:
       
   182   static TypeId GetTypeId (void);
       
   183   ...
       
   184 
       
   185 private:
       
   186   std::queue<Ptr<Packet> > m_packets;
       
   187   uint32_t m_maxPackets;
       
   188 };
       
   189 @end verbatim
       
   190 
       
   191 Let's consider things that a user may want to do with the value of
       
   192 m_maxPackets:
       
   193 
       
   194 @itemize @bullet
       
   195 @item Set a default value for the system, such that whenever a new
       
   196 DropTailQueue is created, this member is initialized to that default. 
       
   197 @item Set or get the value on an already instantiated queue.
       
   198 @end itemize
       
   199 
       
   200 The above things typically require providing Set() and Get() functions,
       
   201 and some type of global default value.
       
   202 
       
   203 In the ns-3 attribute system, these value definitions and accessor
       
   204 functions are moved into the TypeId class; e.g.:  
       
   205 @verbatim
       
   206 TypeId DropTailQueue::GetTypeId (void) 
       
   207 {
       
   208   static TypeId tid = TypeId ("ns3::DropTailQueue")
       
   209     .SetParent<Queue> ()
       
   210     .AddConstructor<DropTailQueue> ()
       
   211     .AddAttribute ("MaxPackets", "The maximum number of packets accepted by this DropTailQueue.",
       
   212                    Uinteger (100),
       
   213                    MakeUintegerAccessor (&DropTailQueue::m_maxPackets),
       
   214                    MakeUintegerChecker<uint32_t> ())
       
   215     ;
       
   216   
       
   217   return tid;
       
   218 }
       
   219 @end verbatim
       
   220 
       
   221 The AddAttribute() method is performing a number of things with this
       
   222 value:
       
   223 @itemize @bullet
       
   224 @item Binding the variable m_maxPackets to a string "MaxPackets"
       
   225 @item Providing a default value (100 packets)
       
   226 @item Providing some help text defining the value
       
   227 @item Providing a "checker" (not used in this example) that can be used to set
       
   228 bounds on the allowable range of values
       
   229 @end itemize
       
   230 
       
   231 The key point is that now the value of this variable and its default
       
   232 value are accessible in the attribute namespace, which is based on
       
   233 strings such as "MaxPackets" and TypeId strings.  In the next
       
   234 section, we will provide an example script that shows how users
       
   235 may manipulate these values.
       
   236 
       
   237 @node Basic usage
       
   238 @subsection Basic usage
       
   239 
       
   240 Let's look at how a user script might access these values.  
       
   241 This is based on the script found at @code{samples/main-attribute-value.cc},
       
   242 with some details stripped out.
       
   243 @verbatim
       
   244 //
       
   245 // This is a basic example of how to use the attribute system to
       
   246 // set and get a value in the underlying system; namely, an unsigned
       
   247 // integer of the maximum number of packets in a queue
       
   248 //
       
   249 
       
   250 int 
       
   251 main (int argc, char *argv[])
       
   252 {
       
   253 
       
   254   // By default, the MaxPackets attribute has a value of 100 packets
       
   255   // (this default can be observed in the function DropTailQueue::GetTypeId)
       
   256   // 
       
   257   // Here, we set it to 80 packets.  We could use one of two value types:
       
   258   // a string-based value or a Uinteger value
       
   259   Config::SetDefault ("ns3::DropTailQueue::MaxPackets", String ("80"));
       
   260   // The below function call is redundant
       
   261   Config::SetDefault ("ns3::DropTailQueue::MaxPackets", Uinteger(80));
       
   262 
       
   263   // Allow the user to override any of the defaults and the above
       
   264   // SetDefaults() at run-time, via command-line arguments
       
   265   CommandLine cmd;
       
   266   cmd.Parse (argc, argv);
       
   267 @end verbatim
       
   268 
       
   269 The main thing to notice in the above are the two calls to 
       
   270 @code{Config::SetDefault}.  This is how we set the default value
       
   271 for all subsequently instantiated DropTailQueues.  We illustrate
       
   272 that two types of Value classes, a String and a Uinteger class,
       
   273 can be used to assign the value to the attribute named by
       
   274 "ns3::DropTailQueue::MaxPackets".
       
   275 
       
   276 Now, we will create a few objects using the low-level API; here,
       
   277 our newly created queues will not have a m_maxPackets initialized to
       
   278 100 packets but to 80 packets, because of what we did above with
       
   279 default values.
       
   280 @verbatim
       
   281   Ptr<Node> n0 = CreateObject<Node> ();
       
   282 
       
   283   Ptr<PointToPointNetDevice> net0 = CreateObject<PointToPointNetDevice> ();
       
   284   n0->AddDevice (net0);
       
   285 
       
   286   Ptr<Queue> q = CreateObject<DropTailQueue> ();
       
   287   net0->AddQueue(q);
       
   288 @end verbatim
       
   289 
       
   290 At this point, we have created a single node (Node 0) and a 
       
   291 single PointToPointNetDevice (NetDevice 0) and added a 
       
   292 DropTailQueue to it.
       
   293 
       
   294 Now, we can manipulate the MaxPackets value of the already 
       
   295 instantiated DropTailQueue.  Here are various ways to do that.
       
   296 
       
   297 @subsubsection Pointer-based access
       
   298 
       
   299 We assume that a smart pointer (Ptr) to a relevant network device is 
       
   300 in hand; here, it is the net0 pointer. 
       
   301 
       
   302 One way to change the value is to access a pointer to the
       
   303 underlying queue and modify its attribute.
       
   304  
       
   305 First, we observe that we can get a pointer to the (base class)
       
   306 queue via the PointToPointNetDevice attributes, where it is called
       
   307 TxQueue 
       
   308 @verbatim
       
   309   Ptr<Queue> txQueue = net0->GetAttribute ("TxQueue");
       
   310 @end verbatim
       
   311 
       
   312 Using the GetObject function, we can perform a safe downcast
       
   313 to a DropTailQueue, where MaxPackets is a member
       
   314 @verbatim
       
   315   Ptr<DropTailQueue> dtq = txQueue->GetObject <DropTailQueue> ();
       
   316   NS_ASSERT (dtq);
       
   317 @end verbatim
       
   318 
       
   319 Next, we can get the value of an attribute on this queue
       
   320 We have introduced wrapper "Value" classes for the underlying
       
   321 data types, similar to Java wrappers around these types, since
       
   322 the attribute system stores values and not disparate types.
       
   323 Here, the attribute value is assigned to a Uinteger, and
       
   324 the Get() method on this value produces the (unwrapped) uint32_t.
       
   325 @verbatim
       
   326   Uinteger limit = dtq->GetAttribute ("MaxPackets");
       
   327   NS_LOG_INFO ("1.  dtq limit: " << limit.Get () << " packets");
       
   328 @end verbatim
       
   329   
       
   330 Note that the above downcast is not really needed; we could have
       
   331 done the same using the Ptr<Queue> even though the attribute
       
   332 is a member of the subclass
       
   333 @verbatim
       
   334   limit = txQueue->GetAttribute ("MaxPackets");
       
   335   NS_LOG_INFO ("2.  txQueue limit: " << limit.Get () << " packets");
       
   336 @end verbatim
       
   337 
       
   338 Now, let's set it to another value (60 packets)
       
   339 @verbatim
       
   340   txQueue->SetAttribute("MaxPackets", Uinteger (60));
       
   341   limit = txQueue->GetAttribute ("MaxPackets");
       
   342   NS_LOG_INFO ("3.  txQueue limit changed: " << limit.Get () << " packets");
       
   343 @end verbatim
       
   344 
       
   345 @subsubsection Namespace-based access
       
   346 
       
   347 An alternative way to get at the attribute is to use the configuration
       
   348 namespace.  Here, this attribute resides on a known path in this
       
   349 namespace; this approach is useful if one doesn't have access to
       
   350 the underlying pointers and would like to configure a specific
       
   351 attribute with a single statement.  
       
   352 @verbatim
       
   353   Config::Set ("/NodeList/0/DeviceList/0/TxQueue/MaxPackets", Uinteger (25));
       
   354   limit = txQueue->GetAttribute ("MaxPackets"); 
       
   355   NS_LOG_INFO ("4.  txQueue limit changed through namespace: " << 
       
   356     limit.Get () << " packets");
       
   357 @end verbatim
       
   358 
       
   359 We could have also used wildcards to set this value for all nodes
       
   360 and all net devices (which in this simple example has the same
       
   361 effect as the previous Set())
       
   362 @verbatim
       
   363   Config::Set ("/NodeList/*/DeviceList/*/TxQueue/MaxPackets", Uinteger (15));
       
   364   limit = txQueue->GetAttribute ("MaxPackets"); 
       
   365   NS_LOG_INFO ("5.  txQueue limit changed through wildcarded namespace: " << 
       
   366     limit.Get () << " packets");
       
   367 @end verbatim
       
   368 
       
   369 @node Setting through constructors and helper classes
       
   370 @subsection Setting through constructors helper classes
       
   371 
       
   372 Arbitrary combinations of attributes can be set and fetched from
       
   373 the helper and low-level APIs; either from the constructors themselves:
       
   374 @verbatim
       
   375 Ptr<Object> p = CreateObject<MyNewObject> ("n1", v1, "n2", v2, ...);
       
   376 @end verbatim
       
   377 or from the higher-level helper APIs, such as:
       
   378 @verbatim
       
   379   mobility.SetPositionAllocator ("GridPositionAllocator",
       
   380                                  "MinX", FpValue (-100.0),
       
   381                                  "MinY", FpValue (-100.0),
       
   382                                  "DeltaX", FpValue (5.0),
       
   383                                  "DeltaY", FpValue (20.0),
       
   384                                  "GridWidth", UintValue (20),
       
   385                                  "LayoutType", "RowFirst");
       
   386 @end verbatim
       
   387 
       
   388 @node Value classes
       
   389 @subsection Value classes
       
   390 Readers will note the new Value classes.  These can be thought of as
       
   391 an intermediate class that can be used to convert from raw types to the
       
   392 Values that are used by the system.  Recall that this database is holding
       
   393 objects of many types with a single generic type.  Conversions to this
       
   394 type can either be done using an intermediate class (IntValue, FpValue for
       
   395 "floating point") or via strings.  Direct implicit conversion of types
       
   396 to Value is not really practical.  So in the above, users have a choice
       
   397 of using strings or values: 
       
   398 @verbatim
       
   399 p->Set ("cwnd", "100"); // string-based setter
       
   400 p->Set ("cwnd", IntValue(100)); // value-based setter
       
   401 @end verbatim
       
   402 
       
   403 The system provides some macros that help users declare and define
       
   404 new Value subclasses for new types that they want to introduce into
       
   405 the attribute system.
       
   406 
       
   407 @node Extending attributes
       
   408 @section Extending attributes
       
   409 
       
   410 The ns-3 system will place a number of internal values under the
       
   411 attribute system, but undoubtedly users will want to extend this
       
   412 to pick up ones we have missed, or to add their own classes to this.
       
   413 
       
   414 @subsection Adding an existing internal variable to the metadata system 
       
   415 
       
   416 // XXX revise me
       
   417 
       
   418 Consider this variable in class TcpSocket:
       
   419 @verbatim
       
   420  uint32_t m_cWnd;   // Congestion window
       
   421 @end verbatim
       
   422 
       
   423 Suppose that someone working with Tcp wanted to get or set the 
       
   424 value of that variable using the metadata system.  If it were not
       
   425 already provided by ns-3, the user could declare the following addition 
       
   426 in the metadata system (to the TypeId declaration for TcpSocket):
       
   427 @verbatim
       
   428     .AddParameter ("Congestion window", 
       
   429                    "Tcp congestion window (bytes)",
       
   430                    MakeUIntParamSpec (&TcpSocket::m_cWnd, 1));
       
   431 
       
   432 @end verbatim
       
   433 
       
   434 Now, the user with a pointer to the TcpSocket can perform operations
       
   435 such as setting and getting the value, without having to add these
       
   436 functions explicitly.  Furthermore, access controls can be applied, such
       
   437 as allowing the parameter to be read and not written, or bounds
       
   438 checking on the permissible values can be applied.
       
   439 
       
   440 @subsection Adding a new TypeId
       
   441 
       
   442 Here, we discuss the impact on a user who wants to add a new class to
       
   443 ns-3; what additional things must be done to hook it into this system.
       
   444 
       
   445 We've already introduced what a TypeId definition looks like:
       
   446 @verbatim
       
   447 TypeId
       
   448 RandomWalk2dMobilityModel::GetTypeId (void)
       
   449 {  
       
   450   static TypeId tid = TypeId ("RandomWalkMobilityModel")    
       
   451     .SetParent<MobilityModel> ()    
       
   452     .SetGroupName ("Mobility")    
       
   453     .AddConstructor<RandomWalk2dMobilityModel> ()
       
   454     // followed by a number of Parameters
       
   455     .AddParameter ("bounds",                   
       
   456                    "Bounds of the area to cruise.",                   
       
   457                     MakeRectangleParamSpec (&RandomWalk2dMobilityModel::m_bounds,                                           Rectangle (0.0, 0.0, 100.0, 100.0)))    
       
   458     .AddParameter ("time",                   
       
   459                    "Change current direction and speed after moving for this delay.",              
       
   460                    MakeTimeParamSpec (&RandomWalk2dMobilityModel::m_modeTime,
       
   461                                       Seconds (1.0)))
       
   462 
       
   463     // etc (more parameters).
       
   464 @end verbatim
       
   465 
       
   466 The declaration for this in the class declaration is one-line public
       
   467 member method:
       
   468 @verbatim
       
   469  public:
       
   470   static TypeId GetTypeId (void);
       
   471 @end verbatim
       
   472 
       
   473 @section Adding new class type to the Value system
       
   474 
       
   475 From the perspective of the user who writes a new class in the system and
       
   476 wants to hook it in to the attribute system, there is mainly the matter 
       
   477 of writing 
       
   478 the conversions to/from strings and Values.  Most of this can be
       
   479 copy/pasted with macro-ized code.  For instance, consider class
       
   480 Rectangle in the @code{src/mobility/} directory:
       
   481 
       
   482 One line is added to the class declaration:
       
   483 @verbatim
       
   484 /**
       
   485  * \brief a 2d rectangle
       
   486  */
       
   487 class Rectangle
       
   488 {
       
   489 ...
       
   490 
       
   491   VALUE_HELPER_HEADER_1 (Rectangle);
       
   492 };
       
   493 @end verbatim
       
   494  
       
   495 One templatized declaration, and two operators, are added below the 
       
   496 class declaration:
       
   497 
       
   498 @verbatim
       
   499 std::ostream &operator << (std::ostream &os, const Rectangle &rectangle);
       
   500 std::istream &operator >> (std::istream &is, Rectangle &rectangle);
       
   501 
       
   502 VALUE_HELPER_HEADER_2 (Rectangle);
       
   503 @end verbatim
       
   504 
       
   505 In the class definition, the code looks like this:
       
   506 
       
   507 @verbatim
       
   508 VALUE_HELPER_CPP (Rectangle);
       
   509 
       
   510 std::ostream &
       
   511 operator << (std::ostream &os, const Rectangle &rectangle)
       
   512 {
       
   513   os << rectangle.xMin << "|" << rectangle.xMax << "|" << rectangle.yMin << "|" << rectangle.yMax;
       
   514   return os;
       
   515 }
       
   516 std::istream &
       
   517 operator >> (std::istream &is, Rectangle &rectangle)
       
   518  {
       
   519   char c1, c2, c3;
       
   520   is >> rectangle.xMin >> c1 >> rectangle.xMax >> c2 >> rectangle.yMin >> c3 >> rectangle.yMax;
       
   521   if (c1 != '|' ||
       
   522       c2 != '|' ||
       
   523       c3 != '|')
       
   524     {
       
   525       is.setstate (std::ios_base::failbit);
       
   526     }
       
   527   return is;
       
   528 }
       
   529 @end verbatim
       
   530 
       
   531 These stream operators simply convert from a string representation of the
       
   532 Rectangle ("xMin|xMax|yMin|yMax") to the underlying Rectangle, and the
       
   533 modeler must specify these operators and the string syntactical representation 
       
   534 of an instance of the new class.
       
   535