feat: parse gb32960 realtime data units
This commit is contained in:
@@ -149,11 +149,84 @@ func parseDataBody(version string, body []byte, fields map[string]any) (time.Tim
|
||||
fields[envelope.FieldLongitude] = unit["longitude"]
|
||||
fields[envelope.FieldLatitude] = unit["latitude"]
|
||||
cursor += 9
|
||||
case 0x02:
|
||||
if len(body[cursor:]) < 1 {
|
||||
units = append(units, map[string]any{"type": "0x02", "error": "truncated"})
|
||||
return eventTime, units
|
||||
}
|
||||
size := 1 + int(body[cursor])*12
|
||||
if len(body[cursor:]) < size {
|
||||
units = append(units, map[string]any{"type": "0x02", "error": "truncated"})
|
||||
return eventTime, units
|
||||
}
|
||||
unit := parseDriveMotorData(body[cursor : cursor+size])
|
||||
units = append(units, map[string]any{"type": "0x02", "name": "drive_motor", "value": unit})
|
||||
cursor += size
|
||||
case 0x03:
|
||||
if len(body[cursor:]) < 8 {
|
||||
units = append(units, map[string]any{"type": "0x03", "error": "truncated"})
|
||||
return eventTime, units
|
||||
}
|
||||
probeCount := int(binary.BigEndian.Uint16(body[cursor+6 : cursor+8]))
|
||||
size := 8 + probeCount + 10
|
||||
if len(body[cursor:]) < size {
|
||||
units = append(units, map[string]any{"type": "0x03", "error": "truncated"})
|
||||
return eventTime, units
|
||||
}
|
||||
unit := parseFuelCellData(body[cursor : cursor+size])
|
||||
units = append(units, map[string]any{"type": "0x03", "name": "fuel_cell", "value": unit})
|
||||
fields["fuel_cell_hydrogen_consumption_kg_per_100km"] = unit["hydrogen_consumption_kg_per_100km"]
|
||||
fields["fuel_cell_voltage_v"] = unit["fuel_cell_voltage_v"]
|
||||
fields["fuel_cell_current_a"] = unit["fuel_cell_current_a"]
|
||||
cursor += size
|
||||
case 0x04:
|
||||
if len(body[cursor:]) < 5 {
|
||||
units = append(units, map[string]any{"type": "0x04", "error": "truncated"})
|
||||
return eventTime, units
|
||||
}
|
||||
unit := parseEngineData(body[cursor : cursor+5])
|
||||
units = append(units, map[string]any{"type": "0x04", "name": "engine", "value": unit})
|
||||
cursor += 5
|
||||
case 0x06:
|
||||
if len(body[cursor:]) < 14 {
|
||||
units = append(units, map[string]any{"type": "0x06", "error": "truncated"})
|
||||
return eventTime, units
|
||||
}
|
||||
unit := parseExtremeData(body[cursor : cursor+14])
|
||||
units = append(units, map[string]any{"type": "0x06", "name": "extreme", "value": unit})
|
||||
cursor += 14
|
||||
case 0x07:
|
||||
size, ok := alarmDataSize(body[cursor:])
|
||||
if !ok {
|
||||
units = append(units, map[string]any{"type": "0x07", "error": "truncated"})
|
||||
return eventTime, units
|
||||
}
|
||||
unit := parseAlarmData(body[cursor : cursor+size])
|
||||
units = append(units, map[string]any{"type": "0x07", "name": "alarm", "value": unit})
|
||||
cursor += size
|
||||
case 0x08:
|
||||
size, ok := voltageDataSize(body[cursor:])
|
||||
if !ok {
|
||||
units = append(units, map[string]any{"type": "0x08", "error": "truncated"})
|
||||
return eventTime, units
|
||||
}
|
||||
unit := parseVoltageData(body[cursor : cursor+size])
|
||||
units = append(units, map[string]any{"type": "0x08", "name": "voltage", "value": unit})
|
||||
cursor += size
|
||||
case 0x09:
|
||||
size, ok := temperatureDataSize(body[cursor:])
|
||||
if !ok {
|
||||
units = append(units, map[string]any{"type": "0x09", "error": "truncated"})
|
||||
return eventTime, units
|
||||
}
|
||||
unit := parseTemperatureData(body[cursor : cursor+size])
|
||||
units = append(units, map[string]any{"type": "0x09", "name": "temperature", "value": unit})
|
||||
cursor += size
|
||||
default:
|
||||
units = append(units, map[string]any{
|
||||
"type": fmt.Sprintf("0x%02X", unitType),
|
||||
"raw_tail": strings.ToUpper(hex.EncodeToString(body[cursor:])),
|
||||
"parse": "unsupported_unit",
|
||||
"parse": "unknown_unit",
|
||||
"byte_size": len(body) - cursor,
|
||||
})
|
||||
return eventTime, units
|
||||
@@ -180,6 +253,217 @@ func parseVehicleData(data []byte) map[string]any {
|
||||
}
|
||||
}
|
||||
|
||||
func parseDriveMotorData(data []byte) map[string]any {
|
||||
count := int(data[0])
|
||||
motors := make([]map[string]any, 0, count)
|
||||
cursor := 1
|
||||
for i := 0; i < count; i++ {
|
||||
motors = append(motors, map[string]any{
|
||||
"serial_no": int(data[cursor]),
|
||||
"state": int(data[cursor+1]),
|
||||
"controller_temperature_c": int(data[cursor+2]) - 40,
|
||||
"speed_rpm": int(binary.BigEndian.Uint16(data[cursor+3:cursor+5])) - 20000,
|
||||
"torque_nm": float64(int(binary.BigEndian.Uint16(data[cursor+5:cursor+7]))-20000) / 10,
|
||||
"motor_temperature_c": int(data[cursor+7]) - 40,
|
||||
"controller_voltage_v": float64(binary.BigEndian.Uint16(data[cursor+8:cursor+10])) / 10,
|
||||
"controller_current_a": float64(binary.BigEndian.Uint16(data[cursor+10:cursor+12]))/10 - 1000,
|
||||
})
|
||||
cursor += 12
|
||||
}
|
||||
return map[string]any{
|
||||
"count": count,
|
||||
"motors": motors,
|
||||
}
|
||||
}
|
||||
|
||||
func parseFuelCellData(data []byte) map[string]any {
|
||||
probeCount := int(binary.BigEndian.Uint16(data[6:8]))
|
||||
probes := make([]int, 0, probeCount)
|
||||
cursor := 8
|
||||
for i := 0; i < probeCount; i++ {
|
||||
probes = append(probes, int(data[cursor])-40)
|
||||
cursor++
|
||||
}
|
||||
out := map[string]any{
|
||||
"fuel_cell_voltage_v": float64(binary.BigEndian.Uint16(data[0:2])) / 10,
|
||||
"fuel_cell_current_a": float64(binary.BigEndian.Uint16(data[2:4])) / 10,
|
||||
"hydrogen_consumption_kg_per_100km": float64(binary.BigEndian.Uint16(data[4:6])) / 100,
|
||||
"temperature_probe_count": probeCount,
|
||||
"temperature_probe_values_c": probes,
|
||||
"max_hydrogen_temperature_c": float64(binary.BigEndian.Uint16(data[cursor:cursor+2]))/10 - 40,
|
||||
"max_hydrogen_temperature_probe_id": int(data[cursor+2]),
|
||||
"max_hydrogen_concentration_fraction": float64(binary.BigEndian.Uint16(data[cursor+3:cursor+5])) * 0.000001,
|
||||
"max_hydrogen_concentration_probe_id": int(data[cursor+5]),
|
||||
"max_hydrogen_pressure_mpa": float64(binary.BigEndian.Uint16(data[cursor+6:cursor+8])) / 10,
|
||||
"max_hydrogen_pressure_probe_id": int(data[cursor+8]),
|
||||
"dc_dc_status": int(data[cursor+9]),
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func parseEngineData(data []byte) map[string]any {
|
||||
return map[string]any{
|
||||
"engine_status": int(data[0]),
|
||||
"crank_speed_rpm": binary.BigEndian.Uint16(data[1:3]),
|
||||
"fuel_rate": binary.BigEndian.Uint16(data[3:5]),
|
||||
}
|
||||
}
|
||||
|
||||
func parseExtremeData(data []byte) map[string]any {
|
||||
return map[string]any{
|
||||
"max_voltage_subsystem_no": int(data[0]),
|
||||
"max_voltage_cell_no": int(data[1]),
|
||||
"max_voltage_v": float64(binary.BigEndian.Uint16(data[2:4])) / 1000,
|
||||
"min_voltage_subsystem_no": int(data[4]),
|
||||
"min_voltage_cell_no": int(data[5]),
|
||||
"min_voltage_v": float64(binary.BigEndian.Uint16(data[6:8])) / 1000,
|
||||
"max_temp_subsystem_no": int(data[8]),
|
||||
"max_temp_probe_no": int(data[9]),
|
||||
"max_temp_c": int(data[10]) - 40,
|
||||
"min_temp_subsystem_no": int(data[11]),
|
||||
"min_temp_probe_no": int(data[12]),
|
||||
"min_temp_c": int(data[13]) - 40,
|
||||
}
|
||||
}
|
||||
|
||||
func alarmDataSize(data []byte) (int, bool) {
|
||||
if len(data) < 5 {
|
||||
return 0, false
|
||||
}
|
||||
cursor := 5
|
||||
for i := 0; i < 4; i++ {
|
||||
if len(data[cursor:]) < 1 {
|
||||
return 0, false
|
||||
}
|
||||
count := int(data[cursor])
|
||||
cursor++
|
||||
cursor += count * 4
|
||||
if cursor > len(data) {
|
||||
return 0, false
|
||||
}
|
||||
}
|
||||
return cursor, true
|
||||
}
|
||||
|
||||
func parseAlarmData(data []byte) map[string]any {
|
||||
cursor := 5
|
||||
readList := func() []string {
|
||||
count := int(data[cursor])
|
||||
cursor++
|
||||
values := make([]string, 0, count)
|
||||
for i := 0; i < count; i++ {
|
||||
values = append(values, fmt.Sprintf("0x%08X", binary.BigEndian.Uint32(data[cursor:cursor+4])))
|
||||
cursor += 4
|
||||
}
|
||||
return values
|
||||
}
|
||||
return map[string]any{
|
||||
"max_alarm_level": int(data[0]),
|
||||
"general_alarm_flag": fmt.Sprintf("0x%08X", binary.BigEndian.Uint32(data[1:5])),
|
||||
"battery_faults": readList(),
|
||||
"motor_faults": readList(),
|
||||
"engine_faults": readList(),
|
||||
"other_faults": readList(),
|
||||
}
|
||||
}
|
||||
|
||||
func voltageDataSize(data []byte) (int, bool) {
|
||||
if len(data) < 1 {
|
||||
return 0, false
|
||||
}
|
||||
cursor := 1
|
||||
for i := 0; i < int(data[0]); i++ {
|
||||
if len(data[cursor:]) < 10 {
|
||||
return 0, false
|
||||
}
|
||||
frameCellCount := int(data[cursor+9])
|
||||
cursor += 10 + frameCellCount*2
|
||||
if cursor > len(data) {
|
||||
return 0, false
|
||||
}
|
||||
}
|
||||
return cursor, true
|
||||
}
|
||||
|
||||
func parseVoltageData(data []byte) map[string]any {
|
||||
subCount := int(data[0])
|
||||
cursor := 1
|
||||
totalVoltage := 0.0
|
||||
maxCell := 0.0
|
||||
minCell := 0.0
|
||||
cellSeen := false
|
||||
for i := 0; i < subCount; i++ {
|
||||
totalVoltage += float64(binary.BigEndian.Uint16(data[cursor+1:cursor+3])) / 10
|
||||
cellCount := int(binary.BigEndian.Uint16(data[cursor+5 : cursor+7]))
|
||||
frameCellCount := int(data[cursor+9])
|
||||
cursor += 10
|
||||
for c := 0; c < frameCellCount; c++ {
|
||||
voltage := float64(binary.BigEndian.Uint16(data[cursor:cursor+2])) / 1000
|
||||
if !cellSeen || voltage > maxCell {
|
||||
maxCell = voltage
|
||||
}
|
||||
if !cellSeen || voltage < minCell {
|
||||
minCell = voltage
|
||||
}
|
||||
cellSeen = true
|
||||
cursor += 2
|
||||
}
|
||||
_ = cellCount
|
||||
}
|
||||
return map[string]any{
|
||||
"subsystem_count": subCount,
|
||||
"total_voltage_v": totalVoltage,
|
||||
"max_cell_v": maxCell,
|
||||
"min_cell_v": minCell,
|
||||
}
|
||||
}
|
||||
|
||||
func temperatureDataSize(data []byte) (int, bool) {
|
||||
if len(data) < 1 {
|
||||
return 0, false
|
||||
}
|
||||
cursor := 1
|
||||
for i := 0; i < int(data[0]); i++ {
|
||||
if len(data[cursor:]) < 3 {
|
||||
return 0, false
|
||||
}
|
||||
probeCount := int(binary.BigEndian.Uint16(data[cursor+1 : cursor+3]))
|
||||
cursor += 3 + probeCount
|
||||
if cursor > len(data) {
|
||||
return 0, false
|
||||
}
|
||||
}
|
||||
return cursor, true
|
||||
}
|
||||
|
||||
func parseTemperatureData(data []byte) map[string]any {
|
||||
subCount := int(data[0])
|
||||
cursor := 1
|
||||
maxTemp := 0
|
||||
minTemp := 0
|
||||
seen := false
|
||||
for i := 0; i < subCount; i++ {
|
||||
probeCount := int(binary.BigEndian.Uint16(data[cursor+1 : cursor+3]))
|
||||
cursor += 3
|
||||
for p := 0; p < probeCount; p++ {
|
||||
temp := int(data[cursor]) - 40
|
||||
if !seen || temp > maxTemp {
|
||||
maxTemp = temp
|
||||
}
|
||||
if !seen || temp < minTemp {
|
||||
minTemp = temp
|
||||
}
|
||||
seen = true
|
||||
cursor++
|
||||
}
|
||||
}
|
||||
return map[string]any{
|
||||
"subsystem_count": subCount,
|
||||
"max_temp_c": maxTemp,
|
||||
"min_temp_c": minTemp,
|
||||
}
|
||||
}
|
||||
|
||||
func parsePositionData(data []byte) map[string]any {
|
||||
return map[string]any{
|
||||
"position_status": int(data[0]),
|
||||
|
||||
Reference in New Issue
Block a user