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.

482 lines
13 KiB

6 years ago
/*
Package for parsing devicePrs byte stream to struct.
*/
package parser
import (
"bytes"
"encoding/binary"
"fmt"
"time"
"math"
6 years ago
//"encoding/hex"
"strconv"
"sort"
"github.com/ipsusila/opt"
"../model"
)
const (
ALLOWED_TIME_MINUTES = -5
)
6 years ago
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]
6 years ago
fmt.Printf("cmd %v\n",cmd)
6 years ago
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
6 years ago
tags_ := tags
6 years ago
//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)
strVal := fmt.Sprintf("%v",int16(ioVal))
//fmt.Printf("strVal %v\n",strVal)
6 years ago
tagDevice_ := tags_[strconv.Itoa(int(ioID))]
6 years ago
tagDevice_.TagId = strconv.Itoa(int(ioID))
tagDevice_.TagVal = strVal
6 years ago
if tagDevice_.TagName != ""{
6 years ago
//tags_[strconv.Itoa(int(ioID))] = tagDevice_
tags_[tagDevice_.TagName] = tagDevice_
delete(tags_, strconv.Itoa(int(ioID)))
6 years ago
}
}
//--------------------------------------------------------------------
//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)
// fmt.Printf("io2Val INT %d\n",int16(ioVal))
strVal := fmt.Sprintf("%v",int16(ioVal))
//fmt.Printf("strVal %v\n",strVal)
6 years ago
6 years ago
tagDevice_ := tags_[strconv.Itoa(int(ioID))]
tagDevice_.TagVal = strVal
6 years ago
if tagDevice_.TagName != ""{
6 years ago
tags_[tagDevice_.TagName] = tagDevice_
6 years ago
}
}
//--------------------------------------------------------------------
//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)
strVal := fmt.Sprintf("%v",int32(ioVal))
//fmt.Printf("strVal %v\n",strVal)
6 years ago
tagDevice_ := tags_[strconv.Itoa(int(ioID))]
tagDevice_.TagVal = strVal
6 years ago
if tagDevice_.TagName != ""{
6 years ago
tags_[tagDevice_.TagName] = tagDevice_
6 years ago
}
}
//--------------------------------------------------------------------
//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)
strVal := fmt.Sprintf("%v",int64(ioVal))
//fmt.Printf("strVal %v\n",strVal)
6 years ago
tagDevice_ := tags_[strconv.Itoa(int(ioID))]
tagDevice_.TagVal = strVal
6 years ago
if tagDevice_.TagName != ""{
6 years ago
tags_[tagDevice_.TagName] = tagDevice_
6 years ago
}
}
6 years ago
//Delete unset tags value
for tagKey := range tags {
tagDevice_ := tags_[tagKey]
if tagDevice_.TagId == tagKey {
delete(tags_, tagKey)
}
}
deviceRecord.TagDevices = tags_
tstampToNow := time.Since(deviceRecord.Tstamp)
hCurr := math.Round(tstampToNow.Minutes()*100)/100
if hCurr >= ALLOWED_TIME_MINUTES {
deviceRecords = append(deviceRecords, deviceRecord)
}
6 years ago
}
rec.NumRec = uint16(len(deviceRecords))
6 years ago
//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
//--------------------------------------------------------------------
}