Files
motorBase/motorApp/NewportSrc/XPS_trajectoryScan.st
T

1245 lines
46 KiB
Smalltalk

program XPS_trajectoryScan("P=13BMC:,R=traj1,IPADDR=164.54.160.34,PORT=5001,"
"USERNAME=Administrator,PASSWORD=Administrator,"
"M1=m1,M2=m2,M3=m3,M4=m4,M5=m5,M6=m6,M7=m7,M8=m8,"
"GROUP=g1,"
"P1=p1,P2=p2,P3=p3,P4=p4,P5=p5,P6=p6,P7=p7,P8=p8")
/*
* This sequencer program works with trajectoryScan.db. It implements
* coordinated trajectory motion with the Newport XPS-C8 motor controller.
* It can be used with the Newport General Purpose Diffractometer or with any
* other set of motors connected to that controller.
*
* Original author: Jon Kelly, based on Mark Rivers' version for the MM4000.
* Current author: Mark Rivers
*
* Modifications:
*/
%% #include <string.h>
%% #include <stdio.h>
%% #include <math.h>
%% #include "XPS_C8_drivers.h"
%% #include "Socket.h"
%% #include "xps_ftp.h"
/* This program must be compiled with the recursive option */
option +r;
/* Maximum # of trajectory elements. The number of points XPS can accept
* is almost unlimited because the data is stored in a file but the channel
* access limit with a double data type is 2000 on R3.13 clients.
*/
#define MAX_ELEMENTS 2000
/* Maximum # of output pulses. At the moment the pulses are defined by a
* timer which is synchronised with the trajectory points so the max number
* of pulses equals the max number of elements.
*/
#define MAX_PULSES 2000
/* Note that MAX_ELEMENTS, and MAX_PULSES must be defined before
* including the trajectoryScan.h */
#include "trajectoryScan.h"
/* Buffer sizes */
#define NAME_LEN 100
/* Polling interval for waiting for motors to reach their targets */
#define POLL_INTERVAL 0.1
/* Socket timeouts */
#define POLL_TIMEOUT 1.0
#define DRIVE_TIMEOUT 100000. /* Forever */
#define ABORT_TIMEOUT 10.
/* Used within the exec state as a timeout within the while loops which wait for
* the ss xpsTrajectoryRun to catch up. The delays are 0.1 second, so 100 loops
* is 10 seconds. */
#define COUNT_TIMEOUT 100
/* The maximum size of the item names in gathering, e.g. "GROUP2.POSITIONER1.CurrentPosition" */
#define MAX_GATHERING_AXIS_STRING 60
/* Number of items per axis */
#define NUM_GATHERING_ITEMS 2
/* Total length of gathering string */
#define MAX_GATHERING_STRING MAX_GATHERING_AXIS_STRING * NUM_GATHERING_ITEMS * MAX_AXES
/* Constants used for FTP to the XPS */
#define TRAJECTORY_DIRECTORY "/Admin/public/Trajectories"
#define TRAJECTORY_FILE "TrajectoryScan.trj"
#define GATHERING_DIRECTORY "/Admin/public/"
#define GATHERING_FILE "Gathering.dat"
int status;
int i;
int j;
int k;
int anyMoving;
int ncomplete;
int nextra;
int npoints;
int dir;
int pollSocket;
int driveSocket;
int abortSocket;
int positionSocket;
int xpsStatus;
int count;
double dtime;
double posActual;
double posTheory;
double expectedTime;
double initialPos[MAX_AXES];
double trajVel;
double preDistance[MAX_AXES];
double postDistance[MAX_AXES];
double motorLowLimit[MAX_AXES];
double motorHighLimit[MAX_AXES];
double motorMinPos[MAX_AXES];
double motorMaxPos[MAX_AXES];
double pulseTime;
double pulsePeriod;
char groupName[NAME_LEN];
char xpsAddress[NAME_LEN];
char *axisName[MAX_AXES];
char macroBuf[NAME_LEN];
char motorName[NAME_LEN];
char userName[NAME_LEN];
char password[NAME_LEN];
int xpsPort;
/* Define PVs */
/* All PVs which will be accessed in local C functions need to have their index
* extracted with pvIndex() */
int motorCurrentIndex[MAX_AXES];
int epicsMotorDoneIndex[MAX_AXES];
/* Note, this should be time_t, but SNL doesn't understand that. This is
* the defininition in vxWorks. */
unsigned long startTime;
/* Define escaped C functions at end of file */
%% static int getMotorPositions(SS_ID ssId, struct UserVar *pVar, double *pos);
%% static int getMotorMoving(SS_ID ssId, struct UserVar *pVar);
%% static int waitMotors(SS_ID ssId, struct UserVar *pVar);
%% static int getEpicsMotorMoving(SS_ID ssId, struct UserVar *pVar);
%% static int waitEpicsMotors(SS_ID ssId, struct UserVar *pVar);
%% static int getSocket(SS_ID ssId, struct UserVar *pVar, double timeout);
%% static void trajectoryExecute(SS_ID ssId, struct UserVar *pVar);
%% static void buildAndVerify(SS_ID ssId, struct UserVar *pVar);
%% static int currentElement(SS_ID ssId, struct UserVar *pVar);
%% static int getGroupStatus(SS_ID ssId, struct UserVar *pVar);
%% static void readGathering(SS_ID ssId, struct UserVar *pVar);
%% static int trajectoryAbort(SS_ID ssId, struct UserVar *pVar);
ss xpsTrajectoryScan {
/* Initialize things when first starting */
state init {
when() {
/* Get the values from the macro parameteters */
strcpy(groupName, macValueGet("GROUP"));
strcpy(xpsAddress, macValueGet("IPADDR"));
strcpy(userName, macValueGet("USERNAME"));
strcpy(password, macValueGet("PASSWORD"));
xpsPort = atoi(macValueGet("PORT"));
if (debugLevel > 0) {
printf("XPS_trajectoryScan: ss xpsTrajectoryScan: state init:"
" xpsAddress=%s groupName=%s\n",
xpsAddress, groupName);
}
for (i=0; i<numAxes; i++) {
axisName[i] = malloc(NAME_LEN);
sprintf(macroBuf, "P%d", i+1);
sprintf(axisName[i], "%s.%s", groupName, macValueGet(macroBuf));
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);
}
xpsStatus = 0;
ncomplete = 1;
/* Set the arrays to zero */
for (i=0; i<MAX_ELEMENTS; i++) {
for (j=0; j<MAX_AXES; j++) {
motorTrajectory[j][i] = 0.0;
}
}
/* Force numAxes to be <= MAX_AXES */
if (numAxes > MAX_AXES) numAxes = MAX_AXES;
/* Get sockets for communicating with XPS */
%%pVar->pollSocket = getSocket(ssId, pVar, POLL_TIMEOUT);
%%pVar->driveSocket = getSocket(ssId, pVar, DRIVE_TIMEOUT);
%%pVar->abortSocket = getSocket(ssId, pVar, ABORT_TIMEOUT);
for (j=0; j<numAxes; j++) {
motorCurrentIndex[j] = pvIndex(motorCurrent[j]);
epicsMotorDoneIndex[j] = pvIndex(epicsMotorDone[j]);
}
/* Clear all event flags */
efClear(buildMon);
efClear(executeMon);
efClear(abortMon);
efClear(readbackMon);
efClear(nelementsMon);
} state monitor_inputs
}
/* Monitor inputs which control what to do (Build, Execute, Read) */
state monitor_inputs {
entry {
if (debugLevel > 0) {
printf("XPS_trajectoryScan: ss xpsTrajectoryScan: state monitor_inputs:"
" entry\n");
}
}
when(efTestAndClear(buildMon) && (build==1)) {
} state build
when(efTestAndClear(executeMon) && (execute==1)
&& (buildStatus == STATUS_SUCCESS)){
} state execute
when(efTestAndClear(readbackMon) && (readback==1)
&& (execStatus == STATUS_SUCCESS)) {
} 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. */
if (moveMode == MOVE_MODE_RELATIVE)
endPulses = nelements;
else
endPulses = nelements-1;
pvPut(endPulses);
} state monitor_inputs
}
/* Build and verify trajectory */
state build {
entry {
if (debugLevel > 0) {
printf("XPS_trajectoryScan: ss xpsTrajectoryScan: state build:"
" entry\n");
}
}
when() {
/* Set busy flag while building */
buildState = BUILD_STATE_BUSY;
pvPut(buildState);
buildStatus=STATUS_UNDEFINED;
pvPut(buildStatus);
if (moveMode == MOVE_MODE_RELATIVE)
npoints = nelements;
else
npoints = nelements-1;
/* If total time mode, calc time per element and write,
else use the array timeTraj */
if (timeMode == TIME_MODE_TOTAL) {
dtime = time/npoints;
for (i=0; i<nelements; i++) timeTrajectory[i] = dtime;
pvPut(timeTrajectory);
}
/* Compute expected time for trajectory & check element period */
expectedTime=0;
for (i=0; i<npoints; i++) {
expectedTime += timeTrajectory[i];
}
%%buildAndVerify(ssId, pVar);
/* Export values to PVs */
for (j=0; j<numAxes; j++) {
pvPut(motorMVA[j]);
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);
/* Set exec status to UNDEFINED */
execStatus = STATUS_UNDEFINED;
pvPut(execStatus);
strcpy(execMessage, " ");
} state monitor_inputs
}
state execute {
entry {
if (debugLevel > 0) {
printf("XPS_trajectoryScan: ss xpsTrajectoryScan: state execute:"
" entry\n");
}
%%waitMotors(ssId, pVar);
/* Get the initial positions of the motors */
for (j=0; j<numAxes; j++) initialPos[j] = epicsMotorPos[j];
/* Move to start position if required.
* Subtract distance of initial acceleration element */
if (moveMode == MOVE_MODE_ABSOLUTE) {
for (j=0; j<numAxes; j++) {
if (moveAxis[j]) {
epicsMotorPos[j] = motorTrajectory[j][0] - preDistance[j];
pvPut(epicsMotorPos[j]);
}
}
} else {
/* Backup by distance of initial acceleration element */
for (j=0; j<numAxes; j++) {
if (moveAxis[j]) {
epicsMotorPos[j] = epicsMotorPos[j] - preDistance[j];
pvPut(epicsMotorPos[j]);
}
}
}
%%waitEpicsMotors(ssId, pVar);
/* Define pulse output for trajectory */
if (npulses > 0) {
/* Check validity, modify values if necessary */
if (startPulses < 1) startPulses = 1;
if (startPulses > nelements) startPulses = nelements;
pvPut(startPulses);
if (endPulses < startPulses) endPulses = startPulses;
if (endPulses > nelements) endPulses = nelements;
pvPut(endPulses);
/* The XPS can only output pulses at a fixed period, not a fixed
* distance along the trajectory.
* The trajectory elements where pulses start and stop are
* defined with the PVs startPulses and endPulses.
* Compute the time between pulses as the total time over which pulses
* should be output divided by the number of pulses to be output. */
pulseTime=0;
for (i=startPulses; i<=endPulses; i++) {
pulseTime += timeTrajectory[i-1];
}
pulsePeriod = pulseTime/npulses;
} else {
pulsePeriod = 0.;
}
/* Check that the trajectory won't exceed the software limits */
for (j=0; j<numAxes; j++) {
if (moveAxis[j]) {
status = PositionerUserTravelLimitsGet(pollSocket,
axisName[j],
&motorLowLimit[j],
&motorHighLimit[j]);
if ((epicsMotorPos[j] + motorMinPos[j]) < motorLowLimit[j]) {
execStatus = STATUS_FAILURE;
pvPut(execStatus);
sprintf(execMessage, "Low soft limit violation on motor %d", j);
pvPut(execMessage);
}
if ((epicsMotorPos[j] + motorMaxPos[j]) > motorHighLimit[j]) {
execStatus = STATUS_FAILURE;
pvPut(execStatus);
sprintf(execMessage, "High soft limit violation on motor %d", j);
pvPut(execMessage);
}
}
}
}
when (execStatus == STATUS_FAILURE) {
/* Clear execute command, post. This is a "busy" record, don't
* want to do this until execution is complete. */
if (debugLevel > 0) {
printf("XPS_trajectoryScan: ss xpsTrajectoryScan: state execute:"
" execStatus = STATUS_FAILURE\n");
}
execute=0;
pvPut(execute);
} state monitor_inputs
when (execStatus != STATUS_FAILURE) {
%%pVar->xpsStatus = getGroupStatus(ssId, pVar);
/* Setting execState here will cause the xpsTrajectoryRun SS to wake up */
if (debugLevel > 0) {
printf("XPS_trajectoryScan: ss xpsTrajectoryScan: state execute:"
" setting execState = EXECUTE_STATE_MOVE_START\n");
}
execState = EXECUTE_STATE_MOVE_START;
pvPut(execState);
count = 0;
while (execState != EXECUTE_STATE_EXECUTING &&
count < COUNT_TIMEOUT ) {
epicsThreadSleep(0.1);
count++;
}
if (count == COUNT_TIMEOUT) {
strcpy(execMessage, "Exec Timeout!");
pvPut(execMessage);
execStatus = STATUS_ABORT;
pvPut(execStatus);
}
count = 0;
/* Wait until ss xpsTrajectoryRun has started the traj scan */
while (xpsStatus != 45 &&
count < COUNT_TIMEOUT &&
execStatus != STATUS_FAILURE ) {
epicsThreadSleep(0.1);
count++;
%%pVar->xpsStatus = getGroupStatus(ssId, pVar);
}
if (count == COUNT_TIMEOUT) {
strcpy(execMessage, "Exec Timeout!");
pvPut(execMessage);
execStatus = STATUS_ABORT;
pvPut(execStatus);
}
readStatus=STATUS_UNDEFINED;
pvPut(readStatus);
} state wait_execute
}
/* Wait for trajectory to complete */
state wait_execute {
entry {
if (debugLevel > 0) {
printf("XPS_trajectoryScan: ss xpsTrajectoryScan: state wait_execute:"
" entry\n");
}
}
when (execStatus == STATUS_ABORT) {
if (debugLevel > 0) {
printf("XPS_trajectoryScan: ss xpsTrajectoryScan: state wait_execute:"
" execStatus = STATUS_ABORT\n");
}
/* 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 (delay(0.1) && execState==EXECUTE_STATE_EXECUTING) {
/* Get the current motor positions, post them */
%%getMotorPositions(ssId, pVar, pVar->motorCurrent);
for (j=0; j<numAxes; j++) pvPut(motorCurrent[j]);
%%pVar->xpsStatus = getGroupStatus(ssId, pVar);
if (xpsStatus == 45) {
%%pVar->ncomplete = currentElement(ssId, pVar);
sprintf(execMessage, "Executing element %d/%d",
ncomplete, nelements);
pvPut(execMessage);
}
else if (xpsStatus == 12) {
/* 12 = ready from move */
execState = EXECUTE_STATE_FLYBACK;
execStatus = STATUS_SUCCESS;
strcpy(execMessage, " ");
}
else if (xpsStatus < 10) {
/* The xps group status reflects an error. */
execState = EXECUTE_STATE_FLYBACK;
execStatus = STATUS_FAILURE;
sprintf(execMessage,"XPS Status Error=%d", xpsStatus);
}
/* See if the elapsed time is more than expected, time out */
if (difftime(time(0), startTime) > (expectedTime+10)) {
execState = EXECUTE_STATE_FLYBACK;
execStatus = STATUS_TIMEOUT;
strcpy(execMessage, "Timeout");
}
} state wait_execute
when (execState==EXECUTE_STATE_FLYBACK) {
if (debugLevel > 0) {
printf("XPS_trajectoryScan: ss xpsTrajectoryScan: state wait_execute:"
" execStatus = EXECUTE_STATE_FLYBACK\n");
}
/* Stop the detector */
detOff = 1;
pvPut(detOff);
pvPut(execState);
pvPut(execStatus);
pvPut(execMessage);
/* Only do the following if the trajectory executed OK */
if (execStatus == STATUS_SUCCESS) {
/* Make sure the motors have stopped */
%%waitMotors(ssId, pVar);
%%waitEpicsMotors(ssId, pVar);
/* Get the current motor positions, post them */
%%getMotorPositions(ssId, pVar, pVar->motorCurrent);
for (j=0; j<numAxes; j++) pvPut(motorCurrent[j]);
for (j=0; j<numAxes; j++) {
epicsMotorPos[j] = motorCurrent[j];
pvPut(epicsMotorPos[j]);
}
%%waitEpicsMotors(ssId, pVar);
}
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 {
entry {
if (debugLevel > 0) {
printf("XPS_trajectoryScan: ss xpsTrajectoryScan: state readback:"
" entry\n");
}
}
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.;
}
}
%%readGathering(ssId, pVar);
/* readGathering has set the actual number of positions read back */
pvPut(nactual);
/* 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. */
ss xpsTrajectoryAbort {
state monitorAbort {
when ((efTestAndClear(abortMon)) && (abort==1) &&
(execState==EXECUTE_STATE_EXECUTING)) {
execStatus = STATUS_ABORT;
if (debugLevel > 0) {
printf("XPS_trajectoryScan: ss xpsTrajectoryAbort: state monitorAbort:"
" setting execStatus = STATUS_ABORT\n");
}
pvPut(execStatus);
strcpy(execMessage, "Motion aborted");
pvPut(execMessage);
%%trajectoryAbort(ssId, pVar);
/* 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
}
}
/* This state was required because the trajectory execute call does not return
* until the end of the trajectory. So to poll during the scan we call the
* scan from a separate state set. */
ss xpsTrajectoryRun {
state asyncExecute {
when (efTestAndClear(execStateMon) && (execState == EXECUTE_STATE_MOVE_START)) {
/*%%pVar->xpsStatus = getGroupStatus(ssId, pVar);*/
if (debugLevel > 0) {
printf("XPS_trajectoryScan: ss xpsTrajectoryRun: state asyncExecute:"
" setting execState = EXECUTE_STATE_EXECUTING\n");
}
execState = EXECUTE_STATE_EXECUTING;
pvPut(execState);
/* If ready to move */
if (xpsStatus > 9 && xpsStatus < 20) {
/* Start the detector */
detOn = 1;
pvPut(detOn);
/* Get start time of execute */
startTime = time(0);
/* Call the C function from here so that the main state set can poll */
%%trajectoryExecute(ssId, pVar);
} else {
execStatus = STATUS_FAILURE;
pvPut(execStatus);
}
} state asyncExecute
}
}
/* This state set polls every second to keep the motor current position medm
* screen up to date when a scan is not running */
ss xpsTrajectoryPosition {
state positionUpdate {
when (delay (1) && (execState == EXECUTE_STATE_DONE)) {
%%getMotorPositions(ssId, pVar, pVar->motorCurrent);
for (j=0; j<numAxes; j++) pvPut(motorCurrent[j]);
} state positionUpdate
}
}
/* C functions */
%{
/* getMotorPositions returns the positions of each motor.
* It reads the positions from the controller, and then converts to
* EPICS motor user coordinates */
static int getMotorPositions(SS_ID ssId, struct UserVar *pVar, double *pos)
{
int status;
int j;
int dir;
/* Read the current positions of all the axes */
status = GroupPositionCurrentGet(pVar->positionSocket,
pVar->groupName,pVar->numAxes,pos);
if (status != 0)
printf("Error performing GroupPositionCurrentGet%i\n", status);
/* Convert from XPS units (which are EPICS motor dial units) to user units */
for (j=0; j<pVar->numAxes; j++) {
if (pVar->epicsMotorDir[j] == 0) dir=1; else dir=-1;
pos[j] = pos[j]*dir + pVar->epicsMotorOff[j];
}
return(status);
}
/* Returns 0 when no motors are moving */
static int getMotorMoving(SS_ID ssId, struct UserVar *pVar)
{
int status;
int moving=0;
int groupStatus;
/* Read the current status of the group */
status = GroupStatusGet(pVar->pollSocket,pVar->groupName,&groupStatus);
if (status != 0)
printf("Error performing GroupStatusGet %i\n",status);
if (groupStatus > 42)
moving = 1;
return(moving);
}
/* 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 */
static int getEpicsMotorMoving(SS_ID ssId, struct UserVar *pVar)
{
int j;
int result=0, mask=0x01;
for (j=0; j<pVar->numAxes; j++) {
seq_pvGet(ssId, pVar->epicsMotorDoneIndex[j], 0);
if (pVar->epicsMotorDone[j] == 0) result |= mask;
mask = mask << 1;
}
return(result);
}
/* waitMotors waits for all motors to stop moving. It reads and posts the
* motor positions during each loop. */
static int waitMotors(SS_ID ssId, struct UserVar *pVar)
{
int j;
/* Logic is that we always want to post position motor positions
* after the end of move is detected. */
while(getMotorMoving(ssId, pVar)) {
/* Get the current motor positions, post them */
getMotorPositions(ssId, pVar, pVar->motorCurrent);
for (j=0; j<pVar->numAxes; j++) seq_pvPut(ssId, pVar->motorCurrentIndex[j], 0);
epicsThreadSleep(POLL_INTERVAL);
}
getMotorPositions(ssId, pVar, pVar->motorCurrent);
for (j=0; j<pVar->numAxes; j++) seq_pvPut(ssId, pVar->motorCurrentIndex[j], 0);
return(0);
}
/* waitEpicsMotors waits for all motors to stop moving using the EPICS motor
* records.. It reads and posts the motor positions during each loop. */
static int waitEpicsMotors(SS_ID ssId, struct UserVar *pVar)
{
int j;
/* Logic is that we always want to post position motor positions
* after the end of move is detected. */
while(getEpicsMotorMoving(ssId, pVar)) {
/* Get the current motor positions, post them */
for (j=0; j<pVar->numAxes; j++) {
pVar->motorCurrent[j] = pVar->epicsMotorPos[j];
seq_pvPut(ssId, pVar->motorCurrentIndex[j], 0);
}
epicsThreadSleep(POLL_INTERVAL);
}
for (j=0; j<pVar->numAxes; j++) {
pVar->motorCurrent[j] = pVar->epicsMotorPos[j];
seq_pvPut(ssId, pVar->motorCurrentIndex[j], 0);
}
return(0);
}
/* Function to ask the XPS for a socket this requires Socket.h */
static int getSocket(SS_ID ssId, struct UserVar *pVar, double timeout)
{
int sock = 0;
sock = TCP_ConnectToServer(pVar->xpsAddress, pVar->xpsPort, timeout);
if (sock < 0)
printf("Error TCP_ConnectToServer, status=%d\n",sock);
return (sock);
}
/* Function to setup the gathering, events and start the trajectory */
static void trajectoryExecute(SS_ID ssId, struct UserVar *pVar)
{
int status;
int j;
char buffer[MAX_GATHERING_STRING];
int eventId;
if (pVar->debugLevel > 0) {
printf("XPS_trajectoryScan: trajectoryExecute:"
" entry\n");
}
/* Configure Gathering */
/* Reset gathering.
* This must be done because GatheringOneData just appends to in-memory list */
if (pVar->debugLevel > 0) {
printf("XPS_trajectoryScan: trajectoryExecute:"
" calling GatheringReset(%d)\n", pVar->pollSocket);
}
status = GatheringReset(pVar->pollSocket);
if (status != 0) {
printf("Error performing GatheringReset, status=%d\n",status);
return;
}
/* Write list of gathering parameters.
* Note that there must be NUM_GATHERING_ITEMS per axis in this list. */
strcpy(buffer, "");
for (j=0; j<pVar->numAxes; j++) {
strcat (buffer, pVar->axisName[j]);
strcat (buffer, ".SetpointPosition;");
strcat (buffer, pVar->axisName[j]);
strcat (buffer, ".CurrentPosition;");
}
/* Define what is to be saved in the GatheringExternal.dat.
* 3 pieces of information per axis. */
if (pVar->debugLevel > 0) {
printf("XPS_trajectoryScan: trajectoryExecute:"
" calling GatheringConfigurationSet(%d, %d, %s)\n",
pVar->pollSocket, pVar->numAxes*NUM_GATHERING_ITEMS, buffer);
}
status = GatheringConfigurationSet(pVar->pollSocket,
pVar->numAxes*NUM_GATHERING_ITEMS, buffer);
if (status != 0)
printf("Error performing GatheringConfigurationSet, status=%d, buffer=%p\n",
status, buffer);
/* Define trajectory output pulses.
* startPulses and endPulses are defined as 1=first real element, need to add
* 1 to each to skip the acceleration element.
* The XPS is told the element to stop outputting pulses, and it seems to stop
* outputting at the start of that element. So we need to have that element be
* the decceleration endPulses is the element, which means adding another +1. */
if (pVar->debugLevel > 0) {
printf("XPS_trajectoryScan: trajectoryExecute:"
" calling MultipleAxesPVTPulseOutputSet(%d, %s, %d, %d, %f)\n",
pVar->pollSocket, pVar->groupName,
pVar->startPulses+1,
pVar->endPulses+2,
pVar->pulsePeriod);
}
status = MultipleAxesPVTPulseOutputSet(pVar->pollSocket, pVar->groupName,
pVar->startPulses+1,
pVar->endPulses+2,
pVar->pulsePeriod);
/* Define trigger */
sprintf(buffer, "Always;%s.PVT.TrajectoryPulse", pVar->groupName);
if (pVar->debugLevel > 0) {
printf("XPS_trajectoryScan: trajectoryExecute:"
" calling EventExtendedConfigurationTriggerSet(%d, %d, %s, %s, %s, %s, %s)\n",
pVar->pollSocket, 2, buffer,
"", "", "", "");
}
status = EventExtendedConfigurationTriggerSet(pVar->pollSocket, 2, buffer,
"", "", "", "");
if (status != 0) {
printf("Error performing EventExtendedConfigurationTriggerSet, status=%d, buffer=%s\n",
status, buffer);
return;
}
/* Define action */
if (pVar->debugLevel > 0) {
printf("XPS_trajectoryScan: trajectoryExecute:"
" calling EventExtendedConfigurationActionSet(%d, %d, %s, %s, %s, %s, %s)\n",
pVar->pollSocket, 1,
"GatheringOneData",
"", "", "", "");
}
status = EventExtendedConfigurationActionSet(pVar->pollSocket, 1,
"GatheringOneData",
"", "", "", "");
if (status != 0) {
printf("Error performing EventExtendedConfigurationActionSet, status=%d\n",
status);
return;
}
/* Start gathering */
if (pVar->debugLevel > 0) {
printf("XPS_trajectoryScan: trajectoryExecute:"
" calling EventExtendedStart(%d, %p)\n",
pVar->pollSocket, &eventId);
}
status= EventExtendedStart(pVar->pollSocket, &eventId);
if (status != 0) {
printf("Error performing EventExtendedStart, status=%d\n",status);
return;
}
if (pVar->debugLevel > 0) {
printf("XPS_trajectoryScan: trajectoryExecute:"
" calling MultipleAxesPVTExecution(%d, %s, %s, %d)\n",
pVar->driveSocket, pVar->groupName,
TRAJECTORY_FILE, 1);
}
status = MultipleAxesPVTExecution(pVar->driveSocket, pVar->groupName,
TRAJECTORY_FILE, 1);
/* status -27 means the trajectory was aborted */
if ((status != 0) && (status != -27))
printf("Error performing MultipleAxesPVTExecution, status=%d\n", status);
/* Remove the event */
if (pVar->debugLevel > 0) {
printf("XPS_trajectoryScan: trajectoryExecute:"
" calling EventExtendedRemove(%d, %d)\n", pVar->pollSocket, eventId);
}
status = EventExtendedRemove(pVar->pollSocket, eventId);
if (status != 0) {
printf("Error performing ExtendedEventRemove, status=%d\n",status);
return;
}
/* Save the gathered data to a file */
if (pVar->debugLevel > 0) {
printf("XPS_trajectoryScan: trajectoryExecute:"
" calling GatheringStopAndSave(%d)\n", pVar->pollSocket);
}
status = GatheringStopAndSave(pVar->pollSocket);
/* status -30 means gathering not started i.e. aborted before the end of
1 trajectory element */
if ((status != 0) && (status != -30))
printf("Error performing GatheringStopAndSave, status=%d\n", status);
return;
}
/* Function to build, install and verify trajectory */
static void buildAndVerify(SS_ID ssId, struct UserVar *pVar)
{
FILE *trajFile;
int i, j, status;
int npoints;
double trajVel;
double D0, D1, T0, T1;
int ftpSocket;
char fileName[NAME_LEN];
double distance;
double maxVelocity[MAX_AXES], maxAcceleration[MAX_AXES];
double minJerkTime[MAX_AXES], maxJerkTime[MAX_AXES];
double preTimeMax, postTimeMax;
double preVelocity[MAX_AXES], postVelocity[MAX_AXES];
double time;
int dir[MAX_AXES];
if (pVar->debugLevel > 0) {
printf("XPS_trajectoryScan: buildAndVerify:"
" entry\n");
}
/* We create trajectories with an extra element at the beginning and at the end.
* The distance and time of the first element is defined so that the motors will
* accelerate from 0 to the velocity of the first "real" element at their
* maximum allowed acceleration.
* Similarly, the distance and time of last element is defined so that the
* motors will decelerate from the velocity of the last "real" element to 0
* at the maximum allowed acceleration.
/* Compute the velocity of each motor during the first real trajectory element,
* and the time required to reach this velocity. */
preTimeMax = 0.;
postTimeMax = 0.;
/* Zero values since axes may not be used */
for (j=0; j<pVar->numAxes; j++) {
preVelocity[j] = 0.;
postVelocity[j] = 0.;
}
for (j=0; j<pVar->numAxes; j++) {
if (!pVar->moveAxis[j]) continue;
status = PositionerSGammaParametersGet(pVar->pollSocket, pVar->axisName[j],
&maxVelocity[j], &maxAcceleration[j],
&minJerkTime[j], &maxJerkTime[j]);
if (status != 0) {
printf("Error calling positionerSGammaParametersSet, status=%d\n",
status);
}
/* The calculation using maxAcceleration read from controller below
* is "correct" but subject to roundoff errors when sending ASCII commands
* to XPS. Reduce acceleration 10% to account for this. */
maxAcceleration[j] = 0.9 * maxAcceleration[j];
/* Note: the preDistance and postDistance numbers computed here are
* in user coordinates, not XPS coordinates, because they are used for
* EPICS moves at the start and end of the scan */
if (pVar->moveMode == MOVE_MODE_RELATIVE) {
distance = pVar->motorTrajectory[j][0];
} else {
distance = pVar->motorTrajectory[j][1] - pVar->motorTrajectory[j][0];
}
preVelocity[j] = distance/pVar->timeTrajectory[0];
time = fabs(preVelocity[j]) / maxAcceleration[j];
if (time > preTimeMax) preTimeMax = time;
if (pVar->moveMode == MOVE_MODE_RELATIVE) {
distance = pVar->motorTrajectory[j][pVar->nelements-1];
} else {
distance = pVar->motorTrajectory[j][pVar->nelements-1] -
pVar->motorTrajectory[j][pVar->nelements-2];
}
postVelocity[j] = distance/pVar->timeTrajectory[pVar->nelements-1];
time = fabs(postVelocity[j]) / maxAcceleration[j];
if (time > postTimeMax) postTimeMax = time;
}
/* Compute the distance that each motor moves during its acceleration phase.
* Only move it this far. */
for (j=0; j<pVar->numAxes; j++) {
pVar->preDistance[j] = 0.5 * preVelocity[j] * preTimeMax;
pVar->postDistance[j] = 0.5 * postVelocity[j] * postTimeMax;
}
/* Create the trajectory file */
trajFile = fopen (TRAJECTORY_FILE, "w");
/* Compute the sign relationship of user coordinates to XPS coordinates for
* each axis */
for (j=0; j<pVar->numAxes; j++) {
if (pVar->epicsMotorDir[j] == 0) dir[j]=1; else dir[j]=-1;
}
/* Create the initial acceleration element */
fprintf(trajFile,"%f", preTimeMax);
for (j=0; j<pVar->numAxes; j++)
fprintf(trajFile,", %f, %f", pVar->preDistance[j]*dir[j], preVelocity[j]*dir[j]);
fprintf(trajFile,"\n");
/* The number of points in the file is nelements for MOVE_MODE_RELATIVE and
* nelements-1 for other modes */
if (pVar->moveMode == MOVE_MODE_RELATIVE)
npoints = pVar->nelements;
else
npoints = pVar->nelements-1;
for (i=0; i<npoints; i++) {
T0 = pVar->timeTrajectory[i];
if (i < npoints-1)
T1 = pVar->timeTrajectory[i+1];
else
T1 = T0;
for (j=0; j<pVar->numAxes; j++) {
if (pVar->moveMode == MOVE_MODE_RELATIVE) {
D0 = pVar->motorTrajectory[j][i] * dir[j];
if (i < npoints-1)
D1 = pVar->motorTrajectory[j][i+1] * dir[j];
else
D1 = D0;
} else {
D0 = pVar->motorTrajectory[j][i+1] *dir[j] -
pVar->motorTrajectory[j][i] * dir[j];
if (i < npoints-1)
D1 = pVar->motorTrajectory[j][i+2] * dir[j] -
pVar->motorTrajectory[j][i+1] * dir[j];
else
D1 = D0;
}
/* Average either side of the point? */
trajVel = ((D0 + D1) / (T0 + T1));
if (!(pVar->moveAxis[j])) {
D0 = 0.0; /* Axis turned off*/
trajVel = 0.0;
}
if (j == 0) fprintf(trajFile,"%f", pVar->timeTrajectory[i]);
fprintf(trajFile,", %f, %f",D0,trajVel);
if (j == (pVar->numAxes-1)) fprintf(trajFile,"\n");
}
}
/* Create the final acceleration element. Final velocity must be 0. */
fprintf(trajFile,"%f", postTimeMax);
for (j=0; j<pVar->numAxes; j++)
fprintf(trajFile,", %f, %f", pVar->postDistance[j]*dir[j], 0.);
fprintf(trajFile,"\n");
fclose (trajFile);
/* FTP the trajectory file from the local directory to the XPS */
status = ftpConnect(pVar->xpsAddress, pVar->userName, pVar->password, &ftpSocket);
if (status != 0) {
printf("Error calling ftpConnect, status=%d\n", status);
return;
}
status = ftpChangeDir(ftpSocket, TRAJECTORY_DIRECTORY);
if (status != 0) {
printf("Error calling ftpChangeDir, status=%d\n", status);
return;
}
status = ftpStoreFile(ftpSocket, TRAJECTORY_FILE);
if (status != 0) {
printf("Error calling ftpStoreFile, status=%d\n", status);
return;
}
status = ftpDisconnect(ftpSocket);
if (status != 0) {
printf("Error calling ftpDisconnect, status=%d\n", status);
return;
}
/* Verify trajectory */
if (pVar->debugLevel > 0) {
printf("XPS_trajectoryScan: buildAndVerify:"
" calling MultipleAxesPVTVerification(%d, %s, %s)\n",
pVar->pollSocket, pVar->groupName, TRAJECTORY_FILE);
}
status = MultipleAxesPVTVerification(pVar->pollSocket, pVar->groupName,
TRAJECTORY_FILE);
pVar->buildStatus = STATUS_FAILURE;
if (status == 0) {
strcpy(pVar->buildMessage, " ");
pVar->buildStatus = STATUS_SUCCESS;
}
else if (status == -69)
strcpy(pVar->buildMessage, "Acceleration Too High");
else if (status == -68)
strcpy(pVar->buildMessage, "Velocity Too High");
else if (status == -70)
strcpy(pVar->buildMessage, "Final Velocity Non Zero");
else if (status == -75)
strcpy(pVar->buildMessage, "Negative or Null Delta Time");
else
sprintf(pVar->buildMessage, "Unknown trajectory verify error=%d", status);
/* Read dynamic parameters*/
if (status == 0){
pVar->buildStatus = STATUS_SUCCESS;
}
if (1) { /* We may need to test for status here */
for (j=0; j<pVar->numAxes; j++) {
status = MultipleAxesPVTVerificationResultGet(pVar->pollSocket,
pVar->axisName[j], fileName, &pVar->motorMinPos[j], &pVar->motorMaxPos[j],
&pVar->motorMVA[j], &pVar->motorMAA[j]);
if (status != 0) {
printf("Error performing MultipleAxesPVTVerificationResultGet for axis %s, status=%d\n",
pVar->axisName[j], status);
}
}
} else {
for (j=0; j<pVar->numAxes; j++) {
pVar->motorMVA[j] = 0;
pVar->motorMAA[j] = 0;
}
}
return;
}
/* Function returns the current trajectory element*/
static int currentElement(SS_ID ssId, struct UserVar *pVar)
{
int status;
int number;
char fileName[100];
status = MultipleAxesPVTParametersGet(pVar->pollSocket,
pVar->groupName, fileName, &number);
if (status != 0)
printf("Error performing MultipleAxesPVTParametersGet, status=%d\n",
status);
return (number);
}
static int getGroupStatus(SS_ID ssId, struct UserVar *pVar)
{
int status;
int groupStatus;
/* Read the current status of the group */
status = GroupStatusGet(pVar->pollSocket,pVar->groupName,&groupStatus);
if (status != 0)
printf("Error performing GroupStatusGet, status=%d\n", status);
return(groupStatus);
}
/* Function to load the GatheringExternal.dat file which was written
* by the XPS after the trajectory was performed and read back the
* actual motor positions and calculate the position errors */
static void readGathering(SS_ID ssId, struct UserVar *pVar)
{
char buffer[MAX_GATHERING_STRING];
FILE *gatheringFile;
int i,j;
int nitems;
double setpointPosition, actualPosition;
int ftpSocket;
int status;
int dir;
if (pVar->debugLevel > 0) {
printf("XPS_trajectoryScan: readGathering:"
" entry\n");
}
/* FTP the gathering file from the XPS to the local directory */
status = ftpConnect(pVar->xpsAddress, pVar->userName, pVar->password, &ftpSocket);
if (status != 0) {
printf("Error calling ftpConnect, status=%d\n", status);
return;
}
status = ftpChangeDir(ftpSocket, GATHERING_DIRECTORY);
if (status != 0) {
printf("Error calling ftpChangeDir, status=%d\n", status);
return;
}
status = ftpRetrieveFile(ftpSocket, GATHERING_FILE);
if (status != 0) {
printf("Error calling ftpRetrieveFile, status=%d\n", status);
return;
}
status = ftpDisconnect(ftpSocket);
if (status != 0) {
printf("Error calling ftpDisconnect, status=%d\n", status);
return;
}
gatheringFile = fopen(GATHERING_FILE, "r");
/* Read 1st 2 lines which only contain the axis names */
for (i=0; i<2; i++) {
fgets (buffer, MAX_GATHERING_STRING, gatheringFile);
}
/* The next line is the motor positions at the start of the trajectory element
* where pulses began to be output. Skip this line because this pulse just
* started the MCS counting, there are no data yet. */
fgets (buffer, MAX_GATHERING_STRING, gatheringFile);
for (i=0; i<pVar->npulses; i++) {
for (j=0; j<pVar->numAxes; j++) {
/* Note the trailing white space in this format is required to make
* fscanf read the newline */
nitems = fscanf(gatheringFile, "%lf %lf ",
&setpointPosition, &actualPosition);
if (nitems != NUM_GATHERING_ITEMS) {
printf("Error reading Gathering.dat file, nitems=%d, should be %d\n",
nitems, NUM_GATHERING_ITEMS);
goto done;
}
pVar->motorError[j][i] = actualPosition - setpointPosition;
/* Convert from XPS units to EPICS motor user units */
if (pVar->epicsMotorDir[j] == 0) dir=1; else dir=-1;
pVar->motorReadbacks[j][i] = actualPosition * dir + pVar->epicsMotorOff[j];
}
}
done:
fclose (gatheringFile);
pVar->nactual = i;
return;
}
/* Function aborts the trajectory/motion */
static int trajectoryAbort(SS_ID ssId, struct UserVar *pVar)
{
int status;
status = GroupMoveAbort(pVar->abortSocket,pVar->groupName);
if (status != 0)
printf("Error performing GroupMoveAbort, status=%d\n", status);
return (0);
}
}%