μHAL (v2.8.17)
Part of the IPbus software repository
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
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 <mutex>
37#include <string>
38#include <vector>
39
40#include <boost/asio.hpp>
41#include <boost/fusion/adapted/std_pair.hpp>
42#include <boost/spirit/include/qi.hpp>
43#include <boost/spirit/include/qi_eps.hpp>
44
45#include "uhal/Buffers.hpp"
47
48
49namespace uhal
50{
51 std::pair< uint32_t , uint16_t > ExtractTargetID ( const URI& aUri )
52 {
53 NameValuePairVectorType::const_iterator lIt = aUri.mArguments.begin();
54
55 for ( ; lIt != aUri.mArguments.end() ; ++lIt )
56 {
57 if ( lIt->first == "target" )
58 {
59 break;
60 }
61 }
62
63 if ( lIt == aUri.mArguments.end() )
64 {
65 exception::XMLfileMissingRequiredParameters lExc;
66 log ( lExc , "Expected URI arguments of the form " , Quote ( "target=192.168.200.200:50001" ) ,". It appears that this is missing in URI " , aUri );
67 throw lExc;
68 }
69
70 std::pair< std::string , std::string > lIP;
71
72 try
73 {
74 boost::spirit::qi::phrase_parse ( lIt->second.begin() ,
75 lIt->second.end(),
76 ( boost::spirit::qi::eps >
77 * ( boost::spirit::qi::char_ - boost::spirit::qi::lit ( ":" ) ) >
78 boost::spirit::qi::lit ( ":" ) >
79 *boost::spirit::qi::char_
80 ) ,
81 boost::spirit::ascii::space ,
82 lIP
83 );
84 }
85 catch ( const std::exception& aExc )
86 {
87 exception::ParsingTargetURLfailed lExc;
88 log ( lExc , "Expected a string of the form " , Quote ( "hostIP:port" ) , " or " , Quote ( "hostname:port" ) , " but received " , Quote ( lIt->second ) , "." );
89 throw lExc;
90 }
91
92 std::string lAddr;
93 uint16_t lPort;
94
95 try
96 {
97 boost::asio::io_service lService;
98 boost::asio::ip::udp::endpoint lEndpoint (
99 *boost::asio::ip::udp::resolver::iterator (
100 boost::asio::ip::udp::resolver ( lService ).resolve (
101 boost::asio::ip::udp::resolver::query ( boost::asio::ip::udp::v4() , lIP.first , lIP.second )
102 )
103 )
104 );
105 lAddr = lEndpoint.address().to_string();
106 lPort = lEndpoint.port();
107 }
108 catch ( const std::exception& aExc )
109 {
110 exception::HostnameToIPlookupFailed lExc;
111 log ( lExc , "Hostname to IP look up failed for hostname=" , lIP.first , ", port=" , lIP.second );
112 log ( lExc , "ASIO threw exception with what returning: ", Quote ( aExc.what() ) );
113 throw lExc;
114 }
115
116 std::vector< uint32_t > lIPAddr;
117
118 try
119 {
120 boost::spirit::qi::phrase_parse ( lAddr.begin() ,
121 lAddr.end() ,
122 ( boost::spirit::qi::eps >
123 boost::spirit::qi::uint_ > boost::spirit::qi::lit ( "." ) >
124 boost::spirit::qi::uint_ > boost::spirit::qi::lit ( "." ) >
125 boost::spirit::qi::uint_ > boost::spirit::qi::lit ( "." ) >
126 boost::spirit::qi::uint_ ),
127 boost::spirit::ascii::space ,
128 lIPAddr
129 );
130 }
131 catch ( const std::exception& aExc )
132 {
133 exception::ParsingTargetURLfailed lExc;
134 log ( lExc , "Boost::ASIO returned address " , Quote ( lAddr ) , " for hostname " , Quote (lIP.first) , " which could not be parsed as " , Quote ( "aaa.bbb.ccc.ddd" ) );
135 throw lExc;
136 }
137
138 uint32_t lIPaddress = ( lIPAddr[0] <<24 ) | ( lIPAddr[1] <<16 ) | ( lIPAddr[2] <<8 ) | ( lIPAddr[3] );
139 log ( Info() , "Converted IP address string " , Quote ( lIt->second ) , " to " ,
140 Integer ( lIPAddr[0] ) , "." , Integer ( lIPAddr[1] ) , "." , Integer ( lIPAddr[2] ) , "." , Integer ( lIPAddr[3] ) , ":" , Integer ( lPort ) ,
141 " and converted this to IP " , Integer ( lIPaddress, IntFmt< hex , fixed >() ) , ", port " , Integer ( lPort, IntFmt< hex , fixed >() ) );
142 return std::make_pair ( lIPaddress , lPort );
143 }
144
145
146 template < typename InnerProtocol >
147 ControlHub< InnerProtocol >::ControlHub ( const std::string& aId, const URI& aUri ) :
148 InnerProtocol ( aId , aUri ),
149 mDeviceIPaddress ( 0 ),
150 mDevicePort ( 0 )
151 {
152 std::pair< uint32_t , uint16_t > lPair ( ExtractTargetID ( aUri ) );
153 mDeviceIPaddress = htonl ( lPair.first );
154 mDevicePort = htons ( lPair.second );
155 }
156
157
158 template < typename InnerProtocol >
160 {
161 }
162
163
164 template < typename InnerProtocol >
165 void ControlHub< InnerProtocol >::preamble ( std::shared_ptr< Buffers > aBuffers )
166 {
167 // -------------------------------------------------------------------------------------------------------------
168 // 8 bytes form the preamble:
169 // Device IP address (4 bytes)
170 // Device Port number (2 bytes)
171 // Word-count (2 bytes) will be updated before transmission in predispatch
172 // -------------------------------------------------------------------------------------------------------------
173 // 12 bytes form the preamble reply:
174 // Chunk Byte-count (4 bytes)
175 // Device IP address (4 bytes)
176 // Device Port number (2 bytes)
177 // Error code (2 bytes)
178 // -------------------------------------------------------------------------------------------------------------
179 {
180 std::lock_guard<std::mutex> lPreamblesLock ( mPreamblesMutex );
181 mPreambles.push_back ( tpreamble () );
182 tpreamble* lPreambles = & mPreambles.back();
183 // lPreambles->mSendByteCountPtr = ( uint32_t* ) ( aBuffers->send ( ( uint32_t ) ( 0 ) ) );
184 aBuffers->send ( mDeviceIPaddress );
185 aBuffers->send ( mDevicePort );
186 lPreambles->mSendWordCountPtr = ( uint16_t* ) ( aBuffers->send ( ( uint16_t ) ( 0 ) ) );
187 // aBuffers->receive ( lPreambles->mReplyTotalByteCounter );
188 aBuffers->receive ( lPreambles->mReplyChunkByteCounter );
189 aBuffers->receive ( lPreambles->mReplyDeviceIPaddress );
190 aBuffers->receive ( lPreambles->mReplyDevicePort );
191 aBuffers->receive ( lPreambles->mReplyErrorCode );
192 }
193 InnerProtocol::preamble ( aBuffers );
194 }
195
196
197
198 template < typename InnerProtocol >
200 {
201 return InnerProtocol::getPreambleSize() + 2;
202 }
203
204
205 template < typename InnerProtocol >
206 void ControlHub< InnerProtocol >::predispatch ( std::shared_ptr< Buffers > aBuffers )
207 {
208 InnerProtocol::predispatch ( aBuffers );
209 std::lock_guard<std::mutex> lPreamblesLock ( mPreamblesMutex );
210 tpreamble& lPreambles = mPreambles.back();
211 uint32_t lByteCount ( aBuffers->sendCounter() );
212 * ( lPreambles.mSendWordCountPtr ) = htons ( ( lByteCount-8 ) >>2 );
213 }
214
215
216 template < typename InnerProtocol >
218 uint8_t* aSendBufferEnd ,
219 std::deque< std::pair< uint8_t* , uint32_t > >::iterator aReplyStartIt ,
220 std::deque< std::pair< uint8_t* , uint32_t > >::iterator aReplyEndIt )
221 {
222 aReplyStartIt++;
223 uint32_t lReplyIPaddress ( * ( ( uint32_t* ) ( aReplyStartIt->first ) ) );
224
225 if ( lReplyIPaddress != mDeviceIPaddress )
226 {
227 uhal::exception::ControlHubReturnedWrongAddress* lExc = new uhal::exception::ControlHubReturnedWrongAddress();
228 log ( *lExc , "Returned IP address " , Integer ( lReplyIPaddress , IntFmt< hex , fixed >() ) ,
229 " does not match that sent " , Integer ( mDeviceIPaddress, IntFmt< hex , fixed >() ) ,
230 " for device with URI: " , this->uri() );
231 std::lock_guard<std::mutex> lPreamblesLock ( mPreamblesMutex );
232 mPreambles.pop_front();
233 return lExc;
234 }
235
236 aReplyStartIt++;
237 uint16_t lReplyPort ( * ( ( uint16_t* ) ( aReplyStartIt->first ) ) );
238
239 if ( lReplyPort != mDevicePort )
240 {
241 uhal::exception::ControlHubReturnedWrongAddress* lExc = new uhal::exception::ControlHubReturnedWrongAddress();
242 log ( *lExc , "Returned Port number " , Integer ( lReplyPort ) ,
243 " does not match that sent " , Integer ( mDevicePort ) ,
244 " for device with URI: " , this->uri() );
245 std::lock_guard<std::mutex> lPreamblesLock ( mPreamblesMutex );
246 mPreambles.pop_front();
247 return lExc;
248 }
249
250 aReplyStartIt++;
251 uint16_t lErrorCode ( ntohs ( * ( ( uint16_t* ) ( aReplyStartIt->first ) ) ) );
252
253 if ( lErrorCode != 0 )
254 {
255 std::lock_guard<std::mutex> lPreamblesLock ( mPreamblesMutex );
256 mPreambles.pop_front();
257
258 if ( lErrorCode == 1 || lErrorCode == 3 || lErrorCode == 4 )
259 {
260 uhal::exception::ControlHubTargetTimeout* lExc = new uhal::exception::ControlHubTargetTimeout();
261 log ( *lExc , "The ControlHub did not receive any response from the target with URI ", Quote(this->uri()) );
262 log ( *lExc , "ControlHub returned error code ", Integer ( lErrorCode ), " = '", TranslatedFmt<uint16_t>(lErrorCode, translateErrorCode), "'");
263 return lExc ;
264 }
265
266 uhal::exception::ControlHubErrorCodeSet* lExc = new uhal::exception::ControlHubErrorCodeSet();
267 log ( *lExc , "The ControlHub returned error code " , Integer ( lErrorCode, IntFmt< hex , fixed >() ),
268 " = ", TranslatedFmt<uint16_t>(lErrorCode, translateErrorCode),
269 " for target with URI " , Quote(this->uri()) );
270 return lExc;
271 }
272
273 {
274 std::lock_guard<std::mutex> lPreamblesLock ( mPreamblesMutex );
275 mPreambles.pop_front();
276 }
277 return InnerProtocol::validate ( ( aSendBufferStart+=8 ) , aSendBufferEnd , ( ++aReplyStartIt ) , aReplyEndIt );
278 }
279
280
281 template < typename InnerProtocol >
283 {
284 return 60;
285 }
286
287
288 template < typename InnerProtocol >
290 {
291 {
292 std::lock_guard<std::mutex> lPreamblesLock ( mPreamblesMutex );
293 mPreambles.clear();
294 }
295 InnerProtocol::dispatchExceptionHandler();
296 }
297
298
299 template < typename InnerProtocol >
300 void ControlHub< InnerProtocol >::translateErrorCode(std::ostream& aStream, const uint16_t& aErrorCode)
301 {
302 switch (aErrorCode) {
303 case 0:
304 aStream << "success";
305 break;
306 case 1:
307 aStream << "no reply to control packet";
308 break;
309 case 2:
310 aStream << "internal timeout within ControlHub";
311 break;
312 case 3:
313 aStream << "no reply to status packet";
314 break;
315 case 4:
316 aStream << "no reply to resend request";
317 break;
318 case 5:
319 aStream << "malformed status packet received";
320 break;
321 case 6:
322 aStream << "request uses incorrect protocol version";
323 break;
324 case 7:
325 aStream << "device is not in the ControlHub's allowlist";
326 break;
327 default:
328 aStream << "UNKNOWN";
329 }
330 }
331
332
333 template class ControlHub< IPbus<1, 3> >;
334 template class ControlHub< IPbus<2, 0> >;
335}
336
337
\rst Wraps a Python iterator so that it can also be used as a C++ input iterator
Definition: pytypes.h:1102
Transport protocol to transfer an IPbus buffer via ControlHub.
virtual void preamble(std::shared_ptr< Buffers > aBuffers)
Add a preamble to an IPbus buffer.
ControlHub(const std::string &aId, const URI &aUri)
Constructor.
uint32_t mDeviceIPaddress
The IP address of the target device that is connected to the Control Hub.
static void translateErrorCode(std::ostream &aStream, const uint16_t &aErrorCode)
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 predispatch(std::shared_ptr< Buffers > aBuffers)
Finalize an IPbus buffer before it is transmitted.
virtual void dispatchExceptionHandler()
Function which tidies up this protocol layer in the event of an exception.
virtual uint32_t getPreambleSize()
Get the size of the preamble added by this protocol layer.
virtual uint32_t getMaxNumberOfBuffers()
Returns the maximum number of buffers that should be in-flight from the uHAL client at any given time...
uint16_t mDevicePort
The port number of the target device that is connected to the Control Hub.
virtual ~ControlHub()
Destructor.
An abstract base exception class, including an interface to throw as the derived type (for passing ex...
Definition: exception.hpp:71
_Quote< T > Quote(const T &aT)
_Integer< T, IntFmt<> > Integer(const T &aT)
Forward declare a function which creates an instance of the ultra-lightweight wrapper from an integer...
void log(FatalLevel &aFatal, const T0 &aArg0)
Function to add a log entry at Fatal level.
Definition: log.hxx:18
std::pair< uint32_t, uint16_t > ExtractTargetID(const URI &aUri)
Extract an IP-address and port number from a URI object.
InfoLevel Info
Definition: LogLevels.cpp:115
A struct representing the preamble which will be prepended to an IPbus buffer for the benefit of the ...
uint16_t * mSendWordCountPtr
The number of 32-bit words in the IPbus packet (legacy and could be removed)
uint32_t mReplyChunkByteCounter
A legacy counter.
uint16_t mReplyDevicePort
The returned target device ID (port number)
uint16_t mReplyErrorCode
An error code returned describing the status of the control hub.
uint32_t mReplyDeviceIPaddress
The returned target device ID (IP address)
Empty struct which acts as a dummy variable for passing the formatting information around.
Struct to store a URI when parsed by boost spirit.
Definition: URI.hpp:50
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