Making Data Accessible to ACNET from a VxWorks Front End

Jeff Utterback
Fermilab AD/Controls
August 21, 1995

SCOPE

This is a "how to" document for making data accessible to ACNET from a VxWorks front end. This document will attempt to explain:

This document will not deal with alarms, continuous plots, or snap shot plots. These items can be successfully used in a VxWorks front end but are topics which are best left to a more advanced level discussion.

TERMS

ACNET. ACNET is the key backbone to the entire Fermilab Accelerator Control System. It is the ACcelerator NETwork on which important accelerator data is sent and received. It is beyond the scope of this document to fully explain ACNET but any reader who is unfamiliar with ACNET would be wise to learn more about it as a prerequisite.

DataBase Name.All access to ACNET data is performed using a database name. All database names are in the form of X:YYYYYY. A single character followed by a colon, followed by up to six more characters. A database name is created using a utility called Dabbel.

DataBase Device. When a database name is created, several pieces of information are associated with the name for the purpose of defining what front end the data resides on, what format the data is in, what device properties the data has etc. One of the most important pieces of information is called the SSDN. The database name along with the associated information is called the database device.

Device Properties. The typical properties that a device may have are reading, setting, read the setting, basic status, and basic control. A device may have one or more of these properties. The properties that a device uses can be set up using Dabbel.

SSDN. The SSDN (Subsystem Device Number) is an important part of the database device entry. It contains network information about which front end the data resides on so that the access will be directed to the correct computer. It also contains information about the device such as its channel number, and its Object ID. The SSDN is entered along with the other device information using Dabbel.

Dabbel. A program which resides on the VAX Cluster which is used to create or alter a database device.

VxWorks. VxWorks is a commercial multitasking operating system that the AD/Controls Group has adopted for use with microcomputers connected to ACNET.

Front End. A Front End (FE) is any computer that is capable of providing data to ACNET directly. Historically, all front end computers contain the tasks RETDAT (return data) and SETDAT (set data.)

Front End Keeper. This is the person in charge of the front end software.

MOOC. Minimal Object Oriented Communication. MOOC is a library of C functions written by Boris Lublinsky which follows the object oriented paradigm. The MOOC library is used extensively when making front end data accessible to ACNET.

Method. This is an object oriented name for a function which manipulates data associated with an object.

Console Parameter Page. A console parameter page provides minimal access to the data associated with any database name. Using a parameter page, the user can read the device, set the device, read the setting, read the basic status, and set the basic control.

Custom Console Application Page. If basic parameter page access is not enough then a custom console application page can be written. Using a custom application page, the data can be read, set, and displayed in any way that is required. The user interface can be customized so that the data is accessed in any convenient fashion that suits the needs of the user. Another advantage that a custom application has over a parameter page is that the length and offset parameters can be used to access the data in different ways on the fly without changing the database entry.

Length And Offset. Length and offset are two extra parameters that get sent to the front end along with the SSDN when accessing data. Length is used to tell the front end exactly how many bytes of data are being requested. Offset is used if the device is capable of returning an array of data. Offset tells the front end which element in the array to start returning data from. These two parameters are powerful because they are the only parameters that can be changed on the fly without changing the database entry.

THE ACNET SSDN

When ACNET makes a network access, it sends out a block of data known as the SSDN (Subsystem Device Number.) The SSDN contains several pieces of information so that the correct front end on the network will respond with the specific data which is required. Its important to understand the SSDN before attempting to write software for the front end. The SSDN may have a different format for different front ends on the network. The format explained here only pertains to VxWorks front ends using MOOC. The SSDN consists of 4 16-bit words.

Lets use this SSDN for an example.09CC 0034 0001 2101

The first word in the SSDN is the trunk number and node number assigned to the front end. In this case its trunk 9, node CC. Every new front end must get a trunk and node assigned by an ACNET expert. At the present time the person to contact for getting a new trunk and node assigned is Glenn Johnson.

The second word in the SSDN is the OOC ID (OID), in this case it is 0034. The OID is a code which tells the front end which object the data is to come from. The OID is usually defined in the front end code in a file called dev_guts.h.

