mirror of
https://github.com/odriverobotics/ODrive.git
synced 2026-09-12 14:27:20 +08:00
add property read/write functionality to ASCII protocol
Command examples: r vbus_voltage w axis0.config.enable_step_dir 1
This commit is contained in:
@@ -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++){
|
||||
|
||||
@@ -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<TMembers...> make_protocol_object(const char * name, TMembers&&..
|
||||
return ProtocolObject<TMembers...>(name, std::forward<TMembers>(member_list)...);
|
||||
}
|
||||
|
||||
|
||||
// TODO: move to cpp_utils
|
||||
#define ENABLE_IF_SAME(a, b, type) \
|
||||
template<typename T = a> typename std::enable_if_t<std::is_same<T, b>::value, bool>
|
||||
|
||||
template<typename TProperty>
|
||||
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<TProperty>, float, bool)
|
||||
get_string_ex(char * buffer, size_t length, int) {
|
||||
snprintf(buffer, length, "%f", *property_);
|
||||
return true;
|
||||
}
|
||||
ENABLE_IF_SAME(std::decay_t<TProperty>, 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<TProperty>, 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<TProperty>, 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<TProperty, TProperties...> {
|
||||
|
||||
|
||||
template<typename TObj, typename TRet, typename ... TArgs>
|
||||
class ProtocolFunction : Endpoint {
|
||||
class ProtocolFunction : public Endpoint {
|
||||
public:
|
||||
static constexpr size_t endpoint_count = 1 + MemberList<ProtocolProperty<TArgs>...>::endpoint_count;
|
||||
template<typename ... TNames>
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user