ax2550.cc
Go to the documentation of this file.
00001 #include "ax2550/ax2550.h"
00002 
00003 #include <iostream>
00004 #include <sstream>
00005 #include <cstdio>
00006 #include <cstdarg>
00007 
00008 #include <boost/algorithm/string.hpp>
00009 
00010 using namespace ax2550;
00011 
00012 using std::string;
00013 using std::stringstream;
00014 using serial::Serial;
00015 using serial::utils::SerialListener;
00016 using serial::utils::BufferedFilterPtr;
00017 using serial::utils::TokenPtr;
00018 
00019 inline void defaultInfo(const string &msg) {
00020   std::cout << "AX2550 Info: " << msg << std::endl;
00021 }
00022 
00023 // Tokenizes on carriage return or W
00024 inline void tokenizer(const std::string &data,
00025                       std::vector<TokenPtr> &tokens)
00026 {
00027   // Find the number of W's
00028   size_t number_of_Ws =
00029     (size_t) std::count(data.begin(), data.end(), 'W');
00030   // Create tokens for each of the acks
00031   for(size_t i = 0; i < number_of_Ws; ++i) {
00032     tokens.push_back(TokenPtr( new std::string("W") ));
00033   }
00034   // Split on W
00035   typedef std::vector<std::string> find_vector_type;
00036   find_vector_type t;
00037   boost::split(t, data, boost::is_any_of("W"));
00038   // Rejoin without the W's
00039   string new_data = "";
00040   for (find_vector_type::iterator it = t.begin(); it != t.end(); ++it) {
00041     new_data.append((*it));
00042   }
00043   // Split on \r or \n
00044   find_vector_type t2;
00045   boost::split(t2, new_data, boost::is_any_of("\r\n"));
00046   for (find_vector_type::iterator it = t2.begin(); it != t2.end(); ++it) {
00047     tokens.push_back(TokenPtr( new std::string(*it) ));
00048   }
00049 }
00050 
00051 AX2550::AX2550 (string port)
00052 : port_(""), serial_port_(NULL), serial_listener_(1),
00053   connected_(false), synced_(false)
00054 {
00055   this->port_ = port;
00056   // Set default callbacks
00057   this->info = defaultInfo;
00058   this->watch_dog_callback = NULL;
00059   // Set the custom tokenizer
00060   this->serial_listener_.setTokenizer(tokenizer);
00061   // If the port is set, connect
00062   if (!this->port_.empty()) {
00063     this->connect();
00064   }
00065 }
00066 
00067 AX2550::~AX2550 () {
00068   this->disconnect();
00069 }
00070 
00071 void
00072 AX2550::connect (string port) {
00073   // Make sure we aren't already connected
00074   if (this->connected_) {
00075     AX2550_THROW(ConnectionException, "already connected");
00076   }
00077   // If a port was passed in, set the interal one
00078   if (!port.empty()) {
00079     this->port_ = port;
00080   }
00081   // Check to see if the port is set to something
00082   if (this->port_.empty()) {
00083     AX2550_THROW(ConnectionException, "serial port name is empty");
00084   }
00085   // Call disconnect to ensure we are in a clean state
00086   this->disconnect();
00087   // Setup the filters
00088   this->setupFilters_();
00089   // Setup the serial port
00090   this->serial_port_ = new Serial();
00091   this->serial_port_->setPort(this->port_);
00092   this->serial_port_->setBaudrate(9600);
00093   this->serial_port_->setParity(serial::parity_even);
00094   this->serial_port_->setStopbits(serial::stopbits_one);
00095   this->serial_port_->setBytesize(serial::sevenbits);
00096   serial::Timeout to = serial::Timeout::simpleTimeout(10);
00097   this->serial_port_->setTimeout(to);
00098   // Open the serial port
00099   this->serial_port_->open();
00100   // Setup the serial listener
00101   this->serial_listener_.setChunkSize(2);
00102   this->serial_listener_.startListening((*this->serial_port_));
00103   // Synchronize with the motor controller
00104   this->sync_();
00105   this->connected_ = true;
00106 }
00107 
00108 void
00109 AX2550::disconnect () {
00110   this->connected_ = false;
00111   if (this->serial_listener_.isListening()) {
00112     this->serial_listener_.stopListening();
00113   }
00114   if (this->serial_port_ != NULL) {
00115     delete this->serial_port_;
00116     this->serial_port_ = NULL;
00117   }
00118 }
00119 
00120 bool
00121 AX2550::isConnected () {
00122   return this->connected_;
00123 }
00124 
00125 bool
00126 AX2550::issueCommand (const string &command, string &fail_why) {
00127   // Setup an echo filter
00128   BufferedFilterPtr echo_filt = this->serial_listener_.createBufferedFilter(
00129     SerialListener::exactly(command));
00130   this->serial_port_->write(command+"\r");
00131   // Listen for the echo
00132   if (echo_filt->wait(50).empty()) {
00133     fail_why = "failed to receive an echo";
00134     return false;
00135   }
00136   return true;
00137 }
00138 
00139 inline string
00140 string_format(const string &fmt, ...) {
00141   int size = 100;
00142   string str;
00143   va_list ap;
00144   while (1) {
00145     str.resize(size);
00146     va_start(ap, fmt);
00147     int n = vsnprintf((char *)str.c_str(), size, fmt.c_str(), ap);
00148     va_end(ap);
00149     if (n > -1 && n < size) {
00150       str.resize(n);
00151       return str;
00152     }
00153     if (n > -1) {
00154       size = n + 1;
00155     } else {
00156       size *= 2;
00157     }
00158   }
00159   return str;
00160 }
00161 
00162 void
00163 AX2550::move (double speed, double direction) {
00164   if(!this->connected_) {
00165     AX2550_THROW(CommandFailedException, "must be connected to move");
00166   }
00167   // Grab the lock
00168   boost::mutex::scoped_lock lock(this->mc_mutex);
00169   string serial_buffer;
00170   unsigned char speed_hex, direction_hex;
00171   string fail_why;
00172   // Create the speed command
00173   speed_hex = (unsigned char) (fabs(speed));
00174   if(speed < 0) {
00175     serial_buffer = string_format("!a%.2X", speed_hex);
00176   } else {
00177     serial_buffer = string_format("!A%.2X", speed_hex);
00178   }
00179   // Issue the speed command
00180   if (!this->issueCommand(serial_buffer, fail_why)) {
00181     AX2550_THROW(CommandFailedException, fail_why.c_str());
00182   }
00183   // Listen for an ack or nak
00184   this->ack_nak_filt_->clear();
00185   string result = this->ack_nak_filt_->wait(100);
00186   if (result != "+") {
00187     if (result == "-") {
00188       AX2550_THROW(CommandFailedException, "nak received, command failed");
00189     }
00190     AX2550_THROW(CommandFailedException, "did not receive an ack or nak");
00191   }
00192   // Create the direction command
00193   direction_hex = (unsigned char) (fabs(direction));
00194   if(direction < 0) {
00195     serial_buffer = string_format("!b%.2X", direction_hex);
00196   } else {
00197     serial_buffer = string_format("!B%.2X", direction_hex);
00198   }
00199   // Issue the direction command
00200   if (!this->issueCommand(string(serial_buffer), fail_why)) {
00201     AX2550_THROW(CommandFailedException, fail_why.c_str());
00202   }
00203   // Listen for an ack or nak
00204   this->ack_nak_filt_->clear();
00205   result = this->ack_nak_filt_->wait(100);
00206   if (result != "+") {
00207     if (result == "-") {
00208       AX2550_THROW(CommandFailedException, "nak received, command failed");
00209     }
00210     AX2550_THROW(CommandFailedException, "did not receive an ack or nak");
00211   }
00212 }
00213 
00214 void
00215 AX2550::queryEncoders (long &encoder1, long &encoder2, bool relative) {
00216   if(!this->connected_) {
00217     AX2550_THROW(CommandFailedException, "must be connected to query "
00218                                          "the encoders");
00219   }
00220   // Check the count in the encoder filter, should be 0
00221   if (this->encoders_filt_->count()) {
00222     stringstream ss;
00223     ss << "There were " << this->encoders_filt_->count()
00224        << " orphaned encoder messages in the filter...";
00225     this->warn(ss.str());
00226   }
00227   // Clear the encoder queue
00228   this->encoders_filt_->clear();
00229   // Grab the lock
00230   boost::mutex::scoped_lock lock(this->mc_mutex);
00231   // Query the first encoder
00232   string cmd1, cmd2, fail_why;
00233   if (relative) {
00234     cmd1 = "?q4";
00235   } else {
00236     cmd1 = "?q0";
00237   }
00238   this->serial_port_->write(cmd1+"\r");
00239   // Query the second encoder
00240   if (relative) {
00241     cmd2 = "?q5";
00242   } else {
00243     cmd2 = "?q1";
00244   }
00245   this->serial_port_->write(cmd2+"\r");
00246   // Listen for Query 1
00247   string response = this->encoders_filt_->wait(100);
00248   if (response.empty()) {
00249     string msg = string("failed to receive a response from ")+cmd1;
00250     AX2550_THROW(CommandFailedException, msg.c_str());
00251   }
00252   // Parse the response
00253   char fillbyte;
00254   // Determine sign
00255   if(response.substr(0,1).find_first_of("01234567") != std::string::npos) {
00256     // Then positive
00257     fillbyte = '0';
00258   } else {
00259     // Then negative
00260     fillbyte = 'F';
00261   }
00262   // Add filler bytes
00263   size_t difference = 8 - response.length();
00264   string filler(difference, fillbyte);
00265   response.insert(0, filler);
00266   // Convert to integer
00267   signed int encoder = 0;
00268   sscanf(response.c_str(), "%X", &encoder);
00269   encoder1 = encoder;
00270   // reset stuff
00271   fillbyte = '0';
00272   difference = 0;
00273   filler = "";
00274   encoder = 0;
00275   // Listen for Query 2
00276   response = this->encoders_filt_->wait(100);
00277   if (response.empty()) {
00278     string msg = "failed to receive a response from "+cmd2;
00279     AX2550_THROW(CommandFailedException, msg.c_str());
00280   }
00281   // Parse the response
00282   // Determine sign
00283   if(response.substr(0,1).find_first_of("01234567") != std::string::npos) {
00284     // Then positive
00285     fillbyte = '0';
00286   } else {
00287     // Then negative
00288     fillbyte = 'F';
00289   }
00290   // Add filler bytes
00291   difference = 8 - response.length();
00292   filler = string(difference, fillbyte);
00293   response.insert(0, filler);
00294   // Convert to integer
00295   encoder = 0;
00296   sscanf(response.c_str(), "%X", &encoder);
00297   encoder2 = encoder;
00298 }
00299 
00300 void
00301 AX2550::sync_ () {
00302   if (this->synced_)
00303     return;
00304   boost::mutex::scoped_lock lock(this->mc_mutex);
00305   // Reset the motor controller
00306   this->serial_port_->write("%rrrrrr\r");
00307   // Wait for an R/C Message
00308   {
00309     BufferedFilterPtr rc_msg_filt =
00310       this->serial_listener_.createBufferedFilter(
00311         SerialListener::startsWith(":"));
00312     rc_msg_filt->clear();
00313     if (rc_msg_filt->wait(2000).empty()) {
00314       AX2550_THROW(SynchronizationException,
00315         "did not receive an R/C message after reset");
00316     }
00317   }
00318   // Write \r to the port until in serial mode
00319   BufferedFilterPtr ok_filt =
00320     this->serial_listener_.createBufferedFilter(
00321       SerialListener::contains("OK"));
00322   bool got_ok = false;
00323   for (int i = 0; i < 20; ++i) {
00324     this->serial_port_->write("\r");
00325     if (!ok_filt->wait(50).empty()) {
00326       got_ok = true;
00327       break;
00328     }
00329   }
00330   // Check to see if we ever got an OK
00331   if (!got_ok) {
00332     AX2550_THROW(SynchronizationException, "failed to get into serial mode");
00333   }
00334   this->synced_ = true;
00335   this->info("Synchronized with the ax2550");
00336 }
00337 
00338 inline bool
00339 isAnEncoderMsg (const string &token) {
00340   string test = "0123456789abcdefABCDEF";
00341 #if 0
00342   std::cout << "isAnEncoderMsg: Is `" << token
00343             << "` an encoder message?: ";
00344   if (token.substr(0,1).find_first_of(test) != string::npos) {
00345     std::cout << "True";
00346   } else {
00347     std::cout << "False";
00348   }
00349   std::cout << std::endl;
00350 #endif
00351   // If token[0] is any of 0123456789abcdefABCDEF (hex)
00352   if (token.substr(0,1).find_first_of(test) != string::npos) {
00353     return true;
00354   }
00355   return false;
00356 }
00357 
00358 void
00359 AX2550::watchDogCallback_ (const string &token) {
00360   if (this->watch_dog_callback != NULL) {
00361     this->watch_dog_callback();
00362   }
00363 }
00364 
00365 inline bool
00366 isAckOrNak (const string &token) {
00367 #if 0
00368   std::cout << "isAckOrNak: Is `" << token
00369             << "` an ack or nak?: ";
00370   if (token.find_first_of("+-") != string::npos) {
00371     std::cout << "True";
00372   } else {
00373     std::cout << "False";
00374   }
00375   std::cout << std::endl;
00376 #endif
00377   if (token.find_first_of("+-") != string::npos) {
00378     return true;
00379   }
00380   return false;
00381 }
00382 
00383 void
00384 AX2550::setupFilters_ () {
00385   // Setup the encoder filter
00386   this->encoders_filt_ =
00387     this->serial_listener_.createBufferedFilter(isAnEncoderMsg);
00388   // Setup the watchdog filter
00389   this->watch_dog_filt_ = this->serial_listener_.createFilter(
00390     SerialListener::exactly("W"),
00391     boost::bind(&AX2550::watchDogCallback_, this, _1));
00392   // Setup ack/nak filter
00393   this->ack_nak_filt_ =
00394     this->serial_listener_.createBufferedFilter(isAckOrNak);
00395   // // Setup R/C message filter
00396   // this->rc_msg_filt_ = this->serial_listener_.createBufferedFilter(
00397   //   SerialListener::startsWith(":"));
00398 }
00399 
00400 
00401 
00402 
00403 


ax2550
Author(s): William Woodall , Michael Carroll
autogenerated on Sat Jun 8 2019 18:26:02