Files
epics-base/src/libCom/sydSubrCA.c
1992-03-09 10:51:26 +00:00

582 lines
17 KiB
C
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* $Id$
* Author: Roger A. Cole
* Date: 06-12-91
*
* Experimental Physics and Industrial Control System (EPICS)
*
* Copyright 1991-92, the Regents of the University of California,
* and the University of Chicago Board of Governors.
*
* This software was produced under U.S. Government contracts:
* (W-7405-ENG-36) at the Los Alamos National Laboratory,
* and (W-31-109-ENG-38) at Argonne National Laboratory.
*
* Initial development by:
* The Controls and Automation Group (AT-8)
* Ground Test Accelerator
* Accelerator Technology Division
* Los Alamos National Laboratory
*
* Co-developed with
* The Controls and Computing Group
* Accelerator Systems Division
* Advanced Photon Source
* Argonne National Laboratory
*
* Modification Log:
* -----------------
* .00 06-12-91 rac initial version
* .01 06-18-91 rac installed in SCCS
* .02 06-19-91 rac replace <fields.h> with <alarm.h>
* .03 08-15-91 rac use new call for sydOpen
* .04 09-23-91 rac allow async completion of ca_search
* .05 02-13-92 rac use ADEL for monitoring; perform time-stamp
* rounding if requested
* .06 03-08-92 rac use deadband from pSspec for monitoring
*
* make options
* -DvxWorks makes a version for VxWorks
* -DNDEBUG don't compile assert() checking
* -DDEBUG compile various debug code, including checks on
* malloc'd memory
*/
/*+/mod***********************************************************************
* TITLE sydSubrCA.c - acquire synchronous samples from Channel Access
*
* DESCRIPTION
*
*-***************************************************************************/
#include <genDefs.h>
#define SYD_PRIVATE
#include <sydDefs.h>
#include <alarm.h>
#ifndef INCLcadefh
# include <cadef.h>
#endif
#ifdef vxWorks
# include <vxWorks.h>
# include <stdioLib.h>
#else
# include <stdio.h>
#endif
long sydCAFunc();
void sydCAFuncConnHandler();
void sydCAFuncMonHandler();
long sydFuncCA_finishConn();
#define sydCA_searchNOW /* force immediate completion of connection */
#undef sydCA_searchNOW /* asynchronous completion of connection */
void sydCAFuncGetGR(), sydCAFuncInitGR();
long
sydOpenCA(ppSspec, pHandle)
SYD_SPEC **ppSspec; /* O pointer to synchronous set spec pointer */
void *pHandle; /* I NULL; someday might implement a callback
routine to be called when a sample is available */
{
long stat;
assert(ppSspec != NULL);
if ((*ppSspec = (SYD_SPEC *)GenMalloc(sizeof(SYD_SPEC))) == NULL)
return S_syd_noMem;
(*ppSspec)->pFunc = sydCAFunc;
(*ppSspec)->type = SYD_TY_CA;
if ((stat = sydCAFunc(*ppSspec, NULL, SYD_FC_INIT, pHandle)) != S_syd_OK){
GenFree((char *)*ppSspec);
return stat;
}
(*ppSspec)->nInBufs = SYD_CHAN_MAX_IN;
return sydOpen(ppSspec);
}
/*+/subr**********************************************************************
* NAME sydCAFunc - handle "Channel Access" data interactions
*
* DESCRIPTION
* Handle acquisition of synchronous samples from Channel Access.
*
* The caller must periodically call ca_pend_event in order for these
* routines to make connections to channels, detect connect/disconnect,
* and receive data. The frequency with which ca_pend_event is called
* should be about the same as the maximum frequency that data values
* will change.
*
* sydCAFunc(pSspec, NULL, SYD_FC_INIT, pCallback)
* sydCAFunc(pSspec, pSChan, SYD_FC_OPEN, NULL) chanName already in pSChan
* sydCAFunc(pSspec, pSChan, SYD_FC_READ, NULL)
* sydCAFunc(pSspec, pSChan, SYD_FC_POSITION, &stamp) no operation performed
* sydCAFunc(pSspec, pSChan, SYD_FC_CLOSE, NULL)
* sydCAFunc(pSspec, NULL, SYD_FC_FILEINFO, outStream)
* sydCAFunc(pSspec, NULL, SYD_FC_WRAPUP, NULL) no operation performed
*
* RETURNS
* S_syd_OK, or
* S_syd_EOF, or
* S_syd_ERROR, or
* other code indicating error
*
* BUGS
* o doesn't do anything (or even detect) overwriting un-sampled
* buffers (for SYD_FC_READ)
*
* SEE ALSO
*
* EXAMPLE
*
*-*/
long
sydCAFunc(pSspec, pStruct, funcCode, pArg)
SYD_SPEC *pSspec; /* IO pointer to synchronous set spec */
void *pStruct; /* IO pointer to data struct used by funcCode */
enum sydFuncCode funcCode;/* I function code */
void *pArg; /* I pointer to arg, as required by funcCode */
{
SYD_CHAN *pSChan; /* pointer to syncSet channel descriptor */
chid pCh; /* channel pointer */
long retStat=S_syd_OK;
long stat;
int i;
pSChan = (SYD_CHAN *)pStruct;
if (funcCode == SYD_FC_INIT) {
pSspec->pHandle = (void *)NULL;
}
else if (funcCode == SYD_FC_OPEN) {
/*-----------------------------------------------------------------------------
* Initiate a connection to Channel Access for this channel. The action
* depends on whether searches are to be completed _now_, or asynchronously.
*
* If searches are to be completed now, then a ca_pend_io is done. If
* the search isn't successful, an error return is given.
*
* If searches are to be asynchronous, the connection handler is allowed
* to do the required connection processing.
*----------------------------------------------------------------------------*/
pSChan->dbrType = TYPENOTCONN;
pSChan->elCount = 0;
pSChan->evid = NULL;
pSChan->gotGr = 0;
pSChan->restart = 1;
sydCAFuncInitGR(pSChan);
#ifdef sydCA_searchNOW
stat = ca_search(pSChan->name, &pCh);
#else
stat = ca_build_and_connect(pSChan->name, TYPENOTCONN, 0, &pCh,
NULL, sydCAFuncConnHandler, pSChan);
#endif
if (stat != ECA_NORMAL) {
retStat = S_syd_ERROR;
(void)printf("sydCAFunc: error on ca_search for %s :\n%s\n",
pSChan->name, ca_message(stat));
return retStat;
}
pSChan->pHandle = (void *)pCh;
#ifdef sydCA_searchNOW
stat = ca_pend_io(2.);
if (stat != ECA_NORMAL) {
retStat = S_syd_chanNotFound;
ca_clear_channel(pCh);
return retStat;
}
ca_puser(pCh) = pSChan;
retStat = sydFuncCA_finishConn(pSChan, pCh);
if (retStat != S_syd_OK)
return retStat;
stat = ca_change_connection_event(pCh, sydCAFuncConnHandler);
if (stat != ECA_NORMAL) {
retStat = S_syd_ERROR;
(void)printf("sydCAFunc: error on ca_search for %s :\n%s\n",
pSChan->name, ca_message(stat));
ca_clear_channel(pCh);
return retStat;
}
#else
retStat = S_syd_chanNotConn;
#endif
}
else if (funcCode == SYD_FC_READ) {
;
}
else if (funcCode == SYD_FC_POSITION) {
;
}
else if (funcCode == SYD_FC_CLOSE) {
if (pSChan->pHandle != NULL) {
stat = ca_clear_channel((chid)pSChan->pHandle);
if (stat != ECA_NORMAL)
retStat = S_syd_ERROR;
}
}
else if (funcCode == SYD_FC_FILEINFO) {
(void)fprintf((FILE *)pArg, "no info for Channel Access\n");
}
else if (funcCode == SYD_FC_WRAPUP) {
;
}
return retStat;
}
/*/subhead sydFuncCA_finishConn------------------------------------------------
*
*----------------------------------------------------------------------------*/
static long
sydFuncCA_finishConn(pSChan, pCh)
SYD_CHAN *pSChan; /* pointer to syncSet channel descriptor */
chid pCh; /* channel pointer */
{
long retStat=S_syd_OK;
long stat;
pSChan->dbfType = ca_field_type(pCh);
pSChan->dbrType = dbf_type_to_DBR_TIME(pSChan->dbfType);
pSChan->elCount = ca_element_count(pCh);
pSChan->dbrGrType = dbf_type_to_DBR_GR(pSChan->dbfType);
stat = ca_array_get_callback(pSChan->dbrGrType, 1, pCh,
sydCAFuncMonHandler, NULL);
if (stat != ECA_NORMAL) {
retStat = S_syd_ERROR;
(void)printf(
"sydCAFunc: error on ca_array_get_callback(GR) for %s :\n%s\n",
pSChan->name, ca_message(stat));
ca_clear_channel(pCh);
return retStat;
}
if (pSChan->evid == NULL) {
stat = ca_add_masked_array_event(pSChan->dbrType, pSChan->elCount, pCh,
sydCAFuncMonHandler, NULL, 0., 0., 0., &pSChan->evid,
pSChan->pSspec->deadband);
if (stat != ECA_NORMAL) {
retStat = S_syd_ERROR;
(void)printf(
"sydCAFunc: error on ca_add_masked_array_event for %s :\n%s\n",
pSChan->name, ca_message(stat));
ca_clear_channel(pCh);
return retStat;
}
}
pSChan->conn = 1;
pSChan->discon = 0;
retStat = sydChanOpen1(pSChan->pSspec, pSChan);
return retStat;
}
/*+/subr**********************************************************************
* NAME sydCAFuncConnHandler - connection handler for sync CA data routines
*
* DESCRIPTION
*
* RETURNS
*
* BUGS
* o text
*
* SEE ALSO
*
* EXAMPLE
*
*-*/
static void
sydCAFuncConnHandler(arg)
struct connection_handler_args arg;
{
SYD_CHAN *pSChan; /* pointer to syncSet channel descriptor */
chid pCh; /* channel pointer */
long retStat=S_syd_OK;
long stat;
int i;
pCh = arg.chid;
pSChan = (SYD_CHAN *)ca_puser(pCh);
if (arg.op == CA_OP_CONN_UP) {
if (pSChan->conn == 0)
retStat = sydFuncCA_finishConn(pSChan, pCh);
if (retStat != S_syd_OK) {
(void)printf(
"sydCAFuncConnHandler: error finishing connection for %s\n", ca_name(pCh));
}
pSChan->restart = 1;
}
else {
pSChan->discon = 1;
}
}
/*+/subr**********************************************************************
* NAME sydCAFuncMonHandler - handle monitor and get_callback
*
* DESCRIPTION
* Receive data from Channel Access and store in the input buffers
* of channels' synchronous data descriptors. The data will be
* retrieved later and assembled into synchronous samples by
* sydSampleGet.
*
* If data are received from Channel Access faster than the program
* removes it with sydSampleGet, then the oldest unretrieved data
* is lost.
*
* If time stamp rounding is to be done, it is done here.
*
* RETURNS
* void
*
* BUGS
* o no indication is given of lost data
* o no precautions are taken to protect against the case where a
* monitor comes in (following a disconnect/connect) with an old, old
* time stamp.
*
* SEE ALSO
* sydSampleGet, sydSampleStore
*
* EXAMPLE
* sydResetBufKeepNewest(pSspec);
* sydSampleSetAlloc(pSspec, nSamp);
* pSspec->sampleCount = 0;
* pSspec->refTs->secPastEpoch = 0;
* pSspec->refTs->nsec = 0;
*
* while (sydSampleGet(pSspec) != S_syd_noDataNow)
* sydSampleStore(pSspec);
*
*
*-*/
void
sydCAFuncMonHandler(arg)
struct event_handler_args arg;
{
SYD_CHAN *pSChan; /* pointer to syncSet channel descriptor */
SYD_SPEC *pSspec; /* pointer to syncSet spec */
chid pCh; /* channel pointer */
long retStat=S_syd_OK;
long stat;
int i, i1, j;
long inStatus;
union db_access_val *pBuf;
pCh = arg.chid;
pBuf = arg.dbr;
pSChan = (SYD_CHAN *)ca_puser(pCh);
pSspec = pSChan->pSspec;
if (dbr_type_is_GR(arg.type)) {
pSChan->gotGr = 1;
sydCAFuncGetGR(pSChan, (union db_access_val *)arg.dbr);
stat = sydChanOpenGR(pSChan);
return;
}
if (!dbr_type_is_TIME(arg.type))
return;
if (pSspec->roundNsec > 0)
sydTsRound(&pBuf->tfltval.stamp, pSspec->roundNsec);
if (pSChan->lastInBuf == pSChan->firstInBuf &&
pBuf->tfltval.stamp.secPastEpoch != 0) {
if (pSspec->refTs.secPastEpoch == 0 ||
TsCmpStampsLE(&pBuf->tfltval.stamp, &pSspec->refTs)) {
pSChan->lastInBuf = pSChan->firstInBuf = -1;
pSspec->refTs = pBuf->tfltval.stamp;
}
}
i = NEXT_INBUF(pSChan, pSChan->lastInBuf);
i1 = NEXT_INBUF(pSChan, i);
#if 0
if (i == pSChan->firstInBuf)
return; /* don't overwrite old data */
else if (pSChan->restart) {
#endif
if (pSChan->restart) {
pSChan->inStatus[i] = SYD_B_RESTART;
pSChan->restart = 0;
}
else
pSChan->inStatus[i] = SYD_B_FULL;
if (pSChan->dbrType == DBR_TIME_FLOAT) {
pSChan->pInBuf[i]->tfltval = pBuf->tfltval;
if (pSChan->elCount > 1) {
float *pDest = &pSChan->pInBuf[i]->tfltval.value + 1;
float *pSrc = &pBuf->tfltval.value + 1;
for (j=1; j<pSChan->elCount; j++)
*pDest++ = *pSrc++;
}
}
else if (pSChan->dbrType == DBR_TIME_SHORT) {
pSChan->pInBuf[i]->tshrtval = pBuf->tshrtval;
if (pSChan->elCount > 1) {
short *pDest = &pSChan->pInBuf[i]->tshrtval.value + 1;
short *pSrc = &pBuf->tshrtval.value + 1;
for (j=1; j<pSChan->elCount; j++)
*pDest++ = *pSrc++;
}
}
else if (pSChan->dbrType == DBR_TIME_DOUBLE) {
pSChan->pInBuf[i]->tdblval = pBuf->tdblval;
if (pSChan->elCount > 1) {
double *pDest = &pSChan->pInBuf[i]->tdblval.value + 1;
double *pSrc = &pBuf->tdblval.value + 1;
for (j=1; j<pSChan->elCount; j++)
*pDest++ = *pSrc++;
}
}
else if (pSChan->dbrType == DBR_TIME_LONG) {
pSChan->pInBuf[i]->tlngval = pBuf->tlngval;
if (pSChan->elCount > 1) {
long *pDest = &pSChan->pInBuf[i]->tlngval.value + 1;
long *pSrc = &pBuf->tlngval.value + 1;
for (j=1; j<pSChan->elCount; j++)
*pDest++ = *pSrc++;
}
}
else if (pSChan->dbrType == DBR_TIME_STRING) {
pSChan->pInBuf[i]->tstrval = pBuf->tstrval;
if (pSChan->elCount > 1) {
char *pDest = pSChan->pInBuf[i]->tstrval.value + db_strval_dim;
char *pSrc = pBuf->tstrval.value + db_strval_dim;
for (j=1; j<pSChan->elCount; j++) {
strcpy(pDest, pSrc);
pDest += db_strval_dim;
pSrc += db_strval_dim;
}
}
}
else if (pSChan->dbrType == DBR_TIME_ENUM) {
pSChan->pInBuf[i]->tenmval = pBuf->tenmval;
if (pSChan->elCount > 1) {
short *pDest = &pSChan->pInBuf[i]->tenmval.value + 1;
short *pSrc = &pBuf->tenmval.value + 1;
for (j=1; j<pSChan->elCount; j++)
*pDest++ = *pSrc++;
}
}
else if (pSChan->dbrType == DBR_TIME_CHAR) {
pSChan->pInBuf[i]->tchrval = pBuf->tchrval;
if (pSChan->elCount > 1) {
unsigned char *pDest = &pSChan->pInBuf[i]->tchrval.value + 1;
unsigned char *pSrc = &pBuf->tchrval.value + 1;
for (j=1; j<pSChan->elCount; j++)
*pDest++ = *pSrc++;
}
}
pSChan->lastInBuf = i;
if (pSChan->firstInBuf < 0)
pSChan->firstInBuf = i;
#if 0
need to check if about to overflow buffers, and signal handler if so
#endif
}
/*/subhead sydCAFuncGetGR-----------------------------------------------------
*
*----------------------------------------------------------------------------*/
static void
sydCAFuncGetGR(pSChan, pGrBuf)
SYD_CHAN *pSChan; /* pointer to syncSet channel descriptor */
union db_access_val *pGrBuf;/* pointer to buffer with graphics info */
{
if (pSChan->dbrGrType == DBR_GR_FLOAT)
pSChan->grBuf.gfltval = pGrBuf->gfltval;
else if (pSChan->dbrGrType == DBR_GR_SHORT)
pSChan->grBuf.gshrtval = pGrBuf->gshrtval;
else if (pSChan->dbrGrType == DBR_GR_DOUBLE)
pSChan->grBuf.gdblval = pGrBuf->gdblval;
else if (pSChan->dbrGrType == DBR_GR_LONG)
pSChan->grBuf.glngval = pGrBuf->glngval;
else if (pSChan->dbrGrType == DBR_GR_STRING)
pSChan->grBuf.gstrval = pGrBuf->gstrval;
else if (pSChan->dbrGrType == DBR_GR_ENUM)
pSChan->grBuf.genmval = pGrBuf->genmval;
else if (pSChan->dbrGrType == DBR_GR_CHAR)
pSChan->grBuf.gchrval = pGrBuf->gchrval;
else
assertAlways(0);
}
/*/subhead sydCAFuncInitGR-----------------------------------------------------
*
*----------------------------------------------------------------------------*/
static void
sydCAFuncInitGR(pSChan)
SYD_CHAN *pSChan; /* pointer to syncSet channel descriptor */
{
if (pSChan->dbrType == DBR_TIME_FLOAT) {
#define FLT_DEST pSChan->grBuf.gfltval
FLT_DEST.status = NO_ALARM;
FLT_DEST.severity = NO_ALARM;
FLT_DEST.precision = 3;
(void)strcpy(FLT_DEST.units, " ");
FLT_DEST.upper_disp_limit = 0.;
FLT_DEST.lower_disp_limit = 0.;
FLT_DEST.upper_alarm_limit = 0.;
FLT_DEST.lower_alarm_limit = 0.;
FLT_DEST.upper_warning_limit = 0.;
FLT_DEST.lower_warning_limit = 0.;
}
else if (pSChan->dbrType == DBR_TIME_SHORT) {
#define SHRT_DEST pSChan->grBuf.gshrtval
SHRT_DEST.status = NO_ALARM;
SHRT_DEST.severity = NO_ALARM;
(void)strcpy(SHRT_DEST.units, " ");
SHRT_DEST.upper_disp_limit = 0;
SHRT_DEST.lower_disp_limit = 0;
SHRT_DEST.upper_alarm_limit = 0;
SHRT_DEST.lower_alarm_limit = 0;
SHRT_DEST.upper_warning_limit = 0;
SHRT_DEST.lower_warning_limit = 0;
}
else if (pSChan->dbrType == DBR_TIME_DOUBLE) {
#define DBL_DEST pSChan->grBuf.gdblval
DBL_DEST.status = NO_ALARM;
DBL_DEST.severity = NO_ALARM;
DBL_DEST.precision = 3;
(void)strcpy(DBL_DEST.units, " ");
DBL_DEST.upper_disp_limit = 0.;
DBL_DEST.lower_disp_limit = 0.;
DBL_DEST.upper_alarm_limit = 0.;
DBL_DEST.lower_alarm_limit = 0.;
DBL_DEST.upper_warning_limit = 0.;
DBL_DEST.lower_warning_limit = 0.;
}
else if (pSChan->dbrType == DBR_TIME_LONG) {
#define LNG_DEST pSChan->grBuf.glngval
LNG_DEST.status = NO_ALARM;
LNG_DEST.severity = NO_ALARM;
(void)strcpy(LNG_DEST.units, " ");
LNG_DEST.upper_disp_limit = 0;
LNG_DEST.lower_disp_limit = 0;
LNG_DEST.upper_alarm_limit = 0;
LNG_DEST.lower_alarm_limit = 0;
LNG_DEST.upper_warning_limit = 0;
LNG_DEST.lower_warning_limit = 0;
}
else if (pSChan->dbrType == DBR_TIME_STRING) {
#define STR_DEST pSChan->grBuf.gstrval
STR_DEST.status = NO_ALARM;
STR_DEST.severity = NO_ALARM;
}
else if (pSChan->dbrType == DBR_TIME_ENUM) {
#define ENM_DEST pSChan->grBuf.genmval
ENM_DEST.status = NO_ALARM;
ENM_DEST.severity = NO_ALARM;
ENM_DEST.no_str = 0;
}
else if (pSChan->dbrType == DBR_TIME_CHAR) {
#define CHR_DEST pSChan->grBuf.gchrval
CHR_DEST.status = NO_ALARM;
CHR_DEST.severity = NO_ALARM;
(void)strcpy(CHR_DEST.units, " ");
CHR_DEST.upper_disp_limit = 0;
CHR_DEST.lower_disp_limit = 0;
CHR_DEST.upper_alarm_limit = 0;
CHR_DEST.lower_alarm_limit = 0;
CHR_DEST.upper_warning_limit = 0;
CHR_DEST.lower_warning_limit = 0;
}
}