diff --git a/src/main/java/com/iot/transport/jt808/common/Consts.java b/src/main/java/com/iot/transport/jt808/common/Consts.java index 01845b8..8325166 100644 --- a/src/main/java/com/iot/transport/jt808/common/Consts.java +++ b/src/main/java/com/iot/transport/jt808/common/Consts.java @@ -32,6 +32,8 @@ public class Consts { public static final Integer MSGID_PARAM_QUERY_RESP = 0x0104; /** 按键事件上报 **/ public static final Integer MSGID_BUTTON_EVENT = 0x0006; + /** 定位数据批量上传 **/ + public static final Integer MSGID_LOCATION_BATCH_UPLOAD = 0x0704; /** 平台通用应答 **/ diff --git a/src/main/java/com/iot/transport/jt808/entity/request/BatchLocationPack.java b/src/main/java/com/iot/transport/jt808/entity/request/BatchLocationPack.java new file mode 100644 index 0000000..31cce6a --- /dev/null +++ b/src/main/java/com/iot/transport/jt808/entity/request/BatchLocationPack.java @@ -0,0 +1,52 @@ +package com.iot.transport.jt808.entity.request; + +import com.iot.transport.jt808.entity.DataPack; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; +import java.util.ArrayList; +import java.util.List; + +/** + * 定位数据批量上传消息 (0x0704) + */ +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class BatchLocationPack extends DataPack { + + /** + * 数据项个数 + */ + private int count; + + /** + * 位置数据类型 + * 0:正常位置批量汇报 + * 1:盲区补报 + */ + private int type; + + /** + * 位置数据列表 + * 每个元素都是一个完整的 LocationPack (解析内容相同) + */ + private List items; + + public BatchLocationPack() { + this.items = new ArrayList<>(); + } + + public BatchLocationPack(DataPack packageData) { + this(); + this.channel = packageData.getChannel(); + this.checkSum = packageData.getCheckSum(); + this.bodyBytes = packageData.getBodyBytes(); + this.packHead = packageData.getPackHead(); + } + + public void addItem(LocationPack item) { + this.items.add(item); + } +} + diff --git a/src/main/java/com/iot/transport/jt808/service/codec/DataDecoder.java b/src/main/java/com/iot/transport/jt808/service/codec/DataDecoder.java index 38c6b01..2b1ff7b 100644 --- a/src/main/java/com/iot/transport/jt808/service/codec/DataDecoder.java +++ b/src/main/java/com/iot/transport/jt808/service/codec/DataDecoder.java @@ -9,6 +9,7 @@ import com.iot.transport.jt808.entity.DataPack.PackHead; import com.iot.transport.jt808.entity.request.LocationPack; import com.iot.transport.jt808.entity.request.RegisterPack; import com.iot.transport.jt808.entity.request.ButtonEventPack; +import com.iot.transport.jt808.entity.request.BatchLocationPack; import com.iot.transport.jt808.entity.request.RegisterPack.TerminalRegInfo; import com.iot.transport.jt808.util.BCDUtil; import com.iot.transport.jt808.util.BitUtil; @@ -226,10 +227,48 @@ public class DataDecoder { ret.setKeyState(this.parseIntFromBytes(data, 1, 1)); } + return ret; + } + + public BatchLocationPack toBatchLocationPack(DataPack packageData) { + BatchLocationPack ret = new BatchLocationPack(packageData); + byte[] data = ret.getBodyBytes(); + + // 1. 数据项个数 (WORD) + int count = this.parseIntFromBytes(data, 0, 2); + ret.setCount(count); + + // 2. 位置数据类型 (BYTE) + ret.setType(this.parseIntFromBytes(data, 2, 1)); + + // 3. 循环解析位置数据项 + int index = 3; + for (int i = 0; i < count; i++) { + if (index + 2 > data.length) break; + + // 每一项的数据长度 (WORD) + int itemLen = this.parseIntFromBytes(data, index, 2); + index += 2; + + if (index + itemLen > data.length) break; + + // 提取该项的字节数组 + byte[] itemBytes = new byte[itemLen]; + System.arraycopy(data, index, itemBytes, 0, itemLen); + + // 使用 toLocationInfoUploadMsg 复用解析逻辑 + // 构造一个临时的 DataPack + DataPack itemPack = new DataPack(); + itemPack.setBodyBytes(itemBytes); + LocationPack loc = this.toLocationInfoUploadMsg(itemPack); + ret.addItem(loc); + + index += itemLen; + } + return ret; } - public LocationPack toLocationInfoUploadMsg(DataPack packageData) { LocationPack ret = new LocationPack(packageData); final byte[] data = ret.getBodyBytes(); diff --git a/src/main/java/com/iot/transport/jt808/service/handler/MessageHandlerFactory.java b/src/main/java/com/iot/transport/jt808/service/handler/MessageHandlerFactory.java index b89d0d8..bb5f283 100644 --- a/src/main/java/com/iot/transport/jt808/service/handler/MessageHandlerFactory.java +++ b/src/main/java/com/iot/transport/jt808/service/handler/MessageHandlerFactory.java @@ -9,6 +9,7 @@ import com.iot.transport.jt808.service.handler.terminal.LocationUploadHandler; import com.iot.transport.jt808.service.handler.terminal.LoginOutHandler; import com.iot.transport.jt808.service.handler.terminal.RegisterHandler; import com.iot.transport.jt808.service.handler.terminal.ButtonEventHandler; +import com.iot.transport.jt808.service.handler.terminal.BatchLocationHandler; public class MessageHandlerFactory { @@ -24,6 +25,7 @@ public class MessageHandlerFactory { handlerMap.put(Consts.MSGID_AUTHENTICATION, AuthenticationHandler.class); // 终端鉴权 handlerMap.put(Consts.MSGID_LOCATION_UPLOAD, LocationUploadHandler.class); // 位置信息汇报 handlerMap.put(Consts.MSGID_BUTTON_EVENT, ButtonEventHandler.class); // 按键事件上报 + handlerMap.put(Consts.MSGID_LOCATION_BATCH_UPLOAD, BatchLocationHandler.class); // 批量定位上传 } diff --git a/src/main/java/com/iot/transport/jt808/service/handler/TCPServerHandler.java b/src/main/java/com/iot/transport/jt808/service/handler/TCPServerHandler.java index 404543b..c438d1f 100644 --- a/src/main/java/com/iot/transport/jt808/service/handler/TCPServerHandler.java +++ b/src/main/java/com/iot/transport/jt808/service/handler/TCPServerHandler.java @@ -25,6 +25,7 @@ import com.iot.transport.jt808.entity.request.BatteryVersionInfo; import com.iot.transport.jt808.entity.request.BluetoothInfo; import com.iot.transport.jt808.entity.request.LocationPack; import com.iot.transport.jt808.entity.request.ButtonEventPack; +import com.iot.transport.jt808.entity.request.BatchLocationPack; import java.util.List; public class TCPServerHandler extends ChannelInboundHandlerAdapter { // (1) @@ -135,6 +136,32 @@ public class TCPServerHandler extends ChannelInboundHandlerAdapter { // (1) } catch (Exception e) { logMap.put("details", packageData.toString() + " (Parse Error: " + e.getMessage() + ")"); } + } else if (msgId == Consts.MSGID_LOCATION_BATCH_UPLOAD || msgId == 0x0704) { + try { + BatchLocationPack batchPack = this.decoder.toBatchLocationPack(packageData); + logMap.put("details", "Batch Upload: " + batchPack.getCount() + " items"); + logMap.put("type", "badge"); + logMap.put("id", header.getTerminalPhone()); + + // Use the latest location item to update the UI + if (batchPack.getItems() != null && !batchPack.getItems().isEmpty()) { + LocationPack lastLoc = batchPack.getItems().get(batchPack.getItems().size() - 1); + + // Location + Map locMap = new HashMap<>(); + locMap.put("lat", lastLoc.getLatitude()); + locMap.put("lon", lastLoc.getLongitude()); + logMap.put("location", locMap); + + // Battery + BatteryVersionInfo batInfo = lastLoc.getBatteryVersionInfo(); + if (batInfo != null) { + logMap.put("battery", batInfo.getBatteryLevel()); + } + } + } catch (Exception e) { + logMap.put("details", packageData.toString() + " (Parse Error: " + e.getMessage() + ")"); + } } else { logMap.put("details", packageData.toString()); } diff --git a/src/main/java/com/iot/transport/jt808/service/handler/terminal/BatchLocationHandler.java b/src/main/java/com/iot/transport/jt808/service/handler/terminal/BatchLocationHandler.java new file mode 100644 index 0000000..f517b98 --- /dev/null +++ b/src/main/java/com/iot/transport/jt808/service/handler/terminal/BatchLocationHandler.java @@ -0,0 +1,51 @@ +package com.iot.transport.jt808.service.handler.terminal; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.iot.transport.jt808.entity.DataPack; +import com.iot.transport.jt808.entity.DataPack.PackHead; +import com.iot.transport.jt808.entity.request.BatchLocationPack; +import com.iot.transport.jt808.entity.request.LocationPack; +import com.iot.transport.jt808.entity.response.ServerBodyPack; +import com.iot.transport.jt808.service.handler.MessageHandler; + +/** + * 定位数据批量上传 (0x0704) ==> 平台通用应答 + */ +public class BatchLocationHandler extends MessageHandler { + + private final Logger logger = LoggerFactory.getLogger(getClass()); + + public BatchLocationHandler() { + super(); + } + + @Override + public void process(DataPack packageData) { + + PackHead header = packageData.getPackHead(); + logger.info("[批量定位],msgid={}, phone={},flowid={}", header.getId(), header.getTerminalPhone(), header.getFlowId()); + + try { + BatchLocationPack msg = this.decoder.toBatchLocationPack(packageData); + logger.info("批量定位数据: Count={}, Type={}, Items={}", msg.getCount(), msg.getType(), msg.getItems().size()); + + // 可以在这里处理每个 LocationPack,例如入库或推送到前端 + for (LocationPack item : msg.getItems()) { + // TODO: Process each location item + logger.debug(" -> Item: Lat={}, Lon={}, Time={}", item.getLatitude(), item.getLongitude(), item.getTime()); + } + + // 回复通用应答 + int flowId = super.getFlowId(msg.getChannel()); + ServerBodyPack respMsgBody = new ServerBodyPack(header.getFlowId(), header.getId(), ServerBodyPack.success); + byte[] bs = this.msgEncoder.encode4ServerCommonRespMsg(msg, respMsgBody, flowId); + super.send2Client(msg.getChannel(), bs); + + } catch (Exception e) { + logger.error("[批量定位]错误, err={}", e.getMessage()); + } + } +} + diff --git a/src/main/resources/static/index.html b/src/main/resources/static/index.html index 89a240b..2c6aa6d 100644 --- a/src/main/resources/static/index.html +++ b/src/main/resources/static/index.html @@ -113,7 +113,7 @@