3 Commits
1.3.0 ... 1.4.2

Author SHA1 Message Date
21a73717a5 Fixed handshake detection and reset bugs
Some checks failed
Test And Build / Lint (push) Failing after 4s
Test And Build / Build (push) Successful in 7s
2025-08-14 17:16:14 +02:00
2cbb4f9028 Forgot to add masterMacs.db to Makefile
Some checks failed
Test And Build / Lint (push) Failing after 4s
Test And Build / Build (push) Successful in 7s
2025-08-14 14:21:05 +02:00
23a911206a Removed node reset from doReset and moved it into dedicated function
Some checks failed
Test And Build / Lint (push) Failing after 4s
Test And Build / Build (push) Successful in 7s
A "normal" error reset should not trigger a node reset. However, this
option is still available via a dedicated PV xx:NodeReset and a
corresponding function in masterMacsAxis.
2025-08-12 15:51:12 +02:00
7 changed files with 106 additions and 21 deletions

View File

@@ -23,6 +23,7 @@ SOURCES += src/masterMacsController.cpp
# Store the record files # Store the record files
TEMPLATES += sinqMotor/db/asynRecord.db TEMPLATES += sinqMotor/db/asynRecord.db
TEMPLATES += sinqMotor/db/sinqMotor.db TEMPLATES += sinqMotor/db/sinqMotor.db
TEMPLATES += db/masterMacs.db
# This file registers the motor-specific functions in the IOC shell. # This file registers the motor-specific functions in the IOC shell.
DBDS += sinqMotor/src/sinqMotor.dbd DBDS += sinqMotor/src/sinqMotor.dbd

7
db/masterMacs.db Executable file
View File

@@ -0,0 +1,7 @@
# Call the nodeReset function of the corresponding masterMacsAxis.
# This record is coupled to the parameter library via nodeReset_ -> NODE_RESET.
record(longout, "$(INSTR)$(M):NodeReset") {
field(DTYP, "asynInt32")
field(OUT, "@asyn($(CONTROLLER),$(AXIS),1) NODE_RESET")
field(PINI, "NO")
}

View File

