These are mainly for redundant IOCs, but may be useful elsewhere. iocBuild prepares the IOC and freezes it just before it goes live. A subsequent iocRun kicks it into life, or restarts it when paused. iocPause freezes all scan tasks and disconnects the server.
753 lines
21 KiB
C
753 lines
21 KiB
C
/*************************************************************************\
|
||
* Copyright (c) 2008 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found
|
||
* in file LICENSE that is included with this distribution.
|
||
\*************************************************************************/
|
||
/* dbScan.c */
|
||
/* tasks and subroutines to scan the database */
|
||
/*
|
||
* Original Author: Bob Dalesio
|
||
* Current Author: Marty Kraimer
|
||
* Date: 07/18/91
|
||
*/
|
||
|
||
#include <epicsStdlib.h>
|
||
#include <stddef.h>
|
||
#include <stdio.h>
|
||
#include <string.h>
|
||
#include <limits.h>
|
||
#include <math.h>
|
||
|
||
#include "epicsStdioRedirect.h"
|
||
#include "dbDefs.h"
|
||
#include "ellLib.h"
|
||
#include "taskwd.h"
|
||
#include "epicsMutex.h"
|
||
#include "epicsEvent.h"
|
||
#include "epicsExit.h"
|
||
#include "epicsInterrupt.h"
|
||
#include "epicsThread.h"
|
||
#include "epicsTime.h"
|
||
#include "cantProceed.h"
|
||
#include "epicsRingPointer.h"
|
||
#include "epicsPrint.h"
|
||
#include "dbBase.h"
|
||
#include "dbStaticLib.h"
|
||
#include "dbFldTypes.h"
|
||
#include "link.h"
|
||
#include "devSup.h"
|
||
#include "dbCommon.h"
|
||
#define epicsExportSharedSymbols
|
||
#include "dbAddr.h"
|
||
#include "callback.h"
|
||
#include "dbAccessDefs.h"
|
||
#include "dbLock.h"
|
||
#include "recGbl.h"
|
||
#include "dbScan.h"
|
||
|
||
|
||
/* Task Control */
|
||
enum ctl {ctlRun, ctlPause, ctlExit};
|
||
|
||
/* Task Startup/Shutdown Synchronization */
|
||
static epicsEventId startStopEvent;
|
||
|
||
volatile enum ctl scanCtl;
|
||
|
||
/* SCAN ONCE */
|
||
|
||
static int onceQueueSize = 1000;
|
||
static epicsEventId onceSem;
|
||
static epicsRingPointerId onceQ;
|
||
static epicsThreadId onceTaskId;
|
||
static void *exitOnce;
|
||
|
||
|
||
/* All other scan types */
|
||
typedef struct scan_list{
|
||
epicsMutexId lock;
|
||
ELLLIST list;
|
||
short modified;/*has list been modified?*/
|
||
} scan_list;
|
||
/*scan_elements are allocated and the address stored in dbCommon.spvt*/
|
||
typedef struct scan_element{
|
||
ELLNODE node;
|
||
scan_list *pscan_list;
|
||
struct dbCommon *precord;
|
||
} scan_element;
|
||
|
||
|
||
/* PERIODIC */
|
||
|
||
typedef struct periodic_scan_list {
|
||
scan_list scan_list;
|
||
double period;
|
||
volatile enum ctl scanCtl;
|
||
epicsEventId loopEvent;
|
||
} periodic_scan_list;
|
||
|
||
static int nPeriodic = 0;
|
||
static periodic_scan_list **papPeriodic; /* pointer to array of pointers */
|
||
static epicsThreadId *periodicTaskId; /* array of thread ids */
|
||
|
||
|
||
static char *priorityName[NUM_CALLBACK_PRIORITIES] = {
|
||
"Low", "Medium", "High"
|
||
};
|
||
|
||
|
||
/* EVENT */
|
||
|
||
#define MAX_EVENTS 256
|
||
typedef struct event_scan_list {
|
||
CALLBACK callback;
|
||
scan_list scan_list;
|
||
} event_scan_list;
|
||
static event_scan_list *pevent_list[NUM_CALLBACK_PRIORITIES][MAX_EVENTS];
|
||
|
||
|
||
/* IO_EVENT*/
|
||
|
||
typedef struct io_scan_list {
|
||
CALLBACK callback;
|
||
scan_list scan_list;
|
||
struct io_scan_list *next;
|
||
} io_scan_list;
|
||
|
||
static io_scan_list *iosl_head[NUM_CALLBACK_PRIORITIES] = {
|
||
NULL, NULL, NULL
|
||
};
|
||
|
||
|
||
/* Private routines */
|
||
static void onceTask(void *);
|
||
static void initOnce(void);
|
||
static void periodicTask(void *arg);
|
||
static void initPeriodic(void);
|
||
static void spawnPeriodic(int ind);
|
||
static void initEvent(void);
|
||
static void eventCallback(CALLBACK *pcallback);
|
||
static void ioeventCallback(CALLBACK *pcallback);
|
||
static void printList(scan_list *psl, char *message);
|
||
static void scanList(scan_list *psl);
|
||
static void buildScanLists(void);
|
||
static void addToList(struct dbCommon *precord, scan_list *psl);
|
||
static void deleteFromList(struct dbCommon *precord, scan_list *psl);
|
||
|
||
static void scanShutdown(void *arg)
|
||
{
|
||
int i;
|
||
|
||
scanOnce((dbCommon *)&exitOnce);
|
||
epicsEventWait(startStopEvent);
|
||
|
||
for (i = 0; i < nPeriodic; i++) {
|
||
papPeriodic[i]->scanCtl = ctlExit;
|
||
epicsEventSignal(papPeriodic[i]->loopEvent);
|
||
epicsEventWait(startStopEvent);
|
||
}
|
||
}
|
||
|
||
long scanInit()
|
||
{
|
||
int i;
|
||
|
||
startStopEvent = epicsEventMustCreate(epicsEventEmpty);
|
||
scanCtl = ctlPause;
|
||
|
||
initOnce();
|
||
initPeriodic();
|
||
initEvent();
|
||
buildScanLists();
|
||
for (i = 0; i < nPeriodic; i++)
|
||
spawnPeriodic(i);
|
||
|
||
epicsAtExit(scanShutdown, NULL);
|
||
return 0;
|
||
}
|
||
|
||
void scanRun(void)
|
||
{
|
||
int i;
|
||
|
||
scanCtl = ctlRun;
|
||
for (i = 0; i < nPeriodic; i++)
|
||
papPeriodic[i]->scanCtl = ctlRun;
|
||
}
|
||
|
||
void scanPause(void)
|
||
{
|
||
int i;
|
||
|
||
scanCtl = ctlPause;
|
||
for (i = 0; i < nPeriodic; i++)
|
||
papPeriodic[i]->scanCtl = ctlPause;
|
||
}
|
||
|
||
void scanAdd(struct dbCommon *precord)
|
||
{
|
||
int scan;
|
||
|
||
/* get the list on which this record belongs */
|
||
scan = precord->scan;
|
||
if (scan == SCAN_PASSIVE) return;
|
||
if (scan < 0 || scan >= nPeriodic + SCAN_1ST_PERIODIC) {
|
||
recGblRecordError(-1, (void *)precord,
|
||
"scanAdd detected illegal SCAN value");
|
||
} else if (scan == SCAN_EVENT) {
|
||
int evnt;
|
||
int prio;
|
||
event_scan_list *pesl;
|
||
|
||
evnt = precord->evnt;
|
||
if (evnt < 0 || evnt >= MAX_EVENTS) {
|
||
recGblRecordError(S_db_badField, (void *)precord,
|
||
"scanAdd detected illegal EVNT value");
|
||
precord->scan = SCAN_PASSIVE;
|
||
return;
|
||
}
|
||
prio = precord->prio;
|
||
if (prio < 0 || prio >= NUM_CALLBACK_PRIORITIES) {
|
||
recGblRecordError(-1, (void *)precord,
|
||
"scanAdd: illegal prio field");
|
||
precord->scan = SCAN_PASSIVE;
|
||
return;
|
||
}
|
||
pesl = pevent_list[prio][evnt];
|
||
if (pesl == NULL) {
|
||
pesl = dbCalloc(1, sizeof(event_scan_list));
|
||
pevent_list[prio][evnt] = pesl;
|
||
pesl->scan_list.lock = epicsMutexMustCreate();
|
||
callbackSetCallback(eventCallback, &pesl->callback);
|
||
callbackSetPriority(prio, &pesl->callback);
|
||
callbackSetUser(pesl, &pesl->callback);
|
||
ellInit(&pesl->scan_list.list);
|
||
}
|
||
addToList(precord, &pesl->scan_list);
|
||
} else if (scan == SCAN_IO_EVENT) {
|
||
io_scan_list *piosl = NULL;
|
||
int prio;
|
||
DEVSUPFUN get_ioint_info;
|
||
|
||
if (precord->dset == NULL){
|
||
recGblRecordError(-1, (void *)precord,
|
||
"scanAdd: I/O Intr not valid (no DSET) ");
|
||
precord->scan = SCAN_PASSIVE;
|
||
return;
|
||
}
|
||
get_ioint_info = precord->dset->get_ioint_info;
|
||
if (get_ioint_info == NULL) {
|
||
recGblRecordError(-1, (void *)precord,
|
||
"scanAdd: I/O Intr not valid (no get_ioint_info)");
|
||
precord->scan = SCAN_PASSIVE;
|
||
return;
|
||
}
|
||
if (get_ioint_info(0, precord, &piosl)) {
|
||
precord->scan = SCAN_PASSIVE;
|
||
return;
|
||
}
|
||
if (piosl == NULL) {
|
||
recGblRecordError(-1, (void *)precord,
|
||
"scanAdd: I/O Intr not valid");
|
||
precord->scan = SCAN_PASSIVE;
|
||
return;
|
||
}
|
||
prio = precord->prio;
|
||
if (prio < 0 || prio >= NUM_CALLBACK_PRIORITIES) {
|
||
recGblRecordError(-1, (void *)precord,
|
||
"scanAdd: illegal prio field");
|
||
precord->scan = SCAN_PASSIVE;
|
||
return;
|
||
}
|
||
piosl += prio; /* get piosl for correct priority*/
|
||
addToList(precord, &piosl->scan_list);
|
||
} else if (scan >= SCAN_1ST_PERIODIC) {
|
||
addToList(precord, &papPeriodic[scan - SCAN_1ST_PERIODIC]->scan_list);
|
||
}
|
||
return;
|
||
}
|
||
|
||
void scanDelete(struct dbCommon *precord)
|
||
{
|
||
short scan;
|
||
|
||
/* get the list on which this record belongs */
|
||
scan = precord->scan;
|
||
if (scan == SCAN_PASSIVE) return;
|
||
if (scan < 0 || scan >= nPeriodic + SCAN_1ST_PERIODIC) {
|
||
recGblRecordError(-1, (void *)precord,
|
||
"scanDelete detected illegal SCAN value");
|
||
} else if (scan == SCAN_EVENT) {
|
||
int evnt;
|
||
int prio;
|
||
event_scan_list *pesl;
|
||
scan_list *psl = 0;
|
||
|
||
evnt = precord->evnt;
|
||
if (evnt < 0 || evnt >= MAX_EVENTS) {
|
||
recGblRecordError(S_db_badField, (void *)precord,
|
||
"scanAdd detected illegal EVNT value");
|
||
precord->scan = SCAN_PASSIVE;
|
||
return;
|
||
}
|
||
prio = precord->prio;
|
||
if (prio < 0 || prio >= NUM_CALLBACK_PRIORITIES) {
|
||
recGblRecordError(-1, (void *)precord,
|
||
"scanAdd: illegal prio field");
|
||
precord->scan = SCAN_PASSIVE;
|
||
return;
|
||
}
|
||
pesl = pevent_list[prio][evnt];
|
||
if (pesl) psl = &pesl->scan_list;
|
||
if (!pesl || !psl)
|
||
recGblRecordError(-1, (void *)precord,
|
||
"scanDelete for bad evnt");
|
||
else
|
||
deleteFromList(precord, psl);
|
||
} else if (scan == SCAN_IO_EVENT) {
|
||
io_scan_list *piosl=NULL;
|
||
int prio;
|
||
DEVSUPFUN get_ioint_info;
|
||
|
||
if (precord->dset==NULL) {
|
||
recGblRecordError(-1, (void *)precord,
|
||
"scanDelete: I/O Intr not valid (no DSET)");
|
||
return;
|
||
}
|
||
get_ioint_info=precord->dset->get_ioint_info;
|
||
if (get_ioint_info == NULL) {
|
||
recGblRecordError(-1, (void *)precord,
|
||
"scanDelete: I/O Intr not valid (no get_ioint_info)");
|
||
return;
|
||
}
|
||
if (get_ioint_info(1, precord, &piosl)) return;
|
||
if (piosl == NULL) {
|
||
recGblRecordError(-1, (void *)precord,
|
||
"scanDelete: I/O Intr not valid");
|
||
return;
|
||
}
|
||
prio = precord->prio;
|
||
if (prio < 0 || prio >= NUM_CALLBACK_PRIORITIES) {
|
||
recGblRecordError(-1, (void *)precord,
|
||
"scanDelete: get_ioint_info returned illegal priority");
|
||
return;
|
||
}
|
||
piosl += prio; /*get piosl for correct priority*/
|
||
deleteFromList(precord, &piosl->scan_list);
|
||
} else if (scan >= SCAN_1ST_PERIODIC) {
|
||
deleteFromList(precord, &papPeriodic[scan - SCAN_1ST_PERIODIC]->scan_list);
|
||
}
|
||
return;
|
||
}
|
||
|
||
double scanPeriod(int scan) {
|
||
scan -= SCAN_1ST_PERIODIC;
|
||
if (scan < 0 || scan >= nPeriodic)
|
||
return 0.0;
|
||
return papPeriodic[scan]->period;
|
||
}
|
||
|
||
int scanppl(double period) /* print periodic list */
|
||
{
|
||
periodic_scan_list *ppsl;
|
||
char message[80];
|
||
int i;
|
||
|
||
for (i = 0; i < nPeriodic; i++) {
|
||
ppsl = papPeriodic[i];
|
||
if (ppsl == NULL) continue;
|
||
if (period > 0.0 && (fabs(period - ppsl->period) >.05)) continue;
|
||
sprintf(message, "Scan Period = %g seconds ", ppsl->period);
|
||
printList(&ppsl->scan_list, message);
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
int scanpel(int event_number) /* print event list */
|
||
{
|
||
char message[80];
|
||
int prio, evnt;
|
||
event_scan_list *pesl;
|
||
|
||
for (evnt = 0; evnt < MAX_EVENTS; evnt++) {
|
||
if (event_number && evnt<event_number) continue;
|
||
if (event_number && evnt>event_number) break;
|
||
for (prio = 0; prio < NUM_CALLBACK_PRIORITIES; prio++) {
|
||
pesl = pevent_list[prio][evnt];
|
||
if (!pesl) continue;
|
||
if (ellCount(&pesl->scan_list.list) == 0) continue;
|
||
sprintf(message, "Event %d Priority %s", evnt, priorityName[prio]);
|
||
printList(&pesl->scan_list, message);
|
||
}
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
int scanpiol() /* print io_event list */
|
||
{
|
||
io_scan_list *piosl;
|
||
int prio;
|
||
char message[80];
|
||
|
||
for(prio = 0; prio < NUM_CALLBACK_PRIORITIES; prio++) {
|
||
piosl = iosl_head[prio];
|
||
if (piosl == NULL) continue;
|
||
sprintf(message, "IO Event: Priority %s", priorityName[prio]);
|
||
while(piosl != NULL) {
|
||
printList(&piosl->scan_list, message);
|
||
piosl = piosl->next;
|
||
}
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
static void eventCallback(CALLBACK *pcallback)
|
||
{
|
||
event_scan_list *pesl;
|
||
|
||
callbackGetUser(pesl, pcallback);
|
||
scanList(&pesl->scan_list);
|
||
}
|
||
|
||
static void initEvent(void)
|
||
{
|
||
int evnt, prio;
|
||
|
||
for (prio = 0; prio < NUM_CALLBACK_PRIORITIES; prio++) {
|
||
for (evnt = 0; evnt < MAX_EVENTS; evnt++) {
|
||
pevent_list[prio][evnt] = NULL;
|
||
}
|
||
}
|
||
}
|
||
|
||
void post_event(int event)
|
||
{
|
||
int prio;
|
||
event_scan_list *pesl;
|
||
|
||
if (scanCtl != ctlRun) return;
|
||
if (event < 0 || event >= MAX_EVENTS) {
|
||
errMessage(-1, "illegal event passed to post_event");
|
||
return;
|
||
}
|
||
for (prio=0; prio<NUM_CALLBACK_PRIORITIES; prio++) {
|
||
pesl = pevent_list[prio][event];
|
||
if (!pesl) continue;
|
||
if (ellCount(&pesl->scan_list.list) >0)
|
||
callbackRequest((void *)pesl);
|
||
}
|
||
}
|
||
|
||
void scanIoInit(IOSCANPVT *ppioscanpvt)
|
||
{
|
||
int prio;
|
||
|
||
/* Allocate an array of io_scan_lists, one for each priority. */
|
||
/* IOSCANPVT will hold the address of this array of structures */
|
||
*ppioscanpvt = dbCalloc(NUM_CALLBACK_PRIORITIES, sizeof(io_scan_list));
|
||
for (prio = 0; prio < NUM_CALLBACK_PRIORITIES; prio++) {
|
||
io_scan_list *piosl = &(*ppioscanpvt)[prio];
|
||
callbackSetCallback(ioeventCallback, &piosl->callback);
|
||
callbackSetPriority(prio, &piosl->callback);
|
||
callbackSetUser(piosl, &piosl->callback);
|
||
ellInit(&piosl->scan_list.list);
|
||
piosl->scan_list.lock = epicsMutexMustCreate();
|
||
piosl->next = iosl_head[prio];
|
||
iosl_head[prio] = piosl;
|
||
}
|
||
}
|
||
|
||
|
||
void scanIoRequest(IOSCANPVT pioscanpvt)
|
||
{
|
||
int prio;
|
||
|
||
if (scanCtl != ctlRun) return;
|
||
for (prio = 0; prio < NUM_CALLBACK_PRIORITIES; prio++) {
|
||
io_scan_list *piosl = &pioscanpvt[prio];
|
||
if (ellCount(&piosl->scan_list.list) > 0)
|
||
callbackRequest(&piosl->callback);
|
||
}
|
||
}
|
||
|
||
void scanOnce(struct dbCommon *precord)
|
||
{
|
||
static int newOverflow = TRUE;
|
||
int lockKey;
|
||
int pushOK;
|
||
|
||
lockKey = epicsInterruptLock();
|
||
pushOK = epicsRingPointerPush(onceQ, precord);
|
||
epicsInterruptUnlock(lockKey);
|
||
|
||
if (!pushOK) {
|
||
if (newOverflow) errlogPrintf("scanOnce: Ring buffer overflow\n");
|
||
newOverflow = FALSE;
|
||
} else {
|
||
newOverflow = TRUE;
|
||
}
|
||
epicsEventSignal(onceSem);
|
||
}
|
||
|
||
static void onceTask(void *arg)
|
||
{
|
||
taskwdInsert(0, NULL, NULL);
|
||
epicsEventSignal(startStopEvent);
|
||
|
||
while (TRUE) {
|
||
void *precord;
|
||
|
||
epicsEventMustWait(onceSem);
|
||
while ((precord = epicsRingPointerPop(onceQ))) {
|
||
if (precord == &exitOnce) goto shutdown;
|
||
dbScanLock(precord);
|
||
dbProcess(precord);
|
||
dbScanUnlock(precord);
|
||
}
|
||
}
|
||
|
||
shutdown:
|
||
taskwdRemove(0);
|
||
epicsEventSignal(startStopEvent);
|
||
}
|
||
|
||
int scanOnceSetQueueSize(int size)
|
||
{
|
||
onceQueueSize = size;
|
||
return 0;
|
||
}
|
||
|
||
static void initOnce(void)
|
||
{
|
||
if ((onceQ = epicsRingPointerCreate(onceQueueSize)) == NULL) {
|
||
cantProceed("initOnce: Ring buffer create failed\n");
|
||
}
|
||
onceSem = epicsEventMustCreate(epicsEventEmpty);
|
||
onceTaskId = epicsThreadCreate("scanOnce", epicsThreadPriorityScanHigh,
|
||
epicsThreadGetStackSize(epicsThreadStackBig), onceTask, 0);
|
||
|
||
epicsEventWait(startStopEvent);
|
||
}
|
||
|
||
static void periodicTask(void *arg)
|
||
{
|
||
periodic_scan_list *ppsl = (periodic_scan_list *)arg;
|
||
|
||
epicsTimeStamp start_time, end_time;
|
||
double delay;
|
||
|
||
taskwdInsert(0, NULL, NULL);
|
||
epicsEventSignal(startStopEvent);
|
||
|
||
while (ppsl->scanCtl != ctlExit) {
|
||
epicsTimeGetCurrent(&start_time);
|
||
if (ppsl->scanCtl == ctlRun) scanList(&ppsl->scan_list);
|
||
epicsTimeGetCurrent(&end_time);
|
||
delay = ppsl->period - epicsTimeDiffInSeconds(&end_time, &start_time);
|
||
if (delay <= 0.0) delay = 0.1;
|
||
epicsEventWaitWithTimeout(ppsl->loopEvent, delay);
|
||
}
|
||
|
||
taskwdRemove(0);
|
||
epicsEventSignal(startStopEvent);
|
||
}
|
||
|
||
|
||
static void initPeriodic()
|
||
{
|
||
dbMenu *pmenu;
|
||
periodic_scan_list *ppsl;
|
||
int i;
|
||
|
||
pmenu = dbFindMenu(pdbbase, "menuScan");
|
||
if (!pmenu) {
|
||
epicsPrintf("initPeriodic: menuScan not present\n");
|
||
return;
|
||
}
|
||
nPeriodic = pmenu->nChoice - SCAN_1ST_PERIODIC;
|
||
papPeriodic = dbCalloc(nPeriodic, sizeof(periodic_scan_list*));
|
||
periodicTaskId = dbCalloc(nPeriodic, sizeof(void *));
|
||
for (i = 0; i < nPeriodic; i++) {
|
||
ppsl = dbCalloc(1, sizeof(periodic_scan_list));
|
||
|
||
ppsl->scan_list.lock = epicsMutexMustCreate();
|
||
ellInit(&ppsl->scan_list.list);
|
||
epicsScanDouble(pmenu->papChoiceValue[i + SCAN_1ST_PERIODIC],
|
||
&ppsl->period);
|
||
ppsl->scanCtl = ctlPause;
|
||
ppsl->loopEvent = epicsEventMustCreate(epicsEventEmpty);
|
||
|
||
papPeriodic[i] = ppsl;
|
||
}
|
||
}
|
||
|
||
static void spawnPeriodic(int ind)
|
||
{
|
||
periodic_scan_list *ppsl;
|
||
char taskName[20];
|
||
|
||
ppsl = papPeriodic[ind];
|
||
sprintf(taskName, "scan%g", ppsl->period);
|
||
periodicTaskId[ind] = epicsThreadCreate(
|
||
taskName, epicsThreadPriorityScanLow + ind,
|
||
epicsThreadGetStackSize(epicsThreadStackBig),
|
||
periodicTask, (void *)ppsl);
|
||
|
||
epicsEventWait(startStopEvent);
|
||
}
|
||
|
||
static void ioeventCallback(CALLBACK *pcallback)
|
||
{
|
||
io_scan_list *piosl;
|
||
|
||
callbackGetUser(piosl, pcallback);
|
||
scanList(&piosl->scan_list);
|
||
}
|
||
|
||
static void printList(scan_list *psl, char *message)
|
||
{
|
||
scan_element *pse;
|
||
|
||
epicsMutexMustLock(psl->lock);
|
||
pse = (scan_element *)ellFirst(&psl->list);
|
||
epicsMutexUnlock(psl->lock);
|
||
if (pse == NULL) return;
|
||
printf("%s\n", message);
|
||
while (pse != NULL) {
|
||
printf(" %-28s\n", pse->precord->name);
|
||
epicsMutexMustLock(psl->lock);
|
||
if (pse->pscan_list != psl) {
|
||
epicsMutexUnlock(psl->lock);
|
||
printf("Scan list changed while processing.");
|
||
return;
|
||
}
|
||
pse = (scan_element *)ellNext(&pse->node);
|
||
epicsMutexUnlock(psl->lock);
|
||
}
|
||
}
|
||
|
||
static void scanList(scan_list *psl)
|
||
{
|
||
/* When reading this code remember that the call to dbProcess can result
|
||
* in the SCAN field being changed in an arbitrary number of records.
|
||
*/
|
||
|
||
scan_element *pse;
|
||
scan_element *prev = NULL;
|
||
scan_element *next = NULL;
|
||
|
||
epicsMutexMustLock(psl->lock);
|
||
psl->modified = FALSE;
|
||
pse = (scan_element *)ellFirst(&psl->list);
|
||
if (pse) next = (scan_element *)ellNext(&pse->node);
|
||
epicsMutexUnlock(psl->lock);
|
||
|
||
while (pse) {
|
||
struct dbCommon *precord = pse->precord;
|
||
|
||
dbScanLock(precord);
|
||
dbProcess(precord);
|
||
dbScanUnlock(precord);
|
||
|
||
epicsMutexMustLock(psl->lock);
|
||
if (!psl->modified) {
|
||
prev = pse;
|
||
pse = (scan_element *)ellNext(&pse->node);
|
||
if (pse) next = (scan_element *)ellNext(&pse->node);
|
||
} else if (pse->pscan_list == psl) {
|
||
/*This scan element is still in same scan list*/
|
||
prev = pse;
|
||
pse = (scan_element *)ellNext(&pse->node);
|
||
if (pse) next = (scan_element *)ellNext(&pse->node);
|
||
psl->modified = FALSE;
|
||
} else if (prev && prev->pscan_list == psl) {
|
||
/*Previous scan element is still in same scan list*/
|
||
pse = (scan_element *)ellNext(&prev->node);
|
||
if (pse) {
|
||
prev = (scan_element *)ellPrevious(&pse->node);
|
||
next = (scan_element *)ellNext(&pse->node);
|
||
}
|
||
psl->modified = FALSE;
|
||
} else if (next && next->pscan_list == psl) {
|
||
/*Next scan element is still in same scan list*/
|
||
pse = next;
|
||
prev = (scan_element *)ellPrevious(&pse->node);
|
||
next = (scan_element *)ellNext(&pse->node);
|
||
psl->modified = FALSE;
|
||
} else {
|
||
/*Too many changes. Just wait till next period*/
|
||
epicsMutexUnlock(psl->lock);
|
||
return;
|
||
}
|
||
epicsMutexUnlock(psl->lock);
|
||
}
|
||
}
|
||
|
||
static void buildScanLists(void)
|
||
{
|
||
dbRecordType *pdbRecordType;
|
||
|
||
/*Look for first record*/
|
||
for (pdbRecordType = (dbRecordType *)ellFirst(&pdbbase->recordTypeList);
|
||
pdbRecordType;
|
||
pdbRecordType = (dbRecordType *)ellNext(&pdbRecordType->node)) {
|
||
dbRecordNode *pdbRecordNode;
|
||
for (pdbRecordNode = (dbRecordNode *)ellFirst(&pdbRecordType->recList);
|
||
pdbRecordNode;
|
||
pdbRecordNode = (dbRecordNode *)ellNext(&pdbRecordNode->node)) {
|
||
dbCommon *precord = pdbRecordNode->precord;
|
||
if (precord->name[0] == 0) continue;
|
||
scanAdd(precord);
|
||
}
|
||
}
|
||
}
|
||
|
||
static void addToList(struct dbCommon *precord, scan_list *psl)
|
||
{
|
||
scan_element *pse, *ptemp;
|
||
|
||
epicsMutexMustLock(psl->lock);
|
||
pse = precord->spvt;
|
||
if (pse==NULL) {
|
||
pse = dbCalloc(1, sizeof(scan_element));
|
||
precord->spvt = pse;
|
||
pse->precord = precord;
|
||
}
|
||
pse ->pscan_list = psl;
|
||
ptemp = (scan_element *)ellFirst(&psl->list);
|
||
while (ptemp) {
|
||
if (ptemp->precord->phas > precord->phas) {
|
||
ellInsert(&psl->list, ellPrevious(&ptemp->node), &pse->node);
|
||
break;
|
||
}
|
||
ptemp = (scan_element *)ellNext(&ptemp->node);
|
||
}
|
||
if (ptemp == NULL) ellAdd(&psl->list, (void *)pse);
|
||
psl->modified = TRUE;
|
||
epicsMutexUnlock(psl->lock);
|
||
}
|
||
|
||
static void deleteFromList(struct dbCommon *precord, scan_list *psl)
|
||
{
|
||
scan_element *pse;
|
||
|
||
epicsMutexMustLock(psl->lock);
|
||
if (precord->spvt==NULL) {
|
||
epicsMutexUnlock(psl->lock);
|
||
return;
|
||
}
|
||
pse = precord->spvt;
|
||
if (pse == NULL || pse->pscan_list != psl) {
|
||
epicsMutexUnlock(psl->lock);
|
||
errlogPrintf("deleteFromList failed");
|
||
return;
|
||
}
|
||
pse->pscan_list = NULL;
|
||
ellDelete(&psl->list, (void *)pse);
|
||
psl->modified = TRUE;
|
||
epicsMutexUnlock(psl->lock);
|
||
}
|