Skip to content

Commit

Permalink
Merge pull request betaflight#7749 from blckmn/string_set
Browse files Browse the repository at this point in the history
Adding string settings
  • Loading branch information
mikeller authored Apr 15, 2019
2 parents ddd5041 + 4cb1f65 commit edb7ca3
Show file tree
Hide file tree
Showing 5 changed files with 257 additions and 129 deletions.
253 changes: 143 additions & 110 deletions src/main/cli/cli.c
Original file line number Diff line number Diff line change
Expand Up @@ -538,6 +538,10 @@ static void printValuePointer(const clivalue_t *var, const void *valuePointer, b
} else {
cliPrintf("OFF");
}
break;
case MODE_STRING:
cliPrintf("%s", (char *)valuePointer);
break;
}

if (valueIsCorrupted) {
Expand Down Expand Up @@ -756,7 +760,6 @@ static void cliSetVar(const clivalue_t *var, const uint32_t value)
}
*(uint32_t *)ptr = workValue;
break;

}
} else {
switch (var->type & VALUE_TYPE_MASK) {
Expand Down Expand Up @@ -3954,6 +3957,19 @@ static uint8_t getWordLength(char *bufBegin, char *bufEnd)
return bufEnd - bufBegin;
}

uint16_t cliGetSettingIndex(char *name, uint8_t length)
{
for (uint32_t i = 0; i < valueTableEntryCount; i++) {
const char *settingName = valueTable[i].name;

// ensure exact match when setting to prevent setting variables with shorter names
if (strncasecmp(name, settingName, strlen(settingName)) == 0 && length == strlen(settingName)) {
return i;
}
}
return valueTableEntryCount;
}

STATIC_UNIT_TESTED void cliSet(char *cmdline)
{
const uint32_t len = strlen(cmdline);
Expand All @@ -3977,140 +3993,157 @@ STATIC_UNIT_TESTED void cliSet(char *cmdline)
eqptr++;
eqptr = skipSpace(eqptr);

for (uint32_t i = 0; i < valueTableEntryCount; i++) {
const clivalue_t *val = &valueTable[i];

// ensure exact match when setting to prevent setting variables with shorter names
if (strncasecmp(cmdline, val->name, strlen(val->name)) == 0 && variableNameLength == strlen(val->name)) {
const uint16_t index = cliGetSettingIndex(cmdline, variableNameLength);
if (index >= valueTableEntryCount) {
cliPrintErrorLinef("INVALID NAME");
return;
}
const clivalue_t *val = &valueTable[index];

bool valueChanged = false;
int16_t value = 0;
switch (val->type & VALUE_MODE_MASK) {
case MODE_DIRECT: {
if ((val->type & VALUE_TYPE_MASK) == VAR_UINT32) {
uint32_t value = strtol(eqptr, NULL, 10);
bool valueChanged = false;
int16_t value = 0;
switch (val->type & VALUE_MODE_MASK) {
case MODE_DIRECT: {
if ((val->type & VALUE_TYPE_MASK) == VAR_UINT32) {
uint32_t value = strtol(eqptr, NULL, 10);

if (value <= val->config.u32Max) {
cliSetVar(val, value);
valueChanged = true;
}
} else {
int value = atoi(eqptr);
if (value <= val->config.u32Max) {
cliSetVar(val, value);
valueChanged = true;
}
} else {
int value = atoi(eqptr);

int min;
int max;
getMinMax(val, &min, &max);
int min;
int max;
getMinMax(val, &min, &max);

if (value >= min && value <= max) {
cliSetVar(val, value);
valueChanged = true;
}
}
if (value >= min && value <= max) {
cliSetVar(val, value);
valueChanged = true;
}
}
}

break;
case MODE_LOOKUP:
case MODE_BITSET: {
int tableIndex;
if ((val->type & VALUE_MODE_MASK) == MODE_BITSET) {
tableIndex = TABLE_OFF_ON;
} else {
tableIndex = val->config.lookup.tableIndex;
}
const lookupTableEntry_t *tableEntry = &lookupTables[tableIndex];
bool matched = false;
for (uint32_t tableValueIndex = 0; tableValueIndex < tableEntry->valueCount && !matched; tableValueIndex++) {
matched = tableEntry->values[tableValueIndex] && strcasecmp(tableEntry->values[tableValueIndex], eqptr) == 0;
break;
case MODE_LOOKUP:
case MODE_BITSET: {
int tableIndex;
if ((val->type & VALUE_MODE_MASK) == MODE_BITSET) {
tableIndex = TABLE_OFF_ON;
} else {
tableIndex = val->config.lookup.tableIndex;
}
const lookupTableEntry_t *tableEntry = &lookupTables[tableIndex];
bool matched = false;
for (uint32_t tableValueIndex = 0; tableValueIndex < tableEntry->valueCount && !matched; tableValueIndex++) {
matched = tableEntry->values[tableValueIndex] && strcasecmp(tableEntry->values[tableValueIndex], eqptr) == 0;

if (matched) {
value = tableValueIndex;
if (matched) {
value = tableValueIndex;

cliSetVar(val, value);
valueChanged = true;
}
}
cliSetVar(val, value);
valueChanged = true;
}
}
}

break;

case MODE_ARRAY: {
const uint8_t arrayLength = val->config.array.length;
char *valPtr = eqptr;

int i = 0;
while (i < arrayLength && valPtr != NULL) {
// skip spaces
valPtr = skipSpace(valPtr);

// process substring starting at valPtr
// note: no need to copy substrings for atoi()
// it stops at the first character that cannot be converted...
switch (val->type & VALUE_TYPE_MASK) {
default:
case VAR_UINT8:
{
// fetch data pointer
uint8_t *data = (uint8_t *)cliGetValuePointer(val) + i;
// store value
*data = (uint8_t)atoi((const char*) valPtr);
}
break;

break;
case VAR_INT8:
{
// fetch data pointer
int8_t *data = (int8_t *)cliGetValuePointer(val) + i;
// store value
*data = (int8_t)atoi((const char*) valPtr);
}
case MODE_ARRAY: {
const uint8_t arrayLength = val->config.array.length;
char *valPtr = eqptr;

break;
case VAR_UINT16:
{
// fetch data pointer
uint16_t *data = (uint16_t *)cliGetValuePointer(val) + i;
// store value
*data = (uint16_t)atoi((const char*) valPtr);
}
int i = 0;
while (i < arrayLength && valPtr != NULL) {
// skip spaces
valPtr = skipSpace(valPtr);

break;
case VAR_INT16:
{
// fetch data pointer
int16_t *data = (int16_t *)cliGetValuePointer(val) + i;
// store value
*data = (int16_t)atoi((const char*) valPtr);
}
// process substring starting at valPtr
// note: no need to copy substrings for atoi()
// it stops at the first character that cannot be converted...
switch (val->type & VALUE_TYPE_MASK) {
default:
case VAR_UINT8:
{
// fetch data pointer
uint8_t *data = (uint8_t *)cliGetValuePointer(val) + i;
// store value
*data = (uint8_t)atoi((const char*) valPtr);
}

break;
}
break;
case VAR_INT8:
{
// fetch data pointer
int8_t *data = (int8_t *)cliGetValuePointer(val) + i;
// store value
*data = (int8_t)atoi((const char*) valPtr);
}

// find next comma (or end of string)
valPtr = strchr(valPtr, ',') + 1;
break;
case VAR_UINT16:
{
// fetch data pointer
uint16_t *data = (uint16_t *)cliGetValuePointer(val) + i;
// store value
*data = (uint16_t)atoi((const char*) valPtr);
}

i++;
break;
case VAR_INT16:
{
// fetch data pointer
int16_t *data = (int16_t *)cliGetValuePointer(val) + i;
// store value
*data = (int16_t)atoi((const char*) valPtr);
}
}

// mark as changed
valueChanged = true;
break;
}

break;
// find next comma (or end of string)
valPtr = strchr(valPtr, ',') + 1;

i++;
}
}

if (valueChanged) {
cliPrintf("%s set to ", val->name);
cliPrintVar(val, 0);
} else {
cliPrintErrorLinef("INVALID VALUE");
cliPrintVarRange(val);
}
// mark as changed
valueChanged = true;

return;
break;
case MODE_STRING: {
char *valPtr = eqptr;
valPtr = skipSpace(valPtr);

const unsigned int len = strlen(valPtr);
const uint8_t min = val->config.string.minlength;
const uint8_t max = val->config.string.maxlength;
const bool updatable = ((val->config.string.flags & STRING_FLAGS_WRITEONCE) == 0 ||
strlen((char *)cliGetValuePointer(val)) == 0 ||
strncmp(valPtr, (char *)cliGetValuePointer(val), len) == 0);

if (updatable && len > 0 && len <= max) {
memset((char *)cliGetValuePointer(val), 0, max);
if (len >= min && strncmp(valPtr, emptyName, len)) {
strncpy((char *)cliGetValuePointer(val), valPtr, len);
}
valueChanged = true;
}
}
break;
}
cliPrintErrorLinef("INVALID NAME");

if (valueChanged) {
cliPrintf("%s set to ", val->name);
cliPrintVar(val, 0);
} else {
cliPrintErrorLinef("INVALID VALUE");
cliPrintVarRange(val);
}

return;
} else {
// no equals, check for matching variables.
cliGet(cmdline);
Expand Down
6 changes: 6 additions & 0 deletions src/main/cli/settings.c
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@
#include "pg/sdio.h"
#include "pg/rcdevice.h"
#include "pg/stats.h"
#include "pg/board.h"

#include "rx/rx.h"
#include "rx/cc2500_frsky_common.h"
Expand Down Expand Up @@ -1419,8 +1420,13 @@ const clivalue_t valueTable[] = {
#ifdef USE_PERSISTENT_STATS
{ "stats", VAR_INT8 | MASTER_VALUE | MODE_LOOKUP, .config.lookup = { TABLE_OFF_ON }, PG_STATS_CONFIG, offsetof(statsConfig_t, stats_enabled) },
{ "stats_total_flights", VAR_UINT32 | MASTER_VALUE, .config.u32Max = UINT32_MAX, PG_STATS_CONFIG, offsetof(statsConfig_t, stats_total_flights) },

{ "stats_total_time_s", VAR_UINT32 | MASTER_VALUE, .config.u32Max = UINT32_MAX, PG_STATS_CONFIG, offsetof(statsConfig_t, stats_total_time_s) },
{ "stats_total_dist_m", VAR_UINT32 | MASTER_VALUE, .config.u32Max = UINT32_MAX, PG_STATS_CONFIG, offsetof(statsConfig_t, stats_total_dist_m) },
#endif
{ "name", VAR_UINT8 | MASTER_VALUE | MODE_STRING, .config.string = { 1, MAX_NAME_LENGTH, STRING_FLAGS_NONE }, PG_PILOT_CONFIG, offsetof(pilotConfig_t, name) },
#ifdef USE_OSD
{ "display_name", VAR_UINT8 | MASTER_VALUE | MODE_STRING, .config.string = { 1, MAX_NAME_LENGTH, STRING_FLAGS_NONE }, PG_PILOT_CONFIG, offsetof(pilotConfig_t, displayName) },
#endif
};

Expand Down
21 changes: 16 additions & 5 deletions src/main/cli/settings.h
Original file line number Diff line number Diff line change
Expand Up @@ -160,17 +160,18 @@ typedef enum {
PROFILE_RATE_VALUE = (2 << VALUE_SECTION_OFFSET),
HARDWARE_VALUE = (3 << VALUE_SECTION_OFFSET), // Part of the master section, but used for the hardware definition

// value mode, bits 5-6
// value mode, bits 5-7
MODE_DIRECT = (0 << VALUE_MODE_OFFSET),
MODE_LOOKUP = (1 << VALUE_MODE_OFFSET),
MODE_ARRAY = (2 << VALUE_MODE_OFFSET),
MODE_BITSET = (3 << VALUE_MODE_OFFSET)
MODE_BITSET = (3 << VALUE_MODE_OFFSET),
MODE_STRING = (4 << VALUE_MODE_OFFSET),
} cliValueFlag_e;


#define VALUE_TYPE_MASK (0x07)
#define VALUE_SECTION_MASK (0x18)
#define VALUE_MODE_MASK (0x60)
#define VALUE_MODE_MASK (0xE0)

typedef struct cliMinMaxConfig_s {
const int16_t min;
Expand All @@ -190,18 +191,28 @@ typedef struct cliArrayLengthConfig_s {
const uint8_t length;
} cliArrayLengthConfig_t;

typedef struct cliStringLengthConfig_s {
const uint8_t minlength;
const uint8_t maxlength;
const uint8_t flags;
} cliStringLengthConfig_t;

#define STRING_FLAGS_NONE (0)
#define STRING_FLAGS_WRITEONCE (1 << 0)

typedef union {
cliLookupTableConfig_t lookup; // used for MODE_LOOKUP excl. VAR_UINT32
cliMinMaxConfig_t minmax; // used for MODE_DIRECT with signed parameters
cliMinMaxUnsignedConfig_t minmaxUnsigned; // used for MODE_DIRECT with unsigned parameters
cliArrayLengthConfig_t array; // used for MODE_ARRAY
cliStringLengthConfig_t string; // used for MODE_STRING
uint8_t bitpos; // used for MODE_BITSET
uint32_t u32Max; // used for MODE_DIRECT with VAR_UINT32
uint32_t u32Max; // used for MODE_DIRECT with VAR_UINT32
} cliValueConfig_t;

typedef struct clivalue_s {
const char *name;
const uint8_t type; // see cliValueFlag_e
const uint8_t type; // see cliValueFlag_e
const cliValueConfig_t config;

pgn_t pgn;
Expand Down
3 changes: 2 additions & 1 deletion src/test/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ TARGET_DIR = $(USER_DIR)/target
include $(ROOT)/make/system-id.mk
include $(ROOT)/make/targets_list.mk

VPATH := $(VPATH):$(USER_DIR):$(TEST_DIR)

# specify which files that are included in the test in addition to the unittest file.
# variables available:
# <test_name>_SRC
Expand Down Expand Up @@ -658,4 +660,3 @@ target_list:
@echo $(foreach target,$(ALT_TARGETS),$(target)\>$(call get_base_target,$(target)))
@echo ========== ALT/BASE FULL MAPPING ==========
@echo $(foreach target,$(VALID_TARGETS),$(target)\>$(call get_base_target,$(target)))

Loading

0 comments on commit edb7ca3

Please sign in to comment.