131 lines
3.4 KiB
Go
131 lines
3.4 KiB
Go
package jt808
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"errors"
|
|
|
|
"lingniu-vehicle-ingest/go/vehicle-gateway/internal/envelope"
|
|
)
|
|
|
|
var ErrResponseHeaderTooShort = errors.New("jt808 response header too short")
|
|
|
|
const (
|
|
msgTerminalGeneralResponse = uint16(0x0001)
|
|
msgTerminalRegister = uint16(0x0100)
|
|
msgPlatformGeneralResponse = uint16(0x8001)
|
|
msgPlatformRegisterAck = uint16(0x8100)
|
|
)
|
|
|
|
type AutoResponder struct {
|
|
authCode string
|
|
}
|
|
|
|
func NewAutoResponder(authCode string) AutoResponder {
|
|
if authCode == "" {
|
|
authCode = "g7gps"
|
|
}
|
|
return AutoResponder{authCode: authCode}
|
|
}
|
|
|
|
func (r AutoResponder) Respond(raw []byte, env envelope.FrameEnvelope) ([]byte, bool, error) {
|
|
if env.ParseStatus == envelope.ParseBadFrame {
|
|
return nil, false, nil
|
|
}
|
|
header, err := parseResponseHeader(raw)
|
|
if err != nil {
|
|
return nil, false, err
|
|
}
|
|
if header.messageID == msgTerminalGeneralResponse {
|
|
return nil, false, nil
|
|
}
|
|
if header.messageID == msgTerminalRegister {
|
|
body := make([]byte, 3, 3+len(r.authCode))
|
|
binary.BigEndian.PutUint16(body[0:2], header.sequence)
|
|
body[2] = 0
|
|
body = append(body, []byte(r.authCode)...)
|
|
return encodeResponse(msgPlatformRegisterAck, header, body), true, nil
|
|
}
|
|
body := make([]byte, 5)
|
|
binary.BigEndian.PutUint16(body[0:2], header.sequence)
|
|
binary.BigEndian.PutUint16(body[2:4], header.messageID)
|
|
body[4] = 0
|
|
return encodeResponse(msgPlatformGeneralResponse, header, body), true, nil
|
|
}
|
|
|
|
type responseHeader struct {
|
|
messageID uint16
|
|
versioned bool
|
|
protocolVersion byte
|
|
phoneBCD []byte
|
|
sequence uint16
|
|
}
|
|
|
|
func parseResponseHeader(raw []byte) (responseHeader, error) {
|
|
if len(raw) < 12 {
|
|
return responseHeader{}, ErrResponseHeaderTooShort
|
|
}
|
|
props := binary.BigEndian.Uint16(raw[2:4])
|
|
versioned := props&0x4000 != 0
|
|
phoneStart := 4
|
|
phoneLen := 6
|
|
protocolVersion := byte(0)
|
|
if versioned {
|
|
if len(raw) < 17 {
|
|
return responseHeader{}, ErrResponseHeaderTooShort
|
|
}
|
|
protocolVersion = raw[4]
|
|
phoneStart = 5
|
|
phoneLen = 10
|
|
}
|
|
sequenceStart := phoneStart + phoneLen
|
|
if len(raw) < sequenceStart+2 {
|
|
return responseHeader{}, ErrResponseHeaderTooShort
|
|
}
|
|
phone := append([]byte(nil), raw[phoneStart:sequenceStart]...)
|
|
return responseHeader{
|
|
messageID: binary.BigEndian.Uint16(raw[0:2]),
|
|
versioned: versioned,
|
|
protocolVersion: protocolVersion,
|
|
phoneBCD: phone,
|
|
sequence: binary.BigEndian.Uint16(raw[sequenceStart : sequenceStart+2]),
|
|
}, nil
|
|
}
|
|
|
|
func encodeResponse(messageID uint16, header responseHeader, body []byte) []byte {
|
|
props := uint16(len(body)) & 0x03ff
|
|
if header.versioned {
|
|
props |= 0x4000
|
|
}
|
|
payload := make([]byte, 0, 4+1+len(header.phoneBCD)+2+len(body)+1)
|
|
payload = appendU16(payload, messageID)
|
|
payload = appendU16(payload, props)
|
|
if header.versioned {
|
|
payload = append(payload, header.protocolVersion)
|
|
}
|
|
payload = append(payload, header.phoneBCD...)
|
|
payload = appendU16(payload, header.sequence)
|
|
payload = append(payload, body...)
|
|
payload = append(payload, checksum(payload))
|
|
return frame(payload)
|
|
}
|
|
|
|
func appendU16(out []byte, value uint16) []byte {
|
|
return append(out, byte(value>>8), byte(value))
|
|
}
|
|
|
|
func frame(payload []byte) []byte {
|
|
out := []byte{0x7e}
|
|
for _, value := range payload {
|
|
switch value {
|
|
case 0x7e:
|
|
out = append(out, 0x7d, 0x02)
|
|
case 0x7d:
|
|
out = append(out, 0x7d, 0x01)
|
|
default:
|
|
out = append(out, value)
|
|
}
|
|
}
|
|
out = append(out, 0x7e)
|
|
return out
|
|
}
|