39 Commits
0.8.0 ... main

Author SHA1 Message Date
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
f1c41d3081 Perform poll even if init fails
Some checks failed
Test And Build / Lint (push) Failing after 5s
Test And Build / Build (push) Successful in 8s
2025-09-17 13:12:21 +02:00
d78586a815 Updated sinqMotor to 1.5.6
Some checks failed
Test And Build / Lint (push) Failing after 4s
Test And Build / Build (push) Successful in 7s
2025-09-17 12:38:30 +02:00
ebcf99ac56 Updated sinqMotor to 1.5.5 2025-09-17 12:34:47 +02:00
de32298609 Updated sinqMotor to 1.5.4 2025-09-17 12:19:00 +02:00
8f457889c0 Updated sinqMotor to 1.5.3 2025-09-17 11:28:53 +02:00
6f63e521c1 Updated sinqMotor to 1.5.2 2025-09-17 11:25:40 +02:00
670f01fbe3 Updated sinqMotor to 1.5.1 to get a better error message in case of speed problems 2025-09-17 10:54:56 +02:00
25286652d5 Moved version definition in Makefile
Some checks failed
Test And Build / Lint (push) Failing after 3s
Test And Build / Build (push) Successful in 7s
The expected version can now be set in the Makefile via USR_CXXFLAGS.
Additionally, the README.md has received documentation regarding the
version check. Lastly, the version check can now be disabled by omitting
the flags or setting one of them to a negative value.
2025-08-22 13:18:43 +02:00
27f7cc8602 Added version check prototype
Some checks failed
Test And Build / Lint (push) Failing after 4s
Test And Build / Build (push) Successful in 8s
2025-08-22 08:46:30 +02:00
21a73717a5 Fixed handshake detection and reset bugs
Some checks failed
Test And Build / Lint (push) Failing after 4s
Test And Build / Build (push) Successful in 7s
2025-08-14 17:16:14 +02:00
2cbb4f9028 Forgot to add masterMacs.db to Makefile
Some checks failed
Test And Build / Lint (push) Failing after 4s
Test And Build / Build (push) Successful in 7s
2025-08-14 14:21:05 +02:00
23a911206a Removed node reset from doReset and moved it into dedicated function
Some checks failed
Test And Build / Lint (push) Failing after 4s
Test And Build / Build (push) Successful in 7s
A "normal" error reset should not trigger a node reset. However, this
option is still available via a dedicated PV xx:NodeReset and a
corresponding function in masterMacsAxis.
2025-08-12 15:51:12 +02:00
6553b468c8 Updated to sinqMotor 1.4.0 and hid all symbols
Some checks failed
Test And Build / Lint (push) Failing after 3s
Test And Build / Build (push) Successful in 7s
2025-08-12 09:44:15 +02:00
954fc82414 Moved error handling out of error read condition.
Some checks failed
Test And Build / Lint (push) Failing after 9s
Test And Build / Build (push) Successful in 12s
Previously, error messaging was only done after the error has been read.
This means that cached errors were simply ignored, if e.g. the motor was
moving. This commit now messages an error as long as it exists in the
cache "masterMacsAxisImpl->axisError".
2025-08-05 09:04:11 +02:00
ff183576ec Added axis reinitialization after node reset
When resetting the node, values within the controller may change, which
need to be reread by the init function.
2025-08-05 08:58:50 +02:00
83f9be3be8 Switched to forcedPoll method 2025-07-24 13:20:30 +02:00
2c0c9a33b7 Updated sinqMotor version 2025-07-24 13:19:24 +02:00
8bb81b1716 Fixed wrong comments
Some checks failed
Test And Build / Lint (push) Failing after 4s
Test And Build / Build (push) Successful in 8s
2025-07-22 17:13:09 +02:00
c32708e49c Removed lastSpeed caching
Some checks failed
Test And Build / Lint (push) Failing after 4s
Test And Build / Build (push) Successful in 7s
After discussion with Sven Schlienger, I now send the new speed
unconditionally before each move command. This also means that the
caching of the last speed is no longer needed.
2025-07-16 11:23:37 +02:00
14a733fb67 adds gitea action
Some checks failed
Test And Build / Lint (push) Failing after 4s
Test And Build / Build (push) Successful in 8s
2025-07-04 14:18:44 +02:00
64b932c3ae Fixed bug 2025-07-01 10:05:39 +02:00
09897b6125 Added node reset to doReset 2025-07-01 10:03:20 +02:00
9d47cd4e24 Fixed docs 2025-06-18 09:26:32 +02:00
52fa4e1e78 Updated sinqMotor version 2025-06-18 08:35:00 +02:00
a9e08460d9 Updated sinqMotor 2025-06-18 08:17:21 +02:00
37dacbd3ee Updated sinqMotor version and removed unnecessary require 2025-06-17 13:21:59 +02:00
d198dc8c9f Use axisParam accessor macros 2025-06-17 13:21:55 +02:00
13f2ec4fbf Switched to macro-based getters and setters for paramLib 2025-06-17 13:21:08 +02:00
6ccb60f263 Merge pull request 'Remove sinqmotor dep, it's statically linked. specify rhel8 arch' (#1) from makefile-fixes into main
Reviewed-on: #1
2025-06-11 16:52:42 +02:00
b8730e80e0 Remove sinqmotor dep, it's statically linked. specify rhel8 arch 2025-05-28 10:54:59 +02:00
deb6e6996e Ready for release 1.0 2025-05-23 12:29:30 +02:00
d1d694ad6b Applied PIMPL principle 2025-05-23 12:16:25 +02:00
e0a74c5598 Changed sinqMotor version to 0.15.2 2025-05-16 16:15:01 +02:00
15 changed files with 1080 additions and 870 deletions

View File

@@ -0,0 +1,24 @@
name: Test And Build
on: [push]
jobs:
Lint:
runs-on: linepics
steps:
- name: checkout repo
uses: actions/checkout@v4
- name: cppcheck
run: cppcheck --std=c++17 --addon=cert --addon=misc --error-exitcode=1 src/*.cpp
- name: formatting
run: clang-format --style=file --Werror --dry-run src/*.cpp
Build:
runs-on: linepics
steps:
- name: checkout repo
uses: actions/checkout@v4
with:
submodules: 'true'
- run: |
sed -i 's/ARCH_FILTER=.*/ARCH_FILTER=linux%/' Makefile
echo -e "\nIGNORE_SUBMODULES += sinqmotor" >> Makefile
make install

4
.gitignore vendored Normal file
View File

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

View File

@@ -1,48 +0,0 @@
default:
image: docker.psi.ch:5000/sinqdev/sinqepics:latest
stages:
- lint
- build
- test
cppcheck:
stage: lint
script:
- cppcheck --std=c++17 --addon=cert --addon=misc --suppress=cert-STR07-C --error-exitcode=1 src/*.cpp
artifacts:
expire_in: 1 week
tags:
- sinq
formatting:
stage: lint
script:
- clang-format --style=file --Werror --dry-run src/*.cpp
artifacts:
expire_in: 1 week
tags:
- sinq
build_module:
stage: build
script:
- export SINQMOTOR_VERSION="$(grep 'sinqMotor_VERSION=' Makefile | cut -d= -f2)"
- git clone --depth 1 --branch "${SINQMOTOR_VERSION}" https://gitlab-ci-token:${CI_JOB_TOKEN}@git.psi.ch/sinq-epics-modules/sinqmotor.git
- pushd sinqmotor
- sed -i 's/ARCH_FILTER=.*/ARCH_FILTER=linux%/' Makefile
- echo "LIBVERSION=${SINQMOTOR_VERSION}" >> Makefile
- make install
- popd
- sed -i 's/ARCH_FILTER=.*/ARCH_FILTER=linux%/' Makefile
- echo "LIBVERSION=${CI_COMMIT_TAG:-0.0.1}" >> Makefile
- make install
- cp -rT "/ioc/modules/masterMacs/$(ls -U /ioc/modules/masterMacs/ | head -1)" "./masterMacs-${CI_COMMIT_TAG:-$CI_COMMIT_SHORT_SHA}"
artifacts:
name: "masterMacs-${CI_COMMIT_TAG:-$CI_COMMIT_SHORT_SHA}"
paths:
- "masterMacs-${CI_COMMIT_TAG:-$CI_COMMIT_SHORT_SHA}/*"
expire_in: 1 week
when: always
tags:
- sinq

View File

@@ -6,9 +6,6 @@ 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
@@ -26,9 +23,14 @@ SOURCES += src/masterMacsController.cpp
# Store the record files # Store the record files
TEMPLATES += sinqMotor/db/asynRecord.db TEMPLATES += sinqMotor/db/asynRecord.db
TEMPLATES += sinqMotor/db/sinqMotor.db TEMPLATES += sinqMotor/db/sinqMotor.db
TEMPLATES += db/masterMacs.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 += sinqMotor/src/sinqMotor.dbd
DBDS += src/masterMacs.dbd DBDS += src/masterMacs.dbd
USR_CFLAGS += -Wall -Wextra -Weffc++ -Wunused-result -Wextra -Werror USR_CFLAGS += -Wall -Wextra -Wunused-result -Wextra -Werror
# These flags define the expected firmware version. See README.md, section
# "Firmware version checking" for details.
USR_CXXFLAGS += -DFIRMWARE_MAJOR_VERSION=2 -DFIRMWARE_MINOR_VERSION=2 -Wall -Wextra -Weffc++ -Wunused-result -Wextra

BIN
MasterMACS_manual.pdf Normal file

Binary file not shown.

View File

