/** @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 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 * * 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); }