Skip to content
Snippets Groups Projects
Commit a294ff5c authored by Nicolas Pope's avatar Nicolas Pope
Browse files

Initial new frame structure

parent 919120b1
No related branches found
No related tags found
No related merge requests found
add_library(ftldata INTERFACE) add_library(ftldata STATIC ./src/new_frame.cpp)
target_include_directories(ftldata INTERFACE target_include_directories(ftldata PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/include) ${CMAKE_CURRENT_SOURCE_DIR}/include)
target_link_libraries(ftldata INTERFACE ftlcommon Eigen3::Eigen ftlcodecs) target_link_libraries(ftldata ftlcommon Eigen3::Eigen ftlcodecs)
#add_subdirectory(test) add_subdirectory(test)
#ifndef _FTL_DATA_NEWFRAME_HPP_
#define _FTL_DATA_NEWFRAME_HPP_
#include <map>
#include <unordered_set>
#include <any>
#include <optional>
#include <list>
#include <unordered_map>
#include <ftl/codecs/channels.hpp>
#include <ftl/exception.hpp>
namespace ftl {
namespace data {
class Frame {
public:
uint32_t id=0;
int64_t timestamp=0;
public:
Frame() : parent_(nullptr) {};
explicit Frame(Frame *parent) : parent_(parent) {};
~Frame() { flush(); };
inline bool has(ftl::codecs::Channel c) {
return data_.find(c) != data_.end() || (parent_ && parent_->has(c));
}
inline bool changed(ftl::codecs::Channel c) {
return changed_.find(c) != changed_.end();
}
template <typename T>
bool isType(ftl::codecs::Channel c);
template <typename T>
const T *get(ftl::codecs::Channel c) const;
template <typename T>
T *getMutable(ftl::codecs::Channel c);
inline void touch(ftl::codecs::Channel c) {
changed_.emplace(c);
}
inline void untouch(ftl::codecs::Channel c) {
changed_.erase(c);
}
template <typename T>
void create(ftl::codecs::Channel c, const T &value);
template <typename T>
void create(ftl::codecs::Channel c);
template <typename T>
void push(ftl::codecs::Channel c, const T &v);
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(const std::function<bool(Frame&,ftl::codecs::Channel)> &cb) {
any_triggers_.push_back(cb);
}
/** Send changes back through origin stream. */
bool flush();
// onBeginFlush
// onEndFlush
// onError
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_;
};
}
}
// ==== Implementations ========================================================
template <typename T>
bool ftl::data::Frame::isType(ftl::codecs::Channel c) {
auto i = data_.find(c);
if (i != data_.end()) {
return typeid(T) == i->second.type();
} else return false;
}
template <typename T>
const T *ftl::data::Frame::get(ftl::codecs::Channel c) const {
auto i = data_.find(c);
if (i != data_.end()) {
return std::any_cast<T>(&i->second);
} else if (parent_) {
return parent_->get<T>(c);
} else return nullptr;
}
template <typename T>
void ftl::data::Frame::create(ftl::codecs::Channel c, const T &value) {
data_[c] = value;
touch(c);
}
template <typename T>
void ftl::data::Frame::create(ftl::codecs::Channel c) {
if (!isType<T>(c)) data_[c] = T{};
touch(c);
}
template <typename T>
void ftl::data::Frame::push(ftl::codecs::Channel c, const T &v) {
auto i = data_.find(c);
if (i != data_.end()) {
auto *p = std::any_cast<std::vector<T>>(&i->second);
p->push_back(v);
} else {
throw FTL_Error("Push on missing channel (" << static_cast<unsigned int>(c) << ")");
}
touch(c);
}
template <typename T>
void ftl::data::Frame::set(ftl::codecs::Channel c, const T &v) {
auto i = data_.find(c);
if (i != data_.end()) {
i->second = v;
} else if (parent_ && parent_->isType<T>(c)) {
create<T>(c, v);
} else {
throw FTL_Error("Set on missing channel (" << static_cast<unsigned int>(c) << ")");
}
touch(c);
}
#endif
\ No newline at end of file
#include <ftl/data/new_frame.hpp>
using ftl::data::Frame;
#define LOGURU_REPLACE_GLOG 1
#include <loguru.hpp>
bool Frame::flush() {
if (parent_) {
for (auto c : changed_) {
parent_->changed_.emplace(c);
parent_->data_[c] = std::move(data_[c]);
data_.erase(c);
}
parent_->flush();
} else {
for (auto c : changed_) {
auto i = triggers_.find(c);
if (i != triggers_.end()) {
for (auto f : i->second) {
try {
f(*this, c);
} catch (const std::exception &e) {
LOG(ERROR) << "Exception in frame flush: " << e.what();
}
}
}
}
}
changed_.clear();
return true;
}
### Frame Unit #################################################################
add_executable(nframe_unit
$<TARGET_OBJECTS:CatchTest>
./frame_unit.cpp
../src/new_frame.cpp
)
target_include_directories(nframe_unit PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/../include")
target_link_libraries(nframe_unit
ftlcommon ftlcodecs)
\ No newline at end of file
#include "catch.hpp"
#include <ftl/data/new_frame.hpp>
using ftl::data::Frame;
using ftl::codecs::Channel;
TEST_CASE("ftl::data::Frame create get", "[Frame]") {
SECTION("write and read integers") {
Frame f;
f.create<int>(Channel::Pose, 55);
auto x = f.get<int>(Channel::Pose);
REQUIRE( x );
REQUIRE( *x == 55 );
}
SECTION("write and read floats") {
Frame f;
f.create<float>(Channel::Pose, 44.0f);
auto x = f.get<float>(Channel::Pose);
REQUIRE( x );
REQUIRE( *x == 44.0f );
}
SECTION("write and read structures") {
struct Test {
int a=44;
float b=33.0f;
};
Frame f;
f.create<Test>(Channel::Pose, {});
auto x = f.get<Test>(Channel::Pose);
REQUIRE( x );
REQUIRE( x->a == 44 );
REQUIRE( x->b == 33.0f );
}
SECTION("write and read fail") {
struct Test {
int a=44;
float b=33.0f;
};
Frame f;
f.create<Test>(Channel::Pose, {});
auto x = f.get<int>(Channel::Pose);
REQUIRE( !x );
}
}
TEST_CASE("ftl::data::Frame isType", "[Frame]") {
SECTION("is int type") {
Frame f;
f.create<int>(Channel::Pose, 55);
REQUIRE( f.isType<int>(Channel::Pose) );
REQUIRE( !f.isType<float>(Channel::Pose) );
}
SECTION("is struct type") {
struct Test {
int a; int b;
};
Frame f;
f.create<Test>(Channel::Pose, {3,4});
REQUIRE( f.isType<Test>(Channel::Pose) );
REQUIRE( !f.isType<float>(Channel::Pose) );
}
SECTION("missing") {
Frame f;
REQUIRE( !f.isType<float>(Channel::Pose) );
}
}
TEST_CASE("ftl::data::Frame changed", "[Frame]") {
SECTION("change on create") {
Frame f;
REQUIRE( !f.changed(Channel::Pose) );
f.create<int>(Channel::Pose, 55);
REQUIRE( f.changed(Channel::Pose) );
}
SECTION("no change on untouch") {
Frame f;
f.create<int>(Channel::Pose, 55);
REQUIRE( f.changed(Channel::Pose) );
f.untouch(Channel::Pose);
REQUIRE( !f.changed(Channel::Pose) );
}
}
TEST_CASE("ftl::data::Frame create", "[Frame]") {
SECTION("same value on create") {
Frame f;
f.create<int>(Channel::Pose, 55);
auto x = f.get<int>(Channel::Pose);
REQUIRE( x );
REQUIRE( *x == 55 );
f.create<int>(Channel::Pose);
auto y = f.get<int>(Channel::Pose);
REQUIRE( y );
REQUIRE( *y == 55 );
REQUIRE( x == y );
}
SECTION("change of type") {
Frame f;
f.create<int>(Channel::Pose, 55);
auto x = f.get<int>(Channel::Pose);
REQUIRE( x );
REQUIRE( *x == 55 );
f.create<float>(Channel::Pose);
auto y = f.get<float>(Channel::Pose);
REQUIRE( y );
REQUIRE( *y == 0.0f );
}
}
TEST_CASE("ftl::data::Frame use of parent", "[Frame]") {
SECTION("get from parent") {
Frame p;
Frame f(&p);
p.create<int>(Channel::Pose, 55);
auto x = f.get<int>(Channel::Pose);
REQUIRE( x );
REQUIRE( *x == 55 );
auto y = p.get<int>(Channel::Pose);
REQUIRE( x == y );
}
SECTION("has from parent") {
Frame p;
Frame f(&p);
p.create<int>(Channel::Pose, 55);
REQUIRE( f.has(Channel::Pose) );
}
SECTION("no change in parent") {
Frame p;
Frame f(&p);
p.create<int>(Channel::Pose, 55);
p.untouch(Channel::Pose);
REQUIRE( !f.changed(Channel::Pose) );
REQUIRE( !p.changed(Channel::Pose) );
f.set<int>(Channel::Pose, 66);
REQUIRE( f.changed(Channel::Pose) );
REQUIRE( !p.changed(Channel::Pose) );
auto x = f.get<int>(Channel::Pose);
REQUIRE( x );
REQUIRE( *x == 66 );
auto y = p.get<int>(Channel::Pose);
REQUIRE( y );
REQUIRE( *y == 55 );
}
}
TEST_CASE("ftl::data::Frame flush", "[Frame]") {
SECTION("event on flush") {
Frame f;
int event = 0;
f.on(Channel::Pose, [&event](Frame &frame, Channel c) {
event++;
return true;
});
f.create<int>(Channel::Pose, 55);
REQUIRE( event == 0 );
f.flush();
REQUIRE( event == 1 );
}
SECTION("parent event on flush") {
Frame p;
Frame f(&p);
int event = 0;
p.on(Channel::Pose, [&event](Frame &frame, Channel c) {
event++;
return true;
});
f.create<int>(Channel::Pose, 55);
REQUIRE( event == 0 );
f.flush();
REQUIRE( event == 1 );
}
SECTION("parent change on flush") {
Frame p;
Frame f(&p);
p.create<int>(Channel::Pose, 55);
p.flush();
f.set<int>(Channel::Pose, 66);
auto x = p.get<int>(Channel::Pose);
REQUIRE( x );
REQUIRE( *x == 55 );
f.flush();
x = p.get<int>(Channel::Pose);
REQUIRE( x );
REQUIRE( *x == 66 );
}
SECTION("untouched on flush") {
Frame f;
f.create<int>(Channel::Pose, 55);
REQUIRE( f.changed(Channel::Pose) );
f.flush();
REQUIRE( !f.changed(Channel::Pose) );
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment