Studies of the Newport XPS controller by Vanik

From GlueXWiki
Jump to: navigation, search

These studies were done using halldxps1 XPS-C8 controller from Newport Corporation. The motion system includes a Stepper motor and a Stage for microscope beam test.

Configuration of the XPS Controller

Configuration of stepper motor ST55D and stage

First stepper motor we used was ST55D5C020 from Shinano Kenshi corporation. It has not an encoder. We connected it to the XPS-DRV01 driver module. Definition of the "stages.ini" file which contains the necessary information of the motion system is located in a "CONFIG" folder of XPS controller.

  • Connect to the XPS Controller halldxps1 using an internet browser.
  • Login as Administrator.
  • Select STAGE, then "Add Custom Stage" and fill all fields.

I defined a stage with the name Stage_ST55D. The parameters of this motion system is defined as follows:

[Stage_ST55D]
SmartStageName=
; Position servo loop type
CorrectorType=NoEncoderPosition
MotionDoneMode=Theoretical
; Driver command interface
MotorDriverInterface=AnalogStepperPosition
ScalingCurrent=3
DisplacementPerFullStep=1
PeakCurrentPerPhase=1.3
StandbyPeakCurrentPerPhase=0.6
BaseVelocity=0.
; Motor driver model
DriverName=XPS-DRV01
DriverPWMFrequency=50
DriverStepperWinding=Half
; Position encoder interface
Backlash=0
CurrentVelocityCutOffFrequency=100
CurrentAccelerationCutOffFrequency=100
PositionerMappingFileName=
; Limit sensors input plug
ServitudesType=StandardEORDriverPlug
MinimumTargetPosition=-10000000
MaximumTargetPosition=+10000000
HomePreset=0
MaximumVelocity=400
MaximumAcceleration=100
EmergencyDecelerationMultiplier=4
MinimumJerkTime=0.005
MaximumJerkTime=0.05
TrackingCutOffFrequency=5
; Home search process
HomeSearchSequenceType=MinusEndOfRunHomeSearch
; HomeSearchSequenceType=CurrentPositionAsHome
; HomeSearchSequenceType=PlusEndOfRunHomeSearch
; HomeSearchSequenceType=MechanicalZeroHomeSearch
HomeSearchMaximumVelocity=250
HomeSearchMaximumAcceleration=70
HomeSearchTimeOut=600.

Configuration of system and motion groups

Building of "sistem.ini" file which is located in the "CONFIG" folder of the controller.

  • Select SYSTEM then "Manual Configuration" and define motion groups and stages on them.

I defined one MultipleAxes group - with Mult1 group name and with P1 positioner name for the Stage_ST55D.

sistem.ini file format:

[GENERAL]
BootScriptFileName = 
BootScriptArguments = 
[GROUPS]
SingleAxisInUse = 
SpindleInUse = 
XYInUse = 
XYZInUse = 
MultipleAxesInUse =  Mult1
[Mult1]
PositionerNumber = 1
PositionerInUse = P1
InitializationAndHomeSearchSequence = Together
[Mult1.P1]
PlugNumber = 1
StageName = Stage_ST55D
; Time flasher filter 
TimeFlasherBaseFrequency = 40e6 ; Hz

Testing of the XPS Controller by WEB interface

  • Connect to the XPS Controller halldxps1 using an internet browser.
  • Login as Administrator or User.
  • Select "FRONT PANEL", then "Move".
  • Select "Initialize" button for the initialization of the positioner (motion system).
  • Select "Home" for homing. After initialization, any motion group must first be homed befor any motion can be executed. After homing the Controller and the Positioner are ready.
  • "Move" button under the "FRONT PANEL". In this site, one can select some velocity and number of Absolute or Relative movements and press "Go" button. "Position" field indicates position of stage during and after movement.
  • "Jog" button under the "FRONT PANEL". I tested the jog motion, where only motion direction and speed are defined. Any time it is possible to stop movement pressing "STOP" button.
  • If a limit switch (high or low) is detected during movements then an emergency brake and the positioner will go in "NOT INITIALIZED" status. So, it is neseccary to make a new "Initialization" and "Homing" to start a new motion. To protect these situations there is a possibility to use software limits (see stage.ini file).
  • The "TERMINAL" tool allows to execute all functions (more 100) of the XPS controller.
  • The "STAGE - Modify" tool allows to review and modify all parameters of stage included in the stages.ini file.
  • Using the "Positioner errors", "Hardware status", "Driver status" under the "FRONT PANEL" or "Error file display" under the "SYSTEM" one can check errors and statuses of the motion system.

No any problem. Everything is working.

Testing of Home search process

After initialization, any motion group must be homed i.e. define an reference point as an origin. There are different homing processes available in the XPS controller. I tested several types of home definitions:

  • CurrentPositionAsHome. In this option current position is defined as a home. There is no any absolute reference point. We have no idea where is stage after shut down or a power-off cycle. This option works, but it is not useful for us.
  • Home switch as home. The home switch was put at the middle part of the stage. The homing procedure is different for the cases with and without encoder. SST55D motor has not an encoder and in this case XPS suportes MechanicalZeroHomeSearch option when only mechanical zero signal from home switch is used for home search process. XPS always defines fixed home searching direction -from high position to low position. If after initialization the current position of positioner is greater than home switch position everything is OK. Home switch will be found and positioner will be ready for any type of movements. But if current position of positioner is less than home switch position we will have a big problem. It will be impossible to find home switch. The positioner goes in "NOT INITIALIZED" status when during movements the Low limit switch is detected and then no any movements will be possible. So, we have refused this option and we will investigate this later with the motor with encoder.
  • Low/High Limit Switch As Home. In this case one of limit switches is defined as a home. I tested both options - MinusEndOfRunHomeSearch and PlusEndOfRunHomeSearch. They work. For convenience I used MinusEndOfRunHomeSearch - Low limit switchas as home option. In this case we will have positive movement axis.

Testing of the XPS Controller by EPICS

Motor_expert

Mark Rivers from the APS developed EPICS driver for XPS controller which works with the EPICS motor record. I developed CSS (Control System Studio) GUI to control it. The motor_expert.opi file is located in folder Hall-D/Default/XPS-C8 of CSS workspace. The xpsMotor.cmd command file for IOC is located in /misc/halld/Online/controls/epics/R3-14-12-2/app/iocBoot/iocxpsMotor folder.

