lbsLoc.lua 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331
  1. --[[
  2. @module lbsLoc
  3. @summary lbsLoc 发送基站定位请求
  4. @version 1.0
  5. @date 2022.12.16
  6. @author luatos
  7. @usage
  8. --注意:因使用了sys.wait()所有api需要在协程中使用
  9. -- 用法实例
  10. PRODUCT_KEY = "VmhtOb81EgZau6YyuuZJzwF6oUNGCbXi"
  11. local lbsLoc = require("lbsLoc")
  12. local function reqLbsLoc()
  13. lbsLoc.request(getLocCb)
  14. end
  15. 功能 :获取基站对应的经纬度后的回调函数
  16. 参数 :
  17. result:number类型,0表示成功,1表示网络环境尚未就绪,2表示连接服务器失败,3表示发送数据失败,4表示接收服务器应答超时,5表示服务器返回查询失败;为0时,后面的5个参数才有意义
  18. lat:string类型,纬度,整数部分3位,小数部分7位,例如031.2425864
  19. lng:string类型,经度,整数部分3位,小数部分7位,例如121.4736522
  20. addr:目前无意义
  21. time:string类型或者nil,服务器返回的时间,6个字节,年月日时分秒,需要转为十六进制读取
  22. 第一个字节:年减去2000,例如2017年,则为0x11
  23. 第二个字节:月,例如7月则为0x07,12月则为0x0C
  24. 第三个字节:日,例如11日则为0x0B
  25. 第四个字节:时,例如18时则为0x12
  26. 第五个字节:分,例如59分则为0x3B
  27. 第六个字节:秒,例如48秒则为0x30
  28. locType:numble类型或者nil,定位类型,0表示基站定位成功,255表示WIFI定位成功
  29. 返回值:无
  30. function getLocCb(result, lat, lng, addr, time, locType)
  31. log.info("testLbsLoc.getLocCb", result, lat, lng)
  32. -- 获取经纬度成功
  33. if result == 0 then
  34. log.info("服务器返回的时间", time:toHex())
  35. log.info("定位类型,基站定位成功返回0", locType)
  36. -- 失败
  37. end
  38. sys.timerStart(lbsLoc,20000)
  39. end
  40. reqLbsLoc()
  41. ]]
  42. local lbsLoc = {}
  43. local d1Name = "D1_TASKL"
  44. --- 阻塞等待网卡的网络连接上,只能用于任务函数中
  45. -- @string 任务标志
  46. -- @int 超时时间,如果==0或者空,则没有超时一致等待
  47. -- @... 其他参数和socket.linkup一致
  48. -- @return 失败或者超时返回false 成功返回true
  49. local function waitLink(taskName, timeout, ...)
  50. local is_err, result = socket.linkup(...)
  51. if is_err then
  52. return false
  53. end
  54. if not result then
  55. result = sys_wait(taskName, socket.LINK, timeout)
  56. else
  57. return true
  58. end
  59. if type(result) == 'table' and result[2] == 0 then
  60. return true
  61. else
  62. return false
  63. end
  64. end
  65. --- 阻塞等待IP或者域名连接上,如果加密连接还要等握手完成,只能用于任务函数中
  66. -- @string 任务标志
  67. -- @int 超时时间,如果==0或者空,则没有超时一致等待
  68. -- @... 其他参数和socket.connect一致
  69. -- @return 失败或者超时返回false 成功返回true
  70. local function connect(taskName,timeout, ... )
  71. local is_err, result = socket.connect(...)
  72. if is_err then
  73. return false
  74. end
  75. if not result then
  76. result = sys_wait(taskName, socket.ON_LINE, timeout)
  77. else
  78. return true
  79. end
  80. if type(result) == 'table' and result[2] == 0 then
  81. return true
  82. else
  83. return false
  84. end
  85. end
  86. --- 阻塞等待数据发送完成,只能用于任务函数中
  87. -- @string 任务标志
  88. -- @int 超时时间,如果==0或者空,则没有超时一致等待
  89. -- @... 其他参数和socket.tx一致
  90. -- @return
  91. -- @boolean 失败或者超时返回false,缓冲区满了或者成功返回true
  92. -- @boolean 缓存区是否满了
  93. local function tx(taskName,timeout, ...)
  94. local is_err, is_full, result = socket.tx(...)
  95. if is_err then
  96. return false, is_full
  97. end
  98. if is_full then
  99. return true, true
  100. end
  101. if not result then
  102. result = sys_wait(taskName, socket.TX_OK, timeout)
  103. else
  104. return true, is_full
  105. end
  106. if type(result) == 'table' and result[2] == 0 then
  107. return true, false
  108. else
  109. return false, is_full
  110. end
  111. end
  112. --- ASCII字符串 转化为 BCD编码格式字符串(仅支持数字)
  113. -- @string inStr 待转换字符串
  114. -- @number destLen 转换后的字符串期望长度,如果实际不足,则填充F
  115. -- @return string data,转换后的字符串
  116. -- @usage
  117. local function numToBcdNum(inStr,destLen)
  118. local l,t,num = string.len(inStr or ""),{}
  119. destLen = destLen or (inStr:len()+1)/2
  120. for i=1,l,2 do
  121. num = tonumber(inStr:sub(i,i+1),16)
  122. if i==l then
  123. num = 0xf0+num
  124. else
  125. num = (num%0x10)*0x10 + (num-(num%0x10))/0x10
  126. end
  127. table.insert(t,num)
  128. end
  129. local s = string.char(unpack(t))
  130. l = string.len(s)
  131. if l < destLen then
  132. s = s .. string.rep("\255",destLen-l)
  133. elseif l > destLen then
  134. s = string.sub(s,1,destLen)
  135. end
  136. return s
  137. end
  138. --- BCD编码格式字符串 转化为 号码ASCII字符串(仅支持数字)
  139. -- @string num 待转换字符串
  140. -- @return string data,转换后的字符串
  141. -- @usage
  142. local function bcdNumToNum(num)
  143. local byte,v1,v2
  144. local t = {}
  145. for i=1,num:len() do
  146. byte = num:byte(i)
  147. v1,v2 = bit.band(byte,0x0f),bit.band(bit.rshift(byte,4),0x0f)
  148. if v1 == 0x0f then break end
  149. table.insert(t,v1)
  150. if v2 == 0x0f then break end
  151. table.insert(t,v2)
  152. end
  153. return table.concat(t)
  154. end
  155. local function netCB(msg)
  156. log.info("未处理消息", msg[1], msg[2], msg[3], msg[4])
  157. end
  158. local function enCellInfo(s)
  159. local ret,t,mcc,mnc,lac,ci,rssi,k,v,m,n,cntrssi = "",{}
  160. for k,v in pairs(s) do
  161. mcc,mnc,lac,ci,rssi = v.mcc,v.mnc,v.tac,v.cid,((v.rsrq + 144) >31) and 31 or (v.rsrq + 144)
  162. local handle = nil
  163. for k,v in pairs(t) do
  164. if v.lac == lac and v.mcc == mcc and v.mnc == mnc then
  165. if #v.rssici < 8 then
  166. table.insert(v.rssici,{rssi=rssi,ci=ci})
  167. end
  168. handle = true
  169. break
  170. end
  171. end
  172. if not handle then
  173. table.insert(t,{mcc=mcc,mnc=mnc,lac=lac,rssici={{rssi=rssi,ci=ci}}})
  174. end
  175. log.info("rssi、mcc、mnc、lac、ci", rssi,mcc,mnc,lac,ci)
  176. end
  177. for k,v in pairs(t) do
  178. ret = ret .. pack.pack(">HHb",v.lac,v.mcc,v.mnc)
  179. for m,n in pairs(v.rssici) do
  180. cntrssi = bit.bor(bit.lshift(((m == 1) and (#v.rssici-1) or 0),5),n.rssi)
  181. ret = ret .. pack.pack(">bi",cntrssi,n.ci)
  182. end
  183. end
  184. return string.char(#t)..ret
  185. end
  186. local function enWifiInfo(tWifi)
  187. local ret,cnt,k,v = "",0
  188. if tWifi then
  189. for k,v in pairs(tWifi) do
  190. log.info("lbsLoc.enWifiInfo",k,v)
  191. ret = ret..pack.pack("Ab",(k:gsub(":","")):fromHex(),(v<0) and (v+255) or v)
  192. cnt = cnt+1
  193. end
  194. end
  195. return string.char(cnt)..ret
  196. end
  197. local function enMuid() --获取模块MUID
  198. local muid = mobile.muid()
  199. return string.char(muid:len())..muid
  200. end
  201. local function trans(str)
  202. local s = str
  203. if str:len()<10 then
  204. s = str..string.rep("0",10-str:len())
  205. end
  206. return s:sub(1,3).."."..s:sub(4,10)
  207. end
  208. local function taskClient(cbFnc, reqAddr, timeout, productKey, host, port,reqTime, reqWifi)
  209. while mobile.status() == 0 do
  210. if not sys.waitUntil("IP_READY", timeout) then return cbFnc(1) end
  211. end
  212. local retryCnt = 0
  213. sys.wait(3000)
  214. local reqStr = pack.pack("bAbAAAA", productKey:len(), productKey,
  215. (reqAddr and 2 or 0) + (reqTime and 4 or 0) + 8 +(reqWifi and 16 or 0) + 32, "",
  216. numToBcdNum(mobile.imei()), enMuid(),
  217. enCellInfo(mobile.getCellInfo()),
  218. enWifiInfo(reqWifi))
  219. log.info("reqStr", reqStr:toHex())
  220. local rx_buff = zbuff.create(17)
  221. -- sys.wait(5000)
  222. while true do
  223. netc = socket.create(nil, d1Name) -- 创建socket对象
  224. if not netc then cbFnc(6) return end -- 创建socket失败
  225. socket.debug(netc, false)
  226. socket.config(netc, nil, true, nil)
  227. waitLink(d1Name, 0, netc)
  228. local result = connect(d1Name, 15000, netc, host, port)
  229. if result then
  230. while true do
  231. log.info(" lbsloc socket_service connect true")
  232. sys.wait(2000);
  233. local result, _ = tx(d1Name, 0, netc, reqStr) ---发送数据
  234. if result then
  235. sys.wait(5000);
  236. local is_err, param, _, _ = socket.rx(netc, rx_buff) -- 接收数据
  237. log.info("是否接收和数据长度", not is_err, param)
  238. if not is_err then -- 如果接收成功
  239. socket.close(netc) -- 关闭连接
  240. socket.release(netc)
  241. local read_buff = rx_buff:toStr(0, param)
  242. rx_buff:clear()
  243. log.info("lbsLoc receive", read_buff:toHex())
  244. if read_buff:len() >= 11 and(read_buff:byte(1) == 0 or read_buff:byte(1) == 0xFF) then
  245. local locType = read_buff:byte(1)
  246. cbFnc(0, trans(bcdNumToNum(read_buff:sub(2, 6))),
  247. trans(bcdNumToNum(read_buff:sub(7, 11))), reqAddr and
  248. read_buff:sub(13, 12 + read_buff:byte(12)) or nil,
  249. read_buff:sub(reqAddr and (13 + read_buff:byte(12)) or 12, -1),
  250. locType)
  251. else
  252. log.warn("lbsLoc.query", "根据基站查询经纬度失败")
  253. if read_buff:byte(1) == 2 then
  254. log.warn("lbsLoc.query","main.lua中的PRODUCT_KEY和此设备在iot.openluat.com中所属项目的ProductKey必须一致,请去检查")
  255. else
  256. log.warn("lbsLoc.query","基站数据库查询不到所有小区的位置信息")
  257. log.warn("lbsLoc.query","在trace中向上搜索encellinfo,然后在电脑浏览器中打开http://bs.openluat.com/,手动查找encellinfo后的所有小区位置")
  258. log.warn("lbsLoc.query","如果手动可以查到位置,则服务器存在BUG,直接向技术人员反映问题")
  259. log.warn("lbsLoc.query","如果手动无法查到位置,则基站数据库还没有收录当前设备的小区位置信息,向技术人员反馈,我们会尽快收录")
  260. end
  261. cbFnc(5)
  262. end
  263. return
  264. else
  265. socket.close(netc)
  266. socket.release(netc)
  267. retryCnt = retryCnt+1
  268. if retryCnt>=3 then return cbFnc(4) end
  269. break
  270. end
  271. else
  272. socket.close(netc)
  273. socket.release(netc)
  274. retryCnt = retryCnt+1
  275. if retryCnt>=3 then return cbFnc(3) end
  276. break
  277. end
  278. end
  279. else
  280. socket.close(netc)
  281. socket.release(netc)
  282. retryCnt = retryCnt + 1
  283. if retryCnt >= 3 then return cbFnc(2) end
  284. end
  285. end
  286. end
  287. --[[
  288. 发送基站/WIFI定位请求(仅支持中国区域的位置查询)
  289. @api lbsLoc.request(cbFnc,reqAddr,timeout,productKey,host,port,reqTime,reqWifi)
  290. @function cbFnc 用户回调函数,回调函数的调用形式为:cbFnc(result,lat,lng,addr,time,locType)
  291. @bool reqAddr 是否请求服务器返回具体的位置字符串信息,目前此功能不完善,参数可以传nil
  292. @number timeout 请求超时时间,单位毫秒,默认20000毫秒
  293. @string productKey IOT网站上的产品证书,如果在main.lua中定义了PRODUCT_KEY变量,则此参数可以传nil
  294. @string host 服务器域名,此参数可选,目前仅lib中agps.lua使用此参数。应用脚本可以直接传nil
  295. @string port 服务器端口,此参数可选,目前仅lib中agps.lua使用此参数。应用脚本可以直接传nil
  296. @bool reqTime 是否需要服务器返回时间信息,true返回,false或者nil不返回,此参数可选,目前仅lib中agps.lua使用此参数。应用脚本可以直接传nil
  297. ]]
  298. function lbsLoc.request(cbFnc,reqAddr,timeout,productKey,host,port,reqTime,reqWifi)
  299. sysplus.taskInitEx(taskClient, d1Name, netCB, cbFnc,reqAddr or nil,timeout or 20000,productKey or _G.PRODUCT_KEY,host or "bs.openluat.com",port or "12411",reqTime,reqWifi)
  300. end
  301. return lbsLoc