//-----------------------------------------------------------------------------
// Copyright (c) 1994,1995 Southeastern Universities Research Association,
//                         Continuous Electron Beam Accelerator Facility
//
// This software was developed under a United States Government license
// described in the NOTICE file included as part of this distribution.
//
//-----------------------------------------------------------------------------
//
// Description:
//      Channel Access RequestObject Class
//
// Author:  Jie Chen
//
// Revision History:
//   caRequestObject.cc,v
// Revision 1.34  1998/04/10  13:55:41  chen
// add access information to caService
//
// Revision 1.33  1998/04/08  20:19:05  chen
// fix set problem
//
// Revision 1.32  1998/03/30  19:39:53  chen
// bug fix for channel_ null pointer inside destructor
//
// Revision 1.31  1998/03/23  21:17:18  chen
// Fix a bug related to prop channels: found on solaris
//
// Revision 1.30  1998/03/09  15:45:40  chen
// Install ENUM no_str into displayHigh for ENUM type
//
// Revision 1.29  1998/03/05  18:49:13  chen
// fix a bug on context == 2 on property channels
//
// Revision 1.28  1998/03/03  20:50:21  chen
// Return array of strings if one asks about display information on DBR_ENUM channel
//
// Revision 1.27  1998/02/18  19:48:26  chen
// fix ca set access error
//
// Revision 1.26  1997/08/08  16:41:36  chen
// add char type and fix dbr == null
//
// Revision 1.25  1997/07/18  14:24:44  chen
// remove one printf
//
// Revision 1.24  1997/03/03  17:36:28  chen
// add buffering to channel access connection
//
// Revision 1.23  1996/09/18  14:46:50  chen
// Channel holds multiple monitor objects
//
// Revision 1.22  1996/07/23  15:59:14  chen
// send with monitorOff will remove all monitored callbacks
//
// Revision 1.21  1996/05/02  14:18:26  chen
// Fix monitorOn and unfinished transaction bug
//
// Revision 1.20  1996/01/05  18:40:05  chen
// Fix a bug for gcc and a bug for monitoring enum values
//
// Revision 1.19  1995/12/08  15:42:28  chen
// fix double/float set value and add deferred execution mode
//
// Revision 1.18  1995/11/07  14:26:57  akers
// Added CTRLLO and CTRLHI to populateMonitor
//
// Revision 1.17  1995/10/30  13:33:05  akers
// Added cdev specific version of strncpy
//
// Revision 1.16  1995/10/26  14:33:14  akers
// Removal of Warnings
//
// Revision 1.15  1995/10/17  20:20:51  chen
// redefine getState
//
// Revision 1.14  1995/10/13  14:42:37  chen
// add debug flag in the monitorProperty
//
// Revision 1.13  1995/10/03  19:56:25  chen
// add monitoring properties
//
// Revision 1.12  1995/09/19  16:06:36  chen
// minor change on bit monitoring
//
// Revision 1.11  1995/09/18  18:22:49  chen
// add monitoring bit pattern capability for long/short integer
//
// Revision 1.10  1995/09/15  19:27:28  chen
// Fix a bug of array of doubles
//
// Revision 1.1.1.1  1995/06/16  17:14:02  epics
// initial import of cdev
//
//
#include <cdevTranObj.h>
#include <cdevGroup.h>
#include <cdevErrCode.h>
#include <cdevCommon.h>
#include "caRequestObject.h"
#include "caNameSvc.h"
#include "caService.h"
#include "caChannel.h"
#include "caMisc.h"

#ifndef TS_EPOCH_SEC_PAST_1970
/* 1/1/90 20 yr (5 leap) of seconds */
#define TS_EPOCH_SEC_PAST_1970 7305*86400 
#endif

int caRequestObject::numAttributes = 14;

caAttr caRequestObject::caAttributes[]=
{
  {"value",       0x00000001},
  {"status",      0x00000002},
  {"severity",    0x00000004},
  {"time",        0x00000008},
  {"precision",   0x00000010},
  {"units",       0x00000020},
  {"displayHigh", 0x00000040},
  {"displayLow",  0x00000080},
  {"alarmHigh",   0x00000100},
  {"alarmLow",    0x00000200},
  {"warningHigh", 0x00000400},
  {"warningLow",  0x00000800},
  {"controlHigh", 0x00001000},
  {"controlLow",  0x00002000},
};

//=========================================================================
//          Implementation of caRequestObject
//=========================================================================
caRequestObject::caRequestObject (char *device, char *msg, 
				  caService *,
				  cdevSystem& system)
:cdevRequestObject (device, msg, system), cache_ (0), prop_ (0), readonly_ (0),
 reqType_ (caRequestObject::VALUE), reqMask_ (0x1), channel_ (0)
{
#ifdef _TRACE_OBJECTS
  printf ("              Create caRequestObject class \n");
#endif
  caRequestObject::findAction (msg, action_);
}

caRequestObject::~caRequestObject (void)
{
#ifdef _TRACE_OBJECTS
  printf ("              Delete caRequestObject class \n");
#endif
  caService *svc = (caService *)service_;
  if (channel_ && --channel_->count_ <= 0){// no more reference to the channel
    svc->removeChannel (channel_->channelName_, channel_);
    delete channel_;
  }
  // reset the pointer of cached properties to zero
  if (cache_) {
    delete cache_;
    cache_ = 0;
  }
  if (prop_) {
    delete prop_;
    prop_ = 0;
  }
}

void
caRequestObject::attachChannel (void)
{
  int status;
  caService* svc = (caService *)service_;
  caPchannel* pchs = 0;

  // caller may need to monitor properties, setup properties cache buffer
  // order of calls is important: We have to create property channel before
  // we create main channel
  if (reqType_ != caRequestObject::VALUE 
      && action_ == caRequestObject::MONITOR_ON) {
    setPropCache ();
    pchs = caChannel::createPchannels ();
    // create all pchannels 
    initPropChannels (pchs);

    // flush out the property channel connection requests
    svc->flush ();
  }

  // get service data first
  char svcData[64];
  status = findSvcData (deviceName_, message_, system_,
			svcData, sizeof (svcData));

  status = svc->channelPtr (svcData, channel_);

  if (status != 0){ // the channel has not created yet
#ifdef _CDEV_DEBUG
    printf("channel %s not created \n", svcData);
#endif
    channel_ = new caChannel (svcData, svc);
    svc->addChannel (svcData, channel_);
    channel_->count_ = 1;
  }
  else{
#ifdef _CDEV_DEBUG
    printf("channel %s is created \n", svcData);
#endif
    channel_->count_ += 1;
#ifdef _CA_SYNC_CONN
    if (!channel_->connected_){ // channel not connected
      channel_->connect ();
    }
#endif
  }

  // assign pchannels to this channel
  if (pchs)
    channel_->propertyChannels (pchs);
}  

int
caRequestObject::getState (void)
{
  if (channel_->connected_)
    return CDEV_STATE_CONNECTED;
  else
    return CDEV_STATE_NOTCONNECTED;
}

int
caRequestObject::getAccess (void)
{
  return channel_->readonly_;
}

int
caRequestObject::setContext (cdevData &cxt)
{
  int temp;

  // clean the mask
  reqMask_ = 0x0;
  reqType_ = caRequestObject::VALUE;
  // get bitmask from cdev requestobject layer
  (void)cdevRequestObject::setContext (cxt);

  for (int i = 0; i < caRequestObject::numAttributes; i++){
    if (cxt.get ((caRequestObject::caAttributes[i]).attrName, &temp) == CDEV_SUCCESS){
      reqMask_ |= (caRequestObject::caAttributes[i]).attrValue;
    }
  }

  if (reqMask_ == 0x0)
    reqType_ = caRequestObject::VALUE;
  else if (reqMask_ >= caRequestObject::VALUE &&
      reqMask_ < caRequestObject::STATUS)
    reqType_ = caRequestObject::VALUE;
  else if (reqMask_ >= caRequestObject::STATUS &&
	   reqMask_ < caRequestObject::TIMESTMP)
    reqType_ = caRequestObject::STATUS;
  else if (reqMask_ >= caRequestObject::TIMESTMP &&
	   reqMask_ <  caRequestObject::GRINFO)
    reqType_ = caRequestObject::TIMESTMP;
  else if (reqMask_ >= caRequestObject::GRINFO &&
	   reqMask_ < caRequestObject::CTRLINFO)
    reqType_ = caRequestObject::GRINFO;
  else
    reqType_ = caRequestObject::CTRLINFO;
#ifdef _CDEV_DEBUG
  printf("reqType_ is %d mask is 0x%x \n", reqType_, reqMask_);
#endif
  return CDEV_SUCCESS;
}

int
caRequestObject::getValueCbk (caChannel* channel, cdevTranObj *obj)
{
  int status;
  chtype reqType;

  if (reqType_ != caRequestObject::TIMESTMP && channel->type_ != DBR_STRING)
    reqType = dbf_type_to_DBR_CTRL (channel->type_);
  else
    reqType = dbf_type_to_DBR_TIME (channel->type_);

  status = ::ca_array_get_callback (reqType, channel->numElements_,
				    channel->cId_,
				    &(caRequestObject::defaultGetCallback),
				    (void *)obj);
  if (status == ECA_NORMAL)
    return CDEV_SUCCESS;
  else { 
    // no callback will be called, remove transaction object
    // otherwise group will block
    cdevCallback *cbkobj = obj->userCallback_;
    cdevRequestObject *reqobj = obj->reqObj_;
    cdevData result;
    
    if (cbkobj)
      (*cbkobj->callbackFunction())(CDEV_IOFAILED, cbkobj->userarg(),
				    *reqobj, result);
    delete obj;
    return CDEV_IOFAILED;
  }
}

int
caRequestObject::send (cdevData& out, cdevData& result)
{
  return send (&out, &result);
}

int
caRequestObject::send (cdevData* out, cdevData& result)
{
  return send (out, &result);
}

int
caRequestObject::send (cdevData& out, cdevData* result)
{
  return send (&out, result);
}

int
caRequestObject::send (cdevData* out, cdevData* result)
{
  int status = CDEV_ERROR;

  // check channel has been created or not
  if (!channel_)
    attachChannel ();

#ifndef _CA_SYNC_CONN
  int st = channel_->asyncConnect ();
  if (st != CDEV_SUCCESS)
    return st;
#endif
  if (channel_->connected_){
    switch (action_){
    case caRequestObject::GET:
      if (result == 0)
	return CDEV_INVALIDARG;
      {
	cdevGroup getGrp (2, system_);
	getGrp.start();
	sendNoBlock (out, result);
	getGrp.end ();
	// wait for maximum DEFAULT_TIMEOUT
	getGrp.pend ((double)DEFAULT_TIMEOUT);
	if (getGrp.allFinished ())
	  status = CDEV_SUCCESS;
	else
	  status = CDEV_TIMEOUT;
      }
      break;
    case caRequestObject::SET:
      if (out == 0)
	return CDEV_INVALIDARG;
      if (readonly_)
	return CDEV_INVALIDARG;
      // synchronous set need group
      {
	cdevGroup setGrp (2, system_);
	setGrp.start();
	sendNoBlock (out, result);
	setGrp.end ();
	// wait for maximum DEFAULT_TIMEOUT
	setGrp.pend ((double)DEFAULT_TIMEOUT);
	if (setGrp.allFinished ()) {
	  if (out->get (caService::CA_TAG_RESULT_CODE, &status)!= CDEV_SUCCESS)
	    status = CDEV_SUCCESS;
	  else 
	    out->remove (caService::CA_TAG_RESULT_CODE);
	}
	else
	  status = CDEV_TIMEOUT;
      }
      break;
    case caRequestObject::MONITOR_ON:
      status = CDEV_INVALIDARG;
      break;
    case caRequestObject::MONITOR_OFF:
      {
	// remove every monitor callbacks
	// remove a monitor object from all the channels
	cdevCallback userCallback (0, 0);
	channel_->removeCallback (&userCallback);
	if (channel_->pinited_) {
	  caPchannel* pch = channel_->pchannels_;
       
	  for (int i = 0; i < MAX_NUM_PCHANNELS; i++) {
	    if (pch[i].channel != 0) 
	      pch[i].channel->removeCallback (&userCallback);
	  }
	}
      }
      status = CDEV_SUCCESS;
      break;
    default:
#ifdef _CDEV_DEBUG
      printf("Unsupported action message %s\n", action_);
#endif
      system_.reportError (CDEV_SEVERITY_ERROR, deviceName_, this, 
			   "%s %s requestObject does not support message %d\n",
			   deviceName_, message_, action_);
      status = CDEV_INVALIDARG;
      break;
    }
  }
  else{
#ifdef _CDEV_DEBUG
    printf("channel disconnected\n");
#endif
    system_.reportError (CDEV_SEVERITY_ERROR, deviceName_, this, 
			 "%s %s requestObject is not connected\n",
			 deviceName_, message_);
    status = CDEV_NOTCONNECTED;
  }
  return status;
}

