sys.lua 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606
  1. --- 模块功能:Luat协程调度框架
  2. --[[
  3. @module sys
  4. @summary LuaTask核心逻辑
  5. @version 1.0
  6. @date 2018.01.01
  7. @author 稀饭/wendal/晨旭
  8. @usage
  9. -- sys一般会内嵌到固件内, 不需要手工添加到脚本列表,除非你正在修改sys.lua
  10. -- 本文件修改后, 需要调用 update_lib_inline 对 vfs 中的C文件进行更新
  11. _G.sys = require("sys")
  12. sys.taskInit(function()
  13. sys.wait(1000)
  14. log.info("sys", "say hi")
  15. end)
  16. sys.run()
  17. ]]
  18. local sys = {}
  19. local table = _G.table
  20. local unpack = table.unpack
  21. local rtos = _G.rtos
  22. local coroutine = _G.coroutine
  23. local log = _G.log
  24. -- lib脚本版本号,只要lib中的任何一个脚本做了修改,都需要更新此版本号
  25. SCRIPT_LIB_VER = "2.3.2"
  26. -- TaskID最大值
  27. local TASK_TIMER_ID_MAX = 0x1FFFFF
  28. -- msgId 最大值(请勿修改否则会发生msgId碰撞的危险)
  29. local MSG_TIMER_ID_MAX = 0x7FFFFF
  30. -- 任务定时器id
  31. local taskTimerId = 0
  32. -- 消息定时器id
  33. local msgId = TASK_TIMER_ID_MAX
  34. -- 定时器id表
  35. local timerPool = {}
  36. --消息定时器参数表
  37. local para = {}
  38. --定时器是否循环表
  39. --local loop = {}
  40. --lua脚本运行出错时,是否回退为本地烧写的版本
  41. --local sRollBack = true
  42. _G.COROUTINE_ERROR_ROLL_BACK = true
  43. _G.COROUTINE_ERROR_RESTART = true
  44. -- 对coroutine.resume加一个修饰器用于捕获协程错误
  45. --local rawcoresume = coroutine.resume
  46. local function wrapper(co,...)
  47. local arg = {...}
  48. if not arg[1] then
  49. local traceBack = debug.traceback(co)
  50. traceBack = (traceBack and traceBack~="") and (arg[2].."\r\n"..traceBack) or arg[2]
  51. log.error("coroutine.resume",traceBack)
  52. --if errDump and type(errDump.appendErr)=="function" then
  53. -- errDump.appendErr(traceBack)
  54. --end
  55. if _G.COROUTINE_ERROR_ROLL_BACK then
  56. sys.timerStart(assert,500,false,traceBack)
  57. elseif _G.COROUTINE_ERROR_RESTART then
  58. rtos.reboot()
  59. end
  60. end
  61. return ...
  62. end
  63. sys.coresume = function(...)
  64. local arg = {...}
  65. return wrapper(arg[1], coroutine.resume(...))
  66. end
  67. function sys.check_task()
  68. local co, ismain = coroutine.running()
  69. if ismain then
  70. error(debug.traceback("attempt to yield from outside a coroutine"))
  71. end
  72. return co
  73. end
  74. --- Task任务延时函数,只能用于任务函数中
  75. -- @number ms 整数,最大等待126322567毫秒
  76. -- @return 定时结束返回nil,被其他线程唤起返回调用线程传入的参数
  77. -- @usage sys.wait(30)
  78. function sys.wait(ms)
  79. -- 参数检测,参数不能为负值
  80. --assert(ms > 0, "The wait time cannot be negative!")
  81. -- 选一个未使用的定时器ID给该任务线程
  82. local co = sys.check_task()
  83. while true do
  84. if taskTimerId >= TASK_TIMER_ID_MAX - 1 then
  85. taskTimerId = 0
  86. else
  87. taskTimerId = taskTimerId + 1
  88. end
  89. if timerPool[taskTimerId] == nil then
  90. break
  91. end
  92. end
  93. local timerid = taskTimerId
  94. timerPool[timerid] = co
  95. -- 调用core的rtos定时器
  96. if 1 ~= rtos.timer_start(timerid, ms) then log.debug("rtos.timer_start error") return end
  97. -- 挂起调用的任务线程
  98. local message = {coroutine.yield()}
  99. if #message ~= 0 then
  100. rtos.timer_stop(timerid)
  101. timerPool[timerid] = nil
  102. return unpack(message)
  103. end
  104. end
  105. --- Task任务的条件等待函数(包括事件消息和定时器消息等条件),只能用于任务函数中。
  106. -- @param id 消息ID
  107. -- @number ms 等待超时时间,单位ms,最大等待126322567毫秒
  108. -- @return result 接收到消息返回true,超时返回false
  109. -- @return data 接收到消息返回消息参数
  110. -- @usage result, data = sys.waitUntil("SIM_IND", 120000)
  111. function sys.waitUntil(id, ms)
  112. local co = sys.check_task()
  113. sys.subscribe(id, co)
  114. local message = ms and {sys.wait(ms)} or {coroutine.yield()}
  115. sys.unsubscribe(id, co)
  116. return message[1] ~= nil, unpack(message, 2, #message)
  117. end
  118. --- 同上,但不返回等待结果
  119. function sys.waitUntilMsg(id)
  120. local co = sys.check_task()
  121. sys.subscribe(id, co)
  122. local message = {coroutine.yield()}
  123. sys.unsubscribe(id, co)
  124. return unpack(message, 2, #message)
  125. end
  126. --- Task任务的条件等待函数扩展(包括事件消息和定时器消息等条件),只能用于任务函数中。
  127. -- @param id 消息ID
  128. -- @number ms 等待超时时间,单位ms,最大等待126322567毫秒
  129. -- @return message 接收到消息返回message,超时返回false
  130. -- @return data 接收到消息返回消息参数
  131. -- @usage result, data = sys.waitUntilExt("SIM_IND", 120000)
  132. function sys.waitUntilExt(id, ms)
  133. local co = sys.check_task()
  134. sys.subscribe(id, co)
  135. local message = ms and {sys.wait(ms)} or {coroutine.yield()}
  136. sys.unsubscribe(id, co)
  137. if message[1] ~= nil then return unpack(message) end
  138. return false
  139. end
  140. --- 创建一个任务线程,在模块最末行调用该函数并注册模块中的任务函数,main.lua导入该模块即可
  141. -- @param fun 任务函数名,用于resume唤醒时调用
  142. -- @param ... 任务函数fun的可变参数
  143. -- @return co 返回该任务的线程号
  144. -- @usage sys.taskInit(task1,'a','b')
  145. function sys.taskInit(fun, ...)
  146. local co = coroutine.create(fun)
  147. sys.coresume(co, ...)
  148. return co
  149. end
  150. ------------------------------------------ rtos消息回调处理部分 ------------------------------------------
  151. --[[
  152. 函数名:cmpTable
  153. 功能 :比较两个table的内容是否相同,注意:table中不能再包含table
  154. 参数 :
  155. t1:第一个table
  156. t2:第二个table
  157. 返回值:相同返回true,否则false
  158. ]]
  159. local function cmpTable(t1, t2)
  160. if not t2 then return #t1 == 0 end
  161. if #t1 == #t2 then
  162. for i = 1, #t1 do
  163. if unpack(t1, i, i) ~= unpack(t2, i, i) then
  164. return false
  165. end
  166. end
  167. return true
  168. end
  169. return false
  170. end
  171. --- 关闭定时器
  172. -- @param val 值为number时,识别为定时器ID,值为回调函数时,需要传参数
  173. -- @param ... val值为函数时,函数的可变参数
  174. -- @return 无
  175. -- @usage timerStop(1)
  176. function sys.timerStop(val, ...)
  177. -- val 为定时器ID
  178. if type(val) == 'number' then
  179. timerPool[val], para[val] = nil, nil
  180. rtos.timer_stop(val)
  181. else
  182. for k, v in pairs(timerPool) do
  183. -- 回调函数相同
  184. if type(v) == 'table' and v.cb == val or v == val then
  185. -- 可变参数相同
  186. if cmpTable({...}, para[k]) then
  187. rtos.timer_stop(k)
  188. timerPool[k], para[k] = nil, nil
  189. break
  190. end
  191. end
  192. end
  193. end
  194. end
  195. --- 关闭同一回调函数的所有定时器
  196. -- @param fnc 定时器回调函数
  197. -- @return 无
  198. -- @usage timerStopAll(cbFnc)
  199. function sys.timerStopAll(fnc)
  200. for k, v in pairs(timerPool) do
  201. if type(v) == "table" and v.cb == fnc or v == fnc then
  202. rtos.timer_stop(k)
  203. timerPool[k], para[k] = nil, nil
  204. end
  205. end
  206. end
  207. function sys.timerAdvStart(fnc, ms, _repeat, ...)
  208. --回调函数和时长检测
  209. --assert(fnc ~= nil, "sys.timerStart(first param) is nil !")
  210. --assert(ms > 0, "sys.timerStart(Second parameter) is <= zero !")
  211. -- 关闭完全相同的定时器
  212. local arg = {...}
  213. if #arg == 0 then
  214. sys.timerStop(fnc)
  215. else
  216. sys.timerStop(fnc, ...)
  217. end
  218. -- 为定时器申请ID,ID值 1-20 留给任务,20-30留给消息专用定时器
  219. while true do
  220. if msgId >= MSG_TIMER_ID_MAX then msgId = TASK_TIMER_ID_MAX end
  221. msgId = msgId + 1
  222. if timerPool[msgId] == nil then
  223. timerPool[msgId] = fnc
  224. break
  225. end
  226. end
  227. --调用底层接口启动定时器
  228. if rtos.timer_start(msgId, ms, _repeat) ~= 1 then return end
  229. --如果存在可变参数,在定时器参数表中保存参数
  230. if #arg ~= 0 then
  231. para[msgId] = arg
  232. end
  233. --返回定时器id
  234. return msgId
  235. end
  236. --- 开启一个定时器
  237. -- @param fnc 定时器回调函数
  238. -- @number ms 整数,最大定时126322567毫秒
  239. -- @param ... 可变参数 fnc的参数
  240. -- @return number 定时器ID,如果失败,返回nil
  241. function sys.timerStart(fnc, ms, ...)
  242. return sys.timerAdvStart(fnc, ms, 0, ...)
  243. end
  244. --- 开启一个循环定时器
  245. -- @param fnc 定时器回调函数
  246. -- @number ms 整数,最大定时126322567毫秒
  247. -- @param ... 可变参数 fnc的参数
  248. -- @return number 定时器ID,如果失败,返回nil
  249. function sys.timerLoopStart(fnc, ms, ...)
  250. return sys.timerAdvStart(fnc, ms, -1, ...)
  251. end
  252. --- 判断某个定时器是否处于开启状态
  253. -- @param val 有两种形式
  254. --一种是开启定时器时返回的定时器id,此形式时不需要再传入可变参数...就能唯一标记一个定时器
  255. --另一种是开启定时器时的回调函数,此形式时必须再传入可变参数...才能唯一标记一个定时器
  256. -- @param ... 可变参数
  257. -- @return number 开启状态返回true,否则nil
  258. function sys.timerIsActive(val, ...)
  259. if type(val) == "number" then
  260. return timerPool[val]
  261. else
  262. for k, v in pairs(timerPool) do
  263. if v == val then
  264. if cmpTable({...}, para[k]) then return true end
  265. end
  266. end
  267. end
  268. end
  269. ------------------------------------------ LUA应用消息订阅/发布接口 ------------------------------------------
  270. -- 订阅者列表
  271. local subscribers = {}
  272. --内部消息队列
  273. local messageQueue = {}
  274. --- 订阅消息
  275. -- @param id 消息id
  276. -- @param callback 消息回调处理
  277. -- @usage subscribe("NET_STATUS_IND", callback)
  278. function sys.subscribe(id, callback)
  279. --if not id or type(id) == "boolean" or (type(callback) ~= "function" and type(callback) ~= "thread") then
  280. -- log.warn("warning: sys.subscribe invalid parameter", id, callback)
  281. -- return
  282. --end
  283. --log.debug("sys", "subscribe", id, callback)
  284. if type(id) == "table" then
  285. -- 支持多topic订阅
  286. for _, v in pairs(id) do sys.subscribe(v, callback) end
  287. return
  288. end
  289. if not subscribers[id] then subscribers[id] = {} end
  290. subscribers[id][callback] = true
  291. end
  292. --- 取消订阅消息
  293. -- @param id 消息id
  294. -- @param callback 消息回调处理
  295. -- @usage unsubscribe("NET_STATUS_IND", callback)
  296. function sys.unsubscribe(id, callback)
  297. --if not id or type(id) == "boolean" or (type(callback) ~= "function" and type(callback) ~= "thread") then
  298. -- log.warn("warning: sys.unsubscribe invalid parameter", id, callback)
  299. -- return
  300. --end
  301. --log.debug("sys", "unsubscribe", id, callback)
  302. if type(id) == "table" then
  303. -- 支持多topic订阅
  304. for _, v in pairs(id) do sys.unsubscribe(v, callback) end
  305. return
  306. end
  307. if subscribers[id] then subscribers[id][callback] = nil end
  308. -- 判断消息是否无其他订阅
  309. for k, _ in pairs(subscribers[id]) do
  310. return
  311. end
  312. subscribers[id] = nil
  313. end
  314. --- 发布内部消息,存储在内部消息队列中
  315. -- @param ... 可变参数,用户自定义
  316. -- @return 无
  317. -- @usage publish("NET_STATUS_IND")
  318. function sys.publish(...)
  319. table.insert(messageQueue, {...})
  320. end
  321. -- 分发消息
  322. local function dispatch()
  323. while true do
  324. if #messageQueue == 0 then
  325. break
  326. end
  327. local message = table.remove(messageQueue, 1)
  328. if subscribers[message[1]] then
  329. local tmpt = {}
  330. for callback, _ in pairs(subscribers[message[1]]) do
  331. table.insert(tmpt, callback)
  332. end
  333. for _, callback in ipairs(tmpt) do
  334. if type(callback) == "function" then
  335. callback(unpack(message, 2, #message))
  336. elseif type(callback) == "thread" then
  337. sys.coresume(callback, unpack(message))
  338. end
  339. end
  340. end
  341. end
  342. end
  343. -- rtos消息回调
  344. --local handlers = {}
  345. --setmetatable(handlers, {__index = function() return function() end end, })
  346. --- 注册rtos消息回调处理函数
  347. -- @number id 消息类型id
  348. -- @param handler 消息处理函数
  349. -- @return 无
  350. -- @usage rtos.on(rtos.MSG_KEYPAD, function(param) handle keypad message end)
  351. --function sys.on(id, handler)
  352. -- handlers[id] = handler
  353. --end
  354. ------------------------------------------ Luat 主调度框架 ------------------------------------------
  355. function sys.safeRun()
  356. -- 分发内部消息
  357. dispatch()
  358. -- 阻塞读取外部消息
  359. local msg, param, exparam = rtos.receive(rtos.INF_TIMEOUT)
  360. --log.info("sys", msg, param, exparam, tableNSize(timerPool), tableNSize(para), tableNSize(taskTimerPool), tableNSize(subscribers))
  361. -- 空消息?
  362. if not msg or msg == 0 then
  363. -- 无任何操作
  364. -- 判断是否为定时器消息,并且消息是否注册
  365. elseif msg == rtos.MSG_TIMER and timerPool[param] then
  366. if param < TASK_TIMER_ID_MAX then
  367. local taskId = timerPool[param]
  368. timerPool[param] = nil
  369. sys.coresume(taskId)
  370. else
  371. local cb = timerPool[param]
  372. --如果不是循环定时器,从定时器id表中删除此定时器
  373. if exparam == 0 then timerPool[param] = nil end
  374. if para[param] ~= nil then
  375. cb(unpack(para[param]))
  376. if exparam == 0 then para[param] = nil end
  377. else
  378. cb()
  379. end
  380. --如果是循环定时器,继续启动此定时器
  381. --if loop[param] then rtos.timer_start(param, loop[param]) end
  382. end
  383. --其他消息(音频消息、充电管理消息、按键消息等)
  384. --elseif type(msg) == "number" then
  385. -- handlers[msg](param, exparam)
  386. --else
  387. -- handlers[msg.id](msg)
  388. end
  389. end
  390. --- run()从底层获取core消息并及时处理相关消息,查询定时器并调度各注册成功的任务线程运行和挂起
  391. -- @return 无
  392. -- @usage sys.run()
  393. if _G.SYSP then
  394. function sys.run() end
  395. else
  396. function sys.run()
  397. while true do
  398. sys.safeRun()
  399. end
  400. end
  401. end
  402. _G.sys_pub = sys.publish
  403. -- 并入原本的sysplus
  404. ----------------------------------------------
  405. -- 提供给异步c接口使用, by 晨旭
  406. sys.cwaitMt = {
  407. wait = function(t,r)
  408. return function()
  409. if r and type(r) == "table" then--新建等待失败的返回
  410. return table.unpack(r)
  411. end
  412. return sys.waitUntilMsg(t)
  413. end
  414. end,
  415. cb = function(t,r)
  416. return function(f)
  417. if type(f) ~= "function" then return end
  418. sys.taskInit(function ()
  419. if r and type(r) == "table" then
  420. --sys.wait(1)--如果回调里调用了sys.publish,直接调用回调,会触发不了下一行的吧。。。
  421. f(table.unpack(r))
  422. return
  423. end
  424. f(sys.waitUntilMsg(t))
  425. end)
  426. end
  427. end,
  428. }
  429. sys.cwaitMt.__index = function(t,i)
  430. if sys.cwaitMt[i] then
  431. return sys.cwaitMt[i](rawget(t,"w"),rawget(t,"r"))
  432. else
  433. rawget(t,i)
  434. end
  435. end
  436. _G.sys_cw = function (w,...)
  437. local r = {...}
  438. local t = {w=w,r=(#r > 0 and r or nil)}
  439. setmetatable(t,sys.cwaitMt)
  440. return t
  441. end
  442. -------------------------------------------------------------------
  443. ------------- 基于任务的task扩展 by 李思琦---------------------------
  444. --任务列表
  445. local taskList = {}
  446. --- 创建一个任务线程,在模块最末行调用该函数并注册模块中的任务函数,main.lua导入该模块即可
  447. -- @param fun 任务函数名,用于resume唤醒时调用
  448. -- @param taskName 任务名称,用于唤醒任务的id
  449. -- @param cbFun 接收到非目标消息时的回调函数
  450. -- @param ... 任务函数fun的可变参数
  451. -- @return co 返回该任务的线程号
  452. -- @usage sys.taskInitEx(task1,'a',callback)
  453. function sys.taskInitEx(fun, taskName, cbFun, ...)
  454. taskList[taskName]={msgQueue={}, To=false, cb=cbFun}
  455. return sys.taskInit(fun, ...)
  456. end
  457. --- 删除由taskInitEx创建的任务线程
  458. -- @param taskName 任务名称,用于唤醒任务的id
  459. -- @return 无
  460. -- @usage sys.taskDel('a')
  461. function sys.taskDel(taskName)
  462. taskList[taskName]=nil
  463. end
  464. local function waitTo(taskName)
  465. taskList[taskName].To = true
  466. sys.publish(taskName)
  467. end
  468. --- 等待接收一个目标消息
  469. -- @param taskName 任务名称,用于唤醒任务的id
  470. -- @param target 目标消息,如果为nil,则表示接收到任意消息都会退出
  471. -- @param ms 超时时间,如果为nil,则表示无超时,永远等待
  472. -- @return msg or false 成功返回table型的msg,超时返回false
  473. -- @usage sys.waitMsg('a', 'b', 1000)
  474. function sys.waitMsg(taskName, target, ms)
  475. if taskList[taskName] == nil then
  476. log.error("sysplus", "sys.taskInitEx启动的task才能使用waitMsg")
  477. return false
  478. end
  479. local msg = false
  480. local message = nil
  481. if #taskList[taskName].msgQueue > 0 then
  482. msg = table.remove(taskList[taskName].msgQueue, 1)
  483. if target == nil then
  484. return msg
  485. end
  486. if (msg[1] == target) then
  487. return msg
  488. elseif type(taskList[taskName].cb) == "function" then
  489. taskList[taskName].cb(msg)
  490. end
  491. end
  492. sys.subscribe(taskName, coroutine.running())
  493. sys.timerStop(waitTo, taskName)
  494. if ms and ms ~= 0 then
  495. sys.timerStart(waitTo, ms, taskName)
  496. end
  497. taskList[taskName].To = false
  498. local finish=false
  499. while not finish do
  500. message = coroutine.yield()
  501. if #taskList[taskName].msgQueue > 0 then
  502. msg = table.remove(taskList[taskName].msgQueue, 1)
  503. -- sys.info("check target", msg[1], target)
  504. if target == nil then
  505. finish = true
  506. else
  507. if (msg[1] == target) then
  508. finish = true
  509. elseif type(taskList[taskName].cb) == "function" then
  510. taskList[taskName].cb(msg)
  511. end
  512. end
  513. elseif taskList[taskName].To then
  514. -- sys.info(taskName, "wait message timeout")
  515. finish = true
  516. end
  517. end
  518. if taskList[taskName].To then
  519. msg = nil
  520. end
  521. taskList[taskName].To = false
  522. sys.timerStop(waitTo, taskName)
  523. sys.unsubscribe(taskName, coroutine.running())
  524. return msg
  525. end
  526. --- 向目标任务发送一个消息
  527. -- @param taskName 任务名称,用于唤醒任务的id
  528. -- @param param1 消息中的参数1,同时也是waitMsg里的target
  529. -- @param param2 消息中的参数2
  530. -- @param param3 消息中的参数3
  531. -- @param param4 消息中的参数4
  532. -- @return true or false 成功返回true
  533. -- @usage sys.sendMsg('a', 'b')
  534. function sys.sendMsg(taskName, param1, param2, param3, param4)
  535. if taskList[taskName]~=nil then
  536. table.insert(taskList[taskName].msgQueue, {param1, param2, param3, param4})
  537. sys.publish(taskName)
  538. return true
  539. end
  540. return false
  541. end
  542. function sys.cleanMsg(taskName)
  543. if taskList[taskName]~=nil then
  544. taskList[taskName].msgQueue = {}
  545. return true
  546. end
  547. return false
  548. end
  549. function sys.taskCB(taskName, msg)
  550. if taskList[taskName]~=nil then
  551. if type(taskList[taskName].cb) == "function" then
  552. taskList[taskName].cb(msg)
  553. return
  554. end
  555. end
  556. log.error(taskName, "no cb fun")
  557. end
  558. _G.sys_send = sys.sendMsg
  559. _G.sys_wait = sys.waitMsg
  560. return sys
  561. ----------------------------