19 Commits

Author SHA1 Message Date
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
6553b468c8 Updated to sinqMotor 1.4.0 and hid all symbols
Some checks failed
Test And Build / Lint (push) Failing after 3s
Test And Build / Build (push) Successful in 7s
2025-08-12 09:44:15 +02:00
954fc82414 Moved error handling out of error read condition.
Some checks failed
Test And Build / Lint (push) Failing after 9s
Test And Build / Build (push) Successful in 12s
Previously, error messaging was only done after the error has been read.
This means that cached errors were simply ignored, if e.g. the motor was
moving. This commit now messages an error as long as it exists in the
cache "masterMacsAxisImpl->axisError".
2025-08-05 09:04:11 +02:00
ff183576ec Added axis reinitialization after node reset
When resetting the node, values within the controller may change, which
need to be reread by the init function.
2025-08-05 08:58:50 +02:00
83f9be3be8 Switched to forcedPoll method 2025-07-24 13:20:30 +02:00
2c0c9a33b7 Updated sinqMotor version 2025-07-24 13:19:24 +02:00
8bb81b1716 Fixed wrong comments
Some checks failed
Test And Build / Lint (push) Failing after 4s
Test And Build / Build (push) Successful in 8s
2025-07-22 17:13:09 +02:00
c32708e49c Removed lastSpeed caching
Some checks failed
Test And Build / Lint (push) Failing after 4s
Test And Build / Build (push) Successful in 7s
After discussion with Sven Schlienger, I now send the new speed
unconditionally before each move command. This also means that the
caching of the last speed is no longer needed.
2025-07-16 11:23:37 +02:00
14a733fb67 adds gitea action
Some checks failed
Test And Build / Lint (push) Failing after 4s
Test And Build / Build (push) Successful in 8s
2025-07-04 14:18:44 +02:00
64b932c3ae Fixed bug 2025-07-01 10:05:39 +02:00
09897b6125 Added node reset to doReset 2025-07-01 10:03:20 +02:00
9d47cd4e24 Fixed docs 2025-06-18 09:26:32 +02:00
52fa4e1e78 Updated sinqMotor version 2025-06-18 08:35:00 +02:00
a9e08460d9 Updated sinqMotor 2025-06-18 08:17:21 +02:00
37dacbd3ee Updated sinqMotor version and removed unnecessary require 2025-06-17 13:21:59 +02:00
d198dc8c9f Use axisParam accessor macros 2025-06-17 13:21:55 +02:00
13f2ec4fbf Switched to macro-based getters and setters for paramLib 2025-06-17 13:21:08 +02:00
6ccb60f263 Merge pull request 'Remove sinqmotor dep, it's statically linked. specify rhel8 arch' (#1) from makefile-fixes into main
Reviewed-on: #1
2025-06-11 16:52:42 +02:00
10 changed files with 400 additions and 559 deletions

View File

@@ -0,0 +1,24 @@
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
echo -e "\nIGNORE_SUBMODULES += sinqmotor" >> Makefile
make install

View File

@@ -1,48 +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 --suppress=cert-STR07-C --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:
- export SINQMOTOR_VERSION="$(grep 'sinqMotor_VERSION=' Makefile | cut -d= -f2)"
- git clone --depth 1 --branch "${SINQMOTOR_VERSION}" https://gitlab-ci-token:${CI_JOB_TOKEN}@git.psi.ch/sinq-epics-modules/sinqmotor.git
- pushd sinqmotor
- sed -i 's/ARCH_FILTER=.*/ARCH_FILTER=linux%/' Makefile
- echo "LIBVERSION=${SINQMOTOR_VERSION}" >> Makefile
- make install
- popd
- sed -i 's/ARCH_FILTER=.*/ARCH_FILTER=linux%/' Makefile
- echo "LIBVERSION=${CI_COMMIT_TAG:-0.0.1}" >> Makefile
- make install
- cp -rT "/ioc/modules/masterMacs/$(ls -U /ioc/modules/masterMacs/ | head -1)" "./masterMacs-${CI_COMMIT_TAG:-$CI_COMMIT_SHORT_SHA}"
artifacts:
name: "masterMacs-${CI_COMMIT_TAG:-$CI_COMMIT_SHORT_SHA}"
paths:
- "masterMacs-${CI_COMMIT_TAG:-$CI_COMMIT_SHORT_SHA}/*"
expire_in: 1 week
when: always
tags:
- sinq

View File

@@ -4,7 +4,7 @@ include /ioc/tools/driver.makefile
MODULE=masterMacs
BUILDCLASSES=Linux
EPICS_VERSIONS=7.0.7
ARCH_FILTER=RHEL8%
ARCH_FILTER=RHEL%
# Specify the version of asynMotor we want to build against
motorBase_VERSION=7.2.2
@@ -23,6 +23,7 @@ SOURCES += src/masterMacsController.cpp
# Store the record files
TEMPLATES += sinqMotor/db/asynRecord.db
TEMPLATES += sinqMotor/db/sinqMotor.db
TEMPLATES += db/masterMacs.db
# This file registers the motor-specific functions in the IOC shell.
DBDS += sinqMotor/src/sinqMotor.dbd

View File

