/*************************************************************************\
* Copyright (c) 2002 The University of Chicago, as Operator of Argonne
*     National Laboratory.
* Copyright (c) 2002 The Regents of the University of California, as
*     Operator of Los Alamos National Laboratory.
* EPICS BASE Versions 3.13.7
* and higher are distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution. 
\*************************************************************************/

/*
 *      Original Author: Ned D. Arnold
 *      Date:            07-18-94
 *
 */

#define VERSION 3.13



#include	<vxWorks.h> 
#include	<types.h>
#include	<stdioLib.h>
#include	<lstLib.h>
#include	<string.h>
#include	<math.h>
#include	<tickLib.h>
#include	<semLib.h>
#include	<taskLib.h>
#include        <wdLib.h>
#include        <sysLib.h>

#include "dbDefs.h"
#include "epicsPrint.h"
#include	<alarm.h>
#include	<dbAccess.h>
#include	<dbEvent.h>
#include	<dbScan.h>
#include        <dbDefs.h>
#include	<dbFldTypes.h>
#include	<devSup.h>
#include	<errMdef.h>
#include	<recSup.h>
#include	<special.h>
#include	<choiceScan.h>
#define GEN_SIZE_OFFSET
#include	<scanRecord.h>
#undef  GEN_SIZE_OFFSET
#include	<callback.h>
#include	<taskwd.h>
#include        <drvTS.h>   /* also includes timers.h and tsDefs.h */

#include        <recDynLink.h>


/***************************
  Declare constants
***************************/
#define DEF_WF_SIZE	100
#define PVN_SIZE        40

#define POSITIONER      0
#define READBACK        1
#define DETECTOR        2
#define TRIGGER         3
#define FWD_LINK        4

#define NUM_POS         4
#define NUM_RDKS        4
#define NUM_DET         15
#define NUM_TRGS        2 
#define NUM_MISC        2  /* Before Scan PV, After Scan PV */
#define NUM_PVS         (NUM_POS + NUM_RDKS + NUM_DET + NUM_TRGS + NUM_MISC)
/* Determine total # of dynLinks (each positioner requires two: IN & OUT */
#define NUM_LINKS       (NUM_PVS + NUM_POS)

/* Define special type constants for first and last dynamic PV name */
#define FIRST_PV    REC_SC_P1
#define LAST_PV     REC_SC_AS

/* Predefine some index numbers */
#define P1_IN       0
#define P2_IN       1
#define P3_IN       2
#define P4_IN       3
#define R1_IN       (NUM_POS)
#define R2_IN       (NUM_POS + 1)
#define R3_IN       (NUM_POS + 2)
#define R4_IN       (NUM_POS + 3)
#define D1_IN       (NUM_POS + NUM_RDKS)

#define T1_OUT      (D1_IN + NUM_DET)
#define T2_OUT      (T1_OUT + 1)

#define BS_OUT      (T1_OUT + NUM_TRGS)
#define AS_OUT      (BS_OUT + 1)

/* Added four recDynLinks at the end of PV's for positioner outs */
#define P1_OUT      (NUM_PVS)
#define P2_OUT      (P1_OUT + 1)
#define P3_OUT      (P2_OUT + 1)
#define P4_OUT      (P3_OUT + 1)

#define MIN_MON         3 /* # of ticks between monitor postings. 3 = 50 ms */

#define PV_OK           REC_SCAN_DYNL_OK     /* from choiceScan.h */
#define PV_NC           REC_SCAN_DYNL_NC     /* from choiceScan.h */
#define NO_PV           REC_SCAN_DYNL_NO_PV  /* from choiceScan.h */

#define A_BUFFER        0
#define B_BUFFER        1

#define IDLE            0
#define INIT_SCAN       1 
#define BEFORE_SCAN     2 
#define MOVE_MOTORS     3
#define CHECK_MOTORS    4
#define TRIG_DETCTRS    5
#define READ_DETCTRS    6
#define AFTER_SCAN      7 
#define PREVIEW         8 
#define SCAN_PENDING    9 

#define CLEAR_MSG       0
#define CHECK_LIMITS    1
#define PREVIEW_SCAN    2 /* Preview the SCAN positions */
#define CLEAR_RECORD    3 /* Clear PV's, frzFlags, modes, abs/rel, etc */
#define CLEAR_POSITIONERS     4 /* Clear positioner PV's, frzFlags, modes, abs/rel, etc */

#define DBE_VAL_LOG     (DBE_VALUE | DBE_LOG)


/* Create RSET - Record Support Entry Table*/
#define report NULL
#define initialize NULL
static long init_record();
static long process();
static long special();
#define get_value NULL
static long cvt_dbaddr();
static long get_array_info();
static long put_array_info();
static long get_units();
static long get_precision();
static long get_enum_str();
static long get_enum_strs();
static long put_enum_str();
static long get_graphic_double();
static long get_control_double();
static long get_alarm_double(); 
 
struct rset scanRSET={
	RSETNUMBER,
	report,
	initialize,
	init_record,
	process,
	special,
	get_value,
	cvt_dbaddr,
	get_array_info,
	put_array_info,
	get_units,
	get_precision,
	get_enum_str,
	get_enum_strs,
	put_enum_str,
	get_graphic_double,
	get_control_double,
	get_alarm_double
};

typedef struct recDynLinkPvt {
    struct scanRecord *pscan;     /* pointer to scan record */
    unsigned short     linkIndex; /* specifies which dynamic link */
    unsigned short     linkType;  /* Positioner, Rdbk, Trig, Det */
    struct   dbAddr   *pAddr;     /* Pointer to dbAddr for local PV's */
    long               dbAddrNv;  /* Zero if dbNameToAddr succeeded */
    unsigned long      nelem;     /* # of elements for this PV  */
    unsigned short     ts;        /* if 1, use timestamp as value */
    short              fldOffset; /* For arrays, offset from pscan, used
                                     for get_array_info, cvt_dbaddr */
}recDynLinkPvt;

typedef struct dynLinkInfo {
    DBRunits
    DBRprecision
    DBRgrDouble
    DBRctrlDouble
    float        value;
}dynLinkInfo;

typedef struct posBuffers {
               double *pFill;
               double *pBufA;
               double *pBufB;
} posBuffers;
    
typedef struct detBuffers {
               float  *pFill;
               float  *pBufA;
               float  *pBufB;
               int     size;
               short   vector;
} detBuffers;
    

/* the following structure must match EXACTLY with the order and type of
   fields defined in scanRecord.h for each positioner (even including
   the "Created Pad"s  */

typedef struct posFields {
        double          p_pp;           /* P1 Previous Position */
        double          p_cv;           /* P1 Current Value */
        double          p_dv;           /* P1 Desired Value */
        double          p_lv;           /* P1 Last Value Posted */
        double          p_sp;           /* P1 Start Position */
        double          p_si;           /* P1 Step Increment */
        double          p_ep;           /* P1 End Position */
        double          p_cp;           /* P1 Center Position */
        double          p_wd;           /* P1 Scan Width */
        double          r_cv;           /* P1 Readback Value */
        double          r_lv;           /* P1 Rdbk Last Val Pst */
        double          r_dl;           /* P1 Readback Delta */
        double          p_hr;           /* P1 High Oper Range */
        double          p_lr;           /* P1 Low  Oper Range */
        double *         p_pa;          /* P1 Step Array */
        double *         p_ra;          /* P1 Readback Array */
        unsigned short  p_fs;           /* P1 Freeze Start Pos */
        unsigned short  p_fi;           /* P1 Freeze Step Inc */
        unsigned short  p_fe;           /* P1 Freeze End Pos */
        unsigned short  p_fc;           /* P1 Freeze Center Pos */
        unsigned short  p_fw;           /* P1 Freeze Width */
        unsigned short  p_sm;           /* P1 Step Mode */
        unsigned short  p_ar;           /* P1 Absolute/Relative */
        char            p_eu[16];               /* P1 Engineering Units */
        short           p_pr;           /* P1 Display Precision */
}posFields;
        
/* the following structure must match EXACTLY with the order and type of
   fields defined in scanRecord.h for each detector (even including
   the "Created Pad"s  */

typedef struct detFields {
        double          d_hr;           /* D1 High Oper Range */
        double          d_lr;           /* D1 Low  Oper Range */
        float *          d_da;          /* D1 Data Array */
        float           d_cv;           /* D1 Current Value */
        float           d_lv;           /* D1 Last Value Posted */
        unsigned long   d_ne;           /* D1 # of Elements/Pt */
        char            d_eu[16];               /* D1 Engineering Units */
        short           d_pr;           /* D1 Display Precision */
}detFields;


typedef struct recPvtStruct {
    /* THE CODE ASSUMES doPutsCallback is THE FIRST THING IN THIS STRUCTURE!*/
    CALLBACK           doPutsCallback;/* do output links callback structure */
    WDOG_ID            wd_id;       /* watchdog for delay if PV_NC and .EXSC */
    struct scanRecord *pscan;        /* ptr to record which needs work done */
    short              phase;        /* what to do to the above record */
    short              validBuf;      /* which data array buffer is valid */
    recDynLink         caLinkStruct[NUM_LINKS]; /* req'd for recDynLink */
    posBuffers         posBufPtr[NUM_POS];
    detBuffers         detBufPtr[NUM_DET];
    unsigned short     valPosPvs;    /* # of valid positioners */
    unsigned short     valRdbkPvs;   /* # of valid Readbacks   */
    unsigned short     valDetPvs;    /* # of valid Detectors */
    unsigned short     valTrigPvs;   /* # of valid Det Triggers  */
    unsigned short     acqDet[NUM_DET]; /* which detectors to acquire */
    dynLinkInfo       *pDynLinkInfo;
    short              pffo;         /* previous state of ffo */
    short              fpts;         /* backup copy of all freeze flags */
    unsigned short     prevSm[NUM_POS];   /* previous states of p_sm */
    posFields          posParms[NUM_POS]; /* backup copy of all pos. parms */ 
    unsigned long      tablePts[NUM_POS]; /* # of pts loaded in P_PA */
    short              onTheFly;
    float             *nullArray;
    unsigned long      tickStart;    /* used to time the scan */
    char               movPos[NUM_POS]; /* used to indicate interactive move */
    unsigned char      scanErr;
    unsigned char      badOutputPv;  /* positioner, detector trig, readbk */
    unsigned char      badInputPv;   /* detector BAD_PV */
    char               nptsCause;    /* who caused the "# of points to change:
                                        -1:operator; 0-3 Positioners*/
}recPvtStruct;

/*  forward declarations */

static void checkMonitors();
static long initScan();
static void contScan();
static void endScan();
static void doPuts();
static void adjLinParms();
static void changedNpts();
static long checkScanLimits();
static void saveFrzFlags();
static void resetFrzFlags();
static void restoreFrzFlags();

static void previewScan(struct scanRecord *pscan);

static void lookupPV(struct scanRecord *pscan, unsigned short i); 
static void checkConnections(struct scanRecord *pscan); 
static void pvSearchCallback(recDynLink *precDynLink);
static void posMonCallback(recDynLink *precDynLink);
static void restorePosParms(struct scanRecord *pscan, unsigned short i);
static void savePosParms(struct scanRecord *pscan, unsigned short i);
static void zeroPosParms(struct scanRecord *pscan, unsigned short i);

/* variables ... */
long    recScanDebug=0;
long    recScanViewPos=0; 
long    recScanDontCheckLimits=0; 


static long init_record(pscan,pass)
    struct scanRecord	*pscan;
    int pass;
{
    recPvtStruct *precPvt = (recPvtStruct *)pscan->rpvt;
    posFields    *pPosFields;
    detFields    *pDetFields;

    char            *ppvn[PVN_SIZE];
    unsigned short  *pPvStat;
    unsigned short  i;

    recDynLinkPvt   *puserPvt;


    if (pass==0) {
      pscan->vers = VERSION;
      if(pscan->mpts < 100) pscan->mpts = DEF_WF_SIZE;

      /* First time through, rpvt needs initialized */
      pscan->rpvt = calloc(1, sizeof(recPvtStruct));
      precPvt = (recPvtStruct *)pscan->rpvt;

      precPvt->phase = IDLE;

      precPvt->prevSm[0] = pscan->p1sm;
      precPvt->prevSm[1] = pscan->p2sm;
      precPvt->prevSm[2] = pscan->p3sm;
      precPvt->prevSm[3] = pscan->p4sm;

      precPvt->pscan = pscan;

      precPvt->pDynLinkInfo = (dynLinkInfo *)calloc(1, sizeof(dynLinkInfo));

      /* init the private area of the caLinkStruct's   */
      for(i=0;i<(NUM_LINKS); i++) {
        precPvt->caLinkStruct[i].puserPvt 
                 = calloc(1,sizeof(struct recDynLinkPvt));
        puserPvt = (recDynLinkPvt *)precPvt->caLinkStruct[i].puserPvt;
        puserPvt->pscan = pscan;
        puserPvt->linkIndex = i;
        puserPvt->pAddr = calloc(1, sizeof(struct dbAddr));
        puserPvt->dbAddrNv  = -1;
        if     (i < R1_IN)  puserPvt->linkType = POSITIONER;
        else if(i < D1_IN)  puserPvt->linkType = READBACK;
        else if(i < T1_OUT) puserPvt->linkType = DETECTOR;
        else if(i < BS_OUT) puserPvt->linkType = TRIGGER;
        else if(i < P1_OUT) puserPvt->linkType = FWD_LINK;
      }

      pscan->p1pa = (double *) calloc(pscan->mpts, sizeof(double));
      pscan->p2pa = (double *) calloc(pscan->mpts, sizeof(double));
      pscan->p3pa = (double *) calloc(pscan->mpts, sizeof(double));
      pscan->p4pa = (double *) calloc(pscan->mpts, sizeof(double));

      /* Readbacks need double buffering. Allocate space and initialize */
      /* fill pointer and readback array pointers */
      precPvt->validBuf = A_BUFFER;
      pPosFields = (posFields *)&pscan->p1pp;
      for(i=0; i<NUM_RDKS; i++, pPosFields++) {
        precPvt->posBufPtr[i].pBufA =
            (double *) calloc(pscan->mpts, sizeof(double));
        precPvt->posBufPtr[i].pBufB =
            (double *) calloc(pscan->mpts, sizeof(double));

        pPosFields->p_ra = precPvt->posBufPtr[i].pBufA;
        precPvt->posBufPtr[i].pFill = precPvt->posBufPtr[i].pBufB;
      }

      /* allocate arrays for 4 detectors assuming nelem = 1 */
      /* (previewScan uses the first four detectors) */
      /* For now, just point other detectors to a NULL array */

      precPvt->nullArray = (float *) calloc(pscan->mpts, sizeof(float));
      pDetFields = (detFields *)&pscan->d1hr;
      for(i=0; i<NUM_DET; i++, pDetFields++) {
          puserPvt = (recDynLinkPvt *)precPvt->caLinkStruct[D1_IN+i].puserPvt;
          if(i<4) {
              precPvt->detBufPtr[i].pBufA = 
                  (float *) calloc(pscan->mpts, sizeof(float));
              precPvt->detBufPtr[i].pBufB = 
                  (float *) calloc(pscan->mpts, sizeof(float));
              puserPvt->nelem = 1;
          }
          else {
              precPvt->detBufPtr[i].pBufA = precPvt->nullArray;
              precPvt->detBufPtr[i].pBufB = precPvt->nullArray;
              puserPvt->nelem = 0;
          }
          pDetFields->d_da = precPvt->detBufPtr[i].pBufA;
          precPvt->detBufPtr[i].pFill = precPvt->detBufPtr[i].pBufB;
      }

      return(0);
    }

    callbackSetCallback(doPuts, &precPvt->doPutsCallback);
    callbackSetPriority(pscan->prio, &precPvt->doPutsCallback);
    precPvt->wd_id = wdCreate();

    /* initialize all linear scan fields */
    precPvt->nptsCause = -1; /* resolve all positioner parameters */
    changedNpts(pscan);

    if(pscan->ffo) {
       saveFrzFlags(pscan);
       resetFrzFlags(pscan);
    }

    /* init field values */
    pscan->exsc = 0;
    pscan->pxsc = 0;

    *ppvn = &pscan->p1pv[0];
    pPvStat = &pscan->p1nv;

    /* check all dynLink PV's for non-NULL  */
    for(i=0;i<NUM_PVS; i++, pPvStat++, *ppvn += PVN_SIZE) {
        if(*ppvn[0] != NULL) {
            *pPvStat = PV_NC;
            lookupPV(pscan, i);
        }
        else {
            *pPvStat = NO_PV;
        }
    }

    
    return(0);
}

