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(
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
00197
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
00299 closed_ = true;
00300 io_service_.stop();
00301 return;
00302 }
00303 else if (error)
00304 {
00305
00306 closed_ = true;
00307 io_service_.stop();
00308 }
00309 while (true)
00310 {
00311 if (buf_.size() < sizeof(vssp::Header))
00312 {
00313 break;
00314 }
00315
00316 const vssp::Header header = *boost::asio::buffer_cast<const vssp::Header *>(buf_.data());
00317 if (header.mark != vssp::VSSP_MARK)
00318 {
00319
00320
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
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
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
00424 break;
00425 case TYPE_DAT:
00426
00427 break;
00428 case TYPE_VER:
00429
00430 break;
00431 case TYPE_PNG:
00432
00433 if (cb_ping_)
00434 cb_ping_(header, time_read);
00435 break;
00436 case TYPE_RI:
00437 case TYPE_RO:
00438
00439 if (!tbl_h_loaded_ || !tbl_v_loaded_ || !cb_point_)
00440 {
00441
00442 break;
00443 }
00444 {
00445
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
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
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
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
00477 success = rangeToXYZ<vssp::DataRangeIntensity>(
00478 range_header, range_header_v2r1, range_index, index, points);
00479 break;
00480 case TYPE_RO:
00481
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
00496 {
00497
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
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 }
00533
00534 #endif // VSSP_H