The third word in the SSDN is a 16 bit User Defined Field also known as the Misc field. In this case the field has been set to 0001. The user defined field can be set to any value for any purpose. It can be used as a code to tell the front end program what format the data should be returned in or which piece of data should be returned. The SSDN is defined when the database device is created using Dabbel. As long as the front end keeper and the database device creator agree on the user defined field, all is well. Often the front end keeper is also the database device creator. That makes it easy.

The last word in the SSDN contains a Device Type byte and a Device Channel byte. The Device Type is also known as the Class Number which is defined in the front end program in the file dev_guts.h. The front end may have several devices under its control. The Device Type number in the SSDN tells the front end which device type this data access pertains to.

Often a device or a device type may have several channels associated with it. The channel number in the SSDN is used to distinguish between device channels.

There are 2 more pieces of information that get sent along with the SSDN when ACNET is accessing data. These are know as length and offset. Length is used to tell the front end exactly how many bytes of data are being requested. Offset is used if the device is capable of returning an array of data. Offset tells the front end which element in the array to start returning data from.

Length and offset are unique because they are the only 2 things that can be specified from within a console application page. The SSDN is locked in when the device is created in the database so none of the data in the SSDN can be changed on the fly. On the other hand, length and offset can be set from within a custom console application page. This is quite a valuable feature at times when flexibility is needed to get data in different formats from the same database device. For example, offset could be used as a code instead of an array offset. The code might tell the front end to return different pieces of information about the same device. A detailed discussion of the use of length and offset follows later in this document.

DEFINING AN EXAMPLE

The following example will be used throughout the rest of this document. Suppose we have a 68000 based front end which gathers data from 8 radiation detection devices. The front end gathers and manipulates the data so that it ends up in floating point format and has units of counts per second. The data from the 8 devices needs to be made available to ACNET. Also the front end runs the data through a mathematical filter so that the count rate data is smoothed out. The smoothed out data also needs to be made available to ACNET.

IMPLEMENTING THE FRONT END PROGRAM

At present, the software for VxWorks Front Ends is developed on a Unix machine known as crusher. The files needed for our example front end project are:

dev_guts.h The OIDs and the Class numbers are defined here.
ratedev.c The access method functions and the init_class function reside here.
ooc_ini.c OOC initialization functions.
STEP 1: SETUP OIDS AND class NUMBERS
The first step in implementing the front end program is to setup the OID and class numbers. It is customary to define the OID and class numbers in a file called dev_guts.h. Its just a simple matter of choosing an unused number. Make sure the number is unique for all classes and OIDs in this front end. These OIDs and class numbers will be used in the SSDN for the devices when the database devices are created. This example shows the generic class and OID numbers which Boris has pre-defined along with the numbers I have chosen for the RATECLS and the RATEOID.
/* Device class definitions */ #define TESCLS 1 /* Test class device */ #define TIMCLS 2 /* Time since boot class */ #define SLMCLS 3 /* Slam enable class */ #define VERCLS 4 /* Version class */ #define ALRCLS 5 /* Alarm class */ #define SCPCLS 6 /* Slow continuous plot class */ #define RATECLS Ox21 /* Rate class */ /* classes OIDs */ #define TIMOID 2 /* Time from boot OID */ #define SLMOID 3 /* Slam control OID */ #define VEROID 4 /* Version OID */ #define TESTOID 10 /* Starting OID for test device */ #define RATEOID Ox34 /* rate OID */


STEP 2: WRITE THE ACCESS METHODS
In order to make data available to ACNET, the front end keeper is required to write several small functions which are known as Methods in the object oriented world. The required functions are the reading method, the setting method, the reading of setting method, the basic status method, and the initialization method. It is not required that all 5 of these methods be used. Some devices may only require a subset of these methods.

For our example we only want ACNET to be able to read the count rates. The count rates are not setable and they do not have any binary status associated with them, so we will only have to write the reading method and the init method. It is customary to put the method functions in a file called xxxdev.c. For our example the following methods would be placed into a file called ratedev.c. Lets look at the details of the reading method.


/*----------------------------------------------------------------------*/
/* Reading method for class RATECLS                                     */
/*----------------------------------------------------------------------*/
extern float CountRate[8];
extern float FilteredCountRate[8];