static long process(pscan)
	struct scanRecord	*pscan;
{
        recPvtStruct *precPvt = (recPvtStruct *)pscan->rpvt;
        long	status = 0;


	/* Decide what to do. */
        if(precPvt->phase == SCAN_PENDING) return(status);
        /* Brand new scan */
	if ((pscan->pxsc == 0) && (pscan->exsc == 1)) { 
          precPvt->phase = INIT_SCAN;
          /* use TimeStamp to record beginning of scan */
          recGblGetTimeStamp(pscan);
          initScan(pscan);
        } 

        /* Before Scan Fwd Link is complete, initScan again */
        else if(precPvt->phase == BEFORE_SCAN) {
          initScan(pscan);
        }

        else if ((pscan->pxsc == 1) && (pscan->exsc == 0)) {
          sprintf(pscan->smsg,"Scan aborted by operator");
          db_post_events(pscan,&pscan->smsg,DBE_VALUE);
          endScan(pscan);
        }

        else if (pscan->exsc == 1) {
          if(precPvt->badOutputPv) {
              pscan->alrt = 1;
              db_post_events(pscan,&pscan->alrt, DBE_VALUE);
              sprintf(pscan->smsg,"Lost connection to Control PV");
              db_post_events(pscan,&pscan->smsg,DBE_VALUE);
              pscan->exsc = 0;
              db_post_events(pscan,&pscan->exsc, DBE_VALUE);
              endScan(pscan);
          }
          else {
              contScan(pscan);
          }
        }

        checkMonitors(pscan);

        /* do forward link on last scan aquisition */
        if ((pscan->pxsc == 1) && (pscan->exsc == 0)) {
	    recGblFwdLink(pscan);   /* process the forward scan link record */
            if(recScanDebug) {
              printf("Scan Time = %.2f ms\n",
                     (float)((tickGet()-(precPvt->tickStart))*16.67)); 
            }

	}

        pscan->pxsc = pscan->exsc;
        recGblResetAlarms(pscan);

	pscan->pact = FALSE;
	return(status);
}

static long special(paddr,after)
    struct dbAddr *paddr;
    int	   	  after;
{
    struct scanRecord  	*pscan = (struct scanRecord *)(paddr->precord);
    recPvtStruct        *precPvt = (recPvtStruct *)pscan->rpvt;
    recDynLinkPvt       *puserPvt;
    posFields           *pPosFields;
    int           	 special_type = paddr->special;
    char                *ppvn[PVN_SIZE];
    unsigned short      *pPvStat;
    unsigned short       oldStat;
    short                i = -1;
    long status;
    unsigned char        prevAlrt;

    if(!after) {
        precPvt->pffo = pscan->ffo; /* save previous ffo flag */
        return(0);
    }

    if(recScanDebug > 5) printf("special(),special_type = %d\n",special_type); 
    switch(special_type) {
    case(SPC_MOD):   
        if(paddr->pfield==(void *)&pscan->exsc) {
            /* If aborting, scanOnce immediately */
            if(!pscan->exsc) {
                scanOnce(pscan);
            /* If .EXSC, only scanOnce if IDLE OR PREVIEW */
            } else if((precPvt->phase == IDLE) || (precPvt->phase == PREVIEW)) {
                checkConnections(pscan);
                if(precPvt->badOutputPv || precPvt->badInputPv) {
                    /* postpone the scan for 5 seconds via watchdog */
                    pscan->alrt = 1;
                    db_post_events(pscan,&pscan->alrt,DBE_VALUE);
		    strcpy(pscan->smsg,"Some PV's not connected ...");
                    db_post_events(pscan,&pscan->smsg,DBE_VALUE);
                    precPvt->phase = SCAN_PENDING;
                    wdStart(precPvt->wd_id,(3 * sysClkRateGet()), 
                            (FUNCPTR)callbackRequest, 
                            (int)(&precPvt->doPutsCallback));
                }
                else {
                    pscan->alrt = 0;
                    db_post_events(pscan,&pscan->alrt, DBE_VALUE);
                    scanOnce(pscan);
                }
            }
            return(0);
        } else if(paddr->pfield==(void *)&pscan->cmnd) { 
            if(pscan->cmnd == CLEAR_MSG) {
                pscan->alrt = 0;
                db_post_events(pscan,&pscan->alrt,DBE_VALUE);
		strcpy(pscan->smsg,"");
                db_post_events(pscan,&pscan->smsg,DBE_VALUE);
            }
            else if((pscan->cmnd == CHECK_LIMITS) && !(pscan->exsc)) {
                prevAlrt = pscan->alrt;
                pscan->alrt = 0;
                checkScanLimits(pscan);
                db_post_events(pscan,&pscan->smsg,DBE_VALUE);
                if(pscan->alrt != prevAlrt) {
                    db_post_events(pscan,&pscan->alrt,DBE_VALUE);
                } 
            }
            else if((pscan->cmnd == PREVIEW_SCAN) && !(pscan->exsc)) {
                /* get_array_info() needs to know that we're previewing */
                precPvt->phase = PREVIEW;
                previewScan(pscan);
            }
            else if(((pscan->cmnd == CLEAR_RECORD) ||
                     (pscan->cmnd == CLEAR_POSITIONERS)) &&
                    !(pscan->exsc)) {
                /* clear PV's, frzFlags, modes, etc */
                pscan->scan = 0;
                db_post_events(pscan,&pscan->scan,DBE_VALUE);
                resetFrzFlags(pscan);
                pscan->p1sm = 0;
                db_post_events(pscan,&pscan->p1sm,DBE_VALUE);
                pscan->p1ar = 0;
                db_post_events(pscan,&pscan->p1ar,DBE_VALUE);
                pscan->p2sm = 0;
                db_post_events(pscan,&pscan->p2sm,DBE_VALUE);
                pscan->p2ar = 0;
                db_post_events(pscan,&pscan->p2ar,DBE_VALUE);
                pscan->p3sm = 0;
                db_post_events(pscan,&pscan->p3sm,DBE_VALUE);
                pscan->p3ar = 0;
                db_post_events(pscan,&pscan->p3ar,DBE_VALUE);
                pscan->p4sm = 0;
                db_post_events(pscan,&pscan->p4sm,DBE_VALUE);
                pscan->p4ar = 0;
                db_post_events(pscan,&pscan->p4ar,DBE_VALUE);
                pscan->pasm = 0;
                db_post_events(pscan,&pscan->pasm,DBE_VALUE);
                pscan->ffo = 0;
                db_post_events(pscan,&pscan->ffo,DBE_VALUE);
                for(i=0; i<NUM_PVS; i++) {
                    puserPvt = (recDynLinkPvt *)precPvt->caLinkStruct[i].puserPvt;
                    if((pscan->cmnd == CLEAR_RECORD) ||
                       ((pscan->cmnd == CLEAR_POSITIONERS) &&
                       (puserPvt->linkType == POSITIONER))) {
                        /* clear this PV */
                        pPvStat = &pscan->p1nv + i; /* pointer arithmetic */
                        oldStat = *pPvStat;
                        *ppvn = &pscan->p1pv[0] + (i*PVN_SIZE);
                        *ppvn[0] = NULL;
                        db_post_events(pscan,*ppvn,DBE_VALUE);
                        if(*pPvStat != NO_PV) {
                           /* PV is now NULL but didn't used to be */
                           *pPvStat = NO_PV;              /* PV just cleared */
                            if(*pPvStat != oldStat) {
                               db_post_events(pscan,pPvStat,DBE_VALUE);
                            }
                            if(puserPvt->dbAddrNv) { /*Clear if non-local */
                                recDynLinkClear(&precPvt->caLinkStruct[i]);
                                 /* Positioners have two recDynLinks */
                                if(i<NUM_POS)
                                    recDynLinkClear(&precPvt->caLinkStruct[i+NUM_PVS]);
                            }
                            else puserPvt->dbAddrNv = -1;
                        }
                        else {
                            /* PV is NULL, but previously MAY have been "time" */
                            puserPvt->ts = 0;
                        }
                    }
                }
            }
            else if(paddr->pfield==(void *)&pscan->prio) {
                callbackSetPriority(pscan->prio, &precPvt->doPutsCallback);
                return(0);
            }
            return(0);
        } 
        /* Check to see if it was a command to send to the positioner */
        else if(paddr->pfield==(void *)&pscan->p1dv) i = P1_IN;
        else if(paddr->pfield==(void *)&pscan->p2dv) i = P2_IN; 
        else if(paddr->pfield==(void *)&pscan->p3dv) i = P3_IN; 
        else if(paddr->pfield==(void *)&pscan->p4dv) i = P4_IN; 

        /* if a positioner command and not scanning .... */
        if((i>=0) && ((precPvt->phase==IDLE) || (precPvt->phase==PREVIEW))) {
           pPvStat = &precPvt->pscan->p1nv + i;
           pPosFields = (posFields *)&precPvt->pscan->p1pp + i;
           if(*pPvStat == PV_OK) {
               puserPvt = precPvt->caLinkStruct[i].puserPvt;
               if(puserPvt->dbAddrNv) {
                  status = recDynLinkPut(&precPvt->caLinkStruct[i+P1_OUT],
                              &(pPosFields->p_dv), 1);
               } else {
                  /* Must use the callback task ... */
                  precPvt->movPos[i] = 1;
                  callbackRequest(&precPvt->doPutsCallback);
               }
           }
        }


        break;

    case(SPC_SC_MO): /* Step Mode changed for a positioner */
        
        pPosFields = (posFields *)&precPvt->pscan->p1pp;
        for(i=0; i<NUM_POS; i++, pPosFields++) {
            if(paddr->pfield==(void *)&pPosFields->p_sm) {
                /* Entering Table mode ? */
                if((pPosFields->p_sm == REC_SCAN_MO_TAB) &&
                   (precPvt->prevSm[i] != REC_SCAN_MO_TAB)) {
                    savePosParms(pscan, (unsigned short) i);
                    zeroPosParms(pscan, (unsigned short) i);
                    precPvt->prevSm[i] = pPosFields->p_sm;
                    if(precPvt->tablePts[i] < pscan->npts) {
                      sprintf(pscan->smsg,"Pts in P%d Table < # of Steps",i+1);
                      db_post_events(pscan,&pscan->smsg,DBE_VALUE);
                      if(!pscan->alrt) {
                          pscan->alrt = 1;
                          db_post_events(pscan,&pscan->alrt,DBE_VALUE);
                      }
                    }
                }
                /* Leaving Table mode ? */
                else if((pPosFields->p_sm != REC_SCAN_MO_TAB) &&
                        (precPvt->prevSm[i] == REC_SCAN_MO_TAB)) {
                    restorePosParms(pscan, (unsigned short) i);
                    prevAlrt = pscan->alrt;
                    precPvt->nptsCause = -1;
                    changedNpts(pscan);
                    db_post_events(pscan,&pscan->smsg,DBE_VALUE);
                    if(pscan->alrt != prevAlrt) {
                        db_post_events(pscan,&pscan->alrt,DBE_VALUE);
                    }
                    precPvt->prevSm[i] = pPosFields->p_sm;
                }
            }
        }
        break;
            
    case(SPC_SC_S):  /* linear scan parameter change */
    case(SPC_SC_I):  
    case(SPC_SC_E):  
    case(SPC_SC_C):  
    case(SPC_SC_W):  
        /* resolve linear scan parameters affected by this fields change */
        prevAlrt = pscan->alrt;
        pscan->alrt = 0;
	strcpy(pscan->smsg,"");
        adjLinParms(paddr);       
        db_post_events(pscan,&pscan->smsg,DBE_VALUE);
        if(pscan->alrt != prevAlrt) {
            db_post_events(pscan,&pscan->alrt,DBE_VALUE);
        }
        if(recScanViewPos) {
            previewScan(pscan);
        }
        break;

    case(SPC_SC_N):  
        /* adjust all linear scan parameters per new # of steps */
        if(pscan->npts > pscan->mpts) {
            pscan->npts = pscan->mpts;
            db_post_events(pscan,&pscan->npts,DBE_VALUE);
        }
        prevAlrt = pscan->alrt;
        pscan->alrt = 0;
	strcpy(pscan->smsg,"");
        precPvt->nptsCause = -1; /* resolve all positioner parameters */
        changedNpts(pscan);       
        db_post_events(pscan,&pscan->smsg,DBE_VALUE);
        if(pscan->alrt != prevAlrt) {
            db_post_events(pscan,&pscan->alrt,DBE_VALUE);
        } 
        if(recScanViewPos) {
            previewScan(pscan);
        } 
        break;

    case(SPC_SC_FFO):
        /* Freeze Flag Override field */
        if((pscan->ffo) && (!precPvt->pffo)) {
           saveFrzFlags(pscan);
           resetFrzFlags(pscan);
        }
        else if(!pscan->ffo && precPvt->pffo) /* only on 1->0 */
           restoreFrzFlags(pscan);
    
        break;

    case(SPC_SC_F):
        /* Freeze Flag Override field */
        if(pscan->ffo)
           resetFrzFlags(pscan);

        break;


    default:
        /* Check for a dynamic link PV change */
        if((special_type >= FIRST_PV) && (special_type <= LAST_PV)) {
            i = special_type - FIRST_PV;
            puserPvt = (recDynLinkPvt *)precPvt->caLinkStruct[i].puserPvt;
            pPvStat = &pscan->p1nv + i; /* pointer arithmetic */
            oldStat = *pPvStat;
            *ppvn = &pscan->p1pv[0] + (i*PVN_SIZE);
            if(*ppvn[0] != NULL) {
                if(recScanDebug > 5) printf("Search during special \n");
                *pPvStat = PV_NC;
                /* force flags to indicate PV_NC until callback happens */
                if((i<D1_IN) || (i==T1_OUT) || (i==T2_OUT)) {
                    precPvt->badOutputPv = 1;
                }
                else { 
                    precPvt->badInputPv = 1;
                }
                /* need to post_event before recDynLinkAddXxx because
                   SearchCallback could happen immediatley */
                if(*pPvStat != oldStat) {
                    db_post_events(pscan,pPvStat,DBE_VALUE);
                }
                lookupPV(pscan, i);
            }
            else if(*pPvStat != NO_PV) {
                /* PV is now NULL but didn't used to be */
                *pPvStat = NO_PV;              /* PV just cleared */
                if(*pPvStat != oldStat) {
                    db_post_events(pscan,pPvStat,DBE_VALUE);
                }
                if(puserPvt->dbAddrNv) { /*Clear if non-local */
                    recDynLinkClear(&precPvt->caLinkStruct[i]);
                    /* Positioners have two recDynLinks */
                    if(i<NUM_POS) 
                        recDynLinkClear(&precPvt->caLinkStruct[i+NUM_PVS]);
                }
                else puserPvt->dbAddrNv = -1;
            }
            else {
                /* PV is NULL, but previously MAY have been "time" */
                puserPvt->ts = 0;
            }
         }
    }
return(0);
}

static long cvt_dbaddr(paddr)
    struct dbAddr *paddr;
{
    struct scanRecord *pscan=(struct scanRecord *)paddr->precord;
    
    posFields          *pPosFields = (posFields *)&pscan->p1pp;
    detFields          *pDetFields = (detFields *)&pscan->d1hr;
    short               fieldOffset;
    unsigned short      i;

    fieldOffset = ((dbFldDes *)(paddr->pfldDes))->offset;

    /* for the double buffered arrays, must use the field offset
       because you don't know what the pointer will be */

    for(i=0; i < NUM_DET; i++, pDetFields++) {
        if(((char *)&pDetFields->d_da - (char *)pscan) == fieldOffset) {
            paddr->pfield = pDetFields->d_da;
            paddr->no_elements = pscan->mpts;
            paddr->field_type = DBF_FLOAT;
            paddr->field_size = sizeof(float);
            paddr->dbr_field_type = DBF_FLOAT;
            return(0);
        }
    }

    for(i=0; i < NUM_POS; i++, pPosFields++) {
        if(((char *)&pPosFields->p_pa - (char *)pscan) == fieldOffset) { 
               paddr->pfield = (void *)(pPosFields->p_pa);
               paddr->no_elements = pscan->mpts;
               paddr->field_type = DBF_DOUBLE;
               paddr->field_size = sizeof(double);
               paddr->dbr_field_type = DBF_DOUBLE;
        } else if(((char *)&pPosFields->p_ra - (char *)pscan) == fieldOffset) {
               paddr->pfield = (void *)(pPosFields->p_ra);
               paddr->no_elements = pscan->mpts;
               paddr->field_type = DBF_DOUBLE;
               paddr->field_size = sizeof(double);
               paddr->dbr_field_type = DBF_DOUBLE;
        }
    }
    return(0);
}

