Motor A recipe for driver implementation using the C++ asyn motor API Jon Thompson For the 2013 EPICS for experiment control workshop, Diamond Light Source,

Slides:



Advertisements
Similar presentations
Device Drivers. Linux Device Drivers Linux supports three types of hardware device: character, block and network –character devices: R/W without buffering.
Advertisements

EPICS Collaboration Meeting, June 3, 2010 asynPortDriver C++ Base Class for asyn Port Drivers Mark Rivers University of Chicago Advanced Photon Source.
Delta Tau Coordinate Systems and PLCs at Diamond Matthew Pearson Controls Group, Diamond Light Source Overview: Delta Tau Geobrick motor controller Delta.
17th Oct 08 Matthew Pearson - Motion Control at DLS 1 Motion Control Developments at Diamond Light Source Matthew Pearson DLS Controls Group.
CS-341 Dick Steflik Introduction. C++ General purpose programming language A superset of C (except for minor details) provides new flexible ways for defining.
CS-341 Dick Steflik Introduction. C++ General purpose programming language A superset of C (except for minor details) provides new flexible ways for defining.
SLAC asyn class, Day 1, August 26, 2010 Example asyn driver Modbus Mark Rivers, Marty Kraimer, Eric Norum University of Chicago Advanced Photon Source.
EPICS Meeting, APS, June 14 th 2006 MCS-8 The 8 axes motion control system Rok Šabjan
Input/Output Controller (IOC) Overview Andrew Johnson Computer Scientist, AES Controls Group.
ORNL is managed by UT-Battelle for the US Department of Energy EPICS State Notation Language (SNL), “Sequencer” Kay Kasemir, SNS/ORNL Many slides from.
JavaIOC Marty Kraimer EPICS Collaboration Meeting ICALEPCS October
Managed by UT-Battelle for the Department of Energy Kay Kasemir ORNL/SNS Feb Motor Support.
Imperial College Tracker Slow Control & Monitoring.
Automatic Regression Test Facility for Support Modules Jon Thompson, Diamond Light Source Vancouver, 1 May 2009.
ACE Address Configuration Executive. Why ACE? ACE provides access to several address resolution protocols under a single API ACE is the only API available.
LiveCycle Data Services Introduction Part 2. Part 2? This is the second in our series on LiveCycle Data Services. If you missed our first presentation,
EPICS devSNMP Extensions Euan Troup, CSIRO Australia Telescope National Facility ASKAP Project Paul Wild Observatory.
Programming in Java Unit 2. Class and variable declaration A class is best thought of as a template from which objects are created. You can create many.
Marcel Grunder, EPICS Collaboration Meeting October Aerotech A3200 high precision rotation stage Windows based Epics IOC driver implementation.
The Java Programming Language
REVIEW On Friday we explored Client-Server Applications with Sockets. Servers must create a ServerSocket object on a specific Port #. They then can wait.
CS212: Object Oriented Analysis and Design Lecture 6: Friends, Constructor and destructors.
On-the-fly scanning: Improvements in the EPICS motor and mca modules Mark Rivers University of Chicago.
Dec 8-10, 2004EPICS Collaboration Meeting – Tokai, Japan MicroIOC: A Simple Robust Platform for Integrating Devices Mark Pleško
Spring 2008 Mark Fontenot CSE 1341 Principles of Computer Science I Note Set 2.
JavaIOC EPICS Meeting SSRF March Presented by: Marty Kraimer.
ORNL is managed by UT-Battelle for the US Department of Energy EPICS Automation Kay Kasemir, SNS/ORNL June 2014.
Session 08 Module 14: Generics and Iterator Module 15: Anonymous & partial class & Nullable type.
Area Detector Drivers Towards A Pattern Jon Thompson.
An introduction to areaDetector Tom Cobb. What is areaDetector?  C++ framework for detectors in EPICS  Drivers produces NDArrays of data  Plugins consume.
 Remote Method Invocation  A true distributed computing application interface for Java, written to provide easy access to objects existing on remote.