static int MreadD(short cls, READ_REQ* req, float* rep, void* ivs)
{
    OMSP_DEF* omsp;
    unsigned int chan;

    if (!req || !rep)
        return ERR_INMEM;

    if (req ->OFFSET != 0)
        return ERR_BADOFF; /* check offset */

    if (req->ILEN != 4)
        return ERR_BADLEN; /* check length */

    omsp = (OMSP_DEF *) &req->OMSP;
    if (omsp->typ != RATECLS)
        return ERR_WRONGTP;

    chan = omsp->chan;
    if (chan >= 8)
        return ERR_BADCHN;

    switch(omsp->misc) {
     case 0:
        *rep = CountRate[chan];
        break;

     case 1:
        *rep = FilteredCountRate[chan]; 
        break;

     default:
        return ERR_BADFORM;
        break;
    }
    return 0;
}

When the reading method gets called, 4 variables are passed in. These 4 variables are cls (class), req (request), rep (reply), and ivs (instance variables). The req variable has a type of READ_REQ. The READ_REQ type is defined as a structure in Boris's odasocc.h header file.
typedef struct {
    short int mt;     /* Message type                                       */
    int OMSP;         /* OOC-Message-Specific Parameters                    */
    short int ILEN;   /* Length of data (in bytes) that ACNET is requesting */
    short int OFFSET; /* Offset                                             */
    short int filler; /* filler                                             */
} READ_REQ; 

The OMSP variable in the READ_REQ type is defined as an int but it is really a 32 bit structure known as OMSP_DEF.
typedef struct {
    unsigned int misc:16;  /* 16 bit user defined field from SSDN  */
    unsigned int typ :8;   /* 8 bit Device type from SSDN          */
    unsigned int chan:8;   /* 8 bit Channel number from SSDN       */
} OMSP_DEF;

These two structures contain important information that the reading method needs to decide what data, how much data, and what data format ACNET is requesting. The next variable passed in is float* rep. This is the reply pointer. The data which ACNET is requesting should be placed into memory at the address pointed to by rep. The type of rep does not have to be float. The front end keeper can define rep to be any standard type such as short, int, unsigned long, etc.

In the example reading method the first step performed is checking the passed in parameters. If the parameters are unacceptable, the function returns an error code. This error code ultimately gets sent back to ACNET and will show up on what ever console page was requesting the data. The error codes shown in the example are standard errors defined in Boris's ooc_user.h header file.

First req and rep are checked for NULL. If one of them is NULL, there is an obvious error so the function returns an error code right away. Next, the passed in offset is checked. Since this method only returns one data item per channel and does not return an array of data, offset is always expected to be zero. A full discussion of the use of offset is saved for later in this document. The next thing checked is the length parameter. Length is always specified in terms of bytes. Since in this example the returned data is a float, length should be 4 bytes. If it isn't, a length error code is returned.

The next step performed in the example read method is to obtain the SSDN information from the OMSP (OOC Method Specific Parameters???). This gives the programmer access to the misc field (user defined field), the type field, and channel field from the SSDN. The type is checked against the class number which is defined in dev_guts.h, and the channel number is checked for range.

Now we are finally ready to return the requested data. In this example the misc field is used as a code to decide if the CountRate is to be returned or if the FilteredCountRate is to be returned. The channel field is used as an array index to return data from the specified channel. Notice also that if the misc field is unrecognized, an error code is returned via the default: at the end of the switch statement.

The only other method we need to write for our example is the init method. The init method is used to provide information about alarms, continuous plots, and snap shot plots. For this example we will not be using alarms or plots, so the init method simply returns.


/*---------------------------------------------------------------*/
/* Init method for class RATECLS                                 */
/*---------------------------------------------------------------*/
static int Minit(short cls, short* oid, void* rep, void* ivs)
{
    return 0;
}