@@ -17,15 +17,38 @@ 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:
```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
# 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
```
### Usage in IOC shell ### Usage in IOC shell
masterMacs exposes the following IOC shell functions (all in masterMacsController.cpp): 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:
``` ```
# Define the name of the controller and the corresponding port # Define the name of the controller and the corresponding port
@@ -55,17 +78,45 @@ setMaxSubsequentTimeouts("$(NAME)", 20);
setThresholdComTimeout("$(NAME)", 100, 1); setThresholdComTimeout("$(NAME)", 100, 1);
# Parametrize the EPICS record database with the substitution file named after the MCU. # Parametrize the EPICS record database with the substitution file named after the MCU.
epicsEnvSet("SINQDBPATH","$(sinqMotor_DB)/sinqMotor.db") epicsEnvSet("SINQDBPATH","$(masterMacs_DB)/sinqMotor.db")
dbLoadTemplate("$(TOP)/$(NAME).substitutions", "INSTR=$(INSTR)$(NAME):,CONTROLLER=$(NAME)") dbLoadTemplate("$(TOP)/$(NAME).substitutions", "INSTR=$(INSTR)$(NAME):,CONTROLLER=$(NAME)")
epicsEnvSet("SINQDBPATH","$(masterMacs_DB)/masterMacs.db") epicsEnvSet("SINQDBPATH","$(masterMacs_DB)/masterMacs.db")
dbLoadTemplate("$(TOP)/$(NAME).substitutions", "INSTR=$(INSTR)$(NAME):,CONTROLLER=$(NAME)") dbLoadTemplate("$(TOP)/$(NAME).substitutions", "INSTR=$(INSTR)$(NAME):,CONTROLLER=$(NAME)")
dbLoadRecords("$(sinqMotor_DB)/asynRecord.db","P=$(INSTR)$(NAME),PORT=$(ASYN_PORT)") dbLoadRecords("$(masterMacs_DB)/asynRecord.db","P=$(INSTR)$(NAME),PORT=$(ASYN_PORT)")
``` ```
## 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.
### Firmware version checking
This driver expects a certain version of the firmware running on the controller itself.
This is checked at IOC startup by reading the version directly from the hardware.
If the firmware version is incompatible to the driver, the IOC will be shut down.
If the firmware version cannot be read (e.g. because the variable used to do so
does not exist yet on old firmware versions), the firmware is assumed to be compatible
to the driver.
The version check is separated into a check of the major and the minor firmware
version against expected values. The firmware is seen as compatible if the following conditions hold:
- Read-out major version == Expected major version
- Read-out read major version >= Expected minor version
The expected versions are defined via compiler flags in `Makefile`:
```
USR_CXXFLAGS += -DFIRMWARE_MAJOR_VERSION=1 -DFIRMWARE_MINOR_VERSION=0
```
Be aware that these flags are only used to compile C++-files (.cpp, .cxx) and not
C-files (.c). For C-files, the Makefile variable `USR_CFLAGS` must be used.
In order to disable the checks, the flags can be set to -1 or just be removed
entirely. If one of the flags is not given, both the major and the minor version
checks are deactivated.
### How to build it ### How to build it
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.

7
db/masterMacs.db Executable file
View File

@@ -0,0 +1,7 @@
# Call the nodeReset function of the corresponding masterMacsAxis.
# This record is coupled to the parameter library via nodeReset_ -> NODE_RESET.
record(longout, "$(INSTR)$(M):NodeReset") {
field(DTYP, "asynInt32")
field(OUT, "@asyn($(CONTROLLER),$(AXIS),1) NODE_RESET")
field(PINI, "NO")
}

View File