@@ -357,7 +357,10 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
if (timedOut) { if (timedOut) {
setAxisParamChecked(this, motorMessageText, 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); pC_->read(axisNo_, 86, response);
@@ -377,14 +380,10 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
pMasterMacsA_->waitForHandshake = false; pMasterMacsA_->waitForHandshake = false;
pMasterMacsA_->targetReachedUninitialized = false; pMasterMacsA_->targetReachedUninitialized = false;
} else { } else {
// Still waiting for the handshake - try again in the next busy
// poll. This is already part of the movement procedure.
*moving = true; *moving = true;
setAxisParamChecked(this, motorStatusMoving, *moving); setAxisParamChecked(this, motorStatusMoving, *moving);
setAxisParamChecked(this, motorStatusDone, !(*moving)); setAxisParamChecked(this, motorStatusDone, !(*moving));
return poll_status;
return asynSuccess;
} }
} }
@@ -403,6 +402,9 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
return rw_status; 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) { if (pMasterMacsA_->targetReachedUninitialized) {
*moving = false; *moving = false;
} else { } else {
@@ -679,7 +681,6 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
if (pl_status != asynSuccess) { if (pl_status != asynSuccess) {
return pl_status; return pl_status;
} }
return poll_status; return poll_status;
} }
@@ -805,15 +806,26 @@ asynStatus masterMacsAxis::doReset() {
asynStatus status = asynSuccess; asynStatus status = asynSuccess;
// Reset the controller ("node reset") // Reset any errors
status = pC_->write(axisNo_, 16, ""); status = pC_->write(axisNo_, 17, "");
if (status != asynSuccess) { if (status != asynSuccess) {
setAxisParamChecked(this, motorStatusProblem, true); setAxisParamChecked(this, motorStatusProblem, true);
} }
// Reset any errors in the controller. Since the node reset results in a // Move out of the handshake wait loop, if we're currently inside it.
pMasterMacsA_->waitForHandshake = false;
// Disable the axis
return enable(false);
}
asynStatus masterMacsAxis::nodeReset() {
asynStatus status = asynSuccess;
// Reset the controller ("node reset"). Since the node reset results in a
// power cycle, we use the corresponding timeout. // power cycle, we use the corresponding timeout.
status = pC_->write(axisNo_, 17, "", PowerCycleTimeout); status = pC_->write(axisNo_, 16, "", PowerCycleTimeout);
if (status != asynSuccess) { if (status != asynSuccess) {
setAxisParamChecked(this, motorStatusProblem, true); setAxisParamChecked(this, motorStatusProblem, true);
} }

View File

@@ -75,12 +75,27 @@ class HIDDEN masterMacsAxis : public sinqAxis {
*/ */
asynStatus doReset(); asynStatus doReset();
/**
* @brief Performs a "node reset" on the axis as defined in the CANopen
* standard
*
* A "node reset" is a factory reset on the axis which completely deletes
* all configured information (e.g. limits or speed) from the axis. The
* MasterMACS controller then reapplies the initial configuration to this
* axis. It can therefore be seen as a "hard" version of the normal error
* reset performed by the `doReset` method.
*
* @return asynStatus
*/
asynStatus nodeReset();
/** /**
* @brief Readout of some values from the controller at IOC startup * @brief Readout of some values from the controller at IOC startup
* *
* The following steps are performed: * The following steps are performed:
* - Read out the motor status, motor position, velocity and acceleration * - Read out the motor status, motor position, velocity and
* from the MCU and store this information in the parameter library. * acceleration from the MCU and store this information in the parameter
* library.
* - Set the enable PV accordint to the initial status of the axis. * - Set the enable PV accordint to the initial status of the axis.
* *
* @return asynStatus * @return asynStatus
@@ -96,8 +111,8 @@ class HIDDEN masterMacsAxis : public sinqAxis {
asynStatus enable(bool on); asynStatus enable(bool on);
/** /**
* @brief Read the encoder type (incremental or absolute) for this axis from * @brief Read the encoder type (incremental or absolute) for this axis
* the MCU and store the information in the PV ENCODER_TYPE. * from the MCU and store the information in the PV ENCODER_TYPE.
* *
* @return asynStatus * @return asynStatus
*/ */
@@ -112,7 +127,8 @@ class HIDDEN masterMacsAxis : public sinqAxis {
bool needInit(); bool needInit();
/** /**
* @brief Instruct the axis to run its init() function during the next poll * @brief Instruct the axis to run its init() function during the next
* poll
* *
* @param needInit * @param needInit
*/ */
@@ -124,8 +140,8 @@ class HIDDEN masterMacsAxis : public sinqAxis {
virtual masterMacsController *pController() override { return pC_; }; virtual masterMacsController *pController() override { return pC_; };
/** /**
* @brief Read the Master MACS status with the xR10 command and store the * @brief Read the Master MACS status with the xR10 command and store
* result in axisStatus_ * the result in axisStatus_
* *
*/ */
asynStatus readAxisStatus(); asynStatus readAxisStatus();
@@ -203,8 +219,8 @@ class HIDDEN masterMacsAxis : public sinqAxis {
bool powerEnabled(); bool powerEnabled();
/** /**
* @brief Read the Master MACS status with the xR10 command and store the * @brief Read the Master MACS status with the xR10 command and store
* result in axisStatus_ * the result in axisStatus_
* *
*/ */
asynStatus readAxisError(); asynStatus readAxisError();

View File

@@ -14,6 +14,9 @@
struct masterMacsControllerImpl { struct masterMacsControllerImpl {
double comTimeout; double comTimeout;
// Indices of additional ParamLib entries
int nodeReset;
}; };
/** /**
@@ -68,6 +71,20 @@ masterMacsController::masterMacsController(const char *portName,
.comTimeout = comTimeout, .comTimeout = comTimeout,
}); });
// =========================================================================
// Create additional parameter library entries
status =
createParam("NODE_RESET", asynParamInt32, &pMasterMacsC_->nodeReset);
if (status != asynSuccess) {
asynPrint(this->pasynUser(), ASYN_TRACE_ERROR,
"Controller \"%s\" => %s, line %d\nFATAL ERROR (creating a "
"parameter failed with %s).\nTerminating IOC",
portName, __PRETTY_FUNCTION__, __LINE__,
stringifyAsynStatus(status));
exit(-1);
}
// ========================================================================= // =========================================================================
/* /*
@@ -120,6 +137,22 @@ masterMacsAxis *masterMacsController::getMasterMacsAxis(int axisNo) {
return dynamic_cast<masterMacsAxis *>(asynAxis); return dynamic_cast<masterMacsAxis *>(asynAxis);
} }
asynStatus masterMacsController::writeInt32(asynUser *pasynUser,
epicsInt32 value) {
int function = pasynUser->reason;
// =====================================================================
masterMacsAxis *axis = getMasterMacsAxis(pasynUser);
// Handle custom PVs
if (function == nodeReset()) {
return axis->nodeReset();
} else {
return sinqController::writeInt32(pasynUser, value);
}
}
asynStatus masterMacsController::read(int axisNo, int tcpCmd, char *response, asynStatus masterMacsController::read(int axisNo, int tcpCmd, char *response,
double comTimeout) { double comTimeout) {
return writeRead(axisNo, tcpCmd, NULL, response); return writeRead(axisNo, tcpCmd, NULL, response);
@@ -459,6 +492,8 @@ asynStatus masterMacsController::readInt32(asynUser *pasynUser,
double masterMacsController::comTimeout() { return pMasterMacsC_->comTimeout; } double masterMacsController::comTimeout() { return pMasterMacsC_->comTimeout; }
int masterMacsController::nodeReset() { return pMasterMacsC_->nodeReset; }
/***************************************************************************/ /***************************************************************************/
/** The following functions are C-wrappers, and can be called directly from /** The following functions are C-wrappers, and can be called directly from
* iocsh */ * iocsh */

View File

@@ -65,6 +65,17 @@ class HIDDEN masterMacsController : public sinqController {
*/ */
masterMacsAxis *getMasterMacsAxis(int axisNo); masterMacsAxis *getMasterMacsAxis(int axisNo);
/**
* @brief Overloaded function of sinqController
*
* The function is overloaded to allow resetting the node
*
* @param pasynUser Specify the axis via the asynUser
* @param value New value
* @return asynStatus
*/
virtual asynStatus writeInt32(asynUser *pasynUser, epicsInt32 value);
/** /**
* @brief Send a command to the hardware (S mode) * @brief Send a command to the hardware (S mode)
* *
@@ -141,6 +152,9 @@ class HIDDEN masterMacsController : public sinqController {
*/ */
double comTimeout(); double comTimeout();
// Accessors for additional PVs
int nodeReset();
private: private:
std::unique_ptr<masterMacsControllerImpl> pMasterMacsC_; std::unique_ptr<masterMacsControllerImpl> pMasterMacsC_;
}; };