Compare commits
5 Commits
Author | SHA1 | Date | |
---|---|---|---|
ca7bede4b7 | |||
d3307db987 | |||
591509bd43 | |||
5854d2c9d0 | |||
f134a61649 |
@ -1,6 +1,6 @@
|
||||
default:
|
||||
image: docker.psi.ch:5000/sinqdev/sinqepics:latest
|
||||
|
||||
|
||||
stages:
|
||||
- lint
|
||||
- build
|
||||
@ -24,16 +24,6 @@ formatting:
|
||||
tags:
|
||||
- sinq
|
||||
|
||||
# clangtidy:
|
||||
# stage: lint
|
||||
# script:
|
||||
# - curl https://docker.psi.ch:5000/v2/_catalog
|
||||
# # - dnf update -y
|
||||
# # - dnf install -y clang-tools-extra
|
||||
# # - clang-tidy sinqEPICSApp/src/*.cpp sinqEPICSApp/src/*.c sinqEPICSApp/src/*.h -checks=cppcoreguidelines-*,cert-*
|
||||
# # tags:
|
||||
# # - sinq
|
||||
|
||||
build_module:
|
||||
stage: build
|
||||
script:
|
||||
@ -48,4 +38,4 @@ build_module:
|
||||
expire_in: 1 week
|
||||
when: always
|
||||
tags:
|
||||
- sinq
|
||||
- sinq
|
||||
|
2
Makefile
2
Makefile
@ -10,10 +10,12 @@ ARCH_FILTER=RHEL%
|
||||
asynMotor_VERSION=7.2.2
|
||||
|
||||
# Source files to build
|
||||
SOURCES += src/msgPrintControl.cpp
|
||||
SOURCES += src/sinqAxis.cpp
|
||||
SOURCES += src/sinqController.cpp
|
||||
|
||||
# Headers which allow using this library in concrete driver implementations
|
||||
HEADERS += src/msgPrintControl.h
|
||||
HEADERS += src/sinqAxis.h
|
||||
HEADERS += src/sinqController.h
|
||||
|
||||
|
54
README.md
54
README.md
@ -42,34 +42,41 @@ epicsEnvSet("INSTR","SQ:SINQTEST:")
|
||||
iocInit()
|
||||
```
|
||||
The first line is a so-called shebang which instructs Linux to execute the file with the executable located at the given path - the IOC shell in this case. The controller script "mcu1.cmd" looks like this:
|
||||
The script for controller 1 ("mcu1.cmd") for a Turbo PMAC (see https://git.psi.ch/sinq-epics-modules/turboPmac) has the following structure. The scripts for other controller types can be found in the README.md of their respective repositories.
|
||||
|
||||
```
|
||||
# Define some needed parameters (they can be safely overwritten in e.g. mcu2.cmd)
|
||||
# Define the name of the controller and the corresponding port
|
||||
epicsEnvSet("NAME","mcu1")
|
||||
epicsEnvSet("ASYN_PORT","p$(NAME)")
|
||||
|
||||
# Define the IP adress of the controller
|
||||
# Create the TCP/IP socket used to talk with the controller. The socket can be adressed from within the IOC shell via the port name
|
||||
drvAsynIPPortConfigure("$(ASYN_PORT)","172.28.101.24:1025")
|
||||
|
||||
# Create the controller object in EPICS. The function "pmacv3Controller" is
|
||||
# provided by loading the shared library turboPmac.
|
||||
pmacv3Controller("$(NAME)","$(ASYN_PORT)",8,0.05,1,0.05);
|
||||
# Create the controller object with the defined name and connect it to the socket via the port name.
|
||||
# The other parameters are as follows:
|
||||
# 8: Maximum number of axes
|
||||
# 0.05: Busy poll period in seconds
|
||||
# 1: Idle poll period in seconds
|
||||
# 1: Socket communication timeout in seconds
|
||||
turboPmacController("$(NAME)", "$(ASYN_PORT)", 8, 0.05, 1, 1);
|
||||
|
||||
# Create four axes objects on slots 1, 2, 3 and 5 of the controller.
|
||||
pmacv3Axis("$(NAME)",1);
|
||||
pmacv3Axis("$(NAME)",2);
|
||||
pmacv3Axis("$(NAME)",3);
|
||||
pmacv3Axis("$(NAME)",5);
|
||||
# Define some axes for the specified MCU at the given slot (1, 2 and 5). No slot may be used twice!
|
||||
turboPmacAxis("$(NAME)",1);
|
||||
turboPmacAxis("$(NAME)",2);
|
||||
turboPmacAxis("$(NAME)",5);
|
||||
|
||||
# Create some general PVs of an asynRecord, substituting the macro P by concatenating INSTR and NAME and PORT by ASYN_PORT.
|
||||
dbLoadRecords("$(sinqMotor_DB)/asynRecord.db","P=$(INSTR)$(NAME),PORT=$(ASYN_PORT)")
|
||||
# Set the number of subsequent timeouts
|
||||
setMaxSubsequentTimeouts("$(NAME)", 20);
|
||||
|
||||
# Create PVs provided by the sinqMotor database template. This template is parametrized by the substitution file "mcu1.substitutions" (see below)
|
||||
# Configure the timeout frequency watchdog:
|
||||
setThresholdComTimeout("$(NAME)", 100, 1);
|
||||
|
||||
# Parametrize the EPICS record database with the substitution file named after the MCU.
|
||||
epicsEnvSet("SINQDBPATH","$(sinqMotor_DB)/sinqMotor.db")
|
||||
dbLoadTemplate("$(TOP)/mcu1.substitutions", "INSTR=$(INSTR)$(NAME):,CONTROLLER=$(NAME)")
|
||||
|
||||
# Create PVs specific for pmacv3. Again, we load a database template and parametrize it with the substitution file "mcu1.substitutions"
|
||||
epicsEnvSet("SINQDBPATH","$(pmacv3_DB)/pmacv3.db")
|
||||
dbLoadTemplate("$(TOP)/mcu1.substitutions", "INSTR=$(INSTR)$(NAME):,CONTROLLER=$(NAME)")
|
||||
dbLoadTemplate("$(TOP)/$(NAME).substitutions", "INSTR=$(INSTR)$(NAME):,CONTROLLER=$(NAME)")
|
||||
epicsEnvSet("SINQDBPATH","$(turboPmac_DB)/turboPmac.db")
|
||||
dbLoadTemplate("$(TOP)/$(NAME).substitutions", "INSTR=$(INSTR)$(NAME):,CONTROLLER=$(NAME)")
|
||||
dbLoadRecords("$(sinqMotor_DB)/asynRecord.db","P=$(INSTR)$(NAME),PORT=$(ASYN_PORT)")
|
||||
```
|
||||
|
||||
### Substitution file
|
||||
@ -189,6 +196,9 @@ sinqMotor offers a variety of additional methods for children classes to standar
|
||||
- `setOffsetMovTimeout`: Set a linear offset for the expected movement time. This function is also available in the IOC shell.
|
||||
- `setScaleMovTimeout`: Set a scaling factor for the expected movement time. This function is also available in the IOC shell.
|
||||
|
||||
#### msgPrintControl.h
|
||||
In addition to the two extension classes this library also includes a mechanism which prevents excessive repetitions of the same error message to the IOC shell via the classes `msgPrintControl` and `msgPrintControlKey`. A detailed description of the mechanism can be found in the docstring of `msgPrintControl`. The implementation of the `poll` function of `sinqAxis` also contains an example how to use it. Using this feature in derived drivers is entirely optional.
|
||||
|
||||
### Versioning
|
||||
|
||||
The versioning is done via git tags. Git tags are recognized by the PSI build system: If you tag a version as 1.0, it will be built into the directory /ioc/modules/sinqMotor/1.0. The tag is directly coupled to a commit so that it is always clear which source code was used to build which binary.
|
||||
@ -197,6 +207,12 @@ All existing tags can be listed with `git tag` in the sinqMotor directory. Detai
|
||||
|
||||
### How to build it
|
||||
|
||||
The makefile in the top directory includes all necessary steps for compiling a shared library together with the header files into `/ioc/modules` (using the PSI EPICS build system). Therefore it is sufficient to clone this repository to a suitable location (`git clone https://git.psi.ch/sinq-epics-modules/sinqmotor/-/tree/main`). Afterwards, switch to the directory (`cd sinqmotor`) and run `make install`.
|
||||
This library is based on the PSI version of the EPICS motor record, which can be found here: `https://git.psi.ch/epics_driver_modules/motorBase`. We use a branch with a bugfix which is currently not merged into master due to resistance of the PSI userbase: `https://git.psi.ch/epics_driver_modules/motorBase/-/tree/pick_fix-lockup-VAL-HOMF-VAL`. This library can be build with the following steps, assuming GCC and make are available:
|
||||
- `git clone https://git.psi.ch/epics_driver_modules/motorBase/-/tree/pick_fix-lockup-VAL-HOMF-VAL`
|
||||
- `cd motorBase`
|
||||
- `git tag 7.2.2`. The latest version on master is currently 7.2.1, hence we increment the bugfix version counter by one
|
||||
- `make install`
|
||||
|
||||
To build sinqMotor itself, the makefile in the top directory includes all necessary steps for compiling a shared library together with the header files into `/ioc/modules` (using the PSI EPICS build system). Therefore it is sufficient to clone this repository to a suitable location (`git clone https://git.psi.ch/sinq-epics-modules/sinqmotor/-/tree/main`). Afterwards, switch to the directory (`cd sinqmotor`) and run `make install`.
|
||||
|
||||
To use the library when writing a concrete motor driver, include it in the makefile of your application / library the same way as other libraries such as e.g. `asynMotor` by adding `REQUIRED+=sinqMotor` to your Makefile. The version can be specified with `sinqMotor_VERSION=x.x.x.`
|
||||
|
100
src/msgPrintControl.cpp
Normal file
100
src/msgPrintControl.cpp
Normal file
@ -0,0 +1,100 @@
|
||||
#include "msgPrintControl.h"
|
||||
#include <unordered_map>
|
||||
|
||||
msgPrintControlKey::msgPrintControlKey(char *controller, int axisNo,
|
||||
const char *functionName, int line) {
|
||||
controller_ = controller;
|
||||
axisNo_ = axisNo;
|
||||
line_ = line;
|
||||
functionName_ = functionName;
|
||||
}
|
||||
|
||||
void msgPrintControlKey::format(char *buffer, size_t bufferSize) {
|
||||
snprintf(buffer, bufferSize, "controller %s, axis %d, function %s, line %d",
|
||||
controller_.c_str(), axisNo_, functionName_, line_);
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
|
||||
msgPrintControl::msgPrintControl(size_t maxRepetitions) {
|
||||
maxRepetitions_ = maxRepetitions;
|
||||
}
|
||||
|
||||
bool msgPrintControl::shouldBePrinted(msgPrintControlKey &key, bool wantToPrint,
|
||||
asynUser *pasynUser) {
|
||||
|
||||
// Reset the suffix
|
||||
suffix_[0] = 0;
|
||||
|
||||
if (wantToPrint) {
|
||||
/*
|
||||
We want to print the message associated with key -> Check if the number
|
||||
of allowed repetitions is exceeded. If true, inform the user that
|
||||
further output is suppressed.
|
||||
*/
|
||||
if (map_.find(key) != map_.end()) {
|
||||
size_t repetitions = map_[key];
|
||||
if (repetitions < maxRepetitions_) {
|
||||
// Number of allowed repetitions not exceeded -> Printing the
|
||||
// message is ok.
|
||||
map_[key] = repetitions + 1;
|
||||
return true;
|
||||
} else if (repetitions == maxRepetitions_) {
|
||||
// Reached number of allowed repetitions -> Printing the message
|
||||
// is ok, but further trys are rejected.
|
||||
char formattedKey[100] = {0};
|
||||
key.format(formattedKey, sizeof(formattedKey));
|
||||
snprintf(suffix_, sizeof(suffix_),
|
||||
" Further repetition of this error message (key "
|
||||
"\"%s\") is suppressed.",
|
||||
formattedKey);
|
||||
map_[key] = repetitions + 1;
|
||||
return true;
|
||||
} else {
|
||||
// Exceeded number of allowed repetitions -> Do not print the
|
||||
// message
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// Message is not yet in map -> create an entry so it is watched in
|
||||
// the future.
|
||||
map_[key] = 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
} else {
|
||||
/*
|
||||
We do not want to print the message associated with key -> If the key is
|
||||
part of the map, set the counter back to zero.
|
||||
*/
|
||||
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);
|
||||
map_[key] = 0;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool msgPrintControl::shouldBePrinted(char *portName, int axisNo,
|
||||
const char *functionName, int line,
|
||||
bool wantToPrint, asynUser *pasynUser) {
|
||||
msgPrintControlKey key =
|
||||
msgPrintControlKey(portName, axisNo, functionName, __LINE__);
|
||||
return shouldBePrinted(key, wantToPrint, pasynUser);
|
||||
}
|
||||
|
||||
void msgPrintControl::resetCount(msgPrintControlKey &key) {
|
||||
if (map_.find(key) != map_.end()) {
|
||||
map_[key] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
char *msgPrintControl::getSuffix() { return suffix_; }
|
133
src/msgPrintControl.h
Normal file
133
src/msgPrintControl.h
Normal file
@ -0,0 +1,133 @@
|
||||
#ifndef msgPrintControl_H
|
||||
#define msgPrintControl_H
|
||||
|
||||
#include <asynDriver.h>
|
||||
#include <string.h>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
/**
|
||||
* @brief Class to identify a message print location. See the docstring of
|
||||
* `msgPrintControl` on how to use this key.
|
||||
*
|
||||
*/
|
||||
class msgPrintControlKey {
|
||||
public:
|
||||
std::string controller_;
|
||||
// -1 is a non-axis specific message
|
||||
int axisNo_;
|
||||
const char *functionName_;
|
||||
int line_;
|
||||
|
||||
msgPrintControlKey(char *controller_, int axisNo, const char *fileName,
|
||||
int line);
|
||||
|
||||
bool operator==(const msgPrintControlKey &other) const {
|
||||
return axisNo_ == other.axisNo_ && line_ == other.line_ &&
|
||||
strcmp(functionName_, other.functionName_) == 0 &&
|
||||
controller_ == other.controller_;
|
||||
}
|
||||
|
||||
void format(char *buffer, size_t bufferSize);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Implementation of the hash functionality for msgPrintControlKey
|
||||
*
|
||||
*/
|
||||
namespace std {
|
||||
template <> struct hash<msgPrintControlKey> {
|
||||
size_t operator()(const msgPrintControlKey &obj) const {
|
||||
// Combine the hashes of the members (x and y)
|
||||
size_t h1 = std::hash<std::string>{}(obj.controller_);
|
||||
size_t h2 = hash<int>{}(obj.axisNo_);
|
||||
size_t h3 = std::hash<const char *>{}(obj.functionName_);
|
||||
size_t h4 = hash<int>{}(obj.line_);
|
||||
// Combine the hashes (simple XOR and shifting technique)
|
||||
return h1 ^ (h2 << 1) ^ (h3 << 2) ^ (h4 << 3);
|
||||
}
|
||||
};
|
||||
} // namespace std
|
||||
|
||||
/**
|
||||
* @brief Class to control the number of repetitions of error messages
|
||||
*
|
||||
* This class is used to prevent excessive repetitions of identical error
|
||||
* messages. For example, if the communication between a controller and an
|
||||
* axis fails, a corresponding error message is created in each poll. This
|
||||
* could "flood" the IOC shell with noise. To prevent this, this class keeps
|
||||
* track of the number of subsequent error message repetition. Each message is
|
||||
* uniquely identified by "msgPrintControlKey". The function shouldBePrinted
|
||||
* can be used in order to see if a message should be printed or not:
|
||||
*
|
||||
* ```
|
||||
* const char* controller = "MCU" // Name of the controller
|
||||
* int axisNo = 0; // Number of the axis
|
||||
* bool wantToPrint = evaluateConditions(...); *
|
||||
* if (msgPrintControl.shouldBePrinted(controller, axisNo, __PRETTY_FUNCTION__,
|
||||
* __LINE__, wantToPrint)) { asynPrint(...)
|
||||
* }
|
||||
*
|
||||
* ```
|
||||
*/
|
||||
class msgPrintControl {
|
||||
public:
|
||||
msgPrintControl(size_t maxRepetitions);
|
||||
|
||||
/**
|
||||
* @brief Checks if the error message associated with "key" has been printed
|
||||
* more than "maxRepetitions_" times in a row. If yes, returns false,
|
||||
* otherwise true. Counter is reset if wantToPrint is false.
|
||||
*
|
||||
* If the conditions for printing a message are met, "wantToPrint" must be
|
||||
* set to true. The function then checks if "maxRepetitions_" has been
|
||||
* exceeded. If yes, the function returns no, indicating that the message
|
||||
* should not be printed. If no, the number of repetitions stored in the map
|
||||
* is incremented and the function returns true, indicating that the message
|
||||
* should be printed.
|
||||
*
|
||||
* If the conditions for printing a message are not met, "wantToPrint" must
|
||||
* be set to false. This resets the map entry.
|
||||
*
|
||||
* @param key Key associated with the message, used to
|
||||
* identify individual messages
|
||||
* @param wantToPrint If the message associated with key should be
|
||||
* printed, this value should be true, otherwise false.
|
||||
* @return bool If true, the message should be printed, if
|
||||
* false, it should not.
|
||||
*/
|
||||
bool shouldBePrinted(msgPrintControlKey &key, bool wantToPrint,
|
||||
asynUser *pasynUser);
|
||||
|
||||
/**
|
||||
* @brief Like `shouldBePrinted(msgPrintControlKey key, bool wantToPrint)`,
|
||||
* but constructs the key from the first four arguments.
|
||||
*
|
||||
* @param controller_
|
||||
* @param axisNo
|
||||
* @param fileName
|
||||
* @param line
|
||||
* @param wantToPrint
|
||||
* @return true
|
||||
* @return false
|
||||
*/
|
||||
bool shouldBePrinted(char *controller, int axisNo, const char *functionName,
|
||||
int line, bool wantToPrint, asynUser *pasynUser);
|
||||
|
||||
void resetCount(msgPrintControlKey &key);
|
||||
|
||||
/**
|
||||
* @brief Maximum number of times a message is printed before it is
|
||||
* suppressed.
|
||||
*
|
||||
*/
|
||||
size_t maxRepetitions_;
|
||||
|
||||
char *getSuffix();
|
||||
|
||||
private:
|
||||
std::unordered_map<msgPrintControlKey, size_t> map_;
|
||||
char suffix_[200] = {0};
|
||||
};
|
||||
|
||||
#endif
|
@ -10,9 +10,7 @@ sinqAxis::sinqAxis(class sinqController *pC, int axisNo)
|
||||
: asynMotorAxis((asynMotorController *)pC, axisNo), pC_(pC) {
|
||||
asynStatus status = asynSuccess;
|
||||
|
||||
initial_poll_ = true;
|
||||
watchdogMovActive_ = false;
|
||||
init_poll_counter_ = 0;
|
||||
scaleMovTimeout_ = 2.0;
|
||||
offsetMovTimeout_ = 30;
|
||||
targetPosition_ = 0.0;
|
||||
@ -188,14 +186,18 @@ asynStatus sinqAxis::poll(bool *moving) {
|
||||
// According to the function documentation of asynMotorAxis::poll, this
|
||||
// function should be called at the end of a poll implementation.
|
||||
pl_status = callParamCallbacks();
|
||||
if (pl_status != asynSuccess) {
|
||||
// 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
|
||||
bool wantToPrint = pl_status != asynSuccess;
|
||||
if (pC_->msgPrintControl_.shouldBePrinted(
|
||||
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, wantToPrint,
|
||||
pC_->pasynUserSelf)) {
|
||||
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
|
||||
"Controller \"%s\", axis %d => %s, line "
|
||||
"%d:\ncallParamCallbacks failed with %s.\n",
|
||||
"%d:\ncallParamCallbacks failed with %s.%s\n",
|
||||
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
|
||||
pC_->stringifyAsynStatus(poll_status));
|
||||
pC_->stringifyAsynStatus(poll_status),
|
||||
pC_->msgPrintControl_.getSuffix());
|
||||
}
|
||||
if (wantToPrint) {
|
||||
poll_status = pl_status;
|
||||
}
|
||||
|
||||
|
@ -4,8 +4,8 @@ This class extends asynMotorAxis by some features used in SINQ.
|
||||
Stefan Mathis, November 2024
|
||||
*/
|
||||
|
||||
#ifndef __SINQDRIVER
|
||||
#define __SINQDRIVER
|
||||
#ifndef sinqAxis_H
|
||||
#define sinqAxis_H
|
||||
#include "asynMotorAxis.h"
|
||||
|
||||
class epicsShareClass sinqAxis : public asynMotorAxis {
|
||||
@ -294,9 +294,6 @@ class epicsShareClass sinqAxis : public asynMotorAxis {
|
||||
friend class sinqController;
|
||||
|
||||
protected:
|
||||
bool initial_poll_;
|
||||
int init_poll_counter_;
|
||||
|
||||
// Internal variables used in the movement timeout watchdog
|
||||
time_t expectedArrivalTime_;
|
||||
time_t offsetMovTimeout_;
|
||||
|
@ -46,15 +46,11 @@ sinqController::sinqController(const char *portName,
|
||||
0, // No additional interfaces beyond those in base class
|
||||
0, // No additional callback interfaces beyond those in base class
|
||||
ASYN_CANBLOCK | ASYN_MULTIDEVICE,
|
||||
1, // autoconnect
|
||||
0,
|
||||
0) // Default priority and stack size
|
||||
{
|
||||
1, // autoconnect
|
||||
0, 0), // Default priority and stack size
|
||||
msgPrintControl_(4) {
|
||||
|
||||
// Initialization of local variables
|
||||
asynStatus status = asynSuccess;
|
||||
|
||||
// Initialization of all member variables
|
||||
lowLevelPortUser_ = nullptr;
|
||||
|
||||
// Initial values for the average timeout mechanism, can be overwritten
|
||||
@ -274,6 +270,10 @@ sinqController::~sinqController(void) {
|
||||
free(this->pAxes_);
|
||||
}
|
||||
|
||||
msgPrintControl &sinqController::getMsgPrintControl() {
|
||||
return msgPrintControl_;
|
||||
}
|
||||
|
||||
asynStatus sinqController::writeInt32(asynUser *pasynUser, epicsInt32 value) {
|
||||
int function = pasynUser->reason;
|
||||
|
||||
@ -281,6 +281,7 @@ asynStatus sinqController::writeInt32(asynUser *pasynUser, epicsInt32 value) {
|
||||
|
||||
asynMotorAxis *asynAxis = getAxis(pasynUser);
|
||||
sinqAxis *axis = dynamic_cast<sinqAxis *>(asynAxis);
|
||||
|
||||
if (axis == nullptr) {
|
||||
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
|
||||
"Controller \"%s\", axis %d => %s, line %d:\nAxis is not an "
|
||||
@ -292,6 +293,8 @@ asynStatus sinqController::writeInt32(asynUser *pasynUser, epicsInt32 value) {
|
||||
// Handle custom PVs
|
||||
if (function == motorEnable_) {
|
||||
return axis->enable(value != 0);
|
||||
} else if (function == motorForceStop_) {
|
||||
return axis->stop(0.0);
|
||||
} else {
|
||||
return asynMotorController::writeInt32(pasynUser, value);
|
||||
}
|
||||
@ -302,10 +305,11 @@ asynStatus sinqController::readInt32(asynUser *pasynUser, epicsInt32 *value) {
|
||||
// Casting into a sinqAxis is necessary to get access to the field axisNo_
|
||||
asynMotorAxis *asynAxis = getAxis(pasynUser);
|
||||
sinqAxis *axis = dynamic_cast<sinqAxis *>(asynAxis);
|
||||
|
||||
if (axis == nullptr) {
|
||||
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
|
||||
"Controller \"%s\", axis %d => %s, line %d:\nAxis is not an "
|
||||
"instance of sinqAxis",
|
||||
"instance of sinqAxis.\n",
|
||||
portName, axis->axisNo_, __PRETTY_FUNCTION__, __LINE__);
|
||||
return asynError;
|
||||
}
|
||||
@ -323,13 +327,13 @@ asynStatus sinqController::errMsgCouldNotParseResponse(const char *command,
|
||||
const char *response,
|
||||
int axisNo,
|
||||
const char *functionName,
|
||||
int lineNumber) {
|
||||
int line) {
|
||||
asynStatus pl_status = asynSuccess;
|
||||
|
||||
asynPrint(lowLevelPortUser_, ASYN_TRACE_ERROR,
|
||||
"Controller \"%s\", axis %d => %s, line %d:\nCould not interpret "
|
||||
"response \"%s\" for command \"%s\".\n",
|
||||
portName, axisNo, functionName, lineNumber, response, command);
|
||||
portName, axisNo, functionName, line, response, command);
|
||||
|
||||
pl_status = setStringParam(
|
||||
motorMessageText_,
|
||||
@ -352,16 +356,17 @@ asynStatus sinqController::paramLibAccessFailed(asynStatus status,
|
||||
const char *parameter,
|
||||
int axisNo,
|
||||
const char *functionName,
|
||||
int lineNumber) {
|
||||
int line) {
|
||||
|
||||
if (status != asynSuccess) {
|
||||
// Log the error message and try to propagate it. If propagating fails,
|
||||
// there is nothing we can do here anyway.
|
||||
asynPrint(lowLevelPortUser_, ASYN_TRACE_ERROR,
|
||||
"Controller \"%s\", axis %d => %s, line %d:\n Accessing the "
|
||||
"parameter library failed for parameter %s with error %s.\n",
|
||||
portName, axisNo, functionName, lineNumber, parameter,
|
||||
portName, axisNo, functionName, line, parameter,
|
||||
stringifyAsynStatus(status));
|
||||
|
||||
// Log the error message and try to propagate it. If propagating fails,
|
||||
// there is nothing we can do here anyway.
|
||||
setStringParam(motorMessageText_,
|
||||
"Accessing paramLib failed. Please call the support.");
|
||||
}
|
||||
@ -391,20 +396,26 @@ asynStatus sinqController::checkComTimeoutWatchdog(int axisNo,
|
||||
}
|
||||
|
||||
// Check if the maximum allowed number of events has been exceeded
|
||||
if (timeoutEvents_.size() > maxNumberTimeouts_) {
|
||||
bool wantToPrint = timeoutEvents_.size() > maxNumberTimeouts_;
|
||||
|
||||
if (msgPrintControl_.shouldBePrinted(portName, axisNo, __PRETTY_FUNCTION__,
|
||||
__LINE__, wantToPrint,
|
||||
pasynUserSelf)) {
|
||||
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
|
||||
"Controller \"%s\", axis %d => %s, line %d:\nMore than %ld "
|
||||
"communication timeouts in %ld "
|
||||
"seconds.%s\n",
|
||||
portName, axisNo, __PRETTY_FUNCTION__, __LINE__,
|
||||
maxNumberTimeouts_, comTimeoutWindow_,
|
||||
msgPrintControl_.getSuffix());
|
||||
}
|
||||
|
||||
if (wantToPrint) {
|
||||
snprintf(motorMessage, motorMessageSize,
|
||||
"More than %ld communication timeouts in %ld seconds. Please "
|
||||
"call the support.",
|
||||
maxNumberTimeouts_, comTimeoutWindow_);
|
||||
|
||||
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
|
||||
"Controller \"%s\", axis %d => %s, line %d:\nMore than %ld "
|
||||
"communication timeouts in %ld "
|
||||
"seconds\n",
|
||||
portName, axisNo, __PRETTY_FUNCTION__, __LINE__,
|
||||
maxNumberTimeouts_, comTimeoutWindow_);
|
||||
|
||||
paramLibStatus = setIntegerParam(motorStatusCommsError_, 1);
|
||||
if (paramLibStatus != asynSuccess) {
|
||||
return paramLibAccessFailed(paramLibStatus,
|
||||
|
@ -5,11 +5,13 @@ README.md for details.
|
||||
Stefan Mathis, November 2024
|
||||
*/
|
||||
|
||||
#ifndef __sinqController
|
||||
#define __sinqController
|
||||
#ifndef sinqController_H
|
||||
#define sinqController_H
|
||||
#include "asynMotorController.h"
|
||||
#include "msgPrintControl.h"
|
||||
#include <deque>
|
||||
#include <initHooks.h>
|
||||
#include <unordered_map>
|
||||
|
||||
#define motorMessageIsFromDriverString "MOTOR_MESSAGE_DRIVER"
|
||||
#define motorMessageTextString "MOTOR_MESSAGE_TEXT"
|
||||
@ -88,13 +90,13 @@ class epicsShareClass sinqController : public asynMotorController {
|
||||
error messages.
|
||||
* @param functionName Name of the caller function. It is recommended
|
||||
to use a macro, e.g. __func__ or __PRETTY_FUNCTION__.
|
||||
* @param lineNumber Source code line where this function is
|
||||
* @param line Source code line where this function is
|
||||
called. It is recommended to use a macro, e.g. __LINE__.
|
||||
* @return asynStatus Returns input status.
|
||||
*/
|
||||
asynStatus paramLibAccessFailed(asynStatus status, const char *parameter,
|
||||
int axisNo, const char *functionName,
|
||||
int lineNumber);
|
||||
int line);
|
||||
|
||||
/**
|
||||
* @brief Error handling in case parsing a command response failed.
|
||||
@ -109,14 +111,13 @@ class epicsShareClass sinqController : public asynMotorController {
|
||||
* @param axisNo_ Axis where the problem occurred
|
||||
* @param functionName Name of the caller function. It is recommended
|
||||
to use a macro, e.g. __func__ or __PRETTY_FUNCTION__.
|
||||
* @param lineNumber Source code line where this function is
|
||||
* @param line Source code line where this function is
|
||||
called. It is recommended to use a macro, e.g. __LINE__.
|
||||
* @return asynStatus Returns asynError.
|
||||
*/
|
||||
asynStatus errMsgCouldNotParseResponse(const char *command,
|
||||
const char *response, int axisNo,
|
||||
const char *functionName,
|
||||
int lineNumber);
|
||||
const char *functionName, int line);
|
||||
|
||||
/**
|
||||
* @brief Convert an asynStatus into a descriptive string.
|
||||
@ -213,13 +214,22 @@ class epicsShareClass sinqController : public asynMotorController {
|
||||
return asynSuccess;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get a reference to the map used to control the maximum number of
|
||||
* message repetitions. See the documentation of printRepetitionWatchdog in
|
||||
* msgPrintControl.h for details.
|
||||
*
|
||||
* @return std::unordered_map<msgPrintControlKey, size_t>&
|
||||
*/
|
||||
msgPrintControl &getMsgPrintControl();
|
||||
|
||||
friend class sinqAxis;
|
||||
|
||||
protected:
|
||||
asynUser *lowLevelPortUser_;
|
||||
|
||||
double movingPollPeriod_;
|
||||
double idlePollPeriod_;
|
||||
msgPrintControl msgPrintControl_;
|
||||
|
||||
// Internal variables used in the communication timeout frequency watchdog
|
||||
time_t comTimeoutWindow_; // Size of the time window
|
||||
|
Reference in New Issue
Block a user