229 lines
5.5 KiB
C
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);
|
|
}
|