Files
sics/site_ansto/hardsup/sct_oxfordprot.c
Douglas Clowes 1e1fce78be Script Context layers test for "ASCERR: " and the space is significant.
Update code and comments to reflect that
2013-07-04 15:46:29 +10:00

295 lines
7.0 KiB
C

/** @file Oxford protocol handler for script-context based controllers.
*
* If you 'send' commands to a oxford controller using this protocol you
* will get one of three possible responses,
* 1. A value
* eg
* 2. An acknowledgement (ie 'OK')
* eg
* sct_mc2 send "MOG"
* OK
* 3. An error message
* eg
* sct_mc2 send "BGG"
* ASCERR: 20 Begin not valid with motor off (during read finished)
*/
#include <errno.h>
#include <ascon.h>
#include <ascon.i>
#include <dynstring.h>
enum {
ProtocolOxford = 0,
ProtocolHamilton = 1,
ProtocolSyringe = 2
};
struct OxfordProtocol_t {
unsigned int magic;
Ascon* parent;
double last_line_time;
double last_char_time;
double inter_line_time;
double inter_char_time;
int protocol_subtype;
};
typedef struct OxfordProtocol_t OxfordProtocol;
struct OxfordQProtocol_t {
unsigned int magic;
Ascon* parent;
double last_line_time;
double last_char_time;
double inter_line_time;
double inter_char_time;
};
typedef struct OxfordQProtocol_t OxfordQProtocol;
/** @brief Set line terminator before sending command
*/
int OxfordWriteStart(Ascon *a) {
OxfordProtocol* private;
double now;
private = (OxfordProtocol*) a->private;
assert(private->magic == 0xcafef00d);
now = DoubleTime();
if (now < private->last_line_time) {
private->last_line_time = now;
}
private->last_char_time = now;
/*
if (now > private->last_line_time + private->inter_line_time) {
return 1;
}
*/
DynStringConcat(a->wrBuffer,"\r");
a->wrPos = 0;
a->state = AsconWriting;
/* if there is only the terminator, do not send it */
if (GetDynStringLength(a->wrBuffer) <= 1) {
a->state = AsconWriteDone;
}
return 1;
}
/** @brief Write the line one character at a time
*/
int OxfordWriting(Ascon *a) {
OxfordProtocol* private;
int ret;
double now;
private = (OxfordProtocol*) a->private;
assert(private->magic == 0xcafef00d);
now = DoubleTime();
if (now < private->last_char_time) {
private->last_char_time = now;
}
if (now < private->last_char_time + private->inter_char_time) {
return 1;
}
if (a->wrPos == 0)
AsconReadGarbage(a->fd);
ret = AsconWriteChars(a->fd, GetCharArray(a->wrBuffer) + a->wrPos, 1);
if (ret < 0) {
AsconError(a, "send failed:", errno);
/*
* Ooops: which state shall we go to after a write fail?
* This seems to retry.
*/
} else {
private->last_char_time = now;
a->wrPos += ret;
if (a->wrPos >= GetDynStringLength(a->wrBuffer)) {
private->last_line_time = now;
a->state = AsconWriteDone;
}
}
return 1;
}
/** @brief Map oxford replies to OK, ASCERR: ..., value.
* You can use the first character to sort replies from a oxford controller
*/
int OxfordReading(Ascon *a) {
int ret;
char chr, ch[2];
char* cp = NULL;
char term;
OxfordProtocol* private;
private = (OxfordProtocol*) a->private;
switch (private->protocol_subtype) {
case ProtocolOxford:
case ProtocolHamilton:
term = '\r';
break;
case ProtocolSyringe:
term = '\003';
break;
}
ret = AsconReadChar(a->fd, &chr);
while (ret > 0) {
a->start = DoubleTime();
/* Accept ASCII CR or ETX as terminator */
if (chr != term) {
DynStringConcatChar(a->rdBuffer, chr);
}
else {
int ilen, olen;
switch (private->protocol_subtype) {
case ProtocolHamilton:
ilen = GetDynStringLength(a->rdBuffer);
olen = GetDynStringLength(a->wrBuffer) - 1; /* omit CR */
if (olen == ilen) {
char *itext, *otext;
itext = GetCharArray(a->rdBuffer);
otext = GetCharArray(a->wrBuffer);
if (memcmp(itext, otext, ilen) == 0) { /* echo? */
DynStringClear(a->rdBuffer);
ret = AsconReadChar(a->fd, &chr);
continue;
}
}
break;
}
a->state = AsconReadDone;
break;
}
ret = AsconReadChar(a->fd, &chr);
}
if (ret < 0) {
AsconError(a, "AsconReadChar failed:", errno);
return 0;
}
if (a->state == AsconReadDone) {
DynStringConcatChar(a->rdBuffer, '\0');
} else {
if (a->timeout > 0) {
if (DoubleTime() - a->start > a->timeout) {
AsconError(a, "read timeout", 0);
a->state = AsconTimeout;
}
}
}
return 0;
}
/** @brief Oxford protocol handler.
* This handler formats commands (ie adds cr line terminator) and
* sorts replies into standard responses of
* <value>
* OK
* ASCERR: ...
*/
int OxfordProtHandler(Ascon *a) {
int ret;
switch(a->state){
case AsconWriteStart:
ret = OxfordWriteStart(a);
return ret;
break;
case AsconWriting:
ret = OxfordWriting(a);
return ret;
break;
case AsconReadStart:
a->start = DoubleTime();
ret = AsconStdHandler(a);
return ret;
break;
case AsconReading:
ret = OxfordReading(a);
return ret;
break;
default:
ret = AsconStdHandler(a);
return ret;
break;
}
return 1;
}
int OxfordQProtHandler(Ascon *a) {
int ret;
switch(a->state){
case AsconWriteStart:
ret = OxfordWriteStart(a);
return ret;
break;
case AsconWriting:
ret = OxfordWriting(a);
return ret;
break;
case AsconReading:
ret = OxfordReading(a);
return ret;
break;
default:
ret = AsconStdHandler(a);
return ret;
}
return 1;
}
void OxfordKillPrivate(void *arg) {
OxfordProtocol *private = (OxfordProtocol *) arg;
assert(private->magic == 0xcafef00d);
assert(private->parent->private == private);
private->magic = 0;
free(arg);
}
static int GenericInit(Ascon *a, SConnection *con,
int argc, char *argv[], int subtype) {
OxfordProtocol *private;
int ret;
ret = AsconStdInit(a, con, argc, argv);
private = calloc(sizeof(OxfordProtocol), 1);
a->private = (void *) private;
private->magic = 0xcafef00d;
private->parent = a;
private->inter_line_time = 0.100;
private->inter_char_time = 0.010;
private->protocol_subtype = subtype;
a->killPrivate = OxfordKillPrivate;
return ret;
}
static int OxfordInit(Ascon *a, SConnection *con, int argc, char *argv[]) {
return GenericInit(a, con, argc, argv, ProtocolOxford);
}
static int HamiltonInit(Ascon *a, SConnection *con, int argc, char *argv[]) {
return GenericInit(a, con, argc, argv, ProtocolHamilton);
}
static int SyringeInit(Ascon *a, SConnection *con, int argc, char *argv[]) {
return GenericInit(a, con, argc, argv, ProtocolSyringe);
}
void AddOxfordProtocoll(){
AsconProtocol *prot = NULL;
prot = calloc(sizeof(AsconProtocol), 1);
prot->name = strdup("oxford");
prot->init = OxfordInit;
prot->handler = OxfordProtHandler;
AsconInsertProtocol(prot);
prot = calloc(sizeof(AsconProtocol), 1);
prot->name = strdup("hamilton");
prot->init = HamiltonInit;
prot->handler = OxfordProtHandler;
AsconInsertProtocol(prot);
prot = calloc(sizeof(AsconProtocol), 1);
prot->name = strdup("syringe");
prot->init = SyringeInit;
prot->handler = OxfordQProtHandler;
AsconInsertProtocol(prot);
}