跳转至

sys - LuatOS运行框架

作者:朱天华

一、概述

sys核心库是LuatOS运行框架库,是LuatOS应用程序运行核心大脑,所有LuatOS应用项目都会使用到sys核心库;

sys核心库提供了如下四大类功能(task任务、message消息、timer定时器、run调度器):

1、task:LuatOS 任务功能,task分为基础task和高级task两种;

从设计原理的角度来看,基础task和高级task的区别是
(1) 所有的基础task共享一个全局消息队列
(2) 每个高级task都有自己独立的消息队列,同时又能使用全局消息队列;


从用户使用的角度来看,基础task和高级task的区别是
(1) 基础task如果阻塞功能使用不当,可能会丢失自己应该处理的消息;
(2) 高级task如果阻塞功能使用不当,不会丢失自己应该处理的消息;

虽然从设计原理来看,高级task比基础task使用起来不容易犯错
但是由于基础task使用起来简洁,基础task还是需要掌握,一旦掌握之后,也不容易犯错;


sys核心库提供的task管理功能有以下几种
(1) 基础task的创建和启动运行sys.taskInit(task_func, ...)
(2) 高级task的创建和启动运行sys.taskInitEx(task_func, task_name, non_targeted_msg_cbfunc, ...)
(3) 高级task管理表资源的释放sys.taskDel(task_name)

2、message:LuatOS 消息功能;

从消息接收处理方的角度来划分,消息可以分为全局消息和定向消息;
全局消息是指消息可以被任意订阅方接收处理;
定向消息是指消息只能被指定的task接收处理


sys核心库提供的全局消息管理功能有以下几种
(1) 全局消息发布:sys.publish(msg, ...)
(2) 全局消息订阅:sys.subscribe(msg, msg_cbfunc)
(3) 全局消息取消订阅:sys.unsubscribe(msg, msg_cbfunc)
(4) 全局消息阻塞等待(只能在task中使用):sys.waitUntil(msg, timeout)


sys核心库提供的定向消息管理功能有以下几种
(1) 定向消息发布:sys.sendMsg(task_name, msg, arg2, arg3, arg4)
(2) 定向消息阻塞等待(只能在task中使用):sys.waitMsg(task_name, msg, timeout)
(3) 定向消息清除:sys.cleanMsg(task_name)

3、timer:LuatOS软件定时器功能,有以下几种:

(1) 单次定时器创建并且启动:sys.timerStart(cbfunc, timeout, ...)
(2) 循环定时器创建并且启动:sys.timerLoopStart(cbfunc, timeout, ...)
(3) 单个定时器停止并且删除:sys.timerStop(timer_id)
(4) 单个定时器停止并且删除:sys.timerStop(cbfunc, ...)
(5) 多个定时器停止并且删除:sys.timerStopAll(cbfunc)


(6) 阻塞等待一段时间(只能在task中使用):sys.wait(timeout)
(7) 阻塞等待全局消息或者阻塞等待一段时间(只能在task中使用):sys.waitUntil(msg, timeout)
(8) 阻塞等待定向消息或者阻塞等待一段时间(只能在task中使用):sys.waitMsg(task_name, msg, timeout)

4、sys.run():LuatOS调度器,负责整个LuatOS应用脚本程序的调度和管理,主要功能包括:

(1) task的调度和管理
(2) message的调度和管理
(3) timer的调度和管理

注意事项:

1、在早期的版本中,LuatOS运行框架拆分为sys和sysplus两个核心库
   当前状态下,sysplus核心库的所有功能已经合并到sys核心库中,只使用sys核心库即可
   如果你写的旧代码使用了sysplus核心库,也没有关系,不会报错,仍然可以正常使用;
   推荐后续新开发的代码都仅使用sys核心库,之前的sysplus应用代码,直接将sysplus这几个字符替换为sys即可;
   例如sysplus.taskInitEx替换为sys.taskInitEx

2、在早期的版本中,需要在Lua应用脚本中主动写代码require "sys",才能加载sys核心库
   当前状态下,LuatOS内核固件运行起来之后,会自动加载sys核心库,不需要在Lua应用脚本中写代码require "sys"来加载;
   当然,如果你要在Lua应用脚本中写代码require "sys"也不会报错,只是这行代码是冗余代码,不会有什么实际作用;

二、核心示例

1、核心示例是指:使用本库文件提供的核心API,开发的基础业务逻辑的演示代码;

2、核心示例的作用是:帮助开发者快速理解如何使用本库,所以核心示例的逻辑都比较简单;

3、更加完整和详细的demo,请参考 LuatOS仓库 中各个产品目录下的demo/luatos_framework

--[[
本核心示例的的业务逻辑为:
1、创建并且启动一个高级task,task名称为"wait_msg_extask",在task的任务处理函数内及时接收发送给自己的定向消息"SEND_DATA_REQ";
2、创建并且启动一个基础task,在task的任务处理函数内及时接收全局消息"SEND_DATA_REQ";
3、创建一个基础task,每隔1秒钟发布一条全局消息"SEND_DATA_REQ",每隔2秒钟向名称为"wait_msg_extask"的task发送一条定向消息"SEND_DATA_REQ";
]]

-- wait_msg_extask任务处理函数的业务逻辑为:
-- 循环等待其他模块发给自己的定向消息"SEND_DATA_REQ",每次等待超时时长为1秒钟
local function wait_msg_extask()
    local msg
    while true do
        msg = sys.waitMsg("wait_msg_extask", "SEND_DATA_REQ", 1000)
        -- 收到"SEND_DATA_REQ"消息
        if msg then
            log.info("wait_msg_extask", msg[1], msg[2], msg[3], msg[4])
        -- 1秒超时,没有收到"SEND_DATA_REQ"消息
        else
            log.info("wait_msg_extask", "timeout")
        end
    end
end

-- send_msg_task任务处理函数的业务逻辑为:
-- 每隔1秒钟发布一条全局消息
-- 每隔2秒钟发送一条定向消息
local function send_msg_task()
    local count = 0

    while true do
        -- 计数值加1
        count = count+1

        -- 延时等待1秒
        sys.wait(1000)

        -- 发布一条全局消息
        -- 消息名称为"SEND_DATA_REQ"
        -- 消息携带两个参数:
        -- 第一个参数是"from task"
        -- 第二个参数是number类型的count
        sys.publish("SEND_DATA_REQ", "from task", count)

        -- 延时等待2秒
        sys.wait(2000)

        -- 发布一条定向消息到名称为"wait_msg_extask"的高级task
        -- 消息名称为"SEND_DATA_REQ"
        -- 消息携带两个参数:
        -- 第一个参数是"from task"
        -- 第二个参数是number类型的count
        sys.sendMsg("wait_msg_extask", "SEND_DATA_REQ", "from task", count)
    end
end

-- 创建并且启动一个高级task,task名称为"wait_msg_extask"
-- 运行这个task的任务处理函数wait_msg_extask
sys.taskInitEx(wait_msg_extask, "wait_msg_extask")

-- 创建并且启动一个基础task,运行task的任务处理函数send_msg_task
sys.taskInit(send_msg_task)

三、常量详解

核心库常量,顾名思义是由合宙LuatOS内核固件中定义的、不可重新赋值或修改的固定值,在脚本代码中不需要声明,可直接调用;

sys核心库没有常量。

四、task函数详解

sys.taskInit(task_func, ...)

功能

创建并且启动运行一个基础task;

注意事项

可以在能够执行到的任意代码位置使用此函数;

在LuatOS中,对创建的task数量没有特别限制,只要ram够用,可以一直创建;

参数

task_func

参数含义:task的处理函数
数据类型:function
取值范围:任意有效的函数名都行;
是否必选:必须传入此参数;
注意事项:
    特别需要注意传入的函数名的作用域,如果使用不当,很可能会使用一个无效的函数名;
    如下代码就是一个典型的错误示例
    -- 此处sys.taskInit(led_task_func)中使用的led_task_func并不是一个function类型,而是nil类型;    
    -- 因为下文定义的函数led_task_func,没办法在其上部去使用;
    -- 任何函数都要先定义后使用,不允许先使用后定义
    sys.taskInit(led_task_func)

    local function led_task_func()
        -- 此处省略了代码
    end
参数示例:
    如下方所示,定义了一个函数led_task_funcled_task_func就可以做为此参数传入
    local function led_task_func()
        -- 此处省略了代码
    end

    sys.taskInit(led_task_func)

...

参数含义:task的处理函数携带的可变参数
数据类型:任意数据类型;
取值范围:无特别限制;
是否必选:可选传入此参数;
注意事项:...Lua语言的一种语法,表示可变参数,参数数量可以是0个,1个,2个,...,多个;
参数示例:
    不传入,或者boolean类型的true,或者number类型的2,或者string类型的"led",或者table类型的{name="LuatOS", password="123456"}等等等等;
    总之,任何数据类型的任何自定义内容都行;

返回值

local task_object = sys.taskInit(task_func, ...)

