Merge branch 'SINQSW-86-read-after-motor-reconnection' into 'master'

SINQSW-86 fix build error

See merge request sinqdev/sinqepicsapp!2
This commit is contained in:
2024-09-10 09:21:15 +02:00
8 changed files with 317 additions and 83 deletions

1
.gitignore vendored
View File

@ -66,6 +66,7 @@ local.properties
*.user *.user
*.sln.docstates *.sln.docstates
*.sdf *.sdf
.cvsignore
#Test results #Test results
*.log *.log

View File

@ -15,7 +15,7 @@ REQUIRED+=asynMotor
# using a test version # using a test version
#scaler_VERSION=2024 #scaler_VERSION=2024
LIBVERSION=2024-dev LIBVERSION=2024-v2
TEMPLATES += sinqEPICSApp/Db/dimetix.db TEMPLATES += sinqEPICSApp/Db/dimetix.db
TEMPLATES += sinqEPICSApp/Db/slsvme.db TEMPLATES += sinqEPICSApp/Db/slsvme.db

View File

@ -46,3 +46,18 @@ record(longin, "$(P)$(M):Enable_RBV") {
field(SCAN, "1 second") field(SCAN, "1 second")
} }
# reread encoder
record(longout, "$(P)$(M):Reread_Encoder") {
field(DTYP, "asynInt32")
field(OUT, "@asyn($(PORT),$(N),1) REREAD_ENCODER_POSITION")
field(PINI, "YES")
}
# reread encoder
record(longin, "$(P)$(M):Reread_Encoder_RBV") {
field(DTYP, "asynInt32")
field(INP, "@asyn($(PORT),$(N),1) REREAD_ENCODER_POSITION_RBV")
field(PINI, "NO")
field(SCAN, "1 second")
}

View File

