├── .gitignore
├── LICENSE
├── README.md
├── package.json
├── timer.lua
├── timer_min.lua
└── timer_simplifier.lua
/.gitignore:
--------------------------------------------------------------------------------
1 | # Compiled Lua sources
2 | luac.out
3 |
4 | # luarocks build files
5 | *.src.rock
6 | *.zip
7 | *.tar.gz
8 |
9 | # Object files
10 | *.o
11 | *.os
12 | *.ko
13 | *.obj
14 | *.elf
15 |
16 | # Precompiled Headers
17 | *.gch
18 | *.pch
19 |
20 | # Libraries
21 | *.lib
22 | *.a
23 | *.la
24 | *.lo
25 | *.def
26 | *.exp
27 |
28 | # Shared objects (inc. Windows DLLs)
29 | *.dll
30 | *.so
31 | *.so.*
32 | *.dylib
33 |
34 | # Executables
35 | *.exe
36 | *.out
37 | *.app
38 | *.i*86
39 | *.x86_64
40 | *.hex
41 |
42 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 simen
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | nodemcu-timer
2 | ========================
3 | Current version: v1.0.0 (stable)
4 | Compatible Lua version: 5.1.x
5 |
6 |
7 | ## Table of Contents
8 |
9 | 1. [Overiew](#Overiew)
10 | 2. [Installation](#Installation)
11 | 3. [APIs](#APIs)
12 | 4. [Example: LED Blinks](#LED)
13 |
14 |
15 |
16 | ## 1. Overview
17 |
18 | The [**nodemcu**](https://github.com/nodemcu/nodemcu-firmware) `tmr` module has 7 timers (id=0~6) for you to schedule your things with `tmr.alarm()`. You would face a problem of managing callbacks with the specified timer ids to start or stop the scheduled alarms. This is the reason why **nodemcu-timer** comes out. With **nodemcu-timer**, you don't have to worry about whether a timer is available or not. It is a soft timer utility that allows you to schedule tasks with Javascript(/node.js) style APIs in your **nodemcu** project, i.e., `setTimeout()` and `clearTimeout()`.
19 |
20 | **nodemcu-timer** uses a single timer to simulate the behavior of `setTimeout()`, `setInterval()`, and `setImmediate()`. Only one task executes when a tick fires (excepts those scheduled by [`setImmediate()`](#API_setImmediate)), thus **nodemcu-timer** does not guarantee that the callback will fire at exact timing but as close as possilbe. When a callback is _setImmediate_, it will be executed at right next tick immediately even if there are other tasks being due at the same time. The internal timer will automatically start when a scheduled task enqueues, and automatically stopped when there is no task in queues. **nodemcu-timer** uses only a single timer (id=6 by default) internally, and you are free to use other 5 timers that `tmr` provides.
21 |
22 | If you like to code something in asynchronous style on **nodemcu**, might I sugguest [**lua-events**](https://github.com/simenkid/lua-events)? They are working well with each other to help arrange your asynchronous flow, e.g. your function can defer its callback and return right away.
23 |
24 | [**Note**]
25 | This module internally polls its task queues every 2ms. When you call `setTimeout()` and `setInterval()`, it is better to give the `delay` with an even number, e.g., `setTimeout(callback, 2000)` will fire the callback in 2 seconds. It is okay for `delay` to be odd, it will minus 1 to be even implicitly and will result in a timing error as short as 1ms. I think this small error is negligible if you are scheduling your task with an interval beyond few hundreds of ms.
26 | (I didn't use a tick of 1ms, because `tmr` is a bad ass when you give him a repeat interval of 1ms. If you do so, nodemcu will crash and I don't know why.)
27 |
28 |
29 | ## 2. Installation
30 |
31 | Clone from github
32 | > $ git clone https://github.com/simenkid/nodemcu-timer.git
33 |
34 | or use npm install
35 | > $ npm install nodemcu-timer
36 |
37 | Just include the file `timer.lua` or use the minified one `timer_min.lua` in your project.
38 | If you are with the **nodemcu** on ESP8266, it would be good for you to compile `*.lua` text file into `*.lc` bytecode to further lower memory usage.
39 |
40 | * FS/Memory footprint
41 |
42 | * timer.lua: ~3.9 KB(file size) / ~20 KB (heap available after loaded)
43 | * timer_min.lua: ~2.0 KB(size) / ~20 KB (heap)
44 | * timer.lc: ~3.2 KB(size) / ~25 KB (heap)
45 | * timer.lc + events.lc: ~17.7 KB (heap)
46 |
47 |
48 |
49 | ## 3. APIs
50 |
51 | * [timer.setTimeout()](#API_setTimeout)
52 | * [timer.clearTimeout()](#API_clearTimeout)
53 | * [timer.setInterval()](#API_setInterval)
54 | * [timer.clearInterval()](#API_clearInterval)
55 | * [timer.setImmediate()](#API_setImmediate)
56 | * [timer.clearImmediate()](#API_clearImmediate)
57 | * [timer.set()](#API_set)
58 |
59 | *************************************************
60 | ### timer utility
61 | Exposed by `require 'timer'`
62 |
63 | ```lua
64 | local timer = require 'timer' -- or 'timer_min'
65 | ```
66 |
67 |
68 |
69 |
70 | ### setTimeout(callback, delay[, ...])
71 | Schedules a one-time callback after `delay` ms. This API returns a timer-object(`tobj`) for possible use with clearTimeout(). You can also pass parameters to the callback via the variadic arguments.
72 |
73 |
74 | **Arguments:**
75 |
76 | 1. `callback` (_function_): The function to be scheduled.
77 | 2. `delay` (_number_): Time in milisecond, an even number would be better.
78 | 3. `...` (_variadic arguments_): The arguments pass to your callback.
79 |
80 | **Returns:**
81 |
82 | * (_object_) Timer-object.
83 |
84 | **Examples:**
85 |
86 | ```lua
87 | -- fires after 10 seconds
88 | local tobj = timer.setTimeout(function ()
89 | print('I am fired')
90 | end, 10000)
91 |
92 | ```
93 |
94 | ********************************************
95 |
96 |
97 | ### clearTimeout(tobj)
98 | Removes the timeout timer-object from triggering.
99 |
100 | **Arguments:**
101 |
102 | 1. `tobj` (_object_): Timer-object to remove.
103 |
104 | **Returns:**
105 |
106 | * (_none_) nil
107 |
108 | **Examples:**
109 |
110 | ```lua
111 | local greet = function ()
112 | print('Bonjour!')
113 | end
114 |
115 | local tmout = timer.setTimeout(greet, 5000)
116 |
117 | timer.clearTimeout(tmout)
118 | ```
119 |
120 | ********************************************
121 |
122 |
123 | ### setInterval(callback, delay, ...)
124 | Schedules a callback to be executed every `delay` ms. This API returns a timer-object(`tobj`) for possible use with clearInterval(). You can also pass parameters to the callback via the variadic arguments.
125 |
126 | If `delay` is larger than 2147483647 ms (~25 days) or less than 2, **nodemcu-timer** will use 2 as the `delay`.
127 |
128 | **Arguments:**
129 |
130 | 1. `callback` (_function_): The function to be scheduled.
131 | 2. `delay` (_number_): Time in milisecond, an even number would be better.
132 | 3. `...` (_variadic arguments_): The arguments pass to your callback.
133 |
134 | **Returns:**
135 |
136 | * (_object_) Timer-object.
137 |
138 | **Examples:**
139 |
140 | ```lua
141 | local tobj = timer.setInterval(function ()
142 | print('Hello')
143 | end, 2000)
144 | ```
145 |
146 | ********************************************
147 |
148 |
149 | ### clearInterval(tobj)
150 | Removes a time-object of interval from repeatly triggering.
151 |
152 | **Arguments:**
153 |
154 | 1. `tobj` (_object_): Timed-object to remove.
155 |
156 | **Returns:**
157 |
158 | * (_none_) nil
159 |
160 | **Examples:**
161 |
162 | ```lua
163 | local count = 0
164 | local repeater = timer.setInterval(function ()
165 | count = count + 1
166 | print(count ' times triggered!')
167 | end, 1000)
168 |
169 | timer.clearInterval(repeater)
170 | ```
171 |
172 | Be careful if you are trying to cancel the scheduled task inside itself. The following example won't work!
173 |
174 | ```lua
175 | local count = 0
176 | local repeater = timer.setInterval(function ()
177 | count = count + 1
178 | if (count == 5) then
179 | -- This will not work.
180 | timer.clearInterval(repeater)
181 | end
182 | end, 1000)
183 | ```
184 |
185 | This is because the `repeater` variable is not referencing properly. It is a problem of Lua. All you have to do is to decalre your local variable first, and then assign something to it. Here is an example:
186 |
187 | ```lua
188 | local count = 0
189 | local repeater -- declare first
190 | -- and then assign
191 | repeater = timer.setInterval(function ()
192 | count = count + 1
193 | if (count == 5) then
194 | -- This will work.
195 | timer.clearInterval(repeater)
196 | end
197 | end, 1000)
198 | ```
199 |
200 | ********************************************
201 |
202 |
203 | ### setImmediate(callback, ...)
204 | Schedules a callback to be immediately executed at next tick, its priority is higher than those tasks set by `setTimeout()` and `setInterval()`. This API returns a timer-object(`tobj`) for possible use with clearImmediate(). You can also pass parameters to the callback via the variadic arguments.
205 |
206 | The callbacks for immediate execution enqueues in the order in which they were created. All immediate callbacks in the queue will be invoked right away when a tick fires. If you queue an immediate callback from inside an executing callback, that immediate callback won't be invoked until the next tick comes. Remember that do not schedule a long task for immediate execution as possible.
207 |
208 |
209 | **Arguments:**
210 |
211 | 1. `callback` (_function_): The function to be scheduled.
212 | 2. `...` (_variadic arguments_): The arguments pass to your callback.
213 |
214 | **Returns:**
215 |
216 | * (_object_) Timer-object.
217 |
218 | **Examples:**
219 |
220 | ```lua
221 | local tobj = timer.setImmediate(function ()
222 | print(gpio.read(0))
223 | end)
224 | ```
225 |
226 | ********************************************
227 |
228 |
229 | ### clearImmediate(tobj)
230 | Removes an immediate time-object from triggering.
231 |
232 | **Arguments:**
233 |
234 | 1. `tobj` (_object_): Timed-object to remove.
235 |
236 | **Returns:**
237 |
238 | * (_none_) nil
239 |
240 | **Examples:**
241 |
242 | ```lua
243 | local tobj = timer.setImmediate(function ()
244 | print(gpio.read(0))
245 | end)
246 |
247 | timer.clearImmediate(tobj)
248 | ```
249 |
250 |
251 | ********************************************
252 |
253 |
254 | ### set(tid)
255 | Change the internal timer accroding to the specified timer id (default is 6). Be aware of that all callbacks in queue will be cleared when you change to a new timer. It is suggested to use this function within your code of initialization only.
256 |
257 | You can use the property `timer.id` to know which timer is used as an internal one.
258 |
259 | **Arguments:**
260 |
261 | 1. `tid` (_number_): Id of the timer to use. If it is the same with the current id, nothing will happen.
262 |
263 | **Returns:**
264 |
265 | * (_none_) nil
266 |
267 | **Examples:**
268 |
269 | ```lua
270 | print(timer.id) -- 6
271 |
272 | timer.set(2)
273 | print(timer.id) -- 2
274 | ```
275 |
276 |
277 |
278 | ## 4. Example: LED Blinks
279 |
280 | * The first old-fashioned example is to repeatly turn on an LED for 1 second and turn if off for another second. (my LED is configured with active-low to gpio)
281 |
282 | ```lua
283 | local timer = require 'timer'
284 |
285 | local LED_PIN1 = 0
286 | gpio.mode(LED_PIN1, gpio.OUTPUT)
287 |
288 | local sw1 = true
289 | timer.setInterval(function ()
290 | if (sw1) then
291 | gpio.write(LED_PIN1, gpio.LOW)
292 | else
293 | gpio.write(LED_PIN1, gpio.HIGH)
294 | end
295 | sw1 = not sw1
296 | end, 1000)
297 | ```
298 |
299 | * The second one shows a generic blinkLED() function used to blink LEDs. It's reentrant and you don't have to worry about which timer is available. Instead, what you have to do is to manage a time-object(a scheduled task) and not the timer itself.
300 | This example drives three LEDs that blink with different rate and repeat with different times. Each of them is triggered at 1234ms, 3528ms, and 5104ms after scheduled. Try to schedule these simple blinking things with `tmr`, and you may find that it is really a pain in the ass. This is why I made **nodemcu-timer** to shcedule things - to fix my own ass.
301 |
302 | ```lua
303 | local timer = require 'timer'
304 |
305 | local LED_PIN1, LED_PIN2, LED_PIN3 = 0, 1, 2
306 |
307 | gpio.mode(LED_PIN1, gpio.OUTPUT)
308 | gpio.mode(LED_PIN2, gpio.OUTPUT)
309 | gpio.mode(LED_PIN3, gpio.OUTPUT)
310 |
311 | function blinkLED(led, times, interval)
312 | local sw, count, tobj = true, 0
313 |
314 | tobj = timer.setInterval(function ()
315 | if (sw) then
316 | gpio.write(led, gpio.LOW)
317 | else
318 | gpio.write(led, gpio.HIGH)
319 | count = count + 1
320 | end
321 | sw = not sw
322 |
323 | if (count == times) then
324 | timer.clearInterval(tobj)
325 | gpio.write(led, gpio.HIGH)
326 | end
327 | end, interval)
328 | end
329 |
330 | timer.setTimeout(function ()
331 | blinkLED(LED_PIN1, 5, 560)
332 | end, 1234)
333 |
334 | timer.setTimeout(function ()
335 | blinkLED(LED_PIN2, 3, 1024)
336 | end, 3528)
337 |
338 | timer.setTimeout(function ()
339 | blinkLED(LED_PIN3, 10, 200)
340 | end, 5104)
341 | ```
342 |
343 |
344 |
345 |
346 | ********************************************
347 |
348 | ## License
349 | MIT
350 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "nodemcu-timer",
3 | "version": "1.0.1",
4 | "description": "A soft timer utility that allows you to schedule tasks with Javascript(/node.js) style APIs in your NodeMcu project on ESP8266",
5 | "main": "timer.lua",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "repository": {
10 | "type": "git",
11 | "url": "git+https://github.com/simenkid/nodemcu-timer.git"
12 | },
13 | "keywords": [
14 | "timer",
15 | "nodemcu",
16 | "esp8266"
17 | ],
18 | "author": "Simen Li",
19 | "license": "MIT",
20 | "bugs": {
21 | "url": "https://github.com/simenkid/nodemcu-timer/issues"
22 | },
23 | "homepage": "https://github.com/simenkid/nodemcu-timer#readme"
24 | }
25 |
--------------------------------------------------------------------------------
/timer.lua:
--------------------------------------------------------------------------------
1 | ------------------------------------------------------------------------------
2 | -- Timer Utility in Node.js Style
3 | -- LICENSE: MIT
4 | -- Simen Li
5 | ------------------------------------------------------------------------------
6 | local timer = {}
7 | local _exequeImmed, exequeImmed, exeque, ttbl = {}, {}, {}, {}
8 | local tick, lock = 2, false
9 |
10 | timer.enable = false
11 | timer.id = 6
12 |
13 | local function rmEntry(tbl, pred)
14 | if (pred == nil) then return end
15 | local x, len = 0, #tbl
16 | for i = 1, len do
17 | local trusy, idx = false, (i - x)
18 | if (type(pred) == 'function') then trusy = pred(tbl[idx])
19 | else trusy = tbl[idx] == pred
20 | end
21 |
22 | if (tbl[idx] ~= nil and trusy) then
23 | tbl[idx] = nil
24 | table.remove(tbl, idx)
25 | x = x + 1
26 | end
27 | end
28 | return tbl
29 | end
30 |
31 | local function checkloops()
32 | if (lock) then return else lock = true end
33 | local tobj
34 | for i, tob in ipairs(ttbl) do
35 | tob.delay = tob.delay - tick
36 | if (tob.delay <= tick) then
37 | table.insert(exeque, tob)
38 | ttbl = rmEntry(ttbl, tob)
39 | end
40 | end
41 |
42 | for i = 1, #_exequeImmed do table.insert(exequeImmed, _exequeImmed[i]) end
43 | rmEntry(_exequeImmed, function (v) return v ~= nil end)
44 |
45 | if (#exequeImmed > 0) then -- Immediately execute all targets
46 | for i, immed in ipairs(exequeImmed) do
47 | local status, err = pcall(immed.f, unpack(immed.argus))
48 | if not (status) then print("Task execution fails: " .. tostring(err)) end
49 | end
50 | rmEntry(exequeImmed, function (v) return v ~= nil end)
51 | elseif (#exeque > 0) then
52 | tobj = exeque[1]
53 | table.remove(exeque, 1)
54 | elseif (#ttbl == 0) then
55 | tmr.stop(timer.id)
56 | timer.enable = false
57 | end
58 |
59 | if (tobj ~= nil) then -- Re-insert the repeatable tobj to table
60 | if (tobj.rp > 0) then
61 | tobj.delay = tobj.rp
62 | if (tobj.delay <= tick) then table.insert(exeque, tobj)
63 | else table.insert(ttbl, tobj)
64 | end
65 | end
66 | local status, err = pcall(tobj.f, unpack(tobj.argus))
67 | if not (status) then print("Task execution fails: " .. tostring(err)) end
68 | end
69 | lock = false
70 | end
71 |
72 | function timer.start()
73 | tmr.alarm(timer.id, 2, 1, checkloops) -- tid = 6, intvl = 2ms, repeat = 1
74 | timer.enable = true
75 | end
76 |
77 | function timer.stop()
78 | tmr.stop(timer.id)
79 | timer.enable = false
80 | _exequeImmed = rmEntry(_exequeImmed, function (v) return v ~= nil end)
81 | exequeImmed = rmEntry(exequeImmed, function (v) return v ~= nil end)
82 | exeque = rmEntry(exeque, function (v) return v ~= nil end)
83 | ttbl = rmEntry(ttbl, function (v) return v ~= nil end)
84 | end
85 |
86 | function timer.set(tid)
87 | if (tid ~= timer.id) then
88 | timer.stop()
89 | timer.id = tid
90 | timer.start()
91 | end
92 | end
93 |
94 | function timer.setImmediate(fn, ...)
95 | local tobj = { delay = 0, f = fn, rp = 0, argus = {...} }
96 | table.insert(_exequeImmed, tobj)
97 | if (timer.enable == false) then timer.start() end
98 | return tobj
99 | end
100 |
101 | function timer.setTimeout(fn, delay, ...)
102 | local tobj = { delay = delay, f = fn, rp = 0, argus = {...} }
103 | if (delay <= tick or delay > 2147483646) then
104 | tobj.delay = tick
105 | table.insert(exeque, tobj)
106 | else
107 | table.insert(ttbl, tobj)
108 | end
109 | if (timer.enable == false) then timer.start() end
110 | return tobj
111 | end
112 |
113 | function timer.setInterval(fn, delay, ...)
114 | local tobj = timer.setTimeout(fn, delay, ...)
115 | tobj.rp = delay
116 | if (timer.enable == false) then timer.start() end
117 | return tobj
118 | end
119 |
120 | function timer.clearImmediate(tobj)
121 | _exequeImmed = rmEntry(_exequeImmed, tobj)
122 | exequeImmed = rmEntry(exequeImmed, tobj)
123 | end
124 |
125 | function timer.clearTimeout(tobj)
126 | rmEntry(exeque, tobj)
127 | rmEntry(ttbl, tobj)
128 | end
129 |
130 | timer.clearInterval = timer.clearTimeout
131 |
132 | return timer
133 |
--------------------------------------------------------------------------------
/timer_min.lua:
--------------------------------------------------------------------------------
1 | local timer={}local a,b,c,d={},{},{},{}local e,g=2,false;timer.enable=false;timer.id=6;
2 | local function h(i,j)local k,l=0,#i;for m=1,l do local n,o=false,m-k;if type(j)=='function'then n=j(i[o])else n=i[o]==j end;if i[o]~=nil and n then i[o]=nil;table.remove(i,o)k=k+1 end end;return i end;
3 | local function p()if g then return else g=true end;local q;for m,r in ipairs(d)do r.delay=r.delay-e;if r.delay<=e then table.insert(c,r)d=h(d,r)end end;
4 | for m=1,#a do table.insert(b,a[m])end;h(a,function(s)return s~=nil end)if#b>0 then for m,t in ipairs(b)do local u,v=pcall(t.f,unpack(t.args))if not u then print("Task execution fails: "..tostring(v))end end;
5 | h(b,function(s)return s~=nil end)elseif#c>0 then q=c[1]table.remove(c,1)elseif#d==0 then tmr.stop(timer.id)timer.enable=false end;
6 | if q~=nil then if q.rp>0 then q.delay=q.rp;if q.delay<=e then table.insert(c,q)else table.insert(d,q)end end;local u,v=pcall(q.f,unpack(q.args))if not u then print("Task execution fails: "..tostring(v))end end;
7 | g=false end;function timer.start()tmr.alarm(timer.id,2,1,p)timer.enable=true end;function timer.stop()tmr.stop(timer.id)timer.enable=false;
8 | a=h(a,function(s)return s~=nil end)b=h(b,function(s)return s~=nil end)c=h(c,function(s)return s~=nil end)d=h(d,function(s)return s~=nil end)end;
9 | function timer.set(w)if w~=timer.id then timer.stop()timer.id=w;timer.start()end end;function timer.setImmediate(x,...)local q={delay=0,f=x,rp=0,args={...}}table.insert(a,q)if timer.enable==false then timer.start()end;
10 | return q end;function timer.setTimeout(x,delay,...)local q={delay=delay,f=x,rp=0,args={...}}if delay<=e or delay>2147483646 then q.delay=e;table.insert(c,q)else table.insert(d,q)end;
11 | if timer.enable==false then timer.start()end;return q end;function timer.setInterval(x,delay,...)local q=timer.setTimeout(x,delay,...)q.rp=delay;if timer.enable==false then timer.start()end;return q end;
12 | function timer.clearImmediate(q)a=h(a,q)b=h(b,q)end;function timer.clearTimeout(q)h(c,q)h(d,q)end;timer.clearInterval=timer.clearTimeout;return timer
--------------------------------------------------------------------------------
/timer_simplifier.lua:
--------------------------------------------------------------------------------
1 | ------------------------------------------------------------------------------
2 | -- Timer Utility in Node.js Style
3 | -- LICENSE: MIT
4 | -- Simen Li
5 | ------------------------------------------------------------------------------
6 | local tm, lock, ttbl, exec = { id = 6, enable = false, tick = 1000 }, false, {}, {}
7 |
8 | local function rm(tbl, pred)
9 | if (pred == nil) then return tbl end
10 | local x, len = 0, #tbl
11 | for i = 1, len do
12 | local trusy, idx = false, (i - x)
13 | if (type(pred) == 'function') then trusy = pred(tbl[idx])
14 | else trusy = tbl[idx] == pred
15 | end
16 |
17 | if (trusy) then
18 | table.remove(tbl, idx)
19 | x = x + 1
20 | end
21 | end
22 | return tbl
23 | end
24 |
25 | local function chk()
26 | if (lock) then return else lock = true end
27 | if (#ttbl == 0) then tm.stop() return end
28 |
29 | for i, tob in ipairs(ttbl) do
30 | tob.delay = tob.delay - 1
31 | if (tob.delay == 0) then
32 | if (tob.rp > 0) then tob.delay = tob.rp end
33 | table.insert(exec, tob)
34 | end
35 | end
36 |
37 | for ii, tt in ipairs(exec) do
38 | rm(ttbl, tt)
39 | local status, err = pcall(tt.f, unpack(tt.argus))
40 | if not (status) then print("Task execution fails: " .. tostring(err)) end
41 | if (tt.delay > 0) then table.insert(ttbl, tt) end
42 | exec[ii] = nil
43 | end
44 | lock = false
45 | end
46 |
47 | function tm.start()
48 | tmr.alarm(tm.id, tm.tick, 1, chk) -- tid = 6, intvl = 2ms, repeat = 1
49 | tm.enable = true
50 | end
51 |
52 | function tm.stop()
53 | tmr.stop(tm.id)
54 | tm.enable = false
55 | ttbl = rm(ttbl, function (v) return v ~= nil end)
56 | lock = false
57 | end
58 |
59 | function tm.set(tid)
60 | if (tid ~= tm.id) then
61 | tm.stop()
62 | tm.id = tid
63 | tm.start()
64 | end
65 | end
66 |
67 | function tm.setTimeout(fn, delay, ...)
68 | local tobj = { delay = delay, f = fn, rp = 0, argus = {...} }
69 | if (delay < 2) then tobj.delay = 1 end
70 |
71 | table.insert(ttbl, tobj)
72 | if (not tm.enable) then tm.start() end
73 | return tobj
74 | end
75 |
76 | function tm.setInterval(fn, delay, ...)
77 | local tobj = tm.setTimeout(fn, delay, ...)
78 | tobj.rp = tobj.delay
79 | return tobj
80 | end
81 |
82 | function tm.clear(tobj)
83 | tobj.rp = 0
84 | rm(exec, tobj)
85 | rm(ttbl, tobj)
86 | end
87 |
88 | return tm
89 |
--------------------------------------------------------------------------------