EPICS Controls Argonne National Laboratory

Experimental Physics and
Industrial Control System

2002  2003  2004  2005  2006  2007  <20082009  2010  2011  2012  2013  2014  2015  2016  2017  2018  2019  2020  2021  2022  2023  2024  Index 2002  2003  2004  2005  2006  2007  <20082009  2010  2011  2012  2013  2014  2015  2016  2017  2018  2019  2020  2021  2022  2023  2024 
<== Date ==> <== Thread ==>

Subject: Re: seq!
From: "Liu, Gongfa" <[email protected]>
To: Andrew Johnson <[email protected]>, EPICS core-talk <[email protected]>
Cc: Matthias Clausen <[email protected]>, Michael Laznovsky <[email protected]>
Date: Thu, 13 Nov 2008 10:16:21 +0100
Hi, all,

The attached file is the "svn diff" output.

Regrads,
Gongfa

Andrew Johnson wrote:
Hi Gongfa,

On Tuesday 11 November 2008 10:43:21 Liu, Gongfa wrote:
I have checked out sncseq from the address you provided and finished
the mergence of our changes:
(1) support accessing the local variable. The implementation is done by
  Michael Laznovsky originally.
(2) support debugging
(3) support redundancy

Now we need an account to commit our changes. Would you like to create
an account for us?

I would like to see your changes reviewed before they are committed by mailing the 'svn diff' output to core-talk, with a CC to Michael since I don't know if he has subscribed to that list yet from his new address.

I will also let Michael as the SNCSEQ maintainer decide whether to make all commits himself or to let others have accounts to do so, although I don't he has an SVN account himself yet.

I think with this change the version number should become 2.1 rather than 2.0.13 since I imagine it involves some significant modification or additions to the internals of the sequencer.

- Andrew

--
----------------------------------------------------------
Gongfa Liu                         MKS-2, DESY
phone:  +49-40-8998-1642           Notkestr. 85
fax:    +49-40-8998-4388           22607 Hamburg
e-mail: [email protected]         Germany
----------------------------------------------------------
Index: src/snc/phase2.c
===================================================================
--- src/snc/phase2.c	(revision 194)
+++ src/snc/phase2.c	(working copy)
@@ -30,6 +30,7 @@
 06mar00,wfl	Added threadInit() to main program; removed ASYNC/SYNC #defines.
 17mar00,wfl	Added necessary includes for C main program.
 31mar00,wfl	Supported entry handler.
+13dec07,liu	Add num_locvars to support accessing local variables.
 ***************************************************************************/
 /*#define DEBUG 1*/
 
@@ -51,6 +52,7 @@
 int	num_ss = 0;		/* number of state sets */
 int	max_delays = 0;		/* maximum number of delays per state */
 int	num_errors = 0;		/* number of errors detected in phase 2 processing */
+int	num_locvars = 0;	/* number of local variables (includes assigned to PVs?) */
 
 void	gen_preamble();
 void	gen_opt_defn();
@@ -145,10 +147,12 @@
 	printf("#define NUM_CHANNELS %d\n", num_channels);
 	printf("#define NUM_EVENTS %d\n", num_events);
 	printf("#define NUM_QUEUES %d\n", num_queues);
+	printf("#define NUM_LOCVARS %d\n", num_locvars);
 
 	/* The following definition should be consistant with db_access.h */
+#define MAX_STRING_SIZE 40
 	printf("\n");
-	printf("#define MAX_STRING_SIZE 40\n");
+	printf("#define MAX_STRING_SIZE %d\n",MAX_STRING_SIZE);
 
 	/* #define's for compiler options */
 	printf("\n");
@@ -180,7 +184,6 @@
 	    printf("    macro_def = (argc>1)?argv[1]:NULL;\n");
 	    printf("    threadId = seq((void *)&%s, macro_def, 0);\n", prog_name);
             printf("    if(callIocsh) {\n");
-            printf("        seqRegisterSequencerCommands();\n");
             printf("        iocsh(0);\n");
             printf("    } else {\n");
             printf("        epicsThreadExitMain();\n");
@@ -213,6 +216,11 @@
 	extern Expr		*ss_list, *entry_code_list, *exit_code_list;
 	Expr			*ssp, *ep;
 
+	extern Var		*var_list;
+	Var			*vp;
+
+	char			*db_type_str();
+
 	for (ssp = ss_list; ssp != 0; ssp = ssp->next)
 	{
 #ifdef	DEBUG
@@ -233,6 +241,13 @@
 		traverseExprTree(ep, E_VAR, 0, connect_variable, 0);
 	}
 
+	/* count all local variables we know about (ie. not declared in escaped code) */
+	for (vp  = var_list;
+	     vp != NULL;
+	     vp  = vp->next)
+	{
+	  if (strlen(db_type_str(vp->type)) > 0) num_locvars++;
+	}
 }
 
 /* Connect a variable in an expression to the the Var structure */
Index: src/snc/gen_tables.c
===================================================================
--- src/snc/gen_tables.c	(revision 194)
+++ src/snc/gen_tables.c	(working copy)
@@ -26,6 +26,7 @@
 		avoided warnings when no variables are mapped to channels.
 18feb00,wfl     Partial support for local declarations (not yet complete).
 31mar00,wfl	Supported entry handler.
+18jan08,liu	add gen_var_blocks() to support accessing local variables.
 ***************************************************************************/
 /*#define	DEBUG	1*/
 
@@ -39,6 +40,7 @@
 
 void encode_state_options(Expr *sp);
 void		gen_db_blocks();
+void		gen_var_blocks();
 void		fill_db_block();
 void		gen_state_blocks();
 void		fill_state_block();
@@ -52,6 +54,8 @@
 
 int		find_error_state();
 
+#define max(a,b) (((a) > (b)) ? (a) : (b))	/* generic max() */
+
 /*+************************************************************************
 *  NAME: gen_tables
 *
@@ -73,6 +77,9 @@
 	/* Generate DB blocks */
 	gen_db_blocks();
 
+	/* Generate Local Variable Blocks */
+	gen_var_blocks();
+
 	/* Generate State Blocks */
 	gen_state_blocks();
 
@@ -130,7 +137,7 @@
 	}
 	return;
 }
-
+
 /* Fill in a db block with data (all elements for "seqChan" struct) */
 void fill_db_block(cp, elem_num)
 Chan		*cp;
@@ -153,6 +160,7 @@
 	else
 		elem_str[0] = '\0';
 
+
 	if (vp->type == V_STRING)
 		suffix = "[0]";
 	else
@@ -237,6 +245,78 @@
 	}
 }
 
+/* Generate blocks with data for each defined (or referenced) local variable
+ *  ... lazmo@lsac 22-Mar-2006
+ */
+void gen_var_blocks()
+{
+	extern Var	*var_list;
+	Var		*vp;
+	char		*vstr;
+	int		nv;
+
+	char		*suf1, *suf2;
+
+	extern int	reent_opt;
+	extern int	num_locvars;
+
+	char		*db_type_str(); 
+
+	printf("\n");
+	printf("/* Local Variables (some may also be connected to PVs above)\n");
+	printf(" *   ... does not include escaped declarations\n");
+	printf(" */\n");
+
+
+	if (var_list) {
+
+	  printf("static struct seqVar seqVar[NUM_LOCVARS] = {\n");
+
+	  printf("  /* name type_i type_s class dim1 dim2 initial address */\n");
+
+	  for (nv=0, vp = var_list;
+	       vp != NULL; 
+	       nv++, vp = vp->next)
+	  {
+           if(strlen(db_type_str(vp->type)) > 0)  /*don't include escaped vars */
+           {
+	    if (nv > 0) printf(",\n");
+	    printf("  {");
+
+	    printf(" \"%s\",\t", vp->name);				/* name (string) */
+	    printf(" %2d,",   vp->type);				/* type (enum) */
+	    printf(" \"%s\",\t", db_type_str(vp->type));		/* type (string) */
+	    printf(" %d,", vp->class);					/* class (enum) */
+	    printf(" %3d, %3d,", vp->length1, vp->length2);		/* array dimensions; [1][1] if none */
+
+	    /* run-time address pointer suffix */
+	    suf1 =    (vp->class == VC_ARRAY1 || vp->class == VC_ARRAYP) ? "[0]"	/* 1-dim */
+		    : (vp->class == VC_ARRAY2)                           ? "[0][0]"	/* 2-dim */
+		    : "";								/* no dim */
+
+	    suf2 = (vp->type == V_STRING) ? "[0]" : "";			/* string also needs "[0]" */
+
+	    printf(vp->value ? " \"%s\",\t" : " NULL,", vp->value);	/* initial value (string); NULL if none */
+
+	    /* ptr (or offset) to variable
+	     *  ... reentrant entries are resolved in at runtime
+	     */
+	    printf(reent_opt
+		   ? " (void *)OFFSET(struct UserVar, %s%s%s)"
+		   : " (void *)&%s%s%s",
+		   vp->name, suf1, suf2);
+
+	    printf(" }");
+           }
+	  }
+	  printf("\n};\n");
+	}
+	else {
+	  printf("\n/* No Variables; create 1 for ptr init. */\n");
+	  printf("static struct seqVar seqVar[1];\n");
+	}
+}
+
 /* Generate structure and data for state blocks */
 void gen_state_blocks()
 {
@@ -292,6 +372,8 @@
         Expr *ep;
 	int isEntry = FALSE, isExit = FALSE;
 
+	extern int      debug_opt;
+
 	/* Check if there are any entry or exit "transitions" in this
 	   state so that if so the state block will be initialized to include a
 	   reference to the function which implements them, but otherwise just
@@ -462,9 +544,11 @@
 	printf("\t/* *name */              \"%s\",\n", prog_name);/* program name */
 
 	printf("\t/* *pChannels */         seqChan,\n");	/* table of db channels */
-
 	printf("\t/* numChans */           NUM_CHANNELS,\n");	/* number of db channels */
 
