]> git.uio.no Git - u/mrichter/AliRoot.git/blame - EVE/EveHLT/AliEveHOMERManager.cxx
* Added new Handler Classes for HOMER Proxy
[u/mrichter/AliRoot.git] / EVE / EveHLT / AliEveHOMERManager.cxx
CommitLineData
d810d0de 1// $Id$
2// Main authors: Matevz Tadel & Alja Mrak-Tadel: 2006, 2007
fd31e9de 3// Author: Jochen Thaeder <thaeder@kip.uni-heidelberg.de> *
4// for The ALICE HLT Project. *
059c30e4 5
7279ee15 6//-*- Mode: C++ -*-
7
d810d0de 8/** @file AliEveHOMERManager.cxx
059c30e4 9 @author Jochen Thaeder
51346b82 10 @date
059c30e4 11 @brief Manger for HOMER in offline
12*/
13
14#if __GNUC__>= 3
15 using namespace std;
16#endif
17
d810d0de 18#include "AliEveHOMERManager.h"
059c30e4 19
7279ee15 20#define EVE_DEBUG 1
46eadbb4 21// -- -- -- -- -- -- --
059c30e4 22#include "AliHLTHOMERLibManager.h"
059c30e4 23#include "AliHLTHOMERSourceDesc.h"
24#include "AliHLTHOMERBlockDesc.h"
46eadbb4 25// -- -- -- -- -- -- --
d810d0de 26#include "AliEveHOMERSource.h"
46eadbb4 27// -- -- -- -- -- -- --
059c30e4 28#include "TString.h"
29#include <TApplication.h>
30#include "Riostream.h"
31#include "TXMLAttr.h"
32#include "TCollection.h"
33#include "TList.h"
34#include "TObjString.h"
35#include "TObjArray.h"
46eadbb4 36// -- -- -- -- -- -- --
37#include "AliLog.h"
a15e6d7d 38
39//______________________________________________________________________________
40//
41// Manage connections to HLT data-sources.
42
d810d0de 43ClassImp(AliEveHOMERManager)
059c30e4 44
45/*
46 * ---------------------------------------------------------------------------------
51346b82 47 * Constructor / Destructor
48 * ---------------------------------------------------------------------------------
059c30e4 49 */
50
51//##################################################################################
d810d0de 52AliEveHOMERManager::AliEveHOMERManager( TString xmlFile ) :
53 TEveElementList("AliEveHOMERManager"),
059c30e4 54 fLibManager(new AliHLTHOMERLibManager),
7279ee15 55 fXMLHandler( new AliEveHOMERXMLHandler( xmlFile ) ),
059c30e4 56 fSourceList(NULL),
57 fReader(NULL),
58 fBlockList(NULL),
59 fNBlks(0),
60 fEventID(0),
61 fCurrentBlk(0),
62 fConnected(kFALSE),
6612c5b3 63 fStateHasChanged(kTRUE),
06272c83 64 fSrcList(NULL) {
46eadbb4 65 // This Class should handle the communication
66 // from the HLT to AliEVE. The HLT sends data via
67 // the HOMER interface on several TCP ports of nodes
68 // in the CERN GPN and DCS network.
69 // All this communication is hidden from the user.
70 //
71 // Right now, a xml file ( SCC1 ) is used to get the
72 // configuration, this will/ has to change to a proxy
73 // running on dedicated nodes.
7279ee15 74
059c30e4 75}
76
059c30e4 77//##################################################################################
d810d0de 78AliEveHOMERManager::~AliEveHOMERManager() {
46eadbb4 79 // The destructor
059c30e4 80
81 if ( fLibManager ) {
82 if ( fReader )
83 fLibManager->DeleteReader(fReader);
84 delete fLibManager;
85 fLibManager = NULL;
86 fReader = NULL;
87 }
88
7279ee15 89 if ( fXMLHandler != NULL )
90 delete fXMLHandler;
91 fXMLHandler = NULL;
059c30e4 92
93 if ( fSourceList != NULL )
94 delete fSourceList;
95 fSourceList = NULL;
96
97 if ( fBlockList != NULL )
98 delete fBlockList;
99 fBlockList = NULL;
6612c5b3 100
46eadbb4 101 if ( fSrcList != NULL )
102 delete fSrcList;
103 fSrcList = NULL;
059c30e4 104}
105
106/*
107 * ---------------------------------------------------------------------------------
7279ee15 108 * Source Handling
51346b82 109 * ---------------------------------------------------------------------------------
059c30e4 110 */
111
112//##################################################################################
d810d0de 113Int_t AliEveHOMERManager::CreateHOMERSourcesList() {
46eadbb4 114 // Create Sources List from HOMER-Proxy
059c30e4 115
7279ee15 116 Int_t iResult = 0;
059c30e4 117
118 // -- Initialize sources list
119 DestroyElements();
120 if ( fSourceList != NULL )
121 delete fSourceList;
122 fSourceList = NULL;
51346b82 123
059c30e4 124 fSourceList = new TList();
125 fSourceList->SetOwner( kTRUE );
126
7279ee15 127 iResult = fXMLHandler->FillSourceList( fSourceList );
059c30e4 128
059c30e4 129 if ( iResult ) {
130 AliWarning( Form("There have been errors, while creating the sources list.") );
131 }
132 else {
133 AliInfo( Form("New sources list created.") );
7279ee15 134
135 // -- New SourceList has been created --> All Sources are new --> State has changed
136 fStateHasChanged = kTRUE;
46eadbb4 137
138 if ( fSrcList )
139 delete fSrcList;
140
141 // -- Create new AliEVE sources list
7279ee15 142 fSrcList = new AliEveHOMERSourceList("HLT Sources");
46eadbb4 143 fSrcList->SetManager(this);
144
145 AddElement(fSrcList);
146 fSrcList->CreateByType();
059c30e4 147 }
148
149 return iResult;
150}
151
152//##################################################################################
d810d0de 153void AliEveHOMERManager::SetSourceState( AliHLTHOMERSourceDesc * source, Bool_t state ) {
46eadbb4 154 // Set state of a source
155 // * param source Pointer to AliHLTHOMERSourceDesc object.
156 // * param state New (selected/not selected) state.
157
059c30e4 158 if ( source->IsSelected() != state ) {
159 source->SetState( state );
160 fStateHasChanged = kTRUE;
161 }
162
163 return;
164}
165
059c30e4 166/*
167 * ---------------------------------------------------------------------------------
168 * Connection Handling
51346b82 169 * ---------------------------------------------------------------------------------
059c30e4 170 */
171
172//##################################################################################
d810d0de 173Int_t AliEveHOMERManager::ConnectHOMER(){
46eadbb4 174 // Connect to HOMER sources, out of Readout List, which gets created when state has changed
175 // * return 0 on sucess, "HOMER" errors on error
176
51346b82 177 Int_t iResult = 0;
059c30e4 178
46eadbb4 179 fStateHasChanged = fSrcList->GetSelectedSources();
180
059c30e4 181 // -- Check if already connected and state has not changed
182 if ( fStateHasChanged == kFALSE && IsConnected() ) {
183 AliInfo( Form("No need for reconnection.") );
184 return iResult;
185 }
51346b82 186
059c30e4 187 // -- If already connected, disconnect before connect
51346b82 188 if ( IsConnected() )
059c30e4 189 DisconnectHOMER();
190
191 // *** Create the Readoutlist
192
193 UShort_t* sourcePorts = new UShort_t [fSourceList->GetEntries()];
194 const char ** sourceHostnames = new const char* [fSourceList->GetEntries()];
195 UInt_t sourceCount = 0;
196
197 CreateReadoutList( sourceHostnames, sourcePorts, sourceCount );
51346b82 198
059c30e4 199 if ( sourceCount == 0 ) {
200 AliError(Form("No sources selected, aborting.") );
201 return iResult;
202 }
203
204 // *** Connect to data sources
51346b82 205
059c30e4 206 if ( !fReader ) {
51346b82 207 if ( fLibManager )
059c30e4 208 fReader = fLibManager->OpenReader( sourceCount, sourceHostnames, sourcePorts );
209 }
51346b82 210
059c30e4 211 iResult = fReader->GetConnectionStatus();
51346b82 212
213 if ( iResult ) {
214 // -- Connection failed
215
059c30e4 216 UInt_t ndx = fReader->GetErrorConnectionNdx();
51346b82 217
059c30e4 218 if ( ndx < sourceCount ) {
51346b82 219 AliError( Form("Error : Error establishing connection to TCP source %s:%hu: %s (%d)",
059c30e4 220 sourceHostnames[ndx], sourcePorts[ndx], strerror(iResult), iResult) );
221 }
222 else {
223 AliError( Form("Error : Error establishing connection to unknown source with index %d: %s (%d)",
224 ndx, strerror(iResult), iResult) );
225 }
226
227 if ( fReader )
228 fLibManager->DeleteReader( fReader );
229 fReader = NULL;
230
51346b82 231 }
059c30e4 232 else {
233 // -- Connection ok - set reader
51346b82 234 fConnected = kTRUE;
235
059c30e4 236 AliInfo( Form("Connection established") );
237 }
51346b82 238
059c30e4 239 delete[] sourceHostnames;
240 delete[] sourcePorts;
51346b82 241
059c30e4 242 return iResult;
243}
244
245//##################################################################################
d810d0de 246void AliEveHOMERManager::DisconnectHOMER(){
46eadbb4 247 // Disconnect from HOMER sources
059c30e4 248
249 if ( ! IsConnected() )
250 return;
251
252 if ( fReader )
253 fLibManager->DeleteReader( fReader );
254 fReader = NULL;
255
256 fStateHasChanged = kTRUE;
257 fConnected = kFALSE;
51346b82 258
059c30e4 259 AliInfo( Form("Connection closed") );
260
261 return;
262}
263
264//##################################################################################
d810d0de 265Int_t AliEveHOMERManager::ReconnectHOMER(){
46eadbb4 266 // Reconnect from HOMER sources
267 // * return 0 on sucess, "ConnectHOMER()" errors on error
059c30e4 268
269 Int_t iResult = 0;
270
271 if ( IsConnected() )
272 DisconnectHOMER();
273
274 iResult = ConnectHOMER();
275 if ( iResult ) {
276 AliError( Form("Error connecting.") );
277 }
278
279 return iResult;
280}
281
059c30e4 282//##################################################################################
d810d0de 283void AliEveHOMERManager::CreateReadoutList( const char** sourceHostnames, UShort_t *sourcePorts, UInt_t &sourceCount ){
46eadbb4 284 // Create a readout list for Hostname and ports
285 // * param socurceHostnames Array of selected hostnames
286 // * param socurcePorts Array of selected ports
287 // * param socurceCount Number of selected hostname:port
059c30e4 288
289 AliHLTHOMERSourceDesc * source= NULL;
290
291 // -- Read all sources and check if they should be read out
292 TIter next( fSourceList );
293 while ( ( source = (AliHLTHOMERSourceDesc*)next() ) ) {
294
51346b82 295 if ( ! source->IsSelected() )
059c30e4 296 continue;
51346b82 297
059c30e4 298 Bool_t exists = kFALSE;
299
300 // -- Loop over existing entries and check if entry is already in readout list
301 for ( UInt_t ii = 0; ii < sourceCount; ii++ ){
302 if ( !strcmp( sourceHostnames[ii], source->GetHostname().Data() ) && sourcePorts[ii] == source->GetPort() ) {
303 exists = kTRUE;
304 break;
305 }
306 }
51346b82 307
059c30e4 308 // -- Add new entires to readout list
309 if ( ! exists ) {
310 sourcePorts[sourceCount] = source->GetPort();
311 sourceHostnames[sourceCount] = source->GetHostname().Data();
312 sourceCount++;
313 }
314
315 } // while ( ( source = (AliHLTHOMERSourceDesc*)next() ) ) {
316
317 fStateHasChanged = kFALSE;
318
319 return;
320}
321
322/*
323 * ---------------------------------------------------------------------------------
16718cdc 324 * Event Handling
51346b82 325 * ---------------------------------------------------------------------------------
059c30e4 326 */
327
328//##################################################################################
d810d0de 329Int_t AliEveHOMERManager::NextEvent(){
46eadbb4 330 // Loads the next Event, after being connected
331 // * return 0 on sucess, "HOMER" errors on error
51346b82 332
059c30e4 333 Int_t iResult = 0;
7279ee15 334 Int_t iRetryCount = 0;
51346b82 335
059c30e4 336 if ( !fReader || ! IsConnected() ) {
337 AliWarning( Form( "Not connected yet." ) );
338 return 1;
339 }
51346b82 340
7279ee15 341 // fReader->SetEventRequestAdvanceTime( 20000000 /*timeout in us*/ );
342
059c30e4 343 // -- Read next event data and error handling for HOMER (error codes and empty blocks)
344 while( 1 ) {
7279ee15 345
346 iResult = fReader->ReadNextEvent( 40000000 /*timeout in us*/);
51346b82 347
059c30e4 348 if ( iResult == 111 || iResult == 32 || iResult == 6 ) {
349 Int_t ndx = fReader->GetErrorConnectionNdx();
46eadbb4 350 AliError( Form("Error, No Connection to source %d: %s (%d)",
351 ndx, strerror(iResult), iResult) );
46eadbb4 352 return 2;
059c30e4 353 }
354 else if ( iResult == 110 ) {
355 Int_t ndx = fReader->GetErrorConnectionNdx();
46eadbb4 356 AliError( Form("Timout occured, reading event from source %d: %s (%d)",
357 ndx, strerror(iResult), iResult) );
51346b82 358 return 3;
059c30e4 359 }
360 else if ( iResult == 56) {
361 Int_t ndx = fReader->GetErrorConnectionNdx();
7279ee15 362
363 ++iRetryCount;
364
365 if ( iRetryCount >= 20 ) {
366 AliError( Form("Retry Failed: Error reading event from source %d: %s (%d)",
367 ndx, strerror(iResult), iResult) );
368 return 4;
369 }
370 else {
371 AliError( Form("Retry: Error reading event from source %d: %s (%d)",
372 ndx, strerror(iResult), iResult) );
373 continue;
374 }
059c30e4 375 }
376 else if ( iResult ) {
377 Int_t ndx = fReader->GetErrorConnectionNdx();
46eadbb4 378 AliError( Form("General Error reading event from source %d: %s (%d)",
379 ndx, strerror(iResult), iResult) );
380 fConnected = kFALSE;
059c30e4 381 return 2;
382 }
383 else {
384 break;
385 }
386 } // while( 1 ) {
387
51346b82 388 if ( iResult )
059c30e4 389 return iResult;
46eadbb4 390
059c30e4 391 // -- Get blockCnt and eventID
392 fNBlks = (ULong_t) fReader->GetBlockCnt();
393 fEventID = (ULong64_t) fReader->GetEventID();
394 fCurrentBlk = 0;
395
16718cdc 396 AliInfo( Form("Event 0x%016LX (%Lu) with %lu blocks", fEventID, fEventID, fNBlks) );
059c30e4 397
7279ee15 398#if EVE_DEBUG
059c30e4 399 // Loop for Debug only
400 for ( ULong_t i = 0; i < fNBlks; i++ ) {
401 Char_t tmp1[9], tmp2[5];
402 memset( tmp1, 0, 9 );
403 memset( tmp2, 0, 5 );
404 void *tmp11 = tmp1;
405 ULong64_t* tmp12 = (ULong64_t*)tmp11;
406 *tmp12 = fReader->GetBlockDataType( i );
407 void *tmp21 = tmp2;
408 ULong_t* tmp22 = (ULong_t*)tmp21;
409 *tmp22 = fReader->GetBlockDataOrigin( i );
46eadbb4 410 AliInfo( Form("Block %lu length: %lu - type: %s - origin: %s",
411 i, fReader->GetBlockDataLength( i ), tmp1, tmp2) );
059c30e4 412 } // end for ( ULong_t i = 0; i < fNBlks; i++ ) {
059c30e4 413#endif
414
415 // -- Create BlockList
46eadbb4 416 AliInfo( Form("Create Block List") );
417 iResult = CreateBlockList();
059c30e4 418
a15e6d7d 419 return iResult;
059c30e4 420}
421
422//##################################################################################
d810d0de 423Int_t AliEveHOMERManager::CreateBlockList() {
46eadbb4 424 // Create a TList of blocks, which have been readout
059c30e4 425
059c30e4 426
06272c83 427 Int_t iResult = 0;
428#if 0
059c30e4 429 // -- Initialize block list
430 if ( fBlockList != NULL )
431 delete fBlockList;
432 fBlockList = NULL;
433
434 fBlockList = new TList();
435 fBlockList->SetOwner( kTRUE );
436
437 void* iter = GetFirstBlk();
438
439 // -- Fill block list
440 while ( iter != NULL ){
51346b82 441
059c30e4 442 // -- Create new block
51346b82 443 AliHLTHOMERBlockDesc * block = new AliHLTHOMERBlockDesc( GetBlk(), GetBlkSize(), GetBlkOrigin(),
059c30e4 444 GetBlkType(), GetBlkSpecification() );
51346b82 445
059c30e4 446 // -- Check sources list if block is requested
447 if ( CheckIfRequested( block ) )
51346b82 448 fBlockList->Add( block );
46eadbb4 449 else {
7279ee15 450 //The Following 2 line commented out and the previous is added.
451 // delete block;
452 // block = NULL;
453 fBlockList->Add( block );
46eadbb4 454 }
059c30e4 455 iter = GetNextBlk();
51346b82 456
059c30e4 457 } // while ( iter != NULL ){
06272c83 458#endif
059c30e4 459 return iResult;
460}
461
462/*
463 * ---------------------------------------------------------------------------------
464 * BlockHandling
51346b82 465 * ---------------------------------------------------------------------------------
059c30e4 466 */
467
468//##################################################################################
d810d0de 469void* AliEveHOMERManager::GetBlk( Int_t ndx ) {
46eadbb4 470 // Get pointer to current block in current event
471 // * param ndx Block index
472 // * return returns pointer to blk, NULL if no block present
473
059c30e4 474 void* data = NULL;
51346b82 475
059c30e4 476 if ( !fReader || ! IsConnected() ) {
51346b82 477 AliError( Form("Not connected yet.") );
059c30e4 478 }
479 else {
51346b82 480 if ( ( ndx ) < (Int_t) fNBlks )
059c30e4 481 data = (void*) fReader->GetBlockData( ndx );
482 }
483
484 return data;
485}
486
487//##################################################################################
d810d0de 488ULong_t AliEveHOMERManager::GetBlkSize( Int_t ndx ) {
46eadbb4 489 // Get size of block ndx
490 // * param ndx Block index
491 // * return returns pointer to blk, 0 if no block present
492
059c30e4 493 ULong_t length = 0;
494
495 if ( !fReader || ! IsConnected() ) {
51346b82 496 AliError( Form("Not connected yet.") );
059c30e4 497 }
498 else {
51346b82 499 if ( ( ndx ) < (Int_t) fNBlks )
059c30e4 500 length = (ULong_t) fReader->GetBlockDataLength( ndx );
501 }
502
503 return length;
504}
505
506//##################################################################################
d810d0de 507TString AliEveHOMERManager::GetBlkOrigin( Int_t ndx ) {
46eadbb4 508 // Get origin of block ndx
509 // * param ndx Block index
510 // * return origin of block
059c30e4 511
512 TString origin = "";
513
514 // -- Check for Connection
515 if ( !fReader || ! IsConnected() ) {
51346b82 516 AliError( Form("Not connected yet.") );
059c30e4 517 return origin;
518 }
519
520 // -- Check block index
521 if ( ( ndx ) >= (Int_t) fNBlks ) {
51346b82 522 AliError( Form("Block index %d out of range.", ndx ) );
523 return origin;
059c30e4 524 }
525
526 // -- Get origin
527 union{
528 UInt_t data;
529 Char_t array[4];
530 } reverseOrigin;
531
532 reverseOrigin.data = (UInt_t) fReader->GetBlockDataOrigin( ndx );
533
534 // -- Reverse the order
535 for (Int_t ii = 3; ii >= 0; ii-- )
536 if ( reverseOrigin.array[ii] != ' ')
537 origin.Append( reverseOrigin.array[ii] );
538
539 return origin;
540}
541
542//##################################################################################
7279ee15 543TString AliEveHOMERManager::GetBlkType( Int_t ndx ) {
46eadbb4 544 // Get type of block ndx
545 // * param ndx Block index
546 // * return type of block
059c30e4 547
548 TString type = "";
549
550 // -- Check for Connection
551 if ( !fReader || ! IsConnected() ) {
51346b82 552 AliError( Form("Not connected yet.") );
059c30e4 553 return type;
554 }
555
556 // -- Check blockk index
557 if ( ( ndx ) >= (Int_t) fNBlks ) {
51346b82 558 AliError( Form("Block index %d out of range.", ndx ) );
559 return type;
059c30e4 560 }
561
562 // -- Get type
563 union{
564 ULong64_t data;
565 Char_t array[8];
566 } reverseType;
567
568 reverseType.data = (ULong64_t) fReader->GetBlockDataType( ndx );
569
570 // -- Reverse the order
571 for (Int_t ii = 7; ii >= 0; ii-- )
572 if ( reverseType.array[ii] != ' ')
573 type.Append( reverseType.array[ii] );
574
575 return type;
576}
577
059c30e4 578//##################################################################################
7279ee15 579ULong_t AliEveHOMERManager::GetBlkSpecification( Int_t ndx ) {
46eadbb4 580 // Get specification of block ndx
581 // * param ndx Block index
582 // * return specification of block
059c30e4 583
584 ULong_t spec = 0;
585
059c30e4 586 // -- Check for Connection
587 if ( !fReader || ! IsConnected() ) {
51346b82 588 AliError( Form("Not connected yet.") );
059c30e4 589 return spec;
590 }
591
592 // -- Check blockk index
593 if ( ( ndx ) >= (Int_t) fNBlks ) {
51346b82 594 AliError( Form("Block index %d out of range.", ndx ) );
595 return spec;
059c30e4 596 }
51346b82 597
059c30e4 598 spec = (ULong_t) fReader->GetBlockDataSpec( ndx );
599
600 return spec;
601}
602
603//##################################################################################
06272c83 604Bool_t AliEveHOMERManager::CheckIfRequested( AliHLTHOMERBlockDesc */* block*/ ) {
46eadbb4 605 // Checks if current Block should was requested
606 // * return returns kTRUE, if block should was requested
059c30e4 607
608 Bool_t requested = kFALSE;
06272c83 609#if 0
059c30e4 610 AliHLTHOMERSourceDesc * source= NULL;
51346b82 611
059c30e4 612 // -- Read all sources and check if they should be read out
613 TIter next( fSourceList );
614 while ( ( source = (AliHLTHOMERSourceDesc*)next() ) ) {
7279ee15 615
51346b82 616 if ( ! source->IsSelected() )
059c30e4 617 continue;
618
7279ee15 619 if ( !( block->GetDetector().CompareTo( "*** " ) && block->GetDetector().CompareTo( "***" ) ) ) {
620 // if not any detector
621 if ( source->GetDetector().CompareTo( block->GetDetector() ) )
622 continue;
623 }
059c30e4 624
7279ee15 625 if ( ! ( block->GetDataType().CompareTo( "******* " ) && block->GetDataType().CompareTo( "******* " ) ) ) {
626 if ( source->GetDataType().CompareTo( block->GetDataType() ) )
627 continue;
628 }
059c30e4 629
630 if ( ! block->HasSubDetectorRange() ) {
6612c5b3 631 if ( source->GetSubDetector().Atoi() != block->GetSubDetector().Atoi() )
059c30e4 632 continue;
51346b82 633
059c30e4 634 if ( ! block->HasSubSubDetectorRange() ) {
51346b82 635
46eadbb4 636 if ( source->GetSubSubDetector().Atoi() != block->GetSubSubDetector().Atoi() )
637 continue;
51346b82 638
059c30e4 639 } // if ( ! block->HasSubSubDetectorRange ) {
640 } // if ( ! block->HasSubDetectorRange ) {
51346b82 641
059c30e4 642 requested = kTRUE;
7279ee15 643 break;
51346b82 644
059c30e4 645 } // while ( ( source = (AliHLTHOMERSourceDesc*)next() ) ) {
7279ee15 646
647#if EVE_DEBUG
51346b82 648
7279ee15 649 if ( block->GetDataType().CompareTo("CLUSTERS") ) {
650 if ( requested ) {
651 AliError( Form("Block requested : %s - %s : %s/%s -> %s ", block->GetDetector().Data(), block->GetDataType().Data(),
652 block->GetSubDetector().Data(), block->GetSubSubDetector().Data(), block->GetClassName().Data() ) );
059c30e4 653 }
7279ee15 654 else {
655 AliError( Form("Block NOT requested : %s - %s : %s/%s -> %s ", block->GetDetector().Data(), block->GetDataType().Data(),
656 block->GetSubDetector().Data(), block->GetSubSubDetector().Data(), block->GetClassName().Data() ) );
657 }
658
659 }
660#endif
06272c83 661#endif
059c30e4 662 return requested;
663}
664