Using this GUI one can:

  • To change some parameters of the motor (speed, acceleration, resolution) and stage (high and low limits)
  • To make several types of stage movements: Absolute move - the motion device moves from a current position to a desired destination, Relative move - the motion device moves from a current position by a defined increment, Jog - indeterminate forward or reverse motion (defines motion direction only), Home - to move the stage to the home position
  • To operate the movement (stop, pause, move, go)
  • To see the motor and stage statuses

xpsMotor.cmd file :

#!../../bin/linux-x86/xpsMotor
< envPaths
cd ${TOP}
dbLoadDatabase("dbd/xpsMotor.dbd")
xpsMotor_registerRecordDeviceDriver(pdbbase)
dbLoadTemplate "db/xpsMotor_1.substitutions"
dbLoadTemplate "db/XPSAux_1.substitutions"
#asSetFilename("ca_security.txt")
# asyn port, IP address, IP port, number of axes, 
# active poll period (ms), idle poll period (ms), 
# enable set position, set position settling time (ms)
XPSCreateController("XPS1", "129.57.37.26", 5001, 1, 10, 500, 0, 500)
asynSetTraceIOMask("XPS1", 0, 2)
# asynPort, IP address, IP port, poll period (ms)
XPSAuxConfig("XPS_AUX1", "129.57.37.26", 5001, 50)
# XPS asyn port,  axis, groupName.positionerName, stepSize
#XPSCreateAxis("XPS1",0,"SingleGroup1.Pos1",   "0")
XPSCreateAxis("XPS1",0,"Mult1.P1",   "1")
# XPS asyn port,  max points, FTP username, FTP password
# Note: this must be done after configuring axes
XPSCreateProfile("XPS1", 2000, "Administrator", "Administrator")
iocInit

PVT Trajectory scanning

PVT (Position, Velocity,Time) trajectory scanning allows to output logic pulses during the execution of some trajectory which is defined by the Move time, the End position and End speed. The profileMove.opi is the css GUI to define, build, execute PVT trajectories and read back the actual positions when the pulse was output.

xpsPVT.cmd is the command file for the IOC and is located in /misc/halld/Online/controls/epics/R3-14-12-2/app/iocBoot/iocxpsMotor folder.

xpsPVT.cmd file:

#!../../bin/linux-x86/xpsMotor
< envPaths
cd ${TOP}
#dbLoadDatabase("../../dbd/WithAsyn.dbd")
#WithAsyn_registerRecordDeviceDriver(pdbbase)
dbLoadDatabase("dbd/xpsMotor.dbd")
xpsMotor_registerRecordDeviceDriver(pdbbase)
dbLoadTemplate "db/xpsPVT.substitutions"
dbLoadTemplate "db/XPSAux_1.substitutions"
# asyn port, IP address, IP port, number of axes, 
# active poll period (ms), idle poll period (ms), 
# enable set position, set position settling time (ms)
XPSCreateController("XPS1", "129.57.37.26", 5001, 1, 10, 500, 0, 500)
asynSetTraceIOMask("XPS1", 0, 2)
# asynPort, IP address, IP port, poll period (ms)
XPSAuxConfig("XPS_AUX1", "129.57.37.26", 5001, 50)
# XPS asyn port,  axis, groupName.positionerName, stepSize
XPSCreateAxis("XPS1",0,"Mult1.P1",   "1")
# XPS asyn port,  max points, FTP username, FTP password
# Note: this must be done after configuring axes
XPSCreateProfile("XPS1", 2000, "Administrator", "Administrator")
#cd ${TOP}/iocBoot/ioc${IOC}
iocInit

Use the GUI fields to define the necessary parameters for the PVT scanning and use several two commands to define the number of trajectory elements (n), the absolute positions (Pi) and the time (Ti) for the i-th trajectory element. Positioner goes from Pi to Pi+1 position during Ti seconds.

caput -a halldxps1:Prof1:M1Positions n P1 P2 ... Pn
caput -a halldxps1:Prof1:Times n T1 T2 ... Tn

Configuration of stepper motor S22 with encoder

We changed the ST55D stepper motor with S22 motor which has an encoder. We connected it to the XPS-DRV01 driver module. Definition of the "stages.ini" file which contains the necessary information of the motion system is located in a "CONFIG" folder of XPS controller. The parameters of this motion system is defined as follows:

[Stage1]
SmartStageName=
; Position servo loop type
CorrectorType=PIPosition
ClosedLoopStatus=Closed
;ClosedLoopStatus=Opened
FatalFollowingError=1000000
KP=0
KI=12.5
IntegrationTime=100000
DeadBandThreshold=0
NotchFrequency1=0
NotchBandwidth1=0
NotchGain1=0
NotchFrequency2=0
NotchBandwidth2=0
NotchGain2=0
MotionDoneMode=Theoretical
;MotionDoneMode=VelocityAndPositionWindow
;-doesn t work-MotionDoneMode=VelocityAndPositionWindowMotionDone
MotionDonePositionThreshold=4
MotionDoneVelocityThreshold=100
MotionDoneCheckingTime=0.1
MotionDoneMeanPeriod=0.001
MotionDoneTimeout=0.5
; Driver command interface
MotorDriverInterface=AnalogStepperPosition
ScalingCurrent=3
DisplacementPerFullStep=0.00215
PeakCurrentPerPhase=1.5
StandbyPeakCurrentPerPhase=0.7
BaseVelocity=0
; Motor driver model
DriverName=XPS-DRV01
DriverPWMFrequency=50
DriverStepperWinding=Full
; Position encoder interface
EncoderType=AquadB
EncoderResolution=0.000215
LinearEncoderCorrection=0
Backlash=0
CurrentVelocityCutOffFrequency=100
CurrentAccelerationCutOffFrequency=100
PositionerMappingFileName=
; Limit sensors input plug
ServitudesType=StandardEORDriverPlug
MinimumTargetPosition=-1000000
MaximumTargetPosition=+1000000
HomePreset=0
MaximumVelocity=2.
MaximumAcceleration=8
EmergencyDecelerationMultiplier=4
MinimumJerkTime=0.005
MaximumJerkTime=0.05
TrackingCutOffFrequency=5
; Home search process
;HomeSearchSequenceType=CurrentPositionAsHome
;HomeSearchSequenceType=PlusEndOfRunHomeSearch
;HomeSearchSequenceType=MinusEndOfRunHomeSearch
;HomeSearchSequenceType=MechanicalZeroHomeSearch
;HomeSearchSequenceType=MechanicalZeroAndIndexHomeSearch
;HomeSearchSequenceType=MinusEndOfRunAndIndexHomeSearch
;HomeSearchSequenceType=IndexHomeSearch
HomeSearchSequenceType=MinusEndOfRunAndIndexHomeSearch
HomeSearchMaximumVelocity=1.5
HomeSearchMaximumAcceleration=6
HomeSearchTimeOut=600

