lbsLoc.lua 11 KB

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