lzq 2025-09-27 19:00:07 +08:00
parent 83745a2d43
commit ee5b061e4d
70 changed files with 1303 additions and 573 deletions

View File

@ -3,7 +3,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>com.njzscloud</groupId> <groupId>com.njzscloud</groupId>
<artifactId>gps</artifactId> <artifactId>localizer</artifactId>
<version>0.0.1</version> <version>0.0.1</version>
<packaging>jar</packaging> <packaging>jar</packaging>
@ -18,6 +18,11 @@
<artifactId>fastjson2</artifactId> <artifactId>fastjson2</artifactId>
<version>2.0.51</version> <version>2.0.51</version>
</dependency> </dependency>
<dependency>
<groupId>org.eclipse.paho</groupId>
<artifactId>org.eclipse.paho.client.mqttv3</artifactId>
<version>1.2.5</version>
</dependency>
<!--<editor-fold desc="jackson">--> <!--<editor-fold desc="jackson">-->
<dependency> <dependency>
<groupId>com.fasterxml.jackson.core</groupId> <groupId>com.fasterxml.jackson.core</groupId>

View File

@ -1,21 +1,14 @@
package com.njzscloud; package com.njzscloud;
import com.njzscloud.server.TcpServer; import com.njzscloud.common.mqtt.Mqtt;
import com.njzscloud.tuqiang.Tuqiang; import com.njzscloud.common.tuqiang.Tuqiang;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@Slf4j @Slf4j
public class App { public class App {
static Tuqiang tuqiang;
public static void main(String[] args) { public static void main(String[] args) {
tuqiang = new Tuqiang(); Mqtt.run("tcp://localhost:1883", "tuqiang", "admin", "123456");
TcpServer tcpServer = new TcpServer(18888, 1, 2); Mqtt.addListener(new MqttMsgHandlers());
try { Tuqiang.run(18888, 1, 2);
tcpServer.start();
log.info("TCP服务器已停止");
} catch (Exception e) {
log.error("TCP服务器启动失败", e);
}
} }
} }

View File

@ -0,0 +1,71 @@
package com.njzscloud;
import com.njzscloud.common.mqtt.support.MqttListener;
import com.njzscloud.common.mqtt.support.MqttMsg;
import com.njzscloud.common.tuqiang.Tuqiang;
import com.njzscloud.param.*;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class MqttMsgHandlers {
/**
*
*/
@MqttListener(topic = "location/track")
public void onMessage(MqttMsg msg) {
LocationTrackParam locationTrackParam = msg.getMsg(LocationTrackParam.class);
Tuqiang.track(locationTrackParam.getTerminalId(), locationTrackParam.getInterval());
}
/**
*
*/
@MqttListener(topic = "location/current")
public void currentLocation(MqttMsg msg) {
LocationCurrentParam locationCurrentParam = msg.getMsg(LocationCurrentParam.class);
Tuqiang.currentLocation(locationCurrentParam.getTerminalId());
}
/**
*
*/
@MqttListener(topic = "location/warn")
public void enableWarn(MqttMsg msg) {
EnableWarnParam enableWarnParam = msg.getMsg(EnableWarnParam.class);
String terminalId = enableWarnParam.getTerminalId();
boolean enable = enableWarnParam.isEnable();
Tuqiang.enableWarn(terminalId, enable);
}
/**
*
*/
@MqttListener(topic = "location/speed_threshold")
public void speedThreshold(MqttMsg msg) {
SpeedThresholdParam speedThresholdParam = msg.getMsg(SpeedThresholdParam.class);
String terminalId = speedThresholdParam.getTerminalId();
int speed = speedThresholdParam.getSpeed();
Tuqiang.speedThreshold(terminalId, speed);
}
/**
*
*/
@MqttListener(topic = "location/device_info")
public void obtainDeviceInfo(MqttMsg msg) {
ObtainDeviceInfoParam obtainDeviceInfoParam = msg.getMsg(ObtainDeviceInfoParam.class);
String terminalId = obtainDeviceInfoParam.getTerminalId();
Tuqiang.obtainDeviceInfo(terminalId);
}
/**
*
*/
@MqttListener(topic = "location/heartbeat")
public void setHeartbeat(MqttMsg msg) {
SetHeartbeatParam setHeartbeatParam = msg.getMsg(SetHeartbeatParam.class);
String terminalId = setHeartbeatParam.getTerminalId();
Tuqiang.setHeartbeat(terminalId, setHeartbeatParam.getInterval());
}
}

View File

@ -0,0 +1,102 @@
package com.njzscloud;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.map.MapUtil;
import com.njzscloud.common.jt808.support.MessageBody;
import com.njzscloud.common.mqtt.Mqtt;
import com.njzscloud.common.tuqiang.msg.DeviceInfo;
import com.njzscloud.common.tuqiang.msg.LocationReportMsg;
import com.njzscloud.common.tuqiang.msg.SearchLocationResponseMsg;
import com.njzscloud.common.tuqiang.msg.TerminalTxtReportMsg;
import com.njzscloud.common.tuqiang.support.TuqiangListener;
import com.njzscloud.result.RealtimeLocationResult;
import io.netty.buffer.ByteBuf;
import lombok.extern.slf4j.Slf4j;
import static com.njzscloud.common.jt808.support.JT808MessageResolver.*;
@Slf4j
public class TuqiangListeners {
/**
*
*/
@TuqiangListener(messageId = Heartbeat)
public void onHeartbeat(MessageBody message) {
String terminalId = message.getTerminalId();
Mqtt.publish(terminalId + "/online", MapUtil.builder()
.put("online", true)
.put("terminalId", terminalId)
.put("time", DateUtil.now())
.build());
}
/**
*
*/
@TuqiangListener(messageId = LocationReport)
public void onLocationReport(MessageBody message) {
ByteBuf byteBuf = message.getBody();
LocationReportMsg locationReportMsg = new LocationReportMsg(byteBuf);
log.info("终端位置信息汇报消息: {}", locationReportMsg);
String terminalId = message.getTerminalId();
RealtimeLocationResult realtimeLocationResult = BeanUtil.copyProperties(locationReportMsg, RealtimeLocationResult.class)
.setTerminalId(terminalId);
Mqtt.publish(terminalId + "/track_location", realtimeLocationResult);
}
/**
*
*/
@TuqiangListener(messageId = LocationBatchReport)
public void onLocationBatchUpload(MessageBody message) {
String terminalId = message.getTerminalId();
ByteBuf body = message.getBody();
int count = body.readUnsignedShort();
int type = body.readByte();
for (int i = 0; i < count; i++) {
int length = body.readUnsignedShort();
LocationReportMsg locationReportMsg = new LocationReportMsg(body.slice(body.readerIndex(), length));
body.skipBytes(length);
BeanUtil.copyProperties(locationReportMsg, RealtimeLocationResult.class);
RealtimeLocationResult realtimeLocationResult = BeanUtil.copyProperties(locationReportMsg, RealtimeLocationResult.class)
.setTerminalId(terminalId)
.setType(type == 0 ? "正常位置汇报" : "盲区补报");
Mqtt.publish(terminalId + "/track_location", realtimeLocationResult);
}
}
/**
*
*/
@TuqiangListener(messageId = LocationSearch)
public void onSearchLocationResponse(MessageBody message) {
String terminalId = message.getTerminalId();
ByteBuf body = message.getBody();
SearchLocationResponseMsg searchLocationResponseMsg = new SearchLocationResponseMsg(body);
log.info("查询位置响应消息: {}", searchLocationResponseMsg);
LocationReportMsg locationReportMsg = searchLocationResponseMsg.getLocationReportMsg();
RealtimeLocationResult realtimeLocationResult = BeanUtil.copyProperties(locationReportMsg, RealtimeLocationResult.class)
.setTerminalId(terminalId);
Mqtt.publish(terminalId + "/current_location", realtimeLocationResult);
}
/**
*
*/
@TuqiangListener(messageId = TxtReport)
public void onTerminalTxtReport(MessageBody message) {
TerminalTxtReportMsg terminalTxtReportMsg = new TerminalTxtReportMsg(message.getBody());
log.info("终端文本信息汇报消息: {}", terminalTxtReportMsg);
String txt = terminalTxtReportMsg.getTxt();
DeviceInfo deviceInfo = DeviceInfo.parse(txt);
if (deviceInfo != null) {
String terminalId = message.getTerminalId();
Mqtt.publish(terminalId + "/device_info", deviceInfo);
}
}
}

View File

@ -1,4 +1,4 @@
package com.njzscloud.common.ex; package com.njzscloud.common.core.ex;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;

View File

@ -1,4 +1,4 @@
package com.njzscloud.common.ex; package com.njzscloud.common.core.ex;
import cn.hutool.core.lang.Assert; import cn.hutool.core.lang.Assert;

View File