STEP 3: WRITE THE init_class FUNCTION
The init_class function is used to set up important information that the MOOC system needs to know in order to make data accessible to ACNET. This function tells MOOC how many methods the class has, the names of the methods, and whether there are any super classes to be dealt with. Super classes are used for alarms, for continuous plots and for snap shot plots. It is customary to make this the last function in the xxxdev.c file.
int init_class_rated(void)
{
    int stat;
/*  short scl[] = {ALRCLS};   super class arrary  */

    if ((stat = create_class(RATECLS,     /* class name */
                             0,           /* how many super classes */
                             NULL,        /* pointer to super class array */
                             2,           /* how many methods in the class */
                             0)) != NOERR)
        return stat;

    if ((stat = name_class(RATECLS, "rate dev")) != NOERR)
        return stat;

    if ((stat = add_class_msg(RATECLS, Init, (PMETHOD)Minit)) != NOERR)
        return stat;

    if ((stat = add_class_msg(RATECLS, rPRREAD, (PMETHOD) MreadD)) != NOERR)
        return stat;
/*  
    if ((stat = add_class_msg(RATECLS, sPRSET, (PMETHOD) MSetD)) != NOERR)
        return stat;

    if ((stat = add_class_msg(RATECLS, rPRSET, (PMETHOD) MReSetD)) != NOERR)
        return stat;

    if ((stat = add_class_msg(RATECLS, rPRBSTS, (PMETHOD) MBasStD)) != NOERR)
        return stat;
*/
    return NOERR;
}

The first function called is the create_class function. Its easy to forget about this function when creating a class but it is very important to send it the correct parameters. In our example we have no super classes so the super class array has been commented out. If we had alarms the ALRCLS would be our super class. See Boris's TESTDEV for a complete example with alarms.

The rest of the init_class function just calls the name_class function and the add_class_msg functions. There is one add_class_msg call for every method. The names of the method functions are passed to add_class_msg along with the associated ACNET property. The method names could be anything but it is customary to name them MreadD, MsetD, etc.

STEP 4: CREATE THE OOC_INI.C FILE
The ooc_ini function is customarily kept in the ooc_ini.c file. The ooc_ini function is used to initialize all of the classes in the system. First the init_class_xxx function is called for each class in the system. Recall that we created the init_class_rated function in the file ratedev.c in step 3. Then the create_instance function is called. There could be more than one instance of a class but usually there is only one.
#include "vxWorks.h"
#include "stdio.h"
#include "ctype.h"
#include "/users/boris/mooc/ooc_user.h" /* User's definitions for OOC+- */
#include "/users/boris/mooc/mtdefs.h"   /* Message type definitions */
#include "/users/boris/mooc/odasooc.h"
#include "dev_guts.h"

int ERROR_FACILITY = 57;  /* Error facility code 57 is standard for MOOC*/

static struct {
    short ver;
    short rev;
} cur_ver = {1, 1};       /* Current version */

void ooc_ini(void)
{
    short stat, i, startn;

    /* Initiate Dictionary */

    init_clasobj();

    /* Initiate classes */

    if ((stat = init_class_alarms()) != NOERR) {   /* Alarm class */
        pr_ooc_error(stat);
        exit(NULL);
    }

    if ((stat = init_class_slcnpl()) != NOERR) {  /* Slow continious plot class */
        pr_ooc_error(stat);
        exit(NULL);
    }

    if ((stat = init_class_slsnpl()) != NOERR) {  /* Slow snapshot plot class */
        pr_ooc_error(stat);
        exit(NULL);
    }

    if ((stat = init_class_versd()) != NOERR) {   /* Version class */
        pr_ooc_error(stat);
        exit(NULL);
    }

    if ((stat = init_class_timd()) != NOERR) {    /* Age class */
        pr_ooc_error(stat);
        exit(NULL);
    }

    if ((stat = init_class_slamd()) != NOERR) {   /* Slam control class */
        pr_ooc_error(stat);
        exit(NULL);
    }

    if ((stat = init_class_testd()) != NOERR) {   /* Test class */
        pr_ooc_error(stat);
        exit(NULL);
    }

    if ((stat = init_class_rated()) != NOERR) {   /* rate class */
        pr_ooc_error(stat);
        exit(NULL);
    }

    /* Create version devices */

    for (i = 0, startn = VEROID; i < 1; i++, startn++)
        if ((stat = create_instance(startn, VERCLS, &cur_ver, "Version_dev")) != NOERR) {
            pr_ooc_error(stat);
            exit(NULL);
        }

    /* Create age devices */

    for (i = 0, startn = TIMOID; i < 1; i++, startn++)
        if ((stat = create_instance(startn, TIMCLS, NULL, "Time_dev")) != NOERR) {
            pr_ooc_error(stat);
            exit(NULL);
        }

    /* Create alarm control devices */

    for (i = 0, startn = SLMOID; i < 1; i++, startn++)
        if ((stat = create_instance(startn, SLMCLS, NULL, "Slam_dev")) != NOERR) {
            pr_ooc_error(stat);
            exit(NULL);
        }

    /* Create test devices */

    for (i = 0, startn = TESTOID; i < 1; i++, startn++)
        if ((stat = create_instance(startn, TESCLS, &startn, "Test_dev")) != NOERR) {
            pr_ooc_error(stat);
            exit(NULL);
        }

    /* Create rate devices */

    for (i = 0, startn = RATEOID; i < 1; i++, startn++)
        if ((stat = create_instance(startn, RATECLS, &startn, "rate_dev")) != NOERR) {
            pr_ooc_error(stat);
            exit(NULL);
        }
    return;
}

