ChimeraTK-DeviceAccess  03.18.00
Utilities.cpp
Go to the documentation of this file.
1 // SPDX-FileCopyrightText: Deutsches Elektronen-Synchrotron DESY, MSK, ChimeraTK Project <chimeratk-support@desy.de>
2 // SPDX-License-Identifier: LGPL-3.0-or-later
3 
4 #include "Utilities.h"
5 
6 #include "BackendFactory.h"
7 #include "DMapFileParser.h"
8 
9 #include <boost/algorithm/string.hpp>
10 #include <boost/algorithm/string/predicate.hpp>
11 
12 #include <cxxabi.h>
13 #include <execinfo.h>
14 #include <utility>
15 #include <vector>
16 
17 namespace ChimeraTK {
18 
19  /********************************************************************************************************************/
20 
21  size_t Utilities::countOccurence(std::string theString, char delimiter) {
22  size_t count = std::count(theString.begin(), theString.end(), delimiter);
23  return count;
24  }
25 
26  /********************************************************************************************************************/
27 
28  bool Utilities::isSdm(const std::string& theString) {
29  size_t signatureLen = 6;
30  if(theString.length() < signatureLen) return false;
31  if(theString.substr(0, 6) != "sdm://") return false;
32  return true;
33  }
34 
35  /********************************************************************************************************************/
36 
37  bool Utilities::isDeviceDescriptor(std::string theString) {
38  boost::trim(theString);
39  if(theString.length() < 3) return false;
40  if(theString.substr(0, 1) != "(") return false;
41  if(theString.substr(theString.length() - 1, 1) != ")") return false;
42  return true;
43  }
44 
45  /********************************************************************************************************************/
46 
48  DeviceDescriptor result;
49 
50  // first trim the string to remove whitespaces before and after the outer
51  // parentheses
52  boost::trim(cddString);
53 
54  // simple initial checks
55  if(cddString.length() < 3) {
56  throw ChimeraTK::logic_error("Invalid ChimeraTK device descriptor (too short): " + cddString);
57  }
58  if(cddString.substr(0, 1) != "(") {
59  throw ChimeraTK::logic_error("Invalid ChimeraTK device descriptor (missing opening parenthesis): " + cddString);
60  }
61  if(cddString.substr(cddString.length() - 1, 1) != ")") {
62  throw ChimeraTK::logic_error("Invalid ChimeraTK device descriptor (missing opening parenthesis): " + cddString);
63  }
64 
65  // walk through string
66  size_t parenthesesLevel = 0;
67  enum class tokenType { backendType, address, parameters };
68  tokenType currentTokenType = tokenType::backendType;
69  bool escapeNext = false;
70  std::string token;
71  size_t positionPlusOne = 0;
72  for(auto& c : cddString) {
73  ++positionPlusOne;
74  // are we indide the outer main parenthesis but not within a deeper level?
75  if(parenthesesLevel == 1) {
76  // should the current character be escaped?
77  if(escapeNext) {
78  if(c == ' ' || c == '?' || c == '&' || c == '(' || c == ')' || c == '\\') {
79  token += c;
80  }
81  else {
82  throw ChimeraTK::logic_error("Invalid ChimeraTK device descriptor (bad escape character): " + cddString);
83  }
84  escapeNext = false;
85  continue; // no further parsing of this character!
86  }
87  // current character should be parsed normally
88  if(c == '\\') {
89  // escape next character
90  escapeNext = true;
91  }
92  else if(currentTokenType == tokenType::backendType && (c == ':' || c == '?' || c == ')')) {
93  // backendType token complete
94  boost::trim(token);
95  if(token.length() == 0) {
96  throw ChimeraTK::logic_error("Invalid ChimeraTK device descriptor "
97  "(backend type must be non-empty): " +
98  cddString);
99  }
100  if(!boost::all(token, boost::is_alnum())) {
101  throw ChimeraTK::logic_error("Invalid ChimeraTK device descriptor "
102  "(backend type must be alphanumeric): " +
103  cddString);
104  }
105  result.backendType = token;
106  token = "";
107  // determine next token type by checking the delimiter (')' will be
108  // processed below)
109  if(c == ':') {
110  currentTokenType = tokenType::address;
111  }
112  else if(c == '?') {
113  currentTokenType = tokenType::parameters;
114  }
115  }
116  else if(currentTokenType == tokenType::address && (c == '?' || c == ')')) {
117  // address token complete
118  boost::trim(token);
119  result.address = token;
120  token = "";
121  currentTokenType = tokenType::parameters; // ')' will be processed below
122  }
123  else if(currentTokenType == tokenType::parameters && (c == '&' || c == ')')) {
124  // parameter token complete (for one key-value pair)
125  boost::trim(token);
126  if(token.length() > 0) { // ignore empty parameter
127  auto equalSign = token.find_first_of('=');
128  if(equalSign == std::string::npos) {
129  throw ChimeraTK::logic_error("Invalid ChimeraTK device descriptor (parameters must be "
130  "specified as key=value pairs): " +
131  cddString);
132  }
133  auto key = token.substr(0, equalSign);
134  auto value = token.substr(equalSign + 1);
135  boost::trim(key);
136  if(key.length() == 0) {
137  throw ChimeraTK::logic_error("Invalid ChimeraTK device descriptor (parameter key names must "
138  "not be empty): " +
139  cddString);
140  }
141  if(!boost::all(key, boost::is_alnum())) {
142  throw ChimeraTK::logic_error("Invalid ChimeraTK device descriptor (parameter key names must "
143  "contain only "
144  "alphanumeric characters): " +
145  cddString);
146  }
147  boost::trim(value);
148  if(result.parameters.find(key) != result.parameters.end()) {
149  throw ChimeraTK::logic_error("Invalid ChimeraTK device descriptor (parameter '" + key +
150  "' specified multiple times): " + cddString);
151  }
152  result.parameters[key] = value;
153  token = "";
154  }
155  }
156  else {
157  token += c;
158  }
159  }
160  else if(parenthesesLevel > 1) {
161  // deeper level: add all characters to current token
162  token += c;
163  }
164  if(c == '(') {
165  ++parenthesesLevel;
166  }
167  else if(c == ')') {
168  --parenthesesLevel;
169  if(parenthesesLevel == 0 && positionPlusOne != cddString.length()) {
170  // main parenthesis closed but not yet end of the string
171  throw ChimeraTK::logic_error("Invalid ChimeraTK device descriptor (additional characters after "
172  "last closing parenthesis): " +
173  cddString);
174  }
175  }
176  }
177 
178  // check if final parenthesis found
179  if(parenthesesLevel != 0) {
180  throw ChimeraTK::logic_error("Invalid ChimeraTK device descriptor (unmatched parenthesis): " + cddString);
181  }
182 
183  // return the result
184  return result;
185  }
186 
187  /********************************************************************************************************************/
188 
189  Sdm Utilities::parseSdm(const std::string& sdmString) {
190  Sdm sdmInfo;
191  size_t signatureLen = 6;
192  if(sdmString.length() < signatureLen) throw ChimeraTK::logic_error("Invalid sdm.");
193  if(sdmString.substr(0, 6) != "sdm://") throw ChimeraTK::logic_error("Invalid sdm.");
194  int pos = 6;
195 
196  std::size_t found = sdmString.find_first_of('/', pos);
197  std::string subUri;
198  if(found != std::string::npos) {
199  sdmInfo.host = sdmString.substr(pos, found - pos); // Get the Host
200  }
201  else {
202  throw ChimeraTK::logic_error("Invalid sdm.");
203  }
204  if(sdmString.length() < found + 1) return sdmInfo;
205  subUri = sdmString.substr(found + 1);
206  // let's do a sanity check, only one delimiter occurrence is allowed.
207  if(countOccurence(subUri, ':') > 1) /* check ':' */
208  {
209  throw ChimeraTK::logic_error("Invalid sdm.");
210  }
211  if(countOccurence(subUri, ';') > 1) /* check ';' */
212  {
213  throw ChimeraTK::logic_error("Invalid sdm.");
214  }
215  if(countOccurence(subUri, '=') > 1) /* check '=' */
216  {
217  throw ChimeraTK::logic_error("Invalid sdm.");
218  }
219  std::vector<std::string> tokens;
220  boost::split(tokens, subUri, boost::is_any_of(":;="));
221  size_t numOfTokens = tokens.size();
222  if(numOfTokens < 1) return sdmInfo;
223  size_t counter = 0;
224  sdmInfo.interface = tokens[counter]; // Get the Interface
225  counter++;
226  if(counter < numOfTokens) {
227  // Get the Instance
228  found = sdmString.find_first_of(':', pos);
229  if(found != std::string::npos) {
230  sdmInfo.instance = tokens[counter];
231  counter++;
232  }
233  }
234  if(counter < numOfTokens) {
235  // Get the Protocol
236  found = sdmString.find_first_of(';', pos);
237  if(found != std::string::npos) {
238  sdmInfo.protocol = tokens[counter];
239  counter++;
240  }
241  }
242  if(counter < numOfTokens) {
243  // Get the Parameters
244  found = sdmString.find_first_of('=', pos);
245  if(found != std::string::npos) {
246  std::string parameters = tokens[counter];
247  std::vector<std::string> paramterTokens;
248  boost::split(paramterTokens, parameters, boost::is_any_of(","));
249  for(auto& paramterToken : paramterTokens) {
250  sdmInfo.parameters.push_back(paramterToken);
251  }
252  }
253  }
254 
255  return sdmInfo;
256  }
257 
258  /********************************************************************************************************************/
259 
260  Sdm Utilities::parseDeviceString(const std::string& deviceString) {
261  Sdm sdmInfo;
262  if(deviceString.substr(0, 5) == "/dev/") {
263  sdmInfo.interface = "pci";
264  if(deviceString.length() > 5) {
265  sdmInfo.instance = deviceString.substr(5);
266  }
267  }
268  else if((boost::ends_with(deviceString, ".map")) || (boost::ends_with(deviceString, ".mapp"))) {
269  sdmInfo.interface = "dummy";
270  sdmInfo.instance = deviceString;
271  /*another change in interface requires now instance
272  to be ignored and old expect old Instance parameter
273  as firt item of the Parameters list*/
274  sdmInfo.parameters.push_back(sdmInfo.instance);
275  }
276  else {
277  return sdmInfo;
278  }
279  sdmInfo.host = ".";
280  sdmInfo.protocol = "";
281  return sdmInfo;
282  }
283 
284  /********************************************************************************************************************/
285 
286  DeviceInfoMap::DeviceInfo Utilities::aliasLookUp(const std::string& aliasName, const std::string& dmapFilePath) {
287  DeviceInfoMap::DeviceInfo deviceInfo;
288  auto deviceInfoPointer = ChimeraTK::DMapFileParser::parse(dmapFilePath);
289  deviceInfoPointer->getDeviceInfo(aliasName, deviceInfo);
290  return deviceInfo;
291  }
292 
293  /********************************************************************************************************************/
294 
295  std::vector<std::string> Utilities::getAliasList() {
296  std::string dmapFileName = getDMapFilePath();
297  if(dmapFileName.empty()) {
298  throw ChimeraTK::logic_error("Dmap file not set");
299  }
300 
301  try {
302  auto deviceInfoMap = ChimeraTK::DMapFileParser::parse(dmapFileName);
303 
304  std::vector<std::string> listOfDeviceAliases;
305  listOfDeviceAliases.reserve(deviceInfoMap->getSize());
306 
307  for(auto&& deviceInfo : *deviceInfoMap) {
308  listOfDeviceAliases.push_back(deviceInfo.deviceName);
309  }
310 
311  return listOfDeviceAliases;
312  }
313  catch(ChimeraTK::runtime_error& e) {
314  std::cout << e.what() << std::endl;
315  return {}; // empty list in case of failure
316  }
317  }
318 
319  /********************************************************************************************************************/
320 
321  std::string getDMapFilePath() {
323  }
324 
325  /********************************************************************************************************************/
326 
327  void setDMapFilePath(std::string dmapFilePath) {
328  BackendFactory::getInstance().setDMapFilePath(std::move(dmapFilePath));
329  }
330 
331  /********************************************************************************************************************/
332 
334  void* trace[16];
335  char** messages;
336  int i, trace_size = 0;
337 
338  trace_size = backtrace(trace, 16);
339  messages = backtrace_symbols(trace, trace_size);
340  printf("[bt] Execution path:\n");
341  for(i = 0; i < trace_size; ++i) {
342  std::string msg(messages[i]);
343  size_t a = msg.find_first_of('(');
344  size_t b = msg.find_first_of('+');
345  std::string functionName = boost::core::demangle(msg.substr(a + 1, b - a - 1).c_str());
346  std::cout << "[bt] #" << i << " " << functionName << std::endl;
347  }
348  }
349 
350  /********************************************************************************************************************/
351 
352 } /* namespace ChimeraTK */
ChimeraTK::runtime_error::what
const char * what() const noexcept override
Return the message describing what exactly went wrong.
Definition: Exception.cpp:14
ChimeraTK::Sdm::host
std::string host
Definition: Utilities.h:42
ChimeraTK::DMapFileParser::parse
static DeviceInfoMapPointer parse(const std::string &file_name)
Performs parsing of specified DMAP file.
Definition: DMapFileParser.cpp:18
ChimeraTK::Sdm::interface
std::string interface
Definition: Utilities.h:43
ChimeraTK::Utilities::parseDeviceDesciptor
DeviceDescriptor parseDeviceDesciptor(std::string cddString)
Parse a ChimeraTK device descriptor (CDD) and return the information in the DeviceDescriptor struct.
Definition: Utilities.cpp:47
ChimeraTK::BackendFactory::getInstance
static BackendFactory & getInstance()
Static function to get an instance of factory.
Definition: BackendFactory.cc:191
ChimeraTK::Utilities::countOccurence
size_t countOccurence(std::string theString, char delimiter)
Check if the given string only contains alphanumeric characters.
Definition: Utilities.cpp:21
Utilities.h
ChimeraTK::Utilities::getAliasList
std::vector< std::string > getAliasList()
Returns the list of device aliases from the DMap file set using BackendFactory::setDMapFilePath.
Definition: Utilities.cpp:295
ChimeraTK::Sdm::instance
std::string instance
Definition: Utilities.h:44
ChimeraTK::Sdm
This structure holds the information of an SDM.
Definition: Utilities.h:40
ChimeraTK::DeviceDescriptor
This structure holds the information of an ChimeraTK device descriptor.
Definition: Utilities.h:31
ChimeraTK::BackendFactory::setDMapFilePath
void setDMapFilePath(std::string dMapFilePath)
This function sets the _DMapFilePath.
Definition: BackendFactory.cc:161
ChimeraTK::DeviceDescriptor::parameters
std::map< std::string, std::string > parameters
Definition: Utilities.h:34
ChimeraTK::runtime_error
Exception thrown when a runtime error has occured.
Definition: Exception.h:18
ChimeraTK::DeviceDescriptor::backendType
std::string backendType
Definition: Utilities.h:32
ChimeraTK::Utilities::parseDeviceString
Sdm parseDeviceString(const std::string &deviceString)
Parse an old-style device string (either path to device node or map file name for dummies)
Definition: Utilities.cpp:260
ChimeraTK::DeviceDescriptor::address
std::string address
Definition: Utilities.h:33
ChimeraTK::Utilities::isSdm
bool isSdm(const std::string &theString)
Check wehter the given string seems to be an SDM.
Definition: Utilities.cpp:28
DMapFileParser.h
ChimeraTK::Utilities::parseSdm
Sdm parseSdm(const std::string &sdmString)
Parse an SDM URI and return the device information in the Sdm struct.
Definition: Utilities.cpp:189
ChimeraTK::DeviceInfoMap::DeviceInfo
Stores information about one device.
Definition: DeviceInfoMap.h:30
ChimeraTK::Sdm::parameters
std::list< std::string > parameters
Definition: Utilities.h:46
ChimeraTK::Utilities::isDeviceDescriptor
bool isDeviceDescriptor(std::string theString)
Check whether the given string seems to be a CDD.
Definition: Utilities.cpp:37
ChimeraTK::getDMapFilePath
std::string getDMapFilePath()
Returns the dmap file name which the library currently uses for looking up device(alias) names.
Definition: Utilities.cpp:321
BackendFactory.h
ChimeraTK::Utilities::aliasLookUp
DeviceInfoMap::DeviceInfo aliasLookUp(const std::string &aliasName, const std::string &dmapFilePath)
Search for an alias in a given DMap file and return the DeviceInfo entry.
Definition: Utilities.cpp:286
ChimeraTK::Sdm::protocol
std::string protocol
Definition: Utilities.h:45
ChimeraTK::setDMapFilePath
void setDMapFilePath(std::string dmapFilePath)
Set the location of the dmap file.
Definition: Utilities.cpp:327
ChimeraTK
Definition: DummyBackend.h:16
ChimeraTK::Utilities::printStackTrace
void printStackTrace()
Print a call stack trace (but continue executing the process normally).
Definition: Utilities.cpp:333
ChimeraTK::logic_error
Exception thrown when a logic error has occured.
Definition: Exception.h:51