@ -1,4 +1,4 @@
package com.njzscloud.common.ex; package com.njzscloud.common.core.ex;
/** /**

View File

@ -1,4 +1,4 @@
package com.njzscloud.common.ex; package com.njzscloud.common.core.ex;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;

View File

@ -1,4 +1,4 @@
package com.njzscloud.common.ex; package com.njzscloud.common.core.ex;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;

View File

@ -1,4 +1,4 @@
package com.njzscloud.common.ex; package com.njzscloud.common.core.ex;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;

View File

@ -1,4 +1,4 @@
package com.njzscloud.common.ex; package com.njzscloud.common.core.ex;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;

View File

@ -1,4 +1,4 @@
package com.njzscloud.common.ex; package com.njzscloud.common.core.ex;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;

View File

@ -1,7 +1,7 @@
package com.njzscloud.common.ex; package com.njzscloud.common.core.ex;
import cn.hutool.core.map.MapUtil; import cn.hutool.core.map.MapUtil;
import com.njzscloud.common.jackson.Jackson; import com.njzscloud.common.core.jackson.Jackson;
/** /**
* *

View File

@ -1,4 +1,4 @@
package com.njzscloud.common.fastjson; package com.njzscloud.common.core.fastjson;
import cn.hutool.core.date.DatePattern; import cn.hutool.core.date.DatePattern;
import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSON;

View File

@ -1,14 +1,14 @@
package com.njzscloud.common.fastjson.serializer; package com.njzscloud.common.core.fastjson.serializer;
import com.alibaba.fastjson2.JSONReader; import com.alibaba.fastjson2.JSONReader;
import com.alibaba.fastjson2.reader.ObjectReader; import com.alibaba.fastjson2.reader.ObjectReader;
import com.alibaba.fastjson2.util.TypeUtils; import com.alibaba.fastjson2.util.TypeUtils;
import com.njzscloud.common.ex.Exceptions; import com.njzscloud.common.core.ex.Exceptions;
import com.njzscloud.common.ienum.Dict; import com.njzscloud.common.core.ienum.Dict;
import com.njzscloud.common.ienum.DictInt; import com.njzscloud.common.core.ienum.DictInt;
import com.njzscloud.common.ienum.DictStr; import com.njzscloud.common.core.ienum.DictStr;
import com.njzscloud.common.ienum.IEnum; import com.njzscloud.common.core.ienum.IEnum;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import java.lang.reflect.Type; import java.lang.reflect.Type;

View File

@ -1,12 +1,12 @@
package com.njzscloud.common.fastjson.serializer; package com.njzscloud.common.core.fastjson.serializer;
import com.alibaba.fastjson2.JSONWriter; import com.alibaba.fastjson2.JSONWriter;
import com.alibaba.fastjson2.util.TypeUtils; import com.alibaba.fastjson2.util.TypeUtils;
import com.alibaba.fastjson2.writer.ObjectWriter; import com.alibaba.fastjson2.writer.ObjectWriter;
import com.njzscloud.common.ienum.Dict; import com.njzscloud.common.core.ienum.Dict;
import com.njzscloud.common.ienum.DictInt; import com.njzscloud.common.core.ienum.DictInt;
import com.njzscloud.common.ienum.DictStr; import com.njzscloud.common.core.ienum.DictStr;
import com.njzscloud.common.ienum.IEnum; import com.njzscloud.common.core.ienum.IEnum;
import java.lang.reflect.Type; import java.lang.reflect.Type;

View File

@ -1,11 +1,11 @@
package com.njzscloud.common.ienum; package com.njzscloud.common.core.ienum;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.njzscloud.common.fastjson.serializer.DictObjectDeserializer; import com.njzscloud.common.core.fastjson.serializer.DictObjectDeserializer;
import com.njzscloud.common.fastjson.serializer.DictObjectSerializer; import com.njzscloud.common.core.fastjson.serializer.DictObjectSerializer;
import com.njzscloud.common.jackson.serializer.DictDeserializer; import com.njzscloud.common.core.jackson.serializer.DictDeserializer;
import com.njzscloud.common.jackson.serializer.DictSerializer; import com.njzscloud.common.core.jackson.serializer.DictSerializer;
/** /**
* <br/> * <br/>

View File

@ -1,4 +1,4 @@
package com.njzscloud.common.ienum; package com.njzscloud.common.core.ienum;
/** /**

View File

@ -1,4 +1,4 @@
package com.njzscloud.common.ienum; package com.njzscloud.common.core.ienum;
/** /**
* "值" String<br/> * "值" String<br/>

View File

@ -1,4 +1,4 @@
package com.njzscloud.common.ienum; package com.njzscloud.common.core.ienum;
/** /**
* *

View File

@ -1,4 +1,4 @@
package com.njzscloud.common.jackson; package com.njzscloud.common.core.jackson;
import cn.hutool.core.date.DatePattern; import cn.hutool.core.date.DatePattern;
import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude;
@ -9,10 +9,10 @@ import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.dataformat.xml.XmlMapper; import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.njzscloud.common.ex.Exceptions; import com.njzscloud.common.core.ex.Exceptions;
import com.njzscloud.common.jackson.serializer.BigDecimalModule; import com.njzscloud.common.core.jackson.serializer.BigDecimalModule;
import com.njzscloud.common.jackson.serializer.LongModule; import com.njzscloud.common.core.jackson.serializer.LongModule;
import com.njzscloud.common.jackson.serializer.TimeModule; import com.njzscloud.common.core.jackson.serializer.TimeModule;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import java.io.InputStream; import java.io.InputStream;

View File

@ -1,4 +1,4 @@
package com.njzscloud.common.jackson.serializer; package com.njzscloud.common.core.jackson.serializer;
import com.fasterxml.jackson.databind.deser.std.NumberDeserializers; import com.fasterxml.jackson.databind.deser.std.NumberDeserializers;
import com.fasterxml.jackson.databind.module.SimpleModule; import com.fasterxml.jackson.databind.module.SimpleModule;

View File

@ -1,4 +1,4 @@
package com.njzscloud.common.jackson.serializer; package com.njzscloud.common.core.jackson.serializer;
import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.JsonSerializer;

View File

@ -1,15 +1,15 @@
package com.njzscloud.common.jackson.serializer; package com.njzscloud.common.core.jackson.serializer;
import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer; import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.node.IntNode; import com.fasterxml.jackson.databind.node.IntNode;
import com.fasterxml.jackson.databind.node.TextNode; import com.fasterxml.jackson.databind.node.TextNode;
import com.njzscloud.common.ex.Exceptions; import com.njzscloud.common.core.ex.Exceptions;
import com.njzscloud.common.ienum.Dict; import com.njzscloud.common.core.ienum.Dict;
import com.njzscloud.common.ienum.DictInt; import com.njzscloud.common.core.ienum.DictInt;
import com.njzscloud.common.ienum.DictStr; import com.njzscloud.common.core.ienum.DictStr;
import com.njzscloud.common.ienum.IEnum; import com.njzscloud.common.core.ienum.IEnum;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import java.io.IOException; import java.io.IOException;

View File

@ -1,13 +1,13 @@
package com.njzscloud.common.jackson.serializer; package com.njzscloud.common.core.jackson.serializer;
import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonStreamContext; import com.fasterxml.jackson.core.JsonStreamContext;
import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.SerializerProvider;
import com.njzscloud.common.ienum.Dict; import com.njzscloud.common.core.ienum.Dict;
import com.njzscloud.common.ienum.DictInt; import com.njzscloud.common.core.ienum.DictInt;
import com.njzscloud.common.ienum.DictStr; import com.njzscloud.common.core.ienum.DictStr;
import com.njzscloud.common.ienum.IEnum; import com.njzscloud.common.core.ienum.IEnum;
import java.io.IOException; import java.io.IOException;

View File

@ -1,4 +1,4 @@
package com.njzscloud.common.jackson.serializer; package com.njzscloud.common.core.jackson.serializer;
import com.fasterxml.jackson.databind.deser.std.NumberDeserializers; import com.fasterxml.jackson.databind.deser.std.NumberDeserializers;
import com.fasterxml.jackson.databind.module.SimpleModule; import com.fasterxml.jackson.databind.module.SimpleModule;

View File

@ -1,4 +1,4 @@
package com.njzscloud.common.jackson.serializer; package com.njzscloud.common.core.jackson.serializer;
import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.JsonSerializer;

View File

@ -1,4 +1,4 @@
package com.njzscloud.common.jackson.serializer; package com.njzscloud.common.core.jackson.serializer;
import cn.hutool.core.date.DatePattern; import cn.hutool.core.date.DatePattern;
import com.fasterxml.jackson.databind.module.SimpleModule; import com.fasterxml.jackson.databind.module.SimpleModule;

View File

@ -1,4 +1,4 @@
package com.njzscloud.common.log; package com.njzscloud.common.core.log;
import java.util.Arrays; import java.util.Arrays;

View File

@ -1,4 +1,4 @@
package com.njzscloud.common.log; package com.njzscloud.common.core.log;
import ch.qos.logback.classic.Level; import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.classic.spi.ILoggingEvent;

View File

@ -1,4 +1,4 @@
package com.njzscloud.common.utils; package com.njzscloud.common.core.utils;
public class BCD { public class BCD {
public static String bcdToStr(byte[] bcd) { public static String bcdToStr(byte[] bcd) {

View File

@ -1,9 +1,9 @@
package com.njzscloud.common.utils; package com.njzscloud.common.core.utils;
import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil; import cn.hutool.core.map.MapUtil;
import com.njzscloud.common.ex.Exceptions; import com.njzscloud.common.core.ex.Exceptions;
import com.njzscloud.common.fastjson.Fastjson; import com.njzscloud.common.core.fastjson.Fastjson;
import java.util.*; import java.util.*;
import java.util.function.BiFunction; import java.util.function.BiFunction;

View File

@ -0,0 +1,148 @@
package com.njzscloud.common.jt808;
import com.njzscloud.common.jt808.support.JT808Message;
import com.njzscloud.common.jt808.support.JT808MessageResolver;
import com.njzscloud.common.jt808.support.MessageBody;
import com.njzscloud.common.jt808.util.FlowId;
import com.njzscloud.common.tcp.TcpServer;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFutureListener;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;
/**
* JT808
* 便JT808
*/
@Slf4j
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class JT808 {
// 终端手机号与Channel的映射
private static final Map<String, Channel> terminalChannels = new ConcurrentHashMap<>();
private static TcpServer tcpServer;
public static void run(int port, int bossThreads, int workerThreads) {
tcpServer = new TcpServer(port, bossThreads, workerThreads);
tcpServer.start();
}
public static void stop() {
if (tcpServer != null) {
tcpServer.stop();
tcpServer = null;
}
}
/**
*
*/
public static void addListener(String messageId, Consumer<MessageBody> listener) {
JT808MessageResolver.addResolver(messageId, listener);
}
/**
*
*/
public static void register(String terminalId, Channel channel) {
if (terminalId != null && channel != null) {
if (terminalChannels.get(terminalId) != channel) {
terminalChannels.put(terminalId, channel);
}
}
}
/**
*
*/
public static void unregister(Channel channel) {
if (channel != null) {
terminalChannels.values().removeIf(ch -> ch == channel);
}
}
/**
* (0x8001)
*/
public static void sendGeneralResponse(JT808Message message) {
String terminalPhone = message.getTerminalPhone();
int flowId = message.getFlowId();
int messageId = message.getMessageId();
ByteBuf body = Unpooled.buffer();
ByteBufUtil.writeShortBE(body, flowId);
ByteBufUtil.writeShortBE(body, messageId);
body.writeByte(0);
// 构建消息
byte[] bytes = ByteBufUtil.getBytes(body);
body.release();
message = JT808.createBaseMessage(terminalPhone, 0x8001, bytes);
sendMessage(message);
}
/**
*
*/
public static void sendMessage(JT808Message message) {
String terminalPhone = message.getTerminalPhone();
Channel channel = terminalChannels.get(terminalPhone);
if (channel != null && channel.isActive()) {
channel.writeAndFlush(message)
.addListener((ChannelFutureListener) future -> {
if (future.isSuccess()) {
log.info("消息发送成功, 终端: {}, 消息ID: 0x{}, 流水号: {}", terminalPhone, Integer.toHexString(message.getMessageId()), message.getFlowId());
} else {
log.error("消息发送失败, 终端: {}, 消息ID: 0x{}, 流水号: {}", terminalPhone, Integer.toHexString(message.getMessageId()), message.getFlowId(), future.cause());
}
});
} else {
unregister(channel);
// 终端不在线或通道已关闭
log.warn("终端不在线: {}", terminalPhone);
}
}
/**
*
*/
public static JT808Message createBaseMessage(String terminalId, int messageId) {
return createBaseMessage(terminalId, messageId, null);
}
public static JT808Message createBaseMessage(String terminalId, int messageId, byte[] body) {
JT808Message message = new JT808Message()
.setFlowId(FlowId.next(terminalId, messageId));
// 设置消息ID
message.setMessageId(messageId);
// 设置消息体属性
// 其他属性默认:不分包,不加密
if (body != null) {
message.setMessageBodyProps(body.length);
} else {
message.setMessageBodyProps(0);
}
// 设置终端手机号
message.setTerminalPhone(terminalId);
// 设置消息体
message.setMessageBody(body);
return message;
}
}

