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(
00061       const vssp::Header &,
00062       const vssp::RangeHeader &,
00063       const vssp::RangeIndex &,
00064       const boost::shared_array<uint16_t> &,
00065       const boost::shared_array<vssp::XYZI> &,
00066       const boost::posix_time::ptime &)> cb_point_;
00067   boost::function<void(
00068       const vssp::Header &,
00069       const vssp::AuxHeader &,
00070       const boost::shared_array<vssp::Aux> &,
00071       const boost::posix_time::ptime &)> cb_aux_;
00072   boost::function<void(
00073       const vssp::Header &,
00074       const boost::posix_time::ptime &)> cb_ping_;
00075   boost::function<void(
00076       const vssp::Header &,
00077       const std::string &,
00078       const boost::posix_time::ptime &)> cb_error_;
00079   boost::function<void(bool)> cb_connect_;
00080   boost::shared_array<const double> tbl_h_;
00081   std::vector<boost::shared_array<const TableSincos>> tbl_v_;
00082   bool tbl_h_loaded_;
00083   bool tbl_v_loaded_;
00084   std::vector<bool> tbl_vn_loaded_;
00085   double timeout_;
00086 
00087   boost::asio::streambuf buf_;
00088 
00089 public:
00090   VsspDriver()
00091     : socket_(io_service_)
00092     , timer_(io_service_)
00093     , closed_(false)
00094     , aux_factor_(AUX_FACTOR_DEFAULT)
00095     , cb_point_(0)
00096     , cb_aux_(0)
00097     , cb_ping_(0)
00098     , tbl_h_loaded_(false)
00099     , tbl_v_loaded_(false)
00100     , timeout_(1.0)
00101   {
00102   }
00103   void setTimeout(const double to)
00104   {
00105     timeout_ = to;
00106   }
00107   void connect(const char *ip, const unsigned int port, decltype(cb_connect_) cb)
00108   {
00109     cb_connect_ = cb;
00110     boost::asio::ip::tcp::endpoint endpoint(boost::asio::ip::address::from_string(ip), port);
00111     timer_.expires_from_now(boost::posix_time::seconds(timeout_));
00112     timer_.async_wait(boost::bind(&VsspDriver::onTimeoutConnect, this, boost::asio::placeholders::error));
00113     socket_.async_connect(endpoint, boost::bind(&vssp::VsspDriver::onConnect, this, boost::asio::placeholders::error));
00114   }
00115   void registerErrorCallback(decltype(cb_error_) cb)
00116   {
00117     cb_error_ = cb;
00118   }
00119   void registerCallback(decltype(cb_point_) cb)
00120   {
00121     cb_point_ = cb;
00122   }
00123   void registerAuxCallback(decltype(cb_aux_) cb)
00124   {
00125     cb_aux_ = cb;
00126   }
00127   void registerPingCallback(decltype(cb_ping_) cb)
00128   {
00129     cb_ping_ = cb;
00130   }
00131   void setAutoReset(const bool enable)
00132   {
00133     if (enable)
00134       send(std::string("SET:_ars=1\n"));
00135     else
00136       send(std::string("SET:_ars=0\n"));
00137   }
00138   [[deprecated("use setHorizontalInterlace() instead of setInterlace()")]]
00139   void setInterlace(const int itl)
00140   {
00141     setHorizontalInterlace(itl);
00142   }
00143   void setHorizontalInterlace(const int itl)
00144   {
00145     send((boost::format("SET:_itl=0,%02d\n") % itl).str());
00146   }
00147   void setVerticalInterlace(const int itl)
00148   {
00149     send((boost::format("SET:_itv=0,%02d\n") % itl).str());
00150   }
00151   void requestVerticalTable(const int itl = 1)
00152   {
00153     tbl_v_.resize(itl);
00154     tbl_vn_loaded_.resize(itl);
00155     if (itl == 1)
00156     {
00157       send(std::string("GET:tblv\n"));
00158     }
00159     else
00160     {
00161       for (int i = 0; i < itl; ++i)
00162       {
00163         send((boost::format("GET:tv%02d\n") % i).str());
00164         tbl_vn_loaded_[i] = false;
00165       }
00166     }
00167   }
00168   void requestHorizontalTable()
00169   {
00170     send(std::string("GET:tblh\n"));
00171   }
00172   void requestPing()
00173   {
00174     send(std::string("PNG\n"));
00175   }
00176   void requestAuxData(const bool start = 1)
00177   {
00178     send((boost::format("DAT:ax=%d\n") % static_cast<int>(start)).str());
00179   }
00180   void requestData(const bool intensity = 1, const bool start = 1)
00181   {
00182     if (intensity)
00183     {
00184       send((boost::format("DAT:ri=%d\n") % static_cast<int>(start)).str());
00185     }
00186     else
00187     {
00188       send((boost::format("DAT:ro=%d\n") % static_cast<int>(start)).str());
00189     }
00190   }
00191   void receivePackets()
00192   {
00193     timer_.cancel();
00194     timer_.expires_from_now(boost::posix_time::seconds(timeout_));
00195     timer_.async_wait(boost::bind(&VsspDriver::onTimeout, this, boost::asio::placeholders::error));
00196     // Read at least 4 bytes.
00197     // In most case, callback function will be called for each VSSP line.
00198     boost::asio::async_read(socket_, buf_, boost::asio::transfer_at_least(4),
00199                             boost::bind(&VsspDriver::onRead, this, boost::asio::placeholders::error));
00200   }
00201   bool poll()
00202   {
00203     boost::system::error_code ec;
00204     io_service_.poll(ec);
00205     if (!ec)
00206       return true;
00207     return false;
00208   }
00209   void spin()
00210   {
00211     io_service_.run();
00212   }
00213   void stop()
00214   {
00215     io_service_.stop();
00216   }
00217   boost::asio::io_service &getIoService()
00218   {
00219     return io_service_;
00220   }
00221 
00222 private:
00223   void send(const std::string cmd)
00224   {
00225     boost::shared_ptr<std::string> data(new std::string(cmd));
00226     boost::asio::async_write(socket_, boost::asio::buffer(*data),
00227                              boost::bind(&VsspDriver::onSend, this, boost::asio::placeholders::error, data));
00228   }
00229   void onTimeoutConnect(const boost::system::error_code &error)
00230   {
00231     if (!error)
00232     {
00233       closed_ = true;
00234       io_service_.stop();
00235     }
00236   }
00237   void onTimeout(const boost::system::error_code &error)
00238   {
00239     if (!error)
00240     {
00241       closed_ = true;
00242       io_service_.stop();
00243     }
00244   }
00245   void onConnect(const boost::system::error_code &error)
00246   {
00247     timer_.cancel();
00248     if (error)
00249     {
00250       closed_ = true;
00251       cb_connect_(false);
00252       return;
00253     }
00254     cb_connect_(true);
00255   }
00256   void onSend(const boost::system::error_code &error, boost::shared_ptr<std::string> data)
00257   {
00258     if (error)
00259     {
00260       closed_ = true;
00261       return;
00262     }
00263   }
00264   template <class DATA_TYPE>
00265   bool rangeToXYZ(
00266       const vssp::RangeHeader &range_header,
00267       const vssp::RangeHeaderV2R1 &range_header_v2r1,
00268       const vssp::RangeIndex &range_index,
00269       const boost::shared_array<const uint16_t> &index,
00270       const boost::shared_array<vssp::XYZI> &points)
00271   {
00272     if (tbl_vn_loaded_.size() != range_header_v2r1.vertical_interlace)
00273       return false;
00274 
00275     int i = 0;
00276     const double h_head = range_header.line_head_h_angle_ratio * 2.0 * M_PI / 65535.0;
00277     const double h_tail = range_header.line_tail_h_angle_ratio * 2.0 * M_PI / 65535.0;
00278     const DATA_TYPE *data = boost::asio::buffer_cast<const DATA_TYPE *>(buf_.data());
00279     const int tv = range_header_v2r1.vertical_field;
00280     for (int s = 0; s < range_index.nspots; s++)
00281     {
00282       const double spot = s + range_header.spot;
00283       const double h_rad = h_head + (h_tail - h_head) * tbl_h_[spot];
00284       const double h_cos = cos(h_rad);
00285       const double h_sin = sin(h_rad);
00286       const vssp::XYZI dir(tbl_v_[tv][spot].s, tbl_v_[tv][spot].c, h_sin, h_cos);
00287       for (int e = index[s]; e < index[s + 1]; e++)
00288         points[i++] = dir * data[e];
00289     }
00290     return true;
00291   }
00292   void onRead(const boost::system::error_code &error)
00293   {
00294     const auto time_read = boost::posix_time::microsec_clock::universal_time();
00295     const auto length_total = buf_.size();
00296     if (error == boost::asio::error::eof)
00297     {
00298       // Connection closed_
00299       closed_ = true;
00300       io_service_.stop();
00301       return;
00302     }
00303     else if (error)
00304     {
00305       // Connection error
00306       closed_ = true;
00307       io_service_.stop();
00308     }
00309     while (true)
00310     {
00311       if (buf_.size() < sizeof(vssp::Header))
00312       {
00313         break;
00314       }
00315       // Read packet Header
00316       const vssp::Header header = *boost::asio::buffer_cast<const vssp::Header *>(buf_.data());
00317       if (header.mark != vssp::VSSP_MARK)
00318       {
00319         // Invalid packet
00320         // find VSSP mark
00321         const uint8_t *data = boost::asio::buffer_cast<const uint8_t *>(buf_.data());
00322         for (size_t i = 1; i < buf_.size() - sizeof(uint32_t); i++)
00323         {
00324           const uint32_t *mark = reinterpret_cast<const uint32_t *>(data + i);
00325           if (*mark == vssp::VSSP_MARK)
00326           {
00327             buf_.consume(i);
00328             break;
00329           }
00330         }
00331         break;
00332       }
00333       if (buf_.size() < header.length)
00334         break;
00335 
00336       size_t length = header.length - header.header_length;
00337       buf_.consume(header.header_length);
00338 
00339       do
00340       {
00341         switch (header.type)
00342         {
00343           case TYPE_ERR:
00344           case TYPE_ER:
00345             // Error message
00346             {
00347               const std::string data(boost::asio::buffer_cast<const char *>(buf_.data()));
00348               std::string message(data, 0, header.length - header.header_length - 1);
00349               if (cb_error_)
00350                 cb_error_(header, message, time_read);
00351             }
00352             break;
00353           default:
00354             break;
00355         }
00356         if (header.status != vssp::STATUS_OK)
00357           break;
00358 
00359         switch (header.type)
00360         {
00361           case TYPE_GET:
00362             // Response to get command
00363             {
00364               const std::string data(boost::asio::buffer_cast<const char *>(buf_.data()));
00365               std::vector<std::string> lines;
00366               boost::algorithm::split(lines, data, boost::algorithm::is_any_of("\n\r"));
00367               if (lines.size() == 0)
00368                 break;
00369 
00370               if (lines[0].compare(0, 7, "GET:tbl") == 0 ||
00371                   lines[0].compare(0, 6, "GET:tv") == 0)
00372               {
00373                 if (lines.size() < 2)
00374                   break;
00375                 std::vector<std::string> cells;
00376                 boost::algorithm::split(cells, lines[1], boost::algorithm::is_any_of(","));
00377 
00378                 if (lines[0].compare("GET:tblv") == 0 ||
00379                     lines[0].compare(0, 6, "GET:tv") == 0)
00380                 {
00381                   int tv = 0;
00382                   if (lines[0].compare(0, 6, "GET:tv") == 0)
00383                     tv = std::stoi(lines[0].substr(6));
00384 
00385                   if (tv >= static_cast<int>(tbl_v_.size()))
00386                     break;
00387 
00388                   boost::shared_array<TableSincos> tbl_v(new TableSincos[cells.size()]);
00389                   int i = 0;
00390                   for (auto &cell : cells)
00391                   {
00392                     const double rad(std::strtol(cell.c_str(), nullptr, 16) * 2.0 * M_PI / 65535.0);
00393                     sincos(rad, &tbl_v[i].s, &tbl_v[i].c);
00394                     i++;
00395                   }
00396                   tbl_v_[tv] = tbl_v;
00397                   tbl_vn_loaded_[tv] = true;
00398 
00399                   bool load_finished = true;
00400                   for (auto loaded : tbl_vn_loaded_)
00401                   {
00402                     if (!loaded)
00403                       load_finished = false;
00404                   }
00405                   tbl_v_loaded_ = load_finished;
00406                 }
00407                 else if (lines[0].compare("GET:tblh") == 0)
00408                 {
00409                   boost::shared_array<double> tbl_h(new double[cells.size()]);
00410                   int i = 0;
00411                   for (auto &cell : cells)
00412                   {
00413                     tbl_h[i] = std::strtol(cell.c_str(), nullptr, 16) / 65535.0;
00414                     i++;
00415                   }
00416                   tbl_h_ = tbl_h;
00417                   tbl_h_loaded_ = true;
00418                 }
00419               }
00420             }
00421             break;
00422           case TYPE_SET:
00423             // Response to set command
00424             break;
00425           case TYPE_DAT:
00426             // Response to data request command
00427             break;
00428           case TYPE_VER:
00429             // Response to version request command
00430             break;
00431           case TYPE_PNG:
00432             // Response to ping command
00433             if (cb_ping_)
00434               cb_ping_(header, time_read);
00435             break;
00436           case TYPE_RI:
00437           case TYPE_RO:
00438             // Range data
00439             if (!tbl_h_loaded_ || !tbl_v_loaded_ || !cb_point_)
00440             {
00441               // Something wrong
00442               break;
00443             }
00444             {
00445               // Decode range data Header
00446               const vssp::RangeHeader range_header = *boost::asio::buffer_cast<const vssp::RangeHeader *>(buf_.data());
00447               vssp::RangeHeaderV2R1 range_header_v2r1 = RANGE_HEADER_V2R1_DEFAULT;
00448               if (range_header.header_length >= 24)
00449               {
00450                 range_header_v2r1 = *boost::asio::buffer_cast<const vssp::RangeHeaderV2R1 *>(
00451                     buf_.data() + sizeof(vssp::RangeHeader));
00452               }
00453               buf_.consume(range_header.header_length);
00454               length -= range_header.header_length;
00455 
00456               // Decode range index Header
00457               const vssp::RangeIndex range_index = *boost::asio::buffer_cast<const vssp::RangeIndex *>(buf_.data());
00458               size_t index_length = range_index.index_length;
00459               buf_.consume(sizeof(vssp::RangeIndex));
00460               index_length -= sizeof(vssp::RangeIndex);
00461               length -= sizeof(vssp::RangeIndex);
00462 
00463               // Decode range index
00464               boost::shared_array<uint16_t> index(new uint16_t[range_index.nspots + 1]);
00465               std::memcpy(index.get(), boost::asio::buffer_cast<const vssp::RangeIndex *>(buf_.data()),
00466                           sizeof(uint16_t) * (range_index.nspots + 1));
00467               buf_.consume(index_length);
00468               length -= index_length;
00469 
00470               // Decode range data
00471               boost::shared_array<vssp::XYZI> points(new vssp::XYZI[index[range_index.nspots]]);
00472               bool success;
00473               switch (header.type)
00474               {
00475               case TYPE_RI:
00476                 // Range and Intensity
00477                 success = rangeToXYZ<vssp::DataRangeIntensity>(
00478                     range_header, range_header_v2r1, range_index, index, points);
00479                 break;
00480               case TYPE_RO:
00481                 // Range
00482                 success = rangeToXYZ<vssp::DataRangeOnly>(
00483                     range_header, range_header_v2r1, range_index, index, points);
00484                 break;
00485               default:
00486                 success = false;
00487                 break;
00488               }
00489               if (!success)
00490                 break;
00491               cb_point_(header, range_header, range_index, index, points, time_read);
00492             }
00493             break;
00494           case TYPE_AX:
00495             // Aux data
00496             {
00497               // Decode range data Header
00498               const vssp::AuxHeader aux_header = *boost::asio::buffer_cast<const vssp::AuxHeader *>(buf_.data());
00499               buf_.consume(aux_header.header_length);
00500               length -= aux_header.header_length;
00501 
00502               // Decode Aux data
00503               boost::shared_array<vssp::Aux> auxs(new vssp::Aux[aux_header.data_count]);
00504               for (int i = 0; i < aux_header.data_count; i++)
00505               {
00506                 const vssp::AuxData *data = boost::asio::buffer_cast<const vssp::AuxData *>(buf_.data());
00507                 int offset = 0;
00508                 for (AuxId b = vssp::AX_MASK_LAST; b >= vssp::AX_MASK_FIRST; b = static_cast<AuxId>(b - 1))
00509                 {
00510                   if (aux_header.data_bitfield & (1 << static_cast<int>(b)))
00511                     auxs[i][b] = aux_factor_[b] * data[offset++].val;
00512                 }
00513                 buf_.consume(sizeof(int32_t) * offset);
00514                 length -= sizeof(int32_t) * offset;
00515               }
00516               if (cb_aux_)
00517                 cb_aux_(header, aux_header, auxs, time_read);
00518             }
00519             break;
00520           default:
00521             break;
00522         }
00523       }
00524       while (false);
00525       buf_.consume(length);
00526     }
00527     receivePackets();
00528     return;
00529   }
00530 };
00531 
00532 }  // namespace vssp
00533 
00534 #endif  // VSSP_H


hokuyo3d
Author(s): Atsushi Watanabe
autogenerated on Thu Jun 6 2019 20:08:29