8 Commits
0.2.0 ... 0.5.0

Author SHA1 Message Date
e86c517fc7 Bumped required sinqMotor version to 0.11.0 2025-04-10 09:09:27 +02:00
f733718ee7 Using appropriate sinqMotor version 2025-04-09 15:26:45 +02:00
a8c3499dc5 Set required sinqMotor version to mathis_s 2025-04-04 13:31:49 +02:00
fe52245e38 Custom timeout for enable and position methods
Added a custom timeout for the enable command, as it takes quite a bit
of time for the motor controller to answer and we don't want to show a
premature communication timeout error. Also changed the code in order to
use the motorPosition() and setMotorPosition() methods instead of
directly accessing the paramLib.
2025-03-31 10:48:41 +02:00
a3e849f386 Changed to the "motorPosition" and "setMotorPosition" functions provided
by sinqMotor.
2025-03-28 14:53:04 +01:00
16564011a6 Added stop and error reset function for masterMacs 2025-03-19 15:07:09 +01:00
631ee46a50 Removed friend class declaration and replaced access to private,properties with accessors 2025-03-10 17:07:33 +01:00
cf9899062a Modified communication protocol for MCU software 2.0 2025-03-10 14:29:56 +01:00
6 changed files with 239 additions and 225 deletions

View File

@ -13,7 +13,7 @@ REQUIRED+=sinqMotor
motorBase_VERSION=7.2.2
# Specify the version of sinqMotor we want to build against
sinqMotor_VERSION=0.8.0
sinqMotor_VERSION=0.11.0
# These headers allow to depend on this library for derived drivers.
HEADERS += src/masterMacsAxis.h

View File

