Home
About NuDesign
Sales
Support
Eval Download
Partners
Testimonials
SNMP Links
Tutorials
News

Tutorial for Monitoring Thresholds and Sending Traps SNMP Agent

This document explains the architecture (with emphasis on notifications) of the SNMP agent that implements ND-GARAGE-MIB mib (Garage agent).

The Garage agent is generated using NuDesign Visual SNMP xAgentBuilder for C++.  If you are not familiar with xAgentBuilder please take a look at the xAgentBuilder help topics titled "Create Standalone SNMP Agent" and "Create SNMP Extension Dll".  In this tutorial the Garage agent is implemented as both standalone and an extension DLL.

Both projects (standalone:GarageC, extension DLL:Garage) are built using the evaluation version of NuDesign Visual SNMP xAgentBuilder.  You may need to update your Include and Lib directories in project settings to reflect the installation directory of the NuDesign Visual SNMP xAgentBuilder on your system.

Note that the MIB instrumentation code is identical in both cases (except for the handling of notifications!).

ND-GARAGE-MIB

The ND-GARAGE-MIB consists of a few scalar objects, one table and two notifications.  Here are the definitions that are of interest for this article.

garageCOLevel OBJECT-TYPE

     SYNTAX INTEGER

     MAX-ACCESS read-only

     STATUS current

     DESCRIPTION

         "CO toxicity level in garage (airborne concentration level expressed in

         parts per million or PPM)."

::= { garageObjects 4 }

garageCOLevelRisingThreshold >OBJECT-TYPE

     SYNTAX INTEGER

     MAX-ACCESS read-write

     STATUS current

     DESCRIPTION

        "Level of CO toxicity level in garage (in parts per million or PPM)

        considered dangerous for humans.

        400 PPM causes serious headache

        after 1-2 hours of exposure.

        Thislevel is life threatening after 3 hours.

        When the current sampled value is greater than or equal to

        this threshold, and the value at the last sampling interval

        was less than this threshold, a single event

        (coLevelRisingAlarm) will be generated.

        After a rising event is generated, another such event will not be

        generated until the sampled value falls below this threshold and

        reaches the garageCOLevelFallingThreshold."

::= { garageObjects 5 }

 

garageCOLevelFallingThreshold OBJECT-TYPE

     SYNTAX INTEGER

     MAX-ACCESS read-write

     STATUS current

     DESCRIPTION

        "Level of CO toxicity level in garage (in parts per million or PPM)

        considered harmless for humans.  35 PPM is maximum exposure allowed by

        OSHA in the workplace over an eight hour period.

        When the current sampled value is less than or equal to this threshold,

        and the value at the last sampling interval was greater than this threshold,

        a single event (coLevelFallingAlarm) will be generated.

        After a falling event is generated, another such event will not be

        generated until the sampled value rises above this threshold and

        reaches the garageCOLevelRisingThreshold."

::= { garageObjects 6 }

 

The garageCOLevel represents the current level of carbon monoxide (CO) toxicity level in the garage (airborne concentration level expressed in parts per million or PPM).  When the CO toxicity level becomes dangerous (i.e. it exceeds the value of the rising threshold specified in the garageCOLevelRisingThreshold object) an SNMP trap will be sent.  The prudent implementation of the trap receiver will then turn on the fans in order to ventilate the garage area.  After the CO toxicity level drops to or below the harmless level (the value of falling threshold specified in the garageCOLevelFallingThreshold object) an SNMP trap will be sent.  In response to this trap, the trap receiver application will turn off the fans in the garage.

The trap that the Garage agent sends when it detects that CO level has exceeded the rising threshold is:

coLevelRisingAlarm NOTIFICATION-TYPE

    OBJECTS {

       garageCOLevel

     }

    STATUS current

    DESCRIPTION

        "The SNMP trap that is generated when CO toxicity level in garage

        crosses its rising threshold (garageCOLevelRisingThreshold)."

::= { appEvents 1 }

A rising-alarm is generated at the agent start-up if the first sampled CO level exceeds the rising threshold.

After a rising-alarm is generated another such event will not be generated until the CO level has fallen below the rising threshold, reached the falling threshold, and then subsequently reached the rising threshold again.

The trap that the Garage agent sends when it detects that the CO level has dropped below the falling threshold is:

coLevelFallingAlarm NOTIFICATION-TYPE

    OBJECTS {

        garageCOLevel

    }

    STATUS current

    DESCRIPTION

        "The SNMP trap that is generated when CO toxicity level in garage

        crosses its falling threshold (garageCOLevelFallingThreshold)."

