Added interpose driver from DLS
Added a low-level interpose driver to allow usage of stream devices.
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
#include "turboPmacController.h"
|
||||
#include "asynInt32SyncIO.h"
|
||||
#include "asynMotorController.h"
|
||||
#include "asynOctetSyncIO.h"
|
||||
#include "pmacAsynIPPort.h"
|
||||
#include "turboPmacAxis.h"
|
||||
#include <epicsExport.h>
|
||||
#include <errlog.h>
|
||||
@@ -79,6 +81,16 @@ turboPmacController::turboPmacController(const char *portName,
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
status = createParam("FLUSH_HARDWARE", asynParamInt32, &flushHardware_);
|
||||
if (status != asynSuccess) {
|
||||
asynPrint(this->pasynUserSelf, 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);
|
||||
}
|
||||
|
||||
/*
|
||||
Define the end-of-string of a message coming from the device to EPICS.
|
||||
It is not necessary to append a terminator to outgoing messages, since
|
||||
@@ -88,7 +100,8 @@ turboPmacController::turboPmacController(const char *portName,
|
||||
const char *message_from_device =
|
||||
"\006"; // Hex-code for ACK (acknowledge) -> Each message from the MCU
|
||||
// is terminated by this value
|
||||
status = pasynOctetSyncIO->setInputEos(ipPortUser_, message_from_device,
|
||||
status = pasynOctetSyncIO->setInputEos(pasynOctetSyncIOipPort_,
|
||||
message_from_device,
|
||||
strlen(message_from_device));
|
||||
if (status != asynSuccess) {
|
||||
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
|
||||
@@ -96,7 +109,7 @@ turboPmacController::turboPmacController(const char *portName,
|
||||
"(setting input EOS failed with %s).\nTerminating IOC",
|
||||
portName, __PRETTY_FUNCTION__, __LINE__,
|
||||
stringifyAsynStatus(status));
|
||||
pasynOctetSyncIO->disconnect(ipPortUser_);
|
||||
pasynOctetSyncIO->disconnect(pasynOctetSyncIOipPort_);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
@@ -108,7 +121,23 @@ turboPmacController::turboPmacController(const char *portName,
|
||||
"with %s).\nTerminating IOC",
|
||||
portName, __PRETTY_FUNCTION__, __LINE__,
|
||||
stringifyAsynStatus(status));
|
||||
pasynOctetSyncIO->disconnect(ipPortUser_);
|
||||
pasynOctetSyncIO->disconnect(pasynOctetSyncIOipPort_);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
// =========================================================================;
|
||||
|
||||
/*
|
||||
We try to connect to the port via the port name provided by the constructor.
|
||||
If this fails, the function is terminated via exit.
|
||||
*/
|
||||
pasynInt32SyncIO->connect(ipPortConfigName, 0, &pasynInt32SyncIOipPort_,
|
||||
NULL);
|
||||
if (status != asynSuccess || pasynInt32SyncIOipPort_ == nullptr) {
|
||||
errlogPrintf("Controller \"%s\" => %s, line %d:\nFATAL ERROR (cannot "
|
||||
"connect to MCU controller).\n"
|
||||
"Terminating IOC",
|
||||
portName, __PRETTY_FUNCTION__, __LINE__);
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
@@ -175,83 +204,25 @@ asynStatus turboPmacController::writeRead(int axisNo, const char *command,
|
||||
}
|
||||
const size_t commandLength = strlen(command);
|
||||
|
||||
// NOT NEEDED ANYMORE DUE TO THE LOW LEVEL DRIVER
|
||||
// /*
|
||||
// The message protocol of the turboPmac used at PSI looks as follows (all
|
||||
// characters immediately following each other without a newline):
|
||||
// 0x40 (ASCII value of @) -> Request for download
|
||||
// 0xBF (ASCII value of ¿) -> Select mode "get_response"
|
||||
// 0x00 (ASCII value of 0)
|
||||
// 0x00 (ASCII value of 0)
|
||||
// 0x00 (ASCII value of 0)
|
||||
// 0x00 (ASCII value of 0)
|
||||
// 0x00 (ASCII value of 0)
|
||||
// [message length in network byte order] -> Use the htons function for this
|
||||
// value [Actual message] It is not necessary to append a terminator, since
|
||||
// this protocol encodes the message length at the beginning. See Turbo PMAC
|
||||
// User Manual, page 418 in VR_PMAC_GETRESPONSE
|
||||
// x0D (ASCII value of carriage return) -> The controller needs a carriage
|
||||
// return at the end of a "send" command (a command were we transmit data
|
||||
// via
|
||||
// =). For "request" commands (e.g. read status or position), this is not
|
||||
// necessary, but it doesn't hurt either, therefore we always add a carriage
|
||||
// return.
|
||||
|
||||
// The message has to be build manually into the buffer fullCommand, since
|
||||
// it contains NULL terminators in its middle, therefore the string
|
||||
// manipulation methods of C don't work.
|
||||
// */
|
||||
|
||||
//
|
||||
// const int offset = 9;
|
||||
|
||||
// // Positions 2 to 6 must have the value 0. Since fullCommand is
|
||||
// initialized
|
||||
// // as an array of zeros, we don't need to set these bits manually.
|
||||
// fullCommand[0] = '\x40';
|
||||
// fullCommand[1] = '\xBF';
|
||||
|
||||
// // The size of size_t is platform dependant (pointers-sized), while htons
|
||||
// // needs an unsigned int. The byte order is then converted from host to
|
||||
// // network order. The offset "+1" is for the carriage return.
|
||||
// u_int16_t len = htons(static_cast<u_int16_t>(commandLength + 1));
|
||||
|
||||
// // Split up into the upper and the lower byte
|
||||
// fullCommand[7] = (char)(len >> 8); // Shift the 8 higher bits to the
|
||||
// right fullCommand[8] = (char)(len & 0xFF); // Mask the higher bits
|
||||
|
||||
// // Write the actual command behind the protocol
|
||||
// for (size_t i = 0; i < commandLength; i++) {
|
||||
// fullCommand[i + offset] = command[i];
|
||||
// }
|
||||
// fullCommand[offset + commandLength] = '\x0D';
|
||||
|
||||
// // +1 for the carriage return.
|
||||
// const size_t fullComandLength = offset + commandLength + 1;
|
||||
|
||||
/*
|
||||
We use separated write and read commands here, not the combined writeRead
|
||||
method, because the latter is actually a flushWriteRead (see
|
||||
https://epics.anl.gov/modules/soft/asyn/R4-14/asynDriver.pdf, p. 31) ->
|
||||
Calls the flush command, then the write command, then the read command.
|
||||
The flush itself reads repeatedly from the MCU until no messages are there
|
||||
anymore. (The Diamond Light Source driver first send a PMAC flush command
|
||||
and then does the same as the asyn flush). We don't want this behaviour.
|
||||
(https://www.slac.stanford.edu/grp/lcls/controls/global/doc/epics-modules/R3-14-12/asyn/asyn-R4-18-lcls2/asyn/interfaces/asynOctetBase.c)
|
||||
The writeRead command performs the following steps:
|
||||
1) Flush the socket buffer on the IOC side (not the controller!)
|
||||
2) Write a command to the controller
|
||||
3) Read the response
|
||||
|
||||
If a timeout occurs during writing or reading, inform the user that we're
|
||||
trying to reconnect. If the problem persists, ask them to call the support
|
||||
*/
|
||||
status = pasynOctetSyncIO->writeRead(
|
||||
pasynOctetSyncIOipPort(), command, commandLength, response, MAXBUF_,
|
||||
comTimeout_, &nbytesOut, &nbytesIn, &eomReason);
|
||||
|
||||
status = pasynOctetSyncIO->write(ipPortUser_, command, commandLength,
|
||||
comTimeout_, &nbytesOut);
|
||||
|
||||
msgPrintControlKey writeKey =
|
||||
msgPrintControlKey comKey =
|
||||
msgPrintControlKey(portName, axisNo, __PRETTY_FUNCTION__, __LINE__);
|
||||
|
||||
if (status == asynTimeout) {
|
||||
|
||||
if (msgPrintControl_.shouldBePrinted(writeKey, true, pasynUserSelf)) {
|
||||
if (msgPrintControl_.shouldBePrinted(comKey, true, pasynUserSelf)) {
|
||||
asynPrint(
|
||||
this->pasynUserSelf, ASYN_TRACE_ERROR,
|
||||
"Controller \"%s\", axis %d => %s, line %d\nTimeout while "
|
||||
@@ -268,8 +239,9 @@ asynStatus turboPmacController::writeRead(int axisNo, const char *command,
|
||||
checkMaxSubsequentTimeouts(timeoutCounter, axis);
|
||||
timeoutCounter += 1;
|
||||
|
||||
status = pasynOctetSyncIO->write(
|
||||
ipPortUser_, command, commandLength, comTimeout_, &nbytesOut);
|
||||
status = pasynOctetSyncIO->writeRead(
|
||||
pasynOctetSyncIOipPort(), command, commandLength, response,
|
||||
MAXBUF_, comTimeout_, &nbytesOut, &nbytesIn, &eomReason);
|
||||
if (status != asynTimeout) {
|
||||
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
|
||||
"Controller \"%s\", axis %d => %s, line "
|
||||
@@ -279,7 +251,7 @@ asynStatus turboPmacController::writeRead(int axisNo, const char *command,
|
||||
}
|
||||
}
|
||||
} else if (status != asynSuccess) {
|
||||
if (msgPrintControl_.shouldBePrinted(writeKey, true, pasynUserSelf)) {
|
||||
if (msgPrintControl_.shouldBePrinted(comKey, true, pasynUserSelf)) {
|
||||
asynPrint(
|
||||
this->pasynUserSelf, ASYN_TRACE_ERROR,
|
||||
"Controller \"%s\", axis %d => %s, line %d\nError %s while "
|
||||
@@ -288,57 +260,7 @@ asynStatus turboPmacController::writeRead(int axisNo, const char *command,
|
||||
stringifyAsynStatus(status), msgPrintControl_.getSuffix());
|
||||
}
|
||||
} else {
|
||||
msgPrintControl_.resetCount(writeKey, pasynUserSelf);
|
||||
}
|
||||
|
||||
// Read the response from the MCU buffer
|
||||
status = pasynOctetSyncIO->read(ipPortUser_, response, MAXBUF_, comTimeout_,
|
||||
&nbytesIn, &eomReason);
|
||||
|
||||
msgPrintControlKey readKey =
|
||||
msgPrintControlKey(portName, axisNo, __PRETTY_FUNCTION__, __LINE__);
|
||||
|
||||
if (status == asynTimeout) {
|
||||
|
||||
if (msgPrintControl_.shouldBePrinted(readKey, true, pasynUserSelf)) {
|
||||
asynPrint(
|
||||
this->pasynUserSelf, ASYN_TRACE_ERROR,
|
||||
"Controller \"%s\", axis %d => %s, line %d\nTimeout while "
|
||||
"reading from the MCU.%s\n",
|
||||
portName, axisNo, __PRETTY_FUNCTION__, __LINE__,
|
||||
msgPrintControl_.getSuffix());
|
||||
}
|
||||
|
||||
// Add this event to the back of the timeout event counter
|
||||
timeoutStatus = checkComTimeoutWatchdog(axisNo, drvMessageText,
|
||||
sizeof(drvMessageText));
|
||||
|
||||
int timeoutCounter = 0;
|
||||
while (1) {
|
||||
checkMaxSubsequentTimeouts(timeoutCounter, axis);
|
||||
timeoutCounter += 1;
|
||||
|
||||
status = pasynOctetSyncIO->read(ipPortUser_, response, MAXBUF_,
|
||||
comTimeout_, &nbytesIn, &eomReason);
|
||||
if (status != asynTimeout) {
|
||||
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
|
||||
"Controller \"%s\", axis %d => %s, line "
|
||||
"%d\nReconnected after read timeout\n",
|
||||
portName, axisNo, __PRETTY_FUNCTION__, __LINE__);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (status != asynSuccess) {
|
||||
if (msgPrintControl_.shouldBePrinted(readKey, true, pasynUserSelf)) {
|
||||
asynPrint(
|
||||
this->pasynUserSelf, ASYN_TRACE_ERROR,
|
||||
"Controller \"%s\", axis %d => %s, line %d\nError %s while "
|
||||
"reading from the controller.%s\n",
|
||||
portName, axisNo, __PRETTY_FUNCTION__, __LINE__,
|
||||
stringifyAsynStatus(status), msgPrintControl_.getSuffix());
|
||||
}
|
||||
} else {
|
||||
msgPrintControl_.resetCount(readKey, pasynUserSelf);
|
||||
msgPrintControl_.resetCount(comKey, pasynUserSelf);
|
||||
}
|
||||
|
||||
if (timeoutStatus == asynError) {
|
||||
@@ -352,7 +274,8 @@ asynStatus turboPmacController::writeRead(int axisNo, const char *command,
|
||||
status = asynError;
|
||||
|
||||
snprintf(drvMessageText, sizeof(drvMessageText),
|
||||
"Terminated message due to reason %d (should be 2).",
|
||||
"Terminated message due to reason %d (should be 2). Please "
|
||||
"call the support.",
|
||||
eomReason);
|
||||
|
||||
if (msgPrintControl_.shouldBePrinted(terminateKey, true,
|
||||
@@ -469,6 +392,22 @@ asynStatus turboPmacController::writeRead(int axisNo, const char *command,
|
||||
return status;
|
||||
}
|
||||
|
||||
asynStatus turboPmacController::doFlushHardware() {
|
||||
/*
|
||||
Temporarily overwrite the "reason" field with the FLUSH_HARDWARE
|
||||
constant defined in pmacAsynIPPort.c. This reason is then used within
|
||||
the write method of pasynInt32SyncIO to select the flush function.
|
||||
*/
|
||||
int temp = pasynInt32SyncIOipPort_->reason;
|
||||
pasynInt32SyncIOipPort_->reason = FLUSH_HARDWARE;
|
||||
asynStatus status = (asynStatus)pasynInt32SyncIO->write(
|
||||
pasynInt32SyncIOipPort_, 1, comTimeout_);
|
||||
|
||||
// Reset the status afterwards
|
||||
pasynInt32SyncIOipPort_->reason = temp;
|
||||
return status;
|
||||
}
|
||||
|
||||
asynStatus turboPmacController::writeInt32(asynUser *pasynUser,
|
||||
epicsInt32 value) {
|
||||
int function = pasynUser->reason;
|
||||
@@ -482,6 +421,8 @@ asynStatus turboPmacController::writeInt32(asynUser *pasynUser,
|
||||
return axis->rereadEncoder();
|
||||
} else if (function == readConfig_) {
|
||||
return axis->init();
|
||||
} else if (function == flushHardware_) {
|
||||
return doFlushHardware();
|
||||
} else {
|
||||
return sinqController::writeInt32(pasynUser, value);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user