Files
epics-base/src/sequencer/seq_ca.c
Marty Kraimer fcc317ccdf The sequencer did not properly keep track of number of channels connected.
It now checks if it is actually connected/disconnect before it increments/decrements the counts.
2004-04-02 17:09:23 +00:00

294 lines
7.7 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.
/*************************************************************************\
* Copyright (c) 2002 The University of Chicago, as Operator of Argonne
* National Laboratory.
* Copyright (c) 2002 The Regents of the University of California, as
* Operator of Los Alamos National Laboratory.
* EPICS BASE Versions 3.13.7
* and higher are distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
\*************************************************************************/
/*
seq_ca.c,v 1.2 1995/06/27 15:25:54 wright Exp
* DESCRIPTION: Channel access interface for sequencer.
*
* Author: Andy Kozubal
* Date: July, 1991
*
*/
#define ANSI
#include "seq.h"
#include "string.h"
#include "taskVarLib.h"
LOCAL VOID proc_db_events(union db_access_val *, CHAN *, long);
/*#define DEBUG*/
#ifdef DEBUG
#undef LOCAL
#define LOCAL
#endif /*DEBUG*/
/*
* seq_connect() - Connect to all database channels through channel access.
*/
long seq_connect(SPROG *pSP)
{
CHAN *pDB;
int status, i;
extern VOID seq_conn_handler();
/*
* For each channel: connect to db & isssue monitor (if monFlag is TRUE).
*/
for (i = 0, pDB = pSP->pChan; i < pSP->numChans; i++, pDB++)
{
if (pDB->dbName == NULL || pDB->dbName[0] == 0)
continue; /* skip records without pv names */
pDB->assigned = TRUE;
pSP->assignCount += 1; /* keep track of number of *assigned* channels */
#ifdef DEBUG
logMsg("seq_connect: connect %s to %s\n",
pDB->pVarName, pDB->dbName);
#endif /*DEBUG*/
/* Connect to it */
status = ca_build_and_connect(
pDB->dbName, /* DB channel name */
TYPENOTCONN, /* Don't get initial value */
0, /* get count (n/a) */
&(pDB->chid), /* ptr to chid */
0, /* ptr to value (n/a) */
seq_conn_handler, /* connection handler routine */
pDB); /* user ptr is CHAN structure */
if (status != ECA_NORMAL)
{
SEVCHK(status, "ca_search");
ca_task_exit();
return -1;
}
/* Clear monitor indicator */
pDB->monitored = FALSE;
/*
* Issue monitor request
*/
if (pDB->monFlag)
{
seq_pvMonitor((SS_ID)pSP->pSS, i);
}
}
ca_flush_io();
return 0;
}
#define GET_COMPLETE 0
#define MON_COMPLETE 1
/*
* seq_event_handler() - Channel access events (monitors) come here.
* args points to CA event handler argument structure. args.usr contains
* a pointer to the channel structure (CHAN *).
*/
VOID seq_event_handler(struct event_handler_args args)
{
/* Process event handling in each state set */
proc_db_events((union db_access_val *)args.dbr, (CHAN *)args.usr, MON_COMPLETE);
return;
}
/*
* seq_callback_handler() - Sequencer callback handler.
* Called when a "get" completes.
* args.usr points to the db structure (CHAN *) for tis channel.
*/
VOID seq_callback_handler(struct event_handler_args args)
{
/* Process event handling in each state set */
proc_db_events((union db_access_val *)args.dbr, (CHAN *)args.usr, GET_COMPLETE);
return;
}
/* Common code for event and callback handling */
LOCAL VOID proc_db_events(union db_access_val *pAccess, CHAN *pDB, long complete_type)
{
SPROG *pSP;
void *pVal;
#ifdef DEBUG
logMsg("proc_db_events: var=%s, pv=%s\n", pDB->VarName, pDB->dbName);
#endif /*DEBUG*/
/* Copy value returned into user variable */
pVal = (void *)((long)pAccess + pDB->dbOffset); /* ptr to data in CA structure */
bcopy(pVal, pDB->pVar, pDB->size * pDB->dbCount);
/* Copy status & severity */
pDB->status = pAccess->tchrval.status;
pDB->severity = pAccess->tchrval.severity;
/* Copy time stamp */
pDB->timeStamp = pAccess->tchrval.stamp;
/* Get ptr to the state program that owns this db entry */
pSP = pDB->sprog;
/* Indicate completed pvGet() */
if (complete_type == GET_COMPLETE)
{
pDB->getComplete = TRUE;
}
/* Wake up each state set that uses this channel in an event */
seqWakeup(pSP, pDB->eventNum);
/* If there's an event flag associated with this channel, set it */
if (pDB-> efId > 0)
seq_efSet((SS_ID)pSP->pSS, pDB->efId);
/* Special processing for completed synchronous (-a) pvGet() */
if ( (complete_type == GET_COMPLETE) && ((pSP->options & OPT_ASYNC) == 0) )
semGive(pDB->getSemId);
return;
}
/* Disconnect all database channels */
/*#define DEBUG_DISCONNECT*/
long seq_disconnect(SPROG *pSP)
{
CHAN *pDB;
STATUS status;
int i;
extern int seqAuxTaskId;
SPROG *pMySP; /* will be NULL if this task is not a sequencer task */
/* Did we already disconnect? */
if (pSP->connCount < 0)
return 0;
/* Import Channel Access context from the auxillary seq. task */
pMySP = seqFindProg(taskIdSelf() );
if (pMySP == NULL)
{
status = ca_import(seqAuxTaskId); /* not a sequencer task */
SEVCHK (status, "seq_disconnect: ca_import");
}
pDB = pSP->pChan;
for (i = 0; i < pSP->numChans; i++, pDB++)
{
if (!pDB->assigned)
continue;
#ifdef DEBUG_DISCONNECT
logMsg("seq_disconnect: disconnect %s from %s\n",
pDB->pVarName, pDB->dbName);
taskDelay(30);
#endif /*DEBUG_DISCONNECT*/
/* Disconnect this channel */
status = ca_clear_channel(pDB->chid);
SEVCHK (status, "seq_disconnect: ca_clear_channel");
/* Clear monitor & connect indicators */
pDB->monitored = FALSE;
pDB->connected = FALSE;
}
pSP->connCount = -1; /* flag to indicate all disconnected */
ca_flush_io();
/* Cancel CA context if it was imported above */
if (pMySP == NULL)
{
#ifdef DEBUG_DISCONNECT
logMsg("seq_disconnect: ca_import_cancel\n");
#endif /*DEBUG_DISCONNECT*/
SEVCHK(ca_import_cancel(taskIdSelf()),
"seq_disconnect: ca_import_cancel() failed!");
}
return 0;
}
/*
* seq_conn_handler() - Sequencer connection handler.
* Called each time a connection is established or broken.
*/
VOID seq_conn_handler(struct connection_handler_args args)
{
CHAN *pDB;
SPROG *pSP;
/* User argument is db ptr (specified at ca_search() ) */
pDB = (CHAN *)ca_puser(args.chid);
/* State program that owns this db entry */
pSP = pDB->sprog;
/* Check for connected */
if (ca_field_type(args.chid) == TYPENOTCONN)
{
#ifdef DEBUG
logMsg("%s disconnected from %s\n", pDB->VarName, pDB->dbName);
#endif /*DEBUG*/
if(pDB->connected) {
pDB->connected = FALSE;
pSP->connCount--;
pDB->monitored = FALSE;
} else {
printf("%s disconnected but already disconnected %s\n",
pDB->pVarName,pDB->dbName);
}
}
else /* PV connected */
{
#ifdef DEBUG
logMsg("%s connected to %s\n", pDB->VarName, pDB->dbName);
#endif /*DEBUG*/
if(!pDB->connected) {
pDB->connected = TRUE;
pSP->connCount++;
if (pDB->monFlag)
pDB->monitored = TRUE;
pDB->dbCount = ca_element_count(args.chid);
if (pDB->dbCount > pDB->count)
pDB->dbCount = pDB->count;
} else {
printf("%s connected but already connected %s\n",
pDB->pVarName,pDB->dbName);
}
}
/* Wake up each state set that is waiting for event processing */
seqWakeup(pSP, 0);
return;
}
/*
* seqWakeup() -- wake up each state set that is waiting on this event
* based on the current event mask. EventNum = 0 means wake all state sets.
*/
VOID seqWakeup(SPROG *pSP, long eventNum)
{
int nss;
SSCB *pSS;
/* Check event number against mask for all state sets: */
for (nss = 0, pSS = pSP->pSS; nss < pSP->numSS; nss++, pSS++)
{
/* If event bit in mask is set, wake that state set */
if ( (eventNum == 0) || bitTest(pSS->pMask, eventNum) )
{
semGive(pSS->syncSemId); /* wake up the ss task */
}
}
return;
}