Files
epics-base/src/sequencer/seq_main.c
1993-07-26 13:00:33 +00:00

551 lines
14 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, 91, 92, 93, The Regents of the University of California.
Los Alamos National Laboratory
$Id$
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 task for channel access, 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
***************************************************************************/
/*#define DEBUG 1*/
#include "seq.h"
/* ANSI functional prototypes for local routines */
LOCAL SPROG *alloc_task_area(SPROG *);
LOCAL VOID copy_sprog(SPROG *, SPROG *);
LOCAL VOID init_sprog(SPROG *);
LOCAL VOID init_sscb(SPROG *);
LOCAL VOID seq_logInit(SPROG *);
LOCAL VOID seqChanNameEval(SPROG *);
#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.
*/
int seq(pSP_orig, macro_def, stack_size)
SPROG *pSP_orig; /* ptr to original state program table */
char *macro_def; /* optional macro def'n string */
int stack_size; /* optional stack size (bytes) */
{
int tid;
extern sequencer(); /* Sequencer task entry point */
extern sprog_delete(); /* Task delete routine */
extern char *seqVersion;
SPROG *pSP, *alloc_task_area();
char *pname, *pvalue, *ptask_name;
extern seqAuxTask();
/* Print version & date of sequencer */
printf("%s\n", seqVersion);
/* Exit if no parameters specified */
if (pSP_orig == 0)
{
return 0;
}
/* Check for correct state program format */
if (pSP_orig->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;
}
#ifdef DEBUG
print_sp_info(pSP_orig);
#endif DEBUG
/* 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 */
}
/* Specify a routine to run at task delete */
if (!seqDeleteHookAdded)
{
taskDeleteHookAdd(sprog_delete);
seqDeleteHookAdded = TRUE;
}
/* Allocate a contiguous space for all dynamic structures */
pSP = alloc_task_area(pSP_orig);
/* Make a private copy of original structures (but change pointers!) */
copy_sprog(pSP_orig, pSP);
/* Initialize state program block */
init_sprog(pSP);
/* Initialize state set control blocks */
init_sscb(pSP);
/* Initialize the macro definition table */
seqMacTblInit(pSP->mac_ptr); /* Init macro table */
/* Parse the macro definitions from the "program" statement */
seqMacParse(pSP->params, 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;
pname = "stack";
pvalue = seqMacValGet(pname, strlen(pname), pSP->mac_ptr);
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 */
pname = "name";
pvalue = seqMacValGet(pname, strlen(pname), pSP->mac_ptr);
if (pvalue != NULL && strlen(pvalue) > 0)
ptask_name = pvalue;
else
ptask_name = pSP->name;
/* Spawn the initial sequencer task */
tid = taskSpawn(ptask_name, SPAWN_PRIORITY, SPAWN_OPTIONS,
stack_size, 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->name, ptask_name);
seq_log(pSP, " Task id = %d = 0x%x\n", tid, tid);
/* Return task id to calling program */
return tid;
}
/*
* ALLOC_TASK_AREA
* Allocate a single block for all dynamic structures
* The pointer to the allocated area is saved for task delete hook routine.
*/
LOCAL SPROG *alloc_task_area(pSP_orig)
SPROG *pSP_orig; /* original state program structure */
{
int size, nss, nstates, nchannels, user_size,
prog_size, ss_size, state_size, chan_size, mac_size, scr_size;
SPROG *pSP_new; /* ptr to new state program struct */
char *dyn_ptr, *dyn_ptr_start; /* ptr to allocated area */
nss = pSP_orig->nss;
nstates = pSP_orig->nstates;
nchannels = pSP_orig->nchan;
/* Calc. # of bytes to allocate for all structures */
prog_size = sizeof(SPROG);
ss_size = nss*sizeof(SSCB);
state_size = nstates*sizeof(STATE);
chan_size = nchannels*sizeof(CHAN);
user_size = pSP_orig->user_size;
mac_size = MAX_MACROS*sizeof(MACRO);
scr_size = SCRATCH_SIZE;
/* Total # bytes to allocate */
size = prog_size + ss_size + state_size +
chan_size + user_size + mac_size + scr_size;
/* Alloc the dynamic task area */
dyn_ptr = dyn_ptr_start = (char *)calloc(size, 1);
#ifdef DEBUG
printf("Allocate task area:\n");
printf(" nss=%d, nstates=%d, nchannels=%d\n", nss, nstates, nchannels);
printf(" prog_size=%d, ss_size=%d, state_size=%d\n",
prog_size, ss_size, state_size);
printf(" user_size=%d, mac_size=%d, scr_size=%d\n",
user_size, mac_size, scr_size);
printf(" size=%d=0x%x\n", size, size);
printf(" dyn_ptr=%d=0x%x\n", dyn_ptr, dyn_ptr);
#endif DEBUG
/* Set ptrs in the PROG structure */
pSP_new = (SPROG *)dyn_ptr;
/* Copy the SPROG struct contents */
*pSP_new = *pSP_orig;
/* Allocate space for copies of the original structures */
dyn_ptr += prog_size;
pSP_new->sscb = (SSCB *)dyn_ptr;
dyn_ptr += ss_size;
pSP_new->states = (STATE *)dyn_ptr;
dyn_ptr += state_size;
pSP_new->channels = (CHAN *)dyn_ptr;
dyn_ptr += chan_size;
if (user_size != 0)
{
pSP_new->user_area = (char *)dyn_ptr;
dyn_ptr += user_size;
}
else
pSP_new->user_area = NULL;
/* Create additional dynamic structures for macros and scratch area */
pSP_new->mac_ptr = (MACRO *)dyn_ptr;
dyn_ptr += mac_size;
pSP_new->scr_ptr = (char *)dyn_ptr;
pSP_new->scr_nleft = scr_size;
/* Save ptr to start of allocated area so we can free it at task delete */
pSP_new->dyn_ptr = dyn_ptr_start;
return pSP_new;
}
/*
* Copy the SSCB, STATE, and CHAN structures into this task's dynamic structures.
* Note: we have to change some pointers in the SPROG struct, user variables,
* and all SSCB structs.
*/
LOCAL VOID copy_sprog(pSP_orig, pSP)
SPROG *pSP_orig; /* original ptr to program struct */
SPROG *pSP; /* new ptr */
{
SSCB *pSS, *pSS_orig;
STATE *pST, *pST_orig;
CHAN *pDB, *pDB_orig;
int nss, nstates, nchan;
char *var_ptr;
/* Ptr to 1-st SSCB in original SPROG */
pSS_orig = pSP_orig->sscb;
/* Ptr to 1-st SSCB in new SPROG */
pSS = pSP->sscb;
/* Copy structures for each state set */
for (nss = 0, pST = pSP->states; nss < pSP->nss; nss++)
{
*pSS = *pSS_orig; /* copy SSCB */
pSS->states = pST; /* new ptr to 1-st STATE */
pST_orig = pSS_orig->states;
for (nstates = 0; nstates < pSS->num_states; nstates++)
{
*pST = *pST_orig; /* copy STATE struct */
pST++;
pST_orig++;
}
pSS++;
pSS_orig++;
}
/* Copy database channel structures */
pDB = pSP->channels;
pDB_orig = pSP_orig->channels;
var_ptr = pSP->user_area;
for (nchan = 0; nchan < pSP->nchan; nchan++)
{
*pDB = *pDB_orig;
/* Reset ptr to SPROG structure */
pDB->sprog = pSP;
/* +r: Convert offset to address of the user variable.
* -r: var_ptr is an absolute address.
*/
if (pSP->options & OPT_REENT)
pDB->var += (int)var_ptr;
pDB++;
pDB_orig++;
}
/* Note: user area is not copied; it should be all zeros */
return;
}
/*
* Initialize the state program block
*/
LOCAL VOID init_sprog(pSP)
SPROG *pSP;
{
int i;
/* 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->logFd = 0;
/* Clear all event flags */
for (i = 0; i < NWRDS; i++)
pSP->events[i] = 0;
return;
}
/*
* Initialize the state set control blocks
*/
LOCAL VOID init_sscb(pSP)
SPROG *pSP;
{
SSCB *pSS;
int nss, i;
for (nss = 0, pSS = pSP->sscb; nss < pSP->nss; nss++, pSS++)
{
pSS->task_id = 0;
/* 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 pvGet() synconizing */
if (!pSP->options & OPT_ASYNC)
{
pSS->getSemId =
semBCreate(SEM_Q_FIFO, SEM_FULL);
if (pSS->getSemId == NULL)
{
logMsg("can't create getSemId\n");
return;
}
}
pSS->current_state = 0; /* initial state */
pSS->next_state = 0;
pSS->action_complete = TRUE;
}
return;
}
/*
* 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->channels;
for (i = 0; i < pSP->nchan; i++, pDB++)
{
pDB->db_name = seqAlloc(pSP, MACRO_STR_LEN);
seqMacEval(pDB->db_uname, pDB->db_name, MACRO_STR_LEN, pSP->mac_ptr);
}
}
/*
* seqAlloc: Allocate memory from the program scratch area.
* Always round up to even no. bytes.
*/
char *seqAlloc(pSP, nChar)
SPROG *pSP;
int nChar;
{
char *pStr;
pStr = pSP->scr_ptr;
/* round to even no. bytes */
if ((nChar & 1) != 0)
nChar++;
if (pSP->scr_nleft >= nChar)
{
pSP->scr_ptr += nChar;
pSP->scr_nleft -= nChar;
return pStr;
}
else
return NULL;
}
/*
* seq_logInit() - Initialize logging.
* If "logfile" is not specified, then we log to standard output.
*/
LOCAL VOID seq_logInit(pSP)
SPROG *pSP;
{
char *pname, *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. */
pname = "logfile";
pvalue = seqMacValGet(pname, strlen(pname), pSP->mac_ptr);
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 time of day and task id.
* The format looks like "mytask 12/13/91 10:07:43: <user's message>".
*/
#include "tsDefs.h"
#define LOG_BFR_SIZE 200
VOID 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);
/* We're going to 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);
if (status != count)
{
logMsg("Log file error, fd=%d, error no.=%d\n", fd, errnoGet());
printErrno(errnoGet());
return;
}
/* If this is an NSF file flush the buffer */
if (fd != ioGlobalStdGet(1) )
{
ioctl(fd, FIOSYNC, 0);
}
semGive(pSP->logSemId);
return;
}
/*
* seqLog() - State program interface to seq_log().
* Does not require ptr to state program block.
*/
STATUS seqLog(fmt, arg1,arg2, arg3, arg4, arg5, arg6, arg7, arg8)
char *fmt; /* format string */
int arg1,arg2, arg3, arg4, arg5, arg6, arg7, arg8; /* arguments */
{
SPROG *pSP, *seqFindProg();
pSP = seqFindProg(taskIdSelf());
if (pSP == NULL)
return ERROR;
seq_log(pSP, fmt, arg1,arg2, arg3, arg4, arg5, arg6, arg7, arg8);
return OK;
}
/*
* seq_optGet: return the value of an option.
* FALSE means "-" and TRUE means "+".
*/
BOOL seq_optGet(pSP, opt)
SPROG *pSP;
char *opt; /* one of the snc options as a strign (e.g. "a") */
{
switch (opt[0])
{
case 'a': return ( (pSP->options & OPT_ASYNC) != 0);
case 'c': return ( (pSP->options & OPT_CONN) != 0);
case 'd': return ( (pSP->options & OPT_DEBUG) != 0);
case 'r': return ( (pSP->options & OPT_REENT) != 0);
case 'e': return ( (pSP->options & OPT_NEWEF) != 0);
default: return FALSE;
}
}