Air202学习 五

air202 使用lua程序开发个人理解(基于操作系统)

其实使用lua开发和C语言开发是一样的,只不过使用lua时把硬件的底层给封装好,不需要在自己配置寄存器了, 这一块就需要调用air202官方给封装好的lib库文件

一. 函数运行:

1. 当在文件只是定义实现函数时,它是不会运行的,需要在调用一下, 如:

--test.lua文件
local function TASK(  )
    print("TASK function test")
end
TASK()        --直接调用,在main.lua文件中require"test"的时候则会调用该函数

此时只会打印一次"TASK function test"信息

2. 在协程中运行:死循环一直运行,通过wait挂起

--test.lua文件

--定义的函数时不会执行的,需要调用
local function ss(  )
    print("ss function test")
end 


--调动sys.lua中的函数taskInit 弄出个协程来 
sys.taskInit(function()
    cnt = 0
    while true do
       ss() 
       sys.wait(1000)          -- 挂起1000ms,同理为每隔1000ms运行一次
    end
end)

定义的函数在协程中调用1000ms执行一次

3. 定时器和协程一起使用:

--test.lua文件
local function ss(  )
    print("Task function test")
end

local function TIM()
	print("TIM function test")
end 

--直接调用
--ss()        --直接调用,在main.lua文件中require"test"的时候则会调用该函数

--协程
sys.taskInit(function()
    cnt = 0
    while true do
       ss() 
       sys.wait(1000)          -- 挂起1000ms,同理为每隔1000ms运行一次
    end
end)

--定时器调用
--sys.timerStart(ss,3000)   --3秒运行一次
sys.timerLoopStart (TIM,1000)  --循环定时器,每隔1秒运行一次

定时器和协程一起运行结果:

二. 简单总结

1. 因为lua程序是顺序执行下来, 且只执行一遍的那么就需要另外的方法,让需要一直执行的程序块一直运行

2. 如果想要程序一直运行可以像C语言开发单片机那样, 直接用一个循环, 在循环内是一个需要一直运行的程序块,不过要记住把系统要挂起,不知会不会直接死在这.

3. air202 模块还使用了另一方法实现类似C语言开发单片机中的中断方式,当一个操作完成时就会发出一个信号; 其他函数可以订阅该消息并做对应的操作,在接收这个信号后执行某段程序(subscribe订阅方式) ; 调用  sys.lua  ( Luat协程调度框架 )  中的函数

三. 程序注册 (抄的)

LuaTask通过订阅发布来实现消息机制。

当函数完成一个操作后,可以发布一个消息,其他函数可以订阅该消息并做对应的操作。举个例子,当socket发送完数据后发布“SEND_FINISH”。这时开发者想socket发布完成后通过串口发送数据或者改变某个IO口的状态。就可以订阅该消息subscribe("SEND_FINISH",callback)。callback为接收到SEND_FINISH消息后需要做的事。

先来看一个程序

testMsgPub.lua

--testMsgPub.lua
module(...,package.seeall)

require"sys"
local  a = 2
local function pub()
    print("pub")
    sys.publish("TEST",a)       --可以发布多个变量sys.publish("TEST",1,2,3)
end
pub()

testMsgSub.lua

--testMsgSub.lua
module(...,package.seeall)

require"sys"

local function subCallBack(...)
    print("rev",arg[1]) 
end

sys.subscribe("TEST",subCallBack)

如果要在任务函数中订阅消息并做相应的处理,怎么办?

module(...,package.seeall)

require"sys"
local  a = 2
local function pub()
    print("pub")
    sys.publish("TEST",a)
end
pub()
sys.taskInit(function()
    while true do
        result, data = sys.waitUntil("TEST", 10000)
        if result == true then 
            print("rev")
            print(data)
        end
        sys.wait(2000)
    end
end)

调用sys.waitUntil()函数即可。

接下来分析实现的源码

为了更好的理解源码,需要以下的预备知识:

1、回调函数的实现

local function callBackTest(...)
    print("callBack",arg[1])
end

local function test( a,callBackTest )
    if a > 1 then
        callBackTest(1)
    end
end
test(2,callBackTest)
--输出
--callBack  1

2、不定参数

function g (a, b, ...) end
g(3)              -- a=3, b=nil, arg={n=0}   -- n为不定参数的个数
g(3, 4)           -- a=3, b=4, arg={n=0}
g(3, 4, 5, 8)     -- a=3, b=4, arg={5, 8; n=2}  

进入正题

订阅和发布官方给的实例:

------------------------------------------ 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        --标记id和callback关系
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  --删除id和callback关系
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
                    print("unpack",unpack(message, 2, #message))
                    callback(unpack(message, 2, #message))   -- 返回第二个到最后一个
                elseif type(callback) == "thread" then
                    coroutine.resume(callback, unpack(message))
                end
            end
        end
    end
end

以sys.publish("TEST",a)和sys.subscribe("TEST",subCallBack),订阅者列表为local subscribers = {}。内部消息队列为local messageQueue = {}为例:

1、在publish函数中,将"TEST"消息和参数插入messageQueue列表中

此时messageQueue中为{{"TEST",2;n=1}}

2、在subscribe函数中判断消息和callback类型是否正确,如果正确则在subscribers中建立消息与回调函数之间的关系。

此时subscribers["TEST"][subCallBack] = true。表明TEST消息对应的回掉函数为subCallBack

3、在dispatch()函数中,获得表头列表。

local message = table.remove(messageQueue, 1)

此时message为{"TEST",2;n=1}

找到该消息对应的回调函数或消息。将message中的参数传给回调函数。

通过pairs遍历得到消息对应的回调函数或者任务。

如果callback是函数,那么将publish时候的参数传给回调函数。

如果callback是线程,那么唤醒该线程。

以上只是单个消息举例,多个消息同理,因为每次循环都会将messageQueue的头部出队列,满足FIFO原则。

在有上基础下容易的理解waitUntil()的实现

--- 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

1、订阅id,并传入线程号

2、阻塞线程,如果接收到了消息,那么返回message

3、取消订阅该id

4、返回结果

猜你喜欢

转载自blog.csdn.net/dianzishi123/article/details/84108509