385 lines
9.8 KiB
C
385 lines
9.8 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%5.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;
|
||
}
|
||
}
|