vssp.h
Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2014, ATR, Atsushi Watanabe
00003  * All rights reserved.
00004  *
00005  * Redistribution and use in source and binary forms, with or without
00006  * modification, are permitted provided that the following conditions are met:
00007  *
00008  *     * Redistributions of source code must retain the above copyright
00009  *       notice, this list of conditions and the following disclaimer.
00010  *     * Redistributions in binary form must reproduce the above copyright
00011  *       notice, this list of conditions and the following disclaimer in the
00012  *       documentation and/or other materials provided with the distribution.
00013  *     * Neither the name of the copyright holder nor the names of its
00014  *       contributors may be used to endorse or promote products derived from
00015  *       this software without specific prior written permission.
00016  *
00017  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
00018  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00019  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00020  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
00021  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
00022  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
00023  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
00024  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
00025  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
00026  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00027  * POSSIBILITY OF SUCH DAMAGE.
00028  */
00029 
00030 #ifndef VSSP_H
00031 #define VSSP_H
00032 
00033 #include <boost/asio.hpp>
00034 #include <boost/array.hpp>
00035 #include <boost/format.hpp>
00036 #include <boost/bind.hpp>
00037 #include <boost/serialization/access.hpp>
00038 #include <boost/archive/binary_iarchive.hpp>
00039 #include <boost/shared_array.hpp>
00040 #include <boost/algorithm/string.hpp>
00041 #include <boost/chrono.hpp>
00042 
00043 #include <vector>
00044 #include <string>
00045 
00046 #include <vsspdefs.h>
00047 
00048 namespace vssp
00049 {
00050 
00051 class VsspDriver
00052 {
00053 private:
00054   boost::asio::io_service io_service_;
00055   boost::asio::ip::tcp::socket socket_;
00056   boost::asio::deadline_timer timer_;
00057   bool closed_;
00058   AuxFactorArray aux_factor_;
00059 
00060   boost::function<void(const vssp::Header &, const vssp::RangeHeader &, const vssp::RangeIndex &,
00061                        const boost::shared_array<uint16_t> &, const boost::shared_array<vssp::XYZI> &,
00062                        const boost::chrono::microseconds &delayRead)> cb_point_;
00063   boost::function<void(const vssp::Header &, const vssp::AuxHeader &, const boost::shared_array<vssp::Aux> &,
00064                        const boost::chrono::microseconds &delayRead)> cb_aux_;
00065   boost::function<void(const vssp::Header &, const boost::chrono::microseconds &)> cb_ping_;
00066   boost::function<void(const vssp::Header &, const std::string &)> cb_error_;
00067   boost::function<void(bool)> cb_connect_;
00068   boost::shared_array<const double> tbl_h_;
00069   boost::shared_array<const TableSincos> tbl_v_;
00070   bool tbl_h_loaded_;
00071   bool tbl_v_loaded_;
00072   double timeout_;
00073 
00074   boost::chrono::time_point<boost::chrono::system_clock> time_read_last_;
00075   boost::asio::streambuf buf_;
00076 
00077 public:
00078   VsspDriver()
00079     : socket_(io_service_)
00080     , timer_(io_service_)
00081     , closed_(false)
00082     , aux_factor_(AUX_FACTOR_DEFAULT)
00083     , cb_point_(0)
00084     , cb_aux_(0)
00085     , cb_ping_(0)
00086     , tbl_h_loaded_(false)
00087     , tbl_v_loaded_(false)
00088     , timeout_(1.0)
00089   {
00090   }
00091   void setTimeout(const double to)
00092   {
00093     timeout_ = to;
00094   };
00095   void connect(const char *ip, const unsigned int port, decltype(cb_connect_) cb)
00096   {
00097     cb_connect_ = cb;
00098     boost::asio::ip::tcp::endpoint endpoint(boost::asio::ip::address::from_string(ip), port);
00099     timer_.expires_from_now(boost::posix_time::seconds(timeout_));
00100     timer_.async_wait(boost::bind(&VsspDriver::onTimeoutConnect, this, boost::asio::placeholders::error));
00101     socket_.async_connect(endpoint, boost::bind(&vssp::VsspDriver::onConnect, this, boost::asio::placeholders::error));
00102     time_read_last_ = boost::chrono::system_clock::now();
00103   };
00104   void registerErrorCallback(decltype(cb_error_) cb)
00105   {
00106     cb_error_ = cb;
00107   };
00108   void registerCallback(decltype(cb_point_) cb)
00109   {
00110     cb_point_ = cb;
00111   };
00112   void registerAuxCallback(decltype(cb_aux_) cb)
00113   {
00114     cb_aux_ = cb;
00115   };
00116   void registerPingCallback(decltype(cb_ping_) cb)
00117   {
00118     cb_ping_ = cb;
00119   };
00120   void setInterlace(const int itl)
00121   {
00122     send((boost::format("SET:_itl=0,%02d\n") % itl).str());
00123   };
00124   void requestVerticalTable()
00125   {
00126     send(std::string("GET:tblv\n"));
00127   };
00128   void requestHorizontalTable()
00129   {
00130     send(std::string("GET:tblh\n"));
00131   };
00132   void requestPing()
00133   {
00134     send(std::string("PNG\n"));
00135   };
00136   void requestAuxData(const bool start = 1)
00137   {
00138     send((boost::format("DAT:ax=%d\n") % static_cast<int>(start)).str());
00139   }
00140   void requestData(const bool intensity = 1, const bool start = 1)
00141   {
00142     if (intensity)
00143     {
00144       send((boost::format("DAT:ri=%d\n") % static_cast<int>(start)).str());
00145     }
00146     else
00147     {
00148       send((boost::format("DAT:ro=%d\n") % static_cast<int>(start)).str());
00149     }
00150   };
00151   void receivePackets()
00152   {
00153     timer_.cancel();
00154     timer_.expires_from_now(boost::posix_time::seconds(timeout_));
00155     timer_.async_wait(boost::bind(&VsspDriver::onTimeout, this, boost::asio::placeholders::error));
00156     // Read at least 4 bytes.
00157     // In most case, callback function will be called for each VSSP line.
00158     boost::asio::async_read(socket_, buf_, boost::asio::transfer_at_least(4),
00159                             boost::bind(&VsspDriver::onRead, this, boost::asio::placeholders::error));
00160   };
00161   bool poll()
00162   {
00163     if (!closed_)
00164     {
00165       boost::system::error_code ec;
00166       io_service_.poll(ec);
00167       if (!ec)
00168         return true;
00169       closed_ = true;
00170     }
00171     return false;
00172   };
00173 
00174 private:
00175   void send(const std::string cmd)
00176   {
00177     boost::shared_ptr<std::string> data(new std::string(cmd));
00178     boost::asio::async_write(socket_, boost::asio::buffer(*data),
00179                              boost::bind(&VsspDriver::onSend, this, boost::asio::placeholders::error, data));
00180   };
00181   void onTimeoutConnect(const boost::system::error_code &error)
00182   {
00183     if (!error)
00184     {
00185       closed_ = true;
00186       socket_.cancel();
00187     }
00188   }
00189   void onTimeout(const boost::system::error_code &error)
00190   {
00191     if (!error)
00192     {
00193       closed_ = true;
00194       socket_.cancel();
00195     }
00196   }
00197   void onConnect(const boost::system::error_code &error)
00198   {
00199     timer_.cancel();
00200     if (error)
00201     {
00202       closed_ = true;
00203       cb_connect_(false);
00204       return;
00205     }
00206     cb_connect_(true);
00207   };
00208   void onSend(const boost::system::error_code &error, boost::shared_ptr<std::string> data)
00209   {
00210     if (error)
00211     {
00212       closed_ = true;
00213       return;
00214     }
00215   };
00216   template <class DATA_TYPE>
00217   void rangeToXYZ(const vssp::RangeHeader &RangeHeader, const vssp::RangeIndex &RangeIndex,
00218                   const boost::shared_array<const uint16_t> &index,
00219                   const boost::shared_array<vssp::XYZI> &points)
00220   {
00221     int i = 0;
00222 
00223     const double h_head = RangeHeader.line_head_h_angle_ratio * 2.0 * M_PI / 65535.0;
00224     const double h_tail = RangeHeader.line_tail_h_angle_ratio * 2.0 * M_PI / 65535.0;
00225     const DATA_TYPE *data = boost::asio::buffer_cast<const DATA_TYPE *>(buf_.data());
00226     for (int s = 0; s < RangeIndex.nspots; s++)
00227     {
00228       const double spot = s + RangeHeader.spot;
00229       const double h_rad = h_head + (h_tail - h_head) * tbl_h_[spot];
00230       const double h_cos = cos(h_rad);
00231       const double h_sin = sin(h_rad);
00232       const vssp::XYZI dir(tbl_v_[spot].s, tbl_v_[spot].c, h_sin, h_cos);
00233       for (int e = index[s]; e < index[s + 1]; e++)
00234         points[i++] = dir * data[e];
00235     }
00236   };
00237   void onRead(const boost::system::error_code &error)
00238   {
00239     const auto time_read = boost::chrono::system_clock::now();
00240     auto time = time_read_last_;
00241     const auto duration = time_read - time_read_last_;
00242     const auto length_total = buf_.size();
00243     if (error == boost::asio::error::eof)
00244     {
00245       // Connection closed_
00246       closed_ = true;
00247       return;
00248     }
00249     else if (error)
00250     {
00251       // Connection error
00252       closed_ = true;
00253     }
00254     while (true)
00255     {
00256       if (buf_.size() < sizeof(vssp::Header))
00257       {
00258         break;
00259       }
00260       // Read packet Header
00261       const vssp::Header Header = *boost::asio::buffer_cast<const vssp::Header *>(buf_.data());
00262       if (Header.mark != vssp::VSSP_MARK)
00263       {
00264         // Invalid packet
00265         // find VSSP mark
00266         const uint8_t *data = boost::asio::buffer_cast<const uint8_t *>(buf_.data());
00267         for (size_t i = 1; i < buf_.size() - sizeof(uint32_t); i++)
00268         {
00269           const uint32_t *mark = reinterpret_cast<const uint32_t *>(data + i);
00270           if (*mark == vssp::VSSP_MARK)
00271           {
00272             buf_.consume(i);
00273             break;
00274           }
00275         }
00276         break;
00277       }
00278       if (buf_.size() < Header.length)
00279         break;
00280       const auto delay = boost::chrono::duration_cast<boost::chrono::microseconds>(
00281           boost::chrono::system_clock::now() - time);
00282       time += duration * Header.length / length_total;
00283 
00284       size_t length = Header.length - Header.header_length;
00285       buf_.consume(Header.header_length);
00286 
00287       do
00288       {
00289         switch (Header.type)
00290         {
00291           case TYPE_ERR:
00292           case TYPE_ER:
00293             // Error message
00294             {
00295               const std::string data(boost::asio::buffer_cast<const char *>(buf_.data()));
00296               std::string message(data, 0, Header.length - Header.header_length - 1);
00297               if (!cb_error_.empty())
00298                 cb_error_(Header, message);
00299             }
00300             break;
00301           default:
00302             break;
00303         }
00304         if (Header.status != vssp::STATUS_OK)
00305           break;
00306 
00307         switch (Header.type)
00308         {
00309           case TYPE_GET:
00310             // Response to get command
00311             {
00312               const std::string data(boost::asio::buffer_cast<const char *>(buf_.data()));
00313               std::vector<std::string> lines;
00314               boost::algorithm::split(lines, data, boost::algorithm::is_any_of("\n\r"));
00315               if (lines.size() == 0)
00316                 break;
00317 
00318               if (lines[0].compare(0, 7, "GET:tbl") == 0)
00319               {
00320                 if (lines.size() < 2)
00321                   break;
00322                 std::vector<std::string> cells;
00323                 boost::algorithm::split(cells, lines[1], boost::algorithm::is_any_of(","));
00324                 int i = 0;
00325 
00326                 if (lines[0].compare("GET:tblv") == 0)
00327                 {
00328                   boost::shared_array<TableSincos> tbl_v(new TableSincos[cells.size()]);
00329                   for (auto &cell : cells)
00330                   {
00331                     const double rad(std::strtol(cell.c_str(), NULL, 16) * 2.0 * M_PI / 65535);
00332                     sincos(rad, &tbl_v[i].s, &tbl_v[i].c);
00333                     i++;
00334                   }
00335                   tbl_v_ = tbl_v;
00336                   tbl_v_loaded_ = true;
00337                 }
00338                 else if (lines[0].compare("GET:tblh") == 0)
00339                 {
00340                   boost::shared_array<double> tbl_h(new double[cells.size()]);
00341                   for (auto &cell : cells)
00342                   {
00343                     tbl_h[i] = static_cast<double>(std::strtol(cell.c_str(), NULL, 16) / 65535);
00344                     i++;
00345                   }
00346                   tbl_h_ = tbl_h;
00347                   tbl_h_loaded_ = true;
00348                 }
00349               }
00350             }
00351             break;
00352           case TYPE_SET:
00353             // Response to set command
00354             break;
00355           case TYPE_DAT:
00356             // Response to data request command
00357             break;
00358           case TYPE_VER:
00359             // Response to version request command
00360             break;
00361           case TYPE_PNG:
00362             // Response to ping command
00363             if (!cb_ping_.empty())
00364               cb_ping_(Header, delay);
00365             break;
00366           case TYPE_RI:
00367           case TYPE_RO:
00368             // Range data
00369             if (!tbl_h_loaded_ || !tbl_v_loaded_ || cb_point_.empty())
00370             {
00371               // Something wrong
00372               break;
00373             }
00374             {
00375               // Decode range data Header
00376               const vssp::RangeHeader RangeHeader = *boost::asio::buffer_cast<const vssp::RangeHeader *>(buf_.data());
00377               buf_.consume(RangeHeader.header_length);
00378               length -= RangeHeader.header_length;
00379 
00380               // Decode range index Header
00381               const vssp::RangeIndex RangeIndex = *boost::asio::buffer_cast<const vssp::RangeIndex *>(buf_.data());
00382               size_t index_length = RangeIndex.index_length;
00383               buf_.consume(sizeof(vssp::RangeIndex));
00384               index_length -= sizeof(vssp::RangeIndex);
00385               length -= sizeof(vssp::RangeIndex);
00386 
00387               // Decode range index
00388               boost::shared_array<uint16_t> index(new uint16_t[RangeIndex.nspots + 1]);
00389               std::memcpy(index.get(), boost::asio::buffer_cast<const vssp::RangeIndex *>(buf_.data()),
00390                           sizeof(uint16_t) * (RangeIndex.nspots + 1));
00391               buf_.consume(index_length);
00392               length -= index_length;
00393 
00394               // Decode range data
00395               boost::shared_array<vssp::XYZI> points(new vssp::XYZI[index[RangeIndex.nspots]]);
00396               switch (Header.type)
00397               {
00398                 case TYPE_RI:
00399                   // Range and Intensity
00400                   rangeToXYZ<vssp::DataRangeIntensity>(RangeHeader, RangeIndex, index, points);
00401                   break;
00402                 case TYPE_RO:
00403                   // Range
00404                   rangeToXYZ<vssp::DataRangeOnly>(RangeHeader, RangeIndex, index, points);
00405                   break;
00406               }
00407               cb_point_(Header, RangeHeader, RangeIndex, index, points, delay);
00408             }
00409             break;
00410           case TYPE_AX:
00411             // Aux data
00412             {
00413               // Decode range data Header
00414               const vssp::AuxHeader AuxHeader = *boost::asio::buffer_cast<const vssp::AuxHeader *>(buf_.data());
00415               buf_.consume(AuxHeader.header_length);
00416               length -= AuxHeader.header_length;
00417 
00418               // Decode Aux data
00419               boost::shared_array<vssp::Aux> auxs(new vssp::Aux[AuxHeader.data_count]);
00420               for (int i = 0; i < AuxHeader.data_count; i++)
00421               {
00422                 const vssp::AuxData *data = boost::asio::buffer_cast<const vssp::AuxData *>(buf_.data());
00423                 int offset = 0;
00424                 for (AuxId b = vssp::AX_MASK_LAST; b >= vssp::AX_MASK_FIRST; b = static_cast<AuxId>(b - 1))
00425                 {
00426                   if (AuxHeader.data_bitfield & (1 << static_cast<int>(b)))
00427                     auxs[i][b] = aux_factor_[b] * data[offset++].val;
00428                 }
00429                 buf_.consume(sizeof(int32_t) * offset);
00430                 length -= sizeof(int32_t) * offset;
00431               }
00432               if (!cb_aux_.empty())
00433                 cb_aux_(Header, AuxHeader, auxs, delay);
00434             }
00435             break;
00436           default:
00437             break;
00438         }
00439       }
00440       while (false);
00441       buf_.consume(length);
00442     }
00443     receivePackets();
00444     time_read_last_ = time_read;
00445     return;
00446   };
00447 };
00448 
00449 }  // namespace vssp
00450 
00451 #endif  // VSSP_H


hokuyo3d
Author(s): Atsushi Watanabe
autogenerated on Wed Sep 27 2017 02:52:08