@ -1,9 +1,13 @@
# masterMacs
## <span style="color:red">Please read the documentation of sinqMotor first: https://git.psi.ch/sinq-epics-modules/sinqmotor</span>
## Overview
This is a driver for the masterMacs motion controller with the SINQ communication protocol. It is based on the sinqMotor shared library (https://git.psi.ch/sinq-epics-modules/sinqmotor). The header files contain detailed documentation for all public functions. The headers themselves are exported when building the library to allow other drivers to depend on this one.
Compatible to MasterMACS software 2.0.0.
## User guide
This driver is a standard sinqMotor-derived driver and does not need any specific configuration. For the general configuration, please see https://git.psi.ch/sinq-epics-modules/sinqmotor/-/blob/main/README.md.

View File

@ -78,13 +78,13 @@ masterMacsAxis::masterMacsAxis(masterMacsController *pC, int axisNo)
bounds, asynMotorAxis prints an error. However, we want the IOC creation to
stop completely, since this is a configuration error.
*/
if (axisNo >= pC->numAxes_) {
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
if (axisNo >= pC->numAxes()) {
asynPrint(pC_->asynUserSelf(), ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d:: FATAL ERROR: "
"Axis index %d must be smaller than the total number of axes "
"%d. Call the support.",
pC_->portName, axisNo, __PRETTY_FUNCTION__, __LINE__, axisNo_,
pC->numAxes_);
pC->numAxes());
exit(-1);
}
@ -108,10 +108,10 @@ masterMacsAxis::masterMacsAxis(masterMacsController *pC, int axisNo)
timeAtHandshake_ = 0;
// masterMacs motors can always be disabled
status = pC_->setIntegerParam(axisNo_, pC_->motorCanDisable_, 1);
status = pC_->setIntegerParam(axisNo_, pC_->motorCanDisable(), 1);
if (status != asynSuccess) {
asynPrint(
pC_->pasynUserSelf, ASYN_TRACE_ERROR,
pC_->asynUserSelf(), ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d:\nFATAL ERROR "
"(setting a parameter value failed with %s)\n. Terminating IOC",
pC_->portName, axisNo, __PRETTY_FUNCTION__, __LINE__,
@ -120,10 +120,10 @@ masterMacsAxis::masterMacsAxis(masterMacsController *pC, int axisNo)
}
// Assume that the motor is initially not moving
status = pC_->setIntegerParam(axisNo_, pC_->motorStatusMoving_, false);
status = pC_->setIntegerParam(axisNo_, pC_->motorStatusMoving(), false);
if (status != asynSuccess) {
asynPrint(
pC_->pasynUserSelf, ASYN_TRACE_ERROR,
pC_->asynUserSelf(), ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d:\nFATAL ERROR "
"(setting a parameter value failed with %s)\n. Terminating IOC",
pC_->portName, axisNo, __PRETTY_FUNCTION__, __LINE__,
@ -163,11 +163,11 @@ asynStatus masterMacsAxis::init() {
time_t now = time(NULL);
time_t maxInitTime = 60;
while (1) {
pl_status = pC_->getDoubleParam(axisNo_, pC_->motorRecResolution_,
pl_status = pC_->getDoubleParam(axisNo_, pC_->motorRecResolution(),
&motorRecResolution);
if (pl_status == asynParamUndefined) {
if (now + maxInitTime < time(NULL)) {
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
asynPrint(pC_->asynUserSelf(), ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line "
"%d\nInitializing the parameter library failed.\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__,
@ -190,7 +190,7 @@ asynStatus masterMacsAxis::init() {
}
nvals = sscanf(response, "%lf", &motorPosition);
if (nvals != 1) {
return pC_->errMsgCouldNotParseResponse("R12", response, axisNo_,
return pC_->couldNotParseResponse("R12", response, axisNo_,
__PRETTY_FUNCTION__, __LINE__);
}
@ -201,7 +201,7 @@ asynStatus masterMacsAxis::init() {
}
nvals = sscanf(response, "%lf", &motorVelocity);
if (nvals != 1) {
return pC_->errMsgCouldNotParseResponse("R05", response, axisNo_,
return pC_->couldNotParseResponse("R05", response, axisNo_,
__PRETTY_FUNCTION__, __LINE__);
}
@ -212,7 +212,7 @@ asynStatus masterMacsAxis::init() {
}
nvals = sscanf(response, "%lf", &motorVmax);
if (nvals != 1) {
return pC_->errMsgCouldNotParseResponse("R26", response, axisNo_,
return pC_->couldNotParseResponse("R26", response, axisNo_,
__PRETTY_FUNCTION__, __LINE__);
}
@ -223,7 +223,7 @@ asynStatus masterMacsAxis::init() {
}
nvals = sscanf(response, "%lf", &motorAccel);
if (nvals != 1) {
return pC_->errMsgCouldNotParseResponse("R06", response, axisNo_,
return pC_->couldNotParseResponse("R06", response, axisNo_,
__PRETTY_FUNCTION__, __LINE__);
}
@ -232,15 +232,10 @@ asynStatus masterMacsAxis::init() {
// MasterMACS.
lastSetSpeed_ = motorVelocity;
// Transform from motor to parameter library coordinates
motorPosition = motorPosition / motorRecResolution;
// Store these values in the parameter library
pl_status =
pC_->setDoubleParam(axisNo_, pC_->motorPosition_, motorPosition);
// Store the motor position in the parameter library
pl_status = setMotorPosition(motorPosition);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorPosition_", axisNo_,
__PRETTY_FUNCTION__, __LINE__);
return pl_status;
}
// Write to the motor record fields
@ -264,7 +259,7 @@ asynStatus masterMacsAxis::init() {
// If we can't communicate with the parameter library, it doesn't
// make sense to try and upstream this to the user -> Just log the
// error
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
asynPrint(pC_->asynUserSelf(), ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line "
"%d:\ncallParamCallbacks failed with %s.\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
@ -303,6 +298,34 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
// Are we currently waiting for a handshake?
if (waitForHandshake_) {
// Check if the handshake takes too long and communicate an error in
// this case. A handshake should not take more than 5 seconds.
time_t currentTime = time(NULL);
bool timedOut = (currentTime > timeAtHandshake_ + 5);
if (pC_->getMsgPrintControl().shouldBePrinted(
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, timedOut,
pC_->asynUserSelf())) {
asynPrint(pC_->asynUserSelf(), ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d\nAsked for a "
"handshake at %ld s and didn't get a positive reply yet "
"(current time is %ld s).\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
timeAtHandshake_, currentTime);
}
if (timedOut) {
pl_status =
setStringParam(pC_->motorMessageText(),
"Timed out while waiting for a handshake");
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorMessageText_",
axisNo_, __PRETTY_FUNCTION__,
__LINE__);
}
}
pC_->read(axisNo_, 86, response);
if (rw_status != asynSuccess) {
return rw_status;
@ -310,8 +333,8 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
nvals = sscanf(response, "%lf", &handshakePerformed);
if (nvals != 1) {
return pC_->errMsgCouldNotParseResponse(
"R86", response, axisNo_, __PRETTY_FUNCTION__, __LINE__);
return pC_->couldNotParseResponse("R86", response, axisNo_,
__PRETTY_FUNCTION__, __LINE__);
}
if (handshakePerformed == 1.0) {
@ -323,14 +346,14 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
// poll. This is already part of the movement procedure.
*moving = true;
pl_status = setIntegerParam(pC_->motorStatusMoving_, *moving);
pl_status = setIntegerParam(pC_->motorStatusMoving(), *moving);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status,
"motorStatusMoving_", axisNo_,
__PRETTY_FUNCTION__, __LINE__);
}
pl_status = setIntegerParam(pC_->motorStatusDone_, !(*moving));
pl_status = setIntegerParam(pC_->motorStatusDone(), !(*moving));
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorStatusDone_",
axisNo_, __PRETTY_FUNCTION__,
@ -342,7 +365,7 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
}
// Motor resolution from parameter library
pl_status = pC_->getDoubleParam(axisNo_, pC_->motorRecResolution_,
pl_status = pC_->getDoubleParam(axisNo_, pC_->motorRecResolution(),
&motorRecResolution);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorRecResolution_",
@ -351,17 +374,11 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
}
// Read the previous motor position
pl_status =
pC_->getDoubleParam(axisNo_, pC_->motorPosition_, &previousPosition);
pl_status = motorPosition(&previousPosition);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorPosition_", axisNo_,
__PRETTY_FUNCTION__, __LINE__);
return pl_status;
}
// Transform from EPICS to motor coordinates (see comment in
// masterMacsAxis::readConfig())
previousPosition = previousPosition * motorRecResolution;
// Update the axis status
rw_status = readAxisStatus();
if (rw_status != asynSuccess) {
@ -383,7 +400,7 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
}
nvals = sscanf(response, "%lf", &currentPosition);
if (nvals != 1) {
return pC_->errMsgCouldNotParseResponse("R12", response, axisNo_,
return pC_->couldNotParseResponse("R12", response, axisNo_,
__PRETTY_FUNCTION__, __LINE__);
}
@ -403,17 +420,17 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
since this information is not reliable.
*/
if (communicationError()) {
if (pC_->msgPrintControl_.shouldBePrinted(keyError, true,
pC_->pasynUserSelf)) {
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
if (pC_->getMsgPrintControl().shouldBePrinted(
keyError, true, pC_->asynUserSelf())) {
asynPrint(pC_->asynUserSelf(), ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line "
"%d\nCommunication error.%s\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
pC_->msgPrintControl_.getSuffix());
pC_->getMsgPrintControl().getSuffix());
}
pl_status =
setStringParam(pC_->motorMessageText_,
setStringParam(pC_->motorMessageText(),
"Communication error between PC and motor "
"controller. Please call the support.");
if (pl_status != asynSuccess) {
@ -561,18 +578,18 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
}
if (strlen(shellMessage) > 0) {
if (pC_->msgPrintControl_.shouldBePrinted(keyError, true,
pC_->pasynUserSelf)) {
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
if (pC_->getMsgPrintControl().shouldBePrinted(
keyError, true, pC_->asynUserSelf())) {
asynPrint(pC_->asynUserSelf(), ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line "
"%d\n%s.%s\n",
"%d\n%s%s\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__,
__LINE__, shellMessage,
pC_->msgPrintControl_.getSuffix());
pC_->getMsgPrintControl().getSuffix());
}
}
pl_status = setStringParam(pC_->motorMessageText_, userMessage);
pl_status = setStringParam(pC_->motorMessageText(), userMessage);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorMessageText_",
axisNo_, __PRETTY_FUNCTION__,
@ -580,7 +597,7 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
}
}
} else {
pC_->msgPrintControl_.resetCount(keyError);
pC_->getMsgPrintControl().resetCount(keyError, pC_->asynUserSelf());
}
// Read out the limits, if the motor is not moving
@ -591,8 +608,8 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
}
nvals = sscanf(response, "%lf", &lowLimit);
if (nvals != 1) {
return pC_->errMsgCouldNotParseResponse(
"R34", response, axisNo_, __PRETTY_FUNCTION__, __LINE__);
return pC_->couldNotParseResponse("R34", response, axisNo_,
__PRETTY_FUNCTION__, __LINE__);
}
rw_status = pC_->read(axisNo_, 33, response);
@ -601,8 +618,8 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
}
nvals = sscanf(response, "%lf", &highLimit);
if (nvals != 1) {
return pC_->errMsgCouldNotParseResponse(
"R33", response, axisNo_, __PRETTY_FUNCTION__, __LINE__);
return pC_->couldNotParseResponse("R33", response, axisNo_,
__PRETTY_FUNCTION__, __LINE__);
}
/*
@ -618,7 +635,7 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
directly, but need to shrink them a bit. In this case, we're shrinking
them by 0.1 mm or 0.1 degree (depending on the axis type) on both sides.
*/
pl_status = pC_->getDoubleParam(axisNo_, pC_->motorLimitsOffset_,
pl_status = pC_->getDoubleParam(axisNo_, pC_->motorLimitsOffset(),
&limitsOffset);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorLimitsOffset_",
@ -628,15 +645,15 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
highLimit = highLimit - limitsOffset;
lowLimit = lowLimit + limitsOffset;
pl_status = pC_->setDoubleParam(axisNo_, pC_->motorHighLimitFromDriver_,
highLimit);
pl_status = pC_->setDoubleParam(
axisNo_, pC_->motorHighLimitFromDriver(), highLimit);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(
pl_status, "motorHighLimitFromDriver_", axisNo_,
__PRETTY_FUNCTION__, __LINE__);
}
pl_status = pC_->setDoubleParam(axisNo_, pC_->motorLowLimitFromDriver_,
pl_status = pC_->setDoubleParam(axisNo_, pC_->motorLowLimitFromDriver(),
lowLimit);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorLowLimit_",
@ -646,7 +663,7 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
}
// Update the enable PV
pl_status = setIntegerParam(pC_->motorEnableRBV_,
pl_status = setIntegerParam(pC_->motorEnableRBV(),
readyToBeSwitchedOn() && switchedOn());
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorEnableRBV_", axisNo_,
@ -664,41 +681,36 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
// Update the parameter library
if (hasError) {
pl_status = setIntegerParam(pC_->motorStatusProblem_, true);
pl_status = setIntegerParam(pC_->motorStatusProblem(), true);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorStatusProblem_",
axisNo_, __PRETTY_FUNCTION__,
__LINE__);
}
}
pl_status = setIntegerParam(pC_->motorStatusMoving_, *moving);
pl_status = setIntegerParam(pC_->motorStatusMoving(), *moving);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorStatusMoving_",
axisNo_, __PRETTY_FUNCTION__,
__LINE__);
}
pl_status = setIntegerParam(pC_->motorStatusDone_, !(*moving));
pl_status = setIntegerParam(pC_->motorStatusDone(), !(*moving));
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorStatusDone_", axisNo_,
__PRETTY_FUNCTION__, __LINE__);
}
pl_status = setIntegerParam(pC_->motorStatusDirection_, direction);
pl_status = setIntegerParam(pC_->motorStatusDirection(), direction);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorStatusDirection_",
axisNo_, __PRETTY_FUNCTION__,
__LINE__);
}
// Transform from motor to EPICS coordinates (see comment in
// masterMacsAxis::init())
currentPosition = currentPosition / motorRecResolution;
pl_status = setDoubleParam(pC_->motorPosition_, currentPosition);
pl_status = setMotorPosition(currentPosition);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorPosition_", axisNo_,
__PRETTY_FUNCTION__, __LINE__);
return pl_status;
}
return poll_status;
@ -723,13 +735,13 @@ asynStatus masterMacsAxis::doMove(double position, int relative,
// =========================================================================
pl_status = pC_->getIntegerParam(axisNo_, pC_->motorEnableRBV_, &enabled);
pl_status = pC_->getIntegerParam(axisNo_, pC_->motorEnableRBV(), &enabled);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "enableMotorRBV_", axisNo_,
__PRETTY_FUNCTION__, __LINE__);
}
pl_status = pC_->getDoubleParam(axisNo_, pC_->motorRecResolution_,
pl_status = pC_->getDoubleParam(axisNo_, pC_->motorRecResolution(),
&motorRecResolution);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorRecResolution_",
@ -738,7 +750,7 @@ asynStatus masterMacsAxis::doMove(double position, int relative,
}
if (enabled == 0) {
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
asynPrint(pC_->asynUserSelf(), ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d:\nAxis is "
"disabled.\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__);
@ -750,12 +762,12 @@ asynStatus masterMacsAxis::doMove(double position, int relative,
motorVelocity = maxVelocity * motorRecResolution;
asynPrint(
pC_->pasynUserSelf, ASYN_TRACE_FLOW,
pC_->asynUserSelf(), ASYN_TRACE_FLOW,
"Controller \"%s\", axis %d => %s, line %d:\nMove to position %lf.\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, position);
// Check if the speed is allowed to be changed
pl_status = pC_->getIntegerParam(axisNo_, pC_->motorCanSetSpeed_,
pl_status = pC_->getIntegerParam(axisNo_, pC_->motorCanSetSpeed(),
&motorCanSetSpeed);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorCanSetSpeed_",
@ -766,7 +778,7 @@ asynStatus masterMacsAxis::doMove(double position, int relative,
// Initialize the movement handshake
rw_status = pC_->write(axisNo_, 86, "0");
if (rw_status != asynSuccess) {
pl_status = setIntegerParam(pC_->motorStatusProblem_, true);
pl_status = setIntegerParam(pC_->motorStatusProblem(), true);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorStatusProblem_",
axisNo_, __PRETTY_FUNCTION__,
@ -784,7 +796,7 @@ asynStatus masterMacsAxis::doMove(double position, int relative,
snprintf(value, sizeof(value), "%lf", motorVelocity);
rw_status = pC_->write(axisNo_, 05, value);
if (rw_status != asynSuccess) {
pl_status = setIntegerParam(pC_->motorStatusProblem_, true);
pl_status = setIntegerParam(pC_->motorStatusProblem(), true);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status,
"motorStatusProblem_", axisNo_,
@ -793,7 +805,7 @@ asynStatus masterMacsAxis::doMove(double position, int relative,
return rw_status;
}
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_FLOW,
asynPrint(pC_->asynUserSelf(), ASYN_TRACE_FLOW,
"Controller \"%s\", axis %d => %s, line %d:\nSetting speed "
"to %lf.\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
@ -804,7 +816,7 @@ asynStatus masterMacsAxis::doMove(double position, int relative,
snprintf(value, sizeof(value), "%lf", motorCoordinatesPosition);
rw_status = pC_->write(axisNo_, 02, value);
if (rw_status != asynSuccess) {
pl_status = setIntegerParam(pC_->motorStatusProblem_, true);
pl_status = setIntegerParam(pC_->motorStatusProblem(), true);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorStatusProblem_",
axisNo_, __PRETTY_FUNCTION__,
@ -820,7 +832,7 @@ asynStatus masterMacsAxis::doMove(double position, int relative,
rw_status = pC_->write(axisNo_, 00, "1");
}
if (rw_status != asynSuccess) {
pl_status = setIntegerParam(pC_->motorStatusProblem_, true);
pl_status = setIntegerParam(pC_->motorStatusProblem(), true);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorStatusProblem_",
axisNo_, __PRETTY_FUNCTION__,
@ -855,7 +867,29 @@ asynStatus masterMacsAxis::stop(double acceleration) {
rw_status = pC_->write(axisNo_, 00, "8");
if (rw_status != asynSuccess) {
pl_status = setIntegerParam(pC_->motorStatusProblem_, true);
pl_status = setIntegerParam(pC_->motorStatusProblem(), true);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorStatusProblem_",
axisNo_, __PRETTY_FUNCTION__,
__LINE__);
}
}
return rw_status;
}
asynStatus masterMacsAxis::doReset() {
// Status of read-write-operations of ASCII commands to the controller
asynStatus rw_status = asynSuccess;
// Status of parameter library operations
asynStatus pl_status = asynSuccess;
// =========================================================================
rw_status = pC_->write(axisNo_, 17, "");
if (rw_status != asynSuccess) {
pl_status = setIntegerParam(pC_->motorStatusProblem(), true);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorStatusProblem_",
axisNo_, __PRETTY_FUNCTION__,
@ -882,7 +916,7 @@ asynStatus masterMacsAxis::doHome(double min_velocity, double max_velocity,
// =========================================================================
pl_status = pC_->getStringParam(axisNo_, pC_->encoderType_,
pl_status = pC_->getStringParam(axisNo_, pC_->encoderType(),
sizeof(response), response);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "encoderType_", axisNo_,
@ -895,7 +929,7 @@ asynStatus masterMacsAxis::doHome(double min_velocity, double max_velocity,
// Initialize the movement handshake
rw_status = pC_->write(axisNo_, 86, "0");
if (rw_status != asynSuccess) {
pl_status = setIntegerParam(pC_->motorStatusProblem_, true);
pl_status = setIntegerParam(pC_->motorStatusProblem(), true);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status,
"motorStatusProblem_", axisNo_,
@ -944,7 +978,7 @@ asynStatus masterMacsAxis::readEncoderType() {
nvals = sscanf(response, "%d", &encoder_id);
if (nvals != 1) {
return pC_->errMsgCouldNotParseResponse(command, response, axisNo_,
return pC_->couldNotParseResponse(command, response, axisNo_,
__PRETTY_FUNCTION__, __LINE__);
}
@ -955,9 +989,9 @@ asynStatus masterMacsAxis::readEncoderType() {
2=SSI (Absolute encoder with BiSS interface)
*/
if (encoder_id == 0) {
pl_status = setStringParam(pC_->encoderType_, IncrementalEncoder);
pl_status = setStringParam(pC_->encoderType(), IncrementalEncoder);
} else {
pl_status = setStringParam(pC_->encoderType_, AbsoluteEncoder);
pl_status = setStringParam(pC_->encoderType(), AbsoluteEncoder);
}
if (pl_status != asynSuccess) {
@ -989,13 +1023,13 @@ asynStatus masterMacsAxis::enable(bool on) {
// axis instead of "moving", since status -6 is also moving, but the
// motor can actually be disabled in this state!
if (moving) {
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
asynPrint(pC_->asynUserSelf(), ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d:\nAxis is not "
"idle and can therefore not be disabled.\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__);
pl_status =
setStringParam(pC_->motorMessageText_,
setStringParam(pC_->motorMessageText(),
"Axis cannot be disabled while it is moving.");
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorMessageText_",
@ -1011,7 +1045,7 @@ asynStatus masterMacsAxis::enable(bool on) {
if ((readyToBeSwitchedOn() && switchedOn()) == on) {
asynPrint(
pC_->pasynUserSelf, ASYN_TRACE_WARNING,
pC_->asynUserSelf(), ASYN_TRACE_WARNING,
"Controller \"%s\", axis %d => %s, line %d:\nAxis is already %s.\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
on ? "enabled" : "disabled");
@ -1020,21 +1054,25 @@ asynStatus masterMacsAxis::enable(bool on) {
// Enable / disable the axis if it is not moving
snprintf(value, sizeof(value), "%d", on);
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_FLOW,
asynPrint(pC_->asynUserSelf(), ASYN_TRACE_FLOW,
"Controller \"%s\", axis %d => %s, line %d:\n%s axis.\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
on ? "Enable" : "Disable");
if (on == 0) {
pl_status = setStringParam(pC_->motorMessageText_, "Disabling ...");
pl_status = setStringParam(pC_->motorMessageText(), "Disabling ...");
} else {
pl_status = setStringParam(pC_->motorMessageText_, "Enabling ...");
pl_status = setStringParam(pC_->motorMessageText(), "Enabling ...");
}
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorMessageText_",
axisNo_, __PRETTY_FUNCTION__,
__LINE__);
}
rw_status = pC_->write(axisNo_, 04, value);
// The answer to the enable command on MasterMACS might take some time,
// hence we wait for a custom timespan in seconds instead of
// pC_->comTimeout_
rw_status = pC_->write(axisNo_, 04, value, 1.0);
if (rw_status != asynSuccess) {
return rw_status;
}
@ -1061,7 +1099,7 @@ asynStatus masterMacsAxis::enable(bool on) {
// Failed to change axis status within timeout_enable_disable => Send a
// corresponding message
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
asynPrint(pC_->asynUserSelf(), ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d:\nFailed to %s axis "
"within %d seconds\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
@ -1070,7 +1108,7 @@ asynStatus masterMacsAxis::enable(bool on) {
// Output message to user
snprintf(value, sizeof(value), "Failed to %s within %d seconds",
on ? "enable" : "disable", timeout_enable_disable);
pl_status = setStringParam(pC_->motorMessageText_, "Enabling ...");
pl_status = setStringParam(pC_->motorMessageText(), "Enabling ...");
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorMessageText_",
axisNo_, __PRETTY_FUNCTION__,
@ -1104,8 +1142,8 @@ asynStatus masterMacsAxis::readAxisStatus() {
float axisStatus = 0;
int nvals = sscanf(response, "%f", &axisStatus);
if (nvals != 1) {
return pC_->errMsgCouldNotParseResponse(
"R10", response, axisNo_, __PRETTY_FUNCTION__, __LINE__);
return pC_->couldNotParseResponse("R10", response, axisNo_,
__PRETTY_FUNCTION__, __LINE__);
}
axisStatus_ = toBitset(axisStatus);
@ -1125,8 +1163,8 @@ asynStatus masterMacsAxis::readAxisError() {
float axisError = 0;
int nvals = sscanf(response, "%f", &axisError);
if (nvals != 1) {
return pC_->errMsgCouldNotParseResponse(
"R11", response, axisNo_, __PRETTY_FUNCTION__, __LINE__);
return pC_->couldNotParseResponse("R11", response, axisNo_,
__PRETTY_FUNCTION__, __LINE__);
}
axisError_ = toBitset(axisError);
}

View File

@ -24,15 +24,6 @@ class masterMacsAxis : public sinqAxis {
*/
virtual ~masterMacsAxis();
/**
* @brief Implementation of the `stop` function from asynMotorAxis
*
* @param acceleration Acceleration ACCEL from the motor record. This
* value is currently not used.
* @return asynStatus
*/
asynStatus stop(double acceleration);
/**
* @brief Implementation of the `doHome` function from sinqAxis. The
* parameters are described in the documentation of `sinqAxis::doHome`.
@ -69,6 +60,23 @@ class masterMacsAxis : public sinqAxis {
asynStatus doMove(double position, int relative, double min_velocity,
double max_velocity, double acceleration);
/**
* @brief Implementation of the `stop` function from asynMotorAxis
*
* @param acceleration Acceleration ACCEL from the motor record. This
* value is currently not used.
* @return asynStatus
*/
asynStatus stop(double acceleration);
/**
* @brief Implementation of the `doReset` function from sinqAxis.
*
* @param on
* @return asynStatus
*/
asynStatus doReset();
/**
* @brief Readout of some values from the controller at IOC startup
*
@ -273,9 +281,6 @@ class masterMacsAxis : public sinqAxis {
* @brief Read the property from axisError_
*/
bool stoFault() { return axisError_[15]; }
private:
friend class masterMacsController;
};
#endif

View File

@ -49,14 +49,10 @@ masterMacsController::masterMacsController(const char *portName,
int numAxes, double movingPollPeriod,
double idlePollPeriod,
double comTimeout)
: sinqController(
portName, ipPortConfigName, numAxes, movingPollPeriod, idlePollPeriod,
/*
The following parameter library entries are added in this driver:
- REREAD_ENCODER_POSITION
- READ_CONFIG
*/
NUM_masterMacs_DRIVER_PARAMS)
: sinqController(portName, ipPortConfigName, numAxes, movingPollPeriod,
idlePollPeriod,
// No additional parameter library entries
0)
{
@ -64,39 +60,25 @@ masterMacsController::masterMacsController(const char *portName,
asynStatus status = asynSuccess;
// Initialization of all member variables
lowLevelPortUser_ = nullptr;
comTimeout_ = comTimeout;
// =========================================================================;
/*
We try to connect to the port via the port name provided by the constructor.
If this fails, the function is terminated via exit
*/
pasynOctetSyncIO->connect(ipPortConfigName, 0, &lowLevelPortUser_, NULL);
if (status != asynSuccess || lowLevelPortUser_ == nullptr) {
errlogPrintf("Controller \"%s\" => %s, line %d:\nFATAL ERROR (cannot "
"connect to MCU controller).\nTerminating IOC",
portName, __PRETTY_FUNCTION__, __LINE__);
exit(-1);
}
// =========================================================================
/*
Define the end-of-string of a message coming from the device to EPICS.
It is not necessary to append a terminator to outgoing messages, since
the message length is encoded in the message header in the getSetResponse
method.
the message length is encoded in the message header.
*/
const char *message_from_device = "\x03"; // Hex-code for ETX
status = pasynOctetSyncIO->setInputEos(
lowLevelPortUser_, message_from_device, strlen(message_from_device));
const char *message_from_device = "\x0D"; // Hex-code for CR
status = pasynOctetSyncIO->setInputEos(ipPortUser_, message_from_device,
strlen(message_from_device));
if (status != asynSuccess) {
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\" => %s, line %d:\nFATAL ERROR (setting "
"input EOS failed with %s).\nTerminating IOC",
portName, __PRETTY_FUNCTION__, __LINE__,
stringifyAsynStatus(status));
pasynOctetSyncIO->disconnect(lowLevelPortUser_);
pasynOctetSyncIO->disconnect(ipPortUser_);
exit(-1);
}
@ -107,7 +89,7 @@ masterMacsController::masterMacsController(const char *portName,
"ParamLib callbacks failed with %s).\nTerminating IOC",
portName, __PRETTY_FUNCTION__, __LINE__,
stringifyAsynStatus(status));
pasynOctetSyncIO->disconnect(lowLevelPortUser_);
pasynOctetSyncIO->disconnect(ipPortUser_);
exit(-1);
}
}
@ -131,18 +113,19 @@ masterMacsAxis *masterMacsController::getMasterMacsAxis(int axisNo) {
return dynamic_cast<masterMacsAxis *>(asynAxis);
}
asynStatus masterMacsController::read(int axisNo, int tcpCmd, char *response) {
asynStatus masterMacsController::read(int axisNo, int tcpCmd, char *response,
double comTimeout) {
return writeRead(axisNo, tcpCmd, NULL, response);
}
asynStatus masterMacsController::write(int axisNo, int tcpCmd,
const char *payload) {
return writeRead(axisNo, tcpCmd, payload, NULL);
const char *payload, double comTimeout) {
return writeRead(axisNo, tcpCmd, payload, NULL, comTimeout);
}
asynStatus masterMacsController::writeRead(int axisNo, int tcpCmd,
const char *payload,
char *response) {
const char *payload, char *response,
double comTimeout) {
// Definition of local variables.
asynStatus status = asynSuccess;
@ -174,94 +157,93 @@ asynStatus masterMacsController::writeRead(int axisNo, int tcpCmd,
// =========================================================================
// Check if a timeout has been given
if (comTimeout < 0.0) {
comTimeout = comTimeout_;
}
masterMacsAxis *axis = getMasterMacsAxis(axisNo);
if (axis == nullptr) {
// We already did the error logging directly in getAxis
return asynError;
}
/*
PSI SINQ uses a custom protocol which is described in
PSI_TCP_Interface_V1-8.pdf (p. // 4-17).
A special case is the message length, which is specified by two bytes
LSB and MSB: MSB = message length / 256 LSB = message length % 256. For
example, a message length of 47 chars would result in MSB = 0, LSB = 47,
whereas a message length of 356 would result in MSB = 1, LSB = 100.
The full protocol looks as follows:
0x05 -> Start of protocol frame ENQ
[LSB]
[MSB]
0x19 -> Data type PDO1
value [Actual message] It is not necessary to append a terminator, since
this protocol encodes the message length in LSB and MSB.
0x0D -> Carriage return (ASCII alias \r)
0x03 -> End of text ETX
*/
fullCommand[0] = '\x05'; // ENQ
fullCommand[1] = 1; // Placeholder value, can be anything other than 0
fullCommand[2] = 1; // Placeholder value, can be anything other than 0
fullCommand[3] = '\x19'; // PD01
// Create the command and add CR and ETX at the end
if (isRead) {
snprintf(&fullCommand[4], MAXBUF_ - 4, "%dR%02d\x0D\x03", axisNo,
tcpCmd);
snprintf(fullCommand, MAXBUF_ - 1, "%dR%02d\x0D", axisNo, tcpCmd);
} else {
snprintf(&fullCommand[4], MAXBUF_ - 4, "%dS%02d=%s\x0D\x03", axisNo,
tcpCmd, payload);
if (strlen(payload) == 0) {
snprintf(fullCommand, MAXBUF_ - 1, "%dS%02d\x0D", axisNo, tcpCmd);
} else {
snprintf(fullCommand, MAXBUF_ - 1, "%dS%02d=%s\x0D", axisNo, tcpCmd,
payload);
}
}
// Calculate the command length
const size_t fullCommandLength = strlen(fullCommand);
// Length of the command without ENQ and ETX
const size_t lenWithMetadata = fullCommandLength - 2;
// Perform both division and modulo operation at once.
div_t lenWithMetadataSep = std::div(lenWithMetadata, 256);
// Now set the actual command length
fullCommand[1] = lenWithMetadataSep.rem; // LSB
fullCommand[2] = lenWithMetadataSep.quot; // MSB
adjustForPrint(printableCommand, fullCommand, MAXBUF_);
// Send out the command
status =
pasynOctetSyncIO->write(lowLevelPortUser_, fullCommand,
fullCommandLength, comTimeout_, &nbytesOut);
status = pasynOctetSyncIO->write(ipPortUser_, fullCommand,
fullCommandLength, comTimeout, &nbytesOut);
if (status != asynSuccess) {
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d:\nError "
"%s while writing to the controller\n",
portName, axisNo, __PRETTY_FUNCTION__, __LINE__,
stringifyAsynStatus(status));
}
msgPrintControlKey writeKey =
msgPrintControlKey(portName, axisNo, __PRETTY_FUNCTION__, __LINE__);
if (status == asynSuccess) {
msgPrintControl_.resetCount(writeKey);
msgPrintControl_.resetCount(writeKey, pasynUserSelf);
// Try to read the answer repeatedly
int maxTrials = 2;
for (int i = 0; i < maxTrials; i++) {
status =
pasynOctetSyncIO->read(lowLevelPortUser_, fullResponse, MAXBUF_,
comTimeout_, &nbytesIn, &eomReason);
/*
A typical response of the MasterMacs controller looks like this:
(.. TCP Header ...) 31 20 52 20 31 31 3d 35 31 32 2e 30 30 30 30 06
0d 00 00 00 00 00 00 00 00 00 00 00 00 00
The message terminator is the carriage return (0d), which is
specified in the controller constructor as the end-of-string (EOS)
character. However, we also need to remove the buffer zeros at the
end, because they will otherwise confuse the
pasynOctetSyncIO->read() during the next call. The
pasynOctetSyncIO->flush() method does exactly that: it takes all
bytes it can find in the socket and throws them away. We don't check
the return value of flush(), because it is always asynSuccess (see
https://www.slac.stanford.edu/grp/lcls/controls/global/doc/epics-modules/R3-14-12/asyn/asyn-R4-18-lcls2/asyn/interfaces/asynOctetBase.c)
*/
status = pasynOctetSyncIO->read(ipPortUser_, fullResponse, MAXBUF_,
comTimeout, &nbytesIn, &eomReason);
pasynOctetSyncIO->flush(ipPortUser_);
if (status == asynSuccess) {
status = parseResponse(fullCommand, fullResponse,
drvMessageText, &valueStart, &valueStop,
axisNo, tcpCmd, isRead);
if (status == asynSuccess) {
// Received the correct message
break;
}
} else {
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
if (status != asynTimeout) {
asynPrint(
this->pasynUserSelf, ASYN_TRACE_ERROR,
"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(
@ -283,10 +265,6 @@ asynStatus masterMacsController::writeRead(int axisNo, int tcpCmd,
}
}
// MasterMACS needs a bit of time between messages, therefore thr
// program execution is paused after the communication happened.
// usleep(1500);
// Create custom error messages for different failure modes
switch (status) {
case asynSuccess:
@ -434,7 +412,7 @@ asynStatus masterMacsController::parseResponse(
}
if (responseValid) {
msgPrintControl_.resetCount(parseKey);
msgPrintControl_.resetCount(parseKey, pasynUserSelf);
// Check if the response matches the expectations. Each response
// contains the string "axisNo R tcpCmd" (including the spaces)
@ -475,7 +453,7 @@ asynStatus masterMacsController::parseResponse(
printableResponse, printableCommand);
return asynError;
} else {
msgPrintControl_.resetCount(responseMatchKey);
msgPrintControl_.resetCount(responseMatchKey, pasynUserSelf);
}
}
return asynSuccess;

View File

@ -49,9 +49,6 @@ class masterMacsController : public sinqController {
*/
masterMacsAxis *getMasterMacsAxis(int axisNo);
protected:
asynUser *lowLevelPortUser_;
/**
* @brief Send a command to the hardware (S mode)
*
@ -60,7 +57,8 @@ class masterMacsController : public sinqController {
* @param payload Value send to MasterMACS.
* @return asynStatus
*/
asynStatus write(int axisNo, int tcpCmd, const char *payload);
asynStatus write(int axisNo, int tcpCmd, const char *payload,
double comTimeout = -1.0);
/**
* @brief Send a command to the hardware and receive a response (R mode)
@ -71,7 +69,8 @@ class masterMacsController : public sinqController {
* expected to have the size MAXBUF_.
* @return asynStatus
*/
asynStatus read(int axisNo, int tcpCmd, char *response);
asynStatus read(int axisNo, int tcpCmd, char *response,
double comTimeout = -1.0);
/**
* @brief Send a command to the hardware (R or S mode) and receive a
@ -88,7 +87,7 @@ class masterMacsController : public sinqController {
* @return asynStatus
*/
asynStatus writeRead(int axisNo, int tcpCmd, const char *payload,
char *response);
char *response, double comTimeout = -1.0);
/**
* @brief Parse "fullResponse" received upon sending "fullCommand".
@ -114,26 +113,16 @@ class masterMacsController : public sinqController {
int *valueStop, int axisNo, int tcpCmd,
bool isRead);
private:
// Set the maximum buffer size. This is an empirical value which must be
// large enough to avoid overflows for all commands to the device /
// responses from it.
static const uint32_t MAXBUF_ = 200;
private:
/*
Stores the constructor input comTimeout
*/
double comTimeout_;
// Indices of additional PVs
#define FIRST_masterMacs_PARAM rereadEncoderPosition_
int rereadEncoderPosition_;
int readConfig_;
#define LAST_masterMacs_PARAM readConfig_
friend class masterMacsAxis;
};
#define NUM_masterMacs_DRIVER_PARAMS \
(&LAST_masterMacs_PARAM - &FIRST_masterMacs_PARAM + 1)
#endif /* masterMacsController_H */