#include "gcl_galil.h" using namespace std; void ec(GReturn rc) { switch (rc) { case G_NO_ERROR: return; case G_TIMEOUT: throw string("1010 TIMEOUT ERROR. Galil::command() took longer than timeout to return\n"); break; case G_OPEN_ERROR: throw string("5002 OPEN ERROR. Galil::Galil() failed to open device\n"); break; case G_COMMAND_CALLED_WITH_ILLEGAL_COMMAND: throw string("7020 INVALID COMMAND ERROR. DL, ED, and QD are not allowed from Galil::command()\n"); break; case G_DATA_RECORD_ERROR: throw string("6150 WRONG BUS ERROR. Galil::record(\"DR\") not supported on RS-232. Use Ethernet or Galil::record(\"QR\")\n"); break; case G_FIRMWARE_LOAD_NOT_SUPPORTED: throw string("6130 WRONG BUS ERROR. Galil::firmwareDownloadFile() isn't allowed via Ethernet. Use RS-232\n"); //21x3 etc. break; case G_ILLEGAL_DATA_IN_PROGRAM: throw string("7060 INVALID CHARACTER ERROR. Galil::programDownload() can't download program with backslash \\ character. Use {^92} in MG commands\n"); break; case G_UNABLE_TO_COMPRESS_PROGRAM_TO_FIT: throw string("7061 INVALID LENGTH ERROR. Galil::programDownload() can't compress\n"); break; case G_BAD_RESPONSE_QUESTION_MARK: throw string("2010 COMMAND ERROR. Galil::command() got ? instead of : response.\n"); break; case G_BAD_FILE: throw string("4000 FILE ERROR. Galil::Galil() failed to open file\n"); break; case G_UNSUPPORTED_FUNCTION: throw string("6000 WRONG BUG ERROR. Function isn't allowed on this bus\n"); break; /* case G_DATA_RECORD_ERROR: case G_BAD_ADDRESS: case G_BAD_LOST_DATA: case G_BAD_VALUE_RANGE: case G_BAD_FULL_MEMORY: case G_ARRAY_NOT_DIMENSIONED: case G_GCLIB_ERROR: case G_GCLIB_UTILITY_ERROR: case G_INVALID_PREPROCESSOR_OPTIONS: */ default: //couldn't find error map, make a gcl-like error from gclib error code char buf[SMALLBUF]; GError(rc, buf, sizeof(buf)); //get the error message throw (to_string(rc) + " GCLIB ERROR. " + string(buf) + '\n'); }//switch } //! Takes a GCL address string and returns the equivalent gclib address string string AddressConvert(const string& gcl_address) { if (gcl_address.size() == 0) //this implementation does not present the user with a connections dialog if a nullstring is passed. throw string("5005 OPEN ERROR. Null string specified in Galil::Galil()\n"); if (gcl_address.find("OFFLINE") != string::npos) throw string("5001 OPEN ERROR. OFFLINE specified to Galil::Galil()\n"); vector<string> args; size_t start = 0; size_t i; for (i = 0; i < gcl_address.size(); i++) //split into tokens { if (gcl_address[i] == ' ') { if (start < i) //if not zero length args.push_back(gcl_address.substr(start, i - start)); start = i + 1; //jump over space } } if (start < i) //one token still remaining args.push_back(gcl_address.substr(start, i - start)); bool ei = true; //bools to remember if data streams should be subscribed to, gcl subscribes by default bool mg = true; bool dr = true; string address; args.push_back(""); //allow safe indexing one past the end of the tokens list for (i = 0; i < args.size() - 1; i++) { if (args[i] == "-p1") address.append("--p1 " + args[++i] + " "); else if (args[i] == "-p2") address.append("--p2 " + args[++i] + " "); else if (args[i] == "-udp") address.append("--command UDP "); //TCP is default in gclib else if ((args[i] == "-ei") && (args[++i] == "0")) ei = false; else if ((args[i] == "-mg") && (args[++i] == "0")) mg = false; else if ((args[i] == "-dr") && (args[++i] == "0")) dr = false; else if (args[i] == "-t") address.append("-t " + args[++i] + " "); else if (args[i] == "-s") { address.append("-s NONE "); ei = false; mg = false; dr = false; } else if (args[i] == "-d") ++i; //Debug not yet supported else if (args[i] == "-l") ++i; //Long-timeout not used else if (args[i].find("COM") != string::npos) { address.append(args[i] + " --baud " + args[i + 1] + " "); //COM1 --baud 115200 i++;// jump over baud rate on next pass } else address.append(args[i] + " "); //keep unrecognized tokens, e.g. 192.168.0.123 } if (ei) address.append("-s EI "); if (mg) address.append("-s MG "); if (dr) address.append("-s DR "); address.append("-d "); //add direct-connect switch return address; } string Galil::libraryVersion() { char buf[SMALLBUF]; //function is static, so can't access GalilPrivate members ec(GVersion(buf, sizeof(buf))); return "Galil2.dll wrapper, gclib " + string(buf); } vector<string> Galil::addresses() { vector<string> addresses; //this is the return collection char buf[1024]; ec(GAddresses(buf, sizeof(buf))); //buf now holds the list, but the gcl only holds the first cell short commas = 0; //counter for comma delimiters string address; //temp buffer for holding chars size_t len = strlen(buf); //don't call strlen every iteration for (size_t i = 0; i < len; i++) { if (buf[i] == '\n') //end of line { addresses.push_back(address); address.clear(); commas = 0; //start counting commas from zero continue; } if (commas) //already saw first comma, keep looking for end of line continue; if (buf[i] == ',') { commas++; //count it continue; } address.push_back(buf[i]); //keep the char }//for return addresses; } Galil::Galil(std::string address) { GCon g; timeout_ms = 500; //default value for gcl ec(GOpen(AddressConvert(address).c_str(), &g)); d = new GalilPrivate(this, g); d->InitializeDataRecord(); } Galil::~Galil() { GClose(d->g); //close the connection in gclib delete d; //free memory } string Galil::connection() { ec(GInfo(d->g, d->tbuf, sizeof(d->tbuf))); return string(d->tbuf); } string Galil::command(const std::string& command, const std::string& terminator, const std::string& ack, bool trim) { /* * Note: This wrapper ignores terminator and ack. GCommand does not require them to operate. * If terminator and/or ack are desired, this function can be implemented with GRead and GWrite. * Please contact softwaresupport@galil.com with questions/concerns. */ ec(GTimeout(d->g, (short)timeout_ms)); //obey timeout_ms setting GSize bytes_read; char* response; if (trim) ec(GCmdT(d->g, command.c_str(), d->tbuf, sizeof(d->tbuf), &response)); else { ec(GCommand(d->g, command.c_str(), d->tbuf, sizeof(d->tbuf), &bytes_read)); response = d->tbuf; } ec(GTimeout(d->g, -1)); //replace timeout return string(response); } double Galil::commandValue(const std::string& command) { double value; ec(GTimeout(d->g, (short)timeout_ms)); ec(GCmdD(d->g, command.c_str(), &value)); ec(GTimeout(d->g, -1)); return value; } string Galil::message(int timeout_ms) { /* * From GCL documentation, http://www.galil.com/sw/pub/all/doc/galiltools/html/library.html#message * "If a zero timeout is specified, no errors will be thrown; message() will simply return the waiting queue (even if it is empty, ""). * A -1 timeout will cause message() to block until a message is received." */ GReturn rc = G_NO_ERROR; short t = 5000; //nominal timeout, to be used if -1 is specified if (timeout_ms >= 0) t = (short) timeout_ms; ec(GTimeout(d->g, t)); do { rc = GMessage(d->g, d->tbuf, sizeof(d->tbuf)); } while(timeout_ms == -1 && rc == G_TIMEOUT); ec(GTimeout(d->g, -1)); if (rc == G_GCLIB_NON_BLOCKING_READ_EMPTY) return ""; ec(rc); //check the other possible return codes return string(d->tbuf); } int Galil::interrupt(int timeout_ms) { ec(GTimeout(d->g, (short)timeout_ms)); GStatus status; ec(GInterrupt(d->g, &status)); ec(GTimeout(d->g, -1)); return (int)status; } string Galil::programUpload() { ec(GTimeout(d->g, (short)timeout_ms)); ec(GProgramUpload(d->g, d->tbuf, sizeof(d->tbuf))); ec(GTimeout(d->g, -1)); return string(d->tbuf); } void Galil::programDownload(const std::string& program) { ec(GTimeout(d->g, (short)timeout_ms)); ec(GProgramDownload(d->g, program.c_str(), 0)); //no special preprocessor directives ec(GTimeout(d->g, -1)); } void Galil::programUploadFile(const std::string& file) { ec(GTimeout(d->g, (short)timeout_ms)); ec(GProgramUploadFile(d->g, file.c_str())); ec(GTimeout(d->g, -1)); } void Galil::programDownloadFile(const std::string& file) { ec(GTimeout(d->g, (short)timeout_ms)); ec(GProgramDownloadFile(d->g, file.c_str(), 0)); //no special preprocessor directives ec(GTimeout(d->g, -1)); } vector<double> Galil::arrayUpload(const std::string& name) { ec(GTimeout(d->g, (short)timeout_ms)); ec(GArrayUpload(d->g, name.c_str(), G_BOUNDS, G_BOUNDS, G_CR, d->tbuf, sizeof(d->tbuf))); ec(GTimeout(d->g, -1)); vector<double> vals; int len = strlen(d->tbuf); //gclib null terminates array buf char* start = d->tbuf; //start hold pointer to begining of string to atof for (int i = 0; i < len; i++) { if (d->tbuf[i] == '\r') { vals.push_back(atof(start)); start = d->tbuf + i; } } vals.push_back(atof(start)); //last number still left in tbuf return vals; } void Galil::arrayDownload(const std::vector<double>& array, const std::string& name) { //GArrayDownload requires a cstring containing the data string array_str; for (size_t i = 0; i < array.size(); i++) { sprintf(d->tbuf, "%0.4f\r", array[i]); array_str.append(d->tbuf); } ec(GTimeout(d->g, (short)timeout_ms)); //Galil::arrayDownload auto-dimensions the array table string command = "DA " + name + "[]"; ec(GCmd(d->g, command.c_str())); //Deallocate array command = "DM " + name + "[" + to_string(array.size()) + "]"; ec(GCmd(d->g, command.c_str())); //Allocate array with correct dimension ec(GArrayDownload(d->g, name.c_str(), G_BOUNDS, G_BOUNDS, array_str.c_str())); ec(GTimeout(d->g, -1)); } void Galil::arrayUploadFile(const std::string& file, const std::string& names) { ec(GTimeout(d->g, (short)timeout_ms)); ec(GArrayUploadFile(d->g, file.c_str(), names.c_str())); ec(GTimeout(d->g, -1)); } void Galil::arrayDownloadFile(const std::string& file) { ec(GTimeout(d->g, (short)timeout_ms)); ec(GArrayDownloadFile(d->g, file.c_str())); ec(GTimeout(d->g, -1)); } void Galil::firmwareDownloadFile(const std::string& file) { ec(GTimeout(d->g, (short)timeout_ms)); ec(GFirmwareDownload(d->g, file.c_str())); ec(GTimeout(d->g, -1)); } int Galil::write(const std::string& bytes) { ec(GTimeout(d->g, (short)timeout_ms)); ec(GWrite(d->g, bytes.data(), bytes.length())); ec(GTimeout(d->g, -1)); return bytes.length(); } string Galil::read() { ec(GTimeout(d->g, (short)timeout_ms)); GSize bytes_read; ec(GRead(d->g, d->tbuf, sizeof(d->tbuf), &bytes_read)); ec(GTimeout(d->g, -1)); return string(d->tbuf, bytes_read); //data is not null-terminatated, so construct string "from buffer" }