508 lines
16 KiB
C
508 lines
16 KiB
C
/*--------------------------------------------------------------------------
|
||
|
||
W E S T 4 1 0 0 U T I L . C
|
||
|
||
A few utility functions for dealing with a WEST4100 temperature controller
|
||
within the SINQ setup: host -- TCP/IP -- MAC --- RS-232.
|
||
|
||
Mark Koennecke, Juli 1997
|
||
Mark Lesha, January 2006 (based on ITC4 code)
|
||
Paul Barron, January 2008 (Note: This is based on the old LAKESHORE340 code and
|
||
not the new LS340 code written by Rodney Davies Feb 08)
|
||
|
||
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 "west4100util.h"
|
||
#include "modbustcp.h"
|
||
/*-------------------------------------------------------------------------*/
|
||
|
||
|
||
int WEST4100_Check_Status(pWEST4100 self)
|
||
/* Can be called to check for correct operation of the WEST4100 */
|
||
{
|
||
int iRet, iRetry;
|
||
unsigned char pCommand[20];
|
||
unsigned char pReply[132];
|
||
|
||
iRetry=0;
|
||
do
|
||
{
|
||
// Check Alarm 1
|
||
printf("%-9s %-23s","Checking:", "Status Alarm 1........");
|
||
sprintf(pCommand,"%c%c%c%c%c%c",self->iAdr,0x01,0x00,0x05,0x00,0x01);
|
||
if ((iRet=transactModbusTCP(self->controller,pCommand,6,pReply,79))<=0)
|
||
return iRet;
|
||
|
||
if(pReply[3] != 0x0)
|
||
{
|
||
printf("Warning: Alarm 1 Activated\n");
|
||
strcpy(self->pAns,pReply);
|
||
return 1;
|
||
}else printf("OK\n");
|
||
|
||
// Check Alarm 2
|
||
printf("%-9s %-23s","Checking:", "Status Alarm 2........");
|
||
sprintf(pCommand,"%c%c%c%c%c%c",self->iAdr,0x01,0x00,0x06,0x00,0x01);
|
||
if ((iRet=transactModbusTCP(self->controller,pCommand,6,pReply,79))<=0)
|
||
return iRet;
|
||
|
||
if(pReply[3]!=0x0)
|
||
{
|
||
printf("Warning: Alarm 2 Activated\n");
|
||
strcpy(self->pAns,pReply);
|
||
return 1;
|
||
}else{
|
||
printf("OK\n");
|
||
return 1;
|
||
}
|
||
} while((++iRetry<10));
|
||
/* If we fell out of the loop, the WEST4100 recieved a bad response*/
|
||
sprintf(self->pAns,"Write Status=%s",pReply);
|
||
printf("Bad response received!\n");
|
||
return WEST4100__BADREAD;
|
||
}
|
||
/*-------------------------------------------------------------------------*/
|
||
int WEST4100_ConfigureAndQueryGen(pWEST4100 self, char *command, int fParAdr, float fVal, char *diagnosis)
|
||
{
|
||
int iRet;
|
||
unsigned char pCommandSet[79], pCommandCheck[79];
|
||
unsigned char pReply[79];
|
||
unsigned char fParAdrHex[2],fValHex[2];
|
||
|
||
// Convert int to hexstring
|
||
if((iRet=(int2hexstring((int)fParAdr,fParAdrHex)))==0)
|
||
return iRet;
|
||
if((iRet=(int2hexstring((int)fVal,fValHex)))==0)
|
||
return iRet;
|
||
|
||
/* Construct a write command. */
|
||
printf("%-9s %-23s","Setting: ",command);
|
||
sprintf(pCommandSet,"%c%c%c%c%c%c",self->iAdr,0x06,
|
||
fParAdrHex[0],fParAdrHex[1],fValHex[0],fValHex[1]);
|
||
|
||
/* Issue a write command. */
|
||
if((iRet=transactModbusTCP(self->controller,pCommandSet,/*strlen(pCommand)*/6,pReply,79))!=1)
|
||
return iRet;
|
||
printf("OK\n");
|
||
|
||
/* Construct a read command to check that the paramater was set.*/
|
||
printf("%-9s %-23s","Checking:",command);
|
||
sprintf(pCommandCheck,"%c%c%c%c%c%c",self->iAdr,04,fParAdrHex[0],fParAdrHex[1],0x00,0x1);
|
||
|
||
/* Issue a read command .*/
|
||
if ((iRet=transactModbusTCP(self->controller,pCommandCheck,6,pReply,79))<=0)
|
||
{
|
||
printf("transactRS232 error! Code=%d.\n",iRet);
|
||
printf("DEBUG: pReply='%s' len=%d \n",pReply,strlen(pReply));
|
||
return iRet;
|
||
}
|
||
|
||
// Check that the read data is the same as that was set
|
||
if ( (pCommandSet[4]!=pReply[3]) || (pCommandSet[5]!=pReply[4]) )
|
||
{
|
||
printf("Response was bad, Data not set.\n");
|
||
if (diagnosis&&*diagnosis)
|
||
sprintf(self->pAns,"%s response=%s (%s.)",command,pReply,diagnosis);
|
||
else
|
||
sprintf(self->pAns,"%s response=%s",command,pReply);
|
||
return WEST4100__BADREAD;
|
||
}
|
||
printf("OK\n");
|
||
return 1;
|
||
}
|
||
/*-------------------------------------------------------------------------*/
|
||
int WEST4100_SetControl(pWEST4100 self, int iControl)
|
||
{
|
||
// Left over from lakeshore code, West only has 1 sensor to choose from.
|
||
|
||
return 1;
|
||
}
|
||
/*-------------------------------------------------------------------------*/
|
||
int WEST4100_Setup(pWEST4100 self)
|
||
{
|
||
int iRet;
|
||
unsigned char pCommand[40];
|
||
unsigned char pReply[132];
|
||
//int fVal = 999999.;
|
||
|
||
/* Check the WEST4100 status */
|
||
if ((iRet=WEST4100_Check_Status(self))!=1)
|
||
return iRet;
|
||
|
||
// Check the write status
|
||
printf("%-9s %-23s","Checking:", "Write Status..........");
|
||
sprintf(pCommand,"%c%c%c%c%c%c",self->iAdr,0x01,0x00,0x01,0x00,0x01);
|
||
if ((iRet=transactModbusTCP(self->controller,pCommand,6,pReply,79))<=0)
|
||
{
|
||
printf("Comms error!\n");
|
||
return iRet; // Comms problem
|
||
}
|
||
if (pReply[3] & 0x1)
|
||
{
|
||
printf("OK\n");
|
||
}else if (pReply[3] == 0x00)
|
||
{
|
||
printf("Status is Write Disabled.\n");
|
||
return WEST4100__READONLY;
|
||
}
|
||
|
||
/* Check that the controller is a gen-new-wine WEST4100 */
|
||
printf("%-9s %-23s","Checking:", "ID....................");
|
||
sprintf(pCommand,"%c%c%c%c%c%c",self->iAdr,0x03,0x00,122,0x00,0x01);
|
||
if((iRet=transactModbusTCP(self->controller,pCommand,/*strlen*/6,pReply,79))!=1)
|
||
return iRet;
|
||
|
||
if ((pReply[3]!=0x17) || (pReply[4]!=0xd4))
|
||
{
|
||
printf("Error: Incorrect ID\n");
|
||
strcpy(self->pAns,pReply);
|
||
return WEST4100__NOWEST4100;
|
||
}
|
||
else printf("OK\n");
|
||
|
||
// Set Output Limit
|
||
if((iRet=WEST4100_ConfigureAndQueryGen(self,"Output Power to 40%..",20,40,""))!=1)
|
||
return iRet;
|
||
|
||
// Set Alarm1 Limit
|
||
if((iRet=WEST4100_ConfigureAndQueryGen(self,"Alarm1 to 1600........",13,1600,""))!=1)
|
||
return iRet;
|
||
|
||
// Set Alarm2 Limit
|
||
if((iRet=WEST4100_ConfigureAndQueryGen(self,"Alarm2 to 0...........",14,0,""))!=1)
|
||
return iRet;
|
||
|
||
// Set Upper Limit
|
||
if((iRet=WEST4100_ConfigureAndQueryGen(self,"Upper Limit to 1800...",22,1800,""))!=1)
|
||
return iRet;
|
||
|
||
// Set Lower Limit
|
||
if((iRet=WEST4100_ConfigureAndQueryGen(self,"Lower Limit to 0......",23,0,""))!=1)
|
||
return iRet;
|
||
|
||
// Set Ramp Rate
|
||
if((iRet=WEST4100_ConfigureAndQueryGen(self,"Ramp Rate to 0ff......",24,10000,""))!=1)
|
||
return iRet;
|
||
|
||
/* Check the WEST4100 operating status one last time */
|
||
if ((iRet=WEST4100_Check_Status(self))!=1)
|
||
return iRet;
|
||
|
||
return 1; /* Success */
|
||
}
|
||
/*-------------------------------------------------------------------------*/
|
||
int WEST4100_Open(pWEST4100 *pData, char *pRS232, int iAddress, int iTransaction)
|
||
{
|
||
pWEST4100 self = NULL;
|
||
|
||
self = (pWEST4100)malloc(sizeof(WEST4100));
|
||
if(self == NULL)
|
||
{
|
||
return WEST4100__BADMALLOC;
|
||
}
|
||
*pData = self;
|
||
self->iAdr = iAddress;
|
||
self->iTransact = iTransaction;
|
||
|
||
self->controller = NULL;
|
||
|
||
self->controller = (prs232)FindCommandData(pServ->pSics,pRS232,
|
||
"RS232 Controller");
|
||
if(!self->controller){
|
||
/*SCWrite(pCon,"ERROR: motor controller not found",eError); */
|
||
return WEST4100__BADCOM;
|
||
}
|
||
|
||
return WEST4100_Setup(self);
|
||
}
|
||
/*--------------------------------------------------------------------------*/
|
||
void WEST4100_Close(pWEST4100 *pData)
|
||
{
|
||
pWEST4100 self;
|
||
|
||
self = *pData;
|
||
if (!self)
|
||
return; // Just in case
|
||
|
||
return;
|
||
}
|
||
/*--------------------------------------------------------------------------*/
|
||
int WEST4100_Config(pWEST4100 *pData, int iTmo, int iRead, int iControl)
|
||
{
|
||
pWEST4100 self;
|
||
|
||
self = *pData;
|
||
|
||
return 1;
|
||
}
|
||
/*--------------------------------------------------------------------------*/
|
||
int WEST4100_Send(pWEST4100 *pData, char *pCommand, char *pReply, int iLen)
|
||
{
|
||
int iRet;
|
||
pWEST4100 self;
|
||
|
||
self = *pData;
|
||
|
||
char *ptr = pCommand;
|
||
unsigned int byte;
|
||
unsigned char pCommandHex[79];
|
||
size_t i;
|
||
|
||
// Convert char string command to hex string with every two characters concatenated to one array field
|
||
for (i=0;i<sizeof pCommandHex ;++i)
|
||
{
|
||
if(sscanf(ptr,"%2x",&byte)!=1)
|
||
{
|
||
break;
|
||
}
|
||
pCommandHex[i]=byte;
|
||
ptr +=2;
|
||
}
|
||
|
||
// Issue hex command
|
||
printf("%s ","Issuing Send: ");
|
||
if((iRet=transactModbusTCP(self->controller,pCommandHex,6,pReply,79))!=1){
|
||
printf("%-s","Response: ");
|
||
displayHexString(pReply);
|
||
return iRet;
|
||
}
|
||
printf("OK\n");
|
||
|
||
printf("%-s","Response: ");
|
||
displayHexString(pReply);
|
||
|
||
/* Check the WEST4100 operating status after issuing the command, if it was successful */
|
||
if (iRet>=1)
|
||
iRet=WEST4100_Check_Status(self);
|
||
|
||
return iRet;
|
||
}
|
||
/*--------------------------------------------------------------------------*/
|
||
int WEST4100_Read(pWEST4100 *pData, float *fVal)
|
||
{
|
||
unsigned char pCommand[20], pReply[132];
|
||
int iRet;
|
||
float fRead = -999999.;
|
||
pWEST4100 self;
|
||
|
||
self = *pData;
|
||
|
||
sprintf(pCommand,"%c%c%c%c%c%c",self->iAdr,04,0x0,0x1,0x0,0x1);
|
||
if ((iRet=transactModbusTCP(self->controller,pCommand,6,pReply,79))<=0)
|
||
{
|
||
printf("transactRS232 error! Code=%d.\n",iRet);
|
||
printf("DEBUG: pReply='%s' len=%d \n",pReply,strlen(pReply));
|
||
return iRet;
|
||
}
|
||
|
||
// Because a value read will never be greater than FF FF we can use a simple line to convert
|
||
fRead=(256*pReply[3])+pReply[4];
|
||
|
||
if(fRead > 65535 || fRead < 0) // Not a number, probably an error response
|
||
{
|
||
return WEST4100__BADREAD;
|
||
}
|
||
|
||
*fVal = fRead;
|
||
|
||
return 1;
|
||
}
|
||
/*--------------------------------------------------------------------------*/
|
||
int WEST4100_Query(pWEST4100 *pData, int parAddress, int *parValue)
|
||
{
|
||
unsigned char pCommand[20], pReply[132], pAddress[2];
|
||
int iRet;
|
||
pWEST4100 self;
|
||
|
||
self = *pData;
|
||
|
||
int2hexstring(parAddress,pAddress);
|
||
sprintf(pCommand,"%c%c%c%c%c%c",self->iAdr,0x4,pAddress[0],pAddress[1],0x0,0x1);
|
||
if ((iRet=transactModbusTCP(self->controller,pCommand,6,pReply,79))<=0)
|
||
{
|
||
printf("transactRS232 error! Code=%d.\n",iRet);
|
||
printf("DEBUG: pReply='%s' len=%d \n",pReply,strlen(pReply));
|
||
return iRet;
|
||
}
|
||
|
||
// Because a value read will never be greater than FF FF we can use a simple line to convert
|
||
*parValue=(256*pReply[3])+pReply[4];
|
||
|
||
if(*parValue > 65535 || *parValue < 0) // Not a number, probably an error response
|
||
{
|
||
return WEST4100__BADREAD;
|
||
}
|
||
|
||
return 1;
|
||
}
|
||
/*-------------------------------------------------------------------------*/
|
||
int WEST4100_Write(pWEST4100 *pData, int parAddress, int parValue)
|
||
{
|
||
unsigned char displaytext[40];
|
||
int iRet;
|
||
pWEST4100 self;
|
||
|
||
self = *pData;
|
||
|
||
sprintf(displaytext,"Parameter Number %d...",parAddress);
|
||
if((iRet=WEST4100_ConfigureAndQueryGen(self,displaytext,parAddress,parValue,""))!=1)
|
||
return iRet;
|
||
|
||
if ((iRet=WEST4100_Check_Status(self))!=1)
|
||
return iRet;
|
||
|
||
return 1;
|
||
}
|
||
/*-------------------------------------------------------------------------*/
|
||
int WEST4100_Set(pWEST4100 *pData, float fVal)
|
||
{
|
||
int iRet, i;
|
||
pWEST4100 self;
|
||
|
||
self = *pData;
|
||
|
||
for(i = 0; i < 3; i++)
|
||
{
|
||
// Set setpoint
|
||
if((iRet=WEST4100_ConfigureAndQueryGen(self,"Setpoint...",0x02,fVal,""))!=1)
|
||
return iRet;
|
||
|
||
printf("SETP OK, checking status and returning.\n");
|
||
iRet=WEST4100_Check_Status(self);
|
||
|
||
return iRet;
|
||
}
|
||
printf("SETP failed!\n");
|
||
return WEST4100__BADSET;
|
||
}
|
||
/*-------------------------------------------------------------------------*/
|
||
void WEST4100_ErrorTxt(pWEST4100 *pData,int iCode, char *pError, int iLen)
|
||
{
|
||
char pBueffel[512];
|
||
pWEST4100 self;
|
||
|
||
self = *pData;
|
||
|
||
switch(iCode)
|
||
{
|
||
case WEST4100__BADCOM:
|
||
sprintf(pBueffel,"WEST4100: Invalid command or offline, got %s",
|
||
self->pAns);
|
||
strncpy(pError,pBueffel,iLen);
|
||
break;
|
||
case WEST4100__BADPAR:
|
||
strncpy(pError,"WEST4100: Invalid parameter specified",iLen);
|
||
break;
|
||
case WEST4100__BADMALLOC:
|
||
strncpy(pError,"WEST4100: Error allocating memory in WEST4100",iLen);
|
||
break;
|
||
case WEST4100__BADREAD:
|
||
strncpy(pError,"WEST4100: Badly formatted answer",iLen);
|
||
break;
|
||
case WEST4100__BADSET:
|
||
strncpy(pError,"WEST4100: Failed three times to write new set value to WEST4100",iLen);
|
||
break;
|
||
case WEST4100__FAULT: // Covers various WEST4100 self-diagnosed fault conditions
|
||
sprintf(pBueffel,"WEST4100: Internal fault condition detected: %s",self->pAns);
|
||
strncpy(pError,pBueffel,iLen);
|
||
break;
|
||
case WEST4100__NOWEST4100:
|
||
sprintf(pBueffel,"WEST4100: Wrong model number (driver is for Model 340 only): %s",self->pAns);
|
||
strncpy(pError,pBueffel,iLen);
|
||
break;
|
||
default:
|
||
getRS232Error(iCode, pError,iLen);
|
||
break;
|
||
}
|
||
}
|
||
/*-------------------------------------------------------------------------*/
|
||
int int2hexstring(int fVal, unsigned char *hexstring)
|
||
{
|
||
size_t k;
|
||
int fValInt;
|
||
unsigned char temp[79];
|
||
int result,remainder,index,index2;
|
||
|
||
fValInt=fVal;
|
||
|
||
if(fValInt>65535)
|
||
{
|
||
printf("Value greater than FF FF");
|
||
return 0;
|
||
}
|
||
|
||
// Convert integer to hex and putting each char in an array
|
||
memset(temp,0,sizeof(temp));
|
||
result=1;
|
||
for(k=0;result!=0;k++)
|
||
{
|
||
result=fValInt/16;
|
||
remainder=fValInt%16;
|
||
fValInt=result;
|
||
temp[k]=remainder;
|
||
}
|
||
|
||
// Formatting a new array so that there is one byte per array field
|
||
if((k%2)==0)
|
||
index2=k/2-1;
|
||
else
|
||
index2=k/2;
|
||
|
||
if(fVal>255)
|
||
{
|
||
for(index=0;index2>=0;(index=index+2),index2--)
|
||
{
|
||
hexstring[index2]=(temp[index+1]*16)+temp[index];
|
||
}
|
||
}else{
|
||
hexstring[0]=0x0;
|
||
hexstring[1]=(temp[1]*16)+temp[0];
|
||
}
|
||
|
||
return 1;
|
||
}
|
||
/*-------------------------------------------------------------------------*/
|
||
void displayHexString(unsigned char *hexstring)
|
||
{
|
||
int i;
|
||
|
||
for(i=0;(i<5)|(hexstring[i]!='\0');i++)printf("%02x ",hexstring[i]);
|
||
printf("\n");
|
||
}
|