pvbrowser manual
Back Content Forward

Data Aquisition Template

The template shows howto interface with a shared memory and a mailbox according to the 4 principle

.

Using the template you may add your own custom data acquistion if your interface is not already supported by pvbrowser. The interface uses strings for variable names and values. Thus it is easy to write pvserver's that define the variable names for example in the toolTip[] property of the widgets. Within pvserver you use the class rlDataAquisition from rllib.

You can also interface a technological simulation model using this template. In each cycle of your model you could update the shared memory with values. Thus pvserver can monitor them. Using the mailbox thread you can send inputs like setpoints for example to the model.

Here is the sourcecode of the template:

//#############################################################################
// Author: pvbrowser 2007
//
// This is a template for a daemon implementing your own custom data acquistion.
// From pvserver you can access this data acquistion with the class rlDataAquisition.
//
// There are variables (strings) and values (also strings) which are stored in the shared memory.
// You will have to read the variables from the real world and store them in shared mmemory.
// Thus the pvserver can read them with rlDataAquisition.
//
// Also there is a mailbox.
// pvserver will send variables to this mailbox and you have to output them to the real world.
//
// The variables you have to poll are contained in the dataacquistion.itemlist file
//
// search for "TODO" to find out what you will have to add to this template
//#############################################################################
#include <stdio.h>
#include <string.h>
#include <ctype.h>

#include "rldataaquisitionprovider.h"
#include "rlmailbox.h"
#include "rlthread.h"

#define MAX_PATH_LENGTH 1024

typedef struct
{
  rlMailbox                *mbx;
  rlDataAquisitionProvider *provider;
  int                       running;                    // running = 1 as long as the provider is running
  char                      itemlist[MAX_PATH_LENGTH];  // name of the itemlist
  char                      shmname[MAX_PATH_LENGTH];   // name of the shared memory
  char                      mbxname[MAX_PATH_LENGTH];   // name of the mailbox
  int                       sleep;                      // interval in which data is polled
  int                       max_name_length;            // max length of a value in the shared memory
  int                       debug;                      // switch that may be used for debugging purpose
  long                      shmsize;                    // total size of the shared memory
                                                        // TODO: you may add your own parameters in here
} PV_DAQ_PARAM;

//### this is the main subroutine that is polling the data ####################
static void pollData(PV_DAQ_PARAM *daq, rlThread *thread)
{
  if(daq == NULL || thread == NULL) return;
  const char *cptr;
  static int  itest = 0;
  rlDataAquisitionProvider *provider = daq->provider;

  // poll data forever
  while(daq->running)
  {
    cptr = provider->firstItem();  // get first item
    while(cptr != NULL)            // loop through itemlist
    {
      // thread->lock();           // if you have critical sections, you may lock the thread
                                   // TODO: replace this test with something meaningfull
                                   printf("setIntValue variablename=%s value=%d\n", cptr, itest);
                                   provider->setIntValue(cptr, itest);
                                   itest += 1;
                                   printf("stored value=%d\n", provider->intValue(cptr));
      // thread->unlock();         // if you have critical sections, you may lock the thread
      cptr = provider->nextItem(); // get next item
    }
                                   if(itest > 256*256) break; // this only for testing
    rlsleep(daq->sleep);           // sleep for cycle time
  }  

  daq->running = 0;
}

//### this is the thread that is reading the mailbox ##########################
static void *mbxReaderThread(void *arg)
{
  int  ret;
  char buf[1024], *cptr, *variablename, *value;
  THREAD_PARAM *p    = (THREAD_PARAM *) arg;
  PV_DAQ_PARAM *daq  = (PV_DAQ_PARAM *) p->user;

  if(daq->debug) printf("mbxReaderThread starting\n");

  // read mbx until it is empty
  daq->mbx->clear();

  // wait for commands from clients
  while(daq->running)
  {
    ret = daq->mbx->read(buf,sizeof(buf));
    if(ret < 0) break;             // terminate if mailbox read fails
    cptr = strchr(buf,'\n');       // parse buf
    if(cptr != NULL) *cptr = '\0'; // eliminate newline
    cptr = strchr(buf,',');        // get second parameter
    if(cptr != NULL)
    {
      *cptr = '\0';
      cptr++;
      variablename = &buf[0];      // this is the name of your variable
      value        = cptr;         // this is the value of your variable
      // p->thread->lock();        // if you have critical sections, you may lock the thread
      printf("TODO: send your variables to the real world here. variablename=%s value=%s\n", variablename, value);
      // p->thread->unlock();
    }  
  }

  if(daq->debug) printf("mbxReaderThread terminating\n");
  daq->running = 0;
  return NULL;
}

