Cpp Lesson 8

Tutorial for Visual xAgentBuilder for C++

Lesson 8: Adding Scalar Object to the Agent

In this tutorial we are going to build an agent that implements a single scalar read-only managed object. This object represents the number of vehicles in the garage as defined in ND-GARAGE-MIB.  Additional managed objects e.g. those that demonstrate how SNMP tables can be implemented will follow later on.

Object that will be implemented in this example is garageNumVehicles:

   garageNumVehicles OBJECT-TYPE
      MAX-ACCESS  read-only
      STATUS      current
      DESCRIPTION “Number of vehicles currently parked in the garage.”
   ::= { garage 2 }

Here is the MIB tree for the object defined in the ND-GARAGE-MIB.

[] iso.org.dod.internet.private.enterprises
 +-[4761] nuDesign
   +-[99] ndtExperimental
     +-[11] garage
       +-[ 1] garageObjects
       | | 
       | +-[ 1] -RW- DisplayString garageAddress
       | +-[ 2] -RO- INTEGER garageNumVehicles
       | +-[ 3] vehiclesTable
       |   +-[ 1] vehiclesEntry
       |     +-[ 1] -NA- INTEGER vehicleIndex
       |     +-[ 2] -RC- DisplayString vehicleLicensePlate
       |     +-[ 3] -RC- DisplayString vehicleModel
       |     +-[ 4] -RC- RowStatus vehicleStatus
       | +-[ 4] -RO- INTEGER garageCOLevel
       | +-[ 5] -RW- INTEGER garageCOLevelRisingThreshold
       | +-[ 6] -RW- INTEGER garageCOLevelFallingThreshold
       +-[ 2] garageEvents
         +-[ 1] notPaidAlarm
         +-[ 2] coLevelRisingAlarm
         +-[ 3] coLevelFallingAlarm

enterprises, nuDesign, ndtExperimental and garage are branch nodes while garageNumVehicles is a leaf node (scalar). ASN.1 syntax of this scalar node is INTEGER.

Each MIB object has unique identifier associated with it: OBJECT IDENTIFIER (OID).  Each object defined in the MIB has permanently assigned OID value. This assignment is called registration. Once a registration has been performed, no other item may be registered with the same OID value.

An OID value is an ordered sequence of non-negative integers, written from left to right, containing at least two elements. A rooted tree is often used to illustrate the numbers in the sequence that corresponds to OID values.

“Subtree handler” is object of the class that handles retrieval (get/getNext) or modification (set) requests for MIB objects that fall under its subtree. Class that prototypes “subtree handler” is BMibSubtreeHandler.

For example, assume that we have created class GarageObjects, and this class is the handler for the MIB objects that fall under the “” subtree. We must register object of type GarageObjects with MIB handler using code like this

   GarageObjects go;


When garageNumVehicles object (oid = is requested, MIB handler searches the registered “subtree handlers” for the best candidate to handle request. The best candidate is the registered “subtree handler” with the most matching subIds in the requested object’s oid for get and set request. Situation is little bit more complex for getNext/getBulk and it can involve multiple selection and invocation of handler methods.

In this example (assuming Get request) MIB handler determines that go is the best match (first 9 subIds are identical), and it passes requested varbind(list) to it.  Here is how it looks like:

   int err = go.Get(tUint32  transactionId,

                   BVarBindList& vbl,

                   int& ixFirst,

                   int numVBsToHandle);

go has to resolve numVBsToHandle varbinds in the requested vbl starting from ixFirst-th varbind. If the request vbl contains only 1 varbind, then ixFirst is 0 and numVBsToHandle is 1.

Now this does not help a lot. Yes, the message is decoded but we still have to deal with the full varbinds. Fortunately, there are a few base classes for grouping the MIB objects. These classes are derived from BMibSubtreeHandler so we can directly register them as “subtree handlers” with the MIB handler.

One of these classes is BMibScalarGroupImpl and this class handles scalar nodes that are direct children of this class’s subtree. Since garageNumVehicles is direct child of garageObjects, this is a good choice. Let’s take a look at the implementation.

class GarageObjects : public BMibScalarGroupImpl



   GarageObjects ()

   : BMibScalarGroupImpl(“ND-GARAGE-MIB.garageObjects”, “”)

      , m_nNumVehicles(0)



   ~ GarageObjects ()



   SNMP_ERRSTAT OnGet_NumVehicles(tUint32 transactionId, tASN_INT& nVal)


      nVal = m_nNumVehicles;

      return eSNMP_ERRSTAT_NO_ERROR;




   tASN_INT m_nNumVehicles;



SCALAR_INT(GarageObjects,2,”garageNumVehicles”,eAccess_ReadOnly, OnGet_NumVehicles, null)


Class GarageObjects is derived from BMibScalarGroupImpl, hence the first line in constructor initialize that class. Parameters are name and string representation of the subtree. Here we’ve initialized it to the garageObjects’s oid.

GarageObjects handles garageNumVehicles MIB object that has INTEGER syntax and read-only access. To instrument this object, we’ve added member variable m_nNumVehicles.

Method OnGet_NumVehicles gets called when retrieval operation (either get, getNext or getBulk) resolves to garageNumVehicles object. Parameters to the OnGet_NumVehicles are transactionId (an unique number
associated with this request, not related to requestId in the PDU), and reference to integer value nVal. This parameter should be filled with the value of m_nNumVehicles.

How does the MIB Handler know which method to call? Here is where request handler map comes into the picture. Request handler map consists of entries that associate the last subId in the particular MIB object’s oid with the corresponding method(s). Implementation of the map is provided in a few macros.

Class declaration has DECLARE_SNMP_RQHNDLR_MAP()and class implementation has:

BEGIN_SNMP_RQHNDLR_MAP(<name of the class>)

   <mapping entry>

   <mapping entry>

   . . .


Where <name of the class> is self descriptive, and <mapping entry> is MACRO of the form:


Consult reference for the list of MACROs. Each <mapping entry> provides mapping between <subId> and request
handler methods (<OnGetMethod> and/or <OnSetMethod>). Either method can be null (null is member method of the BMibImpl class, not NULL), but only according to the <access>. If access is read-only, only OnGetMethod must be present, while for read-write objects both methods should be there. Note that the command responder does not check for the validity of the function pointers. If null is present instead of OnGetMethod for the readable object, an exception will occur on the first attempt to call this method.

<name> is simply the name of the object, and can be NULL.

Back to out class. Request handler map contains single entry:

SCALAR_INT(GarageObjects,2,”garageNumVehicles”,eAccess_ReadOnly, OnGet_NumVehicles, null)

This tells the command responder that “garageNumVehicles” object is scalar (first part of the macro name is SCALAR), with syntax INTEGER (INT part in the name of the MACRO), that is read-only (eAccess_ReadOnly), and that the callback method is OnGet_NumVehicles. Also subId of this object (in regard to subtree) is 2; i.e. this object’s oid = Note that since this object is scalar, a zero subId is automatically appended.

The only difference between in the main of this application comparing to the one in the previous lesson is addition of the two lines:

   GarageObjects go;


The first line simply creates object of class GarageObjects, which is then registered as subtree handler in the second line. And that’s all there is. Build the project, run the agent and try to retrieve garageNumVehicles.


In the next lesson, Lesson 9: Read-write Scalar Object in the Agent, we’ll see how to handle set operation.

Previous    Next