Removed ImsSrc; Added motorIms submodule

This commit is contained in:
kpetersn
2019-04-10 14:40:31 -05:00
parent 2784be1ffb
commit b1886641e0
26 changed files with 7 additions and 4740 deletions
+3
View File
@@ -58,3 +58,6 @@
[submodule "modules/motorNewFocus"]
path = modules/motorNewFocus
url = https://github.com/epics-motor/motorNewFocus.git
[submodule "modules/motorIms"]
path = modules/motorIms
url = https://github.com/epics-motor/motorIms.git
@@ -1,60 +0,0 @@
# ImsMDrivePlus asyn model 3 substitutions file
# SREV (not shown) is the number of steps per revolution is 200 by
# default
#
# There are three pivot points to which the six motors are attached.
# Three provide vertical motion: M0Y, M1Y, and M2Y. One provides
# horizontal motion in the Z direction (along beamline): M2Z. Two
# provide horizontal motion in the X direction (perpendicular to
# beamline): M0X, M2X.
#
#
# For SREV:
# 2048 = internal encoder (EQ)
# 250 = external encoder, NUMERIK-JENA
# 200 = external encoder, Renishaw
# 51200 = no encoder
# CXI DG4 Support Motion Stand (DG4) Substitution file
file "$(MOTOR)/db/basic_asyn_motor.db"
{
pattern
{P, N, M, DTYP, PORT, DESC, ADDR, EGU, DIR, VELO, VBAS, ACCL, BDST, BVEL, BACC, DHLM, DLLM, MRES, PREC, INIT}
{IMS:, 1, 1, "asynMotor", IMS1, "MDrive23", 0, mm, Pos, 3.0, 0.017, 2.0, 0, 0.017, 2.0, 100, -100, 0.000083, 4, ""}
}
file "$(MOTOR)/db/IMS_extra.db"
{
pattern
{DEV, AREA, LOC, PORT, ADDR, TIMEOUT}
{IMS, IOC, 1, IMS1, 0, 1}
}
# field(DESC,"Y DG4 DS North Motor") # same
# field(DTYP,"ImsMDrivePlus") # same
# field(OUT, "#C0 S0 @") # same
# field(DIR, "Pos") # same
# field(SREV,"2048") # SREV [steps/rev], MRES = UREV/SREV
# field(UREV,"0.017") # UREV [egu/rev], MRES = UREV/SREV
# field(UEIP,"No")
# field(S, "10") # no equivalent
# field(SMAX,"20") # SMAX[rev/s], VELO = SMAX * UREV
# field(SBAS,"1") # VBAS = SBAS * UREV
# field(ACCL,"2.0") # same
# field(BDST,"0") # same
# field(RDBD,"0.0005")
# field(RTRY,"1")
# field(SBAK,"1") # BVEL = SBAK * UREV
# field(BACC,"2.0") # same
# field(DLY, "0")
# field(PREC,"4") # same
# field(EGU, "mm") # same
# field(DHLM,"200.0") # same
# field(DLLM,"-200.0") # same
# field(INIT,"") # same
# field(TWV, "1") # same
+2 -29
View File
@@ -1,5 +1,5 @@
# The is the "ASYN" example for communication to an
# IMS483 controller. The examples must be configured by including or omitting
# The is the "ASYN" example for communication to a controller
# The examples must be configured by including or omitting
# comment characters (i.e., #'s) from this file.
# "#!" marks lines that can be uncommented.
@@ -28,33 +28,6 @@ dbLoadRecords("$(MOTOR)/db/motorUtil.db", "P=IOC:")
# Configure the ASYN server code. This MUST be configured too!
< st_asynserver.cmd.Vx
# IMS IM483 driver setup parameters:
# (1) maximum number of controllers in system
# (2) motor task polling rate (min=1Hz,max=60Hz)
# SM - single mode PL - party mode
#!IM483SMSetup(1, 10)
#!IM483PLSetup(1, 10)
# IMS IM483 configuration parameters:
# (1) controller# (chain#) being configured,
# (2) ASYN port name
# SM - single mode PL - party mode
#!IM483SMConfig(0, "a-Serial[0]")
#!drvIM483SMdebug=4
#!IM483PLConfig(0, "a-Serial[0]")
#!drvIM483PLdebug=4
# IMS MDrive driver setup parameters:
# (1) maximum number of controllers in system
# (2) motor task polling rate (min=1Hz,max=60Hz)
#!MDriveSetup(1, 10)
# IMS MDrive driver configuration parameters:
# (1) controller# being configured
#!MDriveConfig(0, "a-Serial[0]")
#!drvMDrivedebug = 4
# Aerotech Ensemble digital servo controller Setup
# (1) maximum number of controllers in system
-29
View File
@@ -1,29 +0,0 @@
< envPaths
# Tell EPICS all about the record types, device-support modules, drivers,
# etc. in this build from CARS
dbLoadDatabase("../../dbd/WithAsyn.dbd")
WithAsyn_registerRecordDeviceDriver(pdbbase)
### Motors
# Motors substitutions, customize this for your motor
dbLoadTemplate "motor.substitutions.ims"
# Configure asyn communication port, first
# drvAsynIPPortConfigure(IOPortName, port, priority, disable auto-connect, no process EOS)
drvAsynIPPortConfigure("M06", "ts-b34-nw09:2101", 0, 0, 0 )
# Configure one controller per motor, each controller just has 1 axis
# motorPortName, portName, deviceName, movingPollPeriod, idlePollPeriod
ImsMDrivePlusCreateController("IMS1", "M06", "1", 200, 5000)
# Optional: Enable tracing
#asynSetTraceIOMask("IMS1",0,0)
asynSetTraceMask("IMS1",0,9)
# Initialize the IOC and start processing records
iocInit()
-27
View File
@@ -21,33 +21,6 @@ asynSetOption("L0", -1, "stop", "1")
asynSetOption("L0", -1, "clocal", "Y")
asynSetOption("L0", -1, "crtscts", "N")
# IMS IM483 driver setup parameters:
# (1) maximum number of controllers in system
# (2) motor task polling rate (min=1Hz,max=60Hz)
# SM - single mode PL - party mode
#!IM483SMSetup(1, 10)
#!IM483PLSetup(1, 10)
# IMS IM483 configuration parameters:
# (1) controller# (chain#) being configured,
# (2) ASYN port name
# SM - single mode PL - party mode
#!IM483SMConfig(0, "L0")
#!var drvIM483SMdebug 4
#!IM483PLConfig(0, "L0")
#!var drvIM483PLdebug 4
# IMS MDrive driver setup parameters:
# (1) maximum number of controllers in system
# (2) motor task polling rate (min=1Hz,max=60Hz)
#!MDriveSetup(1, 10)
# IMS MDrive driver configuration parameters:
# (1) controller# being configured,
# (2) ASYN port name
#!MDriveConfig(0, "L0")
#!var drvMDrivedebug 4
# Aerotech Ensemble digital servo controller Setup
# (1) maximum number of controllers in system
-27
View File
@@ -10,33 +10,6 @@ dbLoadRecords("$(MOTOR)/db/motorUtil.db", "P=IOC:")
# This is for WIN32, local serial ports not supported. Must use IP terminal server.
# IMS IM483 driver setup parameters:
# (1) maximum number of controllers in system
# (2) motor task polling rate (min=1Hz,max=60Hz)
# SM - single mode PL - party mode
#!IM483SMSetup(1, 10)
#!IM483PLSetup(1, 10)
# IMS IM483 configuration parameters:
# (1) controller# (chain#) being configured,
# (2) ASYN port name
# SM - single mode PL - party mode
#!IM483SMConfig(0, "L0")
#!var drvIM483SMdebug 4
#!IM483PLConfig(0, "L0")
#!var drvIM483PLdebug 4
# IMS MDrive driver setup parameters:
# (1) maximum number of controllers in system
# (2) motor task polling rate (min=1Hz,max=60Hz)
#!MDriveSetup(1, 10)
# IMS MDrive driver configuration parameters:
# (1) controller# being configured,
# (2) ASYN port name
#!MDriveConfig(0, "L0")
#!var drvMDrivedebug 4
# Aerotech Ensemble digital servo controller Setup
# (1) maximum number of controllers in system
# (2) motor task polling rate (min=1Hz,max=60Hz)
+1
View File
@@ -13,6 +13,7 @@ SUBMODULES += motorFaulhaber
ifdef IPAC
SUBMODULES += motorHytec
endif
SUBMODULES += motorIms
SUBMODULES += motorKohzu
SUBMODULES += motorMclennan
SUBMODULES += motorMicos
+1
Submodule modules/motorIms added at 47aefe2c61
-13
View File
@@ -1,13 +0,0 @@
# Database for IMS
# send IMS S command to save parameters to NVM
record(bo,"$(DEV):$(AREA):$(LOC):IMS_S") {
field(DESC, "Ims Save to NVM")
field(PINI, "NO")
field(DTYP, "asynInt32")
field(OUT, "@asyn($(PORT),$(ADDR),$(TIMEOUT))IMS_SAVETONVM")
field(VAL, "0")
field(ZNAM, "No")
field(ONAM, "Yes")
}
-596
View File
@@ -1,596 +0,0 @@
//! @File : ImsMDrivePlusMotorAxis.cpp
//! Motor record driver level support for Intelligent Motion Systems, Inc.
//! MDrivePlus series; M17, M23, M34.
//! Use "model 3" asyn motor, asynMotorController and asynMotorAxis classes.
//!
//! Original Author : Nia Fong
//! Date : 11-21-2011
//! Current Author : Mitch D'Ewart (SLAC)
//
// Revision History
// ----------------
// 11-21-2011 NF Initial version
// 12-15-2014 RLS Bug fix from Thierry Zamofing (PSI); acceleration was set to the same value as the speed.
#include <stddef.h>
#include <stdlib.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <exception>
#include <epicsThread.h>
#include <iocsh.h>
#include <asynOctetSyncIO.h>
#include "asynMotorController.h"
#include "asynMotorAxis.h"
#include <epicsExport.h>
#include "ImsMDrivePlusMotorController.h"
////////////////////////////////////////////////////////
//! ImsMDrivePlusMotorAxis()
// Constructor
//
//! @param[in] pC pointer to ImsMDrivePlusMotorController
//! @param[in] axisNum axis number
////////////////////////////////////////////////////////
ImsMDrivePlusMotorAxis::ImsMDrivePlusMotorAxis(ImsMDrivePlusMotorController *pC, int axisNum)
: asynMotorAxis(pC, axisNum), pController(pC)
{
static const char *functionName = "ImsMDrivePlusMotorAxis()";
asynPrint(pC->pasynUserSelf, ASYN_TRACEIO_DRIVER, "%s:%s: Create Axis %d\n", DRIVER_NAME, functionName, axisNum);
// run setup/initialize routines here
// check communication, set moving status
if (configAxis() == asynError) {
asynPrint(pC->pasynUserSelf, ASYN_TRACE_ERROR, "%s:%s: controller config failed for motor port=%s\n", DRIVER_NAME, functionName, pController->motorName);
// TODO throw exception
}
callParamCallbacks();
}
////////////////////////////////////////
//! configAxis()
//! Used smarACTMCMotorDriver.cpp as reference
//
//! check communication by checking version returns a good string
//! set moving status (PR MV)
////////////////////////////////////////
asynStatus ImsMDrivePlusMotorAxis::configAxis()
{
asynStatus status = asynError;
char cmd[MAX_CMD_LEN];
char resp[MAX_BUFF_LEN];
size_t nread;
int maxRetries=3;
static const char *functionName = "configAxis()";
// figure out what needs to be done to initialize controller
// try getting firmware version to make sure communication works
sprintf(cmd, "PR VR");
for (int i=0; i<maxRetries; i++) {
status = pController->writeReadController(cmd, resp, sizeof(resp), &nread, IMS_TIMEOUT);
asynPrint(pController->pasynUserSelf, ASYN_TRACEIO_DRIVER, "%s:%s: Version retry.\n", DRIVER_NAME, functionName);
if (status == asynError) {
asynPrint(pController->pasynUserSelf, ASYN_TRACE_ERROR, "%s:%s: Version inquiry FAILED.\n", DRIVER_NAME, functionName);
} else { // ok to check firmware level/format or just strlen? v3.009
if (strlen(resp) < 2) {
asynPrint(pController->pasynUserSelf, ASYN_TRACE_ERROR, "%s:%s: Version inquiry FAILED version=%s.\n", DRIVER_NAME, functionName, resp);
setIntegerParam(pController->motorStatusProblem_, 1);
setIntegerParam(pController->motorStatusCommsError_, 1);
status = asynError; return(status);
}
break;
}
}
// set encoder flags
sprintf(cmd, "PR EE");
status = pController->writeReadController(cmd, resp, sizeof(resp), &nread, IMS_TIMEOUT);
if (status == asynSuccess) {
int val = atoi(resp);
setIntegerParam(pController->motorStatusHasEncoder_, val ? 1:0);
setIntegerParam(pController->motorStatusGainSupport_, val ? 1:0);
asynPrint(pController->pasynUserSelf, ASYN_TRACEIO_DRIVER, "%s:%s: set motorStatusHasEncoder_=%d, motorStatusGainSupport_=%d.\n", DRIVER_NAME, functionName, val, val);
}
// start idle timer
//idleTimeStart = epicsTime::getCurrent();
return status;
}
////////////////////////////////////////////////////////
//! setAxisMoveParameters()
//! set base velocity, moving velocity, and acceleration
//
//! @param[in] minVelocity
//! @param[in] maxVelocity
//! @param[in] acceleration
////////////////////////////////////////////////////////
asynStatus ImsMDrivePlusMotorAxis::setAxisMoveParameters(double minVelocity, double maxVelocity, double acceleration)
{
asynStatus status = asynError;
char cmd[MAX_CMD_LEN];
static const char *functionName = "setAxisMoveParameters()";
// check if using base velocity (VI)
// for MDrivePlus initial velocity must be below max_velocity
if (minVelocity > 0) { // base velocity set
if (minVelocity > maxVelocity) { // illegal state
asynPrint(pController->pasynUserSelf, ASYN_TRACE_ERROR, "%s:%s: base velocity=%f cannot be greater than max velocity=%f\n", DRIVER_NAME, functionName, minVelocity, maxVelocity);
goto bail;
}
// set base velocity
sprintf(cmd, "VI=%ld", (long)minVelocity);
status = pController->writeController(cmd, IMS_TIMEOUT);
if (status) goto bail;
}
// set velocity
sprintf(cmd, "VM=%ld", (long)maxVelocity);
status = pController->writeController(cmd, IMS_TIMEOUT);
if (status) goto bail;
// set accceleration
if (acceleration != 0) {
sprintf(cmd, "A=%ld", (long)acceleration);
status = pController->writeController(cmd, IMS_TIMEOUT);
if (status) goto bail;
}
bail:
if (status) {
char buff[LOCAL_LINE_LEN];
sprintf(buff, "%s:%s: ERROR setting motor velocity and acceleration", DRIVER_NAME, functionName);
handleAxisError(buff);
}
callParamCallbacks();
return status;
}
////////////////////////////////////////////////////////
//! move()
//! Override asynMotorAxis class implementation
// Based on smarActMCSMotorDriver.cpp
//
//! @param[in] position
//! @param[in] relative making absolute or relative move
//! @param[in] minVelocity
//! @param[in] maxVelocity
//! @param[in] acceleration
////////////////////////////////////////////////////////
asynStatus ImsMDrivePlusMotorAxis::move(double position, int relative, double minVelocity, double maxVelocity, double acceleration)
{
asynStatus status = asynError;
char cmd[MAX_CMD_LEN];
static const char *functionName = "move()";
// sent commands to motor to set velocities and acceleration
status = setAxisMoveParameters(minVelocity, maxVelocity, acceleration);
if (status) goto bail;
// move
asynPrint(pController->pasynUserSelf, ASYN_TRACEIO_DRIVER, "%s:%s: VBASE=%f, VELO=%f, ACCL=%f, position=%f, relative=%d\n", DRIVER_NAME, functionName, minVelocity, maxVelocity, acceleration, position, relative);
if (relative) { // relative move MR
sprintf(cmd, "MR %ld", (long)position);
} else { // absolute move MA
sprintf(cmd, "MA %ld", (long)position);
}
status = pController->writeController(cmd, IMS_TIMEOUT);
if (status) goto bail;
bail:
if (status) {
char buff[LOCAL_LINE_LEN];
sprintf(buff, "%s:%s: ERROR moving motor", DRIVER_NAME, functionName);
handleAxisError(buff);
}
callParamCallbacks();
return status;
}
////////////////////////////////////////////////////////
//! moveVelocity()
//! Override asynMotorAxis class implementation
// Based on smarActMCSMotorDriver.cpp
//! move at a fixed velocity until stop() called
//
//! @param[in] minVelocity
//! @param[in] maxVelocity
//! @param[in] acceleration
////////////////////////////////////////////////////////
asynStatus ImsMDrivePlusMotorAxis::moveVelocity(double minVelocity, double maxVelocity, double acceleration)
{
asynStatus status = asynError;
char cmd[MAX_CMD_LEN];
static const char *functionName = "moveVelocity()";
// sent commands to motor to set velocities and acceleration
status = setAxisMoveParameters(minVelocity, maxVelocity, acceleration);
if (status) goto bail;
// move
asynPrint(pController->pasynUserSelf, ASYN_TRACEIO_DRIVER, "%s:%s: VBASE=%f, VELO=%f, ACCL=%f\n", DRIVER_NAME, functionName, minVelocity, maxVelocity, acceleration);
sprintf(cmd, "SL %ld", (long)maxVelocity);
status = pController->writeController(cmd, IMS_TIMEOUT);
if (status) goto bail;
bail:
if (status) {
char buff[LOCAL_LINE_LEN];
sprintf(buff, "%s:%s: ERROR jogging motor", DRIVER_NAME, functionName);
handleAxisError(buff);
}
callParamCallbacks();
return status;
}
////////////////////////////////////////////////////////
//! stop()
//! Override asynMotorAxis class implementation
// Based on smarActMCSMotorDriver.cpp
//
//! @param[in] acceleration
////////////////////////////////////////////////////////
asynStatus ImsMDrivePlusMotorAxis::stop(double acceleration)
{
asynStatus status = asynError;
char cmd[MAX_CMD_LEN];
static const char *functionName = "stop()";
// set accceleration
if (acceleration != 0) {
sprintf(cmd, "A=%ld", (long)acceleration);
status = pController->writeController(cmd, IMS_TIMEOUT);
if (status) goto bail;
}
// move
sprintf(cmd, "SL 0");
status = pController->writeController(cmd, IMS_TIMEOUT);
if (status) goto bail;
bail:
if (status) {
char buff[LOCAL_LINE_LEN];
sprintf(buff, "%s:%s: ERROR stopping motor", DRIVER_NAME, functionName);
handleAxisError(buff);
}
callParamCallbacks();
return status;
}
////////////////////////////////////////////////////////
//! home()
//! Override asynMotorAxis class implementation
// Based on smarActMCSMotorDriver.cpp
//
//! direction=1: slew at maxVelocity in the minus direction (until switch activates) then creep at vi in the plus direction (until switch becomes inactive again)
//! direction=3: slew at maxVelocity in the plus direction (until switch activates) then creep at vi in the minus direction (until switch becomes inactive again)
//
//! @param[in] minVelocity
//! @param[in] maxVelocity
//! @param[in] acceleration
//! @param[in] forwards direction to home, 0=minus direction, 1=plus direction
////////////////////////////////////////////////////////
asynStatus ImsMDrivePlusMotorAxis::home(double minVelocity, double maxVelocity, double acceleration, int forwards)
{
asynStatus status = asynError;
char cmd[MAX_CMD_LEN];
char resp[MAX_BUFF_LEN];
size_t nread;
int direction = 1; // direction to home, initialize homing in minus direction
double baseVelocity=0;
static const char *functionName = "home()";
// check if using base velocity (VI)
// for MDrivePlus initial velocity must be below max_velocity
if (minVelocity > 0) { // base velocity being configured
if (minVelocity > maxVelocity) { // illegal state
asynPrint(pController->pasynUserSelf, ASYN_TRACE_ERROR, "%s:%s: base velocity=%f cannot be greater than max velocity=%f\n", DRIVER_NAME, functionName, minVelocity, maxVelocity);
goto bail;
}
} else { // base velocity needs to be set because creeping back to home switch at base velocity, so make sure it's nonzero
sprintf(cmd, "PR VI"); // get base velocity setting
status = pController->writeReadController(cmd, resp, sizeof(resp), &nread, IMS_TIMEOUT);
if (status) goto bail;
baseVelocity = atof(resp);
if (baseVelocity == 0) { // set to factory default of 1000
baseVelocity=1000;
}
}
// sent commands to motor to set velocities and acceleration
if (status = setAxisMoveParameters(minVelocity, maxVelocity, acceleration)) goto bail;
// home
if (forwards == 1) { // homing in forward direction
direction = 3;
}
asynPrint(pController->pasynUserSelf, ASYN_TRACEIO_DRIVER, "%s:%s: VBASE=%f, VELO=%f, ACCL=%f, forwards=%d\n", DRIVER_NAME, functionName, minVelocity, maxVelocity, acceleration, forwards);
sprintf(cmd, "HM %d", direction);
status = pController->writeController(cmd, IMS_TIMEOUT);
if (status) goto bail;
bail:
if (status) {
char buff[LOCAL_LINE_LEN];
sprintf(buff, "%s:%s: ERROR homing motor", DRIVER_NAME, functionName);
handleAxisError(buff);
}
callParamCallbacks();
return status;
}
////////////////////////////////////////////////////////
//! setPosition()
//! Override asynMotorAxis class implementation
// Based on smarActMCSMotorDriver.cpp
//
//! @param[in] position value to set motor's internal position
////////////////////////////////////////////////////////
asynStatus ImsMDrivePlusMotorAxis::setPosition(double position)
{
asynStatus status = asynError;
char cmd[MAX_CMD_LEN];
static const char *functionName = "setPosition()";
// set position
asynPrint(pController->pasynUserSelf, ASYN_TRACEIO_DRIVER, "%s:%s: position=%f\n", DRIVER_NAME, functionName, position);
sprintf(cmd, "P=%ld", (long)position);
status = pController->writeController(cmd, IMS_TIMEOUT);
if (status) goto bail;
bail:
if (status) {
char buff[LOCAL_LINE_LEN];
sprintf(buff, "%s:%s: ERROR setting motor position", DRIVER_NAME, functionName);
handleAxisError(buff);
}
callParamCallbacks();
return status;
}
////////////////////////////////////////////////////////
//! poll()
//! Override asynMotorAxis class implementation
// Based on smarActMCSMotorDriver.cpp
//
//! Set position and moving flag
//
//! @param[in] moving pointer to moving flag
////////////////////////////////////////////////////////
asynStatus ImsMDrivePlusMotorAxis::poll(bool *moving)
{
asynStatus status = asynError;
char cmd[MAX_CMD_LEN];
char resp[MAX_BUFF_LEN];
size_t nread;
int val=0;
double position;
*moving = false;
static const char *functionName = "poll()";
//epicsTime currentTime;
// get position
sprintf(cmd, "PR P");
status = pController->writeReadController(cmd, resp, sizeof(resp), &nread, IMS_TIMEOUT);
if (status) goto bail;
position = atof(resp);
// update motor record position values, just update encoder's even if not using one
setDoubleParam(pController->motorEncoderPosition_, position);
setDoubleParam(pController->motorPosition_, position);
// get moving flag
sprintf(cmd, "PR MV");
status = pController->writeReadController(cmd, resp, sizeof(resp), &nread, IMS_TIMEOUT);
if (status) goto bail;
val = atoi(resp);
if (val == 1) *moving = true; // updating moving flag
/* else { // not moving
if (prevMovingState == 1) {// state changed, moving before, start idle timer
idleTimeStart = epicsTime::getCurrent();
}
}
prevMovingState = val;
*/
// update motor record status done with moving status
setIntegerParam(pController->motorStatusDone_, ! *moving );
/*
// if position has changed and moving stopped for over 30sec, update NVM with current position so the motor will remember
// its location just in case its power dies
currentTime = epicsTime::getCurrent();
idleTime = currentTime - idleTimeStart;
//printf("prevPos=%f, pos=%f, moving=%d, idleTime=%f\n", prevPosition, position, *moving, idleTime);
if (prevPosition != position && *moving == false && idleTime > 30) {
sprintf(cmd, "S");
if (status = pController->writeController(cmd, IMS_TIMEOUT)) {
asynPrint(pController->pasynUserSelf, ASYN_TRACEIO_DRIVER, "%s:poll(): ERROR saving position to NVM\n", DRIVER_NAME);
goto bail;
}
asynPrint(pController->pasynUserSelf, ASYN_TRACEIO_DRIVER, "%s:poll(): saving parameters to NVM\n", DRIVER_NAME);
idleTimeStart = epicsTime::getCurrent();
prevPosition = position;
}
*/
// get home switch value
if (pController->homeSwitchInput != -1) {
sprintf(cmd, "PR I%d", pController->homeSwitchInput);
status = pController->writeReadController(cmd, resp, sizeof(resp), &nread, IMS_TIMEOUT);
if (status) goto bail;
val = atoi(resp);
setIntegerParam(pController->motorStatusHome_, val);
}
// get positive limit switch value
if (pController->posLimitSwitchInput != -1) {
sprintf(cmd, "PR I%d", pController->posLimitSwitchInput);
status = pController->writeReadController(cmd, resp, sizeof(resp), &nread, IMS_TIMEOUT);
if (status) goto bail;
val = atoi(resp);
setIntegerParam(pController->motorStatusHighLimit_, val);
}
// get negative limit switch value
if (pController->negLimitSwitchInput != -1) {
sprintf(cmd, "PR I%d", pController->negLimitSwitchInput);
status = pController->writeReadController(cmd, resp, sizeof(resp), &nread, IMS_TIMEOUT);
if (status) goto bail;
val = atoi(resp);
setIntegerParam(pController->motorStatusLowLimit_, val);
}
// error polling
bail:
if (status) {
char buff[LOCAL_LINE_LEN];
sprintf(buff, "%s:%s: ERROR polling motor", DRIVER_NAME, functionName);
handleAxisError(buff);
}
// update motor status if polling was successful
if (status == 0) {
setIntegerParam(pController->motorStatusCommsError_, 0); // reset COMM error
setIntegerParam(pController->motorStatusProblem_, 0); // reset problem error, bit 10
}
// update motor record
callParamCallbacks();
int mstat;
pController->getIntegerParam(pController->motorStatus_, &mstat);
asynPrint(pController->pasynUserSelf, ASYN_TRACEIO_DRIVER, "%s:%s: POS=%f, MSTAT=%d\n", DRIVER_NAME, functionName, position, mstat);
return status;
}
////////////////////////////////////////////////////////
//! saveToNVM()
//! Save user variables and flags to non-volatile RAM in case of power loss
//
////////////////////////////////////////////////////////
asynStatus ImsMDrivePlusMotorAxis::saveToNVM()
{
asynStatus status = asynError;
char cmd[MAX_CMD_LEN];
static const char *functionName = "saveToNVM()";
// send save command
sprintf(cmd, "S");
status = pController->writeController(cmd, IMS_TIMEOUT);
if (status) goto bail;
asynPrint(pController->pasynUserSelf, ASYN_TRACEIO_DRIVER, "%s:%s: Saved to NVM\n", DRIVER_NAME, functionName);
bail:
if (status) {
char buff[LOCAL_LINE_LEN];
sprintf(buff, "%s:%s: ERROR saving to NVM", DRIVER_NAME, functionName);
handleAxisError(buff);
}
callParamCallbacks(); //FIXME is this necessary??
return status;
}
////////////////////////////////////////////////////////
//! handleAxisError()
//! Set motorStatusProblem_
//! Then try to get and print error code
//
////! @param[in] errMsg pointer to error message from calling function
////////////////////////////////////////////////////////
void ImsMDrivePlusMotorAxis::handleAxisError(char *errMsg)
{
char cmd[MAX_CMD_LEN];
char resp[MAX_BUFF_LEN];
size_t nread=0;
int errCode=0;
char errCodeString[MAX_BUFF_LEN];
static const char *functionName = "handleAxisError()";
// set motorStatusProblem_ bit
setIntegerParam(pController->motorStatusProblem_, 1);
// read error code
sprintf(cmd, "PR ER");
pController->writeReadController(cmd, resp, sizeof(resp), &nread, IMS_TIMEOUT);
errCode = atoi(resp);
switch (errCode) {
case 0: strcpy(errCodeString, "No Error"); break;
case 6: strcpy(errCodeString, "An I/O is already set to this type. Applies to non-General Purpose I/O"); break;
case 8: strcpy(errCodeString, "Tried to set an I/O to an incorrect I/O type"); break;
case 9: strcpy(errCodeString, "Tried to write to I/O set as Input or is 'TYPED'"); break;
case 10: strcpy(errCodeString, "Illegal I/O number"); break;
case 11: strcpy(errCodeString, "Incorrect CLOCK type"); break;
case 12: strcpy(errCodeString, "Illegal Trip/Capture"); break;
case 20: strcpy(errCodeString, "Tried to set unknown variable or flag"); break;
case 21: strcpy(errCodeString, "Tried to set an incorrect value"); break;
case 22: strcpy(errCodeString, "VI is set greater than or equal to VM"); break;
case 23: strcpy(errCodeString, "VM is set less than or equal to VI"); break;
case 24: strcpy(errCodeString, "Illegal data entered"); break;
case 25: strcpy(errCodeString, "Variable or flag is read only"); break;
case 26: strcpy(errCodeString, "Variable or flag is not allowed to be incremented or decremented"); break;
case 27: strcpy(errCodeString, "Trip not defined"); break;
case 28: strcpy(errCodeString, "Trying to redefine a program label or variable"); break;
case 29: strcpy(errCodeString, "Trying to redefine a build in command, variable, or flag"); break;
case 30: strcpy(errCodeString, "Unknown label or user variable"); break;
case 31: strcpy(errCodeString, "Program label or user variable table is full"); break;
case 32: strcpy(errCodeString, "Trying to set a label"); break;
case 33: strcpy(errCodeString, "Trying to set and instruction"); break;
case 34: strcpy(errCodeString, "Trying to execute a variable or flag"); break;
case 35: strcpy(errCodeString, "Trying to print illegal variable or flag"); break;
case 36: strcpy(errCodeString, "Illegal motor count to encoder count ratio"); break;
case 37: strcpy(errCodeString, "Command, variable, or flag not available in drive"); break;
case 38: strcpy(errCodeString, "Missing parameter separator"); break;
case 39: strcpy(errCodeString, "Trip on position and trip on relative distance not allowed together"); break;
case 40: strcpy(errCodeString, "Program not running"); break;
case 41: strcpy(errCodeString, "Stack overflow"); break;
case 42: strcpy(errCodeString, "Illegal program address"); break;
case 43: strcpy(errCodeString, "Tried to overflow program stack"); break;
case 44: strcpy(errCodeString, "Program locked"); break;
case 45: strcpy(errCodeString, "Trying to overflow program space"); break;
case 46: strcpy(errCodeString, "Not in program mode"); break;
case 47: strcpy(errCodeString, "Tried to write in illegal flash address"); break;
case 48: strcpy(errCodeString, "Program execution stopped by I/O set as stop"); break;
case 61: strcpy(errCodeString, "Trying to set illegal baud rate"); break;
case 62: strcpy(errCodeString, "IV already pending or IF flag already true"); break;
case 63: strcpy(errCodeString, "Character over-run"); break;
case 64: strcpy(errCodeString, "Startup calibration failed"); break;
case 70: strcpy(errCodeString, "Flash check sum failed"); break;
case 71: strcpy(errCodeString, "Internal temperature warning, 10 C to shutdown"); break;
case 72: strcpy(errCodeString, "Internal over temp fault, disabling drive"); break;
case 73: strcpy(errCodeString, "Tried to save while moving"); break;
case 74: strcpy(errCodeString, "Tried to initialize parameters or clear program while moving"); break;
case 75: strcpy(errCodeString, "Linear over temperature error"); break;
case 80: strcpy(errCodeString, "Home switch not defined"); break;
case 81: strcpy(errCodeString, "Home type not defined"); break;
case 82: strcpy(errCodeString, "Went to both limits and did not find home"); break;
case 83: strcpy(errCodeString, "Reached plus limit switch"); break;
case 84: strcpy(errCodeString, "Reached minus limit switch"); break;
case 85: strcpy(errCodeString, "MA or MR isn't allowed during home and home isn't allowed while moving"); break;
case 86: strcpy(errCodeString, "Stall detected"); break;
case 87: strcpy(errCodeString, "In clock mode"); break;
case 88: strcpy(errCodeString, "Following error"); break;
case 90: strcpy(errCodeString, "Motion variables are too low switching to EE=1"); break;
case 91: strcpy(errCodeString, "Motion stopped by I/O set as stop"); break;
case 92: strcpy(errCodeString, "Position error in closed loop"); break;
case 93: strcpy(errCodeString, "MR or MA not allowed while correcting position"); break;
}
asynPrint(pController->pasynUserSelf, ASYN_TRACE_ERROR, "%s:%s: %s, %d:'%s'\n", DRIVER_NAME, functionName, errMsg, errCode, errCodeString);
}
-71
View File
@@ -1,71 +0,0 @@
// Description : This is the "model 3" asyn motor driver for IMS MDrivePlus.
// Based on HytecMotorDriver.h.
#ifndef ImsMDrivePlusMotorAxis_H
#define ImsMDrivePlusMotorAxis_H
#include <epicsTime.h>
#include "asynMotorController.h"
#include "asynMotorAxis.h"
#define DRIVER_NAME "ImsMDrivePlusMotorDriver"
#define NUM_AXES 1
#define DEFAULT_NUM_CARDS 32
#define MAX_MESSAGES 100
#define IMS_TIMEOUT 2
#define MAX_BUFF_LEN 80
#define MAX_CMD_LEN MAX_BUFF_LEN-10 // leave room for line feeds surrounding command
#define MAX_NAME_LEN 10
#define LOCAL_LINE_LEN 256
class epicsShareClass ImsMDrivePlusMotorController;
////////////////////////////////////
// ImsMDrivePlusMotorAxis class
// derived from asynMotorAxis class
////////////////////////////////////
class ImsMDrivePlusMotorAxis : public asynMotorAxis
{
public:
///////////////////////////////////
// Override asynMotorAxis functions
///////////////////////////////////
ImsMDrivePlusMotorAxis(ImsMDrivePlusMotorController *pC, int axis);
asynStatus move(double position, int relative, double min_velocity, double max_velocity, double acceleration);
asynStatus moveVelocity(double min_velocity, double max_velocity, double acceleration);
asynStatus home(double min_velocity, double max_velocity, double acceleration, int forwards);
asynStatus stop(double acceleration);
asynStatus poll(bool *moving);
asynStatus setPosition(double position);
////////////////////////////////////////////////////
// IMS MDrivePlus specific functions
////////////////////////////////////////////////////
asynStatus saveToNVM();
protected:
private:
ImsMDrivePlusMotorController *pController;
//int useEncoder; //! using encoder flag
// FIXME handle lost position in driver or ioc??
// epicsTime idleTimeStart; //! timer used to track idle time for saving to NVM
// double prevPosition; //! previous position used to see if motor has moved and need to update position in NVM in case of power failure
// int prevMovingState; //! saves previous moving value
////////////////////////////////////////////////////
// IMS MDrivePlus specific functions
////////////////////////////////////////////////////
asynStatus configAxis();
asynStatus setAxisMoveParameters(double min_velocity, double max_velocity, double acceleration);
void handleAxisError(char *errMsg);
friend class ImsMDrivePlusMotorController;
};
#endif // ImsMDrivePlusMotorAxis_H
@@ -1,361 +0,0 @@
//! @File : ImsMDrivePlusController.cpp
//! Motor record driver level support for Intelligent Motion Systems, Inc.
//! MDrivePlus series; M17, M23, M34.
//! Simple implementation using "model 3" asynMotorController and asynMotorAxis base classes (derived from asynPortDriver)
//!
//! Original Author : Nia Fong
//! Date : 11-21-2011
//! Current Author : Mitch D'Ewart (SLAC)
//!
//! Assumptions :
//! 1) Like all controllers, the MDrivePlus must be powered-on when EPICS is first booted up.
//! 2) Assume single controller-card-axis relationship; 1 controller = 1 axis.
//! 3) Append Line Feed (ctrl-J) to end of command string for party mode support
//! 4) If not using device name to address device, config ImsMDrivePlusCreateController() with empty string "" for deviceName
//
// Revision History
// ----------------
// 11-21-2011 NF Initial version
#include <stddef.h>
#include <stdlib.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <exception>
#include <epicsThread.h>
#include <iocsh.h>
#include <asynOctetSyncIO.h>
#include "asynMotorController.h"
#include "asynMotorAxis.h"
#include "ImsMDrivePlusMotorAxis.h"
#include <epicsExport.h>
#include "ImsMDrivePlusMotorController.h"
////////////////////////////////////////////////////////
//! @ImsMDrivePlusMotorController()
//! Constructor
//! Driver assumes only 1 axis configured per controller for now...
//!
//! @param[in] motorPortName Name assigned to the port created to communicate with the motor
//! @param[in] IOPortName Name assigned to the asyn IO port, name that was assigned in drvAsynIPPortConfigure()
//! @param[in] devName Name of device (DN) assigned to motor axis in MCode, the device name is prepended to the MCode command to support Party Mode (PY) multidrop communication setup
//! set to empty string "" if no device name needed/not using Party Mode
//! @param[in] movingPollPeriod Moving polling period in milliseconds
//! @param[in] idlePollPeriod Idle polling period in milliseconds
////////////////////////////////////////////////////////
ImsMDrivePlusMotorController::ImsMDrivePlusMotorController(const char *motorPortName, const char *IOPortName, const char *devName, double movingPollPeriod, double idlePollPeriod)
: asynMotorController(motorPortName, NUM_AXES, NUM_IMS_PARAMS,
asynInt32Mask | asynFloat64Mask | asynUInt32DigitalMask,
asynInt32Mask | asynFloat64Mask | asynUInt32DigitalMask,
ASYN_CANBLOCK | ASYN_MULTIDEVICE,
1, // autoconnect
0, 0), // Default priority and stack size
pAsynUserIMS(0)
{
static const char *functionName = "ImsMDrivePlusMotorController()";
asynStatus status;
ImsMDrivePlusMotorAxis *pAxis;
// asynMotorController constructor calloc's memory for array of axis pointers
pAxes_ = (ImsMDrivePlusMotorAxis **)(asynMotorController::pAxes_);
// copy names
strncpy(motorName, motorPortName, (MAX_NAME_LEN - 1));
// setup communication
status = pasynOctetSyncIO->connect(IOPortName, 0, &pAsynUserIMS, NULL);
if (status != asynSuccess) {
printf("\n\n%s:%s: ERROR connecting to Controller's IO port=%s\n\n", DRIVER_NAME, functionName, IOPortName);
// TODO would be good to implement exceptions
// TODO THROW_(SmarActMCSException(MCSConnectionError, "SmarActMCSController: unable to connect serial channel"));
}
// write version, cannot use asynPrint() in constructor since controller (motorPortName) hasn't been created yet
printf("%s:%s: motorPortName=%s, IOPortName=%s, devName=%s \n", DRIVER_NAME, functionName, motorPortName, IOPortName, devName);
// init
pasynOctetSyncIO->setInputEos(pAsynUserIMS, "\n", 1);
pasynOctetSyncIO->setOutputEos(pAsynUserIMS, "\r\n", 2);
// Create controller-specific parameters
createParam(ImsMDrivePlusSaveToNVMControlString, asynParamInt32, &ImsMDrivePlusSaveToNVM_);
createParam(ImsMDrivePlusLoadMCodeControlString, asynParamOctet, &this->ImsMDrivePlusLoadMCode_);
createParam(ImsMDrivePlusClearMCodeControlString, asynParamOctet, &this->ImsMDrivePlusClearMCode_);
// Check the validity of the arguments and init controller object
initController(devName, movingPollPeriod, idlePollPeriod);
// Create axis
// Assuming single axis per controller the way drvAsynIPPortConfigure( "M06", "ts-b34-nw08:2101", 0, 0 0 ) is called in st.cmd script
pAxis = new ImsMDrivePlusMotorAxis(this, 0);
pAxis = NULL; // asynMotorController constructor tracking array of axis pointers
// read home and limit config from S1-S4
readHomeAndLimitConfig();
startPoller(movingPollPeriod, idlePollPeriod, 2);
}
////////////////////////////////////////
//! initController()
//! config controller variables - axis, etc.
//! only support single axis per controller
//
//! @param[in] devName Name of device (DN) used to identify axis within controller for Party Mode
//! @param[in] movingPollPeriod Moving polling period in milliseconds
//! @param[in] idlePollPeriod Idle polling period in milliseconds
////////////////////////////////////////
void ImsMDrivePlusMotorController::initController(const char *devName, double movingPollPeriod, double idlePollPeriod)
{
strncpy(this->deviceName, devName, (MAX_NAME_LEN - 1));
// initialize asynMotorController variables
this->numAxes_ = NUM_AXES; // only support single axis
this->movingPollPeriod_ = movingPollPeriod;
this->idlePollPeriod_ = idlePollPeriod;
// initialize switch inputs
this->homeSwitchInput=-1;
this->posLimitSwitchInput=-1;
this->negLimitSwitchInput=-1;
// flush io buffer
pasynOctetSyncIO->flush(pAsynUserIMS);
}
////////////////////////////////////////
//! readHomeAndLimitConfig
//! read home, positive limit, and neg limit switch configuration from MCode S1-S4 settings
//! S1-S4 must be set up beforehand
//! I1-I4 are used to read the status of S1-S4
// Use logic from existing drvMDrive.cc
////////////////////////////////////////
int ImsMDrivePlusMotorController::readHomeAndLimitConfig()
{
asynStatus status = asynError;
char cmd[MAX_CMD_LEN];
char resp[MAX_BUFF_LEN];
size_t nread;
static const char *functionName = "readHomeAndLimitConfig()";
int type;
// iterate through S1-S4 and parse each configuration to see if home, pos, and neg limits are set
for (int i=1; i<=4; i++) {
sprintf(cmd, "PR S%d", i); // query S1-S4 setting
status = this->writeReadController(cmd, resp, sizeof(resp), &nread, IMS_TIMEOUT);
sscanf(resp, "%d", &type);
//asynPrint(pasynUserSelf, ASYN_TRACEIO_DRIVER, "%s:%s: S%d: type=%d\n", DRIVER_NAME, functionName, i, type);
if (type != 0)
//printf("%s:%s: S%d: type=%d\n", DRIVER_NAME, functionName, i, type);
switch (type) {
case 0: break; // general purpose input
case 1: // home switch input
homeSwitchInput = i; break;
case 2: // positive limit switch input
posLimitSwitchInput = i; break;
case 3: // negative limit switch input
negLimitSwitchInput = i; break;
default:
printf("%s:%s: ERROR invalid data type for S%d=%d\n", DRIVER_NAME, functionName, i, type);
}
}
printf("homeSwitchInput=%d, posLimitSwitchInput=%d, negLimitSwitchInput=%d\n", homeSwitchInput, posLimitSwitchInput, negLimitSwitchInput);
return status;
}
////////////////////////////////////////
//! getAxis()
//! Override asynMotorController function to return pointer to IMS axis object
//
//! Returns a pointer to an ImsMDrivePlusAxis object.
//! Returns NULL if the axis number encoded in pasynUser is invalid
//
//! param[in] pasynUser asynUser structure that encodes the axis index number
////////////////////////////////////////
ImsMDrivePlusMotorAxis* ImsMDrivePlusMotorController::getAxis(asynUser *pasynUser)
{
int axisNo;
getAddress(pasynUser, &axisNo);
return getAxis(axisNo);
}
////////////////////////////////////////
//! getAxis()
//! Override asynMotorController function to return pointer to IMS axis object
//
//! Returns a pointer to an ImsMDrivePlusAxis object.
//! Returns NULL if the axis number is invalid.
//
//! param[in] axisNo Axis index number
////////////////////////////////////////
ImsMDrivePlusMotorAxis* ImsMDrivePlusMotorController::getAxis(int axisNo)
{
if ((axisNo < 0) || (axisNo >= numAxes_)) return NULL;
return pAxes_[axisNo];
}
////////////////////////////////////////
//! writeInt32()
//! Override asynMotorController function to add hooks to IMS records
// Based on XPSController.cpp
//
//! param[in] pointer to asynUser object
//! param[in] value to pass to function
////////////////////////////////////////
asynStatus ImsMDrivePlusMotorController::writeInt32(asynUser *pasynUser, epicsInt32 value)
{
int reason = pasynUser->reason;
int status = asynSuccess;
ImsMDrivePlusMotorAxis *pAxis;
static const char *functionName = "writeInt32";
pAxis = this->getAxis(pasynUser);
if (!pAxis) return asynError;
asynPrint(pasynUserSelf, ASYN_TRACEIO_DRIVER, "%s:%s: function=%s, val=%d\n", DRIVER_NAME, functionName, value);
// Set the parameter and readback in the parameter library. This may be overwritten when we read back the
// status at the end, but that's OK
status = pAxis->setIntegerParam(reason, value);
if (reason == ImsMDrivePlusSaveToNVM_) {
if (value == 1) { // save current user parameters to NVM
status = pAxis->saveToNVM();
if (status) asynPrint(pasynUserSelf, ASYN_TRACE_ERROR, "%s:%s: ERROR saving to NVM\n", DRIVER_NAME, functionName);
else asynPrint(pasynUserSelf, ASYN_TRACE_FLOW, "%s:%s: Successfully saved to NVM\n", DRIVER_NAME, functionName);
} else {
asynPrint(pasynUserSelf, ASYN_TRACE_ERROR, "%s:%s: ERROR, value of 1 to save to NVM\n", DRIVER_NAME, functionName);
}
} else { // call base class method to continue handling
status = asynMotorController::writeInt32(pasynUser, value);
}
callParamCallbacks(pAxis->axisNo_);
return (asynStatus)status;
}
////////////////////////////////////////
//! writeController()
//! reference ACRMotorDriver
//
//! Writes a string to the IMS controller.
//! Prepends deviceName to command string, if party mode not enabled, set device name to ""
//! @param[in] output the string to be written.
//! @param[in] timeout Timeout before returning an error.
////////////////////////////////////////
asynStatus ImsMDrivePlusMotorController::writeController(const char *output, double timeout)
{
size_t nwrite;
asynStatus status;
char outbuff[MAX_BUFF_LEN];
static const char *functionName = "writeController()";
// in party-mode Line Feed must follow command string
sprintf(outbuff, "%s%s", deviceName, output);
asynPrint(pasynUserSelf, ASYN_TRACEIO_DRIVER, "%s:%s: deviceName=%s, command=%s\n", DRIVER_NAME, functionName, deviceName, outbuff);
status = pasynOctetSyncIO->write(pAsynUserIMS, outbuff, strlen(outbuff), timeout, &nwrite);
if (status) { // update comm flag
setIntegerParam(this->motorStatusCommsError_, 1);
}
return status ;
}
////////////////////////////////////////
//! writeReadController()
//! reference ACRMotorDriver
//
//! Writes a string to the IMS controller and reads a response.
//! Prepends deviceName to command string, if party mode not enabled, set device name to ""
//! param[in] output Pointer to the output string.
//! param[out] input Pointer to the input string location.
//! param[in] maxChars Size of the input buffer.
//! param[out] nread Number of characters read.
//! param[out] timeout Timeout before returning an error.*/
////////////////////////////////////////
asynStatus ImsMDrivePlusMotorController::writeReadController(const char *output, char *input, size_t maxChars, size_t *nread, double timeout)
{
size_t nwrite;
asynStatus status;
int eomReason;
char outbuff[MAX_BUFF_LEN];
static const char *functionName = "writeReadController()";
// in party-mode Line Feed must follow command string
sprintf(outbuff, "%s%s", deviceName, output);
status = pasynOctetSyncIO->writeRead(pAsynUserIMS, outbuff, strlen(outbuff), input, maxChars, timeout, &nwrite, nread, &eomReason);
if (status) { // update comm flag
setIntegerParam(this->motorStatusCommsError_, 1);
}
asynPrint(pasynUserSelf, ASYN_TRACEIO_DRIVER, "%s:%s: deviceName=%s, command=%s, response=%s\n", DRIVER_NAME, functionName, deviceName, outbuff, input);
return status;
}
////////////////////////////////////////////////////////
// Start code for iocsh Registration :
// Available Functions :
// ImsMDrivePlusCreateController()
////////////////////////////////////////////////////////
////////////////////////////////////////////////////////
//! ImsMDrivePlusCreateController()
//! IOCSH function
//! Creates a new IMSMDrivePlusController object
//
//! Configuration command, called directly or from iocsh
//! @param[in] motorPortName User-specific name of motor port
//! @param[in] IOPortName User-specific name of port that was configured by drvAsynIPPortConfigure()
//! @param[in] deviceName Name of device, used to address motor by MCODE in party mode
// If not using party mode, config ImsMDrivePlusCreateController() with empty string "" for deviceName
//! @param[in] movingPollPeriod time in ms between polls when any axis is moving
//! @param[in] idlePollPeriod time in ms between polls when no axis is moving
////////////////////////////////////////////////////////
extern "C" int ImsMDrivePlusCreateController(const char *motorPortName, const char *IOPortName, char *devName, double movingPollPeriod, double idlePollPeriod)
{
ImsMDrivePlusMotorController *pImsController;
pImsController = new ImsMDrivePlusMotorController(motorPortName, IOPortName, devName, movingPollPeriod/1000., idlePollPeriod/1000.);
pImsController = NULL;
return(asynSuccess);
}
////////////////////////////////////////////////////////
// ImsMDrivePlus IOCSH Registration
// Copied from ACRMotorDriver.cpp
//
// Motor port name : user-specified name of port
// IO port name : user-specific name of port that was initialized with drvAsynIPPortConfigure()
// Device name : name of device, used to address motor by MCODE in party mode
// : if not using party mode, config ImsMDrivePlusCreateController() with empty string "" for deviceName
// Moving poll period : time in ms between polls when any axis is moving
// Idle poll period : time in ms between polls when no axis is moving
////////////////////////////////////////////////////////
static const iocshArg ImsMDrivePlusCreateControllerArg0 = {"Motor port name", iocshArgString};
static const iocshArg ImsMDrivePlusCreateControllerArg1 = {"IO port name", iocshArgString};
static const iocshArg ImsMDrivePlusCreateControllerArg2 = {"Device name", iocshArgString};
static const iocshArg ImsMDrivePlusCreateControllerArg3 = {"Moving poll period (ms)", iocshArgDouble};
static const iocshArg ImsMDrivePlusCreateControllerArg4 = {"Idle poll period (ms)", iocshArgDouble};
static const iocshArg * const ImsMDrivePlusCreateControllerArgs[] = {&ImsMDrivePlusCreateControllerArg0,
&ImsMDrivePlusCreateControllerArg1,
&ImsMDrivePlusCreateControllerArg2,
&ImsMDrivePlusCreateControllerArg3,
&ImsMDrivePlusCreateControllerArg4};
static const iocshFuncDef ImsMDrivePlusCreateControllerDef = {"ImsMDrivePlusCreateController", 5, ImsMDrivePlusCreateControllerArgs};
static void ImsMDrivePlusCreateControllerCallFunc(const iocshArgBuf *args)
{
ImsMDrivePlusCreateController(args[0].sval, args[1].sval, args[2].sval, args[3].dval, args[4].dval);
}
static void ImsMDrivePlusMotorRegister(void)
{
iocshRegister(&ImsMDrivePlusCreateControllerDef, ImsMDrivePlusCreateControllerCallFunc);
}
extern "C" {
epicsExportRegistrar(ImsMDrivePlusMotorRegister);
}
@@ -1,74 +0,0 @@
//! Description : This is the "model 3" asyn motor driver for IMS MDrivePlus.
//! Based on HytecMotorDriver.h.
//! @ImsMDrivePlus.h
#ifndef ImsMDrivePlusMotorController_H
#define ImsMDrivePlusMotorController_H
#include "asynMotorController.h"
#include "asynMotorAxis.h"
#include "ImsMDrivePlusMotorAxis.h"
////////////////////////////////////
// ImsMDrivePlusMotorController class
//! derived from asynMotorController class
////////////////////////////////////
class epicsShareClass ImsMDrivePlusMotorController : public asynMotorController
{
public:
/////////////////////////////////////////
// Override asynMotorController functions
/////////////////////////////////////////
ImsMDrivePlusMotorController(const char *motorPortName, const char *IOPortName, const char *deviceName, double movingPollPeriod, double idlePollPeriod);
ImsMDrivePlusMotorAxis* getAxis(asynUser *pasynUser);
ImsMDrivePlusMotorAxis* getAxis(int axisNo);
//void report(FILE *fp, int level);
asynStatus writeInt32(asynUser *pasynUser, epicsInt32 value);
/////////////////////////////////////////
// IMS specific functions
/////////////////////////////////////////
asynStatus writeReadController(const char *output, char *input, size_t maxChars, size_t *nread, double timeout);
asynStatus writeController(const char *output, double timeout);
protected:
ImsMDrivePlusMotorAxis **pAxes_; // Array of pointers to axis objects
///////////////////////////////////////////
// IMS MDrivePlus controller function codes
///////////////////////////////////////////
//! extra parameters that the ImsMDrivePlus controller supports
int ImsMDrivePlusLoadMCode_; //! Load MCode string, NOT SUPPORTED YET
int ImsMDrivePlusClearMCode_; //! Clear program buffer, NOT SUPPORTED YET
int ImsMDrivePlusSaveToNVM_; //! Store current user variables and flags to nonvolatile ram
#define FIRST_IMS_PARAM ImsMDrivePlusLoadMCode_
#define LAST_IMS_PARAM ImsMDrivePlusSaveToNVM_
#define NUM_IMS_PARAMS (&LAST_IMS_PARAM - &FIRST_IMS_PARAM + 1)
private:
// drvInfo strings for extra parameters that the ImsMDrivePlus controller supports
#define ImsMDrivePlusLoadMCodeControlString "IMS_LOADMCODE" // NOT SUPPORTED YET
#define ImsMDrivePlusClearMCodeControlString "IMS_CLEARMCODE" // NOT SUPPORTED YET
#define ImsMDrivePlusSaveToNVMControlString "IMS_SAVETONVM"
asynUser *pAsynUserIMS;
char motorName[MAX_NAME_LEN];
char deviceName[MAX_NAME_LEN];
int homeSwitchInput;
int posLimitSwitchInput;
int negLimitSwitchInput;
void initController(const char *devName, double movingPollPeriod, double idlePollPeriod);
int readHomeAndLimitConfig(); // read home, positive limit, and neg limit switch configuration from controller (S1-S4 settings)
friend class ImsMDrivePlusMotorAxis;
};
//! iocsh function to create controller object
//! NOTE: drvAsynIPPortConfigure() must be called first
//extern "C" int ImsMDrivePlusCreateController(const char *motorPortName, const char *IOPortName, char *devName, double movingPollPeriod, double idlePollPeriod);
#endif // ImsMDrivePlusMotorController_H
-90
View File
@@ -1,90 +0,0 @@
/*
FILENAME... ImsRegister.cc
USAGE... Register IMS motor device driver shell commands.
*/
/*****************************************************************
COPYRIGHT NOTIFICATION
*****************************************************************
(C) COPYRIGHT 1993 UNIVERSITY OF CHICAGO
This software was developed under a United States Government license
described on the COPYRIGHT_UniversityOfChicago file included as part
of this distribution.
**********************************************************************/
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <stdio.h>
#include <iocsh.h>
#include "motor.h"
#include "drvIM483.h"
#include "epicsExport.h"
extern "C"
{
// Ims Setup arguments
static const iocshArg setupArg0 = {"Max. controller count", iocshArgInt};
static const iocshArg setupArg1 = {"Polling rate", iocshArgInt};
// Ims Config arguments
static const iocshArg configArg0 = {"Card being configured", iocshArgInt};
static const iocshArg configArg1 = {"asyn port name", iocshArgString};
static const iocshArg * const IM483SetupArgs[2] = {&setupArg0, &setupArg1};
static const iocshArg * const IM483ConfigArgs[2] = {&configArg0, &configArg1};
static const iocshFuncDef setupIM483SM = {"IM483SMSetup", 2, IM483SetupArgs};
static const iocshFuncDef setupIM483PL = {"IM483PLSetup", 2, IM483SetupArgs};
static const iocshFuncDef setupMDrive = {"MDriveSetup", 2, IM483SetupArgs};
static const iocshFuncDef configIM483SM = {"IM483SMConfig", 2, IM483ConfigArgs};
static const iocshFuncDef configIM483PL = {"IM483PLConfig", 2, IM483ConfigArgs};
static const iocshFuncDef configMDrive = {"MDriveConfig", 2, IM483ConfigArgs};
static void setupSMCallFunc(const iocshArgBuf *args)
{
IM483SMSetup(args[0].ival, args[1].ival);
}
static void setupPLCallFunc(const iocshArgBuf *args)
{
IM483PLSetup(args[0].ival, args[1].ival);
}
static void setupMDriveCallFunc(const iocshArgBuf *args)
{
MDriveSetup(args[0].ival, args[1].ival);
}
static void configSMCallFunc(const iocshArgBuf *args)
{
IM483SMConfig(args[0].ival, args[1].sval);
}
static void configPLCallFunc(const iocshArgBuf *args)
{
IM483PLConfig(args[0].ival, args[1].sval);
}
static void configMDriveCallFunc(const iocshArgBuf *args)
{
MDriveConfig(args[0].ival, args[1].sval);
}
static void IMSmotorRegister(void)
{
iocshRegister(&setupIM483SM, setupSMCallFunc);
iocshRegister(&setupIM483PL, setupPLCallFunc);
iocshRegister(&setupMDrive, setupMDriveCallFunc);
iocshRegister(&configIM483SM, configSMCallFunc);
iocshRegister(&configIM483PL, configPLCallFunc);
iocshRegister(&configMDrive, configMDriveCallFunc);
}
epicsExportRegistrar(IMSmotorRegister);
} // extern "C"
-24
View File
@@ -1,24 +0,0 @@
# Makefile
TOP = ../..
include $(TOP)/configure/CONFIG
# The following are used for debugging messages.
#USR_CXXFLAGS += -DDEBUG
DBD += devImsMotor.dbd
LIBRARY_IOC = Ims
# Intelligent Motion Systems driver support.
SRCS += ImsRegister.cc
SRCS += devIM483SM.cc drvIM483SM.cc
SRCS += devIM483PL.cc drvIM483PL.cc
SRCS += devMDrive.cc drvMDrive.cc
# model3 driver
SRCS += ImsMDrivePlusMotorController.cpp ImsMDrivePlusMotorAxis.cpp
Ims_LIBS += motor asyn
Ims_LIBS += $(EPICS_BASE_IOC_LIBS)
include $(TOP)/configure/RULES
-151
View File
@@ -1,151 +0,0 @@
MDrive 17,23,34
==============================================================================
Configuring MDrive for party mode.
----------------------------------
The following is a modified version of the procedure defined in Section 2.1,
"Multiple MDrive Motion Control System (Party Mode)", page #27, of the
"MDrive Motion Control" manual; Revision 01.24.2005.
1. Connect the first MDrive Motion Control to the Host PC configured for Single
Mode Operation. Since the MDrive uses RS-485, a RS-232 to RS-485
converter is required for a Host PC to MDrive communication connection.
Set the PC Host RS-232 port characteristics to 9600 Baud, 8 Data bits,
1 Stop bit, No parity, No flow control.
2. Establish communications.
If you can see the sign-on message "Copyright 2001-2003 by Intelligent
Motion Systems, Inc.", then you are up and running. If the sign-on
message does not appear, try using a software reset. Hold down the
"Ctrl" key and press "C" (^C). If the sign-on message still does not
appear then there may be a problem with either the connections,
hardware or software configuration of the MDrive Motion Control or
Host.
3. Using the command DN, name the MDrive Motion Control. This must be a number
1-9. (DN="1"{enter}). Label the motor with the device name.
4. Define the plus, minus and home limit switches, if available, using the
S<1-4> command.
5. Set Echo Mode EM=2{enter}.
6. Set the party flag PY=1{enter}.
7. Type CTRL+J to activate the Party Mode.
8. Type {DN}S CTRL+J where {DN} is the device name given in step #3 (Save
device name, Echo mode, I/O Setup and Party Mode).
9. Remove power.
10. Repeat steps 1 through 8 for each additional MDrive in the system using
successive device names "1" through "9".
IMPORTANT NOTES
---------------
1. The EPICS motor record does NOT support the optional encoder mode. The
Encoder Enable Flag (EE) must be 0.
MDrive Facts
------------
* Default step resolution - 51,200 steps / 1 motor revolution (MS=256).
* Encoder resolution - 2048 ticks / 1 motor revolution.
IM483
==============================================================================
Configuring IM483's for Party Mode communication.
------------------------------------------------
The following is a modified version of the procedure defined in the
"Communication" section, page #9, of the "Software Reference Manual"; Revision
051794.
1) Power down the IM483's. Configure each IM483 module for stand alone (single
mode) communications. For the APS Diviero chassis, jumper J4 for Stand Alone
(1-2) and J7 for Master (1-2).
2) Connect an RS-232 serial connection (9600,none,8,1) from a host terminal to
the first ("A") controller's front panel connector.
3) Power up the IM483.
4) Assign the axis name. At the host terminal, enter the letter "A" (0x41)
followed by a space charter (0x20).
5) The IM483 controller should respond by echoing the "A" character and the
sign-on message at the host terminal, i.e.,
A 1342 4038 ADVANCED MICRO SYSTEMS, INC
MAX-2000 v1.15i
6) Change the "Limit Polarity" to active high. At the host terminal, enter
"l1<enter>". The IM483 should echo your input at the host terminal.
7) Store parameters. At the host terminal, enter "S<enter>".
8) Move RS-232 connection to next controller. At the host terminal, enter
control c (^c). Repeat steps 4 through 7 while incrementing the axis name
(i.e, A, B, C, etc.).
9) Set all the IM483's for Party Mode communication. For the APS Diviero
chassis, configure all of the IM483's for Party Mode by setting J4 to (2-3).
Connect the RS-232 port to the Master IM483 and set all the remaining IM483's
to Slave mode by setting J7 to (2-3).
SSCAN THROUGH-PUT NOTES
-----------------------
The following results where done using the following;
- SSCAN support module R2.5.1, with scan.db configured as follows;
- Read(R1PV) set to "time".
- Drive(P1PV) set to motor record VAL field.
- #PTS(NPTS) set to 1000.
- Step Size(P1SI) set to MRES of the motor record.
- Motor record R5.7; configured as follows;
- Set slew velocity (VELO) 5.0 = (500 * MRES).
- Set base velocity (VBAS) 4.9 = (499 * MRES).
- Acceleration time (ACCL) does not matter.
- EPICS base R3.14.7
- WRS Tornado 2.2.1
- MVME5100
- IM483SM with firmware version; 1342 4038 AMS, INC MAX-2000 v1.15i
Controller | sysClkRate | polling rate | ms/scan pt.
-----------|------------|--------------|------------
IMS483SM | 60 | 1HZ | 121.0
-----------|------------|--------------|------------
IMS483SM | 60 | 1HZ | 834.2 with VBAS/VELO removed
-----------|------------|--------------|------------
IMS483SM | 60 | 10HZ | 121.0
-----------|------------|--------------|------------
IMS483SM | 60 | 10HZ | 154.4 with VBAS/VELO removed
-----------|------------|--------------|------------
IMS483SM | 60 | 60HZ | 121.0
-----------|------------|--------------|------------
IMS483SM | 60 | 60HZ | 102.9 with VBAS/VELO removed
-----------|------------|--------------|------------
IMS483SM | 100 | 1HZ | 121.0
-----------|------------|--------------|------------
IMS483SM | 1000 | 1000HZ | 121.0
-----------|------------|--------------|------------
IMS483SM | 1000 | 1000HZ | 96.0 with VBAS/VELO removed
KNOWN PROBLEMS
==============
- The MDrive overshoots the target position on small moves when the velocity
base is large.
-120
View File
@@ -1,120 +0,0 @@
=======================================
ImsMDrivePlusMotor AsynPortDriver Notes
Author: Nia Fong, SLAC National Laboratory
First Initial: 10/28/2011
This driver is based on "model 3" motor asyn port driver defined by Mark Rivers.
The driver assumes:
1) each controller is configured for a single axis
2) the motor is set up for Party Mode (PY=1) and Echo Mode 2 (EM=2)
=======================================
=======================
Building an Application
=======================
Building the driver into an application requires
1) The 'asyn', 'motor' and 'ImsMDrivePlusMotor' (this driver) packages/modules to be built and
installed. The application's RELEASE file must point to these packages so that the build process
locates headers and libraries etc.
2) The application's '.dbd' file must contain
- motorSupport.dbd
- drvAsynIPPort.dbd (or equivalent 'asyn' port driver package)
- devImsMotor.dbd (this include support for all Ims drivers)
These '.dbd' files are best listed in the application src/Makefile:
<app>_DBD += motorSupport.dbd
<app>_DBD += devImsMotor.dbd
<app>_DBD += drvAsynIPPort.dbd
3) The application must be linked against the 'Ims', 'motor' and 'asyn'
libraries, e.g.,
<app>_LIBS += Ims motor asyn
===============
Custom Database
===============
There is an IMS specific IMS_extra.db database that contains IMS-specific records.
IMS_S: saves user variables and flags to controller's NVM
record(bo,"$(DEV):$(AREA):$(LOC):IMS_S") {
field(DESC, "Ims Save to NVM")
field(PINI, "NO")
field(DTYP, "asynInt32")
field(OUT, "@asyn($(PORT),$(ADDR),$(TIMEOUT))IMS_SAVETONVM")
field(VAL, "0")
field(ZNAM, "No")
field(ONAM, "Yes")
}
=============================
Driver Run-Time Configuration
=============================
To configure the motor from the iocsh or a C/C++ application
1) Create and configure an asyn IP port
drvAsynIPPortConfigure(IOPortName, port, priority, disable auto-connect, no process EOS)
motorPortName: name string assigned to the asyn IP port being created
port: host:port [protocol]
2) Create and configure an ImsMDrivePlus controller
ImsMDrivePlusCreateController(motorPortName, IOPortName, devName, movingPollPeriod, idlePollPeriod)
motorPortName: name string assigned to the controller
IOPortName: name of asyn IO port that was created by drvAsynIPPortConfigure()
devName: unique device name used to address the motor in MCode
NOTE: the device name is prepended to all MCode commands to communicate in Party Mode.
See README file for more information.
movingPollPeriod: time in milliseconds between polls when axis is moving
idlePollPeriod: time in milliseconds between polls when axis is not moving
=========================
Example iocsh st.cmd file
=========================
#!../../bin/linux-x86/ImsMDrivePlus
# Register all support components
dbLoadDatabase("dbd/ImsMDrivePlus.dbd")
ImsMDrivePlus_registerRecordDeviceDriver pdbbase
# Motors substitutions, customize this for your motor
dbLoadTemplate "db/motor.mdriveplus.substitutions"
# Configure asyn communication port, first
drvAsynIPPortConfigure("M06", "ts-b34-nw08:2101", 0, 0, 0 )
# Configure one controller per motor, each controller just has 1 axis
ImsMDrivePlusCreateController("IMS1", "M06", "1", 200, 5000)
# Optional: Enable tracing
asynSetTraceIOMask("IMS1",0,2)
asynSetTraceMask("IMS1",0,2)
=============================================
Example application database substitions file
=============================================
Db/motor.mdrive.substitutions file:
file "$(MOTOR)/db/basic_asyn_motor.db"
{
pattern
{P, N, M, DTYP, PORT, DESC, ADDR, EGU, DIR, VELO, VBAS, ACCL, BDST, BVEL, BACC, DHLM, DLLM, MRES, PREC, INIT}
{IMS:, 1, 1, "asynMotor", IMS1, "MDrive23", 0, mm, Pos, 3.0, 0.017, 2.0, 0, 0.017, 2.0, 100, -100, 0.000083, 4, ""}
}
file "$(MOTOR)/db/IMS_extra.db"
{
pattern
{DEV, AREA, LOC, PORT, ADDR, TIMEOUT}
{ims, IOC, 1, IMS1, 0, 1}
-315
View File
@@ -1,315 +0,0 @@
/*
FILENAME... devIM483PL.cc
USAGE... Motor record device level support for Intelligent Motion
Systems, Inc. IM483(I/IE).
*/
/*
* Original Author: Ron Sluiter
* Date: 07/10/2000
*
* Experimental Physics and Industrial Control System (EPICS)
*
* Copyright 1991, the Regents of the University of California,
* and the University of Chicago Board of Governors.
*
* This software was produced under U.S. Government contracts:
* (W-7405-ENG-36) at the Los Alamos National Laboratory,
* and (W-31-109-ENG-38) at Argonne National Laboratory.
*
* Initial development by:
* The Controls and Automation Group (AT-8)
* Ground Test Accelerator
* Accelerator Technology Division
* Los Alamos National Laboratory
*
* Co-developed with
* The Controls and Computing Group
* Accelerator Systems Division
* Advanced Photon Source
* Argonne National Laboratory
*
* Modification Log:
* -----------------
* .01 07/10/00 rls copied from devIM483SM.c
* .02 05/16/01 rls Added support for changing jog velocity while jogging.
* .03 03/01/02 rls eliminated "ASCII record separator (IS2) = /x1E".
* .04 04/15/02 rls Must support PRIMITIVE in build_trans() for INIT field to
* work, and add axis name place holder (?) to message.
* .05 03/07/03 rls R3.14 conversion.
*/
#include <string.h>
#include <errlog.h>
#include "motorRecord.h"
#include "motor.h"
#include "motordevCom.h"
#include "drvIM483.h"
#include "epicsExport.h"
#define STATIC static
extern struct driver_table IM483PL_access;
/* ----------------Create the dsets for devIM483PL----------------- */
STATIC struct driver_table *drvtabptr;
STATIC long IM483PL_init(int);
STATIC long IM483PL_init_record(void *);
STATIC long IM483PL_start_trans(struct motorRecord *);
STATIC RTN_STATUS IM483PL_build_trans(motor_cmnd, double *, struct motorRecord *);
STATIC RTN_STATUS IM483PL_end_trans(struct motorRecord *);
struct motor_dset devIM483PL =
{
{8, NULL, (DEVSUPFUN) IM483PL_init, (DEVSUPFUN) IM483PL_init_record, NULL},
motor_update_values,
IM483PL_start_trans,
IM483PL_build_trans,
IM483PL_end_trans
};
extern "C" {epicsExportAddress(dset,devIM483PL);}
/* --------------------------- program data --------------------- */
/* This table is used to define the command types */
/* WARNING! this must match "motor_cmnd" in motor.h */
static msg_types IM483PL_table[] = {
MOTION, /* MOVE_ABS */
MOTION, /* MOVE_REL */
MOTION, /* HOME_FOR */
MOTION, /* HOME_REV */
IMMEDIATE, /* LOAD_POS */
IMMEDIATE, /* SET_VEL_BASE */
IMMEDIATE, /* SET_VELOCITY */
IMMEDIATE, /* SET_ACCEL */
IMMEDIATE, /* GO */
IMMEDIATE, /* SET_ENC_RATIO */
INFO, /* GET_INFO */
MOVE_TERM, /* STOP_AXIS */
VELOCITY, /* JOG */
IMMEDIATE, /* SET_PGAIN */
IMMEDIATE, /* SET_IGAIN */
IMMEDIATE, /* SET_DGAIN */
IMMEDIATE, /* ENABLE_TORQUE */
IMMEDIATE, /* DISABL_TORQUE */
IMMEDIATE, /* PRIMITIVE */
IMMEDIATE, /* SET_HIGH_LIMIT */
IMMEDIATE, /* SET_LOW_LIMIT */
VELOCITY /* JOG_VELOCITY */
};
static struct board_stat **IM483PL_cards;
/* --------------------------- program data --------------------- */
/* initialize device support for IM483PL stepper motor */
STATIC long IM483PL_init(int after)
{
long rtnval;
if (!after)
{
drvtabptr = &IM483PL_access;
(drvtabptr->init)();
}
rtnval = motor_init_com(after, *drvtabptr->cardcnt_ptr, drvtabptr, &IM483PL_cards);
return(rtnval);
}
/* initialize a record instance */
STATIC long IM483PL_init_record(void *arg)
{
struct motorRecord *mr = (struct motorRecord *) arg;
return(motor_init_record_com(mr, *drvtabptr->cardcnt_ptr, drvtabptr, IM483PL_cards));
}
/* start building a transaction */
STATIC long IM483PL_start_trans(struct motorRecord *mr)
{
return(OK);
}
/* end building a transaction */
STATIC RTN_STATUS IM483PL_end_trans(struct motorRecord *mr)
{
return(OK);
}
/* add a part to the transaction */
STATIC RTN_STATUS IM483PL_build_trans(motor_cmnd command, double *parms, struct motorRecord *mr)
{
struct motor_trans *trans = (struct motor_trans *) mr->dpvt;
struct mess_node *motor_call;
struct controller *brdptr;
struct IM483controller *cntrl;
char buff[110];
int axis, card, maxdigits;
unsigned int size;
double dval, cntrl_units;
RTN_STATUS rtnval;
bool send;
send = true; /* Default to send motor command. */
rtnval = OK;
buff[0] = '\0';
/* Protect against NULL pointer with WRTITE_MSG(GO/STOP_AXIS/GET_INFO, NULL). */
dval = (parms == NULL) ? 0.0 : *parms;
motor_start_trans_com(mr, IM483PL_cards);
motor_call = &(trans->motor_call);
card = motor_call->card;
axis = motor_call->signal + 1;
brdptr = (*trans->tabptr->card_array)[card];
if (brdptr == NULL)
return(rtnval = ERROR);
cntrl = (struct IM483controller *) brdptr->DevicePrivate;
cntrl_units = dval;
maxdigits = 2;
if (IM483PL_table[command] > motor_call->type)
motor_call->type = IM483PL_table[command];
if (trans->state != BUILD_STATE)
return(rtnval = ERROR);
if (command == PRIMITIVE && mr->init != NULL && strlen(mr->init) != 0)
{
strcat(motor_call->message, "? ");
strcat(motor_call->message, mr->init);
}
switch (command)
{
case MOVE_ABS:
case MOVE_REL:
case HOME_FOR:
case HOME_REV:
case JOG:
if (strlen(mr->prem) != 0)
{
strcat(motor_call->message, mr->prem);
strcat(motor_call->message, ";");
}
if (strlen(mr->post) != 0)
motor_call->postmsgptr = (char *) &mr->post;
break;
default:
break;
}
switch (command)
{
case MOVE_ABS:
sprintf(buff, " R%.*f", maxdigits, cntrl_units);
break;
case MOVE_REL:
sprintf(buff, " %+.*f", maxdigits, cntrl_units);
break;
case HOME_FOR:
sprintf(buff, " F1000 0");
break;
case HOME_REV:
sprintf(buff, " F1000 1");
break;
case LOAD_POS:
if (cntrl_units == 0.0)
sprintf(buff, " O");
else
{
send = false;
rtnval = ERROR;
}
break;
case SET_VEL_BASE:
sprintf(buff, " I%.*f;", maxdigits, cntrl_units);
break;
case SET_VELOCITY:
sprintf(buff, " V%.*f;", maxdigits, cntrl_units);
break;
case SET_ACCEL:
/* ????? MAKE SENSE OF ACCELERATION PARAMETER ??????*/
send = false;
break;
case GO:
/* The IM483PL starts moving immediately on move commands, GO command
* does nothing. */
send = false;
break;
case PRIMITIVE:
case GET_INFO:
/* These commands are not actually done by sending a message, but
rather they will indirectly cause the driver to read the status
of all motors */
break;
case STOP_AXIS:
sprintf(buff, " @ 0");
break;
case JOG_VELOCITY:
case JOG:
sprintf(buff, " M%.*f;", maxdigits, cntrl_units);
break;
case SET_PGAIN:
case SET_IGAIN:
case SET_DGAIN:
send = false;
break;
case ENABLE_TORQUE:
sprintf(buff, " MO;");
break;
case DISABL_TORQUE:
sprintf(buff, " MF;");
break;
case SET_HIGH_LIMIT:
case SET_LOW_LIMIT:
case SET_ENC_RATIO:
trans->state = IDLE_STATE; /* No command sent to the controller. */
send = false;
break;
default:
send = false;
rtnval = ERROR;
}
size = strlen(buff);
if (send == false)
return(rtnval);
else if (size > sizeof(buff) || (strlen(motor_call->message) + size) > MAX_MSG_SIZE)
errlogMessage("IM483PL_build_trans(): buffer overflow.\n");
else
{
strcat(motor_call->message, buff);
motor_end_trans_com(mr, drvtabptr);
}
return(rtnval);
}
-312
View File
@@ -1,312 +0,0 @@
/*
FILENAME... devIM483SM.cc
USAGE... Motor record device level support for Intelligent Motion
Systems, Inc. IM483(I/IE).
*/
/*
* Original Author: Ron Sluiter
* Date: 02/10/2000
*
* Experimental Physics and Industrial Control System (EPICS)
*
* Copyright 1991, the Regents of the University of California,
* and the University of Chicago Board of Governors.
*
* This software was produced under U.S. Government contracts:
* (W-7405-ENG-36) at the Los Alamos National Laboratory,
* and (W-31-109-ENG-38) at Argonne National Laboratory.
*
* Initial development by:
* The Controls and Automation Group (AT-8)
* Ground Test Accelerator
* Accelerator Technology Division
* Los Alamos National Laboratory
*
* Co-developed with
* The Controls and Computing Group
* Accelerator Systems Division
* Advanced Photon Source
* Argonne National Laboratory
*
* Modification Log:
* -----------------
* .01 02/10/00 rls copied from devMM4000.c
* .02 05/16/01 rls Added support for changing jog velocity while jogging.
* .03 03/01/02 rls eliminated "ASCII record separator (IS2) = /x1E".
* .04 04/15/02 rls Must support PRIMITIVE in build_trans() for INIT field to
* work.
* .05 03/07/03 rls R3.14 conversion.
*/
#include <string.h>
#include <errlog.h>
#include "motorRecord.h"
#include "motor.h"
#include "motordevCom.h"
#include "drvIM483.h"
#include "epicsExport.h"
#define STATIC static
extern struct driver_table IM483SM_access;
/* ----------------Create the dsets for devIM483SM----------------- */
STATIC struct driver_table *drvtabptr;
STATIC long IM483SM_init(int);
STATIC long IM483SM_init_record(void *);
STATIC long IM483SM_start_trans(struct motorRecord *);
STATIC RTN_STATUS IM483SM_build_trans(motor_cmnd, double *, struct motorRecord *);
STATIC RTN_STATUS IM483SM_end_trans(struct motorRecord *);
struct motor_dset devIM483SM =
{
{8, NULL, (DEVSUPFUN) IM483SM_init, (DEVSUPFUN) IM483SM_init_record, NULL},
motor_update_values,
IM483SM_start_trans,
IM483SM_build_trans,
IM483SM_end_trans
};
extern "C" {epicsExportAddress(dset,devIM483SM);}
/* --------------------------- program data --------------------- */
/* This table is used to define the command types */
/* WARNING! this must match "motor_cmnd" in motor.h */
static msg_types IM483SM_table[] = {
MOTION, /* MOVE_ABS */
MOTION, /* MOVE_REL */
MOTION, /* HOME_FOR */
MOTION, /* HOME_REV */
IMMEDIATE, /* LOAD_POS */
IMMEDIATE, /* SET_VEL_BASE */
IMMEDIATE, /* SET_VELOCITY */
IMMEDIATE, /* SET_ACCEL */
IMMEDIATE, /* GO */
IMMEDIATE, /* SET_ENC_RATIO */
INFO, /* GET_INFO */
MOVE_TERM, /* STOP_AXIS */
VELOCITY, /* JOG */
IMMEDIATE, /* SET_PGAIN */
IMMEDIATE, /* SET_IGAIN */
IMMEDIATE, /* SET_DGAIN */
IMMEDIATE, /* ENABLE_TORQUE */
IMMEDIATE, /* DISABL_TORQUE */
IMMEDIATE, /* PRIMITIVE */
IMMEDIATE, /* SET_HIGH_LIMIT */
IMMEDIATE, /* SET_LOW_LIMIT */
VELOCITY /* JOG_VELOCITY */
};
static struct board_stat **IM483SM_cards;
/* --------------------------- program data --------------------- */
/* initialize device support for IM483SM stepper motor */
STATIC long IM483SM_init(int after)
{
long rtnval;
if (!after)
{
drvtabptr = &IM483SM_access;
(drvtabptr->init)();
}
rtnval = motor_init_com(after, *drvtabptr->cardcnt_ptr, drvtabptr, &IM483SM_cards);
return(rtnval);
}
/* initialize a record instance */
STATIC long IM483SM_init_record(void *arg)
{
struct motorRecord *mr = (struct motorRecord *) arg;
return(motor_init_record_com(mr, *drvtabptr->cardcnt_ptr, drvtabptr, IM483SM_cards));
}
/* start building a transaction */
STATIC long IM483SM_start_trans(struct motorRecord *mr)
{
return(OK);
}
/* end building a transaction */
STATIC RTN_STATUS IM483SM_end_trans(struct motorRecord *mr)
{
return(OK);
}
/* add a part to the transaction */
STATIC RTN_STATUS IM483SM_build_trans(motor_cmnd command, double *parms, struct motorRecord *mr)
{
struct motor_trans *trans = (struct motor_trans *) mr->dpvt;
struct mess_node *motor_call;
struct controller *brdptr;
struct IM483controller *cntrl;
char buff[110];
int axis, card, maxdigits;
unsigned int size;
double dval, cntrl_units;
RTN_STATUS rtnval;
bool send;
send = true; /* Default to send motor command. */
rtnval = OK;
buff[0] = '\0';
/* Protect against NULL pointer with WRTITE_MSG(GO/STOP_AXIS/GET_INFO, NULL). */
dval = (parms == NULL) ? 0.0 : *parms;
motor_start_trans_com(mr, IM483SM_cards);
motor_call = &(trans->motor_call);
card = motor_call->card;
axis = motor_call->signal + 1;
brdptr = (*trans->tabptr->card_array)[card];
if (brdptr == NULL)
return(rtnval = ERROR);
cntrl = (struct IM483controller *) brdptr->DevicePrivate;
cntrl_units = dval;
maxdigits = 2;
if (IM483SM_table[command] > motor_call->type)
motor_call->type = IM483SM_table[command];
if (trans->state != BUILD_STATE)
return(rtnval = ERROR);
if (command == PRIMITIVE && mr->init != NULL && strlen(mr->init) != 0)
strcat(motor_call->message, mr->init);
switch (command)
{
case MOVE_ABS:
case MOVE_REL:
case HOME_FOR:
case HOME_REV:
case JOG:
if (strlen(mr->prem) != 0)
{
strcat(motor_call->message, mr->prem);
strcat(motor_call->message, ";");
}
if (strlen(mr->post) != 0)
motor_call->postmsgptr = (char *) &mr->post;
break;
default:
break;
}
switch (command)
{
case MOVE_ABS:
sprintf(buff, "R%.*f", maxdigits, cntrl_units);
break;
case MOVE_REL:
sprintf(buff, "%+.*f", maxdigits, cntrl_units);
break;
case HOME_FOR:
sprintf(buff, "F1000 0");
break;
case HOME_REV:
sprintf(buff, "F1000 1");
break;
case LOAD_POS:
if (cntrl_units == 0.0)
sprintf(buff, "O");
else
{
send = false;
rtnval = ERROR;
}
break;
case SET_VEL_BASE:
sprintf(buff, "I%.*f;", maxdigits, cntrl_units);
break;
case SET_VELOCITY:
sprintf(buff, "V%.*f;", maxdigits, cntrl_units);
break;
case SET_ACCEL:
/* ????? MAKE SENSE OF ACCELERATION PARAMETER ??????*/
send = false;
break;
case GO:
/* The IM483 starts moving immediately on move commands, GO command
* does nothing. */
send = false;
break;
case PRIMITIVE:
case GET_INFO:
/* These commands are not actually done by sending a message, but
rather they will indirectly cause the driver to read the status
of all motors */
break;
case STOP_AXIS:
sprintf(buff, "@ 0");
break;
case JOG_VELOCITY:
case JOG:
sprintf(buff, "M%.*f;", maxdigits, cntrl_units);
break;
case SET_PGAIN:
case SET_IGAIN:
case SET_DGAIN:
send = false;
break;
case ENABLE_TORQUE:
sprintf(buff, "MO;");
break;
case DISABL_TORQUE:
sprintf(buff, "MF;");
break;
case SET_HIGH_LIMIT:
case SET_LOW_LIMIT:
case SET_ENC_RATIO:
trans->state = IDLE_STATE; /* No command sent to the controller. */
send = false;
break;
default:
send = false;
rtnval = ERROR;
}
size = strlen(buff);
if (send == false)
return(rtnval);
else if (size > sizeof(buff) || (strlen(motor_call->message) + size) > MAX_MSG_SIZE)
errlogMessage("IM483SM_build_trans(): buffer overflow.\n");
else
{
strcat(motor_call->message, buff);
motor_end_trans_com(mr, drvtabptr);
}
return(rtnval);
}
-12
View File
@@ -1,12 +0,0 @@
# Intelligent Motion Systems driver support.
device(motor,VME_IO,devIM483SM,"IM483SM")
device(motor,VME_IO,devIM483PL,"IM483PL")
device(motor,VME_IO,devMDrive, "MDrive")
driver(drvIM483SM)
driver(drvIM483PL)
driver(drvMDrive)
registrar(IMSmotorRegister)
registrar(ImsMDrivePlusMotorRegister)
variable(drvIM483SMdebug)
variable(drvIM483PLdebug)
variable(drvMDrivedebug)
-327
View File
@@ -1,327 +0,0 @@
/*
FILENAME... devMDrive.cc
USAGE... Motor record device level support for Intelligent Motion
Systems, Inc. MDrive series of controllers.
*/
/*
* Original Author: Ron Sluiter
* Date: 03/21/03
*
* Experimental Physics and Industrial Control System (EPICS)
*
* Copyright 1991, the Regents of the University of California,
* and the University of Chicago Board of Governors.
*
* This software was produced under U.S. Government contracts:
* (W-7405-ENG-36) at the Los Alamos National Laboratory,
* and (W-31-109-ENG-38) at Argonne National Laboratory.
*
* Initial development by:
* The Controls and Automation Group (AT-8)
* Ground Test Accelerator
* Accelerator Technology Division
* Los Alamos National Laboratory
*
* Co-developed with
* The Controls and Computing Group
* Accelerator Systems Division
* Advanced Photon Source
* Argonne National Laboratory
*
* Modification Log:
* -----------------
* .01 03/21/03 rls copied from devIM483PL.c
* .02 05/15/03 rls R3.14 compatible.
* .03 03/15/04 rls bug fix for LOAD_POS command with encoder.
* .04 03/16/04 rls Protect against NULL "parms" argument in
* MDrive_build_trans().
* .05 09/20/04 rls remove '?' command string padding.
* .06 04/06/05 rls Bug fix for not setting accel = decel.
* .07 01/28/11 jps MDrive Release 3.x requires an '=' on velocity commands.
*/
#include <string.h>
#include <errlog.h>
#include "motorRecord.h"
#include "motor.h"
#include "motordevCom.h"
#include "drvIM483.h"
#include "epicsExport.h"
#define STATIC static
extern struct driver_table MDrive_access;
/* ----------------Create the dsets for devMDrive----------------- */
STATIC struct driver_table *drvtabptr;
STATIC long MDrive_init(int);
STATIC long MDrive_init_record(void *);
STATIC long MDrive_start_trans(struct motorRecord *);
STATIC RTN_STATUS MDrive_build_trans(motor_cmnd, double *, struct motorRecord *);
STATIC RTN_STATUS MDrive_end_trans(struct motorRecord *);
struct motor_dset devMDrive =
{
{8, NULL, (DEVSUPFUN) MDrive_init, (DEVSUPFUN) MDrive_init_record, NULL},
motor_update_values,
MDrive_start_trans,
MDrive_build_trans,
MDrive_end_trans
};
extern "C" {epicsExportAddress(dset,devMDrive);}
/* --------------------------- program data --------------------- */
/* This table is used to define the command types */
/* WARNING! this must match "motor_cmnd" in motor.h */
static msg_types MDrive_table[] = {
MOTION, /* MOVE_ABS */
MOTION, /* MOVE_REL */
MOTION, /* HOME_FOR */
MOTION, /* HOME_REV */
IMMEDIATE, /* LOAD_POS */
IMMEDIATE, /* SET_VEL_BASE */
IMMEDIATE, /* SET_VELOCITY */
IMMEDIATE, /* SET_ACCEL */
IMMEDIATE, /* GO */
IMMEDIATE, /* SET_ENC_RATIO */
INFO, /* GET_INFO */
MOVE_TERM, /* STOP_AXIS */
VELOCITY, /* JOG */
IMMEDIATE, /* SET_PGAIN */
IMMEDIATE, /* SET_IGAIN */
IMMEDIATE, /* SET_DGAIN */
IMMEDIATE, /* ENABLE_TORQUE */
IMMEDIATE, /* DISABL_TORQUE */
IMMEDIATE, /* PRIMITIVE */
IMMEDIATE, /* SET_HIGH_LIMIT */
IMMEDIATE, /* SET_LOW_LIMIT */
VELOCITY /* JOG_VELOCITY */
};
static struct board_stat **MDrive_cards;
/* --------------------------- program data --------------------- */
/* initialize device support for MDrive stepper motor */
STATIC long MDrive_init(int after)
{
long rtnval;
if (!after)
{
drvtabptr = &MDrive_access;
(drvtabptr->init)();
}
rtnval = motor_init_com(after, *drvtabptr->cardcnt_ptr, drvtabptr, &MDrive_cards);
return(rtnval);
}
/* initialize a record instance */
STATIC long MDrive_init_record(void *arg)
{
struct motorRecord *mr = (struct motorRecord *) arg;
return(motor_init_record_com(mr, *drvtabptr->cardcnt_ptr, drvtabptr, MDrive_cards));
}
/* start building a transaction */
STATIC long MDrive_start_trans(struct motorRecord *mr)
{
return(OK);
}
/* end building a transaction */
STATIC RTN_STATUS MDrive_end_trans(struct motorRecord *mr)
{
return(OK);
}
/* add a part to the transaction */
STATIC RTN_STATUS MDrive_build_trans(motor_cmnd command, double *parms, struct motorRecord *mr)
{
struct motor_trans *trans = (struct motor_trans *) mr->dpvt;
struct mess_node *motor_call;
struct controller *brdptr;
struct IM483controller *cntrl;
char buff[110];
int axis, card, intval;
unsigned int size;
RTN_STATUS rtnval;
bool send;
msta_field msta;
send = true; /* Default to send motor command. */
rtnval = OK;
buff[0] = '\0';
/* Protect against NULL pointer with WRTITE_MSG(GO/STOP_AXIS/GET_INFO, NULL). */
intval = (parms == NULL) ? 0 : NINT(parms[0]);
msta.All = mr->msta;
motor_start_trans_com(mr, MDrive_cards);
motor_call = &(trans->motor_call);
card = motor_call->card;
axis = motor_call->signal + 1;
brdptr = (*trans->tabptr->card_array)[card];
if (brdptr == NULL)
return(rtnval = ERROR);
cntrl = (struct IM483controller *) brdptr->DevicePrivate;
if (MDrive_table[command] > motor_call->type)
motor_call->type = MDrive_table[command];
if (trans->state != BUILD_STATE)
return(rtnval = ERROR);
if (command == PRIMITIVE && mr->init != NULL && strlen(mr->init) != 0)
{
strcat(motor_call->message, " ");
strcat(motor_call->message, mr->init);
}
switch (command)
{
case MOVE_ABS:
case MOVE_REL:
case HOME_FOR:
case HOME_REV:
case JOG:
if (strlen(mr->prem) != 0)
{
strcat(motor_call->message, mr->prem);
strcat(motor_call->message, " ");
}
if (strlen(mr->post) != 0)
motor_call->postmsgptr = (char *) &mr->post;
break;
default:
break;
}
switch (command)
{
case MOVE_ABS:
sprintf(buff, "MA %d", intval);
break;
case MOVE_REL:
sprintf(buff, "MR %d", intval);
break;
case HOME_FOR:
sprintf(buff, " F1000 0");
break;
case HOME_REV:
sprintf(buff, " F1000 1");
break;
case LOAD_POS:
sprintf(buff, "P=%d", intval);
if (msta.Bits.EA_PRESENT == 1)
{
/* Finish 1st message; MDrive can only handle one msg. */
strcpy(motor_call->message, buff);
rtnval = motor_end_trans_com(mr, drvtabptr);
rtnval = (RTN_STATUS) motor_start_trans_com(mr, MDrive_cards);
intval = NINT(mr->dval / mr->eres);
sprintf(buff, "C2=%d", intval);
motor_call->type = MDrive_table[command];
}
break;
case SET_VEL_BASE:
sprintf(buff, "VI=%d", intval);
break;
case SET_VELOCITY:
sprintf(buff, "VM=%d", intval);
break;
case SET_ACCEL:
sprintf(buff, "A=%d", intval);
strcpy(motor_call->message, buff);
rtnval = motor_end_trans_com(mr, drvtabptr);
rtnval = (RTN_STATUS) motor_start_trans_com(mr, MDrive_cards);
sprintf(buff, "D=A");
motor_call->type = MDrive_table[command];
break;
case GO:
/* The MDrive starts moving immediately on move commands, GO command
* does nothing. */
send = false;
break;
case PRIMITIVE:
case GET_INFO:
/* These commands are not actually done by sending a message, but
rather they will indirectly cause the driver to read the status
of all motors */
break;
case STOP_AXIS:
sprintf(buff, "SL 0");
break;
case JOG_VELOCITY:
case JOG:
sprintf(buff, "SL=%d", intval);
break;
case SET_PGAIN:
case SET_IGAIN:
case SET_DGAIN:
send = false;
break;
case ENABLE_TORQUE:
sprintf(buff, "DE=1");
break;
case DISABL_TORQUE:
sprintf(buff, "DE=0");
break;
case SET_HIGH_LIMIT:
case SET_LOW_LIMIT:
case SET_ENC_RATIO:
trans->state = IDLE_STATE; /* No command sent to the controller. */
send = false;
break;
default:
send = false;
rtnval = ERROR;
}
size = strlen(buff);
if (send == false)
return(rtnval);
else if (size > sizeof(buff) || (strlen(motor_call->message) + size) > MAX_MSG_SIZE)
errlogMessage("MDrive_build_trans(): buffer overflow.\n");
else
{
strcat(motor_call->message, buff);
motor_end_trans_com(mr, drvtabptr);
}
return(rtnval);
}
-78
View File
@@ -1,78 +0,0 @@
/*
FILENAME... drvIM483.h
USAGE... This file contains driver "include" information that is specific to
Intelligent Motion Systems, Inc. IM483(I/IE) and MDrive controllers.
*/
/*
* Original Author: Ron Sluiter
* Date: 02/10/2000
*
* Experimental Physics and Industrial Control System (EPICS)
*
* Copyright 1991, the Regents of the University of California,
* and the University of Chicago Board of Governors.
*
* This software was produced under U.S. Government contracts:
* (W-7405-ENG-36) at the Los Alamos National Laboratory,
* and (W-31-109-ENG-38) at Argonne National Laboratory.
*
* Initial development by:
* The Controls and Automation Group (AT-8)
* Ground Test Accelerator
* Accelerator Technology Division
* Los Alamos National Laboratory
*
* Co-developed with
* The Controls and Computing Group
* Accelerator Systems Division
* Advanced Photon Source
* Argonne National Laboratory
*
*
*
* Modification Log:
* -----------------
* .01 02/10/2000 rls copied from drvMM4000.h
* .02 07/01/2004 rls Converted from MPF to asyn.
* .03 03/18/2005 rls Added MDrive input configuration structure.
*/
#ifndef INCdrvIM483h
#define INCdrvIM483h 1
#include "motordrvCom.h"
#include "asynDriver.h"
#include "asynOctetSyncIO.h"
#define COMM_TIMEOUT 2 /* Timeout in seconds */
/* MDrive input configuration - 0 indicates unassigned. */
typedef struct inputc
{
epicsUInt8 plusLS; /* Input # of + limit switch. */
epicsUInt8 minusLS; /* Input # of - limit switch. */
epicsUInt8 homeLS; /* Input # of home switch. */
} input_config;
/* IMS specific data is stored in this structure. */
struct IM483controller
{
asynUser *pasynUser; /* For RS-232 */
char asyn_port[80]; /* asyn port name */
CommStatus status; /* Controller communication status. */
input_config *inconfig; /* Discrete input configuration - MDrive only. */
};
/* Function prototypes. */
extern RTN_STATUS IM483SMSetup(int, int);
extern RTN_STATUS IM483PLSetup(int, int);
extern RTN_STATUS MDriveSetup(int, int);
extern RTN_STATUS IM483SMConfig(int, const char *);
extern RTN_STATUS IM483PLConfig(int, const char *);
extern RTN_STATUS MDriveConfig(int, const char *);
#endif /* INCdrvIM483h */
-622
View File
@@ -1,622 +0,0 @@
/*
FILENAME... drvIM483PL.cc
USAGE... Motor record driver level support for Intelligent Motion
Systems, Inc. IM483(I/IE).
*/
/*****************************************************************
COPYRIGHT NOTIFICATION
*****************************************************************
(C) COPYRIGHT 1993 UNIVERSITY OF CHICAGO
This software was developed under a United States Government license
described on the COPYRIGHT_UniversityOfChicago file included as part
of this distribution.
**********************************************************************/
/*
* Original Author: Ron Sluiter
* Date: 07/10/2000
*
* Modification Log:
* -----------------
* .01 07/10/00 rls copied from drvIM483SM.c
* .02 10/02/01 rls allow one retry after a communication error.
* .03 04/15/02 rls Bug fix for limit switches. Set RA_DIRECTION in
* set_status() based on (new - old) commanded position.
* Removed support for "ASCII record separator (IS2) = /x1E"
* from send_mess().
* .04 03/07/03 rls R3.14 conversion.
* .05 02/03/04 rls Eliminate erroneous "Motor motion timeout ERROR".
* .06 07/01/04 rls Converted from MPF to asyn.
* .07 09/20/04 rls - increase BUFF_SIZE; response was exceeding 13 characters.
* - support for 32axes/controller.
* - remove '?' command line padding.
* .08 12/14/04 rls - asyn R4.0 support.
* - make debug variables always available.
* - MS Visual C compatibility; make all epicsExportAddress
* extern "C" linkage.
* - retry on initial communication.
*/
/*
DESIGN LIMITATIONS...
1 - Like all controllers, the IM483 must be powered-on when EPICS is first
booted up.
2 - The IM483 cannot be power cycled while EPICS is up and running. The
consequences are permanent communication lose with the IM483 until
EPICS is rebooted.
3 - Like the Newport MM3000, the IM483's position can only be set to zero.
4 - The IM483 uses an internal look-up table for acceleration/deceleration.
Translation between the IM483 and the ACCL/BACC fields is not obvious.
*/
#include <string.h>
#include <epicsThread.h>
#include <drvSup.h>
#include <stdlib.h>
#include <errlog.h>
#include "motor.h"
#include "drvIM483.h"
#include "asynOctetSyncIO.h"
#include "epicsExport.h"
/* Read Limit Status response values. */
#define L_ALIMIT 1
#define L_BLIMIT 2
#define L_BOTH_LIMITS 3
#define IM483PL_NUM_CARDS 8
#define MAX_AXES 8
#define BUFF_SIZE 50 /* Maximum length of string to/from IM483PL */
/*----------------debugging-----------------*/
volatile int drvIM483PLdebug = 0;
extern "C" {epicsExportAddress(int, drvIM483PLdebug);}
static inline void Debug(int level, const char *format, ...) {
#ifdef DEBUG
if (level < drvIM483PLdebug) {
va_list pVar;
va_start(pVar, format);
vprintf(format, pVar);
va_end(pVar);
}
#endif
}
/* --- Local data. --- */
int IM483PL_num_cards = 0;
static char *IM483PL_axis[] = {"A", "B", "C", "D", "E", "F", "G", "H"};
/* Local data required for every driver; see "motordrvComCode.h" */
#include "motordrvComCode.h"
/*----------------functions-----------------*/
static int recv_mess(int, char *, int);
static RTN_STATUS send_mess(int, char const *, char *);
static int set_status(int, int);
static long report(int);
static long init();
static int motor_init();
static void query_done(int, int, struct mess_node *);
/*----------------functions-----------------*/
struct driver_table IM483PL_access =
{
motor_init,
motor_send,
motor_free,
motor_card_info,
motor_axis_info,
&mess_queue,
&queue_lock,
&free_list,
&freelist_lock,
&motor_sem,
&motor_state,
&total_cards,
&any_motor_in_motion,
send_mess,
recv_mess,
set_status,
query_done,
NULL,
&initialized,
IM483PL_axis
};
struct
{
long number;
long (*report) (int);
long (*init) (void);
} drvIM483PL = {2, report, init};
extern "C" {epicsExportAddress(drvet, drvIM483PL);}
static struct thread_args targs = {SCAN_RATE, &IM483PL_access, 0.0};
/*********************************************************
* Print out driver status report
*********************************************************/
static long report(int level)
{
int card;
if (IM483PL_num_cards <=0)
printf(" No IM483PL controllers configured.\n");
else
{
for (card = 0; card < IM483PL_num_cards; card++)
{
struct controller *brdptr = motor_state[card];
if (brdptr == NULL)
printf(" IM483PL controller %d connection failed.\n", card);
else
{
struct IM483controller *cntrl;
cntrl = (struct IM483controller *) brdptr->DevicePrivate;
printf(" IM483PL controller #%d, port=%s, id: %s \n", card,
cntrl->asyn_port, brdptr->ident);
}
}
}
return(OK);
}
static long init()
{
/*
* We cannot call motor_init() here, because that function can do GPIB I/O,
* and hence requires that the drvGPIB have already been initialized.
* That cannot be guaranteed, so we need to call motor_init from device
* support
*/
/* Check for setup */
if (IM483PL_num_cards <= 0)
{
Debug(1, "init(): IM483PL driver disabled. IM483PLSetup() missing from startup script.\n");
}
return((long) 0);
}
static void query_done(int card, int axis, struct mess_node *nodeptr)
{
}
/********************************************************************************
* *
* FUNCTION NAME: set_status *
* *
* LOGIC: *
* Initialize. *
* Send "Moving Status" query. *
* Read response. *
* IF normal response to query. *
* Set communication status to NORMAL. *
* ELSE *
* IF communication status is NORMAL. *
* Set communication status to RETRY. *
* NORMAL EXIT. *
* ELSE *
* Set communication status error. *
* ERROR EXIT. *
* ENDIF *
* ENDIF *
* *
* IF "Moving Status" indicates any motion (i.e. status != 0). *
* Clear "Done Moving" status bit. *
* ELSE *
* Set "Done Moving" status bit. *
* ENDIF *
* *
* *
********************************************************************************/
static int set_status(int card, int signal)
{
struct IM483controller *cntrl;
struct mess_node *nodeptr;
register struct mess_info *motor_info;
/* Message parsing variables */
char buff[BUFF_SIZE];
int rtnval, rtn_state;
double motorData;
bool plusdir, ls_active = false;
msta_field status;
cntrl = (struct IM483controller *) motor_state[card]->DevicePrivate;
motor_info = &(motor_state[card]->motor_info[signal]);
nodeptr = motor_info->motor_motion;
status.All = motor_info->status.All;
send_mess(card, " ^", IM483PL_axis[signal]);
rtn_state = recv_mess(card, buff, 1);
if (rtn_state > 0)
{
cntrl->status = NORMAL;
status.Bits.CNTRL_COMM_ERR = 0;
}
else
{
if (cntrl->status == NORMAL)
{
cntrl->status = RETRY;
rtn_state = 0;
goto exit;
}
else
{
cntrl->status = COMM_ERR;
status.Bits.CNTRL_COMM_ERR = 1;
status.Bits.RA_PROBLEM = 1;
rtn_state = 1;
goto exit;
}
}
rtnval = atoi(&buff[4]);
status.Bits.RA_DONE = (rtnval != 0) ? 0 : 1;
/*
* Parse motor position
* Position string format: 1TP5.012,2TP1.123,3TP-100.567,...
* Skip to substring for this motor, convert to double
*/
send_mess(card, " Z 0", IM483PL_axis[signal]);
recv_mess(card, buff, 1);
motorData = atof(&buff[5]);
if (motorData == motor_info->position)
{
if (nodeptr != 0) /* Increment counter only if motor is moving. */
motor_info->no_motion_count++;
}
else
{
epicsInt32 newposition;
newposition = NINT(motorData);
status.Bits.RA_DIRECTION = (newposition >= motor_info->position) ? 1 : 0;
motor_info->position = newposition;
motor_info->no_motion_count = 0;
}
plusdir = (status.Bits.RA_DIRECTION) ? true : false;
send_mess(card, " ] 0", IM483PL_axis[signal]);
recv_mess(card, buff, 1);
rtnval = atoi(&buff[5]);
/* Set limit switch error indicators. */
if (rtnval & 1)
{
status.Bits.RA_PLUS_LS = 1;
if (plusdir == true)
ls_active = true;
}
else
status.Bits.RA_PLUS_LS = 0;
if (rtnval & 2)
{
status.Bits.RA_MINUS_LS = 1;
if (plusdir == false)
ls_active = true;
}
else
status.Bits.RA_MINUS_LS = 0;
send_mess(card, " ] 1", IM483PL_axis[signal]);
recv_mess(card, buff, 1);
rtnval = buff[5];
status.Bits.RA_HOME = (rtnval & 0x01) ? 1 : 0;
/* !!! Assume no closed-looped control!!!*/
status.Bits.EA_POSITION = 0;
/* encoder status */
status.Bits.EA_SLIP = 0;
status.Bits.EA_SLIP_STALL = 0;
status.Bits.EA_HOME = 0;
if (motor_state[card]->motor_info[signal].encoder_present == NO)
motor_info->encoder_position = 0;
else
{
send_mess(card, " z 0", IM483PL_axis[signal]);
recv_mess(card, buff, 1);
motorData = atof(&buff[5]);
motor_info->encoder_position = (epicsInt32) motorData;
}
status.Bits.RA_PROBLEM = 0;
/* Parse motor velocity? */
/* NEEDS WORK */
motor_info->velocity = 0;
if (!status.Bits.RA_DIRECTION)
motor_info->velocity *= -1;
rtn_state = (!motor_info->no_motion_count || ls_active == true ||
status.Bits.RA_DONE | status.Bits.RA_PROBLEM) ? 1 : 0;
/* Test for post-move string. */
if ((status.Bits.RA_DONE || ls_active == true) && nodeptr != 0 && nodeptr->postmsgptr != 0)
{
strcpy(buff, nodeptr->postmsgptr);
send_mess(card, buff, IM483PL_axis[signal]);
nodeptr->postmsgptr = NULL;
}
exit:
motor_info->status.All = status.All;
return(rtn_state);
}
/*****************************************************/
/* send a message to the IM483PL board */
/* send_mess() */
/*****************************************************/
static RTN_STATUS send_mess(int card, char const *com, char *name)
{
char local_buff[MAX_MSG_SIZE];
struct IM483controller *cntrl;
int comsize, namesize;
size_t nwrite;
comsize = (com == NULL) ? 0 : strlen(com);
namesize = (name == NULL) ? 0 : strlen(name);
if ((comsize + namesize) > MAX_MSG_SIZE)
{
errlogMessage("drvIM483PL.c:send_mess(); message size violation.\n");
return(ERROR);
}
else if (comsize == 0) /* Normal exit on empty input message. */
return(OK);
if (!motor_state[card])
{
errlogPrintf("drvIM483PL.c:send_mess() - invalid card #%d\n", card);
return(ERROR);
}
/* Make a local copy of the string and add the command line terminator. */
if (namesize != 0)
{
strcpy(local_buff, name); /* put in axis */
strcat(local_buff, com);
}
else
strcpy(local_buff, com);
Debug(2, "send_mess(): message = %s\n", local_buff);
cntrl = (struct IM483controller *) motor_state[card]->DevicePrivate;
pasynOctetSyncIO->write(cntrl->pasynUser, local_buff, strlen(local_buff), COMM_TIMEOUT, &nwrite);
return(OK);
}
/*****************************************************/
/* receive a message from the IM483 board */
/* recv_mess() */
/*****************************************************/
static int recv_mess(int card, char *com, int flag)
{
struct IM483controller *cntrl;
size_t nread = 0;
asynStatus status = asynError;
int eomReason;
/* Check that card exists */
if (!motor_state[card])
return(ERROR);
cntrl = (struct IM483controller *) motor_state[card]->DevicePrivate;
if (flag == FLUSH)
pasynOctetSyncIO->flush(cntrl->pasynUser);
else
status = pasynOctetSyncIO->read(cntrl->pasynUser, com, BUFF_SIZE, COMM_TIMEOUT, &nread, &eomReason);
if ((status != asynSuccess) || (nread <= 0))
{
com[0] = '\0';
nread = 0;
}
Debug(2, "recv_mess(): message = \"%s\"\n", com);
return(nread);
}
/*****************************************************/
/* Setup system configuration */
/* IM483PLSetup() */
/*****************************************************/
RTN_STATUS
IM483PLSetup(int num_cards, /* maximum number of controllers in system. */
int scan_rate) /* polling rate - 1/60 sec units. */
{
int itera;
if (num_cards < 1 || num_cards > IM483PL_NUM_CARDS)
IM483PL_num_cards = IM483PL_NUM_CARDS;
else
IM483PL_num_cards = num_cards;
/* Set motor polling task rate */
if (scan_rate >= 1 && scan_rate <= 60)
targs.motor_scan_rate = scan_rate;
else
targs.motor_scan_rate = SCAN_RATE;
/*
* Allocate space for motor_state structures. Note this must be done
* before IM483Config is called, so it cannot be done in motor_init()
* This means that we must allocate space for a card without knowing
* if it really exists, which is not a serious problem
*/
motor_state = (struct controller **) malloc(IM483PL_num_cards * sizeof(struct controller *));
for (itera = 0; itera < IM483PL_num_cards; itera++)
motor_state[itera] = (struct controller *) NULL;
return(OK);
}
/*****************************************************/
/* Configure a controller */
/* IM483PLConfig() */
/*****************************************************/
RTN_STATUS
IM483PLConfig(int card, /* card being configured */
const char *name) /* asyn server task name */
{
struct IM483controller *cntrl;
if (card < 0 || card >= IM483PL_num_cards)
return(ERROR);
motor_state[card] = (struct controller *) malloc(sizeof(struct controller));
motor_state[card]->DevicePrivate = malloc(sizeof(struct IM483controller));
cntrl = (struct IM483controller *) motor_state[card]->DevicePrivate;
strcpy(cntrl->asyn_port, name);
return(OK);
}
/*****************************************************/
/* initialize all software and hardware */
/* This is called from the initialization routine in */
/* device support. */
/* motor_init() */
/*****************************************************/
static int motor_init()
{
struct controller *brdptr;
struct IM483controller *cntrl;
int card_index, motor_index;
char buff[BUFF_SIZE];
int total_axis = 0;
int status;
asynStatus success_rtn;
static const char output_terminator[] = "\n";
static const char input_terminator[] = "\n";
initialized = true; /* Indicate that driver is initialized. */
/* Check for setup */
if (IM483PL_num_cards <= 0)
return(ERROR);
for (card_index = 0; card_index < IM483PL_num_cards; card_index++)
{
if (!motor_state[card_index])
continue;
brdptr = motor_state[card_index];
brdptr->ident[0] = (char) NULL; /* No controller identification message. */
brdptr->cmnd_response = true;
total_cards = card_index + 1;
cntrl = (struct IM483controller *) brdptr->DevicePrivate;
/* Initialize communications channel */
success_rtn = pasynOctetSyncIO->connect(cntrl->asyn_port, 0, &cntrl->pasynUser, NULL);
if (success_rtn == asynSuccess)
{
pasynOctetSyncIO->setOutputEos(cntrl->pasynUser, output_terminator, strlen(output_terminator));
pasynOctetSyncIO->setInputEos(cntrl->pasynUser, input_terminator, strlen(input_terminator));
/* Send a message to the board, see if it exists */
/* flush any junk at input port - should not be any data available */
pasynOctetSyncIO->flush(cntrl->pasynUser);
for (total_axis = 0; total_axis < MAX_AXES; total_axis++)
{
int retry = 0;
do
{
send_mess(card_index, " Z 0", IM483PL_axis[total_axis]);
status = recv_mess(card_index, buff, 1);
retry++;
} while (status <= 0 && retry < 3);
if (status <= 0)
break;
}
brdptr->total_axis = total_axis;
}
if (success_rtn == asynSuccess && total_axis > 0)
{
brdptr->localaddr = (char *) NULL;
brdptr->motor_in_motion = 0;
for (motor_index = 0; motor_index < total_axis; motor_index++)
{
struct mess_info *motor_info = &brdptr->motor_info[motor_index];
int loop_state;
motor_info->status.All = 0;
motor_info->no_motion_count = 0;
motor_info->encoder_position = 0;
motor_info->position = 0;
brdptr->motor_info[motor_index].motor_motion = NULL;
/* Assume encoder support, i.e., IM483IE. */
motor_info->encoder_present = YES;
motor_info->status.Bits.EA_PRESENT = 1;
/* Determine if encoder present based on open/closed loop mode. */
loop_state = 0;
if (loop_state != 0)
{
motor_info->pid_present = YES;
motor_info->status.Bits.GAIN_SUPPORT = 1;
}
set_status(card_index, motor_index); /* Read status of each motor */
}
}
else
motor_state[card_index] = (struct controller *) NULL;
}
any_motor_in_motion = 0;
mess_queue.head = (struct mess_node *) NULL;
mess_queue.tail = (struct mess_node *) NULL;
free_list.head = (struct mess_node *) NULL;
free_list.tail = (struct mess_node *) NULL;
epicsThreadCreate((char *) "IM483PL_motor", epicsThreadPriorityMedium,
epicsThreadGetStackSize(epicsThreadStackMedium),
(EPICSTHREADFUNC) motor_task, (void *) &targs);
return(OK);
}
-654
View File
@@ -1,654 +0,0 @@
/*
FILENAME... drvIM483SM.cc
USAGE... Motor record driver level support for Intelligent Motion
Systems, Inc. IM483(I/IE).
*/
/*****************************************************************
COPYRIGHT NOTIFICATION
*****************************************************************
(C) COPYRIGHT 1993 UNIVERSITY OF CHICAGO
This software was developed under a United States Government license
described on the COPYRIGHT_UniversityOfChicago file included as part
of this distribution.
**********************************************************************/
/*
* Original Author: Ron Sluiter
* Date: 02/10/2000
*
* Modification Log:
* -----------------
* .01 02/10/00 rls copied from drvMM4000.c
* .02 10/02/01 rls allow one retry after a communication error.
* .03 04/15/02 rls Bug fix for limit switches. Set RA_DIRECTION in
* set_status() based on (new - old) commanded position.
* Removed support for "ASCII record separator (IS2) = /x1E"
* from send_mess().
* .04 03/07/03 rls R3.14 conversion.
* .05 02/03/04 rls Eliminate erroneous "Motor motion timeout ERROR".
* .06 07/01/04 rls Converted from MPF to asyn.
* .07 09/20/04 rls support for 32axes/controller.
* .08 12/14/04 rls - asyn R4.0 support.
* - make debug variables always available.
* - MS Visual C compatibility; make all epicsExportAddress
* extern "C" linkage.
* - retry on initial communication.
*/
/*
DESIGN LIMITATIONS...
1 - Like all controllers, the IM483 must be powered-on when EPICS is first
booted up.
2 - The IM483 cannot be power cycled while EPICS is up and running. The
consequences are permanent communication lose with the IM483 until
EPICS is rebooted.
3 - Like the Newport MM3000, the IM483's position can only be set to zero.
4 - The IM483 uses an internal look-up table for acceleration/deceleration.
Translation between the IM483 and the ACCL/BACC fields is not obvious.
*/
#include <string.h>
#include <ctype.h>
#include <epicsThread.h>
#include <drvSup.h>
#include <stdlib.h>
#include <errlog.h>
#include "motor.h"
#include "drvIM483.h"
#include "asynOctetSyncIO.h"
#include "epicsExport.h"
/* Read Limit Status response values. */
#define L_ALIMIT 1
#define L_BLIMIT 2
#define L_BOTH_LIMITS 3
#define IM483SM_NUM_CARDS 8
#define BUFF_SIZE 50 /* Maximum length of string to/from IM483 */
/*----------------debugging-----------------*/
volatile int drvIM483SMdebug = 0;
extern "C" {epicsExportAddress(int, drvIM483SMdebug);}
static inline void Debug(int level, const char *format, ...) {
#ifdef DEBUG
if (level < drvIM483SMdebug) {
va_list pVar;
va_start(pVar, format);
vprintf(format, pVar);
va_end(pVar);
}
#endif
}
/* --- Local data. --- */
int IM483SM_num_cards = 0;
/* Local data required for every driver; see "motordrvComCode.h" */
#include "motordrvComCode.h"
/*----------------functions-----------------*/
static int recv_mess(int, char *, int);
static RTN_STATUS send_mess(int, char const *, char *);
static int set_status(int, int);
static long report(int);
static long init();
static int motor_init();
static void query_done(int, int, struct mess_node *);
/*----------------functions-----------------*/
struct driver_table IM483SM_access =
{
motor_init,
motor_send,
motor_free,
motor_card_info,
motor_axis_info,
&mess_queue,
&queue_lock,
&free_list,
&freelist_lock,
&motor_sem,
&motor_state,
&total_cards,
&any_motor_in_motion,
send_mess,
recv_mess,
set_status,
query_done,
NULL,
&initialized,
NULL
};
struct
{
long number;
long (*report) (int);
long (*init) (void);
} drvIM483SM = {2, report, init};
extern "C" {epicsExportAddress(drvet, drvIM483SM);}
static struct thread_args targs = {SCAN_RATE, &IM483SM_access, 0.0};
/*********************************************************
* Print out driver status report
*********************************************************/
static long report(int level)
{
int card;
if (IM483SM_num_cards <=0)
printf(" No IM483SM controllers configured.\n");
else
{
for (card = 0; card < IM483SM_num_cards; card++)
{
struct controller *brdptr = motor_state[card];
if (brdptr == NULL)
printf(" IM483SM controller %d connection failed.\n", card);
else
{
struct IM483controller *cntrl;
cntrl = (struct IM483controller *) brdptr->DevicePrivate;
printf(" IM483SM controller #%d, port=%s, id: %s \n", card,
cntrl->asyn_port, brdptr->ident);
}
}
}
return(OK);
}
static long init()
{
/*
* We cannot call motor_init() here, because that function can do GPIB I/O,
* and hence requires that the drvGPIB have already been initialized.
* That cannot be guaranteed, so we need to call motor_init from device
* support
*/
/* Check for setup */
if (IM483SM_num_cards <= 0)
{
Debug(1, "init(): IM483SM driver disabled. IM483SMSetup() missing from startup script.\n");
}
return((long) 0);
}
static void query_done(int card, int axis, struct mess_node *nodeptr)
{
}
/********************************************************************************
* *
* FUNCTION NAME: set_status *
* *
* LOGIC: *
* Initialize. *
* Send "Moving Status" query. *
* Read response. *
* IF normal response to query. *
* Set communication status to NORMAL. *
* ELSE *
* IF communication status is NORMAL. *
* Set communication status to RETRY. *
* NORMAL EXIT. *
* ELSE *
* Set communication status error. *
* ERROR EXIT. *
* ENDIF *
* ENDIF *
* *
* IF "Moving Status" indicates any motion (i.e. status != 0). *
* Clear "Done Moving" status bit. *
* ELSE *
* Set "Done Moving" status bit. *
* ENDIF *
* *
* *
********************************************************************************/
static int set_status(int card, int signal)
{
struct IM483controller *cntrl;
struct mess_node *nodeptr;
register struct mess_info *motor_info;
/* Message parsing variables */
char buff[BUFF_SIZE];
int rtnval, rtn_state;
double motorData;
bool plusdir, ls_active = false;
msta_field status;
cntrl = (struct IM483controller *) motor_state[card]->DevicePrivate;
motor_info = &(motor_state[card]->motor_info[signal]);
nodeptr = motor_info->motor_motion;
status.All = motor_info->status.All;
send_mess(card, "^", (char*) NULL);
rtn_state = recv_mess(card, buff, 1);
if (rtn_state > 0)
{
cntrl->status = NORMAL;
status.Bits.CNTRL_COMM_ERR = 0;
}
else
{
if (cntrl->status == NORMAL)
{
cntrl->status = RETRY;
rtn_state = 0;
goto exit;
}
else
{
cntrl->status = COMM_ERR;
status.Bits.CNTRL_COMM_ERR = 1;
status.Bits.RA_PROBLEM = 1;
rtn_state = 1;
goto exit;
}
}
rtnval = atoi(&buff[4]);
status.Bits.RA_DONE = (rtnval != 0) ? 0 : 1;
/*
* Parse motor position
* Position string format: 1TP5.012,2TP1.123,3TP-100.567,...
* Skip to substring for this motor, convert to double
*/
send_mess(card, "Z 0", (char*) NULL);
recv_mess(card, buff, 1);
motorData = atof(&buff[5]);
if (motorData == motor_info->position)
{
if (nodeptr != 0) /* Increment counter only if motor is moving. */
motor_info->no_motion_count++;
}
else
{
epicsInt32 newposition;
newposition = NINT(motorData);
status.Bits.RA_DIRECTION = (newposition >= motor_info->position) ? 1 : 0;
motor_info->position = newposition;
motor_info->no_motion_count = 0;
}
plusdir = (status.Bits.RA_DIRECTION) ? true : false;
send_mess(card, "] 0", (char*) NULL);
recv_mess(card, buff, 1);
rtnval = atoi(&buff[5]);
/* Set limit switch error indicators. */
if (rtnval & 1)
{
status.Bits.RA_PLUS_LS = 1;
if (plusdir == true)
ls_active = true;
}
else
status.Bits.RA_PLUS_LS = 0;
if (rtnval & 2)
{
status.Bits.RA_MINUS_LS = 1;
if (plusdir == false)
ls_active = true;
}
else
status.Bits.RA_MINUS_LS = 0;
send_mess(card, "] 1", (char*) NULL);
recv_mess(card, buff, 1);
rtnval = buff[5];
status.Bits.RA_HOME = (rtnval & 0x01) ? 1 : 0;
/* !!! Assume no closed-looped control!!!*/
status.Bits.EA_POSITION = 0;
/* encoder status */
status.Bits.EA_SLIP = 0;
status.Bits.EA_SLIP_STALL = 0;
status.Bits.EA_HOME = 0;
if (motor_state[card]->motor_info[signal].encoder_present == NO)
motor_info->encoder_position = 0;
else
{
send_mess(card, "z 0", (char*) NULL);
recv_mess(card, buff, 1);
motorData = atof(&buff[5]);
motor_info->encoder_position = (epicsInt32) motorData;
}
status.Bits.RA_PROBLEM = 0;
/* Parse motor velocity? */
/* NEEDS WORK */
motor_info->velocity = 0;
if (!status.Bits.RA_DIRECTION)
motor_info->velocity *= -1;
rtn_state = (!motor_info->no_motion_count || ls_active == true || status.Bits.RA_DONE | status.Bits.RA_PROBLEM) ? 1 : 0;
/* Test for post-move string. */
if ((status.Bits.RA_DONE || ls_active == true) && nodeptr != 0 &&
nodeptr->postmsgptr != 0)
{
strcpy(buff, nodeptr->postmsgptr);
send_mess(card, buff, (char*) NULL);
nodeptr->postmsgptr = NULL;
}
exit:
motor_info->status.All = status.All;
return(rtn_state);
}
/*****************************************************/
/* send a message to the IM483SM board */
/* send_mess() */
/*****************************************************/
static RTN_STATUS send_mess(int card, char const *com, char *name)
{
struct IM483controller *cntrl;
int size;
size_t nwrite;
size = strlen(com);
if (size > MAX_MSG_SIZE)
{
errlogMessage("drvIM483SM.c:send_mess(); message size violation.\n");
return(ERROR);
}
else if (size == 0) /* Normal exit on empty input message. */
return(OK);
if (!motor_state[card])
{
errlogPrintf("drvIM483SM.c:send_mess() - invalid card #%d\n", card);
return(ERROR);
}
if (name != NULL)
{
errlogPrintf("drvIM483SM.c:send_mess() - invalid argument = %s\n", name);
return(ERROR);
}
Debug(2, "send_mess(): message = %s\n", com);
cntrl = (struct IM483controller *) motor_state[card]->DevicePrivate;
pasynOctetSyncIO->write(cntrl->pasynUser, com, size, COMM_TIMEOUT, &nwrite);
return(OK);
}
/*****************************************************/
/* receive a message from the IM483 board */
/* recv_mess() */
/*****************************************************/
static int recv_mess(int card, char *com, int flag)
{
struct IM483controller *cntrl;
size_t nread = 0;
asynStatus status = asynError;
int eomReason;
/* Check that card exists */
if (!motor_state[card])
return(ERROR);
cntrl = (struct IM483controller *) motor_state[card]->DevicePrivate;
if (flag == FLUSH)
pasynOctetSyncIO->flush(cntrl->pasynUser);
else
status = pasynOctetSyncIO->read(cntrl->pasynUser, com, BUFF_SIZE, COMM_TIMEOUT, &nread, &eomReason);
if ((status != asynSuccess) || (nread <= 0))
{
com[0] = '\0';
nread = 0;
}
Debug(2, "recv_mess(): message = \"%s\"\n", com);
return(nread);
}
/*****************************************************/
/* Setup system configuration */
/* IM483SMSetup() */
/*****************************************************/
RTN_STATUS
IM483SMSetup(int num_cards, /* maximum number of controllers in system. */
int scan_rate) /* polling rate - 1/60 sec units. */
{
int itera;
if (num_cards < 1 || num_cards > IM483SM_NUM_CARDS)
IM483SM_num_cards = IM483SM_NUM_CARDS;
else
IM483SM_num_cards = num_cards;
/* Set motor polling task rate */
if (scan_rate >= 1 && scan_rate <= 60)
targs.motor_scan_rate = scan_rate;
else
targs.motor_scan_rate = SCAN_RATE;
/*
* Allocate space for motor_state structures. Note this must be done
* before IM483SMConfig is called, so it cannot be done in motor_init()
* This means that we must allocate space for a card without knowing
* if it really exists, which is not a serious problem
*/
motor_state = (struct controller **) malloc(IM483SM_num_cards * sizeof(struct controller *));
for (itera = 0; itera < IM483SM_num_cards; itera++)
motor_state[itera] = (struct controller *) NULL;
return(OK);
}
/*****************************************************/
/* Configure a controller */
/* IM483SMConfig() */
/*****************************************************/
RTN_STATUS
IM483SMConfig(int card, /* card being configured */
const char *name) /* asyn server task name */
{
struct IM483controller *cntrl;
if (card < 0 || card >= IM483SM_num_cards)
return(ERROR);
motor_state[card] = (struct controller *) malloc(sizeof(struct controller));
motor_state[card]->DevicePrivate = malloc(sizeof(struct IM483controller));
cntrl = (struct IM483controller *) motor_state[card]->DevicePrivate;
strcpy(cntrl->asyn_port, name);
return(OK);
}
/*****************************************************/
/* initialize all software and hardware */
/* This is called from the initialization routine in */
/* device support. */
/* motor_init() */
/*****************************************************/
static int motor_init()
{
struct controller *brdptr;
struct IM483controller *cntrl;
int card_index, motor_index;
char buff[BUFF_SIZE];
int total_axis = 0;
int status = 0;
asynStatus success_rtn;
static const char output_terminator[] = "\r";
static const char input_terminator[] = "\r\n";
initialized = true; /* Indicate that driver is initialized. */
/* Check for setup */
if (IM483SM_num_cards <= 0)
return(ERROR);
for (card_index = 0; card_index < IM483SM_num_cards; card_index++)
{
if (!motor_state[card_index])
continue;
brdptr = motor_state[card_index];
brdptr->cmnd_response = true;
total_cards = card_index + 1;
cntrl = (struct IM483controller *) brdptr->DevicePrivate;
/* Initialize communications channel */
success_rtn = pasynOctetSyncIO->connect(cntrl->asyn_port, 0, &cntrl->pasynUser, NULL);
if (success_rtn == asynSuccess)
{
int itera, retry = 0;
char *src, *dest;
pasynOctetSyncIO->setOutputEos(cntrl->pasynUser, output_terminator, strlen(output_terminator));
pasynOctetSyncIO->setInputEos(cntrl->pasynUser, input_terminator, strlen(input_terminator));
do
{
/* Send a message to the board, see if it exists */
/* flush any junk at input port - should not be any data available */
pasynOctetSyncIO->flush(cntrl->pasynUser);
send_mess(card_index, "\003", (char*) NULL); /* Reset device. */
epicsThreadSleep(1.0);
send_mess(card_index, " ", (char*) NULL);
/* Save controller identification message. */
src = buff;
dest = brdptr->ident;
*src = (char) NULL;
for (itera = 0; itera < 50; itera++)
{
if (*src == (char) NULL)
{
status = recv_mess(card_index, buff, 1);
if (status <= 0)
{
if (itera != 0)
{
*dest = (char) NULL;
status = 1;
}
break;
}
src = buff;
while (isspace(*src++));
--src;
if (itera != 0)
{
*dest++ = ' ';
itera++;
}
}
else if (isspace(*src))
{
while (isspace(*src++));
src -= 2;
}
else if (strncmp(src, "AD", 2) == 0)
{
strcpy(dest, "AMS");
src += 22;
dest += 3;
itera += 3;
}
*dest++ = *src++;
}
retry++;
/* Return value is length of response string */
} while (status == 0 && retry < 3);
}
if (success_rtn == asynSuccess && status > 0)
{
brdptr->localaddr = (char *) NULL;
brdptr->motor_in_motion = 0;
brdptr->total_axis = total_axis = 1;
for (motor_index = 0; motor_index < total_axis; motor_index++)
{
struct mess_info *motor_info = &brdptr->motor_info[motor_index];
int loop_state;
motor_info->status.All = 0;
motor_info->no_motion_count = 0;
motor_info->encoder_position = 0;
motor_info->position = 0;
brdptr->motor_info[motor_index].motor_motion = NULL;
/* Determine if encoder present based on open/closed loop mode. */
loop_state = 0;
if (loop_state != 0)
{
motor_info->encoder_present = YES;
motor_info->status.Bits.EA_PRESENT = 1;
motor_info->pid_present = YES;
motor_info->status.Bits.GAIN_SUPPORT = 1;
}
set_status(card_index, motor_index); /* Read status of each motor */
}
}
else
motor_state[card_index] = (struct controller *) NULL;
}
any_motor_in_motion = 0;
mess_queue.head = (struct mess_node *) NULL;
mess_queue.tail = (struct mess_node *) NULL;
free_list.head = (struct mess_node *) NULL;
free_list.tail = (struct mess_node *) NULL;
epicsThreadCreate((char *) "IM483SM_motor", epicsThreadPriorityMedium,
epicsThreadGetStackSize(epicsThreadStackMedium),
(EPICSTHREADFUNC) motor_task, (void *) &targs);
return(OK);
}
-745
View File
@@ -1,745 +0,0 @@
/*
FILENAME... drvMDrive.cc
USAGE... Motor record driver level support for Intelligent Motion
Systems, Inc. MDrive series; M17, M23, M34.
*/
/*
* Original Author: Ron Sluiter
* Date: 03/21/03
*
* Experimental Physics and Industrial Control System (EPICS)
*
* Copyright 1991, the Regents of the University of California,
* and the University of Chicago Board of Governors.
*
* This software was produced under U.S. Government contracts:
* (W-7405-ENG-36) at the Los Alamos National Laboratory,
* and (W-31-109-ENG-38) at Argonne National Laboratory.
*
* Initial development by:
* The Controls and Automation Group (AT-8)
* Ground Test Accelerator
* Accelerator Technology Division
* Los Alamos National Laboratory
*
* Co-developed with
* The Controls and Computing Group
* Accelerator Systems Division
* Advanced Photon Source
* Argonne National Laboratory
*
* NOTES
* -----
* Verified with firmware:
*
* - 1.071 E
* - 3.003 (MDI1FRD17B4-EQ)
* - 3.010
*
* Modification Log:
* -----------------
* .01 03/21/03 rls copied from drvIM483PL.c
* .02 02/03/04 rls Eliminate erroneous "Motor motion timeout ERROR".
* .03 03/15/04 rls Previous driver releases not working. Fixed by adding
* Kevin Peterson's eat_garbage() function. Added support
* for encoder detection via "ident".
* .04 07/01/04 rls Converted from MPF to asyn.
* .05 09/20/04 rls - support for 32axes/controller.
* - remove '?' command string padding.
* .06 12/16/04 rls - asyn R4.0 support.
* - make debug variables always available.
* - MS Visual C compatibility; make all epicsExportAddress
* extern "C" linkage.
* .07 03/18/05 rls - Flexible MDrive I/O configuration.
* - Change Echo mode to 2; eliminate eat_garbage().
* .08 12/17/10 rls - Test to determine if encoder is present changed to
* accommodate new firmware.
* - support actual velocity status update.
* .09 02/06/11 rls - "PR PN" response overflows input buffer; increased
* BUFF_SIZE from 13 to 80 bytes.
* - Slow "PR PN" response; increased timeout from 1 to 2 sec.
* - Extra "\r\n" from "PR PN" response; buffer flush added.
* - Eliminate compiler warnings on MDrive_axis[].
*/
/*
DESIGN LIMITATIONS...
1 - Like all controllers, the MDrive must be powered-on when EPICS is first
booted up.
2 - The MDrive cannot be power cycled while EPICS is up and running. The
consequences are permanent communication lose with the MDrive until
EPICS is rebooted.
*/
#include <string.h>
#include <epicsThread.h>
#include <drvSup.h>
#include <stdlib.h>
#include <errlog.h>
#include "motor.h"
#include "drvIM483.h"
#include "asynOctetSyncIO.h"
#include "epicsExport.h"
#define MDrive_NUM_CARDS 8
#define MAX_AXES 8
#define BUFF_SIZE 80 /* Maximum length of string to/from MDrive */
/*----------------debugging-----------------*/
volatile int drvMDrivedebug = 0;
extern "C" {epicsExportAddress(int, drvMDrivedebug);}
static inline void Debug(int level, const char *format, ...) {
#ifdef DEBUG
if (level < drvMDrivedebug) {
va_list pVar;
va_start(pVar, format);
vprintf(format, pVar);
va_end(pVar);
}
#endif
}
/* --- Local data. --- */
int MDrive_num_cards = 0;
static const char* const MDrive_axis[] = {"1", "2", "3", "4", "5", "6", "7", "8"};
/* Local data required for every driver; see "motordrvComCode.h" */
#include "motordrvComCode.h"
/*----------------functions-----------------*/
static int recv_mess(int, char *, int);
static RTN_STATUS send_mess(int, char const *, const char *const);
static int set_status(int, int);
static long report(int);
static long init();
static int motor_init();
static void query_done(int, int, struct mess_node *);
/*----------------functions-----------------*/
struct driver_table MDrive_access =
{
motor_init,
motor_send,
motor_free,
motor_card_info,
motor_axis_info,
&mess_queue,
&queue_lock,
&free_list,
&freelist_lock,
&motor_sem,
&motor_state,
&total_cards,
&any_motor_in_motion,
(RTN_STATUS (*)(int, const char*, char*)) send_mess,
recv_mess,
set_status,
query_done,
NULL,
&initialized,
(char **) MDrive_axis
};
struct drvMDrive_drvet
{
long number;
long (*report) (int);
long (*init) (void);
} drvMDrive = {2, report, init};
extern "C" {epicsExportAddress(drvet, drvMDrive);}
static struct thread_args targs = {SCAN_RATE, &MDrive_access, 0.0};
/*********************************************************
* Print out driver status report
*********************************************************/
static long report(int level)
{
int card;
if (MDrive_num_cards <= 0)
printf(" No MDrive controllers configured.\n");
else
{
for (card = 0; card < MDrive_num_cards; card++)
{
struct controller *brdptr = motor_state[card];
if (brdptr == NULL)
printf(" MDrive controller %d connection failed.\n", card);
else
{
struct IM483controller *cntrl;
cntrl = (struct IM483controller *) brdptr->DevicePrivate;
printf(" MDrive controller #%d, port=%s, id: %s \n", card,
cntrl->asyn_port, brdptr->ident);
}
}
}
return(OK);
}
static long init()
{
/*
* We cannot call motor_init() here, because that function can do GPIB I/O,
* and hence requires that the drvGPIB have already been initialized. That
* cannot be guaranteed, so we need to call motor_init from device support
*/
/* Check for setup */
if (MDrive_num_cards <= 0)
{
Debug(1, "init(): MDrive driver disabled. MDriveSetup() missing from startup script.\n");
}
return((long) 0);
}
static void query_done(int card, int axis, struct mess_node *nodeptr)
{
}
/******************************************************************************
*
* FUNCTION NAME: set_status
*
* LOGIC:
* Initialize.
* Send "Moving Status" query.
* Read response.
* IF normal response to query.
* Set communication status to NORMAL.
* ELSE
* IF communication status is NORMAL.
* Set communication status to RETRY.
* NORMAL EXIT.
* ELSE
* Set communication status error.
* ERROR EXIT.
* ENDIF
* ENDIF
*
* IF "Moving Status" indicates any motion (i.e. status != 0).
* Clear "Done Moving" status bit.
* ELSE
* Set "Done Moving" status bit.
* ENDIF
*
******************************************************************************/
static int set_status(int card, int signal)
{
struct IM483controller *cntrl;
struct mess_node *nodeptr;
register struct mess_info *motor_info;
/* Message parsing variables */
char buff[BUFF_SIZE];
int rtnval, rtn_state;
double motorData;
epicsUInt8 Lswitch;
bool plusdir, ls_active = false;
msta_field status;
cntrl = (struct IM483controller *) motor_state[card]->DevicePrivate;
input_config *confptr = cntrl->inconfig;
motor_info = &(motor_state[card]->motor_info[signal]);
nodeptr = motor_info->motor_motion;
status.All = motor_info->status.All;
/* Protect against extra "\r\n" terminator. */
rtn_state = recv_mess(card, buff, FLUSH);
send_mess(card, "PR MV", MDrive_axis[signal]);
rtn_state = recv_mess(card, buff, 1);
if (rtn_state > 0)
{
cntrl->status = NORMAL;
status.Bits.CNTRL_COMM_ERR = 0;
}
else
{
if (cntrl->status == NORMAL)
{
cntrl->status = RETRY;
rtn_state = OK;
goto exit;
}
else
{
cntrl->status = COMM_ERR;
status.Bits.CNTRL_COMM_ERR = 1;
status.Bits.RA_PROBLEM = 1;
rtn_state = 1;
goto exit;
}
}
rtnval = atoi(buff);
status.Bits.RA_DONE = (rtnval != 0) ? 0 : 1;
/*
* Parse motor position
* Position string format: 1TP5.012,2TP1.123,3TP-100.567,...
* Skip to substring for this motor, convert to double
*/
send_mess(card, "PR P", MDrive_axis[signal]);
recv_mess(card, buff, 1);
motorData = atof(buff);
if (motorData == motor_info->position)
{
if (nodeptr != 0) /* Increment counter only if motor is moving. */
motor_info->no_motion_count++;
}
else
{
epicsInt32 newposition;
newposition = NINT(motorData);
status.Bits.RA_DIRECTION = (newposition >= motor_info->position) ? 1 : 0;
motor_info->position = newposition;
motor_info->no_motion_count = 0;
}
plusdir = (status.Bits.RA_DIRECTION) ? true : false;
if (confptr->plusLS == 0 || confptr->minusLS == 0)
{
status.Bits.RA_PLUS_LS = 0;
status.Bits.RA_MINUS_LS = 0;
}
else
{
sprintf(buff, "PR I%d", confptr->plusLS);
send_mess(card, buff, MDrive_axis[signal]);
recv_mess(card, buff, 1);
Lswitch = atoi(buff);
/* Set limit Lswitch error indicators. */
if (Lswitch != 0)
{
status.Bits.RA_PLUS_LS = 1;
if (plusdir == true)
ls_active = true;
}
else
status.Bits.RA_PLUS_LS = 0;
sprintf(buff, "PR I%d", confptr->minusLS);
send_mess(card, buff, MDrive_axis[signal]);
recv_mess(card, buff, 1);
Lswitch = atoi(buff);
if (Lswitch != 0)
{
status.Bits.RA_MINUS_LS = 1;
if (plusdir == false)
ls_active = true;
}
else
status.Bits.RA_MINUS_LS = 0;
}
if (confptr->homeLS == 0)
status.Bits.RA_HOME = 0;
else
{
sprintf(buff, "PR I%d", confptr->homeLS);
send_mess(card, buff, MDrive_axis[signal]);
recv_mess(card, buff, 1);
Lswitch = atoi(buff);
status.Bits.RA_HOME = (Lswitch) ? 1 : 0;
}
/* !!! Assume no closed-looped control!!! */
status.Bits.EA_POSITION = 0;
/* encoder status */
status.Bits.EA_SLIP = 0;
status.Bits.EA_SLIP_STALL = 0;
status.Bits.EA_HOME = 0;
if (motor_state[card]->motor_info[signal].encoder_present == NO)
motor_info->encoder_position = 0;
else
{
send_mess(card, "PR C2", MDrive_axis[signal]);
recv_mess(card, buff, 1);
motorData = atof(buff);
motor_info->encoder_position = (epicsInt32) motorData;
}
status.Bits.RA_PROBLEM = 0;
/* Get current motor velocity */
send_mess(card, "PR V", MDrive_axis[signal]);
recv_mess(card, buff, 1);
motorData = atof(buff);
motor_info->velocity = (epicsInt32) motorData;
if (!status.Bits.RA_DIRECTION)
motor_info->velocity *= -1;
rtn_state = (!motor_info->no_motion_count || ls_active == true ||
status.Bits.RA_DONE | status.Bits.RA_PROBLEM) ? 1 : 0;
/* Test for post-move string. */
if ((status.Bits.RA_DONE || ls_active == true) && nodeptr != 0 &&
nodeptr->postmsgptr != 0)
{
strcpy(buff, nodeptr->postmsgptr);
send_mess(card, buff, MDrive_axis[signal]);
nodeptr->postmsgptr = NULL;
}
exit:
motor_info->status.All = status.All;
return(rtn_state);
}
/*****************************************************/
/* send a message to the MDrive board */
/* send_mess() */
/*****************************************************/
static RTN_STATUS send_mess(int card, char const *com, const char *const name)
{
char local_buff[MAX_MSG_SIZE];
struct IM483controller *cntrl;
int comsize, namesize;
size_t nwrite;
comsize = (com == NULL) ? 0 : strlen(com);
namesize = (name == NULL) ? 0 : strlen(name);
if ((comsize + namesize) > MAX_MSG_SIZE)
{
errlogMessage("drvMDrive.c:send_mess(); message size violation.\n");
return(ERROR);
}
else if (comsize == 0) /* Normal exit on empty input message. */
return(OK);
if (!motor_state[card])
{
errlogPrintf("drvMDrive.c:send_mess() - invalid card #%d\n", card);
return(ERROR);
}
/* Make a local copy of the string and add the command line terminator. */
if (namesize != 0)
{
strcpy(local_buff, name); /* put in axis */
strcat(local_buff, com);
}
else
strcpy(local_buff, com);
Debug(2, "send_mess(): message = %s\n", local_buff);
cntrl = (struct IM483controller *) motor_state[card]->DevicePrivate;
pasynOctetSyncIO->write(cntrl->pasynUser, local_buff, strlen(local_buff),
COMM_TIMEOUT, &nwrite);
return(OK);
}
/*****************************************************/
/* receive a message from the MDrive board */
/* recv_mess() */
/*****************************************************/
static int recv_mess(int card, char *com, int flag)
{
struct IM483controller *cntrl;
const double timeout = 2.0;
size_t nread = 0;
asynStatus status = asynError;
int eomReason;
/* Check that card exists */
if (!motor_state[card])
return(ERROR);
cntrl = (struct IM483controller *) motor_state[card]->DevicePrivate;
if (flag == FLUSH)
pasynOctetSyncIO->flush(cntrl->pasynUser);
else
status = pasynOctetSyncIO->read(cntrl->pasynUser, com, BUFF_SIZE,
timeout, &nread, &eomReason);
if ((status != asynSuccess) || (nread <= 0))
{
com[0] = '\0';
nread = 0;
}
Debug(2, "recv_mess(): message = \"%s\"\n", com);
return(nread);
}
/*****************************************************/
/* Setup system configuration */
/* MDriveSetup() */
/*****************************************************/
RTN_STATUS
MDriveSetup(int num_cards, /* maximum number of chains in system. */
int scan_rate) /* polling rate - 1/60 sec units. */
{
int itera;
if (num_cards < 1 || num_cards > MDrive_NUM_CARDS)
MDrive_num_cards = MDrive_NUM_CARDS;
else
MDrive_num_cards = num_cards;
/* Set motor polling task rate */
if (scan_rate >= 1 && scan_rate <= 60)
targs.motor_scan_rate = scan_rate;
else
targs.motor_scan_rate = SCAN_RATE;
/*
* Allocate space for motor_state structures. Note this must be done
* before IM483Config is called, so it cannot be done in motor_init() This
* means that we must allocate space for a card without knowing if it
* really exists, which is not a serious problem
*/
motor_state = (struct controller **)
malloc(MDrive_num_cards * sizeof(struct controller *));
for (itera = 0; itera < MDrive_num_cards; itera++)
motor_state[itera] = (struct controller *) NULL;
return(OK);
}
/*****************************************************/
/* Configure a controller */
/* MDriveConfig() */
/*****************************************************/
RTN_STATUS
MDriveConfig(int card, /* chain being configured */
const char *name) /* ASYN port name */
{
struct IM483controller *cntrl;
if (card < 0 || card >= MDrive_num_cards)
return(ERROR);
motor_state[card] = (struct controller *) malloc(sizeof(struct controller));
motor_state[card]->DevicePrivate = malloc(sizeof(struct IM483controller));
cntrl = (struct IM483controller *) motor_state[card]->DevicePrivate;
strcpy(cntrl->asyn_port, name);
return(OK);
}
/*****************************************************/
/* initialize all software and hardware */
/* This is called from the initialization routine in */
/* device support. */
/* motor_init() */
/*****************************************************/
static int motor_init()
{
struct controller *brdptr;
struct IM483controller *cntrl;
int card_index, motor_index;
char buff[BUFF_SIZE];
int total_axis = 0;
int status;
asynStatus success_rtn;
static const char output_terminator[] = "\n";
static const char input_terminator[] = "\r\n";
initialized = true; /* Indicate that driver is initialized. */
/* Check for setup */
if (MDrive_num_cards <= 0)
return(ERROR);
for (card_index = 0; card_index < MDrive_num_cards; card_index++)
{
if (!motor_state[card_index])
continue;
brdptr = motor_state[card_index];
brdptr->ident[0] = (char) NULL; /* No controller ID message. */
brdptr->cmnd_response = false;
total_cards = card_index + 1;
cntrl = (struct IM483controller *) brdptr->DevicePrivate;
/* Initialize communications channel */
success_rtn = pasynOctetSyncIO->connect(cntrl->asyn_port, 0,
&cntrl->pasynUser, NULL);
if (success_rtn == asynSuccess)
{
pasynOctetSyncIO->setOutputEos(cntrl->pasynUser, output_terminator,
strlen(output_terminator));
pasynOctetSyncIO->setInputEos(cntrl->pasynUser, input_terminator,
strlen(input_terminator));
/* Send a message to the board, see if it exists */
/* flush any junk at input port - should not be any data available */
pasynOctetSyncIO->flush(cntrl->pasynUser);
for (total_axis = 0; total_axis < MAX_AXES; total_axis++)
{
int retry = 0;
/* Try 3 times to connect to controller. */
do
{
send_mess(card_index, "PR VR", MDrive_axis[total_axis]);
status = recv_mess(card_index, buff, 1);
retry++;
} while (status == 0 && retry < 3);
if (status <= 0)
break;
else if (total_axis == 0)
strcpy(brdptr->ident, buff);
}
brdptr->total_axis = total_axis;
cntrl->inconfig = (input_config *)
malloc(sizeof(struct IM483controller) * total_axis);
}
if (success_rtn == asynSuccess && total_axis > 0)
{
input_config *confptr = cntrl->inconfig;
brdptr->localaddr = (char *) NULL;
brdptr->motor_in_motion = 0;
for (motor_index = 0; motor_index < total_axis; motor_index++)
{
int itera;
bool encoder = false; /* Default to no encoder detected. */
struct mess_info *motor_info = &brdptr->motor_info[motor_index];
motor_info->status.All = 0;
motor_info->no_motion_count = 0;
motor_info->encoder_position = 0;
motor_info->position = 0;
brdptr->motor_info[motor_index].motor_motion = NULL;
/* Determine if encoder present based last character of "ident". */
if (brdptr->ident[strlen(brdptr->ident) - 1] == 'E')
encoder = true;
else
{
int size;
/* Try to get "Part number" (not in old firmware). */
send_mess(card_index, "PR PN", MDrive_axis[motor_index]);
size = recv_mess(card_index, buff, 1);
if (size > 0)
{
bool found = false;
found |= !strcmp(&buff[size-1],"E");
found |= !strcmp(&buff[size-2],"EQ");
found |= !strcmp(&buff[size-2],"EE");
found |= !strcmp(&buff[size-3],"EJM");
found |= !strcmp(&buff[size-5],"EJM-N");
encoder = found;
}
}
if (encoder == true)
{
motor_info->pid_present = YES;
motor_info->status.Bits.GAIN_SUPPORT = 1;
motor_info->encoder_present = YES;
motor_info->status.Bits.EA_PRESENT = 1;
}
else
motor_info->encoder_present = NO;
/* Protect against extra "\r\n" terminator. */
status = recv_mess(card_index, buff, 1);
/* Determine input configuration. */
confptr->homeLS = confptr->minusLS = confptr->plusLS = 0;
for (itera = 1; itera <= 4; itera++)
{
int type, active;
sprintf(buff, "PR S%d", itera);
send_mess(card_index, buff, MDrive_axis[motor_index]);
status = recv_mess(card_index, buff, 1);
if (status == 0)
{
errlogPrintf("Error reading I/O configuration.\n");
break;
}
status = sscanf(buff, "%d,%d", &type, &active);
switch (type)
{
case 0:
break;
case 1: /* Home switch. */
confptr->homeLS = itera;
break;
case 2: /* Plus limit switch. */
confptr->plusLS = itera;
break;
case 3: /* Minus limit switch. */
confptr->minusLS = itera;
break;
default:
errlogPrintf("Invalid I/O type: %d.\n", type);
}
}
/* Test for missing configuration. */
if (confptr->minusLS == 0 || confptr->plusLS == 0)
{
const char p_label[6] = "Plus", m_label[6] = "Minus";
errlogPrintf("MDrive chain #%d, motor #%d %s LS not configured.\n",
card_index, motor_index,
(confptr->minusLS == 0) ? m_label : p_label);
}
set_status(card_index, motor_index); /* Read status of each motor */
}
}
else
motor_state[card_index] = (struct controller *) NULL;
}
any_motor_in_motion = 0;
mess_queue.head = (struct mess_node *) NULL;
mess_queue.tail = (struct mess_node *) NULL;
free_list.head = (struct mess_node *) NULL;
free_list.tail = (struct mess_node *) NULL;
epicsThreadCreate((char *) "MDrive_motor", epicsThreadPriorityMedium,
epicsThreadGetStackSize(epicsThreadStackMedium),
(EPICSTHREADFUNC) motor_task, (void *) &targs);
return(OK);
}
-3
View File
@@ -21,9 +21,6 @@ OmsAsynSrc_DEPEND_DIRS = MotorSrc
DIRS += MotorSimSrc
MotorSimSrc_DEPEND_DIRS = MotorSrc
DIRS += ImsSrc
ImsSrc_DEPEND_DIRS = MotorSrc
DIRS += PIGCS2Src
PIGCS2Src_DEPEND_DIRS = MotorSrc