// ### this is the main worker subroutine #####################################
static int run(PV_DAQ_PARAM *daq)
{
  if(daq == NULL) return -1;
  rlThread thread;

  // create instance of provider and mbx
  daq->provider = new rlDataAquisitionProvider(daq->max_name_length, daq->shmname, daq->shmsize);
  if(daq->provider->shmStatus() == rlDataAquisitionProvider::DAQ_PROVIDER_ERROR)
  {
    printf("error shared memory\n");
    return -1;
  }
  daq->mbx = new rlMailbox(daq->mbxname);

  // read the itemlist
  if(daq->provider->readItemList(daq->itemlist) != 0)
  {
    // cleanup
    delete daq->provider;
    delete daq->mbx;
    return -1;
  }

  daq->running = 1;

  // create mbxReaderThread
  thread.create(mbxReaderThread, daq);

  // poll data
  pollData(daq, &thread);

  // cleanup
  delete daq->provider;
  delete daq->mbx;

  return 0;
}

//### the main program follows ################################################

static void printusage(char *filename)
{
  printf("Usage: %s <-itemlist=filename> <-shm=filename> <-mbx=filename> <-sleep=milliseconds> <-max_name_length=char> <-shmsize=bytes> <-debug>\n\n", filename);

  printf("Defaults:\n");
  printf("-itemlist=dataacquistion.itemlist                                                              # may be created by Browse\n");
  printf("-shm=/srv/automation/shm/dataacquistion.shm OR c:\\automation\\shm\\dataacquistion.shm on windows # location of the shared memory\n");
  printf("-mbx=/srv/automation/mbx/dataacquistion.mbx OR c:\\automation\\mbx\\dataacquistion.mbx on windows # location of the mailbox\n");
  printf("-sleep=1000                                                                                    # time between read calls\n");
  printf("-max_name_length=31                                                                            # max length of result name\n"); 
  printf("-shmsize=65536                                                                                 # total size of the shared memory\n\n");
}

#define MAX_PATH_LENGTH 1024

int main(int argc, char *argv[])
{
  char *arg;
  int   i;
  PV_DAQ_PARAM daq;

  // set the default parameters
  daq.mbx      = NULL;
  daq.provider = NULL;
  daq.running  = 0;
  strcpy(daq.itemlist,"dataacquistion.itemlist");
#ifdef _WIN32
  strcpy(daq.shmname,"c:\\automation\\shm\\dataacquistion.shm");
  strcpy(daq.mbxname,"c:\\automation\\mbx\\dataacquistion.mbx");
#else
  strcpy(daq.shmname,"/srv/automation/shm/dataacquistion.shm");
  strcpy(daq.mbxname,"/srv/automation/mbx/dataacquistion.mbx");
#endif
  daq.sleep           = 1000;
  daq.max_name_length = 31;
  daq.shmsize         = 65536;
  daq.debug           = 0;

  // check the command line parameters
  // TODO: you may add your own parameters
  for(i=1; i<argc; i++)
  {
    arg = argv[i];
    if(strlen(arg) <= MAX_PATH_LENGTH)
    {
      if(strncmp(arg,"-itemlist=",10) == 0)
      {
        sscanf(arg,"-itemlist=%s",daq.itemlist);
      }
      if(strncmp(arg,"-shm=",5) == 0)
      {
        sscanf(arg,"-shm=%s",daq.shmname);
      }
      if(strncmp(arg,"-mbx=",5) == 0)
      {
        sscanf(arg,"-mbx=%s",daq.mbxname);
      }
      if(strncmp(arg,"-sleep=",7) == 0)
      {
        sscanf(arg,"-sleep=%d",&daq.sleep);
        if(daq.sleep < 10) daq.sleep = 10;
      }
      if(strncmp(arg,"-max_name_length=",17) == 0)
      {
        sscanf(arg,"-max_name_length=%d",&daq.max_name_length);
        if(daq.max_name_length < 31) daq.max_name_length = 31;
      }
      if(strncmp(arg,"-shmsize=",8) == 0)
      {
        sscanf(arg,"-shmsize=%ld",&daq.shmsize);
        if(daq.shmsize < 128) daq.shmsize = 128;
      }
      if(strcmp(arg,"-debug") == 0)
      {
        daq.debug = 1;
      }
      if(strncmp(arg,"-h",2) == 0)
      {
        printusage(argv[0]);
        return 0;
      }
    }
    else
    {
      printf("arg is too long arg=%s\n", arg);
    }
  }

  return run(&daq); // here the work is done
}

Back Content Forward