6 Commits

Author SHA1 Message Date
9cc9b1d144 Added explanation why the return status of doPoll is not used in the
enable function.
2025-05-13 14:44:49 +02:00
2e42cbc6da Fixed some inaccuracies in the description 2025-05-12 17:25:30 +02:00
c5f5cf3065 Specified new version for sinqMotor 2025-05-12 12:04:09 +02:00
468ca46010 Static linking of sinqMotor into masterMacs 2025-05-12 09:04:26 +02:00
cd57409f3c Added motorConnected logic 2025-04-25 15:58:03 +02:00
21ffcba8be Back to dev version 2025-04-22 15:07:45 +02:00
7 changed files with 165 additions and 74 deletions

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "sinqMotor"]
path = sinqMotor
url = https://gitea.psi.ch/lin-epics-modules/sinqMotor

View File

@ -12,18 +12,23 @@ REQUIRED+=sinqMotor
# Specify the version of asynMotor we want to build against
motorBase_VERSION=7.2.2
# Specify the version of sinqMotor we want to build against
sinqMotor_VERSION=0.13.0
# These headers allow to depend on this library for derived drivers.
HEADERS += src/masterMacsAxis.h
HEADERS += src/masterMacsController.h
# Source files to build
SOURCES += sinqMotor/src/msgPrintControl.cpp
SOURCES += sinqMotor/src/sinqAxis.cpp
SOURCES += sinqMotor/src/sinqController.cpp
SOURCES += src/masterMacsAxis.cpp
SOURCES += src/masterMacsController.cpp
# Store the record files
TEMPLATES += sinqMotor/db/asynRecord.db
TEMPLATES += sinqMotor/db/sinqMotor.db
# This file registers the motor-specific functions in the IOC shell.
DBDS += sinqMotor/src/sinqMotor.dbd
DBDS += src/masterMacs.dbd
USR_CFLAGS += -Wall -Wextra -Weffc++ -Wunused-result -Wpedantic -Wextra -Werror

View File

