/*-------------------------------------------------------------------------- E U R O D R I V This file contains the implementation for the Eurotherm temperature controller as used at SANS. The Eurotherm is a grossly strange device which has a very weird command protocoll. The implementation here uses the EI-Bisynch Protocoll option. This has to be configured in the Eurotherm on the manual interface!! Also watch out for the unusual RS232- setting: 7 bits, even parity, 1 stop bit. Mark Koennecke, May 1999 Copyright: see copyright.h ----------------------------------------------------------------------------*/ #include #include #include #include #include #include #include #include #include typedef struct __EVDriver *pEVDriver; #include #include "hardsup/el734_def.h" #include "hardsup/el734fix.h" #include "hardsup/serialsinq.h" #include "eurodriv.h" #define INVALIDANSWER -1005 #define INVALIDNUMBER -1006 #define ERRNAK -1007 #define NOSEND -1008 /*-----------------------------------------------------------------------*/ typedef struct { void *pData; char *pHost; int iPort; int iChannel; int iLastError; } EuroDriv, *pEuroDriv; /*------------------------------------------------------------------------*/ int EuroGetParameter(void **pData, char *pPar,int iLen, float *fVal) { char pCommand[20]; char pReply[20]; char *pStart = NULL, *pEnd = NULL; int iRet,i; /* configure the serial port */ SerialATerm(pData,"1\x3"); /* ETX */ pCommand[0] = '\x4'; /* EOT */ pCommand[1] = '0'; /* GID*/ pCommand[2] = '0'; /* GID*/ pCommand[3] = '1'; /* UID*/ pCommand[4] = '1'; /* UID*/ pCommand[5] = '1'; /* CHAN */ for(i = 0; i < iLen; i++) { pCommand[6+i] = pPar[i]; } pCommand[6+iLen] = '\x5'; /* ENQ*/ pCommand[7+iLen] = '\0'; /* send */ iRet = SerialWriteRead(pData,pCommand,pReply,19); if(iRet != 1) { return iRet; } /* decode reply */ pStart = strstr(pReply,pPar); if(!pStart) { if(strstr(pReply,"?TMO")) { return EL734__BAD_TMO; } else { return INVALIDANSWER; } } iRet = sscanf(pStart+strlen(pPar),"%f",fVal); if(iRet != 1) { return INVALIDNUMBER; } return 1; } /*------------------------------------------------------------------------*/ int EuroSetParameter(void **pData, char *pPar,int iLen, char *pFormat, float fVal) { char pCommand[30]; char pNum[10]; char *pPtr, *pPtr2; char pReply[20]; char bcc; int iRet,i; /* configure the serial port */ SerialATerm(pData,"1\x06\x15"); /* ACK,NAK */ pCommand[0] = '\x04'; /* EOT */ pCommand[1] = '0'; /* GID*/ pCommand[2] = '0'; /* GID*/ pCommand[3] = '1'; /* UID*/ pCommand[4] = '1'; /* UID*/ pCommand[5] = '\x02'; pCommand[6] = '1'; /* CHAN */ for(i = 0; i < iLen; i++) { pCommand[7+i] = pPar[i]; } pPtr = pCommand + 7 + iLen; sprintf(pNum,pFormat,fVal); strcpy(pPtr,pNum); pPtr += strlen(pNum); *pPtr = '\x03'; pPtr++; /* build the checksum */ bcc = pCommand[6]; pPtr2 = &pCommand[7]; while(pPtr2 != pPtr) { bcc = bcc ^ *pPtr2; pPtr2++; } *pPtr = bcc; pPtr++; *pPtr = '\0'; /* send */ iRet = SerialSend(pData,pCommand); if(iRet != 1) { return iRet; } iRet = SerialReceiveWithTerm(pData,pReply,19,&bcc); /* printf("%s\n",pReply); */ if(iRet != 1) { return iRet; } if(bcc == '\x15') { return ERRNAK; } if(strstr(pReply,"?TMO")) { return EL734__BAD_TMO; } return 1; } /*---------------------------------------------------------------------------*/ static int GetEuroPos(pEVDriver self, float *fPos) { pEuroDriv pMe = NULL; int iRet; assert(self); pMe = (pEuroDriv)self->pPrivate; assert(pMe); iRet = EuroGetParameter(&(pMe->pData),"PV",2,fPos); if(iRet != 1) { pMe->iLastError = iRet; return 0; } return 1; } /*----------------------------------------------------------------------------*/ static int EuroRun(pEVDriver self, float fVal) { pEuroDriv pMe = NULL; int iRet; assert(self); pMe = (pEuroDriv )self->pPrivate; assert(pMe); iRet = EuroSetParameter(&(pMe->pData),"SL",2,"%4.1f",fVal); if(iRet != 1) { pMe->iLastError = iRet; return 0; } return 1; } /*--------------------------------------------------------------------------*/ static int EuroError(pEVDriver self, int *iCode, char *error, int iErrLen) { pEuroDriv pMe = NULL; assert(self); pMe = (pEuroDriv)self->pPrivate; assert(pMe); *iCode = pMe->iLastError; switch(pMe->iLastError) { case INVALIDANSWER: strncpy(error,"Unexpected reply from Eurotherm",iErrLen); break; case INVALIDNUMBER: strncpy(error,"No number in Eurotherm answer",iErrLen); break; case ERRNAK: strncpy(error,"Eurothem did NOT acknowledge command",iErrLen); break; case NOSEND: strncpy(error, "Eurotherm has a bizarre protocoll, sending things is very STUPID",iErrLen); break; default: SerialError(pMe->iLastError,error,iErrLen); break; } return 1; } /*--------------------------------------------------------------------------*/ static int EuroSend(pEVDriver self, char *pCommand, char *pReply, int iLen) { pEuroDriv pMe = NULL; assert(self); pMe = (pEuroDriv)self->pPrivate; assert(pMe); pMe->iLastError = NOSEND; strncpy(pReply,"ERROR: Eurotherm does not support send functionality", iLen); return 0; } /*--------------------------------------------------------------------------*/ static int EuroInit(pEVDriver self) { pEuroDriv pMe = NULL; int iRet; assert(self); pMe = (pEuroDriv )self->pPrivate; assert(pMe); pMe->pData = NULL; iRet = SerialOpen(&pMe->pData, pMe->pHost, pMe->iPort, pMe->iChannel); if(iRet != 1) { pMe->iLastError = iRet; return 0; } SerialSendTerm(&pMe->pData,""); return 1; } /*--------------------------------------------------------------------------*/ static int EuroClose(pEVDriver self) { pEuroDriv pMe = NULL; int iRet; assert(self); pMe = (pEuroDriv )self->pPrivate; assert(pMe); SerialClose(&pMe->pData); return 1; } /*---------------------------------------------------------------------------*/ static int EuroFix(pEVDriver self, int iError) { pEuroDriv pMe = NULL; int iRet; assert(self); pMe = (pEuroDriv )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: EuroClose(self); iRet = EuroInit(self); if(iRet) { return DEVREDO; } else { return DEVFAULT; } break; /* handable protocoll errors */ case EL734__BAD_TMO: return DEVREDO; break; case ERRNAK: case NOSEND: case INVALIDANSWER: case INVALIDNUMBER: return DEVFAULT; default: return DEVFAULT; break; } return DEVFAULT; } /*--------------------------------------------------------------------------*/ static int EuroHalt(pEVDriver *self) { assert(self); return 1; } /*------------------------------------------------------------------------*/ void KillEuro(void *pData) { pEuroDriv pMe = NULL; pMe = (pEuroDriv)pData; assert(pMe); if(pMe->pHost) { free(pMe->pHost); } free(pMe); } /*------------------------------------------------------------------------*/ pEVDriver CreateEURODriv(int argc, char *argv[]) { pEVDriver pNew = NULL; pEuroDriv pSim = NULL; /* check for arguments */ if(argc < 3) { return NULL; } pNew = CreateEVDriver(argc,argv); pSim = (pEuroDriv)malloc(sizeof(EuroDriv)); memset(pSim,0,sizeof(EuroDriv)); if(!pNew || !pSim) { return NULL; } pNew->pPrivate = pSim; pNew->KillPrivate = KillEuro; /* initalise pDILLUDriver */ pSim->iLastError = 0; pSim->pHost = strdup(argv[0]); pSim->iPort = atoi(argv[1]); pSim->iChannel = atoi(argv[2]); /* initialise function pointers */ pNew->SetValue = EuroRun; pNew->GetValue = GetEuroPos; pNew->Send = EuroSend; pNew->GetError = EuroError; pNew->TryFixIt = EuroFix; pNew->Init = EuroInit; pNew->Close = EuroClose; return pNew; }