feat:报文-蓝牙解析
All checks were successful
JT808 CI/CD / build-and-deploy (push) Successful in 28s

This commit is contained in:
lzh
2025-12-12 17:28:34 +08:00
parent b596f0eb72
commit bc56c3b2d2
3 changed files with 170 additions and 2 deletions

View File

@@ -0,0 +1,63 @@
package com.hua.transport.jt808.entity.request;
import java.util.List;
public class BluetoothInfo {
private String mac;
private int rssi;
// 0xF3 specific fields
private String customData;
// Type of bluetooth info: 0xF3 or 0xF4
private int type;
public BluetoothInfo() {}
public BluetoothInfo(String mac, int rssi) {
this.mac = mac;
this.rssi = rssi;
}
public String getMac() {
return mac;
}
public void setMac(String mac) {
this.mac = mac;
}
public int getRssi() {
return rssi;
}
public void setRssi(int rssi) {
this.rssi = rssi;
}
public String getCustomData() {
return customData;
}
public void setCustomData(String customData) {
this.customData = customData;
}
public int getType() {
return type;
}
public void setType(int type) {
this.type = type;
}
@Override
public String toString() {
return "BluetoothInfo{" +
"mac='" + mac + '\'' +
", rssi=" + rssi +
", customData='" + customData + '\'' +
", type=" + String.format("0x%02X", type) +
'}';
}
}

View File

@@ -33,6 +33,9 @@ public class LocationPack extends DataPack {
// GMT+8 时间,本标准中之后涉及的时间均采用此时区 // GMT+8 时间,本标准中之后涉及的时间均采用此时区
private Date time; private Date time;
// Bluetooth Extension Data
private java.util.List<BluetoothInfo> bluetoothInfos;
public LocationPack() { public LocationPack() {
} }
@@ -92,6 +95,14 @@ public class LocationPack extends DataPack {
this.time = time; this.time = time;
} }
public java.util.List<BluetoothInfo> getBluetoothInfos() {
return bluetoothInfos;
}
public void setBluetoothInfos(java.util.List<BluetoothInfo> bluetoothInfos) {
this.bluetoothInfos = bluetoothInfos;
}
public int getWarningFlagField() { public int getWarningFlagField() {
return warningFlagField; return warningFlagField;
} }
@@ -112,7 +123,7 @@ public class LocationPack extends DataPack {
public String toString() { public String toString() {
return "LocationInfoUploadMsg [warningFlagField=" + warningFlagField + ", statusField=" + statusField return "LocationInfoUploadMsg [warningFlagField=" + warningFlagField + ", statusField=" + statusField
+ ", latitude=" + latitude + ", longitude=" + longitude + ", elevation=" + elevation + ", speed=" + ", latitude=" + latitude + ", longitude=" + longitude + ", elevation=" + elevation + ", speed="
+ speed + ", direction=" + direction + ", time=" + time + "]"; + speed + ", direction=" + direction + ", time=" + time + ", bluetoothInfos=" + bluetoothInfos + "]";
} }
} }

View File

