package gb32960 import ( "encoding/binary" "errors" "time" "lingniu-vehicle-ingest/go/vehicle-gateway/internal/envelope" ) var ErrResponseFrameTooShort = errors.New("gb32960 response frame too short") const ( responseSuccess = byte(0x01) responseCommand = byte(0xfe) encryptNone = byte(0x01) ) // AutoResponse builds the protocol ACK for accepted upstream GB/T 32960 frames. // The ACK is written only after the caller has durably published the frame. func AutoResponse(raw []byte, env envelope.FrameEnvelope) ([]byte, bool, error) { if env.ParseStatus == envelope.ParseBadFrame { return nil, false, nil } if len(raw) < headerLen+1 { return nil, false, ErrResponseFrameTooShort } if raw[3] != responseCommand { return nil, false, nil } command := raw[2] if !shouldRespond(command) { return nil, false, nil } body := []byte(nil) if timestamp := responseTime(raw, command); !timestamp.IsZero() { body = encodeGBTime(timestamp) } return buildResponse(raw[0], command, responseSuccess, raw[4:21], body), true, nil } func shouldRespond(command byte) bool { switch command { case 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08: return true default: return false } } func responseTime(raw []byte, command byte) time.Time { bodyLen := len(raw) - headerLen - 1 if bodyLen >= 6 { return parseGBTime(raw[headerLen : headerLen+6]) } return time.Now().In(time.FixedZone("Asia/Shanghai", 8*3600)) } func encodeGBTime(t time.Time) []byte { local := t.In(time.FixedZone("Asia/Shanghai", 8*3600)) return []byte{ byte(local.Year() - 2000), byte(local.Month()), byte(local.Day()), byte(local.Hour()), byte(local.Minute()), byte(local.Second()), } } func buildResponse(start byte, command byte, responseFlag byte, vin17 []byte, body []byte) []byte { out := make([]byte, 0, headerLen+len(body)+1) out = append(out, start, start, command, responseFlag) out = append(out, vin17[:17]...) out = append(out, encryptNone, 0, 0) binary.BigEndian.PutUint16(out[22:24], uint16(len(body))) out = append(out, body...) return append(out, bcc(out[2:])) }