1
0
Fork 0
mirror of https://github.com/iNavFlight/inav.git synced 2025-07-13 19:40:27 +03:00

Add support for getting and setting arbitrary settings via MSPv2

Add MSP2_COMMON_SETTING for retrieving arbitrary settings and
MSP2_COMMON_SET_SETTING for setting them. This exposes any setting
which was only available from the CLI via MSP now.

Add support in settings.rb to generate a JSON file with all the
settings, their types and their possible values for settings using
a table. This file can be used by clients to properly format
messages for settings over MSP.
This commit is contained in:
Alberto García Hierro 2017-10-07 10:23:47 +01:00
parent cce3a25e35
commit ff18b726d3
7 changed files with 232 additions and 11 deletions

1
.gitignore vendored
View file

@ -22,3 +22,4 @@ README.pdf
# build generated files # build generated files
/src/main/fc/settings_generated.h /src/main/fc/settings_generated.h
/src/main/fc/settings_generated.c /src/main/fc/settings_generated.c
/settings.json

View file

@ -939,7 +939,7 @@ CLEAN_ARTIFACTS += $(TARGET_ELF) $(TARGET_OBJS) $(TARGET_MAP)
$(OBJECT_DIR)/$(TARGET)/build/version.o : $(TARGET_SRC) $(OBJECT_DIR)/$(TARGET)/build/version.o : $(TARGET_SRC)
# Settings generator # Settings generator
.PHONY: .FORCE clean-settings .PHONY: .FORCE settings clean-settings
UTILS_DIR = $(ROOT)/src/utils UTILS_DIR = $(ROOT)/src/utils
SETTINGS_GENERATOR = $(UTILS_DIR)/settings.rb SETTINGS_GENERATOR = $(UTILS_DIR)/settings.rb
BUILD_STAMP = $(UTILS_DIR)/build_stamp.rb BUILD_STAMP = $(UTILS_DIR)/build_stamp.rb
@ -959,6 +959,9 @@ $(STAMP): .FORCE
$(V1) echo "settings.yaml -> settings_generated.h, settings_generated.c" "$(STDOUT)" $(V1) echo "settings.yaml -> settings_generated.h, settings_generated.c" "$(STDOUT)"
$(V1) CFLAGS="$(CFLAGS)" TARGET=$(TARGET) ruby $(SETTINGS_GENERATOR) . $(SETTINGS_FILE) $(V1) CFLAGS="$(CFLAGS)" TARGET=$(TARGET) ruby $(SETTINGS_GENERATOR) . $(SETTINGS_FILE)
settings-json:
$(V0) CFLAGS="$(CFLAGS)" TARGET=$(TARGET) ruby $(SETTINGS_GENERATOR) . $(SETTINGS_FILE) --json settings.json
clean-settings: clean-settings:
$(V1) $(RM) $(GENERATED_SETTINGS) $(V1) $(RM) $(GENERATED_SETTINGS)

View file

