init: Air780EPM 官方 LuatOS 项目代码基线
- 来源: 合宙 LuatOS 官方仓库 air780epm 模块完整代码 - 路径: luatos/air780epm/module/Air780EPM/demo 含官方 demo(含 mqtt/mqtts/socket/uart 等) - 后续: 基于 demo 开发 UART<->MQTT 数据上报功能
This commit is contained in:
@@ -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
|
||||
Reference in New Issue
Block a user