You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
446 lines
12 KiB
446 lines
12 KiB
6 years ago
|
/*
|
||
|
Package for parsing devicePrs byte stream to struct.
|
||
|
*/
|
||
|
|
||
|
package parser
|
||
|
|
||
|
import (
|
||
|
"bytes"
|
||
|
"encoding/binary"
|
||
|
"fmt"
|
||
|
"time"
|
||
|
//"encoding/hex"
|
||
|
"strconv"
|
||
|
"sort"
|
||
|
"github.com/ipsusila/opt"
|
||
|
|
||
|
"../model"
|
||
|
)
|
||
|
|
||
|
type ruptelaTchHeader struct {
|
||
|
Length uint16
|
||
|
IMEI uint64
|
||
|
CommandID byte
|
||
|
PortID byte
|
||
|
Reserved uint16
|
||
|
Timestamp uint32
|
||
|
}
|
||
|
|
||
|
type DeviceRecordHeader struct {
|
||
|
Imei uint64
|
||
|
Tstamp time.Time
|
||
|
NumRec uint16
|
||
|
DeviceRecords []DeviceRecord
|
||
|
}
|
||
|
|
||
|
type DeviceRecord struct {
|
||
|
Tstamp time.Time
|
||
|
TstampInt int64
|
||
|
Imei uint64
|
||
|
Longitude float64
|
||
|
Latitude float64
|
||
|
Altitude uint16
|
||
|
Angle uint16
|
||
|
Satelites uint16
|
||
|
Speed uint16
|
||
|
TagDevices map[string]TagDevice
|
||
|
}
|
||
|
|
||
|
type TagDevice struct {
|
||
|
TagName string
|
||
|
TagId string
|
||
|
TagDataType string
|
||
|
TagVal string
|
||
|
}
|
||
|
|
||
|
// ByTstamp implements sort.Interface for []DeviceRecord based on
|
||
|
// the TstampInt field.
|
||
|
type ByTstamp []DeviceRecord
|
||
|
|
||
|
func (a ByTstamp) Len() int { return len(a) }
|
||
|
func (a ByTstamp) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
||
|
func (a ByTstamp) Less(i, j int) bool { return a[i].TstampInt < a[j].TstampInt }
|
||
|
|
||
|
/*
|
||
|
Verify that content of the byte stream match defined format
|
||
|
*/
|
||
|
func verifyStream(data []byte, minSize int, maxSize int, tagOption *opt.Options) (imei uint64, cmd byte, rec DeviceRecordHeader, err error) {
|
||
|
dlen := len(data)
|
||
|
if dlen < minSize {
|
||
|
return 0, 0,rec, fmt.Errorf("Packet size is too small (< %d)", dlen)
|
||
|
} else if dlen > maxSize {
|
||
|
return 0, 0,rec, fmt.Errorf("Packet size is greater than maximum allowed size (%d)", dlen)
|
||
|
}
|
||
|
|
||
|
//Extract packet size
|
||
|
sz := int(binary.BigEndian.Uint16(data[:2]))
|
||
|
if sz != (dlen - 4) {
|
||
|
return 0, 0,rec, fmt.Errorf("Size field mismatched (!%d)", dlen)
|
||
|
}
|
||
|
|
||
|
//extract IMEI
|
||
|
imei = binary.BigEndian.Uint64(data[2:10])
|
||
|
|
||
|
//extract and verify CRC
|
||
|
crc := binary.BigEndian.Uint16(data[dlen-2:])
|
||
|
ccrc := crc16CCITT(data[2 : dlen-2])
|
||
|
if crc != ccrc {
|
||
|
return 0, 0,rec, fmt.Errorf("Invalid Ruptela packet (CRC-16 does not match)")
|
||
|
}
|
||
|
|
||
|
//parse data
|
||
|
rec = parseData(data,imei,tagOption)
|
||
|
|
||
|
//extract command
|
||
|
cmd = data[10]
|
||
|
|
||
|
return imei, cmd, rec, nil
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
Parse byte stream and convert to standard record format
|
||
|
*/
|
||
|
func parseData(data []byte, imei uint64, tagOption *opt.Options) (rec DeviceRecordHeader) {
|
||
|
//allocate records
|
||
|
rec.Tstamp = time.Now()
|
||
|
rec.Imei = imei
|
||
|
|
||
|
tagOption_ := tagOption.GetObjectArray("tags")
|
||
|
lengthTags := len(tagOption_)
|
||
|
tags := make(map[string]TagDevice)
|
||
|
for i:=0; i<lengthTags; i++ {
|
||
|
var tag TagDevice
|
||
|
tag.TagName = tagOption_[i].GetString("tagName","")
|
||
|
tag.TagId = tagOption_[i].GetString("tagId","")
|
||
|
tag.TagDataType = tagOption_[i].GetString("tagDataType","")
|
||
|
tag.TagVal = ""
|
||
|
|
||
|
tags[tag.TagId] = tag
|
||
|
}
|
||
|
|
||
|
//number of records
|
||
|
currByte := 12
|
||
|
plusByte := currByte + 1
|
||
|
numRec := convBinaryToUint16(addOneByteInTwoByte(data[currByte:plusByte]),2,"numRec")
|
||
|
//fmt.Printf("Number of records %d\n", numRec)
|
||
|
rec.NumRec = numRec
|
||
|
deviceRecords := make([]DeviceRecord, 0)
|
||
|
for j := 0; j < int(numRec); j++ {
|
||
|
var deviceRecord DeviceRecord
|
||
|
|
||
|
//imei
|
||
|
deviceRecord.Imei = imei
|
||
|
|
||
|
//Timestamp
|
||
|
currByte = plusByte
|
||
|
plusByte = currByte + 4
|
||
|
tstampInt := convBinaryToInt32(data[currByte:plusByte],8,"tstampInt")
|
||
|
tstamp := time.Unix(int64(tstampInt), 0)
|
||
|
//fmt.Printf("Timestamp %v\n",tstamp.String())
|
||
|
deviceRecord.TstampInt = int64(tstampInt)
|
||
|
deviceRecord.Tstamp = tstamp
|
||
|
|
||
|
//Priority
|
||
|
currByte = plusByte
|
||
|
plusByte = currByte + 2
|
||
|
|
||
|
//Longitude
|
||
|
currByte = plusByte
|
||
|
plusByte = currByte + 4
|
||
|
lonInt := convBinaryToInt32(data[currByte:plusByte],4,"lonInt")
|
||
|
lon :=float64(lonInt)/10000000
|
||
|
//fmt.Printf("lon %f\n",lon)
|
||
|
deviceRecord.Longitude = lon
|
||
|
|
||
|
//Latitude
|
||
|
currByte = plusByte
|
||
|
plusByte = currByte + 4
|
||
|
latInt := convBinaryToInt32(data[currByte:plusByte],4,"latInt")
|
||
|
lat :=float64(latInt)/10000000
|
||
|
//fmt.Printf("lat %f\n",lat)
|
||
|
deviceRecord.Latitude = lat
|
||
|
|
||
|
//Altitude
|
||
|
currByte = plusByte
|
||
|
plusByte = currByte + 2
|
||
|
alt := convBinaryToUint16(data[currByte:plusByte],2,"alt")
|
||
|
//fmt.Printf("alt %d\n",alt)
|
||
|
deviceRecord.Altitude = alt/10
|
||
|
|
||
|
//Angle
|
||
|
currByte = plusByte
|
||
|
plusByte = currByte + 2
|
||
|
angle := convBinaryToUint16(data[currByte:plusByte],2,"angle")
|
||
|
//fmt.Printf("angle %d\n",angle)
|
||
|
deviceRecord.Angle = angle/100
|
||
|
|
||
|
//Satelite
|
||
|
currByte = plusByte
|
||
|
plusByte = currByte + 1
|
||
|
satelites := convBinaryToUint16(addOneByteInTwoByte(data[currByte:plusByte]),2,"satelites")
|
||
|
//fmt.Printf("satelites %d\n",satelites)
|
||
|
deviceRecord.Satelites = satelites
|
||
|
|
||
|
//Speed
|
||
|
currByte = plusByte
|
||
|
plusByte = currByte + 2
|
||
|
speed := convBinaryToUint16(data[currByte:plusByte],2,"speed")
|
||
|
//fmt.Printf("speed %d\n",speed)
|
||
|
deviceRecord.Speed = speed
|
||
|
|
||
|
//HDOP - 09 (HEX) 9 (DEC) need to be divided by 10
|
||
|
currByte = plusByte
|
||
|
plusByte = currByte + 1
|
||
|
|
||
|
//IO data cause record - 09 (HEX) 9 (DEC)
|
||
|
currByte = plusByte
|
||
|
plusByte = currByte + 1
|
||
|
|
||
|
//--------------------------------------------------------------------
|
||
|
//read 1 Byte I/O values
|
||
|
//--------------------------------------------------------------------
|
||
|
//Total IO elements, which length is 1 Byte
|
||
|
currByte = plusByte
|
||
|
plusByte = currByte + 1
|
||
|
totalOneByteIO := convBinaryToUint16(addOneByteInTwoByte(data[currByte:plusByte]),2,"totalOneByteIO")
|
||
|
//fmt.Printf("total IO1 %d\n",totalOneByteIO)
|
||
|
for i := 0; i < int(totalOneByteIO); i++ {
|
||
|
|
||
|
currByte = plusByte
|
||
|
plusByte = currByte + 1
|
||
|
ioID := convBinaryToUint16(addOneByteInTwoByte(data[currByte:plusByte]),2,"io1ID")
|
||
|
//fmt.Printf("io1ID %d\n",ioID)
|
||
|
|
||
|
currByte = plusByte
|
||
|
plusByte = currByte + 1
|
||
|
ioVal := convBinaryToUint16(addOneByteInTwoByte(data[currByte:plusByte]),2,"io1Val")
|
||
|
//fmt.Printf("io1Val %d\n",ioVal)
|
||
|
|
||
|
tagDevice_ := tags[strconv.Itoa(int(ioID))]
|
||
|
tagDevice_.TagId = strconv.Itoa(int(ioID))
|
||
|
tagDevice_.TagVal = strconv.Itoa(int(ioVal))
|
||
|
if tagDevice_.TagName != ""{
|
||
|
tags[strconv.Itoa(int(ioID))] = tagDevice_
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
//--------------------------------------------------------------------
|
||
|
//read 2 Byte I/O values
|
||
|
//--------------------------------------------------------------------
|
||
|
//Total IO elements, which length is 2 Byte
|
||
|
currByte = plusByte
|
||
|
plusByte = currByte + 1
|
||
|
totalTwoByteIO := convBinaryToUint16(addOneByteInTwoByte(data[currByte:plusByte]),2,"totalTwoByteIO")
|
||
|
//fmt.Printf("total IO2 %d\n",totalTwoByteIO)
|
||
|
for i := 0; i < int(totalTwoByteIO); i++ {
|
||
|
|
||
|
currByte = plusByte
|
||
|
plusByte = currByte + 1
|
||
|
ioID := convBinaryToUint16(addOneByteInTwoByte(data[currByte:plusByte]),2,"io2ID")
|
||
|
//fmt.Printf("io2ID %d\n",ioID)
|
||
|
|
||
|
currByte = plusByte
|
||
|
plusByte = currByte + 2
|
||
|
ioVal := convBinaryToUint16(data[currByte:plusByte],2,"io2Val")
|
||
|
//fmt.Printf("io2Val %d\n",ioVal)
|
||
|
|
||
|
tagDevice_ := tags[strconv.Itoa(int(ioID))]
|
||
|
tagDevice_.TagId = strconv.Itoa(int(ioID))
|
||
|
tagDevice_.TagVal = strconv.Itoa(int(ioVal))
|
||
|
if tagDevice_.TagName != ""{
|
||
|
tags[strconv.Itoa(int(ioID))] = tagDevice_
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//--------------------------------------------------------------------
|
||
|
//read 4 Byte I/O values
|
||
|
//--------------------------------------------------------------------
|
||
|
//Total IO elements, which length is 4 Byte
|
||
|
currByte = plusByte
|
||
|
plusByte = currByte + 1
|
||
|
totalFourByteIO := convBinaryToUint16(addOneByteInTwoByte(data[currByte:plusByte]),2,"totalFourByteIO")
|
||
|
//fmt.Printf("total IO4 %d\n",totalFourByteIO)
|
||
|
for i := 0; i < int(totalFourByteIO); i++ {
|
||
|
|
||
|
currByte = plusByte
|
||
|
plusByte = currByte + 1
|
||
|
ioID := convBinaryToUint16(addOneByteInTwoByte(data[currByte:plusByte]),2,"io4ID")
|
||
|
//fmt.Printf("io4ID %d\n",ioID)
|
||
|
|
||
|
currByte = plusByte
|
||
|
plusByte = currByte + 4
|
||
|
ioVal := convBinaryToInt32(data[currByte:plusByte],4,"io4Val")
|
||
|
//fmt.Printf("io4Val %d\n",ioVal)
|
||
|
|
||
|
tagDevice_ := tags[strconv.Itoa(int(ioID))]
|
||
|
tagDevice_.TagId = strconv.Itoa(int(ioID))
|
||
|
tagDevice_.TagVal = strconv.FormatInt(int64(ioVal), 10)
|
||
|
if tagDevice_.TagName != ""{
|
||
|
tags[strconv.Itoa(int(ioID))] = tagDevice_
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//--------------------------------------------------------------------
|
||
|
//read 8 Byte I/O values
|
||
|
//--------------------------------------------------------------------
|
||
|
//Total IO elements, which length is 8 Byte
|
||
|
currByte = plusByte
|
||
|
plusByte = currByte + 1
|
||
|
total8ByteIO := convBinaryToUint16(addOneByteInTwoByte(data[currByte:plusByte]),2,"total8ByteIO")
|
||
|
//fmt.Printf("total IO8 %d\n",total8ByteIO)
|
||
|
for i := 0; i < int(total8ByteIO); i++ {
|
||
|
|
||
|
currByte = plusByte
|
||
|
plusByte = currByte + 1
|
||
|
ioID := convBinaryToUint16(addOneByteInTwoByte(data[currByte:plusByte]),2,"io8ID")
|
||
|
//fmt.Printf("io8ID %d\n",ioID)
|
||
|
|
||
|
currByte = plusByte
|
||
|
plusByte = currByte + 8
|
||
|
ioVal := convBinaryToInt64(data[currByte:plusByte],8,"io8Val")
|
||
|
//fmt.Printf("io8Val %d\n",ioVal)
|
||
|
|
||
|
tagDevice_ := tags[strconv.Itoa(int(ioID))]
|
||
|
tagDevice_.TagId = strconv.Itoa(int(ioID))
|
||
|
tagDevice_.TagVal = strconv.FormatInt(int64(ioVal), 10)
|
||
|
if tagDevice_.TagName != ""{
|
||
|
tags[strconv.Itoa(int(ioID))] = tagDevice_
|
||
|
}
|
||
|
}
|
||
|
|
||
|
deviceRecord.TagDevices = tags
|
||
|
deviceRecords = append(deviceRecords, deviceRecord)
|
||
|
|
||
|
}
|
||
|
|
||
|
//sort ascending by date
|
||
|
sort.Sort(ByTstamp(deviceRecords))
|
||
|
|
||
|
rec.DeviceRecords = deviceRecords
|
||
|
|
||
|
//CRC-16
|
||
|
currByte = plusByte
|
||
|
plusByte = currByte + 2
|
||
|
|
||
|
return rec
|
||
|
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
Convert Binary to uint16
|
||
|
*/
|
||
|
func addOneByteInTwoByte(data []byte) (resultData []byte){
|
||
|
|
||
|
dataNew := make([]byte, 2)
|
||
|
dataNew[0] = 0x0
|
||
|
copy(dataNew[1:], data)
|
||
|
|
||
|
return dataNew
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
Convert Binary to uint16
|
||
|
*/
|
||
|
func convBinaryToUint16(data []byte, byteLength int, desc string) (reslt uint16){
|
||
|
var dataInt uint16
|
||
|
dataNew := make([]byte, byteLength)
|
||
|
copy(dataNew[0:], data)
|
||
|
buf1 := bytes.NewReader(dataNew)
|
||
|
err := binary.Read(buf1, binary.BigEndian, &dataInt)
|
||
|
if err != nil {
|
||
|
fmt.Printf("binary.Read failed %v :%v\n", desc, err)
|
||
|
dataInt = 0
|
||
|
}
|
||
|
return dataInt
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
Convert Binary to int32
|
||
|
*/
|
||
|
func convBinaryToInt32(data []byte, byteLength int, desc string) (reslt int32){
|
||
|
var dataInt int32
|
||
|
dataNew := make([]byte, byteLength)
|
||
|
copy(dataNew[0:], data)
|
||
|
buf1 := bytes.NewReader(dataNew)
|
||
|
err := binary.Read(buf1, binary.BigEndian, &dataInt)
|
||
|
if err != nil {
|
||
|
fmt.Printf("binary.Read failed %v :%v\n", desc, err)
|
||
|
dataInt = 0
|
||
|
}
|
||
|
return dataInt
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
Convert Binary to int64
|
||
|
*/
|
||
|
func convBinaryToInt64(data []byte, byteLength int, desc string) (reslt int64){
|
||
|
var dataInt int64
|
||
|
dataNew := make([]byte, byteLength)
|
||
|
copy(dataNew[0:], data)
|
||
|
buf1 := bytes.NewReader(dataNew)
|
||
|
err := binary.Read(buf1, binary.BigEndian, &dataInt)
|
||
|
if err != nil {
|
||
|
fmt.Printf("binary.Read failed %v :%v\n", desc, err)
|
||
|
dataInt = 0
|
||
|
}
|
||
|
return dataInt
|
||
|
}
|
||
|
|
||
|
func parseTchStream(data []byte, minSize int, maxSize int) (*model.DisplayRequest, error) {
|
||
|
rec := &model.DisplayRequest{}
|
||
|
|
||
|
//check length
|
||
|
dataLen := len(data)
|
||
|
if dataLen < minSize {
|
||
|
return rec, fmt.Errorf("Packet size is too small (< %d)", dataLen)
|
||
|
}
|
||
|
if dataLen > maxSize {
|
||
|
return rec, fmt.Errorf("Packet size is greater than maximum allowed size (%d)", dataLen)
|
||
|
}
|
||
|
|
||
|
//create buffer
|
||
|
buf := bytes.NewBuffer(data)
|
||
|
|
||
|
//read packet header part
|
||
|
pktHdr := &ruptelaTchHeader{}
|
||
|
err := binary.Read(buf, binary.BigEndian, pktHdr)
|
||
|
if err != nil {
|
||
|
return rec, fmt.Errorf("Failed to read packet header (%v)", err)
|
||
|
}
|
||
|
//2+8+1+1+2+4 (len, imei, cmd, port, reserved, timestamp)
|
||
|
idx := 2 + 8 + 1 + 1 + 2 + 4
|
||
|
|
||
|
rec.IMEI = pktHdr.IMEI
|
||
|
rec.PortID = pktHdr.PortID
|
||
|
rec.Timestamp = time.Unix(int64(pktHdr.Timestamp), 0)
|
||
|
rec.Data = data[idx:(dataLen - 2)]
|
||
|
|
||
|
return rec, nil
|
||
|
}
|
||
|
|
||
|
// Calculate CRC16 with CCITT algorithm
|
||
|
// Based on C/C++ code in DevicePrs protocol documents
|
||
|
// CRC calculation omits Length(2-bytes) and CRC itself (2-bytes)
|
||
|
func crc16CCITT(data []byte) uint16 {
|
||
|
//--------------------------------------------------------------------
|
||
|
var ucBit, ucCarry uint16
|
||
|
//--------------------------------------------------------------------
|
||
|
var usPoly uint16 = 0x8408 //reversed 0x1021
|
||
|
var usCRC uint16 //initialized as zero
|
||
|
//--------------------------------------------------------------------
|
||
|
for _, d := range data {
|
||
|
usCRC ^= uint16(d)
|
||
|
for ucBit = 0; ucBit < 8; ucBit++ {
|
||
|
ucCarry = usCRC & 1
|
||
|
usCRC >>= 1
|
||
|
if ucCarry != 0 {
|
||
|
usCRC ^= usPoly
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
//--------------------------------------------------------------------
|
||
|
return usCRC
|
||
|
//--------------------------------------------------------------------
|
||
|
}
|