void
caRequestObject::updateResult (int reqMask, int type,
			       struct dbr_time_short& value, 
			       caMonData &result,
			       caMonData* cache)
{
  int *val = (int *)(result.value);
  *val = value.value;
  if (type != caRequestObject::VALUE) {
    int i = 1;
    if (reqMask & (0x1 << i++))
      *(result.status) = (int)(value.status);
    if (reqMask & (0x1 << i++))
      *(result.severity) = (int)(value.severity);

    cdev_TS_STAMP* tval = result.tstamp;
    tval->secPastEpoch = TS_EPOCH_SEC_PAST_1970
      + (unsigned long)value.stamp.secPastEpoch;
    tval->nsec = value.stamp.nsec;

    // timeStmp skipped epics fault  + unit tag + precision tag
    i += 3;

    if (reqMask & (0x1 << i++))  
      *(result.disphi) = *(cache->disphi);
    if (reqMask & (0x1 << i++))  
      *(result.displo) = *(cache->displo);
    if (reqMask & (0x1 << i++))  
      *(result.alrmhi) =  *(cache->alrmhi);
    if (reqMask & (0x1 << i++))  
      *(result.alrmlo) = *(cache->alrmlo);
    if (reqMask & (0x1 << i++))  
      *(result.warnhi) = *(cache->warnhi);
    if (reqMask & (0x1 << i++))  
      *(result.warnlo) = *(cache->warnlo);
    if (reqMask & (0x1 << i++))  
      *(result.ctrlhi) = *(cache->ctrlhi);
    if (reqMask & (0x1 << i++)) 
      *(result.ctrllo) = *(cache->ctrllo);
  }
}


void
caRequestObject::updateResult (int reqMask, int type,
			       struct dbr_time_char& value, 
			       caMonData &result,
			       caMonData* cache)
{
  BYTE *val = (BYTE *)(result.value);
  *val = (BYTE)value.value;
  if (type != caRequestObject::VALUE) {
    int i = 1;
    if (reqMask & (0x1 << i++))
      *(result.status) = (int)(value.status);
    if (reqMask & (0x1 << i++))
      *(result.severity) = (int)(value.severity);

    cdev_TS_STAMP* tval = result.tstamp;
    tval->secPastEpoch = TS_EPOCH_SEC_PAST_1970
      + (unsigned long)value.stamp.secPastEpoch;
    tval->nsec = value.stamp.nsec;

    // timeStmp skipped epics fault  + unit tag + precision
    i += 3;

    if (reqMask & (0x1 << i++))  
      *(result.disphi) = *(cache->disphi);
    if (reqMask & (0x1 << i++))  
      *(result.displo) = *(cache->displo);
    if (reqMask & (0x1 << i++))  
      *(result.alrmhi) =  *(cache->alrmhi);
    if (reqMask & (0x1 << i++))  
      *(result.alrmlo) = *(cache->alrmlo);
    if (reqMask & (0x1 << i++))  
      *(result.warnhi) = *(cache->warnhi);
    if (reqMask & (0x1 << i++))  
      *(result.warnlo) = *(cache->warnlo);
    if (reqMask & (0x1 << i++))  
      *(result.ctrlhi) = *(cache->ctrlhi);
    if (reqMask & (0x1 << i++)) 
      *(result.ctrllo) = *(cache->ctrllo);
  }
}

void
caRequestObject::updateResult (int reqMask, int type,
			       struct dbr_time_long& value, 
			       caMonData &result,
			       caMonData* cache)
{
  int *val = (int *)(result.value);
  *val = value.value;
  if (type != caRequestObject::VALUE) {
    int i = 1;
    if (reqMask & (0x1 << i++))
      *(result.status) = (int)(value.status);
    if (reqMask & (0x1 << i++))
      *(result.severity) = (int)(value.severity);

    cdev_TS_STAMP* tval = result.tstamp;
    tval->secPastEpoch = TS_EPOCH_SEC_PAST_1970
      + (unsigned long)value.stamp.secPastEpoch;
    tval->nsec = value.stamp.nsec;

    // timeStmp skipped epics fault  + unit tag + precision
    i += 3;

    if (reqMask & (0x1 << i++))  
      *(result.disphi) = *(cache->disphi);
    if (reqMask & (0x1 << i++))  
      *(result.displo) = *(cache->displo);
    if (reqMask & (0x1 << i++))  
      *(result.alrmhi) =  *(cache->alrmhi);
    if (reqMask & (0x1 << i++))  
      *(result.alrmlo) = *(cache->alrmlo);
    if (reqMask & (0x1 << i++))  
      *(result.warnhi) = *(cache->warnhi);
    if (reqMask & (0x1 << i++))  
      *(result.warnlo) = *(cache->warnlo);
    if (reqMask & (0x1 << i++))  
      *(result.ctrlhi) = *(cache->ctrlhi);
    if (reqMask & (0x1 << i++)) 
      *(result.ctrllo) = *(cache->ctrllo);
  }
}

void
caRequestObject::updateResult (int reqMask, int type,
			       struct dbr_time_float& value, 
			       caMonData &result,
			       caMonData* cache)
{
  float *val = (float *)(result.value);
  *val = value.value;
  if (type != caRequestObject::VALUE) {
    int i = 1;
    if (reqMask & (0x1 << i++))
      *(result.status) = (int)(value.status);
    if (reqMask & (0x1 << i++))
      *(result.severity) = (int)(value.severity);

    cdev_TS_STAMP* tval = result.tstamp;
    tval->secPastEpoch = TS_EPOCH_SEC_PAST_1970
      + (unsigned long)value.stamp.secPastEpoch;
    tval->nsec = value.stamp.nsec;

    // timeStmp skipped epics fault  + unit tag
    i += 2;

    if (reqMask & (0x1 << i++))  
      *(result.precision) = *(cache->precision);
    if (reqMask & (0x1 << i++))  
      *(result.disphi) = *(cache->disphi);
    if (reqMask & (0x1 << i++))  
      *(result.displo) = *(cache->displo);
    if (reqMask & (0x1 << i++))  
      *(result.alrmhi) =  *(cache->alrmhi);
    if (reqMask & (0x1 << i++))  
      *(result.alrmlo) = *(cache->alrmlo);
    if (reqMask & (0x1 << i++))  
      *(result.warnhi) = *(cache->warnhi);
    if (reqMask & (0x1 << i++))  
      *(result.warnlo) = *(cache->warnlo);
    if (reqMask & (0x1 << i++))  
      *(result.ctrlhi) = *(cache->ctrlhi);
    if (reqMask & (0x1 << i++)) 
      *(result.ctrllo) = *(cache->ctrllo);
  }
}

void
caRequestObject::updateResult (int reqMask, int type,
			       struct dbr_time_double& value, 
			       caMonData &result,
			       caMonData* cache)
{
  double *val = (double *)(result.value);
  *val = value.value;
  if (type != caRequestObject::VALUE) {
    int i = 1;
    if (reqMask & (0x1 << i++))
      *(result.status) = (int)(value.status);
    if (reqMask & (0x1 << i++))
      *(result.severity) = (int)(value.severity);

    cdev_TS_STAMP* tval = result.tstamp;
    tval->secPastEpoch = TS_EPOCH_SEC_PAST_1970
      + (unsigned long)value.stamp.secPastEpoch;
    tval->nsec = value.stamp.nsec;

    // timeStmp skipped epics fault  + unit tag
    i += 2;

    if (reqMask & (0x1 << i++))  
      *(result.precision) = *(cache->precision);
    if (reqMask & (0x1 << i++))  
      *(result.disphi) = *(cache->disphi);
    if (reqMask & (0x1 << i++))  
      *(result.displo) = *(cache->displo);
    if (reqMask & (0x1 << i++))  
      *(result.alrmhi) =  *(cache->alrmhi);
    if (reqMask & (0x1 << i++))  
      *(result.alrmlo) = *(cache->alrmlo);
    if (reqMask & (0x1 << i++))  
      *(result.warnhi) = *(cache->warnhi);
    if (reqMask & (0x1 << i++))  
      *(result.warnlo) = *(cache->warnlo);
    if (reqMask & (0x1 << i++))  
      *(result.ctrlhi) = *(cache->ctrlhi);
    if (reqMask & (0x1 << i++)) 
      *(result.ctrllo) = *(cache->ctrllo);
  }
}

void
caRequestObject::fillResult (int reqMask, int type,
			     struct dbr_time_string& value, 
			     cdevData &result)
{
  if (type != caRequestObject::VALUE) {
    int i = 1;
    if (reqMask & (0x1 << i++))
      result.insert (caService::CA_TAG_STATUS, (int)value.status);
    if (reqMask & (0x1 << i++))
      result.insert (caService::CA_TAG_SEVERITY, (int)value.severity);
  }

  cdev_TS_STAMP tval;
  tval.secPastEpoch = TS_EPOCH_SEC_PAST_1970
    + (unsigned long)value.stamp.secPastEpoch;
  tval.nsec = value.stamp.nsec;
  result.insert (caService::CA_TAG_TIME, tval);
}

void
caRequestObject::fillResult (int reqMask, int type,
			     struct dbr_time_short& value, 
			     cdevData &result)
{
  if (type != caRequestObject::VALUE) {
    int i = 1;
    if (reqMask & (0x1 << i++))
      result.insert (caService::CA_TAG_STATUS, (int)value.status);
    if (reqMask & (0x1 << i++))
      result.insert (caService::CA_TAG_SEVERITY, (int)value.severity);
  }

  cdev_TS_STAMP tval;
  tval.secPastEpoch = TS_EPOCH_SEC_PAST_1970
    + (unsigned long)value.stamp.secPastEpoch;
  tval.nsec = value.stamp.nsec;
  result.insert (caService::CA_TAG_TIME, tval);
}


void
caRequestObject::fillResult (int reqMask, int type,
			     struct dbr_time_char& value, 
			     cdevData &result)
{
  if (type != caRequestObject::VALUE) {
    int i = 1;
    if (reqMask & (0x1 << i++))
      result.insert (caService::CA_TAG_STATUS, (short)value.status);
    if (reqMask & (0x1 << i++))
      result.insert (caService::CA_TAG_SEVERITY, (short)value.severity);
  }

  cdev_TS_STAMP tval;
  tval.secPastEpoch = TS_EPOCH_SEC_PAST_1970
    + (unsigned long)value.stamp.secPastEpoch;
  tval.nsec = value.stamp.nsec;
  result.insert (caService::CA_TAG_TIME, tval);
}

void
caRequestObject::fillResult (int reqMask, int type,
			     struct dbr_time_long& value, 
			     cdevData &result)
{
  if (type != caRequestObject::VALUE) {
    int i = 1;
    if (reqMask & (0x1 << i++))
      result.insert (caService::CA_TAG_STATUS, (int)value.status);
    if (reqMask & (0x1 << i++))
      result.insert (caService::CA_TAG_SEVERITY, (int)value.severity);
  }

  cdev_TS_STAMP tval;
  tval.secPastEpoch = TS_EPOCH_SEC_PAST_1970
    + (unsigned long)value.stamp.secPastEpoch;
  tval.nsec = value.stamp.nsec;
  result.insert (caService::CA_TAG_TIME, tval);
}

void
caRequestObject::fillResult (int reqMask, int type,
			     struct dbr_time_float& value,
			     cdevData &result)
{
  if (type != caRequestObject::VALUE) {
    int i = 1;
    if (reqMask & (0x1 << i++)){
      result.insert (caService::CA_TAG_STATUS, (int)value.status);
    }
    if (reqMask & (0x1 << i++)){
      result.insert (caService::CA_TAG_SEVERITY, (int)value.severity);
    }
  }

  cdev_TS_STAMP tval;
  tval.secPastEpoch = TS_EPOCH_SEC_PAST_1970
    + (unsigned long)value.stamp.secPastEpoch;
  tval.nsec = value.stamp.nsec;
  result.insert (caService::CA_TAG_TIME, tval);
}

