Fully-featured version of the masterMACS driver
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
#---------------------------------------------
|
||||
# SINQ specific DB definitions
|
||||
#---------------------------------------------
|
||||
registrar(masterMacsRegister)
|
||||
registrar(masterMacsControllerRegister)
|
||||
registrar(masterMacsAxisRegister)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -70,7 +70,7 @@ class masterMacsAxis : public sinqAxis {
|
||||
double max_velocity, double acceleration);
|
||||
|
||||
/**
|
||||
* @brief Implementation of the `atFirstPoll` function from sinqAxis.
|
||||
* @brief Readout of some values from the controller at IOC startup
|
||||
*
|
||||
* The following steps are performed:
|
||||
* - Read out the motor status, motor position, velocity and acceleration
|
||||
@@ -79,7 +79,7 @@ class masterMacsAxis : public sinqAxis {
|
||||
*
|
||||
* @return asynStatus
|
||||
*/
|
||||
asynStatus atFirstPoll();
|
||||
asynStatus init();
|
||||
|
||||
/**
|
||||
* @brief Enable / disable the axis.
|
||||
@@ -100,9 +100,10 @@ class masterMacsAxis : public sinqAxis {
|
||||
protected:
|
||||
masterMacsController *pC_;
|
||||
double lastSetSpeed_;
|
||||
bool waitForHandshake_;
|
||||
time_t timeAtHandshake_;
|
||||
|
||||
asynStatus readConfig();
|
||||
bool initial_poll_;
|
||||
|
||||
/*
|
||||
The axis status and axis error of MasterMACS are given as an integer from
|
||||
@@ -134,10 +135,7 @@ class masterMacsAxis : public sinqAxis {
|
||||
*/
|
||||
bool switchedOn() { return axisStatus_[1]; }
|
||||
|
||||
/**
|
||||
* @brief Read the property from axisStatus_
|
||||
*/
|
||||
bool enabled() { return axisStatus_[2]; }
|
||||
// Bit 2 is unused
|
||||
|
||||
/**
|
||||
* @brief Read the property from axisStatus_
|
||||
@@ -162,12 +160,9 @@ class masterMacsAxis : public sinqAxis {
|
||||
/**
|
||||
* @brief Read the property from axisStatus_
|
||||
*/
|
||||
bool newMoveCommandWhileMoving() { return axisStatus_[7]; }
|
||||
bool warning() { return axisStatus_[7]; }
|
||||
|
||||
/**
|
||||
* @brief Read the property from axisStatus_
|
||||
*/
|
||||
bool isMoving() { return axisStatus_[8]; }
|
||||
// Bit 8 is unused
|
||||
|
||||
/**
|
||||
* @brief Read the property from axisStatus_
|
||||
@@ -209,6 +204,76 @@ class masterMacsAxis : public sinqAxis {
|
||||
boolean.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Read the property from axisError_
|
||||
*/
|
||||
bool shortCircuit() { return axisError_[1]; }
|
||||
|
||||
/**
|
||||
* @brief Read the property from axisError_
|
||||
*/
|
||||
bool encoderError() { return axisError_[2]; }
|
||||
|
||||
/**
|
||||
* @brief Read the property from axisError_
|
||||
*/
|
||||
bool followingError() { return axisError_[3]; }
|
||||
|
||||
/**
|
||||
* @brief Read the property from axisError_
|
||||
*/
|
||||
bool communicationError() { return axisError_[4]; }
|
||||
|
||||
/**
|
||||
* @brief Read the property from axisError_
|
||||
*/
|
||||
bool feedbackError() { return axisError_[5]; }
|
||||
|
||||
/**
|
||||
* @brief Read the property from axisError_
|
||||
*/
|
||||
bool positiveLimitSwitch() { return axisError_[6]; }
|
||||
|
||||
/**
|
||||
* @brief Read the property from axisError_
|
||||
*/
|
||||
bool negativeLimitSwitch() { return axisError_[7]; }
|
||||
|
||||
/**
|
||||
* @brief Read the property from axisError_
|
||||
*/
|
||||
bool positiveSoftwareLimit() { return axisError_[8]; }
|
||||
|
||||
/**
|
||||
* @brief Read the property from axisError_
|
||||
*/
|
||||
bool negativeSoftwareLimit() { return axisError_[9]; }
|
||||
|
||||
/**
|
||||
* @brief Read the property from axisError_
|
||||
*/
|
||||
bool overCurrent() { return axisError_[10]; }
|
||||
|
||||
/**
|
||||
* @brief Read the property from axisError_
|
||||
*/
|
||||
bool overTemperature() { return axisError_[11]; }
|
||||
|
||||
/**
|
||||
* @brief Read the property from axisError_
|
||||
*/
|
||||
bool overVoltage() { return axisError_[12]; }
|
||||
|
||||
/**
|
||||
* @brief Read the property from axisError_
|
||||
*/
|
||||
bool underVoltage() { return axisError_[13]; }
|
||||
|
||||
/**
|
||||
* @brief Read the property from axisError_
|
||||
*/
|
||||
bool stoFault() { return axisError_[15]; }
|
||||
|
||||
private:
|
||||
friend class masterMacsController;
|
||||
};
|
||||
|
||||
@@ -75,10 +75,9 @@ masterMacsController::masterMacsController(const char *portName,
|
||||
*/
|
||||
pasynOctetSyncIO->connect(ipPortConfigName, 0, &lowLevelPortUser_, NULL);
|
||||
if (status != asynSuccess || lowLevelPortUser_ == nullptr) {
|
||||
errlogPrintf(
|
||||
"%s => line %d:\nFATAL ERROR (cannot connect to MCU controller).\n"
|
||||
"Terminating IOC",
|
||||
__PRETTY_FUNCTION__, __LINE__);
|
||||
errlogPrintf("Controller \"%s\" => %s, line %d:\nFATAL ERROR (cannot "
|
||||
"connect to MCU controller).\nTerminating IOC",
|
||||
portName, __PRETTY_FUNCTION__, __LINE__);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
@@ -93,20 +92,21 @@ masterMacsController::masterMacsController(const char *portName,
|
||||
lowLevelPortUser_, message_from_device, strlen(message_from_device));
|
||||
if (status != asynSuccess) {
|
||||
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
|
||||
"%s => line %d:\nFATAL ERROR (setting input EOS failed "
|
||||
"with %s).\nTerminating IOC",
|
||||
__PRETTY_FUNCTION__, __LINE__, stringifyAsynStatus(status));
|
||||
"Controller \"%s\" => %s, line %d:\nFATAL ERROR (setting "
|
||||
"input EOS failed with %s).\nTerminating IOC",
|
||||
portName, __PRETTY_FUNCTION__, __LINE__,
|
||||
stringifyAsynStatus(status));
|
||||
pasynOctetSyncIO->disconnect(lowLevelPortUser_);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
status = callParamCallbacks();
|
||||
if (status != asynSuccess) {
|
||||
asynPrint(
|
||||
this->pasynUserSelf, ASYN_TRACE_ERROR,
|
||||
"%s => line %d:\nFATAL ERROR (executing ParamLib callbacks failed "
|
||||
"with %s).\nTerminating IOC",
|
||||
__PRETTY_FUNCTION__, __LINE__, stringifyAsynStatus(status));
|
||||
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
|
||||
"Controller \"%s\" => %s, line %d:\nFATAL ERROR (executing "
|
||||
"ParamLib callbacks failed with %s).\nTerminating IOC",
|
||||
portName, __PRETTY_FUNCTION__, __LINE__,
|
||||
stringifyAsynStatus(status));
|
||||
pasynOctetSyncIO->disconnect(lowLevelPortUser_);
|
||||
exit(-1);
|
||||
}
|
||||
@@ -144,10 +144,10 @@ masterMacsAxis *masterMacsController::castToAxis(asynMotorAxis *asynAxis) {
|
||||
// an instance of Axis
|
||||
masterMacsAxis *axis = dynamic_cast<masterMacsAxis *>(asynAxis);
|
||||
if (axis == nullptr) {
|
||||
asynPrint(
|
||||
this->pasynUserSelf, ASYN_TRACE_ERROR,
|
||||
"%s => line %d:\nAxis %d is not an instance of masterMacsAxis",
|
||||
__PRETTY_FUNCTION__, __LINE__, axis->axisNo_);
|
||||
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
|
||||
"Controller \"%s\", axis %d => %s, line %d:\nAxis is not "
|
||||
"an instance of masterMacsAxis",
|
||||
portName, axis->axisNo_, __PRETTY_FUNCTION__, __LINE__);
|
||||
}
|
||||
return axis;
|
||||
}
|
||||
@@ -248,9 +248,10 @@ asynStatus masterMacsController::writeRead(int axisNo, int tcpCmd,
|
||||
fullCommand[2] = lenWithMetadataSep.quot; // MSB
|
||||
|
||||
adjustForPrint(printableCommand, fullCommand, MAXBUF_);
|
||||
asynPrint(this->pasynUserSelf, ASYN_TRACEIO_DRIVER,
|
||||
"%s => line %d:\nSending command %s\n", __PRETTY_FUNCTION__,
|
||||
__LINE__, printableCommand);
|
||||
asynPrint(
|
||||
this->pasynUserSelf, ASYN_TRACEIO_DRIVER,
|
||||
"Controller \"%s\", axis %d => %s, line %d:\nSending command %s\n",
|
||||
portName, axisNo, __PRETTY_FUNCTION__, __LINE__, printableCommand);
|
||||
|
||||
// Send out the command
|
||||
status =
|
||||
@@ -276,25 +277,28 @@ asynStatus masterMacsController::writeRead(int axisNo, int tcpCmd,
|
||||
}
|
||||
} else {
|
||||
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
|
||||
"%s => line %d:\nError %s while reading from the "
|
||||
"controller\n",
|
||||
__PRETTY_FUNCTION__, __LINE__,
|
||||
"Controller \"%s\", axis %d => %s, line %d:\nError "
|
||||
"%s while reading from the controller\n",
|
||||
portName, axisNo, __PRETTY_FUNCTION__, __LINE__,
|
||||
stringifyAsynStatus(status));
|
||||
break;
|
||||
}
|
||||
|
||||
if (i + 1 == maxTrials && status == asynError) {
|
||||
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
|
||||
"%s => line %d:\nFailed %d times to get the "
|
||||
"correct response. Aborting read.\n",
|
||||
__PRETTY_FUNCTION__, __LINE__, maxTrials);
|
||||
asynPrint(
|
||||
this->pasynUserSelf, ASYN_TRACE_ERROR,
|
||||
"Controller \"%s\", axis %d => %s, line %d:\nFailed "
|
||||
"%d times to get the correct response. Aborting read.\n",
|
||||
portName, axisNo, __PRETTY_FUNCTION__, __LINE__, maxTrials);
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
|
||||
"%s => line %d:\nError %s while writing to the controller\n",
|
||||
__PRETTY_FUNCTION__, __LINE__, stringifyAsynStatus(status));
|
||||
"Controller \"%s\", axis %d => %s, line %d:\nError %s while "
|
||||
"writing to the controller\n",
|
||||
portName, axisNo, __PRETTY_FUNCTION__, __LINE__,
|
||||
stringifyAsynStatus(status));
|
||||
}
|
||||
|
||||
// MasterMACS needs a bit of time between messages, therefore thr program
|
||||
@@ -341,9 +345,10 @@ asynStatus masterMacsController::writeRead(int axisNo, int tcpCmd,
|
||||
// Log the overall status (communication successfull or not)
|
||||
if (status == asynSuccess) {
|
||||
adjustForPrint(printableResponse, fullResponse, MAXBUF_);
|
||||
asynPrint(lowLevelPortUser_, ASYN_TRACEIO_DRIVER,
|
||||
"%s => line %d:\nReturn value: %s\n", __PRETTY_FUNCTION__,
|
||||
__LINE__, printableResponse);
|
||||
asynPrint(
|
||||
lowLevelPortUser_, ASYN_TRACEIO_DRIVER,
|
||||
"Controller \"%s\", axis %d => %s, line %d:\nReturn value: %s\n",
|
||||
portName, axisNo, __PRETTY_FUNCTION__, __LINE__, printableResponse);
|
||||
pl_status = axis->setIntegerParam(this->motorStatusCommsError_, 0);
|
||||
} else {
|
||||
|
||||
@@ -355,26 +360,29 @@ asynStatus masterMacsController::writeRead(int axisNo, int tcpCmd,
|
||||
getIntegerParam(axisNo, motorStatusProblem_, &motorStatusProblem);
|
||||
if (pl_status != asynSuccess) {
|
||||
return paramLibAccessFailed(pl_status, "motorStatusProblem_",
|
||||
__PRETTY_FUNCTION__, __LINE__);
|
||||
axisNo, __PRETTY_FUNCTION__, __LINE__);
|
||||
}
|
||||
|
||||
if (motorStatusProblem == 0) {
|
||||
pl_status = axis->setStringParam(motorMessageText_, drvMessageText);
|
||||
if (pl_status != asynSuccess) {
|
||||
return paramLibAccessFailed(pl_status, "motorMessageText_",
|
||||
__PRETTY_FUNCTION__, __LINE__);
|
||||
axisNo, __PRETTY_FUNCTION__,
|
||||
__LINE__);
|
||||
}
|
||||
|
||||
pl_status = axis->setIntegerParam(motorStatusProblem_, 1);
|
||||
if (pl_status != asynSuccess) {
|
||||
return paramLibAccessFailed(pl_status, "motorStatusProblem",
|
||||
__PRETTY_FUNCTION__, __LINE__);
|
||||
axisNo, __PRETTY_FUNCTION__,
|
||||
__LINE__);
|
||||
}
|
||||
|
||||
pl_status = axis->setIntegerParam(motorStatusProblem_, 1);
|
||||
if (pl_status != asynSuccess) {
|
||||
return paramLibAccessFailed(pl_status, "motorStatusCommsError_",
|
||||
__PRETTY_FUNCTION__, __LINE__);
|
||||
axisNo, __PRETTY_FUNCTION__,
|
||||
__LINE__);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -414,9 +422,10 @@ asynStatus masterMacsController::parseResponse(
|
||||
} else if (fullResponse[i] == '\x15') {
|
||||
// NAK
|
||||
snprintf(drvMessageText, MAXBUF_, "Communication failed.");
|
||||
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
|
||||
"%s => line %d:\nCommunication failed\n",
|
||||
__PRETTY_FUNCTION__, __LINE__);
|
||||
asynPrint(this->pasynUserSelf, ASYN_TRACEIO_DRIVER,
|
||||
"Controller \"%s\", axis %d => %s, line "
|
||||
"%d:\nCommunication failed\n",
|
||||
portName, axisNo, __PRETTY_FUNCTION__, __LINE__);
|
||||
break;
|
||||
} else if (fullResponse[i] == '\x18') {
|
||||
// CAN
|
||||
@@ -424,9 +433,10 @@ asynStatus masterMacsController::parseResponse(
|
||||
"Tried to write with a read-only command. This is a "
|
||||
"bug, please call the support.");
|
||||
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
|
||||
"%s => line %d:\nTried to write with the read-only "
|
||||
"command %s\n",
|
||||
__PRETTY_FUNCTION__, __LINE__, printableCommand);
|
||||
"Controller \"%s\", axis %d => %s, line %d:\nTried to "
|
||||
"write with the read-only command %s\n",
|
||||
portName, axisNo, __PRETTY_FUNCTION__, __LINE__,
|
||||
printableCommand);
|
||||
responseValid = false;
|
||||
break;
|
||||
}
|
||||
@@ -452,11 +462,11 @@ asynStatus masterMacsController::parseResponse(
|
||||
adjustForPrint(printableCommand, fullCommand, MAXBUF_);
|
||||
adjustForPrint(printableResponse, fullResponse, MAXBUF_);
|
||||
|
||||
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
|
||||
"%s => line %d:\nMismatched response %s to "
|
||||
"command %s\n",
|
||||
__PRETTY_FUNCTION__, __LINE__, printableResponse,
|
||||
printableCommand);
|
||||
asynPrint(this->pasynUserSelf, ASYN_TRACEIO_DRIVER,
|
||||
"Controller \"%s\", axis %d => %s, line %d:\nMismatched "
|
||||
"response %s to command %s\n",
|
||||
portName, axisNo, __PRETTY_FUNCTION__, __LINE__,
|
||||
printableResponse, printableCommand);
|
||||
|
||||
snprintf(drvMessageText, MAXBUF_,
|
||||
"Mismatched response %s to command %s. Please call the "
|
||||
@@ -511,72 +521,6 @@ asynStatus masterMacsCreateController(const char *portName,
|
||||
return asynSuccess;
|
||||
}
|
||||
|
||||
/*
|
||||
C wrapper for the axis constructor. Please refer to the masterMacsAxis
|
||||
constructor documentation. The controller is read from the portName.
|
||||
*/
|
||||
asynStatus masterMacsCreateAxis(const char *portName, int axis) {
|
||||
masterMacsAxis *pAxis;
|
||||
|
||||
/*
|
||||
findAsynPortDriver is a asyn library FFI function which uses the C ABI.
|
||||
Therefore it returns a void pointer instead of e.g. a pointer to a
|
||||
superclass of the controller such as asynPortDriver. Type-safe upcasting
|
||||
via dynamic_cast is therefore not possible directly. However, we do know
|
||||
that the void pointer is either a pointer to asynPortDriver (if a driver
|
||||
with the specified name exists) or a nullptr. Therefore, we first do a
|
||||
nullptr check, then a cast to asynPortDriver and lastly a (typesafe)
|
||||
dynamic_upcast to Controller
|
||||
https://stackoverflow.com/questions/70906749/is-there-a-safe-way-to-cast-void-to-class-pointer-in-c
|
||||
*/
|
||||
void *ptr = findAsynPortDriver(portName);
|
||||
if (ptr == nullptr) {
|
||||
/*
|
||||
We can't use asynPrint here since this macro would require us
|
||||
to get a lowLevelPortUser_ from a pointer to an asynPortDriver.
|
||||
However, the given pointer is a nullptr and therefore doesn't
|
||||
have a lowLevelPortUser_! printf is an EPICS alternative which
|
||||
works w/o that, but doesn't offer the comfort provided
|
||||
by the asynTrace-facility
|
||||
*/
|
||||
errlogPrintf("%s => line %d:\nPort %s not found.", __PRETTY_FUNCTION__,
|
||||
__LINE__, portName);
|
||||
return asynError;
|
||||
}
|
||||
// Unsafe cast of the pointer to an asynPortDriver
|
||||
asynPortDriver *apd = (asynPortDriver *)(ptr);
|
||||
|
||||
// Safe downcast
|
||||
masterMacsController *pC = dynamic_cast<masterMacsController *>(apd);
|
||||
if (pC == nullptr) {
|
||||
errlogPrintf("%s => line %d:\ncontroller on port %s is not a "
|
||||
"masterMacsController.",
|
||||
__PRETTY_FUNCTION__, __LINE__, portName);
|
||||
return asynError;
|
||||
}
|
||||
|
||||
// Prevent manipulation of the controller from other threads while we
|
||||
// create the new axis.
|
||||
pC->lock();
|
||||
|
||||
/*
|
||||
We create a new instance of the axis, using the "new" keyword to
|
||||
allocate it on the heap while avoiding RAII.
|
||||
https://github.com/epics-modules/motor/blob/master/motorApp/MotorSrc/asynMotorController.cpp
|
||||
https://github.com/epics-modules/asyn/blob/master/asyn/asynPortDriver/asynPortDriver.cpp
|
||||
|
||||
The created object is registered in EPICS in its constructor and can
|
||||
safely be "leaked" here.
|
||||
*/
|
||||
#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
|
||||
#pragma GCC diagnostic ignored "-Wunused-variable"
|
||||
pAxis = new masterMacsAxis(pC, axis);
|
||||
|
||||
// Allow manipulation of the controller again
|
||||
pC->unlock();
|
||||
return asynSuccess;
|
||||
}
|
||||
|
||||
/*
|
||||
This is boilerplate code which is used to make the FFI functions
|
||||
CreateController and CreateAxis "known" to the IOC shell (iocsh).
|
||||
@@ -613,30 +557,13 @@ static void configMasterMacsCreateControllerCallFunc(const iocshArgBuf *args) {
|
||||
args[3].dval, args[4].dval, args[5].dval);
|
||||
}
|
||||
|
||||
/*
|
||||
Same procedure as for the CreateController function, but for the axis
|
||||
itself.
|
||||
*/
|
||||
static const iocshArg CreateAxisArg0 = {"Controller name (e.g. mmacs1)",
|
||||
iocshArgString};
|
||||
static const iocshArg CreateAxisArg1 = {"Axis number", iocshArgInt};
|
||||
static const iocshArg *const CreateAxisArgs[] = {&CreateAxisArg0,
|
||||
&CreateAxisArg1};
|
||||
static const iocshFuncDef configMasterMacsCreateAxis = {"masterMacsAxis", 2,
|
||||
CreateAxisArgs};
|
||||
static void configMasterMacsCreateAxisCallFunc(const iocshArgBuf *args) {
|
||||
masterMacsCreateAxis(args[0].sval, args[1].ival);
|
||||
}
|
||||
|
||||
// This function is made known to EPICS in masterMacs.dbd and is called by
|
||||
// EPICS in order to register both functions in the IOC shell
|
||||
static void masterMacsRegister(void) {
|
||||
static void masterMacsControllerRegister(void) {
|
||||
iocshRegister(&configMasterMacsCreateController,
|
||||
configMasterMacsCreateControllerCallFunc);
|
||||
iocshRegister(&configMasterMacsCreateAxis,
|
||||
configMasterMacsCreateAxisCallFunc);
|
||||
}
|
||||
epicsExportRegistrar(masterMacsRegister);
|
||||
epicsExportRegistrar(masterMacsControllerRegister);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
Reference in New Issue
Block a user