View File

@ -1,4 +1,4 @@
package com.njzscloud.jt808.protocol; package com.njzscloud.common.jt808.support;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil; import io.netty.buffer.ByteBufUtil;

View File

@ -1,4 +1,4 @@
package com.njzscloud.jt808.protocol; package com.njzscloud.common.jt808.support;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;

View File

@ -1,7 +1,7 @@
package com.njzscloud.jt808.protocol; package com.njzscloud.common.jt808.support;
import cn.hutool.core.util.ReflectUtil; import cn.hutool.core.util.ReflectUtil;
import com.njzscloud.common.fastjson.Fastjson; import com.njzscloud.common.core.fastjson.Fastjson;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import lombok.experimental.Accessors; import lombok.experimental.Accessors;
@ -30,9 +30,6 @@ public class JT808Message {
// 校验码 (1字节) // 校验码 (1字节)
private byte checkCode; private byte checkCode;
public JT808Message() {
}
// 从消息体属性中获取消息体长度 // 从消息体属性中获取消息体长度
public int getMessageBodyLength() { public int getMessageBodyLength() {
return messageBodyProps & 0x3FF; return messageBodyProps & 0x3FF;

View File

@ -1,6 +1,6 @@
package com.njzscloud.jt808.protocol; package com.njzscloud.common.jt808.support;
import com.njzscloud.jt808.util.JT808; import com.njzscloud.common.jt808.JT808;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.group.ChannelGroup; import io.netty.channel.group.ChannelGroup;
@ -25,26 +25,17 @@ public class JT808MessageHandler extends ChannelInboundHandlerAdapter {
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
if (msg instanceof JT808Message) { if (msg instanceof JT808Message) {
JT808Message message = (JT808Message) msg; JT808Message message = (JT808Message) msg;
log.info("收到消息, 终端: {}, 消息ID: 0x{}, 流水号: {}", message.getTerminalPhone(), Integer.toHexString(message.getMessageId()), message.getFlowId()); int messageId = message.getMessageId();
// 根据消息类型处理 log.info("收到消息, 设备号: {}消息ID: 0x{}, 流水号: {}", message.getTerminalPhone(), Integer.toHexString(messageId), message.getFlowId());
handleMessageByType(ctx, message); if (messageId == 0x0100) {
JT808.register(message.getTerminalPhone(), ctx.channel());
}
JT808MessageResolver.dispatchMsg(messageId, message);
} else { } else {
super.channelRead(ctx, msg); super.channelRead(ctx, msg);
} }
} }
/**
* ID
*/
private void handleMessageByType(ChannelHandlerContext ctx, JT808Message message) {
int messageId = message.getMessageId();
if (messageId == 0x0100) {
JT808.register(message.getTerminalPhone(), ctx.channel());
}
JT808.triggerListener(messageId, message);
}
@Override @Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception { public void channelInactive(ChannelHandlerContext ctx) throws Exception {
log.info("客户端断开连接: {}", ctx.channel().remoteAddress()); log.info("客户端断开连接: {}", ctx.channel().remoteAddress());

View File

@ -0,0 +1,147 @@
package com.njzscloud.common.jt808.support;
import cn.hutool.core.map.MapUtil;
import com.njzscloud.common.jt808.JT808;
import com.njzscloud.common.tuqiang.msg.TerminalAuthMsg;
import com.njzscloud.common.tuqiang.msg.TerminalGeneralResponseMsg;
import com.njzscloud.common.tuqiang.msg.TerminalRegisterMsg;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled;
import lombok.extern.slf4j.Slf4j;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;
@Slf4j
public class JT808MessageResolver {
public static final String Heartbeat = "Heartbeat";
public static final String LocationReport = "LocationReport";
public static final String LocationBatchReport = "LocationBatchReport";
public static final String LocationSearch = "LocationSearch";
public static final String TxtReport = "TxtReport";
private static Map<String, Consumer<MessageBody>> resolvers = new ConcurrentHashMap<>();
private static Map<Integer, Consumer<JT808Message>> stdResolvers = MapUtil.<Integer, Consumer<JT808Message>>builder()
.put(0x0100, JT808MessageResolver::onTerminalRegister)
.put(0x0102, JT808MessageResolver::onTerminalAuth)
.put(0x0002, JT808MessageResolver::onHeartbeat)
.put(0x0200, JT808MessageResolver::onLocationReport)
.put(0x704, JT808MessageResolver::onLocationBatchUpload)
.put(0x0201, JT808MessageResolver::onSearchLocationResponse)
.put(0x0001, JT808MessageResolver::onTerminalGeneralResponse)
.put(0x6006, JT808MessageResolver::onTerminalTxtReport)
.build();
public static void addResolver(String messageId, Consumer<MessageBody> resolver) {
JT808MessageResolver.resolvers.put(messageId, resolver);
}
public static void dispatchMsg(String messageId, JT808Message message) {
Consumer<MessageBody> heartbeat = resolvers.get(messageId);
if (heartbeat != null) {
String terminalPhone = message.getTerminalPhone();
byte[] messageBody = message.getMessageBody();
ByteBuf byteBuf = Unpooled.wrappedBuffer(messageBody);
try {
heartbeat.accept(new MessageBody()
.setTerminalId(terminalPhone)
.setBody(byteBuf));
} catch (Exception e) {
log.error("处理消息时发生异常,消息 Id{},设备号:{} ", messageId, terminalPhone, e);
} finally {
byteBuf.release();
}
}
}
public static void dispatchMsg(Integer messageId, JT808Message message) {
Consumer<JT808Message> stdResolver = stdResolvers.get(messageId);
if (stdResolver != null) {
stdResolver.accept(message);
}
}
/**
*
*/
public static void onTerminalRegister(JT808Message message) {
String terminalPhone = message.getTerminalPhone();
ByteBuf body = Unpooled.buffer();
ByteBufUtil.writeShortBE(body, message.getFlowId());
body.writeByte(0);
ByteBufUtil.writeUtf8(body, terminalPhone);
// 构建消息
byte[] bytes = ByteBufUtil.getBytes(body);
body.release();
JT808.sendMessage(JT808.createBaseMessage(terminalPhone, 0x8100, bytes));
TerminalRegisterMsg terminalRegisterMsg = message.getMessageBody(TerminalRegisterMsg.class);
log.info("终端注册消息: {}", terminalRegisterMsg);
}
/**
*
*/
public static void onTerminalAuth(JT808Message message) {
JT808.sendGeneralResponse(message);
TerminalAuthMsg authMsg = message.getMessageBody(TerminalAuthMsg.class);
log.info("终端鉴权消息: {}", authMsg);
}
/**
*
*/
public static void onTerminalGeneralResponse(JT808Message message) {
TerminalGeneralResponseMsg terminalGeneralResponseMsg = message.getMessageBody(TerminalGeneralResponseMsg.class);
int messageId = message.getMessageId();
String terminalPhone = message.getTerminalPhone();
int result = terminalGeneralResponseMsg.getResult();
log.info("设备通用响应,设备号:{},消息 Id{},结果:{}", terminalPhone, Integer.toHexString(messageId), result);
}
/**
*
*/
public static void onHeartbeat(JT808Message message) {
JT808.sendGeneralResponse(message);
dispatchMsg(Heartbeat, message);
}
/**
*
*/
public static void onLocationReport(JT808Message message) {
dispatchMsg(LocationReport, message);
}
/**
*
*/
public static void onLocationBatchUpload(JT808Message message) {
dispatchMsg(LocationBatchReport, message);
}
/**
*
*/
public static void onSearchLocationResponse(JT808Message message) {
JT808.sendGeneralResponse(message);
dispatchMsg(LocationSearch, message);
}
/**
*
*/
public static void onTerminalTxtReport(JT808Message message) {
JT808.sendGeneralResponse(message);
int messageId = message.getMessageId();
String terminalPhone = message.getTerminalPhone();
dispatchMsg(TxtReport, message);
}
}

View File

@ -0,0 +1,16 @@
package com.njzscloud.common.jt808.support;
import io.netty.buffer.ByteBuf;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import lombok.experimental.Accessors;
@Getter
@Setter
@ToString
@Accessors(chain = true)
public class MessageBody {
private String terminalId;
private ByteBuf body;
}

View File

@ -0,0 +1,19 @@
package com.njzscloud.common.jt808.util;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class FlowId {
private static final Map<String, Integer> flowId = new ConcurrentHashMap<>();
public static synchronized int next(String terminalId, int messageId) {
String key = terminalId + "_" + messageId;
Integer i = flowId.get(key);
if (i == null || i >= 0xFFFF) {
i = 0;
}
i++;
flowId.put(key, i);
return i;
}
}

View File

@ -0,0 +1,129 @@
package com.njzscloud.common.mqtt;
import cn.hutool.core.util.StrUtil;
import com.njzscloud.common.core.jackson.Jackson;
import com.njzscloud.common.mqtt.support.MqttListener;
import com.njzscloud.common.mqtt.support.MqttMsg;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.paho.client.mqttv3.*;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;
@Slf4j
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class Mqtt {
private static final Map<String, Consumer<MqttMsg>> listeners = new ConcurrentHashMap<>();
private static MqttClient client;
public static void run(String broker, String clientId, String username, String password) {
try {
client = new MqttClient(broker, clientId);
MqttConnectOptions options = new MqttConnectOptions();
if (StrUtil.isNotBlank(username) && StrUtil.isNotBlank(password)) {
options.setUserName(username);
options.setPassword(password.toCharArray());
}
client.connect(options);
client.setCallback(new MsgHandler());
log.info("mqtt连接成功{} {}", broker, clientId);
} catch (Exception e) {
log.error("mqtt连接失败", e);
shutdown();
}
}
public static void shutdown() {
try {
if (client != null) {
client.disconnect();
client = null;
}
} catch (Exception e) {
log.error("mqtt关闭失败", e);
}
}
public static void subscribe(String topic, int qos) {
try {
client.subscribe(topic, qos);
log.info("mqtt 订阅成功:{} {}", topic, qos);
} catch (Exception e) {
log.error("mqtt 订阅失败:{} {}", topic, qos, e);
}
}
public static void subscribe(String topic) {
subscribe(topic, 0);
}
public static void subscribe(String topic, int qos, Consumer<MqttMsg> handler) {
listeners.put(topic, handler);
subscribe(topic, qos);
}
public static void subscribe(String topic, Consumer<MqttMsg> handler) {
subscribe(topic, 0, handler);
}
public static void publish(String topic, int qos, Object msg) {
String jsonStr = null;
try {
jsonStr = Jackson.toJsonStr(msg);
log.info("mqtt 发布消息:{} {} {}", topic, qos, jsonStr);
MqttMessage message = new MqttMessage(jsonStr.getBytes());
message.setQos(qos);
client.publish(topic, message);
} catch (Exception e) {
log.error("mqtt 发布消息失败:{} {} {}", topic, qos, jsonStr, e);
}
}
public static void publish(String topic, Object msg) {
publish(topic, 0, msg);
}
public static void addListener(Object obj) {
Class<?> clazz = obj.getClass();
for (Method method : clazz.getDeclaredMethods()) {
MqttListener mqttListener = method.getAnnotation(MqttListener.class);
if (mqttListener == null) continue;
String topic = mqttListener.topic();
int qos = mqttListener.qos();
subscribe(topic, qos);
Consumer<MqttMsg> handler = (msg) -> {
try {
method.invoke(obj, msg);
} catch (Exception e) {
log.error("mqtt 消息处理失败:{}", topic, e);
}
};
listeners.put(topic, handler);
}
}
public static class MsgHandler implements MqttCallback {
public void messageArrived(String topic, MqttMessage message) throws Exception {
Consumer<MqttMsg> handler = listeners.get(topic);
if (handler == null) return;
try {
handler.accept(new MqttMsg(message.getPayload()));
} catch (Exception e) {
log.error("mqtt 消息处理失败:{} {}", topic, new String(message.getPayload()), e);
}
}
public void connectionLost(Throwable cause) {
log.error("mqtt 连接已断开", cause);
}
public void deliveryComplete(IMqttDeliveryToken token) {
log.info("消息投递结果:{}", token.isComplete());
}
}
}

View File

@ -0,0 +1,14 @@
package com.njzscloud.common.mqtt.config;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class MqttProperties {
private boolean enabled;
private String broker;
private String clientId;
private String username;
private String password;
}

View File

@ -0,0 +1,14 @@
package com.njzscloud.common.mqtt.support;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface MqttListener {
String topic();
int qos() default 0;
}

View File

@ -0,0 +1,15 @@
package com.njzscloud.common.mqtt.support;
import com.njzscloud.common.core.jackson.Jackson;
public class MqttMsg {
private final byte[] msg;
public MqttMsg(byte[] msg) {
this.msg = msg;
}
public <T> T getMsg(Class<T> clazz) {
return Jackson.toBean(msg, clazz);
}
}

View File

@ -1,8 +1,9 @@
package com.njzscloud.server; package com.njzscloud.common.tcp;
import com.njzscloud.jt808.protocol.JT808Decoder; import com.njzscloud.common.core.ex.Exceptions;
import com.njzscloud.jt808.protocol.JT808Encoder; import com.njzscloud.common.jt808.support.JT808Decoder;
import com.njzscloud.jt808.protocol.JT808MessageHandler; import com.njzscloud.common.jt808.support.JT808Encoder;
import com.njzscloud.common.jt808.support.JT808MessageHandler;
import io.netty.bootstrap.ServerBootstrap; import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*; import io.netty.channel.*;
import io.netty.channel.nio.NioIoHandler; import io.netty.channel.nio.NioIoHandler;
@ -13,30 +14,23 @@ import lombok.extern.slf4j.Slf4j;
@Slf4j @Slf4j
public class TcpServer { public class TcpServer {
private final int port; private final int port;
private final int bossThreads; private EventLoopGroup bossGroup;
private final int workerThreads; private EventLoopGroup workerGroup;
private ServerBootstrap bootstrap;
public TcpServer(int port, int bossThreads, int workerThreads) { public TcpServer(int port, int bossThreads, int workerThreads) {
this.port = port; this.port = port;
this.bossThreads = bossThreads; buildServer(bossThreads, workerThreads);
this.workerThreads = workerThreads;
}
public TcpServer() {
this.port = 18888;
this.bossThreads = 5;
this.workerThreads = 200;
} }
public void start() throws Exception { public void buildServer(int bossThreads, int workerThreads) {
// 创建两个EventLoopGroupbossGroup用于接收客户端连接workerGroup用于处理连接后的IO操作
EventLoopGroup bossGroup = new MultiThreadIoEventLoopGroup(bossThreads, NioIoHandler.newFactory());
EventLoopGroup workerGroup = new MultiThreadIoEventLoopGroup(workerThreads, NioIoHandler.newFactory());
try { try {
// 创建两个EventLoopGroupbossGroup用于接收客户端连接workerGroup用于处理连接后的IO操作
bossGroup = new MultiThreadIoEventLoopGroup(bossThreads, NioIoHandler.newFactory());
workerGroup = new MultiThreadIoEventLoopGroup(workerThreads, NioIoHandler.newFactory());
// 服务器启动辅助类 // 服务器启动辅助类
ServerBootstrap bootstrap = new ServerBootstrap(); bootstrap = new ServerBootstrap();
bootstrap.group(bossGroup, workerGroup) bootstrap.group(bossGroup, workerGroup)
// 指定使用NIO的服务器通道 // 指定使用NIO的服务器通道
.channel(NioServerSocketChannel.class) .channel(NioServerSocketChannel.class)
@ -55,17 +49,34 @@ public class TcpServer {
.addLast(new JT808MessageHandler()); // 消息处理器 .addLast(new JT808MessageHandler()); // 消息处理器
} }
}); });
} catch (Exception e) {
// 绑定端口并开始接收连接 log.error("服务器启动失败", e);
ChannelFuture future = bootstrap.bind(port).sync(); this.stop();
log.info("服务器已启动,监听端口: {}", port); throw Exceptions.error(e, "服务器构建失败");
// 等待服务器 socket 关闭
future.channel().closeFuture().sync();
} finally {
// 关闭事件循环组
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
log.info("服务器已停止");
} }
} }
public void start() {
try {
ChannelFuture future = bootstrap.bind(port).sync();
log.info("服务器已启动,监听端口: {}", port);
future.channel().closeFuture().sync();
} catch (Exception e) {
log.error("服务器启动失败,端口: {}", port, e);
stop();
throw Exceptions.error(e, "服务器启动失败");
}
}
public void stop() {
if (bossGroup != null) {
bossGroup.shutdownGracefully();
bossGroup = null;
}
if (workerGroup != null) {
workerGroup.shutdownGracefully();
workerGroup = null;
}
log.info("服务器已停止");
}
} }

