/* Package for parsing devicePrs byte stream to struct. */ package parser import ( "bytes" "encoding/binary" "fmt" "time" "math" //"encoding/hex" "strconv" "sort" "github.com/ipsusila/opt" "../model" ) const ( ALLOWED_TIME_MINUTES = -5 ) 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] fmt.Printf("cmd %v\n",cmd) 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= ALLOWED_TIME_MINUTES { deviceRecords = append(deviceRecords, deviceRecord) } } rec.NumRec = uint16(len(deviceRecords)) //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 //-------------------------------------------------------------------- }