init: Air780EPM 官方 LuatOS 项目代码基线

- 来源: 合宙 LuatOS 官方仓库 air780epm 模块完整代码
- 路径: luatos/air780epm/module/Air780EPM/demo 含官方 demo(含 mqtt/mqtts/socket/uart 等)
- 后续: 基于 demo 开发 UART<->MQTT 数据上报功能
This commit is contained in:
wangfq
2026-08-31 08:53:49 +08:00
commit 173dd6874f
813 changed files with 125484 additions and 0 deletions
@@ -0,0 +1,112 @@
--[[
@module udp_server_main
@summary udp server 主应用功能模块
@version 1.0
@date 2025.11.15
@author 王世豪
@usage
本文件为udp server 主应用功能模块,核心业务逻辑为:
1、创建一个udp server,监听指定端口;
2、处理通信异常,出现异常后,重新初始化UDP服务以恢复正常数据接收;
3、调用udp_server_receiver和udp_server_sender中的外部接口,进行数据收发处理;
本文件没有对外接口,直接在main.lua中require "udp_server_main"就可以加载运行;
]]
local udpsrv = require "udpsrv"
-- 加载UDP服务器数据接收功能模块
local udp_server_receiver = require "udp_server_receiver"
-- 加载UDP服务器数据发送功能模块
local udp_server_sender = require "udp_server_sender"
-- 服务器监听端口
local SERVER_PORT = 50003
-- 服务器主题(用于接收消息)
SERVER_TOPIC = "udp_server"
-- udp server socket的任务处理函数
local function udp_server_main_task_func()
local udp_server
local ret, data, remote_ip, remote_port
while true do
-- 如果当前时间点设置的网卡还没有连接成功,一直在这里循环等待
while not socket.adapter(socket.dft()) do
log.warn("udp_client_main_task_func", "wait IP_READY", socket.dft())
-- 在此处阻塞等待默认网卡连接成功的消息"IP_READY"
-- 或者等待1秒超时退出阻塞等待状态;
-- 注意:此处的1000毫秒超时不要修改的更长;
-- 因为当使用exnetif.set_priority_order配置多个网卡连接外网的优先级时,会隐式的修改默认使用的网卡
-- 当exnetif.set_priority_order的调用时序和此处的socket.adapter(socket.dft())判断时序有可能不匹配
-- 此处的1秒,能够保证,即使时序不匹配,也能1秒钟退出阻塞状态,再去判断socket.adapter(socket.dft())
sys.waitUntil("IP_READY", 1000)
end
-- 检测到了IP_READY消息
log.info("udp_server_main_task_func", "recv IP_READY", socket.dft())
-- 创建UDP服务器对象
-- 注意:udpsrv.create有3个参数,最后一个参数是网络适配器编号
udp_server = udpsrv.create(SERVER_PORT, SERVER_TOPIC, socket.dft())
if not udp_server then
log.error("udp_server_main_task_func", "udpsrv.create error")
goto EXCEPTION_PROC
end
log.info("udp_server_main_task_func", "UDP server started on port", SERVER_PORT)
-- 发送一条广播消息,通知端口号为50000的客户端,UDP服务器已启动
udp_server:send("UDP Server is UP", "255.255.255.255", 50000)
-- 数据收发以及网络连接异常事件总处理逻辑
while true do
-- 数据发送处理
if not udp_server_sender.proc(udp_server) then
log.error("udp_server_main_task_func", "udp_server_sender.proc error")
end
-- 等待接收数据事件
ret, data, remote_ip, remote_port = sys.waitUntil(SERVER_TOPIC, 15000)
if ret then
-- 判断是否是发送就绪事件(通过 data 内容或 remote_ip 是否为 nil
if data == "SEND_READY" and remote_ip == nil then
-- 这是发送就绪事件,无需处理接收数据,直接继续循环以发送数据
log.info("udp_server_main_task_func", "send ready event received")
-- 网络异常事件
elseif data == "SOCKET_CLOSED" then
goto EXCEPTION_PROC
else
-- 真实接收到的数据
if not udp_server_receiver.proc(data, remote_ip, remote_port) then
log.error("udp_server_main_task_func", "udp_server_receiver.proc error")
end
end
else
-- 超时,发送一条心跳广播
log.info("udp_server_main_task_func", "No data received, sending broadcast heartbeat")
udp_server:send("UDP Server Heartbeat", "255.255.255.255", 50000)
end
end
::EXCEPTION_PROC::
-- 数据发送应用模块对来不及发送的数据做清空和通知失败处理
udp_server_sender.exception_proc()
-- 关闭UDP服务器
if udp_server then
udp_server:close()
udp_server = nil
end
-- 5秒后跳转到循环体开始位置,重建udp server
sys.wait(5000)
end
end
--创建并且启动一个task
--运行这个task的主函数udp_server_main_task_func
sys.taskInit(udp_server_main_task_func)
@@ -0,0 +1,71 @@
--[[
@module udp_server_receiver
@summary udp server socket数据接收应用功能模块
@version 1.0
@date 2025.11.15
@author 王世豪
@usage
本文件为udp server socket数据接收应用功能模块,核心业务逻辑为:
从内核读取接收到的数据,然后将数据发送给其他应用功能模块做进一步处理;
本文件的对外接口有2个:
1、udp_server_receiver.proc(socket_server):数据接收应用逻辑处理入口,在udp_server_main.lua中调用;
2、sys.publish("RECV_DATA_FROM_CLIENT", data, remote_ip, remote_port)
将接收到的数据通过消息"RECV_DATA_FROM_CLIENT"发布出去;
需要处理数据的应用功能模块订阅处理此消息即可;
]]
local udp_server_receiver = {}
-- 客户端信息
local client_info = {}
-- 获取客户端信息
function udp_server_receiver.get_client_info()
return client_info
end
-- 重置客户端信息
function udp_server_receiver.reset_client_info()
client_info.ip = nil
client_info.port = nil
end
-- 初始化客户端信息
udp_server_receiver.reset_client_info()
--[[
检查udp server是否收到数据,如果收到数据,读取并且处理完所有数据
@api udp_server_receiver.proc(data, remote_ip, remote_port)
@param1 data string
表示接收到的数据;
@param2 remote_ip string
表示发送数据的client的IP地址;
@param3 remote_port number
表示发送数据的client的端口号;
@return1 result bool
表示处理结果,成功为true,失败为false
@usage
udp_server_receiver.proc(data, remote_ip, remote_port)
]]
function udp_server_receiver.proc(data, remote_ip, remote_port)
log.info("udp_server_receiver.proc", "收到数据", data, "来自", remote_ip, remote_port)
client_info.ip = remote_ip
client_info.port = remote_port
log.info("client_info", client_info.ip, client_info.port)
-- 将接收到的数据通过消息发布出去
sys.publish("RECV_DATA_FROM_CLIENT", data, remote_ip, remote_port)
return true
end
return udp_server_receiver
@@ -0,0 +1,115 @@
--[[
@module udp_server_sender
@summary udp server socket数据发送应用功能模块
@version 1.0
@date 2025.11.15
@author 王世豪
@usage
本文件为udp server socket数据发送应用功能模块,核心业务逻辑为:
1、sys.subscribe("SEND_DATA_REQ", send_data_req_proc_func)订阅"SEND_DATA_REQ"消息,将其他应用模块需要发送的数据存储到队列send_queue中;
2、udp_server_main主任务调用udp_server_sender.proc接口,遍历队列send_queue,逐条发送数据到client
3、udp server socket如果出现异常,udp_server_main主任务调用udp_server_sender.exception_proc接口,丢弃掉队列send_queue中未发送的数据;
4、任何一条数据无论发送成功还是失败,只要这条数据有回调函数,都会通过回调函数通知数据发送方;
本文件的对外接口有3个:
1、sys.subscribe("SEND_DATA_REQ", send_data_req_proc_func):订阅"SEND_DATA_REQ"消息;
其他应用模块如果需要发送数据,直接sys.publish这个消息即可,将需要发送的数据、目标IP、目标端口以及回调函数和回调参数一起publish出去;
2、udp_server_sender.proc:数据发送应用逻辑处理入口,在udp_server_main.lua中调用;
3、udp_server_sender.exception_proc:数据发送应用逻辑异常处理入口,在udp_server_main.lua中调用;
]]
local udp_server_sender = {}
--[[
数据发送队列,数据结构为:
{
[1] = {data="data1", ip="127.0.0.1", port=8888, cb={func=callback_function1, para=callback_para1}},
[2] = {data="data2", ip="127.0.0.1", port=8888, cb={func=callback_function2, para=callback_para2}},
}
data的内容为真正要发送的数据,必须存在;
ip的内容为目标IP,必须存在;
port的内容为目标端口,必须存在;
func的内容为数据发送结果的用户回调函数,可以不存在
para的内容为数据发送结果的用户回调函数的回调参数,可以不存在;
]]
local send_queue = {}
-- "SEND_DATA_REQ"消息的处理函数
local function send_data_req_proc_func(tag, data, ip, port, cb)
-- 将原始数据增加前缀,然后插入到发送队列send_queue中
table.insert(send_queue, {data="send from "..tag..": "..data, ip=ip, port=port, cb=cb})
log.info("send_queue", #send_queue)
-- 通知主任务:有数据待发送,唤醒阻塞
sys.publish("udp_server", "SEND_READY", nil, nil) -- 后两个参数为 remote_ip 和 remote_port,这里置为 nil
end
--[[
检查udp server是否需要发送数据,如果需要发送数据,读取并且发送完发送队列中的所有数据
@api udp_server_sender.proc(udp_server)
@param
表示由udpsrv.create接口创建的udp_server对象;
必须传入,不允许为空或者nil
@return1 result bool
表示处理结果,成功为true,失败为false
@usage
udp_server_sender.proc(udp_server)
]]
function udp_server_sender.proc(udp_server)
local send_item
local result
-- 遍历数据发送队列send_queue
while #send_queue>0 do
-- 取出来第一条数据赋值给send_item
-- 同时从队列send_queue中删除这一条数据
send_item = table.remove(send_queue,1)
result = udp_server:send(send_item.data, send_item.ip, send_item.port)
-- 发送失败
if not result then
log.error("udp_server_sender.proc", "udp_server:send error")
-- 如果当前发送的数据有用户回调函数,则执行用户回调函数
if send_item.cb and send_item.cb.func then
send_item.cb.func(false, send_item.cb.para)
end
return false
end
log.info("udp_server_sender.proc", "send success", send_item.ip, send_item.port)
-- 发送成功,如果当前发送的数据有用户回调函数,则执行用户回调函数
if send_item.cb and send_item.cb.func then
send_item.cb.func(true, send_item.cb.para)
end
end
return true
end
-- UDP服务器出现异常时,清空等待发送的数据,并且执行发送方的回调函数
function udp_server_sender.exception_proc()
-- 遍历数据发送队列send_queue
while #send_queue>0 do
local send_item = table.remove(send_queue,1)
-- 发送失败,如果当前发送的数据有用户回调函数,则执行用户回调函数
if send_item.cb and send_item.cb.func then
send_item.cb.func(false, send_item.cb.para)
end
end
end
-- 订阅"SEND_DATA_REQ"消息;
-- 其他应用模块如果需要发送数据,直接sys.publish这个消息即可,将需要发送的数据以及回调函数和回调参数一起publish出去;
-- 参数格式: sys.publish("SEND_DATA_REQ", tag, data, ip, port, cb)
-- tag: 发送方标识, data: 要发送的数据, ip: 目标IP, port: 目标端口, cb: 回调函数
-- 例如: sys.publish("SEND_DATA_REQ", "app1", "hello client", "192.168.1.100", 50000)
-- 本demo项目中uart_app.lua和timer_app.lua中publish了这个消息;
sys.subscribe("SEND_DATA_REQ", send_data_req_proc_func)
return udp_server_sender