Extending the DNP Scan Task

[note] Currently available only for the UNIX version.

To extend the DNP Scan Task you will need the following:

The DNP scan task has been written such that customers can extend its functionality by defining or overriding functions for each of the object types.

[note] The function must be able to handle all of the different variations of an object (i.e. there isn't a seperate function for each object's variation).

To add or replace a function for an object:

  1. Create a working directory. Copy the following files into it:
  2. Declare new function in objectFunc.c. For example,
    extern rtInt DNPStorageOffset (OFFSET_ARGS);
    extern rtInt DNPStorageExtract (EXTRACT_ARGS);
    	    
    [note] Use the #define's declared in objectFunc.h for the function's arguments (i.e. EXTRACT_ARGS, FORMAT_ARGS, or OFFSET_ARGS). This will ensure that any changes will occur automatically when re-compiled.

    [note] The DNPScanInput_t, DNPScanOutput_t, and ApObjectHeader_t structures can be found in the DNPScan.h header file.

  3. Change the object entry in the objectFunctions declaration in objectFunc.c file to the new function. For example, the device storage object will look like the following:
            /* Device Storage               81 */
            {
                NULL,
                DNPStorageExtract,
                DNPStorageOffset
            },
    
    [note] This will either replace the NULL(s) already declared or it will replace an existing function.

  4. Write the function. The source file should include the following header files:
    [note] Use the printDebug (char *format, ...); function to print out any debug information.

    For example, file classExtract.c:

    /* Found in /usr/tesserNet/include on 9.x */
    /* Found in /opt/tesserNet/include on 10.x */
    #include <dnpscan.h>
    #include <objectfunc.h>
    #include <scandebug.h>
    
    rtInt
    DNPStorageOffset (OFFSET_ARGS)
    {
        ...
    
        rtInt                       offset = 3;    /* Default */
    
        static char                 *funcName = "DNPStorageOffset";
    
        ...
    
        if (objectHdr->objectVariation == 1) {
            offset = objectHdr->range.rangeLength * 3;
        }
    
        ...
    
        return (offset);
    }
    
    rtInt
    DNPStorageExtract (EXTRACT_ARGS)
    {
        ...
    
        char                        errmsg[512];
        rtInt                       offset;
        static char                 *funcName = "DNPStorageExtract";
    
        ...
    
        switch (objectHdr->objectVariation) {
    
            case 1:
    
                ...
    
                break;
    
            default:
                (void) sprintf (errmsg, "Unknown variation %d.", objectHdr->objectVariation);
                rtSetError (funcName, rtE_DATA, errmsg);
                rtLogError (1, "");
                *data = (rtUInt8 *) NULL;
                *deType = rtUNDEFINED;
                *stBuf = (rtUInt8 *) NULL;
                *stBufSize = 0;
                if (debugLevel > DEBUG_LEVEL_6) {
                    printDebug ("%s: %s\n", funcName, errmsg);
                }
                break;
        }
    
        ...
    
        offset = DNPStorageOffset (objectHdr, NULL);
    
        /* Always return the number of bytes this object should take up. */
        return (offset);
    }
    
  5. Add the new file(s) name into the Makefile (found in /usr/tesserNet/lib) in the USER_FUNCS declaration. For example,

    USER_FUNCS = classExtract.c

  6. Re-compile the scan task (i.e. run "make -f Makefile.HP").

There are three groups of functions that can be written. One set, is for extracting the data from a message from the remote device (extract), another is to format a message to the remote device (format), and one for determining the size of data for a given object (offset). In all cases, the function is responsible for logging any errors that it may have encountered.

For functions that extract the data, the memory space for the value returned must be allocated by the function. This can be handled by either malloc'ing the memory or by using static variables within the function.

[warning] The memory for the data return value for extract functions is NOT free'ed, and must be handled by the function itself (i.e. if it is called again, the function should handle free'ing memory it may have allocated previously, or re-using the allocated memory).

The extract function will be called once for each scan input record that was included in the request. This will occur as a result of optimization (see Data Transfer Optimization). It is up to the extract function to properly extract the data for the specified scan input record.

The extract function must always return the number of bytes to skip over for this particular object/variation, even if an error occurs. This offset should be the same offset returned from the offset function.

The format function should return the number of bytes that it has added to the request buffer. If the message could not be formatted, returning rtFAILED will result in a failure for the affected scan output record(s). The function is responsible for logging any errors.

Special CE Functions

Two special CE functions for extracting multiple values from the scan task buffer (see Device Independent Fields Treated Specially ), have been included as part of the release. To incorporate these CE functions in with your existing CE functions, the following files have been provided in the /usr/tesserNet/obj/CE directory:

[note] The contents of oem_stub.c and oem_table.c could be transferred into user_stub.c and user_table.c if you want the CE functions to reside in the user table.

To re-make the executables, you will need to copy the following files into the directory before running make:

[note] These files can be found in the ${RTAPROOT}/obj directory.

[note] The makefile will use the rtMqdbm.o and rtDbCfServer.o files from the ${RTAPROOT}/obj directory. If you want to use different object files, you will have to manually change the appropriate makefile.