有一个返回值task_object

task_object

含义说明:创建的task对象;如果为thread类型,表示创建成功;如果为nil类型,表示创建失败;
数据类型:thread或者nil
取值范围:无特别限制;
注意事项:虽然这个函数有返回值,但是这个返回值在整个LuatOS系统中,没有应用场景,所以不用深入了解这个返回值的用途;

示例

-- led task的任务处理函数
local function led_task_func()
    local count = 0
    while true do
        count = count + 1
        log.info("led_task_func", "运行中,计数:", count)
        -- 等待500ms
        sys.wait(500)  
    end
end

-- 创建并启动一个task
-- 运行这个task的任务处理函数led_task_func
sys.taskInit(led_task_func)

sys.taskInitEx(task_func, task_name, non_targeted_msg_cbfunc, ...)

功能

创建并且启动运行一个高级task;

注意事项

可以在能够执行到的任意代码位置使用此函数;

高级task的定义:参考本文第一章节的描述;

在LuatOS中,对创建的task数量没有特别限制,只要ram够用,可以一直创建;

参数

task_func

参数含义:task的处理函数
数据类型:function
取值范围:任意有效的函数名都行;
是否必选:必须传入此参数;
注意事项:
    特别需要注意传入的函数名的作用域,如果使用不当,很可能会使用一个无效的函数名;
    如下代码就是一个典型的错误示例

    -- 此处sys.taskInitEx(led_task_func, "LED_TASK")中使用的led_task_func并不是一个function类型,而是nil类型;    
    -- 因为下文定义的函数led_task_func,没办法在其上部去使用;
    -- 任何函数都要先定义后使用,不允许先使用后定义
    sys.taskInitEx(led_task_func, "LED_TASK")

    local function led_task_func()
        -- 此处省略了代码
    end
参数示例:
    如下所示,定义了一个函数led_task_funcled_task_func就可以做为此参数传入
    local function led_task_func()
        -- 此处省略了代码
    end

    sys.taskInitEx(led_task_func, "LED_TASK")

task_name

参数含义:task的名称
数据类型:推荐string类型(虽然number类型也行,但是不好理解,不要使用);
取值范围:任意string类型的字符串都行,无特别限制;
是否必选:必须传入此参数;
注意事项:在一个的LuatOS项目中,创建的所有高级task的task_name不能重复,目前在核心库中没有检查是否重复,需要用户自行保证;
参数示例:"LED_TASK""GPIO_TASK"等任意自定义的字符串;

non_targeted_msg_cbfunc

参数含义:
    task的处理函数中使用sys.waitMsg(task_name, msg, timeout)接口阻塞等待msg消息时,接收到msg之外的其他消息时的回调函数
    称之为:非目标消息回调函数;
    回调函数的格式为:

    -- 处理sys.waitMsg(task_name, msg, timeout)接口阻塞等待时,接收到的msg之外的消息
    function non_targeted_msg_cbfunc(msg)
        -- msg[1]为消息名,msg[2]为消息携带的第一个参数,msg[3]为消息携带的第二个参数,msg[4]为消息携带的第三个参数
        -- msg[1], msg[2], msg[3], msg[4] 分别对应sys.sendMsg(task_name, msg, arg2, arg3, arg4)中的 msg, arg2, arg3, arg4
        log.info("non_targeted_msg_cbfunc", msg[1], msg[2], msg[3], msg[4])
    end

    回调函数是在task之外的业务逻辑中被执行的    
    在回调函数内部无法使用sys.wait(timeout)sys.waitUntil(msg, timeout)sys.waitMsg(task_name, msg, timeout)等必须用在task中的函数
数据类型:function或者nil
取值范围:无特别限制;
是否必选:可选传入此参数;
注意事项:
    特别需要注意传入的函数名的作用域,如果使用不当,很可能会使用一个无效的函数名;
    如下代码就是一个典型的错误示例

    -- 此处sys.taskInitEx(led_task_func, "LED_TASK", led_task_cbfunc)中使用的led_task_cbfunc并不是一个function类型,而是nil类型;    
    -- 因为下文定义的函数led_task_cbfunc,没办法在其上部去使用;
    -- 任何函数都要先定义后使用,不允许先使用后定义
    local function led_task_func()
        -- 此处省略了代码
    end

    sys.taskInitEx(led_task_func, "LED_TASK", led_task_cbfunc)

    local function led_task_cbfunc(msg)
        -- 此处省略了代码
    end
参数示例:
    如下所示,定义了一个函数led_task_cbfuncled_task_cbfunc就可以做为此参数传入
    local function led_task_func()
        -- 此处省略了代码
    end       

    local function led_task_cbfunc(msg)
        -- 此处省略了代码
    end

    sys.taskInitEx(led_task_func, "LED_TASK", led_task_cbfunc)

...

参数含义:task的处理函数携带的可变参数
数据类型:任意数据类型;
取值范围:无特别限制;
是否必选:可选传入此参数;
注意事项:...Lua语言的一种语法,表示可变参数,参数数量可以是0个,1个,2个,...,多个;
参数示例:
    不传入,或者boolean类型的true,或者number类型的2,或者string类型的"led",或者table类型的{name="LuatOS", password="123456"}等等等等;
    总之,任何数据类型的任何自定义内容都行;

返回值

local task_object = sys.taskInitEx(task_func, task_name, non_targeted_msg_cbfunc, ...)

有一个返回值task_object

task_object

含义说明:表示创建的task对象;如果为thread类型,表示创建成功;如果为nil类型,表示创建失败;
数据类型:thread或者nil
取值范围:无特别限制;
注意事项:虽然这个函数有返回值,但是这个返回值在整个LuatOS系统中,没有应用场景,所以不用深入了解这个返回值的用途;

示例

-- led task的非目标消息回调函数
local function led_task_cbfunc(msg)
    -- msg[1]为消息名,msg[2]为消息携带的第一个参数,msg[3]为消息携带的第二个参数,msg[4]为消息携带的第三个参数
    -- msg[1], msg[2], msg[3], msg[4] 分别对应sys.sendMsg(task_name, msg, arg2, arg3, arg4)中的 msg, arg2, arg3, arg4
    log.info("led_task_cbfunc", msg[1], msg[2], msg[3], msg[4])
end

-- led task的任务处理函数
local function led_task_func()
    local count = 0
    while true do
        count = count + 1
        log.info("led_task_func", "运行中,计数:", count)
        -- 等待500ms
        sys.wait(500)  
    end
end

-- 创建并启动一个task,task的处理函数为led_task_func,task的名称为"LED_TASK"
-- task的非目标消息的回调函数为led_task_cbfunc
-- 运行这个task的任务处理函数led_task_func
sys.taskInit(led_task_func, "LED_TASK", led_task_cbfunc)

sys.taskDel(task_name)

功能

删除一个高级task对应的管理表资源;

注意事项

可以在能够执行到的任意代码位置使用此函数;

高级task的定义:参考本文第一章节的描述;

此函数仅仅删除一个高级task对应的管理表资源,并不能删除一个高级task;

一般来说,只有如下一个场景需要用到这个函数:

在高级task的任务处理函数中,如果函数执行结束,需要退出,在退出前执行sys.taskDel(task_name)

高级task的任务处理函数执行结束退出后,这个task就消亡了,消亡后这个task_name绑定的资源也没用处了;

所以在退出之前执行sys.taskDel(task_name),释放资源,防止出现内存泄漏;

参数

task_name

参数含义:task的名称,和sys.taskInitEx(task_func, task_name, non_targeted_msg_cbfunc, ...)中的task_name保持一致
数据类型:推荐string类型(虽然number类型也行,但是不好理解,不要使用);
取值范围:任意string类型的字符串都行,无特别限制;
是否必选:必须传入此参数;
注意事项:暂无;
参数示例:"LED_TASK""GPIO_TASK"等任意自定义的字符串;

返回值

nil

示例

以下代码片段演示一个task的处理函数在函数末尾退出前执行sys.taskDel函数的场景

-- led task的任务处理函数
local function led_task_func()
    log.info("led_task_func enter")

    -- 等待500ms
    sys.wait(500)

    log.info("led_task_func exit")

    sys.cleanMsg("LED_TASK")
    sys.taskDel("LED_TASK")
end

-- 创建并启动一个task,task的处理函数为led_task_func,task的名称为"LED_TASK"
-- 运行这个task的任务处理函数led_task_func
sys.taskInit(led_task_func, "LED_TASK")

以下代码片段演示一个task的处理函数在两种场景下执行sys.taskDel函数:

1、在中途的一个分支执行return语句,退出处理函数前,执行sys.taskDel函数

2、处理函数末尾退出前,执行sys.taskDel函数

-- led task的任务处理函数
local function led_task_func()
    -- 阻塞等待"IP_READY"消息,超时时间为5000毫秒
    local result = sys.waitUntil("IP_READY", 5000)

    -- 如果收到了"IP_READY"消息
    if result then
        sys.cleanMsg("LED_TASK")        
        sys.taskDel("LED_TASK")
        return
    end

    sys.publish("LED_TASK_WAIT_TIMEOUT")

    sys.cleanMsg("LED_TASK")
    sys.taskDel("LED_TASK")