::= { appEvents 1 }

A falling-alarm is generated at the agent start-up if the first sampled CO level is below the falling threshold.

After a falling-alarm is generated another such event will not be generated until the CO level has risen above the falling threshold, reached the rising threshold, and then subsequently reached the falling threshold again.

Here is tree structure of the ND-GARAGE-MIB

[1.3.6.1.4.1] iso.org.dod.internet.private.enterprises
  |
  +-[4761] nuDesign
    |
    +-[99] ndtExperimental
      |
      +-[11] garage
        |
        +-[ 1] garageObjects
        | |
        | +-[ 1] -RW- DisplayString garageAddress
        | +-[ 2] -RO- INTEGER garageNumVehicles
        | +-[ 3] -RW- DisplayString 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
          |
          +-[ 2] coLevelRisingAlarm
          +-[ 3] coLevelFallingAlarm

 

Agent

SNMP access to MIB objects is implemented in two classes: DGarageObjects and DVehiclesTable.  Please consult xAgentBuilder help and tutorials for the explanation of MIB subtree handler classes.

The part that is of interest in this article is the implementation of the CO level monitoring.  The global object sensor represents our carbon monoxide sensor.  It is an instance of COSensor class.  For convenience this class also hosts both threshold levels.  Sensor will be accesed by multiple threads, hence a single-writer/multi-reader locking object is also part of it.

Since this is simulating an agent, we need something to feed in the values for CO level.  COSensorSimulatorThread is used just for that.  An instance of this class, simulatorThread, is initialized in the constructor with the pointer to our sensor object.  CO value simulation has the form of a Triangular Wave function; the value of sensor is incremented every interval (default = 1 second) by step value (default = 10).  When the sensor value reaches it's maximum value (default max = 200) the sign of the step value is changed; i.e. after the next interval the value will be decremented by the step value.  When it reaches zero, the step value's sign will be reversed; and so on.  All three parameters (maxvalue, step, interval) can be changed in the COSensorSimulatorThread constructor.

The instance of DGarageObjects class, ggarageObjects, is initialized with the pointer to sensor object too.  SNMP requests for coLevel, coLevelRisingThreshold, and coLevelFallingThreshold will be resolved by reading/modifying sensor.  Note that the appropriate lock is used before calling sensor object's methods (see for example OnGet_GarageCOLevel method).

This architecture decouples the actual task of reading sensor value from the SNMP agent.  SNMP requests are simply fullfiled on variables that represent MIB objects ( sensor object).  Note that the value retrieved through an SNMP request is actually a 'cashed' value, and does not reflect the sensor value at the time of the request, but at the last time when simulatorThread updated it.  In other words, it could be off by one second (i.e. the interval used by simulatorThread).

Now, the interesting part: monitoring the threshold levels.  This is done in MonitorThread class.  Basically, every second (or whatever number of milliseconds is specified as interval in constructor) the value of the CO level is acquired and compared with thresholds.  If the threshold is reached/crossed (the value is less than or equal to falling threshold, or the value is greater than or equal to rising threshold) a corrseponding alarm is generated.

Requirements state that after a rising-alarm is generated another such event will not be generated until the CO level has fallen below the rising threshold, reached the falling threshold, and then subsequently reached the rising threshold again.  The rules for generation of a falling alarm are the reverse of those in previous sentence.  The m_eCOAlarmState variable is used to ensure that no two consecutive alarms of the same type will be generated.  When a rising-alarm is generated, m_eCOAlarmState is set to eCOAlarmState_high.  This ensures that subsequent threshold tests will be done only for falling-alarm condition.  When falling-alarm is generated, m_eCOAlarmState is set to eCOAlarmState_low, and subsequent threshold tests will be done only for rising-alarm condition.  Initially m_eCOAlarmState is set to eCOAlarmState_initial and both threshold tests will be performed.

When the condition for sending a trap is satisfied, a trap object is created ( TrapCOLevelRisingAlarm or TrapCOLevelFallingAlarm) and passed as an argument in the call to the SendTrap method:

gpTrapSender->SendTrap(trap);

Both notifications defined in ND-GARAGE-MIB have a single variable specified in OBJECTS clause: garageCOLevel.  Prior to sending, this value is assigned to the trap object:

trap.SetValue(coLevel);

And that's all there is to it.  Handling of traps is identical for standalone agents and extension DLLs.