static long get_array_info(paddr,no_elements,offset)
    struct dbAddr *paddr;
    long          *no_elements;
    long          *offset;
{
    struct scanRecord  *pscan=(struct scanRecord *)paddr->precord;
    recPvtStruct       *precPvt = (recPvtStruct *)pscan->rpvt;
    detFields          *pDetFields = (detFields *)&pscan->d1hr;
    posFields          *pPosFields = (posFields *)&pscan->p1pp;
    short               fieldOffset;
    unsigned short     *pPvStat; 
    unsigned short           i;

    /* This routine is called because someone wants an array. Determine
       which array they are interested by comparing the address of the 
       field to the array pointers */          

    fieldOffset = ((dbFldDes *)(paddr->pfldDes))->offset;

    pPvStat = &precPvt->pscan->d1nv;
    for(i=0; i < NUM_DET; i++, pDetFields++, pPvStat++) {
        if(((char *)&pDetFields->d_da - (char *)pscan) == fieldOffset) {
            if((precPvt->acqDet[i]) ||
               ((i<NUM_POS) && (precPvt->phase == PREVIEW))) {
                if(precPvt->validBuf)
                    paddr->pfield = precPvt->detBufPtr[i].pBufB;
                else
                    paddr->pfield = precPvt->detBufPtr[i].pBufA;
            } else {
                paddr->pfield = precPvt->nullArray;
            }
            *no_elements = pscan->mpts;
            *offset = 0;
            return(0);
        }
    }
    for(i=0; i < NUM_POS; i++, pPosFields++) {
        if(((char *)&pPosFields->p_ra - (char *)pscan) == fieldOffset) {
            if(precPvt->validBuf) 
                paddr->pfield = precPvt->posBufPtr[i].pBufB;
            else
                paddr->pfield = precPvt->posBufPtr[i].pBufA;
                
            *no_elements = pscan->mpts;
            *offset = 0;
            return(0);
        }
    }

    if (paddr->pfield == pscan->p1pa) {
        *no_elements = pscan->mpts;
        *offset = 0;
    } else if (paddr->pfield == pscan->p2pa) {
        *no_elements = pscan->mpts;
        *offset = 0;
    } else if (paddr->pfield == pscan->p3pa) {
        *no_elements = pscan->mpts;
        *offset = 0;
    } else if (paddr->pfield == pscan->p4pa) {
        *no_elements = pscan->mpts;
        *offset = 0;
    } else {
        *no_elements = 0;
        *offset = 0;
    }

    return(0);
}

static long put_array_info(paddr,nNew)
    struct dbAddr *paddr;
    long          nNew;
{
    struct scanRecord  *pscan=(struct scanRecord *)paddr->precord;
    recPvtStruct       *precPvt = (recPvtStruct *)pscan->rpvt;
    posFields          *pPosFields = (posFields *)&pscan->p1pp;
    short               fieldOffset;
    unsigned short      i;

    /* This routine is called because someone wrote a table to the
       "positioner" array p_pa. Determine which positioner and store
       nelem for future use. Also check against current npts  */

    fieldOffset = ((dbFldDes *)(paddr->pfldDes))->offset;
    
    for(i=0; i < NUM_POS; i++, pPosFields++) {
        if(((char *)&pPosFields->p_pa - (char *)pscan) == fieldOffset) {
            precPvt->tablePts[i] = nNew;
            if(nNew < pscan->npts) {
                sprintf(pscan->smsg,"Pts in P%d Table < # of Steps", i+1);
                db_post_events(pscan,&pscan->smsg,DBE_VALUE);
                if(!pscan->alrt) {
                    pscan->alrt = 1;
                    db_post_events(pscan,&pscan->alrt,DBE_VALUE);
                }
            } else {
		strcpy(pscan->smsg,"");
                db_post_events(pscan,&pscan->smsg,DBE_VALUE);
                if(pscan->alrt) {
                    pscan->alrt = 0;
                    db_post_events(pscan,&pscan->alrt,DBE_VALUE);
                }
            }
        return(0);
        }
    }
    return(0);
}


static long get_enum_str(paddr,pstring)
    struct dbAddr *paddr;
    char          *pstring;
{
    struct scanRecord     *pscan=(struct scanRecord *)paddr->precord;

    if(paddr->pfield==(void *)&pscan->cmnd)
    {
        sprintf(pstring, "%d",pscan->cmnd);
    }
    else
    {
        strcpy(pstring,"No string");
    }
    return(0);
}
static long get_enum_strs(paddr,pes)
    struct dbAddr *paddr;
    struct dbr_enumStrs *pes;
{
    struct scanRecord     *pscan=(struct scanRecord *)paddr->precord;

    if(paddr->pfield==(void *)&pscan->cmnd)
    {
        memset(pes->strs,'\0',sizeof(pes->strs));
        strncpy(pes->strs[0],"0-Clear Msg",sizeof("0-Clear Msg"));
        strncpy(pes->strs[1],"1-Check Limits",sizeof("1-Check Limits"));
        pes->no_str = 2;
    }
    else
    {
        strcpy(pes->strs[0],"No string");
        pes->no_str=1;
    }

    return(0);
}

static long put_enum_str(paddr,pstring)
    struct dbAddr *paddr;
    char          *pstring;
{
    struct scanRecord     *pscan=(struct scanRecord *)paddr->precord;

    if(paddr->pfield==(void *)&pscan->cmnd)
    {
        if(sscanf(pstring,"%hu",&pscan->cmnd)<=0)
            return(S_db_badChoice);
    }
    else
    {
        return(S_db_badChoice);
    }

    return(0);
}

static long get_units(paddr,units)
    struct dbAddr *paddr;
    char	  *units;
{
    struct scanRecord  *pscan=(struct scanRecord *)paddr->precord;
    posFields          *pPosFields = (posFields *)&pscan->p1pp;
    detFields          *pDetFields = (detFields *)&pscan->d1hr;
    short               fieldOffset;
    short               i;

   
    /* Use the field offset to see which detector or positioner it is */
    fieldOffset = ((dbFldDes *)(paddr->pfldDes))->offset;

    for(i=0; i < NUM_POS; i++, pPosFields++) {
        if((fieldOffset >= ((char *)&pPosFields->p_pp - (char *)pscan)) &&
           (fieldOffset <= ((char *)&pPosFields->p_pr - (char *)pscan))) {
               strncpy(units, pPosFields->p_eu, 7);
               units[7] = NULL;
               return(0);
        }
    }
    for(i=0; i < NUM_DET; i++, pDetFields++) {
        if((fieldOffset >= ((char *)&pDetFields->d_hr - (char *)pscan)) &&
           (fieldOffset <= ((char *)&pDetFields->d_pr - (char *)pscan))) {
               strncpy(units, pDetFields->d_eu, 7);
               units[7] = NULL;
               return(0);
        }
    }

    return(0);
}

static long get_precision(paddr,precision)
    struct dbAddr *paddr;
    long	  *precision;
{
    struct scanRecord  *pscan=(struct scanRecord *)paddr->precord;
    posFields          *pPosFields = (posFields *)&pscan->p1pp;
    detFields          *pDetFields = (detFields *)&pscan->d1hr;
    short               fieldOffset;
    short               i;

    
    /* Use the field offset to see which detector or positioner it is */
    fieldOffset = ((dbFldDes *)(paddr->pfldDes))->offset;

    for(i=0; i < NUM_POS; i++, pPosFields++) {
        if((fieldOffset >= ((char *)&pPosFields->p_pp - (char *)pscan)) &&
           (fieldOffset <= ((char *)&pPosFields->p_pr - (char *)pscan))) {
               *precision = pPosFields->p_pr;
               return(0);
        }
    }
    for(i=0; i < NUM_DET; i++, pDetFields++) {
        if((fieldOffset >= ((char *)&pDetFields->d_hr - (char *)pscan)) &&
           (fieldOffset <= ((char *)&pDetFields->d_pr - (char *)pscan))) {
               *precision = pDetFields->d_pr;
               return(0);
        }
    }
    
    *precision = 2;
    return(0);
}

static long get_graphic_double(paddr,pgd)
    struct dbAddr *paddr;
    struct dbr_grDouble	*pgd;
{
    struct scanRecord  *pscan=(struct scanRecord *)paddr->precord;
    posFields          *pPosFields = (posFields *)&pscan->p1pp;
    detFields          *pDetFields = (detFields *)&pscan->d1hr;
    short               fieldOffset;
    short               i;

   
    /* Use the field offset to see which detector or positioner it is */
    fieldOffset = ((dbFldDes *)(paddr->pfldDes))->offset;

    for(i=0; i < NUM_POS; i++, pPosFields++) {
        if((fieldOffset >= ((char *)&pPosFields->p_pp - (char *)pscan)) &&
           (fieldOffset <= ((char *)&pPosFields->p_pr - (char *)pscan))) {
               pgd->upper_disp_limit = pPosFields->p_hr;
               pgd->lower_disp_limit = pPosFields->p_lr;
               return(0);
        }
    }
    for(i=0; i < NUM_DET; i++, pDetFields++) {
        if((fieldOffset >= ((char *)&pDetFields->d_hr - (char *)pscan)) &&
           (fieldOffset <= ((char *)&pDetFields->d_pr - (char *)pscan))) {
               pgd->upper_disp_limit = pDetFields->d_hr;
               pgd->lower_disp_limit = pDetFields->d_lr;
               return(0);
        }
    }

    recGblGetGraphicDouble(paddr,pgd);
    return(0);
}

static long get_control_double(paddr,pcd)
    struct dbAddr *paddr;
    struct dbr_ctrlDouble *pcd;
{
    /* for testing .... */
    /* return upper_disp_limit and lower_disp_limit as control limits */
    struct dbr_grDouble grDblStruct;
    long status;

    status = get_graphic_double(paddr,&grDblStruct);
    pcd->upper_ctrl_limit = grDblStruct.upper_disp_limit;
    pcd->lower_ctrl_limit = grDblStruct.lower_disp_limit;

    return(0);
}

static long get_alarm_double(paddr,pad)
    struct dbAddr *paddr;
    struct dbr_alDouble *pad;
{
    recGblGetAlarmDouble(paddr,pad);
    return(0);
}


static void checkMonitors(pscan)
    struct scanRecord   *pscan;
{
    recPvtStruct        *precPvt = (recPvtStruct *)pscan->rpvt;
    detFields           *pDetFields = (detFields *)&pscan->d1hr;
    posFields           *pPosFields = (posFields *)&pscan->p1pp;
    unsigned long        now;
    int                  i;

    /* If last posting time is > MIN_MON, check to see if any dynamic fields 
       have changed (also post monitors on end of scan)*/

    now=tickGet();
    if(((now - pscan->tolp) > MIN_MON) || 
       ((pscan->pxsc == 1) && (pscan->exsc == 0))) {
        pscan->tolp = now;

        /* check all detectors */
        for(i=0; i < NUM_DET; i++, pDetFields++) {
          if(fabs(pDetFields->d_lv - pDetFields->d_cv) > 0) {
            db_post_events(pscan,&pDetFields->d_cv, DBE_VALUE);
            pDetFields->d_lv = pDetFields->d_cv;
          }
        }

        /* check all positioners and readbacks  */
        for(i=0; i < NUM_POS; i++, pPosFields++) {
          if(fabs(pPosFields->p_lv - pPosFields->p_dv) > 0) {
            db_post_events(pscan,&pPosFields->p_dv, DBE_VALUE);
            pPosFields->p_lv = pPosFields->p_dv;
          }
          if(fabs(pPosFields->r_lv - pPosFields->r_cv) > 0) {
            db_post_events(pscan,&pPosFields->r_cv, DBE_VALUE);
            pPosFields->r_lv = pPosFields->r_cv;
          }
        }

        if(pscan->pcpt != pscan->cpt) {
            db_post_events(pscan,&pscan->cpt, DBE_VALUE);
            pscan->pcpt = pscan->cpt;
        }

        /* Post a monitor on VAL to indicate new point */
        /* (checking pxsc skips the first pass when D_CV's are
           not valid and also catches the last point after exsc == 0 */
        if(pscan->pxsc) {
            db_post_events(pscan,&pscan->val, DBE_VALUE); 
        }
    }

    /* if this is the end of a scan, post data arrays */
    /* post these with DBE_LOG option for archiver    */
    if ((pscan->pxsc == 1) && (pscan->exsc == 0)) { 
 
        /* Must post events on both pointers, since toggle */
        db_post_events(pscan,precPvt->posBufPtr[0].pBufA, DBE_VAL_LOG);
        db_post_events(pscan,precPvt->posBufPtr[0].pBufB, DBE_VAL_LOG);
        db_post_events(pscan,precPvt->posBufPtr[1].pBufA, DBE_VAL_LOG);
        db_post_events(pscan,precPvt->posBufPtr[1].pBufB, DBE_VAL_LOG);
        db_post_events(pscan,precPvt->posBufPtr[2].pBufA, DBE_VAL_LOG);
        db_post_events(pscan,precPvt->posBufPtr[2].pBufB, DBE_VAL_LOG);
        db_post_events(pscan,precPvt->posBufPtr[3].pBufA, DBE_VAL_LOG);
        db_post_events(pscan,precPvt->posBufPtr[3].pBufB, DBE_VAL_LOG);

        for(i=0; i<NUM_DET; i++) {
            db_post_events(pscan,precPvt->detBufPtr[i].pBufA, DBE_VAL_LOG);
            db_post_events(pscan,precPvt->detBufPtr[i].pBufB, DBE_VAL_LOG);
        }
        /* I must also post a monitor on the NULL array, because some
           clients connected to D?PV's without valid PV's ! */
        db_post_events(pscan, precPvt->nullArray, DBE_VALUE);

        /* post alert if changed */
        if(precPvt->scanErr) {
            pscan->alrt = precPvt->scanErr;
            db_post_events(pscan,&pscan->alrt, DBE_VALUE);
        }

    if (pscan->pxsc != pscan->exsc) 
        db_post_events(pscan,&pscan->exsc, DBE_VALUE);

    }
}


/**************************************************************
 * lookupPV()
 * This is the routine that looks up newly entered "dynamic"
 * links. A dbNameToAddr call is done first to
 * see if the PV resides on this IOC. Much better performance is
 * obtained by using dbGet/dbPutField rather than the recDynLink
 * library for local PV's. If the dbNameToAddr fails, 
 * recDynLinkAddInput is used.
 *
 * For positioners:     
 *     If the PV is local, the dbAddr is used for puts and gets (
 *        to get the "previous positioner" before a scan starts. A 
 *        recDynLinkInput is also used to monitor the positioner
 *        for changes made from other places.
 *        This value is stored in p_cv.
 *     If the PV is not local, a recDynLinkOutput and a 
 *        recDynLinkInput are used. The recDynLinkInput has a 
 *        monitor callback routine for p_cv, but a get is used
 *        to find the previous position.
 *
 * For Readbacks:
 *     If the PV name starts with TIME or time, set a flag that
 *        indicates that the timestamp should be used as the
 *        readback value.
 *     Otherwise, do a PV lookup.
 **************************************************************/
