/* * Authors (alphabetical order) * - Andre Bernet * - Andreas Weitl * - Bertrand Songis * - Bryan J. Rentoul (Gruvin) * - Cameron Weeks * - Erez Raviv * - Gabriel Birkus * - Jean-Pierre Parisy * - Karl Szmutny * - Michael Blandford * - Michal Hlavinka * - Pat Mackenzie * - Philip Moss * - Rob Thomson * - Romolo Manfredini * - Thomas Husterer * * opentx is based on code named * gruvin9x by Bryan J. Rentoul: http://code.google.com/p/gruvin9x/, * er9x by Erez Raviv: http://code.google.com/p/er9x/, * and the original (and ongoing) project by * Thomas Husterer, th9x: http://code.google.com/p/th9x/ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * */ #include #include #include "opentx.h" #include "stamp-opentx.h" #include "bin_allocator.h" #include "timers.h" #if !defined(SIMU) extern "C" { #endif #include #include #include #include #if !defined(SIMU) } #endif #if defined(PCBFLAMENCO) #include "lua_exports_flamenco.inc" // this line must be after lua headers #elif defined(PCBTARANIS) #include "lua_exports_taranis.inc" // this line must be after lua headers #endif #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)) #define lua_pushtableboolean(L, k, v) (lua_pushstring(L, (k)), lua_pushboolean(L, (v)), lua_settable(L, -3)) #define lua_pushtableinteger(L, k, v) (lua_pushstring(L, (k)), lua_pushinteger(L, (v)), lua_settable(L, -3)) #define lua_pushtablenumber(L, k, v) (lua_pushstring(L, (k)), lua_pushnumber(L, (v)), lua_settable(L, -3)) #define lua_pushtablestring(L, k, v) (lua_pushstring(L, (k)), lua_pushstring(L, (v)), lua_settable(L, -3)) #define lua_pushtablenzstring(L, k, v) { char tmp[sizeof(v)+1]; strncpy(tmp, (v), sizeof(v)); tmp[sizeof(v)] = '\0'; lua_pushstring(L, (k)); lua_pushstring(L, tmp); lua_settable(L, -3); } #define lua_pushtablezstring(L, k, v) { char tmp[sizeof(v)+1]; zchar2str(tmp, (v), sizeof(v)); lua_pushstring(L, (k)); lua_pushstring(L, tmp); lua_settable(L, -3); } #define lua_registerlib(L, name, tab) (luaL_newmetatable(L, name), luaL_setfuncs(L, tab, 0), lua_setglobal(L, name)) lua_State *L = NULL; uint8_t luaState = 0; uint8_t luaScriptsCount = 0; ScriptInternalData scriptInternalData[MAX_SCRIPTS] = { { SCRIPT_NOFILE, 0 } }; ScriptInputsOutputs scriptInputsOutputs[MAX_SCRIPTS] = { {0} }; ScriptInternalData standaloneScript = { SCRIPT_NOFILE, 0 }; uint16_t maxLuaInterval = 0; uint16_t maxLuaDuration = 0; bool luaLcdAllowed; #define PERMANENT_SCRIPTS_MAX_INSTRUCTIONS (10000/100) #define MANUAL_SCRIPTS_MAX_INSTRUCTIONS (20000/100) #define SET_LUA_INSTRUCTIONS_COUNT(x) (instructionsPercent=0, lua_sethook(L, hook, LUA_MASKCOUNT, x)) struct our_longjmp * global_lj = 0; /* custom panic handler */ static int custom_lua_atpanic(lua_State *lua) { TRACE("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 */ } return 0; } static int instructionsPercent = 0; void hook(lua_State* L, lua_Debug *ar) { instructionsPercent++; 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, hook, LUA_MASKLINE, 0); luaL_error(L, ""); } } static int luaGetVersion(lua_State *L) { lua_pushstring(L, VERS_STR); return 1; } static int luaGetTime(lua_State *L) { lua_pushunsigned(L, get_tmr10ms()); return 1; } static void luaPushDateTime(lua_State *L, uint32_t year, uint32_t mon, uint32_t day, uint32_t hour, uint32_t min, uint32_t sec) { lua_createtable(L, 0, 6); lua_pushtableinteger(L, "year", year); lua_pushtableinteger(L, "mon", mon); lua_pushtableinteger(L, "day", day); lua_pushtableinteger(L, "hour", hour); lua_pushtableinteger(L, "min", min); lua_pushtableinteger(L, "sec", sec); } static int luaGetDateTime(lua_State *L) { struct gtm utm; gettime(&utm); luaPushDateTime(L, utm.tm_year + 1900, utm.tm_mon + 1, utm.tm_mday, utm.tm_hour, utm.tm_min, utm.tm_sec); return 1; } static void luaPushLatLon(TelemetrySensor & telemetrySensor, TelemetryItem & telemetryItem) /* result is lua table containing members ["lat"] and ["lon"] as lua_Number (doubles) in decimal degrees */ { lua_Number lat = 0.0; lua_Number lon = 0.0; uint32_t gpsLat = 0; uint32_t gpsLon = 0; telemetryItem.gps.extractLatitudeLongitude(&gpsLat, &gpsLon); /* close, but not the format we want */ lat = gpsLat / 1000000.0; if (telemetryItem.gps.latitudeNS == 'S') lat = -lat; lon = gpsLon / 1000000.0; if (telemetryItem.gps.longitudeEW == 'W') lon = -lon; lua_createtable(L, 0, 2); lua_pushtablenumber(L, "lat", lat); lua_pushtablenumber(L, "lon", lon); } static void luaPushTelemetryDateTime(TelemetrySensor & telemetrySensor, TelemetryItem & telemetryItem) { luaPushDateTime(L, telemetryItem.datetime.year + 2000, telemetryItem.datetime.month, telemetryItem.datetime.day, telemetryItem.datetime.hour, telemetryItem.datetime.min, telemetryItem.datetime.sec); } static void luaPushCells(TelemetrySensor & telemetrySensor, TelemetryItem & telemetryItem) { if (telemetryItem.cells.count == 0) lua_pushinteger(L, (int)0); // returns zero if no cells else { lua_createtable(L, telemetryItem.cells.count, 0); for (int i = 0; i < telemetryItem.cells.count; i++) { lua_pushnumber(L, i + 1); lua_pushnumber(L, telemetryItem.cells.values[i].value / 100.0); lua_settable(L, -3); } } } static void luaGetValueAndPush(int src) { getvalue_t value = getValue(src); // ignored for GPS, DATETIME, and CELLS if (src >= MIXSRC_FIRST_TELEM && src <= MIXSRC_LAST_TELEM) { src = (src-MIXSRC_FIRST_TELEM) / 3; // telemetry values if (TELEMETRY_STREAMING() && telemetryItems[src].isAvailable()) { TelemetrySensor & telemetrySensor = g_model.telemetrySensors[src]; switch (telemetrySensor.unit) { case UNIT_GPS: luaPushLatLon(telemetrySensor, telemetryItems[src]); break; case UNIT_DATETIME: luaPushTelemetryDateTime(telemetrySensor, telemetryItems[src]); break; case UNIT_CELLS: luaPushCells(telemetrySensor, telemetryItems[src]); break; default: if (telemetrySensor.prec > 0) lua_pushnumber(L, float(value)/(telemetrySensor.prec == 2 ? 100.0 : 10.0)); else lua_pushinteger(L, value); break; } } else { // telemetry not working, return zero for telemetry sources lua_pushinteger(L, (int)0); } } else if (src == MIXSRC_TX_VOLTAGE) { lua_pushnumber(L, float(value)/10.0); } else { lua_pushinteger(L, value); } } struct LuaField { uint16_t id; char desc[50]; }; #define FIND_FIELD_DESC 0x01 /** Return field data for a given field name */ bool luaFindFieldByName(const char * name, LuaField & field, unsigned int flags=0) { // TODO better search method (binary lookup) for (unsigned int n=0; n= count) { // TODO error } if (flags & BLINK) { drawFilledRect(x, y, w-9, count*9+2, SOLID, ERASE); lcd_rect(x, y, w-9, count*9+2); for (int i=0; isrcRaw || expo->chn >= chn) { return i; } } return 0; } static unsigned int getInputsCountFromFirst(unsigned int chn, unsigned int first) { unsigned int count = 0; for (unsigned int i=first; isrcRaw || expo->chn!=chn) break; count++; } return count; } static unsigned int getInputsCount(unsigned int chn) { return getInputsCountFromFirst(chn, getFirstInput(chn)); } static int luaModelGetInputsCount(lua_State *L) { unsigned int chn = luaL_checkunsigned(L, 1); int count = getInputsCount(chn); lua_pushinteger(L, count); return 1; } static int luaModelGetInput(lua_State *L) { unsigned int chn = luaL_checkunsigned(L, 1); unsigned int idx = luaL_checkunsigned(L, 2); unsigned int first = getFirstInput(chn); unsigned int count = getInputsCountFromFirst(chn, first); if (idx < count) { ExpoData * expo = expoAddress(first+idx); lua_newtable(L); lua_pushtablezstring(L, "name", expo->name); lua_pushtableinteger(L, "source", expo->srcRaw); lua_pushtableinteger(L, "weight", expo->weight); lua_pushtableinteger(L, "offset", expo->offset); lua_pushtableinteger(L, "switch", expo->swtch); } else { lua_pushnil(L); } return 1; } static int luaModelInsertInput(lua_State *L) { unsigned int chn = luaL_checkunsigned(L, 1); unsigned int idx = luaL_checkunsigned(L, 2); unsigned int first = getFirstInput(chn); unsigned int count = getInputsCountFromFirst(chn, first); if (chnname, name, sizeof(expo->name)); } else if (!strcmp(key, "source")) { expo->srcRaw = luaL_checkinteger(L, -1); } else if (!strcmp(key, "weight")) { expo->weight = luaL_checkinteger(L, -1); } else if (!strcmp(key, "offset")) { expo->offset = luaL_checkinteger(L, -1); } else if (!strcmp(key, "switch")) { expo->swtch = luaL_checkinteger(L, -1); } } } return 0; } static int luaModelDeleteInput(lua_State *L) { unsigned int chn = luaL_checkunsigned(L, 1); unsigned int idx = luaL_checkunsigned(L, 2); int first = getFirstInput(chn); unsigned int count = getInputsCountFromFirst(chn, first); if (idx < count) { deleteExpoMix(1, first+idx); } return 0; } static int luaModelDeleteInputs(lua_State *L) { clearInputs(); return 0; } static int luaModelDefaultInputs(lua_State *L) { defaultInputs(); return 0; } static unsigned int getFirstMix(unsigned int chn) { for (unsigned int i=0; isrcRaw || mix->destCh>=chn) { return i; } } return 0; } static unsigned int getMixesCountFromFirst(unsigned int chn, unsigned int first) { unsigned int count = 0; for (unsigned int i=first; isrcRaw || mix->destCh!=chn) break; count++; } return count; } static unsigned int getMixesCount(unsigned int chn) { return getMixesCountFromFirst(chn, getFirstMix(chn)); } static int luaModelGetMixesCount(lua_State *L) { unsigned int chn = luaL_checkunsigned(L, 1); unsigned int count = getMixesCount(chn); lua_pushinteger(L, count); return 1; } static int luaModelGetMix(lua_State *L) { unsigned int chn = luaL_checkunsigned(L, 1); unsigned int idx = luaL_checkunsigned(L, 2); unsigned int first = getFirstMix(chn); unsigned int count = getMixesCountFromFirst(chn, first); if (idx < count) { MixData * mix = mixAddress(first+idx); lua_newtable(L); lua_pushtablezstring(L, "name", mix->name); lua_pushtableinteger(L, "source", mix->srcRaw); lua_pushtableinteger(L, "weight", mix->weight); lua_pushtableinteger(L, "offset", mix->offset); lua_pushtableinteger(L, "switch", mix->swtch); lua_pushtableinteger(L, "curveType", mix->curve.type); lua_pushtableinteger(L, "curveValue", mix->curve.value); lua_pushtableinteger(L, "multiplex", mix->mltpx); lua_pushtableinteger(L, "flightModes", mix->flightModes); lua_pushtableboolean(L, "carryTrim", mix->carryTrim); lua_pushtableinteger(L, "mixWarn", mix->mixWarn); lua_pushtableinteger(L, "delayUp", mix->delayUp); lua_pushtableinteger(L, "delayDown", mix->delayDown); lua_pushtableinteger(L, "speedUp", mix->speedUp); lua_pushtableinteger(L, "speedDown", mix->speedDown); } else { lua_pushnil(L); } return 1; } static int luaModelInsertMix(lua_State *L) { unsigned int chn = luaL_checkunsigned(L, 1); unsigned int idx = luaL_checkunsigned(L, 2); unsigned int first = getFirstMix(chn); unsigned int count = getMixesCountFromFirst(chn, first); if (chnname, name, sizeof(mix->name)); } else if (!strcmp(key, "source")) { mix->srcRaw = luaL_checkinteger(L, -1); } else if (!strcmp(key, "weight")) { mix->weight = luaL_checkinteger(L, -1); } else if (!strcmp(key, "offset")) { mix->offset = luaL_checkinteger(L, -1); } else if (!strcmp(key, "switch")) { mix->swtch = luaL_checkinteger(L, -1); } else if (!strcmp(key, "curveType")) { mix->curve.type = luaL_checkinteger(L, -1); } else if (!strcmp(key, "curveValue")) { mix->curve.value = luaL_checkinteger(L, -1); } else if (!strcmp(key, "multiplex")) { mix->mltpx = luaL_checkinteger(L, -1); } else if (!strcmp(key, "flightModes")) { mix->flightModes = luaL_checkinteger(L, -1); } else if (!strcmp(key, "carryTrim")) { mix->carryTrim = lua_toboolean(L, -1); } else if (!strcmp(key, "mixWarn")) { mix->mixWarn = luaL_checkinteger(L, -1); } else if (!strcmp(key, "delayUp")) { mix->delayUp = luaL_checkinteger(L, -1); } else if (!strcmp(key, "delayDown")) { mix->delayDown = luaL_checkinteger(L, -1); } else if (!strcmp(key, "speedUp")) { mix->speedUp = luaL_checkinteger(L, -1); } else if (!strcmp(key, "speedDown")) { mix->speedDown = luaL_checkinteger(L, -1); } } } return 0; } static int luaModelDeleteMix(lua_State *L) { unsigned int chn = luaL_checkunsigned(L, 1); unsigned int idx = luaL_checkunsigned(L, 2); unsigned int first = getFirstMix(chn); unsigned int count = getMixesCountFromFirst(chn, first); if (idx < count) { deleteExpoMix(0, first+idx); } return 0; } static int luaModelDeleteMixes(lua_State *L) { memset(g_model.mixData, 0, sizeof(g_model.mixData)); return 0; } static int luaModelGetLogicalSwitch(lua_State *L) { unsigned int idx = luaL_checkunsigned(L, 1); if (idx < NUM_LOGICAL_SWITCH) { LogicalSwitchData * sw = lswAddress(idx); lua_newtable(L); lua_pushtableinteger(L, "func", sw->func); lua_pushtableinteger(L, "v1", sw->v1); lua_pushtableinteger(L, "v2", sw->v2); lua_pushtableinteger(L, "v3", sw->v3); lua_pushtableinteger(L, "and", sw->andsw); lua_pushtableinteger(L, "delay", sw->delay); lua_pushtableinteger(L, "duration", sw->duration); } else { lua_pushnil(L); } return 1; } static int luaModelSetLogicalSwitch(lua_State *L) { unsigned int idx = luaL_checkunsigned(L, 1); if (idx < NUM_LOGICAL_SWITCH) { LogicalSwitchData * sw = lswAddress(idx); memclear(sw, sizeof(LogicalSwitchData)); luaL_checktype(L, -1, LUA_TTABLE); for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) { luaL_checktype(L, -2, LUA_TSTRING); // key is string const char * key = luaL_checkstring(L, -2); if (!strcmp(key, "func")) { sw->func = luaL_checkinteger(L, -1); } else if (!strcmp(key, "v1")) { sw->v1 = luaL_checkinteger(L, -1); } else if (!strcmp(key, "v2")) { sw->v2 = luaL_checkinteger(L, -1); } else if (!strcmp(key, "v3")) { sw->v3 = luaL_checkinteger(L, -1); } else if (!strcmp(key, "and")) { sw->andsw = luaL_checkinteger(L, -1); } else if (!strcmp(key, "delay")) { sw->delay = luaL_checkinteger(L, -1); } else if (!strcmp(key, "duration")) { sw->duration = luaL_checkinteger(L, -1); } } eeDirty(EE_MODEL); } return 0; } static int luaModelGetCurve(lua_State *L) { unsigned int idx = luaL_checkunsigned(L, 1); if (idx < MAX_CURVES) { CurveInfo & curveInfo = g_model.curves[idx]; lua_newtable(L); lua_pushtablezstring(L, "name", g_model.curveNames[idx]); lua_pushtableinteger(L, "type", curveInfo.type); lua_pushtableboolean(L, "smooth", curveInfo.smooth); lua_pushtableinteger(L, "points", curveInfo.points+5); lua_pushstring(L, "y"); lua_newtable(L); int8_t * point = curveAddress(idx); for (int i=0; iplay.name); } else { lua_pushtableinteger(L, "value", cfn->all.val); lua_pushtableinteger(L, "mode", cfn->all.mode); lua_pushtableinteger(L, "param", cfn->all.param); } lua_pushtableinteger(L, "active", CFN_ACTIVE(cfn)); } else { lua_pushnil(L); } return 1; } static int luaModelSetCustomFunction(lua_State *L) { unsigned int idx = luaL_checkunsigned(L, 1); if (idx < NUM_CFN) { CustomFunctionData * cfn = &g_model.customFn[idx]; memclear(cfn, sizeof(CustomFunctionData)); luaL_checktype(L, -1, LUA_TTABLE); for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) { luaL_checktype(L, -2, LUA_TSTRING); // key is string const char * key = luaL_checkstring(L, -2); if (!strcmp(key, "switch")) { CFN_SWITCH(cfn) = luaL_checkinteger(L, -1); } else if (!strcmp(key, "func")) { CFN_FUNC(cfn) = luaL_checkinteger(L, -1); } else if (!strcmp(key, "name")) { const char * name = luaL_checkstring(L, -1); strncpy(cfn->play.name, name, sizeof(cfn->play.name)); } else if (!strcmp(key, "value")) { cfn->all.val = luaL_checkinteger(L, -1); } else if (!strcmp(key, "mode")) { cfn->all.mode = luaL_checkinteger(L, -1); } else if (!strcmp(key, "param")) { cfn->all.param = luaL_checkinteger(L, -1); } else if (!strcmp(key, "active")) { CFN_ACTIVE(cfn) = luaL_checkinteger(L, -1); } } eeDirty(EE_MODEL); } return 0; } static int luaModelGetOutput(lua_State *L) { unsigned int idx = luaL_checkunsigned(L, 1); if (idx < NUM_CHNOUT) { LimitData * limit = limitAddress(idx); lua_newtable(L); lua_pushtablezstring(L, "name", limit->name); lua_pushtableinteger(L, "min", limit->min-1000); lua_pushtableinteger(L, "max", limit->max+1000); lua_pushtableinteger(L, "offset", limit->offset); lua_pushtableinteger(L, "ppmCenter", limit->ppmCenter); lua_pushtableinteger(L, "symetrical", limit->symetrical); lua_pushtableinteger(L, "revert", limit->revert); if (limit->curve) lua_pushtableinteger(L, "curve", limit->curve-1); else lua_pushtablenil(L, "curve"); } else { lua_pushnil(L); } return 1; } static int luaModelSetOutput(lua_State *L) { unsigned int idx = luaL_checkunsigned(L, 1); if (idx < NUM_CHNOUT) { LimitData * limit = limitAddress(idx); luaL_checktype(L, -1, LUA_TTABLE); for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) { luaL_checktype(L, -2, LUA_TSTRING); // key is string const char * key = luaL_checkstring(L, -2); if (!strcmp(key, "name")) { const char * name = luaL_checkstring(L, -1); str2zchar(limit->name, name, sizeof(limit->name)); } else if (!strcmp(key, "min")) { limit->min = luaL_checkinteger(L, -1)+1000; } else if (!strcmp(key, "max")) { limit->max = luaL_checkinteger(L, -1)-1000; } else if (!strcmp(key, "offset")) { limit->offset = luaL_checkinteger(L, -1); } else if (!strcmp(key, "ppmCenter")) { limit->ppmCenter = luaL_checkinteger(L, -1); } else if (!strcmp(key, "symetrical")) { limit->symetrical = luaL_checkinteger(L, -1); } else if (!strcmp(key, "revert")) { limit->revert = luaL_checkinteger(L, -1); } else if (!strcmp(key, "curve")) { if (lua_isnil(L, -1)) limit->curve = 0; else limit->curve = luaL_checkinteger(L, -1) + 1; } } eeDirty(EE_MODEL); } return 0; } static int luaModelGetGlobalVariable(lua_State *L) { unsigned int idx = luaL_checkunsigned(L, 1); unsigned int phase = luaL_checkunsigned(L, 2); if (phase < MAX_FLIGHT_MODES && idx < MAX_GVARS) lua_pushinteger(L, g_model.flightModeData[phase].gvars[idx]); else lua_pushnil(L); return 1; } static int luaModelSetGlobalVariable(lua_State *L) { unsigned int idx = luaL_checkunsigned(L, 1); unsigned int phase = luaL_checkunsigned(L, 2); int value = luaL_checkinteger(L, 3); if (phase < MAX_FLIGHT_MODES && idx < MAX_GVARS && value >= -GVAR_MAX && value <= GVAR_MAX) { g_model.flightModeData[phase].gvars[idx] = value; eeDirty(EE_MODEL); } return 0; } static int luaPopupInput(lua_State *L) { uint8_t event = luaL_checkinteger(L, 2); s_warning_input_value = luaL_checkinteger(L, 3); s_warning_input_min = luaL_checkinteger(L, 4); s_warning_input_max = luaL_checkinteger(L, 5); s_warning = luaL_checkstring(L, 1); s_warning_type = WARNING_TYPE_INPUT; displayWarning(event); if (s_warning_result) { s_warning_result = 0; lua_pushstring(L, "OK"); } else if (!s_warning) { lua_pushstring(L, "CANCEL"); } else { lua_pushinteger(L, s_warning_input_value); } s_warning = NULL; return 1; } static int luaDefaultStick(lua_State *L) { uint8_t channel = luaL_checkinteger(L, 1); lua_pushinteger(L, channel_order(channel+1)-1); return 1; } static int luaDefaultChannel(lua_State *L) { uint8_t stick = luaL_checkinteger(L, 1); for (int i=1; i<=4; i++) { int tmp = channel_order(i) - 1; if (tmp == stick) { lua_pushinteger(L, i-1); return 1; } } lua_pushnil(L); return 1; } int luaGetInputs(ScriptInputsOutputs & sid) { if (!lua_istable(L, -1)) return -1; 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 if (sid.inputsCount 100) { TRACE("Script killed"); standaloneScript.state = SCRIPT_KILLED; luaState = INTERPRETER_RELOAD_PERMANENT_SCRIPTS; } else if (lua_isstring(L, -1)) { char nextScript[_MAX_LFN+1]; strncpy(nextScript, lua_tostring(L, -1), _MAX_LFN); nextScript[_MAX_LFN] = '\0'; luaExec(nextScript); } else { TRACE("Script run function returned unexpected value"); standaloneScript.state = SCRIPT_SYNTAX_ERROR; luaState = INTERPRETER_RELOAD_PERMANENT_SCRIPTS; } } else { int scriptResult = lua_tointeger(L, -1); lua_pop(L, 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) { lcd_hline(0, 7*FH-1, lcdLastPos+6, ERASE); lcd_puts(0, 7*FH, "GV Use: "); lcd_outdezAtt(lcdLastPos, 7*FH, luaGetMemUsed(), LEFT); lcd_putc(lcdLastPos, 7*FH, 'b'); lcd_hline(0, 7*FH-2, lcdLastPos+6, FORCE); lcd_vlineStip(lcdLastPos+6, 7*FH-2, FH+2, SOLID, FORCE); } } } else { TRACE("Script error: %s", lua_tostring(L, -1)); standaloneScript.state = (instructionsPercent > 100 ? SCRIPT_KILLED : SCRIPT_SYNTAX_ERROR); luaState = INTERPRETER_RELOAD_PERMANENT_SCRIPTS; } if (standaloneScript.state != SCRIPT_OK) { luaError(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; } else if (evt == EVT_KEY_LONG(KEY_MENU)) { killEvents(evt); luaDisplayStatistics = !luaDisplayStatistics; } } } bool luaDoOneRunPermanentScript(uint8_t evt, int i, uint32_t scriptType) { ScriptInternalData & sid = scriptInternalData[i]; if (sid.state != SCRIPT_OK) return false; SET_LUA_INSTRUCTIONS_COUNT(PERMANENT_SCRIPTS_MAX_INSTRUCTIONS); int inputsCount = 0; #if defined(SIMU) || defined(DEBUG) const char *filename; #endif ScriptInputsOutputs * sio = NULL; #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(L, LUA_REGISTRYINDEX, sid.run); for (int j=0; jinputsCount; j++) { if (sio->inputs[j].type == 1) luaGetValueAndPush((uint8_t)sd.inputs[j]); else lua_pushinteger(L, sd.inputs[j] + sio->inputs[j].def); } } else if ((scriptType & RUN_FUNC_SCRIPT) && (sid.reference >= SCRIPT_FUNC_FIRST && sid.reference <= SCRIPT_FUNC_LAST)) { CustomFunctionData & fn = g_model.customFn[sid.reference-SCRIPT_FUNC_FIRST]; if (!getSwitch(fn.swtch)) return false; #if defined(SIMU) || defined(DEBUG) filename = fn.play.name; #endif lua_rawgeti(L, LUA_REGISTRYINDEX, sid.run); } else { #if defined(SIMU) || defined(DEBUG) TelemetryScriptData & script = g_model.frsky.screens[sid.reference-SCRIPT_TELEMETRY_FIRST].script; filename = script.file; #endif if ((scriptType & RUN_TELEM_FG_SCRIPT) && #if defined(PCBFLAMENCO) (g_menuStack[0]==menuMainView && sid.reference==SCRIPT_TELEMETRY_FIRST+g_eeGeneral.view-VIEW_TELEM1)) { #else (g_menuStack[0]==menuTelemetryFrsky && sid.reference==SCRIPT_TELEMETRY_FIRST+s_frsky_view)) { #endif lua_rawgeti(L, LUA_REGISTRYINDEX, sid.run); lua_pushinteger(L, evt); inputsCount = 1; } else if ((scriptType & RUN_TELEM_BG_SCRIPT) && (sid.background)) { lua_rawgeti(L, LUA_REGISTRYINDEX, sid.background); } else { return false; } } if (lua_pcall(L, inputsCount, sio ? sio->outputsCount : 0, 0) == 0) { if (sio) { for (int j=sio->outputsCount-1; j>=0; j--) { if (!lua_isnumber(L, -1)) { sid.state = (instructionsPercent > 100 ? SCRIPT_KILLED : SCRIPT_SYNTAX_ERROR); TRACE("Script %8s disabled", filename); break; } sio->outputs[j].value = lua_tointeger(L, -1); lua_pop(L, 1); } } } else { if (instructionsPercent > 100) { TRACE("Script %8s killed", filename); sid.state = SCRIPT_KILLED; } else { TRACE("Script %8s error: %s", filename, lua_tostring(L, -1)); sid.state = SCRIPT_SYNTAX_ERROR; } } if (sid.state != SCRIPT_OK) { luaFree(sid); } else { if (instructionsPercent > sid.instructions) { sid.instructions = instructionsPercent; } } return true; } void luaDoGc() { if (L) { PROTECT_LUA() { lua_gc(L, LUA_GCCOLLECT, 0); #if defined(SIMU) || defined(DEBUG) static int lastgc = 0; int gc = luaGetMemUsed(); if (gc != lastgc) { lastgc = gc; TRACE("GC Use: %dbytes", gc); } #endif } else { // we disable Lua for the rest of the session luaDisable(); } UNPROTECT_LUA(); } } bool luaTask(uint8_t evt, uint8_t scriptType, bool allowLcdUsage) { if (luaState == INTERPRETER_PANIC) return false; luaLcdAllowed = allowLcdUsage; bool scriptWasRun = 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(); } 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