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;
|
|
}
|
|
}
|