@ -54,6 +54,7 @@
#include "fc/rc_controls.h" #include "fc/rc_controls.h"
#include "fc/rc_modes.h" #include "fc/rc_modes.h"
#include "fc/runtime_config.h" #include "fc/runtime_config.h"
#include "fc/settings.h"
#include "flight/failsafe.h" #include "flight/failsafe.h"
#include "flight/imu.h" #include "flight/imu.h"
@ -1971,6 +1972,148 @@ static mspResult_e mspFcProcessInCommand(uint16_t cmdMSP, sbuf_t *src)
return MSP_RESULT_ACK; return MSP_RESULT_ACK;
} }
static const setting_t *mspReadSettingName(sbuf_t *src)
{
char name[SETTING_MAX_NAME_LENGTH];
uint8_t c;
size_t s = 0;
while (1) {
if (!sbufReadU8Safe(&c, src)) {
return NULL;
}
name[s++] = c;
if (c == '\0') {
break;
}
if (s == SETTING_MAX_NAME_LENGTH) {
// Name is too long
return NULL;
}
}
return setting_find(name);
}
static bool mspSettingCommand(sbuf_t *dst, sbuf_t *src)
{
const setting_t *setting = mspReadSettingName(src);
if (!setting) {
return false;
}
const void *ptr = setting_get_value_pointer(setting);
switch (SETTING_TYPE(setting)) {
case VAR_UINT8:
FALLTHROUGH;
case VAR_INT8:
sbufWriteU8(dst, *((uint8_t*)ptr));
break;
case VAR_UINT16:
FALLTHROUGH;
break;
case VAR_INT16:
sbufWriteU16(dst, *((uint16_t*)ptr));
break;
case VAR_UINT32:
sbufWriteU32(dst, *((uint32_t*)ptr));
break;
case VAR_FLOAT:
{
float *val = (float *)ptr;
sbufWriteData(dst, val, sizeof(float));
}
break;
}
return true;
}
static bool mspSetSettingCommand(sbuf_t *dst, sbuf_t *src)
{
UNUSED(dst);
const setting_t *setting = mspReadSettingName(src);
if (!setting) {
return false;
}
setting_min_t min = setting_get_min(setting);
setting_max_t max = setting_get_max(setting);
void *ptr = setting_get_value_pointer(setting);
switch (SETTING_TYPE(setting)) {
case VAR_UINT8:
{
uint8_t val;
if (!sbufReadU8Safe(&val, src)) {
return false;
}
if (val > max) {
return false;
}
*((uint8_t*)ptr) = val;
}
break;
case VAR_INT8:
{
int8_t val;
if (!sbufReadI8Safe(&val, src)) {
return false;
}
if (val < min || val > (int8_t)max) {
return false;
}
*((int8_t*)ptr) = val;
}
break;
case VAR_UINT16:
{
uint16_t val;
if (!sbufReadU16Safe(&val, src)) {
return false;
}
if (val > max) {
return false;
}
*((uint16_t*)ptr) = val;
}
break;
case VAR_INT16:
{
int16_t val;
if (!sbufReadI16Safe(&val, src)) {
return false;
}
if (val < min || val > (int16_t)max) {
return false;
}
*((int16_t*)ptr) = val;
}
break;
case VAR_UINT32:
{
uint32_t val;
if (!sbufReadU32Safe(&val, src)) {
return false;
}
if (val > max) {
return false;
}
*((uint32_t*)ptr) = val;
}
break;
case VAR_FLOAT:
{
float val;
if (!sbufReadDataSafe(src, &val, sizeof(float))) {
return false;
}
*((float*)ptr) = val;
}
break;
}
return true;
}
/* /*
* Returns MSP_RESULT_ACK, MSP_RESULT_ERROR or MSP_RESULT_NO_REPLY * Returns MSP_RESULT_ACK, MSP_RESULT_ERROR or MSP_RESULT_NO_REPLY
*/ */
@ -2000,6 +2143,10 @@ mspResult_e mspFcProcessCommand(mspPacket_t *cmd, mspPacket_t *reply, mspPostPro
mspFcDataFlashReadCommand(dst, src); mspFcDataFlashReadCommand(dst, src);
ret = MSP_RESULT_ACK; ret = MSP_RESULT_ACK;
#endif #endif
} else if (cmdMSP == MSP2_COMMON_SETTING) {
ret = mspSettingCommand(dst, src) ? MSP_RESULT_ACK : MSP_RESULT_ERROR;
} else if (cmdMSP == MSP2_COMMON_SET_SETTING) {
ret = mspSetSettingCommand(dst, src) ? MSP_RESULT_ACK : MSP_RESULT_ERROR;
} else { } else {
ret = mspFcProcessInCommand(cmdMSP, src); ret = mspFcProcessInCommand(cmdMSP, src);
} }

View file

