Compare commits
24 Commits
Author | SHA1 | Date | |
---|---|---|---|
52fa4e1e78 | |||
a9e08460d9 | |||
37dacbd3ee | |||
d198dc8c9f | |||
13f2ec4fbf | |||
6ccb60f263 | |||
b8730e80e0 | |||
deb6e6996e | |||
d1d694ad6b | |||
e0a74c5598 | |||
c334ed9f04 | |||
4b0031c3af | |||
61335970ce | |||
081a21073b | |||
f2e8eb2762 | |||
e93f11e779 | |||
989410474e | |||
8d8561d833 | |||
a56a8cf646 | |||
cd57409f3c | |||
21ffcba8be | |||
3bfc2414b6 | |||
60053244a1 | |||
699b588ba5 |
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
|
16
Makefile
16
Makefile
@ -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.11.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
|
||||||
|
@ -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
1
sinqMotor
Submodule
Submodule sinqMotor added at 55a9fe6f3e
File diff suppressed because it is too large
Load Diff
@ -1,12 +1,10 @@
|
|||||||
#ifndef masterMacsAXIS_H
|
#ifndef masterMacsAXIS_H
|
||||||
#define masterMacsAXIS_H
|
#define masterMacsAXIS_H
|
||||||
|
#include "masterMacsController.h"
|
||||||
#include "sinqAxis.h"
|
#include "sinqAxis.h"
|
||||||
#include <bitset>
|
#include <memory>
|
||||||
|
|
||||||
// Forward declaration of the controller class to resolve the cyclic dependency
|
struct masterMacsAxisImpl;
|
||||||
// between the controller and the axis .h-file. See
|
|
||||||
// https://en.cppreference.com/w/cpp/language/class.
|
|
||||||
class masterMacsController;
|
|
||||||
|
|
||||||
class masterMacsAxis : public sinqAxis {
|
class masterMacsAxis : public sinqAxis {
|
||||||
public:
|
public:
|
||||||
@ -105,20 +103,25 @@ 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();
|
||||||
|
|
||||||
asynStatus readConfig();
|
/**
|
||||||
|
* @brief Instruct the axis to run its init() function during the next poll
|
||||||
|
*
|
||||||
|
* @param needInit
|
||||||
|
*/
|
||||||
|
void setNeedInit(bool needInit);
|
||||||
|
|
||||||
/*
|
/**
|
||||||
The axis status and axis error of MasterMACS are given as an integer from
|
* @brief Return a pointer to the axis controller
|
||||||
the controller. The 16 individual bits contain the actual information.
|
*/
|
||||||
*/
|
virtual masterMacsController *pController() override { return pC_; };
|
||||||
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
|
||||||
@ -128,7 +131,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 +139,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 +218,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
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -129,11 +136,8 @@ asynStatus masterMacsController::writeRead(int axisNo, int tcpCmd,
|
|||||||
|
|
||||||
// Definition of local variables.
|
// Definition of local variables.
|
||||||
asynStatus status = asynSuccess;
|
asynStatus 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 +161,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 +172,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 +187,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,66 +230,55 @@ 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_);
|
setAxisParamChecked(axis, motorStatusCommsError, false);
|
||||||
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
|
||||||
pl_status =
|
axis is still up-to-date the next time we get a connection. Therefore,
|
||||||
getIntegerParam(axisNo, motorStatusProblem_, &motorStatusProblem);
|
an info flag is set which the axis object can use at the start of its
|
||||||
if (pl_status != asynSuccess) {
|
poll method to try to initialize itself.
|
||||||
return paramLibAccessFailed(pl_status, "motorStatusProblem_",
|
*/
|
||||||
axisNo, __PRETTY_FUNCTION__, __LINE__);
|
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
|
||||||
|
*/
|
||||||
|
getAxisParamChecked(axis, motorStatusProblem, &motorStatusProblem);
|
||||||
|
|
||||||
if (motorStatusProblem == 0) {
|
if (motorStatusProblem == 0) {
|
||||||
pl_status = axis->setStringParam(motorMessageText_, drvMessageText);
|
setAxisParamChecked(axis, motorMessageText, drvMessageText);
|
||||||
if (pl_status != asynSuccess) {
|
setAxisParamChecked(axis, motorStatusProblem, true);
|
||||||
return paramLibAccessFailed(pl_status, "motorMessageText_",
|
setAxisParamChecked(axis, motorStatusCommsError, false);
|
||||||
axisNo, __PRETTY_FUNCTION__,
|
|
||||||
__LINE__);
|
|
||||||
}
|
|
||||||
|
|
||||||
pl_status = axis->setIntegerParam(motorStatusProblem_, 1);
|
|
||||||
if (pl_status != asynSuccess) {
|
|
||||||
return paramLibAccessFailed(pl_status, "motorStatusProblem",
|
|
||||||
axisNo, __PRETTY_FUNCTION__,
|
|
||||||
__LINE__);
|
|
||||||
}
|
|
||||||
|
|
||||||
pl_status = axis->setIntegerParam(motorStatusProblem_, 1);
|
|
||||||
if (pl_status != asynSuccess) {
|
|
||||||
return paramLibAccessFailed(pl_status, "motorStatusCommsError_",
|
|
||||||
axisNo, __PRETTY_FUNCTION__,
|
|
||||||
__LINE__);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -361,12 +300,22 @@ 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__);
|
||||||
|
|
||||||
|
masterMacsAxis *axis = getMasterMacsAxis(axisNo);
|
||||||
|
if (axis == nullptr) {
|
||||||
|
return asynError;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Was the motor previously connected?
|
||||||
|
getAxisParamChecked(axis, motorConnected, &prevConnected);
|
||||||
|
|
||||||
// We don't use strlen here since the C string terminator 0x00
|
// 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 +324,57 @@ 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__);
|
||||||
|
|
||||||
|
setAxisParamChecked(axis, motorConnected, true);
|
||||||
|
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__);
|
||||||
|
|
||||||
|
setAxisParamChecked(axis, motorConnected, false);
|
||||||
|
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 +383,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 +399,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 +423,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 +440,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 +457,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 */
|
||||||
|
@ -8,9 +8,16 @@
|
|||||||
|
|
||||||
#ifndef masterMacsController_H
|
#ifndef masterMacsController_H
|
||||||
#define masterMacsController_H
|
#define masterMacsController_H
|
||||||
#include "masterMacsAxis.h"
|
|
||||||
#include "sinqAxis.h"
|
#include "sinqAxis.h"
|
||||||
#include "sinqController.h"
|
#include "sinqController.h"
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
// Forward declaration of the controller class to resolve the cyclic dependency
|
||||||
|
// between the controller and the axis .h-file. See
|
||||||
|
// https://en.cppreference.com/w/cpp/language/class.
|
||||||
|
class masterMacsAxis;
|
||||||
|
|
||||||
|
struct masterMacsControllerImpl;
|
||||||
|
|
||||||
class masterMacsController : public sinqController {
|
class masterMacsController : public sinqController {
|
||||||
|
|
||||||
@ -31,6 +38,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 +134,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
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