end

-- 创建并启动一个task,task的处理函数为led_task_func,task的名称为"LED_TASK"
-- 运行这个task的任务处理函数led_task_func
sys.taskInit(led_task_func, "LED_TASK", )

五、msg函数详解

sys.publish(msg, ...)

功能

发布一个全局消息;

注意事项

全局消息的定义:参考本文第一章节的描述;

可以在能够执行到的任意代码位置使用此函数;

sys.publish(msg, ...)是全局消息的生产者,全局消息有生产就会有消费,不然消息就没有存在的意义了;

有两个接口可以注册全局消息的消费者:

1、一个是sys.subscribe(msg, msg_cbfunc)中注册的msg_cbfunc消息回调函数;

2、一个是sys.waitUntil(msg, timeout)所在的task;

所以全局消息的生产者和消费者的使用组合,有以下两种:

1、sys.publish(msg, ...) 和 sys.subscribe(msg, msg_cbfunc)

在sys.publish(msg, ...)之前,必须使用sys.subscribe(msg, msg_cbfunc)注册消息回调函数;

这样才能保证发布的msg消息可以被msg_cbfunc消息回调函数处理;

2、sys.publish(msg, ...) 和 sys.waitUntil(msg, timeout)

在sys.publish(msg, ...)之前,必须保证task正在sys.waitUntil(msg, timeout)代码处,处于阻塞等待状态;

这样才能保证发布的msg消息可以被task处理;

参数

msg

参数含义:消息的名称;
数据类型:推荐string类型(虽然number类型也行,但是不好理解,不要使用);
取值范围:任意string类型的字符串都行,无特别限制;
是否必选:必须传入此参数;
注意事项:暂无;
参数示例:"SEND_DATA_REQ"等任意自定义的字符串;

...

参数含义:消息携带的可变参数;
数据类型:任意数据类型;
取值范围:无特别限制;
是否必选:可选传入此参数;
注意事项:...Lua语言的一种语法,表示可变参数,参数数量可以是0个,1个,2个,...,多个;
参数示例:
    不传入,或者boolean类型的true,或者number类型的2,或者string类型的"led",或者table类型的{name="LuatOS", password="123456"}等等等等;
    总之,任何数据类型的任何自定义内容都行;

返回值

nil

示例

-- 发布一条全局消息"SEND_DATA_REQ",携带一个参数"123456"
sys.publish("SEND_DATA_REQ", "123456")

sys.subscribe(msg, msg_cbfunc)

功能

订阅一个全局消息的回调函数;

注意事项

全局消息的定义:参考本文第一章节的描述;

可以在能够执行到的任意代码位置使用此函数;

sys.publish(msg, ...) 和 sys.subscribe(msg, msg_cbfunc)配合使用时:

在sys.publish(msg, ...)之前,必须使用sys.subscribe(msg, msg_cbfunc)注册消息回调函数;

这样才能保证发布的msg消息可以被msg_cbfunc消息回调函数处理;

同一个全局消息msg,可以多次调用sys.subscribe(msg, msg_cbfunc)订阅多个不同的回调函数;

参数

msg

参数含义:全局消息的名称,和sys.publish(msg, ...)中的msg保持一致
数据类型:推荐string类型(虽然number类型也行,但是不好理解,不要使用);
取值范围:任意string类型的字符串都行,无特别限制;
是否必选:必须传入此参数;
注意事项:暂无;
参数示例:"SEND_DATA_REQ"等任意自定义的字符串;

msg_cbfunc

参数含义:
    全局消息msg的回调函数     
    回调函数的格式有两种:

    第一种:使用...来接收全局消息携带的参数
    function msg_cbfunc(...)
        -- ...是Lua语言的一种语法,表示可变参数,参数数量可以是0个,1个,2个,...,多个;
        -- 如下代码所示,args[1]表示可变参数的第一个参数,args[2]表示可变参数的第二个参数,...
        local args = {...}
        log.info("msg_cbfunc", args[1], args[2], args[3])
    end

    第二种:使用具体的形参名来接收全局消息携带的参数,形参的个数只要不少于sys.publish(msg, ...)...的参数个数就行
    function msg_cbfunc(arg1, arg2, arg3)
        log.info("msg_cbfunc", arg1, arg2, arg3)
    end

    回调函数是在task之外的业务逻辑中被执行的    
    在回调函数内部无法使用sys.wait(timeout)sys.waitUntil(msg, timeout)sys.waitMsg(task_name, msg, timeout)等必须用在task中的函数
数据类型:function
取值范围:无特别限制;
是否必选:必须传入此参数;
注意事项:
    特别需要注意传入的函数名的作用域,如果使用不当,很可能会使用一个无效的函数名;
    如下代码就是一个典型的错误示例

    -- 此处sys.subscribe("SEND_DATA_REQ", send_data_req_cbfunc)中使用的send_data_req_cbfunc并不是一个function类型,而是nil类型;    
    -- 因为下文定义的函数send_data_req_cbfunc,没办法在其上部去使用;
    -- 任何函数都要先定义后使用,不允许先使用后定义    
    sys.subscribe("SEND_DATA_REQ", send_data_req_cbfunc)

    local function send_data_req_cbfunc()
        -- 此处省略了代码
    end
参数示例:
    如下所示,定义了一个函数send_data_req_cbfuncsend_data_req_cbfunc就可以做为此参数传入
    local function send_data_req_cbfunc()
        -- 此处省略了代码
    end

    sys.subscribe("SEND_DATA_REQ", send_data_req_cbfunc)

返回值

nil

示例

local function send_data_req_cbfunc1(data, tag)
    -- 此处的data为"123456",tag为"LuatOS"
    log.info("send_data_req_cbfunc1", data, tag)
end

local function send_data_req_cbfunc2(...)
    local args = {...}
    -- 此处的args[1]为"123456",args[2]为"LuatOS"
    log.info("send_data_req_cbfunc2", args[1] args[2])
end

sys.subscribe("SEND_DATA_REQ", send_data_req_cbfunc1)
sys.subscribe("SEND_DATA_REQ", send_data_req_cbfunc2)

-- 发布一条全局消息"SEND_DATA_REQ",携带的第一个参数为"123456",携带的第二个参数为"LuatOS"
sys.publish("SEND_DATA_REQ", "123456", "LuatOS")

sys.unsubscribe(msg, msg_cbfunc)

功能

取消订阅一个全局消息的回调函数;

注意事项

全局消息的定义:参考本文第一章节的描述;

可以在能够执行到的任意代码位置使用此函数;

参数

msg

参数含义:全局消息的名称,和sys.subscribe(msg, msg_cbfunc)中的msg保持一致
数据类型:推荐string类型(虽然number类型也行,但是不好理解,不要使用);
取值范围:任意string类型的字符串都行,无特别限制;
是否必选:必须传入此参数;
注意事项:暂无;
参数示例:"SEND_DATA_REQ"等任意自定义的字符串;

msg_cbfunc

参数含义:
    全局消息msg的回调函数,和sys.subscribe(msg, msg_cbfunc)中的msg_cbfunc保持一致     
    回调函数的格式有两种:

    第一种:使用...来接收全局消息携带的参数
    function msg_cbfunc(...)
        -- ...是Lua语言的一种语法,表示可变参数,参数数量可以是0个,1个,2个,...,多个;
        -- 如下代码所示,args[1]表示可变参数的第一个参数,args[2]表示可变参数的第二个参数,...
        local args = {...}
        log.info("msg_cbfunc", args[1], args[2], args[3])
    end

    第二种:使用具体的形参名来接收全局消息携带的参数,形参的个数只要不少于sys.publish(msg, ...)...的参数个数就行
    function msg_cbfunc(arg1, arg2, arg3)
        log.info("msg_cbfunc", arg1, arg2, arg3)
    end
数据类型:function
取值范围:无特别限制;
是否必选:必须传入此参数;
注意事项:
    特别需要注意传入的函数名的作用域,如果使用不当,很可能会使用一个无效的函数名;
    如下代码就是一个典型的错误示例

    -- 此处sys.unsubscribe("SEND_DATA_REQ", send_data_req_cbfunc)中使用的send_data_req_cbfunc并不是一个function类型,而是nil类型;    
    -- 因为下文定义的函数send_data_req_cbfunc,没办法在其上部去使用;
    -- 任何函数都要先定义后使用,不允许先使用后定义

    sys.unsubscribe("SEND_DATA_REQ", send_data_req_cbfunc)

    local function send_data_req_cbfunc()
        -- 此处省略了代码
    end
参数示例:
    如下所示,定义了一个函数send_data_req_cbfuncsend_data_req_cbfunc就可以做为此参数传入
    local function send_data_req_cbfunc()
        -- 此处省略了代码
    end

    sys.unsubscribe("SEND_DATA_REQ", send_data_req_cbfunc)

