diff --git a/site_ansto/Makefile b/site_ansto/Makefile index 7f836a04..16e66905 100644 --- a/site_ansto/Makefile +++ b/site_ansto/Makefile @@ -70,7 +70,8 @@ OBJ= site_ansto.o anstoutil.o\ motor_asim.o motor_dmc2280.o\ lh45.o lh45driv.o \ lakeshore340.o lakeshore340driv.o \ - counterdriv.o\ + nhq200.o nhq200driv.o \ + counterdriv.o\ ../psi/tcpdocho.o ../psi/tcpdornier.o \ anstohttp.o \ hmcontrol_ansto.o diff --git a/site_ansto/hardsup/makefile b/site_ansto/hardsup/makefile index 59f99ce1..1ee4416b 100644 --- a/site_ansto/hardsup/makefile +++ b/site_ansto/hardsup/makefile @@ -10,7 +10,7 @@ SRC = . CC = gcc CFLAGS = -g -DLINUX $(DFORTIFY) -I$(SRC) -I../.. -HOBJ= serialsinq.o itc4util.o lh45util.o lakeshore340util.o asynsrv_utility.o geterrno.o strjoin.o +HOBJ= serialsinq.o nhq200util.o itc4util.o lh45util.o lakeshore340util.o asynsrv_utility.o geterrno.o strjoin.o libhlib.a: $(HOBJ) rm -f libhlib.a diff --git a/site_ansto/hardsup/nhq200util.c b/site_ansto/hardsup/nhq200util.c new file mode 100644 index 00000000..dbc7e8a4 --- /dev/null +++ b/site_ansto/hardsup/nhq200util.c @@ -0,0 +1,511 @@ +/*-------------------------------------------------------------------------- + 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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]; + + /* 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; + } +} diff --git a/site_ansto/hardsup/nhq200util.h b/site_ansto/hardsup/nhq200util.h new file mode 100644 index 00000000..43126e61 --- /dev/null +++ b/site_ansto/hardsup/nhq200util.h @@ -0,0 +1,131 @@ +/*--------------------------------------------------------------------------- + N H Q 2 0 0 U T I L + + A few utility functions for talking to a NHQ 200 + voltage controller via the SINQ setup: TCP/IP--MAC--RS-232-- + NHQ200. + + Mark Koennecke, Juli 1997 + Mark Lesha, January 2006 (based on ITC4 code) + Douglas Clowes, December 2006 (based on LAKESHORE340 code) + + ----------------------------------------------------------------------------*/ +#ifndef SINQNHQ200 +#define SINQNHQ200 + +/*----------------------- ERRORCODES-------------------------------------- + Most functions return a negative error code on failure. Error codes + defined are those defined for serialsinq plus a few additional ones: + */ + +#define NHQ200__BADCOM -501 +/* command not recognized */ +#define NHQ200__BADPAR -502 +/* bad parameter to command */ +#define NHQ200__BADMALLOC -503 +/* error allocating memory */ +#define NHQ200__BADREAD -504 +/* error analysing command string on Read */ +#define NHQ200__FAULT -505 +/* fault or overload condition exists in NHQ200 */ +#define NHQ200__NONHQ200 -510 +/* Controller is not NHQ200 */ +#define NHQ200__BADSET -530 +/* failed three times to set temperature */ +#define NHQ200__READONLY -531 +/*------------------------------------------------------------------------*/ +typedef struct __NHQ200 { + int iRead; + int iControl; + void *pData; + char pAns[80]; /* should be enough for NHQ200 errors */ + /* The NHQ200 does not need multipliers or dividers but + leave them in anyway for back compatibility or future use + but force the value to 1.0 all the time */ + float fDiv; + float fMult; + int iReadOnly; + prs232 controller; +} NHQ200; + +typedef struct __NHQ200 *pNHQ200; + +/*-----------------------------------------------------------------------*/ +int NHQ200_Open(pNHQ200 *pData,char *pHost, int iPort, int iChannel, int iMode); +/***** creates an NHQ200 datastructure and opens a connection to the NHQ200 + controller. Input Parameters are: + the hostname + the port number + the RS-232 channel number on the Mac. +iMode: 1 for ReadOnly, 0 for normal mode + +Return values are 1 for success, a negative error code on +failure. + + */ + +void NHQ200_Close(pNHQ200 *pData); +/****** close a connection to an NHQ200controller and frees its + data structure. The only parameter is a pointer to the data + structure for this controller. This pointer will be invalid after + this call. + */ + +int NHQ200_Config(pNHQ200 *pData, int iTmo, int iRead, + int iControl, float fDiv, float fMult); +/***** configure some aspects of a NHQ200temperature controller. + The parameter are: + - a pointer to the data structure for the controller as + returned by NHQ200_Open + - a value for the connection timeout + - the temperature sensor to use for reading the + temperature. + - the temperature sensor used by the NHQ200controller + for regulating the temperature. + - the divisor needed to calculate the real temperature + from the sensor. + The function returns 1 on success, a negative error code on + failure. + */ + +int NHQ200_Send(pNHQ200 *pData, char *pCommand, char *pReply, int iLen); +/******* send a the command in pCommand to the NHQ200controller. + A possible reply is returned in the buffer pReply. + Maximum iLen characters are copied to pReply. + The first parameter is a pointer to a NHQ200data structure + as returned by NHQ200_Open. + + Return values are 1 for success, a negative error code on + failure. + */ + +int NHQ200_Read(pNHQ200 *pData, float *fVal); +/******* reads the current actual temperature of the sensor + configured by ConfigNHQ200for reading. The value is returned + in fVal. The first parameter is a pointer to a NHQ200 + data structure as returned by NHQ200_Open. + + Return values are 1 for success, a negative error code on + failure. + */ + +int NHQ200_Set(pNHQ200 *pData, float fVal); +/****** sets a new preset temperature in the NHQ200temperature + controller. Parameters are: + - a pointer to a NHQ200data structure as returned by NHQ200_Open. + - the new preset value. + + Return values are 1 for success, a negative error code on + failure. + */ + +void NHQ200_ErrorTxt(pNHQ200 *pData, int iCode, char *pError, int iLen); +/******* translates one of the negative error NHQ200error codes + into text. Maximum iLen bytes will be copied to the + buffer pError; + */ + + +#endif + + diff --git a/site_ansto/nhq200.c b/site_ansto/nhq200.c new file mode 100644 index 00000000..c7105ad8 --- /dev/null +++ b/site_ansto/nhq200.c @@ -0,0 +1,285 @@ +/*--------------------------------------------------------------------------- + N H Q 2 0 0 + + This is the implementation for a NHQ200 object derived from a more general + environment controller. + + Mark Koennecke, August 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "nhq200.h" + +/*---------------------------------------------------------------------------*/ +int NHQ200SetPar(pEVControl self, char *name, float fNew, SConnection *pCon) +{ + int iRet; + + /* check authorisation */ + if(!SCMatchRights(pCon,usUser)) + { + SCWrite(pCon,"ERROR: you are not authorised to change this parameter", + eError); + return 0; + } + + /* just catch those three names which we understand */ + if(strcmp(name,"sensor") == 0) + { + iRet = SetSensorNHQ200(self->pDriv,(int)fNew); + if(!iRet) + { + SCWrite(pCon,"ERROR: value out of range",eError); + return 0; + } + iRet = ConfigNHQ200(self->pDriv); + if(iRet != 1) + { + SCWrite(pCon,"ERROR: NHQ200 configuration failed! ",eError); + SCWrite(pCon,"INFO: Probably comm problem, Retry!",eError); + return 0; + } + SCSendOK(pCon); + return 1; + } + else if(strcmp(name,"control") == 0) + { + iRet = SetControlNHQ200(self->pDriv,(int)fNew); + if(!iRet) + { + SCWrite(pCon,"ERROR: value out of range",eError); + return 0; + } + iRet = ConfigNHQ200(self->pDriv); + if(iRet != 1) + { + SCWrite(pCon,"ERROR: NHQ200 configuration failed! ",eError); + SCWrite(pCon,"INFO: Probably comm problem, Retry!",eError); + return 0; + } + SCSendOK(pCon); + return 1; + } + else if(strcmp(name,"timeout") == 0) + { + iRet = SetTMONHQ200(self->pDriv,(int)fNew); + if(!iRet) + { + SCWrite(pCon,"ERROR: value out of range",eError); + return 0; + } + iRet = ConfigNHQ200(self->pDriv); + if(iRet != 1) + { + SCWrite(pCon,"ERROR: NHQ200 configuration failed! ",eError); + SCWrite(pCon,"INFO: Probably comm problem, Retry!",eError); + return 0; + } + SCSendOK(pCon); + return 1; + } + /* Note divisors and multipliers will not be used with the NHQ200 + but leave in for back compatibility */ + else if(strcmp(name,"divisor") == 0) + { + iRet = SetDivisorNHQ200(self->pDriv,fNew); + if(!iRet) + { + SCWrite(pCon,"ERROR: value out of range",eError); + return 0; + } + iRet = ConfigNHQ200(self->pDriv); + if(iRet != 1) + { + SCWrite(pCon,"ERROR: NHQ200 configuration failed! ",eError); + SCWrite(pCon,"INFO: Probably comm problem, Retry!",eError); + return 0; + } + SCSendOK(pCon); + return 1; + } + else if(strcmp(name,"multiplicator") == 0) + { + iRet = SetMultNHQ200(self->pDriv,fNew); + if(!iRet) + { + SCWrite(pCon,"ERROR: value out of range",eError); + return 0; + } + iRet = ConfigNHQ200(self->pDriv); + if(iRet != 1) + { + SCWrite(pCon,"ERROR: NHQ200 configuration failed! ",eError); + SCWrite(pCon,"INFO: Probably comm problem, Retry!",eError); + return 0; + } + SCSendOK(pCon); + return 1; + } + else + return EVCSetPar(self,name,fNew,pCon); +} +/*--------------------------------------------------------------------------*/ +int NHQ200GetPar(pEVControl self, char *name, float *fNew) +{ + int iRet; + float fDiv; + + /* just catch those two names which we understand */ + if(strcmp(name,"sensor") == 0) + { + iRet = GetSensorNHQ200(self->pDriv); + *fNew = (float)iRet; + return 1; + } + else if(strcmp(name,"control") == 0) + { + iRet = GetControlNHQ200(self->pDriv); + *fNew = (float)iRet; + return 1; + } + else if(strcmp(name,"timeout") == 0) + { + iRet = GetTMONHQ200(self->pDriv); + *fNew = (float)iRet; + return 1; + } + else if(strcmp(name,"divisor") == 0) + { + fDiv = GetDivisorNHQ200(self->pDriv); + *fNew = fDiv; + return 1; + } + else if(strcmp(name,"multiplicator") == 0) + { + fDiv = GetMultNHQ200(self->pDriv); + *fNew = fDiv; + return 1; + } + else + return EVCGetPar(self,name,fNew); +} +/*---------------------------------------------------------------------------*/ +int NHQ200List(pEVControl self, SConnection *pCon) +{ + char pBueffel[132]; + int iRet; + + iRet = EVCList(self,pCon); + sprintf(pBueffel,"%s.sensor = %d\n",self->pName, + GetSensorNHQ200(self->pDriv)); + SCWrite(pCon,pBueffel,eValue); + sprintf(pBueffel,"%s.control = %d\n",self->pName, + GetControlNHQ200(self->pDriv)); + SCWrite(pCon,pBueffel,eValue); + sprintf(pBueffel,"%s.timeout = %d\n",self->pName, + GetTMONHQ200(self->pDriv)); + SCWrite(pCon,pBueffel,eValue); + sprintf(pBueffel,"%s.divisor = %f\n",self->pName, + GetDivisorNHQ200(self->pDriv)); + SCWrite(pCon,pBueffel,eValue); + sprintf(pBueffel,"%s.multiplicator = %f\n",self->pName, + GetMultNHQ200(self->pDriv)); + SCWrite(pCon,pBueffel,eValue); + return iRet; +} +/*-------------------------------------------------------------------------*/ +int NHQ200Wrapper(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]) +{ + pEVControl self = NULL; + char pBueffel[256]; + int iRet; + double fNum; + float fVal; + + self = (pEVControl)pData; + assert(self); + assert(pCon); + assert(pSics); + + if(argc < 2) + { + return EVControlWrapper(pCon,pSics,pData,argc,argv); + } + + strtolower(argv[1]); + if((strcmp(argv[1],"sensor") == 0) || (strcmp(argv[1],"control") == 0) || + (strcmp(argv[1],"timeout") == 0) || (strcmp(argv[1],"divisor") == 0) || + (strcmp(argv[1],"multiplicator") == 0) ) + { + if(argc > 2) /* set case */ + { + iRet = Tcl_GetDouble(pSics->pTcl,argv[2],&fNum); + if(iRet != TCL_OK) + { + sprintf(pBueffel,"ERROR: expected number, got %s",argv[2]); + SCWrite(pCon,pBueffel,eError); + return 0; + } + return NHQ200SetPar(self,argv[1],(float)fNum,pCon); + } + else /* get case */ + { + iRet = NHQ200GetPar(self,argv[1],&fVal); + sprintf(pBueffel,"%s.%s = %f\n",self->pName, + argv[1],fVal); + SCWrite(pCon,pBueffel,eValue); + return 1; + } + } + else if(strcmp(argv[1],"list") == 0) + { + return NHQ200List(self,pCon); + } + else + { + return EVControlWrapper(pCon,pSics,pData,argc,argv); + } + /* not reached */ + return 0; +} diff --git a/site_ansto/nhq200.h b/site_ansto/nhq200.h new file mode 100644 index 00000000..4d5b0bc6 --- /dev/null +++ b/site_ansto/nhq200.h @@ -0,0 +1,49 @@ +/*------------------------------------------------------------------------- + NHQ 200 + + Support for NHQ 200 Voltage controllers for SICS. + The meaning and working of the functions defined is as desribed for a + general environment controller. + + Mark Koennecke, Juli 1997 + Mark Lesha, January 2006 (based on ITC4 code) + Douglas Clowes, December 2006 (based on LAKESHORE340 code) + + copyright: see implementation file. + + -----------------------------------------------------------------------------*/ +#ifndef SICSNHQ200 +#define SICSNHQ200 +/*------------------------- The Driver ------------------------------------*/ + +pEVDriver CreateNHQ200Driver(int argc, char *argv[]); +int ConfigNHQ200(pEVDriver self); +int SetSensorNHQ200(pEVDriver self, int iSensor); +int SetControlNHQ200(pEVDriver self, int iSensor); +int GetSensorNHQ200(pEVDriver self); +int GetControlNHQ200(pEVDriver self); +/* Divisors and multipliers should not be applicable to the NHQ200 + since it uses floating point variables, but we leave the functions + in here anyway for the time being (setting will have no effect and + a value of 1.0 will always be returned. */ +int SetDivisorNHQ200(pEVDriver self, float iSensor); +float GetDivisorNHQ200(pEVDriver self); +int SetMultNHQ200(pEVDriver self, float iSensor); +float GetMultNHQ200(pEVDriver self); +/* Leave in time-out functionality */ +int SetTMONHQ200(pEVDriver self, int iSensor); +int GetTMONHQ200(pEVDriver self); + + +/*------------------------- The NHQ200 object ------------------------------*/ + +int NHQ200Wrapper(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]); +int NHQ200SetPar(pEVControl self, char *name, float fNew, + SConnection *pCon); +int NHQ200GetPar(pEVControl self, char *name, float *fVal); +int NHQ200List(pEVControl self, SConnection *pCon); + + +#endif + diff --git a/site_ansto/nhq200driv.c b/site_ansto/nhq200driv.c new file mode 100644 index 00000000..6598ad27 --- /dev/null +++ b/site_ansto/nhq200driv.c @@ -0,0 +1,488 @@ +/*-------------------------------------------------------------------------- + N H Q 2 0 0 D R I V + + This file contains the implementation of a driver for the + NHQ 200 Voltage controller. + + + 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 +#include +#include +#include +#include +#include +#include +#include +#include + +typedef struct __EVDriver *pEVDriver; + +#include +/* Do we need these ? +#include +#include + */ +#include +#include "hardsup/nhq200util.h" +#include "hardsup/el734_def.h" +#include "hardsup/el734fix.h" + +#define SHITTYVALUE -777 +/*------------------------- The Driver ------------------------------------*/ + +pEVDriver CreateNHQ200Driver(int argc, char *argv[]); +int ConfigNHQ200(pEVDriver self); + + +/*-----------------------------------------------------------------------*/ +typedef struct { + pNHQ200 pData; + char *pHost; + int iPort; + int iChannel; + int iControl; /* NHQ200 control */ + float fDiv; + float fMult; + int iRead; /* NHQ200 sensor */ + int iTmo; + int iLastError; +} NHQ200Driv, *pNHQ200Driv; +/*----------------------------------------------------------------------------*/ +static int GetNHQ200Pos(pEVDriver self, float *fPos) +{ + pNHQ200Driv pMe = NULL; + int iRet; + + assert(self); + pMe = (pNHQ200Driv)self->pPrivate; + assert(pMe); + + iRet = NHQ200_Read(&pMe->pData,fPos); + if(iRet <= 0 ) + { + pMe->iLastError = iRet; + return 0; + } + if( (*fPos < 0) || (*fPos > 10000) ) + { + *fPos = -999.; + pMe->iLastError = SHITTYVALUE; + return 0; + } + return 1; +} +/*----------------------------------------------------------------------------*/ +static int NHQ200Run(pEVDriver self, float fVal) +{ + pNHQ200Driv pMe = NULL; + int iRet; + + assert(self); + pMe = (pNHQ200Driv )self->pPrivate; + assert(pMe); + + iRet = NHQ200_Set(&pMe->pData,fVal); + if(iRet != 1) + { + pMe->iLastError = iRet; + return 0; + } + return 1; +} +/*--------------------------------------------------------------------------*/ +static int NHQ200Error(pEVDriver self, int *iCode, char *error, int iErrLen) +{ + pNHQ200Driv pMe = NULL; + + assert(self); + pMe = (pNHQ200Driv)self->pPrivate; + assert(pMe); + + *iCode = pMe->iLastError; + if(pMe->iLastError == SHITTYVALUE) + { + strncpy(error,"Invalid temperature returned form NHQ200, check sensor",iErrLen); + } + else + { + NHQ200_ErrorTxt(&pMe->pData,pMe->iLastError,error,iErrLen); + } + return 1; +} +/*--------------------------------------------------------------------------*/ +static int NHQ200Send(pEVDriver self, char *pCommand, char *pReply, int iLen) +{ + pNHQ200Driv pMe = NULL; + int iRet; + + assert(self); + pMe = (pNHQ200Driv )self->pPrivate; + assert(pMe); + + iRet = NHQ200_Send(&pMe->pData,pCommand, pReply,iLen); + if(iRet <= 0) + { + pMe->iLastError = iRet; + return 0; + } + return 1; + +} +/*--------------------------------------------------------------------------*/ +static int NHQ200Init(pEVDriver self) +{ + pNHQ200Driv pMe = NULL; + int iRet; + + assert(self); + pMe = (pNHQ200Driv )self->pPrivate; + assert(pMe); + + pMe->pData = NULL; + iRet = NHQ200_Open(&pMe->pData, pMe->pHost, pMe->iRead, pMe->iControl,0); + if(iRet != 1) + { + if(iRet == NHQ200__NONHQ200) + { + return -1; + } + else + { + pMe->iLastError = iRet; + return 0; + } + } + return 1; +} +/*--------------------------------------------------------------------------*/ +static int NHQ200Close(pEVDriver self) +{ + pNHQ200Driv pMe = NULL; + int iRet; + + assert(self); + pMe = (pNHQ200Driv )self->pPrivate; + assert(pMe); + + NHQ200_Close(&pMe->pData); + return 1; +} +/*---------------------------------------------------------------------------*/ +static int NHQ200Fix(pEVDriver self, int iError) +{ + pNHQ200Driv pMe = NULL; + int iRet; + + assert(self); + pMe = (pNHQ200Driv )self->pPrivate; + assert(pMe); + + switch(iError) + { + /* network errors */ + case EL734__BAD_FLUSH: + case EL734__BAD_RECV: + case EL734__BAD_RECV_NET: + case EL734__BAD_RECV_UNKN: + case EL734__BAD_RECVLEN: + case EL734__BAD_RECV1: + case EL734__BAD_RECV1_PIPE: + case EL734__BAD_RNG: + case EL734__BAD_SEND: + case EL734__BAD_SEND_PIPE: + case EL734__BAD_SEND_NET: + case EL734__BAD_SEND_UNKN: + case EL734__BAD_SENDLEN: + NHQ200Close(self); + iRet = NHQ200Init(self); + if(iRet) + { + return DEVREDO; + } + else + { + return DEVFAULT; + } + break; + /* handable protocoll errors */ + case EL734__BAD_TMO: + return DEVREDO; + break; + case -501: /* Bad_COM */ + return DEVREDO; + case -504: /* Badly formatted */ + return DEVREDO; + default: + return DEVFAULT; + break; + } + return DEVFAULT; +} + +/*--------------------------------------------------------------------------*/ +static int NHQ200Halt(pEVDriver *self) +{ + assert(self); + + return 1; +} +/*------------------------------------------------------------------------*/ +void KillNHQ200(void *pData) +{ + pNHQ200Driv pMe = NULL; + + pMe = (pNHQ200Driv)pData; + assert(pMe); + + if(pMe->pHost) + { + free(pMe->pHost); + } + free(pMe); +} +/*------------------------------------------------------------------------*/ +pEVDriver CreateNHQ200Driver(int argc, char *argv[]) +{ + pEVDriver pNew = NULL; + pNHQ200Driv pSim = NULL; + + /* check for arguments */ + if(argc < 3) + { + return NULL; + } + + pNew = CreateEVDriver(argc,argv); + pSim = (pNHQ200Driv)malloc(sizeof(NHQ200Driv)); + memset(pSim,0,sizeof(NHQ200Driv)); + if(!pNew || !pSim) + { + return NULL; + } + pNew->pPrivate = pSim; + pNew->KillPrivate = KillNHQ200; + + /* initalise pNHQ200Driver */ + pSim->iControl = atoi(argv[2]); + pSim->iRead = atoi(argv[1]); + pSim->iLastError = 0; + pSim->iTmo = 10; + + /* The NHQ200 doesn't require divisors or multipliers + and they are always forced to 1.0 */ + pSim->fDiv = 1.0; + pSim->fMult = 1.0; + + pSim->pHost = strdup(argv[0]); + pSim->iPort = 0; + pSim->iChannel = 0; + + + /* initialise function pointers */ + pNew->SetValue = NHQ200Run; + pNew->GetValue = GetNHQ200Pos; + pNew->Send = NHQ200Send; + pNew->GetError = NHQ200Error; + pNew->TryFixIt = NHQ200Fix; + pNew->Init = NHQ200Init; + pNew->Close = NHQ200Close; + + return pNew; +} +/*--------------------------------------------------------------------------*/ +int ConfigNHQ200(pEVDriver self) +{ + pNHQ200Driv pMe = NULL; + int iRet; + + assert(self); + pMe = (pNHQ200Driv )self->pPrivate; + assert(pMe); + + iRet = NHQ200_Config(&pMe->pData, pMe->iTmo, pMe->iRead, + pMe->iControl,pMe->fDiv,pMe->fMult); + if(iRet < 0) + { + pMe->iLastError = iRet; + return 0; + } + return 1; +} +/*-------------------------------------------------------------------------*/ +int SetSensorNHQ200(pEVDriver self, int iSensor) +{ + pNHQ200Driv pMe = NULL; + int iRet; + + assert(self); + pMe = (pNHQ200Driv )self->pPrivate; + assert(pMe); + + /* The NHQ200 incorporates two voltage supplies so allow iSensor=1 to 2 */ + if( (iSensor < 1) || (iSensor > 2) ) + { + return 0; + } + pMe->iRead = iSensor; + pMe->pData->iRead = iSensor; + return 1; +} +/*-------------------------------------------------------------------------*/ +int SetControlNHQ200(pEVDriver self, int iSensor) +{ + pNHQ200Driv pMe = NULL; + int iRet; + + assert(self); + pMe = (pNHQ200Driv )self->pPrivate; + assert(pMe); + + /* The NHQ200 incorporates two voltage supplies so allow iSensor=1 to 2 */ + if( (iSensor < 1) || (iSensor > 2) ) + { + return 0; + } + pMe->iControl = iSensor; + pMe->pData->iControl = iSensor; + return 1; +} +/*-------------------------------------------------------------------------*/ +int SetTMONHQ200(pEVDriver self, int iSensor) +{ + pNHQ200Driv pMe = NULL; + int iRet; + + assert(self); + pMe = (pNHQ200Driv )self->pPrivate; + assert(pMe); + + if(iSensor < 10) + { + return 0; + } + pMe->iTmo = iSensor; + return 1; +} +/*-------------------------------------------------------------------------*/ +int GetControlNHQ200(pEVDriver self) +{ + pNHQ200Driv pMe = NULL; + int iRet; + + assert(self); + pMe = (pNHQ200Driv )self->pPrivate; + assert(pMe); + + return pMe->iControl; +} +/*-------------------------------------------------------------------------*/ +int GetSensorNHQ200(pEVDriver self) +{ + pNHQ200Driv pMe = NULL; + int iRet; + + assert(self); + pMe = (pNHQ200Driv )self->pPrivate; + assert(pMe); + + return pMe->iRead; +} +/*-------------------------------------------------------------------------*/ +int GetTMONHQ200(pEVDriver self) +{ + pNHQ200Driv pMe = NULL; + int iRet; + + assert(self); + pMe = (pNHQ200Driv )self->pPrivate; + assert(pMe); + + return pMe->iTmo; +} +/*-------------------------------------------------------------------------*/ +float GetDivisorNHQ200(pEVDriver self) +{ + pNHQ200Driv pMe = NULL; + int iRet; + + assert(self); + pMe = (pNHQ200Driv )self->pPrivate; + assert(pMe); + + return pMe->fDiv; /* but forced to 1.0 for NHQ200, not used */ +} +/*--------------------------------------------------------------------------*/ +int SetDivisorNHQ200(pEVDriver self, float fDiv) +{ + pNHQ200Driv pMe = NULL; + int iRet; + + assert(self); + pMe = (pNHQ200Driv )self->pPrivate; + assert(pMe); + + /* The NHQ200 doesn't need divisor, force to 1.0 */ + pMe->fDiv = 1.0; /* fDiv */; + return 1; +} +/*-------------------------------------------------------------------------*/ +float GetMultNHQ200(pEVDriver self) +{ + pNHQ200Driv pMe = NULL; + int iRet; + + assert(self); + pMe = (pNHQ200Driv )self->pPrivate; + assert(pMe); + + return pMe->fMult; /* but forced to 1.0 for NHQ200, not used */ +} +/*--------------------------------------------------------------------------*/ +int SetMultNHQ200(pEVDriver self, float fDiv) +{ + pNHQ200Driv pMe = NULL; + int iRet; + + assert(self); + pMe = (pNHQ200Driv )self->pPrivate; + assert(pMe); + + /* The NHQ200 doesn't need multiplier, force to 1.0 */ + pMe->fMult = 1.0; /* fDiv */; + return 1; +} + diff --git a/site_ansto/site_ansto.c b/site_ansto/site_ansto.c index 8cb5c27f..5e1a72e0 100644 --- a/site_ansto/site_ansto.c +++ b/site_ansto/site_ansto.c @@ -30,6 +30,8 @@ /* Added customized HMControl object to support ANSTO OPAL NBI Histogram Server */ #include "hmcontrol.h" #include "hmcontrol_ansto.h" // extends hmcontrol.h +/* Added code for NHQ200 HV Power Supply */ +#include "nhq200.h" /*@observer@*//*@null@*/ pCounterDriver CreateMonCounter(/*@observer@*/SConnection *pCon, /*@observer@*/char *name, char *params); @@ -176,6 +178,18 @@ static pEVControl InstallEnvironmentController(SicsInterp *pSics, } } + /* Added code for new NHQ 200 driver */ + if(strcmp(argv[3],"nhq200") == 0) { + pDriv = CreateNHQ200Driver(argc-4,&argv[4]); + if(pDriv){ + pNew = CreateEVController(pDriv,argv[2],&status); + if(pNew != NULL){ + AddCommand(pSics,argv[2],NHQ200Wrapper,DeleteEVController, + pNew); + } + } + } + return pNew; } /*-----------------------------------------------------------------*/