Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 25286652d5 | |||
| 27f7cc8602 | |||
| 21a73717a5 |
4
Makefile
4
Makefile
@@ -30,3 +30,7 @@ DBDS += sinqMotor/src/sinqMotor.dbd
|
||||
DBDS += src/masterMacs.dbd
|
||||
|
||||
USR_CFLAGS += -Wall -Wextra -Weffc++ -Wunused-result -Wextra -Werror
|
||||
|
||||
# These flags define the expected firmware version. See README.md, section
|
||||
# "Firmware version checking" for details.
|
||||
USR_CXXFLAGS += -DFIRMWARE_MAJOR_VERSION=2 -DFIRMWARE_MINOR_VERSION=2
|
||||
|
||||
26
README.md
26
README.md
@@ -66,6 +66,32 @@ dbLoadRecords("$(masterMacs_DB)/asynRecord.db","P=$(INSTR)$(NAME),PORT=$(ASYN_PO
|
||||
|
||||
Please see the documentation for the module sinqMotor: https://git.psi.ch/sinq-epics-modules/sinqmotor/-/blob/main/README.md.
|
||||
|
||||
### Firmware version checking
|
||||
|
||||
This driver expects a certain version of the firmware running on the controller itself.
|
||||
This is checked at IOC startup by reading the version directly from the hardware.
|
||||
If the firmware version is incompatible to the driver, the IOC will be shut down.
|
||||
If the firmware version cannot be read (e.g. because the variable used to do so
|
||||
does not exist yet on old firmware versions), the firmware is assumed to be compatible
|
||||
to the driver.
|
||||
|
||||
The version check is separated into a check of the major and the minor firmware
|
||||
version against expected values. The firmware is seen as compatible if the following conditions hold:
|
||||
- Read-out major version == Expected major version
|
||||
- Read-out read major version >= Expected minor version
|
||||
|
||||
The expected versions are defined via compiler flags in `Makefile`:
|
||||
|
||||
```
|
||||
USR_CXXFLAGS += -DFIRMWARE_MAJOR_VERSION=1 -DFIRMWARE_MINOR_VERSION=0
|
||||
```
|
||||
Be aware that these flags are only used to compile C++-files (.cpp, .cxx) and not
|
||||
C-files (.c). For C-files, the Makefile variable `USR_CFLAGS` must be used.
|
||||
|
||||
In order to disable the checks, the flags can be set to -1 or just be removed
|
||||
entirely. If one of the flags is not given, both the major and the minor version
|
||||
checks are deactivated.
|
||||
|
||||
### How to build it
|
||||
|
||||
Please see the documentation for the module sinqMotor: https://git.psi.ch/sinq-epics-modules/sinqmotor/-/blob/main/README.md.
|
||||
|
||||
Submodule sinqMotor updated: 8689c79f19...7a0de4e9d9
@@ -357,7 +357,10 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
|
||||
|
||||
if (timedOut) {
|
||||
setAxisParamChecked(this, motorMessageText,
|
||||
"Timed out while waiting for a handshake");
|
||||
"Timed out while waiting for a handshake. "
|
||||
"Please call the support.");
|
||||
|
||||
poll_status = asynError;
|
||||
}
|
||||
|
||||
pC_->read(axisNo_, 86, response);
|
||||
@@ -377,14 +380,10 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
|
||||
pMasterMacsA_->waitForHandshake = false;
|
||||
pMasterMacsA_->targetReachedUninitialized = false;
|
||||
} else {
|
||||
// Still waiting for the handshake - try again in the next busy
|
||||
// poll. This is already part of the movement procedure.
|
||||
*moving = true;
|
||||
|
||||
setAxisParamChecked(this, motorStatusMoving, *moving);
|
||||
setAxisParamChecked(this, motorStatusDone, !(*moving));
|
||||
|
||||
return asynSuccess;
|
||||
return poll_status;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -403,6 +402,9 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
|
||||
return rw_status;
|
||||
}
|
||||
|
||||
// If we wait for a handshake, but the motor was moving in its last poll
|
||||
// cycle and has reached its target, it is not moving. Otherwise it is
|
||||
// considered moving, even if we're still waiting for the handshake.
|
||||
if (pMasterMacsA_->targetReachedUninitialized) {
|
||||
*moving = false;
|
||||
} else {
|
||||
@@ -679,7 +681,6 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
|
||||
if (pl_status != asynSuccess) {
|
||||
return pl_status;
|
||||
}
|
||||
|
||||
return poll_status;
|
||||
}
|
||||
|
||||
@@ -805,8 +806,8 @@ asynStatus masterMacsAxis::doReset() {
|
||||
|
||||
asynStatus status = asynSuccess;
|
||||
|
||||
// Reset the controller ("node reset")
|
||||
status = pC_->write(axisNo_, 16, "");
|
||||
// Reset any errors
|
||||
status = pC_->write(axisNo_, 17, "");
|
||||
if (status != asynSuccess) {
|
||||
setAxisParamChecked(this, motorStatusProblem, true);
|
||||
}
|
||||
@@ -822,9 +823,9 @@ asynStatus masterMacsAxis::nodeReset() {
|
||||
|
||||
asynStatus status = asynSuccess;
|
||||
|
||||
// Reset any errors in the controller. Since the node reset results in a
|
||||
// Reset the controller ("node reset"). Since the node reset results in a
|
||||
// power cycle, we use the corresponding timeout.
|
||||
status = pC_->write(axisNo_, 17, "", PowerCycleTimeout);
|
||||
status = pC_->write(axisNo_, 16, "", PowerCycleTimeout);
|
||||
if (status != asynSuccess) {
|
||||
setAxisParamChecked(this, motorStatusProblem, true);
|
||||
}
|
||||
|
||||
@@ -12,6 +12,28 @@
|
||||
#include <string>
|
||||
#include <unistd.h>
|
||||
|
||||
/*
|
||||
These functions are used to read out the compiler flags defining the major and
|
||||
minor versions. See README.md, section "Firmware version checking" for
|
||||
details. If these flags are not given, a default value of -1 is used, which
|
||||
disables the version checks (it suffices to have one of these at -1 to disable
|
||||
both major and minor version check)
|
||||
*/
|
||||
constexpr int firmware_major_version() {
|
||||
#ifdef FIRMWARE_MAJOR_VERSION
|
||||
return FIRMWARE_MAJOR_VERSION;
|
||||
#else
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
constexpr int firmware_minor_version() {
|
||||
#ifdef FIRMWARE_MINOR_VERSION
|
||||
return FIRMWARE_MINOR_VERSION;
|
||||
#else
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
struct masterMacsControllerImpl {
|
||||
double comTimeout;
|
||||
|
||||
@@ -65,6 +87,7 @@ masterMacsController::masterMacsController(const char *portName,
|
||||
|
||||
// Initialization of local variables
|
||||
asynStatus status = asynSuccess;
|
||||
char response[MAXBUF_] = {0};
|
||||
|
||||
pMasterMacsC_ =
|
||||
std::make_unique<masterMacsControllerImpl>((masterMacsControllerImpl){
|
||||
@@ -116,6 +139,60 @@ masterMacsController::masterMacsController(const char *portName,
|
||||
pasynOctetSyncIO->disconnect(pasynOctetSyncIOipPort());
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
|
||||
if (firmware_major_version() >= 0 && firmware_minor_version() >= 0) {
|
||||
// Check the firmware version according to the conditions outlined in
|
||||
// README.md
|
||||
status = read(0, 99, response);
|
||||
|
||||
if (status == asynSuccess) {
|
||||
|
||||
// Just interpret the version if the variable already exists
|
||||
double versionRaw = 0.0;
|
||||
int nvals = sscanf(response, "%lf", &versionRaw);
|
||||
if (nvals == 1 && versionRaw != 0.0) {
|
||||
// Discard decimal part
|
||||
long long versionInt = (long long)versionRaw;
|
||||
|
||||
// Extract bugfix (last 3 digits)
|
||||
// Currently not used, just here for completions sake
|
||||
// int bugfix = versionInt % 1000;
|
||||
versionInt /= 1000;
|
||||
|
||||
// Extract minor (next 3 digits)
|
||||
int minor = versionInt % 1000;
|
||||
versionInt /= 1000;
|
||||
|
||||
// Remaining is major
|
||||
int major = (int)versionInt;
|
||||
|
||||
// Compare to target values
|
||||
if (firmware_major_version() != major ||
|
||||
firmware_minor_version() > minor) {
|
||||
asynPrint(this->pasynUser(), ASYN_TRACE_ERROR,
|
||||
"Controller \"%s\" => %s, line %d\nFATAL ERROR "
|
||||
"(Incorrect "
|
||||
"version number of firmware: Expected major "
|
||||
"version equal "
|
||||
"to %d, got %d. Expected minor version equal to "
|
||||
"or larger "
|
||||
"than %d, got %d).\nTerminating IOC",
|
||||
portName, __PRETTY_FUNCTION__, __LINE__,
|
||||
firmware_major_version(), major,
|
||||
firmware_minor_version(), minor);
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
asynPrint(
|
||||
this->pasynUser(), ASYN_TRACE_ERROR,
|
||||
"Controller \"%s\" => %s, line %d\nCould not read firmware "
|
||||
"version\n",
|
||||
portName, __PRETTY_FUNCTION__, __LINE__);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -199,12 +276,6 @@ asynStatus masterMacsController::writeRead(int axisNo, int tcpCmd,
|
||||
comTimeout = pMasterMacsC_->comTimeout;
|
||||
}
|
||||
|
||||
masterMacsAxis *axis = getMasterMacsAxis(axisNo);
|
||||
if (axis == nullptr) {
|
||||
// We already did the error logging directly in getAxis
|
||||
return asynError;
|
||||
}
|
||||
|
||||
// Build the full command depending on the inputs to this function
|
||||
if (isRead) {
|
||||
snprintf(fullCommand, MAXBUF_ - 1, "%dR%02d\x0D", axisNo, tcpCmd);
|
||||
@@ -286,32 +357,39 @@ asynStatus masterMacsController::writeRead(int axisNo, int tcpCmd,
|
||||
}
|
||||
|
||||
// Log the overall status (communication successfull or not)
|
||||
if (status == asynSuccess) {
|
||||
setAxisParamChecked(axis, motorStatusCommsError, false);
|
||||
} else {
|
||||
if (axisNo != 0) {
|
||||
masterMacsAxis *axis = getMasterMacsAxis(axisNo);
|
||||
if (axis == nullptr) {
|
||||
// We already did the error logging directly in getAxis
|
||||
return asynError;
|
||||
}
|
||||
|
||||
/*
|
||||
Since the communication failed, there is the possibility that the
|
||||
controller is not connected at all to the network. In that case, we
|
||||
cannot be sure that the information read out in the init method of the
|
||||
axis is still up-to-date the next time we get a connection. Therefore,
|
||||
an info flag is set which the axis object can use at the start of its
|
||||
poll method to try to initialize itself.
|
||||
*/
|
||||
axis->setNeedInit(true);
|
||||
|
||||
/*
|
||||
Check if the axis already is in an error communication mode. If
|
||||
it is not, upstream the error. This is done to avoid "flooding"
|
||||
the user with different error messages if more than one error
|
||||
ocurred before an error-free communication
|
||||
*/
|
||||
getAxisParamChecked(axis, motorStatusProblem, &motorStatusProblem);
|
||||
|
||||
if (motorStatusProblem == 0) {
|
||||
setAxisParamChecked(axis, motorMessageText, drvMessageText);
|
||||
setAxisParamChecked(axis, motorStatusProblem, true);
|
||||
if (status == asynSuccess) {
|
||||
setAxisParamChecked(axis, motorStatusCommsError, false);
|
||||
} else {
|
||||
/*
|
||||
Since the communication failed, there is the possibility that the
|
||||
controller is not connected at all to the network. In that case, we
|
||||
cannot be sure that the information read out in the init method of
|
||||
the axis is still up-to-date the next time we get a connection.
|
||||
Therefore, an info flag is set which the axis object can use at the
|
||||
start of its poll method to try to initialize itself.
|
||||
*/
|
||||
axis->setNeedInit(true);
|
||||
|
||||
/*
|
||||
Check if the axis already is in an error communication mode. If
|
||||
it is not, upstream the error. This is done to avoid "flooding"
|
||||
the user with different error messages if more than one error
|
||||
ocurred before an error-free communication
|
||||
*/
|
||||
getAxisParamChecked(axis, motorStatusProblem, &motorStatusProblem);
|
||||
|
||||
if (motorStatusProblem == 0) {
|
||||
setAxisParamChecked(axis, motorMessageText, drvMessageText);
|
||||
setAxisParamChecked(axis, motorStatusProblem, true);
|
||||
setAxisParamChecked(axis, motorStatusCommsError, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -334,7 +412,7 @@ asynStatus masterMacsController::parseResponse(
|
||||
bool responseValid = false;
|
||||
int responseStart = 0;
|
||||
asynStatus status = asynSuccess;
|
||||
int prevConnected = 0;
|
||||
int prevConnected = 1;
|
||||
char printableCommand[MAXBUF_] = {0};
|
||||
char printableResponse[MAXBUF_] = {0};
|
||||
|
||||
@@ -342,12 +420,14 @@ asynStatus masterMacsController::parseResponse(
|
||||
msgPrintControlKey(portName, axisNo, __PRETTY_FUNCTION__, __LINE__);
|
||||
|
||||
masterMacsAxis *axis = getMasterMacsAxis(axisNo);
|
||||
if (axis == nullptr) {
|
||||
if (axisNo != 0 && axis == nullptr) {
|
||||
return asynError;
|
||||
}
|
||||
|
||||
// Was the motor previously connected?
|
||||
getAxisParamChecked(axis, motorConnected, &prevConnected);
|
||||
if (axis != nullptr) {
|
||||
getAxisParamChecked(axis, motorConnected, &prevConnected);
|
||||
}
|
||||
|
||||
// We don't use strlen here since the C string terminator 0x00
|
||||
// occurs in the middle of the char array.
|
||||
@@ -371,14 +451,17 @@ asynStatus masterMacsController::parseResponse(
|
||||
"connected.\n",
|
||||
portName, axisNo, __PRETTY_FUNCTION__, __LINE__);
|
||||
|
||||
setAxisParamChecked(axis, motorConnected, true);
|
||||
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;
|
||||
if (axis != nullptr) {
|
||||
setAxisParamChecked(axis, motorConnected, true);
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -399,14 +482,17 @@ asynStatus masterMacsController::parseResponse(
|
||||
"disconnected.\n",
|
||||
portName, axisNo, __PRETTY_FUNCTION__, __LINE__);
|
||||
|
||||
setAxisParamChecked(axis, motorConnected, false);
|
||||
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;
|
||||
if (axis != nullptr) {
|
||||
setAxisParamChecked(axis, motorConnected, false);
|
||||
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;
|
||||
|
||||
Reference in New Issue
Block a user