返回值

nil

示例

local function send_data_req_cbfunc1(data, tag)
    -- 此处的data为"123456",tag为"LuatOS"
    log.info("send_data_req_cbfunc1", data, tag)
end

local function send_data_req_cbfunc2(...)
    local args = {...}
    -- 此处的args[1]为"123456",args[2]为"LuatOS"
    log.info("send_data_req_cbfunc2", args[1] args[2])
end

sys.subscribe("SEND_DATA_REQ", send_data_req_cbfunc1)
sys.subscribe("SEND_DATA_REQ", send_data_req_cbfunc2)

-- 执行下面这行代码后
-- SEND_DATA_REQ"的回调函数send_data_req_cbfunc1会被清除
-- 本代码片段中"SEND_DATA_REQ"只有send_data_req_cbfunc2这一个回调函数了
sys.unsubscribe("SEND_DATA_REQ", send_data_req_cbfunc1)

-- 发布一条全局消息"SEND_DATA_REQ",携带的第一个参数为"123456",携带的第二个参数为"LuatOS"
sys.publish("SEND_DATA_REQ", "123456", "LuatOS")

sys.waitUntil(msg, timeout)

功能

在task中阻塞等待一个全局消息;

注意事项

全局消息的定义:参考本文第一章节的描述;

基础task和高级task的定义:参考本文第一章节的描述;

只能在基础task和高级task处理函数的业务逻辑中使用此函数;

sys.publish(msg, ...) 和 sys.waitUntil(msg, timeout)配合使用时:

在sys.publish(msg, ...)之前,必须保证task正在sys.waitUntil(msg, timeout)代码处,处于阻塞等待状态;

这样才能保证发布的msg消息可以被task处理;

同一个全局消息msg,可以被多个正在sys.waitUntil(msg, timeout)代码处阻塞等待的task处理;

参数

msg

参数含义:全局消息的名称,和sys.publish(msg, ...)中的msg保持一致
数据类型:推荐string类型(虽然number类型也行,但是不好理解,不要使用);
取值范围:任意string类型的字符串都行,无特别限制;
是否必选:必须传入此参数;
注意事项:暂无;
参数示例:"SEND_DATA_REQ"等任意自定义的字符串;

timeout

参数含义:阻塞等待全局消息msg的超时时长,单位毫秒;
数据类型:number或者nil
取值范围:
    大于等于1,小于等于0x7FFFFFFF,之间的所有正整数;最大时长0x7FFFFFFF毫秒  596小时  24.85天;
    如果为nil,表示一直阻塞等待全局消息,不会超时;
是否必选:可选传入此参数;
注意事项:
    此处的超时机制基于软件定时器实现,受系统负载、任务数量、消息堆积、网络中断优先级最高等多因素影响,无法实现高精度;
    尤其是几个毫秒级别,几十毫秒级别,几百毫秒级别的超时时长,误差都比较大,秒级别以上的超时时长误差较小;
    例如:设置的1毫秒,可能要等几十毫秒;设置的几十毫秒,可能要等上百毫秒;设置的几百毫秒,可能要等几百几十毫秒;
    可以简单的认为会延迟几十毫秒左右,以此来评估超时时长精度是否可以满足业务需求;

    调用本接口时,如果传入了timeout参数,则sys核心库内部会自动创建并且运行一个软件定时器,超时时长到达后,会自动删除这个定时器;
    LuatOS应用程序中可用的软件定时器总数量为64个    
    注意控制自已应用程序中的同时运行的软件定时器总数量不要超过64个,否则创建新的定时器会返回失败;
参数示例:50100060000等;

返回值

local result, arg1, arg2, arg3, argN = sys.waitUntil(msg, timeout)

有数量不固定的返回值:

第一个返回值为result

剩余的返回值arg1, arg2, arg3, argN,表示可变数量的返回值,只有当第一个返回值result为true时,这些可变数量的返回值才有意义,和sys.publish(msg, ...)中...表示的可变参数一一对应

result

含义说明:阻塞等待的结果;true表示收到了msg消息false表示超时没有收到msg消息
数据类型:boolean
取值范围:true或者false
注意事项:暂无;

arg1, arg2, arg3, argN

含义说明:
    result为true时arg1, arg2, arg3, argN表示sys.publish(msg, ...)...可变参数,从前到后一一对应;
    result为false时arg1, arg2, arg3, argN全部都为nil,没有任何意义;
数据类型:
    result为true时arg1, arg2, arg3, argN的数据类型和sys.publish(msg, ...)...可变参数的数据类型,从前到后一一对应;
    result为false时arg1, arg2, arg3, argN全部都为nil类型
取值范围:无特别限制;
注意事项:暂无;

正确示例

local function send_data_task_func()
    -- 此处可以等到"SEND_DATA_REQ"消息,result为true, data为"123456",tag为"LuatOS"
    local result, data, tag = sys.waitUntil("SEND_DATA_REQ", 5000)
    log.info("send_data_task_func", result, data, tag)
end

local function send_data_extask_func()
    -- 此处接收不到"UNKNOWN_SEND_DATA_REQ"消息,因为没有发布这个消息,所以超时5秒后退出阻塞等待状态
    -- result为false, data为nil,tag为nil
    local result, data, tag = sys.waitUntil("UNKNOWN_SEND_DATA_REQ", 5000)
    log.info("send_data_extask_func", result, data, tag)

    sys.cleanMsg("EXTASK2")
    sys.taskDel("EXTASK2")
end

sys.taskInit(send_data_task_func)
sys.taskInitEx(send_data_extask_func, "EXTASK2")

-- 发布一条全局消息"SEND_DATA_REQ",携带的第一个参数为"123456",携带的第二个参数为"LuatOS"
sys.publish("SEND_DATA_REQ", "123456", "LuatOS")

错误示例

local function send_data_task_func()
    -- 此处阻塞等待1秒钟是一种典型的错误写法;
    -- sys.publish("SEND_DATA_REQ", "123456", "LuatOS")时,task在sys.wait(1000)代码处阻塞等待
    -- 会导致后续的sys.waitUntil("SEND_DATA_REQ", 5000)接收不到"SEND_DATA_REQ"消息
    sys.wait(1000)

    -- 此处接收不到"SEND_DATA_REQ"消息
    -- 因为在sys.publish("SEND_DATA_REQ", "123456", "LuatOS")时,task没有在sys.waitUntil("SEND_DATA_REQ", 5000)代码处阻塞等待
    -- 所以超时5秒后退出阻塞等待状态
    -- result为false, data为nil,tag为nil
    local result, data, tag = sys.waitUntil("SEND_DATA_REQ", 5000)
    log.info("send_data_task_func", result, data, tag)
end

local function send_data_extask_func()
    -- 此处阻塞等待1秒钟是一种典型的错误写法;
    -- sys.publish("SEND_DATA_REQ", "123456", "LuatOS")时,task在sys.wait(1000)代码处阻塞等待
    -- 会导致后续的sys.waitUntil("SEND_DATA_REQ", 5000)接收不到"SEND_DATA_REQ"消息
    sys.wait(1000)

    -- 此处接收不到"SEND_DATA_REQ"消息
    -- 因为在sys.publish("SEND_DATA_REQ", "123456", "LuatOS")时,task没有在sys.waitUntil("SEND_DATA_REQ", 5000)代码处阻塞等待
    -- 所以超时5秒后退出阻塞等待状态
    -- result为false, data为nil,tag为nil
    local result, data, tag = sys.waitUntil("SEND_DATA_REQ", 5000)
    log.info("send_data_extask_func", result, data, tag)

    sys.cleanMsg("EXTASK2")
    sys.taskDel("EXTASK2")
end

sys.taskInit(send_data_task_func)
sys.taskInitEx(send_data_extask_func, "EXTASK2")

-- 发布一条全局消息"SEND_DATA_REQ",携带的第一个参数为"123456",携带的第二个参数为"LuatOS"
sys.publish("SEND_DATA_REQ", "123456", "LuatOS")

sys.sendMsg(task_name, msg, arg2, arg3, arg4)

功能

向名称为task_name的task发布一个定向消息;

注意事项

定向消息的定义:参考本文第一章节的描述;

可以在能够执行到的任意代码位置使用此函数;

sys.sendMsg(task_name, msg, arg2, arg3, arg4)是定向消息的生产者,定向消息有生产就会有消费,不然消息就没有存在的意义了;

sys.waitMsg(task_name, msg, timeout)所在的task是定向消息的消费者;

sys.sendMsg(task_name, msg, arg2, arg3, arg4) 和 sys.waitMsg(task_name, msg, timeout)配合使用;

在sys.sendMsg(task_name, msg, arg2, arg3, arg4)之前,需要保证名称为task_name的task已经被创建,否则定向消息也会丢失;

参数

task_name

