diff --git a/libuavcan/include/uavcan/data_type.hpp b/libuavcan/include/uavcan/data_type.hpp index 2c74644cf5..dbc371fdaa 100644 --- a/libuavcan/include/uavcan/data_type.hpp +++ b/libuavcan/include/uavcan/data_type.hpp @@ -120,17 +120,12 @@ public: const DataTypeSignature& getSignature() const { return signature_; } const char* getName() const { return name_; } + bool match(DataTypeKind kind, const char* name) const; + std::string toString() const; bool operator!=(const DataTypeDescriptor& rhs) const { return !operator==(rhs); } - bool operator==(const DataTypeDescriptor& rhs) const - { - return - (kind_ == rhs.kind_) && - (id_ == rhs.id_) && - (signature_ == rhs.signature_) && - !std::strcmp(name_, rhs.name_); - } + bool operator==(const DataTypeDescriptor& rhs) const; }; } diff --git a/libuavcan/include/uavcan/global_data_type_registry.hpp b/libuavcan/include/uavcan/global_data_type_registry.hpp new file mode 100644 index 0000000000..0b8ece466c --- /dev/null +++ b/libuavcan/include/uavcan/global_data_type_registry.hpp @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2014 Pavel Kirienko + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace uavcan +{ + +typedef std::bitset DataTypeIDMask; + +class GlobalDataTypeRegistry : Noncopyable +{ + struct Entry : public LinkedListNode + { + DataTypeDescriptor decriptor; + + Entry() { } + + Entry(DataTypeKind kind, uint16_t id, const DataTypeSignature& signature, const char* name) + : decriptor(kind, id, signature, name) + { } + }; + + typedef LinkedListRoot List; + List msgs_; + List srvs_; + bool frozen_; + + GlobalDataTypeRegistry() : frozen_(false) { } + + const List* selectList(DataTypeKind kind) const; + List* selectList(DataTypeKind kind); + + void remove(Entry* dtd); + bool add(Entry* dtd); + +public: + static GlobalDataTypeRegistry& instance(); + + /// Last call wins + template + bool assign(uint16_t id) + { + if (isFrozen()) + return false; + static Entry entry; + remove(&entry); + entry = Entry(DataTypeKind(Type::DataTypeKind), id, Type::getDataTypeSignature(), Type::getDataTypeName()); + return add(&entry); + } + + /// Further calls to add() will fail + void freeze() { frozen_ = true; } + bool isFrozen() const { return frozen_; } + + const DataTypeDescriptor* find(DataTypeKind kind, const char* name) const; + + DataTypeSignature computeAggregateSignature(const DataTypeIDMask& msgs, const DataTypeIDMask& srvs) const; + + void getDataTypeIDMask(DataTypeKind kind, DataTypeIDMask& mask) const; + + unsigned int getNumMessageTypes() const { return msgs_.getLength(); } + unsigned int getNumServiceTypes() const { return srvs_.getLength(); } + +#if UAVCAN_DEBUG + /// Required for unit testing + void reset() + { + frozen_ = false; + while (msgs_.get()) + msgs_.remove(msgs_.get()); + while (srvs_.get()) + srvs_.remove(srvs_.get()); + } +#endif +}; + + +template +struct DefaultDataTypeRegistrator +{ + DefaultDataTypeRegistrator() + { + if (!GlobalDataTypeRegistry::instance().assign(Type::DefaultDataTypeID)) + { +#if UAVCAN_EXCEPTIONS + throw std::logic_error("Type registration failed"); +#else + assert(0); + std::abort(); +#endif + } + } +}; + +} diff --git a/libuavcan/src/data_type.cpp b/libuavcan/src/data_type.cpp index 4182cf7189..afb6e062db 100644 --- a/libuavcan/src/data_type.cpp +++ b/libuavcan/src/data_type.cpp @@ -3,6 +3,7 @@ */ #include +#include #include #include #include @@ -38,6 +39,11 @@ TransferCRC DataTypeSignature::toTransferCRC() const /* * DataTypeDescriptor */ +bool DataTypeDescriptor::match(DataTypeKind kind, const char* name) const +{ + return (kind_ == kind) && !std::strcmp(name_, name); +} + std::string DataTypeDescriptor::toString() const { char kindch = '?'; @@ -53,4 +59,13 @@ std::string DataTypeDescriptor::toString() const return os.str(); } +bool DataTypeDescriptor::operator==(const DataTypeDescriptor& rhs) const +{ + return + (kind_ == rhs.kind_) && + (id_ == rhs.id_) && + (signature_ == rhs.signature_) && + !std::strcmp(name_, rhs.name_); +} + } diff --git a/libuavcan/src/global_data_type_registry.cpp b/libuavcan/src/global_data_type_registry.cpp new file mode 100644 index 0000000000..fd804f1c21 --- /dev/null +++ b/libuavcan/src/global_data_type_registry.cpp @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2014 Pavel Kirienko + */ + +#include +#include +#include + +namespace uavcan +{ + +const GlobalDataTypeRegistry::List* GlobalDataTypeRegistry::selectList(DataTypeKind kind) const +{ + switch (kind) + { + case DataTypeKindMessage: return &msgs_; + case DataTypeKindService: return &srvs_; + default: + assert(0); + return NULL; + } +} + +GlobalDataTypeRegistry::List* GlobalDataTypeRegistry::selectList(DataTypeKind kind) +{ + switch (kind) + { + case DataTypeKindMessage: return &msgs_; + case DataTypeKindService: return &srvs_; + default: + assert(0); + return NULL; + } +} + +void GlobalDataTypeRegistry::remove(Entry* dtd) +{ + assert(dtd); + if (isFrozen()) + return; + + List* list = selectList(dtd->decriptor.getKind()); + if (!list) + return; + + Entry* p = list->get(); + while (p) + { + Entry* const next = p->getNextListNode(); + if (p->decriptor.match(dtd->decriptor.getKind(), dtd->decriptor.getName())) + list->remove(dtd); + p = next; + } +} + +bool GlobalDataTypeRegistry::add(Entry* dtd) +{ + assert(dtd); + if (isFrozen()) + return false; + + List* list = selectList(dtd->decriptor.getKind()); + if (!list) + return false; + + list->insert(dtd); + return true; +} + +GlobalDataTypeRegistry& GlobalDataTypeRegistry::instance() +{ + static GlobalDataTypeRegistry inst; + return inst; +} + +const DataTypeDescriptor* GlobalDataTypeRegistry::find(DataTypeKind kind, const char* name) const +{ + const List* list = selectList(kind); + if (list == NULL) + { + assert(0); + return NULL; + } + Entry* p = list->get(); + while (p) + { + if (p->decriptor.match(kind, name)) + return &p->decriptor; + p = p->getNextListNode(); + } + return NULL; +} + +DataTypeSignature GlobalDataTypeRegistry::computeAggregateSignature(const DataTypeIDMask& msgs, + const DataTypeIDMask& srvs) const +{ + (void)msgs; + (void)srvs; + return DataTypeSignature::zero(); // TODO: implementation +} + +void GlobalDataTypeRegistry::getDataTypeIDMask(DataTypeKind kind, DataTypeIDMask& mask) const +{ + mask.reset(); + const List* list = selectList(kind); + if (list == NULL) + { + assert(0); + return; + } + Entry* p = list->get(); + while (p) + { + assert(p->decriptor.getKind() == kind); + mask[p->decriptor.getID()] = true; + p = p->getNextListNode(); + } +} + +} diff --git a/libuavcan/test/data_type.cpp b/libuavcan/test/data_type.cpp index 52b870b435..ccd2e5aff4 100644 --- a/libuavcan/test/data_type.cpp +++ b/libuavcan/test/data_type.cpp @@ -102,3 +102,13 @@ TEST(DataTypeDescriptor, ToString) uavcan::DataTypeSignature(0xdeadbeef1234), "Bar"); ASSERT_EQ("Bar:123m:0000deadbeef1234", desc.toString()); } + + +TEST(DataTypeDescriptor, Match) +{ + uavcan::DataTypeDescriptor desc(uavcan::DataTypeKindMessage, 123, + uavcan::DataTypeSignature(0xdeadbeef1234), "namespace.TypeName"); + ASSERT_TRUE(desc.match(uavcan::DataTypeKindMessage, "namespace.TypeName")); + ASSERT_FALSE(desc.match(uavcan::DataTypeKindMessage, "boo")); + ASSERT_FALSE(desc.match(uavcan::DataTypeKindService, "namespace.TypeName")); +} diff --git a/libuavcan/test/global_data_type_registry.cpp b/libuavcan/test/global_data_type_registry.cpp new file mode 100644 index 0000000000..f0cc73bb0a --- /dev/null +++ b/libuavcan/test/global_data_type_registry.cpp @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2014 Pavel Kirienko + */ + +#include +#include + +namespace +{ + struct DataTypeAMessage + { + enum { DefaultDataTypeID = 0 }; + enum { DataTypeKind = uavcan::DataTypeKindMessage }; + static uavcan::DataTypeSignature getDataTypeSignature() { return uavcan::DataTypeSignature(123); } + static const char* getDataTypeName() { return "my_namespace.DataTypeA"; } + }; + + struct DataTypeAService + { + enum { DefaultDataTypeID = 0 }; + enum { DataTypeKind = uavcan::DataTypeKindService }; + static uavcan::DataTypeSignature getDataTypeSignature() { return uavcan::DataTypeSignature(789); } + static const char* getDataTypeName() { return "my_namespace.DataTypeA"; } + }; + + struct DataTypeB + { + enum { DefaultDataTypeID = 42 }; + enum { DataTypeKind = uavcan::DataTypeKindMessage }; + static uavcan::DataTypeSignature getDataTypeSignature() { return uavcan::DataTypeSignature(456); } + static const char* getDataTypeName() { return "my_namespace.DataTypeB"; } + }; + + + template + uavcan::DataTypeDescriptor extractDescriptor(uint16_t dtid = Type::DefaultDataTypeID) + { + return uavcan::DataTypeDescriptor(uavcan::DataTypeKind(Type::DataTypeKind), dtid, + Type::getDataTypeSignature(), Type::getDataTypeName()); + } +} + + +TEST(GlobalDataTypeRegistry, Basic) +{ + using uavcan::GlobalDataTypeRegistry; + + GlobalDataTypeRegistry::instance().reset(); + ASSERT_FALSE(GlobalDataTypeRegistry::instance().isFrozen()); + ASSERT_EQ(0, GlobalDataTypeRegistry::instance().getNumMessageTypes()); + ASSERT_EQ(0, GlobalDataTypeRegistry::instance().getNumServiceTypes()); + + uavcan::DataTypeIDMask dtmask; + dtmask.set(); + GlobalDataTypeRegistry::instance().getDataTypeIDMask(uavcan::DataTypeKindMessage, dtmask); + ASSERT_FALSE(dtmask.any()); + dtmask.set(); + GlobalDataTypeRegistry::instance().getDataTypeIDMask(uavcan::DataTypeKindService, dtmask); + ASSERT_FALSE(dtmask.any()); + + /* + * Static registrations + */ + uavcan::DefaultDataTypeRegistrator reg_DataTypeAMessage; + uavcan::DefaultDataTypeRegistrator reg_DataTypeB; + + ASSERT_EQ(2, GlobalDataTypeRegistry::instance().getNumMessageTypes()); + ASSERT_EQ(0, GlobalDataTypeRegistry::instance().getNumServiceTypes()); + + GlobalDataTypeRegistry::instance().getDataTypeIDMask(uavcan::DataTypeKindMessage, dtmask); + ASSERT_TRUE(dtmask[0]); + ASSERT_TRUE(dtmask[42]); + dtmask[0] = dtmask[42] = false; + ASSERT_FALSE(dtmask.any()); + + /* + * Runtime registrations + */ + ASSERT_TRUE(GlobalDataTypeRegistry::instance().assign(DataTypeAService::DefaultDataTypeID)); + + ASSERT_EQ(2, GlobalDataTypeRegistry::instance().getNumMessageTypes()); + ASSERT_EQ(1, GlobalDataTypeRegistry::instance().getNumServiceTypes()); + + GlobalDataTypeRegistry::instance().getDataTypeIDMask(uavcan::DataTypeKindService, dtmask); + ASSERT_TRUE(dtmask[0]); + dtmask[0] = false; + ASSERT_FALSE(dtmask.any()); + + /* + * Runtime re-registration + */ + ASSERT_TRUE(GlobalDataTypeRegistry::instance().assign(147)); + ASSERT_TRUE(GlobalDataTypeRegistry::instance().assign(741)); + + ASSERT_EQ(2, GlobalDataTypeRegistry::instance().getNumMessageTypes()); + ASSERT_EQ(1, GlobalDataTypeRegistry::instance().getNumServiceTypes()); + + GlobalDataTypeRegistry::instance().getDataTypeIDMask(uavcan::DataTypeKindMessage, dtmask); + ASSERT_TRUE(dtmask[0]); + ASSERT_TRUE(dtmask[741]); + dtmask[0] = dtmask[741] = false; + ASSERT_FALSE(dtmask.any()); + + GlobalDataTypeRegistry::instance().getDataTypeIDMask(uavcan::DataTypeKindService, dtmask); + ASSERT_TRUE(dtmask[147]); + dtmask[147] = false; + ASSERT_FALSE(dtmask.any()); + + /* + * Frozen state + */ + GlobalDataTypeRegistry::instance().freeze(); + + ASSERT_FALSE(GlobalDataTypeRegistry::instance().assign(555)); // Rejected + ASSERT_FALSE(GlobalDataTypeRegistry::instance().assign(999)); // Rejected + ASSERT_FALSE(GlobalDataTypeRegistry::instance().assign(888)); // Rejected + + /* + * Searching + */ + const uavcan::DataTypeDescriptor* pdtd = NULL; + ASSERT_FALSE(GlobalDataTypeRegistry::instance().find(uavcan::DataTypeKindMessage, "Nonexistent")); + // Asking for service, but this is a message: + ASSERT_FALSE(GlobalDataTypeRegistry::instance().find(uavcan::DataTypeKindService, "my_namespace.DataTypeB")); + + ASSERT_TRUE((pdtd = GlobalDataTypeRegistry::instance().find(uavcan::DataTypeKindMessage, "my_namespace.DataTypeB"))); + ASSERT_EQ(extractDescriptor(741), *pdtd); + + ASSERT_TRUE((pdtd = GlobalDataTypeRegistry::instance().find(uavcan::DataTypeKindMessage, "my_namespace.DataTypeA"))); + ASSERT_EQ(extractDescriptor(), *pdtd); + + ASSERT_TRUE((pdtd = GlobalDataTypeRegistry::instance().find(uavcan::DataTypeKindService, "my_namespace.DataTypeA"))); + ASSERT_EQ(extractDescriptor(147), *pdtd); + + /* + * Aggregate signature computation + */ + // TODO: test + + /* + * Since we're dealing with singleton, we need to reset it for other tests to use + */ + GlobalDataTypeRegistry::instance().reset(); +}