21 Commits
0.4.0 ... v1.0

Author SHA1 Message Date
847f40970f Bugfix for 1.0 2025-06-17 16:32:59 +02:00
deb6e6996e Ready for release 1.0 2025-05-23 12:29:30 +02:00
d1d694ad6b Applied PIMPL principle 2025-05-23 12:16:25 +02:00
e0a74c5598 Changed sinqMotor version to 0.15.2 2025-05-16 16:15:01 +02:00
c334ed9f04 Add default value for motorMessageText 2025-05-15 12:22:33 +02:00
4b0031c3af Fixed bug with readInt32 function 2025-05-15 12:03:06 +02:00
61335970ce Use latest version of sinqMotor 2025-05-15 11:43:40 +02:00
081a21073b Adjusted Makefile for static linking 2025-05-15 11:34:43 +02:00
f2e8eb2762 Fixed serious bug in sinqMotor 2025-05-15 11:32:35 +02:00
e93f11e779 Adjusted usage of motorMessageText to be an error text only 2025-05-14 16:28:51 +02:00
989410474e Added sinqMotor 0.15.0 as static dependency 2025-05-14 16:20:08 +02:00
8d8561d833 Removed sinqMotor from required
sinqMotor is statically compiled into this driver.
2025-05-14 12:20:15 +02:00
a56a8cf646 Added explanation why the return status of doPoll is not used in the enable function. 2025-05-13 14:46:13 +02:00
cd57409f3c Added motorConnected logic 2025-04-25 15:58:03 +02:00
21ffcba8be Back to dev version 2025-04-22 15:07:45 +02:00
3bfc2414b6 Fixed sinqMotor version for tagging 2025-04-22 15:05:16 +02:00
60053244a1 Fixed moving after enable bug
When a MasterMacs motor is powered for the first time, it does not have
a target set. Therefore, the targetReached bit is 0, which the driver
used to interpret as "moving". This is solved now by an additional flag
which checks if the motor did a handshake.

Additionally, the communication module was simplified and new utility
scripts were added. It is now made sure that the communication timeout
for enabling and sending move commands is now at least equal to
PowerCycleTimeout defined in src/masterMacsAxis.cpp.
2025-04-17 16:50:42 +02:00
699b588ba5 Replaced ipPortUser_ with ipPortAsynOctetSyncIO_
See comment to sinqMotor 0.12.0
2025-04-15 17:22:15 +02:00
e86c517fc7 Bumped required sinqMotor version to 0.11.0 2025-04-10 09:09:27 +02:00
f733718ee7 Using appropriate sinqMotor version 2025-04-09 15:26:45 +02:00
a8c3499dc5 Set required sinqMotor version to mathis_s 2025-04-04 13:31:49 +02:00
9 changed files with 653 additions and 258 deletions

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "sinqMotor"]
path = sinqMotor
url = https://gitea.psi.ch/lin-epics-modules/sinqMotor

View File

@ -6,24 +6,26 @@ BUILDCLASSES=Linux
EPICS_VERSIONS=7.0.7 EPICS_VERSIONS=7.0.7
ARCH_FILTER=RHEL% ARCH_FILTER=RHEL%
# Additional module dependencies
REQUIRED+=sinqMotor
# Specify the version of asynMotor we want to build against # Specify the version of asynMotor we want to build against
motorBase_VERSION=7.2.2 motorBase_VERSION=7.2.2
# Specify the version of sinqMotor we want to build against
sinqMotor_VERSION=0.10.0
# These headers allow to depend on this library for derived drivers. # These headers allow to depend on this library for derived drivers.
HEADERS += src/masterMacsAxis.h HEADERS += src/masterMacsAxis.h
HEADERS += src/masterMacsController.h HEADERS += src/masterMacsController.h
# Source files to build # 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/masterMacsAxis.cpp
SOURCES += src/masterMacsController.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. # This file registers the motor-specific functions in the IOC shell.
DBDS += sinqMotor/src/sinqMotor.dbd
DBDS += src/masterMacs.dbd DBDS += src/masterMacs.dbd
USR_CFLAGS += -Wall -Wextra -Weffc++ -Wunused-result -Wpedantic -Wextra -Werror USR_CFLAGS += -Wall -Wextra -Weffc++ -Wunused-result -Wextra -Werror

View File

@ -14,6 +14,8 @@ This driver is a standard sinqMotor-derived driver and does not need any specifi
The folder "utils" contains utility scripts for working with masterMacs motor controllers: 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. - 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 ## Developer guide

1
sinqMotor Submodule

Submodule sinqMotor added at c2eca33ce8

View File

