/*------------------------------------------------------------------------- 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: strlcpy(pError, "No Connection to Bruker Controller", iErrLen); break; case MISERABLE: case BADSTATE: strlcpy(pError, "The LTC-11 is in a very bad state", iErrLen); break; case BADANSWER: strlcpy(pError, "The LTC-11 returned a bad reply", iErrLen); break; case BADCONFIRM: strlcpy(pError, "The LTC-11 did not accept the new set point", iErrLen); break; case TIMEOUT: strlcpy(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); }