diff --git a/GalilSup/src/GalilAxis.cpp b/GalilSup/src/GalilAxis.cpp index e0d8b3e..dc42083 100644 --- a/GalilSup/src/GalilAxis.cpp +++ b/GalilSup/src/GalilAxis.cpp @@ -64,7 +64,7 @@ GalilAxis::GalilAxis(class GalilController *pC, //Pointer to controller instance int switch_type) //motor enable/disable switch type : asynMotorAxis(pC, (toupper(axisname[0]) - AASCII)), pC_(pC), last_encoder_position_(0), smoothed_encoder_position_(0), encoder_smooth_factor_(0.0), motor_dly_(0.0), - first_poll_(true),encDirOk_(true), pollRequest_(10, sizeof(int)) + first_poll_(true),encDirOk_(true), last_done_(1), pollRequest_(10, sizeof(int)) { string limit_code; //Code generated for limits interrupt on this axis string digital_code; //Code generated for digital interrupt related to this axis @@ -2180,7 +2180,7 @@ void GalilAxis::checkHoming(void) // ISIS: need to confirm limits high/low limit behaviour bool home_timeout = homing_ && (stoppedTime_ >= homing_timeout) && !cancelHomeSent_; - bool home_soft_limits_hit = (((readback > highLimit_ && softlimits) || (readback < lowLimit_ && softlimits)) && homing_ && !cancelHomeSent_ && done_); + bool home_soft_limits_hit = (((readback > highLimit_ && softlimits) || (readback < lowLimit_ && softlimits)) && home_timeout && done_); if (home_timeout || home_soft_limits_hit) { sprintf(pC_->cmd_, "MG homed%c\n", axisName_); @@ -2976,7 +2976,10 @@ asynStatus GalilAxis::poller(bool& moving) //Extract axis motion data from controller datarecord, and load into GalilAxis instance status |= getStatus(); - if (status) goto skip; + if (status) { + done_ = last_done_; + goto skip; + } //Set poll variables in GalilAxis based on data record info setStatus(&moving); @@ -3046,7 +3049,6 @@ asynStatus GalilAxis::poller(bool& moving) smoothed_encoder_position_ = (1.0 - encoder_smooth_factor_) * encoder_position_ + encoder_smooth_factor_ * smoothed_encoder_position_; } -skip: //Save encoder position, and done for next poll cycle last_encoder_position_ = encoder_position_; last_done_ = done_; @@ -3056,7 +3058,7 @@ asynStatus GalilAxis::poller(bool& moving) //By this driver during homing revlast_ = rev_; fwdlast_ = fwd_; - +skip: //Set status if (encoder_smooth_factor_ != 0.0) { diff --git a/GalilSup/src/GalilConnector.cpp b/GalilSup/src/GalilConnector.cpp index d461eaa..f34338d 100644 --- a/GalilSup/src/GalilConnector.cpp +++ b/GalilSup/src/GalilConnector.cpp @@ -81,6 +81,8 @@ void GalilConnector::run(void) //Check GalilController for response //Test synchronous communication //Query controller for synchronous connection handle + strcpy(pC_->cmd_, "EO 0"); // first we also turn off echo mode as it really breaks things + pC_->sync_writeReadController(); strcpy(pC_->cmd_, "WH"); sync_status = pC_->sync_writeReadController(true); //Store the handle controller used for sync diff --git a/GalilSup/src/GalilController.cpp b/GalilSup/src/GalilController.cpp index 8ed20b8..a977e82 100644 --- a/GalilSup/src/GalilController.cpp +++ b/GalilSup/src/GalilController.cpp @@ -533,6 +533,16 @@ static void signalHandler(int sig) } } +static std::string rawToEscapedString(const char* inbuf, size_t inlen) +{ + size_t outlen = epicsStrnEscapedFromRawSize(inbuf, inlen); + char* outbuf = new char[outlen + 1]; + epicsStrnEscapedFromRaw(outbuf, outlen, inbuf, inlen); + std::string s(outbuf); + delete[] outbuf; + return s; +} + //EPICS shutdown handler static void shutdownCallback(void *pPvt) { @@ -984,6 +994,7 @@ void GalilController::connect(void) address_string = address.substr(0, n); baud = atoi(address.substr(n).c_str()); } + std::cerr << "Connecting asyn port \"" << syncPort_ << "\" to serial device " << address_string << std::endl; drvAsynSerialPortConfigure(syncPort_, (const char *)address_string.c_str(), epicsThreadPriorityMax, 0, 1); if (baud != 0) { @@ -992,12 +1003,17 @@ void GalilController::connect(void) asynSetOption(syncPort_, 0, "bits", "8"); asynSetOption(syncPort_, 0, "parity", "none"); asynSetOption(syncPort_, 0, "stop", "1"); + asynSetOption(syncPort_, 0, "clocal", "Y"); // disable XON/XOFF flow control. This seemed to be required in the old driver that used the GalilTools DLL // but here it seems to lead to parts of the data record being interpreted as XOFF and getting dropped // from the readback particularly when a motor is moving. It was only enabled in the old version as otherwise // download of the homing programs timed out, but that doesn't seem to be an issue here + std::cerr << syncPort_ << ": disabling software flow control (XON/XOFF) - check this dip switch (if present) on galil controller is OFF" << std::endl; + std::cerr << syncPort_ << ": enabling hardware flow control (RTS/CTS) - check Handshake dip switch (if present) on galil controller is ON" << std::endl; asynSetOption(syncPort_, 0, "ixon", "N"); asynSetOption(syncPort_, 0, "ixoff", "N"); +// asynSetOption(syncPort_, 0, "crtscts", "Y"); + asynSetOption(syncPort_, 0, "crtscts", "N"); } //Flag try_async_ records false for serial connections try_async_ = false; @@ -1265,7 +1281,7 @@ void GalilController::connected(void) numAxesMax_ = model_[6] - ZEROASCII; else if (model_[3] == '3') //DMC30000 series numAxesMax_ = 1; - else //DMC21x3, DMC41x3, DMC40x0 + else //DMC21x3, DMC41x3, DMC40x0, DMC42x0 numAxesMax_ = model_[5] - ZEROASCII; } else //RIO PLC @@ -5147,6 +5163,7 @@ void GalilController::processUnsolicitedMesgs(void) { //Terminate the buffer rawbuf[len] = '\0'; + std::cerr << "processing unsolicited message " << rawbuf << std::endl; //Take backup before splitting into tokens string rawbufOriginal = rawbuf; //Break message into tokens: name value name value etc. @@ -5243,7 +5260,7 @@ void GalilController::processUnsolicitedMesgs(void) //Unknown message //Display message in controller console rawbufOriginal.erase(rawbufOriginal.find_last_not_of(" \n\r\t:")+1); - setCtrlError(rawbufOriginal); + setCtrlError(std::string("Unknown unsolicited message: ") + rawbufOriginal); callParamCallbacks(); //All complete break; @@ -5444,7 +5461,7 @@ bool GalilController::my_isascii(int c) asynStatus GalilController::readDataRecord(char *input, unsigned bytesize) { asynStatus status; //Asyn status - size_t nread = 0; //Asyn read bytes + size_t nread; //Asyn read bytes int eomReason; //Asyn end of message reason char buf[MAX_GALIL_DATAREC_SIZE];//Temporary buffer to hold data record in some circumstances char mesg[MAX_GALIL_STRING_SIZE] = {0x0};//Unsolicited mesg buffer @@ -5459,19 +5476,20 @@ asynStatus GalilController::readDataRecord(char *input, unsigned bytesize) for (;;) { + nread = 0; //Read bytesize using octet interface and user supplied buffer if (async_records_) status = pAsyncOctet_->read(pAsyncOctetPvt_, pasynUserAsyncGalil_, input, readsize, &nread, &eomReason); else status = pSyncOctet_->read(pSyncOctetPvt_, pasynUserSyncGalil_, input, readsize, &nread, &eomReason); - //Serial mode characters arrive with nread = 1 + //Serial mode characters can arrive with nread = 1 but also with nread > 1 but less than readsize //UDP async mode unsolicited mesg always cause read (above) to return with nread set to mesg length //TCP sync mode unsolicited mesg sometimes cause read to return with nread set to mesg length //TCP sync mode unsolicited mesg mostly cause read to return with nread = readsize //Readsize varies when reading data record tail in tcp sync with unsolicited message - if (nread == bytesize && eomReason == ASYN_EOM_CNT) + if (!status && nread == bytesize && eomReason == ASYN_EOM_CNT) { //Read returned ok, with expected bytes //Look for record header at expected location in buffer @@ -5480,9 +5498,13 @@ asynStatus GalilController::readDataRecord(char *input, unsigned bytesize) //Record header cannot be found here in rs232 mode check = (unsigned char)input[3] << 8; check = (unsigned char)input[2] + check; - if (check == datarecsize_) + if (input[1] & 0x80 != 0 && check == datarecsize_) { + if (j > 0) { + std::cerr << "readDataRecord(): discarding message " << mesg << " length " << j << std::endl; + } return status;//Found record at expected location, job done } + } if (!status && nread > 0) { @@ -5505,15 +5527,13 @@ asynStatus GalilController::readDataRecord(char *input, unsigned bytesize) { //Detected record header recstart = true; - if (nread == bytesize && eomReason == ASYN_EOM_CNT) - { - //Received expected number of bytes, but didn't find header at expected location - //Not a serial connection, so it must be synchronous tcp - //This means the header is not at expected location due to unsolicited message - //Store buffer index where datarecord header starts - //Which is also number of data record bytes remaining (tail) to read - readsize = i - HEADER_BYTES + 1; - } + //either Received expected number of bytes, but didn't find header at expected location + //or read less bytes than expected. need to queue an extra read for later after prcoessing these bytes + //This means the header is not at expected location due to unsolicited message + // we read nread and header started at offset (i - HEADER_BYTES + 1) + int offset_to_header = i - HEADER_BYTES + 1; // location of found header in current input buffer + readsize = bytesize + offset_to_header; // nread will be subtracted later + memcpy(buf, input + offset_to_header, HEADER_BYTES); } if (!recstart) { @@ -5556,6 +5576,12 @@ asynStatus GalilController::readDataRecord(char *input, unsigned bytesize) //Data record read complete //Copy data record into user supplied buffer memcpy(input, buf, bytesize); + if (j > 0) { + std::cerr << "readDataRecord(): discarding message " << mesg << " length " << j << std::endl; + } + if (i != nread-1) { + std::cerr << "readDataRecord(): i error" << std::endl; + } return status; } } @@ -5565,8 +5591,13 @@ asynStatus GalilController::readDataRecord(char *input, unsigned bytesize) previous = input[nread - 1]; //Loop back and keep reading until we get the data record or error } - else - return asynError;//Stop if any asyn error + else { + if (j > 0) { + std::cerr << "readDataRecord(): discarding message " << mesg << " length " << j << std::endl; + } + return asynError;//Stop if any asyn error + } + readsize -= nread; } } @@ -5592,8 +5623,11 @@ void GalilController::acquireDataRecord(void) strcpy(cmd_, "QR\r"); //Write the QR query to controller recstatus_ = pSyncOctet_->write(pSyncOctetPvt_, pasynUserSyncGalil_, cmd_, 3, &nwrite); - if (!recstatus_) //Solicited data record includes an extra colon at the end + if (!recstatus_) {//Solicited data record includes an extra colon at the end recstatus_ = readDataRecord(resp_, datarecsize_ + 1); //Get the record + } else { + std::cerr << "acquireDataRecord: failed to send QR" << std::endl; + } unlock(); } else //Asynchronous poll @@ -5608,8 +5642,10 @@ void GalilController::acquireDataRecord(void) } //Track timeouts - if (recstatus_ != asynSuccess) + if (recstatus_ != asynSuccess) { consecutive_acquire_timeouts_++; + std::cerr << "acquireDataRecord: data record acquire timeout number " << consecutive_acquire_timeouts_ << std::endl; + } //Force disconnect if any errors if (consecutive_acquire_timeouts_ > ALLOWED_TIMEOUTS) @@ -5639,7 +5675,7 @@ void GalilController::acquireDataRecord(void) asynStatus GalilController::sync_writeReadController(bool testQuery, bool logCommand) { const char *functionName="sync_writeReadController"; - size_t nread; + size_t nread = 0; int status; size_t len; static const char* debug_file_name = macEnvExpand("$(GALIL_DEBUG_FILE=)"); @@ -5708,12 +5744,14 @@ asynStatus GalilController::sync_writeReadController(const char *output, char *i unsigned i = 0; //Number of raw bytes received, general counting unsigned j = 0; //Number of unsolicited bytes received unsigned k = 0; //Number of solicited bytes received - size_t nwrite; //Bytes written + unsigned m = 0; //Number of discarded bytes received + size_t nwrite = 0; //Bytes written asynStatus status = asynSuccess;//Asyn status int eomReason; //End of message reason char buf[MAX_GALIL_DATAREC_SIZE] = ""; //Receive buffer char mesg[MAX_GALIL_DATAREC_SIZE] = ""; //Unsolicited buffer char resp[MAX_GALIL_DATAREC_SIZE] = ""; //Solicited buffer + char discard[MAX_GALIL_DATAREC_SIZE] = ""; //discard buffer string inp = ""; //Solicited data concatenated over multiple reads //Sometimes caller puts many commands on one line separated by ; so we must string out_string = output; //Determine number of output terminators to search for from requested command @@ -5721,7 +5759,10 @@ asynStatus GalilController::sync_writeReadController(const char *output, char *i int found_terminators = 0; //Terminator characters found so far unsigned char value; //Used to identify unsolicited traffic bool done = false; //Read complete? + bool term_done = false; // found all terminators? + size_t this_nread; + *nread = 0; //Null user supplied input buffer strcpy(input, ""); //Set timeout for Sync connection @@ -5734,12 +5775,13 @@ asynStatus GalilController::sync_writeReadController(const char *output, char *i while (!done) { //Read any response - status = pSyncOctet_->read(pSyncOctetPvt_, pasynUserSyncGalil_, buf, MAX_GALIL_DATAREC_SIZE, nread, &eomReason); + this_nread = 0; + status = pSyncOctet_->read(pSyncOctetPvt_, pasynUserSyncGalil_, buf, MAX_GALIL_DATAREC_SIZE, &this_nread, &eomReason); //If read successful, search for terminator characters - if (!status && *nread > 0) + if (!status && this_nread > 0) { //Search for terminating characters - for (i = 0; i < *nread; i++) + for (i = 0; i < this_nread; i++) { //Controller responds with ? or : for each command separated by ; if (buf[i] == '?') @@ -5765,35 +5807,72 @@ asynStatus GalilController::sync_writeReadController(const char *output, char *i mesg[j++] = (unsigned char)value; //Terminate the buffers mesg[j] = '\0'; + //Send unsolicited message if last char was line feed + if (mesg[j - 1] == '\n') + { + sendUnsolicitedMessage(mesg); + mesg[0] = '\0'; + j = 0; + } } else { //Byte looks like a solicited packet - //Check for overrun - if (k > MAX_GALIL_DATAREC_SIZE - 2) - return asynError;//No solicited message should be this long return error - resp[k++] = buf[i];//Byte is part of solicited message - //Terminate the buffer - resp[k] = '\0'; + if (!term_done) + { + //Check for overrun + if (k > MAX_GALIL_DATAREC_SIZE - 2) + return asynError;//No solicited message should be this long return error + resp[k++] = buf[i];//Byte is part of solicited message + //Terminate the buffer + resp[k] = '\0'; + } else { + discard[m++] = buf[i]; + discard[m] = '\0'; + } + if (found_terminators == target_terminators) { + term_done = true; + } + } + } + if (found_terminators > target_terminators) + { + std::cerr << "sync_writeReadController(): Found " << found_terminators - target_terminators << " more terminators than expected" << std::endl; } //If received all expected terminators, read is complete - if (found_terminators == target_terminators) + if (term_done) { //Don't attempt any more reads done = true; //stop searching this read, and return the resp, then send unsolicited mesg break; } - } } else //Stop read if any asyn error + { + if (j != 0) { + std::cerr << "sync_writeReadController(): after error - Discarded unsolicited message: " << mesg << " length " << j << std::endl; + } + if (m != 0) { + std::cerr << "sync_writeReadController(): after error - Discarded bytes: " << rawToEscapedString(discard, m) << " length " << m << std::endl; + } return asynError; + } }//while (!done) //Copy solicited response into user supplied buffer strcpy(input, resp); - //Send unsolicited mesg to queue - if (j != 0) - sendUnsolicitedMessage(mesg); + *nread = strlen(resp); + //Check for any remaining unsolicited messages + //Any here did not end in a \n - discard or send anyway? + if (j != 0) { + std::cerr << "sync_writeReadController(): after success - Discarded unsolicited message: " << mesg << " length " << j << std::endl; + std::cerr << "\"" << output << "\" \"" << input << "\"" << std::endl; + //sendUnsolicitedMessage(mesg); + } + if (m != 0) { + std::cerr << "sync_writeReadController(): after success - Discarded bytes: " << rawToEscapedString(discard, m) << " length " << m << std::endl; + std::cerr << "\"" << output << "\" \"" << input << "\"" << std::endl; + } }//write ok return status; } @@ -6657,9 +6736,9 @@ void GalilController::GalilStartController(char *code_file, int burn_program, in timeout_ = 10; //Upload code currently in controller for comparison to generated/user code status = programUpload(&uc); - if (status) //Upload failed - errlogPrintf("\nError uploading code model %s, address %s\n",model_.c_str(), address_.c_str()); - + if (status) { //Upload failed + errlogPrintf("\nError uploading code for comparison, model %s, address %s\nWill assume any on controller code is outdated\n",model_.c_str(), address_.c_str()); + } if ((display_code == 2) || (display_code == 3)) { //Print out the uploaded code from the controller printf("\nUploaded code is\n\n"); @@ -6753,7 +6832,7 @@ void GalilController::GalilStartController(char *code_file, int burn_program, in uc.push_back('\r'); } else { - errlogPrintf("\nError uploading code model %s, address %s\n",model_.c_str(), address_.c_str()); + errlogPrintf("\nError uploading code after transfer to verify, model %s, address %s\n",model_.c_str(), address_.c_str()); } //Start thread 0 if code on controller matches what was downloaded @@ -7232,6 +7311,7 @@ void GalilController::InitializeDataRecord(void) axis_b = atoi(charstr); //Store the data record size datarecsize_ = HEADER_BYTES + (axes * axis_b) + general_b + coord_b; + std::cerr << "Galil data record is " << datarecsize_ << " bytes in size" << std::endl; //DMC300x0 returns 1 18 16 36, search for "DMC31" in model string to determine 16bit ADC if (general_b == 18) return Init30010(model.find("DMC31") != string::npos); //DMC40x0/DMC41x3/DMC50000 8 52 26 36 diff --git a/GalilSup/src/GalilPoller.cpp b/GalilSup/src/GalilPoller.cpp index 2c8aec8..5c6ca10 100644 --- a/GalilSup/src/GalilPoller.cpp +++ b/GalilSup/src/GalilPoller.cpp @@ -232,6 +232,9 @@ void GalilPoller::wakePoller(bool restart_async) //Set most signficant bit for unsolicited bytes strcpy(pC_->cmd_, "CW 1"); status = pC_->sync_writeReadController(); + // set program behaviour on fifo full + strcpy(pC_->cmd_, "CW ,0"); + status = pC_->sync_writeReadController(); } } }