μHAL (v2.8.22)
Part of the IPbus software repository
Loading...
Searching...
No Matches
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_context lService;
98 boost::asio::ip::udp::endpoint lEndpoint (
99 *boost::asio::ip::udp::resolver ( lService ).resolve (
100 boost::asio::ip::udp::v4() , lIP.first , lIP.second
101 ).begin()
102 );
103 lAddr = lEndpoint.address().to_string();
104 lPort = lEndpoint.port();
105 }
106 catch ( const std::exception& aExc )
107 {
108 exception::HostnameToIPlookupFailed lExc;
109 log ( lExc , "Hostname to IP look up failed for hostname=" , lIP.first , ", port=" , lIP.second );
110 log ( lExc , "ASIO threw exception with what returning: ", Quote ( aExc.what() ) );
111 throw lExc;
112 }
113
114 std::vector< uint32_t > lIPAddr;
115
116 try
117 {
118 boost::spirit::qi::phrase_parse ( lAddr.begin() ,
119 lAddr.end() ,
120 ( boost::spirit::qi::eps >
121 boost::spirit::qi::uint_ > boost::spirit::qi::lit ( "." ) >
122 boost::spirit::qi::uint_ > boost::spirit::qi::lit ( "." ) >
123 boost::spirit::qi::uint_ > boost::spirit::qi::lit ( "." ) >
124 boost::spirit::qi::uint_ ),
125 boost::spirit::ascii::space ,
126 lIPAddr
127 );
128 }
129 catch ( const std::exception& aExc )
130 {
131 exception::ParsingTargetURLfailed lExc;
132 log ( lExc , "Boost::ASIO returned address " , Quote ( lAddr ) , " for hostname " , Quote (lIP.first) , " which could not be parsed as " , Quote ( "aaa.bbb.ccc.ddd" ) );
133 throw lExc;
134 }
135
136 uint32_t lIPaddress = ( lIPAddr[0] <<24 ) | ( lIPAddr[1] <<16 ) | ( lIPAddr[2] <<8 ) | ( lIPAddr[3] );
137 log ( Info() , "Converted IP address string " , Quote ( lIt->second ) , " to " ,
138 Integer ( lIPAddr[0] ) , "." , Integer ( lIPAddr[1] ) , "." , Integer ( lIPAddr[2] ) , "." , Integer ( lIPAddr[3] ) , ":" , Integer ( lPort ) ,
139 " and converted this to IP " , Integer ( lIPaddress, IntFmt< hex , fixed >() ) , ", port " , Integer ( lPort, IntFmt< hex , fixed >() ) );
140 return std::make_pair ( lIPaddress , lPort );
141 }
142
143
144 template < typename InnerProtocol >
145 ControlHub< InnerProtocol >::ControlHub ( const std::string& aId, const URI& aUri ) :
146 InnerProtocol ( aId , aUri ),
147 mDeviceIPaddress ( 0 ),
148 mDevicePort ( 0 )
149 {
150 std::pair< uint32_t , uint16_t > lPair ( ExtractTargetID ( aUri ) );
151 mDeviceIPaddress = htonl ( lPair.first );
152 mDevicePort = htons ( lPair.second );
153 }
154
155
156 template < typename InnerProtocol >
158 {
159 }
160
161
162 template < typename InnerProtocol >
163 void ControlHub< InnerProtocol >::preamble ( std::shared_ptr< Buffers > aBuffers )
164 {
165 // -------------------------------------------------------------------------------------------------------------
166 // 8 bytes form the preamble:
167 // Device IP address (4 bytes)
168 // Device Port number (2 bytes)
169 // Word-count (2 bytes) will be updated before transmission in predispatch
170 // -------------------------------------------------------------------------------------------------------------
171 // 12 bytes form the preamble reply:
172 // Chunk Byte-count (4 bytes)
173 // Device IP address (4 bytes)
174 // Device Port number (2 bytes)
175 // Error code (2 bytes)
176 // -------------------------------------------------------------------------------------------------------------
177 {
178 std::lock_guard<std::mutex> lPreamblesLock ( mPreamblesMutex );
179 mPreambles.push_back ( tpreamble () );
180 tpreamble* lPreambles = & mPreambles.back();
181 // lPreambles->mSendByteCountPtr = ( uint32_t* ) ( aBuffers->send ( ( uint32_t ) ( 0 ) ) );
182 aBuffers->send ( mDeviceIPaddress );
183 aBuffers->send ( mDevicePort );
184 lPreambles->mSendWordCountPtr = ( uint16_t* ) ( aBuffers->send ( ( uint16_t ) ( 0 ) ) );
185 // aBuffers->receive ( lPreambles->mReplyTotalByteCounter );
186 aBuffers->receive ( lPreambles->mReplyChunkByteCounter );
187 aBuffers->receive ( lPreambles->mReplyDeviceIPaddress );
188 aBuffers->receive ( lPreambles->mReplyDevicePort );
189 aBuffers->receive ( lPreambles->mReplyErrorCode );
190 }
191 InnerProtocol::preamble ( aBuffers );
192 }
193
194
195
196 template < typename InnerProtocol >
198 {
199 return InnerProtocol::getPreambleSize() + 2;
200 }
201
202
203 template < typename InnerProtocol >
204 void ControlHub< InnerProtocol >::predispatch ( std::shared_ptr< Buffers > aBuffers )
205 {
206 InnerProtocol::predispatch ( aBuffers );
207 std::lock_guard<std::mutex> lPreamblesLock ( mPreamblesMutex );
208 tpreamble& lPreambles = mPreambles.back();
209 uint32_t lByteCount ( aBuffers->sendCounter() );
210 * ( lPreambles.mSendWordCountPtr ) = htons ( ( lByteCount-8 ) >>2 );
211 }
212
213
214 template < typename InnerProtocol >
216 uint8_t* aSendBufferEnd ,
217 std::deque< std::pair< uint8_t* , uint32_t > >::iterator aReplyStartIt ,
218 std::deque< std::pair< uint8_t* , uint32_t > >::iterator aReplyEndIt )
219 {
220 aReplyStartIt++;
221 uint32_t lReplyIPaddress ( * ( ( uint32_t* ) ( aReplyStartIt->first ) ) );
222
223 if ( lReplyIPaddress != mDeviceIPaddress )
224 {
225 uhal::exception::ControlHubReturnedWrongAddress* lExc = new uhal::exception::ControlHubReturnedWrongAddress();
226 log ( *lExc , "Returned IP address " , Integer ( lReplyIPaddress , IntFmt< hex , fixed >() ) ,
227 " does not match that sent " , Integer ( mDeviceIPaddress, IntFmt< hex , fixed >() ) ,
228 " for device with URI: " , this->uri() );
229 std::lock_guard<std::mutex> lPreamblesLock ( mPreamblesMutex );
230 mPreambles.pop_front();
231 return lExc;
232 }
233
234 aReplyStartIt++;
235 uint16_t lReplyPort ( * ( ( uint16_t* ) ( aReplyStartIt->first ) ) );
236
237 if ( lReplyPort != mDevicePort )
238 {
239 uhal::exception::ControlHubReturnedWrongAddress* lExc = new uhal::exception::ControlHubReturnedWrongAddress();
240 log ( *lExc , "Returned Port number " , Integer ( lReplyPort ) ,
241 " does not match that sent " , Integer ( mDevicePort ) ,
242 " for device with URI: " , this->uri() );
243 std::lock_guard<std::mutex> lPreamblesLock ( mPreamblesMutex );
244 mPreambles.pop_front();
245 return lExc;
246 }
247
248 aReplyStartIt++;
249 uint16_t lErrorCode ( ntohs ( * ( ( uint16_t* ) ( aReplyStartIt->first ) ) ) );
250
251 if ( lErrorCode != 0 )
252 {
253 std::lock_guard<std::mutex> lPreamblesLock ( mPreamblesMutex );
254 mPreambles.pop_front();
255
256 if ( lErrorCode == 1 || lErrorCode == 3 || lErrorCode == 4 )
257 {
258 uhal::exception::ControlHubTargetTimeout* lExc = new uhal::exception::ControlHubTargetTimeout();
259 log ( *lExc , "The ControlHub did not receive any response from the target with URI ", Quote(this->uri()) );
260 log ( *lExc , "ControlHub returned error code ", Integer ( lErrorCode ), " = '", TranslatedFmt<uint16_t>(lErrorCode, translateErrorCode), "'");
261 return lExc ;
262 }
263
264 uhal::exception::ControlHubErrorCodeSet* lExc = new uhal::exception::ControlHubErrorCodeSet();
265 log ( *lExc , "The ControlHub returned error code " , Integer ( lErrorCode, IntFmt< hex , fixed >() ),
266 " = ", TranslatedFmt<uint16_t>(lErrorCode, translateErrorCode),
267 " for target with URI " , Quote(this->uri()) );
268 return lExc;
269 }
270
271 {
272 std::lock_guard<std::mutex> lPreamblesLock ( mPreamblesMutex );
273 mPreambles.pop_front();
274 }
275 return InnerProtocol::validate ( ( aSendBufferStart+=8 ) , aSendBufferEnd , ( ++aReplyStartIt ) , aReplyEndIt );
276 }
277
278
279 template < typename InnerProtocol >
281 {
282 return 60;
283 }
284
285
286 template < typename InnerProtocol >
288 {
289 {
290 std::lock_guard<std::mutex> lPreamblesLock ( mPreamblesMutex );
291 mPreambles.clear();
292 }
293 InnerProtocol::dispatchExceptionHandler();
294 }
295
296
297 template < typename InnerProtocol >
298 void ControlHub< InnerProtocol >::translateErrorCode(std::ostream& aStream, const uint16_t& aErrorCode)
299 {
300 switch (aErrorCode) {
301 case 0:
302 aStream << "success";
303 break;
304 case 1:
305 aStream << "no reply to control packet";
306 break;
307 case 2:
308 aStream << "internal timeout within ControlHub";
309 break;
310 case 3:
311 aStream << "no reply to status packet";
312 break;
313 case 4:
314 aStream << "no reply to resend request";
315 break;
316 case 5:
317 aStream << "malformed status packet received";
318 break;
319 case 6:
320 aStream << "request uses incorrect protocol version";
321 break;
322 case 7:
323 aStream << "device is not in the ControlHub's allowlist";
324 break;
325 default:
326 aStream << "UNKNOWN";
327 }
328 }
329
330
331 template class ControlHub< IPbus<1, 3> >;
332 template class ControlHub< IPbus<2, 0> >;
333}
334
335
\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