Files
epics-base/src/sequencer/seq_main.c
1996-05-09 19:30:37 +00:00

640 lines
18 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.
/**************************************************************************
GTA PROJECT AT division
Copyright, 1990-1994, The Regents of the University of California
and the University of Chicago.
Los Alamos National Laboratory
seq_main.c,v 1.2 1995/06/27 15:25:58 wright Exp
DESCRIPTION: Seq() initiates a sequence as a group of cooperating
tasks. An optional string parameter specifies the values for
macros. The channel access context and task are shared by all state
programs.
ENVIRONMENT: VxWorks
HISTORY:
23apr91,ajk Fixed problem with state program invoking the sequencer.
01jul91,ajk Added ANSI functional prototypes.
05jul91,ajk Changed semCreate() in three places to semBCreate.
Modified semTake() second param. to WAIT_FOREVER.
These provide VX5.0 compatability.
16aug91,ajk Improved "magic number" error message.
25oct91,ajk Code to create semaphores "pSS->getSemId" was left out.
Added this code to init_sscb().
25nov91,ajk Removed obsolete seqLog() code dealing with global locking.
04dec91,ajk Implemented state program linked list, eliminating need for
task variables.
11dec91,ajk Cleaned up comments.
05feb92,ajk Decreased minimum allowable stack size to SPAWN_STACK_SIZE/2.
24feb92,ajk Print error code for log file failure.
28apr92,ajk Implemented new event flag mode.
29apr92,ajk Now alocates private program structures, even when reentry option
is not specified. This avoids problems with seqAddTask().
29apr92,ajk Implemented mutual exclusion lock in seq_log().
16feb93,ajk Converted to single channel access task for all state programs.
16feb93,ajk Removed VxWorks pre-v5 stuff.
17feb93,ajk Evaluation of channel names moved here from seq_ca.c.
19feb93,ajk Fixed time stamp format for seq_log().
16jun93,ajk Fixed taskSpawn() to have 15 args per vx5.1.
20jul93,ajk Replaced obsolete delete() with remove() per vx5.1 release notes.
20jul93,ajk Removed #define ANSI
15mar94,ajk Implemented i/f to snc through structures in seqCom.h.
15mar94,ajk Allowed assignment of array elements to db.
15mar94,ajk Rearranged code that builds program structures.
02may94,ajk Performed initialization when sequencer is evoked, even w/o
parameters.
19jul95,ajk Added unsigned types (unsigned char, short, int, long).
20jul95,ajk Added priority specification at run time.
03aug95,ajk Fix problem with +r option: user variable space (pSP->pVar)
was not being allocated.
***************************************************************************/
/*#define DEBUG 1*/
#include "seqCom.h"
#include "seq.h"
#ifdef DEBUG
#undef LOCAL
#define LOCAL
#endif DEBUG
/* ANSI functional prototypes for local routines */
LOCAL SPROG *seqInitTables(struct seqProgram *);
LOCAL SPROG *alloc_task_area(struct seqProgram *);
LOCAL VOID init_sprog(struct seqProgram *, SPROG *);
LOCAL VOID init_sscb(struct seqProgram *, SPROG *);
LOCAL VOID init_chan(struct seqProgram *, SPROG *);
LOCAL VOID init_mac(SPROG *);
LOCAL VOID seq_logInit(SPROG *);
LOCAL VOID seqChanNameEval(SPROG *);
LOCAL int countStates(struct seqProgram *);
LOCAL int roundUp(int);
LOCAL VOID selectDBtype(char *, short *, short *, short *, short *);
#define SCRATCH_SIZE (MAX_MACROS*(MAX_STRING_SIZE+1)*12)
/* Globals */
/* Flag to indicate that "taskDeleteHookAdd()" was called */
int seqDeleteHookAdded = FALSE;
/* Auxillary sequencer task id; used to share CA context. */
int seqAuxTaskId = 0;
/*
* seq: User-callable routine to initiate a state program.
* Usage: seq(<pSP>, <macros string>, <stack size>)
* pSP is the ptr to the state program structure.
* Example: seq(&myprog, "logfile=mylog", 0)
* When called from the shell, the 2nd & 3rd parameters are optional.
*
* Creates the initial state program task and returns its task id.
* Most initialization is performed here.
*/
long seq(pSeqProg, macro_def, stack_size)
struct seqProgram *pSeqProg; /* state program info generated by snc */
char *macro_def; /* optional macro def'n string */
long stack_size; /* optional stack size (bytes) */
{
int tid;
extern long sequencer();/* Sequencer task entry point */
extern sprog_delete(); /* Task delete routine */
extern char *seqVersion;
SPROG *pSP;
char *pValue, *ptask_name;
extern seqAuxTask();
/* Print version & date of sequencer */
printf("%s\n", seqVersion);
/* Spawn the sequencer auxillary task */
if (seqAuxTaskId == 0)
{
taskSpawn("seqAux", SPAWN_PRIORITY-1, VX_FP_TASK, 2000, seqAuxTask,
0,0,0,0,0,0,0,0,0,0);
while (seqAuxTaskId == 0)
taskDelay(5); /* wait for task to init. ch'l access */
#ifdef DEBUG
logMsg("task seqAux spawned, tid=0x%x\n", seqAuxTaskId);
#endif DEBUG
}
/* Specify a routine to run at task delete */
if (!seqDeleteHookAdded)
{
taskDeleteHookAdd(sprog_delete);
seqDeleteHookAdded = TRUE;
}
/* Exit if no parameters specified */
if (pSeqProg == 0)
{
return 0;
}
/* Check for correct state program format */
if (pSeqProg->magic != MAGIC)
{ /* Oops */
logMsg("Illegal magic number in state program.\n");
logMsg(" - Possible mismatch between SNC & SEQ versions\n");
logMsg(" - Re-compile your program?\n");
return -1;
}
/* Initialize the sequencer tables */
pSP = seqInitTables(pSeqProg);
/* Parse the macro definitions from the "program" statement */
seqMacParse(pSeqProg->pParams, pSP);
/* Parse the macro definitions from the command line */
seqMacParse(macro_def, pSP);
/* Do macro substitution on channel names */
seqChanNameEval(pSP);
/* Initialize sequencer logging */
seq_logInit(pSP);
/* Specify stack size */
if (stack_size == 0)
stack_size = SPAWN_STACK_SIZE;
pValue = seqMacValGet(pSP->pMacros, "stack");
if (pValue != NULL && strlen(pValue) > 0)
{
sscanf(pValue, "%d", &stack_size);
}
if (stack_size < SPAWN_STACK_SIZE/2)
stack_size = SPAWN_STACK_SIZE/2;
/* Specify task name */
pValue = seqMacValGet(pSP->pMacros, "name");
if (pValue != NULL && strlen(pValue) > 0)
ptask_name = pValue;
else
ptask_name = pSP->pProgName;
/* Spawn the initial sequencer task */
#ifdef DEBUG
logMsg("Spawing task %s, stack_size=%d\n", ptask_name, stack_size);
#endif
/* Specify task priority */
pSP->taskPriority = SPAWN_PRIORITY;
pValue = seqMacValGet(pSP->pMacros, "priority");
if (pValue != NULL && strlen(pValue) > 0)
{
sscanf(pValue, "%d", &(pSP->taskPriority));
}
if (pSP->taskPriority < SPAWN_PRIORITY)
pSP->taskPriority = SPAWN_PRIORITY;
if (pSP->taskPriority > 255)
pSP->taskPriority = 255;
tid = taskSpawn(ptask_name, pSP->taskPriority, SPAWN_OPTIONS,
stack_size, (FUNCPTR)sequencer, (int)pSP, stack_size, (int)ptask_name,
0,0,0,0,0,0,0);
seq_log(pSP, "Spawning state program \"%s\", task name = \"%s\"\n",
pSP->pProgName, ptask_name);
seq_log(pSP, " Task id = %d = 0x%x\n", tid, tid);
/* Return task id to calling program */
return tid;
}
/* seqInitTables - initialize sequencer tables */
LOCAL SPROG *seqInitTables(pSeqProg)
struct seqProgram *pSeqProg;
{
SPROG *pSP;
pSP = (SPROG *)calloc(1, sizeof (SPROG));
/* Initialize state program block */
init_sprog(pSeqProg, pSP);
/* Initialize state set control blocks */
init_sscb(pSeqProg, pSP);
/* Initialize database channel blocks */
init_chan(pSeqProg, pSP);
/* Initialize the macro table */
init_mac(pSP);
return pSP;
}
/* Count the total number of states in a state program */
LOCAL int countStates(pSeqProg)
struct seqProgram *pSeqProg;
{
struct seqSS *pSeqSS;
int nstates, nss;
nstates = 0;
for (nss = 0, pSeqSS = pSeqProg->pSS; nss < pSeqProg->numSS; nss++, pSeqSS++)
nstates += pSeqSS->numStates;
}
/*
* Copy data from seqCom.h structures into this task's dynamic structures as defined
* in seq.h.
*/
LOCAL VOID init_sprog(pSeqProg, pSP)
struct seqProgram *pSeqProg;
SPROG *pSP;
{
SSCB *pSS;
STATE *pState;
CHAN *pDB;
char *pVar;
int i, nWords;
/* Copy information for state program */
pSP->numSS = pSeqProg->numSS;
pSP->numChans = pSeqProg->numChans;
pSP->numEvents = pSeqProg->numEvents;
pSP->options = pSeqProg->options;
pSP->pProgName = pSeqProg->pProgName;
pSP->exitFunc = pSeqProg->exitFunc;
pSP->varSize = pSeqProg->varSize;
/* Allocate user variable area if reentrant option (+r) is set */
if ((pSP->options & OPT_REENT) != 0)
pSP->pVar = (char *)calloc(pSP->varSize, 1);
#ifdef DEBUG
logMsg("init_sprog: num SS=%d, num Chans=%d, num Events=%d, Prog Name=%s, var Size=%d\n",
pSP->numSS, pSP->numChans, pSP->numEvents, pSP->pProgName, pSP->varSize);
#endif DEBUG
/* Create a semaphore for resource locking on CA events */
pSP->caSemId = semBCreate(SEM_Q_FIFO, SEM_FULL);
if (pSP->caSemId == NULL)
{
logMsg("can't create caSemId\n");
return;
}
pSP->task_is_deleted = FALSE;
pSP->connCount = 0;
pSP->assignCount = 0;
pSP->logFd = 0;
/* Allocate an array for event flag bits */
nWords = (pSP->numEvents + NBITS - 1) / NBITS;
if (nWords == 0)
nWords = 1;
pSP->pEvents = (bitMask *)calloc(nWords, sizeof(bitMask));
for (i = 0; i < nWords; i++)
pSP->pEvents[i] = 0;
return;
}
/*
* Initialize the state set control blocks
*/
LOCAL VOID init_sscb(pSeqProg, pSP)
struct seqProgram *pSeqProg;
SPROG *pSP;
{
SSCB *pSS;
STATE *pState;
int nss, i, nstates;
struct seqSS *pSeqSS;
struct seqState *pSeqState;
/* Allocate space for the SSCB structures */
pSP->pSS = pSS = (SSCB *)calloc(pSeqProg->numSS, sizeof(SSCB));
/* Copy information for each state set and state */
pSeqSS = pSeqProg->pSS;
for (nss = 0; nss < pSeqProg->numSS; nss++, pSS++, pSeqSS++)
{
/* Fill in SSCB */
pSS->pSSName = pSeqSS->pSSName;
pSS->numStates = pSeqSS->numStates;
pSS->errorState = pSeqSS->errorState;
pSS->currentState = 0; /* initial state */
pSS->nextState = 0;
pSS->prevState = 0;
pSS->taskId = 0;
pSS->sprog = pSP;
#ifdef DEBUG
logMsg("init_sscb: SS Name=%s, num States=%d, pSS=0x%x\n",
pSS->pSSName, pSS->numStates, pSS);
#endif DEBUG
/* Create a binary semaphore for synchronizing events in a SS */
pSS->syncSemId = semBCreate(SEM_Q_FIFO, SEM_FULL);
if (pSS->syncSemId == NULL)
{
logMsg("can't create syncSemId\n");
return;
}
/* Create a binary semaphore for synchronous pvGet() (-a) */
if ((pSP->options & OPT_ASYNC) == 0)
{
pSS->getSemId =
semBCreate(SEM_Q_FIFO, SEM_FULL);
if (pSS->getSemId == NULL)
{
logMsg("can't create getSemId\n");
return;
}
}
/* Allocate & fill in state blocks */
pSS->pStates = pState = (STATE *)calloc(pSS->numStates, sizeof(STATE));
pSeqState = pSeqSS->pStates;
for (nstates = 0; nstates < pSeqSS->numStates;
nstates++, pState++, pSeqState++)
{
pState->pStateName = pSeqState->pStateName;
pState->actionFunc = pSeqState->actionFunc;
pState->eventFunc = pSeqState->eventFunc;
pState->delayFunc = pSeqState->delayFunc;
pState->pEventMask = pSeqState->pEventMask;
#ifdef DEBUG
logMsg("init_sscb: State Name=%s, Event Mask=0x%x\n",
pState->pStateName, *pState->pEventMask);
#endif DEBUG
}
}
#ifdef DEBUG
logMsg("init_sscb: numSS=%d\n", pSP->numSS);
#endif DEBUG
return;
}
/*
* init_chan--Build the database channel structures.
* Note: Actual PV name is not filled in here. */
LOCAL VOID init_chan(pSeqProg, pSP)
struct seqProgram *pSeqProg;
SPROG *pSP;
{
int nchan;
CHAN *pDB;
struct seqChan *pSeqChan;
/* Allocate space for the CHAN structures */
pSP->pChan = (CHAN *)calloc(pSP->numChans, sizeof(CHAN));
pDB = pSP->pChan;
pSeqChan = pSeqProg->pChan;
for (nchan = 0; nchan < pSP->numChans; nchan++, pDB++, pSeqChan++)
{
#ifdef DEBUG
logMsg("init_chan: pDB=0x%x\n", pDB);
#endif DEBUG
pDB->sprog = pSP;
pDB->dbAsName = pSeqChan->dbAsName;
pDB->pVarName = pSeqChan->pVarName;
pDB->pVarType = pSeqChan->pVarType;
pDB->pVar = pSeqChan->pVar; /* this is an offset for +r option */
pDB->count = pSeqChan->count;
pDB->efId = pSeqChan->efId;
pDB->monFlag = pSeqChan->monFlag;
pDB->eventNum = pSeqChan->eventNum;
pDB->assigned = 0;
/* Fill in get/put database types, element size, & access offset */
selectDBtype(pSeqChan->pVarType, &pDB->getType,
&pDB->putType, &pDB->size, &pDB->dbOffset);
/* Reentrant option: Convert offset to address of the user variable. */
if ((pSP->options & OPT_REENT) != 0)
pDB->pVar += (int)pSP->pVar;
#ifdef DEBUG
logMsg(" Assigned Name=%s, VarName=%s, VarType=%s, count=%d\n",
pDB->dbAsName, pDB->pVarName, pDB->pVarType, pDB->count);
logMsg(" size=%d, dbOffset=%d\n", pDB->size, pDB->dbOffset);
logMsg(" efId=%d, monFlag=%d, eventNum=%d\n",
pDB->efId, pDB->monFlag, pDB->eventNum);
#endif DEBUG
}
}
/*
* init_mac - initialize the macro table.
*/
LOCAL VOID init_mac(pSP)
SPROG *pSP;
{
int i;
MACRO *pMac;
pSP->pMacros = pMac = (MACRO *)calloc(MAX_MACROS, sizeof (MACRO));
#ifdef DEBUG
logMsg("init_mac: pMac=0x%x\n", pMac);
#endif
for (i = 0 ; i < MAX_MACROS; i++, pMac++)
{
pMac->pName = NULL;
pMac->pValue = NULL;
}
}
/*
* Evaluate channel names by macro substitution.
*/
#define MACRO_STR_LEN (MAX_STRING_SIZE+1)
LOCAL VOID seqChanNameEval(pSP)
SPROG *pSP;
{
CHAN *pDB;
int i;
pDB = pSP->pChan;
for (i = 0; i < pSP->numChans; i++, pDB++)
{
pDB->dbName = calloc(1, MACRO_STR_LEN);
seqMacEval(pDB->dbAsName, pDB->dbName, MACRO_STR_LEN, pSP->pMacros);
#ifdef DEBUG
logMsg("seqChanNameEval: \"%s\" evaluated to \"%s\"\n",
pDB->dbAsName, pDB->dbName);
#endif DEBUG
}
}
/*
* selectDBtype -- returns types for DB put/getm element size, and db access
* offset based on user variable type.
* Mapping is determined by the following typeMap[] array.
* DBR_TIME_* types for gets/monitors returns status and time stamp.
* Note that type "int" is mapped into DBR_LONG, because DBR_INT is actually
* 16 bits as defined in the database. This could cause future problems
* if the cpu architecture or compiler doesn't make this same assumption!
*/
LOCAL struct typeMap {
char *pTypeStr;
short putType;
short getType;
short size;
short offset;
} typeMap[] = {
"char", DBR_CHAR, DBR_TIME_CHAR,
sizeof (char), OFFSET(struct dbr_time_char, value),
"short", DBR_SHORT, DBR_TIME_SHORT,
sizeof (short), OFFSET(struct dbr_time_short, value),
"int", DBR_LONG, DBR_TIME_LONG,
sizeof (long), OFFSET(struct dbr_time_long, value),
"long", DBR_LONG, DBR_TIME_LONG,
sizeof (long), OFFSET(struct dbr_time_long, value),
"unsigned char", DBR_CHAR, DBR_TIME_CHAR,
sizeof (char), OFFSET(struct dbr_time_char, value),
"unsigned short", DBR_SHORT, DBR_TIME_SHORT,
sizeof (short), OFFSET(struct dbr_time_short, value),
"unsigned int", DBR_LONG, DBR_TIME_LONG,
sizeof (long), OFFSET(struct dbr_time_long, value),
"unsigned long", DBR_LONG, DBR_TIME_LONG,
sizeof (long), OFFSET(struct dbr_time_long, value),
"float", DBR_FLOAT, DBR_TIME_FLOAT,
sizeof (float), OFFSET(struct dbr_time_float, value),
"double", DBR_DOUBLE, DBR_TIME_DOUBLE,
sizeof (double), OFFSET(struct dbr_time_double, value),
"string", DBR_STRING, DBR_TIME_STRING,
MAX_STRING_SIZE, OFFSET(struct dbr_time_string, value[0]),
0, 0, 0, 0, 0
};
LOCAL VOID selectDBtype(pUserType, pGetType, pPutType, pSize, pOffset)
char *pUserType;
short *pGetType, *pPutType, *pSize, *pOffset;
{
struct typeMap *pMap;
for (pMap = &typeMap[0]; *pMap->pTypeStr != 0; pMap++)
{
if (strcmp(pUserType, pMap->pTypeStr) == 0)
{
*pGetType = pMap->getType;
*pPutType = pMap->putType;
*pSize = pMap->size;
*pOffset = pMap->offset;
return;
}
}
*pGetType = *pPutType = *pSize = *pOffset = 0; /* this shouldn't happen */
return;
}
/*
* seq_logInit() - Initialize logging.
* If "logfile" is not specified, then we log to standard output.
*/
LOCAL VOID seq_logInit(pSP)
SPROG *pSP;
{
char *pValue;
int fd;
/* Create a logging resource locking semaphore */
pSP->logSemId = semBCreate(SEM_Q_FIFO, SEM_FULL);
if (pSP->logSemId == NULL)
{
logMsg("can't create logSemId\n");
return;
}
pSP->logFd = ioGlobalStdGet(1); /* default fd is std out */
/* Check for logfile spec. */
pValue = seqMacValGet(pSP->pMacros, "logfile");
if (pValue != NULL && strlen(pValue) > 0)
{ /* Create & open a new log file for write only */
remove(pValue); /* remove the file if it exists */
fd = open(pValue, O_CREAT | O_WRONLY, 0664);
if (fd != ERROR)
pSP->logFd = fd;
printf("logfile=%s, fd=%d\n", pValue, fd);
}
}
/*
* seq_log
* Log a message to the console or a file with task name, date, & time of day.
* The format looks like "mytask 12/13/93 10:07:43: Hello world!".
*/
#include "tsDefs.h"
#define LOG_BFR_SIZE 200
STATUS seq_log(pSP, fmt, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8)
SPROG *pSP;
char *fmt; /* format string */
int arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8; /* arguments */
{
int fd, count, status;
TS_STAMP timeStamp;
char logBfr[LOG_BFR_SIZE], *pBfr;
pBfr = logBfr;
/* Enter taskname */
sprintf(pBfr, "%s ", taskName(taskIdSelf()) );
pBfr += strlen(pBfr);
/* Get time of day */
tsLocalTime(&timeStamp); /* time stamp format */
/* Convert to text format: "mm/dd/yy hh:mm:ss.nano-sec" */
tsStampToText(&timeStamp, TS_TEXT_MMDDYY, pBfr);
/* Truncate the ".nano-sec" part */
if (pBfr[2] == '/') /* valid t-s? */
pBfr += 17;
/* Insert ": " */
*pBfr++ = ':';
*pBfr++ = ' ';
/* Append the user's msg to the buffer */
sprintf(pBfr, fmt, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8);
pBfr += strlen(pBfr);
/* Write the msg */
semTake(pSP->logSemId, WAIT_FOREVER); /* lock it */
fd = pSP->logFd;
count = pBfr - logBfr + 1;
status = write(fd, logBfr, count);
semGive(pSP->logSemId);
if (status != count)
{
logMsg("Log file error, fd=%d, error no.=%d\n", fd, errnoGet());
printErrno(errnoGet());
return ERROR;
}
/* If this is an NSF file flush the buffer */
if (fd != ioGlobalStdGet(1) )
{
ioctl(fd, FIOSYNC, 0);
}
return OK;
}
/*
* seq_seqLog() - State program interface to seq_log().
* Does not require ptr to state program block.
*/
STATUS seq_seqLog(ssId, fmt, arg1,arg2, arg3, arg4, arg5, arg6, arg7, arg8)
SS_ID ssId;
char *fmt; /* format string */
int arg1,arg2, arg3, arg4, arg5, arg6, arg7, arg8; /* arguments */
{
SPROG *pSP;
pSP = ((SSCB *)ssId)->sprog;
return seq_log(pSP, fmt, arg1,arg2, arg3, arg4, arg5, arg6, arg7, arg8);
}