@ -925,6 +925,7 @@ asynStatus pmacV3Axis::poll(bool *moving) {
} }
callParamCallbacks(); callParamCallbacks();
return status ? asynError : asynSuccess; return status ? asynError : asynSuccess;
} }
@ -950,10 +951,11 @@ asynStatus pmacV3Axis::getAxisStatus(bool *moving) {
if (cmdStatus || nvals != 3) { if (cmdStatus || nvals != 3) {
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR, asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
"drvPmacAxisGetStatus: Failed to read position and status, " "drvPmacAxisGetStatus: Failed to read position and status, "
"Status: %d\nCommand :%s\nResponse:%s\n", "Status: %d\nCommand :%s\nParsed: %d\nResponse:%s\n",
cmdStatus, command, response); cmdStatus, command, nvals, response);
updateMsgTxtFromDriver("Cannot read Axis position and status"); updateMsgTxtFromDriver("Cannot read Axis position and status");
} }
pmacV3Controller *p3C_ = (pmacV3Controller *)pC_; pmacV3Controller *p3C_ = (pmacV3Controller *)pC_;
IsEnable = axStat != -3; IsEnable = axStat != -3;
@ -1063,6 +1065,7 @@ asynStatus pmacV3Axis::getAxisStatus(bool *moving) {
cmdStatus = cmdStatus > st ? cmdStatus : st; cmdStatus = cmdStatus > st ? cmdStatus : st;
nvals = sscanf(response, "%d %d %d", &axErr, &highLim, &lowLim); nvals = sscanf(response, "%d %d %d", &axErr, &highLim, &lowLim);
if (st || nvals != 3) { if (st || nvals != 3) {
asynPrint( asynPrint(
pC_->pasynUserSelf, ASYN_TRACE_ERROR, pC_->pasynUserSelf, ASYN_TRACE_ERROR,
@ -1073,9 +1076,6 @@ asynStatus pmacV3Axis::getAxisStatus(bool *moving) {
"Cannot read Axis Error, high and low limit flag Status"); "Cannot read Axis Error, high and low limit flag Status");
} }
// errlogPrintf("Polling, axStat = %d, axErr = %d, position = %f\n", axStat,
// axErr, position);
sprintf(message, "poll results: axis %d, status %d, axErr %d, done = %d", sprintf(message, "poll results: axis %d, status %d, axErr %d, done = %d",
axisNo_, axStat, axErr, done); axisNo_, axStat, axErr, done);
pC_->debugFlow(message); pC_->debugFlow(message);

View File

@ -498,24 +498,20 @@ return pAxes_[axisNo];
/** Polls the controller, rather than individual axis.*/ /** Polls the controller, rather than individual axis.*/
asynStatus pmacController::poll() asynStatus pmacController::poll()
{ {
static const char *functionName = "pmacController::poll"; static const char *functionName = "pmacController::poll";
debugFlow(functionName); debugFlow(functionName);
if (!lowLevelPortUser_) { if (!lowLevelPortUser_) {
setIntegerParam(this->motorStatusCommsError_, 1); setIntegerParam(this->motorStatusCommsError_, 1);
return asynError; return asynError;
}
asynMotorController::poll();
return callParamCallbacks();
} }
asynMotorController::poll();
callParamCallbacks();
return asynSuccess;
}
/*************************************************************************************/ /*************************************************************************************/
/** The following functions have C linkage, and can be called directly or from iocsh */ /** The following functions have C linkage, and can be called directly or from iocsh */
@ -571,17 +567,22 @@ createParam(MotorSetPositionString, asynParamFloat64, &se
callParamCallbacks(); callParamCallbacks();
} }
pmacV3Controller::pmacV3Controller(const char *portName, pmacV3Controller::pmacV3Controller(
const char *lowLevelPortName, const char *portName,
int lowLevelPortAddress, int numAxes, const char *lowLevelPortName,
double movingPollPeriod, int lowLevelPortAddress, int numAxes,
double idlePollPeriod, double movingPollPeriod,
const int &extraParams) : double idlePollPeriod,
pmacController(portName, lowLevelPortName, lowLevelPortAddress, numAxes, movingPollPeriod, idlePollPeriod, extraParams) { const int &extraParams
static const char *functionName = "pmacV3Controller::pmacV3Controller"; ) : pmacController(portName, lowLevelPortName, lowLevelPortAddress, numAxes, movingPollPeriod, idlePollPeriod, extraParams) {
createParam(EnableAxisString, asynParamInt32, &enableAxis_);
createParam(AxisEnabledString, asynParamInt32, &axisEnabled_); static const char *functionName = "pmacV3Controller::pmacV3Controller";
callParamCallbacks(); createParam(EnableAxisString, asynParamInt32, &enableAxis_);
createParam(AxisEnabledString, asynParamInt32, &axisEnabled_);
createParam(ReReadEncoderPositionString, asynParamInt32, &rereadEncoderPosition_);
createParam(ReReadEncoderPositionString_RBV, asynParamInt32, &rereadEncoderPositionRBV_);
callParamCallbacks();
} }
/** /**
@ -871,19 +872,20 @@ return status;
asynStatus pmacV3Controller::writeInt32(asynUser *pasynUser, epicsInt32 value) { asynStatus pmacV3Controller::writeInt32(asynUser *pasynUser, epicsInt32 value) {
int function = pasynUser->reason;
asynStatus status = asynSuccess; int function = pasynUser->reason;
pmacV3Axis *pAxis = NULL; asynStatus status = asynSuccess;
char command[64] = {0}; pmacV3Axis *pAxis = NULL;
char response[64] = {0}; char command[64] = {0};
int isOn; char response[64] = {0};
time_t startTime; int isOn;
bool moving; time_t startTime;
static const char *functionName = "pmacV3Controller::writeInt32"; bool moving;
static const char *functionName = "pmacV3Controller::writeInt32";
debugFlow(functionName); debugFlow(functionName);
pAxis = (pmacV3Axis*)this->getAxis(pasynUser); pAxis = (pmacV3Axis*) this->getAxis(pasynUser);
if (!pAxis) { if (!pAxis) {
return asynError; return asynError;
} }
@ -893,43 +895,125 @@ static const char *functionName = "pmacV3Controller::writeInt32";
pAxis->setIntegerParam(function, value); pAxis->setIntegerParam(function, value);
if (function == enableAxis_) { if (function == enableAxis_) {
// only send commands when a state change is necessary
// Get Current Status
snprintf(command, sizeof(command), "P%2.2d00", pAxis->axisNo_); snprintf(command, sizeof(command), "P%2.2d00", pAxis->axisNo_);
this->lowLevelWriteRead(pAxis->axisNo_, command, response); this->lowLevelWriteRead(pAxis->axisNo_, command, response);
isOn = strtol(response, NULL, 10) != -3; isOn = strtol(response, NULL, 10) != -3;
/* This was an attempt at automatically reading the encoder. asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW,
This is disabled as it did not work. "%s: Axis %d on controller %s %s on\n",
*/ functionName, pAxis->axisNo_, portName, isOn ? "is" : "isn't");
if(value == 1 && isOn) { usleep(100);
sprintf(command, "M%2.2d=15", pAxis->axisNo_);
// lowLevelWriteRead(pAxis->axisNo_, command, response);
}
// Valid code continues here
sprintf(command, "M%2.2d14=%d", pAxis->axisNo_, value);
pAxis->updateMsgTxtFromDriver("");
if(isOn != value) {
asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW,
"%s: Enable axis on controller %s, axis %d enable=%d\n",
functionName, portName, pAxis->axisNo_, value);
lowLevelWriteRead(pAxis->axisNo_, command, response);
startTime = time(NULL);
while(time(NULL) < startTime + 2.){
snprintf(command, sizeof(command), "P%2.2d00", pAxis->axisNo_);
this->lowLevelWriteRead(pAxis->axisNo_, command, response);
isOn = strtol(response, NULL, 10) != -3;
if(value == isOn){
pAxis->poll(&moving); // to update enable status
return asynSuccess;
}
usleep(100);
}
errlogPrintf("PMAC: failed to switch axis %d enable status within 2 seconds\n",
pAxis->axisNo_);
return asynError;
}
}
return pmacController::writeInt32(pasynUser, value); if (isOn == value) {
return asynSuccess;
}
// Switch On/Off
snprintf(command, sizeof(command), "M%2.2d14=%d", pAxis->axisNo_, value);
asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW,
"%s: %s axis %d on controller %s\n",
functionName, value ? "Enable" : "Disable", pAxis->axisNo_, portName);
this->lowLevelWriteRead(pAxis->axisNo_, command, response);
// Wait for motor status to change
snprintf(command, sizeof(command), "P%2.2d00", pAxis->axisNo_);
startTime = time(NULL);
while (time(NULL) < startTime + 2.) {
usleep(100);
this->lowLevelWriteRead(pAxis->axisNo_, command, response);
isOn = strtol(response, NULL, 10) != -3;
if(isOn == value) {
pAxis->poll(&moving); // to update enable status
return asynSuccess;
}
}
errlogPrintf(
"PMAC: Failed to %s axis %d on controller %s within 2 seconds\n",
value ? "enable" : "disable", pAxis->axisNo_, portName);
return asynError;
} else if (function == rereadEncoderPosition_) {
// This is not a command that can always be run when enabling a motor as it
// also causes relative encoders to reread a position necessitating
// recalibration. We only want it to run on absolute encoders. We also want
// it to be clear to instrument scientists, that power has to be cut to the
// motor, in order to reread the encoder as not all motors have breaks and
// they may start to move when disabled. For that reason, we don't
// automatically disable the motors to run the command and instead require
// that the scientists first disable the motor.
if (!value) {
return asynSuccess;
}
// Check if this is an aboslute encoder
snprintf(command, sizeof(command), "I%2.2X04", pAxis->axisNo_);
this->lowLevelWriteRead(pAxis->axisNo_, command, response);
int reponse_length = strlen(response);
if (reponse_length < 3) {
errlogPrintf(
"%s: Unexpected reponse '%s' from axis %d on controller %s while rereading encoder. Aborting...\n",
functionName, response, pAxis->axisNo_, portName);
return asynError;
}
// We are only interested in the last two digits and the last value in the
// string before the terminator is \r
int encoder_id = 0;
sscanf(response + (reponse_length - 3), "%2X", &encoder_id);
snprintf(command, sizeof(command), "P46", pAxis->axisNo_);
this->lowLevelWriteRead(pAxis->axisNo_, command, response);
int number_of_axes = strtol(response, NULL, 10);
if (encoder_id <= number_of_axes) {
errlogPrintf(
"%s: Trying to reread absolute encoder of axis %d on controller %s, but it is a relative encoder. Aborting...\n",
functionName, pAxis->axisNo_, portName);
return asynError;
}
// Get Current Status
snprintf(command, sizeof(command), "P%2.2d00", pAxis->axisNo_);
this->lowLevelWriteRead(pAxis->axisNo_, command, response);
isOn = strtol(response, NULL, 10) != -3;
asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW,
"%s: Axis %d on controller %s %s on\n",
functionName, pAxis->axisNo_, portName, isOn ? "is" : "isn't");
if (isOn) {
errlogPrintf(
"%s: First disable axis %d on controller %s to reread encoder position\n",
functionName, pAxis->axisNo_, portName);
return asynError;
}
// Reread current absolute position if possible
sprintf(command, "M%2.2d=15", pAxis->axisNo_);
asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW,
"%s: Rereading Absolute Encoder of axis %d on controller %s via command %s\n",
functionName, pAxis->axisNo_, portName, command);
this->lowLevelWriteRead(pAxis->axisNo_, command, response);
// Switching on the axis while the rereading process is still ongoing
// causes it to fail. We currently have no way to check if it is actually
// finished, so we instead wait for 0.5 seconds.
usleep(500000);
// turn off parameter as finished rereading
// this will only be immediately noticed in the read back variable though
pAxis->setIntegerParam(function, 0);
return asynSuccess;
} else {
return pmacController::writeInt32(pasynUser, value);
}
} }
asynStatus pmacV3Controller::readInt32(asynUser *pasynUser, epicsInt32 *value) { asynStatus pmacV3Controller::readInt32(asynUser *pasynUser, epicsInt32 *value) {
@ -954,7 +1038,12 @@ asynStatus pmacV3Controller::readInt32(asynUser *pasynUser, epicsInt32 *value) {
axStat = *value != -3; axStat = *value != -3;
int st = setIntegerParam(axisEnabled_, axStat); int st = setIntegerParam(axisEnabled_, axStat);
callParamCallbacks(); callParamCallbacks();
} else if (function == rereadEncoderPositionRBV_) {
getIntegerParam(pAxis->axisNo_, rereadEncoderPosition_, value);
setIntegerParam(pAxis->axisNo_, rereadEncoderPositionRBV_, *value);
return callParamCallbacks();
} }
return pmacController::readInt32(pasynUser, value); return pmacController::readInt32(pasynUser, value);
} }