void
caRequestObject::fillResult (int reqMask, int type,
			     struct dbr_time_double& value, 
			     cdevData &result)
{
  if (type != caRequestObject::VALUE) {
    int i = 1;
    if (reqMask & (0x1 << i++)){
      result.insert (caService::CA_TAG_STATUS, (int)value.status);
    }
    if (reqMask & (0x1 << i++)){
      result.insert (caService::CA_TAG_SEVERITY, (int)value.severity);
    }
  }

  cdev_TS_STAMP tval;
  tval.secPastEpoch = TS_EPOCH_SEC_PAST_1970
    + (unsigned long)value.stamp.secPastEpoch;
  tval.nsec = value.stamp.nsec;
  result.insert (caService::CA_TAG_TIME, tval);
}

void
caRequestObject::fillResult (int reqMask, int type,
			     struct dbr_time_enum& value,
			     cdevData &result)
{
  if (type != caRequestObject::VALUE) {
    int i = 1;
    if (reqMask & (0x1 << i++)){
      result.insert (caService::CA_TAG_STATUS, (int)value.status);
    }
    if (reqMask & (0x1 << i++)){
      result.insert (caService::CA_TAG_SEVERITY, (int)value.severity);
    }
  }

  cdev_TS_STAMP tval;
  tval.secPastEpoch = TS_EPOCH_SEC_PAST_1970
    + (unsigned long)value.stamp.secPastEpoch;
  tval.nsec = value.stamp.nsec;
  result.insert (caService::CA_TAG_TIME, tval);
}

void
caRequestObject::fillResult (int reqMask, int type,
			     struct dbr_ctrl_short& value, 
			     cdevData& result)
{
  if (type != caRequestObject::VALUE) {
    int i = 1;
    if (reqMask & (0x1 << i++))
      result.insert (caService::CA_TAG_STATUS, (int)value.status);
    if (reqMask & (0x1 << i++))  
      result.insert (caService::CA_TAG_SEVERITY, (int)value.severity);
    // timeStmp skipped epics fault :-
    i++; 
    i++;  // no precision information
    if (reqMask & (0x1 << i++))  
      result.insert (caService::CA_TAG_UNITS, value.units);
    if (reqMask & (0x1 << i++))  
      result.insert (caService::CA_TAG_DISPHI, (int)value.upper_disp_limit);
    if (reqMask & (0x1 << i++))  
      result.insert (caService::CA_TAG_DISPLO, (int)value.lower_disp_limit);
    if (reqMask & (0x1 << i++))  
      result.insert (caService::CA_TAG_ALRMHI, (int)value.upper_alarm_limit);
    if (reqMask & (0x1 << i++))  
      result.insert (caService::CA_TAG_ALRMLO, (int)value.lower_alarm_limit);
    if (reqMask & (0x1 << i++))  
      result.insert (caService::CA_TAG_WRNHI, (int)value.upper_warning_limit);
    if (reqMask & (0x1 << i++))  
      result.insert (caService::CA_TAG_WRNLO, (int)value.lower_warning_limit);
    if (reqMask & (0x1 << i++))  
      result.insert (caService::CA_TAG_CTRLHI, (int)value.upper_ctrl_limit);
    if (reqMask & (0x1 << i++))  
      result.insert (caService::CA_TAG_CTRLLO, (int)value.lower_ctrl_limit);
  }
}


void
caRequestObject::fillResult (int reqMask, int type,
			     struct dbr_ctrl_char& value, 
			     cdevData& result)
{
  if (type != caRequestObject::VALUE) {
    int i = 1;
    if (reqMask & (0x1 << i++))
      result.insert (caService::CA_TAG_STATUS, (short)value.status);
    if (reqMask & (0x1 << i++))  
      result.insert (caService::CA_TAG_SEVERITY, (short)value.severity);
    // timeStmp skipped epics fault :-
    i++;
    i++;  // no precision information
    if (reqMask & (0x1 << i++))  
      result.insert (caService::CA_TAG_UNITS, value.units);
    if (reqMask & (0x1 << i++))  
      result.insert (caService::CA_TAG_DISPHI, (BYTE)value.upper_disp_limit);
    if (reqMask & (0x1 << i++))  
      result.insert (caService::CA_TAG_DISPLO, (BYTE)value.lower_disp_limit);
    if (reqMask & (0x1 << i++))  
      result.insert (caService::CA_TAG_ALRMHI, (BYTE)value.upper_alarm_limit);
    if (reqMask & (0x1 << i++))  
      result.insert (caService::CA_TAG_ALRMLO, (BYTE)value.lower_alarm_limit);
    if (reqMask & (0x1 << i++))  
      result.insert (caService::CA_TAG_WRNHI, (BYTE)value.upper_warning_limit);
    if (reqMask & (0x1 << i++))  
      result.insert (caService::CA_TAG_WRNLO, (BYTE)value.lower_warning_limit);
    if (reqMask & (0x1 << i++))  
      result.insert (caService::CA_TAG_CTRLHI, (BYTE)value.upper_ctrl_limit);
    if (reqMask & (0x1 << i++))  
      result.insert (caService::CA_TAG_CTRLLO, (BYTE)value.lower_ctrl_limit);
  }
}

void
caRequestObject::fillResult (int reqMask, int type,
			     struct dbr_ctrl_long& value, 
			     cdevData& result)
{
  if (type != caRequestObject::VALUE) {
    int i = 1;
    if (reqMask & (0x1 << i++))
      result.insert (caService::CA_TAG_STATUS, (int)value.status);
    if (reqMask & (0x1 << i++))  
      result.insert (caService::CA_TAG_SEVERITY, (int)value.severity);
    // timeStmp skipped epics fault :-
    i++;
    i++; // no precision information
    if (reqMask & (0x1 << i++))  
      result.insert (caService::CA_TAG_UNITS, value.units);
    if (reqMask & (0x1 << i++))  
      result.insert (caService::CA_TAG_DISPHI, (int)value.upper_disp_limit);
    if (reqMask & (0x1 << i++))  
      result.insert (caService::CA_TAG_DISPLO, (int)value.lower_disp_limit);
    if (reqMask & (0x1 << i++))  
      result.insert (caService::CA_TAG_ALRMHI, (int)value.upper_alarm_limit);
    if (reqMask & (0x1 << i++))  
      result.insert (caService::CA_TAG_ALRMLO, (int)value.lower_alarm_limit);
    if (reqMask & (0x1 << i++))  
      result.insert (caService::CA_TAG_WRNHI, (int)value.upper_warning_limit);
    if (reqMask & (0x1 << i++))  
      result.insert (caService::CA_TAG_WRNLO, (int)value.lower_warning_limit);
    if (reqMask & (0x1 << i++))  
      result.insert (caService::CA_TAG_CTRLHI, (int)value.upper_ctrl_limit);
    if (reqMask & (0x1 << i++))  
      result.insert (caService::CA_TAG_CTRLLO, (int)value.lower_ctrl_limit);
  }
}
      
void
caRequestObject::fillResult (int reqMask, int type,
			     struct dbr_ctrl_float& value,
			     cdevData& result)
{
  if (type != caRequestObject::VALUE) {
    int i = 1;
    if (reqMask & (0x1 << i++))
      result.insert (caService::CA_TAG_STATUS, (int)value.status);
    if (reqMask & (0x1 << i++))  
      result.insert (caService::CA_TAG_SEVERITY, (int)value.severity);
    // timeStamp skipped epics fault :-
    i++;
    if (reqMask & (0x1 << i++)) 
      result.insert (caService::CA_TAG_PRECISION, value.precision);
    if (reqMask & (0x1 << i++))  
      result.insert (caService::CA_TAG_UNITS, value.units);
    if (reqMask & (0x1 << i++)) 
      result.insert (caService::CA_TAG_DISPHI, (float)value.upper_disp_limit);
    if (reqMask & (0x1 << i++))  
      result.insert (caService::CA_TAG_DISPLO, (float)value.lower_disp_limit);
    if (reqMask & (0x1 << i++))  
      result.insert (caService::CA_TAG_ALRMHI, (float)value.upper_alarm_limit);
    if (reqMask & (0x1 << i++))  
      result.insert (caService::CA_TAG_ALRMLO, (float)value.lower_alarm_limit);
    if (reqMask & (0x1 << i++))  
      result.insert (caService::CA_TAG_WRNHI,(float)value.upper_warning_limit);
    if (reqMask & (0x1 << i++))  
      result.insert (caService::CA_TAG_WRNLO,(float)value.lower_warning_limit);
    if (reqMask & (0x1 << i++)) 
      result.insert (caService::CA_TAG_CTRLHI, (float)value.upper_ctrl_limit);
    if (reqMask & (0x1 << i++)) 
      result.insert (caService::CA_TAG_CTRLLO, (float)value.lower_ctrl_limit);
  }
}

void
caRequestObject::fillResult (int reqMask, int type,
			     struct dbr_ctrl_double& value,
			     cdevData& result)
{
  if (type != caRequestObject::VALUE) {
    int i = 1;
    if (reqMask & (0x1 << i++))
      result.insert (caService::CA_TAG_STATUS, (int)value.status);
    if (reqMask & (0x1 << i++))  
      result.insert (caService::CA_TAG_SEVERITY, (int)value.severity);
    // timeStmp skipped epics fault :-
    i++;
    if (reqMask & (0x1 << i++)) 
      result.insert (caService::CA_TAG_PRECISION, value.precision);
    if (reqMask & (0x1 << i++))  
	result.insert (caService::CA_TAG_UNITS, value.units);
    if (reqMask & (0x1 << i++)) 
      result.insert (caService::CA_TAG_DISPHI, (double)value.upper_disp_limit);
    if (reqMask & (0x1 << i++)) 
      result.insert (caService::CA_TAG_DISPLO, (double)value.lower_disp_limit);
    if (reqMask & (0x1 << i++))  
      result.insert (caService::CA_TAG_ALRMHI, (double)value.upper_alarm_limit);
    if (reqMask & (0x1 << i++))  
      result.insert (caService::CA_TAG_ALRMLO, (double)value.lower_alarm_limit);
    if (reqMask & (0x1 << i++))  
      result.insert (caService::CA_TAG_WRNHI,(double)value.upper_warning_limit);
    if (reqMask & (0x1 << i++))  
      result.insert (caService::CA_TAG_WRNLO,(double)value.lower_warning_limit);
    if (reqMask & (0x1 << i++))  
      result.insert (caService::CA_TAG_CTRLHI, (double)value.upper_ctrl_limit);
    if (reqMask & (0x1 << i++))  
      result.insert (caService::CA_TAG_CTRLLO, (double)value.lower_ctrl_limit);
  }
}

void
caRequestObject::fillResult (int reqMask, int type,
			     struct dbr_ctrl_enum& value,
			     cdevData& result)
{
  if (type != caRequestObject::VALUE) {
    int i = 1;
    if (reqMask & (0x1 << i++))
      result.insert (caService::CA_TAG_STATUS, (int)value.status);
    if (reqMask & (0x1 << i++))  
      result.insert (caService::CA_TAG_SEVERITY, (int)value.severity);
    // timeStmp skipped epics fault :-
    i++;
    // no precision information
    i++;
    // no units information
    i++;
    if (reqMask & (0x1 << i++)) 
      result.insert (caService::CA_TAG_DISPHI, (int)value.no_str - 1);
    if (reqMask & (0x1 << i++)) 
      result.insert (caService::CA_TAG_DISPLO, (int)0);
  }
}

int 
caRequestObject::sendNoBlock (cdevData& out, cdevData& result)
{
  return sendNoBlock (&out, &result);
}

int 
caRequestObject::sendNoBlock (cdevData& out, cdevData *result)
{
  return sendNoBlock (&out, result);
}

int
caRequestObject::sendNoBlock (cdevData* out, cdevData& result)
{
  return sendNoBlock (out, &result);
}

