diff --git a/components/structures/include/ftl/data/new_frame.hpp b/components/structures/include/ftl/data/new_frame.hpp index 48c18ed8b6c4c672ff994f3ad4d4714b31c42a06..963e91b5fdf39f097ce3de20631671cf7a47d859 100644 --- a/components/structures/include/ftl/data/new_frame.hpp +++ b/components/structures/include/ftl/data/new_frame.hpp @@ -13,6 +13,32 @@ namespace ftl { namespace data { +class Session; + +/** Kind of channel in terms of data persistence */ +enum class ChannelMode { + PERSISTENT, // Most recent value, even from previous fram + IMMEDIATE, // Only most recent value since last frame + SEQUENCE // All changes since last frame +}; + +struct ChannelConfig { + std::string name; + ChannelMode mode; + size_t type_id; +}; + +template <typename T> +ChannelConfig make_channel(const std::string &name, ChannelMode mode) { + // TODO: Generate packer + unpacker? + return {name, mode, typeid(T).hash_code()}; +} + +//template <> +//ChannelConfig make_channel<void>(const std::string &name, ChannelMode mode) { +// return {name, mode, 0}; +//} + class Frame { public: uint32_t id=0; @@ -20,7 +46,7 @@ class Frame { public: Frame() : parent_(nullptr) {}; - explicit Frame(Frame *parent) : parent_(parent) {}; + explicit Frame(Session *parent) : parent_(parent) {}; ~Frame() { flush(); }; Frame(Frame &&f) { @@ -38,13 +64,9 @@ class Frame { Frame(const Frame &)=delete; Frame &operator=(const Frame &)=delete; - inline bool has(ftl::codecs::Channel c) { - return data_.find(c) != data_.end() || (parent_ && parent_->has(c)); - } + inline bool has(ftl::codecs::Channel c); - inline bool changed(ftl::codecs::Channel c) { - return changed_.find(c) != changed_.end(); - } + inline bool changed(ftl::codecs::Channel c); inline const std::unordered_set<ftl::codecs::Channel> &changed() const { return changed_; } @@ -83,14 +105,9 @@ class Frame { template <typename T> void set(ftl::codecs::Channel c, const T &v); - inline void on(ftl::codecs::Channel c, const std::function<bool(Frame&,ftl::codecs::Channel)> &cb) { - if (parent_) parent_->on(c, cb); - else triggers_[c].push_back(cb); // TODO: Find better way to enable removal - } + inline void on(ftl::codecs::Channel c, const std::function<bool(Frame&,ftl::codecs::Channel)> &cb); - inline void on(const std::function<bool(Frame&,ftl::codecs::Channel)> &cb) { - any_triggers_.push_back(cb); - } + inline void on(const std::function<bool(Frame&,ftl::codecs::Channel)> &cb); void merge(Frame &); @@ -114,19 +131,46 @@ class Frame { // onEndFlush // onError + static void registerChannel(ftl::codecs::Channel, const ChannelConfig &config); + static void clearRegistry(); + + static bool isPersistent(ftl::codecs::Channel); + static size_t getChannelType(ftl::codecs::Channel); + static std::string getChannelName(ftl::codecs::Channel); + static ftl::codecs::Channel getChannelByName(const std::string &name); + private: std::map<ftl::codecs::Channel, std::any> data_; std::unordered_set<ftl::codecs::Channel> changed_; std::unordered_map<ftl::codecs::Channel, std::list<std::function<bool(Frame&,ftl::codecs::Channel)>>> triggers_; std::list<std::function<bool(Frame&,ftl::codecs::Channel)>> any_triggers_; - Frame *parent_; + Session *parent_; }; +class Session : public Frame {}; + } } // ==== Implementations ======================================================== +bool ftl::data::Frame::has(ftl::codecs::Channel c) { + return data_.find(c) != data_.end() || (parent_ && parent_->has(c)); +} + +bool ftl::data::Frame::changed(ftl::codecs::Channel c) { + return changed_.find(c) != changed_.end(); +} + +void ftl::data::Frame::on(ftl::codecs::Channel c, const std::function<bool(Frame&,ftl::codecs::Channel)> &cb) { + if (parent_) parent_->on(c, cb); + else triggers_[c].push_back(cb); // TODO: Find better way to enable removal +} + +void ftl::data::Frame::on(const std::function<bool(Frame&,ftl::codecs::Channel)> &cb) { + any_triggers_.push_back(cb); +} + template <typename T> bool ftl::data::Frame::isType(ftl::codecs::Channel c) { auto i = data_.find(c); @@ -160,6 +204,8 @@ const T &ftl::data::Frame::get(ftl::codecs::Channel c) const { template <typename T> T &ftl::data::Frame::create(ftl::codecs::Channel c, const T &value) { touch(c); + size_t t = getChannelType(c); + if (t > 0 && t != typeid(T).hash_code()) throw FTL_Error("Incorrect type for channel " << static_cast<unsigned int>(c)); auto &d = data_[c]; d = value; return *std::any_cast<T>(&d); @@ -168,6 +214,8 @@ T &ftl::data::Frame::create(ftl::codecs::Channel c, const T &value) { template <typename T> T &ftl::data::Frame::create(ftl::codecs::Channel c) { touch(c); + size_t t = getChannelType(c); + if (t > 0 && t != typeid(T).hash_code()) throw FTL_Error("Incorrect type for channel " << static_cast<unsigned int>(c)); if (!isType<T>(c)) return data_[c].emplace<T>(); else return *std::any_cast<T>(&data_[c]); } diff --git a/components/structures/src/new_frame.cpp b/components/structures/src/new_frame.cpp index 9d030ed8c259549d14b43b23d73db472b2612a74..36a8b0c64e5f9a46bc2dc42afbcccedae27ecf04 100644 --- a/components/structures/src/new_frame.cpp +++ b/components/structures/src/new_frame.cpp @@ -1,10 +1,47 @@ #include <ftl/data/new_frame.hpp> using ftl::data::Frame; +using ftl::data::ChannelConfig; +using ftl::data::ChannelMode; #define LOGURU_REPLACE_GLOG 1 #include <loguru.hpp> +static std::unordered_map<ftl::codecs::Channel, ChannelConfig> reg_channels; + +void Frame::registerChannel(ftl::codecs::Channel c, const ChannelConfig &config) { + auto i = reg_channels.find(c); + if (i != reg_channels.end()) { + throw FTL_Error("Channel " << static_cast<unsigned int>(c) << " already registered"); + } + + reg_channels[c] = config; +} + +void Frame::clearRegistry() { + reg_channels.clear(); +} + +bool Frame::isPersistent(ftl::codecs::Channel c) { + auto i = reg_channels.find(c); + return (i != reg_channels.end()) ? i->second.mode == ChannelMode::PERSISTENT : true; +} + +size_t Frame::getChannelType(ftl::codecs::Channel c) { + auto i = reg_channels.find(c); + return (i != reg_channels.end()) ? i->second.type_id : 0; +} + +std::string Frame::getChannelName(ftl::codecs::Channel c) { + auto i = reg_channels.find(c); + return (i != reg_channels.end()) ? i->second.name : ""; +} + +ftl::codecs::Channel Frame::getChannelByName(const std::string &name) { + return ftl::codecs::Channel::Colour; +} + + bool Frame::flush() { if (parent_) { for (auto c : changed_) { diff --git a/components/structures/test/frame_unit.cpp b/components/structures/test/frame_unit.cpp index da8ffca5905337bc68c2716c9f4cb1f37112ebfa..7cf289868ff22f1053997da25ee22fb3695583ba 100644 --- a/components/structures/test/frame_unit.cpp +++ b/components/structures/test/frame_unit.cpp @@ -2,7 +2,7 @@ #include <ftl/data/new_frame.hpp> - +using ftl::data::Session; using ftl::data::Frame; using ftl::codecs::Channel; @@ -133,7 +133,7 @@ TEST_CASE("ftl::data::Frame create", "[Frame]") { TEST_CASE("ftl::data::Frame use of parent", "[Frame]") { SECTION("get from parent") { - Frame p; + Session p; Frame f(&p); p.create<int>(Channel::Pose, 55); @@ -147,7 +147,7 @@ TEST_CASE("ftl::data::Frame use of parent", "[Frame]") { } SECTION("has from parent") { - Frame p; + Session p; Frame f(&p); p.create<int>(Channel::Pose, 55); @@ -155,7 +155,7 @@ TEST_CASE("ftl::data::Frame use of parent", "[Frame]") { } SECTION("no change in parent") { - Frame p; + Session p; Frame f(&p); p.create<int>(Channel::Pose, 55); @@ -197,7 +197,7 @@ TEST_CASE("ftl::data::Frame flush", "[Frame]") { } SECTION("parent event on flush") { - Frame p; + Session p; Frame f(&p); int event = 0; @@ -214,7 +214,7 @@ TEST_CASE("ftl::data::Frame flush", "[Frame]") { } SECTION("parent change on flush") { - Frame p; + Session p; Frame f(&p); p.create<int>(Channel::Pose, 55); @@ -242,6 +242,33 @@ TEST_CASE("ftl::data::Frame flush", "[Frame]") { } } +TEST_CASE("ftl::data::Frame register", "[Frame]") { + SECTION("register typed channel and valid create") { + Frame f; + + Frame::registerChannel(Channel::Colour, ftl::data::make_channel<float>("colour", ftl::data::ChannelMode::PERSISTENT)); + REQUIRE( f.create<float>(Channel::Colour, 5.0f) == 5.0f ); + + Frame::clearRegistry(); + } + + SECTION("register typed channel and invalid create") { + Frame f; + + Frame::registerChannel(Channel::Colour, ftl::data::make_channel<float>("colour", ftl::data::ChannelMode::PERSISTENT)); + + bool err = false; + try { + f.create<int>(Channel::Colour, 5); + } catch(const std::exception &e) { + err = true; + } + REQUIRE( err ); + + Frame::clearRegistry(); + } +} + // ==== Complex type overload test ============================================= struct TestA {