View File

@ -0,0 +1,109 @@
package com.njzscloud.common.tuqiang;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import com.njzscloud.TuqiangListeners;
import com.njzscloud.common.jt808.JT808;
import com.njzscloud.common.jt808.support.JT808Message;
import com.njzscloud.common.tuqiang.msg.PublishTxtMsg;
import com.njzscloud.common.tuqiang.support.TuqiangListener;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import java.lang.reflect.Method;
@Slf4j
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class Tuqiang {
public static void run(int port, int bossThreads, int workerThreads) {
addListener(new TuqiangListeners());
JT808.run(port, bossThreads, workerThreads);
}
private static void addListener(Object object) {
Method[] methods = object.getClass().getDeclaredMethods();
for (Method method : methods) {
if (method.isAnnotationPresent(TuqiangListener.class)) {
TuqiangListener tuqiangListener = method.getAnnotation(TuqiangListener.class);
String messageId = tuqiangListener.messageId();
JT808.addListener(messageId, (msg) -> {
try {
method.invoke(object, msg);
} catch (Exception e) {
log.error("途强处理消息 {} 时出错", messageId, e);
}
});
}
}
}
/**
*
*
* @param terminalId ID
* @param interval 0-36000
*/
public static void track(String terminalId, int interval) {
Assert.isTrue(interval >= 0 && interval <= 3600, "上报间隔必须为0-3600秒");
JT808.sendMessage(JT808.createBaseMessage(terminalId, 0x8300, new PublishTxtMsg(StrUtil.format("<SPBSJ*P:BSJGPS*C:{}*H:300>", interval)).toBytes()));
}
/**
*
*
* @param terminalId ID
*/
public static void obtainDeviceInfo(String terminalId) {
JT808.sendMessage(JT808.createBaseMessage(terminalId, 0x8300, new PublishTxtMsg("<CKBSJ>").toBytes()));
}
/**
*
*
* @param terminalId ID
*/
public static void setHeartbeat(String terminalId, int interval) {
ByteBuf body = Unpooled.buffer();
body.writeByte(1);
body.writeByte(1);
body.writeInt(0x0001);
// body.writeByte(1);
body.writeByte(interval);
byte[] bytes = ByteBufUtil.getBytes(body);
body.release();
JT808.sendMessage(JT808.createBaseMessage(terminalId, 0x8103, bytes));
}
/**
*
*
* @param terminalId ID
*/
public static void enableWarn(String terminalId, boolean enable) {
JT808.sendMessage(JT808.createBaseMessage(terminalId, 0x8300, new PublishTxtMsg(StrUtil.format("<SPBSJ*P:BSJGPS*6W:{}>", enable ? 1 : 0)).toBytes()));
}
/**
*
*
* @param terminalId ID
* @param speed km/h
*/
public static void speedThreshold(String terminalId, int speed) {
JT808.sendMessage(JT808.createBaseMessage(terminalId, 0x8300, new PublishTxtMsg(StrUtil.format("<SPBSJ*P:BSJGPS*CS:{}*H:300>", speed)).toBytes()));
}
/**
*
*
* @param terminalId ID
*/
public static void currentLocation(String terminalId) {
JT808Message baseMessage = JT808.createBaseMessage(terminalId, 0x8201);
JT808.sendMessage(baseMessage);
}
}

