Files
epics-base/src/rec/scanRecord.c
1998-03-19 20:46:33 +00:00

3311 lines
122 KiB
C
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* Original Author: Ned D. Arnold
* Date: 07-18-94
*
* Experimental Physics and Industrial Control System (EPICS)
*
* Copyright 1991, the Regents of the University of California,
* and the University of Chicago Board of Governors.
*
* This software was produced under U.S. Government contracts:
* (W-7405-ENG-36) at the Los Alamos National Laboratory,
* and (W-31-109-ENG-38) at Argonne National Laboratory.
*
* Initial development by:
* The Controls and Automation Group (AT-8)
* Ground Test Accelerator
* Accelerator Technology Division
* Los Alamos National Laboratory
*
* Co-developed with
* The Controls and Computing Group
* Accelerator Systems Division
* Advanced Photon Source
* Argonne National Laboratory
*
*
* Modification Log:
* -----------------
* .01 07-18-94 nda significantly expanded functionality from prototype
* .02 08-18-94 nda Starting with R3.11.6, dbGetField locks the record
* before fetching the data. This can cause deadlocks
* within a database. Change all dbGetField() to dbGet()
* .03 08-23-94 nda added code for checking/adjusting linear scan
* params (it gets a little messy !)
* .04 08-25-94 nda Added check of scan positions vs Positioner Control
* Limits
* .05 08-29-94 nda Added "viewScanPos" that puts desired positions
* in D1 array any time a scan parameter is changed
* .06 10-03-94 nda added code for enumerated string .CMND. Changed
* .EXSC to a SHORT in record definition. Added VERSION
* for .VERS field (1.06) to keep track of version.
* .07 10-21-94 nda changed linear scan parameter algorithms so changing
* start/end modifies step/width unless frozen. This
* seems more intuitive.
* .08 12-06-94 nda added support for .FFO .When set to 1, frzFlag values
* are saved in recPvtStruct. Restored when FFO set to 0.
* .09 02-02-95 nda fixed order of posting monitors to be what people
* expect (i.e. .cpt, .pxdv, .dxcv)
* .10 02-10-95 nda fixed on-the-fly so 1st step is to end position
* .11 02-21-95 nda added "Return To Start" flag. If set, positioners
* will be commanded to the start pos after the scan.
* .12 03-02-95 nda added positioner readback arrays (PxRA). These arrays
* will contain actual readback positions (if RxPV are
* defined. If not, the desired positions will be loaded
* into them.
* .13 03-02-95 nda Post .val field when a new point is complete during a
* scan. This will assist in poin by point plots.
* .14 03-02-95 nda Bug fix on filling PxRA's. ALWAYS CALL CHECK_MOTORS.
* .15 03-15-95 nda If no readback PV (RxPV) is specified, copy desired
* value (PxDV) to current value (RxCV). Now, plotting
* programs can always monitor RxCV.
* .16 04-03-95 nda If PV field = DESC, change to VAL
*
* 3.00 08-28-95 nda > Significant rewrite to add Channel Access
* for dynamic links using recDynLink.c . All
* inputs are now "monitored" via Channel Access.
* > Name Valid field is used to keep track of PV
* connection status: 0-PV_OK,
* 1-NotConnected, 2-NO_PV
* > added relative/absolute mode on positioners
* > added retrace options of stay/start/prior
* > supports 15 detectors
* > added "preview scan"
* > added "Before Scan" and "After Scan" links
* > fetch DRVL/DRVH/prec/egu from dynamic links
* > when posting arrays at end of scan, use DBE_LOG, too
* > Record timeStamp only at beginning of scan
* > Record positioner readbacks when reading detectors
* > "TIME" or "time" in a readback PV records time
*
* 3.01 03-07-96 nda Rearrange order of precedence of linear scan parameters
* per Tim Mooney's memo duplicated at end of this file
*
* 3.02 03-12-96 nda If scan request and any PV's are PV_NC, callback 3
* seconds later ... if still PV_NC, abort. Also, if any
* control PV's (positioners, readbacks, detctr triggers)
* lose connection during a scan, abort scan.
*
* 3.02 03-12-96 nda Changed detector reading to accommodate the possibility
* of connections "coming and going" during a scan: If a
* detector PV is valid at the beginning of the scan, the
* value will be read each point. If the PV is not
* connected for a given point, a zero will be stored.
* Previously, nothing would be stored, which left old
* data in the array for those points. Any detector PV's
* added during a scan will be ignored until the next scan
*
* 3.02 03-12-96 nda Writing a 3 to the .CMND field will clear all PV's,
* set all positioners to LINEAR & ABSOLUTE, resets all
* freeze flags & .FFO, sets .SCAN to Passive,RETRACE=stay
*
* 3.03 04-26-96 tmm Writing a 4 to the .CMND field will clear positioner
* PV's, set all positioners to LINEAR & ABSOLUTE, reset
* all freeze flags & .FFO, set .SCAN to Passive, set
* RETRACE=stay
*
* 3.13 _______ nda changes for 3.13
* 03-15-96 - remove pads from detFields structure
* - use fieldOffset indexes
*/
#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>
*/