Compare commits

...

24 Commits
1.5.1 ... 1.5.3

Author SHA1 Message Date
b5dcc7bdf1 Fixed scaling of the written motorVelocity value
Some checks failed
Test And Build / Lint (push) Failing after 4s
Test And Build / Build (push) Successful in 7s
2026-02-12 12:04:20 +01:00
9a332f7a19 Squashed commit of the following:
commit d90869cbca
Author: smathis <stefan.mathis@psi.ch>
Date:   Tue Feb 10 13:18:43 2026 +0100

    Added reminder comment

commit eadce0f594
Author: smathis <stefan.mathis@psi.ch>
Date:   Tue Feb 10 12:31:01 2026 +0100

    Draft velocity mode

    See TODO in src/masterMacsAxis.cpp, line 552. The velocity readback is
    the only thing that doesn't work properly, everything else does work.
    Once a solution has been found here, this can be a new minor release.

commit 0e1f95a94b
Author: smathis <stefan.mathis@psi.ch>
Date:   Tue Feb 10 09:04:04 2026 +0100

    Updated sinqMotor and use setLimits

commit b01f398533
Author: smathis <stefan.mathis@psi.ch>
Date:   Tue Feb 10 09:00:58 2026 +0100

    Set move flag correctly in velocity / jog mode

commit 56d9d20c3a
Author: smathis <stefan.mathis@psi.ch>
Date:   Tue Feb 3 17:57:00 2026 +0100

    Simplified handling of velocity mode using standard EPICS motor record fields

commit 4634609891
Author: smathis <stefan.mathis@psi.ch>
Date:   Tue Feb 3 13:35:24 2026 +0100

    Rolled back to sinqMotor 1.5.7
2026-02-12 11:38:42 +01:00
b81e7926d1 Fixed limitswitch errors during homing 2026-02-12 11:35:21 +01:00
dbb4e92171 Rolled back to sinqMotor 1.5.7
Some checks failed
Test And Build / Lint (push) Failing after 4s
Test And Build / Build (push) Successful in 7s
2026-02-11 10:45:25 +01:00
41c1dad208 Motor now ignores limit switch hit errors when homing
Some checks failed
Test And Build / Lint (push) Failing after 3s
Test And Build / Build (push) Failing after 6s
See comment in src/masterMacsAxis.cpp, line 640ff.
2026-02-11 10:38:22 +01:00
4e3694977d Updated sinqMotor version 2026-02-11 10:38:15 +01:00
d24d2da50a Removed debug prints 2026-02-11 10:23:46 +01:00
516b8e7d68 added debug print 2026-02-11 10:19:44 +01:00
238a47f38e Ignore limit switch errors when the motor is homing or has been homed
Some checks failed
Test And Build / Lint (push) Failing after 4s
Test And Build / Build (push) Successful in 7s
2026-02-10 12:57:52 +01:00
fa4a20d83d Updated to sinqMotor 1.7 2026-01-22 11:33:45 +01:00
9a52a4b9ce Implemented velocity mode, but didn't test it yet
Some checks failed
Test And Build / Lint (push) Failing after 4s
Test And Build / Build (push) Successful in 7s
2026-01-20 16:52:52 +01:00
18e68b193a Updated to sinqMotor 1.6.1 2026-01-20 16:50:03 +01:00
fd41d4c9c0 PositionDeadband, dynamic limit detection, velocity mode
Some checks failed
Test And Build / Lint (push) Failing after 4s
Test And Build / Build (push) Failing after 6s
Integrated a readout function for the position deadband. Also read from
the controller if the axis has dynamic limits and only poll the limits
repeatedly if that is the case. Lastly, added support for the velocity
mode (untested!).
2026-01-20 15:09:51 +01:00
f3f0a77f10 Updated sinqMotor to 1.6 2026-01-20 14:16:27 +01:00
d834c8858e maxVelocity is not unused!
Some checks failed
Test And Build / Lint (push) Failing after 3s
Test And Build / Build (push) Successful in 7s
2026-01-20 13:25:15 +01:00
5314362c83 maxVelocity is not unused!
Some checks failed
Test And Build / Lint (push) Failing after 4s
Test And Build / Build (push) Failing after 5s
2026-01-20 13:21:32 +01:00
5efb94f83e Changed default for motorConnected and updated manual
Some checks failed
Test And Build / Lint (push) Failing after 3s
Test And Build / Build (push) Failing after 5s
2026-01-20 11:43:26 +01:00
7e7b8f486c Updated sinqMotor to fix segfault and improved docs 2025-12-23 11:47:12 +01:00
6adca95ade Fixed wrong doc
Some checks failed
Test And Build / Lint (push) Failing after 4s
Test And Build / Build (push) Successful in 7s
2025-11-03 11:17:04 +01:00
b4454a3ab6 Fixed wrong doc
Some checks failed
Test And Build / Lint (push) Failing after 4s
Test And Build / Build (push) Successful in 7s
2025-11-03 11:16:24 +01:00
73c96a73bf Usage in IOC shell is part of the user guide
Some checks failed
Test And Build / Lint (push) Failing after 4s
Test And Build / Build (push) Successful in 8s
2025-11-03 11:14:39 +01:00
e1732639b2 Added manual and .gitignore
Some checks failed
Test And Build / Lint (push) Failing after 4s
Test And Build / Build (push) Successful in 7s
2025-11-03 08:40:25 +01:00
6f72766ae6 Improved script docs and script usage description in README.md
Some checks failed
Test And Build / Lint (push) Failing after 4s
Test And Build / Build (push) Successful in 7s
Also introduced graceful error handling when trying to access
interactive mode on Windows.
2025-09-24 15:43:22 +02:00
a435c3c960 Update src/masterMacsAxis.cpp
Some checks failed
Test And Build / Lint (push) Failing after 4s
Test And Build / Build (push) Successful in 7s
Updated comment: encoder type 0 can also mean "no encoder"
2025-09-23 15:11:07 +02:00
13 changed files with 723 additions and 383 deletions

4
.gitignore vendored Normal file
View File

@@ -0,0 +1,4 @@
O.*
.cvsignore
.vscode
utils/__pycache__

View File

@@ -7,7 +7,7 @@ EPICS_VERSIONS=7.0.7
ARCH_FILTER=RHEL% ARCH_FILTER=RHEL%
# 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.3.2
# 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
@@ -30,7 +30,8 @@ DBDS += sinqMotor/src/sinqMotor.dbd
DBDS += src/masterMacs.dbd DBDS += src/masterMacs.dbd
USR_CFLAGS += -Wall -Wextra -Wunused-result -Wextra -Werror USR_CFLAGS += -Wall -Wextra -Wunused-result -Wextra -Werror
USR_CXXFLAGS += -Wall -Wextra -Wunused-result
# These flags define the expected firmware version. See README.md, section # These flags define the expected firmware version. See README.md, section
# "Firmware version checking" for details. # "Firmware version checking" for details.
USR_CXXFLAGS += -DFIRMWARE_MAJOR_VERSION=2 -DFIRMWARE_MINOR_VERSION=2 -Wall -Wextra -Weffc++ -Wunused-result -Wextra USR_CXXFLAGS += -DFIRMWARE_MAJOR_VERSION=2 -DFIRMWARE_MINOR_VERSION=2

BIN
MasterMACS_manual.pdf Normal file

Binary file not shown.

View File