参数含义:task的名称,和sys.taskInitEx(task_func, task_name, non_targeted_msg_cbfunc, ...)中的task_name保持一致
数据类型:推荐string类型(虽然number类型也行,但是不好理解,不要使用);
取值范围:任意string类型的字符串都行,无特别限制;
是否必选:必须传入此参数;
注意事项:暂无;
参数示例:"LED_TASK""GPIO_TASK"等任意自定义的字符串;

msg

参数含义:消息的名称;
数据类型:推荐string类型(虽然number类型也行,但是不好理解,不要使用);
取值范围:任意string类型的字符串都行,无特别限制;
是否必选:必须传入此参数;
注意事项:暂无;
参数示例:"SEND_DATA_REQ"等任意自定义的字符串;

arg2

参数含义:msg消息携带的第一个参数
数据类型:任意数据类型;
取值范围:无特别限制;
是否必选:可选传入此参数;
注意事项:暂无;
参数示例:
    不传入,或者boolean类型的true,或者number类型的2,或者string类型的"led",或者table类型的{name="LuatOS", password="123456"}等等等等;
    总之,任何数据类型的任何自定义内容都行;

arg3

参数含义:msg消息携带的第二个参数
数据类型:任意数据类型;
取值范围:无特别限制;
是否必选:可选传入此参数;
注意事项:暂无;
参数示例:
    不传入,或者boolean类型的true,或者number类型的2,或者string类型的"led",或者table类型的{name="LuatOS", password="123456"}等等等等;
    总之,任何数据类型的任何自定义内容都行;

arg4

参数含义:msg消息携带的第三个参数
数据类型:任意数据类型;
取值范围:无特别限制;
是否必选:可选传入此参数;
注意事项:暂无;
参数示例:
    不传入,或者boolean类型的true,或者number类型的2,或者string类型的"led",或者table类型的{name="LuatOS", password="123456"}等等等等;
    总之,任何数据类型的任何自定义内容都行;

返回值

local result = sys.sendMsg(task_name, msg, arg2, arg3, arg4)

result

含义说明:定向消息发布结果,成功返回true,失败返回false
数据类型:boolean
取值范围:true或者false
注意事项:暂无;

示例

-- 向名称为"TCP_CLIENT_TASK"的task发布一条定向消息"SEND_DATA_REQ",携带一个参数"123456"
sys.sendMsg("TCP_CLIENT_TASK", "SEND_DATA_REQ", "123456")

sys.waitMsg(task_name, msg, timeout)

功能

在task中阻塞等待名称为task_name的task定向消息;

注意事项

定向消息的定义:参考本文第一章节的描述;

高级task的定义:参考本文第一章节的描述;

只能在高级task处理函数的业务逻辑中使用此函数;

sys.sendMsg(task_name, msg, arg2, arg3, arg4)是定向消息的生产者,定向消息有生产就会有消费,不然消息就没有存在的意义了;

sys.waitMsg(task_name, msg, timeout)所在的task是定向消息的消费者;

sys.sendMsg(task_name, msg, arg2, arg3, arg4) 和 sys.waitMsg(task_name, msg, timeout)配合使用;

在sys.sendMsg(task_name, msg, arg2, arg3, arg4)之前,需要保证名称为task_name的task已经被创建,否则定向消息也会丢失;

参数

task_name

参数含义:task的名称,和sys.taskInitEx(task_func, task_name, non_targeted_msg_cbfunc, ...)中的task_name保持一致
数据类型:推荐string类型(虽然number类型也行,但是不好理解,不要使用);
取值范围:任意string类型的字符串都行,无特别限制;
是否必选:必须传入此参数;
注意事项:暂无;
参数示例:"LED_TASK""GPIO_TASK"等任意自定义的字符串;

msg

参数含义:消息的名称,和sys.sendMsg(task_name, msg, arg2, arg3, arg4)中的msg保持一致
数据类型:推荐string类型(虽然number类型也行,但是不好理解,不要使用);
取值范围:任意string类型的字符串都行,无特别限制;
是否必选:必须传入此参数;
注意事项:暂无;
参数示例:"SEND_DATA_REQ"等任意自定义的字符串;

timeout

参数含义:阻塞等待定向消息msg的超时时长,单位毫秒,nil表示一直阻塞等待
数据类型:number或者nil
取值范围:
    大于等于1,小于等于0x7FFFFFFF,之间的所有正整数;最大时长0x7FFFFFFF毫秒  596小时  24.85天;
    如果为nil,表示一直阻塞等待全局消息,不会超时;
是否必选:可选传入此参数;
注意事项:
    此处的超时机制基于软件定时器实现,受系统负载、任务数量、消息堆积、网络中断优先级最高等多因素影响,无法实现高精度;
    尤其是几个毫秒级别,几十毫秒级别,几百毫秒级别的超时时长,误差都比较大,秒级别以上的超时时长误差较小;
    例如:设置的1毫秒,可能要等几十毫秒;设置的几十毫秒,可能要等上百毫秒;设置的几百毫秒,可能要等几百几十毫秒;
    可以简单的认为会延迟几十毫秒左右,以此来评估超时时长精度是否可以满足业务需求;


    调用本接口时,如果传入了timeout参数,则sys核心库内部会自动创建并且运行一个软件定时器,超时时长到达后,会自动删除这个定时器;
    LuatOS应用程序中可用的软件定时器总数量为64个    
    注意控制自已应用程序中的同时运行的软件定时器总数量不要超过64个,否则创建新的定时器会返回失败;
参数示例:50100060000等;

返回值

local message = sys.waitMsg(task_name, msg, timeout)

有一个返回值为message

message

含义说明:
    阻塞等待的结果;table类型表示接收到msg消息false表示超时没有收到msg消息
    当接收到msg消息时message[1],message[2],message[3],message[4]sys.sendMsg(task_name, msg, arg2, arg3, arg4)中的 msg, arg2, arg3, arg4 一一对应;
    当超时没有收到msg消息时message为false
数据类型:table或者boolean
取值范围:无特别限制;
注意事项:暂无;

示例

local function send_data_extask1_func()
    -- 此处可以等到"SEND_DATA_REQ"消息,message为table类型
    -- message[1]为"SEND_DATA_REQ",message[2]为"123456",message[3]为"LuatOS"
    local message = sys.waitMsg("EXTASK1", "SEND_DATA_REQ", 5000)

    if message then
        log.info("send_data_extask1_func", message[1], message[2], message[3])
    end

    sys.cleanMsg("EXTASK1")
    sys.taskDel("EXTASK1")
end

local function send_data_extask2_func()
    -- 此处等不到"UNKNOWN_SEND_DATA_REQ"消息,超时5秒退出阻塞状态,message为false
    local message = sys.waitMsg("EXTASK2", "UNKNOWN_SEND_DATA_REQ", 5000)

    if not message then
        log.info("send_data_extask2_func", "wait timeout")
    end

    sys.cleanMsg("EXTASK2")
    sys.taskDel("EXTASK2")
end

sys.taskInitEx(send_data_extask1_func, "EXTASK1")
sys.taskInitEx(send_data_extask2_func, "EXTASK2")

-- 向名称为"EXTASK1"的task发布一条定向消息"SEND_DATA_REQ",携带的第一个参数为"123456",携带的第二个参数为"LuatOS"
sys.sendMsg("EXTASK1", "SEND_DATA_REQ" "123456", "LuatOS")

sys.cleanMsg(task_name)

功能

清除名称为task_name的task的所有定向消息;

注意事项

定向消息的定义:参考本文第一章节的描述;

高级task的定义:参考本文第一章节的描述;

可以在能够执行到的任意代码位置使用此函数;

一般来说,有两种场景需要用到这个函数:

1、在高级task的任务处理函数中,如果函数执行结束,需要退出,在退出前执行sys.cleanMsg(task_name)

高级task的任务处理函数执行结束退出后,这个task就消亡了,消亡后这个task_name绑定的资源也没用处了;

所以在退出之前执行sys.cleanMsg(task_name),释放资源,防止出现内存泄漏;

2、根据自己的业务需求,如果某一时刻,不再需要处理高级task绑定的消息队列中的消息,执行sys.cleanMsg(task_name)

参数

task_name

参数含义:task的名称,和sys.taskInitEx(task_func, task_name, non_targeted_msg_cbfunc, ...)中的task_name保持一致
数据类型:推荐string类型(虽然number类型也行,但是不好理解,不要使用);
取值范围:任意string类型的字符串都行,无特别限制;
是否必选:必须传入此参数;
注意事项:暂无;
参数示例:"LED_TASK""GPIO_TASK"等任意自定义的字符串;

返回值

local result = sys.cleanMsg(task_name)

有一个返回值为result

result

含义说明:执行结果,true 表示成功,false 表示失败( task_name 不存在)
数据类型:boolean
取值范围:true 或者 false
注意事项:暂无;

示例

