KaCanOpen
 All Classes Functions Variables Typedefs Enumerations Pages
eds_library.cpp
1 /*
2  * Copyright (c) 2015, Thomas Keh
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *
11  * 2. Redistributions in binary form must reproduce the above copyright
12  * notice, this list of conditions and the following disclaimer in the
13  * documentation and/or other materials provided with the distribution.
14  *
15  * 3. Neither the name of the copyright holder nor the names of its
16  * contributors may be used to endorse or promote products derived from
17  * this software without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
23  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 #include "eds_library.h"
33 #include "eds_reader.h"
34 #include "device.h"
35 #include "logger.h"
36 #include "canopen_error.h"
37 #include "types.h"
38 #include "value.h"
39 #include "global_config.h"
40 
41 #include <vector>
42 #include <string>
43 #include <unordered_map>
44 
45 #include <boost/filesystem.hpp>
46 #include <boost/property_tree/ptree.hpp> // property_tree
47 #include <boost/property_tree/json_parser.hpp>
48 
49 // This is set by CMake...
50 //#define SHARE_SOURCE_PATH ...
51 //#define SHARE_INSTALLED_PATH ...
52 
53 namespace kaco {
54 
55  namespace fs = boost::filesystem;
56 
57  EDSLibrary::EDSLibrary(std::unordered_map<Address, Entry>& dictionary, std::unordered_map<std::string, Address>& name_to_address)
58  : m_dictionary(dictionary), m_name_to_address(name_to_address), m_ready(false)
59  { }
60 
61  bool EDSLibrary::lookup_library(std::string path) {
62 
63  m_ready = false;
64 
65  std::vector<std::string> paths;
66  if (!path.empty()) {
67  paths.push_back(path+"/eds_files.json");
68  }
69 
70  paths.push_back(SHARE_SOURCE_PATH "/eds_library/eds_files.json");
71  paths.push_back(SHARE_INSTALLED_PATH "/eds_library/eds_files.json");
72  paths.push_back("/usr/local/share/kacanopen/eds_library/eds_files.json");
73  paths.push_back("/usr/share/kacanopen/eds_library/eds_files.json");
74  // TODO: typical windows / mac osx paths?
75  // TODO: environment variable
76 
77  bool success = false;
78  for (const std::string& path : paths) {
79  if (fs::exists(path)) {
80  m_library_path = fs::path(path).parent_path().string();
81  DEBUG_LOG("[EDSLibrary::lookup_library] Found EDS library in "<<m_library_path);
82  success = true;
83  break;
84  }
85  }
86 
87  if (!success) {
88  DEBUG_LOG("[EDSLibrary::lookup_library] Could not find EDS library. You should pass a custum path or check your local installation.")
89  return false;
90  }
91 
92  m_ready = true;
93  return true;
94  }
95 
97  return load_default_eds(301);
98  }
99 
100  bool EDSLibrary::load_default_eds(uint16_t device_profile_number) {
101 
102  assert(m_ready);
103 
104  std::string path = m_library_path + "/CiA_profiles/"+std::to_string(device_profile_number)+".eds";
105  if (!fs::exists(path)) {
106  DEBUG_LOG("[EDSLibrary::load_default_eds] Default EDS file not available: "<<path);
107  return false;
108  }
109 
110 
113  }
114 
115  EDSReader reader(m_dictionary, m_name_to_address);
116  bool success = reader.load_file(path);
117 
118  if (!success) {
119  ERROR("[EDSLibrary::load_default_eds] Loading file not successful.");
120  return false;
121  }
122 
123  DEBUG_LOG("[EDSLibrary::load_default_eds] Found EDS file: "<<path);
124  success = reader.import_entries();
125  most_recent_eds_file = path;
126 
127  if (!success) {
128  ERROR("[EDSLibrary::load_default_eds] Importing entries failed.");
129  return false;
130  }
131 
132  return true;
133 
134  }
135 
137 
138  boost::property_tree::ptree eds_files;
139  boost::property_tree::json_parser::read_json(m_library_path+"/eds_files.json", eds_files);
140  std::unordered_map<std::string,Value> cache;
141 
142  for (const auto& level0 : eds_files) {
143 
144  const std::string& filename = level0.second.get<std::string>("file");
145  DEBUG_LOG("Testing if "<<filename<<" fits.");
146  const boost::property_tree::ptree& matches = level0.second.get_child("match");
147  bool fits = true;
148 
149  for (const auto& level1 : matches) {
150 
151  const std::string& field = level1.first;
152  const std::string& value_expected = level1.second.get_value<std::string>();
153  bool has_field = false;
154 
155  if (cache.count(field)>0) {
156  // field is already in cache
157  if (cache[field].type != Type::invalid) {
158  has_field = true;
159  }
160  } else {
161  try {
162  const Value& temp = device.get_entry(field);
163  cache[field] = temp;
164  has_field = true;
165  } catch (const canopen_error& err){
166  DEBUG_LOG(" ("<<err.what()<<")");
167  cache[field] = Value(); // invalid value
168  }
169  }
170 
171  if (has_field) {
172  const std::string& value = cache[field].to_string().substr(0,value_expected.length());
173  if (value_expected == value) {
174  DEBUG_LOG(" "<<field<<": "<<value<<" == "<<value_expected<<" (expected) -> continue.");
175  } else {
176  DEBUG_LOG(" "<<field<<": "<<value<<" != "<<value_expected<<" (expected) -> break.");
177  fits = false;
178  }
179  } else {
180  DEBUG_LOG(" Field does not exist -> break...");
181  fits = false;
182  }
183 
184  if (!fits) {
185  break;
186  }
187 
188  }
189 
190  if (fits) {
191 
192  DEBUG_LOG(" "<<filename<<" fits.");
193  const std::string path = m_library_path + "/"+filename;
194  assert(fs::exists(path));
195 
198  }
199 
200  EDSReader reader(m_dictionary, m_name_to_address);
201  bool success = reader.load_file(path);
202 
203  if (!success) {
204  ERROR("[EDSLibrary::load_manufacturer_eds] Loading file not successful.");
205  return false;
206  }
207 
208  success = reader.import_entries();
209  most_recent_eds_file = path;
210 
211  if (!success) {
212  ERROR("[EDSLibrary::load_manufacturer_eds] Importing entries failed.");
213  return false;
214  }
215 
216  return true;
217 
218  } else {
219  DEBUG_LOG(" "<<filename<<" does not fit.");
220  }
221 
222  }
223 
224  DEBUG_LOG("No suitable manufacturer EDS file found.");
225  return false;
226 
227  }
228 
229  bool EDSLibrary::load_manufacturer_eds_deprecated(uint32_t vendor_id, uint32_t product_code, uint32_t revision_number) {
230  assert(m_ready);
231 
232  // check if there is an EDS file for this revision
233  std::string path = m_library_path + "/"+std::to_string(vendor_id)+"/"+std::to_string(product_code)+"."+std::to_string(revision_number)+".eds";
234  if (!fs::exists(path)) {
235  // check if there is a generic EDS file for product
236  path = m_library_path + "/"+std::to_string(vendor_id)+"/"+std::to_string(product_code)+".eds";
237  if (!fs::exists(path)) {
238  DEBUG_LOG("[EDSLibrary::load_manufacturer_eds] Manufacturer device specific EDS file not available: "<<path);
239  return false;
240  }
241  }
242 
243  DEBUG_LOG("[EDSLibrary::load_manufacturer_eds] Found manufacturer EDS: "<<path);
244 
247  }
248 
249  EDSReader reader(m_dictionary, m_name_to_address);
250  bool success = reader.load_file(path);
251  most_recent_eds_file = path;
252 
253  if (!success) {
254  ERROR("[EDSLibrary::load_manufacturer_eds] Loading file not successful.");
255  return false;
256  }
257 
258  success = reader.import_entries();
259 
260  if (!success) {
261  ERROR("[EDSLibrary::load_manufacturer_eds] Importing entries failed.");
262  return false;
263  }
264 
265  return true;
266 
267  }
268 
269  bool EDSLibrary::ready() const {
270  return m_ready;
271  }
272 
274  m_dictionary.clear();
275  m_name_to_address.clear();
276  }
277 
279  return most_recent_eds_file;
280  }
281 
282 } // end namespace kaco
const Value & get_entry(const std::string &entry_name, const ReadAccessMethod access_method=ReadAccessMethod::use_default)
Gets the value of a dictionary entry by name internally. If there is no cached value or the entry is ...
Definition: device.cpp:102
bool import_entries()
Import entries from the EDS file into the given dictionary.
Definition: eds_reader.cpp:67
This class allows reading EDS files (like standardized in CiA 306) and inserting all contained entrie...
Definition: eds_reader.h:50
bool load_default_eds(uint16_t device_profile_number)
Loads entries defined in generic CiA profile EDS files.
This is the base class of all types of exceptions thrown by the KaCanOpen library. It can be used directly like std::runtime_error if there isn't any more specific error class.
Definition: canopen_error.h:43
bool load_mandatory_entries()
Loads mandatory dictionary entries defined in CiA 301 standard.
Definition: eds_library.cpp:96
bool lookup_library(std::string path="")
Finds EDS library on disk.
Definition: eds_library.cpp:61
bool load_manufacturer_eds(Device &device)
Loads entries defined in device specific EDS files proviced by manufacturers.
This class represents a CanOpen slave device in the network.
Definition: device.h:83
bool ready() const
Checks if lookup_library() was successful.
static bool eds_library_clear_dictionary
If this is set to true, EDSLibrary will clear dictionary and me-to-address mappings before loading an...
Definition: global_config.h:59
bool load_file(std::string filename)
Loads an EDS file from file system.
Definition: eds_reader.cpp:54
void reset_dictionary()
Resets the dictionary and the name-address mapping.
EDSLibrary(std::unordered_map< Address, Entry > &dictionary, std::unordered_map< std::string, Address > &name_to_address)
Constructor.
Definition: eds_library.cpp:57
std::string get_most_recent_eds_file_path() const
Returns the path to the most recently loaded EDS file.
This class contains a value to be stored in the object dictionary. The value can have one of the type...
Definition: value.h:53
bool load_manufacturer_eds_deprecated(uint32_t vendor_id, uint32_t product_code, uint32_t revision_number)
Loads entries defined in device specific EDS files proviced by manufacturers.