sys.lua 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427
  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. local taskTimerPool = {}
  37. --消息定时器参数表
  38. local para = {}
  39. --定时器是否循环表
  40. --local loop = {}
  41. --lua脚本运行出错时,是否回退为本地烧写的版本
  42. --local sRollBack = true
  43. _G.COROUTINE_ERROR_ROLL_BACK = true
  44. _G.COROUTINE_ERROR_RESTART = true
  45. -- 对coroutine.resume加一个修饰器用于捕获协程错误
  46. --local rawcoresume = coroutine.resume
  47. local function wrapper(co,...)
  48. local arg = {...}
  49. if not arg[1] then
  50. local traceBack = debug.traceback(co)
  51. traceBack = (traceBack and traceBack~="") and (arg[2].."\r\n"..traceBack) or arg[2]
  52. log.error("coroutine.resume",traceBack)
  53. --if errDump and type(errDump.appendErr)=="function" then
  54. -- errDump.appendErr(traceBack)
  55. --end
  56. if _G.COROUTINE_ERROR_ROLL_BACK then
  57. sys.timerStart(assert,500,false,traceBack)
  58. elseif _G.COROUTINE_ERROR_RESTART then
  59. rtos.reboot()
  60. end
  61. end
  62. return ...
  63. end
  64. sys.coresume = function(...)
  65. local arg = {...}
  66. return wrapper(arg[1], coroutine.resume(...))
  67. end
  68. --- Task任务延时函数,只能用于任务函数中
  69. -- @number ms 整数,最大等待126322567毫秒
  70. -- @return 定时结束返回nil,被其他线程唤起返回调用线程传入的参数
  71. -- @usage sys.wait(30)
  72. function sys.wait(ms)
  73. -- 参数检测,参数不能为负值
  74. --assert(ms > 0, "The wait time cannot be negative!")
  75. -- 选一个未使用的定时器ID给该任务线程
  76. while true do
  77. if taskTimerId >= TASK_TIMER_ID_MAX - 1 then
  78. taskTimerId = 0
  79. else
  80. taskTimerId = taskTimerId + 1
  81. end
  82. if taskTimerPool[taskTimerId] == nil then
  83. break
  84. end
  85. end
  86. local timerid = taskTimerId
  87. taskTimerPool[coroutine.running()] = timerid
  88. timerPool[timerid] = coroutine.running()
  89. -- 调用core的rtos定时器
  90. if 1 ~= rtos.timer_start(timerid, ms) then log.debug("rtos.timer_start error") return end
  91. -- 挂起调用的任务线程
  92. local message = {coroutine.yield()}
  93. if #message ~= 0 then
  94. rtos.timer_stop(timerid)
  95. taskTimerPool[coroutine.running()] = nil
  96. timerPool[timerid] = nil
  97. return unpack(message)
  98. end
  99. end
  100. --- Task任务的条件等待函数(包括事件消息和定时器消息等条件),只能用于任务函数中。
  101. -- @param id 消息ID
  102. -- @number ms 等待超时时间,单位ms,最大等待126322567毫秒
  103. -- @return result 接收到消息返回true,超时返回false
  104. -- @return data 接收到消息返回消息参数
  105. -- @usage result, data = sys.waitUntil("SIM_IND", 120000)
  106. function sys.waitUntil(id, ms)
  107. sys.subscribe(id, coroutine.running())
  108. local message = ms and {sys.wait(ms)} or {coroutine.yield()}
  109. sys.unsubscribe(id, coroutine.running())
  110. return message[1] ~= nil, unpack(message, 2, #message)
  111. end
  112. --- 同上,但不返回等待结果
  113. function sys.waitUntilMsg(id)
  114. sys.subscribe(id, coroutine.running())
  115. local message = {coroutine.yield()}
  116. sys.unsubscribe(id, coroutine.running())
  117. return unpack(message, 2, #message)
  118. end
  119. --- Task任务的条件等待函数扩展(包括事件消息和定时器消息等条件),只能用于任务函数中。
  120. -- @param id 消息ID
  121. -- @number ms 等待超时时间,单位ms,最大等待126322567毫秒
  122. -- @return message 接收到消息返回message,超时返回false
  123. -- @return data 接收到消息返回消息参数
  124. -- @usage result, data = sys.waitUntilExt("SIM_IND", 120000)
  125. function sys.waitUntilExt(id, ms)
  126. sys.subscribe(id, coroutine.running())
  127. local message = ms and {sys.wait(ms)} or {coroutine.yield()}
  128. sys.unsubscribe(id, coroutine.running())
  129. if message[1] ~= nil then return unpack(message) end
  130. return false
  131. end
  132. --- 创建一个任务线程,在模块最末行调用该函数并注册模块中的任务函数,main.lua导入该模块即可
  133. -- @param fun 任务函数名,用于resume唤醒时调用
  134. -- @param ... 任务函数fun的可变参数
  135. -- @return co 返回该任务的线程号
  136. -- @usage sys.taskInit(task1,'a','b')
  137. function sys.taskInit(fun, ...)
  138. local co = coroutine.create(fun)
  139. sys.coresume(co, ...)
  140. return co
  141. end
  142. ------------------------------------------ rtos消息回调处理部分 ------------------------------------------
  143. --[[
  144. 函数名:cmpTable
  145. 功能 :比较两个table的内容是否相同,注意:table中不能再包含table
  146. 参数 :
  147. t1:第一个table
  148. t2:第二个table
  149. 返回值:相同返回true,否则false
  150. ]]
  151. local function cmpTable(t1, t2)
  152. if not t2 then return #t1 == 0 end
  153. if #t1 == #t2 then
  154. for i = 1, #t1 do
  155. if unpack(t1, i, i) ~= unpack(t2, i, i) then
  156. return false
  157. end
  158. end
  159. return true
  160. end
  161. return false
  162. end
  163. --- 关闭定时器
  164. -- @param val 值为number时,识别为定时器ID,值为回调函数时,需要传参数
  165. -- @param ... val值为函数时,函数的可变参数
  166. -- @return 无
  167. -- @usage timerStop(1)
  168. function sys.timerStop(val, ...)
  169. -- val 为定时器ID
  170. if type(val) == 'number' then
  171. timerPool[val], para[val] = nil
  172. rtos.timer_stop(val)
  173. else
  174. for k, v in pairs(timerPool) do
  175. -- 回调函数相同
  176. if type(v) == 'table' and v.cb == val or v == val then
  177. -- 可变参数相同
  178. if cmpTable({...}, para[k]) then
  179. rtos.timer_stop(k)
  180. timerPool[k], para[k] = nil
  181. break
  182. end
  183. end
  184. end
  185. end
  186. end
  187. --- 关闭同一回调函数的所有定时器
  188. -- @param fnc 定时器回调函数
  189. -- @return 无
  190. -- @usage timerStopAll(cbFnc)
  191. function sys.timerStopAll(fnc)
  192. for k, v in pairs(timerPool) do
  193. if type(v) == "table" and v.cb == fnc or v == fnc then
  194. rtos.timer_stop(k)
  195. timerPool[k], para[k] = nil
  196. end
  197. end
  198. end
  199. function sys.timerAdvStart(fnc, ms, _repeat, ...)
  200. --回调函数和时长检测
  201. --assert(fnc ~= nil, "sys.timerStart(first param) is nil !")
  202. --assert(ms > 0, "sys.timerStart(Second parameter) is <= zero !")
  203. -- 关闭完全相同的定时器
  204. local arg = {...}
  205. if #arg == 0 then
  206. sys.timerStop(fnc)
  207. else
  208. sys.timerStop(fnc, ...)
  209. end
  210. -- 为定时器申请ID,ID值 1-20 留给任务,20-30留给消息专用定时器
  211. while true do
  212. if msgId >= MSG_TIMER_ID_MAX then msgId = TASK_TIMER_ID_MAX end
  213. msgId = msgId + 1
  214. if timerPool[msgId] == nil then
  215. timerPool[msgId] = fnc
  216. break
  217. end
  218. end
  219. --调用底层接口启动定时器
  220. if rtos.timer_start(msgId, ms, _repeat) ~= 1 then return end
  221. --如果存在可变参数,在定时器参数表中保存参数
  222. if #arg ~= 0 then
  223. para[msgId] = arg
  224. end
  225. --返回定时器id
  226. return msgId
  227. end
  228. --- 开启一个定时器
  229. -- @param fnc 定时器回调函数
  230. -- @number ms 整数,最大定时126322567毫秒
  231. -- @param ... 可变参数 fnc的参数
  232. -- @return number 定时器ID,如果失败,返回nil
  233. function sys.timerStart(fnc, ms, ...)
  234. return sys.timerAdvStart(fnc, ms, 0, ...)
  235. end
  236. --- 开启一个循环定时器
  237. -- @param fnc 定时器回调函数
  238. -- @number ms 整数,最大定时126322567毫秒
  239. -- @param ... 可变参数 fnc的参数
  240. -- @return number 定时器ID,如果失败,返回nil
  241. function sys.timerLoopStart(fnc, ms, ...)
  242. return sys.timerAdvStart(fnc, ms, -1, ...)
  243. end
  244. --- 判断某个定时器是否处于开启状态
  245. -- @param val 有两种形式
  246. --一种是开启定时器时返回的定时器id,此形式时不需要再传入可变参数...就能唯一标记一个定时器
  247. --另一种是开启定时器时的回调函数,此形式时必须再传入可变参数...才能唯一标记一个定时器
  248. -- @param ... 可变参数
  249. -- @return number 开启状态返回true,否则nil
  250. function sys.timerIsActive(val, ...)
  251. if type(val) == "number" then
  252. return timerPool[val]
  253. else
  254. for k, v in pairs(timerPool) do
  255. if v == val then
  256. if cmpTable({...}, para[k]) then return true end
  257. end
  258. end
  259. end
  260. end
  261. ------------------------------------------ LUA应用消息订阅/发布接口 ------------------------------------------
  262. -- 订阅者列表
  263. local subscribers = {}
  264. --内部消息队列
  265. local messageQueue = {}
  266. --- 订阅消息
  267. -- @param id 消息id
  268. -- @param callback 消息回调处理
  269. -- @usage subscribe("NET_STATUS_IND", callback)
  270. function sys.subscribe(id, callback)
  271. --if not id or type(id) == "boolean" or (type(callback) ~= "function" and type(callback) ~= "thread") then
  272. -- log.warn("warning: sys.subscribe invalid parameter", id, callback)
  273. -- return
  274. --end
  275. --log.debug("sys", "subscribe", id, callback)
  276. if type(id) == "table" then
  277. -- 支持多topic订阅
  278. for _, v in pairs(id) do sys.subscribe(v, callback) end
  279. return
  280. end
  281. if not subscribers[id] then subscribers[id] = {} end
  282. subscribers[id][callback] = true
  283. end
  284. --- 取消订阅消息
  285. -- @param id 消息id
  286. -- @param callback 消息回调处理
  287. -- @usage unsubscribe("NET_STATUS_IND", callback)
  288. function sys.unsubscribe(id, callback)
  289. --if not id or type(id) == "boolean" or (type(callback) ~= "function" and type(callback) ~= "thread") then
  290. -- log.warn("warning: sys.unsubscribe invalid parameter", id, callback)
  291. -- return
  292. --end
  293. --log.debug("sys", "unsubscribe", id, callback)
  294. if type(id) == "table" then
  295. -- 支持多topic订阅
  296. for _, v in pairs(id) do sys.unsubscribe(v, callback) end
  297. return
  298. end
  299. if subscribers[id] then subscribers[id][callback] = nil end
  300. -- 判断消息是否无其他订阅
  301. for k, _ in pairs(subscribers[id]) do
  302. return
  303. end
  304. subscribers[id] = nil
  305. end
  306. --- 发布内部消息,存储在内部消息队列中
  307. -- @param ... 可变参数,用户自定义
  308. -- @return 无
  309. -- @usage publish("NET_STATUS_IND")
  310. function sys.publish(...)
  311. table.insert(messageQueue, {...})
  312. end
  313. -- 分发消息
  314. local function dispatch()
  315. while true do
  316. if #messageQueue == 0 then
  317. break
  318. end
  319. local message = table.remove(messageQueue, 1)
  320. if subscribers[message[1]] then
  321. for callback, _ in pairs(subscribers[message[1]]) do
  322. if type(callback) == "function" then
  323. callback(unpack(message, 2, #message))
  324. elseif type(callback) == "thread" then
  325. sys.coresume(callback, unpack(message))
  326. end
  327. end
  328. end
  329. end
  330. end
  331. -- rtos消息回调
  332. --local handlers = {}
  333. --setmetatable(handlers, {__index = function() return function() end end, })
  334. --- 注册rtos消息回调处理函数
  335. -- @number id 消息类型id
  336. -- @param handler 消息处理函数
  337. -- @return 无
  338. -- @usage rtos.on(rtos.MSG_KEYPAD, function(param) handle keypad message end)
  339. --function sys.on(id, handler)
  340. -- handlers[id] = handler
  341. --end
  342. ------------------------------------------ Luat 主调度框架 ------------------------------------------
  343. function sys.safeRun()
  344. -- 分发内部消息
  345. dispatch()
  346. -- 阻塞读取外部消息
  347. local msg, param, exparam = rtos.receive(rtos.INF_TIMEOUT)
  348. --log.info("sys", msg, param, exparam, tableNSize(timerPool), tableNSize(para), tableNSize(taskTimerPool), tableNSize(subscribers))
  349. -- 空消息?
  350. if not msg or msg == 0 then
  351. -- 无任何操作
  352. -- 判断是否为定时器消息,并且消息是否注册
  353. elseif msg == rtos.MSG_TIMER and timerPool[param] then
  354. if param < TASK_TIMER_ID_MAX then
  355. local taskId = timerPool[param]
  356. timerPool[param] = nil
  357. if taskTimerPool[taskId] == param then
  358. taskTimerPool[taskId] = nil
  359. sys.coresume(taskId)
  360. end
  361. else
  362. local cb = timerPool[param]
  363. --如果不是循环定时器,从定时器id表中删除此定时器
  364. if exparam == 0 then timerPool[param] = nil end
  365. if para[param] ~= nil then
  366. cb(unpack(para[param]))
  367. if exparam == 0 then para[param] = nil end
  368. else
  369. cb()
  370. end
  371. --如果是循环定时器,继续启动此定时器
  372. --if loop[param] then rtos.timer_start(param, loop[param]) end
  373. end
  374. --其他消息(音频消息、充电管理消息、按键消息等)
  375. --elseif type(msg) == "number" then
  376. -- handlers[msg](param, exparam)
  377. --else
  378. -- handlers[msg.id](msg)
  379. end
  380. end
  381. --- run()从底层获取core消息并及时处理相关消息,查询定时器并调度各注册成功的任务线程运行和挂起
  382. -- @return 无
  383. -- @usage sys.run()
  384. if _G.SYSP then
  385. function sys.run() end
  386. else
  387. function sys.run()
  388. while true do
  389. sys.safeRun()
  390. end
  391. end
  392. end
  393. _G.sys_pub = sys.publish
  394. return sys
  395. ----------------------------