Integrating EPICS and LabVIEW on Windows using DCOM Freddie Akeroyd ISIS Computing Group.
Chapter 6 Introduction to Defining Classes. Objectives: Design and implement a simple class from user requirements. Organize a program in terms of a view.
CPSC 252 The Big Three Page 1 The “Big Three” Every class that has data members pointing to dynamically allocated memory must implement these three methods:
EPICS Application Development At The Canadian Light Source Glen Wright.
Fast Fault Finder A Machine Protection Component.
EPICS Release 3.15 Bob Dalesio May 19, Features for 3.15 Support for large arrays - done for rsrv in 3.14 Channel access priorities - planned to.
1 1999/Ph 514: Flow of Control EPICS Flow of Control Marty Kraimer APS.
GPRS functionality overview in Horner OCS. GPRS functionality – Peer to Peer communication over GPRS – CSCAPE connectivity over GPRS – Data exchange using.
1. LabVIEW and EPICS Workshop EPICS Collaboration Meeting Fall 2011.
Motor drivers for asyn motor device support Mark Rivers GeoSoilEnviroCARS, Advanced Photon Source University of Chicago.
 In the java programming language, a keyword is one of 50 reserved words which have a predefined meaning in the language; because of this,
An EPICS solution for Galil products that can provide a comprehensive, and high performance motor and PLC control system for use at synchrotrons and other.
Linux Operations and Administration
Classes, Interfaces and Packages
Quick Review of OOP Constructs Classes:  Data types for structured data and behavior  fields and methods Objects:  Variables whose data type is a class.
1 1999/Ph 514: Supported Hardware EPICS Supported Hardware Ned D. Arnold APS.
72 4/11/98 CSE 143 Abstract Data Types [Sections , ]
1 EPICS Flow of Control: EPICS Workshop at IHEP, Beijing, August 2001 EPICS Flow of Control Marty Kraimer APS.
Motion Control at the Advanced Photon Source Argonne National Laboratory Mark Rivers University of Chicago 7 GeV synchrotron x-ray light source, the largest.
LonWorks Introduction Hwayoung Chae.
16th Oct 08 Tom Cobb - Python at DLS 1 Uses of Python at Diamond Light Source.
Software Troubleshooting Eliminate possible programming and timing errors by stopping all programs ( and ). Use the Executive program terminal and send.
Monitoring Dynamic IOC Installations Using the alive Record Dohn Arms Beamline Controls & Data Acquisition Group Advanced Photon Source.
11 DEPLOYING AN UPDATE MANAGEMENT INFRASTRUCTURE Chapter 6.
AsynMotor: Support For Coordinated Multi-Axis Motion In EPICS Mark Rivers University of Chicago.
JavaIOC Overview and Update
Chapter Objectives In this chapter, you will learn:
Diamond Motion Control Emma Shepherd & Matthew Pearson May 2011
C++ Base Class for asyn Port Drivers
Mark Rivers University of Chicago
C++ Base Class for asyn Port Drivers
Advanced Photon Source
asynMotor: Support For Coordinated Multi-Axis Motion In EPICS
Motion Control (at APS)
Object Oriented Analysis and Design
C++ Base Class for asyn Port Drivers
C++ Base Class for asyn Port Drivers
EPICS Integration of PLC based systems
Chapter 13: I/O Systems.
Presentation transcript:

Motor A recipe for driver implementation using the C++ asyn motor API Jon Thompson For the 2013 EPICS for experiment control workshop, Diamond Light Source, April 2013.

Introduction Standard EPICS interface between device support and drivers is only loosely defined The motor record did define an API for device support but was hard to extend. Using asyn for motor support provides extensibility and all the standard I/O. The C++ API builds on the asynPortDriver API.

The C++ API The asynMotorController base class represents the controller. – uses ‘parameters’ to communicate motor commands. – Define further ‘parameters’ to extend. The asynMotorAxis base class represents a single motor. – Override the motor control functions to send commands to the hardware. Connect to a port driver to communicate with the device.

An Example Smaract piezo motor modular control system. The protocol consists of messages with the following syntax: ‘:’ [ ‘,’ ] ‘LF’ SCLS, -> E-1,0Set the axis velocity MPA,, -> E, Move to absolute position GP -> P, Get current position GS -> S, Get status FRM,,, -> E, Find reference mark (home) S -> E, Stop a channel’s motion SP, -> E, Set the position CS -> E, Calibrate the sensor GIV -> IV,, Get version information

An Example Derive from asynMotorController – Implement poll – Override writeInt32 to provide extensions Derive from asynMotorAxis. – Implement motor control functions.

