μHAL (v2.7.9)
Part of the IPbus software repository
ProtocolControlHub.cpp
Go to the documentation of this file.
1 /*
2 ---------------------------------------------------------------------------
3 
4  This file is part of uHAL.
5 
6  uHAL is a hardware access library and programming framework
7  originally developed for upgrades of the Level-1 trigger of the CMS
8  experiment at CERN.
9 
10  uHAL is free software: you can redistribute it and/or modify
11  it under the terms of the GNU General Public License as published by
12  the Free Software Foundation, either version 3 of the License, or
13  (at your option) any later version.
14 
15  uHAL is distributed in the hope that it will be useful,
16  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18  GNU General Public License for more details.
19 
20  You should have received a copy of the GNU General Public License
21  along with uHAL. If not, see <http://www.gnu.org/licenses/>.
22 
23 
24  Andrew Rose, Imperial College, London
25  email: awr01 <AT> imperial.ac.uk
26 
27  Marc Magrans de Abril, CERN
28  email: marc.magrans.de.abril <AT> cern.ch
29 
30 ---------------------------------------------------------------------------
31 */
32 
34 
35 
36 #include <string>
37 #include <vector>
38 
39 #include <boost/asio.hpp>
40 #include <boost/fusion/adapted/std_pair.hpp>
41 #include <boost/spirit/include/qi.hpp>
42 #include <boost/spirit/include/qi_eps.hpp>
43 
44 #include "uhal/Buffers.hpp"
45 #include "uhal/ProtocolIPbus.hpp"
46 
47 
48 namespace uhal
49 {
50  std::pair< uint32_t , uint16_t > ExtractTargetID ( const URI& aUri )
51  {
52  NameValuePairVectorType::const_iterator lIt = aUri.mArguments.begin();
53 
54  for ( ; lIt != aUri.mArguments.end() ; ++lIt )
55  {
56  if ( lIt->first == "target" )
57  {
58  break;
59  }
60  }
61 
62  if ( lIt == aUri.mArguments.end() )
63  {
64  exception::XMLfileMissingRequiredParameters lExc;
65  log ( lExc , "Expected URI arguments of the form " , Quote ( "target=192.168.200.200:50001" ) ,". It appears that this is missing in URI " , aUri );
66  throw lExc;
67  }
68 
69  std::pair< std::string , std::string > lIP;
70 
71  try
72  {
73  boost::spirit::qi::phrase_parse ( lIt->second.begin() ,
74  lIt->second.end(),
75  ( boost::spirit::qi::eps >
76  * ( boost::spirit::qi::char_ - boost::spirit::qi::lit ( ":" ) ) >
77  boost::spirit::qi::lit ( ":" ) >
78  *boost::spirit::qi::char_
79  ) ,
80  boost::spirit::ascii::space ,
81  lIP
82  );
83  }
84  catch ( const std::exception& aExc )
85  {
86  exception::ParsingTargetURLfailed lExc;
87  log ( lExc , "Expected a string of the form " , Quote ( "hostIP:port" ) , " or " , Quote ( "hostname:port" ) , " but received " , Quote ( lIt->second ) , "." );
88  throw lExc;
89  }
90 
91  std::string lAddr;
92  uint16_t lPort;
93 
94  try
95  {
96  boost::asio::io_service lService;
97  boost::asio::ip::udp::endpoint lEndpoint (
98  *boost::asio::ip::udp::resolver::iterator (
99  boost::asio::ip::udp::resolver ( lService ).resolve (
100  boost::asio::ip::udp::resolver::query ( boost::asio::ip::udp::v4() , lIP.first , lIP.second )
101  )
102  )
103  );
104  lAddr = lEndpoint.address().to_string();
105  lPort = lEndpoint.port();
106  }
107  catch ( const std::exception& aExc )
108  {
109  exception::HostnameToIPlookupFailed lExc;
110  log ( lExc , "Hostname to IP look up failed for hostname=" , lIP.first , ", port=" , lIP.second );
111  log ( lExc , "ASIO threw exception with what returning: ", Quote ( aExc.what() ) );
112  throw lExc;
113  }
114 
115  std::vector< uint32_t > lIPAddr;
116 
117  try
118  {
119  boost::spirit::qi::phrase_parse ( lAddr.begin() ,
120  lAddr.end() ,
121  ( boost::spirit::qi::eps >
122  boost::spirit::qi::uint_ > boost::spirit::qi::lit ( "." ) >
123  boost::spirit::qi::uint_ > boost::spirit::qi::lit ( "." ) >
124  boost::spirit::qi::uint_ > boost::spirit::qi::lit ( "." ) >
125  boost::spirit::qi::uint_ ),
126  boost::spirit::ascii::space ,
127  lIPAddr
128  );
129  }
130  catch ( const std::exception& aExc )
131  {
132  exception::ParsingTargetURLfailed lExc;
133  log ( lExc , "Boost::ASIO returned address " , Quote ( lAddr ) , " for hostname " , Quote (lIP.first) , " which could not be parsed as " , Quote ( "aaa.bbb.ccc.ddd" ) );
134  throw lExc;
135  }
136 
137  uint32_t lIPaddress = ( lIPAddr[0] <<24 ) | ( lIPAddr[1] <<16 ) | ( lIPAddr[2] <<8 ) | ( lIPAddr[3] );
138  log ( Info() , "Converted IP address string " , Quote ( lIt->second ) , " to " ,
139  Integer ( lIPAddr[0] ) , "." , Integer ( lIPAddr[1] ) , "." , Integer ( lIPAddr[2] ) , "." , Integer ( lIPAddr[3] ) , ":" , Integer ( lPort ) ,
140  " and converted this to IP " , Integer ( lIPaddress, IntFmt< hex , fixed >() ) , ", port " , Integer ( lPort, IntFmt< hex , fixed >() ) );
141  return std::make_pair ( lIPaddress , lPort );
142  }
143 
144 
145  template < typename InnerProtocol >
146  ControlHub< InnerProtocol >::ControlHub ( const std::string& aId, const URI& aUri ) :
147  InnerProtocol ( aId , aUri ),
148  mDeviceIPaddress ( 0 ),
149  mDevicePort ( 0 )
150  {
151  std::pair< uint32_t , uint16_t > lPair ( ExtractTargetID ( aUri ) );
152  mDeviceIPaddress = htonl ( lPair.first );
153  mDevicePort = htons ( lPair.second );
154  }
155 
156 
157  template < typename InnerProtocol >
159  {
160  }
161 
162 
163  template < typename InnerProtocol >
165  {
166  // -------------------------------------------------------------------------------------------------------------
167  // 8 bytes form the preamble:
168  // Device IP address (4 bytes)
169  // Device Port number (2 bytes)
170  // Word-count (2 bytes) will be updated before transmission in predispatch
171  // -------------------------------------------------------------------------------------------------------------
172  // 12 bytes form the preamble reply:
173  // Chunk Byte-count (4 bytes)
174  // Device IP address (4 bytes)
175  // Device Port number (2 bytes)
176  // Error code (2 bytes)
177  // -------------------------------------------------------------------------------------------------------------
178  {
179  boost::lock_guard<boost::mutex> lPreamblesLock ( mPreamblesMutex );
180  mPreambles.push_back ( tpreamble () );
181  tpreamble* lPreambles = & mPreambles.back();
182  // lPreambles->mSendByteCountPtr = ( uint32_t* ) ( aBuffers->send ( ( uint32_t ) ( 0 ) ) );
183  aBuffers->send ( mDeviceIPaddress );
184  aBuffers->send ( mDevicePort );
185  lPreambles->mSendWordCountPtr = ( uint16_t* ) ( aBuffers->send ( ( uint16_t ) ( 0 ) ) );
186  // aBuffers->receive ( lPreambles->mReplyTotalByteCounter );
187  aBuffers->receive ( lPreambles->mReplyChunkByteCounter );
188  aBuffers->receive ( lPreambles->mReplyDeviceIPaddress );
189  aBuffers->receive ( lPreambles->mReplyDevicePort );
190  aBuffers->receive ( lPreambles->mReplyErrorCode );
191  }
192  InnerProtocol::preamble ( aBuffers );
193  }
194 
195 
196 
197  template < typename InnerProtocol >
199  {
200  return InnerProtocol::getPreambleSize() + 2;
201  }
202 
203 
204  template < typename InnerProtocol >
206  {
207  InnerProtocol::predispatch ( aBuffers );
208  boost::lock_guard<boost::mutex> lPreamblesLock ( mPreamblesMutex );
209  tpreamble& lPreambles = mPreambles.back();
210  uint32_t lByteCount ( aBuffers->sendCounter() );
211  * ( lPreambles.mSendWordCountPtr ) = htons ( ( lByteCount-8 ) >>2 );
212  }
213 
214 
215  template < typename InnerProtocol >
217  uint8_t* aSendBufferEnd ,
218  std::deque< std::pair< uint8_t* , uint32_t > >::iterator aReplyStartIt ,
219  std::deque< std::pair< uint8_t* , uint32_t > >::iterator aReplyEndIt )
220  {
221  aReplyStartIt++;
222  uint32_t lReplyIPaddress ( * ( ( uint32_t* ) ( aReplyStartIt->first ) ) );
223 
224  if ( lReplyIPaddress != mDeviceIPaddress )
225  {
226  uhal::exception::ControlHubReturnedWrongAddress* lExc = new uhal::exception::ControlHubReturnedWrongAddress();
227  log ( *lExc , "Returned IP address " , Integer ( lReplyIPaddress , IntFmt< hex , fixed >() ) ,
228  " does not match that sent " , Integer ( mDeviceIPaddress, IntFmt< hex , fixed >() ) ,
229  " for device with URI: " , this->uri() );
230  boost::lock_guard<boost::mutex> lPreamblesLock ( mPreamblesMutex );
231  mPreambles.pop_front();
232  return lExc;
233  }
234 
235  aReplyStartIt++;
236  uint16_t lReplyPort ( * ( ( uint16_t* ) ( aReplyStartIt->first ) ) );
237 
238  if ( lReplyPort != mDevicePort )
239  {
240  uhal::exception::ControlHubReturnedWrongAddress* lExc = new uhal::exception::ControlHubReturnedWrongAddress();
241  log ( *lExc , "Returned Port number " , Integer ( lReplyPort ) ,
242  " does not match that sent " , Integer ( mDevicePort ) ,
243  " for device with URI: " , this->uri() );
244  boost::lock_guard<boost::mutex> lPreamblesLock ( mPreamblesMutex );
245  mPreambles.pop_front();
246  return lExc;
247  }
248 
249  aReplyStartIt++;
250  uint16_t lErrorCode ( ntohs ( * ( ( uint16_t* ) ( aReplyStartIt->first ) ) ) );
251 
252  if ( lErrorCode != 0 )
253  {
254  boost::lock_guard<boost::mutex> lPreamblesLock ( mPreamblesMutex );
255  mPreambles.pop_front();
256 
257  if ( lErrorCode == 1 || lErrorCode == 3 || lErrorCode == 4 )
258  {
259  uhal::exception::ControlHubTargetTimeout* lExc = new uhal::exception::ControlHubTargetTimeout();
260  log ( *lExc , "The ControlHub did not receive any response from the target with URI ", Quote(this->uri()) );
261  log ( *lExc , "ControlHub returned error code ", Integer ( lErrorCode ), " = '", TranslatedFmt<uint16_t>(lErrorCode, translateErrorCode), "'");
262  return lExc ;
263  }
264 
265  uhal::exception::ControlHubErrorCodeSet* lExc = new uhal::exception::ControlHubErrorCodeSet();
266  log ( *lExc , "The ControlHub returned error code " , Integer ( lErrorCode, IntFmt< hex , fixed >() ),
267  " = ", TranslatedFmt<uint16_t>(lErrorCode, translateErrorCode),
268  " for target with URI " , Quote(this->uri()) );
269  return lExc;
270  }
271 
272  {
273  boost::lock_guard<boost::mutex> lPreamblesLock ( mPreamblesMutex );
274  mPreambles.pop_front();
275  }
276  return InnerProtocol::validate ( ( aSendBufferStart+=8 ) , aSendBufferEnd , ( ++aReplyStartIt ) , aReplyEndIt );
277  }
278 
279 
280  template < typename InnerProtocol >
282  {
283  return 60;
284  }
285 
286 
287  template < typename InnerProtocol >
289  {
290  {
291  boost::lock_guard<boost::mutex> lPreamblesLock ( mPreamblesMutex );
292  mPreambles.clear();
293  }
294  InnerProtocol::dispatchExceptionHandler();
295  }
296 
297 
298  template < typename InnerProtocol >
299  void ControlHub< InnerProtocol >::translateErrorCode(std::ostream& aStream, const uint16_t& aErrorCode)
300  {
301  switch (aErrorCode) {
302  case 0:
303  aStream << "success";
304  break;
305  case 1:
306  aStream << "no reply to control packet";
307  break;
308  case 2:
309  aStream << "internal timeout within ControlHub";
310  break;
311  case 3:
312  aStream << "no reply to status packet";
313  break;
314  case 4:
315  aStream << "no reply to resend request";
316  break;
317  case 5:
318  aStream << "malformed status packet received";
319  break;
320  case 6:
321  aStream << "request uses incorrect protocol version";
322  break;
323  default:
324  aStream << "UNKNOWN";
325  }
326  }
327 
328 
329  template class ControlHub< IPbus<1, 3> >;
330  template class ControlHub< IPbus<2, 0> >;
331 }
332 
333 
uhal::ControlHub::mDevicePort
uint16_t mDevicePort
The port number of the target device that is connected to the Control Hub.
Definition: ProtocolControlHub.hpp:151
boost::shared_ptr
Definition: DerivedNodeFactory.hpp:52
uhal::ControlHub::tpreamble::mReplyChunkByteCounter
uint32_t mReplyChunkByteCounter
A legacy counter.
Definition: ProtocolControlHub.hpp:160
uhal::ControlHub::ControlHub
ControlHub(const std::string &aId, const URI &aUri)
Constructor.
Definition: ProtocolControlHub.cpp:146
uhal::URI::mArguments
NameValuePairVectorType mArguments
The "key1=val1&key2=val2&key3=val3" part of a URI of the form "protocol://host:port/patha/pathb/blah....
Definition: URI.hpp:62
uhal::ControlHub::tpreamble::mReplyDeviceIPaddress
uint32_t mReplyDeviceIPaddress
The returned target device ID (IP address)
Definition: ProtocolControlHub.hpp:162
uhal::ControlHub::mDeviceIPaddress
uint32_t mDeviceIPaddress
The IP address of the target device that is connected to the Control Hub.
Definition: ProtocolControlHub.hpp:148
uhal::ControlHub::getPreambleSize
virtual uint32_t getPreambleSize()
Get the size of the preamble added by this protocol layer.
Definition: ProtocolControlHub.cpp:198
uhal::IntFmt
Empty struct which acts as a dummy variable for passing the formatting information around.
Definition: log_inserters.integer.hpp:68
uhal::ControlHub::tpreamble::mSendWordCountPtr
uint16_t * mSendWordCountPtr
The number of 32-bit words in the IPbus packet (legacy and could be removed)
Definition: ProtocolControlHub.hpp:157
uhal::ControlHub::translateErrorCode
static void translateErrorCode(std::ostream &aStream, const uint16_t &aErrorCode)
Definition: ProtocolControlHub.cpp:299
uhal::ControlHub::tpreamble::mReplyDevicePort
uint16_t mReplyDevicePort
The returned target device ID (port number)
Definition: ProtocolControlHub.hpp:164
uhal::exception::exception
An abstract base exception class, including an interface to throw as the derived type (for passing ex...
Definition: exception.hpp:71
uhal
Definition: HttpResponseGrammar.hpp:49
uhal::ControlHub::preamble
virtual void preamble(boost::shared_ptr< Buffers > aBuffers)
Add a preamble to an IPbus buffer.
Definition: ProtocolControlHub.cpp:164
ProtocolControlHub.hpp
uhal::ControlHub::tpreamble::mReplyErrorCode
uint16_t mReplyErrorCode
An error code returned describing the status of the control hub.
Definition: ProtocolControlHub.hpp:166
uhal::ControlHub::predispatch
virtual void predispatch(boost::shared_ptr< Buffers > aBuffers)
Finalize an IPbus buffer before it is transmitted.
Definition: ProtocolControlHub.cpp:205
uhal::Info
InfoLevel Info
Definition: LogLevels.cpp:115
uhal::log
void log(FatalLevel &aFatal, const T0 &aArg0)
Function to add a log entry at Fatal level.
Definition: log.hxx:20
uhal::Integer
_Integer< T, IntFmt<> > Integer(const T &aT)
Forward declare a function which creates an instance of the ultra-lightweight wrapper from an integer...
Definition: log_inserters.integer.hxx:43
uhal::ControlHub::tpreamble
A struct representing the preamble which will be prepended to an IPbus buffer for the benefit of the ...
Definition: ProtocolControlHub.hpp:155
uhal::ExtractTargetID
std::pair< uint32_t, uint16_t > ExtractTargetID(const URI &aUri)
Extract an IP-address and port number from a URI object.
Definition: ProtocolControlHub.cpp:50
ProtocolIPbus.hpp
uhal::ControlHub::getMaxNumberOfBuffers
virtual uint32_t getMaxNumberOfBuffers()
Returns the maximum number of buffers that should be in-flight from the uHAL client at any given time...
Definition: ProtocolControlHub.cpp:281
uhal::Quote
_Quote< T > Quote(const T &aT)
Definition: log_inserters.quote.hxx:49
uhal::tests::uri
std::string uri
Definition: test_single.cpp:89
uhal::ControlHub::validate
virtual exception::exception * validate(uint8_t *aSendBufferStart, uint8_t *aSendBufferEnd, std::deque< std::pair< uint8_t *, uint32_t > >::iterator aReplyStartIt, std::deque< std::pair< uint8_t *, uint32_t > >::iterator aReplyEndIt)
Function which the dispatch calls when the reply is received to check that the headers are as expecte...
Definition: ProtocolControlHub.cpp:216
uhal::TranslatedFmt
Definition: ProtocolIPbusCore.hpp:263
uhal::ControlHub::~ControlHub
virtual ~ControlHub()
Destructor.
Definition: ProtocolControlHub.cpp:158
uhal::URI
Struct to store a URI when parsed by boost spirit.
Definition: URI.hpp:50
uhal::ControlHub
Transport protocol to transfer an IPbus buffer via ControlHub.
Definition: ProtocolControlHub.hpp:86
Buffers.hpp
uhal::ControlHub::dispatchExceptionHandler
virtual void dispatchExceptionHandler()
Function which tidies up this protocol layer in the event of an exception.
Definition: ProtocolControlHub.cpp:288