+	printf("\t/* *pVars */             seqVar,\n");	/* table of local variables */
+	printf("\t/* numVars */            NUM_LOCVARS,\n");	/* number of local variables */
+
 	printf("\t/* *pSS */               seqSS,\n");		/* array of SS blocks */
 
 	printf("\t/* numSS */              NUM_SS,\n");	/* number of state sets */
Index: src/snc/gen_ss_code.c
===================================================================
--- src/snc/gen_ss_code.c	(revision 194)
+++ src/snc/gen_ss_code.c	(working copy)
@@ -25,6 +25,7 @@
 18feb00,wfl	More partial support for local declarations (still not done)
 31mar00,wfl	Put 'when' code in a block (allows local declarations);
 		supported entry handler
+14dec07,liu	add gen_event_string_func();
 ***************************************************************************/
 #include	<stdio.h>
 #include	<string.h>
@@ -60,6 +61,7 @@
 void eval_delay(Expr *ep, Expr *sp);
 void gen_action_func(Expr *sp, Expr *ssp);
 void gen_event_func(Expr *sp, Expr *ssp);
+void gen_event_string_func(Expr *sp, Expr *ssp);
 void eval_expr(int stmt_type, Expr *ep, Expr *sp, int level);
 void indent(int level);
 void gen_ef_func(int stmt_type,Expr *ep,Expr *sp,char *fname,
@@ -75,6 +77,9 @@
 int state_block_index_from_name(Expr *ssp,char *state_name);
 int special_func(int stmt_type,Expr *ep,Expr *sp);
 
+extern int      debug_opt;
+
+
 /*+************************************************************************
 *  NAME: gen_ss_code
 *
@@ -123,6 +128,9 @@
 			/* Generate event processing function */
 			gen_event_func(sp, ssp);
 
+			/* Generate when()-string access function */
+			gen_event_string_func(sp, ssp);
+
 			/* Generate action processing function */
 			gen_action_func(sp, ssp);
 		}
@@ -162,6 +170,7 @@
 	/* Entry function declaration */
 	printf("\n/* Entry function for state \"%s\" in state set \"%s\" */\n",
  		sp->value, ssp->value);
+
 	printf("static void I_%s_%s(SS_ID ssId, struct UserVar *pVar)\n{\n",
 		ssp->value, sp->value);
 
@@ -253,25 +262,24 @@
  */
 void eval_delay(Expr *ep, Expr *sp)
 {
-	int		delay_id;
+        int             delay_id;
 
-#ifdef	DEBUG
-	fprintf(stderr, "eval_delay: type=%s\n", stype[ep->type]);
-#endif	/*DEBUG*/
+#ifdef  DEBUG
+        fprintf(stderr, "eval_delay: type=%s\n", stype[ep->type]);
+#endif  /*DEBUG*/
 
-	/* Generate 1-st part of function w/ 1-st 2 parameters */
-	delay_id = (int)ep->right; /* delay id was previously assigned */
-	printf("\tseq_delayInit(ssId, %d, (", delay_id);
+        /* Generate 1-st part of function w/ 1-st 2 parameters */
+        delay_id = (int)ep->right; /* delay id was previously assigned */
+        printf("\tseq_delayInit(ssId, %d, (", delay_id);
 
-	/* Evaluate & generate the 3-rd parameter (an expression) */
-	eval_expr(EVENT_STMT, ep->left, sp, 0);
+        /* Evaluate & generate the 3-rd parameter (an expression) */
+        eval_expr(EVENT_STMT, ep->left, sp, 0); 
 
-	/* Complete the function call */
-	printf("));\n");
+        /* Complete the function call */ 
+        printf("));\n");
 }
 
 
-
 /* Generate action processing functions:
    Each state has one action routine.  It's name is derived from the
    state set name and the state name.
@@ -281,18 +289,22 @@
 	Expr		*tp;
 	Expr		*ap;
 	int		trans_num;
-	extern int	line_num;
+	extern int      line_num;
 
 	/* Action function declaration */
+
 	printf("\n/* Action function for state \"%s\" in state set \"%s\" */\n",
 	 sp->value, ssp->value);
+
 	printf("static void A_%s_%s(SS_ID ssId, struct UserVar *pVar, short transNum)\n{\n",
 	 ssp->value, sp->value);
 
 	/* "switch" statment based on the transition number */
 	printf("\tswitch(transNum)\n\t{\n");
+
 	trans_num = 0;
 	line_num = 0;
+
 	/* For each transition ("when" statement) ... */
 	for (tp = sp->left; tp != NULL; tp = tp->next)
 	{
@@ -330,8 +342,10 @@
                          trans_num++;
 		}
 	}
+
 	/* end of switch stmt */
 	printf("\t}\n");
+
 	/* end of function */
 	printf("}\n");
 	return;
@@ -345,21 +359,27 @@
 
 	printf("\n/* Event function for state \"%s\" in state set \"%s\" */\n",
 	 sp->value, ssp->value);
+
 	printf("static long E_%s_%s(SS_ID ssId, struct UserVar *pVar, short *pTransNum, short *pNextState)\n{\n",
 	 ssp->value, sp->value);
+
 	trans_num = 0;
