/*------------------------------------------------------------------------- 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); }