Files
sics/site_ansto/hardsup/sct_newportprot.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

229 lines
5.5 KiB
C

/** @file Newport 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 response
* eg
* 2. An acknowledgement (ie 'OK')
* eg
* sct_rva send "STT03E8"
* OK
* 3. An error message
* eg
* sct_mc2 send "BGG"
* ASCERR: 20 Begin not valid with motor off (during read finished)
*/
#include <ctype.h>
#include <errno.h>
#include <ascon.h>
#include <ascon.i>
#include <dynstring.h>
struct NewportProtocol_t {
unsigned int magic;
Ascon* parent;
double last_line_time;
double last_char_time;
double inter_line_time;
double inter_char_time;
};
typedef struct NewportProtocol_t NewportProtocol;
/** @brief Set line terminator before sending command
*/
int NewportWriteStart(Ascon *a) {
NewportProtocol* private;
double now;
int i, len;
char *text;
private = (NewportProtocol*) 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;
}
*/
len = GetDynStringLength(a->wrBuffer);
text = GetCharArray(a->wrBuffer);
if (len < 1 || text[len - 1] != '\r')
DynStringConcat(a->wrBuffer, "\r");
len = GetDynStringLength(a->wrBuffer);
/* if there is only the terminator, do not send it */
if (len <= 1) {
a->state = AsconWriteDone;
return 1;
}
a->wrPos = 0;
a->state = AsconWriting;
text = GetCharArray(a->wrBuffer);
for (i = 0; i < len; ++i)
if (islower(text[i]))
text[i] = toupper(text[i]);
return 1;
}
/** @brief Write the line one character at a time
*/
int NewportWriting(Ascon *a) {
NewportProtocol* private;
int ret;
double now;
private = (NewportProtocol*) 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 replies to OK, ASCERR: ..., value.
* You can not use the first character to sort replies from a Newport controller
*/
int NewportReading(Ascon *a) {
int ret;
char chr, ch[2];
char* cp = NULL;
ret = AsconReadChar(a->fd, &chr);
while (ret > 0) {
a->start = DoubleTime();
DynStringConcatChar(a->rdBuffer, chr);
ret = AsconReadChar(a->fd, &chr);
}
if (ret < 0) {
AsconError(a, "AsconReadChar failed:", errno);
return 0;
} else {
/* TODO: should always timeout and check for trailing CR */
double now = DoubleTime();
char *text = GetCharArray(a->rdBuffer);
int len = GetDynStringLength(a->rdBuffer);
double timeout;
if (len < 5) {
if (a->timeout > 0.0)
timeout = a->timeout;
else
timeout = 1.0;
} else if (text[len - 1] == '\r') {
timeout = 0.2;
} else {
timeout = 0.2;
}
if ((now - a->start) > timeout) {
if (len < 5) {
a->state = AsconTimeout;
AsconError(a, "read timeout", 0);
} else if (text[len - 1] != '\r') {
AsconError(a, "missing terminator", 0);
} else if (len > 5 && text[len - 6] != '\r') {
AsconError(a, "format error", 0);
} else {
int i;
for (i = 0; i < len - 1; ++i)
if (text[i] == '\r')
text[i] = ' ';
text[len - 1] = '\0';
a->state = AsconReadDone;
}
}
}
return 0;
}
/** @brief Newport protocol handler.
* This handler formats commands (ie adds cr line terminator) and
* sorts replies into standard responses of
* <value>
* OK
* ASCERR: ...
*/
int NewportProtHandler(Ascon *a) {
int ret;
switch(a->state){
case AsconWriteStart:
ret = NewportWriteStart(a);
return ret;
break;
case AsconWriting:
ret = NewportWriting(a);
return ret;
break;
case AsconReadStart:
a->start = DoubleTime();
ret = AsconStdHandler(a);
return ret;
break;
case AsconReading:
ret = NewportReading(a);
return ret;
break;
default:
ret = AsconStdHandler(a);
return ret;
break;
}
return 1;
}
void NewportKillPrivate(void *arg) {
NewportProtocol *private = (NewportProtocol *) arg;
assert(private->magic == 0xcafef00d);
assert(private->parent->private == private);
private->magic = 0;
free(arg);
}
int NewportInit(Ascon *a, SConnection *con,
int argc, char *argv[]) {
NewportProtocol *private;
int ret;
ret = AsconStdInit(a, con, argc, argv);
private = calloc(sizeof(NewportProtocol), 1);
a->private = (void *) private;
private->magic = 0xcafef00d;
private->parent = a;
private->inter_line_time = 0.100;
private->inter_char_time = 0.010;
a->killPrivate = NewportKillPrivate;
return ret;
}
void AddNewportProtocoll(){
AsconProtocol *prot = NULL;
prot = calloc(sizeof(AsconProtocol), 1);
prot->name = strdup("newport");
prot->init = NewportInit;
prot->handler = NewportProtHandler;
AsconInsertProtocol(prot);
}