+
 	/* For each transition generate an "if" statement ... */
 	for (tp = sp->left; tp != NULL; tp = tp->next)
 	{
 	        if (tp->type == E_WHEN) 
 		{
 		        print_line_num(tp->line_num, tp->src_file);
+
 			printf("\tif (");
+
 			if (tp->left == 0)
 			      printf("TRUE");
 			else
 			      eval_expr(EVENT_STMT, tp->left, sp, 0);
 			printf(")\n\t{\n");
+
 			/* index is the transition number (0, 1, ...) */
 			index = state_block_index_from_name(ssp, tp->value);
 			if (index < 0)
@@ -381,6 +401,56 @@
 	printf("}\n");
 }
 
+/* Generate a C function that returns the when() condition as a string, for a particular state */
+void gen_event_string_func(Expr *sp, Expr *ssp)
+{
+  Expr		*tp;
+  int		index, trans_num;
+  int		n;
+
+  if (!debug_opt) return;
+
+  printf("\n/* Event-string function for state \"%s\" in state set \"%s\" */\n",
+	 sp->value, ssp->value);
+
+  printf("static void ES_%s_%s(SS_ID ssId, short transNum, char **ppString)\n",ssp->value,sp->value);
+  printf("{\n");
+
+  printf("static char *when_string[] = {");
+
+  trans_num = 0;
+  n = 0;
+  for (tp  = sp->left;
+       tp != NULL;
+       tp  = tp->next) {	/* for each transition... */
+
+    if (tp->type == E_WHEN) {
+
+      printf("%s\n\t/* %d */ \"", (n>0) ? "," : "", trans_num);
+      if (tp->left) eval_expr(EVENT_STMT, tp->left, sp, 0);
+      printf("\"");
+
+      /* index is the transition number (0, 1, ...) */
+      index = state_block_index_from_name(ssp, tp->value);
+      if (index < 0) {
+	index = 0;	/* default to 1st state */
+	printf("\t/* state %s does not exist */\n",tp->value);
+      }
+
+      trans_num++;
+      n++;
+    }
+  }
+  printf("\n};\n\n");
+
+  printf("\tif ((transNum < 0) || (transNum >= sizeof(when_string)/sizeof(char*))) {\n");
+  printf("\t  *ppString = \"???\";\n");
+  printf("\t} else {\n");
+  printf("\t  *ppString = when_string[transNum];\n");
+  printf("\t}\n");
+  printf("}\n");
+}
+
 /* Given a state name and state set struct, find the corresponding
    state struct and return its index (1-st one is 0) */
 int state_block_index_from_name(Expr *ssp,char *state_name)
Index: src/snc/parse.c
===================================================================
--- src/snc/parse.c	(revision 194)
+++ src/snc/parse.c	(working copy)
@@ -809,6 +809,10 @@
 Expr		*ep1;	/* beginning of 1-st structure or list */
 Expr		*ep2;	/* beginning 2-nd (append it to 1-st) */
 {
+#ifdef	DEBUG
+	Expr		*ep;
+#endif	/*DEBUG*/
+
 	if (ep1 == 0 && ep2 == 0)
 	        return NULL;
 	else if (ep1 == 0)
Index: src/seq/seq_prog.c
===================================================================
--- src/seq/seq_prog.c	(revision 194)
+++ src/seq/seq_prog.c	(working copy)
@@ -18,6 +18,7 @@
 18feb00,wfl	Avoided memory leak.
 29feb00,wfl	Supported new OSI.
 31mar00,wfl	Added seqFindProgByName().
+14dec07,liu	Support accessing local variabls.
 ***************************************************************************/
 /*#define	DEBUG*/
 
@@ -40,13 +41,14 @@
 
 #define	seqListNext(pNode)	(PROG_NODE *)ellNext((ELLNODE *)pNode)
 
-/*
- * seqFindProg() - find a program in the state program list from thread id.
+/*----------------------------------------------------------------
+ * seqFindProg() - find a program from thread id
+ *----------------------------------------------------------------
  */
 SPROG *seqFindProg(epicsThreadId threadId)
 {
 	PROG_NODE	*pNode;
-	SPROG		*pSP;
+	SPROG		*pSP, *pFound = NULL;
 	SSCB		*pSS;
 	int		n;
 
@@ -55,32 +57,46 @@
 
 	epicsMutexMustLock(seqProgListSemId);
 
-	for (pNode = seqListFirst(&seqProgList); pNode != NULL;
-	     pNode = seqListNext(pNode) )
+	for (pNode  = seqListFirst(&seqProgList);
+	     pNode != NULL;
+	     pNode  = seqListNext(pNode))
 	{
-		pSP = pNode->pSP;
-		if (pSP->threadId == threadId)
-		{
-			epicsMutexUnlock(seqProgListSemId);
-			return pSP;
-		}
-		pSS = pSP->pSS;
-		for (n = 0; n < pSP->numSS; n++, pSS++)
-		{
-			if (pSS->threadId == threadId)
-			{
-				epicsMutexUnlock(seqProgListSemId);
-				return pSP;
-			}
-		}
+	  pSP = pNode->pSP;
+
+#if 1 /* ...also in pSS? */
+	  if (pSP->threadId == threadId) {
+	    pFound = pSP;
+	    break;
+	  }
+#endif
+
+	  pSS = pSP->pSS;
+
+#if 0
+printf("pSP->numSS: %d\n",pSP->numSS);
+#endif
+
+	  for (n = 0; n < pSP->numSS; n++, pSS++) {
+
+#if 0
+printf("pSS->threadId: 0x%x\n",pSS->threadId);
+#endif
+
+	    if (pSS->threadId == threadId) {
+	      pFound = pSP;
+	      break;
+	    }
+	  }
+	  if (pFound) break;
 	}
+
 	epicsMutexUnlock(seqProgListSemId);
-
-	return NULL; /* not in list */
+	return pFound;
 }
 
-/*
- * seqFindProgByName() - find a program in the state program list by name.
+/*----------------------------------------------------------------
+ * seqFindProgByName() - find a program in the state program list by name
+ *----------------------------------------------------------------
  */
 epicsShareFunc SPROG *epicsShareAPI seqFindProgByName(char *pProgName)
 {
@@ -92,8 +108,9 @@
 
 	epicsMutexMustLock(seqProgListSemId);
 
-	for (pNode = seqListFirst(&seqProgList); pNode != NULL;
-	     pNode = seqListNext(pNode) )
+	for (pNode  = seqListFirst(&seqProgList);
+	     pNode != NULL;
+	     pNode  = seqListNext(pNode) )
 	{
 		pSP = pNode->pSP;
 		if (strcmp(pSP->pProgName, pProgName) == 0)
@@ -107,10 +124,11 @@
 	return NULL; /* not in list */
 }
 
-/*
- * seqTraverseProg() - travers programs in the state program list and
- * call the specified routine or function.  Passes one parameter of
- * pointer size.
+/*----------------------------------------------------------------
+ * seqTraverseProg() - traverse the program list and call a specified
+ *  function for each one
+ *  ... passes one parameter of pointer size
+ *----------------------------------------------------------------
  */
 epicsShareFunc epicsStatus seqTraverseProg(pFunc, param)
 void		(*pFunc)();	/* function to call */
@@ -123,8 +141,10 @@
 		return ERROR;
 
 	epicsMutexMustLock(seqProgListSemId);
-	for (pNode = seqListFirst(&seqProgList); pNode != NULL;
-	     pNode = seqListNext(pNode) )
+
+	for (pNode  = seqListFirst(&seqProgList);
+	     pNode != NULL;
+	     pNode  = seqListNext(pNode) )
 	{
 		pSP = pNode->pSP;
 		pFunc(pSP, param);
@@ -134,9 +154,10 @@
 	return OK;
 }
 
-/*
+/*----------------------------------------------------------------
  * seqAddProg() - add a program to the state program list.
  * Returns ERROR if program is already in list, else TRUE.
+ *----------------------------------------------------------------
  */
 epicsShareFunc epicsStatus seqAddProg(pSP)
 SPROG		*pSP;
@@ -147,8 +168,10 @@
 		seqProgListInit(); /* Initialize list */
 
 	epicsMutexMustLock(seqProgListSemId);
-	for (pNode = seqListFirst(&seqProgList); pNode != NULL;
-	     pNode = seqListNext(pNode) )
+
+	for (pNode  = seqListFirst(&seqProgList);
+	     pNode != NULL;
+	     pNode  = seqListNext(pNode) )
 	{
 
 		if (pSP == pNode->pSP)
@@ -180,9 +203,10 @@
 	return OK;
 }
 
-/* 
- *seqDelProg() - delete a program from the program list.
+/*----------------------------------------------------------------
+ * seqDelProg() - delete a program from the program list.
  * Returns TRUE if deleted, else FALSE.
+ *----------------------------------------------------------------
  */
 epicsShareFunc epicsStatus seqDelProg(pSP)
 SPROG		*pSP;
@@ -193,8 +217,10 @@
 		return ERROR;
 
 	epicsMutexMustLock(seqProgListSemId);
-	for (pNode = seqListFirst(&seqProgList); pNode != NULL;
-	     pNode = seqListNext(pNode) )
+
+	for (pNode  = seqListFirst(&seqProgList);
+	     pNode != NULL;
+	     pNode  = seqListNext(pNode) )
 	{
 		if (pNode->pSP == pSP)
 		{
@@ -214,8 +240,9 @@
 	return ERROR; /* not in list */
 }
 
-/*
+/*----------------------------------------------------------------
  * seqProgListInit() - initialize the state program list.
+ *----------------------------------------------------------------
  */
 LOCAL void seqProgListInit()
 {
@@ -227,3 +254,4 @@
 	seqProgListInited = TRUE;
 }
 
+/* end */
Index: src/seq/seq_qry.c
===================================================================
--- src/seq/seq_qry.c	(revision 194)
+++ src/seq/seq_qry.c	(working copy)
@@ -31,6 +31,7 @@
 		stamp unconditionally.
 29feb00,wfl	Supported new OSI; removed remaining OS-dependencies.
 31mar00,wfl	Removed limitation on only printing 5 array elements.
+18jan08,liu     Support accessing local variabls.
 ***************************************************************************/
 
 /*#define	DEBUG	1*/
@@ -40,6 +41,7 @@
 
 #define epicsExportSharedSymbols
 #include	"seq.h"
+#include	"../snc/parse.h"	/* for V_* #defines */
 
 /* User functions */
 LOCAL	int wait_rtn();
@@ -299,11 +301,12 @@
 }
 
 void epicsShareAPI seqcaStats(int *pchans, int *pdiscon) {
-	struct seqStats stats = {0, 0, 0, 0};
-	seqTraverseProg(seqcarCollect, (void *) &stats);
-	if (pchans)  *pchans  = stats.nChans;
-	if (pdiscon) *pdiscon = stats.nChans - stats.nConn;
+        struct seqStats stats = {0, 0, 0, 0};
+        seqTraverseProg(seqcarCollect, (void *) &stats);
+        if (pchans)  *pchans  = stats.nChans;
+        if (pdiscon) *pdiscon = stats.nChans - stats.nConn;
 }
+
 /*
  * seqQueueShow() - Show syncQ queue information for a state program.
  */
@@ -521,3 +524,428 @@
 		printf("No active state programs\n");
 	return;
 }
+
+/*
+ ************************************************************************
+ ************************************************************************
+ * PV dumper
+ *  ... to file? ........................
+ ************************************************************************
+ ************************************************************************
+ */
+
+/*----------------------------------------------------------------
+ * internal func ... list PVs via pSP
+ *----------------------------------------------------------------
+ */
+static void SeqPvShowBySP (SPROG *pSP, void *dummy)
+{
+  CHAN *pDB;
+  int n;
+
+  printf("program %s %p\n",
+	 pSP->pProgName,
+	 pSP->threadId);
+
+  for (n = 0, pDB = pSP->pChan;
+       n < pSP->numChans;
+       n++)
+  {
+    if (pDB->dbName) printf("%s\n",pDB->dbName);
+    pDB++;
+  }
+}
+
+/*----------------------------------------------------------------
+ * list PVs for single task, or all progs if tid==0
+ *  ... ACHTUNG!  seqTraverseProg() waits on "seqProgListSemId" semaphore
+ *----------------------------------------------------------------
+ */
+long epicsShareAPI seqPvShow (epicsThreadId tid)
+{
+  epicsShareFunc epicsStatus seqTraverseProg(void (*pFunc)(), void *param);	/* seq_prog.c */
+
+  SPROG *pSP;
+
+  if (tid == 0) {				/* all progs */
+    seqTraverseProg (SeqPvShowBySP,0);
+    return OK;
+  }
+
+  if (NULL == (pSP = seqFindProg(tid)))		/* find state pgm */
+    return ERROR;
+
+  SeqPvShowBySP (pSP,0);
+
+  return OK;
+}
+
+/*
+ ************************************************************************
+ ************************************************************************
+ * Local-variable access functions
+ ************************************************************************
+ ************************************************************************
+ */
+
+/*----------------------------------------------------------------
+ * seqVarGetPtr -- return pointer to start of local-variable struct array, and var count
+ *----------------------------------------------------------------
+ */
+long epicsShareAPI seqVarGetPtrAndCount (epicsThreadId       tid,	/* thread id */
+					 SVAR		  **ppSV,	/* [returned] ptr to ptr to struct array */
+					 int                *pNV)	/* [returned] ptr to count */ 
+
+{
+  SPROG *pSP;
+  SVAR  *pSV;
+
+  pSP = seqFindProg(tid);
+  if (pSP == NULL) return ERROR;	/* state pgm not found */
+
+  pSV = pSP->pVarTbl;
+  if (pSV == NULL) return ERROR;	/* local variables not found(?) */
+
+  if (ppSV) *ppSV = pSV;
+  else return ERROR;			/* bad arg */
+
+  if (pNV ) *pNV = pSP->numVars;
+  else return ERROR;			/* bad arg */
+
+  return OK;
+}
+
+/*----------------------------------------------------------------
+ * seqVarGetInit -- initialize "get" iterator
+ *----------------------------------------------------------------
+ */
+long epicsShareAPI seqVarGetInit (epicsThreadId       tid)	/* thread id */
+{
+  SPROG *pSP;
+  SVAR  *pSV;
+
+  pSP = seqFindProg(tid);
+  if (pSP == NULL) return ERROR;	/* state pgm not found */
+
+  pSP->pVarGetPtr = NULL;		/* init... */
+  pSP-> varGetCnt = 0;
+
+  pSV = pSP->pVarTbl;
+  if (pSV == NULL) return ERROR;	/* local variables not found(?) */
+
+  pSP->pVarGetPtr = pSV;
+
+  return OK;
+}
+
+/*----------------------------------------------------------------
+ * seqVarGetNext -- get next var entry via iterator
+ *  ... returns NULL SVAR ptr if no more entries
+ *----------------------------------------------------------------
+ */
+long epicsShareAPI seqVarGetNext (epicsThreadId       tid,	/* thread id */
+				  SVAR		    **ppSV)	/* [returned] ptr to ptr to struct array */
+{
+  SPROG *pSP;
+  SVAR  *pSV;
+
+  *ppSV = NULL;				/* init to "done" */
+
+  pSP = seqFindProg(tid);
+  if (pSP == NULL) return ERROR;	/* state pgm not found */
+
+  pSV = pSP->pVarTbl;
+  if (pSV == NULL) return ERROR;	/* local variables not found(?) */
+
+  if (pSP->varGetCnt < pSP->numVars) {
+    *ppSV = pSP->pVarGetPtr;
+    pSP->pVarGetPtr++;
+    pSP->varGetCnt++;
+  }
+
+  return OK;
+}
+
+/*----------------------------------------------------------------
+ * seqVarFind -- return pointer to single local-variable struct, by thread id & var name
+ *----------------------------------------------------------------
+ */
+LOCAL long epicsShareAPI seqVarFind (epicsThreadId       tid,		/* thread id */
+				     char               *varName,	/* var name */
+				     SVAR	       **ppSV)		/* [returned] ptr to ptr to struct */
+{
+  SPROG *pSP;
+  SVAR  *pSV;
+  int n;
+
+  pSP = seqFindProg(tid);
+  if (pSP == NULL) return ERROR;	/* state pgm not found */
+
+  pSV = pSP->pVarTbl;
+  if (pSV == NULL) return ERROR;	/* local variables not found (?) */
+
+  for (n = 0; n < pSP->numVars; n++, pSV++) {
+    if (!strcmp(varName, pSV->name)) {
+      if (ppSV  ) *ppSV  = pSV;		/* return seqVar ptr */
+      return OK;
+    }
+  }
+
+  return ERROR;				/* var not found */
+}
+
+/*----------------------------------------------------------------
+ * seqVarGetValueString -- convert local-var value to string
+ *  ... passed-in string buffer must be large enough to handle anticipated result
+ *  ... array variables will return just the first element
+ *  ... if 2nd arg is NULL, var value is printed out instead
+ *  ... local buf is STATIC ... need to be thread-safe?
+ *----------------------------------------------------------------
+ */
+LOCAL long seqVarGetValueString(SVAR *pSV,
+				char *pStr)	/* [returned] */
+{
+  void *pVar;
+  char *pBuf = pStr;
+
+  static char sbuf[MAX_STRING_SIZE];	/* local buf, for print case */
+
+  if (pSV  == NULL) return ERROR;
+  if (pStr == NULL) pBuf = &sbuf[0];
+
+  pVar = (void*)pSV->addr;
+
+  if (pSV->class == VC_POINTER) {	/* redirect, if pointer */
+    pVar = *(void**)pVar;
+  }
+
+  if (pVar == NULL) {
+    strcpy(pBuf,"(NULL)");
+  }
+  else {
+
+    switch (pSV->type) {	/* convert by type ("V_" defs are in ../snc/parse.h) */
+
+    case V_CHAR:
+    case V_UCHAR:
+      if (VC_POINTER == pSV->class) {		/* show first 3 bytes if it's a char pointer */
+	int i;
+	*pBuf = 0;
+	for (i=0; i<3; i++) {
+	  sprintf (&pBuf[strlen(pBuf)], "%d ",
+		   (pSV->type == V_CHAR) ? *((char*)pVar+i) : *((unsigned char*)pVar+i));
+	}
+	strcat(pBuf,"...");
+      }
+      else {					/* just a single char */
+	sprintf (pBuf, "%d",
+		 (pSV->type == V_CHAR) ? *(char*)pVar : *(unsigned char*)pVar);
+      }
+      break;
+
+    case V_SHORT:	sprintf (pBuf, "%d",    *(         short*) pVar);	break;
+    case V_USHORT:	sprintf (pBuf, "%u",    *(unsigned short*) pVar);	break;
+    case V_INT:		sprintf (pBuf, "%d",    *(           int*) pVar);	break;
+    case V_UINT:	sprintf (pBuf, "%u",    *(unsigned   int*) pVar);	break;
+    case V_LONG:	sprintf (pBuf, "%ld",   *(          long*) pVar);	break;
+    case V_ULONG:	sprintf (pBuf, "%lu",   *(unsigned  long*) pVar);	break;
+    case V_FLOAT:	sprintf (pBuf, "%.7g",  *(         float*) pVar);	break;
+    case V_DOUBLE:	sprintf (pBuf, "%.16g", *(        double*) pVar);	break;
+
+    case V_STRING:
+      strcpy (pBuf, pVar);
+      break;
+
+#if 0
+    case V_EVFLAG: ...?
+    case V_NONE: ...?
+#endif
+
+    default:
+      strcpy(pBuf,"");	/* unknown? */
+      break;
+    }
+  }
+
+  /* print to console if arg was null
+   */
+  if (pStr == NULL) printf("\"%s\"\n",pBuf);
+
+  return OK;
+}
+
+/*----------------------------------------------------------------
+ * seqVarPutValueString -- set local-var value from string
+ *  ... array variables will have just the first element set
+ *----------------------------------------------------------------
+ */
+LOCAL long seqVarPutValueString(SVAR *pSV,
+				char *pStr)
+{
+  void *pVar;
+
+  if (pSV  == NULL) return ERROR;
+  if (pStr == NULL) return ERROR;
+
+  pVar = (void*)pSV->addr;
+
+  if (pSV->class == VC_POINTER) {	/* redirect, if pointer */
+    pVar = *(void**)pVar;
+  }
+
+  if (pVar == NULL) {
+    return ERROR;		/* null ptr... doh! */
+  }
+
+  switch (pSV->type) {		/* convert by type ("V_" defs are in ../snc/parse.h) */
+
+  case V_CHAR:	 if (sscanf (pStr, "%c" , (          char*) pVar) != 1) return ERROR;	break;
+  case V_UCHAR:	 if (sscanf (pStr, "%c",  (unsigned  char*) pVar) != 1) return ERROR;	break;
+  case V_SHORT:	 if (sscanf (pStr, "%hd", (         short*) pVar) != 1) return ERROR;	break;
+  case V_USHORT: if (sscanf (pStr, "%hu", (unsigned short*) pVar) != 1) return ERROR;	break;
+  case V_INT:	 if (sscanf (pStr, "%d" , (           int*) pVar) != 1) return ERROR;	break;
+  case V_UINT:	 if (sscanf (pStr, "%u" , (unsigned   int*) pVar) != 1) return ERROR;	break;
+  case V_LONG:	 if (sscanf (pStr, "%ld", (          long*) pVar) != 1) return ERROR;	break;
+  case V_ULONG:	 if (sscanf (pStr, "%lu", (unsigned  long*) pVar) != 1) return ERROR;	break;
+  case V_FLOAT:	 if (sscanf (pStr, "%g" , (         float*) pVar) != 1) return ERROR;	break;
+  case V_DOUBLE: if (sscanf (pStr, "%lg", (        double*) pVar) != 1) return ERROR;	break;
+
+  case V_STRING:
+    strcpy (pStr, pVar);
+    break;
+
+#if 0
+  case V_EVFLAG: ...?
+  case V_NONE: ...?
+#endif
+
+  default:
+    strcpy(pStr,"");	/* unknown? */
+    break;
+  }
+
+  return OK;
+}
+
+/*----------------------------------------------------------------
+ * seqVarGet -- get single local-var value, as string
+ *----------------------------------------------------------------
+ */
+long epicsShareAPI seqVarGet (epicsThreadId       tid,		/* thread id */
+			      char               *varName,	/* var name */
+			      char               *pStr)		/* [returned] char buf for value */
+{
+  SVAR *pSV;
+  if (seqVarFind(tid, varName, &pSV) != OK) return ERROR;
+  return seqVarGetValueString(pSV,pStr);
+}
+
+/*----------------------------------------------------------------
+ * seqVarPut -- put single local-var value, as string
+ *----------------------------------------------------------------
+ */
+long epicsShareAPI seqVarPut (epicsThreadId       tid,		/* thread id */
+			      char               *varName,	/* var name */
+			      char               *pStr)		/* value */
+{
+  SVAR *pSV;
+  if (seqVarFind(tid, varName, &pSV) != OK) return ERROR;
+  return seqVarPutValueString(pSV,pStr);
+}
+
+/*----------------------------------------------------------------
+ * seqVarShow -- show (print) local var info
+ *  ... ("threadId" == 0) -> show all local var info for all threads
+ *  ... ("varName"  == 0) -> show all local var info for single thread
+ *  ... ("varName"  != 0) -> show single var value
+ *  ... local buf is STATIC ... need to be thread-safe?
+ *----------------------------------------------------------------
+ */
+
+/* local func */
+LOCAL void seqVarShowSP(SPROG *pSP, char *varName)
+{
+  static char sbuf[MAX_STRING_SIZE];
+
+  SVAR    *pSV = pSP->pVarTbl;
+  int      n;
+
+  if (varName == NULL) {	/* title only for multi-line output */
+    printf("\n");
+    printf("Program: %s\n",pSP->pProgName);
+/*
+tabs:	|	|	|	|	|	|	|	|	|	|	|
+  Name			Type    	[n][n]	Address	Value
+  -----------------------------------------------------------
+  namenamenamenamenamen	typetypet *    	[n][n]	addrad	val...
+*/
+    printf("  Name			Type    	[n][n]	Address	Value\n");
+    printf("  -----------------------------------------------------------\n");
+  }
+
+  for (n = 0; n < pSP->numVars; n++, pSV++) {
+
+    if (varName && strcmp(varName,pSV->name)) continue;
+
+    seqVarGetValueString(pSV,sbuf);
+
+    if (varName == NULL) {	/* full only for multi-line output */
+
+      printf("  %-21s",pSV->name);
+
+      printf("\t%s ",pSV->type_str);
+      if (pSV->class == VC_POINTER) printf("*");
+      printf("    \t");
+
+      if (pSV->length1 > 1) printf("[%d]", pSV->length1);
+      if (pSV->length2 > 1) printf("[%d]", pSV->length2);
+
+      printf("\t%-1p", pSV->addr);
+      printf("\t");
+    }
+
+    if (pSV->class == VC_POINTER) {
+      if (*(void**)(pSV->addr) == NULL) {
+	printf("(NULL)");
+      }
+      else {
+	printf("%p -> ",*(void**)(pSV->addr));	/* hope it's a good pointer! :O */
+	printf("\"%s\"",sbuf);
+      }
+    }
+    else {
+      printf("\"%s\"",sbuf);
+    }
+
+    printf("\n");
+  }
+}
+
+/* global func */
+long epicsShareAPI seqVarShow (epicsThreadId  tid,		/* thread id */
+			       char          *varName)		/* var name */
+{
+  SPROG *pSP;
+
+  if (tid == NULL) {			 /* show all progs */
+    seqTraverseProg(seqVarShowSP,0);
+    printf("\n");
+    return OK;
+  }
+
+  pSP = seqFindProg(tid);		/* show single prog & maybe var */
+  if (pSP == NULL) {
+    printf("  Prog not found!\n");
+    return ERROR;
+  }
+
+  seqVarShowSP(pSP,varName);
+  printf("\n");
+  return OK;
+}
+
+/****************************************************************/
+/****************************************************************/
+/****************************************************************/
+
+/* end */
Index: src/seq/seqPvt.h
===================================================================
--- src/seq/seqPvt.h	(revision 194)
+++ src/seq/seqPvt.h	(working copy)
@@ -53,6 +53,9 @@
  * 06mar00,wfl	Added function prototypes for global routines.
  * 31mar00,wfl	Made caSemId a mutex; added seqFindProgXxx() prototypes.
  * 22mar01,mrk	mover to seqPvt.h from seq.h for stupid windows problem
+ * 06Jun07,liu  dbgStRunState, dbgStateJump and dbgStepTrig are added in SSCB,
+                they are used for SNL debugger. 
+ * 21dec07,liu  Support accessing local variables.
  */
 #ifndef	INCLseqPvth
 #define	INCLseqPvth
@@ -138,7 +141,9 @@
 };
 typedef	struct	state_info_block STATE;
 
-#define	MAX_NDELAY	20	/* max # delays allowed in each SS */
+#define	MAX_NDELAY		 20	/* max # delays allowed in each SS */
+#define SEQ_DBG_RUNSTATE_RUNNING 0
+#define SEQ_DBG_RUNSTATE_STOPPED 1
 /* Structure to hold information about a State Set */
 struct	state_set_control_block
 {
@@ -167,7 +172,14 @@
 	epicsBoolean	delayExpired[MAX_NDELAY]; /* TRUE if delay expired */
 	double		timeEntered;	/* time that a state was entered */
 	struct state_program *sprog;	/* ptr back to state program block */
+
+	/* debugger stuff...   */
+        int             dbgRunState;
+        int             dbgStateJump;
+        int             dbgStepTrig;
+        int             dbgStopBySeqStop;
 };
+
 typedef	struct	state_set_control_block SSCB;
 
 /* Macro table */
@@ -187,6 +199,13 @@
 	unsigned int	stackSize;	/* stack size */
 	epicsMutexId	caSemId;	/* mutex for locking CA events */
 	CHAN		*pChan;		/* table of channels */
+
+  /* local variables */
+        SVAR            *pVarTbl;       /* table of local variables */
+        SVAR            *pVarGetPtr;    /* iterator "get" ptr */
+        int             varGetCnt;      /* iterator "get" count */
+        long            numVars;        /* number of local variables */
+
 	long		numChans;	/* number of db channels, incl. unass */
 	long		assignCount;	/* number of db channels assigned */
 	long		connCount;	/* number of channels connected */
Index: src/seq/seqCommands.c
===================================================================
--- src/seq/seqCommands.c	(revision 194)
+++ src/seq/seqCommands.c	(working copy)
@@ -26,7 +26,10 @@
 #define epicsExportSharedSymbols
 #include "seq.h"
 
+long epicsShareAPI seqVarShow (epicsThreadId  tid,
+			       char          *varName);
 
+
 struct sequencerProgram {
     struct seqProgram *prog;
     struct sequencerProgram *next;
@@ -50,9 +53,9 @@
 
 /*
  * Find a thread by name or ID number
+ *  ... renamed to "seq_" and removed "static" [2006-04-28 lazmo@slac]
  */
-static epicsThreadId
-findThread (const char *name)
+epicsThreadId seq_findThread (const char *name)
 {
     epicsThreadId id;
     char *term;
@@ -60,6 +63,7 @@
     id = (epicsThreadId)strtoul (name, &term, 16);
     if ((term != name) && (*term == '\0'))
         return id;
+
     id = epicsThreadGetId (name);
     if (id)
         return id;
@@ -106,7 +110,7 @@
 
     if (name == NULL)
         seqShow (NULL);
-    else if ((id = findThread (name)) != NULL)
+    else if ((id = seq_findThread (name)) != NULL)
         seqShow (id);
 }
 
@@ -119,7 +123,7 @@
     epicsThreadId id;
     char *name = args[0].sval;
 
-    if ((name != NULL) && ((id = findThread (name)) != NULL))
+    if ((name != NULL) && ((id = seq_findThread (name)) != NULL))
         seqQueueShow (id);
 }
 
@@ -132,7 +136,7 @@
     epicsThreadId id;
     char *name = args[0].sval;
 
-    if ((name != NULL) && ((id = findThread (name)) != NULL))
+    if ((name != NULL) && ((id = seq_findThread (name)) != NULL))
         seqStop (id);
 }
 
