CDEV interface to a coda 2.0 run control server


CODA release 2.0 allows application programmers to communicate with a coda run control server through CDEV interface. A device in cdev is regarded as a named entity which can respond to a set of messages. All I/O requests in the system are in the form of messages to devices. In cdev all interface take on the following forms:

	cdevData out, result;
	cdevCallback callback (userFunc, 0);
	cdevDevice& device = cdevDevice::attachRef ("session");
	device.send ("message", out, result);
	device.sendNoBlock ("message", out, result);
	device.sendCallback ("message", out, callback);

In order to communicate with a run control server, an application has to use session name of a run control as a device name in the cdev functions, and to use the following verbs and messages inside those send functions.

	verbs: get set monitorOn monitorOff
	attributes:     version
			startTime
			endTime
			runTypeNum
			clientList
			runMessage
			status
			runNumber
			time
			updateInterval
			components
			runType
			allRunTypes
			database
			hostName
			eventLimit
			dataLimit
			master
	messages:       load
			configure
			download
			prestart
			pause
			end
			reset
			go
			abort
			state
			disconnect
			connect
			connected
In addition, an application has to have EXPID environment variable set to appropriate database, has to have DEVSHOBJ pointing to the right cdev library directory and has to have CDEVDDL pointing to coda.ddl came with coda release. The following is a very simple application using cdev interface to communicate with a run control server.
#include <cdevSystem.h>
#include <cdevRequestObject.h>
#include <cdevDevice.h>
#include <cdevGroup.h>
#include <cdevErrCode.h>
#include <unistd.h>


// network file descriptor open/close callback
static void fdCallback (int fd, int opened, void* arg)
{
  if (opened == 0) {
    fprintf (stderr, "file descriptor %d is closed\n", fd);
  }
  else {
    fprintf (stderr, "file descriptor %d is open\n", fd);
  }
}

// command callback
static void comdCallback (int status, void *userarg,
   		          cdevRequestObject &obj, cdevData& result)
{
  printf ("command callback called======================\n");
}
		     

// utility for manipulating cdevData
static int insertData (cdevData& out, int type, int count)
{
  int dataStatus = 0;
  if (count == 1){
    switch (type){
    case 0:
      {
	char temp[40];
	printf ("Enter char string\n");
	scanf ("%s",temp);
	dataStatus = out.insert ("value", temp);
      }
      break;
    case 1:
    case 5:
      {
	int val;
	printf ("Enter Integer Value \n");
	scanf ("%d", &val);
	dataStatus = out.insert ("value", val);
      }
      break;
    case 2:
      {
	float  fval;
	printf ("Enter double value \n");
	scanf ("%f", &fval);
	dataStatus = out.insert ("value", fval);
      }
    case 6:
      {
	double val;
	float  fval;
	printf ("Enter double value \n");
	scanf ("%f", &fval);
	val = (double)fval;
	dataStatus = out.insert ("value", val);
      }
      break;
    default:
      printf ("unsupported data type %d\n", type);
      break;
    }
  }
  else {
    switch (type){
    case 0:
      {
	static counter = 0;
	char **temp = new char* [count];
	for (int i = 0; i < count; i++){
	  temp[i] = new char[40];
	  sprintf (temp[i], "Hello again there %d", i * counter);
	}
	dataStatus = out.insert ("value",temp, count);
	for (i = 0; i < count; i++)
	  delete []temp[i];
	delete []temp;
	counter++;
	if (counter > 10)
	  counter = 0;
      }
      break;
    case 1:
    case 5:
      {
	static counter = 1;
	int *temp = new int[count];
	for (int i = 0; i < count; i++)
	  temp[i] = i*counter;
	dataStatus = out.insert ("value",temp, count);
	delete []temp;
	counter++;
	if (counter > 3)
	  counter = 1;
      }
      break;
    case 6:
      {
	static counter = 1;
	double *temp = new double[count];
	for (int i = 0; i < count; i++)
	  temp[i] = i*(counter);
	dataStatus = out.insert ("value",temp, count);
	delete []temp;
	counter++;
	if (counter > 3)
	  counter = 1;
      }      
      break;
    default:
      break;
    }
  }
  return dataStatus;
}

