/** @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 #include #include #include struct OxfordProtocol_t { unsigned int magic; Ascon* parent; double last_line_time; double last_char_time; double inter_line_time; double inter_char_time; }; 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; ret = AsconReadChar(a->fd, &chr); while (ret > 0) { a->start = DoubleTime(); if (chr != '\r') { DynStringConcatChar(a->rdBuffer, chr); } else { 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 * * 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 AsconReading: ret = OxfordReading(a); return ret; break; default: ret = AsconStdHandler(a); return ret; } 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); } void OxfordQKillPrivate(void *arg) { OxfordQProtocol *private = (OxfordQProtocol *) arg; assert(private->magic == 0xcafef00d); assert(private->parent->private == private); private->magic = 0; free(arg); } int OxfordInit(Ascon *a, SConnection *con, int argc, char *argv[]) { 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; a->killPrivate = OxfordKillPrivate; return ret; } int OxfordQInit(Ascon *a, SConnection *con, int argc, char *argv[]) { OxfordQProtocol *private; int ret; ret = AsconStdInit(a, con, argc, argv); private = calloc(sizeof(OxfordQProtocol), 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 = OxfordQKillPrivate; return ret; } 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("oxfordq"); prot->init = OxfordQInit; prot->handler = OxfordQProtHandler; AsconInsertProtocol(prot); }