| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379 |
- --- 模块功能:luat协程调度框架
- -- @module sys
- -- @author 稀饭放姜 小强
- -- @license MIT
- -- @copyright openLuat.com
- -- @release 2017.9.13
- require "patch"
- require "errdump"
- require "log"
- module(..., package.seeall)
- -- lib脚本版本号,只要lib中的任何一个脚本做了修改,都需要更新此版本号
- SCRIPT_LIB_VER = "2.0.0"
- -- 脚本发布时的最新core软件版本号
- CORE_MIN_VER = "Luat_V0010_8955"
- -- TaskID最大值
- local TASK_TIMER_ID_MAX = 0x1FFFFFFF
- -- msgId 最大值(请勿修改否则会发生msgId碰撞的危险)
- local MSG_TIMER_ID_MAX = 0x7FFFFFFF
- -- 任务定时器id
- local taskTimerId = 0
- -- 消息定时器id
- local msgId = TASK_TIMER_ID_MAX
- -- 定时器id表
- local timerPool = {}
- local taskTimerPool = {}
- --消息定时器参数表
- local para = {}
- --定时器是否循环表
- local loop = {}
- --- 启动GSM协议栈。例如在充电开机未启动GSM协议栈状态下,如果用户长按键正常开机,此时调用此接口启动GSM协议栈即可
- -- @return 无
- -- @usage sys.powerOn()
- function powerOn()
- rtos.poweron(1)
- end
- --- 软件重启
- -- @string r 重启原因,用户自定义,一般是string类型,重启后的trace中会打印出此重启原因
- -- @return 无
- -- @usage sys.restart('程序超时软件重启')
- function restart(r)
- assert(r and r ~= "", "sys.restart cause null")
- errdump.appendErr("restart[" .. r .. "];")
- rtos.restart()
- end
- --- Task任务延时函数,只能用于任务函数中
- -- @number ms 整数,最大等待126322567毫秒
- -- @return number 正常返回1,失败返回nil
- -- @usage sys.wait(30)
- function wait(ms)
- -- 参数检测,参数不能为负值
- assert(ms > 0, "The wait time cannot be negative!")
- -- 选一个未使用的定时器ID给该任务线程
- if taskTimerId >= TASK_TIMER_ID_MAX then taskTimerId = 0 end
- taskTimerId = taskTimerId + 1
- local timerid = taskTimerId
- taskTimerPool[coroutine.running()] = timerid
- timerPool[timerid] = coroutine.running()
- -- 调用core的rtos定时器
- if 1 ~= rtos.timer_start(timerid, ms) then log.debug("rtos.timer_start error") return end
- -- 挂起调用的任务线程
- local message = {coroutine.yield()}
- if #message ~= 0 then
- rtos.timer_stop(timerid)
- taskTimerPool[coroutine.running()] = nil
- timerPool[timerid] = nil
- return unpack(message)
- end
- end
- --- Task任务的条件等待函数(包括事件消息和定时器消息等条件),只能用于任务函数中。
- -- @param id 消息ID
- -- @number ms 等待超时时间,单位ms,最大等待126322567毫秒
- -- @return result 接收到消息返回true,超时返回false
- -- @return data 接收到消息返回消息参数
- -- @usage result, data = sys.waitUntil("SIM_IND", 120000)
- function waitUntil(id, ms)
- subscribe(id, coroutine.running())
- local message = ms and {wait(ms)} or {coroutine.yield()}
- unsubscribe(id, coroutine.running())
- return message[1] ~= nil, unpack(message, 2, #message)
- end
- --- 创建一个任务线程,在模块最末行调用该函数并注册模块中的任务函数,main.lua导入该模块即可
- -- @param fun 任务函数名,用于resume唤醒时调用
- -- @param ... 任务函数fun的可变参数
- -- @return co 返回该任务的线程号
- -- @usage sys.taskInit(task1,'a','b')
- function taskInit(fun, ...)
- local co = coroutine.create(fun)
- coroutine.resume(co, unpack(arg))
- return co
- end
- --[[
- 检查底层软件版本号和lib脚本需要的最小底层软件版本号是否匹配
- ]]
- local function checkCoreVer()
- local realver = rtos.get_version()
- --如果没有获取到底层软件版本号
- if not realver or realver == "" then
- errdump.appendErr("checkCoreVer[no core ver error];")
- return
- end
-
- local buildver = string.match(realver, "Luat_V(%d+)_")
- --如果底层软件版本号格式错误
- if not buildver then
- errdump.appendErr("checkCoreVer[core ver format error]" .. realver .. ";")
- return
- end
-
- --lib脚本需要的底层软件版本号大于底层软件的实际版本号
- if tonumber(string.match(CORE_MIN_VER, "Luat_V(%d+)_")) > tonumber(buildver) then
- log.info("ril.checkCoreVer" .. realver .. "," .. CORE_MIN_VER .. ";")
- end
- end
- --- Luat平台初始化
- -- @param mode 充电开机是否启动GSM协议栈,1不启动,否则启动
- -- @param lprfnc 用户应用脚本中定义的“低电关机处理函数”,如果有函数名,则低电时,本文件中的run接口不会执行任何动作,否则,会延时1分钟自动关机
- -- @return 无
- -- @usage sys.init(1,0)
- function init(mode, lprfnc)
- -- 用户应用脚本中必须定义PROJECT和VERSION两个全局变量,否则会死机重启,如何定义请参考各个demo中的main.lua
- assert(PROJECT and PROJECT ~= "" and VERSION and VERSION ~= "", "Undefine PROJECT or VERSION")
- collectgarbage("setpause", 80)
-
- -- 设置AT命令的虚拟串口
- uart.setup(uart.ATC, 0, 0, uart.PAR_NONE, uart.STOP_1)
- log.info("poweron reason:", rtos.poweron_reason(), PROJECT, VERSION, SCRIPT_LIB_VER, rtos.get_version())
- if mode == 1 then
- -- 充电开机
- if rtos.poweron_reason() == rtos.POWERON_CHARGER then
- -- 关闭GSM协议栈
- rtos.poweron(0)
- end
- end
- -- 如果存在脚本运行错误文件,打开文件,打印错误信息
- local f = io.open("/luaerrinfo.txt", "r")
- if f then
- log.error(f:read("*a") or "")
- f:close()
- end
- -- 打印LIB_ERR_FILE文件中的错误信息
- errdump.initErr()
- checkCoreVer()
- end
- ------------------------------------------ rtos消息回调处理部分 ------------------------------------------
- --[[
- 函数名:cmpTable
- 功能 :比较两个table的内容是否相同,注意:table中不能再包含table
- 参数 :
- t1:第一个table
- t2:第二个table
- 返回值:相同返回true,否则false
- ]]
- local function cmpTable(t1, t2)
- if not t2 then return #t1 == 0 end
- if #t1 == #t2 then
- for i = 1, #t1 do
- if unpack(t1, i, i) ~= unpack(t2, i, i) then
- return false
- end
- end
- return true
- end
- return false
- end
- --- 关闭定时器
- -- @param val 值为number时,识别为定时器ID,值为回调函数时,需要传参数
- -- @param ... val值为函数时,函数的可变参数
- -- @return 无
- -- @usage timer_stop(1)
- function timer_stop(val, ...)
- -- val 为定时器ID
- if type(val) == 'number' then
- timerPool[val], para[val], loop[val] = nil
- rtos.timer_stop(val)
- else
- for k, v in pairs(timerPool) do
- -- 回调函数相同
- if type(v) == 'table' and v.cb == val or v == val then
- -- 可变参数相同
- if cmpTable(arg, para[k]) then
- rtos.timer_stop(k)
- timerPool[k], para[k], loop[val] = nil
- break
- end
- end
- end
- end
- end
- --- 函数功能--开启一个定时器
- -- @param fnc 定时器回调函数
- -- @number ms 整数,最大定时126322567毫秒
- -- @param ... 可变参数 fnc的参数
- -- @return number 定时器ID,如果失败,返回nil
- function timer_start(fnc, ms, ...)
- --回调函数和时长检测
- assert(fnc ~= nil, "sys.timer_start(first param) is nil !")
- assert(ms > 0, "sys.timer_start(Second parameter) is <= zero !")
- -- 关闭完全相同的定时器
- if arg.n == 0 then
- timer_stop(fnc)
- else
- timer_stop(fnc, unpack(arg))
- end
- -- 为定时器申请ID,ID值 1-20 留给任务,20-30留给消息专用定时器
- while true do
- if msgId >= MSG_TIMER_ID_MAX then msgId = TASK_TIMER_ID_MAX end
- msgId = msgId + 1
- if timerPool[msgId] == nil then
- timerPool[msgId] = fnc
- break
- end
- end
- --调用底层接口启动定时器
- if rtos.timer_start(msgId, ms) ~= 1 then log.debug("rtos.timer_start error") return end
- --如果存在可变参数,在定时器参数表中保存参数
- if arg.n ~= 0 then
- para[msgId] = arg
- end
- --返回定时器id
- return msgId
- end
- --- 函数功能--开启一个循环定时器
- -- @param fnc 定时器回调函数
- -- @number ms 整数,最大定时126322567毫秒
- -- @param ... 可变参数 fnc的参数
- -- @return number 定时器ID,如果失败,返回nil
- function timer_loop_start(fnc,ms,...)
- local tid = timer_start(fnc,ms,unpack(arg))
- if tid then loop[tid] = ms end
- return tid
- end
- --- 函数功能--判断某个定时器是否处于开启状态
- -- @param val 有两种形式
- --一种是开启定时器时返回的定时器id,此形式时不需要再传入可变参数...就能唯一标记一个定时器
- --另一种是开启定时器时的回调函数,此形式时必须再传入可变参数...才能唯一标记一个定时器
- -- @param ... 可变参数
- -- @return number 开启状态返回true,否则nil
- function timer_is_active(val,...)
- if type(val) == "number" then
- return timerPool[val]
- else
- for k,v in pairs(timerPool) do
- if v == val then
- if cmpTable(arg,para[k]) then return true end
- end
- end
- end
- end
- ------------------------------------------ LUA应用消息订阅/发布接口 ------------------------------------------
- -- 订阅者列表
- local subscribers = {}
- --内部消息队列
- local messageQueue = {}
- --- 订阅消息
- -- @param id 消息id
- -- @param callback 消息回调处理
- -- @usage subscribe("NET_STATUS_IND", callback)
- function subscribe(id, callback)
- if type(id) ~= "string" or (type(callback) ~= "function" and type(callback) ~= "thread") then
- log.warn("warning: sys.subscribe invalid parameter", id, callback)
- return
- end
- if not subscribers[id] then subscribers[id] = {} end
- subscribers[id][callback] = true
- end
- --- 取消订阅消息
- -- @param id 消息id
- -- @param callback 消息回调处理
- -- @usage unsubscribe("NET_STATUS_IND", callback)
- function unsubscribe(id, callback)
- if type(id) ~= "string" or (type(callback) ~= "function" and type(callback) ~= "thread") then
- log.warn("warning: sys.unsubscribe invalid parameter", id, callback)
- return
- end
- if subscribers[id] then subscribers[id][callback] = nil end
- end
- --- 发布内部消息,存储在内部消息队列中
- -- @param ... 可变参数,用户自定义
- -- @return 无
- -- @usage publish("NET_STATUS_IND")
- function publish(...)
- table.insert(messageQueue, arg)
- end
- -- 分发消息
- local function dispatch()
- while true do
- if #messageQueue == 0 then
- break
- end
- local message = table.remove(messageQueue, 1)
- if subscribers[message[1]] then
- for callback, _ in pairs(subscribers[message[1]]) do
- if type(callback) == "function" then
- callback(unpack(message, 2, #message))
- elseif type(callback) == "thread" then
- coroutine.resume(callback, unpack(message))
- end
- end
- end
- end
- end
- -- rtos消息回调
- local handlers = {}
- setmetatable(handlers, {__index = function() return function() end end, })
- --- 注册rtos消息回调处理函数
- -- @number id 消息类型id
- -- @param handler 消息处理函数
- -- @return 无
- -- @usage rtos.on(rtos.MSG_KEYPAD, function(param) handle keypad message end)
- rtos.on = function(id, handler)
- handlers[id] = handler
- end
- ------------------------------------------ Luat 主调度框架 ------------------------------------------
- --- run()从底层获取core消息并及时处理相关消息,查询定时器并调度各注册成功的任务线程运行和挂起
- -- @return 无
- -- @usage sys.run()
- function run()
- while true do
- -- 分发内部消息
- dispatch()
- -- 阻塞读取外部消息
- local msg, param = rtos.receive(rtos.INF_TIMEOUT)
- -- 判断是否为定时器消息,并且消息是否注册
- if msg == rtos.MSG_TIMER and timerPool[param] then
- if param < TASK_TIMER_ID_MAX then
- local taskId = timerPool[param]
- timerPool[param] = nil
- if taskTimerPool[taskId] == param then
- taskTimerPool[taskId] = nil
- coroutine.resume(taskId)
- end
- else
- local cb = timerPool[param]
- --如果不是循环定时器,从定时器id表中删除此定时器
- if not loop[param] then timerPool[param] = nil end
- if para[param] ~= nil then
- cb(unpack(para[param]))
- if not loop[param] then para[param] = nil end
- else
- cb()
- end
- --如果是循环定时器,继续启动此定时器
- if loop[param] then rtos.timer_start(param,loop[param]) end
- end
- --其他消息(音频消息、充电管理消息、按键消息等)
- elseif type(msg) == "number" then
- handlers[msg](param)
- else
- handlers[msg.id](msg)
- end
- end
- end
- require "clib"
|