Files
sics/site_ansto/hardsup/nhq200util.c
Douglas Clowes c4f4ea358d robustness
r1402 | dcl | 2007-01-17 09:43:33 +1100 (Wed, 17 Jan 2007) | 2 lines
2012-11-15 12:56:41 +11:00

515 lines
15 KiB
C
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*--------------------------------------------------------------------------
N H Q 2 0 0 U T I L
A few utility functions for dealing with a NHQ200 voltage controller
within the SINQ setup: host -- TCP/IP -- MAC --- RS-232.
Mark Koennecke, Juli 1997
Mark Lesha, January 2006 (based on ITC4 code)
Douglas Clowes, December 2006 (based on LAKESHORE340 code)
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 <sys/socket.h>
#include <netinet/tcp.h>
#include <assert.h>
#include <fortify.h>
#include <sics.h>
#include <modriv.h>
#include <rs232controller.h>
#include "serialsinq.h"
#include "nhq200util.h"
/* -------------------------------------------------------------------------*/
static int writeNHQ200(prs232 rs232, void *data, int dataLen)
{
int i, iRet;
char reply[1];
int replyLen;
char* request = (char *) data;
char crlf[3] = {0x0D, 0x0A, 0x00};
do {
for (i = 0; i < dataLen; ++i)
{
iRet = NETWrite(rs232->pSock, request + i, 1);
if (iRet <= 0)
{
return BADSEND;
}
replyLen = 1;
iRet = readRS232(rs232, reply, &replyLen);
if(iRet == 0)
{
return TIMEOUT;
}
else if(iRet == -1)
{
return INCOMPLETE;
}
if (replyLen != 1)
printf("replyLen = %d, expected 1\n", replyLen);
else if (reply[0] != request[i])
printf("reply[%d] = %x, expected %x\n", i, reply[i], request[i]);
else
printf("reply[%d] = %x, sent %x\n", i, reply[i], request[i]);
}
if (request == crlf)
break;
if (dataLen >= 2 && memcmp(request + dataLen - 2, crlf, 2) == 0)
break;
request = crlf;
dataLen = 2;
} while (1);
return 1;
}
/*------------------------------------------------------------------------*/
int transactNHQ200(prs232 self, void *send, int sendLen,
void *reply, int replyLen)
{
int iRet, len;
assert(self);
/*
if there is still data on the socket: clear it
*/
while(availableRS232(self)){
len = replyLen;
readRS232(self,reply,&len);
}
/*
write data
*/
iRet = writeNHQ200(self,send,sendLen);
if(iRet <= 0)
{
return BADSEND;
}
/*
read
*/
memset(reply,0,replyLen);
iRet = NETReadTillTerm(self->pSock,self->timeout,self->replyTerminator,
reply, replyLen);
if(self->debug > 0)
{
if(iRet > 0)
{
printf("RS232 IN/TRANS: %s",(char *)reply);
if(strchr((char *)reply,'\n') == NULL)
{
puts("");
}
fflush(stdout);
}
else if(iRet == 0)
{
printf("RS232 IN/TRANS: TIMEOUT\n");
}
else
{
printf("RS232 IN/TRANS/INCOMPLETE: %s",(char *)reply);
}
}
if(iRet == 0)
{
return TIMEOUT;
}
else if(iRet == -1)
{
return INCOMPLETE;
}
else
{
return iRet;
}
}
/*------------------------------------------------------------------------*/
int NHQ200_Check_Status(pNHQ200 self)
/* Can be called to check for correct operation of the NHQ200 */
{
int iRet, iRetry, busy, notbusy;
char pCommand[20];
char pReply[132];
/* Check the busy status. */
/* While busy, wait but not too long - set an upper limit of about 100 queries, */
/* should translate to about 1 or 2 seconds which is enough time for any commands */
/* to complete. Since we don't issue any time-consuming commands, this shouldn't */
/* be necessary anyway. */
/* Register a comms failure if a not-busy response isn't able to be received. */
iRetry=0;
printf("Checking status...");
do
{
sprintf(pCommand,"S1");
usleep(50000); // Required to meet Lakeshore340 spec.
if ((iRet=transactNHQ200(self->controller,pCommand,strlen(pCommand),pReply,79))<=0)
{
printf("Comms error!\n");
return iRet; // Comms problem
}
busy=(strncmp(pReply,"S1=ON",15)!=0);
notbusy=!busy;
if (notbusy)
{
printf("Status OK.\n");
return 1; // Lakeshore 340 is ready to accept command
}
} while((++iRetry<100)&&busy);
/* If we fell out of the loop, the Lakeshore 340 is either still busy */
/* or some bad response was received, log the response. */
sprintf(self->pAns,"BUSY response=%s",pReply);
printf("Busy or bad response received!\n");
return NHQ200__BADREAD;
}
static
int NHQ200_ConfigureAndQueryGen(pNHQ200 self, char *command,
char *configandqueryparameters, char *configonlyparameters,char *diagnosis)
/* Issue a command to the Lakeshore 340, if this works, */
/* issue the corresponding query and check the result is correct. */
/* Log and return any errors. */
{
int iRet;
char pCommand[20];
char pReply[132];
/* Issue a command to the Lakeshore 340. */
if (configandqueryparameters&&*configandqueryparameters)
sprintf(pCommand,"%s %s,%s",command,configandqueryparameters,configonlyparameters);
else
sprintf(pCommand,"%s %s",command,configonlyparameters);
printf("Issuing command: '%s'...\n",pCommand);
usleep(50000); // Required to meet Lakeshore340 spec.
if ((iRet=writeNHQ200(self->controller,pCommand,strlen(pCommand)))!=1)
return iRet;
/* Issue the query corresponding to the command, */
/* then check the parameters match what was set. */
if (configandqueryparameters&&*configandqueryparameters)
sprintf(pCommand,"%s? %s",command,configandqueryparameters);
else
sprintf(pCommand,"%s?",command);
printf("Issuing query: '%s'...\n",pCommand);
usleep(50000); // Required to meet Lakeshore340 spec.
if ((iRet=transactNHQ200(self->controller,pCommand,strlen(pCommand),pReply,79))<=0)
{
printf("transactNHQ200 error! Code=%d.\n",iRet);
printf("DEBUG: pReply='%s' len=%d configonlyparameters='%s' len=%d\n",
pReply,strlen(pReply),configonlyparameters,strlen(configonlyparameters));
return iRet;
}
/* Query should return the same data parameters set in the configuration
(i.e. the configonlyparameters), the configandqueryparameters are used
to index objects and are common to both configuration and query. */
printf("DEBUG: pReply='%s' len=%d configonlyparameters='%s' len=%d\n",
pReply,strlen(pReply),configonlyparameters,strlen(configonlyparameters));
if (strcmp(pReply,configonlyparameters)!=0)
{
printf("Response was bad.\n");
if (diagnosis&&*diagnosis)
sprintf(self->pAns,"%s response=%s (%s.)",command,pReply,diagnosis);
else
sprintf(self->pAns,"%s response=%s",command,pReply);
return NHQ200__BADREAD;
}
printf("Response was good.\n");
return 1;
}
static
int NHQ200_ConfigureAndQuery(pNHQ200 self, char *command,
char *parameters, char *diagnosis)
/* Use for config/query transactions that don't require index parameters in the query. */
{
return NHQ200_ConfigureAndQueryGen(self,command,"",parameters,diagnosis);
}
static
int NHQ200_SetControl(pNHQ200 self, int iControl)
{
}
static
int NHQ200_Setup(pNHQ200 self, int iControl) /* Operations common to both Open and Config functions */
{
int iRet;
char pCommand[20];
char pReply[132];
if (!self || !self->controller || !self->controller->pSock)
return NHQ200__BADCOM;
/* MJL enable RS232 debugging mode. */
setRS232Debug(self->controller,1);
printf("***RS232 debug mode enabled for NHQ200***\n");fflush(stdout);
int sock = self->controller->pSock->sockid;
int flag = 1;
int result = setsockopt(sock, /* socket affected */
IPPROTO_TCP, /* set option at TCP level */
TCP_NODELAY, /* name of option */
(char *) &flag, /* the cast is historical cruft */
sizeof(int)); /* length of option value */
if (result < 0)
return NHQ200__BADCOM;
return 1; /* Success */
}
int NHQ200_Open(pNHQ200 *pData, char *pRS232, int iSensor, int iCTRL, int iMode)
{
pNHQ200 self = NULL;
self = (pNHQ200)malloc(sizeof(NHQ200));
if(self == NULL)
{
return NHQ200__BADMALLOC;
}
*pData = self;
self->iControl = iCTRL;
self->iRead = iSensor;
self->iReadOnly = iMode;
/* The NHQ200 doesn't require divisors or multipliers
and they are always forced to 1.0 */
self->fDiv = 1.0;
self->fMult = 1.0;
self->controller = NULL;
self->controller = (prs232)FindCommandData(pServ->pSics,pRS232,
"RS232 Controller");
if(!self->controller){
/*SCWrite(pCon,"ERROR: motor controller not found",eError); */
return NHQ200__BADCOM;
}
if(!self->iReadOnly)
return NHQ200_Setup(self, self->iControl);
}
/*--------------------------------------------------------------------------*/
void NHQ200_Close(pNHQ200 *pData)
{
pNHQ200 self;
self = *pData;
if (!self)
return; // Just in case
/* Try to turn off the heater as a precaution.
*/
/* switch off remote operation */
/* Not sure if this is really necessary but do it just in case */
/*
*/
return;
}
/*--------------------------------------------------------------------------*/
int NHQ200_Config(pNHQ200 *pData, int iTmo, int iRead, int iControl,
float fDiv,float fMult)
{
pNHQ200 self;
self = *pData;
return NHQ200_Setup(self, iControl);
}
/* --------------------------------------------------------------------------*/
int NHQ200_Send(pNHQ200 *pData, char *pCommand, char *pReply, int iLen)
{
int iRet,i,commandlen,isquery;
pNHQ200 self;
self = *pData;
/* Send command direct to the NHQ200 */
commandlen=strlen(pCommand);
usleep(50000); // Required to meet Lakeshore340 spec.
iRet=transactNHQ200(self->controller,pCommand,commandlen,pReply,iLen);
return iRet;
}
/*--------------------------------------------------------------------------*/
int NHQ200_Read(pNHQ200 *pData, float *fVal)
{
char pCommand[20], pReply[132];
int iRet;
float fRead = -9999999.;
pNHQ200 self;
self = *pData;
/* for the NHQ200 there are three temp measurements available */
/* as 'A', 'B', 'C' and 'D'. (This is a bit different from the manual) */
/* We use the Kelvin readings (presumably the cryogenic 'furnace' */
/* operates at low temperatures, so it probably makes more sense) */
switch(self->iRead)
{
case 1:
sprintf(pCommand,"U1");
break;
case 2:
sprintf(pCommand,"U2");
break;
default:
return NHQ200__BADPAR; // But shouldn't happen
}
usleep(50000); // Required to meet Lakeshore340 spec.
iRet = transactNHQ200(self->controller,pCommand,strlen(pCommand),pReply,79);
if (iRet <= 0)
return iRet;
iRet = sscanf(pReply,"%g",&fRead);
if(iRet != 1) // Not a number, probably an error response
{
return NHQ200__BADREAD;
}
*fVal = fRead;
return iRet;
}
/* -------------------------------------------------------------------------*/
int NHQ200_Set(pNHQ200 *pData, float fVal)
{
char pCommand[20], pCommandRead[20], pReply[132], pCommandGo[20];
int iRet, i;
const float fPrecision = 0.1;
float fDelta, fRead;
pNHQ200 self;
self = *pData;
if(self->iReadOnly)
{
return NHQ200__READONLY;
}
/* Note we are using control loop #1 only for temperature control. */
sprintf(pCommand,"D%d=%d", self->iControl, (int) fVal);
sprintf(pCommandRead,"D%d", self->iControl); // To read back and check the set value
/* try three times: send, read, test, if OK return, else resend. */
/* MJL doesn't think this is necessary... left over from itc4 */
for(i = 0; i < 3; i++)
{
/* send Dn=nnn command, we get a blank line response */
usleep(50000); // Required to meet Lakeshore340 spec.
if ((iRet=transactNHQ200(self->controller,pCommand,strlen(pCommand),pReply,131))<=0)
return iRet;
/* read the set value again using the Dn command */
usleep(50000); // Required to meet Lakeshore340 spec.
if ((iRet=transactNHQ200(self->controller,pCommandRead,strlen(pCommandRead),pReply,131))<=0)
return iRet;
printf("Dn: Response %d characters: '%s'\n",iRet,pReply);
if(pReply[0] == '-'&&strlen(pReply)>7)
{
strcpy(self->pAns,pReply);
return NHQ200__BADCOM;
}
/* Convert the value read back. */
/* Note SETP? will return free-format exponentiated number, so use %g. */
if(sscanf(pReply,"%g",&fRead)!=1)
return NHQ200__BADREAD;
printf(" Parsed response OK, value=%g\n",fRead);
/* check the value read back */
printf(" Setpoint=%g actual=%g\n",fVal,fRead);
fDelta = fRead - fVal;
if(fDelta < 0)
fDelta = -fDelta;
printf(" delta=%g precision=%g\n",fDelta,fPrecision);
if(fDelta < fPrecision)
{
/* Success, but check the NHQ200 operating status afterwards, and return */
/* Don't bother to repeat the write if we get an error here as it would indicate
a fault or overload in the NHQ200, not a comms problem */
sprintf(pCommandGo, "G%d", self->iControl);
if ((iRet=transactNHQ200(self->controller,pCommandGo,strlen(pCommandGo),pReply,131))<=0)
return iRet;
printf("SET OK, checking status and returning.\n");
return 1;
}
}
printf("SETP failed!\n");
return NHQ200__BADSET;
}
/* -------------------------------------------------------------------------*/
void NHQ200_ErrorTxt(pNHQ200 *pData,int iCode, char *pError, int iLen)
{
char pBueffel[512];
pNHQ200 self;
self = *pData;
switch(iCode)
{
case NHQ200__BADCOM:
sprintf(pBueffel,"NHQ200: Invalid command or offline, got %s",
self->pAns);
strncpy(pError,pBueffel,iLen);
break;
case NHQ200__BADPAR:
strncpy(pError,"NHQ200: Invalid parameter specified",iLen);
break;
case NHQ200__BADMALLOC:
strncpy(pError,"NHQ200: Error allocating memory in NHQ200",iLen);
break;
case NHQ200__BADREAD:
strncpy(pError,"NHQ200: Badly formatted answer",iLen);
break;
case NHQ200__BADSET:
strncpy(pError,"NHQ200: Failed three times to write new set value to NHQ200",iLen);
break;
case NHQ200__FAULT: // Covers various NHQ200 self-diagnosed fault conditions
sprintf(pBueffel,"NHQ200: Internal fault condition detected: %s",self->pAns);
strncpy(pError,pBueffel,iLen);
break;
case NHQ200__NONHQ200:
sprintf(pBueffel,"NHQ200: Wrong model number (driver is for Model 340 only): %s",self->pAns);
strncpy(pError,pBueffel,iLen);
break;
default:
SerialError(iCode, pError,iLen);
break;
}
}