static void lookupPV(struct scanRecord *pscan, unsigned short i)
{
    recPvtStruct  *precPvt = (recPvtStruct *)pscan->rpvt;
    recDynLinkPvt *puserPvt;
    char          *ppvn[PVN_SIZE];
    char          *pdesc = ".DESC";
    char          *pval  = ".VAL";
    char          *pTIME = "TIME";
    char          *ptime = "time";
    char          *pdot;
    unsigned short *pPvStat;

    /* Point to correct places by using index i */
    *ppvn = &pscan->p1pv[0] + (i * PVN_SIZE);
    pPvStat = &pscan->p1nv + i; /* pointer arithmetic */
    puserPvt = (recDynLinkPvt *)precPvt->caLinkStruct[i].puserPvt;

    /* If PV field name = DESC, change to VAL */
    pdot = strrchr(*ppvn, '.');
    if(pdot!=NULL) {
        if(strncmp(pdot, pdesc, 5) == 0) {
            strcpy(pdot, pval);
            db_post_events(pscan, *ppvn, DBE_VALUE);
        }
    }

    /* See if it's a local PV */
    puserPvt->dbAddrNv = dbNameToAddr(*ppvn, puserPvt->pAddr);
    if(recScanDebug) {
      printf("PV: %s ,dbNameToAddr returned %lx\n",*ppvn, puserPvt->dbAddrNv);
    }

    switch(puserPvt->linkType) {
      case POSITIONER:
        if(puserPvt->dbAddrNv) {
            recDynLinkAddOutput(&precPvt->caLinkStruct[i+NUM_PVS], *ppvn,
                DBR_DOUBLE, rdlSCALAR, pvSearchCallback);
            recDynLinkAddInput(&precPvt->caLinkStruct[i], *ppvn,
                DBR_DOUBLE, rdlSCALAR, pvSearchCallback, posMonCallback);
        }else {
            /* Add a monitor anyway, so p_cv can be kept up to date */
            recDynLinkAddInput(&precPvt->caLinkStruct[i], *ppvn,
                DBR_DOUBLE, rdlSCALAR, pvSearchCallback, posMonCallback);
        }
        break;

      case READBACK:
        /* Check to see if it equals "time" */
        if((strncmp(*ppvn, pTIME, 4)==0) || (strncmp(*ppvn, ptime, 4)==0)) {
            puserPvt->ts = 1;
            *pPvStat = NO_PV;
            db_post_events(pscan, pPvStat, DBE_VALUE);
            break;  /* don't do lookups or pvSearchCallback */
        } else {
            puserPvt->ts = 0;
        }
        if(puserPvt->dbAddrNv) {
            recDynLinkAddInput(&precPvt->caLinkStruct[i], *ppvn,
                DBR_DOUBLE, rdlSCALAR, pvSearchCallback, NULL);
        }else {
            pvSearchCallback(&precPvt->caLinkStruct[i]);
        }
        break;

      case DETECTOR:
        if(puserPvt->dbAddrNv) {
            recDynLinkAddInput(&precPvt->caLinkStruct[i], *ppvn,
                DBR_FLOAT, rdlSCALAR, pvSearchCallback, NULL);
        }else {
            pvSearchCallback(&precPvt->caLinkStruct[i]);
        }
        break;

      case TRIGGER:
      case FWD_LINK:
        if(puserPvt->dbAddrNv) {
            recDynLinkAddOutput(&precPvt->caLinkStruct[i], *ppvn,
                DBR_FLOAT, rdlSCALAR, pvSearchCallback);
        }else {
            pvSearchCallback(&precPvt->caLinkStruct[i]);
        }
        break;
    }
}


LOCAL void pvSearchCallback(recDynLink *precDynLink)
{

    recDynLinkPvt     *puserPvt = (recDynLinkPvt *)precDynLink->puserPvt;
    struct scanRecord *pscan    = puserPvt->pscan;
    recPvtStruct      *precPvt = (recPvtStruct *)pscan->rpvt;
    posFields         *pPosFields = (posFields *)&pscan->p1pp;
    detFields         *pDetFields = (detFields *)&pscan->d1hr;
    unsigned short     index    = puserPvt->linkIndex;
    unsigned short     detIndex;
    unsigned short    *pPvStat;
    unsigned short     oldValid;
    size_t             nelem;
    long               status;
    long               nRequest = 1;
    long               options = DBR_UNITS | DBR_PRECISION | DBR_GR_DOUBLE |
                                 DBR_CTRL_DOUBLE ;
    int                precision;

    pPvStat = &pscan->p1nv + index;    /* pointer arithmetic */

    oldValid = *pPvStat;
    if(puserPvt->dbAddrNv && recDynLinkConnectionStatus(precDynLink)) {
        *pPvStat = PV_NC;
        checkConnections(pscan);
        if(recScanDebug) printf("Search Callback: No Connection\n");
        if(*pPvStat != oldValid) {
            db_post_events(pscan, pPvStat, DBE_VALUE);
        }
        return;
    }
    else {
        *pPvStat = PV_OK;
        checkConnections(pscan);
        if(recScanDebug) printf("Search Callback: Success\n");
    }
    if(*pPvStat != oldValid) {
        db_post_events(pscan, pPvStat, DBE_VALUE);
    }

    /* Do other stuff, depending on the Link Type */
    switch(puserPvt->linkType) {
      case POSITIONER: /* indexes 0,1,2,3 and 25,26,27,28 */
        /* Positioners have an IN and an OUT. Only do the in's */
        if(index > P4_IN)
            break;
        else {
            pPosFields += index;
        }

        /* Get control limits, precision, and egu */
        if(puserPvt->dbAddrNv) {
            /* use the recDynLink lib */
            recDynLinkGetUnits(precDynLink, pPosFields->p_eu, 8);
            recDynLinkGetPrecision(precDynLink, &precision);
            pPosFields->p_pr = precision;
            recDynLinkGetControlLimits(precDynLink,
                &pPosFields->p_lr, &pPosFields->p_hr);
        } else {
            status = dbGet(puserPvt->pAddr, DBR_FLOAT,
                           precPvt->pDynLinkInfo,
                           &options, &nRequest, NULL);
            if(status == OK) {
               strcpy(pPosFields->p_eu, precPvt->pDynLinkInfo->units);
               pPosFields->p_pr = precPvt->pDynLinkInfo->precision;
               pPosFields->p_hr = precPvt->pDynLinkInfo->upper_ctrl_limit;
               pPosFields->p_lr = precPvt->pDynLinkInfo->lower_ctrl_limit;
            }
        }
        db_post_events(pscan,&pPosFields->p_eu, DBE_VALUE);
        db_post_events(pscan,&pPosFields->p_pr, DBE_VALUE);
        db_post_events(pscan,&pPosFields->p_hr, DBE_VALUE);
        db_post_events(pscan,&pPosFields->p_lr, DBE_VALUE);
 
        break;

      case READBACK:
        break;

      case DETECTOR:
        /* Derive pointer to appropriate detector fields */
        detIndex = index - D1_IN;
        pDetFields += detIndex; /* pointer arithmetic */

        /* Get display limits, precision, and egu */
        if(puserPvt->dbAddrNv) {
            /* use the recDynLink lib */
            recDynLinkGetUnits(precDynLink, pDetFields->d_eu, 8);
            recDynLinkGetPrecision(precDynLink, &precision);
            pDetFields->d_pr = precision;
            recDynLinkGetGraphicLimits(precDynLink,
                &pDetFields->d_lr, &pDetFields->d_hr);
        } else {
            status = dbGet(puserPvt->pAddr, DBR_FLOAT,
                           precPvt->pDynLinkInfo,
                           &options, &nRequest, NULL);
            if(status == OK) {
               strcpy(pDetFields->d_eu, precPvt->pDynLinkInfo->units);
               pDetFields->d_pr = precPvt->pDynLinkInfo->precision;
               pDetFields->d_hr = precPvt->pDynLinkInfo->upper_disp_limit;
               pDetFields->d_lr = precPvt->pDynLinkInfo->lower_disp_limit;
            }
        }
        db_post_events(pscan,&pDetFields->d_eu, DBE_VALUE);
        db_post_events(pscan,&pDetFields->d_pr, DBE_VALUE);
        db_post_events(pscan,&pDetFields->d_hr, DBE_VALUE);
        db_post_events(pscan,&pDetFields->d_lr, DBE_VALUE);
  
        /* get # of elements, re-allocate array as necessary */
        if(puserPvt->dbAddrNv) {
            status = recDynLinkGetNelem(precDynLink, &nelem);
        }
        else {
            nelem = puserPvt->pAddr->no_elements;
        }
  
        if(nelem > 1) {
            printf("NOT READY FOR VECTORS YET !!!\n");
            nelem = 1;  /* force it to be a scalar for now */
        }
        if(nelem > puserPvt->nelem) {
            /*
            if(puserPvt->nelem) {
                printf("Freeing memory for dynLink %d  \n", index);
                free(pDetFields->d_da);
            }
            */
            
            printf("Allocating memory for dynLink %d\n  ", index);  
            pDetFields->d_da=(float *)calloc(pscan->mpts*nelem,sizeof(float));
            precPvt->detBufPtr[detIndex].pBufA =
               (float *) calloc(pscan->mpts, sizeof(float));
            precPvt->detBufPtr[detIndex].pBufB =
               (float *) calloc(pscan->mpts, sizeof(float));
            
            if(!(precPvt->detBufPtr[detIndex].pBufA) ||
               !(precPvt->detBufPtr[detIndex].pBufB)) {
                /* MEMORY ALLOCATION FAILED */
                printf("MEMORY ALLOCATION FAILED \n");
                puserPvt->nelem = 0;
            }
            else {
                puserPvt->nelem = nelem;
                if(precPvt->validBuf)
                    precPvt->detBufPtr[detIndex].pFill = 
                        precPvt->detBufPtr[detIndex].pBufA;
                else
                    precPvt->detBufPtr[detIndex].pFill = 
                        precPvt->detBufPtr[detIndex].pBufB;
            }
        }
        break;

      case TRIGGER:
      case FWD_LINK:
        break;
    }

       
}

LOCAL void posMonCallback(recDynLink *precDynLink)
{

    recDynLinkPvt     *puserPvt = (recDynLinkPvt *)precDynLink->puserPvt;
    struct scanRecord *pscan    = puserPvt->pscan;
    recPvtStruct      *precPvt = (recPvtStruct *)pscan->rpvt;
    unsigned short     index    = puserPvt->linkIndex;
    posFields         *pPosFields = (posFields *)&pscan->p1pp + index;
    long               status;
    size_t	       nRequest = 1;


    /* update p_cv with current positioner value */
    /* NOTE: For fast scans, these will get lost, because it is the low
       priority channel access task that is doing these callbacks */
    status = recDynLinkGet(&precPvt->caLinkStruct[index],
                       &pPosFields->p_cv, &nRequest, 0, 0, 0);
    db_post_events(pscan,&pPosFields->p_cv, DBE_VALUE);
}


static void checkConnections(struct scanRecord *pscan)
{
  recPvtStruct   *precPvt = (recPvtStruct *)pscan->rpvt;
  unsigned short *pPvStat;
  unsigned char   badOutputPv = 0;
  unsigned char   badInputPv = 0;
  unsigned short  i;

  pPvStat = &pscan->p1nv;

  for(i=1; i <= NUM_POS; i++, pPvStat++) {
      if(*pPvStat == PV_NC) badOutputPv = 1;
  }
  /* let pPvStat continue to increment into Readbacks */
  for(i=1; i <= NUM_POS; i++, pPvStat++) {
      if(*pPvStat == PV_NC) badOutputPv = 1;
  }
  /* let pPvStat continue to increment into Detectors */
  for(i=1; i <= NUM_DET; i++, pPvStat++) {
      if(*pPvStat == PV_NC) badInputPv = 1;
  }
  /* let pPvStat continue to increment into Triggers */
  for(i=1; i <= NUM_TRGS; i++, pPvStat++) {
      if(*pPvStat == PV_NC) badOutputPv = 1;
  }

  precPvt->badOutputPv = badOutputPv;
  precPvt->badInputPv  = badInputPv;
}



/********************** SCAN Initialization  ********************************/
/*
 * If a "Before Scan Link PV" is defined, this routine will be called twice,
 * once before the link is processed and once after. The starting positions
 * and limits are calculated each time, in case the link caused something to
 * change.
 *
 */
static long initScan(pscan)
struct scanRecord *pscan;
{
  recPvtStruct    *precPvt = (recPvtStruct *)pscan->rpvt;
  posFields       *pPosFields;
  long  status;
  unsigned short  *pPvStat;
  int   i;


  /* General initialization ... */
  if(recScanDebug) {
      precPvt->tickStart = tickGet();
  }

  pscan->cpt = 0;   /* reset point counter */
  precPvt->scanErr = 0;

  /* determine highest valid positioner, readback, trigger, and detector */
  precPvt->valPosPvs = 0;
  precPvt->valRdbkPvs = 0;
  precPvt->valDetPvs = 0;
  precPvt->valTrigPvs = 0;
  pPvStat = &pscan->p1nv;  

  for(i=1; i <= NUM_POS; i++, pPvStat++) {
      if(*pPvStat == PV_OK) precPvt->valPosPvs = i;
  }
  /* let pPvStat continue to increment into Readbacks */
  for(i=1; i <= NUM_POS; i++, pPvStat++) {
      if(*pPvStat == PV_OK) precPvt->valRdbkPvs = i;
  }
  /* let pPvStat continue to increment into Detectors */
  /* flag which detectors should be acquired during scan */
  for(i=1; i <= NUM_DET; i++, pPvStat++) {
      if(*pPvStat == PV_OK) {
          precPvt->valDetPvs = i;
          precPvt->acqDet[i-1] = 1;
      }
      else {
          precPvt->acqDet[i-1] = 0;
      }
  }
  /* let pPvStat continue to increment into Triggers */
  for(i=1; i <= NUM_TRGS; i++, pPvStat++) {
      if(*pPvStat == PV_OK) precPvt->valTrigPvs = i;
  }

  /* if more readbacks than positioners, set valPosPvs to valRdbkPvs */
  if(precPvt->valRdbkPvs > precPvt->valPosPvs) {
      precPvt->valPosPvs = precPvt->valRdbkPvs;
  }
      
  if(recScanDebug > 10) {
      printf("Positioners  : %u\n",precPvt->valPosPvs);
      printf("Readbacks    : %u\n",precPvt->valRdbkPvs);
      printf("Detectors    : %u\n",precPvt->valDetPvs);
      printf("Triggers     : %u\n",precPvt->valTrigPvs);
  }

  /* checkScanLimits must be called to update the current value of each 
     positioner into p_pp. If recScanDontCheckLimits is set, it will return
     successful w/o actually checking */

  if((status = checkScanLimits(pscan))) {
      pscan->exsc = 0;   /* limits didn't pass, abort scan */
      db_post_events(pscan,&pscan->smsg,DBE_VALUE);
      db_post_events(pscan,&pscan->alrt,DBE_VALUE);
      db_post_events(pscan,&pscan->exsc,DBE_VALUE);
      precPvt->phase = IDLE;
      return(status);
  }

  /* Then calculate the starting position */
  precPvt->onTheFly = 0;  /* clear onTheFly flag */
  pPvStat = &pscan->p1nv;
  pPosFields = (posFields *)&pscan->p1pp;
  for(i=0; i < precPvt->valPosPvs; i++, pPosFields++, pPvStat++) {
    if(*pPvStat == PV_OK) {
      /* Figure out starting positions for each positioner */
      if(pPosFields->p_sm == REC_SCAN_MO_TAB) {
          if(pPosFields->p_ar) {
              pPosFields->p_dv = pPosFields->p_pp + pPosFields->p_pa[0];
          }
          else {
              pPosFields->p_dv = pPosFields->p_pa[0];
          }
      }
      else {
          if(pPosFields->p_ar) {
              pPosFields->p_dv = pPosFields->p_pp + pPosFields->p_sp;
          }
          else {
              pPosFields->p_dv = pPosFields->p_sp;
          }
      }
      db_post_events(pscan,&pPosFields->p_dv,DBE_VALUE);
      if(pPosFields->p_sm == REC_SCAN_MO_OTF) {
          precPvt->onTheFly |= 1;  /* set flag if onTheFly */
      }
    }
  }
      
  if((pscan->bsnv == PV_OK) && (precPvt->phase == INIT_SCAN)) {
      precPvt->phase = BEFORE_SCAN;
      sprintf(pscan->smsg,"Before Scan FLNK ...");
      db_post_events(pscan,&pscan->smsg,DBE_VALUE);
  }
  else {
      precPvt->phase = MOVE_MOTORS;
      sprintf(pscan->smsg,"Scanning ...");
      db_post_events(pscan,&pscan->smsg,DBE_VALUE);
  }
  /* request callback to do dbPutFields */
  callbackRequest(&precPvt->doPutsCallback);
  return(OK);
}

static void contScan(pscan)
struct scanRecord *pscan;
{
  recPvtStruct   *precPvt = (recPvtStruct *)pscan->rpvt;
  posFields      *pPosFields = (posFields *)&pscan->p1pp;
  detFields      *pDetFields = (detFields *)&pscan->d1hr;
  recDynLinkPvt  *puserPvt;
  TS_STAMP       currentTs;
  unsigned short *pPvStat;
  unsigned short *pPvStatPos;
  unsigned short i;          
  long  status;
  size_t  nRequest = 1;

