Compare commits
32 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8689c79f19 | |||
| 7ed054d075 | |||
| 7965dd3b2e | |||
| c19e4845e4 | |||
| 4d27783062 | |||
| 5273feef6c | |||
| cccfc79860 | |||
| c65a8de5dd | |||
| 1910eda0b1 | |||
| 977016bdb4 | |||
| ed77125378 | |||
| 4a0c09bd7f | |||
| 1fe21ec192 | |||
| 2fd4851313 | |||
| 55a9fe6f3e | |||
| e618b39687 | |||
| 41dfd1de5a | |||
| 07cab3ac2a | |||
| e194736206 | |||
| 30af284f5d | |||
| 6069aa9194 | |||
| c475beee66 | |||
| b1fe452ed6 | |||
| d395c7bbb7 | |||
| a6f2890c76 | |||
| fef61bc804 | |||
| 3d984f26bc | |||
| 2f8ae23d57 | |||
| 603b3e77af | |||
| 31ff26cb78 | |||
| 43df40aaea | |||
| bdefc6090d |
23
.gitea/workflows/action.yaml
Normal file
23
.gitea/workflows/action.yaml
Normal file
@@ -0,0 +1,23 @@
|
||||
name: Test And Build
|
||||
on: [push]
|
||||
|
||||
jobs:
|
||||
Lint:
|
||||
runs-on: linepics
|
||||
steps:
|
||||
- name: checkout repo
|
||||
uses: actions/checkout@v4
|
||||
- name: cppcheck
|
||||
run: cppcheck --std=c++17 --addon=cert --addon=misc --error-exitcode=1 src/*.cpp
|
||||
- name: formatting
|
||||
run: clang-format --style=file --Werror --dry-run src/*.cpp
|
||||
Build:
|
||||
runs-on: linepics
|
||||
steps:
|
||||
- name: checkout repo
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: 'true'
|
||||
- run: |
|
||||
sed -i 's/ARCH_FILTER=.*/ARCH_FILTER=linux%/' Makefile
|
||||
make install
|
||||
@@ -1,41 +0,0 @@
|
||||
default:
|
||||
image: docker.psi.ch:5000/sinqdev/sinqepics:latest
|
||||
|
||||
stages:
|
||||
- lint
|
||||
- build
|
||||
- test
|
||||
|
||||
cppcheck:
|
||||
stage: lint
|
||||
script:
|
||||
- cppcheck --std=c++17 --addon=cert --addon=misc --error-exitcode=1 src/*.cpp
|
||||
artifacts:
|
||||
expire_in: 1 week
|
||||
tags:
|
||||
- sinq
|
||||
|
||||
formatting:
|
||||
stage: lint
|
||||
script:
|
||||
- clang-format --style=file --Werror --dry-run src/*.cpp
|
||||
artifacts:
|
||||
expire_in: 1 week
|
||||
tags:
|
||||
- sinq
|
||||
|
||||
build_module:
|
||||
stage: build
|
||||
script:
|
||||
- sed -i 's/ARCH_FILTER=.*/ARCH_FILTER=linux%/' Makefile
|
||||
- echo "LIBVERSION=${CI_COMMIT_TAG:-0.0.1}" >> Makefile
|
||||
- make install
|
||||
- cp -rT "/ioc/modules/sinqMotor/$(ls -U /ioc/modules/sinqMotor/ | head -1)" "./sinqMotor-${CI_COMMIT_TAG:-$CI_COMMIT_SHORT_SHA}"
|
||||
artifacts:
|
||||
name: "sinqMotor-${CI_COMMIT_TAG:-$CI_COMMIT_SHORT_SHA}"
|
||||
paths:
|
||||
- "sinqMotor-${CI_COMMIT_TAG:-$CI_COMMIT_SHORT_SHA}/*"
|
||||
expire_in: 1 week
|
||||
when: always
|
||||
tags:
|
||||
- sinq
|
||||
73
README.md
73
README.md
@@ -62,30 +62,28 @@ To find out which version of sinqMotor is needed by a driver, refer to its Makef
|
||||
|
||||
### IOC startup script
|
||||
|
||||
An EPICS IOC for motor control at SINQ is started by executing a script with the IOC shell. In its simplest form, an IOC for two controllers is a file looking like this:
|
||||
An EPICS IOC for motor control at SINQ is started by executing a script with the IOC shell. In its simplest form, an IOC for two controllers run by "exampleDriver" is a file looking like this:
|
||||
```
|
||||
#!/usr/local/bin/iocsh
|
||||
|
||||
# Load libraries needed for the IOC
|
||||
require sinqMotor, 1.0.0
|
||||
require actualDriver, 1.2.0
|
||||
require exampleDriver, 1.0.0
|
||||
|
||||
# Define environment variables used later to parametrize the individual controllers
|
||||
epicsEnvSet("TOP","/ioc/sinq-ioc/sinqtest-ioc/")
|
||||
epicsEnvSet("INSTR","SQ:SINQTEST:")
|
||||
|
||||
# Include other scripts for the controllers 1 and 2
|
||||
< actualDriver.cmd
|
||||
< actualDriver.cmd
|
||||
< exampleDriver1.cmd
|
||||
< exampleDriver2.cmd
|
||||
|
||||
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 ("turboPmac1.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.
|
||||
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 files `exampleDriver1.cmd` or `exampleDriver2` then look like this:
|
||||
|
||||
```
|
||||
# Define the name of the controller and the corresponding port
|
||||
epicsEnvSet("DRIVER_PORT","actualDriver1")
|
||||
epicsEnvSet("DRIVER_PORT","exampleDriver1")
|
||||
epicsEnvSet("IP_PORT","p$(DRIVER_PORT)")
|
||||
|
||||
# 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
|
||||
@@ -116,17 +114,17 @@ setForcedFastPolls("$(DRIVER_PORT)", 10);
|
||||
setThresholdComTimeout("$(DRIVER_PORT)", 300, 10);
|
||||
|
||||
# Parametrize the EPICS record database with the substitution file named after the motor controller.
|
||||
epicsEnvSet("SINQDBPATH","$(sinqMotor_DB)/sinqMotor.db")
|
||||
epicsEnvSet("SINQDBPATH","$(exampleDriver_DB)/sinqMotor.db")
|
||||
dbLoadTemplate("$(TOP)/$(DRIVER_PORT).substitutions", "INSTR=$(INSTR)$(DRIVER_PORT):,CONTROLLER=$(DRIVER_PORT)")
|
||||
epicsEnvSet("SINQDBPATH","$(actualDriver_DB)/turboPmac.db")
|
||||
epicsEnvSet("SINQDBPATH","$(exampleDriver_DB)/turboPmac.db")
|
||||
dbLoadTemplate("$(TOP)/$(DRIVER_PORT).substitutions", "INSTR=$(INSTR)$(DRIVER_PORT):,CONTROLLER=$(DRIVER_PORT)")
|
||||
dbLoadRecords("$(sinqMotor_DB)/asynRecord.db","P=$(INSTR)$(DRIVER_PORT),PORT=$(IP_PORT)")
|
||||
dbLoadRecords("$(exampleDriver_DB)/asynRecord.db","P=$(INSTR)$(DRIVER_PORT),PORT=$(IP_PORT)")
|
||||
```
|
||||
|
||||
### Substitution file
|
||||
|
||||
The substitution file is a table containing axis-specific information which is used to create the axis-specific PVs.
|
||||
To work with sinqMotor, "mcu1.substitutions" needs to look like this (the order of columns does not matter):
|
||||
To work with sinqMotor, `exampleDriver1.substitutions` needs to look like this (the order of columns does not matter):
|
||||
```
|
||||
file "$(SINQDBPATH)"
|
||||
{
|
||||
@@ -206,9 +204,11 @@ transferred to (motor_record_pv_name).MRES or to
|
||||
|
||||
## Developer guide
|
||||
|
||||
### Base classes
|
||||
### File structure
|
||||
|
||||
sinqMotor offers a variety of additional methods for children classes to standardize certain patterns (e.g. writing messages to the IOC shell and the motor message PV). For a detailed description, please see the respective function documentation in the .h-files. All of these functions can be overwritten manually if e.g. a completely different implementation of `poll` is required. Some functions are marked as virtual, because they are called from other functions of sinqMotor and therefore need runtime polymorphism. Functions without that marker are not called anywhere in sinqMotor.
|
||||
sinqMotor offers a variety of additional methods for children classes to standardize certain patterns (e.g. writing messages to the IOC shell and the motor message PV). For a detailed description, please see the respective function documentation in the .h-files. All of these functions can be overwritten manually if e.g. a completely different implementation of `poll` is required. Some functions are marked as virtual, because they are called from other functions of sinqMotor and therefore need runtime polymorphism. Functions without that marker are not called anywhere in sinqMotor (except for `forcedPoll`, which is called in `poll`).
|
||||
|
||||
Adding new virtual methods breaks the ABI and therefore warrants a new major version number!
|
||||
|
||||
#### sinqController.h
|
||||
- `couldNotParseResponse`: Write a standardized message if parsing a device response failed.
|
||||
@@ -230,7 +230,15 @@ It calls `doReset` and performs some fast polls after `doReset` returns.
|
||||
- `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.
|
||||
- `doHome`: This is an empty function which should be overwritten by concrete driver implementations.
|
||||
- `poll`: This is a wrapper around `doPoll` which performs some bookkeeping tasks before and after calling `doPoll`:
|
||||
- `poll`: This is a wrapper around `forcedPoll` which does the following checks before calling `forcedPoll`:
|
||||
- Are there any outstanding fast polls (method `outstandingForcedFastPolls` of the controller returns a value greater zero)?
|
||||
- Was the axis moving last time its status was polled?
|
||||
- Is adaptive polling disabled?
|
||||
- Did an idle period pass since the last poll?
|
||||
If all of these conditions are false, no poll is performed. Otherwise, the `forcedPoll` method is called.
|
||||
This method should not be called in the driver code itself if a poll is needed - use `forcedPoll` instead!
|
||||
|
||||
- `forcedPoll`: This is a wrapper around `doPoll` which performs some bookkeeping tasks before and after calling `doPoll`:
|
||||
|
||||
Before calling `doPoll`:
|
||||
- Check if the paramLib already contains an old error message. If so, put it into a temporary bufffer
|
||||
@@ -255,6 +263,21 @@ It calls `doReset` and performs some fast polls after `doReset` returns.
|
||||
#### 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.
|
||||
|
||||
#### macros.h
|
||||
Contains macros used in `sinqMotor` and derived drivers:
|
||||
|
||||
- **HIDDEN**
|
||||
|
||||
By default, the symbols of classes and functions are hidden to avoid symbol clashes when loading
|
||||
multiple shared libraries which use `sinqMotor`. In order to compile this library with exported
|
||||
symbols, specifiy `-DHIDDEN= ` as a compiler flag (if using the given Makefile, this
|
||||
needs to be added to the `USR_CFLAGS`).
|
||||
|
||||
Derived libraries can use the same mechanism via the macro `HIDDEN` (defined in `macros.h`):
|
||||
```
|
||||
class HIDDEN turboPmacController : public sinqController
|
||||
```
|
||||
|
||||
### 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.
|
||||
@@ -277,18 +300,28 @@ To use the library when writing a concrete motor driver, include it in the makef
|
||||
|
||||
### Usage as static dependency
|
||||
|
||||
This repository is included as a git submodule in some of the driver repositories depending upon sinqMotor. When installing via a Makefile (`make install`) using the PSI build system, the following git command is executed within `/ioc/tools/driver.makefile`:
|
||||
This repository is included as a git submodule in the driver repositories depending upon sinqMotor. When installing via a Makefile (`make install`) using the PSI build system, the following git command is executed within `/ioc/tools/driver.makefile`:
|
||||
|
||||
`git submodule update --init --recursive`
|
||||
|
||||
This forces each submodule to be checked out at the latest commit hash stored in the remote repository. However, this is usually unwanted behaviour, since the higher-level drivers are usually designed to be compiled against a specific version of sinqMotor. In order to set the submodule to a specific version, the following steps need to be done BEFORE calling `make install`:
|
||||
|
||||
- `cd sinqMotor`
|
||||
- `git checkout 0.1`
|
||||
- `git checkout 1.0`
|
||||
- `cd ..`
|
||||
|
||||
Then, the fixation of the version to 0.1 needs to be committed in the parent repository:
|
||||
Then, the fixation of the version to 1.0 needs to be committed in the parent repository:
|
||||
|
||||
- `git commit -m "Update sinqMotor to 0.1"`
|
||||
- `git commit -m "Update sinqMotor to 1.0"`
|
||||
|
||||
After this commit, running `make install` will use the correct driver version for compilation.
|
||||
After this commit, running `make install` will use the correct driver version for compilation.
|
||||
|
||||
If your driver uses another driver as a static dependency via git submodule which in turn includes a sinqMotor submodule, it is not necessary to specify the version of sinqMotor. Instead, specify the desired commit of the direct dependency, commit this change and then update all submodules:
|
||||
|
||||
- `cd turboPmac`
|
||||
- `git checkout 1.0`
|
||||
- `cd ..`
|
||||
- `git commit -m "Update turboPmac to 1.0"`
|
||||
- `git submodule update --init --recursive`
|
||||
|
||||
This will update sinqMotor to the version specified in the 1.0 commit of turboPmac.
|
||||
|
||||
22
src/macros.h
Normal file
22
src/macros.h
Normal file
@@ -0,0 +1,22 @@
|
||||
// Collection of macros used in sinqMotor and derived classes
|
||||
|
||||
#ifndef macros_H
|
||||
#define macros_H
|
||||
|
||||
/*
|
||||
The macro "HIDDEN" hides the symbol of the annotated class / function. This is
|
||||
useful to avoid symbol clashes when loading multiple shared libraries in a
|
||||
single IOC. To override the hiding, add `-DHIDDEN= ` to your compiler
|
||||
flags (in this case, the symbols will be exported with their default
|
||||
visibility).
|
||||
*/
|
||||
#ifndef HIDDEN
|
||||
#if defined(_WIN32) || defined(__CYGWIN__)
|
||||
#define HIDDEN
|
||||
#else
|
||||
#define HIDDEN __attribute__((visibility("hidden")))
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// macros_H
|
||||
#endif
|
||||
@@ -6,6 +6,7 @@
|
||||
#define DefaultMaxRepetitions 4
|
||||
|
||||
#include <asynDriver.h>
|
||||
#include <macros.h>
|
||||
#include <string.h>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
@@ -15,7 +16,7 @@
|
||||
* `msgPrintControl` on how to use this key.
|
||||
*
|
||||
*/
|
||||
class msgPrintControlKey {
|
||||
class HIDDEN msgPrintControlKey {
|
||||
public:
|
||||
std::string controller_;
|
||||
|
||||
@@ -82,7 +83,7 @@ template <> struct hash<msgPrintControlKey> {
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
class msgPrintControl {
|
||||
class HIDDEN msgPrintControl {
|
||||
public:
|
||||
/**
|
||||
* @brief Destroy the msgPrintControl object
|
||||
|
||||
222
src/sinqAxis.cpp
222
src/sinqAxis.cpp
@@ -10,6 +10,8 @@
|
||||
#include <math.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define getControllerMethod pController
|
||||
|
||||
struct sinqAxisImpl {
|
||||
// Internal variables used in the movement timeout watchdog
|
||||
time_t expectedArrivalTime;
|
||||
@@ -28,178 +30,6 @@ struct sinqAxisImpl {
|
||||
epicsTimeStamp lastPollTime;
|
||||
};
|
||||
|
||||
// Generic fallback - if the compiler tries to compile this function, it fails.
|
||||
template <typename T>
|
||||
asynStatus setAxisParam(sinqAxis *axis, const char *indexName,
|
||||
int (sinqController::*func)(), T writeValue,
|
||||
const char *callerFunctionName, int lineNumber) {
|
||||
static_assert(
|
||||
sizeof(T) == 0,
|
||||
"no specialization of setAxisParam exists for the given type");
|
||||
return asynError;
|
||||
}
|
||||
|
||||
template <>
|
||||
asynStatus setAxisParam<int>(sinqAxis *axis, const char *indexName,
|
||||
int (sinqController::*func)(), int writeValue,
|
||||
const char *callerFunctionName, int lineNumber) {
|
||||
int indexValue = (axis->pController()->*func)();
|
||||
asynStatus status = axis->setIntegerParam(indexValue, writeValue);
|
||||
if (status != asynSuccess) {
|
||||
return axis->pController()->paramLibAccessFailed(
|
||||
status, indexName, axis->axisNo(), callerFunctionName, lineNumber);
|
||||
}
|
||||
return asynSuccess;
|
||||
}
|
||||
|
||||
template <>
|
||||
asynStatus setAxisParam<bool>(sinqAxis *axis, const char *indexName,
|
||||
int (sinqController::*func)(), bool writeValue,
|
||||
const char *callerFunctionName, int lineNumber) {
|
||||
return setAxisParam(axis, indexName, func, static_cast<int>(writeValue),
|
||||
callerFunctionName, lineNumber);
|
||||
}
|
||||
|
||||
template <>
|
||||
asynStatus
|
||||
setAxisParam<double>(sinqAxis *axis, const char *indexName,
|
||||
int (sinqController::*func)(), double writeValue,
|
||||
const char *callerFunctionName, int lineNumber) {
|
||||
int indexValue = (axis->pController()->*func)();
|
||||
asynStatus status = axis->setDoubleParam(indexValue, writeValue);
|
||||
if (status != asynSuccess) {
|
||||
return axis->pController()->paramLibAccessFailed(
|
||||
status, indexName, axis->axisNo(), callerFunctionName, lineNumber);
|
||||
}
|
||||
return asynSuccess;
|
||||
}
|
||||
|
||||
template <>
|
||||
asynStatus setAxisParam<char *>(sinqAxis *axis, const char *indexName,
|
||||
int (sinqController::*func)(), char *writeValue,
|
||||
const char *callerFunctionName,
|
||||
int lineNumber) {
|
||||
int indexValue = (axis->pController()->*func)();
|
||||
asynStatus status = axis->setStringParam(indexValue, writeValue);
|
||||
if (status != asynSuccess) {
|
||||
return axis->pController()->paramLibAccessFailed(
|
||||
status, indexName, axis->axisNo(), callerFunctionName, lineNumber);
|
||||
}
|
||||
return asynSuccess;
|
||||
}
|
||||
|
||||
template <>
|
||||
asynStatus setAxisParam<const char *>(sinqAxis *axis, const char *indexName,
|
||||
int (sinqController::*func)(),
|
||||
const char *writeValue,
|
||||
const char *callerFunctionName,
|
||||
int lineNumber) {
|
||||
int indexValue = (axis->pController()->*func)();
|
||||
asynStatus status = axis->setStringParam(indexValue, writeValue);
|
||||
if (status != asynSuccess) {
|
||||
return axis->pController()->paramLibAccessFailed(
|
||||
status, indexName, axis->axisNo(), callerFunctionName, lineNumber);
|
||||
}
|
||||
return asynSuccess;
|
||||
}
|
||||
|
||||
// Generic fallback - if the compiler tries to compile this function, it fails.
|
||||
template <typename T>
|
||||
asynStatus getAxisParam(sinqAxis *axis, const char *indexName,
|
||||
int (sinqController::*func)(), T *readValue,
|
||||
const char *callerFunctionName, int lineNumber) {
|
||||
static_assert(
|
||||
sizeof(T) == 0,
|
||||
"no specialization of getAxisParam exists for the given type");
|
||||
return asynError;
|
||||
}
|
||||
|
||||
template <>
|
||||
asynStatus getAxisParam<int>(sinqAxis *axis, const char *indexName,
|
||||
int (sinqController::*func)(), int *readValue,
|
||||
const char *callerFunctionName, int lineNumber) {
|
||||
int indexValue = (axis->pController()->*func)();
|
||||
asynStatus status = axis->pController()->getIntegerParam(
|
||||
axis->axisNo(), indexValue, readValue);
|
||||
if (status != asynSuccess) {
|
||||
return axis->pController()->paramLibAccessFailed(
|
||||
status, indexName, axis->axisNo(), callerFunctionName, lineNumber);
|
||||
}
|
||||
return asynSuccess;
|
||||
}
|
||||
|
||||
template <>
|
||||
asynStatus getAxisParam<bool>(sinqAxis *axis, const char *indexName,
|
||||
int (sinqController::*func)(), bool *readValue,
|
||||
const char *callerFunctionName, int lineNumber) {
|
||||
return getAxisParam(axis, indexName, func, (int *)readValue,
|
||||
callerFunctionName, lineNumber);
|
||||
}
|
||||
|
||||
template <>
|
||||
asynStatus
|
||||
getAxisParam<double>(sinqAxis *axis, const char *indexName,
|
||||
int (sinqController::*func)(), double *readValue,
|
||||
const char *callerFunctionName, int lineNumber) {
|
||||
int indexValue = (axis->pController()->*func)();
|
||||
asynStatus status = axis->pController()->getDoubleParam(
|
||||
axis->axisNo(), indexValue, readValue);
|
||||
if (status != asynSuccess) {
|
||||
return axis->pController()->paramLibAccessFailed(
|
||||
status, indexName, axis->axisNo(), callerFunctionName, lineNumber);
|
||||
}
|
||||
return asynSuccess;
|
||||
}
|
||||
|
||||
template <>
|
||||
asynStatus getAxisParam<char>(sinqAxis *axis, const char *indexName,
|
||||
int (sinqController::*func)(), char *readValue,
|
||||
const char *callerFunctionName, int lineNumber) {
|
||||
|
||||
int maxChars = 200;
|
||||
|
||||
int indexValue = (axis->pController()->*func)();
|
||||
asynStatus status = axis->pController()->getStringParam(
|
||||
axis->axisNo(), indexValue, maxChars, readValue);
|
||||
if (status != asynSuccess) {
|
||||
return axis->pController()->paramLibAccessFailed(
|
||||
status, indexName, axis->axisNo(), callerFunctionName, lineNumber);
|
||||
}
|
||||
return asynSuccess;
|
||||
}
|
||||
|
||||
template <size_t N>
|
||||
asynStatus getAxisParam(sinqAxis *axis, const char *indexName,
|
||||
int (sinqController::*func)(), char (&readValue)[N],
|
||||
const char *callerFunctionName, int lineNumber) {
|
||||
// Decay the array to char*
|
||||
return getAxisParam<char>(axis, indexName, func,
|
||||
static_cast<char *>(readValue),
|
||||
callerFunctionName, lineNumber);
|
||||
}
|
||||
|
||||
template <>
|
||||
asynStatus
|
||||
getAxisParam<std::string>(sinqAxis *axis, const char *indexName,
|
||||
int (sinqController::*func)(), std::string *readValue,
|
||||
const char *callerFunctionName, int lineNumber) {
|
||||
int indexValue = (axis->pController()->*func)();
|
||||
|
||||
// Convert the pointer to a reference, since getStringParam expects the
|
||||
// latter.
|
||||
std::string &rReadValue = *readValue;
|
||||
|
||||
asynStatus status = axis->pController()->getStringParam(
|
||||
axis->axisNo(), indexValue, rReadValue);
|
||||
if (status != asynSuccess) {
|
||||
return axis->pController()->paramLibAccessFailed(
|
||||
status, indexName, axis->axisNo(), callerFunctionName, lineNumber);
|
||||
}
|
||||
return asynSuccess;
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
|
||||
sinqAxis::sinqAxis(class sinqController *pC, int axisNo)
|
||||
: asynMotorAxis((asynMotorController *)pC, axisNo), pC_(pC) {
|
||||
asynStatus status = asynSuccess;
|
||||
@@ -341,14 +171,7 @@ sinqAxis::sinqAxis(class sinqController *pC, int axisNo)
|
||||
sinqAxis::~sinqAxis() = default;
|
||||
|
||||
asynStatus sinqAxis::poll(bool *moving) {
|
||||
|
||||
// Local variable declaration
|
||||
asynStatus pl_status = asynSuccess;
|
||||
asynStatus poll_status = asynSuccess;
|
||||
int homing = 0;
|
||||
int adaptivePolling = 0;
|
||||
char waitingMessage[pC_->MAXBUF_] = {0};
|
||||
char newMessage[pC_->MAXBUF_] = {0};
|
||||
|
||||
/*
|
||||
If adaptive polling is enabled:
|
||||
@@ -371,22 +194,33 @@ asynStatus sinqAxis::poll(bool *moving) {
|
||||
Check if both adaptive polling is enabled and no forced fast polls are still
|
||||
required.
|
||||
*/
|
||||
if (adaptivePolling != 0 && pC_->outstandingForcedFastPolls() == 0) {
|
||||
// Motor wasn't moving during the last poll
|
||||
if (!pSinqA_->wasMoving) {
|
||||
if (adaptivePolling != 0 && pC_->outstandingForcedFastPolls() == 0 &&
|
||||
!pSinqA_->wasMoving) {
|
||||
|
||||
// Add the idle poll period
|
||||
epicsTimeStamp earliestTimeNextPoll = pSinqA_->lastPollTime;
|
||||
epicsTimeAddSeconds(&earliestTimeNextPoll, pC_->idlePollPeriod());
|
||||
// Add the idle poll period
|
||||
epicsTimeStamp earliestTimeNextPoll = pSinqA_->lastPollTime;
|
||||
epicsTimeAddSeconds(&earliestTimeNextPoll, pC_->idlePollPeriod());
|
||||
|
||||
if (epicsTimeLessThanEqual(&earliestTimeNextPoll, &ts) == 0) {
|
||||
*moving = false;
|
||||
return asynSuccess;
|
||||
}
|
||||
if (epicsTimeLessThanEqual(&earliestTimeNextPoll, &ts) == 0) {
|
||||
*moving = false;
|
||||
return asynSuccess;
|
||||
}
|
||||
}
|
||||
return forcedPoll(moving);
|
||||
}
|
||||
|
||||
asynStatus sinqAxis::forcedPoll(bool *moving) {
|
||||
|
||||
// Local variable declaration
|
||||
asynStatus pl_status = asynSuccess;
|
||||
asynStatus poll_status = asynSuccess;
|
||||
int homing = 0;
|
||||
char waitingMessage[pC_->MAXBUF_] = {0};
|
||||
char newMessage[pC_->MAXBUF_] = {0};
|
||||
|
||||
// Update the start time of the last poll
|
||||
epicsTimeStamp ts;
|
||||
epicsTimeGetCurrent(&ts);
|
||||
pSinqA_->lastPollTime = ts;
|
||||
|
||||
/*
|
||||
@@ -821,6 +655,16 @@ asynStatus sinqAxis::setScaleMovTimeout(time_t scaleMovTimeout) {
|
||||
return asynSuccess;
|
||||
}
|
||||
|
||||
bool sinqAxis::wasMoving() { return pSinqA_->wasMoving; }
|
||||
|
||||
void sinqAxis::setWasMoving(bool wasMoving) { pSinqA_->wasMoving = wasMoving; }
|
||||
|
||||
double sinqAxis::targetPosition() { return pSinqA_->targetPosition; }
|
||||
|
||||
void sinqAxis::setTargetPosition(double targetPosition) {
|
||||
pSinqA_->targetPosition = targetPosition;
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// IOC shell functions
|
||||
extern "C" {
|
||||
|
||||
360
src/sinqAxis.h
360
src/sinqAxis.h
@@ -9,12 +9,13 @@ Stefan Mathis, November 2024
|
||||
#ifndef sinqAxis_H
|
||||
#define sinqAxis_H
|
||||
#include "asynMotorAxis.h"
|
||||
#include <macros.h>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
|
||||
struct sinqAxisImpl;
|
||||
|
||||
class epicsShareClass sinqAxis : public asynMotorAxis {
|
||||
class HIDDEN sinqAxis : public asynMotorAxis {
|
||||
public:
|
||||
/**
|
||||
* @brief Construct a new sinqAxis object
|
||||
@@ -31,6 +32,27 @@ class epicsShareClass sinqAxis : public asynMotorAxis {
|
||||
*/
|
||||
~sinqAxis();
|
||||
|
||||
/**
|
||||
* @brief Check if a poll should be performed. If yes, call `forcedPoll`.
|
||||
*
|
||||
This is a wrapper around `forcedPoll` which does the following checks before
|
||||
calling `forcedPoll`:
|
||||
- Are there any outstanding fast polls (method `outstandingForcedFastPolls`
|
||||
of the controller returns a value greater zero)?
|
||||
- Was the axis moving last time its status was polled?
|
||||
- Is adaptive polling disabled?
|
||||
- Did an idle period pass since the last poll?
|
||||
If all of these conditions are false, no poll is performed. Otherwise, the
|
||||
`forcedPoll` method is called. This method should not be called in the
|
||||
driver code itself if a poll is needed - use `forcedPoll` instead!
|
||||
*
|
||||
* @param moving Forwarded to `forcedPoll` or set to false
|
||||
(depending on whether `forcedPoll was called`).
|
||||
* @return asynStatus Forward the status of `forcedPoll` or set to
|
||||
asynSuccess (depending on whether `forcedPoll was called`).
|
||||
*/
|
||||
virtual asynStatus poll(bool *moving);
|
||||
|
||||
/**
|
||||
* @brief Perform some standardized operations before and after the concrete
|
||||
`doPoll` implementation.
|
||||
@@ -46,7 +68,7 @@ class epicsShareClass sinqAxis : public asynMotorAxis {
|
||||
|
||||
- The flags `motorStatusHome_`, `motorStatusHomed_` and
|
||||
`motorStatusAtHome_` are set to their idle values (0, 1 and 1 respectively)
|
||||
in the `poll()` method once the homing procedure is finished. See the
|
||||
in the `forcedPoll()` method once the homing procedure is finished. See the
|
||||
documentation of the `home()` method for more details.
|
||||
|
||||
- Run `callParamCallbacks()`
|
||||
@@ -56,9 +78,9 @@ class epicsShareClass sinqAxis : public asynMotorAxis {
|
||||
* @param moving Forwarded to `doPoll`.
|
||||
* @return asynStatus Forward the status of `doPoll`, unless one of
|
||||
the parameter library operation fails (in that case, returns the status of
|
||||
the failed operation.
|
||||
the failed operation).
|
||||
*/
|
||||
virtual asynStatus poll(bool *moving);
|
||||
asynStatus forcedPoll(bool *moving);
|
||||
|
||||
/**
|
||||
* @brief Implementation of the "proper", device-specific poll method. This
|
||||
@@ -142,7 +164,7 @@ class epicsShareClass sinqAxis : public asynMotorAxis {
|
||||
*
|
||||
* The flags `motorStatusHome_`, `motorStatusHomed_` and
|
||||
`motorStatusAtHome_` are set to their idle values (0, 1 and 1 respectively)
|
||||
in the `poll()` method once the homing procedure is finished.
|
||||
in the `forcedPoll())` method once the homing procedure is finished.
|
||||
*
|
||||
* @param minVelocity Forwarded to `doHome`.
|
||||
* @param maxVelocity Forwarded to `doHome`.
|
||||
@@ -365,9 +387,52 @@ class epicsShareClass sinqAxis : public asynMotorAxis {
|
||||
asynStatus assertConnected();
|
||||
|
||||
/**
|
||||
* @brief Return a pointer to the axis controller
|
||||
* @brief Return a pointer to the axis controller.
|
||||
*
|
||||
* This function should be overriden in derived classes using the `override`
|
||||
* keyword so the macros `getAxisParamChecked` and `setAxisParamChecked`
|
||||
* work correctly:
|
||||
*
|
||||
* ```
|
||||
* class mySpecialAxis : public sinqAxis {
|
||||
public:
|
||||
mySpecialController* getControllerMethod() override {
|
||||
return mySpecialControllerPtr;
|
||||
}
|
||||
};
|
||||
* ```
|
||||
*/
|
||||
sinqController *pController() { return pC_; };
|
||||
virtual sinqController *pController() { return pC_; };
|
||||
|
||||
/**
|
||||
* @brief Returns true, if the axis was moving in the last poll cycle, and
|
||||
* false otherwise.
|
||||
*
|
||||
* @return true
|
||||
* @return false
|
||||
*/
|
||||
bool wasMoving();
|
||||
|
||||
/**
|
||||
* @brief Override the wasMoving flag (normally, it is automatically updated
|
||||
* during each poll).
|
||||
*
|
||||
*/
|
||||
void setWasMoving(bool wasMoving);
|
||||
|
||||
/**
|
||||
* @brief Read out the last received target position in engineering units.
|
||||
*
|
||||
* @return double
|
||||
*/
|
||||
double targetPosition();
|
||||
|
||||
/**
|
||||
* @brief Override the targetPosition value (normally, it is automatically
|
||||
* updated at every call of the move() method).
|
||||
*
|
||||
*/
|
||||
void setTargetPosition(double targetPosition);
|
||||
|
||||
private:
|
||||
std::unique_ptr<sinqAxisImpl> pSinqA_;
|
||||
@@ -377,6 +442,84 @@ class epicsShareClass sinqAxis : public asynMotorAxis {
|
||||
// =============================================================================
|
||||
// Helper functions and definitions for the macro setAxisParamChecked
|
||||
|
||||
template <typename T> struct TypeTag {};
|
||||
|
||||
// Generic fallback - if the compiler tries to compile this function, it fails.
|
||||
template <typename A, typename C, typename T>
|
||||
asynStatus setAxisParamImpl(A *axis, C *controller, const char *indexName,
|
||||
int (C::*func)(), T writeValue,
|
||||
const char *callerFunctionName, int lineNumber,
|
||||
TypeTag<void>) {
|
||||
static_assert(sizeof(T) == 0, "Unsupported type for setAxisParamImpl");
|
||||
return asynError;
|
||||
}
|
||||
|
||||
template <typename A, typename C>
|
||||
asynStatus setAxisParamImpl(A *axis, C *controller, const char *indexName,
|
||||
int (C::*func)(), int writeValue,
|
||||
const char *callerFunctionName, int lineNumber,
|
||||
TypeTag<int>) {
|
||||
int indexValue = (controller->*func)();
|
||||
asynStatus status = axis->setIntegerParam(indexValue, writeValue);
|
||||
if (status != asynSuccess) {
|
||||
return controller->paramLibAccessFailed(
|
||||
status, indexName, axis->axisNo(), callerFunctionName, lineNumber);
|
||||
}
|
||||
return asynSuccess;
|
||||
}
|
||||
|
||||
template <typename A, typename C>
|
||||
asynStatus setAxisParamImpl(A *axis, C *controller, const char *indexName,
|
||||
int (C::*func)(), bool writeValue,
|
||||
const char *callerFunctionName, int lineNumber,
|
||||
TypeTag<bool>) {
|
||||
return setAxisParamImpl(axis, controller, indexName, func,
|
||||
static_cast<int>(writeValue), callerFunctionName,
|
||||
lineNumber, TypeTag<int>{});
|
||||
}
|
||||
|
||||
template <typename A, typename C>
|
||||
asynStatus setAxisParamImpl(A *axis, C *controller, const char *indexName,
|
||||
int (C::*func)(), double writeValue,
|
||||
const char *callerFunctionName, int lineNumber,
|
||||
TypeTag<double>) {
|
||||
int indexValue = (controller->*func)();
|
||||
asynStatus status = axis->setDoubleParam(indexValue, writeValue);
|
||||
if (status != asynSuccess) {
|
||||
return controller->paramLibAccessFailed(
|
||||
status, indexName, axis->axisNo(), callerFunctionName, lineNumber);
|
||||
}
|
||||
return asynSuccess;
|
||||
}
|
||||
|
||||
template <typename A, typename C>
|
||||
asynStatus setAxisParamImpl(A *axis, C *controller, const char *indexName,
|
||||
int (C::*func)(), char *writeValue,
|
||||
const char *callerFunctionName, int lineNumber,
|
||||
TypeTag<char *>) {
|
||||
int indexValue = (controller->*func)();
|
||||
asynStatus status = axis->setStringParam(indexValue, writeValue);
|
||||
if (status != asynSuccess) {
|
||||
return controller->paramLibAccessFailed(
|
||||
status, indexName, axis->axisNo(), callerFunctionName, lineNumber);
|
||||
}
|
||||
return asynSuccess;
|
||||
}
|
||||
|
||||
template <typename A, typename C>
|
||||
asynStatus setAxisParamImpl(A *axis, C *controller, const char *indexName,
|
||||
int (C::*func)(), const char *writeValue,
|
||||
const char *callerFunctionName, int lineNumber,
|
||||
TypeTag<const char *>) {
|
||||
int indexValue = (controller->*func)();
|
||||
asynStatus status = axis->setStringParam(indexValue, writeValue);
|
||||
if (status != asynSuccess) {
|
||||
return controller->paramLibAccessFailed(
|
||||
status, indexName, axis->axisNo(), callerFunctionName, lineNumber);
|
||||
}
|
||||
return asynSuccess;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Helper function to set an integer / double / string parameter for an
|
||||
* axis in the paramLib
|
||||
@@ -384,8 +527,11 @@ class epicsShareClass sinqAxis : public asynMotorAxis {
|
||||
* This function should not be used directly, but rather through its macro
|
||||
* variant `setAxisParamChecked`.
|
||||
*
|
||||
* @tparam A
|
||||
* @tparam C
|
||||
* @tparam T
|
||||
* @param axis
|
||||
* @param controller
|
||||
* @param indexName
|
||||
* @param func
|
||||
* @param writeValue
|
||||
@@ -393,10 +539,13 @@ class epicsShareClass sinqAxis : public asynMotorAxis {
|
||||
* @param lineNumber
|
||||
* @return asynStatus
|
||||
*/
|
||||
template <typename T>
|
||||
asynStatus setAxisParam(sinqAxis *axis, const char *indexName,
|
||||
int (sinqController::*func)(), T writeValue,
|
||||
const char *callerFunctionName, int lineNumber);
|
||||
template <typename A, typename C, typename T>
|
||||
asynStatus setAxisParam(A *axis, C *controller, const char *indexName,
|
||||
int (C::*func)(), T writeValue,
|
||||
const char *callerFunctionName, int lineNumber) {
|
||||
return setAxisParamImpl(axis, controller, indexName, func, writeValue,
|
||||
callerFunctionName, lineNumber, TypeTag<T>{});
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Macro to set an paramLib parameter and error checking the return value
|
||||
@@ -412,10 +561,10 @@ asynStatus setAxisParam(sinqAxis *axis, const char *indexName,
|
||||
* expands into the following code:
|
||||
* ```
|
||||
* {
|
||||
* int indexValue = axis->pController()->motorStatusProblem_();
|
||||
* int indexValue = controller->motorStatusProblem_();
|
||||
* asynStatus status = axis->setIntegerParam(indexValue, writeValue);
|
||||
* if (status != asynSuccess) {
|
||||
* return axis->pController()->paramLibAccessFailed(
|
||||
* return controller->paramLibAccessFailed(
|
||||
* status, "motorStatusProblem_", axis->axisNo(), __PRETTY_FUNCTION__,
|
||||
* __LINE__);
|
||||
* }
|
||||
@@ -424,23 +573,116 @@ asynStatus setAxisParam(sinqAxis *axis, const char *indexName,
|
||||
* ```
|
||||
* =============================================================================
|
||||
*/
|
||||
#define setAxisParamChecked(axis, indexGetterFunction, writeValue) \
|
||||
{ \
|
||||
asynStatus setStatus = setAxisParam( \
|
||||
axis, #indexGetterFunction, \
|
||||
&std::remove_pointer< \
|
||||
decltype(axis->pController())>::type::indexGetterFunction, \
|
||||
writeValue, __PRETTY_FUNCTION__, __LINE__); \
|
||||
if (setStatus != asynSuccess) { \
|
||||
#define setAxisParamChecked(axis, indexSetterFunction, writeValue) \
|
||||
do { \
|
||||
auto *ctrlPtr = (axis)->pController(); \
|
||||
using ControllerType = \
|
||||
typename std::remove_pointer<decltype(ctrlPtr)>::type; \
|
||||
asynStatus setStatus = \
|
||||
setAxisParam(axis, ctrlPtr, #indexSetterFunction, \
|
||||
static_cast<int (ControllerType::*)()>( \
|
||||
&ControllerType::indexSetterFunction), \
|
||||
writeValue, __PRETTY_FUNCTION__, __LINE__); \
|
||||
if (setStatus != asynSuccess) \
|
||||
return setStatus; \
|
||||
} \
|
||||
}
|
||||
} while (0)
|
||||
|
||||
// =============================================================================
|
||||
// Helper functions and definitions for the macro getAxisParamChecked
|
||||
|
||||
// Generic fallback - if the compiler tries to compile this function, it fails.
|
||||
template <typename A, typename C, typename T>
|
||||
asynStatus getAxisParamImpl(A *axis, C *controller, const char *indexName,
|
||||
int (C::*func)(), T *readValue,
|
||||
const char *callerFunctionName, int lineNumber,
|
||||
size_t msgSize, TypeTag<void>) {
|
||||
static_assert(
|
||||
sizeof(T) == 0,
|
||||
"no specialization of getAxisParam exists for the given type");
|
||||
return asynError;
|
||||
}
|
||||
|
||||
template <typename A, typename C>
|
||||
asynStatus getAxisParamImpl(A *axis, C *controller, const char *indexName,
|
||||
int (C::*func)(), int *readValue,
|
||||
const char *callerFunctionName, int lineNumber,
|
||||
size_t msgSize, TypeTag<int>) {
|
||||
int indexValue = (controller->*func)();
|
||||
asynStatus status =
|
||||
controller->getIntegerParam(axis->axisNo(), indexValue, readValue);
|
||||
if (status != asynSuccess) {
|
||||
return controller->paramLibAccessFailed(
|
||||
status, indexName, axis->axisNo(), callerFunctionName, lineNumber);
|
||||
}
|
||||
return asynSuccess;
|
||||
}
|
||||
|
||||
template <typename A, typename C>
|
||||
asynStatus getAxisParamImpl(A *axis, C *controller, const char *indexName,
|
||||
int (C::*func)(), bool *readValue,
|
||||
const char *callerFunctionName, int lineNumber,
|
||||
size_t msgSize, TypeTag<bool>) {
|
||||
int readValueInt = 0;
|
||||
asynStatus status = getAxisParamImpl(axis, controller, indexName, func,
|
||||
&readValueInt, callerFunctionName,
|
||||
lineNumber, msgSize, TypeTag<int>{});
|
||||
*readValue = readValueInt != 0;
|
||||
return status;
|
||||
}
|
||||
|
||||
template <typename A, typename C>
|
||||
asynStatus getAxisParamImpl(A *axis, C *controller, const char *indexName,
|
||||
int (C::*func)(), double *readValue,
|
||||
const char *callerFunctionName, int lineNumber,
|
||||
size_t msgSize, TypeTag<double>) {
|
||||
int indexValue = (controller->*func)();
|
||||
asynStatus status =
|
||||
controller->getDoubleParam(axis->axisNo(), indexValue, readValue);
|
||||
if (status != asynSuccess) {
|
||||
return controller->paramLibAccessFailed(
|
||||
status, indexName, axis->axisNo(), callerFunctionName, lineNumber);
|
||||
}
|
||||
return asynSuccess;
|
||||
}
|
||||
|
||||
template <typename A, typename C>
|
||||
asynStatus getAxisParamImpl(A *axis, C *controller, const char *indexName,
|
||||
int (C::*func)(), char *readValue,
|
||||
const char *callerFunctionName, int lineNumber,
|
||||
size_t msgSize, TypeTag<char>) {
|
||||
|
||||
int indexValue = (controller->*func)();
|
||||
asynStatus status = controller->getStringParam(axis->axisNo(), indexValue,
|
||||
msgSize, readValue);
|
||||
if (status != asynSuccess) {
|
||||
return controller->paramLibAccessFailed(
|
||||
status, indexName, axis->axisNo(), callerFunctionName, lineNumber);
|
||||
}
|
||||
return asynSuccess;
|
||||
}
|
||||
|
||||
template <typename A, typename C>
|
||||
asynStatus getAxisParamImpl(A *axis, C *controller, const char *indexName,
|
||||
int (C::*func)(), std::string *readValue,
|
||||
const char *callerFunctionName, int lineNumber,
|
||||
size_t msgSize, TypeTag<std::string>) {
|
||||
int indexValue = (controller->*func)();
|
||||
|
||||
// Convert the pointer to a reference, since getStringParam expects the
|
||||
// latter.
|
||||
std::string &rReadValue = *readValue;
|
||||
|
||||
asynStatus status =
|
||||
controller->getStringParam(axis->axisNo(), indexValue, rReadValue);
|
||||
if (status != asynSuccess) {
|
||||
return controller->paramLibAccessFailed(
|
||||
status, indexName, axis->axisNo(), callerFunctionName, lineNumber);
|
||||
}
|
||||
return asynSuccess;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Helper function to set an integer / double / string parameter for an
|
||||
* @brief Helper function to get an integer / double / string parameter for an
|
||||
* axis in the paramLib
|
||||
*
|
||||
* This function should not be used directly, but rather through its macro
|
||||
@@ -448,6 +690,7 @@ asynStatus setAxisParam(sinqAxis *axis, const char *indexName,
|
||||
*
|
||||
* @tparam T
|
||||
* @param axis
|
||||
* @param controller
|
||||
* @param indexName
|
||||
* @param func
|
||||
* @param readValue
|
||||
@@ -458,17 +701,48 @@ asynStatus setAxisParam(sinqAxis *axis, const char *indexName,
|
||||
* to.
|
||||
* @return asynStatus
|
||||
*/
|
||||
template <typename T>
|
||||
asynStatus getAxisParam(sinqAxis *axis, const char *indexName,
|
||||
int (sinqController::*func)(), T *readValue,
|
||||
const char *callerFunctionName, int lineNumber);
|
||||
template <typename A, typename C, typename T>
|
||||
asynStatus getAxisParam(A *axis, C *controller, const char *indexName,
|
||||
int (C::*func)(), T *readValue,
|
||||
const char *callerFunctionName, int lineNumber) {
|
||||
return getAxisParamImpl(axis, controller, indexName, func, readValue,
|
||||
callerFunctionName, lineNumber,
|
||||
controller->msgSize(), TypeTag<T>{});
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Helper function to get a string parameter for an
|
||||
* axis in the paramLib into a char array
|
||||
*
|
||||
* This function should not be used directly, but rather through its macro
|
||||
* variant `getAxisParamChecked`. It is a specialized variant of the general
|
||||
* getAxisParam defined above for char arrays.
|
||||
*
|
||||
* @tparam A
|
||||
* @tparam C
|
||||
* @tparam N
|
||||
* @param axis
|
||||
* @param controller
|
||||
* @param indexName
|
||||
* @param func
|
||||
* @param callerFunctionName
|
||||
* @param lineNumber
|
||||
* @return asynStatus
|
||||
*/
|
||||
template <typename A, typename C, size_t N>
|
||||
asynStatus getAxisParam(A *axis, C *controller, const char *indexName,
|
||||
int (C::*func)(), char (*readValue)[N],
|
||||
const char *callerFunctionName, int lineNumber) {
|
||||
return getAxisParamImpl(axis, controller, indexName, func, *readValue,
|
||||
callerFunctionName, lineNumber, N, TypeTag<char>{});
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Macro to get an paramLib parameter and error checking the return value
|
||||
*
|
||||
* This macro is a wrapper around `getIntegerParam` / `getDoubleParam` /
|
||||
* `getStringParam` which checks if the operation was successfull. If it wasn't,
|
||||
* it returns by calling the paramLibAccessFailed function.
|
||||
* it returns by calling the paramLibAccessFailed function. In order
|
||||
*
|
||||
* For example, the following input:
|
||||
* ```
|
||||
@@ -477,10 +751,10 @@ asynStatus getAxisParam(sinqAxis *axis, const char *indexName,
|
||||
* expands into the following code:
|
||||
* ```
|
||||
* {
|
||||
* int indexValue = axis->pController()->motorStatusProblem_();
|
||||
* asynStatus status = axis->pController()->getIntegerParam(axis->axisNo(),
|
||||
* int indexValue = controller->motorStatusProblem_();
|
||||
* asynStatus status = controller->getIntegerParam(axis->axisNo(),
|
||||
* indexValue, readValue); if (status != asynSuccess) { return
|
||||
* axis->pController()->paramLibAccessFailed( status, "motorStatusProblem_",
|
||||
* controller->paramLibAccessFailed( status, "motorStatusProblem_",
|
||||
* axis->axisNo(), __PRETTY_FUNCTION__,
|
||||
* __LINE__);
|
||||
* }
|
||||
@@ -490,15 +764,17 @@ asynStatus getAxisParam(sinqAxis *axis, const char *indexName,
|
||||
* =============================================================================
|
||||
*/
|
||||
#define getAxisParamChecked(axis, indexGetterFunction, readValue) \
|
||||
{ \
|
||||
asynStatus getStatus = getAxisParam( \
|
||||
axis, #indexGetterFunction, \
|
||||
&std::remove_pointer< \
|
||||
decltype(axis->pController())>::type::indexGetterFunction, \
|
||||
readValue, __PRETTY_FUNCTION__, __LINE__); \
|
||||
if (getStatus != asynSuccess) { \
|
||||
do { \
|
||||
auto *ctrlPtr = (axis)->pController(); \
|
||||
using ControllerType = \
|
||||
typename std::remove_pointer<decltype(ctrlPtr)>::type; \
|
||||
asynStatus getStatus = \
|
||||
getAxisParam(axis, ctrlPtr, #indexGetterFunction, \
|
||||
static_cast<int (ControllerType::*)()>( \
|
||||
&ControllerType::indexGetterFunction), \
|
||||
readValue, __PRETTY_FUNCTION__, __LINE__); \
|
||||
if (getStatus != asynSuccess) \
|
||||
return getStatus; \
|
||||
} \
|
||||
}
|
||||
} while (0)
|
||||
|
||||
#endif
|
||||
#endif
|
||||
@@ -12,6 +12,7 @@ Stefan Mathis, November 2024
|
||||
#include "asynMotorController.h"
|
||||
#include "msgPrintControl.h"
|
||||
#include <initHooks.h>
|
||||
#include <macros.h>
|
||||
#include <memory>
|
||||
|
||||
#define motorMessageIsFromDriverString "MOTOR_MESSAGE_DRIVER"
|
||||
@@ -20,9 +21,9 @@ Stefan Mathis, November 2024
|
||||
#define AbsoluteEncoder "absolute"
|
||||
#define NoEncoder "none"
|
||||
|
||||
struct sinqControllerImpl;
|
||||
struct HIDDEN sinqControllerImpl;
|
||||
|
||||
class epicsShareClass sinqController : public asynMotorController {
|
||||
class HIDDEN sinqController : public asynMotorController {
|
||||
public:
|
||||
/**
|
||||
* @brief Construct a new sinqController object
|
||||
@@ -380,9 +381,17 @@ class epicsShareClass sinqController : public asynMotorController {
|
||||
*/
|
||||
int outstandingForcedFastPolls();
|
||||
|
||||
// Maximum error message buffer size. This is an empirical value which must
|
||||
// be large enough to avoid overflows for all commands to the device /
|
||||
// responses from it.
|
||||
/**
|
||||
* @brief Return the maximum error message buffer size
|
||||
*
|
||||
* This is an empirical value which must be large enough to avoid overflows
|
||||
* for all commands to the device / responses from it.
|
||||
*
|
||||
* @return uint32_t
|
||||
*/
|
||||
uint32_t msgSize() { return MAXBUF_; }
|
||||
|
||||
// Maximum message size
|
||||
static const uint32_t MAXBUF_ = 200;
|
||||
|
||||
// =========================================================================
|
||||
|
||||
Reference in New Issue
Block a user