EPICS Controls Argonne National Laboratory

Experimental Physics and
Industrial Control System

1994  1995  1996  1997  1998  1999  2000  2001  2002  2003  2004  2005  2006  2007  2008  2009  2010  2011  2012  2013  <20142015  2016  2017  2018  2019  2020  2021  2022  2023  2024  Index 1994  1995  1996  1997  1998  1999  2000  2001  2002  2003  2004  2005  2006  2007  2008  2009  2010  2011  2012  2013  <20142015  2016  2017  2018  2019  2020  2021  2022  2023  2024 
<== Date ==> <== Thread ==>

Subject: Re: Sequencer 2.2.0
From: Benjamin Franksen <[email protected]>
To: Mark Rivers <[email protected]>
Cc: "'EPICS Techtalk'" <[email protected]>
Date: Wed, 6 Aug 2014 19:22:44 +0200
Hi Mark

thanks for trying out the new version. I'll comment in detail below.

On Wednesday 06 August 2014 13:54:33 Mark Rivers wrote:
> I have tried building the synApps module with seq 2.2.0.1.  I am
> running into some problems that I could not figure out from the
> release notes:
>
> ../MAX_trajectoryScan.st: In function
> 'seqg_action_maxTrajectoryScan_0_execute':
> ../MAX_trajectoryScan.st:588:40: error: called object
> 'seqg_var->time' is not a function
>
> The line with the error, which is in SNL code, not escaped C code, is:
> startTime = time(0);

I remember I've stumbled over this problem when testing with synApps
some time ago. The problem is that there exists an SNL variable
definition (included from MAX_trajectoryScan.h) with the same name
'time'.

