//-----------------------------------------------------------------------------
// 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:
//      Epics Channel Access request object class 
//
// Author:  Jie Chen
//
// Revision History:
//   caRequestObject.h,v
// Revision 1.13  1998/04/10  13:55:41  chen
// add access information to caService
//
// Revision 1.12  1998/03/05  18:49:14  chen
// fix a bug on context == 2 on property channels
//
// Revision 1.11  1997/08/08  16:41:37  chen
// add char type and fix dbr== null
//
// Revision 1.10  1997/03/03  17:36:31  chen
// add buffering to channel access connection
//
// Revision 1.9  1996/09/18  14:46:52  chen
// Channel holds multiple monitor objects
//
// Revision 1.8  1996/09/10  19:21:05  chen
// change C++ include header
//
// Revision 1.7  1996/05/02  14:18:38  chen
// Fix monitorOn and unfinished transaction bug
//
// Revision 1.6  1995/10/17  20:20:42  chen
// redefine getState
//
// Revision 1.5  1995/10/06  19:03:38  chen
// add type name on enum
//
// Revision 1.4  1995/10/03  19:56:32  chen
// add monitoring properties
//
// Revision 1.3  1995/07/06  19:48:22  chen
// add default frequency to do connection
//
// Revision 1.2  1995/07/05  18:33:12  chen
// sync implemented by async + removal some codes
//
// Revision 1.1.1.1  1995/06/16  17:14:03  epics
// initial import of cdev
//
//
#ifndef _CA_REQUEST_OBJECT_H
#define _CA_REQUEST_OBJECT_H

#include <cdevSystem.h>
#include <cdevRequestObject.h>

// epics stuff
#if !defined (_EPICS_3_12)
#define __STDC__
extern "C" {
#endif

#include <cadef.h>
#include <db_access.h>

#if !defined (_EPICS_3_12)
};
#undef __STDC__
#endif

#include "caMonObj.h"

typedef struct _ca_attr
{
  char *attrName;
  int  attrValue;
}caAttr;

class caService;
class caChannel;
class caPchannel;
class cdevTranObj;
class caMonObj;

class caRequestObject: public cdevRequestObject
{
public:
  // constructor and destructor
  caRequestObject (char *device, 
		   char *msg, 
		   caService *svc,
		   cdevSystem& system = cdevSystem::defaultSystem() );
  ~caRequestObject (void);

  int setContext (cdevData &ctx);
  // PURPOSE: set io context to this request object.
  //          Redefine setContext inherited from cdevIOcontext
  // REQUIRE: nothing
  // PROMISE: value tag will be always set, return CDEV_SUCCESS

  int send (cdevData& out, cdevData& result);
  int send (cdevData *out, cdevData& result);
  int send (cdevData& out, cdevData* result);
  int send (cdevData *out, cdevData* result);
  // PURPOSE: Synchronous IO operations "get", "set", "monitorOn/off"
  // REQUIRE: When set, out must be supplied. When get result must be provided
  // PROMISE: return CDEV_SUCCESS: success. 
  //          There will be no synchronous monitorOn.

  int sendNoBlock (cdevData& out, cdevData& result);
  int sendNoBlock (cdevData* out, cdevData& result);
  int sendNoBlock (cdevData& out, cdevData* result);
  int sendNoBlock (cdevData* out, cdevData* result);
  // PURPOSE: Asynchronous IO operations used in conjunction with cdevGroup or system
  // REQUIRE: When set, out must be supplied. When get result must be provied.
  //          Caller must call system.pend or group.pend to flush out network
  //          requests.
  // PROMISE: return CDEV_SUCCESS: requests have been sent out. 

  int sendCallback (cdevData& out, cdevCallback& callback);
  int sendCallback (cdevData* out, cdevCallback& callback);
  // PURPOSE: Asynchromous IO operations with a user supplied callback function
  // REQUIRE: When set, out must be provided.
  // PROMISE: User callback function will be called with status information
  //          status = CDEV_DISCONNECTED: channel discconected
  //          status = CDEV_SUCCESS     : everything is OK
  //          status = CDEV_RECONNECTED : channel reconnected
  //          status = CDEV_ERROR       : something fishy. 

  int getState (void);
  // PURPOSE: get connection state
  // REQUIRE: none
  // PROMISE: CDEV_CONNECTED or CDEV_NOTCONNECTED

  int getAccess (void);
  // PURPOSE: get access state
  // REQUIRE: none
  // PROMISE: CDEV_ACCESS_NONE, CDEV_ACCESS_READONLY, CDEV_ACCESS_WRITE

  const char *className (void) const {return "caRequestObject";}

protected:
  // attach to a particular channel
  void attachChannel (void);

  // real implementation of of execNoBlock & execCallback
  int execNoBlock (cdevData* out, cdevData* result, void* arg);
  int execCallback (cdevData* out, cdevCallback& callback, void* arg);