Code Required smaractsmaractAppsrc mcsController.h mcsController.cppmcsAxis.hmcsAxis.cpp smaract.dbd Makefile Db mcsController.template mcsAxis.template Makefile configure RELEASE Dependency definition in the ‘RELEASE’ file. C++ code in the ‘src’ directory. Database template code in the ‘Db’ directory.

Controller Class Declaration class mcsController : public asynMotorController { friend class mcsAxis; private: // Constants enum {RXBUFFERSIZE=200, TXBUFFERSIZE=200, RESBUFFERSIZE=30, CONNECTIONPOLL=10}; public: mcsController(const char *portName, int controllerNum, const char* serialPortName, int serialPortAddress, int numAxes, double movingPollPeriod, double idlePollPeriod); virtual ~mcsController(); // Overridden from asynMotorController virtual asynStatus poll(); virtual asynStatus writeInt32(asynUser *pasynUser, epicsInt32 value); class mcsController : public asynMotorController { friend class mcsAxis; private: // Constants enum {RXBUFFERSIZE=200, TXBUFFERSIZE=200, RESBUFFERSIZE=30, CONNECTIONPOLL=10}; public: mcsController(const char *portName, int controllerNum, const char* serialPortName, int serialPortAddress, int numAxes, double movingPollPeriod, double idlePollPeriod); virtual ~mcsController(); // Overridden from asynMotorController virtual asynStatus poll(); virtual asynStatus writeInt32(asynUser *pasynUser, epicsInt32 value); Constants. Constructor and overrides. Notice similarity with asynPortDriver.

Controller Class Declaration private: /* Methods for use by the axes */ bool sendReceive(const char* tx, const char* txTerminator, char* rx, size_t rxSize, const char* rxTerminator); bool command(const char* cmd, const char* rsp, int* a, int* b, int*c); bool command(const char* cmd, int p, int q, const char* rsp, int* a, int* b, int*c); bool command(const char* cmd, const char* rsp, int* a); bool command(const char* cmd, int p, const char* rsp, int* a); bool command(const char* cmd, int p, int q, const char* rsp, int* a); bool command(const char* cmd, int p, const char* rsp, int* a, int* b); bool command(const char* cmd, int p, const char* rsp, int* a, int* b, int* c); bool command(const char* cmd, int p, int q, const char* rsp, int* a, int* b); bool command(const char* cmd, int p, int q, int r, const char* rsp, int* a, int* b); bool command(const char* cmd, int p, int q, int r, int s, const char* rsp, int* a, int* b); private: /* Methods for use by the axes */ bool sendReceive(const char* tx, const char* txTerminator, char* rx, size_t rxSize, const char* rxTerminator); bool command(const char* cmd, const char* rsp, int* a, int* b, int*c); bool command(const char* cmd, int p, int q, const char* rsp, int* a, int* b, int*c); bool command(const char* cmd, const char* rsp, int* a); bool command(const char* cmd, int p, const char* rsp, int* a); bool command(const char* cmd, int p, int q, const char* rsp, int* a); bool command(const char* cmd, int p, const char* rsp, int* a, int* b); bool command(const char* cmd, int p, const char* rsp, int* a, int* b, int* c); bool command(const char* cmd, int p, int q, const char* rsp, int* a, int* b); bool command(const char* cmd, int p, int q, int r, const char* rsp, int* a, int* b); bool command(const char* cmd, int p, int q, int r, int s, const char* rsp, int* a, int* b); Protocol transceiver helper functions

Controller Class Declaration protected: int FIRST_PARAM; int indexVersionHigh; int indexVersionLow; int indexVersionBuild; int indexConnected; int indexActiveHold; int indexCalibrateSensor; int LAST_PARAM; private: /* Data */ asynUser* serialPortUser; int controllerNum; int connectionPollRequired; }; protected: int FIRST_PARAM; int indexVersionHigh; int indexVersionLow; int indexVersionBuild; int indexConnected; int indexActiveHold; int indexCalibrateSensor; int LAST_PARAM; private: /* Data */ asynUser* serialPortUser; int controllerNum; int connectionPollRequired; }; Parameter indices. Private member data.

