fix(go): flatten jt808 additional fields
This commit is contained in:
@@ -30,14 +30,7 @@ type Location struct {
|
||||
DirectionDeg uint16
|
||||
Time time.Time
|
||||
TotalMileageKM *float64
|
||||
Additional []AdditionalItem
|
||||
}
|
||||
|
||||
type AdditionalItem struct {
|
||||
ID byte
|
||||
Length int
|
||||
ValueHex string
|
||||
Parsed any
|
||||
Additional map[string]any
|
||||
}
|
||||
|
||||
// ExtractFrames splits JT/T 808 TCP streams by 0x7e delimiters and unescapes
|
||||
@@ -311,88 +304,70 @@ func parseLocation(body []byte) (Location, error) {
|
||||
return location, nil
|
||||
}
|
||||
|
||||
func parseAdditional(data []byte, location *Location) []AdditionalItem {
|
||||
var out []AdditionalItem
|
||||
func parseAdditional(data []byte, location *Location) map[string]any {
|
||||
out := map[string]any{}
|
||||
for len(data) >= 2 {
|
||||
id := data[0]
|
||||
size := int(data[1])
|
||||
data = data[2:]
|
||||
if len(data) < size {
|
||||
out = append(out, AdditionalItem{
|
||||
ID: id,
|
||||
Length: size,
|
||||
ValueHex: strings.ToUpper(hex.EncodeToString(data)),
|
||||
Parsed: "truncated",
|
||||
})
|
||||
return out
|
||||
}
|
||||
value := data[:size]
|
||||
item := AdditionalItem{
|
||||
ID: id,
|
||||
Length: size,
|
||||
ValueHex: strings.ToUpper(hex.EncodeToString(value)),
|
||||
}
|
||||
var parsed map[string]any
|
||||
switch {
|
||||
case id == 0x01 && size == 4:
|
||||
rawMileage := binary.BigEndian.Uint32(value)
|
||||
mileage := float64(rawMileage) / 10
|
||||
location.TotalMileageKM = &mileage
|
||||
item.Parsed = map[string]any{
|
||||
"name": envelope.FieldTotalMileageKM,
|
||||
"raw_value_tenth_km": rawMileage,
|
||||
"value": mileage,
|
||||
"unit": "km",
|
||||
parsed = map[string]any{
|
||||
"name": envelope.FieldTotalMileageKM,
|
||||
"value": mileage,
|
||||
}
|
||||
case id == 0x02 && size == 2:
|
||||
rawFuel := binary.BigEndian.Uint16(value)
|
||||
item.Parsed = map[string]any{
|
||||
"name": "fuel_l",
|
||||
"raw_value_tenth_l": rawFuel,
|
||||
"value": float64(rawFuel) / 10,
|
||||
"unit": "L",
|
||||
parsed = map[string]any{
|
||||
"name": "fuel_l",
|
||||
"value": float64(rawFuel) / 10,
|
||||
}
|
||||
case id == 0x03 && size == 2:
|
||||
rawSpeed := binary.BigEndian.Uint16(value)
|
||||
item.Parsed = map[string]any{
|
||||
"name": "recorder_speed_kmh",
|
||||
"raw_value_tenth_kmh": rawSpeed,
|
||||
"value": float64(rawSpeed) / 10,
|
||||
"unit": "km/h",
|
||||
parsed = map[string]any{
|
||||
"name": "recorder_speed_kmh",
|
||||
"value": float64(rawSpeed) / 10,
|
||||
}
|
||||
case id == 0x04 && size == 2:
|
||||
item.Parsed = map[string]any{
|
||||
parsed = map[string]any{
|
||||
"name": "manual_alarm_event_id",
|
||||
"value": binary.BigEndian.Uint16(value),
|
||||
}
|
||||
case id == 0x05 && size == 30:
|
||||
item.Parsed = map[string]any{
|
||||
parsed = map[string]any{
|
||||
"name": "tire_pressure",
|
||||
"values": parseTirePressure(value),
|
||||
}
|
||||
case id == 0x06 && size == 2:
|
||||
item.Parsed = map[string]any{
|
||||
parsed = map[string]any{
|
||||
"name": "carriage_temperature_c",
|
||||
"value": int16(binary.BigEndian.Uint16(value)),
|
||||
"unit": "C",
|
||||
}
|
||||
case id == 0x11 && (size == 1 || size == 5):
|
||||
parsed := map[string]any{
|
||||
parsed = map[string]any{
|
||||
"name": "overspeed_alarm",
|
||||
"location_type": value[0],
|
||||
}
|
||||
if size == 5 {
|
||||
parsed["area_id"] = binary.BigEndian.Uint32(value[1:5])
|
||||
}
|
||||
item.Parsed = parsed
|
||||
case id == 0x12 && size == 6:
|
||||
item.Parsed = map[string]any{
|
||||
parsed = map[string]any{
|
||||
"name": "area_route_alarm",
|
||||
"location_type": value[0],
|
||||
"area_id": binary.BigEndian.Uint32(value[1:5]),
|
||||
"direction": value[5],
|
||||
}
|
||||
case id == 0x13 && size == 7:
|
||||
item.Parsed = map[string]any{
|
||||
parsed = map[string]any{
|
||||
"name": "road_section_driving_time_alarm",
|
||||
"road_section_id": binary.BigEndian.Uint32(value[0:4]),
|
||||
"driving_time_seconds": binary.BigEndian.Uint16(value[4:6]),
|
||||
@@ -402,38 +377,38 @@ func parseAdditional(data []byte, location *Location) []AdditionalItem {
|
||||
}
|
||||
case id == 0x25 && size == 4:
|
||||
rawStatus := binary.BigEndian.Uint32(value)
|
||||
item.Parsed = map[string]any{
|
||||
parsed = map[string]any{
|
||||
"name": "extended_vehicle_signal_status",
|
||||
"value": rawStatus,
|
||||
"bits": parseExtendedVehicleSignal(rawStatus),
|
||||
}
|
||||
case id == 0x2a && size == 2:
|
||||
rawStatus := binary.BigEndian.Uint16(value)
|
||||
item.Parsed = map[string]any{
|
||||
parsed = map[string]any{
|
||||
"name": "io_status",
|
||||
"value": rawStatus,
|
||||
"bits": parseIOStatus(rawStatus),
|
||||
}
|
||||
case id == 0x2b && size == 4:
|
||||
analog := binary.BigEndian.Uint32(value)
|
||||
item.Parsed = map[string]any{
|
||||
parsed = map[string]any{
|
||||
"name": "analog",
|
||||
"ad0": analog & 0xffff,
|
||||
"ad1": analog >> 16,
|
||||
"value": analog,
|
||||
}
|
||||
case id == 0x30 && size == 1:
|
||||
item.Parsed = map[string]any{
|
||||
parsed = map[string]any{
|
||||
"name": "network_signal_strength",
|
||||
"value": value[0],
|
||||
}
|
||||
case id == 0x31 && size == 1:
|
||||
item.Parsed = map[string]any{
|
||||
parsed = map[string]any{
|
||||
"name": "gnss_satellite_count",
|
||||
"value": value[0],
|
||||
}
|
||||
}
|
||||
out = append(out, item)
|
||||
addParsedAdditional(out, parsed)
|
||||
data = data[size:]
|
||||
}
|
||||
return out
|
||||
@@ -448,7 +423,6 @@ func locationToMap(location Location) map[string]any {
|
||||
"altitude_m": location.AltitudeM,
|
||||
"speed_kmh": location.SpeedKMH,
|
||||
"direction_deg": location.DirectionDeg,
|
||||
"additional": additionalToMaps(location.Additional),
|
||||
}
|
||||
if !location.Time.IsZero() {
|
||||
out["device_time"] = location.Time.Format(time.RFC3339)
|
||||
@@ -456,23 +430,38 @@ func locationToMap(location Location) map[string]any {
|
||||
if location.TotalMileageKM != nil {
|
||||
out[envelope.FieldTotalMileageKM] = *location.TotalMileageKM
|
||||
}
|
||||
for key, value := range location.Additional {
|
||||
out[key] = value
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func additionalToMaps(items []AdditionalItem) []map[string]any {
|
||||
out := make([]map[string]any, 0, len(items))
|
||||
for _, item := range items {
|
||||
value := map[string]any{
|
||||
"id": fmt.Sprintf("0x%02X", item.ID),
|
||||
"length": item.Length,
|
||||
"value_hex": item.ValueHex,
|
||||
}
|
||||
if item.Parsed != nil {
|
||||
value["parsed"] = item.Parsed
|
||||
}
|
||||
out = append(out, value)
|
||||
func addParsedAdditional(out map[string]any, parsed map[string]any) {
|
||||
if parsed == nil {
|
||||
return
|
||||
}
|
||||
return out
|
||||
name, ok := parsed["name"].(string)
|
||||
if !ok || name == "" {
|
||||
return
|
||||
}
|
||||
if value, ok := parsed["value"]; ok && len(parsed) <= 2 {
|
||||
out[name] = value
|
||||
return
|
||||
}
|
||||
value := make(map[string]any, len(parsed)-1)
|
||||
for key, item := range parsed {
|
||||
if key == "name" {
|
||||
continue
|
||||
}
|
||||
value[key] = item
|
||||
}
|
||||
if len(value) == 1 {
|
||||
if singleValue, ok := value["value"]; ok {
|
||||
out[name] = singleValue
|
||||
return
|
||||
}
|
||||
}
|
||||
out[name] = value
|
||||
}
|
||||
|
||||
func parseTirePressure(data []byte) []map[string]any {
|
||||
|
||||
Reference in New Issue
Block a user