Files
BR_YKC/4G/code/lib/link.lua
2026-03-31 15:46:04 +08:00

462 lines
14 KiB
Lua
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
--- 模块功能:数据链路激活(创建、连接、状态维护)
-- @module link
-- @author openLuat
-- @license MIT
-- @copyright openLuat
-- @release 2017.9.20
-- 4G网络下不手动激活pdp注册上网后发cgdcont?等默认承载激活后上报IP_READY_IND
-- 2G网络下先cgact?查询有任一一路pdp激活则直接上报IP_READY_IND否则cgact激活cid_manual
require "net"
module(..., package.seeall)
local publish = sys.publish
local request = ril.request
local ipAddr = ""
local gprsAttached
local cid_manual = 5
local readyTable = {false, false, false}
-- 链路层网络类型:
-- 蜂窝模组数据网络
CELLULAR = 1
-- ch395以太网
CH395 = 2
-- w5500以太网
W5500 = 3
-- ESP8266WIFI网络
ESP8266 = 4
local network = CELLULAR
function setReady(mode, state)
readyTable[mode] = state
end
function getIp()
return ipAddr
end
function isReady()
return readyTable[network]
end
-- apn用户名密码
local apnname, username, password
local dnsIP
local authProt, authApn, authUser, authPassword
function setAPN(apn, user, pwd)
apnname, username, password = apn, user, pwd
end
function setDnsIP(ip1, ip2)
dnsIP = "\"" .. (ip1 or "") .. "\",\"" .. (ip2 or "") .. "\""
end
local function setCgdf()
request("AT+AUTOAPN=0")
request('AT*CGDFLT=1,"IP","' .. authApn .. '",,,,,,,,,,,,,,,,,,1')
request('AT*CGDFAUTH=1,' .. authProt .. ',"' .. authUser .. '","' .. authPassword .. '"', nil, function(cmd, result)
if result then
sys.restart("CGDFAUTH")
else
sys.timerStart(setCgdf, 5000)
end
end)
end
--- 设置专网卡APN(注意在main.lua中尽可能靠前的位置调用此接口)
-- 第一次设置成功之后,软件会自动重启,因为重启后才能生效
-- @number[opt=0] prot 加密方式0:不加密1:PAP2:CHAP
-- @string[opt=""] apn apn名称
-- @string[opt=""] user apn用户名
-- @string[opt=""] pwd apn密码
-- @return nil
-- @usage
-- c = link.setAuthApn(2,"MYAPN","MYNAME","MYPASSWORD")
function setAuthApn(prot, apn, user, pwd)
--[[
local coreVer = rtos.get_version()
local verNo = coreVer:match("Luat_V(%d+)_ASR1802_")
if verNo and tonumber(verNo)>=27 then
request("AT+AUTOAPN=0")]]
-- 0保存并重启生效
-- 1不保存立即生效
-- 2保存并立即生效
-- 3删除保存的文件
request('AT+CPNETAPN=2,"' .. apn .. '","' .. user .. '","' .. pwd .. '",' .. prot)
--[[else
authProt,authApn,authUser,authPassword = prot or 0,apn or "",user or "",pwd or ""
request("AT*CGDFLT?")
ril.regUrc("*CGDFLT", function(data)
local dftApn = data:match("CGDFLT:%s*\"%w*\",\"(.-)\"")
if dftApn~=authApn then
setCgdf()
end
end)
end]]
end
local function Pdp_Act()
log.info("link.Pdp_Act", readyTable[CELLULAR], net.getNetMode(), gprsAttached)
if readyTable[CELLULAR] then
request("AT+CGDCONT?", nil, cgdcontRsp)
return
end
if net.getNetMode() == net.NetMode_LTE then
if not gprsAttached then
gprsAttached = true
sys.publish("GPRS_ATTACH", true)
end
if not apnname then
sys.timerStart(pdpCmdCnf, 1000, "SET_PDP_4G_WAITAPN", true)
else
request("AT+CGDCONT?", nil, cgdcontRsp)
-- request(string.format('AT*CGDFLT=0,"IP","%s"', apnname), nil, pdpCmdCnf)
end
else
request('AT+CGATT?')
end
end
local function procshut(curCmd, result, respdata, interdata)
if network ~= CELLULAR then
return
end
if IsCidActived(cid_manual, interdata) then
ril.request(string.format('AT+CGACT=0,%d', cid_manual), nil, function(cmd, result)
if result then
readyTable[CELLULAR] = false
sys.publish('IP_ERROR_IND')
if net.getState() ~= 'REGISTERED' then
return
end
sys.timerStart(Pdp_Act, 2000)
end
end)
else
readyTable[CELLULAR] = false
sys.publish('IP_ERROR_IND')
if net.getState() ~= 'REGISTERED' then
return
end
sys.timerStart(Pdp_Act, 2000)
end
end
--[[
如果是默认承载,是去激活不了的,
如果是手动激活的pdp去激活cid_manual后也还是有默认承载存在
所以如果上层在去激活后要发起socket是能连上的所以这里直接上报IP_ERROR_IND由上层自己管理shut之后的逻辑
]]
function shut()
if network ~= CELLULAR then
return
end
-- ril.request("AT+CGACT?",nil,procshut)
readyTable[CELLULAR] = false
sys.publish('IP_ERROR_IND')
if net.getState() ~= 'REGISTERED' then
return
end
sys.timerStart(Pdp_Act, 2000)
end
function analysis_cgdcont(data)
local tmp, loc, result
while data do
_, loc = string.find(data, "\r\n")
if loc then
tmp = string.sub(data, 1, loc)
data = string.sub(data, loc + 1, -1)
log.info("analysis_cgdcont ", tmp, loc, data)
else
tmp = data
data = nil
log.info("analysis_cgdcont end", tmp, loc, data)
end
if tmp then
local cid, pdptyp, apn, addr = string.match(tmp, "(%d+),(.+),(.+),[\"\'](.+)[\"\']")
if not cid or not pdptyp or not apn or not addr then
log.info("analysis_cgdcont CGDCONT is empty")
ipAddr = ""
result = false
else
log.info("analysis_cgdcont ", cid, pdptyp, apn, addr)
if addr:match("%d+%.%d+%.%d+%.%d") then
ipAddr = addr
return true
else
log.info("analysis_cgdcont CGDCONT is empty1")
ipAddr = ""
return false
end
end
else
ipAddr = ""
log.info("analysis_cgdcont tmp is empty")
end
end
return result
end
function IsCidActived(cid, data)
if not data then
return
end
for k, v in string.gfind(data, "(%d+),%s*(%d)") do
log.info("iscidactived ", k, v)
if cid == tonumber(k) and v == '1' then
return true
end
end
return
end
function IsExistActivedCid(data)
if not data then
return
end
for k, v in string.gfind(data, "(%d+),%s*(%d)") do
if v == '1' then
log.info("ExistActivedCid ", k, v)
return true
end
end
return
end
local cgdcontResult
function cgdcontRsp()
if cgdcontResult then
pdpCmdCnf("CONNECT_DELAY", true)
end
end
function pdpCmdCnf(curCmd, result, respdata, interdata)
log.info("link.pdpCmdCnf", curCmd, result, respdata, interdata)
if string.find(curCmd, "CGDCONT%?") then
if result and interdata then
result = analysis_cgdcont(interdata)
else
result = false
end
end
if result then
cgdcontResult = false
if string.find(curCmd, "CGDCONT=") then
request(string.format('AT+CGACT=1,%d', cid_manual), nil, pdpCmdCnf)
elseif string.find(curCmd, "CGDCONT%?") then
-- sys.timerStart(pdpCmdCnf, 100, "CONNECT_DELAY",true)
cgdcontResult = true
elseif string.find(curCmd, "CONNECT_DELAY") and network == CELLULAR then
log.info("publish IP_READY_IND")
readyTable[CELLULAR] = true
publish("IP_READY_IND")
elseif string.find(curCmd, "CGACT=") then
request("AT+CGDCONT?", nil, cgdcontRsp)
elseif string.find(curCmd, "CGACT%?") then
if IsExistActivedCid(interdata) then
sys.timerStart(pdpCmdCnf, 100, "CONNECT_DELAY", true)
else
request(string.format('AT+CGDCONT=%d,"IP","%s"', cid_manual, authApn or apnname), nil, pdpCmdCnf)
end
elseif string.find(curCmd, "CGDFLT") then
request("AT+CGDCONT?", nil, cgdcontRsp)
elseif string.find(curCmd, "SET_PDP_4G_WAITAPN") then
if not apnname then
sys.timerStart(pdpCmdCnf, 100, "SET_PDP_4G_WAITAPN", true)
else
request("AT+CGDCONT?", nil, cgdcontRsp, 1000)
-- request(string.format('AT*CGDFLT=0,"IP","%s"', apnname), nil, pdpCmdCnf)
end
end
else
if net.getState() ~= 'REGISTERED' then
return
end
if net.getNetMode() == net.NetMode_LTE then
request("AT+CGDCONT?", nil, cgdcontRsp, 1000)
else
request("AT+CGATT?", nil, nil, 1000)
end
end
end
-- SIM卡 IMSI READY以后自动设置APN
sys.subscribe("IMSI_READY", function()
if not apnname then -- 如果未设置APN设置默认APN
local mcc, mnc = tonumber(sim.getMcc(), 16), tonumber(sim.getMnc(), 16)
apnname, username, password = apn and apn.get_default_apn(mcc, mnc) -- 如果存在APN库自动获取运营商的APN
if not apnname or apnname == '' or apnname == "CMNET" then -- 默认情况如果联通卡设置为联通APN 其他都默认为CMIOT
apnname = (mcc == 0x460 and (mnc == 0x01 or mnc == 0x06)) and 'UNINET' or 'CMIOT'
end
end
username = username or ''
password = password or ''
end)
ril.regRsp('+CGATT', function(a, b, c, intermediate)
local attached = (intermediate == "+CGATT: 1")
if gprsAttached ~= attached then
gprsAttached = attached
sys.publish("GPRS_ATTACH", attached)
end
if readyTable[CELLULAR] then
return
end
if attached then
log.info("pdp active", apnname, username, password)
request("AT+CGACT?", nil, pdpCmdCnf, 1000)
elseif net.getState() == 'REGISTERED' then
sys.timerStart(request, 2000, "AT+CGATT=1")
sys.timerStart(request, 2000, "AT+CGATT?")
end
end)
rtos.on(rtos.MSG_PDP_DEACT_IND, function()
if network ~= CELLULAR then
return
end
readyTable[CELLULAR] = false
sys.publish('IP_ERROR_IND')
if net.getState() ~= 'REGISTERED' then
return
end
sys.timerStart(Pdp_Act, 2000)
end)
-- 网络注册成功 :AT+CGDCONT?查询默认承载是否激活
-- 2/3G发起GPRS附着状态查询
sys.subscribe("NET_STATE_REGISTERED", Pdp_Act)
local function cindCnf(cmd, result)
if not result then
request("AT+CIND=1", nil, cindCnf, 1000)
end
end
local function cgevurc(data)
if network ~= CELLULAR then
return
end
local cid = 0
log.info("link.cgevurc", data)
if string.match(data, "DEACT") then
cid = string.match(data, "DEACT,(%d)")
cid = tonumber(cid)
if cid == cid_manual then
request("AT+CFUN?")
readyTable[CELLULAR] = false
sys.publish('IP_ERROR_IND')
sys.publish('PDP_DEACT_IND')
if net.getState() ~= 'REGISTERED' then
return
end
sys.timerStart(Pdp_Act, 2000)
end
end
end
request("AT+CIND=1", nil, cindCnf)
ril.regUrc("*CGEV", cgevurc)
ril.regUrc("+CGDCONT", function(data)
pdpCmdCnf("AT+CGDCONT?", true, "OK", data)
end)
--- 打开链路层网络类型
-- 注意设置网络类型后并不会关机保存下次开机会自动恢复为默认的link.CELLULAR类型
-- @number[opt=link.CELLULAR] mode 取值如下:
-- link.CELLULAR蜂窝模组数据网络
-- link.CH395CH395以太网络
-- link.ESP8266ESP8266WIFI网络
-- @table[opt=nil] para 取值如下:
-- 当mode为link.CELLULAR时参数para无意义可以直接传入nil
-- 当mode为link.CH395para为table类型表示以太网的配置参数参数结构如下
-- para= {
-- mode = 1, --1表示客户端2表示服务器默认为1
-- intPin = pio.P0_22, --以太网芯片中断通知引脚
-- rstPin = pio.P0_23, --复位以太网芯片引脚
-- spiCs = pio.P0_23, --spi片选
-- serverAddr = "192.168.1.112", --做服务器应用时,本机的地址
-- serverPort = 1888, --做服务器应用时,本机的端口
-- serverGateway = "192.168.1.1", --做服务器应用时,本机的网关地址
-- 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 true/false,执行成功返回true,失败返回false。
-- @usage
-- 设置为蜂窝数据网络:
-- c = link.setNetwork(link.CELLULAR, para)
-- 设置为CH395以太网络
-- link.setNetwork(link.CH395, para)
-- 设置为ESP8266WIFI网络:
-- link.setNetwork(link.ESP8266, para)
function openNetwork(mode, para)
local tSocketModule = {
[CH395] = socketCh395,
[W5500] = socketW5500,
[ESP8266] = socketESP8266
}
local md = mode or CELLULAR
closeNetWork()
network = md
if network == CELLULAR then
net.switchFly(false)
return true
else
ipAddr = tSocketModule[network].open(para)
if ipAddr ~= "" then
return true
else
log.info('link', 'open CH395 err')
return false
end
end
return false
end
--- 关闭链路层网络类型
-- 注意:关闭链路层网络类型,不会改变链路层网络类型,需要打开链路层网络类型配置才能切换。
-- @return true/false,执行成功返回true,失败返回false。
-- @usage
-- 关闭链路层网络类型:
-- link.closeNetWork()
function closeNetWork()
local tSocketModule = {
[CH395] = socketCh395,
[W5500] = socketW5500,
[ESP8266] = socketESP8266
}
if network == CELLULAR then
-- 飞行模式
net.switchFly(true)
return true
else
return tSocketModule[network].close()
end
return false
end
--- 获取链路层网络类型
-- @return networknumber类型取值如下
-- link.CELLULAR蜂窝模组数据网络
-- link.CH395CH395以太网络
-- link.ESP8266ESP8266WIFI网络
-- @usage
-- 获取数据网络类型:
-- mode = link.getNetwork()
function getNetwork()
return network
end