// print out cdevData
static void printValue (cdevData& result)
{
  int type;
  int tag;
  size_t dim, len1;
  result.tagC2I ("value", &tag);
  int st = result.getDim (tag, &dim);
  if (dim != 0)
    {
    // ***********************************************************************
    // * cdevBounds support added for support of arbitrary dimensional data
    // ***********************************************************************
    cdevBounds bounds;
    st = result.getBounds (tag, &bounds, 1);
    len1 = bounds.length;
    }
  else
    len1 = 1;
  printf ("Length is %d\n", len1);
  type = result.getType (tag);
  printf ("data type is %d\n",type);
  float lval, hval;
  st = result.get ("displayHigh", &hval);
  if (st == CDEV_SUCCESS)
    printf ("Display High limit is %f\n", hval);
  st = result.get ("displayLow", &lval);
  if (st == CDEV_SUCCESS)
    printf ("Display Low Limit is %f\n", lval);
  char *stt;
  st = result.get ("status", &stt);
  if (st == CDEV_SUCCESS)
    printf ("status of value is %s\n", stt);
  char *sver;
  st = result.get ("severity", &sver);
  if (st == CDEV_SUCCESS)
    printf ("severity of value is %s\n", sver);
  cdev_TS_STAMP tsm;
  st = result.get ("time", &tsm);
  if (st == CDEV_SUCCESS) {
    printf ("time value sec is %d nanosec is %d\n",tsm.secPastEpoch,
	    tsm.nsec);
    printf ("In dat %s\n",ctime ((const time_t *)&tsm.secPastEpoch));
  }
  switch (type){
  case CDEV_INT32:
    if (len1 == 1){
      int retVal;
      result.get (tag, &retVal);
      printf ("result is 0x%x %d\n",retVal, retVal);
    }
    else{
      int *retVal = new int[len1];
      result.get (tag, retVal);
      printf ("result is \n");
      for (int i = 0; i < len1; i++)
	printf ("%d\n", retVal[i]);
      printf ("__________________\n");
      delete []retVal;
    }
    break;
  case CDEV_FLOAT:
    if (len1 == 1) {
      float dval;
      result.get (tag, &dval);
      printf ("result is %f\n", dval);
    }
    else {
      float *dval = new float[len1];
      result.get (tag, dval);
      for (int i = 0; i < len1; i++)
	printf ("%f\n", dval[i]);
      printf ("__________________\n");
      delete []dval;	  
    }
    break;
  case CDEV_DOUBLE:
    if (len1 == 1) {
      double dval;
      result.get (tag, &dval);
      printf ("result is %f\n", dval);
    }
    else {
      double *dval = new double[len1];
      result.get (tag, dval);
      for (int i = 0; i < len1; i++)
	printf ("%f\n", dval[i]);
      printf ("__________________\n");
      delete []dval;	  
    }
    break;
  case CDEV_STRING:
    if (len1 == 1){
      char temp[40];
      result.get (tag,temp, sizeof (temp));
      printf ("result is %s\n", temp);
    }
    else{
      char **temp;
      temp = new char* [len1];
      
      result.get (tag, temp);
      
      printf ("Strings are \n");
      for (int i = 0; i < len1; i++){
	printf ("%s\n",temp[i]);
	delete []temp[i];
      }
      delete []temp;
    }
    break;
  default:
    printf ("Something is wrong\n");
    break;
  }
}

static void myCallback (int status, void *userarg,
			cdevRequestObject &obj, cdevData& result);

static void myCallback2 (int status, void *userarg,
			 cdevRequestObject &obj, cdevData& result);

static void myCallback3 (int status, void *userarg,
			 cdevRequestObject &obj, cdevData& result);

static void mySetCallback (int status, void *userarg,
			cdevRequestObject &obj, cdevData& result);

static int done = 0;

cdevCallback getCallback(myCallback, 0);
cdevCallback getCallback1 (myCallback2, 0);
cdevCallback getCallback2 (myCallback3, 0);
cdevRequestObject* monObj = 0;

