Files
epics-base/src/sequencer/seq_ca.c

421 lines
10 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$
* 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;
}
}