/* * 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" #if !defined(SIMU) extern "C" { #endif #include #include #include #include #if !defined(SIMU) } #endif #include "lua_exports.cpp" // this line must be after lua headers #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; #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 luaGetValueAndPush(int src) { if (src >= MIXSRC_FIRST_TELEM && src <= MIXSRC_LAST_TELEM) { // telemetry values if ((src != MIXSRC_FIRST_TELEM-1+TELEM_TX_VOLTAGE) && !TELEMETRY_STREAMING()) { // telemetry not working, return zero for telemetry sources // except for "tx voltage" lua_pushinteger(L, (int)0); return; } } int idx = src; switch (src) { case MIXSRC_FIRST_TELEM-1+TELEM_TX_VOLTAGE: case MIXSRC_FIRST_TELEM-1+TELEM_VFAS: case MIXSRC_FIRST_TELEM-1+TELEM_MIN_VFAS: case MIXSRC_FIRST_TELEM-1+TELEM_CELLS_SUM: case MIXSRC_FIRST_TELEM-1+TELEM_MIN_CELLS_SUM: case MIXSRC_FIRST_TELEM-1+TELEM_CURRENT: case MIXSRC_FIRST_TELEM-1+TELEM_MAX_CURRENT: case MIXSRC_FIRST_TELEM-1+TELEM_ASPEED: case MIXSRC_FIRST_TELEM-1+TELEM_MAX_ASPEED: // these need to be divided by 10 lua_pushnumber(L, getValue(src)/10.0); break; case MIXSRC_FIRST_TELEM-1+TELEM_MIN_A1: case MIXSRC_FIRST_TELEM-1+TELEM_MIN_A2: case MIXSRC_FIRST_TELEM-1+TELEM_MIN_A3: case MIXSRC_FIRST_TELEM-1+TELEM_MIN_A4: idx -= TELEM_MIN_A1-TELEM_A1; // no break case MIXSRC_FIRST_TELEM-1+TELEM_A1: case MIXSRC_FIRST_TELEM-1+TELEM_A2: case MIXSRC_FIRST_TELEM-1+TELEM_A3: case MIXSRC_FIRST_TELEM-1+TELEM_A4: // convert raw Ax values to calibrated values idx -= (MIXSRC_FIRST_TELEM+TELEM_A1-1); lua_pushnumber(L, applyChannelRatio(idx, getValue(src))/100.0); break; case MIXSRC_FIRST_TELEM-1+TELEM_CELL: case MIXSRC_FIRST_TELEM-1+TELEM_MIN_CELL: case MIXSRC_FIRST_TELEM-1+TELEM_ALT: case MIXSRC_FIRST_TELEM-1+TELEM_ACCx: case MIXSRC_FIRST_TELEM-1+TELEM_ACCy: case MIXSRC_FIRST_TELEM-1+TELEM_ACCz: case MIXSRC_FIRST_TELEM-1+TELEM_VSPEED: // these need to be divided by 100 lua_pushnumber(L, getValue(src)/100.0); break; //TODO: add other values that need special treatment default: lua_pushinteger(L, getValue(src)); } } 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= EXTRA_FIRST ) { // one of the extra fields return luaGetExtraValue(src); } luaGetValueAndPush(src); return 1; } static int luaPlayFile(lua_State *L) { const char * filename = luaL_checkstring(L, 1); if (filename[0] != '/') { // relative sound file path - use current languague dir for absolute path char file[AUDIO_FILENAME_MAXLEN+1]; char * str = getAudioPath(file); strncpy(str, filename, AUDIO_FILENAME_MAXLEN - (str-file)); file[AUDIO_FILENAME_MAXLEN] = 0; PLAY_FILE(file, 0, 0); } else { PLAY_FILE(filename, 0, 0); } return 0; } static int luaPlayNumber(lua_State *L) { int number = luaL_checkinteger(L, 1); int unit = luaL_checkinteger(L, 2); int att = luaL_checkinteger(L, 3); playNumber(number, unit, att, 0); return 0; } static int luaPlayDuration(lua_State *L) { int duration = luaL_checkinteger(L, 1); bool playTime = (luaL_checkinteger(L, 2) != 0); playDuration(duration, playTime ? PLAY_TIME : 0, 0); return 0; } static int luaPlayTone(lua_State *L) { int frequency = luaL_checkinteger(L, 1); int length = luaL_checkinteger(L, 2); int pause = luaL_checkinteger(L, 3); int flags = luaL_optinteger(L, 4, 0); int freqIncr = luaL_optinteger(L, 5, 0); audioQueue.playTone(frequency, length, pause, flags, freqIncr); return 0; } static int luaKillEvents(lua_State *L) { int event = luaL_checkinteger(L, 1); killEvents(event); return 0; } static int luaGetGeneralSettings(lua_State *L) { lua_newtable(L); lua_pushtablenumber(L, "battMin", double(90+g_eeGeneral.vBatMin)/10); lua_pushtablenumber(L, "battMax", double(120+g_eeGeneral.vBatMax)/10); return 1; } static int luaLcdLock(lua_State *L) { lcd_locked = true; return 0; } static int luaLcdClear(lua_State *L) { lcd_clear(); return 0; } static int luaLcdDrawPoint(lua_State *L) { int x = luaL_checkinteger(L, 1); int y = luaL_checkinteger(L, 2); lcd_plot(x, y); return 0; } static int luaLcdDrawLine(lua_State *L) { int x1 = luaL_checkinteger(L, 1); int y1 = luaL_checkinteger(L, 2); int x2 = luaL_checkinteger(L, 3); int y2 = luaL_checkinteger(L, 4); int pat = luaL_checkinteger(L, 5); int flags = luaL_checkinteger(L, 6); lcd_line(x1, y1, x2, y2, pat, flags); return 0; } static int luaLcdGetLastPos(lua_State *L) { lua_pushinteger(L, lcdLastPos); return 1; } static int luaLcdDrawText(lua_State *L) { int x = luaL_checkinteger(L, 1); int y = luaL_checkinteger(L, 2); const char * s = luaL_checkstring(L, 3); int att = luaL_checkinteger(L, 4); lcd_putsAtt(x, y, s, att); return 0; } static int luaLcdDrawTimer(lua_State *L) { int x = luaL_checkinteger(L, 1); int y = luaL_checkinteger(L, 2); int seconds = luaL_checkinteger(L, 3); int att = luaL_checkinteger(L, 4); putsTimer(x, y, seconds, att|LEFT, att); return 0; } static int luaLcdDrawNumber(lua_State *L) { int x = luaL_checkinteger(L, 1); int y = luaL_checkinteger(L, 2); int n = luaL_checkinteger(L, 3); int att = luaL_checkinteger(L, 4); lcd_outdezAtt(x, y, n, att); return 0; } static int luaLcdDrawChannel(lua_State *L) { int x = luaL_checkinteger(L, 1); int y = luaL_checkinteger(L, 2); int channel = -1; if (lua_isnumber(L, 3)) { channel = luaL_checkinteger(L, 3); } else { const char * what = luaL_checkstring(L, 3); LuaField field; bool found = luaFindFieldByName(what, field); if (found) { channel = field.id; } } int att = luaL_checkinteger(L, 4); getvalue_t value = getValue(channel); putsTelemetryChannel(x, y, channel-MIXSRC_FIRST_TELEM, value, att); return 0; } static int luaLcdDrawSwitch(lua_State *L) { int x = luaL_checkinteger(L, 1); int y = luaL_checkinteger(L, 2); int s = luaL_checkinteger(L, 3); int att = luaL_checkinteger(L, 4); putsSwitches(x, y, s, att); return 0; } static int luaLcdDrawSource(lua_State *L) { int x = luaL_checkinteger(L, 1); int y = luaL_checkinteger(L, 2); int s = luaL_checkinteger(L, 3); int att = luaL_checkinteger(L, 4); putsMixerSource(x, y, s, att); return 0; } static int luaLcdDrawPixmap(lua_State *L) { int x = luaL_checkinteger(L, 1); int y = luaL_checkinteger(L, 2); const char * filename = luaL_checkstring(L, 3); uint8_t bitmap[2+LCD_W/2*LCD_H/2]; // width max is LCD_W/2 pixels for saving stack and avoid a malloc here const pm_char * error = bmpLoad(bitmap, filename, LCD_W/2, LCD_H); if (!error) { lcd_bmp(x, y, bitmap); } return 0; } static int luaLcdDrawRectangle(lua_State *L) { int x = luaL_checkinteger(L, 1); int y = luaL_checkinteger(L, 2); int w = luaL_checkinteger(L, 3); int h = luaL_checkinteger(L, 4); lcd_rect(x, y, w, h, 0xff, 0); return 0; } static int luaLcdDrawFilledRectangle(lua_State *L) { int x = luaL_checkinteger(L, 1); int y = luaL_checkinteger(L, 2); int w = luaL_checkinteger(L, 3); int h = luaL_checkinteger(L, 4); int flags = luaL_checkinteger(L, 5); lcd_filled_rect(x, y, w, h, SOLID, flags); return 0; } static int luaLcdDrawGauge(lua_State *L) { int x = luaL_checkinteger(L, 1); int y = luaL_checkinteger(L, 2); int w = luaL_checkinteger(L, 3); int h = luaL_checkinteger(L, 4); int num = luaL_checkinteger(L, 5); int den = luaL_checkinteger(L, 6); // int flags = luaL_checkinteger(L, 7); lcd_rect(x, y, w, h); uint8_t len = limit((uint8_t)1, uint8_t(w*num/den), uint8_t(w)); for (int i=1; i= count) { // TODO error } if (flags & BLINK) { lcd_filled_rect(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) break; if (expo->chn == chn) { return i; } } return -1; } static int getInputsCountFromFirst(int chn, int first) { int count = 0; if (first >= 0) { for (int i=first; isrcRaw || expo->chn!=chn) break; count++; } } return count; } static int getInputsCount(int chn) { int first = getFirstInput(chn); return getInputsCountFromFirst(chn, first); } static int luaModelGetInputsCount(lua_State *L) { int chn = luaL_checkunsigned(L, 1); int count = getInputsCount(chn); lua_pushinteger(L, count); return 1; } static int luaModelGetInput(lua_State *L) { int chn = luaL_checkunsigned(L, 1); int idx = luaL_checkunsigned(L, 2); int first = getFirstInput(chn); int count = getInputsCountFromFirst(chn, first); if (first>=0 && idxname); lua_pushtableinteger(L, "source", expo->srcRaw); lua_pushtableinteger(L, "weight", expo->weight); lua_pushtableinteger(L, "offset", expo->offset); } else { lua_pushnil(L); } return 1; } static int luaModelInsertInput(lua_State *L) { int chn = luaL_checkunsigned(L, 1); int idx = luaL_checkunsigned(L, 2); int first = getFirstInput(chn); 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) { int chn = luaL_checkunsigned(L, 1); int idx = luaL_checkunsigned(L, 2); int first = getFirstInput(chn); int count = getInputsCountFromFirst(chn, first); if (first>=0 && idxsrcRaw || mix->destCh>=chn) { return i; } } return 0; } static int getMixesCountFromFirst(int chn, int first) { int count = 0; if (first >= 0) { for (int i=first; isrcRaw || mix->destCh!=chn) break; count++; } } return count; } static int getMixesCount(int chn) { int first = getFirstMix(chn); return getMixesCountFromFirst(chn, first); } static int luaModelGetMixesCount(lua_State *L) { int chn = luaL_checkunsigned(L, 1); int count = getMixesCount(chn); lua_pushinteger(L, count); return 1; } static int luaModelGetMix(lua_State *L) { int chn = luaL_checkunsigned(L, 1); int idx = luaL_checkunsigned(L, 2); int first = getFirstMix(chn); int count = getMixesCountFromFirst(chn, first); if (idxname); 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, "multiplex", mix->mltpx); } else { lua_pushnil(L); } return 1; } static int luaModelInsertMix(lua_State *L) { int chn = luaL_checkunsigned(L, 1); int idx = luaL_checkunsigned(L, 2); int first = getFirstMix(chn); 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, "multiplex")) { mix->mltpx = luaL_checkinteger(L, -1); } } } return 0; } static int luaModelDeleteMix(lua_State *L) { int chn = luaL_checkunsigned(L, 1); int idx = luaL_checkunsigned(L, 2); int first = getFirstMix(chn); int count = getMixesCountFromFirst(chn, first); if (idxfunc); 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) { 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 luaModelGetCustomFunction(lua_State *L) { int idx = luaL_checkunsigned(L, 1); if (idx < NUM_CFN) { CustomFnData * cfn = &g_model.funcSw[idx]; lua_newtable(L); lua_pushtableinteger(L, "switch", CFN_SWITCH(cfn)); lua_pushtableinteger(L, "func", CFN_FUNC(cfn)); if (CFN_FUNC(cfn) == FUNC_PLAY_TRACK || CFN_FUNC(cfn) == FUNC_BACKGND_MUSIC || CFN_FUNC(cfn) == FUNC_PLAY_SCRIPT) { lua_pushtablenzstring(L, "name", cfn->play.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) { int idx = luaL_checkunsigned(L, 1); if (idx < NUM_CFN) { CustomFnData * cfn = &g_model.funcSw[idx]; memclear(cfn, sizeof(CustomFnData)); 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) { 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) { 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) { int idx = luaL_checkunsigned(L, 1); 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) { int idx = luaL_checkunsigned(L, 1); 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 luaModelGetTelemetryChannel(lua_State *L) { int idx = luaL_checkunsigned(L, 1); if (idx < MAX_FRSKY_A_CHANNELS) { FrSkyChannelData & channel = g_model.frsky.channels[idx]; lua_newtable(L); double range = applyChannelRatio(idx, 255-channel.offset); double offset = applyChannelRatio(idx, 0); double alarm1 = applyChannelRatio(idx, channel.alarms_value[0]); double alarm2 = applyChannelRatio(idx, channel.alarms_value[1]); if (ANA_CHANNEL_UNIT(idx) >= UNIT_RAW) { range /= 10; offset /= 10; alarm1 /= 10; alarm2 /= 10; } else { range /= 100; offset /= 100; alarm1 /= 100; alarm2 /= 100; } lua_pushtablenumber(L, "range", range); lua_pushtablenumber(L, "offset", offset); lua_pushtablenumber(L, "alarm1", alarm1); lua_pushtablenumber(L, "alarm2", alarm2); lua_pushtableinteger(L, "unit", channel.type); } else { lua_pushnil(L); } return 1; } int findmult(float value, float base) { int vvalue = value*10; int vbase = base*10; vvalue--; int mult = 0; for (int i=8; i>=0; i--) { if (vvalue/vbase >= (1<= UNIT_RAW) { range /= 10; offset /= 10; alarm1 /= 10; alarm2 /= 10; } else { range /= 100; offset /= 100; alarm1 /= 100; alarm2 /= 100; } 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, "unit")) { channel.type = luaL_checkinteger(L, -1); } else if (!strcmp(key, "range")) { range = luaL_checknumber(L, -1); double value = range; if (ANA_CHANNEL_UNIT(idx) < UNIT_RAW) { value *= 10; } channel.multiplier = findmult(value, 255); channel.ratio = (int)(round(value)) / (1< 0) { channel.offset = round((offset * 255) / range); channel.alarms_value[0] = limit(0, round((alarm1-offset)*255/range), 255); channel.alarms_value[1] = limit(0, round((alarm2-offset)*255/range), 255); } else { channel.offset = 0;; channel.alarms_value[0] = 0; channel.alarms_value[1] = 0; } 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 error"); 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+FW, 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+FW, FORCE); lcd_vlineStip(lcdLastPos+FW, 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; } } } void luaDoOneRunPermanentScript(uint8_t evt, int i) { ScriptInternalData & sid = scriptInternalData[i]; if (sid.state == SCRIPT_OK) { SET_LUA_INSTRUCTIONS_COUNT(PERMANENT_SCRIPTS_MAX_INSTRUCTIONS); int inputsCount = 0; #if defined(SIMU) || defined(DEBUG) const char *filename; #endif ScriptInputsOutputs * sio = NULL; if (sid.reference >= SCRIPT_MIX_FIRST && sid.reference <= SCRIPT_MIX_LAST) { 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 (sid.reference >= SCRIPT_FUNC_FIRST && sid.reference <= SCRIPT_FUNC_LAST) { CustomFnData & fn = g_model.funcSw[sid.reference-SCRIPT_FUNC_FIRST]; if (!getSwitch(fn.swtch)) { return; } #if defined(SIMU) || defined(DEBUG) filename = fn.play.name; #endif lua_rawgeti(L, LUA_REGISTRYINDEX, sid.run); } else { #if defined(SIMU) || defined(DEBUG) filename = "[telem]"; #endif if (g_menuStack[0]==menuTelemetryFrsky && sid.reference==SCRIPT_TELEMETRY_FIRST+s_frsky_view) { lua_rawgeti(L, LUA_REGISTRYINDEX, sid.run); lua_pushinteger(L, evt); inputsCount = 1; } else if (sid.background) { lua_rawgeti(L, LUA_REGISTRYINDEX, sid.background); } else { return; } } 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; } } } } 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(); } } void luaTask(uint8_t evt) { lcd_locked = false; uint32_t t0 = get_tmr10ms(); static uint32_t lastLuaTime = 0; uint16_t interval = (lastLuaTime == 0 ? 0 : (t0 - lastLuaTime)); lastLuaTime = t0; if (interval > maxLuaInterval) { maxLuaInterval = interval; } if (luaState == INTERPRETER_PANIC) { return; } if (luaState & INTERPRETER_RUNNING_STANDALONE_SCRIPT) { PROTECT_LUA() { luaDoOneRunStandalone(evt); } else { luaDisable(); return; } UNPROTECT_LUA(); } else { // model scripts if (luaState & INTERPRETER_RELOAD_PERMANENT_SCRIPTS) { luaState = 0; luaInit(); if (luaState == INTERPRETER_PANIC) return; luaLoadPermanentScripts(); if (luaState == INTERPRETER_PANIC) return; } for (int i=0; i maxLuaDuration) { maxLuaDuration = t0; } } int luaGetMemUsed() { return (lua_gc(L, LUA_GCCOUNT, 0) << 10) + lua_gc(L, LUA_GCCOUNTB, 0); }