Files
sics/site_ansto/hardsup/sct_rfamp.c
2014-05-16 17:23:44 +10:00

363 lines
12 KiB
C

/**
* @brief Protocol handler for Mirrotron RF power supplies
Author: Ferdi Franceschini
TODO: Provide acknowledgement when setting a parameter
"OK", when successful
"ANSRFAMP: Set failed", on error
Current should be ramped up in steps of 0.5A but it doesn't
have to be ramped down.
Provides two commands (L)ist and (S)et
eg,
The following command lists status info from the RF Amp at address 3.
sct_rfamp send L:3
The reply string has the following form
address=1|type=L|curr=00|freq=170|voltage=50|K3=1|K2=1|K1=1|O=1|CC=1|CV=0|H=1
The following command sets the current(I), frequency, switches(K1,K2,K3) and output(O).
sct_rfamp send S:3:I=5:F=170:K3=0:K2=1:k1=0:O=1
NOTE: The current is an integer which equals the desired current x10, ie 71 -> 7.1A
*/
#include <errno.h>
#include <ctype.h>
#include <ascon.h>
#include <ascon.i>
#include <string.h>
#include <dynstring.h>
#define ERRLEN 128
#define LASTBYTENUM 12
#define NOTBOOLEAN(b) (b==1 || b==0 ? 0 : 1)
#define MINCURRSPEC 0
#define MAXCURRSPEC 71
#define MINFREQ 170
#define MAXFREQ 500
#define LCMDLEN 4
#define SCMDLEN 10
#define REPLYLEN 13
struct RFAmpData {
int transactInProg;
int targetCurrent;
char rfCmd[16];
char statusCmd[5];
int currTol;
int timeout; // timeout delay before sending a L command after a S command is sent
};
void RFAmpKill(void *private)
{
struct RFAmpData *data = private;
free(data);
}
enum transactionState {txNormalRead, txPreSet, txPostSet, txCheckReply};
/**
* @brief Translates ASCII command string into a properly formatted command for the RF amplifier
*/
int RFAmpWriteStart (Ascon *a)
{
char *dynStrBuffer, errMsg[ERRLEN];
char ctype='X', address='0';
unsigned char switches=0;
int curr, freq, outOn, K1, K2, K3;
int i, ret;
struct RFAmpData *data = a->private;
curr=freq=outOn=K1=K2=K3=0;
dynStrBuffer = GetCharArray(a->wrBuffer);
for (i=0; dynStrBuffer[i]; i++)
dynStrBuffer[i]=toupper(dynStrBuffer[i]);
ret = sscanf(dynStrBuffer, " %c:%c:I=%d:F=%d:K3=%d:K2=%d:K1=%d:O=%d ", &ctype, &address, &curr, &freq, &K3, &K2, &K1, &outOn);
if (ret==0) {
snprintf(errMsg, ERRLEN, "ANSRFAMP:Invalid command string %s", dynStrBuffer);
a->state = AsconWriteDone;
a->noResponse = 1;
AsconError(a, errMsg, 0);
return 1;
}
switch (ctype) {
case 'L':
sprintf(data->rfCmd,"%c%c%c%c", 2, address, ctype, 3);
DynStringClear(a->wrBuffer);
DynStringConcatBytes(a->wrBuffer, data->rfCmd, LCMDLEN);
a->state = AsconWriting;
a->noResponse = 0;
a->wrPos = 0;
return 1;
break;
case 'S':
if (ret < 8) {
snprintf(errMsg, ERRLEN, "ANSRFAMP: Invalid command string %s", dynStrBuffer);
a->state = AsconWriteDone;
a->noResponse = 1;
AsconError(a, errMsg, 0);
return 1;
}
if (address < '1' || address > '9') {
snprintf(errMsg, ERRLEN, "ANSRFAMP: Invalid address %c, it should be between 1 and 9", address);
a->state = AsconWriteDone;
a->noResponse = 1;
AsconError(a, errMsg, 0);
return 1;
}
if (curr < MINCURRSPEC || curr > MAXCURRSPEC) {
snprintf(errMsg, ERRLEN, "ANSRFAMP: Invalid current specifier %d, it should be between %d and %d. NOTE:divide by 10 to convert to Amps", \
curr, MINCURRSPEC, MAXCURRSPEC);
a->state = AsconWriteDone;
a->noResponse = 1;
AsconError(a, errMsg, 0);
return 1;
}
if (freq < MINFREQ || freq > MAXFREQ) {
snprintf(errMsg, ERRLEN, "ANSRFAMP: Invalid frequency %d, it should be between %d and %d", freq, MINFREQ, MAXFREQ);
a->state = AsconWriteDone;
a->noResponse = 1;
AsconError(a, errMsg, 0);
return 1;
}
if (NOTBOOLEAN(K1) || NOTBOOLEAN(K2) || NOTBOOLEAN(K3) || NOTBOOLEAN(outOn)) {
a->state = AsconWriteDone;
a->noResponse = 1;
AsconError(a, "ANSRFAMP: K1, K2, K3 and O must be boolean values (ie 1 or 0)", 0);
return 1;
}
switches = (K3 << 3) | (K2 << 2) | (K1 << 1) | outOn;
sprintf(data->rfCmd,"%c%c%c%02d%03d%c%c", 2, address, ctype, curr, freq, switches, 3);
if (data->rfCmd[0] != 2 || data->rfCmd[9] != 3) {
snprintf(errMsg, ERRLEN, "ANSRFAMP: Failed to construct a valid packet for %s", dynStrBuffer);
a->state = AsconWriteDone;
a->noResponse = 1;
AsconError(a, errMsg, 0);
return 1;
}
sprintf(data->statusCmd,"%c%c%c%c", 2, address, 'L', 3);
DynStringClear(a->wrBuffer);
DynStringConcatBytes(a->wrBuffer, data->statusCmd, LCMDLEN);
data->transactInProg = txPreSet;
data->targetCurrent = curr;
a->state = AsconWriting;
a->noResponse = 0;
a->wrPos = 0;
return 1;
break;
default:
snprintf(errMsg, ERRLEN, "ANSRFAMP: Unknown command type %c, allowed types are 'L' or 'S'", ctype);
a->state = AsconWriteDone;
a->noResponse = 1;
AsconError(a, errMsg, 0);
return 1;
break;
}
}
int RFAmpReading (Ascon *a)
{
int rdChRet, GetReplyFailed=0, errNum=0;
char chr, address, ctype, curr[3], freq[4], voltage[3], replyStr[128];
unsigned char switches, opstate;
unsigned char K3, K2, K1, outOn, CC, CV, heat;
char errMsg[ERRLEN];
struct RFAmpData *data = a->private;
static int delay=0;
switches=opstate=K3=K2=K1=outOn=CC=CV=heat=0;
if(data->transactInProg == txPostSet){
data->transactInProg = txCheckReply;
//Jing: add delay to wait for ramping complete
/* ffr: Reverted SICS-405 sleep(data->timeout); */
DynStringClear(a->wrBuffer);
DynStringConcatBytes(a->wrBuffer, data->statusCmd, LCMDLEN);
a->state = AsconWriting;
a->noResponse = 0;
a->wrPos = 0;
delay = 0;
} else {
// Start reading when byte = 2 and stop when 3
while ( ( rdChRet = AsconReadChar(a->fd, &chr) ) > 0) {
a->start = DoubleTime();
if (a->readState) {
if (a->readState >= LASTBYTENUM) {
a->readState = 0;
if (a->readState > LASTBYTENUM) {
snprintf(errMsg, ERRLEN, "ANSRFAMP: Packet larger than expected, size exceeds %d", LASTBYTENUM);
GetReplyFailed = 1;
break;
}
if (chr != 3) {
snprintf(errMsg, ERRLEN, "ANSRFAMP: Unexpected value %X for packet end signal", chr);
GetReplyFailed = 1;
break;
}
if (0 == DynStringConcatChar(a->rdBuffer, '\0')) {
strcpy(errMsg, "ANSRFAMP: DynStringConcatChar failed:");
errNum = ENOMEM;
GetReplyFailed = 1;
break;
}
address = GetCharArray(a->rdBuffer)[0];
ctype = GetCharArray(a->rdBuffer)[1];
memset(curr, 0, sizeof(curr));
memset(freq, 0, sizeof(freq));
memset(voltage, 0, sizeof(voltage));
strncpy(curr, &GetCharArray(a->rdBuffer)[2], 2);
strncpy(freq, &GetCharArray(a->rdBuffer)[4], 3);
strncpy(voltage, &GetCharArray(a->rdBuffer)[7], 2);
switches = (unsigned char)GetCharArray(a->rdBuffer)[9];
opstate = GetCharArray(a->rdBuffer)[10];
if (data->transactInProg == txPreSet) {
data->transactInProg = txPostSet;
if (abs(data->targetCurrent - atoi(curr)) <= (5 + data->currTol)) {
DynStringClear(a->wrBuffer);
DynStringConcatBytes(a->wrBuffer, data->rfCmd, SCMDLEN);
delay = round(abs(data->targetCurrent - atoi(curr))/0.5 + 0.5);
a->state = AsconWriting;
a->noResponse = 0;
a->wrPos = 0;
} else {
strcpy(errMsg, "ANSRFAMP: Step size should be <= 5 for current");
GetReplyFailed = 1;
break;
}
} else if (data->transactInProg == txCheckReply) {
// TODO Compare rdBuffer to rfCmd if they match then set replyStr="OK" else set error message
char tmpCurr[3], tmpFreq[4];
unsigned char tmpSwitchs;
memset(tmpCurr, 0 , sizeof(tmpCurr));
memset(tmpFreq, 0 , sizeof(tmpFreq));
strncpy(tmpCurr, &data->rfCmd[3], 2);
strncpy(tmpFreq, &data->rfCmd[5], 3);
tmpSwitchs = (unsigned char)data->rfCmd[8];
// TODO SICS-405 ffr Removed check because the read values don't immediately match the set values
/*
if( (abs(atoi(curr) - atoi(tmpCurr)) > data->currTol) ||
(atoi(freq) != atoi(tmpFreq)) ||
((switches & 0x0F) != (tmpSwitchs & 0x0F)) ) {
strcpy(errMsg, "ANSRFAMP: the (S)ET command failed");
GetReplyFailed = 1;
break;
} else {
DynStringReplace(a->rdBuffer, "OK", 0);
}
*/
/* ffr Just report OK until we have a proper fix */
DynStringReplace(a->rdBuffer, "OK", 0);
a->state = AsconReadDone;
data->transactInProg = txNormalRead;
} else if (data->transactInProg == txNormalRead) {
K3 = (switches & 0x08) >> 3;
K2 = (switches & 0x04) >> 2;
K1 = (switches & 0x02) >> 1;
outOn = switches & 0x01;
CC = (opstate & 0x04) >> 2;
CV = (opstate & 0x02) >> 1;
heat = opstate & 0x01;
snprintf(replyStr, 128, "address=%c|type=%c|curr=%s|freq=%s|voltage=%s|K3=%d|K2=%d|K1=%d|O=%d|CC=%d|CV=%d|H=%d", \
address, ctype, curr, freq, voltage, K3, K2, K1, outOn, CC, CV, heat);
DynStringClear(a->rdBuffer);
if (0 == DynStringConcatBytes(a->rdBuffer, replyStr, 128)) {
strcpy(errMsg, "ANSRFAMP: DynStringReplace failed:");
errNum = ENOMEM;
GetReplyFailed = 1;
break;
}
a->state = AsconReadDone;
}
return 0;
}
if (0 == DynStringConcatChar(a->rdBuffer, chr)) {
strcpy(errMsg, "ANSRFAMP: DynStringConcatChar failed:");
errNum = ENOMEM;
GetReplyFailed = 1;
break;
}
a->readState++;
} else if (chr == 2) {
a->readState = 1;
}
}
}
if (rdChRet < 0) {
AsconError(a, "ANSRFAMP: AsconReadChar failed:", errno);
return 0;
}
if (GetReplyFailed) {
a->state = AsconReadDone;
a->noResponse = 1;
a->readState = 0;
data->transactInProg = txNormalRead;
AsconError(a, errMsg, errNum);
return 0;
}
if (a->timeout > 0) {
if (DoubleTime() - a->start > a->timeout) {
/* a->state = AsconTimeout; */
AsconError(a, "ANSRFAMP: read timeout", 0);
}
}
return 0;
}
int RFAmpProtHandler (Ascon *a)
{
int ret;
switch (a->state) {
case AsconWriteStart:
ret = RFAmpWriteStart(a);
return ret;
break;
case AsconReading:
ret = RFAmpReading(a);
return ret;
break;
default:
return AsconStdHandler(a);
}
}
int RFAmpInit(Ascon *a, SConnection *con, int argc, char *argv[])
{
struct RFAmpData *data;
a->readState=0;
a->private = data = (struct RFAmpData *) malloc(sizeof(struct RFAmpData));
data->transactInProg = txNormalRead;
data->targetCurrent = 0;
data->rfCmd[0] = '\0';
data->statusCmd[0] = '\0';
data->currTol = 1; /* TODO This should be a configuration parameter */
data->timeout = 1; /* TODO This should be a configuration parameter */
a->killPrivate = RFAmpKill;
return AsconStdInit(a, con, argc, argv);
}
void AddRFAmpProtocol()
{
AsconProtocol *prot = NULL;
prot = calloc(sizeof(AsconProtocol), 1);
prot->name = strdup("rfamp");
prot->init = RFAmpInit;
prot->handler = RFAmpProtHandler;
AsconInsertProtocol(prot);
}