00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
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
00157
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
00246 closed_ = true;
00247 return;
00248 }
00249 else if (error)
00250 {
00251
00252 closed_ = true;
00253 }
00254 while (true)
00255 {
00256 if (buf_.size() < sizeof(vssp::Header))
00257 {
00258 break;
00259 }
00260
00261 const vssp::Header Header = *boost::asio::buffer_cast<const vssp::Header *>(buf_.data());
00262 if (Header.mark != vssp::VSSP_MARK)
00263 {
00264
00265
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
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
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
00354 break;
00355 case TYPE_DAT:
00356
00357 break;
00358 case TYPE_VER:
00359
00360 break;
00361 case TYPE_PNG:
00362
00363 if (!cb_ping_.empty())
00364 cb_ping_(Header, delay);
00365 break;
00366 case TYPE_RI:
00367 case TYPE_RO:
00368
00369 if (!tbl_h_loaded_ || !tbl_v_loaded_ || cb_point_.empty())
00370 {
00371
00372 break;
00373 }
00374 {
00375
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
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
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
00395 boost::shared_array<vssp::XYZI> points(new vssp::XYZI[index[RangeIndex.nspots]]);
00396 switch (Header.type)
00397 {
00398 case TYPE_RI:
00399
00400 rangeToXYZ<vssp::DataRangeIntensity>(RangeHeader, RangeIndex, index, points);
00401 break;
00402 case TYPE_RO:
00403
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
00412 {
00413
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
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 }
00450
00451 #endif // VSSP_H