451 lines
13 KiB
C
451 lines
13 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 <fortify.h>
|
|
#include <sics.h>
|
|
#include <modriv.h>
|
|
#include <rs232controller.h>
|
|
#include "serialsinq.h"
|
|
#include "itc4util.h"
|
|
/*-------------------------------------------------------------------------*/
|
|
|
|
|
|
int ITC4_Open(pITC4 *pData, char *pRS232, int iSensor, int iCTRL, 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 = iCTRL;
|
|
self->iRead = iSensor;
|
|
self->iReadOnly = iMode;
|
|
self->fDiv = 10.;
|
|
self->fMult = 10.;
|
|
self->controller = NULL;
|
|
|
|
self->controller = (prs232)FindCommandData(pServ->pSics,pRS232,
|
|
"RS232 Controller");
|
|
if(!self->controller){
|
|
/*SCWrite(pCon,"ERROR: motor controller not found",eError); */
|
|
return ITC4__BADCOM;
|
|
}
|
|
|
|
/* 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;
|
|
sprintf(pCommand,"V");
|
|
iRet = transactRS232(self->controller, pCommand,strlen(pCommand),
|
|
pReply,79);
|
|
|
|
if(iRet != 1)
|
|
{
|
|
return iRet;
|
|
}
|
|
if(strstr(pReply,"ITC503") != NULL)
|
|
{
|
|
self->i503 = 1;
|
|
}
|
|
|
|
if(!self->iReadOnly)
|
|
{
|
|
/* switch to remote and unlocked operation pg48 */
|
|
sprintf(pCommand,"C3");
|
|
iRet = transactRS232(self->controller, pCommand,strlen(pCommand),
|
|
pReply,79);
|
|
if(iRet != 1)
|
|
{
|
|
return iRet;
|
|
}
|
|
if(pReply[0] == '?')
|
|
{
|
|
strcpy(self->pAns,pReply);
|
|
return ITC4__BADCOM;
|
|
}
|
|
|
|
/* Set heater and gas flow controls to manual pg51 */
|
|
sprintf(pCommand,"A0");
|
|
iRet = transactRS232(self->controller, pCommand,strlen(pCommand),
|
|
pReply,79);
|
|
if(iRet != 1)
|
|
{
|
|
return iRet;
|
|
}
|
|
if(pReply[0] == '?')
|
|
{
|
|
strcpy(self->pAns,pReply);
|
|
return ITC4__BADCOM;
|
|
}
|
|
|
|
/* Set heater sensor pg 52 */
|
|
sprintf(pCommand,"H%1.1d",self->iControl);
|
|
iRet = transactRS232(self->controller, pCommand,strlen(pCommand),
|
|
pReply,79);
|
|
if(iRet != 1)
|
|
{
|
|
return ITC4__BADCOM;
|
|
}
|
|
if(pReply[0] == '?')
|
|
{
|
|
strcpy(self->pAns,pReply);
|
|
return ITC4__BADCOM;
|
|
}
|
|
|
|
/* Display on control sensor on front panel pg51 */
|
|
sprintf(pCommand,"F%1.1d",self->iControl);
|
|
iRet = transactRS232(self->controller, pCommand,strlen(pCommand),
|
|
pReply,79);
|
|
if(iRet != 1)
|
|
{
|
|
return iRet;
|
|
}
|
|
if(pReply[0] == '?')
|
|
{
|
|
strcpy(self->pAns,pReply);
|
|
return ITC4__BADCOM;
|
|
}
|
|
|
|
/* Heater Auto, Gas Manual pg51 */
|
|
sprintf(pCommand,"A1");
|
|
iRet = transactRS232(self->controller, pCommand,strlen(pCommand),
|
|
pReply,79);
|
|
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)
|
|
{
|
|
}
|
|
/*--------------------------------------------------------------------------*/
|
|
/* Called by ansto/itc4driv.c:ConfigITC4 */
|
|
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;
|
|
|
|
/* 3 input channels see p52 */
|
|
/* BUG ALLOWS iControl=4 */
|
|
if (iControl < 1 || iControl > 3) {
|
|
return 1;
|
|
} else {
|
|
/* switch to remote and unlocked operation pg48 */
|
|
sprintf(pCommand,"C3");
|
|
iRet = transactRS232(self->controller, pCommand,strlen(pCommand), pReply,79);
|
|
if(iRet != 1)
|
|
{
|
|
return iRet;
|
|
}
|
|
if(pReply[0] == '?')
|
|
{
|
|
strcpy(self->pAns,pReply);
|
|
return ITC4__BADCOM;
|
|
}
|
|
|
|
/* Set heater and gas flow controls to manual pg51 */
|
|
sprintf(pCommand,"A0");
|
|
iRet = transactRS232(self->controller, pCommand,strlen(pCommand),
|
|
pReply,79);
|
|
if(iRet != 1)
|
|
{
|
|
return iRet;
|
|
}
|
|
if(pReply[0] == '?')
|
|
{
|
|
strcpy(self->pAns,pReply);
|
|
return ITC4__BADCOM;
|
|
}
|
|
|
|
/* Set heater sensor pg 52 */
|
|
sprintf(pCommand,"H%1.1d",iControl);
|
|
iRet = transactRS232(self->controller, pCommand,strlen(pCommand),
|
|
pReply,79);
|
|
if(iRet != 1)
|
|
{
|
|
return ITC4__BADCOM;
|
|
} else if(pReply[0] == '?')
|
|
{
|
|
strcpy(self->pAns,pReply);
|
|
return ITC4__BADCOM;
|
|
} else {
|
|
self->iControl = iControl;
|
|
}
|
|
|
|
/* Display on control sensor on front panel pg51 */
|
|
sprintf(pCommand,"F%1.1d",iControl);
|
|
iRet = transactRS232(self->controller, pCommand,strlen(pCommand),
|
|
pReply,79);
|
|
if(iRet != 1)
|
|
{
|
|
return iRet;
|
|
}
|
|
if(pReply[0] == '?')
|
|
{
|
|
strcpy(self->pAns,pReply);
|
|
return ITC4__BADCOM;
|
|
}
|
|
|
|
/* Heater Auto, Gas Manual pg51 */
|
|
sprintf(pCommand,"A1");
|
|
iRet = transactRS232(self->controller, pCommand,strlen(pCommand),
|
|
pReply,79);
|
|
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;
|
|
}
|
|
|
|
|
|
}
|
|
}
|
|
/*--------------------------------------------------------------------------*/
|
|
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 transactRS232(self->controller,pCommand,strlen(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",self->iRead);
|
|
iRet = transactRS232(self->controller, pCommand,strlen(pCommand),
|
|
pReply,79);
|
|
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",fVal);
|
|
}
|
|
else
|
|
{
|
|
fSet = fVal;
|
|
iSet = (int)(fSet*self->fMult);
|
|
sprintf(pCommand,"T%05.5d",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 = transactRS232(self->controller,pCommand,strlen(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;
|
|
}
|
|
}
|