Files
BR_YKC/4G/code/lib/socketCh395.lua

1570 lines
56 KiB
Lua
Raw Normal View History

2026-03-31 15:46:04 +08:00
--- 模块功能数据链路激活、socketCh395管理(创建、连接、数据收发、状态维护)
-- @module socketCh395
-- @author openLuat
-- @license MIT
-- @copyright openLuat
-- @release 2017.9.25
require "link"
require "utils"
module(..., package.seeall)
local sockets = {}
-- 单次发送数据最大值
local SENDSIZE = 1024
-- 缓冲区最大下标
local INDEX_MAX = 2048
-- 是否有socket正处在链接
local socketsConnected = 0
-- CH395配置信息
local configurationCh395 = nil
-- 以太网模块供电设置函数
local powerCbFnc = nil
-- 获取芯片版本使用0x44以上版本)
local ch395Version = nil
-- 芯片MAC地址
local CH395MAC = nil
local resvnum = 0
TCPCLIENT = 0
UDPCLIENT = 1
RAW = 3
TCPSERVER = 4
local function str2short(str)
local f, n = pack.unpack(str, '<H')
return f and f > 0 and n or 0
end
local function shorts(n)
return pack.pack('<H', n)
end
local function sub_z(data, num)
return string.sub(data, num + 1, data:len())
end
local function ip2num(str)
if (not (str and #str == 4)) then
return ''
end
local t = {pack.unpack(str, 'bbbb')}
return string.format('%d.%d.%d.%d', t[2], t[3], t[4], t[5])
end
local function iptostr(ip)
local d1, d2, d3, d4 = string.match(ip, '(%d+)%.(%d+)%.(%d+).(%d+)')
return {string.sub(shorts(d1), 1, 1), string.sub(shorts(d2), 1, 1), string.sub(shorts(d3), 1, 1),
string.sub(shorts(d4), 1, 1)}
end
------------
local function spiSend(id, data)
--log.info('send SPI data:', data:toHex())
local dataSpi
if configurationCh395['spiCs'] then
local setSpi_CS = pins.setup(configurationCh395['spiCs'], 0)
dataSpi = spi.send_recv(id, data)
setSpi_CS(1)
else
dataSpi = spi.send_recv(id, data)
end
-- log.info('recv SPI data', dataSpi:toHex())
return dataSpi
end
-- 命令执行查询
local function inquire2c(time)
local num = 0
while true do
sys.wait(time or 10)
local data = string.sub(spiSend(spi.SPI_1, '\x2c\xff'), 2, 2)
if data ~= '\x10' then
return data
end
num = num + 1
if num > 100 then
return data
end
end
end
-- 获取MAC地址
function getMac()
return CH395MAC
end
-- 本地端口,初始化默认端口
local socketLocalPort = 0
-- 动态获取本地端口号,每次获取累加
local function socketTcpClientPort()
-- socketLocalPort = socketLocalPort + 123
-- if socketLocalPort > 65535 then
-- socketLocalPort = 1000
-- end
-- return socketLocalPort
local time = string.sub(rtos.tick(), 3)
if socketLocalPort < 8 then
socketLocalPort = socketLocalPort + 1
else
socketLocalPort = 0
end
return (socketLocalPort + time) * 5
end
-- 初始化socket
local function SocketClientInit(type, socketId, soalAddress, soalPort, localPort)
localPort = localPort or socketTcpClientPort()
local addr = iptostr(soalAddress)
-- 设置协议类型
if type == TCPCLIENT then
spiSend(spi.SPI_1, string.char(0x34, socketId, 3))
elseif type == UDPCLIENT then
spiSend(spi.SPI_1, string.char(0x34, socketId, 2))
end
-- 设置本地端口
spiSend(spi.SPI_1, '\x33' .. string.char(socketId) .. shorts(tonumber(localPort)))
-- 设置目的端口号
spiSend(spi.SPI_1, '\x32' .. string.char(socketId) .. shorts(tonumber(soalPort)))
-- 设置目的地址
spiSend(spi.SPI_1, string.char(0x31, socketId) .. addr[1] .. addr[2] .. addr[3] .. addr[4])
-- 打开socket
spiSend(spi.SPI_1, string.char(0x35, socketId))
-- 查询执行状态
local state = inquire2c(50)
log.info('socketCh395', 'open socket' .. socketId .. state:toHex())
if state ~= '\x00' or type == UDPCLIENT then
if type ~= UDPCLIENT then
log.info('socketCh395', 'open socket err', state:toHex())
else
sys.timerStart(function()
sys.publish('SOCK_CONN_CNF', {
['socket_index'] = socketId,
['id'] = 34,
['result'] = 0
})
end, 200)
end
return state
end
spiSend(spi.SPI_1, string.char(0x37, socketId))
state = inquire2c()
if state == '\x00' then
--sys.publish('SOCK_CONN_CNF', {
-- ['socket_index'] = socketId,
-- ['id'] = 34,
-- ['result'] = 0
--})
else
log.info('socketCh395', 'open socket' .. socketId, state:toHex())
end
return state
end
-- 配置8路socket收发缓存
local function socketBufferConfig()
-- 只有硬件版本大于47才有4-7路
local b = 0
for i = 0, 7 do
spiSend(spi.SPI_1, string.char(0x52, i, b, 4))
b = b + 4
spiSend(spi.SPI_1, string.char(0x53, i, b, 2))
b = b + 2
sys.wait(500)
end
end
-- 关闭套接字连接
local function sockectCh395Close(socketId)
if sockets[socketId] == nil or sockets[socketId] == true then
log.info('socketCh395', 'close err, not socket ' .. socketId)
end
if sockets[socketId] == true or sockets[socketId].protocol == 'TCPSERVERCLIENT' then
-- server客户端断开连接
spiSend(spi.SPI_1, string.char(0x38, socketId))
local state = inquire2c()
log.info('socketCh395', 'close server socket ' .. socketId)
sys.publish('SOCK_CLOSE_CNF', {
['socket_index'] = socketId,
['id'] = 33,
['result'] = 0
})
elseif sockets[socketId].protocol == 'TCPSERVER' then
-- server关闭断开连接关闭所有server通道
log.info('socketCh395', 'close server ' .. socketId)
for i, v in ipairs(sockets) do
if sockets[i] ~= true and not sockets[i] and sockets[i].protocol == 'TCPSERVERCLIENT' then
local id = sockets[i].id
spiSend(spi.SPI_1, string.char(0x38, sockets[i].id))
spiSend(spi.SPI_1, string.char(0x3D, socketId))
sockets[i] = nil
log.info('socketCh395', 'close server socket ' .. id)
end
end
spiSend(spi.SPI_1, string.char(0x3D, socketId))
log.info('socketCh395', 'close server ' .. socketId)
local state = inquire2c()
sys.publish('SOCK_CLOSE_CNF', {
['socket_index'] = socketId,
['id'] = 33,
['result'] = 0
})
else
spiSend(spi.SPI_1, string.char(0x3D, socketId))
local stateClose = inquire2c(10)
log.info('sockectCh395', 'close socket' .. socketId .. 'true' .. stateClose:toHex())
sys.publish('SOCK_CLOSE_CNF', {
['socket_index'] = socketId,
['id'] = 33,
['result'] = 0
})
end
end
local function ipError()
if link.isReady() and link.getNetwork() == link.CH395 then
link.setReady(link.CH395, false)
sys.publish('IP_ERROR_IND')
log.info('socketCh395', 'IP_ERROR_IND')
end
end
-- 处理中断
local function interruptProcess()
local intAll = string.sub(spiSend(spi.SPI_1, '\x19\xff\xff'), 2, -1)
-- log.info('INT30', intAll:toHex())
intAll = str2short(intAll)
if intAll == 0 or intAll == 65535 then
return
end
-- 处理PHY中断
if bit.isset(intAll, 2) then
local statePhy = string.sub(spiSend(spi.SPI_1, '\x26\xff'), 2, -1)
if statePhy == '\x01' then
sys.publish("PHY_RESULT", false)
log.info('socketCh395', 'phy false')
ipError()
elseif statePhy == '\x08' then
sys.publish("PHY_RESULT", true)
log.info('socketCh395', 'phy true')
end
end
-- 处理DHCP中断
if bit.isset(intAll, 3) then
-- DHCP中断
local stateDhcp = string.sub(spiSend(spi.SPI_1, '\x42\xff'), 2, -1)
if stateDhcp == '\x00' then
log.info('socketCh395', 'DHCP true')
elseif stateDhcp == '\x01' then
log.info('socketCh395', 'DHCP false')
ipError()
end
end
-- 处理不可达中断
if bit.isset(intAll, 0) then
-- 读取不可达信息?
log.info('socketCh395', 'Unreachable interrupt')
-- getUnreachIpport()
end
-- 处理IP冲突中断
if bit.isset(intAll, 1) then
log.info("IP", 'IP conflict')
-- 建议重新修改本地IP并初始化芯片未加
end
-- socket0-7中断
local socketIntId = {}
for i = 4, 11 do
if bit.isset(intAll, i) then
table.insert(socketIntId, (i - 4))
end
end
local socketCloss = {}
-- 逐个socket查询中断并处理
for q = 1, #socketIntId do
local socketState = string.sub(spiSend(spi.SPI_1, string.char(0x30, socketIntId[q], 0xff, 0xff)), 3, -1)
-- log.info('INT19:' .. socketIntId[q], socketState:toHex())
socketState = str2short(socketState)
-- TCP连接
if bit.isset(socketState, 3) then
-- sockets[socketIntId[q]].connected = true
if sockets[socketIntId[q]] == true then
local data = string.sub(spiSend(spi.SPI_1,
string.char(0x2d, socketIntId[q], 0xff, 0xff, 0xff, 0xff, 0xff, 0xff)), 3, 8)
-- server
local socketData = {
id = socketIntId[q],
ip = ip2num(string.sub(data, 1, 4)),
port = str2short(string.sub(data, 5, 6))
}
sys.publish('tcpServer', socketData)
else
sys.publish('SOCK_CONN_CNF', {
['socket_index'] = socketIntId[q],
['id'] = 34,
['result'] = 0
})
end
end
-- 接收缓冲区非空
if bit.isset(socketState, 2) then
local data_leng
local data
data_leng = string.sub(spiSend(spi.SPI_1, string.char(0x3b, socketIntId[q], 0xff, 0xff)), 3, -1)
-- if data_leng == '\x00\x00' then
-- break
-- end
data = string.sub(spiSend(spi.SPI_1, string.char(0x3c, socketIntId[q]) .. data_leng ..
string.rep('\xff', str2short(data_leng))), 5, -1)
sys.timerStart(function()
sys.publish('MSG_SOCK_RECV_IND', {
['socket_index'] = socketIntId[q],
['id'] = 31,
['result'] = 0,
['recv_len'] = str2short(data_leng),
['recv_data'] = data
})
end, 10)
-- data_leng = string.sub(spiSend(spi.SPI_1, string.char(0x3b, socketIntId[q], 0xff, 0xff)), 3, -1)
-- data = string.sub(spiSend(spi.SPI_1, string.char(0x3c, socketIntId[q]) .. data_leng ..
-- string.rep('\xff', str2short(data_leng))), 5, -1)
-- sys.publish('MSG_SOCK_RECV_IND', {
-- ['socket_index'] = socketIntId[q],
-- ['id'] = 31,
-- ['result'] = 0,
-- ['recv_len'] = str2short(data_leng),
-- ['recv_data'] = data
-- })
end
-- 发送缓冲区空闲
if bit.isset(socketState, 0) then
if sockets[socketIntId[q]] and sockets[socketIntId[q]] ~= true then
sockets[socketIntId[q]].sendstate = true
else
log.info('socketCh395', 'sendstate err:' .. socketIntId[q])
end
end
-- 发送成功
if bit.isset(socketState, 1) then
sys.publish('SOCK_SEND_CNF', {
['socket_index'] = socketIntId[q],
['id'] = 32,
['result'] = 0
})
end
-- TCP断开
if bit.isset(socketState, 4) then
table.insert(socketCloss, {
['socket_index'] = socketIntId[q],
['id'] = 33,
['result'] = 0
})
end
-- 超时
if bit.isset(socketState, 6) then
-- 超时处理,重新开启重新连接(未加)
-- 打开socket
log.info('socketCh395', 'socket conn timeout' .. socketIntId[q], sockets[socketIntId[q]], socketIntId[q])
if not sockets[socketIntId[q]] then
sockectCh395Close(socketIntId[q])
elseif sockets[socketIntId[q]] ~= 0 then
local ty = sockets[socketIntId[q]].protocol
local soalAddress = sockets[socketIntId[q]].address
local soalPort = sockets[socketIntId[q]].port
local localPort = sockets[socketIntId[q]].localPort
sockectCh395Close(socketIntId[q])
--if ty == 'TCP' then
--ty = TCPCLIENT
--elseif ty == 'UDP' then
--ty = UDPCLIENT
--end
--SocketClientInit(ty, socketIntId[q], soalAddress, soalPort, localPort)
end
end
end
for i = 0, 7 do
if sockets[i] then
local data_leng
local data
data_leng = string.sub(spiSend(spi.SPI_1, string.char(0x3b, i, 0xff, 0xff)), 3, -1)
if data_leng ~= "\x00\x00" then
data = string.sub(spiSend(spi.SPI_1,
string.char(0x3c, i) .. data_leng .. string.rep('\xff', str2short(data_leng))), 5, -1)
sys.timerStart(function()
sys.publish('MSG_SOCK_RECV_IND', {
['socket_index'] = i,
['id'] = 31,
['result'] = 0,
['recv_len'] = str2short(data_leng),
['recv_data'] = data
})
end, 10)
end
end
end
for w = 1, #socketCloss do
local data =
string.sub(spiSend(spi.SPI_1, string.char(0x2F, socketCloss[w]['socket_index'], 0xff, 0xff)), 3, -1)
local data1 = string.sub(data, 1, 1)
local data2 = string.sub(data, 2, 2)
-- if data1 == '\x00' then
if not sockets[socketCloss[w]['socket_index']] or sockets[socketCloss[w]['socket_index']] == true then
sys.timerStart(function(...)
log.info('socketCh395', 'socket ' .. socketCloss[w]['socket_index'] .. 'state' .. data:toHex())
sys.publish('MSG_SOCK_CLOSE_IND', socketCloss[w])
log.info('socketCh395', 'Disconnection socket ', socketCloss[w]['socket_index'])
end, 200)
elseif sockets[socketCloss[w]['socket_index']].protocol == 'TCPSERVERCLIENT' then
-- sockets[socketCloss[w]['socket_index']]=true
sys.timerStart(function(...)
log.info('socketCh395', 'socket ' .. socketCloss[w]['socket_index'] .. 'state' .. data:toHex())
sys.publish('MSG_SOCK_CLOSE_IND', socketCloss[w])
log.info('socketCh395', 'Disconnection socket ', socketCloss[w]['socket_index'])
end, 200)
else
sys.timerStart(function(...)
log.info('socketCh395', 'socket ' .. socketCloss[w]['socket_index'] .. 'state' .. data:toHex())
sys.publish('MSG_SOCK_CLOSE_IND', socketCloss[w])
log.info('socketCh395', 'Disconnection socket ', socketCloss[w]['socket_index'])
end, 200)
end
-- end
end
sys.wait(10)
end
local number = 0
sys.taskInit(function()
while true do
if number > 0 or sys.waitUntil("interruptProcess") then
interruptProcess()
number = number - 1
-- sys.wait(5)
end
end
end)
-- 配置中断检测
local function setInt(state)
if state then
pio.pin.setdebounce(5)
pins.setup(configurationCh395['intPin'], function(a)
if a == 2 then
number = number + 2
sys.publish("interruptProcess")
else
end
end)
pio.pin.setdebounce(20)
else
pins.close(configurationCh395['intPin'])
end
end
--- 初始化CH395模块
-- @table para 取值如下:
-- para为table类型表示以太网的配置参数参数结构如下
-- {
-- mode = 1, --1表示客户端2表示服务器默认为1
-- intPin = pio.P0_22, --以太网芯片中断通知引脚
-- rstPin = pio.P0_23, --复位以太网芯片引脚
-- clientNum = 6, --server可连路数
-- spiCs = pio.P0_23, --SPI片选
-- CH395MAC = "84C2E4A82950", --MAC地址
-- localAddr = "192.168.1.112", --本机的地址
-- localSubnetMas = "255.255.255.0", --子网掩码
-- localPort = 1888, --server本机的端口
-- spiCs=pio.P0_7, --spi片选
-- localGateway = "192.168.1.1", --本机的网关地址
-- func=function(id, msg, dat)end, --中断处理函数处理server中断事件
-- powerFunc=function(state) end --控制以太网模块的供电开关函数ret为true开启供电false关闭供电
-- spi = {spi.SPI_1,0,0,8,800000}, --SPI通道参数id,cpha,cpol,dataBits,clock默认spi.SPI_1,0,0,8,800000
-- }
-- @return result 数据接收结果
-- true表示成功
-- false表示失败
-- @usage
-- socketCh395.open(para)
function open(para)
configurationCh395 = para
powerCbFnc = nil or para['powerFunc']
-- 供电及其他前置条件
if powerCbFnc then
powerCbFnc(true)
end
-- 复位芯片
spiSend(spi.SPI_1, '\x05')
-- 开启SPI
if not spi.setup(para['spi'][1], para['spi'][2], para['spi'][3], para['spi'][4], para['spi'][5], 1) == 1 then
log.info('socketCh395', 'open spi err')
return ''
end
-- 设置中断
setInt(true)
-- 芯片进入SPI模式
local setGpioFnc_RSTI = pins.setup(para['rstPin'], 0)
setGpioFnc_RSTI(0)
sys.wait(100)
setGpioFnc_RSTI(1)
sys.wait(200)
-- 测试是否成功建立通讯
local state06 = string.sub(spiSend(spi.SPI_1, '\x06\x55\xff'), 3, 3)
state06 = state06 == '\xaa'
log.info('CH395 state', state06)
if not state06 then
log.info('socketCh395', 'err cannot work')
return ''
end
-- server多连接设置
if para['mode'] == 2 then
spiSend(spi.SPI_1, '\x55\x02')
local stateInit = inquire2c(50)
if stateInit ~= '\x00' then
log.info('open server')
else
log.info('open server err', stateInit:toHex())
end
end
-- 获取芯片版本
ch395Version = string.sub(spiSend(spi.SPI_1, '\x01\xff'), 2, 2)
-- 配置芯片信息
if para['localAddr'] and para['localGateway'] then
-- 配置本地信息
local gateway = iptostr(para['localGateway'])
local addr = iptostr(para['localAddr'])
spiSend(spi.SPI_1, '\x22' .. addr[1] .. addr[2] .. addr[3] .. addr[4])
spiSend(spi.SPI_1, '\x23' .. gateway[1] .. gateway[2] .. gateway[3] .. gateway[4])
if para['localSubnetMas'] then
local subnetMas = iptostr(para['localSubnetMas'])
spiSend(spi.SPI_1, '\x24' .. subnetMas[1] .. subnetMas[2] .. subnetMas[3] .. subnetMas[4])
end
end
-- 设置收发缓存默认8路
socketBufferConfig()
-- 模块初始化
spiSend(spi.SPI_1, '\x27')
local stateInit = inquire2c(50)
if stateInit ~= '\x00' then
log.info('socketCh395', 'ch395 INIT err' .. stateInit:toHex())
return ''
end
local result, result2 = sys.waitUntil("PHY_RESULT", 60000)
if not result or not result2 then
return ''
end
-- 设置MAC地址
if para['CH395MAC'] and string.len(para['CH395MAC']) == 12 then
spiSend(spi.SPI_1, '\x21' .. string.fromHex(para['CH395MAC']))
sys.wait(200)
end
-- 获取芯片MAC
CH395MAC = string.sub(spiSend(spi.SPI_1, string.fromHex('40FFFFFFFFFFFF2cff')), 2, 7)
if CH395MAC == string.char(0, 0, 0, 0, 0, 0) then
CH395MAC = nil
end
-- DHCP
if not (para['localAddr'] and para['localGateway']) then
spiSend(spi.SPI_1, '\x41\x01')
local stateDhcp = inquire2c(30)
if stateDhcp ~= '\x00' then
log.info('socketCh395', 'ch395 INIT err' .. stateDhcp:toHex())
return ''
end
end
-- 获取本地IP
for q = 1, 20 do
sys.wait(500)
local dataIp = string.sub(spiSend(spi.SPI_1, string.fromHex('43ffffffffffffffffffffffffffffffffffffffff')), 2,
-1)
if link.getNetwork() == link.CH395 and string.sub(dataIp, 1, 4) ~= '\xff\xff\xff\xff' and
string.sub(dataIp, 1, 4) ~= '\x00\x00\x00\x00' then
local ch395Ip = ip2num(string.sub(dataIp, 1, 4))
log.info('ip', ch395Ip)
link.setReady(link.CH395, true)
sys.publish('IP_READY_IND')
return ch395Ip
end
end
return ''
end
--- 开关模块
function close()
link.setReady(link.CH395, false)
sys.publish('IP_ERROR_IND')
-- 关闭所有socket
for i = 0, 7 do
spiSend(spi.SPI_1, string.char(0x3D, i))
sockets[i] = nil
end
-- 关闭中断检测
setInt(false)
-- 关闭SPI
if not spi.close(configurationCh395['spi'][1]) == 1 then
log.info('socketCh395 close spi err', configurationCh395['spi'][1], configurationCh395['spi'][2],
configurationCh395['spi'][3], configurationCh395['spi'][4], configurationCh395['spi'][5], 1)
return false
end
-- 关闭模块
if powerCbFnc then
powerCbFnc(false)
end
return true
end
---建立套接字连接
local function sockectCh395Conn(type, addr, port, lcoalPort, data)
local socketId = nil
for i = 0, 7 do
if not sockets[i] then
socketId = i
data.id=i
sockets[i] = data
break
end
if i == 7 then
log.info('socketCh395', 'No socket available ')
return nil
end
end
if type == TCPCLIENT or type == UDPCLIENT then
local tcpClientState = SocketClientInit(type, socketId, addr, port, lcoalPort)
if tcpClientState == '\x00' then
return socketId
else
spiSend(spi.SPI_1, string.char(0x3D, socketId))
local stateClose = inquire2c(50)
if stateClose == '\x00' then
log.info('sockectCh395', 'close socket' .. socketId .. 'true')
end
sockets[socketId] = nil
return nil
end
elseif type == TCPSERVER then
end
end
-- 发送数据
local function sockectCh395Send(socketId, data)
for i = 1, 10 do
if sockets[socketId] and sockets[socketId] ~= true and sockets[socketId].sendstate then
spiSend(spi.SPI_1, '\x39' .. string.char(socketId) .. shorts(#data) .. data)
local state = inquire2c(50)
if state == '\x00' then
sys.publish('SOCK_SEND_CNF', {
['socket_index'] = socketId,
['id'] = 32,
['result'] = 0
})
else
sys.publish('SOCK_SEND_CNF', {
['socket_index'] = socketId,
['id'] = 32,
['result'] = 1
})
end
log.info('socketCh395', socketId .. 'send state' .. state:toHex())
break
elseif not sockets[socketId] or sockets[socketId] == true then
-- log.info('socketCh395', 'send err, not socket ' .. socketId)
else
sys.wait(100)
end
end
end
-- 接收数据
local function sockectCh395Recv(socketId, buffLen)
local data_leng = string.sub(spiSend(spi.SPI_1, string.char(0x3b, socketId, 0xff, 0xff)), 3, -1)
local data = string.sub(spiSend(spi.SPI_1, string.char(0x3c, socketId) .. data_leng ..
string.rep('\xff', str2short(data_leng))), 5, -1)
return data
end
-- table 深拷贝
local function clone(tb)
local ret = {}
for k, v in pairs(tb) do
ret[k] = type(v) == "table" and clone(v) or v
end
return ret
end
-- 创建server
local function initServer(port, num, data)
if num > 7 then
return
end
local dd = true
local serverTable = {}
local serverid = nil
for i = 0, 7 do
if not sockets[i] then
table.insert(serverTable, i)
if dd then
sockets[i] = data
dd = false
else
sockets[i] = clone(data)
end
if #serverTable == num + 1 then
break
end
end
if i == 7 then
log.info('socketCh395', 'No socket available ')
for q = 1, #serverTable do
sockets[serverTable[q]] = nil
end
return nil
end
end
for i = 1, #serverTable do
spiSend(spi.SPI_1, string.char(0x34, serverTable[i], 3))
spiSend(spi.SPI_1, string.char(0x33, serverTable[i]) .. shorts(port))
if i == 1 then
spiSend(spi.SPI_1, string.char(0x35, serverTable[i]))
sockets[i].id = serverTable[i]
serverid = serverTable[i]
local state = inquire2c(50)
if state == '\x00' then
sockets[serverTable[i]].id = serverTable[i]
sockets[serverTable[i]].protocol = 'TCPSERVER'
else
log.info('socket open err', state:toHex())
log.info('socketCh395', 'socket open err')
end
else
local state = inquire2c(50)
if state == '\x00' then
log.info('socketCh395', 'server open' .. serverTable[i])
sockets[serverTable[i]].protocol = 'TCPSERVERCLIENT' .. serverTable[i]
sockets[serverTable[i]] = true
end
if i == num + 1 then
spiSend(spi.SPI_1, string.char(0x36, serverid))
local state = inquire2c(50)
if state == '\x00' then
return serverid
end
end
end
end
end
------------------------
local function errorInd(error)
local coSuspended = {}
for _, c in pairs(sockets) do -- IP状态出错时通知所有已连接的socket
if c ~= true then
c.error = error
-- 不能打开如下3行代码IP出错时会通知每个socketsocket会主动close
-- 如果设置了connected=false则主动close时直接退出不会执行close动作导致core中的socket资源没释放
-- 会引发core中socket耗尽以及socket id重复的问题
-- c.connected = false
-- socketsConnected = c.connected or socketsConnected
-- if error == 'CLOSED' then sys.publish("SOCKET_ACTIVE", socketsConnected) end
if c.co and coroutine.status(c.co) == "suspended" then
-- coroutine.resume(c.co, false)
table.insert(coSuspended, c.co)
end
end
end
for k, v in pairs(coSuspended) do
if v and coroutine.status(v) == "suspended" then
coroutine.resume(v, false, error)
end
end
end
sys.subscribe("IP_ERROR_IND", function()
errorInd('IP_ERROR_IND')
end)
-- sys.subscribe('IP_SHUT_IND', function()errorInd('CLOSED') end)
-- 创建socket函数
local mt = {}
mt.__index = mt
local function socket(protocol, cert, tCoreExtPara)
local ssl = protocol:match("SSL")
local co = coroutine.running()
if not co then
log.warn("socket.socket: socket must be called in coroutine")
return nil
end
-- if protocol == 'TCPSERVER' and type(tCoreExtPara) == 'table' then
-- protocol
-- end
-- 实例的属性参数表
local o = {
id = nil,
protocol = protocol,
tCoreExtPara = tCoreExtPara,
ssl = ssl,
cert = cert,
co = co,
input = {},
output = {},
wait = "",
connected = false,
iSubscribe = false,
subMessage = nil,
isBlock = false,
msg = nil,
rcvProcFnc = nil,
sendstate = true, -- 发送缓冲区状态true为空闲可发送数据
localPort = nil -- 本地端口
}
if tCoreExtPara and tCoreExtPara['localport'] and type(tCoreExtPara['localport']) == 'number' then
o.localPort = tCoreExtPara['port']
end
if protocol == 'TCPSERVER' and tCoreExtPara['type']== 'TCPSERVER' then
o.port = configurationCh395["localPort"]
local id = initServer(configurationCh395["localPort"], configurationCh395["clientNum"], setmetatable(o, mt))
if not id then
log.info('socketCh395', 'server socket err')
return
end
elseif protocol == 'TCPSERVER' and tCoreExtPara['type']~= 'TCPSERVER' then
o.address = tCoreExtPara['ip']
o.port = tCoreExtPara['port']
o.id = tCoreExtPara['id']
o.protocol = 'TCPSERVERCLIENT'
sockets[tCoreExtPara['id']] = setmetatable(o, mt)
end
return setmetatable(o, mt)
end
--- 创建基于TCP的socket对象
-- @bool[opt=nil] ssl 是否为ssl连接true表示是其余表示否
-- @table[opt=nil] cert 保留参数ssl功能还未实现。
-- @table[opt=nil] tCoreExtPara 建立链接扩展参数
-- {
-- id =0, --server socket索引ID
-- ip ="192.168.1.1", --server socket client ip
-- port ="8000", --server socket client port
-- type ="TCPSERVER", --server socket type
-- localport ="8000", -- socket client port
-- }
-- @return client创建成功返回socket客户端对象创建失败返回nil
function tcp(ssl, cert, tCoreExtPara)
if tCoreExtPara and tCoreExtPara['type'] == "TCPSERVER" then
return socket("TCPSERVER", nil, tCoreExtPara)
elseif tCoreExtPara and tCoreExtPara['type'] ~= "TCPSERVER" then
return socket("TCPSERVER", nil, tCoreExtPara)
end
return socket("TCP" .. (ssl == true and "SSL" or ""), (ssl == true) and cert or nil, tCoreExtPara)
end
--- 创建基于UDP的socket对象
-- @return client创建成功返回socket客户端对象创建失败返回nil
-- @usage c = socketCh395.udp()
function udp(localPort)
return socket("UDP", nil, localPort)
end
--- 连接服务器
-- @string address 服务器地址支持ip和域名
-- @param port string或者number类型服务器端口
-- @number[opt=120] timeout 可选参数,连接超时时间,单位秒
-- @return bool result true - 成功false - 失败
-- @return string ,id '0' -- '8' ,返回通道ID编号
-- @usage
-- socketClient = socketCh395.tcp()
-- socketClient:connect("www.baidu.com","80")
function mt:connect(address, port, timeout)
assert(self.co == coroutine.running(), "socket:connect: coroutine mismatch")
log.info('socketCh395', 'socket data' .. self.protocol, address, port)
if not link.isReady() then
log.info("socket.connect: ip not ready")
return false
end
self.address = address
self.port = port
local tCoreExtPara = self.tCoreExtPara or {}
-- 默认缓冲区大小
local rcvBufferSize = tCoreExtPara.rcvBufferSize or 0
local d1, d2, d3, d4 = string.match(address, '(%d+)%.(%d+)%.(%d+).(%d+)')
if not (d1 == nil or d2 == nil or d3 == nil or d4 == nil) then
if self.protocol == 'TCP' then
self.id = sockectCh395Conn(TCPCLIENT, address, port, self.localPort, self)
elseif self.protocol == 'TCPSSL' then
-- 未实现
log.info("socketCh395", "no ssl")
else
self.id = sockectCh395Conn(UDPCLIENT, address, port, self.localPort, self)
end
else
self.id = -2
end
if type(socketcore.sock_conn_ext) == "function" then
if not self.id or self.id < 0 then
if self.id == -2 then
require "http"
-- 请求腾讯云免费HttpDns解析
http.request("GET", "119.29.29.29/d?dn=" .. address, nil, nil, nil, 40000,
function(result, statusCode, head, body)
log.info("socket.httpDnsCb", result, statusCode, head, body)
sys.publish("SOCKET_HTTPDNS_RESULT_" .. address .. "_" .. port, result, statusCode, head, body)
end)
local _, result, statusCode, head, body = sys.waitUntil(
"SOCKET_HTTPDNS_RESULT_" .. address .. "_" .. port)
-- DNS解析成功
if result and statusCode == "200" and body and body:match("^[%d%.]+") then
return self:connect(body:match("^([%d%.]+)"), port, timeout)
end
end
self.id = nil
end
end
if not self.id then
log.info("socket:connect: core sock conn error", self.protocol, address, port, self.cert)
return false
end
log.info("socket:connect-coreid,prot,addr,port,cert,timeout", self.id, self.protocol, address, port, self.cert,
timeout or 120)
sockets[self.id] = self
self.wait = "SOCKET_CONNECT"
self.timerId = sys.timerStart(coroutine.resume, (timeout or 120) * 1000, self.co, false, "TIMEOUT")
local result, reason = coroutine.yield()
if not result and reason == 'server close' then
return false
end
if self.timerId and reason ~= "TIMEOUT" then
sys.timerStop(self.timerId)
end
if not result then
log.info("socket:connect: connect fail" .. self.id, reason)
if reason == "RESPONSE" then
sockets[self.id] = nil
self.id = nil
end
sys.publish("LIB_SOCKET_CONNECT_FAIL_IND", self.ssl, self.protocol, address, port)
return false
end
log.info("socket:connect: connect ok")
if not self.connected then
self.connected = true
socketsConnected = socketsConnected + 1
sys.publish("SOCKET_ACTIVE", socketsConnected > 0)
end
return true, self.id
end
function mt:serverClose()
if not self then
return false
end
if self.error then
log.warn('socket.client:asyncSelect', 'error', self.error)
return false
end
coroutine.resume(self.co, false, 'server close')
end
--- server发送数据
-- @number[opt=nil] keepAlive 服务器和客户端最大通信间隔时间,也叫心跳包最大时间,单位秒
-- @string[opt=nil] pingreq 心跳包的字符串
-- @return boole,false 失败true 表示成功
-- @usage
-- socketClient = socketCh395.tcp()
-- socketClient:connect("www.baidu.com","80")
-- while socketClient:serverSelect() do end
function mt:serverSelect(keepAlive, pingreq)
assert(self.co == coroutine.running(), "socket:asyncSelect: coroutine mismatch")
if self.error then
log.warn('socket.client:asyncSelect', 'error', self.error)
return false
end
self.wait = "SOCKET_SEND"
local dataLen = 0
-- log.info("socket.asyncSelect #self.output",#self.output)
while #self.output ~= 0 do
local data = table.concat(self.output)
dataLen = string.len(data)
self.output = {}
local sendSize = SENDSIZE
for i = 1, dataLen, sendSize do
-- 按最大MTU单元对data分包
sockectCh395Send(self.id, data:sub(i, i + sendSize - 1))
if self.timeout then
self.timerId = sys.timerStart(coroutine.resume, self.timeout * 1000, self.co, false, "TIMEOUT")
end
-- log.info("socket.asyncSelect self.timeout",self.timeout)
local result, reason = coroutine.yield()
if not result and reason == 'server close' then
return false
end
log.info('发送结果', result, reason)
if self.timerId and reason ~= "TIMEOUT" then
sys.timerStop(self.timerId)
end
sys.publish("SOCKET_ASYNC_SEND", result)
if not result then
sys.publish("LIB_SOCKET_SEND_FAIL_IND", self.ssl, self.protocol, self.address, self.port)
log.warn('socket.asyncSelect', 'send error', data:sub(i, i + sendSize - 1))
return false
end
end
end
self.wait = "SOCKET_WAIT"
-- log.info("socket.asyncSelect",dataLen,self.id)
if dataLen > 0 then
sys.publish("SOCKET_SEND", self.id, true)
end
if keepAlive and keepAlive ~= 0 then
if type(pingreq) == "function" then
sys.timerStart(pingreq, keepAlive * 1000)
else
sys.timerStart(self.asyncSend, keepAlive * 1000, self, pingreq or "\0")
end
end
local result, reason = coroutine.yield()
if not result and reason == 'server close' then
return false
end
return result, reason
end
--- 异步发送数据
-- @number[opt=nil] keepAlive 服务器和客户端最大通信间隔时间,也叫心跳包最大时间,单位秒
-- @string[opt=nil] pingreq 心跳包的字符串
-- @return boole,false 失败true 表示成功
-- @usage
-- socketClient = socketCh395.tcp()
-- socketClient:connect("www.baidu.com","80")
-- while socketClient:asyncSelect() do end
function mt:asyncSelect(keepAlive, pingreq)
assert(self.co == coroutine.running(), "socket:asyncSelect: coroutine mismatch")
if self.error then
log.warn('socket.client:asyncSelect', 'error', self.error)
return false
end
self.wait = "SOCKET_SEND"
local dataLen = 0
-- log.info("socket.asyncSelect #self.output",#self.output)
while #self.output ~= 0 do
local data = table.concat(self.output)
dataLen = string.len(data)
self.output = {}
local sendSize = self.protocol == "UDP" and 1472 or SENDSIZE
for i = 1, dataLen, sendSize do
-- 按最大MTU单元对data分包
-- socketcore.sock_send(self.id, data:sub(i, i + sendSize - 1))
sockectCh395Send(self.id, data:sub(i, i + sendSize - 1))
if self.timeout then
self.timerId = sys.timerStart(coroutine.resume, self.timeout * 1000, self.co, false, "TIMEOUT")
end
-- log.info("socket.asyncSelect self.timeout",self.timeout)
local result, reason = coroutine.yield()
if not result and reason == 'server close' then
return false
end
if self.timerId and reason ~= "TIMEOUT" then
sys.timerStop(self.timerId)
end
sys.publish("SOCKET_ASYNC_SEND", result)
if not result then
sys.publish("LIB_SOCKET_SEND_FAIL_IND", self.ssl, self.protocol, self.address, self.port)
-- log.warn('socket.asyncSelect', 'send error')
return false
end
end
end
self.wait = "SOCKET_WAIT"
-- log.info("socket.asyncSelect",dataLen,self.id)
if dataLen > 0 then
sys.publish("SOCKET_SEND", self.id, true)
end
if keepAlive and keepAlive ~= 0 then
if type(pingreq) == "function" then
sys.timerStart(pingreq, keepAlive * 1000)
else
sys.timerStart(self.asyncSend, keepAlive * 1000, self, pingreq or "\0")
end
end
local result, reason = coroutine.yield()
if not result and reason == 'server close' then
return false
end
return result, reason
end
function mt:getAsyncSend()
if self.error then
return 0
end
return #(self.output)
end
--- server缓存待发送的数据
-- @string data 数据
-- @number[opt=nil] timeout 可选参数发送超时时间单位秒为nil时表示不支持timeout
-- @return result true - 成功false - 失败
-- @usage
-- socketClient = socketCh395.tcp()
-- socketClient:connect("www.baidu.com","80")
-- socketClient:serverSend("12345678")
function mt:serverSend(data, timeout)
if self.error then
log.warn('socket.client:asyncSend', 'error', self.error)
return false
end
self.timeout = timeout
table.insert(self.output, data or "")
-- log.info("socket.asyncSend",self.wait)
if self.wait == "SOCKET_WAIT" then
coroutine.resume(self.co, true)
end
return true
end
--- server接收数据
-- @return data 表示接收到的数据(如果是UDP返回最新的一包数据如果是TCP,返回所有收到的数据)
-- ""表示未收到数据
-- @usage
-- socketClient = socketCh395.tcp()
-- socketClient:connect("www.baidu.com","80")
-- data = socketClient:serverRecv()
function mt:serverRecv()
if #self.input == 0 then
return ""
end
if self.protocol == "UDP" then
return table.remove(self.input)
else
local s = table.concat(self.input)
self.input = {}
if self.isBlock then
table.insert(self.input, recv(self.msg.socket_index, self.msg.recv_len))
end
return s
end
end
--- 异步缓存待发送的数据
-- @string data 数据
-- @number[opt=nil] timeout 可选参数发送超时时间单位秒为nil时表示不支持timeout
-- @return result true - 成功false - 失败
-- @usage
-- socketClient = socketCh395.tcp()
-- socketClient:connect("www.baidu.com","80")
-- socketClient:asyncSend("12345678")
function mt:asyncSend(data, timeout)
if self.error then
log.warn('socket.client:asyncSend', 'error', self.error)
return false
end
self.timeout = timeout
table.insert(self.output, data or "")
-- log.info("socket.asyncSend",self.wait)
if self.wait == "SOCKET_WAIT" then
coroutine.resume(self.co, true)
end
return true
end
--- 异步接收数据
-- @return data 表示接收到的数据(如果是UDP返回最新的一包数据如果是TCP,返回所有收到的数据)
-- ""表示未收到数据
-- @usage
-- socketClient = socketCh395.tcp()
-- socketClient:connect("www.baidu.com","80")
-- data = socketClient:asyncRecv()
function mt:asyncRecv()
if #self.input == 0 then
return ""
end
if self.protocol == "UDP" then
return table.remove(self.input)
else
local s = table.concat(self.input)
self.input = {}
if self.isBlock then
table.insert(self.input, socketcore.sock_recv(self.msg.socket_index, self.msg.recv_len))
end
return s
end
end
--- 同步发送数据
-- @string data 数据
-- 此处传入的数据长度和剩余可用内存有关,只要内存够用,可以随便传入数据
-- 虽然说此处的数据长度没有特别限制但是调用core中的socket发送接口时每次最多发送11200字节的数据
-- 例如此处传入的data长度是112000字节则在这个send接口中会循环10次每次发送11200字节的数据
-- @number[opt=120] timeout 可选参数,发送超时时间,单位秒
-- @return result true - 成功false - 失败
-- @usage
-- socketClient = socketCh395.tcp()
-- socketClient:connect("www.baidu.com","80")
-- socketClient:send("12345678")
function mt:send(data, timeout)
assert(self.co == coroutine.running(), "socket:send: coroutine mismatch")
if self.error then
log.warn('socket.client:send', 'error', self.error)
return false
end
log.debug("socket.send", "total " .. string.len(data or "") .. " bytes", "first 30 bytes", (data or ""):sub(1, 30))
local sendSize = self.protocol == "UDP" and 1472 or SENDSIZE
for i = 1, string.len(data or ""), sendSize do
-- 按最大MTU单元对data分包
self.wait = "SOCKET_SEND"
sockectCh395Send(self.id, data:sub(i, i + sendSize - 1))
self.timerId = sys.timerStart(coroutine.resume, (timeout or 120) * 1000, self.co, false, "TIMEOUT")
local result, reason = coroutine.yield()
if not result and reason == 'server close' then
return false
end
if self.timerId and reason ~= "TIMEOUT" then
sys.timerStop(self.timerId)
end
if not result then
log.info("socket:send" .. self.id, "send fail", reason)
sys.publish("LIB_SOCKET_SEND_FAIL_IND", self.ssl, self.protocol, self.address, self.port)
return false
end
end
return true
end
--- 同步接收数据
-- @number[opt=0] timeout 可选参数,接收超时时间,单位毫秒
-- @string[opt=nil] msg 可选参数控制socket所在的线程退出recv阻塞状态
-- @bool[opt=nil] msgNoResume 可选参数控制socket所在的线程退出recv阻塞状态
-- false或者nil表示“在recv阻塞状态收到msg消息可以退出阻塞状态”true表示不退出
-- 此参数仅lib内部使用应用脚本不要使用此参数
-- @return result 数据接收结果
-- true表示成功接收到了数据
-- false表示失败没有接收到数据
-- @return data
-- 如果result为truedata表示接收到的数据(如果是UDP返回最新的一包数据如果是TCP,返回所有收到的数据)
-- 如果result为false超时失败data为"timeout"
-- 如果result为falsemsg控制退出data为msg的字符串
-- 如果result为falsesocket连接被动断开控制退出data为"CLOSED"
-- 如果result为falsePDP断开连接控制退出data为"IP_ERROR_IND"
-- @return param 如果是msg控制退出param的值是msg的参数
-- @usage
-- socketClient = socketCh395.tcp()
-- socketClient:connect("www.baidu.com","80")
-- result,data = socketClient:recv(60000,"APP_SOCKET_SEND_DATA")
function mt:recv(timeout, msg, msgNoResume)
assert(self.co == coroutine.running(), "socket:recv: coroutine mismatch")
if self.error then
log.warn('socket.client:recv', 'error', self.error)
return false
end
self.msgNoResume = msgNoResume
if msg and not self.iSubscribe then
self.iSubscribe = msg
self.subMessage = function(data)
-- if data then table.insert(self.output, data) end
if self.wait == "+RECEIVE" and not self.msgNoResume then
if data then
table.insert(self.output, data)
end
coroutine.resume(self.co, 0xAA)
end
end
sys.subscribe(msg, self.subMessage)
end
if msg and #self.output > 0 then
sys.publish(msg, false)
end
if #self.input == 0 then
self.wait = "+RECEIVE"
if timeout and timeout > 0 then
local r, s = sys.wait(timeout)
if r == nil then
return false, "timeout"
elseif r == 0xAA then
local dat = table.concat(self.output)
self.output = {}
return false, msg, dat
else
return r, s
end
else
local r, s = coroutine.yield()
if not r and s == 'server close' then
return false
end
if r == 0xAA then
local dat = table.concat(self.output)
self.output = {}
return false, msg, dat
else
return r, s
end
end
end
if self.protocol == "UDP" then
local s = table.remove(self.input)
return true, s
else
log.warn("-------------------使用缓冲区---------------")
local s = table.concat(self.input)
self.input = {}
if self.isBlock then
table.insert(self.input, sockectCh395Recv(self.msg.socket_index, self.msg.recv_len))
end
return true, s
end
end
--- 主动关闭并且销毁一个socket
-- @return nil
-- @usage
-- socketClient = socketCh395.tcp()
-- socketClient:connect("www.baidu.com","80")
-- socketClient:close()
function mt:close()
assert(self.co == coroutine.running(), "socket:close: coroutine mismatch")
if self.iSubscribe then
sys.unsubscribe(self.iSubscribe, self.subMessage)
self.iSubscribe = false
end
-- log.info('socketCh395', 'close' .. self.id)
-- 此处不要再判断状态否则在连接超时失败时conneted状态仍然是未连接会导致无法close
-- if self.connected then
log.info("socket:sock_close", self.id)
local result, reason
if self.id then
sockectCh395Close(self.id)
self.wait = "SOCKET_CLOSE"
while true do
result, reason = coroutine.yield()
if not result and reason == 'server close' then
return false
end
if reason == "RESPONSE" then
break
end
end
end
if self.connected then
self.connected = false
if socketsConnected > 0 then
socketsConnected = socketsConnected - 1
end
sys.publish("SOCKET_ACTIVE", socketsConnected > 0)
end
if self.input then
self.input = {}
end
if self.protocol == 'TCPSERVERCLIENT' then
sockets[self.id] = true
else
if self.id ~= nil then
sockets[self.id] = nil
end
end
end
-- socket接收自定义控制处理
-- @function[opt=nil] rcvCbFncsocket接收到数据后执行的回调函数回调函数的调用形式为
-- rcvCbFnc(readFnc,socketIndex,rcvDataLen)
-- rcvCbFnc内部会判断是否读取数据如果读取执行readFnc(socketIndex,rcvDataLen)返回true否则返回false或者nil
function mt:setRcvProc(rcvCbFnc)
assert(self.co == coroutine.running(), "socket:setRcvProc: coroutine mismatch")
self.rcvProcFnc = rcvCbFnc
end
function on_response(msg)
local t = {
[rtos.MSG_SOCK_CLOSE_CNF] = 'SOCKET_CLOSE',
-- 33
[rtos.MSG_SOCK_SEND_CNF] = 'SOCKET_SEND',
-- 32
[rtos.MSG_SOCK_CONN_CNF] = 'SOCKET_CONNECT'
-- 34
}
if not sockets[msg.socket_index] then
log.warn('response on nil socket', msg.socket_index, t[msg.id], msg.result)
return
end
if sockets[msg.socket_index] == true then
return
end
log.info(sockets[msg.socket_index].wait, t[msg.id], msg.socket_index, msg.id)
if sockets[msg.socket_index].wait ~= t[msg.id] then
log.warn('response on invalid wait', sockets[msg.socket_index].id, sockets[msg.socket_index].wait, t[msg.id],
msg.socket_index)
return
end
log.info('socket:on_response:', msg.socket_index, t[msg.id], msg.result)
if sockets[msg.socket_index].protocol == 'TCPSERVERCLIENT' then
msg.result=0
end
coroutine.resume(sockets[msg.socket_index].co, msg.result == 0, 'RESPONSE')
end
sys.subscribe('SOCK_CONN_CNF', on_response)
sys.subscribe('SOCK_CLOSE_CNF', on_response)
sys.subscribe('SOCK_SEND_CNF', on_response)
-- 被动关闭
sys.subscribe('MSG_SOCK_CLOSE_IND', function(msg)
local data = string.sub(spiSend(spi.SPI_1, string.char(0x2F, msg.socket_index, 0xff, 0xff)), 3, -1)
log.info('socketCh395', 'close socket ' .. msg.socket_index .. 'state', data:toHex())
if string.sub(data, 1, 1) ~= '\x00' then
return
end
log.info('socket.rtos.MSG_SOCK_CLOSE_IND',msg.socket_index)
if not sockets[msg.socket_index] then
return
elseif sockets[msg.socket_index] == true then
return
elseif sockets[msg.socket_index].protocol == 'TCPSERVERCLIENT' then
coroutine.resume(sockets[msg.socket_index].co, false, 'CLOSED')
sockets[msg.socket_index] = true
return
end
if not sockets[msg.socket_index] then
log.warn('close ind on nil socket', msg.socket_index, msg.id)
return
end
if sockets[msg.socket_index].connected then
sockets[msg.socket_index].connected = false
if socketsConnected > 0 then
socketsConnected = socketsConnected - 1
end
sys.publish('SOCKET_ACTIVE', socketsConnected > 0)
end
sockets[msg.socket_index].error = 'CLOSED'
--[[
if type(socketcore.sock_destroy) == "function" then
socketcore.sock_destroy(msg.socket_index)
end]]
sys.publish('LIB_SOCKET_CLOSE_IND', sockets[msg.socket_index].ssl, sockets[msg.socket_index].protocol,
sockets[msg.socket_index].address, sockets[msg.socket_index].port)
coroutine.resume(sockets[msg.socket_index].co, false, 'CLOSED')
end)
sys.subscribe('MSG_SOCK_RECV_IND', function(msg)
if type(sockets[msg.socket_index])~='table' then
log.warn('close ind on nil socket', msg.socket_index, msg.id)
return
end
log.debug('socket.recv', msg.recv_len, sockets[msg.socket_index].rcvProcFnc)
if sockets[msg.socket_index].rcvProcFnc then
sockets[msg.socket_index].rcvProcFnc(msg.recv_data)
else
if sockets[msg.socket_index].wait == '+RECEIVE' then
coroutine.resume(sockets[msg.socket_index].co, true, msg.recv_data)
else -- 数据进缓冲区,缓冲区溢出采用覆盖模式
if #sockets[msg.socket_index].input > INDEX_MAX then
log.error('socket recv', 'out of stack', 'block')
-- sockets[msg.socket_index].input = {}
sockets[msg.socket_index].isBlock = true
sockets[msg.socket_index].msg = msg
else
sockets[msg.socket_index].isBlock = false
table.insert(sockets[msg.socket_index].input, msg.recv_data)
end
sys.publish('SOCKET_RECV', msg.socket_index)
end
end
end)
-- 设置TCP层自动重传的参数
-- @number[opt=4] retryCnt 重传次数取值范围0到12
-- @number[opt=16] retryMaxTimeout 限制每次重传允许的最大超时时间(单位秒)取值范围1到16
-- @return nil
-- @usage
-- setTcpResendPara(3,8)
-- setTcpResendPara(4,16)
function setTcpResendPara(retryCnt, retryMaxTimeout)
ril.request("AT+TCPUSERPARAM=6," .. (retryCnt or 4) .. ",7200," .. (retryMaxTimeout or 16))
end
-- 设置域名解析参数
-- 注意0027以及之后的core版本才支持此功能
-- @number[opt=4] retryCnt 重传次数取值范围1到8
-- @number[opt=4] retryTimeoutMulti 重传超时时间倍数取值范围1到5
-- 第n次重传超时时间的计算方式为第n次的重传超时基数*retryTimeoutMulti单位为秒
-- 重传超时基数表为{1, 1, 2, 4, 4, 4, 4, 4}
-- 第1次重传超时时间为1*retryTimeoutMulti 秒
-- 第2次重传超时时间为1*retryTimeoutMulti 秒
-- 第3次重传超时时间为2*retryTimeoutMulti 秒
-- ...........................................
-- 第8次重传超时时间为8*retryTimeoutMulti 秒
-- @return nil
-- @usage
-- socket.setDnsParsePara(8,5)
function setDnsParsePara(retryCnt, retryTimeoutMulti)
ril.request("AT*DNSTMOUT=" .. (retryCnt or 4) .. "," .. (retryTimeoutMulti or 4))
end
function setIpStatis(interval)
end
--- 打印所有socket的状态
-- @return 无
-- @usage socketCh395.printStatus()
function printStatus()
for _, client in pairs(sockets) do
for k, v in pairs(client) do
log.info('socket.printStatus', 'client', client.id, k, v)
end
end
end
-- 设置数据传输后,允许进入休眠状态的延时时长
-- 3024版本以及之后的版本才支持此功能
-- 此功能设置的参数,设置成功后,掉电会自动保存
-- @number tm数据传输后允许进入休眠状态的延时时长单位为秒取值范围1到20
-- 注意:此时间越短,允许进入休眠状态越快,功耗越低;但是在某些网络环境中,此时间越短,可能会造成数据传输不稳定
-- 建议在可以接受的功耗范围内,此值设置的越大越好
-- 如果没有设置此参数此延时时长是和基站的配置有关一般来说是10秒左右
-- @return nil
-- @usage
-- socketCh395.setLowPower(5)
function setLowPower(tm)
ril.request("AT*RTIME=" .. tm)
end