μHAL (v2.6.5)
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  //log ( Debug() , ThisLocation() );
155  }
156 
157 
158  template < typename InnerProtocol >
160  {
161  //log ( Debug() , ThisLocation() );
162  }
163 
164 
165  template < typename InnerProtocol >
167  {
168  //log ( Debug() , ThisLocation() );
169  // -------------------------------------------------------------------------------------------------------------
170  // 8 bytes form the preamble:
171  // Device IP address (4 bytes)
172  // Device Port number (2 bytes)
173  // Word-count (2 bytes) will be updated before transmission in predispatch
174  // -------------------------------------------------------------------------------------------------------------
175  // 12 bytes form the preamble reply:
176  // Chunk Byte-count (4 bytes)
177  // Device IP address (4 bytes)
178  // Device Port number (2 bytes)
179  // Error code (2 bytes)
180  // -------------------------------------------------------------------------------------------------------------
181  {
182  boost::lock_guard<boost::mutex> lPreamblesLock ( mPreamblesMutex );
183  mPreambles.push_back ( tpreamble () );
184  tpreamble* lPreambles = & mPreambles.back();
185  // lPreambles->mSendByteCountPtr = ( uint32_t* ) ( aBuffers->send ( ( uint32_t ) ( 0 ) ) );
186  aBuffers->send ( mDeviceIPaddress );
187  aBuffers->send ( mDevicePort );
188  lPreambles->mSendWordCountPtr = ( uint16_t* ) ( aBuffers->send ( ( uint16_t ) ( 0 ) ) );
189  // aBuffers->receive ( lPreambles->mReplyTotalByteCounter );
190  aBuffers->receive ( lPreambles->mReplyChunkByteCounter );
191  aBuffers->receive ( lPreambles->mReplyDeviceIPaddress );
192  aBuffers->receive ( lPreambles->mReplyDevicePort );
193  aBuffers->receive ( lPreambles->mReplyErrorCode );
194  }
195  InnerProtocol::preamble ( aBuffers );
196  }
197 
198 
199 
200  template < typename InnerProtocol >
202  {
203  return InnerProtocol::getPreambleSize() +2;
204  }
205 
206 
207  template < typename InnerProtocol >
209  {
210  InnerProtocol::predispatch ( aBuffers );
211  //log ( Debug() , ThisLocation() );
212  boost::lock_guard<boost::mutex> lPreamblesLock ( mPreamblesMutex );
213  tpreamble& lPreambles = mPreambles.back();
214  uint32_t lByteCount ( aBuffers->sendCounter() );
215  // * ( lPreambles.mSendByteCountPtr ) = htonl ( lByteCount-4 );
216  * ( lPreambles.mSendWordCountPtr ) = htons ( ( lByteCount-8 ) >>2 );
217  }
218 
219 
220  template < typename InnerProtocol >
222  uint8_t* aSendBufferEnd ,
223  std::deque< std::pair< uint8_t* , uint32_t > >::iterator aReplyStartIt ,
224  std::deque< std::pair< uint8_t* , uint32_t > >::iterator aReplyEndIt )
225  {
226  // aReplyStartIt++;
227  aReplyStartIt++;
228  uint32_t lReplyIPaddress ( * ( ( uint32_t* ) ( aReplyStartIt->first ) ) );
229 
230  if ( lReplyIPaddress != mDeviceIPaddress )
231  {
232  uhal::exception::ControlHubReturnedWrongAddress* lExc = new uhal::exception::ControlHubReturnedWrongAddress();
233  log ( *lExc , "Returned IP address " , Integer ( lReplyIPaddress , IntFmt< hex , fixed >() ) ,
234  " does not match that sent " , Integer ( mDeviceIPaddress, IntFmt< hex , fixed >() ) ,
235  " for device with URI: " , this->uri() );
236  boost::lock_guard<boost::mutex> lPreamblesLock ( mPreamblesMutex );
237  mPreambles.pop_front();
238  return lExc;
239  }
240 
241  aReplyStartIt++;
242  uint16_t lReplyPort ( * ( ( uint16_t* ) ( aReplyStartIt->first ) ) );
243 
244  if ( lReplyPort != mDevicePort )
245  {
246  uhal::exception::ControlHubReturnedWrongAddress* lExc = new uhal::exception::ControlHubReturnedWrongAddress();
247  log ( *lExc , "Returned Port number " , Integer ( lReplyPort ) ,
248  " does not match that sent " , Integer ( mDevicePort ) ,
249  " for device with URI: " , this->uri() );
250  boost::lock_guard<boost::mutex> lPreamblesLock ( mPreamblesMutex );
251  mPreambles.pop_front();
252  return lExc;
253  }
254 
255  aReplyStartIt++;
256  uint16_t lErrorCode ( ntohs ( * ( ( uint16_t* ) ( aReplyStartIt->first ) ) ) );
257 
258  if ( lErrorCode != 0 )
259  {
260  boost::lock_guard<boost::mutex> lPreamblesLock ( mPreamblesMutex );
261  mPreambles.pop_front();
262 
263  if ( lErrorCode == 1 || lErrorCode == 3 || lErrorCode == 4 )
264  {
265  uhal::exception::ControlHubTargetTimeout* lExc = new uhal::exception::ControlHubTargetTimeout();
266  log ( *lExc , "The ControlHub did not receive any response from the target with URI ", Quote(this->uri()) );
267  log ( *lExc , "ControlHub returned error code ", Integer ( lErrorCode ), " = '", TranslatedFmt<uint16_t>(lErrorCode, translateErrorCode), "'");
268  return lExc ;
269  }
270 
271  uhal::exception::ControlHubErrorCodeSet* lExc = new uhal::exception::ControlHubErrorCodeSet();
272  log ( *lExc , "The ControlHub returned error code " , Integer ( lErrorCode, IntFmt< hex , fixed >() ),
273  " = ", TranslatedFmt<uint16_t>(lErrorCode, translateErrorCode),
274  " for target with URI " , Quote(this->uri()) );
275  return lExc;
276  }
277 
278  //aReplyStartIt++;
279  // log ( Info() , "Control Hub has validated the packet headers" );
280  {
281  boost::lock_guard<boost::mutex> lPreamblesLock ( mPreamblesMutex );
282  mPreambles.pop_front();
283  }
284  return InnerProtocol::validate ( ( aSendBufferStart+=8 ) , aSendBufferEnd , ( ++aReplyStartIt ) , aReplyEndIt );
285  }
286 
287 
288 
289  template < typename InnerProtocol >
291  {
292  log ( Info , ThisLocation() );
293  {
294  boost::lock_guard<boost::mutex> lPreamblesLock ( mPreamblesMutex );
295  mPreambles.clear();
296  }
297  InnerProtocol::dispatchExceptionHandler();
298  }
299 
300 
301  template < typename InnerProtocol >
302  void ControlHub< InnerProtocol >::translateErrorCode(std::ostream& aStream, const uint16_t& aErrorCode)
303  {
304  switch (aErrorCode) {
305  case 0:
306  aStream << "success";
307  break;
308  case 1:
309  aStream << "no reply to control packet";
310  break;
311  case 2:
312  aStream << "internal timeout within ControlHub";
313  break;
314  case 3:
315  aStream << "no reply to status packet";
316  break;
317  case 4:
318  aStream << "no reply to resend request";
319  break;
320  case 5:
321  aStream << "malformed status packet received";
322  break;
323  case 6:
324  aStream << "request uses incorrect protocol version";
325  break;
326  default:
327  aStream << "UNKNOWN";
328  }
329  }
330 
331 
332  template class ControlHub< IPbus<1, 3> >;
333  template class ControlHub< IPbus<2, 0> >;
334 }
335 
336 
virtual uint32_t getPreambleSize()
Get the size of the preamble added by this protocol layer.
uint32_t mDeviceIPaddress
The IP address of the target device that is connected to the Control Hub.
virtual void dispatchExceptionHandler()
Function which tidies up this protocol layer in the event of an exception.
uint32_t mReplyChunkByteCounter
A legacy counter.
ControlHub(const std::string &aId, const URI &aUri)
Constructor.
uint32_t mReplyDeviceIPaddress
The returned target device ID (IP address)
uint16_t * mSendWordCountPtr
The number of 32-bit words in the IPbus packet (legacy and could be removed)
boost::mutex mPreamblesMutex
Mutex to be used when accessing mPreambles.
An abstract base exception class providing an interface to a throw/ThrowAsDerivedType mechanism which...
Definition: exception.hpp:89
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...
virtual void preamble(boost::shared_ptr< Buffers > aBuffers)
Add a preamble to an IPbus buffer.
std::deque< tpreamble > mPreambles
A queue of preample structs making the memory used by the preambles persistent during the dispatch...
A struct representing the preamble which will be prepended to an IPbus buffer for the benefit of the ...
std::pair< uint32_t, uint16_t > ExtractTargetID(const URI &aUri)
Extract an IP-address and port number from a URI object.
Transport protocol to transfer an IPbus buffer via ControlHub.
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
Empty struct which acts as a dummy variable for passing the formatting information around...
_Quote< T > Quote(const T &aT)
static void translateErrorCode(std::ostream &aStream, const uint16_t &aErrorCode)
virtual void predispatch(boost::shared_ptr< Buffers > aBuffers)
Finalize an IPbus buffer before it is transmitted.
uint16_t mReplyErrorCode
An error code returned describing the status of the control hub.
virtual ~ControlHub()
Destructor.
#define ThisLocation()
uint16_t mReplyDevicePort
The returned target device ID (port number)
std::string uri
Definition: test_single.cpp:89
InfoLevel Info
Definition: LogLevels.cpp:114
uint16_t mDevicePort
The port number of the target device that is connected to the Control Hub.
_Integer< T, IntFmt<> > Integer(const T &aT)
Forward declare a function which creates an instance of the ultra-lightweight wrapper from an integer...
Struct to store a URI when parsed by boost spirit.
Definition: URI.hpp:49