ChimeraTK-DeviceAccess-DoocsBackend  01.09.02
CatalogueCache.cc
Go to the documentation of this file.
1 #include "CatalogueCache.h"
2 
3 #include "RegisterInfo.h"
4 #include "StringUtility.h"
5 #include <doocs/EqCall.h>
6 #include <eq_types.h>
7 #include <libxml++/libxml++.h>
8 
9 #include <boost/algorithm/string.hpp>
10 #include <boost/filesystem.hpp>
11 
12 #include <cerrno>
13 #include <cstdlib>
14 #include <cstring>
15 #include <fstream>
16 
17 /********************************************************************************************************************/
18 
19 namespace Cache {
20 
21  static std::unique_ptr<xmlpp::DomParser> createDomParser(const std::string& xmlfile);
22  static xmlpp::Element* getRootNode(xmlpp::DomParser& parser);
23  static unsigned int convertToUint(const std::string& s, int line);
24  static int convertToInt(const std::string& s, int line);
25  static void parseRegister(xmlpp::Element const* registerNode, DoocsBackendRegisterCatalogue& catalogue);
26  static unsigned int parseLength(xmlpp::Element const* c);
27  static int parseTypeId(xmlpp::Element const* c);
28  static ChimeraTK::AccessModeFlags parseAccessMode(xmlpp::Element const* c);
29  static void addRegInfoXmlNode(const DoocsBackendRegisterInfo& r, xmlpp::Node* rootNode);
30 
31  /********************************************************************************************************************/
32 
33  DoocsBackendRegisterCatalogue readCatalogue(const std::string& xmlfile) {
35  auto parser = createDomParser(xmlfile);
36  auto registerList = getRootNode(*parser);
37 
38  for(auto const node : registerList->get_children()) {
39  auto reg = dynamic_cast<const xmlpp::Element*>(node);
40  if(reg == nullptr) {
41  continue;
42  }
43  parseRegister(reg, catalogue);
44  }
45  return catalogue;
46  }
47 
48  /********************************************************************************************************************/
49  bool is_empty(std::ifstream& f) {
50  return f.peek() == std::ifstream::traits_type::eof();
51  }
52 
53  bool is_empty(std::ifstream&& f) {
54  return is_empty(f);
55  }
56 
57  /********************************************************************************************************************/
58 
59  void saveCatalogue(const DoocsBackendRegisterCatalogue& c, const std::string& xmlfile) {
60  xmlpp::Document doc;
61 
62  auto rootNode = doc.create_root_node("catalogue");
63  rootNode->set_attribute("version", "1.0");
64 
65  for(auto& doocsRegInfo : c) {
66  addRegInfoXmlNode(doocsRegInfo, rootNode);
67  }
68 
69  const std::string pathTemplate = "%%%%%%-doocs-backend-cache-%%%%%%.tmp";
70  boost::filesystem::path temporaryName;
71 
72  try {
73  temporaryName = boost::filesystem::unique_path(boost::filesystem::path(pathTemplate));
74  }
75  catch(boost::filesystem::filesystem_error& e) {
76  throw ChimeraTK::runtime_error(std::string{"Failed to generate temporary path: "} + e.what());
77  }
78 
79  {
80  auto stream = std::ofstream(temporaryName);
81  doc.write_to_stream_formatted(stream);
82  }
83 
84  // check for empty tmp file:
85  // xmlpp::Document::write_to_file_formatted sometimes misbehaves on exceptions, creating
86  // empty files.
87  if(is_empty(std::ifstream(temporaryName))) {
88  throw ChimeraTK::runtime_error(std::string{"Failed to save cache File"});
89  }
90 
91  try {
92  boost::filesystem::rename(temporaryName, xmlfile);
93  }
94  catch(boost::filesystem::filesystem_error& e) {
95  throw ChimeraTK::runtime_error(std::string{"Failed to replace cache file: "} + e.what());
96  }
97  }
98 
99  /********************************************************************************************************************/
100 
101  void parseRegister(xmlpp::Element const* registerNode, DoocsBackendRegisterCatalogue& catalogue) {
102  std::string name;
103  unsigned int len{};
104  int doocsTypeId{};
105  ChimeraTK::DataDescriptor descriptor{};
106  ChimeraTK::AccessModeFlags flags{};
107 
108  for(auto& node : registerNode->get_children()) {
109  auto e = dynamic_cast<const xmlpp::Element*>(node);
110  if(e == nullptr) {
111  continue;
112  }
113  std::string nodeName = e->get_name();
114 
115  if(nodeName == "name") {
116  name = e->get_child_text()->get_content();
117  }
118  else if(nodeName == "length") {
119  len = parseLength(e);
120  }
121  else if(nodeName == "access_mode") {
122  flags = parseAccessMode(e);
123  }
124  else if(nodeName == "doocs_type_id") {
125  doocsTypeId = parseTypeId(e);
126  }
127  }
128 
129  bool is_ifff = (doocsTypeId == DATA_IFFF);
130 
131  if(is_ifff) {
132  auto pattern = detail::endsWith(name, {"/I", "/F1", "/F2", "/F3"}).second;
133  // remove pattern from name for getRegInfo to work correctly;
134  // precondition: patten is contained in name.
135  name.erase(name.end() - pattern.length(), name.end());
136  }
137 
138  // add corresponding registers (note: only registers not yet being in the catalogue will be added)
139  catalogue.addProperty(name, len, doocsTypeId, flags);
140  }
141 
142  /********************************************************************************************************************/
143 
144  unsigned int parseLength(xmlpp::Element const* c) {
145  return convertToUint(c->get_child_text()->get_content(), c->get_line());
146  }
147 
148  /********************************************************************************************************************/
149 
150  int parseTypeId(xmlpp::Element const* c) {
151  return convertToInt(c->get_child_text()->get_content(), c->get_line());
152  }
153 
154  /********************************************************************************************************************/
155 
156  ChimeraTK::AccessModeFlags parseAccessMode(xmlpp::Element const* c) {
157  std::string accessMode{};
158  auto t = c->get_child_text();
159  if(t != nullptr) {
160  accessMode = t->get_content();
161  }
162  return ChimeraTK::AccessModeFlags::deserialize(accessMode);
163  }
164 
165  /********************************************************************************************************************/
166 
167  std::unique_ptr<xmlpp::DomParser> createDomParser(const std::string& xmlfile) {
168  try {
169  return std::make_unique<xmlpp::DomParser>(xmlfile);
170  }
171  catch(std::exception& e) {
172  throw ChimeraTK::logic_error("Error opening " + xmlfile + ": " + e.what());
173  }
174  }
175 
176  /********************************************************************************************************************/
177 
178  xmlpp::Element* getRootNode(xmlpp::DomParser& parser) {
179  try {
180  auto root = parser.get_document()->get_root_node();
181  if(root->get_name() != "catalogue") {
182  ChimeraTK::logic_error("Expected tag 'catalog' got: " + root->get_name());
183  }
184  return root;
185  }
186  catch(std::exception& e) {
187  throw ChimeraTK::logic_error(e.what());
188  }
189  }
190 
191  /********************************************************************************************************************/
192 
193  unsigned int convertToUint(const std::string& s, int line) {
194  try {
195  return std::stoul(s);
196  }
197  catch(std::invalid_argument& e) {
198  throw ChimeraTK::logic_error("Failed to parse node at line " + std::to_string(line) + ":" + e.what());
199  }
200  catch(std::out_of_range& e) {
201  throw ChimeraTK::logic_error("Failed to parse node at line " + std::to_string(line) + ":" + e.what());
202  }
203  }
204  /********************************************************************************************************************/
205 
206  int convertToInt(const std::string& s, int line) {
207  try {
208  return std::stol(s);
209  }
210  catch(std::invalid_argument& e) {
211  throw ChimeraTK::logic_error("Failed to parse node at line " + std::to_string(line) + ":" + e.what());
212  }
213  catch(std::out_of_range& e) {
214  throw ChimeraTK::logic_error("Failed to parse node at line " + std::to_string(line) + ":" + e.what());
215  }
216  }
217 
218  /********************************************************************************************************************/
219 
220  void addRegInfoXmlNode(const DoocsBackendRegisterInfo& r, xmlpp::Node* rootNode) {
221  auto registerTag = rootNode->add_child("register");
222 
223  auto nameTag = registerTag->add_child("name");
224  nameTag->set_child_text(static_cast<std::string>(r.getRegisterName()));
225 
226  auto lengthTag = registerTag->add_child("length");
227  lengthTag->set_child_text(std::to_string(r.getNumberOfElements()));
228 
229  auto accessMode = registerTag->add_child("access_mode");
230  accessMode->set_child_text(r.accessModeFlags.serialize());
231 
232  auto typeId = registerTag->add_child("doocs_type_id");
233  typeId->set_child_text(std::to_string(r.doocsTypeId));
234 
235  std::string comment_text = std::string("doocs id: ") + doocs::EqData().type_string(r.doocsTypeId);
236  registerTag->add_child_comment(comment_text);
237  }
238 
239 } // namespace Cache
DoocsBackendRegisterInfo::getNumberOfElements
unsigned int getNumberOfElements() const override
Definition: RegisterInfo.h:25
DoocsBackendRegisterCatalogue::addProperty
void addProperty(const std::string &name, unsigned int length, int doocsType, ChimeraTK::AccessModeFlags flags)
Definition: RegisterInfo.cc:9
Cache::saveCatalogue
void saveCatalogue(const DoocsBackendRegisterCatalogue &c, const std::string &xmlfile)
Definition: CatalogueCache.cc:59
CatalogueCache.h
Cache::readCatalogue
DoocsBackendRegisterCatalogue readCatalogue(const std::string &xmlfile)
Definition: CatalogueCache.cc:33
DoocsBackendRegisterInfo::doocsTypeId
int doocsTypeId
Definition: RegisterInfo.h:43
DoocsBackendRegisterInfo::getRegisterName
ChimeraTK::RegisterPath getRegisterName() const override
Definition: RegisterInfo.h:23
DoocsBackendRegisterCatalogue
Definition: RegisterInfo.h:12
Cache::is_empty
bool is_empty(std::ifstream &f)
Definition: CatalogueCache.cc:49
RegisterInfo.h
DoocsBackendRegisterInfo::accessModeFlags
ChimeraTK::AccessModeFlags accessModeFlags
Definition: RegisterInfo.h:42
Cache
Definition: CatalogueCache.h:8
detail::endsWith
std::pair< bool, std::string > endsWith(std::string const &s, const std::vector< std::string > &patterns)
Definition: StringUtility.cc:9
DoocsBackendRegisterInfo
Definition: RegisterInfo.h:21
StringUtility.h