From f1afe7b6ab2ab2b385f28fda80a16a7c97ac8994 Mon Sep 17 00:00:00 2001 From: Yury Popov Date: Tue, 2 Aug 2016 23:35:53 +0300 Subject: [PATCH] Add support for dynamic timers, deprecating the static ones (#1424) --- app/modules/tmr.c | 133 ++++++++++++++++++++++++++++++----------- docs/en/modules/tmr.md | 72 +++++++++++++++++----- 2 files changed, 155 insertions(+), 50 deletions(-) diff --git a/app/modules/tmr.c b/app/modules/tmr.c index 142ecc7c..b85ec80f 100755 --- a/app/modules/tmr.c +++ b/app/modules/tmr.c @@ -71,7 +71,7 @@ static const char* MAX_TIMEOUT_ERR_STR = "Range: 1-"STRINGIFY(MAX_TIMEOUT_DEF); typedef struct{ os_timer_t os; - sint32_t lua_ref; + sint32_t lua_ref, self_ref; uint32_t interval; uint8_t mode; }timer_struct_t; @@ -95,11 +95,17 @@ static timer_struct_t alarm_timers[NUM_TMR]; static os_timer_t rtc_timer; static void alarm_timer_common(void* arg){ - timer_t tmr = &alarm_timers[(uint32_t)arg]; + timer_t tmr = (timer_t)arg; lua_State* L = lua_getstate(); if(tmr->lua_ref == LUA_NOREF) return; lua_rawgeti(L, LUA_REGISTRYINDEX, tmr->lua_ref); + if (tmr->self_ref == LUA_REFNIL) { + uint32_t id = tmr - alarm_timers; + lua_pushinteger(L, id); + } else { + lua_rawgeti(L, LUA_REGISTRYINDEX, tmr->self_ref); + } //if the timer was set to single run we clean up after it if(tmr->mode == TIMER_MODE_SINGLE){ luaL_unref(L, LUA_REGISTRYINDEX, tmr->lua_ref); @@ -108,7 +114,11 @@ static void alarm_timer_common(void* arg){ }else if(tmr->mode == TIMER_MODE_SEMI){ tmr->mode |= TIMER_IDLE_FLAG; } - lua_call(L, 0, 0); + if (tmr->mode != TIMER_MODE_AUTO && tmr->self_ref != LUA_REFNIL) { + luaL_unref(L, LUA_REGISTRYINDEX, tmr->self_ref); + tmr->self_ref = LUA_NOREF; + } + lua_call(L, 1, 0); } // Lua: tmr.delay( us ) @@ -135,20 +145,32 @@ static int tmr_now(lua_State* L){ return 1; } -// Lua: tmr.register( id, interval, mode, function ) +static timer_t tmr_get( lua_State *L, int stack ) { + // Deprecated: static 0-6 timers control by index. + luaL_argcheck(L, (lua_isuserdata(L, stack) || lua_isnumber(L, stack)), 1, "timer object or numerical ID expected"); + if (lua_isuserdata(L, stack)) { + return (timer_t)luaL_checkudata(L, stack, "tmr.timer"); + } else { + uint32_t id = luaL_checkinteger(L, 1); + luaL_argcheck(L, platform_tmr_exists(id), 1, "invalid timer index"); + return &alarm_timers[id]; + } + return 0; +} + +// Lua: tmr.register( id / ref, interval, mode, function ) static int tmr_register(lua_State* L){ - uint32_t id = luaL_checkinteger(L, 1); + timer_t tmr = tmr_get(L, 1); + uint32_t interval = luaL_checkinteger(L, 2); uint8_t mode = luaL_checkinteger(L, 3); - //Check if provided parameters are valid - MOD_CHECK_ID(tmr, id); + luaL_argcheck(L, (interval > 0 && interval <= MAX_TIMEOUT), 2, MAX_TIMEOUT_ERR_STR); - luaL_argcheck(L, (mode == TIMER_MODE_SINGLE || mode == TIMER_MODE_SEMI || mode == TIMER_MODE_AUTO), 3, "Invalid mode"); - luaL_argcheck(L, (lua_type(L, 4) == LUA_TFUNCTION || lua_type(L, 4) == LUA_TLIGHTFUNCTION), 4, "Must be function"); + luaL_argcheck(L, (mode == TIMER_MODE_SINGLE || mode == TIMER_MODE_SEMI || mode == TIMER_MODE_AUTO), 3, "Invalid mode"); + luaL_argcheck(L, (lua_type(L, 4) == LUA_TFUNCTION || lua_type(L, 4) == LUA_TLIGHTFUNCTION), 4, "Must be function"); //get the lua function reference lua_pushvalue(L, 4); sint32_t ref = luaL_ref(L, LUA_REGISTRYINDEX); - timer_t tmr = &alarm_timers[id]; if(!(tmr->mode & TIMER_IDLE_FLAG) && tmr->mode != TIMER_MODE_OFF) ets_timer_disarm(&tmr->os); //there was a bug in this part, the second part of the following condition was missing @@ -157,15 +179,19 @@ static int tmr_register(lua_State* L){ tmr->lua_ref = ref; tmr->mode = mode|TIMER_IDLE_FLAG; tmr->interval = interval; - ets_timer_setfn(&tmr->os, alarm_timer_common, (void*)id); + ets_timer_setfn(&tmr->os, alarm_timer_common, tmr); return 0; } -// Lua: tmr.start( id ) +// Lua: tmr.start( id / ref ) static int tmr_start(lua_State* L){ - uint8_t id = luaL_checkinteger(L, 1); - MOD_CHECK_ID(tmr,id); - timer_t tmr = &alarm_timers[id]; + timer_t tmr = tmr_get(L, 1); + + if (tmr->self_ref == LUA_NOREF) { + lua_pushvalue(L, 1); + tmr->self_ref = luaL_ref(L, LUA_REGISTRYINDEX); + } + //we return false if the timer is not idle if(!(tmr->mode&TIMER_IDLE_FLAG)){ lua_pushboolean(L, 0); @@ -177,17 +203,21 @@ static int tmr_start(lua_State* L){ return 1; } -// Lua: tmr.alarm( id, interval, repeat, function ) +// Lua: tmr.alarm( id / ref, interval, repeat, function ) static int tmr_alarm(lua_State* L){ tmr_register(L); return tmr_start(L); } -// Lua: tmr.stop( id ) +// Lua: tmr.stop( id / ref ) static int tmr_stop(lua_State* L){ - uint8_t id = luaL_checkinteger(L, 1); - MOD_CHECK_ID(tmr,id); - timer_t tmr = &alarm_timers[id]; + timer_t tmr = tmr_get(L, 1); + + if (tmr->self_ref != LUA_REFNIL) { + luaL_unref(L, LUA_REGISTRYINDEX, tmr->self_ref); + tmr->self_ref = LUA_NOREF; + } + //we return false if the timer is idle (of not registered) if(!(tmr->mode & TIMER_IDLE_FLAG) && tmr->mode != TIMER_MODE_OFF){ tmr->mode |= TIMER_IDLE_FLAG; @@ -199,11 +229,15 @@ static int tmr_stop(lua_State* L){ return 1; } -// Lua: tmr.unregister( id ) +// Lua: tmr.unregister( id / ref ) static int tmr_unregister(lua_State* L){ - uint8_t id = luaL_checkinteger(L, 1); - MOD_CHECK_ID(tmr,id); - timer_t tmr = &alarm_timers[id]; + timer_t tmr = tmr_get(L, 1); + + if (tmr->self_ref != LUA_REFNIL) { + luaL_unref(L, LUA_REGISTRYINDEX, tmr->self_ref); + tmr->self_ref = LUA_NOREF; + } + if(!(tmr->mode & TIMER_IDLE_FLAG) && tmr->mode != TIMER_MODE_OFF) ets_timer_disarm(&tmr->os); if(tmr->lua_ref != LUA_NOREF) @@ -213,13 +247,12 @@ static int tmr_unregister(lua_State* L){ return 0; } -// Lua: tmr.interval( id, interval ) +// Lua: tmr.interval( id / ref, interval ) static int tmr_interval(lua_State* L){ - uint8_t id = luaL_checkinteger(L, 1); - MOD_CHECK_ID(tmr,id); - timer_t tmr = &alarm_timers[id]; + timer_t tmr = tmr_get(L, 1); + uint32_t interval = luaL_checkinteger(L, 2); - luaL_argcheck(L, (interval > 0 && interval <= MAX_TIMEOUT), 2, MAX_TIMEOUT_ERR_STR); + luaL_argcheck(L, (interval > 0 && interval <= MAX_TIMEOUT), 2, MAX_TIMEOUT_ERR_STR); if(tmr->mode != TIMER_MODE_OFF){ tmr->interval = interval; if(!(tmr->mode&TIMER_IDLE_FLAG)){ @@ -230,11 +263,10 @@ static int tmr_interval(lua_State* L){ return 0; } -// Lua: tmr.state( id ) +// Lua: tmr.state( id / ref ) static int tmr_state(lua_State* L){ - uint8_t id = luaL_checkinteger(L, 1); - MOD_CHECK_ID(tmr,id); - timer_t tmr = &alarm_timers[id]; + timer_t tmr = tmr_get(L, 1); + if(tmr->mode == TIMER_MODE_OFF){ lua_pushnil(L); return 1; @@ -307,8 +339,34 @@ static int tmr_softwd( lua_State* L ){ return 0; } +// Lua: tmr.create() +static int tmr_create( lua_State *L ) { + timer_t ud = (timer_t)lua_newuserdata(L, sizeof(timer_struct_t)); + if (!ud) return luaL_error(L, "not enough memory"); + luaL_getmetatable(L, "tmr.timer"); + lua_setmetatable(L, -2); + ud->lua_ref = LUA_NOREF; + ud->self_ref = LUA_NOREF; + ud->mode = TIMER_MODE_OFF; + ets_timer_disarm(&ud->os); + return 1; +} + // Module function map +static const LUA_REG_TYPE tmr_dyn_map[] = { + { LSTRKEY( "register" ), LFUNCVAL( tmr_register ) }, + { LSTRKEY( "alarm" ), LFUNCVAL( tmr_alarm ) }, + { LSTRKEY( "start" ), LFUNCVAL( tmr_start ) }, + { LSTRKEY( "stop" ), LFUNCVAL( tmr_stop ) }, + { LSTRKEY( "unregister" ), LFUNCVAL( tmr_unregister ) }, + { LSTRKEY( "state" ), LFUNCVAL( tmr_state ) }, + { LSTRKEY( "interval" ), LFUNCVAL( tmr_interval) }, + { LSTRKEY( "__gc" ), LFUNCVAL( tmr_unregister ) }, + { LSTRKEY( "__index" ), LROVAL( tmr_dyn_map ) }, + { LNILKEY, LNILVAL } +}; + static const LUA_REG_TYPE tmr_map[] = { { LSTRKEY( "delay" ), LFUNCVAL( tmr_delay ) }, { LSTRKEY( "now" ), LFUNCVAL( tmr_now ) }, @@ -321,7 +379,8 @@ static const LUA_REG_TYPE tmr_map[] = { { LSTRKEY( "stop" ), LFUNCVAL( tmr_stop ) }, { LSTRKEY( "unregister" ), LFUNCVAL( tmr_unregister ) }, { LSTRKEY( "state" ), LFUNCVAL( tmr_state ) }, - { LSTRKEY( "interval" ), LFUNCVAL( tmr_interval) }, + { LSTRKEY( "interval" ), LFUNCVAL( tmr_interval ) }, + { LSTRKEY( "create" ), LFUNCVAL( tmr_create ) }, { LSTRKEY( "ALARM_SINGLE" ), LNUMVAL( TIMER_MODE_SINGLE ) }, { LSTRKEY( "ALARM_SEMI" ), LNUMVAL( TIMER_MODE_SEMI ) }, { LSTRKEY( "ALARM_AUTO" ), LNUMVAL( TIMER_MODE_AUTO ) }, @@ -330,8 +389,12 @@ static const LUA_REG_TYPE tmr_map[] = { int luaopen_tmr( lua_State *L ){ int i; + + luaL_rometatable(L, "tmr.timer", (void *)tmr_dyn_map); + for(i=0; i