View File

@ -168,12 +168,14 @@ class SeleneController : public pmacController {
#define EnableAxisString "ENABLE_AXIS" #define EnableAxisString "ENABLE_AXIS"
#define AxisEnabledString "AXIS_ENABLED" #define AxisEnabledString "AXIS_ENABLED"
#define ReReadEncoderPositionString "REREAD_ENCODER_POSITION"
#define ReReadEncoderPositionString_RBV "REREAD_ENCODER_POSITION_RBV"
class pmacV3Controller : public pmacController { class pmacV3Controller : public pmacController {
public: public:
pmacV3Controller(const char *portName, const char *lowLevelPortName, pmacV3Controller(const char *portName, const char *lowLevelPortName,
int lowLevelPortAddress, int numAxes, double movingPollPeriod, int lowLevelPortAddress, int numAxes, double movingPollPeriod,
double idlePollPeriod, const int &extraParams = 2); double idlePollPeriod, const int &extraParams = 4);
// overloaded because we want to enable/disable the motor // overloaded because we want to enable/disable the motor
asynStatus writeInt32(asynUser *pasynUser, epicsInt32 value); asynStatus writeInt32(asynUser *pasynUser, epicsInt32 value);
@ -190,6 +192,8 @@ protected:
int enableAxis_; int enableAxis_;
int axisEnabled_; int axisEnabled_;
int rereadEncoderPosition_;
int rereadEncoderPositionRBV_;
}; };
#endif /* pmacController_H */ #endif /* pmacController_H */

View File

@ -7,7 +7,6 @@ registrar(EuroMoveRegister)
registrar(NanotecRegister) registrar(NanotecRegister)
registrar(pmacControllerRegister) registrar(pmacControllerRegister)
registrar(pmacAsynIPPortRegister) registrar(pmacAsynIPPortRegister)
registrar(drvAsynMMACSPortRegisterCommands)
registrar(MasterMACSRegister) registrar(MasterMACSRegister)
registrar(SINQControllerRegister) registrar(SINQControllerRegister)

View File

@ -1,3 +1,4 @@
#!/usr/bin/env python3
""" """
This is some code for communicating with a Delta-Tau PMAC motor This is some code for communicating with a Delta-Tau PMAC motor
controller as used at SINQ from python. Python 3 is assumed and controller as used at SINQ from python. Python 3 is assumed and
@ -14,6 +15,7 @@
import struct import struct
import socket import socket
import curses
def packPmacCommand(command): def packPmacCommand(command):
# 0x40 = VR_DOWNLOAD # 0x40 = VR_DOWNLOAD
@ -41,11 +43,135 @@ def readPmacReply(input):
if __name__ == "__main__": if __name__ == "__main__":
from sys import argv from sys import argv
addr = argv[1].split(':')
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect((addr[0],int(addr[1])))
buf = packPmacCommand(argv[2])
s.send(buf)
reply = readPmacReply(s)
print(reply.decode('utf-8') + '\n')
try:
addr = argv[1].split(':')
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect((addr[0],int(addr[1])))
if len(argv) == 3:
buf = packPmacCommand(argv[2])
s.send(buf)
reply = readPmacReply(s)
print(reply.decode('utf-8') + '\n')
else:
try:
stdscr = curses.initscr()
curses.noecho()
curses.cbreak()
stdscr.keypad(True)
stdscr.addstr(">> ")
stdscr.refresh()
history = [""]
ptr = len(history) - 1
while True:
c = stdscr.getch()
if c == curses.KEY_RIGHT:
(y, x) = stdscr.getyx()
if x < len(history[ptr]) + 3:
stdscr.move(y, x+1)
stdscr.refresh()
elif c == curses.KEY_LEFT:
(y, x) = stdscr.getyx()
if x > 3:
stdscr.move(y, x-1)
stdscr.refresh()
elif c == curses.KEY_UP:
if ptr > 0:
ptr -= 1
stdscr.addch("\r")
stdscr.clrtoeol()
stdscr.addstr(">> " + history[ptr])
elif c == curses.KEY_DOWN:
if ptr < len(history) - 1:
ptr += 1
stdscr.addch("\r")
stdscr.clrtoeol()
stdscr.addstr(">> " + history[ptr])
elif c == curses.KEY_ENTER or c == ord('\n') or c == ord('\r'):
if history[ptr] == 'quit':
break
# because of arrow keys move back to the end of the line
(y, x) = stdscr.getyx()
stdscr.move(y, 3+len(history[ptr]))
if history[ptr]:
buf = packPmacCommand(history[ptr])
s.send(buf)
reply = readPmacReply(s)
stdscr.addstr("\n" + reply.decode('utf-8')[0:-1])
if ptr == len(history) - 1 and history[ptr] != "":
history += [""]
else:
history[-1] = ""
ptr = len(history) - 1
stdscr.addstr("\n>> ")
stdscr.refresh()
else:
if ptr < len(history) - 1: # Modifying previous input
if len(history[-1]) == 0:
history[-1] = history[ptr]
ptr = len(history) - 1
else:
history += [history[ptr]]
ptr = len(history) - 1
if c == curses.KEY_BACKSPACE:
if len(history[ptr]) == 0:
continue
(y, x) = stdscr.getyx()
history[ptr] = history[ptr][0:x-4] + history[ptr][x-3:]
stdscr.addch("\r")
stdscr.clrtoeol()
stdscr.addstr(">> " + history[ptr])
stdscr.move(y, x-1)
stdscr.refresh()
else:
(y, x) = stdscr.getyx()
history[ptr] = history[ptr][0:x-3] + chr(c) + history[ptr][x-3:]
stdscr.addch("\r")
stdscr.clrtoeol()
stdscr.addstr(">> " + history[ptr])
stdscr.move(y, x+1)
stdscr.refresh()
finally:
# to quit
curses.nocbreak()
stdscr.keypad(False)
curses.echo()
curses.endwin()
except:
print("""
Invalid Arguments
Option 1: Single Command
------------------------
Usage: deltatau.py pmachost:port command
This then returns the response for command.
Option 2: CLI Mode
------------------
Usage: deltatau.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.
""")