View File

@ -1,4 +1,4 @@
package com.njzscloud.tuqiang; package com.njzscloud.common.tuqiang.msg;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
@ -10,34 +10,90 @@ import lombok.experimental.Accessors;
@ToString @ToString
@Accessors(chain = true) @Accessors(chain = true)
public class DeviceInfo { public class DeviceInfo {
// GPS定位状态 // region GPS定位状态
/**
* GPS
* 1. V = ValidN No Valid
* 2. 0 = 04
* 3. / 0 = 0 = 1=GPS2 =
*/
private String gpsValid; // 定位有效性(V/N) private String gpsValid; // 定位有效性(V/N)
private int satelliteCount; // 卫星数量 private int satelliteCount; // 卫星数量
private int gpsAccuracy; // 定位精度或备用参数 private int gpsAccuracy; // 定位精度或备用参数
/**
* GPS OK = GPS ERR GPS
*/
private String gpsModuleStatus; // GPS模块状态(OK/ERR) private String gpsModuleStatus; // GPS模块状态(OK/ERR)
// endregion
// 网络与信号状态 // region 网络与信号状态
private String serverIp; // 服务器IP private String serverIp; // 服务器IP
private int serverPort; // 服务器端口 private int serverPort; // 服务器端口
/**
* 2G/4G CGREG GSM/UMTS
* 1 =
* 0= 2= 3= SIM
*/
private int cgregStatus; // 网络注册状态 private int cgregStatus; // 网络注册状态
/**
* CSQ = Carrier Signal Quality
* 0-312212-20 21-31 0
* -78dBmCSQ dBm dBm = -113 + (CSQ×2)22 - 113+44=-69dBm 0
*/
private int csq; // 信号强度 private int csq; // 信号强度
// endregion
// 硬件与电源状态 // region 硬件与电源状态
/**
* V3.5V 3.3V-4.2V 3.3V
*/
private double batteryVoltage; // 电池电压(V) private double batteryVoltage; // 电池电压(V)
/**
* /
* 1= 2= 0=
* 2=
*/
private int powerMode; // 供电模式(1=外接电源,2=电池) private int powerMode; // 供电模式(1=外接电源,2=电池)
/**
* BD = BeiDou
* 0 = / 10使
*/
private int beidouStatus; // 北斗模块状态(0=未启用,1=启用) private int beidouStatus; // 北斗模块状态(0=未启用,1=启用)
// endregion
// 功能与附加状态 // region 功能与附加状态
/**
* * = * A:1= A:2=
*/
private String alarmStatus; // 报警状态(*) private String alarmStatus; // 报警状态(*)
/**
* / 0
*/
private int counter; // 计数器/里程相关 private int counter; // 计数器/里程相关
/**
* 300 = 5 10 60 300
*/
private int reportInterval; // 定位上报间隔(秒) private int reportInterval; // 定位上报间隔(秒)
private int powerSavingMode; // 省电模式(0=关闭,1=开启) /**
private int backupPower; // 备用电源状态 *
private int vibrationStatus; // 震动传感器状态 */
private int speedThreshold; // 超速阈值(km/h)
/**
*
*/
private int logOff; // 关闭日志(0=关闭,1=开启)
/**
*
*/
private int inflectionPointOff; // 拐点关闭状态(0=关闭,1=开启)
private int heartbeatInterval; // 心跳包间隔(秒) private int heartbeatInterval; // 心跳包间隔(秒)
private int accStatus; // ACC点火状态(1=通电,0=断电) /**
*
*/
private int baseStationOff; // 基站关闭状态(1=关闭,0=开启)
private int chargingStatus; // 充电状态(0=未充电,1=充电中) private int chargingStatus; // 充电状态(0=未充电,1=充电中)
private int bluetoothStatus; // 蓝牙状态(1=开启,0=关闭) private int bluetoothStatus; // 蓝牙状态(1=开启,0=关闭)
private int enableWarn; // 蓝牙状态(1=开启,0=关闭)
private String extendStatus; // 扩展状态码(十六进制) private String extendStatus; // 扩展状态码(十六进制)
private String temperature; // 温度传感器状态 private String temperature; // 温度传感器状态
private String deviceTime; // 设备本地时间 private String deviceTime; // 设备本地时间
@ -48,16 +104,19 @@ public class DeviceInfo {
private int lowBatteryThreshold; // 低电阈值状态 private int lowBatteryThreshold; // 低电阈值状态
private int accelerometerStatus; // 加速度传感器状态 private int accelerometerStatus; // 加速度传感器状态
private int flightMode; // 飞行模式(0=关闭,1=开启) private int flightMode; // 飞行模式(0=关闭,1=开启)
// endregion
public static DeviceInfo parse(String str) {
public static DeviceInfo parse(String gpsString) {
DeviceInfo data = new DeviceInfo(); DeviceInfo data = new DeviceInfo();
if (gpsString == null || gpsString.isEmpty()) { if (str == null || str.isEmpty()) {
return data; return null;
}
if (!str.startsWith("<") || !str.endsWith(">")) {
return null;
} }
// 去除首尾的<和> // 去除首尾的<和>
String content = gpsString.replaceAll("[<>]", ""); String content = str.replaceAll("[<>]", "");
// 按*分割字段 // 按*分割字段
String[] fields = content.split("\\*"); String[] fields = content.split("\\*");
@ -111,19 +170,19 @@ public class DeviceInfo {
data.reportInterval = parseInt(value); data.reportInterval = parseInt(value);
break; break;
case "CS": case "CS":
data.powerSavingMode = parseInt(value); data.speedThreshold = parseInt(value);
break; break;
case "3U": case "3U":
data.backupPower = parseInt(value); data.logOff = parseInt(value);
break; break;
case "3Z": case "3Z":
data.vibrationStatus = parseInt(value); data.inflectionPointOff = parseInt(value);
break; break;
case "H": case "H":
data.heartbeatInterval = parseInt(value); data.heartbeatInterval = parseInt(value);
break; break;
case "2A": case "2A":
data.accStatus = parseInt(value); data.baseStationOff = parseInt(value);
break; break;
case "5C": case "5C":
data.chargingStatus = parseInt(value); data.chargingStatus = parseInt(value);
@ -131,6 +190,9 @@ public class DeviceInfo {
case "60": case "60":
data.bluetoothStatus = parseInt(value); data.bluetoothStatus = parseInt(value);
break; break;
case "6W":
data.enableWarn = parseInt(value);
break;
case "3E": case "3E":
data.extendStatus = value; data.extendStatus = value;
break; break;
@ -202,6 +264,4 @@ public class DeviceInfo {
return -1; // 用-1表示解析失败 return -1; // 用-1表示解析失败
} }
} }
} }

View File

@ -1,8 +1,8 @@
package com.njzscloud.tuqiang.msg; package com.njzscloud.common.tuqiang.msg;
import com.njzscloud.common.utils.BCD; import cn.hutool.core.date.DateUtil;
import com.njzscloud.common.core.utils.BCD;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import lombok.ToString; import lombok.ToString;
@ -33,50 +33,33 @@ public class LocationReportMsg {
private int direction; private int direction;
private String time; private String time;
public LocationReportMsg(byte[] bytes) { private boolean overspeed;
ByteBuf body = Unpooled.wrappedBuffer(bytes); private boolean powerUnderVoltage;
private boolean soundAlarm;
private boolean accOn;
private boolean position;
private boolean south;
private boolean west;
public LocationReportMsg(ByteBuf body) {
this.alarmFlag = body.readUnsignedInt(); this.alarmFlag = body.readUnsignedInt();
this.status = body.readUnsignedInt(); this.status = body.readUnsignedInt();
double longitude = body.readUnsignedInt() * 1.0 / 1000000;
this.longitude = isWest() ? -longitude : longitude;
double latitude = body.readUnsignedInt() * 1.0 / 1000000; double latitude = body.readUnsignedInt() * 1.0 / 1000000;
this.latitude = isSouth() ? -latitude : latitude; this.latitude = isWest() ? -latitude : latitude;
double longitude = body.readUnsignedInt() * 1.0 / 1000000;
this.longitude = isSouth() ? -longitude : longitude;
this.altitude = body.readUnsignedShort(); this.altitude = body.readUnsignedShort();
this.speed = body.readUnsignedShort() * 1.0 / 10; this.speed = body.readUnsignedShort() * 1.0 / 10;
this.direction = body.readUnsignedShort(); this.direction = body.readUnsignedShort();
byte[] terminalIdBytes = new byte[7]; byte[] terminalIdBytes = new byte[6];
body.readBytes(terminalIdBytes); body.readBytes(terminalIdBytes);
this.time = BCD.bcdToStr(terminalIdBytes); this.time = DateUtil.format(DateUtil.parse(BCD.bcdToStr(terminalIdBytes), "yyMMddHHmmss"), "yyyy-MM-dd HH:mm:ss");
this.overspeed = (alarmFlag & 0x02) == 0x02;
this.powerUnderVoltage = (alarmFlag & 0x40) == 0x40;
body.release(); this.soundAlarm = (alarmFlag & 0x80) == 0x80;
} this.accOn = (status & 0x01) == 0x01;
this.position = (status & 0x02) == 0x02;
public boolean isOverspeed() { this.south = (status & 0x04) == 0x04;
return (alarmFlag & 0x02) == 0x02; this.west = (status & 0x08) == 0x08;
}
public boolean isPowerUnderVoltage() {
return (alarmFlag & 0x40) == 0x40;
}
public boolean isSoundAlarm() {
return (alarmFlag & 0x80) == 0x80;
}
public boolean isAccOn() {
return (status & 0x01) == 0x01;
}
public boolean isPosition() {
return (status & 0x02) == 0x02;
}
public boolean isSouth() {
return (status & 0x04) == 0x04;
}
public boolean isWest() {
return (status & 0x08) == 0x08;
} }
} }

View File

@ -0,0 +1,36 @@
package com.njzscloud.common.tuqiang.msg;
import cn.hutool.core.util.CharsetUtil;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import lombok.experimental.Accessors;
import lombok.extern.slf4j.Slf4j;
import java.io.UnsupportedEncodingException;
@Getter
@Setter
@ToString
@Slf4j
@Accessors(chain = true)
public class PublishTxtMsg {
private byte flag;
private String txt;
public PublishTxtMsg(String txt) {
this.flag = 0x01;
this.txt = txt;
}
public byte[] toBytes() {
byte[] messageBody = new byte[txt.length() + 1];
messageBody[0] = flag;
try {
System.arraycopy(txt.getBytes(CharsetUtil.GBK), 0, messageBody, 1, txt.length());
} catch (UnsupportedEncodingException e) {
log.error("文本消息构建失败,原文本:{}", txt, e);
}
return messageBody;
}
}

View File

@ -1,8 +1,6 @@
package com.njzscloud.tuqiang.msg; package com.njzscloud.common.tuqiang.msg;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import lombok.ToString; import lombok.ToString;
@ -17,10 +15,9 @@ public class SearchLocationResponseMsg {
private int flowId; private int flowId;
private LocationReportMsg locationReportMsg; private LocationReportMsg locationReportMsg;
public SearchLocationResponseMsg(byte[] bytes) { public SearchLocationResponseMsg(ByteBuf body) {
ByteBuf body = Unpooled.wrappedBuffer(bytes);
this.flowId = body.readUnsignedShort(); this.flowId = body.readUnsignedShort();
byte[] locationReportMsgBytes = ByteBufUtil.getBytes(body); ByteBuf locationReportMsgBody = body.slice(body.readerIndex(), body.readableBytes());
this.locationReportMsg = new LocationReportMsg(locationReportMsgBytes); this.locationReportMsg = new LocationReportMsg(locationReportMsgBody);
} }
} }