@@ -17,23 +17,50 @@ The folder "utils" contains utility scripts for working with masterMacs motor co
- decodeError.py: Take the return message of a R11 (read error) 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. - writeRead.py: Send messages to the controller and receive answers.
## Developer guide These scripts can be run from anywhere. On Linux, the shebang (#!) automatically
calls the system Python 3 executable:
### Usage in IOC shell ```bash
# To show the help, use either flag -h or --help (works on all scripts)
/path/to/mastermacs_repo/utils/decodeStatus.py -h
/path/to/mastermacs_repo/utils/decodeError.py --help
/path/to/mastermacs_repo/utils/writeRead.py -h
masterMacs exposes the following IOC shell functions (all in masterMacsController.cpp): # To run in non-interactive mode, give the value as an argument
/path/to/mastermacs_repo/utils/decodeStatus.py 1234
/path/to/mastermacs_repo/utils/decodeError.py 5678
/path/to/mastermacs_repo/utils/writeRead.py "R11"
# To run in interactive mode, don't give any argument. This only works on Linux
/path/to/mastermacs_repo/utils/decodeStatus.py
/path/to/mastermacs_repo/utils/decodeError.py
/path/to/mastermacs_repo/utils/writeRead.py
```
To use these scripts on Windows, prefix the Python 3 executable:
```bash
C:/path/to/python3.exe C:/path/to/mastermacs_repo/utils/decodeStatus.py 1234
```
### IOC startup script
masterMacs exposes the following IOC shell functions:
- `masterMacsController`: Create a new controller object. - `masterMacsController`: Create a new controller object.
- `masterMacsAxis`: Create a new axis object. - `masterMacsAxis`: Create a new axis object.
The full mcu.cmd file looks like this: The full masterMacsX.cmd file looks like this:
``` ```bash
# Define the name of the controller and the corresponding port # Define the name of the controller and the corresponding port
epicsEnvSet("NAME","mcu") epicsEnvSet("NAME","mcu")
epicsEnvSet("ASYN_PORT","p$(NAME)") epicsEnvSet("ASYN_PORT","p$(NAME)")
# Create the TCP/IP socket used to talk with the controller. The socket can be adressed from within the IOC shell via the port name # Create the TCP/IP socket used to talk with the controller. The socket can be
drvAsynIPPortConfigure("$(ASYN_PORT)","172.28.101.24:1025") # adressed from within the IOC shell via the port name
# We do not use the standard asyn port driver here, but a PMAC-specific one
# which enables the usage of StreamDevices for
# communicating with the controller directly.
masterMacsAsynIPPortConfigure("$(ASYN_PORT)","172.28.101.24:1025")
# Create the controller object with the defined name and connect it to the socket via the port name. # Create the controller object with the defined name and connect it to the socket via the port name.
# The other parameters are as follows: # The other parameters are as follows:
@@ -62,6 +89,8 @@ dbLoadTemplate("$(TOP)/$(NAME).substitutions", "INSTR=$(INSTR)$(NAME):,CONTROLLE
dbLoadRecords("$(masterMacs_DB)/asynRecord.db","P=$(INSTR)$(NAME),PORT=$(ASYN_PORT)") dbLoadRecords("$(masterMacs_DB)/asynRecord.db","P=$(INSTR)$(NAME),PORT=$(ASYN_PORT)")
``` ```
## Developer guide
### Versioning ### Versioning
Please see the documentation for the module sinqMotor: https://git.psi.ch/sinq-epics-modules/sinqmotor/-/blob/main/README.md. Please see the documentation for the module sinqMotor: https://git.psi.ch/sinq-epics-modules/sinqmotor/-/blob/main/README.md.

View File

@@ -5,3 +5,13 @@ record(longout, "$(INSTR)$(M):NodeReset") {
field(OUT, "@asyn($(CONTROLLER),$(AXIS),1) NODE_RESET") field(OUT, "@asyn($(CONTROLLER),$(AXIS),1) NODE_RESET")
field(PINI, "NO") field(PINI, "NO")
} }
# Overrides the default value for the "Connected" record provided by sinqMotor.
record(longin, "$(INSTR)$(M):Connected")
{
field(DTYP, "asynInt32")
field(INP, "@asyn($(CONTROLLER),$(AXIS)) MOTOR_CONNECTED")
field(SCAN, "I/O Intr")
field(PINI, "NO")
field(VAL, "0")
}

View File

@@ -11,6 +11,11 @@
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
enum moveMode {
positionMode,
velocityMode,
};
struct masterMacsAxisImpl { struct masterMacsAxisImpl {
/* /*
The axis status and axis error of MasterMACS are given as an integer from The axis status and axis error of MasterMACS are given as an integer from
@@ -23,6 +28,8 @@ struct masterMacsAxisImpl {
time_t timeAtHandshake; time_t timeAtHandshake;
bool needInit = true; bool needInit = true;
bool targetReachedUninitialized; bool targetReachedUninitialized;
bool dynamicLimits;
moveMode lastMoveCommand;
}; };
/* /*
@@ -92,6 +99,8 @@ masterMacsAxis::masterMacsAxis(masterMacsController *pC, int axisNo)
.waitForHandshake = false, .waitForHandshake = false,
.timeAtHandshake = 0, .timeAtHandshake = 0,
.targetReachedUninitialized = true, .targetReachedUninitialized = true,
.dynamicLimits = false,
.lastMoveCommand = positionMode,
})) { })) {
asynStatus status = asynSuccess; asynStatus status = asynSuccess;
@@ -179,14 +188,16 @@ Read out the following values:
asynStatus masterMacsAxis::init() { asynStatus masterMacsAxis::init() {
// Local variable declaration // Local variable declaration
asynStatus pl_status = asynSuccess; asynStatus status = asynSuccess;
char response[pC_->MAXBUF_] = {0}; char response[pC_->MAXBUF_] = {0};
int nvals = 0; int nvals = 0;
double motorRecResolution = 0.0; double motRecResolution = 0.0;
double motorPosition = 0.0; double motPosition = 0.0;
double motorVelocity = 0.0; double motPositionDeadband = 0.0;
double motorVmax = 0.0; double motVelocity = 0.0;
double motorAccel = 0.0; double motVmax = 0.0;
double motAccel = 0.0;
int dynamicLimits = 0;
// ========================================================================= // =========================================================================
@@ -195,9 +206,9 @@ asynStatus masterMacsAxis::init() {
time_t now = time(NULL); time_t now = time(NULL);
time_t maxInitTime = 60; time_t maxInitTime = 60;
while (1) { while (1) {
pl_status = pC_->getDoubleParam(axisNo_, pC_->motorRecResolution(), status = pC_->getDoubleParam(axisNo_, pC_->motorRecResolution(),
&motorRecResolution); &motRecResolution);
if (pl_status == asynParamUndefined) { if (status == asynParamUndefined) {
if (now + maxInitTime < time(NULL)) { if (now + maxInitTime < time(NULL)) {
asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR, asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line " "Controller \"%s\", axis %d => %s, line "
@@ -206,83 +217,114 @@ asynStatus masterMacsAxis::init() {
__LINE__); __LINE__);
return asynError; return asynError;
} }
} else if (pl_status == asynSuccess) { } else if (status == asynSuccess) {
break; break;
} else if (pl_status != asynSuccess) { } else if (status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorRecResolution_", return pC_->paramLibAccessFailed(status, "motorRecResolution_",
axisNo_, __PRETTY_FUNCTION__, axisNo_, __PRETTY_FUNCTION__,
__LINE__); __LINE__);
} }
} }
// Initially assume that the axis is disconnected. It will be set to
// connected after the first communication attempted succeeded.
setAxisParamChecked(this, motorConnected, false);
// Read out the current position // Read out the current position
pl_status = pC_->read(axisNo_, 12, response); status = pC_->read(axisNo_, 12, response);
if (pl_status != asynSuccess) { if (status != asynSuccess) {
return pl_status; return status;
} }
nvals = sscanf(response, "%lf", &motorPosition); nvals = sscanf(response, "%lf", &motPosition);
if (nvals != 1) { if (nvals != 1) {
return pC_->couldNotParseResponse("R12", response, axisNo_, return pC_->couldNotParseResponse("R12", response, axisNo_,
__PRETTY_FUNCTION__, __LINE__); __PRETTY_FUNCTION__, __LINE__);
} }
status = setMotorPosition(motPosition);
if (status != asynSuccess) {
return status;
}
// Read out the current velocity // Read out the current velocity
pl_status = pC_->read(axisNo_, 05, response); status = pC_->read(axisNo_, 05, response);
if (pl_status != asynSuccess) { if (status != asynSuccess) {
return pl_status; return status;
} }
nvals = sscanf(response, "%lf", &motorVelocity); nvals = sscanf(response, "%lf", &motVelocity);
if (nvals != 1) { if (nvals != 1) {
return pC_->couldNotParseResponse("R05", response, axisNo_, return pC_->couldNotParseResponse("R05", response, axisNo_,
__PRETTY_FUNCTION__, __LINE__); __PRETTY_FUNCTION__, __LINE__);
} }
// Read out the maximum velocity // Read out the maximum velocity
pl_status = pC_->read(axisNo_, 26, response); status = pC_->read(axisNo_, 26, response);
if (pl_status != asynSuccess) { if (status != asynSuccess) {
return pl_status; return status;
} }
nvals = sscanf(response, "%lf", &motorVmax); nvals = sscanf(response, "%lf", &motVmax);
if (nvals != 1) { if (nvals != 1) {
return pC_->couldNotParseResponse("R26", response, axisNo_, return pC_->couldNotParseResponse("R26", response, axisNo_,
__PRETTY_FUNCTION__, __LINE__); __PRETTY_FUNCTION__, __LINE__);
} }
// Read out the acceleration status = setVeloFields(abs(motVelocity), 0.0, motVmax);
pl_status = pC_->read(axisNo_, 06, response); if (status != asynSuccess) {
if (pl_status != asynSuccess) { return status;
return pl_status;
} }
nvals = sscanf(response, "%lf", &motorAccel);
// Read out the acceleration
status = pC_->read(axisNo_, 06, response);
if (status != asynSuccess) {
return status;
}
nvals = sscanf(response, "%lf", &motAccel);
if (nvals != 1) { if (nvals != 1) {
return pC_->couldNotParseResponse("R06", response, axisNo_, return pC_->couldNotParseResponse("R06", response, axisNo_,
__PRETTY_FUNCTION__, __LINE__); __PRETTY_FUNCTION__, __LINE__);
} }
status = setAcclField(motAccel);
// Store the motor position in the parameter library if (status != asynSuccess) {
pl_status = setMotorPosition(motorPosition); return status;
if (pl_status != asynSuccess) {
return pl_status;
} }
// Write to the motor record fields // Read out the motor position deadband
pl_status = setVeloFields(motorVelocity, 0.0, motorVmax); status = pC_->read(axisNo_, 13, response);
if (pl_status != asynSuccess) { if (status != asynSuccess) {
return pl_status; return status;
} }
pl_status = setAcclField(motorAccel); nvals = sscanf(response, "%lf", &motPositionDeadband);
if (pl_status != asynSuccess) { if (nvals != 1) {
return pl_status; return pC_->couldNotParseResponse("R13", response, axisNo_,
__PRETTY_FUNCTION__, __LINE__);
}
setAxisParamChecked(this, motorPositionDeadband, motPositionDeadband);
// Check if the motor has dynamic limits
status = pC_->read(axisNo_, 32, response);
if (status != asynSuccess) {
return status;
}
nvals = sscanf(response, "%d", &dynamicLimits);
if (nvals != 1) {
return pC_->couldNotParseResponse("R32", response, axisNo_,
__PRETTY_FUNCTION__, __LINE__);
}
pMasterMacsA_->dynamicLimits = bool(dynamicLimits);
status = readEncoderType();
if (status != asynSuccess) {
return status;
} }
pl_status = readEncoderType(); // Read the axis limits
if (pl_status != asynSuccess) { status = readLimits();
return pl_status; if (status != asynSuccess) {
return status;
} }
// Update the parameter library immediately // Update the parameter library immediately
pl_status = callParamCallbacks(); status = callParamCallbacks();
if (pl_status != asynSuccess) { if (status != asynSuccess) {
// 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
@@ -290,14 +332,68 @@ asynStatus masterMacsAxis::init() {
"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(status));
return pl_status; return status;
} }
// Axis is fully initialized // Axis is fully initialized
setNeedInit(false); setNeedInit(false);
return pl_status; return status;
}
asynStatus masterMacsAxis::readLimits() {
asynStatus status = asynSuccess;
char response[pC_->MAXBUF_] = {0};
int nvals = 0;
double highLimit = 0.0;
double lowLimit = 0.0;
double limitsOffset = 0.0;
// =========================================================================
status = pC_->read(axisNo_, 34, response);
if (status != asynSuccess) {
return status;
}
nvals = sscanf(response, "%lf", &lowLimit);
if (nvals != 1) {
return pC_->couldNotParseResponse("R34", response, axisNo_,
__PRETTY_FUNCTION__, __LINE__);
}
status = pC_->read(axisNo_, 33, response);
if (status != asynSuccess) {
return status;
}
nvals = sscanf(response, "%lf", &highLimit);
if (nvals != 1) {
return pC_->couldNotParseResponse("R33", response, axisNo_,
__PRETTY_FUNCTION__, __LINE__);
}
/*
The axis limits are set as: ({[]})
where [] are the positive and negative limits set in EPICS/NICOS, {} are
the software limits set on the MCU and () are the hardware limit
switches. In other words, the EPICS/NICOS limits must be stricter than
the software limits on the MCU which in turn should be stricter than the
hardware limit switches. For example, if the hardware limit switches are
at [-10, 10], the software limits could be at [-9, 9] and the EPICS /
NICOS limits could be at
[-8, 8]. Therefore, we cannot use the software limits read from the MCU
directly, but need to shrink them a bit. In this case, we're shrinking
them by 0.1 mm or 0.1 degree (depending on the axis type) on both sides.
*/
getAxisParamChecked(this, motorLimitsOffset, &limitsOffset);
highLimit = highLimit - limitsOffset;
lowLimit = lowLimit + limitsOffset;
return setLimits(highLimit, lowLimit);
} }
// Perform the actual poll // Perform the actual poll
@@ -307,10 +403,10 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
asynStatus poll_status = asynSuccess; asynStatus poll_status = asynSuccess;
// Status of read-write-operations of ASCII commands to the controller // Status of read-write-operations of ASCII commands to the controller
asynStatus rw_status = asynSuccess; asynStatus rwStatus = asynSuccess;
// Status of parameter library operations // Status of parameter library operations
asynStatus pl_status = asynSuccess; asynStatus plStatus = asynSuccess;
char response[pC_->MAXBUF_] = {0}; char response[pC_->MAXBUF_] = {0};
int nvals = 0; int nvals = 0;
@@ -320,16 +416,14 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
double currentPosition = 0.0; double currentPosition = 0.0;
double previousPosition = 0.0; double previousPosition = 0.0;
double motorRecResolution = 0.0; double motorRecResolution = 0.0;
double highLimit = 0.0;
double lowLimit = 0.0;
double limitsOffset = 0.0;
double handshakePerformed = 0; double handshakePerformed = 0;
// ========================================================================= // =========================================================================
// Does the axis need to be intialized? // Does the axis need to be initialized?
if (needInit()) { if (needInit()) {
// Perform the rest of the poll, but remember if sth. failed in the init. // Perform the rest of the poll, but remember if sth. failed in the
// init.
poll_status = init(); poll_status = init();
} }
@@ -361,8 +455,8 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
} }
pC_->read(axisNo_, 86, response); pC_->read(axisNo_, 86, response);
if (rw_status != asynSuccess) { if (rwStatus != asynSuccess) {
return rw_status; return rwStatus;
} }
nvals = sscanf(response, "%lf", &handshakePerformed); nvals = sscanf(response, "%lf", &handshakePerformed);
@@ -388,17 +482,56 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
getAxisParamChecked(this, motorRecResolution, &motorRecResolution); getAxisParamChecked(this, motorRecResolution, &motorRecResolution);
// Read the previous motor position // Read the previous motor position
pl_status = motorPosition(&previousPosition); plStatus = motorPosition(&previousPosition);
if (pl_status != asynSuccess) { if (plStatus != asynSuccess) {
return pl_status; return plStatus;
} }
// Update the axis status // Update the axis status
rw_status = readAxisStatus(); rwStatus = readAxisStatus();
if (rw_status != asynSuccess) { if (rwStatus != asynSuccess) {
return rw_status; return rwStatus;
} }
rwStatus = pC_->read(axisNo_, 12, response);
if (rwStatus != asynSuccess) {
return rwStatus;
}
nvals = sscanf(response, "%lf", &currentPosition);
if (nvals != 1) {
return pC_->couldNotParseResponse("R12", response, axisNo_,
__PRETTY_FUNCTION__, __LINE__);
}
plStatus = setMotorPosition(currentPosition);
if (plStatus != asynSuccess) {
return plStatus;
}
setAxisParamChecked(this, motorEncoderPosition, currentPosition);
if (pMasterMacsA_->lastMoveCommand == velocityMode && !speedEqualZero()) {
double actualVelocity = 0.0;
rwStatus = pC_->read(axisNo_, 14, response);
if (rwStatus != asynSuccess) {
return rwStatus;
}
nvals = sscanf(response, "%lf", &actualVelocity);
if (nvals != 1) {
return pC_->couldNotParseResponse("R14", response, axisNo_,
__PRETTY_FUNCTION__, __LINE__);
}
// Writes the actual speed in steps per second to the paramLib. This
// value is then returned by the RVEL field of the motor record.
setAxisParamChecked(this, motorVelocity,
actualVelocity / motorRecResolution);
// Motor is moving in velocity mode
*moving = true;
} else {
// If we wait for a handshake, but the motor was moving in its last poll // If we wait for a handshake, but the motor was moving in its last poll
// cycle and has reached its target, it is not moving. Otherwise it is // cycle and has reached its target, it is not moving. Otherwise it is
// considered moving, even if we're still waiting for the handshake. // considered moving, even if we're still waiting for the handshake.
@@ -415,24 +548,14 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
if (targetReached()) { if (targetReached()) {
pMasterMacsA_->targetReachedUninitialized = false; pMasterMacsA_->targetReachedUninitialized = false;
} }
// Read the current position
rw_status = pC_->read(axisNo_, 12, response);
if (rw_status != asynSuccess) {
return rw_status;
}
nvals = sscanf(response, "%lf", &currentPosition);
if (nvals != 1) {
return pC_->couldNotParseResponse("R12", response, axisNo_,
__PRETTY_FUNCTION__, __LINE__);
} }
/* /*
Read out the error if either a fault condition status flag has been set or Read out the error if either a fault condition status flag has been set
if a movement has just ended. or if a movement has just ended.
*/ */
if (faultConditionSet() || !(*moving)) { if (faultConditionSet() || !(*moving)) {
rw_status = readAxisError(); rwStatus = readAxisError();
} }
msgPrintControlKey keyError = msgPrintControlKey( msgPrintControlKey keyError = msgPrintControlKey(
@@ -505,12 +628,52 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
poll_status = asynError; poll_status = asynError;
} }
/*
If the motor is homing or has been homed, ignore limit switch errors.
*/
int homing = 0;
int homed = 0;
getAxisParamChecked(this, motorStatusHome, &homing);
getAxisParamChecked(this, motorStatusHomed, &homed);
/*
Ignore limit switch errors when homing / motor has been homed or when
the motor is moving.
Background:
MasterMACS controllers move the motor outside the allowed range defined
by the "software limits" defined within the controllers because they
need to hit the physical end switch to determine the motor position. The
motor then rests close to the end switch, which might be outside the
controller-side software limits. This leads to this error, which is then
forwarded to the user even though nothing went wrong. The three checks
are here to prevent this:
- "homing": Is set at the start of a homing maneuver and removed once
the motor does not move anymore => Prevents the error from showing up
during the homing procedure
- "homed": Is set after a homing maneuver has been finished => Prevents
the error from showing up while the motor is resting idle outside the
software limits.
- "moving": Prevents the error from showing up when moving out of the
homing position.
If the motor hits the limits during normal operation, it is stopped by
the controller. Once stopped, moving is false and then the error is
shown to the user (because "homed" is not set).
Note: strictly speaking, it is not necessary to check homing because
moving would be set to true anyway. The check is here for clarity /
being explicit.
*/
if (!homing && !homed && !(*moving)) {
/* /*
Either the software limits or the end switches of the controller Either the software limits or the end switches of the controller
have been hit. Since the EPICS limits are derived from the software have been hit. Since the EPICS limits are derived from the software
limits and are a little bit smaller, these error cases can only limits and are a little bit smaller, these error cases can only
happen if either the axis has an incremental encoder which is not happen if either the axis has an incremental encoder which is not
properly homed or if a bug occured. properly homed or if the motor moved outside the limits while homing
(but in that case, the error is not shown, see previous
if-statement).
*/ */
if (positiveLimitSwitch() || negativeLimitSwitch() || if (positiveLimitSwitch() || negativeLimitSwitch() ||
positiveSoftwareLimit() || negativeSoftwareLimit()) { positiveSoftwareLimit() || negativeSoftwareLimit()) {
@@ -543,6 +706,7 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
poll_status = asynError; poll_status = asynError;
} }
}
if (overCurrent()) { if (overCurrent()) {
appendErrorMessage(shellMessage, sizeof(shellMessage), appendErrorMessage(shellMessage, sizeof(shellMessage),
@@ -609,48 +773,13 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
pC_->getMsgPrintControl().resetCount(keyError, pC_->pasynUser()); 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 and if the limits are
if (!(*moving)) { // dynamic
rw_status = pC_->read(axisNo_, 34, response); if (pMasterMacsA_->dynamicLimits && !(*moving)) {
if (rw_status != asynSuccess) { rwStatus = readLimits();
return rw_status; if (rwStatus != asynSuccess) {
return rwStatus;
} }
nvals = sscanf(response, "%lf", &lowLimit);
if (nvals != 1) {
return pC_->couldNotParseResponse("R34", response, axisNo_,
__PRETTY_FUNCTION__, __LINE__);
}
rw_status = pC_->read(axisNo_, 33, response);
if (rw_status != asynSuccess) {
return rw_status;
}
nvals = sscanf(response, "%lf", &highLimit);
if (nvals != 1) {
return pC_->couldNotParseResponse("R33", response, axisNo_,
__PRETTY_FUNCTION__, __LINE__);
}
/*
The axis limits are set as: ({[]})
where [] are the positive and negative limits set in EPICS/NICOS, {} are
the software limits set on the MCU and () are the hardware limit
switches. In other words, the EPICS/NICOS limits must be stricter than
the software limits on the MCU which in turn should be stricter than the
hardware limit switches. For example, if the hardware limit switches are
at [-10, 10], the software limits could be at [-9, 9] and the EPICS /
NICOS limits could be at
[-8, 8]. Therefore, we cannot use the software limits read from the MCU
directly, but need to shrink them a bit. In this case, we're shrinking
them by 0.1 mm or 0.1 degree (depending on the axis type) on both sides.
*/
getAxisParamChecked(this, motorLimitsOffset, &limitsOffset);
highLimit = highLimit - limitsOffset;
lowLimit = lowLimit + limitsOffset;
setAxisParamChecked(this, motorHighLimitFromDriver, highLimit);
setAxisParamChecked(this, motorLowLimitFromDriver, lowLimit);
} }
// Update the enable PV // Update the enable PV
@@ -674,26 +803,83 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
setAxisParamChecked(this, motorStatusDone, !(*moving)); setAxisParamChecked(this, motorStatusDone, !(*moving));
setAxisParamChecked(this, motorStatusDirection, direction); setAxisParamChecked(this, motorStatusDirection, direction);
pl_status = setMotorPosition(currentPosition);
if (pl_status != asynSuccess) {
return pl_status;
}
return poll_status; return poll_status;
} }
asynStatus masterMacsAxis::moveVelocity(double minVelocity, double maxVelocity,
double acceleration) {
// Suppress unused variable warning
(void)minVelocity;
(void)acceleration;
// Status of read-write-operations of ASCII commands to the controller
asynStatus status = asynSuccess;
char command[pC_->MAXBUF_];
double motorRecResolution = 0.0;
double motorVelocity = 0.0;
int enabled = 0;
// =========================================================================
getAxisParamChecked(this, motorEnableRBV, &enabled);
getAxisParamChecked(this, motorRecResolution, &motorRecResolution);
if (enabled == 0) {
asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d:\nAxis is "
"disabled.\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__);
return asynSuccess;
}
// Convert from EPICS to user / motor units
motorVelocity = maxVelocity * motorRecResolution;
snprintf(command, sizeof(command), "%lf", motorVelocity);
status = pC_->write(axisNo_, 05, command);
if (status != asynSuccess) {
setAxisParamChecked(this, motorStatusProblem, true);
return status;
}
asynPrint(pC_->pasynUser(), ASYN_TRACE_FLOW,
"Controller \"%s\", axis %d => %s, line %d:\nSetting speed "
"to %lf.\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
motorVelocity);
double timeout = pC_->comTimeout();
if (pMasterMacsA_->targetReachedUninitialized &&
timeout < PowerCycleTimeout) {
timeout = PowerCycleTimeout;
}
// Start the move. We do not use the MovTimeout watchdog here, because the
// motor can move for any time in velocity mode.
status = pC_->write(axisNo_, 00, "3", timeout);
if (status != asynSuccess) {
return status;
}
// Cache the information that the current movement is in velocity mode
pMasterMacsA_->lastMoveCommand = velocityMode;
return status;
}
asynStatus masterMacsAxis::doMove(double position, int relative, asynStatus masterMacsAxis::doMove(double position, int relative,
double minVelocity, double maxVelocity, double minVelocity, double maxVelocity,
double acceleration) { double acceleration) {
// Suppress unused variable warning // Suppress unused variable warning
(void)minVelocity; (void)minVelocity;
(void)maxVelocity;
(void)acceleration; (void)acceleration;
// Status of read-write-operations of ASCII commands to the controller // Status of read-write-operations of ASCII commands to the controller
asynStatus status = asynSuccess; asynStatus status = asynSuccess;
char value[pC_->MAXBUF_]; char command[pC_->MAXBUF_];
double motorCoordinatesPosition = 0.0; double motorCoordinatesPosition = 0.0;
double motorRecResolution = 0.0; double motorRecResolution = 0.0;
double motorVelocity = 0.0; double motorVelocity = 0.0;
@@ -736,8 +922,8 @@ asynStatus masterMacsAxis::doMove(double position, int relative,
// motor speed changed since the last move command. // motor speed changed since the last move command.
if (motorCanSetSpeed != 0) { if (motorCanSetSpeed != 0) {
snprintf(value, sizeof(value), "%lf", motorVelocity); snprintf(command, sizeof(command), "%lf", motorVelocity);
status = pC_->write(axisNo_, 05, value); status = pC_->write(axisNo_, 05, command);
if (status != asynSuccess) { if (status != asynSuccess) {
setAxisParamChecked(this, motorStatusProblem, true); setAxisParamChecked(this, motorStatusProblem, true);
return status; return status;
@@ -751,14 +937,14 @@ asynStatus masterMacsAxis::doMove(double position, int relative,
} }
// Set the target position // Set the target position
snprintf(value, sizeof(value), "%lf", motorCoordinatesPosition); snprintf(command, sizeof(command), "%lf", motorCoordinatesPosition);
status = pC_->write(axisNo_, 02, value); status = pC_->write(axisNo_, 02, command);
if (status != asynSuccess) { if (status != asynSuccess) {
setAxisParamChecked(this, motorStatusProblem, true); setAxisParamChecked(this, motorStatusProblem, true);
return status; return status;
} }
// If the motor has just been enabled, use Enable // If the motor has just been enabled, use a longer timeout for starting
double timeout = pC_->comTimeout(); double timeout = pC_->comTimeout();
if (pMasterMacsA_->targetReachedUninitialized && if (pMasterMacsA_->targetReachedUninitialized &&
timeout < PowerCycleTimeout) { timeout < PowerCycleTimeout) {
@@ -787,6 +973,8 @@ asynStatus masterMacsAxis::doMove(double position, int relative,
return asynError; return asynError;
} }
// Cache the information that the current movement is in position mode
pMasterMacsA_->lastMoveCommand = positionMode;
return status; return status;
} }
@@ -916,7 +1104,7 @@ asynStatus masterMacsAxis::readEncoderType() {
/* /*
Defined encoder IDs: Defined encoder IDs:
0=INC (Incremental) 0=INC (Incremental or no encoder)
1=SSI (Absolute encoder with SSI interface) 1=SSI (Absolute encoder with SSI interface)
2=SSI (Absolute encoder with BiSS interface) 2=SSI (Absolute encoder with BiSS interface)
*/ */
@@ -953,9 +1141,7 @@ asynStatus masterMacsAxis::enable(bool on) {
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
// command and inform the user. We check the last known status of the // command and inform the user.
// axis instead of "moving", since status -6 is also moving, but the
// motor can actually be disabled in this state!
if (moving) { if (moving) {
asynPrint(pC_->pasynUser(), 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 "
@@ -1037,6 +1223,28 @@ asynStatus masterMacsAxis::enable(bool on) {
return asynError; return asynError;
} }
asynStatus masterMacsAxis::setMode(int mode) {
char command[pC_->MAXBUF_] = {0};
// Map the EPICS value to MasterMACS values (see
// MasterMACS_manual.pdf).
int adjustedMode = 0;
if (mode == 0) {
adjustedMode = 1;
} else if (mode == 1) {
adjustedMode = 3;
} else {
// This branch is unreachable, as it is is already checked
// within sinqController::writeInt32 that value is either 0
// or 1.
return asynError;
}
snprintf(command, sizeof(command), "%d", adjustedMode);
return pC_->write(axisNo(), 07, command);
}
bool masterMacsAxis::needInit() { return pMasterMacsA_->needInit; } bool masterMacsAxis::needInit() { return pMasterMacsA_->needInit; }
/** /**
@@ -1067,8 +1275,8 @@ asynStatus masterMacsAxis::readAxisStatus() {
// ========================================================================= // =========================================================================
asynStatus rw_status = pC_->read(axisNo_, 10, response); asynStatus rwStatus = pC_->read(axisNo_, 10, response);
if (rw_status == asynSuccess) { if (rwStatus == asynSuccess) {
float axisStatus = 0; float axisStatus = 0;
int nvals = sscanf(response, "%f", &axisStatus); int nvals = sscanf(response, "%f", &axisStatus);
@@ -1080,7 +1288,7 @@ asynStatus masterMacsAxis::readAxisStatus() {
pMasterMacsA_->axisStatus = toBitset(axisStatus); pMasterMacsA_->axisStatus = toBitset(axisStatus);
} }
return rw_status; return rwStatus;
} }
asynStatus masterMacsAxis::readAxisError() { asynStatus masterMacsAxis::readAxisError() {
@@ -1088,8 +1296,8 @@ asynStatus masterMacsAxis::readAxisError() {
// ========================================================================= // =========================================================================
asynStatus rw_status = pC_->read(axisNo_, 11, response); asynStatus rwStatus = pC_->read(axisNo_, 11, response);
if (rw_status == asynSuccess) { if (rwStatus == asynSuccess) {
float axisError = 0; float axisError = 0;
int nvals = sscanf(response, "%f", &axisError); int nvals = sscanf(response, "%f", &axisError);
@@ -1099,7 +1307,7 @@ asynStatus masterMacsAxis::readAxisError() {
} }
pMasterMacsA_->axisError = toBitset(axisError); pMasterMacsA_->axisError = toBitset(axisError);
} }
return rw_status; return rwStatus;
} }
bool masterMacsAxis::readyToBeSwitchedOn() { bool masterMacsAxis::readyToBeSwitchedOn() {
@@ -1126,6 +1334,8 @@ bool masterMacsAxis::remoteMode() { return pMasterMacsA_->axisStatus[9]; }
bool masterMacsAxis::targetReached() { return pMasterMacsA_->axisStatus[10]; } bool masterMacsAxis::targetReached() { return pMasterMacsA_->axisStatus[10]; }
bool masterMacsAxis::speedEqualZero() { return pMasterMacsA_->axisStatus[12]; }
bool masterMacsAxis::internalLimitActive() { bool masterMacsAxis::internalLimitActive() {
return pMasterMacsA_->axisStatus[11]; return pMasterMacsA_->axisStatus[11];
} }

View File

@@ -51,6 +51,17 @@ class HIDDEN masterMacsAxis : public sinqAxis {
*/ */
asynStatus doPoll(bool *moving); asynStatus doPoll(bool *moving);
/**
* @brief TODO
*
* @param minVelocity
* @param maxVelocity
* @param acceleration
* @return asynStatus
*/
asynStatus moveVelocity(double minVelocity, double maxVelocity,
double acceleration);
/** /**
* @brief Implementation of the `doMove` function from sinqAxis. The * @brief Implementation of the `doMove` function from sinqAxis. The
* parameters are described in the documentation of `sinqAxis::doMove`. * parameters are described in the documentation of `sinqAxis::doMove`.
@@ -117,6 +128,15 @@ class HIDDEN masterMacsAxis : public sinqAxis {
*/ */
asynStatus enable(bool on); asynStatus enable(bool on);
/**
* @brief Write the new operation mode (position or velocity) to the
* MasterMACS controller.
*
* @param mode
* @return asynStatus
*/
asynStatus setMode(int mode);
/** /**
* @brief Read the encoder type (incremental or absolute) for this axis * @brief Read the encoder type (incremental or absolute) for this axis
* from the MCU and store the information in the PV ENCODER_TYPE. * from the MCU and store the information in the PV ENCODER_TYPE.
@@ -148,163 +168,203 @@ class HIDDEN masterMacsAxis : public sinqAxis {
/** /**
* @brief Read the Master MACS status with the xR10 command and store * @brief Read the Master MACS status with the xR10 command and store
* the result in axisStatus_ * the result in axisStatus (see masterMacsAxisImpl redefinition in
* masterMacsAxis.cpp)
* *
*/ */
asynStatus readAxisStatus(); asynStatus readAxisStatus();
/**
* @brief Read the upper and lower limits and store them in the parameter
* library.
*/
asynStatus readLimits();
/* /*
The functions below read the specified status bit from the axisStatus The functions below read the specified status bit from the axisStatus (see
bitset. Since a bit can either be 0 or 1, the return value is given as a masterMacsAxisImpl redefinition in masterMacsAxis.cpp) bitset. Since a bit
boolean. can either be 0 or 1, the return value is given as a boolean.
*/ */
/** /**
* @brief Read the property from axisStatus_ * @brief Read the property from axisStatus (see masterMacsAxisImpl
* redefinition in masterMacsAxis.cpp)
*/ */
bool readyToBeSwitchedOn(); bool readyToBeSwitchedOn();
/** /**
* @brief Read the property from axisStatus_ * @brief Read the property from axisStatus (see masterMacsAxisImpl
* redefinition in masterMacsAxis.cpp)
*/ */
bool switchedOn(); bool switchedOn();
// Bit 2 is unused // Bit 2 is unused
/** /**
* @brief Read the property from axisStatus_ * @brief Read the property from axisStatus (see masterMacsAxisImpl
* redefinition in masterMacsAxis.cpp)
*/ */
bool faultConditionSet(); bool faultConditionSet();
/** /**
* @brief Read the property from axisStatus_ * @brief Read the property from axisStatus (see masterMacsAxisImpl
* redefinition in masterMacsAxis.cpp)
*/ */
bool voltagePresent(); bool voltagePresent();
/** /**
* @brief Read the property from axisStatus_ * @brief Read the property from axisStatus (see masterMacsAxisImpl
* redefinition in masterMacsAxis.cpp)
*/ */
bool quickStopping(); bool quickStopping();
/** /**
* @brief Read the property from axisStatus_ * @brief Read the property from axisStatus (see masterMacsAxisImpl
* redefinition in masterMacsAxis.cpp)
*/ */
bool switchOnDisabled(); bool switchOnDisabled();
/** /**
* @brief Read the property from axisStatus_ * @brief Read the property from axisStatus (see masterMacsAxisImpl
* redefinition in masterMacsAxis.cpp)
*/ */
bool warning(); bool warning();
// Bit 8 is unused // Bit 8 is unused
/** /**
* @brief Read the property from axisStatus_ * @brief Read the property from axisStatus (see masterMacsAxisImpl
* redefinition in masterMacsAxis.cpp)
*/ */
bool remoteMode(); bool remoteMode();
/** /**
* @brief Read the property from axisStatus_ * @brief Read the property from axisStatus (see masterMacsAxisImpl
* redefinition in masterMacsAxis.cpp)
*/ */
bool targetReached(); bool targetReached();
/** /**
* @brief Read the property from axisStatus_ * @brief Read the property from axisStatus (see masterMacsAxisImpl
* redefinition in masterMacsAxis.cpp)
*/ */
bool internalLimitActive(); bool internalLimitActive();
/**
* @brief Read the property from axisStatus (see masterMacsAxisImpl
* redefinition in masterMacsAxis.cpp)
*/
bool speedEqualZero();
// Bits 12 and 13 are unused // Bits 12 and 13 are unused
/** /**
* @brief Read the property from axisStatus_ * @brief Read the property from axisStatus (see masterMacsAxisImpl
* redefinition in masterMacsAxis.cpp)
*/ */
bool setEventHasOcurred(); bool setEventHasOcurred();
/** /**
* @brief Read the property from axisStatus_ * @brief Read the property from axisStatus (see masterMacsAxisImpl
* redefinition in masterMacsAxis.cpp)
*/ */
bool powerEnabled(); bool powerEnabled();
/** /**
* @brief Read the Master MACS status with the xR10 command and store * @brief Read the Master MACS error with the xR10 command and store
* the result in axisStatus_ * the result in axisError (see masterMacsAxisImpl redefinition in
* masterMacsAxis.cpp)
* *
*/ */
asynStatus readAxisError(); asynStatus readAxisError();
/* /*
The functions below read the specified error bit from the axisError_ The functions below read the specified error bit from the axisError (see
bitset. Since a bit can either be 0 or 1, the return value is given as a masterMacsAxisImpl redefinition in masterMacsAxis.cpp) bitset. Since a bit
boolean. can either be 0 or 1, the return value is given as a boolean.
*/ */
/** /**
* @brief Read the property from axisError_ * @brief Read the property from axisError (see masterMacsAxisImpl
* redefinition in masterMacsAxis.cpp)
*/ */
bool shortCircuit(); bool shortCircuit();
/** /**
* @brief Read the property from axisError_ * @brief Read the property from axisError (see masterMacsAxisImpl
* redefinition in masterMacsAxis.cpp)
*/ */
bool encoderError(); bool encoderError();
/** /**
* @brief Read the property from axisError_ * @brief Read the property from axisError (see masterMacsAxisImpl
* redefinition in masterMacsAxis.cpp)
*/ */
bool followingError(); bool followingError();
/** /**
* @brief Read the property from axisError_ * @brief Read the property from axisError (see masterMacsAxisImpl
* redefinition in masterMacsAxis.cpp)
*/ */
bool communicationError(); bool communicationError();
/** /**
* @brief Read the property from axisError_ * @brief Read the property from axisError (see masterMacsAxisImpl
* redefinition in masterMacsAxis.cpp)
*/ */
bool feedbackError(); bool feedbackError();
/** /**
* @brief Read the property from axisError_ * @brief Read the property from axisError (see masterMacsAxisImpl
* redefinition in masterMacsAxis.cpp)
*/ */
bool positiveLimitSwitch(); bool positiveLimitSwitch();
/** /**
* @brief Read the property from axisError_ * @brief Read the property from axisError (see masterMacsAxisImpl
* redefinition in masterMacsAxis.cpp)
*/ */
bool negativeLimitSwitch(); bool negativeLimitSwitch();
/** /**
* @brief Read the property from axisError_ * @brief Read the property from axisError (see masterMacsAxisImpl
* redefinition in masterMacsAxis.cpp)
*/ */
bool positiveSoftwareLimit(); bool positiveSoftwareLimit();
/** /**
* @brief Read the property from axisError_ * @brief Read the property from axisError (see masterMacsAxisImpl
* redefinition in masterMacsAxis.cpp)
*/ */
bool negativeSoftwareLimit(); bool negativeSoftwareLimit();
/** /**
* @brief Read the property from axisError_ * @brief Read the property from axisError (see masterMacsAxisImpl
* redefinition in masterMacsAxis.cpp)
*/ */
bool overCurrent(); bool overCurrent();
/** /**
* @brief Read the property from axisError_ * @brief Read the property from axisError (see masterMacsAxisImpl
* redefinition in masterMacsAxis.cpp)
*/ */
bool overTemperature(); bool overTemperature();
/** /**
* @brief Read the property from axisError_ * @brief Read the property from axisError (see masterMacsAxisImpl
* redefinition in masterMacsAxis.cpp)
*/ */
bool overVoltage(); bool overVoltage();
/** /**
* @brief Read the property from axisError_ * @brief Read the property from axisError (see masterMacsAxisImpl
* redefinition in masterMacsAxis.cpp)
*/ */
bool underVoltage(); bool underVoltage();
/** /**
* @brief Read the property from axisError_ * @brief Read the property from axisError (see masterMacsAxisImpl
* redefinition in masterMacsAxis.cpp)
*/ */
bool stoFault(); bool stoFault();

View File

@@ -429,8 +429,6 @@ asynStatus masterMacsController::parseResponse(
getAxisParamChecked(axis, motorConnected, &prevConnected); getAxisParamChecked(axis, motorConnected, &prevConnected);
} }
// We don't use strlen here since the C string terminator 0x00
// occurs in the middle of the char array.
for (uint32_t i = 0; i < MAXBUF_; i++) { for (uint32_t i = 0; i < MAXBUF_; i++) {
if (fullResponse[i] == '\x19') { if (fullResponse[i] == '\x19') {
responseStart = i; responseStart = i;
@@ -572,7 +570,7 @@ asynStatus masterMacsController::readInt32(asynUser *pasynUser,
*value = 1; *value = 1;
return asynSuccess; return asynSuccess;
} else { } else {
return asynMotorController::readInt32(pasynUser, value); return sinqController::readInt32(pasynUser, value);
} }
} }

View File

@@ -75,7 +75,8 @@ class HIDDEN masterMacsController : public sinqController {
/** /**
* @brief Overloaded function of sinqController * @brief Overloaded function of sinqController
* *
* The function is overloaded to allow resetting the node * The function is overloaded to allow resetting the node and changing the
* operation mode.
* *
* @param pasynUser Specify the axis via the asynUser * @param pasynUser Specify the axis via the asynUser
* @param value New value * @param value New value

View File

@@ -9,6 +9,8 @@ To read the manual, simply run this script without any arguments.
Stefan Mathis, January 2025 Stefan Mathis, January 2025
""" """
import platform
from decodeCommon import interactive, decode, print_decoded from decodeCommon import interactive, decode, print_decoded
# List of tuples which encodes the states given in the file description. # List of tuples which encodes the states given in the file description.
@@ -32,50 +34,57 @@ interpretation = [
("Ok", "STO fault (STO input is on disable state)"), # Bit 15 ("Ok", "STO fault (STO input is on disable state)"), # Bit 15
] ]
help = """
Decode R11 message of MasterMACs
------------------
MasterMACs returns its error message (R11) as a floating-point number.
The bits of this float encode different states. These states are stored
in the interpretation variable.
This script can be used in two different ways:
Option 1: Single Command
------------------------
Usage: decodeError.py value
'value' is the return value of a R11 command. This value is interpreted
bit-wise and the result is printed out.
Option 2: CLI Mode (Linux-only)
-------------------------------
Usage: decodeError.py
ONLY AVAILABLE ON LINUX!
A prompt will be opened. Type in the return value of a R11 command, hit
enter and the interpretation will be printed in the prompt. After that,
the next value can be typed in. Type 'quit' to close the prompt.
"""
if __name__ == "__main__": if __name__ == "__main__":
from sys import argv from sys import argv
if "-h" or "--help" in argv:
print(help)
if len(argv) == 1: if len(argv) == 1:
# Start interactive mode # Start interactive mode
if platform.system() == "Linux":
interactive() interactive()
else: else:
print(help)
else:
number = None number = None
try: try:
number = int(float(argv[1])) number = int(float(argv[1]))
except: except:
print(""" print(help)
Decode R11 message of MasterMACs
------------------
MasterMACs returns its error message (R11) as a floating-point number.
The bits of this float encode different states. These states are stored
in the interpretation variable.
This script can be used in two different ways:
Option 1: Single Command
------------------------
Usage: decodeError.py value
'value' is the return value of a R11 command. This value is interpreted
bit-wise and the result is printed out.
Option 2: CLI Mode
------------------
Usage: decodeError.py
A prompt will be opened. Type in the return value of a R11 command, hit
enter and the interpretation will be printed in the prompt. After that,
the next value can be typed in. Type 'quit' to close the prompt.
""")
if number is not None: if number is not None:
print("Motor error") print("Motor error")
print("============") print("===========")
(bit_list, interpreted) = decode(number, interpretation) (bit_list, interpreted) = decode(number, interpretation)
print_decoded(bit_list, interpreted) print_decoded(bit_list, interpreted)

View File

@@ -9,6 +9,8 @@ To read the manual, simply run this script without any arguments.
Stefan Mathis, December 2024 Stefan Mathis, December 2024
""" """
import platform
from decodeCommon import interactive, decode, print_decoded from decodeCommon import interactive, decode, print_decoded
# List of tuples which encodes the states given in the file description. # List of tuples which encodes the states given in the file description.
@@ -23,7 +25,8 @@ interpretation = [
("Switch on enabled", "Switch on disabled"), # Bit 6 ("Switch on enabled", "Switch on disabled"), # Bit 6
("Ok", "Warning: Movement function was called while motor is still moving. The function call is ignored"), # Bit 7 ("Ok", "Warning: Movement function was called while motor is still moving. The function call is ignored"), # Bit 7
("Not specified", "Not specified"), # Bit 8 ("Not specified", "Not specified"), # Bit 8
("Motor does not execute command messages (local mode)", "Motor does execute command messages (remote mode)"), # Bit 9 ("Motor does not execute command messages (local mode)",
"Motor does execute command messages (remote mode)"), # Bit 9
("Target not reached", "Target reached"), # Bit 10 ("Target not reached", "Target reached"), # Bit 10
("Ok", "Internal limit active (current, voltage, velocity or position)"), # Bit 11 ("Ok", "Internal limit active (current, voltage, velocity or position)"), # Bit 11
("Not specified", "Not specified"), # Bit 12 ("Not specified", "Not specified"), # Bit 12
@@ -32,48 +35,55 @@ interpretation = [
("Not specified", "Not specified"), # Bit 15 ("Not specified", "Not specified"), # Bit 15
] ]
help = """
Decode R10 message of MasterMACs
------------------
MasterMACs returns its status message (R10) as a floating-point number.
The bits of this float encode different states. These states are stored
in the interpretation variable.
This script can be used in two different ways:
Option 1: Single Command
------------------------
Usage: decodeStatus.py value
'value' is the return value of a R10 command. This value is interpreted
bit-wise and the result is printed out.
Option 2: CLI Mode (Linux-only)
-------------------------------
Usage: decodeStatus.py
ONLY AVAILABLE ON LINUX!
A prompt will be opened. Type in the return value of a R10 command, hit
enter and the interpretation will be printed in the prompt. After that,
the next value can be typed in. Type 'quit' to close the prompt.
"""
if __name__ == "__main__": if __name__ == "__main__":
from sys import argv from sys import argv
if "-h" or "--help" in argv:
print(help)
if len(argv) == 1: if len(argv) == 1:
# Start interactive mode # Start interactive mode
if platform.system() == "Linux":
interactive() interactive()
else: else:
print(help)
else:
number = None number = None
try: try:
number = int(float(argv[1])) number = int(float(argv[1]))
except: except:
print(""" print(help)
Decode R10 message of MasterMACs
------------------
MasterMACs returns its status message (R10) as a floating-point number.
The bits of this float encode different states. These states are stored
in the interpretation variable.
This script can be used in two different ways:
Option 1: Single Command
------------------------
Usage: decodeStatus.py value
'value' is the return value of a R10 command. This value is interpreted
bit-wise and the result is printed out.
Option 2: CLI Mode
------------------
Usage: decodeStatus.py
A prompt will be opened. Type in the return value of a R10 command, hit
enter and the interpretation will be printed in the prompt. After that,
the next value can be typed in. Type 'quit' to close the prompt.
""")
if number is not None: if number is not None:
print("Motor status") print("Motor status")
print("============") print("============")

View File

@@ -6,23 +6,47 @@ To read the manual, simply run this script without any arguments.
Stefan Mathis, April 2025 Stefan Mathis, April 2025
""" """
import platform
import struct import struct
import socket import socket
import curses
help = """
Send commands to and receive replies from MasterMACS controllers
Option 1: Single Command
------------------------
Usage: writeRead.py pmachost:port command
This then returns the response for command.
Option 2: CLI Mode (Linux-only)
-------------------------------
Usage: writeRead.py pmachost:port
ONLY AVAILABLE ON LINUX!
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.
"""
def packMasterMacsCommand(command): def packMasterMacsCommand(command):
# 0x0D = Carriage return # 0x0D = Carriage return
buf = struct.pack('B',0x0D) buf = struct.pack('B', 0x0D)
buf = bytes(command,'utf-8') + buf buf = bytes(command, 'utf-8') + buf
return bytes(command,'utf-8') return bytes(command, 'utf-8')
def readMasterMacsReply(input): def readMasterMacsReply(input):
msg = bytearray() msg = bytearray()
expectAck = True expectAck = True
while True: while True:
b = input.recv(1) b = input.recv(1)
bint = int.from_bytes(b,byteorder='little') bint = int.from_bytes(b, byteorder='little')
if bint == 2 or bint == 7: #STX or BELL if bint == 2 or bint == 7: # STX or BELL
expectAck = False expectAck = False
continue continue
if expectAck and bint == 6: # ACK if expectAck and bint == 6: # ACK
@@ -33,24 +57,21 @@ def readMasterMacsReply(input):
else: else:
msg.append(bint) msg.append(bint)
if __name__ == "__main__": if __name__ == "__main__":
from sys import argv from sys import argv
try: if "-h" or "--help" in argv:
print(help)
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: else:
addr = argv[1].split(':')
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((addr[0], int(addr[1])))
try: if len(argv) == 2:
if platform.system() == "Linux":
import curses
stdscr = curses.initscr() stdscr = curses.initscr()
curses.noecho() curses.noecho()
@@ -125,7 +146,8 @@ if __name__ == "__main__":
if len(history[ptr]) == 0: if len(history[ptr]) == 0:
continue continue
(y, x) = stdscr.getyx() (y, x) = stdscr.getyx()
history[ptr] = history[ptr][0:x-4] + history[ptr][x-3:] history[ptr] = history[ptr][0:x-4] + \
history[ptr][x-3:]
stdscr.addch("\r") stdscr.addch("\r")
stdscr.clrtoeol() stdscr.clrtoeol()
stdscr.addstr(">> " + history[ptr]) stdscr.addstr(">> " + history[ptr])
@@ -134,38 +156,24 @@ if __name__ == "__main__":
else: else:
(y, x) = stdscr.getyx() (y, x) = stdscr.getyx()
history[ptr] = history[ptr][0:x-3] + chr(c) + history[ptr][x-3:] history[ptr] = history[ptr][0:x-3] + \
chr(c) + history[ptr][x-3:]
stdscr.addch("\r") stdscr.addch("\r")
stdscr.clrtoeol() stdscr.clrtoeol()
stdscr.addstr(">> " + history[ptr]) stdscr.addstr(">> " + history[ptr])
stdscr.move(y, x+1) stdscr.move(y, x+1)
stdscr.refresh() stdscr.refresh()
finally:
# to quit # to quit
curses.nocbreak() curses.nocbreak()
stdscr.keypad(False) stdscr.keypad(False)
curses.echo() curses.echo()
curses.endwin() curses.endwin()
else:
except: print(help)
print(""" elif len(argv) == 3:
Invalid Arguments buf = packMasterMacsCommand(argv[2])
s.send(buf)
Option 1: Single Command reply = readMasterMacsReply(s)
------------------------ print(reply.decode('utf-8') + '\n')
else:
Usage: writeRead.py pmachost:port command print(help)
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.
""")