  // async get value callback function
  int getValueCbk        (caChannel* channel, cdevTranObj *obj);

  // insert result to user cdevData according to mask
  static void fillResult (int mask, int type, 
			  struct dbr_time_string& val, 
			  cdevData& result);
  static void fillResult (int mask, int type,
			  struct dbr_time_short&  val, 
			  cdevData& result);
  static void fillResult (int mask, int type,
			  struct dbr_time_char&  val, 
			  cdevData& result);
  static void fillResult (int mask, int type,
			  struct dbr_time_long&   val, 
			  cdevData& result);
  static void fillResult (int mask, int type,
			  struct dbr_time_float&  val, 
			  cdevData& result);
  static void fillResult (int mask, int type,
			  struct dbr_time_double& val, 
			  cdevData& result);
  static void fillResult (int mask, int type,
			  struct dbr_time_enum&   val, 
			  cdevData& result);
  static void fillResult (int mask, int type,
			  struct dbr_ctrl_short&  val, 
			  cdevData& result);
  static void fillResult (int mask, int type,
			  struct dbr_ctrl_char&  val, 
			  cdevData& result);
  static void fillResult (int mask, int type,
			  struct dbr_ctrl_long&   val, 
			  cdevData& result);
  static void fillResult (int mask, int type,
			  struct dbr_ctrl_float&  val, 
			  cdevData& result);
  static void fillResult (int mask, int type,
			  struct dbr_ctrl_double& val, 
			  cdevData& result);
  static void fillResult (int mask, int type,
			  struct dbr_ctrl_enum&   val, 
			  cdevData& result);

  // update data value to fixed buffers of result and update property
  // values if necessary pointed by cache
  static void updateResult (int mask, int type,
			    struct dbr_time_short&  val, 
			    caMonData& result,
			    caMonData* cache);
  static void updateResult (int mask, int type,
			    struct dbr_time_char&  val, 
			    caMonData& result,
			    caMonData* cache);
  static void updateResult (int mask, int type,
			    struct dbr_time_long&   val, 
			    caMonData& result,
			    caMonData* cache);
  static void updateResult (int mask, int type,
			    struct dbr_time_float&  val, 
			    caMonData& result,
			    caMonData* cache);
  static void updateResult (int mask, int type,
			    struct dbr_time_double& val, 
			    caMonData& result,
			    caMonData* cache);

  // return 1 if the context contains new trigger source (context
  // value >= 2
  int                      newTriggerSource (void);

  // setup property monitoring buffer which holds latest values
  // of properties
  void                     setPropCache     (void);
  caMonData*               propCache        (void) const;
  static void              updatePropCache  (caRequestObject* obj,
					     int tag,
					     cdevData& data);

  // convert data from channel access data type to cdevdata according to
  // channel type and request mask/type
  static void convertData (int chtype,
			   int tag,
			   int reqMask,
			   int reqType,
			   struct event_handler_args& args,
			   cdevData& data);

  // set value callback
  int  setValueCbk        (caChannel* channel, cdevTranObj *obj);

  // monitor channel 
  int  monitorValue       (caChannel* channel, caMonObj  *obj);
  void initPropChannels   (caPchannel* pchannel);
  void initPropChannels   (caChannel* channel);
  void monitorProperties  (caChannel* channel, caMonObj *obj);
  int  monitorPropValue   (caChannel* channel, caMonObj *obj);

  // add callback to the property channel that has been created
  void addCbksToPChannels (caChannel* channel,
			   cdevCallback& cbk,
			   cdevData*     out);

  // data area
  // enumerated value for ca request data type
  typedef enum {VALUE = 1, STATUS = 4, TIMESTMP = 8, GRINFO = 16, 
	       CTRLINFO = 0x00001000} CAENUMTAG;
  // static channel acccess attribute
  static caAttr caAttributes[];
  static int    numAttributes;
  // static requestObject action
  typedef enum {GET = 0x1000, SET, MONITOR_ON, 
	       MONITOR_OFF, PUT, UNKNOWN} CAENUMVERB;

private:
  // default get callback
  static void defaultGetCallback (struct event_handler_args);
  // default set callback
  static void defaultSetCallback (struct event_handler_args);
  // call user callback on monitor
  static void callMonitorCallback (struct event_handler_args);
  // call user callback on property monitoring callback 
  static void callPropMonitorCallback (struct event_handler_args); 
  // find out service data
  int findSvcData (char *device, char *msg, cdevSystem& system, 
		   char *svcData, int dataLen);
  // find action data from message
  static int findAction (char *msg, int& action);

  // properties cached values
  caMonData* cache_;
  cdevData*  prop_;

  // action type
  int action_;
  // readonly flag
  int readonly_;
  // real channel access channel class object ptr
  caChannel *channel_;
  // request data type flag
  int reqType_;
  int reqMask_;
};
#endif
