forked from epics_driver_modules/motorBase
542 lines
18 KiB
C++
542 lines
18 KiB
C++
/*
|
|
FILENAME... PIasynController.cpp
|
|
USAGE... PI GCS2 Motor Support.
|
|
|
|
*************************************************************************
|
|
* Copyright (c) 2011-2013 Physik Instrumente (PI) GmbH & Co. KG
|
|
* This file is distributed subject to the EPICS Open License Agreement
|
|
* found in the file LICENSE that is included with this distribution.
|
|
*************************************************************************
|
|
|
|
Version: $Revision$
|
|
Modified By: $Author$
|
|
Last Modified: $Date$
|
|
HeadURL: $URL$
|
|
|
|
Original Author: Steffen Rau
|
|
Created: January 2011
|
|
|
|
Based on drvMotorSim.c, Mark Rivers, December 13, 2009
|
|
|
|
*/
|
|
|
|
|
|
#include <math.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
|
|
|
|
#include <epicsTime.h>
|
|
#include <epicsThread.h>
|
|
#include <epicsString.h>
|
|
#include <epicsMutex.h>
|
|
#include <ellLib.h>
|
|
#include <iocsh.h>
|
|
#include <asynOctetSyncIO.h>
|
|
//#include <motorVersion.h>
|
|
#include <motor_interface.h>
|
|
#include <ctype.h>
|
|
|
|
#include "PIasynController.h"
|
|
#include "PIGCSController.h"
|
|
#include "PIasynAxis.h"
|
|
#include <epicsExport.h>
|
|
|
|
|
|
//#undef asynPrint
|
|
//#define asynPrint(user,reason,format...) 0
|
|
|
|
|
|
static const char *driverName = "PIasynDriver";
|
|
|
|
|
|
static ELLLIST PIasynControllerList;
|
|
static int PIasynControllerListInitialized = 0;
|
|
|
|
PIasynController::PIasynController(const char *portName, const char* asynPort, int numAxes, int priority, int stackSize, int movingPollPeriod, int idlePollPeriod)
|
|
: asynMotorController(portName, numAxes, 10,
|
|
asynInt32Mask | asynFloat64Mask,
|
|
asynInt32Mask | asynFloat64Mask,
|
|
ASYN_CANBLOCK | ASYN_MULTIDEVICE,
|
|
1, // autoconnect
|
|
priority, stackSize)
|
|
, m_pGCSController( NULL )
|
|
, movesDeferred( 0 )
|
|
{
|
|
createParam(PI_SUP_POSITION_String, asynParamFloat64, &PI_SUP_POSITION);
|
|
createParam(PI_SUP_TARGET_String, asynParamFloat64, &PI_SUP_TARGET);
|
|
createParam(PI_SUP_SERVO_String, asynParamInt32, &PI_SUP_SERVO);
|
|
createParam(PI_SUP_LAST_ERR_String, asynParamInt32, &PI_SUP_LAST_ERR);
|
|
createParam(PI_SUP_PIVOT_X_String, asynParamFloat64, &PI_SUP_PIVOT_X);
|
|
createParam(PI_SUP_PIVOT_Y_String, asynParamFloat64, &PI_SUP_PIVOT_Y);
|
|
createParam(PI_SUP_PIVOT_Z_String, asynParamFloat64, &PI_SUP_PIVOT_Z);
|
|
createParam(PI_SUP_RBPIVOT_X_String, asynParamFloat64, &PI_SUP_RBPIVOT_X);
|
|
createParam(PI_SUP_RBPIVOT_Y_String, asynParamFloat64, &PI_SUP_RBPIVOT_Y);
|
|
createParam(PI_SUP_RBPIVOT_Z_String, asynParamFloat64, &PI_SUP_RBPIVOT_Z);
|
|
|
|
int axis;
|
|
PIasynAxis *pAxis;
|
|
PIasynControllerNode *pNode;
|
|
|
|
if (!PIasynControllerListInitialized)
|
|
{
|
|
PIasynControllerListInitialized = 1;
|
|
ellInit(&PIasynControllerList);
|
|
}
|
|
|
|
// We should make sure this portName is not already in the list */
|
|
pNode = (PIasynControllerNode*) calloc(1, sizeof(PIasynControllerNode));
|
|
pNode->portName = epicsStrDup(portName);
|
|
pNode->pController = this;
|
|
ellAdd(&PIasynControllerList, (ELLNODE *)pNode);
|
|
|
|
|
|
asynStatus status;
|
|
asynUser* pInterface;
|
|
status = pasynOctetSyncIO->connect(asynPort, 0, &pInterface, NULL);
|
|
if (status)
|
|
{
|
|
asynPrint(pInterface, ASYN_TRACE_ERROR|ASYN_TRACE_FLOW,
|
|
"echoHandler: unable to connect to port %s\n",
|
|
asynPort);
|
|
return;
|
|
}
|
|
status = pasynOctetSyncIO->setInputEos(pInterface, "\n", 1);
|
|
if (status) {
|
|
asynPrint(pInterface, ASYN_TRACE_ERROR|ASYN_TRACE_FLOW,
|
|
"echoHandler: unable to set input EOS on %s: %s\n",
|
|
asynPort, pInterface->errorMessage);
|
|
return;
|
|
}
|
|
status = pasynOctetSyncIO->setOutputEos(pInterface, "", 0);
|
|
if (status) {
|
|
asynPrint(pInterface, ASYN_TRACE_ERROR|ASYN_TRACE_FLOW,
|
|
"echoHandler: unable to set output EOS on %s: %s\n",
|
|
asynPort, pInterface->errorMessage);
|
|
return;
|
|
}
|
|
|
|
char inputBuff[256];
|
|
inputBuff[0] = '\0';
|
|
status = PIGCSController::sendAndReceive(pInterface, "*IDN?", inputBuff, 255, pInterface);
|
|
asynPrint(pInterface, ASYN_TRACE_ERROR|ASYN_TRACE_FLOW,
|
|
"read from %s: %s\n",
|
|
asynPort, inputBuff);
|
|
|
|
char* p = inputBuff;
|
|
while (*p != '\0') { *p = toupper(*p); p++; }
|
|
|
|
m_pGCSController = PIGCSController::CreateGCSController(pInterface, inputBuff);
|
|
if (NULL == m_pGCSController)
|
|
{
|
|
asynPrint(pInterface, ASYN_TRACE_ERROR|ASYN_TRACE_FLOW,
|
|
"PIasynController: unknown controller type %s: %s\n",
|
|
asynPort, inputBuff);
|
|
return;
|
|
}
|
|
|
|
m_pGCSController->init();
|
|
|
|
if (numAxes < 1 ) numAxes = 1;
|
|
this->numAxes_ = numAxes;
|
|
|
|
if (m_pGCSController->getNrFoundAxes()<size_t(numAxes))
|
|
{
|
|
// more axes configured than connected to controller
|
|
asynPrint(pInterface, ASYN_TRACE_ERROR|ASYN_TRACE_FLOW,
|
|
"PIasynController: requested number of axes (%d) out of range, only %d axis/axes supported\n",
|
|
numAxes, int(m_pGCSController->getNrFoundAxes()));
|
|
delete m_pGCSController;
|
|
m_pGCSController = NULL;
|
|
return;
|
|
}
|
|
|
|
for (axis=0; axis<numAxes; axis++)
|
|
{
|
|
pAxis = new PIasynAxis(this, m_pGCSController, axis, m_pGCSController->getAxesID(axis));
|
|
pAxis->Init(portName);
|
|
}
|
|
|
|
startPoller(double(movingPollPeriod)/1000, double(idlePollPeriod)/1000, 10);
|
|
}
|
|
|
|
|
|
|
|
|
|
void PIasynController::report(FILE *fp, int level)
|
|
{
|
|
int axis;
|
|
PIasynAxis *pAxis;
|
|
|
|
fprintf(fp, "Simulation motor driver %s, numAxes=%d\n",
|
|
this->portName, this->numAxes_);
|
|
|
|
for (axis=0; axis<this->numAxes_; axis++)
|
|
{
|
|
pAxis = getPIAxis(axis);
|
|
fprintf(fp, " axis %d\n",
|
|
pAxis->axisNo_);
|
|
|
|
if (level > 0)
|
|
{
|
|
if (pAxis->m_isHoming)
|
|
{
|
|
fprintf(fp, " Currently homing axis\n" );
|
|
}
|
|
}
|
|
}
|
|
|
|
// Call the base class method
|
|
asynMotorController::report(fp, level);
|
|
}
|
|
|
|
//PIasynAxis * PIasynController::getPIAxis(asynUser *pasynUser)
|
|
//{
|
|
// int axis;
|
|
// PIasynAxis *pAxis;
|
|
//
|
|
// getAddress(pasynUser, &axis);
|
|
// pAxis = this->m_pAxes[axis];
|
|
// pAxis->m_pasynUser = pasynUser;
|
|
// return(pAxis);
|
|
//}
|
|
|
|
|
|
|
|
asynStatus PIasynController::processDeferredMoves()
|
|
{
|
|
asynStatus status = asynError;
|
|
int axis;
|
|
PIasynAxis *pAxesArray[PIGCSController::MAX_NR_AXES];
|
|
int targetsCts[PIGCSController::MAX_NR_AXES];
|
|
|
|
int numDeferredAxes = 0;
|
|
for (axis=0; axis<this->numAxes_; axis++)
|
|
{
|
|
PIasynAxis *pAxis = getPIAxis(axis);
|
|
if (pAxis->deferred_move)
|
|
{
|
|
pAxesArray[numDeferredAxes] = pAxis;
|
|
targetsCts[numDeferredAxes] = pAxis->deferred_position;
|
|
pAxis->setIntegerParam(motorStatusDone_, 0);
|
|
pAxis->callParamCallbacks();
|
|
numDeferredAxes++;
|
|
}
|
|
}
|
|
if (numDeferredAxes > 0)
|
|
{
|
|
status = m_pGCSController->moveCts(pAxesArray, targetsCts, numDeferredAxes);
|
|
}
|
|
|
|
for (axis=0; axis<this->numAxes_; axis++)
|
|
{
|
|
if (getPIAxis(axis)->deferred_move)
|
|
{
|
|
getPIAxis(axis)->deferred_move = 0;
|
|
}
|
|
}
|
|
epicsEventSignal(pollEventId_);
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
asynStatus PIasynController::writeInt32(asynUser *pasynUser, epicsInt32 value)
|
|
{
|
|
if (NULL == m_pGCSController)
|
|
{
|
|
asynPrint(pasynUser, ASYN_TRACE_ERROR|ASYN_TRACE_FLOW,
|
|
"PIasynController::writeInt32() GCS controller not initialized!\n");
|
|
|
|
return asynError;
|
|
}
|
|
m_pGCSController->m_pCurrentLogSink = pasynUser;
|
|
int function = pasynUser->reason;
|
|
asynStatus status = asynSuccess;
|
|
PIasynAxis *pAxis = (PIasynAxis *)this->getAxis(pasynUser);
|
|
static const char *functionName = "writeInt32";
|
|
|
|
lock();
|
|
/* 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(function, value);
|
|
|
|
if (function == motorClosedLoop_)
|
|
{
|
|
asynPrint(pasynUser, ASYN_TRACE_FLOW,
|
|
"%s:%s: %sing Closed-Loop Control flag on driver %s\n",
|
|
value != 0.0?"Enabl":"Disabl",
|
|
driverName, functionName, this->portName);
|
|
status = m_pGCSController->setServo(pAxis, (value!=0)?1:0);
|
|
|
|
}
|
|
else if (function == motorDeferMoves_)
|
|
{
|
|
asynPrint(pasynUser, ASYN_TRACE_FLOW,
|
|
"%s:%s: %sing Deferred Move flag on driver %s\n",
|
|
value != 0.0?"Sett":"Clear",
|
|
driverName, functionName, this->portName);
|
|
if (value == 0.0 && this->movesDeferred != 0)
|
|
{
|
|
processDeferredMoves();
|
|
}
|
|
this->movesDeferred = value;
|
|
} else {
|
|
/* Call base class call its method (if we have our parameters check this here) */
|
|
status = asynMotorController::writeInt32(pasynUser, value);
|
|
}
|
|
unlock();
|
|
/* Do callbacks so higher layers see any changes */
|
|
pAxis->callParamCallbacks();
|
|
if (status)
|
|
asynPrint(pasynUser, ASYN_TRACE_ERROR|ASYN_TRACE_FLOW,
|
|
"%s:%s: error, status=%d function=%d, value=%d\n",
|
|
driverName, functionName, status, function, value);
|
|
else
|
|
asynPrint(pasynUser, ASYN_TRACEIO_DRIVER,
|
|
"%s:%s: function=%d, value=%d\n",
|
|
driverName, functionName, function, value);
|
|
return status;
|
|
}
|
|
|
|
asynStatus PIasynController::writeFloat64(asynUser *pasynUser, epicsFloat64 value)
|
|
{
|
|
if (NULL == m_pGCSController)
|
|
{
|
|
asynPrint(pasynUser, ASYN_TRACE_ERROR|ASYN_TRACE_FLOW,
|
|
"PIasynController::writeFloat64() GCS controller not initialized!\n");
|
|
|
|
return asynError;
|
|
}
|
|
m_pGCSController->m_pCurrentLogSink = pasynUser;
|
|
int function = pasynUser->reason;
|
|
asynStatus status = asynSuccess;
|
|
PIasynAxis *pAxis = (PIasynAxis *)this->getAxis(pasynUser);
|
|
static const char *functionName = "writeFloat64";
|
|
|
|
/* 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->setDoubleParam(function, value);
|
|
|
|
if (function == PI_SUP_TARGET)
|
|
{
|
|
printf("PI_SUP_TargetAO: %f for axis %d\n", value, pAxis->axisNo_);
|
|
}
|
|
else if (function == PI_SUP_PIVOT_X)
|
|
{
|
|
status = m_pGCSController->SetPivotX(value);
|
|
}
|
|
else if (function == PI_SUP_PIVOT_Y)
|
|
{
|
|
status = m_pGCSController->SetPivotY(value);
|
|
}
|
|
else if (function == PI_SUP_PIVOT_Z)
|
|
{
|
|
status = m_pGCSController->SetPivotZ(value);
|
|
}
|
|
else if (function == motorPosition_) // Entspricht das DFH ?
|
|
{
|
|
// pAxis->enc_offset = (double) value - pAxis->nextpoint.axis[0].p;
|
|
asynPrint(pasynUser, ASYN_TRACE_FLOW,
|
|
"%s:%s: Set axis %d to position %d",
|
|
driverName, functionName, pAxis->axisNo_, value);
|
|
}
|
|
else if (function == motorResolution_ )
|
|
{
|
|
/* Call base class call its method (if we have our parameters check this here) */
|
|
status = asynMotorController::writeFloat64(pasynUser, value);
|
|
}
|
|
else if (function == motorEncoderRatio_)
|
|
{
|
|
/* Call base class call its method (if we have our parameters check this here) */
|
|
status = asynMotorController::writeFloat64(pasynUser, value);
|
|
}
|
|
else
|
|
{
|
|
/* Call base class call its method (if we have our parameters check this here) */
|
|
status = asynMotorController::writeFloat64(pasynUser, value);
|
|
}
|
|
/* Do callbacks so higher layers see any changes */
|
|
pAxis->callParamCallbacks();
|
|
if (status)
|
|
asynPrint(pasynUser, ASYN_TRACE_ERROR|ASYN_TRACE_FLOW,
|
|
"%s:%s: error, status=%d function=%d, value=%f\n",
|
|
driverName, functionName, status, function, value);
|
|
else
|
|
asynPrint(pasynUser, ASYN_TRACEIO_DRIVER,
|
|
"%s:%s: function=%d, value=%f\n",
|
|
driverName, functionName, function, value);
|
|
return status;
|
|
}
|
|
|
|
|
|
|
|
|
|
asynStatus PIasynController::profileMove(asynUser *pasynUser, int npoints, double positions[], double times[], int relative, int trigger )
|
|
{
|
|
asynPrint(pasynUser, ASYN_TRACE_FLOW|ASYN_TRACE_ERROR,
|
|
"PIasynController::profileMove() - not implemented\n");
|
|
return asynError;
|
|
}
|
|
|
|
asynStatus PIasynController::triggerProfile(asynUser *pasynUser)
|
|
{
|
|
asynPrint(pasynUser, ASYN_TRACE_FLOW|ASYN_TRACE_ERROR,
|
|
"PIasynController::profileMove() - not implemented\n");
|
|
return asynError;
|
|
}
|
|
|
|
asynStatus PIasynController::configAxis(PIasynAxis *pAxis)
|
|
{
|
|
asynUser* logSink = pasynManager->createAsynUser(0,0);
|
|
asynStatus status = pasynManager->connectDevice(logSink, portName, pAxis->getAxisNo());
|
|
if (status != asynSuccess)
|
|
{
|
|
asynPrint(logSink, ASYN_TRACE_FLOW|ASYN_TRACE_ERROR,
|
|
"PIasynController::configAxis() - connectDevice() failed\n");
|
|
return status;
|
|
}
|
|
m_pGCSController->m_pCurrentLogSink = logSink;
|
|
|
|
pAxis->setIntegerParam(motorAxisHasClosedLoop, 1);
|
|
pAxis->callParamCallbacks();
|
|
|
|
m_pGCSController->initAxis(pAxis);
|
|
double resolution;
|
|
m_pGCSController->getResolution(pAxis, resolution);
|
|
m_pGCSController->getAxisVelocity(pAxis);
|
|
m_pGCSController->getAxisPositionCts(pAxis);
|
|
pAxis->setDoubleParam(this->motorPosition_, pAxis->m_positionCts);
|
|
pAxis->setDoubleParam(this->motorMoveAbs_, pAxis->m_positionCts);
|
|
double negLimit, posLimit;
|
|
m_pGCSController->getTravelLimits(pAxis, negLimit, posLimit);
|
|
pAxis->setDoubleParam(this->motorLowLimit_, negLimit);
|
|
pAxis->setDoubleParam(this->motorHighLimit_, posLimit);
|
|
m_pGCSController->getReferencedState(pAxis);
|
|
pAxis->setIntegerParam( this->motorStatusHomed_, pAxis->m_homed );
|
|
|
|
pasynManager->freeAsynUser(logSink);
|
|
|
|
/* Send a signal to the poller task which will make it do a poll,
|
|
* updating values for this axis to use the new resolution (stepSize) */
|
|
epicsEventSignal(pollEventId_);
|
|
|
|
return(asynSuccess);
|
|
}
|
|
|
|
/**\defgroup PIasynTask Routines to implement the motor axis simulation task
|
|
@{
|
|
*/
|
|
|
|
/** Process one iteration of an axis
|
|
|
|
This routine takes a single axis and propogates its motion forward a given amount
|
|
of time.
|
|
|
|
\param pAxis [in] Pointer to axis information.
|
|
\param delta [in] Time in seconds to propogate motion forwards.
|
|
|
|
\return Integer indicating 0 (asynSuccess) for success or non-zero for failure.
|
|
*/
|
|
|
|
|
|
//
|
|
//static void PIasynTaskC(void *drvPvt)
|
|
//{
|
|
// PIasynController *pController = (PIasynController*)drvPvt;
|
|
// pController->PIasynTask();
|
|
//}
|
|
//
|
|
//
|
|
//
|
|
//void PIasynController::PIasynTask()
|
|
//{
|
|
// double timeout = m_idlePollingRate;
|
|
// int axis;
|
|
// PIasynAxis *pAxis;
|
|
// epicsEventSignal(pollEventId_); /* Force on poll at startup */
|
|
//
|
|
// int status;
|
|
// while ( 1 )
|
|
// {
|
|
// if (timeout != 0.) status = epicsEventWaitWithTimeout(pollEventId_, timeout);
|
|
// else status = epicsEventWait(pollEventId_);
|
|
//
|
|
// lock();
|
|
// m_pGCSController->getGlobalState(pAxes_, numAxes_);
|
|
// unlock();
|
|
//
|
|
// bool bAnyAxisMoving = false;
|
|
// for (axis=0; axis<this->numAxes_; axis++)
|
|
// {
|
|
// lock();
|
|
// pAxis = getPIAxis(axis);
|
|
// process(pAxis);
|
|
// pAxis->callParamCallbacks();
|
|
// unlock();
|
|
// bAnyAxisMoving = (pAxis->m_bMoving != 0);
|
|
// }
|
|
// m_pGCSController->m_bAnyAxisMoving = bAnyAxisMoving;
|
|
// if (m_pGCSController->m_bAnyAxisMoving)
|
|
// timeout = m_movingPollingRate;
|
|
// else
|
|
// timeout = m_idlePollingRate;
|
|
// }
|
|
//}
|
|
|
|
asynStatus PIasynController::poll()
|
|
{
|
|
m_pGCSController->getGlobalState(pAxes_, numAxes_);
|
|
|
|
setDoubleParam( 0, PI_SUP_RBPIVOT_X, m_pGCSController->GetPivotX());
|
|
setDoubleParam( 0, PI_SUP_RBPIVOT_Y, m_pGCSController->GetPivotY());
|
|
setDoubleParam( 0, PI_SUP_RBPIVOT_Z, m_pGCSController->GetPivotZ());
|
|
|
|
setIntegerParam( 0, PI_SUP_LAST_ERR, m_pGCSController->GetLastError() );
|
|
|
|
callParamCallbacks();
|
|
return asynSuccess;
|
|
}
|
|
|
|
|
|
/** Configuration command, called directly or from iocsh */
|
|
extern "C" int PI_GCS2_CreateController(const char *portName, const char* asynPort, int numAxes, int priority, int stackSize, int movingPollingRate, int idlePollingRate)
|
|
{
|
|
PIasynController *pasynController
|
|
= new PIasynController(portName, asynPort, numAxes, priority, stackSize, movingPollingRate, idlePollingRate);
|
|
pasynController = NULL;
|
|
return(asynSuccess);
|
|
}
|
|
|
|
/** Code for iocsh registration */
|
|
static const iocshArg PI_GCS2_CreateControllerArg0 = {"Port name", iocshArgString};
|
|
static const iocshArg PI_GCS2_CreateControllerArg1 = {"asyn Port name", iocshArgString};
|
|
static const iocshArg PI_GCS2_CreateControllerArg2 = {"Number of axes", iocshArgInt};
|
|
static const iocshArg PI_GCS2_CreateControllerArg3 = {"priority", iocshArgInt};
|
|
static const iocshArg PI_GCS2_CreateControllerArg4 = {"stackSize", iocshArgInt};
|
|
static const iocshArg PI_GCS2_CreateControllerArg5 = {"moving polling time [msec]", iocshArgInt};
|
|
static const iocshArg PI_GCS2_CreateControllerArg6 = {"idle polling time [msec]", iocshArgInt};
|
|
static const iocshArg * const PI_GCS2_CreateControllerArgs[] = {&PI_GCS2_CreateControllerArg0,
|
|
&PI_GCS2_CreateControllerArg1,
|
|
&PI_GCS2_CreateControllerArg2,
|
|
&PI_GCS2_CreateControllerArg3,
|
|
&PI_GCS2_CreateControllerArg4,
|
|
&PI_GCS2_CreateControllerArg5,
|
|
&PI_GCS2_CreateControllerArg6};
|
|
static const iocshFuncDef PI_GCS2_CreateControllerDef = {"PI_GCS2_CreateController", 7, PI_GCS2_CreateControllerArgs};
|
|
static void PI_GCS2_CreateControllerCallFunc(const iocshArgBuf *args)
|
|
{
|
|
PI_GCS2_CreateController(args[0].sval, args[1].sval, args[2].ival, args[3].ival, args[4].ival, args[5].ival, args[6].ival);
|
|
}
|
|
|
|
|
|
static void PIasynDriverRegister(void)
|
|
{
|
|
iocshRegister(&PI_GCS2_CreateControllerDef, PI_GCS2_CreateControllerCallFunc);
|
|
}
|
|
|
|
extern "C" {
|
|
epicsExportRegistrar(PIasynDriverRegister);
|
|
}
|