Go to the documentation of this file.00001 #include "MSP.hpp"
00002
00003 #include <future>
00004 #include <iostream>
00005 #include <type_traits>
00006
00007 namespace msp {
00008
00009 MSP::MSP() : port(io), wait(10) { }
00010
00011 MSP::MSP(const std::string &device, const uint baudrate) : port(io), wait(10) {
00012 connect(device, baudrate);
00013 }
00014
00015 bool MSP::connect(const std::string &device, const uint baudrate) {
00016 this->device = device;
00017 try {
00018 port.open(device);
00019 }
00020 catch(const asio::system_error &e) {
00021 throw NoConnection(device, e.what());
00022 }
00023
00024 port.set_option(asio::serial_port::baud_rate(baudrate));
00025 port.set_option(asio::serial_port::parity(asio::serial_port::parity::none));
00026 port.set_option(asio::serial_port::character_size(asio::serial_port::character_size(8)));
00027 port.set_option(asio::serial_port::stop_bits(asio::serial_port::stop_bits::one));
00028
00029
00030 clear();
00031
00032 std::cout<<"Connected to: "<<device<<std::endl;
00033 return true;
00034 }
00035
00036 bool MSP::request(msp::Request &request) {
00037 if(!sendData(request.id()))
00038 return false;
00039
00040 std::this_thread::sleep_for(std::chrono::microseconds(wait));
00041
00042 try {
00043 const DataID pkg = receiveData();
00044 if(pkg.id==uint8_t(request.id()))
00045 request.decode(pkg.data);
00046 return pkg.id==uint8_t(request.id());
00047 }
00048 catch(const MalformedHeader &e) {
00049 std::cerr<<e.what()<<std::endl;
00050 return false;
00051 }
00052 catch(const WrongCRC &e) {
00053 std::cerr<<e.what()<<std::endl;
00054 return false;
00055 }
00056 catch(const UnknownMsgId &e) {
00057 if(e.getInvalidID()==uint8_t(request.id())) {
00058 std::cerr<<e.what()<<std::endl;
00059 }
00060 return false;
00061 }
00062 catch(msp::NoData) { return false; }
00063 catch(asio::system_error) { return false; }
00064 }
00065
00066 bool MSP::request_block(msp::Request &request) {
00067 bool success = false;
00068 while(success==false) {
00069
00070 if(!sendData(request.id())) {
00071 success = false;
00072 continue;
00073 }
00074
00075 std::this_thread::sleep_for(std::chrono::microseconds(wait));
00076
00077 try {
00078 const DataID pkg = receiveData();
00079 success = (pkg.id==uint8_t(request.id()));
00080 if(success)
00081 request.decode(pkg.data);
00082 }
00083 catch(const MalformedHeader &e) {
00084 std::cerr<<e.what()<<std::endl;
00085 success = false;
00086 }
00087 catch(const WrongCRC &e) {
00088 std::cerr<<e.what()<<std::endl;
00089 success = false;
00090 }
00091 catch(const UnknownMsgId &e) {
00092 if(e.getInvalidID()==uint8_t(request.id())) {
00093 std::cerr<<e.what()<<std::endl;
00094 return false;
00095 }
00096 success = false;
00097 }
00098 catch(msp::NoData) { success = false; }
00099 catch(asio::system_error) { success = false; }
00100 }
00101
00102 return true;
00103 }
00104
00105 bool MSP::request_wait(msp::Request &request, const uint wait_ms, const uint min_payload_size) {
00106 const std::chrono::milliseconds wait(wait_ms);
00107
00108 bool success = false;
00109 while(success==false) {
00110
00111 while(sendData(request.id())!=true);
00112
00113 std::this_thread::sleep_for(wait);
00114
00115 try {
00116 if(hasData()>=int(FRAME_SIZE+min_payload_size)) {
00117 DataID pkg = receiveData();
00118 success = (pkg.id==uint8_t(request.id()));
00119 if(success)
00120 request.decode(pkg.data);
00121 }
00122 }
00123 catch(const MalformedHeader &e) {
00124 std::cerr<<e.what()<<std::endl;
00125 success = false;
00126 }
00127 catch(const WrongCRC &e) {
00128 std::cerr<<e.what()<<std::endl;
00129 success = false;
00130 }
00131 catch(const UnknownMsgId &e) {
00132 if(e.getInvalidID()==uint8_t(request.id())) {
00133 std::cerr<<e.what()<<std::endl;
00134 return false;
00135 }
00136 success = false;
00137 }
00138 catch(msp::NoData) { success = false; }
00139 catch(asio::system_error) { success = false; }
00140 }
00141
00142 return true;
00143 }
00144
00145 bool MSP::respond(const msp::Response &response) {
00146 if(!sendData(response.id(), response.encode()))
00147 return false;
00148
00149 std::this_thread::sleep_for(std::chrono::microseconds(wait));
00150
00151 try {
00152 const DataID pkg = receiveData();
00153 return (pkg.id==uint8_t(response.id()) && pkg.data.size()==0);
00154 }
00155 catch(const MalformedHeader &e) {
00156 std::cerr<<e.what()<<std::endl;
00157 return false;
00158 }
00159 catch(asio::system_error) { return false; }
00160 }
00161
00162 bool MSP::respond_block(const msp::Response &response) {
00163 bool success = false;
00164 while(success==false) {
00165
00166 if(!sendData(response.id(), response.encode())) {
00167 success = false;
00168 continue;
00169 }
00170
00171 try {
00172 const DataID pkg = receiveData();
00173 success = (pkg.id==uint8_t(response.id()) && pkg.data.size()==0);
00174 }
00175 catch(const MalformedHeader &e) {
00176 std::cerr<<e.what()<<std::endl;
00177 success = false;
00178 }
00179 catch(msp::NoData) { success = false; }
00180 catch(asio::system_error) { success = false; }
00181 }
00182
00183 return true;
00184 }
00185
00186 bool MSP::sendData(const uint8_t id, const ByteVector &data) {
00187 ByteVector msg;
00188 msg.reserve(6+data.size());
00189
00190 msg.push_back('$');
00191 msg.push_back('M');
00192 msg.push_back('<');
00193 msg.push_back(uint8_t(data.size()));
00194 msg.push_back(id);
00195 msg.insert(msg.end(), data.begin(), data.end());
00196 msg.push_back( crc(id, data) );
00197
00198 return write(msg);
00199 }
00200
00201 DataID MSP::receiveData() {
00202
00203 if(hasData()<1) {
00204 throw NoData();
00205 }
00206
00207 while( char(read()) != '$');
00208
00209 const char hdr = char(read());
00210 if(hdr != 'M')
00211 throw MalformedHeader('M', uint8_t(hdr));
00212
00213 const char com_state = char(read());
00214 if(com_state != '>') {
00215 switch(com_state) {
00216 case '!': {
00217
00218 read();
00219 const uint8_t id = read();
00220 throw UnknownMsgId(id);
00221 }
00222 default:
00223 throw MalformedHeader('>', uint8_t(com_state));
00224 }
00225 }
00226
00227
00228 const uint8_t data_size = read();
00229
00230
00231 const uint8_t id = read();
00232
00233
00234 const ByteVector data = read(data_size);
00235
00236
00237 const uint8_t rcv_crc = read();
00238 const uint8_t exp_crc = crc(id, data);
00239
00240 if(rcv_crc!=exp_crc)
00241 throw WrongCRC(id, exp_crc, rcv_crc);
00242
00243 return DataID(data,id);
00244 }
00245
00246 uint8_t MSP::crc(const uint8_t id, const ByteVector &data) {
00247 uint8_t crc = uint8_t(data.size())^id;
00248
00249 for(uint8_t d : data)
00250 crc = crc^d;
00251
00252 return crc;
00253 }
00254
00255 bool MSP::write(const std::vector<uint8_t> &data) {
00256 std::lock_guard<std::mutex> lock(lock_write);
00257 try {
00258 const std::size_t bytes_written = asio::write(port, asio::buffer(data.data(), data.size()));
00259 return (bytes_written==data.size());
00260 }
00261 catch(const asio::system_error &e) {
00262 throw NoConnection(device, e.what());
00263 }
00264 }
00265
00266 size_t MSP::read(std::vector<uint8_t> &data) {
00267 std::lock_guard<std::mutex> lock(lock_read);
00268 return asio::read(port, asio::buffer(data.data(), data.size()));
00269 }
00270
00271 std::vector<uint8_t> MSP::read(std::size_t n_bytes) {
00272 std::vector<uint8_t> data(n_bytes);
00273 const size_t nread = read(data);
00274 assert(nread==n_bytes);
00275 return data;
00276 }
00277
00278 int MSP::hasData() {
00279 #if __unix__ || __APPLE__
00280 int available_bytes;
00281 if(ioctl(port.native_handle(), FIONREAD, &available_bytes)!=-1) {
00282 return available_bytes;
00283 }
00284 else {
00285 return -1;
00286 }
00287 #elif _WIN32
00288 COMSTAT comstat;
00289 if (ClearCommError(port.native_handle(), NULL, &comstat) == true) {
00290 return comstat.cbInQue;
00291 }
00292 else {
00293 return -1;
00294 }
00295 #else
00296 #warning "hasData() will be unimplemented"
00297 #endif
00298 }
00299
00300 void MSP::clear() {
00301 #if __unix__ || __APPLE__
00302 tcflush(port.native_handle(),TCIOFLUSH);
00303 #elif _WIN32
00304 PurgeComm(port.native_handle(), PURGE_TXCLEAR);
00305 #else
00306 #warning "clear() will be unimplemented"
00307 #endif
00308 }
00309
00310 }