STEP 5: MAKE
The last step in creating the front end software is to "Make" the program. In the Unix world the word "Make" refers to compiling and linking the code. The Make utility is capable of discerning which software modules have changed since the last Make so it only recompiles modules which need it. The following is an example make file. It is customary for there to be one make file in the project directory. The file name is Makefile.
# Makefile
# in order to do a make  type -> make
CPU = MC68020
HOST = hpux

#these are short hand names which will be used later in the Makefile

DESTDIR = ./
LIBDIR = /users/briegel/vw/ACNET/
LIBSSM = /users/briegel/vw/acnet/ssm/
LIBUTILS = /users/briegel/vw/acnet/
LIBMOOC = /users/boris/mooc/
TRIGPATH = /users/boris/triggers/
FTPPATH = /users/boris/ftpman/
UCDPATH = /users/briegel/vw/acnet/trig/trig/
VW = /usr/vw_51/
VWH = $(VW)h/
H1 = $(VWH)
H2 = $(VWH)/net
H3 = $(VWH)/rpc
H4 = $(VWH)68k/

# this tells Make where to get the include files
INCLUDES = -I$(H1) -I$(H2) -I$(H3) -I$(H4) -I/usr/include -I$(LIBACNET) \
 -I$(TRIGPATH) -I$(LIBMOOC)

CC = cc68k
CFLAGS = -ansi -g -O2 -pipe -fvolatile -traditional -m68020 -m68881 -DCPU=$(CPU)
CPP = cpp68k
CPPFLAGS = -P
ASM = as68k
AFLAGS = -a -mc68020
LINK = ld68k
LFLAGS = -r -M
AR = ar68k
ARFLAGS = crsv

# list system software modules here.  Make will compile the
# corresponding .c files to get .o files if changes have been made

OBJS = ooc_ini.o  ratedev.o  example.o

#list system libraries here
LIBRARIES = $(LIBDIR)libacnet.a $(LIBUTILS)libutils.a $(LIBSSM)libssm.a \
            $(TRIGPATH)libtrig.a $(FTPPATH)libftpm.a $(LIBMOOC)libmooc.a

.SUFFIXES: .o .c .asm .s

.c.o:   $*.c
        $(CC) -D CPU=$(CPU) $(CFLAGS) $(INCLUDES) -c $*.c

.asm.o: $*.asm
        $(CPP) $(CPPFLAGS) $*.asm $*.s
        $(ASM) $(AFLAGS) -o $*.o $*.s > $*.lis
        mv $*.s $*.s.asm

# this creates the example.out file

# the first line tells make to relink if any of the OBJs have changed
# or if the Makefile has changed the rest of it tells make what
# modules to link into the example.out file notice that the libraries
# are last so that only unresolved functions are taken from the
# libraries

example.out : $(OBJS) Makefile
              $(LINK) $(LFLAGS) -o example.out \
              $(LIBACNET)echo.o \
              $(LIBMOOC)inient.o \
              $(OBJS) $(LIBRARIES) > example.map

# this is where dependencies go.  if these .h files change, Make will
#recompile these must come after the big link command(s) above

ratedev.o : dev_guts.h
ooc_ini.o : dev_guts.h
example.o : example.h

After the front end software has been made, it can be downloaded to the crate using the VxWorks bootup process.