local function send_data_extask1_func()
    -- 此处可以等到"SEND_DATA_REQ"消息,message为table类型
    -- message[1]为"SEND_DATA_REQ",message[2]为"123456",message[3]为"LuatOS"
    local message = sys.waitMsg("EXTASK1", "SEND_DATA_REQ", 5000)

    if message then
        log.info("send_data_extask1_func", message[1], message[2], message[3])
    end

    sys.cleanMsg("EXTASK1")
    sys.taskDel("EXTASK1")
end

sys.taskInitEx(send_data_extask1_func, "EXTASK1")

-- 向名称为"EXTASK1"的task发布一条定向消息"SEND_DATA_REQ",携带的第一个参数为"123456",携带的第二个参数为"LuatOS"
sys.sendMsg("EXTASK1", "SEND_DATA_REQ" "123456", "LuatOS")
-- 此处演示的代码仅仅是从mqtt demo中摘录出来的不完整的代码片段
-- 可以用来理解sys.cleanMsg(TASK_NAME)的应用场景

local TASK_NAME = "MQTT_CLIENT_MAIN"

-- mqtt main task 的任务处理函数
local function mqtt_client_main_task_func()

    local msg

    while true do

        -- 清空此task绑定的消息队列中的未处理的消息
        sys.cleanMsg(TASK_NAME)

        -- 连接、断开连接、订阅、取消订阅、异常等各种事件的处理调度逻辑
        while true do
            -- 等待"MQTT_EVENT"消息
            msg = sys.waitMsg(TASK_NAME, "MQTT_EVENT")
            log.info("mqtt_client_main_task_func waitMsg", msg[2], msg[3], msg[4])

            -- connect连接结果
            if msg[2] == "CONNECT" then
            -- subscribe订阅结果
            elseif msg[2] == "SUBSCRIBE" then
            -- unsubscribe取消订阅成功
            elseif msg[2] == "UNSUBSCRIBE" then
            -- 需要主动关闭mqtt连接
            elseif msg[2] == "CLOSE" then
            -- 被动关闭了mqtt连接
            elseif msg[2] == "DISCONNECTED" then
                break
            -- 出现了其他异常
            elseif msg[2] == "ERROR" then
                break
            end
        end

        -- 清空此task绑定的消息队列中的未处理的消息
        sys.cleanMsg(TASK_NAME)

        -- 5秒后跳转到循环体开始位置,自动发起重连
        sys.wait(5000)
    end
end

--创建并且启动一个task
--运行这个task的处理函数mqtt_client_main_task_func
sys.taskInitEx(mqtt_client_main_task_func, TASK_NAME)

六、timer函数详解

sys.timerStart(cbfunc, timeout, ...)

功能

创建并且运行一个单次定时器;

注意事项

可以在能够执行到的任意代码位置使用此函数;

有两种方式可以唯一标识一个定时器:

1、定时器id;如果使用sys.timerStart(cbfunc, timeout, ...)创建定时器成功,会返回定时器id

2、定时器回调函数cbfunc和可变参数...,此种方式的说明如下:

如果cbfunc 和...相同,重复调用sys.timerStart(cbfunc, timeout, ...)接口创建并且运行定时器;

在 sys.timerStart内部会自动停止并且删除已经存在的重复定时器;

例如执行如下三行代码后:

sys.timerStart(led_on_timer_cbfunc, 1000, "red")

sys.timerStart(led_on_timer_cbfunc, 2000, "red")

sys.timerStart(led_on_timer_cbfunc, 3000, "red")

最后只有 sys.timerStart(led_on_timer_cbfunc, 3000, "red") 这个定时器在运行,前面创建的两个定时器都被自动删除了,没有完整运行;

参数

cbfunc

参数含义:
    定时器的回调函数,超时时长之后,回自动执行此函数;回调函数的格式为:
    使用具体的形参名来接收sys.timerStart(cbfunc, timeout, ...)创建定时器时的...可变参数    
    形参的个数只要不少于sys.timerStart(cbfunc, timeout, ...)...的参数个数就行

    function led_on_timer_cbfunc(arg1, arg2, arg3)
        log.info("led_on_timer_cbfunc", arg1, arg2, arg3)
    end

    回调函数是在task之外的业务逻辑中被执行的        
    在回调函数内部无法使用sys.wait(timeout)sys.waitUntil(msg, timeout)sys.waitMsg(task_name, msg, timeout)等必须用在task中的函数
数据类型:function
取值范围:无特别限制;
是否必选:必须传入此参数;
注意事项:
    特别需要注意传入的函数名的作用域,如果使用不当,很可能会使用一个无效的函数名;
    如下代码就是一个典型的错误示例

    -- 此处sys.timerStart(led_on_timer_cbfunc, 1000, 12)中使用的led_on_timer_cbfunc并不是一个function类型,而是nil类型;    
    -- 因为下文定义的函数led_on_timer_cbfunc,没办法在其上部去使用;
    -- 任何函数都要先定义后使用,不允许先使用后定义

    sys.timerStart(led_on_timer_cbfunc, 1000, 12)

    local function led_on_timer_cbfunc(gpio_id)
        -- 此处省略了代码
    end
参数示例:
    如下所示,定义了一个函数led_on_timer_cbfuncled_on_timer_cbfunc就可以做为此参数传入

    local function led_on_timer_cbfunc(gpio_id)
        -- 此处的gpio_id的值为12
        log.info("led_on_timer_cbfunc", gpio_id)
    end

    sys.timerStart(led_on_timer_cbfunc, 1000, 12)

timeout

参数含义:定时器的超时时长,单位毫秒;
数据类型:number
取值范围:
    大于等于1,小于等于0x7FFFFFFF,之间的所有正整数;最大时长0x7FFFFFFF毫秒  596小时  24.85天;
是否必选:必须传入此参数;
注意事项:
    此处的超时机制基于软件定时器实现,受系统负载、任务数量、消息堆积、网络中断优先级最高等多因素影响,无法实现高精度;
    尤其是几个毫秒级别,几十毫秒级别,几百毫秒级别的超时时长,误差都比较大,秒级别以上的超时时长误差较小;
    例如:设置的1毫秒,可能要等几十毫秒;设置的几十毫秒,可能要等上百毫秒;设置的几百毫秒,可能要等几百几十毫秒;
    可以简单的认为会延迟几十毫秒左右,以此来评估超时时长精度是否可以满足业务需求;


    调用本接口时,sys核心库内部会自动创建并且运行一个软件定时器,超时时长到达后,会自动删除这个定时器;
    LuatOS应用程序中可用的软件定时器总数量为64个        
    注意控制自已应用程序中的同时运行的软件定时器总数量不要超过64个,否则创建新的定时器会返回失败;
参数示例:50100060000等;

...

参数含义:定时器携带的可变参数,当定时器超时时长到达后,自动执行回调函数时,这些可变参数按照顺序传递给回调函数使用;
数据类型:任意数据类型;
取值范围:无特别限制;
是否必选:可选传入此参数;
注意事项:...Lua语言的一种语法,表示可变参数,参数数量可以是0个,1个,2个,...,多个;
参数示例:
    不传入,或者boolean类型的true,或者number类型的2,或者string类型的"led",或者table类型的{name="LuatOS", password="123456"}等等等等;
    总之,任何数据类型的任何自定义内容都行;

返回值

local timer_id = sys.timerStart(cbfunc, timeout, ...)

有一个返回值为timer_id

timer_id

含义说明:创建结果;如果是number类型,表示创建成功,timer_id就是定时器id;如果为nil,表示创建失败;
数据类型:number或者nil
取值范围:无特别限制;
注意事项:暂无;

示例

local function led_on_timer_cbfunc(color)
    log.info("led_on_timer_cbfunc", color)
end

-- 以下三行代码创建的是同一个定时器,当三行代码顺序执行结束后
-- 只有sys.timerStart(led_on_timer_cbfunc, 3000, "red")这一个定时器存在

-- 创建一个单次定时器,1秒后执行回调函数led_on_timer_cbfunc,"red"传递给回调函数
sys.timerStart(led_on_timer_cbfunc, 1000, "red")
-- 创建一个单次定时器,2秒后执行回调函数led_on_timer_cbfunc,"red"传递给回调函数
sys.timerStart(led_on_timer_cbfunc, 2000, "red")
-- 创建一个单次定时器,3秒后执行回调函数led_on_timer_cbfunc,"red"传递给回调函数
sys.timerStart(led_on_timer_cbfunc, 3000, "red")



-- 以下三行代码创建的是三个定时器,当三行代码顺序执行结束后
-- 三个定时器都在运行

-- 创建一个单次定时器,1秒后执行回调函数led_on_timer_cbfunc,"yellow"传递给回调函数
sys.timerStart(led_on_timer_cbfunc, 1000, "yellow")
-- 创建一个单次定时器,2秒后执行回调函数led_on_timer_cbfunc,"green"传递给回调函数
sys.timerStart(led_on_timer_cbfunc, 2000, "green")
-- 创建一个单次定时器,3秒后执行回调函数led_on_timer_cbfunc,"blue"传递给回调函数
sys.timerStart(led_on_timer_cbfunc, 3000, "blue")