Calibration of the stage

Length of the stage is 620mm. In this distance the motor is doing 290000 steps. So 1 step is equal to 0.00215mm. We defined mm as a unit for this stage. In the configuration file we took

DisplacementPerFullStep=0.00215 
EncoderResolution=0.000215

Distance Spaced Pulses (PCO - Position Compare Output)

Modification of the EPICS Driver for PCO

Changed files for the "motorR6-7-1"

1. $EPICS_SUPPORT/motorR6-7/motorApp/Db/Makefile
2. $EPICS_SUPPORT/motorR6-7/motorApp/NewportSrc/XPSController.h
3. $EPICS_SUPPORT/motorR6-7/motorApp/NewportSrc/XPSController.cpp
4. $EPICS_SUPPORT/motorR6-7/motorApp/NewportSrc/XPSAxis.h
5. $EPICS_SUPPORT/motorR6-7/motorApp/NewportSrc/XPSAxis.cpp
6. $EPICS_SUPPORT/motorR6-7/motorApp/MotorSrc/asynMotorAxis.h
7. $EPICS_SUPPORT/motorR6-7/motorApp/MotorSrc/asynMotorAxis.cpp
8. $EPICS/app/xpsMotorApp/Db/Makefile

1. Add several line to the $EPICS_SUPPORT/motorR6-7/motorApp/Db/Makefile

41d40
< DB += XPS_PCO.template


2. Add several lines to the $EPICS_SUPPORT/motorR6-7/motorApp/NewportSrc/XPSController.h file:

[kakoyan@halld-sc NewportSrc]$ diff XPSController.h XPSController_original.h
29,61d28
< // drvInfo strings for PCO parameters 
< #define pcoSetString                    "PCO_SET"
< #define pcoEnableString                 "PCO_ENABLE"
< #define pcoDisableString                "PCO_DISABLE"
< #define pcoMinPositionString            "PCO_MIN_POSITION"
< #define pcoMaxPositionString            "PCO_MAX_POSITION"
< #define pcoPositionStepString           "PCO_POSITION_STEP"
< #define pcoPulseWidthString             "PCO_PULSE_WIDTH"
< #define encoderSettlingTimeString       "ENCODER_SETTLING_TIME"
< #define pcoMinPositionActualString      "PCO_MIN_POSITION_ACTUAL"
< #define pcoMaxPositionActualString      "PCO_MAX_POSITION_ACTUAL"
< #define pcoPositionStepActualString     "PCO_POSITION_STEP_ACTUAL"
< #define pcoPulseWidthActualString       "PCO_PULSE_WIDTH_ACTUAL"
< #define encoderSettlingTimeActualString "ENCODER_SETTLING_TIME_ACTUAL"
< #define pcoEnableDisableStateString     "PCO_ENABLE_DISABLE_STATE"
< #define pcoPositionerNameString         "PCO_POSITIONER_NAME"
< #define pcoMessageString                "PCO_MESSAGE"
< #define pcoSetStatusString              "PCO_SET_STATUS"
< #define pcoSetMessageString             "PCO_SET_MESSAGE"
< #define pcoGetMessageString             "PCO_GET_MESSAGE"
< #define pcoEnableStatusString           "PCO_ENABLE_STATUS"
< #define pcoEnableMessageString          "PCO_ENABLE_MESSAGE"
< #define pcoDisableStatusString          "PCO_DISABLE_STATUS"
< #define pcoDisableMessageString         "PCO_DISABLE_MESSAGE"
< 
< /* Status codes for PCO Set,Enable and Disable */
< enum PcoStatus {
<   PCO_STATUS_UNDEFINED,
<   PCO_STATUS_SUCCESS,
<   PCO_STATUS_FAILURE,
<   PCO_STATUS_TIMEOUT
< };
84,89d50
<   /* These are the functions for PCO (Position Compare Output) */
<    asynStatus getPco(size_t axis);
<    asynStatus setPco(size_t axis);
<    asynStatus enablePco(size_t axis);
<    asynStatus disablePco(size_t axis);
< 
115,137d75
<   int pcoSet_;
<   int pcoEnable_;
<   int pcoDisable_;
<   int pcoMinPosition_;
<   int pcoMaxPosition_;
<   int pcoPositionStep_;
<   int pcoPulseWidth_;
<   int encoderSettlingTime_; 
<   int pcoMinPositionActual_;
<   int pcoMaxPositionActual_;
<   int pcoPositionStepActual_;
<   int pcoPulseWidthActual_;
<   int encoderSettlingTimeActual_; 
<   int pcoEnableDisableState_;
<   int pcoMessage_;
<   int pcoPositionerName_;
<   int pcoSetStatus_;
<   int pcoSetMessage_;
<   int pcoGetMessage_;
<   int pcoEnableStatus_;
<   int pcoEnableMessage_;
<   int pcoDisableStatus_;
<   int pcoDisableMessage_;

3. Add several lines to the $EPICS_SUPPORT/motorR6-7/motorApp/NewportSrc/XPSController.cpp file:

