From aee1a7343df853f8bdd79b99bc8530616541cdfe Mon Sep 17 00:00:00 2001 From: Samuel Sadok Date: Sat, 14 Apr 2018 23:03:03 -0700 Subject: [PATCH] add property read/write functionality to ASCII protocol Command examples: r vbus_voltage w axis0.config.enable_step_dir 1 --- Firmware/MotorControl/ascii_protocol.cpp | 40 ++++++++- Firmware/MotorControl/protocol.hpp | 105 ++++++++++++++++++++++- 2 files changed, 141 insertions(+), 4 deletions(-) diff --git a/Firmware/MotorControl/ascii_protocol.cpp b/Firmware/MotorControl/ascii_protocol.cpp index 08eecc1d..ad24b1b5 100644 --- a/Firmware/MotorControl/ascii_protocol.cpp +++ b/Firmware/MotorControl/ascii_protocol.cpp @@ -18,7 +18,9 @@ /* Global variables ----------------------------------------------------------*/ /* Private constant data -----------------------------------------------------*/ -#define MAX_LINE_LENGTH 64 +#define MAX_LINE_LENGTH 256 +#define TO_STR_INNER(s) #s +#define TO_STR(s) TO_STR_INNER(s) /* Private variables ---------------------------------------------------------*/ /* Private function prototypes -----------------------------------------------*/ @@ -131,8 +133,40 @@ void ASCII_protocol_process_line(const uint8_t* buffer, size_t len, StreamSink& respond(response_channel, use_checksum, "Flash Size: %#x KiB", STM_ID_GetFlashSize()); respond(response_channel, use_checksum, "Serial number: %s", serial_number_str); -// } else if (cmd[0] == 'r') { // read property -// } else if (cmd[0] == 'w') { // write property + } else if (cmd[0] == 'r') { // read property + char name[MAX_LINE_LENGTH]; + int numscan = sscanf(cmd, "r %" TO_STR(MAX_LINE_LENGTH) "s", name); + if (numscan < 1) { + respond(response_channel, use_checksum, "invalid command format"); + } else { + Endpoint* endpoint = application_endpoints->get_by_name(name, sizeof(name)); + if (!endpoint) { + respond(response_channel, use_checksum, "invalid property"); + } else { + char response[10]; + bool success = endpoint->get_string(response, sizeof(response)); + if (!success) + respond(response_channel, use_checksum, "not implemented"); + else + respond(response_channel, use_checksum, response); + } + } + } else if (cmd[0] == 'w') { // write property + char name[MAX_LINE_LENGTH]; + char value[MAX_LINE_LENGTH]; + int numscan = sscanf(cmd, "w %" TO_STR(MAX_LINE_LENGTH) "s %" TO_STR(MAX_LINE_LENGTH) "s", name, value); + if (numscan < 1) { + respond(response_channel, use_checksum, "invalid command format"); + } else { + Endpoint* endpoint = application_endpoints->get_by_name(name, sizeof(name)); + if (!endpoint) { + respond(response_channel, use_checksum, "invalid property"); + } else { + bool success = endpoint->set_string(value, sizeof(value)); + if (!success) + respond(response_channel, use_checksum, "not implemented"); + } + } } else if (cmd[0] == 'h') { // HALT for(size_t i = 0; i < AXIS_COUNT; i++){ diff --git a/Firmware/MotorControl/protocol.hpp b/Firmware/MotorControl/protocol.hpp index bd853608..cb0f666b 100644 --- a/Firmware/MotorControl/protocol.hpp +++ b/Firmware/MotorControl/protocol.hpp @@ -407,12 +407,15 @@ class Endpoint { public: //const char* const name_; virtual void handle(const uint8_t* input, size_t input_length, StreamSink* output) = 0; + virtual bool get_string(char * output, size_t length) { return false; }; + virtual bool set_string(char * buffer, size_t length) { return false; } }; class EndpointProvider { public: virtual size_t get_endpoint_count() = 0; virtual void write_json(size_t id, StreamSink* output) = 0; + virtual Endpoint* get_by_name(char * name, size_t length) = 0; virtual void register_endpoints(Endpoint** list, size_t id, size_t length) = 0; }; @@ -456,6 +459,9 @@ public: void register_endpoints(Endpoint** list, size_t id, size_t length) { // no action } + Endpoint* get_by_name(const char * name, size_t length) { + return nullptr; + } std::tuple<> get_names_as_tuple() const { return std::tuple<>(); } }; @@ -485,6 +491,12 @@ public: subsequent_members_.write_json(id + TMember::endpoint_count, output); } + Endpoint* get_by_name(const char * name, size_t length) { + Endpoint* result = this_member_.get_by_name(name, length); + if (result) return result; + else return subsequent_members_.get_by_name(name, length); + } + void register_endpoints(Endpoint** list, size_t id, size_t length) /*final*/ { this_member_.register_endpoints(list, id, length); subsequent_members_.register_endpoints(list, id + TMember::endpoint_count, length); @@ -516,6 +528,14 @@ public: write_string("]}", output); } + Endpoint* get_by_name(const char * name, size_t length) { + size_t segment_length = strlen(name); + if (!strncmp(name, name_, length)) + return member_list_.get_by_name(name + segment_length + 1, length - segment_length - 1); + else + return nullptr; + } + void register_endpoints(Endpoint** list, size_t id, size_t length) { member_list_.register_endpoints(list, id, length); } @@ -529,6 +549,11 @@ ProtocolObject make_protocol_object(const char * name, TMembers&&.. return ProtocolObject(name, std::forward(member_list)...); } + +// TODO: move to cpp_utils +#define ENABLE_IF_SAME(a, b, type) \ + template typename std::enable_if_t::value, bool> + template class ProtocolProperty : public Endpoint { public: @@ -585,6 +610,71 @@ public: write_string("}", output); } + Endpoint* get_by_name(const char * name, size_t length) { + if (!strncmp(name, name_, length)) + return this; + else + return nullptr; + } + + + // *** ASCII protocol handlers *** + + ENABLE_IF_SAME(std::decay_t, float, bool) + get_string_ex(char * buffer, size_t length, int) { + snprintf(buffer, length, "%f", *property_); + return true; + } + ENABLE_IF_SAME(std::decay_t, int32_t, bool) + get_string_ex(char * buffer, size_t length, int) { + snprintf(buffer, length, "%ld", *property_); + return true; + } + ENABLE_IF_SAME(std::decay_t, uint32_t, bool) + get_string_ex(char * buffer, size_t length, int) { + snprintf(buffer, length, "%lu", *property_); + return true; + } + ENABLE_IF_SAME(std::decay_t, bool, bool) + get_string_ex(char * buffer, size_t length, int) { + buffer[0] = (*property_) ? '1' : '0'; + buffer[1] = 0; + return true; + } + bool get_string_ex(char * buffer, size_t length, ...) { + return false; + } + bool get_string(char * buffer, size_t length) final { + return get_string_ex(buffer, length, 0); + } + ENABLE_IF_SAME(TProperty, float, bool) + set_string_ex(char * buffer, size_t length, int) { + return sscanf(buffer, "%f", property_) == 1; + } + ENABLE_IF_SAME(TProperty, int32_t, bool) + set_string_ex(char * buffer, size_t length, int) { + return sscanf(buffer, "%ld", property_) == 1; + } + ENABLE_IF_SAME(TProperty, uint32_t, bool) + set_string_ex(char * buffer, size_t length, int) { + return sscanf(buffer, "%lu", property_) == 1; + } + ENABLE_IF_SAME(TProperty, bool, bool) + set_string_ex(char * buffer, size_t length, int) { + int val; + if (sscanf(buffer, "%d", &val) != 1) + return false; + *property_ = val; + return true; + } + bool set_string_ex(char * buffer, size_t length, ...) { + return false; + } + bool set_string(char * buffer, size_t length) final { + //__asm ("bkpt"); + return set_string_ex(buffer, length, 0); + } + void register_endpoints(Endpoint** list, size_t id, size_t length) { if (id < length) list[id] = this; @@ -686,7 +776,7 @@ struct PropertyListFactory { template -class ProtocolFunction : Endpoint { +class ProtocolFunction : public Endpoint { public: static constexpr size_t endpoint_count = 1 + MemberList...>::endpoint_count; template @@ -722,6 +812,10 @@ public: write_string("]}", output); } + Endpoint* get_by_name(const char * name, size_t length) { + return nullptr; // can't address functions by name + } + void register_endpoints(Endpoint** list, size_t id, size_t length) { if (id < length) list[id] = this; @@ -765,6 +859,14 @@ public: void register_endpoints(Endpoint** list, size_t id, size_t length) final { return member_list_.register_endpoints(list, id, length); } + Endpoint* get_by_name(char * name, size_t length) final { + for (size_t i = 0; i < length; i++) { + if (name[i] == '.') + name[i] = 0; + } + name[length-1] = 0; + return member_list_.get_by_name(name, length); + } T& member_list_; }; @@ -775,5 +877,6 @@ void set_application_endpoints(EndpointProvider* endpoints); extern Endpoint* endpoints_[]; extern size_t n_endpoints_; extern const size_t max_endpoints_; +extern EndpointProvider* application_endpoints; #endif