fix: support extended gb32960 platform login
This commit is contained in:
@@ -38,7 +38,7 @@ func ExtractFrames(stream []byte) (frames [][]byte, remainder []byte, err error)
|
||||
return frames, append([]byte(nil), remaining...), nil
|
||||
}
|
||||
bodyLen := int(binary.BigEndian.Uint16(remaining[22:24]))
|
||||
frameLen := headerLen + bodyLen + 1
|
||||
frameLen := frameLength(remaining, bodyLen)
|
||||
if len(remaining) < frameLen {
|
||||
return frames, append([]byte(nil), remaining...), nil
|
||||
}
|
||||
@@ -59,7 +59,8 @@ func ParseFrame(raw []byte, receivedAtMS int64, sourceEndpoint string) (envelope
|
||||
return envelope.FrameEnvelope{}, fmt.Errorf("%w: %s", ErrBadStartSymbol, hex.EncodeToString(raw[:2]))
|
||||
}
|
||||
bodyLen := int(binary.BigEndian.Uint16(raw[22:24]))
|
||||
if len(raw) != headerLen+bodyLen+1 {
|
||||
actualBodyLen := len(raw) - headerLen - 1
|
||||
if len(raw) != headerLen+bodyLen+1 && !isExtendedPlatformLogin(raw[2], bodyLen, actualBodyLen) {
|
||||
return envelope.FrameEnvelope{}, fmt.Errorf("%w: declared=%d actual=%d", ErrBodyLength, bodyLen, len(raw)-headerLen-1)
|
||||
}
|
||||
if got, want := bcc(raw[2:len(raw)-1]), raw[len(raw)-1]; got != want {
|
||||
@@ -69,15 +70,16 @@ func ParseFrame(raw []byte, receivedAtMS int64, sourceEndpoint string) (envelope
|
||||
command := raw[2]
|
||||
responseFlag := raw[3]
|
||||
vin := strings.TrimRight(string(raw[4:21]), "\x00 ")
|
||||
body := raw[headerLen : headerLen+bodyLen]
|
||||
body := raw[headerLen : headerLen+actualBodyLen]
|
||||
parsed := map[string]any{
|
||||
"header": map[string]any{
|
||||
"version": version,
|
||||
"command": fmt.Sprintf("0x%02X", command),
|
||||
"response_flag": fmt.Sprintf("0x%02X", responseFlag),
|
||||
"vin": vin,
|
||||
"encrypt": raw[21],
|
||||
"body_length": bodyLen,
|
||||
"version": version,
|
||||
"command": fmt.Sprintf("0x%02X", command),
|
||||
"response_flag": fmt.Sprintf("0x%02X", responseFlag),
|
||||
"vin": vin,
|
||||
"encrypt": raw[21],
|
||||
"body_length": bodyLen,
|
||||
"actual_body_length": actualBodyLen,
|
||||
},
|
||||
}
|
||||
fields := map[string]any{}
|
||||
@@ -91,6 +93,16 @@ func ParseFrame(raw []byte, receivedAtMS int64, sourceEndpoint string) (envelope
|
||||
parsed["device_time"] = eventTime.Format(time.RFC3339)
|
||||
}
|
||||
parsed["data_units"] = units
|
||||
} else if command == 0x05 {
|
||||
login := parsePlatformLogin(body)
|
||||
parsed["platform_login"] = login
|
||||
if deviceTime, ok := login["login_time"].(string); ok && deviceTime != "" {
|
||||
fields["device_time"] = deviceTime
|
||||
parsed["device_time"] = deviceTime
|
||||
}
|
||||
if account, ok := login["username"].(string); ok && account != "" {
|
||||
fields["platform_account"] = account
|
||||
}
|
||||
}
|
||||
|
||||
env := envelope.FrameEnvelope{
|
||||
@@ -113,7 +125,7 @@ func parseDataBody(version string, body []byte, fields map[string]any) (time.Tim
|
||||
if len(body) < 6 {
|
||||
return time.Time{}, nil
|
||||
}
|
||||
eventTime := parseBCDTime(body[:6])
|
||||
eventTime := parseGBTime(body[:6])
|
||||
cursor := 6
|
||||
var units []map[string]any
|
||||
for cursor < len(body) {
|
||||
@@ -303,6 +315,54 @@ func parseDataBody(version string, body []byte, fields map[string]any) (time.Tim
|
||||
return eventTime, units
|
||||
}
|
||||
|
||||
func frameLength(frameStart []byte, bodyLen int) int {
|
||||
standardLen := headerLen + bodyLen + 1
|
||||
if len(frameStart) < headerLen || frameStart[2] != 0x05 || bodyLen != 41 {
|
||||
return standardLen
|
||||
}
|
||||
// Some field platform-login frames declare the standard 41-byte body but
|
||||
// carry a 32-byte password, making the actual body 53 bytes. Only consume
|
||||
// the extended form when its own BCC verifies so the splitter does not eat
|
||||
// bytes from the next frame.
|
||||
extendedLen := headerLen + 53 + 1
|
||||
if len(frameStart) < extendedLen {
|
||||
return standardLen
|
||||
}
|
||||
extended := frameStart[:extendedLen]
|
||||
if bcc(extended[2:len(extended)-1]) == extended[len(extended)-1] {
|
||||
return extendedLen
|
||||
}
|
||||
return standardLen
|
||||
}
|
||||
|
||||
func isExtendedPlatformLogin(command byte, declaredBodyLen int, actualBodyLen int) bool {
|
||||
return command == 0x05 && declaredBodyLen == 41 && actualBodyLen == 53
|
||||
}
|
||||
|
||||
func parsePlatformLogin(body []byte) map[string]any {
|
||||
login := map[string]any{
|
||||
"raw_body_length": len(body),
|
||||
}
|
||||
if len(body) < 9 {
|
||||
login["error"] = "truncated"
|
||||
return login
|
||||
}
|
||||
loginTime := parseGBTime(body[:6])
|
||||
if !loginTime.IsZero() {
|
||||
login["login_time"] = loginTime.Format(time.RFC3339)
|
||||
}
|
||||
login["serial_no"] = binary.BigEndian.Uint16(body[6:8])
|
||||
if len(body) >= 21 {
|
||||
login["username"] = trimASCII(body[8:20])
|
||||
}
|
||||
if len(body) > 21 {
|
||||
passwordEnd := len(body) - 1
|
||||
login["password"] = trimASCII(body[20:passwordEnd])
|
||||
login["encrypt_rule"] = body[passwordEnd]
|
||||
}
|
||||
return login
|
||||
}
|
||||
|
||||
func parseVehicleData(data []byte) map[string]any {
|
||||
return map[string]any{
|
||||
"vehicle_status": int(data[0]),
|
||||
@@ -751,24 +811,24 @@ func version(a byte, b byte) (string, bool) {
|
||||
}
|
||||
}
|
||||
|
||||
func parseBCDTime(data []byte) time.Time {
|
||||
func parseGBTime(data []byte) time.Time {
|
||||
if len(data) != 6 {
|
||||
return time.Time{}
|
||||
}
|
||||
year := 2000 + bcdByte(data[0])
|
||||
month := time.Month(bcdByte(data[1]))
|
||||
day := bcdByte(data[2])
|
||||
hour := bcdByte(data[3])
|
||||
minute := bcdByte(data[4])
|
||||
second := bcdByte(data[5])
|
||||
year := 2000 + int(data[0])
|
||||
month := time.Month(data[1])
|
||||
day := int(data[2])
|
||||
hour := int(data[3])
|
||||
minute := int(data[4])
|
||||
second := int(data[5])
|
||||
if month < 1 || month > 12 || day < 1 || day > 31 || hour > 23 || minute > 59 || second > 59 {
|
||||
return time.Time{}
|
||||
}
|
||||
return time.Date(year, month, day, hour, minute, second, 0, time.FixedZone("Asia/Shanghai", 8*3600))
|
||||
}
|
||||
|
||||
func bcdByte(value byte) int {
|
||||
return int(value>>4)*10 + int(value&0x0f)
|
||||
func trimASCII(data []byte) string {
|
||||
return strings.TrimRight(string(data), "\x00 ")
|
||||
}
|
||||
|
||||
func bcc(data []byte) byte {
|
||||
|
||||
Reference in New Issue
Block a user