diff --git a/radio/src/lua/api_general.cpp b/radio/src/lua/api_general.cpp index c4b006b3e..0d30b4586 100644 --- a/radio/src/lua/api_general.cpp +++ b/radio/src/lua/api_general.cpp @@ -327,6 +327,8 @@ void luaGetValueAndPush(lua_State* L, int src) */ bool luaFindFieldByName(const char * name, LuaField & field, unsigned int flags) { + strncpy(field.name, name, sizeof(field.name) - 1); + field.name[sizeof(field.name) - 1] = '\0'; // TODO better search method (binary lookup) for (unsigned int n=0; n= fieldLen + 1 && isdigit(name[fieldLen])) { + index = name[fieldLen] - '0'; + if (len >= fieldLen + 2 && isdigit(name[fieldLen + 1])) { + index = 10 * index + (name[fieldLen + 1] - '0'); + } + index -= 1; } else { continue; } if (index < luaMultipleFields[n].count) { - if(luaMultipleFields[n].id == MIXSRC_FIRST_TELEM) - field.id = luaMultipleFields[n].id + index*3; - else - field.id = luaMultipleFields[n].id + index; + if(luaMultipleFields[n].id == MIXSRC_FIRST_TELEM) { + index *= 3; + if (name[len - 1] == '-') + index += 1; + else if (name[len - 1] == '+') + index += 2; + } + field.id = luaMultipleFields[n].id + index; if (flags & FIND_FIELD_DESC) { snprintf(field.desc, sizeof(field.desc)-1, luaMultipleFields[n].desc, index+1); field.desc[sizeof(field.desc)-1] = '\0'; @@ -402,6 +409,73 @@ bool luaFindFieldByName(const char * name, LuaField & field, unsigned int flags) return false; // not found } +// Return field data for a given field id +bool luaFindFieldById(int id, LuaField & field, unsigned int flags) +{ + field.id = id; + field.name[sizeof(field.name) - 1] = '\0'; + field.desc[0] = '\0'; + + // TODO better search method (binary lookup) + for (unsigned int n = 0; n < DIM(luaSingleFields); ++n) { + if (id == luaSingleFields[n].id) { + strncpy(field.name, luaSingleFields[n].name, sizeof(field.name) - 1); + if (flags & FIND_FIELD_DESC) { + strncpy(field.desc, luaSingleFields[n].desc, sizeof(field.desc) - 1); + field.desc[sizeof(field.desc) - 1] = '\0'; + } + return true; + } + } + + // search in multiples + for (unsigned int n = 0; n < DIM(luaMultipleFields); ++n) { + int index = id - luaMultipleFields[n].id; + if (0 <= index && index < luaMultipleFields[n].count) { + int index2 = 0; + if(luaMultipleFields[n].id == MIXSRC_FIRST_TELEM) { + index2 = index % 3; + index /= 3; + } + switch (index2) { + case 0: + snprintf(field.name, sizeof(field.name), "%s%i", luaMultipleFields[n].name, index + 1); + break; + case 1: + snprintf(field.name, sizeof(field.name), "%s%i-", luaMultipleFields[n].name, index + 1); + break; + case 2: + snprintf(field.name, sizeof(field.name), "%s%i+", luaMultipleFields[n].name, index + 1); + } + if (flags & FIND_FIELD_DESC) + snprintf(field.desc, sizeof(field.desc), luaMultipleFields[n].desc, index + 1); + return true; + } + } + + // search in telemetry + for (int i = 0; i < MAX_TELEMETRY_SENSORS; i++) { + if (isTelemetryFieldAvailable(i)) { + int index = id - (MIXSRC_FIRST_TELEM + 3 * i); + if (0 <= index && index < 3) { + const char* sensorName = g_model.telemetrySensors[i].label; + switch (index) { + case 0: + snprintf(field.name, sizeof(field.name), "%s", sensorName); + break; + case 1: + snprintf(field.name, sizeof(field.name), "%s-", sensorName); + break; + case 2: + snprintf(field.name, sizeof(field.name), "%s+", sensorName); + } + } + } + } + + return false; // not found +} + /*luadoc @function getRotEncSpeed() @@ -739,7 +813,7 @@ static int luaCrossfireTelemetryPush(lua_State * L) #endif /*luadoc -@function getFieldInfo(name) +@function getFieldInfo(source) Return detailed information about field (source) @@ -752,7 +826,7 @@ The list of valid sources is available: | 2.2 | [X9D and X9D+](http://downloads.open-tx.org/2.2/release/firmware/lua_fields_x9d.txt), [X9E](http://downloads.open-tx.org/2.2/release/firmware/lua_fields_x9e.txt), [Horus](http://downloads.open-tx.org/2.2/release/firmware/lua_fields_x12s.txt) | | 2.3 | [X9D and X9D+](http://downloads.open-tx.org/2.3/release/firmware/lua_fields_x9d.txt), [X9E](http://downloads.open-tx.org/2.3/release/firmware/lua_fields_x9e.txt), [X7](http://downloads.open-tx.org/2.3/release/firmware/lua_fields_x7.txt), [Horus](http://downloads.open-tx.org/2.3/release/firmware/lua_fields_x12s.txt) | -@param name (string) name of the field +@param source can be an index (number) (which was obtained by `getFieldInfo` or `getSourceIndex`) or a name (string) of the source. @retval table information about requested field, table elements: * `id` (number) field identifier @@ -766,13 +840,18 @@ The list of valid sources is available: */ static int luaGetFieldInfo(lua_State * L) { - const char * what = luaL_checkstring(L, 1); + bool found; LuaField field; - bool found = luaFindFieldByName(what, field, FIND_FIELD_DESC); + + if (lua_isnumber(L, 1)) + found = luaFindFieldById(luaL_checkinteger(L, 1), field, FIND_FIELD_DESC); + else + found = luaFindFieldByName(luaL_checkstring(L, 1), field, FIND_FIELD_DESC); + if (found) { lua_newtable(L); lua_pushtableinteger(L, "id", field.id); - lua_pushtablestring(L, "name", what); + lua_pushtablestring(L, "name", field.name); lua_pushtablestring(L, "desc", field.desc); if (field.id >= MIXSRC_FIRST_TELEM && field.id <= MIXSRC_LAST_TELEM) { TelemetrySensor & telemetrySensor = g_model.telemetrySensors[(int)((field.id-MIXSRC_FIRST_TELEM)/3)]; @@ -806,8 +885,7 @@ To get a telemetry value simply use it's sensor name. For example: * to get the current altitude use the source "Alt" * to get the minimum altitude use the source "Alt-", to get the maximum use "Alt+" -@param source can be an identifier (number) (which was obtained by the getFieldInfo()) -or a name (string) of the source. +@param source can be an index (number) (which was obtained by `getFieldInfo` or `getSourceIndex`) or a name (string) of the source. @retval value current source value (number). Zero is returned for: * non-existing sources @@ -1998,39 +2076,174 @@ static int luaGetSwitchValue(lua_State * L) } /*luadoc -@function getPhysicalSwitches() +@function switches([first[, last]]) -@retval switches: table in the format `{{switchName, switchIndex}, ...}` +@param first: the first switch index. If `nil` or omitted, the first available switch is used. -Returns a list of physical switch positions. +@param last: the last switch index. If `nil` or omitted, the last available switch is used. -@status current Introduced in 2.6 +This is an iterator function over switch positions. `for switchIndex, switchName in switches() do ...` will iterate over all available switch positions. + +@status current Introduced in 2.7 */ -static int luaGetPhysicalSwitches(lua_State * L) +static int luaNextSwitch(lua_State * L) { - int i = 1; - lua_newtable(L); - for (swsrc_t idx = 1 - SWSRC_FIRST_TRIM; idx < SWSRC_FIRST_TRIM; idx++) { - if (idx != SWSRC_NONE && isSwitchAvailableInLogicalSwitches(idx)) { + swsrc_t last = luaL_checkinteger(L, 1); + swsrc_t idx = luaL_checkinteger(L, 2); + + while (++idx <= last) { + if (isSwitchAvailableInLogicalSwitches(idx)) { char* name = getSwitchPositionName(idx); - lua_pushinteger(L, i++); - lua_createtable (L, 2, 0); - - lua_pushinteger(L, 1); - lua_pushstring(L, name); - lua_settable(L, -3); - - lua_pushinteger(L, 2); lua_pushinteger(L, idx); - lua_settable(L, -3); - - lua_settable(L, -3); + lua_pushstring(L, name); + return 2; } } + + lua_pushnil(L); return 1; } +static int luaSwitches(lua_State * L) +{ + swsrc_t first; + swsrc_t last; + + if (lua_isnumber(L, 1)) { + first = luaL_checkinteger(L, 1) - 1; + if (first < SWSRC_FIRST - 1) + first = SWSRC_FIRST - 1; + } else + first = SWSRC_FIRST - 1; + + if (lua_isnumber(L, 2)) { + last = luaL_checkinteger(L, 2); + if (last > SWSRC_LAST) + last = SWSRC_LAST; + } else + last = SWSRC_LAST; + + lua_pushcfunction(L, luaNextSwitch); + lua_pushinteger(L, last); + lua_pushinteger(L, first); + return 3; +} + +/*luadoc +@function getSourceIndex(sourceName) + +@param sourceName: string naming a value source as it is shown on radio menus where you can select it. Notice that many names have special characters in them. + +@retval sourceIndex: integer. The source index, which can be used as input for `getSourceName(sourceIndex)`, `getValue(sourceIndex)`, and `getFieldInfo(sourceIndex)`. + +@notice the source names shown on the screen are not the same as the names used by `getFieldInfo` and `getValue`. But the indices are the same, so `getValue(index)` will work with the indices obtained here. +This function is rather time consuming, and should not be used repeatedly in a script, if it can be avoided. + +@status current Introduced in 2.7 +*/ + +static int luaGetSourceIndex(lua_State * L) +{ + const char * name = luaL_checkstring(L, 1); + bool found = false; + mixsrc_t idx; + + for (idx = MIXSRC_NONE; idx <= MIXSRC_LAST_TELEM; idx++) { + if (isSourceAvailable(idx)) { + char* s = getSourceString(idx); + if (!strncasecmp(s, name, 31)) { + found = true; + break; + } + } + } + + if (found) + lua_pushinteger(L, idx); + else + lua_pushnil(L); + + return 1; +} + +/*luadoc +@function getSourceName(sourceIndex) + +@param sourceIndex: integer identifying a value source as returned by `getSourceIndex(sourceName)` or the `id` field in the table returned by `getFieldInfo`. + +@retval sourceName: string naming the value source as it is shown on radio menus where a source can be chosen. + +@notice the source names shown on the screen are not the same as the names used by `getFieldInfo` and `getValue`. But the indices are the same, so `getValue(index)` will work with the indices used here. + +@status current Introduced in 2.7 +*/ + +static int luaGetSourceName(lua_State * L) +{ + mixsrc_t idx = luaL_checkinteger(L, 1); + if (idx <= MIXSRC_LAST_TELEM && isSourceAvailable(idx)) { + char* name = getSourceString(idx); + lua_pushstring(L, name); + } + else + lua_pushnil(L); + return 1; +} + +/*luadoc +@function sources([first[, last]]) + +@param first: the first source index. If `nil` or omitted, the first available source is used. + +@param last: the last soure index. If `nil` or omitted, the last available source is used. + +This is an iterator function over value sources. `for sourceIndex, sourceName in sources() do ...` will iterate over all available value sources. + +@status current Introduced in 2.7 +*/ + +static int luaNextSource(lua_State * L) +{ + mixsrc_t last = luaL_checkinteger(L, 1); + mixsrc_t idx = luaL_checkinteger(L, 2); + + while (++idx <= last) { + if (isSourceAvailable(idx)) { + char* name = getSourceString(idx); + lua_pushinteger(L, idx); + lua_pushstring(L, name); + return 2; + } + } + + lua_pushnil(L); + return 1; +} + +static int luaSources(lua_State * L) +{ + mixsrc_t first; + mixsrc_t last; + + if (lua_isnumber(L, 1)) + first = luaL_checkinteger(L, 1) - 1; + else + first = MIXSRC_NONE - 1; + + if (lua_isnumber(L, 2)) { + last = luaL_checkinteger(L, 2); + if (last > INPUTSRC_LAST) + last = INPUTSRC_LAST; + } else + last = INPUTSRC_LAST; + + lua_pushcfunction(L, luaNextSource); + lua_pushinteger(L, last); + lua_pushinteger(L, first); + return 3; +} + const luaL_Reg opentxLib[] = { { "getTime", luaGetTime }, { "getDateTime", luaGetDateTime }, @@ -2095,7 +2308,10 @@ const luaL_Reg opentxLib[] = { { "getSwitchIndex", luaGetSwitchIndex }, { "getSwitchName", luaGetSwitchName }, { "getSwitchValue", luaGetSwitchValue }, - { "getPhysicalSwitches", luaGetPhysicalSwitches }, + { "switches", luaSwitches }, + { "getSourceIndex", luaGetSourceIndex }, + { "getSourceName", luaGetSourceName }, + { "sources", luaSources }, { nullptr, nullptr } /* sentinel */ }; diff --git a/radio/src/lua/lua_api.h b/radio/src/lua/lua_api.h index 612645afa..cd93e7519 100644 --- a/radio/src/lua/lua_api.h +++ b/radio/src/lua/lua_api.h @@ -208,10 +208,12 @@ extern uint8_t instructionsPercent; struct LuaField { uint16_t id; + char name[20]; char desc[50]; }; bool luaFindFieldByName(const char * name, LuaField & field, unsigned int flags=0); +bool luaFindFieldById(int id, LuaField & field, unsigned int flags=0); void luaLoadThemes(); void luaRegisterLibraries(lua_State * L); void registerBitmapClass(lua_State * L);