Added PVs for error reset and status problem reporting and fixed a bug
in msgPrintControl
This commit is contained in:
@ -171,7 +171,10 @@ sinqMotor offers a variety of additional methods for children classes to standar
|
||||
- `setMaxSubsequentTimeouts`: Set the limit for the number of subsequent timeouts before the user is informed.
|
||||
|
||||
#### sinqAxis.h
|
||||
- `enable`: This function is called if the "Enable" PV from db/sinqMotor.db is set. This is an empty function which should be overwritten by concrete driver implementations.
|
||||
- `enable`: This function is called if the `$(INSTR)$(M):Enable` PV from db/sinqMotor.db is set.
|
||||
This is an empty function which should be overwritten by concrete driver implementations.
|
||||
- `reset`: This function is called when the `$(INSTR)$(M):Reset` PV from db/sinqMotor.db is set.
|
||||
This is an empty function which should be overwritten by concrete driver implementations.
|
||||
- `move`: This function sets the absolute target position in the parameter library and then calls `doMove`.
|
||||
- `doMove`: This is an empty function which should be overwritten by concrete driver implementations.
|
||||
- `home`: This function sets the internal status flags for the homing process and then calls doHome.
|
||||
@ -187,7 +190,6 @@ sinqMotor offers a variety of additional methods for children classes to standar
|
||||
- Reset `motorStatusProblem_`, `motorStatusCommsError_` and `motorMessageText_` if `doPoll` returned `asynSuccess`
|
||||
- Run `callParamCallbacks`
|
||||
- Return the status of `doPoll`
|
||||
- `doPoll`: This is an empty function which should be overwritten by concrete driver implementations.
|
||||
- `setVeloFields`: Populates the motor record fields VELO (actual velocity), VBAS (minimum allowed velocity) and VMAX (maximum allowed velocity) from the driver.
|
||||
- `setAcclField`: Populates the motor record field ACCL from the driver.
|
||||
- `startMovTimeoutWatchdog`: Starts a watchdog for the movement time. This watchdog compares the actual time spent in a movement operation with an expected time, which is calculated based on the distance of the current and the target position.
|
||||
|
@ -41,11 +41,29 @@ record(motor,"$(INSTR)$(M)")
|
||||
field(RMOD,"3") # Retry mode 3 ("In-Position"): This suppresses any retries from the motor record.
|
||||
}
|
||||
|
||||
# This PV reads out the 10th bit of the MSTA field of the motor record, which
|
||||
# is the "motorStatusProblem_" bit. The 10th bit is adressed by 0x0200. If the
|
||||
# bit is 0, the .VAL field is 0 as well. If the bit is 1, we need to divide by
|
||||
# 512 (=2^9) in order to set the .VAL field to 1 (and not to 512).
|
||||
record(calc, "$(INSTR)$(M):StatusProblem")
|
||||
{
|
||||
field(INPA, "$(INSTR)$(M).MSTA CP")
|
||||
field(CALC, "(A&0x200)/512")
|
||||
}
|
||||
|
||||
# Call the reset function of the corresponding sinqAxis
|
||||
# This record is coupled to the parameter library via motorReset_ -> MOTOR_RESET.
|
||||
record(longout, "$(INSTR)$(M):Reset") {
|
||||
field(DTYP, "asynInt32")
|
||||
field(OUT, "@asyn($(CONTROLLER),$(AXIS),1) MOTOR_RESET")
|
||||
field(PINI, "NO")
|
||||
}
|
||||
|
||||
# This PV allows force-stopping the motor record from within the driver by setting
|
||||
# the motorForceStop_ value in the parameter library to 1. It should be reset to 0 by the driver afterwards.
|
||||
# The implementation strategy is taken from https://epics.anl.gov/tech-talk/2022/msg00464.php.
|
||||
# This record is coupled to the parameter library via motorForceStop_ -> MOTOR_FORCE_STOP.
|
||||
record(longin, "$(INSTR)$(M):STOP_RBV")
|
||||
record(longin, "$(INSTR)$(M):StopRBV")
|
||||
{
|
||||
field(DTYP, "asynInt32")
|
||||
field(INP, "@asyn($(CONTROLLER),$(AXIS)) MOTOR_FORCE_STOP")
|
||||
@ -53,7 +71,7 @@ record(longin, "$(INSTR)$(M):STOP_RBV")
|
||||
field(FLNK, "$(INSTR)$(M):Stop2Field")
|
||||
}
|
||||
record(longout, "$(INSTR)$(M):Stop2Field") {
|
||||
field(DOL, "$(INSTR)$(M):STOP_RBV CP")
|
||||
field(DOL, "$(INSTR)$(M):StopRBV CP")
|
||||
field(OUT, "$(INSTR)$(M).STOP")
|
||||
field(OMSL, "closed_loop")
|
||||
}
|
||||
|
@ -69,13 +69,16 @@ bool msgPrintControl::shouldBePrinted(msgPrintControlKey &key, bool wantToPrint,
|
||||
*/
|
||||
if (map_.find(key) != map_.end()) {
|
||||
if (map_[key] != 0) {
|
||||
char formattedKey[100] = {0};
|
||||
key.format(formattedKey, sizeof(formattedKey));
|
||||
asynPrint(pasynUser, ASYN_TRACE_ERROR,
|
||||
"Controller \"%s\", axis %d => %s, line %d\nError "
|
||||
"associated with key \"%s\" has been resolved.\n",
|
||||
key.controller_.c_str(), key.axisNo_,
|
||||
key.functionName_, key.line_, formattedKey);
|
||||
if (pasynUser != nullptr) {
|
||||
char formattedKey[100] = {0};
|
||||
key.format(formattedKey, sizeof(formattedKey));
|
||||
asynPrint(
|
||||
pasynUser, ASYN_TRACE_ERROR,
|
||||
"Controller \"%s\", axis %d => %s, line %d\nError "
|
||||
"associated with key \"%s\" has been resolved.\n",
|
||||
key.controller_.c_str(), key.axisNo_, key.functionName_,
|
||||
key.line_, formattedKey);
|
||||
}
|
||||
map_[key] = 0;
|
||||
}
|
||||
}
|
||||
@ -91,9 +94,20 @@ bool msgPrintControl::shouldBePrinted(char *portName, int axisNo,
|
||||
return shouldBePrinted(key, wantToPrint, pasynUser);
|
||||
}
|
||||
|
||||
void msgPrintControl::resetCount(msgPrintControlKey &key) {
|
||||
void msgPrintControl::resetCount(msgPrintControlKey &key, asynUser *pasynUser) {
|
||||
if (map_.find(key) != map_.end()) {
|
||||
map_[key] = 0;
|
||||
if (map_[key] != 0) {
|
||||
if (pasynUser != nullptr) {
|
||||
char formattedKey[100] = {0};
|
||||
key.format(formattedKey, sizeof(formattedKey));
|
||||
asynPrint(pasynUser, ASYN_TRACE_ERROR,
|
||||
"Controller \"%s\", axis %d => %s, line %d\nError "
|
||||
"associated with key \"%s\" has been resolved.\n",
|
||||
key.controller_.c_str(), key.axisNo_,
|
||||
key.functionName_, key.line_, formattedKey);
|
||||
}
|
||||
map_[key] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -93,6 +93,9 @@ class msgPrintControl {
|
||||
* identify individual messages
|
||||
* @param wantToPrint If the message associated with key should be
|
||||
* printed, this value should be true, otherwise false.
|
||||
* @param pasynUser If the problem has been resolved (wantToPrint =
|
||||
* false), a corresponding status message is printed using the given
|
||||
* asynUser. If this pointer is a nullptr, no message is printed.
|
||||
* @return bool If true, the message should be printed, if
|
||||
* false, it should not.
|
||||
*/
|
||||
@ -108,13 +111,22 @@ class msgPrintControl {
|
||||
* @param fileName
|
||||
* @param line
|
||||
* @param wantToPrint
|
||||
* @return true
|
||||
* @return false
|
||||
* @param pasynUser
|
||||
*/
|
||||
bool shouldBePrinted(char *controller, int axisNo, const char *functionName,
|
||||
int line, bool wantToPrint, asynUser *pasynUser);
|
||||
|
||||
void resetCount(msgPrintControlKey &key);
|
||||
/**
|
||||
* @brief Reset the error message count incremented in shouldBePrinted for
|
||||
* the given key
|
||||
*
|
||||
* @param key Key associated with the message, used to
|
||||
* identify individual messages
|
||||
* @param pasynUser If the problem has been resolved (wantToPrint =
|
||||
* false), a corresponding status message is printed using the given
|
||||
* asynUser. If this pointer is a nullptr, no message is printed.
|
||||
*/
|
||||
void resetCount(msgPrintControlKey &key, asynUser *pasynUser);
|
||||
|
||||
/**
|
||||
* @brief Maximum number of times a message is printed before it is
|
||||
|
@ -309,6 +309,8 @@ asynStatus sinqAxis::doHome(double minVelocity, double maxVelocity,
|
||||
return asynSuccess;
|
||||
}
|
||||
|
||||
asynStatus sinqAxis::reset() { return asynSuccess; }
|
||||
|
||||
asynStatus sinqAxis::enable(bool on) { return asynSuccess; }
|
||||
|
||||
asynStatus sinqAxis::setVeloFields(double velo, double vbas, double vmax) {
|
||||
|
@ -160,10 +160,23 @@ class epicsShareClass sinqAxis : public asynMotorAxis {
|
||||
virtual asynStatus doHome(double minVelocity, double maxVelocity,
|
||||
double acceleration, int forwards);
|
||||
|
||||
/**
|
||||
* @brief This function is called when the PV "$(INSTR)$(M):Reset" is set to
|
||||
* any value. This method should be implemented by a child class of
|
||||
* sinqAxis.
|
||||
*
|
||||
* @return asynStatus
|
||||
*/
|
||||
virtual asynStatus reset();
|
||||
|
||||
/**
|
||||
* @brief This function enables / disables an axis. It should be implemented
|
||||
* by a child class of sinqAxis.
|
||||
*
|
||||
* The concrete implementation should (but doesn't need to) follow the
|
||||
* convetion that a value of 0 disables the axis and any other value enables
|
||||
* it.
|
||||
*
|
||||
* @param on
|
||||
* @return asynStatus
|
||||
*/
|
||||
|
@ -109,6 +109,16 @@ sinqController::sinqController(const char *portName,
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
status = createParam("MOTOR_RESET", asynParamInt32, &motorReset_);
|
||||
if (status != asynSuccess) {
|
||||
asynPrint(this->pasynUserSelf, 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);
|
||||
}
|
||||
|
||||
status = createParam("MOTOR_ENABLE_RBV", asynParamInt32, &motorEnableRBV_);
|
||||
if (status != asynSuccess) {
|
||||
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
|
||||
@ -293,6 +303,8 @@ asynStatus sinqController::writeInt32(asynUser *pasynUser, epicsInt32 value) {
|
||||
// Handle custom PVs
|
||||
if (function == motorEnable_) {
|
||||
return axis->enable(value != 0);
|
||||
} else if (function == motorReset_) {
|
||||
return axis->reset();
|
||||
} else if (function == motorForceStop_) {
|
||||
return axis->stop(0.0);
|
||||
} else {
|
||||
|
@ -245,6 +245,7 @@ class epicsShareClass sinqController : public asynMotorController {
|
||||
|
||||
#define FIRST_SINQMOTOR_PARAM motorMessageText_
|
||||
int motorMessageText_;
|
||||
int motorReset_;
|
||||
int motorEnable_;
|
||||
int motorEnableRBV_;
|
||||
int motorCanDisable_;
|
||||
|
Reference in New Issue
Block a user