sys.timerLoopStart(cbfunc, timeout, ...)

功能

创建并且运行一个循环定时器;

注意事项

可以在能够执行到的任意代码位置使用此函数;

有两种方式可以唯一标识一个定时器:

1、定时器id;如果使用sys.timerLoopStart(cbfunc, timeout, ...)创建定时器成功,会返回定时器id

2、定时器回调函数cbfunc和可变参数...,此种方式的说明如下:

如果cbfunc 和...相同,重复调用sys.timerLoopStart(cbfunc, timeout, ...)接口创建并且运行定时器;

在 sys.timerLoopStart内部会自动停止并且删除已经存在的重复定时器;

例如执行如下三行代码后:

sys.timerLoopStart(led_on_timer_cbfunc, 1000, "red")

sys.timerLoopStart(led_on_timer_cbfunc, 2000, "red")

sys.timerLoopStart(led_on_timer_cbfunc, 3000, "red")

最后只有 sys.timerLoopStart(led_on_timer_cbfunc, 3000, "red") 这个定时器在运行,前面创建的两个定时器都被自动删除了,没有完整运行;

参数

cbfunc

参数含义:
    定时器的回调函数,超时时长之后,回自动执行此函数;回调函数的格式为:
    使用具体的形参名来接收sys.timerLoopStart(cbfunc, timeout, ...)创建定时器时的...可变参数    
    形参的个数只要不少于sys.timerLoopStart(cbfunc, timeout, ...)...的参数个数就行

    function led_on_timer_cbfunc(arg1, arg2, arg3)
        log.info("led_on_timer_cbfunc", arg1, arg2, arg3)
    end

    回调函数是在task之外的业务逻辑中被执行的        
    在回调函数内部无法使用sys.wait(timeout)sys.waitUntil(msg, timeout)sys.waitMsg(task_name, msg, timeout)等必须用在task中的函数
数据类型:function
取值范围:无特别限制;
是否必选:必须传入此参数;
注意事项:
    特别需要注意传入的函数名的作用域,如果使用不当,很可能会使用一个无效的函数名;
    如下代码就是一个典型的错误示例

    -- 此处sys.timerLoopStart(led_on_timer_cbfunc, 1000, 12)中使用的led_on_timer_cbfunc并不是一个function类型,而是nil类型;    
    -- 因为下文定义的函数led_on_timer_cbfunc,没办法在其上部去使用;
    -- 任何函数都要先定义后使用,不允许先使用后定义

    sys.timerLoopStart(led_on_timer_cbfunc, 1000, 12)

    local function led_on_timer_cbfunc(gpio_id)
        -- 此处省略了代码
    end
参数示例:
    如下所示,定义了一个函数led_on_timer_cbfuncled_on_timer_cbfunc就可以做为此参数传入
    local function led_on_timer_cbfunc(gpio_id)
        -- 此处的gpio_id的值为12
        log.info("led_on_timer_cbfunc", gpio_id)
    end

    sys.timerLoopStart(led_on_timer_cbfunc, 1000, 12)

timeout

参数含义:定时器的超时时长,单位毫秒;
数据类型:number
取值范围:
    大于等于1,小于等于0x7FFFFFFF,之间的所有正整数;最大时长0x7FFFFFFF毫秒  596小时  24.85天;
是否必选:必须传入此参数;
注意事项:
    此处的超时机制基于软件定时器实现,受系统负载、任务数量、消息堆积、网络中断优先级最高等多因素影响,无法实现高精度;
    尤其是几个毫秒级别,几十毫秒级别,几百毫秒级别的超时时长,误差都比较大,秒级别以上的超时时长误差较小;
    例如:设置的1毫秒,可能要等几十毫秒;设置的几十毫秒,可能要等上百毫秒;设置的几百毫秒,可能要等几百几十毫秒;
    可以简单的认为会延迟几十毫秒左右,以此来评估超时时长精度是否可以满足业务需求;


    调用本接口时,sys核心库内部会自动创建并且运行一个软件定时器,循环运行;
    LuatOS应用程序中可用的软件定时器总数量为64个    
    注意控制自已应用程序中的同时运行的软件定时器总数量不要超过64个,否则创建新的定时器会返回失败;
参数示例:50100060000等;

...

参数含义:定时器携带的可变参数,当定时器超时时长到达后,自动执行回调函数时,这些可变参数按照顺序传递给回调函数使用;
数据类型:任意数据类型;
取值范围:无特别限制;
是否必选:可选传入此参数;
注意事项:...Lua语言的一种语法,表示可变参数,参数数量可以是0个,1个,2个,...,多个;
参数示例:
    不传入,或者boolean类型的true,或者number类型的2,或者string类型的"led",或者table类型的{name="LuatOS", password="123456"}等等等等;
    总之,任何数据类型的任何自定义内容都行;

返回值

local timer_id = sys.timerLoopStart(cbfunc, timeout, ...)

有一个返回值为timer_id

timer_id

含义说明:创建结果;如果是number类型,表示创建成功,timer_id就是定时器id;如果为nil,表示创建失败;
数据类型:number或者nil
取值范围:无特别限制;
注意事项:暂无;

示例

local function led_on_timer_cbfunc(color)
    log.info("led_on_timer_cbfunc", color)
end

-- 以下三行代码创建的是同一个定时器,当三行代码顺序执行结束后
-- 只有sys.timerLoopStart(led_on_timer_cbfunc, 3000, "red")这一个循环定时器存在

-- 创建一个单次定时器,1秒后执行回调函数led_on_timer_cbfunc,"red"传递给回调函数
sys.timerStart(led_on_timer_cbfunc, 1000, "red")
-- 创建一个循环定时器,每隔2秒后执行回调函数led_on_timer_cbfunc,"red"传递给回调函数
sys.timerLoopStart(led_on_timer_cbfunc, 2000, "red")
-- 创建一个循环定时器,每隔3秒后执行回调函数led_on_timer_cbfunc,"red"传递给回调函数
sys.timerLoopStart(led_on_timer_cbfunc, 3000, "red")



-- 以下三行代码创建的是三个定时器,当三行代码顺序执行结束后
-- 三个定时器都在运行

-- 创建一个循环定时器,每隔1秒后执行回调函数led_on_timer_cbfunc,"yellow"传递给回调函数
sys.timerLoopStart(led_on_timer_cbfunc, 1000, "yellow")
-- 创建一个单次定时器,2秒后执行回调函数led_on_timer_cbfunc,"green"传递给回调函数
sys.timerStart(led_on_timer_cbfunc, 2000, "green")
-- 创建一个循环定时器,每隔3秒后执行回调函数led_on_timer_cbfunc,"blue"传递给回调函数
sys.timerLoopStart(led_on_timer_cbfunc, 3000, "blue")

sys.timerStop(timer_id)

功能

根据定时器id停止运行并且删除一个定时器;

注意事项

可以在能够执行到的任意代码位置使用此函数;

有两种方式可以唯一标识一个定时器:

1、定时器id;如果使用sys.timerStart(cbfunc, timeout, ...)或者sys.timerLoopStart(cbfunc, timeout, ...)创建定时器成功,会返回定时器id

2、定时器回调函数cbfunc和可变参数...;

参数

timer_id

参数含义:定时器id,和sys.timerStart(cbfunc, timeout, ...)或者sys.timerLoopStart(cbfunc, timeout, ...)创建定时器成功后的返回值保持一致
数据类型:number
取值范围:无特别限制;
是否必选:必须传入此参数;
注意事项:暂无;
参数示例:
    如下所示,timer_id就可以做为此参数传入

    local function led_on_timer_cbfunc(gpio_id)
        -- 此处的gpio_id的值为12
        log.info("led_on_timer_cbfunc", gpio_id)
    end

    local timer_id = sys.timerLoopStart(led_on_timer_cbfunc, 1000, 12)

返回值

nil

示例

local function led_on_timer_cbfunc(color)
    log.info("led_on_timer_cbfunc", color)
end

-- 创建一个循环定时器,每隔1秒后执行回调函数led_on_timer_cbfunc,"yellow"传递给回调函数
local timer1_id = sys.timerLoopStart(led_on_timer_cbfunc, 1000, "yellow")
-- 创建一个单次定时器,2秒后执行回调函数led_on_timer_cbfunc,"green"传递给回调函数
local timer2_id = sys.timerStart(led_on_timer_cbfunc, 2000, "green")
-- 创建一个循环定时器,每隔3秒后执行回调函数led_on_timer_cbfunc,"blue"传递给回调函数
local timer3_id = sys.timerLoopStart(led_on_timer_cbfunc, 3000, "blue")

-- 停止并且删除timer1_id标识的第一个定时器
sys.timerStop(timer1_id)
-- 停止并且删除timer3_id标识的第三个定时器
sys.timerStop(timer3_id)

-- 至此,只有timer2_id所标识的定时器还在运行

sys.timerStop(cbfunc, ...)

功能

根据定时器的回调函数cbfunc和可变参数...停止运行并且删除一个定时器;

