Garage Project

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 xAgentBuilder9 for C++.  If you are not familiar with xAgentBuilder9 please take a look at the xAgentBuilder9 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. Please note that Monitoring Thresholds Sample Project needs to be downloaded in addition to the xAgentBuilder9 Eval.

Both projects (standalone:GarageC, extension DLL:Garage) are built using the evaluation version of NuDesign Visual SNMP xAgentBuilder9.  You may need to update your Include and Lib directories in project settings to reflect the installation directory of the NuDesign Visual SNMP xAgentBuilder9 on your system. For convenience a prebuild agent and dll are also included in the Sample download.

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
text-tree

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 the MonitorThread class.  Basically, every second (or whatever number of milliseconds is specified as the interval in the 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 the falling threshold, or the value is greater than or equal to the rising threshold) a corresponding 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.

Alternate Project Targets

The project files generated by our xAgentBuilder9 code generator are intended to be usable across a wide variety of Visual Studio versions. The specific version of these generated project files are for Visual Studio 2008. These project files can be imported into all versions of Visual Studio since then.

The files also default to a win32 project, but you can easily add a new platform, e.g. x64, by adding it in the Visual Studio configuration manager, once the project has been loaded. There is a section in the NuDesign xAgentBuilder9 help pertaining to this.

The layout of the projects provided by the code generator places all executables in the main directory of their respective sub-project.

E.g.
EXE, DLL & REM

The created project files also place in these same directories, the configuration files for the standalone agent and the remote agent.

E.g.
If the project name is ‘Garage’, then ‘Garage.xnv’ is place under EXE and ‘Garage.txt’ is placed under REM.

When a ‘Debug’ target is selected, the projects append a ‘D’ to the end of the name of each of the executables, so that they can reside in the same directory without colliding with the release version. This makes it convenient to test ‘Debug’ and ‘Release’ versions of the code against a single configuration file.

When you add the ‘x64’ platform to the project that same still applies. The final executables are placed in the same directory. It might be advisable to rename the output executable files of each of the sub-projects to avoid ‘colliding’ with the x86 versions of the executables.

E.g.
If the project name is ‘Garage’, rename the EXE output file to be ‘Garage64.exe’

If you are importing to a more recent version of Visual Studio, then due to the placement of executables, you will see a number of warnings from the conversion process. These are mostly related to all sub-project output files going to the one sub-project directory. Typically there will be multiple associated with each but relate to the same problem.

This placement of executables is not necessarily ‘standard’ so when building the project on a newer Visual Studio, you will see related
warnings during the build. Again this is due to the layout of the projects. These too can generally be ignored, except if you have chosen not to rename the output files of your x64 builds, in which case it will act as a reminder to you that you may be overwriting an existing file.