objdict.cpp
Go to the documentation of this file.
00001 #include <canopen_master/objdict.h>
00002 #include <socketcan_interface/string.h>
00003 #include <boost/property_tree/ptree.hpp>
00004 #include <boost/property_tree/ini_parser.hpp>
00005 #include <boost/foreach.hpp>
00006 #include <boost/lexical_cast.hpp>
00007 #include <boost/algorithm/string.hpp>
00008 
00009 namespace canopen{
00010     size_t hash_value(ObjectDict::Key const& k)  { return k.hash;  }
00011     std::ostream& operator<<(std::ostream& stream, const ObjectDict::Key &k) { return stream << std::string(k); }
00012 }
00013 
00014 using namespace canopen;
00015 
00016 template<> const String & HoldAny::get() const{
00017     return buffer;
00018 }
00019 
00020 template<> String & ObjectStorage::Data::access(){
00021     if(!valid){
00022         THROW_WITH_KEY(std::length_error("buffer not valid") , key);
00023     }
00024     return buffer;
00025 }
00026 template<> String & ObjectStorage::Data::allocate(){
00027     buffer.resize(0);
00028     valid = true;
00029     return buffer;
00030 }
00031 
00032 size_t ObjectDict::Key::fromString(const std::string &str){
00033     uint16_t index = 0;
00034     uint8_t sub_index = 0;
00035     int num = sscanf(str.c_str(),"%hxsub%hhx", &index, &sub_index);
00036     return (index << 16) | (num==2?sub_index:0xFFFF);
00037 }
00038 ObjectDict::Key::operator std::string() const{
00039     std::stringstream sstr;
00040     sstr << std::hex << index();
00041     if(hasSub()) sstr << "sub" << (int) sub_index();
00042     return sstr.str();
00043 }
00044 void ObjectStorage::Data::init(){
00045     boost::mutex::scoped_lock lock(mutex);
00046 
00047     if(entry->init_val.is_empty()) return;
00048 
00049     if(valid && !entry->def_val.is_empty() && buffer != entry->def_val.data()) return; // buffer was changed
00050 
00051     if(!valid || buffer != entry->init_val.data()){
00052         buffer = entry->init_val.data();
00053         valid = true;
00054         if(entry->writable && (entry->def_val.is_empty() || entry->init_val.data() != entry->def_val.data()))
00055             write_delegate(*entry, buffer);
00056     }
00057 }
00058 void ObjectStorage::Data::force_write(){
00059     boost::mutex::scoped_lock lock(mutex);
00060     
00061     if(!valid && entry->readable){
00062         read_delegate(*entry, buffer);
00063         valid = true;
00064     }
00065     if(valid) write_delegate(*entry, buffer);
00066 }
00067 
00068 void ObjectStorage::Data::reset(){
00069     boost::mutex::scoped_lock lock(mutex);
00070     if(!entry->def_val.is_empty() && entry->def_val.type() == type_guard){
00071         buffer = entry->def_val.data();
00072         valid = true;
00073     }else{
00074         valid = false;
00075     }
00076 }
00077 
00078 bool ObjectDict::iterate(boost::unordered_map<Key, boost::shared_ptr<const Entry> >::const_iterator &it) const{
00079     if(it != boost::unordered_map<Key, boost::shared_ptr<const Entry> >::const_iterator()){
00080         ++it;
00081     }else it = dict_.begin();
00082     return it != dict_.end();
00083 }
00084 void set_access( ObjectDict::Entry &entry, std::string access){
00085     boost::algorithm::to_lower(access);
00086     entry.constant = false;
00087     if(access == "ro"){
00088         entry.readable = true;
00089         entry.writable = false;
00090     }else if (access == "wo"){
00091         entry.readable = false;
00092         entry.writable = true;
00093     }else if (access == "rw"){
00094         entry.readable = true;
00095         entry.writable = true;
00096     }else if (access == "rwr"){
00097         entry.readable = true;
00098         entry.writable = true;
00099     }else if (access == "rww"){
00100         entry.readable = true;
00101         entry.writable = true;
00102     }else if (access == "const"){
00103         entry.readable = true;
00104         entry.writable = false;
00105         entry.constant = true;
00106     }else{
00107         THROW_WITH_KEY(ParseException("Cannot determine access"), ObjectDict::Key(entry));
00108     }
00109 }
00110 
00111 template<typename T> T int_from_string(const std::string &s);
00112 
00113 template<> int8_t int_from_string(const std::string &s){
00114     return strtol(s.c_str(), 0, 0);
00115 }
00116 template<> uint8_t int_from_string(const std::string &s){
00117     return strtoul(s.c_str(), 0, 0);
00118 }
00119 template<> int16_t int_from_string(const std::string &s){
00120     return strtol(s.c_str(), 0, 0);
00121 }
00122 template<> uint16_t int_from_string(const std::string &s){
00123     return strtoul(s.c_str(), 0, 0);
00124 }
00125 template<> int32_t int_from_string(const std::string &s){
00126     return strtol(s.c_str(), 0, 0);
00127 }
00128 template<> uint32_t int_from_string(const std::string &s){
00129     return strtoul(s.c_str(), 0, 0);
00130 }
00131 
00132 template<> int64_t int_from_string(const std::string &s){
00133     return strtoll(s.c_str(), 0, 0);
00134 }
00135 template<> uint64_t int_from_string(const std::string &s){
00136     return strtoull(s.c_str(), 0, 0);
00137 }
00138 
00139 template<typename T> HoldAny parse_int(boost::property_tree::iptree &pt, const std::string &key){
00140     if(pt.count(key) == 0) return HoldAny(TypeGuard::create<T>());
00141                                           
00142     std::string str = boost::trim_copy(pt.get<std::string>(key));
00143     if(boost::istarts_with(str,"$NODEID")){
00144         return HoldAny(NodeIdOffset<T>(int_from_string<T>(boost::trim_copy(str.substr(str.find("+",7)+1)))));
00145     }else return HoldAny(int_from_string<T>(str));
00146 }
00147 
00148 template<typename T> HoldAny parse_octets(boost::property_tree::iptree &pt, const std::string &key){
00149     std::string out;
00150     if(pt.count(key) == 0 || can::hex2buffer(out,pt.get<std::string>(key), true)) return HoldAny(TypeGuard::create<T>());
00151     return HoldAny(T(out));
00152 }
00153 
00154 template<typename T> HoldAny parse_typed_value(boost::property_tree::iptree &pt, const std::string &key){
00155     if(pt.count(key) == 0) return HoldAny(TypeGuard::create<T>());
00156     return HoldAny(pt.get<T>(key));
00157 }
00158 template<> HoldAny parse_typed_value<String>(boost::property_tree::iptree &pt, const std::string &key){
00159     if(pt.count(key) == 0) return HoldAny(TypeGuard::create<String>());
00160     return HoldAny(String(pt.get<std::string>(key)));
00161 }
00162 
00163 struct ReadAnyValue{
00164     template<const ObjectDict::DataTypes dt> static HoldAny func(boost::property_tree::iptree &pt, const std::string &key);
00165     static HoldAny read_value(boost::property_tree::iptree &pt, uint16_t data_type, const std::string &key){
00166         return branch_type<ReadAnyValue, HoldAny (boost::property_tree::iptree &, const std::string &)>(data_type)(pt, key);
00167     }
00168 };
00169 template<> HoldAny ReadAnyValue::func<ObjectDict::DEFTYPE_INTEGER8>(boost::property_tree::iptree &pt, const std::string &key){  return parse_int<int8_t>(pt,key); }
00170 template<> HoldAny ReadAnyValue::func<ObjectDict::DEFTYPE_INTEGER16>(boost::property_tree::iptree &pt, const std::string &key){  return parse_int<int16_t>(pt,key); }
00171 template<> HoldAny ReadAnyValue::func<ObjectDict::DEFTYPE_INTEGER32>(boost::property_tree::iptree &pt, const std::string &key){  return parse_int<int32_t>(pt,key); }
00172 template<> HoldAny ReadAnyValue::func<ObjectDict::DEFTYPE_INTEGER64>(boost::property_tree::iptree &pt, const std::string &key){  return parse_int<int64_t>(pt,key); }
00173 
00174 template<> HoldAny ReadAnyValue::func<ObjectDict::DEFTYPE_UNSIGNED8>(boost::property_tree::iptree &pt, const std::string &key){  return parse_int<uint8_t>(pt,key); }
00175 template<> HoldAny ReadAnyValue::func<ObjectDict::DEFTYPE_UNSIGNED16>(boost::property_tree::iptree &pt, const std::string &key){  return parse_int<uint16_t>(pt,key); }
00176 template<> HoldAny ReadAnyValue::func<ObjectDict::DEFTYPE_UNSIGNED32>(boost::property_tree::iptree &pt, const std::string &key){  return parse_int<uint32_t>(pt,key); }
00177 template<> HoldAny ReadAnyValue::func<ObjectDict::DEFTYPE_UNSIGNED64>(boost::property_tree::iptree &pt, const std::string &key){  return parse_int<uint64_t>(pt,key); }
00178 
00179 template<> HoldAny ReadAnyValue::func<ObjectDict::DEFTYPE_DOMAIN>(boost::property_tree::iptree &pt, const std::string &key)
00180 { return parse_octets<ObjectStorage::DataType<ObjectDict::DEFTYPE_DOMAIN>::type>(pt,key); }
00181 
00182 template<> HoldAny ReadAnyValue::func<ObjectDict::DEFTYPE_OCTET_STRING>(boost::property_tree::iptree &pt, const std::string &key)
00183 { return parse_octets<ObjectStorage::DataType<ObjectDict::DEFTYPE_OCTET_STRING>::type>(pt,key); }
00184 
00185 template<const ObjectDict::DataTypes dt> HoldAny ReadAnyValue::func(boost::property_tree::iptree &pt, const std::string &key){
00186     return parse_typed_value<typename ObjectStorage::DataType<dt>::type>(pt, key);
00187 }
00188 
00189 template<typename T> void read_optional(T& var, boost::property_tree::iptree &pt, const std::string &key){
00190     var = pt.get(key, T());
00191 }
00192 
00193 template<typename T> void read_integer(T& var, boost::property_tree::iptree &pt, const std::string &key){
00194     var = int_from_string<T>(pt.get<std::string>(key));
00195 }
00196 
00197 template<typename T> T read_integer(boost::property_tree::iptree &pt, const std::string &key){
00198     return int_from_string<T>(pt.get<std::string>(key, std::string()));
00199 }
00200 
00201 
00202 void read_var(ObjectDict::Entry &entry, boost::property_tree::iptree &object){
00203         read_integer<uint16_t>(entry.data_type, object, "DataType");
00204         entry.mappable = object.get<bool>("PDOMapping", false);
00205         try{
00206             set_access(entry, object.get<std::string>("AccessType"));
00207         }
00208         catch(...){
00209             THROW_WITH_KEY(ParseException("No AccessType") , ObjectDict::Key(entry));
00210 
00211         }
00212         entry.def_val = ReadAnyValue::read_value(object, entry.data_type, "DefaultValue");
00213         entry.init_val = ReadAnyValue::read_value(object, entry.data_type, "ParameterValue");
00214 }
00215 
00216 void parse_object(boost::shared_ptr<ObjectDict> dict, boost::property_tree::iptree &pt, const std::string &name, const uint8_t* sub_index = 0){
00217         boost::optional<boost::property_tree::iptree&> object =  pt.get_child_optional(name.substr(2));
00218         if(!object) return;
00219 
00220         boost::shared_ptr<ObjectDict::Entry> entry = boost::make_shared<ObjectDict::Entry>();
00221         entry->index = int_from_string<uint16_t>(name);
00222         entry->obj_code = ObjectDict::Code(int_from_string<uint16_t>(object->get<std::string>("ObjectType", boost::lexical_cast<std::string>((uint16_t)ObjectDict::VAR))));
00223         entry->desc = object->get<std::string>("Denotation",object->get<std::string>("ParameterName"));
00224         
00225         // std::cout << name << ": "<< entry->desc << std::endl;
00226         if(entry->obj_code == ObjectDict::VAR || entry->obj_code == ObjectDict::DOMAIN_DATA || sub_index){
00227             entry->sub_index = sub_index? *sub_index: 0;
00228             read_var(*entry, *object);
00229             dict->insert(sub_index != 0, entry);
00230         }else if(entry->obj_code == ObjectDict::ARRAY || entry->obj_code == ObjectDict::RECORD){
00231             uint8_t subs = read_integer<uint8_t>(*object, "CompactSubObj");
00232             if(subs){ // compact
00233                 dict->insert(true, boost::make_shared<const canopen::ObjectDict::Entry>(entry->index, 0, ObjectDict::DEFTYPE_UNSIGNED8, "NrOfObjects", true, false, false, HoldAny(subs)));
00234 
00235                 read_var(*entry, *object);
00236                 
00237                 for(uint8_t i=1; i< subs; ++i){
00238                     std::string subname = pt.get<std::string>(name.substr(2)+"Name." + boost::lexical_cast<std::string>((int)i),entry->desc + boost::lexical_cast<std::string>((int)i));
00239                     subname = pt.get<std::string>(name.substr(2)+"Denotation." + boost::lexical_cast<std::string>((int)i), subname);
00240                     
00241                     dict->insert(true, boost::make_shared<const canopen::ObjectDict::Entry>(entry->index, i, entry->data_type, name, entry->readable, entry->writable, entry->mappable, entry->def_val,
00242                        ReadAnyValue::read_value(pt, entry->data_type, name.substr(2)+"Value." + boost::lexical_cast<std::string>((int)i))));
00243                 }
00244             }else{
00245                 read_integer(subs, *object, "SubNumber");
00246                 for(uint8_t i=0; i< subs; ++i){
00247                    std::stringstream buf;
00248                    buf << name << "sub" << std::hex << int(i);
00249                    // std::cout << "added " << buf.str() <<  "  " << int(i) << std::endl;
00250                    parse_object(dict, pt, buf.str(), &i);
00251                 }
00252             }
00253         }else{
00254             THROW_WITH_KEY(ParseException("Object type not supported") , ObjectDict::Key(*entry));
00255         }
00256 }
00257 void parse_objects(boost::shared_ptr<ObjectDict> dict, boost::property_tree::iptree &pt, const std::string &key){
00258     if(!pt.count(key)) return;
00259     
00260     boost::property_tree::iptree objects = pt.get_child(key);
00261     uint16_t count = read_integer<uint16_t>(objects, "SupportedObjects");
00262     for(uint16_t i=0; i < count; ++i){
00263         std::string name = objects.get<std::string>(boost::lexical_cast<std::string>(i+1));
00264         parse_object(dict, pt, name);
00265     }
00266 }
00267 boost::shared_ptr<ObjectDict> ObjectDict::fromFile(const std::string &path, const ObjectDict::Overlay &overlay){
00268     DeviceInfo info;
00269     boost::property_tree::iptree pt;
00270     boost::shared_ptr<ObjectDict> dict;
00271     
00272     boost::property_tree::read_ini(path, pt);
00273     
00274     boost::property_tree::iptree di = pt.get_child("DeviceInfo");
00275     
00276     read_optional(info.vendor_name, di, "VendorName");
00277     read_optional(info.vendor_number, di, "VendorNumber");
00278     read_optional(info.product_name, di, "ProductName");
00279     read_optional(info.product_number, di, "ProductNumber");
00280     read_optional(info.revision_number, di, "RevisionNumber");
00281     read_optional(info.order_code, di, "OrderCode");
00282     read_optional(info.simple_boot_up_master, di, "SimpleBootUpMaster");
00283     read_optional(info.simple_boot_up_slave, di, "SimpleBootUpSlave");
00284     read_optional(info.granularity, di, "Granularity");
00285     read_optional(info.dynamic_channels_supported, di, "DynamicChannelsSupported");
00286     read_optional(info.group_messaging, di, "GroupMessaging");
00287     read_optional(info.nr_of_rx_pdo, di, "NrOfRXPDO");
00288     read_optional(info.nr_of_tx_pdo, di, "NrOfTXPDO");
00289     read_optional(info.lss_supported, di, "LSS_Supported");
00290 
00291     boost::unordered_set<uint32_t> baudrates;
00292     boost::unordered_set<uint16_t> dummy_usage;
00293 
00294     BOOST_FOREACH(boost::property_tree::iptree::value_type &v, di){
00295         if(v.first.find("BaudRate_") == 0){
00296             uint16_t rate = int_from_string<uint16_t>(v.first.substr(9));
00297             if(v.second.get_value<bool>())
00298                 info.baudrates.insert(rate * 1000);
00299         }
00300     }
00301 
00302     if(pt.count("DummyUsage")){
00303         BOOST_FOREACH(boost::property_tree::iptree::value_type &v, pt.get_child("DummyUsage")){
00304             if(v.first.find("Dummy") == 0){
00305                 // std::cout << ("0x"+v.first.substr(5)) << std::endl;
00306                 uint16_t dummy = int_from_string<uint16_t>("0x"+v.first.substr(5));
00307                 if(v.second.get_value<bool>())
00308                     info.dummy_usage.insert(dummy);
00309             }
00310         }
00311     }
00312 
00313     dict = boost::make_shared<ObjectDict>(info);
00314 
00315     for(Overlay::const_iterator it= overlay.begin(); it != overlay.end(); ++it){
00316         pt.get_child(it->first).put("ParameterValue", it->second);
00317     }
00318 
00319     parse_objects(dict, pt, "MandatoryObjects");
00320     parse_objects(dict, pt, "OptionalObjects");
00321     parse_objects(dict, pt, "ManufacturerObjects");
00322     
00323     return dict;
00324 }
00325 
00326 size_t ObjectStorage::map(const boost::shared_ptr<const ObjectDict::Entry> &e, const ObjectDict::Key &key, const ReadDelegate & read_delegate, const WriteDelegate & write_delegate){
00327     boost::unordered_map<ObjectDict::Key, boost::shared_ptr<Data> >::iterator it = storage_.find(key);
00328     
00329     if(it == storage_.end()){
00330         
00331         boost::shared_ptr<Data> data;
00332         
00333         
00334         if(!e->def_val.type().valid()){
00335             THROW_WITH_KEY(std::bad_cast() , key);
00336         }
00337         
00338         data = boost::make_shared<Data>(key, e,e->def_val.type(),read_delegate_, write_delegate_);
00339         
00340         std::pair<boost::unordered_map<ObjectDict::Key, boost::shared_ptr<Data> >::iterator, bool>  ok = storage_.insert(std::make_pair(key, data));
00341         it = ok.first;
00342         it->second->reset();
00343 
00344     }
00345 
00346     if(read_delegate && write_delegate){
00347         it->second->set_delegates(read_delegate_, write_delegate);
00348         it->second->force_write(); // update buffer
00349         it->second->set_delegates(read_delegate, write_delegate_);
00350     }else if(write_delegate) {
00351         it->second->set_delegates(read_delegate_, write_delegate);
00352         it->second->force_write(); // update buffer
00353     }else if(read_delegate){
00354         it->second->set_delegates(read_delegate, write_delegate_);
00355     }
00356     return it->second->size();
00357 }
00358 
00359 size_t ObjectStorage::map(uint16_t index, uint8_t sub_index, const ReadDelegate & read_delegate, const WriteDelegate & write_delegate){
00360     boost::mutex::scoped_lock lock(mutex_);
00361     
00362     try{
00363         ObjectDict::Key key(index,sub_index);
00364         const boost::shared_ptr<const ObjectDict::Entry> e = dict_->get(key);
00365         return map(e, key,read_delegate, write_delegate);
00366     }
00367     catch(std::out_of_range) {
00368         if(sub_index != 0) throw;
00369         
00370         ObjectDict::Key key(index);
00371         const boost::shared_ptr<const ObjectDict::Entry> e = dict_->get(key);
00372         return map(e, key, read_delegate, write_delegate);
00373     }
00374 }
00375 
00376 ObjectStorage::ObjectStorage(boost::shared_ptr<const ObjectDict> dict, uint8_t node_id, ReadDelegate read_delegate, WriteDelegate write_delegate)
00377 :read_delegate_(read_delegate), write_delegate_(write_delegate), dict_(dict), node_id_(node_id){
00378     assert(dict_);
00379     assert(!read_delegate_.empty());
00380     assert(!write_delegate_.empty());
00381 }
00382     
00383 void ObjectStorage::init_nolock(const ObjectDict::Key &key, const boost::shared_ptr<const ObjectDict::Entry> &entry){
00384 
00385     if(!entry->init_val.is_empty()){
00386         boost::unordered_map<ObjectDict::Key, boost::shared_ptr<Data> >::iterator it = storage_.find(key);
00387         
00388         if(it == storage_.end()){
00389             boost::shared_ptr<Data> data = boost::make_shared<Data>(key,entry, entry->init_val.type(), read_delegate_, write_delegate_);
00390             std::pair<boost::unordered_map<ObjectDict::Key, boost::shared_ptr<Data> >::iterator, bool>  ok = storage_.insert(std::make_pair(key, data));
00391             it = ok.first;
00392             if(!ok.second){
00393                 THROW_WITH_KEY(std::bad_alloc() , key);
00394             }
00395         }
00396         it->second->init();
00397     }
00398 }
00399 void ObjectStorage::init(const ObjectDict::Key &key){
00400     boost::mutex::scoped_lock lock(mutex_);
00401     init_nolock(key, dict_->get(key));
00402 }
00403 void ObjectStorage::init_all(){
00404     boost::mutex::scoped_lock lock(mutex_);
00405 
00406     boost::unordered_map<ObjectDict::Key, boost::shared_ptr<const ObjectDict::Entry> >::const_iterator entry_it;
00407     while(dict_->iterate(entry_it)){
00408         init_nolock(entry_it->first, entry_it->second);
00409     }
00410 }
00411 
00412 void ObjectStorage::reset(){
00413     boost::mutex::scoped_lock lock(mutex_);
00414     for(boost::unordered_map<ObjectDict::Key, boost::shared_ptr<Data> >::iterator it = storage_.begin(); it != storage_.end(); ++it){
00415         it->second->reset();
00416     }
00417 }
00418 
00419 template<const ObjectDict::DataTypes dt, typename T> std::string formatValue(const T &value){
00420     std::stringstream sstr;
00421     sstr << value;
00422     return sstr.str();
00423 }
00424 template<> std::string formatValue<ObjectDict::DEFTYPE_DOMAIN>(const std::string &value){
00425     return can::buffer2hex(value, false);
00426 }
00427 template<> std::string formatValue<ObjectDict::DEFTYPE_OCTET_STRING>(const std::string &value){
00428     return can::buffer2hex(value, false);
00429 }
00430 
00431 struct PrintValue {
00432     template<const ObjectDict::DataTypes dt> static std::string func(ObjectStorage& storage, const ObjectDict::Key &key, bool cached){
00433         ObjectStorage::Entry<typename ObjectStorage::DataType<dt>::type> entry = storage.entry<typename ObjectStorage::DataType<dt>::type>(key);
00434         return formatValue<dt>(cached? entry.get_cached() : entry.get() );
00435     }
00436     static boost::function<std::string()> getReader(ObjectStorage& storage, const ObjectDict::Key &key, bool cached){
00437         ObjectDict::DataTypes data_type = (ObjectDict::DataTypes) storage.dict_->get(key)->data_type;
00438         return boost::bind(branch_type<PrintValue, std::string (ObjectStorage&, const ObjectDict::Key &, bool)>(data_type),boost::ref(storage), key, cached);
00439     }
00440 };
00441 
00442 boost::function<std::string()> ObjectStorage::getStringReader(const ObjectDict::Key &key, bool cached){
00443     return PrintValue::getReader(*this, key, cached);
00444 }
00445 
00446 struct WriteStringValue {
00447     typedef HoldAny (*reader_type)(boost::property_tree::iptree &, const std::string &);
00448     template<typename T> static void write(ObjectStorage::Entry<T> entry, bool cached, reader_type reader, const std::string &value){
00449         boost::property_tree::iptree pt;
00450         pt.put("value", value);
00451         HoldAny any = reader(pt, "value");
00452         if(cached){ 
00453             entry.set_cached(any.get<T>());
00454         } else {
00455             entry.set(any.get<T>());
00456         }
00457     }
00458     template<const ObjectDict::DataTypes dt> static boost::function<void (const std::string&)> func(ObjectStorage& storage, const ObjectDict::Key &key, bool cached){
00459         ObjectStorage::Entry<typename ObjectStorage::DataType<dt>::type> entry = storage.entry<typename ObjectStorage::DataType<dt>::type>(key);
00460         reader_type reader = branch_type<ReadAnyValue, HoldAny (boost::property_tree::iptree &, const std::string &)>(dt);
00461         return boost::bind(&WriteStringValue::write<typename ObjectStorage::DataType<dt>::type >, entry, cached, reader, _1);
00462     }
00463     static boost::function<void (const std::string&)> getWriter(ObjectStorage& storage, const ObjectDict::Key &key, bool cached){
00464         ObjectDict::DataTypes data_type = (ObjectDict::DataTypes) storage.dict_->get(key)->data_type;
00465         return branch_type<WriteStringValue,  boost::function<void (const std::string&)> (ObjectStorage&, const ObjectDict::Key &, bool)>(data_type)(storage, key, cached);
00466     }
00467 };
00468 
00469 boost::function<void (const std::string&)> ObjectStorage::getStringWriter(const ObjectDict::Key &key, bool cached){
00470     return WriteStringValue::getWriter(*this, key, cached);
00471 }


canopen_master
Author(s): Mathias Lüdtke
autogenerated on Thu Jun 6 2019 20:43:54