@ -11,6 +11,29 @@
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
struct masterMacsAxisImpl {
/*
The axis status and axis error of MasterMACS are given as an integer from
the controller. The 16 individual bits contain the actual information.
*/
std::bitset<16> axisStatus;
std::bitset<16> axisError;
double lastSetSpeed;
bool waitForHandshake;
time_t timeAtHandshake;
bool needInit = true;
bool targetReachedUninitialized;
};
/*
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 Contains all instances of turboPmacAxis which have been created and is used in
the initialization hook function. the initialization hook function.
@ -79,7 +102,7 @@ masterMacsAxis::masterMacsAxis(masterMacsController *pC, int axisNo)
stop completely, since this is a configuration error. stop completely, since this is a configuration error.
*/ */
if (axisNo >= pC->numAxes()) { if (axisNo >= pC->numAxes()) {
asynPrint(pC_->asynUserSelf(), ASYN_TRACE_ERROR, asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d:: FATAL ERROR: " "Controller \"%s\", axis %d => %s, line %d:: FATAL ERROR: "
"Axis index %d must be smaller than the total number of axes " "Axis index %d must be smaller than the total number of axes "
"%d. Call the support.", "%d. Call the support.",
@ -96,22 +119,20 @@ masterMacsAxis::masterMacsAxis(masterMacsController *pC, int axisNo)
// Collect all axes into this list which will be used in the hook function // Collect all axes into this list which will be used in the hook function
axes.push_back(this); axes.push_back(this);
// Initialize all member variables pMasterMacsA_ = std::make_unique<masterMacsAxisImpl>((masterMacsAxisImpl){
axisStatus_ = std::bitset<16>(0); .axisStatus = std::bitset<16>(0),
axisError_ = std::bitset<16>(0); .axisError = std::bitset<16>(0),
.lastSetSpeed = 0.0,
// Initial value for the motor speed, is overwritten in atFirstPoll. .waitForHandshake = false,
lastSetSpeed_ = 0.0; .timeAtHandshake = 0,
.targetReachedUninitialized = true,
// Flag for handshake waiting });
waitForHandshake_ = false;
timeAtHandshake_ = 0;
// masterMacs motors can always be disabled // masterMacs motors can always be disabled
status = pC_->setIntegerParam(axisNo_, pC_->motorCanDisable(), 1); status = pC_->setIntegerParam(axisNo_, pC_->motorCanDisable(), 1);
if (status != asynSuccess) { if (status != asynSuccess) {
asynPrint( asynPrint(
pC_->asynUserSelf(), ASYN_TRACE_ERROR, pC_->pasynUser(), ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d:\nFATAL ERROR " "Controller \"%s\", axis %d => %s, line %d:\nFATAL ERROR "
"(setting a parameter value failed with %s)\n. Terminating IOC", "(setting a parameter value failed with %s)\n. Terminating IOC",
pC_->portName, axisNo, __PRETTY_FUNCTION__, __LINE__, pC_->portName, axisNo, __PRETTY_FUNCTION__, __LINE__,
@ -123,12 +144,26 @@ masterMacsAxis::masterMacsAxis(masterMacsController *pC, int axisNo)
status = pC_->setIntegerParam(axisNo_, pC_->motorStatusMoving(), false); status = pC_->setIntegerParam(axisNo_, pC_->motorStatusMoving(), false);
if (status != asynSuccess) { if (status != asynSuccess) {
asynPrint( asynPrint(
pC_->asynUserSelf(), ASYN_TRACE_ERROR, pC_->pasynUser(), ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d:\nFATAL ERROR " "Controller \"%s\", axis %d => %s, line %d:\nFATAL ERROR "
"(setting a parameter value failed with %s)\n. Terminating IOC", "(setting a parameter value failed with %s)\n. Terminating IOC",
pC_->portName, axisNo, __PRETTY_FUNCTION__, __LINE__, pC_->portName, axisNo, __PRETTY_FUNCTION__, __LINE__,
pC_->stringifyAsynStatus(status)); pC_->stringifyAsynStatus(status));
} }
// Even though this happens already in sinqAxis, a default value for
// motorMessageText is set here again, because apparently the sinqAxis
// constructor is not run before the string is accessed?
status = setStringParam(pC_->motorMessageText(), "");
if (status != asynSuccess) {
asynPrint(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__,
pC_->stringifyAsynStatus(status));
exit(-1);
}
} }
masterMacsAxis::~masterMacsAxis(void) { masterMacsAxis::~masterMacsAxis(void) {
@ -148,7 +183,7 @@ asynStatus masterMacsAxis::init() {
// Local variable declaration // Local variable declaration
asynStatus pl_status = asynSuccess; asynStatus pl_status = asynSuccess;
char response[pC_->MAXBUF_]; char response[pC_->MAXBUF_] = {0};
int nvals = 0; int nvals = 0;
double motorRecResolution = 0.0; double motorRecResolution = 0.0;
double motorPosition = 0.0; double motorPosition = 0.0;
@ -167,7 +202,7 @@ asynStatus masterMacsAxis::init() {
&motorRecResolution); &motorRecResolution);
if (pl_status == asynParamUndefined) { if (pl_status == asynParamUndefined) {
if (now + maxInitTime < time(NULL)) { if (now + maxInitTime < time(NULL)) {
asynPrint(pC_->asynUserSelf(), ASYN_TRACE_ERROR, asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line " "Controller \"%s\", axis %d => %s, line "
"%d\nInitializing the parameter library failed.\n", "%d\nInitializing the parameter library failed.\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, pC_->portName, axisNo_, __PRETTY_FUNCTION__,
@ -230,7 +265,7 @@ asynStatus masterMacsAxis::init() {
// Cache the motor speed. If this value differs from the one in the motor // 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 // record at the start of a movement, the motor record value is sent to
// MasterMACS. // MasterMACS.
lastSetSpeed_ = motorVelocity; pMasterMacsA_->lastSetSpeed = motorVelocity;
// Store the motor position in the parameter library // Store the motor position in the parameter library
pl_status = setMotorPosition(motorPosition); pl_status = setMotorPosition(motorPosition);
@ -259,13 +294,17 @@ asynStatus masterMacsAxis::init() {
// If we can't communicate with the parameter library, it doesn't // 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 // make sense to try and upstream this to the user -> Just log the
// error // error
asynPrint(pC_->asynUserSelf(), ASYN_TRACE_ERROR, asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line " "Controller \"%s\", axis %d => %s, line "
"%d:\ncallParamCallbacks failed with %s.\n", "%d:\ncallParamCallbacks failed with %s.\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
pC_->stringifyAsynStatus(pl_status)); pC_->stringifyAsynStatus(pl_status));
return pl_status; return pl_status;
} }
// Axis is fully initialized
setNeedInit(false);
return pl_status; return pl_status;
} }
@ -281,7 +320,7 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
// Status of parameter library operations // Status of parameter library operations
asynStatus pl_status = asynSuccess; asynStatus pl_status = asynSuccess;
char response[pC_->MAXBUF_]; char response[pC_->MAXBUF_] = {0};
int nvals = 0; int nvals = 0;
int direction = 0; int direction = 0;
@ -296,23 +335,31 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
// ========================================================================= // =========================================================================
// Does the axis need to be intialized?
if (needInit()) {
rw_status = init();
if (rw_status != asynSuccess) {
return rw_status;
}
}
// Are we currently waiting for a handshake? // Are we currently waiting for a handshake?
if (waitForHandshake_) { if (pMasterMacsA_->waitForHandshake) {
// Check if the handshake takes too long and communicate an error in // Check if the handshake takes too long and communicate an error in
// this case. A handshake should not take more than 5 seconds. // this case. A handshake should not take more than 5 seconds.
time_t currentTime = time(NULL); time_t currentTime = time(NULL);
bool timedOut = (currentTime > timeAtHandshake_ + 5); bool timedOut = (currentTime > pMasterMacsA_->timeAtHandshake + 5);
if (pC_->getMsgPrintControl().shouldBePrinted( if (pC_->getMsgPrintControl().shouldBePrinted(
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, timedOut, pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, timedOut,
pC_->asynUserSelf())) { pC_->pasynUser())) {
asynPrint(pC_->asynUserSelf(), ASYN_TRACE_ERROR, asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d\nAsked for a " "Controller \"%s\", axis %d => %s, line %d\nAsked for a "
"handshake at %ld s and didn't get a positive reply yet " "handshake at %ld s and didn't get a positive reply yet "
"(current time is %ld s).\n", "(current time is %ld s).\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
timeAtHandshake_, currentTime); pMasterMacsA_->timeAtHandshake, currentTime);
} }
if (timedOut) { if (timedOut) {
@ -340,7 +387,8 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
if (handshakePerformed == 1.0) { if (handshakePerformed == 1.0) {
// Handshake has been performed successfully -> Continue with the // Handshake has been performed successfully -> Continue with the
// poll // poll
waitForHandshake_ = false; pMasterMacsA_->waitForHandshake = false;
pMasterMacsA_->targetReachedUninitialized = false;
} else { } else {
// Still waiting for the handshake - try again in the next busy // Still waiting for the handshake - try again in the next busy
// poll. This is already part of the movement procedure. // poll. This is already part of the movement procedure.
@ -385,12 +433,18 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
return rw_status; return rw_status;
} }
// If the motor is switched off or if it reached its target, it is not if (pMasterMacsA_->targetReachedUninitialized) {
// moving.
if (targetReached() || !switchedOn()) {
*moving = false; *moving = false;
} else { } else {
*moving = true; if (targetReached() || !switchedOn()) {
*moving = false;
} else {
*moving = true;
}
}
if (targetReached()) {
pMasterMacsA_->targetReachedUninitialized = false;
} }
// Read the current position // Read the current position
@ -420,9 +474,9 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
since this information is not reliable. since this information is not reliable.
*/ */
if (communicationError()) { if (communicationError()) {
if (pC_->getMsgPrintControl().shouldBePrinted( if (pC_->getMsgPrintControl().shouldBePrinted(keyError, true,
keyError, true, pC_->asynUserSelf())) { pC_->pasynUser())) {
asynPrint(pC_->asynUserSelf(), ASYN_TRACE_ERROR, asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line " "Controller \"%s\", axis %d => %s, line "
"%d\nCommunication error.%s\n", "%d\nCommunication error.%s\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
@ -444,7 +498,7 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
// This buffer must be initialized to zero because we build the // This buffer must be initialized to zero because we build the
// error message by appending strings. // error message by appending strings.
char userMessage[pC_->MAXBUF_] = {0}; char errorMessage[pC_->MAXBUF_] = {0};
char shellMessage[pC_->MAXBUF_] = {0}; char shellMessage[pC_->MAXBUF_] = {0};
// Concatenate all other errors // Concatenate all other errors
@ -452,7 +506,7 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
appendErrorMessage(shellMessage, sizeof(shellMessage), appendErrorMessage(shellMessage, sizeof(shellMessage),
"Short circuit fault."); "Short circuit fault.");
appendErrorMessage( appendErrorMessage(
userMessage, sizeof(userMessage), errorMessage, sizeof(errorMessage),
"Short circuit error. Please call the support."); "Short circuit error. Please call the support.");
poll_status = asynError; poll_status = asynError;
@ -461,7 +515,7 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
if (encoderError()) { if (encoderError()) {
appendErrorMessage(shellMessage, sizeof(shellMessage), appendErrorMessage(shellMessage, sizeof(shellMessage),
"Encoder error."); "Encoder error.");
appendErrorMessage(userMessage, sizeof(userMessage), appendErrorMessage(errorMessage, sizeof(errorMessage),
"Encoder error. Please call the support."); "Encoder error. Please call the support.");
poll_status = asynError; poll_status = asynError;
@ -472,7 +526,7 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
shellMessage, sizeof(shellMessage), shellMessage, sizeof(shellMessage),
"Maximum callowed following error exceeded."); "Maximum callowed following error exceeded.");
appendErrorMessage( appendErrorMessage(
userMessage, sizeof(userMessage), errorMessage, sizeof(errorMessage),
"Maximum allowed following error exceeded.Check if " "Maximum allowed following error exceeded.Check if "
"movement range is blocked. Otherwise please call the " "movement range is blocked. Otherwise please call the "
"support."); "support.");
@ -483,7 +537,7 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
if (feedbackError()) { if (feedbackError()) {
appendErrorMessage(shellMessage, sizeof(shellMessage), appendErrorMessage(shellMessage, sizeof(shellMessage),
"Feedback error."); "Feedback error.");
appendErrorMessage(userMessage, sizeof(userMessage), appendErrorMessage(errorMessage, sizeof(errorMessage),
"Feedback error. Please call the support."); "Feedback error. Please call the support.");
poll_status = asynError; poll_status = asynError;
@ -519,7 +573,7 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
// Generic error message for user // Generic error message for user
appendErrorMessage( appendErrorMessage(
userMessage, sizeof(userMessage), errorMessage, sizeof(errorMessage),
"Software limits or end switch hit. Try homing the motor, " "Software limits or end switch hit. Try homing the motor, "
"moving in the opposite direction or check the SPS for " "moving in the opposite direction or check the SPS for "
"errors (if available). Otherwise please call the " "errors (if available). Otherwise please call the "
@ -532,7 +586,7 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
appendErrorMessage(shellMessage, sizeof(shellMessage), appendErrorMessage(shellMessage, sizeof(shellMessage),
"Overcurrent error."); "Overcurrent error.");
appendErrorMessage( appendErrorMessage(
userMessage, sizeof(userMessage), errorMessage, sizeof(errorMessage),
"Overcurrent error. Please call the support."); "Overcurrent error. Please call the support.");
poll_status = asynError; poll_status = asynError;
@ -542,7 +596,7 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
appendErrorMessage(shellMessage, sizeof(shellMessage), appendErrorMessage(shellMessage, sizeof(shellMessage),
"Overtemperature error."); "Overtemperature error.");
appendErrorMessage( appendErrorMessage(
userMessage, sizeof(userMessage), errorMessage, sizeof(errorMessage),
"Overtemperature error. Please call the support."); "Overtemperature error. Please call the support.");
poll_status = asynError; poll_status = asynError;
@ -552,7 +606,7 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
appendErrorMessage(shellMessage, sizeof(shellMessage), appendErrorMessage(shellMessage, sizeof(shellMessage),
"Overvoltage error."); "Overvoltage error.");
appendErrorMessage( appendErrorMessage(
userMessage, sizeof(userMessage), errorMessage, sizeof(errorMessage),
"Overvoltage error. Please call the support."); "Overvoltage error. Please call the support.");
poll_status = asynError; poll_status = asynError;
@ -562,7 +616,7 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
appendErrorMessage(shellMessage, sizeof(shellMessage), appendErrorMessage(shellMessage, sizeof(shellMessage),
"Undervoltage error."); "Undervoltage error.");
appendErrorMessage( appendErrorMessage(
userMessage, sizeof(userMessage), errorMessage, sizeof(errorMessage),
"Undervoltage error. Please call the support."); "Undervoltage error. Please call the support.");
poll_status = asynError; poll_status = asynError;
@ -571,7 +625,7 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
if (stoFault()) { if (stoFault()) {
appendErrorMessage(shellMessage, sizeof(shellMessage), appendErrorMessage(shellMessage, sizeof(shellMessage),
"STO input is on disable state."); "STO input is on disable state.");
appendErrorMessage(userMessage, sizeof(userMessage), appendErrorMessage(errorMessage, sizeof(errorMessage),
"STO fault. Please call the support."); "STO fault. Please call the support.");
poll_status = asynError; poll_status = asynError;
@ -579,8 +633,8 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
if (strlen(shellMessage) > 0) { if (strlen(shellMessage) > 0) {
if (pC_->getMsgPrintControl().shouldBePrinted( if (pC_->getMsgPrintControl().shouldBePrinted(
keyError, true, pC_->asynUserSelf())) { keyError, true, pC_->pasynUser())) {
asynPrint(pC_->asynUserSelf(), ASYN_TRACE_ERROR, asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line " "Controller \"%s\", axis %d => %s, line "
"%d\n%s%s\n", "%d\n%s%s\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, pC_->portName, axisNo_, __PRETTY_FUNCTION__,
@ -589,7 +643,7 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
} }
} }
pl_status = setStringParam(pC_->motorMessageText(), userMessage); pl_status = setStringParam(pC_->motorMessageText(), errorMessage);
if (pl_status != asynSuccess) { if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorMessageText_", return pC_->paramLibAccessFailed(pl_status, "motorMessageText_",
axisNo_, __PRETTY_FUNCTION__, axisNo_, __PRETTY_FUNCTION__,
@ -597,7 +651,7 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
} }
} }
} else { } else {
pC_->getMsgPrintControl().resetCount(keyError, pC_->asynUserSelf()); pC_->getMsgPrintControl().resetCount(keyError, pC_->pasynUser());
} }
// Read out the limits, if the motor is not moving // Read out the limits, if the motor is not moving
@ -750,7 +804,7 @@ asynStatus masterMacsAxis::doMove(double position, int relative,
} }
if (enabled == 0) { if (enabled == 0) {
asynPrint(pC_->asynUserSelf(), ASYN_TRACE_ERROR, asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d:\nAxis is " "Controller \"%s\", axis %d => %s, line %d:\nAxis is "
"disabled.\n", "disabled.\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__); pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__);
@ -762,7 +816,7 @@ asynStatus masterMacsAxis::doMove(double position, int relative,
motorVelocity = maxVelocity * motorRecResolution; motorVelocity = maxVelocity * motorRecResolution;
asynPrint( asynPrint(
pC_->asynUserSelf(), ASYN_TRACE_FLOW, pC_->pasynUser(), ASYN_TRACE_FLOW,
"Controller \"%s\", axis %d => %s, line %d:\nMove to position %lf.\n", "Controller \"%s\", axis %d => %s, line %d:\nMove to position %lf.\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, position); pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, position);
@ -789,9 +843,9 @@ asynStatus masterMacsAxis::doMove(double position, int relative,
// Set the new motor speed, if the user is allowed to do so and if the // Set the new motor speed, if the user is allowed to do so and if the
// motor speed changed since the last move command. // motor speed changed since the last move command.
if (motorCanSetSpeed != 0 && lastSetSpeed_ != motorVelocity) { if (motorCanSetSpeed != 0 && pMasterMacsA_->lastSetSpeed != motorVelocity) {
lastSetSpeed_ = motorVelocity; pMasterMacsA_->lastSetSpeed = motorVelocity;
snprintf(value, sizeof(value), "%lf", motorVelocity); snprintf(value, sizeof(value), "%lf", motorVelocity);
rw_status = pC_->write(axisNo_, 05, value); rw_status = pC_->write(axisNo_, 05, value);
@ -805,7 +859,7 @@ asynStatus masterMacsAxis::doMove(double position, int relative,
return rw_status; return rw_status;
} }
asynPrint(pC_->asynUserSelf(), ASYN_TRACE_FLOW, asynPrint(pC_->pasynUser(), ASYN_TRACE_FLOW,
"Controller \"%s\", axis %d => %s, line %d:\nSetting speed " "Controller \"%s\", axis %d => %s, line %d:\nSetting speed "
"to %lf.\n", "to %lf.\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
@ -825,11 +879,18 @@ asynStatus masterMacsAxis::doMove(double position, int relative,
return rw_status; return rw_status;
} }
// If the motor has just been enabled, use Enable
double timeout = pC_->comTimeout();
if (pMasterMacsA_->targetReachedUninitialized &&
timeout < PowerCycleTimeout) {
timeout = PowerCycleTimeout;
}
// Start the move // Start the move
if (relative) { if (relative) {
rw_status = pC_->write(axisNo_, 00, "2"); rw_status = pC_->write(axisNo_, 00, "2", timeout);
} else { } else {
rw_status = pC_->write(axisNo_, 00, "1"); rw_status = pC_->write(axisNo_, 00, "1", timeout);
} }
if (rw_status != asynSuccess) { if (rw_status != asynSuccess) {
pl_status = setIntegerParam(pC_->motorStatusProblem(), true); pl_status = setIntegerParam(pC_->motorStatusProblem(), true);
@ -843,8 +904,8 @@ asynStatus masterMacsAxis::doMove(double position, int relative,
// In the next poll, we will check if the handshake has been performed in a // In the next poll, we will check if the handshake has been performed in a
// reasonable time // reasonable time
waitForHandshake_ = true; pMasterMacsA_->waitForHandshake = true;
timeAtHandshake_ = time(NULL); pMasterMacsA_->timeAtHandshake = time(NULL);
// Waiting for a handshake is already part of the movement procedure => // Waiting for a handshake is already part of the movement procedure =>
// Start the watchdog // Start the watchdog
@ -875,6 +936,10 @@ asynStatus masterMacsAxis::stop(double acceleration) {
} }
} }
// 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 rw_status;
} }
@ -897,6 +962,10 @@ asynStatus masterMacsAxis::doReset() {
} }
} }
// 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 rw_status;
} }
@ -912,7 +981,7 @@ asynStatus masterMacsAxis::doHome(double min_velocity, double max_velocity,
// Status of parameter library operations // Status of parameter library operations
asynStatus pl_status = asynSuccess; asynStatus pl_status = asynSuccess;
char response[pC_->MAXBUF_]; char response[pC_->MAXBUF_] = {0};
// ========================================================================= // =========================================================================
@ -945,8 +1014,8 @@ asynStatus masterMacsAxis::doHome(double min_velocity, double max_velocity,
// In the next poll, we will check if the handshake has been performed // In the next poll, we will check if the handshake has been performed
// in a reasonable time // in a reasonable time
waitForHandshake_ = true; pMasterMacsA_->waitForHandshake = true;
timeAtHandshake_ = time(NULL); pMasterMacsA_->timeAtHandshake = time(NULL);
return asynSuccess; return asynSuccess;
} else { } else {
return asynError; return asynError;
@ -964,7 +1033,8 @@ asynStatus masterMacsAxis::readEncoderType() {
// Status of parameter library operations // Status of parameter library operations
asynStatus pl_status = asynSuccess; 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 nvals = 0;
int encoder_id = 0; int encoder_id = 0;
@ -1012,10 +1082,22 @@ asynStatus masterMacsAxis::enable(bool on) {
// Status of parameter library operations // Status of parameter library operations
asynStatus pl_status = asynSuccess; 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.
*/
pMasterMacsA_->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); doPoll(&moving);
// If the axis is currently moving, it cannot be disabled. Ignore the // If the axis is currently moving, it cannot be disabled. Ignore the
@ -1023,7 +1105,7 @@ asynStatus masterMacsAxis::enable(bool on) {
// axis instead of "moving", since status -6 is also moving, but the // axis instead of "moving", since status -6 is also moving, but the
// motor can actually be disabled in this state! // motor can actually be disabled in this state!
if (moving) { if (moving) {
asynPrint(pC_->asynUserSelf(), ASYN_TRACE_ERROR, asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d:\nAxis is not " "Controller \"%s\", axis %d => %s, line %d:\nAxis is not "
"idle and can therefore not be disabled.\n", "idle and can therefore not be disabled.\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__); pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__);
@ -1045,7 +1127,7 @@ asynStatus masterMacsAxis::enable(bool on) {
if ((readyToBeSwitchedOn() && switchedOn()) == on) { if ((readyToBeSwitchedOn() && switchedOn()) == on) {
asynPrint( asynPrint(
pC_->asynUserSelf(), ASYN_TRACE_WARNING, pC_->pasynUser(), ASYN_TRACE_WARNING,
"Controller \"%s\", axis %d => %s, line %d:\nAxis is already %s.\n", "Controller \"%s\", axis %d => %s, line %d:\nAxis is already %s.\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
on ? "enabled" : "disabled"); on ? "enabled" : "disabled");
@ -1054,25 +1136,21 @@ asynStatus masterMacsAxis::enable(bool on) {
// Enable / disable the axis if it is not moving // Enable / disable the axis if it is not moving
snprintf(value, sizeof(value), "%d", on); snprintf(value, sizeof(value), "%d", on);
asynPrint(pC_->asynUserSelf(), ASYN_TRACE_FLOW, asynPrint(pC_->pasynUser(), ASYN_TRACE_FLOW,
"Controller \"%s\", axis %d => %s, line %d:\n%s axis.\n", "Controller \"%s\", axis %d => %s, line %d:\n%s axis.\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
on ? "Enable" : "Disable"); on ? "Enable" : "Disable");
if (on == 0) {
pl_status = setStringParam(pC_->motorMessageText(), "Disabling ...");
} else {
pl_status = setStringParam(pC_->motorMessageText(), "Enabling ...");
}
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorMessageText_",
axisNo_, __PRETTY_FUNCTION__,
__LINE__);
}
// The answer to the enable command on MasterMACS might take some time, // The answer to the enable command on MasterMACS might take some time,
// hence we wait for a custom timespan in seconds instead of // hence we wait for a custom timespan in seconds instead of
// pC_->comTimeout_ // pC_->comTimeout_
rw_status = pC_->write(axisNo_, 04, value, 1.0); double timeout = pC_->comTimeout();
if (pMasterMacsA_->targetReachedUninitialized &&
timeout < PowerCycleTimeout) {
timeout = PowerCycleTimeout;
}
rw_status = pC_->write(axisNo_, 04, value, timeout);
if (rw_status != asynSuccess) { if (rw_status != asynSuccess) {
return rw_status; return rw_status;
} }
@ -1099,7 +1177,7 @@ asynStatus masterMacsAxis::enable(bool on) {
// Failed to change axis status within timeout_enable_disable => Send a // Failed to change axis status within timeout_enable_disable => Send a
// corresponding message // corresponding message
asynPrint(pC_->asynUserSelf(), ASYN_TRACE_ERROR, asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d:\nFailed to %s axis " "Controller \"%s\", axis %d => %s, line %d:\nFailed to %s axis "
"within %d seconds\n", "within %d seconds\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
@ -1108,7 +1186,7 @@ asynStatus masterMacsAxis::enable(bool on) {
// Output message to user // Output message to user
snprintf(value, sizeof(value), "Failed to %s within %d seconds", snprintf(value, sizeof(value), "Failed to %s within %d seconds",
on ? "enable" : "disable", timeout_enable_disable); on ? "enable" : "disable", timeout_enable_disable);
pl_status = setStringParam(pC_->motorMessageText(), "Enabling ..."); pl_status = setStringParam(pC_->motorMessageText(), value);
if (pl_status != asynSuccess) { if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorMessageText_", return pC_->paramLibAccessFailed(pl_status, "motorMessageText_",
axisNo_, __PRETTY_FUNCTION__, axisNo_, __PRETTY_FUNCTION__,
@ -1117,6 +1195,17 @@ asynStatus masterMacsAxis::enable(bool on) {
return asynError; return asynError;
} }
bool masterMacsAxis::needInit() { return pMasterMacsA_->needInit; }
/**
* @brief Instruct the axis to run its init() function during the next poll
*
* @param needInit
*/
void masterMacsAxis::setNeedInit(bool needInit) {
pMasterMacsA_->needInit = needInit;
}
/** /**
Convert a float to an unint16_t bitset Convert a float to an unint16_t bitset
*/ */
@ -1132,7 +1221,7 @@ std::bitset<16> toBitset(float val) {
} }
asynStatus masterMacsAxis::readAxisStatus() { asynStatus masterMacsAxis::readAxisStatus() {
char response[pC_->MAXBUF_]; char response[pC_->MAXBUF_] = {0};
// ========================================================================= // =========================================================================
@ -1146,14 +1235,14 @@ asynStatus masterMacsAxis::readAxisStatus() {
__PRETTY_FUNCTION__, __LINE__); __PRETTY_FUNCTION__, __LINE__);
} }
axisStatus_ = toBitset(axisStatus); pMasterMacsA_->axisStatus = toBitset(axisStatus);
} }
return rw_status; return rw_status;
} }
asynStatus masterMacsAxis::readAxisError() { asynStatus masterMacsAxis::readAxisError() {
char response[pC_->MAXBUF_]; char response[pC_->MAXBUF_] = {0};
// ========================================================================= // =========================================================================
@ -1166,11 +1255,83 @@ asynStatus masterMacsAxis::readAxisError() {
return pC_->couldNotParseResponse("R11", response, axisNo_, return pC_->couldNotParseResponse("R11", response, axisNo_,
__PRETTY_FUNCTION__, __LINE__); __PRETTY_FUNCTION__, __LINE__);
} }
axisError_ = toBitset(axisError); pMasterMacsA_->axisError = toBitset(axisError);
} }
return rw_status; return rw_status;
} }
bool masterMacsAxis::readyToBeSwitchedOn() {
return pMasterMacsA_->axisStatus[0];
}
bool masterMacsAxis::switchedOn() { return pMasterMacsA_->axisStatus[1]; }
bool masterMacsAxis::faultConditionSet() {
return pMasterMacsA_->axisStatus[3];
}
bool masterMacsAxis::voltagePresent() { return pMasterMacsA_->axisStatus[4]; }
bool masterMacsAxis::quickStopping() {
return pMasterMacsA_->axisStatus[5] == 0;
}
bool masterMacsAxis::switchOnDisabled() { return pMasterMacsA_->axisStatus[6]; }
bool masterMacsAxis::warning() { return pMasterMacsA_->axisStatus[7]; }
bool masterMacsAxis::remoteMode() { return pMasterMacsA_->axisStatus[9]; }
bool masterMacsAxis::targetReached() { return pMasterMacsA_->axisStatus[10]; }
bool masterMacsAxis::internalLimitActive() {
return pMasterMacsA_->axisStatus[11];
}
bool masterMacsAxis::setEventHasOcurred() {
return pMasterMacsA_->axisStatus[14];
}
bool masterMacsAxis::powerEnabled() { return pMasterMacsA_->axisStatus[15]; }
bool masterMacsAxis::shortCircuit() { return pMasterMacsA_->axisError[1]; }
bool masterMacsAxis::encoderError() { return pMasterMacsA_->axisError[2]; }
bool masterMacsAxis::followingError() { return pMasterMacsA_->axisError[3]; }
bool masterMacsAxis::communicationError() {
return pMasterMacsA_->axisError[4];
}
bool masterMacsAxis::feedbackError() { return pMasterMacsA_->axisError[5]; }
bool masterMacsAxis::positiveLimitSwitch() {
return pMasterMacsA_->axisError[6];
}
bool masterMacsAxis::negativeLimitSwitch() {
return pMasterMacsA_->axisError[7];
}
bool masterMacsAxis::positiveSoftwareLimit() {
return pMasterMacsA_->axisError[8];
}
bool masterMacsAxis::negativeSoftwareLimit() {
return pMasterMacsA_->axisError[9];
}
bool masterMacsAxis::overCurrent() { return pMasterMacsA_->axisError[10]; }
bool masterMacsAxis::overTemperature() { return pMasterMacsA_->axisError[11]; }
bool masterMacsAxis::overVoltage() { return pMasterMacsA_->axisError[12]; }
bool masterMacsAxis::underVoltage() { return pMasterMacsA_->axisError[13]; }
bool masterMacsAxis::stoFault() { return pMasterMacsA_->axisError[15]; }
/***************************************************************************/ /***************************************************************************/
/** The following functions are C-wrappers, and can be called directly from /** The following functions are C-wrappers, and can be called directly from
* iocsh */ * iocsh */

View File

@ -1,13 +1,15 @@
#ifndef masterMacsAXIS_H #ifndef masterMacsAXIS_H
#define masterMacsAXIS_H #define masterMacsAXIS_H
#include "sinqAxis.h" #include "sinqAxis.h"
#include <bitset> #include <memory>
// Forward declaration of the controller class to resolve the cyclic dependency // Forward declaration of the controller class to resolve the cyclic dependency
// between the controller and the axis .h-file. See // between the controller and the axis .h-file. See
// https://en.cppreference.com/w/cpp/language/class. // https://en.cppreference.com/w/cpp/language/class.
class masterMacsController; class masterMacsController;
struct masterMacsAxisImpl;
class masterMacsAxis : public sinqAxis { class masterMacsAxis : public sinqAxis {
public: public:
/** /**
@ -105,21 +107,23 @@ class masterMacsAxis : public sinqAxis {
*/ */
asynStatus readEncoderType(); asynStatus readEncoderType();
protected: /**
masterMacsController *pC_; * @brief Check if the axis needs to run its initialization function
double lastSetSpeed_; *
bool waitForHandshake_; * @return true
time_t timeAtHandshake_; * @return false
*/
bool needInit();
/**
* @brief Instruct the axis to run its init() function during the next poll
*
* @param needInit
*/
void setNeedInit(bool needInit);
asynStatus readConfig(); asynStatus readConfig();
/*
The axis status and axis error of MasterMACS are given as an integer from
the controller. The 16 individual bits contain the actual information.
*/
std::bitset<16> axisStatus_;
std::bitset<16> axisError_;
/** /**
* @brief Read the Master MACS status with the xR10 command and store the * @brief Read the Master MACS status with the xR10 command and store the
* result in axisStatus_ * result in axisStatus_
@ -128,7 +132,7 @@ class masterMacsAxis : public sinqAxis {
asynStatus readAxisStatus(); asynStatus readAxisStatus();
/* /*
The functions below read the specified status bit from the axisStatus_ The functions below read the specified status bit from the axisStatus
bitset. Since a bit can either be 0 or 1, the return value is given as a bitset. Since a bit can either be 0 or 1, the return value is given as a
boolean. boolean.
*/ */
@ -136,68 +140,68 @@ class masterMacsAxis : public sinqAxis {
/** /**
* @brief Read the property from axisStatus_ * @brief Read the property from axisStatus_
*/ */
bool readyToBeSwitchedOn() { return axisStatus_[0]; } bool readyToBeSwitchedOn();
/** /**
* @brief Read the property from axisStatus_ * @brief Read the property from axisStatus_
*/ */
bool switchedOn() { return axisStatus_[1]; } bool switchedOn();
// Bit 2 is unused // Bit 2 is unused
/** /**
* @brief Read the property from axisStatus_ * @brief Read the property from axisStatus_
*/ */
bool faultConditionSet() { return axisStatus_[3]; } bool faultConditionSet();
/** /**
* @brief Read the property from axisStatus_ * @brief Read the property from axisStatus_
*/ */
bool voltagePresent() { return axisStatus_[4]; } bool voltagePresent();
/** /**
* @brief Read the property from axisStatus_ * @brief Read the property from axisStatus_
*/ */
bool quickStopping() { return axisStatus_[5] == 0; } bool quickStopping();
/** /**
* @brief Read the property from axisStatus_ * @brief Read the property from axisStatus_
*/ */
bool switchOnDisabled() { return axisStatus_[6]; } bool switchOnDisabled();
/** /**
* @brief Read the property from axisStatus_ * @brief Read the property from axisStatus_
*/ */
bool warning() { return axisStatus_[7]; } bool warning();
// Bit 8 is unused // Bit 8 is unused
/** /**
* @brief Read the property from axisStatus_ * @brief Read the property from axisStatus_
*/ */
bool remoteMode() { return axisStatus_[9]; } bool remoteMode();
/** /**
* @brief Read the property from axisStatus_ * @brief Read the property from axisStatus_
*/ */
bool targetReached() { return axisStatus_[10]; } bool targetReached();
/** /**
* @brief Read the property from axisStatus_ * @brief Read the property from axisStatus_
*/ */
bool internalLimitActive() { return axisStatus_[11]; } bool internalLimitActive();
// Bits 12 and 13 are unused // Bits 12 and 13 are unused
/** /**
* @brief Read the property from axisStatus_ * @brief Read the property from axisStatus_
*/ */
bool setEventHasOcurred() { return axisStatus_[14]; } bool setEventHasOcurred();
/** /**
* @brief Read the property from axisStatus_ * @brief Read the property from axisStatus_
*/ */
bool powerEnabled() { return axisStatus_[15]; } bool powerEnabled();
/** /**
* @brief Read the Master MACS status with the xR10 command and store the * @brief Read the Master MACS status with the xR10 command and store the
@ -215,72 +219,76 @@ class masterMacsAxis : public sinqAxis {
/** /**
* @brief Read the property from axisError_ * @brief Read the property from axisError_
*/ */
bool shortCircuit() { return axisError_[1]; } bool shortCircuit();
/** /**
* @brief Read the property from axisError_ * @brief Read the property from axisError_
*/ */
bool encoderError() { return axisError_[2]; } bool encoderError();
/** /**
* @brief Read the property from axisError_ * @brief Read the property from axisError_
*/ */
bool followingError() { return axisError_[3]; } bool followingError();
/** /**
* @brief Read the property from axisError_ * @brief Read the property from axisError_
*/ */
bool communicationError() { return axisError_[4]; } bool communicationError();
/** /**
* @brief Read the property from axisError_ * @brief Read the property from axisError_
*/ */
bool feedbackError() { return axisError_[5]; } bool feedbackError();
/** /**
* @brief Read the property from axisError_ * @brief Read the property from axisError_
*/ */
bool positiveLimitSwitch() { return axisError_[6]; } bool positiveLimitSwitch();
/** /**
* @brief Read the property from axisError_ * @brief Read the property from axisError_
*/ */
bool negativeLimitSwitch() { return axisError_[7]; } bool negativeLimitSwitch();
/** /**
* @brief Read the property from axisError_ * @brief Read the property from axisError_
*/ */
bool positiveSoftwareLimit() { return axisError_[8]; } bool positiveSoftwareLimit();
/** /**
* @brief Read the property from axisError_ * @brief Read the property from axisError_
*/ */
bool negativeSoftwareLimit() { return axisError_[9]; } bool negativeSoftwareLimit();
/** /**
* @brief Read the property from axisError_ * @brief Read the property from axisError_
*/ */
bool overCurrent() { return axisError_[10]; } bool overCurrent();
/** /**
* @brief Read the property from axisError_ * @brief Read the property from axisError_
*/ */
bool overTemperature() { return axisError_[11]; } bool overTemperature();
/** /**
* @brief Read the property from axisError_ * @brief Read the property from axisError_
*/ */
bool overVoltage() { return axisError_[12]; } bool overVoltage();
/** /**
* @brief Read the property from axisError_ * @brief Read the property from axisError_
*/ */
bool underVoltage() { return axisError_[13]; } bool underVoltage();
/** /**
* @brief Read the property from axisError_ * @brief Read the property from axisError_
*/ */
bool stoFault() { return axisError_[15]; } bool stoFault();
private:
masterMacsController *pC_;
std::unique_ptr<masterMacsAxisImpl> pMasterMacsA_;
}; };
#endif #endif

View File

@ -12,6 +12,10 @@
#include <string> #include <string>
#include <unistd.h> #include <unistd.h>
struct masterMacsControllerImpl {
double comTimeout;
};
/** /**
* @brief Copy src into dst and replace all NULL terminators up to the carriage * @brief Copy src into dst and replace all NULL terminators up to the carriage
* return with spaces. This allows to print *dst with asynPrint. * return with spaces. This allows to print *dst with asynPrint.
@ -59,8 +63,10 @@ masterMacsController::masterMacsController(const char *portName,
// Initialization of local variables // Initialization of local variables
asynStatus status = asynSuccess; asynStatus status = asynSuccess;
// Initialization of all member variables pMasterMacsC_ =
comTimeout_ = comTimeout; std::make_unique<masterMacsControllerImpl>((masterMacsControllerImpl){
.comTimeout = comTimeout,
});
// ========================================================================= // =========================================================================
@ -70,7 +76,8 @@ masterMacsController::masterMacsController(const char *portName,
the message length is encoded in the message header. the message length is encoded in the message header.
*/ */
const char *message_from_device = "\x0D"; // Hex-code for CR const char *message_from_device = "\x0D"; // Hex-code for CR
status = pasynOctetSyncIO->setInputEos(ipPortUser_, message_from_device, status = pasynOctetSyncIO->setInputEos(pasynOctetSyncIOipPort(),
message_from_device,
strlen(message_from_device)); strlen(message_from_device));
if (status != asynSuccess) { if (status != asynSuccess) {
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
@ -78,7 +85,7 @@ masterMacsController::masterMacsController(const char *portName,
"input EOS failed with %s).\nTerminating IOC", "input EOS failed with %s).\nTerminating IOC",
portName, __PRETTY_FUNCTION__, __LINE__, portName, __PRETTY_FUNCTION__, __LINE__,
stringifyAsynStatus(status)); stringifyAsynStatus(status));
pasynOctetSyncIO->disconnect(ipPortUser_); pasynOctetSyncIO->disconnect(pasynOctetSyncIOipPort());
exit(-1); exit(-1);
} }
@ -89,7 +96,7 @@ masterMacsController::masterMacsController(const char *portName,
"ParamLib callbacks failed with %s).\nTerminating IOC", "ParamLib callbacks failed with %s).\nTerminating IOC",
portName, __PRETTY_FUNCTION__, __LINE__, portName, __PRETTY_FUNCTION__, __LINE__,
stringifyAsynStatus(status)); stringifyAsynStatus(status));
pasynOctetSyncIO->disconnect(ipPortUser_); pasynOctetSyncIO->disconnect(pasynOctetSyncIOipPort());
exit(-1); exit(-1);
} }
} }
@ -132,8 +139,6 @@ asynStatus masterMacsController::writeRead(int axisNo, int tcpCmd,
asynStatus pl_status = asynSuccess; asynStatus pl_status = asynSuccess;
char fullCommand[MAXBUF_] = {0}; char fullCommand[MAXBUF_] = {0};
char fullResponse[MAXBUF_] = {0}; char fullResponse[MAXBUF_] = {0};
char printableCommand[MAXBUF_] = {0};
char printableResponse[MAXBUF_] = {0};
char drvMessageText[MAXBUF_] = {0}; char drvMessageText[MAXBUF_] = {0};
int motorStatusProblem = 0; int motorStatusProblem = 0;
@ -157,9 +162,9 @@ asynStatus masterMacsController::writeRead(int axisNo, int tcpCmd,
// ========================================================================= // =========================================================================
// Check if a timeout has been given // Check if a custom timeout has been given
if (comTimeout < 0.0) { if (comTimeout < 0.0) {
comTimeout = comTimeout_; comTimeout = pMasterMacsC_->comTimeout;
} }
masterMacsAxis *axis = getMasterMacsAxis(axisNo); masterMacsAxis *axis = getMasterMacsAxis(axisNo);
@ -168,6 +173,7 @@ asynStatus masterMacsController::writeRead(int axisNo, int tcpCmd,
return asynError; return asynError;
} }
// Build the full command depending on the inputs to this function
if (isRead) { if (isRead) {
snprintf(fullCommand, MAXBUF_ - 1, "%dR%02d\x0D", axisNo, tcpCmd); snprintf(fullCommand, MAXBUF_ - 1, "%dR%02d\x0D", axisNo, tcpCmd);
} else { } else {
@ -182,94 +188,39 @@ asynStatus masterMacsController::writeRead(int axisNo, int tcpCmd,
// Calculate the command length // Calculate the command length
const size_t fullCommandLength = strlen(fullCommand); const size_t fullCommandLength = strlen(fullCommand);
adjustForPrint(printableCommand, fullCommand, MAXBUF_); // 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);
// Send out the command // If a communication error occured, print this message to the
status = pasynOctetSyncIO->write(ipPortUser_, fullCommand, msgPrintControlKey comKey =
fullCommandLength, comTimeout, &nbytesOut);
if (status != asynSuccess) {
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d:\nError "
"%s while writing to the controller\n",
portName, axisNo, __PRETTY_FUNCTION__, __LINE__,
stringifyAsynStatus(status));
}
msgPrintControlKey writeKey =
msgPrintControlKey(portName, axisNo, __PRETTY_FUNCTION__, __LINE__); msgPrintControlKey(portName, axisNo, __PRETTY_FUNCTION__, __LINE__);
if (status != asynSuccess) {
if (status == asynSuccess) { if (getMsgPrintControl().shouldBePrinted(comKey, true, pasynUserSelf)) {
msgPrintControl_.resetCount(writeKey, pasynUserSelf); char printableCommand[MAXBUF_] = {0};
adjustForPrint(printableCommand, fullCommand, MAXBUF_);
// Try to read the answer repeatedly asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
int maxTrials = 2; "Controller \"%s\", axis %d => %s, line %d:\nError "
for (int i = 0; i < maxTrials; i++) { "%s while sending command %s to the controller\n",
portName, axisNo, __PRETTY_FUNCTION__, __LINE__,
/* stringifyAsynStatus(status), printableCommand);
A typical response of the MasterMacs controller looks like this:
(.. TCP Header ...) 31 20 52 20 31 31 3d 35 31 32 2e 30 30 30 30 06
0d 00 00 00 00 00 00 00 00 00 00 00 00 00
The message terminator is the carriage return (0d), which is
specified in the controller constructor as the end-of-string (EOS)
character. However, we also need to remove the buffer zeros at the
end, because they will otherwise confuse the
pasynOctetSyncIO->read() during the next call. The
pasynOctetSyncIO->flush() method does exactly that: it takes all
bytes it can find in the socket and throws them away. We don't check
the return value of flush(), because it is always asynSuccess (see
https://www.slac.stanford.edu/grp/lcls/controls/global/doc/epics-modules/R3-14-12/asyn/asyn-R4-18-lcls2/asyn/interfaces/asynOctetBase.c)
*/
status = pasynOctetSyncIO->read(ipPortUser_, fullResponse, MAXBUF_,
comTimeout, &nbytesIn, &eomReason);
pasynOctetSyncIO->flush(ipPortUser_);
if (status == asynSuccess) {
status = parseResponse(fullCommand, fullResponse,
drvMessageText, &valueStart, &valueStop,
axisNo, tcpCmd, isRead);
if (status == asynSuccess) {
// Received the correct message
break;
}
} else {
if (status != asynTimeout) {
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);
}
} }
} else { } else {
if (msgPrintControl_.shouldBePrinted(writeKey, true, pasynUserSelf)) { getMsgPrintControl().resetCount(comKey, 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());
}
} }
// Create custom error messages for different failure modes // Create custom error messages for different failure modes
switch (status) { switch (status) {
case asynSuccess: 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 If a property has been read, we need just the part between the
"=" (0x3D) and the [ACK] (0x06). Therefore, we remove all "=" (0x3D) and the [ACK] (0x06). Therefore, we remove all
@ -280,49 +231,61 @@ asynStatus masterMacsController::writeRead(int axisNo, int tcpCmd,
response[i] = fullResponse[i + valueStart]; response[i] = fullResponse[i + valueStart];
} }
} }
break; break;
case asynTimeout: case asynTimeout:
snprintf(drvMessageText, sizeof(drvMessageText), snprintf(drvMessageText, sizeof(drvMessageText),
"connection timeout for axis %d", axisNo); "Connection timeout. Please call the support.");
break; break;
case asynDisconnected: case asynDisconnected:
snprintf(drvMessageText, sizeof(drvMessageText), snprintf(drvMessageText, sizeof(drvMessageText),
"axis is not connected"); "Axis is not connected.");
break; break;
case asynDisabled: case asynDisabled:
snprintf(drvMessageText, sizeof(drvMessageText), "axis is disabled"); snprintf(drvMessageText, sizeof(drvMessageText), "Axis is disabled.");
break; break;
case asynError: case asynError:
// Do nothing - error message drvMessageText has already been set. // Do nothing - error message drvMessageText has already been set.
break; break;
default: default:
snprintf(drvMessageText, sizeof(drvMessageText), snprintf(drvMessageText, sizeof(drvMessageText),
"Communication failed (%s)", stringifyAsynStatus(status)); "Communication failed (%s). Please call the support.",
stringifyAsynStatus(status));
break; break;
} }
// Log the overall status (communication successfull or not) // Log the overall status (communication successfull or not)
if (status == asynSuccess) { if (status == asynSuccess) {
adjustForPrint(printableResponse, fullResponse, MAXBUF_);
pl_status = axis->setIntegerParam(this->motorStatusCommsError_, 0); pl_status = axis->setIntegerParam(this->motorStatusCommsError_, 0);
} else { } else {
// Check if the axis already is in an error communication mode. If /*
// it is not, upstream the error. This is done to avoid "flooding" Since the communication failed, there is the possibility that the
// the user with different error messages if more than one error controller is not connected at all to the network. In that case, we
// ocurred before an error-free communication cannot be sure that the information read out in the init method of the
axis is still up-to-date the next time we get a connection. Therefore,
an info flag is set which the axis object can use at the start of its
poll method to try to initialize itself.
*/
axis->setNeedInit(true);
/*
Check if the axis already is in an error communication mode. If
it is not, upstream the error. This is done to avoid "flooding"
the user with different error messages if more than one error
ocurred before an error-free communication
*/
pl_status = pl_status =
getIntegerParam(axisNo, motorStatusProblem_, &motorStatusProblem); getIntegerParam(axisNo, motorStatusProblem_, &motorStatusProblem);
if (pl_status != asynSuccess) { if (pl_status != asynSuccess) {
return paramLibAccessFailed(pl_status, "motorStatusProblem_", return paramLibAccessFailed(pl_status, "motorStatusProblem", axisNo,
axisNo, __PRETTY_FUNCTION__, __LINE__); __PRETTY_FUNCTION__, __LINE__);
} }
if (motorStatusProblem == 0) { if (motorStatusProblem == 0) {
pl_status = axis->setStringParam(motorMessageText_, drvMessageText); pl_status =
axis->setStringParam(motorMessageText(), drvMessageText);
if (pl_status != asynSuccess) { if (pl_status != asynSuccess) {
return paramLibAccessFailed(pl_status, "motorMessageText_", return paramLibAccessFailed(pl_status, "motorMessageText",
axisNo, __PRETTY_FUNCTION__, axisNo, __PRETTY_FUNCTION__,
__LINE__); __LINE__);
} }
@ -361,12 +324,21 @@ asynStatus masterMacsController::parseResponse(
bool responseValid = false; bool responseValid = false;
int responseStart = 0; int responseStart = 0;
asynStatus status = asynSuccess;
int prevConnected = 0;
char printableCommand[MAXBUF_] = {0}; char printableCommand[MAXBUF_] = {0};
char printableResponse[MAXBUF_] = {0}; char printableResponse[MAXBUF_] = {0};
msgPrintControlKey parseKey = msgPrintControlKey parseKey =
msgPrintControlKey(portName, axisNo, __PRETTY_FUNCTION__, __LINE__); 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 // We don't use strlen here since the C string terminator 0x00
// occurs in the middle of the char array. // occurs in the middle of the char array.
for (uint32_t i = 0; i < MAXBUF_; i++) { for (uint32_t i = 0; i < MAXBUF_; i++) {
@ -375,20 +347,75 @@ asynStatus masterMacsController::parseResponse(
} else if (fullResponse[i] == '=') { } else if (fullResponse[i] == '=') {
*valueStart = i + 1; *valueStart = i + 1;
} else if (fullResponse[i] == '\x06') { } else if (fullResponse[i] == '\x06') {
// ACK
*valueStop = i; *valueStop = i;
responseValid = true; responseValid = true;
break;
} else if (fullResponse[i] == '\x15') {
// NAK
snprintf(drvMessageText, MAXBUF_, "Communication failed.");
if (msgPrintControl_.shouldBePrinted(parseKey, true, // Motor wasn't connected before -> Update the paramLib entry and PV
pasynUserSelf)) { // to show it is now connected.
if (prevConnected == 0) {
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line " "Controller \"%s\", axis %d => %s, line "
"%d:\nCommunication failed.%s\n", "%d:\nAxis connection status has changed to "
portName, axisNo, __PRETTY_FUNCTION__, __LINE__, "connected.\n",
msgPrintControl_.getSuffix()); 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;
} 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;
}
} }
break; break;
} else if (fullResponse[i] == '\x18') { } else if (fullResponse[i] == '\x18') {
@ -397,14 +424,15 @@ asynStatus masterMacsController::parseResponse(
"Tried to write with a read-only command. This is a " "Tried to write with a read-only command. This is a "
"bug, please call the support."); "bug, please call the support.");
if (msgPrintControl_.shouldBePrinted(parseKey, true, if (getMsgPrintControl().shouldBePrinted(parseKey, true,
pasynUserSelf)) { pasynUserSelf)) {
adjustForPrint(printableCommand, fullCommand, MAXBUF_);
asynPrint( asynPrint(
this->pasynUserSelf, ASYN_TRACE_ERROR, this->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d:\nTried to " "Controller \"%s\", axis %d => %s, line %d:\nTried to "
"write with the read-only command %s.%s\n", "write with the read-only command %s.%s\n",
portName, axisNo, __PRETTY_FUNCTION__, __LINE__, portName, axisNo, __PRETTY_FUNCTION__, __LINE__,
printableCommand, msgPrintControl_.getSuffix()); printableCommand, getMsgPrintControl().getSuffix());
} }
responseValid = false; responseValid = false;
break; break;
@ -412,7 +440,7 @@ asynStatus masterMacsController::parseResponse(
} }
if (responseValid) { if (responseValid) {
msgPrintControl_.resetCount(parseKey, pasynUserSelf); getMsgPrintControl().resetCount(parseKey, pasynUserSelf);
// Check if the response matches the expectations. Each response // Check if the response matches the expectations. Each response
// contains the string "axisNo R tcpCmd" (including the spaces) // contains the string "axisNo R tcpCmd" (including the spaces)
@ -436,15 +464,15 @@ asynStatus masterMacsController::parseResponse(
adjustForPrint(printableCommand, fullCommand, MAXBUF_); adjustForPrint(printableCommand, fullCommand, MAXBUF_);
adjustForPrint(printableResponse, fullResponse, MAXBUF_); adjustForPrint(printableResponse, fullResponse, MAXBUF_);
if (msgPrintControl_.shouldBePrinted(parseKey, true, if (getMsgPrintControl().shouldBePrinted(parseKey, true,
pasynUserSelf)) { pasynUserSelf)) {
asynPrint( asynPrint(this->pasynUserSelf, ASYN_TRACEIO_DRIVER,
this->pasynUserSelf, ASYN_TRACEIO_DRIVER, "Controller \"%s\", axis %d => %s, line "
"Controller \"%s\", axis %d => %s, line %d:\nMismatched " "%d:\nMismatched "
"response %s to command %s.%s\n", "response %s to command %s.%s\n",
portName, axisNo, __PRETTY_FUNCTION__, __LINE__, portName, axisNo, __PRETTY_FUNCTION__, __LINE__,
printableResponse, printableCommand, printableResponse, printableCommand,
msgPrintControl_.getSuffix()); getMsgPrintControl().getSuffix());
} }
snprintf(drvMessageText, MAXBUF_, snprintf(drvMessageText, MAXBUF_,
@ -453,15 +481,16 @@ asynStatus masterMacsController::parseResponse(
printableResponse, printableCommand); printableResponse, printableCommand);
return asynError; return asynError;
} else { } else {
msgPrintControl_.resetCount(responseMatchKey, pasynUserSelf); getMsgPrintControl().resetCount(responseMatchKey, pasynUserSelf);
} }
} }
return asynSuccess; return asynSuccess;
} }
asynStatus sinqController::readInt32(asynUser *pasynUser, epicsInt32 *value) { asynStatus masterMacsController::readInt32(asynUser *pasynUser,
epicsInt32 *value) {
// masterMacs can be disabled // masterMacs can be disabled
if (pasynUser->reason == motorCanDisable_) { if (pasynUser->reason == motorCanDisable()) {
*value = 1; *value = 1;
return asynSuccess; return asynSuccess;
} else { } else {
@ -469,6 +498,8 @@ asynStatus sinqController::readInt32(asynUser *pasynUser, epicsInt32 *value) {
} }
} }
double masterMacsController::comTimeout() { return pMasterMacsC_->comTimeout; }
/***************************************************************************/ /***************************************************************************/
/** The following functions are C-wrappers, and can be called directly from /** The following functions are C-wrappers, and can be called directly from
* iocsh */ * iocsh */

View File

@ -11,6 +11,9 @@
#include "masterMacsAxis.h" #include "masterMacsAxis.h"
#include "sinqAxis.h" #include "sinqAxis.h"
#include "sinqController.h" #include "sinqController.h"
#include <memory>
struct masterMacsControllerImpl;
class masterMacsController : public sinqController { class masterMacsController : public sinqController {
@ -31,6 +34,15 @@ class masterMacsController : public sinqController {
int numAxes, double movingPollPeriod, int numAxes, double movingPollPeriod,
double idlePollPeriod, double comTimeout); double idlePollPeriod, double comTimeout);
/**
* @brief Overloaded version of the sinqController version
*
* @param pasynUser
* @param value
* @return asynStatus
*/
asynStatus readInt32(asynUser *pasynUser, epicsInt32 *value);
/** /**
* @brief Get the axis object * @brief Get the axis object
* *
@ -118,11 +130,15 @@ class masterMacsController : public sinqController {
// responses from it. // responses from it.
static const uint32_t MAXBUF_ = 200; static const uint32_t MAXBUF_ = 200;
/**
* @brief Get the communication timeout used in the writeRead command
*
* @return double Timeout in seconds
*/
double comTimeout();
private: private:
/* std::unique_ptr<masterMacsControllerImpl> pMasterMacsC_;
Stores the constructor input comTimeout
*/
double comTimeout_;
}; };
#endif /* masterMacsController_H */ #endif /* masterMacsController_H */

171
utils/writeRead.py Normal file
View 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.
""")