int
caRequestObject::sendNoBlock (cdevData* out, cdevData* result)
{
  if (!channel_)
    attachChannel ();

  // first check whether this is deferred mode
  if (deferExecution (out, result))
    return CDEV_SUCCESS;

  int  status = CDEV_ERROR;
  cdevTranObj *xobj = 0;

  switch (action_){
  case caRequestObject::GET:
    if (result == 0)
      return CDEV_INVALIDARG;
    xobj = new cdevTranObj (&system_, this, result, 0);
    // if channel is not connected, put all executions in the buffer
    if (!channel_->connected_) {
      bufferExecution (0, result, (cdevGroup *)channel_->egrp_,
		       (void *)xobj);
      return CDEV_SUCCESS;
    }
    status = getValueCbk (channel_, xobj);
    break;
  case caRequestObject::SET:
    if (out == 0)
      return CDEV_INVALIDARG;
    if (readonly_)
      return CDEV_INVALIDARG;

    // if channel is not connected, put all executions in the buffer
    // must clone the data as the caller may delete it
    if (!channel_->connected_) {
      cdevData *cloned = new cdevData(*out);
      xobj = new cdevTranObj (&system_, this, cloned, 0);
      bufferExecution (cloned, result, (cdevGroup *)channel_->egrp_,
		       (void *)xobj);
      return CDEV_SUCCESS;
    } else {
     xobj = new cdevTranObj (&system_, this, out, 0);
    }
    status = setValueCbk (channel_, xobj);
    break;
  case caRequestObject::MONITOR_ON:
    status = CDEV_INVALIDARG;
    break;
  case caRequestObject::MONITOR_OFF:
    {
      // remove every monitor callbacks
      // remove a monitor object from all the channels
      cdevCallback userCallback (0, 0);
      channel_->removeCallback (&userCallback);
      if (channel_->pinited_) {
	caPchannel* pch = channel_->pchannels_;
       
	for (int i = 0; i < MAX_NUM_PCHANNELS; i++) {
	  if (pch[i].channel != 0) 
	    pch[i].channel->removeCallback (&userCallback);
	}
      }
    }
    status = CDEV_SUCCESS;
    break;
  default:
#ifdef _CDEV_DEBUG
    printf("Unsupported action message %s\n", action_);
#endif
    system_.reportError (CDEV_SEVERITY_ERROR, deviceName_, this, 
			 "%s %s requestObject does not support message %d\n",
			 deviceName_, message_, action_);
    status = CDEV_INVALIDARG;
    break;
  }
  return status;
}


int
caRequestObject::execNoBlock (cdevData* out, cdevData* result, void* arg)
{
  // arg is a cdevTranObj
  cdevTranObj* xobj = (cdevTranObj *)arg;

  // first check whether this is deferred mode
  if (deferExecution (out, result)) {
    delete xobj;
    return CDEV_SUCCESS;
  }

  int  status = CDEV_ERROR;

  switch (action_){
  case caRequestObject::GET:
    // if channel is not connected, put all executions in the buffer
    if (!channel_->connected_) {
      bufferExecution (0, result, (cdevGroup *)channel_->egrp_,
		       (void *)xobj);
      return CDEV_SUCCESS;
    }
    status = getValueCbk (channel_, xobj);
    break;
  case caRequestObject::SET:
    // if channel is not connected, put all executions in the buffer
    if (!channel_->connected_) {
      cdevData *cloned = new cdevData(*out);
      bufferExecution (cloned, result, (cdevGroup *)channel_->egrp_,
		       (void *)xobj);
      return CDEV_SUCCESS;
    }
    status = setValueCbk (channel_, xobj);
    break;
  default:
#ifdef _CDEV_DEBUG
    printf("Unsupported action message %s\n", action_);
#endif
    system_.reportError (CDEV_SEVERITY_ERROR, deviceName_, this, 
			 "%s %s requestObject does not support message %d\n",
			 deviceName_, message_, action_);
    status = CDEV_INVALIDARG;
    break;
  }
  return status;
}


int
caRequestObject::setValueCbk (caChannel* channel, cdevTranObj *xobj)
{
  int caStatus = -1;
  int dataStatus = CDEV_SUCCESS;
  int ioStatus = CDEV_SUCCESS;

  cdevData          *out = xobj->resultData_;
  cdevData          result;
  cdevCallback      *cbkobj = xobj->userCallback_;
  cdevRequestObject *reqObj = xobj->reqObj_;

  if (dataMatch (*out, (int)channel->type_, 
		 (int)(channel_->numElements_)) != CDEV_SUCCESS){
    // I have to delete this transaction object
    if (cbkobj) { // has callback
      (*cbkobj->callbackFunction())(CDEV_ERROR, cbkobj->userarg(),
				    *reqObj, result);
    }
    delete xobj;  
    return CDEV_INVALIDARG;
  }

  switch (channel->type_){
  case DBR_STRING:
    if (channel->numElements_ == 1){
      char temp[MAX_STRING_SIZE];
      dataStatus = out->get (caService::CA_TAG_VALUE, temp, sizeof(temp));
      if (dataStatus == CDEV_SUCCESS){
	// epics 3.11 has no ca_array_put_callback
#ifdef _EPICS_3_12
	caStatus = ::ca_array_put_callback (DBR_STRING, channel->numElements_, 
		    channel->cId_, temp,
		    &(caRequestObject::defaultSetCallback),
		    (void *)xobj);
#else
	caStatus = ::ca_array_put (DBR_STRING, channel->numElements_,
				   channel->cId_, temp);
#endif
      }
    }
    else {
      char **temp = new char*[channel->numElements_];
      // each item will be allocated by the following
      dataStatus = out->get (caService::CA_TAG_VALUE, temp);
      int count = (int)channel->numElements_;
      dataStatus = out->getElems (caService::CA_TAG_VALUE, (size_t *)&count);
      if (dataStatus == CDEV_SUCCESS){
	char *cbuf = new char[channel->numElements_*MAX_STRING_SIZE];
	int j = 0;
	for (int i = 0; i < channel->numElements_; i++){
	  ::cdevStrncpy (&(cbuf[j]), temp[i], MAX_STRING_SIZE);
	  j += MAX_STRING_SIZE;
	}
      // epics 3.11 has no ca_array_put_callback
#ifdef _EPICS_3_12
	caStatus = ::ca_array_put_callback (DBR_STRING, channel->numElements_, 
		    channel->cId_, cbuf,
		    &(caRequestObject::defaultSetCallback),
		    (void *)xobj);
#else
	caStatus = ::ca_array_put (DBR_STRING, channel->numElements_,
				   channel->cId_, cbuf);
#endif
	for (i = 0; i < count; i++)
	  delete []temp[i];
	delete []temp;
	delete []cbuf;
      }
    }      
    break;
  case DBR_ENUM:
    if (channel->numElements_ == 1){
      char temp[MAX_STRING_SIZE];
      char temp2[MAX_STRING_SIZE];
      int  tind;
      dataStatus = out->get (caService::CA_TAG_VALUE, temp, sizeof(temp));
      if (dataStatus == CDEV_SUCCESS){
	int i = 0;
	sscanf (temp, "%d", &tind);
	// if a caller supplies (only) an integer, it will be treated as an index
	if (sscanf (temp, "%d%s", &tind, temp2) == 1 &&
	    tind >= 0 && 
	    tind < channel->accessBuffer_->cenmval.no_str)
	  i = tind;
	else {
	  for (i = 0; i < channel->accessBuffer_->cenmval.no_str; i++) {
	    if (::strcmp (temp, channel->accessBuffer_->cenmval.strs[i]) == 0)
	      break;
	  }
	  if (i >= channel->accessBuffer_->cenmval.no_str){
#ifdef _CDEV_DEBUG
	    printf("Unmatched string %s to enum record\n", temp);
	    printf("The following are the string values\n");
	    for (i = 0; i <  channel->accessBuffer_->cenmval.no_str; i++)
	      printf(" %s ",channel->accessBuffer_->cenmval.strs[i]);
	    printf("\n");
#endif
	    return CDEV_INVALIDARG;
	  }
	}
	short desiredIndex = i;
#ifdef _EPICS_3_12
	caStatus = ::ca_array_put_callback (DBR_ENUM, channel->numElements_,
		    channel->cId_, &desiredIndex,
		    &(caRequestObject::defaultSetCallback),
		    (void *)xobj);
#else
	caStatus = ::ca_array_put (DBR_ENUM, channel->numElements_,
				   channel->cId_, &desiredIndex);
#endif 
      }
    }
    else {
#ifdef _CDEV_DEBUG
      printf ("Multiple elements of DBR_ENUM is not supported in CDEV\n");
#endif
      return CDEV_INVALIDARG;
    }
    break;
  case DBR_SHORT:
    if (channel->numElements_ == 1){
      int value;
      dataStatus = out->get (caService::CA_TAG_VALUE, &value);
      if (dataStatus == CDEV_SUCCESS){
	// epics 3.11 does not have ca_array_put_callback
#ifdef _EPICS_3_12
	caStatus = ::ca_array_put_callback (DBR_LONG, channel->numElements_,
		    channel->cId_, &value,
		    &(caRequestObject::defaultSetCallback),
		    (void *)xobj); 
#else
	caStatus = ::ca_array_put (DBR_LONG, channel->numElements_,
				   channel->cId_, &value);
#endif
      }
    }
    else{
      int *value = new int[channel->numElements_];
      dataStatus = out->get (caService::CA_TAG_VALUE, value);
      if (dataStatus == CDEV_SUCCESS){
	// epics 3.11 does not have ca_array_put_callback
#ifdef _EPICS_3_12
	caStatus = ::ca_array_put_callback (DBR_LONG, channel->numElements_,
		    channel->cId_, value,
		    &(caRequestObject::defaultSetCallback),
		    (void *)xobj); 
#else
	caStatus = ::ca_array_put (DBR_LONG, channel->numElements_,
				   channel->cId_, value);
#endif
	delete []value;
      }
    }
    break;
  case DBR_CHAR:
    if (channel->numElements_ == 1){
      BYTE value;
      dataStatus = out->get (caService::CA_TAG_VALUE, &value);
      if (dataStatus == CDEV_SUCCESS){
	// epics 3.11 does not have ca_array_put_callback
#ifdef _EPICS_3_12
	caStatus = ::ca_array_put_callback (DBR_CHAR, channel->numElements_,
		    channel->cId_, &value,
		    &(caRequestObject::defaultSetCallback),
		    (void *)xobj); 
#else
	caStatus = ::ca_array_put (DBR_CHAR, channel->numElements_,
				   channel->cId_, &value);
#endif
      }
    }
    else{
      BYTE *value = new BYTE[channel->numElements_];
      dataStatus = out->get (caService::CA_TAG_VALUE, value);
      if (dataStatus == CDEV_SUCCESS){
	// epics 3.11 does not have ca_array_put_callback
#ifdef _EPICS_3_12
	caStatus = ::ca_array_put_callback (DBR_CHAR, channel->numElements_,
		    channel->cId_, value,
		    &(caRequestObject::defaultSetCallback),
		    (void *)xobj); 
#else
	caStatus = ::ca_array_put (DBR_CHAR, channel->numElements_,
				   channel->cId_, value);
#endif
	delete []value;
      }
    }
    break;    
  case DBR_LONG:
    if (channel->numElements_ == 1){
      int value;
      dataStatus = out->get (caService::CA_TAG_VALUE, &value);
      if (dataStatus == CDEV_SUCCESS){
	// epics 3.11 does not have ca_array_put_callback
#ifdef _EPICS_3_12
	caStatus = ::ca_array_put_callback (DBR_LONG, channel->numElements_,
		    channel->cId_, &value,
		    &(caRequestObject::defaultSetCallback),
		    (void *)xobj); 
#else
	caStatus = ::ca_array_put (DBR_LONG, channel->numElements_,
				   channel->cId_, &value);
#endif
      }
    }
    else {
      int *value = new int[channel->numElements_];
      dataStatus = out->get (caService::CA_TAG_VALUE, value);
      if (dataStatus == CDEV_SUCCESS){
	// epics 3.11 does not have ca_array_put_callback
#ifdef _EPICS_3_12
	caStatus = ::ca_array_put_callback (DBR_LONG, channel->numElements_,
		    channel->cId_, value,
		    &(caRequestObject::defaultSetCallback),
		    (void *)xobj); 
#else
	caStatus = ::ca_array_put (DBR_LONG, channel->numElements_,
				   channel->cId_, value);
#endif
	delete []value;
      }
    }      
    break;
  case DBR_FLOAT:
  case DBR_DOUBLE:
    if (channel->numElements_ == 1) {
      double fval;
      dataStatus = out->get (caService::CA_TAG_VALUE, &fval);
      if (dataStatus == CDEV_SUCCESS){
	// epics 3.11 does not have put callback
#ifdef _EPICS_3_12
	caStatus = ::ca_array_put_callback  (DBR_DOUBLE, channel->numElements_,
		     channel->cId_, &fval,
		     &(caRequestObject::defaultSetCallback),
		     (void *)xobj);
#else
	caStatus = ::ca_array_put (DBR_DOUBLE, channel->numElements_,
				   channel->cId_, &fval);
#endif
      }
    }
    else {
      double *fval = new double[channel->numElements_];
      dataStatus = out->get (caService::CA_TAG_VALUE, fval);
      if (dataStatus == CDEV_SUCCESS){
	// epics 3.11 does not have put callback
#ifdef _EPICS_3_12
	caStatus = ::ca_array_put_callback  (DBR_DOUBLE, channel->numElements_,
		     channel->cId_, fval,
		     &(caRequestObject::defaultSetCallback),
		     (void *)xobj);
#else
	caStatus = ::ca_array_put (DBR_DOUBLE, channel->numElements_,
				   channel->cId_, fval);
#endif
	delete []fval;
      }
    }      
    break;
  default:
#ifdef _CDEV_DEBUG
    printf("unsupported data type \n");
#endif
    return CDEV_INVALIDARG;
    // Break not needed because of return statement
    // break;
  }
  // check data status first
  if (dataStatus != CDEV_SUCCESS) {
    // remove transaction objecyt so a group will not block
    if (cbkobj) {
      (*cbkobj->callbackFunction())(dataStatus, cbkobj->userarg(),
				    *reqObj, result);
      dataStatus = CDEV_SUCCESS; // problem now handled, no more to report
    }
    else { 
      // use user outbound data as result code
      out->insert (caService::CA_TAG_RESULT_CODE, dataStatus);
    }
    delete xobj;
    return dataStatus;
  }

  // check channel access status
  if (caStatus != ECA_NORMAL){
#ifdef _CDEV_DEBUG
    printf("ca_array_put_callback failed\n");
#endif
    // remove transaction objecyt so a group will not block
    if (cbkobj) {
      (*cbkobj->callbackFunction())(CDEV_IOFAILED, cbkobj->userarg(),
				    *reqObj, result);
      ioStatus = CDEV_SUCCESS; // problem now handled, no more to report
    }
    else {
      // use user outbound data as result code
      out->insert (caService::CA_TAG_RESULT_CODE, CDEV_IOFAILED);
      ioStatus = CDEV_IOFAILED;
    }
    delete xobj;  
    return ioStatus;
  }

  // trasaction object will be removed from default callback function
  // for epics 3.11 I have no idea when put will finish
  // so I have to delete this transaction object
#ifndef _EPICS_3_12
  // so what I do, just call callback :---
  if (cbkobj) {
    (*cbkobj->callbackFunction())(CDEV_SUCCESS, cbkobj->userarg(),
				  *reqObj, result);
  }
  else {
    // use user outbound data as result code
    out->insert (caService::CA_TAG_RESULT_CODE, CDEV_SUCCESS);
  }

  delete xobj;  
#endif
  return CDEV_SUCCESS;
}

