363 lines
12 KiB
C
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);
|
|
}
|