@@ -147,7 +151,7 @@
     char *name = args[0].sval;
     char *chan = args[1].sval;
 
-    if ((name != NULL) && ((id = findThread (name)) != NULL))
+    if ((name != NULL) && ((id = seq_findThread (name)) != NULL))
         seqChanShow (id, chan);
 }
 
@@ -160,6 +164,131 @@
     seqcar (args[0].ival);
 }
 
+/*----------------------------------------------------------------
+ * seqVarShow (threadid, varname)
+ *  ... ("threadId" == 0) -> show all local var info for all threads (progs, actually)
+ *  ... ("varName"  == 0) -> show all local var info for single thread
+ *  ... ("varName"  != 0) -> show single var info
+ *----------------------------------------------------------------
+ */
+static const iocshArg         seqVarShowArg0    = { "sequencer",iocshArgString };
+static const iocshArg         seqVarShowArg1    = { "varName"  ,iocshArgString };
+
+static const iocshArg * const seqVarShowArgs[2] = { &seqVarShowArg0,
+						    &seqVarShowArg1 };
+
+static const iocshFuncDef     seqVarShowFuncDef = { "seqVarShow",2,seqVarShowArgs };
+
+static void seqVarShowCallFunc(const iocshArgBuf *args)
+{
+  epicsThreadId id;
+  char *thdName = args[0].sval;
+  char *varName = args[1].sval;
+
+  if (thdName == NULL) {
+    seqVarShow (NULL,NULL);
+  }
+  else {
+    if ((id = seq_findThread (thdName)) != NULL) {
+      seqVarShow (id,varName);
+    }
+  }
+}
+
+/*----------------------------------------------------------------
+ * seqVarGet (thread, varname, NULL)
+ *  ... last arg is forced to NULL for iocShell version (ie. no return via pointer)
+ *----------------------------------------------------------------
+ */
+static const iocshArg         seqVarGetArg0    = { "sequencer",iocshArgString };
+static const iocshArg         seqVarGetArg1    = { "varName"  ,iocshArgString };
+
+static const iocshArg * const seqVarGetArgs[2] = { &seqVarGetArg0,
+						   &seqVarGetArg1 };
+
+static const iocshFuncDef     seqVarGetFuncDef = { "seqVarGet",2,seqVarGetArgs };
+
+static void seqVarGetCallFunc(const iocshArgBuf *args)
+{
+  epicsThreadId id;
+  char *thdName = args[0].sval;
+  char *varName = args[1].sval;
+
+  if (thdName != NULL) {
+    if ((id = seq_findThread (thdName)) != NULL) {
+      seqVarGet (id,varName,NULL);
+    }
+  }
+}
+
+/*----------------------------------------------------------------
+ * seqVarPut (thread, varname, newval)
+ *----------------------------------------------------------------
+ */
+static const iocshArg         seqVarPutArg0    = { "sequencer",iocshArgString };
+static const iocshArg         seqVarPutArg1    = { "varName"  ,iocshArgString };
+static const iocshArg         seqVarPutArg2    = { "newVal"   ,iocshArgString };
+
+static const iocshArg * const seqVarPutArgs[3] = { &seqVarPutArg0,
+						   &seqVarPutArg1,
+						   &seqVarPutArg2 };
+
+static const iocshFuncDef     seqVarPutFuncDef = { "seqVarPut",3,seqVarPutArgs };
+
+static void seqVarPutCallFunc(const iocshArgBuf *args)
+{
+  epicsThreadId id;
+  char *thdName = args[0].sval;
+  char *varName = args[1].sval;
+  char *newVal  = args[2].sval;
+
+  if (thdName != NULL) {
+    if ((id = seq_findThread (thdName)) != NULL) {
+      seqVarPut (id,varName,newVal);
+    }
+  }
+}
+
+/*----------------------------------------------------------------
+ * seqPvShow (thread)
+ *  ... accepts missing arg as "0"
+ *----------------------------------------------------------------
+ */
+static const iocshArg         seqPvShowArg0    = { "sequencer",iocshArgString };
+
+static const iocshArg * const seqPvShowArgs[1] = { &seqPvShowArg0 };
+
+static const iocshFuncDef     seqPvShowFuncDef = { "seqPvShow",1,seqPvShowArgs };
+
+static void seqPvShowCallFunc(const iocshArgBuf *args)
+{
+  epicsThreadId id = 0;
+  char *thdName = args[0].sval;
+
+  if (thdName != NULL) {
+    if ((id = seq_findThread (thdName)) == NULL) id = 0;
+  }
+
+  seqPvShow (id);
+}
+
+/* SEQ_Run */
+static const iocshFuncDef SEQ_RunFuncDef = {"SEQ_Run",0,NULL};
+static void SEQ_RunCallFunc(const iocshArgBuf *args)
+{
+    SEQ_Run();
+}
+
+/* SEQ_Pause */
+static const iocshFuncDef SEQ_PauseFuncDef = {"SEQ_Pause",0,NULL};
+static void SEQ_PauseCallFunc(const iocshArgBuf *args)
+{
+    SEQ_Pause();
+}
+
+/*----------------------------------------------------------------*/
+/*----------------------------------------------------------------*/
+
 /*
  * This routine is called before multitasking has started, so there's
  * no race condition in the test/set of firstTime.
@@ -169,11 +298,23 @@
     static int firstTime = 1;
     if (firstTime) {
         firstTime = 0;
-        iocshRegister(&seqFuncDef,seqCallFunc);
-        iocshRegister(&seqShowFuncDef,seqShowCallFunc);
-        iocshRegister(&seqQueueShowFuncDef,seqQueueShowCallFunc);
-        iocshRegister(&seqStopFuncDef,seqStopCallFunc);
-        iocshRegister(&seqChanShowFuncDef,seqChanShowCallFunc);
-        iocshRegister(&seqcarFuncDef,seqcarCallFunc);
+        iocshRegister ( &seqFuncDef          , seqCallFunc          );
+        iocshRegister ( &seqShowFuncDef      , seqShowCallFunc      );
+        iocshRegister ( &seqQueueShowFuncDef , seqQueueShowCallFunc );
+        iocshRegister ( &seqStopFuncDef      , seqStopCallFunc      );
+        iocshRegister ( &seqChanShowFuncDef  , seqChanShowCallFunc  );
+        iocshRegister ( &seqcarFuncDef       , seqcarCallFunc       );
+        iocshRegister ( &seqVarShowFuncDef   , seqVarShowCallFunc   );
+        iocshRegister ( &seqVarGetFuncDef    , seqVarGetCallFunc    );
+        iocshRegister ( &seqVarPutFuncDef    , seqVarPutCallFunc    );
+        iocshRegister ( &seqPvShowFuncDef    , seqPvShowCallFunc    );
+        iocshRegister ( &SEQ_RunFuncDef	     , SEQ_RunCallFunc      );
+        iocshRegister ( &SEQ_PauseFuncDef    , SEQ_PauseCallFunc    );
+
     }
 }
+
+/*----------------------------------------------------------------*/
+/*----------------------------------------------------------------*/
+
+/* end */
Index: src/seq/seq_task.c
===================================================================
--- src/seq/seq_task.c	(revision 194)
+++ src/seq/seq_task.c	(working copy)
@@ -43,6 +43,9 @@
 29feb00,wfl	Supported new OSI (and errlogPrintf); new use of DEBUG macro;
 		implemented new sequencer deletion method.
 31mar00,wfl	Changed caSemId to be a mutex.