int
caRequestObject::sendCallback (cdevData& out, cdevCallback& callback)
{
  return sendCallback (&out, callback);
}

int
caRequestObject::sendCallback (cdevData* out, cdevCallback& userCallback)
{
  if (!channel_)
    attachChannel ();

  if (deferExecution (out, &userCallback))
    return CDEV_SUCCESS;

  int status = CDEV_ERROR;
  cdevTranObj *xobj = 0;
  caMonObj*  obj = 0;

  // copy userCallback into the following callback, which will free
  // user from taking care cdevCallback object. 
  // Note: cdevCallback's new operator is an overloaded operator
  cdevCallback* callback = 0;

  switch (action_){
  case caRequestObject::GET:
    callback = new cdevCallback (userCallback);
    xobj = new cdevTranObj (&system_, this, 0, callback);
    if (!channel_->connected_) {
      bufferExecution (0, callback, (cdevGroup *)channel_->egrp_,
		       (void *)xobj);
      return CDEV_SUCCESS;
    }
    status = getValueCbk (channel_, xobj);
    break;
  case caRequestObject::SET:
    if (out == 0)
      return CDEV_INVALIDARG;
    if (readonly_)
      return CDEV_INVALIDARG;
    callback = new cdevCallback (userCallback);      

    // if deferred execution, clone the data as caller may delete it
    if (!channel_->connected_) {
      cdevData *cloned = new cdevData(*out);
      xobj = new cdevTranObj (&system_, this, cloned, callback);
      bufferExecution (cloned, callback, (cdevGroup *)channel_->egrp_,
		       (void *)xobj);
      return CDEV_SUCCESS;
    } else {
      xobj = new cdevTranObj (&system_, this, out, callback);
    }
    status = setValueCbk (channel_, xobj);
    break;
  case caRequestObject::MONITOR_ON:
    {
      // copy user callback
      callback = new cdevCallback (userCallback);
      // create a new transaction object
      xobj = new cdevTranObj (&system_, this, 0, callback);

      //==================================================
      // if there is one callback is the same as the new
      // one user wants, discard the new one
      //=================================================
      if (channel_->hasSameCallback (*callback)) {
	delete xobj;
	system_.reportError (CDEV_SEVERITY_ERROR, deviceName_, this, 
			     "%s %s dislikes the same callbacks\n",
			     deviceName_, message_);
	return CDEV_ERROR;
      }

      // if channel is not connected, buffer this up
      if (!channel_->connected_) {
	bufferExecution (0, callback, (cdevGroup *)channel_->egrp_,
			 (void *)xobj);
	return CDEV_SUCCESS;
      }

      //==================================================
      // if this requests has the same context i.e.
      // or the same req type and request mask, use the
      // same channel access event Id to get network
      // response from the server. 
      //=================================================
      caMonObj* eobj = 0;
      if ((eobj = channel_->hasSameCallingCxt (reqType_, reqMask_)) != 0) {
	// create new callback object and add to the list
	caMonCbk* cbk = new caMonCbk (*xobj->userCallback_, xobj->resultData_);
	eobj->addCallback (cbk);
	// immediately call user callback now
	eobj->callCallback (cbk);

	// call monitor properties which will add more monitor
	// objects to the appropriate property channels
	addCbksToPChannels (channel_, *xobj->userCallback_,
			    xobj->resultData_);
	delete xobj;

	return CDEV_SUCCESS;
      }
      // get monitor object from transaction object
      // and disable the transaction object from deleting
      // the callback object
      obj = new caMonObj (*xobj);
      // set up associated arguments
      obj->setMonitorInfo (caService::CA_TAG_VALUE,
			   CDEV_SUCCESS,
			   channel_,
			   reqType_,
			   reqMask_);
      xobj->disableDeleteCbk ();

      monitorProperties     (channel_, obj);
      status = monitorValue (channel_, obj);
    }
    break;
  case caRequestObject::MONITOR_OFF:
    {
      // remove a monitor object from all the channels
      // monitor objects' destructor will call all monitor
      // monitor callbacks to denote caller that monitor
      // is over
      channel_->removeCallback (&userCallback);
      if (channel_->pinited_) {
	caPchannel* pch = channel_->pchannels_;
	
	for (int i = 0; i < MAX_NUM_PCHANNELS; i++) {
	  if (pch[i].channel != 0) 
	    pch[i].channel->removeCallback (&userCallback);
	}
      }
    }
    status = CDEV_SUCCESS;
    break;
  default:
#ifdef _CDEV_DEBUG
    printf("Unsupported action message %s\n", action_);
#endif
    system_.reportError (CDEV_SEVERITY_ERROR, deviceName_, this, 
			 "%s %s requestObject does not support message %d\n",
			 deviceName_, message_, action_);
    status = CDEV_INVALIDARG;
    break;
  }
  return status;
}


int
caRequestObject::execCallback (cdevData* out, 
			       cdevCallback& userCallback,
			       void* arg)
{
  // arg is actually a transcation object
  cdevTranObj* xobj = (cdevTranObj *)arg;
  if (deferExecution (out, &userCallback)) {
    // the above code will make copy of user callback, so it is safe
    // to delete xobj that has userCallback in it here
    delete xobj;
    return CDEV_SUCCESS;
  }

  int status = CDEV_ERROR;
  caMonObj*  obj = 0;

  // copy userCallback into the following callback, which will free
  // user from taking care cdevCallback object. 
  // Note: cdevCallback's new operator is an overloaded operator
  cdevCallback* callback = 0;

  switch (action_){
  case caRequestObject::GET:
    if (!channel_->connected_) {
      bufferExecution (0, xobj->userCallback_, 
		       (cdevGroup *)channel_->egrp_,
		       (void *)xobj);
      return CDEV_SUCCESS;
    }
    status = getValueCbk (channel_, xobj);
    break;
  case caRequestObject::SET:
    if (!channel_->connected_) {
      cdevData *cloned = new cdevData(*out);
      bufferExecution (cloned, xobj->userCallback_, 
		       (cdevGroup *)channel_->egrp_,
		       (void *)xobj);
      return CDEV_SUCCESS;
    }
    status = setValueCbk (channel_, xobj);
    break;
  case caRequestObject::MONITOR_ON:
    {
      if (!channel_->connected_) {
	bufferExecution (0, xobj->userCallback_, 
			 (cdevGroup *)channel_->egrp_,
			 (void *)xobj);
	return CDEV_SUCCESS;
      }

      //==================================================
      // if this requests has the same context i.e.
      // or the same req type and request mask, use the
      // same channel access event Id to get network
      // response from the server. 
      //=================================================
      caMonObj* eobj = 0;
      if ((eobj = channel_->hasSameCallingCxt (reqType_, reqMask_)) != 0) {
	// create new callback object and add to the list
	caMonCbk* cbk = new caMonCbk (*xobj->userCallback_, xobj->resultData_);
	eobj->addCallback (cbk);
	// immediately call user callback now
	eobj->callCallback (cbk);

	// call monitor properties which will add more monitor
	// objects to the appropriate property channels
	addCbksToPChannels (channel_, *xobj->userCallback_,
			    xobj->resultData_);
	delete xobj;

	return CDEV_SUCCESS;
      }
      // get monitor object from transaction object
      // and disable the transaction object from deleting
      // the callback object
      obj = new caMonObj (*xobj);
      // set up associated arguments
      obj->setMonitorInfo (caService::CA_TAG_VALUE,
			   CDEV_SUCCESS,
			   channel_,
			   reqType_,
			   reqMask_);
      xobj->disableDeleteCbk ();

      monitorProperties     (channel_, obj);
      status = monitorValue (channel_, obj);
    }
    break;
  default:
#ifdef _CDEV_DEBUG
    printf("Unsupported action message %s\n", action_);
#endif
    system_.reportError (CDEV_SEVERITY_ERROR, deviceName_, this, 
			 "%s %s requestObject does not support message %d\n",
			 deviceName_, message_, action_);
    status = CDEV_INVALIDARG;
    break;
  }
  return status;
}

void
caRequestObject::initPropChannels (caPchannel* pch)
{
  cdevData&  cxt = getContext ();
  int        val;
  char       svcData[64];
  caService* svc = (caService *)service_;

  // scan through all property channels to establish connection if necessary
  for (int i = 2; i < MAX_NUM_PCHANNELS; i++) {
    if (cxt.get (pch[i].tag, &val) == CDEV_SUCCESS)
      // caller specify a trigger or I am going to setup channel
      // for those properties which are not changed rapidly
      if (val == 2 || i >= 2) { 
	// construct channel access record.attr
	::strcpy (svcData, device_->name());
	::strcat (svcData, ".");
	::strcat (svcData, svc->attr (pch[i].tag));

	if (pch[i].channel == 0) {
#ifdef _CDEV_DEBUG
	  printf ("property channel %s is not created, do it now\n", svcData);
#endif
	  caChannel* ch = 0;

	  if (svc->channelPtr (svcData, ch) != 0) {
	    // the channel has not created yet
#ifdef _CDEV_DEBUG
	    printf("property channel %s not created \n", svcData);
#endif
	    ch = new caChannel (svcData, svc);
	    svc->addChannel (svcData, ch);
	    ch->count_ = 1;
	  }
	  else{
#ifdef _CDEV_DEBUG
	    printf("property channel %s created \n", svcData);
#endif
	    ch->count_ += 1;
#ifdef _CA_SYNC_CONN
	    if (!ch->connected_){ // channel not connected
	      channel_->connect ();
	    }
#endif
	  }
	  pch[i].channel = ch;
	}
      }
  }
}