  switch (precPvt->phase) {
    case TRIG_DETCTRS:
        /* go output to detector trigger fields */
        callbackRequest(&precPvt->doPutsCallback);
        return;

    case CHECK_MOTORS:
        if(recScanDebug >=5) {
            printf("CHECK_MOTORS  - Point %d\n",precPvt->pscan->cpt);
        }
        /* check if a readback PV and a delta are specified */
        pPvStat = &pscan->r1nv;
        pPvStatPos  = &pscan->p1nv;
        for(i=0; i < NUM_POS; i++, pPosFields++, pPvStatPos++, pPvStat++) {
          if((pPosFields->r_dl != 0) && 
             (*pPvStat == PV_OK) &&
             (*pPvStatPos == PV_OK)) {
            puserPvt = precPvt->caLinkStruct[i+NUM_POS].puserPvt;
            if(puserPvt->dbAddrNv) {
                status = recDynLinkGet(&precPvt->caLinkStruct[i+NUM_POS],
                       &pPosFields->r_cv, &nRequest, 0, 0, 0);
            }
            else {
                status = dbGet(puserPvt->pAddr,DBR_DOUBLE, &pPosFields->r_cv,
                           0,0,NULL);
            }
 
            if((pPosFields->r_dl > 0) &&
              (fabs(pPosFields->p_dv-pPosFields->r_cv) > pPosFields->r_dl)) {
                sprintf(pscan->smsg,"SCAN Aborted: P1 Readback > delta");
                db_post_events(pscan,&pscan->smsg,DBE_VALUE);
                precPvt->scanErr = 1;
                endScan(pscan);
                return;
            }
          } 
        }

        /* if there is a valid detector trigger ...   */
        /*    AND we are NOT in onTheFly              */
        /*       OR  we are doing Point 0             */
        /* go trigger detectors and wait for the next process */
        if(precPvt->valTrigPvs && (!precPvt->onTheFly || (pscan->cpt==0))) {
            /* do detector trigger fields */
            precPvt->phase = TRIG_DETCTRS;
            callbackRequest(&precPvt->doPutsCallback);
            return;
        }

        /* if no validTrigPvs, fall through to READ_DETCTRS */
        /* if on-the-fly mode, fall through to READ_DETCTRS */
        
    case READ_DETCTRS: 
        if(recScanDebug >=5) {
            printf("READ_DETCTRS - Point %d\n",precPvt->pscan->cpt);
        }
        /* Store the appropriate value into the positioner readback array */
        /* from RxCV or PxDV or TIME */
        pPvStat = &pscan->r1nv;
        pPvStatPos  = &pscan->p1nv;
        pPosFields = (posFields *)&pscan->p1pp;
        for(i=0; i < NUM_POS; i++, pPosFields++, pPvStatPos++, pPvStat++) {
          /* if readback PV is OK, use that value */
          if(*pPvStat == PV_OK) {
            puserPvt = precPvt->caLinkStruct[i+NUM_POS].puserPvt;
            if(puserPvt->dbAddrNv) {
                status = recDynLinkGet(&precPvt->caLinkStruct[i+NUM_POS],
                       &pPosFields->r_cv, &nRequest, 0, 0, 0);
            }
            else {
                status = dbGet(puserPvt->pAddr,DBR_DOUBLE, &pPosFields->r_cv,
                           0,0,NULL);
            }

            precPvt->posBufPtr[i].pFill[pscan->cpt] = pPosFields->r_cv;

          }

          /* Does the readback PV = "time" ? */
          else if(((recDynLinkPvt *)
                  (precPvt->caLinkStruct[i+NUM_POS].puserPvt))->ts) {
            TSgetTimeStamp((int)precPvt->pscan->tse,
                           (struct timespec*)&currentTs);
            TsDiffAsDouble(&pPosFields->r_cv, &currentTs, &pscan->time);
            precPvt->posBufPtr[i].pFill[pscan->cpt] = pPosFields->r_cv;
          }
          
          /* Is the positioner PV valid ? */
          else if(*pPvStatPos == PV_OK) {
            /* stuff array with desired value */
            /* if onTheFly and cpt>0,add the step increment to the previous*/
            if((pPosFields->p_sm != REC_SCAN_MO_OTF) || (pscan->cpt == 0)) {
               pPosFields->r_cv = pPosFields->p_dv;
               precPvt->posBufPtr[i].pFill[pscan->cpt] = pPosFields->r_cv;
            } else {
               pPosFields->r_cv =
                 precPvt->posBufPtr[i].pFill[pscan->cpt-1] + pPosFields->p_si;
               precPvt->posBufPtr[i].pFill[pscan->cpt] = pPosFields->r_cv;
            }
          }
          else {
            /* Neither PV is valid, store a 0 */
            pPosFields->r_cv = 0;
            precPvt->posBufPtr[i].pFill[pscan->cpt] = pPosFields->r_cv;
          }
        }

        /* read each valid detector PV, place data in buffered array */
        status = 0;
        pPvStat = &pscan->d1nv;
        pDetFields = (detFields *)&pscan->d1hr;
        for(i=0; i < precPvt->valDetPvs; i++, pDetFields++, pPvStat++) {
          if(precPvt->acqDet[i] && (precPvt->detBufPtr[i].pFill != NULL)) {
            if(*pPvStat == PV_OK) {
              puserPvt = precPvt->caLinkStruct[i+D1_IN].puserPvt;
              if(puserPvt->dbAddrNv) {
                  status |= recDynLinkGet(&precPvt->caLinkStruct[i+D1_IN],
                         &pDetFields->d_cv, &nRequest, 0, 0, 0);
              }
              else {
                  status |= dbGet(puserPvt->pAddr, DBR_FLOAT, &pDetFields->d_cv,
                           0,0, NULL);
              }
            }
            else {
              pDetFields->d_cv = 0;
            }
            precPvt->detBufPtr[i].pFill[pscan->cpt] = pDetFields->d_cv;
          }
        }

        pscan->udf=0;

        pscan->cpt++;
        /* Has number of points been reached ? */
        if(pscan->cpt < (pscan->npts)) {
            /* determine next desired position for each positioner */
            pPosFields = (posFields *)&pscan->p1pp;
            pPvStat = &pscan->p1nv;

            /* figure out next position */
            for(i=0; i < precPvt->valPosPvs; i++, pPosFields++, pPvStat++) {
              if(*pPvStat == PV_OK) {
                  switch(pPosFields->p_sm) {
                    case REC_SCAN_MO_LIN:
                        pPosFields->p_dv = pPosFields->p_dv + pPosFields->p_si;
                      break;
                    case REC_SCAN_MO_TAB:
                        if(pPosFields->p_ar) {
                            pPosFields->p_dv = pPosFields->p_pp + 
                                               pPosFields->p_pa[pscan->cpt];
                        }
                        else {
                            pPosFields->p_dv = pPosFields->p_pa[pscan->cpt];
                        }
                      break;
                    case REC_SCAN_MO_OTF:
                        if(pPosFields->p_ar) {
                            pPosFields->p_dv = pPosFields->p_pp +
                                               pPosFields->p_ep;
                        }
                        else {
                            pPosFields->p_dv = pPosFields->p_ep;
                        }
                      break;
                  }
              }
            }

            /* request callback to move motors to new positions */
            precPvt->phase = MOVE_MOTORS; 
            /* For onTheFly, it will fall through to TRIG_DETCTRS */
            /* after MOVE_MOTORS */
            callbackRequest(&precPvt->doPutsCallback);
            return;
        }
        else {
            endScan(pscan);   /* scan is successfully complete */
            sprintf(pscan->smsg,"SCAN Complete");
            db_post_events(pscan,&pscan->smsg,DBE_VALUE);
            precPvt->scanErr = 0;
            return;
        }
   } 
}

static void endScan(pscan)
struct scanRecord *pscan;
{

  recPvtStruct    *precPvt = (recPvtStruct *)pscan->rpvt;
  int              counter;
  unsigned short  *pPvStat, i;
  detFields       *pDetFields;


  /* Done with scan. Do we want to fill the remainder of the
     array with 0 or something ? */
  /* It turns out that medm plots the whole array, so for it
     to look right the remainder of the arrays will be filled
     with the last values. This will cause medm to plot the
     same point over and over again, but it will look correct */

  counter = pscan->cpt;
  if(recScanDebug) {
      printf("endScan(): Fill Counter - %d\n",counter);
  }
  while (counter < pscan->mpts) {   
      /* Fill valid detector arrays with last value */
      pDetFields = (detFields *)&pscan->d1hr;
      pPvStat = &pscan->d1nv;
      for(i=0; i < precPvt->valDetPvs; i++, pDetFields++, pPvStat++) {
        if(precPvt->acqDet[i]) {
          precPvt->detBufPtr[i].pFill[counter] = pDetFields->d_cv;   
        }
      }
      /* Fill in the readback arrays with last values */
      precPvt->posBufPtr[0].pFill[counter] = 
              precPvt->posBufPtr[0].pFill[counter-1];

      precPvt->posBufPtr[1].pFill[counter] = 
              precPvt->posBufPtr[1].pFill[counter-1];

      precPvt->posBufPtr[2].pFill[counter] = 
              precPvt->posBufPtr[2].pFill[counter-1];

      precPvt->posBufPtr[3].pFill[counter] = 
              precPvt->posBufPtr[3].pFill[counter-1];

      counter++;
  }

  /* Switch validBuf flag and fill pointers */
  if(precPvt->validBuf == A_BUFFER) {
      precPvt->validBuf = B_BUFFER;
      precPvt->posBufPtr[0].pFill = precPvt->posBufPtr[0].pBufA;
      precPvt->posBufPtr[1].pFill = precPvt->posBufPtr[1].pBufA;
      precPvt->posBufPtr[2].pFill = precPvt->posBufPtr[2].pBufA;
      precPvt->posBufPtr[3].pFill = precPvt->posBufPtr[3].pBufA;
      for(i=0; i < NUM_DET; i++) {
          precPvt->detBufPtr[i].pFill = precPvt->detBufPtr[i].pBufA;
      }
  } else {
      precPvt->validBuf = A_BUFFER;
      precPvt->posBufPtr[0].pFill = precPvt->posBufPtr[0].pBufB;
      precPvt->posBufPtr[1].pFill = precPvt->posBufPtr[1].pBufB;
      precPvt->posBufPtr[2].pFill = precPvt->posBufPtr[2].pBufB;
      precPvt->posBufPtr[3].pFill = precPvt->posBufPtr[3].pBufB;
      for(i=0; i < NUM_DET; i++) {
          precPvt->detBufPtr[i].pFill = precPvt->detBufPtr[i].pBufB;
      }
  }
      
  pscan->exsc = 0;  /* done with scan */
  
  /* Check the "after scan flag to see what to do about the positioners */
  /* Also see if there is an After Scan Link to process */

  if(pscan->pasm || (pscan->asnv == PV_OK)) {
      precPvt->phase = AFTER_SCAN;
      /* request callback to do dbPutFields */
      callbackRequest(&precPvt->doPutsCallback);
  }
  else {
      precPvt->phase = IDLE;
  }
  return;

}


/****************************************************************************
 *
 * This is the code that is executed to initiate the next
 * step in the scan or trigger detectors (does the "puts" to the
 * reassignable links). Since the recDynLinkPut request is actually
 * done with a separate task, one need not worry about lock sets.
 * 
 * Note that the argument (which is the address of the CALLBACK structure),
 * is interpreted to be a pointer to the record private area. 
 *
 ***************************************************************************/
void doPuts(precPvt)
   recPvtStruct *precPvt;
{
  struct scanRecord *pscan = precPvt->pscan;
  recDynLinkPvt  *puserPvt;
  posFields      *pPosFields;
  unsigned short *pPvStat;
  int   i;
  long status;

  switch (precPvt->phase) {
    case MOVE_MOTORS:
        if(recScanDebug >=5) {
            printf("MOVE_MOTORS  - Point %d\n",precPvt->pscan->cpt);
        }
        /* Schedule the next phase as CHECK_MOTORS */
        precPvt->phase = CHECK_MOTORS;

        pPosFields = (posFields *)&precPvt->pscan->p1pp;
        pPvStat = &precPvt->pscan->p1nv;

        /* for each valid positioner, write the desired position */
        /* if positioner is "on-the-fly", only write if step 0 or 1 */
        for(i=0; i < precPvt->valPosPvs; i++, pPosFields++, pPvStat++) {
          if((*pPvStat == PV_OK) && 
              (!((pPosFields->p_sm == REC_SCAN_MO_OTF) &&
                 (precPvt->pscan->cpt > 1)) || precPvt->phase == AFTER_SCAN)) {
                puserPvt = precPvt->caLinkStruct[i].puserPvt;
                if(puserPvt->dbAddrNv) {
                  status = recDynLinkPut(&precPvt->caLinkStruct[i+P1_OUT],
                              &(pPosFields->p_dv), 1);
                } else {
                  status = dbPutField(puserPvt->pAddr,DBF_DOUBLE,
                              &(pPosFields->p_dv), 1);
                }
          }
        }

        if(!(precPvt->onTheFly && precPvt->pscan->cpt))
            break;
        /* Fall through to TRIG_DETCTRS if in onTheFly mode and flying */

    case TRIG_DETCTRS:
        if(recScanDebug >=5) {
            printf("TRIG_DETCTRS - Point %d\n",precPvt->pscan->cpt);
        }
        /* Schedule the next phase  */
        if(!precPvt->onTheFly || (precPvt->pscan->cpt == 0)) {
            precPvt->phase = READ_DETCTRS;
        }
        else {
            precPvt->phase = CHECK_MOTORS;
        }

        /* for each valid detector trigger, write the desired value */
        if(!precPvt->pscan->t1nv) {
            puserPvt = precPvt->caLinkStruct[T1_OUT].puserPvt;
            if(puserPvt->dbAddrNv) {
                status = recDynLinkPut(&precPvt->caLinkStruct[T1_OUT],
                            &(precPvt->pscan->t1cd), 1);
            } else {
                status = dbPutField(puserPvt->pAddr,DBF_FLOAT,
                            &(precPvt->pscan->t1cd), 1);
            }
        }
        if(!precPvt->pscan->t2nv) {
            puserPvt = precPvt->caLinkStruct[T2_OUT].puserPvt;
            if(puserPvt->dbAddrNv) {
                status = recDynLinkPut(&precPvt->caLinkStruct[T2_OUT],
                            &(precPvt->pscan->t2cd), 1);
            } else {
                status = dbPutField(puserPvt->pAddr,DBF_FLOAT,
                            &(precPvt->pscan->t2cd), 1);
            }
        }
        break;

    case BEFORE_SCAN:
        if(recScanDebug >=5)  printf("BEFORE_SCAN Fwd Lnk\n");
        if(precPvt->pscan->bsnv == OK) {
            puserPvt = precPvt->caLinkStruct[BS_OUT].puserPvt;
            if(puserPvt->dbAddrNv) {
                status = recDynLinkPut(&precPvt->caLinkStruct[BS_OUT],
                            &(precPvt->pscan->bscd), 1);
            } else {
                status = dbPutField(puserPvt->pAddr,DBF_FLOAT,
                            &(precPvt->pscan->bscd), 1);
            }
        }
        /* Leave the phase at BEFORE_SCAN */
        break;
        
    case AFTER_SCAN:
        /* If Retrace indicates motors must move ... */
        if(precPvt->pscan->pasm) {
             if(recScanDebug >=5)  printf("RETRACE\n");
             pPosFields = (posFields *)&precPvt->pscan->p1pp;
             pPvStat = &precPvt->pscan->p1nv;
             for(i=0; i < precPvt->valPosPvs; i++, pPosFields++, pPvStat++) {
                 if(*pPvStat == PV_OK) {
                     /* Figure out where to go ... */
                     if(precPvt->pscan->pasm == REC_SCAN_AS_BS) {
                         pPosFields->p_dv = pPosFields->p_pp;
                     } else {
                         if(pPosFields->p_sm == REC_SCAN_MO_TAB) {
                           if(pPosFields->p_ar) {
                             pPosFields->p_dv =
                                       pPosFields->p_pp + pPosFields->p_pa[0];
                           }
                           else {
                             pPosFields->p_dv = pPosFields->p_pa[0];
                           }
                         }
                         else {
                           if(pPosFields->p_ar) {
                             pPosFields->p_dv =
                                       pPosFields->p_pp + pPosFields->p_sp;
                           }
                           else {
                             pPosFields->p_dv = pPosFields->p_sp;
                           }
                         }
                     }

                     /* Command motor */
                     puserPvt = precPvt->caLinkStruct[i].puserPvt;
                     if(puserPvt->dbAddrNv) {
                       status = recDynLinkPut(&precPvt->caLinkStruct[i+P1_OUT],
                                    &(pPosFields->p_dv), 1);
                     } else {
                       status = dbPutField(puserPvt->pAddr,DBF_DOUBLE,
                                    &(pPosFields->p_dv), 1);
                     }
                 }
             }
         }

        /* If an After Scan Link PV is valid, execute it */
        if(precPvt->pscan->asnv == PV_OK) {
            if(recScanDebug >=5)  printf("AFTER_SCAN Fwd Lnk\n");
            puserPvt = precPvt->caLinkStruct[AS_OUT].puserPvt;
            if(puserPvt->dbAddrNv) {
                status = recDynLinkPut(&precPvt->caLinkStruct[AS_OUT],
                            &(precPvt->pscan->ascd), 1);
            } else {
                status = dbPutField(puserPvt->pAddr,DBF_FLOAT,
                            &(precPvt->pscan->ascd), 1);
            }
        }

        precPvt->phase = IDLE;
        break;

    case IDLE:
    case PREVIEW:
        /* This must be a request to move a positioner interactivly ... */
        
        pPvStat = &precPvt->pscan->p1nv;
        pPosFields = (posFields *)&precPvt->pscan->p1pp;
        for(i=0; i < NUM_POS; i++, pPosFields++, pPvStat++) {
          if((*pPvStat == PV_OK) && precPvt->movPos[i]) {
              puserPvt = precPvt->caLinkStruct[i].puserPvt;
              status = dbPutField(puserPvt->pAddr,DBF_DOUBLE,
                              &(pPosFields->p_dv), 1);
              precPvt->movPos[i] = 0;
          }
        }
        break;

    case SCAN_PENDING:
        checkConnections(pscan);
        /* If .EXSC == 0, or no longer in SCAN_PENDING */
        if((!pscan->exsc) || (precPvt->phase != SCAN_PENDING)) {
            if(recScanDebug) printf("scanPending - no effect\n");
            return;
        }

        precPvt->phase = IDLE;

        /* Only scanOnce if no bad PV's */
        if(pscan->exsc && !precPvt->badOutputPv && !precPvt->badInputPv)  {
            pscan->alrt = 0;
            db_post_events(pscan,&pscan->alrt,DBE_VALUE);
            strcpy(pscan->smsg,"");
            db_post_events(pscan,&pscan->smsg,DBE_VALUE);
            scanOnce(pscan);
            if(recScanDebug) printf("scanPending - start scan\n");
        }
        else {
            pscan->exsc = 0;
            db_post_events(pscan,&pscan->exsc,DBE_VALUE);
            sprintf(pscan->smsg,"Scan aborted, PV(s) not connected");
            db_post_events(pscan,&pscan->smsg,DBE_VALUE);
            if(recScanDebug) printf("scanPending - end scan\n");
        }
        break;
    default:
	break;
  }
}