Axis Class Declaration Override standard motor control functions Functions that implement extensions. class mcsAxis : public asynMotorAxis { public: mcsAxis(mcsController* ctlr, int axisNum, bool rotary); virtual ~mcsAxis(); // Overridden from asynMotorAxis virtual asynStatus move(double position, int relative, double minVelocity, double maxVelocity, double acceleration); virtual asynStatus moveVelocity(double minVelocity, double maxVelocity, double acceleration); virtual asynStatus home(double minVelocity, double maxVelocity, double acceleration, int forwards); virtual asynStatus stop(double acceleration); virtual asynStatus setPosition(double position); virtual asynStatus poll(bool *moving); // Used by mcsController void calibrateSensor(int yes); private: /* Data */ mcsController* controller; int axisNum; int curPosition; }; class mcsAxis : public asynMotorAxis { public: mcsAxis(mcsController* ctlr, int axisNum, bool rotary); virtual ~mcsAxis(); // Overridden from asynMotorAxis virtual asynStatus move(double position, int relative, double minVelocity, double maxVelocity, double acceleration); virtual asynStatus moveVelocity(double minVelocity, double maxVelocity, double acceleration); virtual asynStatus home(double minVelocity, double maxVelocity, double acceleration, int forwards); virtual asynStatus stop(double acceleration); virtual asynStatus setPosition(double position); virtual asynStatus poll(bool *moving); // Used by mcsController void calibrateSensor(int yes); private: /* Data */ mcsController* controller; int axisNum; int curPosition; };

Asyn Parameter Names #define indexVersionHighString "VERSION_HIGH" #define indexVersionLowString "VERSION_LOW" #define indexVersionBuildString "VERSION_BUILD" #define indexConnectedString "CONNECTED“ #define indexCalibrateSensorString "CALIBRATE_SENSOR“ #define NUM_PARAMS(&LAST_PARAM - &FIRST_PARAM - 1) #define indexVersionHighString "VERSION_HIGH" #define indexVersionLowString "VERSION_LOW" #define indexVersionBuildString "VERSION_BUILD" #define indexConnectedString "CONNECTED“ #define indexCalibrateSensorString "CALIBRATE_SENSOR“ #define NUM_PARAMS(&LAST_PARAM - &FIRST_PARAM - 1) Define the extension parameter names. Just the same as asynPortDriver example.

Controller Class Constructor mcsController::mcsController(const char* portName, int controllerNum, const char* serialPortName, int serialPortAddress, int numAxes, double movingPollPeriod, double idlePollPeriod) : asynMotorController(portName, numAxes, NUM_PARAMS, /*interfaceMask=*/0, /*interruptMask=*/0, /*asynFlags=*/ASYN_MULTIDEVICE | ASYN_CANBLOCK, /*autoConnect=*/1, /*priority=*/0, /*stackSize=*/0), controllerNum(controllerNum), connectionPollRequired(0) mcsController::mcsController(const char* portName, int controllerNum, const char* serialPortName, int serialPortAddress, int numAxes, double movingPollPeriod, double idlePollPeriod) : asynMotorController(portName, numAxes, NUM_PARAMS, /*interfaceMask=*/0, /*interruptMask=*/0, /*asynFlags=*/ASYN_MULTIDEVICE | ASYN_CANBLOCK, /*autoConnect=*/1, /*priority=*/0, /*stackSize=*/0), controllerNum(controllerNum), connectionPollRequired(0) Pass in the name and address of the serial asyn port. Parameters to the base class constructor The number of asyn parameters we are going to define Define which asyn interfaces we are using. Note that the base class ORs in all the necessary bits. Various flags Initialise members

Controller Class Constructor createParam(indexVersionHighString, asynParamInt32, &indexVersionHigh); createParam(indexVersionLowString, asynParamInt32, &indexVersionLow); createParam(indexVersionBuildString, asynParamInt32, &indexVersionBuild); createParam(indexConnectedString, asynParamInt32, &indexConnected); createParam(indexCalibrateSensorString, asynParamInt32, &indexCalibrateSensor); setIntegerParam(indexConnected, 0); setIntegerParam(indexCalibrateSensor, 0); setIntegerParam(indexVersionHigh, 0); setIntegerParam(indexVersionLow, 0); setIntegerParam(indexVersionBuild, 0); createParam(indexVersionHighString, asynParamInt32, &indexVersionHigh); createParam(indexVersionLowString, asynParamInt32, &indexVersionLow); createParam(indexVersionBuildString, asynParamInt32, &indexVersionBuild); createParam(indexConnectedString, asynParamInt32, &indexConnected); createParam(indexCalibrateSensorString, asynParamInt32, &indexCalibrateSensor); setIntegerParam(indexConnected, 0); setIntegerParam(indexCalibrateSensor, 0); setIntegerParam(indexVersionHigh, 0); setIntegerParam(indexVersionLow, 0); setIntegerParam(indexVersionBuild, 0); Create the extra asyn parameters Usually one per PV. Binds the name to the parameter. Returns an integer handle to the parameter. Initialise parameters if necessary.

Controller Class Constructor // Connect to the serial port if(pasynOctetSyncIO->connect(serialPortName, serialPortAddress, &serialPortUser, NULL) != asynSuccess) { printf("mcsController: Failed to connect to serial port %s\n", serialPortName); } // Create the poller thread startPoller(movingPollPeriod, idlePollPeriod, /*forcedFastPolls-*/10); } // Connect to the serial port if(pasynOctetSyncIO->connect(serialPortName, serialPortAddress, &serialPortUser, NULL) != asynSuccess) { printf("mcsController: Failed to connect to serial port %s\n", serialPortName); } // Create the poller thread startPoller(movingPollPeriod, idlePollPeriod, /*forcedFastPolls-*/10); } Connect up to the asyn serial port. Start the polling thread (but we don’t have to create it this time).

Polling Controller State asynStatus mcsController::poll() { bool ok = true; int verHigh, verLow, verBuild; ok = command("GIV", "IV", &verHigh, &verLow, &verBuild); if(ok) { setIntegerParam(indexVersionHigh, verHigh); setIntegerParam(indexVersionLow, verLow); setIntegerParam(indexVersionBuild, verBuild); setIntegerParam(indexConnected, 1); } else { setIntegerParam(indexConnected, 0); } callParamCallbacks(); } asynStatus mcsController::poll() { bool ok = true; int verHigh, verLow, verBuild; ok = command("GIV", "IV", &verHigh, &verLow, &verBuild); if(ok) { setIntegerParam(indexVersionHigh, verHigh); setIntegerParam(indexVersionLow, verLow); setIntegerParam(indexVersionBuild, verBuild); setIntegerParam(indexConnected, 1); } else { setIntegerParam(indexConnected, 0); } callParamCallbacks(); } This function only needs to poll controller wide information. Called with the lock already taken. Useful to determine controller connection state. Note the transmit receive function ‘command’.

Handling PV Writes asynStatus mcsController::writeInt32(asynUser *pasynUser, epicsInt32 value) { int a; int b; int function = pasynUser->reason; mcsAxis* axis = dynamic_cast (getAxis(pasynUser)); if(axis != NULL && function == indexCalibrateSensor) { // Calibrate the sensor of the specified axis axis->calibrateSensor(value); } return asynMotorController::writeInt32(pasynUser, value); } asynStatus mcsController::writeInt32(asynUser *pasynUser, epicsInt32 value) { int a; int b; int function = pasynUser->reason; mcsAxis* axis = dynamic_cast (getAxis(pasynUser)); if(axis != NULL && function == indexCalibrateSensor) { // Calibrate the sensor of the specified axis axis->calibrateSensor(value); } return asynMotorController::writeInt32(pasynUser, value); } Base class writes parameter and does call backs. We just send information to the axis.

Communicating With the Device Same principles as the asynPortDriver example. In this example, a set of ‘command’ functions provide easy access to the smaract command set. Exact details will vary depending on the device.

The Axis Class Just a matter of sending the correct commands for each of the motor control functions. The poll function should get the current state of the axis; position, limits, etc.

Explaining to EPICS static const iocshArg mcsControllerConfigArg0 = {"port name", iocshArgString}; static const iocshArg mcsControllerConfigArg1 = {"controller number", iocshArgInt}; static const iocshArg mcsControllerConfigArg2 = {"serial port name", iocshArgString}; static const iocshArg mcsControllerConfigArg3 = {"serial port address", iocshArgInt}; static const iocshArg mcsControllerConfigArg4 = {"number of axes", iocshArgInt}; static const iocshArg mcsControllerConfigArg5 = {"moving poll period (ms)", iocshArgInt}; static const iocshArg mcsControllerConfigArg6 = {"idle poll period (ms)", iocshArgInt}; static const iocshArg *const mcsControllerConfigArgs[] = { &mcsControllerConfigArg0, &mcsControllerConfigArg1, &mcsControllerConfigArg2, &mcsControllerConfigArg3, &mcsControllerConfigArg4, &mcsControllerConfigArg5, &mcsControllerConfigArg6 }; static const iocshFuncDef mcsControllerConfigDef = {"mcsControllerConfig", 7, mcsControllerConfigArgs}; static void mcsControllerConfigCallFunc(const iocshArgBuf *args) { mcsControllerConfig(args[0].sval, args[1].ival, args[2].sval, args[3].ival, args[4].ival, args[5].ival, args[6].ival); } static const iocshArg mcsAxisConfigArg0 = {"controller port name", iocshArgString}; static const iocshArg mcsAxisConfigArg1 = {"axis number", iocshArgInt}; static const iocshArg mcsAxisConfigArg2 = {"a rotary stage", iocshArgInt}; static const iocshArg *const mcsAxisConfigArgs[] = { &mcsAxisConfigArg0, &mcsAxisConfigArg1, &mcsAxisConfigArg2 }; static const iocshFuncDef mcsAxisConfigDef = {"mcsAxisConfig", 3, mcsAxisConfigArgs}; static const iocshArg mcsControllerConfigArg0 = {"port name", iocshArgString}; static const iocshArg mcsControllerConfigArg1 = {"controller number", iocshArgInt}; static const iocshArg mcsControllerConfigArg2 = {"serial port name", iocshArgString}; static const iocshArg mcsControllerConfigArg3 = {"serial port address", iocshArgInt}; static const iocshArg mcsControllerConfigArg4 = {"number of axes", iocshArgInt}; static const iocshArg mcsControllerConfigArg5 = {"moving poll period (ms)", iocshArgInt}; static const iocshArg mcsControllerConfigArg6 = {"idle poll period (ms)", iocshArgInt}; static const iocshArg *const mcsControllerConfigArgs[] = { &mcsControllerConfigArg0, &mcsControllerConfigArg1, &mcsControllerConfigArg2, &mcsControllerConfigArg3, &mcsControllerConfigArg4, &mcsControllerConfigArg5, &mcsControllerConfigArg6 }; static const iocshFuncDef mcsControllerConfigDef = {"mcsControllerConfig", 7, mcsControllerConfigArgs}; static void mcsControllerConfigCallFunc(const iocshArgBuf *args) { mcsControllerConfig(args[0].sval, args[1].ival, args[2].sval, args[3].ival, args[4].ival, args[5].ival, args[6].ival); } static const iocshArg mcsAxisConfigArg0 = {"controller port name", iocshArgString}; static const iocshArg mcsAxisConfigArg1 = {"axis number", iocshArgInt}; static const iocshArg mcsAxisConfigArg2 = {"a rotary stage", iocshArgInt}; static const iocshArg *const mcsAxisConfigArgs[] = { &mcsAxisConfigArg0, &mcsAxisConfigArg1, &mcsAxisConfigArg2 }; static const iocshFuncDef mcsAxisConfigDef = {"mcsAxisConfig", 3, mcsAxisConfigArgs}; Mostly Boilerplate Two IOC shell commands this time. static void mcsAxisConfigCallFunc(const iocshArgBuf *args) { mcsAxisConfig(args[0].sval, args[1].ival, args[2].ival); } static void mcsRegister(void) { iocshRegister(&mcsControllerConfigDef, mcsControllerConfigCallFunc); iocshRegister(&mcsAxisConfigDef, mcsAxisConfigCallFunc); } epicsExportRegistrar(mcsRegister); static void mcsAxisConfigCallFunc(const iocshArgBuf *args) { mcsAxisConfig(args[0].sval, args[1].ival, args[2].ival); } static void mcsRegister(void) { iocshRegister(&mcsControllerConfigDef, mcsControllerConfigCallFunc); iocshRegister(&mcsAxisConfigDef, mcsAxisConfigCallFunc); } epicsExportRegistrar(mcsRegister);

And a DBD File registrar(“mcsRegister") Connects EPICS to the driver.

Database Template # Communications state indication record(bi, "$(P)$(R):CONNECTED") { field(SCAN, "I/O Intr") field(DTYP, "asynInt32") field(VAL, "0") field(INP, field(ZNAM, "Disconnected") field(ONAM, "Connected") } record(longin, "$(P)$(R):VERSIONHI") { field(SCAN, "I/O Intr") field(DTYP, "asynInt32") field(VAL, "0") field(INP, } # Communications state indication record(bi, "$(P)$(R):CONNECTED") { field(SCAN, "I/O Intr") field(DTYP, "asynInt32") field(VAL, "0") field(INP, field(ZNAM, "Disconnected") field(ONAM, "Connected") } record(longin, "$(P)$(R):VERSIONHI") { field(SCAN, "I/O Intr") field(DTYP, "asynInt32") field(VAL, "0") field(INP, } Often two templates, one for the controller.

Database Template record(bo, "$(P)$(R):CALIBRATE") { field(SCAN, "Passive") field(DTYP, "asynInt32") field(VAL, "0") field(OUT, field(PINI, "1") field(ZNAM, "Idle") field(ONAM, "Calibrate") } record(bo, "$(P)$(R):CALIBRATE") { field(SCAN, "Passive") field(DTYP, "asynInt32") field(VAL, "0") field(OUT, field(PINI, "1") field(ZNAM, "Idle") field(ONAM, "Calibrate") } And another for an axis. All standard axis fields are connected to a motor record. Extensions need their own records.

Using – The Startup Script cd "$(INSTALL)" dbLoadDatabase("dbd/example.dbd") example_registerRecordDeviceDriver(pdbbase) drvAsynIPPortConfigure(“smaract1_ser", "localhost:9015", 100, 0, 0) mcsControllerConfig("smaract1", 1, "smaract1_s", 0, 3, 1000, 1000) mcsAxisConfig("smaract1", 0) mcsAxisConfig("smaract1", 1) mcsAxisConfig("smaract1", 2) dbLoadRecords("db/example.db") iocInit cd "$(INSTALL)" dbLoadDatabase("dbd/example.dbd") example_registerRecordDeviceDriver(pdbbase) drvAsynIPPortConfigure(“smaract1_ser", "localhost:9015", 100, 0, 0) mcsControllerConfig("smaract1", 1, "smaract1_s", 0, 3, 1000, 1000) mcsAxisConfig("smaract1", 0) mcsAxisConfig("smaract1", 1) mcsAxisConfig("smaract1", 2) dbLoadRecords("db/example.db") iocInit Note the chaining of an IP asyn port to the Smaract controller. Definition of each axis that the controller is to support.

Motion at Diamond Also have about 10 Newport XPS. Plus a few one-off controllers. 24 operational beamlines. >1000 motorised axes VME PMAC Turbo 2 + separate UMAC and Amplifiers crates Geobrick LV IMS Integrated amplifiers 4U box Ethernet comms

Coordinated Motion 1. High level software at the motor record level. Set VAL in two or more motor records as quick as you can. caput {motor1}.VAL 1.0 caput {motor2}.VAL 2.0 Not very synchronized. Motors start and stop at different times. Simple. 2. Queue up move commands in the driver (‘deferred moves’). caput {controller}:DEFER 1 (turn on deferred moves) caput {motor1}.VAL 1.0 caput {motor2}.VAL 2.0 caput {controller}:DEFER 0 (turn off deferred moves – motor start moving). A little more synchronized (best effort by the driver). Motors start and stop at different times. Makes client app more complicated because multiple threads are sometimes needed.

Coordinated Motion 3. Use controller co-ordinated, or group, moves. For PMAC: New driver for co-ordinate systems PLC running on controller Controller motion program True synchronized motion – axes start and stop at the same time. More complicated. More code. For XPS: Group axes into a MultipleAxis group. Do a group move on 1 or more axes. Complexity hidden in XPS firmware. EPICS driver can do this using deferred moves.

Coordinated Motion PMAC co-ordinate system example:

Trajectory Scanning Trajectory, or continuous, scanning. -> moving 1 or more axes (synchronized), outputting gate and trigger signals PMAC Position compare. Define a start/stop/step. Gate and trigger output. NOTE: unreliable – we developed a custom hardware to do it for us. No trajectory scanning support unless done at the PLC level. XPS Position compare (same as PMAC). Time flasher mode (time based pulses). Trajectory scanning with time based pulses. Axis positions at each point are stored. Note: mostly the requirement is to trigger off one axis only, but move multiple axes together. This can be done using PMAC or XPS position compare and moving the axes doing normal coordinated motion.