@@ -12,6 +12,11 @@ import com.hua.transport.jt808.entity.request.RegisterPack.TerminalRegInfo;
import com.hua.transport.jt808.util.BCDUtil; import com.hua.transport.jt808.util.BCDUtil;
import com.hua.transport.jt808.util.BitUtil; import com.hua.transport.jt808.util.BitUtil;
import com.hua.transport.jt808.entity.request.BluetoothInfo;
import com.hua.transport.jt808.util.HexUtil;
import java.util.ArrayList;
import java.util.List;
/** /**
* 数据包解码器 * 数据包解码器
* @author huaxl * @author huaxl
@@ -219,7 +224,7 @@ public class DataDecoder {
// 5. byte[16-17] 高程(WORD(16)) 海拔高度,单位为米( m // 5. byte[16-17] 高程(WORD(16)) 海拔高度,单位为米( m
ret.setElevation(this.parseIntFromBytes(data, 16, 2)); ret.setElevation(this.parseIntFromBytes(data, 16, 2));
// byte[18-19] 速度(WORD) 1/10km/h // byte[18-19] 速度(WORD) 1/10km/h
ret.setSpeed(this.parseFloatFromBytes(data, 18, 2)); ret.setSpeed(this.parseIntFromBytes(data, 18, 2) / 10.0f);
// byte[20-21] 方向(WORD) 0-359正北为 0顺时针 // byte[20-21] 方向(WORD) 0-359正北为 0顺时针
ret.setDirection(this.parseIntFromBytes(data, 20, 2)); ret.setDirection(this.parseIntFromBytes(data, 20, 2));
// byte[22-x] 时间(BCD[6]) YY-MM-DD-hh-mm-ss // byte[22-x] 时间(BCD[6]) YY-MM-DD-hh-mm-ss
@@ -229,6 +234,95 @@ public class DataDecoder {
byte[] tmp = new byte[6]; byte[] tmp = new byte[6];
System.arraycopy(data, 22, tmp, 0, 6); System.arraycopy(data, 22, tmp, 0, 6);
String time = this.parseBcdStringFromBytes(data, 22, 6); String time = this.parseBcdStringFromBytes(data, 22, 6);
// 扩展协议解析 (Starting from index 28)
int index = 28;
List<BluetoothInfo> bluetoothInfos = new ArrayList<>();
while (index < data.length) {
int extId = data[index] & 0xFF;
// Ensure we have at least ID and Length bytes
if (index + 1 >= data.length) break;
int extLen = data[index + 1] & 0xFF;
if (extId == 0xF3) {
// F3 扩展 (蓝牙信标扫描附加信息)
// index+2: 特定标识 0xE6
// index+3: 特定标识 0x0C
// index+4: 扫描到的蓝牙数据组数
if (index + 4 < data.length) {
int groupCount = data[index + 4] & 0xFF;
int currentOffset = index + 5;
for (int i = 0; i < groupCount; i++) {
if (currentOffset + 7 > data.length) break;
// MAC (6 bytes)
byte[] macBytes = new byte[6];
System.arraycopy(data, currentOffset, macBytes, 0, 6);
String mac = HexUtil.toHexString(macBytes);
// Add colons to MAC
StringBuilder sb = new StringBuilder();
for(int k=0; k<mac.length(); k+=2) {
if(k>0) sb.append(":");
sb.append(mac.substring(k, k+2));
}
String macStr = sb.toString().toUpperCase();
// RSSI (1 byte)
int rssi = data[currentOffset + 6]; // Signed byte
BluetoothInfo info = new BluetoothInfo(macStr, rssi);
info.setType(0xF3);
bluetoothInfos.add(info);
currentOffset += 7;
}
// Try to capture custom data if present at the end of the block
int expectedEnd = index + 2 + extLen;
if (currentOffset < expectedEnd && expectedEnd <= data.length) {
int customLen = expectedEnd - currentOffset;
byte[] customBytes = new byte[customLen];
System.arraycopy(data, currentOffset, customBytes, 0, customLen);
String customHex = HexUtil.toHexString(customBytes);
if (!bluetoothInfos.isEmpty()) {
bluetoothInfos.get(bluetoothInfos.size() - 1).setCustomData(customHex);
}
}
}
} else if (extId == 0xF4) {
// F4 扩展 (蓝牙BLE扫描周围同类产品)
// index+2: 扫描到的蓝牙数据组数
if (index + 2 < data.length) {
int groupCount = data[index + 2] & 0xFF;
int currentOffset = index + 3;
for (int i = 0; i < groupCount; i++) {
// Each group: 6 bytes MAC (from IMEI) + 1 byte RSSI = 7 bytes
if (currentOffset + 7 > data.length) break;
byte[] macBytes = new byte[6];
System.arraycopy(data, currentOffset, macBytes, 0, 6);
String mac = HexUtil.toHexString(macBytes); // This is partial IMEI
// RSSI (1 byte)
int rssi = data[currentOffset + 6];
BluetoothInfo info = new BluetoothInfo(mac, rssi); // raw hex as mac for now
info.setType(0xF4);
bluetoothInfos.add(info);
currentOffset += 7;
}
}
}
index += (2 + extLen);
}
ret.setBluetoothInfos(bluetoothInfos);
return ret; return ret;
} }