KaCanOpen
 All Classes Functions Variables Typedefs Enumerations Pages
device.cpp
1 /*
2  * Copyright (c) 2015-2016, 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 "device.h"
33 #include "core.h"
34 #include "utils.h"
35 #include "logger.h"
36 #include "dictionary_error.h"
37 #include "profiles.h"
38 #include "sdo_error.h"
39 #include "global_config.h"
40 
41 #include <cassert>
42 #include <algorithm>
43 #include <functional>
44 #include <memory>
45 
46 namespace kaco {
47 
48 Device::Device(Core& core, uint8_t node_id)
49  : m_core(core), m_node_id(node_id), m_eds_library(m_dictionary, m_name_to_address) { }
50 
52  { }
53 
54 void Device::start() {
55 
56  m_core.nmt.send_nmt_message(m_node_id,NMT::Command::start_node);
57 
58  if (!m_eds_library.lookup_library()) {
59  throw canopen_error("[Device::start] EDS library not found. If and only if you make sure for yourself, that mandatory"
60  " entries and operations are available, you can catch this error and go on.");
61  }
62 
63  if (!m_eds_library.load_mandatory_entries()) {
64  throw canopen_error("[Device::start] Could not load mandatory dictionary entries."
65  " If and only if you make sure for yourself, that mandatory"
66  " entries and operations are available, you can catch this error and go on.");
67  }
68 
71 
72 }
73 
74 uint8_t Device::get_node_id() const {
75  return m_node_id;
76 }
77 
78 bool Device::has_entry(const std::string& entry_name) {
79  const std::string name = Utils::escape(entry_name);
80  return m_name_to_address.count(name) > 0 && has_entry(m_name_to_address[name].index, m_name_to_address[name].subindex);
81 }
82 
83 bool Device::has_entry(const uint16_t index, const uint8_t subindex) {
84  return m_dictionary.count(Address{index,subindex}) > 0;
85 }
86 
87 Type Device::get_entry_type(const std::string& entry_name) {
88  const std::string name = Utils::escape(entry_name);
89  if (!has_entry(name)) {
90  throw dictionary_error(dictionary_error::type::unknown_entry, name);
91  }
92  return m_dictionary[m_name_to_address[name]].get_type();
93 }
94 
95 Type Device::get_entry_type(const uint16_t index, const uint8_t subindex) {
96  if (!has_entry(index, subindex)) {
97  throw dictionary_error(dictionary_error::type::unknown_entry, std::to_string(index)+"sub"+std::to_string(subindex));
98  }
99  return m_dictionary[Address{index,subindex}].get_type();
100 }
101 
102 const Value& Device::get_entry(const std::string& entry_name, const ReadAccessMethod access_method) {
103  const std::string name = Utils::escape(entry_name);
104  if (!has_entry(name)) {
105  throw dictionary_error(dictionary_error::type::unknown_entry, name);
106  }
107  const Address address = m_name_to_address[name];
108  return get_entry(address.index, address.subindex, access_method);
109 }
110 
111 const Value& Device::get_entry(const uint16_t index, const uint8_t subindex, const ReadAccessMethod access_method) {
112  if (!has_entry(index, subindex)) {
113  throw dictionary_error(dictionary_error::type::unknown_entry, std::to_string(index)+"sub"+std::to_string(subindex));
114  }
115  Entry& entry = m_dictionary[Address{index,subindex}];
116  if (access_method==ReadAccessMethod::sdo || (access_method==ReadAccessMethod::use_default && entry.read_access_method==ReadAccessMethod::sdo)) {
117  DEBUG_LOG("[Device::get_entry] SDO update on read.");
118  entry.set_value(get_entry_via_sdo(entry.index, entry.subindex, entry.type));
119  }
120  return entry.get_value();
121 }
122 
123 void Device::set_entry(const std::string& entry_name, const Value& value, const WriteAccessMethod access_method) {
124  const std::string name = Utils::escape(entry_name);
125  if (!has_entry(name)) {
126  throw dictionary_error(dictionary_error::type::unknown_entry, name);
127  }
128  const Address address = m_name_to_address[name];
129  return set_entry(address.index, address.subindex, value, access_method);
130 }
131 
132 void Device::set_entry(const uint16_t index, const uint8_t subindex, const Value& value, const WriteAccessMethod access_method) {
133  const std::string index_string = std::to_string(index)+"sub"+std::to_string(subindex);
134  if (!has_entry(index, subindex)) {
135  throw dictionary_error(dictionary_error::type::unknown_entry, index_string);
136  }
137  Entry& entry = m_dictionary[Address{index,subindex}];
138  if (value.type != entry.type) {
139  throw dictionary_error(dictionary_error::type::wrong_type, index_string,
140  "Entry type: "+Utils::type_to_string(entry.type)+", given type: "+Utils::type_to_string(value.type));
141  }
142  entry.set_value(value);
143  if (access_method==WriteAccessMethod::sdo || (access_method==WriteAccessMethod::use_default && entry.write_access_method==WriteAccessMethod::sdo)) {
144  DEBUG_LOG("[Device::set_entry] SDO update on write.");
145  set_entry_via_sdo(entry.index, entry.subindex, value);
146  }
147 }
148 
149 void Device::add_entry(const uint16_t index, const uint8_t subindex, const std::string& name, const Type type, const AccessType access_type) {
150  const std::string entry_name = Utils::escape(name);
151  if (m_name_to_address.count(entry_name)>0) {
152  throw canopen_error("[Device::add_entry] Entry with name \""+entry_name+"\" already exists.");
153  }
154  if (has_entry(index, subindex)) {
155  throw canopen_error("[Device::add_entry] Entry with index "+std::to_string(index)+"sub"+std::to_string(subindex)+" already exists.");
156  }
157  Entry entry(index,subindex,entry_name,type,access_type);
158  const Address address{index,subindex};
159  m_dictionary.insert(std::make_pair(address, std::move(entry)));
160  m_name_to_address.insert(std::make_pair(entry_name,address));
161 }
162 
163 void Device::add_receive_pdo_mapping(uint16_t cob_id, const std::string& entry_name, uint8_t offset) {
164 
165  // TODO: update entry's default access method
166 
167  const std::string name = Utils::escape(entry_name);
168 
169  if (!has_entry(name)) {
170  throw dictionary_error(dictionary_error::type::unknown_entry, name);
171  }
172 
173  Entry& entry = m_dictionary[m_name_to_address[name]];
174 
175  const uint8_t type_size = Utils::get_type_size(entry.type);
176 
177  if (offset+type_size > 8) {
178  throw dictionary_error(dictionary_error::type::mapping_size, name,
179  "offset ("+std::to_string(offset)+") + type_size ("+std::to_string(type_size)+") > 8.");
180  }
181 
182 
183  ReceivePDOMapping *pdo_temp;
184 
185  {
186  std::lock_guard<std::mutex> lock(m_receive_pdo_mappings_mutex);
187  m_receive_pdo_mappings.push_front({cob_id,name,offset});
188  pdo_temp = &m_receive_pdo_mappings.front();
189  }
190 
191  ReceivePDOMapping& pdo = *pdo_temp;
192 
193  // TODO: this only works while add_pdo_received_callback takes callback by value.
194  auto binding = std::bind(&Device::pdo_received_callback, this, pdo, std::placeholders::_1);
195  m_core.pdo.add_pdo_received_callback(cob_id, std::move(binding));
196 
197 }
198 
199 
200 void Device::add_transmit_pdo_mapping(uint16_t cob_id, const std::vector<Mapping>& mappings, TransmissionType transmission_type, std::chrono::milliseconds repeat_time) {
201 
202  TransmitPDOMapping *pdo_temp;
203 
204  {
205  std::lock_guard<std::mutex> lock(m_transmit_pdo_mappings_mutex); // unlocks in case of exception
206  // Contructor can throw dictionary_error. Letting user handle this.
207  m_transmit_pdo_mappings.emplace_front(m_core, m_dictionary, m_name_to_address, cob_id, transmission_type, repeat_time, mappings);
208  pdo_temp = &m_transmit_pdo_mappings.front();
209  }
210 
211  TransmitPDOMapping& pdo = *pdo_temp;
212 
213  if (transmission_type==TransmissionType::ON_CHANGE) {
214 
215  for (const Mapping& mapping : pdo.mappings) {
216 
217  const std::string entry_name = Utils::escape(mapping.entry_name);
218 
219  // entry exists because check_correctness() == true.
220  Entry& entry = m_dictionary.at(m_name_to_address.at(entry_name));
221 
222  entry.add_value_changed_callback([entry_name, &pdo](const Value& value){
223  DEBUG_LOG("[Callback] Value of "<<entry_name<<" changed to "<<value);
224  pdo.send();
225  });
226  }
227 
228  } else {
229 
230  // transmission_type==TransmissionType::PERIODIC
231 
232  if (repeat_time == std::chrono::milliseconds(0)) {
233  WARN("[Device::add_transmit_pdo_mapping] Repeat time is 0. This could overload the bus.");
234  }
235 
236  pdo.transmitter = std::unique_ptr<std::thread>(new std::thread([&pdo, repeat_time](){
237 
238  while (true) {
239  DEBUG_LOG("[Timer thread] Sending periodic PDO.");
240  pdo.send();
241  std::this_thread::sleep_for(repeat_time);
242  }
243 
244  }));
245 
246  }
247 
248 }
249 
250 void Device::pdo_received_callback(const ReceivePDOMapping& mapping, std::vector<uint8_t> data) {
251 
252  DEBUG_LOG("[Device::pdo_received_callback] Received a PDO for mapping '"<<mapping.entry_name<<"'!");
253 
254  const std::string entry_name = Utils::escape(mapping.entry_name);
255  Entry& entry = m_dictionary[m_name_to_address[entry_name]];
256  const uint8_t offset = mapping.offset;
257  const uint8_t type_size = Utils::get_type_size(entry.type);
258 
259  if (data.size() < offset+type_size) {
260  // We don't throw an exception here, because this could be a network error.
261  WARN("[Device::pdo_received_callback] PDO has wrong size. Ignoring it...");
262  DUMP(data.size());
263  DUMP(offset);
264  DUMP(type_size);
265  return;
266  }
267 
268  DEBUG_LOG("Updating entry "<<entry.name<<".");
269  std::vector<uint8_t> bytes(data.begin()+offset, data.begin()+offset+type_size);
270  entry.set_value(Value(entry.type,bytes));
271 
272 }
273 
275  // Using the address here because this makes read_dictionary_from_eds() shorter...
276  uint32_t device_type = get_entry(0x1000); // Device type
277  return (device_type & 0xFFFF);
278 }
279 
280 Value Device::get_entry_via_sdo(uint32_t index, uint8_t subindex, Type type) {
281 
282  sdo_error last_error(sdo_error::type::unknown);
283 
284  for (size_t i=0; i<Config::repeats_on_sdo_timeout+1; ++i) {
285  try {
286  std::vector<uint8_t> data = m_core.sdo.upload(m_node_id, index, subindex);
287  return Value(type, data);
288  } catch (const sdo_error& error) {
289  last_error = error;
290  if (i<Config::repeats_on_sdo_timeout) {
291  DEBUG_LOG("[Device::get_entry_via_sdo] device " << std::to_string(m_node_id) << " " << error.what() <<" -> Repetition "<<std::to_string(i+1)
292  <<" of "<<std::to_string(Config::repeats_on_sdo_timeout+1)<<".");
293  std::this_thread::sleep_for(std::chrono::milliseconds(kaco::Config::sdo_response_timeout_ms));
294  }
295  }
296  }
297 
298  throw sdo_error(sdo_error::type::response_timeout, "Device::get_entry_via_sdo() device " + std::to_string(m_node_id) + " failed after "
299  +std::to_string(Config::repeats_on_sdo_timeout+1)+" repeats. Last error: "+std::string(last_error.what()));
300 
301 }
302 
303 void Device::set_entry_via_sdo(uint32_t index, uint8_t subindex, const Value& value) {
304 
305  sdo_error last_error(sdo_error::type::unknown);
306 
307  for (size_t i=0; i<Config::repeats_on_sdo_timeout+1; ++i) {
308  try {
309  const auto& bytes = value.get_bytes();
310  m_core.sdo.download(m_node_id,index,subindex,bytes.size(),bytes);
311  return;
312  } catch (const sdo_error& error) {
313  last_error = error;
314  if (i<Config::repeats_on_sdo_timeout) {
315  DEBUG_LOG("[Device::set_entry_via_sdo] device " << std::to_string(m_node_id) << " " << error.what() << " -> Repetition "<<std::to_string(i+1)
316  <<" of "<<std::to_string(Config::repeats_on_sdo_timeout+1)<<".");
317  std::this_thread::sleep_for(std::chrono::milliseconds(kaco::Config::sdo_response_timeout_ms));
318  }
319  }
320  }
321 
322  throw sdo_error(sdo_error::type::response_timeout, "Device::set_entry_via_sdo() device " + std::to_string(m_node_id) + " failed after "
323  +std::to_string(Config::repeats_on_sdo_timeout+1)+" repeats. Last error: "+std::string(last_error.what()));
324 
325 }
326 
328 
329  if (!m_eds_library.ready()) {
330  throw canopen_error("[Device::load_dictionary_from_library] EDS library is not available.");
331  }
332 
333  DEBUG_LOG("Device::load_dictionary_from_library()...");
334  std::string eds_path = "";
335 
336  // First, we try to load manufacturer specific entries.
337 
339  const bool success = m_eds_library.load_manufacturer_eds(*this);
341 
342  if (success) {
343  DEBUG_LOG("[Device::load_dictionary_from_library] Device "<<std::to_string(m_node_id)<<": Successfully loaded manufacturer-specific dictionary: " << m_eds_library.get_most_recent_eds_file_path());
344  DEBUG_LOG("[Device::load_dictionary_from_library] Now we will add additional mappings from standard conformal entry names to the entries...");
345  eds_path = m_eds_library.get_most_recent_eds_file_path();
347  } else {
348  DEBUG_LOG("[Device::load_dictionary_from_library] Device "<<std::to_string(m_node_id)<<": There is no manufacturer-specific EDS file available. Going on with the default dictionary...");
349  Config::eds_reader_just_add_mappings = false; // should be already false...
350  }
351 
352  // Load entries like they are defined in the CiA CANopen standard documents...
353  // Either just the names are added or the whole dictionary depending on Config::eds_reader_just_add_mappings
354  load_cia_dictionary();
355  if (eds_path.empty()) {
356  // no manufacturer EDS...
357  eds_path = m_eds_library.get_most_recent_eds_file_path();
358  }
360 
361  return eds_path;
362 
363 }
364 
365 void Device::load_cia_dictionary() {
367  const uint16_t profile = get_device_profile_number();
368  if (m_eds_library.load_default_eds(profile)) {
369  DEBUG_LOG("[Device::load_dictionary_from_library] Device "<<std::to_string(m_node_id)<<": Successfully loaded profile-specific dictionary: " << m_eds_library.get_most_recent_eds_file_path());
370  } else {
371  Config::eds_library_clear_dictionary = false; // should be already false...
372  if (m_eds_library.load_mandatory_entries()) {
373  DEBUG_LOG("[Device::load_dictionary_from_library] Device "<<std::to_string(m_node_id)<<": Successfully loaded mandatory entries: " << m_eds_library.get_most_recent_eds_file_path());
374  } else {
375  throw canopen_error("Could not load mandatory CiA 301 dictionary entries for device with ID "+std::to_string(m_node_id)+". This can break various parts of KaCanOpen!");
376  }
377  }
379 }
380 
381 void Device::load_dictionary_from_eds(const std::string& path) {
382 
383  m_eds_library.reset_dictionary();
384  Config::eds_reader_just_add_mappings = false; // should be already false...
385  Config::eds_reader_mark_entries_as_generic = false; // should be already false...
386  EDSReader reader(m_dictionary, m_name_to_address);
387 
388  if (!reader.load_file(path)) {
389  throw canopen_error("[EDSLibrary::load_dictionary_from_eds] Loading file not successful: "+path);
390  }
391 
392  if (!reader.import_entries()) {
393  throw canopen_error("[EDSLibrary::load_dictionary_from_eds] Importing entries failed for file "+path);
394  }
395 
396  // Load generic names from the standard CiA profiles on top of the existing dictionary.
397  if (m_eds_library.ready()) {
398  // Wo know nothing about the EDS... No mandatory entries here. At least 0x1000 is required for load_cia_dictionary():
399  if (!has_entry(0x1000)) {
400  add_entry(0x1000,0,"device_type",Type::uint32,AccessType::read_only);
401  }
403  load_cia_dictionary();
405  } else {
406  WARN("[Device::load_dictionary_from_eds] Cannot load generic entry names because EDS library is not available.");
407  }
408 
409 }
410 
412  const uint16_t profile = get_device_profile_number();
413  if (Profiles::operations.count(profile)>0) {
414  m_operations.insert(Profiles::operations.at(profile).cbegin(),Profiles::operations.at(profile).cend());
415  return true;
416  }
417  return false;
418 }
419 
420 void Device::add_operation(const std::string& operation_name, const Operation& operation) {
421  const std::string name = Utils::escape(operation_name);
422  if (m_operations.count(name)>0) {
423  WARN("[Device::add_operation] Overwriting operation \""<<name<<"\".");
424  }
425  m_operations[name] = operation;
426 }
427 
428 Value Device::execute(const std::string& operation_name, const Value& argument) {
429  const std::string name = Utils::escape(operation_name);
430  if (m_operations.count(name) == 0) {
431  throw dictionary_error(dictionary_error::type::unknown_operation, name);
432  }
433  return m_operations[name](*this,argument);
434 }
435 
437  const uint16_t profile = get_device_profile_number();
438  if (Profiles::constants.count(profile)>0) {
439  m_constants.insert(Profiles::constants.at(profile).cbegin(),Profiles::constants.at(profile).cend());
440  return true;
441  }
442  return false;
443 }
444 
445 void Device::add_constant(const std::string& constant_name, const Value& constant) {
446  const std::string name = Utils::escape(constant_name);
447  if (m_constants.count(name)>0) {
448  WARN("[Device::add_constant] Overwriting constant \""<<name<<"\".");
449  }
450  m_constants[name] = constant;
451 }
452 
453 const Value& Device::get_constant(const std::string& constant_name) const {
454  const std::string name = Utils::escape(constant_name);
455  if (m_constants.count(name) == 0) {
456  throw dictionary_error(dictionary_error::type::unknown_constant, name);
457  }
458  return m_constants.at(name);
459 }
460 
462 
463  using EntryRef = std::reference_wrapper<const kaco::Entry>;
464  std::vector<EntryRef> entries;
465 
466  for (const auto& pair : m_dictionary) {
467  if (!pair.second.disabled) {
468  entries.push_back(std::ref(pair.second));
469  }
470  }
471 
472  // sort by index and subindex
473  std::sort(entries.begin(), entries.end(),
474  [](const EntryRef& l, const EntryRef& r) { return l.get()<r.get(); });
475 
476  for (const auto& entry : entries) {
477  entry.get().print();
478  }
479 
480 }
481 
483  for (auto& pair : m_dictionary) {
484  try {
485  get_entry(pair.second.name);
486  } catch (const sdo_error& error) {
487  pair.second.disabled = true;
488  DEBUG_LOG("[Device::read_complete_dictionary] SDO error for field "<<pair.second.name<<": "<<error.what()<<" -> disable entry.");
489  }
490  }
491 }
492 
493 
494 const Value Device::m_dummy_value = Value();
495 
496 } // end namespace kaco
This type of exception is thrown if there are problems accessing the object dictionary or arguments d...
void print_dictionary() const
Prints the dictionary together with currently cached values to command line.
Definition: device.cpp:461
virtual const char * what() const noexceptoverride
Returns error description.
Definition: sdo_error.cpp:105
void add_transmit_pdo_mapping(uint16_t cob_id, const std::vector< Mapping > &mappings, TransmissionType transmission_type=TransmissionType::ON_CHANGE, std::chrono::milliseconds repeat_time=std::chrono::milliseconds(0))
Adds a transmit PDO mapping. This means values from the dictionary cache are sent to the device...
Definition: device.cpp:200
void send_nmt_message(uint8_t node_id, Command cmd)
Sends a NMT message to a given device.
Definition: nmt.cpp:47
uint8_t offset
index of the first mapped byte in the PDO message
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
void add_value_changed_callback(ValueChangedCallback callback)
Registers a given function to be called when the value is changed.
Definition: entry.cpp:110
Tuple of object dictionary index and subindex.
Definition: address.h:41
Type type
Data type of the value.
Definition: entry.h:129
void add_operation(const std::string &coperation_name, const Operation &operation)
Adds a convenience operation.
Definition: device.cpp:420
This class represents a mapping from one or more dictionary entries to one transmit PDO...
NMT nmt
The NMT sub-protocol.
Definition: core.h:109
PDO pdo
The PDO sub-protocol.
Definition: core.h:115
bool import_entries()
Import entries from the EDS file into the given dictionary.
Definition: eds_reader.cpp:67
const Value & get_value() const
Returns the value.
Definition: entry.cpp:94
void download(uint8_t node_id, uint16_t index, uint8_t subindex, uint32_t size, const std::vector< uint8_t > &bytes)
SDO download: Write value into remote device's object dictionary.
Definition: sdo.cpp:50
static bool eds_reader_mark_entries_as_generic
If this is set to true, EDSReader will mark all entries as generic (Entry::is_generic) This is used i...
Definition: global_config.h:51
WriteAccessMethod write_access_method
Standard method for writing this entry. Used by Device::set_entry().
Definition: entry.h:140
void set_value(const Value &value)
Sets the value.
Definition: entry.cpp:63
static const std::map< uint16_t, std::map< std::string, const Operation > > operations
Convenience operations for CiA profiles. Type: map < profile number , map < operation name ...
Definition: profiles.h:56
This class allows reading EDS files (like standardized in CiA 306) and inserting all contained entrie...
Definition: eds_reader.h:50
uint16_t index
index in dictionary
Definition: entry.h:118
void add_entry(const uint16_t index, const uint8_t subindex, const std::string &name, const Type type, const AccessType access_type)
Adds an entry to the dictionary. You have to take care that exactly this entry exists on the device f...
Definition: device.cpp:149
void start()
Starts the node via NMT protocol and loads mandatory entries, operations, and constants.
Definition: device.cpp:54
void add_receive_pdo_mapping(uint16_t cob_id, const std::string &entry_name, uint8_t offset)
Adds a receive PDO mapping. This means values sent by the device via PDO are saved into the dictionar...
Definition: device.cpp:163
bool load_default_eds(uint16_t device_profile_number)
Loads entries defined in generic CiA profile EDS files.
std::string name
Human-readable name Should be escaped for consitency using Utils::escape().
Definition: entry.h:126
static std::string escape(const std::string &str)
Converts entry names to lower case and replaces all spaces and '-' by underscores.
Definition: utils.cpp:166
std::unique_ptr< std::thread > transmitter
The transmitter thread.
uint8_t subindex
subindex in dictionary. if is_array==true, this variable is not used
Definition: entry.h:122
This type of exception is thrown when there are problems while accessing devices via SDO...
Definition: sdo_error.h:48
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
uint16_t get_device_profile_number()
Returns the CiA profile number.
Definition: device.cpp:274
void add_constant(const std::string &constant_name, const Value &constant)
Adds a constant.
Definition: device.cpp:445
bool load_mandatory_entries()
Loads mandatory dictionary entries defined in CiA 301 standard.
Definition: eds_library.cpp:96
std::string entry_name
Name of the mapped dictionary entry.
Definition: mapping.h:46
~Device()
Destructor.
Definition: device.cpp:51
void send() const
Sends the PDO.
This class implements the Core of KaCanOpen It communicates with the CAN driver, sends CAN messages a...
Definition: core.h:59
static size_t sdo_response_timeout_ms
Timeout in milliseconds when waiting for an SDO response.
Definition: global_config.h:44
bool lookup_library(std::string path="")
Finds EDS library on disk.
Definition: eds_library.cpp:61
static std::string type_to_string(Type type)
Converts data types to a string.
Definition: utils.cpp:42
static size_t repeats_on_sdo_timeout
Number of repetitions when an SDO timeout occurs in SDO download/upload.
Definition: global_config.h:47
void add_pdo_received_callback(uint16_t cob_id, PDOReceivedCallback::Callback callback)
Adds a callback which will be called when a PDO has been received with the given COB-ID.
Definition: pdo.cpp:102
uint8_t get_node_id() const
Returns the node ID of the device.
Definition: device.cpp:74
ReadAccessMethod read_access_method
Standard method for reading this entry. Used by Device::get_entry().
Definition: entry.h:136
Type type
Tyoe of the value.
Definition: value.h:56
Value execute(const std::string &operation_name, const Value &argument=m_dummy_value)
Executes a convenience operation. It must exist due to a previous load_operations() or add_operation(...
Definition: device.cpp:428
bool load_manufacturer_eds(Device &device)
Loads entries defined in device specific EDS files proviced by manufacturers.
std::string load_dictionary_from_library()
Tries to load the most specific EDS file available in KaCanOpen's internal EDS library. This is either device specific, CiA profile specific, or mandatory CiA 301.
Definition: device.cpp:327
This struct represents a mapping from one receive PDO to one dictionary entry. There may be multiple ...
This class represents an entry in the object dictionary of a device.
Definition: entry.h:53
bool load_operations()
Loads convenience operations associated with the device profile.
Definition: device.cpp:411
SDO sdo
The SDO sub-protocol.
Definition: core.h:112
bool ready() const
Checks if lookup_library() was successful.
void print() const
Prints relevant information concerning this entry on standard output - name, index, possibly value, ... This is used by Device::print_dictionary()
Definition: entry.cpp:115
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
void set_entry(const std::string &entry_name, const Value &value, const WriteAccessMethod access_method=WriteAccessMethod::use_default)
Sets the value of a dictionary entry by name internally. If the entry is configured to send an SDO on...
Definition: device.cpp:123
bool load_file(std::string filename)
Loads an EDS file from file system.
Definition: eds_reader.cpp:54
uint8_t subindex
Sub-index.
Definition: address.h:47
const Value & get_constant(const std::string &constant_name) const
Returns a constant. It must exist due to a previous load_constants() or add_constant() call...
Definition: device.cpp:453
static bool eds_reader_just_add_mappings
If this is set to true, EDSReader won't create any entries, but just adds name-to-address mappings fo...
Definition: global_config.h:55
void reset_dictionary()
Resets the dictionary and the name-address mapping.
std::string entry_name
Name of the dictionary entry.
Type get_entry_type(const std::string &entry_name)
Returns the type of a dictionary entry identified by name as it is defined in the local dictionary...
Definition: device.cpp:87
void read_complete_dictionary()
Fetches all dictionary entries from the device. Afterwards, all values exist in cache and can for exa...
Definition: device.cpp:482
std::vector< uint8_t > upload(uint8_t node_id, uint16_t index, uint8_t subindex)
SDO download: Get value from remote device's object dictionary.
Definition: sdo.cpp:84
void load_dictionary_from_eds(const std::string &path)
Loads the dictionary from a custom EDS file.
Definition: device.cpp:381
bool has_entry(const std::string &entry_name)
Returns true if the entry is contained in the device dictionary.
Definition: device.cpp:78
std::string get_most_recent_eds_file_path() const
Returns the path to the most recently loaded EDS file.
Device(Core &core, uint8_t node_id)
Constructor.
Definition: device.cpp:48
std::function< Value(Device &, const Value &)> Operation
Type of a operation. See Profiles::Operation in profiles.h.
Definition: device.h:88
std::vector< Mapping > mappings
Mapped entries with offset (see Mapping class)
bool load_constants()
Loads constants associated with the device profile.
Definition: device.cpp:436
uint16_t index
Index.
Definition: address.h:44
This struct represents one mapped entry inside a TransmitPDOMapping.
Definition: mapping.h:43
static uint8_t get_type_size(Type type)
Returns the size of a data type in bytes.
Definition: utils.cpp:112
This class contains a value to be stored in the object dictionary. The value can have one of the type...
Definition: value.h:53
static const std::map< uint16_t, std::map< std::string, const Value > > constants
Constants for CiA profiles. Type: map < profile number , map < operation name , constant value > > ...
Definition: profiles.h:60