/* see protocol.md for the protocol specification */ #ifndef __PROTOCOL_HPP #define __PROTOCOL_HPP #include #include #include //#include #include #include #include #include #include #include #include typedef struct { uint16_t json_crc; uint16_t endpoint_id; } endpoint_ref_t; namespace fibre { // These symbols are defined in the autogenerated endpoints.hpp extern const unsigned char embedded_json[]; extern const size_t embedded_json_length; extern const uint16_t json_crc_; extern const uint32_t json_version_id_; bool endpoint_handler(int idx, cbufptr_t* input_buffer, bufptr_t* output_buffer); bool endpoint0_handler(cbufptr_t* input_buffer, bufptr_t* output_buffer); bool is_endpoint_ref_valid(endpoint_ref_t endpoint_ref); bool set_endpoint_from_float(endpoint_ref_t endpoint_ref, float value); } namespace fibre { template struct Codec { static std::optional decode(cbufptr_t* buffer) { return std::nullopt; } }; template<> struct Codec { static std::optional decode(cbufptr_t* buffer) { return (buffer->begin() == buffer->end()) ? std::nullopt : std::make_optional((bool)*(buffer->begin()++)); } static bool encode(bool value, bufptr_t* buffer) { return SimpleSerializer::write(value, &(buffer->begin()), buffer->end()); } }; template<> struct Codec { static std::optional decode(cbufptr_t* buffer) { return SimpleSerializer::read(&(buffer->begin()), buffer->end()); } static bool encode(int8_t value, bufptr_t* buffer) { return SimpleSerializer::write(value, &(buffer->begin()), buffer->end()); } }; template<> struct Codec { static std::optional decode(cbufptr_t* buffer) { return SimpleSerializer::read(&(buffer->begin()), buffer->end()); } static bool encode(uint8_t value, bufptr_t* buffer) { return SimpleSerializer::write(value, &(buffer->begin()), buffer->end()); } }; template<> struct Codec { static std::optional decode(cbufptr_t* buffer) { return SimpleSerializer::read(&(buffer->begin()), buffer->end()); } static bool encode(int16_t value, bufptr_t* buffer) { return SimpleSerializer::write(value, &(buffer->begin()), buffer->end()); } }; template<> struct Codec { static std::optional decode(cbufptr_t* buffer) { return SimpleSerializer::read(&(buffer->begin()), buffer->end()); } static bool encode(uint16_t value, bufptr_t* buffer) { return SimpleSerializer::write(value, &(buffer->begin()), buffer->end()); } }; template<> struct Codec { static std::optional decode(cbufptr_t* buffer) { return SimpleSerializer::read(&(buffer->begin()), buffer->end()); } static bool encode(int32_t value, bufptr_t* buffer) { return SimpleSerializer::write(value, &(buffer->begin()), buffer->end()); } }; template<> struct Codec { static std::optional decode(cbufptr_t* buffer) { return SimpleSerializer::read(&(buffer->begin()), buffer->end()); } static bool encode(uint32_t value, bufptr_t* buffer) { return SimpleSerializer::write(value, &(buffer->begin()), buffer->end()); } }; template<> struct Codec { static std::optional decode(cbufptr_t* buffer) { return SimpleSerializer::read(&(buffer->begin()), buffer->end()); } static bool encode(int64_t value, bufptr_t* buffer) { return SimpleSerializer::write(value, &(buffer->begin()), buffer->end()); } }; template<> struct Codec { static std::optional decode(cbufptr_t* buffer) { return SimpleSerializer::read(&(buffer->begin()), buffer->end()); } static bool encode(uint64_t value, bufptr_t* buffer) { return SimpleSerializer::write(value, &(buffer->begin()), buffer->end()); } }; template<> struct Codec { static std::optional decode(cbufptr_t* buffer) { std::optional int_val = Codec::decode(buffer); return int_val.has_value() ? std::optional(*reinterpret_cast(&*int_val)) : std::nullopt; } static bool encode(float value, bufptr_t* buffer) { void* ptr = &value; return Codec::encode(*reinterpret_cast(ptr), buffer); } }; template struct Codec::value>> { using int_type = std::underlying_type_t; static std::optional decode(cbufptr_t* buffer) { std::optional int_val = SimpleSerializer::read(&(buffer->begin()), buffer->end()); return int_val.has_value() ? std::make_optional(static_cast(*int_val)) : std::nullopt; } static bool encode(T value, bufptr_t* buffer) { return SimpleSerializer::write(value, &(buffer->begin()), buffer->end()); } }; template<> struct Codec { static std::optional decode(cbufptr_t* buffer) { std::optional val0 = SimpleSerializer::read(&(buffer->begin()), buffer->end()); std::optional val1 = SimpleSerializer::read(&(buffer->begin()), buffer->end()); return (val0.has_value() && val1.has_value()) ? std::make_optional(endpoint_ref_t{*val1, *val0}) : std::nullopt; } static bool encode(endpoint_ref_t value, bufptr_t* buffer) { return SimpleSerializer::write(value.endpoint_id, &(buffer->begin()), buffer->end()) && SimpleSerializer::write(value.json_crc, &(buffer->begin()), buffer->end()); } }; } /* ToString / FromString functions -------------------------------------------*/ /* * These functions are currently not used by Fibre and only here to * support the ODrive ASCII protocol. * TODO: find a general way for client code to augment endpoints with custom * functions */ template struct format_traits_t; // template<> struct format_traits_t { using type = void; // static constexpr const char * fmt = "%f"; // static constexpr const char * fmtp = "%f"; // }; template<> struct format_traits_t { using type = void; static constexpr const char * fmt = "%lld"; static constexpr const char * fmtp = "%lld"; using scn_type = long long; }; template<> struct format_traits_t { using type = void; static constexpr const char * fmt = "%llu"; static constexpr const char * fmtp = "%llu"; using scn_type = unsigned long long; }; template<> struct format_traits_t { using type = void; static constexpr const char * fmt = "%ld"; static constexpr const char * fmtp = "%ld"; using scn_type = long; }; template<> struct format_traits_t { using type = void; static constexpr const char * fmt = "%lu"; static constexpr const char * fmtp = "%lu"; using scn_type = unsigned long; }; template<> struct format_traits_t { using type = void; static constexpr const char * fmt = "%d"; static constexpr const char * fmtp = "%d"; using scn_type = int; }; template<> struct format_traits_t { using type = void; static constexpr const char * fmt = "%u"; static constexpr const char * fmtp = "%u"; using scn_type = unsigned int; }; template<> struct format_traits_t { using type = void; static constexpr const char * fmt = "%d"; static constexpr const char * fmtp = "%d"; using scn_type = int; }; template<> struct format_traits_t { using type = void; static constexpr const char * fmt = "%u"; static constexpr const char * fmtp = "%u"; using scn_type = unsigned int; }; template<> struct format_traits_t { using type = void; static constexpr const char * fmt = "%d"; static constexpr const char * fmtp = "%d"; using scn_type = int; }; template<> struct format_traits_t { using type = void; static constexpr const char * fmt = "%u"; static constexpr const char * fmtp = "%u"; using scn_type = unsigned int; }; template::type> static bool to_string(const T& value, char * buffer, size_t length, int) { snprintf(buffer, length, format_traits_t::fmtp, value); return true; } // Special case for float because printf promotes float to double, and we get warnings template static bool to_string(const float& value, char * buffer, size_t length, int) { snprintf(buffer, length, "%f", (double)value); return true; } template static bool to_string(const bool& value, char * buffer, size_t length, int) { buffer[0] = value ? '1' : '0'; buffer[1] = 0; return true; } template static bool to_string(const T& value, char * buffer, size_t length, ...) { return false; } template::type> static bool from_string(const char * buffer, size_t length, T* property, int) { // sscanf doesn't work well with integers that are smaller than int, so we // first scan into an appropriate int type and then convert it. // (e.g. %hhu has been observed to not work on some compilers) typename format_traits_t::scn_type val; if (sscanf(buffer, format_traits_t::fmt, &val) == 1) { *property = (T)val; return true; } else { return false; } } // Special case for float because printf promotes float to double, and we get warnings template static bool from_string(const char * buffer, size_t length, float* property, int) { return sscanf(buffer, "%f", property) == 1; } template static bool from_string(const char * buffer, size_t length, bool* property, int) { int val; if (sscanf(buffer, "%d", &val) != 1) return false; *property = val; return true; } template static bool from_string(const char * buffer, size_t length, T* property, ...) { return false; } //template //bool set_from_float_ex(float value, T* property) { // return false; //} namespace conversion { //template template bool set_from_float_ex(float value, float* property, int) { return *property = value, true; } template bool set_from_float_ex(float value, bool* property, int) { return *property = (value >= 0.0f), true; } template::value && !std::is_const::value>> bool set_from_float_ex(float value, T* property, int) { return *property = static_cast(std::round(value)), true; } template bool set_from_float_ex(float value, T* property, ...) { return false; } template bool set_from_float(float value, T* property) { return set_from_float_ex(value, property, 0); } } template struct Property { Property(void* ctx, T(*getter)(void*), void(*setter)(void*, T)) : ctx_(ctx), getter_(getter), setter_(setter) {} Property(T* ctx) : ctx_(ctx), getter_([](void* ctx){ return *(T*)ctx; }), setter_([](void* ctx, T val){ *(T*)ctx = val; }) {} Property& operator*() { return *this; } Property* operator->() { return this; } T read() const { return (*getter_)(ctx_); } T exchange(std::optional value) const { T old_value = (*getter_)(ctx_); if (value.has_value()) { (*setter_)(ctx_, *value); } return old_value; } void* ctx_; T(*getter_)(void*); void(*setter_)(void*, T); }; template struct Property { Property(void* ctx, T(*getter)(void*)) : ctx_(ctx), getter_(getter) {} Property(const T* ctx) : ctx_(const_cast(ctx)), getter_([](void* ctx){ return *(const T*)ctx; }) {} Property& operator*() { return *this; } Property* operator->() { return this; } T read() const { return (*getter_)(ctx_); } void* ctx_; T(*getter_)(void*); }; #endif