|
TOBI Interface A
0.1
|
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