void
caRequestObject::initPropChannels (caChannel* parent)
{
  cdevData&  cxt = getContext ();
  int        val;
  char       svcData[64];
  caService* svc = (caService *)service_;

  caPchannel* pch = parent->pchannels_;

  if (!pch)
    return;

  // scan through all property channels to establish connection if necessary
  for (int i = 2; i < MAX_NUM_PCHANNELS; i++) {
    if (cxt.get (pch[i].tag, &val) == CDEV_SUCCESS)
      // caller specify a trigger or I am going to setup channel
      // for those properties which are not changed rapidly
      if (val == 2 || i >= 2) { 
	// construct channel access record.attr
	::strcpy (svcData, device_->name());
	::strcat (svcData, ".");
	::strcat (svcData, svc->attr (pch[i].tag));

	if (pch[i].channel == 0) {
#ifdef _CDEV_DEBUG
	  printf ("property channel %s is not created, do it now\n", svcData);
#endif
	  caChannel* ch = 0;

	  if (svc->channelPtr (svcData, ch) != 0) {
	    // the channel has not created yet
#ifdef _CDEV_DEBUG
	    printf("property channel %s not created \n", ch->channelName_);
#endif
	    ch = new caChannel (svcData, svc);
	    svc->addChannel (svcData, ch);
	    ch->count_ = 1;
	  }
	  else{
#ifdef _CDEV_DEBUG
	    printf("property channel %s created \n", svcData);
#endif
	    ch->count_ += 1;
#ifdef _CA_SYNC_CONN
	    if (!ch->connected_){ // channel not connected
	      channel_->connect ();
	    }
#endif
	  }
	  pch[i].channel = ch;
	}
      }
  }
}

void
caRequestObject::monitorProperties (caChannel* parent, caMonObj *obj)
{
  cdevData&  cxt = getContext ();
  int        val;
  char       svcData[64];
  caService* svc = (caService *)service_;

  // get property channel pointer
  caPchannel* pch = parent->pchannels_;

  if (!pch) /* no property channel, return */
    return;

  // scan through all property channels to establish connection if necessary
  for (int i = 2; i < MAX_NUM_PCHANNELS; i++) {
    if (cxt.get (pch[i].tag, &val) == CDEV_SUCCESS)
      // caller specify a trigger or I am going to setup channel
      // for those properties which are not changed rapidly
      if (val == 2 || i >= 2) { 
	// construct channel access record.attr
	if (pch[i].channel && pch[i].channel->connected_){
	  if (!pch[i].monitored) {
	    caMonObj *newobj = new caMonObj (*obj, obj->cbks_->cbk_,
					     obj->cbks_->out_);
	    newobj->tobj_ = 0;
	    newobj->tag_ = pch[i].tag;
	    newobj->cbkStatus_ = CDEV_SUCCESS;
	    newobj->channel_ = pch[i].channel;

	    // sep-27-1995 -- Jie chen
	    // if this context value == 1, we cache its value instead of
	    // calling user callback directly
	    if (val == 1) 
	      newobj->cbks_->enableCache ();
	  
	    if (monitorPropValue (pch[i].channel, newobj) == CDEV_SUCCESS) {
	      pch[i].channel->addMonitorObj (newobj);
	      pch[i].monitored = 1;
	    }
	    else {
	      delete pch[i].channel;
	      pch[i].channel = 0;
	    }
	  }
	  else {
	    // get first monitor object pointer from this property
	    // channel: it should never return 0
	    caMonObj* fobj = pch[i].channel->firstMonitorObj ();
	    if (!fobj) {
	      system_.reportError (CDEV_SEVERITY_ERROR, deviceName_, this, 
				   "%s %s has bad property channel\n",
				   deviceName_, message_);
	      return;
	    }

	    // create a new callback object with dupliate user callback
	    // information in it
	    caMonCbk* cbkobj = new caMonCbk (*obj->cbks_->cbk_,
					     obj->cbks_->out_);
	    if (val == 1)
	      cbkobj->enableCache ();
	    
	    fobj->addCallback (cbkobj);
	  }
	}
      }
  }
}


void
caRequestObject::addCbksToPChannels (caChannel* parent, 
				     cdevCallback& cbk,
				     cdevData* out)
{
  cdevData&  cxt = getContext ();
  int        val;
  char       svcData[64];
  caService* svc = (caService *)service_;

  // get property channel pointer
  caPchannel* pch = parent->pchannels_;
  if (!pch)
    return;

  // scan through all property channels to establish connection if necessary
  for (int i = 2; i < MAX_NUM_PCHANNELS; i++) {
    if (cxt.get (pch[i].tag, &val) == CDEV_SUCCESS)
      // caller specify a trigger or I am going to setup channel
      // for those properties which are not changed rapidly

      if ((val == 2 || i >= 2) && pch[i].channel != 0 &&
	  pch[i].channel->connected_ && pch[i].monitored) { 
#ifdef _CDEV_DEBUG
	printf ("property channel %s is created already\n", svcData);
#endif
	// get first monitor object pointer from this property
	// channel: it should never return 0
	caMonObj* fobj = pch[i].channel->firstMonitorObj ();
	if (!fobj) {
	  system_.reportError (CDEV_SEVERITY_ERROR, deviceName_, this, 
			       "%s %s has bad property channel\n",
			       deviceName_, message_);
	  return;
	}

	// create a new callback object with dupliate user callback
	// information in it
	caMonCbk* cbkobj = new caMonCbk (cbk, out);
	
	if (val == 1)
	  cbkobj->enableCache ();
	else
	  // call callbak right away
	  fobj->callCallback (cbkobj);

	fobj->addCallback (cbkobj);
      }
  }
}

int
caRequestObject::monitorValue (caChannel* channel, caMonObj *xobj)
{
  // check value context
  cdevData&  cxt = getContext ();
  int        val;
  int        mask = DBE_VALUE | DBE_ALARM;

  if (cxt.get (caService::CA_TAG_VALUE, &val) == CDEV_SUCCESS) {
    if (val == 0) // if 0: no monitor on value
      mask = 0;
  }

  // get property channel pointer
  caPchannel* pch = channel->pchannels_;
  
  if (pch) {
    // scan first two  property channels to change monitor mask if necessary
    for (int i = 0; i < 2; i++) {
      if (cxt.get (pch[i].tag, &val) == CDEV_SUCCESS){
	if (val >=2)
	  mask = mask | DBE_ALARM;
      }
    }
  }

  if (mask == 0) { // we have nothing to do
    // no callback will be called, remove transaction object
    // otherwise group will block
    cdevCallback *cbkobj = xobj->cbks_->cbk_;
    cdevRequestObject *reqobj = xobj->reqObj_;
    cdevData result;

    cbkobj->fireCallback (CDEV_IOFAILED, cbkobj->userarg (),
			  *reqobj, result, 0);

    if (xobj->tobj_) {
      delete xobj->tobj_;
      xobj->tobj_ = 0;
    }
    delete xobj;
    return CDEV_IOFAILED;
  }

  int status, caStatus;
  int reqType;
  // get everything of status and severity and timestamp
  reqType = (int)dbf_type_to_DBR_TIME (channel->type_);

  caStatus = ::ca_add_masked_array_event (reqType,
		  channel->numElements_,
		  channel->cId_,
	          &(caRequestObject::callMonitorCallback),
		  (void *)(xobj),
		  0, 0, 0,
		  &(xobj->mEvId_),
		  mask);
  if (caStatus != ECA_NORMAL) {
    // no callback will be called, remove transaction object
    // otherwise group will block
    cdevCallback *cbkobj = xobj->cbks_->cbk_;
    cdevRequestObject *reqobj = xobj->reqObj_;
    cdevData result;
    
    cbkobj->fireCallback (CDEV_IOFAILED, cbkobj->userarg (),
			  *reqobj, result, 0);
    if (xobj->tobj_) {
      delete xobj->tobj_;
      xobj->tobj_ = 0;
    }
    delete xobj;
    status = CDEV_IOFAILED;
  }
  else 
    status = CDEV_SUCCESS;

  return status;
}

int
caRequestObject::monitorPropValue (caChannel* channel, caMonObj *xobj)
{
  int status = ::ca_add_masked_array_event (channel->type_,
		  channel->numElements_,
		  channel->cId_,
	          &(caRequestObject::callPropMonitorCallback),
		  (void *)(xobj),
		  0, 0, 0,
		  &(xobj->mEvId_),
		  DBE_VALUE);
  if (status != ECA_NORMAL)
    return CDEV_IOFAILED;
  else 
    return CDEV_SUCCESS;
}

