forked from epics_driver_modules/motorBase
295 lines
11 KiB
C++
295 lines
11 KiB
C++
/* This script was adapted from the Newport Socket.cpp code
|
|
to provide TCP/IP sockets for the XPSC8 motion controller
|
|
using EPICS asynOctetSyncIO.cc
|
|
|
|
By Jon Kelly July 2005
|
|
Re-written by Mark Rivers March 2006
|
|
*/
|
|
|
|
/* includes */
|
|
|
|
#ifdef vxWorks
|
|
#else
|
|
#define TRUE 1
|
|
#define FALSE 0
|
|
typedef int BOOL;
|
|
#endif
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
#include <epicsThread.h>
|
|
#include <epicsMutex.h>
|
|
#include <epicsString.h>
|
|
#include <asynDriver.h>
|
|
#include <asynOctetSyncIO.h>
|
|
#include <asynCommonSyncIO.h>
|
|
#include <drvAsynIPPort.h>
|
|
#include <epicsExport.h>
|
|
|
|
|
|
/* The maximum number of sockets to XPS controllers. The driver uses
|
|
* one socket per motor plus one per controller, so a maximum of 9 per controller.
|
|
* This number is used to create an array because the Newport code uses integers for
|
|
* the socket ID. */
|
|
#define MAX_SOCKETS 1000
|
|
#define PORT_NAME_SIZE 100
|
|
#define ERROR_STRING_SIZE 100
|
|
#define DEFAULT_TIMEOUT 0.2
|
|
#define XPS_TERMINATOR ",EndOfAPI"
|
|
|
|
#define MAX_RETRIES 2
|
|
|
|
static int nextSocket = 0;
|
|
|
|
/* Pointer to the connection info for each socket
|
|
the asynUser structure is defined in asynDriver.h */
|
|
typedef struct {
|
|
asynUser *pasynUser;
|
|
asynUser *pasynUserCommon;
|
|
double timeout;
|
|
char errorString[ERROR_STRING_SIZE];
|
|
int connected;
|
|
epicsMutexId mutexId;
|
|
} socketStruct;
|
|
static socketStruct socketStructs[MAX_SOCKETS];
|
|
|
|
|
|
/***************************************************************************************/
|
|
int ConnectToServer(char *IpAddress, int IpPort, double timeout)
|
|
{
|
|
|
|
char portName[PORT_NAME_SIZE];
|
|
char ipString[PORT_NAME_SIZE];
|
|
asynUser *pasynUser, *pasynUserCommon;
|
|
socketStruct *psock;
|
|
int status;
|
|
|
|
if (nextSocket >= MAX_SOCKETS) {
|
|
printf("ConnectToServer: too many open sockets, max=%d\n", MAX_SOCKETS);
|
|
return -1;
|
|
}
|
|
/* Create a new asyn port */
|
|
epicsSnprintf(ipString, PORT_NAME_SIZE, "%s:%d TCP", IpAddress, IpPort);
|
|
epicsSnprintf(portName, PORT_NAME_SIZE, "%s:%d:%d", IpAddress, IpPort, nextSocket);
|
|
/* Create port with noAutoConnect and noProcessEos options */
|
|
drvAsynIPPortConfigure(portName, ipString, 0, 1, 1);
|
|
|
|
/* Connect to driver with asynOctet interface */
|
|
status = pasynOctetSyncIO->connect(portName, 0, &pasynUser, NULL);
|
|
if (status != asynSuccess) {
|
|
printf("ConnectToServer, error calling pasynOctetSyncIO->connect %s\n", pasynUser->errorMessage);
|
|
return -1;
|
|
}
|
|
psock = &socketStructs[nextSocket];
|
|
psock->pasynUser = pasynUser;
|
|
|
|
/* Connect to driver with asynCommon interface */
|
|
status = pasynCommonSyncIO->connect(portName, 0, &pasynUserCommon, NULL);
|
|
if (status != asynSuccess) {
|
|
printf("ConnectToServer, error calling pasynCommonSyncIO->connect %s\n",
|
|
pasynUserCommon->errorMessage);
|
|
return -1;
|
|
}
|
|
psock->pasynUserCommon = pasynUserCommon;
|
|
|
|
/* Connect to controller */
|
|
status = pasynCommonSyncIO->connectDevice(pasynUserCommon);
|
|
if (status != asynSuccess) {
|
|
printf("ConnectToServer, error calling pasynCommonSyncIO->connectDevice port=%s error=%s\n",
|
|
portName, pasynUserCommon->errorMessage);
|
|
return -1;
|
|
}
|
|
|
|
/* Create a mutex to prevent more than 1 thread using socket at once
|
|
* Normally the SyncIO-.writeRead function takes care of this, but for long responses
|
|
* we can't use a single write/read operation */
|
|
psock->mutexId = epicsMutexMustCreate();
|
|
|
|
psock->timeout = timeout;
|
|
psock->connected = 1;
|
|
strcpy(psock->errorString, "");
|
|
|
|
nextSocket++;
|
|
return nextSocket-1;
|
|
}
|
|
|
|
/***************************************************************************************/
|
|
void SetTCPTimeout(int SocketIndex, double TimeOut)
|
|
{
|
|
if ((SocketIndex < 0) || (SocketIndex >= nextSocket)) {
|
|
printf("SetTCPTimeout, SocketIndex=%d, must be >=0 and < %d\n", SocketIndex, nextSocket);
|
|
return;
|
|
}
|
|
socketStructs[SocketIndex].timeout = TimeOut;
|
|
}
|
|
|
|
|
|
/***************************************************************************************/
|
|
void SendAndReceive (int SocketIndex, char buffer[], char valueRtrn[], int returnSize)
|
|
{
|
|
size_t nbytesOut;
|
|
size_t nbytesIn;
|
|
int eomReason;
|
|
int bufferLength;
|
|
socketStruct *psock;
|
|
int status;
|
|
int retries;
|
|
int errStat;
|
|
int nread;
|
|
|
|
/* Check to see if the Socket is valid! */
|
|
|
|
bufferLength = strlen(buffer);
|
|
if ((SocketIndex < 0) || (SocketIndex >= nextSocket)) {
|
|
printf("SendAndReceive: invalid SocketIndex %d\n", SocketIndex);
|
|
strcpy(valueRtrn,"-22");
|
|
return;
|
|
}
|
|
psock = &socketStructs[SocketIndex];
|
|
if (!psock->connected) {
|
|
printf("SendAndReceive: socket not connected %d\n", SocketIndex);
|
|
strcpy(valueRtrn,"-22");
|
|
return;
|
|
}
|
|
|
|
epicsMutexMustLock(psock->mutexId);
|
|
/* If timeout > 0. then we do a write read. If < 0. then write. */
|
|
|
|
if (psock->timeout > 0.0) {
|
|
status = pasynOctetSyncIO->writeRead(psock->pasynUser,
|
|
(char const *)buffer,
|
|
bufferLength,
|
|
valueRtrn,
|
|
returnSize,
|
|
psock->timeout,
|
|
&nbytesOut,
|
|
&nbytesIn,
|
|
&eomReason);
|
|
if ( status != asynSuccess ) {
|
|
asynPrint(psock->pasynUser, ASYN_TRACE_ERROR,
|
|
"SendAndReceive error calling writeRead, output=%s status=%d, error=%s\n",
|
|
buffer, status, psock->pasynUser->errorMessage);
|
|
}
|
|
asynPrint(psock->pasynUser, ASYN_TRACEIO_DRIVER,
|
|
"SendAndReceive, sent: '%s', received: '%s'\n",
|
|
buffer, valueRtrn);
|
|
nread = nbytesIn;
|
|
/* Loop until we the response contains ",EndOfAPI" or we get an error */
|
|
while ((status==asynSuccess) &&
|
|
(strcmp(valueRtrn + nread - strlen(XPS_TERMINATOR), XPS_TERMINATOR) != 0)) {
|
|
status = pasynOctetSyncIO->read(psock->pasynUser,
|
|
&valueRtrn[nread],
|
|
returnSize-nread,
|
|
psock->timeout,
|
|
&nbytesIn,
|
|
&eomReason);
|
|
asynPrint(psock->pasynUser, ASYN_TRACEIO_DRIVER,
|
|
"SendAndReceive, received: nread=%d, returnSize-nread=%d, nbytesIn=%d\n",
|
|
nread, returnSize-nread, nbytesIn);
|
|
nread += nbytesIn;
|
|
}
|
|
} else {
|
|
/* This is typically used for the "Move" commands, and we don't want to wait for the response */
|
|
/* Fake the response by putting "-1" (for error) or "0" (for success) in the return string */
|
|
for (retries=0; retries<MAX_RETRIES; retries++) {
|
|
status = pasynOctetSyncIO->writeRead(psock->pasynUser,
|
|
(char const *)buffer,
|
|
bufferLength,
|
|
valueRtrn,
|
|
returnSize,
|
|
-psock->timeout,
|
|
&nbytesOut,
|
|
&nbytesIn,
|
|
&eomReason);
|
|
if (status == asynError) {
|
|
asynPrint(psock->pasynUser, ASYN_TRACE_ERROR,
|
|
"SendAndReceive error calling write, output=%s status=%d, error=%s\n",
|
|
buffer, status, psock->pasynUser->errorMessage);
|
|
strcpy(valueRtrn, "-1");
|
|
break;
|
|
}
|
|
asynPrint(psock->pasynUser, ASYN_TRACEIO_DRIVER,
|
|
"SendAndReceive, sent: '%s'\n", buffer);
|
|
/* A timeout is OK */
|
|
if (status == asynTimeout) {
|
|
asynPrint(psock->pasynUser, ASYN_TRACEIO_DRIVER,
|
|
"SendAndReceive, timeout on read\n");
|
|
break;
|
|
} else {
|
|
asynPrint(psock->pasynUser, ASYN_TRACEIO_DRIVER,
|
|
"SendAndReceive, read: '%s'\n", valueRtrn);
|
|
errStat = atoi(valueRtrn);
|
|
if (errStat == 0) break; /* All done */
|
|
if (errStat == -1) continue; /* Error that previous command not complete */
|
|
asynPrint(psock->pasynUser, ASYN_TRACE_ERROR,
|
|
"SendAndReceive unexpected response =%s\n",
|
|
valueRtrn);
|
|
break;
|
|
}
|
|
}
|
|
strcpy(valueRtrn, "0");
|
|
}
|
|
epicsMutexUnlock(psock->mutexId);
|
|
}
|
|
|
|
|
|
/***************************************************************************************/
|
|
void CloseSocket(int SocketIndex)
|
|
{
|
|
socketStruct *psock;
|
|
asynUser *pasynUser;
|
|
int status;
|
|
|
|
if ((SocketIndex < 0) || (SocketIndex >= nextSocket)) {
|
|
printf("CloseSocket: invalid SocketIndex %d\n", SocketIndex);
|
|
return;
|
|
}
|
|
psock = &socketStructs[SocketIndex];
|
|
pasynUser = psock->pasynUserCommon;
|
|
status = pasynCommonSyncIO->disconnectDevice(pasynUser);
|
|
if (status != asynSuccess ) {
|
|
asynPrint(pasynUser, ASYN_TRACE_ERROR,
|
|
"CloseSocket: error calling pasynCommonSyncIO->disconnect, status=%d, %s\n",
|
|
status, pasynUser->errorMessage);
|
|
return;
|
|
}
|
|
psock->connected = 0;
|
|
}
|
|
|
|
/***************************************************************************************/
|
|
void closeXPSSockets(void)
|
|
{
|
|
int i;
|
|
|
|
for (i=0; i<nextSocket; i++) {
|
|
if (socketStructs[i].connected) CloseSocket(i);
|
|
}
|
|
}
|
|
|
|
/***************************************************************************************/
|
|
char * GetError(int SocketIndex)
|
|
{
|
|
if ((SocketIndex < 0) || (SocketIndex >= nextSocket)) {
|
|
printf("GetError: invalid SocketIndex %d\n", SocketIndex);
|
|
return "Invalid socket";
|
|
}
|
|
return socketStructs[SocketIndex].errorString;
|
|
}
|
|
|
|
/***************************************************************************************/
|
|
void strncpyWithEOS(char * szStringOut, const char * szStringIn, int nNumberOfCharToCopy, int nStringOutSize)
|
|
{
|
|
if (nNumberOfCharToCopy < nStringOutSize)
|
|
{
|
|
strncpy (szStringOut, szStringIn, nNumberOfCharToCopy);
|
|
szStringOut[nNumberOfCharToCopy] = '\0';
|
|
}
|
|
else
|
|
{
|
|
strncpy (szStringOut, szStringIn, nStringOutSize - 1);
|
|
szStringOut[nStringOutSize - 1] = '\0';
|
|
}
|
|
}
|