[kakoyan@halld-sc NewportSrc]$ diff XPSController.cpp XPSController_original.cpp
171,195d170
<   // Create PCO parameters
<   createParam(pcoSetString,                      asynParamInt32, &pcoSet_);    
<   createParam(pcoEnableString,                   asynParamInt32, &pcoEnable_);    
<   createParam(pcoDisableString,                  asynParamInt32, &pcoDisable_);
<   createParam(pcoMinPositionString,            asynParamFloat64, &pcoMinPosition_);     
<   createParam(pcoMaxPositionString,            asynParamFloat64, &pcoMaxPosition_);    
<   createParam(pcoPositionStepString,           asynParamFloat64, &pcoPositionStep_);    
<   createParam(pcoPulseWidthString,               asynParamInt32, &pcoPulseWidth_);
<   createParam(encoderSettlingTimeString,         asynParamInt32, &encoderSettlingTime_);
<   createParam(pcoMinPositionActualString,      asynParamFloat64, &pcoMinPositionActual_);
<   createParam(pcoMaxPositionActualString,      asynParamFloat64, &pcoMaxPositionActual_);
<   createParam(pcoPositionStepActualString,     asynParamFloat64, &pcoPositionStepActual_);
<   createParam(pcoPulseWidthActualString,       asynParamFloat64, &pcoPulseWidthActual_);
<   createParam(encoderSettlingTimeActualString, asynParamFloat64, &encoderSettlingTimeActual_);
<   createParam(pcoPositionerNameString,           asynParamOctet, &pcoPositionerName_);
<   createParam(pcoEnableDisableStateString,       asynParamInt32, &pcoEnableDisableState_);  
<   createParam(pcoMessageString,                  asynParamOctet, &pcoMessage_);
<   createParam(pcoSetStatusString,                asynParamInt32, &pcoSetStatus_);
<   createParam(pcoSetMessageString,               asynParamOctet, &pcoSetMessage_);
<   createParam(pcoGetMessageString,               asynParamOctet, &pcoGetMessage_);
<   createParam(pcoEnableStatusString,             asynParamInt32, &pcoEnableStatus_);
<   createParam(pcoEnableMessageString,            asynParamOctet, &pcoEnableMessage_);
<   createParam(pcoDisableStatusString,            asynParamInt32, &pcoDisableStatus_);
<   createParam(pcoDisableMessageString,           asynParamOctet, &pcoDisableMessage_);
< 
478,486d452
<   } else if (function == pcoMinPosition_)
<   {
<     pAxis->pcoMinPosSet_ = value;
<   } else if (function == pcoMaxPosition_)
<   {
<     pAxis->pcoMaxPosSet_ = value;
<   } else if (function == pcoPositionStep_)
<   {
<     pAxis->pcoStepSet_ = value; 
506d471
<   int axis;

513d476
<   axis = pAxis->axisNo_;
544,585d506
<   } else if (function == pcoSet_) {
<      status = setPco(axis);
<   } else if (function == pcoEnable_) {
<      status = enablePco(axis);
<   } else if (function == pcoDisable_) {
<      status = disablePco(axis);
<   } else if (function == pcoPulseWidth_) {
<        switch (value) {
<      case 0:
<        pAxis->pcoPulseWidthSet_=0.2;  
<        break;
<      case 1:
<        pAxis->pcoPulseWidthSet_=1.;  
<        break;     
<      case 2:
<        pAxis->pcoPulseWidthSet_=2.5;  
<        break;     
<      case 3:
<        pAxis->pcoPulseWidthSet_=10.;  
<        break;     
<      default: 
<        pAxis->pcoPulseWidthSet_=0.2;  
<        break;
<    }
<        switch (value) {
<      case 0:
<        pAxis->encoderSettlingTimeSet_=0.075;  
<        break;
<      case 1:
<        pAxis->encoderSettlingTimeSet_=1.;  
<        break;     
<      case 2:
<        pAxis->encoderSettlingTimeSet_=4;  
<        break;     
<      case 3:
<        pAxis->encoderSettlingTimeSet_=12.;  
<        break;     
<      default: 
<        pAxis->encoderSettlingTimeSet_=0.075;  
<        break;
<    }
1494,1764d1414
< /* --------------------------------------------------------------------*/
< /* Function to get PCO (Position Compare Output) parameters */ 
< asynStatus XPSController::getPco(size_t axis)
< {   
<    int status;
<    int enableDisableState=2;
<    bool enableState;
<    bool pcoGetOK=true;
<    char message[MAX_MESSAGE_LEN]=" ";
<    double minPositionActual;
<    double positionStepActual;
<    double pulseWidthActual;
<    double encoderSettlTimeActual; 
<    static const char *functionName = "getPco";
<    XPSAxis *pAxis;
<    pAxis = getAxis(axis);
<    asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW,
<              "%s:%s: entry\n",
<              driverName, functionName);
<    
< /* Get configured parameters of the PCO*/
<    status = PositionerPositionCompareGet(pAxis->pollSocket_,pAxis->positionerName_,&minPositionActual,&maxPositionActual,  &positionStepActual,&enableState);
<    if (status) pcoGetOK = false;
< 
<    switch (-status) {
<      case 0:
<        enableDisableState=enableState;
<        pAxes_[axis]->setDoubleParam(pcoMinPositionActual_,   minPositionActual);
<        pAxes_[axis]->setDoubleParam(pcoMaxPositionActual_,   maxPositionActual);
<        pAxes_[axis]->setDoubleParam(pcoPositionStepActual_,  positionStepActual);
<        pAxes_[axis]->setIntegerParam(pcoEnableDisableState_, enableDisableState);
<        break;
<      case 8:
<        strcpy(message, " Check the position encoder\n 'AquadB' or 'AnalogInterpolated'");
<        break;     
<      case 23:
<        strcpy(message, " Check the parameters \n Must be configured");
<        break;
<      case 21:
<        strcpy(message, "XPS initialization in progress");
<        break;
<      default: 
<        sprintf(message, "Unknown PositionerPositionCompareGet error=%d", status);
<        break;
<    }
< 
< /* Get PCO pulse parameters*/
<    status =PositionerPositionComparePulseParametersGet(pAxis->pollSocket_,pAxis->positionerName_,&pulseWidthActual, encoderSettlTimeActual);
<    if (status) pcoGetOK = false;
<   
<    switch (-status) {
<      case 0:
<        if (!(pulseWidthActual==0.2||pulseWidthActual==1.||pulseWidthActual==2.5||pulseWidthActual==10.)){       
<         strcpy(message, " Check the Pulse Width \n Must be 0.2, 1, 2.5 or 10");        
<        }
<        if (!(encoderSettlTimeActual==0.075||encoderSettlTimeActual==1.||encoderSettlTimeActual==4.||encoderSettlTimeActual==12.)){
<         strcpy(message, " Check the EncoderSettlingTime \n Must be 0.075, 1, 4 or 12");        
<        }
<        pAxes_[axis]->setDoubleParam(pcoPulseWidthActual_,    pulseWidthActual);     
<        pAxes_[axis]->setDoubleParam(encoderSettlingTimeActual_,  encoderSettlTimeActual);
<        break;
<      case 8:
<        strcpy(message, " Check the positioner\n Must not be secondary");
<        break;     
<      case 21:
<        strcpy(message, "XPS initialization in progress");
<        break;
<      default: 
<        sprintf(message, "Unknown PositionerPositionComparePulseParametersGet error=%d", status);
<        break;
<    }
< 
<    pAxes_[axis]->setStringParam(pcoGetMessage_, message);
<    pAxes_[axis]->setStringParam(pcoPositionerName_,   pAxis->positionerName_);
<    callParamCallbacks(); 
<    return pcoGetOK ? asynError : asynSuccess; 
< }
< 
< /* Function to set PCO (Position Compare Output) parameters */ 
< asynStatus XPSController::setPco(size_t axis)
< {   
<    int status;
<    bool pcoSetOK=true;
<    char message[MAX_MESSAGE_LEN];
<    int setStatus;
<    static const char *functionName = "setPco";
<    XPSAxis *pAxis;
<    pAxis = getAxis(axis);
< 
<    asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW,
<              "%s:%s: entry\n",
<              driverName, functionName);
< 
<   status=PositionerPositionComparePulseParametersSet(pAxis->pollSocket_,pAxis->positionerName_,pAxis->pcoPulseWidthSet_,pAxis->encoderSettlingTimeSet_);
<    switch (-status) {
<      case 0:
<        strcpy(message, " ");  
<        break;
<      case 8:
<        strcpy(message, "Set: Check the position type\n Must not be a secondary");
<        break;     
<      case 17:
<        strcpy(message, "Set: Check parameter values\n PulseWidth and EncodrSettlingTime");
<        break;     
<      case 21:
<        strcpy(message, "Set: XPS initialization in progress");     
<        break;
<      case 22:
<        strcpy(message, "Set: Check PCO state\n Must be disabled ");
<        break;
<      case 24:
<        strcpy(message, "Set: Check the position encoder\n 'AquadB' or 'AnalogInterpolated'");
<        break;     
<      case 115:
<        strcpy(message, "Set: Check if the CIE board supports this function");
<        break;     
<      default: 
<        sprintf(message, "Set: Unknown PositionerPositionComparePulseParametersSet error=%d", status);
<        break;
<    }
<       if (status) goto done;
<       
<    status =PositionerPositionCompareSet(pAxis->pollSocket_,pAxis->positionerName_,pAxis->pcoMinPosSet_,pAxis->pcoMaxPosSet_,pAxis->pcoStepSet_);
<    if (status) pcoSetOK = false;
< 
<    switch (-status) {
<      case 0:
<        strcpy(message, " ");  
<        break;
<      case 8:
<        strcpy(message, "Set: Check the position encoder\n 'AquadB' or 'AnalogInterpolated'");
<        break;     
<      case 17:
<        if(pAxis->pcoMinPosSet_>=pAxis->pcoMaxPosSet_){
<          strcpy(message, "Set: Check parameter values\n MinPos < MaxPos ?");
<        }
<        if(pAxis->pcoStepSet_<0){
<          strcpy(message, "Set: Check parameter values\n Step > 0 ?");
<        }
<        if((pAxis->pcoMinPosSet_<pAxis->pcoMaxPosSet_)&&(pAxis->pcoStepSet_>(pAxis->pcoMaxPosSet_-pAxis->pcoMinPosSet_))){
<          strcpy(message, "Set: Check parameter values\n Step size is too large");
<        }      
<        break;
<      case 21:
<        strcpy(message, "Set: XPS initialization in progress");     
<        break;
<      case 22:
<        strcpy(message, "Set: Check PCO state\n Must be disabled ");
<        break;
<      case 24:
<        strcpy(message, "Set: No position encoder");
<        break;     
<      default: 
<        sprintf(message, "Set: Unknown PositionerPositionCompareSet error=%d", status);
<        break;
<    }
< 
<    done:
<    pAxes_[axis]->setStringParam(pcoMessage_, message);
<    pAxes_[axis]->setStringParam(pcoSetMessage_, message);
< 
<    strcpy(message, " ");  
<    pAxes_[axis]->setStringParam(pcoEnableMessage_, message);
<    pAxes_[axis]->setStringParam(pcoDisableMessage_, message);
< 
<    setStatus = (pcoSetOK) ?  PCO_STATUS_SUCCESS : PCO_STATUS_FAILURE;
<   pAxes_[axis]->setIntegerParam(pcoSetStatus_, setStatus);
<   pAxes_[axis]->setIntegerParam(pcoEnableStatus_, 0);
<   pAxes_[axis]->setIntegerParam(pcoDisableStatus_, 0);
<   pAxes_[axis]->setIntegerParam(pcoSet_, 0);
<    callParamCallbacks();
<    return status ? asynError : asynSuccess; 
< }
< /* Function to enable PCO */
< asynStatus XPSController::enablePco(size_t axis)
< {   
<    int status;
<    bool pcoEnableOK=true;
<    char message[MAX_MESSAGE_LEN];
<    int enableStatus;
<    static const char *functionName = "enablePco";
<    XPSAxis *pAxis;
<    pAxis = getAxis(axis);
< 
<    asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW,
<              "%s:%s: entry\n",
<              driverName, functionName);
<   
<    status = PositionerPositionCompareEnable(pAxis->pollSocket_,pAxis->positionerName_);
<    if (status) pcoEnableOK = false;
< 
<    switch (-status) {
<      case 0:
<        strcpy(message, " ");
<        pAxes_[axis]->setIntegerParam(pcoEnableDisableState_, 1);
<        break;
<      case 8:
<        strcpy(message, "Enable: Check the position encoder\n 'AquadB' or 'AnalogInterpolated'");
<        break;
<      case 21:
<        strcpy(message, "Enable: XPS initialization in progress");     
<        break;
<      case 22:
<        strcpy(message, "Enable: Positioner is not ready");
<        break;
<      default: 
<        sprintf(message, "Enable: Unknown PositionerPositionCompareEnable error=%d", status);
<        break;
<    }     
<   
<    pAxes_[axis]->setStringParam(pcoMessage_, message);
<    pAxes_[axis]->setStringParam(pcoEnableMessage_, message);
< 
<    strcpy(message, " ");  
<    pAxes_[axis]->setStringParam(pcoSetMessage_, message);
<    pAxes_[axis]->setStringParam(pcoDisableMessage_, message);
< 
<    enableStatus = (pcoEnableOK) ?  PCO_STATUS_SUCCESS : PCO_STATUS_FAILURE;
<   pAxes_[axis]->setIntegerParam(pcoEnableStatus_, enableStatus);
<   pAxes_[axis]->setIntegerParam(pcoDisableStatus_, 0);
<   pAxes_[axis]->setIntegerParam(pcoSetStatus_, 0);
<   pAxes_[axis]->setIntegerParam(pcoEnable_, 0);
<    callParamCallbacks();
<    return status ? asynError : asynSuccess; 
< }
< /* Function to disable PCO */
< asynStatus XPSController::disablePco(size_t axis)
< {   
<    int status;
<    bool pcoDisableOK=true;
<    char message[MAX_MESSAGE_LEN];
<    int disableStatus;
<    static const char *functionName = "disablePco";
<    XPSAxis *pAxis;
<    pAxis = getAxis(axis);
< 
<    asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW,
<              "%s:%s: entry\n",
<              driverName, functionName);
<    
<    status = PositionerPositionCompareDisable(pAxis->pollSocket_,pAxis->positionerName_);
<    if (status) pcoDisableOK = false; 
< 
<    switch (-status) {
<      case 0:
<        strcpy(message, " ");
<        pAxes_[axis]->setIntegerParam(pcoEnableDisableState_, 0);    
<        break;
<      case 8:
<        strcpy(message, "Disable: Check the position encoder\n 'AquadB' or 'AnalogInterpolated'");
<        break;
<      case 21:
<        strcpy(message, "Disable: XPS initialization in progress");     
<        break;
<      default: 
<        sprintf(message, "Disable: Unknown PositionerPositionCompareDisable error=%d", status);
<        break;
<    }     
<    
<    pAxes_[axis]->setStringParam(pcoMessage_, message);
<    pAxes_[axis]->setStringParam(pcoDisableMessage_, message);
< 
<    strcpy(message, " ");  
<    pAxes_[axis]->setStringParam(pcoSetMessage_, message);
<    pAxes_[axis]->setStringParam(pcoEnableMessage_, message);
< 
<    pAxes_[axis]->setStringParam(pcoDisableMessage_, message);
<    disableStatus = (pcoDisableOK) ?  PCO_STATUS_SUCCESS : PCO_STATUS_FAILURE;
<   pAxes_[axis]->setIntegerParam(pcoDisableStatus_, disableStatus);
<   pAxes_[axis]->setIntegerParam(pcoEnableStatus_, 0);
<   pAxes_[axis]->setIntegerParam(pcoSetStatus_, 0);
<   pAxes_[axis]->setIntegerParam(pcoDisable_, 0);
<    callParamCallbacks();
<    return status ? asynError : asynSuccess; 
< }