Now, in version 2.2 variables and functions share the same name space
(as they do in C), so the variable now shadows (in SNL) the foreign
function. So the SNL compiler generates variable access code ('seqg_var-
>time') and now the C compiler complains because the struct member
'seqg_var->time' is not of function (or pointer to function) type.

The recommended solution is to rename the variable.

Alternatively, you can "rename" the function e.g. with a macro:

  %%#define get_time() time(0)
  ...
  startTime = get_time();

If these are both impossible or not desired due to other constraints,
there is the possibility to escape the statement:

  %%startTime = time(0);

but then you must also declare the variable on the C side as

  %%time_t startTime;

which may not work as now the variable is shared between all program
instances (noticing that option +r is turned on).

> This is the definition of startTime at the start of the SNL code.
> /* Note, this should be time_t, but SNL doesn't understand that.  This
> is * the defininition in vxWorks. */
> unsigned long startTime;

Note that (just BTW) this can now be written directly in SNL as

typename time_t startTime;

> The error occurs whether or not the following line is included at the
> top of the SNL code: %% #include <time.h>

(which is because <time.h> already gets included indirectly via
epicsTime.h which is included by the generated code)

> The next error is:
>
> ../MAX_trajectoryScan.st:996:3: error: too few arguments to function
> 'seq_pvGet'
>
> The offending line is:
>
>               seq_pvGet(ssId, pVar->epicsMotorDoneIndex[j], 0);
>
> This is in escaped C code at the end of the file.

See the note under
http://www-csr.bessy.de/control/SoftDist/sequencer/ReleaseNotes-2-2.html#timeout-arguments-for-pvget-and-pvput

> The program is built with the "+r" option.
>
>
> The final error is:
>
> /corvette/home/epics/devel/seq-2-2-0-1/bin/linux-x86/snc
> getFilledBuckets.i -o getFilledBuckets.c ../getFilledBuckets.st:151:
> error: calling built-in function delay not allowed here
>
> Line 151 is the delay(1.0) statement in the code below:
>
>                       when((srInjectStatus ==  NOTINJECTING) &&
>                               (srBeamStatus) == STOREDBEAM) {
>                               delay(1.0);  /* 1 sec delay */
>
> Is this error due to this change in 2.2.0:
>
> "Since calling delay outside of the condition of a state transition
> never had any useful effect, it is now disallowed."

Yes. Whatever your program (or its author) hoped to achieve with this
call would not happen anyway (with any version from 2.0 onward), so I
guess making this an error has already proved useful... ;-)

 * * * * *

I remember playing with this program (or rather its predecessor) for a
while back when I tested the 2.2 branch with synApps. It is a good
example for how the new features enable one to write programs without
recourse to C-escaping: all the escaped C code (except the %%#includes
at the top) can be replaced with SNL code, now. In fact you can just
drop all the escapes, remove the ssId and pVar arguments (if any), drop
the pVar-> for variable access, and replace seq_pvXXX with regular pvXXX
calls. That's it... more or less.

I just tried that with a the MM4005_trajectoryScan.st from synApps-5.6.
I attached the modified file for reference. While trying to compile this
I noticed a stupid bug I made in the code generator for SNL functions,
which I why I have just uploaded release 2.2.0.2.

Cheers
Ben

--
"Make it so they have to reboot after every typo." ― Scott Adams

________________________________

Helmholtz-Zentrum Berlin für Materialien und Energie GmbH

Mitglied der Hermann von Helmholtz-Gemeinschaft Deutscher Forschungszentren e.V.

Aufsichtsrat: Vorsitzender Prof. Dr. Dr. h.c. mult. Joachim Treusch, stv. Vorsitzende Dr. Beatrix Vierkorn-Rudolph
Geschäftsführung: Prof. Dr. Anke Rita Kaysser-Pyzalla, Thomas Frederking

Sitz Berlin, AG Charlottenburg, 89 HRB 5583

Postadresse:
Hahn-Meitner-Platz 1
D-14109 Berlin

http://www.helmholtz-berlin.de
program MM4005_trajectoryScan("P=13IDC:,R=traj1,M1=M1,M2=M2,M3=M3,M4=M4,M5=M5,M6=M6,M7=M7,M8=M8,PORT=serial1")

/*  This sequencer program works with trajectoryScan.db.  It implements
 *  coordinated trajectory motion with the Newport MM4005 motor controller.
 *  It can be used with the Newport General Purpose Diffractometer or with any
 *  other set of motors connected to that controller.
 *
 *  Mark Rivers
 *  August 12, 2000
*/

%% #include <stdlib.h>	/* for atof() */
%% #include <string.h>
%% #include <stdio.h>
%% #include <epicsString.h>
%% #include <asynOctetSyncIO.h>

/* This program must be compiled with the recursive option */
option +r;

%%#define get_time() time(0)

/* Maximum # of trajectory elements.  The MM4005 allows 2000, and this is also
 * the channel access limit with a double data type.  However this uses 
 * a lot of memory, the variable motorTrajectory uses MAX_AXES*MAX_ELEMENTS*8 
 * bytes in this SNL program (up to 128KB). Similar memory will be required 
 * for the records in the database. Restrict to 1000 for now.
 */
#define MAX_ELEMENTS 2000

/* Maximum # of output pulses.  The MM4005 allows 2000, and this is also
 * the channel access limit with a double data type.  However this uses 
 * a lot of memory, the variables motorActual and motorError each use
 * MAX_AXES*MAX_PULSES*8 bytes (up to 256KB total). Similar memory will be 
 * required for the records in the database. Restrict to 1000 for now.
 */
#define MAX_PULSES 2000

/* Note that MAX_AXES, MAX_ELEMENTS, and MAX_PULSES must be defined before
 * including the trajectoryScan.h */
#include "trajectoryScan.h"

/* Maximum size of string to/from MM4005, typically for TQ command. */
#define MAX_MM4000_STRING 256

/* Buffer sizes */
#define NAME_LEN 100

/* Maximum size of string in EPICS string PVs.  This is defined in 
 * epicsTypes.h, but in order to include that file it must be escaped, and then
 * SNL compiler gives a warning. */
#define MAX_STRING_SIZE 40

/* Time for each "padding" trajectory element added to trajectory because it
 * is not a multiple of 4 elements */
#define PAD_TIME 0.1

/* Polling interval for waiting for motors to reach their targets */
#define POLL_INTERVAL 0.1


char stringOut[MAX_MM4000_STRING];
char stringIn[MAX_MM4000_STRING];
char *asynPort;
typename asynUser *pasynUser;
int status;
int i;
int j;
int k;
double delay;
int anyMoving;
int ncomplete;
int nextra;
int npoints;
int dir;
double dtime;
double dpos;
double posActual;
double posTheory;
double expectedTime;
double initialPos[MAX_AXES];
char macroBuf[NAME_LEN];
char motorName[NAME_LEN];
char *p;
char *tok_save;

typename time_t startTime;


ss trajectoryScan {

    /* Initialize things when first starting */
    state init {
        when() {
            /* Force numAxes to be <= MAX_AXES */
            if (numAxes > MAX_AXES) numAxes = MAX_AXES;
            for (i=0; i<numAxes; i++) {
                sprintf(macroBuf, "M%d", i+1);
                sprintf(motorName, "%s%s.VAL", macValueGet("P"), macValueGet(macroBuf));
                pvAssign(epicsMotorPos[i], motorName);
                sprintf(motorName, "%s%s.DIR", macValueGet("P"), macValueGet(macroBuf));
                pvAssign(epicsMotorDir[i], motorName);
                sprintf(motorName, "%s%s.OFF", macValueGet("P"), macValueGet(macroBuf));
                pvAssign(epicsMotorOff[i], motorName);
                sprintf(motorName, "%s%s.DMOV", macValueGet("P"), macValueGet(macroBuf));
                pvAssign(epicsMotorDone[i], motorName);
            }

            asynPort = macValueGet("PORT");
            status = pasynOctetSyncIO->connect(asynPort, 0, &pasynUser, NULL);
            if (status != 0) {
               printf("trajectoryScan error in pasynOctetSyncIO->connect\n");
               printf("   status=%d, port=%s\n", status, asynPort);
            }
            /* Read the maximum allowable speed error between blocks */
            for (j=0; j<numAxes; j++) {
                sprintf(stringOut, "%dGC?", j+1);
                writeRead(stringOut);
                /* Parse the return string which is of the form 1GCxxx */
                motorMDVS[j] = atof(stringIn+3);
                pvPut(motorMDVS[j]);
            }
            /* Clear all event flags */
            efClear(buildMon);
            efClear(executeMon);
            efClear(abortMon);
            efClear(readbackMon);
            efClear(nelementsMon);
            efClear(motorMDVSMon);
        } state monitor_inputs
    }


    /* Monitor inputs which control what to do (Build, Execute, Read) */
    state monitor_inputs {
        when(efTestAndClear(buildMon) && (build==1)) {
        } state build

        when(efTestAndClear(executeMon) && (execute==1)) {
        } state execute

        when(efTestAndClear(readbackMon) && (readback==1)) {
        } state readback

        when(efTestAndClear(nelementsMon) && (nelements>=1)) {
            /* If nelements changes, then change endPulses to this value,
             * since this is what the user normally wants.  endPulses can be
             * changed again after changing nelements if this is desired. */
            endPulses = nelements;
            pvPut(endPulses);
        } state monitor_inputs

        when(efTestAndClear(motorMDVSMon)) {
            /* One of the motorMDVS values has changed.  The event flag is on
             * the array, so we can't tell which one.  No harm in writing all
             * the values to the MM4005. */
            for (j=0; j<numAxes; j++) {
                sprintf(stringOut, "%dGC%f", j+1, motorMDVS[j]);
                writeOnly(stringOut);
            }
        } state monitor_inputs
    }


    /* Build and verify trajectory */
    state build {
        when() {
            /* Set busy flag while building */
            buildState = BUILD_STATE_BUSY;
            pvPut(buildState);
            buildStatus=STATUS_UNDEFINED;
            pvPut(buildStatus);
            /* Initialize new trajectory */
            strcpy(stringOut, "NC");
            writeOnly(stringOut);
            /* Define which motors are to be moved */
            for (i=0; i<numAxes; i++) {
                sprintf(stringOut, "%dDC%d", i+1, moveAxis[i]);
                writeOnly(stringOut);
            }
            /* Set acceleration time */
            sprintf(stringOut, "UC%f", accel);
            writeOnly(stringOut);
            /* If time mode is TIME_MODE_TOTAL then construct timeTrajectory 
             * and post it */
            if (timeMode == TIME_MODE_TOTAL) {
                dtime = time/nelements;
                for (i=0; i<nelements; i++) timeTrajectory[i] = dtime;
                pvPut(timeTrajectory);
            }

            /* Make sure number of trajectory elements is a multiple of 4.
             * If not, pad with up to 3 entries of PAD_TIME duration each.
             * Continue the trajectory at the same velocity. 
             * Change nelements and post new value */
            if (moveMode == MOVE_MODE_RELATIVE) {
                npoints=nelements;
            } else {
                npoints=nelements-1;
            }
            nextra = (npoints % 4);
            if (nextra != 0) {
                nextra = 4-nextra;
                /* Compute the increment to move the motors during these
                 * padding elements, keeping velocity constant */
                for (i=0; i<nextra; i++) {
                    timeTrajectory[npoints+i] = PAD_TIME;
                    for (j=0; j<numAxes; j++) {
                        if (!moveAxis[j]) continue;
                        if (moveMode == MOVE_MODE_RELATIVE) {
                            motorTrajectory[j][nelements+i] = 
                                        motorTrajectory[j][nelements-1] *
                                        PAD_TIME / timeTrajectory[nelements-1];
                        } else {
                            dpos = (motorTrajectory[j][nelements-1] -
                                            motorTrajectory[j][nelements-2]) *
                                        PAD_TIME / timeTrajectory[nelements-2];
                            motorTrajectory[j][nelements+i] = 
                                        motorTrajectory[j][nelements-1] +
                                                                dpos*(i+1);
                        }
                    }
                }
                nelements += nextra;
                npoints += nextra;
                pvPut(nelements);
                pvPut(timeTrajectory);
                /* Post the new trajectory position arrays */
                for (j=0; j<numAxes; j++) {
                   pvPut(motorTrajectory[j]);
                }
            }
            /* Compute expected time for trajectory */
            expectedTime=0;
            for (i=0; i<npoints; i++) 
                                expectedTime += timeTrajectory[i];
            /* Define each element in trajectory */
            for (i=0; i<npoints; i++) {
                sprintf(buildMessage, "Building element %d/%d", i+1, nelements);
                pvPut(buildMessage);
                sprintf(stringOut, "%dDT%f", i+1, timeTrajectory[i]);
                writeOnly(stringOut);
                for (j=0; j<numAxes; j++) {
                    if (!moveAxis[j]) continue;
                    if (moveMode == MOVE_MODE_RELATIVE) {
                        dpos = motorTrajectory[j][i];
                    } else {
                        dpos = motorTrajectory[j][i+1] - motorTrajectory[j][i];
                    }
                    /* Convert from user units to MM4000 units */
                    if (epicsMotorDir[j] == 0) dir=1; else dir=-1;
                    dpos = dpos*dir;
                    sprintf(stringOut, "%dDX%f", j+1, dpos);
                    writeOnly(stringOut);
                }
                /* The following command is intended to prevent buffer overflow in
                 * the MM4005 by reading introducing a delay (reading status) when 
                 * downloading many-element trajectories */
                if (((i+1) % 20) == 0) writeRead("TB");
            }
            /* Define pulse output for trajectory */
            if (npulses > 0) {
                /* Check validity, modify values if necessary */
                if (startPulses < 1) startPulses = 1;
                if (startPulses > npoints) startPulses = npoints;
                pvPut(startPulses);
                if (endPulses < startPulses) endPulses = startPulses;
                if (endPulses > npoints) endPulses = npoints;
                pvPut(endPulses);
                /* There seems to be a bug in the MM4005, it puts out one fewer
                 * pulse than requested.  Add one */
                sprintf(stringOut, "MB%d,ME%d,MN%d", 
                                startPulses, endPulses, npulses+1);
                writeOnly(stringOut);
            }
            /* Verify trajectory */
            strcpy(buildMessage, "Verifying trajectory");
            pvPut(buildMessage);
            strcpy(stringOut, "VC");
            writeOnly(stringOut);
            /* Read error code back from MM4000 */
            writeRead("TB");
            /* Set status and message string */
            if (stringIn[2] == '@') {
                buildStatus = STATUS_SUCCESS;
                strcpy(buildMessage, " ");
            } else {
                buildStatus = STATUS_FAILURE;
                strncpy(buildMessage, stringIn, MAX_STRING_SIZE-1);
            }
            /* Read dynamic parameters, post them */
            for (j=0; j<numAxes; j++) {
                p = stringIn;
                /* This query can only be done for axes which are active in the
                 * trajectory */
                if (!moveAxis[j]) continue;
                /* We could query all parameters with one nRc, but the parsing
                 * is a pain, much simpler to query one at a time */
                /* Maximum speed change element and value */
                sprintf(stringOut, "%dRC1", j+1);
                writeRead(stringOut);
                motorMDVE[j] = atoi(p+3);
                pvPut(motorMDVE[j]);
                sprintf(stringOut, "%dRC2", j+1);
                writeRead(stringOut);
                motorMDVA[j] = atof(p+3);
                pvPut(motorMDVA[j]);
                /* Maximum velocity element and value */
                sprintf(stringOut, "%dRC3", j+1);
                writeRead(stringOut);
                motorMVE[j] = atoi(p+3);
                pvPut(motorMVE[j]);
                sprintf(stringOut, "%dRC4", j+1);
                writeRead(stringOut);
                motorMVA[j] = atof(p+3);
                pvPut(motorMVA[j]);
                /* Maximum acceleration element and value */
                sprintf(stringOut, "%dRC5", j+1);
                writeRead(stringOut);
                motorMAE[j] = atoi(p+3);
                pvPut(motorMAE[j]);
                sprintf(stringOut, "%dRC6", j+1);
                writeRead(stringOut);
                motorMAA[j] = atof(p+3);
                pvPut(motorMAA[j]);
            }       
            /* Clear busy flag, post status */
            buildState = BUILD_STATE_DONE;
            pvPut(buildState);
            pvPut(buildStatus);
            pvPut(buildMessage);
            /* Clear build command, post.  This is a "busy" record, don't want
             * to do this until build is complete. */
            build=0;
            pvPut(build);
        } state monitor_inputs
    }


    state execute {
        when () {
            /* Set busy flag */
            execState = EXECUTE_STATE_MOVE_START;
            pvPut(execState);
            /* Set status to INVALID */
            execStatus = STATUS_UNDEFINED;
            pvPut(execStatus);
            /* Get the initial positions of the motors */
            for (j=0; j<numAxes; j++) initialPos[j] = epicsMotorPos[j];
            /* Move to start position if required */
            if (moveMode == MOVE_MODE_ABSOLUTE) {
                for (j=0; j<numAxes; j++) {
                    if (!moveAxis[j]) continue;
                    epicsMotorPos[j] = motorTrajectory[j][0];
                    pvPut(epicsMotorPos[j]);
                }
                waitEpicsMotors();
            }
            /* Send the execute command, along with simulation mode and time
             * scaling factor */
            sprintf(stringOut, "LS,%dEC%f",simMode,timeScale);
            writeOnly(stringOut);
            /* Get start time of execute */
            startTime = get_time();
            execState = EXECUTE_STATE_EXECUTING;
            pvPut(execState);
            /* This is an attempt to fix the problem of TP sometimes not responding */
            epicsThreadSleep(0.1);
        } state wait_execute
    }


    /* Wait for trajectory to complete */
    state wait_execute {
        when (execStatus == STATUS_ABORT) {
            /* The trajectory_abort state set has detected an abort. It has
             * already posted the status and message.  Don't execute flyback
             * return to top */
            execState = EXECUTE_STATE_DONE;
            pvPut(execState);
            /* Clear execute command, post.  This is a "busy" record, don't
             * want to do this until execution is complete. */
            execute=0;
            pvPut(execute);
        } state monitor_inputs

        when(execState==EXECUTE_STATE_EXECUTING) {
            /* Get the current motor positions, post them */
            getMotorPositions(motorCurrent);
            for (j=0; j<numAxes; j++) pvPut(motorCurrent[j]);
            /* Send XC1 command, read last trajectory element done */
            writeRead("XC1");
            /* Parse response, which is of the form XCnnnn */
            ncomplete = atoi(&stringIn[2]);
            sprintf(execMessage, "Executing element %d/%d", 
                                        ncomplete, nelements);
            pvPut(execMessage);
            anyMoving = getMotorMoving();
            if (!anyMoving) {
                execState = EXECUTE_STATE_FLYBACK;
                execStatus = STATUS_SUCCESS;
                strcpy(execMessage, " ");
            }
            /* See if the elapsed time is more than twice expected, time out */
            if (difftime(get_time(), startTime) > expectedTime*timeScale*2.) {
                execState = EXECUTE_STATE_FLYBACK;
                execStatus = STATUS_TIMEOUT;
                strcpy(execMessage, "Timeout");
            }
            /* Send TB command, read any error messages */
            writeRead("TB");
            /* Parse the return string, of form "TBx message". If 'x' is '@'
               then there is no error, else stop with error code */
            if (stringIn[2] != '@') {
                execState = EXECUTE_STATE_FLYBACK;
                execStatus = STATUS_FAILURE;
                strncpy(execMessage, stringIn, MAX_STRING_SIZE-1);
            }
        } state wait_execute

        when(execState==EXECUTE_STATE_FLYBACK) {
            pvPut(execState);
            pvPut(execStatus);
            pvPut(execMessage);
            /* Get the current motor positions, post them */
            getMotorPositions(motorCurrent);
            for (j=0; j<numAxes; j++) pvPut(motorCurrent[j]);
            for (j=0; j<numAxes; j++) {
                if (!moveAxis[j]) continue;
                epicsMotorPos[j] = motorCurrent[j];
                pvPut(epicsMotorPos[j]);
            }
            waitEpicsMotors();
            execState = EXECUTE_STATE_DONE;
            pvPut(execState);
            /* Clear execute command, post.  This is a "busy" record, don't
             * want to do this until execution is complete. */
            execute=0;
            pvPut(execute);
        } state monitor_inputs
    }


    /* Read back actual positions */
    state readback {
        when() {
            /* Set busy flag */
            readState = READ_STATE_BUSY;
            pvPut(readState);
            readStatus=STATUS_UNDEFINED;
            pvPut(readStatus);
            /* Erase the readback and error arrays */
            for (j=0; j<numAxes; j++) {
                for (i=0; i<MAX_PULSES; i++) {
                    motorReadbacks[j][i] = 0.;
                    motorError[j][i] = 0.;
                }
            }
            /* Read the actual number of trace points */
            writeRead("NQ");
            /* Parse response, which is of the form NQnnnn */
            nactual = atoi(&stringIn[2]);
            pvPut(nactual);
            /* Read actual positions */
            for (i=0; i<nactual; i++) {
                sprintf(readMessage, "Reading point %d/%d", i+1, nactual);
                pvPut(readMessage);
                sprintf(stringOut, "%dTQ", i+1);
                writeRead(stringOut);
                /* Parse the return string which is of the form 
                 * 15TQ,1TH2.7,1TP2.65,2TH3.1,2TP3.1 ... */
                tok_save = 0;
                /* Skip the first token, which is nnTQ */
                p = epicsStrtok_r(stringIn, ",", &tok_save);
                for (j=0; (j<numAxes && p!=0); j++) {
                    p = epicsStrtok_r(0, ",", &tok_save);
                    posTheory = atof(p+3);
                    p = epicsStrtok_r(0, ",", &tok_save);
                    if (epicsMotorDir[j] == 0) dir=1; else dir=-1;
                    posActual = atof(p+3);
                    motorError[j][i] = posActual-posTheory;
                    /* Convert from MM4000 units to user units */
                    posActual = posActual*dir + epicsMotorOff[j];
                    motorReadbacks[j][i] = posActual;
                }
            }
            /* Post the readback and error arrays */
            for (j=0; j<numAxes; j++) {
                pvPut(motorReadbacks[j]);
                pvPut(motorError[j]);
            }
            /* Clear busy flag */
            readState = READ_STATE_DONE;
            pvPut(readState);
            /* For now we are not handling read errors */
            readStatus = STATUS_SUCCESS;  
            pvPut(readStatus);
            strcpy(readMessage, " ");
            pvPut(readMessage);
            /* Clear readback command, post.  This is a "busy" record, don't
             * want to do this until readback is complete. */
            readback=0;
            pvPut(readback);
        } state monitor_inputs
    }
}


/* This state set simply monitors the abort input.  It is a separate state set
 * so that it is always active, no matter what the state of the trajectoryScan
 * state set. If an abort is received it sends the "AB" command to the MM4005, 
 * sets the execStatus to STATUS_ABORT and writes a message to execMessage */
ss trajectoryAbort {
    state monitorAbort {
        when (efTestAndClear(abortMon) && (abort==1)) {
            /* Send AB command */
            strcpy(stringOut,"AB");
            writeOnly(stringOut);
            execStatus = STATUS_ABORT;
            pvPut(execStatus);
            strcpy(execMessage, "Motion aborted");
            pvPut(execMessage);
            /* Clear abort command, post.  This is a "busy" record, don't
             * want to do this until abort command has been sent. */
            abort=0;
            pvPut(abort);
        } state monitorAbort
    }
}


/* functions */

/* writeOnly sends a command to the MM4005 */
int writeOnly(char *command)
{
    typename asynStatus status;
    typename size_t nwrite;
    char buffer[MAX_MM4000_STRING];

    /* Copy command so we can add terminator */
    strncpy(buffer, command, MAX_MM4000_STRING-3);
    strcat(buffer, "\r");
    status = pasynOctetSyncIO->write(pasynUser, buffer,
                               strlen(buffer), 1.0, &nwrite);
    return(status);
}

/* writeRead sends a command to the MM4005 and reads the response
 * It also writes the response string to another PV so it can be displayed. */
int writeRead(char *command)
{
    typename asynStatus status;
    typename size_t nwrite, nread;
    int eomReason;
    char buffer[MAX_MM4000_STRING];

    /* Copy command so we can add terminator */
    strncpy(buffer, command, MAX_MM4000_STRING-3);
    strcat(buffer, "\r");
    /* Use 30 second timeout, some commands take a long time to reply */
    status = pasynOctetSyncIO->writeRead(pasynUser, buffer,
                               strlen(buffer), stringIn, MAX_MM4000_STRING, 
                               30.0, &nwrite, &nread, &eomReason);
    return(status);
}


/* getMotorPositions returns the positions of each motor */
int getMotorPositions(double *pos)
{
    char *p, *tok_save;
    int j;
    int dir;

    /* Read the current positions of all the axes */
    writeRead("TP");
    /* Parse the return string which is of the form 
     * 1TP2.65,2TP3.1 ... */
    tok_save = 0;
    p = epicsStrtok_r(stringIn, ",", &tok_save);
    for (j=0; (j<numAxes && p!=0); j++) {
        if (epicsMotorDir[j] == 0) dir=1; else dir=-1;
        pos[j] = atof(p+3)*dir + epicsMotorOff[j];
        p = epicsStrtok_r(0, ",", &tok_save);
    }
    return(0);
}


/* getMotorMoving returns the moving status of each motor, packed into a single
 * int.  Bit 0 = motor 1, bit 1 = motor 2, etc. 0=not moving, 1=moving.
 * If the entire int is 0 then no motors are moving */
int getMotorMoving()
{
    char *p, *tok_save;
    int j;
    int result=0, mask=0x01;

    /* Read the current status of all the axes */
    writeRead("MS");
    /* Parse the return string which is of the form 
     * 1MSA,2MS@ ... */
    tok_save = 0;
    p = epicsStrtok_r(stringIn, ",", &tok_save);
    for (j=0; (j<numAxes && p!=0); j++) {
        /* The low order bit in the status byte is the MOVING bit */
        if (*(p+3) & 0x01) result |= mask;
        mask = mask << 1;
        p = epicsStrtok_r(0, ",", &tok_save);
    }
    return(result);
}

/* getEpicsMotorMoving returns the EPICS moving status of each motor, packed into 
 * a single int.  Bit 0 = motor 1, bit 1 = motor 2, etc. 0=not moving, 1=moving.
 * If the entire int is 0 then no motors are moving */
int getEpicsMotorMoving()
{
    int j;
    int result=0, mask=0x01;

    for (j=0; j<numAxes; j++) {
        pvGet(epicsMotorDone[j]);
        if (epicsMotorDone[j] == 0) result |= mask;
        mask = mask << 1;
    }
    return(result);
}


/* waitEpicsMotors waits for all motors to stop moving using the EPICS motor
 * records..  It reads and posts the motor positions during each loop. */
int waitEpicsMotors()
{
    int j;

    /* Logic is that we always want to post position motor positions 
     * after the end of move is detected. */
    while(getEpicsMotorMoving()) {
        /* Get the current motor positions, post them */
        for (j=0; j<numAxes; j++) {
            motorCurrent[j] = epicsMotorPos[j];
            pvPut(motorCurrent[j]);
        }
        epicsThreadSleep(POLL_INTERVAL);
    }
    for (j=0; j<numAxes; j++) {
        motorCurrent[j] = epicsMotorPos[j];
        pvPut(motorCurrent[j]);
    }
    return(0);
}

References:
Sequencer 2.2.0 Benjamin Franksen
RE: Sequencer 2.2.0 Mark Rivers

Navigate by Date:
Prev: Re: Sequencer 2.2.0 Andrew Johnson
Next: Sequencer 2.2.0.2 Benjamin Franksen
Index: 1994  1995  1996  1997  1998  1999  2000  2001  2002  2003  2004  2005  2006  2007  2008  2009  2010  2011  2012  2013  <20142015  2016  2017  2018  2019  2020  2021  2022  2023  2024 
Navigate by Thread:
Prev: Re: Sequencer 2.2.0 Andrew Johnson
Next: Sequencer 2.2.0.2 Benjamin Franksen
Index: 1994  1995  1996  1997  1998  1999  2000  2001  2002  2003  2004  2005  2006  2007  2008  2009  2010  2011  2012  2013  <20142015  2016  2017  2018  2019  2020  2021  2022  2023  2024 
ANJ, 17 Dec 2015 Valid HTML 4.01! · Home · News · About · Base · Modules · Extensions · Distributions · Download ·
· Search · EPICS V4 · IRMIS · Talk · Bugs · Documents · Links · Licensing ·