421 lines
10 KiB
C
421 lines
10 KiB
C
/* $Id$
|
||
* DESCRIPTION: Channel access interface for sequencer.
|
||
*
|
||
* Author: Andy Kozubal
|
||
* Date: July, 1991
|
||
*
|
||
* Experimental Physics and Industrial Control System (EPICS)
|
||
*
|
||
* Copyright 1991, 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:
|
||
* -----------------
|
||
* .01 07-03-91 ajk .
|
||
*/
|
||
|
||
#include "seq.h"
|
||
|
||
#ifdef ANSI
|
||
LOCAL void *getPtrToValue(union db_access_val *, chtype);
|
||
#else
|
||
LOCAL void *getPtrToValue();
|
||
#endif
|
||
|
||
|
||
/*#define DEBUG*/
|
||
|
||
#define MACRO_STR_LEN (MAX_STRING_SIZE+1)
|
||
|
||
/* Connect to the database channels through channel access */
|
||
seq_connect(sp_ptr)
|
||
SPROG *sp_ptr;
|
||
{
|
||
CHAN *db_ptr;
|
||
int status, i;
|
||
MACRO *macro_tbl;
|
||
extern seq_conn_handler();
|
||
|
||
/* Initialize CA task */
|
||
ca_task_initialize();
|
||
|
||
sp_ptr->conn_count = 0;
|
||
macro_tbl = sp_ptr->mac_ptr;
|
||
|
||
/* Search for all channels */
|
||
db_ptr = sp_ptr->channels;
|
||
for (i = 0; i < sp_ptr->nchan; i++)
|
||
{
|
||
/* Do macro substitution on channel name */
|
||
db_ptr->db_name = seqAlloc(sp_ptr, MACRO_STR_LEN);
|
||
seqMacEval(db_ptr->db_uname, db_ptr->db_name, MACRO_STR_LEN,
|
||
macro_tbl);
|
||
#ifdef DEBUG
|
||
printf("connect to \"%s\"\n", db_ptr->db_name);
|
||
#endif DEBUG
|
||
/* Connect to it */
|
||
status = ca_build_and_connect(
|
||
db_ptr->db_name, /* DB channel name */
|
||
TYPENOTCONN, /* Don't get initial value */
|
||
0, /* get count (n/a) */
|
||
&(db_ptr->chid), /* ptr to chid */
|
||
0, /* ptr to value (n/a) */
|
||
seq_conn_handler, /* connection handler routine */
|
||
db_ptr); /* user ptr is CHAN structure */
|
||
if (status != ECA_NORMAL)
|
||
{
|
||
SEVCHK(status, "ca_search");
|
||
ca_task_exit();
|
||
return -1;
|
||
}
|
||
/* Clear monitor indicator */
|
||
db_ptr->monitored = FALSE;
|
||
|
||
/* Issue monitor requests */
|
||
if (db_ptr->mon_flag)
|
||
{
|
||
seq_pvMonitor(sp_ptr, 0, db_ptr);
|
||
}
|
||
db_ptr++;
|
||
}
|
||
ca_flush_io();
|
||
|
||
if (sp_ptr->conn_flag)
|
||
{ /* Wait for all connections to complete */
|
||
while (sp_ptr->conn_count < sp_ptr->nchan)
|
||
taskDelay(30);
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
/* Channel access events (monitors) come here */
|
||
VOID seq_event_handler(args)
|
||
struct event_handler_args args;
|
||
{
|
||
SPROG *sp_ptr;
|
||
CHAN *db_ptr;
|
||
struct dbr_sts_char *dbr_sts_ptr;
|
||
void *value_ptr;
|
||
int i, nbytes;
|
||
|
||
/* User arg is ptr to db channel structure */
|
||
db_ptr = (CHAN *)args.usr;
|
||
|
||
/* Copy value returned into user variable */
|
||
value_ptr = getPtrToValue(
|
||
(union db_access_val *)args.dbr, db_ptr->get_type);
|
||
nbytes = db_ptr->size * db_ptr->count;
|
||
bcopy(value_ptr, db_ptr->var, nbytes);
|
||
|
||
/* Copy status & severity */
|
||
dbr_sts_ptr = (struct dbr_sts_char *)args.dbr;
|
||
db_ptr->status = dbr_sts_ptr->status;
|
||
db_ptr->severity = dbr_sts_ptr->severity;
|
||
|
||
/* Process event handling in each state set */
|
||
sp_ptr = db_ptr->sprog; /* State program that owns this db entry */
|
||
|
||
/* Wake up each state set that is waiting for event processing */
|
||
seq_efSet(sp_ptr, 0, db_ptr->index + 1);
|
||
|
||
return;
|
||
}
|
||
/* Sequencer connection handler:
|
||
Called each time a connection is established or broken */
|
||
VOID seq_conn_handler(args)
|
||
struct connection_handler_args args;
|
||
{
|
||
CHAN *db_ptr;
|
||
SPROG *sp_ptr;
|
||
|
||
|
||
/* user argument (from ca_search() */
|
||
db_ptr = (CHAN *)ca_puser(args.chid);
|
||
|
||
/* State program that owns this db entry */
|
||
sp_ptr = db_ptr->sprog;
|
||
|
||
/* Check for connected */
|
||
if (ca_field_type(args.chid) == TYPENOTCONN)
|
||
{
|
||
db_ptr->connected = FALSE;
|
||
sp_ptr->conn_count--;
|
||
seq_log(sp_ptr, "Channel \"%s\" disconnected\n", db_ptr->db_name);
|
||
}
|
||
else
|
||
{
|
||
db_ptr->connected = TRUE;
|
||
sp_ptr->conn_count++;
|
||
seq_log(sp_ptr, "Channel \"%s\" connected\n", db_ptr->db_name);
|
||
if (db_ptr->count > ca_element_count(args.chid))
|
||
{
|
||
db_ptr->count = ca_element_count(args.chid);
|
||
seq_log(sp_ptr, "\"%s\": reset count to %d\n",
|
||
db_ptr->db_name, db_ptr->count);
|
||
}
|
||
}
|
||
|
||
/* Wake up each state set that is waiting for event processing */
|
||
seq_efSet(sp_ptr, 0, 0);
|
||
|
||
return;
|
||
}
|
||
|
||
/* Wake up each state set that is waiting for event processing */
|
||
VOID seq_efSet(sp_ptr, dummy, ev_flag)
|
||
SPROG *sp_ptr;
|
||
int dummy;
|
||
int ev_flag; /* event flag */
|
||
{
|
||
SSCB *ss_ptr;
|
||
int nss;
|
||
|
||
ss_ptr = sp_ptr->sscb;
|
||
/* For all state sets: */
|
||
for (nss = 0; nss < sp_ptr->nss; nss++, ss_ptr++)
|
||
{
|
||
/* Apply resource lock */
|
||
semTake(sp_ptr->caSemId, WAIT_FOREVER);
|
||
|
||
/* Test for possible event trig based on bit mask for this state */
|
||
if ( (ev_flag == 0) || bitTest(ss_ptr->pMask, ev_flag) )
|
||
{
|
||
bitSet(ss_ptr->events, ev_flag);
|
||
semGive(ss_ptr->syncSemId); /* wake up the ss task */
|
||
}
|
||
|
||
/* Unlock resource */
|
||
semGive(sp_ptr->caSemId);
|
||
}
|
||
}
|
||
|
||
/* Test event flag against outstanding events */
|
||
seq_efTest(sp_ptr, ss_ptr, ev_flag)
|
||
SPROG *sp_ptr;
|
||
SSCB *ss_ptr;
|
||
int ev_flag; /* event flag */
|
||
{
|
||
return bitTest(ss_ptr->events, ev_flag);
|
||
}
|
||
/* Get DB value (uses channel access) */
|
||
seq_pvGet(sp_ptr, ss_ptr, db_ptr)
|
||
SPROG *sp_ptr; /* ptr to state program */
|
||
SSCB *ss_ptr; /* ptr to current state set */
|
||
CHAN *db_ptr; /* ptr to channel struct */
|
||
{
|
||
int status, sem_status;
|
||
extern seq_callback_handler();
|
||
|
||
/* Check for channel connected */
|
||
if (!db_ptr->connected)
|
||
return ECA_DISCONN;
|
||
|
||
/* Flag this pvGet() as not completed */
|
||
db_ptr->get_complete = 0;
|
||
|
||
/* If synchronous pvGet then clear the pvGet pend semaphore */
|
||
if (!sp_ptr->async_flag)
|
||
{
|
||
db_ptr->getSemId = ss_ptr->getSemId;
|
||
semTake(ss_ptr->getSemId, NO_WAIT);
|
||
}
|
||
|
||
/* Perform the CA get operation with a callback routine specified */
|
||
status = ca_array_get_callback(
|
||
db_ptr->get_type, /* db request type */
|
||
db_ptr->count, /* element count */
|
||
db_ptr->chid, /* chid */
|
||
seq_callback_handler, /* callback handler */
|
||
db_ptr); /* user arg */
|
||
|
||
if (sp_ptr->async_flag || (status != ECA_NORMAL) )
|
||
return status;
|
||
|
||
/* Synchronous pvGet() */
|
||
|
||
ca_flush_io();
|
||
|
||
/* Wait for completion (10s timeout) */
|
||
sem_status = semTake(ss_ptr->getSemId, 600);
|
||
if (sem_status == ERROR)
|
||
status = ECA_TIMEOUT;
|
||
|
||
return status;
|
||
}
|
||
|
||
/* Sequencer callback handler - called when a "get" completes */
|
||
seq_callback_handler(args)
|
||
struct event_handler_args args;
|
||
{
|
||
SPROG *sp_ptr;
|
||
CHAN *db_ptr;
|
||
struct dbr_sts_char *dbr_sts_ptr;
|
||
int i, nbytes;
|
||
void *value_ptr;
|
||
|
||
/* User arg is ptr to db channel structure */
|
||
db_ptr = (CHAN *)args.usr;
|
||
|
||
/* Copy value returned into user variable */
|
||
value_ptr = getPtrToValue(
|
||
(union db_access_val *)args.dbr, db_ptr->get_type);
|
||
nbytes = db_ptr->size * db_ptr->count;
|
||
bcopy(value_ptr, db_ptr->var, nbytes);
|
||
|
||
/* Copy status & severity */
|
||
dbr_sts_ptr = (struct dbr_sts_char *)args.dbr;
|
||
db_ptr->status = dbr_sts_ptr->status;
|
||
db_ptr->severity = dbr_sts_ptr->severity;
|
||
|
||
/* Set get complete flag */
|
||
db_ptr->get_complete = TRUE;
|
||
|
||
/* Wake up each state set that is waiting for event processing) */
|
||
sp_ptr = db_ptr->sprog; /* State program that owns this db entry */
|
||
seq_efSet(sp_ptr, 0, db_ptr->index + 1);
|
||
|
||
/* If syncronous pvGet then notify pending state set */
|
||
if (!sp_ptr->async_flag)
|
||
semGive(db_ptr->getSemId);
|
||
|
||
return 0;
|
||
}
|
||
|
||
/* Flush requests */
|
||
VOID seq_pvFlush()
|
||
{
|
||
ca_flush_io();
|
||
}
|
||
|
||
/* Put DB value (uses channel access) */
|
||
seq_pvPut(sp_ptr, ss_ptr, db_ptr)
|
||
SPROG *sp_ptr; /* ptr to state program */
|
||
SSCB *ss_ptr; /* ptr to current state set */
|
||
CHAN *db_ptr; /* ptr to channel struct */
|
||
{
|
||
int status;
|
||
|
||
if (!db_ptr->connected)
|
||
return ECA_DISCONN;
|
||
|
||
status = ca_array_put(db_ptr->put_type, db_ptr->count,
|
||
db_ptr->chid, db_ptr->var);
|
||
if (status != ECA_NORMAL)
|
||
{
|
||
seq_log(sp_ptr, "pvPut on \"%s\" failed (%d)\n",
|
||
db_ptr->db_name, status);
|
||
seq_log(sp_ptr, " put_type=%d\n", db_ptr->put_type);
|
||
seq_log(sp_ptr, " size=%d, count=%d\n", db_ptr->size, db_ptr->count);
|
||
}
|
||
|
||
return status;
|
||
}
|
||
/* Initiate a monitor on a channel */
|
||
seq_pvMonitor(sp_ptr, ss_ptr, db_ptr)
|
||
SPROG *sp_ptr; /* ptr to state program */
|
||
SSCB *ss_ptr; /* ptr to current state set */
|
||
CHAN *db_ptr; /* ptr to channel struct */
|
||
{
|
||
int status;
|
||
extern seq_event_handler();
|
||
|
||
#ifdef DEBUG
|
||
printf("monitor \"%s\"\n", db_ptr->db_name);
|
||
#endif DEBUG
|
||
|
||
if (db_ptr->monitored)
|
||
return -1;
|
||
|
||
status = ca_add_array_event(
|
||
db_ptr->get_type, /* requested type */
|
||
db_ptr->count, /* element count */
|
||
db_ptr->chid, /* chid */
|
||
seq_event_handler, /* function to call */
|
||
db_ptr, /* user arg (db struct) */
|
||
db_ptr->delta, /* pos. delta value */
|
||
db_ptr->delta, /* neg. delta value */
|
||
db_ptr->timeout, /* timeout */
|
||
&db_ptr->evid); /* where to put event id */
|
||
|
||
if (status != ECA_NORMAL)
|
||
{
|
||
SEVCHK(status, "ca_add_array_event");
|
||
ca_task_exit(); /* this is serious */
|
||
return -1;
|
||
}
|
||
ca_flush_io();
|
||
|
||
db_ptr->monitored = TRUE;
|
||
return 0;
|
||
}
|
||
|
||
/* Cancel a monitor */
|
||
seq_pvStopMonitor(sp_ptr, ss_ptr, db_ptr)
|
||
SPROG *sp_ptr; /* ptr to state program */
|
||
SSCB *ss_ptr; /* ptr to current state set */
|
||
CHAN *db_ptr; /* ptr to channel struct */
|
||
{
|
||
int status;
|
||
|
||
if (!db_ptr->monitored)
|
||
return -1;
|
||
|
||
status = ca_clear_event(db_ptr->evid);
|
||
if (status != ECA_NORMAL)
|
||
{
|
||
SEVCHK(status, "ca_clear_event");
|
||
return status;
|
||
}
|
||
|
||
db_ptr->monitored = FALSE;
|
||
|
||
return status;
|
||
}
|
||
|
||
/* Given ptr to value structure & type, return ptr to value */
|
||
LOCAL void *getPtrToValue(pBuf, dbrType)
|
||
union db_access_val *pBuf;
|
||
chtype dbrType;
|
||
{
|
||
switch (dbrType) {
|
||
case DBR_STRING: return (void *)pBuf->strval;
|
||
case DBR_STS_STRING: return (void *)pBuf->sstrval.value;
|
||
|
||
case DBR_SHORT: return (void *)&pBuf->shrtval;
|
||
case DBR_STS_SHORT: return (void *)&pBuf->sshrtval.value;
|
||
|
||
case DBR_FLOAT: return (void *)&pBuf->fltval;
|
||
case DBR_STS_FLOAT: return (void *)&pBuf->sfltval.value;
|
||
|
||
case DBR_ENUM: return (void *)&pBuf->enmval;
|
||
case DBR_STS_ENUM: return (void *)&pBuf->senmval.value;
|
||
|
||
case DBR_CHAR: return (void *)&pBuf->charval;
|
||
case DBR_STS_CHAR: return (void *)&pBuf->schrval.value;
|
||
|
||
case DBR_LONG: return (void *)&pBuf->longval;
|
||
case DBR_STS_LONG: return (void *)&pBuf->slngval.value;
|
||
|
||
case DBR_DOUBLE: return (void *)&pBuf->doubleval;
|
||
case DBR_STS_DOUBLE: return (void *)&pBuf->sdblval.value;
|
||
default: return NULL;
|
||
}
|
||
}
|
||
|