+16jan08,liu     Support accessing local variabls.
+22oct08,liu     Add SEQ_Run() and SEQ_Pause() to restart or freeze sequencer for
+		supporting ioc redundancy.
 ***************************************************************************/
 #define	DEBUG nothing /* "nothing", "printf", "errlogPrintf" etc. */
 
@@ -54,6 +57,10 @@
 #define epicsExportSharedSymbols
 #include	"seq.h"
 
+/* Task Control */
+enum ctl {ctlRun, ctlPause};
+volatile enum ctl SEQ_Ctl = ctlRun;
+
 /* Used to disable debug output */
 void nothing(const char *format,...) {}
 
@@ -152,6 +159,12 @@
 	pSS->nextState = -1;
 	pSS->prevState = -1;
 
+        /* debugger stuff */
+        pSS->dbgRunState 	= SEQ_DBG_RUNSTATE_RUNNING; 
+        pSS->dbgStateJump 	= FALSE;                    
+        pSS->dbgStepTrig 	= FALSE;                    
+        pSS->dbgStopBySeqStop 	= FALSE;                   
+
 	/* Use the event mask for this state */
 	pSS->pMask = (pST->pEventMask);
 	nWords = (pSP->numEvents + NBITS - 1) / NBITS;
@@ -186,16 +199,37 @@
 		/* Loop until an event is triggered, i.e. when() returns TRUE
 		 */
 		do {
+
 			/* Wake up on PV event, event flag, or expired delay */
 			delay = seq_getTimeout(pSS); /* min. delay from list */
-			if (delay > 0.0)
-				(void) epicsEventWaitWithTimeout(pSS->syncSemId,
-							    delay);
+			if( delay > 0.0 )
+		          (void) epicsEventWaitWithTimeout(pSS->syncSemId, delay);
 
 			/* Check whether we have been asked to exit */
 			if (epicsEventTryWait(pSS->death1SemId) ==
 							epicsEventWaitOK) goto exit;
 
+			/* sleep here when ss is suspended.                           */
+                	/* single step runnig is supported only when ss is suspended. */
+                	while(pSS->dbgRunState != SEQ_DBG_RUNSTATE_RUNNING)
+                	{
+                   		epicsThreadSleep(0.1); 
+
+                   		if(pSS->dbgStepTrig == TRUE)
+                   		{
+                     			pSS->dbgStepTrig = FALSE;
+                     			break;
+                   		}
+                	}
+
+                	/* update pST. It is used for state jumping */
+                	while(pSS->dbgStateJump == TRUE)
+                	{
+                   		epicsThreadSleep(0.1); 
+                   		pSS->dbgStateJump = FALSE;
+                	}
+                	pST = pSS->pStates + pSS->currentState;
+
 			/* Call the event function to check for an event
 			 * trigger. The statement inside the when() statement
 			 * is executed. Note, we lock out PV events while doing 
@@ -241,6 +275,25 @@
 		pSS->prevState = pSS->currentState;
 		pSS->currentState = pSS->nextState;
 		pST = pSS->pStates + pSS->currentState;
+
+
+                /* (1)when IOC is slave, sleep here and update pST.                    */
+                /* (2)support state program reloading. when seqStop(tid) is executed,  */
+                /*    it set pSS->dbgStopBySeqStop to TRUE which awakes ss_entry(pSS). */
+
+		while( SEQ_Ctl == ctlPause ) 
+                {
+		   if( pSS->dbgStopBySeqStop == TRUE)
+                   {
+                     pSS->dbgStopBySeqStop = FALSE;
+                     break;  
+                   }
+  
+                   epicsThreadSleep(0.1); 
+		   pSS->prevState = pSS->currentState;
+		   pSS->currentState = pSS->nextState;
+		   pST = pSS->pStates + pSS->currentState;
+                }
 	}
 
 	/* Thread exit has been requested */
