μHAL (v2.6.5)
Part of the IPbus software repository
ConnectionManager.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 #include "uhal/Node.hpp"
36 #include "uhal/NodeTreeBuilder.hpp"
37 #include "uhal/ClientInterface.hpp"
38 #include "uhal/ClientFactory.hpp"
39 #include "uhal/utilities/files.hpp"
40 #include "uhal/utilities/xml.hpp"
41 
42 #include "uhal/log/log.hpp"
43 
44 #include <boost/regex.hpp>
45 #include <boost/spirit/include/qi.hpp>
46 
47 
48 #if BOOST_VERSION >= 106000
49 // Resolve boost bind placeholders (_1, _2, ...) that moved within boost::laceholders namespace from v1.60
50 using boost::placeholders::_1;
51 using boost::placeholders::_2;
52 using boost::placeholders::_3;
53 #endif
54 
55 
56 namespace uhal
57 {
58 
59 
60  ConnectionManager::ConnectionDescriptor::ConnectionDescriptor ( const pugi::xml_node& aNode , const boost::filesystem::path& aConnectionFile , bool& aSuccess ) :
61  connection_file ( aConnectionFile )
62  {
63  aSuccess=false;
64 
65  if ( ! uhal::utilities::GetXMLattribute<true> ( aNode , "id" , id ) )
66  {
67  return;
68  }
69 
70  if ( ! uhal::utilities::GetXMLattribute<true> ( aNode , "uri" , uri ) )
71  {
72  return;
73  }
74 
75  if ( ! uhal::utilities::GetXMLattribute<true> ( aNode , "address_table" , address_table ) )
76  {
77  return;
78  }
79 
80  aSuccess=true;
81  }
82 
84  {
85  if ( id != aConnectionDescriptor.id )
86  {
87  return false;
88  }
89 
90  if ( uri != aConnectionDescriptor.uri )
91  {
92  return false;
93  }
94 
95  if ( address_table != aConnectionDescriptor.address_table )
96  {
97  return false;
98  }
99 
100  /*
101  if ( connection_file != aConnectionDescriptor.connection_file )
102  {
103  return false;
104  }
105  */
106  return true;
107  }
108 
109 
110 
111 
112 
113  // Given a glob expression, parse all the files matching it (e.g. $BUILD/config/*.xml). If one parsing fails throw an exception and return filename and line number
114 
115  ConnectionManager::ConnectionManager ( const std::string& aFilenameExpr )
116  {
117  //Mutex lock here to be on the safe side
118  boost::lock_guard<boost::mutex> lLock ( mMutex );
119  std::vector< std::pair<std::string, std::string> > lConnectionFiles; //protocol, filename
120  uhal::utilities::ParseSemicolonDelimitedUriList ( aFilenameExpr , lConnectionFiles );
121 
122  for ( std::vector< std::pair<std::string, std::string> >::iterator lIt = lConnectionFiles.begin() ; lIt != lConnectionFiles.end() ; ++lIt )
123  {
124  uhal::utilities::OpenFile ( lIt->first , lIt->second , boost::filesystem::current_path() , boost::bind ( &ConnectionManager::CallBack, boost::ref ( *this ) , _1 , _2 , _3 ) );
125  }
126  }
127 
128 
130  {
131  }
132 
133 
134  /*
135  Retrieves protocol, host, and port from the connection file to create the ClientInterface.
136  Retrieves the address table file from the connection file to create the HwInterface.
137  */
138  HwInterface ConnectionManager::getDevice ( const std::string& aId )
139  {
140  //We need a mutex lock here to protect access to the TodeTreeBuilder and the ClientFactory
141  boost::lock_guard<boost::mutex> lLock ( mMutex );
142 
143  if ( mConnectionDescriptors.size() == 0 )
144  {
145  exception::ConnectionUIDDoesNotExist lExc;
146  log ( lExc , "Connection map contains no entries" );
147  throw lExc;
148  }
149 
150  std::map< std::string, ConnectionDescriptor >::iterator lIt = mConnectionDescriptors.find ( aId );
151 
152  if ( lIt == mConnectionDescriptors.end() )
153  {
154  exception::ConnectionUIDDoesNotExist lExc;
155  log ( lExc , "Device ID , " , Quote ( aId ) , ", does not exist in connection map" );
156  throw lExc;
157  }
158 
159  //The node tree builder returns a newly created Node which we can safely wrap as a shared_ptr
160  boost::shared_ptr< Node > lNode ( NodeTreeBuilder::getInstance().getNodeTree ( lIt->second.address_table , lIt->second.connection_file ) );
161  log ( Info() , "ConnectionManager created node tree: " , *lNode );
162  boost::shared_ptr<ClientInterface> lClientInterface ( ClientFactory::getInstance().getClient ( lIt->second.id , lIt->second.uri ) );
163  return HwInterface ( lClientInterface , lNode );
164  }
165 
166  //Static method for building device on the fly
167  HwInterface ConnectionManager::getDevice ( const std::string& aId , const std::string& aUri , const std::string& aAddressFileExpr )
168  {
169  //We need a mutex lock here to protect access to the TodeTreeBuilder and the ClientFactory
170  boost::lock_guard<boost::mutex> lLock ( mMutex );
171  boost::shared_ptr< Node > lNode ( NodeTreeBuilder::getInstance().getNodeTree ( aAddressFileExpr , boost::filesystem::current_path() / "." ) );
172  log ( Info() , "ConnectionManager created node tree: " , *lNode );
173  boost::shared_ptr<ClientInterface> lClientInterface ( ClientFactory::getInstance().getClient ( aId , aUri ) );
174  return HwInterface ( lClientInterface , lNode );
175  }
176 
177 
178  //Given a regex return the ids that match the
179  std::vector<std::string> ConnectionManager::getDevices ( ) const
180  {
181  std::vector<std::string> lDevices;
182  lDevices.reserve ( mConnectionDescriptors.size() ); //prevent reallocations
183 
184  for ( std::map< std::string, ConnectionDescriptor >::const_iterator lIt = mConnectionDescriptors.begin() ; lIt != mConnectionDescriptors.end() ; ++lIt )
185  {
186  lDevices.push_back ( lIt->first );
187  }
188 
189  return lDevices;
190  }
191 
192 
193  std::vector<std::string> ConnectionManager::getDevices ( const std::string& aRegex ) const
194  {
195  std::vector<std::string> lDevices;
196  lDevices.reserve ( mConnectionDescriptors.size() ); //prevent reallocations
197 
198  for ( std::map< std::string, ConnectionDescriptor >::const_iterator lIt = mConnectionDescriptors.begin() ; lIt != mConnectionDescriptors.end() ; ++lIt )
199  {
200  boost::cmatch lMatch;
201 
202  if ( boost::regex_match ( lIt->first.c_str() , lMatch , boost::regex ( aRegex ) ) ) //to allow partial match, add boost::match_default|boost::match_partial as fourth argument
203  {
204  lDevices.push_back ( lIt->first );
205  }
206  }
207 
208  return lDevices;
209  }
210 
211 
213  {
214  // Need a mutex lock here to protect access to NodeTreeBuilder
215  boost::lock_guard<boost::mutex> lLock ( mMutex );
216  log( Info(), "ConnectionManager is clearing the address filename -> Node tree cache");
218  }
219 
220 
221 
222  void ConnectionManager::CallBack ( const std::string& aProtocol , const boost::filesystem::path& aPath , std::vector<uint8_t>& aFile )
223  {
224  std::pair< std::set< std::string >::iterator , bool > lInsert = mPreviouslyOpenedFiles.insert ( aProtocol+ ( aPath.string() ) );
225 
226  if ( ! lInsert.second )
227  {
228  log ( Info() , "File " , Quote ( aProtocol+ ( aPath.string() ) ) , " has already been parsed. I am not reparsing and will continue with next document for now but be aware!" );
229  return;
230  }
231 
232  pugi::xml_document lXmlDocument;
233  pugi::xml_parse_result lLoadResult = lXmlDocument.load_buffer_inplace ( & ( aFile[0] ) , aFile.size() );
234 
235  if ( !lLoadResult )
236  {
237  //Mark says to throw on this condition, I will leave it continuing for now...
238  uhal::utilities::PugiXMLParseResultPrettifier ( lLoadResult , aPath , aFile );
239  return;
240  }
241 
242  pugi::xpath_node_set lConnections = lXmlDocument.select_nodes ( "/connections/connection" );
243 
244  for ( pugi::xpath_node_set::const_iterator lConnectionIt = lConnections.begin(); lConnectionIt != lConnections.end(); ++lConnectionIt )
245  {
246  bool lSuccess;
247  ConnectionDescriptor lDescriptor ( lConnectionIt->node() , aPath , lSuccess );
248 
249  if ( lSuccess )
250  {
251  std::pair< std::map< std::string, ConnectionDescriptor >::iterator , bool > lInsert = mConnectionDescriptors.insert ( std::make_pair ( lDescriptor.id , lDescriptor ) );
252 
253  if ( !lInsert.second )
254  {
255  if ( lInsert.first->second == lDescriptor )
256  {
257  log ( Info() , "Duplicate connection entry found:"
258  "\n > id = " , lDescriptor.id ,
259  "\n > uri = " , lDescriptor.uri ,
260  "\n > address_table = " , lDescriptor.address_table ,
261  "\n Continuing for now but be aware!" );
262  }
263  else
264  {
265  exception::DuplicatedUID lExc;
266  log ( lExc , "Duplicate connection ID " , Quote ( lDescriptor.id ) , " found in connections file " ,
267  Quote ( aProtocol+ ( aPath.string() ) ) , " but parameters do not match! Bailing!" );
268  throw lExc;
269  }
270  }
271  }
272  else
273  {
274  log ( Error() , "Construction of Connection Descriptor failed. Continuing with next Connection Descriptor for now but be aware!" );
275  }
276  }
277  }
278 
279 
280  boost::mutex ConnectionManager::mMutex;
281 
282 }
283 
284 
285 
void ParseSemicolonDelimitedUriList(const std::string &aSemicolonDelimitedUriList, std::vector< std::pair< std::string, std::string > > &aUriList)
Parse a semicolon delimited list of URIs into a vector of protocol/address pairs. ...
Definition: files.cpp:46
std::map< std::string, ConnectionDescriptor > mConnectionDescriptors
A map of connection identifiers to stucts containing details of the parsed XML node.
A class which bundles a node tree and an IPbus client interface together providing everything you nee...
Definition: HwInterface.hpp:59
static ClientFactory & getInstance()
Static method to retrieve the single instance of the class.
virtual ~ConnectionManager()
Destructor.
ConnectionManager(const std::string &aFilenameExpr)
Default constructor Given a semi-colon delimeted list of glob expressions, parse all the files matchi...
std::string address_table
The address table for building the node tree.
static boost::mutex mMutex
A mutex lock to protect access to the factory methods in multithreaded environments.
void clearAddressFileCache()
Clears address filename -> Node tree cache. NOT thread safe; for tread-safety, use ConnectionManager ...
HwInterface getDevice(const std::string &aId)
Retrieves protocol, host, and port from the connection file to create an IPbus Client Retrieves the a...
template bool GetXMLattribute< true >(const pugi::xml_node &aNode, const char *aAttrName, std::string &aTarget)
std::string uri
The full uri for making the connection.
ErrorLevel Error
Definition: LogLevels.cpp:61
void PugiXMLParseResultPrettifier(const pugi::xml_parse_result &aLoadResult, const boost::filesystem::path &aPath, const std::vector< uint8_t > &aFile)
Helper function to make debugging failures when parsing XML files easier.
Definition: xml.cpp:57
_Quote< T > Quote(const T &aT)
std::set< std::string > mPreviouslyOpenedFiles
A set of previously opened filenames, so that the same file is not parsed multiple times...
ConnectionDescriptor(const pugi::xml_node &aNode, const boost::filesystem::path &aConnectionFile, bool &aSuccess)
Constructor.
static NodeTreeBuilder & getInstance()
Static method to retrieve the single instance of the class.
A struct to hold the fields of each entry in the XML connections file.
std::string id
An identifier for an individual.
std::vector< std::string > getDevices() const
Return all device IDs known to this connection manager.
bool operator==(const ConnectionDescriptor &aConnectionDescriptor) const
Comparison operation.
void OpenFile(const std::string &aProtocol, const std::string &aFilenameExpr, const boost::filesystem::path &aParentPath, boost::_bi::bind_t< R, F, L > aBinder)
Given a protocol and either a URL or a linux shell expression, open the file and call the callback fu...
Definition: files.hpp:359
static void clearAddressFileCache()
Clears cache of Node tree structure for previously-opened address files (thread safe) ...
InfoLevel Info
Definition: LogLevels.cpp:114
void CallBack(const std::string &aProtocol, const boost::filesystem::path &aPath, std::vector< uint8_t > &aFile)
Method called once the file specified in the constructor has been opened.