378 lines
8.5 KiB
C
378 lines
8.5 KiB
C
/*--------------------------------------------------------------------------
|
|
E U R O D R I V
|
|
|
|
This file contains the implementation for the Eurotherm temperature
|
|
controller as used at SANS. The Eurotherm is a grossly strange device
|
|
which has a very weird command protocoll. The implementation here uses
|
|
the EI-Bisynch Protocoll option. This has to be configured in the
|
|
Eurotherm on the manual interface!! Also watch out for the unusual RS232-
|
|
setting: 7 bits, even parity, 1 stop bit.
|
|
|
|
Mark Koennecke, May 1999
|
|
|
|
Copyright: see copyright.h
|
|
----------------------------------------------------------------------------*/
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <time.h>
|
|
#include <math.h>
|
|
#include <assert.h>
|
|
#include <fortify.h>
|
|
#include <conman.h>
|
|
#include <fortify.h>
|
|
#include <strlutil.h>
|
|
|
|
#include "evdriver.h"
|
|
#include "hardsup/el734_def.h"
|
|
#include "hardsup/el734fix.h"
|
|
#include "hardsup/serialsinq.h"
|
|
#include "eurodriv.h"
|
|
|
|
#define INVALIDANSWER -1005
|
|
#define INVALIDNUMBER -1006
|
|
#define ERRNAK -1007
|
|
#define NOSEND -1008
|
|
/*-----------------------------------------------------------------------*/
|
|
typedef struct {
|
|
void *pData;
|
|
char *pHost;
|
|
int iPort;
|
|
int iChannel;
|
|
int iLastError;
|
|
} EuroDriv, *pEuroDriv;
|
|
|
|
/*------------------------------------------------------------------------*/
|
|
int EuroGetParameter(void **pData, char *pPar, int iLen, float *fVal)
|
|
{
|
|
char pCommand[20];
|
|
char pReply[20];
|
|
char *pStart = NULL, *pEnd = NULL;
|
|
int iRet, i;
|
|
|
|
/* configure the serial port */
|
|
SerialATerm(pData, "1\x3"); /* ETX */
|
|
pCommand[0] = '\x4'; /* EOT */
|
|
pCommand[1] = '0'; /* GID */
|
|
pCommand[2] = '0'; /* GID */
|
|
pCommand[3] = '1'; /* UID */
|
|
pCommand[4] = '1'; /* UID */
|
|
pCommand[5] = '1'; /* CHAN */
|
|
for (i = 0; i < iLen; i++) {
|
|
pCommand[6 + i] = pPar[i];
|
|
}
|
|
pCommand[6 + iLen] = '\x5'; /* ENQ */
|
|
pCommand[7 + iLen] = '\0';
|
|
|
|
/* send */
|
|
iRet = SerialWriteRead(pData, pCommand, pReply, 19);
|
|
if (iRet != 1) {
|
|
return iRet;
|
|
}
|
|
|
|
/* decode reply */
|
|
pStart = strstr(pReply, pPar);
|
|
if (!pStart) {
|
|
if (strstr(pReply, "?TMO")) {
|
|
return EL734__BAD_TMO;
|
|
} else {
|
|
return INVALIDANSWER;
|
|
}
|
|
}
|
|
iRet = sscanf(pStart + strlen(pPar), "%f", fVal);
|
|
if (iRet != 1) {
|
|
return INVALIDNUMBER;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/*------------------------------------------------------------------------*/
|
|
int EuroSetParameter(void **pData, char *pPar, int iLen,
|
|
char *pFormat, float fVal)
|
|
{
|
|
char pCommand[30];
|
|
char pNum[10];
|
|
char *pPtr, *pPtr2;
|
|
char pReply[20];
|
|
char bcc;
|
|
int iRet, i;
|
|
|
|
|
|
/* configure the serial port */
|
|
SerialATerm(pData, "1\x06\x15"); /* ACK,NAK */
|
|
pCommand[0] = '\x04'; /* EOT */
|
|
pCommand[1] = '0'; /* GID */
|
|
pCommand[2] = '0'; /* GID */
|
|
pCommand[3] = '1'; /* UID */
|
|
pCommand[4] = '1'; /* UID */
|
|
pCommand[5] = '\x02';
|
|
pCommand[6] = '1'; /* CHAN */
|
|
for (i = 0; i < iLen; i++) {
|
|
pCommand[7 + i] = pPar[i];
|
|
}
|
|
pPtr = pCommand + 7 + iLen;
|
|
sprintf(pNum, pFormat, fVal);
|
|
strcpy(pPtr, pNum);
|
|
pPtr += strlen(pNum);
|
|
*pPtr = '\x03';
|
|
pPtr++;
|
|
|
|
/* build the checksum */
|
|
bcc = pCommand[6];
|
|
pPtr2 = &pCommand[7];
|
|
while (pPtr2 != pPtr) {
|
|
bcc = bcc ^ *pPtr2;
|
|
pPtr2++;
|
|
}
|
|
*pPtr = bcc;
|
|
pPtr++;
|
|
*pPtr = '\0';
|
|
|
|
/* send */
|
|
iRet = SerialSend(pData, pCommand);
|
|
if (iRet != 1) {
|
|
return iRet;
|
|
}
|
|
iRet = SerialReceiveWithTerm(pData, pReply, 19, &bcc);
|
|
/*
|
|
printf("%s\n",pReply);
|
|
*/
|
|
if (iRet != 1) {
|
|
return iRet;
|
|
}
|
|
if (bcc == '\x15') {
|
|
return ERRNAK;
|
|
}
|
|
if (strstr(pReply, "?TMO")) {
|
|
return EL734__BAD_TMO;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
static int GetEuroPos(pEVDriver self, float *fPos)
|
|
{
|
|
pEuroDriv pMe = NULL;
|
|
int iRet;
|
|
|
|
assert(self);
|
|
pMe = (pEuroDriv) self->pPrivate;
|
|
assert(pMe);
|
|
|
|
iRet = EuroGetParameter(&(pMe->pData), "PV", 2, fPos);
|
|
if (iRet != 1) {
|
|
pMe->iLastError = iRet;
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
static int EuroRun(pEVDriver self, float fVal)
|
|
{
|
|
pEuroDriv pMe = NULL;
|
|
int iRet;
|
|
|
|
assert(self);
|
|
pMe = (pEuroDriv) self->pPrivate;
|
|
assert(pMe);
|
|
|
|
iRet = EuroSetParameter(&(pMe->pData), "SL", 2, "%4.1f", fVal);
|
|
if (iRet != 1) {
|
|
pMe->iLastError = iRet;
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
static int EuroError(pEVDriver self, int *iCode, char *error, int iErrLen)
|
|
{
|
|
pEuroDriv pMe = NULL;
|
|
|
|
assert(self);
|
|
pMe = (pEuroDriv) self->pPrivate;
|
|
assert(pMe);
|
|
|
|
*iCode = pMe->iLastError;
|
|
switch (pMe->iLastError) {
|
|
case INVALIDANSWER:
|
|
strlcpy(error, "Unexpected reply from Eurotherm", iErrLen);
|
|
break;
|
|
case INVALIDNUMBER:
|
|
strlcpy(error, "No number in Eurotherm answer", iErrLen);
|
|
break;
|
|
case ERRNAK:
|
|
strlcpy(error, "Eurothem did NOT acknowledge command", iErrLen);
|
|
break;
|
|
case NOSEND:
|
|
strlcpy(error,
|
|
"Eurotherm has a bizarre protocoll, sending things is very STUPID",
|
|
iErrLen);
|
|
break;
|
|
default:
|
|
SerialError(pMe->iLastError, error, iErrLen);
|
|
break;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
static int EuroSend(pEVDriver self, char *pCommand, char *pReply, int iLen)
|
|
{
|
|
pEuroDriv pMe = NULL;
|
|
|
|
assert(self);
|
|
pMe = (pEuroDriv) self->pPrivate;
|
|
assert(pMe);
|
|
|
|
pMe->iLastError = NOSEND;
|
|
strlcpy(pReply, "ERROR: Eurotherm does not support send functionality",
|
|
iLen);
|
|
return 0;
|
|
}
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
static int EuroInit(pEVDriver self)
|
|
{
|
|
pEuroDriv pMe = NULL;
|
|
int iRet;
|
|
|
|
assert(self);
|
|
pMe = (pEuroDriv) self->pPrivate;
|
|
assert(pMe);
|
|
|
|
pMe->pData = NULL;
|
|
iRet = SerialOpen(&pMe->pData, pMe->pHost, pMe->iPort, pMe->iChannel);
|
|
if (iRet != 1) {
|
|
pMe->iLastError = iRet;
|
|
return 0;
|
|
}
|
|
SerialSendTerm(&pMe->pData, "");
|
|
return 1;
|
|
}
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
static int EuroClose(pEVDriver self)
|
|
{
|
|
pEuroDriv pMe = NULL;
|
|
int iRet;
|
|
|
|
assert(self);
|
|
pMe = (pEuroDriv) self->pPrivate;
|
|
assert(pMe);
|
|
|
|
SerialClose(&pMe->pData);
|
|
return 1;
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
static int EuroFix(pEVDriver self, int iError)
|
|
{
|
|
pEuroDriv pMe = NULL;
|
|
int iRet;
|
|
|
|
assert(self);
|
|
pMe = (pEuroDriv) 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:
|
|
EuroClose(self);
|
|
iRet = EuroInit(self);
|
|
if (iRet) {
|
|
return DEVREDO;
|
|
} else {
|
|
return DEVFAULT;
|
|
}
|
|
break;
|
|
/* handable protocoll errors */
|
|
case EL734__BAD_TMO:
|
|
return DEVREDO;
|
|
break;
|
|
case ERRNAK:
|
|
case NOSEND:
|
|
case INVALIDANSWER:
|
|
case INVALIDNUMBER:
|
|
return DEVFAULT;
|
|
default:
|
|
return DEVFAULT;
|
|
break;
|
|
}
|
|
return DEVFAULT;
|
|
}
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
static int EuroHalt(pEVDriver * self)
|
|
{
|
|
assert(self);
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*------------------------------------------------------------------------*/
|
|
void KillEuro(void *pData)
|
|
{
|
|
pEuroDriv pMe = NULL;
|
|
|
|
pMe = (pEuroDriv) pData;
|
|
assert(pMe);
|
|
|
|
if (pMe->pHost) {
|
|
free(pMe->pHost);
|
|
}
|
|
free(pMe);
|
|
}
|
|
|
|
/*------------------------------------------------------------------------*/
|
|
pEVDriver CreateEURODriv(int argc, char *argv[])
|
|
{
|
|
pEVDriver pNew = NULL;
|
|
pEuroDriv pSim = NULL;
|
|
|
|
/* check for arguments */
|
|
if (argc < 3) {
|
|
return NULL;
|
|
}
|
|
|
|
pNew = CreateEVDriver(argc, argv);
|
|
pSim = (pEuroDriv) malloc(sizeof(EuroDriv));
|
|
memset(pSim, 0, sizeof(EuroDriv));
|
|
if (!pNew || !pSim) {
|
|
return NULL;
|
|
}
|
|
pNew->pPrivate = pSim;
|
|
pNew->KillPrivate = KillEuro;
|
|
|
|
/* initalise pDILLUDriver */
|
|
pSim->iLastError = 0;
|
|
pSim->pHost = strdup(argv[0]);
|
|
pSim->iPort = atoi(argv[1]);
|
|
pSim->iChannel = atoi(argv[2]);
|
|
|
|
/* initialise function pointers */
|
|
pNew->SetValue = EuroRun;
|
|
pNew->GetValue = GetEuroPos;
|
|
pNew->Send = EuroSend;
|
|
pNew->GetError = EuroError;
|
|
pNew->TryFixIt = EuroFix;
|
|
pNew->Init = EuroInit;
|
|
pNew->Close = EuroClose;
|
|
|
|
return pNew;
|
|
}
|