/* routine to resolve and adjust linear scan definition.
 * Might get complicated !!!
 */

static void adjLinParms(paddr)      
    struct dbAddr       *paddr;
{
    struct scanRecord   *pscan =   (struct scanRecord *)(paddr->precord);
    recPvtStruct        *precPvt = (recPvtStruct *)pscan->rpvt;
    struct posFields    *pParms =  (posFields *)&precPvt->pscan->p1pp;

    int                 special_type = paddr->special;
    int                 i;
    int                 foundOne = 0;

    /* determine which positioner */
    for(i=0; i<NUM_POS; i++, pParms++) {
        if ((paddr->pfield >= (void *)&pParms->p_pp) &&
            (paddr->pfield <= (void *)&pParms->p_pr)) {
            
            foundOne = 1;
            break;
        }
    }
    if(!foundOne) return;   /* couldn't determine positioner */

    if(pParms->p_sm == REC_SCAN_MO_TAB) {
        /* if positioner is in table mode, zero parms and return */
        zeroPosParms(pscan, (unsigned short) i);
        sprintf(pscan->smsg,"Positioner #%1d is in Table Mode !",i+1);
        pscan->alrt = 1;
        return;
    }

    if(recScanDebug) printf("Positioner %d\n",i);
    switch(special_type) {
      case(SPC_SC_S):              /* start position changed */
        /* if step increment/center/width are not frozen, change them  */
        if(!pParms->p_fi && !pParms->p_fc && !pParms->p_fw) {
            pParms->p_si = (pParms->p_ep - pParms->p_sp)/(pscan->npts - 1);
            db_post_events(pscan,&pParms->p_si,DBE_VALUE);
            pParms->p_cp = (pParms->p_ep + pParms->p_sp)/2;
            db_post_events(pscan,&pParms->p_cp,DBE_VALUE);
            pParms->p_wd = (pParms->p_ep - pParms->p_sp);
            db_post_events(pscan,&pParms->p_wd,DBE_VALUE);
            return;
        /* if # of points/center/width are not frozen, change them  */
        } else if(!pscan->fpts && !pParms->p_fc && !pParms->p_fw) {
            pscan->npts = ((pParms->p_ep - pParms->p_sp)/(pParms->p_si)) + 1;
            if(pscan->npts > pscan->mpts) {
              pscan->npts = pscan->mpts;
              sprintf(pscan->smsg,"P%1d Request Exceeded Maximum Points!",i+1);
              /* adjust changed field to be consistent */
              pParms->p_sp = pParms->p_ep-(pParms->p_si * (pscan->npts - 1));
              db_post_events(pscan,&pParms->p_sp,DBE_VALUE);
              pscan->alrt = 1;
            }
            db_post_events(pscan,&pscan->npts,DBE_VALUE);
            pParms->p_cp = (pParms->p_ep + pParms->p_sp)/2;
            db_post_events(pscan,&pParms->p_cp,DBE_VALUE);
            pParms->p_wd = (pParms->p_ep - pParms->p_sp);
            db_post_events(pscan,&pParms->p_wd,DBE_VALUE);
            precPvt->nptsCause = i; /* indicate cause of # of points changed */
            changedNpts(pscan);
            return;
        /* if end/center are not frozen, change them  */
        } else if(!pParms->p_fe && !pParms->p_fc) {      
            pParms->p_ep = pParms->p_sp +
                               ((pscan->npts - 1) * pParms->p_si);
            db_post_events(pscan,&pParms->p_ep,DBE_VALUE);
            pParms->p_cp = (pParms->p_ep + pParms->p_sp)/2;
            db_post_events(pscan,&pParms->p_cp,DBE_VALUE);
            return;
        /* if step increment/end/width are not frozen, change them  */
        } else if(!pParms->p_fi && !pParms->p_fe && !pParms->p_fw) { 
            pParms->p_wd = (pParms->p_cp - pParms->p_sp) * 2;
            db_post_events(pscan,&pParms->p_wd,DBE_VALUE);
            pParms->p_si = pParms->p_wd/(pscan->npts - 1);
            db_post_events(pscan,&pParms->p_si,DBE_VALUE);
            pParms->p_ep = (pParms->p_sp + pParms->p_wd);
            db_post_events(pscan,&pParms->p_ep,DBE_VALUE);
            return;
        /* if # of points/end/width are not frozen, change them  */
        } else if(!pscan->fpts && !pParms->p_fe && !pParms->p_fw) { 
            pscan->npts = ((pParms->p_cp - pParms->p_sp)*2/(pParms->p_si)) + 1;
            if(pscan->npts > pscan->mpts) {
              pscan->npts = pscan->mpts;
              sprintf(pscan->smsg,"P%1d Request Exceeded Maximum Points!",i+1);
              /* adjust changed field to be consistent */
              pParms->p_sp=pParms->p_cp-((pParms->p_si*(pscan->npts - 1))/2);
              db_post_events(pscan,&pParms->p_sp,DBE_VALUE);
              pscan->alrt = 1;
            }
            db_post_events(pscan,&pscan->npts,DBE_VALUE);
            pParms->p_ep = pParms->p_sp + ((pscan->npts - 1) * pParms->p_si);
            db_post_events(pscan,&pParms->p_cp,DBE_VALUE);
            pParms->p_wd = (pParms->p_ep - pParms->p_sp);
            db_post_events(pscan,&pParms->p_wd,DBE_VALUE);
            precPvt->nptsCause = i; /* indicate cause of # of points changed */
            changedNpts(pscan);
            return;
        }
        break;

      case(SPC_SC_I):              /* step increment changed */
        /* if # of points is not frozen, change it  */
        if(!pscan->fpts) {
            pscan->npts = ((pParms->p_ep - pParms->p_sp)/(pParms->p_si)) + 1;
            if(pscan->npts > pscan->mpts) {
              pscan->npts = pscan->mpts;
              sprintf(pscan->smsg,"P%1d Request Exceeded Maximum Points !",i+1);
              /* adjust changed field to be consistent */
              pParms->p_si = (pParms->p_ep - pParms->p_sp)/(pscan->npts - 1);
              db_post_events(pscan,&pParms->p_si,DBE_VALUE);
              pscan->alrt = 1;
            }
            db_post_events(pscan,&pscan->npts,DBE_VALUE);
            precPvt->nptsCause = i; /* indicate cause of # of points changed */
            changedNpts(pscan);
            return;
        /* if end/center/width are not frozen, change them */
        } else if(!pParms->p_fe && !pParms->p_fc && !pParms->p_fw) { 
            pParms->p_ep = pParms->p_sp + ((pscan->npts - 1) * pParms->p_si);
            db_post_events(pscan,&pParms->p_ep,DBE_VALUE);
            pParms->p_cp = (pParms->p_ep + pParms->p_sp)/2;
            db_post_events(pscan,&pParms->p_cp,DBE_VALUE);
            pParms->p_wd = (pParms->p_ep - pParms->p_sp);
            db_post_events(pscan,&pParms->p_wd,DBE_VALUE);
            return;
        /* if start/center/width are not frozen, change them */
        } else if(!pParms->p_fs && !pParms->p_fc && !pParms->p_fw) { 
            pParms->p_sp = pParms->p_ep - ((pscan->npts - 1) * pParms->p_si);
            db_post_events(pscan,&pParms->p_sp,DBE_VALUE);
            pParms->p_cp = (pParms->p_ep + pParms->p_sp)/2;
            db_post_events(pscan,&pParms->p_cp,DBE_VALUE);
            pParms->p_wd = (pParms->p_ep - pParms->p_sp);
            db_post_events(pscan,&pParms->p_wd,DBE_VALUE);
            return;
        /* if start/end/width are not frozen, change them */
        } else if(!pParms->p_fs && !pParms->p_fe && !pParms->p_fw) { 
            pParms->p_sp = pParms->p_cp - ((pscan->npts - 1) * pParms->p_si)/2;
            db_post_events(pscan,&pParms->p_sp,DBE_VALUE);
            pParms->p_ep = pParms->p_sp + ((pscan->npts - 1) * pParms->p_si);
            db_post_events(pscan,&pParms->p_ep,DBE_VALUE);
            pParms->p_wd = (pParms->p_ep - pParms->p_sp);
            db_post_events(pscan,&pParms->p_wd,DBE_VALUE);
            return;
        } else {                   /* too constrained !! */
            pParms->p_si = (pParms->p_ep - pParms->p_sp)/(pscan->npts - 1);
            db_post_events(pscan,&pParms->p_si,DBE_VALUE);
            sprintf(pscan->smsg,"P%1d SCAN Parameters Too Constrained !",i+1);
            pscan->alrt = 1;
            return;
        }
        break;

      case(SPC_SC_E):              /* end position changed   */
        /* if step increment/center/width are not frozen, change them */
        if(!pParms->p_fi && !pParms->p_fc && !pParms->p_fw) {
            pParms->p_si = (pParms->p_ep - pParms->p_sp)/(pscan->npts - 1);
            db_post_events(pscan,&pParms->p_si,DBE_VALUE);
            pParms->p_cp = (pParms->p_ep + pParms->p_sp)/2;
            db_post_events(pscan,&pParms->p_cp,DBE_VALUE);
            pParms->p_wd = (pParms->p_ep - pParms->p_sp);
            db_post_events(pscan,&pParms->p_wd,DBE_VALUE);
            return;
        /* if # of points/center/width are not frozen, change them */
        } else if(!pscan->fpts && !pParms->p_fc && !pParms->p_fw) {
            pscan->npts = ((pParms->p_ep - pParms->p_sp)/(pParms->p_si)) + 1;
            if(pscan->npts > pscan->mpts) {
              pscan->npts = pscan->mpts;
              sprintf(pscan->smsg,"P%1d Request Exceeded Maximum Points !",i+1);
              /* adjust changed field to be consistent */
              pParms->p_ep=pParms->p_sp + (pParms->p_si * (pscan->npts - 1));
              db_post_events(pscan,&pParms->p_ep,DBE_VALUE);
              pscan->alrt = 1;
            }
            db_post_events(pscan,&pscan->npts,DBE_VALUE);
            pParms->p_cp = (pParms->p_ep + pParms->p_sp)/2;
            db_post_events(pscan,&pParms->p_cp,DBE_VALUE);
            pParms->p_wd = (pParms->p_ep - pParms->p_sp);
            db_post_events(pscan,&pParms->p_wd,DBE_VALUE);
            precPvt->nptsCause = i; /* indicate cause of # of points changed */
            changedNpts(pscan);
            return;
        /* if start/center are not frozen, change them  */
        } else if(!pParms->p_fs && !pParms->p_fc) {
            pParms->p_sp = pParms->p_ep - ((pscan->npts - 1) * pParms->p_si);
            db_post_events(pscan,&pParms->p_sp,DBE_VALUE);
            pParms->p_cp = (pParms->p_ep + pParms->p_sp)/2;
            db_post_events(pscan,&pParms->p_cp,DBE_VALUE);
            return;
        /* if step start/width/increment are not frozen, change them */
        } else if(!pParms->p_fs && !pParms->p_fw && !pParms->p_fi) {
            pParms->p_wd = (pParms->p_ep - pParms->p_cp) * 2;
            db_post_events(pscan,&pParms->p_wd,DBE_VALUE);
            pParms->p_sp = pParms->p_ep - pParms->p_wd;
            db_post_events(pscan,&pParms->p_sp,DBE_VALUE);
            pParms->p_si = (pParms->p_ep - pParms->p_sp)/(pscan->npts - 1);
            db_post_events(pscan,&pParms->p_si,DBE_VALUE);
            return;
        /* if # of points/start/width are not frozen, change them */
        } else if(!pscan->fpts && !pParms->p_fs && !pParms->p_fw) {
            pscan->npts=(((pParms->p_ep - pParms->p_cp)*2)/(pParms->p_si)) + 1;
            if(pscan->npts > pscan->mpts) {
              pscan->npts = pscan->mpts;
              sprintf(pscan->smsg,"P%1d Request Exceeded Maximum Points !",i+1);
              /* adjust changed field to be consistent */
              pParms->p_ep=pParms->p_cp+(pParms->p_si * (pscan->npts - 1)/2);
              db_post_events(pscan,&pParms->p_ep,DBE_VALUE);
              pscan->alrt = 1;
            }
            db_post_events(pscan,&pscan->npts,DBE_VALUE);
            pParms->p_wd = (pParms->p_ep - pParms->p_cp) * 2;
            db_post_events(pscan,&pParms->p_wd,DBE_VALUE);
            pParms->p_sp = pParms->p_ep - pParms->p_wd; 
            db_post_events(pscan,&pParms->p_sp,DBE_VALUE);
            precPvt->nptsCause = i; /* indicate cause of # of points changed */
            changedNpts(pscan);
            return;
        } else {                   /* too constrained !! */
            pParms->p_ep = pParms->p_sp + ((pscan->npts - 1) * pParms->p_si);
            db_post_events(pscan,&pParms->p_ep,DBE_VALUE);
            sprintf(pscan->smsg,"P%1d SCAN Parameters Too Constrained !",i+1);
            pscan->alrt = 1;
            return;
        }
        break;

      case(SPC_SC_C):              /* center position changed   */
        /* if start/end are not frozen, change them */
        if(!pParms->p_fs && !pParms->p_fe) { 
            pParms->p_sp = pParms->p_cp - ((pscan->npts - 1) * pParms->p_si)/2;
            db_post_events(pscan,&pParms->p_sp,DBE_VALUE);
            pParms->p_ep = pParms->p_sp + ((pscan->npts - 1) * pParms->p_si);
            db_post_events(pscan,&pParms->p_ep,DBE_VALUE);
            return;
        /* if end/inc/width are not frozen, change them */
        } else if(!pParms->p_fe && !pParms->p_fi && !pParms->p_fw) {
            pParms->p_wd = (pParms->p_cp - pParms->p_sp)*2;
            db_post_events(pscan,&pParms->p_wd,DBE_VALUE);
            pParms->p_ep = pParms->p_sp + pParms->p_wd;
            db_post_events(pscan,&pParms->p_ep,DBE_VALUE);
            pParms->p_si = (pParms->p_ep - pParms->p_sp)/(pscan->npts - 1);
            db_post_events(pscan,&pParms->p_si,DBE_VALUE);
            return;
        /* if start/inc/width are not frozen, change them */
        } else if(!pParms->p_fs && !pParms->p_fi && !pParms->p_fw) { 
            pParms->p_wd = (pParms->p_ep - pParms->p_cp)*2;
            db_post_events(pscan,&pParms->p_wd,DBE_VALUE);
            pParms->p_sp = pParms->p_ep - pParms->p_wd;
            db_post_events(pscan,&pParms->p_sp,DBE_VALUE);
            pParms->p_si = (pParms->p_ep - pParms->p_sp)/(pscan->npts - 1);
            db_post_events(pscan,&pParms->p_si,DBE_VALUE);
            return;
        /* if # of points/end/width are not frozen, change them */
        } else if(!pscan->fpts && !pParms->p_fe && !pParms->p_fw) {
            pscan->npts=(((pParms->p_cp - pParms->p_sp)*2)/(pParms->p_si)) + 1;
            if(pscan->npts > pscan->mpts) {
              pscan->npts = pscan->mpts;
              sprintf(pscan->smsg,"P%1d Request Exceeded Maximum Points !",i+1);
              /* adjust changed field to be consistent */
              pParms->p_cp=pParms->p_sp+(pParms->p_si * (pscan->npts - 1)/2);
              db_post_events(pscan,&pParms->p_cp,DBE_VALUE);
              pscan->alrt = 1;
            }
            db_post_events(pscan,&pscan->npts,DBE_VALUE);
            pParms->p_wd = (pParms->p_cp - pParms->p_sp) * 2;
            db_post_events(pscan,&pParms->p_wd,DBE_VALUE);
            pParms->p_ep = pParms->p_sp + pParms->p_wd;
            db_post_events(pscan,&pParms->p_ep,DBE_VALUE);
            precPvt->nptsCause = i; /* indicate cause of # of points changed */
            changedNpts(pscan);
            return;
        /* if # of points/start/width are not frozen, change them */
        } else if(!pscan->fpts && !pParms->p_fs && !pParms->p_fw) {
            pscan->npts=(((pParms->p_ep - pParms->p_cp)*2)/(pParms->p_si)) + 1;
            if(pscan->npts > pscan->mpts) {
              pscan->npts = pscan->mpts;
              sprintf(pscan->smsg,"P%1d Request Exceeded Maximum Points !",i+1);
              /* adjust changed field to be consistent */
              pParms->p_cp=pParms->p_ep-(pParms->p_si * (pscan->npts - 1)/2);
              db_post_events(pscan,&pParms->p_cp,DBE_VALUE);
              pscan->alrt = 1;
            }
            db_post_events(pscan,&pscan->npts,DBE_VALUE);
            pParms->p_wd = (pParms->p_ep - pParms->p_cp) * 2;
            db_post_events(pscan,&pParms->p_wd,DBE_VALUE);
            pParms->p_sp = pParms->p_ep - pParms->p_wd; 
            db_post_events(pscan,&pParms->p_sp,DBE_VALUE);
            precPvt->nptsCause = i; /* indicate cause of # of points changed */
            changedNpts(pscan);
            return;
        } else {                   /* too constrained !! */
            pParms->p_cp = pParms->p_sp + ((pscan->npts - 1) * pParms->p_si)/2;
            db_post_events(pscan,&pParms->p_cp,DBE_VALUE);
            sprintf(pscan->smsg,"P%1d SCAN Parameters Too Constrained !",i+1);
            pscan->alrt = 1;
            return;
        }
        break;

      case(SPC_SC_W):              /* width position changed   */
        /* if step start/inc/end are not frozen, change them */
        if(!pParms->p_fs && !pParms->p_fi && !pParms->p_fe) { 
            pParms->p_si = pParms->p_wd/(pscan->npts - 1);
            db_post_events(pscan,&pParms->p_si,DBE_VALUE);
            pParms->p_sp = pParms->p_cp - ((pscan->npts - 1) * pParms->p_si)/2;
            db_post_events(pscan,&pParms->p_sp,DBE_VALUE);
            pParms->p_ep = pParms->p_sp + ((pscan->npts - 1) * pParms->p_si);
            db_post_events(pscan,&pParms->p_ep,DBE_VALUE);
            return;
        /* if # of points/start/end are not frozen, change them */
        } else if(!pscan->fpts && !pParms->p_fs && !pParms->p_fe) {
            pscan->npts = (pParms->p_wd/pParms->p_si) + 1;
            if(pscan->npts > pscan->mpts) {
              pscan->npts = pscan->mpts;
              sprintf(pscan->smsg,"P%1d Request Exceeded Maximum Points !",i+1);
              /* adjust changed field to be consistent */
              pParms->p_wd = (pParms->p_si * (pscan->npts - 1));
              db_post_events(pscan,&pParms->p_wd,DBE_VALUE);
              pscan->alrt = 1;
            }
            db_post_events(pscan,&pscan->npts,DBE_VALUE);
            pParms->p_sp = pParms->p_cp - ((pscan->npts - 1) * pParms->p_si)/2;
            db_post_events(pscan,&pParms->p_sp,DBE_VALUE);
            pParms->p_ep = pParms->p_sp + ((pscan->npts - 1) * pParms->p_si);
            db_post_events(pscan,&pParms->p_ep,DBE_VALUE);
            precPvt->nptsCause = i; /* indicate cause of # of points changed */
            changedNpts(pscan);
            return;
        /* if center/end/inc are not frozen, change them */
        } else if(!pParms->p_fc && !pParms->p_fe && !pParms->p_fi) { 
            pParms->p_si = pParms->p_wd/(pscan->npts - 1);
            db_post_events(pscan,&pParms->p_si,DBE_VALUE);
            pParms->p_ep = pParms->p_sp + ((pscan->npts - 1) * pParms->p_si);
            db_post_events(pscan,&pParms->p_ep,DBE_VALUE);
            pParms->p_cp = (pParms->p_ep + pParms->p_sp)/2;
            db_post_events(pscan,&pParms->p_cp,DBE_VALUE);
            return;
        /* if start/center/inc are not frozen, change them */
        } else if(!pParms->p_fs && !pParms->p_fc && !pParms->p_fi) { 
            pParms->p_si = pParms->p_wd/(pscan->npts - 1);
            db_post_events(pscan,&pParms->p_si,DBE_VALUE);
            pParms->p_sp = pParms->p_ep - ((pscan->npts - 1) * pParms->p_si);
            db_post_events(pscan,&pParms->p_sp,DBE_VALUE);
            pParms->p_cp = (pParms->p_ep + pParms->p_sp)/2;
            db_post_events(pscan,&pParms->p_cp,DBE_VALUE);
            return;
        /* if # of points/center/end are not frozen, change them */
        } else if(!pscan->fpts && !pParms->p_fc && !pParms->p_fe) {
            pscan->npts = (pParms->p_wd/pParms->p_si) + 1;
            if(pscan->npts > pscan->mpts) {
              pscan->npts = pscan->mpts;
              sprintf(pscan->smsg,"P%1d Request Exceeded Maximum Points !",i+1);
              /* adjust changed field to be consistent */
              pParms->p_wd = (pParms->p_si * (pscan->npts - 1));
              db_post_events(pscan,&pParms->p_wd,DBE_VALUE);
              pscan->alrt = 1;
            }
            db_post_events(pscan,&pscan->npts,DBE_VALUE);
            pParms->p_ep = pParms->p_sp + ((pscan->npts - 1) * pParms->p_si);
            db_post_events(pscan,&pParms->p_ep,DBE_VALUE);
            pParms->p_cp = (pParms->p_ep + pParms->p_sp)/2;
            db_post_events(pscan,&pParms->p_cp,DBE_VALUE);
            precPvt->nptsCause = i; /* indicate cause of # of points changed */
            changedNpts(pscan);
            return;
        /* if # of points/start/center are not frozen, change them */
        } else if(!pscan->fpts && !pParms->p_fs && !pParms->p_fc) {
            pscan->npts = (pParms->p_wd/pParms->p_si) + 1;
            if(pscan->npts > pscan->mpts) {
              pscan->npts = pscan->mpts;
              sprintf(pscan->smsg,"P%1d Request Exceeded Maximum Points !",i+1);
              /* adjust changed field to be consistent */
              pParms->p_wd = (pParms->p_si * (pscan->npts - 1));
              db_post_events(pscan,&pParms->p_wd,DBE_VALUE);
              pscan->alrt = 1;
            }
            db_post_events(pscan,&pscan->npts,DBE_VALUE);
            pParms->p_sp = pParms->p_ep - ((pscan->npts - 1) * pParms->p_si);
            db_post_events(pscan,&pParms->p_sp,DBE_VALUE);
            pParms->p_cp = (pParms->p_ep + pParms->p_sp)/2;
            db_post_events(pscan,&pParms->p_cp,DBE_VALUE);
            precPvt->nptsCause = i; /* indicate cause of # of points changed */
            changedNpts(pscan);
            return;
        } else {                   /* too constrained !! */
            pParms->p_wd = (pParms->p_ep - pParms->p_sp);
            db_post_events(pscan,&pParms->p_wd,DBE_VALUE);
            sprintf(pscan->smsg,"P%1d SCAN Parameters Too Constrained !",i+1);
            pscan->alrt = 1;
            return;
        }
        break;

      default:
        return;
    }
}
  