void
caRequestObject::convertData (int chtype, 
			      int tag,
			      int reqMask,
			      int reqType,
			      struct event_handler_args& args,
			      cdevData& resData)
{
  switch (chtype){
  case DBR_STRING:
    {
      struct dbr_time_string *retValue = 0;
      retValue = (struct dbr_time_string *)args.dbr;
      if (args.count == 1){
	resData.insert (tag, retValue->value);
	caRequestObject::fillResult (reqMask, reqType, *retValue, resData);
      }
      else{
	char **insertBuf = new char* [args.count];
	insertBuf[0] = new char[MAX_STRING_SIZE];
	::cdevStrncpy (insertBuf[0], retValue->value, MAX_STRING_SIZE);

	char *buffer = (char *)args.dbr;
	int j = sizeof (struct dbr_time_string);
	for (int i = 1; i < args.count; i++){
	  insertBuf[i] = new char[MAX_STRING_SIZE];
	  ::cdevStrncpy (insertBuf[i], &(buffer[j]), MAX_STRING_SIZE);
	  j += MAX_STRING_SIZE;
	}	
	resData.insert (tag, insertBuf, (int)args.count);
	caRequestObject::fillResult (reqMask, reqType, *retValue, resData);

	for (i = 0; i < args.count; i++)
	  delete []insertBuf[i];
	delete []insertBuf;
      }
    }
    break;
  case DBR_ENUM:
    if (args.count == 1){
      if (reqType != caRequestObject::TIMESTMP){
	struct dbr_ctrl_enum *retValue = 0;
	retValue = (struct dbr_ctrl_enum *)args.dbr;
	if (reqType >= caRequestObject::GRINFO) { 
	  int i = 0;
	  // we want display information
	  char **strs = 0;
	  if (retValue->no_str > 0) {
	    strs = new char*[retValue->no_str];
	    for (i = 0; i < retValue->no_str; i++) {
	      strs[i] = new char[strlen (retValue->strs[i]) + 1];
	      strcpy (strs[i], retValue->strs[i]);
	    }
	  }
	  resData.insert (tag, strs, retValue->no_str);

	  // free memory
	  for (i = 0; i < retValue->no_str; i++)
	    delete []strs[i];
	  delete []strs;
	}
	else {
	  unsigned short index = retValue->value;
	  // If this string is a null string, we return index value
	  if (!strlen (retValue->strs[index]))
	      resData.insert (tag, index);
	  else
	    resData.insert (tag, retValue->strs[index]);
	}
	caRequestObject::fillResult (reqMask, reqType, *retValue, resData);
      }
      else{
	struct dbr_time_enum *retValue = 0;
	retValue = (struct dbr_time_enum *)args.dbr;
	// since dbr_time_enum has no string in it's structure
	// it is impossible to find corresponding strings
	resData.insert (tag, retValue->value);
	caRequestObject::fillResult (reqMask, reqType, *retValue, resData);
      }
    }
    else{
#ifdef _CDEV_DEBUG
      printf ("Multiple elements of DBR_ENUM is not supported in CDEV\n");
#endif
    }
    break;
  case DBR_SHORT:
    if (reqType != caRequestObject::TIMESTMP){
      struct dbr_ctrl_short *retValue = 0;
      retValue = (struct dbr_ctrl_short *)args.dbr;
      if (args.count == 1){
	resData.insert (tag, (int)(retValue->value));
	caRequestObject::fillResult (reqMask, reqType, *retValue, resData);
      }
      else {
	int* insertBuf = new int[args.count];
	insertBuf[0] = (int)(retValue->value);

	char *buffer = (char *)args.dbr;
	int j = sizeof (struct dbr_ctrl_short);
	short tmp;
	for (int i = 1; i < args.count; i++){
	  tmp = *(short *)&(buffer[j]);
	  insertBuf[i] = (int)tmp;
	  j += sizeof (short);
	}
	resData.insert (tag, insertBuf, (int)args.count);
	caRequestObject::fillResult (reqMask, reqType, *retValue, resData);

	delete []insertBuf;
      }
    }
    else{
      struct dbr_time_short *retValue = 0;
      retValue = (struct dbr_time_short *)args.dbr;
      if (args.count == 1){
	resData.insert (tag, (int)(retValue->value));
	caRequestObject::fillResult (reqMask, reqType, *retValue, resData);
      }
      else {
	int* insertBuf = new int[args.count];
	insertBuf[0] = (int)(retValue->value);

	char *buffer = (char *)args.dbr;
	int j = sizeof (struct dbr_time_short);
	short tmp;
	for (int i = 1; i < args.count; i++){
	  tmp = *(short *)&(buffer[j]);
	  insertBuf[i] = (int)tmp;
	  j += sizeof (short);
	}
	resData.insert (tag, insertBuf, (int)args.count);
	caRequestObject::fillResult (reqMask, reqType, *retValue, resData);
	delete []insertBuf;
      }
    }
    break;
  case DBR_CHAR:
    if (reqType != caRequestObject::TIMESTMP){
      struct dbr_ctrl_char *retValue = 0;
      retValue = (struct dbr_ctrl_char *)args.dbr;
      if (args.count == 1){
	resData.insert (tag, (BYTE)(retValue->value));
	caRequestObject::fillResult (reqMask, reqType, *retValue, resData);
      }
      else {
	BYTE* insertBuf = new BYTE[args.count];
	insertBuf[0] = (BYTE)(retValue->value);

	char *buffer = (char *)args.dbr;
	int j = sizeof (struct dbr_ctrl_char);
	char tmp;
	for (int i = 1; i < args.count; i++){
	  tmp = *(char *)&(buffer[j]);
	  insertBuf[i] = (BYTE)tmp;
	  j += sizeof (char);
	}
	resData.insert (tag, insertBuf, (int)args.count);
	caRequestObject::fillResult (reqMask, reqType, *retValue, resData);

	delete []insertBuf;
      }
    }
    else{
      struct dbr_time_char *retValue = 0;
      retValue = (struct dbr_time_char *)args.dbr;
      if (args.count == 1){
	resData.insert (tag, (BYTE)(retValue->value));
	caRequestObject::fillResult (reqMask, reqType, *retValue, resData);
      }
      else {
	BYTE* insertBuf = new BYTE[args.count];
	insertBuf[0] = (BYTE)(retValue->value);

	char *buffer = (char *)args.dbr;
	int j = sizeof (struct dbr_time_char);
	char tmp;
	for (int i = 1; i < args.count; i++){
	  tmp = *(char *)&(buffer[j]);
	  insertBuf[i] = (BYTE)tmp;
	  j += sizeof (char);
	}
	resData.insert (tag, insertBuf, (int)args.count);
	caRequestObject::fillResult (reqMask, reqType, *retValue, resData);
	delete []insertBuf;
      }
    }
    break;
  case DBR_LONG:
    if (reqType != caRequestObject::TIMESTMP){
      struct dbr_ctrl_long *retValue = 0;
      retValue = (struct dbr_ctrl_long *)args.dbr;
      if (args.count == 1){
	resData.insert (tag, (int)(retValue->value));
	caRequestObject::fillResult (reqMask, reqType, *retValue, resData);
      }
      else {
	int* insertBuf = new int[args.count];
	insertBuf[0] = (int)(retValue->value);

	char *buffer = (char *)args.dbr;
	int j = sizeof (struct dbr_ctrl_long);
	for (int i = 1; i < args.count; i++){
	  insertBuf[i] = *(int *)&(buffer[j]);
	  j += sizeof (long);
	}
	resData.insert (tag, insertBuf, (int)args.count);
	caRequestObject::fillResult (reqMask, reqType, *retValue, resData);
	delete []insertBuf;
      }
    }
    else{
      struct dbr_time_long *retValue = 0;
      retValue = (struct dbr_time_long *)args.dbr;
      if (args.count == 1){
	resData.insert (tag, (int)(retValue->value));
	caRequestObject::fillResult (reqMask, reqType, *retValue, resData);
      }
      else {
	int* insertBuf = new int[args.count];
	insertBuf[0] = (int)(retValue->value);

	char *buffer = (char *)args.dbr;
	int j = sizeof (struct dbr_time_long);
	for (int i = 1; i < args.count; i++){
	  insertBuf[i] = *(int *)&(buffer[j]);
	  j += sizeof (long);
	}
	resData.insert (tag, insertBuf, (int)args.count);
	caRequestObject::fillResult (reqMask, reqType, *retValue, resData);
	delete []insertBuf;
      }
    }
    break;
  case DBR_FLOAT:
    if (reqType != caRequestObject::TIMESTMP){
      struct dbr_ctrl_float *retValue = 0;
      retValue = (struct dbr_ctrl_float *)args.dbr;
      if (args.count == 1){
	resData.insert (tag, (float)retValue->value);
	caRequestObject::fillResult (reqMask, reqType, *retValue, resData);
      }
      else{
	float* insertBuf = new float[args.count];
	insertBuf[0] = (float)(retValue->value);

	char *buffer = (char *)args.dbr;
	int j = sizeof (struct dbr_ctrl_float);
	for (int i = 1; i < args.count; i++){
	  insertBuf[i] = *(float *)&(buffer[j]);
	  j += sizeof (float);
	}
	resData.insert (tag, insertBuf, (int)args.count);
	caRequestObject::fillResult (reqMask, reqType, *retValue, resData);
	delete []insertBuf;
      }
    }
    else{
      struct dbr_time_float *retValue = 0;
      retValue = (struct dbr_time_float *)args.dbr;
      if (args.count == 1){
	resData.insert (tag, (float)retValue->value);
	caRequestObject::fillResult (reqMask, reqType, *retValue, resData);
      }
      else {
	float* insertBuf = new float[args.count];
	insertBuf[0] = (float)(retValue->value);

	char *buffer = (char *)args.dbr;
	int j = sizeof (struct dbr_time_float);
	for (int i = 1; i < args.count; i++){
	  insertBuf[i] = *(float *)&(buffer[j]);
	  j += sizeof (float);
	}
	resData.insert (tag, insertBuf, (int)args.count);
	caRequestObject::fillResult (reqMask, reqType, *retValue, resData);
	delete []insertBuf;
      }
    }
    break;
  case DBR_DOUBLE:
    if (reqType != caRequestObject::TIMESTMP){
      struct dbr_ctrl_double *retValue = 0;
      retValue = (struct dbr_ctrl_double *)args.dbr;
      if (args.count == 1){
	resData.insert (tag, (double)retValue->value);
	caRequestObject::fillResult (reqMask, reqType, *retValue, resData);
      }
      else {
	double* insertBuf = new double[args.count];
	insertBuf[0] = (double)retValue->value;

	char *buffer = (char *)args.dbr;
	int j = sizeof (struct dbr_ctrl_double);	
	for (int i = 1; i < args.count; i++){
	  insertBuf[i] = *(double *)&(buffer[j]);
	  j += sizeof (double);
	}
	resData.insert (tag, insertBuf, (int)args.count);
	caRequestObject::fillResult (reqMask, reqType, *retValue, resData);	
	delete []insertBuf;
      }
    }
    else{
      struct dbr_time_double *retValue = 0;
      retValue = (struct dbr_time_double *)args.dbr;
      if (args.count == 1) {
	resData.insert (tag, (double)retValue->value);
	caRequestObject::fillResult (reqMask, reqType, *retValue, resData);
      }
      else {
	double* insertBuf = new double[args.count];
	insertBuf[0] = (double)retValue->value;

	char *buffer = (char *)args.dbr;
	int j = sizeof (struct dbr_time_double);
	for (int i = 1; i < args.count; i++){
	  insertBuf[i] = *(double *)&(buffer[j]);
	  j += sizeof (double);
	}
	resData.insert (tag, insertBuf, (int)args.count);
	caRequestObject::fillResult (reqMask, reqType, *retValue, resData);	
	delete []insertBuf;
      }
    }
    break;
  default:
    printf("Fatal Error: Unsupported data type\n");
    break;
  }
}
    
void
caRequestObject::defaultGetCallback (struct event_handler_args args)
{
#ifdef _CDEV_DEBUG
  printf("get callback called\n");
#endif
  cdevTranObj *xobj = (cdevTranObj *)args.usr;

  if (xobj->isTrash ()) {
    // this transaction object is deleted already
    delete xobj;
    return;
  }

  //=========================================================
  //       One must know the requestObject from transaction
  //       is caRequestObject. So no vialation here
  //=========================================================
  caRequestObject *reqObj = (caRequestObject *)xobj->reqObj_;
  //=========================================================
  //       Check data type
  //=========================================================
  cdevData     *resData = xobj->resultData_;
  cdevCallback *cbkobj = xobj->userCallback_;
  cdevData     result;

  if (resData) // send no block
    resData->remove();
  else         // send callback
    resData = &result;

  if (args.count > 0 && args.dbr != 0) {
    // convert data from channel access type to cdevData type
    caRequestObject::convertData ((int)reqObj->channel_->type_,
				  caService::CA_TAG_VALUE,
				  reqObj->reqMask_, 
				  reqObj->reqType_, 
				  args, *resData);

    if (cbkobj) { // called from sendCallback
      (*cbkobj->callbackFunction ()) (CDEV_SUCCESS, cbkobj->userarg (),
				      *reqObj, result);
    }
  }
  else {
    if (cbkobj) { // called from sendCallback
      (*cbkobj->callbackFunction ()) (CDEV_IOFAILED, cbkobj->userarg (),
				      *reqObj, result);
    }
  }
  delete xobj;
}

void
caRequestObject::defaultSetCallback (struct event_handler_args args)
{
  // for now just remove transaction object
#ifdef _CDEV_DEBUG
  printf("default set callback called\n");
#endif
  int status = CDEV_SUCCESS;

  cdevTranObj *xobj = (cdevTranObj *)args.usr;

  if (xobj->isTrash ()) {
    // this transaction object is deleted already
    delete xobj;
    return;
  }

  //=========================================================
  //       One must know the requestObject from transaction
  //       is caRequestObject. So no vialation here
  //=========================================================
  caRequestObject *reqObj = (caRequestObject *)xobj->reqObj_;
  cdevData result;

  cdevCallback *cbkobj = xobj->userCallback_;
  if (cbkobj) { // from sendCallback
    if (args.status != ECA_NORMAL)
      status = CDEV_IOFAILED;
    (*cbkobj->callbackFunction())(status, cbkobj->userarg(),
				  *reqObj, result);
  }
  delete xobj;
}

void
caRequestObject::callPropMonitorCallback (struct event_handler_args args)
{
  caMonObj *xobj = (caMonObj *)args.usr;
  caChannel* ch = xobj->channel_;

  // get tag value associated with this channel
  int    tagval = xobj->tag_;
  // get callback status value assocaited with this channel
  int    cbkst  = xobj->cbkStatus_;

  int status = CDEV_ERROR;

  if (args.count > 0 && args.dbr != 0) {
    // clean out old information
    xobj->cache_.remove ();
    switch (ch->type_){
    case DBR_STRING:
      {
	char *retValue;
	retValue = (char *)args.dbr;
	xobj->cache_.insert (tagval, (char *)(retValue));
      }
      break;
    case DBR_ENUM:
      {
	// get index value
	unsigned short index = *(unsigned short *)args.dbr;
	if (ch->accessBuffer_)
	  status = xobj->cache_.insert (tagval,
				ch->accessBuffer_->cenmval.strs[index]);
	else
	  status = xobj->cache_.insert (tagval, index);
      }
      break;
    case DBR_SHORT:
      {
	short tmp = *(short *)(args.dbr);
	status = xobj->cache_.insert (tagval, (int)tmp);
      }
      break;
    case DBR_CHAR:
      {
	BYTE tmp = *(BYTE *)(args.dbr);
	status = xobj->cache_.insert (tagval, (BYTE)tmp);
      }
      break;
    case DBR_LONG:
      {
	int tmp = *(int *)(args.dbr);
	status = xobj->cache_.insert (tagval, tmp);
      }
      break;
    case DBR_FLOAT:
      {
	float tmp = *(float *)(args.dbr);
	status = xobj->cache_.insert (tagval, tmp);
      }
      break;
    case DBR_DOUBLE:
      {
	double tmp = *(double *)(args.dbr);
	status = xobj->cache_.insert (tagval, tmp);
      }
      break;
    default:
      cdevSystem::defaultSystem().reportError
	  	(CDEV_SEVERITY_ERROR, 
	    "caRequestObject", NULL,
		"Unsupported data type %d\n",
		ch->type_);
      break;
    }
  }

  if (status == CDEV_SUCCESS) {
    updatePropCache ((caRequestObject *)xobj->reqObj_, 
		     tagval, 
		     xobj->cache_);
    xobj->callCallbacks (cbkst, xobj->reqObj_, xobj->cache_);
  }
  else 
    xobj->callCallbacks (CDEV_ERROR, xobj->reqObj_, xobj->cache_);
}  

