From c62aac7bded492a91543d1d9bf5a15f50ddb93c8 Mon Sep 17 00:00:00 2001 From: jfrickmann Date: Sun, 20 Jun 2021 15:52:16 -0400 Subject: [PATCH 1/6] Lua task preempts long running scripts instead of killing them Ported to EdgeTX from https://github.com/opentx/opentx/pull/8194 --- radio/src/gui/128x64/model_custom_scripts.cpp | 3 - radio/src/gui/128x64/view_telemetry.cpp | 1 - radio/src/gui/212x64/model_custom_scripts.cpp | 3 - radio/src/gui/212x64/view_telemetry.cpp | 1 - radio/src/lua/api_general.cpp | 7 +- radio/src/lua/interface.cpp | 981 ++++++++++-------- radio/src/lua/lua_api.h | 61 +- radio/src/lua/widgets.cpp | 57 +- radio/src/main.cpp | 33 +- 9 files changed, 640 insertions(+), 507 deletions(-) diff --git a/radio/src/gui/128x64/model_custom_scripts.cpp b/radio/src/gui/128x64/model_custom_scripts.cpp index 8bb90859a..a09b5ec37 100644 --- a/radio/src/gui/128x64/model_custom_scripts.cpp +++ b/radio/src/gui/128x64/model_custom_scripts.cpp @@ -157,9 +157,6 @@ void menuModelCustomScripts(event_t event) case SCRIPT_SYNTAX_ERROR: lcdDrawText(30*FW+2, y, "(error)"); break; - case SCRIPT_KILLED: - lcdDrawText(29*FW+2, y, "(killed)"); - break; default: lcdDrawNumber(34*FW, y, luaGetCpuUsed(scriptIndex), RIGHT); lcdDrawChar(34*FW, y, '%'); diff --git a/radio/src/gui/128x64/view_telemetry.cpp b/radio/src/gui/128x64/view_telemetry.cpp index 3b6835f64..a3d6afee0 100644 --- a/radio/src/gui/128x64/view_telemetry.cpp +++ b/radio/src/gui/128x64/view_telemetry.cpp @@ -177,7 +177,6 @@ bool displayTelemetryScreen() return false; // requested lua telemetry screen not available case SCRIPT_SYNTAX_ERROR: case SCRIPT_PANIC: - case SCRIPT_KILLED: luaError(lsScripts, state, false); return true; } diff --git a/radio/src/gui/212x64/model_custom_scripts.cpp b/radio/src/gui/212x64/model_custom_scripts.cpp index 12b904e04..d79ae9f88 100644 --- a/radio/src/gui/212x64/model_custom_scripts.cpp +++ b/radio/src/gui/212x64/model_custom_scripts.cpp @@ -150,9 +150,6 @@ void menuModelCustomScripts(event_t event) case SCRIPT_SYNTAX_ERROR: lcdDrawText(30*FW+2, y, "(error)"); break; - case SCRIPT_KILLED: - lcdDrawText(29*FW+2, y, "(killed)"); - break; default: lcdDrawNumber(34*FW, y, luaGetCpuUsed(scriptIndex), RIGHT); lcdDrawChar(34*FW, y, '%'); diff --git a/radio/src/gui/212x64/view_telemetry.cpp b/radio/src/gui/212x64/view_telemetry.cpp index e15fc5781..2fe96d768 100644 --- a/radio/src/gui/212x64/view_telemetry.cpp +++ b/radio/src/gui/212x64/view_telemetry.cpp @@ -179,7 +179,6 @@ bool displayTelemetryScreen() return false; // requested lua telemetry screen not available case SCRIPT_SYNTAX_ERROR: case SCRIPT_PANIC: - case SCRIPT_KILLED: luaError(lsScripts, state, false); return true; } diff --git a/radio/src/lua/api_general.cpp b/radio/src/lua/api_general.cpp index 9cd4db639..d324cdced 100644 --- a/radio/src/lua/api_general.cpp +++ b/radio/src/lua/api_general.cpp @@ -1081,12 +1081,13 @@ Stops key state machine. See [Key Events](../key_events.md) for the detailed des */ static int luaKillEvents(lua_State * L) { - uint8_t key = EVT_KEY_MASK(luaL_checkinteger(L, 1)); + event_t key = EVT_KEY_MASK(luaL_checkinteger(L, 1)); // prevent killing maskable keys (only in telemetry scripts) - // TODO add which tpye of script is running before p_call() + // TODO add which type of script is running before lua_resume() if (IS_MASKABLE(key)) { killEvents(key); - } + luaEmptyEventBuffer(); + } return 0; } diff --git a/radio/src/lua/interface.cpp b/radio/src/lua/interface.cpp index 755c67150..d7b56f140 100644 --- a/radio/src/lua/interface.cpp +++ b/radio/src/lua/interface.cpp @@ -39,20 +39,25 @@ extern "C" { #include } -#define PERMANENT_SCRIPTS_MAX_INSTRUCTIONS (10000/100) -#define MANUAL_SCRIPTS_MAX_INSTRUCTIONS (20000/100) -#define LUA_WARNING_INFO_LEN 64 +#define PERMANENT_SCRIPTS_MAX_INSTRUCTIONS 100 +#define LUA_WARNING_INFO_LEN 64 +#define LUA_TASK_PERIOD_TICKS 5 // 50 ms +// Since we may not run FG every time, keep the first two events in a buffer +event_t events[2] = {0, 0}; +// The main thread - lsScripts is now a coroutine +lua_State * L = nullptr; lua_State *lsScripts = nullptr; uint8_t luaState = 0; uint8_t luaScriptsCount = 0; bool luaLcdAllowed = false; ScriptInternalData scriptInternalData[MAX_SCRIPTS]; ScriptInputsOutputs scriptInputsOutputs[MAX_SCRIPTS]; -ScriptInternalData standaloneScript; + uint16_t maxLuaInterval = 0; uint16_t maxLuaDuration = 0; uint8_t instructionsPercent = 0; +tmr10ms_t luaCycleStart; char lua_warning_info[LUA_WARNING_INFO_LEN+1]; struct our_longjmp * global_lj = 0; #if defined(COLORLCD) @@ -95,7 +100,7 @@ void *tracer_alloc(void * ud, void * ptr, size_t osize, size_t nsize) /* custom panic handler */ int custom_lua_atpanic(lua_State * L) { - TRACE("PANIC: unprotected error in call to Lua API (%s)", lua_tostring(L, -1)); + TRACE_ERROR("PANIC: unprotected error in call to Lua API (%s)\n", lua_tostring(L, -1)); if (global_lj) { longjmp(global_lj->b, 1); /* will never return */ @@ -103,32 +108,13 @@ int custom_lua_atpanic(lua_State * L) return 0; } -void luaHook(lua_State * L, lua_Debug *ar) +static void luaHook(lua_State * L, lua_Debug *ar) { if (ar->event == LUA_HOOKCOUNT) { - instructionsPercent++; -#if defined(DEBUG) - // Disable Lua script instructions limit in DEBUG mode, - // just report max value reached - static uint16_t max = 0; - if (instructionsPercent > 100) { - if (max + 10 < instructionsPercent) { - max = instructionsPercent; - TRACE("LUA instructionsPercent %u%%", (uint32_t)max); + if (get_tmr10ms() - luaCycleStart >= LUA_TASK_PERIOD_TICKS) { + lua_yield(lsScripts, 0); } } - else if (instructionsPercent < 10) { - max = 0; - } -#else - if (instructionsPercent > 100) { - // From now on, as soon as a line is executed, error - // keep erroring until you're script reaches the top - lua_sethook(L, luaHook, LUA_MASKLINE, 0); - luaL_error(L, "CPU limit"); - } -#endif - } #if defined(LUA_ALLOCATOR_TRACER) else if (ar->event == LUA_HOOKLINE) { lua_getinfo(L, "nSl", ar); @@ -144,6 +130,12 @@ void luaHook(lua_State * L, lua_Debug *ar) #endif // #if defined(LUA_ALLOCATOR_TRACER) } +void luaEmptyEventBuffer() +{ + events[0] = 0; + events[1] = 0; +} + void luaSetInstructionsLimit(lua_State * L, int count) { instructionsPercent = 0; @@ -154,31 +146,34 @@ void luaSetInstructionsLimit(lua_State * L, int count) #endif } -int luaGetInputs(lua_State * L, ScriptInputsOutputs & sid) +int luaGetInputs(lua_State * L2, ScriptInputsOutputs & sid) { - if (!lua_istable(L, -1)) + if (!lua_istable(L2, -1)) return -1; memclear(sid.inputs, sizeof(sid.inputs)); sid.inputsCount = 0; - for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) { - luaL_checktype(L, -2, LUA_TNUMBER); // key is number - luaL_checktype(L, -1, LUA_TTABLE); // value is table + for (lua_pushnil(L2); lua_next(L2, -2); lua_pop(L2, 1)) { + luaL_checktype(L2, -2, LUA_TNUMBER); // key is number + luaL_checktype(L2, -1, LUA_TTABLE); // value is table if (sid.inputsCountname = lua_tostring(L, -1); + luaL_checktype(L2, -2, LUA_TNUMBER); // key is number + luaL_checktype(L2, -1, LUA_TSTRING); // value is string + lua_xmove(lsScripts, L, 1); // To preserve the string value, move it to the main stack + lua_pushnil(lsScripts); // Keep the stack balanced + lua_insert(L, -2); // Keep the coroutine at the top of the main stack + si->name = lua_tostring(L, -2); break; case 1: - luaL_checktype(L, -2, LUA_TNUMBER); // key is number - luaL_checktype(L, -1, LUA_TNUMBER); // value is number - type = lua_tointeger(L, -1); + luaL_checktype(L2, -2, LUA_TNUMBER); // key is number + luaL_checktype(L2, -1, LUA_TNUMBER); // value is number + type = lua_tointeger(L2, -1); if (type >= INPUT_TYPE_FIRST && type <= INPUT_TYPE_LAST) { si->type = type; } @@ -191,24 +186,24 @@ int luaGetInputs(lua_State * L, ScriptInputsOutputs & sid) } break; case 2: - luaL_checktype(L, -2, LUA_TNUMBER); // key is number - luaL_checktype(L, -1, LUA_TNUMBER); // value is number + luaL_checktype(L2, -2, LUA_TNUMBER); // key is number + luaL_checktype(L2, -1, LUA_TNUMBER); // value is number if (si->type == INPUT_TYPE_VALUE) { - si->min = lua_tointeger(L, -1); + si->min = lua_tointeger(L2, -1); } break; case 3: - luaL_checktype(L, -2, LUA_TNUMBER); // key is number - luaL_checktype(L, -1, LUA_TNUMBER); // value is number + luaL_checktype(L2, -2, LUA_TNUMBER); // key is number + luaL_checktype(L2, -1, LUA_TNUMBER); // value is number if (si->type == INPUT_TYPE_VALUE) { - si->max = lua_tointeger(L, -1); + si->max = lua_tointeger(L2, -1); } break; case 4: - luaL_checktype(L, -2, LUA_TNUMBER); // key is number - luaL_checktype(L, -1, LUA_TNUMBER); // value is number + luaL_checktype(L2, -2, LUA_TNUMBER); // key is number + luaL_checktype(L2, -1, LUA_TNUMBER); // value is number if (si->type == INPUT_TYPE_VALUE) { - si->def = lua_tointeger(L, -1); + si->def = lua_tointeger(L2, -1); } break; } @@ -220,18 +215,21 @@ int luaGetInputs(lua_State * L, ScriptInputsOutputs & sid) return 0; } -int luaGetOutputs(lua_State * L, ScriptInputsOutputs & sid) +int luaGetOutputs(lua_State * L2, ScriptInputsOutputs & sid) { - if (!lua_istable(L, -1)) + if (!lua_istable(L2, -1)) return -1; sid.outputsCount = 0; - for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) { - luaL_checktype(L, -2, LUA_TNUMBER); // key is number - luaL_checktype(L, -1, LUA_TSTRING); // value is string - if (sid.outputsCountfdate/ftime @@ -380,14 +376,10 @@ static void luaDumpState(lua_State * L, const char * filename, const FILINFO * f /** @fn luaLoadScriptFileToState(lua_State * L, const char * filename, const char * mode) - Load a Lua script file into a given lua_State (stack). May use OpenTx's optional pre-compilation feature to save memory and time during load. - @param L (lua_State) the Lua stack to load into. - @param filename (string) full path and file name of script. - @param mode (string) controls whether the file can be text or binary (that is, a pre-compiled file). Possible values are: "b" only binary. @@ -400,7 +392,6 @@ static void luaDumpState(lua_State * L, const char * filename, const FILINFO * f Eg: "tc" or "btc" (forces "t", overrides "x"). Add "d" to keep extra debug info in the compiled binary. Eg: "td", "btd", or "tcd" (no effect with just "b" or with "x"). - @retval (int) SCRIPT_OK on success (LUA_OK) SCRIPT_NOFILE if file wasn't found for specified mode or Lua could not open file (LUA_ERRFILE) @@ -552,196 +543,141 @@ int luaLoadScriptFileToState(lua_State * L, const char * filename, const char * return ret; } -static int luaLoad(lua_State * L, const char * filename, ScriptInternalData & sid, ScriptInputsOutputs * sio=nullptr) +#if defined(SIMU) || defined(DEBUG) +// Get the name of a script for error reporting etc. +static const char * getScriptName(uint8_t idx) { - int init = 0; - int lstatus = 0; + int ref = scriptInternalData[idx].reference; - sid.instructions = 0; - sid.state = SCRIPT_OK; - - if (luaState == INTERPRETER_PANIC) { - return SCRIPT_PANIC; + if (ref <= SCRIPT_MIX_LAST) { + return g_model.scriptsData[ref - SCRIPT_MIX_FIRST].file; } - - luaSetInstructionsLimit(L, MANUAL_SCRIPTS_MAX_INSTRUCTIONS); - - PROTECT_LUA() { - sid.state = luaLoadScriptFileToState(L, filename, LUA_SCRIPT_LOAD_MODE); - if (sid.state == SCRIPT_OK && (lstatus = lua_pcall(L, 0, 1, 0)) == LUA_OK && lua_istable(L, -1)) { - for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) { - const char * key = lua_tostring(L, -2); - if (!strcmp(key, "init")) { - init = luaL_ref(L, LUA_REGISTRYINDEX); - lua_pushnil(L); - } - else if (!strcmp(key, "run")) { - sid.run = luaL_ref(L, LUA_REGISTRYINDEX); - lua_pushnil(L); - } - else if (!strcmp(key, "background")) { - sid.background = luaL_ref(L, LUA_REGISTRYINDEX); - lua_pushnil(L); - } - else if (sio && !strcmp(key, "input")) { - luaGetInputs(L, *sio); - } - else if (sio && !strcmp(key, "output")) { - luaGetOutputs(L, *sio); - } - } - - if (init) { - lua_rawgeti(L, LUA_REGISTRYINDEX, init); - if (lua_pcall(L, 0, 0, 0) != 0) { - TRACE_ERROR("luaLoad(%s): Error in script init(): %s\n", filename, lua_tostring(L, -1)); - sid.state = SCRIPT_SYNTAX_ERROR; - } - luaL_unref(L, LUA_REGISTRYINDEX, init); - lua_gc(L, LUA_GCCOLLECT, 0); - } - } - else if (sid.state == SCRIPT_OK) { - TRACE_ERROR("luaLoad(%s): Error parsing script (%d): %s\n", filename, lstatus, lua_tostring(L, -1)); - sid.state = SCRIPT_SYNTAX_ERROR; - } + else if (ref <= SCRIPT_FUNC_LAST) { + return g_model.customFn[ref - SCRIPT_FUNC_FIRST].play.name; } + else if (ref <= SCRIPT_GFUNC_LAST) { + return g_eeGeneral.customFn[ref - SCRIPT_GFUNC_FIRST].play.name; + } +#if defined(PCBTARANIS) + else if (ref <= SCRIPT_TELEMETRY_LAST) { + return g_model.screens[ref - SCRIPT_TELEMETRY_FIRST].script.file; + } +#endif else { - luaDisable(); - return SCRIPT_PANIC; + return "standalone"; } - UNPROTECT_LUA(); +} +#endif + +static bool luaLoad(const char * filename, ScriptInternalData & sid) +{ + sid.state = luaLoadScriptFileToState(lsScripts, filename, LUA_SCRIPT_LOAD_MODE); if (sid.state != SCRIPT_OK) { - luaFree(L, sid); + luaFree(lsScripts, sid); + return true; } - - luaDoGc(L, true); - - return sid.state; + return false; } -bool luaLoadMixScript(uint8_t index) +static bool luaLoadMixScript(uint8_t ref) { - ScriptData & sd = g_model.scriptsData[index]; + uint8_t idx = ref - SCRIPT_MIX_FIRST; + ScriptData & sd = g_model.scriptsData[idx]; if (ZEXIST(sd.file)) { ScriptInternalData & sid = scriptInternalData[luaScriptsCount++]; - ScriptInputsOutputs * sio = &scriptInputsOutputs[index]; - sid.reference = SCRIPT_MIX_FIRST+index; - sid.state = SCRIPT_NOFILE; + sid.reference = ref; + char filename[sizeof(SCRIPTS_MIXES_PATH) + LEN_SCRIPT_FILENAME + sizeof(SCRIPT_EXT)] = SCRIPTS_MIXES_PATH "/"; strncpy(filename + sizeof(SCRIPTS_MIXES_PATH), sd.file, LEN_SCRIPT_FILENAME); filename[sizeof(SCRIPTS_MIXES_PATH) + LEN_SCRIPT_FILENAME] = '\0'; strcat(filename + sizeof(SCRIPTS_MIXES_PATH), SCRIPT_EXT); - if (luaLoad(lsScripts, filename, sid, sio) == SCRIPT_PANIC) { - return false; - } + + return luaLoad(filename, sid); } - return true; + return false; } -bool luaLoadFunctionScript(uint8_t index, uint8_t ref) +static bool luaLoadFunctionScript(uint8_t ref) { + uint8_t idx; CustomFunctionData * fn; - if (ref < SCRIPT_GFUNC_FIRST) - fn = &g_model.customFn[index]; - else if (!g_model.noGlobalFunctions) - fn = &g_eeGeneral.customFn[index]; + if (ref <= SCRIPT_FUNC_LAST) { + idx = ref - SCRIPT_FUNC_FIRST; + fn = &g_model.customFn[idx]; + } + else if (!g_model.noGlobalFunctions) { + idx = ref - SCRIPT_GFUNC_FIRST; + fn = &g_eeGeneral.customFn[idx]; + } else - return true; + return false; - if (fn->func == FUNC_PLAY_SCRIPT && ZEXIST(fn->play.name)) { + if (fn -> func == FUNC_PLAY_SCRIPT && ZEXIST(fn -> play.name)) { if (luaScriptsCount < MAX_SCRIPTS) { ScriptInternalData & sid = scriptInternalData[luaScriptsCount++]; - sid.reference = ref + index; - sid.state = SCRIPT_NOFILE; + sid.reference = ref; + char filename[sizeof(SCRIPTS_FUNCS_PATH) + LEN_FUNCTION_NAME + sizeof(SCRIPT_EXT)] = SCRIPTS_FUNCS_PATH "/"; strncpy(filename + sizeof(SCRIPTS_FUNCS_PATH), fn->play.name, LEN_FUNCTION_NAME); filename[sizeof(SCRIPTS_FUNCS_PATH) + LEN_FUNCTION_NAME] = '\0'; strcat(filename + sizeof(SCRIPTS_FUNCS_PATH), SCRIPT_EXT); - if (luaLoad(lsScripts, filename, sid) == SCRIPT_PANIC) { - return false; - } + + return luaLoad(filename, sid); } else { POPUP_WARNING(STR_TOO_MANY_LUA_SCRIPTS); - return false; + return true; } } - return true; + return false; } #if defined(PCBTARANIS) -bool luaLoadTelemetryScript(uint8_t index) +static bool luaLoadTelemetryScript(uint8_t ref) { - TelemetryScreenType screenType = TELEMETRY_SCREEN_TYPE(index); + uint8_t idx = ref - SCRIPT_TELEMETRY_FIRST; + TelemetryScreenType screenType = TELEMETRY_SCREEN_TYPE(idx); if (screenType == TELEMETRY_SCREEN_TYPE_SCRIPT) { - TelemetryScriptData & script = g_model.screens[index].script; + TelemetryScriptData & script = g_model.screens[idx].script; + if (ZEXIST(script.file)) { if (luaScriptsCount < MAX_SCRIPTS) { ScriptInternalData & sid = scriptInternalData[luaScriptsCount++]; - sid.reference = SCRIPT_TELEMETRY_FIRST+index; - sid.state = SCRIPT_NOFILE; + sid.reference = ref; + char filename[sizeof(SCRIPTS_TELEM_PATH) + LEN_SCRIPT_FILENAME + sizeof(SCRIPT_EXT)] = SCRIPTS_TELEM_PATH "/"; strncpy(filename + sizeof(SCRIPTS_TELEM_PATH), script.file, LEN_SCRIPT_FILENAME); filename[sizeof(SCRIPTS_TELEM_PATH) + LEN_SCRIPT_FILENAME] = '\0'; strcat(filename + sizeof(SCRIPTS_TELEM_PATH), SCRIPT_EXT); - if (luaLoad(lsScripts, filename, sid) == SCRIPT_PANIC) { - return false; - } + + return luaLoad(filename, sid); } else { POPUP_WARNING(STR_TOO_MANY_LUA_SCRIPTS); - return false; + return true; } } } - return true; + return false; } #endif -uint8_t isTelemetryScriptAvailable(uint8_t index) +uint8_t isTelemetryScriptAvailable(uint8_t idx) { - for (int i=0; i 100) { - TRACE("Script killed"); - standaloneScript.state = SCRIPT_KILLED; - luaState = INTERPRETER_RELOAD_PERMANENT_SCRIPTS; + // Run in the right interactive mode + if (allowLcdUsage != luaLcdAllowed) { +#if defined(PCBTARANIS) + if (luaLcdAllowed && scriptInternalData[0].reference != SCRIPT_STANDALONE && menuHandlers[menuLevel] != menuViewTelemetry) { + // Telemetry screen was exited while foreground function was preempted - finish in the background + luaLcdAllowed = false; + } + else +#endif + return scriptWasRun; + } + + do { + ScriptInternalData & sid = scriptInternalData[idx]; + if (sid.state != SCRIPT_OK) continue; + + uint8_t ref = sid.reference; + int inputsCount = 0; + int luaStatus = lua_status(lsScripts); + + if (luaStatus == LUA_OK) { + // Not preempted - setup another function call + lua_settop(lsScripts, 0); + + if (allowLcdUsage) { +#if defined(PCBTARANIS) + if ((menuHandlers[menuLevel] == menuViewTelemetry && ref == SCRIPT_TELEMETRY_FIRST + s_frsky_view) || ref == SCRIPT_STANDALONE) { +#else + if (ref == SCRIPT_STANDALONE) { +#endif + // Pull a new event from the buffer + evt = events[0]; + events[0] = events[1]; + events[1] = 0; + + lua_rawgeti(lsScripts, LUA_REGISTRYINDEX, sid.run); + lua_pushunsigned(lsScripts, evt); + inputsCount = 1; + } + else continue; + } + else { + if (ref <= SCRIPT_MIX_LAST) { + lua_rawgeti(lsScripts, LUA_REGISTRYINDEX, sid.run); + + ScriptData & sd = g_model.scriptsData[ref - SCRIPT_MIX_FIRST]; + ScriptInputsOutputs * sio = & scriptInputsOutputs[ref - SCRIPT_MIX_FIRST]; + inputsCount = sio -> inputsCount; + + for (int j = 0; j < inputsCount; j++) { + if (sio -> inputs[j].type == INPUT_TYPE_SOURCE) + luaGetValueAndPush(lsScripts, sd.inputs[j].source); + else + lua_pushinteger(lsScripts, sd.inputs[j].value + sio -> inputs[j].def); + } + } + else if (ref <= SCRIPT_GFUNC_LAST) { + CustomFunctionData * fn; + + if (ref <= SCRIPT_FUNC_LAST) + fn = &g_model.customFn[ref - SCRIPT_FUNC_FIRST]; + else + fn = &g_eeGeneral.customFn[ref - SCRIPT_GFUNC_FIRST]; + + if (getSwitch(fn -> swtch)) { + lua_rawgeti(lsScripts, LUA_REGISTRYINDEX, sid.run); + } + else { + if (sid.background == LUA_NOREF) continue; + lua_rawgeti(lsScripts, LUA_REGISTRYINDEX, sid.background); + } + } +#if defined(PCBTARANIS) + else if (ref <= SCRIPT_TELEMETRY_LAST) { + if (sid.background == LUA_NOREF) continue; + lua_rawgeti(lsScripts, LUA_REGISTRYINDEX, sid.background); + } +#endif + else continue; + } + } + + // Full garbage collection at the start of every cycle + luaDoGc(lsScripts, fullGC); + fullGC = false; + + // Resume running the coroutine + luaStatus = lua_resume(lsScripts, 0, inputsCount); + + if (luaStatus == LUA_YIELD) { + // Coroutine yielded - wait for the next cycle + return scriptWasRun; + } + else if (luaStatus == LUA_OK) { + // Coroutine returned + scriptWasRun = true; + + if (ref <= SCRIPT_MIX_LAST) { + ScriptInputsOutputs * sio = & scriptInputsOutputs[ref - SCRIPT_MIX_FIRST]; + lua_settop(lsScripts, sio -> outputsCount); + + for (int j = sio -> outputsCount - 1; j >= 0; j--) { + if (!lua_isnumber(lsScripts, -1)) { + TRACE_ERROR("Script %s: run function did not return a number\n", getScriptName(idx)); + sid.state = SCRIPT_SYNTAX_ERROR; + break; + } + sio -> outputs[j].value = lua_tointeger(lsScripts, -1); + lua_pop(lsScripts, 1); + } + } + else if (ref == SCRIPT_STANDALONE) { + lua_settop(lsScripts, 1); + if (lua_isnumber(lsScripts, -1)) { + int scriptResult = lua_tointeger(lsScripts, -1); + lua_pop(lsScripts, 1); /* pop returned value */ + + if (scriptResult != 0) { + TRACE("Script finished with status %d", scriptResult); + sid.state = SCRIPT_FINISHED; + } + else if (luaDisplayStatistics) { + #if defined(COLORLCD) + #else + lcdDrawSolidHorizontalLine(0, 7*FH-1, lcdLastRightPos+6, ERASE); + lcdDrawText(0, 7*FH, "GV Use: "); + lcdDrawNumber(lcdLastRightPos, 7*FH, luaGetMemUsed(lsScripts), LEFT); + lcdDrawChar(lcdLastRightPos, 7*FH, 'b'); + lcdDrawSolidHorizontalLine(0, 7*FH-2, lcdLastRightPos+6, FORCE); + lcdDrawVerticalLine(lcdLastRightPos+6, 7*FH-2, FH+2, SOLID, FORCE); + #endif + } } else if (lua_isstring(lsScripts, -1)) { char nextScript[FF_MAX_LFN+1]; strncpy(nextScript, lua_tostring(lsScripts, -1), FF_MAX_LFN); nextScript[FF_MAX_LFN] = '\0'; luaExec(nextScript); + return scriptWasRun; } else { - TRACE("Script run function returned unexpected value"); - standaloneScript.state = SCRIPT_SYNTAX_ERROR; - luaState = INTERPRETER_RELOAD_PERMANENT_SCRIPTS; + TRACE_ERROR("Script run function returned unexpected value\n"); + sid.state = SCRIPT_SYNTAX_ERROR; } - } - else { - int scriptResult = lua_tointeger(lsScripts, -1); - lua_pop(lsScripts, 1); /* pop returned value */ - if (scriptResult != 0) { - TRACE("Script finished with status %d", scriptResult); - standaloneScript.state = SCRIPT_NOFILE; - luaState = INTERPRETER_RELOAD_PERMANENT_SCRIPTS; - return; - } - else if (luaDisplayStatistics) { -#if !defined(COLORLCD) - lcdDrawSolidHorizontalLine(0, 7*FH-1, lcdLastRightPos+6, ERASE); - lcdDrawText(0, 7*FH, "GV Use: "); - lcdDrawNumber(lcdLastRightPos, 7*FH, luaGetMemUsed(lsScripts), LEFT); - lcdDrawChar(lcdLastRightPos, 7*FH, 'b'); - lcdDrawSolidHorizontalLine(0, 7*FH-2, lcdLastRightPos+6, FORCE); - lcdDrawVerticalLine(lcdLastRightPos+6, 7*FH-2, FH+2, SOLID, FORCE); -#endif - } - } - } - else { - TRACE("Script error: %s", lua_tostring(lsScripts, -1)); - standaloneScript.state = (instructionsPercent > 100 ? SCRIPT_KILLED : SCRIPT_SYNTAX_ERROR); - luaState = INTERPRETER_RELOAD_PERMANENT_SCRIPTS; - } - if (standaloneScript.state != SCRIPT_OK) { - luaError(lsScripts, standaloneScript.state); - luaState = INTERPRETER_RELOAD_PERMANENT_SCRIPTS; - } - - if (evt == EVT_KEY_LONG(KEY_EXIT)) { - TRACE("Script force exit"); - killEvents(evt); - standaloneScript.state = SCRIPT_NOFILE; - luaState = INTERPRETER_RELOAD_PERMANENT_SCRIPTS; - } + if (evt == EVT_KEY_LONG(KEY_EXIT)) { + TRACE("Script force exit"); + killEvents(evt); + luaEmptyEventBuffer(); + sid.state = SCRIPT_FINISHED; + } #if defined(KEYS_GPIO_REG_MENU) - // TODO find another key and add a #define - else if (evt == EVT_KEY_LONG(KEY_MENU)) { - killEvents(evt); - luaDisplayStatistics = !luaDisplayStatistics; - } -#endif - } - else { - TRACE("Script run method missing"); - standaloneScript.state = SCRIPT_SYNTAX_ERROR; - luaState = INTERPRETER_RELOAD_PERMANENT_SCRIPTS; - } -} - -bool luaDoOneRunPermanentScript(event_t evt, int i, uint32_t scriptType) -{ - ScriptInternalData & sid = scriptInternalData[i]; - if (sid.state != SCRIPT_OK) return false; - - luaSetInstructionsLimit(lsScripts, PERMANENT_SCRIPTS_MAX_INSTRUCTIONS); - int inputsCount = 0; -#if defined(SIMU) || defined(DEBUG) - const char *filename; -#endif - ScriptInputsOutputs * sio = nullptr; -#if SCRIPT_MIX_FIRST > 0 - if ((scriptType & RUN_MIX_SCRIPT) && (sid.reference >= SCRIPT_MIX_FIRST && sid.reference <= SCRIPT_MIX_LAST)) { -#else - if ((scriptType & RUN_MIX_SCRIPT) && (sid.reference <= SCRIPT_MIX_LAST)) { -#endif - ScriptData & sd = g_model.scriptsData[sid.reference-SCRIPT_MIX_FIRST]; - sio = &scriptInputsOutputs[sid.reference-SCRIPT_MIX_FIRST]; - inputsCount = sio->inputsCount; -#if defined(SIMU) || defined(DEBUG) - filename = sd.file; -#endif - lua_rawgeti(lsScripts, LUA_REGISTRYINDEX, sid.run); - for (int j=0; jinputsCount; j++) { - if (sio->inputs[j].type == INPUT_TYPE_SOURCE) - luaGetValueAndPush(lsScripts, sd.inputs[j].source); - else - lua_pushinteger(lsScripts, sd.inputs[j].value + sio->inputs[j].def); - } - } - else if ((scriptType & RUN_FUNC_SCRIPT) && (sid.reference >= SCRIPT_FUNC_FIRST && sid.reference <= SCRIPT_GFUNC_LAST)) { - CustomFunctionData & fn = (sid.reference < SCRIPT_GFUNC_FIRST ? g_model.customFn[sid.reference-SCRIPT_FUNC_FIRST] : g_eeGeneral.customFn[sid.reference-SCRIPT_GFUNC_FIRST]); -#if defined(SIMU) || defined(DEBUG) - filename = fn.play.name; -#endif - if (getSwitch(fn.swtch)) - lua_rawgeti(lsScripts, LUA_REGISTRYINDEX, sid.run); - else if (sid.background) - lua_rawgeti(lsScripts, LUA_REGISTRYINDEX, sid.background); - else - return false; - } - else { -#if defined(PCBTARANIS) -#if defined(SIMU) || defined(DEBUG) - TelemetryScriptData & script = g_model.screens[sid.reference-SCRIPT_TELEMETRY_FIRST].script; - filename = script.file; -#endif - if ((scriptType & RUN_TELEM_FG_SCRIPT) && (menuHandlers[0]==menuViewTelemetry && sid.reference==SCRIPT_TELEMETRY_FIRST+s_frsky_view)) { - lua_rawgeti(lsScripts, LUA_REGISTRYINDEX, sid.run); - lua_pushunsigned(lsScripts, evt); - inputsCount = 1; - } - else if ((scriptType & RUN_TELEM_BG_SCRIPT) && (sid.background)) { - lua_rawgeti(lsScripts, LUA_REGISTRYINDEX, sid.background); - } - else { - return false; - } -#else // !PCBTARANIS - // COLORLCD - return false; -#endif - } - - if (lua_pcall(lsScripts, inputsCount, sio ? sio->outputsCount : 0, 0) == 0) { - if (sio) { - for (int j=sio->outputsCount-1; j>=0; j--) { - if (!lua_isnumber(lsScripts, -1)) { - sid.state = (instructionsPercent > 100 ? SCRIPT_KILLED : SCRIPT_SYNTAX_ERROR); - TRACE("Script %8s disabled", filename); - break; + // TODO find another key and add a #define + else if (evt == EVT_KEY_LONG(KEY_MENU)) { + killEvents(evt); + luaEmptyEventBuffer(); + luaDisplayStatistics = !luaDisplayStatistics; } - sio->outputs[j].value = lua_tointeger(lsScripts, -1); - lua_pop(lsScripts, 1); +#endif } } - } - else { - if (instructionsPercent > 100) { - TRACE("Script %8s killed", filename); - sid.state = SCRIPT_KILLED; - } else { - TRACE("Script %8s error: %s", filename, lua_tostring(lsScripts, -1)); + // Error sid.state = SCRIPT_SYNTAX_ERROR; + TRACE_ERROR("%s: %s\n", getScriptName(idx), lua_tostring(lsScripts, -1)); + + if (sid.reference == SCRIPT_STANDALONE) + luaError(lsScripts, sid.state); + else { + // Replace the dead coroutine with a new one + lua_pop(L, 1); // Pop the dead coroutine off the main stack + lsScripts = lua_newthread(L); // Push the new coroutine + } } - } - if (sid.state != SCRIPT_OK) { - luaFree(lsScripts, sid); - } - else { - if (instructionsPercent > sid.instructions) { - sid.instructions = instructionsPercent; + if (sid.state != SCRIPT_OK) { + if (sid.reference == SCRIPT_STANDALONE) { + luaState = INTERPRETER_RELOAD_PERMANENT_SCRIPTS; + return scriptWasRun; + } + else luaFree(lsScripts, sid); } - } - return true; -} + } while (++idx < luaScriptsCount); + + // Toggle between background and foreground scripts + luaLcdAllowed = !luaLcdAllowed; + idx = 0; + + return scriptWasRun; +} //resumeLua(...) -// This loop-pass should run in checkEvents(). -// However, libopenui has 2 main separated routines: -// - checkEvents(): runs the event loop and can effect an async redraw (if visible) -// - paint(): (if visible) the redraw itself -// -bool luaTask(event_t evt, uint8_t scriptType, bool allowLcdUsage) +bool luaTask(event_t evt, bool allowLcdUsage) { - if (luaState == INTERPRETER_PANIC) return false; - luaLcdAllowed = allowLcdUsage; + bool init = false; bool scriptWasRun = false; - // TRACE("luaTask(0x%x,0x%x,%s)", - // evt, scriptType, - // allowLcdUsage ? "true" : "false"); - - // we run either standalone script or permanent scripts - if (luaState & INTERPRETER_RUNNING_STANDALONE_SCRIPT) { - // run standalone script - if ((scriptType & RUN_STNDAL_SCRIPT) == 0) return false; - PROTECT_LUA() { - luaDoOneRunStandalone(evt); - scriptWasRun = true; - } - else { - luaDisable(); - return false; - } - UNPROTECT_LUA(); + // Add event to buffer + if (evt != 0) { + if (events[0] == 0) events[0] = evt; + else if (events[1] == 0) events[1] = evt; } - else { - // run permanent scripts - if (luaState & INTERPRETER_RELOAD_PERMANENT_SCRIPTS) { - luaState = 0; - luaInit(); - if (luaState == INTERPRETER_PANIC) return false; - luaLoadPermanentScripts(); - if (luaState == INTERPRETER_PANIC) return false; - } - for (int i=0; i LUA_MEM_MAX) { - TRACE("checkLuaMemoryUsage(): max limit reached (%u), killing Lua", totalMemUsed); + TRACE_ERROR("checkLuaMemoryUsage(): max limit reached (%u), killing Lua\n", totalMemUsed); // disable Lua scripts luaClose(&lsScripts); luaDisable(); @@ -1127,6 +1182,7 @@ void checkLuaMemoryUsage() #endif } + uint32_t luaGetMemUsed(lua_State * L) { return L ? (lua_gc(L, LUA_GCCOUNT, 0) << 10) + lua_gc(L, LUA_GCCOUNTB, 0) : 0; @@ -1137,25 +1193,36 @@ void luaInit() TRACE("luaInit"); luaClose(&lsScripts); + L = nullptr; if (luaState != INTERPRETER_PANIC) { #if defined(USE_BIN_ALLOCATOR) - lsScripts = lua_newstate(bin_l_alloc, nullptr); //we use our own allocator! + L = lua_newstate(bin_l_alloc, nullptr); //we use our own allocator! #elif defined(LUA_ALLOCATOR_TRACER) - memset(&lsScriptsTrace, 0 , sizeof(lsScriptsTrace)); + memset(&lsScriptsTrace, 0 , sizeof(lsScriptsTrace); lsScriptsTrace.script = "lua_newstate(scripts)"; - lsScripts = lua_newstate(tracer_alloc, &lsScriptsTrace); //we use tracer allocator + L = lua_newstate(tracer_alloc, &lsScriptsTrace); //we use tracer allocator #else - lsScripts = lua_newstate(l_alloc, nullptr); //we use Lua default allocator + L = lua_newstate(l_alloc, nullptr); //we use Lua default allocator #endif - if (lsScripts) { + if (L) { // install our panic handler - lua_atpanic(lsScripts, &custom_lua_atpanic); + lua_atpanic(L, &custom_lua_atpanic); #if defined(LUA_ALLOCATOR_TRACER) - lua_sethook(lsScripts, luaHook, LUA_MASKLINE, 0); + lua_sethook(L, luaHook, LUA_MASKCOUNT|LUA_MASKLINE, PERMANENT_SCRIPTS_MAX_INSTRUCTIONS); +#else + lua_sethook(L, luaHook, LUA_MASKCOUNT, PERMANENT_SCRIPTS_MAX_INSTRUCTIONS); #endif + // lsScripts is now a coroutine in lieu of the main thread to support preemption + lsScripts = lua_newthread(L); + + // Clear loaded scripts + memset(scriptInternalData, 0, sizeof(scriptInternalData)); + memset(scriptInputsOutputs, 0, sizeof(scriptInputsOutputs)); + luaScriptsCount = 0; + // protect libs and constants registration PROTECT_LUA() { luaRegisterLibraries(lsScripts); diff --git a/radio/src/lua/lua_api.h b/radio/src/lua/lua_api.h index 36f658d1a..30626eaba 100644 --- a/radio/src/lua/lua_api.h +++ b/radio/src/lua/lua_api.h @@ -47,15 +47,19 @@ extern Fifo * luaRxFifo; #endif extern lua_State * lsScripts; -extern lua_State * lsWidgets; + extern bool luaLcdAllowed; + #if defined(COLORLCD) +extern lua_State * lsWidgets; extern uint32_t luaExtraMemoryUsage; +void luaInitThemesAndWidgets(); #endif void luaInit(); -void luaInitThemesAndWidgets(); +void luaEmptyEventBuffer(); +#define LUA_INIT_THEMES_AND_WIDGETS() luaInitThemesAndWidgets() #define lua_registernumber(L, n, i) (lua_pushnumber(L, (i)), lua_setglobal(L, (n))) #define lua_registerint(L, n, i) (lua_pushinteger(L, (i)), lua_setglobal(L, (n))) #define lua_pushtablenil(L, k) (lua_pushstring(L, (k)), lua_pushnil(L), lua_settable(L, -3)) @@ -78,12 +82,6 @@ void luaInitThemesAndWidgets(); #define lua_registerlib(L, name, tab) (luaL_newmetatable(L, name), luaL_setfuncs(L, tab, 0), lua_setglobal(L, name)) -#define RUN_MIX_SCRIPT (1 << 0) -#define RUN_FUNC_SCRIPT (1 << 1) -#define RUN_TELEM_BG_SCRIPT (1 << 2) -#define RUN_TELEM_FG_SCRIPT (1 << 3) -#define RUN_STNDAL_SCRIPT (1 << 4) - enum luaScriptInputType { INPUT_TYPE_FIRST = 0, INPUT_TYPE_VALUE = INPUT_TYPE_FIRST, @@ -98,17 +96,20 @@ struct ScriptInput { int16_t max; int16_t def; }; + struct ScriptOutput { const char *name; int16_t value; }; + enum ScriptState { SCRIPT_OK, SCRIPT_NOFILE, SCRIPT_SYNTAX_ERROR, SCRIPT_PANIC, - SCRIPT_KILLED + SCRIPT_FINISHED }; + enum ScriptReference { SCRIPT_MIX_FIRST, SCRIPT_MIX_LAST=SCRIPT_MIX_FIRST+MAX_SCRIPTS-1, @@ -116,9 +117,13 @@ enum ScriptReference { SCRIPT_FUNC_LAST=SCRIPT_FUNC_FIRST+MAX_SPECIAL_FUNCTIONS-1, // model functions SCRIPT_GFUNC_FIRST, SCRIPT_GFUNC_LAST=SCRIPT_GFUNC_FIRST+MAX_SPECIAL_FUNCTIONS-1, // global functions +#if defined(PCBTARANIS) SCRIPT_TELEMETRY_FIRST, - SCRIPT_TELEMETRY_LAST=SCRIPT_TELEMETRY_FIRST+MAX_SCRIPTS, // telem0 and telem1 .. telem7 + SCRIPT_TELEMETRY_LAST=SCRIPT_TELEMETRY_FIRST+MAX_SCRIPTS, // telem0 and telem1 .. telem7 +#endif + SCRIPT_STANDALONE // Standalone script }; + struct ScriptInternalData { uint8_t reference; uint8_t state; @@ -126,41 +131,55 @@ struct ScriptInternalData { int background; uint8_t instructions; }; + struct ScriptInputsOutputs { uint8_t inputsCount; ScriptInput inputs[MAX_SCRIPT_INPUTS]; uint8_t outputsCount; ScriptOutput outputs[MAX_SCRIPT_OUTPUTS]; }; -#define INTERPRETER_RUNNING_STANDALONE_SCRIPT 1 -#define INTERPRETER_RELOAD_PERMANENT_SCRIPTS 2 -#define INTERPRETER_PANIC 255 + + +enum InterpreterState { + INTERPRETER_RELOAD_PERMANENT_SCRIPTS = 1, + INTERPRETER_LOADING, + INTERPRETER_START_RUNNING, + INTERPRETER_RUNNING, + INTERPRETER_PANIC = 255 +}; + extern uint8_t luaState; extern uint8_t luaScriptsCount; extern bool luaLcdAllowed; -extern ScriptInternalData standaloneScript; + extern ScriptInternalData scriptInternalData[MAX_SCRIPTS]; extern ScriptInputsOutputs scriptInputsOutputs[MAX_SCRIPTS]; + void luaClose(lua_State ** L); -bool luaTask(event_t evt, uint8_t scriptType, bool allowLcdUsage); +bool luaTask(event_t evt, bool allowLcdUsage); void checkLuaMemoryUsage(); void luaExec(const char * filename); void luaDoGc(lua_State * L, bool full); void luaError(lua_State * L, uint8_t error, bool acknowledge=true); uint32_t luaGetMemUsed(lua_State * L); void luaGetValueAndPush(lua_State * L, int src); -#define luaGetCpuUsed(idx) scriptInternalData[idx].instructions uint8_t isTelemetryScriptAvailable(uint8_t index); -#define LUA_LOAD_MODEL_SCRIPTS() luaState |= INTERPRETER_RELOAD_PERMANENT_SCRIPTS -#define LUA_LOAD_MODEL_SCRIPT(idx) luaState |= INTERPRETER_RELOAD_PERMANENT_SCRIPTS + +#define luaGetCpuUsed(idx) scriptInternalData[idx].instructions +#define LUA_LOAD_MODEL_SCRIPTS() luaState = INTERPRETER_RELOAD_PERMANENT_SCRIPTS +#define LUA_LOAD_MODEL_SCRIPT(idx) luaState = INTERPRETER_RELOAD_PERMANENT_SCRIPTS + // Lua PROTECT/UNPROTECT #include + struct our_longjmp { struct our_longjmp *previous; jmp_buf b; volatile int status; /* error code */ }; + extern struct our_longjmp * global_lj; + #define PROTECT_LUA() { struct our_longjmp lj; \ lj.previous = global_lj; /* chain new error handler */ \ global_lj = &lj; \ @@ -172,14 +191,16 @@ extern uint16_t maxLuaDuration; extern uint8_t instructionsPercent; #if defined(KEYS_GPIO_REG_PAGE) - #define IS_MASKABLE(key) ((key) != KEY_EXIT && (key) != KEY_ENTER && ((luaState & INTERPRETER_RUNNING_STANDALONE_SCRIPT) || (key) != KEY_PAGE)) + #define IS_MASKABLE(key) ((key) != KEY_EXIT && (key) != KEY_ENTER && ((scriptInternalData[0].reference == SCRIPT_STANDALONE) || (key) != KEY_PAGE)) #else #define IS_MASKABLE(key) ((key) != KEY_EXIT && (key) != KEY_ENTER) #endif + struct LuaField { uint16_t id; char desc[50]; }; + bool luaFindFieldByName(const char * name, LuaField & field, unsigned int flags=0); void luaLoadThemes(); void luaRegisterLibraries(lua_State * L); @@ -207,8 +228,6 @@ struct LuaMemTracer { }; void * tracer_alloc(void * ud, void * ptr, size_t osize, size_t nsize); -void luaHook(lua_State * L, lua_Debug *ar); - #else // defined(LUA) diff --git a/radio/src/lua/widgets.cpp b/radio/src/lua/widgets.cpp index a5d3dae8b..a45278620 100644 --- a/radio/src/lua/widgets.cpp +++ b/radio/src/lua/widgets.cpp @@ -40,6 +40,57 @@ extern int custom_lua_atpanic(lua_State *L); #define LUA_WIDGET_FILENAME "/main.lua" #define LUA_FULLPATH_MAXLEN (LEN_FILE_PATH_MAX + LEN_SCRIPT_FILENAME + LEN_FILE_EXTENSION_MAX) // max length (example: /SCRIPTS/THEMES/mytheme.lua) +static void luaHook(lua_State * L, lua_Debug *ar) +{ + if (ar->event == LUA_HOOKCOUNT) { + instructionsPercent++; +#if defined(DEBUG) + // Disable Lua script instructions limit in DEBUG mode, + // just report max value reached + static uint16_t max = 0; + if (instructionsPercent > 100) { + if (max + 10 < instructionsPercent) { + max = instructionsPercent; + TRACE("LUA instructionsPercent %u%%", (uint32_t)max); + } + } + else if (instructionsPercent < 10) { + max = 0; + } +#else + if (instructionsPercent > 100) { + // From now on, as soon as a line is executed, error + // keep erroring until you're script reaches the top + lua_sethook(L, luaHook, LUA_MASKLINE, 0); + luaL_error(L, "CPU limit"); + } +#endif + } +#if defined(LUA_ALLOCATOR_TRACER) + else if (ar->event == LUA_HOOKLINE) { + lua_getinfo(L, "nSl", ar); + LuaMemTracer * tracer = GET_TRACER(L); + if (tracer->alloc || tracer->free) { + TRACE("LT: [+%u,-%u] %s:%d", tracer->alloc, tracer->free, tracer->script, tracer->lineno); + } + tracer->script = ar->source; + tracer->lineno = ar->currentline; + tracer->alloc = 0; + tracer->free = 0; + } +#endif // #if defined(LUA_ALLOCATOR_TRACER) +} + +void luaSetInstructionsLimit(lua_State * L, int count) +{ + instructionsPercent = 0; +#if defined(LUA_ALLOCATOR_TRACER) + lua_sethook(L, luaHook, LUA_MASKCOUNT|LUA_MASKLINE, count); +#else + lua_sethook(L, luaHook, LUA_MASKCOUNT, count); +#endif +} + void exec(int function, int nresults=0) { if (lsWidgets == 0) return; @@ -344,12 +395,16 @@ void LuaWidget::refresh(BitmapBuffer* dc) // Enable drawing into the current LCD buffer luaLcdBuffer = dc; + + // This little hack is needed to not interfere with the LCD usage of preempted scripts + bool lla = luaLcdAllowed; luaLcdAllowed = true; + if (lua_pcall(lsWidgets, 1, 0, 0) != 0) { setErrorMessage("refresh()"); } // Remove LCD - luaLcdAllowed = false; + luaLcdAllowed = lla; luaLcdBuffer = nullptr; // mark as refreshed diff --git a/radio/src/main.cpp b/radio/src/main.cpp index 24dd94066..5c927db79 100644 --- a/radio/src/main.cpp +++ b/radio/src/main.cpp @@ -356,7 +356,7 @@ void guiMain(event_t evt) DEBUG_TIMER_START(debugTimerLua); // Run Lua scripts first that don't use LCD - luaTask( 0, RUN_MIX_SCRIPT | RUN_FUNC_SCRIPT, false); + luaTask( 0, false); // This is run from StandaloneLuaWindow::checkEvents() // luaTask(evt, RUN_STNDAL_SCRIPT, true); @@ -382,29 +382,28 @@ void guiMain(event_t evt) } #elif defined(GUI) -void handleGui(event_t event) { - // if Lua standalone, run it and don't clear the screen (Lua will do it) - // else if Lua telemetry view, run it and don't clear the screen - // else clear scren and show normal menus +bool handleGui(event_t event) { + bool refreshNeeded; #if defined(LUA) - if (luaTask(event, RUN_STNDAL_SCRIPT, true)) { - // standalone script is active + refreshNeeded = luaTask(event, true); + if (menuHandlers[menuLevel] == menuViewTelemetry && TELEMETRY_SCREEN_TYPE(s_frsky_view) == TELEMETRY_SCREEN_TYPE_SCRIPT) { + menuHandlers[menuLevel](event); } - else if (luaTask(event, RUN_TELEM_FG_SCRIPT, true)) { - // the telemetry screen is active - menuHandlers[menuLevel](event); - } - else + else if (scriptInternalData[0].reference != SCRIPT_STANDALONE) #endif +// No foreground Lua script is running - clear the screen show normal menu { lcdClear(); menuHandlers[menuLevel](event); drawStatusLine(); + refreshNeeded = true; } + return refreshNeeded; } void guiMain(event_t evt) { + bool refreshNeeded = menuEvent || warningText || (popupMenuItemsCount > 0); #if defined(LUA) // TODO better lua stopwatch uint32_t t0 = get_tmr10ms(); @@ -416,7 +415,7 @@ void guiMain(event_t evt) } // run Lua scripts that don't use LCD (to use CPU time while LCD DMA is running) - luaTask(0, RUN_MIX_SCRIPT | RUN_FUNC_SCRIPT | RUN_TELEM_BG_SCRIPT, false); + luaTask(0, false); t0 = get_tmr10ms() - t0; if (t0 > maxLuaDuration) { @@ -440,10 +439,10 @@ void guiMain(event_t evt) } if (isEventCaughtByPopup()) { - handleGui(0); + refreshNeeded |= handleGui(0); } else { - handleGui(evt); + refreshNeeded |= handleGui(evt); evt = 0; } @@ -463,8 +462,8 @@ void guiMain(event_t evt) } } - lcdRefresh(); - + if (refreshNeeded) lcdRefresh(); + if (mainRequestFlags & (1u << REQUEST_SCREENSHOT)) { writeScreenshot(); mainRequestFlags &= ~(1u << REQUEST_SCREENSHOT); From c76532f33af759149a09e17a1c7acb590e046453 Mon Sep 17 00:00:00 2001 From: jfrickmann Date: Tue, 22 Jun 2021 16:44:10 -0400 Subject: [PATCH 2/6] Fixed Lua error messages in standalone and telemetry scripts. TODO: If a standalone script errors in init() on COLORLCD it just quits without showing error. Is that because it calls luaError from luaExec() in radio_tools.cpp instead of from luaTask() in standalone_lua.cpp? --- radio/src/lua/interface.cpp | 76 ++++++++++++++++++++++++++----------- 1 file changed, 53 insertions(+), 23 deletions(-) diff --git a/radio/src/lua/interface.cpp b/radio/src/lua/interface.cpp index d7b56f140..359135409 100644 --- a/radio/src/lua/interface.cpp +++ b/radio/src/lua/interface.cpp @@ -115,6 +115,7 @@ static void luaHook(lua_State * L, lua_Debug *ar) lua_yield(lsScripts, 0); } } + #if defined(LUA_ALLOCATOR_TRACER) else if (ar->event == LUA_HOOKLINE) { lua_getinfo(L, "nSl", ar); @@ -136,16 +137,6 @@ void luaEmptyEventBuffer() events[1] = 0; } -void luaSetInstructionsLimit(lua_State * L, int count) -{ - instructionsPercent = 0; -#if defined(LUA_ALLOCATOR_TRACER) - lua_sethook(L, luaHook, LUA_MASKCOUNT|LUA_MASKLINE, count); -#else - lua_sethook(L, luaHook, LUA_MASKCOUNT, count); -#endif -} - int luaGetInputs(lua_State * L2, ScriptInputsOutputs & sid) { if (!lua_istable(L2, -1)) @@ -370,7 +361,7 @@ static void luaDumpState(lua_State * L, const char * filename, const FILINFO * f TRACE("luaDumpState(%s): Saved bytecode to file.", filename); } } else - TRACE_ERROR("luaDumpState(%s): Error: Could not open output file.", filename); + TRACE_ERROR("luaDumpState(%s): Error: Could not open output file\n", filename); } #endif // LUA_COMPILER @@ -685,18 +676,46 @@ void displayLuaError(const char * title) #if !defined(COLORLCD) drawMessageBox(title); #endif + if (lua_warning_info[0]) { - char * split = strstr(lua_warning_info, ": "); + char *split = strstr(lua_warning_info, ": "); +#if !defined(COLORLCD) if (split) { - lcdDrawSizedText(WARNING_LINE_X, WARNING_LINE_Y+FH+3, lua_warning_info, split-lua_warning_info, SMLSIZE); - lcdDrawSizedText(WARNING_LINE_X, WARNING_LINE_Y+2*FH+2, split+2, lua_warning_info+LUA_WARNING_INFO_LEN-split, SMLSIZE); - } - else { - lcdDrawSizedText(WARNING_LINE_X, WARNING_LINE_Y+FH+3, lua_warning_info, 40, SMLSIZE); +#if LCD_W == 128 + if (strlen(split + 2) <= 20) { + lcdDrawSizedText(WARNING_LINE_X, WARNING_LINE_Y + FH + 3, + lua_warning_info, split - lua_warning_info, SMLSIZE); + lcdDrawSizedText(WARNING_LINE_X, WARNING_LINE_Y + 2 * FH + 2, split + 2, + strlen(split + 2), SMLSIZE); + } else { + lcdDrawSizedText(WARNING_LINE_X, WARNING_LINE_Y + FH, lua_warning_info, + split - lua_warning_info, SMLSIZE); + lcdDrawSizedText(WARNING_LINE_X, WARNING_LINE_Y + 2 * FH, split + 2, 20, + SMLSIZE); + lcdDrawSizedText(WARNING_LINE_X, WARNING_LINE_Y + 3 * FH, split + 22, + strlen(split + 22), SMLSIZE); + } +#else + lcdDrawSizedText(WARNING_LINE_X, WARNING_LINE_Y + FH + 3, + lua_warning_info, split - lua_warning_info, SMLSIZE); + lcdDrawSizedText(WARNING_LINE_X, WARNING_LINE_Y + 2 * FH + 2, split + 2, + lua_warning_info + LUA_WARNING_INFO_LEN - split, + SMLSIZE); +#endif + } else { + // TODO lcdDrawSizedText(WARNING_LINE_X, WARNING_LINE_Y + FH + 3, + // lua_warning_info, 40, SMLSIZE); } +#else + // Split line... + //if (split) { *(split+1) = '\n'; } + (void)split; // mark as unused + POPUP_WARNING(title, lua_warning_info); +#endif } } +#if !defined(COLORLCD) void displayAcknowledgeLuaError(event_t event) { warningResult = false; @@ -705,6 +724,8 @@ void displayAcknowledgeLuaError(event_t event) warningText = nullptr; } } +#endif + void luaError(lua_State * L, uint8_t error, bool acknowledge) { const char * errorTitle; @@ -720,22 +741,32 @@ void luaError(lua_State * L, uint8_t error, bool acknowledge) errorTitle = STR_SCRIPT_ERROR; break; } + const char * msg = lua_tostring(L, -1); if (msg) { #if defined(SIMU) if (!strncmp(msg, ".", 2)) msg += 1; #endif +#if LCD_W == 128 + msg = strrchr(msg, '/') + 1; +#else if (!strncmp(msg, "/SCRIPTS/", 9)) msg += 9; +#endif strncpy(lua_warning_info, msg, LUA_WARNING_INFO_LEN); lua_warning_info[LUA_WARNING_INFO_LEN] = '\0'; } else { lua_warning_info[0] = '\0'; } + if (acknowledge) { +#if !defined(COLORLCD) warningText = errorTitle; warningType = WARNING_TYPE_INFO; popupFunc = displayAcknowledgeLuaError; +#else + displayLuaError(errorTitle); +#endif } else { displayLuaError(errorTitle); @@ -881,7 +912,10 @@ static void luaLoadScripts(bool init, const char * filename = nullptr) if (initFunction != LUA_NOREF) TRACE_ERROR("luaLoadScripts(%s): init function: %s\n", getScriptName(idx), lua_tostring(lsScripts, -1)); - else + luaL_unref(lsScripts, LUA_REGISTRYINDEX, initFunction); + lua_settop(lsScripts, 0); + initFunction = LUA_NOREF; + } else TRACE_ERROR("luaLoadScripts(%s): %s\n", getScriptName(idx), lua_tostring(lsScripts, -1)); if (ref == SCRIPT_STANDALONE) { @@ -889,11 +923,7 @@ static void luaLoadScripts(bool init, const char * filename = nullptr) luaState = INTERPRETER_RELOAD_PERMANENT_SCRIPTS; return; } - - if (initFunction != LUA_NOREF) { - luaL_unref(lsScripts, LUA_REGISTRYINDEX, initFunction); - initFunction = LUA_NOREF; - } + // Replace the dead coroutine with a new one lua_pop(L, 1); // Pop the dead coroutine off the main stack lsScripts = lua_newthread(L); // Push the new coroutine From dcac3cb9da5c56da9535693206f686b774ae44e1 Mon Sep 17 00:00:00 2001 From: jfrickmann Date: Wed, 23 Jun 2021 17:44:05 -0400 Subject: [PATCH 3/6] Fixed Lua error messages in standalone and telemetry scripts - again. There is still a problem that the call to POPUP_WARNING in displayLuaError does not return and the gui goes dark on colorlcd. Could it be long jump? --- radio/src/gui/colorlcd/standalone_lua.cpp | 13 +++++++++---- radio/src/lua/interface.cpp | 7 ++----- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/radio/src/gui/colorlcd/standalone_lua.cpp b/radio/src/gui/colorlcd/standalone_lua.cpp index abe74cf81..4f4306977 100644 --- a/radio/src/gui/colorlcd/standalone_lua.cpp +++ b/radio/src/gui/colorlcd/standalone_lua.cpp @@ -156,17 +156,22 @@ void StandaloneLuaWindow::runLua(event_t evt) luaLcdBuffer = &lcdBuffer; TRACE("evt = 0x%x", evt); + + bool hasRun = luaTask(evt, true); + + if (luaState == INTERPRETER_RELOAD_PERMANENT_SCRIPTS) { + // Script does not run anymore... + TRACE("LUA standalone script exited: deleting window!"); + deleteLater(); + } - bool hasRun = luaTask(evt, RUN_STNDAL_SCRIPT, true); if (hasRun) { #if defined(DEBUG_WINDOWS) TRACE("# StandaloneLuaWindow::invalidate()"); #endif invalidate(); } else { - // Script does not run anymore... - TRACE("LUA standalone script exited: deleting window!"); - deleteLater(); + // The script was preempted, and the LCD should not be updated yet } // Kill global LUA LCD buffer diff --git a/radio/src/lua/interface.cpp b/radio/src/lua/interface.cpp index 359135409..419019f67 100644 --- a/radio/src/lua/interface.cpp +++ b/radio/src/lua/interface.cpp @@ -912,15 +912,12 @@ static void luaLoadScripts(bool init, const char * filename = nullptr) if (initFunction != LUA_NOREF) TRACE_ERROR("luaLoadScripts(%s): init function: %s\n", getScriptName(idx), lua_tostring(lsScripts, -1)); - luaL_unref(lsScripts, LUA_REGISTRYINDEX, initFunction); - lua_settop(lsScripts, 0); - initFunction = LUA_NOREF; - } else + else TRACE_ERROR("luaLoadScripts(%s): %s\n", getScriptName(idx), lua_tostring(lsScripts, -1)); if (ref == SCRIPT_STANDALONE) { - luaError(lsScripts, sid.state); luaState = INTERPRETER_RELOAD_PERMANENT_SCRIPTS; + luaError(lsScripts, sid.state, true); return; } From 76d2ef54e35230a30955f71da3743bb7a3298758 Mon Sep 17 00:00:00 2001 From: jfrickmann Date: Sat, 26 Jun 2021 12:47:14 -0400 Subject: [PATCH 4/6] Fixing standalone scripts with preemption on colorlcd --- radio/src/gui/colorlcd/standalone_lua.cpp | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/radio/src/gui/colorlcd/standalone_lua.cpp b/radio/src/gui/colorlcd/standalone_lua.cpp index 4f4306977..95bd504d0 100644 --- a/radio/src/gui/colorlcd/standalone_lua.cpp +++ b/radio/src/gui/colorlcd/standalone_lua.cpp @@ -155,25 +155,23 @@ void StandaloneLuaWindow::runLua(event_t evt) // Set global LUA LCD buffer luaLcdBuffer = &lcdBuffer; - TRACE("evt = 0x%x", evt); + if (luaState != INTERPRETER_RELOAD_PERMANENT_SCRIPTS) { + if (luaTask(evt, true)) { +#if defined(DEBUG_WINDOWS) + TRACE("# StandaloneLuaWindow::invalidate()"); +#endif + invalidate(); + } else { + // The script was preempted, and the LCD should not be updated yet + } + } - bool hasRun = luaTask(evt, true); - if (luaState == INTERPRETER_RELOAD_PERMANENT_SCRIPTS) { // Script does not run anymore... TRACE("LUA standalone script exited: deleting window!"); deleteLater(); } - if (hasRun) { -#if defined(DEBUG_WINDOWS) - TRACE("# StandaloneLuaWindow::invalidate()"); -#endif - invalidate(); - } else { - // The script was preempted, and the LCD should not be updated yet - } - // Kill global LUA LCD buffer luaLcdBuffer = nullptr; } From 56718388641b6b570ac0bc3e4230c0020efb66d2 Mon Sep 17 00:00:00 2001 From: jfrickmann Date: Fri, 9 Jul 2021 18:00:43 -0400 Subject: [PATCH 5/6] Cleaned up code for Mixer/Custom/Model scripts and added compiler directives --- radio/src/lua/interface.cpp | 176 +++++++++++++++++++----------------- radio/src/lua/lua_api.h | 2 + 2 files changed, 97 insertions(+), 81 deletions(-) diff --git a/radio/src/lua/interface.cpp b/radio/src/lua/interface.cpp index 419019f67..4f2fea594 100644 --- a/radio/src/lua/interface.cpp +++ b/radio/src/lua/interface.cpp @@ -137,34 +137,35 @@ void luaEmptyEventBuffer() events[1] = 0; } -int luaGetInputs(lua_State * L2, ScriptInputsOutputs & sid) +#if defined(LUA_MODEL_SCRIPTS) +void luaGetInputs(ScriptInputsOutputs & sid) { - if (!lua_istable(L2, -1)) - return -1; + if (!lua_istable(lsScripts, -1)) + return; memclear(sid.inputs, sizeof(sid.inputs)); sid.inputsCount = 0; - for (lua_pushnil(L2); lua_next(L2, -2); lua_pop(L2, 1)) { - luaL_checktype(L2, -2, LUA_TNUMBER); // key is number - luaL_checktype(L2, -1, LUA_TTABLE); // value is table + for (lua_pushnil(lsScripts); lua_next(lsScripts, -2); lua_pop(lsScripts, 1)) { + luaL_checktype(lsScripts, -2, LUA_TNUMBER); // key is number + luaL_checktype(lsScripts, -1, LUA_TTABLE); // value is table if (sid.inputsCountname = lua_tostring(L, -2); break; case 1: - luaL_checktype(L2, -2, LUA_TNUMBER); // key is number - luaL_checktype(L2, -1, LUA_TNUMBER); // value is number - type = lua_tointeger(L2, -1); + luaL_checktype(lsScripts, -2, LUA_TNUMBER); // key is number + luaL_checktype(lsScripts, -1, LUA_TNUMBER); // value is number + type = lua_tointeger(lsScripts, -1); if (type >= INPUT_TYPE_FIRST && type <= INPUT_TYPE_LAST) { si->type = type; } @@ -177,24 +178,24 @@ int luaGetInputs(lua_State * L2, ScriptInputsOutputs & sid) } break; case 2: - luaL_checktype(L2, -2, LUA_TNUMBER); // key is number - luaL_checktype(L2, -1, LUA_TNUMBER); // value is number + luaL_checktype(lsScripts, -2, LUA_TNUMBER); // key is number + luaL_checktype(lsScripts, -1, LUA_TNUMBER); // value is number if (si->type == INPUT_TYPE_VALUE) { - si->min = lua_tointeger(L2, -1); + si->min = lua_tointeger(lsScripts, -1); } break; case 3: - luaL_checktype(L2, -2, LUA_TNUMBER); // key is number - luaL_checktype(L2, -1, LUA_TNUMBER); // value is number + luaL_checktype(lsScripts, -2, LUA_TNUMBER); // key is number + luaL_checktype(lsScripts, -1, LUA_TNUMBER); // value is number if (si->type == INPUT_TYPE_VALUE) { - si->max = lua_tointeger(L2, -1); + si->max = lua_tointeger(lsScripts, -1); } break; case 4: - luaL_checktype(L2, -2, LUA_TNUMBER); // key is number - luaL_checktype(L2, -1, LUA_TNUMBER); // value is number + luaL_checktype(lsScripts, -2, LUA_TNUMBER); // key is number + luaL_checktype(lsScripts, -1, LUA_TNUMBER); // value is number if (si->type == INPUT_TYPE_VALUE) { - si->def = lua_tointeger(L2, -1); + si->def = lua_tointeger(lsScripts, -1); } break; } @@ -202,19 +203,17 @@ int luaGetInputs(lua_State * L2, ScriptInputsOutputs & sid) sid.inputsCount++; } } - - return 0; } -int luaGetOutputs(lua_State * L2, ScriptInputsOutputs & sid) +void luaGetOutputs(ScriptInputsOutputs & sid) { - if (!lua_istable(L2, -1)) - return -1; + if (!lua_istable(lsScripts, -1)) + return; sid.outputsCount = 0; - for (lua_pushnil(L2); lua_next(L2, -2); ) { - luaL_checktype(L2, -2, LUA_TNUMBER); // key is number - luaL_checktype(L2, -1, LUA_TSTRING); // value is string + for (lua_pushnil(lsScripts); lua_next(lsScripts, -2); ) { + luaL_checktype(lsScripts, -2, LUA_TNUMBER); // key is number + luaL_checktype(lsScripts, -1, LUA_TSTRING); // value is string if (sid.outputsCount < MAX_SCRIPT_OUTPUTS) { lua_xmove(lsScripts, L, 1); // To preserve the string value, move it to the main stack lua_insert(L, -2); // Keep the coroutine at the top of the main stack @@ -222,9 +221,8 @@ int luaGetOutputs(lua_State * L2, ScriptInputsOutputs & sid) } else lua_pop(lsScripts, 1); } - - return 0; } +#endif void luaDisable() { @@ -540,10 +538,12 @@ static const char * getScriptName(uint8_t idx) { int ref = scriptInternalData[idx].reference; +#if defined(LUA_MODEL_SCRIPTS) if (ref <= SCRIPT_MIX_LAST) { return g_model.scriptsData[ref - SCRIPT_MIX_FIRST].file; - } - else if (ref <= SCRIPT_FUNC_LAST) { + } else +#endif + if (ref <= SCRIPT_FUNC_LAST) { return g_model.customFn[ref - SCRIPT_FUNC_FIRST].play.name; } else if (ref <= SCRIPT_GFUNC_LAST) { @@ -571,6 +571,7 @@ static bool luaLoad(const char * filename, ScriptInternalData & sid) return false; } +#if defined(LUA_MODEL_SCRIPTS) static bool luaLoadMixScript(uint8_t ref) { uint8_t idx = ref - SCRIPT_MIX_FIRST; @@ -589,6 +590,7 @@ static bool luaLoadMixScript(uint8_t ref) } return false; } +#endif static bool luaLoadFunctionScript(uint8_t ref) { @@ -610,7 +612,7 @@ static bool luaLoadFunctionScript(uint8_t ref) if (luaScriptsCount < MAX_SCRIPTS) { ScriptInternalData & sid = scriptInternalData[luaScriptsCount++]; sid.reference = ref; - + char filename[sizeof(SCRIPTS_FUNCS_PATH) + LEN_FUNCTION_NAME + sizeof(SCRIPT_EXT)] = SCRIPTS_FUNCS_PATH "/"; strncpy(filename + sizeof(SCRIPTS_FUNCS_PATH), fn->play.name, LEN_FUNCTION_NAME); filename[sizeof(SCRIPTS_FUNCS_PATH) + LEN_FUNCTION_NAME] = '\0'; @@ -634,17 +636,17 @@ static bool luaLoadTelemetryScript(uint8_t ref) if (screenType == TELEMETRY_SCREEN_TYPE_SCRIPT) { TelemetryScriptData & script = g_model.screens[idx].script; - + if (ZEXIST(script.file)) { if (luaScriptsCount < MAX_SCRIPTS) { ScriptInternalData & sid = scriptInternalData[luaScriptsCount++]; sid.reference = ref; - + char filename[sizeof(SCRIPTS_TELEM_PATH) + LEN_SCRIPT_FILENAME + sizeof(SCRIPT_EXT)] = SCRIPTS_TELEM_PATH "/"; strncpy(filename + sizeof(SCRIPTS_TELEM_PATH), script.file, LEN_SCRIPT_FILENAME); filename[sizeof(SCRIPTS_TELEM_PATH) + LEN_SCRIPT_FILENAME] = '\0'; strcat(filename + sizeof(SCRIPTS_TELEM_PATH), SCRIPT_EXT); - + return luaLoad(filename, sid); } else { @@ -778,7 +780,7 @@ static int luaRegisterFunction(const char * key) { lua_getfield(lsScripts, -1, key); int typ = lua_type(lsScripts, -1); - + if (typ == LUA_TFUNCTION) { return luaL_ref(lsScripts, LUA_REGISTRYINDEX); } @@ -801,7 +803,7 @@ static void luaLoadScripts(bool init, const char * filename = nullptr) if (init) { luaInit(); if (luaState == INTERPRETER_PANIC) return; - + luaLcdAllowed = false; initFunction = LUA_NOREF; luaEmptyEventBuffer(); @@ -811,10 +813,14 @@ static void luaLoadScripts(bool init, const char * filename = nullptr) ref = SCRIPT_STANDALONE; } else { +#if defined(LUA_MODEL_SCRIPTS) ref = SCRIPT_MIX_FIRST; +#else + ref = SCRIPT_FUNC_FIRST; +#endif } } - + // Continue loop with existing reference value do { uint8_t countBefore = luaScriptsCount; @@ -822,10 +828,12 @@ static void luaLoadScripts(bool init, const char * filename = nullptr) // If Lua is not yielded, then find the next script to load if (luaStatus == LUA_OK) { +#if defined(LUA_MODEL_SCRIPTS) if (ref <= SCRIPT_MIX_LAST) { if (luaLoadMixScript(ref)) continue; // If error then skip the rest of the loop - } - else if (ref <= SCRIPT_GFUNC_LAST) { + } else +#endif + if (ref <= SCRIPT_GFUNC_LAST) { if (luaLoadFunctionScript(ref)) continue; } #if defined(PCBTARANIS) @@ -846,15 +854,15 @@ static void luaLoadScripts(bool init, const char * filename = nullptr) // Skip the rest of the loop if we did not get a new script if (countBefore == luaScriptsCount) continue; } - + int idx = luaScriptsCount - 1; ScriptInternalData & sid = scriptInternalData[idx]; - + // 1. run chunk() 2. run init(), if available: do { // Resume running the coroutine luaStatus = lua_resume(lsScripts, 0, 0); - + if (luaStatus == LUA_YIELD) { // Coroutine yielded - wait for the next cycle return; @@ -870,7 +878,7 @@ static void luaLoadScripts(bool init, const char * filename = nullptr) else { // chunk() returned lua_settop(lsScripts, 1); // Only one return value, please - + if (lua_istable(lsScripts, -1)) { // Register functions from the table sid.run = luaRegisterFunction("run"); @@ -880,25 +888,27 @@ static void luaLoadScripts(bool init, const char * filename = nullptr) TRACE_ERROR("luaLoadScripts(%s): No run function\n", getScriptName(idx)); sid.state = SCRIPT_SYNTAX_ERROR; } +#if defined(LUA_MODEL_SCRIPTS) // Get input/output tables for mixer scripts if (ref <= SCRIPT_MIX_LAST) { ScriptInputsOutputs * sio = & scriptInputsOutputs[idx]; lua_getfield(lsScripts, -1, "input"); - luaGetInputs(lsScripts, *sio); + luaGetInputs(*sio); lua_pop(lsScripts, 1); lua_getfield(lsScripts, -1, "output"); - luaGetOutputs(lsScripts, *sio); + luaGetOutputs(*sio); lua_pop(lsScripts, 1); } +#endif } else { TRACE_ERROR("luaLoadScripts(%s): The script did not return a table\n", getScriptName(idx)); sid.state = SCRIPT_SYNTAX_ERROR; } - + // Pop the table off the stack lua_pop(lsScripts, 1); - + // If init(), push it on the stack if (initFunction != LUA_NOREF) { lua_rawgeti(lsScripts, LUA_REGISTRYINDEX, initFunction); @@ -914,7 +924,7 @@ static void luaLoadScripts(bool init, const char * filename = nullptr) TRACE_ERROR("luaLoadScripts(%s): init function: %s\n", getScriptName(idx), lua_tostring(lsScripts, -1)); else TRACE_ERROR("luaLoadScripts(%s): %s\n", getScriptName(idx), lua_tostring(lsScripts, -1)); - + if (ref == SCRIPT_STANDALONE) { luaState = INTERPRETER_RELOAD_PERMANENT_SCRIPTS; luaError(lsScripts, sid.state, true); @@ -925,13 +935,13 @@ static void luaLoadScripts(bool init, const char * filename = nullptr) lua_pop(L, 1); // Pop the dead coroutine off the main stack lsScripts = lua_newthread(L); // Push the new coroutine } - + luaDoGc(lsScripts, true); - + } while(initFunction != LUA_NOREF); - + } while(++ref < SCRIPT_STANDALONE); - + // Loading has finished - start running scripts luaState = INTERPRETER_START_RUNNING; } // luaLoadScripts @@ -951,7 +961,7 @@ static bool resumeLua(bool init, bool allowLcdUsage) bool scriptWasRun = false; bool fullGC = !allowLcdUsage; static uint8_t luaDisplayStatistics = false; - + // Run in the right interactive mode if (allowLcdUsage != luaLcdAllowed) { #if defined(PCBTARANIS) @@ -963,11 +973,11 @@ static bool resumeLua(bool init, bool allowLcdUsage) #endif return scriptWasRun; } - + do { ScriptInternalData & sid = scriptInternalData[idx]; if (sid.state != SCRIPT_OK) continue; - + uint8_t ref = sid.reference; int inputsCount = 0; int luaStatus = lua_status(lsScripts); @@ -975,7 +985,7 @@ static bool resumeLua(bool init, bool allowLcdUsage) if (luaStatus == LUA_OK) { // Not preempted - setup another function call lua_settop(lsScripts, 0); - + if (allowLcdUsage) { #if defined(PCBTARANIS) if ((menuHandlers[menuLevel] == menuViewTelemetry && ref == SCRIPT_TELEMETRY_FIRST + s_frsky_view) || ref == SCRIPT_STANDALONE) { @@ -986,7 +996,7 @@ static bool resumeLua(bool init, bool allowLcdUsage) evt = events[0]; events[0] = events[1]; events[1] = 0; - + lua_rawgeti(lsScripts, LUA_REGISTRYINDEX, sid.run); lua_pushunsigned(lsScripts, evt); inputsCount = 1; @@ -994,28 +1004,30 @@ static bool resumeLua(bool init, bool allowLcdUsage) else continue; } else { +#if defined(LUA_MODEL_SCRIPTS) if (ref <= SCRIPT_MIX_LAST) { lua_rawgeti(lsScripts, LUA_REGISTRYINDEX, sid.run); - + ScriptData & sd = g_model.scriptsData[ref - SCRIPT_MIX_FIRST]; ScriptInputsOutputs * sio = & scriptInputsOutputs[ref - SCRIPT_MIX_FIRST]; inputsCount = sio -> inputsCount; - + for (int j = 0; j < inputsCount; j++) { if (sio -> inputs[j].type == INPUT_TYPE_SOURCE) luaGetValueAndPush(lsScripts, sd.inputs[j].source); else lua_pushinteger(lsScripts, sd.inputs[j].value + sio -> inputs[j].def); } - } - else if (ref <= SCRIPT_GFUNC_LAST) { + } else +#endif + if (ref <= SCRIPT_GFUNC_LAST) { CustomFunctionData * fn; - + if (ref <= SCRIPT_FUNC_LAST) fn = &g_model.customFn[ref - SCRIPT_FUNC_FIRST]; else fn = &g_eeGeneral.customFn[ref - SCRIPT_GFUNC_FIRST]; - + if (getSwitch(fn -> swtch)) { lua_rawgeti(lsScripts, LUA_REGISTRYINDEX, sid.run); } @@ -1048,7 +1060,8 @@ static bool resumeLua(bool init, bool allowLcdUsage) else if (luaStatus == LUA_OK) { // Coroutine returned scriptWasRun = true; - + +#if defined(LUA_MODEL_SCRIPTS) if (ref <= SCRIPT_MIX_LAST) { ScriptInputsOutputs * sio = & scriptInputsOutputs[ref - SCRIPT_MIX_FIRST]; lua_settop(lsScripts, sio -> outputsCount); @@ -1062,13 +1075,14 @@ static bool resumeLua(bool init, bool allowLcdUsage) sio -> outputs[j].value = lua_tointeger(lsScripts, -1); lua_pop(lsScripts, 1); } - } - else if (ref == SCRIPT_STANDALONE) { + } else +#endif + if (ref == SCRIPT_STANDALONE) { lua_settop(lsScripts, 1); if (lua_isnumber(lsScripts, -1)) { int scriptResult = lua_tointeger(lsScripts, -1); lua_pop(lsScripts, 1); /* pop returned value */ - + if (scriptResult != 0) { TRACE("Script finished with status %d", scriptResult); sid.state = SCRIPT_FINISHED; @@ -1096,7 +1110,7 @@ static bool resumeLua(bool init, bool allowLcdUsage) TRACE_ERROR("Script run function returned unexpected value\n"); sid.state = SCRIPT_SYNTAX_ERROR; } - + if (evt == EVT_KEY_LONG(KEY_EXIT)) { TRACE("Script force exit"); killEvents(evt); @@ -1126,7 +1140,7 @@ static bool resumeLua(bool init, bool allowLcdUsage) lsScripts = lua_newthread(L); // Push the new coroutine } } - + if (sid.state != SCRIPT_OK) { if (sid.reference == SCRIPT_STANDALONE) { luaState = INTERPRETER_RELOAD_PERMANENT_SCRIPTS; @@ -1135,11 +1149,11 @@ static bool resumeLua(bool init, bool allowLcdUsage) else luaFree(lsScripts, sid); } } while (++idx < luaScriptsCount); - + // Toggle between background and foreground scripts luaLcdAllowed = !luaLcdAllowed; idx = 0; - + return scriptWasRun; } //resumeLua(...) @@ -1148,16 +1162,16 @@ bool luaTask(event_t evt, bool allowLcdUsage) { bool init = false; bool scriptWasRun = false; - + // Add event to buffer if (evt != 0) { if (events[0] == 0) events[0] = evt; else if (events[1] == 0) events[1] = evt; } - + // For preemption if (!allowLcdUsage) luaCycleStart = get_tmr10ms(); - + // Trying to replace CPU usage measure instructionsPercent = 100 * maxLuaDuration / LUA_TASK_PERIOD_TICKS; @@ -1165,7 +1179,7 @@ bool luaTask(event_t evt, bool allowLcdUsage) case INTERPRETER_RELOAD_PERMANENT_SCRIPTS: init = true; luaState = INTERPRETER_LOADING; - + case INTERPRETER_LOADING: PROTECT_LUA() { luaLoadScripts(init); @@ -1173,11 +1187,11 @@ bool luaTask(event_t evt, bool allowLcdUsage) else luaDisable(); UNPROTECT_LUA(); break; - + case INTERPRETER_START_RUNNING: init = true; luaState = INTERPRETER_RUNNING; - + case INTERPRETER_RUNNING: PROTECT_LUA() { scriptWasRun = resumeLua(init, allowLcdUsage); @@ -1244,7 +1258,7 @@ void luaInit() // lsScripts is now a coroutine in lieu of the main thread to support preemption lsScripts = lua_newthread(L); - + // Clear loaded scripts memset(scriptInternalData, 0, sizeof(scriptInternalData)); memset(scriptInputsOutputs, 0, sizeof(scriptInputsOutputs)); diff --git a/radio/src/lua/lua_api.h b/radio/src/lua/lua_api.h index 30626eaba..ca0d9c294 100644 --- a/radio/src/lua/lua_api.h +++ b/radio/src/lua/lua_api.h @@ -111,8 +111,10 @@ enum ScriptState { }; enum ScriptReference { +#if defined(LUA_MODEL_SCRIPTS) SCRIPT_MIX_FIRST, SCRIPT_MIX_LAST=SCRIPT_MIX_FIRST+MAX_SCRIPTS-1, +#endif SCRIPT_FUNC_FIRST, SCRIPT_FUNC_LAST=SCRIPT_FUNC_FIRST+MAX_SPECIAL_FUNCTIONS-1, // model functions SCRIPT_GFUNC_FIRST, From b0732e156406017e30b6f20cd19dd02034996a62 Mon Sep 17 00:00:00 2001 From: jfrickmann Date: Wed, 14 Jul 2021 15:49:27 -0400 Subject: [PATCH 6/6] Don't kill the mixer scripts - just preempt them :-) --- radio/src/gui/colorlcd/model_mixer_scripts.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/radio/src/gui/colorlcd/model_mixer_scripts.cpp b/radio/src/gui/colorlcd/model_mixer_scripts.cpp index 0e0fd012a..dec877b7b 100644 --- a/radio/src/gui/colorlcd/model_mixer_scripts.cpp +++ b/radio/src/gui/colorlcd/model_mixer_scripts.cpp @@ -167,7 +167,6 @@ class ScriptEditWindow : public Page { }; constexpr char SCRIPT_STATUS_ERROR[] = "(error)"; -constexpr char SCRIPT_STATUS_KILLED[] = "(killed)"; class ScriptLineButton : public Button { @@ -204,10 +203,6 @@ class ScriptLineButton : public Button dc->drawSizedText(x, y, SCRIPT_STATUS_ERROR, sizeof(SCRIPT_STATUS_ERROR), textColor); break; - case SCRIPT_KILLED: - dc->drawSizedText(x, y, SCRIPT_STATUS_KILLED, - sizeof(SCRIPT_STATUS_KILLED), textColor); - break; default: dc->drawNumber(x, y, runtimeData->instructions, textColor, 0, nullptr, "%"); break;