/* This routine attempts to bring all linear scan parameters into
 * "consistency". It is used at init and when the # of pts changes.
 * If the number of points changed because of a change in a Positioner's
 * scan parameters, that positioner is skipped to make sure that there is
 * no conflict with that logic. Any positioner in TABLE mode is also skipped.
 *
 * Any positioner that is in TABLE mode is checked to make sure the last
 * table loaded into p_pa had enough points for the new npts. If not, the
 * operator is warned
 *
 */

static void changedNpts(pscan)
    struct scanRecord   *pscan;
{

    recPvtStruct        *precPvt = (recPvtStruct *)pscan->rpvt;
    posFields           *pParms = (posFields *)&pscan->p1pp;
    int                  i;
    unsigned short       freezeState = 0;

    /* for each positioner, calculate scan params as best as we can */
    /* if the positioner is in table mode, don't touch linear scan parms !*/
    for(i=0; i<NUM_POS; i++, pParms++) {
      /* Check if Positioner is in TABLE Mode */
      if(pParms->p_sm == REC_SCAN_MO_TAB) {
          if(precPvt->tablePts[i] < pscan->npts) {
              sprintf(pscan->smsg,"Pts in P%d Table < # of Steps",i+1);
              if(!pscan->alrt) {
                  pscan->alrt = 1;
              }
          } 
      }
      /* Skip the positioner that caused this */
      else if(i != precPvt->nptsCause) {
        /* Adjust linear scan params as best we can */

        /* develop freezeState switching word ... */
        /* START_FRZ | STEP_FRZ | END_FRZ | CENTER_FRZ | WIDTH_FRZ */

        freezeState = (pParms->p_fs << 4) |
                      (pParms->p_fi << 3) |   
                      (pParms->p_fe << 2) |   
                      (pParms->p_fc << 1) |   
                      (pParms->p_fw); 

        if(recScanDebug) {
            printf("Freeze State of P%1d = 0x%hx \n",i,freezeState);
        }
        
        /* a table describing what happens is at the end of the file */
        switch(freezeState) {
          case(0):
          case(1):  /* if center or width is frozen, but not inc , ... */
          case(2):
          case(3):
          case(4):
          case(5):  /* if end/center or end/width are frozen, but not inc, */
          case(6):
          case(7):
          case(16):
          case(17): /*if start/center or start/width are frozen, but not inc*/
          case(18):
          case(19):
          case(20):
          case(21):
          case(22):
          case(23):
            pParms->p_si = (pParms->p_ep - pParms->p_sp)/(pscan->npts - 1);
            db_post_events(pscan,&pParms->p_si,DBE_VALUE);
            break;

          case(8):  /* end/center/width unfrozen, change them */
          case(24):
            pParms->p_ep = pParms->p_sp + ((pscan->npts - 1) * pParms->p_si);
            db_post_events(pscan,&pParms->p_ep,DBE_VALUE);
            pParms->p_cp = (pParms->p_ep + pParms->p_sp)/2;
            db_post_events(pscan,&pParms->p_cp,DBE_VALUE);
            pParms->p_wd = (pParms->p_ep - pParms->p_sp);
            db_post_events(pscan,&pParms->p_wd,DBE_VALUE);
            break;

          case(12): /* start/center/width are not frozen, change them  */
            pParms->p_sp = pParms->p_ep - ((pscan->npts - 1) * pParms->p_si);
            db_post_events(pscan,&pParms->p_sp,DBE_VALUE);
            pParms->p_cp = (pParms->p_ep + pParms->p_sp)/2;
            db_post_events(pscan,&pParms->p_cp,DBE_VALUE);
            pParms->p_wd = (pParms->p_ep - pParms->p_sp);
            db_post_events(pscan,&pParms->p_wd,DBE_VALUE);
            break;

          case(10): /* if center is frozen, but not width/start/end , ...  */
            pParms->p_sp = pParms->p_cp - ((pscan->npts - 1) * pParms->p_si)/2;
            db_post_events(pscan,&pParms->p_sp,DBE_VALUE);
            pParms->p_ep = pParms->p_sp + ((pscan->npts - 1) * pParms->p_si);
            db_post_events(pscan,&pParms->p_ep,DBE_VALUE);
            pParms->p_wd = (pParms->p_ep - pParms->p_sp);
            db_post_events(pscan,&pParms->p_wd,DBE_VALUE);
            break;

          /* The following freezeStates are known to be "Too Constrained" */
          /* 9,11,13,14,15,25,26,27,28,29,30,31 */
          default:                 /* too constrained !! */
            sprintf(pscan->smsg,"P%1d SCAN Parameters Too Constrained !",i+1);
            pscan->alrt = 1;
            break;
        }

      }
    }
}



static long checkScanLimits(pscan)
    struct scanRecord   *pscan;
{

  recDynLinkPvt   *puserPvt;
  recPvtStruct    *precPvt = (recPvtStruct *)pscan->rpvt;
  posFields       *pPosFields = (posFields *)&pscan->p1pp;
  unsigned short  *pPvStat = &pscan->p1nv;


  /* for each valid positioner, fetch control limits */
  long    status;
  size_t    nRequest = 1;
  int     i,j;
  double  value;

  if(recScanDebug) {
      if(!pscan->p1nv) 
          printf("P1 Control Limits : %.4f   %.4f\n",
              pscan->p1lr, pscan->p1hr);
      if(!pscan->p2nv) 
          printf("P2 Control Limits : %.4f   %.4f\n",
              pscan->p2lr, pscan->p2hr);
      if(!pscan->p3nv) 
          printf("P3 Control Limits : %.4f   %.4f\n",
              pscan->p3lr, pscan->p3hr);
      if(!pscan->p4nv) 
          printf("P4 Control Limits : %.4f   %.4f\n",
              pscan->p4lr, pscan->p4hr);
  }

  /* Update "previous position" of positioners to use in relative mode */
  pPvStat = &pscan->p1nv;
  pPosFields = (posFields *)&pscan->p1pp;
  for(i=0; i < NUM_POS; i++, pPosFields++, pPvStat++) {
    if(*pPvStat == PV_OK) {
      puserPvt = precPvt->caLinkStruct[i].puserPvt;
      if(puserPvt->dbAddrNv) {
          status |= recDynLinkGet(&precPvt->caLinkStruct[i],
                 &pPosFields->p_pp, &nRequest, 0, 0, 0);
      }
      else {
          status |= dbGet(puserPvt->pAddr, DBR_DOUBLE, &pPosFields->p_pp,
                     0,0, NULL);
      }
      db_post_events(pscan,&pPosFields->p_pp,DBE_VALUE);
    }
  }

  /* If recScanDontCheckLimits flag is set and EXSC = 1, don't proceed */
  if(recScanDontCheckLimits && pscan->exsc) return(OK);

  /* First check if any pos'rs are in Table mode with insufficient points*/
  pPvStat = &pscan->p1nv;
  pPosFields = (posFields *)&pscan->p1pp;
  for(i=0; i < NUM_POS; i++, pPosFields++, pPvStat++) {
    if((*pPvStat == PV_OK) &&
       (pPosFields->p_sm == REC_SCAN_MO_TAB) &&
       (precPvt->tablePts[i] < pscan->npts)) {
           sprintf(pscan->smsg,"Pts in P%d Table < # of Steps",i+1);
           db_post_events(pscan,&pscan->smsg,DBE_VALUE);
           if(!pscan->alrt) {
               pscan->alrt = 1;
               db_post_events(pscan,&pscan->alrt,DBE_VALUE);
           }
           return(ERROR);
    }
  }

