295 lines
7.0 KiB
C
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);
|
|
}
|