μHAL (v2.8.17)
Part of the IPbus software repository
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
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
36#include <mutex>
37
38#include <boost/filesystem/operations.hpp>
39#include <boost/regex.hpp>
40#include <boost/spirit/include/qi.hpp>
41
42#include "uhal/Node.hpp"
48
49#include "uhal/log/log.hpp"
50
51
52// Resolve std bind placeholders (_1, _2, ...)
53namespace arg = std::placeholders;
54
55
56namespace uhal
57{
58
59 ConnectionManager::ConnectionDescriptor::ConnectionDescriptor ( const pugi::xml_node& aNode , const boost::filesystem::path& aConnectionFile , bool& aSuccess ) :
60 connection_file ( aConnectionFile )
61 {
62 aSuccess=false;
63
64 if ( ! uhal::utilities::GetXMLattribute<true> ( aNode , "id" , id ) )
65 {
66 return;
67 }
68
69 if ( ! uhal::utilities::GetXMLattribute<true> ( aNode , "uri" , uri ) )
70 {
71 return;
72 }
73
74 if ( ! uhal::utilities::GetXMLattribute<true> ( aNode , "address_table" , address_table ) )
75 {
76 return;
77 }
78
79 aSuccess=true;
80 }
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 return true;
101 }
102
103
104
105
106 ConnectionManager::ConnectionManager ( const std::string& aFilenameExpr )
107 {
108 //Mutex lock here to be on the safe side
109 std::lock_guard<std::mutex> lLock ( mMutex );
110 std::vector< std::pair<std::string, std::string> > lConnectionFiles; //protocol, filename
111 uhal::utilities::ParseSemicolonDelimitedUriList ( aFilenameExpr , lConnectionFiles );
112
113 for (const auto& x: lConnectionFiles)
114 uhal::utilities::OpenFile ( x.first , x.second , boost::filesystem::current_path() , std::bind ( &ConnectionManager::CallBack, this , arg::_1 , arg::_2 , arg::_3 ) );
115 }
116
117
118 ConnectionManager::ConnectionManager ( const std::string& aFilenameExpr , const std::vector<std::string>& aUserClientActivationList ) :
119 mUserClientActivationList(aUserClientActivationList)
120 {
121 //Mutex lock here to be on the safe side
122 std::lock_guard<std::mutex> lLock ( mMutex );
123 std::vector< std::pair<std::string, std::string> > lConnectionFiles; //protocol, filename
124 uhal::utilities::ParseSemicolonDelimitedUriList ( aFilenameExpr , lConnectionFiles );
125
126 for (const auto& x: lConnectionFiles)
127 uhal::utilities::OpenFile ( x.first , x.second , boost::filesystem::current_path() , std::bind ( &ConnectionManager::CallBack, this , arg::_1 , arg::_2 , arg::_3 ) );
128 }
129
130
132 {
133 }
134
135
136 HwInterface ConnectionManager::getDevice ( const std::string& aId )
137 {
138 //We need a mutex lock here to protect access to the NodeTreeBuilder and the ClientFactory
139 std::lock_guard<std::mutex> lLock ( mMutex );
140
141 if ( mConnectionDescriptors.size() == 0 )
142 {
143 exception::ConnectionUIDDoesNotExist lExc;
144 log ( lExc , "Connection map contains no entries" );
145 throw lExc;
146 }
147
148 std::map< std::string, ConnectionDescriptor >::iterator lIt = mConnectionDescriptors.find ( aId );
149
150 if ( lIt == mConnectionDescriptors.end() )
151 {
152 exception::ConnectionUIDDoesNotExist lExc;
153 log ( lExc , "Device ID , " , Quote ( aId ) , ", does not exist in connection map" );
154 throw lExc;
155 }
156
157 //The node tree builder returns a newly created Node which we can safely wrap as a shared_ptr
158 std::shared_ptr< Node > lNode ( NodeTreeBuilder::getInstance().getNodeTree ( lIt->second.address_table , lIt->second.connection_file ) );
159 log ( Info() , "ConnectionManager created node tree: " , *lNode );
160 std::shared_ptr<ClientInterface> lClientInterface ( ClientFactory::getInstance().getClient ( lIt->second.id , lIt->second.uri , mUserClientActivationList ) );
161 return HwInterface ( lClientInterface , lNode );
162 }
163
164
165 HwInterface ConnectionManager::getDevice ( const std::string& aId , const std::string& aUri , const std::string& aAddressFileExpr )
166 {
167 //We need a mutex lock here to protect access to the TodeTreeBuilder and the ClientFactory
168 std::lock_guard<std::mutex> lLock ( mMutex );
169 std::shared_ptr< Node > lNode ( NodeTreeBuilder::getInstance().getNodeTree ( aAddressFileExpr , boost::filesystem::current_path() / "." ) );
170 log ( Info() , "ConnectionManager created node tree: " , *lNode );
171 std::shared_ptr<ClientInterface> lClientInterface ( ClientFactory::getInstance().getClient ( aId , aUri ) );
172 return HwInterface ( lClientInterface , lNode );
173 }
174
175
176 HwInterface ConnectionManager::getDevice ( const std::string& aId , const std::string& aUri , const std::string& aAddressFileExpr, const std::vector<std::string>& aUserClientActivationList )
177 {
178 //We need a mutex lock here to protect access to the TodeTreeBuilder and the ClientFactory
179 std::lock_guard<std::mutex> lLock ( mMutex );
180 std::shared_ptr< Node > lNode ( NodeTreeBuilder::getInstance().getNodeTree ( aAddressFileExpr , boost::filesystem::current_path() / "." ) );
181 log ( Info() , "ConnectionManager created node tree: " , *lNode );
182 std::shared_ptr<ClientInterface> lClientInterface ( ClientFactory::getInstance().getClient ( aId , aUri , aUserClientActivationList ) );
183 return HwInterface ( lClientInterface , lNode );
184 }
185
186
187 std::vector<std::string> ConnectionManager::getDevices ( ) const
188 {
189 std::vector<std::string> lDevices;
190 lDevices.reserve ( mConnectionDescriptors.size() ); //prevent reallocations
191
192 for ( std::map< std::string, ConnectionDescriptor >::const_iterator lIt = mConnectionDescriptors.begin() ; lIt != mConnectionDescriptors.end() ; ++lIt )
193 {
194 lDevices.push_back ( lIt->first );
195 }
196
197 return lDevices;
198 }
199
200
201 std::vector<std::string> ConnectionManager::getDevices ( const std::string& aRegex ) const
202 {
203 std::vector<std::string> lDevices;
204 lDevices.reserve ( mConnectionDescriptors.size() ); //prevent reallocations
205
206 for ( std::map< std::string, ConnectionDescriptor >::const_iterator lIt = mConnectionDescriptors.begin() ; lIt != mConnectionDescriptors.end() ; ++lIt )
207 {
208 boost::cmatch lMatch;
209
210 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
211 {
212 lDevices.push_back ( lIt->first );
213 }
214 }
215
216 return lDevices;
217 }
218
219
221 {
222 // Need a mutex lock here to protect access to NodeTreeBuilder
223 std::lock_guard<std::mutex> lLock ( mMutex );
224 log( Info(), "ConnectionManager is clearing the address filename -> Node tree cache");
226 }
227
228
229 void ConnectionManager::CallBack ( const std::string& aProtocol , const boost::filesystem::path& aPath , std::vector<uint8_t>& aFile )
230 {
231 std::pair< std::set< std::string >::iterator , bool > lInsert = mPreviouslyOpenedFiles.insert ( aProtocol+ ( aPath.string() ) );
232
233 if ( ! lInsert.second )
234 {
235 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!" );
236 return;
237 }
238
239 pugi::xml_document lXmlDocument;
240 pugi::xml_parse_result lLoadResult = lXmlDocument.load_buffer_inplace ( & ( aFile[0] ) , aFile.size() );
241
242 if ( !lLoadResult )
243 {
244 //Mark says to throw on this condition, I will leave it continuing for now...
245 uhal::utilities::PugiXMLParseResultPrettifier ( lLoadResult , aPath , aFile );
246 return;
247 }
248
249 pugi::xpath_node_set lConnections = lXmlDocument.select_nodes ( "/connections/connection" );
250
251 for ( pugi::xpath_node_set::const_iterator lConnectionIt = lConnections.begin(); lConnectionIt != lConnections.end(); ++lConnectionIt )
252 {
253 bool lSuccess;
254 ConnectionDescriptor lDescriptor ( lConnectionIt->node() , aPath , lSuccess );
255
256 if ( lSuccess )
257 {
258 std::pair< std::map< std::string, ConnectionDescriptor >::iterator , bool > lInsert = mConnectionDescriptors.insert ( std::make_pair ( lDescriptor.id , lDescriptor ) );
259
260 if ( !lInsert.second )
261 {
262 if ( lInsert.first->second == lDescriptor )
263 {
264 log ( Info() , "Duplicate connection entry found:"
265 "\n > id = " , lDescriptor.id ,
266 "\n > uri = " , lDescriptor.uri ,
267 "\n > address_table = " , lDescriptor.address_table ,
268 "\n Continuing for now but be aware!" );
269 }
270 else
271 {
272 exception::DuplicatedUID lExc;
273 log ( lExc , "Duplicate connection ID " , Quote ( lDescriptor.id ) , " found in connections file " ,
274 Quote ( aProtocol+ ( aPath.string() ) ) , " but parameters do not match! Bailing!" );
275 throw lExc;
276 }
277 }
278 }
279 else
280 {
281 log ( Error() , "Construction of Connection Descriptor failed. Continuing with next Connection Descriptor for now but be aware!" );
282 }
283 }
284 }
285
286
287 std::mutex ConnectionManager::mMutex;
288
289}
290
xml_parse_result load_buffer_inplace(void *contents, size_t size, unsigned int options=parse_default, xml_encoding encoding=encoding_auto)
Definition: pugixml.cpp:7142
xpath_node_set select_nodes(const char_t *query, xpath_variable_set *variables=0) const
Definition: pugixml.cpp:12770
const_iterator end() const
Definition: pugixml.cpp:12211
const_iterator begin() const
Definition: pugixml.cpp:12206
static ClientFactory & getInstance()
Static method to retrieve the single instance of the class.
ConnectionManager(const ConnectionManager &)=delete
static void clearAddressFileCache()
Clears cache of Node tree structure for previously-opened address files (thread safe)
std::vector< std::string > getDevices() const
Return all device IDs known to this connection manager.
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.
const std::vector< std::string > mUserClientActivationList
std::map< std::string, ConnectionDescriptor > mConnectionDescriptors
A map of connection identifiers to stucts containing details of the parsed XML node.
virtual ~ConnectionManager()
Destructor.
HwInterface getDevice(const std::string &aId)
Retrieves protocol, host, and port from the connection file to create an IPbus Client Retrieves the a...
std::set< std::string > mPreviouslyOpenedFiles
A set of previously opened filenames, so that the same file is not parsed multiple times.
static std::mutex mMutex
A mutex lock to protect access to the factory methods in multithreaded environments.
A class which bundles a node tree and an IPbus client interface together providing everything you nee...
Definition: HwInterface.hpp:56
static NodeTreeBuilder & getInstance()
Static method to retrieve the single instance of the class.
void clearAddressFileCache()
Clears address filename -> Node tree cache. NOT thread safe; for tread-safety, use ConnectionManager ...
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:63
template bool GetXMLattribute< true >(const pugi::xml_node &aNode, const std::string &aAttrName, std::string &aTarget)
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
void OpenFile(const std::string &aProtocol, const std::string &aFilenameExpr, const boost::filesystem::path &aParentPath, const detail::FileCallback_t &aCallback)
Given a protocol and either a URL or a linux shell expression, open the file and call the callback fu...
Definition: files.cpp:329
_Quote< T > Quote(const T &aT)
ErrorLevel Error
Definition: LogLevels.cpp:61
void log(FatalLevel &aFatal, const T0 &aArg0)
Function to add a log entry at Fatal level.
Definition: log.hxx:18
InfoLevel Info
Definition: LogLevels.cpp:115
Annotation for arguments.
Definition: cast.h:1238
A struct to hold the fields of each entry in the XML connections file.
bool operator==(const ConnectionDescriptor &aConnectionDescriptor) const
Comparison operation.
std::string id
An identifier for an individual.
std::string uri
The full uri for making the connection.
std::string address_table
The address table for building the node tree.
ConnectionDescriptor(const pugi::xml_node &aNode, const boost::filesystem::path &aConnectionFile, bool &aSuccess)
Constructor.