4. Add several lines to the $EPICS_SUPPORT/motorR6-7/motorApp/NewportSrc/XPSAxis.h file:

[kakoyan@halld-sc NewportSrc]$ diff XPSAxis.h XPSAxis_original.h
86,90c86
<   double pcoMinPosSet_;
<   double pcoMaxPosSet_;
<   double pcoStepSet_;
<   double pcoPulseWidthSet_;
<   double encoderSettlingTimeSet_;

5. Add several lines to the $EPICS_SUPPORT/motorR6-7/motorApp/NewportSrc/XPSAxis.cpp file:

[kakoyan@halld-sc NewportSrc]$ diff XPSAxis.cpp XPSAxis_original.cpp
147,154d146
<   /* Clean PCO message boxes */
<   char message[10];
<   strcpy(message, " ");
<   setStringParam(pC_->pcoSetMessage_, message);
<   setStringParam(pC_->pcoEnableMessage_, message);
<   setStringParam(pC_->pcoDisableMessage_, message);
<   setStringParam(pC_->pcoMessage_, message);
< 
311d302
<     if(pTempAxis==0)  continue;    	
584,586d574
<  /* run PositionerPositionCompareGet() function*/
<   pC_->getPco(axisNo_);
< 


6. Add several lines to the $EPICS_SUPPORT/motorR6-7/motorApp/MotorSrc/asynMotorAxis.h file:

[kakoyan@halld-sc MotorSrc]$ diff asynMotorAxis.h asynMotorAxis_original.h
27d26
<   virtual asynStatus setStringParam(int index, const char *value);


7. Add several lines to the $EPICS_SUPPORT/motorR6-7/motorApp/MotorSrc/asynMotorAxis.cpp file:

[kakoyan@halld-sc MotorSrc]$ diff asynMotorAxis.cpp asynMotorAxis_original.cpp
195,198d194
< asynStatus asynMotorAxis::setStringParam(int function, const char *value)
< {
<      return pC_->setStringParam(axisNo_, function, value);
< }

7. Add several line to the $EPICS/app/xpsMotorApp/Db/Makefile

DB += xpsPCO.substitutions

Additional files

1. motorApp/Db/XPS_PCO.template
2. iocBoot/iocWithAsyn/xpsPCO.substitutions
3. motorApp/op/opi/PCO1.opi
4. motorApp/op/opi/PCO2.opi

1. XPS_PCO.templat

Put XPS_PCO.template file(db file for the PCO) to the folder $EPICS_SUPPORT/motorR6-7/motorApp/Db. The values for the parameters ($(P),$(R),...) are token from xpsPCO.substitution file during 'dbLoadTemplate "db/xpsPCO.substitutions"' command line of xpsPCO.cmd file.


The XPS_PCO.templat file:

# Database for Distance Spaced Pulses (PCO-Position Compare Output) for XPS  with asynMotor
#
#    Vanik Kakoyan
#    July 02, 2012
#
#
# Macro paramters:
#   $(P)        - PV name prefix
#   $(R)        - PV base record name
#   $(PORT)     - asyn port for this controller
#   $(MINPOS)   - Minimum position for pulses
#   $(MAXPOS)   - Maximum position for pulses
#   $(POSSTEP)  - Position step for pulses
#   $(TIMEOUT)  - asyn timeout
#   $(PREC)      - Precision for this axis
#
#------------------------------------------------
# PVs controlling the min, max positions and step of output pulses
record(ao,"$(P)$(R)PCOMinPosition") {
   field(DESC, "min position for pulses")
   field(PINI, "YES")
   field(DTYP, "asynFloat64")
   field(OUT,  "@asyn($(PORT),$(ADDR),$(TIMEOUT))PCO_MIN_POSITION")
   field(VAL,  "$(MINPOS)")
   field(PREC, "3")
}
record(ao,"$(P)$(R)PCOMaxPosition") {
   field(DESC, "max position for pulses")
   field(PINI, "YES")
   field(DTYP, "asynFloat64")
   field(OUT,  "@asyn($(PORT),$(ADDR),$(TIMEOUT))PCO_MAX_POSITION")
   field(VAL,  "$(MAXPOS)")
   field(PREC, "3")
}
record(ao,"$(P)$(R)PCOPositionStep") {
   field(DESC, "position step for pulses")
   field(PINI, "YES")
   field(DTYP, "asynFloat64")
   field(OUT,  "@asyn($(PORT),$(ADDR),$(TIMEOUT))PCO_POSITION_STEP")
   field(VAL,  "$(POSSTEP)")
   field(PREC, "3")
}
#
# PVs for the Actual (seted) min, max positions and step of output pulses
#
record(ai,"$(P)$(R)PCOMinPositionActual") {
   field(DESC, "Actual min position for pulses")
   field(DTYP, "asynFloat64")
   field(INP,  "@asyn($(PORT),$(ADDR),$(TIMEOUT))PCO_MIN_POSITION_ACTUAL")
   field(PREC, "$(PREC)")
   field(VAL,  "0")
   field(SCAN, "I/O Intr")
}
record(ai,"$(P)$(R)PCOMaxPositionActual") {
   field(DESC, "Actual max position for pulses")
   field(DTYP, "asynFloat64")
   field(INP,  "@asyn($(PORT),$(ADDR),$(TIMEOUT))PCO_MAX_POSITION_ACTUAL")
   field(PREC, "$(PREC)")
   field(VAL,  "0")
   field(SCAN, "I/O Intr")
}
record(ai,"$(P)$(R)PCOPositionStepActual") {
   field(DESC, "Actual step for pulses")
   field(DTYP, "asynFloat64")
   field(INP,  "@asyn($(PORT),$(ADDR),$(TIMEOUT))PCO_POSITION_STEP_ACTUAL")
   field(PREC, "$(PREC)")
   field(VAL,  "0")
   field(SCAN, "I/O Intr")
}
#
# PVs for the  PCO PulseWidth and EncoderSettlingTime 
#
record(mbbo,"$(P)$(R)PCOPulseWidth") {
   field(DESC, "PCO pulse width")
   field(PINI, "YES")
   field(DTYP, "asynInt32")
   field(OUT,  "@asyn($(PORT),$(ADDR),$(TIMEOUT))PCO_PULSE_WIDTH")
   field(ZRVL, "0")
   field(ZRST, "0.2 usec")
   field(ONVL, "1")
   field(ONST, "1 usec")
   field(TWVL, "2")
   field(TWST, "2.5 usec")
   field(THVL, "3")
   field(THST, "10 usec")
}
record(mbbo,"$(P)$(R)EncoderSettlingTime") {
   field(DESC, "Encoder Settling Time")
   field(PINI, "YES")
   field(DTYP, "asynInt32")
   field(OUT,  "@asyn($(PORT),$(ADDR),$(TIMEOUT))ENCODER_SETTLING_TIME")
   field(ZRVL, "0")
   field(ZRST, "0.075 usec")
   field(ONVL, "1")
   field(ONST, "1 usec")
   field(TWVL, "2")
   field(TWST, "4 usec")
   field(THVL, "3")
   field(THST, "12 usec")
}
#
# PVs for the Actual (seted)  PCOPulseWidth and EncoderSettlingTime 
#
record(ai,"$(P)$(R)PCOPulseWidthActual") {
   field(DESC, "Actual PCO pulse width")
   field(DTYP, "asynFloat64")
   field(INP,  "@asyn($(PORT),$(ADDR),$(TIMEOUT))PCO_PULSE_WIDTH_ACTUAL")
   field(PREC, "$(PREC)")
   field(SCAN, "I/O Intr")
}
record(ai,"$(P)$(R)EncoderSettlingTimeActual") {
   field(DESC, "Actual Encoder Settling Time")
   field(DTYP, "asynFloat64")
   field(INP,  "@asyn($(PORT),$(ADDR),$(TIMEOUT))ENCODER_SETTLING_TIME_ACTUAL")
   field(PREC, "$(PREC)")
   field(SCAN, "I/O Intr")
}
#
# PCO Enable/Disable State
#
record(mbbi,"$(P)$(R)PCOEnableDisableState") {
   field(DESC, "Position Compare State")
   field(DTYP, "asynInt32")
   field(INP,  "@asyn($(PORT),$(ADDR),$(TIMEOUT))PCO_ENABLE_DISABLE_STATE")
   field(ZRVL, "0")
   field(ZRST, "Disable")
   field(ONVL, "1")
   field(ONST, "Enable")
   field(TWVL, "2")
   field(TWST, "Undefined")
   field(VAL,  "2")
   field(SCAN, "I/O Intr")
}
#
# Common Message 
#
record(waveform,"$(P)$(R)PCOMessage") {
   field(DESC, "PCO message")
   field(DTYP, "asynOctetRead")
   field(INP,  "@asyn($(PORT),$(ADDR),$(TIMEOUT))PCO_MESSAGE")
   field(FTVL, "CHAR")
   field(NELM, "256")
   field(SCAN, "I/O Intr")
}
#
# Positioner name 
#
record(stringin, "$(P)$(R)PositionerName") {
   field(DESC, "XPS PCO positioner name")
   field(DTYP, "asynOctetRead")
   field(INP,  "@asyn($(PORT),$(ADDR),$(TIMEOUT))PCO_POSITIONER_NAME")
   field(SCAN, "I/O Intr")
}
#
# PVs for PositionerPositionCompareSet() command to Set the PCO
#
record(busy,"$(P)$(R)PCOSet") {
   field(DESC,"Set:PositionerPositionCompareSet")
   field(DTYP, "asynInt32")
   field(OUT,  "@asyn($(PORT),$(ADDR),$(TIMEOUT))PCO_SET")
    field(ZNAM, "Done")
    field(ONAM, "Enable")
}
record(mbbi,"$(P)$(R)PCOSetStatus") {
   field(DESC,"PCO Set status")
   field(DTYP, "asynInt32")
   field(INP,  "@asyn($(PORT),$(ADDR),$(TIMEOUT))PCO_SET_STATUS")
   field(ZRVL, "0")
   field(ZRST, "Undefined")
   field(ZRSV, "INVALID")
   field(ONVL, "1")
   field(ONST, "Success")
   field(ONSV, "NO_ALARM")
   field(TWVL, "2")
   field(TWST, "Failure")
   field(TWSV, "MAJOR")
   field(SCAN, "I/O Intr")
}
record(waveform,"$(P)$(R)PCOSetMessage") {
   field(DESC, "PCO Set command message")
   field(DTYP, "asynOctetRead")
   field(INP,  "@asyn($(PORT),$(ADDR),$(TIMEOUT))PCO_SET_MESSAGE")
   field(FTVL, "CHAR")
   field(NELM, "256")
   field(SCAN, "I/O Intr")
}
record(waveform,"$(P)$(R)PCOGetMessage") {
   field(DESC, "PCO Get command message")
   field(DTYP, "asynOctetRead")
   field(INP,  "@asyn($(PORT),$(ADDR),$(TIMEOUT))PCO_GET_MESSAGE")
   field(FTVL, "CHAR")
   field(NELM, "256")
   field(SCAN, "I/O Intr")
}
#
# PVs for PositionerPositionCompareEnable() command to Enable the PCO
#
record(busy,"$(P)$(R)PCOEnable") {
   field(DESC,"Enable:PositionerPositionCompareEnable")
   field(DTYP, "asynInt32")
   field(OUT,  "@asyn($(PORT),$(ADDR),$(TIMEOUT))PCO_ENABLE")
   field(ZNAM, "Done")
   field(ONAM, "Enable")
}
record(mbbi,"$(P)$(R)PCOEnableStatus") {
   field(DESC,"PCO Enable status")
   field(DTYP, "asynInt32")
   field(INP,  "@asyn($(PORT),$(ADDR),$(TIMEOUT))PCO_ENABLE_STATUS")
   field(ZRVL, "0")
   field(ZRST, "Undefined")
   field(ZRSV, "INVALID")
   field(ONVL, "1")
   field(ONST, "Success")
   field(ONSV, "NO_ALARM")
   field(TWVL, "2")
   field(TWST, "Failure")
   field(TWSV, "MAJOR")
   field(SCAN, "I/O Intr")
}
record(waveform,"$(P)$(R)PCOEnableMessage") {
   field(DESC, "PCO Enable command message")
   field(DTYP, "asynOctetRead")
   field(INP,  "@asyn($(PORT),$(ADDR),$(TIMEOUT))PCO_ENABLE_MESSAGE")
   field(FTVL, "CHAR")
   field(NELM, "256")
   field(SCAN, "I/O Intr")
}
#
# PVs for PositionerPositionCompareDisable() command to Disable  the PCO
#
record(busy,"$(P)$(R)PCODisable") {
   field(DESC,"Disable:PositionerPositionCompareDisable")
   field(DTYP, "asynInt32")
   field(OUT,  "@asyn($(PORT),$(ADDR),$(TIMEOUT))PCO_DISABLE")
   field(ZNAM, "Done")
   field(ONAM, "Enable")
}
record(mbbi,"$(P)$(R)PCODisableStatus") {
   field(DESC,"PCO Disable status")
   field(DTYP, "asynInt32")
   field(INP,  "@asyn($(PORT),$(ADDR),$(TIMEOUT))PCO_DISABLE_STATUS")
   field(ZRVL, "0")
   field(ZRST, "Undefined")
   field(ZRSV, "INVALID")
   field(ONVL, "1")
   field(ONST, "Success")
   field(ONSV, "NO_ALARM")
   field(TWVL, "2")
   field(TWST, "Failure")
   field(TWSV, "MAJOR")
   field(SCAN, "I/O Intr")
}
record(waveform,"$(P)$(R)PCODisableMessage") {
   field(DESC, "PCO Disable command message")
   field(DTYP, "asynOctetRead")
   field(INP,  "@asyn($(PORT),$(ADDR),$(TIMEOUT))PCO_DISABLE_MESSAGE")
   field(FTVL, "CHAR")
   field(NELM, "256")
   field(SCAN, "I/O Intr")
}


