-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathat_server.lua
More file actions
228 lines (199 loc) · 6.55 KB
/
at_server.lua
File metadata and controls
228 lines (199 loc) · 6.55 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
local AT_Server = {}
AT_Server.__index = AT_Server
function AT_Server.parse_args(param_str)
local args = {}
if not param_str or param_str == "" then
return args
end
for v in param_str:gmatch('([^,]+)') do
local item = v:match("^%s*(.-)%s*$") -- 去除首尾空格
local first = item:sub(1, 1)
local last = item:sub(-1, -1)
-- 检查引号。只要出现引号,必须首尾成对。
if first == '"' or last == '"' then
if first == '"' and last == '"' and #item >= 2 then
table.insert(args, item:sub(2, -2)) -- 存为字符串
else
return nil -- 引号不匹配,判定解析失败
end
else
-- 无引号内容必须是合法数值
local num = tonumber(item)
if num then
table.insert(args, num) -- 存为数值
else
return nil -- 既非数值也无引号,判定解析失败
end
end
end
return args
end
function AT_Server.new(send_handler)
local self = setmetatable({}, AT_Server)
self._commands = {}
self._line_buffer = ""
self._echo = false -- 默认关闭回显
self._send = send_handler or function()
end
-- 通用原始数据上下文
self._raw_ctx = nil -- { on_data, on_timeout, timeout_sec, last_time }
return self
end
-- 内部方法,供指令 handler 调用以启动数据接收
-- on_data: function(chunk)
-- on_timeout: function()
-- timeout_sec: 超时时间,单位秒
-- 返回 true: 继续接收数据
-- 返回 false: 接收完成,切回指令模式
-- result: 可选,需要发送给客户端的内容
function AT_Server:_enter_raw_mode(on_data, on_timeout, timeout_sec)
self._raw_ctx = {
on_data = on_data,
on_timeout = on_timeout,
timeout_sec = timeout_sec,
last_time = os.time() -- 记录进入时间
}
self._send(">") -- 标准提示符
return true
end
function AT_Server:add_command(cmd_name, callback)
self._commands[cmd_name:upper()] = callback
end
function AT_Server:feed(data)
-- 原始数据模式:处理二进制流
if self._raw_ctx then
-- 检查是否已经超时
if os.difftime(os.time(), self._raw_ctx.last_time) > self._raw_ctx.timeout_sec then
local status, result = pcall(self._raw_ctx.on_timeout)
self._raw_ctx = nil -- 清除上下文,强制回到指令模式
-- 如果逻辑执行崩溃
if not status then
self._send("\r\nERROR\r\n")
return
end
-- 发送用户定义的回显或结果内容
if result and result ~= "" then
self._send(tostring(result))
end
return
end
self._raw_ctx.last_time = os.time() -- 更新活跃时间
-- 立即调用用户定义的数据回调函数
if #data > 0 then
local status, is_continue, result = pcall(self._raw_ctx.on_data, data)
-- 如果逻辑执行崩溃
if not status then
self._raw_ctx = nil
self._send("\r\nERROR\r\n")
return
end
-- 发送用户定义的回显或结果内容
if result and result ~= "" then
self._send(tostring(result))
end
-- 判断是否结束
if is_continue == false then
self._raw_ctx = nil
return
end
end
return
end
-- 指令模式:处理文本行
-- 如果开启了回显,收到什么就立即发回什么
if self._echo then
self._send(data)
end
self._line_buffer = self._line_buffer .. data
while true do
local line, term, rest = self._line_buffer:match("^([^\r\n]-)([\r\n]+)(.*)")
if not line then
break
end
self._line_buffer = rest
-- 过滤掉纯换行产生的空行(例如 \r\n 拆分出的空部分)
if line ~= "" or #term > 0 then
-- 如果 line 是空的但有换行符,通常是由于连续换行,AT标准通常忽略或处理为空指令
if line ~= "" then
self:_dispatch(line)
end
end
end
end
function AT_Server:_dispatch(line)
-- 清理不可见字符
line = line:gsub("\r", ""):gsub("\n", "")
-- line = line:match("^%s*(.-)%s*$") -- 去除首尾空格
if line == "" then
return
end
local upper_line = line:upper()
-- 基本格式检查
if not upper_line:find("^AT") then
self._send("\r\nERROR\r\n")
return
end
-- 提取内容
local content = line:sub(3)
if content == "" then
self._send("\r\nOK\r\n")
return
end
-- 内置指令处理:ATE0 / ATE1 (开关回显)
if content:upper() == "E0" then
self._echo = false
self._send("\r\nOK\r\n")
return
elseif content:upper() == "E1" then
self._echo = true
self._send("\r\nOK\r\n")
return
end
-- 解析指令、模式与参数
local cmd, mode, param
if content:find("=%?$") then
cmd = content:sub(1, -3);
mode = "TEST"
elseif content:find("=") then
cmd, param = content:match("([^=]+)=(.*)")
mode = "SET"
elseif content:find("%?$") then
cmd = content:sub(1, -2);
mode = "READ"
else
cmd = content;
mode = "EXE"
end
if cmd then
cmd = cmd:upper()
end
local handler = self._commands[cmd]
if handler then
-- 调用 Handler 并支持结果控制
local status, is_ok, result = pcall(handler, mode, param, self)
if status then
if is_ok == "WAIT_DATA" then
-- Handler 告知需要接收数据,不发送 OK
return
elseif is_ok then
-- 成功:先发送结果内容(如果有),再发送 OK
if result and result ~= "" then
self._send("\r\n" .. tostring(result) .. "\r\n")
end
self._send("\r\nOK\r\n")
else
-- 逻辑失败
if result and result ~= "" then
self._send("\r\n" .. tostring(result) .. "\r\n")
end
self._send("\r\nERROR\r\n")
end
else
-- 运行时崩溃
self._send("\r\nERROR\r\n")
end
else
self._send("\r\nERROR\r\n")
end
end
return AT_Server