@@ -450,6 +503,7 @@
 		/* Ask the thread to exit */
 		DEBUG("      tid=%p\n", pSS->threadId);
 		epicsEventSignal(pSS->death1SemId);
+		pSS->dbgStopBySeqStop = TRUE; /* awake the sleeping state set for SLAVE */ 
 	}
 
 	/* Wake up all state-sets */
@@ -590,6 +644,9 @@
 	/* Free SSCBs */
 	free(pSP->pSS);
 
+	/* Free local-variable table, if allocated */
+	if (pSP->options & OPT_REENT) free (pSP->pVarTbl);
+
 	/* Free SPROG */
 	free(pSP);
 }
@@ -599,34 +656,49 @@
  */
 void *seqAuxThread(void *tArgs)
 {
-	AUXARGS		*pArgs = (AUXARGS *)tArgs;
-	char		*pPvSysName = pArgs->pPvSysName;
-	long		debug = pArgs->debug;
-	int		status;
-	extern		epicsThreadId seqAuxThreadId;
+    AUXARGS	*pArgs = (AUXARGS *)tArgs;
+    char	*pPvSysName = pArgs->pPvSysName;
+    long	debug = pArgs->debug;
+    int		status;
+    extern	epicsThreadId seqAuxThreadId;
 
-	/* Register this thread with the EPICS watchdog */
-	taskwdInsert(epicsThreadGetIdSelf(),(SEQVOIDFUNCPTR)0, (void *)0);
+    /* Register this thread with the EPICS watchdog */
+    taskwdInsert(epicsThreadGetIdSelf(),(SEQVOIDFUNCPTR)0, (void *)0);
 
-	/* All state program threads will use a common PV context (subtract
-	   1 from debug level for PV debugging) */
-	status = pvSysCreate(pPvSysName, debug>0?debug-1:0, &pvSys);
-        if (status != pvStatOK)
-        {
-                errlogPrintf("seqAuxThread: pvSysCreate() %s failure: %s\n",
-                            pPvSysName, pvSysGetMess(pvSys));
-		seqAuxThreadId = (epicsThreadId) -1;
-                return NULL;
-        }
-	seqAuxThreadId = epicsThreadGetIdSelf(); /* AFTER pvSysCreate() */
+    /* All state program threads will use a common PV context (subtract
+       1 from debug level for PV debugging) */
+    status = pvSysCreate(pPvSysName, debug>0?debug-1:0, &pvSys);
+    if (status != pvStatOK)
+    {
+        errlogPrintf("seqAuxThread: pvSysCreate() %s failure: %s\n",
+                      pPvSysName, pvSysGetMess(pvSys));
+	seqAuxThreadId = (epicsThreadId) -1;
+               return NULL;
+    }
+    seqAuxThreadId = epicsThreadGetIdSelf(); /* AFTER pvSysCreate() */
 
-	/* This loop allows for check for connect/disconnect on PVs */
-	for (;;)
-	{
-		pvSysPend(pvSys, 10.0, TRUE); /* returns every 10 sec. */
-	}
+    /* This loop allows for check for connect/disconnect on PVs */
+    for (;;)
+    {
+	pvSysPend(pvSys, 10.0, TRUE); /* returns every 10 sec. */
+    }
 
-	/* Return no result (never exit in any case) */
-	return NULL;
+    /* Return no result (never exit in any case) */
+    /*return NULL; */
 }
 