2. IOC command file for XPS-PCO

put xpsPCO.cmd file to the $EPICS/app/iocBoot/iocxpsMotor and $(EPICS_SUPPORT)/motorR6-7/iocBoot/iocWithAsyn

The xpsPCO.cmd.xps file:

#!../../bin/linux-x86/xpsMotor
< envPaths
cd ${TOP}
dbLoadDatabase("dbd/xpsMotor.dbd")
xpsMotor_registerRecordDeviceDriver(pdbbase)
### Motors
dbLoadTemplate "db/xpsPCO.substitutions"
#asSetFilename("ca_security.txt")
# asyn port, IP address, IP port, number of axes, 
# active poll period (ms), idle poll period (ms), 
# enable set position, set position settling time (ms)
XPSCreateController("XPS1", "129.57.37.26", 5001, 8, 10, 500, 0, 500)
#asynSetTraceIOMask("XPS1", 0, 2)
#asynSetTraceMask("XPS1", 0, 255)
# XPS asyn port,  axis, groupName.positionerName, stepSize
XPSCreateAxis("XPS1",0,"Mult1.P1",   "465.11627")
#
iocInit


3. put xpsPCO.substitutions file to the folder $EPICS/app/xpsMotorApp/Db/ and $(EPICS_SUPPORT)/motorR6-7/iocBoot/iocWithAsyn

The xpsPCO.substitutions file:

file "$(MOTOR_SUPPORT)/db/basic_asyn_motor.db"
{
pattern
{P,	     N,   M,	     DTYP,     PORT, ADDR,     DESC,	  EGU,    DIR,   VELO,  VBAS, ACCL, BDST, BVEL, BACC,  MRES,  PREC,    DHLM,  DLLM, INIT}
{halldxps1:, 1, "m$(N)", "asynMotor",  XPS1,  0,     "motor-S22",  mm,    Pos,    1.5,   0.,   4.,   0,    1,    .2,   0.00215,  3,    600,  -600, ""}
}
file "$(MOTOR_SUPPORT)/db/XPS_extra.db"
{
pattern
{P,	     R,  PORT,  ADDR}
{halldxps1:,  m1, XPS1,   0}
}
file "$(MOTOR_SUPPORT)/db/XPS_PCO.template"
{
pattern
{P,	      R,    PORT,  ADDR, MINPOS, MAXPOS, POSSTEP, PREC, TIMEOUT}
{halldxps1:,  m1,   XPS1,    0,   1,	 3,      0.1,     3,    10}
}


4 GUIs for the PCO two versions

PCO1.opi (common message box for all commands).
PCO2.opi (with individual state and message boxes for Set, Enable and Disable commands).

Motor based applications

Collimator table

Tagger dump harp