main (int argc, char **argv)
{
  cdevSystem& system = cdevSystem::defaultSystem ();
  cdevCallback setCallback(mySetCallback, (void *)0);

  system.addFdChangedCallback (fdCallback, 0);

  char command[40];
  char deviceName[40], messageName[64], attname[30];
  char msgPtr[32], message[128];
  cdevData result;
  cdevData cxt;

  if (argc < 2) {
    fprintf (stderr, "Usage: %s codaSession\n", argv[0]);
    exit (1);
  }

  // the following is to notify the cdev directory service to add 
  // the session as a device
  cdevDevice& ns = cdevDevice::attachRef ("cdevDirectory");
  cdevData out;
  char ddlstr[80];
  sprintf (ddlstr, "RCS : %s;", argv[1]);
  out.insert ("value", ddlstr);
  int stt = ns.send ("update", out, result);

  while (!done){
    fprintf (stdout, "cdevTest Command>");
    scanf ("%s", command);
    if (::strcmp (command, "get") == 0){
      printf ("cdevTest devicename attribute> \n");
      scanf ("%s %s",deviceName, attname);
      ::strcpy (messageName, command);
      ::strcat (messageName, " ");
      ::strcat (messageName, attname);
      printf ("device: %s message: %s\n",deviceName, messageName);
      cdevRequestObject *req = cdevRequestObject::attachPtr (deviceName,
							     messageName);
      result.remove ();
      int st = req->send (0, result);
      if (st == CDEV_SUCCESS)
	printValue (result);
      else{
	printf("send failed-----\n");
      }
      cdevRequestObject::detach (req);
    }
    else if (::strcmp (command, "set") == 0){
      printf ("cdevTest devicename attribute> \n");
      scanf ("%s %s",deviceName, attname);
      ::strcpy (messageName, command);
      ::strcat (messageName, " ");
      ::strcat (messageName, attname);
      cdevRequestObject& req = 
	cdevRequestObject::attachRef (deviceName, messageName);
      int type, count;
      printf ("Supported type are: \n");
      printf ("0: string. 1: short. 2: float, 5: long, 6: double\n");
      printf ("Enter type and count for this channel\n");
      scanf ("%d %d",&type, &count);
      cdevData out;
      int dataStatus = insertData (out, type, count);
      if (dataStatus == CDEV_SUCCESS){
	int st = req.send (out, 0);
	if (st == CDEV_SUCCESS)
	  printf ("Set success\n");
	else
	  printf ("Set failed\n");
	cdevRequestObject::detach (req);
      }
      else 
	printf ("Data Insersion Error\n");
    }
    else if (::strcmp (command, "monitorOn") == 0){
      printf ("cdevTest devicename attribute> \n");
      scanf ("%s %s",deviceName, attname);
      ::strcpy (messageName, command);
      ::strcat (messageName, " ");
      ::strcat (messageName, attname);
      monObj = cdevRequestObject::attachPtr (deviceName, messageName);
      int st;
      cdevGroup grp;
      grp.start ();
      st = monObj->sendCallback (0, getCallback);
      grp.end ();
      st = grp.pend (10.0);
      if (grp.allFinished ())
	printf ("Monitor On success\n");
      else
	printf ("MonitorOn failed\n");
      // system is doing monitoring for 20 seconds
      system.pend (20.0);
      cdevRequestObject::detach (monObj);
      monObj = 0;
    }
    else if (::strcmp (command, "load") == 0) {
      printf ("device: %s message: %s\n",deviceName, command);
      cdevRequestObject *req = cdevRequestObject::attachPtr (deviceName,
							     command);

      cdevData out;
      char *extra[2];
      extra[0] = ::getenv ("EXPID");
      extra[1] = deviceName;
      out.insert ("value", extra, 2);

      cdevCallback commandCallback(comdCallback, (void *)0);
      int st = req->sendCallback (out, commandCallback);
      if (st == CDEV_SUCCESS)
	printf ("Send successful-----\n");
      else
	printf("send failed-----\n");
      system.pend (2.0);
      cdevRequestObject::detach (req);
    }
    else if (::strcmp (command, "configure") == 0) {
      printf ("cdevTest runtype> \n");
      scanf ("%s",message);
      printf ("device: %s message: %s\n",deviceName, command);
      cdevRequestObject *req = cdevRequestObject::attachPtr (deviceName,
							     command);
      cdevData out;
      out.insert ("value", message);
      int st = req->send (out, 0);
      if (st == CDEV_SUCCESS)
	printf ("Send successful-----\n");
      else
	printf("send failed-----\n");
      cdevRequestObject::detach (req);
    }
    else if (::strcmp (command, "download") == 0) {
      cdevRequestObject *req = cdevRequestObject::attachPtr (deviceName,
							     command);
      int st = req->send (0, 0);
      if (st == CDEV_SUCCESS)
	printf ("Send successful-----\n");
      else
	printf("send failed-----\n");
      cdevRequestObject::detach (req);
    }
    else if (::strcmp (command, "prestart") == 0) {
      cdevRequestObject *req = cdevRequestObject::attachPtr (deviceName,
							     command);
      int st = req->send (0, 0);
      if (st == CDEV_SUCCESS)
	printf ("Send successful-----\n");
      else
	printf("send failed-----\n");
      cdevRequestObject::detach (req);
    }
    else if (::strcmp (command, "pause") == 0) {
      cdevRequestObject *req = cdevRequestObject::attachPtr (deviceName,
							     command);
      int st = req->send (0, 0);
      if (st == CDEV_SUCCESS)
	printf ("Send successful-----\n");
      else
	printf("send failed-----\n");
      cdevRequestObject::detach (req);
    }
    else if (::strcmp (command, "end") == 0) {
      cdevRequestObject *req = cdevRequestObject::attachPtr (deviceName,
							     command);
      int st = req->send (0, 0);
      if (st == CDEV_SUCCESS)
	printf ("Send successful-----\n");
      else
	printf("send failed-----\n");
      cdevRequestObject::detach (req);
    }
    else if (::strcmp (command, "reset") == 0) {
      cdevRequestObject *req = cdevRequestObject::attachPtr (deviceName,
							     command);
      int st = req->send (0, 0);
      if (st == CDEV_SUCCESS)
	printf ("Send successful-----\n");
      else
	printf("send failed-----\n");
      cdevRequestObject::detach (req);
    }
    else if (::strcmp (command, "go") == 0) {
      cdevRequestObject *req = cdevRequestObject::attachPtr (deviceName,
							     command);
      int st = req->send (0, 0);
      if (st == CDEV_SUCCESS)
	printf ("Send successful-----\n");
      else
	printf("send failed-----\n");
      cdevRequestObject::detach (req);
    }
    else if (::strcmp (command, "abort") == 0) {
      cdevRequestObject *req = cdevRequestObject::attachPtr (deviceName,
							     command);
      int st = req->send (0, 0);
      if (st == CDEV_SUCCESS)
	printf ("Send successful-----\n");
      else
	printf("send failed-----\n");
      cdevRequestObject::detach (req);
    }
    else if (::strcmp (command, "state") == 0) {
      cdevRequestObject *req = cdevRequestObject::attachPtr (deviceName,
							     command);
      int st = req->send (0, result);
      void *temp;
      result.find ("value", temp);
      if (st == CDEV_SUCCESS)
	printf ("result is %s\n", (char *)temp);
      else
	printf("send failed-----\n");
      cdevRequestObject::detach (req);
    }      
    else if (::strcmp (command, "quit") == 0)
      done = 1;
    else {
      printf ("Legal commands are: get, quit\n");
    }
  }

}    

static void myCallback (int status, void *userarg,
			cdevRequestObject &obj, cdevData& result)
{
  printf ("user get callback called++++++++\n");
  printValue (result);
}

static void myCallback2 (int status, void *userarg,
			cdevRequestObject &obj, cdevData& result)
{
  printf ("user get callback2 called ##############################\n");
  printValue (result);
  if (monObj) {
    cdevRequestObject& reqObj = cdevRequestObject::attachRef (monObj->device(),
							      "monitorOff nevents");
    reqObj.sendCallback (0, getCallback1);
    cdevRequestObject::detach (reqObj);
  }
}

static void myCallback3 (int status, void *userarg,
			cdevRequestObject &obj, cdevData& result)
{
  printf ("user get callback3 called++++++++++++++++\n");
  printValue (result);
}

static void mySetCallback (int status, void *userarg,
			cdevRequestObject &obj, cdevData& result)
{
  printf ("user set callback called======================\n");
}


Of course, applications may use cdev to communicate not only with a run control server but also with epics through channel access service released with standard cdev distribution. For further information about using multiple cdev services, please check with CDEV references.