commit 064ec37e9aff9b5767c99a4f4538bf5114ebccc9 Author: cvs Date: Fri Jun 20 10:18:47 2003 +0000 - Rearranged directory structure for forking out ANSTO - Refactored site specific stuff into a site module - PSI specific stuff is now in the PSI directory. - The old version has been tagged with pre-ansto diff --git a/A1931.c b/A1931.c new file mode 100644 index 0000000..c4fb79a --- /dev/null +++ b/A1931.c @@ -0,0 +1,344 @@ +/*------------------------------------------------------------------------- + This is the implementation file for a driver for the Risoe A1931a + temperature controller. This driver controls the device through a GPIB + interface. + + copyright: see file COPYRIGHT + + Mark Koennecke, February 2003 + -------------------------------------------------------------------------*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "A1931.h" + +/*========================== private data structure ====================*/ +typedef struct { + int sensor; /* the control sensor */ + pGPIB gpib; /* the GPIB interface to use in order to talk to the thing*/ + int gpibAddress; /* address on bus */ + int devID; /* deviceID of the controller on the GPIB */ + char errorBuffer[132]; /* a buffer for error messages from the thing*/ + char commandLine[132]; /* buffer to keep the offending command line */ + int errorCode; /* error indicator */ +}A1931, *pA1931; +/*============================ defines ================================*/ +#define COMMERROR -300 +#define A1931ERROR -301 +#define FILEERROR -302 +/*====================================================================*/ +static char *A1931comm(pEVDriver pData, char *command){ + char buffer[256], *pPtr; + int status; + pA1931 self = NULL; + Tcl_DString reply; + + self = (pA1931)pData->pPrivate; + assert(self); + + /* + send + */ + strncpy(buffer,command,250); + strcat(buffer,"\n"); + status = GPIBsend(self->gpib,self->devID,buffer,(int)strlen(buffer)); + if(status < 0){ + self->errorCode = COMMERROR; + GPIBerrorDescription(self->gpib,status,self->errorBuffer,131); + return NULL; + } + + /* + read until > is found + */ + Tcl_DStringInit(&reply); + while(1){ + pPtr = GPIBreadTillTerm(self->gpib,self->devID,10); + if(strstr(pPtr,"GPIB READ ERROR") != NULL){ + free(pPtr); + self->errorCode = COMMERROR; + Tcl_DStringFree(&reply); + return NULL; + } else { + Tcl_DStringAppend(&reply,pPtr,-1); + if(strchr(pPtr,'>') != NULL){ + /* + finished + */ + free(pPtr); + break; + } + free(pPtr); + } + } + pPtr = NULL; + pPtr = strdup(Tcl_DStringValue(&reply)); + Tcl_DStringFree(&reply); + if(pPtr[0] == '#'){ + /* + error + */ + self->errorCode = A1931ERROR; + strncpy(self->errorBuffer,pPtr,131); + free(pPtr); + return NULL; + } + return pPtr; +} +/*--------------------------------------------------------------------*/ +static int A1931command(pEVDriver pData, char *command, char *replyBuffer, + int replyBufferLen){ + pA1931 self = NULL; + char *pReply = NULL; + + self = (pA1931)pData->pPrivate; + assert(self); + + pReply = A1931comm(pData,command); + if(pReply != NULL){ + strncpy(replyBuffer,pReply,replyBufferLen); + free(pReply); + return 1; + } else { + strncpy(replyBuffer,self->errorBuffer,replyBufferLen); + return 0; + } +} +/*====================================================================*/ +static int A1931Init(pEVDriver pData){ + pA1931 self = NULL; + + self = (pA1931)pData->pPrivate; + assert(self); + + self->devID = GPIBattach(self->gpib,0,self->gpibAddress,0,13,0,0); + if(self->devID < 0){ + return 0; + } + return 1; +} +/*====================================================================*/ +static int A1931Close(pEVDriver pData){ + pA1931 self = NULL; + + self = (pA1931)pData->pPrivate; + assert(self); + + GPIBdetach(self->gpib,self->devID); + self->devID = 0; + return 1; +} +/*===================================================================*/ +static int A1931Get(pEVDriver pData,float *fPos){ + pA1931 self = NULL; + char buffer[132], command[50]; + int status; + + self = (pA1931)pData->pPrivate; + assert(self); + + sprintf(command,"?TEMP%1.1d",self->sensor); + status = A1931command(pData,command,buffer,131); + if(!status){ + return 0; + } + sscanf(buffer,"%f",fPos); + return 1; +} +/*=====================================================================*/ +static int A1931Set(pEVDriver pData, float fNew){ + pA1931 self = NULL; + char buffer[132], command[50]; + int status; + + self = (pA1931)pData->pPrivate; + assert(self); + + sprintf(command,"SET%1.1d=%f",self->sensor,fNew); + status = A1931command(pData,command,buffer,131); + if(!status){ + return 0; + } + return 1; +} +/*====================================================================*/ +static int A1931error(pEVDriver pData, int *iCode, char *errBuff, int bufLen){ + pA1931 self = NULL; + char pError[256]; + + self = (pA1931)pData->pPrivate; + assert(self); + + *iCode = self->errorCode; + sprintf(pError,"ERROR: %s",self->errorBuffer); + strncpy(errBuff,pError,bufLen); + return 1; +} +/*====================================================================*/ +static int A1931fix(pEVDriver pData, int iCode){ + pA1931 self = NULL; + char pError[256]; + + self = (pA1931)pData->pPrivate; + assert(self); + + if(iCode == COMMERROR){ + GPIBclear(self->gpib,self->devID); + return DEVREDO; + } + return DEVFAULT; +} +/*=====================================================================*/ +pEVDriver CreateA1931Driver(int argc, char *argv[]){ + pEVDriver self = NULL; + pA1931 priv = NULL; + + if(argc < 2){ + return NULL; + } + + /* + allocate space + */ + self = CreateEVDriver(argc,argv); + priv = (pA1931)malloc(sizeof(A1931)); + if(self == NULL || priv == NULL){ + return NULL; + } + memset(priv,0,sizeof(A1931)); + self->pPrivate = priv; + self->KillPrivate = free; + + /* + initialize + */ + priv->gpib = (pGPIB)FindCommandData(pServ->pSics,argv[0],"GPIB"); + if(!priv->gpib){ + DeleteEVDriver(self); + return NULL; + } + priv->sensor = 1; + priv->gpibAddress = atoi(argv[1]); + + /* + initialize function pointers + */ + self->Send = A1931command; + self->Init = A1931Init; + self->Close = A1931Close; + self->GetValue = A1931Get; + self->SetValue = A1931Set; + self->GetError = A1931error; + self->TryFixIt = A1931fix; + + return self; +} +/*=======================================================================*/ +static int downloadFile(pA1931 self, FILE *fd){ + char buffer[132], *pPtr; + int status; + + while(1){ + if(fgets(buffer,130,fd) == NULL){ + self->errorCode = FILEERROR; + strcpy(self->errorBuffer,"Failed to read from file"); + return 0; + } + if(strstr(buffer,"$END") != NULL){ + break; + } + status = GPIBsend(self->gpib,self->devID,buffer,(int)strlen(buffer)); + if(status < 0){ + self->errorCode = COMMERROR; + GPIBerrorDescription(self->gpib,status,self->errorBuffer,131); + return 0; + } + pPtr = GPIBreadTillTerm(self->gpib,self->devID,10); + if(pPtr[0] == '#'){ + self->errorCode = A1931ERROR; + strncpy(self->errorBuffer,pPtr,131); + strncpy(self->commandLine,buffer,131); + free(pPtr); + return 0; + } + free(pPtr); + usleep(50); + } + return 1; +} +/*=======================================================================*/ +int A1931Action(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]){ + pEVControl pEV = NULL; + pA1931 self = NULL; + char buffer[256]; + char error[132]; + FILE *fd = NULL; + int status, iCode; + + pEV = (pEVControl)pData; + assert(pEV); + self = (pA1931)pEV->pDriv->pPrivate; + assert(self); + + if(argc > 1){ + strtolower(argv[1]); + if(strcmp(argv[1],"sensor") == 0){ + if(argc > 2){ + /* set case */ + if(!SCMatchRights(pCon,usUser)){ + return 0; + } + self->sensor = atoi(argv[2]); + SCSendOK(pCon); + return 1; + } else { + /* get case */ + sprintf(buffer,"%s.sensor = %d",argv[0],self->sensor); + SCWrite(pCon,buffer,eValue); + return 1; + } + }else if(strcmp(argv[1],"list") == 0){ + sprintf(buffer,"%s.sensor = %d",argv[0],self->sensor); + SCWrite(pCon,buffer,eValue); + return EVControlWrapper(pCon,pSics,pData,argc,argv); + } else if(strcmp(argv[1],"file") == 0){ + if(!SCMatchRights(pCon,usUser)){ + return 0; + } + if(argc < 3){ + SCWrite(pCon,"ERROR: need filename argument",eError); + return 0; + } + fd = fopen(argv[2],"r"); + if(fd == NULL){ + sprintf(buffer,"ERROR: failed to open %s", argv[2]); + SCWrite(pCon,buffer,eError); + return 0; + } + status = downloadFile(self,fd); + fclose(fd); + if(!status){ + A1931error(pEV->pDriv,&iCode,error,131); + sprintf(buffer,"%s while transfering file", error); + SCWrite(pCon,buffer,eError); + sprintf(buffer,"Offending command: %s",self->commandLine); + SCWrite(pCon,buffer,eError); + return 0; + } + SCSendOK(pCon); + return 1; + } + } + return EVControlWrapper(pCon,pSics,pData,argc,argv); +} diff --git a/A1931.h b/A1931.h new file mode 100644 index 0000000..954813f --- /dev/null +++ b/A1931.h @@ -0,0 +1,20 @@ +/*------------------------------------------------------------------------- + This is the header file for a driver for the Risoe A1931a temperature + controller. This driver controls the device through a GPIB interface. + + copyright: see file COPYRIGHT + + Mark Koennecke, February 2003 + -------------------------------------------------------------------------*/ +#ifndef A1931A +#define A19131A + +#include "sics.h" + +pEVDriver CreateA1931Driver(int argc, char *argv[]); + +int A1931Action(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]); + + +#endif diff --git a/amor2t.c b/amor2t.c new file mode 100644 index 0000000..96bf3b7 --- /dev/null +++ b/amor2t.c @@ -0,0 +1,996 @@ +/*--------------------------------------------------------------------------- + A M O R 2 T + + A class for controlling the two theta movement of the reflectometer + AMOR at SINQ. It is not clear if this class may be useful for other + reflectometers, too. At AMOR the two theta movement of the detector is + realized by translating the detector along x and z. Also it can be + tilted in omega. Furthermore the height of two diaphragms has to be + adjusted when moving two theta as well. In polarizing mode the analyzer + mirror has to be moved as well. + + copyright: see copyright.h + + Mark Koennecke, September 1999 + + Bugs fixed, analyzer included for A2T. Then there is a second thing: + aoz2t which allows to scan the analyzer in two-theta during alignment + of the instrument. As all the parameters are already held in the a2t + structures this extra was added into this module. + + Mark Koennecke, May-June 2000 +---------------------------------------------------------------------------*/ +#include +#include +#include +#include "fortify.h" +#include +#include "sics.h" +#include "motor.h" +#include "obpar.h" + +#define DEBUG 1 + +#define MAXMOT 13 +#define MAXPAR 13 + +#include "amor2t.i" +#include "amor2t.h" + +/* + Defines for accessing various motors and variables. Definition of motor: see + annotated AMOR drawing. +*/ + +/* monochromator omega */ +#define MOTMOM 0 +/* sample omega */ +#define MOTSOM 1 +/* detector height movement */ +#define MOTCOZ 2 +/* detector movement along main axis */ +#define MOTCOX 3 +/* sample holder height movement */ +#define MOTSTZ 4 +/* whole sample table height movement */ +#define MOTSOZ 5 +/* lift for diaphragm 4*/ +#define MOTD4B 6 +/* lift for diaphragm 5 */ +#define MOTD5B 7 +/* detector omega movement */ +#define MOTCOM 8 +/* lift for analyzer */ +#define MOTAOZ 9 +/* analyzer omega */ +#define MOTAOM 10 +/* detector 2 movement */ +#define MOTC3Z 11 + + +/*====================================================================== + The core of it all: The calculation of the settings for the various + motors. +========================================================================*/ + static int CalculateAMORE(pAmor2T self, SConnection *pCon, float fNew) + { + float fMOM, fSOM, fSTZ, fSOZ, fAOM, fAOZ, fC3Z, fconstAOM; + double fAngle, fX, fZ, fZ2, fBase, fPIR; + float fCOZ, fCOX, fCOM; + int iRet; +#ifdef DEBUG + char pBueffel[132]; +#endif + + /* get the necessary angles first */ + iRet = MotorGetSoftPosition(self->aEngine[MOTMOM],pCon,&fMOM); + if(iRet != 1) + { + return iRet; + } + iRet = MotorGetSoftPosition(self->aEngine[MOTSOM],pCon,&fSOM); + if(iRet != 1) + { + return iRet; + } + iRet = MotorGetSoftPosition(self->aEngine[MOTSTZ],pCon,&fSTZ); + if(iRet != 1) + { + return iRet; + } + iRet = MotorGetSoftPosition(self->aEngine[MOTSOZ],pCon,&fSOZ); + if(iRet != 1) + { + return iRet; + } + + /* calculate base height of sample table */ + fBase = fSOZ + ObVal(self->aParameter,PARDH); + fPIR = 180. / 3.1415926; + + /* calculation for detector */ + fAngle = fNew - 2*fMOM; + if(fAngle < 0) + { + fAngle = fAngle + 360.; + } + fAngle /= fPIR; + fX = ObVal(self->aParameter,PARDS)*cos(fAngle); + fZ = ObVal(self->aParameter,PARDS)*sin(fAngle); + self->toStart[0].pMot = self->aEngine[MOTCOX]; + strcpy(self->toStart[0].pName,self->aEngine[MOTCOX]->name); + self->toStart[0].fTarget = fX - ObVal(self->aParameter,PARDS); + self->toStart[1].pMot = self->aEngine[MOTCOZ]; + strcpy(self->toStart[1].pName,self->aEngine[MOTCOZ]->name); + self->toStart[1].fTarget = fZ + fBase - + ObVal(self->aParameter,PARDDH); + self->toStart[2].pMot = self->aEngine[MOTCOM]; + strcpy(self->toStart[2].pName,self->aEngine[MOTCOM]->name); + self->toStart[2].fTarget = fNew - 2*fMOM; + self->iStart = 3; + + /* calculation for diaphragm 4 */ + fZ = ObVal(self->aParameter,PARDD4) * sin(fAngle); + self->toStart[3].pMot = self->aEngine[MOTD4B]; + strcpy(self->toStart[3].pName,self->aEngine[MOTD4B]->name); + self->toStart[3].fTarget = fBase + fZ - + ObVal(self->aParameter,PARD4H); + self->iStart = 4; + + /* calculation for diaphragm 5 */ + fZ = ObVal(self->aParameter,PARDD5) * sin(fAngle); + self->toStart[4].pMot = self->aEngine[MOTD5B]; + strcpy(self->toStart[4].pName,self->aEngine[MOTD5B]->name); + self->toStart[4].fTarget = fBase + fZ - + ObVal(self->aParameter,PARD5H); + self->iStart = 5; +#ifdef DEBUG + sprintf(pBueffel,"2T COZ COX COM D4B D5B "); + SCWrite(pCon,pBueffel,eValue); + sprintf(pBueffel,"%6.2f %6.2f %6.2f %6.2f %6.2f %6.2f", + fNew, self->toStart[1].fTarget, self->toStart[0].fTarget, + self->toStart[2].fTarget, self->toStart[3].fTarget, + self->toStart[4].fTarget); + SCWrite(pCon,pBueffel,eValue); +#endif + + if(ObVal(self->aParameter,ANAFLAG) > 0) + { + /* the analyzer height */ + fZ = ObVal(self->aParameter,PARADIS)*sin(fAngle); + fAOZ = fBase + fZ - ObVal(self->aParameter,PARANA); + self->toStart[5].pMot = self->aEngine[MOTAOZ]; + strcpy(self->toStart[5].pName,self->aEngine[MOTAOZ]->name); + self->toStart[5].fTarget = fAOZ; + self->iStart = 6; + + /* analyzer omega */ + self->toStart[6].pMot = self->aEngine[MOTAOM]; + strcpy(self->toStart[6].pName,self->aEngine[MOTAOM]->name); + self->toStart[6].fTarget = fNew/2. + + ObVal(self->aParameter,PARAOM); + self->iStart = 7; + + /* C3Z */ + fZ2 = (ObVal(self->aParameter,PARDS) - ObVal(self->aParameter, + PARADIS))*sin(fAngle + (fNew/fPIR) ); + + self->toStart[7].pMot = self->aEngine[MOTC3Z]; + strcpy(self->toStart[7].pName,self->aEngine[MOTC3Z]->name); + self->toStart[7].fTarget = fBase + fZ + fZ2 - + ObVal(self->aParameter,PARDDD) - + self->toStart[1].fTarget; + self->iStart = 8; +#ifdef DEBUG + sprintf(pBueffel,"2T AOZ AOM C3Z"); + SCWrite(pCon,pBueffel,eValue); + sprintf(pBueffel,"%6.2f %6.2f %6.2f %6.2f", + fNew, self->toStart[5].fTarget, self->toStart[6].fTarget, + self->toStart[7].fTarget); + SCWrite(pCon,pBueffel,eValue); +#endif + + } + return 1; + } +/*======================================================================= + Calculations for Analyzer two theta +=========================================================================*/ + static int CalculateANA2T(pAmor2T self, SConnection *pCon, float fNew) + { + double fBase, fPIR; + float fAOZ, fIncident, fSOM, fMOM, fDiffracted, fDistance, fX, fZ; + int iRet; +#ifdef DEBUG + char pBueffel[132]; +#endif + + /* calculate base height of analyzer table */ + iRet = MotorGetSoftPosition(self->aEngine[MOTSOZ],pCon,&fAOZ); + if(iRet != 1) + { + return iRet; + } + fBase = fAOZ + ObVal(self->aParameter,PARANA); + fPIR = 180. / 3.1415926; + + /* Calculate the incident angle at the analyzer */ + iRet = MotorGetSoftPosition(self->aEngine[MOTSOM],pCon,&fSOM); + if(iRet != 1) + { + return iRet; + } + iRet = MotorGetSoftPosition(self->aEngine[MOTMOM],pCon,&fMOM); + if(iRet != 1) + { + return iRet; + } + fIncident = fMOM + 2. * fSOM; + + /* calculate the angle of the diffracted beam against the + horizon at the analyzer. + + fDiffracted = fIncident - 2. * AOM. + + There is a problem here. We should read AOM in order to get the + value. However in the context of an omega - two-theta scan on AOM + and ana2t, it is fNew. + */ + fDiffracted = fIncident - fNew; + + + /* calculation for detector */ + fDiffracted /= fPIR; + fDistance = ObVal(self->aParameter,PARDS) - + ObVal(self->aParameter, PARANA); + fX = fDistance*cos(fDiffracted); + fZ = fDistance*sin(fDiffracted); + self->toStart[0].pMot = self->aEngine[MOTCOX]; + strcpy(self->toStart[0].pName,self->aEngine[MOTCOX]->name); + self->toStart[0].fTarget = fX - fDistance; + + self->toStart[1].pMot = self->aEngine[MOTCOZ]; + strcpy(self->toStart[1].pName,self->aEngine[MOTCOZ]->name); + self->toStart[1].fTarget = fZ + fBase - + ObVal(self->aParameter,PARDDH); + + self->toStart[2].pMot = self->aEngine[MOTCOM]; + strcpy(self->toStart[2].pName,self->aEngine[MOTCOM]->name); + self->toStart[2].fTarget = -fDiffracted*fPIR; + self->iStart = 3; + + /* calculation for diaphragm 5 */ + fZ = ObVal(self->aParameter,PARDD5) * sin(fDiffracted); + self->toStart[3].pMot = self->aEngine[MOTD5B]; + strcpy(self->toStart[3].pName,self->aEngine[MOTD5B]->name); + self->toStart[3].fTarget = fBase + fZ - + ObVal(self->aParameter,PARD5H); + self->iStart = 4; + +#ifdef DEBUG + sprintf(pBueffel,"2T COX COZ COM D5B "); + SCWrite(pCon,pBueffel,eValue); + sprintf(pBueffel,"%6.2f %6.2f %6.2f %6.2f %6.2f ", + fNew, self->toStart[0].fTarget, self->toStart[1].fTarget, + self->toStart[2].fTarget,self->toStart[3].fTarget); + SCWrite(pCon,pBueffel,eValue); +#endif + + return 1; + } +/*======================================================================== + Definition of interface functions. +=========================================================================*/ + static long A2TSetValue(void *pData, SConnection *pCon, float fNew) + { + int i, iRet; + pIDrivable pDriv = NULL; + pAmor2T self = (pAmor2T) pData; + + assert(self); + + /* calculation */ + iRet = CalculateAMORE(self,pCon,fNew); + if(iRet != 1) + { + return iRet; + } + + /* start them all */ + for(i = 0; i < self->iStart; i++) + { + pDriv = self->toStart[i].pMot->pDescriptor->GetInterface( + self->toStart[i].pMot,DRIVEID); + if(pDriv != NULL) + { + iRet = pDriv->SetValue(self->toStart[i].pMot,pCon, + self->toStart[i].fTarget); + if(iRet != OKOK) + { + return iRet; + } + } + } + return OKOK; + } +/*--------------------------------------------------------------------*/ + static long ANA2TSetValue(void *pData, SConnection *pCon, float fNew) + { + int i, iRet; + pIDrivable pDriv = NULL; + pAmor2T self = (pAmor2T) pData; + + assert(self); + + /* calculation */ + iRet = CalculateANA2T(self,pCon,fNew); + if(iRet != 1) + { + return iRet; + } + + /* start them all */ + for(i = 0; i < self->iStart; i++) + { + pDriv = self->toStart[i].pMot->pDescriptor->GetInterface( + self->toStart[i].pMot,DRIVEID); + if(pDriv != NULL) + { + iRet = pDriv->SetValue(self->toStart[i].pMot,pCon, + self->toStart[i].fTarget); + if(iRet != OKOK) + { + return iRet; + } + } + } + return OKOK; + } +/*-------------------------------------------------------------------------*/ + static int A2THalt(void *pData) + { + int i, iRet; + pIDrivable pDriv = NULL; + pAmor2T self = (pAmor2T) pData; + + assert(self); + + /* stop them all */ + for(i = 0; i < self->iStart; i++) + { + pDriv = self->toStart[i].pMot->pDescriptor->GetInterface( + self->toStart[i].pMot,DRIVEID); + if(pDriv != NULL) + { + iRet = pDriv->Halt(self->toStart[i].pMot); + } + } + return OKOK; + } +/*-----------------------------------------------------------------------*/ + static int A2TCheck(void *pData, float fNew, char *error, int iErrLen) + { + int i, iRet; + pIDrivable pDriv = NULL; + pAmor2T self = (pAmor2T) pData; + SConnection *pDumCon = NULL; + + + assert(self); + pDumCon = SCCreateDummyConnection(pServ->pSics); + assert(pDumCon); + + /* calculation */ + iRet = CalculateAMORE(self,pDumCon,fNew); + SCDeleteConnection(pDumCon); + if(iRet != 1) + { + return iRet; + } + + /* check them all */ + for(i = 0; i < self->iStart; i++) + { + pDriv = self->toStart[i].pMot->pDescriptor->GetInterface( + self->toStart[i].pMot,DRIVEID); + if(pDriv != NULL) + { + iRet = pDriv->CheckLimits(self->toStart[i].pMot, + self->toStart[i].fTarget, + error,iErrLen); + if(iRet != 1) + { + return iRet; + } + } + } + return 1; + } +/*-------------------------------------------------------------------*/ + static int ANA2TCheck(void *pData, float fNew, char *error, int iErrLen) + { + int i, iRet; + pIDrivable pDriv = NULL; + pAmor2T self = (pAmor2T) pData; + SConnection *pDumCon = NULL; + + + assert(self); + pDumCon = SCCreateDummyConnection(pServ->pSics); + assert(pDumCon); + + /* calculation */ + iRet = CalculateANA2T(self,pDumCon,fNew); + SCDeleteConnection(pDumCon); + if(iRet != 1) + { + return iRet; + } + + /* check them all */ + for(i = 0; i < self->iStart; i++) + { + pDriv = self->toStart[i].pMot->pDescriptor->GetInterface( + self->toStart[i].pMot,DRIVEID); + if(pDriv != NULL) + { + iRet = pDriv->CheckLimits(self->toStart[i].pMot, + self->toStart[i].fTarget, + error,iErrLen); + if(iRet != 1) + { + return iRet; + } + } + } + return 1; + } +/*------------------------------------------------------------------------*/ + static int A2TStatus(void *pData, SConnection *pCon) + { + int i, iRet; + pIDrivable pDriv = NULL; + pAmor2T self = (pAmor2T) pData; + + assert(self); + + /* check them all */ + for(i = 0; i < self->iStart; i++) + { + pDriv = self->toStart[i].pMot->pDescriptor->GetInterface( + self->toStart[i].pMot,DRIVEID); + if(pDriv != NULL) + { + iRet = pDriv->CheckStatus(self->toStart[i].pMot,pCon); + if( (iRet != OKOK) && (iRet != HWIdle) ) + { + return iRet; + } + } + } + return iRet; + } +/*------------------------------------------------------------------------*/ + static float A2TGetValue(void *pData, SConnection *pCon) + { + float fVal, fMOM, fResult; + int iRet; + pAmor2T self = (pAmor2T) pData; + + assert(self); + + /* get COM */ + iRet = MotorGetSoftPosition(self->aEngine[MOTCOM], pCon, &fVal); + if(!iRet) + { + return -9999.99; + } + /* get MOM */ + iRet = MotorGetSoftPosition(self->aEngine[MOTMOM], pCon, &fMOM); + if(!iRet) + { + return -9999.99; + } + + /* retrocalculate 2 theta */ + fResult = fVal + 2*fMOM; + return fResult; + } +/*------------------------------------------------------------------------*/ + static float ANA2TGetValue(void *pData, SConnection *pCon) + { + float fVal, fMOM, fResult; + int iRet; + pAmor2T self = (pAmor2T) pData; + + assert(self); + + /* get AOM */ + iRet = MotorGetSoftPosition(self->aEngine[MOTAOM], pCon, &fVal); + if(!iRet) + { + return -9999.99; + } + + return 2. * fVal; + + } +/*-----------------------------------------------------------------------*/ + static void *A2TGetInterface(void *pData, int iID) + { + pAmor2T self = (pAmor2T) pData; + + assert(self); + if(iID == DRIVEID) + { + return self->pDriv; + } + return NULL; + } +/*------------------------------------------------------------------------*/ + static int A2TSave(void *pData, char *name, FILE *fd) + { + int i; + pAmor2T self = (pAmor2T) pData; + + assert(self); + + fprintf(fd,"%s detectord %f \n", name, ObVal(self->aParameter,PARDS)); + fprintf(fd,"%s sampleh %f \n", name, ObVal(self->aParameter,PARDH)); + fprintf(fd,"%s d4d %f \n", name, ObVal(self->aParameter,PARDD4)); + fprintf(fd,"%s d5d %f \n", name, ObVal(self->aParameter,PARDD5)); + fprintf(fd,"%s interrupt %f \n", name, ObVal(self->aParameter,PARINT)); + fprintf(fd,"%s detectorh %f \n", name, ObVal(self->aParameter,PARDDH)); + fprintf(fd,"%s d4h %f \n", name, ObVal(self->aParameter,PARD4H)); + fprintf(fd,"%s d5h %f \n", name, ObVal(self->aParameter,PARD5H)); + fprintf(fd,"%s anah %f \n", name, ObVal(self->aParameter,PARANA)); + fprintf(fd,"%s anad %f \n", name, ObVal(self->aParameter,PARADIS)); + fprintf(fd,"%s anaflag %f \n", name, ObVal(self->aParameter,ANAFLAG)); + fprintf(fd,"%s c2h %f \n", name, ObVal(self->aParameter,PARDDD)); + fprintf(fd,"%s aomconst %f \n", name, ObVal(self->aParameter,PARAOM)); + return 1; + } +/*------------------------------------------------------------------------*/ + static void A2TList(pAmor2T self, SConnection *pCon, char *name) + { + char pBueffel[132]; + Tcl_DString tString; + + assert(pCon); + assert(self); + + Tcl_DStringInit(&tString); + sprintf(pBueffel, + "%s.detectord %f \n", name, ObVal(self->aParameter,PARDS)); + Tcl_DStringAppend(&tString,pBueffel,-1); + sprintf(pBueffel, + "%s.sampleh %f \n", name, ObVal(self->aParameter,PARDH)); + Tcl_DStringAppend(&tString,pBueffel,-1); + sprintf(pBueffel, + "%s.d4d %f \n", name, ObVal(self->aParameter,PARDD4)); + Tcl_DStringAppend(&tString,pBueffel,-1); + sprintf(pBueffel, + "%s.d5d %f \n", name, ObVal(self->aParameter,PARDD5)); + Tcl_DStringAppend(&tString,pBueffel,-1); + sprintf(pBueffel, + "%s.interrupt %f \n", name, ObVal(self->aParameter,PARINT)); + Tcl_DStringAppend(&tString,pBueffel,-1); + sprintf(pBueffel, + "%s.detectorh %f \n", name, ObVal(self->aParameter,PARDDH)); + Tcl_DStringAppend(&tString,pBueffel,-1); + sprintf(pBueffel, + "%s.d4h %f \n", name, ObVal(self->aParameter,PARD4H)); + Tcl_DStringAppend(&tString,pBueffel,-1); + sprintf(pBueffel, + "%s.d5h %f \n", name, ObVal(self->aParameter,PARD5H)); + Tcl_DStringAppend(&tString,pBueffel,-1); + sprintf(pBueffel, + "%s.anah %f \n", name, ObVal(self->aParameter,PARANA)); + Tcl_DStringAppend(&tString,pBueffel,-1); + sprintf(pBueffel, + "%s.anad %f \n", name, ObVal(self->aParameter,PARADIS)); + Tcl_DStringAppend(&tString,pBueffel,-1); + sprintf(pBueffel, + "%s.anaflag %f \n", name, ObVal(self->aParameter,ANAFLAG)); + Tcl_DStringAppend(&tString,pBueffel,-1); + sprintf(pBueffel, + "%s.c2h %f \n", name, ObVal(self->aParameter,PARDDD)); + Tcl_DStringAppend(&tString,pBueffel,-1); + sprintf(pBueffel, + "%s.aomconst %f \n", name, ObVal(self->aParameter,PARAOM)); + Tcl_DStringAppend(&tString,pBueffel,-1); + SCWrite(pCon,Tcl_DStringValue(&tString),eValue); + Tcl_DStringFree(&tString); + } +/*------------------------------------------------------------------------*/ + static void A2TKill(void *pData) + { + pAmor2T self = (pAmor2T) pData; + + if(self == NULL) + return; + + if(self->pDes) + DeleteDescriptor(self->pDes); + + if(self->pDriv) + free(self->pDriv); + + if(self->aParameter) + ObParDelete(self->aParameter); + + free(self); + } +/*-------------------------------------------------------------------------- + Initialization: All is done from the Factory function. This takes an Tcl + array as parameter which is supposed to hold the names of all motors. + This must fail if one of the motors cannot be accessed. +--------------------------------------------------------------------------*/ + int Amor2TFactory(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]) + { + pAmor2T pNew, pAOM = NULL; + int i, iRet; + char pBueffel[512]; + char *pMot = NULL; + + if(argc < 4) + { + SCWrite(pCon, + "ERROR: Insufficient number of arguments to Amor2tFactory", + eError); + return 0; + } + + /* allocate space ..............*/ + pNew = (pAmor2T)malloc(sizeof(Amor2T)); + if(!pNew) + { + SCWrite(pCon,"ERROR: out of memory in Amor2TFactory",eError); + return 0; + } + memset(pNew,0,sizeof(Amor2T)); + pNew->pDes = CreateDescriptor("Amor2T"); + pNew->aParameter = ObParCreate(MAXPAR); + pNew->pDriv = CreateDrivableInterface(); + if( (!pNew->pDes) || (!pNew->aParameter) || (!pNew->pDriv) ) + { + SCWrite(pCon,"ERROR: out of memory in Amor2TFactory",eError); + A2TKill(pNew); + return 0; + } + + /* find the motors*/ + pMot = Tcl_GetVar2(pSics->pTcl,argv[2],"mom",TCL_GLOBAL_ONLY); + if(!pMot) + { + SCWrite(pCon,"ERROR: no value for mom motr found",eError); + A2TKill(pNew); + return 0; + } + pNew->aEngine[MOTMOM] = FindMotor(pSics,pMot); + if(!pNew->aEngine[MOTMOM]) + { + sprintf(pBueffel,"ERROR: motor %s NOT found!", pMot); + SCWrite(pCon,pBueffel,eError); + A2TKill(pNew); + return 0; + } + + pMot = Tcl_GetVar2(pSics->pTcl,argv[2],"som",TCL_GLOBAL_ONLY); + if(!pMot) + { + SCWrite(pCon,"ERROR: no value for som motor found",eError); + A2TKill(pNew); + return 0; + } + pNew->aEngine[MOTSOM] = FindMotor(pSics,pMot); + if(!pNew->aEngine[MOTSOM]) + { + sprintf(pBueffel,"ERROR: motor %s NOT found!", pMot); + SCWrite(pCon,pBueffel,eError); + A2TKill(pNew); + return 0; + } + + pMot = Tcl_GetVar2(pSics->pTcl,argv[2],"coz",TCL_GLOBAL_ONLY); + if(!pMot) + { + SCWrite(pCon,"ERROR: no value for coz motor found",eError); + A2TKill(pNew); + return 0; + } + pNew->aEngine[MOTCOZ] = FindMotor(pSics,pMot); + if(!pNew->aEngine[MOTCOZ]) + { + sprintf(pBueffel,"ERROR: motor %s NOT found!", pMot); + SCWrite(pCon,pBueffel,eError); + A2TKill(pNew); + return 0; + } + + pMot = Tcl_GetVar2(pSics->pTcl,argv[2],"cox",TCL_GLOBAL_ONLY); + if(!pMot) + { + SCWrite(pCon,"ERROR: no value for cox motor found",eError); + A2TKill(pNew); + return 0; + } + pNew->aEngine[MOTCOX] = FindMotor(pSics,pMot); + if(!pNew->aEngine[MOTCOX]) + { + sprintf(pBueffel,"ERROR: motor %s NOT found!", pMot); + SCWrite(pCon,pBueffel,eError); + A2TKill(pNew); + return 0; + } + + pMot = Tcl_GetVar2(pSics->pTcl,argv[2],"stz",TCL_GLOBAL_ONLY); + if(!pMot) + { + SCWrite(pCon,"ERROR: no value for stz motor found",eError); + A2TKill(pNew); + return 0; + } + pNew->aEngine[MOTSTZ] = FindMotor(pSics,pMot); + if(!pNew->aEngine[MOTSTZ]) + { + sprintf(pBueffel,"ERROR: motor %s NOT found!", pMot); + SCWrite(pCon,pBueffel,eError); + A2TKill(pNew); + return 0; + } + + pMot = Tcl_GetVar2(pSics->pTcl,argv[2],"soz",TCL_GLOBAL_ONLY); + if(!pMot) + { + SCWrite(pCon,"ERROR: no value for soz motor found",eError); + A2TKill(pNew); + return 0; + } + pNew->aEngine[MOTSOZ] = FindMotor(pSics,pMot); + if(!pNew->aEngine[MOTSOZ]) + { + sprintf(pBueffel,"ERROR: motor %s NOT found!", pMot); + SCWrite(pCon,pBueffel,eError); + A2TKill(pNew); + return 0; + } + + pMot = Tcl_GetVar2(pSics->pTcl,argv[2],"d4b",TCL_GLOBAL_ONLY); + if(!pMot) + { + SCWrite(pCon,"ERROR: no value for d4b motor found",eError); + A2TKill(pNew); + return 0; + } + pNew->aEngine[MOTD4B] = FindMotor(pSics,pMot); + if(!pNew->aEngine[MOTD4B]) + { + sprintf(pBueffel,"ERROR: motor %s NOT found!", pMot); + SCWrite(pCon,pBueffel,eError); + A2TKill(pNew); + return 0; + } + + pMot = Tcl_GetVar2(pSics->pTcl,argv[2],"d5b",TCL_GLOBAL_ONLY); + if(!pMot) + { + SCWrite(pCon,"ERROR: no value for d5b motor found",eError); + A2TKill(pNew); + return 0; + } + pNew->aEngine[MOTD5B] = FindMotor(pSics,pMot); + if(!pNew->aEngine[MOTD5B]) + { + sprintf(pBueffel,"ERROR: motor %s NOT found!", pMot); + SCWrite(pCon,pBueffel,eError); + A2TKill(pNew); + return 0; + } + + pMot = Tcl_GetVar2(pSics->pTcl,argv[2],"com",TCL_GLOBAL_ONLY); + if(!pMot) + { + SCWrite(pCon,"ERROR: no value for com motor found",eError); + A2TKill(pNew); + return 0; + } + pNew->aEngine[MOTCOM] = FindMotor(pSics,pMot); + if(!pNew->aEngine[MOTCOM]) + { + sprintf(pBueffel,"ERROR: motor %s NOT found!", pMot); + SCWrite(pCon,pBueffel,eError); + A2TKill(pNew); + return 0; + } + + pMot = Tcl_GetVar2(pSics->pTcl,argv[2],"aoz",TCL_GLOBAL_ONLY); + if(!pMot) + { + SCWrite(pCon,"ERROR: no value for aoz motor found",eError); + A2TKill(pNew); + return 0; + } + pNew->aEngine[MOTAOZ] = FindMotor(pSics,pMot); + if(!pNew->aEngine[MOTAOZ]) + { + sprintf(pBueffel,"ERROR: motor %s NOT found!", pMot); + SCWrite(pCon,pBueffel,eError); + A2TKill(pNew); + return 0; + } + + pMot = Tcl_GetVar2(pSics->pTcl,argv[2],"aom",TCL_GLOBAL_ONLY); + if(!pMot) + { + SCWrite(pCon,"ERROR: no value for aom motor found",eError); + A2TKill(pNew); + return 0; + } + pNew->aEngine[MOTAOM] = FindMotor(pSics,pMot); + if(!pNew->aEngine[MOTAOM]) + { + sprintf(pBueffel,"ERROR: motor %s NOT found!", pMot); + SCWrite(pCon,pBueffel,eError); + A2TKill(pNew); + return 0; + } + + pMot = Tcl_GetVar2(pSics->pTcl,argv[2],"c3z",TCL_GLOBAL_ONLY); + if(!pMot) + { + SCWrite(pCon,"ERROR: no value for c3z motor found",eError); + A2TKill(pNew); + return 0; + } + pNew->aEngine[MOTC3Z] = FindMotor(pSics,pMot); + if(!pNew->aEngine[MOTC3Z]) + { + sprintf(pBueffel,"ERROR: motor %s NOT found!", pMot); + SCWrite(pCon,pBueffel,eError); + A2TKill(pNew); + return 0; + } + + + /* initialize parameters */ + ObParInit(pNew->aParameter,PARDS,"detectord",1400.,usMugger); + ObParInit(pNew->aParameter,PARDH,"sampleh",50.,usMugger); + ObParInit(pNew->aParameter,PARDD4,"d4d",100.,usMugger); + ObParInit(pNew->aParameter,PARDD5,"d5d",200.,usMugger); + ObParInit(pNew->aParameter,PARINT,"interrupt",0.,usMugger); + ObParInit(pNew->aParameter,PARDDH,"detectorh",40.,usMugger); + ObParInit(pNew->aParameter,PARD4H,"d4h",40.,usMugger); + ObParInit(pNew->aParameter,PARD5H,"d5h",400.,usMugger); + ObParInit(pNew->aParameter,PARANA,"anah",400.,usMugger); + ObParInit(pNew->aParameter,PARADIS,"anad",600.,usMugger); + ObParInit(pNew->aParameter,ANAFLAG,"anaflag",-1.,usMugger); + ObParInit(pNew->aParameter,PARDDD,"c2h",100.,usMugger); + ObParInit(pNew->aParameter,PARAOM,"aomconst",3.,usMugger); + + + /* initialize interfaces */ + pNew->pDes->GetInterface = A2TGetInterface; + pNew->pDes->SaveStatus = A2TSave; + pNew->pDriv->Halt = A2THalt; + pNew->pDriv->CheckLimits = A2TCheck; + pNew->pDriv->SetValue = A2TSetValue; + pNew->pDriv->CheckStatus = A2TStatus; + pNew->pDriv->GetValue = A2TGetValue; + + /* copy data structure for second command for aom2t */ + pAOM = (pAmor2T)malloc(sizeof(Amor2T)); + if(!pAOM) + { + A2TKill(pNew); + SCWrite(pCon,"ERROR: out of memory in Amor2TFactory",eError); + return 0; + } + memcpy(pAOM,pNew,sizeof(Amor2T)); + pAOM->pDriv = CreateDrivableInterface(); + pAOM->pDes = CreateDescriptor("Amor2T"); + if(!pAOM->pDriv || !pAOM->pDes ) + { + A2TKill(pNew); + SCWrite(pCon,"ERROR: out of memory in Amor2TFactory",eError); + return 0; + } + + /* set modified interface functions */ + pAOM->pDes->GetInterface = A2TGetInterface; + pAOM->pDriv->Halt = A2THalt; + pAOM->pDriv->CheckLimits = ANA2TCheck; + pAOM->pDriv->SetValue = ANA2TSetValue; + pAOM->pDriv->GetValue = ANA2TGetValue; + pAOM->pDriv->CheckStatus = A2TStatus; + + + /* install commands */ + iRet = AddCommand(pSics,argv[1], + Amor2TAction,A2TKill,pNew); + if(!iRet) + { + sprintf(pBueffel,"ERROR: duplicate command %s NOT created", + argv[1]); + SCWrite(pCon,pBueffel,eError); + A2TKill(pNew); + return 0; + } + iRet = AddCommand(pSics,argv[3], + Amor2TAction,free,pAOM); + if(!iRet) + { + sprintf(pBueffel,"ERROR: duplicate command %s NOT created", + argv[1]); + SCWrite(pCon,pBueffel,eError); + A2TKill(pNew); + return 0; + } + return 1; + } +/*----------------------------------------------------------------------*/ + int Amor2TAction(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]) + { + pAmor2T self = (pAmor2T)pData; + char pBueffel[256]; + float fVal; + double dVal; + ObPar *pPar = NULL; + int iRet; + + assert(self); + + if(argc > 1) + { + strtolower(argv[1]); + /* deal with list */ + if(strcmp(argv[1],"list") == 0) + { + A2TList(self,pCon,argv[0]); + return 1; + } + /* otherwise it should be a parameter command */ + if(argc >= 3) + { + iRet = Tcl_GetDouble(pSics->pTcl,argv[2],&dVal); + if(iRet != TCL_OK) + { + sprintf(pBueffel,"ERROR: failed to convert %s to number", + argv[2]); + SCWrite(pCon,pBueffel,eError); + return 0; + } + iRet = ObParSet(self->aParameter,argv[0],argv[1],(float)dVal,pCon); + if(iRet) + { + SCSendOK(pCon); + } + return iRet; + } + else + { + pPar = ObParFind(self->aParameter,argv[1]); + if(!pPar) + { + sprintf(pBueffel,"ERROR: parameter %s NOT found",argv[1]); + SCWrite(pCon,pBueffel,eError); + return 0; + } + sprintf(pBueffel,"%s.%s = %f",argv[0],pPar->name, pPar->fVal); + SCWrite(pCon,pBueffel,eValue); + return 1; + } + } + else + { + fVal = self->pDriv->GetValue(self,pCon); + sprintf(pBueffel," %s = %f", argv[0], fVal); + SCWrite(pCon,pBueffel,eValue); + return 1; + } + } + + + diff --git a/amor2t.h b/amor2t.h new file mode 100644 index 0000000..6e243cb --- /dev/null +++ b/amor2t.h @@ -0,0 +1,22 @@ + +/*------------------------------------------------------------------------- + A m o r 2 T + A class for controlling the two theta movement of a reflectometer. + Especially the AMOR reflectometer at SINQ. For details see the file + amor2t.tex. DO NOT TOUCH! This file is automatically created from amor2t.w + with nuweb. + + Mark Koennecke, September 1999 +---------------------------------------------------------------------------*/ +#ifndef AMOR2T +#define AMOR2T + + typedef struct __AMOR2T *pAmor2T; + + int Amor2TFactory(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]); + int Amor2TAction(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]); + void Amor2TKill(void *pData); + +#endif diff --git a/amor2t.i b/amor2t.i new file mode 100644 index 0000000..2bcf2ae --- /dev/null +++ b/amor2t.i @@ -0,0 +1,55 @@ + +/*-------------------------------------------------------------------------- + A m o r 2 T . i + Internal data structure definitions for Amor2T. For details see amor2t.tex. + DO NOT TOUCH! This file is automatically created from amor2t.w. + + Mark Koennecke, September 1999 +----------------------------------------------------------------------------*/ + +/* distance detector sample */ +#define PARDS 0 +/* constant height of sample: height = PARDH + MOTSOZ + MOTSTZ */ +#define PARDH 1 +/* distance diaphragm 4 - sample */ +#define PARDD4 2 +/* distance to diaphragm 5 */ +#define PARDD5 3 +/* interrupt to issue when a motor fails on this */ +#define PARINT 4 +/* base height of counter station */ +#define PARDDH 5 +/* height of D4 */ +#define PARD4H 6 +/* height of D5 */ +#define PARD5H 7 +/* base height of analyzer */ +#define PARANA 8 +/* distance of analyzer from sample */ +#define PARADIS 9 +/* flag analyzer calculation on/off */ +#define ANAFLAG 10 +/* constant for second detector */ +#define PARDDD 11 +/* constant part of AOM */ +#define PARAOM 12 + + + typedef struct { + pMotor pMot; + char pName[80]; + float fTarget; + }MotEntry, *pMotEntry; + + + + typedef struct __AMOR2T { + pObjectDescriptor pDes; + pIDrivable pDriv; + pMotor aEngine[MAXMOT]; + MotEntry toStart[MAXMOT]; + int iStart; + ObPar *aParameter; + }Amor2T; + + diff --git a/amor2t.tex b/amor2t.tex new file mode 100644 index 0000000..c458706 --- /dev/null +++ b/amor2t.tex @@ -0,0 +1,204 @@ +\subsection{AMOR Two Theta} +AMOR is SINQ's new reflectometer. It has the peculiar feature that the +two theta movement of the detector is expressed in translations along +the reflectometer base axis and the detector height. Additionally the +detector is tilted. The height of two diaphragms has to be adjusted as +well. And, in polarizing mode, the analyzer has to be operated as +well. Quite a complicated movement. I fear this module may only be +useful for AMOR, but may be, other reflectometers may profit as well. +This object implements this complex movement as a virtual motor. + +The following formulas are used for the necessary calculations: +\begin{eqnarray} +delta height & = & h_{s} - \sin \alpha \\ +delta x & = & |x_{c} - x_{s}| - R \cos \alpha \\ +omega & = & -2 MOM + 2 SOM \\ +\end{eqnarray} +with +\begin{eqnarray} +h_{s} & = & \tan(2MOM)|x_{c} - x_{s}| \\ +R & = & \sqrt{hs^{2} - |x_{c} - x_{s}|^{2}} \\ +\alpha & = & ATT - 2SOM \\ +\beta & = & 180 - 90 - 2MOM \\ +MOM & = & polarizer \omega \\ +SOM & = & sample \omega \\ +x_{c} & = & counter position \\ +x_{s} & = & sample position\\ +\end{eqnarray} +The same equations hold true for the calculations of the diaphragm +heights, just replace the distances. The equations for the analyzer +are not yet known. + +Due to this complicated movement this module needs to know about a lot +of motors and a lot of parameters. The distances of the various +components need to be modified at run time in order to allow for +configuration changes. These are not motorized but must be entered +manually. + +\subsubsection{Data Structures} +Consequently data structures are complex. The first data structure +used is an entry in an array of motors to start: +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap1} +$\langle$putput {\footnotesize ?}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@@\\ +\mbox{}\verb@ typedef struct {@\\ +\mbox{}\verb@ pMotor pMot;@\\ +\mbox{}\verb@ char pName[80];@\\ +\mbox{}\verb@ float fTarget;@\\ +\mbox{}\verb@ }MotEntry, *pMotEntry;@\\ +\mbox{}\verb@@$\diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro referenced in scrap ?. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{description} +\item[pMot] is a pointer to the motors data structure. +\item[pName] is the name of the motor to start. +\item[fTarget] is the target value for the motor. +\end{description} + +The next data structure is the class data structure for amor2t: +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap2} +$\langle$amoredata {\footnotesize ?}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@@\\ +\mbox{}\verb@ typedef struct __AMOR2T {@\\ +\mbox{}\verb@ pObjectDescriptor pDes;@\\ +\mbox{}\verb@ pIDrivable pDriv;@\\ +\mbox{}\verb@ pMotor aEngine[MAXMOT];@\\ +\mbox{}\verb@ MotEntry toStart[MAXMOT];@\\ +\mbox{}\verb@ int iStart;@\\ +\mbox{}\verb@ ObPar *aParameter;@\\ +\mbox{}\verb@ }Amor2T;@\\ +\mbox{}\verb@@$\diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro referenced in scrap ?. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{description} +\item[pDes] The standard SICS object descriptor. +\item[pDriv] The drivable interface. The functions defined for the +drivable interface implement most of the work of this class. +\item[aEngine] An array of pointers to the motor data structures this +class has to deal with. The proper initialization of this is taken +care of during the initialization of the object. +\item[toStart] An array of motors to start when all calculations have +been performed. +\item[iStart] The number of valid entries in toStart. +\item[aParameter] An array of parameters for this object. +\end{description} + +\subsubsection{The Interface} +The interface to this module is quite primitive. Most of the +functionality is hidden in the drivable interface. So there are only +functions for interacting with the interpreter. + +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap3} +$\langle$amorinterface {\footnotesize ?}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@@\\ +\mbox{}\verb@ typedef struct __AMOR2T *pAmor2T;@\\ +\mbox{}\verb@@\\ +\mbox{}\verb@ int Amor2TFactory(SConnection *pCon, SicsInterp *pSics, void *pData,@\\ +\mbox{}\verb@ int argc, char *argv[]);@\\ +\mbox{}\verb@ int Amor2TAction(SConnection *pCon, SicsInterp *pSics, void *pData,@\\ +\mbox{}\verb@ int argc, char *argv[]);@\\ +\mbox{}\verb@ void Amor2TKill(void *pData); @\\ +\mbox{}\verb@@$\diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro referenced in scrap ?. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap4} +\verb@"amor2t.i"@ {\footnotesize ? }$\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@@\\ +\mbox{}\verb@/*--------------------------------------------------------------------------@\\ +\mbox{}\verb@ A m o r 2 T . i@\\ +\mbox{}\verb@ Internal data structure definitions for Amor2T. For details see amor2t.tex.@\\ +\mbox{}\verb@ DO NOT TOUCH! This file is automatically created from amor2t.w.@\\ +\mbox{}\verb@@\\ +\mbox{}\verb@ Mark Koennecke, September 1999@\\ +\mbox{}\verb@----------------------------------------------------------------------------*/@\\ +\mbox{}\verb@@\\ +\mbox{}\verb@/* distance detector sample */@\\ +\mbox{}\verb@#define PARDS 0@\\ +\mbox{}\verb@/* constant height of sample: height = PARDH + MOTSOZ + MOTSTZ */@\\ +\mbox{}\verb@#define PARDH 1@\\ +\mbox{}\verb@/* distance diaphragm 4 - sample */@\\ +\mbox{}\verb@#define PARDD4 2@\\ +\mbox{}\verb@/* distance to diaphragm 5 */@\\ +\mbox{}\verb@#define PARDD5 3@\\ +\mbox{}\verb@/* interrupt to issue when a motor fails on this */@\\ +\mbox{}\verb@#define PARINT 4@\\ +\mbox{}\verb@/* base height of counter station */@\\ +\mbox{}\verb@#define PARDDH 5@\\ +\mbox{}\verb@/* height of D4 */@\\ +\mbox{}\verb@#define PARD4H 6@\\ +\mbox{}\verb@/* height of D5 */@\\ +\mbox{}\verb@#define PARD5H 7@\\ +\mbox{}\verb@/* base height of analyzer */@\\ +\mbox{}\verb@#define PARANA 8@\\ +\mbox{}\verb@/* distance of analyzer from sample */@\\ +\mbox{}\verb@#define PARADIS 9@\\ +\mbox{}\verb@/* flag analyzer calculation on/off */@\\ +\mbox{}\verb@#define ANAFLAG 10@\\ +\mbox{}\verb@/* constant for second detector */@\\ +\mbox{}\verb@#define PARDDD 11@\\ +\mbox{}\verb@/* constant part of AOM */@\\ +\mbox{}\verb@#define PARAOM 12@\\ +\mbox{}\verb@@\\ +\mbox{}\verb@@$\langle$putput {\footnotesize ?}$\rangle$\verb@@\\ +\mbox{}\verb@@\\ +\mbox{}\verb@@$\langle$amoredata {\footnotesize ?}$\rangle$\verb@@\\ +\mbox{}\verb@@\\ +\mbox{}\verb@@$\diamond$ +\end{list} +\vspace{-2ex} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap5} +\verb@"amor2t.h"@ {\footnotesize ? }$\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@@\\ +\mbox{}\verb@/*-------------------------------------------------------------------------@\\ +\mbox{}\verb@ A m o r 2 T@\\ +\mbox{}\verb@ A class for controlling the two theta movement of a reflectometer. @\\ +\mbox{}\verb@ Especially the AMOR reflectometer at SINQ. For details see the file @\\ +\mbox{}\verb@ amor2t.tex. DO NOT TOUCH! This file is automatically created from amor2t.w@\\ +\mbox{}\verb@ with nuweb.@\\ +\mbox{}\verb@@\\ +\mbox{}\verb@ Mark Koennecke, September 1999@\\ +\mbox{}\verb@---------------------------------------------------------------------------*/@\\ +\mbox{}\verb@#ifndef AMOR2T@\\ +\mbox{}\verb@#define AMOR2T@\\ +\mbox{}\verb@@$\langle$amorinterface {\footnotesize ?}$\rangle$\verb@@\\ +\mbox{}\verb@#endif @\\ +\mbox{}\verb@@$\diamond$ +\end{list} +\vspace{-2ex} +\end{minipage}\\[4ex] +\end{flushleft} diff --git a/amor2t.w b/amor2t.w new file mode 100644 index 0000000..9c28a7f --- /dev/null +++ b/amor2t.w @@ -0,0 +1,150 @@ +\subsection{AMOR Two Theta} +AMOR is SINQ's new reflectometer. It has the peculiar feature that the +two theta movement of the detector is expressed in translations along +the reflectometer base axis and the detector height. Additionally the +detector is tilted. The height of two diaphragms has to be adjusted as +well. And, in polarizing mode, the analyzer has to be operated as +well. Quite a complicated movement. I fear this module may only be +useful for AMOR, but may be, other reflectometers may profit as well. +This object implements this complex movement as a virtual motor. + +The following formulas are used for the necessary calculations: +\begin{eqnarray} +delta height & = & h_{s} - \sin \alpha \\ +delta x & = & |x_{c} - x_{s}| - R \cos \alpha \\ +omega & = & -2 MOM + 2 SOM \\ +\end{eqnarray} +with +\begin{eqnarray} +h_{s} & = & \tan(2MOM)|x_{c} - x_{s}| \\ +R & = & \sqrt{hs^{2} - |x_{c} - x_{s}|^{2}} \\ +\alpha & = & ATT - 2SOM \\ +\beta & = & 180 - 90 - 2MOM \\ +MOM & = & polarizer \omega \\ +SOM & = & sample \omega \\ +x_{c} & = & counter position \\ +x_{s} & = & sample position\\ +\end{eqnarray} +The same equations hold true for the calculations of the diaphragm +heights, just replace the distances. The equations for the analyzer +are not yet known. + +Due to this complicated movement this module needs to know about a lot +of motors and a lot of parameters. The distances of the various +components need to be modified at run time in order to allow for +configuration changes. These are not motorized but must be entered +manually. + +\subsubsection{Data Structures} +Consequently data structures are complex. The first data structure +used is an entry in an array of motors to start: +@d putput @{ + typedef struct { + pMotor pMot; + char pName[80]; + float fTarget; + }MotEntry, *pMotEntry; +@} +\begin{description} +\item[pMot] is a pointer to the motors data structure. +\item[pName] is the name of the motor to start. +\item[fTarget] is the target value for the motor. +\end{description} + +The next data structure is the class data structure for amor2t: +@d amoredata @{ + typedef struct __AMOR2T { + pObjectDescriptor pDes; + pIDrivable pDriv; + pMotor aEngine[MAXMOT]; + MotEntry toStart[MAXMOT]; + int iStart; + ObPar *aParameter; + }Amor2T; +@} +\begin{description} +\item[pDes] The standard SICS object descriptor. +\item[pDriv] The drivable interface. The functions defined for the +drivable interface implement most of the work of this class. +\item[aEngine] An array of pointers to the motor data structures this +class has to deal with. The proper initialization of this is taken +care of during the initialization of the object. +\item[toStart] An array of motors to start when all calculations have +been performed. +\item[iStart] The number of valid entries in toStart. +\item[aParameter] An array of parameters for this object. +\end{description} + +\subsubsection{The Interface} +The interface to this module is quite primitive. Most of the +functionality is hidden in the drivable interface. So there are only +functions for interacting with the interpreter. + +@d amorinterface @{ + typedef struct __AMOR2T *pAmor2T; + + int Amor2TFactory(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]); + int Amor2TAction(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]); + void Amor2TKill(void *pData); +@} + +@o amor2t.i @{ +/*-------------------------------------------------------------------------- + A m o r 2 T . i + Internal data structure definitions for Amor2T. For details see amor2t.tex. + DO NOT TOUCH! This file is automatically created from amor2t.w. + + Mark Koennecke, September 1999 +----------------------------------------------------------------------------*/ + +/* distance detector sample */ +#define PARDS 0 +/* constant height of sample: height = PARDH + MOTSOZ + MOTSTZ */ +#define PARDH 1 +/* distance diaphragm 4 - sample */ +#define PARDD4 2 +/* distance to diaphragm 5 */ +#define PARDD5 3 +/* interrupt to issue when a motor fails on this */ +#define PARINT 4 +/* base height of counter station */ +#define PARDDH 5 +/* height of D4 */ +#define PARD4H 6 +/* height of D5 */ +#define PARD5H 7 +/* base height of analyzer */ +#define PARANA 8 +/* distance of analyzer from sample */ +#define PARADIS 9 +/* flag analyzer calculation on/off */ +#define ANAFLAG 10 +/* constant for second detector */ +#define PARDDD 11 +/* constant part of AOM */ +#define PARAOM 12 + +@ + +@ + +@} + +@o amor2t.h @{ +/*------------------------------------------------------------------------- + A m o r 2 T + A class for controlling the two theta movement of a reflectometer. + Especially the AMOR reflectometer at SINQ. For details see the file + amor2t.tex. DO NOT TOUCH! This file is automatically created from amor2t.w + with nuweb. + + Mark Koennecke, September 1999 +---------------------------------------------------------------------------*/ +#ifndef AMOR2T +#define AMOR2T +@ +#endif +@} + diff --git a/amorscan.c b/amorscan.c new file mode 100644 index 0000000..0e53086 --- /dev/null +++ b/amorscan.c @@ -0,0 +1,140 @@ +/*------------------------------------------------------------------------- + A M O R S C A N + + An adaption of the general scan routine to deal with special issues at + the reflectometer AMOR at SINQ. + + copyright: see copyright.h + + Mark Koennecke, September 1999 +--------------------------------------------------------------------------*/ +#include +#include +#include +#include +#include +#include +#include +#include "nxamor.h" +#include "amorscan.h" + +/*--------------------------------------------------------------------*/ + static int AmorHeader(pScanData self) + { + return WriteAmorHeader(self->pFile, self->pCon); + } +/*--------------------------------------------------------------------*/ + static int AmorPoints(pScanData self, int iP) + { + /* write only at last scan point */ + if((iP+1) >= self->iNP) + { + return WriteAmorScan(self->pFile,self->pCon,self); + } + } +/*--------------------------------------------------------------------*/ + static int AmorCollect(pScanData self, int iP) + { + pVarEntry pVar = NULL; + void *pDings; + int i, iRet, status; + float fVal; + char pStatus[512], pItem[20]; + char pHead[512]; + CountEntry sCount; + + assert(self); + assert(self->pCon); + + /* prepare output header */ + sprintf(pHead,"%-5.5s","NP"); + sprintf(pStatus,"%-5d",iP); + + /* loop over all scan variables */ + status = 1; + for(i = 0; i < self->iScanVar; i++) + { + DynarGet(self->pScanVar,i,&pDings); + pVar = (pVarEntry)pDings; + if(pVar) + { + fVal = pVar->pInter->GetValue(pVar->pObject,self->pCon); + pVar->fData[iP] = fVal; + sprintf(pItem,"%-10.10s",pVar->Name); + strcat(pHead,pItem); + sprintf(pItem,"%-10.3f",fVal); + strcat(pStatus,pItem); + } + } + + /* store counter data */ + /* monitors */ + for(i = 1; i < 10; i++) + { + sCount.Monitors[i-1] = GetMonitor((pCounter)self->pCounterData,i, + self->pCon); + } + if( self->iChannel != 0 && self->iChannel != -10 ) + { + sCount.Monitors[self->iChannel - 1] = + GetCounts((pCounter)self->pCounterData, + self->pCon); + } + /* counter1 */ + strcat(pHead,"Counter1 "); + sCount.lCount = GetCounts((pCounter)self->pCounterData,self->pCon); + sprintf(pItem,"%-15d",sCount.lCount); + strcat(pStatus,pItem); + + /* + WARNING + Assignements have to be checked when the Schlumpfes are + ready putting the counter box together. + */ + + /* counter2 */ + strcat(pHead,"Counter2 "); + sCount.Monitors[0] = GetMonitor((pCounter)self->pCounterData, + 1,self->pCon); + sprintf(pItem,"%-15d",sCount.Monitors[0]); + strcat(pStatus,pItem); + + /* monitors */ + sCount.Monitors[3] = GetMonitor((pCounter)self->pCounterData, + 2,self->pCon); + sCount.Monitors[4] = GetMonitor((pCounter)self->pCounterData, + 3,self->pCon); + + /* get time */ + sCount.fTime = GetCountTime((pCounter)self->pCounterData, + self->pCon); + strcat(pHead,"Monitor1 "); + sprintf(pItem,"%-12d",sCount.Monitors[3]); + strcat(pStatus,pItem); + strcat(pHead,"Monitor2 "); + sprintf(pItem,"%-12d",sCount.Monitors[4]); + strcat(pStatus,pItem); + strcat(pHead,"Time "); + sprintf(pItem,"%-6.1f",sCount.fTime); + strcat(pStatus,pItem); + + /* write progress */ + strcat(pHead,"\n"); + strcat(pStatus,"\n"); + SCWrite(self->pCon,pHead,eWarning); + SCWrite(self->pCon,pStatus,eWarning); + + /* stow away */ + DynarReplace(self->pCounts,self->iCounts,&sCount,sizeof(CountEntry)); + self->iCounts++; + return 1; + } +/*-----------------------------------------------------------------------*/ + int ConfigureAmor(pScanData self) + { + self->WriteHeader = AmorHeader; + self->WriteScanPoints = AmorPoints; + self->CollectScanData = AmorCollect; + strcpy(self->ext,".hdf"); + return 1; + } diff --git a/amorscan.h b/amorscan.h new file mode 100644 index 0000000..d97195f --- /dev/null +++ b/amorscan.h @@ -0,0 +1,15 @@ + +/*----------------------------------------------------------------------- + A M O R S C A N + Adaption of the scan command to do things specific to the + reflectometer AMOR at SINQ. + + Mark Koennecke, September 1999 +-----------------------------------------------------------------------*/ +#ifndef AMORSCAN +#define AMORSCAN + + int ConfigureAmor(pScanData pScan); + +#endif + diff --git a/amorscan.w b/amorscan.w new file mode 100644 index 0000000..b52be00 --- /dev/null +++ b/amorscan.w @@ -0,0 +1,57 @@ +\subsection{Amor Scan} +This is a special adaption of the general scan routines for the +reflectometer AMOR at SINQ. It works by replacing the configurable +routines in the general scan command with special ones, suited to the +reflectometers purpose. There are several adaptions to the standard +scan command: +\begin{itemize} +\item Data is written to NeXus files instead of ASCII files. +\item There are two counters to keep track of. +\item Furthermore stubs are provided for dealing with spin flippers. +\end{itemize} + +In order to keep track of counters and monitors the following +convention has been devised: +\begin{itemize} +\item GetCounts gets the main detector. +\item GetMonitor 0 the second detector +\item GetMonitor 1 the first detector other spin +\item GetMonitor 2 the second detector other spin +\item GetMonitor 3 the first monitor +\item GetMonitor 4 the second monitor +\end{itemize} +Thus the monitor channels are used to keep the additional counter +information. + +This module provides only one external function: +@d amorscan @{ + int ConfigureAmor(pScanData pScan); +@} +which configures the variable fields and function pointers in pScan to +functions defined in this module. These then do the right thing. This +module is also an example of how the scan command can be configured to do +tricks based on the syntax and hooks defined in scan.*. + + +@o amorscan.h @{ +/*----------------------------------------------------------------------- + A M O R S C A N + Adaption of the scan command to do things specific to the + reflectometer AMOR at SINQ. + + Mark Koennecke, September 1999 +-----------------------------------------------------------------------*/ +#ifndef AMORSCAN +#define AMORSCAN +@ +#endif + +@} + + + + + + + + diff --git a/amorstat.c b/amorstat.c new file mode 100644 index 0000000..fb422b9 --- /dev/null +++ b/amorstat.c @@ -0,0 +1,919 @@ +/*-------------------------------------------------------------------------- + A M O R S T A T U S + + The implementation file for the amor status display facilitator module. The + reflectometer AMOR needs some advanced feautures for its status display. + These needs are taken care of here. + + copyright: see copyright.h + + Mark Koennecke, September 1999 + + As AMOR's histogram memory becomes too big in tof mode to transfer it + for status information the collapse and subsample functionalities have + been moved to the histogram memory. This code had to be modified to + call SINQHMProject directly. + + Mark Koennecke, August 2001 + --------------------------------------------------------------------------*/ +#include +#include +#include +#include +#include "fortify.h" +#include "sics.h" +#include "counter.h" +#include "stringdict.h" +#include "HistMem.h" +#include "HistMem.i" +#include "HistDriv.i" +#include "hardsup/sinqhm.h" +#include "sinqhmdriv.i" +#include "scan.h" +#include "lld.h" +#include "amorstat.i" +#include "amorstat.h" +/*------------------------------------------------------------------------- + A static which determines if we are in TOF or scan mode. +*/ + static int iTOF = 0; + static pHistMem pHMHM = NULL; +/*-------------------------------------------------------------------------*/ + static int HMCountStartCallback(int iEvent, void *pEvent, void *pUser) + { + SConnection *pCon = (SConnection *)pUser; + const float *fTime = NULL; + int *iTime = NULL; + int iLength, iRet, i; + + assert(pCon); + + if(iEvent == COUNTSTART) + { + /* send current time binning */ + iTOF = 1; + fTime = GetHistTimeBin(pHMHM,&iLength); + iTime = (int *)malloc((iLength+1)*sizeof(int)); + if( (!fTime) || (!iTime)) + { + return 0; + } + iTime[0] = htonl(iLength); + for(i = 0 ; i < iLength; i++) + { + iTime[i+1] = htonl((int)((fTime[i]/10.)*65536.)); + } + /* send new time binning to all clients */ + SCWrite(pCon,"TOFClear",eError); + SCWriteUUencoded(pCon,"arrowaxis_time",iTime, + (iLength+1)*sizeof(int)); + free(iTime); + } + return 1; + } +/*-------------------------------------------------------------------------*/ + static int ScanStartCallback(int iEvent, void *pEvent, void *pUser) + { + float *fAxis = NULL; + int *iAxis = NULL; + int iLength, iRet, i; + char pBueffel[80], pName[40]; + SConnection *pCon = (SConnection *)pUser; + pScanData pScan = (pScanData)pEvent; + + assert(pCon); + assert(pScan); + + + if(iEvent == SCANSTART) + { + iTOF = 0; + /* send current axis */ + iLength = GetScanNP(pScan); + fAxis = (float *)malloc((iLength+1)*sizeof(float)); + iAxis = (int *)malloc((iLength+1)*sizeof(int)); + if( (!fAxis) || (!iAxis)) + { + return 0; + } + iAxis[0] = htonl(iLength); + GetSoftScanVar(pScan,0,fAxis,iLength); + GetScanVarName(pScan,0,pName,39); + sprintf(pBueffel,"arrowaxis_%s",pName); + for(i = 0 ; i < iLength; i++) + { + iAxis[i+1] = htonl((int)(fAxis[i]*65536.)); + } + /* send new axis to client */ + SCWrite(pCon,"SCANClear",eError); + SCWriteUUencoded(pCon,pBueffel,iAxis, + (iLength+1)*sizeof(int)); + free(iAxis); + free(fAxis); + } + return 1; + } +/*------------------------------------------------------------------------*/ + static int ScanPointCallback(int iEvent, void *pEvent, void *pUser) + { + long *lData = NULL; + int *iData = NULL; + int iLength, iRet, i; + SConnection *pCon = (SConnection *)pUser; + pScanData pScan = (pScanData)pEvent; + + assert(pCon); + assert(pScan); + + + if( (iEvent == SCANPOINT) || (iEvent == SCANEND) ) + { + /* send current data */ + iTOF = 0; + iLength = GetScanNP(pScan); + lData = (long *)malloc((iLength+1)*sizeof(long)); + iData = (int *)malloc((iLength+1)*sizeof(int)); + if( (!lData) || (!iData)) + { + return 0; + } + iData[0] = htonl(iLength); + GetScanCounts(pScan,lData,iLength); + for(i = 0 ; i < iLength; i++) + { + iData[i+1] = htonl((int)(lData[i])); + } + /* send counts to client */ + SCWriteUUencoded(pCon,"arrow_spinupup",iData, + (iLength+1)*sizeof(int)); + /* send counts for other detector */ + GetScanMonitor(pScan,2,lData,iLength); + for(i = 0 ; i < iLength; i++) + { + iData[i+1] = htonl((int)(lData[i])); + } + SCWriteUUencoded(pCon,"arrow_spinuplo",iData, + (iLength+1)*sizeof(int)); + /* to do: check for polarization and send spinlo */ + free(iData); + free(lData); + } + return 1; + } +/*------------------------------------------------------------------------*/ + static int SendLoadedData(pAmorStat self, SConnection *pCon) + { + int i, iRet, *iData = NULL; + char pBueffel[80]; + UserData ud; + + SCWrite(pCon,"loaded_CLEAR",eValue); + iRet = LLDnodePtr2First(self->iUserList); + while(iRet != 0) + { + LLDnodeDataTo(self->iUserList,&ud); + iData = (int *)malloc((ud.iNP*2 + 1)*sizeof(int)); + if(!iData) + { + return 0; + } + iData[0] = htonl(ud.iNP); + for(i = 0; i < ud.iNP; i++) + { + iData[i+1] = htonl((int)(ud.fX[i]*65536)); + iData[i+1+ud.iNP] = htonl((int)(ud.fY[i]*65536)); + } + sprintf(pBueffel,"loaded_%s",ud.name); + SCWriteUUencoded(pCon,pBueffel,iData,(ud.iNP*2+1)*sizeof(int)); + iRet = LLDnodePtr2Next(self->iUserList); + } + } +/*------------------------------------------------------------------------*/ + static int LoadCallback(int iEvent, void *pEvent, void *pUser) + { + pAmorStat pAS = NULL; + SConnection *pCon = NULL; + + if(iEvent == FILELOADED) + { + pAS = (pAmorStat)pEvent; + pCon = (SConnection *)pUser; + assert(pAS); + assert(pCon); + SendLoadedData(pAS,pCon); + } + return 1; + } +/*-------------------------------------------------------------------------*/ + static void ClearUserData(pAmorStat self) + { + int iRet; + UserData ud; + + iRet = LLDnodePtr2First(self->iUserList); + while(iRet != 0) + { + LLDnodeDataTo(self->iUserList,&ud); + if(ud.fX != NULL) + free(ud.fX); + if(ud.fY != NULL) + free(ud.fY); + if(ud.name != NULL) + free(ud.name); + iRet = LLDnodePtr2Next(self->iUserList); + } + LLDdelete(self->iUserList); + self->iUserList = LLDcreate(sizeof(UserData)); + } +/*----------------------------------------------------------------------*/ + void KillAmorStatus(void *pData) + { + pAmorStat self = (pAmorStat)pData; + + if(!self) + return; + + if(self->iUserList >= 0) + { + ClearUserData(self); + LLDdelete(self->iUserList); + } + if(self->pDes) + DeleteDescriptor(self->pDes); + if(self->pCall) + DeleteCallBackInterface(self->pCall); + free(self); + } +/*------------------------------------------------------------------*/ + int AmorStatusFactory(SConnection *pCon, SicsInterp *pSics, + void *pData, int argc, char *argv[]) + { + pAmorStat pNew = NULL; + CommandList *pCom = NULL; + char pBueffel[256]; + int iRet; + + /* check number of arguments */ + if(argc < 4) + { + sprintf(pBueffel,"ERROR: insufficient number of arguments to %s", + argv[0]); + SCWrite(pCon,pBueffel,eError); + return 0; + } + + /* allocate a new data structure */ + pNew = (pAmorStat)malloc(sizeof(AmorStat)); + if(!pNew) + { + sprintf(pBueffel,"ERROR: out of memory in %s",argv[0]); + SCWrite(pCon,pBueffel,eError); + return 0; + } + memset(pNew,0,sizeof(AmorStat)); + pNew->pDes = CreateDescriptor("AmorStatus"); + pNew->iUserList = LLDcreate(sizeof(UserData)); + pNew->pCall = CreateCallBackInterface(); + if( (!pNew->pDes) || (pNew->iUserList < 0) || (!pNew->pCall) ) + { + sprintf(pBueffel,"ERROR: out of memory in %s",argv[0]); + SCWrite(pCon,pBueffel,eError); + KillAmorStatus(pNew); + return 0; + } + + /* to locate the HM and the scan object */ + pCom = FindCommand(pSics,argv[2]); + if(pCom) + { + if(pCom->pData) + { + if(!iHasType(pCom->pData,"ScanObject")) + { + sprintf(pBueffel,"ERROR: %s is NO scan object",argv[2]); + SCWrite(pCon,pBueffel,eError); + KillAmorStatus(pNew); + return 0; + } + } + else + { + sprintf(pBueffel,"ERROR: %s is NO scan object",argv[2]); + SCWrite(pCon,pBueffel,eError); + KillAmorStatus(pNew); + return 0; + } + } + else + { + sprintf(pBueffel,"ERROR: %s NOT found",argv[2]); + SCWrite(pCon,pBueffel,eError); + KillAmorStatus(pNew); + return 0; + } + pNew->pScan = (pScanData)pCom->pData; + pCom = FindCommand(pSics,argv[3]); + if(pCom) + { + if(pCom->pData) + { + if(!iHasType(pCom->pData,"HistMem")) + { + sprintf(pBueffel,"ERROR: %s is NO histogram memory object", + argv[3]); + SCWrite(pCon,pBueffel,eError); + KillAmorStatus(pNew); + return 0; + } + } + else + { + sprintf(pBueffel,"ERROR: %s is NO histogram memory object", + argv[3]); + SCWrite(pCon,pBueffel,eError); + KillAmorStatus(pNew); + return 0; + } + } + else + { + sprintf(pBueffel,"ERROR: %s NOT found",argv[3]); + SCWrite(pCon,pBueffel,eError); + KillAmorStatus(pNew); + return 0; + } + pNew->pHM = (pHistMem)pCom->pData; + pHMHM = (pHistMem)pCom->pData; + + /* install command */ + iRet = AddCommand(pSics,argv[1], + AmorStatusAction,KillAmorStatus,pNew); + if(!iRet) + { + sprintf(pBueffel,"ERROR: duplicate command %s NOT created", + argv[1]); + SCWrite(pCon,pBueffel,eError); + KillAmorStatus(pNew); + return 0; + } + + + return 1; + } +/*------------------------------------------------------------------*/ + static int RegisterInterest(pAmorStat self, SConnection *pCon) + { + long lID; + pDummy pDum = NULL; + pICallBack pCall = NULL; + + assert(self); + assert(pCon); + + /* Register all the callbacks. Dependent on the state of + iTOF invoke the apropriate callbacks in order to force + an initial update. + */ + /* file load callback */ + lID = RegisterCallback(self->pCall, FILELOADED, LoadCallback, + pCon, NULL); + SCRegister(pCon,pServ->pSics, self->pCall,lID); + SendLoadedData(self,pCon); + + /* scan object */ + pDum = (pDummy)self->pScan; + pCall = pDum->pDescriptor->GetInterface(pDum,CALLBACKINTERFACE); + if(pCall) + { + lID = RegisterCallback(pCall,SCANSTART,ScanStartCallback, + pCon, NULL); + SCRegister(pCon,pServ->pSics,pCall,lID); + lID = RegisterCallback(pCall,SCANPOINT,ScanPointCallback, + pCon, NULL); + SCRegister(pCon,pServ->pSics,pCall,lID); + lID = RegisterCallback(pCall,SCANEND,ScanPointCallback, + pCon, NULL); + SCRegister(pCon,pServ->pSics,pCall,lID); + if(iTOF == 0) + { + ScanStartCallback(SCANSTART,pDum,pCon); + ScanPointCallback(SCANPOINT,pDum,pCon); + } + } + pDum = (pDummy)self->pHM; + pCall = pDum->pDescriptor->GetInterface(pDum,CALLBACKINTERFACE); + if(pCall) + { + lID = RegisterCallback(pCall,COUNTSTART,HMCountStartCallback, + pCon, NULL); + SCRegister(pCon,pServ->pSics,pCall,lID); + if(iTOF == 1) + { + HMCountStartCallback(COUNTSTART,pDum,pCon); + } + } + return 1; + } +/*-----------------------------------------------------------------*/ + static int FileLoad(pAmorStat self, SConnection *pCon, + char *name, double dScale) + { + char pBueffel[256], pDummy[50]; + FILE *fd = NULL; + UserData ud; + int iNP, i; + float fDummy; + + /* open the file */ + fd = fopen(name,"r"); + if(!fd) + { + sprintf(pBueffel,"ERROR: cannot open %s for reading",name); + SCWrite(pCon,pBueffel,eError); + return 0; + } + + /* skip first line */ + if(fgets(pBueffel,255,fd) == NULL) + { + SCWrite(pCon,"ERROR: premature end of file",eError); + fclose(fd); + return 0; + } + + /* read number of points in second line */ + if(fgets(pBueffel,255,fd) == NULL) + { + SCWrite(pCon,"ERROR: premature end of file",eError); + fclose(fd); + return 0; + } + sscanf(pBueffel,"%s %d",pDummy, &iNP); + /* allocate data */ + ud.iNP = iNP; + ud.fX = (float *)malloc(iNP*sizeof(float)); + ud.fY = (float *)malloc(iNP*sizeof(float)); + ud.name = strdup(name); + + /* skip two lines */ + if(fgets(pBueffel,255,fd) == NULL) + { + SCWrite(pCon,"ERROR: premature end of file",eError); + fclose(fd); + return 0; + } + if(fgets(pBueffel,255,fd) == NULL) + { + SCWrite(pCon,"ERROR: premature end of file",eError); + fclose(fd); + return 0; + } + + /* loop reading data */ + for(i = 0; i < iNP; i++) + { + if(fgets(pBueffel,255,fd) == NULL) + { + SCWrite(pCon,"WARNING: premature end of file",eError); + break; + } + sscanf(pBueffel," %f %f %f",&ud.fX[i],&fDummy, &ud.fY[i]); + ud.fY[i] *= dScale; + } + fclose(fd); + + /* enter ud into list */ + LLDnodeInsertFrom(self->iUserList,&ud); + + return 1; + } +/*----------------------------------------------------------------- + Collapse creates a 2D image from the detector by summing all time + channels together in any given detector. +*/ + + static int Collapse(pAmorStat self, SConnection *pCon) + { + HistInt *lData = NULL; + int i, i2, i3, iDim[MAXDIM], iIdx, iSum, status, length; + int *iImage = NULL, *iPtr; + pSINQHM pHist; + SinqHMDriv *pTata; + int iMax = -999999; + + /* get size of our problem */ + GetHistDim(self->pHM,iDim,&i3); + /* assert(i3 == 3); */ + + /* allocate some data */ + length = 2 + iDim[0]*iDim[1]; + iImage = (int *)malloc(length*sizeof(int)); + if(iImage == NULL) + { + SCWrite(pCon,"ERROR: failed to allocate memory in Collapse",eError); + return 0; + } + memset(iImage,0,(2 + iDim[0]*iDim[1])*sizeof(int)); + + /* first two numbers are the dimension of the image */ + iImage[0] = htonl(iDim[0]); + iImage[1] = htonl(iDim[1]); + + + if(isSINQHMDriv(self->pHM->pDriv)) + { + /* + send a Project request to the histogram memory + */ + pTata = (SinqHMDriv *)self->pHM->pDriv->pPriv; + pHist = (pSINQHM)pTata->pMaster; + /* + The 3 in the following call has to be identical to + PROJECT__COLL in sinqhm_def.h + */ + status = SINQHMProject(pHist, 3, 0, iDim[0], + 0, iDim[1], iImage+2, (length-2)*sizeof(int)); + /* + Byte swapping + */ + for(i = 2; i < length; i++) + { + /* + if(iImage[i] > iMax){ + iMax = iImage[i]; + } + */ + iImage[i] = htonl(iImage[i]); + } + /* + printf("Collapsed maximum: %d\n",iMax); + */ + if(status != 1) + { + SCWrite(pCon,"ERROR: histogram memory refused to Collapse",eError); + return 0; + } + } + else + { + /* + we are in simulation and just create some random numbers + */ + for(i = 0; i < iDim[0]; i++) + { + for(i2 = 0; i2 < iDim[1]; i2++) + { + iIdx = i*iDim[1] + i2; + iImage[iIdx+2] = htonl(random()); + /* iImage[iIdx+2] = htonl(77);*/ + } + } + } + + /* send image */ + SCWriteUUencoded(pCon,"arrow_image",iImage, + ((iDim[0]*iDim[1])+2)*sizeof(int)); + free(iImage); + return 1; + } +/*----------------------------------------------------------------- + SendSingleTOF sends single detector data for TOF mode +*/ + + static int SendSingleTOF(pAmorStat self, SConnection *pCon) + { + HistInt *lData = NULL; + int i, i2, i3, iDim[MAXDIM], iIdx, iSum, status, length, nTime; + pSINQHM pHist; + SinqHMDriv *pTata; + int iMax = -999999; + const float *timebin; + HistInt *iData = NULL; + int iStart; + + /* get size of our problem */ + GetHistDim(self->pHM,iDim,&i3); + + /* allocate some data */ + timebin = GetHistTimeBin(self->pHM, &nTime); + if(nTime < 2) { + return 1; + } + + length = 1 + 2*nTime; + iData = (HistInt *)malloc(length*sizeof(HistInt)); + if(iData == NULL){ + SCWrite(pCon,"ERROR: failed to allocate memory in SendSingleTOF", + eError); + return 0; + } + memset(iData,0,length*sizeof(int)); + + /* first number is the length of each single histogram */ + iData[0] = htonl(nTime); + + + if(isSINQHMDriv(self->pHM->pDriv)) + { + iStart = iDim[0]*iDim[1]*nTime; + GetHistogramDirect(self->pHM,pCon,0,iStart, + iStart + 2*nTime,&iData[1],2*nTime*sizeof(HistInt)); + for(i = 1; i < length; i++) + { + iData[i] = htonl(iData[i]); + } + } + else + { + /* + we are in simulation and just create some random numbers + */ + for(i = 1; i < length; i++) + { + iData[i] = htonl(random()); + } + } + + /* + send, with a little trick to do two histograms. + */ + SCWriteUUencoded(pCon,"SING1",iData, + (nTime+1)*sizeof(int)); + iData[nTime] = htonl(nTime); + SCWriteUUencoded(pCon,"SING2",&iData[nTime], + (nTime+1)*sizeof(int)); + free(iData); + return 1; + } +/*------------------------------------------------------------------- + SubSample sums histogram data in the area defined by the rectangle + x1,y1 x2, y2. Summing is along the time axis. +*/ + static int SubSample(pAmorStat self, SConnection *pCon, + char *name, int x1, int x2, int y1, int y2) + { + int iDim[MAXDIM], i, i2, i3, *iSum = NULL, iLang, *iPtr; + HistInt *lData = NULL; + int iLimit, status, nTime; + char pBueffel[132]; + pSINQHM pHist; + SinqHMDriv *pTata; + const float *fTime; + + /* get histogram dimensions */ + GetHistDim(self->pHM,iDim,&i3); + fTime = GetHistTimeBin(self->pHM,&nTime); + iDim[i3] = nTime; + i3++; + assert(i3 == 3); + + /* check limits */ + if(x2 < x1){ + i = x1; + x1 = x2; + x2 = i +1; + } + if(y2 < y1){ + i = y1; + y1 = y2; + y2 = i + 1; + } + + iLimit = 0; + if( x1 > iDim[0]) + { + iLimit = 1; + x1 = iDim[0] - 1; + } + if(x1 < 0) + { + iLimit = 1; + x1 = 0; + } + if( x2 > iDim[0]) + { + iLimit = 2; + x2 = iDim[0] - 1; + } + if(x2 < 0) + { + iLimit = 2; + x2 = 0; + } + if( y1 > iDim[1]) + { + iLimit = 3; + y1 = iDim[1] - 1; + } + if(y1 < 0) + { + iLimit = 3; + y1 = 0; + } + if( y2 > iDim[1]) + { + iLimit = 4; + y2 = iDim[1] - 1; + } + if(y2 < 0) + { + iLimit = 4; + y2 = 0; + } + if(iLimit != 0) + { + switch(iLimit) + { + case 1: + strcpy(pBueffel,"WARNING: limit violation on x1"); + break; + case 2: + strcpy(pBueffel,"WARNING: limit violation on x2"); + break; + case 3: + strcpy(pBueffel,"WARNING: limit violation on y1"); + break; + case 4: + strcpy(pBueffel,"WARNING: limit violation on y2"); + break; + } + SCWrite(pCon,pBueffel,eWarning); + } + + /* allocate space for result */ + iSum = (int *)malloc((iDim[2]+1)*sizeof(int)); + if(!iSum) + { + SCWrite(pCon,"ERROR: out of memory in SubSample",eError); + return 0; + } + memset(iSum,0,(iDim[2]+1)*sizeof(int)); + + iSum[0] = htonl(iDim[2]); + if(isSINQHMDriv(self->pHM->pDriv)) + { + /* + send project message to histogram memory + */ + pTata = (SinqHMDriv *)self->pHM->pDriv->pPriv; + pHist = (pSINQHM)pTata->pMaster; + status = SINQHMProject(pHist, 4, x1, x2-x1, + y1, y2-y1, iSum+1, iDim[2]*sizeof(int)); + /* + convert to network byte order + */ + for(i = 1; i < iDim[2]+1; i++) + { + iSum[i] = htonl(iSum[i]); + } + if(status != 1) + { + SCWrite(pCon,"ERROR: histogram memory refused to SubSample",eError); + return 0; + } + } + else + { + /* do acouple of random numbers! */ + for(i = 1; i < iDim[2]+1; i++) + { + iSum[i] = htonl(random()); + } + } + + /* send */ + sprintf(pBueffel,"arrowsum_%s",name); + SCWriteUUencoded(pCon,pBueffel,iSum,(iDim[2]+1)*sizeof(int)); + + free(iSum); + return 1; + } +/*------------------------------------------------------------------*/ + int AmorStatusAction(SConnection *pCon, SicsInterp *pSics, + void *pData, int argc, char *argv[]) + { + pAmorStat self = (pAmorStat)pData; + char pBueffel[512]; + double dScale; + int iRet; + int x1, x2, y1, y2; + + assert(self); + + if(argc < 2) + { + sprintf(pBueffel,"ERROR: need argument to %s",argv[0]); + SCWrite(pCon,pBueffel,eError); + return 0; + } + + strtolower(argv[1]); + if(strcmp(argv[1],"interest") == 0) + { + RegisterInterest(self,pCon); + SCSendOK(pCon); + return 1; + } + else if(strcmp(argv[1],"load") == 0) + { + if(argc < 4) + { + sprintf(pBueffel, + "ERROR: need filename and scale argument to %s load", + argv[0]); + SCWrite(pCon,pBueffel,eError); + return 0; + } + iRet = Tcl_GetDouble(pSics->pTcl,argv[3],&dScale); + if(iRet != TCL_OK) + { + sprintf(pBueffel,"ERROR: cannot convert %s to scale factor", + argv[3]); + SCWrite(pCon,pBueffel,eError); + return 0; + } + FileLoad(self,pCon,argv[2],dScale); + InvokeCallBack(self->pCall, FILELOADED,self); + SCSendOK(pCon); + } + else if(strcmp(argv[1],"collapse") == 0) + { + iRet = Collapse(self,pCon); + if(iRet) + { + SCSendOK(pCon); + } + return iRet; + } + else if(strcmp(argv[1],"sample") == 0) + { + if(argc < 7) + { + SCWrite(pCon,"ERROR: insufficent number of arguments to sample", + eError); + return 0; + } + iRet = Tcl_GetInt(pSics->pTcl,argv[3],&x1); + if(iRet != TCL_OK) + { + sprintf(pBueffel,"ERROR: cannot convert %s to int", argv[3]); + SCWrite(pCon,pBueffel,eError); + return 0; + } + iRet = Tcl_GetInt(pSics->pTcl,argv[6],&y2); + if(iRet != TCL_OK) + { + sprintf(pBueffel,"ERROR: cannot convert %s to int", argv[6]); + SCWrite(pCon,pBueffel,eError); + return 0; + } + iRet = Tcl_GetInt(pSics->pTcl,argv[4],&x2); + if(iRet != TCL_OK) + { + sprintf(pBueffel,"ERROR: cannot convert %s to int", argv[4]); + SCWrite(pCon,pBueffel,eError); + return 0; + } + iRet = Tcl_GetInt(pSics->pTcl,argv[5],&y1); + if(iRet != TCL_OK) + { + sprintf(pBueffel,"ERROR: cannot convert %s to int", argv[5]); + SCWrite(pCon,pBueffel,eError); + return 0; + } + iRet = SubSample(self,pCon,argv[2],x1,x2,y1,y2); + if(iRet) + SCSendOK(pCon); + return iRet; + } + else if(strcmp(argv[1],"singletof") == 0) + { + return SendSingleTOF(self,pCon); + } + else if(strcmp(argv[1],"sendloaded") == 0) + { + SendLoadedData(self,pCon); + return 1; + } + else if(strcmp(argv[1],"clear") == 0) + { + ClearUserData(self); + InvokeCallBack(self->pCall, FILELOADED,self); + SCSendOK(pCon); + } + else if(strcmp(argv[1],"tofmode") == 0) + { + HMCountStartCallback(COUNTSTART,NULL,pCon); + return 1; + } + else + { + sprintf(pBueffel,"ERROR: %s nor recognized as subcommand to %s", + argv[1], argv[2]); + SCWrite(pCon,pBueffel,eError); + return 0; + } + return 1; + } + + diff --git a/amorstat.h b/amorstat.h new file mode 100644 index 0000000..6665cec --- /dev/null +++ b/amorstat.h @@ -0,0 +1,21 @@ + +/*------------------------------------------------------------------------ + A M O R S T A T U S + + Public definitions for the AMOR status display + facilitator object. DO NOT CHANGE. This file is automatically + created from amorstat.w. + + Mark Koennecke, September 1999 +---------------------------------------------------------------------*/ +#ifndef AMORSTATUS +#define AMORSTATUS + + int AmorStatusFactory(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]); + int AmorStatusAction(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]); + void KillAmorStatus(void *pData); + +#endif + diff --git a/amorstat.i b/amorstat.i new file mode 100644 index 0000000..5a24a64 --- /dev/null +++ b/amorstat.i @@ -0,0 +1,29 @@ + +/*------------------------------------------------------------------------ + A M O R S T A T U S + + Internal data structure definitions for the AMOR status display + facilitator object. DO NOT CHANGE. This file is automatically + created from amorstat.w. + + Mark Koennecke, September 1999 +---------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------*/ + typedef struct { + float *fX, *fY; + int iNP; + char *name; + }UserData, *pUserData; +/*---------------------------------------------------------------------*/ + typedef struct __AMORSTAT { + pObjectDescriptor pDes; + pICallBack pCall; + int iUserList; + pScanData pScan; + pHistMem pHM; + int iTOF; + }AmorStat, *pAmorStat; + + + diff --git a/amorstat.tex b/amorstat.tex new file mode 100644 index 0000000..8d29533 --- /dev/null +++ b/amorstat.tex @@ -0,0 +1,138 @@ +\subsection{Amor Status Display Support} +The reflectometer AMOR has a few unique status display requirements: +\begin{itemize} +\item In scan mode up to four detector counts curves must be shown for +the two counters in spin-up or spin-down mode. This needs to be +updated after each scan point. +\item Additionally user defined curves may need to be displayed. +\item The usual helper information muste be displayed. +\item In TOF mode it must be possible to define a region on the +detector whose summed counts are displayed versus the time +binning. This must be sent on request. +\end{itemize} +In order to cover all this a special object within SICS is required +which deals with all this and packages information in a status display +compliant way. + +In order to do this the amorstatus object registers callbacks both +with the histogram memory and the scan object. These callback +functions are then responsible for updating the status displays. In +order for amorstatus to be able to do this, the client must register +itself with a special command. + +In order to achieve all this some data structures are needed: +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap1} +$\langle$asdata {\footnotesize ?}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@@\\ +\mbox{}\verb@/*---------------------------------------------------------------------*/@\\ +\mbox{}\verb@ typedef struct {@\\ +\mbox{}\verb@ float *fX, *fY;@\\ +\mbox{}\verb@ int iNP;@\\ +\mbox{}\verb@ char *name;@\\ +\mbox{}\verb@ }UserData, *pUserData; @\\ +\mbox{}\verb@/*---------------------------------------------------------------------*/@\\ +\mbox{}\verb@ typedef struct __AMORSTAT {@\\ +\mbox{}\verb@ pObjectDescriptor pDes;@\\ +\mbox{}\verb@ pICallBack pCall;@\\ +\mbox{}\verb@ int iUserList;@\\ +\mbox{}\verb@ pScanData pScan;@\\ +\mbox{}\verb@ pHistMem pHM;@\\ +\mbox{}\verb@ int iTOF;@\\ +\mbox{}\verb@ }AmorStat, *pAmorStat;@\\ +\mbox{}\verb@ @\\ +\mbox{}\verb@@$\diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro referenced in scrap ?. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +The fourth data structure is the amor status object data structure. It +has the following fields: +\begin{description} +\item[pDes] The standard SICS object descriptor. +\item[pCall] The callback interface. +\item[iUserList] A list of user data loaded data. +\item[pScan] A pointer to the scan object. +\item[pHM] A pointer to the histogram memory. +\item[iTOF] A flag which is true if we are taking measurements in TOF +mode. +\end{description} + +In terms of a function interface this object has not much to +offer. Its main purpose is really as an interface to the status +display clients and thus it is configured through the interpreter +interface function. No need for other SICS objects to access it. + +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap2} +$\langle$asinter {\footnotesize ?}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@@\\ +\mbox{}\verb@ int AmorStatusFactory(SConnection *pCon, SicsInterp *pSics, void *pData,@\\ +\mbox{}\verb@ int argc, char *argv[]);@\\ +\mbox{}\verb@ int AmorStatusAction(SConnection *pCon, SicsInterp *pSics, void *pData,@\\ +\mbox{}\verb@ int argc, char *argv[]);@\\ +\mbox{}\verb@ void KillAmorStatus(void *pData);@\\ +\mbox{}\verb@@$\diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro referenced in scrap ?. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap3} +\verb@"amorstat.i"@ {\footnotesize ? }$\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@@\\ +\mbox{}\verb@/*------------------------------------------------------------------------@\\ +\mbox{}\verb@ A M O R S T A T U S@\\ +\mbox{}\verb@@\\ +\mbox{}\verb@ Internal data structure definitions for the AMOR status display @\\ +\mbox{}\verb@ facilitator object. DO NOT CHANGE. This file is automatically@\\ +\mbox{}\verb@ created from amorstat.w.@\\ +\mbox{}\verb@@\\ +\mbox{}\verb@ Mark Koennecke, September 1999@\\ +\mbox{}\verb@---------------------------------------------------------------------*/@\\ +\mbox{}\verb@@$\langle$asdata {\footnotesize ?}$\rangle$\verb@@\\ +\mbox{}\verb@ @\\ +\mbox{}\verb@@$\diamond$ +\end{list} +\vspace{-2ex} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap4} +\verb@"amorstat.h"@ {\footnotesize ? }$\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@@\\ +\mbox{}\verb@/*------------------------------------------------------------------------@\\ +\mbox{}\verb@ A M O R S T A T U S@\\ +\mbox{}\verb@@\\ +\mbox{}\verb@ Public definitions for the AMOR status display @\\ +\mbox{}\verb@ facilitator object. DO NOT CHANGE. This file is automatically@\\ +\mbox{}\verb@ created from amorstat.w.@\\ +\mbox{}\verb@@\\ +\mbox{}\verb@ Mark Koennecke, September 1999@\\ +\mbox{}\verb@---------------------------------------------------------------------*/@\\ +\mbox{}\verb@#ifndef AMORSTATUS@\\ +\mbox{}\verb@#define AMORSTATUS@\\ +\mbox{}\verb@@$\langle$asinter {\footnotesize ?}$\rangle$\verb@@\\ +\mbox{}\verb@#endif@\\ +\mbox{}\verb@@\\ +\mbox{}\verb@@$\diamond$ +\end{list} +\vspace{-2ex} +\end{minipage}\\[4ex] +\end{flushleft} diff --git a/amorstat.w b/amorstat.w new file mode 100644 index 0000000..a8653d7 --- /dev/null +++ b/amorstat.w @@ -0,0 +1,102 @@ +\subsection{Amor Status Display Support} +The reflectometer AMOR has a few unique status display requirements: +\begin{itemize} +\item In scan mode up to four detector counts curves must be shown for +the two counters in spin-up or spin-down mode. This needs to be +updated after each scan point. +\item Additionally user defined curves may need to be displayed. +\item The usual helper information muste be displayed. +\item In TOF mode it must be possible to define a region on the +detector whose summed counts are displayed versus the time +binning. This must be sent on request. +\end{itemize} +In order to cover all this a special object within SICS is required +which deals with all this and packages information in a status display +compliant way. + +In order to do this the amorstatus object registers callbacks both +with the histogram memory and the scan object. These callback +functions are then responsible for updating the status displays. In +order for amorstatus to be able to do this, the client must register +itself with a special command. + +In order to achieve all this some data structures are needed: +@d asdata @{ +/*---------------------------------------------------------------------*/ + typedef struct { + float *fX, *fY; + int iNP; + char *name; + }UserData, *pUserData; +/*---------------------------------------------------------------------*/ + typedef struct __AMORSTAT { + pObjectDescriptor pDes; + pICallBack pCall; + int iUserList; + pScanData pScan; + pHistMem pHM; + int iTOF; + }AmorStat, *pAmorStat; + +@} + + +The fourth data structure is the amor status object data structure. It +has the following fields: +\begin{description} +\item[pDes] The standard SICS object descriptor. +\item[pCall] The callback interface. +\item[iUserList] A list of user data loaded data. +\item[pScan] A pointer to the scan object. +\item[pHM] A pointer to the histogram memory. +\item[iTOF] A flag which is true if we are taking measurements in TOF +mode. +\end{description} + +In terms of a function interface this object has not much to +offer. Its main purpose is really as an interface to the status +display clients and thus it is configured through the interpreter +interface function. No need for other SICS objects to access it. + +@d asinter @{ + int AmorStatusFactory(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]); + int AmorStatusAction(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]); + void KillAmorStatus(void *pData); +@} + + +@o amorstat.i @{ +/*------------------------------------------------------------------------ + A M O R S T A T U S + + Internal data structure definitions for the AMOR status display + facilitator object. DO NOT CHANGE. This file is automatically + created from amorstat.w. + + Mark Koennecke, September 1999 +---------------------------------------------------------------------*/ +@ + +@} + +@o amorstat.h @{ +/*------------------------------------------------------------------------ + A M O R S T A T U S + + Public definitions for the AMOR status display + facilitator object. DO NOT CHANGE. This file is automatically + created from amorstat.w. + + Mark Koennecke, September 1999 +---------------------------------------------------------------------*/ +#ifndef AMORSTATUS +#define AMORSTATUS +@ +#endif + +@} + + + diff --git a/bruker.c b/bruker.c new file mode 100644 index 0000000..f240770 --- /dev/null +++ b/bruker.c @@ -0,0 +1,999 @@ +/*------------------------------------------------------------------------- + B r u k e r + + An environment control driver and an additonal wrapper function for + controlling a Bruker B-EC-1 magnet controller. This controller can + either control a current or control the current through an external hall + sensor mesuring the magnetic field. In both cases both values: the field + and the current must be readable. + + copyright: see copyright.h + + Mark Koennecke, October 1998 +---------------------------------------------------------------------------*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "hardsup/serialsinq.h" +#include "hardsup/el734_errcodes.h" +#include "hardsup/el734fix.h" +#include "bruker.h" + +/* +#define debug 1 +*/ +/*----------------------------------------------------------------------- + The Bruker Data Structure +*/ + typedef struct { + void *pData; + char *pHost; + int iPort; + int iChannel; + int iMode; + int iLastError; + } BrukerDriv, *pBrukerDriv; +/*----------------------------------------------------------------------- + A couple of defines for Bruker modes and special error conditions +*/ +#define FIELD 100 +#define CURRENT 200 + +/* errors */ +#define NOFUNC -1601 +#define BADARG -1602 +#define NOACCESS -1603 +#define BADRANGE -1604 +#define ERRPENDING -1605 +#define NOPOWER -1606 +#define NOTFIELD -1607 +#define BADINTERN -1608 +#define NOCONN -1609 +#define BTIMEOUT -1610 +#define NOPOLUNIT -1620 + +/* polarity */ +#define PPLUS 0 +#define PMINUS 1 +#define PBUSY 3 + +/* rmtrail.c */ +extern char *rmtrail(char *p); + +/*--------------------------------------------------------------------------- + This Bruker thing has a lot of internal error conditions and a few nasty + habits. Such as to lock up after an error ocurred until the error is reset. + Or to switch the power off, when a current above the limit is requested + after setting a bad value for the magnetic field. These problems can be + detected by analysing the return values from the Bruker. Usually the Bruker + returns the command given to the user plus additional values if requested. + On an error a string of the type E0n is appended to the command echo with + n being a small integer. In order to handle this all commands to the Bruker + are processed through this special function which takes care of the error + handling. +*/ + static int BrukerCommand(pBrukerDriv self, char *pCommand, + char *pReplyBuffer, int iReplyLen) + { + int iTest, iCode; + char *pPtr; + + assert(self); + assert(iReplyLen > 20); /* so small a buffer will hide errors */ + + if(self->pData == NULL) + { + self->iLastError = NOCONN; + return 0; + } + + /* send the command to the Bruker */ + rmtrail(pCommand); + iTest = SerialWriteRead(&(self->pData), pCommand,pReplyBuffer, iReplyLen); +#ifdef debug + printf("Comm: %s , Reply %s\n",pCommand,pReplyBuffer); +#endif + if(iTest != 1) /* communication error */ + { + self->iLastError = iTest; + return 0; + } + + /* identify timeout */ + if(strstr(pReplyBuffer,"?TMO") != NULL) + { + self->iLastError = BTIMEOUT; + return 0; + } + + /* try to find a E0 response indicating a Bruker error */ + if( (pPtr = strstr(pReplyBuffer,"E0")) == NULL) + { + return 1; + } + + /* decode the error */ + sscanf(pPtr+1,"%x",&iCode); + switch(iCode) + { + case 1: + self->iLastError = NOFUNC; + break; + case 2: + self->iLastError = BADARG; + break; + case 4: + self->iLastError = NOACCESS; + break; + case 5: + self->iLastError = BADRANGE; + break; + case 7: + self->iLastError = ERRPENDING; + break; + case 9: + self->iLastError = NOPOWER; + break; + case 10: + self->iLastError = NOTFIELD; + break; + default: + self->iLastError = BADINTERN; + break; + } + return 0; + } +/*-------------------------------------------------------------------------*/ + int BrukerReadField(pEVControl pEva, float *fField) + { + pBrukerDriv self = NULL; + int iRet; + char pBueffel[80]; + char pCommand[6]; + char *pPtr,*pSign; + int iSign = 1; + float fVal; + + self = (pBrukerDriv)pEva->pDriv->pPrivate; + assert(self); + + if(self->pData == NULL) + { + self->iLastError = NOCONN; + return 0; + } + + strcpy(pCommand,"FIE/"); + iRet = BrukerCommand(self,pCommand,pBueffel,79); + if(!iRet) + { + *fField = -99; + return 0; + } + + pPtr = pBueffel+4; /* skip over echo */ + /* deal with obstructing sign */ + if( (pSign = strchr(pPtr,'+')) != NULL) + { + *pSign = ' '; + iSign = 1; + } + if( (pSign = strchr(pPtr,'-')) != NULL) + { + *pSign = ' '; + iSign = -1; + } + sscanf(pPtr,"%f",&fVal); + *fField = iSign * fVal; + return 1; + } +/*-------------------------------------------------------------------------*/ + int BrukerReadCurrent(pEVControl pEva, float *fField) + { + pBrukerDriv self = NULL; + int iRet, iSign = 1; + char pBueffel[80]; + char pCommand[6]; + char *pPtr, *pSign = NULL; + float fVal; + + self = (pBrukerDriv)pEva->pDriv->pPrivate; + assert(self); + + if(self->pData == NULL) + { + self->iLastError = NOCONN; + return 0; + } + + strcpy(pCommand,"CHN/"); + iRet = BrukerCommand(self,pCommand,pBueffel,79); + if(!iRet) + { + *fField = -99; + return 0; + } + + pPtr = pBueffel+4; /* skip over echo */ + /* deal with obstructing sign */ + if( (pSign = strchr(pPtr,'+')) != NULL) + { + *pSign = ' '; + iSign = 1; + } + if( (pSign = strchr(pPtr,'-')) != NULL) + { + *pSign = ' '; + iSign = -1; + } + sscanf(pPtr,"%f",&fVal); + *fField = iSign * fVal; + return 1; + } +/*-------------------------------------------------------------------------*/ + static int BrukerGet(pEVDriver pEva, float *fValue) + { + pBrukerDriv self = NULL; + int iRet, iSign = 1; + char pBueffel[80]; + char pCommand[6]; + char *pPtr, *pSign = NULL; + float fVal; + + self = (pBrukerDriv)pEva->pPrivate; + assert(self); + + if(self->pData == NULL) + { + self->iLastError = NOCONN; + return 0; + } + + if(self->iMode == FIELD) + { + strcpy(pCommand,"CUF/"); + iRet = BrukerCommand(self,pCommand,pBueffel,79); + } + else if(self->iMode == CURRENT) + { + strcpy(pCommand,"CUR/"); + iRet = BrukerCommand(self,pCommand,pBueffel,79); + } + else + { + /* programming error */ + assert(1); + } + + if(!iRet) + { + *fValue = -99; + return 0; + } + + pPtr = pBueffel+4; /* skip over echo */ + /* deal with obstructing sign */ + if( (pSign = strchr(pPtr,'+')) != NULL) + { + *pSign = ' '; + iSign = 1; + } + if( (pSign = strchr(pPtr,'-')) != NULL) + { + *pSign = ' '; + iSign = -1; + } + sscanf(pPtr,"%f",&fVal); + *fValue = iSign * fVal; + return 1; + } +/*-------------------------------------------------------------------------*/ + static int BrukerRun(pEVDriver pEva, float fVal) + { + pBrukerDriv self = NULL; + int iRet; + char pBueffel[80]; + char pCommand[40]; + char *pPtr; + + self = (pBrukerDriv)pEva->pPrivate; + assert(self); + + if(self->pData == NULL) + { + self->iLastError = NOCONN; + return 0; + } + + if(self->iMode == FIELD) + { + sprintf(pCommand,"PTF=%-6.2f",fVal); + iRet = BrukerCommand(self,pCommand,pBueffel,79); + } + else if(self->iMode == CURRENT) + { + sprintf(pCommand,"PNT=%-6.2f",fVal); + iRet = BrukerCommand(self,pCommand,pBueffel,79); + } + else + { + /* programming error */ + assert(1); + } + + if(!iRet) + { + return 0; + } + return 1; + } +/*------------------------------------------------------------------------*/ + static int BrukerError(pEVDriver pEva, int *iCode, char *pError, + int iErrLen) + { + pBrukerDriv self = NULL; + + self = (pBrukerDriv)pEva->pPrivate; + assert(self); + + *iCode = self->iLastError; + switch(*iCode) + { + case NOFUNC: + strncpy(pError, + "Function not supported", + iErrLen); + break; + case BADINTERN: + case BADARG: + strncpy(pError, + "Programming problem, reset Controller & contact Programmer", + iErrLen); + break; + case NOTFIELD: + strncpy(pError,"Bruker not switched to field mode",iErrLen); + break; + case BADRANGE: + strncpy(pError,"Requested value out of range",iErrLen); + break; + case NOACCESS: + strncpy(pError,"No Access, check key position at Controller", + iErrLen); + break; + case ERRPENDING: + strncpy(pError,"Error condition pending in Bruker Controller", + iErrLen); + break; + case NOPOWER: + strncpy(pError, + "Power OFF as consequence of some error in Bruker Controller", + iErrLen); + break; + case NOCONN: + strncpy(pError,"No Connection to Bruker Controller",iErrLen); + break; + case BTIMEOUT: + strncpy(pError,"Timeout at serial port",iErrLen); + break; + case NOPOLUNIT: + strncpy(pError,"No polarity switching unit, try setting negative current", + iErrLen); + break; + default: + SerialError(*iCode,pError,iErrLen); + break; + } + return 1; + } +/*---------------------------------------------------------------------------*/ + static int BrukerSend(pEVDriver pEva, char *pCommand, char *pReply, + int iReplyLen) + { + pBrukerDriv self = NULL; + int iRet; + + self = (pBrukerDriv)pEva->pPrivate; + assert(self); + + if(self->pData == NULL) + { + self->iLastError = NOCONN; + return 0; + } + + + iRet = SerialWriteRead(&(self->pData),pCommand, pReply, iReplyLen); + if(iRet != 1) + { + self->iLastError = iRet; + return 0; + } + return 1; + } +/*--------------------------------------------------------------------------*/ + static int BrukerInit(pEVDriver pEva) + { + pBrukerDriv self = NULL; + int iRet; + char pBueffel[80], pCommand[20]; + + self = (pBrukerDriv)pEva->pPrivate; + assert(self); + + /* open port connection */ + self->pData = NULL; + iRet = SerialOpen(&(self->pData),self->pHost, self->iPort, self->iChannel); + if(iRet != 1) + { + self->iLastError = iRet; + return 0; + } + /* configure serial port terminators */ + SerialSendTerm(&(self->pData),"\r"); + SerialATerm(&(self->pData),"1\r\n"); + + /* set power on */ + strcpy(pCommand,"DCP=1"); + iRet = SerialWriteRead(&(self->pData),pCommand,pBueffel,80); + if(iRet != 1) + { + self->iLastError = iRet; + return 0; + } + + /* switch to current mode as default init mode */ + self->iMode = CURRENT; + strcpy(pCommand,"EXT=0"); + iRet = SerialWriteRead(&(self->pData),pCommand,pBueffel,80); + if(iRet != 1) + { + self->iLastError = iRet; + return 0; + } + return 1; + } +/*-------------------------------------------------------------------------*/ + static int BrukerClose(pEVDriver pEva) + { + pBrukerDriv self = NULL; + + self = (pBrukerDriv)pEva->pPrivate; + assert(self); + + SerialClose(&(self->pData)); + self->pData = 0; + + return 1; + } +/*---------------------------------------------------------------------------*/ + static int BrukerFix(pEVDriver self, int iError) + { + pBrukerDriv pMe = NULL; + int iRet; + char pCommand[20], pBueffel[80]; + + assert(self); + pMe = (pBrukerDriv )self->pPrivate; + assert(pMe); + + switch(iError) + { + /* network errors */ + case EL734__BAD_FLUSH: + case EL734__BAD_RECV: + case EL734__BAD_RECV_NET: + case EL734__BAD_RECV_UNKN: + case EL734__BAD_RECVLEN: + case EL734__BAD_RECV1: + case EL734__BAD_RECV1_PIPE: + case EL734__BAD_RNG: + case EL734__BAD_SEND: + case EL734__BAD_SEND_PIPE: + case EL734__BAD_SEND_NET: + case EL734__BAD_SEND_UNKN: + case EL734__BAD_SENDLEN: + BrukerClose(self); + iRet = BrukerInit(self); + if(iRet) + { + return DEVREDO; + } + else + { + return DEVFAULT; + } + break; + case EL734__FORCED_CLOSED: + case NOCONN: + iRet = BrukerInit(self); + if(iRet) + { + return DEVREDO; + } + else + { + return DEVFAULT; + } + break; + /* fixable Bruker Errors */ + case ERRPENDING: + strcpy(pCommand,"RST=0"); + iRet = BrukerCommand(pMe,pCommand, pBueffel,79); + if(iRet) + { + return DEVREDO; + } + else + { + return DEVFAULT; + } + break; + case NOPOWER: + strcpy(pCommand,"RST=0"); + iRet = BrukerCommand(pMe,pCommand, pBueffel,79); + strcpy(pCommand,"DCP=1"); + iRet = BrukerCommand(pMe,pCommand, pBueffel,79); + if(iRet) + { + return DEVREDO; + } + else + { + return DEVFAULT; + } + break; + case NOTFIELD: + strcpy(pCommand,"EXT=2"); + iRet = BrukerCommand(pMe,pCommand, pBueffel,79); + if(iRet) + { + return DEVREDO; + } + else + { + return DEVFAULT; + } + break; + /* handable protocoll errors */ + case EL734__BAD_TMO: + case BTIMEOUT: + case NOFUNC: + return DEVREDO; + break; + default: + return DEVFAULT; + break; + } + return DEVFAULT; + } +/*------------------------------------------------------------------------*/ + void KillBruker(void *pData) + { + pBrukerDriv pMe = NULL; + + pMe = (pBrukerDriv)pData; + assert(pMe); + + if(pMe->pHost) + { + free(pMe->pHost); + } + free(pMe); + } +/*------------------------------------------------------------------------*/ + pEVDriver CreateBrukerDriver(int argc, char *argv[]) + { + pEVDriver pNew = NULL; + pBrukerDriv pSim = NULL; + + /* check for arguments */ + if(argc < 3) + { + return NULL; + } + + pNew = CreateEVDriver(argc,argv); + pSim = (pBrukerDriv)malloc(sizeof(BrukerDriv)); + memset(pSim,0,sizeof(BrukerDriv)); + if(!pNew || !pSim) + { + return NULL; + } + pNew->pPrivate = pSim; + pNew->KillPrivate = KillBruker; + + /* initalise pBrukerDriver */ + pSim->iLastError = 0; + pSim->pHost = strdup(argv[0]); + pSim->iPort = atoi(argv[1]); + pSim->iChannel = atoi(argv[2]); + + + /* initialise function pointers */ + pNew->SetValue = BrukerRun; + pNew->GetValue = BrukerGet; + pNew->Send = BrukerSend; + pNew->GetError = BrukerError; + pNew->TryFixIt = BrukerFix; + pNew->Init = BrukerInit; + pNew->Close = BrukerClose; + + return pNew; + } +/*-------------------------------------------------------------------------*/ + int BrukerSetMode(pEVControl pEva, SConnection *pCon, int iMode) + { + pBrukerDriv self = NULL; + int iRet; + char pBueffel[80]; + char pCommand[6]; + char *pPtr; + + self = (pBrukerDriv)pEva->pDriv->pPrivate; + assert(self); + + if(self->pData == NULL) + { + self->iLastError = NOCONN; + return 0; + } + + if(iMode == CURRENT) + { + strcpy(pCommand,"EXT=0"); + } + else if(iMode == FIELD) + { + strcpy(pCommand,"EXT=2"); + } + else + { + SCWrite(pCon,"ERROR: Internal: invalid mode for Bruker given",eError); + return 0; + } + iRet = BrukerCommand(self,pCommand,pBueffel,79); + if(!iRet) + { + strcpy(pBueffel,"ERROR:"); + BrukerError(pEva->pDriv,&iRet,(pBueffel+7),70); + SCWrite(pCon,pBueffel,eError); + return 0; + } + self->iMode = iMode; + return 1; + } +/*-------------------------------------------------------------------------*/ + int BrukerGetPolarity(pEVControl pEva, SConnection *pCon, int *iMode) + { + pBrukerDriv self = NULL; + int iRet; + char pBueffel[80]; + char pCommand[6]; + char *pPtr; + + self = (pBrukerDriv)pEva->pDriv->pPrivate; + assert(self); + + if(self->pData == NULL) + { + self->iLastError = NOCONN; + return 0; + } + strcpy(pCommand,"POL/"); + iRet = BrukerCommand(self,pCommand,pBueffel,79); + if(!iRet) + { + strcpy(pBueffel,"ERROR:"); + BrukerError(pEva->pDriv,&iRet,(pBueffel+7),70); + SCWrite(pCon,pBueffel,eError); + return 0; + } + pPtr = pBueffel+4; + sscanf(pPtr,"%d",iMode); + return 1; + } +/*------------------------------------------------------------------------*/ + int BrukerSetPolarity(pEVControl pEva, SConnection *pCon, int iMode) + { + pBrukerDriv self = NULL; + int iRet; + char pBueffel[80]; + char pCommand[6]; + char *pPtr; + + self = (pBrukerDriv)pEva->pDriv->pPrivate; + assert(self); + + if(self->pData == NULL) + { + self->iLastError = NOCONN; + return 0; + } + + if(iMode == PPLUS) + { + strcpy(pCommand,"POL=0"); + } + else if(iMode == PMINUS) + { + strcpy(pCommand,"POL=1"); + } + else + { + assert(1); /* programming error */ + } + + iRet = BrukerCommand(self,pCommand,pBueffel,79); + if( (strstr(pBueffel,"POL=0E01") != NULL) || + (strstr(pBueffel,"POL=1E01") != NULL) ) + { + self->iLastError = NOPOLUNIT; + iRet = 0; + } + if(!iRet) + { + strcpy(pBueffel,"ERROR:"); + BrukerError(pEva->pDriv,&iRet,(pBueffel+6),70); + SCWrite(pCon,pBueffel,eError); + return 0; + } + return 1; + } +/*-------------------------------------------------------------------------- + handle Bruker specific commands: + - polarity for switching polarity + - field for reading field + - current for reading current + - mode for setting and retrieving the current control mode + - list append our own stuff to the rest + in all other cases fall back and call EVControllerWrapper to handle it or + eventually throw an error. +*/ + int BrukerAction(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]) + { + + pEVControl self = NULL; + int iRet, iMode; + char pBueffel[256]; + pBrukerDriv pMe = NULL; + float fVal; + + self = (pEVControl)pData; + assert(self); + pMe = (pBrukerDriv)self->pDriv->pPrivate; + assert(pMe); + + if(argc > 1) + { + strtolower(argv[1]); +/*------ polarity */ + if(strcmp(argv[1],"polarity") == 0) + { + if(argc > 2) /* set case */ + { + strtolower(argv[2]); + if(strcmp(argv[2],"plus") == 0) + { + iMode = PPLUS; + } + else if(strcmp(argv[2],"minus") == 0) + { + iMode = PMINUS; + } + else + { + sprintf(pBueffel,"ERROR: %s is no knwon polarity mode", argv[2]); + SCWrite(pCon,pBueffel,eError); + return 0; + } + /* check permission */ + if(!SCMatchRights(pCon,usUser)) + { + return 0; + } + /* do it */ + iRet = BrukerSetPolarity(self,pCon,iMode); + if(iRet) + { + SCSendOK(pCon); + return 1; + } + else + { + return 0; + } + } + else /* get case */ + { + iRet = BrukerGetPolarity(self,pCon,&iMode); + if(iRet) + { + if(iMode == PPLUS) + { + sprintf(pBueffel,"%s.polarity = plus",argv[0]); + } + else if (iMode == PMINUS) + { + sprintf(pBueffel,"%s.polarity = minus",argv[0]); + } + else + { + assert(1); /* programming problem */ + } + SCWrite(pCon,pBueffel,eValue); + return 1; + } + } + } +/*-------- control mode */ + else if(strcmp(argv[1],"mode") == 0) + { + if(argc > 2) /* set case */ + { + strtolower(argv[2]); + if(strcmp(argv[2],"field") == 0) + { + iMode = FIELD; + } + else if(strcmp(argv[2],"current") == 0) + { + iMode = CURRENT; + } + else + { + sprintf(pBueffel,"ERROR: %s is no known control mode", argv[2]); + SCWrite(pCon,pBueffel,eError); + return 0; + } + /* check permission */ + if(!SCMatchRights(pCon,usUser)) + { + return 0; + } + /* do it */ + iRet = BrukerSetMode(self,pCon,iMode); + if(iRet) + { + SCSendOK(pCon); + return 1; + } + else + { + return 0; + } + } + else /* get case */ + { + if(pMe->iMode == FIELD) + { + sprintf(pBueffel,"%s.mode = field",argv[0]); + } + else if (pMe->iMode == CURRENT) + { + sprintf(pBueffel,"%s.mode = current",argv[0]); + } + else + { + assert(1); /* programming problem */ + } + SCWrite(pCon,pBueffel,eValue); + return 1; + } + } +/*-----------field */ + else if(strcmp(argv[1],"field") == 0) + { + iRet = BrukerReadField(self,&fVal); + if(!iRet) + { + strcpy(pBueffel,"ERROR: "); + self->pDriv->GetError(self->pDriv,&iMode,pBueffel+7,240); + SCWrite(pCon,pBueffel,eError); + return 0; + } + sprintf(pBueffel,"%s.field = %f Tesla",argv[0],fVal); + SCWrite(pCon,pBueffel,eValue); + return 1; + } +/*----------- current */ + else if(strcmp(argv[1],"current") == 0) + { + iRet = BrukerReadCurrent(self,&fVal); + if(!iRet) + { + strcpy(pBueffel,"ERROR: "); + self->pDriv->GetError(self->pDriv,&iMode,pBueffel+7,240); + SCWrite(pCon,pBueffel,eError); + return 0; + } + sprintf(pBueffel,"%s.current = %f A",argv[0],fVal); + SCWrite(pCon,pBueffel,eValue); + return 1; + } +/*--------- list */ + else if(strcmp(argv[1],"list") == 0) + { + /* print generals first */ + EVControlWrapper(pCon,pSics,pData,argc,argv); + /* print our add on stuff */ + iRet = BrukerReadCurrent(self,&fVal); + if(!iRet) + { + strcpy(pBueffel,"ERROR: "); + self->pDriv->GetError(self->pDriv,&iMode,pBueffel+7,240); + SCWrite(pCon,pBueffel,eError); + } + else + { + sprintf(pBueffel,"%s.current = %f A",argv[0],fVal); + SCWrite(pCon,pBueffel,eValue); + } + iRet = BrukerReadField(self,&fVal); + if(!iRet) + { + strcpy(pBueffel,"ERROR: "); + self->pDriv->GetError(self->pDriv,&iMode,pBueffel+7,240); + SCWrite(pCon,pBueffel,eError); + } + else + { + sprintf(pBueffel,"%s.field = %f Tesla",argv[0],fVal); + SCWrite(pCon,pBueffel,eValue); + } + if(pMe->iMode == FIELD) + { + sprintf(pBueffel,"%s.mode = field",argv[0]); + } + else if (pMe->iMode == CURRENT) + { + sprintf(pBueffel,"%s.mode = current",argv[0]); + } + else + { + sprintf(pBueffel,"ERROR: Programming error"); + } + SCWrite(pCon,pBueffel,eValue); + iRet = BrukerGetPolarity(self,pCon,&iMode); + if(iRet) + { + if(iMode == PPLUS) + { + sprintf(pBueffel,"%s.polarity = plus",argv[0]); + } + else if (iMode == PMINUS) + { + sprintf(pBueffel,"%s.polarity = minus",argv[0]); + } + else if(iMode == PBUSY) + { + sprintf(pBueffel,"%s.polarity = busy",argv[0]); + } + else + { + sprintf(pBueffel,"ERROR: Programming problem"); + } + SCWrite(pCon,pBueffel,eValue); + } + else + { + SCWrite(pCon,"ERROR: cannot read polarity",eError); + } + return 1; + } + else + { + return EVControlWrapper(pCon,pSics,pData,argc,argv); + } + } + return EVControlWrapper(pCon,pSics,pData,argc,argv); + } diff --git a/bruker.h b/bruker.h new file mode 100644 index 0000000..bfa26a2 --- /dev/null +++ b/bruker.h @@ -0,0 +1,25 @@ +/*------------------------------------------------------------------------- + B r u k e r + + An environment control driver and an additonal wrapper function for + controlling a Bruker B-EC-1 magnet controller. This controller can + either control a current or control the current through an external hall + sensor mesuring the magnetic field. In both cases both values: the field + and the current must be readable. + + copyright: see copyright.h + + Mark Koennecke, October 1998 +---------------------------------------------------------------------------*/ +#ifndef BRUKERMAGNET +#define BRUKERMAGNET + + pEVDriver CreateBrukerDriver(int argc, char *argv[]); + + int BrukerReadField(pEVControl self, float *fField); + int BrukerReadCurrent(pEVControl self, float *fCurrent); + + int BrukerAction(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]); + +#endif diff --git a/bruker.w b/bruker.w new file mode 100644 index 0000000..2a96bcb --- /dev/null +++ b/bruker.w @@ -0,0 +1,64 @@ +\subsubsection{Bruker Magnet Controller B-EC-1} +SANS is using a Bruker magnet controller. This controller is integrated +into SICS as a derivate of an environment controller. The Bruker controller +can be operated in two modes: in the first the current is controlled, +in the second the current +is controlled by an external hall sensor giving the magnetic field. Whatever +is the controlling sensor, the magnetic field and the current need to be +read. Furthermore this device supports switching the polarity. All this is +achieved with a special driver and an additional wrapper function for +handling extra commands. All this is implemented in the file bruker.h +and bruker.c. The functions defined are: + +\begin{verbatim} + pEVDriver CreateBrukerDriver(int argc, char *argv[]); + + int BrukerReadField(pEVControl self, float *fField); + int BrukerReadCurrent(pEVControl self, float *fCurrent); + + int BrukerAction(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]); + +\end{verbatim} + +\begin{description} +\item[CreateBrukerDriver] creates a driver for the bruker magnet +controller. +\item[BrukerReadField] reads the current magnetic field. +\item[BrukerReadCurrent] reads the current current in Ampere. +\item[BrukerAction] a special SICS interpreter wrapper function for +the Bruker Magnet. This function handles the few special Bruker +commands and passes everything else to the standard environment +controller handler function. +\end{description} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/buffer.c b/buffer.c new file mode 100644 index 0000000..d785eea --- /dev/null +++ b/buffer.c @@ -0,0 +1,584 @@ +/*-------------------------------------------------------------------------- + L N S R \"U N B U F F E R + + + Mark Koennecke, January 1997 + + Copyright: + + Labor fuer Neutronenstreuung + Paul Scherrer Institut + CH-5423 Villigen-PSI + + + The authors hereby grant permission to use, copy, modify, distribute, + and license this software and its documentation for any purpose, provided + that existing copyright notices are retained in all copies and that this + notice is included verbatim in any distributions. No written agreement, + license, or royalty fee is required for any of the authorized uses. + Modifications to this software may be copyrighted by their authors + and need not follow the licensing terms described here, provided that + the new terms are clearly indicated on the first page of each file where + they apply. + + IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY + FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY + DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE + IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE + NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR + MODIFICATIONS. +----------------------------------------------------------------------------*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "buffer.h" +#include +#include +#include "ruli.h" +/*-------------------------------------------------------------------------*/ + static int SaveBuffer(void *pData, char *name, FILE *fd) + { + pRuenBuffer self = NULL; + int iRet; + char *pPtr = NULL; + + assert(fd); + assert(pData); + + self = (pRuenBuffer)pData; + fprintf(fd,"# RuenBuffer %s\n",name); + fprintf(fd,"Buf new %s\n",name); + iRet = LLDnodePtr2First(self->iLineList); + while(iRet != 0) + { + pPtr = (char *)LLDnodePtr(self->iLineList); + fprintf(fd,"%s append %s\n",name,pPtr); + iRet = LLDnodePtr2Next(self->iLineList); + } + return 1; + } + +/*--------------------------------------------------------------------------*/ + pRuenBuffer CreateRuenBuffer(char *name) + { + pRuenBuffer pNew = NULL; + + pNew = (pRuenBuffer)malloc(sizeof(RuenBuffer)); + if(!pNew) + { + return NULL; + } + pNew->pDes = CreateDescriptor("SicsRuenBuffer"); + if(!pNew->pDes) + { + free(pNew); + return NULL; + } + + pNew->name = strdup(name); + Fortify_CheckAllMemory(); + pNew->iLineList = LLDblobCreate(); + if(pNew->iLineList == -1) + { + DeleteDescriptor(pNew->pDes); + free(pNew->name); + free(pNew); + return NULL; + } + pNew->pDes->SaveStatus = SaveBuffer; + return pNew; + } +/*--------------------------------------------------------------------------*/ + static void DeleteLineBuffer(int iList) + { + int iRet; + char *pPtr; + + iRet = LLDnodePtr2First(iList); + while(iRet != 0) + { + pPtr = (char *)LLDnodePtr(iList); + free(pPtr); + iRet = LLDnodePtr2Next(iList); + } + LLDdelete(iList); + } +/*-------------------------------------------------------------------------*/ + void DeleteRuenBuffer(void *self) + { + int iRet; + pRuenBuffer pOld = (pRuenBuffer)self; + + assert(pOld); + /* delete line buffer */ + DeleteLineBuffer(pOld->iLineList); + if(pOld->name) + { + free(pOld->name); + } + if(pOld->pDes) + { + DeleteDescriptor(pOld->pDes); + } + free(pOld); + } +/*--------------------------------------------------------------------------*/ + pRuenBuffer CopyRuenBuffer(pRuenBuffer pOld, char *name) + { + pRuenBuffer pNew = NULL; + int iRet; + char *pPtr; + + pNew = CreateRuenBuffer(name); + if(!pNew) + { + return NULL; + } + + /* copy list*/ + iRet = LLDnodePtr2First(pOld->iLineList); + while(iRet != 0) + { + pPtr = (char *)LLDnodePtr(pOld->iLineList); + LLDstringAdd(pNew->iLineList,pPtr); + iRet = LLDnodePtr2Next(pOld->iLineList); + } + return pNew; + } +/*-------------------------------------------------------------------------*/ + int BufferAppendLine(pRuenBuffer self, char *line) + { + assert(self); + + return LLDstringAppend(self->iLineList,line); + } +/*------------------------------------------------------------------------*/ + int BufferDel(pRuenBuffer self, int i) + { + int iNum; + int iRet; + + assert(self); + + iRet = LLDnodePtr2First(self->iLineList); + iNum = 0; + while(iRet != 0) + { + if(iNum == i) + { + LLDstringDelete(self->iLineList); + return 1; + } + iNum++; + iRet = LLDnodePtr2Next(self->iLineList); + } + return 0; + } +/*------------------------------------------------------------------------*/ + int BufferInsertAfter(pRuenBuffer self, int i, char *line) + { + int iNum; + int iRet; + + assert(self); + + iRet = LLDnodePtr2First(self->iLineList); + iNum = 0; + while(iRet != 0) + { + if(iNum == i) + { + LLDstringInsert(self->iLineList, line); + return 1; + } + iNum++; + iRet = LLDnodePtr2Next(self->iLineList); + } + return 0; + } +/*------------------------------------------------------------------------*/ + int BufferPrint(pRuenBuffer self, SConnection *pCon) + { + int iRet; + char *pPtr = NULL; + char pBueffel[512]; + int iCount = 1; + + iRet = LLDnodePtr2First(self->iLineList); + sprintf(pBueffel,"Listing for Bueffer %s",self->name); + SCWrite(pCon,pBueffel,eValue); + while(iRet != 0) + { + pPtr = (char *)LLDnodePtr(self->iLineList); + sprintf(pBueffel,"[%d] %s",iCount,pPtr); + SCWrite(pCon,pBueffel,eValue); + iRet = LLDnodePtr2Next(self->iLineList); + iCount++; + } + return 1; + } + +/*------------------------------------------------------------------------*/ + extern char *StrReplace(char *str, char *old, char *pNew); + /* realised in Strrepl.c + */ + + int BufferReplace(pRuenBuffer self, char *pattern, char *pReplace) + { + int iRet; + char *pPtr = NULL; + char pBueffel[1024]; + char *pRet; + + iRet = LLDnodePtr2First(self->iLineList); + while(iRet != 0) + { + pPtr = (char *)LLDnodePtr(self->iLineList); + strcpy(pBueffel,pPtr); + pRet = NULL; + pRet = StrReplace(pBueffel,pattern,pReplace); + if(pRet) + { + LLDstringDelete(self->iLineList); + iRet = LLDnodePtr2Next(self->iLineList); + LLDnodePtr2Prev(self->iLineList); + if(iRet) + { + LLDstringInsert(self->iLineList,pBueffel); + } + else + { + LLDstringAppend(self->iLineList,pBueffel); + } + } + iRet = LLDnodePtr2Next(self->iLineList); + } + return 1; + } +/*-----------------------------------------------------------------------*/ + int BufferRun(pRuenBuffer self, SConnection *pCon, SicsInterp *pSics) + { + int iRet; + char *pPtr = NULL; + int iInt, iRes; + + iRes = 1; + iRet = LLDnodePtr2First(self->iLineList); + while(iRet != 0) + { + pPtr = (char *)LLDnodePtr(self->iLineList); + iInt = InterpExecute(pSics,pCon,pPtr); + if(!iInt) + { + iRes = 0; + } + iRet = LLDnodePtr2Next(self->iLineList); + } + return iRes; + } +/*------------------------------------------------------------------------*/ + int BufferSave(pRuenBuffer self, char *file) + { + int iRet; + char *pPtr = NULL; + FILE *fd = NULL; + + fd = fopen(file,"w"); + if(fd == NULL) + { + return 0; + } + iRet = LLDnodePtr2First(self->iLineList); + while(iRet != 0) + { + pPtr = (char *)LLDnodePtr(self->iLineList); + fprintf(fd,"%s\n",pPtr); + iRet = LLDnodePtr2Next(self->iLineList); + } + fclose(fd); + return 1; + } +/*------------------------------------------------------------------------*/ + int BufferLoad(pRuenBuffer self, char *file) + { + int iRet; + char *pPtr = NULL; + FILE *fd = NULL; + char pBueffel[256]; + + fd = fopen(file,"r"); + if(fd == NULL) + { + return 0; + } + + pPtr = fgets(pBueffel,255,fd); + while(pPtr != NULL) + { + LLDstringAppend(self->iLineList,pBueffel); + pPtr = fgets(pBueffel,255,fd); + } + + fclose(fd); + return 1; + } + +/*------------------------------------------------------------------------*/ + pRuenBuffer FindRuenBuffer(SicsInterp *pSics, char *name) + { + pRuenBuffer pBuf = NULL; + CommandList *pCom = NULL; + + pCom = FindCommand(pSics,name); + if(!pCom) + { + return NULL; + } + pBuf = (pRuenBuffer)pCom->pData; + if(!pBuf) + { + return NULL; + } + if(!pBuf->pDes) + { + return NULL; + } + if(strcmp(pBuf->pDes->name,"SicsRuenBuffer") != 0) + { + return NULL; + } + return pBuf; + } +/*-------------------------------------------------------------------------*/ + int InitBufferSys(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]) + { + pRuenStack pStack = NULL; + + pStack = CreateRuenStack(); + if(!pStack) + { + SCWrite(pCon,"ERROR: No memory to create Ruen-Stack",eError); + return 0; + } + AddCommand(pSics,"Buf",BufferCommand,NULL,NULL); + AddCommand(pSics,"Stack",RuenStackAction,DeleteRuenStack,pStack); + return 1; + } +/*------------------------------------------------------------------------*/ + int BufferCommand(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]) + { + int iRet, iRet2; + char pBueffel[512]; + char **argx; + FuPaResult PaRes; + pRuenBuffer pBuf = NULL; + FuncTemplate BufferTemplate[] = { + {"new",1,{FUPATEXT} }, + {"del",1,{FUPATEXT} }, + {"copy",2,{FUPATEXT, FUPATEXT}}, + }; + + assert(pCon); + assert(pSics); + + /* minimum user to use this */ + if(!SCMatchRights(pCon,usUser)) + { + return 0; + } + /* parse function args */ + argtolower(argc,argv); + argx = &argv[1]; + iRet = EvaluateFuPa((pFuncTemplate)&BufferTemplate,3,argc-1,argx,&PaRes); + if(iRet < 0) + { + sprintf(pBueffel,"%s",PaRes.pError); + SCWrite(pCon,pBueffel,eError); + return 0; + } + + switch(iRet) + { + case 0: /* new */ + pBuf = CreateRuenBuffer(PaRes.Arg[0].text); + if(!pBuf) + { + SCWrite(pCon, "ERROR: Out of memory allocating buffer",eError); + return 0; + } + iRet2 = AddCommand(pSics,pBuf->name,BufferAction,DeleteRuenBuffer, + (void *)pBuf); + if(!iRet2) + { + sprintf(pBueffel,"ERROR: duplicate command %s not created",pBuf->name); + SCWrite(pCon,pBueffel,eError); + DeleteRuenBuffer((void *)pBuf); + return 0; + } + return 1; + break; + case 1: /* del */ + return RemoveCommand(pSics,PaRes.Arg[0].text); + break; + case 2: /* copy */ + pBuf = FindRuenBuffer(pSics,PaRes.Arg[0].text); + if(!pBuf) + { + sprintf(pBueffel,"ERROR: Buffer %s not found", + PaRes.Arg[0].text); + SCWrite(pCon,pBueffel,eError); + return 0; + } + pBuf = CopyRuenBuffer(pBuf,PaRes.Arg[1].text); + if(!pBuf) + { + sprintf(pBueffel,"ERROR: creating buffer %s ", + PaRes.Arg[1].text); + SCWrite(pCon,pBueffel,eError); + return 0; + } + iRet2 = AddCommand(pSics,pBuf->name,BufferAction,DeleteRuenBuffer, + (void *)pBuf); + if(!iRet2) + { + sprintf(pBueffel,"ERROR: duplicate command %s not created",pBuf->name); + SCWrite(pCon,pBueffel,eError); + DeleteRuenBuffer((void *)pBuf); + return 0; + } + + return 1; + break; + default: + assert(0); + break; + + } + assert(0); + } +/*-------------------------------------------------------------------------*/ + int BufferAction(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]) + { + int iRet, iRet2; + char pBueffel[512]; + char **argx; + FuPaResult PaRes; + pRuenBuffer pBuf = NULL; + FuncTemplate BufferTemplate[] = { + {"append",0,{FUPATEXT} }, + {"del",1,{FUPAINT} }, + {"ins",1,{FUPAINT}}, + {"save",1,{FUPATEXT}}, + {"load",1,{FUPATEXT}}, + {"subst",2,{FUPATEXT,FUPATEXT}}, + {"print",0,{0,0}}, + {"run",0,{0,0}}, + NULL + }; + + assert(pCon); + assert(pSics); + pBuf = (pRuenBuffer)pData; + assert(pBuf); + + + /* You need to be user in order to do this */ + if(!SCMatchRights(pCon,usUser)) + { + return 0; + } + /* parse function args */ + argx = &argv[1]; + strtolower(argx[0]); + iRet = EvaluateFuPa((pFuncTemplate)&BufferTemplate,8,argc-1,argx,&PaRes); + if(iRet < 0) + { + sprintf(pBueffel,"%s",PaRes.pError); + SCWrite(pCon,pBueffel,eError); + return 0; + } + + switch(iRet) + { + case 0: /* append */ + argx = &argv[2]; + Arg2Text(argc-2,argx,pBueffel,511); + BufferAppendLine(pBuf,pBueffel); + SCSendOK(pCon); + return 1; + break; + case 1: /* del */ + iRet2 = BufferDel(pBuf,PaRes.Arg[0].iVal); + if(iRet2) + SCSendOK(pCon); + break; + case 2: /* ins */ + argx = &argv[3]; + Arg2Text(argc-3,argx,pBueffel,511); + iRet2 = BufferInsertAfter(pBuf,PaRes.Arg[0].iVal,pBueffel); + if(iRet2) + SCSendOK(pCon); + return iRet2; + break; + case 3: /* save */ + iRet2 = BufferSave(pBuf,PaRes.Arg[0].text); + if(!iRet2) + { + sprintf(pBueffel,"ERROR: cannot open %s for writing", + PaRes.Arg[0].text); + SCWrite(pCon,pBueffel,eError); + return 0; + } + else + { + SCSendOK(pCon); + return 1; + } + break; + case 4: /* load */ + iRet2 = BufferLoad(pBuf,PaRes.Arg[0].text); + if(!iRet2) + { + sprintf(pBueffel,"ERROR: cannot open %s for reading ", + PaRes.Arg[0].text); + SCWrite(pCon,pBueffel,eError); + return 0; + } + else + { + SCSendOK(pCon); + return 1; + } + break; + case 5: /* subst */ + iRet2 = BufferReplace(pBuf,PaRes.Arg[0].text,PaRes.Arg[1].text); + if(iRet2) + SCSendOK(pCon); + break; + case 6: /* print */ + return BufferPrint(pBuf,pCon); + break; + case 7: /* run */ + return BufferRun(pBuf,pCon,pSics); + default: + assert(0); + } + return 1; + } + diff --git a/buffer.h b/buffer.h new file mode 100644 index 0000000..d83d4fc --- /dev/null +++ b/buffer.h @@ -0,0 +1,96 @@ +/*--------------------------------------------------------------------------- + + T H E L N S R \" U N B U F F E R + + The LNS has devised a special scheme to operate an instrument + via R\"un sequeneces and buffers. A R\"unbuffer is a series of + commands which are collected in a buffer. This buffer is + implemented here. A buffer can be added to, printed loaded from + a file etc. and can be executed. + + The next schem is the R\"unlist which is a stack of R\"unbuffers. + That list can be exeuted as well. It gets a buffer from the + bottom of the stack and executes it and does so until the stack + is empty. While this is happening you are able to add other + buffers to the top of the stack. This schem is implemented in module + ruli. + + So, here is all necessary to deal with an individual buffer. + For Lists A. Reitsma's lld package will be used. This package + identifies a list by an integer handle. + + Mark Koennecke, January 1996 + + copyright: see implementation file +----------------------------------------------------------------------------*/ +#ifndef RUENBUFFER +#define RUENBUFFER +#include + + + typedef struct { + pObjectDescriptor pDes; /* needed */ + char *name; /* BufferName */ + int iLineList; /* Handle to the Line List */ + } RuenBuffer, *pRuenBuffer; + +/*--------------------- live & death ----------------------------------- */ + pRuenBuffer CreateRuenBuffer(char *name); + void DeleteRuenBuffer(void *pSelf); + pRuenBuffer CopyRuenBuffer(pRuenBuffer pOld, char *NewName); + +/*--------------------- operations --------------------------------------*/ + + int BufferAppendLine(pRuenBuffer self, char *line); + int BufferDel(pRuenBuffer self, int iLine); + /* + deletes line iLine from the RuenBuffer self + +-------------------------------------------------------------------------*/ + int BufferInsertAfter(pRuenBuffer self, int iLine, char *line); + /* + inserts line line AFTER line number iLine in the RuenBuffer self +------------------------------------------------------------------------- */ + int BufferPrint(pRuenBuffer self, SConnection *pCon); + /* + lists the contents of the RuenBuffer on the Connection pCon +------------------------------------------------------------------------ */ + int BufferReplace(pRuenBuffer self, char *pattern, char *pReplace); + /* + replaces all occurences of the string pattern in the whole RuenBuffer + by the replacement string pReplace. +------------------------------------------------------------------------- */ + int BufferRun(pRuenBuffer self, SConnection *pCon, SicsInterp *pSics); + /* + executes the lines of the Ruenbuffer one by one. + Returns 1 on success, 0 on error. +------------------------------------------------------------------------- */ + int BufferSave(pRuenBuffer self, char *file); + /* + writes the contents of Ruenbuffer self to the file specified by + file. + Returns 1 on success, 0 on error. +--------------------------------------------------------------------------*/ + int BufferLoad(pRuenBuffer self, char *file); + /* + reads the contents of file into the RuenBuffer self. + Returns 1 on success, 0 on error. + */ +/* ------------------------ object functions ----------------------------*/ + int InitBufferSys(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]); + int BufferCommand(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]); + int BufferAction(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]); + +/* ----------------------- utility --------------------------------------*/ + pRuenBuffer FindRuenBuffer(SicsInterp *pSics, char *name); + /* + similar to FindCommand in SCinter.h. But checks the object found if + it is a RuenBuffer. + Returns NULL if no RuenBuffer with this name could be found. + Returns a pointer to the RuenBuffer, when a RuenBuffer of this + name could be found in the interpreter pSics +----------------------------------------------------------------------------*/ +#endif diff --git a/dilludriv.c b/dilludriv.c new file mode 100644 index 0000000..5937834 --- /dev/null +++ b/dilludriv.c @@ -0,0 +1,272 @@ +/*-------------------------------------------------------------------------- + D I L L U D R I V + + This file contains the implementation of a driver for the Oxford + Instruments dillution cryostat using the CC0-510/AVSI temperature + controller. + + + Mark Koennecke, October 1997 + + Copyright: see copyright.h +----------------------------------------------------------------------------*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include + + typedef struct __EVDriver *pEVDriver; + +#include +#include "hardsup/dillutil.h" +#include "hardsup/el734_def.h" +#include "hardsup/el734fix.h" +#include "dilludriv.h" + +/*-----------------------------------------------------------------------*/ + typedef struct { + pDILLU pData; + char *pHost; + int iPort; + int iChannel; + int iLastError; + char *pTranslationFile; + } DILLUDriv, *pDILLUDriv; +/*----------------------------------------------------------------------------*/ + static int GetDILLUPos(pEVDriver self, float *fPos) + { + pDILLUDriv pMe = NULL; + int iRet; + + assert(self); + pMe = (pDILLUDriv)self->pPrivate; + assert(pMe); + + iRet = DILLU_Read(&pMe->pData,fPos); + if(iRet != 1 ) + { + pMe->iLastError = iRet; + return 0; + } + if( (*fPos < 0) || (*fPos > 1000) ) + { + *fPos = -999.; + return 0; + } + return 1; + } +/*----------------------------------------------------------------------------*/ + static int DILLURun(pEVDriver self, float fVal) + { + pDILLUDriv pMe = NULL; + int iRet; + + assert(self); + pMe = (pDILLUDriv )self->pPrivate; + assert(pMe); + + iRet = DILLU_Set(&pMe->pData,fVal); + if(iRet != 1) + { + pMe->iLastError = iRet; + return 0; + } + return 1; + } +/*--------------------------------------------------------------------------*/ + static int DILLUError(pEVDriver self, int *iCode, char *error, int iErrLen) + { + pDILLUDriv pMe = NULL; + + assert(self); + pMe = (pDILLUDriv)self->pPrivate; + assert(pMe); + + *iCode = pMe->iLastError; + DILLU_Error2Text(&pMe->pData,pMe->iLastError,error,iErrLen); + + return 1; + } +/*--------------------------------------------------------------------------*/ + static int DILLUSend(pEVDriver self, char *pCommand, char *pReply, int iLen) + { + pDILLUDriv pMe = NULL; + int iRet; + + assert(self); + pMe = (pDILLUDriv )self->pPrivate; + assert(pMe); + + iRet = DILLU_Send(&pMe->pData,pCommand, pReply,iLen); + if(iRet != 1) + { + pMe->iLastError = iRet; + return 0; + } + return 1; + + } +/*--------------------------------------------------------------------------*/ + static int DILLUInit(pEVDriver self) + { + pDILLUDriv pMe = NULL; + int iRet; + + assert(self); + pMe = (pDILLUDriv )self->pPrivate; + assert(pMe); + + pMe->pData = NULL; + iRet = DILLU_Open(&pMe->pData, pMe->pHost, pMe->iPort, pMe->iChannel, + 0,pMe->pTranslationFile); + if(iRet != 1) + { + pMe->iLastError = iRet; + return 0; + } + DILLU_Config(&pMe->pData, 1000); + return 1; + } +/*--------------------------------------------------------------------------*/ + static int DILLUClose(pEVDriver self) + { + pDILLUDriv pMe = NULL; + int iRet; + + assert(self); + pMe = (pDILLUDriv )self->pPrivate; + assert(pMe); + + DILLU_Close(&pMe->pData); + return 1; + } +/*---------------------------------------------------------------------------*/ + static int DILLUFix(pEVDriver self, int iError) + { + pDILLUDriv pMe = NULL; + int iRet; + + assert(self); + pMe = (pDILLUDriv )self->pPrivate; + assert(pMe); + + switch(iError) + { + /* network errors */ + case EL734__BAD_FLUSH: + case EL734__BAD_RECV: + case EL734__BAD_RECV_NET: + case EL734__BAD_RECV_UNKN: + case EL734__BAD_RECVLEN: + case EL734__BAD_RECV1: + case EL734__BAD_RECV1_PIPE: + case EL734__BAD_RNG: + case EL734__BAD_SEND: + case EL734__BAD_SEND_PIPE: + case EL734__BAD_SEND_NET: + case EL734__BAD_SEND_UNKN: + case EL734__BAD_SENDLEN: + DILLUClose(self); + iRet = DILLUInit(self); + if(iRet) + { + return DEVREDO; + } + else + { + return DEVFAULT; + } + break; + /* handable protocoll errors */ + case EL734__BAD_TMO: + return DEVREDO; + break; + case DILLU__NODILLFILE: + case DILLU__ERRORTABLE: + case DILLU__READONLY: + case DILLU__OUTOFRANGE: + case DILLU__BADMALLOC: + case DILLU__FILENOTFOUND: + return DEVFAULT; + case DILLU__BADREAD: + case DILLU__SILLYANSWER: + return DEVREDO; + default: + return DEVFAULT; + break; + } + return DEVFAULT; + } + +/*--------------------------------------------------------------------------*/ + static int DILLUHalt(pEVDriver *self) + { + assert(self); + + return 1; + } +/*------------------------------------------------------------------------*/ + void KillDILLU(void *pData) + { + pDILLUDriv pMe = NULL; + + pMe = (pDILLUDriv)pData; + assert(pMe); + + if(pMe->pHost) + { + free(pMe->pHost); + } + if(pMe->pTranslationFile) + { + free(pMe->pTranslationFile); + } + free(pMe); + } +/*------------------------------------------------------------------------*/ + pEVDriver CreateDILLUDriv(int argc, char *argv[]) + { + pEVDriver pNew = NULL; + pDILLUDriv pSim = NULL; + + /* check for arguments */ + if(argc < 3) + { + return NULL; + } + + pNew = CreateEVDriver(argc,argv); + pSim = (pDILLUDriv)malloc(sizeof(DILLUDriv)); + memset(pSim,0,sizeof(DILLUDriv)); + if(!pNew || !pSim) + { + return NULL; + } + pNew->pPrivate = pSim; + pNew->KillPrivate = KillDILLU; + + /* initalise pDILLUDriver */ + pSim->iLastError = 0; + pSim->pHost = strdup(argv[0]); + pSim->iPort = atoi(argv[1]); + pSim->iChannel = atoi(argv[2]); + pSim->pTranslationFile = strdup(argv[3]); + + + /* initialise function pointers */ + pNew->SetValue = DILLURun; + pNew->GetValue = GetDILLUPos; + pNew->Send = DILLUSend; + pNew->GetError = DILLUError; + pNew->TryFixIt = DILLUFix; + pNew->Init = DILLUInit; + pNew->Close = DILLUClose; + + return pNew; + } + diff --git a/dilludriv.h b/dilludriv.h new file mode 100644 index 0000000..19e0b43 --- /dev/null +++ b/dilludriv.h @@ -0,0 +1,15 @@ +/*------------------------------------------------------------------------ + D I L L U D R I V + + A SICS driver for thedillution cryostat using the CCO-510/AVSI + controller. + + Mark Koennecke, October 1997 + + copyright: see copyright.h +---------------------------------------------------------------------------*/ +#ifndef DILLUDRIV +#define DILLUDRIV + pEVDriver CreateDILLUDriv(int argc, char *argv[]); + +#endif \ No newline at end of file diff --git a/dmc.c b/dmc.c new file mode 100644 index 0000000..31614cf --- /dev/null +++ b/dmc.c @@ -0,0 +1,54 @@ +/*------------------------------------------------------------------------- + D M C + + this modules purpose is solely to initialise the commands specific to + the powder diffractometer DMC. + + Mark Koenencke, March 1997 + + Copyright: + + Labor fuer Neutronenstreuung + Paul Scherrer Institut + CH-5423 Villigen-PSI + + + The authors hereby grant permission to use, copy, modify, distribute, + and license this software and its documentation for any purpose, provided + that existing copyright notices are retained in all copies and that this + notice is included verbatim in any distributions. No written agreement, + license, or royalty fee is required for any of the authorized uses. + Modifications to this software may be copyrighted by their authors + and need not follow the licensing terms described here, provided that + the new terms are clearly indicated on the first page of each file where + they apply. + + IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY + FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY + DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE + IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE + NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR + MODIFICATIONS. +----------------------------------------------------------------------------*/ +#include +#include +#include +#include "fortify.h" +#include "conman.h" +#include "obdes.h" +#include "napi.h" +#include "nxdata.h" +#include "dmc.h" + + int InitDmc(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]) + { + AddCommand(pSics,"StoreData",SNStoreDMC,NULL,NULL); + return 1; + } diff --git a/dmc.h b/dmc.h new file mode 100644 index 0000000..aee4fb3 --- /dev/null +++ b/dmc.h @@ -0,0 +1,19 @@ + +/*------------------------------------------------------------------------- + D M C + + this modules purpose is solely to initialise the commands specific to + the powder diffractometer DMC. + + Mark Koenencke, March 1997 + + copyright: see implementation file. + +--------------------------------------------------------------------------*/ +#ifndef SICSDMC +#define SICSDMC + + int InitDmc(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]); + +#endif diff --git a/dmc.w b/dmc.w new file mode 100644 index 0000000..347b682 --- /dev/null +++ b/dmc.w @@ -0,0 +1,27 @@ +\subsection{DMC module} +This module initialises all DMC specific commands. Currently there is only +one: StoreData. This does not do much, it is just here as a container for +things to come. + +@d Protos @{ + int InitDmc(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]); +@} + +@o dmc.h @{ +/*------------------------------------------------------------------------- + D M C + + this modules purpose is solely to initialise the commands specific to + the powder diffractometer DMC. + + Mark Koenencke, March 1997 + + copyright: see implementation file. + +--------------------------------------------------------------------------*/ +#ifndef SICSDMC +#define SICSDMC +@< Protos @> +#endif +@} \ No newline at end of file diff --git a/docho.c b/docho.c new file mode 100644 index 0000000..1bd3b43 --- /dev/null +++ b/docho.c @@ -0,0 +1,747 @@ +/*-------------------------------------------------------------------------- + D o C h o + + + A SICS driver for a Dornier Chopper Control System accessed through a + RS-232 interface connected to a Macintosh PC running the SerialPortServer + terminal server program. There are two choppers which ususally run at fixed + speed ratios against each other. There ia also a phase difference between + the two choppers. And lots of machine surveillance parameters. + + This driver is used by the generic chopper or device controller as described + in choco.tex. + + + Mark Koennecke, January 1999 + + Modified to support a single chopper only, + + Uwe Filges, Mark Koennecke; November 2001 +--------------------------------------------------------------------------*/ +#include +#include +#include +#include +#include +#include +#include "hardsup/serialsinq.h" +#include "hardsup/el734_errcodes.h" +#include "hardsup/el734fix.h" +#include + +/*----------------------------------------------------------------------- + A private data structure for this Dornier chopper +-------------------------------------------------------------------------*/ + typedef struct { + char *pHost; + int iPort; + int iChannel; + void *pData; + int iRefreshIntervall; + pStringDict pPar; + time_t tRefresh; + int iStop; + long lTask; + int iError; + int iBusy; + float fRatio; + int iSingle; + char pError[80]; + } DoCho, *pDoCho; +/* + pHost, iPort and iChannel combined are the adress of the chopper + controller at the Macintosh terminal server. pData is the serial + port connection data structure needed and managed by the SerialIO + functions. + + As the communication with the Dornier Chopper System is very slow the + parameter list of this driver will only be updated a predefined time + intervalls. In between buffered values will be returned for requests. + The buffered parameters are held in the string dictioanry pPar. + iRefreshIntervall is the time between refreshs. tRefresh is the time for + the next refresh. iBusy is flag which indicates, that it was tried to + modify a variable. This will only be reflected with the next status update. + In between DoChoCheckPar might conclude, that the chopper is already + done. iBusy is meant to stop that. It is set when a parameter is changed + and cleared bu the status message code. DoChoCheckPar checks for it. + + Refreshing will be performed by a special SICS task which will be + started when the driver is initialized. In order to stop this task when + need arises the parameter iStop can be set to true. + + iError is the last error reported on this device. If no error: 0 + + fRatio is the target value for the chopper ratio. In contrast to the + other parameters, its target value cannot be extracted from the chopper + status message. + + iSingle is a flag which is true if only a single chopper is controlled + through this driver. This supports the POLDI single choper case. + +*/ +/*---------------------------------------------------------------------- + ERROR CODES: +*/ +#define UNDRIVABLE -8002 +#define UNKNOWNPAR -8003 +#define PARERROR -8004 +#define BADSYNC -8005 +#define BADSTOP -8006 +#define CHOPERROR -8007 + +extern char *trim(char *pTrim); /* trim.c */ + +/*----------------------------------------------------------------------*/ +static void SplitChopperReply(pCodri self, char *prefix, char *pBueffel) +{ + char pToken[30], pValue[20], pEntry[80]; + char *pPtr, *pTok, *pVal; + int iCount, iRet; + pDoCho pPriv = NULL; + + pPriv = (pDoCho)self->pPrivate; + + /* decompose pBueffel and store into string dictionary */ + pPtr = strtok(pBueffel,";"); + while(pPtr != NULL) + { + iCount = sscanf(pPtr,"%s %s",pToken,pValue); + if(iCount == 2) + { + pTok = trim(pToken); + pVal = trim(pValue); + pEntry[0] = '\0'; + sprintf(pEntry,"%s.%s",prefix,pTok); + iRet = StringDictUpdate(pPriv->pPar,pEntry,pVal); + if(!iRet) + { + StringDictAddPair(pPriv->pPar,pEntry,pVal); + strcat(self->pParList,pEntry); + strcat(self->pParList,","); + } + } + else + { + /* this fixes a bug with oversized messages in dphas */ + if(strstr(pPtr,"dphas") != NULL) + { + sprintf(pEntry,"%s.dphas",prefix); + iRet = StringDictUpdate(pPriv->pPar, + pEntry,pPtr+5); + if(!iRet) + { + StringDictAddPair(pPriv->pPar,pEntry, + pPtr+5); + strcat(self->pParList,pEntry); + strcat(self->pParList,","); + } + } + } + pPtr = strtok(NULL,";"); + } +} +/*------------------------------------------------------------------------- + Well, DoChoStatus sends a status request to the Dornier chopper control + system. There is a gotcha, you need three reads to get the full information. + Then the answer is parsed and decomposed into parameter content for the + string dictionary. The single status components are separated by ;. +-------------------------------------------------------------------------*/ + + static int DoChoStatus(pCodri self) + { + int iRet, iCount, iCode; + char pBueffel[1024], pToken[30], pValue[20]; + char *pPtr, *pTok, *pVal; + pDoCho pPriv = NULL; + + assert(self); + pPriv = (pDoCho)self->pPrivate; + assert(pPriv); + pPriv->iBusy = 0; + pPriv->iError = 0; + + + /* first send, command, returns the echo */ + iRet = SerialWriteRead(&(pPriv->pData),"asyst 1",pBueffel,1023); + if(iRet < 0) + { + pPriv->iError = iRet; + return 0; + } + + /* next send: reads first chopper line */ + iRet = SerialWriteRead(&(pPriv->pData),"",pBueffel,1023); + if(iRet < 0) + { + pPriv->iError = iRet; + return 0; + } + SplitChopperReply(self,"chopper1",pBueffel); + + if(!pPriv->iSingle) + { + /* second send: get next second chopper line */ + iRet = SerialWriteRead(&(pPriv->pData),"",pBueffel,1023); + if(iRet < 0) + { + pPriv->iError = iRet; + return 0; + } + SplitChopperReply(self,"chopper2",pBueffel); + } + + + return 1; + } +/*-------------------------------------------------------------------------*/ + static int DoChoTask(void *pData) + { + pCodri self = NULL; + pDoCho pPriv = NULL; + int iCode, iRet; + char pDummy[60]; + + self = (pCodri)pData; + assert(self); + pPriv = (pDoCho)self->pPrivate; + assert(pPriv); + + /* check for stop */ + if(pPriv->iStop) + return 0; + + + /* check if it is time to run a status request */ + if(time(NULL) > pPriv->tRefresh) + { + /* try, fix error */ + if(pPriv->iError != 0) + { + self->GetError(self,&iCode,pDummy,59); + iRet = self->TryFixIt(self,iCode); + if(iRet == CHFAIL) + { + pPriv->tRefresh = time(NULL) + pPriv->iRefreshIntervall; + return 1; + } + } + /* do it */ + DoChoStatus(self); + pPriv->tRefresh = time(NULL) + pPriv->iRefreshIntervall; + } + return 1; + } +/*------------------------------------------------------------------------*/ + static void DoChoKill(void *pData) + { + pCodri self = NULL; + pDoCho pPriv = NULL; + + self = (pCodri)pData; + if(!self) + return; + pPriv = (pDoCho)self->pPrivate; + if(!pPriv) + return; + + + if(pPriv->pData) + { + SerialClose(&(pPriv->pData)); + pPriv->pData = NULL; + } + + if(pPriv->pHost) + free(pPriv->pHost); + if(pPriv->pPar) + DeleteStringDict(pPriv->pPar); + + free(pPriv); + } +/*-------------------------------------------------------------------------*/ + static int DoChoInit(pCodri self) + { + pDoCho pPriv = NULL; + int iRet; + + assert(self); + pPriv = (pDoCho)self->pPrivate; + assert(pPriv); + pPriv->iError = 0; + + /* first open the connection to the serial port server and channel */ + iRet = SerialOpen(&(pPriv->pData),pPriv->pHost,pPriv->iPort, + pPriv->iChannel); + if(iRet <= 0) + { + pPriv->iError = iRet; + return 0; + } + /* configure the connection */ + SerialConfig(&(pPriv->pData),10000); + SerialATerm(&(pPriv->pData),"1\r\n"); + SerialSendTerm(&(pPriv->pData),"\r"); + + pPriv->iStop = 0; + pPriv->tRefresh = 0; /* force a status request when first run */ + + /* start the update task */ + if(pPriv->lTask == 0) + { + pPriv->lTask = TaskRegister(pServ->pTasker, + DoChoTask, + NULL, + NULL, + self, + 1); + } + return 1; + } +/*------------------------------------------------------------------------*/ + static int DoChoClose(pCodri self) + { + pDoCho pPriv = NULL; + int iRet; + long lVal; + + assert(self); + pPriv = (pDoCho)self->pPrivate; + assert(pPriv); + + if(pPriv->pData) + { + SerialClose(&(pPriv->pData)); + pPriv->pData = NULL; + } + return 1; + } +/*------------------------------------------------------------------------*/ + static int DoChoDelete(pCodri self) + { + pDoCho pPriv = NULL; + + assert(self); + pPriv = (pDoCho)self->pPrivate; + assert(pPriv); + + if(pPriv->pData) + { + SerialClose(&(pPriv->pData)); + pPriv->pData = NULL; + } + + if(pPriv->pHost) + free(pPriv->pHost); + if(pPriv->pPar) + DeleteStringDict(pPriv->pPar); + + free(pPriv); + + return 1; + } +/*--------------------------------------------------------------------------*/ + static int DoChoSetPar2(pCodri self, char *parname, char *pValue) + { + pDoCho pPriv = NULL; + char pCommand[80], pReply[132]; + char pState[20]; + int iRet; + + assert(self); + pPriv = (pDoCho)self->pPrivate; + assert(pPriv); + + /* deal with our four parameters */ + if(strcmp(parname,"chopper1.nspee") == 0) + { + sprintf(pCommand,"nspee 1 %s",pValue); + } + else if(strcmp(parname,"chopper2.nspee") == 0) + { + iRet = StringDictGet(pPriv->pPar,"chopper2.state",pState,19); + if(iRet && strstr(pState,"async") != NULL ) + { + sprintf(pCommand,"nspee 2 %s",pValue); + } + else + { + pPriv->iError = BADSYNC; + return 0; + } + } + else if(strcmp(parname,"chopper2.nphas") == 0) + { + sprintf(pCommand,"nphas 2 %s",pValue); + } + else if(strcmp(parname,"chopper2.ratio") == 0) + { + sprintf(pCommand,"ratio 2 %s",pValue); + } + else + { + pPriv->iError = UNDRIVABLE; + return 0; + } + + iRet = SerialWriteRead(&(pPriv->pData),pCommand,pReply,131); + if(iRet != 1) + { + pPriv->iError = iRet; + return 0; + } + if(strstr(pReply,"error") != NULL) + { + pPriv->iError = CHOPERROR; + strncpy(pPriv->pError,pReply,79); + return 0; + } + else + { + pPriv->iError = 0; + } + pPriv->iBusy = 1; + return 1; + } +/*-------------------------------------------------------------------------*/ + static int DoChoHalt(pCodri self) + { + pDoCho pPriv = NULL; + + assert(self); + pPriv = (pDoCho)self->pPrivate; + assert(pPriv); + + /* + there is no documented way to stop the Dornier chopper + system. This at least makes SICS happy. + */ + pPriv->iError = BADSTOP; + pPriv->iBusy = 0; + return 1; + } +/*---------------------------------------------------------------------------*/ + static int DoChoSetPar(pCodri self, char *parname, float fValue) + { + char pValue[50]; + pDoCho pPriv = NULL; + + assert(self); + pPriv = (pDoCho)self->pPrivate; + assert(pPriv); + + if(strstr(parname,"nspee") != NULL) + { + sprintf(pValue,"%d",(int)fValue); + } + else if(strstr(parname,"ratio") != NULL) + { + sprintf(pValue,"%d",(int)fValue); + pPriv->fRatio = (int)fValue; + } + else if(strcmp(parname,"updateintervall") == 0) + { + sprintf(pValue,"%d",(int)fValue); + StringDictUpdate(pPriv->pPar,"updateintervall",pValue); + pPriv->iRefreshIntervall = (int)fValue; + return 1; + } + else + { + sprintf(pValue,"%f",fValue); + } + return DoChoSetPar2(self,parname, pValue); + } +/*----------------------------------------------------------------------*/ + static int DoChoGetPar(pCodri self, char *parname, + char *pBuffer, int iBufLen) + { + pDoCho pPriv = NULL; + int iRet; + + assert(self); + pPriv = (pDoCho)self->pPrivate; + assert(pPriv); + + if(pPriv->iError != 0) + { + self->GetError(self,&iRet,pBuffer,iBufLen); + return 0; + } + + iRet = StringDictGet(pPriv->pPar,parname,pBuffer,iBufLen); + if(!iRet) + { + pPriv->iError = UNKNOWNPAR; + return 0; + } + return 1; + } +/*-----------------------------------------------------------------------*/ + static int DoChoCheckPar(pCodri self, char *parname) + { + pDoCho pPriv = NULL; + char pVal1[20], pVal2[20]; + float fTarget, fIst, fDelta; + int iRet; + + assert(self); + pPriv = (pDoCho)self->pPrivate; + assert(pPriv); + + /* check the busy flag first */ + if(pPriv->iBusy) + return HWBusy; + + /* was there an error in the status show? */ + if(pPriv->iError != 0) + { + return HWFault; + } + + /* updateintervall is always HWIdle */ + if(strcmp(parname,"updateintervall") == 0) + { + return HWIdle; + } + + /* OK, got a new status let us check the parameter */ + /* chopper 1 speed */ + if(strcmp(parname,"chopper1.nspee") == 0) + { + iRet = StringDictGet(pPriv->pPar,"chopper1.nspee",pVal1,19); + iRet += StringDictGet(pPriv->pPar,"chopper1.aspee",pVal2,19); + if(iRet != 2) + { + pPriv->iError = PARERROR; + return HWFault; + } + sscanf(pVal1,"%f",&fTarget); + sscanf(pVal2,"%f",&fIst); + fDelta = fTarget - fIst; + if(fDelta < 0.0) + fDelta = -fDelta; + if(fDelta > 50) + { + return HWBusy; + } + else + { + return HWIdle; + } + } + /* chopper 2 speed */ + if(strcmp(parname,"chopper2.nspee") == 0) + { + iRet = StringDictGet(pPriv->pPar,"chopper2.nspee",pVal1,19); + iRet += StringDictGet(pPriv->pPar,"chopper2.aspee",pVal2,19); + if(iRet != 2) + { + pPriv->iError = PARERROR; + return HWFault; + } + sscanf(pVal1,"%f",&fTarget); + sscanf(pVal2,"%f",&fIst); + fDelta = fTarget - fIst; + if(fDelta < 0.0) + fDelta = -fDelta; + if(fDelta > 5.) + { + return HWBusy; + } + else + { + return HWIdle; + } + } + + /* phase */ + if(strcmp(parname,"chopper2.nphas") == 0) + { + iRet = StringDictGet(pPriv->pPar,"chopper2.dphas",pVal1,19); + sscanf(pVal1,"%f",&fDelta); + if(fDelta < 0.) + fDelta = - fDelta; + if(fDelta > 0.3) + { + return HWBusy; + } + else + { + return HWIdle; + } + } + + /* ratio */ + if(strcmp(parname,"chopper2.ratio") == 0) + { + iRet = StringDictGet(pPriv->pPar,"chopper2.ratio",pVal1,19); + sscanf(pVal1,"%f",&fIst); + fDelta = fIst - pPriv->fRatio; + if(fDelta < 0.) + fDelta = - fDelta; + if(fDelta > 0.3) + { + return HWBusy; + } + else + { + return HWIdle; + } + } + pPriv->iError = UNKNOWNPAR; + return HWFault; + } +/*-------------------------------------------------------------------------*/ + static int DoChoError(pCodri self, int *iCode, char *pError, int iLen) + { + pDoCho pPriv = NULL; + + assert(self); + pPriv = (pDoCho)self->pPrivate; + assert(pPriv); + + *iCode = pPriv->iError; + switch(pPriv->iError) + { + case UNDRIVABLE: + strncpy(pError,"Parameter is not drivable",iLen); + break; + case UNKNOWNPAR: + strncpy(pError,"Parameter is unknown",iLen); + break; + case PARERROR: + strncpy(pError,"Internal parameter error",iLen); + break; + case BADSYNC: + strncpy(pError,"Cannot drive slave chopper",iLen); + break; + case CHOPERROR: + strncpy(pError,pPriv->pError,iLen); + break; + case BADSTOP: + strncpy(pError, + "User called STOP. WARNING: chopper is still untamed!", + iLen); + break; + default: + SerialError(pPriv->iError,pError,iLen); + break; + } + pPriv->iError = 0; + return 1; + } +/*------------------------------------------------------------------------*/ + static int DoChoFix(pCodri self, int iCode) + { + pDoCho pPriv = NULL; + int iRet; + + assert(self); + pPriv = (pDoCho)self->pPrivate; + assert(pPriv); + + switch(iCode) + { + /* network errors */ + case EL734__BAD_FLUSH: + case EL734__BAD_RECV: + case EL734__BAD_RECV_NET: + case EL734__BAD_RECV_UNKN: + case EL734__BAD_RECVLEN: + case EL734__BAD_RECV1: + case EL734__BAD_RECV1_PIPE: + case EL734__BAD_RNG: + case EL734__BAD_SEND: + case EL734__BAD_SEND_PIPE: + case EL734__BAD_SEND_NET: + case EL734__BAD_SEND_UNKN: + case EL734__BAD_SENDLEN: + case NOCONNECTION: + SerialForceClose(&(pPriv->pData)); + pPriv->pData = NULL; + iRet = SerialOpen(&(pPriv->pData),pPriv->pHost, + pPriv->iPort,pPriv->iChannel); + if(iRet == 1 ) + { + return CHREDO; + } + else + { + return CHFAIL; + } + break; + case EL734__FORCED_CLOSED: + iRet = DoChoInit(self); + if(iRet) + { + return CHREDO; + } + else + { + return CHFAIL; + } + break; + default: + return CHFAIL; + break; + } + return CHFAIL; + } +/*-------------------------------------------------------------------------*/ + pCodri MakeDoChoDriver(char *pHost, int iPort, int iChannel, int iSingle) + { + pCodri pNew = NULL; + pDoCho pPriv = NULL; + char *pText; + + /* allocate memory */ + pText = (char *)malloc(4096*sizeof(char)); + pNew = (pCodri)malloc(sizeof(Codri)); + pPriv = (pDoCho)malloc(sizeof(DoCho)); + if( !pText || !pNew || !pPriv) + { + return NULL; + } + memset(pText,0,4096); + memset(pNew,0,sizeof(Codri)); + memset(pPriv,0,sizeof(DoCho)); + + /* initialize private data structure */ + pPriv->pHost = strdup(pHost); + pPriv->iPort = iPort; + pPriv->iChannel = iChannel; + pPriv->pData = NULL; + pPriv->iRefreshIntervall = 60; + pPriv->pPar = CreateStringDict(); + pPriv->tRefresh = time(NULL); + pPriv->iSingle = iSingle; + if(!pPriv->pPar) + { + free(pText); + free(pNew); + free(pPriv); + return NULL; + } + + /* install codri */ + pNew->Init = DoChoInit; + pNew->Close = DoChoClose; + pNew->Delete = DoChoDelete; + pNew->SetPar = DoChoSetPar; + pNew->SetPar2 = DoChoSetPar2; + pNew->GetPar = DoChoGetPar; + pNew->CheckPar = DoChoCheckPar; + pNew->GetError = DoChoError; + pNew->TryFixIt = DoChoFix; + pNew->Halt = DoChoHalt; + pNew->pParList = pText; + strcpy(pNew->pParList,"updateintervall,"); + StringDictAddPair(pPriv->pPar,"updateintervall","60"); + pNew->pPrivate = pPriv; + + return pNew; + } + + + + diff --git a/ecb.c b/ecb.c new file mode 100644 index 0000000..2ec1cae --- /dev/null +++ b/ecb.c @@ -0,0 +1,496 @@ +/*----------------------------------------------------------------------- + The ECB is a rack controller from Risoe based on a Z80 processor. + This module provides some functions for communicating with such a + device. This is the implementation file. + + WARNING: This contains code which may be endian dependent! + + copyright: see file COPYRIGHT + + Mark Koennecke, January 2002, with some bits taken from the original + tascom code. + -------------------------------------------------------------------------*/ +#include +#include +#include +#include +#include +#include "fortify.h" +#include "sics.h" +#include "ecb.h" +#include "ecb.i" +/*------------- private defines and error codes ------------------------*/ +#define ACKN ('\6') /* Acknowledge character */ +#define READ_BYTES 3 +#define WRITE_BYTES 4 +#define DMAREAD 5 +#define ECB_BYTES 65536L + +typedef union /* Used to swap bytes in 'address' and 'byte_count' */ + { + unsigned short word; + struct + { + unsigned char msb; /* Most significant byte */ + unsigned char lsb; /* Least significant byte */ + }b; + }Swap; +/* ------- error codes */ +#define ECBILLEGALFUNC -100 +#define ECBOVERFLOW -101 + +/*----------------------------------------------------------------------*/ +static int ecbSendFunc(pECB self, int func){ + unsigned char function, response; + int count, status; + + /* + send function code + */ + function = (unsigned char)func; + count = 1; + status = GPIBsend(self->gpib,self->ecbDeviceID,&function,count); + if(status < 0){ + self->lastError = status; + return 0; + } + + /* + read acknowledge byte + */ + status = GPIBread(self->gpib,self->ecbDeviceID,&response,count); + if(status < 0){ + self->lastError = status; + return 0; + } + if(response != ACKN){ + self->lastError = ECBILLEGALFUNC; + return 0; + } + return 1; +} +/*-----------------------------------------------------------------------*/ +int ecbExecute(pECB self, int func, Z80_reg in, Z80_reg *out){ + int count, status; + + assert(self != NULL); + assert(self->gpib != NULL); + self->lastError = 0; + + /* + send function code + */ + status = ecbSendFunc(self,func); + if(status <= 0){ + return status; + } + + /* + send input register + */ + count = 4; + status = GPIBsend(self->gpib,self->ecbDeviceID, &in, count); + if(status < 0){ + self->lastError = status; + return 0; + } + + /* + read result register + */ + status = GPIBread(self->gpib,self->ecbDeviceID, out, count); + if(status < 0){ + self->lastError = status; + return 0; + } + + return 1; +} +/*----------------------------------------------------------------------*/ +static int ecbPrepareIO(pECB self, int func, unsigned short address, + unsigned short byteCount){ + Swap save, adr, count; + int status, bytes; + + if(byteCount > ECB_BYTES){ + self->lastError = ECBOVERFLOW; + return 0; + } + + /* + Swap address and byteCount?? This may be a portability issue! + This may not be necessary on some platforms + */ + save.word = address; /* Swap address bytes */ + adr.b.lsb = save.b.msb; + adr.b.msb = save.b.lsb; + save.word = byteCount; /* Swap byte count bytes */ + count.b.lsb = save.b.msb; + count.b.msb = save.b.lsb; + + status = ecbSendFunc(self,func); + if(status <= 0){ + return status; + } + + /* + send address + */ + bytes = 2; + status = GPIBsend(self->gpib,self->ecbDeviceID,&adr,bytes); + if(status < 0){ + self->lastError = status; + return 0; + } + + /* + send byte count + */ + status = GPIBsend(self->gpib,self->ecbDeviceID,&count,bytes); + if(status < 0){ + self->lastError = status; + return 0; + } + + return 1; +} +/*-----------------------------------------------------------------------*/ +int ecbRead(pECB self, unsigned short address, + void *buffer, int byteCount){ + + int status, count; + + assert(self != NULL); + assert(self->gpib != NULL); + self->lastError = 0; + + status = ecbPrepareIO(self,READ_BYTES,address,(unsigned short)byteCount); + if(status <= 0){ + return 0; + } + + /* + actual read + */ + status = GPIBread(self->gpib,self->ecbDeviceID, buffer, byteCount); + if(status < 0){ + self->lastError = status; + return 0; + } + + return 1; +} +/*----------------------------------------------------------------------*/ +int ecbDMARead(pECB self, unsigned short address, void *buffer, + unsigned short byteCount){ + int status, count; + + assert(self != NULL); + assert(self->gpib != NULL); + self->lastError = 0; + + status = ecbPrepareIO(self,DMAREAD,address,(unsigned short)byteCount); + if(status <= 0){ + return 0; + } + + usleep(20*1000); + + /* + actual read + */ + status = GPIBread(self->gpib,self->ecbDeviceID, buffer, byteCount); + if(status < 0){ + self->lastError = status; + return 0; + } + + return 1; +} +/*----------------------------------------------------------------------*/ +int ecbWrite(pECB self, unsigned short address, + void *buffer, int byteCount){ + + int status, count; + + assert(self != NULL); + assert(self->gpib != NULL); + self->lastError = 0; + + status = ecbPrepareIO(self,WRITE_BYTES,address,(unsigned short)byteCount); + if(status <= 0){ + return 0; + } + + /* + actual read + */ + status = GPIBsend(self->gpib,self->ecbDeviceID, buffer, byteCount); + if(status < 0){ + self->lastError = status; + return 0; + } + + return 1; +} +/*-----------------------------------------------------------------------*/ +void ecbErrorDescription(pECB self, char *buffer, int maxBuffer){ + int positive; + + switch(self->lastError){ + case ECBILLEGALFUNC: + strncpy(buffer,"Illegal ECB function called",maxBuffer); + return; + case ECBOVERFLOW: + strncpy(buffer, + "You tried to copy more then 64K onto the poor ECB, REFUSED!", + maxBuffer); + return; + } + + /* + GPIB error codes + */ + GPIBerrorDescription(self->gpib,self->lastError,buffer, maxBuffer); +} +/*----------------------------------------------------------------------*/ +void ecbClear(pECB self){ + GPIBclear(self->gpib, self->ecbDeviceID); +} +/*-----------------------------------------------------------------------*/ +int fixECBError(pECB self){ + int pos; + + switch(self->lastError){ + case ECBILLEGALFUNC: + case ECBOVERFLOW: + return HWFault; + } + + /* + GPIB error + */ + pos = -self->lastError; + switch(pos){ + case GPIBEABO: + return HWRedo; + default: + return HWFault; + } +} +/*------------------------------------------------------------------------*/ +int ECBAction(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]){ + pECB self = (pECB)pData; + Z80_reg in, out; + char pBuffer[80], pError[132]; + int status, iVal, func; + + assert(self != NULL); + + /* + Only managers will be allowed to wrestle directly with ECB + controllers. + */ + if(!SCinMacro(pCon)){ + if(!SCMatchRights(pCon,usMugger)){ + return 0; + } + } + + if(argc < 2){ + SCWrite(pCon,"ERROR: keyword required for ECB",eError); + return 0; + } + + strtolower(argv[1]); + if(strcmp(argv[1],"func") == 0){ + if(argc < 7){ + SCWrite(pCon,"ERROR: require function code and four register values", + eError); + return 0; + } + status = Tcl_GetInt(pSics->pTcl, argv[2],&func); + if(status != TCL_OK){ + SCWrite(pCon,"ERROR: failed to convert argument to int",eError); + return 0; + } + status = Tcl_GetInt(pSics->pTcl, argv[3],&iVal); + if(status != TCL_OK){ + SCWrite(pCon,"ERROR: failed to convert argument to int",eError); + return 0; + } + in.d = (unsigned char)iVal; + status = Tcl_GetInt(pSics->pTcl, argv[4],&iVal); + if(status != TCL_OK){ + SCWrite(pCon,"ERROR: failed to convert argument to int",eError); + return 0; + } + in.e = (unsigned char)iVal; + status = Tcl_GetInt(pSics->pTcl, argv[5],&iVal); + if(status != TCL_OK){ + SCWrite(pCon,"ERROR: failed to convert argument to int",eError); + return 0; + } + in.b = (unsigned char)iVal; + status = Tcl_GetInt(pSics->pTcl, argv[6],&iVal); + if(status != TCL_OK){ + SCWrite(pCon,"ERROR: failed to convert argument to int",eError); + return 0; + } + in.c = (unsigned char)iVal; + + status = ecbExecute(self,func,in,&out); + if(status != 1){ + ecbErrorDescription(self,pBuffer,79); + sprintf(pError,"ERROR: %s", pBuffer); + SCWrite(pCon,pError,eError); + return 0; + } + sprintf(pBuffer,"%d %d %d %d", + out.d, out.e, out.b, out.c); + SCWrite(pCon,pBuffer,eValue); + return 1; + } else if(strcmp(argv[1],"clear") == 0){ + ecbClear(self); + SCSendOK(pCon); + return 1; + }else if(strcmp(argv[1],"toint")== 0){ + sprintf(pBuffer,"%d",argv[2][0]); + SCWrite(pCon,pBuffer,eValue); + } else { + SCWrite(pCon,"ERROR: ECB does not understand keyword", eError); + return 0; + } +} +/*---------------------------------------------------------------------*/ +int ecbAssignEncoder(pECB self, int encoder, int motorNumber){ + + if(encoder <= 0 || encoder > 3){ + return 0; + } + + self->encoder[encoder-1] = motorNumber; + self->encoderDirty = 1; + return 1; +} +/*----------------------------------------------------------------------*/ +int ecbLoadEncoder(pECB self){ + Z80_reg in, out; + int status; + + if(self->encoderDirty != 1){ + /* + no need to do it if no change + */ + return 1; + } + + if(self->encoder[0] != 0){ + in.d = self->encoder[0]; + }else { + in.d = 0; + } + if(self->encoder[1] != 0){ + in.e = self->encoder[1]; + }else { + in.e = 0; + } + if(self->encoder[2] != 0){ + in.b = self->encoder[2]; + }else { + in.b = 0; + } + in.c = 1; + + status = ecbExecute(self,152,in,&out); + return status; +} +/*-----------------------------------------------------------------------*/ +void ECBKill(void *pData){ + pECB self = (pECB)pData; + + if(self == NULL){ + return; + } + + /* + Detaching here may be dangerous: If the GPIB has been deleted first, + this makes a core dump. Best is the GPIB keeps a list of attached + things and cleans them itself. + + GPIBdetach(self->gpib,self->ecbDeviceID); + */ + if(self->pDes){ + DeleteDescriptor(self->pDes); + } + free(self); +} +/*---------------------------------------------------------------------- +MakeECB name gpibcontroller boardNo gpib-address + -----------------------------------------------------------------------*/ +int MakeECB(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]){ + pECB self = NULL; + int address, status, boardNo; + pGPIB gpib = NULL; + char pError[132]; + + /* + we need a name, the GPIB controller and an address on the GPIB bus for + the ECB as arguments + */ + if(argc < 5){ + SCWrite(pCon,"ERROR: insufficient arguments to MakeECB",eError); + return 0; + } + gpib = FindCommandData(pSics,argv[2],"GPIB"); + if(gpib == NULL){ + sprintf(pError,"ERROR: no GPIB controller %s found", argv[2]); + SCWrite(pCon,pError,eError); + return 0; + } + status = Tcl_GetInt(pSics->pTcl,argv[3], &boardNo); + if(status != TCL_OK){ + sprintf(pError,"ERROR: failed to convert %s to integer",argv[3]); + SCWrite(pCon,pError,eError); + return 0; + } + status = Tcl_GetInt(pSics->pTcl,argv[4], &address); + if(status != TCL_OK){ + sprintf(pError,"ERROR: failed to convert %s to integer",argv[4]); + SCWrite(pCon,pError,eError); + return 0; + } + if(address < 0 || address > 30){ + SCWrite(pCon,"ERROR: invalid GPIB address specified",eError); + return 0; + } + + self = (pECB)malloc(sizeof(ECB)); + if(self == NULL){ + SCWrite(pCon,"ERROR: no memory to allocate ECB",eError); + return 0; + } + memset(self,0,sizeof(ECB)); + self->pDes = CreateDescriptor("ECB"); + if(self->pDes == NULL){ + SCWrite(pCon,"ERROR: no memory to allocate ECB",eError); + return 0; + } + self->gpib = gpib; + self->boardNumber = boardNo; + self->ecbAddress = address; + self->ecbDeviceID =GPIBattach(self->gpib,self->boardNumber, + self->ecbAddress,0, + 13,0,1); + if(self->ecbDeviceID <= 0){ + SCWrite(pCon,"ERROR: failed to initialize ECB connection", + eError); + ECBKill(self); + return 0; + } + AddCommand(pSics,argv[1],ECBAction,ECBKill,self); + return 1; +} + diff --git a/ecb.h b/ecb.h new file mode 100644 index 0000000..dd5b861 --- /dev/null +++ b/ecb.h @@ -0,0 +1,69 @@ + +/*----------------------------------------------------------------------- + The ECB is a rack controller from Risoe based on a Z80 processor. + This module provides some functions for communicating with such a + device. + + WARNING: This contains code which may be endian dependent! + + copyright: see file COPYRIGHT + + Mark Koennecke, January 2002, with some bits taken out of the + original tascom code. +------------------------------------------------------------------------*/ +#ifndef ECBCON +#define ECBCON +#include "gpibcontroller.h" + + +typedef struct { + unsigned char d; /* D register in Z80 */ + unsigned char e; /* E register in Z80 */ + unsigned char b; /* B register in Z80 */ + unsigned char c; /* C register in Z80 */ + } Z80_reg; + +/*-----------------------------------------------------------------------*/ + + typedef struct __ECB *pECB; + + int ecbExecute(pECB self, int func, Z80_reg in, Z80_reg *out); + int ecbRead(pECB self, unsigned short address, + void *buffer, int byteCount); + int ecbWrite(pECB self, unsigned short address, + void *buffer, int byteCount); + int ecbDMARead(pECB self, unsigned short address, void *buffer, + unsigned short byteCount); + void ecbClear(pECB self); + int fixECBError(pECB self); + void ecbErrorDescription(pECB self, char *buffer, + int maxBytes); + int ecbAssignEncoder(pECB self, int encoder, int motorNumber); + int ecbLoadEncoder(pECB self); + + + + + +/*-----------------------------------------------------------------------*/ + + int MakeECB(SConnection *pCon, SicsInterp *pSics, + void *pData, + int ragc, char *argv[]); + + +/*---------------------------------------------------------------------- + for byte packing. result must be an 32 bit integer +----------------------------------------------------------------------*/ +typedef union /* Used to extract and load data to Z80 regs. */{ + unsigned int result; + struct + { + unsigned char byt0; /* Least significant byte */ + unsigned char byt1; + unsigned char byt2; + unsigned char byt3; /* Most significant byte */ + }b; +}Ecb_pack; + +#endif diff --git a/ecb.i b/ecb.i new file mode 100644 index 0000000..6853f58 --- /dev/null +++ b/ecb.i @@ -0,0 +1,23 @@ + +/*----------------------------------------------------------------------- + The ECB is a rack controller from Risoe based on a Z80 processor. + This module provides some functions for communicating with such a + device. This is an internal data structure definition file. + + copyright: see file COPYRIGHT + + Mark Koennecke, January 2002, with some bits taken out of the + original tascom code. +------------------------------------------------------------------------*/ + + struct __ECB { + pObjectDescriptor pDes; + pGPIB gpib; + int boardNumber; + int ecbAddress; + int ecbDeviceID; + int lastError; + int encoder[3]; + int encoderDirty; + }ECB; + diff --git a/ecb.w b/ecb.w new file mode 100644 index 0000000..ae471d9 --- /dev/null +++ b/ecb.w @@ -0,0 +1,173 @@ +\subsection{The ECB Controller} +The ECB Controller is an electronic device created by the Risoe +neutron scattering institute. At its base is a Z80 8-bit +processor. This Z80 processor can perform certain functions such as +controlling count operations, running a motor etc. To this purpose +further electronic widgets are connected to the Z80's backplane. At +the other end is a GPIB controller which allows to discuss with the +Z80. + +This module now implements three basic functionalities of the ECB: +\begin{itemize} +\item Execute a function +\item Read some memory +\item Write some memory +\end{itemize} + +This module also takes care of the encoder assignment for the ECB. The +ECB can have up to three encoders which can be assigned to motors. As +a single motor driver does not know about the assignments of the other +motors, the task of encoder assignement is handled in this module. + +WARNING: this module contains code which may be endian dependend! + +In order to do this we need the following data structure: +@d ecbdat @{ + struct __ECB { + pObjectDescriptor pDes; + pGPIB gpib; + int boardNumber; + int ecbAddress; + int ecbDeviceID; + int lastError; + int encoder[3]; + int encoderDirty; + }ECB; +@} +The fields: +\begin{description} +\item[pDes] The standard SICS object descriptor. +\item[gpib] The GPIB controller used for accessing the ECB. +\item[boardNumber] The GPIB board number in the NI driver. +\item[ecbAddress] The GPIB address of the ECB controller. +\item[ecbDeviceID] The device ID assigned to the ECB when the ECB has +been attached to. +\item[lastError] The last error which occurred. +\item[encoder] An array holding the motor numbers assigned to the +three encoder. +\item[encoderDirty] is a flag which is set to true if a download of +the encoder assignments is necessary. +\end{description} + +A function in the ECB is executed by sending a function number first, +followed by the content of the Z80 4 registers. In order to do this a +data structure is required for these registers: + +@d z80 @{ +typedef struct { + unsigned char d; /* D register in Z80 */ + unsigned char e; /* E register in Z80 */ + unsigned char b; /* B register in Z80 */ + unsigned char c; /* C register in Z80 */ + } Z80_reg; +@} + +The function interface then looks like: + +@d ecbfunc @{ + typedef struct __ECB *pECB; + + int ecbExecute(pECB self, int func, Z80_reg in, Z80_reg *out); + int ecbRead(pECB self, unsigned short address, + void *buffer, int byteCount); + int ecbWrite(pECB self, unsigned short address, + void *buffer, int byteCount); + int ecbDMARead(pECB self, unsigned short address, void *buffer, + unsigned short byteCount); + void ecbClear(pECB self); + int fixECBError(pECB self); + void ecbErrorDescription(pECB self, char *buffer, + int maxBytes); + int ecbAssignEncoder(pECB self, int encoder, int motorNumber); + int ecbLoadEncoder(pECB self); + + + + +@} +\begin{description} +\item[ecbExecute] tries to execute the ECB function func. The input register +content is in in, on success the outpt registers are stored in out. +\item[ecbRead] reads byteCount bytes from the ECB address address into +buffer. Please note that address in this contest is an address in the +ECB's memory space and not the GPIB address. +\item[ecbDMARead] reads byteCount bytes from the ECB DMA address address into +buffer. Please note that address in this contest is an address in the +ECB's memory space and not the GPIB address. +\item[ecbWrite] writes byteCount bytes from buffer to the ECB address +address. Please note that address in this contest is an address in the +ECB's memory space and not the GPIB address. +\item[ecbClear] tries to clear the ECB interface. +\item[fixECBError] tries to fix the last ECB error. +\item[ecbErrorDescription] retrieves a text description of the last +ECB problem. Max maxBytes of description are copied into buffer. +\item[assignEncoder] assigns an encoder to a motor number. +\item[loadEncoder] downloads the encoder assignment to the ECB if necessary. +\end{description} + + +There is also an interface to the SICS interpreter for the ECB. This +can be useful for debugging and testing and as a tool for scriptingy +auxiliary equipment controlled through the ECB. The interface to the +SICS interpreter for the ECB is represented through the ECB Factory +function: +@d ecbint @{ + int MakeECB(SConnection *pCon, SicsInterp *pSics, + void *pData, + int ragc, char *argv[]); +@} + +@o ecb.h @{ +/*----------------------------------------------------------------------- + The ECB is a rack controller from Risoe based on a Z80 processor. + This module provides some functions for communicating with such a + device. + + WARNING: This contains code which may be endian dependent! + + copyright: see file COPYRIGHT + + Mark Koennecke, January 2002, with some bits taken out of the + original tascom code. +------------------------------------------------------------------------*/ +#ifndef ECBCON +#define ECBCON +#include "gpibcontroller.h" + +@ +/*-----------------------------------------------------------------------*/ +@ +/*-----------------------------------------------------------------------*/ +@ + +/*---------------------------------------------------------------------- + for byte packing. result must be an 32 bit integer +----------------------------------------------------------------------*/ +typedef union /* Used to extract and load data to Z80 regs. */{ + unsigned int result; + struct + { + unsigned char byt0; /* Least significant byte */ + unsigned char byt1; + unsigned char byt2; + unsigned char byt3; /* Most significant byte */ + }b; +}Ecb_pack; + +#endif +@} + +@o ecb.i @{ +/*----------------------------------------------------------------------- + The ECB is a rack controller from Risoe based on a Z80 processor. + This module provides some functions for communicating with such a + device. This is an internal data structure definition file. + + copyright: see file COPYRIGHT + + Mark Koennecke, January 2002, with some bits taken out of the + original tascom code. +------------------------------------------------------------------------*/ +@ +@} + diff --git a/ecbdriv.c b/ecbdriv.c new file mode 100644 index 0000000..09bac5e --- /dev/null +++ b/ecbdriv.c @@ -0,0 +1,1261 @@ +/*------------------------------------------------------------------------ + this is a motor driver for the Risoe motor controllers within the + ECB system. The motor is controlled through functions invoked in the + Z80 processor of the ECB system which is connected through a GPIB + bus to the wider world. This driver has to do a lot of extra things: + - it has to convert from physical values to motor steps. + - Quite a few parameters, such as ramping parameters, + have to be downloaded to the ECB + - Risoe motors may have a virtual encoder or a real encoder. + - The motor may have to control air cushions as well. + - Tricky backlash handling. Backlash handling ensures that a position is + always arrived at from a defined direction. If backlash is applied + a restart flag is set in ECBRunTo. ECBGetStatus checks for that and + causes the motor to drive back to the position actually desired. + + This driver support only P2048a motor controllers, as these are the + only ones which seem to have arrived at PSI. The P1648 and Tridynamic + things are not supported. + + Multiplexed motors: Originally the ECB supported 24 motors. This did + prove to be not enough. Therefore another device called P2234e was + introduced which allowed to run 8 motors from one controller port. In this + case the motor parameters have to be copied to the ECB before + driving the motor. Multiplexing is selected through the parameter MULT. + MULT 0 means no multiplexing, MULT > 0 makes MULT the number of the + motor in the multiplexer. MULT is now also used to flag a download of + parameters to the ECB. In such a case MULT is -1. + + + Some of this code was taken from the tascom driver for the ECB. + + copyright: see file COPYRIGHT + + Mark Koennecke, January 2003 + +--------------------------------------------------------------------------*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ecb.h" + +/*------------------------------------------------------------------------ +Parameter indexes in ObPar array and meanings +-------------------------------------------------------------------------*/ +#define ENCODER 0 /* encoder number, 0 if no encoder */ +#define CONTROL 1 /* control signals, > 1 means required. */ +#define RANGE 2 /* 0 = slow, 1 = fast */ +#define MULT 3 /* 0 = not multiplexed, > 0 multiplex motor number*/ +#define MULTCHAN 16 /* multiplexer channel */ +#define ACCTIME 4 /* acceleration time: 500, 1000 or 2000 milSecs */ +#define ROTDIR 5 /* rotation direction */ +#define STARTSPEED 6 /* start speed: 100-500 steps/s */ +#define MAXSPEED 7 /* maximum speed: 100-2000 steps/sec */ +#define SLOWAUTO 8 /* slow speed in auto mode */ +#define SLOWMAN 9 /* slow speed in manual mode */ +#define DELAY 10 /* start delay 0 - 2500 millSecs */ +#define OFFSET 11 /* encoder offset */ +#define TOLERANCE 12 /* tolerance in steps */ +#define STEPS2DEG 13 /* conversion factor motor steps to Degree */ +#define DEG2STEP 14 /* conversion factor from degree to encoder digits */ +#define BACKLASH 15 /* motor backlash */ +#define PORT 17 /* ECB port when multiplexed */ + +#define MAXPAR 19 /* 1 extra for the sentinel, do not forget to initialize! */ + +/*------------------------------ ECB defines -------------------------*/ +#define MAX_ENCODER 40 +#define FENCOR 167 /* read encoder */ +#define MOREAD 145 /* read motor steps */ +#define MOPARA 140 /* motor parameter */ +#define MOCLOA 146 +#define ABS(x) (x < 0 ? -(x) : (x)) +#define MOSTEP 141 +#define MOSTAT 144 + +/********************* error codes *************************************/ +#define COMMERROR -300 +#define ECBMANUELL -301 +#define ECBINUSE -302 +#define UNIDENTIFIED -303 +#define ECBINHIBIT -304 +#define ECBRUNNING -305 +#define ECBSTART -306 +#define ECBLIMIT -307 +#define ECBREADERROR -308 +/*================== The Driver data structure ============================*/ + typedef struct __ECBMotorDriv { + /* general motor driver interface + fields. REQUIRED! + */ + float fUpper; /* upper limit */ + float fLower; /* lower limit */ + char *name; + int (*GetPosition)(void *self,float *fPos); + int (*RunTo)(void *self, float fNewVal); + int (*GetStatus)(void *self); + void (*GetError)(void *self, int *iCode, + char *buffer, int iBufLen); + int (*TryAndFixIt)(void *self,int iError, + float fNew); + int (*Halt)(void *self); + int (*GetDriverPar)(void *self, char *name, + float *value); + int (*SetDriverPar)(void *self,SConnection *pCon, + char *name, float newValue); + void (*ListDriverPar)(void *self, char *motorName, + SConnection *pCon); + void (*KillPrivate)(void *self); + + /* ECB specific fields */ + pECB ecb; /* ECB controller for everything */ + int ecbIndex; /* motor index in ECB */ + int errorCode; + int restart; /* flag if we have to restart + because of backlash + compensation + */ + float restartTarget; /* target to restart to */ + ObPar driverPar[MAXPAR]; /* parameters */ + } ECBMOTDriv, *pECBMotDriv; +/*======================================================================= + Reading the motor position means reading the encoder if such a thing + is present or the counted motor steps (Pseudo Encoder) if not. + If the ECB answers us, the value has to be converted to physical + values. + ----------------------------------------------------------------------*/ +static int readEncoder(pECBMotDriv self, long *digits){ + int status; + Z80_reg in, out; + Ecb_pack data; + + in.c = (unsigned char)ObVal(self->driverPar,ENCODER) + MAX_ENCODER; + status = ecbExecute(self->ecb,FENCOR,in,&out); + if(!status){ + self->errorCode = COMMERROR; + return status; + } + + /* pack bytes */ + data.b.byt3 = 0; + data.b.byt2 = out.b; + data.b.byt1 = out.d; + data.b.byt0 = out.e; + if(out.c != 1){ + *digits = -data.result; + } else { + *digits = data.result; + } + return OKOK; +} +/*---------------------------------------------------------------------*/ +static int readPseudoEncoder(pECBMotDriv self, long *digits){ + int status; + Z80_reg in, out; + Ecb_pack data; + + in.c = (unsigned char)self->ecbIndex; + status = ecbExecute(self->ecb,MOREAD,in,&out); + if(!status){ + self->errorCode = COMMERROR; + return status; + } + + /* pack bytes */ + data.b.byt3 = 0; + data.b.byt2 = out.b; + data.b.byt1 = out.d; + data.b.byt0 = out.e; + if(out.c != 1){ + *digits = -data.result; + } else { + *digits = data.result; + } + return OKOK; +} +/*----------------------------------------------------------------------*/ +int ECBMOTGetPos(void *pData, float *fPos){ + pECBMotDriv self = (pECBMotDriv)pData; + long digits = 0; + int status; + double step2degree; + + assert(self); + self->errorCode = 0; + + if((int)ObVal(self->driverPar,ENCODER) > 0){ + status = readEncoder(self, &digits); + *fPos = digits/ObVal(self->driverPar,DEG2STEP) - + ObVal(self->driverPar,OFFSET); + return status; + } else { + status = readPseudoEncoder(self, &digits); + } + step2degree = ObVal(self->driverPar,STEPS2DEG); + if(step2degree == 0.0){ + step2degree = 1.; + } + *fPos = (float)( (double)digits/step2degree); + + return status; +} +/*======================================================================== +In order to start a motor we need to do a couple of steps: + - check if the motors parameters have been changed or it is a multiplexed + motor. In each case download the motor parameters. + - the direction of the motor has to be determined, the speed to be + selected etc. + - Then the motor can be started. + ------------------------------------------------------------------------*/ +static int mustDownload(pECBMotDriv self){ + int multi; + + multi = (int)rint(ObVal(self->driverPar,MULT)); + if(multi > 0 || multi < 0) { + return 1; + } else { + return 0; + } +} +/*--------------------------------------------------------------------*/ +static int checkMotorResult(pECBMotDriv self, Z80_reg out){ + /* + checks the return values from a motor function invocation + and sets error codes in case of problems. + */ + if(out.c == '\0'){ + switch(out.b){ + case 128: + self->errorCode = ECBMANUELL; + break; + case 64: + self->errorCode = ECBINHIBIT; + break; + case 32: + self->errorCode = ECBRUNNING; + break; + case 1: + self->errorCode = ECBSTART; + break; + case 16: + self->errorCode = ECBLIMIT; + break; + case 4: + self->errorCode = ECBINUSE; + break; + default: + self->errorCode = UNIDENTIFIED; + break; + } + return 0; + } else { + return 1; + } +} +/*---------------------------------------------------------------------*/ +static int loadAcceleration(pECBMotDriv self){ + unsigned char parameter; + Z80_reg in, out; + int accel, status; + + accel = (int)rint(ObVal(self->driverPar,ACCTIME)); + if(accel == 500){ + parameter = 1; + }else if(accel == 1000){ + parameter = 2; + }else if(accel == 2000){ + parameter = 3; + } else { + parameter = 0; + } + /* + apply rotation direction mask + */ + if(ObVal(self->driverPar,ROTDIR) < 0){ + parameter += 128; + } + in.c = (unsigned char)self->ecbIndex; + in.b = 7; + in.e = parameter; + in.d = 0; + out.d = out.e = out.b = out.c = 0; + status = ecbExecute(self->ecb,MOPARA,in,&out); + if(!status){ + self->errorCode = COMMERROR; + return 0; + } + + if(!checkMotorResult(self, out)){ + return 0; + } + + return 1; +} +/*--------------------------- speed tables ------------------------------*/ +#define SPEED_TAB3 64 /* Size of speed table */ +const unsigned int low_2048[SPEED_TAB3] = { + 1, 2, 3, 4, 5, 6, 8, 10, 12, 14, + 16, 20, 24, 28, 32, 36, 40, 44, 48, 56, + 64, 72, 80, 88, 96,104,112,120,128,136, + 144,152,160,168,176,184,192,200,208,216, + 224,236,248,260,272,284,296,308,320,332, + 344,356,368,380,392,404,416,428,440,452, + 464,476,488,500 }; + +#define SPEED_TAB4 96 /* Size of speed table */ +const unsigned int high_2048[SPEED_TAB4] = { + 11, 15, 20, 27, 36, 47, 59, 74, + 93, 107, 124, 143, 165, 190, 213, 239, + 268, 298, 331, 368, 405, 446, 491, 536, + 585, 632, 683, 731, 783, 827, 873, 922, + 974, 1028, 1085, 1146, 1211, 1278, 1349, 1424, + 1503, 1587, 1675, 1720, 1820, 1913, 2014, 2123, + 2237, 2360, 2483, 2620, 2755, 2905, 3058, 3221, + 3384, 3575, 3756, 3945, 4150, 4370, 4600, 4800, + 5000, 5250, 5533, 5822, 6120, 6440, 6770, 7090, + 7450, 7800, 8130, 8500, 8900, 9320, 9730, 10200, + 10700, 11200, 11700, 12200, 12800, 13300, 13900, 14500, + 15100, 15800, 16700, 17300, 18000, 18600, 19300, 20000 }; +/*---------------------------------------------------------------------*/ +static unsigned char getSpeedIndex(float value, + int range, int *actualValue ){ + unsigned char index; + const unsigned int *table; + int length; + + if(range == 0){ + table = low_2048; + length = SPEED_TAB3; + } else { + table = high_2048; + length = SPEED_TAB4; + } + + for(index = 0; index < length-1; index++){ + if(table[index] >= value){ + break; + } + } + *actualValue = table[index]; + return index; +} +/*--------------------------------------------------------------------*/ +static int loadSpeed(pECBMotDriv self, float value, int code){ + unsigned char parameter; + Z80_reg in, out; + int accel, status, actual; + + parameter = getSpeedIndex(value, (int)rint(ObVal(self->driverPar,RANGE)), + &actual); + + in.c = (unsigned char)self->ecbIndex; + in.b = code; + in.e = parameter; + status = ecbExecute(self->ecb,MOPARA,in,&out); + if(!status){ + self->errorCode = COMMERROR; + return 0; + } + if(!checkMotorResult(self, out)){ + return 0; + } + return 1; +} +/*-------------------------------------------------------------------*/ +static int loadDelay(pECBMotDriv self){ + int parameter; + Z80_reg in, out; + int accel, status; + unsigned char control; + + parameter = (int)rint(ObVal(self->driverPar,DELAY)); + control = (unsigned char)rint(ObVal(self->driverPar,CONTROL)); + if(control & 3){ + parameter = 5; + } else{ + parameter/= 10; + } + in.c = (unsigned char)self->ecbIndex; + in.b = 8; + in.e = parameter; + status = ecbExecute(self->ecb,MOPARA,in,&out); + if(!status){ + self->errorCode = COMMERROR; + return 0; + } + + if(!checkMotorResult(self, out)){ + return 0; + } + + return 1; +} +/*---------------------------------------------------------------------*/ +static int loadMulti(pECBMotDriv self){ + int multi, mult_chan; + Z80_reg in, out; + int status; + + multi = rint(ObVal(self->driverPar,MULT)); + if(multi <= 0){ + return 1; /* not multiplexed */ + } + + mult_chan = (unsigned char)rint(ObVal(self->driverPar,MULTCHAN)); + in.b = -1; /* SET_PORT */ + in.d = (unsigned char)(multi + (mult_chan << 4)); + in.e = (unsigned char)rint(ObVal(self->driverPar,PORT)); + in.c = self->ecbIndex; + status = ecbExecute(self->ecb,MOPARA,in,&out); + if(status != 1){ + self->errorCode = COMMERROR; + return 0; + } + return 1; +} +/*------------------------------------------------------------------*/ +static int loadOffset(pECBMotDriv self, float offset){ + Z80_reg in, out; + int status; + Ecb_pack data; + + /* + ignored + */ + if(ObVal(self->driverPar,ENCODER) <=.0){ + return 1; + } + + data.result = offset * ObVal(self->driverPar,STEPS2DEG); + in.b = data.b.byt2; + in.d = data.b.byt1; + in.e = data.b.byt0; + in.c = (unsigned char)rint(ObVal(self->driverPar,ENCODER)); + + status = ecbExecute(self->ecb,168,in,&out); + if(status == 1){ + self->driverPar[OFFSET].fVal = offset; + } else { + self->errorCode = COMMERROR; + } + return status; +} +/*--------------------------------------------------------------------- + This loads the gearing parameters for the CRT display. This should + not have any influence on the running of the motor + ------------------------------------------------------------------------*/ +static double To_ten(int v) { + double vv; + + + vv = 1.0; + if (v == 1) + vv = 10.0; + if (v == 2) + vv = 100.0; + if (v == 3) + vv = 1000.0; + if (v == 4) + vv = 10000.0; + if (v == 5) + vv = 100000.0; + if (v == 6) + vv = 1000000.0; + if (v == 7) + vv = 10000000.0; + return (vv); +} +/*----------------------------------------------------------------------*/ +static int loadGearing(pECBMotDriv self){ + int status; + double dgear; + int gdec, dec = 0, ratio; + Ecb_pack data; + Z80_reg in, out; + + in.c = self->ecbIndex; + dgear = (double) ObVal(self->driverPar,STEPS2DEG);; + + /* Calculate decimals in display and gearing ratio for the ECB system*/ + gdec = (int) (1.0 + (log10(dgear - .01))); + if (dec < gdec) + dec = gdec; /* Display does not work with decimals < gdec */ + ratio = (long) (0.5 + dgear*To_ten(6 + 1 - dec)); + + data.result = ratio; + in.b = data.b.byt2; + in.d = data.b.byt1; + in.e = data.b.byt0; + status = ecbExecute(self->ecb,174,in,&out); + if(status != 1){ + self->errorCode = COMMERROR; + } + + if(ObVal(self->driverPar,ENCODER) == 0){ + in.b = self->ecbIndex; + } else { + in.b = 1; + in.e = (unsigned char)ObVal(self->driverPar,ENCODER); + } + in.d = 0; + in.e = dec; + status = ecbExecute(self->ecb,173,in,&out); + if(status != 1){ + self->errorCode = COMMERROR; + } + + return 1; +} +/*----------------------------------------------------------------------*/ +static int downloadECBParam(pECBMotDriv self){ + int status, parameter; + unsigned char func_code; + Z80_reg in, out; + + /* + We assume that all parameters have useful values. It is the task of + SetDriverPar to ensure just that! + */ + if(status = loadAcceleration(self) <= 0){ + return 0; + } + + if(status = loadSpeed(self,ObVal(self->driverPar,STARTSPEED),6) <= 0){ + return 0; + } + + if(status = loadSpeed(self,ObVal(self->driverPar,MAXSPEED),5) <= 0){ + return 0; + } + + if(status = loadSpeed(self,ObVal(self->driverPar,SLOWAUTO),4) <= 0){ + return 0; + } + + if(status = loadSpeed(self,ObVal(self->driverPar,SLOWMAN),10) <= 0){ + return 0; + } + + if(status = loadDelay(self) <= 0){ + return 0; + } + + if(status = loadMulti(self) <= 0){ + return 0; + } + + if(status = ecbLoadEncoder(self->ecb) <= 0){ + return 0; + } + + if(status = loadOffset(self,ObVal(self->driverPar,OFFSET)) <= 0){ + return 0; + } + + + /* + It would be good practice to read the parameters written back + in order to check them. This does not seem to be supported with the + ECB system though. + */ + if(ObVal(self->driverPar,MULT) < 0.){ + self->driverPar[MULT].fVal = .0; + } + + if(status = loadGearing(self) <= 0){ + return 0; + } + + return 1; +} +/*--------------------------------------------------------------------*/ +int degree2Step(pECBMotDriv self, float degree) +{ + double steps; + + steps = degree*ObVal(self->driverPar,STEPS2DEG); + if (ObVal(self->driverPar,ENCODER) > .0) + steps = steps*ObVal(self->driverPar,DEG2STEP); + if(degree < 0){ + steps = - steps; + } + return ((int) steps); +} +/*---------------------------------------------------------------------- + controlMotor enables or disables the motor, according to flag enable. + This is also used to switch on air cushions and the like. + ------------------------------------------------------------------------*/ +static int controlMotor(pECBMotDriv self, int enable){ + int status, delay, control; + Z80_reg in, out; + + /* + nothing to do if we are not in control + */ + control = (int)rint(ObVal(self->driverPar,CONTROL)); + if(!(control & 1)){ + return 1; + } + + delay = (int)rint(ObVal(self->driverPar,DELAY)); + if(enable == 1){ + /* + enabling + */ + in.e = 12; /* 8 + 4 */ + in.b = 11; /* set control signal */ + in.c = (unsigned char)self->ecbIndex; + status = ecbExecute(self->ecb,MOPARA,in,&out); + if(status != 1){ + self->errorCode = COMMERROR; + return 0; + } + /* + wait for air cushions to settle + */ + usleep(delay); + return 1; + }else { + /* + disable motor + */ + in.e = 8; + in.b = 11; /* set control signal */ + in.c = self->ecbIndex; + status = ecbExecute(self->ecb,MOPARA,in,&out); + if(status != 1){ + self->errorCode = COMMERROR; + return 0; + } + usleep(delay); + in.e = 0; + in.b = 11; /* set control signal */ + in.c = -self->ecbIndex; + status = ecbExecute(self->ecb,MOPARA,in,&out); + if(status != 1){ + self->errorCode = COMMERROR; + return 0; + } + usleep(delay); + return 1; + } +} +/*-----------------------------------------------------------------------*/ +static int ECBRunTo(void *pData, float newPosition){ + pECBMotDriv self = (pECBMotDriv)pData; + long digits = 0; + int status; + float oldValue, diff, steps2degree, backlash; + Ecb_pack data; + Z80_reg in, out; + + assert(self); + + if(mustDownload(self)){ + status = downloadECBParam(self); + if(!status){ + return 0; + } + } + + /* + read old position + */ + status = ECBMOTGetPos(self,&oldValue); + if(status != 1){ + return status; + } + + /* + do not start if there + */ + diff = newPosition - oldValue; + steps2degree= ObVal(self->driverPar,STEPS2DEG); + if(ABS(diff) <= .5/steps2degree + ObVal(self->driverPar,TOLERANCE)){ + return OKOK; + } + + /* + save restartTarget for backlash handling + */ + self->restartTarget = newPosition; + + /* + enable and push up airy cushions + */ + status = controlMotor(self,1); + if(status != 1){ + return status; + } + + + /* + write control data + */ + in.d = 0; + if(diff > .0){ + in.d |= 32; /* positive direction */ + } + in.d |= 16; /* interrupts */ + if(rint(ObVal(self->driverPar,RANGE)) == 1.){ + in.d |= 64; /* fast speed */ + } + in.c = (unsigned char)self->ecbIndex; + status = ecbExecute(self->ecb,MOCLOA,in,&out); + if(status != 1){ + self->errorCode = COMMERROR; + return 0; + } + + /* + calculate steps + */ + self->restart = 0; + backlash = ObVal(self->driverPar,BACKLASH); + if(diff < 0){ + diff = -diff; + if(backlash > 0.){ + diff += backlash; + self->restart = 1; + } + } else { + if(backlash < 0.){ + diff -= backlash; + self->restart = 1; + } + } + data.result = degree2Step(self,diff); + + /* + finally start the motor + */ + in.b = data.b.byt2; + in.d = data.b.byt1; + in.e = data.b.byt0; + in.c = (unsigned char)self->ecbIndex; + status = ecbExecute(self->ecb,MOSTEP,in,&out); + if(status != 1){ + self->errorCode = COMMERROR; + return 0; + } + + if(!checkMotorResult(self, out)){ + return 0; + } + + return OKOK; +} +/*=======================================================================*/ +static int checkStatusResponse(pECBMotDriv self, Z80_reg out){ + + if(out.c == '\0'){ + if(out.b & 4) { + self->errorCode = ECBINUSE; + } else { + self->errorCode = ECBREADERROR; + } + return HWFault; + } + + if(out.b & 128){ + self->errorCode = ECBMANUELL; + return HWFault; + } else if(out.b & 32){ + return HWBusy; + } else if(out.b & 16){ + self->errorCode = ECBLIMIT; + return HWFault; + } + return HWIdle; +} +/*----------------------------------------------------------------------*/ +static int ECBGetStatus(void *pData){ + pECBMotDriv self = (pECBMotDriv)pData; + Z80_reg in, out; + int status, result; + + assert(self); + + in.c = (unsigned char)self->ecbIndex; + in.b = 12; + status = ecbExecute(self->ecb,MOSTAT,in,&out); + if(status != 1){ + self->errorCode = COMMERROR; + return HWFault; + } + + result = checkStatusResponse(self,out); + if(result == HWFault || result == HWIdle){ + /* + run down airy cushions ........ + */ + controlMotor(self,0); + } + + /* + take care of backlash..... + */ + if(result == HWIdle && self->restart == 1){ + self->restart = 0; + ECBRunTo(self,self->restartTarget); + return HWBusy; + } + + return result; +} +/*======================================================================*/ +static void ECBGetError(void *pData, int *iCode, char *buffer, int bufferlen){ + pECBMotDriv self = (pECBMotDriv)pData; + char pBueffel[132]; + + assert(self); + + *iCode = self->errorCode; + switch(self->errorCode){ + case COMMERROR: + strncpy(buffer,"communication problem with ECB",bufferlen); + break; + case ECBMANUELL: + strncpy(buffer,"ECB is in manual mode, trying to switch...",bufferlen); + break; + case ECBINUSE: + strncpy(buffer,"Power supply is in use",bufferlen); + break; + case ECBINHIBIT: + strncpy(buffer,"motor is inhibited",bufferlen); + break; + case ECBRUNNING: + strncpy(buffer,"motor is running",bufferlen); + break; + case ECBSTART: + strncpy(buffer,"failed to start motor",bufferlen); + break; + case ECBLIMIT: + strncpy(buffer,"hit limit switch",bufferlen); + break; + default: + strncpy(buffer,"unidentified error code",bufferlen); + break; + } +} +/*=======================================================================*/ +static int ECBTryAndFixIt(void *pData, int iCode, float fNew){ + pECBMotDriv self = (pECBMotDriv)pData; + int result; + Z80_reg in, out; + + assert(self); + + switch(iCode){ + case ECBMANUELL: + in.d = 1 ; + ecbExecute(self->ecb,162,in,&out); + result = MOTREDO; + break; + case COMMERROR: + ecbClear(self->ecb); + result = MOTREDO; + break; + default: + result = MOTFAIL; + break; + } + return result; +} +/*========================================================================*/ +static int ECBHalt(void *pData){ + pECBMotDriv self = (pECBMotDriv)pData; + Z80_reg in, out; + unsigned char par = 2; + + assert(self); + + if(rint(ObVal(self->driverPar,RANGE)) == 1){ + par |= 64; + } + + in.b = 9; + in.e = par; + in.c = (unsigned char)self->ecbIndex; + ecbExecute(self->ecb,MOPARA,in,&out); + self->restart = 0; + return 1; +} +/*=======================================================================*/ +static int ECBGetDriverPar(void *pData,char *name, float *value){ + pECBMotDriv self = (pECBMotDriv)pData; + ObPar *par = NULL; + + assert(self); + + par = ObParFind(self->driverPar,name); + if(par != NULL){ + *value = par->fVal; + return 1; + } else { + return 0; + } +} +/*=====================================================================*/ +static float fixAccTime(float newValue){ + float corrected, min, diff; + int val, possibleValues[4] = { 500, 1000, 2000, 5000}, i; + + val = (int)rint(newValue); + min = 9999999.99; + for(i = 0; i < 4; i++){ + diff = val - possibleValues[i]; + if(ABS(diff) < min){ + min = ABS(diff); + corrected = possibleValues[i]; + } + } + return corrected; +} +/*--------------------------------------------------------------------*/ +static void setDownloadFlag(pECBMotDriv self, int parNumber){ + int mustDownload; + + switch(parNumber){ + case CONTROL: + case MULT: + case MULTCHAN: + case ACCTIME: + case ROTDIR: + case STARTSPEED: + case MAXSPEED: + case SLOWAUTO: + case SLOWMAN: + case DELAY: + mustDownload = 1; + break; + default: + mustDownload = 0; + break; + } + + if(mustDownload && (self->driverPar[MULT].fVal == 0)){ + self->driverPar[MULT].fVal = -1.0; + } +} +/*--------------------------------------------------------------------*/ +static int putMotorPosition(pECBMotDriv self, float newValue){ + Z80_reg in,out; + Ecb_pack data; + float oldPos; + int status; + + if(ABS(ObVal(self->driverPar,ENCODER)) > .1){ + status = ECBMOTGetPos(self,&oldPos); + if(status != 1){ + return status; + } + return loadOffset(self,oldPos - newValue); + } else { + data.result = newValue*ObVal(self->driverPar,STEPS2DEG); + in.b = data.b.byt2; + in.d = data.b.byt1; + in.e = data.b.byt0; + in.c = (unsigned char)self->ecbIndex; + status = ecbExecute(self->ecb,142,in,&out); + if(status != 1){ + self->errorCode = COMMERROR; + return 0; + } + if(!checkMotorResult(self, out)){ + return 0; + } + } + + return 1; +} +/*---------------------------------------------------------------------*/ +static int ECBSetDriverPar(void *pData, SConnection *pCon, char *name, + float newValue){ + pECBMotDriv self = (pECBMotDriv)pData; + int parNumber, speedNumber, actualSpeed, status; + char pBueffel[256]; + float correctedValue; + + assert(self); + + + /* + only managers shall edit these parameters.... + */ + if(!SCMatchRights(pCon,usMugger)){ + return 0; + } + + /* + this is rather a command and forces a parameter download + to the ECB + */ + if(strcmp(name,"download") == 0){ + status = downloadECBParam(self); + if(status != 1){ + ECBGetError(self,&actualSpeed, pBueffel,254); + SCWrite(pCon,pBueffel,eError); + return status; + } + } + + /* + this is another command and assigns a position to the current + motor place + */ + if(strcmp(name,"putpos") == 0){ + status = putMotorPosition(self,newValue); + if(status != 1){ + ECBGetError(self,&actualSpeed, pBueffel,254); + SCWrite(pCon,pBueffel,eError); + return status; + } + } + + /* + get the parameter number + */ + parNumber = ObParIndex(self->driverPar,name); + if(parNumber < 0){ + return 0; + } + + /* + make these parameters right, at least as far as we can ....... + */ + switch(parNumber){ + case ACCTIME: + correctedValue = fixAccTime(newValue); + break; + case STARTSPEED: + getSpeedIndex(rint(newValue),1,&actualSpeed); + correctedValue = actualSpeed; + if(correctedValue < 10){ + correctedValue = 10; + } + if(correctedValue > 4400){ + correctedValue = 4400; + } + break; + case MAXSPEED: + getSpeedIndex(rint(newValue),1,&actualSpeed); + correctedValue = actualSpeed; + break; + case SLOWAUTO: + case SLOWMAN: + getSpeedIndex(rint(newValue),0,&actualSpeed); + correctedValue = actualSpeed; + if(correctedValue > 500){ + correctedValue = 500; + } + break; + case DELAY: + correctedValue = newValue; + if(correctedValue > 2500){ + correctedValue = 2500; + } + break; + case RANGE: + correctedValue = newValue; + if(correctedValue != 0.0 && correctedValue != 1.0){ + correctedValue = .0; /* slow by default! */ + } + break; + case ENCODER: + if(newValue < 0. || newValue > 3.){ + SCWrite(pCon,"ERROR: encoder numbers can only be 0 - 3", eError); + return 0; + } else if(newValue == 0){ + correctedValue = newValue; + } else { + ecbAssignEncoder(self->ecb,(int)newValue, self->ecbIndex); + correctedValue = newValue; + } + break; + case STEPS2DEG: + case DEG2STEP: + if(ABS(newValue) < .1){ + correctedValue = 1.; + } else { + correctedValue = newValue; + } + break; + case OFFSET: + correctedValue = newValue; + break; + default: + correctedValue = newValue; + break; + } + + if(ABS(correctedValue - newValue) > 0.){ + sprintf(pBueffel,"WARNING: Illegal value %6.2f verbosely coerced to %6.2f", + newValue,correctedValue); + SCWrite(pCon,pBueffel,eWarning); + } + + ObParSet(self->driverPar,self->name,name,correctedValue,pCon); + + setDownloadFlag(self,parNumber); + + return 1; +} +/*=========================================================================*/ +static void ECBListPar(void *pData, char *motorName, SConnection *pCon){ + pECBMotDriv self = (pECBMotDriv)pData; + char pBueffel[256]; + int i; + + assert(self); + + for(i = 0; i < MAXPAR-1; i++){ + sprintf(pBueffel,"%s.%s = %f", + motorName,self->driverPar[i].name, + self->driverPar[i].fVal); + SCWrite(pCon,pBueffel,eValue); + } +} +/*========================================================================*/ +static int interpretArguments(pECBMotDriv self, SConnection *pCon, + int argc, char *argv[]){ + char pBueffel[256]; + TokenList *pList, *pCurrent; + + pList = SplitArguments(argc,argv); + if(!pList || argc < 4){ + SCWrite(pCon,"ERROR: no arguments to CreateECBMotor",eError); + return 0; + } + pCurrent = pList; + + /* + first should be the name of the ECB to use + */ + if(pCurrent->Type != eText){ + sprintf(pBueffel,"ERROR: expected EDB name, got: %s", + pCurrent->text); + DeleteTokenList(pList); + return 0; + } + self->ecb = (pECB)FindCommandData(pServ->pSics,pCurrent->text,"ECB"); + if(!self->ecb){ + sprintf(pBueffel,"ERROR: %s is no ECB controller",pCurrent->text); + SCWrite(pCon,pBueffel,eError); + DeleteTokenList(pList); + return 0; + } + + /* + next the motor number + */ + pCurrent = pCurrent->pNext; + if(pCurrent->Type != eInt){ + sprintf(pBueffel,"ERROR: expected int motor number, got %s", + pCurrent->text); + SCWrite(pCon,pBueffel,eError); + DeleteTokenList(pList); + return 0; + } + self->ecbIndex = pCurrent->iVal; + + /* + next the limits + */ + pCurrent = pCurrent->pNext; + if(pCurrent->Type != eFloat){ + sprintf(pBueffel,"ERROR: expected float type limit, got %s", + pCurrent->text); + SCWrite(pCon,pBueffel,eError); + DeleteTokenList(pList); + return 0; + } + self->fLower = pCurrent->fVal; + pCurrent = pCurrent->pNext; + if(pCurrent->Type != eFloat){ + sprintf(pBueffel,"ERROR: expected float type limit, got %s", + pCurrent->text); + SCWrite(pCon,pBueffel,eError); + DeleteTokenList(pList); + return 0; + } + self->fUpper = pCurrent->fVal; + DeleteTokenList(pList); + + return 1; +} +/*-----------------------------------------------------------------------*/ +static void initializeParameters(pECBMotDriv self){ + ObParInit(self->driverPar,ENCODER,"encoder",0,usMugger); + ObParInit(self->driverPar,CONTROL,"control",0,usMugger); + ObParInit(self->driverPar,RANGE,"range",1,usMugger); + ObParInit(self->driverPar,MULT,"multi",0,usMugger); + ObParInit(self->driverPar,MULTCHAN,"multchan",0,usMugger); + ObParInit(self->driverPar,ACCTIME,"acceleration",500,usMugger); + ObParInit(self->driverPar,ROTDIR,"rotation_dir",1,usMugger); + ObParInit(self->driverPar,STARTSPEED,"startspeed",100,usMugger); + ObParInit(self->driverPar,MAXSPEED,"maxspeed",2000,usMugger); + ObParInit(self->driverPar,SLOWAUTO,"auto",100,usMugger); + ObParInit(self->driverPar,SLOWMAN,"manuell",100,usMugger); + ObParInit(self->driverPar,DELAY,"delay",50,usMugger); + ObParInit(self->driverPar,OFFSET,"offset",0,usMugger); + ObParInit(self->driverPar,TOLERANCE,"dtolerance",0.,usMugger); + ObParInit(self->driverPar,STEPS2DEG,"step2deg",1,usMugger); + ObParInit(self->driverPar,DEG2STEP,"step2dig",0,usMugger); + ObParInit(self->driverPar,BACKLASH,"backlash",0,usMugger); + ObParInit(self->driverPar,PORT,"port",0,usMugger); + ObParInit(self->driverPar,MAXPAR-1,"tueet",-100,-100); /* sentinel! */ +} +/*=======================================================================*/ +void KillECBMotor(void *pDriver){ + int i; + pECBMotDriv self = (pECBMotDriv)pDriver; + + for(i = 0; i < MAXPAR; i++){ + if(self->driverPar[i].name != NULL){ + free(self->driverPar[i].name); + } + } +} +/*------------------------------------------------------------------------*/ +MotorDriver *CreateECBMotor(SConnection *pCon, int argc, char *argv[]){ + pECBMotDriv self = NULL; + + self = (pECBMotDriv)malloc(sizeof(ECBMOTDriv)); + if(self == NULL){ + return NULL; + } + memset(self,0,sizeof(ECBMOTDriv)); + + if(!interpretArguments(self,pCon,argc,argv)){ + free(self); + return 0; + } + + initializeParameters(self); + + /* + set function pointers + */ + self->GetPosition = ECBMOTGetPos; + self->RunTo = ECBRunTo; + self->GetStatus = ECBGetStatus; + self->GetError = ECBGetError; + self->TryAndFixIt = ECBTryAndFixIt; + self->Halt = ECBHalt; + self->GetDriverPar = ECBGetDriverPar; + self->SetDriverPar = ECBSetDriverPar; + self->ListDriverPar = ECBListPar; + self->KillPrivate = KillECBMotor; + + self->errorCode = 0; + return (MotorDriver *)self; +} diff --git a/ecbdriv.h b/ecbdriv.h new file mode 100644 index 0000000..eda63b5 --- /dev/null +++ b/ecbdriv.h @@ -0,0 +1,45 @@ +/*------------------------------------------------------------------------ + this is a motor driver for the Risoe motor controllers within the + ECB system. The motor is controlled through functions invoked in the + Z80 processor of the ECB system which is connected through a GPIB + bus to the wider world. This driver has to do a lot of extra things: + - it has to convert from physical values to motor steps. + - Quite a few parameters, such as ramping parameters, + have to be downloaded to the ECB + - Risoe motors may have a virtual encoder or a real encoder. + - The motor may have to control air cushions as well. + - Tricky backlash handling. Backlash handling ensures that a position is + always arrived at from a defined direction. If backlash is applied + a restart flag is set in ECBRunTo. ECBGetStatus checks for that and + causes the motor to drive back to the position actually desired. + + This driver support only P2048a motor controllers, as these are the + only ones which seem to have arrived at PSI. The P1648 and Tridynamic + things are not supported. + + Multiplexed motors: Originally the ECB supported 24 motors. This did + prove to be not enough. Therefore another device called P2234e was + introduced which allowed to run 8 motors from one controller port. In this + case the motor parameters have to be copied to the ECB before + driving the motor. Multiplexing is selected through the parameter MULT. + MULT 0 means no multiplexing, MULT > 0 makes MULT the number of the + motor in the multiplexer. MULT is now also used to flag a download of + parameters to the ECB. In such a case MULT is -1. + + + Some of this code was taken from the tascom driver for the ECB. + + copyright: see file COPYRIGHT + + Mark Koennecke, January 2003 + +--------------------------------------------------------------------------*/ +#ifndef ECBDRIV +#define ECBDRIV + +MotorDriver *CreateECBMotor(SConnection *pCon, int argc, char *argv[]); +void KillECBMotor(void *driver); + +#endif + + diff --git a/el734dc.c b/el734dc.c new file mode 100644 index 0000000..291437f --- /dev/null +++ b/el734dc.c @@ -0,0 +1,940 @@ +/*------------------------------------------------------------------------- + A motor driver for EL734 DC motors as used at SinQ + + + Mark Koennecke, November 1996 + + Original code foe EL734 stepper, modified for DC motors, the + 11-June-1997 Mark Koennecke + + Copyright: + + Labor fuer Neutronenstreuung + Paul Scherrer Institut + CH-5423 Villigen-PSI + + + The authors hereby grant permission to use, copy, modify, distribute, + and license this software and its documentation for any purpose, provided + that existing copyright notices are retained in all copies and that this + notice is included verbatim in any distributions. No written agreement, + license, or royalty fee is required for any of the authorized uses. + Modifications to this software may be copyrighted by their authors + and need not follow the licensing terms described here, provided that + the new terms are clearly indicated on the first page of each file where + they apply. + + IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY + FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY + DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE + IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE + NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR + MODIFICATIONS. +------------------------------------------------------------------------------*/ +#include +#include +#include +#include +#include +#include "modriv.h" +#include "hardsup/sinq_prototypes.h" +#include "hardsup/rs232c_def.h" +#include "hardsup/el734_def.h" +#include "hardsup/el734fix.h" +#include +#include +#include + + static int EL734EncodeMSR(char *text, int iLen, + int iMSR, int iOMSR, int iFP, int iFR); + + static int EL734AnalyzeMSR(int iMSR, int iOMSR); + +/* addional error codes for Status-things */ +#define MSRBUSY -40 +#define MSRONLIMIT -41 +#define MSRRUNFAULT -42 +#define MSRPOSFAULT -43 +#define MSRDEADCUSHION -44 +#define MSRHALT -45 +#define MSRSTOP -46 +#define MSROK -47 +#define MSRREF -48 +#define MSRFAULT -49 +/*----------------------------------------------------------------------- + The motor driver structure. Please note that the first set of fields has + be identical with the fields of AbstractModriv in ../modriv.h +------------------------------------------------------------------------*/ + typedef struct __MoDriv { + /* general motor driver interface + fields. REQUIRED! + */ + float fUpper; /* upper limit */ + float fLower; /* lower limit */ + char *name; + int (*GetPosition)(void *self,float *fPos); + int (*RunTo)(void *self, float fNewVal); + int (*GetStatus)(void *self); + void (*GetError)(void *self, int *iCode, char *buffer, int iBufLen); + int (*TryAndFixIt)(void *self,int iError, float fNew); + int (*Halt)(void *self); + int (*GetDriverPar)(void *self, char *name, + float *value); + int (*SetDriverPar)(void *self,SConnection *pCon, + char *name, float newValue); + void (*ListDriverPar)(void *self, char *motorName, + SConnection *pCon); + void (*KillPrivate)(void *self); + + + /* EL-734 specific fields */ + int iPort; + char *hostname; + int iChannel; + int iMotor; + void *EL734struct; + int iMSR; + } EL734Driv; + +/* --------------------------------------------------------------------------*/ + static int GetPos(void *self, float *fData) + { + EL734Driv *pDriv; + float fPos; + int iRet, iMSR, iOMSR, iFRC,iFPC, iSS; + + assert(self); + + pDriv = (EL734Driv *)self; + iRet = EL734_GetStatus(&(pDriv->EL734struct), + &iMSR, + &iOMSR, + &iFPC, + &iFRC, + &iSS, + &fPos); + if(iMSR != 0) + { + pDriv->iMSR = iMSR; + } + *fData = fPos; + if(iRet != 1) + { + return HWFault; + } + else + return OKOK; + + } +/*--------------------------------------------------------------------------*/ + static int Run(void *self, float fNew) + { + EL734Driv *pDriv; + int iRet; + + assert(self); + + pDriv = (EL734Driv *)self; + iRet = EL734_MoveNoWait (&(pDriv->EL734struct), fNew); + if(iRet == 1) + { + return OKOK; + } + else + { + return HWFault; + } + } + +/*--------------------------------------------------------------------------- + + EL734Error2Text converts between an EL734 error code to text +-----------------------------------------------------------------------------*/ + static void EL734Error2Text(char *pBuffer, int iErr) + { + strcpy(pBuffer,"ERROR: HW:"); + switch(iErr) + { + case EL734__BAD_ADR: + strcat(pBuffer,"EL734__BAD_ADR"); + break; + case EL734__BAD_BIND: + strcat(pBuffer,"EL734__BAD_BIND"); + break; + case EL734__BAD_CMD: + strcat(pBuffer,"EL734__BAD_CMD"); + break; + case EL734__BAD_CONNECT: + strcat(pBuffer,"EL734__BAD_CONNECT"); + break; + case EL734__BAD_FLUSH: + strcat(pBuffer,"EL734__BAD_FLUSH"); + break; + case EL734__BAD_HOST: + strcat(pBuffer,"EL734__BAD_HOST"); + break; + case EL734__BAD_ID: + strcat(pBuffer,"EL734__BAD_ID"); + break; + case EL734__BAD_ILLG: + strcat(pBuffer,"EL734__BAD_ILLG"); + break; + case EL734__BAD_LOC: + strcat(pBuffer,"EL734__BAD_LOC"); + break; + case EL734__BAD_MALLOC: + strcat(pBuffer,"EL734__BAD_MALLOC"); + break; + case EL734__BAD_NOT_BCD: + strcat(pBuffer,"EL734__BAD_NOT_BCD"); + break; + case EL734__BAD_OFL: + strcat(pBuffer,"EL734__BAD_OFL"); + break; + case EL734__BAD_PAR: + strcat(pBuffer,"EL734__BAD_PAR"); + break; + + case EL734__BAD_RECV: + strcat(pBuffer,"EL734__BAD_RECV"); + break; + case EL734__BAD_RECV_NET: + strcat(pBuffer,"EL734__BAD_RECV_NET"); + break; + case EL734__BAD_RECV_PIPE: + strcat(pBuffer,"EL734__BAD_RECV_PIPE"); + break; + case EL734__BAD_RECV_UNKN: + strcat(pBuffer,"EL734__BAD_RECV_UNKN"); + break; + case EL734__BAD_RECVLEN: + strcat(pBuffer,"EL734__BAD_RECVLEN"); + break; + case EL734__BAD_RECV1: + strcat(pBuffer,"EL734__BAD_RECV1"); + break; + case EL734__BAD_RECV1_NET: + strcat(pBuffer,"EL734__BAD_RECV1_NET"); + break; + case EL734__BAD_RECV1_PIPE: + strcat(pBuffer,"EL734__BAD_RECV1_PIPE"); + break; + case EL734__BAD_RNG: + strcat(pBuffer,"EL734__BAD_RNG"); + break; + case EL734__BAD_SEND: + strcat(pBuffer,"EL734__BAD_SEND"); + break; + case EL734__BAD_SEND_PIPE: + strcat(pBuffer,"EL734__BAD_SEND_PIPE"); + break; + case EL734__BAD_SEND_NET: + strcat(pBuffer,"EL734__BAD_SEND_NET"); + break; + case EL734__BAD_SEND_UNKN: + strcat(pBuffer,"EL734__BAD_SEND_UNKN"); + break; + case EL734__BAD_SENDLEN: + strcat(pBuffer,"EL734__BAD_SENDLEN"); + break; + case EL734__BAD_SOCKET: + strcat(pBuffer,"EL734__BAD_SOCKET"); + break; + case EL734__BAD_TMO: + strcat(pBuffer,"EL734__BAD_TMO"); + break; + case EL734__FORCED_CLOSED: + strcat(pBuffer,"EL734__FORCED_CLOSED"); + break; + case EL734__BAD_STP: + strcat(pBuffer,"EL734__BAD_STP"); + break; + case EL734__EMERG_STOP: + strcat(pBuffer,"EL734__EMERG_STOP"); + break; + case EL734__NOT_OPEN: + strcat(pBuffer,"EL734__NOT_OPEN"); + break; + case EL734__BAD_ASYNSRV: + strcat(pBuffer,"EL734__BAD_ASYNSRV"); + break; + default: + sprintf(pBuffer,"Unknown EL734 error %d", iErr); + break; + } + } + +/*-------------------------------------------------------------------------*/ + static void GetErr(void *self, int *iCode, char *buffer, int iBufLen) + { + EL734Driv *pDriv; + char pBueffel[512]; + int iMSR, iOMSR, iSS; + int iRet, iFPC, iFRC; + int iErr; + float fPos; + char *pErr; + + assert(self); + + /* get EL734 error codes */ + pDriv = (EL734Driv *)self; + EL734_ErrInfo(&pErr,&iMSR,&iOMSR, &iSS); + if(iMSR != 0) + { + EL734Error2Text(pBueffel,iMSR); + strncpy(buffer,pBueffel,(iBufLen-1)); + *iCode = iMSR; + return; + } + else + { /* check status flag for addional errors */ + iRet = EL734_GetStatus(&(pDriv->EL734struct), + &iMSR, + &iOMSR, + &iFPC, + &iFRC, + &iSS, + &fPos); + if(iRet != 1) + { /* failure on this one, this has to be handled */ + EL734_ErrInfo(&pErr,&iMSR,&iOMSR, &iSS); + EL734Error2Text(pBueffel,iMSR); + strncpy(buffer,pBueffel,(iBufLen-1)); + *iCode = iMSR; + return; + + } + else + { + /* we really come down to looking at status flags */ + *iCode = EL734EncodeMSR(buffer,iBufLen,iMSR, iOMSR,iFPC,iFRC); + } + } + } +/* ------------------------------------------------------------------------ + Types of errors possible on EL734: + + Network error: Try reopening connection and redo command. + + Than there are problems which might have to do with a dodgy RS232, + resend command may help + + Some things cannot be fixed. +*/ + + static int FixError(void *self, int iError, float fNew) + { + EL734Driv *pDriv; + int iRet; + char pBueffel[512]; + int iMSR, iOMSR, iSS; + float fPos; + + assert(self); + pDriv = (EL734Driv *)self; + sprintf(pBueffel,"EL734 : %s %d %d %d Problem:",pDriv->hostname, + pDriv->iPort, pDriv->iChannel, pDriv->iMotor); + + /* get & check MSR flags */ + + + /* check for codes */ + switch(iError) + { + case 0: /* no error at all */ + return MOTOK; + case EL734__BAD_ID: /* ID */ + case EL734__BAD_ADR: /* ADR */ + case EL734__BAD_CMD: /* CMD */ + case EL734__BAD_ILLG: /* ILLG */ + case EL734__BAD_PAR: /* PAR */ + case EL734__BAD_TMO: + SICSLogWrite(pBueffel,eHWError); + SICSLogWrite("BAD Command or dodgy RS-232",eHWError); + return MOTREDO; + case EL734__EMERG_STOP: + return MOTFAIL; + case EL734__BAD_RNG: /* RNG */ + case MSRONLIMIT: + SICSLogWrite(pBueffel,eHWError); + SICSLogWrite("Out of Range",eHWError); + return MOTFAIL; + case EL734__BAD_STP: + return MOTFAIL; + break; + case MSRBUSY: + return MOTREDO; + case MSRRUNFAULT: + SICSLogWrite(pBueffel,eHWError); + SICSLogWrite("------ RUN Fault in Controller ---- ",eHWError); + return MOTFAIL; + case MSRPOSFAULT: + SICSLogWrite(pBueffel,eHWError); + SICSLogWrite("------ POS Fault in Controller ---- ",eHWError); + return MOTFAIL; + case MSRDEADCUSHION: + SICSLogWrite(pBueffel,eHWError); + SICSLogWrite("------ Air cushion Fault in Controller ---- ",eHWError); + return MOTFAIL; + case MSRFAULT: + return MOTFAIL; + case MSRHALT: + case MSRSTOP: + return MOTFAIL; + case EL734__FORCED_CLOSED: + case EL734__NOT_OPEN: + iRet = EL734_Open(&(pDriv->EL734struct),pDriv->hostname, + pDriv->iPort,pDriv->iChannel, + pDriv->iMotor,"DCMC EL734"); + if(iRet != 1) + { + return MOTFAIL; + } + else + { + return MOTREDO; + } + break; + case EL734__BAD_LOC: /* LO2 */ + case EL734__BAD_OFL: + EL734_Close(&(pDriv->EL734struct),0); + iRet = EL734_Open(&(pDriv->EL734struct),pDriv->hostname, + pDriv->iPort,pDriv->iChannel, + pDriv->iMotor,"DCMC EL734"); + if(iRet != 1) + { + return MOTFAIL; + } + else + { + return MOTREDO; + } + break; +/* case EL734__BAD_ASYNSRV: + EL734_Close(&(pDriv->EL734struct),1); + return MOTREDO; + break; +*/ default: + SICSLogWrite(pBueffel,eHWError); + SICSLogWrite("Network problem, trying to reopen",eHWError); + EL734_Close(&(pDriv->EL734struct),1); + iRet = EL734_Open(&(pDriv->EL734struct),pDriv->hostname, + pDriv->iPort,pDriv->iChannel, + pDriv->iMotor,"DCMC EL734"); + if(iRet != 1) + { + return MOTFAIL; + } + else + { + return MOTREDO; + } + } + + } +/*--------------------------------------------------------------------------*/ + static int Halt(void *self) + { + EL734Driv *pDriv; + int iRet; + char pBueffel[80]; + + assert(self); + pDriv = (EL734Driv *)self; + iRet = EL734_Stop(&(pDriv->EL734struct)); + if(iRet != 1) + { + return OKOK; + } + return HWFault; + } +/*--------------------------------------------------------------------------*/ + static int GetStat(void *self) + { + EL734Driv *pDriv; + float fPos; + int iRet, iMSR, iOMSR, iFRC,iFPC, iSS; + int eRet; + int iTest; + char pBueffel[80]; + + assert(self); + + pDriv = (EL734Driv *)self; + iRet = EL734_GetStatus(&(pDriv->EL734struct), + &iMSR, + &iOMSR, + &iFPC, + &iFRC, + &iSS, + &fPos); + if(iRet != 1) + { + return HWFault; + } + + if(iMSR != 0) + { + pDriv->iMSR = iMSR; + } + + iTest = EL734AnalyzeMSR(iMSR,iOMSR); + switch(iTest) + { + case MSRDEADCUSHION: + case MSRONLIMIT: + case MSRREF: + case MSRHALT: + case MSRSTOP: + return HWFault; + break; + case MSRRUNFAULT: + case MSRPOSFAULT: + return HWPosFault; + break; + case MSRBUSY: + return HWBusy; + break; + case MSRFAULT: + return HWWarn; + break; + default: + return HWIdle; + break; + } + + } +/*--------------------------------------------------------------------------*/ +extern void KillEL734(void *pdata); /* from el734driv.c */ +/*---------------------------------------------------------------------------*/ + static EL734Driv *MakeEL734DC(char *hostname, int iPort, int iChannel, + int iMotor) + { + EL734Driv *pDriv = NULL; + + int iError; + char pBueffel[80]; + char *pErr; + int iRet; + int iDummy; + + /* create a new struct */ + pDriv = (EL734Driv *)malloc(sizeof(EL734Driv)); + if(!pDriv) + { + return NULL; + } + memset(pDriv,0,sizeof(EL734Driv)); + + /* fill in some of the data entered */ + pDriv->hostname = strdup(hostname); + pDriv->iPort = iPort; + pDriv->iChannel = iChannel; + pDriv->iMotor = iMotor; + pDriv->name = strdup("EL734"); + + /* try opening the motor */ + iRet = EL734_Open(&(pDriv->EL734struct), hostname,iPort, + iChannel,iMotor,"DCMC EL734"); + if(iRet != 1) + { + EL734_ErrInfo(&pErr,&iError,&iRet, &iDummy); + KillEL734((void *)pDriv); + return NULL; + } + + /* now get the limits */ + EL734_GetLimits(&(pDriv->EL734struct),&(pDriv->fLower), + &(pDriv->fUpper)); + + + /* initialise the function pointers */ + pDriv->GetPosition = GetPos; + pDriv->RunTo = Run; + pDriv->GetError = GetErr; + pDriv->GetStatus = GetStat; + pDriv->Halt = Halt; + pDriv->TryAndFixIt = FixError; + pDriv->KillPrivate = KillEL734; + + return pDriv; + } +/*-------------------------------------------------------------------------- + interpreting the driver parameters is up to the driver, this below + inplements just this + */ + MotorDriver *CreateEL734DC(SConnection *pCon, int argc, char *argv[]) + { + EL734Driv *pDriv = NULL; + TokenList *pList = NULL; + TokenList *pCurrent; + char *hostname; + int iPort, iChannel, iMotor; + char pBueffel[512]; + + assert(pCon); + + /* split arguments */ + pList = SplitArguments(argc,argv); + if(!pList) + { + SCWrite(pCon,"Error parsing arguments",eError); + return NULL; + } + + /* first must be hostname */ + pCurrent = pList; + if(pCurrent->Type != eText) + { + sprintf(pBueffel,"EL734DC: Expected hostname but got --> %s <--", + pCurrent->text); + SCWrite(pCon,pBueffel,eError); + DeleteTokenList(pList); + return NULL; + } + hostname = pCurrent->text; + + /* next should be port */ + pCurrent = pCurrent->pNext; + if(!pCurrent) + { + SCWrite(pCon,"EL734DC: Insufficient number of arguments",eError); + DeleteTokenList(pList); + return NULL; + } + if(pCurrent->Type != eInt) + { + sprintf(pBueffel,"EL734DC: Expected Integer as Port number, got --> %s <--", + pCurrent->text); + SCWrite(pCon,pBueffel,eError); + DeleteTokenList(pList); + return NULL; + } + iPort = pCurrent->iVal; + + + /* next should be Channel number */ + pCurrent = pCurrent->pNext; + if(!pCurrent) + { + SCWrite(pCon,"EL734DC: Insufficient number of arguments",eError); + DeleteTokenList(pList); + return NULL; + } + if(pCurrent->Type != eInt) + { + sprintf(pBueffel,"EL734DC: Expected Integer as channel number, got --> %s <--", + pCurrent->text); + SCWrite(pCon,pBueffel,eError); + DeleteTokenList(pList); + return NULL; + } + iChannel = pCurrent->iVal; + + /* finally motor number */ + pCurrent = pCurrent->pNext; + if(!pCurrent) + { + + SCWrite(pCon,"EL734DC: Insufficient number of arguments",eError); + DeleteTokenList(pList); + return NULL; + } + if(pCurrent->Type != eInt) + { + sprintf(pBueffel,"EL734DC: Expected Integer as motor number, got --> %s <--", + pCurrent->text); + SCWrite(pCon,pBueffel,eError); + DeleteTokenList(pList); + return NULL; + } + iMotor = pCurrent->iVal; + + + /* finally initialize driver */ + pDriv = MakeEL734DC(hostname,iPort,iChannel,iMotor); + if(!pDriv) + { + SCWrite(pCon,"EL734DC: error opening motor, check adress",eError); + pDriv = NULL; + } + + /* clean up */ + DeleteTokenList(pList); + return (MotorDriver *)pDriv; + } +/*------------------------------------------------------------------------- + Stolen from David and modified to return an integer error code as well +*/ + static int EL734EncodeMSR (char *text, int text_len, + int msr, + int ored_msr, + int fp_cntr, + int fr_cntr) { + int len; + char my_text[132]; + char my_text_0[32]; + int iRet = 0; + + if (msr == 0) { + ored_msr = ored_msr & ~(MSR__BUSY); /* Zero "Busy" bit */ + if (ored_msr == MSR__OK) { + StrJoin (text, text_len, "Status, MSR = Idle. Positioned OK.", ""); + }else { + if ((ored_msr & MSR__OK) != 0) { + StrJoin (text, text_len, "Status, MSR = Idle. Positioned OK. ", ""); + }else { + StrJoin (text, text_len, "Status, MSR = Idle. ", ""); + } + if ((ored_msr & MSR__REF_OK) != 0) { + StrJoin (my_text, sizeof (my_text), text, ""); + StrJoin (text, text_len, my_text, "Ref. Pos'n OK. "); + } + if ((ored_msr & MSR__LIM_ERR) != 0) { + StrJoin (my_text, sizeof (my_text), text, ""); + StrJoin (text, text_len, my_text, "Limit Switch Problem. "); + iRet = MSRONLIMIT; + } + if ((ored_msr & MSR__AC_FAIL) != 0) { + StrJoin (my_text, sizeof (my_text), text, ""); + StrJoin (text, text_len, my_text, "Air-Cushion Error. "); + iRet = MSRDEADCUSHION; + } + if ((ored_msr & MSR__REF_FAIL) != 0) { + StrJoin (my_text, sizeof (my_text), text, ""); + StrJoin (text, text_len, my_text, "Ref. Pos'n Fail. "); + } + if ((ored_msr & MSR__POS_FAIL) != 0) { + StrJoin (my_text, sizeof (my_text), text, ""); + StrJoin (text, text_len, my_text, "Pos'n Fail. "); + iRet = MSRPOSFAULT; + } + if ((ored_msr & MSR__POS_FAULT) != 0) { + StrJoin (my_text, sizeof (my_text), text, ""); + if (fp_cntr == 1) { + StrJoin (text, text_len, my_text, "1 Pos'n Fault. "); + }else { + sprintf (my_text_0, "%d Pos'n Faults. ", fp_cntr); + StrJoin (text, text_len, my_text, my_text_0); + } + } + if ((ored_msr & MSR__RUN_FAIL) != 0) { + StrJoin (my_text, sizeof (my_text), text, ""); + StrJoin (text, text_len, my_text, "Run Fail. "); + iRet = MSRRUNFAULT; + } + if ((ored_msr & MSR__RUN_FAULT) != 0) { + StrJoin (my_text, sizeof (my_text), text, ""); + if (fr_cntr == 1) { + StrJoin (text, text_len, my_text, "1 Run Fault. "); + }else { + sprintf (my_text_0, "%d Run Faults. ", fr_cntr); + StrJoin (text, text_len, my_text, my_text_0); + } + } + if ((ored_msr & MSR__HALT) != 0) { + StrJoin (my_text, sizeof (my_text), text, ""); + StrJoin (text, text_len, my_text, "Halt. "); + iRet = MSRHALT; + } + if ((ored_msr & MSR__HI_LIM) != 0) { + StrJoin (my_text, sizeof (my_text), text, ""); + StrJoin (text, text_len, my_text, "Hit HiLim. "); + iRet = MSRONLIMIT; + } + if ((ored_msr & MSR__LO_LIM) != 0) { + StrJoin (my_text, sizeof (my_text), text, ""); + StrJoin (text, text_len, my_text, "Hit LoLim. "); + iRet = MSRONLIMIT; + } + if ((ored_msr & MSR__STOPPED) != 0) { + StrJoin (my_text, sizeof (my_text), text, ""); + StrJoin (text, text_len, my_text, "Stopped. "); + iRet = MSRSTOP; + } + } + }else if ((msr & ~(0x2fff)) != 0) { + StrJoin (text, text_len, "Status, MSR = ??", ""); + }else { + sprintf (my_text, "%#x ", msr); + StrJoin (text, text_len, "Status, MSR = ", my_text); + if ((msr & MSR__LIM_ERR) != 0) { + StrJoin (my_text, sizeof (my_text), text, ""); + StrJoin (text, text_len, my_text, "Limit Switch Problem/"); + iRet = MSRONLIMIT; + } + if ((msr & MSR__AC_FAIL) != 0) { + StrJoin (my_text, sizeof (my_text), text, ""); + StrJoin (text, text_len, my_text, "Air-Cushion Error/"); + iRet = MSRDEADCUSHION; + } + if ((msr & MSR__REF_FAIL) != 0) { + StrJoin (my_text, sizeof (my_text), text, ""); + StrJoin (text, text_len, my_text, "Ref. Pos'n Fail/"); + } + if ((msr & MSR__POS_FAIL) != 0) { + StrJoin (my_text, sizeof (my_text), text, ""); + StrJoin (text, text_len, my_text, "Pos'n Fail/"); + iRet = MSRPOSFAULT; + } + if ((msr & MSR__POS_FAULT) != 0) { + StrJoin (my_text, sizeof (my_text), text, ""); + StrJoin (text, text_len, my_text, "Pos'n Fault/"); + } + if ((msr & MSR__RUN_FAIL) != 0) { + StrJoin (my_text, sizeof (my_text), text, ""); + StrJoin (text, text_len, my_text, "Run Fail/"); + iRet = MSRRUNFAULT; + } + if ((msr & MSR__RUN_FAULT) != 0) { + StrJoin (my_text, sizeof (my_text), text, ""); + StrJoin (text, text_len, my_text, "Run Fault/"); + } + if ((msr & MSR__HALT) != 0) { + StrJoin (my_text, sizeof (my_text), text, ""); + StrJoin (text, text_len, my_text, "Halt/"); + iRet = MSRHALT; + } + if ((msr & MSR__HI_LIM) != 0) { + StrJoin (my_text, sizeof (my_text), text, ""); + StrJoin (text, text_len, my_text, "Hit HiLim/"); + iRet = MSRONLIMIT; + } + if ((msr & MSR__LO_LIM) != 0) { + StrJoin (my_text, sizeof (my_text), text, ""); + StrJoin (text, text_len, my_text, "Hit LoLim/"); + iRet = MSRONLIMIT; + } + if ((msr & MSR__STOPPED) != 0) { + StrJoin (my_text, sizeof (my_text), text, ""); + StrJoin (text, text_len, my_text, "Stopped/"); + iRet = MSRSTOP; + } + if ((msr & MSR__REF_OK) != 0) { + StrJoin (my_text, sizeof (my_text), text, ""); + StrJoin (text, text_len, my_text, "Ref. Pos'n OK/"); + } + if ((msr & MSR__OK) != 0) { + StrJoin (my_text, sizeof (my_text), text, ""); + StrJoin (text, text_len, my_text, "OK/"); + } + if ((msr & MSR__BUSY) != 0) { + StrJoin (my_text, sizeof (my_text), text, ""); + StrJoin (text, text_len, my_text, "Busy/"); + } + len = strlen (text); + text[len-1] = '\0'; + } + return iRet; + } +/*-------------------------------------------------------------------------*/ + static int EL734AnalyzeMSR(int msr,int ored_msr) + { + int iRet = 0; + + /* this means the motor is done */ + if (msr == 0) { + ored_msr = ored_msr & ~(MSR__BUSY); /* Zero "Busy" bit */ + if (ored_msr == MSR__OK) { + iRet = MSROK; + }else { + if ((ored_msr & MSR__OK) != 0) { + iRet = MSROK; + }else { + iRet = MSROK; + } + if ((ored_msr & MSR__REF_OK) != 0) { + iRet = MSROK; + } + if ((ored_msr & MSR__LIM_ERR) != 0) { + return MSRONLIMIT; + } + if ((ored_msr & MSR__AC_FAIL) != 0) { + return MSRDEADCUSHION; + } + if ((ored_msr & MSR__REF_FAIL) != 0) { + iRet = MSRREF; + } + if ((ored_msr & MSR__POS_FAIL) != 0) { + return MSRPOSFAULT; + } + if ((ored_msr & MSR__POS_FAULT) != 0) { + iRet = MSRFAULT; + } + if ((ored_msr & MSR__RUN_FAIL) != 0) { + return MSRRUNFAULT; + } + if ((ored_msr & MSR__RUN_FAULT) != 0) { + iRet = MSRFAULT; + } + if ((ored_msr & MSR__HALT) != 0) { + return MSRHALT; + } + if ((ored_msr & MSR__HI_LIM) != 0) { + return MSRONLIMIT; + } + if ((ored_msr & MSR__LO_LIM) != 0) { + return MSRONLIMIT; + } + if ((ored_msr & MSR__STOPPED) != 0) { + return MSRSTOP; + } + } + /* the motor is still fighting along */ + }else if ((msr & ~(0x2fff)) != 0) { + iRet = MSROK; + }else { + if ((msr & MSR__LIM_ERR) != 0) { + return MSRONLIMIT; + } + if ((msr & MSR__AC_FAIL) != 0) { + return MSRDEADCUSHION; + } + if ((msr & MSR__REF_FAIL) != 0) { + iRet = MSRREF; + } + if ((msr & MSR__POS_FAIL) != 0) { + return MSRPOSFAULT; + } + if ((msr & MSR__POS_FAULT) != 0) { + iRet = MSRFAULT; + } + if ((msr & MSR__RUN_FAIL) != 0) { + return MSRRUNFAULT; + } + if ((msr & MSR__RUN_FAULT) != 0) { + iRet = MSRFAULT; + } + if ((msr & MSR__HALT) != 0) { + return MSRHALT; + } + if ((msr & MSR__HI_LIM) != 0) { + return MSRONLIMIT; + } + if ((msr & MSR__LO_LIM) != 0) { + return MSRONLIMIT; + } + if ((msr & MSR__STOPPED) != 0) { + return MSRSTOP; + } + if ((msr & MSR__REF_OK) != 0) { + iRet = MSROK; + } + if ((msr & MSR__OK) != 0) { + iRet = MSROK; + } + if ((msr & MSR__BUSY) != 0) { + iRet = MSRBUSY; + } + } + return iRet; + } + + diff --git a/el734driv.c b/el734driv.c new file mode 100644 index 0000000..195f79e --- /dev/null +++ b/el734driv.c @@ -0,0 +1,954 @@ +/*-------------------------------------------------------------------------- + A motor driver for EL734 type motors as used at SinQ + + + Mark Koennecke, November 1996 + + modified for new directory structure, June 2003, Mark Koennecke + + Copyright: + + Labor fuer Neutronenstreuung + Paul Scherrer Institut + CH-5423 Villigen-PSI + + + The authors hereby grant permission to use, copy, modify, distribute, + and license this software and its documentation for any purpose, provided + that existing copyright notices are retained in all copies and that this + notice is included verbatim in any distributions. No written agreement, + license, or royalty fee is required for any of the authorized uses. + Modifications to this software may be copyrighted by their authors + and need not follow the licensing terms described here, provided that + the new terms are clearly indicated on the first page of each file where + they apply. + + IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY + FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY + DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE + IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE + NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR + MODIFICATIONS. +------------------------------------------------------------------------------*/ +#include +#include +#include +#include +#include +#include +#include "hardsup/sinq_prototypes.h" +#include "hardsup/rs232c_def.h" +#include "hardsup/el734_def.h" +#include "hardsup/el734fix.h" +#include +#include + +/*----------------------------------------------------------------------- + The motor driver structure. Please note that the first set of fields has + be identical with the fields of AbstractModriv in ../modriv.h +------------------------------------------------------------------------*/ + typedef struct __MoDriv { + /* general motor driver interface + fields. REQUIRED! + */ + float fUpper; /* upper limit */ + float fLower; /* lower limit */ + char *name; + int (*GetPosition)(void *self,float *fPos); + int (*RunTo)(void *self, float fNewVal); + int (*GetStatus)(void *self); + void (*GetError)(void *self, int *iCode, char *buffer, int iBufLen); + int (*TryAndFixIt)(void *self,int iError, float fNew); + int (*Halt)(void *self); + int (*GetDriverPar)(void *self, char *name, + float *value); + int (*SetDriverPar)(void *self,SConnection *pCon, + char *name, float newValue); + void (*ListDriverPar)(void *self, char *motorName, + SConnection *pCon); + void (*KillPrivate)(void *self); + + + /* EL-734 specific fields */ + int iPort; + char *hostname; + int iChannel; + int iMotor; + void *EL734struct; + int iMSR; + } EL734Driv; + + static int EL734EncodeMSR(char *text, int iLen, + int iMSR, int iOMSR, int iFP, int iFR); + + static int EL734AnalyzeMSR(int iMSR, int iOMSR); + +/* addional error codes for Status-things */ +#define MSRBUSY -40 +#define MSRONLIMIT -41 +#define MSRRUNFAULT -42 +#define MSRPOSFAULT -43 +#define MSRDEADCUSHION -44 +#define MSRHALT -45 +#define MSRSTOP -46 +#define MSROK -47 +#define MSRREF -48 +#define MSRFAULT -49 +/* --------------------------------------------------------------------------*/ + static int GetPos(void *self, float *fData) + { + EL734Driv *pDriv; + float fPos; + int iRet, iMSR, iOMSR, iFRC,iFPC, iSS; + + assert(self); + + pDriv = (EL734Driv *)self; + iRet = EL734_GetStatus(&(pDriv->EL734struct), + &iMSR, + &iOMSR, + &iFPC, + &iFRC, + &iSS, + &fPos); + if(iMSR != 0) + { + pDriv->iMSR = iMSR; + } + *fData = fPos; + if(iRet != 1) + { + return HWFault; + } + else + return OKOK; + + } +/*--------------------------------------------------------------------------*/ + static int Run(void *self, float fNew) + { + EL734Driv *pDriv; + int iRet; + + assert(self); + + pDriv = (EL734Driv *)self; + iRet = EL734_MoveNoWait (&(pDriv->EL734struct), fNew); + if(iRet == 1) + { + return OKOK; + } + else + { + return HWFault; + } + } + +/*--------------------------------------------------------------------------- + + EL734Error2Text converts between an EL734 error code to text +-----------------------------------------------------------------------------*/ + extern char EL734_IllgText[256]; + + static void EL734Error2Text(char *pBuffer, int iErr) + { + strcpy(pBuffer,"ERROR: HW:"); + switch(iErr) + { + case EL734__BAD_ADR: + strcat(pBuffer,"EL734__BAD_ADR"); + break; + case EL734__BAD_BIND: + strcat(pBuffer,"EL734__BAD_BIND"); + break; + case EL734__BAD_CMD: + strcat(pBuffer,"EL734__BAD_CMD"); + break; + case EL734__BAD_CONNECT: + strcat(pBuffer,"EL734__BAD_CONNECT"); + break; + case EL734__BAD_FLUSH: + strcat(pBuffer,"EL734__BAD_FLUSH"); + break; + case EL734__BAD_HOST: + strcat(pBuffer,"EL734__BAD_HOST"); + break; + case EL734__BAD_ID: + strcat(pBuffer,"EL734__BAD_ID"); + break; + case EL734__BAD_ILLG: + strcat(pBuffer,"EL734__BAD_ILLG "); + + strcat(pBuffer,EL734_IllgText); + + break; + case EL734__BAD_LOC: + strcat(pBuffer,"EL734__BAD_LOC"); + break; + case EL734__BAD_MALLOC: + strcat(pBuffer,"EL734__BAD_MALLOC"); + break; + case EL734__BAD_NOT_BCD: + strcat(pBuffer,"EL734__BAD_NOT_BCD"); + break; + case EL734__BAD_OFL: + strcat(pBuffer,"EL734__BAD_OFL"); + break; + case EL734__BAD_PAR: + strcat(pBuffer,"EL734__BAD_PAR"); + break; + + case EL734__BAD_RECV: + strcat(pBuffer,"EL734__BAD_RECV"); + break; + case EL734__BAD_RECV_NET: + strcat(pBuffer,"EL734__BAD_RECV_NET"); + break; + case EL734__BAD_RECV_PIPE: + strcat(pBuffer,"EL734__BAD_RECV_PIPE"); + break; + case EL734__BAD_RECV_UNKN: + strcat(pBuffer,"EL734__BAD_RECV_UNKN"); + break; + case EL734__BAD_RECVLEN: + strcat(pBuffer,"EL734__BAD_RECVLEN"); + break; + case EL734__BAD_RECV1: + strcat(pBuffer,"EL734__BAD_RECV1"); + break; + case EL734__BAD_RECV1_NET: + strcat(pBuffer,"EL734__BAD_RECV1_NET"); + break; + case EL734__BAD_RECV1_PIPE: + strcat(pBuffer,"EL734__BAD_RECV1_PIPE"); + break; + case EL734__BAD_RNG: + strcat(pBuffer,"EL734__BAD_RNG"); + break; + case EL734__BAD_SEND: + strcat(pBuffer,"EL734__BAD_SEND"); + break; + case EL734__BAD_SEND_PIPE: + strcat(pBuffer,"EL734__BAD_SEND_PIPE"); + break; + case EL734__BAD_SEND_NET: + strcat(pBuffer,"EL734__BAD_SEND_NET"); + break; + case EL734__BAD_SEND_UNKN: + strcat(pBuffer,"EL734__BAD_SEND_UNKN"); + break; + case EL734__BAD_SENDLEN: + strcat(pBuffer,"EL734__BAD_SENDLEN"); + break; + case EL734__BAD_SOCKET: + strcat(pBuffer,"EL734__BAD_SOCKET"); + break; + case EL734__BAD_TMO: + strcat(pBuffer,"EL734__BAD_TMO"); + break; + case EL734__FORCED_CLOSED: + strcat(pBuffer,"EL734__FORCED_CLOSED"); + break; + case EL734__BAD_STP: + strcat(pBuffer,"EL734__BAD_STP"); + break; + case EL734__EMERG_STOP: + strcat(pBuffer,"EL734__EMERG_STOP"); + break; + case EL734__NOT_OPEN: + strcat(pBuffer,"EL734__NOT_OPEN"); + break; + case EL734__BAD_ASYNSRV: + strcat(pBuffer,"EL734__BAD_ASYNSRV"); + break; + default: + sprintf(pBuffer,"Unknown EL734 error %d",iErr); + break; + } + } + +/*-------------------------------------------------------------------------*/ + static void GetErr(void *self, int *iCode, char *buffer, int iBufLen) + { + EL734Driv *pDriv; + char pBueffel[512]; + int iMSR, iOMSR, iSS; + int iRet, iFPC, iFRC; + int iErr; + float fPos; + char *pErr; + + assert(self); + + /* get EL734 error codes */ + pDriv = (EL734Driv *)self; + EL734_ErrInfo(&pErr,&iMSR,&iOMSR, &iSS); + if(iMSR != 0) + { + EL734Error2Text(pBueffel,iMSR); + strncpy(buffer,pBueffel,(iBufLen-1)); + *iCode = iMSR; + return; + } + else + { /* check status flag for addional errors */ + iRet = EL734_GetStatus(&(pDriv->EL734struct), + &iMSR, + &iOMSR, + &iFPC, + &iFRC, + &iSS, + &fPos); + if(iRet != 1) + { /* failure on this one, this has to be handled */ + EL734_ErrInfo(&pErr,&iMSR,&iOMSR, &iSS); + EL734Error2Text(pBueffel,iMSR); + strncpy(buffer,pBueffel,(iBufLen-1)); + *iCode = iMSR; + return; + + } + else + { + /* we really come down to looking at status flags */ + *iCode = EL734EncodeMSR(buffer,iBufLen,iMSR, iOMSR,iFPC,iFRC); + } + } + } +/* ------------------------------------------------------------------------ + Types of errors possible on EL734: + + Network error: Try reopening connection and redo command. + + Than there are problems which might have to do with a dodgy RS232, + resend command may help + + Some things cannot be fixed. +*/ + + static int FixError(void *self, int iError, float fNew) + { + EL734Driv *pDriv; + int iRet; + char pBueffel[512]; + int iMSR, iOMSR, iSS; + float fPos; + + assert(self); + pDriv = (EL734Driv *)self; + sprintf(pBueffel,"EL734 : %s %d %d %d Problem:",pDriv->hostname, + pDriv->iPort, pDriv->iChannel, pDriv->iMotor); + + /* get & check MSR flags */ + + + /* check for codes */ + switch(iError) + { + case 0: /* no error at all */ + return MOTOK; + case EL734__BAD_ID: /* ID */ + case EL734__BAD_ADR: /* ADR */ + case EL734__BAD_CMD: /* CMD */ + case EL734__BAD_ILLG: /* ILLG */ + case EL734__BAD_PAR: /* PAR */ + case EL734__BAD_TMO: /* timeout */ + SICSLogWrite(pBueffel,eHWError); + SICSLogWrite("BAD Command or dodgy RS-232",eHWError); + return MOTREDO; + case EL734__EMERG_STOP: + return MOTFAIL; + case EL734__BAD_STP: /* motor disabled by switch */ + return MOTFAIL; + break; + case EL734__BAD_RNG: /* RNG */ + case MSRONLIMIT: + SICSLogWrite(pBueffel,eHWError); + SICSLogWrite("Out of Range",eHWError); + return MOTFAIL; + case MSRBUSY: + return MOTREDO; + case MSRRUNFAULT: + SICSLogWrite(pBueffel,eHWError); + SICSLogWrite("------ RUN Fault in Controller ---- ",eHWError); + return MOTFAIL; + case MSRPOSFAULT: + SICSLogWrite(pBueffel,eHWError); + SICSLogWrite("------ POS Fault in Controller ---- ",eHWError); + return MOTFAIL; + case MSRDEADCUSHION: + SICSLogWrite(pBueffel,eHWError); + SICSLogWrite("------ Air cushion Fault in Controller ---- ",eHWError); + return MOTFAIL; + case MSRFAULT: + return MOTFAIL; + case MSRHALT: + case MSRSTOP: + return MOTFAIL; + case EL734__FORCED_CLOSED: + case EL734__NOT_OPEN: + iRet = EL734_Open(&(pDriv->EL734struct),pDriv->hostname, + pDriv->iPort,pDriv->iChannel, + pDriv->iMotor,"STPMC EL734"); + if(iRet != 1) + { + return MOTFAIL; + } + else + { + return MOTREDO; + } + break; + case EL734__BAD_OFL: + case EL734__BAD_LOC: /* LOocal mode */ + EL734_Close(&(pDriv->EL734struct),0); + iRet = EL734_Open(&(pDriv->EL734struct),pDriv->hostname, + pDriv->iPort,pDriv->iChannel, + pDriv->iMotor,"STPMC EL734"); + if(iRet != 1) + { + return MOTFAIL; + } + else + { + return MOTREDO; + } + break; +/* case EL734__BAD_ASYNSRV: + EL734_Close(&(pDriv->EL734struct),1); + return MOTREDO; +*/ + default: + SICSLogWrite(pBueffel,eHWError); + SICSLogWrite("Network problem, trying to reopen",eHWError); + EL734_Close(&(pDriv->EL734struct),1); + iRet = EL734_Open(&(pDriv->EL734struct),pDriv->hostname, + pDriv->iPort,pDriv->iChannel, + pDriv->iMotor,"STPMC EL734"); + if(iRet != 1) + { + return MOTFAIL; + } + else + { + return MOTREDO; + } + } + + } +/*--------------------------------------------------------------------------*/ + static int Halt(void *self) + { + EL734Driv *pDriv; + int iRet; + char pBueffel[80]; + + assert(self); + pDriv = (EL734Driv *)self; + iRet = EL734_Stop(&(pDriv->EL734struct)); + if(iRet == 1) + { + return OKOK; + } + return HWFault; + } +/*--------------------------------------------------------------------------*/ + static int GetStat(void *self) + { + EL734Driv *pDriv; + float fPos; + int iRet, iMSR, iOMSR, iFRC,iFPC, iSS; + int eRet; + int iTest; + char pBueffel[80]; + + assert(self); + + pDriv = (EL734Driv *)self; + iRet = EL734_GetStatus(&(pDriv->EL734struct), + &iMSR, + &iOMSR, + &iFPC, + &iFRC, + &iSS, + &fPos); + if(iRet != 1) + { + return HWFault; + } + + if(iMSR != 0) + { + pDriv->iMSR = iMSR; + } + + iTest = EL734AnalyzeMSR(iMSR,iOMSR); + switch(iTest) + { + case MSRDEADCUSHION: + case MSRONLIMIT: + case MSRREF: + case MSRHALT: + case MSRSTOP: + return HWFault; + break; + case MSRRUNFAULT: + case MSRPOSFAULT: + return HWPosFault; + break; + case MSRBUSY: + return HWBusy; + break; + case MSRFAULT: + return HWWarn; + break; + default: + return HWIdle; + break; + } + } + +/*--------------------------------------------------------------------------*/ + void KillEL734(void *self) + { + EL734Driv *pDriv; + + assert(self); + pDriv = (EL734Driv *)self; + + EL734_Close(&(pDriv->EL734struct),0); + if(pDriv->hostname) + free(pDriv->hostname); + } + +/*---------------------------------------------------------------------------*/ + static EL734Driv *MakeEL734(char *hostname, int iPort, int iChannel, + int iMotor) + { + EL734Driv *pDriv = NULL; + + int iError; + char pBueffel[80]; + char *pErr; + int iRet; + int iDummy; + + /* create a new struct */ + pDriv = (EL734Driv *)malloc(sizeof(EL734Driv)); + if(!pDriv) + { + return NULL; + } + memset(pDriv,0,sizeof(EL734Driv)); + + /* fill in some of the data entered */ + pDriv->hostname = strdup(hostname); + pDriv->iPort = iPort; + pDriv->iChannel = iChannel; + pDriv->iMotor = iMotor; + pDriv->name = strdup("EL734"); + + /* try opening the motor */ + iRet = EL734_Open(&(pDriv->EL734struct), hostname,iPort, + iChannel,iMotor,"STPMC EL734"); + if(iRet != 1) + { + EL734_ErrInfo(&pErr,&iError,&iRet, &iDummy); + KillEL734((void *)pDriv); + return NULL; + } + + /* now get the limits */ + EL734_GetLimits(&(pDriv->EL734struct),&(pDriv->fLower), + &(pDriv->fUpper)); + + + /* initialise the function pointers */ + pDriv->GetPosition = GetPos; + pDriv->RunTo = Run; + pDriv->GetError = GetErr; + pDriv->GetStatus = GetStat; + pDriv->Halt = Halt; + pDriv->TryAndFixIt = FixError; + pDriv->KillPrivate = KillEL734; + + return pDriv; + } +/*-------------------------------------------------------------------------- + interpreting the driver parameters is up to the driver, this below + inplements just this + */ + MotorDriver *CreateEL734(SConnection *pCon, int argc, char *argv[]) + { + EL734Driv *pDriv = NULL; + TokenList *pList = NULL; + TokenList *pCurrent; + char *hostname; + int iPort, iChannel, iMotor; + char pBueffel[512]; + + assert(pCon); + + /* split arguments */ + pList = SplitArguments(argc,argv); + if(!pList) + { + SCWrite(pCon,"Error parsing arguments",eError); + return NULL; + } + + /* first must be hostname */ + pCurrent = pList; + if(pCurrent->Type != eText) + { + sprintf(pBueffel,"EL734: Expected hostname but got --> %s <--", + pCurrent->text); + SCWrite(pCon,pBueffel,eError); + DeleteTokenList(pList); + return NULL; + } + hostname = pCurrent->text; + + /* next should be port */ + pCurrent = pCurrent->pNext; + if(!pCurrent) + { + SCWrite(pCon,"EL734: Insufficient number of arguments",eError); + DeleteTokenList(pList); + return NULL; + } + if(pCurrent->Type != eInt) + { + sprintf(pBueffel,"EL734: Expected Integer as Port number, got --> %s <--", + pCurrent->text); + SCWrite(pCon,pBueffel,eError); + DeleteTokenList(pList); + return NULL; + } + iPort = pCurrent->iVal; + + + /* next should be Channel number */ + pCurrent = pCurrent->pNext; + if(!pCurrent) + { + SCWrite(pCon,"EL734: Insufficient number of arguments",eError); + DeleteTokenList(pList); + return NULL; + } + if(pCurrent->Type != eInt) + { + sprintf(pBueffel,"EL734: Expected Integer as channel number, got --> %s <--", + pCurrent->text); + SCWrite(pCon,pBueffel,eError); + DeleteTokenList(pList); + return NULL; + } + iChannel = pCurrent->iVal; + + /* finally motor number */ + pCurrent = pCurrent->pNext; + if(!pCurrent) + { + + SCWrite(pCon,"EL734: Insufficient number of arguments",eError); + DeleteTokenList(pList); + return NULL; + } + if(pCurrent->Type != eInt) + { + sprintf(pBueffel,"EL734: Expected Integer as motor number, got --> %s <--", + pCurrent->text); + SCWrite(pCon,pBueffel,eError); + DeleteTokenList(pList); + return NULL; + } + iMotor = pCurrent->iVal; + + + /* finally initialize driver */ + pDriv = MakeEL734(hostname,iPort,iChannel,iMotor); + if(!pDriv) + { + SCWrite(pCon,"EL734: error opening motor, check adress",eError); + pDriv = NULL; + } + + /* clean up */ + DeleteTokenList(pList); + return (MotorDriver *)pDriv; + } +/*------------------------------------------------------------------------- + Stolen from David and modified to return an integer error code as well +*/ + static int EL734EncodeMSR (char *text, int text_len, + int msr, + int ored_msr, + int fp_cntr, + int fr_cntr) { + int len; + char my_text[132]; + char my_text_0[32]; + int iRet = 0; + + if (msr == 0) { + ored_msr = ored_msr & ~(MSR__BUSY); /* Zero "Busy" bit */ + if (ored_msr == MSR__OK) { + StrJoin (text, text_len, "Status, MSR = Idle. Positioned OK.", ""); + }else { + if ((ored_msr & MSR__OK) != 0) { + StrJoin (text, text_len, "Status, MSR = Idle. Positioned OK. ", ""); + }else { + StrJoin (text, text_len, "Status, MSR = Idle. ", ""); + } + if ((ored_msr & MSR__REF_OK) != 0) { + StrJoin (my_text, sizeof (my_text), text, ""); + StrJoin (text, text_len, my_text, "Ref. Pos'n OK. "); + } + if ((ored_msr & MSR__LIM_ERR) != 0) { + StrJoin (my_text, sizeof (my_text), text, ""); + StrJoin (text, text_len, my_text, "Limit Switch Problem. "); + iRet = MSRONLIMIT; + } + if ((ored_msr & MSR__AC_FAIL) != 0) { + StrJoin (my_text, sizeof (my_text), text, ""); + StrJoin (text, text_len, my_text, "Air-Cushion Error. "); + iRet = MSRDEADCUSHION; + } + if ((ored_msr & MSR__REF_FAIL) != 0) { + StrJoin (my_text, sizeof (my_text), text, ""); + StrJoin (text, text_len, my_text, "Ref. Pos'n Fail. "); + } + if ((ored_msr & MSR__POS_FAIL) != 0) { + StrJoin (my_text, sizeof (my_text), text, ""); + StrJoin (text, text_len, my_text, "Pos'n Fail. "); + iRet = MSRPOSFAULT; + } + if ((ored_msr & MSR__POS_FAULT) != 0) { + StrJoin (my_text, sizeof (my_text), text, ""); + if (fp_cntr == 1) { + StrJoin (text, text_len, my_text, "1 Pos'n Fault. "); + }else { + sprintf (my_text_0, "%d Pos'n Faults. ", fp_cntr); + StrJoin (text, text_len, my_text, my_text_0); + } + } + if ((ored_msr & MSR__RUN_FAIL) != 0) { + StrJoin (my_text, sizeof (my_text), text, ""); + StrJoin (text, text_len, my_text, "Run Fail. "); + iRet = MSRRUNFAULT; + } + if ((ored_msr & MSR__RUN_FAULT) != 0) { + StrJoin (my_text, sizeof (my_text), text, ""); + if (fr_cntr == 1) { + StrJoin (text, text_len, my_text, "1 Run Fault. "); + }else { + sprintf (my_text_0, "%d Run Faults. ", fr_cntr); + StrJoin (text, text_len, my_text, my_text_0); + } + } + if ((ored_msr & MSR__HALT) != 0) { + StrJoin (my_text, sizeof (my_text), text, ""); + StrJoin (text, text_len, my_text, "Halt. "); + iRet = MSRHALT; + } + if ((ored_msr & MSR__HI_LIM) != 0) { + StrJoin (my_text, sizeof (my_text), text, ""); + StrJoin (text, text_len, my_text, "Hit HiLim. "); + iRet = MSRONLIMIT; + } + if ((ored_msr & MSR__LO_LIM) != 0) { + StrJoin (my_text, sizeof (my_text), text, ""); + StrJoin (text, text_len, my_text, "Hit LoLim. "); + iRet = MSRONLIMIT; + } + if ((ored_msr & MSR__STOPPED) != 0) { + StrJoin (my_text, sizeof (my_text), text, ""); + StrJoin (text, text_len, my_text, "Stopped. "); + iRet = MSRSTOP; + } + } + }else if ((msr & ~(0x2fff)) != 0) { + StrJoin (text, text_len, "Status, MSR = ??", ""); + }else { + sprintf (my_text, "%#x ", msr); + StrJoin (text, text_len, "Status, MSR = ", my_text); + if ((msr & MSR__LIM_ERR) != 0) { + StrJoin (my_text, sizeof (my_text), text, ""); + StrJoin (text, text_len, my_text, "Limit Switch Problem/"); + iRet = MSRONLIMIT; + } + if ((msr & MSR__AC_FAIL) != 0) { + StrJoin (my_text, sizeof (my_text), text, ""); + StrJoin (text, text_len, my_text, "Air-Cushion Error/"); + iRet = MSRDEADCUSHION; + } + if ((msr & MSR__REF_FAIL) != 0) { + StrJoin (my_text, sizeof (my_text), text, ""); + StrJoin (text, text_len, my_text, "Ref. Pos'n Fail/"); + } + if ((msr & MSR__POS_FAIL) != 0) { + StrJoin (my_text, sizeof (my_text), text, ""); + StrJoin (text, text_len, my_text, "Pos'n Fail/"); + iRet = MSRPOSFAULT; + } + if ((msr & MSR__POS_FAULT) != 0) { + StrJoin (my_text, sizeof (my_text), text, ""); + StrJoin (text, text_len, my_text, "Pos'n Fault/"); + } + if ((msr & MSR__RUN_FAIL) != 0) { + StrJoin (my_text, sizeof (my_text), text, ""); + StrJoin (text, text_len, my_text, "Run Fail/"); + iRet = MSRRUNFAULT; + } + if ((msr & MSR__RUN_FAULT) != 0) { + StrJoin (my_text, sizeof (my_text), text, ""); + StrJoin (text, text_len, my_text, "Run Fault/"); + } + if ((msr & MSR__HALT) != 0) { + StrJoin (my_text, sizeof (my_text), text, ""); + StrJoin (text, text_len, my_text, "Halt/"); + iRet = MSRHALT; + } + if ((msr & MSR__HI_LIM) != 0) { + StrJoin (my_text, sizeof (my_text), text, ""); + StrJoin (text, text_len, my_text, "Hit HiLim/"); + iRet = MSRONLIMIT; + } + if ((msr & MSR__LO_LIM) != 0) { + StrJoin (my_text, sizeof (my_text), text, ""); + StrJoin (text, text_len, my_text, "Hit LoLim/"); + iRet = MSRONLIMIT; + } + if ((msr & MSR__STOPPED) != 0) { + StrJoin (my_text, sizeof (my_text), text, ""); + StrJoin (text, text_len, my_text, "Stopped/"); + iRet = MSRSTOP; + } + if ((msr & MSR__REF_OK) != 0) { + StrJoin (my_text, sizeof (my_text), text, ""); + StrJoin (text, text_len, my_text, "Ref. Pos'n OK/"); + } + if ((msr & MSR__OK) != 0) { + StrJoin (my_text, sizeof (my_text), text, ""); + StrJoin (text, text_len, my_text, "OK/"); + } + if ((msr & MSR__BUSY) != 0) { + StrJoin (my_text, sizeof (my_text), text, ""); + StrJoin (text, text_len, my_text, "Busy/"); + } + len = strlen (text); + text[len-1] = '\0'; + } + return iRet; + } +/*-------------------------------------------------------------------------*/ + static int EL734AnalyzeMSR(int msr,int ored_msr) + { + int iRet = 0; + + /* this means the motor is done */ + if (msr == 0) { + ored_msr = ored_msr & ~(MSR__BUSY); /* Zero "Busy" bit */ + if (ored_msr == MSR__OK) { + iRet = MSROK; + }else { + if ((ored_msr & MSR__OK) != 0) { + iRet = MSROK; + }else { + iRet = MSROK; + } + if ((ored_msr & MSR__REF_OK) != 0) { + iRet = MSROK; + } + if ((ored_msr & MSR__LIM_ERR) != 0) { + return MSRONLIMIT; + } + if ((ored_msr & MSR__AC_FAIL) != 0) { + return MSRDEADCUSHION; + } + if ((ored_msr & MSR__REF_FAIL) != 0) { + iRet = MSRREF; + } + if ((ored_msr & MSR__POS_FAIL) != 0) { + return MSRPOSFAULT; + } + if ((ored_msr & MSR__POS_FAULT) != 0) { + iRet = MSRFAULT; + } + if ((ored_msr & MSR__RUN_FAIL) != 0) { + return MSRRUNFAULT; + } + if ((ored_msr & MSR__RUN_FAULT) != 0) { + iRet = MSRFAULT; + } + if ((ored_msr & MSR__HALT) != 0) { + return MSRHALT; + } + if ((ored_msr & MSR__HI_LIM) != 0) { + return MSRONLIMIT; + } + if ((ored_msr & MSR__LO_LIM) != 0) { + return MSRONLIMIT; + } + if ((ored_msr & MSR__STOPPED) != 0) { + return MSRSTOP; + } + } + /* the motor is still fighting along */ + }else if ((msr & ~(0x2fff)) != 0) { + iRet = MSROK; + }else { + if ((msr & MSR__LIM_ERR) != 0) { + return MSRONLIMIT; + } + if ((msr & MSR__AC_FAIL) != 0) { + return MSRDEADCUSHION; + } + if ((msr & MSR__REF_FAIL) != 0) { + iRet = MSRREF; + } + if ((msr & MSR__POS_FAIL) != 0) { + return MSRPOSFAULT; + } + if ((msr & MSR__POS_FAULT) != 0) { + iRet = MSRFAULT; + } + if ((msr & MSR__RUN_FAIL) != 0) { + return MSRRUNFAULT; + } + if ((msr & MSR__RUN_FAULT) != 0) { + iRet = MSRFAULT; + } + if ((msr & MSR__HALT) != 0) { + return MSRHALT; + } + if ((msr & MSR__HI_LIM) != 0) { + return MSRONLIMIT; + } + if ((msr & MSR__LO_LIM) != 0) { + return MSRONLIMIT; + } + if ((msr & MSR__STOPPED) != 0) { + return MSRSTOP; + } + if ((msr & MSR__REF_OK) != 0) { + iRet = MSROK; + } + if ((msr & MSR__OK) != 0) { + iRet = MSROK; + } + if ((msr & MSR__BUSY) != 0) { + iRet = MSRBUSY; + } + } + return iRet; + } + + diff --git a/el755driv.c b/el755driv.c new file mode 100644 index 0000000..cceafb1 --- /dev/null +++ b/el755driv.c @@ -0,0 +1,318 @@ +/*-------------------------------------------------------------------------- + E L 7 5 5 D R I V + + This file contains the implementation for the EL755 magnet controller + driver. + + Mark Koennecke, November 1999 + + Copyright: see copyright.h +----------------------------------------------------------------------------*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include + + typedef struct __EVDriver *pEVDriver; + +#include +#include "hardsup/el755_def.h" +#include "hardsup/el755_errcodes.h" +#include "hardsup/sinq_prototypes.h" + +/*-----------------------------------------------------------------------*/ + typedef struct { + void *pData; + char *pHost; + int iPort; + int iChannel; + int iIndex; + int iLastError; + } EL755Driv, *pEL755Driv; + +/*---------------------------------------------------------------------------*/ + static int GetEL755Pos(pEVDriver self, float *fPos) + { + pEL755Driv pMe = NULL; + int iRet; + float fSoll; + + assert(self); + pMe = (pEL755Driv)self->pPrivate; + assert(pMe); + + iRet = EL755_GetCurrents(&(pMe->pData),&fSoll,fPos); + if(iRet != 1) + { + return 0; + } + return 1; + } +/*----------------------------------------------------------------------------*/ + static int EL755Run(pEVDriver self, float fVal) + { + pEL755Driv pMe = NULL; + int iRet; + + assert(self); + pMe = (pEL755Driv )self->pPrivate; + assert(pMe); + + iRet = EL755_SetCurrent(&(pMe->pData),fVal); + if(iRet != 1) + { + return 0; + } + return 1; + } +/*--------------------------------------------------------------------------*/ + static int EL755Error(pEVDriver self, int *iCode, char *error, int iErrLen) + { + pEL755Driv pMe = NULL; + char *pPtr = NULL; + int i1, i2; + char pBueffel[132]; + + assert(self); + pMe = (pEL755Driv)self->pPrivate; + assert(pMe); + + /* retrieve error */ + EL755_ErrInfo(&pPtr,iCode,&i1,&i2); + switch(*iCode) + { + case EL755__TURNED_OFF: + strncpy(error,"EL755__TURNED_OF",iErrLen); + break; + case EL755__TOO_MANY: + strncpy(error,"EL755__TO_MANY",iErrLen); + break; + case EL755__TOO_LARGE: + strncpy(error,"EL755__TOO_LARGE",iErrLen); + break; + case EL755__OVFLOW: + strncpy(error,"EL755_OVFLOW",iErrLen); + break; + case EL755__OUT_OF_RANGE: + strncpy(error,"EL755_OUT_OF_RANGE",iErrLen); + break; + case EL755__OFFLINE: + strncpy(error,"EL755_OFFLINE",iErrLen); + break; + case EL755__NO_SOCKET: + strncpy(error,"EL755__NO_SOCKET",iErrLen); + break; + case EL755__NOT_OPEN: + strncpy(error,"EL755__NOT_OPEN",iErrLen); + break; + case EL755__FORCED_CLOSED: + strncpy(error,"EL755__FORCED_CLOSED",iErrLen); + break; + case EL755__BAD_TMO: + strncpy(error,"EL755__BAD_TMO",iErrLen); + break; + case EL755__BAD_SOCKET: + strncpy(error,"EL755__BAD_SOCKET",iErrLen); + break; + case EL755__BAD_PAR: + strncpy(error,"EL755__BAD_PAR",iErrLen); + break; + case EL755__BAD_OFL: + strncpy(error,"EL755__BAD_OFL",iErrLen); + break; + case EL755__BAD_MALLOC: + strncpy(error,"EL755__BAD_MALLOC",iErrLen); + break; + case EL755__BAD_ILLG: + strncpy(error,"EL755__BAD_ILLG",iErrLen); + break; + case EL755__BAD_DEV: + strncpy(error,"EL755__BAD_DEV",iErrLen); + break; + case EL755__BAD_CMD: + strncpy(error,"EL755__BAD_CMD",iErrLen); + break; + case EL755__BAD_ASYNSRV: + strncpy(error,"EL755__BAD_ASYNSRV",iErrLen); + break; + default: + sprintf(pBueffel,"Unknown error %d found",*iCode); + strncpy(error,pBueffel,iErrLen); + break; + } + + return 1; + } +/*-----------------------------------------------------------------------*/ + int EL755_Send(void **handle, char *pCom, char *reply, int iLen); + /* + * added to el755_utility by M.K. + */ +/*--------------------------------------------------------------------------*/ + static int EL755Send(pEVDriver self, char *pCommand, char *pReply, int iLen) + { + pEL755Driv pMe = NULL; + char *pPtr = NULL; + char pBueffel[132]; + int iRet; + + assert(self); + pMe = (pEL755Driv)self->pPrivate; + assert(pMe); + + if(strlen(pCommand) > 130) + return 0; + + /* make sure that we have a \r at the end */ + strcpy(pBueffel,pCommand); + if(strrchr(pBueffel,(int)'\r') == NULL) + { + strcat(pBueffel,"\r"); + } + + return EL755_Send(&(pMe->pData),pBueffel,pReply,iLen); + } +/*--------------------------------------------------------------------------*/ + static int EL755Init(pEVDriver self) + { + pEL755Driv pMe = NULL; + int iRet; + + assert(self); + pMe = (pEL755Driv )self->pPrivate; + assert(pMe); + + pMe->pData = NULL; + iRet = EL755_Open(&(pMe->pData),pMe->pHost,pMe->iPort,pMe->iChannel, + pMe->iIndex); + return iRet; + } +/*--------------------------------------------------------------------------*/ + static int EL755Close(pEVDriver self) + { + pEL755Driv pMe = NULL; + int iRet; + + assert(self); + pMe = (pEL755Driv )self->pPrivate; + assert(pMe); + + EL755_Close(&(pMe->pData),0); + return 1; + } +/*---------------------------------------------------------------------------*/ + static int EL755Fix(pEVDriver self, int iError) + { + pEL755Driv pMe = NULL; + int iRet; + + assert(self); + pMe = (pEL755Driv )self->pPrivate; + assert(pMe); + + switch(iError) + { + case EL755__TURNED_OFF: + case EL755__TOO_MANY: + case EL755__TOO_LARGE: + case EL755__OUT_OF_RANGE: + case EL755__BAD_PAR: + case EL755__BAD_SOCKET: + case EL755__BAD_MALLOC: + case EL755__BAD_DEV: + case EL755__BAD_CMD: + case EL755__BAD_ASYNSRV: + return DEVFAULT; + break; + case EL755__OVFLOW: + case EL755__BAD_TMO: + case EL755__BAD_ILLG: + return DEVREDO; + break; + case EL755__OFFLINE: + case EL755__BAD_OFL: + EL755_PutOnline(&(pMe->pData),2); + return DEVREDO; + break; + case EL755__NO_SOCKET: + case EL755__NOT_OPEN: + case EL755__FORCED_CLOSED: + EL755_Open(&(pMe->pData),pMe->pHost,pMe->iPort, + pMe->iChannel,pMe->iIndex); + return DEVREDO; + break; + default: + return DEVFAULT; + break; + } + + } + +/*--------------------------------------------------------------------------*/ + static int EL755Halt(pEVDriver *self) + { + assert(self); + + return 1; + } +/*------------------------------------------------------------------------*/ + void KillEL755(void *pData) + { + pEL755Driv pMe = NULL; + + pMe = (pEL755Driv)pData; + assert(pMe); + + if(pMe->pHost) + { + free(pMe->pHost); + } + free(pMe); + } +/*------------------------------------------------------------------------*/ + pEVDriver CreateEL755Driv(int argc, char *argv[]) + { + pEVDriver pNew = NULL; + pEL755Driv pSim = NULL; + + /* check for arguments */ + if(argc < 4) + { + return NULL; + } + + pNew = CreateEVDriver(argc,argv); + pSim = (pEL755Driv)malloc(sizeof(EL755Driv)); + memset(pSim,0,sizeof(EL755Driv)); + if(!pNew || !pSim) + { + return NULL; + } + pNew->pPrivate = pSim; + pNew->KillPrivate = KillEL755; + + pSim->iLastError = 0; + pSim->pHost = strdup(argv[0]); + pSim->iPort = atoi(argv[1]); + pSim->iChannel = atoi(argv[2]); + pSim->iIndex = atoi(argv[3]); + + /* initialise function pointers */ + pNew->SetValue = EL755Run; + pNew->GetValue = GetEL755Pos; + pNew->Send = EL755Send; + pNew->GetError = EL755Error; + pNew->TryFixIt = EL755Fix; + pNew->Init = EL755Init; + pNew->Close = EL755Close; + + return pNew; + } + + + diff --git a/el755driv.h b/el755driv.h new file mode 100644 index 0000000..22ec747 --- /dev/null +++ b/el755driv.h @@ -0,0 +1,15 @@ +/*------------------------------------------------------------------------ + E L 7 5 5 D R I V + + A environment control driver for the EL755 magnet + controller. + + Mark Koennecke, November 1999 + + copyright: see copyright.h +---------------------------------------------------------------------------*/ +#ifndef EL755DRIV +#define EL755DRIV + pEVDriver CreateEL755Driv(int argc, char *argv[]); + +#endif diff --git a/faverage.c b/faverage.c new file mode 100644 index 0000000..5cb03a3 --- /dev/null +++ b/faverage.c @@ -0,0 +1,557 @@ +/*--------------------------------------------------------------------------- + F o c u s A v e r a g e r + + A little averager for FOCUS data. Used by the FOCUS status display. + + copyright: see copyright.h + + Mark Koennecke, October 1998 + + Updated for additional detector banks + + Mark Koennecke, March 2000 + + Added focusraw command for retrieving single detector banks in support + of the colour mapping part of the FOCUS status display. + + Mark Koennecke, July 2001 +---------------------------------------------------------------------------*/ +#include +#include +#include +#include +#include "fortify.h" +#include "sics.h" +#include "sicsvar.h" +#include "counter.h" +#include "HistMem.h" +#include "fomerge.h" +#include "faverage.h" + +/* +#define DEB 1 +*/ +/*-------------------------------------------------------------------------*/ + typedef struct __FocusAverager { + pObjectDescriptor pDes; + pHistMem pHistogram1; + pHistMem pHistogram2; + pHistMem pHistogram3; + } FocusAverager, *pFocusAverager; + +/*------------------------------------------------------------------------*/ + + HistInt *CheckBank(pFocusAverager self, SConnection *pCon, + int iLength, int iBank); + + static void KillFA(void *pData) + { + pFocusAverager self = NULL; + + self = (pFocusAverager)pData; + if(!self) + return; + + if(self->pDes) + DeleteDescriptor(self->pDes); + free(self); + } +/*-------------------------------------------------------------------------*/ + int FocusAverageDo(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]) + { + pFocusAverager self = NULL; + int *iData = NULL; + const float *fTimeBin = NULL; + float fVal; + int iLength, iStart, iEnd, iNum, i,ii, iTest, iBufLen, iRet, iVal; + char pBueffel[256]; + HistInt *hiData = NULL, *hiPtr; + time_t tStart, tEnd; + int iBank = MIDDLE; + pSicsVariable var1 = NULL; + pSicsVariable var2 = NULL; + pSicsVariable var3 = NULL; + int lbank, mbank, ubank; + + self = (pFocusAverager)pData; + assert(self); + assert(pCon); + assert(pSics); + + /* we need two parameters: start and end of averaging */ + if(argc < 3) + { + SCWrite(pCon, + "ERROR: insufficient number of parameters for FocusAverage", + eError); + return 0; + } + iRet = Tcl_GetInt(pSics->pTcl,argv[1],&iStart); + if(iRet != TCL_OK) + { + sprintf(pBueffel,"ERROR: cannot convert %d to integer",argv[1]); + SCWrite(pCon,pBueffel,eError); + return 0; + } + iRet = Tcl_GetInt(pSics->pTcl,argv[2],&iEnd); + if(iRet != TCL_OK) + { + sprintf(pBueffel,"ERROR: cannot convert %d to integer",argv[2]); + SCWrite(pCon,pBueffel,eError); + return 0; + } + + /* another parameter, if available describes the detector bank + */ + if(argc > 3) + { + iRet = Tcl_GetInt(pSics->pTcl,argv[3],&iBank); + if(iRet != TCL_OK) + { + sprintf(pBueffel,"ERROR: cannot convert %d to integer",argv[3]); + SCWrite(pCon,pBueffel,eError); + return 0; + } + } + + /* how much to do: correct parameters? */ + iNum = iEnd - iStart; + if(iNum < 0) + { + SCWrite(pCon,"ERROR: invalid parameters given to FocusAverage", + eError); + return 0; + } + /* may be only one histogram requested */ + if(iNum == 0) + iNum = 1; + if(iStart < 0) + { + SCWrite(pCon,"ERROR: invalid parameters given to FocusAverage", + eError); + return 0; + } + +#ifdef DEB + printf("Starting averaging ...\n"); + fflush(stdout); + tStart = time(NULL); +#endif + + /* do work! first retrieve time binning data */ + + var2 = FindVariable(pServ->pSics,"mbank"); + if(var2) + { + VarGetInt(var2,&mbank); + } else { + SCWrite(pCon,"ERROR: mbank value not found!",eError); + } + if(mbank==1) + { + fTimeBin = GetHistTimeBin(self->pHistogram2,&iLength); + } + else + { + var1 = FindVariable(pServ->pSics,"lbank"); + if(var1) + { + VarGetInt(var1,&lbank); + } else { + SCWrite(pCon,"ERROR: lbank value not found!",eError); + } + if(lbank==1) + { + fTimeBin = GetHistTimeBin(self->pHistogram1,&iLength); + } + else + { + fTimeBin = GetHistTimeBin(self->pHistogram3,&iLength); + } + } + assert(fTimeBin); + if(iLength <= 0) + { + SCWrite(pCon,"ERROR: histogram memory inproperly configured",eError); + return 0; + } + /* allocate result data */ + iBufLen = (iLength *2 +1)*sizeof(int); + iData = (int *)malloc(iBufLen); + memset(iData,0,iBufLen); + + /* get histogram length */ + i = getFMdim(iBank); + /* correct iEnd to maximum allowed */ + iTest = i; + if(iEnd > iTest -1) + { + iEnd = iTest - 1; + iNum = iEnd - iStart; + if(iNum <= 0) + iNum = 1; + } + +#ifdef DEB + printf("Getting histogram....\n"); + fflush(stdout); +#endif + + hiData = CheckBank(self,pCon,iLength,iBank); + +#ifdef DEB + tEnd = time(NULL); + printf("Histogram received in %d seconds\n", tStart - tEnd); + fflush(stdout); +#endif + + if(hiData == NULL) + { + SCWrite(pCon,"ERROR: BAD Configuration",eError); + free(iData); + return 0; + } + + /* first int: length of things to come */ + iData[0] = htonl(iLength); + /* sum up */ + for(i = iStart; i < iEnd; i++) + { + hiPtr = hiData + i*iLength; + for(ii = 0; ii < iLength; ii++) + { + iData[ii+1] += hiPtr[ii]; + } + } + /* average */ + for(i = 1; i < iLength + 1; i++) + { + fVal = (float)iData[i]/(float)iNum; + fVal *= 65536.; + iData[i] = htonl((int)fVal); + } + /* make time binning fixed point */ + for(i = 0; i < iLength; i++) + { + fVal = fTimeBin[i]/10.; + fVal *= 65536.; + iData[iLength+1+i] = htonl((int)fVal); + } +#ifdef DEB + printf("Sending averaged data....\n"); + fflush(stdout); +#endif + /* finally send out uuencoded */ + SCWriteUUencoded(pCon,"FocusAverage",iData,iBufLen); + if(iData) + free(iData); +#ifdef DEB + printf("Averaging finished\n"); + fflush(stdout); +#endif + return 1; + } + +/*-------------------------------------------------------------------------*/ + static int FocusRaw(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]) + { + pFocusAverager self = NULL; + int *iData = NULL; + int iLength, noTimebin, iRet, i; + char pBueffel[256]; + const float *timeBin; + HistInt *hiData = NULL, *hiPtr; + int iBank = MIDDLE; + pSicsVariable var1 = NULL; + pSicsVariable var2 = NULL; + pSicsVariable var3 = NULL; + int lbank, mbank, ubank; + + self = (pFocusAverager)pData; + assert(self); + assert(pCon); + assert(pSics); + + + /* we need one parameter, the bank to read */ + if(argc < 2) + { + SCWrite(pCon, + "ERROR: insufficient number of parameters for FocusRaw", + eError); + return 0; + } + iRet = Tcl_GetInt(pSics->pTcl,argv[1],&iBank); + if(iRet != TCL_OK) + { + sprintf(pBueffel,"ERROR: cannot convert %d to integer",argv[1]); + SCWrite(pCon,pBueffel,eError); + return 0; + } + var2 = FindVariable(pServ->pSics,"mbank"); + if(var2) + { + VarGetInt(var2,&mbank); + } else { + SCWrite(pCon,"ERROR: mbank value not found!",eError); + } + if(var2) + { + timeBin = GetHistTimeBin(self->pHistogram2,&iLength); + } + else + { + var1 = FindVariable(pServ->pSics,"lbank"); + if(var1) + { + VarGetInt(var1,&lbank); + } else { + SCWrite(pCon,"ERROR: lbank value not found!",eError); + } + if(var1) + { + timeBin = GetHistTimeBin(self->pHistogram1,&iLength); + } + else + { + timeBin = GetHistTimeBin(self->pHistogram3,&iLength); + } + } + assert(timeBin); + hiData = CheckBank(self, pCon, iLength, iBank); + + /* get histogram length */ + iLength = getFMdim(iBank); + noTimebin = getFMdim(TIMEBIN); + /* write dimension info*/ + sprintf(pBueffel,"focusrawdim = %d = %d", iLength, noTimebin); + SCWrite(pCon,pBueffel,eValue); + + /* allocate space */ + iData = (int *)malloc((iLength*noTimebin+1)*sizeof(int)); + if(iData == NULL) + { + SCWrite(pCon,"ERROR: out of memory in FocusRaw",eError); + return 0; + } + memset(iData,0,noTimebin*iLength*sizeof(int)); + + /* first int: length of things to come */ + iData[0] = htonl(iLength*noTimebin); + /* network byte order for everything */ + for(i = 0; i < noTimebin*iLength; i++) + { + iData[i+1] = htonl(hiData[i]); + } + /* send away, zipped */ + SCWriteZipped(pCon,"focusraw",iData,(iLength*noTimebin+1)*sizeof(int)); + + free(iData); + return 1; + } +/*-------------------------------------------------------------------------*/ + int MakeFA(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]) + { + pFocusAverager pNew = NULL; + CommandList *pCom = NULL; + pDummy pDum = NULL; + char pBueffel[256]; + int iRet; + pSicsVariable var1 = NULL; + pSicsVariable var2 = NULL; + pSicsVariable var3 = NULL; + int lbank, mbank, ubank; + + assert(pCon); + assert(pSics); + + /* we need two parameters: the name for the averager and the histogram + memory + */ + if(argc < 3) + { + SCWrite(pCon,"ERROR: Insufficient number of parameters to MakeFA", + eError); + return 0; + } + + /* find histogram memory */ + pCom = FindCommand(pSics,argv[2]); + if(!pCom) + { + sprintf(pBueffel,"ERROR: histogram memory %s NOT found!", argv[2]); + SCWrite(pCon,pBueffel,eError); + return 0; + } + pDum = (pDummy)pCom->pData; + if(!pDum) + { + sprintf(pBueffel,"ERROR: histogram memory %s INVALID!", argv[2]); + SCWrite(pCon,pBueffel,eError); + return 0; + } + if(strcmp(pDum->pDescriptor->name,"HMcontrol") != 0) + { + sprintf(pBueffel,"ERROR: %s is NO histogram control object!", argv[2]); + SCWrite(pCon,pBueffel,eError); + return 0; + } + + /* we got what we need: set things up */ + pNew = (pFocusAverager)malloc(sizeof(FocusAverager)); + if(!pNew) + { + SCWrite(pCon,"ERROR: out of memory in MakeFA",eError); + return 0; + } + memset(pNew,0,sizeof(FocusAverager)); + + pNew->pDes = CreateDescriptor("FocusAverager"); + if(!pNew->pDes) + { + SCWrite(pCon,"ERROR: out of memory in MakeFA",eError); + return 0; + } + var2 = FindVariable(pServ->pSics,"mbank"); + if(var2) + { + VarGetInt(var2,&mbank); + } else { + SCWrite(pCon,"ERROR: mbank value not found!",eError); + } + if(mbank==1) + { + pCom = FindCommand(pSics,"hm2"); + pDum = (pDummy)pCom->pData; + pNew->pHistogram2 = (pHistMem)pDum; + } + var1 = FindVariable(pServ->pSics,"lbank"); + if(var1) + { + VarGetInt(var1,&lbank); + } else { + SCWrite(pCon,"ERROR: lbank value not found!",eError); + } + if(lbank==1) + { + pCom = FindCommand(pSics,"hm1"); + pDum = (pDummy)pCom->pData; + pNew->pHistogram1 = (pHistMem)pDum; + } + var3 = FindVariable(pServ->pSics,"ubank"); + if(var3) + { + VarGetInt(var3,&ubank); + } else { + SCWrite(pCon,"ERROR: ubank value not found!",eError); + } + if(ubank==1) + { + pCom = FindCommand(pSics,"hm3"); + pDum = (pDummy)pCom->pData; + pNew->pHistogram3 = (pHistMem)pDum; + } + iRet = AddCommand(pSics,argv[1],FocusAverageDo, KillFA, pNew); + if(!iRet) + { + sprintf(pBueffel,"ERROR: duplicate command %s not created", argv[1]); + SCWrite(pCon,pBueffel,eError); + KillFA(pNew); + return 0; + } + iRet = AddCommand(pSics,"focusraw",FocusRaw, NULL, pNew); + if(!iRet) + { + sprintf(pBueffel,"ERROR: duplicate command focusraw not created"); + SCWrite(pCon,pBueffel,eError); + return 0; + } + return 1; + } + +HistInt *CheckBank(pFocusAverager self, SConnection *pCon, + int iLength, int iBank) +{ + + pSicsVariable var1 = NULL; + pSicsVariable var2 = NULL; + pSicsVariable var3 = NULL; + HistInt *lData = NULL; + HistInt *mData = NULL; + HistInt *uData = NULL; + HistInt *mergData = NULL; + int lbank, mbank, ubank; + + if (iBank==2) + { + var2 = FindVariable(pServ->pSics,"mbank"); + if(var2) + { + VarGetInt(var2,&mbank); + } else { + SCWrite(pCon,"ERROR: mbank value not found!",eError); + } + if(mbank==1) + { + mData = GetHistogramPointer(self->pHistogram2,pCon); + if(mData == NULL) + { + return NULL; + } + setFMDataPointer(mData, iLength,2); + mData = getFMBankPointer(2); + return mData; + } + } + if (iBank==3) + { + var1 = FindVariable(pServ->pSics,"lbank"); + if(var1) + { + VarGetInt(var1,&lbank); + } else { + SCWrite(pCon,"ERROR: lbank value not found!",eError); + } + if(lbank==1) + { + lData = GetHistogramPointer(self->pHistogram1,pCon); + if(lData == NULL) + { + return NULL; + } + setFMDataPointer(lData, iLength, 3); + lData = getFMBankPointer(3); + return lData; + } + } + if (iBank==1) + { + var3 = FindVariable(pServ->pSics,"ubank"); + if(var3) + { + VarGetInt(var3,&ubank); + } else { + SCWrite(pCon,"ERROR: ubank value not found!",eError); + } + if(ubank==1) + { + uData = GetHistogramPointer(self->pHistogram3,pCon); + if(uData == NULL) + { + return NULL; + } + setFMDataPointer(uData, iLength, 1); + uData = getFMBankPointer(1); + return uData; + } + } + if (iBank==4) + { + setFMDataPointer(mergData, iLength,4); + mergData = getFMBankPointer(4); + return mergData; + } +} diff --git a/faverage.h b/faverage.h new file mode 100644 index 0000000..220beb1 --- /dev/null +++ b/faverage.h @@ -0,0 +1,21 @@ + +/*----------------------------------------------------------------------- + F o c u s A v e r a g e + + An averager for FOCUS data. See faverage.tex for more details. + + Mark Koennecke, October 1998 + +--------------------------------------------------------------------------*/ +#ifndef FOCUSAVERAGE +#define FOCUSAVERAGE + + int MakeFA(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]); + + int FocusAverageDo(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]); + + +#endif + diff --git a/faverage.tex b/faverage.tex new file mode 100644 index 0000000..b337277 --- /dev/null +++ b/faverage.tex @@ -0,0 +1,70 @@ +\subsection{The FOCUS Averager} +This is a special object for the instrument FOCUS and its Status display +client. In the FOCUS status display the averaged data from a number of +detectors is displayed. Thus there is already a reduced form of data. The +actual raw data would be 150*1024*4 bytes of data and possibly more. Rather +then transporting all this data to the status display client at regular +intervalls it was choosen to implement this averaging process at the server +and only send the reduced form to the status display client. Which is two +arrays of floating point data 1024 items long. This little object implements this +averager. + +As all SICS objects this object has a little data structure: +\begin{verbatim} + typedef struct __FocusAverager { + pObjectDescriptor pDes; + pHistMem pHist; + } FocusAverager; +\end{verbatim} + +The two fields are the standard object descriptor and a pointer to the +histogram memory object holding the data. + +The interface is minimal: it consists just of the factory function for +installing this object into SICS and the actual function doing the +averaging in the interpreter. + +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap1} +$\langle$faint {\footnotesize ?}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@@\\ +\mbox{}\verb@ int MakeFA(SConnection *pCon, SicsInterp *pSics, void *pData,@\\ +\mbox{}\verb@ int argc, char *argv[]);@\\ +\mbox{}\verb@@\\ +\mbox{}\verb@ int FocusAverageDo(SConnection *pCon, SicsInterp *pSics, void *pData,@\\ +\mbox{}\verb@ int argc, char *argv[]);@\\ +\mbox{}\verb@@\\ +\mbox{}\verb@@$\diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro referenced in scrap ?. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap2} +\verb@"faverage.h"@ {\footnotesize ? }$\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@@\\ +\mbox{}\verb@/*-----------------------------------------------------------------------@\\ +\mbox{}\verb@ F o c u s A v e r a g e@\\ +\mbox{}\verb@@\\ +\mbox{}\verb@ An averager for FOCUS data. See faverage.tex for more details.@\\ +\mbox{}\verb@@\\ +\mbox{}\verb@ Mark Koennecke, October 1998@\\ +\mbox{}\verb@@\\ +\mbox{}\verb@--------------------------------------------------------------------------*/@\\ +\mbox{}\verb@#ifndef FOCUSAVERAGE@\\ +\mbox{}\verb@#define FOCUSAVERAGE@\\ +\mbox{}\verb@@$\langle$faint {\footnotesize ?}$\rangle$\verb@@\\ +\mbox{}\verb@#endif@\\ +\mbox{}\verb@@$\diamond$ +\end{list} +\vspace{-2ex} +\end{minipage}\\[4ex] +\end{flushleft} diff --git a/faverage.w b/faverage.w new file mode 100644 index 0000000..808f56d --- /dev/null +++ b/faverage.w @@ -0,0 +1,50 @@ +\subsection{The FOCUS Averager} +This is a special object for the instrument FOCUS and its Status display +client. In the FOCUS status display the averaged data from a number of +detectors is displayed. Thus there is already a reduced form of data. The +actual raw data would be 150*1024*4 bytes of data and possibly more. Rather +then transporting all this data to the status display client at regular +intervalls it was choosen to implement this averaging process at the server +and only send the reduced form to the status display client. Which is two +arrays of floating point data 1024 items long. This little object implements this +averager. + +As all SICS objects this object has a little data structure: +\begin{verbatim} + typedef struct __FocusAverager { + pObjectDescriptor pDes; + pHistMem pHist; + } FocusAverager; +\end{verbatim} + +The two fields are the standard object descriptor and a pointer to the +histogram memory object holding the data. + +The interface is minimal: it consists just of the factory function for +installing this object into SICS and the actual function doing the +averaging in the interpreter. + +@d faint @{ + int MakeFA(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]); + + int FocusAverageDo(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]); + +@} + +@o faverage.h @{ +/*----------------------------------------------------------------------- + F o c u s A v e r a g e + + An averager for FOCUS data. See faverage.tex for more details. + + Mark Koennecke, October 1998 + +--------------------------------------------------------------------------*/ +#ifndef FOCUSAVERAGE +#define FOCUSAVERAGE +@ +#endif +@} + diff --git a/fowrite.c b/fowrite.c new file mode 100644 index 0000000..14f9cba --- /dev/null +++ b/fowrite.c @@ -0,0 +1,1195 @@ +/*--------------------------------------------------------------------------- + F O W R I T E + + FOCUS data writing object. + + copyright: see copyright.h + + Mark Koennecke, November 1998 + + Added code for three detector banks. + + Mark Koennecke, March 2000 +-----------------------------------------------------------------------------*/ +#include +#include +#include +#include +/* avoid irritating compiler warning M.Z.08.2001 */ +#undef VOID +#include "fortify.h" +#include "sics.h" +#include "event.h" +#include "counter.h" +#include "HistMem.h" +#include "nxdict.h" +#include "nxutil.h" +#include "motor.h" +#include "selector.h" +#include "fowrite.h" +#include "scan.h" +#include "sicsvar.h" +#include "fitcenter.h" +#include "hmcontrol.h" +#include "fomerge.h" + +/* histogram memory names */ +#define HM1 "hm1" +#define HM2 "hm2" +#define HM3 "hm3" + + +/* the name of the SICS chopper controller object */ +#define CHOPPERNAME "choco" + +/*--------- the internal data structure ------------------------------------*/ + typedef struct { + pObjectDescriptor pDes; + pHistMem pHistogram1, pHistogram2, pHistogram3; + int iNew; + time_t tUpdate; + int iInterval; + int iEnd; + SConnection *pCon; + pCounter pCount; + char *pFile; + char *pDictFile; + pFit pFitter; + float fElastic; + pICallBack pCall; + int iUpper, iMiddle, iLower; + /* detector availability flags */ + } FoWrite, *pFoWrite; +/* ------------------- forward declaration of task function --------------*/ + + static int FoTask(void *pData); + static void FoUpdate(pFoWrite self, SConnection *pCon); + + +/*------------------ The Countstart Callback Function ----------------------*/ + static int Countstartcallback(int iEvent, void *pEventData, void *pUser) + { + pFoWrite self = NULL; + + if(iEvent == COUNTSTART) + { + self = (pFoWrite)pUser; + assert(self); + self->iNew = 1; + self->iEnd = 0; + self->tUpdate = time(NULL); + self->pCon = (SConnection *)pEventData; + TaskRegister(pServ->pTasker,FoTask,NULL,NULL,self,1); + return 1; + } + return 1; + } +/*------------------ The Countend Callback Function ----------------------*/ + static int Countendcallback(int iEvent, void *pEventData, void *pUser) + { + pFoWrite self = NULL; + + if(iEvent == COUNTEND) + { + self = (pFoWrite)pUser; + assert(self); + self->tUpdate = time(NULL); + self->iEnd = 1; + /* + FoUpdate(self,self->pCon); + */ + return 1; + } + return 1; + } +/*-----------------------------------------------------------------------*/ + static void SNError(void *pData, char *text) + { + SConnection *pCon; + + assert(pData); + pCon = (SConnection *)pData; + SCWrite(pCon,text,eError); + } +/*------------------------------------------------------------------------*/ + static void WriteSelector(NXhandle pFile, NXdict pDict, SConnection *pCon) + { + pSicsSelector pSel = NULL; + char *pName = NULL; + CommandList *pCom = NULL; + pDummy pDum = NULL; + float fTh, fTTH, fB1, fB2; + int iRet; + + pCom = FindCommand(pServ->pSics,"mono"); + if(!pCom) + { + SCWrite(pCon,"ERROR: no monochromator found",eError); + return ; + } + pSel = (pSicsSelector)pCom->pData; + if(!pSel) + { + SCWrite(pCon,"ERROR: no monochromator found",eError); + return ; + } + pDum = (pDummy)pSel; + if(strcmp(pDum->pDescriptor->name,"CrystalSelector") != 0) + { + SCWrite(pCon,"ERROR: monochromator is invalid",eError); + return ; + } + + NXDputalias(pFile,pDict,"mname",MonoGetType(pSel)); + iRet = GetMonoPositions(pSel,pCon,&fTh, &fTTH, &fB1, &fB2); + if(!iRet) + { + SCWrite(pCon,"ERROR: Problem reading monochromator positions",eError); + SCWrite(pCon,"ERROR: monochromator data missing in file",eError); + return; + } + NXDputalias(pFile,pDict,"mtheta",&fTh); + NXDputalias(pFile,pDict,"mttheta",&fTTH); + SNXSPutDrivable(pServ->pSics,pCon, pFile,pDict,"lambda","mlambda"); + SNXSPutDrivable(pServ->pSics,pCon, pFile,pDict,"qi","menergy"); + } +/*----------------------------------------------------------------------*/ + static float CalculateElastic(pFoWrite self, SConnection *pCon) + { + pIDrivable pDriv; + pSicsVariable pVar; + CommandList *pCom = NULL; + float fLambda, fDist, fResult; + + pCom = FindCommand(pServ->pSics,"lambda"); + if(!pCom) + return 0.; + pDriv = GetDrivableInterface(pCom->pData); + if(!pDriv) + return 0.; + fLambda = pDriv->GetValue(pCom->pData,pCon); + pVar = FindVariable(pServ->pSics,"sampledist"); + if(!pVar) + return 0.; + fDist = pVar->fVal; + pVar = FindVariable(pServ->pSics,"detectordist"); + if(!pVar) + return 0.; + fDist += pVar->fVal; + fResult = 252.78*fLambda*(fDist/1000.); + return fResult; + } +/*------------------------------------------------------------------------- + FoStart writes all the fixed data items, creates a new file etc. + A complete file is obtained after FoStart plus a call to FoUpdate +*/ + static int FoStart(pFoWrite self, SConnection *pCon) + { + NXhandle pFile = NULL; + NXdict pDict = NULL; + pSicsVariable var1 = NULL; + pSicsVariable var2 = NULL; + int lbank, mbank; + int iStat, iLength, i; + char pBueffel[512]; + CounterMode eMode; + float fVal, *fArray; + const float *fTime; + float *fTime2 = NULL; + char pBuffer[50]; + + /* get a filename */ + if(self->pFile) + free(self->pFile); + + self->pFile = SNXMakeFileName(pServ->pSics,pCon); + if(!self->pFile) + { + SCWrite(pCon,"ERROR: Extra severe: failed to create data file name", + eError); + return 0; + } + + /* create a Nexus file */ + NXopen(self->pFile,NXACC_CREATE,&pFile); + if(!pFile) + { + SCWrite(pCon,"ERROR: cannot create data file ",eError); + return 0; + } + + /* tell Uwe User what we are doing */ + sprintf(pBueffel,"Writing %s ......",self->pFile); + SCWrite(pCon,pBueffel,eWarning); + + /* write globals */ + SNXSPutGlobals(pFile,self->pFile,"FOCUS",pCon); + + /* open nxdict and configure nxdict parameters */ + iStat = NXDinitfromfile(self->pDictFile,&pDict); + if(iStat != NX_OK) + { + sprintf(pBueffel,"ERROR: failed to open dictionary file %s", + self->pDictFile); + SCWrite(pCon,pBueffel,eError); + SCWrite(pCon,"ERROR: Aborting data file writing",eError); + SCWrite(pCon,"ERROR: This is a SERIOUS problem!",eError); + SCWrite(pCon,"ERROR: DATA NOT WRITTEN",eError); + NXclose(&pFile); + return 0; + } + + /* put permanent data */ + SNXSPutVariable(pServ->pSics,pCon,pFile,pDict,"etitle","title"); + SNXFormatTime(pBueffel,511); + + /* entry & instrument stuff */ + NXDputalias(pFile,pDict,"estart",pBueffel); + SNXSPutVariable(pServ->pSics,pCon,pFile,pDict,"iname","instrument"); + NXDputalias(pFile,pDict,"sname","SINQ, PSI, Switzerland"); + NXDputalias(pFile,pDict,"stype","continous spallation source"); + + /* disk chopper */ + NXDputalias(pFile,pDict,"cname","Dornier disk chopper"); + + /* be-filter */ + NXDputalias(pFile,pDict,"bname","BE-filter"); + SNXSPutVariable(pServ->pSics,pCon,pFile,pDict,"bstatus","bestatus"); + + /* flight path */ + SNXSPutVariable(pServ->pSics,pCon,pFile,pDict,"fltype","flightpath"); + SNXSPutVariable(pServ->pSics,pCon,pFile,pDict,"fllength","flightpathlength"); + + /* monochromator */ + WriteSelector(pFile,pDict,pCon); + + /* fermi chupper */ + SNXSPutVariable(pServ->pSics,pCon,pFile,pDict,"fcname","ferminame"); + SNXSPutVariable(pServ->pSics,pCon,pFile,pDict,"fcdist","fermidist"); + + /* counting data */ + + var2 = FindVariable(pServ->pSics,"mbank"); + if(var2) + { + VarGetInt(var2,&mbank); + } else { + SCWrite(pCon,"ERROR: mbank value not found!",eError); + } + if(var2) + { + eMode = GetHistCountMode(self->pHistogram2); + fTime = GetHistTimeBin(self->pHistogram2,&iLength); + fVal = GetHistPreset(self->pHistogram2); + } + else + { + var1 = FindVariable(pServ->pSics,"lbank"); + if(var1) + { + VarGetInt(var1,&lbank); + } else { + SCWrite(pCon,"ERROR: lbank value not found!",eError); + } + if(var1) + { + eMode = GetHistCountMode(self->pHistogram1); + fTime = GetHistTimeBin(self->pHistogram1,&iLength); + fVal = GetHistPreset(self->pHistogram1); + } + else + { + eMode = GetHistCountMode(self->pHistogram3); + fTime = GetHistTimeBin(self->pHistogram3,&iLength); + fVal = GetHistPreset(self->pHistogram3); + } + } + + if(eMode == eTimer) + { + strcpy(pBueffel,"timer"); + } + else + { + strcpy(pBueffel,"monitor"); + } + NXDputalias(pFile,pDict,"cnmode",pBueffel); + NXDputalias(pFile,pDict,"cnpreset",&fVal); + + /* detector banks */ + fTime2 = (float *)malloc(iLength*sizeof(float)); + if(fTime2) + { + for(i = 0; i < iLength; i++) + { + fTime2[i] = fTime[i]/10.; + } + sprintf(pBueffel,"%d",iLength); + NXDupdate(pDict,"timebin",pBueffel); + if(self->iMiddle) + { + NXDupdate(pDict,"bank","bank1"); + NXDputalias(pFile,pDict,"dtime",fTime2); + } + if(self->iUpper) + { + NXDupdate(pDict,"bank","upperbank"); + NXDputalias(pFile,pDict,"dtime",fTime2); + } + if(self->iLower) + { + NXDupdate(pDict,"bank","lowerbank"); + NXDputalias(pFile,pDict,"dtime",fTime2); + } + if( (self->iLower || self->iUpper) && self->iMiddle) + { + NXDupdate(pDict,"bank","merged"); + NXDputalias(pFile,pDict,"dtime",fTime2); + } + NXDupdate(pDict,"bank","bank1"); + + + /* calculate theoretical position of elastic peak */ + fVal = CalculateElastic(self,pCon); + self->fElastic = (fVal - fTime2[0]) / (fTime2[1] - fTime2[0]); + free(fTime2); + fTime2 = NULL; + } + else + { + SCWrite(pCon,"ERROR: out of memory while writing time binning", + eError); + } + + SNXSPutVariable(pServ->pSics,pCon,pFile,pDict,"ddist","detectordist"); + + /* theta arrays */ + if(self->iMiddle) + { + NXDupdate(pDict,"bank","bank1"); + iLength = 150; + sprintf(pBuffer,"%d",iLength); + NXDupdate(pDict,"noofdetectors",pBuffer); + fArray = getFMBankTheta(MIDDLE); + NXDputalias(pFile,pDict,"dtheta",fArray); + } + if(self->iLower) + { + NXDupdate(pDict,"bank","lowerbank"); + iLength = 115; + sprintf(pBuffer,"%d",iLength); + NXDupdate(pDict,"noofdetectors",pBuffer); + fArray = getFMBankTheta(LOWER); + NXDputalias(pFile,pDict,"dtheta",fArray); + } + if(self->iUpper) + { + NXDupdate(pDict,"bank","upperbank"); + iLength = 110; + sprintf(pBuffer,"%d",iLength); + NXDupdate(pDict,"noofdetectors",pBuffer); + fArray = getFMBankTheta(UPPER); + NXDputalias(pFile,pDict,"dtheta",fArray); + } + if(self->iMiddle && ( self->iLower || self->iUpper) ) + { + NXDupdate(pDict,"bank","merged"); + iLength = 375; + sprintf(pBuffer,"%d",iLength); + NXDupdate(pDict,"noofdetectors",pBuffer); + fArray = getFMBankTheta(MERGED); + NXDputalias(pFile,pDict,"dtheta",fArray); + } + + NXDupdate(pDict,"bank","bank1"); + SNXSPutVariable(pServ->pSics,pCon,pFile,pDict,"ddelay","delay"); + + + /* sample info */ + SNXSPutVariable(pServ->pSics,pCon,pFile,pDict,"saname","sample"); + SNXSPutVariable(pServ->pSics,pCon,pFile,pDict,"senvir","environment"); + SNXSPutVariable(pServ->pSics,pCon,pFile,pDict,"sdist","sampledist"); + SNXSPutVariable(pServ->pSics,pCon,pFile,pDict,"saangle","sampleangle"); + + /* close everything */ + NXclose(&pFile); + NXDclose(pDict,NULL); + + } +/*---------------------------------------------------------------------------*/ + static void FoUpdate(pFoWrite self, SConnection *pCon) + { + char pBueffel[512]; + int iInt, iStat, iTime, i,ii, j, iDet, iIndex; + pSicsVariable var1 = NULL; + pSicsVariable var2 = NULL; + pSicsVariable var3 = NULL; + int lbank, mbank, ubank; + long lVal; + float fVal; + const float *fTime; + NXhandle pFile = NULL; + NXdict pDict; + HistInt *lData = NULL; + HistInt *mData = NULL; + HistInt *uData = NULL; + int *iSum = NULL; + float *fAxis = NULL; + long *lSum = NULL; + float fCenter, fStdDev, fFWHM; + + /* open everything again */ + NXopen(self->pFile,NXACC_RDWR,&pFile); + if(!pFile) + { + SCWrite(pCon,"ERROR: cannot reopen data file ",eError); + return; + } + iStat = NXDinitfromfile(self->pDictFile,&pDict); + if(iStat != NX_OK) + { + sprintf(pBueffel,"ERROR: failed to open dictionary file %s", + self->pDictFile); + SCWrite(pCon,pBueffel,eError); + SCWrite(pCon,"ERROR: Aborting data file writing",eError); + SCWrite(pCon,"ERROR: This is a SERIOUS problem!",eError); + SCWrite(pCon,"ERROR: DATA NOT WRITTEN",eError); + NXclose(&pFile); + return; + } + + /* tell the user that something is happening */ + sprintf(pBueffel,"Updating %s",self->pFile); + SCWrite(pCon,pBueffel,eWarning); + + /* do the end time */ + SNXFormatTime(pBueffel,511); + NXDputalias(pFile,pDict,"eend",pBueffel); + + /* chopper speeds */ + SNXSPutDrivable(pServ->pSics,pCon,pFile,pDict,"diskspeed","crot"); + SNXSPutDrivable(pServ->pSics,pCon,pFile,pDict,"fermispeed","fcrot"); + SNXSPutDrivable(pServ->pSics,pCon,pFile,pDict,"phase","fcphase"); + SNXSPutDrivable(pServ->pSics,pCon,pFile,pDict,"ratio","cratio"); + + /* counter data */ + var2 = FindVariable(pServ->pSics,"mbank"); + if(var2) + { + VarGetInt(var2,&mbank); + } else { + SCWrite(pCon,"ERROR: mbank value not found!",eError); + } + if(var2) + { + fVal = GetHistCountTime(self->pHistogram2,pCon); + NXDputalias(pFile,pDict,"cntime",&fVal); + lVal = GetHistMonitor(self->pHistogram2,1,pCon); + NXDputalias(pFile,pDict,"cnmon1",&lVal); + lVal = GetHistMonitor(self->pHistogram2,0,pCon); + NXDputalias(pFile,pDict,"cnmon2",&lVal); + lVal = GetHistMonitor(self->pHistogram2,4,pCon); + NXDputalias(pFile,pDict,"cnmon3",&lVal); + fTime = GetHistTimeBin(self->pHistogram2,&iInt); + } + else + { + var1 = FindVariable(pServ->pSics,"lbank"); + if(var1) + { + VarGetInt(var1,&lbank); + } else { + SCWrite(pCon,"ERROR: lbank value not found!",eError); + } + if(var1) + { + fVal = GetHistCountTime(self->pHistogram1,pCon); + NXDputalias(pFile,pDict,"cntime",&fVal); + lVal = GetHistMonitor(self->pHistogram1,1,pCon); + NXDputalias(pFile,pDict,"cnmon1",&lVal); + lVal = GetHistMonitor(self->pHistogram1,0,pCon); + NXDputalias(pFile,pDict,"cnmon2",&lVal); + lVal = GetHistMonitor(self->pHistogram1,4,pCon); + NXDputalias(pFile,pDict,"cnmon3",&lVal); + fTime = GetHistTimeBin(self->pHistogram1,&iInt); + } + else + { + fVal = GetHistCountTime(self->pHistogram3,pCon); + NXDputalias(pFile,pDict,"cntime",&fVal); + lVal = GetHistMonitor(self->pHistogram3,1,pCon); + NXDputalias(pFile,pDict,"cnmon1",&lVal); + lVal = GetHistMonitor(self->pHistogram3,0,pCon); + NXDputalias(pFile,pDict,"cnmon2",&lVal); + lVal = GetHistMonitor(self->pHistogram3,4,pCon); + NXDputalias(pFile,pDict,"cnmon3",&lVal); + fTime = GetHistTimeBin(self->pHistogram3,&iInt); + } + } + + /* histogram with three detector banks */ + iTime = iInt; + sprintf(pBueffel,"%d",iInt); + NXDupdate(pDict,"timebin",pBueffel); + var1 = FindVariable(pServ->pSics,"lbank"); + if(var1) + { + VarGetInt(var1,&lbank); + } else { + SCWrite(pCon,"ERROR: lbank value not found!",eError); + } + if (var1) + { + lData = GetHistogramPointer(self->pHistogram1,pCon); + if(!lData) + { + SCWrite(pCon,"ERROR: failed to find Histogram Memory Data (lower bank)",eError); + NXclose(&pFile); + NXDclose(pDict,NULL); + return; + } + } + if (var2) + { + mData = GetHistogramPointer(self->pHistogram2,pCon); + if(!mData) + { + SCWrite(pCon,"ERROR: failed to find Histogram Memory Data (middle bank)",eError); + NXclose(&pFile); + NXDclose(pDict,NULL); + return; + } + } + var3 = FindVariable(pServ->pSics,"ubank"); + if(var3) + { + VarGetInt(var3,&ubank); + } else { + SCWrite(pCon,"ERROR: ubank value not found!",eError); + } + if (var3) + { + uData = GetHistogramPointer(self->pHistogram3,pCon); + if(!uData) + { + SCWrite(pCon,"ERROR: failed to find Histogram Memory Data (upper bank)",eError); + NXclose(&pFile); + NXDclose(pDict,NULL); + return; + } + } + setFMDataPointer(lData,iTime,3); + setFMDataPointer(mData,iTime,1); + setFMDataPointer(uData,iTime,2); + /* middle bank */ + if(self->iMiddle) + { + NXDupdate(pDict,"bank","bank1"); + iDet = 150; + sprintf(pBueffel,"%d",iDet); + NXDupdate(pDict,"noofdetectors",pBueffel); + mData = getFMBankPointer(MIDDLE); + NXDputalias(pFile,pDict,"dcounts",mData); + /* summed counts for each detector */ + iSum = (int *)malloc(iDet*sizeof(int)); + if(iSum) + { + memset(iSum,0,iDet*sizeof(int)); + for(i = 0; i < iDet; i++) + { + iIndex = i * iTime; + for(j = 0; j < iTime; j++) + { + iSum[i] += mData[iIndex+j]; + } + } + NXDputalias(pFile,pDict,"dsums",iSum); + free(iSum); + } + else + { + SCWrite(pCon,"WARNING: out of memory, failed to do sums", + eWarning); + } + } + if(self->iUpper) + { + NXDupdate(pDict,"bank","upperbank"); + iDet = 110; + sprintf(pBueffel,"%d",iDet); + NXDupdate(pDict,"noofdetectors",pBueffel); + uData = getFMBankPointer(UPPER); + NXDputalias(pFile,pDict,"dcounts",uData); + /* summed counts for each detector */ + iSum = (int *)malloc(iDet*sizeof(int)); + if(iSum) + { + memset(iSum,0,iDet*sizeof(int)); + for(i = 0; i < iDet; i++) + { + iIndex = i * iTime; + for(j = 0; j < iTime; j++) + { + iSum[i] += uData[iIndex+j]; + } + } + NXDputalias(pFile,pDict,"dsums",iSum); + free(iSum); + } + else + { + SCWrite(pCon,"WARNING: out of memory, failed to do sums", + eWarning); + } + } + if(self->iLower) + { + NXDupdate(pDict,"bank","lowerbank"); + iDet = 115; + sprintf(pBueffel,"%d",iDet); + NXDupdate(pDict,"noofdetectors",pBueffel); + lData = getFMBankPointer(LOWER); + NXDputalias(pFile,pDict,"dcounts",lData); + /* summed counts for each detector */ + iSum = (int *)malloc(iDet*sizeof(int)); + if(iSum) + { + memset(iSum,0,iDet*sizeof(int)); + for(i = 0; i < iDet; i++) + { + iIndex = i * iTime; + for(j = 0; j < iTime; j++) + { + iSum[i] += lData[iIndex+j]; + } + } + NXDputalias(pFile,pDict,"dsums",iSum); + free(iSum); + } + else + { + SCWrite(pCon,"WARNING: out of memory, failed to do sums", + eWarning); + } + /* + now get and write tof_monitor + */ + + lData = (HistInt *)malloc(iTime*sizeof(HistInt)); + if(!lData) + { + SCWrite(pCon,"ERROR: out of memory while writing tof-monitor", + eError); + } + else + { + memset(lData,0,iTime*sizeof(HistInt)); + GetHistogramDirect(self->pHistogram1,pCon,0,115*iTime, + 116*iTime, lData, iTime*sizeof(HistInt)); + NXDputalias(pFile,pDict,"tofmon",lData); + } + } + /* merged data */ + if( (self->iUpper || self->iLower) && self->iMiddle) + { + NXDupdate(pDict,"bank","merged"); + iDet = 375; + sprintf(pBueffel,"%d",iDet); + NXDupdate(pDict,"noofdetectors",pBueffel); + lData = getFMBankPointer(MERGED); + NXDputalias(pFile,pDict,"dcounts",lData); + /* summed counts for each detector */ + iSum = (int *)malloc(iDet*sizeof(int)); + if(iSum) + { + memset(iSum,0,iDet*sizeof(int)); + for(i = 0; i < iDet; i++) + { + iIndex = i * iTime; + for(j = 0; j < iTime; j++) + { + iSum[i] += lData[iIndex+j]; + } + } + NXDputalias(pFile,pDict,"dsums",iSum); + free(iSum); + } + else + { + SCWrite(pCon,"WARNING: out of memory, failed to do sums", + eWarning); + } + } + + + /* calculate elastic peak position */ + NXDupdate(pDict,"bank","bank1"); + mData = getFMBankPointer(MIDDLE); + if(mData) + { + lSum = (long *)malloc(iTime *sizeof(long)); + fAxis = (float *)malloc(iTime *sizeof(float)); + if( lSum && fAxis) + { + memset(lSum,0,iTime*sizeof(long)); + memset(fAxis,0,iTime*sizeof(float)); + for(i = 5; i < iDet - 5; i++) + { + iIndex = i * iTime; + for(j = 0; j < iTime; j++) + { + lSum[j] += mData[iIndex+j]; + } + } + for(i = 0; i < iTime; i++) + { + fAxis[i] = (float)i; + } + iStat = CalculateFitFromData(self->pFitter,fAxis,lSum,iTime); + if(iStat != 1) + { + SCWrite(pCon,"WARNING: problem locating elastic peak",eWarning); + } + GetFitResults(self->pFitter,&fCenter,&fStdDev,&fFWHM,&fVal); + fVal = fCenter - self->fElastic; + if(fVal < 0.) + fVal = - fVal; + /* bad value, leave at theoretical value */ + if(fVal > 10.) + { + SCWrite(pCon, + "WARNING: bad fit result, using theoretical elastic peak position", + eWarning); + } + else + { + self->fElastic = fCenter; + } + free(lSum); + free(fAxis); + } + else + { + SCWrite(pCon,"WARNING: out of memory, failed to do sums",eWarning); + } + } + sprintf(pBueffel,"Elastic peak found at detector: %f",self->fElastic); + SCWrite(pCon,pBueffel,eWarning); + NXDputalias(pFile,pDict,"delastic",&self->fElastic); + + + /* sample temperature */ + SNXSPutEVVar(pFile,pDict,"temperature",pCon,"stemp",NULL); + + + /* close everything */ + NXclose(&pFile); + NXDclose(pDict,NULL); + + } +/*------------------------------------------------------------------------- + FoLink sets all the links for the NXdata vGroup. Had to be separate because + at least one update is necessary before this can be done. +*/ + static void FoLink(pFoWrite self, SConnection *pCon) + { + NXhandle pFile; + NXdict pDict; + int iStat; + char pBueffel[512]; + + /* open everything again */ + NXopen(self->pFile,NXACC_RDWR,&pFile); + if(!pFile) + { + SCWrite(pCon,"ERROR: cannot reopen data file ",eError); + return; + } + iStat = NXDinitfromfile(self->pDictFile,&pDict); + if(iStat != NX_OK) + { + sprintf(pBueffel,"ERROR: failed to open dictionary file %s", + self->pDictFile); + SCWrite(pCon,pBueffel,eError); + SCWrite(pCon,"ERROR: Aborting data file writing",eError); + SCWrite(pCon,"ERROR: This is a SERIOUS problem!",eError); + SCWrite(pCon,"ERROR: DATA NOT WRITTEN",eError); + NXclose(&pFile); + return; + } + + if( (self->iUpper || self->iLower) && self->iMiddle) + { + NXDupdate(pDict,"bank","merged"); + NXDaliaslink(pFile,pDict,"dana","dcounts"); + NXDaliaslink(pFile,pDict,"dana","dtime"); + NXDaliaslink(pFile,pDict,"dana","dtheta"); + NXDaliaslink(pFile,pDict,"dana","cnmon1"); + } + + if(self->iUpper) + { + NXDupdate(pDict,"bank","upperbank"); + NXDaliaslink(pFile,pDict,"dana","dcounts"); + NXDaliaslink(pFile,pDict,"dana","dtime"); + NXDaliaslink(pFile,pDict,"dana","dtheta"); + NXDaliaslink(pFile,pDict,"dana","cnmon1"); + } + if(self->iMiddle) + { + NXDupdate(pDict,"bank","bank1"); + NXDaliaslink(pFile,pDict,"dana","dcounts"); + NXDaliaslink(pFile,pDict,"dana","dtime"); + NXDaliaslink(pFile,pDict,"dana","dtheta"); + NXDaliaslink(pFile,pDict,"dana","cnmon1"); + } + if(self->iLower) + { + NXDupdate(pDict,"bank","lowerbank"); + NXDaliaslink(pFile,pDict,"dana","dcounts"); + NXDaliaslink(pFile,pDict,"dana","dtime"); + NXDaliaslink(pFile,pDict,"dana","dtheta"); + NXDaliaslink(pFile,pDict,"dana","cnmon1"); + } + + + /* close everything */ + NXclose(&pFile); + NXDclose(pDict,NULL); + self->iNew = 0; + } +/*--------------------------------------------------------------------------- + This is the task function for updating the data file any now and then + automatically +*/ + static int FoTask(void *pData) + { + pFoWrite self = NULL; + int iWrite, iRet; + + self = (pFoWrite)pData; + if(!self) + return 0; + + /* figure out if we need to write */ + iWrite = 0; + iRet = 1; + /* first case: update intervall */ + if(time(NULL) >= self->tUpdate) + { + self->tUpdate = time(NULL) + self->iInterval; + iWrite = 1; + iRet = 1; + } + if(self->iEnd) + { + self->tUpdate = 0; + iWrite = 0; + iRet = 0; + FoUpdate(self,self->pCon); + } + + if(iWrite) + { + if(self->iNew) + { + FoStart(self,self->pCon); + FoUpdate(self,self->pCon); + FoLink(self,self->pCon); + } + else + { + FoUpdate(self,self->pCon); + } + } + return iRet; + } +/*------------------------------------------------------------------------*/ + static void KillFoWrite(void *pData) + { + pFoWrite self = NULL; + + self = (pFoWrite)pData; + if(!self) + return; + + if(self->pDes) + DeleteDescriptor(self->pDes); + + if(self->pDictFile) + free(self->pDictFile); + + if(self->pFile) + free(self->pFile); + + if(self->pFitter) + DeleteFitCenter(self->pFitter); + + /* free fomerge */ + killFM(); + + free(self); + } +/*-----------------------------------------------------------------------*/ + int FoInstall(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]) + { + CommandList *pCom = NULL; + char pBueffel[512]; + pFoWrite pNew = NULL; + pICallBack pCall = NULL; + pDummy pDum; + pHMcontrol pHMC = NULL; + + /* check arguments */ + if(argc < 4 ) + { + SCWrite(pCon,"ERROR: Insufficient number of arguments to FoInstall", + eError); + return 0; + } + + /* allocate data structure */ + pNew = (pFoWrite)malloc(sizeof(FoWrite)); + if(!pNew) + { + SCWrite(pCon,"ERROR: out of memory in FoInstall",eError); + return 0; + } + memset(pNew,0,sizeof(FoWrite)); + pNew->pDes = CreateDescriptor("FocusWrite"); + pNew->pCall = CreateCallBackInterface(); + pNew->pFitter = CreateFitCenter(NULL); + if( (!pNew->pDes) || (!pNew->pFitter) ) + { + SCWrite(pCon,"ERROR: out of memory in FoInstall",eError); + free(pNew); + return 0; + } + pNew->pDictFile = strdup(argv[2]); + pNew->iInterval = 20*60; + + pHMC = FindCommandData(pSics,argv[1],"HMcontrol"); + if(!pHMC){ + SCWrite(pCon,"ERROR: no histogram memory control found!",eError); + free(pNew); + return 0; + } + + /* find things in interpreter */ + pCom = FindCommand(pSics,"hm1"); + if(!pCom) + { + SCWrite(pCon,"ERROR: Histogram memory for lower detector bank NOT found",eError); + pNew->pHistogram1 = NULL; + } else + { + pNew->pHistogram1 = (pHistMem)pCom->pData; + pNew->iLower =1; + } + + + pCom = FindCommand(pSics,HM2); + if(pCom) + { + pNew->pHistogram2 = (pHistMem)pCom->pData; + pNew->iMiddle =1; + } + else + { + SCWrite(pCon,"ERROR: Histogram memory for middle detector bank NOT found",eError); + pNew->pHistogram2 = NULL; + } + + pCom = FindCommand(pSics,HM3); + if(pCom) + { + pNew->pHistogram3 = (pHistMem)pCom->pData; + pNew->iUpper =1; + } + else + { + SCWrite(pCon,"ERROR: Histogram memory for upper detector bank NOT found",eError); + pNew->pHistogram3 = NULL; + } + + if(!initializeFM(argv[3])) + { + SCWrite(pCon,"ERROR: bad merge data file",eError); + return 0; + } + + pCom = FindCommand(pSics,"counter"); + if(pCom) + { + pNew->pCount = (pCounter)pCom->pData; + } + + RegisterCallback(pHMC->pCall,COUNTSTART,Countstartcallback,pNew,NULL); + RegisterCallback(pHMC->pCall,COUNTEND,Countendcallback,pNew,NULL); + + /* install command */ + AddCommand(pSics,"StoreFocus",FoAction,KillFoWrite,pNew); + return 1; + } +/*-------------------------------------------------------------------------*/ + int FoAction(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]) + { + int iRet, iVal; + pFoWrite self = NULL; + char pBueffel[512]; + + if(argc < 1) + { + SCWrite(pCon,"ERROR: Insufficient number of arguments to StoreFocus", + eError); + return 0; + } + self = (pFoWrite)pData; + assert(self); + + strtolower(argv[1]); + if(strcmp(argv[1],"start") == 0) + { + FoStart(self,pCon); + FoUpdate(self,pCon); + FoLink(self,pCon); + return 1; + } + else if(strcmp(argv[1],"update") == 0) + { + if((self->iNew) || (!self->pFile)) + { + FoStart(self,pCon); + FoUpdate(self,pCon); + FoLink(self,pCon); + } + else + { + FoUpdate(self,pCon); + } + return 1; + } + else if(strcmp(argv[1],"getfile") == 0) + { + sprintf(pBueffel,"storefocus.file = %s",self->pFile); + SCWrite(pCon,pBueffel,eValue); + return 1; + } + else if(strcmp(argv[1],"interval") == 0) + { + if(argc > 2) /* set value */ + { + if(!SCMatchRights(pCon,usUser)) + { + return 0; + } + iRet = Tcl_GetInt(pSics->pTcl,argv[2],&iVal); + if(iRet != TCL_OK) + { + sprintf(pBueffel,"ERROR: cannot convert --> %s <-- to number ",argv[2]); + SCWrite(pCon,pBueffel,eError); + return 0; + } + self->iInterval = iVal*60; /* go to seconds from minutes*/ + SCSendOK(pCon); + return 1; + } + else /* read the value */ + { + sprintf(pBueffel,"storefocus.interval = %d",self->iInterval/60); + SCWrite(pCon,pBueffel,eValue); + return 1; + } + } + else if(strcmp(argv[1],"middle") == 0) + { + if(argc > 2) /* set value */ + { + if(!SCMatchRights(pCon,usMugger)) + { + return 0; + } + iRet = Tcl_GetInt(pSics->pTcl,argv[2],&iVal); + if(iRet != TCL_OK) + { + sprintf(pBueffel,"ERROR: cannot convert --> %s <-- to number ", + argv[2]); + SCWrite(pCon,pBueffel,eError); + return 0; + } + if(iVal < 0) iVal = 0; + self->iMiddle = iVal; + setFMconfiguration(self->iUpper,self->iMiddle,self->iLower); + SCSendOK(pCon); + return 1; + } + else /* read the value */ + { + sprintf(pBueffel,"storefocus.middle = %d",self->iMiddle); + SCWrite(pCon,pBueffel,eValue); + return 1; + } + } + else if(strcmp(argv[1],"lower") == 0) + { + if(argc > 2) /* set value */ + { + if(!SCMatchRights(pCon,usMugger)) + { + return 0; + } + iRet = Tcl_GetInt(pSics->pTcl,argv[2],&iVal); + if(iRet != TCL_OK) + { + sprintf(pBueffel,"ERROR: cannot convert --> %s <-- to number ", + argv[2]); + SCWrite(pCon,pBueffel,eError); + return 0; + } + if(iVal < 0) iVal = 0; + self->iLower = iVal; + setFMconfiguration(self->iUpper,self->iMiddle,self->iLower); + SCSendOK(pCon); + return 1; + } + else /* read the value */ + { + sprintf(pBueffel,"storefocus.lower = %d",self->iLower); + SCWrite(pCon,pBueffel,eValue); + return 1; + } + } + else if(strcmp(argv[1],"upper") == 0) + { + if(argc > 2) /* set value */ + { + if(!SCMatchRights(pCon,usMugger)) + { + return 0; + } + iRet = Tcl_GetInt(pSics->pTcl,argv[2],&iVal); + if(iRet != TCL_OK) + { + sprintf(pBueffel,"ERROR: cannot convert --> %s <-- to number ", + argv[2]); + SCWrite(pCon,pBueffel,eError); + return 0; + } + if(iVal < 0) iVal = 0; + self->iUpper = iVal; + setFMconfiguration(self->iUpper,self->iMiddle,self->iLower); + SCSendOK(pCon); + return 1; + } + else /* read the value */ + { + sprintf(pBueffel,"storefocus.upper = %d",self->iUpper); + SCWrite(pCon,pBueffel,eValue); + return 1; + } + } + SCWrite(pCon,"ERROR: subcommand to storefocus not recognized",eError); + return 0; + } diff --git a/fowrite.h b/fowrite.h new file mode 100644 index 0000000..fdf4ac0 --- /dev/null +++ b/fowrite.h @@ -0,0 +1,21 @@ +/*-------------------------------------------------------------------------- + F O W R I T E + + fowrite is an object for writing FOCUS data files. + + copyright: see copyright.h + + Mark Koennecke, November 1998 +----------------------------------------------------------------------------*/ +#ifndef FOWRITE +#define FOWRITE + + int FoInstall(SConnection *pCon, SicsInterp *pSics, + void *pData, int argc, char *argv[]); + + + int FoAction(SConnection *pCon, SicsInterp *pSics, + void *pData, int argc, char *argv[]); + +#endif + \ No newline at end of file diff --git a/hardsup/README b/hardsup/README new file mode 100644 index 0000000..7159751 --- /dev/null +++ b/hardsup/README @@ -0,0 +1,4 @@ + + This directory contains support files for the SINQ drivers. + + All of the code; David Maden. diff --git a/hardsup/StrMatch.c b/hardsup/StrMatch.c new file mode 100755 index 0000000..6134a05 --- /dev/null +++ b/hardsup/StrMatch.c @@ -0,0 +1,96 @@ +#define ident "1A01" +#ifdef VAXC +#module StrMatch ident +#endif +#ifdef __DECC +#pragma module StrMatch ident +#endif +/* +** +--------------------------------------------------------------+ +** | Paul Scherrer Institute | +** | Department ASQ | +** | | +** | This software may be used freely by non-profit organizations.| +** | It may be copied provided that the name of P.S.I. and of the | +** | author is included. Neither P.S.I. nor the author assume any | +** | responsibility for the use of this software outside of P.S.I.| +** +--------------------------------------------------------------+ +** +** Module Name . . . . . . . . : [...LIB.SINQ]StrMatch.C +** +** Author . . . . . . . . . . : D. Maden +** Date of creation . . . . . . : Nov 1999 +** +** To compile this module, use: + + $ import tasmad + $ define/group sinq_c_tlb mad_lib:sinq_c.tlb + $ cc /debug /noopt /obj=[]StrMatch - + tasmad_disk:[mad.lib.sinq]StrMatch + + sinq_c_tlb/lib + +** To include this module in SINQ.OLB, use: + + $ import tasmad + $ define/group sinq_c_tlb mad_lib:sinq_c.tlb + $ + $ define/group sinq_olb mad_lib:sinq_dbg.olb + $ @tasmad_disk:[mad.lib.sinq]sinq_olb StrMatch debug + $ + $ define/group sinq_olb mad_lib:sinq.olb + $ @tasmad_disk:[mad.lib.sinq]sinq_olb StrMatch +** +** Updates: +** 1A01 12-Nov-1999 DM. Initial version. +**============================================================================ +** The following entry points are included in this module: +** +**------------------------------------------------------------------------- +** #include +** +** char *StrMatch (&str_a, &str_b, min_len) +** ------- +** Input Args: +** char *str_a - Pointer to first string to be compared. +** char *str_b - Pointer to second string to be compared. +** int min_len - The minimum allowed match length. +** Output Args: +** none +** Modified Args: +** none +** Return value: +** True (non-zero) if the 2 strings match. +** Global variables modified: +** none +** Routines called: +** None +** Description: +** The routine compares 2 strings, str_a and str_b, ignoring case. +** The length of str_a must be less than or equal to the length of str_b. +** The length of str_a must be at least min_len. +**------------------------------------------------------------------------- +** Global Definitions +*/ +#include + +#define NIL '\0' +/* +**==================================================================== +*/ +/* +**==================================================================== +** StrMatch - compare two strings. +*/ + int StrMatch ( +/* ======== +*/ char *str_a, + char *str_b, + int min_len) { + + int i = 0; + + while ((tolower(str_a[i]) == tolower(str_b[i])) && (str_a[i] != '\0')) i++; + + return ((str_a[i] == '\0') && (i >= min_len)); + } +/*-------------------------------------------------- End of StrMatch.C =======*/ diff --git a/hardsup/asynsrv_def.h b/hardsup/asynsrv_def.h new file mode 100644 index 0000000..ccaed19 --- /dev/null +++ b/hardsup/asynsrv_def.h @@ -0,0 +1,51 @@ +#ifndef _asynsrv_def_ +#define _asynsrv_def_ +/*------------------------------------------------ AsynSrv_DEF.H Ident V01N +*/ +#ifndef OffsetOf +#define OffsetOf(type, identifier) ((size_t)(&((type*) NULL)->identifier)) +#endif + +#ifndef _asynsrv_errcodes_ +#define _asynsrv_errcodes_ +#include +#endif + +#define AsynSrv_MAX_LINK 8 + /* + ** Structures needed by AsynSrv_Utility. + */ + struct AsynSrv__info { + int skt; /* The socket number of the connection */ + char host[20]; /* The name of RS-232-C server */ + int port; /* The TCP/IP port number of server */ + int chan; /* The RS-232-C channel number on server */ + int msg_id; + int protocol_code; /* Flag to identify the server's protocol level */ + char protocol_id[4]; /* ASCII version of server's protocol level */ + int cmnd_hdr_len; /* Header length for command strings */ + char cmnd_fmt[8]; /* "sprintf" format for cmnd header conversion */ + int rply_hdr_len; /* Header length for response strings */ + char rply_fmt[8]; /* "sscanf" format for rply header conversion */ + char chan_char[4]; /* ASCII encoded version of chan */ + char tmo[4]; /* ASCII encoded time-out (deci-secs) */ + char eot[4]; /* Expected terminators */ + int max_replies; /* Binary version of #replies in response */ + int n_replies; /* # of last response returned to caller */ + void (*idleHandler) (int, int); /* MZ. handler called when waiting .. + ** .. on a response */ + }; + + struct AsynSrv_HostPortSkt { + char host[30]; + int port; + int skt; + int protocol_code; + char protocol_id[4]; + int cmnd_hdr_len; + int rply_hdr_len; + int usage_cnt; + int status; + }; +/*------------------------------------------------ End of AsynSrv_DEF.H --*/ +#endif /* _asynsrv_def_ */ diff --git a/hardsup/asynsrv_errcodes.h b/hardsup/asynsrv_errcodes.h new file mode 100644 index 0000000..493e608 --- /dev/null +++ b/hardsup/asynsrv_errcodes.h @@ -0,0 +1,34 @@ +/* +** TAS_SRC:[LIB]ASYNSRV_ERRCODES.H +** +** Include file generated from ASYNSRV_ERRCODES.OBJ +** +** 29-AUG-2000 09:49:15.56 +*/ + +#define ASYNSRV__NO_ROOM 0x86480CC +#define ASYNSRV__FORCED_CLOSED 0x86480C4 +#define ASYNSRV__BAD_SOCKET 0x86480BC +#define ASYNSRV__BAD_SEND_UNKN 0x86480B4 +#define ASYNSRV__BAD_SEND_PIPE 0x86480AC +#define ASYNSRV__BAD_SEND_NET 0x86480A4 +#define ASYNSRV__BAD_SEND_LEN 0x864809C +#define ASYNSRV__BAD_SEND 0x8648094 +#define ASYNSRV__BAD_REPLY 0x864808C +#define ASYNSRV__BAD_RECV1_PIPE 0x8648084 +#define ASYNSRV__BAD_RECV1_NET 0x864807C +#define ASYNSRV__BAD_RECV1 0x8648074 +#define ASYNSRV__BAD_RECV_UNKN 0x864806C +#define ASYNSRV__BAD_RECV_PIPE 0x8648064 +#define ASYNSRV__BAD_RECV_NET 0x864805C +#define ASYNSRV__BAD_RECV_LEN 0x8648054 +#define ASYNSRV__BAD_RECV 0x864804C +#define ASYNSRV__BAD_PROT_LVL 0x8648044 +#define ASYNSRV__BAD_PAR 0x864803C +#define ASYNSRV__BAD_NOT_BCD 0x8648034 +#define ASYNSRV__BAD_HOST 0x864802C +#define ASYNSRV__BAD_FLUSH 0x8648024 +#define ASYNSRV__BAD_CONNECT 0x864801C +#define ASYNSRV__BAD_CMND_LEN 0x8648014 +#define ASYNSRV__BAD_BIND 0x864800C +#define ASYNSRV__FACILITY 0x864 diff --git a/hardsup/asynsrv_mark.c b/hardsup/asynsrv_mark.c new file mode 100644 index 0000000..5927350 --- /dev/null +++ b/hardsup/asynsrv_mark.c @@ -0,0 +1,1465 @@ +#define ident "1B06" +#ifdef VAXC +#module AsynSrv_Utility ident +#endif +#ifdef __DECC +#pragma module AsynSrv_Utility ident +#endif +/* +** +--------------------------------------------------------------+ +** | Paul Scherrer Institute | +** | Department ASQ | +** | | +** | This software may be used freely by non-profit organizations.| +** | It may be copied provided that the name of P.S.I. and of the | +** | author is included. Neither P.S.I. nor the author assume any | +** | responsibility for the use of this software outside of P.S.I.| +** +--------------------------------------------------------------+ +** +** Module Name . . . . . . . . : [...LIB.SINQ]AsynSrv_Utility.C +** +** Author . . . . . . . . . . : D. Maden +** Date of creation . . . . . . : Mar 1996 +** +** To compile this module, use: + + $ define/group sinq_c_tlb mad_lib:sinq_c.tlb + $ cc /debug /noopt /obj=[]AsynSrv_Utility - + lnsa01::tasmad_disk:[mad.psi.lib.sinq]AsynSrv_Utility + + sinq_c_tlb/lib + +** To include this module in SINQ.OLB, use: + + $ define/group sinq_c_tlb mad_lib:sinq_c.tlb + $ + $ define/group sinq_olb mad_lib:sinq_dbg.olb + $ @lnsa01::tasmad_disk:[mad.psi.lib.sinq]sinq_olb AsynSrv_Utility debug + $ + $ define/group sinq_olb mad_lib:sinq.olb + $ @lnsa01::tasmad_disk:[mad.psi.lib.sinq]sinq_olb AsynSrv_Utility +** +** Updates: +** 1A01 21-Mar-1996 DM. Initial version. +** 1B01 12-Sep-1996 DM. Allow host name to be in dot format too. +** 1B02 5-May-1997 DM. Set 5 sec time-out on "connect" on VMS systems. +**============================================================================ +** The entry points included in this module are described below. Prototypes +** can be defined via: +** +** #include +** +** AsynSrv_Close - Close a connection to an RS-232-C Server. +** AsynSrv_Config - Configure an open AsynSrv_Utility connection. +** AsynSrv_ConfigDflt - Set defaults for AsynSrv_Open. +** AsynSrv_ErrInfo - Return detailed status from last operation. +** AsynSrv_GetReply - Get next reply from a reply buffer. +** AsynSrv_Open - Open a connection to an RS-232-C Server. +** AsynSrv_SendCmnds - Send commands to a channel of an RS-232-C Server. +**--------------------------------------------------------------------- +** int AsynSrv_Close (&asyn_info, force_flag) +** ------------- +** Input Args: +** int force_flag - if non-zero, all connections using the same socket +** will also be marked as force-closed (socket number +** set to -1) and the connection will really be +** closed. This is needed for error recovery operations. +** Output Args: +** none +** Modified Args: +** struct AsynSrv__info *asyn_info - a structure holding skt, host and +** port of the connection. On return +* skt = 0. +** Return status: +** True if no problems detected, otherwise False and AsynSrv_errcode +** is set to indicate the nature of the problem as follows: +** AsynSrv__BAD_PAR = -29 --> skt does not match with host/port. +** Routines called: +** Socket library, "close". +** Description: +** The routine decrements the usage count on the connection to host/port. +** If the counter is still >0, the routine simply returns. +** If the counter is now 0, the routine sends a "-001" message to the +** server to inform it that we are about to close the link, waits for a +** possible 4 bytes of response and then closes the TCP/IP connection. +**--------------------------------------------------------------------- +** int AsynSrv_Config (&asyn_info, &par_id, par_val, ...) +** -------------- +** Input Args: +** char* par_id - Text string identifying the next argument (see below). +** NULL indicates the end of the argument list. +** par_val - The value to set for the argument. The type of the +** argument can depend on par_id. +** Output Args: +** none +** Modified Args: +** struct AsynSrv__info *asyn_info - the structure used in the call to +** AsynSrv_Open. It is used to hold the config +** info for the connection. +** Return status: +** True if no problems detected, otherwise False and AsynSrv_errcode +** is set to indicate the nature of the problem as follows: +** AsynSrv__BAD_PAR = -29 --> Unrecognised par_id or msecTmo < 100 or +** msecTmo > 999'999 or bad eot or .. +** Routines called: +** None +** Description: +** AsynSrv_Config may be used for setting values of parameters for +** use in subsequent calls to AsynSrv_SendCmnds. Defaults for these +** parameters are set via a call to AsynSrv_ConfigDflt, prior to +** calling AsynSrv_Open. Values which may be taken by par_id (warning -- +** par_id is case-sensitive) and the corresponding variable type of +** par_val are: +** +** "msecTmo" int The time-out response for commands sent +** to a serial channel on the server. The +** valid range is 100 to 999'999. +** "eot" char* The expected terminators in responses to +** commands sent to a serial channel on the +** server. The first character specifies the +** number of terminators (max=3). +**--------------------------------------------------------------------- +** int AsynSrv_ConfigDflt (&par_id, par_val, ...) +** ------------------ +** Input Args: +** char* par_id - Text string identifying the next argument (see below). +** NULL indicates the end of the argument list. +** par_val - The value to set for the argument. The type of the +** argument can depend on par_id. +** Output Args: +** none +** Modified Args: +** none +** Return status: +** True if no problems detected, otherwise False and AsynSrv_errcode +** is set to indicate the nature of the problem as follows: +** AsynSrv__BAD_PAR = -29 --> Unrecognised par_id or msecTmo < 100 or +** msecTmo > 999'999 or bad eot or .. +** Routines called: +** None +** Description: +** AsynSrv_ConfigDflt may be used for setting default values of parameters +** for use in subsequent calls to AsynSrv_Open. Values which may be taken +** by par_id (warning -- par_id is case-sensitive) and the corresponding +** variable type of par_val are: +** +** "TmoC" int The time-out in seconds to be used when +** opening a connection to a server. This +** value is only effective on VMS systems. For +** UNIX systems, the systemwide default (usually +** 75 secs) cannot be changed. The initial +** setting for "TmoC" is 5 secs. +** "msecTmo" int The time-out response for commands sent +** to a serial channel on the server. The +** valid range is 100 to 999'999. The initial +** setting for "msecTmo" is 10'000 msec. +** "eot" char* The expected terminators in responses to +** commands sent to a serial channel on the +** server. The first character specifies the +** number of terminators (max=3). The initial +** setting for "eot" is "1\r". +**------------------------------------------------------------------------- +** void AsynSrv_ErrInfo (&entry_txt_ptr, &errcode, &my_errno, &vaxc_errno) +** --------------- +** Input Args: +** None +** Output Args: +** char **entry_txt_ptr - Pointer to a text string giving the call stack +** at the time that the error was detected. +** int *errcode - An internal error code indicating the detected error. +** int *my_errno - Saved value of errno. +** int *vaxc_errno - Saved value of vaxc$errno (OpenVMS only). +** Modified Args: +** none +** Return status: +** none +** Routines called: +** none +** Description: +** Returns detailed status of the last operation. Once an error has been +** detected, the error status is frozen until this routine has been called. +**------------------------------------------------------------------------- +** char *AsynSrv_GetReply (&asyn_info, &rcve_buff, &last_rply) +** ---------------- +** Input Args: +** struct RS__RespStruct *rcve_buff - address of receive buffer used +** in last call to AsynSrv_SendCmnds. +** char *last_rply - Address of last reply processed +** or NULL. +** Output Args: +** none +** Modified Args: +** struct AsynSrv__info *asyn_info - the structure used in the call to +** AsynSrv_Open. It is used to hold status info +** between calls to this routine. +** Return status: +** Address of next reply in the buffer or NULL if no more. Note that this +** is a pointer to the reply and not to the head of the reply structure. +** The terminator byte found is therefore at index [-1] from this address. +** Routines called: +** none +** Description: +** AsynSrv_GetReply unpacks the replies in the response packet from the +** RS232C server which is an argument in the call to AsynSrv_SendCmnds. +** If the routine is called with last_rply = NULL, a pointer to the +** first reply is returned. On calling AsynSrv_GetReply again with +** last_rply set to this address, one receives the address of the second +** reply and so on, until NULL is returned, indicating that all responses +** have been exhausted. +** Warning: +** AsynSrv_GetReply keeps count of the number of responses it returns. +** Responses must therefore be processed in order. +**------------------------------------------------------------------------- +** int AsynSrv_Open (&asyn_info) +** ------------ +** Input Args: +** asyn_info->host - Name of host offering the RS-232-C service. The name +** can be either symbolic or numeric, e.g. +** "lnsw02.psi.ch" or "129.129.90.18". +** asyn_info->port - Number of TCP/IP port of TCP/IP server. +** Output Args: +** none +** Modified Args: +** struct AsynSrv__info *asyn_info - a structure holding skt, host and +** port of the connection. On return +** skt = socket number of connection. +** Set to 0 if error. +** Return status: +** If non-zero, no problems detected and asyn_info->skt is the socket to +** use for communicating with the server. Otherwise, a problem +** was detected and AsynSrv_errcode may be set as follows +** to indicate the nature of the problem: +** AsynSrv__BAD_HOST = -6 --> Call to "gethostbyname" failed to get +** network addr of host. +** AsynSrv__BAD_SOCKET = -7 --> Call to "socket" failed. +** AsynSrv__BAD_BIND = -8 --> Call to "bind" failed. +** AsynSrv__BAD_CONNECT = -9 --> Call to "connect" failed. +** AsynSrv__BAD_PAR = -29 --> Bad parameter found. Probably +** asyn_info->port or asyn_info->chan +** are out of range. +** AsynSrv__NO_ROOM = -40 --> Host/port table full or Active-link +** table full. +** Routines called: +** Socket library routine "open". +** Description: +** The routine maintains a list of hosts/ports to which it has open +** sockets. If an entry is found in the list, the socket is returned +** and the usage count of this connection is incremented. If no entry +** is found in the list, a connection to the host is established and +** entered into the list. +** The routine also maintains a table of active links so that the +** "force-close" function can be performed. The link is added to this +** table too. +**------------------------------------------------------------------------- +** int AsynSrv_SendCmnds (&asyn_info, &send_buff, &rcve_buff, ...) +** ----------------- +** Input Args: +** struct AsynSrv__info *asyn_info - the structure used in the call to +** AsynSrv_Open. It contains settings required +** for setting up and sending send_buff. +** char * ... - A list of commands, terminated by NULL, for +** sending to the channel on the server. The commands +** must have any necessary \r characters included. +** Output Args: +** struct RS__RespStruct *rcve_buff - a buffer to receive the response +** from the server. +** Modified Args: +** struct RS__MsgStruct *send_buff - a buffer for holding the commands +** for sending to the server. +** Return status: +** True if no problems detected, otherwise False and errcode (see +** AsynSrv_ErrInfo) is set to indicate the nature of the problem. +** AsynSrv_errcode may be set as follows: +** AsynSrv__BAD_SENDLEN = -12 --> Too much to send; either too many +** commands or too long. The buffer +** is 232 bytes long and each command +** has a 2-byte header. +** Errors -13 to -16 are related to network errors whilst sending the +** message buffer to the server: +** AsynSrv__BAD_SEND = -13 --> Network problem - server has +** probably abended. +** AsynSrv__BAD_SEND_PIPE = -14 --> Network pipe broken - probably same +** cause as AsynSrv__BAD_SEND. +** AsynSrv__BAD_SEND_NET = -15 --> Some other network problem. "errno" +** may be helpful. +** AsynSrv__BAD_SEND_UNKN = -16 --> Some other network problem happened +** resulting in the message not +** getting sent completely. "errno" is +** probably not helpful in this case. +** Errors AsynSrv__BAD_RECV, AsynSrv__BAD_RECV_PIPE, AsynSrv__BAD_RECV_NET +** and AsynSrv__BAD_RECV_UNKN (-17 to -20) are related to network +** errors whilst receiving the 4-byte response header. They are +** analogous to AsynSrv__BAD_SEND to AsynSrv__BAD_SEND_UNKN. +** AsynSrv__BAD_NOT_BCD = -21 --> The 4-byte response header is not an +** ASCII coded decimal integer. +** AsynSrv__BAD_RECVLEN = -22 --> The body of the response would be too +** big to fit in the input buffer. The +** buffer is ??? bytes long and each +** response has a 5-byte header and a +** trailing zero-byte. The response +** is flushed. +** AsynSrv__BAD_FLUSH = -23 --> Some network error was detected +** during flushing. This is an "or" +** of errors AsynSrv__BAD_RECV to +** AsynSrv__BAD_RECV_UNKN. +** AsynSrv__FORCED_CLOSED = -32 --> The connection to the motor has been +** forcefully closed. See below. +** AsynSrv__BAD_REPLY = -34 --> The n_rply field of the response was +** either non-numeric or <0, indicating +** that the Terminal Server detected an +** error. The reply is added to the +** routine call stack for debug purposes. +** +** Errors AsynSrv__BAD_RECV1, AsynSrv__BAD_RECV1_PIPE and +** AsynSrv__BAD_RECV1_NET (-24 to -26) are related to network +** errors whilst receiving the body of the response. They are +** equivalent to errors AsynSrv__BAD_RECV, to AsynSrv__BAD_RECV_NET. +** +** AsynSrv__FORCED_CLOSED occurs if AsynSrv_Close has been called +** for another device on the same server and the 'force_flag' +** was set (see AsynSrv_Close). The caller should call +** AsynSrv_Close and then AsynSrv_Open to re-establish a +** connection to the server. +** Routines called: +** Socket library routines send and recv. +** Description: +** The list of commands is assembled into a message buffer with appropriate +** header information and sent off to the server. The response is then +** awaited and read in when it arrives. +** +** For any of the following errors: +** AsynSrv__BAD_SEND (Note: AsynSrv__BAD_SENDLEN and +** AsynSrv__BAD_SEND_PIPE AsynSrv__BAD_RECVLEN do not cause a close +** AsynSrv__BAD_SEND_NET +** AsynSrv__BAD_SEND_UNKN +** AsynSrv__BAD_RECV +** AsynSrv__BAD_RECV_PIPE +** AsynSrv__BAD_RECV_NET +** AsynSrv__BAD_RECV_UNKN +** AsynSrv__BAD_NOT_BCD +** AsynSrv__BAD_FLUSH +** AsynSrv__BAD_RECV1 +** AsynSrv__BAD_RECV1_PIPE +** AsynSrv__BAD_RECV1_NET +** the network link to the server is force-closed via a call to AsynSrv_Close. +** Once the error has been corrected, the link can be re-opened via a +** call to AsynSrv_Open. As a result of the force-close, other active handles +** will need to be released via a call to AsynSrv_Close before AsynSrv_Open is +** called. +** +** Note: neither of the errors AsynSrv__BAD_SENDLEN, AsynSrv__BAD_RECVLEN +** nor AsynSrv__BAD_REPLY cause the link to be closed. +**============================================================================*/ +/* +**--------------------------------------------------------------------------- +** Global Definitions +*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "fortify.h" +#include + +#ifdef __VMS +#include +#include +#else +#include +#endif +/*-----------------------------------------------------------------*/ +#include +#include +#include + +#define True 1 +#define False 0 + +#define MAX_OPEN 64 +/*-------------------------------------------------------------------------- +** Global Variables +*/ + static int AsynSrv_call_depth = 0; + static char AsynSrv_routine[5][64]; + static int AsynSrv_errcode = 0; + static int AsynSrv_errno, AsynSrv_vaxc_errno; + static int AsynSrv_connect_tmo = 5; /* Time-out on "connect" */ + static int AsynSrv_msec_tmo = 10000; /* Time-out for responses */ + static char AsynSrv_eot[] = {'1', '\r', '\0','\0'}; /* Terminators */ +/* +** The following is the list of open connections (= number of +** active sockets). +*/ + static int AsynSrv_n_cnct = 0; + static struct AsynSrv_HostPortSkt AsynSrv_HPS_list[AsynSrv_MAX_LINK]; +/* +** The following is the list of active calls to AsynSrv_Open. +*/ + static int AsynSrv_n_active = 0; + static struct AsynSrv__info *AsynSrv_active[MAX_OPEN]; +/* +**--------------------------------------------------------------------------- +** AsynSrv_Close: Close a connection to an RS-232-C server. +*/ + int AsynSrv_Close ( +/* ============= +*/ struct AsynSrv__info *asyn_info, + int force_flag) { + + int i, j, k, my_skt; + char buff[4]; + /*----------------------------------------------- + */ + if (asyn_info == NULL) return True; /* Just return if nothing to do! */ + my_skt = asyn_info->skt; + if (my_skt <= 0) return True; /* Just return if nothing to do! */ + /*----------------------------------------------- + ** Pre-set the routinename (in case of error) + */ + if (AsynSrv_errcode == 0 && AsynSrv_call_depth < 5) { + strcpy (AsynSrv_routine[AsynSrv_call_depth], "AsynSrv_Close"); + AsynSrv_call_depth++; + } + /*------------------------------------------------------ + ** Start by finding the table entry for this connection + */ + for (i = 0; i < AsynSrv_n_cnct; i++) { + if (AsynSrv_HPS_list[i].skt != my_skt) continue; + if (AsynSrv_HPS_list[i].port != asyn_info->port) continue; + if (strcmp (AsynSrv_HPS_list[i].host, asyn_info->host) == 0) break; + } + if (i >= AsynSrv_n_cnct) { /* Did we find the entry? */ + AsynSrv_errcode = AsynSrv__BAD_PAR; /* No! */ + return False; + } + /*------------------------------------------------------ + ** Now find the table entry for the AsynSrvOpen call. + */ + for (j = 0; j < AsynSrv_n_active; j++) { + if ((AsynSrv_active[j] == asyn_info) && + (AsynSrv_active[j]->skt == my_skt)) { + break; + } + } + if (j >= AsynSrv_n_active) { /* Did we find the entry? */ + AsynSrv_errcode = AsynSrv__BAD_PAR; /* No! */ + return False; + } + /*------------------------------------------------------ + ** i is the index for the connection table entry. + ** j is the index for the caller's AsynSrvOpen call entry. + */ + if (AsynSrv_HPS_list[i].usage_cnt <= 0) { /* Is the connection active? */ + AsynSrv_errcode = AsynSrv__BAD_PAR; /* No */ + return False; + } + /*------------------------------------------------------ + ** For the caller, simply set his socket number to zero, + ** mark the AsynSrvOpen entry as free and decrease the + ** usage count (the entries will be compressed later). + */ + AsynSrv_active[j]->skt = 0; /* Mark the close .. */ + AsynSrv_active[j] = NULL; /* .. and flag entry to be removed. */ + AsynSrv_HPS_list[i].usage_cnt--; /* Decrease usage count */ + /*------------------------------------------------------ + ** If this is a force-close, go through all AsynSrv_Open + ** entries looking for a socket match, mark them as + ** free and decrease usage count. + */ + if (force_flag) { + for (k = 0; k < AsynSrv_n_active; k++) { + if (AsynSrv_active[k] != NULL) { + if (AsynSrv_active[k]->skt == my_skt) { + AsynSrv_active[k]->skt = -1; /* Mark the force-close */ + AsynSrv_active[k] = NULL; /* Mark entry to be removed */ + AsynSrv_HPS_list[i].usage_cnt--; /* Decrease usage count */ + } + } + } + if (AsynSrv_HPS_list[i].usage_cnt != 0) { /* Use count should now be .. */ + AsynSrv_errcode = AsynSrv__BAD_PAR; /* .. zero or there's a bug. */ + return False; + } + } + /*------------------------------------------------------ + ** Compress the list of AsynSrv_Open entries + */ + j = 0; + for (k = 0; k < AsynSrv_n_active; k++) { + if (AsynSrv_active[k] != NULL) { + AsynSrv_active[j] = AsynSrv_active[k]; + j++; + } + } + for (k = j; k < AsynSrv_n_active; k++) AsynSrv_active[k] = NULL; + AsynSrv_n_active = j; + /*------------------------------------------------------ + ** If the link is now idle, really close it and compress + ** the connection table entry out of the list. + */ + if (AsynSrv_HPS_list[i].usage_cnt == 0) { + send (my_skt, "-001", 4, 0); /* Tell the TCP/IP server that .. + ** .. we are about to quit. + */ + recv (my_skt, buff, sizeof (buff), 0); /* And wait for his ack */ + close (my_skt); + for (j = i; j < AsynSrv_n_cnct; j++) { + memcpy ((char *) &AsynSrv_HPS_list[j], (char *) &AsynSrv_HPS_list[j+1], + sizeof (AsynSrv_HPS_list[0])); + } + AsynSrv_HPS_list[AsynSrv_n_cnct].skt = 0; /* Invalidate the free entry */ + AsynSrv_n_cnct--; + } + AsynSrv_call_depth--; + return True; + } +/* +**--------------------------------------------------------------------------- +** AsynSrv_Config: Configure an open connection. +*/ + int AsynSrv_Config ( +/* ============== +*/ struct AsynSrv__info *asyn_info, + ...) { + + char buff[16]; + va_list ap; /* Pointer to variable args */ + char *txt_ptr; + int intval; + /* + ** Pre-set the routinename (in case of error) + */ + if (AsynSrv_errcode == 0 && AsynSrv_call_depth < 5) { + strcpy (AsynSrv_routine[AsynSrv_call_depth], "AsynSrv_Config"); + AsynSrv_call_depth++; + } + + va_start (ap, asyn_info); /* Set up var arg machinery */ + txt_ptr = va_arg (ap, char *); /* Get pntr to first parameter ident */ + while (txt_ptr != NULL) { + if (strcmp (txt_ptr, "msecTmo") == 0) { + intval = va_arg (ap, int); + if ((intval < 100) || (intval > 999999)) { + AsynSrv_errcode = AsynSrv__BAD_PAR; + return False; + } + sprintf (buff, "%04d", intval/100); /* Convert to ASCII as .. + ** .. deci-secs */ + memcpy (asyn_info->tmo, buff, 4); + }else if (strcmp (txt_ptr, "eot") == 0) { + txt_ptr = va_arg (ap, char *); + if (txt_ptr == NULL) { + AsynSrv_errcode = AsynSrv__BAD_PAR; + return False; + } + memcpy (asyn_info->eot, "\0\0\0\0", 4); + switch (txt_ptr[0]) { + case '3': asyn_info->eot[3] = txt_ptr[3]; + case '2': asyn_info->eot[2] = txt_ptr[2]; + case '1': asyn_info->eot[1] = txt_ptr[1]; + case '0': + asyn_info->eot[0] = txt_ptr[0]; + break; + default: + AsynSrv_errcode = AsynSrv__BAD_PAR; + return False; + } + }else { + AsynSrv_errcode = AsynSrv__BAD_PAR; + return False; + } + txt_ptr = va_arg (ap, char *); /* Get pntr to next parameter ident */ + } + + AsynSrv_call_depth--; + return True; + } +/* +**--------------------------------------------------------------------------- +** AsynSrv_ConfigDflt: Set default values in AsynSrv_Utility +** which will be used to initialise +** structures in AsynSrv_Open. +*/ + int AsynSrv_ConfigDflt ( +/* ================== +*/ char *par_id, + ...) { + int i; + char buff[4]; + va_list ap; /* Pointer to variable args */ + char *txt_ptr; + int intval; + /* + ** Pre-set the routinename (in case of error) + */ + if (AsynSrv_errcode == 0 && AsynSrv_call_depth < 5) { + strcpy (AsynSrv_routine[AsynSrv_call_depth], "AsynSrv_ConfigDflt"); + AsynSrv_call_depth++; + } + + va_start (ap, par_id); /* Set up var arg machinery */ + txt_ptr = par_id; /* Point to first arg */ + while (txt_ptr != NULL) { + if (strcmp (txt_ptr, "tmoC") == 0) { + intval = va_arg (ap, int); + if ((intval < 1) || (intval > 3600)) { + AsynSrv_errcode = AsynSrv__BAD_PAR; + return False; + } + AsynSrv_connect_tmo = intval; + }else if (strcmp (txt_ptr, "msecTmo") == 0) { + intval = va_arg (ap, int); + if ((intval < 100) || (intval > 999900)) { + AsynSrv_errcode = AsynSrv__BAD_PAR; + return False; + } + AsynSrv_msec_tmo = intval; + }else if (strcmp (txt_ptr, "eot") == 0) { + txt_ptr = va_arg (ap, char *); + if (txt_ptr == NULL) { + AsynSrv_errcode = AsynSrv__BAD_PAR; + return False; + } + switch (txt_ptr[0]) { + case '3': AsynSrv_eot[3] = txt_ptr[3]; + case '2': AsynSrv_eot[2] = txt_ptr[2]; + case '1': AsynSrv_eot[1] = txt_ptr[1]; + case '0': + AsynSrv_eot[0] = txt_ptr[0]; + break; + default: + AsynSrv_errcode = AsynSrv__BAD_PAR; + return False; + } + switch (txt_ptr[0]) { + case '0': AsynSrv_eot[1] = '\0'; + case '1': AsynSrv_eot[2] = '\0'; + case '2': AsynSrv_eot[3] = '\0'; + } + }else { + AsynSrv_errcode = AsynSrv__BAD_PAR; + return False; + } + txt_ptr = va_arg (ap, char *); /* Get pntr to next parameter ident */ + } + + AsynSrv_call_depth--; + return True; + } +/* +**--------------------------------------------------------------------------- +** AsynSrv_ErrInfo: Return detailed status from last operation. +*/ + void AsynSrv_ErrInfo ( +/* =============== +*/ char **entry_txt, + int *errcode, + int *my_errno, + int *vaxc_errno) { + + int i, j, k; + char buff[80]; + + if (AsynSrv_call_depth <= 0) { + strcpy (AsynSrv_routine[0], "AsynSrv_no_error_detected"); + *errcode = 0; + *my_errno = 0; + *vaxc_errno = 0; + }else { + if (AsynSrv_call_depth > 1) { /* Concatenate the names */ + for (i = 1; i < AsynSrv_call_depth; i++) { + strcat (AsynSrv_routine[0], "/"); + StrJoin (AsynSrv_routine[0], sizeof (AsynSrv_routine), + AsynSrv_routine[0], AsynSrv_routine[i]); + } + } + *errcode = AsynSrv_errcode; + *my_errno = AsynSrv_errno; + *vaxc_errno = AsynSrv_vaxc_errno; + switch (AsynSrv_errcode) { + case AsynSrv__BAD_HOST: strcpy (buff, "/AsynSrv__BAD_HOST"); break; + case AsynSrv__BAD_SOCKET: strcpy (buff, "/AsynSrv__BAD_SOCKET"); break; + case AsynSrv__BAD_BIND: strcpy (buff, "/AsynSrv__BAD_BIND"); break; + case AsynSrv__BAD_CONNECT: strcpy (buff, "/AsynSrv__BAD_CONNECT"); break; + case AsynSrv__BAD_SENDLEN: strcpy (buff, "/AsynSrv__BAD_SENDLEN"); break; + case AsynSrv__BAD_SEND: strcpy (buff, "/AsynSrv__BAD_SEND"); break; + case AsynSrv__BAD_SEND_PIPE: strcpy (buff, "/AsynSrv__BAD_SEND_PIPE"); break; + case AsynSrv__BAD_SEND_NET: strcpy (buff, "/AsynSrv__BAD_SEND_NET"); break; + case AsynSrv__BAD_SEND_UNKN: strcpy (buff, "/AsynSrv__BAD_SEND_UNKN"); break; + case AsynSrv__BAD_RECV: strcpy (buff, "/AsynSrv__BAD_RECV"); break; + case AsynSrv__BAD_RECV_PIPE: strcpy (buff, "/AsynSrv__BAD_RECV_PIPE"); break; + case AsynSrv__BAD_RECV_NET: strcpy (buff, "/AsynSrv__BAD_RECV_NET"); break; + case AsynSrv__BAD_RECV_UNKN: strcpy (buff, "/AsynSrv__BAD_RECV_UNKN"); break; + case AsynSrv__BAD_NOT_BCD: strcpy (buff, "/AsynSrv__BAD_NOT_BCD"); break; + case AsynSrv__BAD_RECVLEN: strcpy (buff, "/AsynSrv__BAD_RECVLEN"); break; + case AsynSrv__BAD_FLUSH: strcpy (buff, "/AsynSrv__BAD_FLUSH"); break; + case AsynSrv__BAD_RECV1: strcpy (buff, "/AsynSrv__BAD_RECV1"); break; + case AsynSrv__BAD_RECV1_PIPE:strcpy (buff, "/AsynSrv__BAD_RECV1_PIPE"); break; + case AsynSrv__BAD_RECV1_NET: strcpy (buff, "/AsynSrv__BAD_RECV1_NET"); break; + case AsynSrv__BAD_PAR: strcpy (buff, "/AsynSrv__BAD_PAR"); break; + case AsynSrv__FORCED_CLOSED: strcpy (buff, "/AsynSrv__FORCED_CLOSED"); break; + case AsynSrv__BAD_REPLY: strcpy (buff, "/AsynSrv__BAD_REPLY"); break; + case AsynSrv__BAD_CMND_LEN: strcpy (buff, "/AsynSrv__BAD_CMND_LEN"); break; + case AsynSrv__BAD_PROT_LVL: strcpy (buff, "/AsynSrv__BAD_PROT_LVL"); break; + case AsynSrv__NO_ROOM: strcpy (buff, "/AsynSrv__NO_ROOM"); break; + default: sprintf (buff, "/AsynSrv__unkn_err_code: %d", AsynSrv_errcode); + } + StrJoin (AsynSrv_routine[0], sizeof(AsynSrv_routine), + AsynSrv_routine[0], buff); + } + *entry_txt = AsynSrv_routine[0]; + AsynSrv_call_depth = 0; + AsynSrv_errcode = 0; + } +/* +**--------------------------------------------------------------------------- +** AsynSrv_GetReply: Get next reply from a reply buffer. +*/ + char *AsynSrv_GetReply ( +/* ================ +*/ struct AsynSrv__info *asyn_info, + struct RS__RespStruct *rcve_buff, + char *last_rply) { + + char *pntr = NULL; + int i, rply_len; + + if (last_rply == NULL) { /* Start with first reply? */ + /* Yes */ + asyn_info->n_replies = 1; + if (asyn_info->max_replies > 0) { + pntr = rcve_buff->u.rplys; + pntr = pntr + 1 + asyn_info->rply_hdr_len; + } + }else { /* No - get next reply */ + if (asyn_info->n_replies < asyn_info->max_replies) { /* If there is one */ + i = sscanf ((last_rply - asyn_info->rply_hdr_len - 1), + asyn_info->rply_fmt, &rply_len); + if ((i == 1) && (rply_len >= 0)) { + pntr = last_rply + rply_len + asyn_info->rply_hdr_len; + } + } + } + return pntr; + } +/* +**--------------------------------------------------------------------------- +** AsynSrv_Open: Open a connection to an RS-232-C Server. +*/ + int AsynSrv_Open ( +/* ============ +*/ struct AsynSrv__info *asyn_info) { + + int i, status; + int my_skt; + char old_time_out[4]; + union { + char chars[4]; + int val; + } time_out; + char buff[128]; + struct RS__MsgStruct s_buff; + struct RS__RespStruct r_buff; + unsigned int oto_len, oto_status; + struct hostent *rmt_hostent; + struct in_addr *rmt_inet_addr_pntr; + struct in_addr rmt_inet_addr; + int rmt_sockname_len; + struct sockaddr_in lcl_sockname; + struct sockaddr_in rmt_sockname; + + char *errtxt_ptr; + int errcode, my_errno, my_vaxc_errno; +/*-------------------------------------------------------- +*/ + asyn_info->skt = 0; +/*-------------------------------------------------------- +** Initialise the error info stack and pre-set the +** routine name (in case of error). +*/ + AsynSrv_errcode = AsynSrv_errno = AsynSrv_vaxc_errno = 0; + strcpy (AsynSrv_routine[0], "AsynSrv_Open"); + AsynSrv_call_depth = 1; +/*-------------------------------------------------------- +** Is there room for a new AsynSrv_Open table entry? +*/ + if (AsynSrv_n_active >= MAX_OPEN) { + AsynSrv_errcode = AsynSrv__NO_ROOM; /* There isn't! */ + return False; + } +/*-------------------------------------------------------- +** See if a table entry for this connection already exists. +*/ + for (i = 0; i < AsynSrv_n_cnct; i++) { + if (AsynSrv_HPS_list[i].port != asyn_info->port) continue; + if (strcmp (AsynSrv_HPS_list[i].host, asyn_info->host) == 0) break; + } + if (i < AsynSrv_n_cnct) { /* Did we find an entry? */ + AsynSrv_call_depth--; /* Yes */ + AsynSrv_HPS_list[i].usage_cnt++; /* Up the usage count and .. */ + AsynSrv_active[AsynSrv_n_active] = /* .. remember the open and .. */ + asyn_info; + AsynSrv_n_active++; + asyn_info->skt = /* .. return the socket. */ + AsynSrv_HPS_list[i].skt; + if (asyn_info->chan < 0) asyn_info->chan = 0; + if (asyn_info->chan > 255) asyn_info->chan = 0; + sprintf (buff, "%04d", asyn_info->chan); /* Convert channel # to ASCII */ + memcpy (asyn_info->chan_char, buff, sizeof (asyn_info->chan_char)); + + asyn_info->protocol_code = AsynSrv_HPS_list[i].protocol_code; + memcpy (asyn_info->protocol_id, + AsynSrv_HPS_list[i].protocol_id, + sizeof (asyn_info->protocol_id)); + asyn_info->cmnd_hdr_len = AsynSrv_HPS_list[i].cmnd_hdr_len; + sprintf (asyn_info->cmnd_fmt, "%%0%dd", asyn_info->cmnd_hdr_len); + asyn_info->rply_hdr_len = AsynSrv_HPS_list[i].rply_hdr_len; + sprintf (asyn_info->rply_fmt, "%%%dd", asyn_info->rply_hdr_len); + + sprintf (buff, "%04d", AsynSrv_msec_tmo/100); /* Set dflt time-out .. + ** ..(deci-secs) */ + memcpy (asyn_info->tmo, buff, sizeof (asyn_info->tmo)); + + memcpy (asyn_info->eot, /* Set dflt terminator(s) */ + AsynSrv_eot, sizeof (asyn_info->eot)); + + asyn_info->max_replies = asyn_info->n_replies = 0; + return True; + } +/*-------------------------------------------------------- +** There is no existing connection. Is there room for +** a new connection entry? +*/ + if (AsynSrv_n_cnct >= AsynSrv_MAX_LINK) { + AsynSrv_errcode = AsynSrv__NO_ROOM; /* There isn't! */ + return False; + } +/*-------------------------------------------------------- +** But, before going any further, do some quick checks on +** values in asyn_info. +*/ + if ((asyn_info->port <= 0) || + (asyn_info->port > 65535)) { + AsynSrv_errcode = AsynSrv__BAD_PAR; /* Something is bad! */ + return False; + } + if (asyn_info->chan < 0) asyn_info->chan = 0; + if (asyn_info->chan > 255) asyn_info->chan = 0; +/*-------------------------------------------------------- +** Set up a new connection. +*/ + StrJoin (AsynSrv_HPS_list[AsynSrv_n_cnct].host, + sizeof (AsynSrv_HPS_list[AsynSrv_n_cnct].host), + asyn_info->host, ""); + AsynSrv_HPS_list[AsynSrv_n_cnct].port = asyn_info->port; + /*--------------------------- + ** Get the Internet address of the server. + */ + rmt_inet_addr.s_addr = inet_addr (asyn_info->host); + if (rmt_inet_addr.s_addr != -1) { + rmt_inet_addr_pntr = &rmt_inet_addr; + }else { + rmt_hostent = gethostbyname (asyn_info->host); + if (rmt_hostent == NULL) { + AsynSrv_errcode = AsynSrv__BAD_HOST; + GetErrno (&AsynSrv_errno, &AsynSrv_vaxc_errno); /* Save errno info */ + fprintf (stderr, "\nAsynSrv_Open/gethostbyname: Failed to get Internet " + "address of \"%s\".\n", asyn_info->host); + return False; + } + rmt_inet_addr_pntr = (struct in_addr *) rmt_hostent->h_addr_list[0]; + } + /*--------------------------- + ** Create a TCP/IP socket for connecting to server and bind it. + */ + my_skt = socket (AF_INET, SOCK_STREAM, 0); + if (my_skt <= 0) { + AsynSrv_errcode = AsynSrv__BAD_SOCKET; + GetErrno (&AsynSrv_errno, &AsynSrv_vaxc_errno); /* Save the errno info */ + fprintf (stderr, "\nAsynSrv_Open/socket: Failed to create a socket.\n"); + return False; + } + lcl_sockname.sin_family = AF_INET; + lcl_sockname.sin_port = htons (0); + lcl_sockname.sin_addr.s_addr = 0; + status = bind (my_skt, (struct sockaddr *) &lcl_sockname, + sizeof (lcl_sockname)); + if (status == -1) { + close (my_skt); + AsynSrv_errcode = AsynSrv__BAD_BIND; + GetErrno (&AsynSrv_errno, &AsynSrv_vaxc_errno); /* Save the errno info */ + fprintf (stderr, "\nAsynSrv_Open/bind: Failed to bind socket.\n"); + return False; + } + /*--------------------------- + ** Set short time-out (VMS systems only) + */ +#ifdef __VMS + oto_len = sizeof (old_time_out); /* Save current time-out first */ + oto_status = getsockopt (my_skt, IPPROTO_TCP, UCX$C_TCP_PROBE_IDLE, + old_time_out, &oto_len); + + if (oto_status == 0) { + time_out.val = AsynSrv_connect_tmo; /* Set new time-out */ + status = setsockopt (my_skt, IPPROTO_TCP, UCX$C_TCP_PROBE_IDLE, + time_out.chars, sizeof (time_out)); + } +#endif + /*--------------------------- + ** Connect to RS-232-C Server. + */ + rmt_sockname_len = sizeof (rmt_sockname); + rmt_sockname.sin_family = AF_INET; + rmt_sockname.sin_port = htons (asyn_info->port); + rmt_sockname.sin_addr.s_addr = rmt_inet_addr_pntr->s_addr; + status = connect (my_skt, (struct sockaddr *) &rmt_sockname, + sizeof (rmt_sockname)); + if (status != 0) { + close (my_skt); + AsynSrv_errcode = AsynSrv__BAD_CONNECT; + GetErrno (&AsynSrv_errno, &AsynSrv_vaxc_errno); /* Save the errno info */ + fprintf (stderr, + "\nAsynSrv_Open/connect: Failed to connect to server.\n"); + perror ("AsynSrv_Open"); + return False; + } + /*--------------------------- + ** Restore time-out (VMS only) + */ +#ifdef __VMS + if (oto_status == 0) { + setsockopt (my_skt, IPPROTO_TCP, UCX$C_TCP_PROBE_IDLE, + old_time_out, oto_len); + } +#endif + /*--------------------------------------------------- + ** Setup the defaults in the AsynSrv__info data structure. + */ + asyn_info->skt = my_skt; /* Return socket number to caller */ + + asyn_info->protocol_code = 0; /* Ensure protocol_code set to "unknown" */ + memcpy (asyn_info->protocol_id, "\0\0\0\0", + sizeof (asyn_info->protocol_id)); + asyn_info->cmnd_hdr_len = 4; + strcpy (asyn_info->cmnd_fmt, "%04d"); + asyn_info->rply_hdr_len = 4; + strcpy (asyn_info->rply_fmt, "%4d"); + + sprintf (buff, "%04d", asyn_info->chan); /* Convert channel # to ASCII */ + memcpy (asyn_info->chan_char, buff, sizeof (asyn_info->chan_char)); + + sprintf (buff, "%04d", AsynSrv_msec_tmo/100); /* Set dflt time-out .. + ** .. (deci-secs) */ + memcpy (asyn_info->tmo, buff, sizeof (asyn_info->tmo)); + + memcpy (asyn_info->eot, AsynSrv_eot, sizeof (asyn_info->eot)); /* Set .. + ** .. dflt terminator(s) */ + asyn_info->max_replies = 0; + asyn_info->n_replies = 0; + /* + ** Send a null command buffer to the server. This should give + ** a "protocol mismatch" error response and from this we can get + ** the actual protocol level supported by the server. + */ + status = AsynSrv_SendCmnds (asyn_info, &s_buff, &r_buff, NULL); + if (!status && (AsynSrv_errcode == AsynSrv__BAD_PROT_LVL)) { + /* + ** As expected, we got a "protocol mismatch" error. + ** Save the server's protocol level for future use. + */ + memcpy (asyn_info->protocol_id, r_buff.s_pcol_lvl, + sizeof (r_buff.s_pcol_lvl)); + if (strncmp (r_buff.s_pcol_lvl, RS__PROTOCOL_ID_V01B, + strlen (RS__PROTOCOL_ID_V01B)) == 0) { + asyn_info->protocol_code = RS__PROTOCOL_CODE_V01B; + asyn_info->cmnd_hdr_len = 4; + strcpy (asyn_info->cmnd_fmt, "%04d"); + asyn_info->rply_hdr_len = 4; + strcpy (asyn_info->rply_fmt, "%4d"); + }else if (strncmp (r_buff.s_pcol_lvl, RS__PROTOCOL_ID, + strlen (RS__PROTOCOL_ID)) == 0) { + asyn_info->protocol_code = RS__PROTOCOL_CODE; + asyn_info->cmnd_hdr_len = 2; + strcpy (asyn_info->cmnd_fmt, "%02d"); + asyn_info->rply_hdr_len = 2; + strcpy (asyn_info->rply_fmt, "%2d"); + }else { + close (my_skt); + asyn_info->skt = 0; + fprintf (stderr, + "\nAsynSrv_Open: Server protocol level is unrecognised.\n" + " Server level is \"%4s\"\n", r_buff.s_pcol_lvl); + return False; + } + }else { + close (my_skt); + asyn_info->skt = 0; + AsynSrv_errcode = AsynSrv__BAD_PROT_LVL; + fprintf (stderr, + "\nAsynSrv_Open: Problem getting protocol level of Server!\n"); + return False; + } + /*--------------------------------------------------- + ** Complete the setup of the connection table entry + */ + AsynSrv_HPS_list[AsynSrv_n_cnct].skt = my_skt; + AsynSrv_HPS_list[AsynSrv_n_cnct].protocol_code = asyn_info->protocol_code; + memcpy (AsynSrv_HPS_list[AsynSrv_n_cnct].protocol_id, + asyn_info->protocol_id, sizeof (asyn_info->protocol_id)); + AsynSrv_HPS_list[AsynSrv_n_cnct].cmnd_hdr_len = asyn_info->cmnd_hdr_len; + AsynSrv_HPS_list[AsynSrv_n_cnct].rply_hdr_len = asyn_info->rply_hdr_len; + AsynSrv_HPS_list[AsynSrv_n_cnct].usage_cnt = 1; + AsynSrv_n_cnct++; + + AsynSrv_active[AsynSrv_n_active] = /* Remember the open in case .. */ + asyn_info; /* .. there's a force-exit */ + AsynSrv_n_active++; + + AsynSrv_errcode = AsynSrv_errno = AsynSrv_vaxc_errno = 0; + AsynSrv_call_depth = 0; + return True; + } +/* +**--------------------------------------------------------------------------- +** AsynSrv_Force: Open a connection to an RS-232-C Server. +** Thereby insisting on an own socket. +*/ + int AsynSrv_Force ( +/* ============ +*/ struct AsynSrv__info *asyn_info) { + + int i, status; + int my_skt; + char old_time_out[4]; + union { + char chars[4]; + int val; + } time_out; + char buff[128]; + struct RS__MsgStruct s_buff; + struct RS__RespStruct r_buff; + unsigned int oto_len, oto_status; + struct hostent *rmt_hostent; + struct in_addr *rmt_inet_addr_pntr; + struct in_addr rmt_inet_addr; + int rmt_sockname_len; + struct sockaddr_in lcl_sockname; + struct sockaddr_in rmt_sockname; + + char *errtxt_ptr; + int errcode, my_errno, my_vaxc_errno; +/*-------------------------------------------------------- +*/ + asyn_info->skt = 0; +/*-------------------------------------------------------- +** Initialise the error info stack and pre-set the +** routine name (in case of error). +*/ + AsynSrv_errcode = AsynSrv_errno = AsynSrv_vaxc_errno = 0; + strcpy (AsynSrv_routine[0], "AsynSrv_Open"); + AsynSrv_call_depth = 1; +/*-------------------------------------------------------- +** But, before going any further, do some quick checks on +** values in asyn_info. +*/ + if ((asyn_info->port <= 0) || + (asyn_info->port > 65535)) { + AsynSrv_errcode = AsynSrv__BAD_PAR; /* Something is bad! */ + return False; + } + if (asyn_info->chan < 0) asyn_info->chan = 0; + if (asyn_info->chan > 255) asyn_info->chan = 0; +/*-------------------------------------------------------- +** Set up a new connection. +*/ + /*--------------------------- + ** Get the Internet address of the server. + */ + rmt_inet_addr.s_addr = inet_addr (asyn_info->host); + if (rmt_inet_addr.s_addr != -1) { + rmt_inet_addr_pntr = &rmt_inet_addr; + }else { + rmt_hostent = gethostbyname (asyn_info->host); + if (rmt_hostent == NULL) { + AsynSrv_errcode = AsynSrv__BAD_HOST; + GetErrno (&AsynSrv_errno, &AsynSrv_vaxc_errno); /* Save errno info */ + fprintf (stderr, "\nAsynSrv_Open/gethostbyname: Failed to get Internet " + "address of \"%s\".\n", asyn_info->host); + return False; + } + rmt_inet_addr_pntr = (struct in_addr *) rmt_hostent->h_addr_list[0]; + } + /*--------------------------- + ** Create a TCP/IP socket for connecting to server and bind it. + */ + my_skt = socket (AF_INET, SOCK_STREAM, 0); + if (my_skt <= 0) { + AsynSrv_errcode = AsynSrv__BAD_SOCKET; + GetErrno (&AsynSrv_errno, &AsynSrv_vaxc_errno); /* Save the errno info */ + fprintf (stderr, "\nAsynSrv_Open/socket: Failed to create a socket.\n"); + return False; + } + lcl_sockname.sin_family = AF_INET; + lcl_sockname.sin_port = htons (0); + lcl_sockname.sin_addr.s_addr = 0; + status = bind (my_skt, (struct sockaddr *) &lcl_sockname, + sizeof (lcl_sockname)); + if (status == -1) { + close (my_skt); + AsynSrv_errcode = AsynSrv__BAD_BIND; + GetErrno (&AsynSrv_errno, &AsynSrv_vaxc_errno); /* Save the errno info */ + fprintf (stderr, "\nAsynSrv_Open/bind: Failed to bind socket.\n"); + return False; + } + /*--------------------------- + ** Set short time-out (VMS systems only) + */ +#ifdef __VMS + oto_len = sizeof (old_time_out); /* Save current time-out first */ + oto_status = getsockopt (my_skt, IPPROTO_TCP, UCX$C_TCP_PROBE_IDLE, + old_time_out, &oto_len); + + if (oto_status == 0) { + time_out.val = AsynSrv_connect_tmo; /* Set new time-out */ + status = setsockopt (my_skt, IPPROTO_TCP, UCX$C_TCP_PROBE_IDLE, + time_out.chars, sizeof (time_out)); + } +#endif + /*--------------------------- + ** Connect to RS-232-C Server. + */ + rmt_sockname_len = sizeof (rmt_sockname); + rmt_sockname.sin_family = AF_INET; + rmt_sockname.sin_port = htons (asyn_info->port); + rmt_sockname.sin_addr.s_addr = rmt_inet_addr_pntr->s_addr; + status = connect (my_skt, (struct sockaddr *) &rmt_sockname, + sizeof (rmt_sockname)); + if (status != 0) { + close (my_skt); + AsynSrv_errcode = AsynSrv__BAD_CONNECT; + GetErrno (&AsynSrv_errno, &AsynSrv_vaxc_errno); /* Save the errno info */ + fprintf (stderr, + "\nAsynSrv_Open/connect: Failed to connect to server.\n"); + perror ("AsynSrv_Open"); + return False; + } + /*--------------------------- + ** Restore time-out (VMS only) + */ +#ifdef __VMS + if (oto_status == 0) { + setsockopt (my_skt, IPPROTO_TCP, UCX$C_TCP_PROBE_IDLE, + old_time_out, oto_len); + } +#endif + /*--------------------------------------------------- + ** Setup the defaults in the AsynSrv__info data structure. + */ + asyn_info->skt = my_skt; /* Return socket number to caller */ + + asyn_info->protocol_code = 0; /* Ensure protocol_code set to "unknown" */ + memcpy (asyn_info->protocol_id, "\0\0\0\0", + sizeof (asyn_info->protocol_id)); + asyn_info->cmnd_hdr_len = 4; + strcpy (asyn_info->cmnd_fmt, "%04d"); + asyn_info->rply_hdr_len = 4; + strcpy (asyn_info->rply_fmt, "%4d"); + + sprintf (buff, "%04d", asyn_info->chan); /* Convert channel # to ASCII */ + memcpy (asyn_info->chan_char, buff, sizeof (asyn_info->chan_char)); + + sprintf (buff, "%04d", AsynSrv_msec_tmo/100); /* Set dflt time-out .. + ** .. (deci-secs) */ + memcpy (asyn_info->tmo, buff, sizeof (asyn_info->tmo)); + + memcpy (asyn_info->eot, AsynSrv_eot, sizeof (asyn_info->eot)); /* Set .. + ** .. dflt terminator(s) */ + asyn_info->max_replies = 0; + asyn_info->n_replies = 0; + /* + ** Send a null command buffer to the server. This should give + ** a "protocol mismatch" error response and from this we can get + ** the actual protocol level supported by the server. + */ + status = AsynSrv_SendCmnds (asyn_info, &s_buff, &r_buff, NULL); + if (!status && (AsynSrv_errcode == AsynSrv__BAD_PROT_LVL)) { + /* + ** As expected, we got a "protocol mismatch" error. + ** Save the server's protocol level for future use. + */ + memcpy (asyn_info->protocol_id, r_buff.s_pcol_lvl, + sizeof (r_buff.s_pcol_lvl)); + if (strncmp (r_buff.s_pcol_lvl, RS__PROTOCOL_ID_V01B, + strlen (RS__PROTOCOL_ID_V01B)) == 0) { + asyn_info->protocol_code = RS__PROTOCOL_CODE_V01B; + asyn_info->cmnd_hdr_len = 4; + strcpy (asyn_info->cmnd_fmt, "%04d"); + asyn_info->rply_hdr_len = 4; + strcpy (asyn_info->rply_fmt, "%4d"); + }else if (strncmp (r_buff.s_pcol_lvl, RS__PROTOCOL_ID, + strlen (RS__PROTOCOL_ID)) == 0) { + asyn_info->protocol_code = RS__PROTOCOL_CODE; + asyn_info->cmnd_hdr_len = 2; + strcpy (asyn_info->cmnd_fmt, "%02d"); + asyn_info->rply_hdr_len = 2; + strcpy (asyn_info->rply_fmt, "%2d"); + }else { + close (my_skt); + asyn_info->skt = 0; + fprintf (stderr, + "\nAsynSrv_Open: Server protocol level is unrecognised.\n" + " Server level is \"%4s\"\n", r_buff.s_pcol_lvl); + return False; + } + }else { + close (my_skt); + asyn_info->skt = 0; + AsynSrv_errcode = AsynSrv__BAD_PROT_LVL; + fprintf (stderr, + "\nAsynSrv_Open: Problem getting protocol level of Server!\n"); + return False; + } + AsynSrv_errcode = AsynSrv_errno = AsynSrv_vaxc_errno = 0; + AsynSrv_call_depth = 0; + return True; + } +/* +**--------------------------------------------------------------------------- +** AsynSrv_SendCmnds: Send commands to RS232C server. +*/ + int AsynSrv_SendCmnds ( +/* ================= +*/ struct AsynSrv__info *asyn_info, + struct RS__MsgStruct *send_buff, + struct RS__RespStruct *rcve_buff, + ...) { /* Now we have list of commands - + ** char *txt = pntr to cmnd strng + ** Terminate list with *txt = NULL. + */ + int i, status, c_len, size, max_size, ncmnds; + int bytes_to_come, bytes_left; + char *nxt_byte_ptr; + char err_text[80]; + char text[20]; + va_list ap; /* Pointer to variable args */ + char *txt_ptr; + char *cmnd_lst_ptr; + /*---------------------------------------------- + ** Pre-set the routine name (in case of error) + */ + if (AsynSrv_errcode == 0 && AsynSrv_call_depth < 5) { + strcpy (AsynSrv_routine[AsynSrv_call_depth], "AsynSrv_SendCmnds"); + AsynSrv_call_depth++; + } + /*---------------------------------------------- + ** Do nothing if no connection - the connection gets + ** closed if an error is detected. The connection may + ** also be marked to have been forcefully closed. + */ + if (asyn_info->skt <= 0) { + memset (rcve_buff->msg_size, + '0', sizeof (rcve_buff->msg_size)); + if ((AsynSrv_errcode == 0) && (asyn_info->skt < 0)) { + AsynSrv_errcode = AsynSrv__FORCED_CLOSED; + } + return False; + } + /*---------------------------------------------- + ** Build message for server from the list of commands. + */ + asyn_info->max_replies = asyn_info->n_replies = 0; + + asyn_info->msg_id++; /* Set up an incrementing message id */ + if (asyn_info->msg_id > 9999) asyn_info->msg_id = 1; + sprintf (send_buff->msg_id, "%04d", asyn_info->msg_id); + + memcpy (send_buff->c_pcol_lvl, asyn_info->protocol_id, + sizeof (send_buff->c_pcol_lvl)); + + memcpy (send_buff->serial_port, asyn_info->chan_char, + sizeof (send_buff->serial_port)); + + memcpy (send_buff->tmo, asyn_info->tmo, sizeof (send_buff->tmo)); + + memcpy (send_buff->terms, asyn_info->eot, sizeof (send_buff->terms)); + + memcpy (send_buff->n_cmnds, "0000", sizeof (send_buff->n_cmnds)); + + va_start (ap, rcve_buff); /* Set up var arg machinery */ + + txt_ptr = va_arg (ap, char *); /* Get pntr to next cmnd string */ + ncmnds = 0; + cmnd_lst_ptr = &send_buff->cmnds[0]; + bytes_left = sizeof (*send_buff) - + OffsetOf (struct RS__MsgStruct, cmnds[0]); + + while (txt_ptr != NULL) { + c_len = strlen (txt_ptr); + size = asyn_info->cmnd_hdr_len + c_len; + if (size > bytes_left) { + AsynSrv_errcode = AsynSrv__BAD_SENDLEN; /* Too much to send */ + fprintf (stderr, "\nAsynSrv_SendCmnds/send: too much to send" + " - request ignored.\n"); + memset (rcve_buff->msg_size, '0', sizeof (rcve_buff->msg_size)); + return False; + }else { + sprintf (cmnd_lst_ptr, asyn_info->cmnd_fmt, c_len); + if (cmnd_lst_ptr[asyn_info->cmnd_hdr_len] != '\0') { + AsynSrv_errcode = AsynSrv__BAD_CMND_LEN; + fprintf (stderr, "\nAsynSrv_SendCmnds/send: command too long -" + " - request ignored.\n"); + memset (rcve_buff->msg_size, '0', sizeof (rcve_buff->msg_size)); + return False; + } + cmnd_lst_ptr += asyn_info->cmnd_hdr_len; + strcpy (cmnd_lst_ptr, txt_ptr); + cmnd_lst_ptr += c_len; + ncmnds++; + bytes_left = bytes_left - size; + txt_ptr = va_arg (ap, char *); + } + } + sprintf (text, "%04d", ncmnds); + memcpy (send_buff->n_cmnds, text, sizeof (send_buff->n_cmnds)); + + size = cmnd_lst_ptr - send_buff->msg_id; + size = (size + 3) & (~3); /* Round up to multiple of 4 */ + sprintf (text, "%04d", size); + memcpy (send_buff->msg_size, text, sizeof (send_buff->msg_size)); + + size += sizeof (send_buff->msg_size); + status = send (asyn_info->skt, + (char *) send_buff, size, 0); + if (status != size) { + GetErrno (&AsynSrv_errno, &AsynSrv_vaxc_errno); + if (status == 0) { + AsynSrv_errcode = AsynSrv__BAD_SEND; /* Server exited (probably) */ + fprintf (stderr, "\nAsynSrv_SendCmnds/send: probable network problem"); + }else if (status == -1) { + if (AsynSrv_errno == EPIPE) { + AsynSrv_errcode = AsynSrv__BAD_SEND_PIPE; /* Server exited (probably) */ + fprintf (stderr, "\nAsynSrv_SendCmnds/send: broken network pipe"); + }else { + AsynSrv_errcode = AsynSrv__BAD_SEND_NET; /* It's some other net problem */ + perror ("AsynSrv_SendCmnds/send"); + } + }else { + AsynSrv_errcode = AsynSrv__BAD_SEND_UNKN; /* TCP/IP problems */ + fprintf (stderr, "\nAsynSrv_SendCmnds/send: probable TCP/IP problem"); + } + AsynSrv_Close (asyn_info, True); /* Force close TCP/IP connection */ + fprintf (stderr, " - link to server force-closed.\n"); + return False; + } + + size = sizeof (rcve_buff->msg_size); + status = recv (asyn_info->skt, rcve_buff->msg_size, size, 0); + if (status != size) { + GetErrno (&AsynSrv_errno, &AsynSrv_vaxc_errno); + if (status == 0) { + AsynSrv_errcode = AsynSrv__BAD_RECV; /* Server exited (probably) */ + fprintf (stderr, "\nAsynSrv_SendCmnds/recv: probable network problem"); + }else if (status == -1) { + if (AsynSrv_errno == EPIPE) { + AsynSrv_errcode = AsynSrv__BAD_RECV_PIPE; /* Server exited (probably) */ + fprintf (stderr, "\nAsynSrv_SendCmnds/recv: broken network pipe"); + }else { + AsynSrv_errcode = AsynSrv__BAD_RECV_NET; /* It's some other net problem */ + perror ("AsynSrv_SendCmnds/recv"); + } + }else { + AsynSrv_errcode = AsynSrv__BAD_RECV_UNKN; /* TCP/IP problems */ + fprintf (stderr, "\nAsynSrv_SendCmnds/recv: probable TCP/IP problem"); + } + AsynSrv_Close (asyn_info, True); /* Force close TCP/IP connection */ + fprintf (stderr, " - link to server force-closed.\n"); + return False; + } + if (sscanf (rcve_buff->msg_size, "%4d", &bytes_to_come) != 1) { + AsynSrv_errcode = AsynSrv__BAD_NOT_BCD; /* Header not an ASCII BCD integer */ + AsynSrv_Close (asyn_info, True); /* Force close TCP/IP connection */ + fprintf (stderr, "\nAsynSrv_SendCmnds/recv: non-BCD byte count" + " - link to server force-closed.\n"); + return False; + } + max_size = sizeof (*rcve_buff) - size; + if (bytes_to_come > max_size) { + AsynSrv_errcode = AsynSrv__BAD_RECVLEN; + fprintf (stderr, "\nAsynSrv_SendCmnds/recv: pending message length too big" + " - flushing ...\n"); + nxt_byte_ptr = &rcve_buff->msg_size[size]; + while (bytes_to_come > 0) { /* Flush out the incoming message */ + bytes_left = bytes_to_come; + if (bytes_left > max_size) bytes_left = max_size; + status = recv (asyn_info->skt, nxt_byte_ptr, bytes_left, 0); + if (status <= 0) { + AsynSrv_errcode = AsynSrv__BAD_FLUSH; /* TCP/IP problem during flush */ + GetErrno (&AsynSrv_errno, &AsynSrv_vaxc_errno); + AsynSrv_Close (asyn_info, True); /* Force close TCP/IP connection */ + fprintf (stderr, "\nAsynSrv_SendCmnds/recv: network problem during" + " flush.\nLink to server force-closed.\n"); + return False; + } + bytes_to_come = bytes_to_come - status; + } + fprintf (stderr, "\n flushed OK.\n"); + memset (rcve_buff->msg_size, '0', sizeof (rcve_buff->msg_size)); + return False; + }else { + nxt_byte_ptr = &rcve_buff->msg_size[size]; + bytes_left = bytes_to_come; + while (bytes_left > 0) { /* Read the rest of the response */ + status = recv (asyn_info->skt, nxt_byte_ptr, bytes_left, 0); + if (status <= 0) { + GetErrno (&AsynSrv_errno, &AsynSrv_vaxc_errno); + if (status == 0) { + AsynSrv_errcode = AsynSrv__BAD_RECV1; /* Server exited (probably) */ + fprintf (stderr, "\nAsynSrv_SendCmnds/recv/1: probable network " + "problem"); + }else { + if (AsynSrv_errno == EPIPE) { + AsynSrv_errcode = AsynSrv__BAD_RECV1_PIPE; /* Server exited (probably) */ + fprintf (stderr, "\nAsynSrv_SendCmnds/recv/1: broken network pipe"); + }else { + AsynSrv_errcode = AsynSrv__BAD_RECV1_NET; /* It's some other net fault */ + perror ("AsynSrv_SendCmnds/recv/1"); + } + } + AsynSrv_Close (asyn_info, True); /* Force close TCP/IP connection */ + fprintf (stderr, " - link to server force-closed.\n"); + return False; + } + bytes_left = bytes_left - status; + nxt_byte_ptr = nxt_byte_ptr + status; + } + if (strncmp (asyn_info->protocol_id, rcve_buff->s_pcol_lvl, + sizeof (rcve_buff->s_pcol_lvl)) != 0) { + AsynSrv_errcode = AsynSrv__BAD_PROT_LVL; /* Protocol level is bad */ + return False; + } + if ((sscanf (rcve_buff->n_rply, "%4d", &asyn_info->max_replies) != 1) || + (asyn_info->max_replies < 0)) { + AsynSrv_errcode = AsynSrv__BAD_REPLY; /* Reply is bad */ + if (AsynSrv_call_depth < 5) { /* Add reply to routine stack */ + bytes_to_come = bytes_to_come + 4; + if (bytes_to_come >= sizeof (AsynSrv_routine[0])) + bytes_to_come = sizeof (AsynSrv_routine[0]) - 1; + for (i=0; imsg_size[i] == '\0') + rcve_buff->msg_size[i] = '.'; + } + rcve_buff->msg_size[bytes_to_come] = '\0'; + strcpy (AsynSrv_routine[AsynSrv_call_depth], rcve_buff->msg_size); + AsynSrv_call_depth++; + } + return False; + } + } + AsynSrv_call_depth--; + return True; + } +/*-------------------------------------------- End of AsynSrv_Utility.C -----*/ diff --git a/hardsup/asynsrv_utility.c b/hardsup/asynsrv_utility.c new file mode 100644 index 0000000..2214adf --- /dev/null +++ b/hardsup/asynsrv_utility.c @@ -0,0 +1,2121 @@ +#define ident "1C06" +#ifdef VAXC +#module AsynSrv_Utility ident +#endif +#ifdef __DECC +#pragma module AsynSrv_Utility ident +#endif +/* +** +--------------------------------------------------------------+ +** | Paul Scherrer Institute | +** | Department ASQ | +** | | +** | This software may be used freely by non-profit organizations.| +** | It may be copied provided that the name of P.S.I. and of the | +** | author is included. Neither P.S.I. nor the author assume any | +** | responsibility for the use of this software outside of P.S.I.| +** +--------------------------------------------------------------+ +** +** Module Name . . . . . . . . : [...LIB.SINQ]AsynSrv_Utility.C +** +** Author . . . . . . . . . . : D. Maden +** Date of creation . . . . . . : Mar 1996 +** +** To compile this module, use: + + $ define/group sinq_c_tlb mad_lib:sinq_c.tlb + $ cc /debug /noopt /obj=[]AsynSrv_Utility - + lnsa01::tasmad_disk:[mad.psi.lib.sinq]AsynSrv_Utility + + sinq_c_tlb/lib + +** To include this module in SINQ.OLB, use: + + $ define/group sinq_c_tlb mad_lib:sinq_c.tlb + $ + $ define/group sinq_olb mad_lib:sinq_dbg.olb + $ @lnsa01::tasmad_disk:[mad.psi.lib.sinq]sinq_olb AsynSrv_Utility debug + $ + $ define/group sinq_olb mad_lib:sinq.olb + $ @lnsa01::tasmad_disk:[mad.psi.lib.sinq]sinq_olb AsynSrv_Utility +** +** Updates: +** 1A01 21-Mar-1996 DM. Initial version. +** 1B01 12-Sep-1996 DM. Allow host name to be in dot format too. +** 1B02 5-May-1997 DM. Set 5 sec time-out on "connect" on VMS systems. +** 1B07 11-Mar-1998 DM. Allow range of msecTmo to be 0 - 999999 (it was +** 100 - 999999). +** 1C01 21-Mar-2000 MZ. Introduced idleHandler +** 1C02 30-Mar-2000 DM. Add trace and flush facilities. +** 1C06 30-Aug-2000 DM. Add AsynSrv_GetLenTerm. +**============================================================================ +** The entry points included in this module are described below. Prototypes +** can be defined via: +** +** #include +** +** AsynSrv_ChanClose - Send a "CLOSE CHAN" request to RS-232-C Server. +** AsynSrv_Close - Close a connection to an RS-232-C Server. +** AsynSrv_Config - Configure an open AsynSrv_Utility connection. +** AsynSrv_ConfigDflt - Set defaults for AsynSrv_Open. +** AsynSrv_ErrInfo - Return detailed status from last operation. +** AsynSrv_Flush - Send a "FLUSH" request to an RS-232-C Server. +** AsynSrv_GetLenTerm - Get length and terminator of a reply. +** AsynSrv_GetReply - Get next reply from a reply buffer. +** AsynSrv_Open - Open a connection to an RS-232-C Server. +** AsynSrv_OpenNew - Same as AsynSrv_Open but forces the opening +** of a new socket. +** AsynSrv_SendCmnds - Send commands to a channel of an RS-232-C Server. +** AsynSrv_SendCmndsBig - Similar to AsynSrv_SendCmnds but with user +** defined buffer sizes. +** AsynSrv_Trace - Send a "TRACE ON" or "TRACE OFF" request to +** an RS-232-C Server. +** AsynSrv_Trace_Write - Send a "TRACE WRITE" request to RS-232-C Server. +** Other entry points which are private (i.e. not in ): +** AsynSrv_SendSpecCmnd - Send a "special" command to an RS-232-C Server. +**--------------------------------------------------------------------- +** int AsynSrv_ChanClose (&asyn_info) +** ----------------- +** Input Args: +** struct AsynSrv__info *asyn_info - the structure used in the call to +** AsynSrv_Open. It contains settings required +** for setting up and sending send_buff. +** Output Args: +** none +** Modified Args: +** none +** Return status: +** True if no problems detected, otherwise False and errcode (see +** AsynSrv_ErrInfo) is set to indicate the nature of the problem. +** See AsynSrv_SendSpecCmnd for possible error codes. +** Routines called: +** AsynSrv_SendSpecCmnd +** Description: +** AsynSrv_SendSpecCmnd is called to send the 4-byte "special" +** command "-006" to the server to cause it to close its serial ports. +**--------------------------------------------------------------------- +** int AsynSrv_Close (&asyn_info, force_flag) +** ------------- +** Input Args: +** int force_flag - if non-zero, all connections using the same socket +** will also be marked as force-closed (socket number +** set to -1) and the connection will really be +** closed. This is needed for error recovery operations. +** Output Args: +** none +** Modified Args: +** struct AsynSrv__info *asyn_info - a structure holding skt, host and +** port of the connection. On return +* skt = 0. +** Return status: +** True if no problems detected, otherwise False and AsynSrv_errcode +** is set to indicate the nature of the problem as follows: +** ASYNSRV__BAD_PAR --> skt does not match with host/port. +** Routines called: +** Socket library, "close". +** Description: +** The routine decrements the usage count on the connection to host/port. +** If the counter is still >0, the routine simply returns. +** If the counter is now 0, the routine sends a "-001" message to the +** server to inform it that we are about to close the link, waits for a +** possible 4 bytes of response and then closes the TCP/IP connection. +**--------------------------------------------------------------------- +** int AsynSrv_Config (&asyn_info, &par_id, par_val, ...) +** -------------- +** Input Args: +** char* par_id - Text string identifying the next argument (see below). +** NULL indicates the end of the argument list. +** par_val - The value to set for the argument. The type of the +** argument can depend on par_id. +** Output Args: +** none +** Modified Args: +** struct AsynSrv__info *asyn_info - the structure used in the call to +** AsynSrv_Open. It is used to hold the config +** info for the connection. +** Return status: +** True if no problems detected, otherwise False and AsynSrv_errcode +** is set to indicate the nature of the problem as follows: +** ASYNSRV__BAD_PAR --> Unrecognised par_id or msecTmo < 0 or +** msecTmo > 999'999 or bad eot or .. +** Routines called: +** None +** Description: +** AsynSrv_Config may be used for setting values of parameters for +** use in subsequent calls to AsynSrv_SendCmnds. Defaults for these +** parameters are set via a call to AsynSrv_ConfigDflt, prior to +** calling AsynSrv_Open. Values which may be taken by par_id (warning -- +** par_id is case-sensitive) and the corresponding variable type of +** par_val are: +** +** "msecTmo" int The time-out response for commands sent +** to a serial channel on the server. The +** valid range is 0 to 999'999. +** "eot" char* The expected terminators in responses to +** commands sent to a serial channel on the +** server. The first character specifies the +** number of terminators (max=3). +** "idleHdl" void (*hdl) (int msecTmo, int socket) MZ. +** A handler which is called in AsynSrv_SendCmds +** before receiving the response. The handler +** should contain a call to "select ()" and return +** on a read event on the socket passed as +** argument, or after the timeout specified +** has expired. +**--------------------------------------------------------------------- +** int AsynSrv_ConfigDflt (&par_id, par_val, ...) +** ------------------ +** Input Args: +** char* par_id - Text string identifying the next argument (see below). +** NULL indicates the end of the argument list. +** par_val - The value to set for the argument. The type of the +** argument can depend on par_id. +** Output Args: +** none +** Modified Args: +** none +** Return status: +** True if no problems detected, otherwise False and AsynSrv_errcode +** is set to indicate the nature of the problem as follows: +** ASYNSRV__BAD_PAR --> Unrecognised par_id or msecTmo < 0 or +** msecTmo > 999'999 or bad eot or .. +** Routines called: +** None +** Description: +** AsynSrv_ConfigDflt may be used for setting default values of parameters +** for use in subsequent calls to AsynSrv_Open. Values which may be taken +** by par_id (warning -- par_id is case-sensitive) and the corresponding +** variable type of par_val are: +** +** "TmoC" int The time-out in seconds to be used when +** opening a connection to a server. This +** value is only effective on VMS systems. For +** UNIX systems, the systemwide default (usually +** 75 secs) cannot be changed. The initial +** setting for "TmoC" is 5 secs. +** "msecTmo" int The time-out response for commands sent +** to a serial channel on the server. The +** valid range is 0 to 999'999. The initial +** setting for "msecTmo" is 10'000 msec. +** "eot" char* The expected terminators in responses to +** commands sent to a serial channel on the +** server. The first character specifies the +** number of terminators (max=3). The initial +** setting for "eot" is "1\r". +**------------------------------------------------------------------------- +** void AsynSrv_ErrInfo (&entry_txt_ptr, &errcode, &my_errno, &vaxc_errno) +** --------------- +** Input Args: +** None +** Output Args: +** char **entry_txt_ptr - Pointer to a text string giving the call stack +** at the time that the error was detected. +** int *errcode - An internal error code indicating the detected error. +** int *my_errno - Saved value of errno. +** int *vaxc_errno - Saved value of vaxc$errno (OpenVMS only). +** Modified Args: +** none +** Return status: +** none +** Routines called: +** none +** Description: +** Returns detailed status of the last operation. Once an error has been +** detected, the error status is frozen until this routine has been called. +**------------------------------------------------------------------------- +** int AsynSrv_Flush (&asyn_info) +** ------------- +** Input Args: +** struct AsynSrv__info *asyn_info - the structure used in the call to +** AsynSrv_Open. It contains settings required +** for setting up and sending send_buff. +** Output Args: +** none +** Modified Args: +** none +** Return status: +** Same as AsynSrv_ChanClose +** Routines called: +** Same as AsynSrv_ChanClose +** Description: +** AsynSrv_SendSpecCmnd is called to send the 4-byte "special" +** command "-004" to the server to cause it to close its serial ports. +**------------------------------------------------------------------------- +** int AsynSrv_GetLenTerm (&asyn_info, &rcve_buff, &rply, *len, &term) +** ------------------ +** Input Args: +** struct AsynSrv__info *asyn_info - the structure used in the call to +** AsynSrv_Open. It is used to hold status info +** between calls to this routine. +** struct RS__RespStruct *rcve_buff - address of receive buffer used +** in last call to AsynSrv_SendCmnds. +** char *rply - address of a reply in rcve_buff as +** returned by AsynSrv_GetReply. +** Output Args: +** int *len - address of location to receive the +** length of the reply. +** char *term - address of location to receive the +** terminator of the reply. +** Modified Args: +** none +** Return status: +** True if everything seems to be OK. Otherwise, False. +** Routines called: +** none +** Description: +** AsynSrv_GetLenTerm simply converts the length of the reply as saved +** in rply[-hdr_size-1] from ASCII to binary, subtracts 2 from it (to +** allow for the terminator byte and the null termination character) and +** returns it to the caller. *term is set to the value of the character +** at location rply[-1]. +**------------------------------------------------------------------------- +** char *AsynSrv_GetReply (&asyn_info, &rcve_buff, &last_rply) +** ---------------- +** Input Args: +** struct RS__RespStruct *rcve_buff - address of receive buffer used +** in last call to AsynSrv_SendCmnds. +** char *last_rply - Address of last reply processed +** or NULL. +** Output Args: +** none +** Modified Args: +** struct AsynSrv__info *asyn_info - the structure used in the call to +** AsynSrv_Open. It is used to hold status info +** between calls to this routine. +** Return status: +** Address of next reply in the buffer or NULL if no more. Note that this +** is a pointer to the reply and not to the head of the reply structure. +** The terminator byte found is therefore at index [-1] from this address. +** Routines called: +** none +** Description: +** AsynSrv_GetReply unpacks the replies in the response packet from the +** RS232C server which is an argument in the call to AsynSrv_SendCmnds. +** If the routine is called with last_rply = NULL, a pointer to the +** first reply is returned. On calling AsynSrv_GetReply again with +** last_rply set to this address, one receives the address of the second +** reply and so on, until NULL is returned, indicating that all responses +** have been exhausted. +** Warning: +** AsynSrv_GetReply keeps count of the number of responses it returns. +** Responses must therefore be processed in order. +**------------------------------------------------------------------------- +** int AsynSrv_Open (&asyn_info) +** ------------ +** Input Args: +** struct AsynSrv__info *asyn_info +** asyn_info->host - Name of host offering the RS-232-C service. The name +** can be either symbolic or numeric, e.g. +** "lnsw02.psi.ch" or "129.129.90.18". +** asyn_info->port - Number of TCP/IP port of TCP/IP server. +** asyn_info->chan - Number of RS-232-C channel to be used. +** Output Args: +** none +** Modified Args: +** struct AsynSrv__info *asyn_info - a structure holding skt, host and +** port of the connection. On return +** skt = socket number of connection. +** Set to 0 if error. +** Return status: +** If non-zero, no problems detected and asyn_info->skt is the socket to +** use for communicating with the server. Otherwise, a problem +** was detected and AsynSrv_errcode may be set as follows +** to indicate the nature of the problem: +** ASYNSRV__BAD_HOST --> Call to "gethostbyname" failed to get +** network addr of host. +** ASYNSRV__BAD_SOCKET --> Call to "socket" failed. +** ASYNSRV__BAD_BIND --> Call to "bind" failed. +** ASYNSRV__BAD_CONNECT --> Call to "connect" failed. +** ASYNSRV__BAD_PAR --> Bad parameter found. Probably +** asyn_info->port or asyn_info->chan +** are out of range. +** BAD_PROT_LVL --> Server protocol level is not valid. +** ASYNSRV__NO_ROOM --> Host/port table full or Active-link +** table full. +** Routines called: +** Socket library routine "open". +** Description: +** The routine maintains a list of hosts/ports to which it has open +** sockets. If an entry is found in the list, the socket is returned +** and the usage count of this connection is incremented. If no entry +** is found in the list, a connection to the host is established and +** entered into the list. +** The routine also maintains a table of active links so that the +** "force-close" function can be performed. The link is added to this +** table too. +**------------------------------------------------------------------------- +** int AsynSrv_OpenNew (&asyn_info) +** --------------- +** Input Args: +** struct AsynSrv__info *asyn_info +** asyn_info->host - Name of host offering the RS-232-C service. The name +** can be either symbolic or numeric, e.g. +** "lnsw02.psi.ch" or "129.129.90.18". +** asyn_info->port - Number of TCP/IP port of TCP/IP server. +** asyn_info->chan - Number of RS-232-C channel to be used. +** Output Args: +** none +** Modified Args: +** struct AsynSrv__info *asyn_info - a structure holding skt, host and +** port of the connection. On return +** skt = socket number of connection. +** Set to 0 if error. +** Return status: +** See AsynSrv_Open +** Routines called: +** See AsynSrv_Open +** Description: +** This routine is the same as AsynSrv_Open but forces the opening +** of a new socket. The socket will be marked to ensure that no other +** connections share this connection. +**------------------------------------------------------------------------- +** int AsynSrv_SendCmnds (&asyn_info, &send_buff, &rcve_buff, ...) +** ----------------- +** Input Args: +** struct AsynSrv__info *asyn_info - the structure used in the call to +** AsynSrv_Open. It contains settings required +** for setting up and sending send_buff. +** char * ... - A list of commands, terminated by NULL, for +** sending to the channel on the server. The commands +** must have any necessary \r characters included. +** Output Args: +** struct RS__RespStruct *rcve_buff - a buffer to receive the response +** from the server. +** Modified Args: +** struct RS__MsgStruct *send_buff - a buffer for holding the commands +** for sending to the server. +** Return status: +** True if no problems detected, otherwise False and errcode (see +** AsynSrv_ErrInfo) is set to indicate the nature of the problem. +** AsynSrv_errcode may be set as follows: +** ASYNSRV__BAD_SEND_LEN --> Too much to send; either too many +** commands or too long. The buffer +** is 232 bytes long and each command +** has a 2-byte header. +** ASYNSRV__BAD_CMND_LEN --> A command is too long - it's length cannot +** be encoded into the command header field +** The next 4 errors are related to network errors whilst sending the +** message buffer to the server: +** ASYNSRV__BAD_SEND --> Network problem - server has +** probably abended. +** ASYNSRV__BAD_SEND_PIPE --> Network pipe broken - probably same +** cause as ASYNSRV__BAD_SEND. +** ASYNSRV__BAD_SEND_NET --> Some other network problem. "errno" +** may be helpful. +** ASYNSRV__BAD_SEND_UNKN --> Some other network problem happened +** resulting in the message not +** getting sent completely. "errno" is +** probably not helpful in this case. +** ASYNSRV__BAD_RECV \ These are network errors whilst +** ASYNSRV__BAD_RECV_PIPE > receiving the 4-byte response header. +** ASYNSRV__BAD_RECV_NET / They are analogous to ASYNSRV__BAD_SEND +** ASYNSRV__BAD_RECV_UNKN / ... ASYNSRV__BAD_SEND_UNKN. +** ASYNSRV__BAD_NOT_BCD --> The 4-byte response header is not an +** ASCII coded decimal integer. +** ASYNSRV__BAD_RECV_LEN --> The body of the response would be too +** big to fit in the input buffer. The +** buffer is ??? bytes long and each +** response has a 5-byte header and a +** trailing zero-byte. The response +** is flushed. +** ASYNSRV__BAD_FLUSH --> Some network error was detected +** during flushing. This is an "or" +** of errors ASYNSRV__BAD_RECV to +** ASYNSRV__BAD_RECV_UNKN. +** ASYNSRV__FORCED_CLOSED --> The connection to the channel has been +** forcefully closed. See below. +** ASYNSRV__BAD_REPLY --> The n_rply field of the response was +** either non-numeric or <0, indicating +** that the Terminal Server detected an +** error. The reply is added to the +** routine call stack for debug purposes. +** +** ASYNSRV__BAD_RECV1 \ These are network errors whilst receiving +** ASYNSRV__BAD_RECV1_PIPE > the body of the response. They are +** ASYNSRV__BAD_RECV1_NET / equivalent to ASYNSRV__BAD_RECV, +** ASYNSRV__BAD_RECV_PIPE and +** ASYNSRV__BAD_RECV_NET. +** ASYNSRV__FORCED_CLOSED occurs if AsynSrv_Close has been called +** for another device on the same server and the 'force_flag' +** was set (see AsynSrv_Close). The caller should call +** AsynSrv_Close and then AsynSrv_Open to re-establish a +** connection to the server. +** Routines called: +** Socket library routines send and recv. +** Description: +** The list of commands is assembled into a message buffer with appropriate +** header information and sent off to the server. The response is then +** awaited and read in when it arrives. +** +** For any of the following errors: +** ASYNSRV__BAD_SEND (Note: ASYNSRV__BAD_SEND_LEN and +** ASYNSRV__BAD_SEND_PIPE ASYNSRV__BAD_RECV_LEN and +** ASYNSRV__BAD_SEND_NET ASYNSRV__BAD_REPLY +** ASYNSRV__BAD_SEND_UNKN do not cause a close) +** ASYNSRV__BAD_RECV +** ASYNSRV__BAD_RECV_PIPE +** ASYNSRV__BAD_RECV_NET +** ASYNSRV__BAD_RECV_UNKN +** ASYNSRV__BAD_NOT_BCD +** ASYNSRV__BAD_FLUSH +** ASYNSRV__BAD_RECV1 +** ASYNSRV__BAD_RECV1_PIPE +** ASYNSRV__BAD_RECV1_NET +** the network link to the server is force-closed via a call to +** AsynSrv_Close. Once the error has been corrected, the link can be +** re-opened via a call to AsynSrv_Open. As a result of the force-close, +** other active handles will need to be released via a call to +** AsynSrv_Close before AsynSrv_Open is called. +**------------------------------------------------------------------------- +** int AsynSrv_SendCmndsBig (&asyn_info, &send_buff, send_buff_size, +** -------------------- &rcve_buff, rcve_buff_size, ...) +** Input Args: +** struct AsynSrv__info *asyn_info - the structure used in the call to +** AsynSrv_Open. It contains settings required +** for setting up and sending send_buff. +** int send_buff_size - The size of *send_buff in bytes. +** int rcve_buff_size - The size of *rcve_buff in bytes. +** int *c_len \ - a list of argument pairs specifying the commands +** char *cmd > to be sent. The list is terminated by +** ... / c_len == NULL. If *c_len > 0, it specifies the +** number of bytes in the command. Otherwise, *cmd +** is assumed to be a zero-terminated string. +** The *cmd string must include any terminator +** byte(s) but, if *c_len > 0, it does not need to +** be zero-terminated. +** Output Args: +** struct RS__RespStruct *rcve_buff - a buffer to receive the response +** from the server. Note that this structure must +** be extended to size rcve_buff_size by the use +** of suitable unions. +** Modified Args: +** struct RS__MsgStruct *send_buff - a buffer for holding the commands +** for sending to the server. Note that this +** structure must be extended to size +** send_buff_size by the use of suitable unions. +** Return status: +** Same as AsynSrv_SendCmnds with the addition of error code: +** ASYNSRV__BAD_SEND_PAR --> Either send_buff_size or rcve_buff_size +** is less than 64. +** Routines called: +** Socket library routines send and recv. +** Description: +** The procedure is similar to AsynSrv_SendCmnds except that the commands +** are specified by a pair of arguments (to allow for the binary +** transmission of zeros) and the send and receive structures are assumed +** to have been extended to the sizes specified by suitable declarations +** in the calling module. +**------------------------------------------------------------------------- +** int AsynSrv_SendSpecCmnd (&asyn_info, &cmnd) +** ------------------- +** Input Args: +** struct AsynSrv__info *asyn_info - the structure used in the call to +** AsynSrv_Open. It contains settings required +** for setting up and sending send_buff. +** char *cmnd - the 4-byte special command to be sent. +** Output Args: +** none +** Modified Args: +** none +** Return status: +** True if no problems detected, otherwise False and errcode (see +** AsynSrv_ErrInfo) is set to indicate the nature of the problem. +** AsynSrv_errcode may be set as follows: +** The next 4 errors are related to network errors whilst sending the +** 4-byte message buffer to the server: +** ASYNSRV__BAD_SEND --> Network problem - server has +** probably abended. +** ASYNSRV__BAD_SEND_PIPE --> Network pipe broken - probably same +** cause as ASYNSRV__BAD_SEND. +** ASYNSRV__BAD_SEND_NET --> Some other network problem. "errno" +** may be helpful. +** ASYNSRV__BAD_SEND_UNKN --> Some other network problem happened +** resulting in the message not +** getting sent completely. "errno" is +** probably not helpful in this case. +** ASYNSRV__BAD_RECV \ These are network errors whilst +** ASYNSRV__BAD_RECV_PIPE > receiving the 4-byte response. +** ASYNSRV__BAD_RECV_NET / They are analogous to ASYNSRV__BAD_SEND +** ASYNSRV__BAD_RECV_UNKN / ... ASYNSRV__BAD_SEND_UNKN. +** ASYNSRV__BAD_NOT_BCD --> The 4-byte response header is not an +** echo of the 4 bytes which were sent. +** ASYNSRV__FORCED_CLOSED --> The connection to the channel has been +** forcefully closed. See below. +** ASYNSRV__FORCED_CLOSED occurs if AsynSrv_Close has been called +** for another device on the same server and the 'force_flag' +** was set (see AsynSrv_Close). The caller should call +** AsynSrv_Close and then AsynSrv_Open to re-establish a +** connection to the server. +** Routines called: +** Socket library routines send and recv. +** Description: +** AsynSrv_SendSpecCmnd sends the 4-byte "special" command, cmnd, to the +** server and reads the response. The response should be an echo of the +** command which was sent. +** Note: +** For any of the following errors: +** ASYNSRV__BAD_SEND +** ASYNSRV__BAD_SEND_PIPE +** ASYNSRV__BAD_SEND_NET +** ASYNSRV__BAD_SEND_UNKN +** ASYNSRV__BAD_RECV +** ASYNSRV__BAD_RECV_PIPE +** ASYNSRV__BAD_RECV_NET +** ASYNSRV__BAD_RECV_UNKN +** ASYNSRV__BAD_NOT_BCD +** the network link to the server is force-closed via a call to +** AsynSrv_Close. Once the error has been corrected, the link can be +** re-opened via a call to AsynSrv_Open. As a result of the force-close, +** other active handles will need to be released via a call to +** AsynSrv_Close before AsynSrv_Open is called. +**------------------------------------------------------------------------- +** int AsynSrv_Trace (&asyn_info, state) +** ------------- +** Input Args: +** struct AsynSrv__info *asyn_info - the structure used in the call to +** AsynSrv_Open. It contains settings required +** for setting up and sending send_buff. +** int state - True/False to turn tracing on/off. +** Output Args: +** none +** Modified Args: +** none +** Return status: +** Same as AsynSrv_ChanClose +** Routines called: +** Same as AsynSrv_ChanClose +** Description: +** AsynSrv_SendSpecCmnd is called to send a 4-byte "special" command +** to the server. The command is "-002" to turn on tracing and "-003" +** to turn off tracing. + +** Description: +** To turn on tracing, the 4-byte message "-002" is sent to the server. +** To turn off tracing, the 4-byte message "-003" is sent to the server. +** The server is expected to respond by echoing the message. +** +**------------------------------------------------------------------------- +** int AsynSrv_Trace_Write (&asyn_info) +** ------------------- +** Input Args: +** struct AsynSrv__info *asyn_info - the structure used in the call to +** AsynSrv_Open. It contains settings required +** for setting up and sending send_buff. +** Output Args: +** none +** Modified Args: +** none +** Return status: +** Same as AsynSrv_ChanClose +** Routines called: +** Same as AsynSrv_ChanClose +** Description: +** AsynSrv_SendSpecCmnd is called to send the 4-byte "special" +** command "-005" to the server to cause it to write its trace +** buffer to disk. +**============================================================================*/ +/* +**--------------------------------------------------------------------------- +** Global Definitions +*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#ifdef __VMS +#pragma nostandard /* The "$" characters in ucx$inetdef.h give trouble! */ +#include +#include +#else +#include +#endif +/*-----------------------------------------------------------------*/ +#include +#include +#include + +#define True 1 +#define False 0 + +#define MAX_OPEN 64 + + int AsynSrv_SendSpecCmnd ( /* A prototype for a local routine */ + struct AsynSrv__info *asyn_info, + char *cmnd); +/*-------------------------------------------------------------------------- +** Global Variables +*/ + static int AsynSrv_call_depth = 0; + static char AsynSrv_routine[5][64]; + static int AsynSrv_errcode = 0; + static int AsynSrv_errno, AsynSrv_vaxc_errno; + static int AsynSrv_connect_tmo = 5; /* Time-out on "connect" */ + static int AsynSrv_msec_tmo = 10000; /* Time-out for responses */ + static char AsynSrv_eot[] = {'1', '\r', '\0','\0'}; /* Terminators */ +/* +** The following is the list of open connections (= number of +** active sockets). +*/ + static int AsynSrv_n_cnct = 0; + static struct AsynSrv_HostPortSkt AsynSrv_HPS_list[AsynSrv_MAX_LINK]; +/* +** The following is the list of active calls to AsynSrv_Open. +*/ + static int AsynSrv_n_active = 0; + static struct AsynSrv__info *AsynSrv_active[MAX_OPEN]; +/* +**--------------------------------------------------------------------------- +** AsynSrv_ChanClose: Send a "CLOSE CHAN" request to +** RS232C server. +*/ + int AsynSrv_ChanClose ( +/* ================= +*/ struct AsynSrv__info *asyn_info) { + + int status; + char cmnd[8], rply[8]; + /*---------------------------------------------- + ** Pre-set the routine name (in case of error) + */ + if (AsynSrv_errcode == 0 && AsynSrv_call_depth < 5) { + strcpy (AsynSrv_routine[AsynSrv_call_depth], "AsynSrv_ChanClose"); + AsynSrv_call_depth++; + } + /*---------------------------------------------- + ** Do nothing if no connection - the connection gets + ** closed if an error is detected. The connection may + ** also be marked to have been forcefully closed. + */ + if (asyn_info->skt <= 0) { + if ((AsynSrv_errcode == 0) && (asyn_info->skt < 0)) { + AsynSrv_errcode = ASYNSRV__FORCED_CLOSED; + } + return False; + } + /*---------------------------------------------- + ** Send message and get reply. + */ + status = AsynSrv_SendSpecCmnd (asyn_info, "-006"); + + if (AsynSrv_errcode == 0) AsynSrv_call_depth--; + return status; + } +/* +**--------------------------------------------------------------------------- +** AsynSrv_Close: Close a connection to an RS-232-C server. +*/ + int AsynSrv_Close ( +/* ============= +*/ struct AsynSrv__info *asyn_info, + int force_flag) { + + int i, j, k, my_skt; + char buff[4]; + /*----------------------------------------------- + */ + if (asyn_info == NULL) return True; /* Just return if nothing to do! */ + my_skt = asyn_info->skt; + if (my_skt <= 0) return True; /* Just return if nothing to do! */ + /*----------------------------------------------- + ** Pre-set the routinename (in case of error) + */ + if (AsynSrv_errcode == 0 && AsynSrv_call_depth < 5) { + strcpy (AsynSrv_routine[AsynSrv_call_depth], "AsynSrv_Close"); + AsynSrv_call_depth++; + } + /*------------------------------------------------------ + ** Start by finding the table entry for this connection + */ + for (i = 0; i < AsynSrv_n_cnct; i++) { + if (AsynSrv_HPS_list[i].skt != my_skt) continue; + if (AsynSrv_HPS_list[i].port != asyn_info->port) continue; + if (strcmp (AsynSrv_HPS_list[i].host, asyn_info->host) == 0) break; + } + if (i >= AsynSrv_n_cnct) { /* Did we find the entry? */ + AsynSrv_errcode = ASYNSRV__BAD_PAR; /* No! */ + return False; + } + /*------------------------------------------------------ + ** Now find the table entry for the AsynSrvOpen call. + */ + for (j = 0; j < AsynSrv_n_active; j++) { + if ((AsynSrv_active[j] == asyn_info) && + (AsynSrv_active[j]->skt == my_skt)) { + break; + } + } + if (j >= AsynSrv_n_active) { /* Did we find the entry? */ + AsynSrv_errcode = ASYNSRV__BAD_PAR; /* No! */ + return False; + } + /*------------------------------------------------------ + ** i is the index for the connection table entry. + ** j is the index for the caller's AsynSrvOpen call entry. + */ + if (AsynSrv_HPS_list[i].usage_cnt <= 0) { /* Is the connection active? */ + AsynSrv_errcode = ASYNSRV__BAD_PAR; /* No */ + return False; + } + /*------------------------------------------------------ + ** For the caller, simply set his socket number to zero, + ** mark the AsynSrvOpen entry as free and decrease the + ** usage count (the entries will be compressed later). + */ + AsynSrv_active[j]->skt = 0; /* Mark the close .. */ + AsynSrv_active[j] = NULL; /* .. and flag entry to be removed. */ + AsynSrv_HPS_list[i].usage_cnt--; /* Decrease usage count */ + /*------------------------------------------------------ + ** If this is a force-close, go through all AsynSrv_Open + ** entries looking for a socket match, mark them as + ** free and decrease usage count. + */ + if (force_flag != 0) { + for (k = 0; k < AsynSrv_n_active; k++) { + if (AsynSrv_active[k] != NULL) { + if (AsynSrv_active[k]->skt == my_skt) { + AsynSrv_active[k]->skt = -1; /* Mark the force-close */ + AsynSrv_active[k] = NULL; /* Mark entry to be removed */ + AsynSrv_HPS_list[i].usage_cnt--; /* Decrease usage count */ + } + } + } + if (AsynSrv_HPS_list[i].usage_cnt != 0) { /* Use count should now be .. */ + AsynSrv_errcode = ASYNSRV__BAD_PAR; /* .. zero or there's a bug. */ + return False; + } + } + /*------------------------------------------------------ + ** Compress the list of AsynSrv_Open entries + */ + j = 0; + for (k = 0; k < AsynSrv_n_active; k++) { + if (AsynSrv_active[k] != NULL) { + AsynSrv_active[j] = AsynSrv_active[k]; + j++; + } + } + for (k = j; k < AsynSrv_n_active; k++) AsynSrv_active[k] = NULL; + AsynSrv_n_active = j; + /*------------------------------------------------------ + ** If the link is now idle, really close it and compress + ** the connection table entry out of the list. + */ + if (AsynSrv_HPS_list[i].usage_cnt == 0) { + send (my_skt, "-001", 4, 0); /* Tell the TCP/IP server that .. + ** .. we are about to quit. + */ + recv (my_skt, buff, sizeof (buff), 0); /* And wait for his ack */ + close (my_skt); + for (j = i; j < AsynSrv_n_cnct; j++) { + memcpy ((char *) &AsynSrv_HPS_list[j], (char *) &AsynSrv_HPS_list[j+1], + sizeof (AsynSrv_HPS_list[0])); + } + AsynSrv_HPS_list[AsynSrv_n_cnct].skt = 0; /* Invalidate the free entry */ + AsynSrv_n_cnct--; + } + if (AsynSrv_errcode == 0) AsynSrv_call_depth--; + return True; + } +/* +**--------------------------------------------------------------------------- +** AsynSrv_Config: Configure an open connection. +*/ + int AsynSrv_Config ( +/* ============== +*/ struct AsynSrv__info *asyn_info, + ...) { + + char buff[16], my_eot[4]; + va_list ap; /* Pointer to variable args */ + char *txt_ptr; + int intval; + typedef void (*IdleHandler)(int,int); + /* + ** Pre-set the routinename (in case of error) + */ + if (AsynSrv_errcode == 0 && AsynSrv_call_depth < 5) { + strcpy (AsynSrv_routine[AsynSrv_call_depth], "AsynSrv_Config"); + AsynSrv_call_depth++; + } + + va_start (ap, asyn_info); /* Set up var arg machinery */ + txt_ptr = va_arg (ap, char *); /* Get pntr to first parameter ident */ + while (txt_ptr != NULL) { + if (strcmp (txt_ptr, "msecTmo") == 0) { + intval = va_arg (ap, int); + if ((intval < 0) || (intval > 999999)) { + AsynSrv_errcode = ASYNSRV__BAD_PAR; + return False; + } + sprintf (buff, "%04d", intval/100); /* Convert to ASCII as .. + ** .. deci-secs */ + memcpy (asyn_info->tmo, buff, 4); + }else if (strcmp (txt_ptr, "eot") == 0) { + txt_ptr = va_arg (ap, char *); + if (txt_ptr == NULL) { + AsynSrv_errcode = ASYNSRV__BAD_PAR; + return False; + } + memcpy (my_eot, "\0\0\0\0", 4); + switch (txt_ptr[0]) { + case '3': my_eot[3] = txt_ptr[3]; + case '2': my_eot[2] = txt_ptr[2]; + case '1': my_eot[1] = txt_ptr[1]; + case '0': + my_eot[0] = txt_ptr[0]; + break; + default: + AsynSrv_errcode = ASYNSRV__BAD_PAR; + return False; + } + memcpy (asyn_info->eot, my_eot, 4); + }else if (strcmp (txt_ptr, "idleHdl") == 0) { /* MZ. */ + asyn_info->idleHandler = va_arg (ap, IdleHandler); + }else { + AsynSrv_errcode = ASYNSRV__BAD_PAR; + return False; + } + txt_ptr = va_arg (ap, char *); /* Get pntr to next parameter ident */ + } + + if (AsynSrv_errcode == 0) AsynSrv_call_depth--; + return True; + } +/* +**--------------------------------------------------------------------------- +** AsynSrv_ConfigDflt: Set default values in AsynSrv_Utility +** which will be used to initialise +** structures in AsynSrv_Open. +*/ + int AsynSrv_ConfigDflt ( +/* ================== +*/ char *par_id, + ...) { + int i; + char buff[4]; + va_list ap; /* Pointer to variable args */ + char *txt_ptr; + int intval; + /* + ** Pre-set the routinename (in case of error) + */ + if (AsynSrv_errcode == 0 && AsynSrv_call_depth < 5) { + strcpy (AsynSrv_routine[AsynSrv_call_depth], "AsynSrv_ConfigDflt"); + AsynSrv_call_depth++; + } + + va_start (ap, par_id); /* Set up var arg machinery */ + txt_ptr = par_id; /* Point to first arg */ + while (txt_ptr != NULL) { + if (strcmp (txt_ptr, "tmoC") == 0) { + intval = va_arg (ap, int); + if ((intval < 1) || (intval > 3600)) { + AsynSrv_errcode = ASYNSRV__BAD_PAR; + return False; + } + AsynSrv_connect_tmo = intval; + }else if (strcmp (txt_ptr, "msecTmo") == 0) { + intval = va_arg (ap, int); + if ((intval < 0) || (intval > 999999)) { + AsynSrv_errcode = ASYNSRV__BAD_PAR; + return False; + } + AsynSrv_msec_tmo = intval; + }else if (strcmp (txt_ptr, "eot") == 0) { + txt_ptr = va_arg (ap, char *); + if (txt_ptr == NULL) { + AsynSrv_errcode = ASYNSRV__BAD_PAR; + return False; + } + switch (txt_ptr[0]) { + case '3': AsynSrv_eot[3] = txt_ptr[3]; + case '2': AsynSrv_eot[2] = txt_ptr[2]; + case '1': AsynSrv_eot[1] = txt_ptr[1]; + case '0': + AsynSrv_eot[0] = txt_ptr[0]; + break; + default: + AsynSrv_errcode = ASYNSRV__BAD_PAR; + return False; + } + switch (txt_ptr[0]) { + case '0': AsynSrv_eot[1] = '\0'; + case '1': AsynSrv_eot[2] = '\0'; + case '2': AsynSrv_eot[3] = '\0'; + } + }else { + AsynSrv_errcode = ASYNSRV__BAD_PAR; + return False; + } + txt_ptr = va_arg (ap, char *); /* Get pntr to next parameter ident */ + } + + if (AsynSrv_errcode == 0) AsynSrv_call_depth--; + return True; + } +/* +**--------------------------------------------------------------------------- +** AsynSrv_ErrInfo: Return detailed status from last operation. +*/ + void AsynSrv_ErrInfo ( +/* =============== +*/ char **entry_txt, + int *errcode, + int *my_errno, + int *vaxc_errno) { + + int i, j, k; + char buff[80]; + + if (AsynSrv_call_depth <= 0) { + strcpy (AsynSrv_routine[0], "AsynSrv_no_error_detected"); + *errcode = 0; + *my_errno = 0; + *vaxc_errno = 0; + }else { + if (AsynSrv_call_depth > 1) { /* Concatenate the names */ + for (i = 1; i < AsynSrv_call_depth; i++) { + strcat (AsynSrv_routine[0], "/"); + StrJoin (AsynSrv_routine[0], sizeof (AsynSrv_routine), + AsynSrv_routine[0], AsynSrv_routine[i]); + } + } + *errcode = AsynSrv_errcode; + *my_errno = AsynSrv_errno; + *vaxc_errno = AsynSrv_vaxc_errno; + switch (AsynSrv_errcode) { + case ASYNSRV__BAD_BIND: strcpy (buff, "/ASYNSRV__BAD_BIND"); break; + case ASYNSRV__BAD_CMND_LEN: strcpy (buff, "/ASYNSRV__BAD_CMND_LEN"); break; + case ASYNSRV__BAD_CONNECT: strcpy (buff, "/ASYNSRV__BAD_CONNECT"); break; + case ASYNSRV__BAD_FLUSH: strcpy (buff, "/ASYNSRV__BAD_FLUSH"); break; + case ASYNSRV__BAD_HOST: strcpy (buff, "/ASYNSRV__BAD_HOST"); break; + case ASYNSRV__BAD_NOT_BCD: strcpy (buff, "/ASYNSRV__BAD_NOT_BCD"); break; + case ASYNSRV__BAD_PAR: strcpy (buff, "/ASYNSRV__BAD_PAR"); break; + case ASYNSRV__BAD_PROT_LVL: strcpy (buff, "/ASYNSRV__BAD_PROT_LVL"); break; + case ASYNSRV__BAD_RECV: strcpy (buff, "/ASYNSRV__BAD_RECV"); break; + case ASYNSRV__BAD_RECV_LEN: strcpy (buff, "/ASYNSRV__BAD_RECV_LEN"); break; + case ASYNSRV__BAD_RECV_NET: strcpy (buff, "/ASYNSRV__BAD_RECV_NET"); break; + case ASYNSRV__BAD_RECV_PIPE: strcpy (buff, "/ASYNSRV__BAD_RECV_PIPE"); break; + case ASYNSRV__BAD_RECV_UNKN: strcpy (buff, "/ASYNSRV__BAD_RECV_UNKN"); break; + case ASYNSRV__BAD_RECV1: strcpy (buff, "/ASYNSRV__BAD_RECV1"); break; + case ASYNSRV__BAD_RECV1_NET: strcpy (buff, "/ASYNSRV__BAD_RECV1_NET"); break; + case ASYNSRV__BAD_RECV1_PIPE:strcpy (buff, "/ASYNSRV__BAD_RECV1_PIPE"); break; + case ASYNSRV__BAD_REPLY: strcpy (buff, "/ASYNSRV__BAD_REPLY"); break; + case ASYNSRV__BAD_SEND: strcpy (buff, "/ASYNSRV__BAD_SEND"); break; + case ASYNSRV__BAD_SEND_LEN: strcpy (buff, "/ASYNSRV__BAD_SEND_LEN"); break; + case ASYNSRV__BAD_SEND_NET: strcpy (buff, "/ASYNSRV__BAD_SEND_NET"); break; + case ASYNSRV__BAD_SEND_PIPE: strcpy (buff, "/ASYNSRV__BAD_SEND_PIPE"); break; + case ASYNSRV__BAD_SEND_UNKN: strcpy (buff, "/ASYNSRV__BAD_SEND_UNKN"); break; + case ASYNSRV__BAD_SOCKET: strcpy (buff, "/ASYNSRV__BAD_SOCKET"); break; + case ASYNSRV__FORCED_CLOSED: strcpy (buff, "/ASYNSRV__FORCED_CLOSED"); break; + case ASYNSRV__NO_ROOM: strcpy (buff, "/ASYNSRV__NO_ROOM"); break; + default: sprintf (buff, "/ASYNSRV__unkn_err_code: %d", AsynSrv_errcode); + } + StrJoin (AsynSrv_routine[0], sizeof(AsynSrv_routine), + AsynSrv_routine[0], buff); + } + *entry_txt = AsynSrv_routine[0]; + AsynSrv_call_depth = 0; + AsynSrv_errcode = 0; + } +/* +**--------------------------------------------------------------------------- +** AsynSrv_Flush: Send a Flush command to RS232C server. +*/ + int AsynSrv_Flush ( +/* ============= +*/ struct AsynSrv__info *asyn_info) { + + int status; + char cmnd[8], rply[8]; + /*---------------------------------------------- + ** Pre-set the routine name (in case of error) + */ + if (AsynSrv_errcode == 0 && AsynSrv_call_depth < 5) { + strcpy (AsynSrv_routine[AsynSrv_call_depth], "AsynSrv_Flush"); + AsynSrv_call_depth++; + } + /*---------------------------------------------- + ** Do nothing if no connection - the connection gets + ** closed if an error is detected. The connection may + ** also be marked to have been forcefully closed. + */ + if (asyn_info->skt <= 0) { + if ((AsynSrv_errcode == 0) && (asyn_info->skt < 0)) { + AsynSrv_errcode = ASYNSRV__FORCED_CLOSED; + } + return False; + } + /*---------------------------------------------- + ** Send message and get reply. + */ + status = AsynSrv_SendSpecCmnd (asyn_info, "-004"); + + if (AsynSrv_errcode == 0) AsynSrv_call_depth--; + return status; + } +/* +**--------------------------------------------------------------------------- +** AsynSrv_GetLenTerm: Get length and terminator of given +** reply from reply buffer. +*/ + int AsynSrv_GetLenTerm ( +/* ================== +*/ struct AsynSrv__info *asyn_info, + struct RS__RespStruct *rcve_buff, + char *rply, /* In: Addr of a reply as got .. + ** .. got from _GetReply */ + int *len, /* Out: The returned length */ + char *term) { /* Out: The returned t'nator */ + + int i; + + i = sscanf ((rply - asyn_info->rply_hdr_len - 1), asyn_info->rply_fmt, len); + *len = (i == 1) ? (*len - 2) : 0; + *term = *(rply - 1); + + if (i != 1) return False; + return True; + } +/* +**--------------------------------------------------------------------------- +** AsynSrv_GetReply: Get next reply from a reply buffer. +*/ + char *AsynSrv_GetReply ( +/* ================ +*/ struct AsynSrv__info *asyn_info, + struct RS__RespStruct *rcve_buff, + char *last_rply) { + + char *pntr = NULL; + int i, rply_len; + + if (last_rply == NULL) { /* Start with first reply? */ + /* Yes */ + asyn_info->n_replies = 1; + if (asyn_info->max_replies > 0) { + pntr = rcve_buff->u.rplys; + pntr = pntr + 1 + asyn_info->rply_hdr_len; + } + }else { /* No - get next reply */ + if (asyn_info->n_replies < asyn_info->max_replies) { /* If there is one */ + i = sscanf ((last_rply - asyn_info->rply_hdr_len - 1), + asyn_info->rply_fmt, &rply_len); + if ((i == 1) && (rply_len >= 0)) { + pntr = last_rply + rply_len + asyn_info->rply_hdr_len; + } + } + } + return pntr; + } +/* +**--------------------------------------------------------------------------- +** AsynSrv_Open: Open a connection to an RS-232-C Server. +*/ + int AsynSrv_Open ( +/* ============ +*/ struct AsynSrv__info *asyn_info) { + + int i, status; + int my_skt; + char old_time_out[4]; + union { + char chars[4]; + int val; + } time_out; + char buff[128]; + struct RS__MsgStruct s_buff; + struct RS__RespStruct r_buff; + unsigned int oto_len, oto_status; + struct hostent *rmt_hostent; + struct in_addr *rmt_inet_addr_pntr; + struct in_addr rmt_inet_addr; + int rmt_sockname_len; + struct sockaddr_in lcl_sockname; + struct sockaddr_in rmt_sockname; + + char *errtxt_ptr; + int errcode, my_errno, my_vaxc_errno; +/*-------------------------------------------------------- +*/ + asyn_info->skt = 0; +/*-------------------------------------------------------- +** Initialise the error info stack and pre-set the +** routine name (in case of error). +*/ + AsynSrv_errcode = AsynSrv_errno = AsynSrv_vaxc_errno = 0; + strcpy (AsynSrv_routine[0], "AsynSrv_Open"); + AsynSrv_call_depth = 1; +/*-------------------------------------------------------- +** Is there room for a new AsynSrv_Open table entry? +*/ + if (AsynSrv_n_active >= MAX_OPEN) { + AsynSrv_errcode = ASYNSRV__NO_ROOM; /* There isn't! */ + return False; + } +/*-------------------------------------------------------- +** See if a table entry for this connection already exists. +*/ + for (i = 0; i < AsynSrv_n_cnct; i++) { + if (AsynSrv_HPS_list[i].status != 0) continue; + if (AsynSrv_HPS_list[i].port != asyn_info->port) continue; + if (strcmp (AsynSrv_HPS_list[i].host, asyn_info->host) == 0) break; + } + if (i < AsynSrv_n_cnct) { /* Did we find an entry? */ + /* Yes */ + AsynSrv_HPS_list[i].usage_cnt++; /* Up the usage count and .. */ + AsynSrv_active[AsynSrv_n_active] = /* .. remember the open and .. */ + asyn_info; + AsynSrv_n_active++; + asyn_info->skt = /* .. return the socket. */ + AsynSrv_HPS_list[i].skt; + if (asyn_info->chan < 0) asyn_info->chan = 0; + if (asyn_info->chan > 255) asyn_info->chan = 0; + sprintf (buff, "%04d", asyn_info->chan); /* Convert channel # to ASCII */ + memcpy (asyn_info->chan_char, buff, sizeof (asyn_info->chan_char)); + + asyn_info->protocol_code = AsynSrv_HPS_list[i].protocol_code; + memcpy (asyn_info->protocol_id, + AsynSrv_HPS_list[i].protocol_id, + sizeof (asyn_info->protocol_id)); + asyn_info->cmnd_hdr_len = AsynSrv_HPS_list[i].cmnd_hdr_len; + sprintf (asyn_info->cmnd_fmt, "%%0%dd", asyn_info->cmnd_hdr_len); + asyn_info->rply_hdr_len = AsynSrv_HPS_list[i].rply_hdr_len; + sprintf (asyn_info->rply_fmt, "%%%dd", asyn_info->rply_hdr_len); + + sprintf (buff, "%04d", AsynSrv_msec_tmo/100); /* Set dflt time-out .. + ** ..(deci-secs) */ + memcpy (asyn_info->tmo, buff, sizeof (asyn_info->tmo)); + + memcpy (asyn_info->eot, /* Set dflt terminator(s) */ + AsynSrv_eot, sizeof (asyn_info->eot)); + + asyn_info->max_replies = asyn_info->n_replies = 0; + asyn_info->idleHandler = NULL; + AsynSrv_call_depth--; + return True; + } +/*-------------------------------------------------------- +** There is no existing connection. Open a new one. +*/ + status = AsynSrv_OpenNew (asyn_info); + if (!status) return False; +/*-------------------------------------------------------- +** Allow the entry to be shared (i.e. status = 0) +*/ + AsynSrv_HPS_list[AsynSrv_n_cnct-1].status = 0; +/*-------------------------------------------------------- +*/ + AsynSrv_errcode = AsynSrv_errno = AsynSrv_vaxc_errno = 0; + AsynSrv_call_depth = 0; + return True; + } +/* +**--------------------------------------------------------------------------- +** AsynSrv_OpenNew: Open a new connection to an RS-232-C Server. +*/ + int AsynSrv_OpenNew ( +/* =============== +*/ struct AsynSrv__info *asyn_info) { + + int i, status; + int my_skt; + char old_time_out[4]; + union { + char chars[4]; + int val; + } time_out; + char buff[128]; + struct RS__MsgStruct s_buff; + struct RS__RespStruct r_buff; + unsigned int oto_len, oto_status; + struct hostent *rmt_hostent; + struct in_addr *rmt_inet_addr_pntr; + struct in_addr rmt_inet_addr; + int rmt_sockname_len; + struct sockaddr_in lcl_sockname; + struct sockaddr_in rmt_sockname; + + char *errtxt_ptr; + int errcode, my_errno, my_vaxc_errno; +/*-------------------------------------------------------- +*/ + asyn_info->skt = 0; +/*-------------------------------------------------------- +** Initialise the error info stack and pre-set the +** routine name (in case of error). +*/ + AsynSrv_errcode = AsynSrv_errno = AsynSrv_vaxc_errno = 0; + if ((AsynSrv_call_depth == 1) && + (strcmp (AsynSrv_routine[0], "AsynSrv_Open") == 0)) { + strcpy (AsynSrv_routine[1], "AsynSrv_OpenNew"); + AsynSrv_call_depth = 2; + }else { + strcpy (AsynSrv_routine[0], "AsynSrv_OpenNew"); + AsynSrv_call_depth = 1; + } +/*-------------------------------------------------------- +** Is there room for a new AsynSrv_Open table entry? +*/ + if (AsynSrv_n_active >= MAX_OPEN) { + AsynSrv_errcode = ASYNSRV__NO_ROOM; /* There isn't! */ + return False; + } +/*-------------------------------------------------------- +** Is there room for a new connection entry? +*/ + if (AsynSrv_n_cnct >= AsynSrv_MAX_LINK) { + AsynSrv_errcode = ASYNSRV__NO_ROOM; /* There isn't! */ + return False; + } +/*-------------------------------------------------------- +** There's room for a new connection but, before going any +** further, do some quick checks on values in asyn_info. +*/ + if ((asyn_info->port <= 0) || + (asyn_info->port > 65535) || + (asyn_info->chan < 0) || + (asyn_info->chan > 255)) { + AsynSrv_errcode = ASYNSRV__BAD_PAR; /* Something is bad! */ + return False; + } +/*-------------------------------------------------------- +** Set up a new connection. +*/ + StrJoin (AsynSrv_HPS_list[AsynSrv_n_cnct].host, + sizeof (AsynSrv_HPS_list[AsynSrv_n_cnct].host), + asyn_info->host, ""); + AsynSrv_HPS_list[AsynSrv_n_cnct].port = asyn_info->port; + /*--------------------------- + ** Get the Internet address of the server. + */ + rmt_inet_addr.s_addr = inet_addr (asyn_info->host); + if (rmt_inet_addr.s_addr != -1) { + rmt_inet_addr_pntr = &rmt_inet_addr; + }else { + rmt_hostent = gethostbyname (asyn_info->host); + if (rmt_hostent == NULL) { + AsynSrv_errcode = ASYNSRV__BAD_HOST; + GetErrno (&AsynSrv_errno, &AsynSrv_vaxc_errno); /* Save errno info */ + fprintf (stderr, "\nAsynSrv_OpenNew/gethostbyname: Failed to get Internet " + "address of \"%s\".\n", asyn_info->host); + return False; + } + rmt_inet_addr_pntr = (struct in_addr *) rmt_hostent->h_addr_list[0]; + } + /*--------------------------- + ** Create a TCP/IP socket for connecting to server and bind it. + */ + my_skt = socket (AF_INET, SOCK_STREAM, 0); + if (my_skt <= 0) { + AsynSrv_errcode = ASYNSRV__BAD_SOCKET; + GetErrno (&AsynSrv_errno, &AsynSrv_vaxc_errno); /* Save the errno info */ + fprintf (stderr, "\nAsynSrv_OpenNew/socket: Failed to create a socket.\n"); + return False; + } + lcl_sockname.sin_family = AF_INET; + lcl_sockname.sin_port = htons (0); + lcl_sockname.sin_addr.s_addr = 0; + status = bind (my_skt, (struct sockaddr *) &lcl_sockname, + sizeof (lcl_sockname)); + if (status == -1) { + close (my_skt); + AsynSrv_errcode = ASYNSRV__BAD_BIND; + GetErrno (&AsynSrv_errno, &AsynSrv_vaxc_errno); /* Save the errno info */ + fprintf (stderr, "\nAsynSrv_OpenNew/bind: Failed to bind socket.\n"); + return False; + } + /*--------------------------- + ** Set short time-out (VMS systems only) + */ +#ifdef __VMS + oto_len = sizeof (old_time_out); /* Save current time-out first */ + oto_status = getsockopt (my_skt, IPPROTO_TCP, UCX$C_TCP_PROBE_IDLE, + old_time_out, &oto_len); + + if (oto_status == 0) { + time_out.val = AsynSrv_connect_tmo; /* Set new time-out */ + status = setsockopt (my_skt, IPPROTO_TCP, UCX$C_TCP_PROBE_IDLE, + time_out.chars, sizeof (time_out)); + } +#endif + /*--------------------------- + ** Connect to RS-232-C Server. + */ + rmt_sockname_len = sizeof (rmt_sockname); + rmt_sockname.sin_family = AF_INET; + rmt_sockname.sin_port = htons (asyn_info->port); + rmt_sockname.sin_addr.s_addr = rmt_inet_addr_pntr->s_addr; + status = connect (my_skt, (struct sockaddr *) &rmt_sockname, + sizeof (rmt_sockname)); + if (status != 0) { + close (my_skt); + AsynSrv_errcode = ASYNSRV__BAD_CONNECT; + GetErrno (&AsynSrv_errno, &AsynSrv_vaxc_errno); /* Save the errno info */ + fprintf (stderr, + "\nAsynSrv_OpenNew/connect: Failed to connect to server.\n"); + perror ("AsynSrv_OpenNew"); + return False; + } + /*--------------------------- + ** Restore time-out (VMS only) + */ +#ifdef __VMS + if (oto_status == 0) { + setsockopt (my_skt, IPPROTO_TCP, UCX$C_TCP_PROBE_IDLE, + old_time_out, oto_len); + } +#endif + /*--------------------------------------------------- + ** Setup the defaults in the AsynSrv__info data structure. + */ + asyn_info->skt = my_skt; /* Return socket number to caller */ + + asyn_info->protocol_code = 0; /* Ensure protocol_code set to "unknown" */ + memcpy (asyn_info->protocol_id, "\0\0\0\0", + sizeof (asyn_info->protocol_id)); + asyn_info->cmnd_hdr_len = 4; + strcpy (asyn_info->cmnd_fmt, "%04d"); + asyn_info->rply_hdr_len = 4; + strcpy (asyn_info->rply_fmt, "%4d"); + + sprintf (buff, "%04d", asyn_info->chan); /* Convert channel # to ASCII */ + memcpy (asyn_info->chan_char, buff, sizeof (asyn_info->chan_char)); + + sprintf (buff, "%04d", AsynSrv_msec_tmo/100); /* Set dflt time-out .. + ** .. (deci-secs) */ + memcpy (asyn_info->tmo, buff, sizeof (asyn_info->tmo)); + + memcpy (asyn_info->eot, AsynSrv_eot, sizeof (asyn_info->eot)); /* Set .. + ** .. dflt terminator(s) */ + asyn_info->max_replies = 0; + asyn_info->n_replies = 0; + asyn_info->idleHandler = NULL; + /* + ** Send a null command buffer to the server. This should give + ** a "protocol mismatch" error response and from this we can get + ** the actual protocol level supported by the server. + */ + status = AsynSrv_SendCmnds (asyn_info, &s_buff, &r_buff, NULL); + if (!status && (AsynSrv_errcode == ASYNSRV__BAD_PROT_LVL)) { + /* + ** As expected, we got a "protocol mismatch" error. + ** Save the server's protocol level for future use. + */ + memcpy (asyn_info->protocol_id, r_buff.s_pcol_lvl, + sizeof (r_buff.s_pcol_lvl)); + if (strncmp (r_buff.s_pcol_lvl, RS__PROTOCOL_ID_V01B, + strlen (RS__PROTOCOL_ID_V01B)) == 0) { + asyn_info->protocol_code = RS__PROTOCOL_CODE_V01B; + asyn_info->cmnd_hdr_len = 4; + strcpy (asyn_info->cmnd_fmt, "%04d"); + asyn_info->rply_hdr_len = 4; + strcpy (asyn_info->rply_fmt, "%4d"); + }else if (strncmp (r_buff.s_pcol_lvl, RS__PROTOCOL_ID, + strlen (RS__PROTOCOL_ID)) == 0) { + asyn_info->protocol_code = RS__PROTOCOL_CODE; + asyn_info->cmnd_hdr_len = 2; + strcpy (asyn_info->cmnd_fmt, "%02d"); + asyn_info->rply_hdr_len = 2; + strcpy (asyn_info->rply_fmt, "%2d"); + }else { + close (my_skt); + asyn_info->skt = 0; + fprintf (stderr, + "\nAsynSrv_OpenNew: Server protocol level is unrecognised.\n" + " Server level is \"%4s\"\n", r_buff.s_pcol_lvl); + return False; + } + }else { + close (my_skt); + asyn_info->skt = 0; + AsynSrv_errcode = ASYNSRV__BAD_PROT_LVL; + fprintf (stderr, + "\nAsynSrv_OpenNew: Problem getting protocol level of Server!\n"); + return False; + } + /*--------------------------------------------------- + ** Complete the setup of the connection table entry + */ + AsynSrv_HPS_list[AsynSrv_n_cnct].skt = my_skt; + AsynSrv_HPS_list[AsynSrv_n_cnct].protocol_code = asyn_info->protocol_code; + memcpy (AsynSrv_HPS_list[AsynSrv_n_cnct].protocol_id, + asyn_info->protocol_id, sizeof (asyn_info->protocol_id)); + AsynSrv_HPS_list[AsynSrv_n_cnct].cmnd_hdr_len = asyn_info->cmnd_hdr_len; + AsynSrv_HPS_list[AsynSrv_n_cnct].rply_hdr_len = asyn_info->rply_hdr_len; + AsynSrv_HPS_list[AsynSrv_n_cnct].usage_cnt = 1; + AsynSrv_HPS_list[AsynSrv_n_cnct].status = 1; + AsynSrv_n_cnct++; + + AsynSrv_active[AsynSrv_n_active] = /* Remember the open in case .. */ + asyn_info; /* .. there's a force-exit */ + AsynSrv_n_active++; + + AsynSrv_errcode = AsynSrv_errno = AsynSrv_vaxc_errno = 0; + AsynSrv_call_depth--; + return True; + } +/* +**--------------------------------------------------------------------------- +** AsynSrv_SendCmnds: Send commands to RS232C server. +*/ + int AsynSrv_SendCmnds ( +/* ================= +*/ struct AsynSrv__info *asyn_info, + struct RS__MsgStruct *send_buff, + struct RS__RespStruct *rcve_buff, + ...) { /* Now we have list of commands - + ** char *txt = pntr to cmnd strng + ** Terminate list with *txt = NULL. + */ + int i, status, c_len, size, max_size, ncmnds; + int bytes_to_come, bytes_left; + char *nxt_byte_ptr; + char err_text[80]; + char text[20]; + va_list ap; /* Pointer to variable args */ + char *txt_ptr; + char *cmnd_lst_ptr; + /*---------------------------------------------- + ** Pre-set the routine name (in case of error) + */ + if (AsynSrv_errcode == 0 && AsynSrv_call_depth < 5) { + strcpy (AsynSrv_routine[AsynSrv_call_depth], "AsynSrv_SendCmnds"); + AsynSrv_call_depth++; + } + /*---------------------------------------------- + ** Do nothing if no connection - the connection gets + ** closed if an error is detected. The connection may + ** also be marked to have been forcefully closed. + */ + if (asyn_info->skt <= 0) { + memset (rcve_buff->msg_size, + '0', sizeof (rcve_buff->msg_size)); + if ((AsynSrv_errcode == 0) && (asyn_info->skt < 0)) { + AsynSrv_errcode = ASYNSRV__FORCED_CLOSED; + } + return False; + } + /*---------------------------------------------- + ** Build message for server from the list of commands. + */ + asyn_info->max_replies = asyn_info->n_replies = 0; + + asyn_info->msg_id++; /* Set up an incrementing message id */ + if (asyn_info->msg_id > 9999) asyn_info->msg_id = 1; + sprintf (send_buff->msg_id, "%04d", asyn_info->msg_id); + + memcpy (send_buff->c_pcol_lvl, asyn_info->protocol_id, + sizeof (send_buff->c_pcol_lvl)); + + memcpy (send_buff->serial_port, asyn_info->chan_char, + sizeof (send_buff->serial_port)); + + memcpy (send_buff->tmo, asyn_info->tmo, sizeof (send_buff->tmo)); + + memcpy (send_buff->terms, asyn_info->eot, sizeof (send_buff->terms)); + + memcpy (send_buff->n_cmnds, "0000", sizeof (send_buff->n_cmnds)); + + va_start (ap, rcve_buff); /* Set up var arg machinery */ + + txt_ptr = va_arg (ap, char *); /* Get pntr to next cmnd string */ + ncmnds = 0; + cmnd_lst_ptr = &send_buff->cmnds[0]; + bytes_left = sizeof (*send_buff) - + OffsetOf (struct RS__MsgStruct, cmnds[0]); + + while (txt_ptr != NULL) { + c_len = strlen (txt_ptr); + size = asyn_info->cmnd_hdr_len + c_len; + if (size > bytes_left) { + AsynSrv_errcode = ASYNSRV__BAD_SEND_LEN; /* Too much to send */ + fprintf (stderr, "\nAsynSrv_SendCmnds/send: too much to send" + " - request ignored.\n"); + memset (rcve_buff->msg_size, '0', sizeof (rcve_buff->msg_size)); + return False; + }else { + sprintf (cmnd_lst_ptr, asyn_info->cmnd_fmt, c_len); + if (cmnd_lst_ptr[asyn_info->cmnd_hdr_len] != '\0') { + AsynSrv_errcode = ASYNSRV__BAD_CMND_LEN; + fprintf (stderr, "\nAsynSrv_SendCmnds/send: command too long -" + " - request ignored.\n"); + memset (rcve_buff->msg_size, '0', sizeof (rcve_buff->msg_size)); + return False; + } + cmnd_lst_ptr += asyn_info->cmnd_hdr_len; + strcpy (cmnd_lst_ptr, txt_ptr); + cmnd_lst_ptr += c_len; + ncmnds++; + bytes_left = bytes_left - size; + txt_ptr = va_arg (ap, char *); + } + } + sprintf (text, "%04d", ncmnds); + memcpy (send_buff->n_cmnds, text, sizeof (send_buff->n_cmnds)); + + size = cmnd_lst_ptr - send_buff->msg_id; + size = (size + 3) & (~3); /* Round up to multiple of 4 */ + sprintf (text, "%04d", size); + memcpy (send_buff->msg_size, text, sizeof (send_buff->msg_size)); + + size += sizeof (send_buff->msg_size); + status = send (asyn_info->skt, + (char *) send_buff, size, 0); + if (status != size) { + GetErrno (&AsynSrv_errno, &AsynSrv_vaxc_errno); + if (status == 0) { + AsynSrv_errcode = ASYNSRV__BAD_SEND; /* Server exited (probably) */ + fprintf (stderr, "\nAsynSrv_SendCmnds/send: probable network problem"); + }else if (status == -1) { + if (AsynSrv_errno == EPIPE) { + AsynSrv_errcode = ASYNSRV__BAD_SEND_PIPE; /* Server exited (probably) */ + fprintf (stderr, "\nAsynSrv_SendCmnds/send: broken network pipe"); + }else { + AsynSrv_errcode = ASYNSRV__BAD_SEND_NET; /* It's some other net problem */ + perror ("AsynSrv_SendCmnds/send"); + } + }else { + AsynSrv_errcode = ASYNSRV__BAD_SEND_UNKN; /* TCP/IP problems */ + fprintf (stderr, "\nAsynSrv_SendCmnds/send: probable TCP/IP problem"); + } + AsynSrv_Close (asyn_info, True); /* Force close TCP/IP connection */ + fprintf (stderr, " - link to server force-closed.\n"); + return False; + } + + if (asyn_info->idleHandler != NULL) { /* MZ. */ + sscanf(asyn_info->tmo, "%4d", &i); /* Decode timeout from ASCII .. + ** .. encoded deci-sec */ + asyn_info->idleHandler (i*150, asyn_info->skt); /* Wait for an event .. + ** .. on asyn_info->skt or a .. + ** .. timeout of 1.5*tmo */ + } + + size = sizeof (rcve_buff->msg_size); + status = recv (asyn_info->skt, rcve_buff->msg_size, size, 0); + if (status != size) { + GetErrno (&AsynSrv_errno, &AsynSrv_vaxc_errno); + if (status == 0) { + AsynSrv_errcode = ASYNSRV__BAD_RECV; /* Server exited (probably) */ + fprintf (stderr, "\nAsynSrv_SendCmnds/recv: probable network problem"); + }else if (status == -1) { + if (AsynSrv_errno == EPIPE) { + AsynSrv_errcode = ASYNSRV__BAD_RECV_PIPE; /* Server exited (probably) */ + fprintf (stderr, "\nAsynSrv_SendCmnds/recv: broken network pipe"); + }else { + AsynSrv_errcode = ASYNSRV__BAD_RECV_NET; /* It's some other net problem */ + perror ("AsynSrv_SendCmnds/recv"); + } + }else { + AsynSrv_errcode = ASYNSRV__BAD_RECV_UNKN; /* TCP/IP problems */ + fprintf (stderr, "\nAsynSrv_SendCmnds/recv: probable TCP/IP problem"); + } + AsynSrv_Close (asyn_info, True); /* Force close TCP/IP connection */ + fprintf (stderr, " - link to server force-closed.\n"); + return False; + } + if (sscanf (rcve_buff->msg_size, "%4d", &bytes_to_come) != 1) { + AsynSrv_errcode = ASYNSRV__BAD_NOT_BCD; /* Header not an ASCII BCD integer */ + AsynSrv_Close (asyn_info, True); /* Force close TCP/IP connection */ + fprintf (stderr, "\nAsynSrv_SendCmnds/recv: non-BCD byte count" + " - link to server force-closed.\n"); + return False; + } + max_size = sizeof (*rcve_buff) - size; + if (bytes_to_come > max_size) { + AsynSrv_errcode = ASYNSRV__BAD_RECV_LEN; + fprintf (stderr, "\nAsynSrv_SendCmnds/recv: pending message length too big" + " - flushing ...\n"); + nxt_byte_ptr = &rcve_buff->msg_size[size]; + while (bytes_to_come > 0) { /* Flush out the incoming message */ + bytes_left = bytes_to_come; + if (bytes_left > max_size) bytes_left = max_size; + status = recv (asyn_info->skt, nxt_byte_ptr, bytes_left, 0); + if (status <= 0) { + AsynSrv_errcode = ASYNSRV__BAD_FLUSH; /* TCP/IP problem during flush */ + GetErrno (&AsynSrv_errno, &AsynSrv_vaxc_errno); + AsynSrv_Close (asyn_info, True); /* Force close TCP/IP connection */ + fprintf (stderr, "\nAsynSrv_SendCmnds/recv: network problem during" + " flush.\nLink to server force-closed.\n"); + return False; + } + bytes_to_come = bytes_to_come - status; + } + fprintf (stderr, "\n flushed OK.\n"); + memset (rcve_buff->msg_size, '0', sizeof (rcve_buff->msg_size)); + return False; + }else { + nxt_byte_ptr = &rcve_buff->msg_size[size]; + bytes_left = bytes_to_come; + while (bytes_left > 0) { /* Read the rest of the response */ + status = recv (asyn_info->skt, nxt_byte_ptr, bytes_left, 0); + if (status <= 0) { + GetErrno (&AsynSrv_errno, &AsynSrv_vaxc_errno); + if (status == 0) { + AsynSrv_errcode = ASYNSRV__BAD_RECV1; /* Server exited (probably) */ + fprintf (stderr, "\nAsynSrv_SendCmnds/recv/1: probable network " + "problem"); + }else { + if (AsynSrv_errno == EPIPE) { + AsynSrv_errcode = ASYNSRV__BAD_RECV1_PIPE; /* Server exited (probably) */ + fprintf (stderr, "\nAsynSrv_SendCmnds/recv/1: broken network pipe"); + }else { + AsynSrv_errcode = ASYNSRV__BAD_RECV1_NET; /* It's some other net fault */ + perror ("AsynSrv_SendCmnds/recv/1"); + } + } + AsynSrv_Close (asyn_info, True); /* Force close TCP/IP connection */ + fprintf (stderr, " - link to server force-closed.\n"); + return False; + } + bytes_left = bytes_left - status; + nxt_byte_ptr = nxt_byte_ptr + status; + } + if (strncmp (asyn_info->protocol_id, rcve_buff->s_pcol_lvl, + sizeof (rcve_buff->s_pcol_lvl)) != 0) { + AsynSrv_errcode = ASYNSRV__BAD_PROT_LVL; /* Protocol level is bad */ + return False; + } + if ((sscanf (rcve_buff->n_rply, "%4d", &asyn_info->max_replies) != 1) || + (asyn_info->max_replies < 0)) { + AsynSrv_errcode = ASYNSRV__BAD_REPLY; /* Reply is bad */ + if (AsynSrv_call_depth < 5) { /* Add reply to routine stack */ + bytes_to_come = bytes_to_come + 4; + if (bytes_to_come >= sizeof (AsynSrv_routine[0])) + bytes_to_come = sizeof (AsynSrv_routine[0]) - 1; + for (i=0; imsg_size[i] == '\0') + rcve_buff->msg_size[i] = '.'; + } + rcve_buff->msg_size[bytes_to_come] = '\0'; + strcpy (AsynSrv_routine[AsynSrv_call_depth], rcve_buff->msg_size); + AsynSrv_call_depth++; + } + return False; + } + } + if (AsynSrv_errcode == 0) AsynSrv_call_depth--; + return True; + } +/* +**--------------------------------------------------------------------------- +** AsynSrv_SendCmndsBig: Same as AsynSrv_SendCmnds but with +** user defined buffer sizes. +*/ + int AsynSrv_SendCmndsBig ( +/* ==================== +*/ struct AsynSrv__info *asyn_info, + struct RS__MsgStruct *send_buff, + int send_buff_size, + struct RS__RespStruct *rcve_buff, + int rcve_buff_size, + ...) { /* Now we have list of commands - + ** char *txt = pntr to cmnd strng + ** Terminate list with *txt = NULL. + */ + int i, status, size, max_size, ncmnds; + int bytes_to_come, bytes_left; + char *nxt_byte_ptr; + char err_text[80]; + char text[20]; + va_list ap; /* Pointer to variable args */ + int *c_len, s_len; + char *txt_ptr; + char *cmnd_lst_ptr; + /*---------------------------------------------- + ** Pre-set the routine name (in case of error) + */ + if (AsynSrv_errcode == 0 && AsynSrv_call_depth < 5) { + strcpy (AsynSrv_routine[AsynSrv_call_depth], "AsynSrv_SendCmndsBig"); + AsynSrv_call_depth++; + } + /*---------------------------------------------- + ** Do nothing if no connection - the connection gets + ** closed if an error is detected. The connection may + ** also be marked to have been forcefully closed. + */ + if (asyn_info->skt <= 0) { + memset (rcve_buff->msg_size, + '0', sizeof (rcve_buff->msg_size)); + if ((AsynSrv_errcode == 0) && (asyn_info->skt < 0)) { + AsynSrv_errcode = ASYNSRV__FORCED_CLOSED; + } + return False; + } + /*---------------------------------------------- + ** Build message for server from the list of commands. + */ + if (send_buff_size < 64 || rcve_buff_size < 64) { + AsynSrv_errcode = ASYNSRV__BAD_PAR; return False;} + + asyn_info->max_replies = asyn_info->n_replies = 0; + + asyn_info->msg_id++; /* Set up an incrementing message id */ + if (asyn_info->msg_id > 9999) asyn_info->msg_id = 1; + sprintf (send_buff->msg_id, "%04d", asyn_info->msg_id); + + memcpy (send_buff->c_pcol_lvl, asyn_info->protocol_id, + sizeof (send_buff->c_pcol_lvl)); + + memcpy (send_buff->serial_port, asyn_info->chan_char, + sizeof (send_buff->serial_port)); + + memcpy (send_buff->tmo, asyn_info->tmo, sizeof (send_buff->tmo)); + + memcpy (send_buff->terms, asyn_info->eot, sizeof (send_buff->terms)); + + memcpy (send_buff->n_cmnds, "0000", sizeof (send_buff->n_cmnds)); + + va_start (ap, rcve_buff_size); /* Set up var arg machinery */ + + c_len = va_arg (ap, int *); /* Get pntr to length of next cmnd string */ + ncmnds = 0; + cmnd_lst_ptr = &send_buff->cmnds[0]; + bytes_left = send_buff_size - + OffsetOf (struct RS__MsgStruct, cmnds[0]); + + while (c_len != NULL) { + txt_ptr = va_arg (ap, char *); + s_len = *c_len; + if (s_len <= 0) s_len = strlen (txt_ptr); + size = asyn_info->cmnd_hdr_len + s_len; + if (size > bytes_left) { + AsynSrv_errcode = ASYNSRV__BAD_SEND_LEN; /* Too much to send */ + fprintf (stderr, "\nAsynSrv_SendCmndsBig/send: too much to send" + " - request ignored.\n"); + memset (rcve_buff->msg_size, '0', sizeof (rcve_buff->msg_size)); + return False; + } + sprintf (cmnd_lst_ptr, asyn_info->cmnd_fmt, s_len); + if (cmnd_lst_ptr[asyn_info->cmnd_hdr_len] != '\0') { + AsynSrv_errcode = ASYNSRV__BAD_CMND_LEN; + fprintf (stderr, "\nAsynSrv_SendCmndsBig/send: command too long -" + " - request ignored.\n"); + memset (rcve_buff->msg_size, '0', sizeof (rcve_buff->msg_size)); + return False; + } + cmnd_lst_ptr += asyn_info->cmnd_hdr_len; + memcpy (cmnd_lst_ptr, txt_ptr, s_len); + cmnd_lst_ptr += s_len; + ncmnds++; + bytes_left = bytes_left - size; + c_len = va_arg (ap, int *); + } + sprintf (text, "%04d", ncmnds); + memcpy (send_buff->n_cmnds, text, sizeof (send_buff->n_cmnds)); + + size = cmnd_lst_ptr - send_buff->msg_id; + size = (size + 3) & (~3); /* Round up to multiple of 4 */ + sprintf (text, "%04d", size); + memcpy (send_buff->msg_size, text, sizeof (send_buff->msg_size)); + + size += sizeof (send_buff->msg_size); + status = send (asyn_info->skt, + (char *) send_buff, size, 0); + if (status != size) { + GetErrno (&AsynSrv_errno, &AsynSrv_vaxc_errno); + if (status == 0) { + AsynSrv_errcode = ASYNSRV__BAD_SEND; /* Server exited (probably) */ + fprintf (stderr, "\nAsynSrv_SendCmndsBig/send: probable network problem"); + }else if (status == -1) { + if (AsynSrv_errno == EPIPE) { + AsynSrv_errcode = ASYNSRV__BAD_SEND_PIPE; /* Server exited (probably) */ + fprintf (stderr, "\nAsynSrv_SendCmndsBig/send: broken network pipe"); + }else { + AsynSrv_errcode = ASYNSRV__BAD_SEND_NET; /* It's some other net problem */ + perror ("AsynSrv_SendCmndsBig/send"); + } + }else { + AsynSrv_errcode = ASYNSRV__BAD_SEND_UNKN; /* TCP/IP problems */ + fprintf (stderr, "\nAsynSrv_SendCmndsBig/send: probable TCP/IP problem"); + } + AsynSrv_Close (asyn_info, True); /* Force close TCP/IP connection */ + fprintf (stderr, " - link to server force-closed.\n"); + return False; + } + + size = sizeof (rcve_buff->msg_size); + status = recv (asyn_info->skt, rcve_buff->msg_size, size, 0); + if (status != size) { + GetErrno (&AsynSrv_errno, &AsynSrv_vaxc_errno); + if (status == 0) { + AsynSrv_errcode = ASYNSRV__BAD_RECV; /* Server exited (probably) */ + fprintf (stderr, "\nAsynSrv_SendCmndsBig/recv: probable network problem"); + }else if (status == -1) { + if (AsynSrv_errno == EPIPE) { + AsynSrv_errcode = ASYNSRV__BAD_RECV_PIPE; /* Server exited (probably) */ + fprintf (stderr, "\nAsynSrv_SendCmndsBig/recv: broken network pipe"); + }else { + AsynSrv_errcode = ASYNSRV__BAD_RECV_NET; /* It's some other net problem */ + perror ("AsynSrv_SendCmndsBig/recv"); + } + }else { + AsynSrv_errcode = ASYNSRV__BAD_RECV_UNKN; /* TCP/IP problems */ + fprintf (stderr, "\nAsynSrv_SendCmndsBig/recv: probable TCP/IP problem"); + } + AsynSrv_Close (asyn_info, True); /* Force close TCP/IP connection */ + fprintf (stderr, " - link to server force-closed.\n"); + return False; + } + if (sscanf (rcve_buff->msg_size, "%4d", &bytes_to_come) != 1) { + AsynSrv_errcode = ASYNSRV__BAD_NOT_BCD; /* Header not an ASCII BCD integer */ + AsynSrv_Close (asyn_info, True); /* Force close TCP/IP connection */ + fprintf (stderr, "\nAsynSrv_SendCmndsBig/recv: non-BCD byte count" + " - link to server force-closed.\n"); + return False; + } + max_size = rcve_buff_size - size; + if (bytes_to_come > max_size) { + AsynSrv_errcode = ASYNSRV__BAD_RECV_LEN; + fprintf (stderr, "\nAsynSrv_SendCmndsBig/recv: pending message length " + "too big - flushing ...\n"); + nxt_byte_ptr = &rcve_buff->msg_size[size]; + while (bytes_to_come > 0) { /* Flush out the incoming message */ + bytes_left = bytes_to_come; + if (bytes_left > max_size) bytes_left = max_size; + status = recv (asyn_info->skt, nxt_byte_ptr, bytes_left, 0); + if (status <= 0) { + AsynSrv_errcode = ASYNSRV__BAD_FLUSH; /* TCP/IP problem during flush */ + GetErrno (&AsynSrv_errno, &AsynSrv_vaxc_errno); + AsynSrv_Close (asyn_info, True); /* Force close TCP/IP connection */ + fprintf (stderr, "\nAsynSrv_SendCmndsBig/recv: network problem during" + " flush.\nLink to server force-closed.\n"); + return False; + } + bytes_to_come = bytes_to_come - status; + } + fprintf (stderr, "\n flushed OK.\n"); + memset (rcve_buff->msg_size, '0', sizeof (rcve_buff->msg_size)); + return False; + }else { + nxt_byte_ptr = &rcve_buff->msg_size[size]; + bytes_left = bytes_to_come; + while (bytes_left > 0) { /* Read the rest of the response */ + status = recv (asyn_info->skt, nxt_byte_ptr, bytes_left, 0); + if (status <= 0) { + GetErrno (&AsynSrv_errno, &AsynSrv_vaxc_errno); + if (status == 0) { + AsynSrv_errcode = ASYNSRV__BAD_RECV1; /* Server exited (probably) */ + fprintf (stderr, "\nAsynSrv_SendCmndsBig/recv/1: probable network " + "problem"); + }else { + if (AsynSrv_errno == EPIPE) { + AsynSrv_errcode = ASYNSRV__BAD_RECV1_PIPE; /* Server exited (probably) */ + fprintf (stderr, "\nAsynSrv_SendCmndsBig/recv/1: broken network pipe"); + }else { + AsynSrv_errcode = ASYNSRV__BAD_RECV1_NET; /* It's some other net fault */ + perror ("AsynSrv_SendCmndsBig/recv/1"); + } + } + AsynSrv_Close (asyn_info, True); /* Force close TCP/IP connection */ + fprintf (stderr, " - link to server force-closed.\n"); + return False; + } + bytes_left = bytes_left - status; + nxt_byte_ptr = nxt_byte_ptr + status; + } + if (strncmp (asyn_info->protocol_id, rcve_buff->s_pcol_lvl, + sizeof (rcve_buff->s_pcol_lvl)) != 0) { + AsynSrv_errcode = ASYNSRV__BAD_PROT_LVL; /* Protocol level is bad */ + return False; + } + if ((sscanf (rcve_buff->n_rply, "%4d", &asyn_info->max_replies) != 1) || + (asyn_info->max_replies < 0)) { + AsynSrv_errcode = ASYNSRV__BAD_REPLY; /* Reply is bad */ + if (AsynSrv_call_depth < 5) { /* Add reply to routine stack */ + bytes_to_come = bytes_to_come + 4; + if (bytes_to_come >= sizeof (AsynSrv_routine[0])) + bytes_to_come = sizeof (AsynSrv_routine[0]) - 1; + for (i=0; imsg_size[i] == '\0') + rcve_buff->msg_size[i] = '.'; + } + rcve_buff->msg_size[bytes_to_come] = '\0'; + strcpy (AsynSrv_routine[AsynSrv_call_depth], rcve_buff->msg_size); + AsynSrv_call_depth++; + } + return False; + } + } + if (AsynSrv_errcode == 0) AsynSrv_call_depth--; + return True; + } +/* +**--------------------------------------------------------------------------- +** AsynSrv_SendSpecCmnd: Send a "special" command to an +** RS232C server. +*/ + int AsynSrv_SendSpecCmnd ( +/* ==================== +*/ struct AsynSrv__info *asyn_info, + char *cmnd) { + + int status; + char rply[8]; + /*---------------------------------------------- + ** Pre-set the routine name (in case of error) + */ + if (AsynSrv_errcode == 0 && AsynSrv_call_depth < 5) { + strcpy (AsynSrv_routine[AsynSrv_call_depth], "AsynSrv_SendSpecCmnd"); + AsynSrv_call_depth++; + } + /*---------------------------------------------- + ** Do nothing if no connection - the connection gets + ** closed if an error is detected. The connection may + ** also be marked to have been forcefully closed. + */ + if (asyn_info->skt <= 0) { + if ((AsynSrv_errcode == 0) && (asyn_info->skt < 0)) { + AsynSrv_errcode = ASYNSRV__FORCED_CLOSED; + } + return False; + } + /*---------------------------------------------- + ** Send the message to the server. + */ + status = send (asyn_info->skt, cmnd, 4, 0); + if (status != 4) { + GetErrno (&AsynSrv_errno, &AsynSrv_vaxc_errno); + if (status == 0) { + AsynSrv_errcode = ASYNSRV__BAD_SEND; /* Server exited (probably) */ + fprintf (stderr, "\nAsynSrv_SendSpecCmnd/send: probable network problem"); + }else if (status == -1) { + if (AsynSrv_errno == EPIPE) { + AsynSrv_errcode = ASYNSRV__BAD_SEND_PIPE; /* Server exited (probably) */ + fprintf (stderr, "\nAsynSrv_SendSpecCmnd/send: broken network pipe"); + }else { + AsynSrv_errcode = ASYNSRV__BAD_SEND_NET; /* It's some other net problem */ + perror ("AsynSrv_SendSpecCmnd/send"); + } + }else { + AsynSrv_errcode = ASYNSRV__BAD_SEND_UNKN; /* TCP/IP problems */ + fprintf (stderr, "\nAsynSrv_SendSpecCmnd/send: probable TCP/IP problem"); + } + AsynSrv_Close (asyn_info, True); /* Force close TCP/IP connection */ + fprintf (stderr, " - link to server force-closed.\n"); + return False; + } + + status = recv (asyn_info->skt, rply, 4, 0); + if (status != 4) { + GetErrno (&AsynSrv_errno, &AsynSrv_vaxc_errno); + if (status == 0) { + AsynSrv_errcode = ASYNSRV__BAD_RECV; /* Server exited (probably) */ + fprintf (stderr, "\nAsynSrv_SendSpecCmnd/recv: probable network problem"); + }else if (status == -1) { + if (AsynSrv_errno == EPIPE) { + AsynSrv_errcode = ASYNSRV__BAD_RECV_PIPE; /* Server exited (probably) */ + fprintf (stderr, "\nAsynSrv_SendSpecCmnd/recv: broken network pipe"); + }else { + AsynSrv_errcode = ASYNSRV__BAD_RECV_NET; /* It's some other net problem */ + perror ("AsynSrv_SendSpecCmnd/recv"); + } + }else { + AsynSrv_errcode = ASYNSRV__BAD_RECV_UNKN; /* TCP/IP problems */ + fprintf (stderr, "\nAsynSrv_SendSpecCmnd/recv: probable TCP/IP problem"); + } + AsynSrv_Close (asyn_info, True); /* Force close TCP/IP connection */ + fprintf (stderr, " - link to server force-closed.\n"); + return False; + } + if (memcmp (cmnd, rply, 4) != 0) { + AsynSrv_errcode = ASYNSRV__BAD_NOT_BCD; /* Message not echoed OK */ + AsynSrv_Close (asyn_info, True); /* Force close TCP/IP connection */ + fprintf (stderr, "\nAsynSrv_SendSpecCmnd/recv: command not echoed correctly" + " - link to server force-closed.\n"); + return False; + } + if (AsynSrv_errcode == 0) AsynSrv_call_depth--; + return True; + } +/* +**--------------------------------------------------------------------------- +** AsynSrv_Trace: Send a "TRACE" request to RS232C server. +*/ + int AsynSrv_Trace ( +/* ============= +*/ struct AsynSrv__info *asyn_info, + int state) { + + int status; + char cmnd[8], rply[8]; + /*---------------------------------------------- + ** Pre-set the routine name (in case of error) + */ + if (AsynSrv_errcode == 0 && AsynSrv_call_depth < 5) { + strcpy (AsynSrv_routine[AsynSrv_call_depth], "AsynSrv_Trace"); + AsynSrv_call_depth++; + } + /*---------------------------------------------- + ** Do nothing if no connection - the connection gets + ** closed if an error is detected. The connection may + ** also be marked to have been forcefully closed. + */ + if (asyn_info->skt <= 0) { + if ((AsynSrv_errcode == 0) && (asyn_info->skt < 0)) { + AsynSrv_errcode = ASYNSRV__FORCED_CLOSED; + } + return False; + } + /*---------------------------------------------- + ** Select message for server according to value of state. + */ + if (state) { + strcpy (cmnd, "-002"); + }else { + strcpy (cmnd, "-003"); + } + /*---------------------------------------------- + ** Send message and get reply. + */ + status = AsynSrv_SendSpecCmnd (asyn_info, cmnd); + + if (AsynSrv_errcode == 0) AsynSrv_call_depth--; + return status; + } +/* +**--------------------------------------------------------------------------- +** AsynSrv_Trace_Write: Send a Trace_Write command to +** RS232C server. +*/ + int AsynSrv_Trace_Write ( +/* =================== +*/ struct AsynSrv__info *asyn_info) { + + int status; + char cmnd[8], rply[8]; + /*---------------------------------------------- + ** Pre-set the routine name (in case of error) + */ + if (AsynSrv_errcode == 0 && AsynSrv_call_depth < 5) { + strcpy (AsynSrv_routine[AsynSrv_call_depth], "AsynSrv_Trace_Write"); + AsynSrv_call_depth++; + } + /*---------------------------------------------- + ** Do nothing if no connection - the connection gets + ** closed if an error is detected. The connection may + ** also be marked to have been forcefully closed. + */ + if (asyn_info->skt <= 0) { + if ((AsynSrv_errcode == 0) && (asyn_info->skt < 0)) { + AsynSrv_errcode = ASYNSRV__FORCED_CLOSED; + } + return False; + } + /*---------------------------------------------- + ** Send message and get reply. + */ + status = AsynSrv_SendSpecCmnd (asyn_info, "-005"); + + if (AsynSrv_errcode == 0) AsynSrv_call_depth--; + return status; + } +/*-------------------------------------------- End of AsynSrv_Utility.C -----*/ diff --git a/hardsup/c_interfaces.c b/hardsup/c_interfaces.c new file mode 100644 index 0000000..a514c64 --- /dev/null +++ b/hardsup/c_interfaces.c @@ -0,0 +1,472 @@ +#define ident "1A05" +#ifdef VAXC +#module C_INTERFACES ident +#endif +#ifdef __DECC +#pragma module C_INTERFACES ident +#endif +/* +** +--------------------------------------------------------------+ +** | Paul Scherrer Institute | +** | Computing Section | +** | | +** | This software may be used freely by non-profit organizations.| +** | It may be copied provided that the name of P.S.I. and of the | +** | author is included. Neither P.S.I. nor the author assume any | +** | responsibility for the use of this software outside of P.S.I.| +** +--------------------------------------------------------------+ +** +** Module Name . . . . . . . . : [...LIB.SINQ]C_INTERFACES.C +** +** Author . . . . . . . . . . : D. Maden +** Date of creation . . . . . . : Nov 1993 +** +** C_INTERFACES.C provides some routines which make it easier for C programs +** to call some of the Fortran routines in SINQ.OLB. +** +** To include this module in SINQ.OLB, use: + + $ import tasmad + $ define/job sinq_c_tlb mad_lib:sinq_c_tlb.tlb + $ define/job sinq_olb mad_lib:sinq.olb + $ @lnsa09::tasmad_disk:[mad.psi.lib.sinq]sinq_olb c_interfaces debug + $ + $ define/job sinq_olb mad_lib:sinq.olb + $ @lnsa09::tasmad_disk:[mad.psi.lib.sinq]sinq_olb c_interfaces + +** +** Updates: +** 1A01 16-Nov-1993 DM. Initial version. +** 1A02 24-Nov-1994 DM. Make compatible with DEC C (as opposed to VAX C) +** 1A03 28-Nov-1994 DM. Add the TT_PORT_... entry points. +**==================================================================== +** The following entry pointd are included: +** C_log_arr_get : interface routine from C to LOG_ARR_GET. +** C_log_flt_get : interface routine from C to LOG_FLT_GET. +** C_log_int_get : interface routine from C to LOG_INT_GET. +** C_log_str_get : interface routine from C to LOG_STR_GET. +** +** C_str_edit : interface routine to STR_EDIT. +** +** C_tt_port_connect : interface routine to TT_PORT_CONNECT. +** C_tt_port_disconnect: interface routine to TT_PORT_DISCONNECT. +** C_tt_port_io : interface routine to TT_PORT_IO. +** C_tt_port_config : interface routine to TT_PORT_CONFIG. +**==================================================================== +** Global Definitions +*/ +#ifdef VAXC +#include stdio +#include descrip +#include string +#include sinq_prototypes +#else +#include +#include +#include +#include +#endif +/*-------------------------------------------------------------------------- +** Global Variables +*/ + extern int C_gbl_status = 0; + extern struct dsc$descriptor_s C_name_desc = {0, + DSC$K_DTYPE_T, + DSC$K_CLASS_S, + 0}; +/*-------------------------------------------------------------------------- +** Old-style prototypes of routines which we are +** bridging to. +*/ + int log_arr_get (); + int log_int_get (); + int log_flt_get (); + int log_str_get (); + + int str_edit (); + + int tt_port_connect (); + int tt_port_disconnect (); + int tt_port_io (); + int tt_port_config (); +/* --------------------------------------------------------------------------*/ + int C_log_arr_get (char *name, int arr_size, int *value, int indx) { +/* ============= +** +** This routine is useful for calling LOG_ARR_GET from a C program. +** +** Inputs: +** name - a pointer to the zero-terminated logical name. +** arr_size - the number of elements in the array value. +** indx - the index of the logical name. +** Outputs: +** value - an array of size arr_size set to the values converted +** to binary. +** Return status: +** the return status of the function is zero (false) if LOG_ARR_GET +** returns an error (even) condition code. +** Global variables: +** C_gbl_status - set to the VAX/VMS return status of LOG_ARR_GET. +** C_name_desc - set up as a string descriptor for name. It can +** be used to generate an error message if return status == 0. +*/ + C_name_desc.dsc$w_length = strlen (name); + C_name_desc.dsc$a_pointer = name; + + C_gbl_status = log_arr_get (&C_name_desc, &arr_size, value, &indx); + + return (C_gbl_status & 1); + } +/*--------------------------------------------------------------------------*/ + int C_log_int_get (char *name, long int *value, int indx) { +/* ============= +** +** This routine is useful for calling LOG_INT_GET from a C program. +** +** Inputs: +** name - a pointer to the zero-terminated logical name. +** indx - the index of the logical name. +** Outputs: +** value - the value of the logical converted to binary. +** Return status: +** the return status of the function is zero (false) if LOG_INT_GET +** returns an error (even) condition code. +** Global variables: +** C_gbl_status - set to the VAX/VMS return status of LOG_INT_GET. +** C_name_desc - set up as a string descriptor for name. It can +** be used to generate an error message if return status == 0. +*/ + C_name_desc.dsc$w_length = strlen (name); + C_name_desc.dsc$a_pointer = name; + + C_gbl_status = log_int_get (&C_name_desc, value, &indx); + + return (C_gbl_status & 1); + } +/*--------------------------------------------------------------------------*/ + int C_log_flt_get (char *name, float *value, int indx) { +/* ============= +** +** This routine is useful for calling LOG_FLT_GET from a C program. +** +** Inputs: +** name - a pointer to the zero-terminated logical name. +** indx - the index of the logical name. +** Outputs: +** value - the value of the logical converted to binary. +** Return status: +** the return status of the function is zero (false) if LOG_FLT_GET +** returns an error (even) condition code. +** Global variables: +** C_gbl_status - set to the VAX/VMS return status of LOG_FLT_GET. +** C_name_desc - set up as a string descriptor for name. It can +** be used to generate an error message if return status == 0. +*/ + C_name_desc.dsc$w_length = strlen (name); + C_name_desc.dsc$a_pointer = name; + + C_gbl_status = log_flt_get (&C_name_desc, value, &indx); + + return (C_gbl_status & 1); + } +/*--------------------------------------------------------------------------*/ + int C_log_str_get (char *name, char *value, int val_size, int indx) { +/* ============= +** +** This routine is useful for calling LOG_STR_GET from a C program. +** +** Inputs: +** name - a pointer to the zero-terminated logical name. +** val_size - the size of the value string. +** indx - the index of the logical name. +** Outputs: +** value - zero-terminated string giving the value of the logical. +** Trailing space characters will have been stripped. +** Return status: +** the return status of the function is zero (false) if LOG_STR_GET +** returns an error (even) condition code. +** Global variables: +** C_gbl_status - set to the VAX/VMS return status of LOG_STR_GET. +** C_name_desc - set up as a string descriptor for name. It can +** be used to generate an error message if return status == 0. +*/ + struct dsc$descriptor_s my_val_desc = {0, + DSC$K_DTYPE_T, + DSC$K_CLASS_S, + 0}; + + C_name_desc.dsc$w_length = strlen (name); + C_name_desc.dsc$a_pointer = name; + + my_val_desc.dsc$w_length = val_size - 1; + my_val_desc.dsc$a_pointer = value; + + C_gbl_status = log_str_get (&C_name_desc, &my_val_desc, &indx); + value[val_size - 1] = 0; /* Zero-terminate the string */ + + if (C_gbl_status & 1) { /* If success, strip trailing spaces */ + while ((strlen (value) > 0) && (value[strlen (value) - 1] == ' ')) { + value[strlen (value) - 1] = 0; + } + } + return (C_gbl_status & 1); + } +/*--------------------------------------------------------------------------*/ + int C_str_edit (char *out, char *in, char *ctrl, int *length) { +/* ========== +** +** This routine is useful for calling STR_EDIT from a C program. +** +** Inputs: +** in - the string to be edited. +** ctrl - the string specifying what editing is to be done. +** Outputs: +** out - the edited string. The maximum size of this string must +** be specified as input parameter *length. The string +** will be zero terminated on return. +** Modified: +** *length - an integer specifying, on input, the length of "out" in +** bytes. This must include room for the zero termination. +** On return, length will be set to the number of characters +** copied to "out" (not counting the zero termination byte). +** Return status: +** the return status of the function is zero (false) if STR_EDIT +** returns an error (even) condition code. +** Global variables: +** C_gbl_status - set to the VAX/VMS return status of STR_EDIT. +*/ + struct dsc$descriptor_s out_desc = {0, + DSC$K_DTYPE_T, + DSC$K_CLASS_S, + 0}; + struct dsc$descriptor_s in_desc = {0, + DSC$K_DTYPE_T, + DSC$K_CLASS_S, + 0}; + struct dsc$descriptor_s ctrl_desc = {0, + DSC$K_DTYPE_T, + DSC$K_CLASS_S, + 0}; + + out_desc.dsc$w_length = *length - 1; + out_desc.dsc$a_pointer = out; + + in_desc.dsc$w_length = strlen (in); + in_desc.dsc$a_pointer = in; + + ctrl_desc.dsc$w_length = strlen (ctrl); + ctrl_desc.dsc$a_pointer = ctrl; + + C_gbl_status = str_edit (&out_desc, &in_desc, &ctrl_desc, length); + if (*length >= 0) { /* zero-terminate the output string */ + out[*length] = '\0'; + }else { + out[0] = '\0'; + } + + return (C_gbl_status & 1); + } +/*--------------------------------------------------------------------------*/ + int C_tt_port_connect (int *hndl, int *chan, char *lognam, char *pwd) { +/* ================= +** +** This routine is useful for calling TT_PORT_CONNECT from a C program. +** +** Inputs: +** lognam - a zero-terminated string specifying a logical name which +** defines the RS-232-C port to be connected. See description +** of TT_PORT_CONNECT for full details. +** pwd - a zero-terminated string specifying an optional password. +** This is the password associated with a terminal server +** service. See description of TT_PORT_CONNECT for full +** details. Specify NULL if no password. +** Outputs: +** hndl - an integer handle identifying the connection. It will be +** the address of a dynamically allocated data structure. +** chan - an integer (actually only 16 bit) giving the I/O channel +** associated with the connection. This can be used in QIO +** system calls to the terminal driver. +** Return status: +** the return status of the function is zero (false) if TT_PORT_CONNECT +** returns an error (even) condition code. +** Global variables: +** C_gbl_status - set to the VAX/VMS return status of TT_PORT_CONNECT. +*/ + struct dsc$descriptor_s lognam_desc = {0, + DSC$K_DTYPE_T, + DSC$K_CLASS_S, + 0}; + struct dsc$descriptor_s pwd_desc = {0, + DSC$K_DTYPE_T, + DSC$K_CLASS_S, + 0}; + lognam_desc.dsc$w_length = strlen (lognam); + lognam_desc.dsc$a_pointer = lognam; + + if (pwd != NULL) { + pwd_desc.dsc$w_length = strlen (pwd); + pwd_desc.dsc$a_pointer = pwd; + C_gbl_status = tt_port_connect ( + hndl, chan, &lognam_desc, &pwd_desc); + }else { + C_gbl_status = tt_port_connect (hndl, chan, &lognam_desc, NULL); + } + + return (C_gbl_status & 1); + } +/*--------------------------------------------------------------------------*/ + int C_tt_port_disconnect (int *hndl) { +/* ==================== +** +** This routine is useful for calling TT_PORT_DISCONNECT from a C program. +** +** Inputs: +** hndl - the integer handle identifying the connection as returned +** by C_tt_port_connect. It is the address of a dynamically +** allocated data structure which will also be released +** after the connection has been closed. +** Return status: +** the return status of the function is zero (false) if TT_PORT_DISCONNECT +** returns an error (even) condition code. +** Global variables: +** C_gbl_status - set to the VAX/VMS return status of TT_PORT_DISCONNECT. +*/ + C_gbl_status = tt_port_disconnect (hndl); + + return (C_gbl_status & 1); + } +/*--------------------------------------------------------------------------*/ + int C_tt_port_io ( +/* ============ +*/ int *hndl, + char *rqst, + char *term, + char *answ, + int *answ_len, /* Attention -- Read/Write argument!! */ + int flush, + int tmo) { +/* +** This routine is useful for calling TT_PORT_IO from a C program. +** Refer to the DELTAT.OLB description of TT_PORT_IO to clarify any +** uncertainties in the following description. Note that all arguments +** must be present (there is no portable method in C of getting the +** number of arguments in the call!). +** Inputs: +** hndl - the integer handle identifying the connection as returned +** by C_tt_port_connect. +** rqst - an optional zero-terminated string specifying a character +** string to be sent to the port. Specify NULL to not send +** any characters to the port. +** term - an optional zero-terminated string specifying a list of +** terminating characters for input read from the port. +** Specify NULL to terminate input on an exact character cnt. +** flush - an integer specifying if the type-ahead buffer should be +** flushed before the operation. If non-zero, the buffer +** is flushed. +** tmo - an integer (recommended value = 2) specifying a read +** time-out in seconds. Zero or negative indicates infinity. +** Outputs: +** answ - an optional string buffer to receive characters read from +** the port. If answ is not NULL, answ_len must also be +** not NULL. On return, answ will be zero terminated. The +** terminating character, if any (there is no terminating +** char if the buffer overflows) and if there is room in +** the buffer, will follow the zero character. No padding is +** done. If answ is NULL, no characters are read from the +** port. +** Modify: +** answ_len - an integer specifying, on input, the length of answ in +** bytes. This must include room for the zero termination. +** On return, answ_len will be set to the number of +** characters read (not counting the zero termination byte or +** any terminating character). +** Return status: +** the return status of the function is zero (false) if TT_PORT_IO +** returns an error (even) condition code. +** Global variables: +** C_gbl_status - set to the VAX/VMS return status of TT_PORT_IO. +*/ + struct dsc$descriptor_s rqst_desc = {0, + DSC$K_DTYPE_T, + DSC$K_CLASS_S, + 0}; + struct dsc$descriptor_s term_desc = {0, + DSC$K_DTYPE_T, + DSC$K_CLASS_S, + 0}; + struct dsc$descriptor_s answ_desc = {0, + DSC$K_DTYPE_T, + DSC$K_CLASS_S, + 0}; + char *my_rqst = NULL; + char *my_term = NULL; + char *my_answ = NULL; + + int my_answ_len = 0; + int my_flush = 1; + int my_tmo = 2; + + my_tmo = tmo; + if (my_tmo < 0) my_tmo = 0; + my_flush = flush; + if (my_flush != 0) my_flush = 1; + if (answ != NULL) { + if (answ_len == 0) { + printf ("C_tt_port_io -- argument error.\n"); + printf (" %s\n", + "answ_len must be present if answ is present."); + C_gbl_status = FALSE; + return FALSE; + } + answ_desc.dsc$w_length = *answ_len - 1; + answ_desc.dsc$a_pointer = answ; + } + if (term != NULL) { + term_desc.dsc$w_length = strlen (term); + term_desc.dsc$a_pointer = term; + } + if (rqst != NULL) { + rqst_desc.dsc$w_length = strlen (rqst); + rqst_desc.dsc$a_pointer = rqst; + } + C_gbl_status = tt_port_io (hndl, &rqst_desc, &term_desc, + &answ_desc, &my_answ_len, &my_flush, &my_tmo); + if (answ_desc.dsc$w_length > 0) { /* Process any input string */ + if (answ_desc.dsc$w_length > my_answ_len) { /* Room for terminator? */ + answ[my_answ_len+1] = answ[my_answ_len]; /* Yes, so move it. */ + } + answ[my_answ_len] = '\0'; /* Put in null terminator */ + *answ_len = my_answ_len; /* Return value to caller */ + } + + return (C_gbl_status & 1); + } +/*--------------------------------------------------------------------------*/ + int C_tt_port_config ( +/* ================ +*/ int *hndl, + int mask) { +/* +** This routine is useful for calling TT_PORT_CONFIG from a C program. +** Refer to the DELTAT.OLB description of TT_PORT_CONFIG to clarify any +** uncertainties in the following description. +** Inputs: +** hndl - the integer handle identifying the connection as returned +** by C_tt_port_connect. +** mask - an integer specifying the configuration options. Set bits +** TT_PORT__NO_RETRY = 0x0001 to suppress retries on error +** TT_PORT__NO_SIG = 0x0002 to suppress signals on error +** Outputs: +** None +** Modify: +** None +** Return status: +** always non-zero (true). +** Global variables: +** C_gbl_status - set to the VAX/VMS return status of TT_PORT_CONFIG. +*/ + C_gbl_status = tt_port_config (hndl, &mask); + + return (C_gbl_status & 1); + } +/*=========================================== End of C_INTERFACES.C ========*/ diff --git a/hardsup/dillutil.c b/hardsup/dillutil.c new file mode 100644 index 0000000..c9e8c49 --- /dev/null +++ b/hardsup/dillutil.c @@ -0,0 +1,481 @@ +/*-------------------------------------------------------------------------- + + D I L U T I L + + A few utility functions for dealing with a Dillution emperature controller + CC0-510/AVSI + within the SINQ setup: host -- TCP/IP -- MAC --- RS-232. + + Mark Koennecke, October 1997 + + Copyright: see copyrigh.h +---------------------------------------------------------------------------- */ +#include +#include +#include +#include +#include "serialsinq.h" +#include "dillutil.h" + +#ifdef FORTIFY +#include "../fortify.h" +#endif + +/* +#define debug 1 +*/ +/*-------------------------------------------------------------------------*/ + + int DILLU_Open(pDILLU *pData, char *pHost, int iPort, int iChannel, + int iMode, char *pTransFile) + { + int iRet; + char pCommand[80]; + char pReply[132]; + pDILLU self = NULL; + pSTable pTable = NULL; + FILE *fd = NULL; + + /* check translation file first */ + fd = fopen(pTransFile,"r"); + if(!fd) + { + return DILLU__FILENOTFOUND; + } + fgets(pReply, 131,fd); + if(strstr(pReply,"DILLUTION") == NULL) + { + fclose(fd); + return DILLU__NODILLFILE; + } + + pTable = CreateTable(fd); + fclose(fd); + if(!pTable) + { + return DILLU__ERRORTABLE; + } + + /* allocate a new data structure */ + self = (pDILLU)malloc(sizeof(DILLU)); + if(self == NULL) + { + return DILLU__BADMALLOC; + } + + *pData = self; + self->pTranstable = pTable; + + iRet = SerialOpen(&self->pData, pHost, iPort, iChannel); + if(iRet != 1) + { + return iRet; + } + + /* set an lengthy timeout for the configuration in order to + prevent problems. + */ + iRet = SerialConfig(&self->pData, 100); + if(iRet != 1) + { + return iRet; + } + + self->iReadOnly = iMode; + if(!self->iReadOnly) + { + /* switch to remote operation */ +/* iRet = SerialWriteRead(&self->pData,"C1\r\n",pReply,131); + if(iRet != 1) + { + return iRet; + } +*/ + } + return 1; + } +/* --------------------------------------------------------------------------*/ + void DILLU_Close(pDILLU *pData) + { + char pReply[132]; + int iRet; + pDILLU self; + + self = *pData; + + if(!self) + return; + + /* switch to local operation */ + iRet = SerialWriteRead(&self->pData,"C0\r\n",pReply,131); + /* ignore errors on this one, the thing may be down */ + + /* close connection */ + SerialClose(&self->pData); + + /* free memory */ + free(self); + *pData = NULL; + } +/* --------------------------------------------------------------------------*/ + int DILLU_Config(pDILLU *pData, int iTmo) + { + int iRet; + char pReply[132]; + char pCommand[10]; + pDILLU self; + + self = *pData; + + /* first timeout */ + if(iTmo > 0) + { + iRet = SerialConfig(&self->pData, iTmo); + if(iRet < 0) + { + return iRet; + } + } + return 1; + } +/* --------------------------------------------------------------------------*/ + int DILLU_Send(pDILLU *pData, char *pCommand, char *pReply, int iLen) + { + pDILLU self; + + self = *pData; + + /* make sure, that there is a \r at the end of the command */ + if(strchr(pCommand,(int)'\r') == NULL) + { + strcat(pCommand,"\r\n"); + } + return SerialWriteRead(&self->pData,pCommand,pReply,iLen); + } +/* --------------------------------------------------------------------------*/ + int DILLU_Read(pDILLU *pData, float *fVal) + { + char pCommand[10], pReply[132]; + int iRet; + float fRead = -9999.; + float fOhm; + pDILLU self; + + self = *pData; + + + /* send D command */ + sprintf(pCommand,"D\r\n"); + iRet = SerialWriteRead(&self->pData,pCommand,pReply,131); + if(iRet != 1) + { + return iRet; + } + + /* read ohms */ + iRet = sscanf(pReply,"%f",&fOhm); + if(iRet != 1) + { + return DILLU__BADREAD; + } + if(fOhm > 9999890.) + { + return DILLU__SILLYANSWER; + } + + /* convert to K */ + iRet = InterpolateVal2(self->pTranstable,fOhm,&fRead); + *fVal = fRead; + return 1; + } +/*-------------------------------------------------------------------------*/ + int DILLU_Set(pDILLU *pData, float fVal) + { + char pCommand[50], pReply[132]; + int iRet, i,iRange, iExec; + const float fPrecision = 0.0001; + float fSet, fRead, fOhms, tmax, fTemp; + pDILLU self; + + self = *pData; + + if(self->iReadOnly) + { + return DILLU__READONLY; + } + + /* send D command to read current value*/ + sprintf(pCommand,"D\r\n"); + iRet = SerialWriteRead(&self->pData,pCommand,pReply,131); + if(iRet != 1) + { + return iRet; + } + + /* read ohms */ + iRet = sscanf(pReply,"%f",&fRead); + if(iRet != 1) + { + return DILLU__BADREAD; + } + if(fRead > 9999890.) + { + return DILLU__SILLYANSWER; + } + + + /* convert new set value to ohms */ + iRet = InterpolateVal1(self->pTranstable,fVal,&fOhms); + if(!iRet) + { + return DILLU__OUTOFRANGE; + } + + /* set to remote operation */ +#ifdef debug + printf("C1\n"); +#endif + iRet = SerialWriteRead(&self->pData,"C1\r\n",pReply,131); + if(iRet != 1) + { + return iRet; + } + + /* set heater power */ + strcpy(pCommand,"G3\r"); + if(fOhms > 1125) + { + strcpy(pCommand,"G2\r"); + } + if(fOhms > 4000) + strcpy(pCommand,"G1\r"); +#ifdef debug + printf("A9\n"); +#endif + iRet = SerialWriteRead(&self->pData,"A9\r",pReply,131); + if(iRet != 1) + { + return iRet; + } +#ifdef debug + printf("%s\n",pCommand); +#endif + iRet = SerialWriteRead(&self->pData,pCommand,pReply,131); + if(iRet != 1) + { + return iRet; + } + + /* Integrator time constant */ + strcpy(pCommand,"G2\r"); + if(fOhms > 200) + strcpy(pCommand,"G1\r"); + if(fOhms > 2000) + strcpy(pCommand,"G0\r"); + strcpy(pCommand,"G7\r"); + if(fOhms > 400.) + { + strcpy(pCommand,"G6\r"); + } +#ifdef debug + printf("A4\n"); +#endif + iRet = SerialWriteRead(&self->pData,"A4\r",pReply,131); + if(iRet != 1) + { + return iRet; + } +#ifdef debug + printf("%s\n",pCommand); +#endif + iRet = SerialWriteRead(&self->pData,pCommand,pReply,131); + if(iRet != 1) + { + return iRet; + } + + /* derivator time constant */ + if(fOhms > 1000.) + { + strcpy(pCommand,"G1\r"); + } + else + { + strcpy(pCommand,"G2\r"); + } +#ifdef debug + printf("A5\n"); +#endif + iRet = SerialWriteRead(&self->pData,"A5\r",pReply,131); + if(iRet != 1) + { + return iRet; + } +#ifdef debug + printf("%s\n",pCommand); + iRet = 1; +#endif + iRet = SerialWriteRead(&self->pData,pCommand,pReply,131); + if(iRet != 1) + { + return iRet; + } + + /* proportional gain */ + if(fOhms > 500.) + { + strcpy(pCommand,"G3\r"); + } + if(fOhms > 1000) + { + strcpy(pCommand,"G2\r"); + } + if(fOhms > 2000) + { + strcpy(pCommand,"G1\r"); + } +#ifdef debug + printf("A6\n"); +#endif + iRet = SerialWriteRead(&self->pData,"A6\r",pReply,131); + if(iRet != 1) + { + return iRet; + } +#ifdef debug + printf("%s\n",pCommand); +#endif + iRet = SerialWriteRead(&self->pData,pCommand,pReply,131); + if(iRet != 1) + { + return iRet; + } + + /* range calculation a la Elsenhans */ + iRange = 1; + fTemp = fOhms*10000.; + if( (fRead > 1.9) || (fOhms > 1.9) ) + { + iRange = 2; + fTemp = fOhms*1000.; + } + if( (fRead > 19) || (fOhms > 19) ) + { + iRange = 3; + fTemp = fOhms*100.; + } + if( (fRead > 190) || (fOhms > 190) ) + { + iRange = 4; + fTemp = fOhms*10.; + } + if( (fRead > 750) || (fOhms > 750) ) + { + iRange = 5; + fTemp = fOhms; + } + if( (fRead > 19000) || (fOhms > 19000) ) + { + iRange = 6; + fTemp = fOhms/10.; + } + if( (fRead > 190000) || (fOhms > 190000) ) + { + iRange = 7; + fTemp = fOhms/100.; + } + + sprintf(pCommand,"R%1.1d\r",iRange); +#ifdef debug + printf("%s\n",pCommand); +#endif + iRet = SerialWriteRead(&self->pData,pCommand,pReply,131); + if(iRet != 1) + { + return iRet; + } + + + /* finally set temperature */ +#ifdef debug + printf("Set Val befor hex: %d\n",(int)fTemp); +#endif + sprintf(pCommand,"G%4.4X\r",(int)fTemp); +#ifdef debug + printf("A3\n"); +#endif + iRet = SerialWriteRead(&self->pData,"A3\r",pReply,131); + if(iRet != 1) + { + return iRet; + } +#ifdef debug + printf("%s\n",pCommand); +#endif + iRet = SerialWriteRead(&self->pData,pCommand,pReply,131); + if(iRet != 1) + { + return iRet; + } + + /* unset remote operation, so that users may mess everything up + from the panel + */ +#ifdef debug + printf("C1\n"); +#endif + iRet = SerialWriteRead(&self->pData,"C0\r\n",pReply,131); + if(iRet != 1) + { + return iRet; + } + + return 1; + } +/*-------------------------------------------------------------------------*/ + void DILLU_Error2Text(pDILLU *pData,int iCode, char *pError, int iLen) + { + char pBueffel[512]; + pDILLU self; + + self = *pData; + + switch(iCode) + { + case DILLU__FILENOTFOUND: + strncpy(pError,"Translation Table file not found",iLen); + return; + break; + case DILLU__NODILLFILE: + strncpy(pError,"Translation Table file is not DILLU",iLen); + return; + break; + case DILLU__ERRORTABLE: + strncpy(pError,"Translation Table could not be created",iLen); + return; + break; + case DILLU__BADREAD: + strncpy(pError,"Message corrupted",iLen); + return; + break; + case DILLU__SILLYANSWER: + strncpy(pError,"Message corrupted",iLen); + return; + break; + case DILLU__BADMALLOC: + strncpy(pError,"Out of memory in Open_DILLU",iLen); + return; + break; + case DILLU__READONLY: + strncpy(pError,"DILLU is read-only",iLen); + return; + break; + case DILLU__OUTOFRANGE: + strncpy(pError,"Requested value is out of range",iLen); + return; + break; + default: + SerialError(iCode,pError,iLen); + break; + } + } diff --git a/hardsup/dillutil.h b/hardsup/dillutil.h new file mode 100644 index 0000000..b1ac9c3 --- /dev/null +++ b/hardsup/dillutil.h @@ -0,0 +1,108 @@ +/*--------------------------------------------------------------------------- + D I L U U T I L + + A few utility functions for talking to Dillution temperature controller + CCO-510/ AVSI via the SINQ setup: TCP/IP--MAC--RS-232--DILLU. + + This controller is weird in that way, that is accepts temperatures as + resistance values in Ohms. Therefore a translation table is required + in order to convert from Kelvin to Ohms. + + Mark Koennecke, October 1997 + +----------------------------------------------------------------------------*/ +#ifndef SINQDILLU +#define SINQDILLU +#include +#include "table.h" + +/*----------------------- ERRORCODES-------------------------------------- + Most functions return a negative error code on failure. Error codes + defined are those defined for serialsinq plus a few additional ones: +*/ +#define DILLU__FILENOTFOUND -710 +#define DILLU__NODILLFILE -711 +#define DILLU__ERRORTABLE -712 +#define DILLU__BADREAD -713 +#define DILLU__SILLYANSWER -714 +#define DILLU__READONLY -715 +#define DILLU__OUTOFRANGE -716 +#define DILLU__BADMALLOC -717 +#define DILLU__NODILLUFOUND -711 +/*------------------------------------------------------------------------*/ + typedef struct __DILLU { + void *pData; + pSTable pTranstable; + int iReadOnly; + } DILLU; + + typedef struct __DILLU *pDILLU; + +/*-----------------------------------------------------------------------*/ + int DILLU_Open(pDILLU *pData,char *pHost, int iPort, int iChannel, + int iMode, char *pTransFile); + /***** creates an DILLU datastructure and opens a connection to the ITCL4 + controller. Input Parameters are: + the hostname + the port number + the RS-232 channel number on the Mac. + iMode: 1 for ReadOnly, 0 for normal mode + pTransFile: name and path of the temperature ohms + trnslation file. + + Return values are 1 for success, a negative error code on + failure. + + */ + + void DILLU_Close(pDILLU *pData); + /****** close a connection to an DILLU controller and frees its + data structure. The only parameter is a pointer to the data + structure for this controller. This pointer will be invalid after + this call. + */ + + int DILLU_Config(pDILLU *pData, int iTmo); + /***** configure some aspects of a DILLU temperature controller. + The parameter are: + - a pointer to the data structure for the controller as + returned by Open_DILLU + - a value for the connection timeout + The function returns 1 on success, a negative error code on + failure. + */ + + int DILLU_Send(pDILLU *pData, char *pCommand, char *pReply, int iLen); + /******* send a the command in pCommand to the DILLU controller. + A possible reply is returned in the buffer pReply. + Maximum iLen characters are copied to pReply. + The first parameter is a pointer to a DILLU data structure + as returned by Open_DILLU. + + Return values are 1 for success, a negative error code on + failure. + */ + + int DILLU_Read(pDILLU *pData, float *fVal); + /****** + Reads the current temperature at the controller + + Return values are 1 for success, a negative error code on + failure. + */ + + int DILLU_Set(pDILLU *pData, float fVal); + /****** sets a new preset temperature in the DILL temperature + controller. Parameters are: + - a pointer to a DILLU data structure as returned by Open_DILLU. + - the new preset value. + + Return values are 1 for success, a negative error code on + failure. + */ + + void DILLU_Error2Text(pDILLU *pData, int iCode, char *pError, int iLen); + +#endif + + diff --git a/hardsup/el734_def.h b/hardsup/el734_def.h new file mode 100644 index 0000000..f3a6437 --- /dev/null +++ b/hardsup/el734_def.h @@ -0,0 +1,73 @@ +#ifndef _el734_def_ +#define _el734_def_ +/*------------------------------------------------ EL734_DEF.H Ident V01R +*/ +#include +#include +#include + +#ifndef OffsetOf +#define OffsetOf(type, identifier) ((size_t)(&((type*) NULL)->identifier)) +#endif + +#ifndef _EL734_errcodes_ +#define _EL734_errcodes_ +#include +#endif + +#define MAX_MOT 12 + +enum EL734_Requests {FULL__STATUS, + SHORT__STATUS}; +/* +** Structure to which the EL734_Open handle points. +*/ + struct EL734info { + struct AsynSrv__info asyn_info; /* Contains skt, host, port & chan */ + int motor; + int ored_msr, fp_cntr, fr_cntr; + struct RS__MsgStruct to_host; + struct RS__RespStruct from_host; + }; +/* +** Structure holding everything that is known about a VME Motor Controller. +** It is also the structure of replies from the Server. +*/ + struct Motor_State { + int motor; /* Motor number */ + int exists; /* True if Motor exists */ + int msr; /* MSR - Motor Status Register */ + int ored_msr; /* Cumulated MSR */ + int fp_cntr; /* Counter for *FP reports */ + int fr_cntr; /* Counter for *FR reports */ + int ss; /* SS - Status Flags Register */ + char pos_real[16]; /* U - Position as read (degrees) */ + char name[16]; /* MN */ + int dec_pt; /* A - # of decimal places */ + int enc_factor[2]; /* FD - Encoder scaling factors (numer/denom) */ + int mot_factor[2]; /* FM - Motor scaling factors (numer/denom) */ + char inertia_tol[16];/* D - Inertia tol'nce (sec) (Schleppfehler) */ + int ramp; /* E - Start/stop ramp (kHz/sec) */ + int loop_mode; /* F - Open loop/Closed loop (0/1) */ + int slow_hz; /* G - Start/stop frequency (Mot-S/sec) */ + char lims[2][16]; /* H - Lower/Upper limits */ + int fast_hz; /* J - Top speed (Mot-S/sec) */ + int ref_mode; /* K - Reference mode */ + int backlash; /* L - Backlash par (Mot-S) (Spielausgleich) */ + int pos_tol; /* M - Position tolerance (Enc-Steps) */ + char ref_param[16]; /* Q - Parameter for "Goto Reference" */ + int is_sided; /* T - One-sided operation flag (0 = no) */ + char null_pt[16]; /* V - Null point */ + int ac_par; /* W - Air-cushion dependency */ + int enc_circ; /* Z - circumference of encoder (Enc-Steps) */ + int stat_pos; /* SP - # of positionings */ + int stat_pos_flt; /* ST - # of positioning faults (recovered) */ + int stat_pos_fail; /* SR - # of positioning fails (abandoned) */ + int stat_cush_fail; /* SA - # of air-cushion fails */ + char set_real[16]; /* P - Position as set (degrees) */ + int ac_state; /* AC - Air-cushion state (0 = down) */ + int out; /* SO - State of Output Signal */ + int in; /* RI - State of Input Signal */ + }; +/*------------------------------------------------ End of EL734_DEF.H --*/ +#endif /* _el734_def_ */ diff --git a/hardsup/el734_errcodes.h b/hardsup/el734_errcodes.h new file mode 100644 index 0000000..1a04d81 --- /dev/null +++ b/hardsup/el734_errcodes.h @@ -0,0 +1,28 @@ +/* +** TAS_SRC:[LIB]EL734_ERRCODES.H +** +** Include file generated from EL734_ERRCODES.OBJ +** +** 29-AUG-2000 09:49:19.60 +*/ + +#define EL734__VFY_ERR 0x865809C +#define EL734__NO_SOCKET 0x8658094 +#define EL734__NOT_OPEN 0x865808C +#define EL734__FORCED_CLOSED 0x8658084 +#define EL734__EMERG_STOP 0x865807C +#define EL734__BAD_TMO 0x8658074 +#define EL734__BAD_STP 0x865806C +#define EL734__BAD_SOCKET 0x8658064 +#define EL734__BAD_RNG 0x865805C +#define EL734__BAD_PAR 0x8658054 +#define EL734__BAD_OVFL 0x865804C +#define EL734__BAD_OFL 0x8658044 +#define EL734__BAD_MALLOC 0x865803C +#define EL734__BAD_LOC 0x8658034 +#define EL734__BAD_ILLG 0x865802C +#define EL734__BAD_DEV 0x8658024 +#define EL734__BAD_CMD 0x865801C +#define EL734__BAD_ASYNSRV 0x8658014 +#define EL734__BAD_ADR 0x865800C +#define EL734__FACILITY 0x865 diff --git a/hardsup/el734_utility.c b/hardsup/el734_utility.c new file mode 100644 index 0000000..53efe51 --- /dev/null +++ b/hardsup/el734_utility.c @@ -0,0 +1,2638 @@ +#define ident "1D08" +#ifdef VAXC +#module EL734_Utility ident +#endif +#ifdef __DECC +#pragma module EL734_Utility ident +#endif +/* +** +--------------------------------------------------------------+ +** | Paul Scherrer Institute | +** | Department ASQ | +** | | +** | This software may be used freely by non-profit organizations.| +** | It may be copied provided that the name of P.S.I. and of the | +** | author is included. Neither P.S.I. nor the author assume any | +** | responsibility for the use of this software outside of P.S.I.| +** +--------------------------------------------------------------+ +** +** Module Name . . . . . . . . : [...LIB.SINQ]EL734_Utility.C +** +** Author . . . . . . . . . . : D. Maden +** Date of creation . . . . . . : Nov 1995 +** +** To compile this module, use: + + $ import tasmad + $ define/group sinq_c_tlb mad_lib:sinq_c.tlb + $ cc /debug /noopt /obj=[]EL734_Utility - + tasmad_disk:[mad.psi.lib.sinq]EL734_Utility + - + sinq_c_tlb/lib + +** To include this module in SINQ.OLB, use: + + $ import tasmad + $ define/group sinq_c_tlb mad_lib:sinq_c.tlb + $ + $ define/group sinq_olb mad_lib:sinq_dbg.olb + $ @tasmad_disk:[mad.lib.sinq]sinq_olb EL734_Utility debug + $ + $ define/group sinq_olb mad_lib:sinq.olb + $ @tasmad_disk:[mad.lib.sinq]sinq_olb EL734_Utility +** +** Updates: +** 1A01 2-Nov-1995 DM. Initial version. +** 1B01 21-Mar-1996 DM. Move from DELTAT.OLB to SINQ.OLB. +** 1C01 3-Mar-1997 DM. Add "Forced-close" capability. +** 1C02 14-Apr-1997 DM. Add EL734__BAD_STP to EL734_MoveNoWait. +** 1C11 18-Jun-1998 DM. Modify EL734_GetZeroPoint. +** 1D01 4-Aug-1998 DM. Put messages into a .MSG file. +**============================================================================ +** The entry points included in this module are described below. Prototypes +** can be defined via: +** +** #include +** +** EL734_AddCallStack - Add a routine name to the call stack. +** EL734_Close - Close a connection to a motor. +** EL734_Config - Configure a connection to a motor. +** EL734_EncodeMSR - Encode the MSR status into text. +** EL734_EncodeSS - Encode the SS flags into text. +** EL734_ErrInfo - Return detailed status from last operation. +** EL734_GetAirCush - Get W and AC register values. +** EL734_GetEncGearing - Get FD register values. +** EL734_GetId - Get ID register value. +** EL734_GetLimits - Get H register values. +** EL734_GetMotorGearing - Get FM register values. +** EL734_GetNullPoint - Get V register value. +** EL734_GetPosition - Get U register value = current position. +** EL734_GetPrecision - Get A register value. +** EL734_GetRefMode - Get K register value. +** EL734_GetRefParam - Get Q register value. +** EL734_GetSpeeds - Get G, J and E register values. +** EL734_GetStatus - Get MSR/SS/U register values. +** EL734_GetZeroPoint - Get zero-point of motor. +** EL734_MoveNoWait - Move motor and don't wait for completion. +** EL734_MoveWait - Move motor and wait for completion. +** EL734_Open - Open a connection to a motor. +** EL734_PutOffline - Put the EL734 off-line. +** EL734_PutOnline - Put the EL734 on-line. +** EL734_SendCmnd - Send a command to RS232C server. +** EL734_SetAirCush - Set the air-cushion (AC register). +** EL734_SetErrcode - Set up EL734_errcode. +** EL734_SetHighSpeed - Set the max speed (J register). +** EL734_SetLowSpeed - Set the start/stop speed (G register). +** EL734_SetRamp - Set the start/stop ramp (E register). +** EL734_Stop - Send a stop command to motor. +** EL734_WaitIdle - Wait till MSR goes to zero. +** EL734_ZeroStatus - Zero the "ored-MSR" and fault counters. +**--------------------------------------------------------------------- +** int EL734_AddCallStack (&handle, &name) +** ------------------ +** Add a routine name to the call stack (internal use). +** Input Args: +** struct EL734info *handle - The pointer to the structure returned by +** EL734_Open. +** char *name - The name to be added to the call stack. +** Output Args: +** none +** Modified Args: +** none +** Return status: +** False if an error is detected, otherwise True. +** Routines called: +** none +** Description: +** If an error has already occurred (EL734_errcode != 0), the routine +** simply returns False. Otherwise, *name is added to the call stack. +** Then *handle is checked. +** If NULL, error EL734__NOT_OPEN is set and False is returned. +** Otherwise, the connection's TCP/IP socket number is checked. +** If zero, error EL734__NO_SOCKET is set and False is returned. +** If negative, error EL734__FORCED_CLOSE is set and False is returned. +** Otherwise, True is returned. +**--------------------------------------------------------------------- +** int EL734_Close (&handle, int force_flag) +** ----------- +** Close a connection to a motor. +** Input Args: +** int force_flag - if non-zero, all connections using the same socket +** will also be closed (this gets AsynSrv_Close to +** actually close the socket and is needed for error +** recovery operations). +** Output Args: +** none +** Modified Args: +** void **handle - The pointer to the structure returned by EL734_Open. +** On return, the pointer is set to NULL. +** Return status: +** True always (error returns from close and free are not checked). +** Routines called: +** AsynSrv_Close +** Description: +** The routine calls AsynSrv_Close to close the connection to the RS232C +** server. If 'force_flag' is non-zero, all other connections to the +** RS232C server which use the same socket will also be closed. +** +** The 'force_flag' can be useful in error recovery situations. The AsynSrv +** utility operates by only opening a socket for each separate combination +** of host/port. Hence, if several connections are open to the +** motors on an EL734, then calling EL734_Close doesn't actually close +** the socket until all connections have been closed. In the situation +** where an error has been detected on a motor, it is often desirable to +** close and re-open the socket as part of the recovery procedure. Calling +** EL734_Close with 'force_flag' non-zero will force the socket to be +** closed and will mark all connections using this socket so that they +** will be informed of the event when they next call an EL734_utility +** routine. +** +** Note: The force-close action is effected by the AsynSrv package. A +** force-close will thus also close any connections to other +** RS-232-C devices (e.g. EL737 neutron cntr) on the same server. +**------------------------------------------------------------------------- +** int EL734_Config (&handle, &par_id, par_val, ...) +** ------------ +** Configure a connection to a motor. +** Input Args: +** char* par_id - Text string identifying the next argument (see below). +** NULL indicates the end of the argument list. +** par_val - The value to set for the argument. The type of the +** argument can depend on par_id. +** Output Args: +** none +** Modified Args: +** void **handle - The pointer to the structure returned by EL734_Open. +** It is used to hold the config info for the connection. +** Return status: +** True if no problems detected, otherwise False and EL734_errcode +** is set to indicate the nature of the problem as follows: +** EL734__BAD_PAR --> Unrecognised par_id or msecTmo < 100 or +** msecTmo > 999'999 or bad eot or .. +** Routines called: +** none +** Description: +** The routine sets values in the EL734info data structure. Values which +** may be taken by par_id (warning -- par_id is case-sensitive) and the +** corresponding variable type of par_val are: +** +** "msecTmo" int The time-out response for commands sent to +** the EL734. The valid range is 100 to +** 999'999. Default is 10'000. +** "eot" char* The expected terminators in responses to +** commands sent to the EL734. The first +** character specifies the number of +** terminators (max=3). Default is "1\r". +** "motor" int The index of the motor in the range 1-12 to be +** associated with this connection. +** "chan" int The RS-232-C channel number of the EL734 +** controller associated with this connection. +**------------------------------------------------------------------------- +** char *EL734_EncodeMSR (&text, text_len, msr, ored_msr, fp_cntr, fr_cntr) +** --------------- +** Encode the MSR status into text. +** Input Args: +** int text_len - The size of text. +** int msr - The current MSR. +** int ored_msr - The 'ored' MSR to be encoded. +** int fp_cntr - The counter of *FP faults. +** int fr_cntr - The counter of *FR faults. +** Output Args: +** char *text - The resulting text string is stored here. +** Modified Args: +** none +** Return status: +** A pointer to "text". +** Routines called: +** none +** Description: +** The routine makes an intelligible message out of the MSR input data. +**------------------------------------------------------------------------- +** char *EL734_EncodeSS (&text, text_len, ss) +** -------------- +** Encode the SS flags into text. +** Input Args: +** int text_len - The size of text. +** int ss - The value of SS register. +** Output Args: +** char *text - The resulting text string is stored here. +** Modified Args: +** none +** Return status: +** A pointer to "text". +** Routines called: +** none +** Description: +** The routine makes an intelligible message out of the input SS data. +**------------------------------------------------------------------------- +** void EL734_ErrInfo (&entry_txt_ptr, &errcode, &my_errno, &vaxc_errno) +** ------------- +** Return detailed status from last operation. +** Input Args: +** None +** Output Args: +** char **entry_txt_ptr - Pointer to a text string giving the call stack +** at the time that the error was detected. +** int *errcode - An internal error code indicating the detected error. +** int *my_errno - Saved value of errno. +** int *vaxc_errno - Saved value of vaxc$errno (OpenVMS only). +** Modified Args: +** none +** Return status: +** none +** Routines called: +** none +** Description: +** Returns detailed status of the last operation. Once an error has been +** detected, the error status is frozen until this routine has been called. +**------------------------------------------------------------------------- +** int EL734_GetAirCush (&handle, &present, &state) +** ---------------- +** Get W and AC register values. +** Input Args: +** void **handle - The pointer to the structure returned by EL734_Open. +** Output Args: +** int *present - The W register. If non-zero, motor has an air-cushion. +** int *state - The AC register. If non-zero, air-cushion is up. +** Description: +** The routine is the same as EL734_GetEncGearing except that it issues +** a "W" command and then an "AC" command instead of an "FD" command to +** the controller. +**------------------------------------------------------------------------- +** int EL734_GetEncGearing (&handle, &numerator, &denominator) +** ------------------- +** Get FD register values. +** Input Args: +** void **handle - The pointer to the structure returned by EL734_Open. +** Output Args: +** int *numerator - The encoder gearing numerator. +** int *denominator - The encoder gearing denominator. +** Modified Args: +** none +** Return status: +** True if no problems detected, otherwise False and EL734_ErrInfo +** can be called to identify the problem. Values of Errcode set by +** EL734_GetEncGearing are (other values may be set by the called +** routines): +** EL734__BAD_TMO, BAD_LOC, BAD_CMD, BAD_OFL, +** BAD_ADR, EMERG_STOP --> see EL734_Open. +** EL734__BAD_ILLG --> the response was probably not 2 integers. +** This could happen if there is noise on the +** RS232C connection to the EL734. +** If an error is detected, *numerator and *denominator are set to 0. +** Routines called: +** EL734_AddCallStack, AsynSrv_SendCmnds, AsynSrv_GetReply +** Description: +** The routine issues an "FD" command to the controller and analyses +** the result. The two parameters of the "FD" command are the numerator +** and denominator respectively. +**------------------------------------------------------------------------- +** int EL734_GetId (&handle, &id_txt, id_len) +** ----------- +** Get ID register value. +** Input Args: +** void **handle - The pointer to the structure returned by EL734_Open. +** int id_len - The size of the buffer in bytes. +** Output Args: +** char *id_txt - The EL734 identifier ("ID" parameter). +** Description: +** The routine is the same as EL734_GetEncGearing except that it issues +** an "ID" command instead of an "H" command to the controller. +**------------------------------------------------------------------------- +** int EL734_GetLimits (&handle, &lo, &hi) +** --------------- +** Get H register values. +** Input Args: +** void **handle - The pointer to the structure returned by EL734_Open. +** Output Args: +** float *lo - The lower software limit. +** float *hi - The higher software limit. +** Description: +** The routine is the same as EL734_GetEncGearing except that it issues +** an "H" command instead of an "FD" command to the controller. +**------------------------------------------------------------------------- +** int EL734_GetMotorGearing (&handle, &numerator, &denominator) +** --------------------- +** Get FM register values. +** Input Args: +** void **handle - The pointer to the structure returned by EL734_Open. +** Output Args: +** int *numerator - The motor gearing numerator. +** int *denominator - The motor gearing denominator. +** Description: +** The routine is the same as EL734_GetEncGearing except that it issues +** an "FM" command instead of an "FD" command to the controller. The +** two parameters of the "FM" command are the numerator and denominator +** respectively. +**------------------------------------------------------------------------- +** int EL734_GetNullPoint (&handle, &null_pt) +** ------------------ +** Get V register value. +** Input Args: +** void **handle - The pointer to the structure returned by EL734_Open. +** Output Args: +** int *null_pt - The null point ("V" parameter) of the EL734. +** Description: +** The routine is the same as EL734_GetEncGearing except that it issues +** a "V" command instead of an "FD" command to the controller. +**------------------------------------------------------------------------- +** int EL734_GetPosition (&handle, &ist_posit) +** ----------------- +** Get U register value = current position. +** Input Args: +** void **handle - The pointer to the structure returned by EL734_Open. +** Output Args: +** float *ist_posit - The current position (U command) of the motor. +** Description: +** The routine is the same as EL734_GetEncGearing except that it issues +** a "U" command instead of an "FD" command to the controller. +**------------------------------------------------------------------------- +** int EL734_GetPrecision (&handle, &n_dec) +** ------------------ +** Get A register value. +** Input Args: +** void **handle - The pointer to the structure returned by EL734_Open. +** Output Args: +** int *n_dec - The precision ("A" parameter) of the EL734. +** Description: +** The routine is the same as EL734_GetEncGearing except that it issues +** a "A" command instead of an "FD" command to the controller. +**------------------------------------------------------------------------- +** int EL734_GetRefMode (&handle, &mode) +** ---------------- +** Get K register value. +** Input Args: +** void **handle - The pointer to the structure returned by EL734_Open. +** Output Args: +** int *mode - The reference seek mode ("K" parameter) of the EL734. +** Description: +** The routine is the same as EL734_GetEncGearing except that it issues +** a "K" command instead of an "FD" command to the controller. +**------------------------------------------------------------------------- +** int EL734_GetRefParam (&handle, ¶m) +** ----------------- +** Get Q register value. +** Input Args: +** void **handle - The pointer to the structure returned by EL734_Open. +** Output Args: +** float *param - The reference seek param ("Q" parameter) of the EL734. +** Description: +** The routine is the same as EL734_GetEncGearing except that it issues +** a "Q" command instead of an "FD" command to the controller. +**------------------------------------------------------------------------- +** int EL734_GetSpeeds (&handle, &lo, &hi, &ramp) +** --------------- +** Get G, J and E register values. +** Input Args: +** void **handle - The pointer to the structure returned by EL734_Open. +** Output Args: +** int *lo - The start/stop speed (G register). Units = Steps/sec. +** int *hi - The maximum speed (J register). Units = Steps/sec. +** int *ramp - The start/stop ramp (E register). Units = kHz/sec. +** Description: +** The routine is the same as EL734_GetEncGearing except that it issues +** a "G", "J" and "E" commands instead of an "FD" command to the +** controller. +**------------------------------------------------------------------------- +** int EL734_GetStatus (&handle, &msr, &ored_msr, &fp_cntr, &fr_cntr, +** --------------- &ss, &ist_posit) +** Get MSR/SS/U register values. +** Input Args: +** void **handle - The pointer to the structure returned by EL734_Open. +** Output Args: +** int *msr - The MSR register. +** int *ored_msr - The 'ored'-MSR register. This gets zeroed every time +** a 'positioning' command is executed. +** int *fp_cntr - A counter of the 'Position Faults' (*FP). This gets +** zeroed whenever ored_msr is zeroed. +** int *fr_cntr - A counter of the 'Run Faults' (*FR). This gets +** zeroed whenever ored_msr is zeroed. +** int *ss - The SS register. This will be -1 if the motor is busy. +** float *ist_posit - The current position (U command) of the motor. +** Modified Args: +** none +** Return status: +** True if no problems detected, otherwise False and EL734_ErrInfo +** can be called to identify the problem. Values of Errcode set by +** EL734_GetStatus are (other values may be set by the called routines): +** EL734__BAD_TMO, BAD_LOC, BAD_CMD, BAD_OFL, +** BAD_ADR, EMERG_STOP --> see EL734_Open. +** EL734__BAD_ILLG --> one of the responses could probably not +** be decoded. This could happen if there is noise +** on the RS232C connection to the EL734. +** If an error is detected, ist_posit is set to 0.0 and all other +** arguments to -1. +** Routines called: +** EL734_AddCallStack, AsynSrv_SendCmnds, AsynSrv_GetReply +** Description: +** The routine issues an "MSR", "SS" and "U" command to the controller and +** analyses the result. A count is kept of each time the *FP and *FR bits +** are found to be set and an inclusive-or value of MSR is maintained. +**------------------------------------------------------------------------- +** int EL734_GetZeroPoint (&handle, &zero_pt) +** ------------------ +** Get zero-point of motor. +** Input Args: +** void **handle - The pointer to the structure returned by EL734_Open. +** Output Args: +** float *zero_pt - The zero point of the EL734. +** Return status: +** Any of the errors generated by the called routines is possible plus: +** EL734__BAD_OVFL --> The encoder gearing ratio is zero so +** the conversion would overflow. +** Routines called: +** EL734_AddCallStack, EL734_GetEncGearing, EL734_GetNullPoint +** Description: +** This routine returns the zero point of the motor in the same units +** as used by the "P" and "U" commands. In other words, it reads the +** "V" parameter and converts it from "encoder-step" units to physical +** units using the encoder-gearing parameters. +**------------------------------------------------------------------------- +** int EL734_MoveNoWait (&handle, soll_posit) +** ---------------- +** Move motor and don't wait for completion. +** Input Args: +** void **handle - The pntr to the structure returned by EL734_Open. +** float soll_posit - The position to which the motor should move. +** Output Args: +** none +** Modified Args: +** none +** Return status: +** True if no problems detected, otherwise False and EL734_ErrInfo +** can be called to identify the problem. Values of Errcode set by +** EL734_MoveNoWait are (other values may be set by the called routines): +** EL734__BAD_TMO, BAD_LOC, BAD_CMD, BAD_OFL, +** BAD_ADR, EMERG_STOP --> see EL734_Open. +** EL734__BAD_RNG --> Destination is out-of-range. +** EL734__BAD_STP --> Motor is disabled via Hardware "Stop" +** signal. +** EL734__BAD_ILLG --> some other response obtained from EL734. +** This could happen if there is noise +** on the RS232C connection to the EL734. +** Routines called: +** EL734_AddCallStack, AsynSrv_SendCmnds, AsynSrv_GetReply +** Description: +** The appropriate "P" command is sent to the motor and the response +** checked to check that it has been accepted. The fields "ored_msr", +** "fp_cntr" and "fr_cntr" in the handle are cleared, if so. +**------------------------------------------------------------------------- +** int EL734_MoveWait (&handle, soll_posit, &ored_msr, &fp_cntr, &fr_cntr, +** -------------- &ist_posit) +** Move motor and wait for completion. +** Input Args: +** void **handle - The pntr to the structure returned by EL734_Open. +** float soll_posit - The position to which the motor should move. +** Output Args: +** int *ored_msr \ +** int *fp_cntr \ Same as EL734_WaitIdle. +** int *fr_cntr / +** float *ist_posit / +** none +** Modified Args: +** none +** Return status: +** True if no problems detected, otherwise False. If False, errcode (see +** EL734_ErrInfo) will have been set by EL734_MoveNoWait or EL734_WaitIdle. +** Routines called: +** EL734_AddCallStack, EL734_MoveNoWait, EL734_WaitIdle +** Description: +** The routine calls EL734_MoveNoWait and, if successful, EL734_WaitIdle. +**------------------------------------------------------------------------- +** int EL734_Open (&handle, host, port, chan, motor, id) +** ---------- +** Open a connection to a motor. +** Input Args: +** char *host - Name of host offering the TCP/IP service. +** int port - Number of TCP/IP port of TCP/IP server. +** int chan - RS-232-C Channel number on the TCP/IP server. +** int motor - The motor to be driven. +** char *id - The expected ID of the device, normally "STPMC EL734". +** If id is NULL, the device ID is not checked. +** Output Args: +** void *handle - A pointer to a structure of type EL734info needed for +** subsequent calls to EL734_... routines. Buffer space +** for the structure is allocated dynamically. It gets +** released via a call to EL734_Close. +** Modified Args: +** none +** Return status: +** True if no problems detected, otherwise False. If False, EL734_ErrInfo +** can be called to identify the problem. Values of Errcode set by +** EL734_Open are (other values may be set by the called routines): +** EL734__BAD_TMO --> Time-out error ("?TMO" - this gets +** generated by the RS232C server). +** EL734__BAD_LOC --> EL734 off-line ("?LOC"). This should not +** happen on calls to EL734_Open since it +** sends an "RMT 1" cmnd. +** EL734__BAD_CMD --> Command error ("?CMD"). This could be +** caused by noise in the RS-232-C +** transmission. +** EL734__BAD_OFL --> Connection to EL734 broken ("?OFL"). +** This can get generated by RS232C_SRV +** if, for example, the connection is via +** a terminal server and the terminal +** server loses power. +** EL734__BAD_ILLG --> Some other unrecognised response. This +** should never occur, of course! +** EL734__BAD_SOCKET --> Call to "AsynSrv_Open" failed. +** EL734__BAD_DEV --> Device has wrong ID +** EL734__BAD_MALLOC --> Call to "malloc" failed +** EL734__BAD_ADR --> Bad motor address ("?ADR"). Probably +** a non-existent motor has been addressed. +** EL734__EMERG_STOP --> Emergency stop ("*ES") detected. +** Routines called: +** AsynSrv_Open, the memory alloc routine "malloc", StrJoin, +** EL734_Config, AsynSrv_SendCmnds, AsynSrv_GetReply, +** AsynSrv_Close (if an error is detected). +** Description: +** The routine calls AsynSrv_Open to open a TCP/IP connection to a server +** offering the "RS-232-C" service for an EL734 Motor Controller. "RMT 1" +** and "ECHO 0" commands are sent to ensure the device is on-line, an "ID" +** command is sent (only if 'id' is non-NULL) to ensure that an EL734 is +** being addressed and an "MSR " command is sent to ensure that +** the motor exists. +** Note: +** For all error status returns, there is no open connection to the server +** and the handle is set to zero. +**------------------------------------------------------------------------- +** int EL734_PutOffline (&handle) +** ---------------- +** Send "ECHO 1" and "RMT 0" commands to EL734 server. +** Input Args: +** void **handle - The pntr to the structure returned by EL734_Open. +** Output Args: +** none +** Modified Args: +** none +** Return status: +** True if no problems detected, otherwise False and errcode (see +** EL734_ErrInfo) is set to indicate the nature of the problem. +** Values of Errcode set by EL734_PutOffline are (other values may be set +** by the called routines): +** EL734__BAD_ASYNSRV --> An error occurred in AsynSrv_Utility. +** Call AsynSrv_ErrInfo for more info. +** EL734__BAD_ILLG --> an unrecognised response. This +** should never occur, of course! +** Routines called: +** EL734_AddCallStack, AsynSrv_SendCmnds, AsynSrv_GetReply +** Description: +** The routine calls AsynSrv_SendCmnds to execute "RMT 1", "ECHO 1" +** and "RMT 0" commands. The replies are checked. +**------------------------------------------------------------------------- +** int EL734_PutOnline (&handle, echo) +** --------------- +** Send "RMT 1" and "ECHO x" commands to EL734 server. +** Input Args: +** void **handle - The pntr to the structure returned by EL734_Open. +** int echo - The value for the ECHO command. +** Output Args: +** none +** Modified Args: +** none +** Return status: +** True if no problems detected, otherwise False and errcode (see +** EL734_ErrInfo) is set to indicate the nature of the problem. +** Values of Errcode set by EL734_PutOnline are (other values may be set +** by the called routines): +** EL734__BAD_PAR --> "echo" is not 0, 1 or 2. +** EL734__BAD_ASYNSRV --> An error occurred in AsynSrv_Utility. +** Call AsynSrv_ErrInfo for more info. +** EL734__BAD_ILLG --> an unrecognised response. This +** should never occur, of course! +** Routines called: +** EL734_AddCallStack, AsynSrv_SendCmnds, AsynSrv_GetReply +** Description: +** The routine calls AsynSrv_SendCmnds to execute "RMT 1" and "ECHO x" +** commands. The replies are checked. +**------------------------------------------------------------------------- +** int EL734_SendCmnd (&handle, &cmnd, &rply, rply_size) +** -------------- +** Send a command to RS232C server. +** Input Args: +** void **handle - The pntr to the structure returned by EL734_Open. +** char *cmnd - A command, terminated by NULL, for sending to the +** EL734 counter controller. The command must have +** any necessary \r character included. +** int rply_size - the size of the buffer. +** Output Args: +** char *rply - A buffer for receiving the reply. +** Modified Args: +** none +** Return status: +** True if no problems detected, otherwise False and errcode (see +** EL734_ErrInfo) is set to indicate the nature of the problem. +** Values of Errcode set by EL734_SendCmnd are (other values may be set +** by the called routines): +** EL734__BAD_ASYNSRV --> An error occurred in AsynSrv_Utility. +** Call AsynSrv_ErrInfo for more info. +** Routines called: +** EL734_AddCallStack, AsynSrv_SendCmnds, AsynSrv_GetReply +** Description: +** The command is passed to AsynSrv_SendCmnds and the reply extracted. +**------------------------------------------------------------------------- +** int EL734_SetAirCush (&handle, state) +** ---------------- +** Set AC register value. +** Input Args: +** void **handle - The pointer to the structure returned by EL734_Open. +** Input Args: +** int state - The new state of the AC register. 0 --> down +** non-zero --> up +** Output Args: +** none +** Modified Args: +** none +** Return status: +** True if no problems detected, otherwise False and EL734_ErrInfo +** can be called to identify the problem. Values of Errcode set by +** EL734_SetAirCush are (other values may be set by the called +** routines): +** EL734__BAD_TMO, BAD_LOC, BAD_CMD, BAD_OFL, +** BAD_ADR, EMERG_STOP --> see EL734_Open. +** EL734__VFY_ERR --> the value for the AC register returned by the +** call to EL734_GetAirCush was not the value which +** was sent via the AC command. This could happen +** if there is noise on the RS232C connection to +** the EL734. +** Routines called: +** EL734_AddCallStack, AsynSrv_SendCmnds, AsynSrv_GetReply, +** EL734_GetAirCush +** Description: +** The routine issues an "AC" command to the controller to set the air- +** cushions of the motor up or down. It then calls EL734_GetAirCush +** to check that the air-cushions were set correctly. +**------------------------------------------------------------------------- +** int EL734_SetErrcode (&info_ptr, &response, &cmnd) +** ---------------- +** Set up EL734_errcode (for internal use only) +** Input Args: +** struct EL734info *info_ptr - The pntr to the structure returned by +** EL734_Open. +** char *response - The response received from a command. +** char *cmnd - The command which was sent. +** Output Args: +** none +** Modified Args: +** none +** Return status: +** The value of EL734_errcode. +** Routines called: +** AsynSrv_SendCmnds +** Description: +** The command checks *response for certain keywords. If not recognised, +** extra action is undertaken to try to see if the emergency stop state +** is active or not. +**------------------------------------------------------------------------- +** int EL734_SetHighSpeed (&handle, hi) +** ------------------ +** Set J register value. +** Input Args: +** void **handle - The pointer to the structure returned by EL734_Open. +** Input Args: +** int hi - The maximum speed (J register). Units = Steps/sec. +** Output Args: +** none +** Modified Args: +** none +** Return status: +** True if no problems detected, otherwise False and EL734_ErrInfo +** can be called to identify the problem. Values of Errcode set by +** EL734_SetHighSpeed are (other values may be set by the called +** routines): +** EL734__BAD_TMO, BAD_LOC, BAD_CMD, BAD_OFL, +** BAD_ADR, EMERG_STOP --> see EL734_Open. +** EL734__VFY_ERR --> the value for the J register returned by the call +** to EL734_GetSpeeds was not the value which was +** sent via the J command. This could happen if +** there is noise on the RS232C connection to +** the EL734. +** Routines called: +** EL734_AddCallStack, AsynSrv_SendCmnds, AsynSrv_GetReply, EL734_GetSpeeds +** Description: +** The routine issues a "J" command to the controller to set the max speed +** of the motor. It then calls EL734_GetSpeeds to check that the speed +** was set correctly. +**------------------------------------------------------------------------- +** int EL734_SetLowSpeed (&handle, hi) +** ----------------- +** Set G register value. +** Input Args: +** void **handle - The pointer to the structure returned by EL734_Open. +** Input Args: +** int lo - The start/stop speed (G register). Units = Steps/sec. +** Description: +** The routine is identical to the EL734_SetHighSpeed routine except that +** a "G" command rather than a "J" command is issued to the controller to +** set the start/stop speed. +**------------------------------------------------------------------------- +** int EL734_SetRamp (&handle, ramp) +** ------------- +** Set E register value. +** Input Args: +** void **handle - The pointer to the structure returned by EL734_Open. +** Input Args: +** int ramp - The start/stop ramp (E register). Units = kHz/sec. +** Description: +** The routine is identical to the EL734_SetHighSpeed routine except that +** an "E" command rather than a "J" command is issued to the controller to +** set the start/stop ramp. +**------------------------------------------------------------------------- +** int EL734_Stop (&handle) +** ---------- +** Send a stop command to motor +** Input Args: +** void **handle - The pointer to the structure returned by EL734_Open. +** Output Args: +** None +** Description: +** The routine is similar to EL734_GetEncGearing except that it issues +** a "Q m" command instead of an "FD" command to the controller and +** a null response (rather than parameter values) is expected. +**------------------------------------------------------------------------- +** int EL734_WaitIdle (&handle, &ored_msr, &fp_cntr, &fr_cntr, &ist_posit) +** -------------- +** Wait till MSR goes to zero. +** Input Args: +** void **handle - The pntr to the structure returned by EL734_Open. +** Output Args: +** int *ored_msr \ +** int *fp_cntr \ Same as EL734_GetStatus. +** int *fr_cntr / +** float *ist_posit / +** none +** Modified Args: +** none +** Return status: +** True if no problems detected, otherwise False and Errcode (see +** EL734_ErrInfo) will have been set by the called routines to indicate +** the nature of the problem. +** Routines called: +** EL734_AddCallStack, EL734_GetStatus +** Description: +** Routine EL734_GetStatus is called repeatedly at a predefined frequency +** until the MSR__BUSY bit in the MSR register is zero. +**------------------------------------------------------------------------- +** void EL734_ZeroStatus (&handle) +** ----------------- +** Zero the "ored-MSR" and fault counters. +** Input Args: +** void **handle - The pntr to the structure returned by EL734_Open. +** Output Args: +** none +** Modified Args: +** none +** Return status: +** none +** Routines called: +** none +** Description: +** The "ored-MSR" and fault counters in the handle are zeroed. +**============================================================================*/ +/* +**--------------------------------------------------------------------------- +** Global Definitions +*/ +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#ifdef __VMS +#include +#else +#include +#ifdef FORTIFY +#include +#endif +#endif +/*-----------------------------------------------------------------*/ +#include +#include +#include +#include + +#define True 1 +#define False 0 +/*-------------------------------------------------------------------------- +** Global Variables +*/ + static int EL734_call_depth = 0; + static char EL734_routine[5][64]; + static int EL734_errcode = 0; + static int EL734_errno, EL734_vaxc_errno; + char EL734_IllgText[256]; +/* +**--------------------------------------------------------------------------- +** EL734_AddCallStack: Add a routine name to the call stack. +** This allows EL734_ErrInfo to generate a +** trace-back in case of error. +*/ + int EL734_AddCallStack ( +/* ================== +*/ struct EL734info *pntr, + char *name) { + + if (EL734_errcode != 0) return False; + + if (EL734_call_depth < 5) { + strcpy (EL734_routine[EL734_call_depth], name); + EL734_call_depth++; + } + + if (pntr == NULL) {EL734_errcode = EL734__NOT_OPEN; return False;} + + if (pntr->asyn_info.skt <= 0) { + memset (pntr->from_host.msg_size, + '0', sizeof (pntr->from_host.msg_size)); + EL734_errcode = (pntr->asyn_info.skt < 0) ? EL734__FORCED_CLOSED + : EL734__NO_SOCKET; + return False; + } + return True; + } +/* +**--------------------------------------------------------------------------- +** EL734_Close: Close a connection to a motor. +*/ + int EL734_Close ( +/* =========== +*/ void **handle, + int force_flag) { + + struct EL734info *info_ptr; + char buff[4]; + + info_ptr = (struct EL734info *) *handle; + if (info_ptr == NULL) return True; + + if (info_ptr->asyn_info.skt != 0) { + if (info_ptr->asyn_info.skt > 0) { + AsynSrv_Close (*handle, force_flag); + } + } + free (*handle); + *handle = NULL; + + return True; + } +/* +**--------------------------------------------------------------------------- +** EL734_Config: Configure a connection to a motor. +*/ + int EL734_Config ( +/* ============ +*/ void **handle, + ...) { + + char buff[16]; + va_list ap; /* Pointer to variable args */ + char *txt_ptr; + int intval; + struct EL734info *info_ptr; + /*---------------------------------------------- + */ + info_ptr = (struct EL734info *) *handle; + + if (!EL734_AddCallStack (info_ptr, "EL734_Config")) return False; + /*---------------------------------------------- + */ + va_start (ap, handle); /* Set up var arg machinery */ + txt_ptr = va_arg (ap, char *); /* Get pntr to first parameter ident */ + while (txt_ptr != NULL) { + if (strcmp (txt_ptr, "msecTmo") == 0) { + intval = va_arg (ap, int); + if ((intval < 100) || (intval > 999999)) { + EL734_errcode = EL734__BAD_PAR; + return False; + } + sprintf (buff, "%04d", intval/100); /* Convert to ASCII as .. + ** .. deci-secs */ + memcpy (info_ptr->asyn_info.tmo, buff, 4); + }else if (strcmp (txt_ptr, "eot") == 0) { + txt_ptr = va_arg (ap, char *); + if (txt_ptr == NULL) { + EL734_errcode = EL734__BAD_PAR; + return False; + } + memcpy (info_ptr->asyn_info.eot, "\0\0\0\0", 4); + switch (txt_ptr[0]) { + case '3': info_ptr->asyn_info.eot[3] = txt_ptr[3]; + case '2': info_ptr->asyn_info.eot[2] = txt_ptr[2]; + case '1': info_ptr->asyn_info.eot[1] = txt_ptr[1]; + case '0': + info_ptr->asyn_info.eot[0] = txt_ptr[0]; + break; + default: + EL734_errcode = EL734__BAD_PAR; + return False; + } + }else if (strcmp (txt_ptr, "motor") == 0) { + intval = va_arg (ap, int); + if ((intval < 1) || (intval > 12)) { + EL734_errcode = EL734__BAD_PAR; + return False; + } + info_ptr->motor = intval; + }else if (strcmp (txt_ptr, "chan") == 0) { + intval = va_arg (ap, int); + if ((intval < 0) || (intval > 255)) { + EL734_errcode = EL734__BAD_PAR; + return False; + } + info_ptr->asyn_info.chan = intval; + sprintf (buff, "%04d", intval); /* Convert to ASCII */ + memcpy (info_ptr->asyn_info.chan_char, buff, 4); + }else { + EL734_errcode = EL734__BAD_PAR; + return False; + } + txt_ptr = va_arg (ap, char *); /* Get pntr to next parameter ident */ + } + + if (EL734_errcode != 0) return False; + EL734_call_depth--; + return True; + } +/* +**--------------------------------------------------------------------------- +** EL734_EncodeMSR: Encode the MSR status into text. +*/ + char *EL734_EncodeMSR (char *text, int text_len, +/* =============== +*/ int msr, + int ored_msr, + int fp_cntr, + int fr_cntr) { + int len; + char my_text[132]; + char my_text_0[32]; + + if (msr == 0) { + ored_msr = ored_msr & ~(MSR__BUSY); /* Zero "Busy" bit */ + if (ored_msr == MSR__OK) { + StrJoin (text, text_len, "Status, MSR = Idle. Positioned OK.", ""); + }else { + if ((ored_msr & MSR__OK) != 0) { + StrJoin (text, text_len, "Status, MSR = Idle. Positioned OK. ", ""); + }else { + StrJoin (text, text_len, "Status, MSR = Idle. ", ""); + } + if ((ored_msr & MSR__REF_OK) != 0) { + StrJoin (my_text, sizeof (my_text), text, ""); + StrJoin (text, text_len, my_text, "Ref. Pos'n OK. "); + } + if ((ored_msr & MSR__LIM_ERR) != 0) { + StrJoin (my_text, sizeof (my_text), text, ""); + StrJoin (text, text_len, my_text, "Limit Switch Problem. "); + } + if ((ored_msr & MSR__AC_FAIL) != 0) { + StrJoin (my_text, sizeof (my_text), text, ""); + StrJoin (text, text_len, my_text, "Air-Cushion Error. "); + } + if ((ored_msr & MSR__REF_FAIL) != 0) { + StrJoin (my_text, sizeof (my_text), text, ""); + StrJoin (text, text_len, my_text, "Ref. Pos'n Fail. "); + } + if ((ored_msr & MSR__POS_FAIL) != 0) { + StrJoin (my_text, sizeof (my_text), text, ""); + StrJoin (text, text_len, my_text, "Pos'n Fail. "); + } + if ((ored_msr & MSR__POS_FAULT) != 0) { + StrJoin (my_text, sizeof (my_text), text, ""); + if (fp_cntr == 1) { + StrJoin (text, text_len, my_text, "1 Pos'n Fault. "); + }else { + sprintf (my_text_0, "%d Pos'n Faults. ", fp_cntr); + StrJoin (text, text_len, my_text, my_text_0); + } + } + if ((ored_msr & MSR__RUN_FAIL) != 0) { + StrJoin (my_text, sizeof (my_text), text, ""); + StrJoin (text, text_len, my_text, "Run Fail. "); + } + if ((ored_msr & MSR__RUN_FAULT) != 0) { + StrJoin (my_text, sizeof (my_text), text, ""); + if (fr_cntr == 1) { + StrJoin (text, text_len, my_text, "1 Run Fault. "); + }else { + sprintf (my_text_0, "%d Run Faults. ", fr_cntr); + StrJoin (text, text_len, my_text, my_text_0); + } + } + if ((ored_msr & MSR__HALT) != 0) { + StrJoin (my_text, sizeof (my_text), text, ""); + StrJoin (text, text_len, my_text, "Halt. "); + } + if ((ored_msr & MSR__HI_LIM) != 0) { + StrJoin (my_text, sizeof (my_text), text, ""); + StrJoin (text, text_len, my_text, "Hit HiLim. "); + } + if ((ored_msr & MSR__LO_LIM) != 0) { + StrJoin (my_text, sizeof (my_text), text, ""); + StrJoin (text, text_len, my_text, "Hit LoLim. "); + } + if ((ored_msr & MSR__STOPPED) != 0) { + StrJoin (my_text, sizeof (my_text), text, ""); + StrJoin (text, text_len, my_text, "Stopped. "); + } + } + }else if ((msr & ~(0x2fff)) != 0) { + StrJoin (text, text_len, "Status, MSR = ??", ""); + }else { + sprintf (my_text, "%#x ", msr); + StrJoin (text, text_len, "Status, MSR = ", my_text); + if ((msr & MSR__LIM_ERR) != 0) { + StrJoin (my_text, sizeof (my_text), text, ""); + StrJoin (text, text_len, my_text, "Limit Switch Problem/"); + } + if ((msr & MSR__AC_FAIL) != 0) { + StrJoin (my_text, sizeof (my_text), text, ""); + StrJoin (text, text_len, my_text, "Air-Cushion Error/"); + } + if ((msr & MSR__REF_FAIL) != 0) { + StrJoin (my_text, sizeof (my_text), text, ""); + StrJoin (text, text_len, my_text, "Ref. Pos'n Fail/"); + } + if ((msr & MSR__POS_FAIL) != 0) { + StrJoin (my_text, sizeof (my_text), text, ""); + StrJoin (text, text_len, my_text, "Pos'n Fail/"); + } + if ((msr & MSR__POS_FAULT) != 0) { + StrJoin (my_text, sizeof (my_text), text, ""); + StrJoin (text, text_len, my_text, "Pos'n Fault/"); + } + if ((msr & MSR__RUN_FAIL) != 0) { + StrJoin (my_text, sizeof (my_text), text, ""); + StrJoin (text, text_len, my_text, "Run Fail/"); + } + if ((msr & MSR__RUN_FAULT) != 0) { + StrJoin (my_text, sizeof (my_text), text, ""); + StrJoin (text, text_len, my_text, "Run Fault/"); + } + if ((msr & MSR__HALT) != 0) { + StrJoin (my_text, sizeof (my_text), text, ""); + StrJoin (text, text_len, my_text, "Halt/"); + } + if ((msr & MSR__HI_LIM) != 0) { + StrJoin (my_text, sizeof (my_text), text, ""); + StrJoin (text, text_len, my_text, "Hit HiLim/"); + } + if ((msr & MSR__LO_LIM) != 0) { + StrJoin (my_text, sizeof (my_text), text, ""); + StrJoin (text, text_len, my_text, "Hit LoLim/"); + } + if ((msr & MSR__STOPPED) != 0) { + StrJoin (my_text, sizeof (my_text), text, ""); + StrJoin (text, text_len, my_text, "Stopped/"); + } + if ((msr & MSR__REF_OK) != 0) { + StrJoin (my_text, sizeof (my_text), text, ""); + StrJoin (text, text_len, my_text, "Ref. Pos'n OK/"); + } + if ((msr & MSR__OK) != 0) { + StrJoin (my_text, sizeof (my_text), text, ""); + StrJoin (text, text_len, my_text, "OK/"); + } + if ((msr & MSR__BUSY) != 0) { + StrJoin (my_text, sizeof (my_text), text, ""); + StrJoin (text, text_len, my_text, "Busy/"); + } + len = strlen (text); + text[len-1] = '\0'; + } + return text; + } +/* +**--------------------------------------------------------------------------- +** EL734_EncodeSS: Encode the SS flags into text. +*/ + char *EL734_EncodeSS (char *text, int text_len, int ss) { +/* ============== +*/ + int len; + char my_text[132]; + char my_text_0[32]; + + if (ss == 0) { + StrJoin (text, text_len, "Flags, SS = 0", ""); + }else if ((ss & ~(0x3f)) != 0) { + StrJoin (text, text_len, "Flags, SS = ??", ""); + }else { + sprintf (my_text, "Flags, SS = 0x%02X ", ss); + my_text_0[0] = '\0'; + if ((ss & 0x20) != 0) strcat (my_text_0, "LSX/"); + if ((ss & 0x10) != 0) strcat (my_text_0, "LS2/"); + if ((ss & 0x08) != 0) strcat (my_text_0, "LS1/"); + if ((ss & 0x04) != 0) strcat (my_text_0, "STP/"); + if ((ss & 0x02) != 0) strcat (my_text_0, "CCW/"); + if ((ss & 0x01) != 0) strcat (my_text_0, "HLT/"); + len = strlen (my_text_0); + my_text_0[len-1] = '\0'; + StrJoin (text, text_len, my_text, my_text_0); + } + return text; + } +/* +**------------------------------------------------------------------------- +** EL734_ErrInfo: Return detailed status from last operation. +*/ + void EL734_ErrInfo ( +/* ============= +*/ char **entry_txt, + int *errcode, + int *my_errno, + int *vaxc_errno) { + + int i; + char buff[80], *txt; + int asyn_errcode, asyn_errno, asyn_vaxerrno; + char* asyn_errtxt; + + if (EL734_call_depth <= 0) { + strcpy (EL734_routine[0], "EL734_no_error_detected"); + *errcode = 0; + *my_errno = 0; + *vaxc_errno = 0; + }else { + if (EL734_call_depth > 1) { /* Concatenate the names */ + for (i = 1; i < EL734_call_depth; i++) { + strcat (EL734_routine[0], "/"); + StrJoin (EL734_routine[0], sizeof (EL734_routine), + EL734_routine[0], EL734_routine[i]); + } + } + *errcode = EL734_errcode; + *my_errno = EL734_errno; + *vaxc_errno = EL734_vaxc_errno; + switch (EL734_errcode) { + case EL734__BAD_ADR: txt = "/EL734__BAD_ADR"; break; + case EL734__BAD_ASYNSRV: txt = "/EL734__BAD_ASYNSRV"; break; + case EL734__BAD_CMD: txt = "/EL734__BAD_CMD"; break; + case EL734__BAD_DEV: txt = "/EL734__BAD_DEV"; break; + case EL734__BAD_ILLG: txt = "/EL734__BAD_ILLG"; break; + case EL734__BAD_LOC: txt = "/EL734__BAD_LOC"; break; + case EL734__BAD_MALLOC: txt = "/EL734__BAD_MALLOC"; break; + case EL734__BAD_OFL: txt = "/EL734__BAD_OFL"; break; + case EL734__BAD_OVFL: txt = "/EL734__BAD_OVFL"; break; + case EL734__BAD_PAR: txt = "/EL734__BAD_PAR"; break; + case EL734__BAD_RNG: txt = "/EL734__BAD_RNG"; break; + case EL734__BAD_SOCKET: txt = "/EL734__BAD_SOCKET"; break; + case EL734__BAD_STP: txt = "/EL734__BAD_STP"; break; + case EL734__BAD_TMO: txt = "/EL734__BAD_TMO"; break; + case EL734__EMERG_STOP: txt = "/EL734__EMERG_STOP"; break; + case EL734__FORCED_CLOSED: txt = "/EL734__FORCED_CLOSED"; break; + case EL734__NOT_OPEN: txt = "/EL734__NOT_OPEN"; break; + case EL734__NO_SOCKET: txt = "/EL734__NO_SOCKET"; break; + default: + sprintf (buff, "/EL734__unknown_err_code: %d", EL734_errcode); + txt = buff; + } + StrJoin (EL734_routine[0], sizeof(EL734_routine), EL734_routine[0], txt); + } + AsynSrv_ErrInfo (&asyn_errtxt, &asyn_errcode, &asyn_errno, &asyn_vaxerrno); + if (asyn_errcode != 0) { + strcat (EL734_routine[0], "/"); + StrJoin (EL734_routine[0], sizeof(EL734_routine), + EL734_routine[0], asyn_errtxt); + } + *entry_txt = EL734_routine[0]; + EL734_call_depth = 0; + EL734_errcode = 0; + } +/* +**--------------------------------------------------------------------------- +** EL734_GetAirCush: Get W and AC register values. +*/ + int EL734_GetAirCush ( +/* ================ +*/ void **handle, + int *present, + int *state) { + + int status; + struct EL734info *info_ptr; + char cmnd0[10], cmnd1[10]; + char *rply_ptr, *rply_ptr0, *rply_ptr1; + /*---------------------------------------------- + */ + *present = *state = 0; + info_ptr = (struct EL734info *) *handle; + + if (!EL734_AddCallStack (info_ptr, "EL734_GetAirCush")) return False; + /*---------------------------------------------- + ** Send W and AC cmnds to EL734 + */ + sprintf (cmnd0, "w %d\r", info_ptr->motor); + sprintf (cmnd1, "ac %d\r", info_ptr->motor); + status = AsynSrv_SendCmnds (&info_ptr->asyn_info, + &info_ptr->to_host, &info_ptr->from_host, + cmnd0, cmnd1, NULL); + if (!status) { + *present = *state = 0; + EL734_errcode = EL734__BAD_ASYNSRV; + return False; + }else { + rply_ptr1 = NULL; + rply_ptr0 = AsynSrv_GetReply ( + &info_ptr->asyn_info, &info_ptr->from_host, NULL); + if (rply_ptr0 != NULL) rply_ptr1 = AsynSrv_GetReply ( + &info_ptr->asyn_info, &info_ptr->from_host, rply_ptr0); + if (rply_ptr0 == NULL) rply_ptr0 = "?no_response"; + if (rply_ptr1 == NULL) rply_ptr1 = "?no_response"; + + if ((sscanf (rply_ptr0, "%d", present) != 1) || + (sscanf (rply_ptr1, "%d", state) != 1)) { + if (*rply_ptr0 == '?') { + rply_ptr = rply_ptr0; + }else if (*rply_ptr1 == '?') { + rply_ptr = rply_ptr1; + }else { + rply_ptr = "?funny_response"; + } + *present = *state = 0; + EL734_SetErrcode (info_ptr, rply_ptr, "W\" or \"AC"); + return False; + } + } + if (EL734_errcode != 0) return False; + EL734_call_depth--; + return True; + } +/* +**--------------------------------------------------------------------------- +** EL734_GetEncGearing: Get FD register values. +*/ + int EL734_GetEncGearing ( +/* =================== +*/ void **handle, + int *nominator, + int *denominator) { + + int status; + struct EL734info *info_ptr; + char cmnd0[10]; + char *rply_ptr0; + /*---------------------------------------------- + */ + *nominator = *denominator = 0; + info_ptr = (struct EL734info *) *handle; + + if (!EL734_AddCallStack (info_ptr, "EL734_GetEncGearing")) return False; + /*---------------------------------------------- + ** Send FD cmnd to EL734 + */ + sprintf (cmnd0, "fd %d\r", info_ptr->motor); + status = AsynSrv_SendCmnds (&info_ptr->asyn_info, + &info_ptr->to_host, &info_ptr->from_host, + cmnd0, NULL); + if (!status) { + *nominator = *denominator = 0; + EL734_errcode = EL734__BAD_ASYNSRV; + return False; + }else { + rply_ptr0 = AsynSrv_GetReply ( + &info_ptr->asyn_info, &info_ptr->from_host, NULL); + if (rply_ptr0 == NULL) rply_ptr0 = "?no_response"; + if (sscanf (rply_ptr0, "%d %d", nominator, denominator) == 2) { + if (EL734_errcode != 0) return False; + EL734_call_depth--; + return True; + } + *nominator = *denominator = 0; + EL734_SetErrcode (info_ptr, rply_ptr0, "FD"); + return False; + } + } +/* +**--------------------------------------------------------------------------- +** EL734_GetId: Get ID register value. +*/ + int EL734_GetId ( +/* =========== +*/ void **handle, + char *id_txt, + int id_len) { + + int status; + struct EL734info *info_ptr; + char *rply_ptr0; + /*---------------------------------------------- + */ + *id_txt = '\0'; + info_ptr = (struct EL734info *) *handle; + + if (!EL734_AddCallStack (info_ptr, "EL734_GetId")) return False; + /*---------------------------------------------- + ** Send ID cmnd to EL734 + */ + status = AsynSrv_SendCmnds (&info_ptr->asyn_info, + &info_ptr->to_host, &info_ptr->from_host, + "id\r", NULL); + if (!status) { + EL734_errcode = EL734__BAD_ASYNSRV; + return False; + }else { + rply_ptr0 = AsynSrv_GetReply ( + &info_ptr->asyn_info, &info_ptr->from_host, NULL); + if ((rply_ptr0 != NULL) && + (*rply_ptr0 != '\0') && + (*rply_ptr0 != '?')) { + StrJoin (id_txt, id_len, rply_ptr0, ""); + if (EL734_errcode != 0) return False; + EL734_call_depth--; + return True; + } + if (rply_ptr0 == NULL) rply_ptr0 = "?no_response"; + EL734_SetErrcode (info_ptr, rply_ptr0, "ID"); + return False; + } + } +/* +**--------------------------------------------------------------------------- +** EL734_GetLimits: Get H register values. +*/ + int EL734_GetLimits ( +/* =============== +*/ void **handle, + float *lo, + float *hi) { + + int status; + struct EL734info *info_ptr; + char cmnd0[10]; + char *rply_ptr0; + /*---------------------------------------------- + */ + *lo = *hi = 0.0; + info_ptr = (struct EL734info *) *handle; + + if (!EL734_AddCallStack (info_ptr, "EL734_GetLimits")) return False; + /*---------------------------------------------- + ** Send H cmnd to EL734 + */ + sprintf (cmnd0, "h %d\r", info_ptr->motor); + status = AsynSrv_SendCmnds (&info_ptr->asyn_info, + &info_ptr->to_host, &info_ptr->from_host, + cmnd0, NULL); + if (!status) { + *lo = *hi = 0.0; + EL734_errcode = EL734__BAD_ASYNSRV; + return False; + }else { + rply_ptr0 = AsynSrv_GetReply ( + &info_ptr->asyn_info, &info_ptr->from_host, NULL); + if (rply_ptr0 == NULL) rply_ptr0 = "?no_response"; + if (sscanf (rply_ptr0, "%f %f", lo, hi) == 2) { + if (EL734_errcode != 0) return False; + EL734_call_depth--; + return True; + } + *lo = *hi = 0.0; + EL734_SetErrcode (info_ptr, rply_ptr0, "H"); + return False; + } + } +/* +**--------------------------------------------------------------------------- +** EL734_GetMotorGearing: Get FM register values. +*/ + int EL734_GetMotorGearing ( +/* ===================== +*/ void **handle, + int *nominator, + int *denominator) { + + int status; + struct EL734info *info_ptr; + char cmnd0[10]; + char *rply_ptr0; + /*---------------------------------------------- + */ + *nominator = *denominator = 0; + info_ptr = (struct EL734info *) *handle; + + if (!EL734_AddCallStack (info_ptr, "EL734_GetMotorGearing")) return False; + /*---------------------------------------------- + ** Send FM cmnd to EL734 + */ + sprintf (cmnd0, "fm %d\r", info_ptr->motor); + status = AsynSrv_SendCmnds (&info_ptr->asyn_info, + &info_ptr->to_host, &info_ptr->from_host, + cmnd0, NULL); + if (!status) { + *nominator = *denominator = 0; + EL734_errcode = EL734__BAD_ASYNSRV; + return False; + }else { + rply_ptr0 = AsynSrv_GetReply ( + &info_ptr->asyn_info, &info_ptr->from_host, NULL); + if (rply_ptr0 == NULL) rply_ptr0 = "?no_response"; + if (sscanf (rply_ptr0, "%d %d", nominator, denominator) == 2) { + if (EL734_errcode != 0) return False; + EL734_call_depth--; + return True; + } + *nominator = *denominator = 0; + EL734_SetErrcode (info_ptr, rply_ptr0, "FM"); + return False; + } + } +/* +**--------------------------------------------------------------------------- +** EL734_GetNullPoint: Get V register value. +*/ + int EL734_GetNullPoint ( +/* ================== +*/ void **handle, + int *null_pt) { + + int status; + struct EL734info *info_ptr; + char cmnd0[10]; + char *rply_ptr0; + /*---------------------------------------------- + */ + *null_pt = 0; + info_ptr = (struct EL734info *) *handle; + + if (!EL734_AddCallStack (info_ptr, "EL734_GetNullPoint")) return False; + /*---------------------------------------------- + ** Send V cmnd to EL734 + */ + sprintf (cmnd0, "v %d\r", info_ptr->motor); + status = AsynSrv_SendCmnds (&info_ptr->asyn_info, + &info_ptr->to_host, &info_ptr->from_host, + cmnd0, NULL); + if (!status) { + *null_pt = 0; + EL734_errcode = EL734__BAD_ASYNSRV; + return False; + }else { + rply_ptr0 = AsynSrv_GetReply ( + &info_ptr->asyn_info, &info_ptr->from_host, NULL); + if (rply_ptr0 == NULL) rply_ptr0 = "?no_response"; + if (sscanf (rply_ptr0, "%d", null_pt) == 1) { + if (EL734_errcode != 0) return False; + EL734_call_depth--; + return True; + } + *null_pt = 0; + EL734_SetErrcode (info_ptr, rply_ptr0, "V"); + return False; + } + } +/* +**--------------------------------------------------------------------------- +** EL734_GetPosition: Get U register value, the current position. +*/ + int EL734_GetPosition ( +/* ================= +*/ void **handle, + float *ist_posit) { + + int status; + struct EL734info *info_ptr; + char cmnd0[10]; + char *rply_ptr0; + /*---------------------------------------------- + */ + *ist_posit = 0.0; + info_ptr = (struct EL734info *) *handle; + + if (!EL734_AddCallStack (info_ptr, "EL734_GetPosition")) return False; + /*---------------------------------------------- + ** Send U cmnd to EL734 + */ + sprintf (cmnd0, "u %d\r", info_ptr->motor); + status = AsynSrv_SendCmnds (&info_ptr->asyn_info, + &info_ptr->to_host, &info_ptr->from_host, + cmnd0, NULL); + if (!status) { + *ist_posit = 0.0; + EL734_errcode = EL734__BAD_ASYNSRV; + return False; + }else { + rply_ptr0 = AsynSrv_GetReply ( + &info_ptr->asyn_info, &info_ptr->from_host, NULL); + if (rply_ptr0 == NULL) rply_ptr0 = "?no_response"; + if (sscanf (rply_ptr0, "%f", ist_posit) == 1) { + if (EL734_errcode != 0) return False; + EL734_call_depth--; + return True; + } + *ist_posit = 0.0; + EL734_SetErrcode (info_ptr, rply_ptr0, "U"); + return False; + } + } +/* +**--------------------------------------------------------------------------- +** EL734_GetPrecision: Get A register value. +*/ + int EL734_GetPrecision ( +/* ================== +*/ void **handle, + int *n_dec) { + + int status; + struct EL734info *info_ptr; + char cmnd0[10]; + char *rply_ptr0; + /*---------------------------------------------- + */ + *n_dec = 3; + info_ptr = (struct EL734info *) *handle; + + if (!EL734_AddCallStack (info_ptr, "EL734_GetPrecision")) return False; + /*---------------------------------------------- + ** Send A cmnd to EL734 + */ + sprintf (cmnd0, "a %d\r", info_ptr->motor); + status = AsynSrv_SendCmnds (&info_ptr->asyn_info, + &info_ptr->to_host, &info_ptr->from_host, + cmnd0, NULL); + if (!status) { + *n_dec = 3; + EL734_errcode = EL734__BAD_ASYNSRV; + return False; + }else { + rply_ptr0 = AsynSrv_GetReply ( + &info_ptr->asyn_info, &info_ptr->from_host, NULL); + if (rply_ptr0 == NULL) rply_ptr0 = "?no_response"; + if (sscanf (rply_ptr0, "%d", n_dec) == 1) { + if (EL734_errcode != 0) return False; + EL734_call_depth--; + return True; + } + *n_dec = 3; + EL734_SetErrcode (info_ptr, rply_ptr0, "A"); + return False; + } + } +/* +**--------------------------------------------------------------------------- +** EL734_GetRefMode: Get K register value. +*/ + int EL734_GetRefMode ( +/* ================ +*/ void **handle, + int *mode) { + + int status; + struct EL734info *info_ptr; + char cmnd0[10]; + char *rply_ptr0; + /*---------------------------------------------- + */ + *mode = 0; + info_ptr = (struct EL734info *) *handle; + + if (!EL734_AddCallStack (info_ptr, "EL734_GetRefMode")) return False; + /*---------------------------------------------- + ** Send K cmnd to EL734 + */ + sprintf (cmnd0, "k %d\r", info_ptr->motor); + status = AsynSrv_SendCmnds (&info_ptr->asyn_info, + &info_ptr->to_host, &info_ptr->from_host, + cmnd0, NULL); + if (!status) { + *mode = 0; + EL734_errcode = EL734__BAD_ASYNSRV; + return False; + }else { + rply_ptr0 = AsynSrv_GetReply ( + &info_ptr->asyn_info, &info_ptr->from_host, NULL); + if (rply_ptr0 == NULL) rply_ptr0 = "?no_response"; + if (sscanf (rply_ptr0, "%d", mode) == 1) { + if (EL734_errcode != 0) return False; + EL734_call_depth--; + return True; + } + *mode = 0; + EL734_SetErrcode (info_ptr, rply_ptr0, "K"); + return False; + } + } +/* +**--------------------------------------------------------------------------- +** EL734_GetRefParam: Get Q register value. +*/ + int EL734_GetRefParam ( +/* ================= +*/ void **handle, + float *param) { + + int status; + struct EL734info *info_ptr; + char cmnd0[10]; + char *rply_ptr0; + /*---------------------------------------------- + */ + *param = 0.0; + info_ptr = (struct EL734info *) *handle; + + if (!EL734_AddCallStack (info_ptr, "EL734_GetRefParam")) return False; + /*---------------------------------------------- + ** Send Q cmnd to EL734 + */ + sprintf (cmnd0, "q %d\r", info_ptr->motor); + status = AsynSrv_SendCmnds (&info_ptr->asyn_info, + &info_ptr->to_host, &info_ptr->from_host, + cmnd0, NULL); + if (!status) { + *param = 0.0; + EL734_errcode = EL734__BAD_ASYNSRV; + return False; + }else { + rply_ptr0 = AsynSrv_GetReply ( + &info_ptr->asyn_info, &info_ptr->from_host, NULL); + if (rply_ptr0 == NULL) rply_ptr0 = "?no_response"; + if (sscanf (rply_ptr0, "%f", param) == 1) { + if (EL734_errcode != 0) return False; + EL734_call_depth--; + return True; + } + *param = 0.0; + EL734_SetErrcode (info_ptr, rply_ptr0, "Q"); + return False; + } + } +/* +**--------------------------------------------------------------------------- +** EL734_GetSpeeds: Get G/J/E register values. +*/ + int EL734_GetSpeeds ( +/* =============== +*/ void **handle, + int *lo, + int *hi, + int *ramp) { + + int status; + struct EL734info *info_ptr; + char cmnd0[10]; + char cmnd1[10]; + char cmnd2[10]; + char *rply_ptr; + char *rply_ptr0; + char *rply_ptr1; + char *rply_ptr2; + /*---------------------------------------------- + */ + *lo = *hi = *ramp = 0; + info_ptr = (struct EL734info *) *handle; + + if (!EL734_AddCallStack (info_ptr, "EL734_GetSpeeds")) return False; + /*---------------------------------------------- + ** Send G, J and E cmnds to EL734 + */ + sprintf (cmnd0, "g %d\r", info_ptr->motor); + sprintf (cmnd1, "j %d\r", info_ptr->motor); + sprintf (cmnd2, "e %d\r", info_ptr->motor); + + status = AsynSrv_SendCmnds (&info_ptr->asyn_info, + &info_ptr->to_host, &info_ptr->from_host, + cmnd0, cmnd1, cmnd2, NULL); + if (!status) { + *lo = *hi = *ramp = 0; + EL734_errcode = EL734__BAD_ASYNSRV; + return False; + }else { + rply_ptr1 = rply_ptr2 = NULL; + rply_ptr0 = AsynSrv_GetReply ( + &info_ptr->asyn_info, &info_ptr->from_host, NULL); + if (rply_ptr0 != NULL) rply_ptr1 = AsynSrv_GetReply ( + &info_ptr->asyn_info, &info_ptr->from_host, rply_ptr0); + if (rply_ptr1 != NULL) rply_ptr2 = AsynSrv_GetReply ( + &info_ptr->asyn_info, &info_ptr->from_host, rply_ptr1); + + if (rply_ptr0 == NULL) rply_ptr0 = "?no_response"; + if (rply_ptr1 == NULL) rply_ptr1 = "?no_response"; + if (rply_ptr2 == NULL) rply_ptr2 = "?no_response"; + + if ((sscanf (rply_ptr0, "%d", lo) != 1) || + (sscanf (rply_ptr1, "%d", hi) != 1) || + (sscanf (rply_ptr2, "%d", ramp) != 1)) { + if (*rply_ptr0 == '?') { + rply_ptr = rply_ptr0; + }else if (*rply_ptr1 == '?') { + rply_ptr = rply_ptr1; + }else if (*rply_ptr2 == '?') { + rply_ptr = rply_ptr2; + }else { + rply_ptr = "?funny_response"; + } + *lo = *hi = *ramp = 0; + EL734_SetErrcode (info_ptr, rply_ptr, "G\", \"J\" or \"E"); + return False; + } + } + if (EL734_errcode != 0) return False; + EL734_call_depth--; + return True; + } +/* +**--------------------------------------------------------------------------- +** EL734_GetStatus: Get MSR/SS/U register values. +*/ + int EL734_GetStatus ( +/* =============== +*/ void **handle, + int *msr, + int *ored_msr, + int *fp_cntr, + int *fr_cntr, + int *ss, + float *ist_posit) { + + int status; + struct EL734info *info_ptr; + char cmnd0[10]; + char cmnd1[10]; + char cmnd2[10]; + char *rply_ptr; + char *rply_ptr0; + char *rply_ptr1; + char *rply_ptr2; + /*---------------------------------------------- + */ + *msr = *ored_msr = *fp_cntr = *fr_cntr = *ss = -1; *ist_posit = 0.0; + info_ptr = (struct EL734info *) *handle; + + if (!EL734_AddCallStack (info_ptr, "EL734_GetStatus")) return False; + /*---------------------------------------------- + ** Send MSR, SS and U cmnds to EL734 + */ + sprintf (cmnd0, "msr %d\r", info_ptr->motor); + sprintf (cmnd1, "ss %d\r", info_ptr->motor); + sprintf (cmnd2, "u %d\r", info_ptr->motor); + + status = AsynSrv_SendCmnds (&info_ptr->asyn_info, + &info_ptr->to_host, &info_ptr->from_host, + cmnd0, cmnd1, cmnd2, NULL); + if (!status) { + *msr = *ored_msr = *fp_cntr = *fr_cntr = *ss = -1; *ist_posit = 0.0; + EL734_errcode = EL734__BAD_ASYNSRV; + return False; + }else { + rply_ptr1 = rply_ptr2 = NULL; + rply_ptr0 = AsynSrv_GetReply ( + &info_ptr->asyn_info, &info_ptr->from_host, NULL); + if (rply_ptr0 != NULL) rply_ptr1 = AsynSrv_GetReply ( + &info_ptr->asyn_info, &info_ptr->from_host, rply_ptr0); + if (rply_ptr1 != NULL) rply_ptr2 = AsynSrv_GetReply ( + &info_ptr->asyn_info, &info_ptr->from_host, rply_ptr1); + + if (rply_ptr0 == NULL) rply_ptr0 = "?no_response"; + if (rply_ptr1 == NULL) rply_ptr1 = "?no_response"; + if (rply_ptr2 == NULL) rply_ptr2 = "?no_response"; + + if ((sscanf (rply_ptr0, "%x", msr) == 1) && + (sscanf (rply_ptr2, "%f", ist_posit) == 1)) { + info_ptr->ored_msr = info_ptr->ored_msr | *msr; + if ((*msr & MSR__POS_FAULT) != 0) info_ptr->fp_cntr++; + if ((*msr & MSR__RUN_FAULT) != 0) info_ptr->fr_cntr++; + *ored_msr = info_ptr->ored_msr; + *fp_cntr = info_ptr->fp_cntr; + *fr_cntr = info_ptr->fr_cntr; + /* Remember: we may get "?BSY" for SS and + ** this should not be treated as an error! + */ + if (sscanf (rply_ptr1, "%x", ss) != 1) *ss = -1; + }else { + if (*rply_ptr0 == '?') { + rply_ptr = rply_ptr0; + }else if (*rply_ptr1 == '?') { + rply_ptr = rply_ptr1; + }else if (*rply_ptr2 == '?') { + rply_ptr = rply_ptr2; + }else { + rply_ptr = "?funny_response"; + } + *msr = *ored_msr = *fp_cntr = *fr_cntr = *ss = -1; *ist_posit = 0.0; + EL734_SetErrcode (info_ptr, rply_ptr, "MSR\", \"SS\" or \"U"); + return False; + } + } + if (EL734_errcode != 0) return False; + EL734_call_depth--; + return True; + } +/* +**--------------------------------------------------------------------------- +** EL734_GetZeroPoint: Get zero point (= converted V register value) +*/ + int EL734_GetZeroPoint ( +/* ================== +*/ void **handle, + float *zero_pt) { + + int status, null_pt, nom, denom; + struct EL734info *info_ptr; + char cmnd0[10]; + char *rply_ptr0; + /*---------------------------------------------- + */ + *zero_pt = 0.0; + info_ptr = (struct EL734info *) *handle; + + if (!EL734_AddCallStack (info_ptr, "EL734_GetZeroPoint")) return False; + /*---------------------------------------------- + ** Get V register value. + */ + status = EL734_GetNullPoint (handle, &null_pt); + if (!status) return False; + + /*---------------------------------------------- + ** FD register values. + */ + status = EL734_GetEncGearing (handle, &nom, &denom); + if (!status) return False; + + if (nom == 0) { + EL734_errcode = EL734__BAD_OVFL; /* Encoder gearing ratio is zero */ + return False; + } + + *zero_pt = ((float) denom)/((float) nom); + *zero_pt *= (float) null_pt; + + if (EL734_errcode != 0) return False; + EL734_call_depth--; + return True; + } +/* +**--------------------------------------------------------------------------- +** EL734_MoveNoWait: Move motor and don't wait for completion. +*/ + int EL734_MoveNoWait ( +/* ================ +*/ void **handle, + float soll_posit) { + + int status; + struct EL734info *info_ptr; + char cmnd0[32]; + char *rply_ptr0; + /*---------------------------------------------- + */ + info_ptr = (struct EL734info *) *handle; + + if (!EL734_AddCallStack (info_ptr, "EL734_MoveNoWait")) return False; + /*---------------------------------------------- + ** Send P cmnd to EL734 + */ + sprintf (cmnd0, "p %d %.3f\r", info_ptr->motor, soll_posit); + status = AsynSrv_SendCmnds (&info_ptr->asyn_info, + &info_ptr->to_host, &info_ptr->from_host, + cmnd0, NULL); + if (!status) { + EL734_errcode = EL734__BAD_ASYNSRV; + return False; + }else { + rply_ptr0 = AsynSrv_GetReply ( + &info_ptr->asyn_info, &info_ptr->from_host, NULL); + if (rply_ptr0 == NULL) rply_ptr0 = "?no_response"; + if (*rply_ptr0 == '\0' || *rply_ptr0 == '\r' ) { + /* + ** The command was accepted - so zero the statistics + ** fields in the handle and return to caller. + */ + info_ptr->ored_msr = info_ptr->fp_cntr = info_ptr->fr_cntr = 0; + if (EL734_errcode != 0) return False; + EL734_call_depth--; + return True; + } + EL734_SetErrcode (info_ptr, rply_ptr0, "P"); + return False; + } + } +/* +**--------------------------------------------------------------------------- +** EL734_MoveWait: Move motor and wait for completion. +*/ + int EL734_MoveWait ( +/* ============== +*/ void **handle, + float soll_posit, + int *ored_msr, + int *fp_cntr, + int *fr_cntr, + float *ist_posit) { + + int status; + struct EL734info *info_ptr; + /*---------------------------------------------- + */ + *ored_msr = *fp_cntr = *fr_cntr = -1; *ist_posit = 0.0; + info_ptr = (struct EL734info *) *handle; + + if (!EL734_AddCallStack (info_ptr, "EL734_MoveWait")) return False; + /*---------------------------------------------- + ** Start the movement. + */ + status = EL734_MoveNoWait (handle, soll_posit); + if (status) { + status = EL734_WaitIdle (handle, ored_msr, fp_cntr, fr_cntr, ist_posit); + } + if (status && (EL734_errcode == 0)) EL734_call_depth--; + if (EL734_errcode != 0) return False; + return status; + } +/* +**--------------------------------------------------------------------------- +** EL734_Open: Open a connection to a motor. +*/ + int EL734_Open ( +/* ========== +*/ void **handle, + char *host, + int port, + int chan, + int motor, + char *device_id) { + + int my_msr, status; + struct EL734info *my_handle; + char tmo_save[4]; + char msr_cmnd[20]; + char *rply_ptr; + char *rply_ptr0; + char *rply_ptr1; + char *rply_ptr2; + char *rply_ptr3; +/*-------------------------------------------------------- +** Initialise the error info stack and pre-set the +** routine name (in case of error). +*/ + EL734_errcode = EL734_errno = EL734_vaxc_errno = 0; + strcpy (EL734_routine[0], "EL734_Open"); + EL734_call_depth = 1; +/*-------------------------------------------------------- +** Assume trouble +*/ + *handle = NULL; +/*-------------------------------------------------------- +** Reserve space for the data we need to store. +*/ + my_handle = (struct EL734info *) malloc (sizeof (*my_handle)); + if (my_handle == NULL) { + EL734_errcode = EL734__BAD_MALLOC; /* malloc failed!! */ + return False; + } +/*-------------------------------------------------------- +** Set up the connection +*/ + StrJoin (my_handle->asyn_info.host, sizeof (my_handle->asyn_info.host), + host, ""); + my_handle->asyn_info.port = port; + my_handle->asyn_info.chan = chan; + status = AsynSrv_Open (&my_handle->asyn_info); + if (!status) { + EL734_errcode = EL734__BAD_SOCKET; + GetErrno (&EL734_errno, &EL734_vaxc_errno); /* Save errno info */ + fprintf (stderr, "\nEL734_Open/AsynSrv_Open: " + "Failed to make connection.\n"); + free (my_handle); + return False; + } + + memcpy (tmo_save, my_handle->asyn_info.tmo, 4); /* Save time-out */ + EL734_Config ((void *) &my_handle, + "msecTmo", 500, /* Set a short time-out initially since + ** there should be no reason for the RMT, + ** ECHO or ID commands to take very long. + */ + "eot", "1\r", + "motor", motor, + NULL); + my_handle->ored_msr = 0; + my_handle->fp_cntr = 0; + my_handle->fr_cntr = 0; + /* + ** Now ensure the EL734 is on-line. The first "RMT 1" command can + ** fail due to pending characters in the EL734 input buffer causing + ** the "RMT 1" to be corrupted. The response of the EL734 to this + ** command is ignored for this reason (but the AsynSrv_SendCmnds + ** status must be OK otherwise it indicates a network problem). + */ + status = AsynSrv_SendCmnds (&my_handle->asyn_info, + &my_handle->to_host, &my_handle->from_host, + "rmt 1\r", NULL); + sprintf (msr_cmnd, "msr %d\r", motor); + if (status) { + if (device_id != NULL) { + status = AsynSrv_SendCmnds (&my_handle->asyn_info, + &my_handle->to_host, &my_handle->from_host, + "rmt 1\r", "echo 0\r", "id\r", msr_cmnd, NULL); + }else { + status = AsynSrv_SendCmnds (&my_handle->asyn_info, + &my_handle->to_host, &my_handle->from_host, + "rmt 1\r", "echo 0\r", "echo 0\r", msr_cmnd, NULL); + } + } + memcpy (my_handle->asyn_info.tmo, tmo_save, 4); /* Restore time-out */ + if (!status) { + /* Some error occurred in AsynSrv_SendCmnds */ + EL734_errcode = EL734__BAD_ASYNSRV; + AsynSrv_Close (&my_handle->asyn_info, False); + free (my_handle); + return False; + }else { + /* Check the responses carefully. The 3rd response should + ** be the device identifier (if to be checked). The 4th + ** response should be a hex integer. + */ + rply_ptr1 = rply_ptr2 = rply_ptr3 = NULL; + rply_ptr0 = AsynSrv_GetReply ( + &my_handle->asyn_info, &my_handle->from_host, NULL); + if (rply_ptr0 != NULL) rply_ptr1 = AsynSrv_GetReply ( + &my_handle->asyn_info, &my_handle->from_host, rply_ptr0); + if (rply_ptr1 != NULL) rply_ptr2 = AsynSrv_GetReply ( + &my_handle->asyn_info, &my_handle->from_host, rply_ptr1); + if (rply_ptr2 != NULL) rply_ptr3 = AsynSrv_GetReply ( + &my_handle->asyn_info, &my_handle->from_host, rply_ptr2); + + if (rply_ptr0 == NULL) rply_ptr0 = "?no_response"; + if (rply_ptr1 == NULL) rply_ptr1 = "?no_response"; + if (rply_ptr2 == NULL) rply_ptr2 = "?no_response"; + if (rply_ptr3 == NULL) rply_ptr3 = "?no_response"; + + if (*rply_ptr1 == '?') rply_ptr0 = rply_ptr1; + if (*rply_ptr2 == '?') rply_ptr0 = rply_ptr2; + if (*rply_ptr3 == '?') rply_ptr0 = rply_ptr3; + if (*rply_ptr0 != '?') { + if (device_id != NULL) { /* Check device ID? */ + if (*rply_ptr2 == '\0') { /* Yes. But if response is blank, it + ** may be because Emergency Stop is set. + */ + EL734_SetErrcode (my_handle, rply_ptr2, "ID"); + AsynSrv_Close (&my_handle->asyn_info, False); + free (my_handle); + return False; + }else { + if (strncmp (rply_ptr2, device_id, strlen (device_id)) != 0) { + EL734_errcode = EL734__BAD_DEV; /* Device has wrong ID */ + AsynSrv_Close (&my_handle->asyn_info, False); + free (my_handle); + return False; + } + } + } + if (sscanf (rply_ptr3, "%x", &my_msr) != 1) { + /* MSR response is bad */ + EL734_SetErrcode (my_handle, rply_ptr3, msr_cmnd); /* Check for *ES */ + if (EL734_errcode != EL734__EMERG_STOP) + EL734_errcode = EL734__BAD_DEV; + AsynSrv_Close (&my_handle->asyn_info, False); + free (my_handle); + return False; + } + /* + ** The connection is complete. Pass the data structure + ** back to the caller as a handle. + */ + *handle = my_handle; + if (EL734_errcode != 0) return False; + EL734_call_depth--; + return True; + }else { + EL734_errcode = EL734__BAD_DEV; + if (strcmp (rply_ptr0, "?OFL") == 0) EL734_errcode = EL734__BAD_OFL; + if (strcmp (rply_ptr0, "?CMD") == 0) EL734_errcode = EL734__BAD_CMD; + if (strcmp (rply_ptr0, "?LOC") == 0) EL734_errcode = EL734__BAD_LOC; + if (strcmp (rply_ptr0, "?ADR") == 0) EL734_errcode = EL734__BAD_ADR; + if (strcmp (rply_ptr0, "*ES") == 0) EL734_errcode = EL734__EMERG_STOP; + if (strncmp (rply_ptr0, "?TMO", 4) == 0) EL734_errcode = EL734__BAD_TMO; + if (EL734_errcode == EL734__BAD_DEV) + fprintf (stderr, " Unrecognised initial response: \"%s\"\n", + rply_ptr0); + } + } + AsynSrv_Close (&my_handle->asyn_info, False); + free (my_handle); + return False; + } +/* +**--------------------------------------------------------------------------- +** EL734_PutOffline: put the EL734 off-line +*/ + int EL734_PutOffline ( +/* ================ +*/ void **handle) { + + int status; + struct EL734info *info_ptr; + char *rply_ptr0, *rply_ptr1, *rply_ptr2; + /*---------------------------------------------- + */ + info_ptr = (struct EL734info *) *handle; + + if (!EL734_AddCallStack (info_ptr, "EL734_PutOffline")) return False; + /*---------------------------------------------- + ** The problem which this routine has is that the EL734 + ** may already be off-line. The following is, therefore, + ** rather pedantic for most cases which occur in practice. + */ + status = AsynSrv_SendCmnds (&info_ptr->asyn_info, + &info_ptr->to_host, &info_ptr->from_host, + "\r", "rmt 1\r", "echo 1\r", "rmt 0\r", NULL); + if (!status) { + EL734_errcode = EL734__BAD_ASYNSRV; return False;} + + status = AsynSrv_SendCmnds (&info_ptr->asyn_info, + &info_ptr->to_host, &info_ptr->from_host, + "RMT\r", "", NULL); + if (!status) { + EL734_errcode = EL734__BAD_ASYNSRV; return False;} + + rply_ptr0 = AsynSrv_GetReply ( + &info_ptr->asyn_info, &info_ptr->from_host, NULL); + rply_ptr1 = AsynSrv_GetReply ( + &info_ptr->asyn_info, &info_ptr->from_host, rply_ptr0); + if (rply_ptr0 == NULL) rply_ptr0 = "?no_response"; + if (rply_ptr1 == NULL) rply_ptr1 = "?no_response"; + if ((strcmp (rply_ptr0, "RMT") == 0) && + (strcmp (rply_ptr1, "\n0") == 0)) { + EL734_call_depth--; + return True; + } + + EL734_SetErrcode (info_ptr, rply_ptr0, "RMT"); + return False; + } +/* +**--------------------------------------------------------------------------- +** EL734_PutOnline: put the EL734 on-line +*/ + int EL734_PutOnline ( +/* =============== +*/ void **handle, + int echo) { + + int status, my_echo; + struct EL734info *info_ptr; + char cmnd0[10]; + char *rply_ptr0; + /*---------------------------------------------- + */ + info_ptr = (struct EL734info *) *handle; + + if (!EL734_AddCallStack (info_ptr, "EL734_PutOnline")) return False; + /*---------------------------------------------- + */ + if ((echo != 0) && (echo != 1) && (echo != 2)) { + EL734_errcode = EL734__BAD_PAR; return False; + } + /*---------------------------------------------- + ** The problem which this routine has is that the state + ** of the EL734 is not known. The following is, therefore, + ** rather pedantic for most cases which occur in practice. + */ + sprintf (cmnd0, "echo %d\r", echo); + status = AsynSrv_SendCmnds (&info_ptr->asyn_info, + &info_ptr->to_host, &info_ptr->from_host, + "\r", "rmt 1\r", cmnd0, NULL); + if (!status) { + EL734_errcode = EL734__BAD_ASYNSRV; + return False; + } + + status = AsynSrv_SendCmnds (&info_ptr->asyn_info, + &info_ptr->to_host, &info_ptr->from_host, + "echo\r", NULL); + if (!status) { + EL734_errcode = EL734__BAD_ASYNSRV; return False;} + + rply_ptr0 = AsynSrv_GetReply ( + &info_ptr->asyn_info, &info_ptr->from_host, NULL); + if (rply_ptr0 == NULL) rply_ptr0 = "?no_response"; + if (strcmp (rply_ptr0, "ECHO") == 0) { + rply_ptr0 = AsynSrv_GetReply ( + &info_ptr->asyn_info, &info_ptr->from_host, rply_ptr0); + if (rply_ptr0 == NULL) rply_ptr0 = "?no_response"; + } + if ((sscanf (rply_ptr0, "%d", &my_echo) == 1) && (my_echo == echo)) { + EL734_call_depth--; + return True; + } + + EL734_SetErrcode (info_ptr, rply_ptr0, "ECHO"); + return False; + } +/* +**--------------------------------------------------------------------------- +** EL734_SendCmnd - Send a command to RS232C server. +*/ + int EL734_SendCmnd ( +/* ============== +*/ void **handle, + char *cmnd, + char *rply, + int rply_size) { + + struct EL734info *info_ptr; + int my_status; + char *rply_ptr; + /*---------------------------------------------- + */ + info_ptr = (struct EL734info *) *handle; + + if (!EL734_AddCallStack (info_ptr, "EL734_SendCmnd")) return False; + /*---------------------------------------------- + ** Send command to EL734. + */ + my_status = AsynSrv_SendCmnds (&info_ptr->asyn_info, + &info_ptr->to_host, &info_ptr->from_host, + cmnd, NULL); + if (!my_status) { + EL734_errcode = EL734__BAD_ASYNSRV; return False; + }else { + rply_ptr = AsynSrv_GetReply ( + &info_ptr->asyn_info, &info_ptr->from_host, NULL); + if (rply_ptr == NULL) rply_ptr = "?no_response"; + StrJoin (rply, rply_size, rply_ptr, ""); + } + + if (EL734_errcode != 0) return False; + EL734_call_depth--; + return True; + } +/* +**--------------------------------------------------------------------------- +** EL734_SetAirCush: Set the air-cushion register (AC register) +*/ + int EL734_SetAirCush ( +/* ================ +*/ void **handle, + int state) { + + int status, dum1, my_state; + struct EL734info *info_ptr; + char cmnd0[32]; + char *rply_ptr0; + /*---------------------------------------------- + */ + info_ptr = (struct EL734info *) *handle; + + if (!EL734_AddCallStack (info_ptr, "EL734_SetAirCush")) return False; + /*---------------------------------------------- + ** Send AC cmnd to EL734 + */ + if (state != 0) state = 1; + sprintf (cmnd0, "ac %d %d\r", info_ptr->motor, state); + status = AsynSrv_SendCmnds (&info_ptr->asyn_info, + &info_ptr->to_host, &info_ptr->from_host, + cmnd0, NULL); + if (!status) { + EL734_errcode = EL734__BAD_ASYNSRV; + return False; + }else { + rply_ptr0 = AsynSrv_GetReply ( + &info_ptr->asyn_info, &info_ptr->from_host, NULL); + if (rply_ptr0 == NULL) rply_ptr0 = "?no_response"; + if (*rply_ptr0 == '\0') { + /* + ** The command was accepted - check value is set OK. + */ + status = EL734_GetAirCush (handle, &dum1, &my_state); + if (!status) return False; + if (state != my_state) { + EL734_errcode = EL734__VFY_ERR; + return False; + } + EL734_call_depth--; + return True; + } + EL734_SetErrcode (info_ptr, rply_ptr0, "AC"); + return False; + } + } +/* +**--------------------------------------------------------------------------- +** EL734_SetErrcode - Set up EL734_errcode +*/ + int EL734_SetErrcode ( +/* ================ +*/ struct EL734info *info_ptr, + char *response, + char *cmnd) { + + int status; + char *rply; + char tmo_save[4]; + char eot_save[4]; + + EL734_errcode = EL734__BAD_ILLG; + if (strcmp (response, "?OFL") == 0) EL734_errcode = EL734__BAD_OFL; + if (strcmp (response, "?CMD") == 0) EL734_errcode = EL734__BAD_CMD; + if (strcmp (response, "?LOC") == 0) EL734_errcode = EL734__BAD_LOC; + if (strcmp (response, "?ADR") == 0) EL734_errcode = EL734__BAD_ADR; + if (strcmp (response, "?RNG") == 0) EL734_errcode = EL734__BAD_RNG; + if (strcmp (response, "*ES") == 0) EL734_errcode = EL734__EMERG_STOP; + if (strcmp (response, "*MS" ) == 0) EL734_errcode = EL734__BAD_STP; + if (strncmp (response, "?TMO", 4) == 0) EL734_errcode = EL734__BAD_TMO; + if (EL734_errcode != EL734__BAD_ILLG) return EL734_errcode; + /* + ** The response is not recognised. Perhaps the emergency stop + ** signal is set. To check this, it is necessary to turn off + ** terminator checking since the EL734 prefixes its "*ES" + ** response with a character. We also therefore set + ** a very short time-out. + */ + memcpy (tmo_save, info_ptr->asyn_info.tmo, 4); /* Save time-out */ + memcpy (eot_save, info_ptr->asyn_info.eot, 4); /* Save terminators */ + AsynSrv_Config (&info_ptr->asyn_info, + "msecTmo", 100, /* Set short time-out */ + "eot", "0", /* Set no terminator */ + NULL); + status = AsynSrv_SendCmnds (&info_ptr->asyn_info, + &info_ptr->to_host, &info_ptr->from_host, + "ID\r", NULL); + memcpy (info_ptr->asyn_info.eot, eot_save, 4); /* Restore terminators */ + memcpy (info_ptr->asyn_info.tmo, tmo_save, 4); /* Restore time-out */ + if (status) { + rply = AsynSrv_GetReply ( + &info_ptr->asyn_info, &info_ptr->from_host, NULL); + if (rply == NULL) rply = "?no_response"; + }else { + rply = "?no_response"; + } + if (strstr (rply, "*ES") != NULL) EL734_errcode = EL734__EMERG_STOP; + + if ((EL734_errcode == EL734__BAD_ILLG) && (cmnd != NULL)) { + fprintf (stderr, " Unrecognised response to \"%s\" command: \"%s\"\n", + cmnd, response); + strcpy (EL734_IllgText, cmnd); + strcat (EL734_IllgText, " : "); + strcat (EL734_IllgText, response); + } + return EL734_errcode; + } +/* +**--------------------------------------------------------------------------- +** EL734_SetHighSpeed: Set the max speed (J register) +*/ + int EL734_SetHighSpeed ( +/* ================== +*/ void **handle, + int hi) { + + int status, my_lo, my_hi, my_ramp; + struct EL734info *info_ptr; + char cmnd0[32]; + char *rply_ptr0; + /*---------------------------------------------- + */ + info_ptr = (struct EL734info *) *handle; + + if (!EL734_AddCallStack (info_ptr, "EL734_SetHighSpeed")) return False; + /*---------------------------------------------- + ** Send J cmnd to EL734 + */ + sprintf (cmnd0, "j %d %d\r", info_ptr->motor, hi); + status = AsynSrv_SendCmnds (&info_ptr->asyn_info, + &info_ptr->to_host, &info_ptr->from_host, + cmnd0, NULL); + if (!status) { + EL734_errcode = EL734__BAD_ASYNSRV; + return False; + }else { + rply_ptr0 = AsynSrv_GetReply ( + &info_ptr->asyn_info, &info_ptr->from_host, NULL); + if (rply_ptr0 == NULL) rply_ptr0 = "?no_response"; + if (*rply_ptr0 == '\0') { + /* + ** The command was accepted - check value is set OK. + */ + status = EL734_GetSpeeds (handle, &my_lo, &my_hi, &my_ramp); + if (!status) return False; + if (hi != my_hi) { + EL734_errcode = EL734__VFY_ERR; + return False; + } + EL734_call_depth--; + return True; + } + EL734_SetErrcode (info_ptr, rply_ptr0, "J"); + return False; + } + } +/* +**--------------------------------------------------------------------------- +** EL734_SetLowSpeed: Set the start/stop speed (G register) +*/ + int EL734_SetLowSpeed ( +/* ================= +*/ void **handle, + int lo) { + + int status, my_lo, my_hi, my_ramp; + struct EL734info *info_ptr; + char cmnd0[32]; + char *rply_ptr0; + /*---------------------------------------------- + */ + info_ptr = (struct EL734info *) *handle; + + if (!EL734_AddCallStack (info_ptr, "EL734_SetLowSpeed")) return False; + /*---------------------------------------------- + ** Send G cmnd to EL734 + */ + sprintf (cmnd0, "g %d %d\r", info_ptr->motor, lo); + status = AsynSrv_SendCmnds (&info_ptr->asyn_info, + &info_ptr->to_host, &info_ptr->from_host, + cmnd0, NULL); + if (!status) { + EL734_errcode = EL734__BAD_ASYNSRV; + return False; + }else { + rply_ptr0 = AsynSrv_GetReply ( + &info_ptr->asyn_info, &info_ptr->from_host, NULL); + if (rply_ptr0 == NULL) rply_ptr0 = "?no_response"; + if (*rply_ptr0 == '\0') { + /* + ** The command was accepted - check value is set OK. + */ + status = EL734_GetSpeeds (handle, &my_lo, &my_hi, &my_ramp); + if (!status) return False; + if (lo != my_lo) { + EL734_errcode = EL734__VFY_ERR; + return False; + } + EL734_call_depth--; + return True; + } + EL734_SetErrcode (info_ptr, rply_ptr0, "G"); + return False; + } + } +/* +**--------------------------------------------------------------------------- +** EL734_SetRamp: Set the start/stop ramp (E register) +*/ + int EL734_SetRamp ( +/* ============= +*/ void **handle, + int ramp) { + + int status, my_lo, my_hi, my_ramp; + struct EL734info *info_ptr; + char cmnd0[32]; + char *rply_ptr0; + /*---------------------------------------------- + */ + info_ptr = (struct EL734info *) *handle; + + if (!EL734_AddCallStack (info_ptr, "EL734_SetRamp")) return False; + /*---------------------------------------------- + ** Send E cmnd to EL734 + */ + sprintf (cmnd0, "e %d %d\r", info_ptr->motor, ramp); + status = AsynSrv_SendCmnds (&info_ptr->asyn_info, + &info_ptr->to_host, &info_ptr->from_host, + cmnd0, NULL); + if (!status) { + EL734_errcode = EL734__BAD_ASYNSRV; + return False; + }else { + rply_ptr0 = AsynSrv_GetReply ( + &info_ptr->asyn_info, &info_ptr->from_host, NULL); + if (rply_ptr0 == NULL) rply_ptr0 = "?no_response"; + if (*rply_ptr0 == '\0') { + /* + ** The command was accepted - check value is set OK. + */ + status = EL734_GetSpeeds (handle, &my_lo, &my_hi, &my_ramp); + if (!status) return False; + if (ramp != my_ramp) { + EL734_errcode = EL734__VFY_ERR; + return False; + } + EL734_call_depth--; + return True; + } + EL734_SetErrcode (info_ptr, rply_ptr0, "E"); + return False; + } + } +/* +**--------------------------------------------------------------------------- +** EL734_Stop: Send a stop command to motor. +*/ + int EL734_Stop ( +/* ========== +*/ void **handle) { + + int status; + struct EL734info *info_ptr; + char cmnd0[10]; + char *rply_ptr0; + /*---------------------------------------------- + */ + info_ptr = (struct EL734info *) *handle; + + if (!EL734_AddCallStack (info_ptr, "EL734_Stop")) return False; + /*---------------------------------------------- + ** Send S cmnd to EL734 + */ + sprintf (cmnd0, "s %d\r", info_ptr->motor); + status = AsynSrv_SendCmnds (&info_ptr->asyn_info, + &info_ptr->to_host, &info_ptr->from_host, + cmnd0, NULL); + if (!status) { + EL734_errcode = EL734__BAD_ASYNSRV; + return False; + }else { + rply_ptr0 = AsynSrv_GetReply ( + &info_ptr->asyn_info, &info_ptr->from_host, NULL); + if (rply_ptr0 == NULL) rply_ptr0 = "?no_response"; + if (rply_ptr0[0] == '\0') { + if (EL734_errcode != 0) return False; + EL734_call_depth--; + return True; + } + EL734_SetErrcode (info_ptr, rply_ptr0, "S"); + return False; + } + } +/* +**--------------------------------------------------------------------------- +** EL734_WaitIdle: Wait till MSR goes to zero. +*/ + int EL734_WaitIdle ( +/* ============== +*/ void **handle, + int *ored_msr, + int *fp_cntr, + int *fr_cntr, + float *ist_posit) { +#ifdef __VMS +#include +#define hibernate lib$wait (0.25) +#else +#include +#include + struct timespec delay = {0, 250000000}; + struct timespec delay_left; + +#ifdef LINUX +#define hibernate nanosleep(&delay, &delay_left) +#else +#define hibernate nanosleep_d9 (&delay, &delay_left) +#endif + +#endif + int msr, ss; + struct EL734info *info_ptr; + /*---------------------------------------------- + */ + *ored_msr = *fp_cntr = *fr_cntr = -1; *ist_posit = 0.0; + info_ptr = (struct EL734info *) *handle; + + if (!EL734_AddCallStack (info_ptr, "EL734_WaitIdle")) return False; + /*---------------------------------------------- + ** Poll the motor status till not moving. + */ + while (EL734_GetStatus (handle, + &msr, ored_msr, fp_cntr, fr_cntr, &ss, ist_posit)) { + if ((msr & MSR__BUSY) == 0) { + if (EL734_errcode != 0) return False; + EL734_call_depth--; + return True; + } + hibernate; + } + return False; /* Error detected in EL734_GetStatus */ + } +/* +**--------------------------------------------------------------------------- +** EL734_ZeroStatus: Zero the "ored-MSR" and fault counters. +*/ + void EL734_ZeroStatus ( +/* ================ +*/ void **handle) { + + struct EL734info *info_ptr; + /* + ** Do nothing if no handle! + */ + info_ptr = (struct EL734info *) *handle; + if (info_ptr == NULL) return; + /* + ** Zero the data structure items. + */ + info_ptr->ored_msr = 0; + info_ptr->fp_cntr = 0; + info_ptr->fr_cntr = 0; + memset (info_ptr->from_host.msg_size, + '0', sizeof (info_ptr->from_host.msg_size)); + return; + } +/*-------------------------------------------- End of EL734_Utility.C =======*/ diff --git a/hardsup/el734fix.h b/hardsup/el734fix.h new file mode 100644 index 0000000..e14df3a --- /dev/null +++ b/hardsup/el734fix.h @@ -0,0 +1,29 @@ +/*--------------------------------------------------------------------------- + Fix file for David renaming lots of el734 error codes. + + Mark Koennecke, October 1998 +----------------------------------------------------------------------------*/ +#ifndef EL734FIX +#define EL734FIX +#include "asynsrv_errcodes.h" + +#define EL734__BAD_HOST ASYNSRV__BAD_HOST +#define EL734__BAD_BIND ASYNSRV__BAD_BIND +#define EL734__BAD_SENDLEN ASYNSRV__BAD_SEND_LEN +#define EL734__BAD_SEND ASYNSRV__BAD_SEND +#define EL734__BAD_SEND_PIPE ASYNSRV__BAD_SEND_PIPE +#define EL734__BAD_SEND_UNKN ASYNSRV__BAD_SEND_UNKN +#define EL734__BAD_RECV ASYNSRV__BAD_RECV +#define EL734__BAD_RECV_PIPE ASYNSRV__BAD_RECV_PIPE +#define EL734__BAD_RECV_NET ASYNSRV__BAD_RECV_NET +#define EL734__BAD_SEND_NET ASYNSRV__BAD_SEND_NET +#define EL734__BAD_RECV_UNKN ASYNSRV__BAD_RECV_UNKN +#define EL734__BAD_NOT_BCD ASYNSRV__BAD_NOT_BCD +#define EL734__BAD_RECVLEN ASYNSRV__BAD_RECV_LEN +#define EL734__BAD_FLUSH ASYNSRV__BAD_FLUSH +#define EL734__BAD_RECV1 ASYNSRV__BAD_RECV1 +#define EL734__BAD_RECV1_PIPE ASYNSRV__BAD_RECV1_PIPE +#define EL734__BAD_RECV1_NET ASYNSRV__BAD_RECV1_NET +#define EL734__BAD_CONNECT ASYNSRV__BAD_CONNECT +#define EL734__BAD_ID EL734__BAD_DEV +#endif /* el734fix */ diff --git a/hardsup/el734tcl.c b/hardsup/el734tcl.c new file mode 100644 index 0000000..c4dc11a --- /dev/null +++ b/hardsup/el734tcl.c @@ -0,0 +1,644 @@ +/*-------------------------------------------------------------------------- + + Some code to make EL734 motors as used at SINQ available in TCL. + Just a wrapper around David Maden's motor control routines. + + You are free to use and modify this software for noncommercial + usage. + + No warranties or liabilities of any kind taken by me or my employer + + Mark Koennecke June 1996 +----------------------------------------------------------------------------*/ +#include "sinq_prototypes.h" +/* #include */ +#include +#include +#include +#include +#include "rs232c_def.h" +#include "el734_def.h" + +#define INACTIVE 999.8 +#define MOTACURRACY 0.02 +#define False 0 +#define True 1 + + typedef struct + { + float fUpper; /* upper limit */ + float fLower; /* Lower Limit */ + int iFix; /* fixed, unfixed flag */ + float fSoftZero; /* SW-zero point */ + float fSoftUpper; /* software upper boundary*/ + float fSoftLower; /* " lower " */ + int iLowFlag, iUpFlag, iZeroFlag; /*activity flags */ + void *pData; /* EL734 open struct */ + } EL734st; + + EXTERN int EL734Action(ClientData pDat, Tcl_Interp *i, int a, char *argv[]); + static void EL734Error2Text(char *pBuffer, int errcode); + +/*--------------------------------------------------------------------------- + Tcl has a high niceness level. It deletes a command properly when + exiting, reinitializing etc. I use this facility to kill off the + motor initialised in EL734. +---------------------------------------------------------------------------*/ +EXTERN void EL734Murder(ClientData pData) +{ + EL734st *pTa = (EL734st *)pData; + EL734_Close(&(pTa->pData)); + free(pData); +} +/*---------------------------------------------------------------------------- + EL734 is the main entry point for this stuff. It connects to a motor + and, on success, creates a new command with the name of the motor. + Syntax: + EL734 name host port channel index +---------------------------------------------------------------------------*/ + +int EL734(ClientData clientData, Tcl_Interp *interp, + int argc, char *argv[]) +{ + int iRet; + EL734st *pEL734 = NULL; + int iPort, iChannel, iMotor; + char *pErr = NULL; + char pBueffel[80]; + + /* check arguments */ + if(argc < 6) + { + Tcl_AppendResult(interp, + " Insufficient arguments: EL734 name host port channel index" + , (char *) NULL); + return TCL_ERROR; + } + + /* convert arguments */ + iRet = Tcl_GetInt(interp,argv[3],&iPort); + if(iRet == TCL_ERROR) + { + Tcl_AppendResult(interp,"Need integer value for port", + (char *)NULL); + return iRet; + } + + iRet = Tcl_GetInt(interp,argv[4],&iChannel); + if(iRet == TCL_ERROR) + { + Tcl_AppendResult(interp,"Need integer value for channel", + (char *)NULL); + return iRet; + } + + iRet = Tcl_GetInt(interp,argv[5],&iMotor); + if(iRet == TCL_ERROR) + { + Tcl_AppendResult(interp,"Need integer value for motor", + (char *)NULL); + return iRet; + } + + /* make a new pointer, initialise EL734st */ + pEL734 = (EL734st *)malloc(sizeof(EL734st)); + if(pEL734 ==NULL) + { + Tcl_AppendResult(interp,"No memory in EL734",NULL); + return TCL_ERROR; + } + pEL734->iFix = False; + pEL734->fSoftZero = INACTIVE+1; + pEL734->fSoftUpper = INACTIVE+1.; + pEL734->fSoftLower = -INACTIVE-1.; + pEL734->iZeroFlag = False; + pEL734->iLowFlag = False; + pEL734->iUpFlag = False; + + /* open the motor, finally */ + iRet = EL734_Open(&(pEL734->pData), argv[2],iPort,iChannel,iMotor,"STPMC EL734"); + if(iRet) /* success */ + { + /* figure out motor limits */ + EL734_GetLimits(&(pEL734->pData),&(pEL734->fLower), + &(pEL734->fUpper)); + /* handle TCL, create new command: the motor */ + Tcl_CreateCommand(interp,strdup(argv[1]),EL734Action, + (ClientData)pEL734,EL734Murder); + Tcl_AppendResult(interp,strdup(argv[1]),(char *)NULL); + return TCL_OK; + } + else + { + EL734_ErrInfo(&pErr,&iPort,&iChannel, &iMotor); + EL734Error2Text(pBueffel,iPort); + Tcl_AppendResult(interp,pBueffel,(char *) NULL); + free(pEL734); + return TCL_ERROR; + } +} +/*--------------------------------------------------------------------------- + CheckPos checks a position and converts it to a real position. + Returns TCL_ERROR on mistake, TCL_OK else +---------------------------------------------------------------------------*/ + static int CheckPos(Tcl_Interp *interp, EL734st *pData, + float fRequested, float *fDrive) + { + float fPos; + char pBueffel[132]; + + /* fixed ? */ + if(pData->iFix) + { + Tcl_AppendResult(interp,"Motor fixed",NULL); + return TCL_ERROR; + } + + /* check against SW-boundaries */ + if(pData->iUpFlag) + { + if(fRequested > pData->fSoftUpper) + { + sprintf(pBueffel, + "Requested position: %f violates SW-boundary %f", + fRequested, pData->fSoftUpper); + Tcl_AppendResult(interp,pBueffel,(char *) NULL); + return TCL_ERROR; + } + } + if(pData->iLowFlag) + { + if(fRequested < pData->fSoftLower) + { + sprintf(pBueffel, + "Requested position: %f violates SW-boundary %f", + fRequested, pData->fSoftLower); + Tcl_AppendResult(interp,pBueffel,(char *) NULL); + return TCL_ERROR; + } + } + + /* correct for zero point */ + if(pData->iZeroFlag) + { + fPos = fRequested - pData->fSoftZero; + } + else + { + fPos = fRequested; + } + + /* check HW-boundaries */ + if( (fPos < pData->fLower) || (fPos > pData->fUpper) ) + { + sprintf(pBueffel," %f outside limits %f %f", + fPos,pData->fLower, pData->fUpper); + Tcl_AppendResult(interp,"Requested position: ", + pBueffel,(char *)NULL); + return TCL_ERROR; + } + + *fDrive = fPos; + return TCL_OK; + + } +/* ------------------------------------------------------------------------- + fucking standard library missing functionality!!!!!!!!!!!!!!!! +---------------------------------------------------------------------------*/ + static float absf(float x) + { + if(x < .0) + return -x; + else + return x; + } + +/*-------------------------------------------------------------------------- + + EL734 Action is the routine where commands send to the motor will + end up. + + Syntax: + motor lim shows motor limits + motor dr val drives the motor to val + motor run val set the motor in motion, without waiting + for completion + motor pos shows motor position + motor fix fixes a motor at a position + motor unfix unfixes a fixed motor + motor zero val set a software zero point + motor upper val sets a software upper limit + motor lower val sets a software lower limit +----------------------------------------------------------------------------*/ +EXTERN int EL734Action(ClientData clientData, Tcl_Interp *interp, + int argc, char *argv[]) +{ + EL734st *pData = (EL734st *)clientData; + char pBueffel[80]; + char *pErr = NULL; + int iRet, iMSR, iOMSR, iFPC, iFRC, iSS; + float fPos, fNpos; + double dPos; + int i; + struct RS__RplyStruct *pReply = NULL; + + + /* check parameters */ + if(argc < 2) + { + Tcl_AppendResult(interp, + "Usage: motor and either dr, pos, hlim slim run zero up lo",(char *)NULL); + return TCL_ERROR; + } + if(pData == NULL) + { + Tcl_AppendResult(interp, + "Motor data lost!!!!!!!!",(char *)NULL); + return TCL_ERROR; + } + + /* check for HW-lim */ + if(strcmp(argv[1],"hlim") == 0) + { + sprintf(pBueffel," %f %f",pData->fLower,pData->fUpper); + Tcl_AppendResult(interp,pBueffel,(char *)NULL); + return TCL_OK; + } + + /* check for SW-lim */ + if(strcmp(argv[1],"slim") == 0) + { + sprintf(pBueffel," %f %f",pData->fSoftLower,pData->fSoftUpper); + Tcl_AppendResult(interp,pBueffel,(char *)NULL); + return TCL_OK; + } + + /* fix & unfix */ + if(strcmp(argv[1],"fix") == 0) + { + pData->iFix = True; + return TCL_OK; + } + if(strcmp(argv[1],"unfix") == 0) + { + pData->iFix = False; + return TCL_OK; + } + + /* reset */ + if(strcmp(argv[1],"reset")== 0) + { + pData->iFix = False; + pData->iLowFlag = False; + pData->iUpFlag = False; + pData->iZeroFlag = False; + return TCL_OK; + } + + /* check for pos */ + if(strcmp(argv[1],"pos") == 0) + { + iRet = EL734_GetStatus(&(pData->pData), + &iMSR, + &iOMSR, + &iFPC, + &iFRC, + &iSS, + &fPos); + if(!iRet) + { + EL734_ErrInfo(&pErr,&iMSR,&iOMSR, &iSS); + EL734Error2Text(pBueffel,iMSR); + Tcl_AppendResult(interp,pBueffel,(char *) NULL); + return TCL_ERROR; + } + else + { + if(pData->iZeroFlag) + { + fPos += pData->fSoftZero; + } + sprintf(pBueffel," %f",fPos); + Tcl_AppendResult(interp,pBueffel,NULL); + return TCL_OK; + } + } + + /* zero point */ + if(strcmp(argv[1],"zero") == 0) + { + /* check for zero already been defined */ + if(pData->iZeroFlag) + { + Tcl_AppendResult(interp, + "Request to set new zero point rejected.", + " Use reset before new definition. ", + " I'll get confused otherwise ", + NULL); + return TCL_ERROR; + } + + /* get the new position */ + if(argc < 3) + { + Tcl_AppendResult(interp, + "Usage: motor zero val",NULL); + return TCL_ERROR; + } + + iRet = Tcl_GetDouble(interp,argv[2],&dPos); + fNpos = dPos; + if(iRet == TCL_ERROR) + { + Tcl_AppendResult(interp,"Need float value for new zeropint", + (char *)NULL); + return iRet; + } + pData->fSoftZero = -fNpos; + pData->iZeroFlag = True; + return TCL_OK; + } + + /* upper SW-limit */ + if(strcmp(argv[1],"up") == 0) + { + /* get the new position */ + if(argc < 3) + { + Tcl_AppendResult(interp, + "Usage: motor up val",NULL); + return TCL_ERROR; + } + + iRet = Tcl_GetDouble(interp,argv[2],&dPos); + fNpos = dPos; + if(iRet == TCL_ERROR) + { + Tcl_AppendResult(interp,"Need float value for new upper limit", + (char *)NULL); + return iRet; + } + pData->fSoftUpper = fNpos; + pData->iUpFlag = True; + return TCL_OK; + } + + /* lower SW-limit */ + if(strcmp(argv[1],"lo") == 0) + { + /* get the new position */ + if(argc < 3) + { + Tcl_AppendResult(interp, + "Usage: motor lo val",NULL); + return TCL_ERROR; + } + + iRet = Tcl_GetDouble(interp,argv[2],&dPos); + fNpos = dPos; + if(iRet == TCL_ERROR) + { + Tcl_AppendResult(interp,"Need float value for new lower limit", + (char *)NULL); + return iRet; + } + pData->fSoftLower = fNpos; + pData->iLowFlag = True; + return TCL_OK; + } + + + + /* this is most important: dr for Drive */ + if(strcmp(argv[1],"dr") == 0) + { + /* get the new position */ + if(argc < 3) + { + Tcl_AppendResult(interp, + "Usage: motor dr val",NULL); + return TCL_ERROR; + } + + iRet = Tcl_GetDouble(interp,argv[2],&dPos); + fNpos = dPos; + if(iRet == TCL_ERROR) + { + Tcl_AppendResult(interp,"Need value to drive to", + (char *)NULL); + return iRet; + } + + /* check if compatible with limits */ + if(CheckPos(interp,pData,fNpos,&fPos) == TCL_ERROR) + return TCL_ERROR; + + /* finally move */ + iRet = EL734_MoveWait(&(pData->pData), fPos, &iOMSR, + &iFPC, &iFRC,&fNpos); + /* 99.99999999999% of all code is error checking */ + if(!iRet) + { + EL734_ErrInfo(&pErr,&iMSR,&iOMSR, &iSS); + EL734Error2Text(pBueffel,iMSR); + Tcl_AppendResult(interp,pBueffel,(char *) NULL); + return TCL_ERROR; + } + + /* check if driving has been done */ + if(absf(fPos-fNpos) > MOTACURRACY) + { + Tcl_AppendResult(interp, + " Motor error: inacurate driving!", + " Probably something serious is wrong ", + " Check the fucking hardware ", + NULL); + return TCL_ERROR; + } + return TCL_OK; + } + + /* this is most important: run for Driving without waiting */ + if(strcmp(argv[1],"run") == 0) + { + /* get the new position */ + if(argc < 3) + { + Tcl_AppendResult(interp, + "Usage: motor run val",NULL); + return TCL_ERROR; + } + + iRet = Tcl_GetDouble(interp,argv[2],&dPos); + fNpos = dPos; + if(iRet == TCL_ERROR) + { + Tcl_AppendResult(interp,"Need value to run for", + (char *)NULL); + return iRet; + } + + /* check if compatible with limits */ + if(CheckPos(interp,pData,fNpos,&fPos) == TCL_ERROR) + return TCL_ERROR; + + /* finally move */ + iRet = EL734_MoveNoWait (&(pData->pData), fPos); + + /* 99.99999999999% of all code is error checking */ + if(!iRet) + { + EL734_ErrInfo(&pErr,&iMSR,&iOMSR, &iSS); + EL734Error2Text(pBueffel,iMSR); + Tcl_AppendResult(interp,pBueffel,(char *) NULL); + return TCL_ERROR; + } + return TCL_OK; + } + + /* the dangerous, undocumented expert command: com: + sends something directly to the motor. All args following + com will be concatenated in one string, closed with \r + and send to the motor . A GetReply will be invoked in order + to yield a return value. Usage by normal motor users strictly + discouraged. + */ + if(strcmp(argv[1],"com") == 0) + { + strcpy(pBueffel,argv[2]); + for(i = 3; i < argc; i++) + { + strcat(pBueffel," "); + strcat(pBueffel,argv[i]); + } + sprintf(pBueffel,"%s\r",pBueffel); + iRet = EL734_SendCmnds(&(pData->pData),pBueffel,NULL); + if(!iRet) + { + EL734_ErrInfo(&pErr,&iMSR,&iOMSR, &iSS); + EL734Error2Text(pBueffel,iMSR); + Tcl_AppendResult(interp,pBueffel,(char *) NULL); + return TCL_ERROR; + } + /* fetch reply */ + pReply = (struct RS__RplyStruct *)EL734_GetReply( + &(pData->pData),NULL); + while(pReply != NULL) + { + Tcl_AppendElement(interp,pReply->rply); + pReply = (struct RS__RplyStruct *)EL734_GetReply( + &(pData->pData),pReply); + } + return TCL_OK; + } + + /* if we end here an unknown command has been sent */ + Tcl_AppendResult(interp, + "Usage: motor and either dr, run,zero, pos, hlim" + "slim up low reset fix unfix",(char *)NULL); + return TCL_ERROR; +} +/*--------------------------------------------------------------------------- + + EL734Error2Text converts between an EL734 error code to text +-----------------------------------------------------------------------------*/ + void EL734Error2Text(char *pBuffer, int iErr) + { + switch(iErr) + { + case -28: + strcpy(pBuffer,"EL734__BAD_ADR"); + break; + case -8: + strcpy(pBuffer,"EL734__BAD_BIND"); + break; + case -30: + strcpy(pBuffer,"EL734__BAD_BSY"); + break; + case -3: + strcpy(pBuffer,"EL734__BAD_CMD"); + break; + case -9: + strcpy(pBuffer,"EL734__BAD_CONNECT"); + break; + case -23: + strcpy(pBuffer,"EL734__BAD_FLUSH"); + break; + case -6: + strcpy(pBuffer,"EL734__BAD_HOST"); + break; + case -10: + strcpy(pBuffer,"EL734__BAD_ID"); + break; + case -5: + strcpy(pBuffer,"EL734__BAD_ILLG"); + break; + case -2: + strcpy(pBuffer,"EL734__BAD_LOC"); + break; + case -11: + strcpy(pBuffer,"EL734__BAD_MALLOC"); + break; + case -21: + strcpy(pBuffer,"EL734__BAD_NOT_BCD"); + break; + case -4: + strcpy(pBuffer,"EL734__BAD_OFL"); + break; + case -29: + strcpy(pBuffer,"EL734__BAD_PAR"); + break; + + case -17: + strcpy(pBuffer,"EL734__BAD_RECV"); + break; + case -19: + strcpy(pBuffer,"EL734__BAD_RECV_NET"); + break; + case -18: + strcpy(pBuffer,"EL734__BAD_RECV_PIPE"); + break; + case -20: + strcpy(pBuffer,"EL734__BAD_RECV_UNKN"); + break; + case -22: + strcpy(pBuffer,"EL734__BAD_RECVLEN"); + break; + case -24: + strcpy(pBuffer,"EL734__BAD_RECV1"); + break; + case -26: + strcpy(pBuffer,"EL734__BAD_RECV1_NET"); + break; + case -25: + strcpy(pBuffer,"EL734__BAD_RECV1_PIPE"); + break; + case -27: + strcpy(pBuffer,"EL734__BAD_RNG"); + break; + case -13: + strcpy(pBuffer,"EL734__BAD_SEND"); + break; + case -14: + strcpy(pBuffer,"EL734__BAD_SEND_PIPE"); + break; + case -15: + strcpy(pBuffer,"EL734__BAD_SEND_NET"); + break; + case -16: + strcpy(pBuffer,"EL734__BAD_SEND_UNKN"); + break; + case -12: + strcpy(pBuffer,"EL734__BAD_SENDLEN"); + break; + case -7: + strcpy(pBuffer,"EL734__BAD_SOCKET"); + break; + case -1: + strcpy(pBuffer,"EL734__BAD_TMO"); + break; + default: + strcpy(pBuffer,"Unknown EL734 error"); + break; + } + } diff --git a/hardsup/el737_def.h b/hardsup/el737_def.h new file mode 100644 index 0000000..997e6f3 --- /dev/null +++ b/hardsup/el737_def.h @@ -0,0 +1,67 @@ +#ifndef _el737_def_ +#define _el737_def_ +/*----------------------------------------- [...LIB.SINQ]EL737_DEF.H Ident V02J +** Definitions for the EL737 Neutron Counter +** +** On UNIX systems, this file is located in /public/lib/include +** On VMS systems, this file is a module in mad_lib:sinq_c.tlb +*/ +#include +#include + +#ifndef OffsetOf +#define OffsetOf(type, identifier) ((size_t)(&((type*) NULL)->identifier)) +#endif + +#ifndef _EL737_errcodes_ +#define _EL737_errcodes_ +#include +#endif + +enum EL737_States { + UNKNOWN = -2, + OFFLINE = -1, + MS = 0x0, + PTS = 0x1, + PCS = 0x2, + LRTS = 0x5, + LRCS = 0x6, + PTSP = 0x9, + PCSP = 0xA, + LRTSP = 0xD, + LRCSP = 0xE}; + +enum EL737_Consts { + VMECNT__PRESET_COUNT, + VMECNT__PRESET_TIME, + + VMECNT__FULL, + VMECNT__SHORT, + VMECNT__INCR}; +/* +** Structure to which the EL737_Open handle points. +*/ + struct EL737info { + struct AsynSrv__info asyn_info; /* Contains skt, host, port & chan */ + int c5, c6, c7, c8; + struct RS__MsgStruct to_host; + struct RS__RespStruct from_host; + }; +/* +** Structure holding everything that is known about a VME Neutron Counter. +*/ + struct Counter_State { + int state; /* RS */ + char timer[16]; /* RT \ RA */ + int cntrs[8]; /* RC 1 ... RC 8 / */ + char rates[8][16]; /* RR 1 ... RR 8 */ + char thresh_integ_time[8][16]; /* DI 1 ... DI 8 */ + char rate_integ_time[16]; /* DT */ + int analog_indx; /* DA */ + int thresh_indx; /* DR */ + char threshes[8][16]; /* DL 1 ... DL 8 */ + int mon_preset; /* MP */ + char timer_preset[16]; /* TP */ + }; +/*----------------------------------------------------- End of EL737_DEF.H --*/ +#endif /* _el737_def_ */ diff --git a/hardsup/el737_errcodes.h b/hardsup/el737_errcodes.h new file mode 100644 index 0000000..600ff02 --- /dev/null +++ b/hardsup/el737_errcodes.h @@ -0,0 +1,27 @@ +/* +** TAS_SRC:[LIB]EL737_ERRCODES.H +** +** Include file generated from EL737_ERRCODES.OBJ +** +** 29-AUG-2000 09:49:21.56 +*/ + +#define EL737__NO_VALUE 0x8668094 +#define EL737__NO_SOCKET 0x866808C +#define EL737__NOT_OPEN 0x8668084 +#define EL737__FORCED_CLOSED 0x866807C +#define EL737__CNTR_OVFL 0x8668074 +#define EL737__BAD_TMO 0x866806C +#define EL737__BAD_SOCKET 0x8668064 +#define EL737__BAD_PAR 0x866805C +#define EL737__BAD_OVFL 0x8668054 +#define EL737__BAD_OFL 0x866804C +#define EL737__BAD_MALLOC 0x8668044 +#define EL737__BAD_LOC 0x866803C +#define EL737__BAD_ILLG 0x8668034 +#define EL737__BAD_DEV 0x866802C +#define EL737__BAD_CNTR 0x8668024 +#define EL737__BAD_CMD 0x866801C +#define EL737__BAD_BSY 0x8668014 +#define EL737__BAD_ASYNSRV 0x866800C +#define EL737__FACILITY 0x866 diff --git a/hardsup/el737_utility.c b/hardsup/el737_utility.c new file mode 100644 index 0000000..383ea83 --- /dev/null +++ b/hardsup/el737_utility.c @@ -0,0 +1,1742 @@ +#define ident "2B03" +#ifdef VAXC +#module EL737_Utility ident +#endif +#ifdef __DECC +#pragma module EL737_Utility ident +#endif +/* +** +--------------------------------------------------------------+ +** | Paul Scherrer Institute | +** | Department ASQ | +** | | +** | This software may be used freely by non-profit organizations.| +** | It may be copied provided that the name of P.S.I. and of the | +** | author is included. Neither P.S.I. nor the author assume any | +** | responsibility for the use of this software outside of P.S.I.| +** +--------------------------------------------------------------+ +** +** Module Name . . . . . . . . : [...LIB.SINQ]EL737_Utility.C +** +** Author . . . . . . . . . . : D. Maden +** Date of creation . . . . . . : Apr 1996 +** +** To compile this module, use: + + $ import tasmad + $ define/group sinq_c_tlb mad_lib:sinq_c.tlb + $ cc /debug /noopt /obj=[]EL737_Utility - + tasmad_disk:[mad.psi.lib.sinq]EL737_Utility + - + sinq_c_tlb/lib + +** To include this module in SINQ.OLB, use: + + $ import tasmad + $ define/group sinq_c_tlb mad_lib:sinq_c.tlb + $ + $ define/group sinq_olb mad_lib:sinq_dbg.olb + $ @tasmad_disk:[mad.lib.sinq]sinq_olb EL737_Utility debug + $ + $ define/group sinq_olb mad_lib:sinq.olb + $ @tasmad_disk:[mad.lib.sinq]sinq_olb EL737_Utility +** +** Updates: +** 1A01 2-Nov-1995 DM. Initial version. +** 1B01 21-Mar-1996 DM. Move from DELTAT.OLB to SINQ.OLB. +** 1C01 16-Jul-1997 DM. Add code for EL737_Pause +** 2A01 6-Aug-1997 DM. Cope with new RA response format (= timer first) +** Add EL737_GetLongStatus. +** 2B01 5-Aug-1998 DM. Put messages into a .MSG file. +** 2B02 22-Apr-1999 DM. Add EL737_GetThresh and EL737_SetThresh. +**============================================================================ +** The entry points included in this module are described below. Prototypes +** can be defined via: +** +** #include +** +** EL737_Close - Close a connection to an EL737 counter. +** EL737_Config - Configure a connection to an EL737 counter. +** EL737_Continue - Continue a measurement with an EL737 counter. +** EL737_EnableThresh - Enable/disable threshold monitoring. +** EL737_ErrInfo - Return detailed status from last operation. +** EL737_GetMonIntegTime - Get Monitor Integration Time (DI register). +** EL737_GetRateIntegTime - Get Rate Integration Time (DT register). +** EL737_GetStatus - Get 4 counters and counter status. +** EL737_GetStatusExtra - Get counters 5 to 8. +** EL737_GetThresh - Get threshold monitoring status. +** EL737_Open - Open a connection to an EL737 counter. +** EL737_Pause - Pause a measurement with an EL737 counter. +** EL737_SendCmnd - Send a command to RS232C server. +** EL737_SetErrcode - Set up EL737_errcode. +** EL737_SetThresh - Set threshold monitoring level. +** EL737_StartCnt - Start a preset cnt measurement with an EL737. +** EL737_StartTime - Start a preset time measurement with an EL737. +** EL737_Stop - Stop a measurement with an EL737 counter. +** EL737_StopFast - Same as EL737_Stop but no registers are returned. +** EL737_WaitIdle - Wait till status goes to zero. +**--------------------------------------------------------------------- +** int EL737_Close (&handle, force_flag) +** ----------- +** Input Args: +** int force_flag - if non-zero, all connections using the same socket +** will also be closed (this gets AsynSrv_Close to +** actually close the socket and is needed for error +** recovery operations). +** Output Args: +** none +** Modified Args: +** void **handle - The pointer to the structure returned by EL737_Open. +** On return, the pointer is set to NULL. +** Return status: +** True always (error returns from close and free are not checked). +** Routines called: +** AsynSrv_Close +** Description: +** The routine calls AsynSrv_Close to close the connection to the RS232C +** server. If 'force_flag' is non-zero, all other connections to the +** RS232C server which use the same socket will also be closed. +** +** The 'force_flag' can be useful in error recovery situations. The AsynSrv +** utility operates by only opening a socket for each separate combination +** of host/port. Hence, if several connections are open to a server, +** then calling EL737_Close doesn't actually close the socket until all +** connections have been closed. In the situation where an error has been +** detected, it is often desirable to close and re-open the socket as part +** of the recovery procedure. Calling EL737_Close with 'force_flag' +** non-zero will force the socket to be closed and will mark all other +** connections using this socket so that they will be informed of the +** event when they next call try to be used. +** +** Note: The force-close action is effected by the AsynSrv package. A +** force-close will thus also close any connections to other +** RS-232-C devices (e.g. EL734 motors) on the same server. +**------------------------------------------------------------------------- +** int EL737_Config (&handle, &par_id, par_val, ...) +** ------------ +** Input Args: +** char* par_id - Text string identifying the next argument (see below). +** NULL indicates the end of the argument list. +** par_val - The value to set for the argument. The type of the +** argument can depend on par_id. +** Output Args: +** none +** Modified Args: +** void **handle - The pointer to the structure returned by EL737_Open. +** It is used to hold the config info for the connection. +** Return status: +** True if no problems detected, otherwise False and EL737_errcode +** is set to indicate the nature of the problem as follows: +** EL737__BAD_PAR --> Unrecognised par_id or msecTmo < 100 or +** msecTmo > 999'999 or bad eot or .. +** Routines called: +** none +** Description: +** The routine sets values in the EL737info data structure. Values which +** may be taken by par_id (warning -- par_id is case-sensitive) and the +** corresponding variable type of par_val are: +** +** "msecTmo" int The time-out response for commands sent to +** the EL737. The valid range is 100 to +** 999'999. Default is 10'000. +** "eot" char* The expected terminators in responses to +** commands sent to the EL737. The first +** character specifies the number of +** terminators (max=3). Default is "1\r". +**------------------------------------------------------------------------- +** int EL737_Continue (&handle, &status) +** -------------- +** Input Args: +** void **handle - The pointer to the structure returned by EL737_Open. +** Output Args: +** int *status - The status (RS) of the counter after the CO cmnd. +** Modified Args: +** none +** Return status: +** True if no problems detected, otherwise False and EL737_ErrInfo +** can be called to identify the problem. Values of Errcode set by +** EL737_Continue are (other values may be set by the called routines): +** EL737__OFL, __BAD_CMD, __BAD_TMO, __BAD_ASYNSRV --> see EL737_Open. +** EL737__BAD_ILLG --> the response to the RS command was probably not +** an integer. This could happen if there is noise +** on the RS232C connection to the EL737. +** If an error is detected, *status is set to 0. +** Routines called: +** AsynSrv_SendCmnds +** Description: +** EL737_Continue sends a CO command to the counter to get it to continue +** a paused measurement and then an RS command to read its status. +**------------------------------------------------------------------------- +** int EL737_EnableThresh (&handle, indx) +** ------------------ +** Input Args: +** void **handle - The pointer to the structure returned by EL737_Open. +** int indx - The number of the counter to select as the "active" +** threshold monitoring counter. If (indx == 0), +** threshold monitoring is disabled. +** Output Args: +** none +** Modified Args: +** none +** Return status: +** True if no problems detected, otherwise False and EL737_ErrInfo +** can be called to identify the problem. Values of Errcode set by +** EL737_EnableThresh are (other values may be set by the called routines): +** EL737__OFL, __BAD_CMD, __BAD_TMO, __BAD_ASYNSRV --> see EL737_Open. +** EL737__BAD_BSY --> "?2" response received - cntr probably in +** wrong state. +** EL737__BAD_PAR --> Bad parameter. Illegal value for or "?3" +** or "?4" response received. +** EL737__BAD_ILLG --> the response to the commands was illegal in +** some way. This could happen if there is noise +** on the RS232C connection to the EL737. +** Routines called: +** AsynSrv_SendCmnds +** Description: +** A "DR " command is sent to select counter to be the +** "active" threshold monitoring counter. A value of 0 causes +** threshold monitoring to be disabled in the EL737. The threshold +** for the selected counter will not be changed. If it is required +** to set a threshold value as well as enabling monitoring, it is +** simplest to use EL737_SetThresh. +**------------------------------------------------------------------------- +** void EL737_ErrInfo (&entry_txt_ptr, &errcode, &my_errno, &vaxc_errno) +** ------------- +** Input Args: +** None +** Output Args: +** char **entry_txt_ptr - Pointer to a text string giving the call stack +** at the time that the error was detected. +** int *errcode - An internal error code indicating the detected error. +** int *my_errno - Saved value of errno. +** int *vaxc_errno - Saved value of vaxc$errno (OpenVMS only). +** Modified Args: +** none +** Return status: +** none +** Routines called: +** none +** Description: +** Returns detailed status of the last operation. Once an error has been +** detected, the error status is frozen until this routine has been called. +**------------------------------------------------------------------------- +** int EL737_GetMonIntegTime (&handle, indx, &mon_integ_time) +** --------------------- +** Input Args: +** void **handle - The pointer to the structure returned by EL737_Open. +** int indx - The counter whose integ time is wanted. +** Output Args: +** float *mon_integ_time - The integration time used for monitoring +** the rate threshold. +** Modified Args: +** none +** Return status: +** True if no problems detected, otherwise False and EL737_ErrInfo +** can be called to identify the problem. Values of Errcode set by +** EL737_GetMonIntegTime are (other values may be set by the called +** routines): +** EL737__BAD_TMO, _LOC, _CMD, _OFL, _ADR, _ASYNSRV --> see EL737_Open. +** EL737__BAD_ILLG --> the response was probably not a floating point +** number. This could happen if there is noise +** on the RS232C connection to the EL737. +** If an error is detected, *mon_integ_time is set to 0.1. +** Routines called: +** AsynSrv_SendCmnds +** Description: +** The routine issues a "DI " command to the controller and +** analyses the result. +**------------------------------------------------------------------------- +** int EL737_GetRateIntegTime (&handle, &rate_integ_time) +** ---------------------- +** Input Args: +** void **handle - The pointer to the structure returned by EL737_Open. +** Output Args: +** float *rate_integ_time - The integration time used for calculating +** the rates. +** Modified Args: +** none +** Return status: +** True if no problems detected, otherwise False and EL737_ErrInfo +** can be called to identify the problem. Values of Errcode set by +** EL737_GetRateIntegTime are (other values may be set by the called +** routines): +** EL737__BAD_TMO, _LOC, _CMD, _OFL, _ADR, _ASYNSRV --> see EL737_Open. +** EL737__BAD_ILLG --> the response was probably not a floating point +** number. This could happen if there is noise +** on the RS232C connection to the EL737. +** If an error is detected, *rate_integ_time is set to 0.1. +** Routines called: +** AsynSrv_SendCmnds +** Description: +** The routine issues a DT command to the controller and +** analyses the result. +**------------------------------------------------------------------------- +** int EL737_GetStatus (&handle, &c1, &c2, &c3, &c4, &timer, &rs) +** --------------- +** Input Args: +** void **handle - The pointer to the structure returned by EL737_Open. +** Output Args: +** int *c1 - Counter 1 (Monitor). +** int *c2 - Counter 2 (Detector). +** int *c3 - Counter 3. +** int *c4 - Counter 4. +** float *timer - The measured time. +** int *rs - The counter status (RS command). +** Modified Args: +** none +** Return status: +** True if no problems detected, otherwise False and EL737_ErrInfo +** can be called to identify the problem. Values of Errcode set by +** EL737_GetStatus are (other values may be set by the called routines): +** EL737__BAD_TMO, _LOC, _CMD, _OFL, _ADR, _ASYNSRV --> see EL737_Open. +** EL737__BAD_ILLG --> one of the responses could probably not be +** decoded. This could happen if there is noise +** on the RS232C connection to the EL737. +** If an error is detected, all arguments are set to 0. +** Routines called: +** AsynSrv_SendCmnds +** Description: +** The routine issues an RA and RS command to the controller and +** analyses the result. If a syntax error is detected in either the RA +** or RS response, the routine tries up to 3 times to get a meaningful +** reply. +**------------------------------------------------------------------------- +** int EL737_GetStatusExtra (&handle, &c5, &c6, &c7, &c8) +** -------------------- +** Input Args: +** void **handle - The pointer to the structure returned by EL737_Open. +** Output Args: +** int *c5 - Counter 5. +** int *c6 - Counter 6. +** int *c7 - Counter 7. +** int *c8 - Counter 8. +** Modified Args: +** none +** Return status: +** True always. +** Routines called: +** None +** Description: +** The routine returns values for the counters 5, 6, 7 and 8 from the +** counter's structure. A successful call to any of the routines which +** return values for counters 1, 2, 3 and 4 must precede a call to +** EL737_GetStatusExtra. +**------------------------------------------------------------------------- +** int EL737_GetThresh (&handle, &indx, &val) +** --------------- +** Input Args: +** void **handle - The pointer to the structure returned by EL737_Open. +** Output Args: +** int *indx - The number of the threshold monitor counter. If =0, +** threshold monitoring is disabled. +** float *val - If *indx != 0, the value of the threshold. Otherwise, +** it is zero. +** Modified Args: +** none +** Return status: +** True if no problems detected, otherwise False and EL737_ErrInfo +** can be called to identify the problem. Values of Errcode set by +** EL737_GetThresh are (other values may be set by the called routines): +** EL737__BAD_TMO, _LOC, _CMD, _OFL, _ADR, _ASYNSRV --> see EL737_Open. +** EL737__BAD_ILLG --> one of the responses could probably not be +** decoded. This could happen if there is noise +** on the RS232C connection to the EL737. +** If an error is detected, all arguments are set to 0. +** Routines called: +** AsynSrv_SendCmnds +** Description: +** The routine issues a DR and, if threshold monitoring is enabled, +** a "DL " command to the controller and analyses the responses. +**------------------------------------------------------------------------- +** int EL737_Open (&handle, &host, port, chan) +** ---------- +** Input Args: +** char *host - Name of host offering the TCP/IP service. +** int port - Number of TCP/IP port of TCP/IP server. +** int chan - RS-232-C Channel number on the TCP/IP server. +** Output Args: +** void *handle - A pointer to a structure of type EL737info needed for +** subsequent calls to EL737_... routines. Buffer space +** for the structure is allocated dynamically. It gets +** released via a call to EL737_Close. +** Modified Args: +** none +** Return status: +** True if no problems detected, otherwise False. If False, EL737_ErrInfo +** can be called to identify the problem. Values of Errcode set by +** EL737_Open are (other values may be set by the called routines): +** EL737__BAD_TMO --> Time-out error ("?TMO" - this gets +** generated by the RS232C server). +** EL737__BAD_LOC --> EL737 off-line ("?OF"). This should not +** happen on calls to EL737_Open since it +** sends an "RMT 1" cmnd. +** EL737__BAD_CMD --> Syntax error ("?1"). This could be +** caused by noise in the RS-232-C +** transmission. +** EL737__BAD_OFL --> Connection to EL737 broken ("?OFL"). +** This can get generated by RS232C_SRV +** if, for example, the connection is via +** a terminal server and the terminal +** server loses power. +** EL737__BAD_ILLG --> Some other unrecognised response. This +** should never occur, of course! +** EL737__BAD_DEV --> Device doesn't seem to be an EL737. The +** response to the RA command was bad. +** EL737__BAD_MALLOC --> Call to "malloc" failed. +** EL737__BAD_ASYNSRV --> Call to AsynSrv_SendCmnds failed. Use +** AsynSrv_ErrInfo to get more details. +** Routines called: +** AsynSrv_Open, memory allocation routine "malloc" and AsynSrv_SendCmnds. +** Description: +** The routine opens a TCP/IP connection to a server offering the +** "RS-232-C" service for an EL737 Neutron Counter. "RMT 1" and +** "ECHO 2" commands are sent to ensure the device is on-line and an RA +** command is sent to ensure that an EL737 is being addressed. +**------------------------------------------------------------------------- +** int EL737_Pause (&handle, &status) +** ----------- +** Input Args: +** void **handle - The pointer to the structure returned by EL737_Open. +** Output Args: +** int *status - The status (RS) of the counter after the PS cmnd. +** Modified Args: +** none +** Return status: +** True if no problems detected, otherwise False and EL737_ErrInfo +** can be called to identify the problem. Values of Errcode set by +** EL737_Pause are (other values may be set by the called routines): +** EL737__OFL, __BAD_CMD, __BAD_TMO, __BAD_ASYNSRV --> see EL737_Open. +** EL737__BAD_ILLG --> the response to the RS command was probably not +** an integer. This could happen if there is noise +** on the RS232C connection to the EL737. +** If an error is detected, *status is set to 0. +** Routines called: +** AsynSrv_SendCmnds +** Description: +** EL737_Pause sends a PS command to the counter to get it to pause +** a measurement and then an RS command to read its status. +**------------------------------------------------------------------------- +** int EL737_SendCmnd (&handle, &cmnd, &rply, rply_size) +** -------------- +** Input Args: +** void **handle - The pntr to the structure returned by EL737_Open. +** char *cmnd - A command, terminated by NULL, for sending to the +** EL737 counter controller. The command must have +** any necessary \r character included. +** int rply_size - the size of the buffer. +** Output Args: +** char *rply - A buffer for receiving the reply. +** Modified Args: +** none +** Return status: +** True if no problems detected, otherwise False and errcode (see +** EL737_ErrInfo) is set to indicate the nature of the problem. +** EL737_errcode may be set as follows: +** EL737__BAD_ASYNSRV --> An error occurred in AsynSrv_Utility. +** Call AsynSrv_ErrInfo for more info. +** Routines called: +** AsynSrv_SendCmnds +** Description: +** The command is passed to AsynSrv_SendCmnds and the reply extracted. +**------------------------------------------------------------------------- +** int EL737_SetErrcode (&info_ptr, &response, &cmnd) +** ---------------- +** Set up EL737_errcode (for internal use only) +** Input Args: +** struct EL737info *info_ptr - The pntr to the structure returned by +** EL737_Open. +** char *response - The response received from a command. +** char *cmnd - The command which was sent. +** Output Args: +** none +** Modified Args: +** none +** Return status: +** The value of EL737_errcode. +** Routines called: +** none +** Description: +** The command checks *response for certain keywords and sets EL737_errcode +** accordingly. +**------------------------------------------------------------------------- +** int EL737_SetThresh (&handle, indx, val) +** --------------- +** Input Args: +** void **handle - The pointer to the structure returned by EL737_Open. +** int indx - The number of the counter whose threshold is to +** be set. If (indx == 0), threshold monitoring is +** disabled and val is not used. +** float val - The value of the threshold to be set. +** Output Args: +** none +** Modified Args: +** none +** Return status: +** True if no problems detected, otherwise False and EL737_ErrInfo +** can be called to identify the problem. Values of Errcode set by +** EL737_SetThresh are (other values may be set by the called routines): +** EL737__OFL, __BAD_CMD, __BAD_TMO, __BAD_ASYNSRV --> see EL737_Open. +** EL737__BAD_BSY --> "?2" response received - cntr probably in +** wrong state. +** EL737__BAD_PAR --> Bad parameter. Illegal value for or "?3" +** or "?4" response received. +** EL737__BAD_ILLG --> the response to the commands was illegal in +** some way. This could happen if there is noise +** on the RS232C connection to the EL737. +** Routines called: +** AsynSrv_SendCmnds, EL737_EnableThresh +** Description: +** a) If (indx == 0): EL737_SetThresh simply calls EL737_EnableThresh to +** send a "DR 0" command which will disable threshold +** monitoring by the counter. +** +** b) If (indx != 0): First of all, a "DL ||" command is sent +** to the counter to set the threshold for counter +** to the absolute value of . +** Then, if (val >= 0), EL737_EnableThresh is then called +** to select counter to be the "active" threshold +** monitoring counter. Otherwise, the "active" counter +** is not changed. +**------------------------------------------------------------------------- +** int EL737_StartCnt (&handle, count, &status) +** -------------- +** Input Args: +** void **handle - The pointer to the structure returned by EL737_Open. +** int count - The preset-count for the measurement. +** Output Args: +** int *status - The status (RS) of the counter after the MP cmnd. +** Modified Args: +** none +** Return status: +** True if no problems detected, otherwise False and EL737_ErrInfo +** can be called to identify the problem. Values of Errcode set by +** EL737_StartCnt are (other values may be set by the called routines): +** EL737__OFL, __BAD_CMD, __BAD_TMO, __BAD_ASYNSRV --> see EL737_Open. +** EL737__BAD_BSY --> "?2" response received - cntr probably in +** wrong state. +** EL737__BAD_PAR --> "?3" response received - bad parameter. +** EL737__BAD_ILLG --> the response to the RS command was probably not +** an integer. This could happen if there is noise +** on the RS232C connection to the EL737. +** If an error is detected, *status is set to 0. +** Routines called: +** AsynSrv_SendCmnds +** Description: +** EL737_StartCnt sends a MP command to the counter to get it to start +** a preset-count measurement and then an RS command to read its status. +**------------------------------------------------------------------------- +** int EL737_StartTime (&handle, timer, &status) +** --------------- +** Input Args: +** void **handle - The pointer to the structure returned by EL737_Open. +** float timer - The preset-time for the measurement. +** Output Args: +** int *status - The status (RS) of the counter after the TP cmnd. +** Modified Args: +** none +** Return status: +** True if no problems detected, otherwise False and EL737_ErrInfo +** can be called to identify the problem. Values of Errcode set by +** EL737_StartTime are (other values may be set by the called routines): +** EL737__OFL, __BAD_CMD, __BAD_TMO, __BAD_ASYNSRV --> see EL737_Open. +** EL737__BAD_BSY --> "?2" response received - cntr probably in +** wrong state. +** EL737__BAD_PAR --> "?3" response received - bad parameter. +** EL737__BAD_ILLG --> the response to the RS command was probably not +** an integer. This could happen if there is noise +** on the RS232C connection to the EL737. +** If an error is detected, *status is set to 0. +** Routines called: +** AsynSrv_SendCmnds +** Description: +** EL737_StartTime sends a TP command to the counter to get it to start +** a preset-time measurement and then an RS command to read its status. +**------------------------------------------------------------------------- +** int EL737_Stop (&handle, &c1, &c2, &c3, &c4, &timer, &rs) +** ---------- +** Input Args: +** void **handle - The pointer to the structure returned by EL737_Open. +** Output Args: +** int *c1 - Counter 1 (Monitor). +** int *c2 - Counter 2 (Detector). +** int *c3 - Counter 3. +** int *c4 - Counter 4. +** float *timer - The measured time. +** int *rs - The counter status (RS command) after the S cmnd. +** Modified Args: +** none +** Return status: +** True if no problems detected, otherwise False and EL737_ErrInfo +** can be called to identify the problem. Values of Errcode set by +** EL737_Stop are (other values may be set by the called routines): +** EL737__OFL, __BAD_CMD, __BAD_TMO, __BAD_ASYNSRV --> see EL737_Open. +** EL737__BAD_ILLG --> the response to the RA or RS command was +** probably not an integer. This could happen if +** there is noise on the RS232C connection to the +** EL737. +** If an error is detected, all output args are set to 0. +** Routines called: +** AsynSrv_SendCmnds +** Description: +** EL737_Stop sends an S command to the counter to get it to stop +** a measurement. It then calls EL737_GetStatus to read the registers +** and status. +**------------------------------------------------------------------------- +** int EL737_StopFast (&handle) +** -------------- +** Input Args: +** void **handle - The pointer to the structure returned by EL737_Open. +** Output Args: +** None +** Modified Args: +** none +** Return status: +** See EL737_Stop +** Routines called: +** AsynSrv_SendCmnds +** Description: +** EL737_StopFast sends an S command to the counter to get it to stop +** a measurement. Unlike EL737_Stop, the registers are not read out. +**------------------------------------------------------------------------- +** int EL737_WaitIdle (&handle, &c1, &c2, &c3, &c4, &timer) +** -------------- +** Input Args: +** void **handle - The pntr to the structure returned by EL737_Open. +** Output Args: +** int *c1 \ +** int *c2 \ +** int *c3 \ Same as EL737_GetStatus. +** int *c4 / +** float *timer / +** Modified Args: +** none +** Return status: +** True if no problems detected, otherwise False and Errcode (see +** EL737_ErrInfo) will have been set by the called routines to indicate +** the nature of the problem. +** Routines called: +** EL737_GetStatus +** Description: +** Routine EL737_GetStatus is called repeatedly at a predefined frequency +** until the RS register is zero. +**============================================================================*/ +/* +**--------------------------------------------------------------------------- +** Global Definitions +*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#ifdef __VMS + #include +#else + #include + #ifdef FORTIFY + #include + #endif +#endif +/*-----------------------------------------------------------------*/ +#include +#include +#include + +#define True 1 +#define False 0 +/*-------------------------------------------------------------------------- +** Global Variables +*/ + static int EL737_call_depth = 0; + static char EL737_routine[5][64]; + static int EL737_errcode = 0; + static int EL737_errno, EL737_vaxc_errno; +/* +**--------------------------------------------------------------------------- +** EL737_AddCallStack: Add a routine name to the call stack. +** This allows EL737_ErrInfo to generate a +** trace-back in case of error. +*/ + int EL737_AddCallStack ( +/* ================== +*/ struct EL737info *pntr, + char *name) { + + if (EL737_errcode != 0) return False; + + if (EL737_call_depth < 5) { + strcpy (EL737_routine[EL737_call_depth], name); + EL737_call_depth++; + } + + if (pntr == NULL) {EL737_errcode = EL737__NOT_OPEN; return False;} + + if (pntr->asyn_info.skt <= 0) { + memset (pntr->from_host.msg_size, + '0', sizeof (pntr->from_host.msg_size)); + EL737_errcode = (pntr->asyn_info.skt < 0) ? EL737__FORCED_CLOSED + : EL737__NO_SOCKET; + return False; + } + return True; + } +/* +**--------------------------------------------------------------------------- +** EL737_Close: Close a connection to an EL737 counter. +*/ + int EL737_Close ( +/* =========== +*/ void **handle, + int force_flag) { + + struct EL737info *info_ptr; + char buff[4]; + + info_ptr = (struct EL737info *) *handle; + if (info_ptr == NULL) return True; + + if (info_ptr->asyn_info.skt != 0) { + if (info_ptr->asyn_info.skt > 0) { + AsynSrv_Close (*handle, force_flag); + } + } + free (*handle); + *handle = NULL; + + return True; + } +/* +**--------------------------------------------------------------------------- +** EL737_Config: Configure a connection to an EL737 counter. +*/ + int EL737_Config ( +/* ============ +*/ void **handle, + ...) { + + char buff[16]; + va_list ap; /* Pointer to variable args */ + char *txt_ptr; + int intval; + struct EL737info *info_ptr; + /*---------------------------------------------- + */ + info_ptr = (struct EL737info *) *handle; + + if (!EL737_AddCallStack (info_ptr, "EL737_Config")) return False; + /*---------------------------------------------- + */ + va_start (ap, handle); /* Set up var arg machinery */ + txt_ptr = va_arg (ap, char *); /* Get pntr to first parameter ident */ + while (txt_ptr != NULL) { + if (strcmp (txt_ptr, "msecTmo") == 0) { + intval = va_arg (ap, int); + if ((intval < 100) || (intval > 999999)) { + EL737_errcode = EL737__BAD_PAR; return False; + } + sprintf (buff, "%04d", intval/100); /* Convert to ASCII as .. + ** .. deci-secs */ + memcpy (info_ptr->asyn_info.tmo, buff, 4); + }else if (strcmp (txt_ptr, "eot") == 0) { + txt_ptr = va_arg (ap, char *); + if (txt_ptr == NULL) { + EL737_errcode = EL737__BAD_PAR; return False; + } + memcpy (info_ptr->asyn_info.eot, "\0\0\0\0", 4); + switch (txt_ptr[0]) { + case '3': info_ptr->asyn_info.eot[3] = txt_ptr[3]; + case '2': info_ptr->asyn_info.eot[2] = txt_ptr[2]; + case '1': info_ptr->asyn_info.eot[1] = txt_ptr[1]; + case '0': + info_ptr->asyn_info.eot[0] = txt_ptr[0]; + break; + default: + EL737_errcode = EL737__BAD_PAR; return False; + } + }else { + EL737_errcode = EL737__BAD_PAR; return False; + } + txt_ptr = va_arg (ap, char *); /* Get pntr to next parameter ident */ + } + + if (EL737_errcode != 0) return False; + EL737_call_depth--; + return True; + } +/* +**--------------------------------------------------------------------------- +** EL737_Continue: Continue a measurement with an EL737 counter. +*/ + int EL737_Continue ( +/* ============== +*/ void **handle, + int *status) { + + int my_status; + struct EL737info *info_ptr; + char *rply_ptr0, *rply_ptr1; + /*---------------------------------------------- + */ + *status = 0; + info_ptr = (struct EL737info *) *handle; + + if (!EL737_AddCallStack (info_ptr, "EL737_Continue")) return False; + /*---------------------------------------------- + ** Send CO and RS cmnds to EL737 + */ + my_status = AsynSrv_SendCmnds (&info_ptr->asyn_info, + &info_ptr->to_host, &info_ptr->from_host, + "CO\r", "RS\r", NULL); + if (!my_status) { + EL737_errcode = EL737__BAD_ASYNSRV; + return False; + } + rply_ptr1 = NULL; + rply_ptr0 = AsynSrv_GetReply ( + &info_ptr->asyn_info, &info_ptr->from_host, NULL); + if (rply_ptr0 != NULL) rply_ptr1 = AsynSrv_GetReply ( + &info_ptr->asyn_info, &info_ptr->from_host, rply_ptr0); + + if (rply_ptr0 == NULL) rply_ptr0 = "?"; + if (rply_ptr1 == NULL) rply_ptr1 = "?"; + if ( ((*rply_ptr0 == '\0') || (*rply_ptr0 == '\r') ) && + (sscanf (rply_ptr1, "%d", status) == 1)) { + if (EL737_errcode != 0) return False; + EL737_call_depth--; + return True; + } + *status = 0; + EL737_SetErrcode (info_ptr, rply_ptr0, "CO\" or \"RS"); + return False; + } +/* +**--------------------------------------------------------------------------- +** EL737_EnableThresh: Enable/disable Threshold Monitoring. +*/ + int EL737_EnableThresh ( +/* ================== +*/ void **handle, + int indx) { + + int status; + char cmnd[20]; + struct EL737info *info_ptr; + char *rply_ptr0; + /*---------------------------------------------- + */ + info_ptr = (struct EL737info *) *handle; + + if (!EL737_AddCallStack (info_ptr, "EL737_EnableThresh")) return False; + + if ((indx < 0) || (indx > 8)) { + EL737_errcode = EL737__BAD_PAR; return False; + } + /*---------------------------------------------- + ** Send "DR " cmnd to EL737 to select the + ** "active" threshold rate counter. + */ + sprintf (cmnd, "DR %d\r", indx); + status = AsynSrv_SendCmnds (&info_ptr->asyn_info, + &info_ptr->to_host, &info_ptr->from_host, + cmnd, NULL); + if (!status) { + EL737_errcode = EL737__BAD_ASYNSRV; return False; + } + rply_ptr0 = AsynSrv_GetReply ( + &info_ptr->asyn_info, &info_ptr->from_host, NULL); + if (rply_ptr0 == NULL) rply_ptr0 = "?"; + if ( (*rply_ptr0 == '\0') || (*rply_ptr0 == '\r') ) { + EL737_call_depth--; + return True; + } + EL737_SetErrcode (info_ptr, rply_ptr0, cmnd); + return False; + } +/* +** ------------------------------------------------------------------------- +** EL737_ErrInfo: Return detailed status from last operation. +*/ + void EL737_ErrInfo ( +/* ============= +*/ char **entry_txt, + int *errcode, + int *my_errno, + int *vaxc_errno) { + + int i; + char buff[80]; + int asyn_errcode, asyn_errno, asyn_vaxerrno; + char* asyn_errtxt; + + if (EL737_call_depth <= 0) { + strcpy (EL737_routine[0], "EL737_no_error_detected"); + *errcode = 0; + *my_errno = 0; + *vaxc_errno = 0; + }else { + if (EL737_call_depth > 1) { /* Concatenate the names */ + for (i = 1; i < EL737_call_depth; i++) { + strcat (EL737_routine[0], "/"); + StrJoin (EL737_routine[0], sizeof (EL737_routine), + EL737_routine[0], EL737_routine[i]); + } + } + *errcode = EL737_errcode; + *my_errno = EL737_errno; + *vaxc_errno = EL737_vaxc_errno; + switch (EL737_errcode) { + case EL737__BAD_ASYNSRV: strcpy (buff, "/EL737__BAD_ASYNSRV"); break; + case EL737__BAD_BSY: strcpy (buff, "/EL737__BAD_BSY"); break; + case EL737__BAD_CMD: strcpy (buff, "/EL737__BAD_CMD"); break; + case EL737__BAD_CNTR: strcpy (buff, "/EL737__BAD_CNTR"); break; + case EL737__BAD_DEV: strcpy (buff, "/EL737__BAD_DEV"); break; + case EL737__BAD_ILLG: strcpy (buff, "/EL737__BAD_ILLG"); break; + case EL737__BAD_LOC: strcpy (buff, "/EL737__BAD_LOC"); break; + case EL737__BAD_MALLOC: strcpy (buff, "/EL737__BAD_MALLOC"); break; + case EL737__BAD_OFL: strcpy (buff, "/EL737__BAD_OFL"); break; + case EL737__BAD_OVFL: strcpy (buff, "/EL737__BAD_OVFL"); break; + case EL737__BAD_PAR: strcpy (buff, "/EL737__BAD_PAR"); break; + case EL737__BAD_SOCKET: strcpy (buff, "/EL737__BAD_SOCKET"); break; + case EL737__BAD_TMO: strcpy (buff, "/EL737__BAD_TMO"); break; + case EL737__CNTR_OVFL: strcpy (buff, "/EL737__CNTR_OVFL"); break; + case EL737__FORCED_CLOSED: strcpy (buff, "/EL737__FORCED_CLOSED"); break; + case EL737__NOT_OPEN: strcpy (buff, "/EL737__NOT_OPEN"); break; + case EL737__NO_SOCKET: strcpy (buff, "/EL737__NO_SOCKET"); break; + case EL737__NO_VALUE: strcpy (buff, "/EL737__NO_VALUE"); break; + default: sprintf (buff, "/EL737__unknown_err_code: %d", EL737_errcode); + } + StrJoin (EL737_routine[0], sizeof(EL737_routine), EL737_routine[0], buff); + } + AsynSrv_ErrInfo (&asyn_errtxt, &asyn_errcode, &asyn_errno, &asyn_vaxerrno); + if (asyn_errcode != 0) { + strcat (EL737_routine[0], "/"); + StrJoin (EL737_routine[0], sizeof(EL737_routine), + EL737_routine[0], asyn_errtxt); + } + *entry_txt = EL737_routine[0]; + EL737_call_depth = 0; + EL737_errcode = 0; + } +/* +**--------------------------------------------------------------------------- +** EL737_GetMonIntegTime: Get DI register value for a counter. +*/ + int EL737_GetMonIntegTime ( +/* ===================== +*/ void **handle, + int indx, + float *mon_integ_time) { + + int status; + char cmnd[20]; + struct EL737info *info_ptr; + char *rply_ptr0; + /*---------------------------------------------- + */ + *mon_integ_time = 0.1; + info_ptr = (struct EL737info *) *handle; + + if (!EL737_AddCallStack (info_ptr, "EL737_GetMonIntegTime")) return False; + /*---------------------------------------------- + ** Send "DI " cmnd to EL737 + */ + sprintf (cmnd, "DI %d\r", indx); + status = AsynSrv_SendCmnds (&info_ptr->asyn_info, + &info_ptr->to_host, &info_ptr->from_host, + cmnd, NULL); + if (!status) { + EL737_errcode = EL737__BAD_ASYNSRV; + return False; + }else { + rply_ptr0 = AsynSrv_GetReply ( + &info_ptr->asyn_info, &info_ptr->from_host, NULL); + if (rply_ptr0 == NULL) rply_ptr0 = "?"; + if (sscanf (rply_ptr0, "%f", mon_integ_time) == 1) { + if (EL737_errcode != 0) return False; + EL737_call_depth--; + return True; + } + *mon_integ_time = 0.1; + EL737_SetErrcode (info_ptr, rply_ptr0, cmnd); + return False; + } + } +/* +**--------------------------------------------------------------------------- +** EL737_GetRateIntegTime: Get DT register value. +*/ + int EL737_GetRateIntegTime ( +/* ====================== +*/ void **handle, + float *rate_integ_time) { + + int status; + struct EL737info *info_ptr; + char *rply_ptr0; + /*---------------------------------------------- + */ + *rate_integ_time = 0.1; + info_ptr = (struct EL737info *) *handle; + + if (!EL737_AddCallStack (info_ptr, "EL737_GetRateIntegTime")) return False; + /*---------------------------------------------- + ** Send DT cmnd to EL737 + */ + status = AsynSrv_SendCmnds (&info_ptr->asyn_info, + &info_ptr->to_host, &info_ptr->from_host, + "DT\r", NULL); + if (!status) { + EL737_errcode = EL737__BAD_ASYNSRV; + return False; + }else { + rply_ptr0 = AsynSrv_GetReply ( + &info_ptr->asyn_info, &info_ptr->from_host, NULL); + if (rply_ptr0 == NULL) rply_ptr0 = "?"; + if (sscanf (rply_ptr0, "%f", rate_integ_time) == 1) { + if (EL737_errcode != 0) return False; + EL737_call_depth--; + return True; + } + EL737_SetErrcode (info_ptr, rply_ptr0, "DT"); + *rate_integ_time = 0.1; + return False; + } + } +/* +**--------------------------------------------------------------------------- +** EL737_GetStatus: Get RA/RS register values. +*/ + int EL737_GetStatus ( +/* =============== +*/ void **handle, + int *c1, + int *c2, + int *c3, + int *c4, + float *timer, + int *rs) { + + int i, status, nvals; + struct EL737info *info_ptr; + char *rply_ptr, *p_cmnd; + /*---------------------------------------------- + */ + *c1 = *c2 = *c3 = *c4 = *rs = 0; *timer = 0.0; + info_ptr = (struct EL737info *) *handle; + + if (!EL737_AddCallStack (info_ptr, "EL737_GetStatus")) return False; + info_ptr->c5 = info_ptr->c6 = info_ptr->c7 = info_ptr->c8 = 0; + /*---------------------------------------------- + ** Send RA and RS cmnds to EL737. Since this routine gets + ** used such a lot, try up to 3 times if a syntax error in + ** the reply is detected. + */ + for (i = 0; i < 3; i++) { + status = AsynSrv_SendCmnds (&info_ptr->asyn_info, + &info_ptr->to_host, &info_ptr->from_host, + "RA\r", "RS\r", NULL); + if (!status) {EL737_errcode = EL737__BAD_ASYNSRV; return False;} + p_cmnd = "RA"; + rply_ptr = AsynSrv_GetReply ( + &info_ptr->asyn_info, &info_ptr->from_host, NULL); + if (rply_ptr != NULL) { + nvals = sscanf (rply_ptr, "%f %d %d %d %d %d %d %d %d", + timer, c1, c2, c3, c4, + &info_ptr->c5, &info_ptr->c6, + &info_ptr->c7, &info_ptr->c8); + if (nvals != 9) nvals = sscanf (rply_ptr, "%d %d %d %d %f", + c1, c2, c3, c4, timer); + if (nvals == 5) { + info_ptr->c5 = info_ptr->c6 = info_ptr->c7 = info_ptr->c8 = 0; + nvals = 9; + } + if (nvals == 9) { + p_cmnd = "RS"; + rply_ptr = AsynSrv_GetReply ( + &info_ptr->asyn_info, &info_ptr->from_host, rply_ptr); + if (rply_ptr != NULL) { + if (sscanf (rply_ptr, "%d", rs) == 1) { + EL737_call_depth--; + return True; + } + } + } + } + } + if (rply_ptr == NULL) rply_ptr = "?"; + EL737_SetErrcode (info_ptr, rply_ptr, p_cmnd); + *c1 = *c2 = *c3 = *c4 = *rs = 0; *timer = 0.0; + return False; + } +/* +**--------------------------------------------------------------------------- +** EL737_GetStatusExtra: Get values of extra counters. +*/ + int EL737_GetStatusExtra ( +/* ==================== +*/ void **handle, + int *c5, + int *c6, + int *c7, + int *c8) { + + struct EL737info *info_ptr; + /*---------------------------------------------- + */ + *c5 = *c6 = *c7 = *c8 = 0; + info_ptr = (struct EL737info *) *handle; + + if (!EL737_AddCallStack (info_ptr, "EL737_GetStatusExtra")) return False; + + *c5 = info_ptr->c5; + *c6 = info_ptr->c6; + *c7 = info_ptr->c7; + *c8 = info_ptr->c8; + + if (EL737_errcode != 0) return False; + EL737_call_depth--; + return True; + } +/* +**--------------------------------------------------------------------------- +** EL737_GetThresh: Get threshold monitoring status. +*/ + int EL737_GetThresh ( +/* =============== +*/ void **handle, + int *indx, + float *val) { + + int status, my_indx; + float my_val; + char cmnd[20]; + struct EL737info *info_ptr; + char *rply_ptr0; + /*---------------------------------------------- + */ + *indx = 0; + *val = 0.0; + info_ptr = (struct EL737info *) *handle; + + if (!EL737_AddCallStack (info_ptr, "EL737_GetThresh")) return False; + /*---------------------------------------------- + ** Send DR cmnd to EL737 to get the number of the + ** "active" threshold rate counter. + */ + status = AsynSrv_SendCmnds (&info_ptr->asyn_info, + &info_ptr->to_host, &info_ptr->from_host, + "DR\r", NULL); + if (!status) { + EL737_errcode = EL737__BAD_ASYNSRV; + return False; + } + rply_ptr0 = AsynSrv_GetReply ( + &info_ptr->asyn_info, &info_ptr->from_host, NULL); + if (rply_ptr0 == NULL) rply_ptr0 = "?"; + if ((sscanf (rply_ptr0, "%d", &my_indx) == 1) && + (my_indx >= 0) && + (my_indx <= 8)) { + *indx = my_indx; + if (my_indx != 0) { + /*---------------------------------------------- + ** Now send DL cmnd to EL737 to get the threshold value. + */ + sprintf (cmnd, "DL %d\r", my_indx); + status = AsynSrv_SendCmnds (&info_ptr->asyn_info, + &info_ptr->to_host, &info_ptr->from_host, + cmnd, NULL); + if (!status) { + EL737_errcode = EL737__BAD_ASYNSRV; + return False; + } + rply_ptr0 = AsynSrv_GetReply ( + &info_ptr->asyn_info, &info_ptr->from_host, NULL); + if (rply_ptr0 == NULL) rply_ptr0 = "?"; + if (sscanf (rply_ptr0, "%f", &my_val) == 1) { + *val = my_val; + EL737_call_depth--; + return True; + } + }else { + *val = 0.0; + EL737_call_depth--; + return True; + } + } + EL737_SetErrcode (info_ptr, rply_ptr0, cmnd); + return False; + } +/* +**--------------------------------------------------------------------------- +** EL737_Open: Open a connection to an EL737 counter. +*/ + int EL737_Open ( +/* ========== +*/ void **handle, + char *host, + int port, + int chan) { + + int status, c1, c2, c3, c4, nvals; + float timer; + struct EL737info *my_handle; + char tmo_save[4]; + char *rply_ptr; + char *rply_ptr0; + char *rply_ptr1; + char *rply_ptr2; +/*-------------------------------------------------------- +** Initialise the error info stack and pre-set the +** routine name (in case of error). +*/ + EL737_errcode = EL737_errno = EL737_vaxc_errno = 0; + strcpy (EL737_routine[0], "EL737_Open"); + EL737_call_depth = 1; +/*-------------------------------------------------------- +** Assume trouble +*/ + *handle = NULL; +/*-------------------------------------------------------- +** Reserve space for the data we need to store. +*/ + my_handle = (struct EL737info *) malloc (sizeof (*my_handle)); + if (my_handle == NULL) { + EL737_errcode = EL737__BAD_MALLOC; /* malloc failed!! */ + return False; + } +/*-------------------------------------------------------- +** Set up the connection +*/ + StrJoin (my_handle->asyn_info.host, sizeof (my_handle->asyn_info.host), + host, ""); + my_handle->asyn_info.port = port; + my_handle->asyn_info.chan = chan; + status = AsynSrv_Open (&my_handle->asyn_info); + if (!status) { + EL737_errcode = EL737__BAD_SOCKET; + GetErrno (&EL737_errno, &EL737_vaxc_errno); /* Save errno info */ + fprintf (stderr, "\nEL737_Open/AsynSrv_Open: " + "Failed to make connection.\n"); + free (my_handle); + return False; + } + + memcpy (tmo_save, my_handle->asyn_info.tmo, 4); + EL737_Config ((void *) &my_handle, + "msecTmo", 500, /* Set a short time-out initially since + ** there should be no reason for the RMT, + ** ECHO or RA commands to take very long + */ + "eot", "1\r", + NULL); + /* + ** Now ensure the EL737 is on-line. The first "RMT 1" command can + ** fail due to pending characters in the EL737 input buffer causing + ** the "RMT 1" to be corrupted. The response of the EL737 to this + ** command is ignored for this reason (but the AsynSrv_SendCmnds + ** status must be OK otherwise it indicates a network problem). + */ + status = AsynSrv_SendCmnds (&my_handle->asyn_info, + &my_handle->to_host, &my_handle->from_host, + "RMT 1\r", NULL); + if (status) { + status = AsynSrv_SendCmnds (&my_handle->asyn_info, + &my_handle->to_host, &my_handle->from_host, + "RMT 1\r", "ECHO 2\r", "RA\r", NULL); + } + if (!status) { + /* Some error occurred in AsynSrv_SendCmnds */ + EL737_errcode = EL737__BAD_ASYNSRV; + AsynSrv_Close (&my_handle->asyn_info, False); + free (my_handle); + return False; + }else { + /* Check the responses carefully. + */ + rply_ptr1 = rply_ptr2 = NULL; + rply_ptr0 = AsynSrv_GetReply ( + &my_handle->asyn_info, &my_handle->from_host, NULL); + if (rply_ptr0 != NULL) rply_ptr1 = AsynSrv_GetReply ( + &my_handle->asyn_info, &my_handle->from_host, rply_ptr0); + if (rply_ptr1 != NULL) rply_ptr2 = AsynSrv_GetReply ( + &my_handle->asyn_info, &my_handle->from_host, rply_ptr1); + + if (rply_ptr0 == NULL) rply_ptr0 = "?"; + if (rply_ptr1 == NULL) rply_ptr1 = "?"; + if (rply_ptr2 == NULL) rply_ptr2 = "?"; + + if (*rply_ptr1 == '?') rply_ptr0 = rply_ptr1; + if (*rply_ptr2 == '?') rply_ptr0 = rply_ptr2; + if (*rply_ptr0 != '?') { + nvals = sscanf (rply_ptr2, "%f %d %d %d %d %d %d %d %d", + &timer, &c1, &c2, &c3, &c4, + &my_handle->c5, &my_handle->c6, + &my_handle->c7, &my_handle->c8); + if (nvals != 9) nvals = sscanf (rply_ptr2, "%d %d %d %d %f", + &c1, &c2, &c3, &c4, &timer); + if (nvals == 5) { + my_handle->c5 = my_handle->c6 = my_handle->c7 = my_handle->c8 = 0; + nvals = 9; + } + if (nvals != 9) { + EL737_errcode = EL737__BAD_DEV; /* Device is not EL737 */ + AsynSrv_Close (&my_handle->asyn_info, False); + free (my_handle); + return False; + } + memcpy (my_handle->asyn_info.tmo, tmo_save, 4); /* Restore time-out */ + /* + ** The connection is complete. Pass the data structure + ** back to the caller as a handle. + */ + *handle = my_handle; + if (EL737_errcode != 0) return False; + EL737_call_depth--; + return True; + }else { + EL737_SetErrcode (my_handle, rply_ptr0, "RMT\", \"ECHO\" or \"RA"); + } + } + AsynSrv_Close (&my_handle->asyn_info, False); + free (my_handle); + return False; + } +/* +**--------------------------------------------------------------------------- +** EL737_Pause: Pause a measurement with an EL737 counter. +*/ + int EL737_Pause ( +/* =========== +*/ void **handle, + int *status) { + + int my_status; + struct EL737info *info_ptr; + char *rply_ptr0, *rply_ptr1; + /*---------------------------------------------- + */ + *status = 0; + info_ptr = (struct EL737info *) *handle; + + if (!EL737_AddCallStack (info_ptr, "EL737_Pause")) return False; + /*---------------------------------------------- + ** Send PS and RS cmnds to EL737 + */ + my_status = AsynSrv_SendCmnds (&info_ptr->asyn_info, + &info_ptr->to_host, &info_ptr->from_host, + "PS\r", "RS\r", NULL); + if (!my_status) { + EL737_errcode = EL737__BAD_ASYNSRV; + return False; + }else { + rply_ptr1 = NULL; + rply_ptr0 = AsynSrv_GetReply ( + &info_ptr->asyn_info, &info_ptr->from_host, NULL); + if (rply_ptr0 != NULL) rply_ptr1 = AsynSrv_GetReply ( + &info_ptr->asyn_info, &info_ptr->from_host, rply_ptr0); + + if (rply_ptr0 == NULL) rply_ptr0 = "?"; + if (rply_ptr1 == NULL) rply_ptr1 = "?"; + if ( ((*rply_ptr0 == '\0') || (*rply_ptr0 == '\r') ) && + (sscanf (rply_ptr1, "%d", status) == 1)) { + if (EL737_errcode != 0) return False; + EL737_call_depth--; + return True; + } + EL737_SetErrcode (info_ptr, rply_ptr0, "PS\" or \"RS"); + *status = 0; + return False; + } + } +/* +**--------------------------------------------------------------------------- +** EL737_SendCmnd - Send a command to RS232C server. +*/ + int EL737_SendCmnd ( +/* ============== +*/ void **handle, + char *cmnd, + char *rply, + int rply_size) { + + struct EL737info *info_ptr; + int my_status; + char *rply_ptr; + /*---------------------------------------------- + */ + info_ptr = (struct EL737info *) *handle; + + if (!EL737_AddCallStack (info_ptr, "EL737_SendCmnd")) return False; + /*---------------------------------------------- + ** Send command to EL737. + */ + my_status = AsynSrv_SendCmnds (&info_ptr->asyn_info, + &info_ptr->to_host, &info_ptr->from_host, + cmnd, NULL); + if (!my_status) { + EL737_errcode = EL737__BAD_ASYNSRV; return False; + }else { + rply_ptr = AsynSrv_GetReply ( + &info_ptr->asyn_info, &info_ptr->from_host, NULL); + if (rply_ptr == NULL) rply_ptr = "?"; + StrJoin (rply, rply_size, rply_ptr, ""); + } + + if (EL737_errcode != 0) return False; + EL737_call_depth--; + return True; + } +/* +**--------------------------------------------------------------------------- +** EL737_SetErrcode - Set up EL737_errcode +*/ + int EL737_SetErrcode ( +/* ================ +*/ struct EL737info *info_ptr, + char *response, + char *cmnd) { + + int status, s_len; + char *rply; + char tmo_save[4]; + char eot_save[4]; + + EL737_errcode = EL737__BAD_ILLG; + if (strcmp (response, "?OF" ) == 0) EL737_errcode = EL737__BAD_LOC; + if (strcmp (response, "?OFL") == 0) EL737_errcode = EL737__BAD_OFL; + if (strcmp (response, "?OV" ) == 0) EL737_errcode = EL737__BAD_OVFL; + if (strcmp (response, "?1" ) == 0) EL737_errcode = EL737__BAD_CMD; + if (strcmp (response, "?2" ) == 0) EL737_errcode = EL737__BAD_BSY; + if (strcmp (response, "?3" ) == 0) EL737_errcode = EL737__BAD_PAR; + if (strcmp (response, "?4" ) == 0) EL737_errcode = EL737__BAD_CNTR; + if (strcmp (response, "?5" ) == 0) EL737_errcode = EL737__NO_VALUE; + if (strcmp (response, "?6" ) == 0) EL737_errcode = EL737__CNTR_OVFL; + if (strncmp (response, "?TMO", 4) == 0) EL737_errcode = EL737__BAD_TMO; + + if ((EL737_errcode == EL737__BAD_ILLG) && (cmnd != NULL)) { + s_len = strlen (cmnd); + if (cmnd[s_len-1] == '\r') s_len--; + fprintf (stderr, " Unrecognised response to \"%.*s\" command: \"%s\"\n", + s_len, cmnd, response); + } + + return EL737_errcode; + } +/* +**--------------------------------------------------------------------------- +** EL737_SetThresh: Set threshold monitoring level. +*/ + int EL737_SetThresh ( +/* =============== +*/ void **handle, + int indx, + float val) { + + int status; + char cmnd[32]; + struct EL737info *info_ptr; + char *rply_ptr0; + /*---------------------------------------------- + */ + info_ptr = (struct EL737info *) *handle; + + if (!EL737_AddCallStack (info_ptr, "EL737_SetThresh")) return False; + + if ((indx < 0) || (indx > 8)) { + EL737_errcode = EL737__BAD_PAR; return False; + } + /*---------------------------------------------- + ** If is zero, simply call EL737_EnableThresh to + ** disable threshold monitoring by the counter. + */ + if (indx == 0) { + return EL737_EnableThresh (handle, 0); + } + /*---------------------------------------------- + ** Send "DR ||" cmnd to EL737 to set the + ** threshold for counter . + */ + sprintf (cmnd, "DL %d %.3f\r", indx, fabs (val)); + status = AsynSrv_SendCmnds (&info_ptr->asyn_info, + &info_ptr->to_host, &info_ptr->from_host, + cmnd, NULL); + if (!status) { + EL737_errcode = EL737__BAD_ASYNSRV; return False; + } + rply_ptr0 = AsynSrv_GetReply ( + &info_ptr->asyn_info, &info_ptr->from_host, NULL); + if (rply_ptr0 == NULL) rply_ptr0 = "?"; + if ( (*rply_ptr0 == '\0') || (*rply_ptr0 == '\r') ) { + if (val >= 0) return EL737_EnableThresh (handle, indx); + EL737_call_depth--; + return True; + } + EL737_SetErrcode (info_ptr, rply_ptr0, cmnd); + return False; + } +/* +**--------------------------------------------------------------------------- +** EL737_StartCnt: Start a preset cnt measurement with an EL737. +*/ + int EL737_StartCnt ( +/* ============== +*/ void **handle, + int count, + int *status) { + + int my_status; + char cmnd[20]; + struct EL737info *info_ptr; + char *rply_ptr0, *rply_ptr1; + /*---------------------------------------------- + */ + *status = 0; + info_ptr = (struct EL737info *) *handle; + + if (!EL737_AddCallStack (info_ptr, "EL737_StartCnt")) return False; + /*---------------------------------------------- + ** Send MP and RS cmnds to EL737 + */ + sprintf (cmnd, "MP %d\r", count); /* Encode an appropriate command */ + my_status = AsynSrv_SendCmnds (&info_ptr->asyn_info, /* Send it */ + &info_ptr->to_host, &info_ptr->from_host, + cmnd, "RS\r", NULL); + if (!my_status) { + EL737_errcode = EL737__BAD_ASYNSRV; + return False; + }else { + rply_ptr1 = NULL; + rply_ptr0 = AsynSrv_GetReply ( + &info_ptr->asyn_info, &info_ptr->from_host, NULL); + if (rply_ptr0 != NULL) rply_ptr1 = AsynSrv_GetReply ( + &info_ptr->asyn_info, &info_ptr->from_host, rply_ptr0); + + if (rply_ptr0 == NULL) rply_ptr0 = "?"; + if (rply_ptr1 == NULL) rply_ptr1 = "?"; + if ( ((*rply_ptr0 == '\0') || (*rply_ptr0 == '\r') ) && + (sscanf (rply_ptr1, "%d", status) == 1)) { + if (EL737_errcode != 0) return False; + EL737_call_depth--; + return True; + } + EL737_SetErrcode (info_ptr, rply_ptr0, cmnd); + *status = 0; + return False; + } + } +/* +**--------------------------------------------------------------------------- +** EL737_StartTime: Start a preset time measurement with an EL737. +*/ + int EL737_StartTime ( +/* =============== +*/ void **handle, + float timer, + int *status) { + + int my_status; + char cmnd[20]; + struct EL737info *info_ptr; + char *rply_ptr0, *rply_ptr1; + /*---------------------------------------------- + */ + *status = 0; + info_ptr = (struct EL737info *) *handle; + + if (!EL737_AddCallStack (info_ptr, "EL737_StartTime")) return False; + /*---------------------------------------------- + ** Send TP and RS cmnds to EL737 + */ + sprintf (cmnd, "TP %.2f\r", timer); /* Encode an appropriate command */ + my_status = AsynSrv_SendCmnds (&info_ptr->asyn_info, /* Send it */ + &info_ptr->to_host, &info_ptr->from_host, + cmnd, "RS\r", NULL); + if (!my_status) { + EL737_errcode = EL737__BAD_ASYNSRV; + return False; + }else { + rply_ptr1 = NULL; + rply_ptr0 = AsynSrv_GetReply ( + &info_ptr->asyn_info, &info_ptr->from_host, NULL); + if (rply_ptr0 != NULL) rply_ptr1 = AsynSrv_GetReply ( + &info_ptr->asyn_info, &info_ptr->from_host, rply_ptr0); + + if (rply_ptr0 == NULL) rply_ptr0 = "?"; + if (rply_ptr1 == NULL) rply_ptr1 = "?"; + if ( ((*rply_ptr0 == '\0') || (*rply_ptr0 == 'r') ) && + (sscanf (rply_ptr1, "%d", status) == 1)) { + if (EL737_errcode != 0) return False; + EL737_call_depth--; + return True; + } + EL737_SetErrcode (info_ptr, rply_ptr0, cmnd); + *status = 0; + return False; + } + } +/* +**--------------------------------------------------------------------------- +** EL737_Stop: stop a measurement with an EL737 counter. +*/ + int EL737_Stop ( +/* ========== +*/ void **handle, + int *c1, + int *c2, + int *c3, + int *c4, + float *timer, + int *rs) { + + int my_status, nvals; + struct EL737info *info_ptr; + char *rply_ptr0, *rply_ptr1, *rply_ptr2; + /*---------------------------------------------- + */ + *c1 = *c2 = *c3 = *c4 = *rs = 0; *timer = 0.0; + info_ptr = (struct EL737info *) *handle; + + if (!EL737_AddCallStack (info_ptr, "EL737_Stop")) return False; + /*---------------------------------------------- + ** Send S, RS and RA cmnds to EL737 + */ + my_status = AsynSrv_SendCmnds (&info_ptr->asyn_info, + &info_ptr->to_host, &info_ptr->from_host, + "S\r", "RS\r", "RA\r", NULL); + if (!my_status) { + EL737_errcode = EL737__BAD_ASYNSRV; + return False; + }else { + rply_ptr1 = rply_ptr2 = NULL; + rply_ptr0 = AsynSrv_GetReply ( + &info_ptr->asyn_info, &info_ptr->from_host, NULL); + if (rply_ptr0 != NULL) rply_ptr1 = AsynSrv_GetReply ( + &info_ptr->asyn_info, &info_ptr->from_host, rply_ptr0); + if (rply_ptr1 != NULL) rply_ptr2 = AsynSrv_GetReply ( + &info_ptr->asyn_info, &info_ptr->from_host, rply_ptr1); + + if (rply_ptr0 == NULL) rply_ptr0 = "?"; + if (rply_ptr1 == NULL) rply_ptr1 = "?"; + if (rply_ptr2 == NULL) rply_ptr2 = "?"; + + nvals = sscanf (rply_ptr2, "%f %d %d %d %d %d %d %d %d", + timer, c1, c2, c3, c4, + &info_ptr->c5, &info_ptr->c6, + &info_ptr->c7, &info_ptr->c8); + if (nvals != 9) nvals = sscanf (rply_ptr2, "%d %d %d %d %f", + c1, c2, c3, c4, timer); + if (nvals == 5) { + info_ptr->c5 = info_ptr->c6 = info_ptr->c7 = info_ptr->c8 = 0; + nvals = 9; + } + if ( ((*rply_ptr0 == '\0') || (*rply_ptr0 == '\r') )&& + (sscanf (rply_ptr1, "%d", rs) == 1) && + (nvals == 9)) { + if (EL737_errcode != 0) return False; + EL737_call_depth--; + return True; + } + if (*rply_ptr0 != '?') { + if (*rply_ptr1 == '?') rply_ptr0 = rply_ptr1; + if (*rply_ptr0 == '?') rply_ptr0 = rply_ptr2; + } + EL737_SetErrcode (info_ptr, rply_ptr0, "S\", \"RS\" or \"RA"); + *c1 = *c2 = *c3 = *c4 = *rs = 0; *timer = 0.0; + return False; + } + } +/* +**--------------------------------------------------------------------------- +** EL737_StopFast: stop a measurement with an EL737 counter. +*/ + int EL737_StopFast ( +/* ============== +*/ void **handle) { + + int my_status, nvals; + struct EL737info *info_ptr; + char *rply_ptr0; + /*---------------------------------------------- + */ + info_ptr = (struct EL737info *) *handle; + + if (!EL737_AddCallStack (info_ptr, "EL737_StopFast")) return False; + /*---------------------------------------------- + ** Send S cmnd to EL737 + */ + my_status = AsynSrv_SendCmnds (&info_ptr->asyn_info, + &info_ptr->to_host, &info_ptr->from_host, + "S\r", NULL); + if (!my_status) { + EL737_errcode = EL737__BAD_ASYNSRV; + return False; + }else { + rply_ptr0 = AsynSrv_GetReply ( + &info_ptr->asyn_info, &info_ptr->from_host, NULL); + + if (rply_ptr0 == NULL) rply_ptr0 = "?"; + + if ( (*rply_ptr0 == '\0' || (*rply_ptr0 == '\r') ) ) { + if (EL737_errcode != 0) return False; + EL737_call_depth--; + return True; + } + EL737_SetErrcode (info_ptr, rply_ptr0, "S"); + return False; + } + } +/* +**--------------------------------------------------------------------------- +** EL737_WaitIdle: Wait till RS goes to zero. +*/ + int EL737_WaitIdle ( +/* ============== +*/ void **handle, + int *c1, + int *c2, + int *c3, + int *c4, + float *timer) { +#ifdef __VMS +#include +#define hibernate lib$wait (0.25) +#else +#include +#include + struct timespec delay = {0, 250000000}; + struct timespec delay_left; +#ifdef LINUX +#define hibernate nanosleep(&delay, &delay_left) +#else +#define hibernate nanosleep_d9 (&delay, &delay_left) +#endif + +#endif + int my_rs; + struct EL737info *info_ptr; + /*---------------------------------------------- + */ + *c1 = *c2 = *c3 = *c4 = 0; *timer = 0.0; + info_ptr = (struct EL737info *) *handle; + + if (!EL737_AddCallStack (info_ptr, "EL737_WaitIdle")) return False; + /*---------------------------------------------- + ** Keep reading status till idle. + */ + while (EL737_GetStatus (handle, c1, c2, c3, c4, timer, &my_rs)) { + if (my_rs == 0) { + if (EL737_errcode != 0) return False; + EL737_call_depth--; + return True; + } + hibernate; + } + return False; /* Error detected in EL737_GetStatus */ + } +/*-------------------------------------------- End of EL737_Utility.C =======*/ diff --git a/hardsup/el737fix.h b/hardsup/el737fix.h new file mode 100644 index 0000000..4c7d0a1 --- /dev/null +++ b/hardsup/el737fix.h @@ -0,0 +1,33 @@ +/*--------------------------------------------------------------------------- + Fix file for David renaming lots of el734 error codes. + + Mark Koennecke, October 1998 +----------------------------------------------------------------------------*/ +#ifndef EL737FIX +#define EL737FIX +#include "asynsrv_errcodes.h" + +#define EL737__BAD_HOST ASYNSRV__BAD_HOST +#define EL737__BAD_BIND ASYNSRV__BAD_BIND +#define EL737__BAD_SENDLEN ASYNSRV__BAD_SEND_LEN +#define EL737__BAD_SEND ASYNSRV__BAD_SEND +#define EL737__BAD_SEND_PIPE ASYNSRV__BAD_SEND_PIPE +#define EL737__BAD_SEND_UNKN ASYNSRV__BAD_SEND_UNKN +#define EL737__BAD_RECV ASYNSRV__BAD_RECV +#define EL737__BAD_RECV_PIPE ASYNSRV__BAD_RECV_PIPE +#define EL737__BAD_RECV_NET ASYNSRV__BAD_RECV_NET +#define EL737__BAD_SEND_NET ASYNSRV__BAD_SEND_NET +#define EL737__BAD_RECV_UNKN ASYNSRV__BAD_RECV_UNKN +#define EL737__BAD_NOT_BCD ASYNSRV__BAD_NOT_BCD +#define EL737__BAD_RECVLEN ASYNSRV__BAD_RECV_LEN +#define EL737__BAD_FLUSH ASYNSRV__BAD_FLUSH +#define EL737__BAD_RECV1 ASYNSRV__BAD_RECV1 +#define EL737__BAD_RECV1_PIPE ASYNSRV__BAD_RECV1_PIPE +#define EL737__BAD_RECV1_NET ASYNSRV__BAD_RECV1_NET +#define EL737__BAD_CONNECT ASYNSRV__BAD_CONNECT +#define EL737__BAD_ID -99995 +#define EL737__BAD_SNTX -99991 +#define EL737__BAD_REPLY -99992 +#define EL737__BAD_ADR -99993 +#define EL737__BAD_RNG -99994 +#endif /* el734fix */ diff --git a/hardsup/el737tcl.c b/hardsup/el737tcl.c new file mode 100644 index 0000000..510b6c9 --- /dev/null +++ b/hardsup/el737tcl.c @@ -0,0 +1,400 @@ +/*-------------------------------------------------------------------------- + + Some code to make EL737 COUNTERS as used at SINQ available in TCL. + Just a wrapper around David Maden's COUNTER routines. + + You are free to use and modify this software for noncommercial + usage. + + No warranties or liabilities of any kind taken by me or my employer + + Mark Koennecke July 1996 +----------------------------------------------------------------------------*/ +#include "sinq_prototypes.h" +#include +#include +#include +/* +#include +*/ +#include +#include "el737_def.h" + +#define True 1 +#define False 0 + + typedef struct + { + void *pData; /* EL737 open struct */ + } EL737st; + + EXTERN int EL737Action(ClientData pDat, Tcl_Interp *i, int a, char *argv[]); + static void EL737Error2Text(char *pBuffer, int errcode); + +/*--------------------------------------------------------------------------- + Tcl has a high niceness level. It deletes a command properly when + exiting, reinitializing etc. I use this facility to kill off the + counter initialised in CterEL737. +---------------------------------------------------------------------------*/ +EXTERN void EL737Murder(ClientData pData) +{ + EL737st *pTa = (EL737st *)pData; + EL737_Close(&(pTa->pData)); + free(pData); +} +/*---------------------------------------------------------------------------- + CterEL737 is the main entry point for this stuff. It connects to a counter + and, on success, creates a new command with the name of the counter. + Syntax: + EL737 name host port channel +---------------------------------------------------------------------------*/ + +int CterEL737(ClientData clientData, Tcl_Interp *interp, + int argc, char *argv[]) +{ + int iRet; + EL737st *pEL737 = NULL; + int iPort, iChannel, iMotor; + char *pErr = NULL; + char pBueffel[80]; + + /* check arguments */ + if(argc < 5) + { + Tcl_AppendResult(interp, + " Insufficient arguments: CterEL737 name host port channel" + , (char *) NULL); + return TCL_ERROR; + } + + /* convert arguments */ + iRet = Tcl_GetInt(interp,argv[3],&iPort); + if(iRet == TCL_ERROR) + { + Tcl_AppendResult(interp,"Need integer value for port", + (char *)NULL); + return iRet; + } + + iRet = Tcl_GetInt(interp,argv[4],&iChannel); + if(iRet == TCL_ERROR) + { + Tcl_AppendResult(interp,"Need integer value for channel", + (char *)NULL); + return iRet; + } + + /* make a new pointer, initialise EL737st */ + pEL737 = (EL737st *)malloc(sizeof(EL737st)); + if(pEL737 ==NULL) + { + Tcl_AppendResult(interp,"No memory in EL734",NULL); + return TCL_ERROR; + } + + /* open the rotten Counter, finally */ + iRet = EL737_Open(&(pEL737->pData), argv[2],iPort,iChannel); + if(iRet) /* success */ + { + /* handle TCL, create new command: the Counter */ + Tcl_CreateCommand(interp,strdup(argv[1]),EL737Action, + (ClientData)pEL737,EL737Murder); + Tcl_AppendResult(interp,strdup(argv[1]),(char *)NULL); + return TCL_OK; + } + else + { + EL737_ErrInfo(&pErr,&iPort,&iChannel, &iMotor); + EL737Error2Text(pBueffel,iPort); + Tcl_AppendResult(interp,pBueffel,(char *) NULL); + free(pEL737); + return TCL_ERROR; + } +} +/*-------------------------------------------------------------------------- + + EL737 Action is the routine where commands send to the conter will + end up. + + Syntax: timer starts counter with a preset value + counter wait val for counts or time and does + monitor not return before finished + + timer starts counter with a preset value + counter start val for counts or time and + monitor returns immediatly + counter isDone returns True, false depending if + started run has ended or not + counter value gets counter values as a little list + consisting of: + { counts monitor time } + counter Stop forces counter to stop +----------------------------------------------------------------------------*/ +EXTERN int EL737Action(ClientData clientData, Tcl_Interp *interp, + int argc, char *argv[]) +{ + EL737st *pData = (EL737st *)clientData; + char pBueffel[132]; + char pNumBuf[20]; + char *pErr = NULL; + int iC1, iC2, iC3, iC4, iRS, iRet; + float fTime; + int iFlag = 0; + int iMode; + double dVal; + + /* obviously we need at least a keyword! */ + if(argc < 2) + { + Tcl_AppendResult(interp,"No keyword given",NULL); + return TCL_ERROR; + } + + /* get values out */ + if(strcmp(argv[1],"value") == 0) + { + iRet = EL737_GetStatus(&(pData->pData),&iC1, &iC2, &iC3, + &iC4,&fTime,&iRS); + if(!iRet) + { + EL737_ErrInfo(&pErr,&iC1,&iC2, &iC3); + EL737Error2Text(pBueffel,iC1); + Tcl_AppendResult(interp,pBueffel,(char *) NULL); + return TCL_ERROR; + } + sprintf(pNumBuf,"%d",iC2); + Tcl_AppendElement(interp,pNumBuf); + sprintf(pNumBuf,"%d",iC1); + Tcl_AppendElement(interp,pNumBuf); + sprintf(pNumBuf,"%f",fTime); + Tcl_AppendElement(interp,pNumBuf); + return TCL_OK; + } + + /* isDone ? */ + if(strcmp(argv[1],"isDone") == 0) + { + iRet = EL737_GetStatus(&(pData->pData),&iC1, &iC2, &iC3, + &iC4,&fTime,&iRS); + if(!iRet) + { + EL737_ErrInfo(&pErr,&iC1,&iC2, &iC3); + EL737Error2Text(pBueffel,iC1); + Tcl_AppendResult(interp,pBueffel,(char *) NULL); + return TCL_ERROR; + } + if(iRS == 0) /* done is true */ + { + sprintf(pNumBuf,"%d",True); + } + else + { + sprintf(pNumBuf,"%d",False); + } + Tcl_AppendResult(interp,pNumBuf,(char *) NULL); + return TCL_OK; + } + + /* actual counting neutrons in two different modes */ + if(strcmp(argv[1],"wait") == 0) + { + iFlag = 2; + } + if(strcmp(argv[1],"start") == 0) + { + iFlag = 1; + } + if(iFlag > 0) /* we need to count */ + { + if(argc < 4) /* not enough arguments */ + { + Tcl_AppendResult(interp,"Usage: ",argv[0],argv[1], + " timer or monitor val",NULL); + return TCL_ERROR; + } + + /* timer or monitor preset ? */ + if(strcmp(argv[2],"timer") == 0) + { + iMode = 1; + } + else if (strcmp(argv[2],"monitor") == 0) + { + iMode = 2; + } + else + { + Tcl_AppendResult(interp,"Usage: ",argv[0],argv[1], + " timer or monitor val",NULL); + return TCL_ERROR; + } + + /* get the preset value */ + iRet = Tcl_GetDouble(interp,argv[3],&dVal); + if(iRet == TCL_ERROR) + { + return TCL_ERROR; + } + + /* actual start collecting neutrons */ + if(iMode == 1) + { + iRet = EL737_StartTime(&(pData->pData),(float)dVal, + &iRS); + } + else + { + iRet = EL737_StartCnt(&(pData->pData),(int)dVal, + &iRS); + } + if(!iRet) + { + EL737_ErrInfo(&pErr,&iC1,&iC2, &iC3); + EL737Error2Text(pBueffel,iC1); + Tcl_AppendResult(interp,pBueffel,(char *) NULL); + return TCL_ERROR; + } + } /* end of count startup code */ + + /* if apropriate: wait */ + if(iFlag == 2) + { + iRet = EL737_WaitIdle(&(pData->pData),&iC1, &iC2, &iC3, + &iC4,&fTime); + if(!iRet) + { + EL737_ErrInfo(&pErr,&iC1,&iC2, &iC3); + EL737Error2Text(pBueffel,iC1); + Tcl_AppendResult(interp,pBueffel,(char *) NULL); + return TCL_ERROR; + } + return TCL_OK; + } + else if(iFlag == 1) + { + return TCL_OK; + } + + /* the stop command */ + if(strcmp(argv[1],"stop") == 0) + { + iRet = EL737_Stop(&(pData->pData),&iC1, &iC2, &iC3, + &iC4,&fTime,&iRS); + if(!iRet) + { + EL737_ErrInfo(&pErr,&iC1,&iC2, &iC3); + EL737Error2Text(pBueffel,iC1); + Tcl_AppendResult(interp,pBueffel,(char *) NULL); + return TCL_ERROR; + } + return TCL_OK; + } + + Tcl_AppendResult(interp," obscure command: ",argv[1], + " not understood by EL737 counter", NULL); + return TCL_ERROR; +} +/*--------------------------------------------------------------------------- + + EL737Error2Text converts between an EL734 error code to text +-----------------------------------------------------------------------------*/ + void EL737Error2Text(char *pBuffer, int iErr) + { + switch(iErr) + { + case -28: + strcpy(pBuffer,"EL737__BAD_ADR"); + break; + case -8: + strcpy(pBuffer,"EL737__BAD_OVFL"); + break; + case -30: + strcpy(pBuffer,"EL737__BAD_BSY"); + break; + case -3: + strcpy(pBuffer,"EL737__BAD_SNTX"); + break; + case -9: + strcpy(pBuffer,"EL737__BAD_CONNECT"); + break; + case -23: + strcpy(pBuffer,"EL737__BAD_FLUSH"); + break; + case -6: + strcpy(pBuffer,"EL734__BAD_DEV"); + break; + case -10: + strcpy(pBuffer,"EL737__BAD_ID"); + break; + case -5: + strcpy(pBuffer,"EL737__BAD_ILLG"); + break; + case -2: + strcpy(pBuffer,"EL737__BAD_LOC"); + break; + case -11: + strcpy(pBuffer,"EL737__BAD_MALLOC"); + break; + case -21: + strcpy(pBuffer,"EL737__BAD_NOT_BCD"); + break; + case -4: + strcpy(pBuffer,"EL737__BAD_OFL"); + break; + case -29: + strcpy(pBuffer,"EL737__BAD_PAR"); + break; + + case -17: + strcpy(pBuffer,"EL737__BAD_RECV"); + break; + case -19: + strcpy(pBuffer,"EL737__BAD_RECV_NET"); + break; + case -18: + strcpy(pBuffer,"EL737__BAD_RECV_PIPE"); + break; + case -20: + strcpy(pBuffer,"EL737__BAD_RECV_UNKN"); + break; + case -22: + strcpy(pBuffer,"EL737__BAD_RECVLEN"); + break; + case -24: + strcpy(pBuffer,"EL737__BAD_RECV1"); + break; + case -26: + strcpy(pBuffer,"EL737__BAD_RECV1_NET"); + break; + case -25: + strcpy(pBuffer,"EL737__BAD_RECV1_PIPE"); + break; + case -27: + strcpy(pBuffer,"EL737__BAD_RNG"); + break; + case -13: + strcpy(pBuffer,"EL737__BAD_SEND"); + break; + case -14: + strcpy(pBuffer,"EL737__BAD_SEND_PIPE"); + break; + case -15: + strcpy(pBuffer,"EL737__BAD_SEND_NET"); + break; + case -16: + strcpy(pBuffer,"EL737__BAD_SEND_UNKN"); + break; + case -12: + strcpy(pBuffer,"EL737__BAD_SENDLEN"); + break; + case -7: + strcpy(pBuffer,"EL737__BAD_SOCKET"); + break; + case -1: + strcpy(pBuffer,"EL737__BAD_TMO"); + break; + default: + strcpy(pBuffer,"Unknown EL737 error"); + break; + } + } diff --git a/hardsup/el755_def.h b/hardsup/el755_def.h new file mode 100644 index 0000000..8cfe339 --- /dev/null +++ b/hardsup/el755_def.h @@ -0,0 +1,31 @@ +#ifndef _el755_def_ +#define _el755_def_ +/*------------------------------------------------ EL755_DEF.H Ident V01C +** Definitions for the EL755 Magnet Power Supply Controller +** +** On UNIX systems, this file is located in /public/lib/include +** On VMS systems, this file is a module in mad_lib:sinq_c.tlb +*/ +#include +#include + +#ifndef OffsetOf +#define OffsetOf(type, identifier) ((size_t)(&((type*) NULL)->identifier)) +#endif + +#ifndef _EL755_errcodes_ +#define _EL755_errcodes_ +#include +#endif + +/* +** Structure to which the EL755_Open handle points. +*/ + struct EL755info { + struct AsynSrv__info asyn_info; /* Contains skt, host, port & chan */ + int index; + struct RS__MsgStruct to_host; + struct RS__RespStruct from_host; + }; +/*------------------------------------------------ End of EL755_DEF.H --*/ +#endif /* _el755_def_ */ diff --git a/hardsup/el755_errcodes.h b/hardsup/el755_errcodes.h new file mode 100644 index 0000000..3b9d236 --- /dev/null +++ b/hardsup/el755_errcodes.h @@ -0,0 +1,27 @@ +/* +** TAS_SRC:[LIB]EL755_ERRCODES.H +** +** Include file generated from EL755_ERRCODES.OBJ +** +** 29-AUG-2000 09:49:23.51 +*/ + +#define EL755__TURNED_OFF 0x8678094 +#define EL755__TOO_MANY 0x867808C +#define EL755__TOO_LARGE 0x8678084 +#define EL755__OVFLOW 0x867807C +#define EL755__OUT_OF_RANGE 0x8678074 +#define EL755__OFFLINE 0x867806C +#define EL755__NO_SOCKET 0x8678064 +#define EL755__NOT_OPEN 0x867805C +#define EL755__FORCED_CLOSED 0x8678054 +#define EL755__BAD_TMO 0x867804C +#define EL755__BAD_SOCKET 0x8678044 +#define EL755__BAD_PAR 0x867803C +#define EL755__BAD_OFL 0x8678034 +#define EL755__BAD_MALLOC 0x867802C +#define EL755__BAD_ILLG 0x8678024 +#define EL755__BAD_DEV 0x867801C +#define EL755__BAD_CMD 0x8678014 +#define EL755__BAD_ASYNSRV 0x867800C +#define EL755__FACILITY 0x867 diff --git a/hardsup/el755_errorlog.c b/hardsup/el755_errorlog.c new file mode 100644 index 0000000..5491d21 --- /dev/null +++ b/hardsup/el755_errorlog.c @@ -0,0 +1,26 @@ +#define ident "1A01" +#ifdef VAXC +#module EL755_ErrorLog ident +#endif +#ifdef __DECC +#pragma module EL755_ErrorLog ident +#endif + +#include + +/* +**-------------------------------------------------------------------------- +** EL755_ErrorLog: This routine is called by EL755 routines in +** the case of certain errors. It simply prints +** to stderr. The user should supply his own +** routine if he wishes to log these errors in +** some other way. +*/ + void EL755_ErrorLog ( +/* ============== +*/ char *routine_name, + char *text) { + + fprintf (stderr, "%s: %s\n", routine_name, text); + } +/*-------------------------------------------- End of EL755_ErrorLog.C =======*/ diff --git a/hardsup/el755_utility.c b/hardsup/el755_utility.c new file mode 100644 index 0000000..57cb021 --- /dev/null +++ b/hardsup/el755_utility.c @@ -0,0 +1,1445 @@ +#define ident "1A04" +#ifdef VAXC +#module EL755_Utility ident +#endif +#ifdef __DECC +#pragma module EL755_Utility ident +#endif +/* +** +--------------------------------------------------------------+ +** | Paul Scherrer Institute | +** | Department ASQ | +** | | +** | This software may be used freely by non-profit organizations.| +** | It may be copied provided that the name of P.S.I. and of the | +** | author is included. Neither P.S.I. nor the author assume any | +** | responsibility for the use of this software outside of P.S.I.| +** +--------------------------------------------------------------+ +** +** Module Name . . . . . . . . : TAS_SRC:[PSI.LIB.SINQ]EL755_Utility.C +** +** Author . . . . . . . . . . : D. Maden +** Date of creation . . . . . . : Sep 1998 +** +** To compile this module, use: + + $ import tasmad + $ define/group sinq_c_tlb mad_lib:sinq_c.tlb + $ cc /debug /noopt /obj=[]EL755_Utility - + tas_src:[psi.lib.sinq]EL755_Utility + - + sinq_c_tlb/lib + +** To include this module in SINQ.OLB, use: + + $ import tasmad + $ define/group sinq_c_tlb mad_lib:sinq_c.tlb + $ + $ define/group sinq_olb mad_lib:sinq_dbg.olb + $ @tasmad_disk:[mad.lib.sinq]sinq_olb EL755_Utility debug + $ + $ define/group sinq_olb mad_lib:sinq.olb + $ @tasmad_disk:[mad.lib.sinq]sinq_olb EL755_Utility +** +** Updates: +** 1A01 8-Sep-1998 DM. Initial version. +**============================================================================ +** The entry points included in this module are described below. Prototypes +** can be defined via: +** +** #include +** +** EL755_AddCallStack - Add a routine name to the call stack. +** EL755_Close - Close a connection to an EL755 controller. +** EL755_Config - Configure a connection to an EL755 controller. +** EL755_ErrInfo - Return detailed status from last operation. +** EL755_GetCurrents - Get the actual settings of the EL755 currents. +** EL755_Open - Open a connection to an EL755 controller. +** EL755_PutOffline - Put the EL755 off-line. +** EL755_PutOnline - Put the EL755 on-line. +** EL755_SendTillSameStr - Repeatedly send a command to EL755 controller +** until the same reply is received twice. +** EL755_SendTillSameVal - Repeatedly send a command to EL755 controller +** until the first token of the reply is the +** same fl.pnt. value twice in succession. +** EL755_SetCurrent - Set the EL755 current. +**--------------------------------------------------------------------- +** int EL755_AddCallStack (&info_pntr, &name) +** ------------------ +** Input Args: +** struct EL755info *info_pntr - Pointer to structure returned by +** EL755_Open. Note that the type of the pointer +** is "struct EL755info". +** char *name - The name of the routine to be added to the call stack. +** Output Args: +** none +** Modified Args: +** none +** Return status: +** True if no problems detected, otherwise False and EL755_errcode +** is set to indicate the nature of the problem as follows: +** EL755__NOT_OPEN --> there is no connection open to the EL755, +** i.e. info_pntr is NULL. +** EL755__FORCED_CLOSED --> the connection has been force-closed +** (probably as a result of an error +** on another device on the same host). +** EL755__NO_SOCKET --> The connection has no socket - probably +** it was closed after a previous error. +** Note that EL755_errcode may have been set prior to the call to +** EL755_AddCallStack. In this case, the routine simply returns False. +** Routines called: +** None +** Description: +** The routine is designed to simplify the building of the call-stack +** which is available to the user via EL755_ErrInfo if an error occurs. +** It is intended for internal use only. +** +** If an error has aready occurred prior to the call to EL755_AddCallStack +** (i.e. EL755_errcode is non-zero), the routine simply returns False +** to prevent redundant information being added to the stack. +** +** Otherwise, the caller's name is added to the stack and basic checks +** are made of the EL755info structure. +**--------------------------------------------------------------------- +** int EL755_Close (&handle, force_flag) +** ----------- +** Input Args: +** int force_flag - if non-zero, all connections using the same socket +** will also be closed (this gets AsynSrv_Close to +** actually close the socket and is needed for error +** recovery operations). +** Output Args: +** none +** Modified Args: +** void **handle - The pointer to the structure returned by EL755_Open. +** On return, the pointer is set to NULL. +** Return status: +** True always (error returns from close and free are not checked). +** Routines called: +** Socket library, "close" and memory release routine, "free". +** Description: +** The routine calls AsynSrv_Close to close the connection to the RS232C +** server. If 'force_flag' is non-zero, all other connections to the +** RS232C server which use the same socket will also be closed. +** +** The 'force_flag' can be useful in error recovery situations. The AsynSrv +** utility operates by only opening a socket for each separate combination +** of host/port. Hence, if several connections are open to a server, +** then calling EL755_Close doesn't actually close the socket until all +** connections have been closed. In the situation where an error has been +** detected, it is often desirable to close and re-open the socket as part +** of the recovery procedure. Calling EL755_Close with 'force_flag' +** non-zero will force the socket to be closed and will mark all other +** connections using this socket so that they will be informed of the +** event when they next call an AsynSrv routine. +** +** Note: The force-close action is effected by the AsynSrv package. A +** force-close will thus also close any connections to other +** RS-232-C devices (e.g. EL734 motors) on the same server. +**------------------------------------------------------------------------- +** void EL755_Config (&handle, &par_id, par_val, ...) +** ------------ +** Input Args: +** char* par_id - Text string identifying the next argument (see below). +** NULL indicates the end of the argument list. +** par_val - The value to set for the argument. The type of the +** argument can depend on par_id. +** Output Args: +** none +** Modified Args: +** void **handle - The pointer to the structure returned by EL755_Open. +** It is used to hold the config info for the connection. +** Return status: +** True if no problems detected, otherwise False and EL755_errcode +** is set to indicate the nature of the problem. Values of Errcode set by +** EL755_Config are (other values may be set by the called routines): +** EL755__BAD_PAR --> Unrecognised par_id or msecTmo < 100 or +** msecTmo > 999'999 or bad eot or .. +** Routines called: +** EL755_AddCallStack +** Description: +** The routine sets values in the EL755info data structure and may modify +** the state of the temperature controller. Values which may be taken by +** par_id (par_id is case-insensitive) and the corresponding variable +** type of par_val are: +** +** "msecTmo" int The time-out response for commands sent to +** the EL755. The valid range is 100 to +** 999'999. Default is 3'000. +** "eot" char* The expected terminators in responses to +** commands sent to the EL755. The first +** character specifies the number of +** terminators (max=3). Default is "1\n". +** "index" int The DAC index in the range 1 to 8 to be +** referenced via this EL755info structure. +**------------------------------------------------------------------------- +** void EL755_ErrInfo (&entry_txt_ptr, &errcode, &my_errno, &vaxc_errno) +** ------------- +** Input Args: +** None +** Output Args: +** char **entry_txt_ptr - Pointer to a text string giving the call stack +** at the time that the error was detected. +** int *errcode - An internal error code indicating the detected error. +** int *my_errno - Saved value of errno. +** int *vaxc_errno - Saved value of vaxc$errno (OpenVMS only). +** Modified Args: +** none +** Return status: +** none +** Routines called: +** none +** Description: +** Returns detailed status of the last operation. Once an error has been +** detected, the error status is frozen until this routine has been called. +**------------------------------------------------------------------------- +** int EL755_GetCurrents (&handle, &soll, &ist) +** ----------------- +** +** Input Args: +** void **handle - The pointer to the structure returned by EL755_Open +** Output Args: +** float *soll - the requested current. +** float *ist - the actual current. This may me different to *soll +** since the controller ramps to a new current. +** Modified Args: +** none +** Return status: +** True if no problems detected, otherwise False and EL755_ErrInfo +** can be called to identify the problem. Values of Errcode set by +** EL755_GetCurrents are (other values may be set by the called +** routines): +** EL755__BAD_ASYNSRV --> Call to AsynSrv_SendCmnds failed. Use +** AsynSrv_ErrInfo to get more details. +** EL755__BAD_ILLG --> The response to the "I" command was probably not +** two numbers. This could happen if there is +** noise on the RS232C connection to the EL755. +** EL755__BAD_TMO --> Time-out error ("?TMO" - this gets generated +** by the RS232C server). +** EL755__OFFLINE --> The EL755 is offline ("?OF"). +** EL755__TOO_MANY --> The command was repeated too many times +** and never received the same response +** on 2 consecutive occasions. +** +** If an error is detected, *soll and *ist are undefined. +** Routines called: +** EL755_AddCallStack, AsynSrv_SendCmnds +** Description: +** EL755_Getcurrents sends an "I" command to the controller to obtain the +** currents. The command is repeated until the "soll" value is twice the +** same. +** Note: If the power supply is off or not connected (response is +** "?power-supply OFF", then no error is indicated and *soll=*ist=0.0 +** is returned. +**------------------------------------------------------------------------- +** int EL755_Open (&handle, host, port, chan, indx) +** ---------- +** Input Args: +** char *host - Name of host offering the TCP/IP service. +** int port - Number of TCP/IP port of TCP/IP server. +** int chan - RS-232-C Channel number on the TCP/IP server. +** int indx - The DAC index in the range 1 to 8. This selects which +** of the 8 outputs from the EL755 are to be used. +** Output Args: +** void *handle - A pointer to a structure of type EL755info needed for +** subsequent calls to EL755_??? routines. +** Modified Args: +** none +** Return status: +** True if no problems detected, otherwise False. If False, EL755_ErrInfo +** can be called to identify the problem. Values of Errcode set by +** EL755_Open are (other values may be set by the called routines): +** EL755__BAD_MALLOC --> Call to "malloc" failed +** EL755__BAD_SOCKET --> Call to AsynSrv_Open failed. Use +** AsynSrv_ErrInfo to get more details. +** EL755__BAD_TMO --> Time-out error ("?TMO" - this gets +** generated by the RS232C server). +** EL755__BAD_OFL --> Connection to EL755 broken ("?OFL"). +** This can get generated by RS232C_SRV +** if, for example, the connection is via +** a terminal server and the terminal +** server loses power. +** EL755__BAD_DEV --> Device doesn't seem to be an EL755. The +** response to the "ID\r" command was bad. +** EL755__BAD_ILLG --> Some other unrecognised response. This +** should never occur, of course! +** EL755__BAD_ASYNSRV --> Call to AsynSrv_SendCmnds failed. Use +** AsynSrv_ErrInfo to get more details. +** Routines called: +** "calloc" - memory allocation routine. +** AsynSrv_Open +** AsynSrv_Close - called if an error detected after connection opened. +** EL755_Config +** EL755_SendTillSameStr +** Description: +** The routine opens a TCP/IP connection to a server offering the +** "RS-232-C" service for an EL755 Controller. "RMT 1", "ECHO 0" and +** "ID" commands are sent to ensure the device is an EL755 controller. +**------------------------------------------------------------------------- +** int EL755_PutOffline (&handle) +** ---------------- +** Send "ECHO 1" and "RMT 0" commands to EL755 server. +** Input Args: +** void **handle - The pntr to the structure returned by EL755_Open. +** Output Args: +** none +** Modified Args: +** none +** Return status: +** True if no problems detected, otherwise False and errcode (see +** EL755_ErrInfo) is set to indicate the nature of the problem. +** Values of Errcode set by EL755_PutOffline are (other values may be set +** by the called routines): +** EL755__BAD_ASYNSRV --> An error occurred in AsynSrv_Utility. +** Call AsynSrv_ErrInfo for more info. +** EL755__BAD_ILLG --> an unrecognised response. This +** should never occur, of course! +** Routines called: +** EL755_AddCallStack, AsynSrv_SendCmnds, AsynSrv_GetReply +** Description: +** The routine calls AsynSrv_SendCmnds to execute "RMT 1", "ECHO 1" +** and "RMT 0" commands. The replies are checked. +**------------------------------------------------------------------------- +** int EL755_PutOnline (&handle, echo) +** --------------- +** Send "RMT 1" and "ECHO x" commands to EL755 server. +** Input Args: +** void **handle - The pntr to the structure returned by EL755_Open. +** int echo - The value for the ECHO command. +** Output Args: +** none +** Modified Args: +** none +** Return status: +** True if no problems detected, otherwise False and errcode (see +** EL755_ErrInfo) is set to indicate the nature of the problem. +** Values of Errcode set by EL755_PutOnline are (other values may be set +** by the called routines): +** EL755__BAD_PAR --> "echo" is not 0, 1 or 2. +** EL755__BAD_ASYNSRV --> An error occurred in AsynSrv_Utility. +** Call AsynSrv_ErrInfo for more info. +** EL755__BAD_ILLG --> an unrecognised response. This +** should never occur, of course! +** Routines called: +** EL755_AddCallStack, AsynSrv_SendCmnds, AsynSrv_GetReply +** Description: +** The routine calls AsynSrv_SendCmnds to execute "RMT 1" and "ECHO x" +** commands. The replies are checked. +**------------------------------------------------------------------------- +** int EL755_SendTillSameStr (&handle, &cmnd, &rply, rply_len) +** --------------------- +** Input Args: +** char *cmnd - The command to be sent (incl. terminators). +** int rply_len - The size of . +** Output Args: +** char *rply - The response to . +** Modified Args: +** void **handle - The pointer to the structure returned by EL755_Open. +** Return status: +** True if no problems detected, otherwise False. If False, EL755_ErrInfo +** can be called to identify the problem. Values of Errcode set by +** EL755_SendTillSameStr are (other values may be set by the called routines): +** EL755__BAD_ASYNSRV --> Call to AsynSrv_SendCmnds failed. Use +** AsynSrv_ErrInfo to get more details. +** EL755__BAD_ILLG --> The response to the "I" command was probably not +** two numbers. This could happen if there is +** noise on the RS232C connection to the EL755. +** EL755__BAD_TMO --> Time-out error ("?TMO" - this gets +** generated by the RS232C server). +** EL755__BAD_CMD --> Bad command ("?syntax failure") +** EL755__BAD_OFL --> Connection to EL755 broken ("?OFL"). +** This can get generated by RS232C_SRV +** if, for example, the connection is via +** a terminal server and the terminal +** server loses power. +** EL755__TOO_MANY --> The command was repeated too many times +** and never received the same response +** on 2 consecutive occasions. +** EL755__OFFLINE --> The EL755 is offline ("?OF"). +** Routines called: +** EL755_AddCallStack, EL755_ErrorLog, AsynSrv_SendCmnds, AsynSrv_GetReply +** Description: +** The routine sends the specified command to the EL755 Controller and +** reads the response. The command is repeated up to 5 times until the same +** response is received twice in succession. +** Note: +** The error EL755__TOO_MANY could indicate that is not big enough +** to hold the complete reply. +**------------------------------------------------------------------------- +** int EL755_SendTillSameVal (&handle, &cmnd, &val) +** --------------------- +** Input Args: +** char *cmnd - The command to be sent (incl. terminators). +** Output Args: +** float *val - The response to . +** Modified Args: +** void **handle - The pointer to the structure returned by EL755_Open. +** Return status: +** True if no problems detected, otherwise False. If False, EL755_ErrInfo +** can be called to identify the problem. Values of Errcode set by +** EL755_SendTillSameVal are (other values may be set by the called +** routines): +** EL755__BAD_ASYNSRV --> Call to AsynSrv_SendCmnds failed. Use +** AsynSrv_ErrInfo to get more details. +** EL755__BAD_ILLG --> The response to the "I" command was probably not +** two numbers. This could happen if there is +** noise on the RS232C connection to the EL755. +** EL755__BAD_TMO --> Time-out error ("?TMO" - this gets +** generated by the RS232C server). +** EL755__BAD_CMD --> Bad command ("?syntax failure") +** EL755__BAD_OFL --> Connection to EL755 broken ("?OFL"). +** This can get generated by RS232C_SRV +** if, for example, the connection is via +** a terminal server and the terminal +** server loses power. +** EL755__TOO_MANY --> The command was repeated too many times +** and never received the same response +** on 2 consecutive occasions. +** EL755__OFFLINE --> The EL755 is offline ("?OF"). +** Routines called: +** EL755_AddCallStack, EL755_ErrorLog, AsynSrv_SendCmnds, AsynSrv_GetReply +** Description: +** The routine sends the specified command to the EL755 Controller and +** reads the response. The command is repeated up to 5 times until the +** first token is the same fl.pnt value twice in succession. +** Note 1: +** The error EL755__TOO_MANY could indicate that is not big enough +** to hold the complete reply. +** Note 2: If the power supply is off or not connected (response is +** "?power-supply OFF") then a zero value is returned and the +** return status is True +**------------------------------------------------------------------------- +** int EL755_SetCurrent (&handle, soll) +** ---------------- +** +** Input Args: +** void **handle - The pointer to the structure returned by EL755_Open +** float soll - the requested current. +** Output Args: +** none +** Modified Args: +** none +** Return status: +** True if no problems detected, otherwise False and EL755_ErrInfo +** can be called to identify the problem. Values of Errcode set by +** EL755_SetCurrent are (other values may be set by the called +** routines): +** EL755__TURNED_OFF --> The EL755 power supply on this channel is +** turned off ("?power-supply OFF"). +** EL755__OUT_OF_RANGE --> The set value is out of +** range ("?value out of range"). +** EL755__TOO_LARGE --> The set value is too +** large ("?current limitation"). +** EL755__BAD_ILLG --> the response to the first "I" command was +** not null (indicating that the command was +** rejected) or the response to the second +** "I" command was probably not two numbers. +** This could happen if there is noise +** on the RS232C connection to the EL755. +** Routines called: +** EL755_SendTillSameStr, EL755_GetCurrests +** Description: +** EL755_SetCurrent sends an "I" command to the controller to set the +** current for the DAC index selected for the handle. An "I" is sent +** to check that the value was sent correctly. +** Note: If the power supply is off or not connected (response is +** "?power-supply OFF") and soll == 0.0, then return status is True. +**============================================================================*/ +/* +**--------------------------------------------------------------------------- +** Global Definitions +*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#ifdef __VMS +#include +#else +#include +#endif +/*-----------------------------------------------------------------*/ +#include +#include +#include + +#define True 1 +#define False 0 +#define NIL '\0' +/*-------------------------------------------------------------------------- +** Global Variables +*/ + static int EL755_call_depth = 0; + static char EL755_routine[5][64]; + static int EL755_errcode = 0; + static int EL755_errno, EL755_vaxc_errno; +/* +**--------------------------------------------------------------------------- +** EL755_AddCallStack: Add a routine name to the call stack. +** This allows EL755_ErrInfo to generate a +** trace-back in case of error. +*/ + int EL755_AddCallStack ( +/* ================== +*/ struct EL755info *pntr, + char *name) { + + if (EL755_errcode != 0) return False; + + if (EL755_call_depth < 5) { + StrJoin (EL755_routine[EL755_call_depth], sizeof (EL755_routine[0]), + name, ""); + EL755_call_depth++; + } + + if (pntr == NULL) {EL755_errcode = EL755__NOT_OPEN; return False;} + + if (pntr->asyn_info.skt <= 0) { + memset (pntr->from_host.msg_size, + '0', sizeof (pntr->from_host.msg_size)); + EL755_errcode = (pntr->asyn_info.skt < 0) ? EL755__FORCED_CLOSED + : EL755__NO_SOCKET; + return False; + } + return True; + } +/* +**--------------------------------------------------------------------------- +** EL755_Close: Close a connection to an EL755 controller. +*/ + int EL755_Close ( +/* =========== +*/ void **handle, + int force_flag) { + + struct EL755info *info_ptr; + char buff[4]; + + info_ptr = (struct EL755info *) *handle; + if (info_ptr == NULL) return True; + + if (info_ptr->asyn_info.skt != 0) { + if (info_ptr->asyn_info.skt > 0) { + AsynSrv_Close (*handle, force_flag); + } + } + free (*handle); + *handle = NULL; + + return True; + } +/* +**--------------------------------------------------------------------------- +** EL755_Config: Configure a connection to an EL755 controller. +*/ + int EL755_Config ( +/* ============ +*/ void **handle, + ...) { + + struct EL755info *info_ptr; + char buff[80], rply[256], my_txt[16]; + va_list ap; /* Pointer to variable args */ + char *txt_ptr; + int intval, my_txt_l; + /*---------------------------------------------- + */ + info_ptr = (struct EL755info *) *handle; + + if (!EL755_AddCallStack (info_ptr, "EL755_Config")) return False; + /*---------------------------------------------- + */ + va_start (ap, handle); /* Set up var arg machinery */ + txt_ptr = va_arg (ap, char *); /* Get pntr to first parameter ident */ + while (txt_ptr != NULL) { + my_txt_l = sizeof (my_txt); + StrEdit (my_txt, txt_ptr, "lowercase", &my_txt_l); + /*------------------------------------*/ + if (strcmp (my_txt, "msectmo") == 0) { + intval = va_arg (ap, int); + if ((intval < 100) || (intval > 999999)) { + EL755_errcode = EL755__BAD_PAR; return False; + } + sprintf (buff, "%04d", intval/100); /* Convert to ASCII as .. + ** .. deci-secs */ + memcpy (info_ptr->asyn_info.tmo, buff, 4); + /*------------------------------------*/ + }else if (strcmp (my_txt, "eot") == 0) { + txt_ptr = va_arg (ap, char *); + if (txt_ptr == NULL) { + EL755_errcode = EL755__BAD_PAR; + return False; + } + memcpy (info_ptr->asyn_info.eot, "\0\0\0\0", 4); + switch (txt_ptr[0]) { + case '3': info_ptr->asyn_info.eot[3] = txt_ptr[3]; + case '2': info_ptr->asyn_info.eot[2] = txt_ptr[2]; + case '1': info_ptr->asyn_info.eot[1] = txt_ptr[1]; + case '0': + info_ptr->asyn_info.eot[0] = txt_ptr[0]; + break; + default: + EL755_errcode = EL755__BAD_PAR; + return False; + } + /*------------------------------------*/ + }else if (strcmp (txt_ptr, "index") == 0) { + intval = va_arg (ap, int); + if ((intval < 1) || (intval > 8)) { + EL755_errcode = EL755__BAD_PAR; + return False; + } + info_ptr->index = intval; + /*------------------------------------*/ + }else { + EL755_errcode = EL755__BAD_PAR; + return False; + } + /*------------------------------------*/ + txt_ptr = va_arg (ap, char *); /* Get pntr to next parameter ident */ + } + + if (EL755_errcode == 0) EL755_call_depth--; + return True; + } +/* +** ------------------------------------------------------------------------- +** EL755_ErrInfo: Return detailed status from last operation. +*/ + void EL755_ErrInfo ( +/* ============= +*/ char **entry_txt, + int *errcode, + int *my_errno, + int *vaxc_errno) { + + int i, j, k; + char buff[80]; + int asyn_errcode, asyn_errno, asyn_vaxerrno; + char* asyn_errtxt; + + if (EL755_call_depth <= 0) { + strcpy (EL755_routine[0], "EL755_no_error_detected"); + *errcode = 0; + *my_errno = 0; + *vaxc_errno = 0; + }else { + if (EL755_call_depth > 1) { /* Concatenate the names */ + for (i = 1; i < EL755_call_depth; i++) { + strcat (EL755_routine[0], "/"); + StrJoin (EL755_routine[0], sizeof (EL755_routine), + EL755_routine[0], EL755_routine[i]); + } + } + *errcode = EL755_errcode; + *my_errno = EL755_errno; + *vaxc_errno = EL755_vaxc_errno; + switch (EL755_errcode) { + case EL755__BAD_ASYNSRV: strcpy (buff, "/EL755__BAD_ASYNSRV"); break; + case EL755__BAD_CMD: + case EL755__BAD_DEV: strcpy (buff, "/EL755__BAD_DEV"); break; + case EL755__BAD_ILLG: strcpy (buff, "/EL755__BAD_ILLG"); break; + case EL755__BAD_MALLOC: strcpy (buff, "/EL755__BAD_MALLOC"); break; + case EL755__BAD_OFL: strcpy (buff, "/EL755__BAD_OFL"); break; + case EL755__BAD_PAR: strcpy (buff, "/EL755__BAD_PAR"); break; + case EL755__BAD_SOCKET: strcpy (buff, "/EL755__BAD_SOCKET"); break; + case EL755__BAD_TMO: strcpy (buff, "/EL755__BAD_TMO"); break; + case EL755__FORCED_CLOSED: strcpy (buff, "/EL755__FORCED_CLOSED"); break; + case EL755__NOT_OPEN: strcpy (buff, "/EL755__NOT_OPEN"); break; + case EL755__NO_SOCKET: strcpy (buff, "/EL755__NO_SOCKET"); break; + case EL755__OFFLINE: strcpy (buff, "/EL755__OFFLINE"); break; + case EL755__OUT_OF_RANGE: strcpy (buff, "/EL755__OUT_OF_RANGE"); break; + case EL755__OVFLOW: strcpy (buff, "/EL755__OVFLOW"); break; + case EL755__TOO_LARGE: strcpy (buff, "/EL755__TOO_LARGE"); break; + case EL755__TOO_MANY: strcpy (buff, "/EL755__TOO_MANY"); break; + case EL755__TURNED_OFF: strcpy (buff, "/EL755__TURNED_OFF"); break; + default: sprintf (buff, "/EL755__unknown_err_code: %d", EL755_errcode); + } + StrJoin (EL755_routine[0], sizeof(EL755_routine), EL755_routine[0], buff); + } + AsynSrv_ErrInfo (&asyn_errtxt, &asyn_errcode, &asyn_errno, &asyn_vaxerrno); + if (asyn_errcode != 0) { + strcat (EL755_routine[0], "/"); + StrJoin (EL755_routine[0], sizeof(EL755_routine), + EL755_routine[0], asyn_errtxt); + } + *entry_txt = EL755_routine[0]; + EL755_call_depth = 0; + EL755_errcode = 0; + } +/* +**--------------------------------------------------------------------------- +** EL755_GetCurrents: Get currents from EL755. +*/ + int EL755_GetCurrents ( +/* ================= +*/ void **handle, + float *soll, + float *ist) { + + int iret; + char cmnd[32]; + struct EL755info *info_ptr; + /*---------------------------------------------- + */ + *soll = *ist = 0.0; + info_ptr = (struct EL755info *) *handle; + + if (!EL755_AddCallStack (info_ptr, "EL755_GetCurrents")) return False; + /*---------------------------------------------- + ** Send I command to get EL755 currents. Repeat until + ** first value is same 2 times consecutively. + */ + sprintf (cmnd, "I %d\r", info_ptr->index); + iret = EL755_SendTillTwoVals (handle, cmnd, soll, ist); + if (!iret) return False; + + if (EL755_errcode == 0) EL755_call_depth--; + return True; + } +/* +**--------------------------------------------------------------------------- +** EL755_Open: Open a connection to an EL755 controller. +*/ + int EL755_Open ( +/* ========== +*/ void **handle, + char *host, + int port, + int chan, + int indx) { + + int status, i; + char tmo_save[4]; + char *rply_ptr0, *rply_ptr1, *rply_ptr2; + struct EL755info *my_handle; +/*-------------------------------------------------------- +** Initialise the error info stack and pre-set the +** routine name (in case of error). +*/ + EL755_errcode = EL755_errno = EL755_vaxc_errno = 0; + strcpy (EL755_routine[0], "EL755_Open"); + EL755_call_depth = 1; +/*-------------------------------------------------------- +** Assume trouble +*/ + *handle = NULL; +/*-------------------------------------------------------- +** Reserve space for the data we need to store. +*/ + my_handle = (struct EL755info *) calloc (1, sizeof (*my_handle)); + if (my_handle == NULL) { + EL755_errcode = EL755__BAD_MALLOC; /* calloc failed!! */ + return False; + } +/*-------------------------------------------------------- +** Set up the connection +*/ + StrJoin (my_handle->asyn_info.host, sizeof (my_handle->asyn_info.host), + host, ""); + my_handle->asyn_info.port = port; + my_handle->asyn_info.chan = chan; + status = AsynSrv_Open (&my_handle->asyn_info); + if (!status) { + EL755_errcode = EL755__BAD_SOCKET; + GetErrno (&EL755_errno, &EL755_vaxc_errno); /* Save errno info */ + EL755_ErrorLog ("EL755_Open/AsynSrv_Open", "Failed to make connection."); + free (my_handle); + return False; + } + memcpy (tmo_save, my_handle->asyn_info.tmo, 4); + status = EL755_Config ((void *) &my_handle, + "msecTmo", 100, /* Set a short time-out initially since + ** there should be no reason for the RMT, + ** ECHO or ID commands to take very long. + */ + "eot", "1\r", + "index", indx, + NULL); + if (!status) { + /* Some error occurred in EL755_Config - should be impossible! + */ + AsynSrv_Close (&my_handle->asyn_info, False); + free (my_handle); + return False; + } + /* + ** Now ensure that there's an EL755 connected to the line. The first + ** "RMT 1" command can fail due to pending characters in the EL755 + ** input buffer causing the command to be corrupted. The response is + ** ignored for this reason. + */ + status = AsynSrv_SendCmnds (&my_handle->asyn_info, + &my_handle->to_host, &my_handle->from_host, + "RMT 1\r", /* Try to put EL755 on-line */ + "RMT 1\r", /* Try again in case type-ahead chars corrupted .. + ** .. the first attempt. */ + "ECHO 0\r", /* And turn off echoing */ + NULL); + status = AsynSrv_SendCmnds (&my_handle->asyn_info, + &my_handle->to_host, &my_handle->from_host, + "RMT 1\r", + "ECHO 0\r", + "ID\r", + NULL); + if (!status) { + /* Some error occurred in AsynSrv_SendCmnds. + */ + AsynSrv_Close (&my_handle->asyn_info, False); + free (my_handle); + EL755_errcode = EL755__BAD_ASYNSRV; + return False; + } + + rply_ptr0 = AsynSrv_GetReply (&my_handle->asyn_info, + &my_handle->from_host, NULL); + rply_ptr1 = AsynSrv_GetReply (&my_handle->asyn_info, + &my_handle->from_host, rply_ptr0); + rply_ptr2 = AsynSrv_GetReply (&my_handle->asyn_info, + &my_handle->from_host, rply_ptr1); + if ((rply_ptr0 == NULL) || (rply_ptr1 == NULL) || (rply_ptr2 == NULL)) { + /* Some error occurred in AsynSrv_GetReply. + */ + EL755_AddCallStack (my_handle, "NULL response"); + AsynSrv_Close (&my_handle->asyn_info, False); + EL755_errcode = EL755__BAD_DEV; + free (my_handle); + return False; + } + if (rply_ptr0[0] != '\0') { + EL755_AddCallStack (my_handle, rply_ptr0); + AsynSrv_Close (&my_handle->asyn_info, False); /* Bad response! */ + EL755_errcode = EL755__BAD_DEV; + free (my_handle); + return False; + } + if (rply_ptr1[0] != '\0') { + EL755_AddCallStack (my_handle, rply_ptr1); + AsynSrv_Close (&my_handle->asyn_info, False); /* Bad response! */ + EL755_errcode = EL755__BAD_DEV; + free (my_handle); + return False; + } + if (strncmp (rply_ptr2, "EL755 MAGST", 11) != 0) { + EL755_AddCallStack (my_handle, rply_ptr2); + AsynSrv_Close (&my_handle->asyn_info, False); /* Bad response! */ + EL755_errcode = EL755__BAD_DEV; + free (my_handle); + return False; + } + /* The device seems to be an EL755! */ + + memcpy (my_handle->asyn_info.tmo, tmo_save, 4); /* Restore time-out */ + /* + ** The connection is complete. Pass the data structure + ** back to the caller as a handle. + */ + *handle = my_handle; + if (EL755_errcode == 0) EL755_call_depth--; + return True; + } +/* +**--------------------------------------------------------------------------- +** EL755_PutOffline: put the EL755 off-line +*/ + int EL755_PutOffline ( +/* ================ +*/ void **handle) { + + int status; + struct EL755info *info_ptr; + char *rply_ptr0, *rply_ptr1, *rply_ptr2; + char buff[132]; + /*---------------------------------------------- + */ + info_ptr = (struct EL755info *) *handle; + + if (!EL755_AddCallStack (info_ptr, "EL755_PutOffline")) return False; + /*---------------------------------------------- + ** The problem which this routine has is that the EL755 + ** may already be off-line. The following is, therefore, + ** rather pedantic for most cases which occur in practice. + */ + status = AsynSrv_SendCmnds (&info_ptr->asyn_info, + &info_ptr->to_host, &info_ptr->from_host, + "RMT 1\r", + "RMT 1\r", + "ECHO 1\r", + "RMT 0\r", + NULL); + if (!status) { + EL755_errcode = EL755__BAD_ASYNSRV; + return False; + } + + status = AsynSrv_SendCmnds (&info_ptr->asyn_info, + &info_ptr->to_host, &info_ptr->from_host, + "RMT\r", "", NULL); + if (!status) { + EL755_errcode = EL755__BAD_ASYNSRV; + return False; + } + + rply_ptr0 = AsynSrv_GetReply ( + &info_ptr->asyn_info, &info_ptr->from_host, NULL); + rply_ptr1 = AsynSrv_GetReply ( + &info_ptr->asyn_info, &info_ptr->from_host, rply_ptr0); + if ((rply_ptr0 == NULL) || (rply_ptr1 == NULL)) { + EL755_AddCallStack (info_ptr, "NULL response"); + EL755_errcode = EL755__BAD_ILLG; return False;} + + if ((strcmp (rply_ptr0, "RMT") == 0) && + (strcmp (rply_ptr1, "\n0") == 0)) { + EL755_call_depth--; + return True; + } + + if (strcmp (rply_ptr0, "?OF") == 0) { + EL755_errcode = EL755__OFFLINE; + }else if (strcmp (rply_ptr0, "?OFL") == 0) { + EL755_errcode = EL755__BAD_OFL; + }else if (strcmp (rply_ptr0, "?syntax failure") == 0) { + EL755_errcode = EL755__BAD_CMD; + }else if (strncmp (rply_ptr0, "?TMO", 4) == 0) { + EL755_errcode = EL755__BAD_TMO; + }else { + sprintf (buff, "Cmnd=\"RMT.\" Rply0=\"%.10s\" Rply1=\"%.10s\"", + rply_ptr0, rply_ptr1); + MakePrintable (buff, sizeof(buff), buff); + EL755_AddCallStack (info_ptr, buff); + + sprintf (buff, "Unrecognised responses to RMT command: \"%s\" \"%s\"", + rply_ptr0, rply_ptr1); + MakePrintable (buff, sizeof(buff), buff); + EL755_ErrorLog (EL755_routine[EL755_call_depth-1], buff); + + EL755_errcode = EL755__BAD_ILLG; + } + return False; + } +/* +**--------------------------------------------------------------------------- +** EL755_PutOnline: put the EL755 on-line +*/ + int EL755_PutOnline ( +/* =============== +*/ void **handle, + int echo) { + + int status, my_echo; + struct EL755info *info_ptr; + char cmnd0[10], buff[132]; + char *rply_ptr; + /*---------------------------------------------- + */ + info_ptr = (struct EL755info *) *handle; + + if (!EL755_AddCallStack (info_ptr, "EL755_PutOnline")) return False; + /*---------------------------------------------- + */ + if ((echo != 0) && (echo != 1)) { + EL755_errcode = EL755__BAD_PAR; return False; + } + /*---------------------------------------------- + ** The problem which this routine has is that the state + ** of the EL755 is not known. The following is, therefore, + ** rather pedantic for most cases which occur in practice. + */ + sprintf (cmnd0, "ECHO %d\r", echo); + status = AsynSrv_SendCmnds (&info_ptr->asyn_info, + &info_ptr->to_host, &info_ptr->from_host, + "RMT 1\r", + "RMT 1\r", + cmnd0, + NULL); + if (!status) { + EL755_errcode = EL755__BAD_ASYNSRV; + return False; + } + + status = AsynSrv_SendCmnds (&info_ptr->asyn_info, + &info_ptr->to_host, &info_ptr->from_host, + "ECHO\r", NULL); + if (!status) { + EL755_errcode = EL755__BAD_ASYNSRV; + return False; + } + + rply_ptr = AsynSrv_GetReply ( + &info_ptr->asyn_info, &info_ptr->from_host, NULL); + if (rply_ptr == NULL) { + EL755_AddCallStack (info_ptr, "NULL response"); + EL755_errcode = EL755__BAD_ILLG; return False;} + + if ((echo == 1) && (strcmp (rply_ptr, "ECHO") == 0)) { + EL755_call_depth--; + return True; + }else if ((echo == 0) && + (sscanf (rply_ptr, "%d", &my_echo) == 1) && + (my_echo == echo)) { + EL755_call_depth--; + return True; + } + + if (strcmp (rply_ptr, "?OF") == 0) { + EL755_errcode = EL755__OFFLINE; + }else if (strcmp (rply_ptr, "?OFL") == 0) { + EL755_errcode = EL755__BAD_OFL; + }else if (strcmp (rply_ptr, "?syntax failure") == 0) { + EL755_errcode = EL755__BAD_CMD; + }else if (strncmp (rply_ptr, "?TMO", 4) == 0) { + EL755_errcode = EL755__BAD_TMO; + }else { + sprintf (buff, "Cmnd=\"ECHO.\" Rply=\"%.10s\"", rply_ptr); + MakePrintable (buff, sizeof(buff), buff); + EL755_AddCallStack (info_ptr, buff); + + sprintf (buff, "Unrecognised response to ECHO command: \"%s\"", + rply_ptr); + MakePrintable (buff, sizeof(buff), buff); + EL755_ErrorLog (EL755_routine[EL755_call_depth-1], buff); + + EL755_errcode = EL755__BAD_ILLG; + } + return False; + } +/* +**--------------------------------------------------------------------------- +** EL755_SendTillSameStr: Repeat a command until we get the same +** response on 2 successive occasions. +** +** This routine is intended for internal use only! +** If too many retries, EL755_errcode is set to EL755__TOO_MANY. +*/ + int EL755_SendTillSameStr ( +/* ===================== +*/ void **handle, + char *cmnd, + char *rply, + int rply_len) { + + int iret, i, j, n_ovfl; + struct EL755info *info_ptr; + char *rply_ptr; + char buff[132]; + char replies[6][64]; + /*---------------------------------------------- + */ + info_ptr = (struct EL755info *) *handle; + + if (!EL755_AddCallStack (info_ptr, "EL755_SendTillSameStr")) return False; + /*---------------------------------------------- + ** Send command. Do it in a + ** loop until we get the same response twice to guard + ** against RS-232-C problems with the EL755. + */ + i = n_ovfl = 0; + StrJoin (rply, rply_len, "#", ""); + + while (i < 6) { + iret = AsynSrv_SendCmnds (&info_ptr->asyn_info, + &info_ptr->to_host, &info_ptr->from_host, + cmnd, NULL); + if (!iret) { + EL755_errcode = EL755__BAD_ASYNSRV; return False;} + rply_ptr = AsynSrv_GetReply ( + &info_ptr->asyn_info, &info_ptr->from_host, NULL); + if (rply_ptr == NULL) { + EL755_AddCallStack (info_ptr, "NULL response"); + EL755_errcode = EL755__BAD_ILLG; return False;} + if (strncmp (rply_ptr, "?TMO", 4) == 0) { + EL755_errcode = EL755__BAD_TMO; return False;} + if (strcmp (rply_ptr, "?OF") == 0) { + EL755_errcode = EL755__OFFLINE; return False;} + if (strcmp (rply_ptr, "?OFL") == 0) { + EL755_errcode = EL755__BAD_OFL; return False;} + if (strcmp (rply_ptr, "?syntax failure") == 0) { + EL755_errcode = EL755__BAD_CMD; return False;} + if (strcmp (rply_ptr, "?OV") == 0) { /* Check for overflow. This seems + ** to be an EL755 problem which + ** needs fixing. In the meantime, + ** just force a repeat. + */ + sprintf (buff, "Warning -- \"?OV\" received in response to \"%s\".", + cmnd); + MakePrintable (buff, sizeof(buff), buff); + EL755_ErrorLog (EL755_routine[EL755_call_depth-1], buff); + n_ovfl++; + if (n_ovfl > 10) {EL755_errcode = EL755__TOO_MANY; return False;} + }else { + n_ovfl = 0; + if (strncmp (rply, rply_ptr, rply_len) == 0) break; + StrJoin (rply, rply_len, rply_ptr, ""); + MakePrintable (replies[i], sizeof (replies[0]), rply_ptr); + i++; + } + } + if (strncmp (rply, rply_ptr, rply_len) != 0) { + EL755_errcode = EL755__TOO_MANY; + return False; + } + if (i > 1) { + sprintf (buff, "Warning -- %d retries needed for Cmnd = \"%s\".", + (i - 1), cmnd); + MakePrintable (buff, sizeof(buff), buff); + EL755_ErrorLog (EL755_routine[EL755_call_depth-1], buff); + for (j = 0; j < i; j++) fprintf (stderr, " %d: \"%s\"\n", j, replies[j]); + } + + if (EL755_errcode != 0) return False; + EL755_call_depth--; + return True; + } +/* +**--------------------------------------------------------------------------- +** EL755_SendTillSameVal: Repeat a command until we get the same +** response value on 2 successive occasions. +** +** This routine is intended for internal use only! +** If too many retries, EL755_errcode is set to EL755__TOO_MANY. +*/ + int EL755_SendTillSameVal ( +/* ===================== +*/ void **handle, + char *cmnd, + float *val) { + + int iret, i, n_ovfl, cnt; + struct EL755info *info_ptr; + float last_val; + char *rply_ptr, *tok; + char buff[132]; + /*---------------------------------------------- + */ + info_ptr = (struct EL755info *) *handle; + + if (!EL755_AddCallStack (info_ptr, "EL755_SendTillSameVal")) return False; + /*---------------------------------------------- + ** Send command. Do it in a + ** loop until we get the same response twice to guard + ** against RS-232-C problems with the EL755. + */ + i = n_ovfl = 0; + *val = 9999.999; + last_val = *val - 1.0; + + while (i < 6) { + iret = AsynSrv_SendCmnds (&info_ptr->asyn_info, + &info_ptr->to_host, &info_ptr->from_host, + cmnd, NULL); + if (!iret) { + EL755_errcode = EL755__BAD_ASYNSRV; return False;} + + rply_ptr = AsynSrv_GetReply ( + &info_ptr->asyn_info, &info_ptr->from_host, NULL); + if (rply_ptr == NULL) { + EL755_AddCallStack (info_ptr, "NULL response"); + EL755_errcode = EL755__BAD_ILLG; return False;} + + if (strcmp (rply_ptr, "?OV") == 0) { /* Check for overflow. This seems + ** to be an EL755 problem which + ** needs fixing. In the meantime, + ** just force a repeat. + */ + sprintf (buff, "Warning -- \"?OV\" received in response to \"%s\".", + cmnd); + MakePrintable (buff, sizeof(buff), buff); + EL755_ErrorLog (EL755_routine[EL755_call_depth-1], buff); + n_ovfl++; + if (n_ovfl > 10) {EL755_errcode = EL755__TOO_MANY; return False;} + }else { + n_ovfl = 0; + if (strcmp (rply_ptr, "?OF") == 0) { + EL755_errcode = EL755__OFFLINE; return False;} + if (strcmp (rply_ptr, "?OFL") == 0) { + EL755_errcode = EL755__BAD_OFL; return False;} + if (strcmp (rply_ptr, "?syntax failure") == 0) { + EL755_errcode = EL755__BAD_CMD; return False;} + if (strcmp (rply_ptr, "?power-supply OFF") == 0) { /* If off, return 0 */ + *val = 0.0; + EL755_call_depth--; return True; + }else { + tok = strtok (rply_ptr, " "); + if ((tok == NULL) || + (sscanf (tok, "%f%n", val, &cnt) != 1) || + (cnt != strlen (tok))) { + EL755_AddCallStack (info_ptr, rply_ptr); + EL755_errcode = EL755__BAD_ILLG; return False; + } + if (*val == last_val) break; + last_val = *val; + } + i++; + } + } + if (last_val != *val) { + EL755_errcode = EL755__TOO_MANY; return False;} + + if (i > 1) { + sprintf (buff, "Warning -- %d retries needed for Cmnd = \"%s\".", + (i - 1), cmnd); + MakePrintable (buff, sizeof(buff), buff); + EL755_ErrorLog (EL755_routine[EL755_call_depth-1], buff); + } + + if (EL755_errcode != 0) return False; + EL755_call_depth--; + return True; + } +/* +**--------------------------------------------------------------------------- +** EL755_SendTillTwoVals: Repeat a command until we get 2 fl.pt. +** values and the first is the same on 2 +** successive occasions. +** +** This routine is intended for internal use only! It is +** intended to read the Soll- and Ist-currents where the +** Soll-value should be the same but the Ist-value could be +** changing as the power supply ramps to a new value. +** If too many retries, EL755_errcode is set to EL755__TOO_MANY. +*/ + int EL755_SendTillTwoVals ( +/* ===================== +*/ void **handle, + char *cmnd, + float *val0, + float *val1) { + + int iret, i, n_ovfl, cnt0, cnt1; + struct EL755info *info_ptr; + float last_val; + char *rply_ptr, *tok0, *tok1; + char buff[132]; + /*---------------------------------------------- + */ + info_ptr = (struct EL755info *) *handle; + + if (!EL755_AddCallStack (info_ptr, "EL755_SendTillTwoVals")) return False; + /*---------------------------------------------- + ** Send command. Do it in a + ** loop until we get the same response twice to guard + ** against RS-232-C problems with the EL755. + */ + i = n_ovfl = 0; + *val0 = 9999.999; + last_val = *val0 - 1.0; + + while (i < 6) { + iret = AsynSrv_SendCmnds (&info_ptr->asyn_info, + &info_ptr->to_host, &info_ptr->from_host, + cmnd, NULL); + if (!iret) { + EL755_errcode = EL755__BAD_ASYNSRV; return False;} + + rply_ptr = AsynSrv_GetReply ( + &info_ptr->asyn_info, &info_ptr->from_host, NULL); + if (rply_ptr == NULL) { + EL755_AddCallStack (info_ptr, "NULL response"); + EL755_errcode = EL755__BAD_ILLG; return False;} + + if (strcmp (rply_ptr, "?OV") == 0) { /* Check for overflow. This seems + ** to be an EL755 problem which + ** needs fixing. In the meantime, + ** just force a repeat. + */ + sprintf (buff, "Warning -- \"?OV\" received in response to \"%s\".", + cmnd); + MakePrintable (buff, sizeof(buff), buff); + EL755_ErrorLog (EL755_routine[EL755_call_depth-1], buff); + n_ovfl++; + if (n_ovfl > 10) {EL755_errcode = EL755__TOO_MANY; return False;} + }else { + n_ovfl = 0; + if (strcmp (rply_ptr, "?OF") == 0) { + EL755_errcode = EL755__OFFLINE; return False;} + if (strcmp (rply_ptr, "?OFL") == 0) { + EL755_errcode = EL755__BAD_OFL; return False;} + if (strcmp (rply_ptr, "?syntax failure") == 0) { + EL755_errcode = EL755__BAD_CMD; return False;} + if (strcmp (rply_ptr, "?power-supply OFF") == 0) { /* If off, return 0 */ + *val0 = 0.0; + *val1 = 0.0; + EL755_call_depth--; return True; + }else { + tok0 = strtok (rply_ptr, " "); + tok1 = strtok (NULL, " "); + if ((tok0 == NULL) || + (tok1 == NULL) || + (sscanf (tok0, "%f%n", val0, &cnt0) != 1) || + (sscanf (tok1, "%f%n", val1, &cnt1) != 1) || + (cnt0 != strlen (tok0)) || + (cnt1 != strlen (tok1))) { + EL755_AddCallStack (info_ptr, rply_ptr); + EL755_errcode = EL755__BAD_ILLG; return False; + } + if (*val0 == last_val) break; + last_val = *val0; + } + i++; + } + } + if (last_val != *val0) { + EL755_errcode = EL755__TOO_MANY; return False;} + + if (i > 1) { + sprintf (buff, "Warning -- %d retries needed for Cmnd = \"%s\".", + (i - 1), cmnd); + MakePrintable (buff, sizeof(buff), buff); + EL755_ErrorLog (EL755_routine[EL755_call_depth-1], buff); + } + + if (EL755_errcode != 0) return False; + EL755_call_depth--; + return True; + } +/* +**--------------------------------------------------------------------------- +** EL755_SetCurrent: Sets current via EL755. +*/ + int EL755_SetCurrent ( +/* ================ +*/ void **handle, + float soll) { + + int i, iret; + float my_soll, my_ist; + char cmnd[32], cmnd0[32], buff[132], buff1[132]; + struct EL755info *info_ptr; + /*---------------------------------------------- + */ + info_ptr = (struct EL755info *) *handle; + + if (!EL755_AddCallStack (info_ptr, "EL755_SetCurrent")) return False; + /*---------------------------------------------- + ** Send I command to set EL755 current and I command + ** to read back the set value. + ** Repeat until set value is correct. + */ + sprintf (cmnd, "I %d %.4f\r", info_ptr->index, soll); + sprintf (cmnd0, "I %d\r", info_ptr->index); + i = 0; + my_soll = soll + 1.0; + + while ((i < 6) && (fabs (soll - my_soll) > 0.01)) { + iret = EL755_SendTillSameStr (handle, cmnd, buff, sizeof(buff)); + if (!iret) return False; + if (buff[0] == NIL) { /* We should get a null response */ + iret = EL755_SendTillSameVal (handle, cmnd0, &my_soll); + if (!iret) return False; + }else if (strcmp (buff, "?value out of range") == 0) { + EL755_errcode = EL755__OUT_OF_RANGE; return False; + }else if (strcmp (buff, "?current limitation") == 0) { + EL755_errcode = EL755__TOO_LARGE; return False; + }else if (strcmp (buff, "?power-supply OFF") == 0) { + if (soll == 0.0) { /* Suppress error if trying to set zero and + .. power supply is off! */ + if (EL755_errcode != 0) return False; + EL755_call_depth--; + return True; + }else { + EL755_errcode = EL755__TURNED_OFF; return False; + } + }else { + sprintf (buff1, "Cmnd=\"%s\" Rply=\"%.10s\"", cmnd, buff); + MakePrintable (buff1, sizeof(buff1), buff1); + EL755_AddCallStack (info_ptr, buff1); + EL755_errcode = EL755__BAD_ILLG; + return False; + } + i++; + } + + if (fabs (soll - my_soll) > 0.01) { + EL755_errcode = EL755__TOO_MANY; return False;} + + if (i > 1) { + sprintf (buff, "Warning -- %d retries needed for Cmnd = \"%s\".", + (i - 1), cmnd); + MakePrintable (buff, sizeof(buff), buff); + EL755_ErrorLog (EL755_routine[EL755_call_depth-1], buff); + } + if (EL755_errcode != 0) return False; + EL755_call_depth--; + return True; + } +/* +**--------------------------------------------------------------------------- +** EL755_Send sends a command to the EL755 and gets a reply +** +** This routine is intended for internal use only! +*/ + int EL755_Send ( +/* ===================== +*/ void **handle, + char *cmnd, + char *rply, + int rply_len) { + + int iret, i, j, n_ovfl; + struct EL755info *info_ptr; + char *rply_ptr; + char buff[132]; + char replies[6][64]; + /*---------------------------------------------- + */ + info_ptr = (struct EL755info *) *handle; + + if (!EL755_AddCallStack (info_ptr, "EL755_SendTillSameStr")) return False; + /*---------------------------------------------- + ** Send command. + */ + i = n_ovfl = 0; + StrJoin (rply, rply_len, "#", ""); + + + iret = AsynSrv_SendCmnds (&info_ptr->asyn_info, + &info_ptr->to_host, &info_ptr->from_host, + cmnd, NULL); + if (!iret) { + EL755_errcode = EL755__BAD_ASYNSRV; return False;} + rply_ptr = AsynSrv_GetReply ( + &info_ptr->asyn_info, &info_ptr->from_host, NULL); + if (rply_ptr == NULL) { + EL755_AddCallStack (info_ptr, "NULL response"); + EL755_errcode = EL755__BAD_ILLG; return False;} + if (strncmp (rply_ptr, "?TMO", 4) == 0) { + EL755_errcode = EL755__BAD_TMO; return False;} + if (strcmp (rply_ptr, "?OF") == 0) { + EL755_errcode = EL755__OFFLINE; return False;} + if (strcmp (rply_ptr, "?OFL") == 0) { + EL755_errcode = EL755__BAD_OFL; return False;} + if (strcmp (rply_ptr, "?syntax failure") == 0) { + EL755_errcode = EL755__BAD_CMD; return False;} + if (strcmp (rply_ptr, "?OV") == 0) { /* Check for overflow. This seems + ** to be an EL755 problem which + ** needs fixing. In the meantime, + ** just force a repeat. + */ + sprintf (buff, "Warning -- \"?OV\" received in response to \"%s\".", + cmnd); + MakePrintable (buff, sizeof(buff), buff); + EL755_ErrorLog (EL755_routine[EL755_call_depth-1], buff); + n_ovfl++; + if (n_ovfl > 10) {EL755_errcode = EL755__TOO_MANY; return False;} + }else { + n_ovfl = 0; + if (strncmp (rply, rply_ptr, rply_len) == 0) + { + return False; + } + StrJoin (rply, rply_len, rply_ptr, ""); + MakePrintable (replies[i], sizeof (replies[0]), rply_ptr); + i++; + } + if (EL755_errcode != 0) return False; + EL755_call_depth--; + return True; + } +/*-------------------------------------------- End of EL755_Utility.C =======*/ diff --git a/hardsup/err.c b/hardsup/err.c new file mode 100644 index 0000000..9ef0fad --- /dev/null +++ b/hardsup/err.c @@ -0,0 +1,105 @@ + +/*--------------------------------------------------------------------------- + + EL734Error2Text converts between an EL734 error code to text +-----------------------------------------------------------------------------*/ + void EL734Error2Text(char *pBuffer, int iErr) + { + switch(iErr) + { + case -28: + strcpy(pBuffer,"EL734__BAD_ADR"); + break; + case -8: + strcpy(pBuffer,"EL734__BAD_BIND"); + break; + case -30: + strcpy(pBuffer,"EL734__BAD_BSY"); + break; + case -3: + strcpy(pBuffer,"EL734__BAD_CMD"); + break; + case -9: + strcpy(pBuffer,"EL734__BAD_CONNECT"); + break; + case -23: + strcpy(pBuffer,"EL734__BAD_FLUSH"); + break; + case -6: + strcpy(pBuffer,"EL734__BAD_HOST"); + break; + case -10: + strcpy(pBuffer,"EL734__BAD_ID"); + break; + case -5: + strcpy(pBuffer,"EL734__BAD_ILLG"); + break; + case -2: + strcpy(pBuffer,"EL734__BAD_LOC"); + break; + case -11: + strcpy(pBuffer,"EL734__BAD_MALLOC"); + break; + case -21: + strcpy(pBuffer,"EL734__BAD_NOT_BCD"); + break; + case -4: + strcpy(pBuffer,"EL734__BAD_OFL"); + break; + case -29: + strcpy(pBuffer,"EL734__BAD_PAR"); + break; + + case -17: + strcpy(pBuffer,"EL734__BAD_RECV"); + break; + case -19: + strcpy(pBuffer,"EL734__BAD_RECV_NET"); + break; + case -18: + strcpy(pBuffer,"EL734__BAD_RECV_PIPE"); + break; + case -20: + strcpy(pBuffer,"EL734__BAD_RECV_UNKN"); + break; + case -22: + strcpy(pBuffer,"EL734__BAD_RECVLEN"); + break; + case -24: + strcpy(pBuffer,"EL734__BAD_RECV1"); + break; + case -26: + strcpy(pBuffer,"EL734__BAD_RECV1_NET"); + break; + case -25: + strcpy(pBuffer,"EL734__BAD_RECV1_PIPE"); + break; + case -27: + strcpy(pBuffer,"EL734__BAD_RNG"); + break; + case -13: + strcpy(pBuffer,"EL734__BAD_SEND"); + break; + case -14: + strcpy(pBuffer,"EL734__BAD_SEND_PIPE"); + break; + case -15: + strcpy(pBuffer,"EL734__BAD_SEND_NET"); + break; + case -16: + strcpy(pBuffer,"EL734__BAD_SEND_UNKN"); + break; + case -12: + strcpy(pBuffer,"EL734__BAD_SENDLEN"); + break; + case -7: + strcpy(pBuffer,"EL734__BAD_SOCKET"); + break; + case -1: + strcpy(pBuffer,"EL734__BAD_TMO"); + break; + default: + strcpy(pBuffer,"Unknown EL734 error"); + break; + } + } diff --git a/hardsup/failinet.c b/hardsup/failinet.c new file mode 100644 index 0000000..38c39e8 --- /dev/null +++ b/hardsup/failinet.c @@ -0,0 +1,109 @@ +#define ident "1B01" +#ifdef VAXC +#module FailInet ident +#endif +#ifdef __DECC +#pragma module FailInet ident +#endif +/* +** +--------------------------------------------------------------+ +** | Paul Scherrer Institute | +** | Computing Section | +** | | +** | This software may be used freely by non-profit organizations.| +** | It may be copied provided that the name of P.S.I. and of the | +** | author is included. Neither P.S.I. nor the author assume any | +** | responsibility for the use of this software outside of P.S.I.| +** +--------------------------------------------------------------+ +** +** Module Name . . . . . . . . : [...LIB.SINQ]FAILINET.C +** +** Author . . . . . . . . . . : D. Maden +** Date of creation . . . . . . : Nov 1995 +** +** To compile this module, use: + + $ import tasmad + $ define/group sinq_c_tlb mad_lib:sinq_c.tlb + $ cc /debug /noopt /obj=[]FailInet - + tasmad_disk:[mad.lib.sinq]FailInet + + sinq_c_tlb/lib + +** To include this module in SINQ.OLB, use: + + $ import tasmad + $ define/group sinq_c_tlb mad_lib:sinq_c.tlb + $ + $ define/group sinq_olb mad_lib:sinq_dbg.olb + $ @tasmad_disk:[mad.lib.sinq]sinq_olb FailInet debug + $ + $ define/group sinq_olb mad_lib:sinq.olb + $ @tasmad_disk:[mad.lib.sinq]sinq_olb FailInet +** +** Updates: +** 1A01 2-Nov-1995 DM. Initial version. +** 1B01 21-Mar-1996 DM. Move from DELTAT.OLB to SINQ.OLB. +**============================================================================ +** The following entry points are included: +** + #include + + void FailInet (char *text) +** -------- +** Input Args: +** text - A text string to be printed. +** Output Args: +** none +** Modified Args: +** none +** Return status: +** none +** Global variables modified: +** none +** Routines called: +** GetErrno +** perror +** exit +** Description: +** The routine is useful if a fatal TCP/IP error occurs. +** The value of errno is printed and then "perror" is called. +** Then "exit" is called. +**============================================================================ +** Global Definitions +*/ +#ifdef VAXC +#include stdlib +#include stdio +#include errno +#include sinq_prototypes +#else +#include +#include +#include +#include +#endif +/*-------------------------------------------------------------------------- +** Global Variables +*/ + +/* +** FailInet: Some network failure has occurred. +*/ + void FailInet (char *text) { +/* ======== +** Output the given text and exit the process. +*/ + int my_errno, my_vaxc_errno; + + GetErrno (&my_errno, &my_vaxc_errno); + printf ("### Internet Error ###\n"); +#ifdef __VMS + printf (" ### errno = %d.\n", my_errno); + printf (" ### vaxc$errno = %d.\n", my_vaxc_errno); +#else + printf (" ### errno = %d.\n", my_errno); +#endif + perror (text); + exit (EXIT_FAILURE); + } +/*------------------------------------------------- End of FAILINET.C =======*/ diff --git a/hardsup/geterrno.c b/hardsup/geterrno.c new file mode 100644 index 0000000..c3edcf1 --- /dev/null +++ b/hardsup/geterrno.c @@ -0,0 +1,96 @@ +#define ident "1B01" +#ifdef VAXC +#module GetErrno ident +#endif +#ifdef __DECC +#pragma module GetErrno ident +#endif +/* +** +--------------------------------------------------------------+ +** | Paul Scherrer Institute | +** | Computing Section | +** | | +** | This software may be used freely by non-profit organizations.| +** | It may be copied provided that the name of P.S.I. and of the | +** | author is included. Neither P.S.I. nor the author assume any | +** | responsibility for the use of this software outside of P.S.I.| +** +--------------------------------------------------------------+ +** +** Module Name . . . . . . . . : [...LIB.SINQ]GETERRNO.C +** +** Author . . . . . . . . . . : D. Maden +** Date of creation . . . . . . : Nov 1995 +** +** To compile this module, use: + + $ import tasmad + $ define/group sinq_c_tlb mad_lib:sinq_c.tlb + $ cc /debug /noopt /obj=[]GetErrno - + tasmad_disk:[mad.lib.sinq]GetErrno + + sinq_c_tlb/lib + +** To include this module in SINQ.OLB, use: + + $ import tasmad + $ define/group sinq_c_tlb mad_lib:sinq_c.tlb + $ + $ define/group sinq_olb mad_lib:sinq_dbg.olb + $ @tasmad_disk:[mad.lib.sinq]sinq_olb GetErrno debug + $ + $ define/group sinq_olb mad_lib:sinq.olb + $ @tasmad_disk:[mad.lib.sinq]sinq_olb GetErrno +** +** Updates: +** 1A01 2-Nov-1995 DM. Initial version. +** 1B01 21-Mar-1996 DM. Move from DELTAT.OLB to SINQ.OLB. +**============================================================================ +** The following entry points are included: +** + #include + + void GetErrno (int *his_errno, int *his_vaxc_errno) +** -------- +** Input Args: +** none +** Output Args: +** his_errno - value of "errno". +** his_vaxc_errno - on VMS systems only, value of "vaxc$errno". Otherwise +** set to 1. +** Modified Args: +** none +** Return status: +** none +** Global variables modified: +** none +** Description: +** GetErrno returns a copy of the universal error variable "errno" (and, +** on VMS systems, vaxc$errno) to a local variable supplied by the user. +** This can occasionally be useful when debugging since the debugger on +** VMS can't easily examine them. +**============================================================================ +** Global Definitions +*/ +#ifdef VAXC +#include errno +#else +#include +#endif +/*-------------------------------------------------------------------------- +** Global Variables +*/ + +/*-------------------------------------------------------------------------- +** GetErrno: Make copies of errno and vaxc$errno for debug. +*/ + void GetErrno (int *his_errno, int *his_vaxc_errno) { +/* ======== +*/ + *his_errno = errno; /* Make copy of errno */ +#ifdef __VMS + *his_vaxc_errno = vaxc$errno; /* Make copy of vaxc$errno */ +#else + *his_vaxc_errno = 1; +#endif + return; + } +/*------------------------------------------------- End of GETERRNO.C =======*/ diff --git a/hardsup/itc4util.c b/hardsup/itc4util.c new file mode 100644 index 0000000..1dce9f7 --- /dev/null +++ b/hardsup/itc4util.c @@ -0,0 +1,421 @@ +/*-------------------------------------------------------------------------- + + I T C 4 U T I L + + A few utility functions for dealing with a ITC4 temperature controller + within the SINQ setup: host -- TCP/IP -- MAC --- RS-232. + + Mark Koennecke, Juli 1997 + + Copyright: + + Labor fuer Neutronenstreuung + Paul Scherrer Institut + CH-5423 Villigen-PSI + + + The authors hereby grant permission to use, copy, modify, distribute, + and license this software and its documentation for any purpose, provided + that existing copyright notices are retained in all copies and that this + notice is included verbatim in any distributions. No written agreement, + license, or royalty fee is required for any of the authorized uses. + Modifications to this software may be copyrighted by their authors + and need not follow the licensing terms described here, provided that + the new terms are clearly indicated on the first page of each file where + they apply. + + IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY + FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY + DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE + IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE + NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR + MODIFICATIONS. +---------------------------------------------------------------------------- */ +#include +#include +#include +#include +#include "serialsinq.h" +#include "itc4util.h" +/* -------------------------------------------------------------------------*/ + + int ITC4_Open(pITC4 *pData, char *pHost, int iPort, int iChannel, int iMode) + { + int iRet; + char pCommand[80]; + char pReply[132]; + pITC4 self = NULL; + + self = (pITC4)malloc(sizeof(ITC4)); + if(self == NULL) + { + return ITC4__BADMALLOC; + } + *pData = self; + self->iControl = 1; + self->iRead = 1; + self->iReadOnly = iMode; + self->fDiv = 10.; + self->fMult = 10.; + + iRet = SerialOpen(&self->pData, pHost, iPort, iChannel); + if(iRet != 1) + { + return iRet; + } + + /* set an lengthy timeout for the configuration in order to + prevent problems. + */ + iRet = SerialConfig(&self->pData, 100); + if(iRet != 1) + { + return iRet; + } + + /* an identification test has been here, but I had to removed as not all + ITC4 controllers at SINQ answer the V command. Some versions of the + controller do not recognize it. Sighhhhhhh. I had to put it in again + in order to check for ITC-503, but I handle the thing by default as + an ITC4 if I do not get a proper response. + */ + self->i503 = 0; + iRet = SerialWriteRead(&self->pData,"V\r\n",pReply,131); + if(iRet != 1) + { + return iRet; + } + if(strstr(pReply,"ITC503") != NULL) + { + self->i503 = 1; + } + + if(!self->iReadOnly) + { + /* switch to remote and locked operation */ + iRet = SerialWriteRead(&self->pData,"C3\r\n",pReply,131); + if(iRet != 1) + { + return iRet; + } + if(pReply[0] == '?') + { + strcpy(self->pAns,pReply); + return ITC4__BADCOM; + } + + /* set the control sensor, for this we need to switch A0 first, + the do it and switch back + */ + iRet = SerialWriteRead(&self->pData,"A0\r\n",pReply,131); + if(iRet != 1) + { + return iRet; + } + if(pReply[0] == '?') + { + strcpy(self->pAns,pReply); + return ITC4__BADCOM; + } + + sprintf(pCommand,"H%1.1d\r\n",self->iControl); + iRet = SerialWriteRead(&self->pData,pCommand,pReply,131); + if(iRet != 1) + { + return iRet; + } + if(pReply[0] == '?') + { + strcpy(self->pAns,pReply); + return ITC4__BADCOM; + } + + /* controls to automatic */ + iRet = SerialWriteRead(&self->pData,"A3\r\n",pReply,131); + if(iRet != 1) + { + return iRet; + } + if(pReply[0] == '?') + { + strcpy(self->pAns,pReply); + return ITC4__BADCOM; + } + /* reset timeout */ + iRet = SerialConfig(&self->pData, 10); + if(iRet != 1) + { + return iRet; + } + } + return 1; + } +/*--------------------------------------------------------------------------*/ + void ITC4_Close(pITC4 *pData) + { + char pReply[132]; + int iRet; + pITC4 self; + + self = *pData; + + /* switch to local operation */ + iRet = SerialWriteRead(&self->pData,"C0\r\n",pReply,131); + /* ignore errors on this one, the thing may be down */ + + /* close connection */ + SerialClose(&self->pData); + + /* free memory */ + free(self); + *pData = NULL; + } +/*--------------------------------------------------------------------------*/ + int ITC4_Config(pITC4 *pData, int iTmo, int iRead, int iControl, + float fDiv,float fMult) + { + int iRet; + char pReply[132]; + char pCommand[10]; + pITC4 self; + + self = *pData; + + /* first timeout */ + if(iTmo > 0) + { + iRet = SerialConfig(&self->pData, iTmo); + if(iRet != 1) + { + return iRet; + } + } + + /* Read Sensor */ + if( (iRead > 0) && (iRead < 5) && (self->iRead != iRead) ) + { + self->iRead = iRead; + } + + /* Control Sensor */ + if( (iControl > 0) && (iControl < 5) ) + { + /* set the control sensor, for this we need to switch A0 first, + the do it and switch back + */ + iRet = SerialWriteRead(&self->pData,"A0\r\n",pReply,131); + if(iRet != 1) + { + return iRet; + } + if(pReply[0] == '?') + { + strcpy(self->pAns,pReply); + return ITC4__BADCOM; + } + + /* set sensor */ + sprintf(pCommand,"H%1.1d\r\n",iControl); + iRet = SerialWriteRead(&self->pData,pCommand,pReply,131); + if(iRet != 1) + { + return iRet; + } + if(pReply[0] == '?') + { + strcpy(self->pAns,pReply); + return ITC4__BADCOM; + } + + /* controls to automatic */ + iRet = SerialWriteRead(&self->pData,"A3\r\n",pReply,131); + if(iRet != 1) + { + return iRet; + } + if(pReply[0] == '?') + { + strcpy(self->pAns,pReply); + return ITC4__BADCOM; + } + } + self->fDiv = fDiv; + self->fMult = fMult; + + return 1; + } +/* --------------------------------------------------------------------------*/ + int ITC4_Send(pITC4 *pData, char *pCommand, char *pReply, int iLen) + { + pITC4 self; + + self = *pData; + + /* make sure, that there is a \r at the end of the command */ + if(strchr(pCommand,(int)'\r') == NULL) + { + strcat(pCommand,"\r"); + } + return SerialWriteRead(&self->pData,pCommand,pReply,iLen); + } +/*--------------------------------------------------------------------------*/ + int ITC4_Read(pITC4 *pData, float *fVal) + { + char pCommand[10], pReply[132]; + int iRet; + float fRead = -9999999.; + pITC4 self; + + self = *pData; + + + /* format and send R command */ + sprintf(pCommand,"R%1.1d\r\n",self->iRead); + iRet = SerialWriteRead(&self->pData,pCommand,pReply,131); + if(iRet != 1) + { + return iRet; + } + if(pReply[0] == '?') + { + strcpy(self->pAns,pReply); + return ITC4__BADCOM; + } + + /* analyse reply */ + if(pReply[0] != 'R') + { + strcpy(self->pAns,pReply); + return ITC4__BADCOM; + } + + iRet = sscanf(&pReply[1],"%f",&fRead); + if(iRet != 1) + { + return ITC4__BADREAD; + } + if(self->i503) + { + *fVal = fRead; + } + else + { + *fVal = fRead/self->fDiv; + } + return 1; + } +/* -------------------------------------------------------------------------*/ + int ITC4_Set(pITC4 *pData, float fVal) + { + char pCommand[10], pReply[132]; + int iRet, i, iRead; + const float fPrecision = 0.0001; + float fSet, fDelta, fRead, fDum; + pITC4 self; + int iSet; + + self = *pData; + + if(self->iReadOnly) + { + return ITC4__READONLY; + } + + /* format command */ + if(self->i503) + { + sprintf(pCommand,"T%-7.3f\r\n",fVal); + } + else + { + fSet = fVal; + iSet = (int)(fSet*self->fMult); + sprintf(pCommand,"T%05.5d\r\n",iSet); + } + + /* try three times: send, read, test, if OK return, else + resend. This must be done because the ITC4 tends to loose + characters + */ + for(i = 0; i < 3; i++) + { + /* send command */ + iRet = SerialWriteRead(&self->pData,pCommand,pReply,131); + if(iRet != 1) + { + return iRet; + } + if(pReply[0] == '?') + { + strcpy(self->pAns,pReply); + return ITC4__BADCOM; + } + /* read the set value again */ + iRead = self->iRead; + self->iRead = 0; /* make a R0 */ + fDum = self->fDiv; + self->fDiv = self->fMult; + iRet = ITC4_Read(pData,&fRead); + self->iRead = iRead; + self->fDiv = fDum; + if(iRet != 1) + { + return iRet; + } + /* check the value read back */ + if(self->i503) + { + fDelta = fRead - fVal; + } + else + { + fDelta = fRead - fSet; + } + if(fDelta < 0) + fDelta = -fDelta; + if(fDelta < fPrecision) + { + /* Success, go home */ + return 1; + } + } + return ITC4__BADSET; + } +/* -------------------------------------------------------------------------*/ + void ITC4_ErrorTxt(pITC4 *pData,int iCode, char *pError, int iLen) + { + char pBueffel[512]; + pITC4 self; + + self = *pData; + + switch(iCode) + { + case ITC4__BADCOM: + sprintf(pBueffel,"ITC4: Invalid command or offline, got %s", + self->pAns); + strncpy(pError,pBueffel,iLen); + break; + case ITC4__BADPAR: + strncpy(pError,"ITC4: Invalid parameter specified",iLen); + break; + case ITC4__BADMALLOC: + strncpy(pError,"ITC4: Error allocating memory in ITC4",iLen); + break; + case ITC4__BADREAD: + strncpy(pError,"ITC4: Badly formatted answer",iLen); + break; + case ITC4__BADSET: + strncpy(pError,"ITC4: Failed three times to write new set value to ITC4",iLen); + break; + default: + SerialError(iCode, pError,iLen); + break; + } + } diff --git a/hardsup/itc4util.h b/hardsup/itc4util.h new file mode 100644 index 0000000..f700c8a --- /dev/null +++ b/hardsup/itc4util.h @@ -0,0 +1,124 @@ +/*--------------------------------------------------------------------------- + I T C L 4 U T I L + + A few utility functions for talking to a Oxford Instruments ITCL-4 + temperature controller via the SINQ setup: TCP/IP--MAC--RS-232-- + ITC-4. + + Mark Koennecke, Juli 1997 + +----------------------------------------------------------------------------*/ +#ifndef SINQITCL4 +#define SINQITCL4 + +/*----------------------- ERRORCODES-------------------------------------- + Most functions return a negative error code on failure. Error codes + defined are those defined for serialsinq plus a few additional ones: +*/ + +#define ITC4__BADCOM -501 +/* command not recognized */ +#define ITC4__BADPAR -502 +/* bad parameter to command */ +#define ITC4__BADMALLOC -503 +/* error allocating memory */ +#define ITC4__BADREAD -504 +/* error analysing command string on Read */ +#define ITC4__NOITC -510 +/* Controller is no ITC-4 */ +#define ITC4__BADSET -530 +/* failed three times to set temperature */ +#define ITC4__READONLY -531 +/*------------------------------------------------------------------------*/ + typedef struct __ITC4 { + int iRead; + int iControl; + void *pData; + char pAns[80]; + float fDiv; + float fMult; + int iReadOnly; + int i503; /* flag for model 503, understanding float*/ + } ITC4; + + typedef struct __ITC4 *pITC4; + +/*-----------------------------------------------------------------------*/ + int ITC4_Open(pITC4 *pData,char *pHost, int iPort, int iChannel, int iMode); + /***** creates an ITC4 datastructure and opens a connection to the ITCL4 + controller. Input Parameters are: + the hostname + the port number + the RS-232 channel number on the Mac. + iMode: 1 for ReadOnly, 0 for normal mode + + Return values are 1 for success, a negative error code on + failure. + + */ + + void ITC4_Close(pITC4 *pData); + /****** close a connection to an ITC4controller and frees its + data structure. The only parameter is a pointer to the data + structure for this controller. This pointer will be invalid after + this call. + */ + + int ITC4_Config(pITC4 *pData, int iTmo, int iRead, + int iControl, float fDiv, float fMult); + /***** configure some aspects of a ITC4temperature controller. + The parameter are: + - a pointer to the data structure for the controller as + returned by OpenITCL4 + - a value for the connection timeout + - the temperature sensor to use for reading the + temperature. + - the temperature sensor used by the ITC4controller + for regulating the temperature. + - the divisor needed to calculate the real temperature + from the sensor. + The function returns 1 on success, a negative error code on + failure. + */ + + int ITC4_Send(pITC4 *pData, char *pCommand, char *pReply, int iLen); + /******* send a the command in pCommand to the ITC4controller. + A possible reply is returned in the buffer pReply. + Maximum iLen characters are copied to pReply. + The first parameter is a pointer to a ITC4data structure + as returned by OpenITCL4. + + Return values are 1 for success, a negative error code on + failure. + */ + + int ITC4_Read(pITC4 *pData, float *fVal); + /******* reads the current actual temperature of the sensor + configured by ConfigITC4for reading. The value is returned + in fVal. The first parameter is a pointer to a ITCL4 + data structure as returned by OpenITCL4. + + Return values are 1 for success, a negative error code on + failure. + */ + + int ITC4_Set(pITC4 *pData, float fVal); + /****** sets a new preset temperature in the ITC4temperature + controller. Parameters are: + - a pointer to a ITC4data structure as returned by OpenITCL4. + - the new preset value. + + Return values are 1 for success, a negative error code on + failure. + */ + + void ITC4_ErrorTxt(pITC4 *pData, int iCode, char *pError, int iLen); + /******* translates one of the negative error ITC4error codes + into text. Maximum iLen bytes will be copied to the + buffer pError; + */ + + +#endif + + diff --git a/hardsup/make_gen b/hardsup/make_gen new file mode 100644 index 0000000..32e8a1f --- /dev/null +++ b/hardsup/make_gen @@ -0,0 +1,28 @@ +#--------------------------------------------------------------------------- +# Makefile for the SINQ hardware support library +# included by a machine specific makefile +# +# Mark Koennecke, November 1996 +# Markus Zolliker, March 2003 +#-------------------------------------------------------------------------- +.SUFFIXES: +.SUFFIXES: .c .o + +OBJ= el734_utility.o asynsrv_utility.o stredit.o \ + strjoin.o failinet.o geterrno.o el737_utility.o sinqhm.o serialsinq.o \ + itc4util.o dillutil.o table.o el755_utility.o el755_errorlog.o \ + makeprint.o StrMatch.o + +libhlib.a: $(OBJ) + rm -f libhlib.a + ar cr libhlib.a $(OBJ) + ranlib libhlib.a + +clean: + rm -f *.o *.a + + + + + + diff --git a/hardsup/makefile_alpha b/hardsup/makefile_alpha new file mode 100644 index 0000000..7cd80b9 --- /dev/null +++ b/hardsup/makefile_alpha @@ -0,0 +1,15 @@ +#--------------------------------------------------------------------------- +# Makefile for the SINQ hardware support library +# machine-dependent part for Tru64 Unix +# +# Mark Koennecke, November 1996 +# Markus Zolliker, March 2003 +#-------------------------------------------------------------------------- +# the following line only for fortified version +#DFORTIFY=-DFORTIFY +#========================================================================== + +CC = cc +CFLAGS = -std1 -g $(DFORTIFY) -I$(SRC).. -I$(SRC). + +include make_gen diff --git a/hardsup/makefile_linux b/hardsup/makefile_linux new file mode 100644 index 0000000..08a262d --- /dev/null +++ b/hardsup/makefile_linux @@ -0,0 +1,15 @@ +#--------------------------------------------------------------------------- +# Makefile for the SINQ hardware support library +# machine-dependent part for Redhat Linux with AFS at PSI +# +# Mark Koennecke, November 1996 +# Markus Zolliker, March 2003 +#-------------------------------------------------------------------------- +# the following line only for fortified version +DFORTIFY=-DFORTIFY +#========================================================================== + +CC = gcc +CFLAGS = -g -DLINUX $(DFORTIFY) -I$(SRC). -I$(SRC).. -I../src + +include $(SRC)make_gen diff --git a/hardsup/makeprint.c b/hardsup/makeprint.c new file mode 100644 index 0000000..b35188e --- /dev/null +++ b/hardsup/makeprint.c @@ -0,0 +1,276 @@ +#define ident "1B02" +#ifdef VAXC +#module MakePrint ident +#endif +#ifdef __DECC +#pragma module MakePrint ident +#endif +/* +** +--------------------------------------------------------------+ +** | Paul Scherrer Institute | +** | Department ASQ | +** | | +** | This software may be used freely by non-profit organizations.| +** | It may be copied provided that the name of P.S.I. and of the | +** | author is included. Neither P.S.I. nor the author assume any | +** | responsibility for the use of this software outside of P.S.I.| +** +--------------------------------------------------------------+ +** +** Module Name . . . . . . . . : [...LIB.SINQ]MAKEPRINT.C +** +** Author . . . . . . . . . . : D. Maden +** Date of creation . . . . . . : Nov 1995 +** +** To compile this module, use: + + $ import tasmad + $ define/group sinq_c_tlb mad_lib:sinq_c.tlb + $ cc /debug /noopt /obj=[]MakePrint - + tasmad_disk:[mad.lib.sinq]MakePrint + + sinq_c_tlb/lib + +** To include this module in SINQ.OLB, use: + + $ import tasmad + $ define/group sinq_c_tlb mad_lib:sinq_c.tlb + $ + $ define/group sinq_olb mad_lib:sinq_dbg.olb + $ @tasmad_disk:[mad.lib.sinq]sinq_olb MakePrint debug + $ + $ define/group sinq_olb mad_lib:sinq.olb + $ @tasmad_disk:[mad.lib.sinq]sinq_olb MakePrint +** +** Updates: +** 1A01 30-Nov-1995 DM. Initial version. +** 1B01 21-Mar-1996 DM. Move from DELTAT.OLB to SINQ.OLB. +**============================================================================ +** The entry points included in this module are described below. Prototypes +** can be defined via: +** +** #include +** +** MakeCharPrintable - routine used by MakePrintable and MakeMemPrintable. +** MakeMemPrintable - version of MakePrintable which will handle +** buffers containing a NUL character. +** MakePrint - ensure all characters in a buffer are printable. +** MakePrintable - extended version of MakePrint. +**--------------------------------------------------------------------- +** char *MakePrint (*text) +** --------- +** Input Args: +** none +** Output Args: +** none +** Modified Args: +** char *text +** Return status: +** A pointer to "text". +** Routines called: +** none +** Description: +** The routine ensures that all characters in "text" are 7-bit +** and then replaces any non-printing character with a ".". A trailing +** "\n" or "\r" is removed. +**--------------------------------------------------------------------------- +** int *MakeCharPrintable (*out, out_size, in) +** ----------------- +** Input Args: +** char in -- the character to be converted. +** int out_size -- the size of the out buffer. +** Output Args: +** char *out -- buffer to hold the converted text. +** Modified Args: +** none +** Return status: +** The number of characters put into the output buffer. +** Routines called: +** none +** Description: +** The routine puts a printable version of the character "in" into the +** "out" buffer. The printable version is generated as follows: +** +** a) If the parity bit of a char is set, a "^" is inserted into the +** output buffer, the parity bit of the char is cleared and processed +** further. +** b) If the char is "^", "\^" is inserted into the output buffer. +** c) If the char is "\", "\\" is inserted into the output buffer. +** d) If the char is a standard C-language control char, it gets replaced +** by a recognised backslash escape sequence. The following are +** recognised: +** NUL 0x00 --> \0 +** BEL 0x07 --> \a +** BS 0x08 --> \b +** HT 0x09 --> \t +** LF 0x0a --> \n +** VT 0x0b --> \v +** FF 0x0c --> \f +** CR 0x0d --> \r +** e) If the character is printable (i.e. between " "/0x20 and "~"/0x7e +** inclusive), it is inserted into the output buffer as is. +** f) Anything else gets inserted as "\xxx", where xxx is the octal +** representation of the character. +**--------------------------------------------------------------------------- +** char *MakePrintable (*out, out_size, *in) +** ------------- +** Input Args: +** char *in -- the text to be converted. +** int out_size -- the size of the out buffer. +** Output Args: +** char *out -- buffer to hold the converted text. +** Modified Args: +** none +** Return status: +** A pointer to "out". +** Routines called: +** none +** Description: +** The routine converts characters in the "in" string to a printable +** representation using MakeCharPrintable and copies them to "out" until +** a null is detected. +**--------------------------------------------------------------------------- +** char *MakeMemPrintable (*out, out_size, *in, in_len) +** ---------------- +** Input Args: +** int out_size -- the size of the out buffer. +** char *in -- the text to be converted. +** int in_len -- the number of characters to be converted. +** Output Args: +** char *out -- buffer to hold the converted text. +** Modified Args: +** none +** Return status: +** A pointer to "out". +** Routines called: +** none +** Description: +** The routine is the same as MakePrintable, except that it converts +** a given number of characters rather than a null terminated string. +**============================================================================*/ +/* +**--------------------------------------------------------------------------- +** Global Definitions +*/ +#include +#include +#include +#ifdef FORTIFY + #include +#endif + +#include + +#define NIL ('\0') +/*-------------------------------------------------------------------------- +** Global Variables +*/ +/* +**-------------------------------------------------------------------------- +** MakeCharPrintable: makes a single character printable. +*/ + int MakeCharPrintable (char *out, int out_size, char in) { +/* ================= +** +** Return value is number of chars put into *out. +*/ + char buff[8], *pntr; + + pntr = buff; + + if ((in & 0x80) != 0) { /* Parity bit set? */ + *pntr++ = '^'; /* Yes. Put a '^' in the buffer .. */ + in = in & 0x7f; /* .. and remove the parity bit. */ + } + + switch (in) { + case '^': *pntr++ = '\\'; *pntr++ = '^'; break; + case '\\': *pntr++ = '\\'; *pntr++ = '\\'; break; + case '\000': *pntr++ = '\\'; *pntr++ = '0'; break; + case '\007': *pntr++ = '\\'; *pntr++ = 'a'; break; + case '\010': *pntr++ = '\\'; *pntr++ = 'b'; break; + case '\011': *pntr++ = '\\'; *pntr++ = 't'; break; + case '\012': *pntr++ = '\\'; *pntr++ = 'n'; break; + case '\013': *pntr++ = '\\'; *pntr++ = 'v'; break; + case '\014': *pntr++ = '\\'; *pntr++ = 'f'; break; + case '\015': *pntr++ = '\\'; *pntr++ = 'r'; break; + default: + if ((in < ' ') || (in > '~')) { + pntr += sprintf (pntr, "\\%03.3o", in); + }else { + *pntr++ = in; + } + } + out_size = (out_size > (pntr - buff)) ? (pntr - buff) : out_size; + memcpy (out, buff, out_size); + return out_size; + } +/* +**-------------------------------------------------------------------------- +** MakeMemPrintable: alternative version of MakePrintable. +*/ + char *MakeMemPrintable ( +/* ================ +*/ char *out, + int out_size, + char *in, + int in_len) { + + int i; + char *pntr; + + if (out_size <= 0) return out; + + while ((out_size > 1) && (in_len > 0)) { + i = MakeCharPrintable (out, (out_size - 1), *in); + out += i; out_size -= i; + in++; in_len--; + } + *out = NIL; + return out; + } +/* +**-------------------------------------------------------------------------- +** MakePrint: Make all characters in a buffer printable. +*/ + char *MakePrint (char *chr) { +/* ========= +*/ + int len, i; + + for (i = 0; chr[i] != NIL; i++) chr[i] &= 0x7F; + + len = strlen (chr); + if (len <= 0) return chr; + + if (chr[len-1] == '\r') chr[len-1] = NIL; + if (chr[len-1] == '\n') chr[len-1] = NIL; + + for (i = 0; chr[i] != NIL; i++) { + if (chr[i] < ' ') chr[i] = '.'; + if (chr[i] == 0x7F) chr[i] = '.'; + } + + return chr; + } +/* +**-------------------------------------------------------------------------- +** MakePrintable: improved version of MakePrint. +*/ + char *MakePrintable ( +/* ============= +*/ char *out, + int out_size, + char *in) { + + int i; + char *pntr; + + if (out_size <= 0) return out; + + while ((out_size > 1) && (*in != NIL)) { + i = MakeCharPrintable (out, (out_size - 1), *in); + in++; out += i; out_size -= i; + } + *out = NIL; + return out; + } +/*-------------------------------------------- End of MakePrint.C =======*/ diff --git a/hardsup/rs232c_def.h b/hardsup/rs232c_def.h new file mode 100644 index 0000000..2753bd1 --- /dev/null +++ b/hardsup/rs232c_def.h @@ -0,0 +1,186 @@ +#ifndef _rs232c_def_ +#define _rs232c_def_ +/*------------------------------------------------ RS232C_DEF.H Ident V02G +** Definitions for the RS-232-C Server Protocol +** +** On UNIX systems, this file is located in /public/lib/include +** On VMS systems, this file is a module in mad_lib:sinq_c.tlb +*/ +#define RS__PROTOCOL_ID "V01A" +#define RS__PROTOCOL_ID_V01B "V01B" + +#define RS__PROTOCOL_CODE 1 /* Code corresponding to RS__PROTOCOL_ID */ +#define RS__PROTOCOL_CODE_V01B 2 /* Code corresponding to RS__PROTOCOL_ID_0 */ + +#ifndef OffsetOf +#define OffsetOf(type, identifier) ((size_t)(&((type*) NULL)->identifier)) +#endif +/*---------------------------------------------------------------------------- +** Structure of Message from Client to Server - everything is sent in ASCII +** for LabView's benefit. +** Name #bytes Description +** ==== ====== =========== +** msg_size 4 Number of bytes following (rounded up to multiple +** of 4). +** msg_id 4 Message ident (an incrementing counter for debugging). +** c_pcol_lvl 4 Client-Protocol-Level (should be "V01A"). +** serial_port 4 Serial port to which commands should be sent. This +** is a small integer). +** tmo 4 Time-out in units of 0.1 secs (<0 = "wait for ever"). +** terms 1 + 3 Terminators. The first char gives the number of +** terminators (up to 3) and the following 3 chars +** are valid response terminators, e.g. "1\r\0\0". +** n_cmnds 4 Number of commands following. +** cmnds 356 The command buffer. This is a concatenated list of +** commands with the structure described below. +** +** Special Cases of msg_size +** ------------------------- +** "-001" ==> the client is just about to close his connection. +** "-002" ==> this is a request to the server for him to turn on tracing. +** The reply should be simply an echo of the 4 bytes "-002". +** "-003" ==> this is a request to the server for him to turn off tracing. +** The reply should be simply an echo of the 4 bytes "-003". +** "-004" ==> this is a request to the server for him to flush his buffers. +** The reply should be simply an echo of the 4 bytes "-004". +** +** Structure of a command item in the cmnds buffer. +** +** a) RS__PROTOCOL_ID = "V01A" +** +** Name #bytes Description +** ==== ====== =========== +** cmnd_len 2 The number of bytes following encoded as 2 ASCII +** decimal chars. +** cmnd The command to be sent on Serial Port . +** The string should contain any required terminator +** bytes but should not be zero-terminated (unless +** the zero-byte should be transmitted at the end +** of the command). cmnd_len should count the +** terminator byte. +** +** An example of a command item might be: "06RMT 1\r" +** +** b) RS__PROTOCOL_ID = "V01B" +** +** Name #bytes Description +** ==== ====== =========== +** cmnd_len 4 The number of bytes following encoded as 4 ASCII +** decimal chars. +** cmnd The command to be sent on Serial Port . +** The string should contain any required terminator +** bytes but should not be zero-terminated (unless +** the zero-byte should be transmitted at the end +** of the command). should count the +** terminator byte. +** +** An example of a command item might be: "0006RMT 1\r" +**--------------------------------------------------------------------------*/ + struct RS__MsgStruct { + char msg_size[4]; /* 4 ASCII decimal chars!! */ + char msg_id[4]; + char c_pcol_lvl[4]; /* Client protocol level */ + char serial_port[4]; + char tmo[4]; /* Units are 0.1 secs */ + char terms[4]; + char n_cmnds[4]; + char cmnds[356]; + }; + /* + ** The "cmnds" buffer in RS__MsgStruct is a concatenated + ** list of the following structures. + */ + struct RS__CmndStruct { + char cmnd_len[2]; + char cmnd[1]; + }; + struct RS__CmndStruct_V01B { + char cmnd_len[4]; + char cmnd[1]; + }; +/*---------------------------------------------------------------------------- +** Structure of Reply from Server to Client - everything is sent in ASCII +** for LabView's benefit. +** +** Name #bytes Description +** ==== ====== =========== +** msg_size 4 Number of bytes following (rounded up to multiple +** of 4). +** msg_id 4 Message ident (this is a copy of the msg_id field +** in the message from Client to Server). +** s_pcol_lvl 4 Server-Protocol-Level (should be "V01A" or "V01B"). +** n_rply 4 Number of replies following. If < 0, an error has +** been detected and sub_status may give additional +** information. +** rplys 496 The reply buffer. This is a concatenated list of +** replies with the structure described below. +** sub_status 12 A sub-status code. This field overlays the first 12 +** bytes of rplys and may provide additional +** information in the case that n_rply < 0. +** +** Structure of a reply item in the rplys buffer. +** +** a) RS__PROTOCOL_ID = "V01A" +** +** Name #bytes Description +** ==== ====== =========== +** rply_len 2 The number of bytes following encoded as 2 ASCII +** decimal chars. +** term 1 The terminating character which was detected at the +** end of the reply. This will be one of the +** characters specified in . +** rply The zero-terminated reply. This is effectively the +** reply as received with the terminating character +** replaced by '\0'. +** +** An example of a reply item might be: "08\r12.345\0" +** +** b) RS__PROTOCOL_ID = "V01B" +** +** Name #bytes Description +** ==== ====== =========== +** rply_len 4 The number of bytes following encoded as 4 ASCII +** decimal chars. +** term 1 The terminating character which was detected at the +** end of the reply. This will be one of the +** characters specified in . +** rply The zero-terminated reply. This is effectively the +** reply as received with the terminating character +** replaced by '\0'. +** +** An example of a reply item might be: "0009\r12.3456\0" +**--------------------------------------------------------------------------*/ + struct RS__RespStruct { + char msg_size[4]; + char msg_id[4]; + char s_pcol_lvl[4]; /* Server protocol level */ + char n_rply[4]; /* Error if < 0 */ + union { + char rplys[496]; + char sub_status[12]; + } u; + }; + /* + ** The "rplys" buffer in RS__RespStruct is a + ** concatenated list of the following structures. + */ + struct RS__RplyStruct { + char rply_len[2]; /* 2 ASCII decimal chars!! + ** The length includes the + ** terminator, term, and the + ** zero terminator of rply. + */ + char term; /* The terminating character */ + char rply[1]; /* Zero terminated string */ + }; + struct RS__RplyStruct_V01B { + char rply_len[4]; /* 4 ASCII decimal chars!! + ** The length includes the + ** terminator, term, and the + ** zero terminator of rply. + */ + char term; /* The terminating character */ + char rply[1]; /* Zero terminated string */ + }; +/*------------------------------------------------ End of RS232C_DEF.H --*/ +#endif /* _rs232c_def_ */ diff --git a/hardsup/serialsinq.c b/hardsup/serialsinq.c new file mode 100644 index 0000000..2d40ac7 --- /dev/null +++ b/hardsup/serialsinq.c @@ -0,0 +1,914 @@ +/*------------------------------------------------------------------------- + S E R I A L S I N Q + Implementation file of the functions for talking with a RS--232 port + on a SINQ terminal server. This code has been adapted from code + provided by David Maden for the EL734 motor controller. A new version + became necessary as the Dornier velocity selector supports a + completely different protocoll than the EL734. The basics, however, are + the same. + + Mark Koennecke, Juli 1997 + + Copyright: + + Labor fuer Neutronenstreuung + Paul Scherrer Institut + CH-5423 Villigen-PSI + + + The authors hereby grant permission to use, copy, modify, distribute, + and license this software and its documentation for any purpose, provided + that existing copyright notices are retained in all copies and that this + notice is included verbatim in any distributions. No written agreement, + license, or royalty fee is required for any of the authorized uses. + Modifications to this software may be copyrighted by their authors + and need not follow the licensing terms described here, provided that + the new terms are clearly indicated on the first page of each file where + they apply. + + IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY + FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY + DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE + IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE + NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR + MODIFICATIONS. +----------------------------------------------------------------------------*/ +#include +#include +#include +#include +#include +#include + +#ifdef FORTIFY +#include "../fortify.h" +#endif + +#include +#include +#include +#include +#include +#include + +#include + +#ifdef __VMS +#include +#else +#include +#endif + +/*-----------------------------------------------------------------*/ +#include "sinq_prototypes.h" +#include "el734_def.h" +#include "rs232c_def.h" +#include "el734fix.h" +#include "serialsinq.h" + +#define True 1 +#define False 0 + + struct SerialInfo { + int skt; + int iForce; + int port; + int chan; + char host[20]; + int tmo; + int msg_id; + int n_replies, max_replies; + char pTerms[4]; + char pSendTerm[10]; + struct RS__MsgStruct to_host; + struct RS__RespStruct from_host; + SerialSleep pFunc; + void *pData; + struct AsynSrv__info sAsync; + }; +/*------------------- The default sleep function -----------------------*/ + static int SerialNccrrrh(void *pData, int iTime) + { + usleep(50); + return 1; + } + +/*-----------------------------------------------------------------------*/ + int SerialOpen(void **pData, char *pHost, int iPort, int iChannel) + { + int status; + struct SerialInfo *my_info; + void *my_hndl; + struct hostent *rmt_hostent; + struct in_addr *rmt_inet_addr_pntr; + int rmt_sockname_len; + struct sockaddr_in lcl_sockname; + struct sockaddr_in rmt_sockname; + char msr_cmnd[20]; + struct RS__RplyStruct *rply_ptr; + + *pData = NULL; + +/* +** allocate memory first +*/ + *pData = malloc (sizeof (struct SerialInfo)); + if (*pData == NULL) { + return EL734__BAD_MALLOC; /* malloc failed!! */ + } + my_info = *pData; + memset(my_info,0,sizeof(struct SerialInfo)); + +/* +**-------------------------- Set up the connection +*/ + my_info->sAsync.port = iPort; + strcpy(my_info->sAsync.host,pHost); + my_info->sAsync.chan = iChannel; + status = AsynSrv_Open (&(my_info->sAsync)); + if (status != 1) { + return OPENFAILURE; + } + + /* intialize data structures */ + StrJoin (my_info->host, sizeof (my_info->host), pHost, ""); + my_info->skt = my_info->sAsync.skt; + my_info->port = iPort; + my_info->chan = iChannel; + my_info->tmo = 100; + my_info->msg_id = 0; + my_info->pFunc = SerialNccrrrh; + my_info->pData = NULL; + strcpy(my_info->pTerms,"1\r\n\0"); + my_info->iForce = 0; + memset(my_info->pSendTerm,0,9); + strcpy(my_info->pSendTerm,"\r\n"); + + return 1; + } +/*-----------------------------------------------------------------------*/ + int SerialForceOpen(void **pData, char *pHost, int iPort, int iChannel) + { + int status; + struct SerialInfo *my_info; + void *my_hndl; + struct hostent *rmt_hostent; + struct in_addr *rmt_inet_addr_pntr; + int rmt_sockname_len; + struct sockaddr_in lcl_sockname; + struct sockaddr_in rmt_sockname; + char msr_cmnd[20]; + struct RS__RplyStruct *rply_ptr; + + *pData = NULL; + + /* create pData */ + *pData = malloc (sizeof (struct SerialInfo)); + if (*pData == NULL) { + return EL734__BAD_MALLOC; /* malloc failed!! */ + } + my_info = *pData; + memset(my_info,0,sizeof(struct SerialInfo)); + + +/* +**-------------------------- Set up the connection +*/ + my_info->sAsync.port = iPort; + strcpy(my_info->sAsync.host,pHost); + my_info->sAsync.chan = iChannel; + status = AsynSrv_OpenNew (&(my_info->sAsync)); + if (status != 1) { + return OPENFAILURE; + } + + /* intialize data structures */ + StrJoin (my_info->host, sizeof (my_info->host), pHost, ""); + my_info->skt = my_info->sAsync.skt; + my_info->port = iPort; + my_info->chan = iChannel; + my_info->tmo = 100; + my_info->msg_id = 0; + my_info->pFunc = SerialNccrrrh; + my_info->pData = NULL; + strcpy(my_info->pTerms,"1\r\n\0"); + my_info->iForce = 1; + memset(my_info->pSendTerm,0,9); + strcpy(my_info->pSendTerm,"\r\n"); + + return 1; + } +/*--------------------------------------------------------------------------*/ + int SerialConfig(void **pData, int iTmo) + { + struct SerialInfo *my_info = NULL; + + my_info = (struct SerialInfo *)*pData; + assert(my_info); + + if(iTmo < 100) + { + my_info->tmo = 1; + return 1; + } + else + { + my_info->tmo = iTmo/100; /* convert to deci seconds */ + if(my_info->tmo > 9999)my_info->tmo = 9999; + } + return 1; + } +/*--------------------------------------------------------------------------*/ + int GetSerialTmo(void **pData) + { + struct SerialInfo *my_info = NULL; + int iTmo; + + my_info = (struct SerialInfo *)*pData; + assert(my_info); + + iTmo = my_info->tmo*100-99; /* convert back to milli seconds */ + + return iTmo; + } + int SerialGetTmo(void **pData) + { + return GetSerialTmo(pData); + } +/*--------------------------------------------------------------------------*/ + int SerialGetSocket(void **pData) + { + struct SerialInfo *my_info = NULL; + int iTmo; + + my_info = (struct SerialInfo *)*pData; + assert(my_info); + + return my_info->skt; + + return 1; + } +/*--------------------------------------------------------------------------*/ + int SerialClose(void **pData) + { + + struct SerialInfo *info_ptr; + char buff[4]; + + info_ptr = (struct SerialInfo *) *pData; + if (info_ptr == NULL) return True; + + if (info_ptr->skt != 0) { + AsynSrv_Close (&(info_ptr->sAsync),0); + info_ptr->skt = 0; + } + free (*pData); + *pData = NULL; + return True; + } +/*--------------------------------------------------------------------------*/ + int SerialForceClose(void **pData) + { + + struct SerialInfo *info_ptr; + char buff[4]; + + info_ptr = (struct SerialInfo *) *pData; + if (info_ptr == NULL) return True; + + if (info_ptr->skt != 0) { + AsynSrv_Close (&(info_ptr->sAsync),1); + info_ptr->skt = 0; + } + free (*pData); + *pData = NULL; + return True; + } +/*--------------------------------------------------------------------------*/ + int SerialATerm(void **pData, char *pTerm) + { + struct SerialInfo *my_info = NULL; + + my_info = (struct SerialInfo *)*pData; + if(my_info == NULL) + { + printf("Serious Programming problem: data = NULL\n"); + return 0; + } + + /* only three characters in this field */ + if(strlen(pTerm) > 4) + { + return 0; + } + memset(my_info->pTerms,0,4); + strcpy(my_info->pTerms,pTerm); + + return 1; + } +/*--------------------------------------------------------------------------*/ + int SerialAGetTerm(void **pData, char *pTerm, int iTermLen) + { + struct SerialInfo *my_info = NULL; + + my_info = (struct SerialInfo *)*pData; + assert(my_info); + + strncpy(pTerm,my_info->pTerms,iTermLen); + + return 1; + } +/*-------------------------------------------------------------------------*/ + int SerialSendTerm(void **pData, char *pTerm) + { + struct SerialInfo *my_info = NULL; + + my_info = (struct SerialInfo *)*pData; + assert(my_info); + + /* only 0 characters in this field */ + if(strlen(pTerm) > 9) + { + return 0; + } + strcpy(my_info->pSendTerm,pTerm); + + return 1; + } + +/*---------------------------------------------------------------------------*/ + + int SerialSend(void **pData, char *pCommand) + { + struct SerialInfo *info_ptr; + int status, c_len, size, max_size, ncmnds; + int bytes_to_come, bytes_left; + int iResult; + char *nxt_byte_ptr; + char err_text[80]; + char text[20]; + char *txt_ptr; + char *cmnd_lst_ptr; + char *pComCom = NULL; + + /* + ** Do nothing if no connection - the connection gets + ** closed if an error is detected. + */ + info_ptr = (struct SerialInfo *) *pData; + if (info_ptr == NULL) return NOCONNECTION; + if (info_ptr->skt == 0) { + memset (info_ptr->from_host.msg_size, + '0', sizeof (info_ptr->from_host.msg_size)); + return NOCONNECTION; + } + + info_ptr->msg_id++; /* Set up an incrementing message id */ + if (info_ptr->msg_id > 9999) info_ptr->msg_id = 1; + sprintf (info_ptr->to_host.msg_id, "%04.4d", info_ptr->msg_id); + + memcpy (info_ptr->to_host.c_pcol_lvl, RS__PROTOCOL_ID_V01B, + sizeof (info_ptr->to_host.c_pcol_lvl)); + sprintf (info_ptr->to_host.serial_port, "%04.4d", info_ptr->chan); + sprintf (info_ptr->to_host.tmo, "%04d", info_ptr->tmo); + + strncpy(info_ptr->sAsync.eot,info_ptr->pTerms,4); + memcpy (info_ptr->to_host.terms, info_ptr->pTerms, + sizeof (info_ptr->to_host.terms)); + memcpy (info_ptr->to_host.n_cmnds, "0000", + sizeof (info_ptr->to_host.n_cmnds)); + + + txt_ptr = pCommand; /* Get pntr to cmnd string */ + ncmnds = 0; + cmnd_lst_ptr = &info_ptr->to_host.cmnds[0]; + bytes_left = sizeof (info_ptr->to_host) - + OffsetOf (struct RS__MsgStruct, cmnds[0]); + + size = strlen (txt_ptr) + strlen(info_ptr->pSendTerm); + if (size > bytes_left) { + return EL734__BAD_SENDLEN; /* Too much to send */ + }else { + strcpy (cmnd_lst_ptr+4, txt_ptr); + /* make sure that the string is properly terminated */ + if((strstr(txt_ptr,info_ptr->pSendTerm) == 0) && + (strlen(txt_ptr) > 0) ) + { + strcpy(cmnd_lst_ptr+4+strlen(txt_ptr),info_ptr->pSendTerm); + c_len = strlen(txt_ptr) + strlen(info_ptr->pSendTerm); + } + else + { + c_len = strlen (txt_ptr); + } + sprintf (text, "%04.4d", c_len); + memcpy (cmnd_lst_ptr, text, 4); + cmnd_lst_ptr = cmnd_lst_ptr + c_len + 4; + ncmnds++; + bytes_left = bytes_left - size; + } + + sprintf (text, "%04.4d", ncmnds); + memcpy (info_ptr->to_host.n_cmnds, + text, sizeof (info_ptr->to_host.n_cmnds)); + + size = cmnd_lst_ptr - info_ptr->to_host.msg_id; + size = (size + 3) & (~3); /* Round up to multiple of 4 */ + sprintf (text, "%04.4d", size); + memcpy (info_ptr->to_host.msg_size, text, 4); + + status = send (info_ptr->skt, (char *) &info_ptr->to_host, size+4, 0); + if (status != (size+4)) { + if (status == 0) { + iResult = EL734__BAD_SEND; /* Server exited (probably) */ + }else if (status == -1) { + iResult = EL734__BAD_SEND_PIPE; /* Server exited (probably) */ + } + AsynSrv_Close (&(info_ptr->sAsync),1); + info_ptr->skt = 0; + memset (info_ptr->from_host.msg_size, + '0', sizeof (info_ptr->from_host.msg_size)); + return iResult; + } + return 1; + } +/*-------------------------------------------------------------------------*/ + int SerialReceive(void **pData, char *pBuffer, int iBufLen) + { + struct SerialInfo *info_ptr; + int status, c_len, size, max_size, ncmnds; + int bytes_to_come, bytes_left; + int iResult; + char *nxt_byte_ptr; + char err_text[80]; + char text[20]; + char *txt_ptr; + char *cmnd_lst_ptr; + struct RS__RplyStruct_V01B *ptr = NULL; + long lMask = 0L; + struct timeval tmo = {0,1}; + + + /* + ** Do nothing if no connection - the connection gets + ** closed if an error is detected. + */ + info_ptr = (struct SerialInfo *) *pData; + if (info_ptr == NULL) return NOCONNECTION; + if (info_ptr->skt == 0) { + memset (info_ptr->from_host.msg_size, + '0', sizeof (info_ptr->from_host.msg_size)); + return NOCONNECTION; + } + + /* try with select if there is data */ +/* lMask = (1 << info_ptr->skt); + tmo.tv_usec = 10; + status = select((info_ptr->skt +1), (fd_set *)&lMask, NULL,NULL,&tmo); + if(status <= 0) + { + return SELECTFAIL; + } +*/ + + /* try read the message length to come */ + size = sizeof (info_ptr->from_host.msg_size); + status = recv (info_ptr->skt, info_ptr->from_host.msg_size, size, 0); + if (status != size) { + if(status > 0) + { + iResult = EL734__BAD_RECV; /* Server exited (probably) */ + } + else if (status == -1) { + iResult = EL734__BAD_RECV_NET; /* It's some other net problem */ + } + else + { + iResult = EL734__BAD_RECV_NET; + } + AsynSrv_Close (&(info_ptr->sAsync),1); + info_ptr->skt = 0; + memset (info_ptr->from_host.msg_size, + '0', sizeof (info_ptr->from_host.msg_size)); + return iResult; + } + if (sscanf (info_ptr->from_host.msg_size, "%4d", &bytes_to_come) != 1) { + return EL734__BAD_NOT_BCD; /* Header not an ASCII BCD integer */ + } + + max_size = sizeof (info_ptr->from_host) - + sizeof (info_ptr->from_host.msg_size); + if (bytes_to_come > max_size) { + iResult = EL734__BAD_RECVLEN; + nxt_byte_ptr = &info_ptr->from_host.msg_size[size]; + while (bytes_to_come > 0) { /* Flush out the incoming message */ + bytes_left = bytes_to_come; + if (bytes_left > max_size) bytes_left = max_size; + status = recv (info_ptr->skt, nxt_byte_ptr, bytes_left, 0); + if (status <= 0) { + iResult = EL734__BAD_FLUSH; /* TCP/IP problem whilst flushing */ + AsynSrv_Close (&(info_ptr->sAsync),1); + info_ptr->skt = 0; + memset (info_ptr->from_host.msg_size, + '0', sizeof (info_ptr->from_host.msg_size)); + return iResult; + } + bytes_to_come = bytes_to_come - status; + } + memset (info_ptr->from_host.msg_size, + '0', sizeof (info_ptr->from_host.msg_size)); + return iResult; + }else { + nxt_byte_ptr = &info_ptr->from_host.msg_size[size]; + bytes_left = bytes_to_come; + while (bytes_left > 0) { /* Read the rest of the response */ + status = recv (info_ptr->skt, nxt_byte_ptr, bytes_left, 0); + if (status <= 0) { + if (status == 0) { + iResult = EL734__BAD_RECV1; /* Server exited (probably) */ + }else { + iResult = EL734__BAD_RECV1_NET; /* It's some other net fault */ + } + AsynSrv_Close (&(info_ptr->sAsync),1); + info_ptr->skt = 0; + return iResult; + } + bytes_left = bytes_left - status; + nxt_byte_ptr = nxt_byte_ptr + status; + } + } + /* well, we got data, make it available */ + if (sscanf (info_ptr->from_host.n_rply, "%4d", + &info_ptr->max_replies) != 1)info_ptr->max_replies = 0; + if (info_ptr->max_replies > 0) + ptr = (struct RS__RplyStruct_V01B *) info_ptr->from_host.u.rplys; + info_ptr->n_replies = 1; + if(ptr) + { + strncpy(pBuffer, ptr->rply,iBufLen); + } + else + { + return NOREPLY; + } + return True; + } +/*-------------------------------------------------------------------------*/ + int SerialReceiveWithTerm(void **pData, char *pBuffer, + int iBufLen, char *cTerm ) + { + struct SerialInfo *info_ptr; + int status, c_len, size, max_size, ncmnds; + int bytes_to_come, bytes_left; + int iResult; + char *nxt_byte_ptr; + char err_text[80]; + char text[20]; + char *txt_ptr; + char *cmnd_lst_ptr; + struct RS__RplyStruct_V01B *ptr = NULL; + long lMask = 0L; + struct timeval tmo = {0,1}; + + + /* + ** Do nothing if no connection - the connection gets + ** closed if an error is detected. + */ + info_ptr = (struct SerialInfo *) *pData; + if (info_ptr == NULL) return NOCONNECTION; + if (info_ptr->skt == 0) { + memset (info_ptr->from_host.msg_size, + '0', sizeof (info_ptr->from_host.msg_size)); + return NOCONNECTION; + } + + /* try with select if there is data */ +/* lMask = (1 << info_ptr->skt); + tmo.tv_usec = 10; + status = select((info_ptr->skt +1), (fd_set *)&lMask, NULL,NULL,&tmo); + if(status <= 0) + { + return SELECTFAIL; + } +*/ + + /* try read the message length to come */ + size = sizeof (info_ptr->from_host.msg_size); + status = recv (info_ptr->skt, info_ptr->from_host.msg_size, size, 0); + if (status != size) { + if(status > 0) + { + iResult = EL734__BAD_RECV; /* Server exited (probably) */ + } + else if (status == -1) { + iResult = EL734__BAD_RECV_NET; /* It's some other net problem */ + } + else + { + iResult = EL734__BAD_RECV_NET; + } + AsynSrv_Close (&(info_ptr->sAsync),1); + info_ptr->skt = 0; + memset (info_ptr->from_host.msg_size, + '0', sizeof (info_ptr->from_host.msg_size)); + return iResult; + } + if (sscanf (info_ptr->from_host.msg_size, "%4d", &bytes_to_come) != 1) { + return EL734__BAD_NOT_BCD; /* Header not an ASCII BCD integer */ + } + + max_size = sizeof (info_ptr->from_host) - + sizeof (info_ptr->from_host.msg_size); + if (bytes_to_come > max_size) { + iResult = EL734__BAD_RECVLEN; + nxt_byte_ptr = &info_ptr->from_host.msg_size[size]; + while (bytes_to_come > 0) { /* Flush out the incoming message */ + bytes_left = bytes_to_come; + if (bytes_left > max_size) bytes_left = max_size; + status = recv (info_ptr->skt, nxt_byte_ptr, bytes_left, 0); + if (status <= 0) { + iResult = EL734__BAD_FLUSH; /* TCP/IP problem whilst flushing */ + AsynSrv_Close (&(info_ptr->sAsync),1); + info_ptr->skt = 0; + memset (info_ptr->from_host.msg_size, + '0', sizeof (info_ptr->from_host.msg_size)); + return iResult; + } + bytes_to_come = bytes_to_come - status; + } + memset (info_ptr->from_host.msg_size, + '0', sizeof (info_ptr->from_host.msg_size)); + return iResult; + }else { + nxt_byte_ptr = &info_ptr->from_host.msg_size[size]; + bytes_left = bytes_to_come; + while (bytes_left > 0) { /* Read the rest of the response */ + status = recv (info_ptr->skt, nxt_byte_ptr, bytes_left, 0); + if (status <= 0) { + if (status == 0) { + iResult = EL734__BAD_RECV1; /* Server exited (probably) */ + }else { + iResult = EL734__BAD_RECV1_NET; /* It's some other net fault */ + } + AsynSrv_Close (&(info_ptr->sAsync),1); + info_ptr->skt = 0; + return iResult; + } + bytes_left = bytes_left - status; + nxt_byte_ptr = nxt_byte_ptr + status; + } + } + /* well, we got data, make it available */ + if (sscanf (info_ptr->from_host.n_rply, "%4d", + &info_ptr->max_replies) != 1)info_ptr->max_replies = 0; + if (info_ptr->max_replies > 0) + ptr = (struct RS__RplyStruct_V01B *) info_ptr->from_host.u.rplys; + info_ptr->n_replies = 1; + if(ptr) + { + strncpy(pBuffer, ptr->rply,iBufLen); + *cTerm = ptr->term; + } + else + { + return NOREPLY; + } + return True; + } + +/*---------------------------------------------------------------------------*/ + int SerialError(int iErr, char *pBuffer, int iBufLen) + { + switch(iErr) + { + case -320: + strncpy(pBuffer,"Select failed to find data",iBufLen); + break; + case -300: + case NOCONNECTION: + strncpy(pBuffer,"Not connected",iBufLen); + break; + case -301: + strncpy(pBuffer,"No reply found", iBufLen); + break; + case -100: + strncpy(pBuffer,"No reply found", iBufLen); + break; + case EL734__BAD_ADR: + strncpy(pBuffer,"SERIAL__BAD_ADR",iBufLen); + break; + case EL734__BAD_BIND: + strncpy(pBuffer,"SERIAL__BAD_BIND",iBufLen); + break; + case EL734__BAD_CMD: + strncpy(pBuffer,"SERIAL__BAD_CMD",iBufLen); + break; + case EL734__BAD_CONNECT: + strncpy(pBuffer,"SERIAL__BAD_CONNECT",iBufLen); + break; + case EL734__BAD_FLUSH: + strncpy(pBuffer,"SERIAL__BAD_FLUSH",iBufLen); + break; + case EL734__BAD_HOST: + strncpy(pBuffer,"SERIAL__BAD_HOST",iBufLen); + break; + case EL734__BAD_ID: + strncpy(pBuffer,"SERIAL__BAD_ID",iBufLen); + break; + case EL734__BAD_ILLG: + strncpy(pBuffer,"SERIAL__BAD_ILLG",iBufLen); + break; + case EL734__BAD_LOC: + strncpy(pBuffer,"SERIAL__BAD_LOC",iBufLen); + break; + case EL734__BAD_MALLOC: + strncpy(pBuffer,"SERIAL__BAD_MALLOC",iBufLen); + break; + case EL734__BAD_NOT_BCD: + strncpy(pBuffer,"SERIAL__BAD_NOT_BCD",iBufLen); + break; + case EL734__BAD_OFL: + strncpy(pBuffer,"SERIAL__BAD_OFL",iBufLen); + break; + case EL734__BAD_PAR: + strncpy(pBuffer,"SERIAL__BAD_PAR",iBufLen); + break; + + case EL734__BAD_RECV: + strncpy(pBuffer,"SERIAL__BAD_RECV",iBufLen); + break; + case EL734__BAD_RECV_NET: + strncpy(pBuffer,"SERIAL__BAD_RECV_NET",iBufLen); + break; + case EL734__BAD_RECV_PIPE: + strncpy(pBuffer,"SERIAL__BAD_RECV_PIPE",iBufLen); + break; + case EL734__BAD_RECV_UNKN: + strncpy(pBuffer,"SERIAL__BAD_RECV_UNKN",iBufLen); + break; + case EL734__BAD_RECVLEN: + strncpy(pBuffer,"SERIAL__BAD_RECVLEN",iBufLen); + break; + case EL734__BAD_RECV1: + strncpy(pBuffer,"SERIAL__BAD_RECV1",iBufLen); + break; + case EL734__BAD_RECV1_NET: + strncpy(pBuffer,"SERIAL__BAD_RECV1_NET",iBufLen); + break; + case EL734__BAD_RECV1_PIPE: + strncpy(pBuffer,"SERIAL__BAD_RECV1_PIPE",iBufLen); + break; + case EL734__BAD_RNG: + strncpy(pBuffer,"SERIAL__BAD_RNG",iBufLen); + break; + case EL734__BAD_SEND: + strncpy(pBuffer,"SERIAL__BAD_SEND",iBufLen); + break; + case EL734__BAD_SEND_PIPE: + strncpy(pBuffer,"SERIAL__BAD_SEND_PIPE",iBufLen); + break; + case EL734__BAD_SEND_NET: + strncpy(pBuffer,"SERIAL__BAD_SEND_NET",iBufLen); + break; + case EL734__BAD_SEND_UNKN: + strncpy(pBuffer,"SERIAL__BAD_SEND_UNKN",iBufLen); + break; + case EL734__BAD_SENDLEN: + strncpy(pBuffer,"SERIAL__BAD_SENDLEN",iBufLen); + break; + case EL734__BAD_SOCKET: + strncpy(pBuffer,"SERIAL__BAD_SOCKET",iBufLen); + break; + case EL734__BAD_TMO: + strncpy(pBuffer,"SERIAL__BAD_TMO",iBufLen); + break; + case EL734__FORCED_CLOSED: + strncpy(pBuffer,"SERIAL__FORCED_CLOSED",iBufLen); + break; + case OPENFAILURE: + strncpy(pBuffer, + "FAILED to open connection to serial port server", iBufLen); + break; + default: + strcpy(pBuffer,"Unknown SERIAL error"); + break; + } + return 1; + } +/*---------------------------------------------------------------------------*/ + int SerialWriteRead(void **pData, char *pCommand, + char *pBuffer, int iBufLen) + { + + struct SerialInfo *pInfo = NULL; + int iRet; + time_t tTarget, tCurrent; + + pInfo = (struct SerialInfo *)*pData; + + /* write */ + iRet = SerialSend(pData,pCommand); + if(iRet != 1) + { + SerialError(iRet, pBuffer,iBufLen); + return iRet; + } + + /* check for answers for maximum time out */ + tTarget = tCurrent = time(&tCurrent); + tTarget += pInfo->tmo*100 - 90; + + while(tCurrent < tTarget) + { + pInfo->pFunc(pInfo->pData, 100); + iRet = SerialReceive(pData, pBuffer,iBufLen); + if( iRet != 1) + { + if(iRet != SELECTFAIL) + { + /* error ! */ + SerialError(iRet, pBuffer,iBufLen); + return iRet; + } + } + else + { + return 1; /* there is data read, we are done */ + } + tCurrent = time(&tCurrent); + } + return TIMEOUT; + } +/*---------------------------------------------------------------------------*/ + int SerialNoReply(void **pData, char *pCommand) + { + + struct SerialInfo *pInfo = NULL; + int iRet, iOld, i; + char pBuffer[30]; + + pInfo = (struct SerialInfo *)*pData; + + iOld = pInfo->tmo; + pInfo->tmo = 0; + + /* write */ + iRet = SerialSend(pData,pCommand); + if(iRet != 1) + { + pInfo->tmo = iOld; + return iRet; + } + + /* try some time to find a TMO */ + for(i = 0 ; i < 10; i++) + { + usleep(50); + SerialReceive(pData, pBuffer,29); + if(strcmp(pBuffer,"?TMO") == 0) + { + break; + } + } + if(i > 7) + { + printf("TMO received after %d cycles \n",i); + } + pInfo->tmo = iOld; + return 1; + } +/*-------------------------------------------------------------------------*/ + void SetSerialSleep(void **pData, SerialSleep pFun, void *pUserData) + { + struct SerialInfo *pInfo = NULL; + int iRet; + + pInfo = (struct SerialInfo *)*pData; + pInfo->pFunc = pFun; + pInfo->pData = pUserData; + + } + + + + + + + + + + + + + + + + + diff --git a/hardsup/serialsinq.h b/hardsup/serialsinq.h new file mode 100644 index 0000000..60e5dc8 --- /dev/null +++ b/hardsup/serialsinq.h @@ -0,0 +1,56 @@ + +#line 156 "velodorn.w" + +/*---------------------------------------------------------------------------- + S E R I A L S I N Q + + Utility functions for maintaining a connection to a RS--232 port on a + Macintosh computer running the SINQ terminal server application. + + Mark Koennecke, Juli 1997 + + copyright: see implementation file +------------------------------------------------------------------------------*/ +#ifndef SERIALSINQ +#define SERIALSINQ +#define NOREPLY -100 +#define NOCONNECTION -121 +#define SELECTFAIL -120 +#define TIMEOUT -730 +#define INTERRUPTED -132 +#define OPENFAILURE -133 + +#line 30 "velodorn.w" + + int SerialOpen(void **pData, char *pHost, int iPort, int iChannel); + int SerialForceOpen(void **pData, char *pHost, int iPort, int iChannel); + int SerialConfig(void **pData, int iTmo); + int SerialGetTmo(void **pData); + int SerialATerm(void **pData, char *pTerm); + int SerialAGetTerm(void **pData, char *pTerm, int iTermLen); + int SerialSendTerm(void **pData, char *pTerm); + int SerialGetSocket(void **pData); + int SerialClose(void **pData); + int SerialForceClose(void **pData); + + int SerialSend(void **pData, char *pCommand); + int SerialReceive(void **pData, char *pBuffer, int iBufLen); + int SerialReceiveWithTerm(void **pData, char *pBuffer, + int iBufLen,char *cTerm); + int SerialError(int iError, char *pError, int iErrLen); + int SerialWriteRead(void **pData, char *pCommand, + char *pBuffer, int iBufLen); + int SerialNoReply(void **pData, char *pCommand); + +#line 175 "velodorn.w" + +/*-------------------------- The sleeperette -----------------------------*/ + +#line 116 "velodorn.w" + + typedef int (*SerialSleep)(void *pData, int iTime); + void SetSerialSleep(void **pData, SerialSleep pFunc, void *pUserData); + +#line 177 "velodorn.w" + +#endif diff --git a/hardsup/sinq_defs.h b/hardsup/sinq_defs.h new file mode 100644 index 0000000..c437f8e --- /dev/null +++ b/hardsup/sinq_defs.h @@ -0,0 +1,108 @@ +/* +** TAS_SRC:[LIB]SINQ_DEFS.H +** +** Include file generated from SINQ_DEFS.OBJ +** +** 29-AUG-2000 09:49:31.72 +*/ + +#define SS__NORMAL 0x1 +#define SS__WASSET 0x9 +#define SS__ILLEFC 0xEC +#define SS__UNASEFC 0x234 +#define SEM_BIT 0x0 +#define SEM_PID 0x4 +#define SEM_WFLG 0x8 +#define SEM_IDNT 0xC +#define SEM_CNT0 0x1C +#define SEM_CNT1 0x20 +#define SEM_CNT2 0x24 +#define SEM_CNT3 0x28 +#define SEM_SUB_PID 0x2C +#define SEM_SIZE 0x40 +#define SEM__CAMAC_CSR 0x0 +#define SEM__CAMAC_IVG 0x40 +#define SEM__CAMAC_GPIB 0x80 +#define SEM__CAMAC_3344 0xC0 +#define MAP__CAMAC_FIELD_0 0x0 +#define MAP__CAMAC_FIELD_1 0x1 +#define MAP__CAMAC_FIELD_2 0x2 +#define MAP__CAMAC_FIELD_CSR 0x3 +#define MAP__CAMAC_SEMAPHORE 0x4 +#define MAP__DELTAT_CB 0x5 +#define MAP__DELTAT_SCALERS 0x6 +#define CAMIF__JCC 0x1 +#define CAMIF__GEC0 0x3 +#define CAMIF__CES 0x4 +#define CAMIF__BIRA 0x5 +#define CAMIF__GEC1 0x6 +#define CAMIF__GEC2 0x7 +#define CAMIF__GEC3 0x8 +#define CAMIF__CCP 0x9 +#define CAMIF__OS9 0xA +#define CAMIF__KCBD 0xB +#define CAMIF__VAN 0xC +#define CAMIF__KVCC 0xD +#define CAMIF__M_XQ 0x3 +#define CAMIF__X_Q 0x0 +#define CAMIF__X_NOQ 0x1 +#define CAMIF__NOX_Q 0x2 +#define CAMIF__NOX_NOQ 0x3 +#define CAMIF__NO_CAMIF 0x4 +#define CAMIF__TMOUT 0x8 +#define CAMIF__ILLPAR 0xC +#define CAMIF__RPTFAIL 0x14 +#define CAMIF__SEMTMO 0x18 +#define MSR__BUSY 0x1 +#define MSR__OK 0x2 +#define MSR__REF_OK 0x4 +#define MSR__STOPPED 0x8 +#define MSR__LO_LIM 0x10 +#define MSR__HI_LIM 0x20 +#define MSR__HALT 0x40 +#define MSR__RUN_FAULT 0x80 +#define MSR__RUN_FAIL 0x100 +#define MSR__POS_FAULT 0x200 +#define MSR__POS_FAIL 0x400 +#define MSR__REF_FAIL 0x800 +#define MSR__AC_FAIL 0x1000 +#define MSR__LIM_ERR 0x2000 +#define SS__HALT 0x1 +#define SS__CCW 0x2 +#define SS__STP 0x4 +#define SS__LS1 0x8 +#define SS__LS2 0x10 +#define SS__LSX 0x20 +#define EL737_STATE_UNKNOWN 0xFFFFFFFE +#define EL737_STATE_OFFLINE 0xFFFFFFFF +#define EL737_STATE_MS 0x0 +#define EL737_STATE_PTS 0x1 +#define EL737_STATE_PCS 0x2 +#define EL737_STATE_LRTS 0x5 +#define EL737_STATE_LRCS 0x6 +#define EL737_STATE_PTSP 0x9 +#define EL737_STATE_PCSP 0xA +#define EL737_STATE_LRTSP 0xD +#define EL737_STATE_LRCSP 0xE +#define SINQHM_CNCT 0x1 +#define SINQHM_CONFIG 0x2 +#define SINQHM_DECONFIG 0x3 +#define SINQHM_EXIT 0x4 +#define SINQHM_STATUS 0x5 +#define SINQHM_DBG 0x6 +#define SINQHM_CLOSE 0x101 +#define SINQHM_INH 0x102 +#define SINQHM_IOREG 0x103 +#define SINQHM_READ 0x104 +#define SINQHM_SET_TDC 0x105 +#define SINQHM_SHOW 0x106 +#define SINQHM_WRITE 0x107 +#define SINQHM_ZERO 0x108 +#define INH_SET 0x1 +#define INH_CLR 0x2 +#define INH_TST 0x3 +#define IO_SET 0x1 +#define IO_CLR 0x2 +#define IO_PULSE 0x3 +#define TT_PORT__NO_RETRY 0x1 +#define TT_PORT__NO_SIG 0x2 diff --git a/hardsup/sinq_prototypes.h b/hardsup/sinq_prototypes.h new file mode 100644 index 0000000..2be3165 --- /dev/null +++ b/hardsup/sinq_prototypes.h @@ -0,0 +1,674 @@ +#ifndef _sinq_prototypes_loaded_ +#define _sinq_prototypes_loaded_ +/*---------------------------------------------- SINQ_PROTOTYPES.H Ident V02T +** +** Prototype header file for entry points in SINQ.OLB +** +** Updates: +** V01A 21-Mar-1996 DM Initial version. +*/ +#ifdef VAXC +#include asynsrv_def +#include rs232c_def +#include el734_def +#include el737_def +#else +#include +#include +#include +#include +#endif +/* ---------------------------------------------------------------------*/ + int AsynSrv_ChanClose ( + struct AsynSrv__info *asyn_info); + int AsynSrv_Close ( + struct AsynSrv__info *asyn_info, + int force_flag); + int AsynSrv_Config ( + struct AsynSrv__info *asyn_info, + ...); + int AsynSrv_ConfigDflt ( + char *par_id, + ...); + void AsynSrv_ErrInfo ( + char **entry_txt, + int *errcode, + int *my_errno, + int *vaxc_errno); + int AsynSrv_Flush ( + struct AsynSrv__info *asyn_info); + int AsynSrv_GetLenTerm ( + struct AsynSrv__info *asyn_info, + struct RS__RespStruct *rcve_buff, + char *rply, + int *len, + char *term); + char *AsynSrv_GetReply ( + struct AsynSrv__info *asyn_info, + struct RS__RespStruct *rcve_buff, + char *last_rply); + int AsynSrv_Open ( + struct AsynSrv__info *asyn_info); + int AsynSrv_OpenNew ( + struct AsynSrv__info *asyn_info); + int AsynSrv_SendCmnds ( + struct AsynSrv__info *asyn_info, + struct RS__MsgStruct *send_buff, + struct RS__RespStruct *rcve_buff, + ...); + int AsynSrv_SendCmndsBig ( + struct AsynSrv__info *asyn_info, + struct RS__MsgStruct *send_buff, + int send_buff_size, + struct RS__RespStruct *rcve_buff, + int rcve_buff_size, + ...); + int AsynSrv_Trace ( + struct AsynSrv__info *asyn_info, + int state); + int AsynSrv_Trace_Write ( + struct AsynSrv__info *asyn_info); +/* ---------------------------------------------------------------------*/ + int C_log_arr_get ( + char *name, + int arr_size, + int *value, + int indx); + int C_log_flt_get ( + char *name, + float *value, + int indx); + int C_log_int_get ( + char *name, + long int *value, + int indx); + int C_log_str_get ( + char *name, + char *value, + int val_size, + int indx); +/* ---------------------------------------------------------------------*/ + int C_str_edit ( + char *out, + char *in, + char *ctrl, + int *length); +/* ---------------------------------------------------------------------*/ + int C_tt_port_config ( + int *hndl, + int mask); + int C_tt_port_connect ( + int *hndl, + int *chan, + char *lognam, + char *pwd); + int C_tt_port_disconnect ( + int *hndl); + int C_tt_port_io ( + int *hndl, + char *rqst, + char *term, + char *answ, + int *answ_len, + int flush, + int tmo); +/* ---------------------------------------------------------------------*/ + int EL734_Close ( + void **handle, + int force_flag); + int EL734_Config ( + void **handle, + ...); + char *EL734_EncodeMSR ( + char *text, + int text_len, + int msr, + int ored_msr, + int fp_cntr, + int fr_cntr); + char *EL734_EncodeSS ( + char *text, + int text_len, + int ss); + void EL734_ErrInfo ( + char **entry_txt, + int *errcode, + int *my_errno, + int *vaxc_errno); + int EL734_GetAirCush ( + void **handle, + int *present, + int *state); + int EL734_GetEncGearing ( + void **handle, + int *nominator, + int *denominator); + int EL734_GetId ( + void **handle, + char *id_txt, + int id_len); + int EL734_GetLimits ( + void **handle, + float *lo, + float *hi); + int EL734_GetMotorGearing ( + void **handle, + int *nominator, + int *denominator); + int EL734_GetNullPoint ( + void **handle, + int *null_pt); + int EL734_GetPosition ( + void **handle, + float *ist_posit); + int EL734_GetPrecision ( + void **handle, + int *n_dec); + int EL734_GetRefMode ( + void **handle, + int *mode); + int EL734_GetRefParam ( + void **handle, + float *param); + int EL734_GetSpeeds ( + void **handle, + int *lo, + int *hi, + int *ramp); + int EL734_GetStatus ( + void **handle, + int *msr, + int *ored_msr, + int *fp_cntr, + int *fr_cntr, + int *ss, + float *ist_posit); + int EL734_GetZeroPoint ( + void **handle, + float *zero_pt); + int EL734_MoveNoWait ( + void **handle, + float soll_posit); + int EL734_MoveWait ( + void **handle, + float soll_posit, + int *ored_msr, + int *fp_cntr, + int *fr_cntr, + float *ist_posit); + int EL734_Open ( + void **handle, + char *host, + int port, + int chan, + int motor, + char *id); + int EL734_PutOffline ( + void **handle); + int EL734_PutOnline ( + void **handle, + int echo); + int EL734_SendCmnd ( + void **handle, + char *cmnd, + char *rply, + int rply_size); + int EL734_SetAirCush ( + void **handle, + int state); + int EL734_SetErrcode ( + struct EL734info *info_ptr, + char *response, + char *cmnd); + int EL734_SetHighSpeed ( + void **handle, + int hi); + int EL734_SetLowSpeed ( + void **handle, + int lo); + int EL734_SetRamp ( + void **handle, + int ramp); + int EL734_Stop ( + void **handle); + int EL734_WaitIdle ( + void **handle, + int *ored_msr, + int *fp_cntr, + int *fr_cntr, + float *ist_posit); + void EL734_ZeroStatus ( + void **handle); +/* ---------------------------------------------------------------------*/ + int EL737_Close ( + void **handle, + int force_flag); + int EL737_Config ( + void **handle, + ...); + int EL737_Continue ( + void **handle, + int *status); + int EL737_EnableThresh ( + void **handle, + int indx); + void EL737_ErrInfo ( + char **entry_txt, + int *errcode, + int *my_errno, + int *vaxc_errno); + int EL737_GetMonIntegTime ( + void **handle, + int indx, + float *mon_integ_time); + int EL737_GetRateIntegTime ( + void **handle, + float *rate_integ_time); + void *EL737_GetReply ( + void **handle, + void *last_rply); + int EL737_GetStatus ( + void **handle, + int *c1, + int *c2, + int *c3, + int *c4, + float *timer, + int *rs); + int EL737_GetStatusExtra ( + void **handle, + int *c5, + int *c6, + int *c7, + int *c8); + int EL737_GetThresh ( + void **handle, + int *indx, + float *val); + int EL737_Open ( + void **handle, + char *host, + int port, + int chan); + int EL737_Pause ( + void **handle, + int *status); + int EL737_SendCmnd ( + void **handle, + char *cmnd, + char *rply, + int rply_size); + int EL737_SetErrcode ( + struct EL737info *info_ptr, + char *response, + char *cmnd); + int EL737_SetThresh ( + void **handle, + int indx, + float val); + int EL737_StartCnt ( + void **handle, + int preset_count, + int *status); + int EL737_StartTime ( + void **handle, + float preset_time, + int *status); + int EL737_Stop ( + void **handle, + int *c1, + int *c2, + int *c3, + int *c4, + float *timer, + int *status); + int EL737_StopFast ( + void **handle); + int EL737_WaitIdle ( + void **handle, + int *c1, + int *c2, + int *c3, + int *c4, + float *timer); +/* ---------------------------------------------------------------------*/ + int EL755_Close ( + void **handle, + int force_flag); + int EL755_Config ( + void **handle, + ...); + void EL755_ErrInfo ( + char **entry_txt, + int *errcode, + int *my_errno, + int *vaxc_errno); + int EL755_ErrorLog ( + char *routine_name, + char *text); + int EL755_GetConstant ( + void **handle, + float *value); + int EL755_GetCurrents ( + void **handle, + float *soll, + float *ist); + int EL755_GetId ( + void **handle, + char *id_txt, + int id_len); + int EL755_GetLimit ( + void **handle, + float *value); + int EL755_GetRamp ( + void **handle, + float *value); + int EL755_GetTimeConstant ( + void **handle, + float *value); + int EL755_GetVoltageRange ( + void **handle, + float *value); + int EL755_Open ( + void **handle, + char *host, + int port, + int chan, + int indx); + int EL755_PutOffline ( + void **handle); + int EL755_PutOnline ( + void **handle, + int echo); + int EL755_SendTillSameStr ( + void **handle, + char *cmnd, + char *rply, + int rply_len); + int EL755_SendTillSameVal ( + void **handle, + char *cmnd, + float *val); + int EL755_SendTillTwoVals ( + void **handle, + char *cmnd, + float *val0, + float *val1); + int EL755_SetConstant ( + void **handle, + float value); + int EL755_SetCurrent ( + void **handle, + float soll); + int EL755_SetLimit ( + void **handle, + float value); + int EL755_SetRamp ( + void **handle, + float value); + int EL755_SetTimeConstant ( + void **handle, + float value); + int EL755_SetVoltageRange ( + void **handle, + float value); +/* ---------------------------------------------------------------------*/ + int Fluke_Close ( + void **handle, + int force_flag); + int Fluke_Config ( + void **handle, + ...); + void Fluke_ErrInfo ( + char **entry_txt, + int *errcode, + int *my_errno, + int *vaxc_errno); + int Fluke_ErrorLog ( + char *routine_name, + char *text); + int Fluke_Open ( + void **handle, + char *host, + int port, + int chan); + int Fluke_Read ( + void **handle, + float *ist); + int Fluke_SendTillSame ( + void **handle, + char *cmnd, + char *rply, + int rply_len); + int Fluke_SendTillSameVal ( + void **handle, + char *cmnd, + float *val); +/* ---------------------------------------------------------------------*/ + int ITC_Close ( + void **handle, + int force_flag); + int ITC_Config ( + void **handle, + ...); + int ITC_Dump_RAM ( + void **handle, + int buff_size, + char *buff, + int *dump_len, + int *n_diffs); + void ITC_ErrInfo ( + char **entry_txt, + int *errcode, + int *my_errno, + int *vaxc_errno); + int ITC_GetConfig ( + void **handle, + ...); + int ITC_Load_RAM ( + void **handle, + int load_len, + char *buff); + int ITC_Load_Table ( + void **handle, + char *buff); + int ITC_Open ( + void **handle, + char *host, + int port, + int chan); + int ITC_Read_ITC_Sensor ( + void **handle, + int sensor, + float factor, + float *value); + int ITC_Read_LTC11_Sensor ( + void **handle, + int sensor, + float *value); + int ITC_Read_LTC11_SetPt ( + void **handle, + float *value); + int ITC_ReadAuxTemp ( + void **handle, + float *value); + int ITC_ReadControlTemp ( + void **handle, + float *value); + int ITC_ReadHeaterOp ( + void **handle, + float *op_level, + float *op_percent); + int ITC_ReadId ( + void **handle, + char *id_txt, + int id_txt_len, + int *id_len); + int ITC_ReadPID ( + void **handle, + float *p, + float *i, + float *d); + int ITC_ReadSampleTemp ( + void **handle, + float *s_temp); + int ITC_ReadSetPoint ( + void **handle, + float *sp_temp); + int ITC_ReadStatus ( + void **handle, + char *status_txt, + int status_txt_len, + int *status_len, + int *auto_state, + int *remote_state); + int ITC_SendTillAckOk ( + void **handle, + struct RS__MsgStruct *to_host, + struct RS__RespStruct *from_host, + char *cmnd); + int ITC_SendTillSame ( + void **handle, + struct RS__MsgStruct *to_host, + struct RS__RespStruct *from_host, + char *cmnd, + char *rply, + int rply_len); + int ITC_SendTillSameLen ( + void **handle, + struct RS__MsgStruct *to_host, + struct RS__RespStruct *from_host, + char *cmnd, + char *rply, + int rply_len); + int ITC_SendTillSameLenAckOK ( + void **handle, + struct RS__MsgStruct *to_host, + struct RS__RespStruct *from_host, + char *cmnd, + char *rply, + int rply_len); + int ITC_SetControlTemp ( + void **handle, + float s_temp); + int ITC_SetHeatLevel ( + void **handle, + float heat_percent); + int ITC_ErrorLog ( + char *routine_name, + char *text); +/* ---------------------------------------------------------------------*/ + int SPS_Close ( + void **handle, + int force_flag); + int SPS_Config ( + void **handle, + ...); + void SPS_ErrInfo ( + char **entry_txt, + int *errcode, + int *my_errno, + int *vaxc_errno); + int SPS_ErrorLog ( + char *routine_name, + char *text); + int SPS_GetStatus ( + void **handle, + unsigned char *status_vals, + int n_status_vals, + int *adc_vals, + int n_adc_vals); + int SPS_Open ( + void **handle, + char *host, + int port, + int chan); + int SPS_SendTillSame ( + void **handle, + struct RS__MsgStruct *to_host, + struct RS__RespStruct *from_host, + char *cmnd, + char *rply, + int rply_len); + int SPS_SendTillSameLen ( + void **handle, + struct RS__MsgStruct *to_host, + struct RS__RespStruct *from_host, + char *cmnd, + char *rply, + int rply_len); +/* ---------------------------------------------------------------------*/ + int VelSel_Close ( + void **handle, + int force_flag); + void VelSel_Config ( + void **handle, + int msec_tmo, + char *eot_str); + void VelSel_ErrInfo ( + char **entry_txt, + int *errcode, + int *my_errno, + int *vaxc_errno); + void *VelSel_GetReply ( + void **handle, + void *last_rply); + int VelSel_GetStatus ( + void **handle, + char *status_str, + int status_str_len); + int VelSel_Open ( + void **handle, + char *host, + int port, + int chan); + int VelSel_SendCmnd ( + void **handle, + char *cmnd, + char *rply, + int rply_size); +/* ---------------------------------------------------------------------*/ + void FailInet ( + char *text); + void GetErrno ( + int *his_errno, + int *his_vaxc_errno); + int MakeCharPrintable ( + char *out, + int out_size, + char in); + char *MakePrint ( + char *text); + char *MakePrintable ( + char *out, + int out_size, + char *in); + void *Map_to_ACS (); + char *StrEdit ( + char *out, + char *in, + char *ctrl, + int *ln); + char *StrJoin ( + char *result, + int result_size, + char *str_a, + char *str_b); + int StrMatch ( + char *str_a, + char *str_b, + int min_len); + int Get_TASMAD_Info ( + char *file_name, + int *nItems, + ...); + int Get_TASMAD_Info_Filename ( + char *file_name, + char *buf, + int *bufSize); + int Update_TASMAD_Info ( + char *file_name, + int *nItems, + ...); +/*--------------------------------------------- End of SINQ_PROTOTYPES.H --*/ +#endif /* _sinq_prototypes_loaded_ */ diff --git a/hardsup/sinqhm.c b/hardsup/sinqhm.c new file mode 100644 index 0000000..2130591 --- /dev/null +++ b/hardsup/sinqhm.c @@ -0,0 +1,1770 @@ +/*------------------------------------------------------------------------- + S I N Q H M + + Implementation file for the SINQ histogram memory utility functions. + + David Maden, Mark Koennecke, April 1997 + + Updated for TOF support: Mark Koennecke, December 1998 + + Added Project for AMOR: Mark Koennecke, August 2001 + + Copyright: + + Labor fuer Neutronenstreuung + Paul Scherrer Institut + CH-5423 Villigen-PSI + + + The authors hereby grant permission to use, copy, modify, distribute, + and license this software and its documentation for any purpose, provided + that existing copyright notices are retained in all copies and that this + notice is included verbatim in any distributions. No written agreement, + license, or royalty fee is required for any of the authorized uses. + Modifications to this software may be copyrighted by their authors + and need not follow the licensing terms described here, provided that + the new terms are clearly indicated on the first page of each file where + they apply. + + IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY + FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY + DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE + IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE + NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR + MODIFICATIONS. +----------------------------------------------------------------------------*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef FORTIFY +#include "fortify.h" +#endif + +#include "sinqhm.h" +#include "sinqhm.i" + +/* missing in some network stuff?? */ + +#ifndef MSG_WAITALL +#define MSG_WAITALL 0 +#endif + +/* this may be a cludge for a missing prototype on Digital Unix */ +extern int close(int fp); + +/*-----------------------------------------------------------------------*/ + static int SendDAQCommand(pSINQHM self, int iCommand, int *iDaq) + { + struct req_buff_struct Req_buff; + struct rply_buff_struct Rply_buff; + int status, iRet; + + assert(self); + assert(self->iClientSocket); + + /* prepare a message */ + Req_buff.bigend = htonl (0x12345678); + Req_buff.cmnd = htonl (SQHM_DAQ); + Req_buff.u.daq.sub_cmnd = htonl (iCommand); + + /* send the message */ + status = send (self->iClientSocket, (char *) &Req_buff, sizeof (Req_buff), 0); + if (status == -1) + { + return SEND_ERROR; + } + if (status != sizeof (Req_buff)) + { + return SEND_ERROR; + } + + /* get a response */ + status = recv (self->iClientSocket, (char *) &Rply_buff, + sizeof (Rply_buff), MSG_WAITALL); + + /* check various error conditions */ + if (status == -1) + { + return RECEIVE_ERROR; + } + if (status != sizeof (Rply_buff)) + { + return INSUFFICIENT_DATA; + } + if(ntohl (Rply_buff.bigend) != 0x12345678) + { + return BYTE_ORDER_CHAOS; + } + if((iRet = ntohl (Rply_buff.status)) != KER__SUCCESS) + { + return SOFTWARE_ERROR; + } + + *iDaq = ntohs (Rply_buff.u.daq.daq_now); + + /* success */ + return 1; + } +/*-----------------------------------------------------------------------*/ + static int SendDAQStatus(pSINQHM self, + struct rply_buff_struct *pReply) + { + struct req_buff_struct Req_buff; + int status, iRet; + + assert(self); + + if(!self->iClientSocket) + { + return 0; + } + + /* prepare a message */ + Req_buff.bigend = htonl (0x12345678); + Req_buff.cmnd = htonl (SQHM_STATUS); + + /* send the message */ + status = send (self->iClientSocket, (char *) &Req_buff, sizeof (Req_buff), 0); + if (status == -1) + { + return SEND_ERROR; + } + if (status != sizeof (Req_buff)) + { + return SEND_ERROR; + } + + /* get a response */ + status = recv (self->iClientSocket, (char *) pReply, + sizeof (struct rply_buff_struct), MSG_WAITALL); + + /* check various error conditions */ + if (status == -1) + { + return RECEIVE_ERROR; + } + if (status != sizeof (struct rply_buff_struct)) + { + return INSUFFICIENT_DATA; + } + if(ntohl (pReply->bigend) != 0x12345678) + { + return BYTE_ORDER_CHAOS; + } + if((iRet = ntohl (pReply->status)) != KER__SUCCESS) + { + return SOFTWARE_ERROR; + } + + /* success */ + return 1; + } + +/*-------------------------------------------------------------------------*/ + pSINQHM CreateSINQHM(char *pHMComputer, int iMasterPort) + { + pSINQHM pNew = NULL; + + /* new memory */ + pNew = (pSINQHM)malloc(sizeof(SINQHM)); + if(!pNew) + { + return NULL; + } + memset(pNew,0,sizeof(SINQHM)); + + pNew->pHMComputer = strdup(pHMComputer); + pNew->iMasterPort = iMasterPort; + + return pNew; + } +/*-----------------------------------------------------------------------*/ + pSINQHM CopySINQHM(pSINQHM self) + { + pSINQHM pNew = NULL; + + assert(self); + + pNew = CreateSINQHM(self->pHMComputer,self->iMasterPort); + if(!pNew) + { + return NULL; + } + pNew->iBinWidth = self->iBinWidth; + pNew->iPacket = self->iPacket; + pNew->iRank = self->iRank; + pNew->iLength = self->iLength; + return pNew; + } +/*-------------------------------------------------------------------------*/ + void SINQHMSetPar(pSINQHM self, int iRank, int iLength, int iBin) + { + assert(self); + + self->iRank = iRank; + self->iLength = iLength; + self->iBinWidth = iBin; + } +/*-------------------------------------------------------------------------*/ + void DeleteSINQHM(pSINQHM self) + { + int i; + + assert(self); + + if(self->pHMComputer) + { + free(self->pHMComputer); + } + + for(i = 0; i < self->iBanks; i++) + { + if(self->pBank[i].iEdges) + { + free(self->pBank[i].iEdges); + } + } + + /* make sure a possible clients connection gets murdered */ + if(self->iClientSocket) + { + SINQHMCloseDAQ(self); + } + free(self); + } +/*------------------------------------------------------------------------*/ + int SINQHMConfigure(pSINQHM self, int iMode, int iRank, int iLength, + int iBinWidth, int iLowBin, int iCompress) + { + int status, iRet; + struct req_buff_struct Req_buff; + struct rply_buff_struct Rply_buff; + + assert(self); + + /* branch specially for TOF flight mode */ + if( (iMode >= SQHM__TOF) && (iMode < SQHM__HM_PSD) ) + { + self->iBinWidth = iBinWidth; + return SINQHMTimeBin(self,iMode); + } + + /* fill in the request data structure */ + Req_buff.bigend = htonl (0x12345678); + Req_buff.cmnd = htonl (SQHM_CONFIG); + Req_buff.u.cnfg.mode = htonl (iMode); + Req_buff.u.cnfg.u.hm_dig.n_hists = htonl (iRank); + printf("%d\n", ntohl(Req_buff.u.cnfg.u.hm_dig.n_hists)); + Req_buff.u.cnfg.u.hm_dig.lo_bin = htonl (iLowBin); + Req_buff.u.cnfg.u.hm_dig.num_bins = htonl (iLength); + Req_buff.u.cnfg.u.hm_dig.bytes_per_bin = htonl (iBinWidth); + Req_buff.u.cnfg.u.hm_dig.compress = htonl (iCompress); + + /* try, get a connection to master server */ + status = OpenMasterConnection(self); + if(status < 0) + { + return status; + } + + /* send request */ + status = send(self->iMasterSocket,(char *)&Req_buff, sizeof(Req_buff),0); + if(status == -1) + { + return SEND_ERROR; + } + + /* get a reply */ + iRet = GetMasterReply(self,&Rply_buff,sizeof(Rply_buff)); + if(iRet < 0) + { + /* try close the socket */ + close(self->iMasterSocket); + self->iMasterSocket = 0; + return iRet; + } + else + { + /* configure successful, keep the data */ + self->iBinWidth = iBinWidth; + self->iLength = iLength; + self->iRank = iRank; + + /* close the socket */ + status = close(self->iMasterSocket); + self->iMasterSocket = 0; + if((status != 0) && (errno != ECONNRESET)) + { + return CLOSE_ERROR; + } + } + return 1; /* success, finally */ + } +/*------------------------------------------------------------------------*/ + int SINQHMConfigurePSD(pSINQHM self, int iMode, + int xSize, int xOff, int xFac, + int ySize, int yOff, int yFac, + int iBinWidth, + float *iEdges, int iEdgeLength) + { + int status, iRet; + struct req_buff_struct Req_buff; + struct rply_buff_struct Rply_buff; + int iLength, i, iDelay; + unsigned int iExtra; + char *pBuffer = NULL, *pPtr; + struct tof_edge_arr tea; + int iTeaLength; + struct tof_bank toba; + + assert(self); + + /* set up detector bank information. This code supports only + one detector bank as of now. Which is appropriate for the + detector at hand. + */ + self->iBinWidth = iBinWidth; + SINQHMDefineBank(self,0,0,xSize*ySize, + iEdges,iEdgeLength); + + /* figure out how long we are going to be*/ + iLength = 36 + self->iBanks*sizeof(struct tof_bank); + for(i = 0; i < self->iBanks; i++) + { + iLength += 8 + self->pBank[i].iEdgeLength*sizeof(SQint32); + } + if(iLength < 64) + iLength = 64; + /* allocate send buffer */ + pBuffer = (char *)malloc(iLength*sizeof(char)); + if(!pBuffer) + { + return HIST_BAD_ALLOC; + } + memset(pBuffer,0,iLength); + + /* do the message header */ + iExtra = iLength - sizeof(Req_buff); + if(iExtra < 0) + iExtra = 0; + iDelay = self->pBank[0].iEdges[0]; + Req_buff.bigend = htonl (0x12345678); + Req_buff.cmnd = htonl (SQHM_CONFIG); + Req_buff.u.cnfg.mode = htonl (iMode); + Req_buff.u.cnfg.u.psd.n_extra_bytes = htonl (iExtra); + Req_buff.u.cnfg.u.psd.n_edges = htons (1); + Req_buff.u.cnfg.u.psd.n_banks = htons (1); + Req_buff.u.cnfg.u.psd.xOffset = htons (xOff); + Req_buff.u.cnfg.u.psd.yOffset = htons (yOff); + Req_buff.u.cnfg.u.psd.xFactor = htons (xFac); + Req_buff.u.cnfg.u.psd.yFactor = htons (yFac); + Req_buff.u.cnfg.u.psd.xSize = htons (xSize); + Req_buff.u.cnfg.u.psd.ySize = htons (ySize); + Req_buff.u.cnfg.u.psd.preset_delay = htonl((int)iEdges[0]); + memcpy(pBuffer,&Req_buff,36); + pPtr = pBuffer + 36; + + /* do the edge thingies */ + for(i = 0; i < self->iBanks; i++) + { + tea.n_bins = htonl(self->pBank[i].iEdgeLength-1); + if(self->pBank[i].iEdgeLength == 2) + { + tea.flag = htonl(0); + } + else + { + tea.flag = htonl(1); + } + tea.edges = self->pBank[i].iEdges; + memcpy(pPtr,&tea,8); + pPtr += 8; + iTeaLength = self->pBank[i].iEdgeLength*4; + memcpy(pPtr,self->pBank[i].iEdges,iTeaLength); + pPtr += iTeaLength; + } + + /* do the swiss bank structures */ + for(i = 0; i < self->iBanks; i++) + { + toba.first = htons(self->pBank[i].iStart); + toba.n_cntrs = htons(self->pBank[i].iEnd); + toba.edge_indx = htons(i); + toba.bytes_per_bin = htons(self->iBinWidth); + memcpy(pPtr,&toba,sizeof(struct tof_bank)); + pPtr += sizeof(struct tof_bank); + } + + /* all packed up neat and nicely, send it */ + /* try, get a connection to master server */ + status = OpenMasterConnection(self); + if(status < 0) + { + if(pBuffer) + free(pBuffer); + return status; + } + + /* send request */ + status = send(self->iMasterSocket,pBuffer,iLength ,0); + if(pBuffer) + { + free(pBuffer); + } + if(status == -1) + { + return SEND_ERROR; + } + + /* get a reply */ + iRet = GetMasterReply(self,&Rply_buff,sizeof(Rply_buff)); + if(iRet < 0) + { + /* try close the socket */ + close(self->iMasterSocket); + self->iMasterSocket = 0; + return iRet; + } + else + { + /* close the socket */ + status = close(self->iMasterSocket); + self->iMasterSocket = 0; + if((status != 0) && (errno != ECONNRESET)) + { + return CLOSE_ERROR; + } + } + return 1; /* success, finally */ + } +/*------------------------------------------------------------------------*/ + int SINQHMDeconfigure(pSINQHM self, int iHarsh) + { + int status, iRet; + struct req_buff_struct Req_buff; + struct rply_buff_struct Rply_buff; + + assert(self); + if( (iHarsh != 0) && (iHarsh != 1) ) + { + return INVALID_HARSH; + } + + /* fill in the request data structure */ + Req_buff.bigend = htonl (0x12345678); + Req_buff.cmnd = htonl (SQHM_DECONFIG); + Req_buff.u.decnfg.sub_code = htonl(iHarsh); + + /* try, get a connection to master server */ + status = OpenMasterConnection(self); + if(status < 0) + { + return status; + } + + /* send request */ + status = send(self->iMasterSocket,(char *)&Req_buff, sizeof(Req_buff),0); + if(status == -1) + { + return SEND_ERROR; + } + + /* get a reply */ + iRet = GetMasterReply(self,&Rply_buff,sizeof(Rply_buff)); + if(iRet < 0) + { + close(self->iMasterSocket); + self->iMasterSocket = 0; + return iRet; + } + else + { + status = close(self->iMasterSocket); + self->iMasterSocket = 0; + if((status != 0) && (errno != ECONNRESET)) + { + return CLOSE_ERROR; + } + } + return 1; /* success, finally */ + } +/*------------------------------------------------------------------------*/ + int SINQHMGetStatus(pSINQHM self, int *iMode,int *iDaq, + int *iRank, int *iBinWidth, + int *iLength, int *iClients) + { + int status, iRet; + short sDaq, sFill; + struct req_buff_struct Req_buff; + struct rply_buff_struct Rply_buff; + + assert(self); + + + status = 0; + if(self->iClientSocket) + { + status = SendDAQStatus(self,&Rply_buff); + } + else + { + /* fill in the request data structure */ + Req_buff.bigend = htonl (0x12345678); + Req_buff.cmnd = htonl (SQHM_STATUS); + + /* try, get a connection to master server */ + status = OpenMasterConnection(self); + if(status < 0) + { + return status; + } + + /* send request */ + status = send(self->iMasterSocket,(char *)&Req_buff, sizeof(Req_buff),0); + if(status == -1) + { + return SEND_ERROR; + } + + /* get a reply */ + iRet = GetMasterReply(self,&Rply_buff,sizeof(Rply_buff)); + if(iRet < 0) + { + close(self->iMasterSocket); + self->iMasterSocket = 0; + return iRet; + } + else + { + status = 1; + /* close the socket and go */ + iRet = close(self->iMasterSocket); + self->iMasterSocket = 0; + if((iRet != 0) && (errno != ECONNRESET)) + { + return CLOSE_ERROR; + } + } + } + + if(status) + { + /* transfer results */ + *iMode = ntohl(Rply_buff.u.status.cfg_state); + if((sDaq = ntohs(Rply_buff.u.status.daq_now)) == 0) /* DAQ active */ + { + *iDaq = 1; + } + else + { + sFill = ntohs(Rply_buff.u.status.filler_mask); + if(sFill & sDaq) + { + /* filler is not running */ + *iDaq = 0; + } + else + { + /* inhibited by some mean client */ + *iDaq = 2; + } + } + *iRank = ntohs(Rply_buff.u.status.n_hists); + *iLength = ntohl(Rply_buff.u.status.num_bins); + *iBinWidth = Rply_buff.u.status.bytes_per_bin; + *iClients = Rply_buff.u.status.act_srvrs; + } + return 1; /* success, finally */ + } +/*------------------------------------------------------------------------*/ + int SINQHMDebug(pSINQHM self, int iLevel) + { + int status, iRet; + struct req_buff_struct Req_buff; + struct rply_buff_struct Rply_buff; + + assert(self); + + /* fill in the request data structure */ + Req_buff.bigend = htonl (0x12345678); + Req_buff.cmnd = htonl (SQHM_DBG); + Req_buff.u.dbg.mask = htonl(iLevel); + + /* try, get a connection to master server */ + status = OpenMasterConnection(self); + if(status < 0) + { + return status; + } + + /* send request */ + status = send(self->iMasterSocket,(char *)&Req_buff, sizeof(Req_buff),0); + if(status == -1) + { + return SEND_ERROR; + } + + /* get a reply */ + iRet = GetMasterReply(self,&Rply_buff,sizeof(Rply_buff)); + if(iRet < 0) + { + close(self->iMasterSocket); + self->iMasterSocket = 0; + return iRet; + } + else + { + status = close(self->iMasterSocket); + self->iMasterSocket = 0; + if((status != 0) && (errno != ECONNRESET)) + { + return CLOSE_ERROR; + } + } + return 1; /* success, finally */ + } +/*------------------------------------------------------------------------*/ + int SINQHMKill(pSINQHM self) + { + int status, iRet; + struct req_buff_struct Req_buff; + struct rply_buff_struct Rply_buff; + + assert(self); + + /* fill in the request data structure */ + Req_buff.bigend = htonl (0x12345678); + Req_buff.cmnd = htonl (SQHM_EXIT); + + /* try, get a connection to master server */ + status = OpenMasterConnection(self); + if(status < 0) + { + return status; + } + + /* send request */ + status = send(self->iMasterSocket,(char *)&Req_buff, sizeof(Req_buff),0); + if(status == -1) + { + return SEND_ERROR; + } + + /* get a reply */ + iRet = GetMasterReply(self,&Rply_buff,sizeof(Rply_buff)); + if(iRet < 0) + { + close(self->iMasterSocket); + self->iMasterSocket = 0; + return iRet; + } + else + { + status = close(self->iMasterSocket); + self->iMasterSocket = 0; + if((status != 0) && (errno != ECONNRESET)) + { + return CLOSE_ERROR; + } + } + return 1; /* success, finally */ + } + +/*====================== DAQ functions ==================================*/ + int SINQHMOpenDAQ(pSINQHM self) + { + int status, iRet, iPacket; + struct sockaddr_in lcl_sockname; + struct sockaddr_in rmt_sockname; + struct hostent *rmt_hostent; + struct in_addr *rmt_inet_addr_pntr; + struct req_buff_struct Req_buff; + struct rply_buff_struct Rply_buff; + + assert(self); + + /* fill in the request data structure */ + Req_buff.bigend = htonl (0x12345678); + Req_buff.cmnd = htonl (SQHM_CNCT); + Req_buff.u.cnct.max_pkt = htonl (8192); + Req_buff.u.cnct.strt_mode = htonl (0); + + + /* try, get a connection to master server */ + status = OpenMasterConnection(self); + if(status < 0) + { + return status; + } + + /* send request */ + status = send(self->iMasterSocket,(char *)&Req_buff, sizeof(Req_buff),0); + if(status == -1) + { + return SEND_ERROR; + } + + /* get a reply */ + iRet = GetMasterReply(self,&Rply_buff,sizeof(Rply_buff)); + status = close(self->iMasterSocket); + self->iMasterSocket = 0; + if(iRet < 0) + { + return iRet; + } + if(status != 0) + { + return CLOSE_ERROR; + } + + /* read the port and packet size to use */ + self->iClientPort = ntohl (Rply_buff.u.cnct.port); + iPacket = ntohl (Rply_buff.u.cnct.pkt_size); + self->iPacket = iPacket; + + /* now we are ready to open the connection to our very own histogram + memory slave server + */ + + /* first a socket */ + self->iClientSocket = socket (AF_INET, SOCK_STREAM, 0); + if(self->iClientSocket == -1) + { + return SOCKET_ERROR; + } + + /* now try a bind */ + lcl_sockname.sin_family = AF_INET; + lcl_sockname.sin_port = htons (0); + lcl_sockname.sin_addr.s_addr = 0; + status = bind (self->iClientSocket, (struct sockaddr *) &lcl_sockname, + sizeof (lcl_sockname)); + if (status == -1) + { + self->iClientSocket = 0; + return BIND_ERROR; + } + + /* get hostname (again). This is double work (has happened in + OpenMasterConnection before) but I decided for this in order to + avoid carrying that extra adress pointer needed for connect around. + */ + rmt_hostent = gethostbyname (self->pHMComputer); + if (rmt_hostent == NULL) { + /* this should never happen, as we got it recently in + OpenMasterConnection + */ + return HMCOMPUTER_NOT_FOUND; + } + rmt_inet_addr_pntr = (struct in_addr *) rmt_hostent->h_addr_list[0]; + + /* and connect */ + rmt_sockname.sin_family = AF_INET; + rmt_sockname.sin_port = htons (self->iClientPort); + rmt_sockname.sin_addr.s_addr = rmt_inet_addr_pntr->s_addr; + status = connect (self->iClientSocket, (struct sockaddr *) &rmt_sockname, + sizeof (rmt_sockname)); + if (status == -1) + { + self->iClientSocket = 0; + return CONNECT_ERROR; + } + + /* done! Surprise! Everything worked! */ + return 1; + } +/*------------------------------------------------------------------------*/ + int SINQHMCloseDAQ(pSINQHM self) + { + struct req_buff_struct Req_buff; + int status, iRet; + + assert(self); + if(self->iClientSocket <= 0) + { + /* already colsed */ + return 1; + } + + iRet = 1; + + /* send close message, this helps the master to clean up */ + Req_buff.bigend = htonl (0x12345678); + Req_buff.cmnd = htonl (SQHM_CLOSE); + status = send (self->iClientSocket, (char *) &Req_buff, sizeof (Req_buff), 0); + if (status == -1) + { + iRet = SEND_ERROR; + } + if (status != sizeof (Req_buff)) + { + iRet = SEND_ERROR; + } + status = close (self->iClientSocket); + if (status != 0) + { + iRet = CLOSE_ERROR; + } + self->iClientSocket = 0; + self->iClientPort = 0; + return iRet; + } +/*-----------------------------------------------------------------------*/ + int SINQHMStartDAQ(pSINQHM self) + { + int status, iDaq; + + assert(self); + + status = SendDAQCommand(self,DAQ__GO,&iDaq); + if(status < 0) /* error */ + { + return status; + } + if(iDaq != 0) + { + return DAQ_INHIBIT; + } + return 1; + } +/*-----------------------------------------------------------------------*/ + int SINQHMStopDAQ(pSINQHM self) + { + int status, iDaq; + + assert(self); + + status = SendDAQCommand(self,DAQ__STOP,&iDaq); + if(status < 0) /* error */ + { + return status; + } + if(iDaq == 0) + { + return DAQ_NOTSTOPPED; + } + return 1; + } + +/*-----------------------------------------------------------------------*/ + int SINQHMContinueDAQ(pSINQHM self) + { + int status, iDaq; + + assert(self); + + status = SendDAQCommand(self,DAQ__CLR,&iDaq); + if(status < 0) /* error */ + { + return status; + } + if(iDaq != 0) + { + return DAQ_INHIBIT; + } + return 1; + } +/*-----------------------------------------------------------------------*/ + int SINQHMInhibitDAQ(pSINQHM self) + { + int status, iDaq; + + assert(self); + + status = SendDAQCommand(self,DAQ__INH,&iDaq); + if(status < 0) /* error */ + { + return status; + } + if(iDaq == 0) + { + return DAQ_NOTSTOPPED; + } + return 1; + + } +/*-----------------------------------------------------------------------*/ + int SINQHMWrite(pSINQHM self, int iNum, int iStart, int iEnd, void *pData) + { + long lBytes2Go,lBins, i; + int status, iRet; + struct req_buff_struct Req_buff; + struct rply_buff_struct Rply_buff; + SQint16 *p16; + SQint32 *p32; + char *pPtr; + + assert(self); + + /* calculate number of bins */ + lBins = iEnd; + + /* take care of byte order first */ + if (0x12345678 != ntohl (0x12345678)) + { + /* Swap bytes, if necessary */ + switch (self->iBinWidth) + { + case 1: + break; + case 2: + p16 = (SQint16 *) pData; + for (i=0; i < lBins; i++) + { + p16[i] = htons (p16[i]); + } + break; + case 4: + p32 = (SQint32 *) pData; + for (i=0; i < lBins; i++) + { + p32[i] = htonl (p32[i]); + } + break; + } + } + + /* initialize the Request data */ + Req_buff.bigend = htonl (0x12345678); + Req_buff.cmnd = htonl (SQHM_WRITE); + Req_buff.u.write.n_bins = htonl (lBins); + Req_buff.u.write.first_bin = htonl (iStart); + Req_buff.u.write.bytes_per_bin = htonl (self->iBinWidth); + Req_buff.u.write.hist_no = htonl (iNum); + + /* send the message */ + status = send (self->iClientSocket, (char *) &Req_buff, sizeof (Req_buff), 0); + if (status == -1) + { + return SEND_ERROR; + } + if (status != sizeof (Req_buff)) + { + return SEND_ERROR; + } + + /* send data */ + lBytes2Go = lBins * self->iBinWidth; + pPtr = (char *)pData; + while (lBytes2Go > 0) + { + i = (lBytes2Go > self->iPacket) ? self->iPacket : lBytes2Go; + status = send (self->iClientSocket, (char *) pPtr, i, 0); + if (status <= 0) + { + return SEND_ERROR; + } + lBytes2Go -= status; + pPtr += status; + } + + /* get status */ + status = recv (self->iClientSocket, (char *) &Rply_buff, + sizeof (Rply_buff), MSG_WAITALL); + + /* check various error conditions */ + if (status == -1) + { + return RECEIVE_ERROR; + } + if (status != sizeof (Rply_buff)) + { + return INSUFFICIENT_DATA; + } + if(ntohl (Rply_buff.bigend) != 0x12345678) + { + return BYTE_ORDER_CHAOS; + } + if((iRet = ntohl (Rply_buff.status)) == KER__BAD_VALUE) + { + return HIST_BAD_VALUE; + } + if(iRet != KER__SUCCESS) + { + return HIST_BAD_CODE; + } + /* success */ + return 1; + } +/*-----------------------------------------------------------------------*/ + int SINQHMRead(pSINQHM self, int iNum, int iStart, int iEnd, + void *pData, int iDataLen) + { + long lBins2Get, lSpace,iNoBins, i; + int status, iRet; + struct req_buff_struct Req_buff; + struct rply_buff_struct Rply_buff; + SQint16 *p16; + SQint32 *p32; + char *pPtr; + char pBuffer[8192]; + + assert(self); + + /* initialize the Request data */ + Req_buff.bigend = htonl (0x12345678); + Req_buff.cmnd = htonl (SQHM_READ); + Req_buff.u.read.n_bins = htonl (iEnd-iStart); + Req_buff.u.read.first_bin = htonl (iStart); + Req_buff.u.read.hist_no = htonl (iNum); + + /* send the message */ + status = send (self->iClientSocket, (char *) &Req_buff, + sizeof (Req_buff), 0); + if (status == -1) + { + return SEND_ERROR; + } + if (status != sizeof (Req_buff)) + { + return SEND_ERROR; + } + + /* wait for an answer */ + status = recv (self->iClientSocket, (char *) &Rply_buff, + sizeof (Rply_buff), MSG_WAITALL); + + /* check various error conditions */ + if (status == -1) + { + return RECEIVE_ERROR; + } + if (status != sizeof (Rply_buff)) + { + return INSUFFICIENT_DATA; + } + if(ntohl (Rply_buff.bigend) != 0x12345678) + { + return BYTE_ORDER_CHAOS; + } + iRet = ntohl(Rply_buff.status); + if(iRet != KER__SUCCESS) + { + return HIST_BAD_CODE; + } + + /* calculate the size of things to come */ + lBins2Get = ntohl(Rply_buff.u.read.n_bins) * ntohl(Rply_buff.u.read.bytes_per_bin); + + /* read data */ + pPtr = (char *)pData; + lSpace = iDataLen; + iNoBins = ntohl(Rply_buff.u.read.n_bins); + while (lBins2Get > 0) + { + if(lBins2Get > self->iPacket) + { + i = self->iPacket; + } + else + { + i = lBins2Get; + } + status = recv (self->iClientSocket, pBuffer, + i, 0); + if (status == -1) + { + return SEND_ERROR; + } + lBins2Get -= status; + if((lSpace - status) > 0) + { + memcpy(pPtr,pBuffer,status); + lSpace -= status; + pPtr += status; + } + else + { + if(lSpace > 0) + { + memcpy(pPtr,pBuffer,lSpace); + lSpace = 0; + } + } + } + + /* swap bytes if necessary */ + if ((self->iBinWidth > 0) && (Rply_buff.bigend != 0x12345678)) + { + switch (self->iBinWidth) + { /* Byte swapping is necessary */ + case 2: + /* Not sure how to do this - this might be wrong! */ + p16 = (SQint16 *) pData; + for (i = 0; i < iNoBins; i++) + { + p16[i] = ntohs (p16[i]); + } + break; + case 4: + p32 = (SQint32 *) pData; + for (i = 0; i < iNoBins; i++) + { + p32[i] = ntohl(p32[i]); + } + break; + } + } + /* done */ + return 1; + } +/*-----------------------------------------------------------------------*/ + int SINQHMProject(pSINQHM self, int code, int xStart, int nx, + int yStart, int ny, + void *pData, int iDataLen) + { + long lBins2Get, lSpace,iNoBins, i; + int status, iRet; + struct req_buff_struct Req_buff; + struct rply_buff_struct Rply_buff; + SQint16 *p16; + SQint32 *p32; + char *pPtr; + char pBuffer[8192]; + + assert(self); + + /* initialize the Request data */ + Req_buff.bigend = htonl (0x12345678); + Req_buff.cmnd = htonl (SQHM_PROJECT); + Req_buff.u.project.sub_code = htonl (code); + + Req_buff.u.project.x_lo = htonl (xStart); + Req_buff.u.project.nx = htonl (nx); + Req_buff.u.project.y_lo = htonl (yStart); + Req_buff.u.project.ny = htonl (ny); + Req_buff.u.project.nhist = htonl (1); + + /* send the message */ + status = send (self->iClientSocket, (char *) &Req_buff, + sizeof (Req_buff), 0); + if (status == -1) + { + return SEND_ERROR; + } + if (status != sizeof (Req_buff)) + { + return SEND_ERROR; + } + + /* wait for an answer */ + status = recv (self->iClientSocket, (char *) &Rply_buff, + sizeof (Rply_buff), MSG_WAITALL); + + /* check various error conditions */ + if (status == -1) + { + return RECEIVE_ERROR; + } + if (status != sizeof (Rply_buff)) + { + return INSUFFICIENT_DATA; + } + if(ntohl (Rply_buff.bigend) != 0x12345678) + { + return BYTE_ORDER_CHAOS; + } + iRet = ntohl(Rply_buff.status); + if(iRet != KER__SUCCESS) + { + return HIST_BAD_CODE; + } + + /* calculate the size of things to come */ + lBins2Get = ntohl(Rply_buff.u.project.n_bins) * + ntohl(Rply_buff.u.project.bytes_per_bin); + + /* read data */ + pPtr = (char *)pData; + lSpace = iDataLen; + iNoBins = ntohl(Rply_buff.u.project.n_bins); + while (lBins2Get > 0) + { + if(lBins2Get > self->iPacket) + { + i = self->iPacket; + } + else + { + i = lBins2Get; + } + status = recv (self->iClientSocket, pBuffer, + i, 0); + if (status == -1) + { + return SEND_ERROR; + } + lBins2Get -= status; + if((lSpace - status) > 0) + { + memcpy(pPtr,pBuffer,status); + lSpace -= status; + pPtr += status; + } + else + { + if(lSpace > 0) + { + memcpy(pPtr,pBuffer,lSpace); + lSpace = 0; + } + } + } + + /* swap bytes if necessary */ + iNoBins = iDataLen/self->iBinWidth; + if ((self->iBinWidth > 0) && (Rply_buff.bigend != 0x12345678)) + { + switch (self->iBinWidth) + { /* Byte swapping is necessary */ + case 2: + /* Not sure how to do this - this might be wrong! */ + p16 = (SQint16 *) pData; + for (i = 0; i < iNoBins; i++) + { + p16[i] = ntohs (p16[i]); + } + break; + case 4: + p32 = (SQint32 *) pData; + for (i = 0; i < iNoBins; i++) + { + p32[i] = ntohl(p32[i]); + } + break; + } + } + /* done */ + return 1; + } +/*------------------------------------------------------------------------ + This is the old version, using a master socjet, delete if the other version + with client socket works alright. +*/ + int SINQHMZero2(pSINQHM self, int iNumber, int iStart, int iEnd) + { + int status, iRet; + struct req_buff_struct Req_buff; + struct rply_buff_struct Rply_buff; + + assert(self); + + /* fill in the request data structure */ + Req_buff.bigend = htonl (0x12345678); + Req_buff.cmnd = htonl (SQHM_ZERO); + Req_buff.u.zero.hist_no = htonl (iNumber); + Req_buff.u.zero.first_bin = htonl (iStart); + Req_buff.u.zero.n_bins = htonl (iEnd); + + /* try, get a connection to master server */ + status = OpenMasterConnection(self); + if(status < 0) + { + return status; + } + + /* send request */ + status = send(self->iMasterSocket,(char *)&Req_buff, sizeof(Req_buff),0); + if(status == -1) + { + return SEND_ERROR; + } + + /* get a reply */ + iRet = GetMasterReply(self,&Rply_buff,sizeof(Rply_buff)); + if(iRet < 0) + { + close(self->iMasterSocket); + self->iMasterSocket = 0; + return iRet; + } + else + { + status = close(self->iMasterSocket); + self->iMasterSocket = 0; + if((status != 0) && (errno != ECONNRESET)) + { + return CLOSE_ERROR; + } + } + return 1; /* success, finally */ + } +/*-----------------------------------------------------------------------*/ + int SINQHMZero(pSINQHM self, int iNumber, int iStart, int iEnd) + { + int status, iRet; + struct req_buff_struct Req_buff; + struct rply_buff_struct Rply_buff; + + assert(self); + + /* fill in the request data structure */ + Req_buff.bigend = htonl (0x12345678); + Req_buff.cmnd = htonl (SQHM_ZERO); + Req_buff.u.zero.hist_no = htonl (iNumber); + Req_buff.u.zero.first_bin = htonl (iStart); + Req_buff.u.zero.n_bins = htonl (iEnd); + + /* send request */ + status = send(self->iClientSocket,(char *)&Req_buff, sizeof(Req_buff),0); + if(status == -1) + { + return SEND_ERROR; + } + if(status != sizeof(Req_buff)) + { + return SEND_ERROR; + } + + /* get a reply */ + iRet = recv(self->iClientSocket,(char *)&Rply_buff,sizeof(Rply_buff), + MSG_WAITALL); + if(iRet < 0) + { + return RECEIVE_ERROR; + } + if(iRet != sizeof(Rply_buff)) + { + return INSUFFICIENT_DATA; + } + if(ntohl (Rply_buff.bigend) != 0x12345678) + { + return BYTE_ORDER_CHAOS; + } + iRet = ntohl(Rply_buff.status); + if(iRet != KER__SUCCESS) + { + return HIST_BAD_CODE; + } + return 1; /* success, finally */ + } + +/*------------------------------------------------------------------------*/ + static int OpenMasterConnection(pSINQHM self) + { + struct hostent *rmt_hostent; + struct sockaddr_in lcl_sockname; + int rmt_sockname_len; + struct sockaddr_in rmt_sockname; + struct in_addr *rmt_inet_addr_pntr; + int status; + + + /* get hostname */ + rmt_hostent = gethostbyname (self->pHMComputer); + if (rmt_hostent == NULL) { + return HMCOMPUTER_NOT_FOUND; + } + rmt_inet_addr_pntr = (struct in_addr *) rmt_hostent->h_addr_list[0]; + + + /* try, open socket */ + self->iMasterSocket = socket (AF_INET, SOCK_STREAM, 0); + if (self->iMasterSocket == -1) + { + return SOCKET_ERROR; + } + + /* bind it */ + lcl_sockname.sin_family = AF_INET; + lcl_sockname.sin_port = htons(0); + lcl_sockname.sin_addr.s_addr = 0; + status = bind (self->iMasterSocket, (struct sockaddr *) &lcl_sockname, + sizeof (lcl_sockname)); + if (status == -1) + { + return BIND_ERROR; + } + + /* try to connect */ + rmt_sockname_len = sizeof (rmt_sockname); + rmt_sockname.sin_family = AF_INET; + rmt_sockname.sin_port = htons (self->iMasterPort); + rmt_sockname.sin_addr.s_addr = rmt_inet_addr_pntr->s_addr; + status = connect (self->iMasterSocket, (struct sockaddr *) &rmt_sockname, + sizeof (rmt_sockname)); + if (status == -1) { + return CONNECT_ERROR; + } + + /* Success */ + return 1; + } +/*------------------------------------------------------------------------*/ + static int GetMasterReply(pSINQHM self, struct rply_buff_struct *reply, + int iBufLen) + { + + int status; + + assert(self->iMasterSocket); + + /* get reply structure */ + status = recv (self->iMasterSocket, (char *) reply, + iBufLen, MSG_WAITALL); + if (status == -1) { + return RECEIVE_ERROR; + } else if (status != iBufLen) { + return INSUFFICIENT_DATA; + } + + /* check endedness */ + if (ntohl (reply->bigend) != 0x12345678) { + return BYTE_ORDER_CHAOS; + } + + /* check histogram memory status codes */ + status = ntohl (reply->status); + if (status == KER__SUCCESS) { + return 1; + }else if (status == KER__BAD_CREATE) { + return HIST_BAD_CREATE; + }else if (status == KER__BAD_STATE) { + return HIST_BAD_STATE; + }else if (status == KER__BAD_VALUE) { + return HIST_BAD_VALUE; + }else if (status == KER__BAD_RECV) { + return HIST_BAD_RECV; + }else if (status == KER__BAD_ALLOC) { + return HIST_BAD_ALLOC; + }else { + return HIST_BAD_CODE; + } + /* not reached, usually */ + return HIST_BAD_CODE; + } +/*-------------------------------------------------------------------------*/ + int SINQHMError2Text(int iErr, char *pBuffer, int iBufLen) + { + /* the trivial case */ + if(iErr > 0) + { + strncpy(pBuffer,"No error ocurred",iBufLen); + return 0; + } + + switch(iErr) + { + case HMCOMPUTER_NOT_FOUND: + strncpy(pBuffer, + "No name server entry for histogram memory computer", + iBufLen); + break; + case SOCKET_ERROR: + strncpy(pBuffer, + "Insufficient system resources for socket creation", + iBufLen); + break; + case BIND_ERROR: + strncpy(pBuffer, + "Cannot bind", + iBufLen); + break; + case CONNECT_ERROR: + strncpy(pBuffer, + "Cannot connect, probably port number wrong", + iBufLen); + break; + case RECEIVE_ERROR: + strncpy(pBuffer, + "Error receiving data", iBufLen); + break; + case INSUFFICIENT_DATA: + strncpy(pBuffer, + "Not enough bytes received from host, network trouble", + iBufLen); + break; + case BYTE_ORDER_CHAOS: + strncpy(pBuffer, + "Reply not in network byte order", + iBufLen); + break; + case HIST_BAD_CREATE: + strncpy(pBuffer, + "Master histogram server failed to spawn child", + iBufLen); + break; + case HIST_BAD_VALUE: + strncpy(pBuffer, + "Invalid parameter detected", + iBufLen); + break; + case HIST_BAD_STATE: + strncpy(pBuffer, + "Histogram memory NOT configured", + iBufLen); + break; + case HIST_BAD_RECV: + strncpy(pBuffer, + "Histogram server failed to read command", + iBufLen); + break; + case HIST_BAD_ALLOC: + strncpy(pBuffer, + "Histogram memory out of memory!", + iBufLen); + break; + case HIST_BAD_CODE: + strncpy(pBuffer, + "Unknown or corrupted status code sent from master server", + iBufLen); + break; + case SEND_ERROR: + strncpy(pBuffer, + "Error sending data", + iBufLen); + break; + case CLOSE_ERROR: + strncpy(pBuffer, + "Error closing connection", + iBufLen); + break; + case INVALID_HARSH: + strncpy(pBuffer, + "Invalid parameter for harshness", + iBufLen); + break; + case SOFTWARE_ERROR: + strncpy(pBuffer, + "Internal error or software error at histogram memory computer, consult a hacker", + iBufLen); + break; + case DAQ_INHIBIT: + strncpy(pBuffer, + "Data aquisition inhibited by some client", + iBufLen); + break; + case DAQ_NOTSTOPPED: + strncpy(pBuffer, + "Data aquisition not stopped, suggests SW or network problem", + iBufLen); + break; + + default: + strncpy(pBuffer, + "Unknown error code",iBufLen); + } + return 1; + } +/*------------------------------------------------------------------------- + SINQHM needs an additional top bin defining the upper edge of the + histogram. So, for a 512 bin array, 513 bins are needed. The additional + bin is created in the code below. This explains the strange arithmetic with + EdgeLength and the code at the end of the for loop +*/ + int SINQHMDefineBank(pSINQHM self, int iBankNumber, int iStart, int iEnd, + float *iEdges, int iEdgeLength) + { + pSBank pWork = NULL; + int i, iDelay, iDiff; + + assert(self); + assert(iBankNumber >= 0); + assert(iBankNumber < MAXBANK); + assert(iEdgeLength >= 1); + assert(iStart >= 0); + assert(iEnd >= iStart); + + if(iBankNumber >= self->iBanks) + { + self->iBanks = iBankNumber +1; + } + pWork = &(self->pBank[iBankNumber]); + if(pWork->iEdges != NULL) + { + free(pWork->iEdges); + pWork->iEdges = NULL; + } + iDelay = (int)iEdges[0]; + pWork->iStart = iStart; + pWork->iEnd = iEnd; + pWork->iEdgeLength = iEdgeLength; + if(iEdgeLength == 2) + { /* + fixed binwidth: two values required: start stop in + edge[0], edge[1] + */ + pWork->iFlag = 0; + pWork->iDelay = iDelay; + pWork->iEdges = (unsigned int *)malloc(2*sizeof(unsigned int)); + if(!pWork->iEdges) + { + return HIST_BAD_ALLOC; + } + pWork->iEdges[0] = htonl((unsigned int)iEdges[0]); + pWork->iEdges[1] = htonl((unsigned int)(iEdges[1] - iDelay)); + return 1; + } + + /* + normal case: create the bin boundaries + */ + pWork->iFlag = 1; + pWork->iEdgeLength++; + iEdgeLength++; + pWork->iEdges = (unsigned int *)malloc(iEdgeLength * + sizeof(unsigned int)); + if(!pWork->iEdges) + { + return HIST_BAD_ALLOC; + } + pWork->iDelay = iDelay; + for(i = 0; i < iEdgeLength-1; i++) + { + pWork->iEdges[i] = htonl((unsigned int)(iEdges[i]-iDelay)); + } + iDiff = iEdges[1] - iEdges[0]; + pWork->iEdges[iEdgeLength-1] = htonl(iEdges[iEdgeLength-2] + + iDiff - iDelay); + return 1; + } +/*-----------------------------------------------------------------------*/ + struct tof { + uint n_extra_bytes; + usint n_edges; + usint n_banks; + uint preset_delay; + struct tof_edge_arr edge_0; + struct tof_bank bank_0; + }; + +/*------------------------------------------------------------------------*/ + int SINQHMTimeBin(pSINQHM self,int iMode) + { + int status, iRet; + struct req_buff_struct Req_buff; + struct rply_buff_struct Rply_buff; + int iLength, i, iDelay; + unsigned int iExtra; + char *pBuffer = NULL, *pPtr; + struct tof_edge_arr tea; + int iTeaLength; + struct tof_bank toba; + struct tof tofi; + + assert(self); + + /* figure out how long we are going to be*/ + iLength = 24 + self->iBanks*sizeof(struct tof_bank); + for(i = 0; i < self->iBanks; i++) + { + iLength += 8 + self->pBank[i].iEdgeLength*sizeof(SQint32); + } + if(iLength < 64) + iLength = 64; + /* allocate send buffer */ + pBuffer = (char *)malloc(iLength*sizeof(char)); + if(!pBuffer) + { + return HIST_BAD_ALLOC; + } + memset(pBuffer,0,iLength); + + /* do the message header */ + iExtra = iLength - 64; + if(iExtra < 0) + iExtra = 0; + iDelay = self->pBank[0].iEdges[0]; + Req_buff.bigend = htonl (0x12345678); + Req_buff.cmnd = htonl (SQHM_CONFIG); + Req_buff.u.cnfg.mode = htonl (iMode); + memcpy(pBuffer,&Req_buff,12); + pPtr = pBuffer + 12; + + tofi.n_extra_bytes = htonl(iExtra); + tofi.n_edges = htons(self->iBanks); + tofi.n_banks = htons(self->iBanks); + tofi.preset_delay = htonl(self->pBank[0].iDelay); + memcpy(pPtr,&tofi,12); + pPtr += 12; + + /* do the edge thingies */ + for(i = 0; i < self->iBanks; i++) + { + tea.n_bins = htonl(self->pBank[i].iEdgeLength-1); + if(self->pBank[i].iEdgeLength == 2) + { + tea.flag = htonl(0); + } + else + { + tea.flag = htonl(1); + } + tea.edges = self->pBank[i].iEdges; + memcpy(pPtr,&tea,8); + pPtr += 8; + iTeaLength = self->pBank[i].iEdgeLength*4; + memcpy(pPtr,self->pBank[i].iEdges,iTeaLength); + pPtr += iTeaLength; + } + + /* do the swiss bank structures */ + for(i = 0; i < self->iBanks; i++) + { + toba.first = htons(self->pBank[i].iStart); + toba.n_cntrs = htons(self->pBank[i].iEnd); + toba.edge_indx = htons(i); + toba.bytes_per_bin = htons(self->iBinWidth); + memcpy(pPtr,&toba,sizeof(struct tof_bank)); + pPtr += sizeof(struct tof_bank); + } + + /* all packed up neat and nicely, send it */ + /* try, get a connection to master server */ + status = OpenMasterConnection(self); + if(status < 0) + { + if(pBuffer) + free(pBuffer); + return status; + } + + /* send request */ + status = send(self->iMasterSocket,pBuffer,iLength ,0); + if(pBuffer) + { + free(pBuffer); + } + if(status == -1) + { + return SEND_ERROR; + } + + /* get a reply */ + iRet = GetMasterReply(self,&Rply_buff,sizeof(Rply_buff)); + if(iRet < 0) + { + /* try close the socket */ + close(self->iMasterSocket); + self->iMasterSocket = 0; + return iRet; + } + else + { + /* close the socket */ + status = close(self->iMasterSocket); + self->iMasterSocket = 0; + if((status != 0) && (errno != ECONNRESET)) + { + return CLOSE_ERROR; + } + } + return 1; /* success, finally */ + } diff --git a/hardsup/sinqhm.h b/hardsup/sinqhm.h new file mode 100644 index 0000000..ad692a1 --- /dev/null +++ b/hardsup/sinqhm.h @@ -0,0 +1,107 @@ + +#line 363 "sinqhm.w" + +/*--------------------------------------------------------------------------- + S I N Q H M + Some utility functions for interfacing to the SINQ histogram memory + server. + + David Maden, Mark Koennecke, April 1997 + + copyright: see implementation file. +-----------------------------------------------------------------------------*/ +#ifndef SINQHMUTILITY +#define SINQHMUTILITY +#include "sinqhm_def.h" + + typedef struct __SINQHM *pSINQHM; +/*------------------------------ Error codes -----------------------------*/ + +#line 341 "sinqhm.w" + +#define HMCOMPUTER_NOT_FOUND -2 +#define SOCKET_ERROR -3 +#define BIND_ERROR -4 +#define CONNECT_ERROR -5 +#define RECEIVE_ERROR -6 +#define INSUFFICIENT_DATA -7 +#define BYTE_ORDER_CHAOS -8 +#define HIST_BAD_CREATE -9 +#define HIST_BAD_STATE -10 +#define HIST_BAD_VALUE -11 +#define HIST_BAD_RECV -12 +#define HIST_BAD_ALLOC -13 +#define HIST_BAD_CODE -14 +#define SEND_ERROR -15 +#define CLOSE_ERROR -16 +#define INVALID_HARSH -17 +#define SOFTWARE_ERROR -18 +#define DAQ_INHIBIT -19 +#define DAQ_NOTSTOPPED -20 + +#line 379 "sinqhm.w" + + +/*------------------------------ Prototypes ------------------------------*/ + +#line 118 "sinqhm.w" + + pSINQHM CreateSINQHM(char *pHMComputer, int iMasterPort); + pSINQHM CopySINQHM(pSINQHM self); + void DeleteSINQHM(pSINQHM self); + void SINQHMSetPar(pSINQHM self, int iRank, int iLength, int iBinWidth); + void SINQHMSetPSD(pSINQHM self, int xSize, int xOff, int xFac, + int ySize, int yOff, int yFac); + +#line 142 "sinqhm.w" + + int SINQHMError2Text(int iErr, char *pBuffer, int iBufLen); + +#line 155 "sinqhm.w" + + int SINQHMConfigure(pSINQHM self, int iMode, int iRank, int iLength, + int iBinWidth, int iLowBin, int iCompress); + int SINQHMConfigurePSD(pSINQHM self, int iMode, + int xSize, int xOff, int xFac, + int ySize, int yOff, int yFac, + int iBinWidth, + float *iEdges, int iEdgeLength); + + int SINQHMDeconfigure(pSINQHM self, int iHarsh); + int SINQHMGetStatus(pSINQHM self,int *iMode, int *iDaq, + int *iRank, int *iBinWidth, + int *iLength, int *iClients); + int SINQHMDebug(pSINQHM self, int iLevel); + int SINQHMKill(pSINQHM self); + + +#line 261 "sinqhm.w" + + int SINQHMOpenDAQ(pSINQHM self); + int SINQHMCloseDAQ(pSINQHM self); + + int SINQHMStartDAQ(pSINQHM self); + int SINQHMStopDAQ(pSINQHM self); + int SINQHMInhibitDAQ(pSINQHM self); + int SINQHMContinueDAQ(pSINQHM self); + + int SINQHMWrite(pSINQHM self, int iNum, int iStart, int iEnd, + void *pData); + long SINQHMSize(pSINQHM self, int iNum, int iStart, int iEnd); + int SINQHMRead(pSINQHM self, int iNum, int iStart, int iEnd, + void *pData, int iDataLen); + int SINQHMProject(pSINQHM self, int code, int xStart, int nx, + int yStart, int ny, void *pData, int iDataLen); + int SINQHMZero(pSINQHM self, int iNum, int iStart, int iEnd); + +#line 382 "sinqhm.w" + + +#line 232 "sinqhm.w" + + int SINQHMDefineBank(pSINQHM self, int iBankNumber, int iStart, int iEnd, + float *iEdges, int iEdgeLength); + +#line 383 "sinqhm.w" + +#endif diff --git a/hardsup/sinqhm_def.h b/hardsup/sinqhm_def.h new file mode 100644 index 0000000..6b17b2e --- /dev/null +++ b/hardsup/sinqhm_def.h @@ -0,0 +1,483 @@ +/*=================================================== [...SinqHM]SinqHM_def.h +** +** Definition Include file for SinqHM_SRV and its clients. +** +**------------------------------------------------------------------------------ +*/ +#define SINQHM_DEF_ID "V03C" + +#ifdef __alpha +#ifndef __vms +#pragma pack 1 +#endif +#endif +/*------------------------------------------------------------------------------ +*/ +#ifndef OffsetOf +#define OffsetOf(type, identifier) ((size_t)(&((type*) NULL)->identifier)) +#endif +/*------------------------------------------------------------------------------ +** Define some defaults. +*/ +#define PORT_BASE 2400 /* The Internet Port for Server Requests */ +#define MAX_CLIENTS 8 /* The maximum number of active clients */ +#define MAX_TOF_CNTR 1024 /* The maximum number of individual counters .. + ** which can be handled in TOF mode */ +#define MAX_PSD_CNTR 1048576 /* maximum number of PSD elements */ +#define MAX_TOF_NBINS 32768 /* The maximum number of bins in a TOF histog */ +#define MAX_TOF_EDGE 16 /* The maximum number of TOF edge arrays */ +#define VMIO_BASE_ADDR 0x1900 /* VME address of a (possible) VMIO10 module */ +#define IDENT_MSGE_LEN 256 /* Length of Ident info for SQHM_IDENT */ + +#define uchar unsigned char +#define usint unsigned short int +#define uint unsigned int +/*------------------------------------------------------------------------------ +** Define some status values (similar to VAXeln). +*/ +#define KER__SUCCESS 1 +#define KER__BAD_CREATE -2 +#define KER__BAD_STATE -4 +#define KER__BAD_VALUE -6 +#define KER__EXIT_SIGNAL -10 +#define KER__BAD_RECV -14 +#define KER__BAD_ALLOC -16 + +#ifndef True +#define True 1 +#endif + +#ifndef False +#define False 0 +#endif + +#ifndef NIL +#define NIL '\0' +#endif +/*------------------------------------------------------------------------------ +** Define command verbs to SinqHM. +*/ +#define SQHM_CNCT 0x01 +#define SQHM_CLOSE 0x02 +#define SQHM_CONFIG 0x03 +#define SQHM_DAQ 0x04 +#define SQHM_DBG 0x05 +#define SQHM_DECONFIG 0x06 +#define SQHM_EXIT 0x07 +#define SQHM_IDENT 0x0e +#define SQHM_PROJECT 0x0d +#define SQHM_READ 0x08 +#define SQHM_SELECT 0x09 +#define SQHM_STATUS 0x0a +#define SQHM_WRITE 0x0b +#define SQHM_ZERO 0x0c + /* + ** Define the various operation modes + */ +#define SQHM__TRANS 0x1000 /* Transparent mode */ +#define SQHM__HM_DIG 0x2000 /* Hist mode (with digitised read-out) */ +#define SQHM__TOF 0x3000 /* Time-of-Flight mode */ +#define SQHM__HM_PSD 0x4000 /* Hist mode (with Pos-sens-detect read-out) */ +#define SQHM__HRPT 0x5000 /* Hist mode for HRPT */ + /* + ** Define the various sub-mode bits of the operation modes + */ +#define SQHM__SUB_MODE_MSK 0xff /* Mask for extracting "sub-mode" bits */ +#define SQHM__DEBUG 0x01 /* Debug flag - FILLER will suspend itself .. + ** .. after starting to allow debugging */ +#define SQHM__UD 0x02 /* Use Up/Down bit information */ + +#define SQHM__BO_MSK 0x18 /* Mask for extracting "bin-overflow" bits */ +#define SQHM__BO_IGN 0x00 /* Ignore bin-overflows (bin-contents wrap) */ +#define SQHM__BO_SMAX 0x08 /* On bin-overflow, stop at maximum */ +#define SQHM__BO_CNT 0x10 /* Keep counts of overflow bins */ + +#define SQHM__STROBO 0x20 /* Use strobo-bit information */ +#define SQHM__REFLECT 0x40 /* Reflect histograms */ +#define SQHM__NO_STAT 0x80 /* Suppress status info from "Filler" */ + /* + ** ---------------------------------------------------------- + ** SQHM_DAQ sub-function codes + */ +#define DAQ__EXIT 0xffffffff +#define DAQ__CLR 0x01 +#define DAQ__GO 0x02 +#define DAQ__INH 0x03 +#define DAQ__STOP 0x04 +#define DAQ__TST 0x05 + /* + ** ---------------------------------------------------------- + ** SQHM_PROJECT sub-codes + */ +#define PROJECT__ON_Y 0x0001 /* Project onto y-axis */ +#define PROJECT__1_DIM 0x0002 /* Make projection of a 1-dim histogram */ +#define PROJECT__COLL 0x0003 /* collapse PSD on one time channel */ +#define PROJECT__SAMPLE 0x0004 /* sum a rectangular part of the PSD + detector in time + */ + /* + ** ---------------------------------------------------------- + ** Definition of bits in of TOF edge-array + */ +#define FLAG__VAR_BIN 0x01 /* Bin span of histogram is variable */ + /* + ** ---------------------------------------------------------- + ** Definition of bits in of SQHM_STATUS response + */ +#define STATUS_FLAGS__PF 0x8000 /* PF - Power Fail */ +#define STATUS_FLAGS__SWC 0x4000 /* SWC - Status Word Changed */ +#define STATUS_FLAGS__NRL 0x2000 /* NRL - Neutron Rate Low */ +#define STATUS_FLAGS__DAQ 0x1000 /* DAQ on -- set if Hdr Mask Bits are + ** correct so that data acq is active */ +#define STATUS_FLAGS__SYNC3 0x0800 /* Ext Synch Bit #3 */ +#define STATUS_FLAGS__SYNC2 0x0400 /* Ext Synch Bit #2 */ +#define STATUS_FLAGS__SYNC1 0x0200 /* Ext Synch Bit #1 */ +#define STATUS_FLAGS__SYNC0 0x0100 /* Ext Synch Bit #0 */ +#define STATUS_FLAGS__UD 0x0080 /* UD - Up/Down */ +#define STATUS_FLAGS__GU 0x0040 /* GU - Gummi (i.e. Strobo) */ + /* + ** ---------------------------------------------------------- + */ +#define N_HISTS_MAX 64 /* Maximum number of histograms supported */ +#define N_BINS_MAX 0x00ffff /* Maximum histogram bin number permitted */ +#define N_TOTAL_BYTES 0x400000 /* Maximum total bytes of histogram */ +/* +**------------------------------------------------------------------------------ +** Definitions of Filler states in HRPT mode +*/ +#define HRPT__SRCH_FRAME 1 +#define HRPT__READ_FRAME 2 +/* +**------------------------------------------------------------------------------ +** Definitions for the LWL Datagrams +*/ +#define LWL_HDR_TYPE_MASK (0x1f000000) /* Mask for extracting main dgrm .. + ** .. hdr command-type bits */ +#define LWL_HDR_PF_MASK (0x80000000) /* Mask for extr Power Fail bit */ +#define LWL_HDR_SWC_MASK (0x40000000) /* Mask for extr Status Word Chng bit */ +#define LWL_HDR_NRL_MASK (0x20000000) /* Mask for extr Neutron Rate Low bit */ +#define LWL_HDR_SYNC3_MASK (0x00800000) /* Mask for one of ext synch bits */ +#define LWL_HDR_SYNC2_MASK (0x00400000) /* Mask for one of ext synch bits */ +#define LWL_HDR_SYNC1_MASK (0x00200000) /* Mask for one of ext synch bits */ +#define LWL_HDR_SYNC0_MASK (0x00100000) /* Mask for one of ext synch bits */ +#define LWL_HDR_UD_MASK LWL_HDR_SYNC1_MASK /* Mask for Up/Down bit */ +#define LWL_HDR_GU_MASK LWL_HDR_SYNC0_MASK /* Mask for GU bit */ +#define LWL_HDR_BA_MASK (0x00f00000) /* Mask for TSI Binning Addr */ +#define LWL_HDR_TS_MASK (0x000fffff) /* Mask for TSI Time Stamp */ + +#define LWL_FIFO_EMPTY (0x1e000000) /* FIFO Empty */ + +#define LWL_TSI_TR (0x1f000000) /* Time-Status-Info Transp-Mode */ +#define LWL_TSI_HM_NC (0x1f000000) /* Time-Status-Info Hist-Mode+No-Coinc */ +#define LWL_TSI_HM_C (0x0e000000) /* Time-Status-Info Hist-Mode+Coinc */ +#define LWL_TSI_TOF (0x1f000000) /* Time-Status-Info TOF-Mode */ +#define LWL_TSI_SM_NC (0x1f000000) /* Time-Status-Info Strobo-Mode+No-Coin */ +#define LWL_TSI_SM_C (0x0e000000) /* Time-Status-Info Strobo-Mode+Coinc */ +#define LWL_TSI_DT_MSK (0x000fffff) /* Mask for Dead-Time in TSI */ +#define LWL_TSI_DTS_MSK (0x000fffff) /* Mask for Delay-Time-to-Start in TSI */ + +#define LWL_TR_C1 (0x00000001) /* Transp. Mode Chan 1 */ +#define LWL_TR_C2 (0x00000002) /* Transp. Mode Chan 2 */ +#define LWL_TR_C3 (0x00000003) /* Transp. Mode Chan 3 */ +#define LWL_TR_C4 (0x00000004) /* Transp. Mode Chan 4 */ +#define LWL_TR_C5 (0x00000005) /* Transp. Mode Chan 5 */ +#define LWL_TR_C6 (0x00000006) /* Transp. Mode Chan 6 */ +#define LWL_TR_C7 (0x00000007) /* Transp. Mode Chan 7 */ +#define LWL_TR_C8 (0x00000008) /* Transp. Mode Chan 8 */ +#define LWL_TR_C9 (0x00000009) /* Transp. Mode Chan 9 */ + +#define LWL_HM_NC (0x10000000) /* Hist-Mode/No-Coinc 0 chan dgrm hdr */ +#define LWL_HM_NC_C1 (0x11000000) /* Hist-Mode/No-Coinc 1 chan dgrm hdr */ +#define LWL_HM_NC_C2 (0x12000000) /* Hist-Mode/No-Coinc 2 chan dgrm hdr */ +#define LWL_HM_NC_C3 (0x13000000) /* Hist-Mode/No-Coinc 3 chan dgrm hdr */ +#define LWL_HM_NC_C4 (0x14000000) /* Hist-Mode/No-Coinc 4 chan dgrm hdr */ +#define LWL_HM_NC_C5 (0x15000000) /* Hist-Mode/No-Coinc 5 chan dgrm hdr */ +#define LWL_HM_NC_C6 (0x16000000) /* Hist-Mode/No-Coinc 6 chan dgrm hdr */ +#define LWL_HM_NC_C7 (0x17000000) /* Hist-Mode/No-Coinc 7 chan dgrm hdr */ +#define LWL_HM_NC_C8 (0x18000000) /* Hist-Mode/No-Coinc 8 chan dgrm hdr */ +#define LWL_HM_NC_C9 (0x19000000) /* Hist-Mode/No-Coinc 9 chan dgrm hdr */ + +#define LWL_HM_CO (0x10000000) /* Hist-Mode+Coinc 0 chan dgrm hdr */ +#define LWL_HM_CO_C2 (0x12000000) /* Hist-Mode+Coinc 2 chan dgrm hdr */ +#define LWL_HM_CO_C3 (0x13000000) /* Hist-Mode+Coinc 3 chan dgrm hdr */ +#define LWL_HM_CO_C4 (0x14000000) /* Hist-Mode+Coinc 4 chan dgrm hdr */ +#define LWL_HM_CO_C5 (0x15000000) /* Hist-Mode+Coinc 5 chan dgrm hdr */ +#define LWL_HM_CO_C6 (0x16000000) /* Hist-Mode+Coinc 6 chan dgrm hdr */ +#define LWL_HM_CO_C7 (0x17000000) /* Hist-Mode+Coinc 7 chan dgrm hdr */ +#define LWL_HM_CO_C8 (0x18000000) /* Hist-Mode+Coinc 8 chan dgrm hdr */ +#define LWL_HM_CO_C9 (0x19000000) /* Hist-Mode+Coinc 9 chan dgrm hdr */ + +#define LWL_TOF_C1 (0x01000000) /* TOF-Mode 1 chan dgrm hdr */ +#define LWL_TOF_C2 (0x02000000) /* TOF-Mode 2 chan dgrm hdr */ +#define LWL_TOF_C3 (0x03000000) /* TOF-Mode 3 chan dgrm hdr */ +#define LWL_TOF_C4 (0x04000000) /* TOF-Mode 4 chan dgrm hdr */ +#define LWL_TOF_C5 (0x05000000) /* TOF-Mode 5 chan dgrm hdr */ +#define LWL_TOF_C6 (0x06000000) /* TOF-Mode 6 chan dgrm hdr */ +#define LWL_TOF_C7 (0x07000000) /* TOF-Mode 7 chan dgrm hdr */ +#define LWL_TOF_C8 (0x08000000) /* TOF-Mode 8 chan dgrm hdr */ +#define LWL_TOF_C9 (0x09000000) /* TOF-Mode 9 chan dgrm hdr */ + +#define LWL_PSD_TSI 0x0E000000 /* PSD-Mode TSI datagram */ +#define LWL_PSD_DATA 0x12000000 /* PSD-mode data datagram */ +#define LWL_PSD_PWF 0x20000000 /* PSD-mode Power Fail bit */ +#define LWL_PSD_TIME 0x000fffff /* PSD-mode time stamp extraction + mask */ +#define LWL_PSD_FLASH_MASK 0x00ff /* mask for flash count */ +#define LWL_PSD_XORF 0x2000 /* mask for TDC-XORF bit */ +#define LWL_PSD_CONF 0x0100 /* mask for TDC-CONF flag */ + +#define LWL_SM_NC (0x10000000) /* Strobo-Mode/No-Coinc 0 chan dgrm hdr */ +#define LWL_SM_NC_C1 (0x11000000) /* Strobo-Mode/No-Coinc 1 chan dgrm hdr */ +#define LWL_SM_NC_C2 (0x12000000) /* Strobo-Mode/No-Coinc 2 chan dgrm hdr */ +#define LWL_SM_NC_C3 (0x13000000) /* Strobo-Mode/No-Coinc 3 chan dgrm hdr */ +#define LWL_SM_NC_C4 (0x14000000) /* Strobo-Mode/No-Coinc 4 chan dgrm hdr */ +#define LWL_SM_NC_C5 (0x15000000) /* Strobo-Mode/No-Coinc 5 chan dgrm hdr */ +#define LWL_SM_NC_C6 (0x16000000) /* Strobo-Mode/No-Coinc 6 chan dgrm hdr */ +#define LWL_SM_NC_C7 (0x17000000) /* Strobo-Mode/No-Coinc 7 chan dgrm hdr */ +#define LWL_SM_NC_C8 (0x18000000) /* Strobo-Mode/No-Coinc 8 chan dgrm hdr */ +#define LWL_SM_NC_C9 (0x19000000) /* Strobo-Mode/No-Coinc 9 chan dgrm hdr */ + +#define LWL_SM_CO (0x10000000) /* Strobo-Mode + Coinc 0 chan dgrm hdr */ +#define LWL_SM_CO_C1 (0x11000000) /* Strobo-Mode + Coinc 1 chan dgrm hdr */ +#define LWL_SM_CO_C2 (0x12000000) /* Strobo-Mode + Coinc 2 chan dgrm hdr */ +#define LWL_SM_CO_C3 (0x13000000) /* Strobo-Mode + Coinc 3 chan dgrm hdr */ +#define LWL_SM_CO_C4 (0x14000000) /* Strobo-Mode + Coinc 4 chan dgrm hdr */ +#define LWL_SM_CO_C5 (0x15000000) /* Strobo-Mode + Coinc 5 chan dgrm hdr */ +#define LWL_SM_CO_C6 (0x16000000) /* Strobo-Mode + Coinc 6 chan dgrm hdr */ +#define LWL_SM_CO_C7 (0x17000000) /* Strobo-Mode + Coinc 7 chan dgrm hdr */ +#define LWL_SM_CO_C8 (0x18000000) /* Strobo-Mode + Coinc 8 chan dgrm hdr */ +#define LWL_SM_CO_C9 (0x19000000) /* Strobo-Mode + Coinc 9 chan dgrm hdr */ + +#define LWL_TSI_MODE_MASK (0x000e) /* Mask for mode in Time Status Info */ +#define LWL_TSI_MODE_TR (0x0000) /* TSI Transparent-Mode */ +#define LWL_TSI_MODE_HM (0x0002) /* TSI Hist-Mode */ +#define LWL_TSI_MODE_TOF (0x0004) /* TSI TOF-Mode */ +#define LWL_TSI_MODE_SM1 (0x0006) /* TSI Strobo-Mode 1 - time-stamp coded */ +#define LWL_TSI_MODE_TR_UD (0x0008) /* TSI Transparent-Mode Up-Down */ +#define LWL_TSI_MODE_HM_UD (0x000a) /* TSI Hist-Mode Up-Down */ +#define LWL_TSI_MODE_TOF_UD (0x000c) /* TSI TOF-Mode Up-Down */ +#define LWL_TSI_MODE_SM2 (0x000e) /* TSI Strobo-Mode 2 - h/w coded */ +/* +**------------------------------------------------------------------------------ +** Define structure of a TOF histogram data item. +*/ + struct tof_histog { + int cntr_nmbr; /* Counter number */ + uint lo_edge; /* Low edge of first bin (20-bit value) */ + uint hi_edge; /* Top edge of last bin (20-bit value) */ + usint flag; /* Bit mask giving info on histog -- may be + ** used to help optimise the code */ + usint bytes_per_bin; /* Number of bytes in each histogram bin */ + uint n_bins; /* Number of bins in histogram */ + uint cnt_early_up; /* Count of early events (pol'n up) */ + uint cnt_late_up; /* Count of late events (pol'n up) */ + uint cnt_early_down; /* Count of early events (pol'n down) */ + uint cnt_late_down; /* Count of late events (pol'n down) */ + uint *bin_edge; /* Pointer to array of bin edges */ + union { /* Pointer to histogram array */ + uchar *b_bin_data; /* .. pointer if it's 8-bit bins */ + usint *w_bin_data; /* .. pointer if it's 16-bit bins */ + uint *l_bin_data; /* .. pointer if it's 32-bit bins */ + } u; + }; + +/* Define a TOF 'edge-info' structure. This structure is created +** as a result of a TOF 'edge-array' in a SQHM__TOF config cmnd. +*/ + struct tof_edge_info { + uint n_bins; /* Number of bins in histogram */ + uint flag; /* Flag bits defining type of histo */ + uint bin_span; /* Time spanned by a histogram bin (20-bit + ** value) if bin width is constant. Otherwise + ** it is zero. */ + uint hi_edge; /* Top edge of last bin (20-bit value) */ + uint edges[2]; /* Array of edge data (20-bit values). There + ** are actually (n_bins+1) items in the array + ** and give the bottom edges of the bin */ + }; + +/* Define structure of a TOF 'edge-array' in SQHM__TOF config cmnd +*/ + struct tof_edge_arr { + uint n_bins; /* Number of bins in histogram */ + uint flag; /* Flag (0/1) for fixed/variable bin size */ + uint *edges; /* Array of bottom edges (20-bit values) */ + }; + +/* Define structure of a TOF 'bank' in SQHM__TOF config command +*/ + struct tof_bank { + usint first; /* Number of first counter in bank */ + usint n_cntrs; /* Number of counters in bank */ + usint edge_indx; /* Index of edge array */ + usint bytes_per_bin; /* Number of bytes per bin */ + }; +/* +**------------------------------------------------------------------------------ +** Define command structure. +*/ + struct req_buff_struct { /* For messages to SinqHM */ + uint bigend; + uint cmnd; + union { + char filler[56]; + + struct {uint max_pkt, + strt_mode;} cnct; + + struct {uint mode; + union { + struct { + uint n_buffs; + uint n_bytes; + } trans; + struct { + uint n_hists; + uint lo_bin; + uint num_bins; + uint bytes_per_bin; + uint compress; + } hm_dig; + struct { + uint n_extra_bytes; + usint n_edges; + usint n_banks; + uint preset_delay; + struct tof_edge_arr edge_0; + struct tof_bank bank_0; + } tof; + struct { + uint n_extra_bytes; + usint n_edges; + usint n_banks; + uint preset_delay; + usint xFactor; + usint yFactor; + usint xOffset; + usint yOffset; + usint xSize; + usint ySize; + struct tof_edge_arr edge_0; + struct tof_bank bank_0; + } psd; + } u; + } cnfg; + + struct {uint mask;} dbg; + + struct {uint sub_code;} decnfg; + + struct {uint sub_cmnd;} daq; + + struct {uint sub_code, + x_lo, + nx, + y_lo, + ny, + xdim, + nhist;} project; + + struct {uint hist_no, + first_bin, + n_bins;} read; + + struct {uint hist_no;} select; + + struct {uint hist_no, + first_bin, + n_bins, + bytes_per_bin;} write; + + struct {uint hist_no, + first_bin, + n_bins;} zero; + } u; + }; +/* +**------------------------------------------------------------------------------ +** Define status response structure. +*/ + struct rply_buff_struct { /* For messages from SinqHM */ + uint bigend; + uint status; + uint sub_status; + union { + char message[52]; + + struct {uint port; + uint pkt_size; + uint hm_mode; + uint n_hists; + uint num_bins; + uint bytes_per_bin; + uint curr_hist; + uint max_block; + uint total_bytes; + uint lo_cntr; + uint lo_bin; + uint compress; + uint up_time;} cnct; + + struct {usint daq_now; + usint daq_was; + usint filler_mask; + usint server_mask;} daq; + + struct {uint n_extra_bytes; + uint up_time; + usint offset_vxWorks_ident; + usint offset_vxWorks_date; + usint offset_instr; + usint offset_def_ident; + usint offset_sinqhm_main_ident; + usint offset_sinqhm_main_date; + usint offset_sinqhm_server_ident; + usint offset_sinqhm_server_date; + usint offset_sinqhm_filler_ident; + usint offset_sinqhm_filler_date; + usint offset_sinqhm_routines_ident; + usint offset_sinqhm_routines_date;} ident; + + struct {uint n_bins; + uint bytes_per_bin; + uint cnts_lo; + uint cnts_hi;} project; + + struct {uint first_bin; + uint n_bins; + uint bytes_per_bin; + uint cnts_lo; + uint cnts_hi;} read; + + struct {uint cfg_state; + usint n_hists, curr_hist; + uint num_bins; + uint max_n_hists; + uint max_num_bins; + uchar max_srvrs, act_srvrs, bytes_per_bin, compress; + usint daq_now, filler_mask; + uint max_block; + usint tsi_status, flags; + union { + uint dead_time; + uint dts; + uint both; + } dt_or_dts; + uint num_bad_events; + uint up_time;} status; + } u; + }; +/* +**------------------------------------------------------------------------------ +** Define structure of message to SinqHM-filler. +*/ + struct msg_to_filler_struct { /* For messages to SinqHM-filler */ + union { + char message[32]; /* Ensure buffer is 32 bytes total */ + struct { + uint cmnd; + uint index; + usint new_mask;} uu; + } u; + }; +/*======================================================= End of SinqHM_def.h */ diff --git a/hardsup/stredit.c b/hardsup/stredit.c new file mode 100644 index 0000000..27b61c4 --- /dev/null +++ b/hardsup/stredit.c @@ -0,0 +1,415 @@ +#define ident "1B03" +#ifdef VAXC +#module StrEdit ident +#endif +#ifdef __DECC +#pragma module StrEdit ident +#endif +/* +** +--------------------------------------------------------------+ +** | Paul Scherrer Institute | +** | Department ASQ | +** | | +** | This software may be used freely by non-profit organizations.| +** | It may be copied provided that the name of P.S.I. and of the | +** | author is included. Neither P.S.I. nor the author assume any | +** | responsibility for the use of this software outside of P.S.I.| +** +--------------------------------------------------------------+ +** +** Module Name . . . . . . . . : [...LIB.SINQ]StrEdit.C +** +** Author . . . . . . . . . . : D. Maden +** Date of creation . . . . . . : Jan 1996 +** +** To compile this module, use: + + $ import tasmad + $ define/group sinq_c_tlb mad_lib:sinq_c.tlb + $ cc /debug /noopt /obj=[]StrEdit - + tasmad_disk:[mad.lib.sinq]StrEdit + + sinq_c_tlb/lib + +** To include this module in SINQ.OLB, use: + + $ import tasmad + $ define/group sinq_c_tlb mad_lib:sinq_c.tlb + $ + $ define/group sinq_olb mad_lib:sinq_dbg.olb + $ @tasmad_disk:[mad.lib.sinq]sinq_olb StrEdit debug + $ + $ define/group sinq_olb mad_lib:sinq.olb + $ @tasmad_disk:[mad.lib.sinq]sinq_olb StrEdit +** +** Updates: +** 1A01 19-Jan-1996 DM. Initial version. +** 1B01 21-Mar-1996 DM. Move from DELTAT.OLB to SINQ.OLB. +**============================================================================ +** The following entry points are included in this module: +** +**------------------------------------------------------------------------- +** #include +** +** char *StrEdit (char *out, char *in, char *ctrl, int *ln) +** ------- +** Input Args: +** in - the string to be edited. +** ctrl - the string specifying what is to be done. See Description +** below. +** Output Args: +** out - the edited string. The maximum size of this string must +** be specified as input parameter *ln. The string +** will be zero terminated on return. +** Modified Args: +** *ln - an integer specifying, on input, the length of "out" in +** bytes. This must include room for the zero termination. +** On return, ln will be set to the number of characters +** copied to "out" (not counting the zero termination byte). +** Return value: +** If an error is detected, the return value is a NULL pointer. Otherwise +** it is a pointer to the resulting string (i.e. "out"). +** Global variables: +** none +** Routines called: +** none +** Description: +** StrEdit (out, in, ctrl, ln) - This routine is intended to mimic the +** OpenVMS DCL lexical function F$EDIT. +** +** It first processes the string "in" to convert any C-style +** escape sequences introduced by a '\' character. Recognised +** escape sequences are: +** \a --> \007 BEL +** \b --> \010 BS (backspace) +** \f --> \014 FF (formfeed) +** \n --> \012 LF (linefeed) +** \r --> \015 CR (carriage return) +** \t --> \011 HT (horizontal tab) +** \v --> \013 VT (vertical tab) +** \\ --> \ +** \' --> ' +** \" --> " +** \? --> ? +** \xhh --> hh are an arbitrary number of hex digits. +** \nnn --> nnn are up to 3 octal digits. +** Any unrecognised escape sequence will be left unchanged. +** +** The resulting string is then edited according to the +** keywords specified in the control string "ctrl". The result +** will be written to string "out". The "out" argument may be +** the same as "in". +** +** On entry, "ln" specifies the size of "out" in bytes, including +** space for a null terminating byte. On return, it is set to the +** length of the result (not counting the zero-terminator). +** +** The following control strings are recognised: +** +** COLLAPSE - Removes all spaces and tabs from the string. +** COMPRESS - Replaces multiple spaces and tabs with a +** single space. +** LOWERCASE - Makes the string lower case. +** TRIM - Removes leading and trailing spaces and tabs +** from the string. +** UNCOMMENT - Removes comments from the string. +** UPCASE - Makes the string upper case. +** +** All keywords must be specified in full. They may be separated +** by white-space or commas and be in upper or lower case. +** +** If the input string contains non-escaped double quotes ("), +** then the editing functions are not applied to substrings within +** these quotes ("), there must be an even number of such quotes +** and the quotes are not copied to the resulting string. On the +** other hand, escaped double quotes (\") are treated as normal +** characters. +** +** Return Status: +** StrEdit returns a pointer to "out". If any errors are detected (e.g. an +** odd number of quotes), string editing is abandoned and a null pointer +** is returned. +** +** Example: +** strcpy (in, " asdfg \"hello there\" folks "); +** len = sizeof (in); +** printf ("\"%s\"\n", StrEdit (in, in, "trim upcase compress", &len)); +** will generate +** "ASDFG hello there FOLKS" +**------------------------------------------------------------------------- +** Global Definitions +*/ +#include +#include +#include +#include +#include + +#define NIL '\0' +#define True 1 +#define False 0 +#define QUOTE ((char) (('\"' ^ 0xff) & 0xff)) +/* +**==================================================================== +*/ +/* +**==================================================================== +*/ +/*-------------------------------------------------------------------------- +** Global Variables +*/ +/* +**--------------------------------------------------------------------------- +** StrEdit - edit a string. +** Note: strncat is used exclusively rather than +** strncpy to be sure result is always +** null terminated. +*/ + char *StrEdit ( +/* ======= +*/ char *out, + char *in, + char *ctrl, + int *ln) { + + int i, j, k, l, m, len, inxt, out_size; + char my_ctrl[80]; + char *tok_nxt, *my_in, *my_out, *my_tmp, *nxt; + int do_collapse, do_compress, do_lowercase, do_trim; + int do_uncomment, do_upcase; + + out_size = *ln; + if (out_size < 1) {*ln = 0; return NULL;} /* Can't do anything!! */ + + if (strlen (in) <= 0) { + *out = NIL; *ln = 0; return out; /* Nothing to do!! */ + } + /* + ** Scan ctrl looking to see what has to be done. Do this by first + ** taking a copy of it (in case it is declared "const" in the calling + ** routine, convert to lowercase and split into tokens at any space, + ** tab or comma. + */ + len = strlen (ctrl); + if (len >= sizeof (my_ctrl)) { + *out = NIL; *ln = 0; return NULL; + } + for (i = 0; i <= len; i++) my_ctrl[i] = tolower (ctrl[i]); + + do_collapse = do_compress = do_lowercase = do_trim = do_uncomment = + do_upcase = False; + tok_nxt = strtok (my_ctrl, ", \t\f\v\n"); + while (tok_nxt != NULL) { + if (strcmp (tok_nxt, "collapse") == 0) { + do_collapse = True; + }else if (strcmp (tok_nxt, "compress") == 0) { + do_compress = True; + }else if (strcmp (tok_nxt, "lowercase") == 0) { + do_lowercase = True; + }else if (strcmp (tok_nxt, "trim") == 0) { + do_trim = True; + }else if (strcmp (tok_nxt, "uncomment") == 0) { + do_uncomment = True; + }else if (strcmp (tok_nxt, "upcase") == 0) { + do_upcase = True; + }else { + *out = NIL; *ln = 0; return NULL; /* Illegal ctrl verb */ + } + tok_nxt = strtok (NULL, ", \t\f\v\n"); + } + + len = strlen (in) + 1; + my_in = malloc (len); /* Get some working space */ + if (my_in == NULL) { + *out = NIL; *ln = 0; return NULL; + } + /* + ** Copy "in" to the "my_in" working space, processing any '\' escape + ** sequences as we go. Note that, since "my_in" is big enough to hold + ** "in" and the escape sequence processing can only shorten the length + ** of "in", there's no need to check for an overflow of "my_in". Any + ** non-escaped double quotes are converted to something special so + ** that they can be recognised at the editing stage. + */ + nxt = my_in; + while (*in != '\0') { + if (*in == '\\') { /* Look for escape sequence */ + in++; + switch (*in) { + case 'a': case 'A': *nxt++ = '\007'; in++; break; + case 'b': case 'B': *nxt++ = '\010'; in++; break; + case 'f': case 'F': *nxt++ = '\014'; in++; break; + case 'n': case 'N': *nxt++ = '\012'; in++; break; + case 'r': case 'R': *nxt++ = '\015'; in++; break; + case 't': case 'T': *nxt++ = '\011'; in++; break; + case 'v': case 'V': *nxt++ = '\013'; in++; break; + case '\\': *nxt++ = '\\'; in++; break; + case '\'': *nxt++ = '\''; in++; break; + case '\"': *nxt++ = '\"'; in++; break; + case '\?': *nxt++ = '\?'; in++; break; + case 'x': case 'X': + in++; + i = strspn (in, "0123456789abcdefABCDEF"); + if (i > 0) { + *nxt++ = strtol (in, &in, 16); break; + }else { + *nxt++ = '\\'; break; + } + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + i = strspn (in, "01234567"); + if (i > 3) { + sscanf (in, "%3o", &j); + *nxt++ = j; + in += 3; + break; + }else if (i > 0) { + sscanf (in, "%o", &j); + *nxt++ = j; + in += i; + break; + }else { + *nxt++ = '\\'; + break; + } + default: + *nxt++ = '\\'; /* Invalid esc sequ - just copy it */ + } + }else if (*in == '\"') { /* Look for non-escaped double quotes */ + *nxt++ = QUOTE; *in++; /* Make it something unlikely */ + }else { + *nxt++ = *in++; + } + } + *nxt = '\0'; + + my_out = malloc (len); /* Get some working space */ + if (my_out == NULL) { + free (my_in); *out = NIL; *ln = 0; return NULL; + } + *my_out = NIL; + + my_tmp = malloc (len); /* Get some working space */ + if (my_tmp == NULL) { + free (my_out); free (my_in); + *out = NIL; *ln = 0; return NULL; + } + *my_tmp = NIL; + *out = NIL; + /* + ** Ensure "in" has an even number of non-escaped quotes. Return if not. + */ + i = 0; + for (j = 0; my_in[j] != NIL; j++) if (my_in[j] == QUOTE) i++; + if ((i & 1) == 1) { + free (my_tmp); + free (my_out); + free (my_in); + *ln = strlen (out); + return NULL; + } + /* + ** Scan through "in", substring by substring, to + ** handle quotation marks correctly. + */ + inxt = 0; + while (my_in[inxt] != NIL) { + if (my_in[inxt] == QUOTE) { /* Is there a quoted string next? */ + nxt = strchr (&my_in[inxt+1], QUOTE); /* Yes, find matching quote. */ + j = nxt - &my_in[inxt+1]; + memcpy (my_tmp, &my_in[inxt+1], j); /* Make copy of it */ + my_tmp[j] = NIL; + inxt = inxt + j + 2; + }else { + nxt = strchr (&my_in[inxt], QUOTE); /* Not a quoted string; .. + ** .. find next non-escaped .. + ** .. quote. + */ + if (nxt != NULL) { + j = nxt - my_in - inxt; + }else { + j = strlen (&my_in[inxt]); + } + memcpy (my_tmp, &my_in[inxt], j); /* Make copy for us to work on */ + my_tmp[j] = NIL; + inxt = inxt + j; + /* + ** For collapse and compress, start by turning all white space + ** chars to spaces. + */ + if (do_collapse || do_compress) { + for (k = 0; my_tmp[k] != NIL; k++) { + if (my_tmp[k] == '\t') my_tmp[k] = ' '; + if (my_tmp[k] == '\f') my_tmp[k] = ' '; + if (my_tmp[k] == '\v') my_tmp[k] = ' '; + if (my_tmp[k] == '\n') my_tmp[k] = ' '; + } + if (do_collapse) { + l = 0; + for (k = 0; my_tmp[k] != NIL; k++) { + if (my_tmp[k] != ' ') { + my_tmp[l] = my_tmp[k]; + l++; + } + } + my_tmp[l] = NIL; + }else if (do_compress) { + for (k = 0; my_tmp[k] != NIL; k++) { + if (my_tmp[k] == ' ') { + l = strspn (&my_tmp[k], " "); + if (l > 1) { + for (m = 0; my_tmp[k+l+m] != NIL; m++) { + my_tmp[k+m+1] = my_tmp[k+l+m]; + } + my_tmp[k+m+1] = NIL; + } + } + } + } + } + if (do_lowercase) { + for (k = 0; my_tmp[k] != NIL; k++) my_tmp[k] = _tolower (my_tmp[k]); + } + if (do_upcase) { + for (k = 0; my_tmp[k] != NIL; k++) my_tmp[k] = _toupper (my_tmp[k]); + } + if (do_uncomment) { + nxt = strchr (my_tmp, '!'); + if (nxt != NULL) { + *nxt = NIL; /* Truncate the string at the "!" */ + my_in[inxt] = NIL; /* Stop processing loop too */ + } + } + } + StrJoin (out, out_size, my_out, my_tmp); + strcpy (my_out, out); + } + + if (do_trim) { + i = strspn (my_out, " "); + if (i == strlen (my_out)) { /* If all spaces, result is a null string */ + *out = NIL; + }else { + for (j = strlen (my_out); my_out[j-1] == ' '; j--); + my_out[j] = NIL; + } + strcpy (out, &my_out[i]); + } + free (my_tmp); + free (my_out); + free (my_in); + *ln = strlen (out); + /* + ** Undo any encoded escape characters. + */ + for (i = 0; out[i] != NIL; i++) { + if (out[i] == ~'\"') out[i] = '\"'; + } + + return out; + } +/*-------------------------------------------------- End of StrEdit.C -------*/ diff --git a/hardsup/strjoin.c b/hardsup/strjoin.c new file mode 100644 index 0000000..88a8b72 --- /dev/null +++ b/hardsup/strjoin.c @@ -0,0 +1,142 @@ +#define ident "1B03" +#ifdef VAXC +#module StrJoin ident +#endif +#ifdef __DECC +#pragma module StrJoin ident +#endif +/* +** +--------------------------------------------------------------+ +** | Paul Scherrer Institute | +** | Department ASQ | +** | | +** | This software may be used freely by non-profit organizations.| +** | It may be copied provided that the name of P.S.I. and of the | +** | author is included. Neither P.S.I. nor the author assume any | +** | responsibility for the use of this software outside of P.S.I.| +** +--------------------------------------------------------------+ +** +** Module Name . . . . . . . . : [...LIB.SINQ]STRJOIN.C +** +** Author . . . . . . . . . . : D. Maden +** Date of creation . . . . . . : Nov 1995 +** +** To compile this module, use: + + $ import tasmad + $ define/group sinq_c_tlb mad_lib:sinq_c.tlb + $ cc /debug /noopt /obj=[]StrEdit - + tasmad_disk:[mad.lib.sinq]StrEdit + + sinq_c_tlb/lib + +** To include this module in SINQ.OLB, use: + + $ import tasmad + $ define/group sinq_c_tlb mad_lib:sinq_c.tlb + $ + $ define/group sinq_olb mad_lib:sinq_dbg.olb + $ @tasmad_disk:[mad.lib.sinq]sinq_olb StrEdit debug + $ + $ define/group sinq_olb mad_lib:sinq.olb + $ @tasmad_disk:[mad.lib.sinq]sinq_olb StrEdit +** +** Updates: +** 1A01 2-Nov-1995 DM. Initial version. +** 1B01 21-Mar-1996 DM. Move from DELTAT.OLB to SINQ.OLB. +** 1B03 28-May-1997 DM. Allow result string to be either of source +** strings. +**============================================================================ +** The following entry points are included in this module: +** +**------------------------------------------------------------------------- +** #include +** +** char *StrJoin (&result, result_size, &str_a, &str_b) +** ------- +** Input Args: +** int result_size - max size of "result". The resultant string will +** have a max length of (result_size - 1) to allow +** for the zero terminator +** char *str_a - Pointer to first string to be joined. +** char *str_b - Pointer to second string to be joined. +** Output Args: +** char *result - Pointer to resulting string. +** Modified Args: +** none +** Return value: +** Pointer to resulting string. +** Global variables modified: +** none +** Routines called: +** None +** Description: +** The routine joins 2 strings, checking for total string length and +** ensuring the result will be zero terminated. The "result" arg may be +** the same as "str_a" or "str_b". +**------------------------------------------------------------------------- +** Global Definitions +*/ +#include + +#define NIL '\0' +/* +**==================================================================== +*/ +/* +**==================================================================== +** StrJoin - join 2 strings. +** Note: strncat is used exclusively rather than +** strncpy to be sure result is always +** null terminated. +*/ + char *StrJoin ( +/* ======= +*/ char *result, + int result_size, + char *str_a, + char *str_b) { + + int i, size, size_a, size_b; + + size = result_size - 1; + + if (size < 0) return result; + + if (result == str_a) { /* Are the result and str_a the same? */ + size_a = strlen (str_a); /* Yes */ + if (size_a > size) { /* Check sizes anyway. */ + result[size] = NIL; /* Truncate str_a. No room for str_b! */ + }else { + size = size - strlen (result); /* And append str_b */ + if (size > 0) { + strncat (result, str_b, size); + } + } + }else if (result == str_b) { /* Are the result and str_b the same? */ + size_a = strlen (str_a); /* Yes, this is a bit complicated! */ + size_b = strlen (str_b); + if (size_a >= size) { /* If str_a completely fills result, .. */ + result[0] = NIL; /* .. then just copy in str_a */ + strncat (result, str_a, size); + }else { + /* + ** Otherwise, str_b must first be moved to + ** make room for str_a and then str_a must + ** be put at the front of the result. + */ + if ((size_a + size_b) > size) size_b = size - size_a; + result[size_a+size_b] = NIL; + for (i = (size_b-1); i >= 0; i--) { + result[size_a+i] = str_b[i]; + } + memcpy (result, str_a, size_a); + } + }else { /* Result is neither str_a nor str_b so .. */ + result[0] = NIL; /* .. str_a needs to be copied */ + strncat (result, str_a, size); + size = size - strlen (result); /* And str_a appended */ + if (size > 0) strncat (result, str_b, size); + } + return result; + } +/*-------------------------------------------------- End of STRJOIN.C =======*/ diff --git a/hardsup/table.c b/hardsup/table.c new file mode 100644 index 0000000..41e1b9c --- /dev/null +++ b/hardsup/table.c @@ -0,0 +1,176 @@ +/*------------------------------------------------------------------------- + Implementation file for translation table. + + Mark Koennecke, October 1997 + + copyright: see copyright.h +---------------------------------------------------------------------------*/ +#include +#include +#include +#include +#include "table.h" + +/*-------------------------------------------------------------------------*/ + typedef struct __SicsTable { + float *fVal1; + float *fVal2; + int iLength; + } STable; +/*-------------------------------------------------------------------------*/ + pSTable CreateTable(FILE *fd) + { + pSTable pNew = NULL; + long lStart, lEnd, lData, i; + char *pBuffer = NULL, *pEnd = NULL, *pEndLine, *pPtr; + int iLength, iRet; + float fVal1, fVal2; + + assert(fd); + + /* find length of file, create a buffer and read it in */ + lStart = ftell(fd); + fseek(fd,0L,SEEK_END); + lEnd = ftell(fd); + lData = lEnd - lStart; + pBuffer = (char *)malloc(lData*sizeof(char)); + if(!pBuffer) + { + return NULL; + } + fseek(fd,lStart,SEEK_SET); + fread(pBuffer,sizeof(char),lData,fd); + + /* find number of lines */ + for(i = 0, iLength = 0; i < lData; i++) + { + if(pBuffer[i] == '\n') + { + iLength++; + } + } + + /* allocate the table structure */ + pNew = (pSTable)malloc(sizeof(STable)); + if(!pNew) + { + free(pBuffer); + return NULL; + } + pNew->iLength = iLength; + pNew->fVal1 = (float *)malloc(sizeof(float)*iLength); + pNew->fVal2 = (float *)malloc(sizeof(float)*iLength); + if( (!pNew->fVal1) || (!pNew->fVal2)) + { + free(pBuffer); + free(pNew); + return NULL; + } + memset(pNew->fVal1,0,iLength*sizeof(float)); + memset(pNew->fVal2,0,iLength*sizeof(float)); + + /* dodge through the file reading pairs until end */ + pPtr = pBuffer; + pEnd = pBuffer + lData; + pEndLine = pBuffer; + i = 0; + while(pEndLine < pEnd) + { + if(*pEndLine == '\n') + { + *pEndLine = '\0'; + iRet = sscanf(pPtr,"%f %f",&fVal1, &fVal2); + if(iRet == 2) + { + pNew->fVal1[i] = fVal1; + pNew->fVal2[i] = fVal2; + i++; + } + pEndLine++; + pPtr = pEndLine; + } + else + { + pEndLine++; + } + } + + free(pBuffer); + return pNew; + } +/*--------------------------------------------------------------------------*/ + void DeleteTable(pSTable self) + { + if(self->fVal1) + { + free(self->fVal1); + } + if(self->fVal2) + { + free(self->fVal2); + } + free(self); + } +/*--------------------------------------------------------------------------*/ + int InterpolateVal1(pSTable self, float fKey, float *fResult) + { + float fFrac; + int i1,i; + + assert(self); + assert(self->fVal1); + assert(self->fVal2); + + /* search the entry point */ + for(i = 0; i < self->iLength; i++) + { + if(self->fVal1[i] >= fKey) + { + i1 = i; + break; + } + } + if(i1 >= self->iLength) + { + return 0; + } + + /* interpolate */ + fFrac = (fKey - self->fVal1[i1 -1]) + / (self->fVal1[i1] - self->fVal1[i1 - 1]); + *fResult = self->fVal2[i1-1] + + fFrac*(self->fVal2[i1] - self->fVal2[i1 -1]); + return 1; + } +/*---------------------------------------------------------------------------*/ + int InterpolateVal2(pSTable self, float fKey, float *fResult) + { + float fFrac; + int i1,i; + + assert(self); + assert(self->fVal1); + assert(self->fVal2); + + /* search the entry point */ + for(i = 0; i < self->iLength; i++) + { + if(self->fVal2[i] <= fKey) + { + i1 = i; + break; + } + } + if(i1 >= self->iLength) + { + return 0; + } + + /* interpolate */ + fFrac = (fKey - self->fVal2[i1 -1]) + / (self->fVal2[i1] - self->fVal2[i1 - 1]); + *fResult = self->fVal1[i1-1] + + fFrac*(self->fVal1[i1] - self->fVal1[i1 -1]); + return 1; + } + diff --git a/hardsup/table.h b/hardsup/table.h new file mode 100644 index 0000000..7d98b29 --- /dev/null +++ b/hardsup/table.h @@ -0,0 +1,35 @@ +/*--------------------------------------------------------------------------- + A general purpose translation table and interpolation module. + Interpolation tables are read from a file, which is meant to + contain pairs of val1 val2 per line. + + + Mark Koennecke, October 1997 + + copyright: see copyright.h + +-----------------------------------------------------------------------------*/ +#ifndef SICSTABLE +#define SICSTABLE + typedef struct __SicsTable *pSTable; + +/*------------------------- live & death ----------------------------------*/ + pSTable CreateTable(FILE *fd); + /* + creates a new table from a given file. The file is meant to have + been positioned to the first entry for the table in the file. + This leaves the caller free to examine a header, if any. + */ + void DeleteTable(pSTable self); +/*------------------------- Interpolation --------------------------------*/ + int InterpolateVal1(pSTable pTable, float fKey, float *fResult); + /* + Returns a result from the second column for a key from the + first column. + */ + int InterpolateVal2(pSTable pTable, float fKey, float *fResult); + /* + Returns a result from the first column for a key from the + second column. + */ +#endif diff --git a/hardsup/velsel_def.h b/hardsup/velsel_def.h new file mode 100644 index 0000000..276eb65 --- /dev/null +++ b/hardsup/velsel_def.h @@ -0,0 +1,58 @@ +#ifndef _velsel_def_ +#define _velsel_def_ +/*------------------------------------------------ VelSel_DEF.H Ident V01B +*/ +#include +#include + +#ifndef OffsetOf +#define OffsetOf(type, identifier) ((size_t)(&((type*) NULL)->identifier)) +#endif + +enum VelSel_Errors {VELSEL__BAD_TMO = -1, + VELSEL__BAD_CMD = -3, + VELSEL__BAD_OFL = -4, + VELSEL__BAD_ILLG = -5, + VELSEL__BAD_HOST = -6, + VELSEL__BAD_SOCKET = -7, + VELSEL__BAD_BIND = -8, + VELSEL__BAD_CONNECT = -9, + VELSEL__BAD_DEV = -10, + VELSEL__BAD_MALLOC = -11, + VELSEL__BAD_SENDLEN = -12, + VELSEL__BAD_SEND = -13, + VELSEL__BAD_SEND_PIPE = -14, + VELSEL__BAD_SEND_NET = -15, + VELSEL__BAD_SEND_UNKN = -16, + VELSEL__BAD_RECV = -17, + VELSEL__BAD_RECV_PIPE = -18, + VELSEL__BAD_RECV_NET = -19, + VELSEL__BAD_RECV_UNKN = -20, + VELSEL__BAD_NOT_BCD = -21, + VELSEL__BAD_RECVLEN = -22, + VELSEL__BAD_FLUSH = -23, + VELSEL__BAD_RECV1 = -24, + VELSEL__BAD_RECV1_PIPE = -25, + VELSEL__BAD_RECV1_NET = -26, + VELSEL__BAD_PAR = -29, + VELSEL__BAD_BSY = -30, + VELSEL__BAD_OPEN = -31, + VELSEL__FORCED_CLOSED = -32, + VELSEL__BAD_STP = -33, + VELSEL__NOT_OPEN = -35, + VELSEL__BAD_ASYNSRV = -36, + VELSEL__BAD_REPLY = -34}; +/* +** Structure to which the VelSel_Open handle points. +*/ + struct VelSel_info { + struct AsynSrv__info asyn_info; /* Contains skt, host, port & chan */ + int tmo; + char eot[4]; + int msg_id; + int n_replies, max_replies; + struct RS__MsgStruct to_host; + struct RS__RespStruct from_host; + }; +/*------------------------------------------------ End of VelSel_DEF.H --*/ +#endif /* _velsel_def_ */ diff --git a/hardsup/velsel_utility.c b/hardsup/velsel_utility.c new file mode 100644 index 0000000..3b277b0 --- /dev/null +++ b/hardsup/velsel_utility.c @@ -0,0 +1,928 @@ +#define ident "1A01" +#ifdef VAXC +#module VelSel_Utility ident +#endif +#ifdef __DECC +#pragma module VelSel_Utility ident +#endif +/* +** +--------------------------------------------------------------+ +** | Paul Scherrer Institute | +** | Department ASQ | +** | | +** | This software may be used freely by non-profit organizations.| +** | It may be copied provided that the name of P.S.I. and of the | +** | author is included. Neither P.S.I. nor the author assume any | +** | responsibility for the use of this software outside of P.S.I.| +** +--------------------------------------------------------------+ +** +** Module Name . . . . . . . . : [...LIB.SINQ]VelSel_Utility.C +** +** Author . . . . . . . . . . : D. Maden +** Date of creation . . . . . . : June 1997 +** +** To compile this module, use: + + $ import tasmad + $ define/group sinq_c_tlb mad_lib:sinq_c.tlb + $ cc /debug /noopt /obj=[]VelSel_Utility - + tasmad_disk:[mad.psi.lib.sinq]VelSel_Utility + - + sinq_c_tlb/lib + +** To include this module in SINQ.OLB, use: + + $ import tasmad + $ define/group sinq_c_tlb mad_lib:sinq_c.tlb + $ + $ define/group sinq_olb mad_lib:sinq_dbg.olb + $ @tasmad_disk:[mad.lib.sinq]sinq_olb VelSel_Utility debug + $ + $ define/group sinq_olb mad_lib:sinq.olb + $ @tasmad_disk:[mad.lib.sinq]sinq_olb VelSel_Utility +** +** Updates: +** 1A01 13-Jun-1997 DM. Initial version. +**============================================================================ +** The entry points included in this module are described below. Prototypes +** can be defined via: +** +** #include +** +** VelSel_Close - Close a connection to a Velocity Selector. +** VelSel_Config - Configure a connection to a Velocity Selector. +** VelSel_ErrInfo - Return detailed status from last operation. +** VelSel_GetReply - Get next reply from a reply buffer. +** VelSel_GetStatus - Get "???" response. +** VelSel_Open - Open a connection to a Velocity Selector. +** VelSel_SendCmnds - Send commands to RS232C server. +**--------------------------------------------------------------------- +** int VelSel_Close (&handle, int force_flag) +** ------------ +** Input Args: +** int force_flag - if non-zero, all connections using the same socket +** will also be closed (this gets AsynSrv_Close to +** actually close the socket and is needed for error +** recovery operations). +** Output Args: +** none +** Modified Args: +** void **handle - The pointer to the structure returned by VelSel_Open. +** On return, the pointer is set to NULL. +** Return status: +** True always (error returns from close and free are not checked). +** Routines called: +** AsynSrv_Close +** Description: +** The routine calls AsynSrv_Close to close the connection to the RS232C +** server. If 'force_flag' is non-zero, all other connections to the +** RS232C server which use the same socket will also be closed. +** +** The 'force_flag' can be useful in error recovery situations. The AsynSrv +** utility operates by only opening a socket for each separate combination +** of host/port. Hence, if several connections are open to the server, +** then calling VelSel_Close doesn't actually close the socket until all +** connections have been closed. In the situation +** where an error has been detected, it is often desirable to +** close and re-open the socket as part of the recovery procedure. Calling +** VelSel_Close with 'force_flag' non-zero will force the socket to be +** closed and will mark all connections using this socket so that they +** will be informed of the event when they next call any AsynSrv +** dependent routine. +** +** Note: The force-close action is effected by the AsynSrv package. A +** force-close will thus also close any connections to other +** RS-232-C devices (e.g. EL737 neutron cntr) on the same server. +**------------------------------------------------------------------------- +** void VelSel_Config (&handle, msec_tmo, eot_str) +** ------------- +** Input Args: +** void **handle - The pointer to the structure returned by VelSel_Open. +** int msec_tmo - The time-out for responses. Dflt = 10000. +** char *eot_str - A string of up to 3 characters specifying terminating +** characters for input. +** Output Args: +** none +** Modified Args: +** none +** Return status: +** none +** Routines called: +** none +** Description: +** The routine sets values in the VelSel_info data structure. +**------------------------------------------------------------------------- +** void VelSel_ErrInfo (&entry_txt_ptr, &errcode, &my_errno, &vaxc_errno) +** -------------- +** Input Args: +** None +** Output Args: +** char **entry_txt_ptr - Pointer to a text string giving the call stack +** at the time that the error was detected. +** int *errcode - An internal error code indicating the detected error. +** int *my_errno - Saved value of errno. +** int *vaxc_errno - Saved value of vaxc$errno (OpenVMS only). +** Modified Args: +** none +** Return status: +** none +** Routines called: +** none +** Description: +** Returns detailed status of the last operation. Once an error has been +** detected, the error status is frozen until this routine has been called. +**------------------------------------------------------------------------- +** void *VelSel_GetReply (&handle, last_rply) +** --------------- +** Input Args: +** void **handle - The pntr to the structure returned by VelSel_Open. +** void *last_rply - Address of last reply processed or NULL. +** Output Args: +** none +** Modified Args: +** none +** Return status: +** Address of next reply structure in the buffer or NULL if no more. +** Routines called: +** none +** Description: +** VelSel_GetReply is a utility routine mainly intended for internal use +** by the VelSel_Utility package. It unpacks the replies in the response +** packet from the RS232C server. +** +** Having received a response from the server to a sequence of commands, +** VelSel_GetReply is called with last_rply = NULL. The return value is +** a pointer to the first reply sub-structure in the response. On calling +** VelSel_GetReply again with last_rply set to this address, one receives +** the address of the second reply sub-structure and so on, until NULL +** is returned when all responses have been exhausted. The structure of +** a reply sub-structure is RS__RplyStruct. +**------------------------------------------------------------------------- +** int VelSel_GetStatus (&handle, &status_str, status_str_len) +** ---------------- +** Input Args: +** void **handle - The pointer to the structure returned by VelSel_Open. +** int status_str_len - The length of status_str. +** Output Args: +** char *status_str - Pointer to a buffer to save the status. +** Modified Args: +** none +** Return status: +** True if no problems detected, otherwise False and VelSel_ErrInfo +** can be called to identify the problem. Values of Errcode set by +** VelSel_GetStatus are (other values may be set by the called routines): +** VELSEL__BAD_TMO, _LOC, _CMD, _OFL, _ADR --> see VelSel_Open. +** VELSEL__BAD_ILLG = -5 --> one of the responses could probably not be +** decoded. This could happen if there is noise +** on the RS232C connection to the Velocity +** Selector. +** If an error is detected, ist_posit is set to 0.0 and all other +** arguments to -1. +** Routines called: +** VelSel_SendCmnds +** Description: +** The routine issues a "???" command to the Velocity Selector and +** analyses the result. +**------------------------------------------------------------------------- +** int VelSel_Open (&handle, host, port, chan, id) +** ----------- +** Input Args: +** char *host - Name of host offering the TCP/IP service. +** int port - Number of TCP/IP port of TCP/IP server. +** int chan - RS-232-C Channel number on the TCP/IP server. +** char *id - The expected ID of the device, normally "????". +** If id is NULL, the device ID is not checked. +** Output Args: +** void *handle - A pointer to a structure of type VelSel_info needed +** for subsequent calls to VelSel_... routines. Buffer +** space for the structure is allocated dynamically. +** It gets released via a call to VelSel_Close. +** Modified Args: +** none +** Return status: +** True if no problems detected, otherwise False. If False, VelSel_ErrInfo +** can be called to identify the problem. Values of Errcode set by +** VelSel_Open are (other values may be set by the called routines): +** VELSEL__BAD_TMO = -1 --> Time-out error ("?TMO" - this gets +** generated by the RS232C server). +** VELSEL__BAD_LOC = -2 --> Off-line ("?LOC"). This should not +** happen on calls to VelSel_Open since it +** sends an "RMT 1" cmnd. +** VELSEL__BAD_CMD = -3 --> Command error ("?CMD"). This could be +** caused by noise in the RS-232-C +** transmission. +** VELSEL__BAD_OFL = -4 --> Connection broken ("?OFL"). +** This can get generated by RS232C_SRV +** if, for example, the connection is via +** a terminal server and the terminal +** server loses power. +** VELSEL__BAD_ILLG = -5 --> Some other unrecognised response. This +** should never occur, of course! +** VELSEL__BAD_HOST = -6 --> Call to "gethostbyname" failed to get +** network addr of host. +** VELSEL__BAD_SOCKET = -7 --> Call to "socket" failed. +** VELSEL__BAD_BIND = -8 --> Call to "bind" failed. +** VELSEL__BAD_CONNECT = -9 --> Call to "connect" failed. +** VELSEL__BAD_DEV = -10 --> Bad cmnd response - is device a VelSel? +** VELSEL__BAD_MALLOC = -11 --> Call to "malloc" failed +** Routines called: +** AsynSrv_open, the memory alloc routine "malloc" and VelSel_SendCmnds. +** Description: +** The routine calls AsynSrv_open to open a TCP/IP connection to a server +** offering the "RS-232-C" service for a Velocity Selector. "RMT 1" +** and "ECHO 0" commands are sent to ensure the device is on-line. +**------------------------------------------------------------------------- +** int VelSel_SendCmnds (&handle, ...) +** ---------------- +** Input Args: +** void **handle - The pntr to the structure returned by VelSel_Open. +** char * ... - A list of commands, terminated by NULL, for +** sending to the Velocity Selector. The commands must +** have any necessary \r characters included. +** Output Args: +** none +** Modified Args: +** none +** Return status: +** True if no problems detected, otherwise False and errcode (see +** VelSel_ErrInfo) is set to indicate the nature of the problem. +** VelSel_errcode may be set as follows: +** VELSEL__BAD_SENDLEN = -12 --> Too much to send; either too many +** commands or too long. The buffer +** is 232 bytes long and each command +** has a 2-byte header. +** Errors -13 to -16 are related to network errors whilst sending the +** message buffer to the server: +** VELSEL__BAD_SEND = -13 --> Network problem - server has +** probably abended. +** VELSEL__BAD_SEND_PIPE = -14 --> Network pipe broken - probably same +** cause as VELSEL__BAD_SEND. +** VELSEL__BAD_SEND_NET = -15 --> Some other network problem. "errno" +** may be helpful. +** VELSEL__BAD_SEND_UNKN = -16 --> Some other network problem happened +** resulting in the message not +** getting sent completely. "errno" is +** probably not helpful in this case. +** Errors VELSEL__BAD_RECV, VELSEL__BAD_RECV_PIPE, VELSEL__BAD_RECV_NET +** and VELSEL__BAD_RECV_UNKN (-17 to -20) are related to network +** errors whilst receiving the 4-byte response header. They are +** analogous to VELSEL__BAD_SEND to VELSEL__BAD_SEND_UNKN. +** VELSEL__BAD_NOT_BCD = -21 --> The 4-byte response header is not an +** ASCII coded decimal integer. +** VELSEL__BAD_RECVLEN = -22 --> The body of the response would be too +** big to fit in the input buffer. The +** buffer is 244 bytes long and each +** response has a 3-byte header and a +** trailing zero-byte. The response +** is flushed. +** VELSEL__BAD_FLUSH = -23 --> Some network error was detected +** during flushing. This is an "or" +** of errors VELSEL__BAD_RECV to +** VELSEL__BAD_RECV_UNKN. +** VELSEL__FORCED_CLOSED = -32 --> The connection to the Velocity +** Selector has been forcefully +** closed. See below. +** VELSEL__BAD_REPLY = -34 --> The n_rply field of the response was +** either non-numeric or <0, indicating +** that the Terminal Server detected an +** error. The reply is added to the +** routine call stack for debug purposes. +** +** Errors VELSEL__BAD_RECV1, VELSEL__BAD_RECV1_PIPE and +** VELSEL__BAD_RECV1_NET (-24 to -26) are related to network +** errors whilst receiving the body of the response. They are +** equivalent to errors VELSEL__BAD_RECV, to VELSEL__BAD_RECV_NET. +** +** VELSEL__FORCED_CLOSED occurs if AsynSrv_Close has been called (e.g. +** via a call to VelSel_Close) for another device on the same +** server and the 'force_flag' was set (see VelSel_Close). The +** caller should call VelSel_Close and then VelSel_Open to +** re-establish a connection to the Velocity Selector. +** Routines called: +** Socket library routines send and recv. +** Description: +** The list of commands is assembled into a message buffer with appropriate +** header information and sent off to the server. The response is then +** awaited and read in when it arrives. +** +** For any of the following errors: +** VELSEL__BAD_SEND (Note: VELSEL__BAD_SENDLEN and +** VELSEL__BAD_SEND_PIPE VELSEL__BAD_RECVLEN do not cause a close +** VELSEL__BAD_SEND_NET +** VELSEL__BAD_SEND_UNKN +** VELSEL__BAD_RECV +** VELSEL__BAD_RECV_PIPE +** VELSEL__BAD_RECV_NET +** VELSEL__BAD_RECV_UNKN +** VELSEL__BAD_NOT_BCD +** VELSEL__BAD_FLUSH +** VELSEL__BAD_RECV1 +** VELSEL__BAD_RECV1_PIPE +** VELSEL__BAD_RECV1_NET +** the network link to the server is force-closed via a call to VelSel_Close. +** Once the error has been corrected, the link can be re-opened via a +** call to VelSel_Open. As a result of the force-close, other active handles +** will need to be released via a call to VelSel_Close before VelSel_Open is +** called. +** +** Note: neither of the errors VELSEL__BAD_SENDLEN, VELSEL__BAD_RECVLEN +** nor VELSEL__BAD_REPLY cause the link to be closed. +**============================================================================*/ +/* +**--------------------------------------------------------------------------- +** Global Definitions +*/ +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#ifdef __VMS +#include +#else +#include +#endif +/*-----------------------------------------------------------------*/ +#include +#include +#include +#include + +#define True 1 +#define False 0 +/*-------------------------------------------------------------------------- +** Global Variables +*/ + static int VelSel_call_depth = 0; + static char VelSel_routine[5][64]; + static int VelSel_errcode = 0; + static int VelSel_errno, VelSel_vaxc_errno; +/* +**--------------------------------------------------------------------------- +** VelSel_Close: Close a connection to a Velocity Selector. +*/ + int VelSel_Close ( +/* =========== +*/ void **handle, + int force_flag) { + + struct VelSel_info *info_ptr; + char buff[4]; + + info_ptr = (struct VelSel_info *) *handle; + if (info_ptr == NULL) return True; + + if (info_ptr->asyn_info.skt != 0) { + if (info_ptr->asyn_info.skt > 0) { + AsynSrv_Close (*handle, force_flag); + } + } + free (*handle); + *handle = NULL; + + return True; + } +/* +**--------------------------------------------------------------------------- +** VelSel_Config: Configure a connection to a Velocity Selector. +*/ + void VelSel_Config ( +/* ============ +*/ void **handle, + int msec_tmo, + char *eot_str) { + + int i; + struct VelSel_info *info_ptr; + + info_ptr = (struct VelSel_info *) *handle; + if (info_ptr == NULL) return; + /*------------------------- + ** Set up the time-out + */ + if (msec_tmo < 0) { + info_ptr->tmo = -1; + }else { + info_ptr->tmo = (msec_tmo + 99)/100; /* Convert to deci-secs */ + if (info_ptr->tmo > 9999) info_ptr->tmo = 9999; + } + /*--------------------------------- + ** Set up the end-of-text string + */ + if (eot_str != NULL) { + for (i = 0; i < sizeof (info_ptr->eot); i++) info_ptr->eot[i] = '\0'; + + for (i = 0; i < sizeof (info_ptr->eot); i++) { + if (eot_str[i] == '\0') break; + info_ptr->eot[i+1] = eot_str[i]; + } + info_ptr->eot[0] = '0' + i; + } + return; + } +/* +** ------------------------------------------------------------------------- +** VelSel_ErrInfo: Return detailed status from last operation. +*/ + void VelSel_ErrInfo ( +/* ============= +*/ char **entry_txt, + int *errcode, + int *my_errno, + int *vaxc_errno) { + + int i; + char buff[80]; + int asyn_errcode, asyn_errno, asyn_vaxerrno; + char* asyn_errtxt; + + if (VelSel_call_depth <= 0) { + strcpy (VelSel_routine[0], "VelSel_no_error_detected"); + *errcode = 0; + *my_errno = 0; + *vaxc_errno = 0; + }else { + if (VelSel_call_depth > 1) { /* Concatenate the names */ + for (i = 1; i < VelSel_call_depth; i++) { + strcat (VelSel_routine[0], "/"); + StrJoin (VelSel_routine[0], sizeof (VelSel_routine), + VelSel_routine[0], VelSel_routine[i]); + } + } + *errcode = VelSel_errcode; + *my_errno = VelSel_errno; + *vaxc_errno = VelSel_vaxc_errno; + switch (VelSel_errcode) { + case VELSEL__BAD_TMO: strcpy (buff, "/VELSEL__BAD_TMO"); break; + case VELSEL__BAD_CMD: strcpy (buff, "/VELSEL__BAD_CMD"); break; + case VELSEL__BAD_OFL: strcpy (buff, "/VELSEL__BAD_OFL"); break; + case VELSEL__BAD_ILLG: strcpy (buff, "/VELSEL__BAD_ILLG"); break; + case VELSEL__BAD_HOST: strcpy (buff, "/VELSEL__BAD_HOST"); break; + case VELSEL__BAD_SOCKET: strcpy (buff, "/VELSEL__BAD_SOCKET"); break; + case VELSEL__BAD_BIND: strcpy (buff, "/VELSEL__BAD_BIND"); break; + case VELSEL__BAD_CONNECT: strcpy (buff, "/VELSEL__BAD_CONNECT"); break; + case VELSEL__BAD_DEV: strcpy (buff, "/VELSEL__BAD_DEV"); break; + case VELSEL__BAD_MALLOC: strcpy (buff, "/VELSEL__BAD_MALLOC"); break; + case VELSEL__BAD_SENDLEN: strcpy (buff, "/VELSEL__BAD_SENDLEN"); break; + case VELSEL__BAD_SEND: strcpy (buff, "/VELSEL__BAD_SEND"); break; + case VELSEL__BAD_SEND_PIPE: strcpy (buff, "/VELSEL__BAD_SEND_PIPE"); break; + case VELSEL__BAD_SEND_NET: strcpy (buff, "/VELSEL__BAD_SEND_NET"); break; + case VELSEL__BAD_SEND_UNKN: strcpy (buff, "/VELSEL__BAD_SEND_UNKN"); break; + case VELSEL__BAD_RECV: strcpy (buff, "/VELSEL__BAD_RECV"); break; + case VELSEL__BAD_RECV_PIPE: strcpy (buff, "/VELSEL__BAD_RECV_PIPE"); break; + case VELSEL__BAD_RECV_NET: strcpy (buff, "/VELSEL__BAD_RECV_NET"); break; + case VELSEL__BAD_RECV_UNKN: strcpy (buff, "/VELSEL__BAD_RECV_UNKN"); break; + case VELSEL__BAD_NOT_BCD: strcpy (buff, "/VELSEL__BAD_NOT_BCD"); break; + case VELSEL__BAD_RECVLEN: strcpy (buff, "/VELSEL__BAD_RECVLEN"); break; + case VELSEL__BAD_FLUSH: strcpy (buff, "/VELSEL__BAD_FLUSH"); break; + case VELSEL__BAD_RECV1: strcpy (buff, "/VELSEL__BAD_RECV1"); break; + case VELSEL__BAD_RECV1_PIPE: strcpy (buff, "/VELSEL__BAD_RECV1_PIPE"); break; + case VELSEL__BAD_RECV1_NET: strcpy (buff, "/VELSEL__BAD_RECV1_NET"); break; + case VELSEL__BAD_PAR: strcpy (buff, "/VELSEL__BAD_PAR"); break; + case VELSEL__BAD_BSY: strcpy (buff, "/VELSEL__BAD_BSY"); break; + case VELSEL__BAD_OPEN: strcpy (buff, "/VELSEL__BAD_OPEN"); break; + case VELSEL__FORCED_CLOSED: strcpy (buff, "/VELSEL__FORCED_CLOSED"); break; + case VELSEL__BAD_STP: strcpy (buff, "/VELSEL__BAD_STP"); break; + case VELSEL__BAD_REPLY: strcpy (buff, "/VELSEL__BAD_REPLY"); break; + default: sprintf (buff, "/VELSEL__unknown_err_code: %d", VelSel_errcode); + } + StrJoin (VelSel_routine[0], sizeof(VelSel_routine), VelSel_routine[0], buff); + } + AsynSrv_ErrInfo (&asyn_errtxt, &asyn_errcode, &asyn_errno, &asyn_vaxerrno); + if (asyn_errcode != 0) { + strcat (VelSel_routine[0], "/"); + StrJoin (VelSel_routine[0], sizeof(VelSel_routine), + VelSel_routine[0], asyn_errtxt); + } + *entry_txt = VelSel_routine[0]; + VelSel_call_depth = 0; + VelSel_errcode = 0; + } +/* +**--------------------------------------------------------------------------- +** VelSel_GetReply - Get next reply from a reply buffer. +*/ + void *VelSel_GetReply ( +/* ============== +*/ void **handle, /* Pointer to structure containing + ** message to pull apart */ + void *last_rply) { /* Starting point */ + + int rply_len; + struct RS__RplyStruct *ptr; + struct VelSel_info *my_info_ptr; + struct RS__RplyStruct *my_last_rply; + + ptr = NULL; + my_info_ptr = (struct VelSel_info *) *handle; + my_last_rply = (struct RS__RplyStruct *) last_rply; + + if (my_last_rply == NULL) { /* Start with first reply? */ + /* Yes */ + if (sscanf (my_info_ptr->from_host.n_rply, "%4d", + &my_info_ptr->max_replies) != 1) my_info_ptr->max_replies = 0; + if (my_info_ptr->max_replies > 0) + ptr = (struct RS__RplyStruct *) my_info_ptr->from_host.u.rplys; + my_info_ptr->n_replies = 1; + }else { + my_info_ptr->n_replies++; + if (my_info_ptr->n_replies <= my_info_ptr->max_replies) { + if (sscanf (my_last_rply->rply_len, "%2d", &rply_len) == 1) { + ptr = + (struct RS__RplyStruct *) ((char *) my_last_rply + rply_len + 2); + } + } + } + return (void *) ptr; + } +/* +**--------------------------------------------------------------------------- +** VelSel_GetStatus: Get "???" response from Vel Selector +*/ + int VelSel_GetStatus ( +/* =============== +*/ void **handle, + char *status_str, + int status_str_len) { + + int status; + struct VelSel_info *info_ptr; + struct RS__RplyStruct *rply_ptr; + struct RS__RplyStruct *rply_ptr0; + /*---------------------------------------------- + */ + status_str[0] = '\0'; + /*---------------------------------------------- + ** Pre-set the routine name (in case of error) + */ + if (VelSel_errcode == 0 && VelSel_call_depth < 5) { + strcpy (VelSel_routine[VelSel_call_depth], "VelSel_GetStatus"); + VelSel_call_depth++; + } + /*---------------------------------------------- + ** Do nothing if no connection - the connection gets + ** closed if an error is detected. + */ + info_ptr = (struct VelSel_info *) *handle; + if (info_ptr == NULL) { + return False; + } + if (info_ptr->asyn_info.skt <= 0) { + memset (info_ptr->from_host.msg_size, + '0', sizeof (info_ptr->from_host.msg_size)); + if ((VelSel_errcode == 0) && (info_ptr->asyn_info.skt < 0)) { + VelSel_errcode = VELSEL__FORCED_CLOSED; + } + return False; + } + /*---------------------------------------------- + ** Send "???" command to Velocity Selector + */ + status = VelSel_SendCmnds (handle, "???", NULL); + if (!status) { + /* Error in VelSel_SendCmnds */ + return False; + }else { + rply_ptr0 = VelSel_GetReply (handle, NULL); + if (rply_ptr0 == NULL) rply_ptr0 = (struct RS__RplyStruct *) "06\rNULL"; + StrJoin (status_str, status_str_len, rply_ptr0->rply, ""); + } + VelSel_call_depth--; + return True; + } +/* +**--------------------------------------------------------------------------- +** VelSel_Open: Open a connection to a Velocity Selector. +*/ + int VelSel_Open ( +/* ========== +*/ void **handle, + char *host, + int port, + int chan) { + + int status; + struct VelSel_info *my_handle; + struct RS__RplyStruct *rply_ptr; + struct RS__RplyStruct *rply_ptr0; + struct RS__RplyStruct *rply_ptr1; + struct RS__RplyStruct *rply_ptr2; + struct RS__RplyStruct *rply_ptr3; +/*-------------------------------------------------------- +** Initialise the error info stack and pre-set the +** routine name (in case of error). +*/ + VelSel_errcode = VelSel_errno = VelSel_vaxc_errno = 0; + strcpy (VelSel_routine[0], "VelSel_Open"); + VelSel_call_depth = 1; +/*-------------------------------------------------------- +** Assume trouble +*/ + *handle = NULL; +/*-------------------------------------------------------- +** Reserve space for the data we need to store. +*/ + my_handle = (struct VelSel_info *) malloc (sizeof (*my_handle)); + if (my_handle == NULL) { + VelSel_errcode = VELSEL__BAD_MALLOC; /* malloc failed!! */ + return False; + } +/*-------------------------------------------------------- +** Set up the connection +*/ + StrJoin (my_handle->asyn_info.host, sizeof (my_handle->asyn_info.host), + host, ""); + my_handle->asyn_info.port = port; + my_handle->asyn_info.chan = chan; + status = AsynSrv_Open (&my_handle->asyn_info); + if (!status) { + VelSel_errcode = VELSEL__BAD_SOCKET; + GetErrno (&VelSel_errno, &VelSel_vaxc_errno); /* Save errno info */ + fprintf (stderr, "\nVelSel_Open/AsynSrv_Open: " + "Failed to make connection.\n"); + free (my_handle); + return False; + } + + my_handle->tmo = 25; /* Set a short time-out initially since + ** there should be no reason for the REM + ** command to take very long + */ + strcpy (my_handle->eot, "1\n\0\0"); + my_handle->msg_id = 0; + /* + ** Now ensure the VelSel is on-line. The first "REM" command can + ** fail due to pending characters in the VelSel input buffer causing + ** the "REM" to be corrupted. The response of the VelSel to this + ** command is ignored for this reason (but the VelSel_SendCmnds + ** status must be OK otherwise it indicates a network problem). + */ + status = VelSel_SendCmnds ((void *) &my_handle, "REM", NULL); + if (status) { + status = VelSel_SendCmnds ((void *) &my_handle, "REM", NULL); + } + if (!status) { + /* Some error occurred in VelSel_SendCmnds - Errcode will + ** have been set up there. + */ + AsynSrv_Close (&my_handle->asyn_info, False); + free (my_handle); + return False; + } + /* + ** Check the responses carefully. + */ + rply_ptr0 = VelSel_GetReply ((void *) &my_handle, NULL); + + if (rply_ptr0 == NULL) rply_ptr0 = (struct RS__RplyStruct *) "06\rNULL"; + if (rply_ptr0->rply[0] == '?') { + VelSel_errcode = VELSEL__BAD_DEV; /* Error response - not a VelSel? */ + AsynSrv_Close (&my_handle->asyn_info, False); + free (my_handle); + return False; + } + /* + ** The connection is complete. Pass the data structure + ** back to the caller as a handle. + */ + my_handle->tmo = 100; /* Default time-out is 10 secs */ + *handle = my_handle; + VelSel_call_depth--; + return True; + } +/* +**--------------------------------------------------------------------------- +** VelSel_SendCmnds - Send commands to RS232C server. +*/ + int VelSel_SendCmnds ( +/* ================ +*/ void **handle, + ...) { /* Now we have list of commands - + ** char *txt = pntr to cmnd strng + ** Terminate list with *txt = NULL. + */ + struct VelSel_info *info_ptr; + int i, status, c_len, size, max_size, ncmnds; + int bytes_to_come, bytes_left; + char *nxt_byte_ptr; + char err_text[80]; + char text[20]; + va_list ap; /* Pointer to variable args */ + char *txt_ptr; + char *cmnd_lst_ptr; + /*---------------------------------------------- + ** Pre-set the routine name (in case of error) + */ + if (VelSel_errcode == 0 && VelSel_call_depth < 5) { + strcpy (VelSel_routine[VelSel_call_depth], "VelSel_SendCmnds"); + VelSel_call_depth++; + } + /*---------------------------------------------- + ** Do nothing if no connection - the connection gets + ** closed if an error is detected. The connection may + ** also be marked to have been forcefully closed. + */ + info_ptr = (struct VelSel_info *) *handle; + if (info_ptr == NULL) { + return False; + } + if (info_ptr->asyn_info.skt <= 0) { + memset (info_ptr->from_host.msg_size, + '0', sizeof (info_ptr->from_host.msg_size)); + if ((VelSel_errcode == 0) && (info_ptr->asyn_info.skt < 0)) { + VelSel_errcode = VELSEL__FORCED_CLOSED; + } + return False; + } + /*---------------------------------------------- + ** Build message for Vel Selector from the list of commands. + */ + info_ptr->n_replies = info_ptr->max_replies = 0; + + info_ptr->msg_id++; /* Set up an incrementing message id */ + if (info_ptr->msg_id > 9999) info_ptr->msg_id = 1; + sprintf (info_ptr->to_host.msg_id, "%04.4d", info_ptr->msg_id); + + memcpy (info_ptr->to_host.c_pcol_lvl, RS__PROTOCOL_ID, + sizeof (info_ptr->to_host.c_pcol_lvl)); + sprintf (info_ptr->to_host.serial_port, "%04.4d", info_ptr->asyn_info.chan); + sprintf (info_ptr->to_host.tmo, "%04.4d", info_ptr->tmo); + + memcpy (info_ptr->to_host.terms, info_ptr->eot, + sizeof (info_ptr->to_host.terms)); + memcpy (info_ptr->to_host.n_cmnds, "0000", + sizeof (info_ptr->to_host.n_cmnds)); + + va_start (ap, handle); /* Set up var arg machinery */ + + txt_ptr = va_arg (ap, char *); /* Get pntr to next cmnd string */ + ncmnds = 0; + cmnd_lst_ptr = &info_ptr->to_host.cmnds[0]; + bytes_left = sizeof (info_ptr->to_host) - + OffsetOf (struct RS__MsgStruct, cmnds[0]); + + while (txt_ptr != NULL) { + size = 2 + strlen (txt_ptr); + if (size > bytes_left) { + VelSel_errcode = VELSEL__BAD_SENDLEN; /* Too much to send */ + fprintf (stderr, "\nVelSel_SendCmnds/send: too much to send" + " - request ignored.\n"); + memset (info_ptr->from_host.msg_size, + '0', sizeof (info_ptr->from_host.msg_size)); + return False; + }else { + strcpy (cmnd_lst_ptr+2, txt_ptr); + c_len = strlen (txt_ptr); + sprintf (text, "%02.2d", c_len); + memcpy (cmnd_lst_ptr, text, 2); + cmnd_lst_ptr = cmnd_lst_ptr + c_len + 2; + ncmnds++; + bytes_left = bytes_left - size; + txt_ptr = va_arg (ap, char *); + } + } + sprintf (text, "%04.4d", ncmnds); + memcpy (info_ptr->to_host.n_cmnds, + text, sizeof (info_ptr->to_host.n_cmnds)); + + size = cmnd_lst_ptr - info_ptr->to_host.msg_id; + size = (size + 3) & (~3); /* Round up to multiple of 4 */ + sprintf (text, "%04.4d", size); + memcpy (info_ptr->to_host.msg_size, text, 4); + + status = send (info_ptr->asyn_info.skt, + (char *) &info_ptr->to_host, size+4, 0); + if (status != (size+4)) { + GetErrno (&VelSel_errno, &VelSel_vaxc_errno); + if (status == 0) { + VelSel_errcode = VELSEL__BAD_SEND; /* Server exited (probably) */ + fprintf (stderr, "\nVelSel_SendCmnds/send: probable network problem"); + }else if (status == -1) { + if (VelSel_errno == EPIPE) { + VelSel_errcode = VELSEL__BAD_SEND_PIPE; /* Server exited (probably) */ + fprintf (stderr, "\nVelSel_SendCmnds/send: broken network pipe"); + }else { + VelSel_errcode = VELSEL__BAD_SEND_NET; /* It's some other net problem */ + perror ("VelSel_SendCmnds/send"); + } + }else { + VelSel_errcode = VELSEL__BAD_SEND_UNKN; /* TCP/IP problems */ + fprintf (stderr, "\nVelSel_SendCmnds/send: probable TCP/IP problem"); + } + VelSel_Close (handle, True); /* Force close TCP/IP connection */ + fprintf (stderr, " - link to server force-closed.\n"); + return False; + } + + size = sizeof (info_ptr->from_host.msg_size); + status = recv (info_ptr->asyn_info.skt, + info_ptr->from_host.msg_size, size, 0); + if (status != size) { + GetErrno (&VelSel_errno, &VelSel_vaxc_errno); + if (status == 0) { + VelSel_errcode = VELSEL__BAD_RECV; /* Server exited (probably) */ + fprintf (stderr, "\nVelSel_SendCmnds/recv: probable network problem"); + }else if (status == -1) { + if (VelSel_errno == EPIPE) { + VelSel_errcode = VELSEL__BAD_RECV_PIPE; /* Server exited (probably) */ + fprintf (stderr, "\nVelSel_SendCmnds/recv: broken network pipe"); + }else { + VelSel_errcode = VELSEL__BAD_RECV_NET; /* It's some other net problem */ + perror ("VelSel_SendCmnds/recv"); + } + }else { + VelSel_errcode = VELSEL__BAD_RECV_UNKN; /* TCP/IP problems */ + fprintf (stderr, "\nVelSel_SendCmnds/recv: probable TCP/IP problem"); + } + VelSel_Close (handle, True); /* Force close TCP/IP connection */ + fprintf (stderr, " - link to server force-closed.\n"); + return False; + } + if (sscanf (info_ptr->from_host.msg_size, "%4d", &bytes_to_come) != 1) { + VelSel_errcode = VELSEL__BAD_NOT_BCD; /* Header not an ASCII BCD integer */ + VelSel_Close (handle, True); /* Force close TCP/IP connection */ + fprintf (stderr, "\nVelSel_SendCmnds/recv: non-BCD byte count" + " - link to server force-closed.\n"); + return False; + } + max_size = sizeof (info_ptr->from_host) - + sizeof (info_ptr->from_host.msg_size); + if (bytes_to_come > max_size) { + VelSel_errcode = VELSEL__BAD_RECVLEN; + fprintf (stderr, "\nVelSel_SendCmnds/recv: pending message length too big" + " - flushing ...\n"); + nxt_byte_ptr = &info_ptr->from_host.msg_size[size]; + while (bytes_to_come > 0) { /* Flush out the incoming message */ + bytes_left = bytes_to_come; + if (bytes_left > max_size) bytes_left = max_size; + status = recv (info_ptr->asyn_info.skt, nxt_byte_ptr, bytes_left, 0); + if (status <= 0) { + VelSel_errcode = VELSEL__BAD_FLUSH; /* TCP/IP problem whilst flushing */ + GetErrno (&VelSel_errno, &VelSel_vaxc_errno); + VelSel_Close (handle, True); /* Force close TCP/IP connection */ + fprintf (stderr, "\nVelSel_SendCmnds/recv: network problem during" + " flush.\nLink to server force-closed.\n"); + return False; + } + bytes_to_come = bytes_to_come - status; + } + fprintf (stderr, "\n flushed OK.\n"); + memset (info_ptr->from_host.msg_size, + '0', sizeof (info_ptr->from_host.msg_size)); + return False; + }else { + nxt_byte_ptr = &info_ptr->from_host.msg_size[size]; + bytes_left = bytes_to_come; + while (bytes_left > 0) { /* Read the rest of the response */ + status = recv (info_ptr->asyn_info.skt, nxt_byte_ptr, bytes_left, 0); + if (status <= 0) { + GetErrno (&VelSel_errno, &VelSel_vaxc_errno); + if (status == 0) { + VelSel_errcode = VELSEL__BAD_RECV1; /* Server exited (probably) */ + fprintf (stderr, "\nVelSel_SendCmnds/recv/1: probable network " + "problem"); + }else { + if (VelSel_errno == EPIPE) { + VelSel_errcode = VELSEL__BAD_RECV1_PIPE; /* Server exited (probably) */ + fprintf (stderr, "\nVelSel_SendCmnds/recv/1: broken network pipe"); + }else { + VelSel_errcode = VELSEL__BAD_RECV1_NET; /* It's some other net fault */ + perror ("VelSel_SendCmnds/recv/1"); + } + } + VelSel_Close (handle, True); /* Force close TCP/IP connection */ + fprintf (stderr, " - link to server force-closed.\n"); + return False; + } + bytes_left = bytes_left - status; + nxt_byte_ptr = nxt_byte_ptr + status; + } + if ((sscanf (info_ptr->from_host.n_rply, "%4d", + &info_ptr->max_replies) != 1) || + (info_ptr->max_replies < 0)) { + VelSel_errcode = VELSEL__BAD_REPLY; /* Reply is bad */ + if (VelSel_call_depth < 5) { /* Add reply to routine stack */ + bytes_to_come = bytes_to_come + 4; + if (bytes_to_come >= sizeof (VelSel_routine[0])) + bytes_to_come = sizeof (VelSel_routine[0]) - 1; + for (i=0; ifrom_host.msg_size[i] == '\0') + info_ptr->from_host.msg_size[i] = '.'; + } + info_ptr->from_host.msg_size[bytes_to_come] = '\0'; + strcpy (VelSel_routine[VelSel_call_depth], + info_ptr->from_host.msg_size); + VelSel_call_depth++; + } + return False; + } + } + VelSel_call_depth--; + return True; + } +/*-------------------------------------------- End of VelSel_Utility.C =======*/ diff --git a/itc4.c b/itc4.c new file mode 100644 index 0000000..ae983c1 --- /dev/null +++ b/itc4.c @@ -0,0 +1,281 @@ +/*--------------------------------------------------------------------------- + I T C 4 + + This is the implementation for a ITC4 object derived from an more general + environment controller. + + Mark Koennecke, August 1997 + + Copyright: + + Labor fuer Neutronenstreuung + Paul Scherrer Institut + CH-5423 Villigen-PSI + + + The authors hereby grant permission to use, copy, modify, distribute, + and license this software and its documentation for any purpose, provided + that existing copyright notices are retained in all copies and that this + notice is included verbatim in any distributions. No written agreement, + license, or royalty fee is required for any of the authorized uses. + Modifications to this software may be copyrighted by their authors + and need not follow the licensing terms described here, provided that + the new terms are clearly indicated on the first page of each file where + they apply. + + IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY + FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY + DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE + IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE + NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR + MODIFICATIONS. +----------------------------------------------------------------------------*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "itc4.h" + +/*---------------------------------------------------------------------------*/ + int ITC4SetPar(pEVControl self, char *name, float fNew, SConnection *pCon) + { + int iRet; + + /* check authorsisation */ + if(!SCMatchRights(pCon,usUser)) + { + SCWrite(pCon,"ERROR: you are not authorised to change this parameter", + eError); + return 0; + } + + /* just catch those three names which we understand */ + if(strcmp(name,"sensor") == 0) + { + iRet = SetSensorITC4(self->pDriv,(int)fNew); + if(!iRet) + { + SCWrite(pCon,"ERROR: value out of range",eError); + return 0; + } + iRet = ConfigITC4(self->pDriv); + if(iRet != 1) + { + SCWrite(pCon,"ERROR: ITC4 configuration failed! ",eError); + SCWrite(pCon,"INFO: Probably comm problem, Retry!",eError); + return 0; + } + SCSendOK(pCon); + return 1; + } + else if(strcmp(name,"control") == 0) + { + iRet = SetControlITC4(self->pDriv,(int)fNew); + if(!iRet) + { + SCWrite(pCon,"ERROR: value out of range",eError); + return 0; + } + iRet = ConfigITC4(self->pDriv); + if(iRet != 1) + { + SCWrite(pCon,"ERROR: ITC4 configuration failed! ",eError); + SCWrite(pCon,"INFO: Probably comm problem, Retry!",eError); + return 0; + } + SCSendOK(pCon); + return 1; + } + else if(strcmp(name,"timeout") == 0) + { + iRet = SetTMOITC4(self->pDriv,(int)fNew); + if(!iRet) + { + SCWrite(pCon,"ERROR: value out of range",eError); + return 0; + } + iRet = ConfigITC4(self->pDriv); + if(iRet != 1) + { + SCWrite(pCon,"ERROR: ITC4 configuration failed! ",eError); + SCWrite(pCon,"INFO: Probably comm problem, Retry!",eError); + return 0; + } + SCSendOK(pCon); + return 1; + } + else if(strcmp(name,"divisor") == 0) + { + iRet = SetDivisorITC4(self->pDriv,fNew); + if(!iRet) + { + SCWrite(pCon,"ERROR: value out of range",eError); + return 0; + } + iRet = ConfigITC4(self->pDriv); + if(iRet != 1) + { + SCWrite(pCon,"ERROR: ITC4 configuration failed! ",eError); + SCWrite(pCon,"INFO: Probably comm problem, Retry!",eError); + return 0; + } + SCSendOK(pCon); + return 1; + } + else if(strcmp(name,"multiplicator") == 0) + { + iRet = SetMultITC4(self->pDriv,fNew); + if(!iRet) + { + SCWrite(pCon,"ERROR: value out of range",eError); + return 0; + } + iRet = ConfigITC4(self->pDriv); + if(iRet != 1) + { + SCWrite(pCon,"ERROR: ITC4 configuration failed! ",eError); + SCWrite(pCon,"INFO: Probably comm problem, Retry!",eError); + return 0; + } + SCSendOK(pCon); + return 1; + } + else + return EVCSetPar(self,name,fNew,pCon); + } +/*--------------------------------------------------------------------------*/ + int ITC4GetPar(pEVControl self, char *name, float *fNew) + { + int iRet; + float fDiv; + + /* just catch those two names which we understand */ + if(strcmp(name,"sensor") == 0) + { + iRet = GetSensorITC4(self->pDriv); + *fNew = (float)iRet; + return 1; + } + else if(strcmp(name,"control") == 0) + { + iRet = GetControlITC4(self->pDriv); + *fNew = (float)iRet; + return 1; + } + else if(strcmp(name,"timeout") == 0) + { + iRet = GetTMOITC4(self->pDriv); + *fNew = (float)iRet; + return 1; + } + else if(strcmp(name,"divisor") == 0) + { + fDiv = GetDivisorITC4(self->pDriv); + *fNew = fDiv; + return 1; + } + else if(strcmp(name,"multiplicator") == 0) + { + fDiv = GetMultITC4(self->pDriv); + *fNew = fDiv; + return 1; + } + else + return EVCGetPar(self,name,fNew); + } +/*---------------------------------------------------------------------------*/ + int ITCList(pEVControl self, SConnection *pCon) + { + char pBueffel[132]; + int iRet; + + iRet = EVCList(self,pCon); + sprintf(pBueffel,"%s.sensor = %d\n",self->pName, + GetSensorITC4(self->pDriv)); + SCWrite(pCon,pBueffel,eValue); + sprintf(pBueffel,"%s.control = %d\n",self->pName, + GetControlITC4(self->pDriv)); + SCWrite(pCon,pBueffel,eValue); + sprintf(pBueffel,"%s.timeout = %d\n",self->pName, + GetTMOITC4(self->pDriv)); + SCWrite(pCon,pBueffel,eValue); + sprintf(pBueffel,"%s.divisor = %f\n",self->pName, + GetDivisorITC4(self->pDriv)); + SCWrite(pCon,pBueffel,eValue); + sprintf(pBueffel,"%s.multiplicator = %f\n",self->pName, + GetMultITC4(self->pDriv)); + SCWrite(pCon,pBueffel,eValue); + return iRet; + } +/*-------------------------------------------------------------------------*/ + int ITC4Wrapper(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]) + { + pEVControl self = NULL; + char pBueffel[256]; + int iRet; + double fNum; + float fVal; + + self = (pEVControl)pData; + assert(self); + assert(pCon); + assert(pSics); + + if(argc < 2) + { + return EVControlWrapper(pCon,pSics,pData,argc,argv); + } + + strtolower(argv[1]); + if((strcmp(argv[1],"sensor") == 0) || (strcmp(argv[1],"control") == 0) || + (strcmp(argv[1],"timeout") == 0) || (strcmp(argv[1],"divisor") == 0) || + (strcmp(argv[1],"multiplicator") == 0) ) + { + if(argc > 2) /* set case */ + { + iRet = Tcl_GetDouble(pSics->pTcl,argv[2],&fNum); + if(iRet != TCL_OK) + { + sprintf(pBueffel,"ERROR: expected number, got %s",argv[2]); + SCWrite(pCon,pBueffel,eError); + return 0; + } + return ITC4SetPar(self,argv[1],(float)fNum,pCon); + } + else /* get case */ + { + iRet = ITC4GetPar(self,argv[1],&fVal); + sprintf(pBueffel,"%s.%s = %f\n",self->pName, + argv[1],fVal); + SCWrite(pCon,pBueffel,eValue); + return 1; + } + } + else if(strcmp(argv[1],"list") == 0) + { + return ITCList(self,pCon); + } + else + { + return EVControlWrapper(pCon,pSics,pData,argc,argv); + } + /* not reached */ + return 0; + } diff --git a/itc4.h b/itc4.h new file mode 100644 index 0000000..1068ef3 --- /dev/null +++ b/itc4.h @@ -0,0 +1,43 @@ + +/*------------------------------------------------------------------------- + ITC 4 + + Support for Oxford Instruments ITC4 Temperature controllers for SICS. + The meaning and working of the functions defined is as desribed for a + general environment controller. + + Mark Koennecke, Juli 1997 + + copyright: see implementation file. + +-----------------------------------------------------------------------------*/ +#ifndef SICSITC4 +#define SICSITC4 +/*------------------------- The Driver ------------------------------------*/ + + pEVDriver CreateITC4Driver(int argc, char *argv[]); + int ConfigITC4(pEVDriver self); + int SetSensorITC4(pEVDriver self, int iSensor); + int SetControlITC4(pEVDriver self, int iSensor); + int GetSensorITC4(pEVDriver self); + int GetControlITC4(pEVDriver self); + int SetDivisorITC4(pEVDriver self, float iSensor); + float GetDivisorITC4(pEVDriver self); + int SetMultITC4(pEVDriver self, float iSensor); + float GetMultITC4(pEVDriver self); + int SetTMOITC4(pEVDriver self, int iSensor); + int GetTMOITC4(pEVDriver self); + + +/*------------------------- The ITC4 object ------------------------------*/ + + int ITC4Wrapper(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]); + int ITC4SetPar(pEVControl self, char *name, float fNew, + SConnection *pCon); + int ITC4GetPar(pEVControl self, char *name, float *fVal); + int ITCList(pEVControl self, SConnection *pCon); + + +#endif + diff --git a/itc4.w b/itc4.w new file mode 100644 index 0000000..98d83f9 --- /dev/null +++ b/itc4.w @@ -0,0 +1,74 @@ +\subsubsection{Oxford Instruments ITC4 Temperature Controllers} +SINQ makes heavy use of Oxford Instruments ITC4 temperature controllers. In +order to support them the following software components had to be defined in +addition to the basic environmet controller interfaces: +\begin{itemize} +\item ITC4driver, naturally. +\item A ITC4-controller object as derivation of environment controller. ITC4 +'s allow you to select a sensor which you read as your standard sensor and a +sensor which is used for automatic control. The ITC4 controller object adds +just that additional functionality to the statndard environment controller. +\end{itemize} +The additional data, the selection of sensors, will be kept in the driver. +This serves also an example for implementing inheritance without C++. + +The driver interface: +@d itcd @{ + pEVDriver CreateITC4Driver(int argc, char *argv[]); + int ConfigITC4(pEVDriver self); + int SetSensorITC4(pEVDriver self, int iSensor); + int SetControlITC4(pEVDriver self, int iSensor); + int GetSensorITC4(pEVDriver self); + int GetControlITC4(pEVDriver self); + int SetDivisorITC4(pEVDriver self, float iSensor); + float GetDivisorITC4(pEVDriver self); + int SetMultITC4(pEVDriver self, float iSensor); + float GetMultITC4(pEVDriver self); + int SetTMOITC4(pEVDriver self, int iSensor); + int GetTMOITC4(pEVDriver self); + +@} + +The ConfigITC4 is special. It has to be called to commit changes to the +driver read and control parameters. + +The ITC4 object interface: +@d itco @{ + int ITC4Wrapper(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]); + int ITC4SetPar(pEVControl self, char *name, float fNew, + SConnection *pCon); + int ITC4GetPar(pEVControl self, char *name, float *fVal); + int ITCList(pEVControl self, SConnection *pCon); +@} + +The functions defined are: new parameter handling functions, with just +support for the two extra parameters added and a new Wrapper function for +SICS. The meaning of all these functions, their parameters and return values +are identical to those defined for an environment controller. Additionally, +the standard environment controller functions will work as described. The +functions described above are just needed to implement the extra parameters. + +@o itc4.h @{ +/*------------------------------------------------------------------------- + ITC 4 + + Support for Oxford Instruments ITC4 Temperature controllers for SICS. + The meaning and working of the functions defined is as desribed for a + general environment controller. + + Mark Koennecke, Juli 1997 + + copyright: see implementation file. + +-----------------------------------------------------------------------------*/ +#ifndef SICSITC4 +#define SICSITC4 +/*------------------------- The Driver ------------------------------------*/ +@ +/*------------------------- The ITC4 object ------------------------------*/ +@ + +#endif + +@} diff --git a/itc4driv.c b/itc4driv.c new file mode 100644 index 0000000..fedf505 --- /dev/null +++ b/itc4driv.c @@ -0,0 +1,470 @@ +/*-------------------------------------------------------------------------- + I T C 4 D R I V + + This file contains the implementation of a driver for the Oxford + Instruments ITC4 Temperature controller. + + + Mark Koennecke, Juli 1997 + + Copyright: + + Labor fuer Neutronenstreuung + Paul Scherrer Institut + CH-5423 Villigen-PSI + + + The authors hereby grant permission to use, copy, modify, distribute, + and license this software and its documentation for any purpose, provided + that existing copyright notices are retained in all copies and that this + notice is included verbatim in any distributions. No written agreement, + license, or royalty fee is required for any of the authorized uses. + Modifications to this software may be copyrighted by their authors + and need not follow the licensing terms described here, provided that + the new terms are clearly indicated on the first page of each file where + they apply. + + IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY + FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY + DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE + IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE + NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR + MODIFICATIONS. +----------------------------------------------------------------------------*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include + + typedef struct __EVDriver *pEVDriver; + +#include +#include "hardsup/itc4util.h" +#include "hardsup/el734_def.h" +#include "hardsup/el734fix.h" +#define SHITTYVALUE -777 +/*------------------------- The Driver ------------------------------------*/ + + pEVDriver CreateITC4Driver(int argc, char *argv[]); + int ConfigITC4(pEVDriver self); + + +/*-----------------------------------------------------------------------*/ + typedef struct { + pITC4 pData; + char *pHost; + int iPort; + int iChannel; + int iControl; + float fDiv; + float fMult; + int iRead; + int iTmo; + int iLastError; + } ITC4Driv, *pITC4Driv; +/*----------------------------------------------------------------------------*/ + static int GetITC4Pos(pEVDriver self, float *fPos) + { + pITC4Driv pMe = NULL; + int iRet; + + assert(self); + pMe = (pITC4Driv)self->pPrivate; + assert(pMe); + + iRet = ITC4_Read(&pMe->pData,fPos); + if(iRet != 1 ) + { + pMe->iLastError = iRet; + return 0; + } + if( (*fPos < 0) || (*fPos > 10000) ) + { + *fPos = -999.; + pMe->iLastError = SHITTYVALUE; + return 0; + } + return 1; + } +/*----------------------------------------------------------------------------*/ + static int ITC4Run(pEVDriver self, float fVal) + { + pITC4Driv pMe = NULL; + int iRet; + + assert(self); + pMe = (pITC4Driv )self->pPrivate; + assert(pMe); + + iRet = ITC4_Set(&pMe->pData,fVal); + if(iRet != 1) + { + pMe->iLastError = iRet; + return 0; + } + return 1; + } +/*--------------------------------------------------------------------------*/ + static int ITC4Error(pEVDriver self, int *iCode, char *error, int iErrLen) + { + pITC4Driv pMe = NULL; + + assert(self); + pMe = (pITC4Driv)self->pPrivate; + assert(pMe); + + *iCode = pMe->iLastError; + if(pMe->iLastError == SHITTYVALUE) + { + strncpy(error,"Invalid temperature returned form ITC4, check sensor",iErrLen); + } + else + { + ITC4_ErrorTxt(&pMe->pData,pMe->iLastError,error,iErrLen); + } + return 1; + } +/*--------------------------------------------------------------------------*/ + static int ITC4Send(pEVDriver self, char *pCommand, char *pReply, int iLen) + { + pITC4Driv pMe = NULL; + int iRet; + + assert(self); + pMe = (pITC4Driv )self->pPrivate; + assert(pMe); + + iRet = ITC4_Send(&pMe->pData,pCommand, pReply,iLen); + if(iRet != 1) + { + pMe->iLastError = iRet; + return 0; + } + return 1; + + } +/*--------------------------------------------------------------------------*/ + static int ITC4Init(pEVDriver self) + { + pITC4Driv pMe = NULL; + int iRet; + + assert(self); + pMe = (pITC4Driv )self->pPrivate; + assert(pMe); + + pMe->pData = NULL; + iRet = ITC4_Open(&pMe->pData, pMe->pHost, pMe->iPort, pMe->iChannel,0); + if(iRet != 1) + { + if(iRet == ITC4__NOITC) + { + return -1; + } + else + { + pMe->iLastError = iRet; + return 0; + } + } + return 1; + } +/*--------------------------------------------------------------------------*/ + static int ITC4Close(pEVDriver self) + { + pITC4Driv pMe = NULL; + int iRet; + + assert(self); + pMe = (pITC4Driv )self->pPrivate; + assert(pMe); + + ITC4_Close(&pMe->pData); + return 1; + } +/*---------------------------------------------------------------------------*/ + static int ITC4Fix(pEVDriver self, int iError) + { + pITC4Driv pMe = NULL; + int iRet; + + assert(self); + pMe = (pITC4Driv )self->pPrivate; + assert(pMe); + + switch(iError) + { + /* network errors */ + case EL734__BAD_FLUSH: + case EL734__BAD_RECV: + case EL734__BAD_RECV_NET: + case EL734__BAD_RECV_UNKN: + case EL734__BAD_RECVLEN: + case EL734__BAD_RECV1: + case EL734__BAD_RECV1_PIPE: + case EL734__BAD_RNG: + case EL734__BAD_SEND: + case EL734__BAD_SEND_PIPE: + case EL734__BAD_SEND_NET: + case EL734__BAD_SEND_UNKN: + case EL734__BAD_SENDLEN: + ITC4Close(self); + iRet = ITC4Init(self); + if(iRet) + { + return DEVREDO; + } + else + { + return DEVFAULT; + } + break; + /* handable protocoll errors */ + case EL734__BAD_TMO: + return DEVREDO; + break; + case -501: /* Bad_COM */ + return DEVREDO; + case -504: /* Badly formatted */ + return DEVREDO; + default: + return DEVFAULT; + break; + } + return DEVFAULT; + } + +/*--------------------------------------------------------------------------*/ + static int ITC4Halt(pEVDriver *self) + { + assert(self); + + return 1; + } +/*------------------------------------------------------------------------*/ + void KillITC4(void *pData) + { + pITC4Driv pMe = NULL; + + pMe = (pITC4Driv)pData; + assert(pMe); + + if(pMe->pHost) + { + free(pMe->pHost); + } + free(pMe); + } +/*------------------------------------------------------------------------*/ + pEVDriver CreateITC4Driver(int argc, char *argv[]) + { + pEVDriver pNew = NULL; + pITC4Driv pSim = NULL; + + /* check for arguments */ + if(argc < 3) + { + return NULL; + } + + pNew = CreateEVDriver(argc,argv); + pSim = (pITC4Driv)malloc(sizeof(ITC4Driv)); + memset(pSim,0,sizeof(ITC4Driv)); + if(!pNew || !pSim) + { + return NULL; + } + pNew->pPrivate = pSim; + pNew->KillPrivate = KillITC4; + + /* initalise pITC4Driver */ + pSim->iControl = 1; + pSim->iRead = 1; + pSim->iLastError = 0; + pSim->iTmo = 10; + pSim->fDiv = 10.; + pSim->fMult = 10; + pSim->pHost = strdup(argv[0]); + pSim->iPort = atoi(argv[1]); + pSim->iChannel = atoi(argv[2]); + + + /* initialise function pointers */ + pNew->SetValue = ITC4Run; + pNew->GetValue = GetITC4Pos; + pNew->Send = ITC4Send; + pNew->GetError = ITC4Error; + pNew->TryFixIt = ITC4Fix; + pNew->Init = ITC4Init; + pNew->Close = ITC4Close; + + return pNew; + } +/*--------------------------------------------------------------------------*/ + int ConfigITC4(pEVDriver self) + { + pITC4Driv pMe = NULL; + int iRet; + + assert(self); + pMe = (pITC4Driv )self->pPrivate; + assert(pMe); + + iRet = ITC4_Config(&pMe->pData, pMe->iTmo, pMe->iRead, + pMe->iControl,pMe->fDiv,pMe->fMult); + if(iRet < 0) + { + pMe->iLastError = iRet; + return 0; + } + return 1; + } +/*-------------------------------------------------------------------------*/ + int SetSensorITC4(pEVDriver self, int iSensor) + { + pITC4Driv pMe = NULL; + int iRet; + + assert(self); + pMe = (pITC4Driv )self->pPrivate; + assert(pMe); + + if( (iSensor < 1) || (iSensor > 4) ) + { + return 0; + } + pMe->iRead = iSensor; + return 1; + } +/*-------------------------------------------------------------------------*/ + int SetControlITC4(pEVDriver self, int iSensor) + { + pITC4Driv pMe = NULL; + int iRet; + + assert(self); + pMe = (pITC4Driv )self->pPrivate; + assert(pMe); + + if( (iSensor < 1) || (iSensor > 4) ) + { + return 0; + } + pMe->iControl = iSensor; + return 1; + } +/*-------------------------------------------------------------------------*/ + int SetTMOITC4(pEVDriver self, int iSensor) + { + pITC4Driv pMe = NULL; + int iRet; + + assert(self); + pMe = (pITC4Driv )self->pPrivate; + assert(pMe); + + if(iSensor < 10) + { + return 0; + } + pMe->iTmo = iSensor; + return 1; + } +/*-------------------------------------------------------------------------*/ + int GetControlITC4(pEVDriver self) + { + pITC4Driv pMe = NULL; + int iRet; + + assert(self); + pMe = (pITC4Driv )self->pPrivate; + assert(pMe); + + return pMe->iControl; + } +/*-------------------------------------------------------------------------*/ + int GetSensorITC4(pEVDriver self) + { + pITC4Driv pMe = NULL; + int iRet; + + assert(self); + pMe = (pITC4Driv )self->pPrivate; + assert(pMe); + + return pMe->iRead; + } +/*-------------------------------------------------------------------------*/ + int GetTMOITC4(pEVDriver self) + { + pITC4Driv pMe = NULL; + int iRet; + + assert(self); + pMe = (pITC4Driv )self->pPrivate; + assert(pMe); + + return pMe->iTmo; + } +/*-------------------------------------------------------------------------*/ + float GetDivisorITC4(pEVDriver self) + { + pITC4Driv pMe = NULL; + int iRet; + + assert(self); + pMe = (pITC4Driv )self->pPrivate; + assert(pMe); + + return pMe->fDiv; + } +/*--------------------------------------------------------------------------*/ + int SetDivisorITC4(pEVDriver self, float fDiv) + { + pITC4Driv pMe = NULL; + int iRet; + + assert(self); + pMe = (pITC4Driv )self->pPrivate; + assert(pMe); + + pMe->fDiv = fDiv; + return 1; + } +/*-------------------------------------------------------------------------*/ + float GetMultITC4(pEVDriver self) + { + pITC4Driv pMe = NULL; + int iRet; + + assert(self); + pMe = (pITC4Driv )self->pPrivate; + assert(pMe); + + return pMe->fMult; + } +/*--------------------------------------------------------------------------*/ + int SetMultITC4(pEVDriver self, float fDiv) + { + pITC4Driv pMe = NULL; + int iRet; + + assert(self); + pMe = (pITC4Driv )self->pPrivate; + assert(pMe); + + pMe->fMult = fDiv; + return 1; + } + diff --git a/ltc11.c b/ltc11.c new file mode 100644 index 0000000..2343f17 --- /dev/null +++ b/ltc11.c @@ -0,0 +1,828 @@ +/*------------------------------------------------------------------------- + L T C 1 1 + an environment control device driver for a Neocera LTC-11 temperature + controller. + + copyright: see copyright.h + + Mark Koennecke, November 1998 +---------------------------------------------------------------------------*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "hardsup/serialsinq.h" +#include "hardsup/el734_errcodes.h" +#include "hardsup/el734fix.h" +#include "ltc11.h" + +/* +#define debug 1 +*/ +/*----------------------------------------------------------------------- + The LTC11 Data Structure +*/ + typedef struct { + void *pData; + char *pHost; + int iPort; + int iChannel; + int iMode; + int iSensor; + int iControlHeat; + int iControlAnalog; + int iLastError; + time_t lastRequest; + float fLast; + } LTC11Driv, *pLTC11Driv; +/*----------------------------------------------------------------------- + A couple of defines for LTC11 modes and special error conditions +*/ +#define ANALOG 2 +#define HEATER 1 +#define MISERABLE 3 + +/* errors */ +#define BADSTATE -920 +#define NOCONN -921 +#define BADANSWER -923 +#define BADCONFIRM -924 +/*-----------------------------------------------------------------------*/ + static void LTC11Unlock(pLTC11Driv self) + { + SerialNoReply(&(self->pData),"SLLOCK 0;"); + } + +/*------------------------------------------------------------------------- + The LTC11 can either control a heater or an analog output. It is a common + task to figure out which mode is active. If the value returned from QOUT + is 3, no sensor is defined, if it is 6 it is in monitor mode, in both cases + control is NOT there. +*/ + int LTC11GetMode(pEVDriver pEva, int *iMode) + { + pLTC11Driv self = NULL; + int iRet, iiMode; + char pBueffel[80]; + + self = (pLTC11Driv)pEva->pPrivate; + assert(self); + + if(self->pData == NULL) + { + self->iLastError = NOCONN; + return 0; + } + + /* query the state, it can be in an invalid mode */ + iRet = SerialWriteRead(&(self->pData),"QISTATE?;",pBueffel,79); + LTC11Unlock(self); + if(iRet != 1) + { + self->iLastError = iRet; + return 0; + } + if(strcmp(pBueffel,"?TMO") == 0) + { + self->iLastError = TIMEOUT; + return 0; + } + if(sscanf(pBueffel,"%d",&iiMode) != 1) + { + self->iLastError = EL734__BAD_ILLG; + return 0; + } + if( (iiMode != 1) && (iiMode != 2) ) + { + self->iLastError = BADSTATE; + *iMode = MISERABLE; + return 0; + } + + /* check the sensor in heater mode */ + iRet = SerialWriteRead(&(self->pData),"QOUT?1;",pBueffel,79); + LTC11Unlock(self); + if(iRet != 1) + { + self->iLastError = iRet; + return 0; + } + if(strcmp(pBueffel,"?TMO") == 0) + { + self->iLastError = TIMEOUT; + return 0; + } + if(sscanf(pBueffel,"%d",&iiMode) != 1) + { + self->iLastError = EL734__BAD_ILLG; + return 0; + } + if( (iiMode != 3) && (iiMode != 6 ) ) + { + *iMode = HEATER; + self->iControlHeat = iiMode; + return 1; + } + + /* check the sensor in analog mode */ + iRet = SerialWriteRead(&(self->pData),"QOUT?2;",pBueffel,79); + LTC11Unlock(self); + if(iRet != 1) + { + self->iLastError = iRet; + return 0; + } + if(strcmp(pBueffel,"?TMO") == 0) + { + self->iLastError = TIMEOUT; + return 0; + } + if(sscanf(pBueffel,"%d",&iiMode) != 1) + { + self->iLastError = EL734__BAD_ILLG; + return 0; + } + if( (iiMode != 3) && (iiMode != 6 ) ) + { + *iMode = ANALOG; + self->iControlAnalog = iiMode; + return 1; + } + /* if we are here something is very bad */ + self->iLastError = BADSTATE; + return 0; + } +/*----------------------------------------------------------------------- + iMode below 10 will be interpreted as heater control, above 10 as analog + control. +*/ + int LTC11SetMode(pEVDriver pEva, int iMode) + { + pLTC11Driv self = NULL; + int iRet, iiMode; + char pBueffel[80], pCommand[20]; + + self = (pLTC11Driv)pEva->pPrivate; + assert(self); + + if(self->pData == NULL) + { + self->iLastError = NOCONN; + return 0; + } + + if(iMode < 10) /* heater mode */ + { + sprintf(pCommand,"SHCONT%1.1d;",iMode); + iRet = SerialNoReply(&(self->pData),pCommand); + LTC11Unlock(self); + if(iRet != 1) + { + self->iLastError = iRet; + return 0; + } + return 1; + } + else + { + iMode -= 10; + sprintf(pCommand,"SACONT%1.1d;",iMode); + iRet = SerialNoReply(&(self->pData),pCommand); + LTC11Unlock(self); + if(iRet != 1) + { + self->iLastError = iRet; + return 0; + } + return 1; + } + /* should not get here */ + self->iLastError = BADSTATE; + return 0; + } +/*-------------------------------------------------------------------------*/ + static int LTC11Get(pEVDriver pEva, float *fValue) + { + pLTC11Driv self = NULL; + int iRet; + char pBueffel[80]; + char pCommand[46]; + char c; + float fVal; + + self = (pLTC11Driv)pEva->pPrivate; + assert(self); + + if(self->pData == NULL) + { + self->iLastError = NOCONN; + return 0; + } + + if(time(NULL) < self->lastRequest) + { + *fValue = self->fLast; + return 1; + } + else + { + self->lastRequest = time(NULL) + 5; /* buffer 5 seconds */ + } + sprintf(pCommand,"QSAMP?%1.1d;",self->iSensor); + iRet = SerialWriteRead(&(self->pData),pCommand,pBueffel,79); + LTC11Unlock(self); + if(iRet != 1) + { + self->iLastError = iRet; + return 0; + } + if(strcmp(pBueffel,"?TMO") == 0) + { + self->iLastError = TIMEOUT; + return 0; + } + iRet = sscanf(pBueffel,"%f%c",fValue,&c); + if(iRet != 2) + { + self->iLastError = BADANSWER; + return 0; + } + if( (c != 'K') && (c != 'C') && (c != 'F') && (c != 'N') + && (c != 'V') && (c != 'O') ) + { + self->iLastError = BADANSWER; + return 0; + } + self->fLast = *fValue; + return 1; + } +/*-------------------------------------------------------------------------*/ + static int LTC11Run(pEVDriver pEva, float fVal) + { + pLTC11Driv self = NULL; + int iRet, iMode; + char pBueffel[80]; + char pCommand[40]; + float fTest = 0.0, fDelta; + + self = (pLTC11Driv)pEva->pPrivate; + assert(self); + + if(self->pData == NULL) + { + self->iLastError = NOCONN; + return 0; + } + + /* find our operation mode */ + iRet = LTC11GetMode(pEva,&iMode); + if( (iRet < 1) || (iMode == MISERABLE) ) + { + return 0; + } + + /* format command */ + sprintf(pCommand,"SETP %d,%f;",iMode, fVal); + + /* send command */ + iRet = SerialNoReply(&(self->pData),pCommand); + if(iRet != 1) + { + self->iLastError = iRet; + LTC11Unlock(self); + return 0; + } + + /* read back */ + sprintf(pCommand,"QSETP?%d;", iMode); + iRet = SerialWriteRead(&(self->pData),pCommand,pBueffel,79); + LTC11Unlock(self); + if(iRet != 1) + { + self->iLastError = iRet; + return 0; + } + + /* check confirmation */ + if(strcmp(pBueffel,"?TMO") == 0) + { + self->iLastError = TIMEOUT; + return 0; + } + sscanf(pBueffel,"%f",&fTest); + fDelta = fVal - fTest; + if(fDelta < 0.0) + fDelta = -fDelta; + + if(fDelta > 0.1) + { + self->iLastError = BADCONFIRM; + return 0; + } + + return 1; + } +/*------------------------------------------------------------------------*/ + static int LTC11Error(pEVDriver pEva, int *iCode, char *pError, + int iErrLen) + { + pLTC11Driv self = NULL; + + self = (pLTC11Driv)pEva->pPrivate; + assert(self); + + *iCode = self->iLastError; + switch(*iCode) + { + case NOCONN: + strncpy(pError,"No Connection to Bruker Controller",iErrLen); + break; + case MISERABLE: + case BADSTATE: + strncpy(pError,"The LTC-11 is in a very bad state",iErrLen); + break; + case BADANSWER: + strncpy(pError,"The LTC-11 returned a bad reply",iErrLen); + break; + case BADCONFIRM: + strncpy(pError,"The LTC-11 did not accept the new set point",iErrLen); + break; + case TIMEOUT: + strncpy(pError,"Timeout receiving data from LTC-11",iErrLen); + break; + default: + SerialError(*iCode,pError,iErrLen); + break; + } + return 1; + } +/*---------------------------------------------------------------------------*/ + static int LTC11Send(pEVDriver pEva, char *pCommand, char *pReply, + int iReplyLen) + { + pLTC11Driv self = NULL; + int iRet; + + self = (pLTC11Driv)pEva->pPrivate; + assert(self); + + if(self->pData == NULL) + { + self->iLastError = NOCONN; + return 0; + } + + + iRet = SerialWriteRead(&(self->pData),pCommand, pReply, iReplyLen); + if(iRet != 1) + { + self->iLastError = iRet; + return 0; + } + return 1; + } +/*--------------------------------------------------------------------------*/ + static int LTC11Init(pEVDriver pEva) + { + pLTC11Driv self = NULL; + int iRet; + char pBueffel[80], pCommand[20]; + + self = (pLTC11Driv)pEva->pPrivate; + assert(self); + + /* open port connection */ + self->pData = NULL; + iRet = SerialOpen(&(self->pData),self->pHost, self->iPort, self->iChannel); + if(iRet != 1) + { + self->iLastError = iRet; + return 0; + } + /* configure serial port terminators */ + SerialSendTerm(&(self->pData),";"); + SerialATerm(&(self->pData),"1;"); + SerialConfig(&(self->pData),30000); + + self->iSensor = 1; + + /* initialize control sensors to unknown, then call GetMode + to get real values + */ + self->iControlHeat = 6; + self->iControlAnalog = 6; + LTC11GetMode(pEva,&iRet); + + return 1; + } +/*-------------------------------------------------------------------------*/ + static int LTC11Close(pEVDriver pEva) + { + pLTC11Driv self = NULL; + + self = (pLTC11Driv)pEva->pPrivate; + assert(self); + + SerialClose(&(self->pData)); + self->pData = 0; + + return 1; + } +/*---------------------------------------------------------------------------*/ + static int LTC11Fix(pEVDriver self, int iError) + { + pLTC11Driv pMe = NULL; + int iRet; + char pCommand[20], pBueffel[80]; + + assert(self); + pMe = (pLTC11Driv )self->pPrivate; + assert(pMe); + + switch(iError) + { + /* network errors */ + case EL734__BAD_FLUSH: + case EL734__BAD_RECV: + case EL734__BAD_RECV_NET: + case EL734__BAD_RECV_UNKN: + case EL734__BAD_RECVLEN: + case EL734__BAD_RECV1: + case EL734__BAD_RECV1_PIPE: + case EL734__BAD_RNG: + case EL734__BAD_SEND: + case EL734__BAD_SEND_PIPE: + case EL734__BAD_SEND_NET: + case EL734__BAD_SEND_UNKN: + case EL734__BAD_SENDLEN: + LTC11Close(self); + iRet = LTC11Init(self); + if(iRet) + { + return DEVREDO; + } + else + { + return DEVFAULT; + } + break; + case EL734__FORCED_CLOSED: + case NOCONN: + iRet = LTC11Init(self); + if(iRet) + { + return DEVREDO; + } + else + { + return DEVFAULT; + } + break; + /* fixable LTC11 Errors */ + case MISERABLE: + case BADSTATE: + iRet = SerialNoReply(&(pMe->pData),"SCONT;"); + LTC11Unlock(pMe); + return DEVREDO; + break; + case BADANSWER: + case BADCONFIRM: + case TIMEOUT: + return DEVREDO; + break; + default: + return DEVFAULT; + break; + } + return DEVFAULT; + } +/*------------------------------------------------------------------------*/ + void KillLTC11(void *pData) + { + pLTC11Driv pMe = NULL; + + pMe = (pLTC11Driv)pData; + assert(pMe); + + if(pMe->pHost) + { + free(pMe->pHost); + } + free(pMe); + } +/*------------------------------------------------------------------------*/ + pEVDriver CreateLTC11Driver(int argc, char *argv[]) + { + pEVDriver pNew = NULL; + pLTC11Driv pSim = NULL; + + /* check for arguments */ + if(argc < 3) + { + return NULL; + } + + pNew = CreateEVDriver(argc,argv); + pSim = (pLTC11Driv)malloc(sizeof(LTC11Driv)); + memset(pSim,0,sizeof(LTC11Driv)); + if(!pNew || !pSim) + { + return NULL; + } + pNew->pPrivate = pSim; + pNew->KillPrivate = KillLTC11; + + /* initalise LTC11Driver */ + pSim->iLastError = 0; + pSim->pHost = strdup(argv[0]); + pSim->iPort = atoi(argv[1]); + pSim->iChannel = atoi(argv[2]); + + + /* initialise function pointers */ + pNew->SetValue = LTC11Run; + pNew->GetValue = LTC11Get; + pNew->Send = LTC11Send; + pNew->GetError = LTC11Error; + pNew->TryFixIt = LTC11Fix; + pNew->Init = LTC11Init; + pNew->Close = LTC11Close; + + return pNew; + } +/*------------------------------------------------------------------------*/ + static int LTC11AssignControl(pEVDriver pEva, int iMode, int iSensor) + { + pLTC11Driv self = NULL; + int iRet, iRead = 0; + char pBueffel[80], pCommand[50]; + + self = (pLTC11Driv)pEva->pPrivate; + assert(self); + assert( (iMode == HEATER) || (iMode == ANALOG) ); + + if(!self->pData) + { + self->iLastError = NOCONN; + return 0; + } + sprintf(pCommand,"SOSEN %d,%d;",iMode,iSensor); + iRet = SerialNoReply(&(self->pData),pCommand); + if(iRet != 1) + { + self->iLastError = iRet; + return 0; + } + sprintf(pCommand,"QOUT?%d;",iMode); + iRet = SerialWriteRead(&(self->pData),pCommand,pBueffel,79); + LTC11Unlock(self); + if(strcmp(pBueffel,"?TMO") == 0) + { + self->iLastError = TIMEOUT; + return 0; + } + sscanf(pBueffel,"%d;",&iRead); + if(iRead != iSensor) + { + self->iLastError = BADCONFIRM; + return 0; + } + if(iMode == ANALOG) + { + self->iControlAnalog = iSensor; + } + else + { + self->iControlHeat = iSensor; + } + /* switch back to control mode */ + SerialNoReply(&(self->pData),"SCONT;"); + return 1; + } +/*-------------------------------------------------------------------------- + handle LTC11 specific commands: + - sensor requests or sets read sensor + - mode requests or sets operation mode + - controlheat requests or sets sensor for heater control + - controlanalog requests or sets sensor for analog control + in all other cases fall back and call EVControllerWrapper to handle it or + eventually throw an error. +*/ + int LTC11Action(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]) + { + + pEVControl self = NULL; + int iRet, iMode; + char pBueffel[256], pError[132]; + pLTC11Driv pMe = NULL; + float fVal; + + self = (pEVControl)pData; + assert(self); + pMe = (pLTC11Driv)self->pDriv->pPrivate; + assert(pMe); + + if(argc > 1) + { + strtolower(argv[1]); +/*------ sensor */ + if(strcmp(argv[1],"sensor") == 0) + { + if(argc > 2) /* set case */ + { + /* check permission */ + if(!SCMatchRights(pCon,usUser)) + { + return 0; + } + iRet = Tcl_GetInt(pSics->pTcl,argv[2],&iMode); + if(iRet != TCL_OK) + { + sprintf(pBueffel,"ERROR: needed integer, got %s", + argv[2]); + SCWrite(pCon,pBueffel,eError); + return 0; + } + pMe->iSensor = iMode; + SCSendOK(pCon); + return 1; + } + else /* get case */ + { + sprintf(pBueffel,"%s.sensor = %d", argv[0],pMe->iSensor); + SCWrite(pCon,pBueffel,eValue); + return 1; + } + } +/*------ controlanalog */ + if(strcmp(argv[1],"controlanalog") == 0) + { + if(argc > 2) /* set case */ + { + /* check permission */ + if(!SCMatchRights(pCon,usUser)) + { + return 0; + } + iRet = Tcl_GetInt(pSics->pTcl,argv[2],&iMode); + if(iRet != TCL_OK) + { + sprintf(pBueffel,"ERROR: needed integer, got %s", + argv[2]); + SCWrite(pCon,pBueffel,eError); + return 0; + } + iRet = LTC11AssignControl(self->pDriv,ANALOG,iMode); + if(iRet != 1) + { + self->pDriv->GetError(self->pDriv,&iMode,pError,131); + sprintf(pBueffel,"ERROR: failed to set sensor: %s",pError); + SCWrite(pCon,pBueffel,eError); + return 0; + } + SCSendOK(pCon); + return 1; + } + else /* get case */ + { + sprintf(pBueffel,"%s.controlanalog = %d", argv[0],pMe->iControlAnalog); + SCWrite(pCon,pBueffel,eValue); + return 1; + } + } +/*------ controlheat */ + if(strcmp(argv[1],"controlheat") == 0) + { + if(argc > 2) /* set case */ + { + /* check permission */ + if(!SCMatchRights(pCon,usUser)) + { + return 0; + } + iRet = Tcl_GetInt(pSics->pTcl,argv[2],&iMode); + if(iRet != TCL_OK) + { + sprintf(pBueffel,"ERROR: needed integer, got %s", + argv[2]); + SCWrite(pCon,pBueffel,eError); + return 0; + } + iRet = LTC11AssignControl(self->pDriv,HEATER,iMode); + if(iRet != 1) + { + self->pDriv->GetError(self->pDriv,&iMode,pError,131); + sprintf(pBueffel,"ERROR: failed to set sensor: %s",pError); + SCWrite(pCon,pBueffel,eError); + return 0; + } + SCSendOK(pCon); + return 1; + } + else /* get case */ + { + sprintf(pBueffel,"%s.controlheat = %d", argv[0],pMe->iControlHeat); + SCWrite(pCon,pBueffel,eValue); + return 1; + } + } +/*-------- mode */ + else if(strcmp(argv[1],"mode") == 0) + { + if(argc > 2) /* set case */ + { + /* check permission */ + if(!SCMatchRights(pCon,usUser)) + { + return 0; + } + iRet = Tcl_GetInt(pSics->pTcl,argv[2],&iMode); + if(iRet != TCL_OK) + { + sprintf(pBueffel,"ERROR: needed integer, got %s", + argv[2]); + SCWrite(pCon,pBueffel,eError); + return 0; + } + iRet = LTC11SetMode(self->pDriv,iMode); + if(iRet != 1) + { + self->pDriv->GetError(self->pDriv,&iMode,pError,131); + sprintf(pBueffel,"ERROR: failed to set mode %s",pError); + SCWrite(pCon,pBueffel,eError); + return 0; + } + else + { + SCSendOK(pCon); + return 1; + } + } + else /* get case */ + { + iRet = LTC11GetMode(self->pDriv,&iMode); + if(iRet != 1) + { + self->pDriv->GetError(self->pDriv,&iMode,pError,131); + sprintf(pBueffel,"ERROR: failed to get mode %s",pError); + SCWrite(pCon,pBueffel,eError); + return 0; + } + if(iMode == ANALOG) + { + sprintf(pBueffel,"%s.mode = Analog Control", argv[0]); + } + else + { + sprintf(pBueffel,"%s.mode = Heater Control", argv[0]); + } + SCWrite(pCon,pBueffel,eValue); + return 1; + } + } +/*--------- list */ + else if(strcmp(argv[1],"list") == 0) + { + /* print generals first */ + EVControlWrapper(pCon,pSics,pData,argc,argv); + /* print our add on stuff */ + sprintf(pBueffel,"%s.sensor = %d",argv[0],pMe->iSensor); + SCWrite(pCon,pBueffel,eValue); + sprintf(pBueffel,"%s.controlanalog = %d",argv[0],pMe->iControlAnalog); + SCWrite(pCon,pBueffel,eValue); + sprintf(pBueffel,"%s.controlheat = %d",argv[0],pMe->iControlHeat); + SCWrite(pCon,pBueffel,eValue); + iRet = LTC11GetMode(self->pDriv,&iMode); + if(iRet != 1) + { + self->pDriv->GetError(self->pDriv,&iMode,pError,131); + sprintf(pBueffel,"ERROR: failed to get mode %s",pError); + SCWrite(pCon,pBueffel,eError); + } + if(iMode == ANALOG) + { + sprintf(pBueffel,"%s.mode = Analog Control", argv[0]); + } + else + { + sprintf(pBueffel,"%s.mode = Heater Control", argv[0]); + } + SCWrite(pCon,pBueffel,eValue); + return 1; + } + else + { + return EVControlWrapper(pCon,pSics,pData,argc,argv); + } + } + return EVControlWrapper(pCon,pSics,pData,argc,argv); + } diff --git a/ltc11.h b/ltc11.h new file mode 100644 index 0000000..4b8ed77 --- /dev/null +++ b/ltc11.h @@ -0,0 +1,24 @@ +/*------------------------------------------------------------------------- + L T C 1 1 + + An environment control driver and an additonal wrapper function for + controlling a Neocera LTC-11 temperature controller. This controller can be + in two states: control via a heater channel or control via a analaog + channel. + + copyright: see copyright.h + + Mark Koennecke, November 1998 +---------------------------------------------------------------------------*/ +#ifndef LTC11 +#define LTC11 + + pEVDriver CreateLTC11Driver(int argc, char *argv[]); + + int LTC11GetMode(pEVDriver self, int *iMode); + int LTC11SetMode(pEVDriver self, int iMode); + + int LTC11Action(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]); + +#endif diff --git a/motor/Makefile b/motor/Makefile new file mode 100644 index 0000000..45253ce --- /dev/null +++ b/motor/Makefile @@ -0,0 +1,21 @@ +#-------------------------------------------------------------------------- +# Makefile for Davids motor test program. +# +# Mark Koennecke, October 1998 +#-------------------------------------------------------------------------- +BINTARGET=$(HOME)/bin/sics + +OBJ=el734_test.o makeprint.o +CFLAGS= -I../hardsup -c +LFLAGS= -L../hardsup -lhlib -lX11 -lm + +.c.o: + cc $(CFLAGS) $*.c + +all: $(OBJ) + cc -o el734_test $(OBJ) $(LFLAGS) + - cp el734_test $(BINTARGET) +clean: + - rm el734_test + - rm *.o + \ No newline at end of file diff --git a/motor/el734_test b/motor/el734_test new file mode 100755 index 0000000..59d8cb3 Binary files /dev/null and b/motor/el734_test differ diff --git a/motor/el734_test.c b/motor/el734_test.c new file mode 100644 index 0000000..faf1d20 --- /dev/null +++ b/motor/el734_test.c @@ -0,0 +1,3900 @@ +#define ident "1E07" +#define Active_Motor 1 + +#ifdef __DECC +#pragma module EL734_TEST ident +#endif +/* +** +--------------------------------------------------------------+ +** | Paul Scherrer Institute | +** | SINQ Project | +** | | +** | This software may be used freely by non-profit organizations.| +** | It may be copied provided that the name of P.S.I. and of the | +** | author is included. Neither P.S.I. nor the author assume any | +** | responsibility for the use of this software outside of P.S.I.| +** +--------------------------------------------------------------+ +** +** Link_options - Here is the Linker Option File +**!$! +**!$! To build on LNSA09 ... +**!$! $ build_cc_select :== decc +**!$! $ import tasmad +**!$! $ def/job deltat_c_tlb sinq_c_tlb +**!$! $ bui tas_src:[utils]el734_test debug +**!$! +**!$! To build on PSICLC ... +**!$! $ build_cc_select :== decc +**!$! $ set default usr_scroot:[maden] +**!$! $ copy lnsa09::ud0:[maden.motor]el734_test.c [] +**!$! $ copy lnsa09::tasmad_disk:[mad.lib]sinq_dbg.olb [] +**!$! $ copy lnsa09::tasmad_disk:[mad.lib]sinq_c.tlb [] +**!$! $ def/job sinq_olb usr_scroot:[maden]sinq_dbg.olb +**!$! $ def/job sinq_c_tlb usr_scroot:[maden]sinq_c.tlb +**!$! $ def/job deltat_c_tlb sinq_c_tlb +**!$! $ bui el734_test debug +**!$! +**!$ if p1 .eqs. "DEBUG" then dbg1 := /debug +**!$ if p1 .eqs. "DEBUG" then dbg2 := _dbg +**!$ link 'dbg1'/exe=el734_test'dbg2'.exe sys$input/options +**! el734_test +**! sinq_olb/lib +**! sys$share:decw$xmlibshr12/share +**! sys$share:decw$xtlibshrr5/share +**! sys$share:decw$xlibshr/share +**!$ purge/nolog el734_test'dbg2'.exe +**!$ set prot=w:re el734_test'dbg2'.exe +**!$ my_dir = f$element (0, "]", f$environment ("procedure")) + "]" +**!$ write sys$output "Exec file is ''my_dir'EL734_TEST''DBG2'.EXE +**!$ exit +**!$! +** Link_options_end +** +** Building on Alpha OSF/1: +** +** cc -std1 -g -o ~/motor/el734_test -I/public/lib/include \ +** -transitive_link -L/public/lib -ldeltat \ +** -lrt -lXm -lXt -lX11 -lXext -lm \ +** ~/motor/el734_test.c +** or +** alias makep /usr/opt/posix/usr/bin/make <-- Posix Vn of "make" +** makep -f ~/motor/motor.make ~/motor/el734_test +** +** To run the program, one might need to set the environment variable: +** +** setenv LD_LIBRARY_PATH /public/lib +** +** if the -soname option was not specified to ld when the deltat shared +** library was build. +** +** Resources and Flags File: decw$user_defaults:SinQ_rc.dat +** ------------------- or $HOME/SinQ_rc +** +** Resource Flag Default Description +** -------- ---- ------- ----------- +** - -name el734_test Name to use when looking up the +** resources. The default is the +** image file name. +** ... +** A value given via -name will be converted to lowercase before being used. +**+ +**--------------------------------------------------------------------------- +** Module Name . . . . . . . . : [...MOTOR]EL734_TEST.C +** +** Author . . . . . . . . . . : D. Maden +** Date of creation . . . . . . : Nov 1995 +** +** Purpose +** ======= +** EL734_TEST is a test program for the EL734 Motor Controller. +** Use: +** === +** 1) On VMS, define a foreign command, e.g. +** +** $ el734_test :== $psi_public:[exe_axp]el734_test.exe +** +** On Unix systems, ensure el734_test is in a suitable PATH directory. +** +** 2) Issue commands of the form: +** +** el734_test -host lnsp22 -chan 5 -m 6 -p 25.0 +** +** where +** -host specifies the name of the computer to which the EL734 is +** attached. This computer must be running the +** RS232C_SRV program or equivalent. +** -chan specifies the serial channel number to which the EL734 is +** attached. +** -m specifies the motor number to be driven and +** -p specifies that a positioning command is to be executed. +** +** For a full list of options, issue the command: +** +** el734_test -help +** +** Updates: +** 1A01 25-Nov-1994 DM. Initial version. +** 1C01 12-Sep-1996 DM. Use SINQ.OLB and SINQ_C.TLB on VMS. +** 1D01 6-Nov-1996 DM. Add EC command to SAVE command list. +** 1E01 8-May-1997 DM. Add RESTORE and NO_RESTORE to LOAD command list. +**- +**==================================================================== +*/ +#include +#include +#include +#include + +#include + +#ifdef __VMS +#include +#else +#include +#endif + +#include +/* +**==================================================================== +*/ + +#include +#include +/* + +**-------------------------------------------------------------------------- +** Define global structures and constants. +*/ +#define NIL '\0' +#ifndef True +#define True 1 +#define False 0 +#endif +/* +** Define the file idents for stdin, stdout and stderr +*/ +#define STDIN 0 +#define STDOUT 1 +#define STDERR 2 + +#define N_ELEMENTS(arg) (sizeof (arg)/sizeof (arg[0])) +/*------------------------------------------------------------- +** Global Variables +*/ + static int C_gbl_status; /* Return status from C_... routines */ + static int Ctrl_C_has_happened; + void *Hndl = NULL; + + static XrmOptionDescRec OpTable_0[] = { + {"-name", ".name", XrmoptionSepArg, (XPointer) NULL}, + {"-?", ".el734HelpItem", XrmoptionSepArg, (XPointer) NULL}, + {"-?cmd", ".el734HelpCmd", XrmoptionNoArg, (XPointer) "1"}, + {"-?msg", ".el734HelpMsg", XrmoptionNoArg, (XPointer) "1"}, + {"-?par", ".el734HelpPar", XrmoptionNoArg, (XPointer) "1"}, + {"-?res", ".el734HelpRes", XrmoptionNoArg, (XPointer) "1"}, + {"-chan", ".el734Chan", XrmoptionSepArg, (XPointer) NULL}, + {"-f", ".el734Frequency", XrmoptionSepArg, (XPointer) NULL}, + {"-fb", ".el734Fb", XrmoptionNoArg, (XPointer) "-1"}, + {"-ff", ".el734Ff", XrmoptionNoArg, (XPointer) "-1"}, + {"-frequency",".el734Frequency", XrmoptionSepArg, (XPointer) NULL}, + {"-help", ".el734Help", XrmoptionNoArg, (XPointer) "1"}, + {"-hi", ".el734High", XrmoptionSepArg, (XPointer) NULL}, + {"-high", ".el734High", XrmoptionSepArg, (XPointer) NULL}, + {"-host", ".el734Host", XrmoptionSepArg, (XPointer) NULL}, + {"-hunt", ".el734Hunt", XrmoptionNoArg, (XPointer) "1"}, + {"-id", ".el734Id", XrmoptionSepArg, (XPointer) NULL}, + {"-limits", ".el734Limits", XrmoptionSepArg, (XPointer) NULL}, + {"-lo", ".el734Low", XrmoptionSepArg, (XPointer) NULL}, + {"-load", ".el734Load", XrmoptionSepArg, (XPointer) NULL}, + {"-low", ".el734Low", XrmoptionSepArg, (XPointer) NULL}, + {"-m", ".el734Motor", XrmoptionSepArg, (XPointer) NULL}, + {"-motor", ".el734Motor", XrmoptionSepArg, (XPointer) NULL}, + {"-n", ".el734N", XrmoptionSepArg, (XPointer) NULL}, + {"-p", ".el734Position", XrmoptionSepArg, (XPointer) NULL}, + {"-port", ".el734Port", XrmoptionSepArg, (XPointer) NULL}, + {"-position", ".el734Position", XrmoptionSepArg, (XPointer) NULL}, + {"-random", ".el734Random", XrmoptionNoArg, (XPointer) "1"}, + {"-ref", ".el734Ref", XrmoptionNoArg, (XPointer) "1"}, + {"-rndm", ".el734Random", XrmoptionNoArg, (XPointer) "1"}, + {"-s", ".el734Stop", XrmoptionNoArg, (XPointer) "1"}, + {"-save", ".el734Save", XrmoptionSepArg, (XPointer) NULL}, + {"-saw", ".el734Saw", XrmoptionNoArg, (XPointer) "1"}, + {"-sb", ".el734Sb", XrmoptionNoArg, (XPointer) "-1"}, + {"-scan", ".el734Scan", XrmoptionNoArg, (XPointer) "-1"}, + {"-seed", ".el734Seed", XrmoptionSepArg, (XPointer) NULL}, + {"-sf", ".el734Sf", XrmoptionNoArg, (XPointer) "-1"}, + {"-step", ".el734Step", XrmoptionSepArg, (XPointer) NULL}, + {"-stop", ".el734Stop", XrmoptionNoArg, (XPointer) "1"}, + {"-tmo", ".el734Tmo", XrmoptionSepArg, (XPointer) NULL}, + {"-ur@", ".el734SetPos", XrmoptionSepArg, (XPointer) NULL}, + {"-verbose", ".el734Verbose", XrmoptionSepArg, (XPointer) NULL}, + {"-wait", ".el734Wait", XrmoptionSepArg, (XPointer) NULL}, + }; + char El734_host[20]; + int El734_port; /* TCP/IP Port number for socket */ + int El734_chan; /* Asynch channel number */ + char El734_id0[20]; /* The 1st EL734 identifier string */ + char El734_id1[20]; /* The 2nd EL734 identifier string */ + int Check_EL734_id; + int Motor; /* Motor number */ + int Enc_typ; + int Enc_num; + int Enc_par; + int N_moves; + float Lo_arg, Hi_arg; + int Lo_arg_present, Hi_arg_present; + int Do_help, Do_posit, Do_rndm, Do_saw, Do_scan, Do_step; + int Do_ref, Do_hunt, Do_ff, Do_fb, Do_sf, Do_sb, Do_stop; + int Do_save, Do_load; + int Do_limits, Do_setpos; + float Lim_arg_lo, Lim_arg_hi, Ist_arg; + float Tmo, Tmo_ref; + unsigned int Seed; + int Seed_present; + int Verbose; + int Wait_time, Frequency; + float Soll_posit; + float Step; + char Save_file[80]; + char Load_file[80]; + + int Dec_pt = 3; + int Enc_fact_0, Enc_fact_1; + int Mot_fact_0, Mot_fact_1; + float Inertia_tol; + int Ramp; + int Loop_mode; + int Slow_hz; + float Lo, Hi; + char Ctrl_id[32]; + int Fast_hz; + int Ref_mode; + int Backlash; + int Pos_tol; + char Mot_mem[16]; + char Mot_name[16]; + float Ref_param; + int Is_sided; + int Null_pt; + int Ac_par; + int Enc_circ; + int Stat_pos; + int Stat_pos_flt; + int Stat_pos_fail; + int Stat_cush_fail; + float Ist_pos; + int Prop; + int Integ; + int Deriv; + + char *Errstack; + int Errcode, Errno, Vaxc_errno; +/* +**-------------------------------------------------------------------------- +** PrintErrInfo: print out error information. +*/ + void PrintErrInfo (char *text) { +/* ============ +*/ + + EL734_ErrInfo (&Errstack, &Errcode, &Errno, &Vaxc_errno); + fprintf (stderr, "\n\007" + " Error return from %s\n" + " Errstack = \"%s\"\n" + " Errcode = %d Errno = %d Vaxc$errno = %d\n", + text, Errstack, Errcode, Errno, Vaxc_errno); + switch (Errcode) { + case EL734__BAD_ADR: + fprintf (stderr, " Address error\n"); break; + case EL734__BAD_CMD: + fprintf (stderr, " Command error\n"); break; + case EL734__BAD_ILLG: + fprintf (stderr, " Illegal response\n"); break; + case EL734__BAD_LOC: + fprintf (stderr, " EL734 is in manual mode.\n"); break; + case EL734__BAD_MALLOC: + fprintf (stderr, " Call to \"malloc\" failed\n"); perror (text); break; + case EL734__BAD_OFL: + fprintf (stderr, " Connection to asynch port lost\n"); break; + case EL734__BAD_OVFL: + fprintf (stderr, " Overflow: may be due to bad encoder gearing factor\n"); break; + case EL734__BAD_PAR: + fprintf (stderr, " Illegal parameter specified\n"); break; + case EL734__BAD_RNG: + fprintf (stderr, " Attempted to exceed lower or upper limit\n"); break; + case EL734__BAD_SOCKET: + fprintf (stderr, " Call to \"AsynSrv_Open\" failed\n"); perror (text); break; + case EL734__BAD_STP: + fprintf (stderr, " Motor is disabled: \"Stop\" signal is active!"); + break; + case EL734__BAD_TMO: + fprintf (stderr, " Time-out of EL734 response.\n"); + break; + default: if ((Errno != 0) || (Vaxc_errno != 0)) perror (text); + } + return; + } +/* +**-------------------------------------------------------------------------- +** GetKHVQZ: read the K, H, V, Q and Zero-point parameters +*/ + int GetKHVQZ ( +/* ======== +*/ int *k, + float *lo, + float *hi, + int *v, + float *q, + float *z) { + + int status; + + status = EL734_GetRefMode (&Hndl, k); + if (!status) { + printf ("\n\007"); + printf ("Bad status from EL734_GetRefMode.\n"); + return False; + } + + status = EL734_GetLimits (&Hndl, lo, hi); + if (!status) { + printf ("\n\007"); + printf ("Bad status from EL734_GetLimits.\n"); + return False; + } + + status = EL734_GetNullPoint (&Hndl, v); + if (!status) { + printf ("\n\007"); + printf ("Bad status from EL734_GetZeroPoint.\n"); + return False; + } + + status = EL734_GetRefParam (&Hndl, q); + if (!status) { + printf ("\n\007"); + printf ("Bad status from EL734_GetRefParam.\n"); + return False; + } + + status = EL734_GetZeroPoint (&Hndl, z); + if (!status) { + printf ("\n\007"); + printf ("Bad status from EL734_GetZeroPoint.\n"); + return False; + } + + return True; + } +/* +**--------------------------------------------------------------------------- +** My_WaitIdle: Wait till MSR goes to zero or +** This routine is similar to EL734_WaitIdle +** with the extra verbose argument and a test +** for . +*/ + int My_WaitIdle ( +/* =========== +*/ void **handle, + int verbose, /* Width of display field */ + int *ored_msr, + int *fp_cntr, + int *fr_cntr, + float *ist_posit) { +#ifdef __VMS +#include +#define hibernate lib$wait (0.25) +#else +#include +#include + struct timespec delay = {0, 250000000}; + struct timespec delay_left; +#define hibernate nanosleep_d9 (&delay, &delay_left) +#endif + int i, msr, ss, s_stat; + int my_verbose; + float last_posit; + char buff[64]; + + my_verbose = verbose; + if (my_verbose*2 > sizeof (buff)) my_verbose = sizeof (buff)/2; + if (my_verbose > 0) { + s_stat = EL734_GetStatus (handle, + &msr, ored_msr, fp_cntr, fr_cntr, &ss, ist_posit); + if (!s_stat) { + PrintErrInfo ("My_WaitIdle/EL734_GetStatus"); + return False; + } + last_posit = *ist_posit; + sprintf (buff, "%*.*f", my_verbose, Dec_pt, last_posit); + printf (buff); fflush (NULL); + for (i=0; i 0) && (*ist_posit != last_posit)) { + last_posit = *ist_posit; + sprintf (&buff[my_verbose], "%*.*f", my_verbose, Dec_pt, last_posit); + printf (buff); fflush (NULL); + } + if ((msr & MSR__BUSY) == 0) return True; + hibernate; + if (Ctrl_C_has_happened) return False; + } + PrintErrInfo ("My_WaitIdle/EL734_GetStatus"); /* Error detected in + ** EL734_GetStatus */ + return False; + } +/* +**-------------------------------------------------------------------------- +** LoadCheckTwoInteger: routine to check that a command specifying +** two integers set correctly. +*/ + int LoadCheckTwoInteger (char *cmnd) { +/* =================== +*/ + int status, len; + char my_cmnd[80], rd_cmnd[40], buff[40]; + char *cmnd_tok, *motor_tok, *par0_tok, *par1_tok, *rd0_tok, *rd1_tok; + + StrJoin (my_cmnd, sizeof (my_cmnd), cmnd, ""); /* Make a copy of cmnd */ + len = strlen (my_cmnd); + if (my_cmnd[len-1] == '\r') my_cmnd[len-1] = NIL; + + cmnd_tok = strtok (my_cmnd, " "); + motor_tok = strtok (NULL, " "); + par0_tok = strtok (NULL, " "); + par1_tok = strtok (NULL, " "); + if ((cmnd_tok == NULL) || (motor_tok == NULL) || + (par0_tok == NULL) || (par1_tok == NULL)) { + printf ("\007Software problem in LoadCheckTwoInteger\n"); + return False; + } + StrJoin (buff, sizeof (buff), cmnd_tok, " "); + StrJoin (rd_cmnd, sizeof (rd_cmnd), buff, motor_tok); + len = strlen (rd_cmnd); + rd_cmnd[len] = '\r'; + rd_cmnd[len+1] = NIL; + status = EL734_SendCmnd (&Hndl, rd_cmnd, buff, sizeof (buff)); + if (status) { + rd0_tok = strtok (buff, " "); + rd1_tok = strtok (NULL, " "); + if ((rd0_tok == NULL) || + (rd1_tok == NULL) || + (strcmp (par0_tok, rd0_tok) != 0) || + (strcmp (par1_tok, rd1_tok) != 0)) { + if (rd0_tok == NULL) rd0_tok = ""; + if (rd1_tok == NULL) rd1_tok = ""; + printf ("\007Verify error for command \"%s %s %s %s\"\n", + cmnd_tok, motor_tok, par0_tok, par1_tok); + printf ("Values set in EL734 controller are \"%s %s\"\n" + " They should be \"%s %s\"\n", + rd0_tok, rd1_tok, par0_tok, par1_tok); + return False; + }else { + return True; /* The parameter has been set correctly! */ + } + }else { + printf ("\007LoadCheckTwoInteger -- error from EL734_SendCmnd\n"); + printf ("Command being checked was \"%s %s %s %s\"\n", + cmnd_tok, motor_tok, par0_tok, par1_tok); + return False; + } + } +/* +**-------------------------------------------------------------------------- +** LoadCheckTwoFloat: routine to check that a command specifying +** two real values set correctly. +*/ + int LoadCheckTwoFloat (char *cmnd, int n_dec) { +/* ================= +*/ + int status, len; + char my_cmnd[80], rd_cmnd[40], buff[40], par0[40], par1[40]; + char *cmnd_tok, *motor_tok, *par0_tok, *par1_tok, *rd0_tok, *rd1_tok; + char *whole_tok, *frac_tok; + + StrJoin (my_cmnd, sizeof (my_cmnd), cmnd, ""); /* Make a copy of cmnd */ + len = strlen (my_cmnd); + if (my_cmnd[len-1] == '\r') my_cmnd[len-1] = NIL; + + cmnd_tok = strtok (my_cmnd, " "); + motor_tok = strtok (NULL, " "); + par0_tok = strtok (NULL, " "); + par1_tok = strtok (NULL, " "); + if ((cmnd_tok == NULL) || (motor_tok == NULL) || + (par0_tok == NULL) || (par1_tok == NULL)) { + printf ("\007Software problem in LoadCheckTwoFloat\n"); + return False; + } + /*--------------------------------------------------- + ** Check that the number of decimal places in the first set + ** parameter agrees with the setting of the EL734. + */ + StrJoin (par0, sizeof (par0), par0_tok, ""); + whole_tok = strtok (par0, "."); + frac_tok = strtok (NULL, "."); + if (frac_tok == NULL) { /* Check for a decimal point */ + len = strlen (whole_tok); /* None there, so put in a ".0" */ + frac_tok = whole_tok + len + 1; + frac_tok[0] = '0'; + frac_tok[1] = NIL; + } + len = strlen (frac_tok); + if (len > n_dec) { /* Param has too many decimal places */ + /* Try to remove trailing zeros */ + while ((len >= 0) && (frac_tok[len-1] == '0')) { + len = len - 1; + frac_tok[len] = NIL; + if (len == n_dec) break; + } + if (len != n_dec) { + printf ("Don't expect the parameter to verify correctly.\n" + "You have specified too many decimal places!\n"); + } + }else if (len < n_dec) { /* Param has too few decimal places */ + while (len < n_dec) { /* Pad with zeros */ + frac_tok[len] = '0'; + len = len + 1; + frac_tok[len] = NIL; + } + } + len = strlen (whole_tok); /* Re-join the parts of param again */ + whole_tok[len] = '.'; + /*--------------------------------------------------- + ** Check that the number of decimal places in the second set + ** parameter agrees with the setting of the EL734. + */ + StrJoin (par1, sizeof (par1), par1_tok, ""); + whole_tok = strtok (par1, "."); + frac_tok = strtok (NULL, "."); + if (frac_tok == NULL) { /* Check for a decimal point */ + len = strlen (whole_tok); /* None there, so put in a ".0" */ + frac_tok = whole_tok + len + 1; + frac_tok[0] = '0'; + frac_tok[1] = NIL; + } + len = strlen (frac_tok); + if (len > n_dec) { /* Param has too many decimal places */ + /* Try to remove trailing zeros */ + while ((len >= 0) && (frac_tok[len-1] == '0')) { + len = len - 1; + frac_tok[len] = NIL; + if (len == n_dec) break; + } + if (len != n_dec) { + printf ("Don't expect the parameter to verify correctly.\n" + "You have specified too many decimal places!\n"); + } + }else if (len < n_dec) { /* Param has too few decimal places */ + while (len < n_dec) { /* Pad with zeros */ + frac_tok[len] = '0'; + len = len + 1; + frac_tok[len] = NIL; + } + } + len = strlen (whole_tok); /* Re-join the parts of param again */ + whole_tok[len] = '.'; + /* End of checking number of decimal places + **--------------------------------------------------- + */ + StrJoin (buff, sizeof (buff), cmnd_tok, " "); + StrJoin (rd_cmnd, sizeof (rd_cmnd), buff, motor_tok); + len = strlen (rd_cmnd); + rd_cmnd[len] = '\r'; + rd_cmnd[len+1] = NIL; + status = EL734_SendCmnd (&Hndl, rd_cmnd, buff, sizeof (buff)); + if (status) { + rd0_tok = strtok (buff, " "); + rd1_tok = strtok (NULL, " "); + if ((rd0_tok == NULL) || + (rd1_tok == NULL) || + (strcmp (par0, rd0_tok) != 0) || + (strcmp (par1, rd1_tok) != 0)) { + if (rd0_tok == NULL) rd0_tok = ""; + if (rd1_tok == NULL) rd1_tok = ""; + printf ("\007Verify error for command \"%s %s %s %s\"\n", + cmnd_tok, motor_tok, par0, par1); + printf ("Value set in EL734 controller is \"%s %s\"\n" + " It should be \"%s %s\"\n", + rd0_tok, rd1_tok, par0, par1); + return False; + }else { + return True; /* The parameter has been set correctly! */ + } + }else { + printf ("\007LoadCheckTwoFloat -- error from EL734_SendCmnd\n"); + printf ("Command being checked was \"%s %s %s %s\"\n", + cmnd_tok, motor_tok, par0, par1); + return False; + } + } +/* +**-------------------------------------------------------------------------- +** LoadFloatJuggle: routine to try to get around an EL734 problem. +** +** The problem is that the EL734 does not at the moment +** handle binary <--> float conversion correctly. +*/ + int LoadFloatJuggle ( +/* =============== +*/ char *cmnd, /* The command to be issued */ + char *motor, /* The motor index being loaded */ + char *param, /* The desired parameter */ + int n_dec) { /* The number of decimal places */ +/* +** It is assumed that all parameters are consistent (especially +** param and n_dec) since this is an internal routine and that +** they are not terminated with . +*/ + int status, i, incr0, incr1, incr2; + char set_cmnd[80], read_cmnd[40], my_par[40], buff[40]; + char *rd_tok; + + printf ("Trying to juggle the \"%s\" parameter of Motor %s" + " to be %s ..\n .. ", cmnd, motor, param); + + sprintf (read_cmnd, "%s %s\r", cmnd, motor); /* Prepare the param rd cmnd */ + /* + ** Find indices of last 3 chars to be incremented + */ + incr0 = strlen (param); + incr0--; /* incr0 now indexes the last digit of param */ + + if (!isdigit (param[incr0])) incr0--; /* Be careful not to increment .. */ + /* .. a decimal point! */ + incr1 = incr0 - 1; + if (!isdigit (param[incr1])) incr1--; /* Be careful not to increment .. */ + /* .. a decimal point! */ + incr2 = incr1 - 1; + if (!isdigit (param[incr2])) incr2--; /* Be careful not to increment .. */ + /* .. a decimal point! */ + if ((!isdigit (param[incr0])) || + (!isdigit (param[incr1])) || + (!isdigit (param[incr2]))) { + printf ("LoadFloatJuggle: software problem with decimal point\n" + " The routine probably needs to be enhanced!\n"); + return False; + } + /*---------------------------------------------------------------- + ** First try incrementing the last digit of the set value of the + ** parameter 5 times. + */ + StrJoin (my_par, sizeof (my_par), param, ""); /* Make a copy of param */ + + for (i = 0; i < 5; i++) { + if (my_par[incr0] != '9') { /* Check for carry to next digit */ + my_par[incr0]++; + }else { + my_par[incr0] = '0'; + if (my_par[incr1] != '9') { + my_par[incr1]++; + }else { + my_par[incr1] = '0'; + if (my_par[incr2] != '9') { + my_par[incr2]++; + }else { + my_par[incr2] = '0'; + } + } + } + + printf ("%s .. ", my_par); + sprintf (set_cmnd, "%s %s %s\r", cmnd, motor, my_par); + + status = EL734_SendCmnd (&Hndl, set_cmnd, buff, sizeof (buff)); + if (status) status = EL734_SendCmnd (&Hndl, read_cmnd, buff, sizeof (buff)); + if (!status) { + printf ("\nError with \"%s %s %s\"\n" + "Abandoning parameter juggle!\n", cmnd, motor, my_par); + return False; + } + rd_tok = strtok (buff, " "); /* Skip leading spaces */ + if (strcmp (param, rd_tok) == 0) { + printf ("\n Success. Parameter as sent was %s\n" + " Parameter as read is %s\n", my_par, param); + return True; + } + } + /*---------------------------------------------------------------- + ** Now try decrementing the last digit of the set value of the + ** parameter 5 times. + */ + StrJoin (my_par, sizeof (my_par), param, ""); /* Make a copy of param */ + + for (i = 0; i < 5; i++) { + if (my_par[incr0] != '0') { /* Check for carry to next digit */ + my_par[incr0]--; + }else { + my_par[incr0] = '9'; + if (my_par[incr1] != '0') { + my_par[incr1]--; + }else { + my_par[incr1] = '9'; + if (my_par[incr2] != '0') { + my_par[incr2]--; + }else { + my_par[incr2] = '9'; + } + } + } + + printf ("%s .. ", my_par); + sprintf (set_cmnd, "%s %s %s\r", cmnd, motor, my_par); + + status = EL734_SendCmnd (&Hndl, set_cmnd, buff, sizeof (buff)); + if (status) status = EL734_SendCmnd (&Hndl, read_cmnd, buff, sizeof (buff)); + if (!status) { + printf ("\nError with \"%s %s %s\"\n" + "Abandoning parameter juggle!\n", cmnd, motor, my_par); + return False; + } + rd_tok = strtok (buff, " "); /* Skip leading spaces */ + if (strcmp (param, rd_tok) == 0) { + printf ("\n Success. Parameter as sent was %s\n" + " Parameter as read is %s\n", my_par, param); + return True; + } + } + /* + ** Failed - go back to original setting + */ + sprintf (set_cmnd, "%s %s %s\r", cmnd, motor, param); + status = EL734_SendCmnd (&Hndl, set_cmnd, buff, sizeof (buff)); + if (status) status = EL734_SendCmnd (&Hndl, read_cmnd, buff, sizeof (buff)); + if (status) { + rd_tok = strtok (buff, " "); + printf ("\n Failed. Parameter value is set to %s\n", rd_tok); + }else { + printf ("\n Failed. Parameter value is unknown due to error\n"); + } + return False; + } +/* +**-------------------------------------------------------------------------- +** LoadIntJuggle: routine to try to get around an EL734 problem. +** +** The problem is that some integer parameters (e.g. E) +** do not set correctly. +*/ + int LoadIntJuggle ( +/* ============= +*/ char *cmnd, /* The command to be issued */ + char *motor, /* The motor index being loaded */ + char *param) { /* The desired parameter */ +/* +** It is assumed that the parameters are trimmed (especially +** param) and are not terminated with . +*/ + int status, i, my_par; + char set_cmnd[80], read_cmnd[40], buff[40]; + char *rd_tok; + + printf ("Trying to juggle the \"%s\" parameter of Motor %s" + " to be %s ..\n .. ", cmnd, motor, param); + + sprintf (read_cmnd, "%s %s\r", cmnd, motor); /* Prepare the param rd cmnd */ + + /*---------------------------------------------------------------- + ** First try incrementing the parameter 5 times. + */ + sscanf (param, "%d", &my_par); /* Gen binary value of param */ + + for (i = 0; i < 5; i++) { + my_par++; + + printf ("%d .. ", my_par); + sprintf (set_cmnd, "%s %s %d\r", cmnd, motor, my_par); + + status = EL734_SendCmnd (&Hndl, set_cmnd, buff, sizeof (buff)); + if (status) status = EL734_SendCmnd (&Hndl, read_cmnd, buff, sizeof (buff)); + if (!status) { + printf ("\nError with \"%s %s %d\"\n" + "Abandoning parameter juggle!\n", cmnd, motor, my_par); + return False; + } + rd_tok = strtok (buff, " "); /* Skip leading spaces */ + if (strcmp (param, rd_tok) == 0) { + printf ("\n Success. Parameter as sent was %d\n" + " Parameter as read is %s\n", my_par, param); + return True; + } + } + /*---------------------------------------------------------------- + ** Now try decrementing the last digit of the set value of the + ** parameter 5 times. + */ + sscanf (param, "%d", &my_par); /* Gen binary value of param */ + + for (i = 0; i < 5; i++) { + my_par--; + + printf ("%d .. ", my_par); + sprintf (set_cmnd, "%s %s %d\r", cmnd, motor, my_par); + + status = EL734_SendCmnd (&Hndl, set_cmnd, buff, sizeof (buff)); + if (status) status = EL734_SendCmnd (&Hndl, read_cmnd, buff, sizeof (buff)); + if (!status) { + printf ("\nError with \"%s %s %d\"\n" + "Abandoning parameter juggle!\n", cmnd, motor, my_par); + return False; + } + rd_tok = strtok (buff, " "); /* Skip leading spaces */ + if (strcmp (param, rd_tok) == 0) { + printf ("\n Success. Parameter as sent was %d\n" + " Parameter as read is %s\n", my_par, param); + return True; + } + } + /* + ** Failed - go back to original setting + */ + sprintf (set_cmnd, "%s %s %s\r", cmnd, motor, param); + status = EL734_SendCmnd (&Hndl, set_cmnd, buff, sizeof (buff)); + if (status) status = EL734_SendCmnd (&Hndl, read_cmnd, buff, sizeof (buff)); + if (status) { + rd_tok = strtok (buff, " "); + printf ("\n Failed. Parameter value is set to %s\n", rd_tok); + }else { + printf ("\n Failed. Parameter value is unknown due to error\n"); + } + return False; + } +/* +**-------------------------------------------------------------------------- +** LoadCheckOneInteger: routine to check that a command specifying +** a single integer set correctly. +*/ + int LoadCheckOneInteger (char *cmnd) { +/* =================== +*/ + int status, len; + char my_cmnd[80], rd_cmnd[40], buff[40]; + char *cmnd_tok, *motor_tok, *param_tok, *rd_tok; + + StrJoin (my_cmnd, sizeof (my_cmnd), cmnd, ""); /* Make a copy of cmnd */ + len = strlen (my_cmnd); + if (my_cmnd[len-1] == '\r') my_cmnd[len-1] = NIL; + + cmnd_tok = strtok (my_cmnd, " "); + motor_tok = strtok (NULL, " "); + param_tok = strtok (NULL, " "); + if ((cmnd_tok == NULL) || (motor_tok == NULL) || (param_tok == NULL)) { + printf ("\007Software problem in LoadCheckOneInteger\n"); + return False; + } + StrJoin (buff, sizeof (buff), cmnd_tok, " "); + StrJoin (rd_cmnd, sizeof (rd_cmnd), buff, motor_tok); + len = strlen (rd_cmnd); + rd_cmnd[len] = '\r'; + rd_cmnd[len+1] = NIL; + status = EL734_SendCmnd (&Hndl, rd_cmnd, buff, sizeof (buff)); + if (status) { + rd_tok = strtok (buff, " "); + if ((rd_tok == NULL) || + (strcmp (param_tok, rd_tok) != 0)) { + if (rd_tok == NULL) rd_tok = ""; + printf ("\007Verify error for command \"%s %s %s\"\n", + cmnd_tok, motor_tok, param_tok); + printf ("Value set in EL734 controller is \"%s\"\n" + " It should be \"%s\"\n", + rd_tok, param_tok); + status = LoadIntJuggle (cmnd_tok, motor_tok, param_tok); + return status; + }else { + return True; /* The parameter has been set correctly! */ + } + }else { + printf ("\007LoadCheckOneInteger -- error from EL734_SendCmnd\n"); + printf ("Command being checked was \"%s %s %s\"\n", + cmnd_tok, motor_tok, param_tok); + return False; + } + } +/* +**-------------------------------------------------------------------------- +** LoadCheckOneFloat: routine to check that a command specifying +** a single real value set correctly. +*/ + int LoadCheckOneFloat (char *cmnd, int n_dec) { +/* ================= +*/ + int status, len, n_dec_ok; + char my_cmnd[80], rd_cmnd[40], buff[40], param[40]; + char *cmnd_tok, *motor_tok, *param_tok, *rd_tok; + char *whole_tok, *frac_tok; + + StrJoin (my_cmnd, sizeof (my_cmnd), cmnd, ""); /* Make a copy of cmnd */ + len = strlen (my_cmnd); + if (my_cmnd[len-1] == '\r') my_cmnd[len-1] = NIL; + + cmnd_tok = strtok (my_cmnd, " "); + motor_tok = strtok (NULL, " "); + param_tok = strtok (NULL, " "); + if ((cmnd_tok == NULL) || (motor_tok == NULL) || (param_tok == NULL)) { + printf ("\007Software problem in LoadCheckOneFloat\n"); + return False; + } + /* + ** Check that the number of decimal places in the set parameter + ** agrees with the setting of the EL734. + */ + n_dec_ok = True; /* Assume it will be OK */ + StrJoin (param, sizeof (param), param_tok, ""); + whole_tok = strtok (param, "."); + frac_tok = strtok (NULL, "."); + if (frac_tok == NULL) { /* Check for a decimal point */ + len = strlen (whole_tok); /* None there, so put in a ".0" */ + frac_tok = whole_tok + len + 1; + frac_tok[0] = '0'; + frac_tok[1] = NIL; + } + len = strlen (frac_tok); + if (len > n_dec) { /* Param has too many decimal places */ + /* Try to remove trailing zeros */ + while ((len >= 0) && (frac_tok[len-1] == '0')) { + len = len - 1; + frac_tok[len] = NIL; + if (len == n_dec) break; + } + if (len != n_dec) { + printf ("Don't expect the parameter to verify correctly.\n" + "You have specified too many decimal places!\n"); + n_dec_ok = False; /* Remember it (to suppress retries) */ + } + }else if (len < n_dec) { /* Param has too few decimal places */ + while (len < n_dec) { /* Pad with zeros */ + frac_tok[len] = '0'; + len = len + 1; + frac_tok[len] = NIL; + } + } + len = strlen (whole_tok); /* Re-join the parts of param again */ + whole_tok[len] = '.'; + + StrJoin (buff, sizeof (buff), cmnd_tok, " "); + StrJoin (rd_cmnd, sizeof (rd_cmnd), buff, motor_tok); + len = strlen (rd_cmnd); + rd_cmnd[len] = '\r'; + rd_cmnd[len+1] = NIL; + status = EL734_SendCmnd (&Hndl, rd_cmnd, buff, sizeof (buff)); + if (status) { + rd_tok = strtok (buff, " "); + if ((rd_tok == NULL) || + (strcmp (param, rd_tok) != 0)) { + if (rd_tok == NULL) rd_tok = ""; + printf ("\007Verify error for command \"%s %s %s\"\n", + cmnd_tok, motor_tok, param); + printf ("Value set in EL734 controller is \"%s\"\n" + " It should be \"%s\"\n", + rd_tok, param); + if (n_dec_ok) { + status = LoadFloatJuggle (cmnd_tok, motor_tok, param, n_dec); + } + return status; + }else { + return True; /* The parameter has been set correctly! */ + } + }else { + printf ("\007LoadCheckOneFloat -- error from EL734_SendCmnd\n"); + printf ("Command being checked was \"%s %s %s\"\n", + cmnd_tok, motor_tok, param); + return False; + } + } +/* +**-------------------------------------------------------------------------- +** DoStop: Send a STOP command to the motor. If has +** been detected, assume that this is an emergency +** stop and do fewer tests. +*/ + int DoStop () { +/* ====== +*/ + int status, no_errors, i, len, act_len; + int msr, ored_msr, fp_cntr, fr_cntr, ss; + char recd[132], buff[132], cmnd[80]; + float f_tmp; + + no_errors = True; + + if (!Ctrl_C_has_happened) { + EL734_ZeroStatus (&Hndl); + printf ("Sending STOP command to motor %d ...", Motor); + }else { + printf ("\n\007 detected: Sending STOP command to motor %d ...", + Motor); + } + + status = EL734_Stop (&Hndl); + if (!status) no_errors = False; + + if (no_errors) { + if (Ctrl_C_has_happened) { + printf (" OK.\n"); + return True; + }else { + printf ("\nWwaiting for motor to become idle ... "); + status = My_WaitIdle (&Hndl, Verbose, + &ored_msr, &fp_cntr, &fr_cntr, &Ist_pos); + if (!status) { + if (Ctrl_C_has_happened) { + printf ("\n\007 detected: Wait-for-idle abandoned!\n"); + }else { + printf ("\n\007 Error return status from My_WaitIdle!\n"); + } + return False; + } + if ((ored_msr & MSR__STOPPED) == 0) { + printf ("\n\007 Warning -- MSR STOP bit is not set!\n"); + return False; + } + if ((ored_msr & (~MSR__STOPPED)) != 0) { + printf ("\n\007 ... unexpected MSR obtained!\n"); + printf (" %s\n", EL734_EncodeMSR (buff, sizeof (buff), + 0, ored_msr, fp_cntr, fr_cntr)); + return False; + } + printf (" OK.\n"); + } + return True; + }else { + printf ("\n\007 STOP command not accepted!\n"); + return False; + } + } +/* +**-------------------------------------------------------------------------- +** DoLimits: Set the lower and upper software limits +*/ + int DoLimits ( +/* ======== +*/ float lo, + float hi) { + + int status, no_errors, i, len, act_len; + int msr, ored_msr, fp_cntr, fr_cntr, ss; + char recd[132], buff[132], cmnd[80]; + float f_tmp; + + no_errors = False; + + printf ("Sending command \"h %d %.*f %.*f\" ...", + Motor, Dec_pt, lo, Dec_pt, hi); + + sprintf (cmnd, "h %d %.*f %.*f\r", Motor, Dec_pt, lo, Dec_pt, hi); + status = EL734_SendCmnd (&Hndl, cmnd, buff, sizeof (buff)); + if (status && (buff[0] == NIL)) no_errors = True; + + if (no_errors) { + printf (" OK.\n"); + return True; + }else { + printf ("\n\007 Command not accepted!\n"); + return False; + } + } +/* +**-------------------------------------------------------------------------- +** DoSimpleMove: Send a simple move command to the motor and wait for idle +*/ + int DoSimpleMove ( +/* ============ +*/ char *a_cmnd, + int test_status) { + + int status, i, len, act_len; + int msr, ored_msr, fp_cntr, fr_cntr, ss; + char recd[132], buff[132], cmnd[80]; + float f_tmp; + char *etxt; + + EL734_ZeroStatus (&Hndl); + + sprintf (cmnd, a_cmnd, Motor); + printf ("Sending \"%s\" command ...", cmnd); + + i = strlen (cmnd); + cmnd[i] = '\r'; + cmnd[i+1] = NIL; + status = EL734_SendCmnd (&Hndl, cmnd, buff, sizeof (buff)); + if (!status) { + PrintErrInfo ("EL734_SendCmnd"); + return False; + } + + if (buff[0] != NIL) { + printf ("\007 response was \"%s\".\n", buff); + etxt = "Unrecognised response!"; + if (strcmp (buff, "?BSY") == 0) etxt = "Motor busy!"; + if (strcmp (buff, "?CMD") == 0) etxt = "Bad command!"; + if (strcmp (buff, "?LOC") == 0) etxt = "Controller is in manual mode!"; + if (strcmp (buff, "?ADR") == 0) etxt = "Bad motor number!"; + if (strcmp (buff, "?RNG") == 0) etxt = "Range error! Check low/high limits."; + if (strcmp (buff, "*MS") == 0) + etxt = "Motor is disabled: \"Stop\" signal is active!"; + if (strcmp (buff, "*ES") == 0) + etxt = "Motor is disabled: \"Emergency Stop\" signal is active!"; + if (strncmp (buff, "?TMO", 4) == 0) + etxt = "Time-out! You should check the cables, perhaps."; + printf ("%s\n", etxt); + return False; + } + + printf ("\nWaiting for motor to become idle ..."); fflush (NULL); + status = My_WaitIdle (&Hndl, Verbose, + &ored_msr, &fp_cntr, &fr_cntr, &Ist_pos); + if (!status) { + if (Ctrl_C_has_happened) DoStop (); + return False; + } + if (test_status && ((ored_msr & (~MSR__BUSY)) != MSR__OK)) { + printf ("\n\007 ... unexpected MSR obtained!\n"); + printf (" %s\n", EL734_EncodeMSR (buff, sizeof (buff), + 0, ored_msr, fp_cntr, fr_cntr)); + return False; + } + printf (" OK.\nPosition = %.*f\n", Dec_pt, Ist_pos); + return True; + } +/* +**-------------------------------------------------------------------------- +** DoSimpleSet: Send a parameter set command to the motor and +** check for null response. +*/ + int DoSimpleSet ( +/* =========== +*/ char *a_cmnd) { + + int status, i; + char cmnd[80], buff[40]; + + sprintf (cmnd, a_cmnd, Motor); + printf ("Sending \"%s\" command ...", cmnd); + i = strlen (cmnd); + cmnd[i] = '\r'; + cmnd[i+1] = NIL; + + status = EL734_SendCmnd (&Hndl, cmnd, buff, sizeof (buff)); + if (status && (buff[0] == NIL)) { + printf (" OK.\n"); + return True; + }else if (!status) { + printf ("\n\007"); + PrintErrInfo ("EL734_SendCmnd"); + return False; + }else { + printf ("\n\007Error response from the motor: \"%s\"!\n", buff); + return False; + } + } +/* +**-------------------------------------------------------------------------- +** DoSetPos: Set the current position +*/ + int DoSetPos (float ist) { +/* ======== +*/ + + int status; + char cmnd[80]; + + sprintf (cmnd, "UU %%d %.*f", Dec_pt, ist); + status = DoSimpleSet (cmnd); + if (status) printf ("Position set to %.*f\n", Dec_pt, ist); + return status; + } +/* +**-------------------------------------------------------------------------- +** DoRef: Perform a Reference Seek +*/ + int DoRef ( +/* ===== +*/ float *shift) { + + int status, no_restore, i, len, act_len; + int msr, ored_msr, fp_cntr, fr_cntr, ss; + char recd[132], buff[132], cmnd[80]; + int k, v; + float lo, hi, q, zero_pt, targ; + float f_tmp; +/*----------------------------------------------------------------- +** Absolute encoder? +*/ + status = GetKHVQZ (&k, &lo, &hi, &v, &q, &zero_pt); + if (!status) return status; + + if (k == 0) { + printf ("\n\007Absolute encoder, K = 0, " + "\"-ref\" option is not meaningful!\n"); + return False; + }else { + printf ("Performing reference point seek.\n"); + sprintf (recd, "%.*f", Dec_pt, zero_pt); + if (k == -1 || k == -11) { + printf ("Reference point = %s (lower limit switch) ...", recd); + }else if (k == 1 || k == 11) { + printf ("Reference point = %s (upper limit switch) ...", recd); + }else if (k == 2 || k == 12) { + printf ("Reference point = %s (separate limit switch) ...", recd); + }else { + printf ("Reference point = %s (reference mode = %d (unrecognised)) ...", + recd, k); + } + } +/*---------------------------------------------------------------*/ + *shift = 0.0; + + status = EL734_GetStatus (&Hndl, + &msr, &ored_msr, &fp_cntr, &fr_cntr, &ss, &Ist_pos); + if ((!status) || (msr == -1)) { + printf ("\n\007"); + printf ("Bad status from EL734_GetStatus.\n" + " ... failed to do reference seek.\n"); + return False; + } + EL734_ZeroStatus (&Hndl); + + sprintf (cmnd, "rf %d\r", Motor); /* Start reference seek */ + status = EL734_SendCmnd (&Hndl, cmnd, buff, sizeof (buff)); + if (!status) { + printf ("\n\007" + " ... failed to initiate reference seek.\n"); + PrintErrInfo ("EL734_SendCmnd"); + return False; + }else { + if (buff[0] != NIL) { + printf ("\n\007" + " ... error response when initiating reference seek:" + " \"%s\".\n" + " Operation abandoned.\n", buff); + return False; + } + } + status = My_WaitIdle (&Hndl, Verbose, + &ored_msr, &fp_cntr, &fr_cntr, &Ist_pos); + if (!status) { + if (Ctrl_C_has_happened) { + DoStop (); + }else { + PrintErrInfo ("My_WaitIdle"); + } + return False; + } + if ((ored_msr & MSR__REF_OK) != 0) { + printf (" OK.\n"); + }else { + if ((ored_msr & MSR__REF_FAIL) != 0) { + printf ("\007 failed!\n"); + }else { + printf ("\007 unexpected MSR obtained!\n"); + } + printf (" %s\n", EL734_EncodeMSR (recd, sizeof (recd), + 0, ored_msr, fp_cntr, fr_cntr)); + return False; + } + + *shift = Ist_pos - zero_pt; + sprintf (recd, "%.*f", Dec_pt, *shift); + printf ("Position = %.*f, Zero-point error = %s\n", Dec_pt, Ist_pos, recd); + sscanf (recd, "%f", &f_tmp); + if (f_tmp != 0.0) { + sprintf (recd, "%.*f", Dec_pt, zero_pt); + printf ("\007Setting current position to be %s\n", recd); + status = DoSetPos (zero_pt); + if (!status) return False; + } + if ((zero_pt < lo) || (zero_pt > hi)) { /* Move into range? */ + if (zero_pt < lo) targ = lo; /* Yes */ + if (zero_pt > lo) targ = hi; + printf ("Moving into low-high range ...\n"); + sprintf (cmnd, "P %%d %.*f", Dec_pt, targ); + status = DoSimpleMove (cmnd, True); + } + return True; + } +/* +**-------------------------------------------------------------------------- +** DoFF: Send a FF command to the motor +*/ + int DoFF () { +/* ==== +*/ + int status; + char cmnd[32]; + + if (Frequency > 0) { + sprintf (cmnd, "FF %%d %d", Frequency); + }else { + strcpy (cmnd, "FF %d"); + } + status = DoSimpleMove (cmnd, True); + return status; + } +/* +**-------------------------------------------------------------------------- +** DoFB: Send a FB command to the motor +*/ + int DoFB () { +/* ==== +*/ + int status; + char cmnd[32]; + + if (Frequency > 0) { + sprintf (cmnd, "FB %%d %d", Frequency); + }else { + strcpy (cmnd, "FB %d"); + } + status = DoSimpleMove (cmnd, True); + return status; + } +/* +**-------------------------------------------------------------------------- +** DoSF: Send a SF command to the motor +*/ + int DoSF () { +/* ==== +*/ + int status; + char cmnd[32]; + + if (Frequency > 0) { + sprintf (cmnd, "SF %%d %d", Frequency); + }else { + strcpy (cmnd, "SF %d"); + } + status = DoSimpleMove (cmnd, True); + return status; + } +/* +**-------------------------------------------------------------------------- +** DoSB: Send a SB command to the motor +*/ + int DoSB () { +/* ==== +*/ + int status; + char cmnd[32]; + + if (Frequency > 0) { + sprintf (cmnd, "SB %%d %d", Frequency); + }else { + strcpy (cmnd, "SB %d"); + } + status = DoSimpleMove (cmnd, True); + return status; + } +/* +**-------------------------------------------------------------------------- +** DoHunt: hunt for the motor's reference point. +*/ + int DoHunt () { +/* ====== +*/ + int status; + int k; + float lo, hi, q, shift, zero; + int v; + int msr, ored_msr, fp_cntr, fr_cntr, ss; + float step, targ, f_tmp; + char cmnd[20]; + + status = GetKHVQZ (&k, &lo, &hi, &v, &q, &zero); + if (!status) return status; +/*----------------------------------------------------------------- +** Absolute encoder */ + if (k == 0) { + printf ("\n\007Absolute encoder, K = 0, " + "-hunt option is not meaningful!\n"); + return False; +/*----------------------------------------------------------------- +** Lo-Lim is Ref Pt */ + }else if ((k == -1) || (k == -11)) { + printf ("Reference point = %.*f (low limit switch)\n", Dec_pt, zero); + if (q <= 0) { + printf ("\n\007Q = %.*f. This is inconsistent with K = %d!\n", + Dec_pt, q, k); + return False; + } + status = DoSimpleSet ("H %d -8000 8000"); + if (!status) return status; + + status = DoSetPos (0.0); + if (!status) return status; + + status = DoSimpleMove ("FB %d", False); /* Do FB but don't test MSR at end */ + if (!status) return status; + + DoLimits (lo, hi); /* Reset lo/hi limits */ + + status = EL734_GetStatus (&Hndl, + &msr, &ored_msr, &fp_cntr, &fr_cntr, &ss, &Ist_pos); + if (msr == -1) { + printf ("\n\007Bad status from EL734_GetStatus!"); + return False; + } + + if ((ored_msr & MSR__LO_LIM) == 0) { + printf ("\n\007Low-limit switch was not reached!"); + return False; + } + + status = DoSetPos (zero); + if (!status) return status; + + if ((ss & SS__LSX) != 0) { + printf ("\n\007Reference-point is still active!"); + return False; + } + + status = DoRef (&shift); + return status; +/*----------------------------------------------------------------- +** Hi-Lim is Ref Pt */ + }else if ((k == 1) || (k == 11)) { + printf ("Reference point = %.*f (high limit switch)\n", Dec_pt, zero); + if (q <= 0) { + printf ("\n\007Q = %.*f. This is inconsistent with K = %d!\n", + Dec_pt, q, k); + return False; + } + status = DoSimpleSet ("H %d -8000 8000"); + if (!status) return status; + + status = DoSetPos (0.0); + if (!status) return status; + + status = DoSimpleMove ("FF %d", False); /* Do FF but don't test MSR at end */ + if (!status) return status; + + DoLimits (lo, hi); /* Reset lo/hi limits */ + + status = EL734_GetStatus (&Hndl, + &msr, &ored_msr, &fp_cntr, &fr_cntr, &ss, &Ist_pos); + if (msr == -1) { + printf ("\n\007Bad status from EL734_GetStatus!"); + return False; + } + + if ((ored_msr & MSR__HI_LIM) == 0) { + printf ("\n\007High-limit switch was not reached!"); + return False; + } + + status = DoSetPos (zero); + if (!status) return status; + + if ((ss & SS__LSX) != 0) { + printf ("\n\007Reference-point is still active!"); + return False; + } + + status = DoRef (&shift); + return status; +/*----------------------------------------------------------------- +** Separate Ref Pt */ + }else if ((k == 2) || (k == 12)) { + printf ("Reference point = %.*f (separate switch)\n", Dec_pt, zero); + if (q == 0.0) { + printf ("\n\007Q = %.*f. This is inconsistent with K = %d!\n", + Dec_pt, q, k); + return False; + }else { + sprintf (cmnd, "%.*f", Dec_pt, q); /* Check Q param is not too small */ + sscanf (cmnd, "%f", &f_tmp); + if (f_tmp == 0.0) { + printf ("\n\007Q = %f. This is too small!\n", q); + return False; + } + } + status = DoSimpleSet ("H %d -8000 8000"); + if (!status) return status; + + sprintf (cmnd, "P %%d %.*f", Dec_pt, (zero - (q/2.0))); + printf ("Moving to start position.\n"); + status = DoSimpleMove (cmnd, False); + + status = DoSetPos (0.0); + if (!status) { + DoLimits (lo, hi); + return status; + } + + step = 0.95 * q; + targ = 0.0; + printf ("Low-to-High distance = %.*f\n", Dec_pt, (hi - lo)); + printf ("Step size = %.*f\n", Dec_pt, step); + if (step > 0) { + printf ("Stepping to low-limit switch looking for ref-point ...\n"); + }else { + printf ("Stepping to high-limit switch looking for ref-point ...\n"); + } + fflush (NULL); + status = EL734_GetStatus (&Hndl, + &msr, &ored_msr, &fp_cntr, &fr_cntr, &ss, &Ist_pos); + while (status && + ((ored_msr & MSR__LO_LIM) == 0) && + ((ored_msr & MSR__HI_LIM) == 0) && + ((ss & SS__LSX) == 0)) { + targ = targ - step; + sprintf (cmnd, "P %%d %.*f", Dec_pt, targ); + status = DoSimpleMove (cmnd, False); + if (!status) { + if (Ctrl_C_has_happened) { + DoStop (); + }else { + PrintErrInfo ("DoSimpleMove"); + } + DoLimits (lo, hi); + return False; + } + status = EL734_GetStatus (&Hndl, + &msr, &ored_msr, &fp_cntr, &fr_cntr, &ss, &Ist_pos); + } + if (!status) return False; + if ((ored_msr & (MSR__LO_LIM | MSR__HI_LIM)) != 0) { + printf ("Got to limit switch. Ref-point not found. " + "Returning to Start.\n"); fflush (NULL); + status = DoSimpleMove ("P %d 0.0", False); + if (!status) { + if (Ctrl_C_has_happened) { + DoStop (); + }else { + PrintErrInfo ("DoSimpleMove"); + } + DoLimits (lo, hi); + return False; + } + targ = 0.0; + if (step > 0) { + printf ("Stepping to high-limit switch looking for ref-point ...\n"); + }else { + printf ("Stepping to low-limit switch looking for ref-point ...\n"); + } + status = EL734_GetStatus (&Hndl, + &msr, &ored_msr, &fp_cntr, &fr_cntr, &ss, &Ist_pos); + while (status && + ((ored_msr & MSR__LO_LIM) == 0) && + ((ored_msr & MSR__HI_LIM) == 0) && + ((ss & SS__LSX) == 0)) { + targ = targ + step; + sprintf (cmnd, "P %%d %.*f", Dec_pt, targ); + status = DoSimpleMove (cmnd, False); + if (!status) { + if (Ctrl_C_has_happened) { + DoStop (); + }else { + PrintErrInfo ("DoSimpleMove"); + } + DoLimits (lo, hi); + return False; + } + status = EL734_GetStatus (&Hndl, + &msr, &ored_msr, &fp_cntr, &fr_cntr, &ss, &Ist_pos); + } + if (!status) return False; + if ((ored_msr & (MSR__LO_LIM | MSR__HI_LIM)) != 0) { + printf ("\n\007Got to limit switch. Ref-point not found!\n"); + printf ("Hunt operation abandoned.\n"); + DoLimits (lo, hi); + if (step > 0) DoSetPos (hi); else DoSetPos (lo); + return False; + } + } + DoLimits (lo, hi); /* Reset lo/hi limits */ + if ((ss & SS__LSX) == 0) { + printf ("\n\007Ref-point not found!\n"); + DoLimits (lo, hi); + return False; + } + status = DoRef (&shift); + return status; + }else { + printf ("\n\007Reference Mode, K = %d. Unrecognised value!\n", k); + return False; + } + } +/* +**-------------------------------------------------------------------------- +** DoSave: Get all parameter settings of motor. +*/ + int DoSave () { +/* ====== +*/ + int status, no_errors; + char buff[80]; + int msr, ored_msr, fp_cntr, fr_cntr, ss; + int air_cush, inp_state, act_mot; + FILE *lun; + time_t time_now; + struct EL734info *info_ptr; + + char cmnd00[10], cmnd01[10], cmnd02[10], cmnd03[10], cmnd04[10]; + char cmnd05[10], cmnd06[10], cmnd07[10], cmnd08[10], cmnd09[10]; + char cmnd10[10], cmnd11[10], cmnd12[10], cmnd13[10], cmnd14[10]; + char cmnd15[10], cmnd16[10], cmnd17[10], cmnd18[10], cmnd19[10]; + char cmnd20[10], cmnd21[10], cmnd22[10], cmnd23[10], cmnd24[10]; + char cmnd25[10], cmnd26[10], cmnd27[10], cmnd28[10], cmnd29[10]; + char cmnd30[10], cmnd31[10]; + + char *rptr00, *rptr01, *rptr02, *rptr03, *rptr04; + char *rptr05, *rptr06, *rptr07, *rptr08, *rptr09; + char *rptr10, *rptr11, *rptr12, *rptr13, *rptr14; + char *rptr15, *rptr16, *rptr17, *rptr18, *rptr19; + char *rptr20, *rptr21, *rptr22, *rptr23, *rptr24; + char *rptr25, *rptr26, *rptr27, *rptr28, *rptr29; + char *rptr30, *rptr31; + + int no_EC_cmnd = True; + int no_A_cmnd = True; + int no_FD_cmnd = True; + int no_FM_cmnd = True; + int no_D_cmnd = True; + int no_E_cmnd = True; + int no_F_cmnd = True; + int no_G_cmnd = True; + int no_H_cmnd = True; + int no_J_cmnd = True; + int no_K_cmnd = True; + int no_L_cmnd = True; + int no_M_cmnd = True; + int no_Q_cmnd = True; + int no_T_cmnd = True; + int no_V_cmnd = True; + int no_W_cmnd = True; + int no_Z_cmnd = True; + int no_SP_cmnd = True; + int no_ST_cmnd = True; + int no_SR_cmnd = True; + int no_SA_cmnd = True; + int no_AC_cmnd = True; + int no_RI_cmnd = True; + int no_AM_cmnd = True; + int no_EP_cmnd = True; + int no_KP_cmnd = True; + int no_KI_cmnd = True; + int no_KD_cmnd = True; + + if ((strcmp (Save_file, "-") == 0) || + (strcmp (Save_file, "=") == 0)) { /* Use standard output? */ + lun = stdout; /* Yes */ + }else { + lun = fopen (Save_file, "w"); + if (lun == NULL) return False; + printf ("Writing motor parameters to file %s ...", Save_file); + } + time_now = time (NULL); + fprintf (lun, "! EL734 Status at %s", asctime (localtime (&time_now))); + fprintf (lun, "! ============\n"); + + sprintf (cmnd00, "id\r"); + sprintf (cmnd01, "mn %d\r", Motor); + sprintf (cmnd02, "mem %d\r", Motor); + sprintf (cmnd03, "ec %d\r", Motor); + sprintf (cmnd04, "a %d\r", Motor); + sprintf (cmnd05, "fd %d\r", Motor); + sprintf (cmnd06, "fm %d\r", Motor); + sprintf (cmnd07, "d %d\r", Motor); + sprintf (cmnd08, "e %d\r", Motor); + sprintf (cmnd09, "f %d\r", Motor); + sprintf (cmnd10, "g %d\r", Motor); + sprintf (cmnd11, "h %d\r", Motor); + sprintf (cmnd12, "j %d\r", Motor); + sprintf (cmnd13, "k %d\r", Motor); + sprintf (cmnd14, "l %d\r", Motor); + sprintf (cmnd15, "m %d\r", Motor); + sprintf (cmnd16, "q %d\r", Motor); + sprintf (cmnd17, "t %d\r", Motor); + sprintf (cmnd18, "v %d\r", Motor); + sprintf (cmnd19, "w %d\r", Motor); + sprintf (cmnd20, "z %d\r", Motor); + sprintf (cmnd21, "sp %d\r", Motor); + sprintf (cmnd22, "st %d\r", Motor); + sprintf (cmnd23, "sr %d\r", Motor); + sprintf (cmnd24, "sa %d\r", Motor); + sprintf (cmnd25, "ac %d\r", Motor); + sprintf (cmnd26, "ri %d\r", Motor); + sprintf (cmnd27, "am\r"); + sprintf (cmnd28, "ep %d\r", Motor); + sprintf (cmnd29, "kp %d\r", Motor); + sprintf (cmnd30, "ki %d\r", Motor); + sprintf (cmnd31, "kd %d\r", Motor); + + no_errors = True; + + info_ptr = (struct EL734info *) Hndl; + status = AsynSrv_SendCmnds (&info_ptr->asyn_info, + &info_ptr->to_host, &info_ptr->from_host, + cmnd00, cmnd01, cmnd02, cmnd03, cmnd04, cmnd05, cmnd06, + cmnd07, cmnd08, cmnd09, cmnd10, cmnd11, cmnd12, cmnd13, + cmnd14, cmnd15, cmnd16, cmnd17, cmnd18, cmnd19, cmnd20, + cmnd21, cmnd22, cmnd23, cmnd24, cmnd25, cmnd26, cmnd27, + cmnd28, cmnd29, cmnd30, cmnd31, NULL); + if (status) { + rptr00 = AsynSrv_GetReply (&info_ptr->asyn_info, + &info_ptr->from_host, NULL); + rptr01 = AsynSrv_GetReply (&info_ptr->asyn_info, + &info_ptr->from_host, rptr00); + rptr02 = AsynSrv_GetReply (&info_ptr->asyn_info, + &info_ptr->from_host, rptr01); + rptr03 = AsynSrv_GetReply (&info_ptr->asyn_info, + &info_ptr->from_host, rptr02); + rptr04 = AsynSrv_GetReply (&info_ptr->asyn_info, + &info_ptr->from_host, rptr03); + rptr05 = AsynSrv_GetReply (&info_ptr->asyn_info, + &info_ptr->from_host, rptr04); + rptr06 = AsynSrv_GetReply (&info_ptr->asyn_info, + &info_ptr->from_host, rptr05); + rptr07 = AsynSrv_GetReply (&info_ptr->asyn_info, + &info_ptr->from_host, rptr06); + rptr08 = AsynSrv_GetReply (&info_ptr->asyn_info, + &info_ptr->from_host, rptr07); + rptr09 = AsynSrv_GetReply (&info_ptr->asyn_info, + &info_ptr->from_host, rptr08); + rptr10 = AsynSrv_GetReply (&info_ptr->asyn_info, + &info_ptr->from_host, rptr09); + rptr11 = AsynSrv_GetReply (&info_ptr->asyn_info, + &info_ptr->from_host, rptr10); + rptr12 = AsynSrv_GetReply (&info_ptr->asyn_info, + &info_ptr->from_host, rptr11); + rptr13 = AsynSrv_GetReply (&info_ptr->asyn_info, + &info_ptr->from_host, rptr12); + rptr14 = AsynSrv_GetReply (&info_ptr->asyn_info, + &info_ptr->from_host, rptr13); + rptr15 = AsynSrv_GetReply (&info_ptr->asyn_info, + &info_ptr->from_host, rptr14); + rptr16 = AsynSrv_GetReply (&info_ptr->asyn_info, + &info_ptr->from_host, rptr15); + rptr17 = AsynSrv_GetReply (&info_ptr->asyn_info, + &info_ptr->from_host, rptr16); + rptr18 = AsynSrv_GetReply (&info_ptr->asyn_info, + &info_ptr->from_host, rptr17); + rptr19 = AsynSrv_GetReply (&info_ptr->asyn_info, + &info_ptr->from_host, rptr18); + rptr20 = AsynSrv_GetReply (&info_ptr->asyn_info, + &info_ptr->from_host, rptr19); + rptr21 = AsynSrv_GetReply (&info_ptr->asyn_info, + &info_ptr->from_host, rptr20); + rptr22 = AsynSrv_GetReply (&info_ptr->asyn_info, + &info_ptr->from_host, rptr21); + rptr23 = AsynSrv_GetReply (&info_ptr->asyn_info, + &info_ptr->from_host, rptr22); + rptr24 = AsynSrv_GetReply (&info_ptr->asyn_info, + &info_ptr->from_host, rptr23); + rptr25 = AsynSrv_GetReply (&info_ptr->asyn_info, + &info_ptr->from_host, rptr24); + rptr26 = AsynSrv_GetReply (&info_ptr->asyn_info, + &info_ptr->from_host, rptr25); + rptr27 = AsynSrv_GetReply (&info_ptr->asyn_info, + &info_ptr->from_host, rptr26); + rptr28 = AsynSrv_GetReply (&info_ptr->asyn_info, + &info_ptr->from_host, rptr27); + rptr29 = AsynSrv_GetReply (&info_ptr->asyn_info, + &info_ptr->from_host, rptr28); + rptr30 = AsynSrv_GetReply (&info_ptr->asyn_info, + &info_ptr->from_host, rptr29); + rptr31 = AsynSrv_GetReply (&info_ptr->asyn_info, + &info_ptr->from_host, rptr30); + if ((rptr00 == NULL) || (rptr01 == NULL) || (rptr02 == NULL) || + (rptr03 == NULL) || (rptr04 == NULL) || (rptr05 == NULL) || + (rptr06 == NULL) || (rptr07 == NULL) || (rptr08 == NULL) || + (rptr09 == NULL) || (rptr10 == NULL) || (rptr11 == NULL) || + (rptr12 == NULL) || (rptr13 == NULL) || (rptr14 == NULL) || + (rptr15 == NULL) || (rptr16 == NULL) || (rptr17 == NULL) || + (rptr18 == NULL) || (rptr19 == NULL) || (rptr20 == NULL) || + (rptr21 == NULL) || (rptr22 == NULL) || (rptr23 == NULL) || + (rptr24 == NULL) || (rptr25 == NULL) || (rptr26 == NULL) || + (rptr27 == NULL) || (rptr28 == NULL) || (rptr29 == NULL) || + (rptr30 == NULL) || (rptr31 == NULL)) { + no_errors = False; + }else { + StrJoin (Ctrl_id, sizeof (Ctrl_id), rptr00, ""); + StrJoin (Mot_name, sizeof (Mot_name), rptr01, ""); + StrJoin (Mot_mem, sizeof (Mot_mem), rptr02, ""); + if (sscanf (rptr03, "%d %d", &Enc_typ, &Enc_num) == 2) + no_EC_cmnd = False; + if (sscanf (rptr04, "%d", &Dec_pt) == 1) no_A_cmnd = False; + if (sscanf (rptr05, "%d %d", &Enc_fact_0, &Enc_fact_1) == 2) + no_FD_cmnd = False; + if (sscanf (rptr06, "%d %d", &Mot_fact_0, &Mot_fact_1) == 2) + no_FM_cmnd = False; + if (sscanf (rptr07, "%f", &Inertia_tol) == 1) no_D_cmnd = False; + if (sscanf (rptr08, "%d", &Ramp) == 1) no_E_cmnd = False; + if (sscanf (rptr09, "%d", &Loop_mode) == 1) no_F_cmnd = False; + if (sscanf (rptr10, "%d", &Slow_hz) == 1) no_G_cmnd = False; + if (sscanf (rptr11, "%f %f", &Lo, &Hi) == 2) no_H_cmnd = False; + if (sscanf (rptr12, "%d", &Fast_hz) == 1) no_J_cmnd = False; + if (sscanf (rptr13, "%d", &Ref_mode) == 1) no_K_cmnd = False; + if (sscanf (rptr14, "%d", &Backlash) == 1) no_L_cmnd = False; + if (sscanf (rptr15, "%d", &Pos_tol) == 1) no_M_cmnd = False; + if (sscanf (rptr16, "%f", &Ref_param) == 1) no_Q_cmnd = False; + if (sscanf (rptr17, "%d", &Is_sided) == 1) no_T_cmnd = False; + if (sscanf (rptr18, "%d", &Null_pt) == 1) no_V_cmnd = False; + if (sscanf (rptr19, "%d", &Ac_par) == 1) no_W_cmnd = False; + if (sscanf (rptr20, "%d", &Enc_circ) == 1) no_Z_cmnd = False; + if (sscanf (rptr21, "%d", &Stat_pos) == 1) no_SP_cmnd = False; + if (sscanf (rptr22, "%d", &Stat_pos_flt) == 1) no_ST_cmnd = False; + if (sscanf (rptr23, "%d", &Stat_pos_fail) == 1) + no_SR_cmnd = False; + if (sscanf (rptr24, "%d", &Stat_cush_fail) == 1) + no_SA_cmnd = False; + if (sscanf (rptr25, "%d", &air_cush) == 1) no_AC_cmnd = False; + if (sscanf (rptr26, "%d", &inp_state) == 1) no_RI_cmnd = False; + if (sscanf (rptr27, "%x", &act_mot) == 1) no_AM_cmnd = False; + if (sscanf (rptr28, "%d", &Enc_par) == 1) no_EP_cmnd = False; + if (sscanf (rptr29, "%d", &Prop) == 1) no_KP_cmnd = False; + if (sscanf (rptr30, "%d", &Integ) == 1) no_KI_cmnd = False; + if (sscanf (rptr31, "%d", &Deriv) == 1) no_KD_cmnd = False; + } + }else { + no_errors = False; + } + if (no_errors) { + EL734_GetStatus (&Hndl, + &msr, &ored_msr, &fp_cntr, &fr_cntr, &ss, &Ist_pos); + if (msr == -1) no_errors = False; + } + if (no_errors) goto ds_do; + printf ("\007"); + fprintf (lun, "!\n"); + fprintf (lun, "! Failed to get status of motor\n"); + if (lun != stdout) { + fclose (lun); + printf ("\007error detected.\n"); + } + return False; +ds_do: + if (no_K_cmnd) { + fprintf (lun, "!\n" + "! EL734 ID = \"%s\"\n" + "! Server \"%s\"\n" + "! Port %5d\n" + "! Channel %5d\n" + "! Motor %5d\n", + Ctrl_id, El734_host, El734_port, El734_chan, Motor); + }else { + fprintf (lun, "!\n" + "! Reference mode information: EL734 ID = \"%s\"\n" + "! K = -11 = LoLim + Index is ref. pt. Server \"%s\"\n" + "! -1 = LoLim is ref. pt. Port %5d\n" + "! 0 = Abs encoder Channel %5d\n" + "! 1 = HiLim is ref. pt. Motor %5d\n" + "! 2 = Separate ref. pt.\n" + "! 11 = HiLim + Index is ref. pt.\n" + "! 12 = Separate + Index ref. pt.\n", + Ctrl_id, El734_host, El734_port, El734_chan, Motor); + } + fprintf (lun, "!\n"); + if (!no_SP_cmnd) fprintf (lun, "! # of positionings, SP = %d\n", + Stat_pos); + if (!no_ST_cmnd) fprintf (lun, "! # of positioning faults, ST = %d\n", + Stat_pos_flt); + if (!no_SR_cmnd) fprintf (lun, "! # of positioning failures, SR = %d\n", + Stat_pos_fail); + if (!no_SA_cmnd) fprintf (lun, "! # of air-cushion failures, SA = %d\n", + Stat_cush_fail); + fprintf (lun, "! %s", EL734_EncodeMSR (buff, sizeof (buff), + msr, ored_msr, fp_cntr, fr_cntr)); + fprintf (lun, " %s\n", EL734_EncodeSS (buff, sizeof (buff), ss)); + if (!no_W_cmnd) { + if (Ac_par == 0) { + switch (air_cush) { + case 0: break; /* Don't mention air cushions in this case! */ + case 1: fprintf (lun, "! Air-cushion status is \"on\".\n"); break; + default: fprintf (lun, "! Air-cushion status = %d.\n", air_cush); + } + }else { + switch (air_cush) { + case 0: fprintf (lun, "! Air-cushion is \"down\"\n"); break; + case 1: fprintf (lun, "! Air-cushion is \"up\"\n"); break; + default: fprintf (lun, "! Air-cushion status = %d.\n", air_cush); + } + } + } + if (!no_RI_cmnd) { + switch (inp_state) { + case 0: fprintf (lun, "! Input status is \"off\".\n"); break; + case 1: fprintf (lun, "! Input status is \"on.\"\n"); break; + default: fprintf (lun, "! Input status = %d.\n", inp_state); + } + } + if (!no_AM_cmnd) { + if (act_mot != 0) { + fprintf (lun, "! Active motor status = 0x%03X\n", act_mot); + }else { + fprintf (lun, "! No motors are active.\n"); + } + } + fprintf (lun, "!\n"); + if (Mot_name[0] == NIL) { + sprintf (buff, " mn %%d ..............."); + }else { + sprintf (buff, " mn %%d %s", Mot_name); + } + fprintf (lun, "%-32s! %s\n", buff, "Motor name"); + if (!no_EC_cmnd) { + sprintf (buff, " ec %%d 0 0"); + fprintf (lun, "%-32s! %s\n", buff, "Zero the encoder mapping"); + sprintf (buff, " ec %%d %s", rptr03); + fprintf (lun, "%-32s! %s\n", buff, "Encoder mapping (type/number)"); + } + if (!no_EP_cmnd) { + sprintf (buff, " ep %%d %s", rptr28); + fprintf (lun, "%-32s! %s\n", buff, "Encoder magic parameter"); + } + if (!no_A_cmnd) { + sprintf (buff, " a %%d %s", rptr04); + fprintf (lun, "%-32s! %s\n", buff, "Precision"); + } + if (!no_FD_cmnd) { + sprintf (buff, " fd %%d %s", rptr05); + fprintf (lun, "%-32s! %s\n", buff, "Encoder gearing (numer/denom)"); + } + if (!no_FM_cmnd) { + sprintf (buff, " fm %%d %s", rptr06); + fprintf (lun, "%-32s! %s\n", buff, "Motor gearing (numer/denom)"); + } + if (!no_D_cmnd) { + sprintf (buff, " d %%d %s", rptr07); + fprintf (lun, "%-32s! %s\n", buff, "Inertia tolerance"); + } + if (!no_E_cmnd) { + sprintf (buff, " e %%d %s", rptr08); + fprintf (lun, "%-32s! %s\n", buff, "Start/stop ramp (kHz/sec)"); + } + if (!no_F_cmnd) { + sprintf (buff, " f %%d %s", rptr09); + fprintf (lun, "%-32s! %s\n", buff, "Open loop/Closed loop (0/1)"); + } + if (!no_G_cmnd) { + sprintf (buff, " g %%d %s", rptr10); + fprintf (lun, "%-32s! %s\n", buff, "Start/stop frequency (Mot-S/sec)"); + } + if (!no_H_cmnd) { + sprintf (buff, " h %%d %s", rptr11); + fprintf (lun, "%-32s! %s\n", buff, "Low/High Software Limits"); + } + if (!no_J_cmnd) { + sprintf (buff, " j %%d %s", rptr12); + fprintf (lun, "%-32s! %s\n", buff, "Top speed (Mot-S/sec)"); + } + if (!no_K_cmnd) { + sprintf (buff, " k %%d %s", rptr13); + fprintf (lun, "%-32s! %s\n", buff, "Reference mode"); + } + if (!no_L_cmnd) { + sprintf (buff, " l %%d %s", rptr14); + fprintf (lun, "%-32s! %s\n", buff, "Backlash/Spielausgleich (Mot-S)"); + } + if (!no_M_cmnd) { + sprintf (buff, " m %%d %s", rptr15); + fprintf (lun, "%-32s! %s\n", buff, "Position tolerance (Enc-Steps)"); + } + if (!no_Q_cmnd) { + sprintf (buff, " q %%d %s", rptr16); + fprintf (lun, "%-32s! %s\n", buff, "Reference switch width"); + } + if (!no_T_cmnd) { + sprintf (buff, " t %%d %s", rptr17); + fprintf (lun, "%-32s! %s\n", buff, "One-sided operation flag (0 = no)"); + } + if (!no_V_cmnd) { + sprintf (buff, " v %%d %s", rptr18); + fprintf (lun, "%-32s! %s\n", buff, "Null point"); + } + if (!no_W_cmnd) { + sprintf (buff, " w %%d %s", rptr19); + fprintf (lun, "%-32s! %s\n", buff, "Air-cushion dependency"); + } + if (!no_Z_cmnd) { + sprintf (buff, " z %%d %s", rptr20); + fprintf (lun, "%-32s! %s\n", buff, "Circumf. of encoder (Enc-Steps)"); + } + if (!no_KP_cmnd) { + sprintf (buff, " kp %%d %s", rptr29); + fprintf (lun, "%-32s! %s\n", buff, "Proportional"); + } + if (!no_KI_cmnd) { + sprintf (buff, " ki %%d %s", rptr30); + fprintf (lun, "%-32s! %s\n", buff, "Integral"); + } + if (!no_KD_cmnd) { + sprintf (buff, " kd %%d %s", rptr31); + fprintf (lun, "%-32s! %s\n", buff, "Differential"); + } + if (Mot_mem[0] == NIL) { + sprintf (buff, " mem %%d ..............."); + }else { + sprintf (buff, " mem %%d %s", Mot_mem); + } + fprintf (lun, "%-32s! %s\n", buff, "User data register"); + + if (Ref_mode != 0) { + fprintf (lun, "%-32s! %s\n", " restore", "Incr. encoder" + " - specify position restore"); + } + + fprintf (lun, "!\n"); + fprintf (lun, "! Current position is %.*f\n", Dec_pt, Ist_pos); + fprintf (lun, "!\n"); + if (lun != stdout) { + fclose (lun); + chmod (Save_file, 0644); + printf (" OK.\n"); + } + return True; + } +/* +**-------------------------------------------------------------------------- +** DoLoad: Load parameter settings from a file. +*/ + int DoLoad () { +/* ====== +*/ + int status, go_on, no_errors, no_restore, i, len, act_len; + int msr, ored_msr, fp_cntr, fr_cntr, ss; + FILE *lun; + char recd[132], buff[132], cmnd[80], cmnd_prnt[80]; + /* + ** Setting motor parameters usually causes the current + ** position to get lost. Read it now so that it can be + ** restored at the end if required. + */ + printf ("The current position, "); + status = EL734_GetPrecision (&Hndl, &Dec_pt); + if (status) { + EL734_GetStatus (&Hndl, + &msr, &ored_msr, &fp_cntr, &fr_cntr, &ss, &Ist_pos); + if (msr == -1) status = False; + } + if (!status) { + printf ("\n ... failed to get current position.\n"); + return False; + }else { + printf ("%.*f, can be restored at end of load operation if\n" + "a \"RESTORE\" command is given. Executing a \"U\" or a \"UU\"" + " or any motion command\n" + "will cancel the effect of a \"RESTORE\" command.\n", + Dec_pt, Ist_pos); + no_restore = True; + } + + if ((strcmp (Load_file, "-") == 0) || + (strcmp (Load_file, "=") == 0)) { /* Use standard input? */ + lun = stdin; /* Yes */ + printf ("Getting motor parameters from standard input ...\n> "); + }else { + lun = fopen (Load_file, "r"); + if (lun == NULL) { + printf ("\007Error opening file %s ... load failed.\n", Load_file); + return False; + } + printf ("Getting motor parameters from file %s ...\n", Load_file); + } + + go_on = True; + no_errors = True; + + while (go_on && (fgets (recd, sizeof (recd), lun) != NULL)) { + len = strlen (recd); + if (len <= 1) { + if (lun == stdin) {printf ("> "); fflush (NULL);} + continue; + } + if (recd[len-1] != '\n') { + recd[20] = NIL; + printf ("\007 Record not terminated by \"\\n\". " + "It is probably too long!\n" + " The record starts thus: %s ...\n" + " It has been skipped.\n", recd); + if (lun == stdin) {printf ("> "); fflush (NULL);} + continue; + } + recd[len-1] = NIL; /* Remove the terminating "\n" */ + /* + ** Strip off any trailing stuff (but keep it around so that we + ** can print it out). "Trailing stuff" is anything after a "!". + */ + act_len = strcspn (recd, "!"); + len = sizeof (buff); + StrEdit (buff, recd, "trim compress uncomment", &len); + /* + ** If the remainder is just white-space, do nothing. + */ + if (len <= 0) { + if (lun == stdin) {printf ("> "); fflush (NULL);} + continue; + } + if (strlen (buff) >= sizeof (cmnd)) { + recd[20] = NIL; + printf ("\007 Record has a dubious format!!\n" + " The record starts thus: %s ...\n" + " It has been skipped.\n", recd); + if (lun == stdin) {printf ("> "); fflush (NULL);} + continue; + } + if (sprintf (cmnd, buff, Motor) >= sizeof (cmnd)) { + fprintf (stderr, + "\007 Record has generated a command which is too long.\n" + " This may have corrupted the program. To be safe,\n" + " we are now going to do an emergency exit. Bye.\n"); + exit (False); + } + if ((lun != stdin) && (len > 0)) { /* Show user what's going on */ + strcpy (cmnd_prnt, cmnd); + MakePrint (cmnd_prnt); + printf ("%-32s%s\n", cmnd_prnt, &recd[act_len]); + } + len = sizeof (cmnd); + StrEdit (cmnd, cmnd, "upcase compress", &len); + if (strncmp (cmnd, "EXIT", 4) == 0) { + go_on = False; + continue; + }else if (strncmp (cmnd, "QUIT", 4) == 0) { + go_on = False; + continue; + }else if (strncmp (cmnd, "NO_RESTORE", 10) == 0) { + no_restore = True; + printf ("The restore operation has been suppressed via " + "the \"NO_RESTORE\" command.\n"); + }else if (strncmp (cmnd, "RESTORE", 7) == 0) { + no_restore = False; + printf ("The restore operation has been requested via " + "the \"RESTORE\" command.\n"); + }else if (strncmp (cmnd, "WAIT", 4) == 0) { + status = My_WaitIdle (&Hndl, Verbose, + &ored_msr, &fp_cntr, &fr_cntr, &Ist_pos); + if (!status) { + go_on = no_errors = False; + if (Ctrl_C_has_happened) DoStop (); + continue; + }else { + if ((ored_msr & ~(MSR__BUSY | MSR__OK)) != 0) { + printf ("! %s\n", EL734_EncodeMSR (buff, sizeof (buff), + 0, ored_msr, fp_cntr, fr_cntr)); + } + } + }else { + len = strlen (cmnd); + if (len == 2 && cmnd[0] == '\\' && cmnd[1] == 'R') len = 0; + if (len == 2 && cmnd[0] == '\\' && cmnd[1] == '0') { + cmnd[0] = NIL; /* Null command */ + }else { + cmnd[len] = '\r'; /* Terminate command with a */ + cmnd[len+1] = NIL; + } + status = EL734_SendCmnd (&Hndl, cmnd, buff, sizeof (buff)); + if (!status) { + go_on = no_errors = False; + continue; + }else { + if (buff[0] == NIL) { + len = sizeof (cmnd); + if ((strncmp (cmnd, "U ", 2) == 0) || + (strncmp (cmnd, "UU ", 3) == 0) || + (strncmp (cmnd, "P ", 2) == 0) || + (strncmp (cmnd, "PD ", 3) == 0) || + (strncmp (cmnd, "PR ", 3) == 0) || + (strncmp (cmnd, "R ", 2) == 0) || + (strncmp (cmnd, "FF ", 3) == 0) || + (strncmp (cmnd, "FB ", 3) == 0) || + (strncmp (cmnd, "SF ", 3) == 0) || + (strncmp (cmnd, "SB ", 3) == 0)) { + no_restore = True; + }else if ((strncmp (cmnd, "A ", 2) == 0) || + (strncmp (cmnd, "E ", 2) == 0) || + (strncmp (cmnd, "EP ", 3) == 0) || + (strncmp (cmnd, "F ", 2) == 0) || + (strncmp (cmnd, "G ", 2) == 0) || + (strncmp (cmnd, "J ", 2) == 0) || + (strncmp (cmnd, "K ", 2) == 0) || + (strncmp (cmnd, "L ", 2) == 0) || + (strncmp (cmnd, "M ", 2) == 0) || + (strncmp (cmnd, "T ", 2) == 0) || + (strncmp (cmnd, "V ", 2) == 0) || + (strncmp (cmnd, "W ", 2) == 0) || + (strncmp (cmnd, "Z ", 2) == 0)) { + LoadCheckOneInteger (cmnd); + if (strncmp (cmnd, "A ", 2) == 0) { + status = EL734_GetPrecision (&Hndl, &i); + if (status) Dec_pt = i; + } + }else if ((strncmp (cmnd, "EC ", 3) == 0) || + (strncmp (cmnd, "FD ", 3) == 0) || + (strncmp (cmnd, "FM ", 3) == 0)) { + LoadCheckTwoInteger (cmnd); + }else if ((strncmp (cmnd, "D ", 2) == 0)) { + LoadCheckOneFloat (cmnd, 1); /* D cmnd only has 1 Dec Place */ + }else if ((strncmp (cmnd, "Q ", 2) == 0)) { + LoadCheckOneFloat (cmnd, Dec_pt); + }else if ((strncmp (cmnd, "H ", 2) == 0)) { + LoadCheckTwoFloat (cmnd, Dec_pt); + } + }else { + if (buff[0] == '?') { + printf ("%s\n", buff); + if (lun != stdin) { /* If input from file .. */ + go_on = no_errors = False; /* .. quit */ + continue; + } + }else { + if (strncmp (cmnd, "MSR ", 4) == 0) { + sscanf (buff, "%x", &ored_msr); + printf ("%s ! %s\n", buff, + EL734_EncodeMSR (buff, sizeof (buff), + ored_msr, ored_msr, 0, 0)); + }else if (strncmp (cmnd, "SS ", 3) == 0) { + sscanf (buff, "%x", &ss); + printf ("%s ! %s\n", buff, + EL734_EncodeSS (buff, sizeof (buff), ss)); + }else { + printf ("%s\n", buff); + } + } + } + } + } + if (lun == stdin) {printf ("> "); fflush (NULL);} + } + /* + ** Restore the current motor position. + */ + if (no_errors && !no_restore) { + EL734_GetPrecision (&Hndl, &Dec_pt); + printf ("Restoring %.*f as current motor position ...\n", + Dec_pt, Ist_pos); + sprintf (cmnd, "uu %d %.*f\r", Motor, Dec_pt, Ist_pos); + status = EL734_SendCmnd (&Hndl, cmnd, buff, sizeof(buff)); + if (status) { + if (buff[0] != NIL) { + no_errors = False; + } + }else { + no_errors = False; + } + } + if (lun != stdin) fclose (lun); + if (no_errors) { + printf ("\"load\" completed.\n"); + EL734_GetStatus (&Hndl, + &msr, &ored_msr, &fp_cntr, &fr_cntr, &ss, &Ist_pos); + printf ("The motor position is %.*f\n", Dec_pt, Ist_pos); + }else { + printf ("\007Failed to load motor parameters.\n"); + } + return no_errors; + } +/* +**--------------------------------------------------------------------------- +** DoWait - wait (if necessary) +*/ + void DoWait ( +/* ====== +*/ int print_flag) { + + int my_wait, irand; + float my_rand; + + if (Wait_time == 0) return; + + if (Wait_time > 0) { + if (print_flag) printf (" waiting %d secs ...", Wait_time); + sleep (Wait_time); + if (print_flag) printf ("\n"); + return; + } + + my_wait = -Wait_time; + + irand = rand () & 0x7FFF; + my_rand = ((float) irand)/32768.0; + + my_rand = my_rand * ((float) (my_wait)); + irand = (int) (my_rand + 1); + if (print_flag) printf (" waiting %d secs ...", irand); + sleep (irand); + if (print_flag) printf ("\n"); + } +/* +**-------------------------------------------------------------------------- +** PrintUsage: Auxilliary routine for ShowUsage and ShowItemUsage +*/ + int PrintUsage (char **txt, int n_recd) { +/* ========== +*/ + int i = 0; + int ans; + + printf ("\n"); + while (i < n_recd) { + printf ("%s\n", txt[i]); + i++; + if ((i % 24 == 0) && isatty (STDOUT)) { + printf ("More? "); + ans = getchar (); + if ((ans == EOF) || (toupper (ans) == 'Q')) return False; + } + } + printf ("\n"); + } +/* +**-------------------------------------------------------------------------- +** ShowUsage: A simple help routine. +*/ + void ShowUsage (int level) { +/* ========= +*/ + char *short_help_txt[] = { +"\007To get help on running the program issue the command:", +"", +" el734_test -help"}; + + char *help_txt[] = { +" Usage: el734_test [options ...]", +"", +" Valid options are:", +" -help Generates this help text.", +" -?