View File

@ -1,4 +1,4 @@
package com.njzscloud.tuqiang.msg; package com.njzscloud.common.tuqiang.msg;
import cn.hutool.core.util.CharsetUtil; import cn.hutool.core.util.CharsetUtil;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
@ -20,5 +20,6 @@ public class TerminalAuthMsg {
byte[] authCodeBytes = new byte[body.readableBytes()]; byte[] authCodeBytes = new byte[body.readableBytes()];
body.readBytes(authCodeBytes); body.readBytes(authCodeBytes);
this.authCode = new String(authCodeBytes, CharsetUtil.CHARSET_GBK); this.authCode = new String(authCodeBytes, CharsetUtil.CHARSET_GBK);
body.release();
} }
} }

View File

@ -1,4 +1,4 @@
package com.njzscloud.tuqiang.msg; package com.njzscloud.common.tuqiang.msg;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
@ -14,6 +14,9 @@ import lombok.experimental.Accessors;
public class TerminalGeneralResponseMsg { public class TerminalGeneralResponseMsg {
private int flowId; private int flowId;
private int messageId; private int messageId;
/**
* 0/123
*/
private int result; private int result;
public TerminalGeneralResponseMsg(byte[] bytes) { public TerminalGeneralResponseMsg(byte[] bytes) {

View File

@ -1,4 +1,4 @@
package com.njzscloud.tuqiang.msg; package com.njzscloud.common.tuqiang.msg;
import cn.hutool.core.util.CharsetUtil; import cn.hutool.core.util.CharsetUtil;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;

View File

@ -1,8 +1,7 @@
package com.njzscloud.tuqiang.msg; package com.njzscloud.common.tuqiang.msg;
import cn.hutool.core.util.CharsetUtil; import cn.hutool.core.util.CharsetUtil;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import lombok.ToString; import lombok.ToString;
@ -17,12 +16,11 @@ public class TerminalTxtReportMsg {
/** /**
* *
*/ */
private String text; private String txt;
public TerminalTxtReportMsg(byte[] bytes) { public TerminalTxtReportMsg(ByteBuf body) {
ByteBuf body = Unpooled.wrappedBuffer(bytes);
this.type = body.readByte(); this.type = body.readByte();
this.text = body.toString(body.readerIndex(), body.readableBytes(), this.txt = body.toString(body.readerIndex(), body.readableBytes(),
this.type == 0x00 ? this.type == 0x00 ?
CharsetUtil.CHARSET_GBK : CharsetUtil.CHARSET_GBK :
CharsetUtil.CHARSET_UTF_8); CharsetUtil.CHARSET_UTF_8);

View File

@ -0,0 +1,93 @@
package com.njzscloud.common.tuqiang.support;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
public enum Directive {
/**
*
*/
_C("C"),
/**
*
*/
_H("H"),
/**
* IP
*/
_T("T"),
/**
*
*/
_CS("CS"),
/**
*
*/
_3U("3U"),
/**
*
*/
_3Z("3Z"),
/**
*
*/
_2A("2A"),
/**
*
*/
_5Y("5Y"),
/**
* 8:00 8
*/
_9E("9E"),
/**
*
*/
_3D("3D"),
/**
*
*/
_6W("6W"),
/**
* 5-100
*/
_3E("3E"),
/**
* ID
*/
_N("N"),
/**
* 2G/4G
*/
_CGREG("CGREG"),
/**
* ,0-312212-20 21-31 0
*/
_CSQ("CSQ"),
/**
* GPS ,V-->N-->(V,0,0)
*/
_GPS("GPS"),
/**
* GPS ,OK-->ERR-->
*/
_GP("GP"),
/**
* 0 = 1 =
*/
_5C("5C"),
/**
* 1= 2= 0=
*/
_B("B"),
;
private final String value;
{
String[] strings = {"N:61000602070", "T:139.224.54.144,30100", "CGREG:1", "CSQ:23", "GPS:(V,0,0)", "A:", "C:0", "O:300", "CS:0", "3U:0", "3Z:0", "H:300", "2A:1", "GP:OK", "5C:0", "60:1", "5Y:3.5", "6W:0", "3E:000300", "5T:2,2", "9E:08:00", "1H:1", "3D:000000", "2S:0", "I:0000000", "B:2", "LT:0", "AG:1", "BD:0", "FLY:0"};
}
}

View File

@ -0,0 +1,14 @@
package com.njzscloud.common.tuqiang.support;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import lombok.experimental.Accessors;
@Getter
@Setter
@ToString
@Accessors(chain = true)
public class TuqiangDirectiveMsg {
}

View File

@ -1,10 +1,10 @@
package com.njzscloud.tuqiang; package com.njzscloud.common.tuqiang.support;
import java.lang.annotation.*; import java.lang.annotation.*;
@Documented @Documented
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD}) @Target({ElementType.METHOD})
public @interface Listener { public @interface TuqiangListener {
int messageId(); String messageId();
} }

View File

@ -1,17 +0,0 @@
package com.njzscloud.jt808.util;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class FlowId {
private static final Map<Integer, Integer> flowId = new ConcurrentHashMap<>();
public static synchronized int next(int messageId) {
Integer i = flowId.get(messageId);
if (i == null || i >= 0xFFFF) {
i = 0;
}
flowId.put(messageId, i++);
return i;
}
}

View File

@ -1,213 +0,0 @@
package com.njzscloud.jt808.util;
import com.njzscloud.jt808.protocol.JT808Message;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFutureListener;
import lombok.extern.slf4j.Slf4j;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;
/**
* JT808
* 便JT808
*/
@Slf4j
public class JT808 {
// 终端手机号与Channel的映射
private static final Map<String, Channel> terminalChannels = new ConcurrentHashMap<>();
private static final Map<Integer, List<Consumer<JT808Message>>> listeners = new ConcurrentHashMap<>();
/**
*
*/
public static void addListener(Integer messageId, Consumer<JT808Message> listener) {
if (messageId != null && listener != null) {
listeners.computeIfAbsent(messageId, k -> new java.util.ArrayList<>()).add(listener);
}
}
/**
*
*/
public static void triggerListener(Integer messageId, JT808Message message) {
if (messageId != null && message != null) {
List<Consumer<JT808Message>> consumers = listeners.get(messageId);
if (consumers != null) {
consumers.forEach(m -> m.accept(message));
} else {
log.warn("当前消息无处理函数: 0x{}、消息内容: {}", Integer.toHexString(messageId), message);
}
}
}
/**
*
*/
public static void unregisterListener(String messageId) {
if (messageId != null) {
listeners.remove(messageId);
}
}
/**
*
*/
public static void register(String terminalId, Channel channel) {
if (terminalId != null && channel != null) {
if (terminalChannels.get(terminalId) != channel) {
terminalChannels.put(terminalId, channel);
}
}
}
/**
*
*/
public static void unregister(Channel channel) {
if (channel != null) {
terminalChannels.values().removeIf(ch -> ch == channel);
}
}
/**
* (0x8001)
*/
public static void sendGeneralResponse(JT808Message message) {
sendMessage(message.getTerminalPhone(), createGeneralMessage(message));
}
/**
* (0x8100)
*/
public static void sendTerminalRegisterResponse(String terminalPhone, int flowId, String authCode, int result) {
// 构建消息体
ByteBuf body = Unpooled.buffer();
body.writeByte(result); // 结果
if (result == 0) { // 成功才需要后面的字段
writeString(body, authCode); // 鉴权码
}
// 构建消息
byte[] bytes = ByteBufUtil.getBytes(body);
body.release();
JT808Message message = createBaseMessage(terminalPhone, 0x8100, bytes);
message.setFlowId(flowId);
// 发送消息
sendMessage(terminalPhone, message);
}
/**
* (0x8300)
*/
public static void sendTextMessage(String terminalPhone, String content, int messageType) {
// 构建消息体
ByteBuf body = Unpooled.buffer();
body.writeByte(messageType); // 消息类型
writeString(body, content); // 消息内容
byte[] bytes = ByteBufUtil.getBytes(body);
body.release();
// 构建消息
JT808Message message = createBaseMessage(terminalPhone, 0x8300, bytes);
// 发送消息
sendMessage(terminalPhone, message);
}
/**
*
*/
public static void sendMessage(String terminalId, JT808Message message) {
if (terminalId == null || message == null) {
return;
}
Channel channel = terminalChannels.get(terminalId);
if (channel != null && channel.isActive()) {
channel.writeAndFlush(message)
.addListener((ChannelFutureListener) future -> {
if (future.isSuccess()) {
log.info("消息发送成功, 终端: {}, 消息ID: 0x{}, 流水号: {}", terminalId, Integer.toHexString(message.getMessageId()), message.getFlowId());
} else {
log.error("消息发送失败, 终端: {}", terminalId, future.cause());
}
});
} else {
unregister(channel);
// 终端不在线或通道已关闭
log.warn("终端不在线: {}", terminalId);
}
}
/**
*
*/
public static JT808Message createBaseMessage(String terminalId, int messageId) {
return createBaseMessage(terminalId, messageId, null);
}
public static JT808Message createBaseMessage(String terminalId, int messageId, byte[] body) {
JT808Message message = new JT808Message()
.setFlowId(FlowId.next(messageId));
// 设置消息ID
message.setMessageId(messageId);
// 设置消息体属性
// 其他属性默认:不分包,不加密
if (body != null) {
message.setMessageBodyProps(body.length);
} else {
message.setMessageBodyProps(0);
}
// 设置终端手机号
message.setTerminalPhone(terminalId);
// 设置消息体
message.setMessageBody(body);
return message;
}
public static JT808Message createGeneralMessage(JT808Message message) {
String terminalPhone = message.getTerminalPhone();
int flowId = message.getFlowId();
int messageId = message.getMessageId();
ByteBuf body = Unpooled.buffer();
ByteBufUtil.writeShortBE(body, flowId);
ByteBufUtil.writeShortBE(body, messageId);
body.writeByte(0);
// 构建消息
byte[] bytes = ByteBufUtil.getBytes(body);
body.release();
return JT808.createBaseMessage(terminalPhone, 0x8001, bytes);
}
/**
* JT8081 +
*/
private static void writeString(ByteBuf buf, String str) {
if (str == null || str.isEmpty()) {
buf.writeByte(0);
return;
}
byte[] bytes = str.getBytes();
// 字符串长度(1字节) + 字符串内容
buf.writeByte(bytes.length);
buf.writeBytes(bytes);
}
}

View File

@ -0,0 +1,15 @@
package com.njzscloud.param;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import lombok.experimental.Accessors;
@Getter
@Setter
@ToString
@Accessors(chain = true)
public class EnableWarnParam {
private String terminalId;
private boolean enable;
}

View File

@ -0,0 +1,14 @@
package com.njzscloud.param;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import lombok.experimental.Accessors;
@Getter
@Setter
@ToString
@Accessors(chain = true)
public class LocationCurrentParam {
private String terminalId;
}

View File

@ -0,0 +1,15 @@
package com.njzscloud.param;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import lombok.experimental.Accessors;
@Getter
@Setter
@ToString
@Accessors(chain = true)
public class LocationTrackParam {
private String terminalId;
private int interval;
}

View File

@ -0,0 +1,14 @@
package com.njzscloud.param;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import lombok.experimental.Accessors;
@Getter
@Setter
@ToString
@Accessors(chain = true)
public class ObtainDeviceInfoParam {
private String terminalId;
}

View File

@ -0,0 +1,16 @@
package com.njzscloud.param;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import lombok.experimental.Accessors;
@Getter
@Setter
@ToString
@Accessors(chain = true)
public class SetHeartbeatParam {
private String terminalId;
private int interval;
}

View File

@ -0,0 +1,15 @@
package com.njzscloud.param;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import lombok.experimental.Accessors;
@Getter
@Setter
@ToString
@Accessors(chain = true)
public class SpeedThresholdParam {
private String terminalId;
private int speed;
}

View File

@ -0,0 +1,42 @@
package com.njzscloud.result;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import lombok.experimental.Accessors;
@Getter
@Setter
@ToString
@Accessors(chain = true)
public class RealtimeLocationResult {
private String terminalId;
private String type;
/**
* 1 1
* 7 1
* 8 1:
*/
private long alarmFlag;
/**
* 0 0ACC 1ACC
* 1 0 1
* 2 0 1
* 3 0 1西
*/
private long status;
private double longitude;
private double latitude;
private int altitude;
private double speed;
private int direction;
private String time;
private boolean overspeed;
private boolean powerUnderVoltage;
private boolean soundAlarm;
private boolean accOn;
private boolean position;
private boolean south;
private boolean west;
}

View File

@ -1,79 +0,0 @@
package com.njzscloud.tuqiang;
import com.njzscloud.jt808.protocol.JT808Message;
import com.njzscloud.jt808.util.JT808;
import com.njzscloud.tuqiang.msg.*;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class Listeners {
@Listener(messageId = 0x0100)
public void onTerminalRegister(JT808Message message) {
String terminalPhone = message.getTerminalPhone();
ByteBuf body = Unpooled.buffer();
ByteBufUtil.writeShortBE(body, message.getFlowId());
body.writeByte(0);
ByteBufUtil.writeUtf8(body, terminalPhone);
// 构建消息
byte[] bytes = ByteBufUtil.getBytes(body);
body.release();
JT808.sendMessage(terminalPhone, JT808.createBaseMessage(terminalPhone, 0x8100, bytes));
TerminalRegisterMsg terminalRegisterMsg = message.getMessageBody(TerminalRegisterMsg.class);
log.info("终端注册消息: {}", terminalRegisterMsg);
}
@Listener(messageId = 0x0102)
public void onTerminalAuth(JT808Message message) {
JT808.sendGeneralResponse(message);
TerminalAuthMsg authMsg = message.getMessageBody(TerminalAuthMsg.class);
log.info("终端鉴权消息: {}", authMsg);
// ThreadUtil.sleep(10000);
// log.info("发送指令: {}", "<SPBSJ*P:BSJGPS*C:0*H:300>");
// JT808.sendMessage("61000602070", JT808.createBaseMessage("61000602070", 0x8300, new PublishDirectiveMsg("<SPBSJ*P:BSJGPS*C:0*H:300>").toBytes()));
// ThreadUtil.sleep(10000);
// JT808.sendMessage("61000602070", JT808.createBaseMessage("61000602070", 0x8300, new PublishDirectiveMsg("<CKBSJ>").toBytes()));
}
@Listener(messageId = 0x0002)
public void onHeartbeat(JT808Message message) {
log.info("终端心跳消息: {}", message.getTerminalPhone());
JT808.sendGeneralResponse(message);
}
@Listener(messageId = 0x0200)
public void onLocationReport(JT808Message message) {
LocationReportMsg locationReportMsg = message.getMessageBody(LocationReportMsg.class);
log.info("终端位置信息汇报消息: {}", locationReportMsg);
JT808.sendGeneralResponse(message);
}
@Listener(messageId = 0x0001)
public void onTerminalGeneralResponse(JT808Message message) {
TerminalGeneralResponseMsg terminalGeneralResponseMsg = message.getMessageBody(TerminalGeneralResponseMsg.class);
log.info("终端通用应答消息: {}", terminalGeneralResponseMsg);
JT808.sendGeneralResponse(message);
}
@Listener(messageId = 0x6006)
public void onTerminalTxtReport(JT808Message message) {
TerminalTxtReportMsg terminalTxtReportMsg = message.getMessageBody(TerminalTxtReportMsg.class);
log.info("终端文本信息汇报消息: {}", terminalTxtReportMsg);
JT808.sendGeneralResponse(message);
}
@Listener(messageId = 0x0201)
public void onSearchLocationResponse(JT808Message message) {
SearchLocationResponseMsg searchLocationResponseMsg = message.getMessageBody(SearchLocationResponseMsg.class);
log.info("查询位置响应消息: {}", searchLocationResponseMsg);
JT808.sendGeneralResponse(message);
}
}

View File

@ -1,31 +0,0 @@
package com.njzscloud.tuqiang;
import com.njzscloud.jt808.util.JT808;
import lombok.extern.slf4j.Slf4j;
import java.lang.reflect.Method;
@Slf4j
public class Tuqiang {
public Tuqiang() {
addListeners(new Listeners());
}
private void addListeners(Object object) {
Method[] methods = object.getClass().getMethods();
for (Method method : methods) {
if (method.isAnnotationPresent(Listener.class)) {
Listener listener = method.getAnnotation(Listener.class);
JT808.addListener(listener.messageId(), (msg) -> {
try {
method.invoke(object, msg);
} catch (Exception e) {
log.error("处理消息 {} 时出错", listener.messageId(), e);
}
});
}
}
}
}

View File

@ -1,27 +0,0 @@
package com.njzscloud.tuqiang.msg;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import lombok.experimental.Accessors;
@Getter
@Setter
@ToString
@Accessors(chain = true)
public class PublishDirectiveMsg {
private byte flag;
private String directive;
public PublishDirectiveMsg(String directive) {
this.flag = 0x01;
this.directive = directive;
}
public byte[] toBytes() {
byte[] messageBody = new byte[directive.length() + 1];
messageBody[0] = flag;
System.arraycopy(directive.getBytes(), 0, messageBody, 1, directive.length());
return messageBody;
}
}

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false"> <configuration debug="false">
<conversionRule conversionWord="clr" converterClass="com.njzscloud.common.log.ColorConverter"/> <conversionRule conversionWord="clr" converterClass="com.njzscloud.common.core.log.ColorConverter"/>
<property name="log_path" value="logs"/> <property name="log_path" value="logs"/>
<property name="service_name" value="gps"/> <property name="service_name" value="gps"/>