/* * 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 "opentx.h" #include "stamp-opentx.h" #if !defined(SIMU) extern "C" { #endif #include #include #include #if !defined(SIMU) } #endif #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_pushtablenumber(L, k, v) (lua_pushstring(L, (k)), lua_pushinteger(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_pushtablezstring(L, k, v) { char _zz[sizeof(v)+1]; zchar2str(_zz, (v), sizeof(v)); lua_pushstring(L, (k)); lua_pushstring(L, _zz); 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; ScriptInternalData scriptInternalData[MAX_SCRIPTS] = { { SCRIPT_NOFILE, 0 } }; ScriptInternalData standaloneScript = { SCRIPT_NOFILE, 0 }; #define PERMANENT_SCRIPTS_MAX_INSTRUCTIONS (1000/100) #define MANUAL_SCRIPTS_MAX_INSTRUCTIONS (10000/100) #define SCRIPTS_MAX_HEAP 50 #define SET_LUA_INSTRUCTIONS_COUNT(x) (instructionsPercent=0, lua_sethook(L, hook, LUA_MASKCOUNT, x)) 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_pushnumber(L, VERS_NUM); return 1; } static int luaGetTime(lua_State *L) { lua_pushunsigned(L, get_tmr10ms()); return 1; } static int luaGetValue(lua_State *L) { if (lua_isnumber(L, 1)) { int src = luaL_checkinteger(L, 1); lua_pushinteger(L, getValue(src)); return 1; } else { const char *what = luaL_checkstring(L, 1); if (!strcmp(what, "altitude")) { lua_pushnumber(L, double(frskyData.hub.baroAltitude)/100); return 1; } else if (frskyData.hub.gpsFix) { if (!strcmp(what, "latitude")) { lua_pushnumber(L, gpsToDouble(frskyData.hub.gpsLatitudeNS=='S', frskyData.hub.gpsLatitude_bp, frskyData.hub.gpsLatitude_ap)); return 1; } else if (!strcmp(what, "longitude")) { lua_pushnumber(L, gpsToDouble(frskyData.hub.gpsLongitudeEW=='W', frskyData.hub.gpsLongitude_bp, frskyData.hub.gpsLongitude_ap)); return 1; } else if (!strcmp(what, "pilot latitude")) { lua_pushnumber(L, pilotLatitude); return 1; } else if (!strcmp(what, "pilot longitude")) { lua_pushnumber(L, pilotLongitude); return 1; } } } return 0; } static int luaPlayFile(lua_State *L) { const char * filename = luaL_checkstring(L, 1); PLAY_FILE(filename, 0, 0); return 0; } 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); lcd_line(x1, y1, x2, y2); 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 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 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 luaLcdDrawPixmap(lua_State *L) { int x = luaL_checkinteger(L, 1); int y = luaL_checkinteger(L, 2); const char * filename = luaL_checkstring(L, 3); // TODO int att = luaL_checkinteger(L, 4); bmp_ptr_t bitmap = 0; const pm_char * error = bmpLoad(bitmap, filename, LCD_W, LCD_H); if (bitmap && !error) { lcd_bmp(x, y, bitmap); // free(bitmap); } return 0; } static int luaLcdDrawScreenTitle(lua_State *L) { const char * str = luaL_checkstring(L, 1); int idx = luaL_checkinteger(L, 2); int cnt = luaL_checkinteger(L, 3); displayScreenIndex(idx-1, cnt, 0); lcd_filled_rect(0, 0, LCD_W, FH, SOLID, FILL_WHITE|GREY_DEFAULT); title(str); return 0; } static int luaModelGetTimer(lua_State *L) { int idx = luaL_checkunsigned(L, 1); if (idx < MAX_TIMERS) { TimerData & timer = g_model.timers[idx]; lua_newtable(L); lua_pushtablenumber(L, "mode", timer.mode); lua_pushtablenumber(L, "start", timer.start); lua_pushtablenumber(L, "value", timersStates[idx].val); lua_pushtablenumber(L, "countdownBeep", timer.countdownBeep); lua_pushtableboolean(L, "minuteBeep", timer.minuteBeep); lua_pushtableboolean(L, "persistent", timer.persistent); } else { lua_pushnil(L); } return 1; } static int luaModelSetTimer(lua_State *L) { int idx = luaL_checkunsigned(L, 1); if (idx < MAX_TIMERS) { TimerData & timer = g_model.timers[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, "mode")) { timer.mode = luaL_checkinteger(L, -1); } else if (!strcmp(key, "start")) { timer.start = luaL_checkinteger(L, -1); } else if (!strcmp(key, "value")) { timersStates[idx].val = luaL_checkinteger(L, -1); } else if (!strcmp(key, "countdownBeep")) { timer.countdownBeep = luaL_checkinteger(L, -1); } else if (!strcmp(key, "minuteBeep")) { timer.minuteBeep = luaL_checkinteger(L, -1); } else if (!strcmp(key, "persistent")) { timer.persistent = luaL_checkinteger(L, -1); } } eeDirty(EE_MODEL); } return 0; } static int getFirstInput(int chn) { 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_pushtablenumber(L, "source", expo->srcRaw); lua_pushtablenumber(L, "weight", expo->weight); lua_pushtablenumber(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) break; if (mix->destCh == chn) { return i; } } return -1; } 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 (first>=0 && idxname); lua_pushtablenumber(L, "source", mix->srcRaw); lua_pushtablenumber(L, "weight", mix->weight); lua_pushtablenumber(L, "offset", mix->offset); lua_pushtablenumber(L, "switch", mix->swtch); lua_pushtablenumber(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 (first>=0 && idxfunc); lua_pushtablenumber(L, "v1", sw->v1); lua_pushtablenumber(L, "v2", sw->v2); lua_pushtablenumber(L, "v3", sw->v3); lua_pushtablenumber(L, "and", sw->andsw); lua_pushtablenumber(L, "delay", sw->delay); lua_pushtablenumber(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 = cswAddress(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, "function")) { 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); } } } 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_pushtablenumber(L, "switch", CFN_SWITCH(cfn)); lua_pushtablenumber(L, "function", CFN_FUNC(cfn)); lua_pushtablezstring(L, "name", cfn->play.name); lua_pushtablenumber(L, "value", cfn->all.val); lua_pushtablenumber(L, "mode", cfn->all.mode); lua_pushtablenumber(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]; 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, "function")) { CFN_FUNC(cfn) = luaL_checkinteger(L, -1); } else if (!strcmp(key, "name")) { const char * name = luaL_checkstring(L, -1); str2zchar(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, "active")) { CFN_ACTIVE(cfn) = luaL_checkinteger(L, -1); } } } 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_pushtablenumber(L, "min", limit->min); lua_pushtablenumber(L, "max", limit->min); lua_pushtablenumber(L, "offset", limit->offset); lua_pushtablenumber(L, "ppmCenter", limit->ppmCenter); lua_pushtableboolean(L, "symetrical", limit->symetrical); lua_pushtableboolean(L, "revert", limit->revert); if (limit->curve) lua_pushtablenumber(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); } else if (!strcmp(key, "max")) { limit->max = luaL_checkinteger(L, -1); } 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; } } } 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; } int luaGetInputs(ScriptInternalData & sid) { if (!lua_istable(L, -1)) return -1; sid.inputsCount = 0; for (lua_pushnil(L); lua_next(L, -2) && sid.inputsCount100) { TRACE("Script killed"); standaloneScript.state = SCRIPT_KILLED; } else { TRACE("Script error"); standaloneScript.state = SCRIPT_SYNTAX_ERROR; } luaState = LUASTATE_RELOAD_MODEL_SCRIPTS; } else { int scriptResult = lua_tointeger(L, -1); lua_pop(L, 1); /* pop returned value */ if (scriptResult == 0) { if (lua_gc(L, LUA_GCCOUNT, 0) > SCRIPTS_MAX_HEAP) { TRACE("Script memory leak"); standaloneScript.state = SCRIPT_LEAK; luaState = LUASTATE_RELOAD_MODEL_SCRIPTS; } } else { TRACE("Script finished with status %d", scriptResult); standaloneScript.state = SCRIPT_NOFILE; luaState = LUASTATE_RELOAD_MODEL_SCRIPTS; } } } else { TRACE("Script error: %s", lua_tostring(L, -1)); standaloneScript.state = (instructionsPercent > 100 ? SCRIPT_KILLED : SCRIPT_SYNTAX_ERROR); luaState = LUASTATE_RELOAD_MODEL_SCRIPTS; } if (standaloneScript.state != SCRIPT_OK) { luaError(standaloneScript.state); } } } else { // model scripts if (luaState & LUASTATE_RELOAD_MODEL_SCRIPTS) { luaState = 0; LUA_RESET(); luaLoadModelScripts(); } for (int i=0; i 100 ? SCRIPT_KILLED : SCRIPT_SYNTAX_ERROR); TRACE("Script %10s disabled", sd.file); break; } sid.outputs[j].value = lua_tointeger(L, -1); lua_pop(L, 1); } } else { if (instructionsPercent > 100) { TRACE("Script %10s killed", sd.file); sid.state = SCRIPT_KILLED; } else { TRACE("Script %10s error: %s", sd.file, lua_tostring(L, -1)); sid.state = SCRIPT_SYNTAX_ERROR; } } if (sid.state != SCRIPT_OK) { luaFree(sid); } else { sid.memory += lua_gc(L, LUA_GCCOUNT, 0) - prev_mem; if (instructionsPercent > sid.instructions) { sid.instructions = instructionsPercent; } } } } // TRACE("gc=%d", lua_gc(L, LUA_GCCOUNT, 0)); if (lua_gc(L, LUA_GCCOUNT, 0) > SCRIPTS_MAX_HEAP) { uint8_t max_memory = 0; int8_t max_idx = -1; for (int i=0; i max_memory && sid.memory > 15) { max_idx = i; } } if (max_idx >= 0) { ScriptInternalData & sid = scriptInternalData[max_idx]; TRACE("Script %d killed", max_idx); sid.state = SCRIPT_LEAK; luaFree(sid); } } } }