+/* SEQ_Run()/SEQ_Pause() to restart/freeze sequencer for supporting ioc redundancy. */
+void SEQ_Run(void)
+{
+    SEQ_Ctl = ctlRun;
+    errlogPrintf("SEQ_Run: sequencer is running\n");
+    return;
+}
+
+void SEQ_Pause(void)
+{
+    SEQ_Ctl = ctlPause;
+    errlogPrintf("SEQ_Pause: sequencer is paused\n");
+    return;
+}
+
Index: src/seq/Makefile
===================================================================
--- src/seq/Makefile	(revision 194)
+++ src/seq/Makefile	(working copy)
@@ -5,7 +5,7 @@
 #  ADD MACRO DEFINITIONS AFTER THIS LINE
 
 #  Include files
-INC = seqCom.h
+INC = seq.h seqCom.h seqPvt.h
 
 #  Internal debug control
 #USR_CPPFLAGS = -DDEBUG=TRUE
Index: src/seq/seqCom.h
===================================================================
--- src/seq/seqCom.h	(revision 194)
+++ src/seq/seqCom.h	(working copy)
@@ -34,7 +34,11 @@
  * 22sep99,grw  Supported entry and exit actions; supported state options.
  * 29feb00,wfl	Converted to new OSI; new magic number.
  * 31mar00,wfl	Included pvAlarm.h; added entry function; new magic number.
+ * 20dec07,liu	Add struct seqVar to hold information about a local variable.
+ * 22oct08,liu	Add functions SEQ_Run() and SEQ_Pause() to restart or freeze sequencer
+ *		for supportng ioc redundancy.
  */
+
 #ifndef	INCLseqComh
 #define	INCLseqComh
 
@@ -100,7 +104,8 @@
  * from the start of a structure */
 #define OFFSET(structure, member) ((long) &(((structure *) 0) -> member))
 
-/* Structure to hold information about database channels */
+
+/* Structure to hold information about database channels */
 struct	seqChan
 {
 	char		*dbAsName;	/* assigned channel name */
@@ -117,6 +122,20 @@
 	int		queueIndex;	/* syncQ queue index */
 };
 
+/* Structure to hold information about one local variable */
+struct	seqVar
+{
+	char		*name;		/* name (string) */
+	int		type;		/* type (enum) */
+	char		*type_str;	/* type (string) */
+	int		class;		/* class (enum) */
+	int		length1;	/* 1st array dimension (1 if none) */
+	int		length2;	/* 2nd array dimension (1 if none) */
+	char		*init;		/* initial value (string) */
+	char		*addr;		/* ptr to var (reentrant case is resolved at runtime) */
+};
+typedef struct seqVar SVAR;
+
 /* Structure to hold information about a state */
 struct	seqState
 {
@@ -146,6 +165,8 @@
 	char		*pProgName;	/* program name (for debugging) */
 	struct seqChan	*pChan;		/* table of channels */
 	long		numChans;	/* number of db channels */
+	SVAR		*pVarTbl;	/* table of local vars */
+	long		numVars;	/* number of local vars */
 	struct seqSS	*pSS;		/* array of state set info structs */
 	long		numSS;		/* number of state sets */
 	long		varSize;	/* # bytes in user variable area */
@@ -212,7 +233,15 @@
     seq(struct seqProgram *, char *, unsigned int);
 epicsShareFunc struct state_program *epicsShareAPI seqFindProgByName (char *);
 
+epicsShareFunc long epicsShareAPI seqVarGet (epicsThreadId, char *, char *);
+epicsShareFunc long epicsShareAPI seqVarPut (epicsThreadId, char *, char *);
 
+epicsShareFunc long epicsShareAPI seqPvShow (epicsThreadId);
+
+/* restart or freeze sequencer for supportng ioc redundancy */
+epicsShareFunc void SEQ_Run(void);
+epicsShareFunc void SEQ_Pause(void);
+
 #ifdef __cplusplus
 } /* extern "C" */
 #endif
Index: src/seq/seq_main.c
===================================================================
--- src/seq/seq_main.c	(revision 194)
+++ src/seq/seq_main.c	(working copy)
@@ -6,7 +6,7 @@
 
  	seq_main.c,v 1.2 1995/06/27 15:25:58 wright Exp
 
-	DESCRIPTION: Seq() initiates a sequence as a group of cooperating
+	DESCRIPTION: Seq() initiates a sequencer as a group of cooperating
 	tasks.  An optional string parameter specifies the values for
 	macros.  The PV context and auxiliary thread are shared by all state
 	programs.
@@ -62,6 +62,7 @@
 29feb00,wfl	Supported new OSI (and errlogPrintf); got rid of remaining
 		OS-dependencies; improved command-line interpreter
 31mar00,wfl	Supported 'shell' macro to run shell; made caSemId a mutex
+20dec07,liu	Support accessing local variabls.
 ***************************************************************************/
 /*#define	DEBUG	1*/
 
@@ -89,6 +90,7 @@
 LOCAL	void init_sprog(struct seqProgram *, SPROG *);
 LOCAL	void init_sscb(struct seqProgram *, SPROG *);
 LOCAL	void init_chan(struct seqProgram *, SPROG *);
+LOCAL	void init_locvar(struct seqProgram *, SPROG *);
 LOCAL	void init_mac(SPROG *);
 
 LOCAL	void seq_logInit(SPROG *);
@@ -102,6 +104,8 @@
 /*	Auxiliary sequencer thread id; used to share PV context. */
 epicsThreadId seqAuxThreadId = (epicsThreadId) 0;
 
+/********************************************************************/
+
 /*
  * seq: User-callable routine to initiate a state program.
  * Usage:  seq(<pSP>, <macros string>, <stack size>)
@@ -135,9 +139,9 @@
 	/* Check for correct state program format */
 	if (pSeqProg->magic != MAGIC)
 	{	/* Oops */
-		errlogPrintf("Illegal magic number in state program.\n");
-		errlogPrintf(" - Possible mismatch between SNC & SEQ "
-			"versions\n");
+		errlogPrintf("Illegal magic number in state program!\n");
+		errlogPrintf(" - Possible mismatch between SNC & SEQ versions\n");
+		errlogPrintf(" - snc:%ld seq:%d\n",pSeqProg->magic,MAGIC);
 		errlogPrintf(" - Re-compile your program?\n");
 		epicsThreadSleep( 1.0 );	/* let error messages get printed */
 		return 0;
@@ -213,10 +217,11 @@
 	}
 
 	/* Spawn the initial sequencer thread */
+
 #ifdef	DEBUG
-	printf("Spawning thread %s, stackSize=%d\n", pThreadName,
-		pSP->stackSize);
+	printf("Spawning thread %s, stackSize=%d\n",pThreadName,pSP->stackSize);
 #endif	/*DEBUG*/
+
 	/* Specify thread priority */
 	pSP->threadPriority = THREAD_PRIORITY;
 	pValue = seqMacValGet(pSP->pMacros, "priority");
@@ -235,7 +240,10 @@
 
 	return tid;
 }
-/* seqInitTables - initialize sequencer tables */
+
+/********************************************************************/
+
+/* seqInitTables - initialize sequencer tables */
 LOCAL SPROG *seqInitTables(pSeqProg)
 struct seqProgram	*pSeqProg;
 {
@@ -255,10 +263,15 @@
 	/* Initialize the macro table */
 	init_mac(pSP);
 
+	/* Initialize local variable table */
+	init_locvar(pSeqProg, pSP);
 
 	return pSP;
 }
-/*
+
+/********************************************************************/
+
+/*
  * Copy data from seqCom.h structures into this thread's dynamic structures
  * as defined in seq.h.
  */
@@ -269,14 +282,16 @@
 	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->entryFunc = (ENTRY_FUNC)pSeqProg->entryFunc;
-	pSP->exitFunc = (EXIT_FUNC)pSeqProg->exitFunc;
-	pSP->varSize = pSeqProg->varSize;
+	pSP->numSS      = pSeqProg->numSS;
+	pSP->numChans   = pSeqProg->numChans;
+	pSP->numVars    = pSeqProg->numVars;
+	pSP->numEvents  = pSeqProg->numEvents;
+	pSP->options    = pSeqProg->options;
+	pSP->pProgName  = pSeqProg->pProgName;
+	pSP->entryFunc  = (ENTRY_FUNC)pSeqProg->entryFunc;
+	pSP->exitFunc   = (EXIT_FUNC)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);
@@ -315,6 +330,9 @@
 
 	return;
 }
+
+/********************************************************************/
+
 /*
  * Initialize the state set control blocks
  */
@@ -355,8 +373,7 @@
 		/* Create a binary semaphore for synchronizing events in a SS */
 		pSS->syncSemId = epicsEventMustCreate(epicsEventEmpty);
 
-		/* Create binary semaphores for synchronous pvGet() and
-		   pvPut() */
+		/* Create binary semaphores for synchronous pvGet() and pvPut() */
 		pSS->getSemId = epicsEventMustCreate(epicsEventFull);
 		pSS->putSemId = epicsEventMustCreate(epicsEventFull);
 
