diff --git a/libraries/AP_Common/AP_Var.cpp b/libraries/AP_Common/AP_Var.cpp index 2b3e538dd8..9750b23541 100644 --- a/libraries/AP_Common/AP_Var.cpp +++ b/libraries/AP_Common/AP_Var.cpp @@ -5,9 +5,9 @@ // Free Software Foundation; either version 2.1 of the License, or (at // your option) any later version. // -/// The AP variable interface. This allows different types -/// of variables to be passed to blocks for floating point -/// math, memory management, etc. + +/// @file AP_Var.cpp +/// @brief The AP variable store. #if 0 # include @@ -17,7 +17,9 @@ extern "C" { extern void delay(unsigned long); } # define log(fmt, args...) #endif -#include "AP_Var.h" +#include + +#include // Global constants exported for general use. // @@ -133,6 +135,28 @@ void AP_Var::copy_name(char *buffer, size_t buffer_size) const strlcat_P(buffer, _name, buffer_size); } +// Find a variable by name. +// +AP_Var * +AP_Var::find(const char *name) +{ + AP_Var *vp; + + for (vp = first(); vp; vp = vp->next()) { + char name_buffer[32]; + + // copy the variable's name into our scratch buffer + vp->copy_name(name_buffer, sizeof(name_buffer)); + + // compare with the user-supplied name + if (!strcmp(name, name_buffer)) { + return vp; + } + } + return NULL; +} + + // Save the variable to EEPROM, if supported // bool AP_Var::save(void) @@ -611,3 +635,42 @@ AP_Var_group::_serialize_unserialize(void *buf, size_t buf_size, bool do_seriali } return total_size; } + +// Static pseudo-constant type IDs for known AP_VarT subclasses. +// +AP_Meta_class::Type_id AP_Var::k_typeid_float; ///< meta_type_id() value for AP_Float +AP_Meta_class::Type_id AP_Var::k_typeid_float16; ///< meta_type_id() value for AP_Float16 +AP_Meta_class::Type_id AP_Var::k_typeid_int32; ///< meta_type_id() value for AP_Int32 +AP_Meta_class::Type_id AP_Var::k_typeid_int16; ///< meta_type_id() value for AP_Int16 +AP_Meta_class::Type_id AP_Var::k_typeid_int8; ///< meta_type_id() value for AP_Int8 + +/// A special class used to initialise the k_typeid_* values that AP_Var exports. +/// +class AP_Var_typesetup +{ +public: + /// Constructor + /// + /// This constructor should be run just once by creating a static instance + /// of the class. It will initialise the k_typeid_* values for the well-known + /// AP_VarT subclasses. + /// + /// When a new subclass is created, a new k_typeid_* constant should also be + /// created and the list below should likewise be expanded. + /// + AP_Var_typesetup(void); +}; + +/// Initialise AP_Var's k_typeid_* values +AP_Var_typesetup::AP_Var_typesetup(void) +{ + AP_Var::k_typeid_float = AP_Meta_class::meta_type_id(); + AP_Var::k_typeid_float16 = AP_Meta_class::meta_type_id(); + AP_Var::k_typeid_int32 = AP_Meta_class::meta_type_id(); + AP_Var::k_typeid_int16 = AP_Meta_class::meta_type_id(); + AP_Var::k_typeid_int8 = AP_Meta_class::meta_type_id(); +} + +/// Cause the AP_Var_typesetup constructor to be run. +/// +static AP_Var_typesetup _typesetup __attribute__((used)); diff --git a/libraries/AP_Common/AP_Var.h b/libraries/AP_Common/AP_Var.h index 4d35a3a3c7..567109dbfb 100644 --- a/libraries/AP_Common/AP_Var.h +++ b/libraries/AP_Common/AP_Var.h @@ -9,12 +9,6 @@ /// @file AP_Var.h /// @brief A system for managing and storing variables that are of /// general interest to the system. -/// -/// - -/// The AP variable interface. This allows different types -/// of variables to be passed to blocks for floating point -/// math, memory management, etc. #ifndef AP_VAR_H #define AP_VAR_H @@ -152,6 +146,12 @@ public: /// static const Flags k_flag_unlisted = (1 << 3); + static AP_Meta_class::Type_id k_typeid_float; ///< meta_type_id() value for AP_Float + static AP_Meta_class::Type_id k_typeid_float16; ///< meta_type_id() value for AP_Float16 + static AP_Meta_class::Type_id k_typeid_int32; ///< meta_type_id() value for AP_Int32 + static AP_Meta_class::Type_id k_typeid_int16; ///< meta_type_id() value for AP_Int16 + static AP_Meta_class::Type_id k_typeid_int8; ///< meta_type_id() value for AP_Int8 + /// Constructor for a freestanding variable /// /// @param key The storage key to be associated with this variable. @@ -201,6 +201,16 @@ public: /// void copy_name(char *buffer, size_t bufferSize) const; + /// Find a variable by name. + /// + /// If the variable has no name, it cannot be found by this interface. + /// + /// @param name The full name of the variable to be found. + /// @return A pointer to the variable, or NULL if + /// it does not exist. + /// + static AP_Var *find(const char *name); + /// Save the current value of the variable to EEPROM. /// /// This interface works for any subclass that implements @@ -622,4 +632,47 @@ extern AP_Float AP_Float_unity; extern AP_Float AP_Float_negative_unity; extern AP_Float AP_Float_zero; + +/// Print the value of an AP_Var +/// +/// This function knows about the types listed in the AP_Var::k_typeid_* constants, +/// and it will print their value using Serial, which is assumed to be a BetterStream +/// serial port (e.g. FastSerial). +/// +/// @param vp The variable to print. +/// +extern void AP_Var_print(AP_Var *vp); + +#ifndef __AP_COMMON_MENU_H +# error Must include menu.h +#endif + +/// Menu function for setting an AP_Var. +/// +/// This function can be directly called from a Menu. It expects two args, the +/// first is the full name of a variable, the second is the value to set the variable +/// to. +/// +/// This function knows about the types listed in the AP_Var::k_typeid_* constants. +/// +/// @param argc The number of arguments; must be 3. +/// @param argv Arguments. argv[1] must contain a variable name, argv[2].f and .i +/// are consulted when setting the value. +/// @return Zero if the variable was set successfully, -1 if it could not be set. +/// +extern int8_t AP_Var_menu_set(uint8_t argc, const Menu::arg *argv); + +/// Menu function for displaying AP_Vars. +/// +/// This function can be directly called from a Menu. It expects zero or one argument. +/// If no arguments are supplied, all known variables are printed. If an argument is +/// supplied, it is expected to be the name of a variable and that variable will be +/// printed. +/// +/// @param argc The number of arguments, must be 1 or 2. +/// @param argv Argument array; argv[1] may be set to the name of a variable to show. +/// +extern int8_t AP_Var_menu_show(uint8_t argc, const Menu::arg *argv); + + #endif // AP_VAR_H diff --git a/libraries/AP_Common/AP_Var_menufuncs.cpp b/libraries/AP_Common/AP_Var_menufuncs.cpp new file mode 100644 index 0000000000..217eca7bc9 --- /dev/null +++ b/libraries/AP_Common/AP_Var_menufuncs.cpp @@ -0,0 +1,139 @@ +// -*- tab-width: 4; Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- +// +// This is free software; you can redistribute it and/or modify it under +// the terms of the GNU Lesser General Public License as published by the +// Free Software Foundation; either version 2.1 of the License, or (at +// your option) any later version. +// + +/// @file AP_Var_menufuncs.cpp +/// @brief Useful functions compatible with the menu system for +/// managing AP_Var variables. + +#include +#include + +void +AP_Var_print(AP_Var *vp) +{ + // try to print from variable types that we know + if (vp->meta_type_id() == AP_Var::k_typeid_float) { + + AP_Float *v = (AP_Float *)vp; + Serial.printf_P(PSTR("%f"), v->get()); + + } else if (vp->meta_type_id() == AP_Var::k_typeid_float16) { + + AP_Float16 *v = (AP_Float16 *)vp; + Serial.printf_P(PSTR("%f"), v->get()); + + } else if (vp->meta_type_id() == AP_Var::k_typeid_int32) { + + AP_Int32 *v = (AP_Int32 *)vp; + Serial.printf_P(PSTR("%ld"), v->get()); + + } else if (vp->meta_type_id() == AP_Var::k_typeid_int16) { + + AP_Int16 *v = (AP_Int16 *)vp; + Serial.printf_P(PSTR("%d"), v->get()); + + } else if (vp->meta_type_id() == AP_Var::k_typeid_int8) { + + AP_Int8 *v = (AP_Int8 *)vp; + Serial.printf_P(PSTR("%d"), v->get()); + + } else { + Serial.print_P(PSTR("??")); + } +} + +int8_t +AP_Var_menu_set(uint8_t argc, const Menu::arg *argv) +{ + AP_Var *vp; + + // check argument count + if (argc != 3) { + Serial.println_P(PSTR("missing name or value")); + return -1; + } + Serial.printf_P(PSTR("%s: "), argv[1].str); + + // search for the variable + vp = AP_Var::find(argv[1].str); + if (NULL == vp) { + Serial.println_P(PSTR("not found")); + return -1; + } + + // try to assign to variable types that we know + if (vp->meta_type_id() == AP_Var::k_typeid_float) { + + AP_Float *v = (AP_Float *)vp; + v->set(argv[2].f); + + } else if (vp->meta_type_id() == AP_Var::k_typeid_float16) { + + AP_Float16 *v = (AP_Float16 *)vp; + v->set(argv[2].f); + + } else if (vp->meta_type_id() == AP_Var::k_typeid_int32) { + + AP_Int32 *v = (AP_Int32 *)vp; + v->set(argv[2].i); + + } else if (vp->meta_type_id() == AP_Var::k_typeid_int16) { + + AP_Int16 *v = (AP_Int16 *)vp; + v->set(argv[2].i); + + } else if (vp->meta_type_id() == AP_Var::k_typeid_int8) { + + AP_Int8 *v = (AP_Int8 *)vp; + v->set(argv[2].i); + + } else { + Serial.println_P(PSTR("unknown type")); + return -1; + } + AP_Var_print(vp); + Serial.println(); + return 0; +} + +int8_t +AP_Var_menu_show(uint8_t argc, const Menu::arg *argv) +{ + AP_Var *vp; + + // if no arguments, show all variables + if (argc == 1) { + for (vp = AP_Var::first(); vp; vp = vp->next()) { + char name_buffer[32]; + + // get a displayable name for the variable + vp->copy_name(name_buffer, sizeof(name_buffer)); + if (name_buffer[0] == 0) { + strcpy_P(name_buffer, PSTR("??")); + } + + // print name and value + Serial.printf_P(PSTR("%-32.32s: "), name_buffer); + AP_Var_print(vp); + Serial.println(); + } + return 0; + } + + // show variable by name + Serial.printf_P(PSTR("%s: "), argv[1].str); + vp = AP_Var::find(argv[1].str); + if (NULL == vp) { + Serial.println_P(PSTR("not found")); + return -1; + } + AP_Var_print(vp); + Serial.println(); + + return 0; +}