  /* check each point of scan against control limits. */
  /* Stop on first error */

  pPvStat = &pscan->p1nv;
  pPosFields = (posFields *)&pscan->p1pp;
  for(i=0; i < NUM_POS; i++, pPosFields++, pPvStat++) {
    if(*pPvStat == PV_OK) {
      for(j=0; j<pscan->npts; j++) {
          /* determine next desired position for each positioner */
          switch(pPosFields->p_sm) {
            case REC_SCAN_MO_LIN:
              if(pPosFields->p_ar) {
                value = (pPosFields->p_pp + pPosFields->p_sp) + 
                        (j * pPosFields->p_si);
              } else {
                value = pPosFields->p_sp + (j * pPosFields->p_si);
              }
            break;

            case REC_SCAN_MO_TAB:    
              if(pPosFields->p_ar) {
                value = pPosFields->p_pp + pPosFields->p_pa[j]; 
              } else {
                value = pPosFields->p_pa[j];
              }
            break;

            case REC_SCAN_MO_OTF:    
              if(pPosFields->p_ar) {
                  if(j == 0) value = pPosFields->p_pp + pPosFields->p_sp;
                  else value = pPosFields->p_pp + pPosFields->p_ep;
              } else {
                  if(j == 0) value = pPosFields->p_sp;
                  else value = pPosFields->p_ep;
              }
            break;

            default:
              value = 0;
          }

          if((pPosFields->p_lr != 0) && (value < pPosFields->p_lr)) {
              sprintf(pscan->smsg,"P%-d Value < LO_Limit @ point %1d",i+1,j);
              pscan->alrt = 1;
              return(ERROR);
          }
          else if((pPosFields->p_hr != 0) && (value > pPosFields->p_hr)) {
              sprintf(pscan->smsg,"P%-d Value > HI_Limit @ point %1d",i+1,j);
              pscan->alrt = 1;
              return(ERROR);
          }
      }
    }
  }

  /* No errors if we made it here ... */
  sprintf(pscan->smsg,"SCAN Values within limits");
  return(OK);

}


/* This routine show a "preview" of the scan positions by
 * loading the detector arrays (d_da) with the scan positions at each
 * point and loading the readback arrays (p_ra) with the step #.
 * A plot of d_da vs p_ra will provide a graphical view of the scan 
 * ranges.
 *
 */

static void previewScan(pscan)
    struct scanRecord   *pscan;
{

  recPvtStruct     *precPvt = (recPvtStruct *)pscan->rpvt;
  recDynLinkPvt    *puserPvt;
  posFields        *pPosFields;
  detFields        *pDetFields;
  unsigned short   *pPvStat;
  double           *pPosBuf;
  float            *pDetBuf;
  float             value;
  int               i,j;
  long    status;
  size_t    nRequest = 1;

  /* Update "previous position" of positioners to use in relative mode */
  pPvStat = &pscan->p1nv;
  pPosFields = (posFields *)&pscan->p1pp;
  for(i=0; i < NUM_POS; i++, pPosFields++, pPvStat++) {
    if(*pPvStat == PV_OK) {
      puserPvt = precPvt->caLinkStruct[i].puserPvt;
      if(puserPvt->dbAddrNv) {
          status |= recDynLinkGet(&precPvt->caLinkStruct[i],
                 &pPosFields->p_pp, &nRequest, 0, 0, 0);
      }
      else {
          status |= dbGet(puserPvt->pAddr, DBR_DOUBLE, &pPosFields->p_pp,
                     0,0, NULL);
      }
      db_post_events(pscan,&pPosFields->p_pp,DBE_VALUE);
    }
  }

  /* Run through entire scan for each valid positioner */
  pPvStat = &pscan->p1nv;
  pPosFields = (posFields *)&pscan->p1pp;
  pDetFields = (detFields *)&pscan->d1hr;
  for(i=0; i < NUM_POS; i++, pPosFields++, pPvStat++, pDetFields++) {
    if(*pPvStat == PV_OK) {
      /* must use the current buffer pointer */
      if(precPvt->validBuf) {
          pPosBuf = precPvt->posBufPtr[i].pBufB;
          pDetBuf = precPvt->detBufPtr[i].pBufB;
      }
      else {
          pPosBuf = precPvt->posBufPtr[i].pBufA;
          pDetBuf = precPvt->detBufPtr[i].pBufA;
      }
      for(j=0; j<pscan->npts; j++) {
          /* determine next desired position for each positioner */
          switch(pPosFields->p_sm) {
            case REC_SCAN_MO_LIN:
              if(pPosFields->p_ar) {
                value = (pPosFields->p_pp + pPosFields->p_sp) +
                        (j * pPosFields->p_si);
              } else {
                value = pPosFields->p_sp + (j * pPosFields->p_si);
              }
            break;

            case REC_SCAN_MO_TAB:
              if(pPosFields->p_ar) {
                value = pPosFields->p_pp + pPosFields->p_pa[j];
              } else {
                value = pPosFields->p_pa[j];
              }
            break;

            case REC_SCAN_MO_OTF:
              if(pPosFields->p_ar) {
                  if(j == 0) value = pPosFields->p_pp + pPosFields->p_sp;
                  else value = pPosFields->p_pp + pPosFields->p_ep;
              } else {
                  if(j == 0) value = pPosFields->p_sp;
                  else value = pPosFields->p_ep;
              }
            break;

            default:
              value = 0;
          }
          pPosBuf[j] = j;
          pDetBuf[j] = value;
      }
      /* now fill the rest of the array(s) with the last values */
      for(j=j; j<pscan->mpts;j++) {
          pPosBuf[j] = pPosBuf[j-1];
          pDetBuf[j] = pDetBuf[j-1];
      }
      db_post_events(pscan, precPvt->posBufPtr[i].pBufA, DBE_VALUE);
      db_post_events(pscan, precPvt->posBufPtr[i].pBufB, DBE_VALUE);
      db_post_events(pscan, precPvt->detBufPtr[i].pBufA, DBE_VALUE);
      db_post_events(pscan, precPvt->detBufPtr[i].pBufB, DBE_VALUE);
      /* I must also post a monitor on the NULL array, because some
         clients connected to D?PV's without valid PV's ! */
      db_post_events(pscan, precPvt->nullArray, DBE_VALUE);
    }
  }
}
        
static void saveFrzFlags(pscan)
    struct scanRecord   *pscan;
{

    recPvtStruct *precPvt = (recPvtStruct *)pscan->rpvt;
    posFields    *pPosFields = (posFields *)&pscan->p1pp;
    int           i;


    /* save state of each freeze flag */
    precPvt->fpts = pscan->fpts;
    for(i=0; i<NUM_POS; i++, pPosFields++) {
        precPvt->posParms[i].p_fs = pPosFields->p_fs;
        precPvt->posParms[i].p_fi = pPosFields->p_fi;
        precPvt->posParms[i].p_fc = pPosFields->p_fc;
        precPvt->posParms[i].p_fe = pPosFields->p_fe;
        precPvt->posParms[i].p_fw = pPosFields->p_fw;
    }
}

static void savePosParms(struct scanRecord *pscan, unsigned short i)
{

    recPvtStruct *precPvt = (recPvtStruct *)pscan->rpvt;
    posFields    *pPosFields = (posFields *)&pscan->p1pp + i;

    /* save state of linear scan parameters 0 */
    /* Do this when in table mode so operator is not confused */
        precPvt->posParms[i].p_sp = pPosFields->p_sp;
        precPvt->posParms[i].p_si = pPosFields->p_si;
        precPvt->posParms[i].p_ep = pPosFields->p_ep;
        precPvt->posParms[i].p_cp = pPosFields->p_cp;
        precPvt->posParms[i].p_wd = pPosFields->p_wd;
}

static void zeroPosParms(struct scanRecord *pscan, unsigned short i)
{

    posFields    *pPosFields = (posFields *)&pscan->p1pp + i;

    /* set them to 0 */
    /* Do this when in table mode so operator is not confused */
        pPosFields->p_sp = 0;
        db_post_events(pscan,&pPosFields->p_sp, DBE_VALUE);
        pPosFields->p_si = 0;
        db_post_events(pscan,&pPosFields->p_si, DBE_VALUE);
        pPosFields->p_ep = 0;
        db_post_events(pscan,&pPosFields->p_ep, DBE_VALUE);
        pPosFields->p_cp = 0;
        db_post_events(pscan,&pPosFields->p_cp, DBE_VALUE);
        pPosFields->p_wd = 0;
        db_post_events(pscan,&pPosFields->p_wd, DBE_VALUE);
}

static void resetFrzFlags(pscan)
    struct scanRecord   *pscan;
{

    posFields    *pPosFields = (posFields *)&pscan->p1pp;
    int           i;

    /* reset each frzFlag, post monitor if changed */

    if(pscan->fpts) {
       pscan->fpts = 0;
       db_post_events(pscan,&pscan->fpts, DBE_VALUE);
    }

    for(i=0; i<NUM_POS; i++, pPosFields++) {
        if(pPosFields->p_fs) {
           pPosFields->p_fs = 0;
           db_post_events(pscan,&pPosFields->p_fs, DBE_VALUE);
        }

        if(pPosFields->p_fi) {
           pPosFields->p_fi = 0;
           db_post_events(pscan,&pPosFields->p_fi, DBE_VALUE);
        }

        if(pPosFields->p_fc) {
           pPosFields->p_fc = 0;
           db_post_events(pscan,&pPosFields->p_fc, DBE_VALUE);
        }

        if(pPosFields->p_fe) {
           pPosFields->p_fe = 0;
           db_post_events(pscan,&pPosFields->p_fe, DBE_VALUE);
        }

        if(pPosFields->p_fw) {
           pPosFields->p_fw = 0;
           db_post_events(pscan,&pPosFields->p_fw, DBE_VALUE);
        }
    }
}


/* Restores Freeze Flags to the state they were in */
static void restoreFrzFlags(pscan)
    struct scanRecord   *pscan;
{

    recPvtStruct *precPvt = (recPvtStruct *)pscan->rpvt;
    posFields    *pPosFields = (posFields *)&pscan->p1pp;
    int           i;


    /* restore state of each freeze flag, post if changed */
    pscan->fpts = precPvt->fpts;
    if(pscan->fpts) {
       db_post_events(pscan,&pscan->fpts, DBE_VALUE);
    }

    for(i=0; i<NUM_POS; i++, pPosFields++) {
        pPosFields->p_fs = precPvt->posParms[i].p_fs;
        if(pPosFields->p_fs) {
           db_post_events(pscan,&pPosFields->p_fs, DBE_VALUE);
        }

        pPosFields->p_fi = precPvt->posParms[i].p_fi;
        if(pPosFields->p_fi) {
           db_post_events(pscan,&pPosFields->p_fi, DBE_VALUE);
        }

        pPosFields->p_fc = precPvt->posParms[i].p_fc;
        if(pPosFields->p_fc) {
           db_post_events(pscan,&pPosFields->p_fc, DBE_VALUE);
        }

        pPosFields->p_fe = precPvt->posParms[i].p_fe;
        if(pPosFields->p_fe) {
           db_post_events(pscan,&pPosFields->p_fe, DBE_VALUE);
        }

        pPosFields->p_fw = precPvt->posParms[i].p_fw;
        if(pPosFields->p_fw) {
           db_post_events(pscan,&pPosFields->p_fw, DBE_VALUE);
        }

    }
}

/* Restores Position Parms after leaving table mode (for 1 positioner) */
static void restorePosParms(struct scanRecord *pscan, unsigned short i)
{

    recPvtStruct *precPvt = (recPvtStruct *)pscan->rpvt;
    posFields    *pPosFields = (posFields *)&pscan->p1pp + i;

    pPosFields->p_sp = precPvt->posParms[i].p_sp;
    db_post_events(pscan,&pPosFields->p_sp, DBE_VALUE);

    pPosFields->p_si = precPvt->posParms[i].p_si;
    db_post_events(pscan,&pPosFields->p_si, DBE_VALUE);

    pPosFields->p_ep = precPvt->posParms[i].p_ep;
    db_post_events(pscan,&pPosFields->p_ep, DBE_VALUE);

    pPosFields->p_cp = precPvt->posParms[i].p_cp;
    db_post_events(pscan,&pPosFields->p_cp, DBE_VALUE);

    pPosFields->p_wd = precPvt->posParms[i].p_wd;
    db_post_events(pscan,&pPosFields->p_wd, DBE_VALUE);

}







/*  Memo from Tim Mooney suggesting change in order of precedence */

/*

Ned,

Current effects of scan-parameter changes are summarized below, along
with the effects I think might be preferable.  The ideas are to make NPTS
nearly as easy to change as is INCR, to make END easier to change than
is START, and to prefer changing INCR in response to a change of NPTS
whenever permissible.

If an indirect change in NPTS would make it larger than MPTS, I think
NPTS ideally should be regarded as frozen, rather than being set to
MPTS.  I don't feel all that strongly about this, since I think it
might be more of a pain to code than it's worth.

Comments on any of this?

Tim

====================================================================
changing	now affects		should affect
--------------------------------------------------------------
START	->	INCR CENTER WIDTH	INCR CENTER WIDTH
	or	END CENTER		CENTER NPTS WIDTH
	or	INCR END WIDTH		END CENTER
	or	NPTS CENTER WIDTH	INCR END WIDTH
	or	NPTS END WIDTH		NPTS END WIDTH
	or	CENTER END		<punt>
	or	<punt>			
INCR	->	CENTER END WIDTH	NPTS
	or	START CENTER WIDTH	CENTER END WIDTH
	or	START END WIDTH		START CENTER WIDTH
	or	NPTS			START END WIDTH
	or	<punt>			<punt>
CENTER	->	START END		START END
	or	START INCR WIDTH	END INCR WIDTH
	or	END INCR WIDTH		START INCR WIDTH
	or	NPTS START WIDTH	NPTS END WIDTH
	or	NPTS END WIDTH		NPTS START WIDTH
	or	<punt>						
END	->	INCR CENTER WIDTH	INCR CENTER WIDTH
	or	START CENTER WIDTH	NPTS CENTER WIDTH
	or	START WIDTH INCR	START CENTER
	or	NPTS START WIDTH	START WIDTH INCR
	or	NPTS CENTER WIDTH	NPTS START WIDTH
	or	START CENTER		<punt>
	or	<punt>			
WIDTH	->	START END INCR		START END INCR
	or	CENTER END INCR		START END NPTS
	or	START CENTER INCR	CENTER END INCR
	or	NPTS START END		START CENTER INCR
	or	NPTS CENTER END		NPTS CENTER END
	or	NPTS START CENTER	NPTS START CENTER
	or	<punt>			<punt>

NPTS: given
SIECW (Start,Incr,End,Center,Width freeze-switch states; '1' = 'frozen')
---------------------------------------------------------------
00000	 (0)	END CENTER WIDTH	INCR
00001	 (1)	INCR			INCR
00010	 (2)	START END WIDTH		INCR
00011	 (3)	INCR			INCR
00100	 (4)	START CENTER WIDTH	INCR
00101	 (5)	INCR			INCR
00110	 (6)	INCR			INCR
00111	 (7)	INCR			INCR
01000	 (8)	END CENTER WIDTH	END CENTER WIDTH
01001	 (9)	<punt>			<punt>
01010	(10)	START END WIDTH		START END WIDTH
01011	(11)	<punt>			<punt>
01100	(12)	START CENTER WIDTH	START CENTER WIDTH
01101	(13)	<punt>			<punt>
01110	(14)	<punt>			<punt>
01111	(15)	<punt>			<punt>
10000	(16)	END CENTER WIDTH	INCR
10001	(17)	INCR			INCR
10010	(18)	INCR			INCR
10011	(19)	INCR			INCR
10100	(20)	INCR			INCR
10101	(21)	INCR			INCR
10110	(22)	INCR			INCR
10111	(23)	INCR			INCR
11000	(24)	END CENTER WIDTH	END CENTER WIDTH
11001	(25)	<punt>			<punt>
11010	(26)	<punt>			<punt>
11011	(27)	<punt>			<punt>
11100	(28)	<punt>			<punt>
11101	(29)	<punt>			<punt>
11110	(30)	<punt>			<punt>
11111	(31)	<punt>			<punt>


*/

