/*-------------------------------------------------------------------------- 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 #include "evdriver.h" #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: strlcpy(error, "Unexpected reply from Eurotherm", iErrLen); break; case INVALIDNUMBER: strlcpy(error, "No number in Eurotherm answer", iErrLen); break; case ERRNAK: strlcpy(error, "Eurothem did NOT acknowledge command", iErrLen); break; case NOSEND: strlcpy(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; strlcpy(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; }