| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403 |
- -- LuaTools需要PROJECT和VERSION这两个信息
- PROJECT = "JT808"
- VERSION = "2.0.0"
- --[[
- 本demo演示使用string.pack与unpack函数,实现JT808 终端注册协议数据生成与解析
- ]]
- --加载sys库
- sys = require("sys")
- local netLed = require("netLed")
- --GPIO27配置为输出,用作网络指示灯
- local LEDA= gpio.setup(27, 0, gpio.PULLUP)
- -- 自定义位运算函数
- local bit = {}
- --终端消息ID
- T_COMMON_RSP = 0x0001
- T_REGISTER = 0x0100
- function bit.bxor(a, b)
- return a ~ b -- 按位异或
- end
- --十六进制转二进制
- local function hexToBinary(hexStr)
- local binaryData = {}
- for i = 1, #hexStr, 2 do
- local byte = tonumber(hexStr:sub(i, i+1), 16) -- 每两个字符转换为一个字节
- table.insert(binaryData, string.char(byte))
- end
- return table.concat(binaryData) -- 拼接成二进制字符串
- end
- --字符串转字节数组
- local function stringToBytes(hexStr)
- local bytes = {}
- for i = 1, #hexStr, 2 do
- local byteStr = hexStr:sub(i, i+1)
- local byte = tonumber(byteStr, 16)
- table.insert(bytes, byte)
- end
- return bytes
- end
- --BCD编码
- local function encodeBcdNum(d, n)
- if d:len() < n then
- return (string.rep('0', n - d:len()) .. d):fromHex()
- else
- return (d:sub(1, n)):fromHex()
- end
- end
- --计算异或校验(十六进制字符串)
- local function calculateXor(data)
- local sum = 0
- for i = 1, #data, 2 do
- local byte = tonumber(data:sub(i, i+1), 16)
- -- 检查是否转换成功
- if not byte then
- error(string.format("Invalid hex character: '%s' at position %d", byteStr, i))
- end
- -- 打印当前字节的值(十六进制和十进制)
- --print(string.format("当前字节: %s (十进制: %d)", byteStr, byte))
- if not byte then
- error("Invalid hex character in input string!")
- end
- sum = bit.bxor(sum, byte)
- -- 打印异或后的中间结果
- --print(string.format("异或后 sum = %d (十六进制: 0x%X)", sum, sum))
- end
- return sum
- end
- --
- local function calCrc(hexStr)
- -- 解析hexStr转换为十六进制字符数组
- local byteArray = HexOutput(hexStr)
- -- 计算 CRC
- local crc = calculateXor(byteArray)
- log.info("CRCxor:",crc)
- -- 返回 CRC 值
- return crc
- end
- --将时间转换为BCD格式
- function timeToBCD()
- local t = os.date("*t")
-
- -- 转换为BCD格式
- local year = string.format("%02d", t.year % 100)
- local month = string.format("%02d", t.month)
- local day = string.format("%02d", t.day)
- local hour = string.format("%02d", t.hour)
- local min = string.format("%02d", t.min)
- local sec = string.format("%02d", t.sec)
-
- -- 组合BCD格式字符串
- local bcdTime = year .. month .. day .. hour .. min .. sec
-
- return bcdTime
- end
- --构建通用应答
- function cmnRsp(svrSeq, svrId, result)
- return string.pack('>HHb', svrSeq, svrId, result)
- -- >表示使用大端字节序;
- -- HH 表示两个 16 位无符号整数(svrSeq 和 svrId);
- -- b 表示一个 8 位有符号整数(result)
- end
- --构建设备注册
- function register(ProvinceId,CityId,ManufactureId,TerminalModule,TerminalId,CarColor,CarId)
- local termMd = TerminalModule..string.rep(string.char(0), 16 - (TerminalModule):len())..string.rep(string.char(0), 4)
- --log.info("HexDataByteLength:",#termMd)
- --log.info("HexData is: ",termMd)
- return string.pack('>HHc5c20c7Bc11',ProvinceId,CityId,ManufactureId,termMd,TerminalId:fromHex(),CarColor,CarId)
- end
- --构建位置信息汇报
- function locRpt(alarm, status, lat, lng, alt, spd, course, tm, extInfo)
- -- 使用 '>iiiiHHH' 格式打包数据
- return string.pack('>iiiiHHH', alarm, status, lat, lng, alt, spd, course)..tm:fromHex().. extInfo
- end
- function HexOutput(data)
- log.info("HexDataByteLength:",#data)
- local hex = ""
- for i = 1, #data do
- hex = hex.. string.format("%02x", string.byte(data, i))
- end
- log.info("HexData is: ",hex)
- return hex
- end
- --封装一个完整的JT808终端注册数据帧
- function encodeReg(phoneNo,tSendSeq)
- --消息体
- local msgBody = register(12,123,'10001','GT808','00000000000002',0,'41048063212')
- --消息头
- local msgHead =string.pack('>HH',T_REGISTER,msgBody:len())..encodeBcdNum(phoneNo,12)..tSendSeq
- local curSeq = tSendSeq
- tSendSeq = (tSendSeq == 0xFFFF) and 0 or (tSendSeq + 1)
- --校验码
- HexOutput(msgHead .. msgBody)
- local frame = msgHead .. msgBody
- log.info("encodeReg Frame length is: ",#frame)
- local crc = calCrc(frame)
-
- --转义
- local s = msgHead .. msgBody .. string.char(crc)
- s = s:gsub('\125', '\125\1') -- 7D -> 7D 01
- s = s:gsub('\126', '\125\2') -- 7E -> 7D 02
- return string.char(0x7E) .. s .. string.char(0x7E), curSeq
- end
- function decodeCmnRsp(s)
- log.info("hereNow.",s)
- --local start, tail, msg = s:find('^(7E[^7E]+7E)$')
- --local decPacket, msgAttr = {}
- -- 检查是否以 7E 开头和结尾
- if not s:match("^7E") or not s:match("7E$") then
- log.warn("prot.decode", "packet does not start or end with 7E")
- return nil
- end
- -- 匹配以 7E 开头和结尾的数据包
- local start, tail, msg = s:find('^(7E.+7E)$') -- 改为允许中间部分包含 7E
- log.info("start:", start)
- log.info("tail:", tail)
- log.info("msg:", msg)
- if not msg then
- -- 如果未匹配到
- log.warn('prot.decode', 'wait packet complete')
- return nil
- end
-
- --反转义
- msg = msg:gsub('\125\2', '\126') -- 7D 02 -> 7E
- msg = msg:gsub('\125\1', '\125') -- 7D 01 -> 7D
- log.info("msg:", msg,msg:len())
- if msg:len() < 13 then
- log.error('prot.decode', 'packet len is too short')
- return s:sub(tail + 1, -1), nil
- end
- local hexArray = stringToBytes(msg)
- local dataHexStr = msg:sub(3, -5)
- log.info("消息体和其字节长度:", dataHexStr,dataHexStr:len())
- local crcVal = calculateXor(dataHexStr)
- print("XOR校验值:", crcVal)
- if crcVal ~= hexArray[#hexArray-1] then
- log.error('prot.decode', 'crc value error!')
- return s:sub(tail + 1, -1), nil
- else
- log.info("XOR:校验通过")
- end
- ----------------------------解析消息id
- msgdata = msg:sub(3, 6)
- -- 转换为二进制数据
- local binaryData = hexToBinary(msgdata)
- -- 解析二进制数据
- msgId = string.unpack(">H", binaryData)
- -- 输出结果
- log.info("消息id十六进制:", string.format("0x%04X", msgId))
- ----------------------------解析应答结果
- msgdata = msg:sub(7, 8)
- -- 转换为二进制数据
- local binaryData = hexToBinary(msgdata)
- -- 解析二进制数据
- result = string.unpack(">B", binaryData)
- -- 输出结果
- log.info("结果十六进制:", string.format("0x%02X", result))
- ----------------------------解析鉴权码
- msgdata = msg:sub(9, 10)
- -- 转换为二进制数据
- local binaryData = hexToBinary(msgdata)
- -- 解析二进制数据
- result = string.unpack(">B", binaryData)
- -- 输出结果
- log.info("鉴权码十六进制:", string.format("0x%02X", result))
- ----------------------------解析电话号码
- msgdata = msg:sub(11, 22)
- -- 转换为二进制数据
- local binaryData = hexToBinary(msgdata)
- -- 解析二进制数据
- local b1, b2, b3, b4, b5, b6 = string.unpack(">BBBBBB", binaryData)
- -- 格式化输出为 6 字节的十六进制字符串
- local TerminalPhoneNo = string.format("0x%02X%02X%02X%02X%02X%02X", b1, b2, b3, b4, b5, b6)
- -- 输出电话号码
- log.info("电话号码十六进制:", TerminalPhoneNo)
- ----------------------------解析标头流水号
- msgdata = msg:sub(23, 26)
- -- 转换为二进制数据
- local binaryData = hexToBinary(msgdata)
- -- 解析二进制数据
- local ManualMsgNum = string.unpack(">H", binaryData)
- -- 输出结果
- log.info("标头流水号十六进制:", string.format("0x%04X", ManualMsgNum))
- ----------------------------解析标尾流水号
- msgdata = msg:sub(23, 26)
- -- 转换为二进制数据
- local binaryData = hexToBinary(msgdata)
- -- 解析二进制数据
- local MsgNum = string.unpack(">H", binaryData)
- -- 输出结果
- log.info("标尾流水号十六进制:", string.format("0x%04X", MsgNum))
- ----------------------------解析鉴权码
- msgdata = msg:sub(33, 50)
- -- 转换为二进制数据
- local binaryData = hexToBinary(msgdata)
- local b1, b2, b3, b4, b5, b6, b7, b8, b9 = string.unpack(">BBBBBBBBB", binaryData)
- -- 格式化输出为 6 字节的十六进制字符串
- local JianquanCode = string.format("0x%02X%02X%02X%02X%02X%02X%02X%02X%02X", b1, b2, b3, b4, b5, b6, b7, b8, b9)
- -- 输出结果
- log.info("鉴权码十六进制:", JianquanCode)
- end
- --新建任务,每休眠2000ms继续一次
- sys.taskInit(function()
-
- --实验1:构建立通用应答数据帧,协议可参考JT808协议8.1与8.2
- --输入参数:
- --应答流水号为123,
- --应答 ID为456,
- --结果为1,
- local data = cmnRsp(123, 456, 1)
- HexOutput(data) --HexDataByteLength:5,HexData is:007b01c801 007b为123,01c8为456,1为1
- --实验2:构建终端注册数据帧,协议可参考JT808协议8.5
- --输入参数:
- --12, --省域 ID WORD
- --123, --市县域 ID WORD
- --'10001', --制造商 ID BYTE[5]
- --'GT808' --终端型号 BYTE[20]
- --'00000000000002', --终端 ID BYTE[7]
- --0, --车牌颜色 BYTE
- --'41048063212') --车辆标识 STRING VIN或车牌号均为固定长度,这里假设字符串长度为11
- local data = register(12,123,'10001','GT808','00000000000002',0,'41048063212')
- HexOutput(data)--[HexDataByteLength:] 48,[HexData is: ]
- --输出解析:
- --000c,对应12, --省域 ID WORD
- --007b,对应123, --市县域 ID WORD
- --3130303031,对应'10001', --制造商 ID BYTE[5]
- --4754383038,对应'GT808' --终端型号 BYTE[20],不足字节补0
- --0000000000
- --0000000000
- --0000000000
- --00000000000002 对应'00000000000002', --终端 ID BYTE[7]
- --000, --车牌颜色 BYTE
- --3431303438303633323132,对应'41048063212') --车辆标识 STRING
-
- --实验3:构建位置信息汇报数据帧,协议可参考JT808协议8.18
- --输入参数:
- --0, 报警标志 DWORD
- --1, 状态位 DWORD
- --12345678, 纬度 DWORD
- --87654321, 经度 DWORD
- --100, 高程 WORD
- --60, 速度 WORD
- --180, 方向 WORD
- --os.time(), 时间 BCD[6] YY-MM-DD-hh-mm-ss(GMT+8 时间,本标准中之后涉及的时间均采用此时区)
- --"extra"
- local time = timeToBCD();
- log.info("CurrentTime: ",time)
- local data = locRpt(0, 1, 12345678, 87654321, 100, 60, 180, time, "extra")
- HexOutput(data)--[HexDataByteLength:] 33,[HexData is: ] 000000000000000100bc614e05397fb10064003c00b42411241232046578747261
- --输出解析:
- --00000000,对应 0, 报警标志 DWORD,4字节
- --00000001,对应 1, 状态位 DWORD
- --00bc614e,对应 12345678, 纬度 DWORD
- --05397fb1, 对应 87654321, 经度 DWORD
- --0064,对应 100, 高程 WORD
- --003c,对应60, 速度 WORD
- --00b4,对应180, 方向 WORD
- --241124123204,对应时间 BCD[6] 24-11-24-12-32-04
- --6578747261,对应"extra"
- --实验4:封闭一个完整的JT808注册数据帧
- --输入参数:
- --'018068821447',手机号不足 12位,则在前补充数字,大陆手机号补充数字 0
- --'101',消息流水号
- local data = encodeReg('018068821447','101')
- HexOutput(data) --[HexDataByteLength:] 64
- --[HexData is: ] 7e01000030018068821447313031000c007b3130303031475438303800000000000000000000000000000000000000000002003431303438303633323132627e
- --输出解析:
- --7e,标识位固定为0x7e
- --0100,消息 ID
- --0030,消息体属性
- --018068821447,手机号
- --313031,消息流水号
- --000c007b3130303031475438303800000000000000000000000000000000000000000002003431303438303633323132,注册消息体
- --62,校验
- --7e,标识位固定为0x7e
- --实验5:解析JT808终端注册应答数据帧
- local hexStr = '7E8100000C0404571031660030003000736875616E67776569E07E'
- decodeCmnRsp(hexStr)
- end)
- -- 这里演示4G模块上网后,会自动点亮网络灯,方便用户判断模块是否正常开机
- sys.taskInit(function()
- while true do
- sys.wait(6000)
- if mobile.status() == 1 then
- gpio.set(27, 1)
- else
- gpio.set(27, 0)
- mobile.reset()
- end
- end
- end)
- -- 用户代码已结束---------------------------------------------
- -- 运行lua task,只能调用一次,而且必须写在末尾
- -- 结尾总是这一句
- sys.run()
- -- sys.run()之后后面不要加任何语句!!!!!
|