6 Commits
1.5.1 ... 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
8 changed files with 275 additions and 191 deletions

4
.gitignore vendored Normal file
View File

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

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.
- 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
masterMacs exposes the following IOC shell functions (all in masterMacsController.cpp):
masterMacs exposes the following IOC shell functions:
- `masterMacsController`: Create a new controller 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
@@ -62,6 +85,8 @@ dbLoadTemplate("$(TOP)/$(NAME).substitutions", "INSTR=$(INSTR)$(NAME):,CONTROLLE
dbLoadRecords("$(masterMacs_DB)/asynRecord.db","P=$(INSTR)$(NAME),PORT=$(ASYN_PORT)")
```
## Developer guide
### Versioning
Please see the documentation for the module sinqMotor: https://git.psi.ch/sinq-epics-modules/sinqmotor/-/blob/main/README.md.

View File

@@ -916,7 +916,7 @@ asynStatus masterMacsAxis::readEncoderType() {
/*
Defined encoder IDs:
0=INC (Incremental)
0=INC (Incremental or no encoder)
1=SSI (Absolute encoder with SSI interface)
2=SSI (Absolute encoder with BiSS interface)
*/

View File

@@ -148,163 +148,191 @@ class HIDDEN masterMacsAxis : public sinqAxis {
/**
* @brief Read the Master MACS status with the xR10 command and store
* the result in axisStatus_
* the result in axisStatus (see masterMacsAxisImpl redefinition in
* masterMacsAxis.cpp)
*
*/
asynStatus readAxisStatus();
/*
The functions below read the specified status bit from the axisStatus
bitset. Since a bit can either be 0 or 1, the return value is given as a
boolean.
The functions below read the specified status bit from the axisStatus (see
masterMacsAxisImpl redefinition in masterMacsAxis.cpp) bitset. Since a bit
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();
/**
* @brief Read the property from axisStatus_
* @brief Read the property from axisStatus (see masterMacsAxisImpl
* redefinition in masterMacsAxis.cpp)
*/
bool switchedOn();
// Bit 2 is unused
/**
* @brief Read the property from axisStatus_
* @brief Read the property from axisStatus (see masterMacsAxisImpl
* redefinition in masterMacsAxis.cpp)
*/
bool faultConditionSet();
/**
* @brief Read the property from axisStatus_
* @brief Read the property from axisStatus (see masterMacsAxisImpl
* redefinition in masterMacsAxis.cpp)
*/
bool voltagePresent();
/**
* @brief Read the property from axisStatus_
* @brief Read the property from axisStatus (see masterMacsAxisImpl
* redefinition in masterMacsAxis.cpp)
*/
bool quickStopping();
/**
* @brief Read the property from axisStatus_
* @brief Read the property from axisStatus (see masterMacsAxisImpl
* redefinition in masterMacsAxis.cpp)
*/
bool switchOnDisabled();
/**
* @brief Read the property from axisStatus_
* @brief Read the property from axisStatus (see masterMacsAxisImpl
* redefinition in masterMacsAxis.cpp)
*/
bool warning();
// Bit 8 is unused
/**
* @brief Read the property from axisStatus_
* @brief Read the property from axisStatus (see masterMacsAxisImpl
* redefinition in masterMacsAxis.cpp)
*/
bool remoteMode();
/**
* @brief Read the property from axisStatus_
* @brief Read the property from axisStatus (see masterMacsAxisImpl
* redefinition in masterMacsAxis.cpp)
*/
bool targetReached();
/**
* @brief Read the property from axisStatus_
* @brief Read the property from axisStatus (see masterMacsAxisImpl
* redefinition in masterMacsAxis.cpp)
*/
bool internalLimitActive();
// 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();
/**
* @brief Read the property from axisStatus_
* @brief Read the property from axisStatus (see masterMacsAxisImpl
* redefinition in masterMacsAxis.cpp)
*/
bool powerEnabled();
/**
* @brief Read the Master MACS status with the xR10 command and store
* the result in axisStatus_
* @brief Read the Master MACS error with the xR10 command and store
* the result in axisError (see masterMacsAxisImpl redefinition in
* masterMacsAxis.cpp)
*
*/
asynStatus readAxisError();
/*
The functions below read the specified error bit from the axisError_
bitset. Since a bit can either be 0 or 1, the return value is given as a
boolean.
The functions below read the specified error bit from the axisError (see
masterMacsAxisImpl redefinition in masterMacsAxis.cpp) bitset. Since a bit
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();
/**
* @brief Read the property from axisError_
* @brief Read the property from axisError (see masterMacsAxisImpl
* redefinition in masterMacsAxis.cpp)
*/
bool encoderError();
/**
* @brief Read the property from axisError_
* @brief Read the property from axisError (see masterMacsAxisImpl
* redefinition in masterMacsAxis.cpp)
*/
bool followingError();
/**
* @brief Read the property from axisError_
* @brief Read the property from axisError (see masterMacsAxisImpl
* redefinition in masterMacsAxis.cpp)
*/
bool communicationError();
/**
* @brief Read the property from axisError_
* @brief Read the property from axisError (see masterMacsAxisImpl
* redefinition in masterMacsAxis.cpp)
*/
bool feedbackError();
/**
* @brief Read the property from axisError_
* @brief Read the property from axisError (see masterMacsAxisImpl
* redefinition in masterMacsAxis.cpp)
*/
bool positiveLimitSwitch();
/**
* @brief Read the property from axisError_
* @brief Read the property from axisError (see masterMacsAxisImpl
* redefinition in masterMacsAxis.cpp)
*/
bool negativeLimitSwitch();
/**
* @brief Read the property from axisError_
* @brief Read the property from axisError (see masterMacsAxisImpl
* redefinition in masterMacsAxis.cpp)
*/
bool positiveSoftwareLimit();
/**
* @brief Read the property from axisError_
* @brief Read the property from axisError (see masterMacsAxisImpl
* redefinition in masterMacsAxis.cpp)
*/
bool negativeSoftwareLimit();
/**
* @brief Read the property from axisError_
* @brief Read the property from axisError (see masterMacsAxisImpl
* redefinition in masterMacsAxis.cpp)
*/
bool overCurrent();
/**
* @brief Read the property from axisError_
* @brief Read the property from axisError (see masterMacsAxisImpl
* redefinition in masterMacsAxis.cpp)
*/
bool overTemperature();
/**
* @brief Read the property from axisError_
* @brief Read the property from axisError (see masterMacsAxisImpl
* redefinition in masterMacsAxis.cpp)
*/
bool overVoltage();
/**
* @brief Read the property from axisError_
* @brief Read the property from axisError (see masterMacsAxisImpl
* redefinition in masterMacsAxis.cpp)
*/
bool underVoltage();
/**
* @brief Read the property from axisError_
* @brief Read the property from axisError (see masterMacsAxisImpl
* redefinition in masterMacsAxis.cpp)
*/
bool stoFault();

View File

@@ -9,73 +9,82 @@ To read the manual, simply run this script without any arguments.
Stefan Mathis, January 2025
"""
import platform
from decodeCommon import interactive, decode, print_decoded
# List of tuples which encodes the states given in the file description.
# Index first with the bit index, then with the bit value
interpretation = [
("Not specified", "Not specified"), # Bit 0
("Ok", "Short circuit"), # Bit 1
("Ok", "Encoder error"), # Bit 2
("Ok", "Following error"), # Bit 3
("Ok", "Communication error"), # Bit 4
("Ok", "Feedback error"), # Bit 5
("Ok", "Positive limit switch hit"), # Bit 6
("Ok", "Negative limit switch hit"), # Bit 7
("Ok", "Positive software limit hit"), # Bit 8
("Ok", "Negative software limit hit"), # Bit 9
("Ok", "Over-current"), # Bit 10
("Ok", "Over-temperature drive"), # Bit 11
("Ok", "Over-voltage"), # Bit 12
("Ok", "Under-voltage"), # Bit 13
("Not specified", "Not specified"), # Bit 14
("Ok", "STO fault (STO input is on disable state)"), # Bit 15
("Not specified", "Not specified"), # Bit 0
("Ok", "Short circuit"), # Bit 1
("Ok", "Encoder error"), # Bit 2
("Ok", "Following error"), # Bit 3
("Ok", "Communication error"), # Bit 4
("Ok", "Feedback error"), # Bit 5
("Ok", "Positive limit switch hit"), # Bit 6
("Ok", "Negative limit switch hit"), # Bit 7
("Ok", "Positive software limit hit"), # Bit 8
("Ok", "Negative software limit hit"), # Bit 9
("Ok", "Over-current"), # Bit 10
("Ok", "Over-temperature drive"), # Bit 11
("Ok", "Over-voltage"), # Bit 12
("Ok", "Under-voltage"), # Bit 13
("Not specified", "Not specified"), # Bit 14
("Ok", "STO fault (STO input is on disable state)"), # Bit 15
]
help = """
Decode R11 message of MasterMACs
------------------
MasterMACs returns its error message (R11) as a floating-point number.
The bits of this float encode different states. These states are stored
in the interpretation variable.
This script can be used in two different ways:
Option 1: Single Command
------------------------
Usage: decodeError.py value
'value' is the return value of a R11 command. This value is interpreted
bit-wise and the result is printed out.
Option 2: CLI Mode (Linux-only)
-------------------------------
Usage: decodeError.py
ONLY AVAILABLE ON LINUX!
A prompt will be opened. Type in the return value of a R11 command, hit
enter and the interpretation will be printed in the prompt. After that,
the next value can be typed in. Type 'quit' to close the prompt.
"""
if __name__ == "__main__":
from sys import argv
if "-h" or "--help" in argv:
print(help)
if len(argv) == 1:
# Start interactive mode
interactive()
if platform.system() == "Linux":
interactive()
else:
print(help)
else:
number = None
try:
number = int(float(argv[1]))
except:
print("""
Decode R11 message of MasterMACs
------------------
MasterMACs returns its error message (R11) as a floating-point number.
The bits of this float encode different states. These states are stored
in the interpretation variable.
This script can be used in two different ways:
Option 1: Single Command
------------------------
Usage: decodeError.py value
'value' is the return value of a R11 command. This value is interpreted
bit-wise and the result is printed out.
Option 2: CLI Mode
------------------
Usage: decodeError.py
A prompt will be opened. Type in the return value of a R11 command, hit
enter and the interpretation will be printed in the prompt. After that,
the next value can be typed in. Type 'quit' to close the prompt.
""")
print(help)
if number is not None:
print("Motor error")
print("============")
print("===========")
(bit_list, interpreted) = decode(number, interpretation)
print_decoded(bit_list, interpreted)

View File

@@ -9,71 +9,81 @@ To read the manual, simply run this script without any arguments.
Stefan Mathis, December 2024
"""
import platform
from decodeCommon import interactive, decode, print_decoded
# List of tuples which encodes the states given in the file description.
# Index first with the bit index, then with the bit value
interpretation = [
("Not ready to be switched on", "Ready to be switched on"), # Bit 0
("Not switched on", "Switched on"), # Bit 1
("Disabled", "Enabled"), # Bit 2
("Ok", "Fault condition set"), # Bit 3
("Motor supply voltage absent ", "Motor supply voltage present"), # Bit 4
("Motor performs quick stop", "Ok"), # Bit 5
("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
("Not specified", "Not specified"), # Bit 8
("Motor does not execute command messages (local mode)", "Motor does execute command messages (remote mode)"), # Bit 9
("Target not reached", "Target reached"), # Bit 10
("Ok", "Internal limit active (current, voltage, velocity or position)"), # Bit 11
("Not specified", "Not specified"), # Bit 12
("Not specified", "Not specified"), # Bit 13
("Not specified", "Not specified"), # Bit 14
("Not specified", "Not specified"), # Bit 15
("Not ready to be switched on", "Ready to be switched on"), # Bit 0
("Not switched on", "Switched on"), # Bit 1
("Disabled", "Enabled"), # Bit 2
("Ok", "Fault condition set"), # Bit 3
("Motor supply voltage absent ", "Motor supply voltage present"), # Bit 4
("Motor performs quick stop", "Ok"), # Bit 5
("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
("Not specified", "Not specified"), # Bit 8
("Motor does not execute command messages (local mode)",
"Motor does execute command messages (remote mode)"), # Bit 9
("Target not reached", "Target reached"), # Bit 10
("Ok", "Internal limit active (current, voltage, velocity or position)"), # Bit 11
("Not specified", "Not specified"), # Bit 12
("Not specified", "Not specified"), # Bit 13
("Not specified", "Not specified"), # Bit 14
("Not specified", "Not specified"), # Bit 15
]
help = """
Decode R10 message of MasterMACs
------------------
MasterMACs returns its status message (R10) as a floating-point number.
The bits of this float encode different states. These states are stored
in the interpretation variable.
This script can be used in two different ways:
Option 1: Single Command
------------------------
Usage: decodeStatus.py value
'value' is the return value of a R10 command. This value is interpreted
bit-wise and the result is printed out.
Option 2: CLI Mode (Linux-only)
-------------------------------
Usage: decodeStatus.py
ONLY AVAILABLE ON LINUX!
A prompt will be opened. Type in the return value of a R10 command, hit
enter and the interpretation will be printed in the prompt. After that,
the next value can be typed in. Type 'quit' to close the prompt.
"""
if __name__ == "__main__":
from sys import argv
if "-h" or "--help" in argv:
print(help)
if len(argv) == 1:
# Start interactive mode
interactive()
if platform.system() == "Linux":
interactive()
else:
print(help)
else:
number = None
try:
number = int(float(argv[1]))
except:
print("""
Decode R10 message of MasterMACs
------------------
MasterMACs returns its status message (R10) as a floating-point number.
The bits of this float encode different states. These states are stored
in the interpretation variable.
This script can be used in two different ways:
Option 1: Single Command
------------------------
Usage: decodeStatus.py value
'value' is the return value of a R10 command. This value is interpreted
bit-wise and the result is printed out.
Option 2: CLI Mode
------------------
Usage: decodeStatus.py
A prompt will be opened. Type in the return value of a R10 command, hit
enter and the interpretation will be printed in the prompt. After that,
the next value can be typed in. Type 'quit' to close the prompt.
""")
print(help)
if number is not None:
print("Motor status")
print("============")

View File

@@ -6,51 +6,72 @@ To read the manual, simply run this script without any arguments.
Stefan Mathis, April 2025
"""
import platform
import struct
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):
# 0x0D = Carriage return
buf = struct.pack('B',0x0D)
buf = bytes(command,'utf-8') + buf
return bytes(command,'utf-8')
# 0x0D = Carriage return
buf = struct.pack('B', 0x0D)
buf = bytes(command, 'utf-8') + buf
return bytes(command, 'utf-8')
def readMasterMacsReply(input):
msg = bytearray()
expectAck = True
while True:
b = input.recv(1)
bint = int.from_bytes(b,byteorder='little')
if bint == 2 or bint == 7: #STX or BELL
bint = int.from_bytes(b, byteorder='little')
if bint == 2 or bint == 7: # STX or BELL
expectAck = False
continue
if expectAck and bint == 6: # ACK
if expectAck and bint == 6: # ACK
return bytes(msg)
else:
if bint == 13 and not expectAck: # CR
if bint == 13 and not expectAck: # CR
return bytes(msg)
else:
msg.append(bint)
if __name__ == "__main__":
from sys import argv
try:
if "-h" or "--help" in argv:
print(help)
else:
addr = argv[1].split(':')
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect((addr[0],int(addr[1])))
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((addr[0], int(addr[1])))
if len(argv) == 3:
buf = packMasterMacsCommand(argv[2])
s.send(buf)
reply = readMasterMacsReply(s)
print(reply.decode('utf-8') + '\n')
if len(argv) == 2:
else:
try:
if platform.system() == "Linux":
import curses
stdscr = curses.initscr()
curses.noecho()
@@ -80,13 +101,13 @@ if __name__ == "__main__":
if ptr > 0:
ptr -= 1
stdscr.addch("\r")
stdscr.clrtoeol()
stdscr.clrtoeol()
stdscr.addstr(">> " + history[ptr])
elif c == curses.KEY_DOWN:
if ptr < len(history) - 1:
ptr += 1
stdscr.addch("\r")
stdscr.clrtoeol()
stdscr.clrtoeol()
stdscr.addstr(">> " + history[ptr])
elif c == curses.KEY_ENTER or c == ord('\n') or c == ord('\r'):
if history[ptr] == 'quit':
@@ -112,7 +133,7 @@ if __name__ == "__main__":
stdscr.refresh()
else:
if ptr < len(history) - 1: # Modifying previous input
if ptr < len(history) - 1: # Modifying previous input
if len(history[-1]) == 0:
history[-1] = history[ptr]
ptr = len(history) - 1
@@ -125,47 +146,34 @@ if __name__ == "__main__":
if len(history[ptr]) == 0:
continue
(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.clrtoeol()
stdscr.clrtoeol()
stdscr.addstr(">> " + history[ptr])
stdscr.move(y, x-1)
stdscr.refresh()
else:
(y, x) = stdscr.getyx()
history[ptr] = history[ptr][0:x-3] + chr(c) + history[ptr][x-3:]
history[ptr] = history[ptr][0:x-3] + \
chr(c) + history[ptr][x-3:]
stdscr.addch("\r")
stdscr.clrtoeol()
stdscr.clrtoeol()
stdscr.addstr(">> " + history[ptr])
stdscr.move(y, x+1)
stdscr.refresh()
finally:
# to quit
curses.nocbreak()
stdscr.keypad(False)
curses.echo()
curses.endwin()
except:
print("""
Invalid Arguments
Option 1: Single Command
------------------------
Usage: writeRead.py pmachost:port command
This then returns the response for command.
Option 2: CLI Mode
------------------
Usage: writeRead.py pmachost:port
You can then type in a command, hit enter, and the response will see
the reponse, before being prompted to again enter a command. Type
'quit' to close prompt.
""")
# to quit
curses.nocbreak()
stdscr.keypad(False)
curses.echo()
curses.endwin()
else:
print(help)
elif len(argv) == 3:
buf = packMasterMacsCommand(argv[2])
s.send(buf)
reply = readMasterMacsReply(s)
print(reply.decode('utf-8') + '\n')
else:
print(help)