@ -62,6 +62,19 @@ bool setting_name_exact_match(const setting_t *val, char *buf, const char *cmdli
return sl_strncasecmp(cmdline, buf, strlen(buf)) == 0 && var_name_length == strlen(buf); return sl_strncasecmp(cmdline, buf, strlen(buf)) == 0 && var_name_length == strlen(buf);
} }
const setting_t *setting_find(const char *name)
{
char buf[SETTING_MAX_NAME_LENGTH];
for (int ii = 0; ii < SETTINGS_TABLE_COUNT; ii++) {
const setting_t *setting = &settingsTable[ii];
setting_get_name(setting, buf);
if (strcmp(buf, name) == 0) {
return setting;
}
}
return NULL;
}
pgn_t setting_get_pgn(const setting_t *val) pgn_t setting_get_pgn(const setting_t *val)
{ {
uint16_t pos = val - (const setting_t *)settingsTable; uint16_t pos = val - (const setting_t *)settingsTable;

View file

@ -70,6 +70,9 @@ extern const setting_t settingsTable[];
void setting_get_name(const setting_t *val, char *buf); void setting_get_name(const setting_t *val, char *buf);
bool setting_name_contains(const setting_t *val, char *buf, const char *cmdline); bool setting_name_contains(const setting_t *val, char *buf, const char *cmdline);
bool setting_name_exact_match(const setting_t *val, char *buf, const char *cmdline, uint8_t var_name_length); bool setting_name_exact_match(const setting_t *val, char *buf, const char *cmdline, uint8_t var_name_length);
// Returns a setting_t with the exact name (case sensitive), or
// NULL if no setting with that name exists.
const setting_t *setting_find(const char *name);
pgn_t setting_get_pgn(const setting_t *val); pgn_t setting_get_pgn(const setting_t *val);
// Returns a pointer to the actual value stored by // Returns a pointer to the actual value stored by
// the setting_t. The returned value might be modified. // the setting_t. The returned value might be modified.

View file

@ -17,3 +17,5 @@
#define MSP2_COMMON_TZ 0x1001 //out message Gets the TZ offset for the local time (returns: minutes(i16)) #define MSP2_COMMON_TZ 0x1001 //out message Gets the TZ offset for the local time (returns: minutes(i16))
#define MSP2_COMMON_SET_TZ 0x1002 //in message Sets the TZ offset for the local time (args: minutes(i16)) #define MSP2_COMMON_SET_TZ 0x1002 //in message Sets the TZ offset for the local time (args: minutes(i16))
#define MSP2_COMMON_SETTING 0x1003 //in/out message Returns the value for a setting
#define MSP2_COMMON_SET_SETTING 0x1004 //in message Sets the value for a setting

View file

@ -25,6 +25,8 @@
# along with this program. If not, see http://www.gnu.org/licenses/. # along with this program. If not, see http://www.gnu.org/licenses/.
require 'fileutils' require 'fileutils'
require 'getoptlong'
require 'json'
require 'set' require 'set'
require 'stringio' require 'stringio'
require 'tmpdir' require 'tmpdir'
@ -281,10 +283,8 @@ class Generator
end end
end end
@data = YAML.load_file(@settings_file) load_data
initialize_tables
check_conditions
sanitize_fields sanitize_fields
initialize_name_encoder initialize_name_encoder
initialize_value_encoder initialize_value_encoder
@ -293,6 +293,29 @@ class Generator
write_impl_file(impl_file) write_impl_file(impl_file)
end end
def write_json(jsonFile)
load_data
sanitize_fields(true)
settings = Hash.new
foreach_member do |group, member|
name = member["name"]
s = {
"type" => member["type"],
}
table = member["table"]
if table
s["table"] = @tables[table]
end
settings[name] = s
end
File.open(jsonFile, "w") do |f|
f.write(JSON.pretty_generate(settings))
end
end
def print_stats def print_stats
puts "#{@count} settings" puts "#{@count} settings"
puts "words table has #{@name_encoder.words.length} words" puts "words table has #{@name_encoder.words.length} words"
@ -316,6 +339,13 @@ class Generator
private private
def load_data
@data = YAML.load_file(@settings_file)
initialize_tables
check_conditions
end
def header_file def header_file
File.join(@output_dir, "settings_generated.h") File.join(@output_dir, "settings_generated.h")
end end
@ -651,11 +681,11 @@ class Generator
end end
end end
def sanitize_fields def sanitize_fields(all=false)
pending_types = Hash.new pending_types = Hash.new
has_booleans = false has_booleans = false
foreach_enabled_member do |group, member| block = Proc.new do |group, member|
if !group["name"] if !group["name"]
raise "Missing group name" raise "Missing group name"
end end
@ -682,6 +712,8 @@ class Generator
end end
end end
all ? foreach_member(&block) : foreach_enabled_member(&block)
if has_booleans if has_booleans
@tables[OFF_ON_TABLE["name"]] = OFF_ON_TABLE @tables[OFF_ON_TABLE["name"]] = OFF_ON_TABLE
@used_tables << OFF_ON_TABLE["name"] @used_tables << OFF_ON_TABLE["name"]
@ -836,7 +868,7 @@ class Generator
end end
def usage def usage
puts "Usage: ruby #{__FILE__} <source_dir> <settings_file>" puts "Usage: ruby #{__FILE__} <source_dir> <settings_file> [--json <json_file>]"
end end
if __FILE__ == $0 if __FILE__ == $0
@ -851,10 +883,30 @@ if __FILE__ == $0
end end
gen = Generator.new(src_root, settings_file) gen = Generator.new(src_root, settings_file)
gen.write_files()
opts = GetoptLong.new(
[ "--help", "-h", GetoptLong::NO_ARGUMENT ],
[ "--json", "-j", GetoptLong::REQUIRED_ARGUMENT ],
)
jsonFile = nil
opts.each do |opt, arg|
case opt
when "--help"
usage()
exit(0)
when "--json"
jsonFile = arg
end
end
if jsonFile
gen.write_json(jsonFile)
else
gen.write_files()
if verbose if verbose
gen.print_stats() gen.print_stats()
end end
end
end end