Compare commits

..

15 Commits

Author SHA1 Message Date
mathis_s 69cad69c6f Updated sinqMotor
Test And Build / Lint (push) Failing after 4s
Test And Build / Build (push) Failing after 8s
2026-03-10 13:22:14 +01:00
mathis_s 703519154a Added homing check to driver 2026-03-10 13:21:34 +01:00
mathis_s 6479642f47 Updated sinqMotor
Test And Build / Lint (push) Failing after 4s
Test And Build / Build (push) Failing after 8s
2026-03-09 13:04:47 +01:00
mathis_s 8ef3e3a381 Added param callback 2026-03-09 11:56:06 +01:00
mathis_s 58b0e9fc77 Updated sinqMotor 2026-03-09 11:37:02 +01:00
mathis_s f0fdedfd5a Updated error message 2026-03-06 14:26:13 +01:00
mathis_s e7ce44fdb6 Test version for the homing check
Test And Build / Lint (push) Failing after 4s
Test And Build / Build (push) Failing after 8s
2026-03-06 11:27:25 +01:00
mathis_s 08ce0999a3 Updated underlying version of motorBase to receive the RVEL bugfix
Test And Build / Lint (push) Failing after 3s
Test And Build / Build (push) Failing after 6s
2026-02-12 12:05:34 +01:00
mathis_s 1d662ecd43 Use safe limit setter function from sinqMotor
Test And Build / Lint (push) Failing after 3s
Test And Build / Build (push) Failing after 6s
2026-02-11 11:08:46 +01:00
mathis_s bea68c807b Updated to new sinqMotor 1.6.0 version 2026-02-11 11:07:28 +01:00
mathis_s 01a04d3b24 Fixed of-by-one error
Test And Build / Lint (push) Failing after 3s
Test And Build / Build (push) Failing after 8s
2026-02-03 14:05:12 +01:00
mathis_s 76cc48a49c Reverted back to sinqMotor 1.5.7
Test And Build / Lint (push) Failing after 4s
Test And Build / Build (push) Failing after 7s
2026-02-03 13:44:21 +01:00
soederqvist_a a9f623ba1d Merge pull request 'Document how to do reset when having HW error' (#1) from fix_hw_error_doc into main
Test And Build / Lint (push) Failing after 3s
Test And Build / Build (push) Failing after 7s
Reviewed-on: #1
2026-02-03 10:07:56 +01:00
mathis_s 791e74f82f Expanded a bit more on the FAQ
Test And Build / Lint (push) Failing after 3s
Test And Build / Build (push) Failing after 7s
2026-02-03 10:02:54 +01:00
soederqvist_a 2dc10fe869 Document how to do reset when having HW error
Test And Build / Lint (push) Failing after 3s
Test And Build / Build (push) Failing after 7s
2026-02-03 09:49:36 +01:00
6 changed files with 113 additions and 54 deletions
+1 -1
View File
@@ -10,7 +10,7 @@ ARCH_FILTER=RHEL%
REQUIRED+=motorBase
# Specify the version of motorBase we want to build against
motorBase_VERSION=7.2.2
motorBase_VERSION=7.3.2
# These headers allow to depend on this library for derived drivers.
HEADERS += src/pmacAsynIPPort.h
+36
View File
@@ -94,3 +94,39 @@ repository is checked out AND the change is commited (`git status` shows no
non-committed changes). Please see the section "Usage as static dependency" in
https://gitea.psi.ch/lin-epics-modules/sinqMotor/src/branch/main/README.md for
more details.
## FAQ
### Axis hardware error at startup
If at IOC startup you are met with the following errors:
```
2026/02/02 16:56:49.470 Controller "turboPmac1", axis 3 => asynStatus turboPmacAxis::handleError(int, char*, int), line 735
Driver hardware error triggered.
2026/02/02 16:56:49.485 Controller "turboPmac1", axis 7 => asynStatus turboPmacAxis::handleError(int, char*, int), line 735
Driver hardware error triggered.
2026/02/02 16:56:49.492 Controller "turboPmac1", axis 8 => asynStatus turboPmacAxis::handleError(int, char*, int), line 735
Driver hardware error triggered.
```
You may need to reset to do a axis reset. This can be done with
`utils/writeRead.py`. The following example does so for axis 3.
Confirm the hardware error:
```
utils/writeRead.py 172.28.87.24:1025 P0301 # 03 specifies axis 3, 01 is the command to read out the error.
# If it returns 13 it's a hardware error.
# For the full list of errors see TurboPMAC_manual.pdf or src/turboPmacAxis.cpp, function "handleError".
```
Reset the axis:
```
utils/writeRead.py 172.28.87.24:1025 P0301=0
```
Check if the error has appeared again:
```
utils/writeRead.py 172.28.87.24:1025 P0301
```
If the console output is not 0, the error has been deleted, but appeared
immediately again. In this case, the error cannot be reset remotely.
If it is an error such as 10 (limit switches hit), the motor needs to be
moved away from the limits, this resets the error automatically.
Other errors like 13 represent an actual issue on the hardware which
needs to be resolved by the electronics motion engineers.
+43 -31
View File
@@ -91,9 +91,9 @@ turboPmacAxis::turboPmacAxis(turboPmacController *pC, int axisNo,
}
// Even though this happens already in sinqAxis, a default value for
// motorMessageText is set here again, because apparently the sinqAxis
// motorErrorMessage is set here again, because apparently the sinqAxis
// constructor is not run before the string is accessed?
status = setStringParam(pC_->motorMessageText(), "");
status = setStringParam(pC_->motorErrorMessage(), "");
if (status != asynSuccess) {
asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d:\nFATAL ERROR "
@@ -140,6 +140,9 @@ asynStatus turboPmacAxis::init() {
// Deadband in counts. Can be a fraction of a count, hence double.
double deadband_counts = 0.0;
// Read from the controller if the motor has been homed before
int hasBeenHomed = 0;
// Offset time for the movement watchdog caused by the air cushions in
// milliseconds.
int acoDelay = 0.0;
@@ -176,9 +179,11 @@ asynStatus turboPmacAxis::init() {
Ixx65.
*/
snprintf(command, sizeof(command),
"Q%2.2d10 Q%2.2d03 Q%2.2d04 Q%2.2d06 P%2.2d22 Q%2.2d00 I%2.2d65",
axisNo_, axisNo_, axisNo_, axisNo_, axisNo_, axisNo_, axisNo_);
status = pC_->writeRead(axisNo_, command, response, 7);
"Q%2.2d10 Q%2.2d03 Q%2.2d04 Q%2.2d06 P%2.2d22 Q%2.2d00 I%2.2d65 "
"M%2.2d45",
axisNo_, axisNo_, axisNo_, axisNo_, axisNo_, axisNo_, axisNo_,
axisNo_);
status = pC_->writeRead(axisNo_, command, response, 8);
msgPrintControlKey keyDisconnected = msgPrintControlKey(
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, 1);
@@ -205,9 +210,14 @@ asynStatus turboPmacAxis::init() {
pC_->getMsgPrintControl().resetCount(keyDisconnected, pC_->pasynUser());
}
nvals = sscanf(response, "%lf %lf %lf %lf %d %lf %lf", &motorPos,
nvals = sscanf(response, "%lf %lf %lf %lf %d %lf %lf %d", &motorPos,
&motorVmax, &motorVelocity, &motorAccel, &acoDelay,
&counts_to_eu, &deadband_counts);
&counts_to_eu, &deadband_counts, &hasBeenHomed);
if (nvals != 8) {
return pC_->couldNotParseResponse(command, response, axisNo_,
__PRETTY_FUNCTION__, __LINE__);
}
// The acoDelay is given in milliseconds -> Convert to seconds, rounded
// up
@@ -218,11 +228,6 @@ asynStatus turboPmacAxis::init() {
// here to mm/s^2.
motorAccel = motorAccel * 1000;
if (nvals != 8) {
return pC_->couldNotParseResponse(command, response, axisNo_,
__PRETTY_FUNCTION__, __LINE__);
}
// Store these values in the parameter library
status = setMotorPosition(motorPos);
if (status != asynSuccess) {
@@ -282,6 +287,9 @@ asynStatus turboPmacAxis::init() {
pTurboPmacA_->needInit = false;
}
// Use the homing status reported by the controller
setAxisParamChecked(this, motorStatusHomed, hasBeenHomed);
return status;
}
@@ -339,7 +347,7 @@ asynStatus turboPmacAxis::doPoll(bool *moving) {
}
if (timedOut) {
setAxisParamChecked(this, motorMessageText,
setAxisParamChecked(this, motorErrorMessage,
"Timed out while waiting for a handshake");
pTurboPmacA_->waitForHandshake = false;
}
@@ -459,7 +467,7 @@ asynStatus turboPmacAxis::doPoll(bool *moving) {
pC_->getMsgPrintControl().getSuffix());
}
resetCountStatus = false;
setAxisParamChecked(this, motorMessageText, "Emergency stop");
setAxisParamChecked(this, motorErrorMessage, "Emergency stop");
break;
case -3:
// Disabled
@@ -552,7 +560,7 @@ asynStatus turboPmacAxis::doPoll(bool *moving) {
userMessage, sizeof(userMessage),
"Reached unknown state P%2.2d00 = %d. Please call the support.",
axisNo_, error);
setAxisParamChecked(this, motorMessageText, userMessage);
setAxisParamChecked(this, motorErrorMessage, userMessage);
}
if (resetCountStatus) {
@@ -582,8 +590,10 @@ asynStatus turboPmacAxis::doPoll(bool *moving) {
getAxisParamChecked(this, limFromHardware, &limFromHardware);
if (limFromHardware != 0) {
setAxisParamChecked(this, motorHighLimitFromDriver, highLimit);
setAxisParamChecked(this, motorLowLimitFromDriver, lowLimit);
status = setLimits(highLimit, lowLimit);
if (status != asynSuccess) {
return status;
}
}
status = setMotorPosition(currentPosition);
@@ -614,7 +624,7 @@ asynStatus turboPmacAxis::handleError(int error, char *userMessage,
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
pC_->getMsgPrintControl().getSuffix());
}
setAxisParamChecked(this, motorMessageText,
setAxisParamChecked(this, motorErrorMessage,
"Target position would exceed software limits");
break;
case 5:
@@ -630,7 +640,7 @@ asynStatus turboPmacAxis::handleError(int error, char *userMessage,
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
pC_->getMsgPrintControl().getSuffix());
}
setAxisParamChecked(this, motorMessageText,
setAxisParamChecked(this, motorErrorMessage,
"Axis received move command while it is still "
"moving. Please call the support.");
break;
@@ -651,7 +661,7 @@ asynStatus turboPmacAxis::handleError(int error, char *userMessage,
"(P%2.2d01 = "
"%d). Please call the support.",
axisNo_, error);
setAxisParamChecked(this, motorMessageText, userMessage);
setAxisParamChecked(this, motorErrorMessage, userMessage);
break;
case 9:
if (pC_->getMsgPrintControl().shouldBePrinted(
@@ -670,7 +680,7 @@ asynStatus turboPmacAxis::handleError(int error, char *userMessage,
"No feedback from auxiliary device before movement (P%2.2d01 = "
"%d). Please call the support.",
axisNo_, error);
setAxisParamChecked(this, motorMessageText, userMessage);
setAxisParamChecked(this, motorErrorMessage, userMessage);
break;
case 10:
/*
@@ -696,7 +706,7 @@ asynStatus turboPmacAxis::handleError(int error, char *userMessage,
"the SPS for errors (if available). "
"Otherwise please call the support.",
axisNo_, error);
setAxisParamChecked(this, motorMessageText, userMessage);
setAxisParamChecked(this, motorErrorMessage, userMessage);
break;
case 11:
// Following error
@@ -716,7 +726,7 @@ asynStatus turboPmacAxis::handleError(int error, char *userMessage,
"Check if movement range is blocked. "
"Otherwise please call the support.",
axisNo_, error);
setAxisParamChecked(this, motorMessageText, userMessage);
setAxisParamChecked(this, motorErrorMessage, userMessage);
break;
case 12:
@@ -734,7 +744,7 @@ asynStatus turboPmacAxis::handleError(int error, char *userMessage,
"for errors (if available). Otherwise please call "
"the support.",
axisNo_, error);
setAxisParamChecked(this, motorMessageText, userMessage);
setAxisParamChecked(this, motorErrorMessage, userMessage);
break;
case 13:
@@ -753,7 +763,7 @@ asynStatus turboPmacAxis::handleError(int error, char *userMessage,
"Driver hardware error (P%2.2d01 = 13). Please call the "
"support.",
axisNo_);
setAxisParamChecked(this, motorMessageText, userMessage);
setAxisParamChecked(this, motorErrorMessage, userMessage);
break;
case 14:
// EPICS should already prevent this issue in the first place,
@@ -771,7 +781,7 @@ asynStatus turboPmacAxis::handleError(int error, char *userMessage,
"Move command exceeds hardware limits (P%2.2d01 = %d). Please "
"call the support.",
axisNo_, error);
setAxisParamChecked(this, motorMessageText, userMessage);
setAxisParamChecked(this, motorErrorMessage, userMessage);
break;
default:
@@ -785,7 +795,7 @@ asynStatus turboPmacAxis::handleError(int error, char *userMessage,
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, axisNo_,
error, pC_->getMsgPrintControl().getSuffix());
}
setAxisParamChecked(this, motorMessageText, userMessage);
setAxisParamChecked(this, motorErrorMessage, userMessage);
break;
}
return status;
@@ -1102,7 +1112,7 @@ asynStatus turboPmacAxis::rereadEncoder() {
"disabled before rereading the encoder.\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__);
setAxisParamChecked(
this, motorMessageText,
this, motorErrorMessage,
"Axis must be disabled before rereading the encoder.");
return asynError;
} else {
@@ -1165,7 +1175,7 @@ asynStatus turboPmacAxis::enable(bool on) {
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
axStatus);
setAxisParamChecked(this, motorMessageText,
setAxisParamChecked(this, motorErrorMessage,
"Axis cannot be disabled while it is moving.");
pTurboPmacA_->enableDisable = false;
return asynError;
@@ -1235,7 +1245,9 @@ asynStatus turboPmacAxis::enable(bool on) {
// Enabling / disabling procedure is completed (successfully)
pTurboPmacA_->enableDisable = false;
return asynSuccess;
// Reinitialize the motor
return init();
}
}
@@ -1250,7 +1262,7 @@ asynStatus turboPmacAxis::enable(bool on) {
// Output message to user
snprintf(command, sizeof(command), "Failed to %s within %d seconds",
on ? "enable" : "disable", timeout_enable_disable);
setAxisParamChecked(this, motorMessageText, command);
setAxisParamChecked(this, motorErrorMessage, command);
// Enabling / disabling procedure failed
pTurboPmacA_->enableDisable = false;
+1 -1
View File
@@ -445,7 +445,7 @@ asynStatus turboPmacController::writeRead(int axisNo, const char *command,
getAxisParamChecked(axis, motorStatusProblem, &motorStatusProblem);
if (motorStatusProblem == 0) {
setAxisParamChecked(axis, motorMessageText, drvMessageText);
setAxisParamChecked(axis, motorErrorMessage, drvMessageText);
setAxisParamChecked(axis, motorStatusProblem, true);
setAxisParamChecked(axis, motorStatusCommsError, true);
}
+31 -20
View File
@@ -9,45 +9,55 @@ Stefan Mathis, December 2024
import struct
import socket
import curses
import time
def packPmacCommand(command):
# 0x40 = VR_DOWNLOAD
# 0x40 = VR_DOWNLOAD
# 0xBF = VR_PMAC_GETRESPONSE
buf = struct.pack('BBHHH',0x40,0xBF,0,0,socket.htons(len(command)))
buf = buf + bytes(command,'utf-8')
buf = struct.pack('BBHHH', 0x40, 0xBF, 0, 0, socket.htons(len(command)))
buf = buf + command.encode('utf-8')
return buf
def readPmacReply(input):
def readPmacReply(sock):
msg = bytearray()
expectAck = True
sock.settimeout(2.0)
while True:
b = input.recv(1)
bint = int.from_bytes(b,byteorder='little')
if bint == 2 or bint == 7: #STX or BELL
try:
b = sock.recv(1)
except socket.timeout:
print('Timed out')
return None
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:
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 = packPmacCommand(argv[2])
s.send(buf)
reply = readPmacReply(s)
print(reply.decode('utf-8') + '\n')
if reply:
print(reply.decode('utf-8') + '\n')
else:
@@ -81,13 +91,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':
@@ -113,7 +123,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
@@ -126,18 +136,20 @@ 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()
@@ -169,4 +181,3 @@ if __name__ == "__main__":
the reponse, before being prompted to again enter a command. Type
'quit' to close prompt.
""")