422 lines
11 KiB
C
422 lines
11 KiB
C
/*--------------------------------------------------------------------------
|
||
|
||
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 <string.h>
|
||
#include <stdlib.h>
|
||
#include <stdio.h>
|
||
#include <assert.h>
|
||
#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;
|
||
}
|
||
}
|