@@ -11,6 +11,20 @@
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
struct masterMacsAxisImpl {
/*
The axis status and axis error of MasterMACS are given as an integer from
the controller. The 16 individual bits contain the actual information.
*/
std::bitset<16> axisStatus;
std::bitset<16> axisError;
bool waitForHandshake;
time_t timeAtHandshake;
bool needInit = true;
bool targetReachedUninitialized;
};
/* /*
A special communication timeout is used in the following two cases: A special communication timeout is used in the following two cases:
1) Enable command 1) Enable command
@@ -71,7 +85,14 @@ void appendErrorMessage(char *fullMessage, size_t capacityFullMessage,
} }
masterMacsAxis::masterMacsAxis(masterMacsController *pC, int axisNo) masterMacsAxis::masterMacsAxis(masterMacsController *pC, int axisNo)
: sinqAxis(pC, axisNo), pC_(pC) { : sinqAxis(pC, axisNo), pC_(pC),
pMasterMacsA_(std::make_unique<masterMacsAxisImpl>((masterMacsAxisImpl){
.axisStatus = std::bitset<16>(0),
.axisError = std::bitset<16>(0),
.waitForHandshake = false,
.timeAtHandshake = 0,
.targetReachedUninitialized = true,
})) {
asynStatus status = asynSuccess; asynStatus status = asynSuccess;
@@ -104,18 +125,6 @@ masterMacsAxis::masterMacsAxis(masterMacsController *pC, int axisNo)
// Collect all axes into this list which will be used in the hook function // Collect all axes into this list which will be used in the hook function
axes.push_back(this); axes.push_back(this);
// Initialize all member variables
axisStatus_ = std::bitset<16>(0);
axisError_ = std::bitset<16>(0);
// Initial value for the motor speed, is overwritten in atFirstPoll.
lastSetSpeed_ = 0.0;
// Flag for handshake waiting
waitForHandshake_ = false;
timeAtHandshake_ = 0;
targetReachedUninitialized_ = true;
// masterMacs motors can always be disabled // masterMacs motors can always be disabled
status = pC_->setIntegerParam(axisNo_, pC_->motorCanDisable(), 1); status = pC_->setIntegerParam(axisNo_, pC_->motorCanDisable(), 1);
if (status != asynSuccess) { if (status != asynSuccess) {
@@ -250,11 +259,6 @@ asynStatus masterMacsAxis::init() {
__PRETTY_FUNCTION__, __LINE__); __PRETTY_FUNCTION__, __LINE__);
} }
// Cache the motor speed. If this value differs from the one in the motor
// record at the start of a movement, the motor record value is sent to
// MasterMACS.
lastSetSpeed_ = motorVelocity;
// Store the motor position in the parameter library // Store the motor position in the parameter library
pl_status = setMotorPosition(motorPosition); pl_status = setMotorPosition(motorPosition);
if (pl_status != asynSuccess) { if (pl_status != asynSuccess) {
@@ -289,6 +293,10 @@ asynStatus masterMacsAxis::init() {
pC_->stringifyAsynStatus(pl_status)); pC_->stringifyAsynStatus(pl_status));
return pl_status; return pl_status;
} }
// Axis is fully initialized
setNeedInit(false);
return pl_status; return pl_status;
} }
@@ -319,13 +327,19 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
// ========================================================================= // =========================================================================
// Does the axis need to be intialized?
if (needInit()) {
// Perform the rest of the poll, but remember if sth. failed in the init.
poll_status = init();
}
// Are we currently waiting for a handshake? // Are we currently waiting for a handshake?
if (waitForHandshake_) { if (pMasterMacsA_->waitForHandshake) {
// Check if the handshake takes too long and communicate an error in // Check if the handshake takes too long and communicate an error in
// this case. A handshake should not take more than 5 seconds. // this case. A handshake should not take more than 5 seconds.
time_t currentTime = time(NULL); time_t currentTime = time(NULL);
bool timedOut = (currentTime > timeAtHandshake_ + 5); bool timedOut = (currentTime > pMasterMacsA_->timeAtHandshake + 5);
if (pC_->getMsgPrintControl().shouldBePrinted( if (pC_->getMsgPrintControl().shouldBePrinted(
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, timedOut, pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, timedOut,
@@ -335,18 +349,15 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
"handshake at %ld s and didn't get a positive reply yet " "handshake at %ld s and didn't get a positive reply yet "
"(current time is %ld s).\n", "(current time is %ld s).\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
timeAtHandshake_, currentTime); pMasterMacsA_->timeAtHandshake, currentTime);
} }
if (timedOut) { if (timedOut) {
pl_status = setAxisParamChecked(this, motorMessageText,
setStringParam(pC_->motorMessageText(), "Timed out while waiting for a handshake. "
"Timed out while waiting for a handshake"); "Please call the support.");
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorMessageText_", poll_status = asynError;
axisNo_, __PRETTY_FUNCTION__,
__LINE__);
}
} }
pC_->read(axisNo_, 86, response); pC_->read(axisNo_, 86, response);
@@ -363,39 +374,18 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
if (handshakePerformed == 1.0) { if (handshakePerformed == 1.0) {
// Handshake has been performed successfully -> Continue with the // Handshake has been performed successfully -> Continue with the
// poll // poll
waitForHandshake_ = false; pMasterMacsA_->waitForHandshake = false;
targetReachedUninitialized_ = false; pMasterMacsA_->targetReachedUninitialized = false;
} else { } else {
// Still waiting for the handshake - try again in the next busy
// poll. This is already part of the movement procedure.
*moving = true; *moving = true;
setAxisParamChecked(this, motorStatusMoving, *moving);
pl_status = setIntegerParam(pC_->motorStatusMoving(), *moving); setAxisParamChecked(this, motorStatusDone, !(*moving));
if (pl_status != asynSuccess) { return poll_status;
return pC_->paramLibAccessFailed(pl_status,
"motorStatusMoving_", axisNo_,
__PRETTY_FUNCTION__, __LINE__);
}
pl_status = setIntegerParam(pC_->motorStatusDone(), !(*moving));
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorStatusDone_",
axisNo_, __PRETTY_FUNCTION__,
__LINE__);
}
return asynSuccess;
} }
} }
// Motor resolution from parameter library // Motor resolution from parameter library
pl_status = pC_->getDoubleParam(axisNo_, pC_->motorRecResolution(), getAxisParamChecked(this, motorRecResolution, &motorRecResolution);
&motorRecResolution);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorRecResolution_",
axisNo_, __PRETTY_FUNCTION__,
__LINE__);
}
// Read the previous motor position // Read the previous motor position
pl_status = motorPosition(&previousPosition); pl_status = motorPosition(&previousPosition);
@@ -409,7 +399,10 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
return rw_status; return rw_status;
} }
if (targetReachedUninitialized_) { // 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
// considered moving, even if we're still waiting for the handshake.
if (pMasterMacsA_->targetReachedUninitialized) {
*moving = false; *moving = false;
} else { } else {
if (targetReached() || !switchedOn()) { if (targetReached() || !switchedOn()) {
@@ -420,7 +413,7 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
} }
if (targetReached()) { if (targetReached()) {
targetReachedUninitialized_ = false; pMasterMacsA_->targetReachedUninitialized = false;
} }
// Read the current position // Read the current position
@@ -438,11 +431,12 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
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 or
if a movement has just ended. if a movement has just ended.
*/ */
msgPrintControlKey keyError = msgPrintControlKey(
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__);
if (faultConditionSet() || !(*moving)) { if (faultConditionSet() || !(*moving)) {
rw_status = readAxisError(); rw_status = readAxisError();
}
msgPrintControlKey keyError = msgPrintControlKey(
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__);
/* /*
A communication error is a special case. If a communication between A communication error is a special case. If a communication between
@@ -459,15 +453,9 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
pC_->getMsgPrintControl().getSuffix()); pC_->getMsgPrintControl().getSuffix());
} }
pl_status = setAxisParamChecked(this, motorMessageText,
setStringParam(pC_->motorMessageText(),
"Communication error between PC and motor " "Communication error between PC and motor "
"controller. Please call the support."); "controller. Please call the support.");
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorMessageText_",
axisNo_, __PRETTY_FUNCTION__,
__LINE__);
}
poll_status = asynError; poll_status = asynError;
} else { } else {
@@ -481,8 +469,7 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
if (shortCircuit()) { if (shortCircuit()) {
appendErrorMessage(shellMessage, sizeof(shellMessage), appendErrorMessage(shellMessage, sizeof(shellMessage),
"Short circuit fault."); "Short circuit fault.");
appendErrorMessage( appendErrorMessage(errorMessage, sizeof(errorMessage),
errorMessage, sizeof(errorMessage),
"Short circuit error. Please call the support."); "Short circuit error. Please call the support.");
poll_status = asynError; poll_status = asynError;
@@ -498,8 +485,7 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
} }
if (followingError()) { if (followingError()) {
appendErrorMessage( appendErrorMessage(shellMessage, sizeof(shellMessage),
shellMessage, sizeof(shellMessage),
"Maximum callowed following error exceeded."); "Maximum callowed following error exceeded.");
appendErrorMessage( appendErrorMessage(
errorMessage, sizeof(errorMessage), errorMessage, sizeof(errorMessage),
@@ -561,8 +547,7 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
if (overCurrent()) { if (overCurrent()) {
appendErrorMessage(shellMessage, sizeof(shellMessage), appendErrorMessage(shellMessage, sizeof(shellMessage),
"Overcurrent error."); "Overcurrent error.");
appendErrorMessage( appendErrorMessage(errorMessage, sizeof(errorMessage),
errorMessage, sizeof(errorMessage),
"Overcurrent error. Please call the support."); "Overcurrent error. Please call the support.");
poll_status = asynError; poll_status = asynError;
@@ -581,8 +566,7 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
if (overVoltage()) { if (overVoltage()) {
appendErrorMessage(shellMessage, sizeof(shellMessage), appendErrorMessage(shellMessage, sizeof(shellMessage),
"Overvoltage error."); "Overvoltage error.");
appendErrorMessage( appendErrorMessage(errorMessage, sizeof(errorMessage),
errorMessage, sizeof(errorMessage),
"Overvoltage error. Please call the support."); "Overvoltage error. Please call the support.");
poll_status = asynError; poll_status = asynError;
@@ -591,8 +575,7 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
if (underVoltage()) { if (underVoltage()) {
appendErrorMessage(shellMessage, sizeof(shellMessage), appendErrorMessage(shellMessage, sizeof(shellMessage),
"Undervoltage error."); "Undervoltage error.");
appendErrorMessage( appendErrorMessage(errorMessage, sizeof(errorMessage),
errorMessage, sizeof(errorMessage),
"Undervoltage error. Please call the support."); "Undervoltage error. Please call the support.");
poll_status = asynError; poll_status = asynError;
@@ -608,25 +591,21 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
} }
if (strlen(shellMessage) > 0) { if (strlen(shellMessage) > 0) {
if (pC_->getMsgPrintControl().shouldBePrinted( if (pC_->getMsgPrintControl().shouldBePrinted(keyError, true,
keyError, true, pC_->pasynUser())) { pC_->pasynUser())) {
asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR, asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line " "Controller \"%s\", axis %d => %s, line "
"%d\n%s%s\n", "%d\n%s%s\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
__LINE__, shellMessage, shellMessage, pC_->getMsgPrintControl().getSuffix());
pC_->getMsgPrintControl().getSuffix());
} }
} }
pl_status = setStringParam(pC_->motorMessageText(), errorMessage); setAxisParamChecked(this, motorMessageText, errorMessage);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorMessageText_",
axisNo_, __PRETTY_FUNCTION__,
__LINE__);
} }
}
} else { // No error has been detected -> Reset the error count
if (poll_status == asynSuccess) {
pC_->getMsgPrintControl().resetCount(keyError, pC_->pasynUser()); pC_->getMsgPrintControl().resetCount(keyError, pC_->pasynUser());
} }
@@ -665,40 +644,18 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
directly, but need to shrink them a bit. In this case, we're shrinking 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. them by 0.1 mm or 0.1 degree (depending on the axis type) on both sides.
*/ */
pl_status = pC_->getDoubleParam(axisNo_, pC_->motorLimitsOffset(), getAxisParamChecked(this, motorLimitsOffset, &limitsOffset);
&limitsOffset);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorLimitsOffset_",
axisNo_, __PRETTY_FUNCTION__,
__LINE__);
}
highLimit = highLimit - limitsOffset; highLimit = highLimit - limitsOffset;
lowLimit = lowLimit + limitsOffset; lowLimit = lowLimit + limitsOffset;
pl_status = pC_->setDoubleParam( setAxisParamChecked(this, motorHighLimitFromDriver, highLimit);
axisNo_, pC_->motorHighLimitFromDriver(), highLimit); setAxisParamChecked(this, motorLowLimitFromDriver, lowLimit);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(
pl_status, "motorHighLimitFromDriver_", axisNo_,
__PRETTY_FUNCTION__, __LINE__);
}
pl_status = pC_->setDoubleParam(axisNo_, pC_->motorLowLimitFromDriver(),
lowLimit);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorLowLimit_",
axisNo_, __PRETTY_FUNCTION__,
__LINE__);
}
} }
// Update the enable PV // Update the enable PV
pl_status = setIntegerParam(pC_->motorEnableRBV(), setAxisParamChecked(this, motorEnableRBV,
readyToBeSwitchedOn() && switchedOn()); readyToBeSwitchedOn() && switchedOn());
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorEnableRBV_", axisNo_,
__PRETTY_FUNCTION__, __LINE__);
}
if (*moving) { if (*moving) {
// If the axis is moving, evaluate the movement direction // If the axis is moving, evaluate the movement direction
@@ -711,38 +668,16 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
// Update the parameter library // Update the parameter library
if (hasError) { if (hasError) {
pl_status = setIntegerParam(pC_->motorStatusProblem(), true); setAxisParamChecked(this, motorStatusProblem, true);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorStatusProblem_",
axisNo_, __PRETTY_FUNCTION__,
__LINE__);
}
}
pl_status = setIntegerParam(pC_->motorStatusMoving(), *moving);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorStatusMoving_",
axisNo_, __PRETTY_FUNCTION__,
__LINE__);
}
pl_status = setIntegerParam(pC_->motorStatusDone(), !(*moving));
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorStatusDone_", axisNo_,
__PRETTY_FUNCTION__, __LINE__);
}
pl_status = setIntegerParam(pC_->motorStatusDirection(), direction);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorStatusDirection_",
axisNo_, __PRETTY_FUNCTION__,
__LINE__);
} }
setAxisParamChecked(this, motorStatusMoving, *moving);
setAxisParamChecked(this, motorStatusDone, !(*moving));
setAxisParamChecked(this, motorStatusDirection, direction);
pl_status = setMotorPosition(currentPosition); pl_status = setMotorPosition(currentPosition);
if (pl_status != asynSuccess) { if (pl_status != asynSuccess) {
return pl_status; return pl_status;
} }
return poll_status; return poll_status;
} }
@@ -750,11 +685,13 @@ asynStatus masterMacsAxis::doMove(double position, int relative,
double minVelocity, double maxVelocity, double minVelocity, double maxVelocity,
double acceleration) { double acceleration) {
// Status of read-write-operations of ASCII commands to the controller // Suppress unused variable warning
asynStatus rw_status = asynSuccess; (void)minVelocity;
(void)maxVelocity;
(void)acceleration;
// Status of parameter library operations // Status of read-write-operations of ASCII commands to the controller
asynStatus pl_status = asynSuccess; asynStatus status = asynSuccess;
char value[pC_->MAXBUF_]; char value[pC_->MAXBUF_];
double motorCoordinatesPosition = 0.0; double motorCoordinatesPosition = 0.0;
@@ -765,19 +702,8 @@ asynStatus masterMacsAxis::doMove(double position, int relative,
// ========================================================================= // =========================================================================
pl_status = pC_->getIntegerParam(axisNo_, pC_->motorEnableRBV(), &enabled); getAxisParamChecked(this, motorEnableRBV, &enabled);
if (pl_status != asynSuccess) { getAxisParamChecked(this, motorRecResolution, &motorRecResolution);
return pC_->paramLibAccessFailed(pl_status, "enableMotorRBV_", axisNo_,
__PRETTY_FUNCTION__, __LINE__);
}
pl_status = pC_->getDoubleParam(axisNo_, pC_->motorRecResolution(),
&motorRecResolution);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorRecResolution_",
axisNo_, __PRETTY_FUNCTION__,
__LINE__);
}
if (enabled == 0) { if (enabled == 0) {
asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR, asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
@@ -797,42 +723,24 @@ asynStatus masterMacsAxis::doMove(double position, int relative,
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, position); pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, position);
// Check if the speed is allowed to be changed // Check if the speed is allowed to be changed
pl_status = pC_->getIntegerParam(axisNo_, pC_->motorCanSetSpeed(), getAxisParamChecked(this, motorCanSetSpeed, &motorCanSetSpeed);
&motorCanSetSpeed);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorCanSetSpeed_",
axisNo_, __PRETTY_FUNCTION__,
__LINE__);
}
// Initialize the movement handshake // Initialize the movement handshake
rw_status = pC_->write(axisNo_, 86, "0"); status = pC_->write(axisNo_, 86, "0");
if (rw_status != asynSuccess) { if (status != asynSuccess) {
pl_status = setIntegerParam(pC_->motorStatusProblem(), true); setAxisParamChecked(this, motorStatusProblem, true);
if (pl_status != asynSuccess) { return status;
return pC_->paramLibAccessFailed(pl_status, "motorStatusProblem_",
axisNo_, __PRETTY_FUNCTION__,
__LINE__);
}
return rw_status;
} }
// Set the new motor speed, if the user is allowed to do so and if the // Set the new motor speed, if the user is allowed to do so and if the
// motor speed changed since the last move command. // motor speed changed since the last move command.
if (motorCanSetSpeed != 0 && lastSetSpeed_ != motorVelocity) { if (motorCanSetSpeed != 0) {
lastSetSpeed_ = motorVelocity;
snprintf(value, sizeof(value), "%lf", motorVelocity); snprintf(value, sizeof(value), "%lf", motorVelocity);
rw_status = pC_->write(axisNo_, 05, value); status = pC_->write(axisNo_, 05, value);
if (rw_status != asynSuccess) { if (status != asynSuccess) {
pl_status = setIntegerParam(pC_->motorStatusProblem(), true); setAxisParamChecked(this, motorStatusProblem, true);
if (pl_status != asynSuccess) { return status;
return pC_->paramLibAccessFailed(pl_status,
"motorStatusProblem_", axisNo_,
__PRETTY_FUNCTION__, __LINE__);
}
return rw_status;
} }
asynPrint(pC_->pasynUser(), ASYN_TRACE_FLOW, asynPrint(pC_->pasynUser(), ASYN_TRACE_FLOW,
@@ -844,43 +752,34 @@ asynStatus masterMacsAxis::doMove(double position, int relative,
// Set the target position // Set the target position
snprintf(value, sizeof(value), "%lf", motorCoordinatesPosition); snprintf(value, sizeof(value), "%lf", motorCoordinatesPosition);
rw_status = pC_->write(axisNo_, 02, value); status = pC_->write(axisNo_, 02, value);
if (rw_status != asynSuccess) { if (status != asynSuccess) {
pl_status = setIntegerParam(pC_->motorStatusProblem(), true); setAxisParamChecked(this, motorStatusProblem, true);
if (pl_status != asynSuccess) { return status;
return pC_->paramLibAccessFailed(pl_status, "motorStatusProblem_",
axisNo_, __PRETTY_FUNCTION__,
__LINE__);
}
return rw_status;
} }
// If the motor has just been enabled, use Enable // If the motor has just been enabled, use Enable
double timeout = pC_->comTimeout(); double timeout = pC_->comTimeout();
if (targetReachedUninitialized_ && timeout < PowerCycleTimeout) { if (pMasterMacsA_->targetReachedUninitialized &&
timeout < PowerCycleTimeout) {
timeout = PowerCycleTimeout; timeout = PowerCycleTimeout;
} }
// Start the move // Start the move
if (relative) { if (relative) {
rw_status = pC_->write(axisNo_, 00, "2", timeout); status = pC_->write(axisNo_, 00, "2", timeout);
} else { } else {
rw_status = pC_->write(axisNo_, 00, "1", timeout); status = pC_->write(axisNo_, 00, "1", timeout);
} }
if (rw_status != asynSuccess) { if (status != asynSuccess) {
pl_status = setIntegerParam(pC_->motorStatusProblem(), true); setAxisParamChecked(this, motorStatusProblem, true);
if (pl_status != asynSuccess) { return status;
return pC_->paramLibAccessFailed(pl_status, "motorStatusProblem_",
axisNo_, __PRETTY_FUNCTION__,
__LINE__);
}
return rw_status;
} }
// In the next poll, we will check if the handshake has been performed in a // In the next poll, we will check if the handshake has been performed in a
// reasonable time // reasonable time
waitForHandshake_ = true; pMasterMacsA_->waitForHandshake = true;
timeAtHandshake_ = time(NULL); pMasterMacsA_->timeAtHandshake = time(NULL);
// Waiting for a handshake is already part of the movement procedure => // Waiting for a handshake is already part of the movement procedure =>
// Start the watchdog // Start the watchdog
@@ -888,101 +787,103 @@ asynStatus masterMacsAxis::doMove(double position, int relative,
return asynError; return asynError;
} }
return rw_status; return status;
} }
asynStatus masterMacsAxis::stop(double acceleration) { asynStatus masterMacsAxis::stop(double acceleration) {
// Status of read-write-operations of ASCII commands to the controller // Suppress unused variable warning
asynStatus rw_status = asynSuccess; (void)acceleration;
// Status of parameter library operations asynStatus status = pC_->write(axisNo_, 00, "8");
asynStatus pl_status = asynSuccess; if (status != asynSuccess) {
setAxisParamChecked(this, motorStatusProblem, true);
// =========================================================================
rw_status = pC_->write(axisNo_, 00, "8");
if (rw_status != asynSuccess) {
pl_status = setIntegerParam(pC_->motorStatusProblem(), true);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorStatusProblem_",
axisNo_, __PRETTY_FUNCTION__,
__LINE__);
}
} }
return rw_status; // Reset the driver to idle state and move out of the handshake wait loop,
// if we're currently inside it.
pMasterMacsA_->waitForHandshake = false;
return status;
} }
asynStatus masterMacsAxis::doReset() { asynStatus masterMacsAxis::doReset() {
// Status of read-write-operations of ASCII commands to the controller
asynStatus rw_status = asynSuccess;
// Status of parameter library operations asynStatus status = asynSuccess;
asynStatus pl_status = asynSuccess;
// ========================================================================= // Reset any errors
status = pC_->write(axisNo_, 17, "");
rw_status = pC_->write(axisNo_, 17, ""); if (status != asynSuccess) {
if (rw_status != asynSuccess) { setAxisParamChecked(this, motorStatusProblem, true);
pl_status = setIntegerParam(pC_->motorStatusProblem(), true);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorStatusProblem_",
axisNo_, __PRETTY_FUNCTION__,
__LINE__);
}
} }
return rw_status; // Move out of the handshake wait loop, if we're currently inside it.
pMasterMacsA_->waitForHandshake = false;
// Disable the axis
return enable(false);
}
asynStatus masterMacsAxis::nodeReset() {
asynStatus status = asynSuccess;
// Reset the controller ("node reset"). Since the node reset results in a
// power cycle, we use the corresponding timeout.
status = pC_->write(axisNo_, 16, "", PowerCycleTimeout);
if (status != asynSuccess) {
setAxisParamChecked(this, motorStatusProblem, true);
}
// Move out of the handshake wait loop, if we're currently inside it.
pMasterMacsA_->waitForHandshake = false;
// Reinitialize the axis
status = masterMacsAxis::init();
if (status != asynSuccess) {
return status;
}
bool moving = false;
return forcedPoll(&moving);
} }
/* /*
Home the axis. On absolute encoder systems, this is a no-op Home the axis. On absolute encoder systems, this is a no-op
*/ */
asynStatus masterMacsAxis::doHome(double min_velocity, double max_velocity, asynStatus masterMacsAxis::doHome(double minVelocity, double maxVelocity,
double acceleration, int forwards) { double acceleration, int forwards) {
// Status of read-write-operations of ASCII commands to the controller
asynStatus rw_status = asynSuccess;
// Status of parameter library operations
asynStatus pl_status = asynSuccess;
char response[pC_->MAXBUF_] = {0}; char response[pC_->MAXBUF_] = {0};
// ========================================================================= // =========================================================================
pl_status = pC_->getStringParam(axisNo_, pC_->encoderType(), // Suppress unused variable warning
sizeof(response), response); (void)minVelocity;
if (pl_status != asynSuccess) { (void)maxVelocity;
return pC_->paramLibAccessFailed(pl_status, "encoderType_", axisNo_, (void)acceleration;
__PRETTY_FUNCTION__, __LINE__); (void)forwards;
}
getAxisParamChecked(this, encoderType, &response);
// Only send the home command if the axis has an incremental encoder // Only send the home command if the axis has an incremental encoder
if (strcmp(response, IncrementalEncoder) == 0) { if (strcmp(response, IncrementalEncoder) == 0) {
// Initialize the movement handshake // Initialize the movement handshake
rw_status = pC_->write(axisNo_, 86, "0"); asynStatus status = pC_->write(axisNo_, 86, "0");
if (rw_status != asynSuccess) { if (status != asynSuccess) {
pl_status = setIntegerParam(pC_->motorStatusProblem(), true); setAxisParamChecked(this, motorStatusProblem, true);
if (pl_status != asynSuccess) { return status;
return pC_->paramLibAccessFailed(pl_status,
"motorStatusProblem_", axisNo_,
__PRETTY_FUNCTION__, __LINE__);
}
return rw_status;
} }
rw_status = pC_->write(axisNo_, 00, "9"); status = pC_->write(axisNo_, 00, "9");
if (rw_status != asynSuccess) { if (status != asynSuccess) {
return rw_status; return status;
} }
// In the next poll, we will check if the handshake has been performed // In the next poll, we will check if the handshake has been performed
// in a reasonable time // in a reasonable time
waitForHandshake_ = true; pMasterMacsA_->waitForHandshake = true;
timeAtHandshake_ = time(NULL); pMasterMacsA_->timeAtHandshake = time(NULL);
return asynSuccess; return asynSuccess;
} else { } else {
return asynError; return asynError;
@@ -994,12 +895,6 @@ Read the encoder type and update the parameter library accordingly
*/ */
asynStatus masterMacsAxis::readEncoderType() { asynStatus masterMacsAxis::readEncoderType() {
// Status of read-write-operations of ASCII commands to the controller
asynStatus rw_status = asynSuccess;
// Status of parameter library operations
asynStatus pl_status = asynSuccess;
char command[pC_->MAXBUF_] = {0}; char command[pC_->MAXBUF_] = {0};
char response[pC_->MAXBUF_] = {0}; char response[pC_->MAXBUF_] = {0};
int nvals = 0; int nvals = 0;
@@ -1008,9 +903,9 @@ asynStatus masterMacsAxis::readEncoderType() {
// ========================================================================= // =========================================================================
// Check if this is an absolute encoder // Check if this is an absolute encoder
rw_status = pC_->read(axisNo_, 60, response); asynStatus status = pC_->read(axisNo_, 60, response);
if (rw_status != asynSuccess) { if (status != asynSuccess) {
return rw_status; return status;
} }
nvals = sscanf(response, "%d", &encoder_id); nvals = sscanf(response, "%d", &encoder_id);
@@ -1021,33 +916,23 @@ 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)
*/ */
if (encoder_id == 0) { if (encoder_id == 0) {
pl_status = setStringParam(pC_->encoderType(), IncrementalEncoder); setAxisParamChecked(this, encoderType, IncrementalEncoder);
} else { } else {
pl_status = setStringParam(pC_->encoderType(), AbsoluteEncoder); setAxisParamChecked(this, encoderType, AbsoluteEncoder);
} }
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "encoderType_", axisNo_,
__PRETTY_FUNCTION__, __LINE__);
}
return asynSuccess; return asynSuccess;
} }
asynStatus masterMacsAxis::enable(bool on) { asynStatus masterMacsAxis::enable(bool on) {
int timeout_enable_disable = 2; int timeout_enable_disable = 2;
char value[pC_->MAXBUF_]; char msg[pC_->MAXBUF_];
// Status of read-write-operations of ASCII commands to the controller
asynStatus rw_status = asynSuccess;
// Status of parameter library operations
asynStatus pl_status = asynSuccess;
// ========================================================================= // =========================================================================
@@ -1056,7 +941,7 @@ asynStatus masterMacsAxis::enable(bool on) {
0. In order to prevent the poll method in interpreting the motor state as 0. In order to prevent the poll method in interpreting the motor state as
"moving", this flag is used. It is reset in the handshake. "moving", this flag is used. It is reset in the handshake.
*/ */
targetReachedUninitialized_ = true; pMasterMacsA_->targetReachedUninitialized = true;
/* /*
Continue regardless of the status returned by the poll; we just want to Continue regardless of the status returned by the poll; we just want to
@@ -1077,14 +962,8 @@ asynStatus masterMacsAxis::enable(bool on) {
"idle and can therefore not be disabled.\n", "idle and can therefore not be disabled.\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__); pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__);
pl_status = setAxisParamChecked(this, motorMessageText,
setStringParam(pC_->motorMessageText(),
"Axis cannot be disabled while it is moving."); "Axis cannot be disabled while it is moving.");
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorMessageText_",
axisNo_, __PRETTY_FUNCTION__,
__LINE__);
}
return asynError; return asynError;
} }
@@ -1102,7 +981,7 @@ asynStatus masterMacsAxis::enable(bool on) {
} }
// Enable / disable the axis if it is not moving // Enable / disable the axis if it is not moving
snprintf(value, sizeof(value), "%d", on); snprintf(msg, sizeof(msg), "%d", on);
asynPrint(pC_->pasynUser(), ASYN_TRACE_FLOW, asynPrint(pC_->pasynUser(), ASYN_TRACE_FLOW,
"Controller \"%s\", axis %d => %s, line %d:\n%s axis.\n", "Controller \"%s\", axis %d => %s, line %d:\n%s axis.\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
@@ -1112,13 +991,14 @@ asynStatus masterMacsAxis::enable(bool on) {
// hence we wait for a custom timespan in seconds instead of // hence we wait for a custom timespan in seconds instead of
// pC_->comTimeout_ // pC_->comTimeout_
double timeout = pC_->comTimeout(); double timeout = pC_->comTimeout();
if (targetReachedUninitialized_ && timeout < PowerCycleTimeout) { if (pMasterMacsA_->targetReachedUninitialized &&
timeout < PowerCycleTimeout) {
timeout = PowerCycleTimeout; timeout = PowerCycleTimeout;
} }
rw_status = pC_->write(axisNo_, 04, value, timeout); asynStatus status = pC_->write(axisNo_, 04, msg, timeout);
if (rw_status != asynSuccess) { if (status != asynSuccess) {
return rw_status; return status;
} }
// Query the axis status every few milliseconds until the axis has been // Query the axis status every few milliseconds until the axis has been
@@ -1128,15 +1008,15 @@ asynStatus masterMacsAxis::enable(bool on) {
// Read the axis status // Read the axis status
usleep(100000); usleep(100000);
rw_status = readAxisStatus(); status = readAxisStatus();
if (rw_status != asynSuccess) { if (status != asynSuccess) {
return rw_status; return status;
} }
if (switchedOn() == on) { if (switchedOn() == on) {
bool moving = false; bool moving = false;
// Perform a poll to update the parameter library // Perform a poll to update the parameter library
poll(&moving); forcedPoll(&moving);
return asynSuccess; return asynSuccess;
} }
} }
@@ -1150,17 +1030,24 @@ asynStatus masterMacsAxis::enable(bool on) {
on ? "enable" : "disable", timeout_enable_disable); on ? "enable" : "disable", timeout_enable_disable);
// Output message to user // Output message to user
snprintf(value, sizeof(value), "Failed to %s within %d seconds", snprintf(msg, sizeof(msg), "Failed to %s within %d seconds",
on ? "enable" : "disable", timeout_enable_disable); on ? "enable" : "disable", timeout_enable_disable);
pl_status = setStringParam(pC_->motorMessageText(), value);
if (pl_status != asynSuccess) { setAxisParamChecked(this, motorMessageText, msg);
return pC_->paramLibAccessFailed(pl_status, "motorMessageText_",
axisNo_, __PRETTY_FUNCTION__,
__LINE__);
}
return asynError; return asynError;
} }
bool masterMacsAxis::needInit() { return pMasterMacsA_->needInit; }
/**
* @brief Instruct the axis to run its init() function during the next poll
*
* @param needInit
*/
void masterMacsAxis::setNeedInit(bool needInit) {
pMasterMacsA_->needInit = needInit;
}
/** /**
Convert a float to an unint16_t bitset Convert a float to an unint16_t bitset
*/ */
@@ -1190,7 +1077,7 @@ asynStatus masterMacsAxis::readAxisStatus() {
__PRETTY_FUNCTION__, __LINE__); __PRETTY_FUNCTION__, __LINE__);
} }
axisStatus_ = toBitset(axisStatus); pMasterMacsA_->axisStatus = toBitset(axisStatus);
} }
return rw_status; return rw_status;
@@ -1210,11 +1097,83 @@ asynStatus masterMacsAxis::readAxisError() {
return pC_->couldNotParseResponse("R11", response, axisNo_, return pC_->couldNotParseResponse("R11", response, axisNo_,
__PRETTY_FUNCTION__, __LINE__); __PRETTY_FUNCTION__, __LINE__);
} }
axisError_ = toBitset(axisError); pMasterMacsA_->axisError = toBitset(axisError);
} }
return rw_status; return rw_status;
} }
bool masterMacsAxis::readyToBeSwitchedOn() {
return pMasterMacsA_->axisStatus[0];
}
bool masterMacsAxis::switchedOn() { return pMasterMacsA_->axisStatus[1]; }
bool masterMacsAxis::faultConditionSet() {
return pMasterMacsA_->axisStatus[3];
}
bool masterMacsAxis::voltagePresent() { return pMasterMacsA_->axisStatus[4]; }
bool masterMacsAxis::quickStopping() {
return pMasterMacsA_->axisStatus[5] == 0;
}
bool masterMacsAxis::switchOnDisabled() { return pMasterMacsA_->axisStatus[6]; }
bool masterMacsAxis::warning() { return pMasterMacsA_->axisStatus[7]; }
bool masterMacsAxis::remoteMode() { return pMasterMacsA_->axisStatus[9]; }
bool masterMacsAxis::targetReached() { return pMasterMacsA_->axisStatus[10]; }
bool masterMacsAxis::internalLimitActive() {
return pMasterMacsA_->axisStatus[11];
}
bool masterMacsAxis::setEventHasOcurred() {
return pMasterMacsA_->axisStatus[14];
}
bool masterMacsAxis::powerEnabled() { return pMasterMacsA_->axisStatus[15]; }
bool masterMacsAxis::shortCircuit() { return pMasterMacsA_->axisError[1]; }
bool masterMacsAxis::encoderError() { return pMasterMacsA_->axisError[2]; }
bool masterMacsAxis::followingError() { return pMasterMacsA_->axisError[3]; }
bool masterMacsAxis::communicationError() {
return pMasterMacsA_->axisError[4];
}
bool masterMacsAxis::feedbackError() { return pMasterMacsA_->axisError[5]; }
bool masterMacsAxis::positiveLimitSwitch() {
return pMasterMacsA_->axisError[6];
}
bool masterMacsAxis::negativeLimitSwitch() {
return pMasterMacsA_->axisError[7];
}
bool masterMacsAxis::positiveSoftwareLimit() {
return pMasterMacsA_->axisError[8];
}
bool masterMacsAxis::negativeSoftwareLimit() {
return pMasterMacsA_->axisError[9];
}
bool masterMacsAxis::overCurrent() { return pMasterMacsA_->axisError[10]; }
bool masterMacsAxis::overTemperature() { return pMasterMacsA_->axisError[11]; }
bool masterMacsAxis::overVoltage() { return pMasterMacsA_->axisError[12]; }
bool masterMacsAxis::underVoltage() { return pMasterMacsA_->axisError[13]; }
bool masterMacsAxis::stoFault() { return pMasterMacsA_->axisError[15]; }
/***************************************************************************/ /***************************************************************************/
/** The following functions are C-wrappers, and can be called directly from /** The following functions are C-wrappers, and can be called directly from
* iocsh */ * iocsh */
@@ -1298,8 +1257,9 @@ static const iocshArg CreateAxisArg0 = {"Controller name (e.g. mmacs1)",
static const iocshArg CreateAxisArg1 = {"Axis number", iocshArgInt}; static const iocshArg CreateAxisArg1 = {"Axis number", iocshArgInt};
static const iocshArg *const CreateAxisArgs[] = {&CreateAxisArg0, static const iocshArg *const CreateAxisArgs[] = {&CreateAxisArg0,
&CreateAxisArg1}; &CreateAxisArg1};
static const iocshFuncDef configMasterMacsCreateAxis = {"masterMacsAxis", 2, static const iocshFuncDef configMasterMacsCreateAxis = {
CreateAxisArgs}; "masterMacsAxis", 2, CreateAxisArgs,
"Create a new instance of a MasterMACS axis."};
static void configMasterMacsCreateAxisCallFunc(const iocshArgBuf *args) { static void configMasterMacsCreateAxisCallFunc(const iocshArgBuf *args) {
masterMacsCreateAxis(args[0].sval, args[1].ival); masterMacsCreateAxis(args[0].sval, args[1].ival);
} }

View File

@@ -1,14 +1,12 @@
#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 HIDDEN 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 HIDDEN masterMacsAxis : public sinqAxis {
public: public:
/** /**
* @brief Construct a new masterMacsAxis * @brief Construct a new masterMacsAxis
@@ -18,6 +16,13 @@ class masterMacsAxis : public sinqAxis {
*/ */
masterMacsAxis(masterMacsController *pController, int axisNo); masterMacsAxis(masterMacsController *pController, int axisNo);
/**
* @brief Delete the copy and copy assignment constructors, because this
* class should not be copied (it is tied to hardware!)
*/
masterMacsAxis(const masterMacsAxis &) = delete;
masterMacsAxis &operator=(const masterMacsAxis &) = delete;
/** /**
* @brief Destroy the masterMacsAxis * @brief Destroy the masterMacsAxis
* *
@@ -52,13 +57,13 @@ class masterMacsAxis : public sinqAxis {
* *
* @param position * @param position
* @param relative * @param relative
* @param min_velocity * @param minVelocity
* @param max_velocity * @param maxVelocity
* @param acceleration * @param acceleration
* @return asynStatus * @return asynStatus
*/ */
asynStatus doMove(double position, int relative, double min_velocity, asynStatus doMove(double position, int relative, double minVelocity,
double max_velocity, double acceleration); double maxVelocity, double acceleration);
/** /**
* @brief Implementation of the `stop` function from asynMotorAxis * @brief Implementation of the `stop` function from asynMotorAxis
@@ -77,12 +82,27 @@ class masterMacsAxis : public sinqAxis {
*/ */
asynStatus doReset(); asynStatus doReset();
/**
* @brief Performs a "node reset" on the axis as defined in the CANopen
* standard
*
* A "node reset" is a factory reset on the axis which completely deletes
* all configured information (e.g. limits or speed) from the axis. The
* MasterMACS controller then reapplies the initial configuration to this
* axis. It can therefore be seen as a "hard" version of the normal error
* reset performed by the `doReset` method.
*
* @return asynStatus
*/
asynStatus nodeReset();
/** /**
* @brief Readout of some values from the controller at IOC startup * @brief Readout of some values from the controller at IOC startup
* *
* The following steps are performed: * The following steps are performed:
* - Read out the motor status, motor position, velocity and acceleration * - Read out the motor status, motor position, velocity and
* from the MCU and store this information in the parameter library. * acceleration from the MCU and store this information in the parameter
* library.
* - Set the enable PV accordint to the initial status of the axis. * - Set the enable PV accordint to the initial status of the axis.
* *
* @return asynStatus * @return asynStatus
@@ -98,191 +118,227 @@ class masterMacsAxis : public sinqAxis {
asynStatus enable(bool on); asynStatus enable(bool on);
/** /**
* @brief Read the encoder type (incremental or absolute) for this axis from * @brief Read the encoder type (incremental or absolute) for this axis
* the MCU and store the information in the PV ENCODER_TYPE. * from the MCU and store the information in the PV ENCODER_TYPE.
* *
* @return asynStatus * @return asynStatus
*/ */
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 targetReachedUninitialized_;
asynStatus readConfig();
/*
The axis status and axis error of MasterMACS are given as an integer from
the controller. The 16 individual bits contain the actual information.
*/ */
std::bitset<16> axisStatus_; bool needInit();
std::bitset<16> axisError_;
/** /**
* @brief Read the Master MACS status with the xR10 command and store the * @brief Instruct the axis to run its init() function during the next
* result in axisStatus_ * poll
*
* @param needInit
*/
void setNeedInit(bool needInit);
/**
* @brief Return a pointer to the axis controller
*/
virtual masterMacsController *pController() override { return pC_; };
/**
* @brief Read the Master MACS status with the xR10 command and store
* the result in axisStatus (see masterMacsAxisImpl redefinition in
* masterMacsAxis.cpp)
* *
*/ */
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 (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() { return axisStatus_[0]; } bool readyToBeSwitchedOn();
/** /**
* @brief Read the property from axisStatus_ * @brief Read the property from axisStatus (see masterMacsAxisImpl
* redefinition in masterMacsAxis.cpp)
*/ */
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 (see masterMacsAxisImpl
* redefinition in masterMacsAxis.cpp)
*/ */
bool faultConditionSet() { return axisStatus_[3]; } bool faultConditionSet();
/** /**
* @brief Read the property from axisStatus_ * @brief Read the property from axisStatus (see masterMacsAxisImpl
* redefinition in masterMacsAxis.cpp)
*/ */
bool voltagePresent() { return axisStatus_[4]; } bool voltagePresent();
/** /**
* @brief Read the property from axisStatus_ * @brief Read the property from axisStatus (see masterMacsAxisImpl
* redefinition in masterMacsAxis.cpp)
*/ */
bool quickStopping() { return axisStatus_[5] == 0; } bool quickStopping();
/** /**
* @brief Read the property from axisStatus_ * @brief Read the property from axisStatus (see masterMacsAxisImpl
* redefinition in masterMacsAxis.cpp)
*/ */
bool switchOnDisabled() { return axisStatus_[6]; } bool switchOnDisabled();
/** /**
* @brief Read the property from axisStatus_ * @brief Read the property from axisStatus (see masterMacsAxisImpl
* redefinition in masterMacsAxis.cpp)
*/ */
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 (see masterMacsAxisImpl
* redefinition in masterMacsAxis.cpp)
*/ */
bool remoteMode() { return axisStatus_[9]; } bool remoteMode();
/** /**
* @brief Read the property from axisStatus_ * @brief Read the property from axisStatus (see masterMacsAxisImpl
* redefinition in masterMacsAxis.cpp)
*/ */
bool targetReached() { return axisStatus_[10]; } bool targetReached();
/** /**
* @brief Read the property from axisStatus_ * @brief Read the property from axisStatus (see masterMacsAxisImpl
* redefinition in masterMacsAxis.cpp)
*/ */
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 (see masterMacsAxisImpl
* redefinition in masterMacsAxis.cpp)
*/ */
bool setEventHasOcurred() { return axisStatus_[14]; } bool setEventHasOcurred();
/** /**
* @brief Read the property from axisStatus_ * @brief Read the property from axisStatus (see masterMacsAxisImpl
* redefinition in masterMacsAxis.cpp)
*/ */
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 error with the xR10 command and store
* 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() { return axisError_[1]; } bool shortCircuit();
/** /**
* @brief Read the property from axisError_ * @brief Read the property from axisError (see masterMacsAxisImpl
* redefinition in masterMacsAxis.cpp)
*/ */
bool encoderError() { return axisError_[2]; } bool encoderError();
/** /**
* @brief Read the property from axisError_ * @brief Read the property from axisError (see masterMacsAxisImpl
* redefinition in masterMacsAxis.cpp)
*/ */
bool followingError() { return axisError_[3]; } bool followingError();
/** /**
* @brief Read the property from axisError_ * @brief Read the property from axisError (see masterMacsAxisImpl
* redefinition in masterMacsAxis.cpp)
*/ */
bool communicationError() { return axisError_[4]; } bool communicationError();
/** /**
* @brief Read the property from axisError_ * @brief Read the property from axisError (see masterMacsAxisImpl
* redefinition in masterMacsAxis.cpp)
*/ */
bool feedbackError() { return axisError_[5]; } bool feedbackError();
/** /**
* @brief Read the property from axisError_ * @brief Read the property from axisError (see masterMacsAxisImpl
* redefinition in masterMacsAxis.cpp)
*/ */
bool positiveLimitSwitch() { return axisError_[6]; } bool positiveLimitSwitch();
/** /**
* @brief Read the property from axisError_ * @brief Read the property from axisError (see masterMacsAxisImpl
* redefinition in masterMacsAxis.cpp)
*/ */
bool negativeLimitSwitch() { return axisError_[7]; } bool negativeLimitSwitch();
/** /**
* @brief Read the property from axisError_ * @brief Read the property from axisError (see masterMacsAxisImpl
* redefinition in masterMacsAxis.cpp)
*/ */
bool positiveSoftwareLimit() { return axisError_[8]; } bool positiveSoftwareLimit();
/** /**
* @brief Read the property from axisError_ * @brief Read the property from axisError (see masterMacsAxisImpl
* redefinition in masterMacsAxis.cpp)
*/ */
bool negativeSoftwareLimit() { return axisError_[9]; } bool negativeSoftwareLimit();
/** /**
* @brief Read the property from axisError_ * @brief Read the property from axisError (see masterMacsAxisImpl
* redefinition in masterMacsAxis.cpp)
*/ */
bool overCurrent() { return axisError_[10]; } bool overCurrent();
/** /**
* @brief Read the property from axisError_ * @brief Read the property from axisError (see masterMacsAxisImpl
* redefinition in masterMacsAxis.cpp)
*/ */
bool overTemperature() { return axisError_[11]; } bool overTemperature();
/** /**
* @brief Read the property from axisError_ * @brief Read the property from axisError (see masterMacsAxisImpl
* redefinition in masterMacsAxis.cpp)
*/ */
bool overVoltage() { return axisError_[12]; } bool overVoltage();
/** /**
* @brief Read the property from axisError_ * @brief Read the property from axisError (see masterMacsAxisImpl
* redefinition in masterMacsAxis.cpp)
*/ */
bool underVoltage() { return axisError_[13]; } bool underVoltage();
/** /**
* @brief Read the property from axisError_ * @brief Read the property from axisError (see masterMacsAxisImpl
* redefinition in masterMacsAxis.cpp)
*/ */
bool stoFault() { return axisError_[15]; } bool stoFault();
private:
masterMacsController *pC_;
std::unique_ptr<masterMacsAxisImpl> pMasterMacsA_;
}; };
#endif #endif