CREATING THE DATABASE DEVICES

SSDN
In order to gain access to the data which the front end is now ready to provide, ACNET database devices need to be created. The process of creating database devices involves using the Dabbel utility. It is beyond the scope of this document to explain the use of Dabbel, but I will attempt to explain basic elements required for gaining access to data in a VxWorks front end. Recall for our example, we have 8 count rates and 8 filtered count rates. The database names could be set up like this.

Raw count rates:

S:RATE0   SSDNHX READING(09CC/0034/0000/2100)
S:RATE1   SSDNHX READING(09CC/0034/0000/2101)
"         "      "       "    "    "    "
S:RATE7   SSDNHX READING(09CC/0034/0000/2107)

Filtered count rates:

S:FRATE0  SSDNHX READING(09CC/0034/0001/2100)
S:FRATE1  SSDNHX READING(09CC/0034/0001/2101)
"         "      "       "    "    "    "
S:FRATE7  SSDNHX READING(09CC/0034/0001/2107)

Notice that the channel field in the SSDN changes for each of the 8 devices. Also notice that for the filtered count rates the misc field (user defined field) in the SSDN holds 0001. This corresponds with the way that the read access method (MreadD) function was set up in the front end software. The SSDN gets passed into the reading method in the OMSP.


omsp->misc contains the 16 bit user defined field from the SSDN
omsp->type contains the class number from the SSDN
omsp->chan contains the channel number from the SSDN

It is not etched in stone that these fields be used exactly as stated here. For example, suppose there was a project which required 1000 channels. The channel field can only go up to 255. In this case the 16 bit user defined field could be used instead of the channel field. That leaves the channel field open to be used for other purposes.
DATA SIZE
When creating a database device, the "default data size" must be specified. For our example, the data is returned from the front end as a 4 byte float, so the default data size in the database should be specified as 4. If our data was a 2 byte short int, then the default data size would be 2. The Dabbel entry for our example for data size would be PRO READING(4,4,60)
DATABASE TRANSFORMS
When ACNET reads data, the raw data may not be in its most desirable format. The data may need some mathematical manipulation in order to convert it into its "Engineering Units". The Engineering Units are the units that are most useful to the end user. For example, data from an ADC may need to be converted from a binary number into a floating point voltage. In that case the Engineering Units would be in Volts. Sometimes the raw data may need to have an offset added to it or may need to be multiplied by a scale factor. All of these things can be done in the database using primary and common (secondary) transforms.

At present there are 25 Primary Transforms and 15 Common Transforms available to be used with ACNET devices. A list of the current transforms can be found on page D80 under the Pgm_Tools menu. The primary transforms are generally used to convert the data into a format that can be handled by DEC computers. The common transforms are generally used to convert the data into Engineering Units if needed.

For our example, the data is returned from the front end in a 4 byte float with units of counts per second. The units are already in an acceptable form for the end user, but the 4 byte float is a problem. The front end in our example is a 68000 based computer. The byte ordering of a 68000 is incompatible with a DEC computer. The data needs to be byte swapped and there is a Primary Transform that can do that for us. It is Primary Transform number 24.

We do not need a Common Transform for this example so we will use Common Transform number 0 which is x' = x.

The Dabbel entry for our example for transforms would be:

PDB READING('Ct/s','Ct/s',24,0,4,0,0,0,0,0)
MORE ABOUT TRANSFORMS
It appears that parameter pages and the dio_get functions which are available to console programmers require that readings retrieved from ACNET be in a floating point format. Almost all of the primary transforms convert the data to a float. It seems to be preferred that all ACNET data should end up as a float. If the data from the front end is an integer it still needs to be converted to a float using one of the primary transforms.

For example, if the front end data is a 2 byte integer (know as type "short" in c) declare *rep to be a short in the MreadD function such as;

static int MreadD(short cls, READ_REQ* req, short* rep, void* ivs)

When creating the database device use a data size of 2 bytes and use primary conversion type 10 which is Float (input).

The following primary transforms should be used for 68000 front ends:

If the data is... Use...
1 Byte Int (char)Primary Transform 10
2 Byte Int (short)Primary Transform 10
4 Byte Int (int)Primary Transform 28
4 Byte Float (float)Primary Transform 24

