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 }