View File

@@ -12,6 +12,35 @@
#include <string> #include <string>
#include <unistd.h> #include <unistd.h>
/*
These functions are used to read out the compiler flags defining the major and
minor versions. See README.md, section "Firmware version checking" for
details. If these flags are not given, a default value of -1 is used, which
disables the version checks (it suffices to have one of these at -1 to disable
both major and minor version check)
*/
constexpr int firmware_major_version() {
#ifdef FIRMWARE_MAJOR_VERSION
return FIRMWARE_MAJOR_VERSION;
#else
return -1;
#endif
}
constexpr int firmware_minor_version() {
#ifdef FIRMWARE_MINOR_VERSION
return FIRMWARE_MINOR_VERSION;
#else
return -1;
#endif
}
struct masterMacsControllerImpl {
double comTimeout;
// Indices of additional ParamLib entries
int nodeReset;
};
/** /**
* @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.
@@ -52,15 +81,32 @@ masterMacsController::masterMacsController(const char *portName,
: sinqController(portName, ipPortConfigName, numAxes, movingPollPeriod, : sinqController(portName, ipPortConfigName, numAxes, movingPollPeriod,
idlePollPeriod, idlePollPeriod,
// No additional parameter library entries // No additional parameter library entries
0) 0),
pMasterMacsC_(
std::make_unique<masterMacsControllerImpl>((masterMacsControllerImpl){
.comTimeout = comTimeout,
.nodeReset = 0, // Overwritten later
}))
{ {
// Initialization of local variables // Initialization of local variables
asynStatus status = asynSuccess; asynStatus status = asynSuccess;
char response[MAXBUF_] = {0};
// Initialization of all member variables // =========================================================================
comTimeout_ = comTimeout; // Create additional parameter library entries
status =
createParam("NODE_RESET", asynParamInt32, &pMasterMacsC_->nodeReset);
if (status != asynSuccess) {
asynPrint(this->pasynUser(), ASYN_TRACE_ERROR,
"Controller \"%s\" => %s, line %d\nFATAL ERROR (creating a "
"parameter failed with %s).\nTerminating IOC",
portName, __PRETTY_FUNCTION__, __LINE__,
stringifyAsynStatus(status));
exit(-1);
}
// ========================================================================= // =========================================================================
@@ -93,6 +139,60 @@ masterMacsController::masterMacsController(const char *portName,
pasynOctetSyncIO->disconnect(pasynOctetSyncIOipPort()); pasynOctetSyncIO->disconnect(pasynOctetSyncIOipPort());
exit(-1); exit(-1);
} }
// =========================================================================
if (firmware_major_version() >= 0 && firmware_minor_version() >= 0) {
// Check the firmware version according to the conditions outlined in
// README.md
status = read(0, 99, response);
if (status == asynSuccess) {
// Just interpret the version if the variable already exists
double versionRaw = 0.0;
int nvals = sscanf(response, "%lf", &versionRaw);
if (nvals == 1 && versionRaw != 0.0) {
// Discard decimal part
long long versionInt = (long long)versionRaw;
// Extract bugfix (last 3 digits)
// Currently not used, just here for completions sake
// int bugfix = versionInt % 1000;
versionInt /= 1000;
// Extract minor (next 3 digits)
int minor = versionInt % 1000;
versionInt /= 1000;
// Remaining is major
int major = (int)versionInt;
// Compare to target values
if (firmware_major_version() != major ||
firmware_minor_version() > minor) {
asynPrint(this->pasynUser(), ASYN_TRACE_ERROR,
"Controller \"%s\" => %s, line %d\nFATAL ERROR "
"(Incorrect "
"version number of firmware: Expected major "
"version equal "
"to %d, got %d. Expected minor version equal to "
"or larger "
"than %d, got %d).\nTerminating IOC",
portName, __PRETTY_FUNCTION__, __LINE__,
firmware_major_version(), major,
firmware_minor_version(), minor);
exit(-1);
}
}
} else {
asynPrint(
this->pasynUser(), ASYN_TRACE_ERROR,
"Controller \"%s\" => %s, line %d\nCould not read firmware "
"version\n",
portName, __PRETTY_FUNCTION__, __LINE__);
}
}
} }
/* /*
@@ -114,8 +214,24 @@ masterMacsAxis *masterMacsController::getMasterMacsAxis(int axisNo) {
return dynamic_cast<masterMacsAxis *>(asynAxis); return dynamic_cast<masterMacsAxis *>(asynAxis);
} }
asynStatus masterMacsController::writeInt32(asynUser *pasynUser,
epicsInt32 value) {
int function = pasynUser->reason;
// =====================================================================
masterMacsAxis *axis = getMasterMacsAxis(pasynUser);
// Handle custom PVs
if (function == nodeReset()) {
return axis->nodeReset();
} else {
return sinqController::writeInt32(pasynUser, value);
}
}
asynStatus masterMacsController::read(int axisNo, int tcpCmd, char *response, asynStatus masterMacsController::read(int axisNo, int tcpCmd, char *response,
double comTimeout) { double /*comTimeout*/) {
return writeRead(axisNo, tcpCmd, NULL, response); return writeRead(axisNo, tcpCmd, NULL, response);
} }
@@ -130,7 +246,6 @@ 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 drvMessageText[MAXBUF_] = {0}; char drvMessageText[MAXBUF_] = {0};
@@ -158,13 +273,7 @@ asynStatus masterMacsController::writeRead(int axisNo, int tcpCmd,
// Check if a custom 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);
if (axis == nullptr) {
// We already did the error logging directly in getAxis
return asynError;
} }
// Build the full command depending on the inputs to this function // Build the full command depending on the inputs to this function
@@ -192,7 +301,7 @@ asynStatus masterMacsController::writeRead(int axisNo, int tcpCmd,
msgPrintControlKey comKey = msgPrintControlKey comKey =
msgPrintControlKey(portName, axisNo, __PRETTY_FUNCTION__, __LINE__); msgPrintControlKey(portName, axisNo, __PRETTY_FUNCTION__, __LINE__);
if (status != asynSuccess) { if (status != asynSuccess) {
if (msgPrintControl_.shouldBePrinted(comKey, true, pasynUserSelf)) { if (getMsgPrintControl().shouldBePrinted(comKey, true, pasynUserSelf)) {
char printableCommand[MAXBUF_] = {0}; char printableCommand[MAXBUF_] = {0};
adjustForPrint(printableCommand, fullCommand, MAXBUF_); adjustForPrint(printableCommand, fullCommand, MAXBUF_);
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
@@ -202,7 +311,7 @@ asynStatus masterMacsController::writeRead(int axisNo, int tcpCmd,
stringifyAsynStatus(status), printableCommand); stringifyAsynStatus(status), printableCommand);
} }
} else { } else {
msgPrintControl_.resetCount(comKey, pasynUserSelf); getMsgPrintControl().resetCount(comKey, pasynUserSelf);
} }
// Create custom error messages for different failure modes // Create custom error messages for different failure modes
@@ -248,41 +357,38 @@ asynStatus masterMacsController::writeRead(int axisNo, int tcpCmd,
} }
// Log the overall status (communication successfull or not) // Log the overall status (communication successfull or not)
if (status == asynSuccess) { if (axisNo != 0) {
pl_status = axis->setIntegerParam(this->motorStatusCommsError_, 0); masterMacsAxis *axis = getMasterMacsAxis(axisNo);
} else { if (axis == nullptr) {
// We already did the error logging directly in getAxis
// Check if the axis already is in an error communication mode. If return asynError;
// it is not, upstream the error. This is done to avoid "flooding"
// the user with different error messages if more than one error
// ocurred before an error-free communication
pl_status =
getIntegerParam(axisNo, motorStatusProblem_, &motorStatusProblem);
if (pl_status != asynSuccess) {
return paramLibAccessFailed(pl_status, "motorStatusProblem_",
axisNo, __PRETTY_FUNCTION__, __LINE__);
} }
if (status == asynSuccess) {
setAxisParamChecked(axis, motorStatusCommsError, false);
} else {
/*
Since the communication failed, there is the possibility that the
controller is not connected at all to the network. In that case, we
cannot be sure that the information read out in the init method of
the axis is still up-to-date the next time we get a connection.
Therefore, an info flag is set which the axis object can use at the
start of its poll method to try to initialize itself.
*/
axis->setNeedInit(true);
/*
Check if the axis already is in an error communication mode. If
it is not, upstream the error. This is done to avoid "flooding"
the user with different error messages if more than one error
ocurred before an error-free communication
*/
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__);
} }
} }
} }
@@ -306,18 +412,21 @@ asynStatus masterMacsController::parseResponse(
bool responseValid = false; bool responseValid = false;
int responseStart = 0; int responseStart = 0;
asynStatus status = asynSuccess; asynStatus status = asynSuccess;
int prevConnected = 0; int prevConnected = 1;
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 (axisNo != 0 && axis == nullptr) {
return asynError;
}
// Was the motor previously connected? // Was the motor previously connected?
status = getIntegerParam(axisNo, motorConnected(), &prevConnected); if (axis != nullptr) {
if (status != asynSuccess) { getAxisParamChecked(axis, motorConnected, &prevConnected);
return paramLibAccessFailed(status, "motorConnected", axisNo,
__PRETTY_FUNCTION__, __LINE__);
} }
// We don't use strlen here since the C string terminator 0x00 // We don't use strlen here since the C string terminator 0x00
@@ -342,25 +451,19 @@ asynStatus masterMacsController::parseResponse(
"connected.\n", "connected.\n",
portName, axisNo, __PRETTY_FUNCTION__, __LINE__); portName, axisNo, __PRETTY_FUNCTION__, __LINE__);
masterMacsAxis *axis = getMasterMacsAxis(axisNo); if (axis != nullptr) {
if (axis == nullptr) { setAxisParamChecked(axis, motorConnected, true);
return asynError;
}
status = axis->setIntegerParam(motorConnected(), 1);
if (status != asynSuccess) {
return paramLibAccessFailed(status, "motorConnected",
axisNo, __PRETTY_FUNCTION__,
__LINE__);
}
status = callParamCallbacks(); status = callParamCallbacks();
if (status != asynSuccess) { if (status != asynSuccess) {
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:\nCould not update parameter library\n", "%d:\nCould not update parameter library\n",
portName, axisNo, __PRETTY_FUNCTION__, __LINE__); portName, axisNo, __PRETTY_FUNCTION__,
__LINE__);
return status; return status;
} }
} }
}
break; break;
} else if (fullResponse[i] == '\x15') { } else if (fullResponse[i] == '\x15') {
@@ -379,25 +482,19 @@ asynStatus masterMacsController::parseResponse(
"disconnected.\n", "disconnected.\n",
portName, axisNo, __PRETTY_FUNCTION__, __LINE__); portName, axisNo, __PRETTY_FUNCTION__, __LINE__);
masterMacsAxis *axis = getMasterMacsAxis(axisNo); if (axis != nullptr) {
if (axis == nullptr) { setAxisParamChecked(axis, motorConnected, false);
return asynError;
}
status = axis->setIntegerParam(motorConnected(), 0);
if (status != asynSuccess) {
return paramLibAccessFailed(status, "motorConnected",
axisNo, __PRETTY_FUNCTION__,
__LINE__);
}
status = callParamCallbacks(); status = callParamCallbacks();
if (status != asynSuccess) { if (status != asynSuccess) {
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:\nCould not update parameter library\n", "%d:\nCould not update parameter library\n",
portName, axisNo, __PRETTY_FUNCTION__, __LINE__); portName, axisNo, __PRETTY_FUNCTION__,
__LINE__);
return status; return status;
} }
} }
}
break; break;
} else if (fullResponse[i] == '\x18') { } else if (fullResponse[i] == '\x18') {
// CAN // CAN
@@ -405,7 +502,7 @@ 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_); adjustForPrint(printableCommand, fullCommand, MAXBUF_);
asynPrint( asynPrint(
@@ -413,7 +510,7 @@ asynStatus masterMacsController::parseResponse(
"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;
@@ -421,7 +518,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)
@@ -445,7 +542,7 @@ 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(this->pasynUserSelf, ASYN_TRACEIO_DRIVER, asynPrint(this->pasynUserSelf, ASYN_TRACEIO_DRIVER,
"Controller \"%s\", axis %d => %s, line " "Controller \"%s\", axis %d => %s, line "
@@ -453,7 +550,7 @@ asynStatus masterMacsController::parseResponse(
"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_,
@@ -462,7 +559,7 @@ 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;
@@ -471,7 +568,7 @@ asynStatus masterMacsController::parseResponse(
asynStatus masterMacsController::readInt32(asynUser *pasynUser, asynStatus masterMacsController::readInt32(asynUser *pasynUser,
epicsInt32 *value) { 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 {
@@ -479,6 +576,10 @@ asynStatus masterMacsController::readInt32(asynUser *pasynUser,
} }
} }
double masterMacsController::comTimeout() { return pMasterMacsC_->comTimeout; }
int masterMacsController::nodeReset() { return pMasterMacsC_->nodeReset; }
/***************************************************************************/ /***************************************************************************/
/** The following functions are C-wrappers, and can be called directly from /** The following functions are C-wrappers, and can be called directly from
* iocsh */ * iocsh */
@@ -538,7 +639,8 @@ static const iocshArg *const CreateControllerArgs[] = {
&CreateControllerArg0, &CreateControllerArg1, &CreateControllerArg2, &CreateControllerArg0, &CreateControllerArg1, &CreateControllerArg2,
&CreateControllerArg3, &CreateControllerArg4, &CreateControllerArg5}; &CreateControllerArg3, &CreateControllerArg4, &CreateControllerArg5};
static const iocshFuncDef configMasterMacsCreateController = { static const iocshFuncDef configMasterMacsCreateController = {
"masterMacsController", 6, CreateControllerArgs}; "masterMacsController", 6, CreateControllerArgs,
"Create a new instance of a MasterMACS controller."};
static void configMasterMacsCreateControllerCallFunc(const iocshArgBuf *args) { static void configMasterMacsCreateControllerCallFunc(const iocshArgBuf *args) {
masterMacsCreateController(args[0].sval, args[1].sval, args[2].ival, masterMacsCreateController(args[0].sval, args[1].sval, args[2].ival,
args[3].dval, args[4].dval, args[5].dval); args[3].dval, args[4].dval, args[5].dval);

View File

@@ -8,11 +8,18 @@
#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>
class masterMacsController : public sinqController { // Forward declaration of the controller class to resolve the cyclic dependency
// between the controller and the axis .h-file. See
// https://en.cppreference.com/w/cpp/language/class.
class HIDDEN masterMacsAxis;
struct HIDDEN masterMacsControllerImpl;
class HIDDEN masterMacsController : public sinqController {
public: public:
/** /**
@@ -31,6 +38,13 @@ class masterMacsController : public sinqController {
int numAxes, double movingPollPeriod, int numAxes, double movingPollPeriod,
double idlePollPeriod, double comTimeout); double idlePollPeriod, double comTimeout);
/**
* @brief Delete the copy and copy assignment constructors, because this
* class should not be copied (it is tied to hardware!)
*/
masterMacsController(const masterMacsController &) = delete;
masterMacsController &operator=(const masterMacsController &) = delete;
/** /**
* @brief Overloaded version of the sinqController version * @brief Overloaded version of the sinqController version
* *
@@ -58,6 +72,17 @@ class masterMacsController : public sinqController {
*/ */
masterMacsAxis *getMasterMacsAxis(int axisNo); masterMacsAxis *getMasterMacsAxis(int axisNo);
/**
* @brief Overloaded function of sinqController
*
* The function is overloaded to allow resetting the node
*
* @param pasynUser Specify the axis via the asynUser
* @param value New value
* @return asynStatus
*/
virtual asynStatus writeInt32(asynUser *pasynUser, epicsInt32 value);
/** /**
* @brief Send a command to the hardware (S mode) * @brief Send a command to the hardware (S mode)
* *
@@ -132,13 +157,13 @@ class masterMacsController : public sinqController {
* *
* @return double Timeout in seconds * @return double Timeout in seconds
*/ */
double comTimeout() { return comTimeout_; } double comTimeout();
// Accessors for additional PVs
int nodeReset();
private: private:
/* std::unique_ptr<masterMacsControllerImpl> pMasterMacsC_;
Stores the constructor input comTimeout
*/
double comTimeout_;
}; };
#endif /* masterMacsController_H */ #endif /* masterMacsController_H */

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,20 +34,7 @@ interpretation = [
("Ok", "STO fault (STO input is on disable state)"), # Bit 15 ("Ok", "STO fault (STO input is on disable state)"), # Bit 15
] ]
if __name__ == "__main__": help = """
from sys import argv
if len(argv) == 1:
# Start interactive mode
interactive()
else:
number = None
try:
number = int(float(argv[1]))
except:
print("""
Decode R11 message of MasterMACs Decode R11 message of MasterMACs
------------------ ------------------
@@ -63,19 +52,39 @@ if __name__ == "__main__":
'value' is the return value of a R11 command. This value is interpreted 'value' is the return value of a R11 command. This value is interpreted
bit-wise and the result is printed out. bit-wise and the result is printed out.
Option 2: CLI Mode Option 2: CLI Mode (Linux-only)
------------------ -------------------------------
Usage: decodeError.py Usage: decodeError.py
ONLY AVAILABLE ON LINUX!
A prompt will be opened. Type in the return value of a R11 command, hit 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, 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. the next value can be typed in. Type 'quit' to close the prompt.
""") """
if __name__ == "__main__":
from sys import argv
if "-h" or "--help" in argv:
print(help)
if len(argv) == 1:
# Start interactive mode
if platform.system() == "Linux":
interactive()
else:
print(help)
else:
number = None
try:
number = int(float(argv[1]))
except:
print(help)
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,20 +35,7 @@ interpretation = [
("Not specified", "Not specified"), # Bit 15 ("Not specified", "Not specified"), # Bit 15
] ]
if __name__ == "__main__": help = """
from sys import argv
if len(argv) == 1:
# Start interactive mode
interactive()
else:
number = None
try:
number = int(float(argv[1]))
except:
print("""
Decode R10 message of MasterMACs Decode R10 message of MasterMACs
------------------ ------------------
@@ -63,17 +53,37 @@ if __name__ == "__main__":
'value' is the return value of a R10 command. This value is interpreted 'value' is the return value of a R10 command. This value is interpreted
bit-wise and the result is printed out. bit-wise and the result is printed out.
Option 2: CLI Mode Option 2: CLI Mode (Linux-only)
------------------ -------------------------------
Usage: decodeStatus.py Usage: decodeStatus.py
ONLY AVAILABLE ON LINUX!
A prompt will be opened. Type in the return value of a R10 command, hit 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, 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. the next value can be typed in. Type 'quit' to close the prompt.
""") """
if __name__ == "__main__":
from sys import argv
if "-h" or "--help" in argv:
print(help)
if len(argv) == 1:
# Start interactive mode
if platform.system() == "Linux":
interactive()
else:
print(help)
else:
number = None
try:
number = int(float(argv[1]))
except:
print(help)
if number is not None: if number is not None:
print("Motor status") print("Motor status")
print("============") print("============")

View File

@@ -6,9 +6,32 @@ 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
@@ -16,6 +39,7 @@ def packMasterMacsCommand(command):
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
@@ -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)
else:
addr = argv[1].split(':') addr = argv[1].split(':')
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((addr[0], int(addr[1]))) s.connect((addr[0], int(addr[1])))
if len(argv) == 3: if len(argv) == 2:
buf = packMasterMacsCommand(argv[2])
s.send(buf)
reply = readMasterMacsReply(s)
print(reply.decode('utf-8') + '\n')
else: if platform.system() == "Linux":
import curses
try:
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.
""")