@ -55,11 +55,9 @@ setMaxSubsequentTimeouts("$(NAME)", 20);
setThresholdComTimeout("$(NAME)", 100, 1);
# Parametrize the EPICS record database with the substitution file named after the MCU.
epicsEnvSet("SINQDBPATH","$(sinqMotor_DB)/sinqMotor.db")
epicsEnvSet("SINQDBPATH","$(masterMacs_DB)/sinqMotor.db")
dbLoadTemplate("$(TOP)/$(NAME).substitutions", "INSTR=$(INSTR)$(NAME):,CONTROLLER=$(NAME)")
epicsEnvSet("SINQDBPATH","$(masterMacs_DB)/masterMacs.db")
dbLoadTemplate("$(TOP)/$(NAME).substitutions", "INSTR=$(INSTR)$(NAME):,CONTROLLER=$(NAME)")
dbLoadRecords("$(sinqMotor_DB)/asynRecord.db","P=$(INSTR)$(NAME),PORT=$(ASYN_PORT)")
dbLoadRecords("$(masterMacs_DB)/asynRecord.db","P=$(INSTR)$(NAME),PORT=$(ASYN_PORT)")
```
### Versioning
@ -68,4 +66,4 @@ Please see the documentation for the module sinqMotor: https://git.psi.ch/sinq-e
### How to build it
Please see the documentation for the module sinqMotor: https://git.psi.ch/sinq-epics-modules/sinqmotor/-/blob/main/README.md.
This driver can be compiled and installed by running `make install` from the same directory where the Makefile is located. However, since it uses the git submodule sinqMotor, make sure that the correct version of the submodule repository is checked out AND the change is commited (`git status` shows no non-committed changes). Please see the section "Usage as static dependency" in https://git.psi.ch/sinq-epics-modules/sinqmotor/-/blob/main/README.md for more details.

1
sinqMotor Submodule

Submodule sinqMotor added at 5689402375

View File

@ -60,9 +60,9 @@ void appendErrorMessage(char *fullMessage, size_t capacityFullMessage,
// fullMessage suffices. We need capacity for one additional character
// because of the linebreak.
if (lenFullMessage + lenToBeAppended + 1 < capacityFullMessage) {
// Append the linebreak and readd the null terminator behind it
// fullMessage[lenFullMessage] = '\n';
// fullMessage[lenFullMessage + 1] = '\0';
// Append the linebreak and set the null terminator behind it
fullMessage[lenFullMessage] = '\n';
fullMessage[lenFullMessage + 1] = '\0';
// We check before that the capacity of fullMessage is sufficient
strcat(fullMessage, toBeAppended);
@ -919,6 +919,16 @@ asynStatus masterMacsAxis::doReset() {
}
}
rw_status = pC_->write(axisNo_, 85, "");
if (rw_status != asynSuccess) {
pl_status = setIntegerParam(pC_->motorStatusProblem(), true);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorStatusProblem_",
axisNo_, __PRETTY_FUNCTION__,
__LINE__);
}
}
return rw_status;
}
@ -1035,8 +1045,6 @@ asynStatus masterMacsAxis::enable(bool on) {
// Status of parameter library operations
asynStatus pl_status = asynSuccess;
bool moving = false;
// =========================================================================
/*
@ -1046,6 +1054,13 @@ asynStatus masterMacsAxis::enable(bool on) {
*/
targetReachedUninitialized_ = true;
/*
Continue regardless of the status returned by the poll; we just want to
find out whether the motor is currently moving or not. If the poll
function fails before it can determine that, it is assumed that the motor
is not moving.
*/
bool moving = false;
doPoll(&moving);
// If the axis is currently moving, it cannot be disabled. Ignore the

View File

@ -250,7 +250,14 @@ asynStatus masterMacsController::writeRead(int axisNo, int tcpCmd,
// Log the overall status (communication successfull or not)
if (status == asynSuccess) {
pl_status = axis->setIntegerParam(this->motorStatusCommsError_, 0);
if (pl_status != asynSuccess) {
return paramLibAccessFailed(pl_status, "motorStatusCommsError_",
axisNo, __PRETTY_FUNCTION__, __LINE__);
}
} else if (status == asynDisconnected) {
// Do nothing
} else {
// Set the error status bits only if the axis is not disconnected
// Check if the axis already is in an error communication mode. If
// it is not, upstream the error. This is done to avoid "flooding"
@ -303,14 +310,22 @@ asynStatus masterMacsController::parseResponse(
const char *fullCommand, const char *fullResponse, char *drvMessageText,
int *valueStart, int *valueStop, int axisNo, int tcpCmd, bool isRead) {
bool responseValid = false;
int responseStart = 0;
asynStatus status = asynSuccess;
int prevConnected = 0;
char printableCommand[MAXBUF_] = {0};
char printableResponse[MAXBUF_] = {0};
msgPrintControlKey parseKey =
msgPrintControlKey(portName, axisNo, __PRETTY_FUNCTION__, __LINE__);
// Was the motor previously connected?
status = getIntegerParam(axisNo, motorConnected(), &prevConnected);
if (status != asynSuccess) {
return paramLibAccessFailed(status, "motorConnected", axisNo,
__PRETTY_FUNCTION__, __LINE__);
}
// We don't use strlen here since the C string terminator 0x00
// occurs in the middle of the char array.
for (uint32_t i = 0; i < MAXBUF_; i++) {
@ -319,22 +334,120 @@ asynStatus masterMacsController::parseResponse(
} else if (fullResponse[i] == '=') {
*valueStart = i + 1;
} else if (fullResponse[i] == '\x06') {
// ACK
*valueStop = i;
responseValid = true;
break;
} else if (fullResponse[i] == '\x15') {
// NAK
snprintf(drvMessageText, MAXBUF_, "Communication failed.");
if (msgPrintControl_.shouldBePrinted(parseKey, true,
pasynUserSelf)) {
// Motor wasn't connected before -> Update the paramLib entry and PV
// to show it is now connected.
if (prevConnected == 0) {
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line "
"%d:\nCommunication failed.%s\n",
portName, axisNo, __PRETTY_FUNCTION__, __LINE__,
msgPrintControl_.getSuffix());
"%d:\nAxis connection status has changed to "
"connected.\n",
portName, axisNo, __PRETTY_FUNCTION__, __LINE__);
masterMacsAxis *axis = getMasterMacsAxis(axisNo);
if (axis == nullptr) {
return asynError;
}
status = axis->setIntegerParam(motorConnected(), 1);
if (status != asynSuccess) {
return paramLibAccessFailed(status, "motorConnected",
axisNo, __PRETTY_FUNCTION__,
__LINE__);
}
status = callParamCallbacks();
if (status != asynSuccess) {
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line "
"%d:\nCould not update parameter library\n",
portName, axisNo, __PRETTY_FUNCTION__, __LINE__);
return status;
}
}
break;
msgPrintControl_.resetCount(parseKey, pasynUserSelf);
// Check if the response matches the expectations. Each response
// contains the string "axisNo R tcpCmd" (including the spaces)
char expectedResponseSubstring[MAXBUF_] = {0};
// The response does not contain a leading 0 if tcpCmd only has
// a single digit!
if (isRead) {
snprintf(expectedResponseSubstring, MAXBUF_ - 4, "%d R %d",
axisNo, tcpCmd);
} else {
snprintf(expectedResponseSubstring, MAXBUF_ - 4, "%d S %d",
axisNo, tcpCmd);
}
msgPrintControlKey responseMatchKey = msgPrintControlKey(
portName, axisNo, __PRETTY_FUNCTION__, __LINE__);
if (strstr(&fullResponse[responseStart],
expectedResponseSubstring) == NULL) {
adjustForPrint(printableCommand, fullCommand, MAXBUF_);
adjustForPrint(printableResponse, fullResponse, MAXBUF_);
if (msgPrintControl_.shouldBePrinted(parseKey, true,
pasynUserSelf)) {
asynPrint(this->pasynUserSelf, ASYN_TRACEIO_DRIVER,
"Controller \"%s\", axis %d => %s, line "
"%d:\nMismatched "
"response %s to command %s.%s\n",
portName, axisNo, __PRETTY_FUNCTION__, __LINE__,
printableResponse, printableCommand,
msgPrintControl_.getSuffix());
}
snprintf(
drvMessageText, MAXBUF_,
"Mismatched response %s to command %s. Please call the "
"support.",
printableResponse, printableCommand);
return asynError;
} else {
msgPrintControl_.resetCount(responseMatchKey, pasynUserSelf);
}
return asynSuccess;
} else if (fullResponse[i] == '\x15') {
/*
NAK
This indicates that the axis is not connected. This is not an error!
*/
snprintf(drvMessageText, MAXBUF_, "Axis not connected.");
// Motor was connected before -> Update the paramLib entry and PV
// to show it is now disconnected.
if (prevConnected == 1) {
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line "
"%d:\nAxis connection status has changed to "
"disconnected.\n",
portName, axisNo, __PRETTY_FUNCTION__, __LINE__);
masterMacsAxis *axis = getMasterMacsAxis(axisNo);
if (axis == nullptr) {
return asynError;
}
status = axis->setIntegerParam(motorConnected(), 0);
if (status != asynSuccess) {
return paramLibAccessFailed(status, "motorConnected",
axisNo, __PRETTY_FUNCTION__,
__LINE__);
}
status = callParamCallbacks();
if (status != asynSuccess) {
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line "
"%d:\nCould not update parameter library\n",
portName, axisNo, __PRETTY_FUNCTION__, __LINE__);
return status;
}
}
return asynDisconnected;
} else if (fullResponse[i] == '\x18') {
// CAN
snprintf(drvMessageText, MAXBUF_,
@ -351,60 +464,14 @@ asynStatus masterMacsController::parseResponse(
portName, axisNo, __PRETTY_FUNCTION__, __LINE__,
printableCommand, msgPrintControl_.getSuffix());
}
responseValid = false;
break;
}
}
if (responseValid) {
msgPrintControl_.resetCount(parseKey, pasynUserSelf);
// Check if the response matches the expectations. Each response
// contains the string "axisNo R tcpCmd" (including the spaces)
char expectedResponseSubstring[MAXBUF_] = {0};
// The response does not contain a leading 0 if tcpCmd only has
// a single digit!
if (isRead) {
snprintf(expectedResponseSubstring, MAXBUF_ - 4, "%d R %d", axisNo,
tcpCmd);
} else {
snprintf(expectedResponseSubstring, MAXBUF_ - 4, "%d S %d", axisNo,
tcpCmd);
}
msgPrintControlKey responseMatchKey =
msgPrintControlKey(portName, axisNo, __PRETTY_FUNCTION__, __LINE__);
if (strstr(&fullResponse[responseStart], expectedResponseSubstring) ==
NULL) {
adjustForPrint(printableCommand, fullCommand, MAXBUF_);
adjustForPrint(printableResponse, fullResponse, MAXBUF_);
if (msgPrintControl_.shouldBePrinted(parseKey, true,
pasynUserSelf)) {
asynPrint(this->pasynUserSelf, ASYN_TRACEIO_DRIVER,
"Controller \"%s\", axis %d => %s, line "
"%d:\nMismatched "
"response %s to command %s.%s\n",
portName, axisNo, __PRETTY_FUNCTION__, __LINE__,
printableResponse, printableCommand,
msgPrintControl_.getSuffix());
}
snprintf(drvMessageText, MAXBUF_,
"Mismatched response %s to command %s. Please call the "
"support.",
printableResponse, printableCommand);
return asynError;
} else {
msgPrintControl_.resetCount(responseMatchKey, pasynUserSelf);
}
}
return asynSuccess;
return asynError;
}
asynStatus sinqController::readInt32(asynUser *pasynUser, epicsInt32 *value) {
asynStatus masterMacsController::readInt32(asynUser *pasynUser,
epicsInt32 *value) {
// masterMacs can be disabled
if (pasynUser->reason == motorCanDisable_) {
*value = 1;

View File

@ -125,6 +125,8 @@ class masterMacsController : public sinqController {
*/
double comTimeout() { return comTimeout_; }
asynStatus readInt32(asynUser *pasynUser, epicsInt32 *value);
private:
/*
Stores the constructor input comTimeout