void
caRequestObject::callMonitorCallback (struct event_handler_args args)
{
  static caRequestObject *reqObj = 0;
  caMonObj *xobj = (caMonObj *)args.usr;

  // get request object
  reqObj = (caRequestObject *)xobj->reqObj_;
  // get channel object
  caChannel* ch = xobj->channel_;

  // get tag value associated with this channel
  int    tagval = xobj->tag_;
  // get callback status value assocaited with this channel
  int    cbkst  = xobj->cbkStatus_;

  if (xobj->tobj_ != 0) {
    // remove transaction object so that pend will give back right result
    if (xobj->tobj_->isTrash ()) {  
      // if this transaction object is trash, return
      delete xobj->tobj_;
      xobj->tobj_ = 0;
      return;
    }
    else {
      delete xobj->tobj_;
      xobj->tobj_ = 0;
    }
    // create monitor object from trasanction object
    reqObj->channel_->addMonitorObj (xobj);
  }

  if (args.count > 0 && args.dbr != 0) {
    // get type and mask
    int reqType = xobj->reqType_;
    int reqMask = xobj->reqMask_;
    switch (reqObj->channel_->type_){
    case DBR_STRING:
      // since monitor value always use DBR_TIME strcuture
      xobj->cache_.remove ();
      caRequestObject::convertData ((int)reqObj->channel_->type_,
				    caService::CA_TAG_VALUE,
				    reqMask, caRequestObject::TIMESTMP,
				    args, xobj->cache_);
      xobj->callCallbacks (cbkst, reqObj, xobj->cache_); 
      break;    
    case DBR_ENUM:
      {
	// since monitor value always use DBR_TIME strcuture which
	// has no no_str and str information, the corresponding
	// channel information about no_str and strs are used here
	xobj->cache_.remove ();
	struct dbr_time_enum *retValue = 0;
	retValue = (struct dbr_time_enum *)args.dbr;
	unsigned short index = retValue->value;

	if (ch->accessBuffer_ && ch->accessBuffer_->cenmval.no_str &&
	    index < ch->accessBuffer_->cenmval.no_str &&
	    strlen (ch->accessBuffer_->cenmval.strs[index])) 
	  xobj->cache_.insert (caService::CA_TAG_VALUE,
			       ch->accessBuffer_->cenmval.strs[index]);
	else
	  xobj->cache_.insert (caService::CA_TAG_VALUE, index);
	caRequestObject::fillResult (reqMask, reqType, *retValue, xobj->cache_);
      
	// call user callbacks
	xobj->callCallbacks (cbkst, reqObj, xobj->cache_); 
      }
      break;
    case DBR_SHORT:
      if (!xobj->cacheInited_) {
	// setup internal buffer pointer
	caMonObj::setupMonitorBuffer (tagval, xobj->cache_, xobj->data_,
				      CDEV_INT32);
	xobj->cacheInited_ = 1;
      }
    
      if (args.count == 1){
	struct dbr_time_short *retValue = 0;
	retValue = (struct dbr_time_short *)args.dbr;
	caRequestObject::updateResult (reqMask, reqType, 
				       *retValue, xobj->data_, 
				       reqObj->propCache() );

	if (reqObj->longMask_ != 0) {
	  if (reqObj->bitValuesChanged (*(int *)(xobj->data_.value), 
					reqObj->longMask_))
	    xobj->callCallbacks (cbkst, reqObj, xobj->cache_);	  
	}
	else
	  xobj->callCallbacks (cbkst, reqObj, xobj->cache_);	  
      }
      else {
	xobj->cache_.remove ();
	caRequestObject::convertData ((int)reqObj->channel_->type_,
				      caService::CA_TAG_VALUE,
				      reqMask, caRequestObject::TIMESTMP,
				      args, xobj->cache_);
	xobj->callCallbacks (cbkst, reqObj, xobj->cache_); 
      }
      break;
    case DBR_CHAR:
      if (!xobj->cacheInited_) {
	// setup internal buffer pointer
	caMonObj::setupMonitorBuffer (tagval, xobj->cache_, xobj->data_,
				      CDEV_BYTE);
	xobj->cacheInited_ = 1;
      }
    
      if (args.count == 1){
	struct dbr_time_char *retValue = 0;
	retValue = (struct dbr_time_char *)args.dbr;
	caRequestObject::updateResult (reqMask, reqType, 
				       *retValue, xobj->data_, 
				       reqObj->propCache() );

	if (reqObj->longMask_ != 0) {
	  BYTE vtmp = *(BYTE *)(xobj->data_.value);
	  if (reqObj->bitValuesChanged ((int)vtmp,
					reqObj->longMask_))
	    xobj->callCallbacks (cbkst, reqObj, xobj->cache_);	  
	}
	else
	  xobj->callCallbacks (cbkst, reqObj, xobj->cache_);	  
      }
      else {
	xobj->cache_.remove ();
	caRequestObject::convertData ((int)reqObj->channel_->type_,
				      caService::CA_TAG_VALUE,
				      reqMask, caRequestObject::TIMESTMP,
				      args, xobj->cache_);
	xobj->callCallbacks (cbkst, reqObj, xobj->cache_); 
      }
      break;
    case DBR_LONG:
      if (!xobj->cacheInited_) {
	// setup internal buffer pointer
	caMonObj::setupMonitorBuffer (tagval, xobj->cache_, xobj->data_,
				      CDEV_INT32);
	xobj->cacheInited_ = 1;
      }
      
      if (args.count == 1) {
	struct dbr_time_long *retValue = 0;
	retValue = (struct dbr_time_long *)args.dbr;
	caRequestObject::updateResult (reqMask, reqType, 
				       *retValue, xobj->data_,
				       reqObj->propCache() );

	if (reqObj->longMask_ != 0) {
	  if (reqObj->bitValuesChanged (*(int *)(xobj->data_.value), 
					reqObj->longMask_))
	    xobj->callCallbacks (cbkst, reqObj, xobj->cache_);
	}
	else
	  xobj->callCallbacks (cbkst, reqObj, xobj->cache_);
      }
      else {
	xobj->cache_.remove ();
	caRequestObject::convertData ((int)reqObj->channel_->type_,
				      caService::CA_TAG_VALUE,
				      reqMask, caRequestObject::TIMESTMP,
				      args, xobj->cache_);
	xobj->callCallbacks (cbkst, reqObj, xobj->cache_); 
      }
      break;
    case DBR_FLOAT:
      if (!xobj->cacheInited_) {
	// setup internal buffer pointer
	caMonObj::setupMonitorBuffer (tagval, xobj->cache_, xobj->data_,
				      CDEV_FLOAT);
	xobj->cacheInited_ = 1;
      }

      if (args.count == 1) {
	struct dbr_time_float *retValue = 0;
	retValue = (struct dbr_time_float *)args.dbr;
	caRequestObject::updateResult (reqMask, reqType, *retValue, 
				       xobj->data_,
				       reqObj->propCache() );
	xobj->callCallbacks (cbkst, reqObj, xobj->cache_);
      }
      else {
	xobj->cache_.remove ();
	caRequestObject::convertData ((int)reqObj->channel_->type_,
				      caService::CA_TAG_VALUE,
				      reqMask, caRequestObject::TIMESTMP,
				      args, xobj->cache_);
	xobj->callCallbacks (cbkst, reqObj, xobj->cache_); 
      }
      break;
    case DBR_DOUBLE:
      if (!xobj->cacheInited_) {
	// setup internal buffer pointer
	caMonObj::setupMonitorBuffer (tagval, xobj->cache_, xobj->data_,
				      CDEV_DOUBLE);
	xobj->cacheInited_ = 1;
      }
      
      if (args.count == 1) {
	struct dbr_time_double *retValue = 0;
	retValue = (struct dbr_time_double *)args.dbr;
	caRequestObject::updateResult (reqMask, reqType,
				       *retValue, xobj->data_,
				       reqObj->propCache() );
	xobj->callCallbacks (cbkst, reqObj, xobj->cache_);      
      }
      else {
	xobj->cache_.remove ();
	caRequestObject::convertData ((int)reqObj->channel_->type_,
				      caService::CA_TAG_VALUE,
				      reqMask, caRequestObject::TIMESTMP,
				      args, xobj->cache_);
	xobj->callCallbacks (cbkst, reqObj, xobj->cache_); 
      }
      break;
    default:
      printf("Fatal Error: Unsupported data type\n");
      break;
    }
  }
  else 
    xobj->callCallbacks (CDEV_IOFAILED, reqObj, xobj->cache_);          
}  

int
caRequestObject::findSvcData (char *device,
			      char *msg,
			      cdevSystem& system,
			      char *svcData,
			      int  dataLen)
{
  char message[256], fullname[128];

  if (system.prefix())
    ::sprintf (fullname, "%s%s",system.prefix(),device);
  else
    ::strcpy (fullname, device);

  ::sprintf (message,"resolveServiceData %s %s", fullname, msg);

  cdevData result;
  int status = (system.nameServer()).send (message, 0, result);
  if (status == CDEV_SUCCESS){
    result.get (caService::CA_TAG_PV, svcData, dataLen);
    int rd;
    int dataStatus = result.get (caService::CA_TAG_RO, &rd);
    if (dataStatus == CDEV_SUCCESS){ // channel is readonly
      readonly_ = 1;
    }
#ifdef _CDEV_DEBUG
    printf("Service data is %s\n",svcData);
#endif
    return status;
  }
  else
    return CDEV_ERROR;
}

int
caRequestObject::findAction (char *msg, int& action)
{
  char token0[64], token1[64], token2[64];
  int  status;

#ifdef __GNUC__ 
  // some version of gcc demand a memory location for msg
  char buffer[256];
  strncpy (buffer, msg, sizeof (buffer));
  status = ::sscanf (buffer, "%s %s",token0, token1);
#else
  status = ::sscanf (msg, "%s %s", token0, token1);
#endif

  if (::strcmp (token0, "get") == 0)
    action = caRequestObject::GET;
  else if (::strcmp (token0, "set") == 0)
    action = caRequestObject::SET;
  else if (::strcmp (token0, "monitorOn") == 0)
    action = caRequestObject::MONITOR_ON;
  else if (::strcmp (token0, "monitorOff") == 0)
    action = caRequestObject::MONITOR_OFF;
  else
    action = caRequestObject::UNKNOWN;

  return CDEV_SUCCESS;
}

int
caRequestObject::newTriggerSource (void)
{
  cdevData& cxt = getContext ();
  int val = 0;
  if (cxt.get (caService::CA_TAG_VALUE, &val) == CDEV_SUCCESS) {
    if (val == 0) {
      caPchannel* pch = channel_->pchannels_;
      for (int i = 2; i < MAX_NUM_PCHANNELS; i++) {
	if (cxt.get (pch[i].tag, &val) == CDEV_SUCCESS){
	  // caller specify a trigger or I am going to setup channel
	  // for those properties which are not changed rapidly
	  if (val >= 2)
	    return 1;
	}
      }
    }
  }
  return 0;
}

void
caRequestObject::setPropCache (void)
{
  if (cache_ == 0) {
    prop_ = new cdevData ();
    cache_ = new caMonData ();
    int status = caMonObj::setupMonitorBuffer (caService::CA_TAG_VALUE, 
					       *prop_, *cache_, CDEV_FLOAT);
    if (status != CDEV_SUCCESS) {
      delete cache_;
      delete prop_;
      cache_ = 0;
      prop_ = 0;
    }
  }
}

caMonData*
caRequestObject::propCache (void) const
{
  return cache_;
}

void
caRequestObject::updatePropCache (caRequestObject* obj, 
				  int tag, 
				  cdevData& data)
{
  caMonData* cache = obj->propCache ();
  
  if (cache != 0) {
    if (tag == caService::CA_TAG_STATUS)
      data.get (tag, cache->status);
    else if (tag == caService::CA_TAG_SEVERITY)
      data.get (tag, cache->severity);
    else if (tag == caService::CA_TAG_PRECISION)
      data.get (tag, cache->precision);
    else if (tag == caService::CA_TAG_DISPHI)
      data.get (tag, cache->disphi);
    else if (tag == caService::CA_TAG_DISPLO)
      data.get (tag, cache->displo);
    else if (tag == caService::CA_TAG_ALRMHI)
      data.get (tag, cache->alrmhi);
    else if (tag == caService::CA_TAG_ALRMLO)
      data.get (tag, cache->alrmlo);
    else if (tag == caService::CA_TAG_WRNHI)
      data.get (tag, cache->warnhi);
    else if (tag == caService::CA_TAG_WRNLO)
      data.get (tag, cache->warnlo);
    else if (tag == caService::CA_TAG_CTRLHI)
      data.get (tag, cache->ctrlhi);
    else if (tag == caService::CA_TAG_CTRLLO)
      data.get (tag, cache->ctrllo);
    else 
      cdevSystem::defaultSystem().reportError 
	  	(CDEV_SEVERITY_ERROR, "caRequestObject", obj, 
		"Warning: property cache buffers does not have this tag %d\n",
	     tag);
  }
}
