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