TOBI Interface A  0.1
/home/breidi/Dropbox/libtia/src/tia/client/tia_client_impl.cpp
Go to the documentation of this file.
00001 /*
00002     This file is part of the TOBI Interface A (TiA) library.
00003 
00004     Commercial Usage
00005     Licensees holding valid Graz University of Technology Commercial
00006     licenses may use this file in accordance with the Graz University
00007     of Technology Commercial License Agreement provided with the
00008     Software or, alternatively, in accordance with the terms contained in
00009     a written agreement between you and Graz University of Technology.
00010 
00011     --------------------------------------------------
00012 
00013     GNU Lesser General Public License Usage
00014     Alternatively, this file may be used under the terms of the GNU Lesser
00015     General Public License version 3.0 as published by the Free Software
00016     Foundation and appearing in the file lgpl.txt included in the
00017     packaging of this file.  Please review the following information to
00018     ensure the GNU General Public License version 3.0 requirements will be
00019     met: http://www.gnu.org/copyleft/lgpl.html.
00020 
00021     In case of GNU Lesser General Public License Usage ,the TiA library
00022     is distributed in the hope that it will be useful,
00023     but WITHOUT ANY WARRANTY; without even the implied warranty of
00024     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00025     GNU General Public License for more details.
00026 
00027     You should have received a copy of the GNU Lesser General Public License
00028     along with the TiA library. If not, see <http://www.gnu.org/licenses/>.
00029 
00030     Copyright 2010 Graz University of Technology
00031     Contact: TiA@tobi-project.org
00032 */
00033 
00034 
00039 // STL
00040 #include <iostream>
00041 
00042 // Boost
00043 #include <boost/bind.hpp>
00044 #include <boost/exception/all.hpp>
00045 #include <boost/numeric/conversion/cast.hpp>
00046 
00047 // local
00048 #include "tia-private/client/tia_client_impl.h"
00049 #include "tia-private/config/control_message_decoder.h"
00050 #include "tia-private/config/control_message_encoder.h"
00051 #include "tia-private/config/control_messages.h"
00052 #include "tia-private/datapacket/data_packet_impl.h"
00053 
00054 #ifdef TIMING_TEST
00055   #include "LptTools/LptTools.h"
00056   #define LPT1  0
00057   #define LPT2  1
00058 #endif
00059 
00060 namespace tia
00061 {
00062 using boost::numeric_cast;
00063 using boost::numeric::bad_numeric_cast;
00064 using boost::numeric::positive_overflow;
00065 using boost::numeric::negative_overflow;
00066 
00067 using std::cout;
00068 using std::cerr;
00069 using std::endl;
00070 using std::string;
00071 using std::vector;
00072 using std::stringstream;
00073 using boost::uint16_t;
00074 using boost::uint32_t;
00075 using boost::int32_t;
00076 
00077 //-----------------------------------------------------------------------------
00078 
00079 TiAClientImpl::TiAClientImpl() :
00080   data_socket_tcp_(io_service_),
00081   data_socket_udp_(io_service_),
00082   msg_encoder_(0),
00083   msg_decoder_(0),
00084   ctl_conn_state_(ControlConnState_NotConnected),
00085   data_input_state_(DataInputState_NotConnected),
00086   use_udp_bc_(false),
00087   last_packet_nr_(0),
00088   packet_offset_(0),
00089   buffer_offset_(0),
00090   buffered_data_(0),
00091   buffer_size_(BUFFER_SIZE),
00092   recv_buf_(buffer_size_),
00093   data_buf_(0)
00094 {
00095   msg_encoder_ = new ControlMsgEncoderXML;
00096   msg_decoder_ = new ControlMsgDecoderXML;
00097 
00098   packet_.reset( new DataPacketImpl);
00099 
00100   msg_decoder_->setInputStream(&ctl_conn_stream_);
00101 
00102     #ifdef WIN32
00103       SetPriorityClass(GetCurrentProcess(), ABOVE_NORMAL_PRIORITY_CLASS);
00104     #endif
00105 
00106   #ifdef TIMING_TEST
00107     lpt_flag_ = 0;
00108 
00109     if(!LptDriverInstall())
00110     {
00111       cerr << "Installing LptTools lpt driver failed (do you have access rights for the lpt-port?).";
00112       throw std::runtime_error("Error installing LptTools lpt driver!");
00113     }
00114 
00115 //     LptDetectPorts(tmp,addr,3);
00116 
00117     if(!LptInit())
00118     {
00119       cerr << "Initializing lpt driver failed (do you have access rights for the lpt-port?).";
00120       throw std::runtime_error("Error initializing lpt driver!");
00121     }
00122   #endif
00123 }
00124 
00125 //-----------------------------------------------------------------------------
00126 
00127 TiAClientImpl::~TiAClientImpl()
00128 {
00129   if(msg_encoder_)
00130     delete msg_encoder_;
00131 
00132   if(msg_decoder_)
00133     delete msg_decoder_;
00134 
00135   #ifdef TIMING_TEST
00136     LptExit();
00137   #endif
00138 }
00139 
00140 //-----------------------------------------------------------------------------
00141 
00142 SSConfig TiAClientImpl::config() const
00143 {
00144   return config_;
00145 }
00146 
00147 //-----------------------------------------------------------------------------
00148 
00149 void TiAClientImpl::connect(const std::string& address,  short unsigned port)
00150 {
00151    if (connected())
00152    {
00153      stringstream ex_str;
00154      ex_str << "TiAClient: Already connected!"
00155             << address << ":" << port;
00156      throw std::ios_base::failure(ex_str.str());
00157    }
00158 
00159    stringstream conv;
00160    conv << port;
00161 
00162    ctl_conn_stream_.connect(address, conv.str());
00163    if (!ctl_conn_stream_)
00164    {
00165      stringstream ex_str;
00166      ex_str << "TiAClient: An error occurred while connecting to server "
00167             << address << ":" << port;
00168      throw std::ios_base::failure(ex_str.str());
00169    }
00170 
00171    ctl_conn_state_ = ControlConnState_Connected;
00172 }
00173 
00174 //-----------------------------------------------------------------------------
00175 
00176 bool TiAClientImpl::connected() const
00177 {
00178   return (ctl_conn_state_ & ControlConnState_Connected) != 0;
00179 }
00180 
00181 //-----------------------------------------------------------------------------
00182 
00183 void TiAClientImpl::disconnect()
00184 {
00185   if (!connected()) return;
00186 
00187   if (receiving())
00188   {
00189     stopReceiving();
00190   }
00191 
00192   ctl_conn_stream_.close();
00193 
00194   ctl_conn_state_ = ControlConnState_NotConnected;
00195 }
00196 
00197 //-----------------------------------------------------------------------------
00198 
00199 void TiAClientImpl::requestConfig()
00200 {
00201   if (ctl_conn_state_ == ControlConnState_NotConnected)
00202   {
00203     stringstream ex_str;
00204     ex_str << "TiAClient: Not connected!";
00205     throw std::ios_base::failure(ex_str.str());
00206   }
00207 
00208   GetConfigMsg msg;
00209 
00210   msg_encoder_->encodeMsg(msg, ctl_conn_stream_);
00211 
00212   boost::shared_ptr<ControlMsg> reply(msg_decoder_->decodeMsg());
00213 
00214   if (reply == 0)
00215   {
00216     stringstream ex_str;
00217     ex_str << "TiAClient: Cannot decode message";
00218     throw std::ios_base::failure(ex_str.str());
00219   }
00220 
00221   // Check reply type
00222   switch (reply->msgType())
00223   {
00224     case ControlMsg::Config: break;
00225 
00226     case ControlMsg::ErrorReply:
00227     {
00228       stringstream ex_str;
00229       ex_str << "TiAClient: Getting the config failed due to a server error.";
00230       throw std::ios_base::failure(ex_str.str());
00231     }
00232 
00233     default:
00234     {
00235       stringstream ex_str;
00236       ex_str << "TiAClient: Got unexpected reply of type '" << reply->msgType() << "'";
00237       throw std::ios_base::failure(ex_str.str());
00238     }
00239   }
00240 
00241   boost::shared_ptr<ConfigMsg> config_msg =
00242       boost::static_pointer_cast<ConfigMsg>(reply);
00243 
00244   config_.subject_info = config_msg->subject_info;
00245   config_.signal_info = config_msg->signal_info;
00246 }
00247 
00248 //-----------------------------------------------------------------------------
00249 
00250 void TiAClientImpl::establishDataConnection(bool use_udp_bc)
00251 {
00252   GetDataConnectionMsg msg;
00253   if (use_udp_bc)
00254     msg.setConnectionType(GetDataConnectionMsg::Udp);
00255   else
00256   {
00257     msg.setConnectionType(GetDataConnectionMsg::Tcp);
00258   }
00259 
00260   use_udp_bc_ = use_udp_bc;
00261 
00262   // TODO: check for connection loss
00263   msg_encoder_->encodeMsg(msg, ctl_conn_stream_);
00264 
00265   cout << "TiAClient: Waiting on reply" << endl;
00266 
00267   boost::shared_ptr<ControlMsg> reply(msg_decoder_->decodeMsg());
00268 
00269   if (reply == 0)
00270   {
00271     stringstream ex_str;
00272     ex_str << "TiAClient: Cannot decode message";
00273     throw std::ios_base::failure(ex_str.str());
00274   }
00275 
00276   // Check reply type
00277   switch (reply->msgType())
00278   {
00279     case ControlMsg::DataConnection: break;
00280 
00281     case ControlMsg::ErrorReply:
00282     {
00283       stringstream ex_str;
00284       ex_str << "TiAClient: Establishing data connection failed due to a server error.";
00285       throw std::ios_base::failure(ex_str.str());
00286     }
00287 
00288     default:
00289     {
00290       stringstream ex_str;
00291       ex_str << "TiAClient: Got unexpected reply of type '" << reply->msgType() << "'";
00292       throw std::ios_base::failure(ex_str.str());
00293     }
00294   }
00295   boost::shared_ptr<DataConnectionMsg> data_conn_msg =
00296         boost::static_pointer_cast<DataConnectionMsg>(reply);
00297 
00298   boost::system::error_code ec;
00299   boost::asio::socket_base::receive_buffer_size buffer_size(buffer_size_);
00300 
00301   if (use_udp_bc_)
00302   {
00303     boost::asio::ip::udp::endpoint endpoint(boost::asio::ip::udp::v4(), data_conn_msg->port());
00304     data_socket_udp_.open(boost::asio::ip::udp::v4(), ec);
00305     if (!ec)
00306     {
00307       data_socket_udp_.bind(endpoint, ec);
00308       boost::asio::socket_base::broadcast bcast(true);
00309       data_socket_udp_.set_option(bcast);
00310       data_socket_udp_.set_option(buffer_size);
00311     }
00312   }
00313   else
00314   {
00315      boost::system::error_code ec;
00316      tcp_target_ = boost::asio::ip::tcp::endpoint(
00317         ctl_conn_stream_.rdbuf()->remote_endpoint().address(),
00318         data_conn_msg->port());
00319      data_socket_tcp_.connect(tcp_target_, ec);
00320      boost::asio::socket_base::receive_buffer_size buffer_size(buffer_size_);
00321      data_socket_tcp_.set_option(buffer_size);
00322   }
00323 
00324   if (ec)
00325   {
00326     data_input_state_ = DataInputState_NotConnected;
00327     stringstream ex_str;
00328     ex_str << "TiAClient: Could not connect to signal server:";
00329     ex_str << "-->" << ec.message();
00330     throw std::ios_base::failure(ex_str.str());
00331   }
00332 
00333   data_input_state_ |= DataInputState_Connected;
00334 }
00335 
00336 //-----------------------------------------------------------------------------
00337 
00338 void TiAClientImpl::closeDataConnection()
00339 {
00340     boost::system::error_code ec;
00341 
00342     if(use_udp_bc_)
00343     {
00344       data_socket_udp_.shutdown(boost::asio::ip::udp::socket::shutdown_both, ec);
00345       data_socket_udp_.close(ec);
00346     }
00347     else
00348     {
00349       data_socket_tcp_.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec);
00350       data_socket_tcp_.close(ec);
00351     }
00352 
00353     if (ec)
00354     {
00355       stringstream ex_str;
00356       ex_str << "TiAClient: An error occurred while closing data connection:" << endl;
00357       ex_str << "-->" << ec.message();
00358       throw std::ios_base::failure(ex_str.str());
00359     }
00360 
00361     data_input_state_ = DataInputState_NotConnected;
00362 }
00363 
00364 //-----------------------------------------------------------------------------
00365 
00366 void TiAClientImpl::startReceiving(bool use_udp_bc)
00367 {
00368   if (ctl_conn_state_ == ControlConnState_NotConnected)
00369   {
00370     stringstream ex_str;
00371     ex_str << "TiAClient: Not connected!";
00372     throw std::ios_base::failure(ex_str.str());
00373   }
00374 
00375   if (receiving())
00376   {
00377     stringstream ex_str;
00378     ex_str << "TiAClient: Client already in receiving state!";
00379     throw std::ios_base::failure(ex_str.str());
00380   }
00381 
00382   if (data_input_state_ == DataInputState_NotConnected)
00383   {
00384     try
00385     {
00386       establishDataConnection(use_udp_bc);
00387     }
00388     catch(std::exception& e)
00389     {
00390       throw e;
00391     }
00392   }
00393 
00394   StartTransmissionMsg msg;
00395 
00396   // TODO: check for connection loss
00397   msg_encoder_->encodeMsg(msg, ctl_conn_stream_);
00398 
00399   cout << "TiAClient: Waiting on reply" << endl;
00400 
00401   boost::shared_ptr<ControlMsg> reply(msg_decoder_->decodeMsg());
00402 
00403   if (reply == 0)
00404   {
00405     stringstream ex_str;
00406     ex_str << "TiAClient: Cannot decode message";
00407     throw std::ios_base::failure(ex_str.str());
00408   }
00409 
00410   // Check reply type
00411   switch (reply->msgType())
00412   {
00413     case ControlMsg::OkReply: break;
00414 
00415     case ControlMsg::ErrorReply:
00416     {
00417       stringstream ex_str;
00418       ex_str << "TiAClient: Stop receiving failed due to a server error.";
00419       throw std::ios_base::failure(ex_str.str());
00420     }
00421 
00422     default:
00423     {
00424       stringstream ex_str;
00425       ex_str << "TiAClient: Got unexpected reply of type '" << reply->msgType() << "'";
00426       throw std::ios_base::failure(ex_str.str());
00427     }
00428   }
00429 
00430   data_input_state_ |= DataInputState_Receiving;
00431 }
00432 //-----------------------------------------------------------------------------
00433 
00434 bool TiAClientImpl::receiving() const
00435 {
00436   return (data_input_state_ & DataInputState_Receiving) != 0;
00437 }
00438 
00439 //-----------------------------------------------------------------------------
00440 
00441 void TiAClientImpl::stopReceiving()
00442 {
00443   if (ctl_conn_state_ == ControlConnState_NotConnected)
00444   {
00445     stringstream ex_str;
00446     ex_str << "TiAClient: Not connected!";
00447     throw std::ios_base::failure(ex_str.str());
00448   }
00449 
00450   if (!receiving()) return;
00451 
00452   StopTransmissionMsg msg;
00453 
00454   // TODO: check for connection loss
00455   msg_encoder_->encodeMsg(msg, ctl_conn_stream_);
00456 
00457   cout << "TiAClient: Waiting on reply" << endl;
00458 
00459   boost::shared_ptr<ControlMsg> reply(msg_decoder_->decodeMsg());
00460 
00461   // Check reply type
00462   switch (reply->msgType())
00463   {
00464     case ControlMsg::OkReply: break;
00465 
00466     case ControlMsg::ErrorReply:
00467     {
00468       stringstream ex_str;
00469       ex_str << "TiAClient: Stop receiving failed due to a server error.";
00470       throw std::ios_base::failure(ex_str.str());
00471     }
00472 
00473     default:
00474     {
00475       stringstream ex_str;
00476       ex_str << "TiAClient: Got unexpected reply of type '" << reply->msgType() << "'";
00477       throw std::ios_base::failure(ex_str.str());
00478     }
00479   }
00480 
00481   closeDataConnection();
00482 }
00483 
00484 //-----------------------------------------------------------------------------
00485 
00486 DataPacket* TiAClientImpl::getEmptyDataPacket()
00487 {
00488   return(packet_.get());
00489 }
00490 
00491 //-----------------------------------------------------------------------------
00492 
00493 void TiAClientImpl::getDataPacket(DataPacket& packet)
00494 {
00495   if ((ctl_conn_state_ == ControlConnState_NotConnected))
00496   {
00497     stringstream ex_str;
00498     ex_str << "TiAClient: Not connected!";
00499     throw std::ios_base::failure(ex_str.str());
00500   }
00501 
00502   if (!receiving())
00503   {
00504     stringstream ex_str;
00505     ex_str << "TiAClient: Client not in receiving state!";
00506     throw std::ios_base::failure(ex_str.str());
00507   }
00508 
00509   boost::system::error_code error;
00510   size_t bytes_transferred = 0;
00511   uint32_t packet_size = 0;
00512   DataPacketImpl& p = (dynamic_cast<DataPacketImpl&>(packet));
00513   packet.reset();
00514 
00515   if (packet_offset_ == 0)
00516   {
00517     if(use_udp_bc_)
00518     {
00519         bytes_transferred = data_socket_udp_.receive(boost::asio::buffer(recv_buf_), 0, error);
00520 
00521         if (error)
00522         {
00523           // TODO: try to sent stop cmd to server?
00524           closeDataConnection();
00525           std::string ex_str("TiAClient: Data connection broken\n -->");
00526           ex_str += error.message();
00527 
00528           throw std::ios_base::failure(ex_str);
00529         }
00530     }
00531     else
00532     {
00533         bytes_transferred = data_socket_tcp_.read_some(boost::asio::buffer(recv_buf_), error);
00534 
00535         if (error && error != boost::asio::error::message_size)
00536         {
00537           // TODO: try to sent stop cmd to server?
00538           closeDataConnection();
00539           std::string ex_str("TiAClient: Data connection broken\n -->");
00540           ex_str += error.message();
00541 
00542           throw std::ios_base::failure(ex_str);
00543         }
00544 
00545     }
00546 
00547     data_buf_.resize(buffer_offset_);
00548     data_buf_.insert(data_buf_.end(), recv_buf_.begin(), recv_buf_.begin() + bytes_transferred );
00549 
00550     buffer_offset_ = 0;
00551     packet_size = p.getRequiredRawMemorySize(&(data_buf_[packet_offset_]),
00552                                            numeric_cast<int32_t>(bytes_transferred));
00553   }
00554 
00555   while(packet_offset_ == 0 && ( (bytes_transferred < packet_size) || (packet_size == 0) ) )
00556   {
00557     uint32_t bytes_to_receive = 0;
00558     if(packet_size == 0)
00559       bytes_to_receive = 32;
00560     else
00561       bytes_to_receive = packet_size - bytes_transferred;
00562 
00563     vector<char> v(bytes_to_receive);
00564 
00565     if(use_udp_bc_)
00566     {
00567       cerr << "TiAClient: ERROR -- Packet fragmentation not possible in UDP!"<< endl;
00568       throw (std::ios_base::failure("TiAClientImpl::getDataPacket() --  Can not decode packet"));
00569     }
00570     else
00571     {
00572       boost::asio::read(data_socket_tcp_, boost::asio::buffer(v),
00573       boost::asio::transfer_at_least(bytes_to_receive), error);
00574     }
00575 
00576     if (error && error != boost::asio::error::message_size)
00577     {
00578       // TODO: try to sent stop cmd to server?
00579       closeDataConnection();
00580       std::string ex_str("TiAClient: Data connection broken\n -->");
00581       ex_str += error.message();
00582 
00583       throw std::ios_base::failure(ex_str);
00584     }
00585 
00586     data_buf_.insert(data_buf_.end(), v.begin(), v.end());
00587     bytes_transferred += v.size();
00588     packet_size = p.getRequiredRawMemorySize(&(data_buf_[packet_offset_]),
00589                                            numeric_cast<int32_t>(bytes_transferred));
00590   }
00591 
00592   try
00593   {
00594     p.reset(reinterpret_cast<char*>(&(data_buf_[packet_offset_])));
00595   }
00596   catch(std::runtime_error& e)
00597   {
00598     string ex_str("TiAClient: ***** STL Exception -- Runtime error -- caught! *****\n  -->");
00599     ex_str += e.what();
00600     cerr << ex_str << endl;
00601 
00602     packet_offset_ = 0;
00603     data_buf_.clear();
00604     buffer_offset_ = 0;
00605 
00606     throw (std::ios_base::failure("TiAClientImpl::getDataPacket() --  Can not decode packet"));
00607   }
00608 
00609   packet_size = p.getRequiredRawMemorySize();
00610   packet_offset_ += packet_size;
00611 
00612   try
00613   {
00614     if(packet_offset_ == data_buf_.size())
00615     {
00616       packet_offset_ = 0;
00617       buffer_offset_ = 0;
00618       data_buf_.clear();
00619     }
00620     else
00621     {
00622       uint32_t next_packet_size =
00623       p.getRequiredRawMemorySize(&(data_buf_[packet_offset_]),
00624                                  numeric_cast<int32_t>(data_buf_.size() - packet_offset_));
00625 
00626       if( next_packet_size == 0 ||
00627           numeric_cast<int32_t>(data_buf_.size() - packet_offset_) < numeric_cast<int32_t>(next_packet_size))
00628       {
00629         buffer_offset_ = data_buf_.size() - packet_offset_;
00630         for(unsigned int n = 0; n < buffer_offset_ ; n++ )
00631           data_buf_[n] = data_buf_[n + packet_offset_];
00632 
00633         data_buf_.resize(buffer_offset_);
00634         packet_offset_ = 0;
00635       }
00636     }
00637 
00638     if(p.getSampleNr() > (last_packet_nr_ +1) )
00639     {
00640       cerr << "TiAClient: Warning @packet: " << numeric_cast<uint32_t>(p.getSampleNr());
00641       cerr << " -- lost " << numeric_cast<uint32_t>(p.getSampleNr() - (last_packet_nr_ +1))  << " sample(s)!" << endl;
00642     }
00643     if(p.getSampleNr() < (last_packet_nr_) )
00644     {
00645       cerr << "TiAClient: Warning @packet: " << numeric_cast<uint32_t>(p.getSampleNr());
00646       cerr << " -- previous sample nr: " << numeric_cast<uint32_t>(last_packet_nr_) << " -- got packet twice!" << endl;
00647 //       return;
00648     }
00649   }
00650   catch(positive_overflow& e)
00651   {
00652     string ex_str(" TiAClient: ***** SampleNumber overflow detected! *****\n  -->");
00653     ex_str += boost::diagnostic_information(e);
00654     cerr << ex_str;
00655 
00656     cerr << "TiAClient: *** New packet nr: " << (uint32_t)(p.getSampleNr());
00657     cerr << " -- previous packet nr: " << (uint32_t)(last_packet_nr_) << "!" << endl;
00658 
00659     throw(std::overflow_error(ex_str));
00660   }
00661   last_packet_nr_ =  p.getSampleNr();
00662 
00663   #ifdef TIMING_TEST
00664     int port_state = LptPortIn(LPT1,0);
00665     if(!lpt_flag_)
00666     {
00667       lpt_flag_ = 1;
00668       LptPortOut(LPT1, 0, port_state | 0x04);
00669     }
00670     else
00671     {
00672       lpt_flag_ = 0;
00673       LptPortOut(LPT1, 0, port_state & ~0x04);
00674     }
00675   #endif
00676 }
00677 
00678 //-----------------------------------------------------------------------------
00679 
00680 void TiAClientImpl::setBufferSize(size_t size)
00681 {
00682   recv_buf_.resize(size);
00683   buffer_size_ = size;
00684 }
00685 
00686 //-----------------------------------------------------------------------------
00687 
00688 } // Namespace tobiss
 All Data Structures Files Functions Variables Typedefs Enumerations