注意事项

可以在能够执行到的任意代码位置使用此函数;

有两种方式可以唯一标识一个定时器:

1、定时器id;如果使用sys.timerStart(cbfunc, timeout, ...)或者sys.timerLoopStart(cbfunc, timeout, ...)创建定时器成功,会返回定时器id;

2、定时器回调函数cbfunc和可变参数...;

参数

cbfunc

参数含义:定时器回调函数,和sys.timerStart(cbfunc, timeout, ...)或者sys.timerLoopStart(cbfunc, timeout, ...)创建定时器时的cbfunc保持一致
数据类型:function
取值范围:无特别限制;
是否必选:必须传入此参数;
注意事项:暂无;
参数示例:
    如下所示,led_on_timer_cbfunc就可以做为此参数传入

    local function led_on_timer_cbfunc(gpio_id)
        -- 此处的gpio_id的值为12
        log.info("led_on_timer_cbfunc", gpio_id)
    end

    sys.timerStart(led_on_timer_cbfunc, 1000, 12)

    sys.timerStop(led_on_timer_cbfunc, 12)

返回值

nil

示例

local function led_on_timer_cbfunc(color)
    log.info("led_on_timer_cbfunc", color)
end

-- 创建一个循环定时器,每隔1秒后执行回调函数led_on_timer_cbfunc,"yellow"传递给回调函数
sys.timerLoopStart(led_on_timer_cbfunc, 1000, "yellow")
-- 创建一个单次定时器,2秒后执行回调函数led_on_timer_cbfunc,"green"传递给回调函数
sys.timerStart(led_on_timer_cbfunc, 2000, "green")
-- 创建一个循环定时器,每隔3秒后执行回调函数led_on_timer_cbfunc,"blue"传递给回调函数
sys.timerLoopStart(led_on_timer_cbfunc, 3000, "blue")

-- 停止并且删除led_on_timer_cbfunc和"yellow"标识的第一个定时器
sys.timerStop(led_on_timer_cbfunc, "yellow")
-- 停止并且删除led_on_timer_cbfunc和"blue"标识的第一个定时器
sys.timerStop(led_on_timer_cbfunc, "blue")

-- 至此,只有led_on_timer_cbfunc和"green"标识的定时器还在运行

sys.timerStopAll(cbfunc)

功能

停止运行并且删除回调函数为cbfunc的所有定时器;

注意事项

可以在能够执行到的任意代码位置使用此函数;

参数

cbfunc

参数含义:定时器回调函数,和sys.timerStart(cbfunc, timeout, ...)或者sys.timerLoopStart(cbfunc, timeout, ...)创建定时器时的cbfunc保持一致
数据类型:function
取值范围:无特别限制;
是否必选:必须传入此参数;
注意事项:暂无;
参数示例:
    如下所示,led_on_timer_cbfunc就可以做为此参数传入

    local function led_on_timer_cbfunc(gpio_id)
        -- 此处的gpio_id的值为12
        log.info("led_on_timer_cbfunc", gpio_id)
    end

    sys.timerStart(led_on_timer_cbfunc, 1000, 12)

    sys.timerStopAll(led_on_timer_cbfunc)

返回值

nil

示例

local function led_on_timer_cbfunc(color)
    log.info("led_on_timer_cbfunc", color)
end

-- 创建一个循环定时器,每隔1秒后执行回调函数led_on_timer_cbfunc,"yellow"传递给回调函数
sys.timerLoopStart(led_on_timer_cbfunc, 1000, "yellow")
-- 创建一个单次定时器,2秒后执行回调函数led_on_timer_cbfunc,"green"传递给回调函数
sys.timerStart(led_on_timer_cbfunc, 2000, "green")
-- 创建一个循环定时器,每隔3秒后执行回调函数led_on_timer_cbfunc,"blue"传递给回调函数
sys.timerLoopStart(led_on_timer_cbfunc, 3000, "blue")

-- 停止并且删除回调函数为led_on_timer_cbfunc的所有定时器
-- 运行下面这样代码后,之前创建的三个定时器都被停止运行并且被删除
sys.timerStopAll(led_on_timer_cbfunc)

sys.wait(timeout)

功能

在task中阻塞等待一段时间;

注意事项

基础task和高级task的定义:参考本文第一章节的描述;

只能在基础task和高级task处理函数的业务逻辑中使用此函数;

参数

timeout

参数含义:阻塞等待的超时时长,单位毫秒;
数据类型:number
取值范围:大于等于1,小于等于0x7FFFFFFF,之间的所有正整数;最大时长0x7FFFFFFF毫秒  596小时  24.85天;
是否必选:必须传入此参数;
注意事项:
    此处的超时机制基于软件定时器实现,受系统负载、任务数量、消息堆积、网络中断优先级最高等多因素影响,无法实现高精度;
    尤其是几个毫秒级别,几十毫秒级别,几百毫秒级别的超时时长,误差都比较大,秒级别以上的超时时长误差较小;
    例如:设置的1毫秒,可能要等几十毫秒;设置的几十毫秒,可能要等上百毫秒;设置的几百毫秒,可能要等几百几十毫秒;
    可以简单的认为会延迟几十毫秒左右,以此来评估超时时长精度是否可以满足业务需求;


    调用本接口时,如果传入了timeout参数,则sys核心库内部会自动创建并且运行一个软件定时器,超时时长到达后,会自动删除这个定时器;
    LuatOS应用程序中可用的软件定时器总数量为64个    
    注意控制自已应用程序中的同时运行的软件定时器总数量不要超过64个,否则创建新的定时器会返回失败;
参数示例:50100060000等;

返回值

nil

示例

local function send_data_task_func()
    while true do
        -- 阻塞等待1秒钟
        sys.wait(1000)
        log.info("send_data_task_func")
    end
end

local function send_data_extask_func()
    while true do
        -- 阻塞等待1秒钟
        sys.wait(1000)
        log.info("send_data_extask_func")
    end

    sys.cleanMsg("EXTASK2")
    sys.taskDel("EXTASK2")
end

sys.taskInit(send_data_task_func)
sys.taskInitEx(send_data_extask_func, "EXTASK2")

七、run调度器函数详解

sys.run()

功能

LuatOS应用脚本程序调度器,负责整个LuatOS应用脚本程序的调度和管理;

sys.run()中是一个while循环,不断地读取消息和分发处理消息;

实现task的创建、阻塞和运行,msg的创建、传递和处理,timer的创建和运行等业务逻辑;

总体运行逻辑可以参考下图

注意事项

只能在main.lua中的最后一行使用此函数;

此函数后面的代码否无法执行;

参数

返回值

正常运行过程中,这个函数不会返回;

如果返回,表示整个LuatOS应用程序出现异常,此时就会自动异常重启;

示例

-- 如下为一个典型的main.lua文件中的代码内容,最后一行代码为sys.run()

PROJECT = "HTTP"
VERSION = "001.000.000"

-- 在日志中打印项目名和项目版本号
log.info("main", PROJECT, VERSION)

-- 如果内核固件支持wdt看门狗功能,此处对看门狗进行初始化和定时喂狗处理
-- 如果脚本程序死循环卡死,就会无法及时喂狗,最终会自动重启
if wdt then
    --配置喂狗超时时间为9秒钟
    wdt.init(9000)
    --启动一个循环定时器,每隔3秒钟喂一次狗
    sys.timerLoopStart(wdt.feed, 3000)
end


-- 如果内核固件支持errDump功能,此处进行配置,【强烈建议打开此处的注释】
-- 因为此功能模块可以记录并且上传脚本在运行过程中出现的语法错误或者其他自定义的错误信息,可以初步分析一些设备运行异常的问题
-- 以下代码是最基本的用法,更复杂的用法可以详细阅读API说明文档
-- 启动errDump日志存储并且上传功能,600秒上传一次
-- if errDump then
--     errDump.config(true, 600)
-- end


-- 使用LuatOS开发的任何一个项目,都强烈建议使用远程升级FOTA功能
-- 可以使用合宙的iot.openluat.com平台进行远程升级
-- 也可以使用客户自己搭建的平台进行远程升级
-- 远程升级的详细用法,可以参考fota的demo进行使用


-- 启动一个循环定时器
-- 每隔3秒钟打印一次总内存,实时的已使用内存,历史最高的已使用内存情况
-- 方便分析内存使用是否有异常
-- sys.timerLoopStart(function()
--     log.info("mem.lua", rtos.meminfo())
--     log.info("mem.sys", rtos.meminfo("sys"))
-- end, 3000)

-- 加载网络驱动设备功能模块
require "netdrv_device"

-- 加载http应用功能模块
require "http_app"
-- 加载httpplus应用功能模块
require "httpplus_app"

-- 用户代码已结束---------------------------------------------
-- 结尾总是这一句
sys.run()
-- sys.run()之后不要加任何语句!!!!!因为添加的任何语句都不会被执行

八、产品支持说明

支持LuatOS开发的所有产品都支持sys核心库。