Compare commits
17 Commits
0.2.0
...
static-dep
Author | SHA1 | Date | |
---|---|---|---|
9cc9b1d144 | |||
2e42cbc6da | |||
c5f5cf3065 | |||
468ca46010 | |||
cd57409f3c | |||
21ffcba8be | |||
3bfc2414b6 | |||
60053244a1 | |||
699b588ba5 | |||
e86c517fc7 | |||
f733718ee7 | |||
a8c3499dc5 | |||
fe52245e38 | |||
a3e849f386 | |||
16564011a6 | |||
631ee46a50 | |||
cf9899062a |
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
[submodule "sinqMotor"]
|
||||
path = sinqMotor
|
||||
url = https://gitea.psi.ch/lin-epics-modules/sinqMotor
|
11
Makefile
11
Makefile
@ -12,18 +12,23 @@ REQUIRED+=sinqMotor
|
||||
# Specify the version of asynMotor we want to build against
|
||||
motorBase_VERSION=7.2.2
|
||||
|
||||
# Specify the version of sinqMotor we want to build against
|
||||
sinqMotor_VERSION=0.8.0
|
||||
|
||||
# These headers allow to depend on this library for derived drivers.
|
||||
HEADERS += src/masterMacsAxis.h
|
||||
HEADERS += src/masterMacsController.h
|
||||
|
||||
# Source files to build
|
||||
SOURCES += sinqMotor/src/msgPrintControl.cpp
|
||||
SOURCES += sinqMotor/src/sinqAxis.cpp
|
||||
SOURCES += sinqMotor/src/sinqController.cpp
|
||||
SOURCES += src/masterMacsAxis.cpp
|
||||
SOURCES += src/masterMacsController.cpp
|
||||
|
||||
# Store the record files
|
||||
TEMPLATES += sinqMotor/db/asynRecord.db
|
||||
TEMPLATES += sinqMotor/db/sinqMotor.db
|
||||
|
||||
# This file registers the motor-specific functions in the IOC shell.
|
||||
DBDS += sinqMotor/src/sinqMotor.dbd
|
||||
DBDS += src/masterMacs.dbd
|
||||
|
||||
USR_CFLAGS += -Wall -Wextra -Weffc++ -Wunused-result -Wpedantic -Wextra -Werror
|
||||
|
14
README.md
14
README.md
@ -1,15 +1,21 @@
|
||||
# masterMacs
|
||||
|
||||
## <span style="color:red">Please read the documentation of sinqMotor first: https://git.psi.ch/sinq-epics-modules/sinqmotor</span>
|
||||
|
||||
## Overview
|
||||
|
||||
This is a driver for the masterMacs motion controller with the SINQ communication protocol. It is based on the sinqMotor shared library (https://git.psi.ch/sinq-epics-modules/sinqmotor). The header files contain detailed documentation for all public functions. The headers themselves are exported when building the library to allow other drivers to depend on this one.
|
||||
|
||||
Compatible to MasterMACS software 2.0.0.
|
||||
|
||||
## User guide
|
||||
|
||||
This driver is a standard sinqMotor-derived driver and does not need any specific configuration. For the general configuration, please see https://git.psi.ch/sinq-epics-modules/sinqmotor/-/blob/main/README.md.
|
||||
|
||||
The folder "utils" contains utility scripts for working with masterMacs motor controllers:
|
||||
- decodeStatus.py: Take the return message of a R10 (read status) command and print it in human-readable form.
|
||||
- decodeError.py: Take the return message of a R11 (read error) command and print it in human-readable form.
|
||||
- writeRead.py: Send messages to the controller and receive answers.
|
||||
|
||||
## Developer guide
|
||||
|
||||
@ -49,11 +55,9 @@ 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
|
||||
@ -62,4 +66,4 @@ Please see the documentation for the module sinqMotor: https://git.psi.ch/sinq-e
|
||||
|
||||
### How to build it
|
||||
|
||||
Please see the documentation for the module sinqMotor: https://git.psi.ch/sinq-epics-modules/sinqmotor/-/blob/main/README.md.
|
||||
This driver can be compiled and installed by running `make install` from the same directory where the Makefile is located. However, since it uses the git submodule sinqMotor, make sure that the correct version of the submodule repository is checked out AND the change is commited (`git status` shows no non-committed changes). Please see the section "Usage as static dependency" in https://git.psi.ch/sinq-epics-modules/sinqmotor/-/blob/main/README.md for more details.
|
||||
|
1
sinqMotor
Submodule
1
sinqMotor
Submodule
Submodule sinqMotor added at 5689402375
@ -11,6 +11,14 @@
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/*
|
||||
A special communication timeout is used in the following two cases:
|
||||
1) Enable command
|
||||
2) First move command after enabling the motor
|
||||
This is due to MasterMACS running a powerup cycle, which can delay the answer.
|
||||
*/
|
||||
#define PowerCycleTimeout 10.0 // Value in seconds
|
||||
|
||||
/*
|
||||
Contains all instances of turboPmacAxis which have been created and is used in
|
||||
the initialization hook function.
|
||||
@ -52,9 +60,9 @@ void appendErrorMessage(char *fullMessage, size_t capacityFullMessage,
|
||||
// fullMessage suffices. We need capacity for one additional character
|
||||
// because of the linebreak.
|
||||
if (lenFullMessage + lenToBeAppended + 1 < capacityFullMessage) {
|
||||
// Append the linebreak and readd the null terminator behind it
|
||||
// fullMessage[lenFullMessage] = '\n';
|
||||
// fullMessage[lenFullMessage + 1] = '\0';
|
||||
// Append the linebreak and set the null terminator behind it
|
||||
fullMessage[lenFullMessage] = '\n';
|
||||
fullMessage[lenFullMessage + 1] = '\0';
|
||||
|
||||
// We check before that the capacity of fullMessage is sufficient
|
||||
strcat(fullMessage, toBeAppended);
|
||||
@ -78,13 +86,13 @@ masterMacsAxis::masterMacsAxis(masterMacsController *pC, int axisNo)
|
||||
bounds, asynMotorAxis prints an error. However, we want the IOC creation to
|
||||
stop completely, since this is a configuration error.
|
||||
*/
|
||||
if (axisNo >= pC->numAxes_) {
|
||||
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
|
||||
if (axisNo >= pC->numAxes()) {
|
||||
asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
|
||||
"Controller \"%s\", axis %d => %s, line %d:: FATAL ERROR: "
|
||||
"Axis index %d must be smaller than the total number of axes "
|
||||
"%d. Call the support.",
|
||||
pC_->portName, axisNo, __PRETTY_FUNCTION__, __LINE__, axisNo_,
|
||||
pC->numAxes_);
|
||||
pC->numAxes());
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
@ -106,12 +114,13 @@ masterMacsAxis::masterMacsAxis(masterMacsController *pC, int axisNo)
|
||||
// Flag for handshake waiting
|
||||
waitForHandshake_ = false;
|
||||
timeAtHandshake_ = 0;
|
||||
targetReachedUninitialized_ = true;
|
||||
|
||||
// masterMacs motors can always be disabled
|
||||
status = pC_->setIntegerParam(axisNo_, pC_->motorCanDisable_, 1);
|
||||
status = pC_->setIntegerParam(axisNo_, pC_->motorCanDisable(), 1);
|
||||
if (status != asynSuccess) {
|
||||
asynPrint(
|
||||
pC_->pasynUserSelf, ASYN_TRACE_ERROR,
|
||||
pC_->pasynUser(), ASYN_TRACE_ERROR,
|
||||
"Controller \"%s\", axis %d => %s, line %d:\nFATAL ERROR "
|
||||
"(setting a parameter value failed with %s)\n. Terminating IOC",
|
||||
pC_->portName, axisNo, __PRETTY_FUNCTION__, __LINE__,
|
||||
@ -120,10 +129,10 @@ masterMacsAxis::masterMacsAxis(masterMacsController *pC, int axisNo)
|
||||
}
|
||||
|
||||
// Assume that the motor is initially not moving
|
||||
status = pC_->setIntegerParam(axisNo_, pC_->motorStatusMoving_, false);
|
||||
status = pC_->setIntegerParam(axisNo_, pC_->motorStatusMoving(), false);
|
||||
if (status != asynSuccess) {
|
||||
asynPrint(
|
||||
pC_->pasynUserSelf, ASYN_TRACE_ERROR,
|
||||
pC_->pasynUser(), ASYN_TRACE_ERROR,
|
||||
"Controller \"%s\", axis %d => %s, line %d:\nFATAL ERROR "
|
||||
"(setting a parameter value failed with %s)\n. Terminating IOC",
|
||||
pC_->portName, axisNo, __PRETTY_FUNCTION__, __LINE__,
|
||||
@ -148,7 +157,7 @@ asynStatus masterMacsAxis::init() {
|
||||
|
||||
// Local variable declaration
|
||||
asynStatus pl_status = asynSuccess;
|
||||
char response[pC_->MAXBUF_];
|
||||
char response[pC_->MAXBUF_] = {0};
|
||||
int nvals = 0;
|
||||
double motorRecResolution = 0.0;
|
||||
double motorPosition = 0.0;
|
||||
@ -163,11 +172,11 @@ asynStatus masterMacsAxis::init() {
|
||||
time_t now = time(NULL);
|
||||
time_t maxInitTime = 60;
|
||||
while (1) {
|
||||
pl_status = pC_->getDoubleParam(axisNo_, pC_->motorRecResolution_,
|
||||
pl_status = pC_->getDoubleParam(axisNo_, pC_->motorRecResolution(),
|
||||
&motorRecResolution);
|
||||
if (pl_status == asynParamUndefined) {
|
||||
if (now + maxInitTime < time(NULL)) {
|
||||
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
|
||||
asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
|
||||
"Controller \"%s\", axis %d => %s, line "
|
||||
"%d\nInitializing the parameter library failed.\n",
|
||||
pC_->portName, axisNo_, __PRETTY_FUNCTION__,
|
||||
@ -190,8 +199,8 @@ asynStatus masterMacsAxis::init() {
|
||||
}
|
||||
nvals = sscanf(response, "%lf", &motorPosition);
|
||||
if (nvals != 1) {
|
||||
return pC_->errMsgCouldNotParseResponse("R12", response, axisNo_,
|
||||
__PRETTY_FUNCTION__, __LINE__);
|
||||
return pC_->couldNotParseResponse("R12", response, axisNo_,
|
||||
__PRETTY_FUNCTION__, __LINE__);
|
||||
}
|
||||
|
||||
// Read out the current velocity
|
||||
@ -201,8 +210,8 @@ asynStatus masterMacsAxis::init() {
|
||||
}
|
||||
nvals = sscanf(response, "%lf", &motorVelocity);
|
||||
if (nvals != 1) {
|
||||
return pC_->errMsgCouldNotParseResponse("R05", response, axisNo_,
|
||||
__PRETTY_FUNCTION__, __LINE__);
|
||||
return pC_->couldNotParseResponse("R05", response, axisNo_,
|
||||
__PRETTY_FUNCTION__, __LINE__);
|
||||
}
|
||||
|
||||
// Read out the maximum velocity
|
||||
@ -212,8 +221,8 @@ asynStatus masterMacsAxis::init() {
|
||||
}
|
||||
nvals = sscanf(response, "%lf", &motorVmax);
|
||||
if (nvals != 1) {
|
||||
return pC_->errMsgCouldNotParseResponse("R26", response, axisNo_,
|
||||
__PRETTY_FUNCTION__, __LINE__);
|
||||
return pC_->couldNotParseResponse("R26", response, axisNo_,
|
||||
__PRETTY_FUNCTION__, __LINE__);
|
||||
}
|
||||
|
||||
// Read out the acceleration
|
||||
@ -223,8 +232,8 @@ asynStatus masterMacsAxis::init() {
|
||||
}
|
||||
nvals = sscanf(response, "%lf", &motorAccel);
|
||||
if (nvals != 1) {
|
||||
return pC_->errMsgCouldNotParseResponse("R06", response, axisNo_,
|
||||
__PRETTY_FUNCTION__, __LINE__);
|
||||
return pC_->couldNotParseResponse("R06", response, axisNo_,
|
||||
__PRETTY_FUNCTION__, __LINE__);
|
||||
}
|
||||
|
||||
// Cache the motor speed. If this value differs from the one in the motor
|
||||
@ -232,15 +241,10 @@ asynStatus masterMacsAxis::init() {
|
||||
// MasterMACS.
|
||||
lastSetSpeed_ = motorVelocity;
|
||||
|
||||
// Transform from motor to parameter library coordinates
|
||||
motorPosition = motorPosition / motorRecResolution;
|
||||
|
||||
// Store these values in the parameter library
|
||||
pl_status =
|
||||
pC_->setDoubleParam(axisNo_, pC_->motorPosition_, motorPosition);
|
||||
// Store the motor position in the parameter library
|
||||
pl_status = setMotorPosition(motorPosition);
|
||||
if (pl_status != asynSuccess) {
|
||||
return pC_->paramLibAccessFailed(pl_status, "motorPosition_", axisNo_,
|
||||
__PRETTY_FUNCTION__, __LINE__);
|
||||
return pl_status;
|
||||
}
|
||||
|
||||
// Write to the motor record fields
|
||||
@ -264,7 +268,7 @@ asynStatus masterMacsAxis::init() {
|
||||
// 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
|
||||
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
|
||||
asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
|
||||
"Controller \"%s\", axis %d => %s, line "
|
||||
"%d:\ncallParamCallbacks failed with %s.\n",
|
||||
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
|
||||
@ -286,7 +290,7 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
|
||||
// Status of parameter library operations
|
||||
asynStatus pl_status = asynSuccess;
|
||||
|
||||
char response[pC_->MAXBUF_];
|
||||
char response[pC_->MAXBUF_] = {0};
|
||||
int nvals = 0;
|
||||
|
||||
int direction = 0;
|
||||
@ -303,6 +307,34 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
|
||||
|
||||
// Are we currently waiting for a handshake?
|
||||
if (waitForHandshake_) {
|
||||
|
||||
// Check if the handshake takes too long and communicate an error in
|
||||
// this case. A handshake should not take more than 5 seconds.
|
||||
time_t currentTime = time(NULL);
|
||||
bool timedOut = (currentTime > timeAtHandshake_ + 5);
|
||||
|
||||
if (pC_->getMsgPrintControl().shouldBePrinted(
|
||||
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, timedOut,
|
||||
pC_->pasynUser())) {
|
||||
asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
|
||||
"Controller \"%s\", axis %d => %s, line %d\nAsked for a "
|
||||
"handshake at %ld s and didn't get a positive reply yet "
|
||||
"(current time is %ld s).\n",
|
||||
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
|
||||
timeAtHandshake_, currentTime);
|
||||
}
|
||||
|
||||
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__);
|
||||
}
|
||||
}
|
||||
|
||||
pC_->read(axisNo_, 86, response);
|
||||
if (rw_status != asynSuccess) {
|
||||
return rw_status;
|
||||
@ -310,27 +342,28 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
|
||||
|
||||
nvals = sscanf(response, "%lf", &handshakePerformed);
|
||||
if (nvals != 1) {
|
||||
return pC_->errMsgCouldNotParseResponse(
|
||||
"R86", response, axisNo_, __PRETTY_FUNCTION__, __LINE__);
|
||||
return pC_->couldNotParseResponse("R86", response, axisNo_,
|
||||
__PRETTY_FUNCTION__, __LINE__);
|
||||
}
|
||||
|
||||
if (handshakePerformed == 1.0) {
|
||||
// Handshake has been performed successfully -> Continue with the
|
||||
// poll
|
||||
waitForHandshake_ = false;
|
||||
targetReachedUninitialized_ = false;
|
||||
} else {
|
||||
// Still waiting for the handshake - try again in the next busy
|
||||
// poll. This is already part of the movement procedure.
|
||||
*moving = true;
|
||||
|
||||
pl_status = setIntegerParam(pC_->motorStatusMoving_, *moving);
|
||||
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));
|
||||
pl_status = setIntegerParam(pC_->motorStatusDone(), !(*moving));
|
||||
if (pl_status != asynSuccess) {
|
||||
return pC_->paramLibAccessFailed(pl_status, "motorStatusDone_",
|
||||
axisNo_, __PRETTY_FUNCTION__,
|
||||
@ -342,7 +375,7 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
|
||||
}
|
||||
|
||||
// Motor resolution from parameter library
|
||||
pl_status = pC_->getDoubleParam(axisNo_, pC_->motorRecResolution_,
|
||||
pl_status = pC_->getDoubleParam(axisNo_, pC_->motorRecResolution(),
|
||||
&motorRecResolution);
|
||||
if (pl_status != asynSuccess) {
|
||||
return pC_->paramLibAccessFailed(pl_status, "motorRecResolution_",
|
||||
@ -351,29 +384,29 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
|
||||
}
|
||||
|
||||
// Read the previous motor position
|
||||
pl_status =
|
||||
pC_->getDoubleParam(axisNo_, pC_->motorPosition_, &previousPosition);
|
||||
pl_status = motorPosition(&previousPosition);
|
||||
if (pl_status != asynSuccess) {
|
||||
return pC_->paramLibAccessFailed(pl_status, "motorPosition_", axisNo_,
|
||||
__PRETTY_FUNCTION__, __LINE__);
|
||||
return pl_status;
|
||||
}
|
||||
|
||||
// Transform from EPICS to motor coordinates (see comment in
|
||||
// masterMacsAxis::readConfig())
|
||||
previousPosition = previousPosition * motorRecResolution;
|
||||
|
||||
// Update the axis status
|
||||
rw_status = readAxisStatus();
|
||||
if (rw_status != asynSuccess) {
|
||||
return rw_status;
|
||||
}
|
||||
|
||||
// If the motor is switched off or if it reached its target, it is not
|
||||
// moving.
|
||||
if (targetReached() || !switchedOn()) {
|
||||
if (targetReachedUninitialized_) {
|
||||
*moving = false;
|
||||
} else {
|
||||
*moving = true;
|
||||
if (targetReached() || !switchedOn()) {
|
||||
*moving = false;
|
||||
} else {
|
||||
*moving = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (targetReached()) {
|
||||
targetReachedUninitialized_ = false;
|
||||
}
|
||||
|
||||
// Read the current position
|
||||
@ -383,8 +416,8 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
|
||||
}
|
||||
nvals = sscanf(response, "%lf", ¤tPosition);
|
||||
if (nvals != 1) {
|
||||
return pC_->errMsgCouldNotParseResponse("R12", response, axisNo_,
|
||||
__PRETTY_FUNCTION__, __LINE__);
|
||||
return pC_->couldNotParseResponse("R12", response, axisNo_,
|
||||
__PRETTY_FUNCTION__, __LINE__);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -403,17 +436,17 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
|
||||
since this information is not reliable.
|
||||
*/
|
||||
if (communicationError()) {
|
||||
if (pC_->msgPrintControl_.shouldBePrinted(keyError, true,
|
||||
pC_->pasynUserSelf)) {
|
||||
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
|
||||
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_->msgPrintControl_.getSuffix());
|
||||
pC_->getMsgPrintControl().getSuffix());
|
||||
}
|
||||
|
||||
pl_status =
|
||||
setStringParam(pC_->motorMessageText_,
|
||||
setStringParam(pC_->motorMessageText(),
|
||||
"Communication error between PC and motor "
|
||||
"controller. Please call the support.");
|
||||
if (pl_status != asynSuccess) {
|
||||
@ -561,18 +594,18 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
|
||||
}
|
||||
|
||||
if (strlen(shellMessage) > 0) {
|
||||
if (pC_->msgPrintControl_.shouldBePrinted(keyError, true,
|
||||
pC_->pasynUserSelf)) {
|
||||
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
|
||||
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",
|
||||
"%d\n%s%s\n",
|
||||
pC_->portName, axisNo_, __PRETTY_FUNCTION__,
|
||||
__LINE__, shellMessage,
|
||||
pC_->msgPrintControl_.getSuffix());
|
||||
pC_->getMsgPrintControl().getSuffix());
|
||||
}
|
||||
}
|
||||
|
||||
pl_status = setStringParam(pC_->motorMessageText_, userMessage);
|
||||
pl_status = setStringParam(pC_->motorMessageText(), userMessage);
|
||||
if (pl_status != asynSuccess) {
|
||||
return pC_->paramLibAccessFailed(pl_status, "motorMessageText_",
|
||||
axisNo_, __PRETTY_FUNCTION__,
|
||||
@ -580,7 +613,7 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
pC_->msgPrintControl_.resetCount(keyError);
|
||||
pC_->getMsgPrintControl().resetCount(keyError, pC_->pasynUser());
|
||||
}
|
||||
|
||||
// Read out the limits, if the motor is not moving
|
||||
@ -591,8 +624,8 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
|
||||
}
|
||||
nvals = sscanf(response, "%lf", &lowLimit);
|
||||
if (nvals != 1) {
|
||||
return pC_->errMsgCouldNotParseResponse(
|
||||
"R34", response, axisNo_, __PRETTY_FUNCTION__, __LINE__);
|
||||
return pC_->couldNotParseResponse("R34", response, axisNo_,
|
||||
__PRETTY_FUNCTION__, __LINE__);
|
||||
}
|
||||
|
||||
rw_status = pC_->read(axisNo_, 33, response);
|
||||
@ -601,8 +634,8 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
|
||||
}
|
||||
nvals = sscanf(response, "%lf", &highLimit);
|
||||
if (nvals != 1) {
|
||||
return pC_->errMsgCouldNotParseResponse(
|
||||
"R33", response, axisNo_, __PRETTY_FUNCTION__, __LINE__);
|
||||
return pC_->couldNotParseResponse("R33", response, axisNo_,
|
||||
__PRETTY_FUNCTION__, __LINE__);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -618,7 +651,7 @@ 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_,
|
||||
pl_status = pC_->getDoubleParam(axisNo_, pC_->motorLimitsOffset(),
|
||||
&limitsOffset);
|
||||
if (pl_status != asynSuccess) {
|
||||
return pC_->paramLibAccessFailed(pl_status, "motorLimitsOffset_",
|
||||
@ -628,15 +661,15 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
|
||||
highLimit = highLimit - limitsOffset;
|
||||
lowLimit = lowLimit + limitsOffset;
|
||||
|
||||
pl_status = pC_->setDoubleParam(axisNo_, pC_->motorHighLimitFromDriver_,
|
||||
highLimit);
|
||||
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_,
|
||||
pl_status = pC_->setDoubleParam(axisNo_, pC_->motorLowLimitFromDriver(),
|
||||
lowLimit);
|
||||
if (pl_status != asynSuccess) {
|
||||
return pC_->paramLibAccessFailed(pl_status, "motorLowLimit_",
|
||||
@ -646,7 +679,7 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
|
||||
}
|
||||
|
||||
// Update the enable PV
|
||||
pl_status = setIntegerParam(pC_->motorEnableRBV_,
|
||||
pl_status = setIntegerParam(pC_->motorEnableRBV(),
|
||||
readyToBeSwitchedOn() && switchedOn());
|
||||
if (pl_status != asynSuccess) {
|
||||
return pC_->paramLibAccessFailed(pl_status, "motorEnableRBV_", axisNo_,
|
||||
@ -664,41 +697,36 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
|
||||
|
||||
// Update the parameter library
|
||||
if (hasError) {
|
||||
pl_status = setIntegerParam(pC_->motorStatusProblem_, true);
|
||||
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);
|
||||
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));
|
||||
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);
|
||||
pl_status = setIntegerParam(pC_->motorStatusDirection(), direction);
|
||||
if (pl_status != asynSuccess) {
|
||||
return pC_->paramLibAccessFailed(pl_status, "motorStatusDirection_",
|
||||
axisNo_, __PRETTY_FUNCTION__,
|
||||
__LINE__);
|
||||
}
|
||||
|
||||
// Transform from motor to EPICS coordinates (see comment in
|
||||
// masterMacsAxis::init())
|
||||
currentPosition = currentPosition / motorRecResolution;
|
||||
|
||||
pl_status = setDoubleParam(pC_->motorPosition_, currentPosition);
|
||||
pl_status = setMotorPosition(currentPosition);
|
||||
if (pl_status != asynSuccess) {
|
||||
return pC_->paramLibAccessFailed(pl_status, "motorPosition_", axisNo_,
|
||||
__PRETTY_FUNCTION__, __LINE__);
|
||||
return pl_status;
|
||||
}
|
||||
|
||||
return poll_status;
|
||||
@ -723,13 +751,13 @@ asynStatus masterMacsAxis::doMove(double position, int relative,
|
||||
|
||||
// =========================================================================
|
||||
|
||||
pl_status = pC_->getIntegerParam(axisNo_, pC_->motorEnableRBV_, &enabled);
|
||||
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_,
|
||||
pl_status = pC_->getDoubleParam(axisNo_, pC_->motorRecResolution(),
|
||||
&motorRecResolution);
|
||||
if (pl_status != asynSuccess) {
|
||||
return pC_->paramLibAccessFailed(pl_status, "motorRecResolution_",
|
||||
@ -738,7 +766,7 @@ asynStatus masterMacsAxis::doMove(double position, int relative,
|
||||
}
|
||||
|
||||
if (enabled == 0) {
|
||||
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
|
||||
asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
|
||||
"Controller \"%s\", axis %d => %s, line %d:\nAxis is "
|
||||
"disabled.\n",
|
||||
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__);
|
||||
@ -750,12 +778,12 @@ asynStatus masterMacsAxis::doMove(double position, int relative,
|
||||
motorVelocity = maxVelocity * motorRecResolution;
|
||||
|
||||
asynPrint(
|
||||
pC_->pasynUserSelf, ASYN_TRACE_FLOW,
|
||||
pC_->pasynUser(), ASYN_TRACE_FLOW,
|
||||
"Controller \"%s\", axis %d => %s, line %d:\nMove to position %lf.\n",
|
||||
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, position);
|
||||
|
||||
// Check if the speed is allowed to be changed
|
||||
pl_status = pC_->getIntegerParam(axisNo_, pC_->motorCanSetSpeed_,
|
||||
pl_status = pC_->getIntegerParam(axisNo_, pC_->motorCanSetSpeed(),
|
||||
&motorCanSetSpeed);
|
||||
if (pl_status != asynSuccess) {
|
||||
return pC_->paramLibAccessFailed(pl_status, "motorCanSetSpeed_",
|
||||
@ -766,7 +794,7 @@ asynStatus masterMacsAxis::doMove(double position, int relative,
|
||||
// Initialize the movement handshake
|
||||
rw_status = pC_->write(axisNo_, 86, "0");
|
||||
if (rw_status != asynSuccess) {
|
||||
pl_status = setIntegerParam(pC_->motorStatusProblem_, true);
|
||||
pl_status = setIntegerParam(pC_->motorStatusProblem(), true);
|
||||
if (pl_status != asynSuccess) {
|
||||
return pC_->paramLibAccessFailed(pl_status, "motorStatusProblem_",
|
||||
axisNo_, __PRETTY_FUNCTION__,
|
||||
@ -784,7 +812,7 @@ asynStatus masterMacsAxis::doMove(double position, int relative,
|
||||
snprintf(value, sizeof(value), "%lf", motorVelocity);
|
||||
rw_status = pC_->write(axisNo_, 05, value);
|
||||
if (rw_status != asynSuccess) {
|
||||
pl_status = setIntegerParam(pC_->motorStatusProblem_, true);
|
||||
pl_status = setIntegerParam(pC_->motorStatusProblem(), true);
|
||||
if (pl_status != asynSuccess) {
|
||||
return pC_->paramLibAccessFailed(pl_status,
|
||||
"motorStatusProblem_", axisNo_,
|
||||
@ -793,7 +821,7 @@ asynStatus masterMacsAxis::doMove(double position, int relative,
|
||||
return rw_status;
|
||||
}
|
||||
|
||||
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_FLOW,
|
||||
asynPrint(pC_->pasynUser(), ASYN_TRACE_FLOW,
|
||||
"Controller \"%s\", axis %d => %s, line %d:\nSetting speed "
|
||||
"to %lf.\n",
|
||||
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
|
||||
@ -804,7 +832,7 @@ asynStatus masterMacsAxis::doMove(double position, int relative,
|
||||
snprintf(value, sizeof(value), "%lf", motorCoordinatesPosition);
|
||||
rw_status = pC_->write(axisNo_, 02, value);
|
||||
if (rw_status != asynSuccess) {
|
||||
pl_status = setIntegerParam(pC_->motorStatusProblem_, true);
|
||||
pl_status = setIntegerParam(pC_->motorStatusProblem(), true);
|
||||
if (pl_status != asynSuccess) {
|
||||
return pC_->paramLibAccessFailed(pl_status, "motorStatusProblem_",
|
||||
axisNo_, __PRETTY_FUNCTION__,
|
||||
@ -813,14 +841,20 @@ asynStatus masterMacsAxis::doMove(double position, int relative,
|
||||
return rw_status;
|
||||
}
|
||||
|
||||
// If the motor has just been enabled, use Enable
|
||||
double timeout = pC_->comTimeout();
|
||||
if (targetReachedUninitialized_ && timeout < PowerCycleTimeout) {
|
||||
timeout = PowerCycleTimeout;
|
||||
}
|
||||
|
||||
// Start the move
|
||||
if (relative) {
|
||||
rw_status = pC_->write(axisNo_, 00, "2");
|
||||
rw_status = pC_->write(axisNo_, 00, "2", timeout);
|
||||
} else {
|
||||
rw_status = pC_->write(axisNo_, 00, "1");
|
||||
rw_status = pC_->write(axisNo_, 00, "1", timeout);
|
||||
}
|
||||
if (rw_status != asynSuccess) {
|
||||
pl_status = setIntegerParam(pC_->motorStatusProblem_, true);
|
||||
pl_status = setIntegerParam(pC_->motorStatusProblem(), true);
|
||||
if (pl_status != asynSuccess) {
|
||||
return pC_->paramLibAccessFailed(pl_status, "motorStatusProblem_",
|
||||
axisNo_, __PRETTY_FUNCTION__,
|
||||
@ -855,7 +889,39 @@ asynStatus masterMacsAxis::stop(double acceleration) {
|
||||
|
||||
rw_status = pC_->write(axisNo_, 00, "8");
|
||||
if (rw_status != asynSuccess) {
|
||||
pl_status = setIntegerParam(pC_->motorStatusProblem_, true);
|
||||
pl_status = setIntegerParam(pC_->motorStatusProblem(), true);
|
||||
if (pl_status != asynSuccess) {
|
||||
return pC_->paramLibAccessFailed(pl_status, "motorStatusProblem_",
|
||||
axisNo_, __PRETTY_FUNCTION__,
|
||||
__LINE__);
|
||||
}
|
||||
}
|
||||
|
||||
return rw_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;
|
||||
|
||||
// =========================================================================
|
||||
|
||||
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__);
|
||||
}
|
||||
}
|
||||
|
||||
rw_status = pC_->write(axisNo_, 85, "");
|
||||
if (rw_status != asynSuccess) {
|
||||
pl_status = setIntegerParam(pC_->motorStatusProblem(), true);
|
||||
if (pl_status != asynSuccess) {
|
||||
return pC_->paramLibAccessFailed(pl_status, "motorStatusProblem_",
|
||||
axisNo_, __PRETTY_FUNCTION__,
|
||||
@ -878,11 +944,11 @@ asynStatus masterMacsAxis::doHome(double min_velocity, double max_velocity,
|
||||
// Status of parameter library operations
|
||||
asynStatus pl_status = asynSuccess;
|
||||
|
||||
char response[pC_->MAXBUF_];
|
||||
char response[pC_->MAXBUF_] = {0};
|
||||
|
||||
// =========================================================================
|
||||
|
||||
pl_status = pC_->getStringParam(axisNo_, pC_->encoderType_,
|
||||
pl_status = pC_->getStringParam(axisNo_, pC_->encoderType(),
|
||||
sizeof(response), response);
|
||||
if (pl_status != asynSuccess) {
|
||||
return pC_->paramLibAccessFailed(pl_status, "encoderType_", axisNo_,
|
||||
@ -895,7 +961,7 @@ asynStatus masterMacsAxis::doHome(double min_velocity, double max_velocity,
|
||||
// Initialize the movement handshake
|
||||
rw_status = pC_->write(axisNo_, 86, "0");
|
||||
if (rw_status != asynSuccess) {
|
||||
pl_status = setIntegerParam(pC_->motorStatusProblem_, true);
|
||||
pl_status = setIntegerParam(pC_->motorStatusProblem(), true);
|
||||
if (pl_status != asynSuccess) {
|
||||
return pC_->paramLibAccessFailed(pl_status,
|
||||
"motorStatusProblem_", axisNo_,
|
||||
@ -930,7 +996,8 @@ asynStatus masterMacsAxis::readEncoderType() {
|
||||
// Status of parameter library operations
|
||||
asynStatus pl_status = asynSuccess;
|
||||
|
||||
char command[pC_->MAXBUF_], response[pC_->MAXBUF_];
|
||||
char command[pC_->MAXBUF_] = {0};
|
||||
char response[pC_->MAXBUF_] = {0};
|
||||
int nvals = 0;
|
||||
int encoder_id = 0;
|
||||
|
||||
@ -944,8 +1011,8 @@ asynStatus masterMacsAxis::readEncoderType() {
|
||||
|
||||
nvals = sscanf(response, "%d", &encoder_id);
|
||||
if (nvals != 1) {
|
||||
return pC_->errMsgCouldNotParseResponse(command, response, axisNo_,
|
||||
__PRETTY_FUNCTION__, __LINE__);
|
||||
return pC_->couldNotParseResponse(command, response, axisNo_,
|
||||
__PRETTY_FUNCTION__, __LINE__);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -955,9 +1022,9 @@ asynStatus masterMacsAxis::readEncoderType() {
|
||||
2=SSI (Absolute encoder with BiSS interface)
|
||||
*/
|
||||
if (encoder_id == 0) {
|
||||
pl_status = setStringParam(pC_->encoderType_, IncrementalEncoder);
|
||||
pl_status = setStringParam(pC_->encoderType(), IncrementalEncoder);
|
||||
} else {
|
||||
pl_status = setStringParam(pC_->encoderType_, AbsoluteEncoder);
|
||||
pl_status = setStringParam(pC_->encoderType(), AbsoluteEncoder);
|
||||
}
|
||||
|
||||
if (pl_status != asynSuccess) {
|
||||
@ -978,10 +1045,22 @@ asynStatus masterMacsAxis::enable(bool on) {
|
||||
// Status of parameter library operations
|
||||
asynStatus pl_status = asynSuccess;
|
||||
|
||||
bool moving = false;
|
||||
|
||||
// =========================================================================
|
||||
|
||||
/*
|
||||
When the motor is changing its enable state, its targetReached bit is set to
|
||||
0. In order to prevent the poll method in interpreting the motor state as
|
||||
"moving", this flag is used. It is reset in the handshake.
|
||||
*/
|
||||
targetReachedUninitialized_ = true;
|
||||
|
||||
/*
|
||||
Continue regardless of the status returned by the poll; we just want to
|
||||
find out whether the motor is currently moving or not. If the poll
|
||||
function fails before it can determine that, it is assumed that the motor
|
||||
is not moving.
|
||||
*/
|
||||
bool moving = false;
|
||||
doPoll(&moving);
|
||||
|
||||
// If the axis is currently moving, it cannot be disabled. Ignore the
|
||||
@ -989,13 +1068,13 @@ asynStatus masterMacsAxis::enable(bool on) {
|
||||
// axis instead of "moving", since status -6 is also moving, but the
|
||||
// motor can actually be disabled in this state!
|
||||
if (moving) {
|
||||
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
|
||||
asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
|
||||
"Controller \"%s\", axis %d => %s, line %d:\nAxis is not "
|
||||
"idle and can therefore not be disabled.\n",
|
||||
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__);
|
||||
|
||||
pl_status =
|
||||
setStringParam(pC_->motorMessageText_,
|
||||
setStringParam(pC_->motorMessageText(),
|
||||
"Axis cannot be disabled while it is moving.");
|
||||
if (pl_status != asynSuccess) {
|
||||
return pC_->paramLibAccessFailed(pl_status, "motorMessageText_",
|
||||
@ -1011,7 +1090,7 @@ asynStatus masterMacsAxis::enable(bool on) {
|
||||
|
||||
if ((readyToBeSwitchedOn() && switchedOn()) == on) {
|
||||
asynPrint(
|
||||
pC_->pasynUserSelf, ASYN_TRACE_WARNING,
|
||||
pC_->pasynUser(), ASYN_TRACE_WARNING,
|
||||
"Controller \"%s\", axis %d => %s, line %d:\nAxis is already %s.\n",
|
||||
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
|
||||
on ? "enabled" : "disabled");
|
||||
@ -1020,21 +1099,30 @@ asynStatus masterMacsAxis::enable(bool on) {
|
||||
|
||||
// Enable / disable the axis if it is not moving
|
||||
snprintf(value, sizeof(value), "%d", on);
|
||||
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_FLOW,
|
||||
asynPrint(pC_->pasynUser(), ASYN_TRACE_FLOW,
|
||||
"Controller \"%s\", axis %d => %s, line %d:\n%s axis.\n",
|
||||
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
|
||||
on ? "Enable" : "Disable");
|
||||
if (on == 0) {
|
||||
pl_status = setStringParam(pC_->motorMessageText_, "Disabling ...");
|
||||
pl_status = setStringParam(pC_->motorMessageText(), "Disabling ...");
|
||||
} else {
|
||||
pl_status = setStringParam(pC_->motorMessageText_, "Enabling ...");
|
||||
pl_status = setStringParam(pC_->motorMessageText(), "Enabling ...");
|
||||
}
|
||||
if (pl_status != asynSuccess) {
|
||||
return pC_->paramLibAccessFailed(pl_status, "motorMessageText_",
|
||||
axisNo_, __PRETTY_FUNCTION__,
|
||||
__LINE__);
|
||||
}
|
||||
rw_status = pC_->write(axisNo_, 04, value);
|
||||
|
||||
// The answer to the enable command on MasterMACS might take some time,
|
||||
// hence we wait for a custom timespan in seconds instead of
|
||||
// pC_->comTimeout_
|
||||
double timeout = pC_->comTimeout();
|
||||
if (targetReachedUninitialized_ && timeout < PowerCycleTimeout) {
|
||||
timeout = PowerCycleTimeout;
|
||||
}
|
||||
|
||||
rw_status = pC_->write(axisNo_, 04, value, timeout);
|
||||
if (rw_status != asynSuccess) {
|
||||
return rw_status;
|
||||
}
|
||||
@ -1061,7 +1149,7 @@ asynStatus masterMacsAxis::enable(bool on) {
|
||||
|
||||
// Failed to change axis status within timeout_enable_disable => Send a
|
||||
// corresponding message
|
||||
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
|
||||
asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
|
||||
"Controller \"%s\", axis %d => %s, line %d:\nFailed to %s axis "
|
||||
"within %d seconds\n",
|
||||
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
|
||||
@ -1070,7 +1158,7 @@ asynStatus masterMacsAxis::enable(bool on) {
|
||||
// Output message to user
|
||||
snprintf(value, sizeof(value), "Failed to %s within %d seconds",
|
||||
on ? "enable" : "disable", timeout_enable_disable);
|
||||
pl_status = setStringParam(pC_->motorMessageText_, "Enabling ...");
|
||||
pl_status = setStringParam(pC_->motorMessageText(), "Enabling ...");
|
||||
if (pl_status != asynSuccess) {
|
||||
return pC_->paramLibAccessFailed(pl_status, "motorMessageText_",
|
||||
axisNo_, __PRETTY_FUNCTION__,
|
||||
@ -1094,7 +1182,7 @@ std::bitset<16> toBitset(float val) {
|
||||
}
|
||||
|
||||
asynStatus masterMacsAxis::readAxisStatus() {
|
||||
char response[pC_->MAXBUF_];
|
||||
char response[pC_->MAXBUF_] = {0};
|
||||
|
||||
// =========================================================================
|
||||
|
||||
@ -1104,8 +1192,8 @@ asynStatus masterMacsAxis::readAxisStatus() {
|
||||
float axisStatus = 0;
|
||||
int nvals = sscanf(response, "%f", &axisStatus);
|
||||
if (nvals != 1) {
|
||||
return pC_->errMsgCouldNotParseResponse(
|
||||
"R10", response, axisNo_, __PRETTY_FUNCTION__, __LINE__);
|
||||
return pC_->couldNotParseResponse("R10", response, axisNo_,
|
||||
__PRETTY_FUNCTION__, __LINE__);
|
||||
}
|
||||
|
||||
axisStatus_ = toBitset(axisStatus);
|
||||
@ -1115,7 +1203,7 @@ asynStatus masterMacsAxis::readAxisStatus() {
|
||||
}
|
||||
|
||||
asynStatus masterMacsAxis::readAxisError() {
|
||||
char response[pC_->MAXBUF_];
|
||||
char response[pC_->MAXBUF_] = {0};
|
||||
|
||||
// =========================================================================
|
||||
|
||||
@ -1125,8 +1213,8 @@ asynStatus masterMacsAxis::readAxisError() {
|
||||
float axisError = 0;
|
||||
int nvals = sscanf(response, "%f", &axisError);
|
||||
if (nvals != 1) {
|
||||
return pC_->errMsgCouldNotParseResponse(
|
||||
"R11", response, axisNo_, __PRETTY_FUNCTION__, __LINE__);
|
||||
return pC_->couldNotParseResponse("R11", response, axisNo_,
|
||||
__PRETTY_FUNCTION__, __LINE__);
|
||||
}
|
||||
axisError_ = toBitset(axisError);
|
||||
}
|
||||
|
@ -24,15 +24,6 @@ class masterMacsAxis : public sinqAxis {
|
||||
*/
|
||||
virtual ~masterMacsAxis();
|
||||
|
||||
/**
|
||||
* @brief Implementation of the `stop` function from asynMotorAxis
|
||||
*
|
||||
* @param acceleration Acceleration ACCEL from the motor record. This
|
||||
* value is currently not used.
|
||||
* @return asynStatus
|
||||
*/
|
||||
asynStatus stop(double acceleration);
|
||||
|
||||
/**
|
||||
* @brief Implementation of the `doHome` function from sinqAxis. The
|
||||
* parameters are described in the documentation of `sinqAxis::doHome`.
|
||||
@ -69,6 +60,23 @@ class masterMacsAxis : public sinqAxis {
|
||||
asynStatus doMove(double position, int relative, double min_velocity,
|
||||
double max_velocity, double acceleration);
|
||||
|
||||
/**
|
||||
* @brief Implementation of the `stop` function from asynMotorAxis
|
||||
*
|
||||
* @param acceleration Acceleration ACCEL from the motor record. This
|
||||
* value is currently not used.
|
||||
* @return asynStatus
|
||||
*/
|
||||
asynStatus stop(double acceleration);
|
||||
|
||||
/**
|
||||
* @brief Implementation of the `doReset` function from sinqAxis.
|
||||
*
|
||||
* @param on
|
||||
* @return asynStatus
|
||||
*/
|
||||
asynStatus doReset();
|
||||
|
||||
/**
|
||||
* @brief Readout of some values from the controller at IOC startup
|
||||
*
|
||||
@ -103,6 +111,8 @@ class masterMacsAxis : public sinqAxis {
|
||||
bool waitForHandshake_;
|
||||
time_t timeAtHandshake_;
|
||||
|
||||
bool targetReachedUninitialized_;
|
||||
|
||||
asynStatus readConfig();
|
||||
|
||||
/*
|
||||
@ -273,9 +283,6 @@ class masterMacsAxis : public sinqAxis {
|
||||
* @brief Read the property from axisError_
|
||||
*/
|
||||
bool stoFault() { return axisError_[15]; }
|
||||
|
||||
private:
|
||||
friend class masterMacsController;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -49,14 +49,10 @@ masterMacsController::masterMacsController(const char *portName,
|
||||
int numAxes, double movingPollPeriod,
|
||||
double idlePollPeriod,
|
||||
double comTimeout)
|
||||
: sinqController(
|
||||
portName, ipPortConfigName, numAxes, movingPollPeriod, idlePollPeriod,
|
||||
/*
|
||||
The following parameter library entries are added in this driver:
|
||||
- REREAD_ENCODER_POSITION
|
||||
- READ_CONFIG
|
||||
*/
|
||||
NUM_masterMacs_DRIVER_PARAMS)
|
||||
: sinqController(portName, ipPortConfigName, numAxes, movingPollPeriod,
|
||||
idlePollPeriod,
|
||||
// No additional parameter library entries
|
||||
0)
|
||||
|
||||
{
|
||||
|
||||
@ -64,39 +60,26 @@ masterMacsController::masterMacsController(const char *portName,
|
||||
asynStatus status = asynSuccess;
|
||||
|
||||
// Initialization of all member variables
|
||||
lowLevelPortUser_ = nullptr;
|
||||
comTimeout_ = comTimeout;
|
||||
|
||||
// =========================================================================;
|
||||
|
||||
/*
|
||||
We try to connect to the port via the port name provided by the constructor.
|
||||
If this fails, the function is terminated via exit
|
||||
*/
|
||||
pasynOctetSyncIO->connect(ipPortConfigName, 0, &lowLevelPortUser_, NULL);
|
||||
if (status != asynSuccess || lowLevelPortUser_ == nullptr) {
|
||||
errlogPrintf("Controller \"%s\" => %s, line %d:\nFATAL ERROR (cannot "
|
||||
"connect to MCU controller).\nTerminating IOC",
|
||||
portName, __PRETTY_FUNCTION__, __LINE__);
|
||||
exit(-1);
|
||||
}
|
||||
// =========================================================================
|
||||
|
||||
/*
|
||||
Define the end-of-string of a message coming from the device to EPICS.
|
||||
It is not necessary to append a terminator to outgoing messages, since
|
||||
the message length is encoded in the message header in the getSetResponse
|
||||
method.
|
||||
the message length is encoded in the message header.
|
||||
*/
|
||||
const char *message_from_device = "\x03"; // Hex-code for ETX
|
||||
status = pasynOctetSyncIO->setInputEos(
|
||||
lowLevelPortUser_, message_from_device, strlen(message_from_device));
|
||||
const char *message_from_device = "\x0D"; // Hex-code for CR
|
||||
status = pasynOctetSyncIO->setInputEos(pasynOctetSyncIOipPort(),
|
||||
message_from_device,
|
||||
strlen(message_from_device));
|
||||
if (status != asynSuccess) {
|
||||
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
|
||||
"Controller \"%s\" => %s, line %d:\nFATAL ERROR (setting "
|
||||
"input EOS failed with %s).\nTerminating IOC",
|
||||
portName, __PRETTY_FUNCTION__, __LINE__,
|
||||
stringifyAsynStatus(status));
|
||||
pasynOctetSyncIO->disconnect(lowLevelPortUser_);
|
||||
pasynOctetSyncIO->disconnect(pasynOctetSyncIOipPort());
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
@ -107,7 +90,7 @@ masterMacsController::masterMacsController(const char *portName,
|
||||
"ParamLib callbacks failed with %s).\nTerminating IOC",
|
||||
portName, __PRETTY_FUNCTION__, __LINE__,
|
||||
stringifyAsynStatus(status));
|
||||
pasynOctetSyncIO->disconnect(lowLevelPortUser_);
|
||||
pasynOctetSyncIO->disconnect(pasynOctetSyncIOipPort());
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
@ -131,26 +114,25 @@ masterMacsAxis *masterMacsController::getMasterMacsAxis(int axisNo) {
|
||||
return dynamic_cast<masterMacsAxis *>(asynAxis);
|
||||
}
|
||||
|
||||
asynStatus masterMacsController::read(int axisNo, int tcpCmd, char *response) {
|
||||
asynStatus masterMacsController::read(int axisNo, int tcpCmd, char *response,
|
||||
double comTimeout) {
|
||||
return writeRead(axisNo, tcpCmd, NULL, response);
|
||||
}
|
||||
|
||||
asynStatus masterMacsController::write(int axisNo, int tcpCmd,
|
||||
const char *payload) {
|
||||
return writeRead(axisNo, tcpCmd, payload, NULL);
|
||||
const char *payload, double comTimeout) {
|
||||
return writeRead(axisNo, tcpCmd, payload, NULL, comTimeout);
|
||||
}
|
||||
|
||||
asynStatus masterMacsController::writeRead(int axisNo, int tcpCmd,
|
||||
const char *payload,
|
||||
char *response) {
|
||||
const char *payload, char *response,
|
||||
double comTimeout) {
|
||||
|
||||
// Definition of local variables.
|
||||
asynStatus status = asynSuccess;
|
||||
asynStatus pl_status = asynSuccess;
|
||||
char fullCommand[MAXBUF_] = {0};
|
||||
char fullResponse[MAXBUF_] = {0};
|
||||
char printableCommand[MAXBUF_] = {0};
|
||||
char printableResponse[MAXBUF_] = {0};
|
||||
char drvMessageText[MAXBUF_] = {0};
|
||||
int motorStatusProblem = 0;
|
||||
|
||||
@ -174,124 +156,65 @@ asynStatus masterMacsController::writeRead(int axisNo, int tcpCmd,
|
||||
|
||||
// =========================================================================
|
||||
|
||||
// Check if a custom timeout has been given
|
||||
if (comTimeout < 0.0) {
|
||||
comTimeout = comTimeout_;
|
||||
}
|
||||
|
||||
masterMacsAxis *axis = getMasterMacsAxis(axisNo);
|
||||
if (axis == nullptr) {
|
||||
// We already did the error logging directly in getAxis
|
||||
return asynError;
|
||||
}
|
||||
|
||||
/*
|
||||
PSI SINQ uses a custom protocol which is described in
|
||||
PSI_TCP_Interface_V1-8.pdf (p. // 4-17).
|
||||
A special case is the message length, which is specified by two bytes
|
||||
LSB and MSB: MSB = message length / 256 LSB = message length % 256. For
|
||||
example, a message length of 47 chars would result in MSB = 0, LSB = 47,
|
||||
whereas a message length of 356 would result in MSB = 1, LSB = 100.
|
||||
|
||||
The full protocol looks as follows:
|
||||
0x05 -> Start of protocol frame ENQ
|
||||
[LSB]
|
||||
[MSB]
|
||||
0x19 -> Data type PDO1
|
||||
value [Actual message] It is not necessary to append a terminator, since
|
||||
this protocol encodes the message length in LSB and MSB.
|
||||
0x0D -> Carriage return (ASCII alias \r)
|
||||
0x03 -> End of text ETX
|
||||
*/
|
||||
|
||||
fullCommand[0] = '\x05'; // ENQ
|
||||
fullCommand[1] = 1; // Placeholder value, can be anything other than 0
|
||||
fullCommand[2] = 1; // Placeholder value, can be anything other than 0
|
||||
fullCommand[3] = '\x19'; // PD01
|
||||
|
||||
// Create the command and add CR and ETX at the end
|
||||
// Build the full command depending on the inputs to this function
|
||||
if (isRead) {
|
||||
snprintf(&fullCommand[4], MAXBUF_ - 4, "%dR%02d\x0D\x03", axisNo,
|
||||
tcpCmd);
|
||||
snprintf(fullCommand, MAXBUF_ - 1, "%dR%02d\x0D", axisNo, tcpCmd);
|
||||
} else {
|
||||
snprintf(&fullCommand[4], MAXBUF_ - 4, "%dS%02d=%s\x0D\x03", axisNo,
|
||||
tcpCmd, payload);
|
||||
if (strlen(payload) == 0) {
|
||||
snprintf(fullCommand, MAXBUF_ - 1, "%dS%02d\x0D", axisNo, tcpCmd);
|
||||
} else {
|
||||
snprintf(fullCommand, MAXBUF_ - 1, "%dS%02d=%s\x0D", axisNo, tcpCmd,
|
||||
payload);
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate the command length
|
||||
const size_t fullCommandLength = strlen(fullCommand);
|
||||
|
||||
// Length of the command without ENQ and ETX
|
||||
const size_t lenWithMetadata = fullCommandLength - 2;
|
||||
// Flush the IOC-side socket, then write the command and wait for the
|
||||
// response.
|
||||
status = pasynOctetSyncIO->writeRead(
|
||||
pasynOctetSyncIOipPort(), fullCommand, fullCommandLength, fullResponse,
|
||||
MAXBUF_, comTimeout, &nbytesOut, &nbytesIn, &eomReason);
|
||||
|
||||
// Perform both division and modulo operation at once.
|
||||
div_t lenWithMetadataSep = std::div(lenWithMetadata, 256);
|
||||
|
||||
// Now set the actual command length
|
||||
fullCommand[1] = lenWithMetadataSep.rem; // LSB
|
||||
fullCommand[2] = lenWithMetadataSep.quot; // MSB
|
||||
|
||||
adjustForPrint(printableCommand, fullCommand, MAXBUF_);
|
||||
|
||||
// Send out the command
|
||||
status =
|
||||
pasynOctetSyncIO->write(lowLevelPortUser_, fullCommand,
|
||||
fullCommandLength, comTimeout_, &nbytesOut);
|
||||
|
||||
msgPrintControlKey writeKey =
|
||||
// If a communication error occured, print this message to the
|
||||
msgPrintControlKey comKey =
|
||||
msgPrintControlKey(portName, axisNo, __PRETTY_FUNCTION__, __LINE__);
|
||||
|
||||
if (status == asynSuccess) {
|
||||
msgPrintControl_.resetCount(writeKey);
|
||||
|
||||
// Try to read the answer repeatedly
|
||||
int maxTrials = 2;
|
||||
for (int i = 0; i < maxTrials; i++) {
|
||||
status =
|
||||
pasynOctetSyncIO->read(lowLevelPortUser_, fullResponse, MAXBUF_,
|
||||
comTimeout_, &nbytesIn, &eomReason);
|
||||
|
||||
if (status == asynSuccess) {
|
||||
status = parseResponse(fullCommand, fullResponse,
|
||||
drvMessageText, &valueStart, &valueStop,
|
||||
axisNo, tcpCmd, isRead);
|
||||
if (status == asynSuccess) {
|
||||
// Received the correct message
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
|
||||
"Controller \"%s\", axis %d => %s, line %d:\nError "
|
||||
"%s while reading from the controller\n",
|
||||
portName, axisNo, __PRETTY_FUNCTION__, __LINE__,
|
||||
stringifyAsynStatus(status));
|
||||
break;
|
||||
}
|
||||
|
||||
if (i + 1 == maxTrials && status == asynError) {
|
||||
asynPrint(
|
||||
this->pasynUserSelf, ASYN_TRACE_ERROR,
|
||||
"Controller \"%s\", axis %d => %s, line %d:\nFailed "
|
||||
"%d times to get the correct response. Aborting read.\n",
|
||||
portName, axisNo, __PRETTY_FUNCTION__, __LINE__, maxTrials);
|
||||
}
|
||||
if (status != asynSuccess) {
|
||||
if (msgPrintControl_.shouldBePrinted(comKey, true, pasynUserSelf)) {
|
||||
char printableCommand[MAXBUF_] = {0};
|
||||
adjustForPrint(printableCommand, fullCommand, MAXBUF_);
|
||||
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
|
||||
"Controller \"%s\", axis %d => %s, line %d:\nError "
|
||||
"%s while sending command %s to the controller\n",
|
||||
portName, axisNo, __PRETTY_FUNCTION__, __LINE__,
|
||||
stringifyAsynStatus(status), printableCommand);
|
||||
}
|
||||
|
||||
} else {
|
||||
if (msgPrintControl_.shouldBePrinted(writeKey, true, pasynUserSelf)) {
|
||||
asynPrint(
|
||||
this->pasynUserSelf, ASYN_TRACE_ERROR,
|
||||
"Controller \"%s\", axis %d => %s, line %d:\nError %s while "
|
||||
"writing to the controller.%s\n",
|
||||
portName, axisNo, __PRETTY_FUNCTION__, __LINE__,
|
||||
stringifyAsynStatus(status), msgPrintControl_.getSuffix());
|
||||
}
|
||||
msgPrintControl_.resetCount(comKey, pasynUserSelf);
|
||||
}
|
||||
|
||||
// MasterMACS needs a bit of time between messages, therefore thr
|
||||
// program execution is paused after the communication happened.
|
||||
// usleep(1500);
|
||||
|
||||
// Create custom error messages for different failure modes
|
||||
switch (status) {
|
||||
case asynSuccess:
|
||||
// We did get a response, but does it make sense and is it designated as
|
||||
// OK from the controller? This is checked here.
|
||||
status = parseResponse(fullCommand, fullResponse, drvMessageText,
|
||||
&valueStart, &valueStop, axisNo, tcpCmd, isRead);
|
||||
|
||||
if (isRead) {
|
||||
// Read out the important information from the response
|
||||
if (status == asynSuccess && isRead) {
|
||||
/*
|
||||
If a property has been read, we need just the part between the
|
||||
"=" (0x3D) and the [ACK] (0x06). Therefore, we remove all
|
||||
@ -302,33 +225,39 @@ asynStatus masterMacsController::writeRead(int axisNo, int tcpCmd,
|
||||
response[i] = fullResponse[i + valueStart];
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
case asynTimeout:
|
||||
snprintf(drvMessageText, sizeof(drvMessageText),
|
||||
"connection timeout for axis %d", axisNo);
|
||||
"Connection timeout. Please call the support.");
|
||||
break;
|
||||
case asynDisconnected:
|
||||
snprintf(drvMessageText, sizeof(drvMessageText),
|
||||
"axis is not connected");
|
||||
"Axis is not connected.");
|
||||
break;
|
||||
case asynDisabled:
|
||||
snprintf(drvMessageText, sizeof(drvMessageText), "axis is disabled");
|
||||
snprintf(drvMessageText, sizeof(drvMessageText), "Axis is disabled.");
|
||||
break;
|
||||
case asynError:
|
||||
// Do nothing - error message drvMessageText has already been set.
|
||||
break;
|
||||
default:
|
||||
snprintf(drvMessageText, sizeof(drvMessageText),
|
||||
"Communication failed (%s)", stringifyAsynStatus(status));
|
||||
"Communication failed (%s). Please call the support.",
|
||||
stringifyAsynStatus(status));
|
||||
break;
|
||||
}
|
||||
|
||||
// Log the overall status (communication successfull or not)
|
||||
if (status == asynSuccess) {
|
||||
adjustForPrint(printableResponse, fullResponse, MAXBUF_);
|
||||
pl_status = axis->setIntegerParam(this->motorStatusCommsError_, 0);
|
||||
if (pl_status != asynSuccess) {
|
||||
return paramLibAccessFailed(pl_status, "motorStatusCommsError_",
|
||||
axisNo, __PRETTY_FUNCTION__, __LINE__);
|
||||
}
|
||||
} else if (status == asynDisconnected) {
|
||||
// Do nothing
|
||||
} else {
|
||||
// Set the error status bits only if the axis is not disconnected
|
||||
|
||||
// Check if the axis already is in an error communication mode. If
|
||||
// it is not, upstream the error. This is done to avoid "flooding"
|
||||
@ -381,14 +310,22 @@ asynStatus masterMacsController::parseResponse(
|
||||
const char *fullCommand, const char *fullResponse, char *drvMessageText,
|
||||
int *valueStart, int *valueStop, int axisNo, int tcpCmd, bool isRead) {
|
||||
|
||||
bool responseValid = false;
|
||||
int responseStart = 0;
|
||||
asynStatus status = asynSuccess;
|
||||
int prevConnected = 0;
|
||||
char printableCommand[MAXBUF_] = {0};
|
||||
char printableResponse[MAXBUF_] = {0};
|
||||
|
||||
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__);
|
||||
}
|
||||
|
||||
// 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++) {
|
||||
@ -397,22 +334,120 @@ asynStatus masterMacsController::parseResponse(
|
||||
} else if (fullResponse[i] == '=') {
|
||||
*valueStart = i + 1;
|
||||
} else if (fullResponse[i] == '\x06') {
|
||||
// ACK
|
||||
*valueStop = i;
|
||||
responseValid = true;
|
||||
break;
|
||||
} else if (fullResponse[i] == '\x15') {
|
||||
// NAK
|
||||
snprintf(drvMessageText, MAXBUF_, "Communication failed.");
|
||||
|
||||
if (msgPrintControl_.shouldBePrinted(parseKey, true,
|
||||
pasynUserSelf)) {
|
||||
// Motor wasn't connected before -> Update the paramLib entry and PV
|
||||
// to show it is now connected.
|
||||
if (prevConnected == 0) {
|
||||
|
||||
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
|
||||
"Controller \"%s\", axis %d => %s, line "
|
||||
"%d:\nCommunication failed.%s\n",
|
||||
portName, axisNo, __PRETTY_FUNCTION__, __LINE__,
|
||||
msgPrintControl_.getSuffix());
|
||||
"%d:\nAxis connection status has changed to "
|
||||
"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__);
|
||||
}
|
||||
status = callParamCallbacks();
|
||||
if (status != asynSuccess) {
|
||||
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
|
||||
"Controller \"%s\", axis %d => %s, line "
|
||||
"%d:\nCould not update parameter library\n",
|
||||
portName, axisNo, __PRETTY_FUNCTION__, __LINE__);
|
||||
return status;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
msgPrintControl_.resetCount(parseKey, pasynUserSelf);
|
||||
|
||||
// Check if the response matches the expectations. Each response
|
||||
// contains the string "axisNo R tcpCmd" (including the spaces)
|
||||
char expectedResponseSubstring[MAXBUF_] = {0};
|
||||
|
||||
// The response does not contain a leading 0 if tcpCmd only has
|
||||
// a single digit!
|
||||
if (isRead) {
|
||||
snprintf(expectedResponseSubstring, MAXBUF_ - 4, "%d R %d",
|
||||
axisNo, tcpCmd);
|
||||
} else {
|
||||
snprintf(expectedResponseSubstring, MAXBUF_ - 4, "%d S %d",
|
||||
axisNo, tcpCmd);
|
||||
}
|
||||
|
||||
msgPrintControlKey responseMatchKey = msgPrintControlKey(
|
||||
portName, axisNo, __PRETTY_FUNCTION__, __LINE__);
|
||||
|
||||
if (strstr(&fullResponse[responseStart],
|
||||
expectedResponseSubstring) == NULL) {
|
||||
adjustForPrint(printableCommand, fullCommand, MAXBUF_);
|
||||
adjustForPrint(printableResponse, fullResponse, MAXBUF_);
|
||||
|
||||
if (msgPrintControl_.shouldBePrinted(parseKey, true,
|
||||
pasynUserSelf)) {
|
||||
asynPrint(this->pasynUserSelf, ASYN_TRACEIO_DRIVER,
|
||||
"Controller \"%s\", axis %d => %s, line "
|
||||
"%d:\nMismatched "
|
||||
"response %s to command %s.%s\n",
|
||||
portName, axisNo, __PRETTY_FUNCTION__, __LINE__,
|
||||
printableResponse, printableCommand,
|
||||
msgPrintControl_.getSuffix());
|
||||
}
|
||||
|
||||
snprintf(
|
||||
drvMessageText, MAXBUF_,
|
||||
"Mismatched response %s to command %s. Please call the "
|
||||
"support.",
|
||||
printableResponse, printableCommand);
|
||||
return asynError;
|
||||
} else {
|
||||
msgPrintControl_.resetCount(responseMatchKey, pasynUserSelf);
|
||||
}
|
||||
return asynSuccess;
|
||||
} else if (fullResponse[i] == '\x15') {
|
||||
/*
|
||||
NAK
|
||||
This indicates that the axis is not connected. This is not an error!
|
||||
*/
|
||||
snprintf(drvMessageText, MAXBUF_, "Axis not connected.");
|
||||
|
||||
// Motor was connected before -> Update the paramLib entry and PV
|
||||
// to show it is now disconnected.
|
||||
if (prevConnected == 1) {
|
||||
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
|
||||
"Controller \"%s\", axis %d => %s, line "
|
||||
"%d:\nAxis connection status has changed to "
|
||||
"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__);
|
||||
}
|
||||
status = callParamCallbacks();
|
||||
if (status != asynSuccess) {
|
||||
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
|
||||
"Controller \"%s\", axis %d => %s, line "
|
||||
"%d:\nCould not update parameter library\n",
|
||||
portName, axisNo, __PRETTY_FUNCTION__, __LINE__);
|
||||
return status;
|
||||
}
|
||||
}
|
||||
return asynDisconnected;
|
||||
} else if (fullResponse[i] == '\x18') {
|
||||
// CAN
|
||||
snprintf(drvMessageText, MAXBUF_,
|
||||
@ -421,6 +456,7 @@ asynStatus masterMacsController::parseResponse(
|
||||
|
||||
if (msgPrintControl_.shouldBePrinted(parseKey, true,
|
||||
pasynUserSelf)) {
|
||||
adjustForPrint(printableCommand, fullCommand, MAXBUF_);
|
||||
asynPrint(
|
||||
this->pasynUserSelf, ASYN_TRACE_ERROR,
|
||||
"Controller \"%s\", axis %d => %s, line %d:\nTried to "
|
||||
@ -428,60 +464,14 @@ asynStatus masterMacsController::parseResponse(
|
||||
portName, axisNo, __PRETTY_FUNCTION__, __LINE__,
|
||||
printableCommand, msgPrintControl_.getSuffix());
|
||||
}
|
||||
responseValid = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (responseValid) {
|
||||
msgPrintControl_.resetCount(parseKey);
|
||||
|
||||
// Check if the response matches the expectations. Each response
|
||||
// contains the string "axisNo R tcpCmd" (including the spaces)
|
||||
char expectedResponseSubstring[MAXBUF_] = {0};
|
||||
|
||||
// The response does not contain a leading 0 if tcpCmd only has
|
||||
// a single digit!
|
||||
if (isRead) {
|
||||
snprintf(expectedResponseSubstring, MAXBUF_ - 4, "%d R %d", axisNo,
|
||||
tcpCmd);
|
||||
} else {
|
||||
snprintf(expectedResponseSubstring, MAXBUF_ - 4, "%d S %d", axisNo,
|
||||
tcpCmd);
|
||||
}
|
||||
|
||||
msgPrintControlKey responseMatchKey =
|
||||
msgPrintControlKey(portName, axisNo, __PRETTY_FUNCTION__, __LINE__);
|
||||
|
||||
if (strstr(&fullResponse[responseStart], expectedResponseSubstring) ==
|
||||
NULL) {
|
||||
adjustForPrint(printableCommand, fullCommand, MAXBUF_);
|
||||
adjustForPrint(printableResponse, fullResponse, MAXBUF_);
|
||||
|
||||
if (msgPrintControl_.shouldBePrinted(parseKey, true,
|
||||
pasynUserSelf)) {
|
||||
asynPrint(
|
||||
this->pasynUserSelf, ASYN_TRACEIO_DRIVER,
|
||||
"Controller \"%s\", axis %d => %s, line %d:\nMismatched "
|
||||
"response %s to command %s.%s\n",
|
||||
portName, axisNo, __PRETTY_FUNCTION__, __LINE__,
|
||||
printableResponse, printableCommand,
|
||||
msgPrintControl_.getSuffix());
|
||||
}
|
||||
|
||||
snprintf(drvMessageText, MAXBUF_,
|
||||
"Mismatched response %s to command %s. Please call the "
|
||||
"support.",
|
||||
printableResponse, printableCommand);
|
||||
return asynError;
|
||||
} else {
|
||||
msgPrintControl_.resetCount(responseMatchKey);
|
||||
}
|
||||
}
|
||||
return asynSuccess;
|
||||
return asynError;
|
||||
}
|
||||
|
||||
asynStatus sinqController::readInt32(asynUser *pasynUser, epicsInt32 *value) {
|
||||
asynStatus masterMacsController::readInt32(asynUser *pasynUser,
|
||||
epicsInt32 *value) {
|
||||
// masterMacs can be disabled
|
||||
if (pasynUser->reason == motorCanDisable_) {
|
||||
*value = 1;
|
||||
|
@ -49,9 +49,6 @@ class masterMacsController : public sinqController {
|
||||
*/
|
||||
masterMacsAxis *getMasterMacsAxis(int axisNo);
|
||||
|
||||
protected:
|
||||
asynUser *lowLevelPortUser_;
|
||||
|
||||
/**
|
||||
* @brief Send a command to the hardware (S mode)
|
||||
*
|
||||
@ -60,7 +57,8 @@ class masterMacsController : public sinqController {
|
||||
* @param payload Value send to MasterMACS.
|
||||
* @return asynStatus
|
||||
*/
|
||||
asynStatus write(int axisNo, int tcpCmd, const char *payload);
|
||||
asynStatus write(int axisNo, int tcpCmd, const char *payload,
|
||||
double comTimeout = -1.0);
|
||||
|
||||
/**
|
||||
* @brief Send a command to the hardware and receive a response (R mode)
|
||||
@ -71,7 +69,8 @@ class masterMacsController : public sinqController {
|
||||
* expected to have the size MAXBUF_.
|
||||
* @return asynStatus
|
||||
*/
|
||||
asynStatus read(int axisNo, int tcpCmd, char *response);
|
||||
asynStatus read(int axisNo, int tcpCmd, char *response,
|
||||
double comTimeout = -1.0);
|
||||
|
||||
/**
|
||||
* @brief Send a command to the hardware (R or S mode) and receive a
|
||||
@ -88,7 +87,7 @@ class masterMacsController : public sinqController {
|
||||
* @return asynStatus
|
||||
*/
|
||||
asynStatus writeRead(int axisNo, int tcpCmd, const char *payload,
|
||||
char *response);
|
||||
char *response, double comTimeout = -1.0);
|
||||
|
||||
/**
|
||||
* @brief Parse "fullResponse" received upon sending "fullCommand".
|
||||
@ -114,26 +113,25 @@ class masterMacsController : public sinqController {
|
||||
int *valueStop, int axisNo, int tcpCmd,
|
||||
bool isRead);
|
||||
|
||||
private:
|
||||
// Set the maximum buffer size. This is an empirical value which must be
|
||||
// large enough to avoid overflows for all commands to the device /
|
||||
// responses from it.
|
||||
static const uint32_t MAXBUF_ = 200;
|
||||
|
||||
/**
|
||||
* @brief Get the communication timeout used in the writeRead command
|
||||
*
|
||||
* @return double Timeout in seconds
|
||||
*/
|
||||
double comTimeout() { return comTimeout_; }
|
||||
|
||||
asynStatus readInt32(asynUser *pasynUser, epicsInt32 *value);
|
||||
|
||||
private:
|
||||
/*
|
||||
Stores the constructor input comTimeout
|
||||
*/
|
||||
double comTimeout_;
|
||||
|
||||
// Indices of additional PVs
|
||||
#define FIRST_masterMacs_PARAM rereadEncoderPosition_
|
||||
int rereadEncoderPosition_;
|
||||
int readConfig_;
|
||||
#define LAST_masterMacs_PARAM readConfig_
|
||||
|
||||
friend class masterMacsAxis;
|
||||
};
|
||||
#define NUM_masterMacs_DRIVER_PARAMS \
|
||||
(&LAST_masterMacs_PARAM - &FIRST_masterMacs_PARAM + 1)
|
||||
|
||||
#endif /* masterMacsController_H */
|
||||
|
171
utils/writeRead.py
Normal file
171
utils/writeRead.py
Normal file
@ -0,0 +1,171 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
This script allows direct interaction with a MasterMACS-Controller over an ethernet connection.
|
||||
To read the manual, simply run this script without any arguments.
|
||||
|
||||
Stefan Mathis, April 2025
|
||||
"""
|
||||
|
||||
import struct
|
||||
import socket
|
||||
import curses
|
||||
|
||||
def packMasterMacsCommand(command):
|
||||
# 0x0D = Carriage return
|
||||
buf = struct.pack('B',0x0D)
|
||||
buf = bytes(command,'utf-8') + buf
|
||||
return bytes(command,'utf-8')
|
||||
|
||||
def readMasterMacsReply(input):
|
||||
msg = bytearray()
|
||||
expectAck = True
|
||||
while True:
|
||||
b = input.recv(1)
|
||||
bint = int.from_bytes(b,byteorder='little')
|
||||
if bint == 2 or bint == 7: #STX or BELL
|
||||
expectAck = False
|
||||
continue
|
||||
if expectAck and bint == 6: # ACK
|
||||
return bytes(msg)
|
||||
else:
|
||||
if bint == 13 and not expectAck: # CR
|
||||
return bytes(msg)
|
||||
else:
|
||||
msg.append(bint)
|
||||
|
||||
if __name__ == "__main__":
|
||||
from sys import argv
|
||||
|
||||
try:
|
||||
|
||||
addr = argv[1].split(':')
|
||||
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
|
||||
s.connect((addr[0],int(addr[1])))
|
||||
|
||||
if len(argv) == 3:
|
||||
buf = packMasterMacsCommand(argv[2])
|
||||
s.send(buf)
|
||||
reply = readMasterMacsReply(s)
|
||||
print(reply.decode('utf-8') + '\n')
|
||||
|
||||
else:
|
||||
|
||||
try:
|
||||
|
||||
stdscr = curses.initscr()
|
||||
curses.noecho()
|
||||
curses.cbreak()
|
||||
stdscr.keypad(True)
|
||||
stdscr.scrollok(True)
|
||||
|
||||
stdscr.addstr(">> ")
|
||||
stdscr.refresh()
|
||||
|
||||
history = [""]
|
||||
ptr = len(history) - 1
|
||||
|
||||
while True:
|
||||
c = stdscr.getch()
|
||||
if c == curses.KEY_RIGHT:
|
||||
(y, x) = stdscr.getyx()
|
||||
if x < len(history[ptr]) + 3:
|
||||
stdscr.move(y, x+1)
|
||||
stdscr.refresh()
|
||||
elif c == curses.KEY_LEFT:
|
||||
(y, x) = stdscr.getyx()
|
||||
if x > 3:
|
||||
stdscr.move(y, x-1)
|
||||
stdscr.refresh()
|
||||
elif c == curses.KEY_UP:
|
||||
if ptr > 0:
|
||||
ptr -= 1
|
||||
stdscr.addch("\r")
|
||||
stdscr.clrtoeol()
|
||||
stdscr.addstr(">> " + history[ptr])
|
||||
elif c == curses.KEY_DOWN:
|
||||
if ptr < len(history) - 1:
|
||||
ptr += 1
|
||||
stdscr.addch("\r")
|
||||
stdscr.clrtoeol()
|
||||
stdscr.addstr(">> " + history[ptr])
|
||||
elif c == curses.KEY_ENTER or c == ord('\n') or c == ord('\r'):
|
||||
if history[ptr] == 'quit':
|
||||
break
|
||||
|
||||
# because of arrow keys move back to the end of the line
|
||||
(y, x) = stdscr.getyx()
|
||||
stdscr.move(y, 3+len(history[ptr]))
|
||||
|
||||
if history[ptr]:
|
||||
buf = packMasterMacsCommand(history[ptr])
|
||||
s.send(buf)
|
||||
reply = readMasterMacsReply(s)
|
||||
stdscr.addstr("\n" + reply.decode('utf-8')[0:-1])
|
||||
|
||||
if ptr == len(history) - 1 and history[ptr] != "":
|
||||
history += [""]
|
||||
else:
|
||||
history[-1] = ""
|
||||
ptr = len(history) - 1
|
||||
|
||||
stdscr.addstr("\n>> ")
|
||||
stdscr.refresh()
|
||||
|
||||
else:
|
||||
if ptr < len(history) - 1: # Modifying previous input
|
||||
if len(history[-1]) == 0:
|
||||
history[-1] = history[ptr]
|
||||
ptr = len(history) - 1
|
||||
|
||||
else:
|
||||
history += [history[ptr]]
|
||||
ptr = len(history) - 1
|
||||
|
||||
if c == curses.KEY_BACKSPACE:
|
||||
if len(history[ptr]) == 0:
|
||||
continue
|
||||
(y, x) = stdscr.getyx()
|
||||
history[ptr] = history[ptr][0:x-4] + history[ptr][x-3:]
|
||||
stdscr.addch("\r")
|
||||
stdscr.clrtoeol()
|
||||
stdscr.addstr(">> " + history[ptr])
|
||||
stdscr.move(y, x-1)
|
||||
stdscr.refresh()
|
||||
|
||||
else:
|
||||
(y, x) = stdscr.getyx()
|
||||
history[ptr] = history[ptr][0:x-3] + chr(c) + history[ptr][x-3:]
|
||||
stdscr.addch("\r")
|
||||
stdscr.clrtoeol()
|
||||
stdscr.addstr(">> " + history[ptr])
|
||||
stdscr.move(y, x+1)
|
||||
stdscr.refresh()
|
||||
|
||||
finally:
|
||||
|
||||
# to quit
|
||||
curses.nocbreak()
|
||||
stdscr.keypad(False)
|
||||
curses.echo()
|
||||
curses.endwin()
|
||||
|
||||
except:
|
||||
print("""
|
||||
Invalid Arguments
|
||||
|
||||
Option 1: Single Command
|
||||
------------------------
|
||||
|
||||
Usage: writeRead.py pmachost:port command
|
||||
This then returns the response for command.
|
||||
|
||||
Option 2: CLI Mode
|
||||
------------------
|
||||
|
||||
Usage: writeRead.py pmachost:port
|
||||
|
||||
You can then type in a command, hit enter, and the response will see
|
||||
the reponse, before being prompted to again enter a command. Type
|
||||
'quit' to close prompt.
|
||||
""")
|
||||
|
Reference in New Issue
Block a user