@@ -374,14 +391,15 @@
 		for (nstates = 0; nstates < pSeqSS->numStates;
 					       nstates++, pState++, pSeqState++)
 		{
-			pState->pStateName = pSeqState->pStateName;
-			pState->actionFunc = (ACTION_FUNC)pSeqState->actionFunc;
-			pState->eventFunc = (EVENT_FUNC)pSeqState->eventFunc;
-			pState->delayFunc = (DELAY_FUNC)pSeqState->delayFunc;
-			pState->entryFunc = (ENTRY_FUNC)pSeqState->entryFunc;
-			pState->exitFunc = (EXIT_FUNC)pSeqState->exitFunc;
-			pState->pEventMask = pSeqState->pEventMask;
-                        pState->options = pSeqState->options;
+			pState->pStateName   =                 pSeqState->pStateName;
+			pState->actionFunc   = (ACTION_FUNC)   pSeqState->actionFunc;
+			pState->eventFunc    = (EVENT_FUNC)    pSeqState->eventFunc;
+			pState->delayFunc    = (DELAY_FUNC)    pSeqState->delayFunc;
+			pState->entryFunc    = (ENTRY_FUNC)    pSeqState->entryFunc;
+			pState->exitFunc     = (EXIT_FUNC)     pSeqState->exitFunc;
+
+			pState->pEventMask   = pSeqState->pEventMask;
+                        pState->options      = pSeqState->options;
 #ifdef	DEBUG
 		printf("init_sscb: State Name=%s, Event Mask=%p\n",
 			pState->pStateName, *pState->pEventMask);
@@ -394,7 +412,64 @@
 #endif	/*DEBUG*/
 	return;
 }
-
+
+/********************************************************************/
+
+/* init_one_chan -- called by init_chan (below)
+ */
+LOCAL void init_one_chan(SPROG		*pSP,
+			 CHAN		*pDB,
+			 struct seqChan	*pSeqChan)
+{
+#ifdef	DEBUG
+  printf("init_chan: pDB=%p\n", pDB);
+#endif	/*DEBUG*/
+
+  pDB->sprog    = pSP;
+  pDB->sset     = NULL;	/* set temporarily during get/put */
+
+  pDB->dbAsName = pSeqChan->dbAsName;
+  pDB->pVarName = pSeqChan->pVarName;
+  pDB->pVarType = pSeqChan->pVarType;
+  pDB->pVar     = pSeqChan->pVar;	/* offset for +r option */
+  pDB->count    = pSeqChan->count;
+  pDB->efId     = pSeqChan->efId;
+  pDB->monFlag  = pSeqChan->monFlag;
+  pDB->eventNum = pSeqChan->eventNum;
+  pDB->queued   = pSeqChan->queued;
+
+  pDB->maxQueueSize = pSeqChan->maxQueueSize ?
+                      pSeqChan->maxQueueSize : MAX_QUEUE_SIZE;
+
+  pDB->queueIndex = pSeqChan->queueIndex;
+
+  pDB->assigned = 0;
+
+  /* Latest error message (dynamically allocated) */
+  pDB->message = NULL;
+
+  /* Fill in get/put db types, element size, & access offset */
+  selectDBtype(pSeqChan->pVarType,
+	       &pDB->getType,
+	       &pDB->putType,
+	       &pDB->size,
+	       &pDB->dbOffset);
+
+  /* Reentrant option: Convert offset to addr of the user var. */
+  if ((pSP->options & OPT_REENT) != 0)
+    pDB->pVar += (ptrdiff_t)pSP->pVar;
+
+#ifdef	DEBUG
+  printf(" Assigned Name=%s, VarName=%s, VarType=%s, count=%d\n",
+	 pDB->dbAsName, pDB->pVarName, pDB->pVarType, pDB->count);
+  printf("   size=%d, dbOffset=%d\n", pDB->size, pDB->dbOffset);
+  printf("   efId=%d, monFlag=%d, eventNum=%d\n",
+	 pDB->efId, pDB->monFlag, pDB->eventNum);
+#endif	/*DEBUG*/
+}
+
+/*----------------------------------------------------------------*/
+
 /*
  * init_chan--Build the database channel structures.
  * Note:  Actual PV name is not filled in here. */
@@ -407,53 +482,53 @@
 	struct seqChan	*pSeqChan;
 
 	/* Allocate space for the CHAN structures */
-	pSP->pChan = (CHAN *)calloc(pSP->numChans, sizeof(CHAN));
-	pDB = pSP->pChan;
+	pDB = pSP->pChan = (CHAN *)calloc(pSP->numChans, sizeof(CHAN));
 
 	pSeqChan = pSeqProg->pChan;
-	for (nchan = 0; nchan < pSP->numChans; nchan++, pDB++, pSeqChan++)
+
+	for (nchan = 0;
+	     nchan < pSP->numChans;
+	     nchan++, pDB++, pSeqChan++)
 	{
-#ifdef	DEBUG
-		printf("init_chan: pDB=%p\n", pDB);
-#endif	/*DEBUG*/
-		pDB->sprog = pSP;
-		pDB->sset = NULL;	/* set temporarily during get/put */
-		pDB->dbAsName = pSeqChan->dbAsName;
-		pDB->pVarName = pSeqChan->pVarName;
-		pDB->pVarType = pSeqChan->pVarType;
-		pDB->pVar = pSeqChan->pVar;	/* offset for +r option */
-		pDB->count = pSeqChan->count;
-		pDB->efId = pSeqChan->efId;
-		pDB->monFlag = pSeqChan->monFlag;
-		pDB->eventNum = pSeqChan->eventNum;
-		pDB->queued = pSeqChan->queued;
-		pDB->maxQueueSize = pSeqChan->maxQueueSize ?
-				    pSeqChan->maxQueueSize : MAX_QUEUE_SIZE;
-		pDB->queueIndex = pSeqChan->queueIndex;
-		pDB->assigned = 0;
+	  init_one_chan(pSP,pDB,pSeqChan);
+	}
+}
 
-		/* Latest error message (dynamically allocated) */
-		pDB->message = NULL;
+/********************************************************************/
 
-		/* Fill in get/put db types, element size, & access offset */
-		selectDBtype(pSeqChan->pVarType, &pDB->getType,
-		 &pDB->putType, &pDB->size, &pDB->dbOffset);
+/* init_locvar -- init local-variable table
+ *  ... if re-entrant, make a copy of the seqVars struct, otherwise just 
+ *      use the compiled one
+ */
+LOCAL void init_locvar(pSeqProg, pSP)
+struct seqProgram *pSeqProg;	/* compiled struct */
+SPROG             *pSP;		/* dynamic struct */
+{
+  if (pSP->options & OPT_REENT) {
 
-		/* Reentrant option: Convert offset to addr of the user var. */
-		if ((pSP->options & OPT_REENT) != 0)
-			pDB->pVar += (ptrdiff_t)pSP->pVar;
-#ifdef	DEBUG
-		printf(" Assigned Name=%s, VarName=%s, VarType=%s, "
-			"count=%d\n", pDB->dbAsName, pDB->pVarName,
-			pDB->pVarType, pDB->count);
-		printf("   size=%d, dbOffset=%d\n", pDB->size,
-			pDB->dbOffset);
-		printf("   efId=%d, monFlag=%d, eventNum=%d\n",
-			pDB->efId, pDB->monFlag, pDB->eventNum);
-#endif	/*DEBUG*/
-	}
+    SVAR *pVarTbl = (SVAR*)calloc(pSP->numVars, sizeof(SVAR));
+    int n;
+
+    pSP->pVarTbl = pVarTbl;				/* ptr to thread-specific struct */
+
+    /* copy compiled struct to thread-local version */
+    memcpy(pSP->pVarTbl, pSeqProg->pVarTbl, pSP->numVars * sizeof(SVAR));
+
+    /* resolve address of each local var */
+    for (n = 0;
+	 n < pSP->numVars;
+	 n++, pVarTbl++)
+    {
+      pVarTbl->addr += (ptrdiff_t)pSP->pVar;	/* add start of var block to offset */
+    }
+  }
+  else {		/* not reentrant; use compiled struct w/static addresses */
+    pSP->pVarTbl = pSeqProg->pVarTbl;
+  }
 }
 
+/********************************************************************/
+
 /* 
  * init_mac - initialize the macro table.
  */
@@ -474,7 +549,10 @@
 		pMac->pValue = NULL;
 	}
 }	
-/*
+
+/********************************************************************/
+
+/*
  * Evaluate channel names by macro substitution.
  */
 #define		MACRO_STR_LEN	(MAX_STRING_SIZE+1)
@@ -495,7 +573,10 @@
 #endif	/*DEBUG*/
 	}
 }
-/*
+
+/********************************************************************/
+
+/*
  * selectDBtype -- returns types for DB put/get, element size, and db access
  * offset based on user variable type.
  * Mapping is determined by the following typeMap[] array.
@@ -568,6 +649,8 @@
 	}
 };
 
+/********************************************************************/
+
 LOCAL void selectDBtype(pUserType, pGetType, pPutType, pSize, pOffset)
 char		*pUserType;
 short		*pGetType, *pPutType, *pSize, *pOffset;
@@ -589,7 +672,10 @@
 
 	return;
 }
-/*
+
+/********************************************************************/
+
+/*
  * seq_logInit() - Initialize logging.
  * If "logfile" is not specified, then we log to standard output.
  */
@@ -622,7 +708,10 @@
 		}
 	}
 }
-/*
+
+/********************************************************************/
+
+/*
  * seq_logv
  * Log a message to the console or a file with thread name, date, & time of day.
  * The format looks like "mythread 12/13/93 10:07:43: Hello world!".
@@ -678,7 +767,10 @@
 	}
 	return OK;
 }
-/*
+
+/********************************************************************/
+
+/*
  * seq_seqLog() - State program interface to seq_log().
  * Does not require ptr to state program block.
  */
@@ -694,3 +786,6 @@
     va_end (args);
     return(rtn);
 }
+
+/********************************************************************/
+/********************************************************************/

Navigate by Date:
Prev: Re: errlogVprintf does not print messages to console Andrew Johnson
Index: 2002  2003  2004  2005  2006  2007  <20082009  2010  2011  2012  2013  2014  2015  2016  2017  2018  2019  2020  2021  2022  2023  2024 
Navigate by Thread:
Prev: Re: errlogVprintf does not print messages to console Andrew Johnson
Index: 2002  2003  2004  2005  2006  2007  <20082009  2010  2011  2012  2013  2014  2015  2016  2017  2018  2019  2020  2021  2022  2023  2024 
ANJ, 02 Feb 2012 Valid HTML 4.01! · Home · News · About · Base · Modules · Extensions · Distributions · Download ·
· Search · EPICS V4 · IRMIS · Talk · Bugs · Documents · Links · Licensing ·