ACCESSING DATA FROM A PARAMETER PAGE

After the front end program is complete and the database devices have been created, its time to try accessing the data. A parameter page is usually a good place to start testing new devices. Go to a console and bring up a parameter page. There are several of them on the console system and any one will do for starters such as pages L7, E42 etc. Type in the name of the database device that is to be tested. If the device has reading property, its reading data will be displayed on the page. If the device has basic status, the status will also be displayed as a series of characters to the right of the reading value. The engineering units will also be displayed. For our example, the data is in counts per second and was set up using Dabbel as "Ct/s"

It is beyond the scope of this document to fully explain parameter pages, but they are very powerful for accessing basic device functionality. If the reader is just learning about ACNET, it would be wise to experiment with a parameter page. (Of course, you should not make any settings to devices that don't belong to you!!) All of the basic functionality built into ACNET can be utilized by a parameter page and it is a good place to learn about ACNET devices.

For some projects, a parameter page is all that is required for the user interface. If a suitable existing parameter page can not be found to house the device entries, a new blank parameter page can be set up by speaking to Jim Smedinghoff.

ACCESSING DATA FROM A CUSTOM APPLICATION PAGE

If a parameter page does not provide an adequate user interface for accessing the data, then a custom application page must be written. It is beyond the scope of this document to explain how to write a custom console application, but documentation does exist and there is a myriad of function libraries available to help in the creation of a custom console application. Brian Hendricks is the present person to contact for help on this subject.

LENGTHS AND OFFSETS

It is possible to create a database device which returns an array of data. This array of data can not be easily retrieved using a parameter page. A custom application page can provide very good access to data arrays with the use of the length and offset parameters.

When retrieving array data from a custom application page you can use the dio_get_ functions with the length and offset parameters. For example if you have an array of 4 byte floats and the array has 100 elements (float data[100]) you can get the data in the following ways.

To get the whole array: length = 4 * 100, offset = 0
To get the 5th element: length = 4, offset = 5
To get the last 50 elements: length = 4 * 50, offset = 50

You will have to write code in the xxxdev.c file in order to handle these lengths and offsets. You could set up the MreadD method like this:


offset = req->OFFSET;
numdat = req->ILEN / sizeof(float);
for (i = 0; i < numdat; i++)
    *rep++ = data[offset++];

Notice that Boris has already malloc'ed the space required for rep before MreadD is called so you simply fill in the data.

Length and Offset are the only two parameters that get sent to the front end on the fly. All other parameter such as misc, type, and channel come from the SSDN which can only be changed using Dabbel. That makes Length and Offset very powerful for flexible data access.

Important Note: Dabbel allows you to enter a maximum array size of 16384 but that number is bogus! The maximum array size that can be sent over ACNET is 3982 bytes. If you need to get an array which is larger that 3982 bytes you can do multiple dio_gets in the custom application such as the following pseudo code.

dio_get(length = 3982, offset = 0 * 3982);
dio_get(length = 3982, offset = 1 * 3982);
dio_get(length = 3982, offset = 2 * 3982);

The length parameter must always be used to state the true amount of data to be transferred, but its OK to lie with the offset parameter. What I mean is that offset does not have to be used for an array offset. Offset could be used for anything as long as the author of the custom application page and the author of the front end code (usually the same person) agree. Offset could be used as a code to tell the front end anything such as which piece of data is required, or which mathematical formula to use upon the data, etc. Perhaps there are several arrays associated with one device, in that case offset could be used to determine which array is required by the application page.

CONCLUSION

In this document, I have attempted to explain how to make data available to ACNET from a VxWorks front end. First I explained the importance of the SSDN and how the data contained in the SSDN is used to tell the front end which data is required. Next I explained the necessary front end software which is required to make data available to ACNET. Next I explained the basic elements of a database device entry and how the information in the database entry is used to access data from a front end. I have explain how to access a database device using a parameter page. I also explained that a parameter page is quite useful for basic data access, but for a more complicated user interface, a custom application page is required. I explained how a custom application page has an advantage over a parameter page because it can make use of the length and offset parameters. I finished the document by explaining why the length and offset parameters are powerful because they can be changed on the fly from a custom application page in order to gain flexible access to the data.

Security, Privacy, Legal