@@ -55,11 +55,11 @@ setMaxSubsequentTimeouts("$(NAME)", 20);
setThresholdComTimeout("$(NAME)", 100, 1);
# Parametrize the EPICS record database with the substitution file named after the MCU.
epicsEnvSet("SINQDBPATH","$(sinqMotor_DB)/sinqMotor.db")
epicsEnvSet("SINQDBPATH","$(masterMacs_DB)/sinqMotor.db")
dbLoadTemplate("$(TOP)/$(NAME).substitutions", "INSTR=$(INSTR)$(NAME):,CONTROLLER=$(NAME)")
epicsEnvSet("SINQDBPATH","$(masterMacs_DB)/masterMacs.db")
dbLoadTemplate("$(TOP)/$(NAME).substitutions", "INSTR=$(INSTR)$(NAME):,CONTROLLER=$(NAME)")
dbLoadRecords("$(sinqMotor_DB)/asynRecord.db","P=$(INSTR)$(NAME),PORT=$(ASYN_PORT)")
dbLoadRecords("$(masterMacs_DB)/asynRecord.db","P=$(INSTR)$(NAME),PORT=$(ASYN_PORT)")
```
### Versioning

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

@@ -19,7 +19,6 @@ struct masterMacsAxisImpl {
std::bitset<16> axisStatus;
std::bitset<16> axisError;
double lastSetSpeed;
bool waitForHandshake;
time_t timeAtHandshake;
bool needInit = true;
@@ -122,7 +121,6 @@ masterMacsAxis::masterMacsAxis(masterMacsController *pC, int axisNo)
pMasterMacsA_ = std::make_unique<masterMacsAxisImpl>((masterMacsAxisImpl){
.axisStatus = std::bitset<16>(0),
.axisError = std::bitset<16>(0),
.lastSetSpeed = 0.0,
.waitForHandshake = false,
.timeAtHandshake = 0,
.targetReachedUninitialized = true,
@@ -262,11 +260,6 @@ asynStatus masterMacsAxis::init() {
__PRETTY_FUNCTION__, __LINE__);
}
// Cache the motor speed. If this value differs from the one in the motor
// record at the start of a movement, the motor record value is sent to
// MasterMACS.
pMasterMacsA_->lastSetSpeed = motorVelocity;
// Store the motor position in the parameter library
pl_status = setMotorPosition(motorPosition);
if (pl_status != asynSuccess) {
@@ -363,14 +356,8 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
}
if (timedOut) {
pl_status =
setStringParam(pC_->motorMessageText(),
"Timed out while waiting for a handshake");
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorMessageText_",
axisNo_, __PRETTY_FUNCTION__,
__LINE__);
}
setAxisParamChecked(this, motorMessageText,
"Timed out while waiting for a handshake");
}
pC_->read(axisNo_, 86, response);
@@ -394,32 +381,15 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
// poll. This is already part of the movement procedure.
*moving = true;
pl_status = setIntegerParam(pC_->motorStatusMoving(), *moving);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status,
"motorStatusMoving_", axisNo_,
__PRETTY_FUNCTION__, __LINE__);
}
pl_status = setIntegerParam(pC_->motorStatusDone(), !(*moving));
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorStatusDone_",
axisNo_, __PRETTY_FUNCTION__,
__LINE__);
}
setAxisParamChecked(this, motorStatusMoving, *moving);
setAxisParamChecked(this, motorStatusDone, !(*moving));
return asynSuccess;
}
}
// Motor resolution from parameter library
pl_status = pC_->getDoubleParam(axisNo_, pC_->motorRecResolution(),
&motorRecResolution);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorRecResolution_",
axisNo_, __PRETTY_FUNCTION__,
__LINE__);
}
getAxisParamChecked(this, motorRecResolution, &motorRecResolution);
// Read the previous motor position
pl_status = motorPosition(&previousPosition);
@@ -462,195 +432,181 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
Read out the error if either a fault condition status flag has been set or
if a movement has just ended.
*/
if (faultConditionSet() || !(*moving)) {
rw_status = readAxisError();
}
msgPrintControlKey keyError = msgPrintControlKey(
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__);
if (faultConditionSet() || !(*moving)) {
rw_status = readAxisError();
/*
A communication error is a special case. If a communication between
controller and axis error occurred, all subsequent errors are ignored,
since this information is not reliable.
*/
if (communicationError()) {
if (pC_->getMsgPrintControl().shouldBePrinted(keyError, true,
pC_->pasynUser())) {
asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line "
"%d\nCommunication error.%s\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
pC_->getMsgPrintControl().getSuffix());
}
setAxisParamChecked(this, motorMessageText,
"Communication error between PC and motor "
"controller. Please call the support.");
poll_status = asynError;
} else {
// This buffer must be initialized to zero because we build the
// error message by appending strings.
char errorMessage[pC_->MAXBUF_] = {0};
char shellMessage[pC_->MAXBUF_] = {0};
// Concatenate all other errors
if (shortCircuit()) {
appendErrorMessage(shellMessage, sizeof(shellMessage),
"Short circuit fault.");
appendErrorMessage(errorMessage, sizeof(errorMessage),
"Short circuit error. Please call the support.");
poll_status = asynError;
}
if (encoderError()) {
appendErrorMessage(shellMessage, sizeof(shellMessage),
"Encoder error.");
appendErrorMessage(errorMessage, sizeof(errorMessage),
"Encoder error. Please call the support.");
poll_status = asynError;
}
if (followingError()) {
appendErrorMessage(shellMessage, sizeof(shellMessage),
"Maximum callowed following error exceeded.");
appendErrorMessage(
errorMessage, sizeof(errorMessage),
"Maximum allowed following error exceeded.Check if "
"movement range is blocked. Otherwise please call the "
"support.");
poll_status = asynError;
}
if (feedbackError()) {
appendErrorMessage(shellMessage, sizeof(shellMessage),
"Feedback error.");
appendErrorMessage(errorMessage, sizeof(errorMessage),
"Feedback error. Please call the support.");
poll_status = asynError;
}
/*
A communication error is a special case. If a communication between
controller and axis error occurred, all subsequent errors are ignored,
since this information is not reliable.
*/
if (communicationError()) {
Either the software limits or the end switches of the controller
have been hit. Since the EPICS limits are derived from the software
limits and are a little bit smaller, these error cases can only
happen if either the axis has an incremental encoder which is not
properly homed or if a bug occured.
*/
if (positiveLimitSwitch() || negativeLimitSwitch() ||
positiveSoftwareLimit() || negativeSoftwareLimit()) {
// Distinction for developers
if (positiveLimitSwitch()) {
appendErrorMessage(shellMessage, sizeof(shellMessage),
"Positive limit switch.");
}
if (negativeLimitSwitch()) {
appendErrorMessage(shellMessage, sizeof(shellMessage),
"Negative limit switch.");
}
if (positiveSoftwareLimit()) {
appendErrorMessage(shellMessage, sizeof(shellMessage),
"Positive software limit.");
}
if (negativeSoftwareLimit()) {
appendErrorMessage(shellMessage, sizeof(shellMessage),
"Negative software limit.");
}
// Generic error message for user
appendErrorMessage(
errorMessage, sizeof(errorMessage),
"Software limits or end switch hit. Try homing the motor, "
"moving in the opposite direction or check the SPS for "
"errors (if available). Otherwise please call the "
"support.");
poll_status = asynError;
}
if (overCurrent()) {
appendErrorMessage(shellMessage, sizeof(shellMessage),
"Overcurrent error.");
appendErrorMessage(errorMessage, sizeof(errorMessage),
"Overcurrent error. Please call the support.");
poll_status = asynError;
}
if (overTemperature()) {
appendErrorMessage(shellMessage, sizeof(shellMessage),
"Overtemperature error.");
appendErrorMessage(
errorMessage, sizeof(errorMessage),
"Overtemperature error. Please call the support.");
poll_status = asynError;
}
if (overVoltage()) {
appendErrorMessage(shellMessage, sizeof(shellMessage),
"Overvoltage error.");
appendErrorMessage(errorMessage, sizeof(errorMessage),
"Overvoltage error. Please call the support.");
poll_status = asynError;
}
if (underVoltage()) {
appendErrorMessage(shellMessage, sizeof(shellMessage),
"Undervoltage error.");
appendErrorMessage(errorMessage, sizeof(errorMessage),
"Undervoltage error. Please call the support.");
poll_status = asynError;
}
if (stoFault()) {
appendErrorMessage(shellMessage, sizeof(shellMessage),
"STO input is on disable state.");
appendErrorMessage(errorMessage, sizeof(errorMessage),
"STO fault. Please call the support.");
poll_status = asynError;
}
if (strlen(shellMessage) > 0) {
if (pC_->getMsgPrintControl().shouldBePrinted(keyError, true,
pC_->pasynUser())) {
asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line "
"%d\nCommunication error.%s\n",
"%d\n%s%s\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
pC_->getMsgPrintControl().getSuffix());
}
pl_status =
setStringParam(pC_->motorMessageText(),
"Communication error between PC and motor "
"controller. Please call the support.");
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorMessageText_",
axisNo_, __PRETTY_FUNCTION__,
__LINE__);
}
poll_status = asynError;
} else {
// This buffer must be initialized to zero because we build the
// error message by appending strings.
char errorMessage[pC_->MAXBUF_] = {0};
char shellMessage[pC_->MAXBUF_] = {0};
// Concatenate all other errors
if (shortCircuit()) {
appendErrorMessage(shellMessage, sizeof(shellMessage),
"Short circuit fault.");
appendErrorMessage(
errorMessage, sizeof(errorMessage),
"Short circuit error. Please call the support.");
poll_status = asynError;
}
if (encoderError()) {
appendErrorMessage(shellMessage, sizeof(shellMessage),
"Encoder error.");
appendErrorMessage(errorMessage, sizeof(errorMessage),
"Encoder error. Please call the support.");
poll_status = asynError;
}
if (followingError()) {
appendErrorMessage(
shellMessage, sizeof(shellMessage),
"Maximum callowed following error exceeded.");
appendErrorMessage(
errorMessage, sizeof(errorMessage),
"Maximum allowed following error exceeded.Check if "
"movement range is blocked. Otherwise please call the "
"support.");
poll_status = asynError;
}
if (feedbackError()) {
appendErrorMessage(shellMessage, sizeof(shellMessage),
"Feedback error.");
appendErrorMessage(errorMessage, sizeof(errorMessage),
"Feedback error. Please call the support.");
poll_status = asynError;
}
/*
Either the software limits or the end switches of the controller
have been hit. Since the EPICS limits are derived from the software
limits and are a little bit smaller, these error cases can only
happen if either the axis has an incremental encoder which is not
properly homed or if a bug occured.
*/
if (positiveLimitSwitch() || negativeLimitSwitch() ||
positiveSoftwareLimit() || negativeSoftwareLimit()) {
// Distinction for developers
if (positiveLimitSwitch()) {
appendErrorMessage(shellMessage, sizeof(shellMessage),
"Positive limit switch.");
}
if (negativeLimitSwitch()) {
appendErrorMessage(shellMessage, sizeof(shellMessage),
"Negative limit switch.");
}
if (positiveSoftwareLimit()) {
appendErrorMessage(shellMessage, sizeof(shellMessage),
"Positive software limit.");
}
if (negativeSoftwareLimit()) {
appendErrorMessage(shellMessage, sizeof(shellMessage),
"Negative software limit.");
}
// Generic error message for user
appendErrorMessage(
errorMessage, sizeof(errorMessage),
"Software limits or end switch hit. Try homing the motor, "
"moving in the opposite direction or check the SPS for "
"errors (if available). Otherwise please call the "
"support.");
poll_status = asynError;
}
if (overCurrent()) {
appendErrorMessage(shellMessage, sizeof(shellMessage),
"Overcurrent error.");
appendErrorMessage(
errorMessage, sizeof(errorMessage),
"Overcurrent error. Please call the support.");
poll_status = asynError;
}
if (overTemperature()) {
appendErrorMessage(shellMessage, sizeof(shellMessage),
"Overtemperature error.");
appendErrorMessage(
errorMessage, sizeof(errorMessage),
"Overtemperature error. Please call the support.");
poll_status = asynError;
}
if (overVoltage()) {
appendErrorMessage(shellMessage, sizeof(shellMessage),
"Overvoltage error.");
appendErrorMessage(
errorMessage, sizeof(errorMessage),
"Overvoltage error. Please call the support.");
poll_status = asynError;
}
if (underVoltage()) {
appendErrorMessage(shellMessage, sizeof(shellMessage),
"Undervoltage error.");
appendErrorMessage(
errorMessage, sizeof(errorMessage),
"Undervoltage error. Please call the support.");
poll_status = asynError;
}
if (stoFault()) {
appendErrorMessage(shellMessage, sizeof(shellMessage),
"STO input is on disable state.");
appendErrorMessage(errorMessage, sizeof(errorMessage),
"STO fault. Please call the support.");
poll_status = asynError;
}
if (strlen(shellMessage) > 0) {
if (pC_->getMsgPrintControl().shouldBePrinted(
keyError, true, pC_->pasynUser())) {
asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line "
"%d\n%s%s\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__,
__LINE__, shellMessage,
pC_->getMsgPrintControl().getSuffix());
}
}
pl_status = setStringParam(pC_->motorMessageText(), errorMessage);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorMessageText_",
axisNo_, __PRETTY_FUNCTION__,
__LINE__);
shellMessage, pC_->getMsgPrintControl().getSuffix());
}
}
} else {
setAxisParamChecked(this, motorMessageText, errorMessage);
}
// No error has been detected -> Reset the error count
if (poll_status == asynSuccess) {
pC_->getMsgPrintControl().resetCount(keyError, pC_->pasynUser());
}
@@ -689,40 +645,18 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
directly, but need to shrink them a bit. In this case, we're shrinking
them by 0.1 mm or 0.1 degree (depending on the axis type) on both sides.
*/
pl_status = pC_->getDoubleParam(axisNo_, pC_->motorLimitsOffset(),
&limitsOffset);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorLimitsOffset_",
axisNo_, __PRETTY_FUNCTION__,
__LINE__);
}
getAxisParamChecked(this, motorLimitsOffset, &limitsOffset);
highLimit = highLimit - limitsOffset;
lowLimit = lowLimit + limitsOffset;
pl_status = pC_->setDoubleParam(
axisNo_, pC_->motorHighLimitFromDriver(), highLimit);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(
pl_status, "motorHighLimitFromDriver_", axisNo_,
__PRETTY_FUNCTION__, __LINE__);
}
pl_status = pC_->setDoubleParam(axisNo_, pC_->motorLowLimitFromDriver(),
lowLimit);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorLowLimit_",
axisNo_, __PRETTY_FUNCTION__,
__LINE__);
}
setAxisParamChecked(this, motorHighLimitFromDriver, highLimit);
setAxisParamChecked(this, motorLowLimitFromDriver, lowLimit);
}
// Update the enable PV
pl_status = setIntegerParam(pC_->motorEnableRBV(),
readyToBeSwitchedOn() && switchedOn());
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorEnableRBV_", axisNo_,
__PRETTY_FUNCTION__, __LINE__);
}
setAxisParamChecked(this, motorEnableRBV,
readyToBeSwitchedOn() && switchedOn());
if (*moving) {
// If the axis is moving, evaluate the movement direction
@@ -735,32 +669,11 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
// Update the parameter library
if (hasError) {
pl_status = setIntegerParam(pC_->motorStatusProblem(), true);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorStatusProblem_",
axisNo_, __PRETTY_FUNCTION__,
__LINE__);
}
}
pl_status = setIntegerParam(pC_->motorStatusMoving(), *moving);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorStatusMoving_",
axisNo_, __PRETTY_FUNCTION__,
__LINE__);
}
pl_status = setIntegerParam(pC_->motorStatusDone(), !(*moving));
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorStatusDone_", axisNo_,
__PRETTY_FUNCTION__, __LINE__);
}
pl_status = setIntegerParam(pC_->motorStatusDirection(), direction);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorStatusDirection_",
axisNo_, __PRETTY_FUNCTION__,
__LINE__);
setAxisParamChecked(this, motorStatusProblem, true);
}
setAxisParamChecked(this, motorStatusMoving, *moving);
setAxisParamChecked(this, motorStatusDone, !(*moving));
setAxisParamChecked(this, motorStatusDirection, direction);
pl_status = setMotorPosition(currentPosition);
if (pl_status != asynSuccess) {
@@ -775,10 +688,7 @@ asynStatus masterMacsAxis::doMove(double position, int relative,
double acceleration) {
// Status of read-write-operations of ASCII commands to the controller
asynStatus rw_status = asynSuccess;
// Status of parameter library operations
asynStatus pl_status = asynSuccess;
asynStatus status = asynSuccess;
char value[pC_->MAXBUF_];
double motorCoordinatesPosition = 0.0;
@@ -789,19 +699,8 @@ asynStatus masterMacsAxis::doMove(double position, int relative,
// =========================================================================
pl_status = pC_->getIntegerParam(axisNo_, pC_->motorEnableRBV(), &enabled);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "enableMotorRBV_", axisNo_,
__PRETTY_FUNCTION__, __LINE__);
}
pl_status = pC_->getDoubleParam(axisNo_, pC_->motorRecResolution(),
&motorRecResolution);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorRecResolution_",
axisNo_, __PRETTY_FUNCTION__,
__LINE__);
}
getAxisParamChecked(this, motorEnableRBV, &enabled);
getAxisParamChecked(this, motorRecResolution, &motorRecResolution);
if (enabled == 0) {
asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
@@ -821,42 +720,24 @@ asynStatus masterMacsAxis::doMove(double position, int relative,
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, position);
// Check if the speed is allowed to be changed
pl_status = pC_->getIntegerParam(axisNo_, pC_->motorCanSetSpeed(),
&motorCanSetSpeed);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorCanSetSpeed_",
axisNo_, __PRETTY_FUNCTION__,
__LINE__);
}
getAxisParamChecked(this, motorCanSetSpeed, &motorCanSetSpeed);
// Initialize the movement handshake
rw_status = pC_->write(axisNo_, 86, "0");
if (rw_status != asynSuccess) {
pl_status = setIntegerParam(pC_->motorStatusProblem(), true);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorStatusProblem_",
axisNo_, __PRETTY_FUNCTION__,
__LINE__);
}
return rw_status;
status = pC_->write(axisNo_, 86, "0");
if (status != asynSuccess) {
setAxisParamChecked(this, motorStatusProblem, true);
return status;
}
// Set the new motor speed, if the user is allowed to do so and if the
// motor speed changed since the last move command.
if (motorCanSetSpeed != 0 && pMasterMacsA_->lastSetSpeed != motorVelocity) {
pMasterMacsA_->lastSetSpeed = motorVelocity;
if (motorCanSetSpeed != 0) {
snprintf(value, sizeof(value), "%lf", motorVelocity);
rw_status = pC_->write(axisNo_, 05, value);
if (rw_status != asynSuccess) {
pl_status = setIntegerParam(pC_->motorStatusProblem(), true);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status,
"motorStatusProblem_", axisNo_,
__PRETTY_FUNCTION__, __LINE__);
}
return rw_status;
status = pC_->write(axisNo_, 05, value);
if (status != asynSuccess) {
setAxisParamChecked(this, motorStatusProblem, true);
return status;
}
asynPrint(pC_->pasynUser(), ASYN_TRACE_FLOW,
@@ -868,15 +749,10 @@ asynStatus masterMacsAxis::doMove(double position, int relative,
// Set the target position
snprintf(value, sizeof(value), "%lf", motorCoordinatesPosition);
rw_status = pC_->write(axisNo_, 02, value);
if (rw_status != asynSuccess) {
pl_status = setIntegerParam(pC_->motorStatusProblem(), true);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorStatusProblem_",
axisNo_, __PRETTY_FUNCTION__,
__LINE__);
}
return rw_status;
status = pC_->write(axisNo_, 02, value);
if (status != asynSuccess) {
setAxisParamChecked(this, motorStatusProblem, true);
return status;
}
// If the motor has just been enabled, use Enable
@@ -888,18 +764,13 @@ asynStatus masterMacsAxis::doMove(double position, int relative,
// Start the move
if (relative) {
rw_status = pC_->write(axisNo_, 00, "2", timeout);
status = pC_->write(axisNo_, 00, "2", timeout);
} else {
rw_status = pC_->write(axisNo_, 00, "1", timeout);
status = pC_->write(axisNo_, 00, "1", timeout);
}
if (rw_status != asynSuccess) {
pl_status = setIntegerParam(pC_->motorStatusProblem(), true);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorStatusProblem_",
axisNo_, __PRETTY_FUNCTION__,
__LINE__);
}
return rw_status;
if (status != asynSuccess) {
setAxisParamChecked(this, motorStatusProblem, true);
return status;
}
// In the next poll, we will check if the handshake has been performed in a
@@ -913,60 +784,61 @@ asynStatus masterMacsAxis::doMove(double position, int relative,
return asynError;
}
return rw_status;
return status;
}
asynStatus masterMacsAxis::stop(double acceleration) {
// Status of read-write-operations of ASCII commands to the controller
asynStatus rw_status = asynSuccess;
// Status of parameter library operations
asynStatus pl_status = asynSuccess;
// =========================================================================
rw_status = pC_->write(axisNo_, 00, "8");
if (rw_status != asynSuccess) {
pl_status = setIntegerParam(pC_->motorStatusProblem(), true);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorStatusProblem_",
axisNo_, __PRETTY_FUNCTION__,
__LINE__);
}
asynStatus status = pC_->write(axisNo_, 00, "8");
if (status != asynSuccess) {
setAxisParamChecked(this, motorStatusProblem, true);
}
// Reset the driver to idle state and move out of the handshake wait loop,
// if we're currently inside it.
pMasterMacsA_->waitForHandshake = false;
return rw_status;
return status;
}
asynStatus masterMacsAxis::doReset() {
// Status of read-write-operations of ASCII commands to the controller
asynStatus rw_status = asynSuccess;
// Status of parameter library operations
asynStatus pl_status = asynSuccess;
asynStatus status = asynSuccess;
// =========================================================================
rw_status = pC_->write(axisNo_, 17, "");
if (rw_status != asynSuccess) {
pl_status = setIntegerParam(pC_->motorStatusProblem(), true);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorStatusProblem_",
axisNo_, __PRETTY_FUNCTION__,
__LINE__);
}
// Reset the controller ("node reset")
status = pC_->write(axisNo_, 16, "");
if (status != asynSuccess) {
setAxisParamChecked(this, motorStatusProblem, true);
}
// Reset the driver to idle state and move out of the handshake wait loop,
// if we're currently inside it.
// Move out of the handshake wait loop, if we're currently inside it.
pMasterMacsA_->waitForHandshake = false;
return rw_status;
// Disable the axis
return enable(false);
}
asynStatus masterMacsAxis::nodeReset() {
asynStatus status = asynSuccess;
// Reset any errors in the controller. Since the node reset results in a
// power cycle, we use the corresponding timeout.
status = pC_->write(axisNo_, 17, "", PowerCycleTimeout);
if (status != asynSuccess) {
setAxisParamChecked(this, motorStatusProblem, true);
}
// Move out of the handshake wait loop, if we're currently inside it.
pMasterMacsA_->waitForHandshake = false;
// Reinitialize the axis
status = masterMacsAxis::init();
if (status != asynSuccess) {
return status;
}
bool moving = false;
return forcedPoll(&moving);
}
/*
@@ -975,41 +847,25 @@ Home the axis. On absolute encoder systems, this is a no-op
asynStatus masterMacsAxis::doHome(double min_velocity, double max_velocity,
double acceleration, int forwards) {
// Status of read-write-operations of ASCII commands to the controller
asynStatus rw_status = asynSuccess;
// Status of parameter library operations
asynStatus pl_status = asynSuccess;
char response[pC_->MAXBUF_] = {0};
// =========================================================================
pl_status = pC_->getStringParam(axisNo_, pC_->encoderType(),
sizeof(response), response);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "encoderType_", axisNo_,
__PRETTY_FUNCTION__, __LINE__);
}
getAxisParamChecked(this, encoderType, &response);
// Only send the home command if the axis has an incremental encoder
if (strcmp(response, IncrementalEncoder) == 0) {
// Initialize the movement handshake
rw_status = pC_->write(axisNo_, 86, "0");
if (rw_status != asynSuccess) {
pl_status = setIntegerParam(pC_->motorStatusProblem(), true);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status,
"motorStatusProblem_", axisNo_,
__PRETTY_FUNCTION__, __LINE__);
}
return rw_status;
asynStatus status = pC_->write(axisNo_, 86, "0");
if (status != asynSuccess) {
setAxisParamChecked(this, motorStatusProblem, true);
return status;
}
rw_status = pC_->write(axisNo_, 00, "9");
if (rw_status != asynSuccess) {
return rw_status;
status = pC_->write(axisNo_, 00, "9");
if (status != asynSuccess) {
return status;
}
// In the next poll, we will check if the handshake has been performed
@@ -1027,12 +883,6 @@ Read the encoder type and update the parameter library accordingly
*/
asynStatus masterMacsAxis::readEncoderType() {
// Status of read-write-operations of ASCII commands to the controller
asynStatus rw_status = asynSuccess;
// Status of parameter library operations
asynStatus pl_status = asynSuccess;
char command[pC_->MAXBUF_] = {0};
char response[pC_->MAXBUF_] = {0};
int nvals = 0;
@@ -1041,9 +891,9 @@ asynStatus masterMacsAxis::readEncoderType() {
// =========================================================================
// Check if this is an absolute encoder
rw_status = pC_->read(axisNo_, 60, response);
if (rw_status != asynSuccess) {
return rw_status;
asynStatus status = pC_->read(axisNo_, 60, response);
if (status != asynSuccess) {
return status;
}
nvals = sscanf(response, "%d", &encoder_id);
@@ -1059,28 +909,18 @@ asynStatus masterMacsAxis::readEncoderType() {
2=SSI (Absolute encoder with BiSS interface)
*/
if (encoder_id == 0) {
pl_status = setStringParam(pC_->encoderType(), IncrementalEncoder);
setAxisParamChecked(this, encoderType, IncrementalEncoder);
} else {
pl_status = setStringParam(pC_->encoderType(), AbsoluteEncoder);
setAxisParamChecked(this, encoderType, AbsoluteEncoder);
}
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "encoderType_", axisNo_,
__PRETTY_FUNCTION__, __LINE__);
}
return asynSuccess;
}
asynStatus masterMacsAxis::enable(bool on) {
int timeout_enable_disable = 2;
char value[pC_->MAXBUF_];
// Status of read-write-operations of ASCII commands to the controller
asynStatus rw_status = asynSuccess;
// Status of parameter library operations
asynStatus pl_status = asynSuccess;
char msg[pC_->MAXBUF_];
// =========================================================================
@@ -1110,14 +950,8 @@ asynStatus masterMacsAxis::enable(bool on) {
"idle and can therefore not be disabled.\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__);
pl_status =
setStringParam(pC_->motorMessageText(),
"Axis cannot be disabled while it is moving.");
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorMessageText_",
axisNo_, __PRETTY_FUNCTION__,
__LINE__);
}
setAxisParamChecked(this, motorMessageText,
"Axis cannot be disabled while it is moving.");
return asynError;
}
@@ -1135,7 +969,7 @@ asynStatus masterMacsAxis::enable(bool on) {
}
// Enable / disable the axis if it is not moving
snprintf(value, sizeof(value), "%d", on);
snprintf(msg, sizeof(msg), "%d", on);
asynPrint(pC_->pasynUser(), ASYN_TRACE_FLOW,
"Controller \"%s\", axis %d => %s, line %d:\n%s axis.\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
@@ -1150,9 +984,9 @@ asynStatus masterMacsAxis::enable(bool on) {
timeout = PowerCycleTimeout;
}
rw_status = pC_->write(axisNo_, 04, value, timeout);
if (rw_status != asynSuccess) {
return rw_status;
asynStatus status = pC_->write(axisNo_, 04, msg, timeout);
if (status != asynSuccess) {
return status;
}
// Query the axis status every few milliseconds until the axis has been
@@ -1162,15 +996,15 @@ asynStatus masterMacsAxis::enable(bool on) {
// Read the axis status
usleep(100000);
rw_status = readAxisStatus();
if (rw_status != asynSuccess) {
return rw_status;
status = readAxisStatus();
if (status != asynSuccess) {
return status;
}
if (switchedOn() == on) {
bool moving = false;
// Perform a poll to update the parameter library
poll(&moving);
forcedPoll(&moving);
return asynSuccess;
}
}
@@ -1184,14 +1018,10 @@ asynStatus masterMacsAxis::enable(bool on) {
on ? "enable" : "disable", timeout_enable_disable);
// Output message to user
snprintf(value, sizeof(value), "Failed to %s within %d seconds",
snprintf(msg, sizeof(msg), "Failed to %s within %d seconds",
on ? "enable" : "disable", timeout_enable_disable);
pl_status = setStringParam(pC_->motorMessageText(), value);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorMessageText_",
axisNo_, __PRETTY_FUNCTION__,
__LINE__);
}
setAxisParamChecked(this, motorMessageText, msg);
return asynError;
}

View File

@@ -1,16 +1,12 @@
#ifndef masterMacsAXIS_H
#define masterMacsAXIS_H
#include "masterMacsController.h"
#include "sinqAxis.h"
#include <memory>
// Forward declaration of the controller class to resolve the cyclic dependency
// between the controller and the axis .h-file. See
// https://en.cppreference.com/w/cpp/language/class.
class masterMacsController;
struct HIDDEN masterMacsAxisImpl;
struct masterMacsAxisImpl;
class masterMacsAxis : public sinqAxis {
class HIDDEN masterMacsAxis : public sinqAxis {
public:
/**
* @brief Construct a new masterMacsAxis
@@ -79,12 +75,27 @@ class masterMacsAxis : public sinqAxis {
*/
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
*
* The following steps are performed:
* - Read out the motor status, motor position, velocity and acceleration
* from the MCU and store this information in the parameter library.
* - Read out the motor status, motor position, velocity and
* acceleration from the MCU and store this information in the parameter
* library.
* - Set the enable PV accordint to the initial status of the axis.
*
* @return asynStatus
@@ -100,8 +111,8 @@ class masterMacsAxis : public sinqAxis {
asynStatus enable(bool on);
/**
* @brief Read the encoder type (incremental or absolute) for this axis from
* the MCU and store the information in the PV ENCODER_TYPE.
* @brief Read the encoder type (incremental or absolute) for this axis
* from the MCU and store the information in the PV ENCODER_TYPE.
*
* @return asynStatus
*/
@@ -116,17 +127,21 @@ class masterMacsAxis : public sinqAxis {
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
*/
void setNeedInit(bool needInit);
asynStatus readConfig();
/**
* @brief Return a pointer to the axis controller
*/
virtual masterMacsController *pController() override { return pC_; };
/**
* @brief Read the Master MACS status with the xR10 command and store the
* result in axisStatus_
* @brief Read the Master MACS status with the xR10 command and store
* the result in axisStatus_
*
*/
asynStatus readAxisStatus();
@@ -204,8 +219,8 @@ class masterMacsAxis : public sinqAxis {
bool powerEnabled();
/**
* @brief Read the Master MACS status with the xR10 command and store the
* result in axisStatus_
* @brief Read the Master MACS status with the xR10 command and store
* the result in axisStatus_
*
*/
asynStatus readAxisError();

View File

@@ -14,6 +14,9 @@
struct masterMacsControllerImpl {
double comTimeout;
// Indices of additional ParamLib entries
int nodeReset;
};
/**
@@ -68,6 +71,20 @@ masterMacsController::masterMacsController(const char *portName,
.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);
}
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,
double comTimeout) {
return writeRead(axisNo, tcpCmd, NULL, response);
@@ -136,7 +169,6 @@ asynStatus masterMacsController::writeRead(int axisNo, int tcpCmd,
// Definition of local variables.
asynStatus status = asynSuccess;
asynStatus pl_status = asynSuccess;
char fullCommand[MAXBUF_] = {0};
char fullResponse[MAXBUF_] = {0};
char drvMessageText[MAXBUF_] = {0};
@@ -255,7 +287,7 @@ asynStatus masterMacsController::writeRead(int axisNo, int tcpCmd,
// Log the overall status (communication successfull or not)
if (status == asynSuccess) {
pl_status = axis->setIntegerParam(this->motorStatusCommsError_, 0);
setAxisParamChecked(axis, motorStatusCommsError, false);
} else {
/*
@@ -274,35 +306,12 @@ asynStatus masterMacsController::writeRead(int axisNo, int tcpCmd,
the user with different error messages if more than one error
ocurred before an error-free communication
*/
pl_status =
getIntegerParam(axisNo, motorStatusProblem_, &motorStatusProblem);
if (pl_status != asynSuccess) {
return paramLibAccessFailed(pl_status, "motorStatusProblem", axisNo,
__PRETTY_FUNCTION__, __LINE__);
}
getAxisParamChecked(axis, motorStatusProblem, &motorStatusProblem);
if (motorStatusProblem == 0) {
pl_status =
axis->setStringParam(motorMessageText(), drvMessageText);
if (pl_status != asynSuccess) {
return paramLibAccessFailed(pl_status, "motorMessageText",
axisNo, __PRETTY_FUNCTION__,
__LINE__);
}
pl_status = axis->setIntegerParam(motorStatusProblem_, 1);
if (pl_status != asynSuccess) {
return paramLibAccessFailed(pl_status, "motorStatusProblem",
axisNo, __PRETTY_FUNCTION__,
__LINE__);
}
pl_status = axis->setIntegerParam(motorStatusProblem_, 1);
if (pl_status != asynSuccess) {
return paramLibAccessFailed(pl_status, "motorStatusCommsError_",
axisNo, __PRETTY_FUNCTION__,
__LINE__);
}
setAxisParamChecked(axis, motorMessageText, drvMessageText);
setAxisParamChecked(axis, motorStatusProblem, true);
setAxisParamChecked(axis, motorStatusCommsError, false);
}
}
@@ -332,13 +341,14 @@ asynStatus masterMacsController::parseResponse(
msgPrintControlKey parseKey =
msgPrintControlKey(portName, axisNo, __PRETTY_FUNCTION__, __LINE__);
// Was the motor previously connected?
status = getIntegerParam(axisNo, motorConnected(), &prevConnected);
if (status != asynSuccess) {
return paramLibAccessFailed(status, "motorConnected", axisNo,
__PRETTY_FUNCTION__, __LINE__);
masterMacsAxis *axis = getMasterMacsAxis(axisNo);
if (axis == nullptr) {
return asynError;
}
// Was the motor previously connected?
getAxisParamChecked(axis, motorConnected, &prevConnected);
// We don't use strlen here since the C string terminator 0x00
// occurs in the middle of the char array.
for (uint32_t i = 0; i < MAXBUF_; i++) {
@@ -361,16 +371,7 @@ asynStatus masterMacsController::parseResponse(
"connected.\n",
portName, axisNo, __PRETTY_FUNCTION__, __LINE__);
masterMacsAxis *axis = getMasterMacsAxis(axisNo);
if (axis == nullptr) {
return asynError;
}
status = axis->setIntegerParam(motorConnected(), 1);
if (status != asynSuccess) {
return paramLibAccessFailed(status, "motorConnected",
axisNo, __PRETTY_FUNCTION__,
__LINE__);
}
setAxisParamChecked(axis, motorConnected, true);
status = callParamCallbacks();
if (status != asynSuccess) {
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
@@ -398,16 +399,7 @@ asynStatus masterMacsController::parseResponse(
"disconnected.\n",
portName, axisNo, __PRETTY_FUNCTION__, __LINE__);
masterMacsAxis *axis = getMasterMacsAxis(axisNo);
if (axis == nullptr) {
return asynError;
}
status = axis->setIntegerParam(motorConnected(), 0);
if (status != asynSuccess) {
return paramLibAccessFailed(status, "motorConnected",
axisNo, __PRETTY_FUNCTION__,
__LINE__);
}
setAxisParamChecked(axis, motorConnected, false);
status = callParamCallbacks();
if (status != asynSuccess) {
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
@@ -500,6 +492,8 @@ asynStatus masterMacsController::readInt32(asynUser *pasynUser,
double masterMacsController::comTimeout() { return pMasterMacsC_->comTimeout; }
int masterMacsController::nodeReset() { return pMasterMacsC_->nodeReset; }
/***************************************************************************/
/** The following functions are C-wrappers, and can be called directly from
* iocsh */

View File

@@ -8,14 +8,18 @@
#ifndef masterMacsController_H
#define masterMacsController_H
#include "masterMacsAxis.h"
#include "sinqAxis.h"
#include "sinqController.h"
#include <memory>
struct masterMacsControllerImpl;
// Forward declaration of the controller class to resolve the cyclic dependency
// between the controller and the axis .h-file. See
// https://en.cppreference.com/w/cpp/language/class.
class HIDDEN masterMacsAxis;
class masterMacsController : public sinqController {
struct HIDDEN masterMacsControllerImpl;
class HIDDEN masterMacsController : public sinqController {
public:
/**
@@ -61,6 +65,17 @@ class masterMacsController : public sinqController {
*/
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)
*
@@ -137,6 +152,9 @@ class masterMacsController : public sinqController {
*/
double comTimeout();
// Accessors for additional PVs
int nodeReset();
private:
std::unique_ptr<masterMacsControllerImpl> pMasterMacsC_;
};