1、优化解算结果曲线显示

2、增加ntrip proxy服务
This commit is contained in:
weidong 2024-06-06 10:25:52 +08:00
parent e1e3cf084e
commit fe8eb2e07d
27 changed files with 743 additions and 20 deletions

View File

@ -15,6 +15,7 @@
<module>sec-api</module>
<module>sec-beidou-fwd</module>
<module>sec-test-device</module>
<module>sec-ntrip-proxy</module>
</modules>
<properties>

View File

@ -26,6 +26,9 @@ mybatis-plus.configuration.map-underscore-to-camel-case=false
xfz.server.host = 115.236.153.174
xfz.server.port = 31035
gxlj.server.host = 222.216.2.131
gxlj.server.port = 895
#gzy.server.host = 8.134.84.223
#gzy.server.port = 8088
gzy.server.host = 127.0.0.1
@ -41,3 +44,6 @@ kingma.server.login_user = ceshi
kingma.server.login_pwd = ceshi!123
kingma.server.login_host = https://www.everiaction.com/IOT-ADAPTER-CUSTOM/auth/anon/login
kingma.server.data_host = https://www.everiaction.com/IOT-DATA-GATHER/receive/data/formula
zny.server.host = http://119.3.203.174:8080/cs-detection-1.2/bsn/xyz/addDefo
zny.server.host2 = http://119.3.203.174:8080/cs-detection-2.0/bsn/xyz/addDefo

View File

@ -20,7 +20,7 @@ public class AuthInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String username = SessionUtils.getCurrentUser(request);
if (!StringUtils.isEmpty(username)) {
if (StringUtils.hasText(username)) {
return true;
}
String clientType = request.getHeader("x-imdroid-type");

View File

@ -14,6 +14,7 @@ import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.util.StringUtils;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@ -139,7 +140,7 @@ public class GnssCalcDataController extends BasicController implements CommonExc
public void checkSearchParams(JSONObject search){
if(search != null) {
String parentId = search.getString("sl_d.parentid");
isJoinQuery = (parentId != null && !parentId.isEmpty());
isJoinQuery = (parentId != null && StringUtils.hasText(parentId));
}
else isJoinQuery=false;
}

View File

@ -106,22 +106,22 @@ public class GnssDeviceController extends BasicController{
JSONObject search = (JSONObject) JSONObject.parse(searchParams);
//设备号
String deviceid = search.getString("deviceid");
if (!StringUtils.isEmpty(deviceid)) {
if (StringUtils.hasText(deviceid)) {
queryWrapper.like("deviceid", deviceid);
}
//父设备号
String parentid = search.getString("parentid");
if (!StringUtils.isEmpty(parentid)) {
if (StringUtils.hasText(parentid)) {
queryWrapper.like("parentid", parentid);
}
//项目号
String project_id = search.getString("project_id");
if (!StringUtils.isEmpty(project_id)) {
if (StringUtils.hasText(project_id)) {
queryWrapper.like("project_id", project_id);
}
//所属组织
String tenantname = search.getString("tenantname");
if (!StringUtils.isEmpty(tenantname)) {
if (StringUtils.hasText(tenantname)) {
queryWrapper.like("tenantname", tenantname);
}
//设备类型
@ -337,12 +337,12 @@ public class GnssDeviceController extends BasicController{
JSONObject search = (JSONObject) JSONObject.parse(searchParams);
//设备号
String deviceid = search.getString("deviceid");
if (!StringUtils.isEmpty(deviceid)) {
if (StringUtils.hasText(deviceid)) {
queryWrapper.like("deviceid", deviceid);
}
//父设备号
String parentid = search.getString("parentid");
if (!StringUtils.isEmpty(parentid)) {
if (StringUtils.hasText(parentid)) {
queryWrapper.like("parentid", parentid);
}
//设备类型

View File

@ -54,7 +54,7 @@ public class OssFileController {
@RequestMapping("/upload-device-image")
public HttpResult upload(@RequestParam("file") MultipartFile multipartFile, String deviceId) throws Exception {
System.out.println("upload device image:" + multipartFile.getOriginalFilename());
if (StringUtils.isEmpty(deviceId)) {
if (!StringUtils.hasText(deviceId)) {
return HttpResult.fail("设备编号不能为空");
}

View File

@ -47,7 +47,7 @@ public interface CommonExcelService<T, R> {
*/
default AbstractWrapper prepareQueryWrapper() {
QueryWrapper<T> queryWrapper = new QueryWrapper<>();
if (!StringUtils.isEmpty(getOrderByColumn())) {
if (StringUtils.hasText(getOrderByColumn())) {
if ("desc".equals(getOrder())) {
queryWrapper.orderByDesc(getOrderByColumn());
} else {
@ -192,19 +192,19 @@ public interface CommonExcelService<T, R> {
*/
default void setQueryWrapper(AbstractWrapper queryWrapper, String paraName, Object paraValue) {
// String
if (paraName.startsWith("s") && !StringUtils.isEmpty(paraValue)) {
if (paraName.startsWith("s") && StringUtils.hasText((String)paraValue)) {
addStringQueryWrapper(queryWrapper, paraName, paraValue);
}
// Number
else if (paraName.startsWith("n") && !StringUtils.isEmpty(paraValue)) {
else if (paraName.startsWith("n") && StringUtils.hasText((String)paraValue)) {
addNumberQueryWrapper(queryWrapper, paraName, paraValue);
}
// Date & Time
else if (paraName.startsWith("d") && !StringUtils.isEmpty(paraValue)) {
else if (paraName.startsWith("d") && StringUtils.hasText((String)paraValue)) {
addDateQueryWrapper(queryWrapper, paraName, paraValue);
}
// Boolean
else if (paraName.startsWith("b") && !StringUtils.isEmpty(paraValue)) {
else if (paraName.startsWith("b") && StringUtils.hasText((String)paraValue)) {
addBooleanQueryWrapper(queryWrapper, paraName, paraValue);
}
}
@ -216,7 +216,7 @@ public interface CommonExcelService<T, R> {
default void addStringQueryWrapper(AbstractWrapper queryWrapper, String paraName, Object paraValue) {
String column = getColumn(paraName);
String value = paraValue.toString();
if (StringUtils.isEmpty(value)) {
if (!StringUtils.hasText(value)) {
return;
}
if (paraName.startsWith("s_")) {

View File

@ -1,6 +1,10 @@
# Attention: serverTimezone=Asia/Shanghai !!!
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/beidou?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&useSSL=true
spring.datasource.username=admin
#spring.datasource.url=jdbc:mysql://192.168.101.54:3306/beidou?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&useSSL=true
#spring.datasource.username=beidou
#spring.datasource.password=Passw0rd#123!
#spring.datasource.url=jdbc:mysql://127.0.0.1:3306/beidou?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&useSSL=true
spring.datasource.url=jdbc:mysql://139.9.51.237:3306/beidou?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&useSSL=true
spring.datasource.username=radmin
spring.datasource.password=DBMgr_2022
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.jpa.properties.hibernate.hbm2ddl.auto=update
@ -9,7 +13,7 @@ spring.jpa.show-sql=true
spring.sql.init.mode=always
#spring.datasource.initialize=true
spring.sql.init.schema-locations=classpath:db/schema.sql
#spring.sql.init.schema-locations=classpath:db/schema.sql
#thymeleaf
spring.thymeleaf.cache=false

View File

@ -171,8 +171,8 @@
// 监听搜索操作
form.on('submit(data-search-btn)', function (data) {
var result = JSON.stringify(data.field);
var deviceId = $('#deviceid').val();
searchDeviceId = !isNaN(parseFloat(deviceId));
var deviceId = $('#input_deviceid').val();
searchDeviceId = !isNaN(deviceId);
//执行搜索重载
table.reload('currentTableId', {
page: {

106
sec-ntrip-proxy/pom.xml Normal file
View File

@ -0,0 +1,106 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.imdroid</groupId>
<artifactId>security-monitor</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>sec-ntrip-proxy</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.78.Final</version>
</dependency>
<dependency>
<groupId>com.imdroid</groupId>
<artifactId>sec-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.imdroid</groupId>
<artifactId>sec-common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- 矩阵工具 -->
<dependency>
<groupId>org.ejml</groupId>
<artifactId>ejml-all</artifactId>
<version>0.41</version>
</dependency>
</dependencies>
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>central</id>
<name>ali-mirror</name>
<url>https://maven.aliyun.com/repository/central</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
</project>

View File

@ -0,0 +1,24 @@
package com.imdroid.ntripproxy;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;
import org.springframework.context.annotation.ComponentScan;
/**
* @author Layton
* @date 2023/1/31 20:33
*/
@ComponentScan({"com.imdroid.*"})
@SpringBootApplication(exclude = {
DataSourceAutoConfiguration.class,
DataSourceTransactionManagerAutoConfiguration.class,
HibernateJpaAutoConfiguration.class})
public class NtripProxyApp {
public static void main(String[] args) {
SpringApplication.run(NtripProxyApp.class, args);
}
}

View File

@ -0,0 +1,28 @@
package com.imdroid.ntripproxy.executor;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.List;
@Component
public class BizExecutors {
private static HashMap<Class<?>, Object> executors = new HashMap<>();
private static List<Executor<?, ?>> executorList;
public BizExecutors(List<Executor<?, ?>> executorList) {
this.executorList = executorList;
for (Executor<?, ?> executor : this.executorList) {
System.out.println("executor type:" + executor.getMessageType().getName());
executors.put(executor.getMessageType(), executor);
}
}
public static <Q, R> R execute(Q query) {
Executor<Q, R> executor = (Executor<Q, R>)(executors.get(query.getClass()));
return executor.execute(query);
}
}

View File

@ -0,0 +1,38 @@
package com.imdroid.ntripproxy.executor;
import com.imdroid.ntripproxy.message.D333RtcmMessage;
import com.imdroid.ntripproxy.service.OnlineChannels;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
/**
* @author Layton
* @date 2023/2/2 20:49
*/
@Component
public class D333RtcmMessageExecutor implements Executor<D333RtcmMessage, Void> {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Override
public Void execute(D333RtcmMessage message) {
String id = message.getId();
// 补齐tenantId
if (logger.isDebugEnabled()) {
logger.debug("receive d333 message of device: "+id+", seq:"+message.getSeq()+", len:"+message.getLen());
}
// 推送基站数据
OnlineChannels.INSTANCE.get(id).ifPresent(deviceChannel -> {
deviceChannel.writeAndFlush(message.getTransBuf());
});
return null;
}
@Override
public Class<?> getMessageType() {
return D333RtcmMessage.class;
}
}

View File

@ -0,0 +1,42 @@
package com.imdroid.ntripproxy.executor;
import com.imdroid.common.util.ThreadManager;
import com.imdroid.ntripproxy.message.D341GgaMessage;
import com.imdroid.ntripproxy.service.UDPClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* @author Layton
* @date 2023/2/2 20:50
*/
@Component
public class D341GgaMessageExecutor implements Executor<D341GgaMessage, Void> {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Autowired
UDPClient udpClient;
@Override
public Void execute(D341GgaMessage message) {
if (logger.isDebugEnabled()) {
logger.debug("receive d341 message of device: "+message.getId()+", seq:"+message.getSeq()+", len:"+message.getLen());
}
// 发给ntrip server
ThreadManager.getFixedThreadPool().submit(() -> {
byte[] data = new byte[message.getTransBuf().readableBytes()];
message.getTransBuf().getBytes(0,data);
udpClient.sendMessage(data);
});
return null;
}
@Override
public Class<?> getMessageType() {
return D341GgaMessage.class;
}
}

View File

@ -0,0 +1,12 @@
package com.imdroid.ntripproxy.executor;
/**
* @author Layton
* @date 2022/4/8 22:32
*/
public interface Executor<Q, R> {
R execute(Q message);
Class<?> getMessageType();
}

View File

@ -0,0 +1,39 @@
package com.imdroid.ntripproxy.executor;
import com.imdroid.ntripproxy.message.*;
import io.netty.buffer.ByteBuf;
import java.util.HashMap;
import java.util.Map;
/**
* @author Layton
* @date 2023/2/2 20:41
*/
public class MessageParser {
private static final Map<Short, Class<? extends NtripMessage>> types = new HashMap<>();
public static final MessageParser instance = new MessageParser();
private MessageParser() {
}
static {
types.put((short)0xd333, D333RtcmMessage.class);
types.put((short)0xd341, D341GgaMessage.class);
}
public NtripMessage parse(ByteBuf src) throws Exception {
short flag = src.getShort(0); // msg flag
Class<? extends NtripMessage> clz = types.get(flag);
if (clz == null) {
throw new UnSupportedMessageException();
}
NtripMessage message = clz.newInstance();
message.decode(src);
return message;
}
}

View File

@ -0,0 +1,31 @@
package com.imdroid.ntripproxy.message;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* @author Layton
* @date 2023/2/2 20:50
*/
@Data
@EqualsAndHashCode(callSuper=true)
public class D333RtcmMessage extends NtripMessage {
@Override
public void decodeBody(ByteBuf src) {
fromDevice = false;
// d3 42 length(2048+6) device_id(4bytes) seq(2bytes) data
int transMsgLen = src.readableBytes() + NON_NTRIP_HEAD_LEN-DEVICE_ID_OFFSET;
byte[] padding = new byte[NON_NTRIP_HEAD_LEN];
// 修改d341为d332
transBuf = Unpooled.buffer();
transBuf.writeShort(0xd331);//head
transBuf.writeShort(transMsgLen);//length
transBuf.writeBytes(src,DEVICE_ID_OFFSET,4);//id
transBuf.writeBytes(padding);
transBuf.writeBytes(src,DEVICE_ID_OFFSET+4,src.readableBytes()-(DEVICE_ID_OFFSET+4));
}
}

View File

@ -0,0 +1,29 @@
package com.imdroid.ntripproxy.message;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* @author Layton
* @date 2023/2/2 20:50
*/
@Data
@EqualsAndHashCode(callSuper=true)
public class D341GgaMessage extends NtripMessage {
@Override
public void decodeBody(ByteBuf src) {
fromDevice = true;
// length, excluding pps,pitch,roll,yaw,shock
int transMsgLen = src.readableBytes() - NON_NTRIP_HEAD_LEN-DEVICE_ID_OFFSET;
// 修改d341为d332
transBuf = Unpooled.buffer();
transBuf.writeShort(0xd332);//head
transBuf.writeShort(transMsgLen);//length
transBuf.writeBytes(src,DEVICE_ID_OFFSET,4);//id
transBuf.writeBytes(src,GGA_OFFSET,src.readableBytes()-GGA_OFFSET);
}
}

View File

@ -0,0 +1,46 @@
package com.imdroid.ntripproxy.message;
import io.netty.buffer.ByteBuf;
import lombok.Data;
/**
* @author Layton
* @date 2023/2/2 20:32
*/
@Data
public abstract class NtripMessage {
// D3XX(2B) Seq/len(2B) ID PPS(2B) PITCH ROLL YAW Shock
final int DEVICE_ID_OFFSET = 4;
final int GGA_OFFSET = 26;
final int NON_NTRIP_HEAD_LEN = 18;
protected int header;
protected String id;
protected int len;
protected int seq;
boolean fromDevice = false;
ByteBuf transBuf;
public void decode(ByteBuf src) {
if (shouldDecodeHeader()) {
// read操作会移动ByteBuf内部指针除D331外其他都用read来读
//int packetLen = src.readableBytes();
int pos = 0;
this.header = src.getUnsignedShort(pos);pos+=2;
this.len = src.getUnsignedShort(pos);pos+=2;
this.seq = this.len >> 11;
this.len = this.len & 0x7FF;
this.id = String.valueOf(src.getUnsignedInt(pos));
}
decodeBody(src);
}
public abstract void decodeBody(ByteBuf src);
public boolean shouldDecodeHeader() {
return true;
}
}

View File

@ -0,0 +1,8 @@
package com.imdroid.ntripproxy.message;
/**
* @author Layton
* @date 2023/2/2 20:43
*/
public class UnSupportedMessageException extends RuntimeException {
}

View File

@ -0,0 +1,50 @@
package com.imdroid.ntripproxy.service;
import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
import io.netty.channel.socket.DatagramPacket;
import lombok.Data;
import java.net.InetSocketAddress;
/**
* @author Layton
* @date 2023/2/2 21:00
*/
@Data
public class DeviceChannel {
private String deviceId;
private String imei;
private Channel channel;
private InetSocketAddress address;
private long lastTime;
private boolean tcp;
public DeviceChannel(String deviceId, Channel channel, InetSocketAddress address) {
this.deviceId = deviceId;
this.channel = channel;
this.address = address;
lastTime = System.currentTimeMillis();
this.tcp = (address == null);
}
public boolean isOnline() {
if (tcp) {
return channel.isActive();
}
// return (System.currentTimeMillis() - lastTime) < 28 * 1000L;
return true;
}
public void writeAndFlush(ByteBuf buf) {
if (tcp) {
channel.writeAndFlush(buf);
} else {
channel.writeAndFlush(new DatagramPacket(buf, address));
}
}
}

View File

@ -0,0 +1,45 @@
package com.imdroid.ntripproxy.service;
import io.netty.channel.Channel;
import java.net.InetSocketAddress;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
/**
* @author Layton
* @date 2023/2/2 21:00
* 提供两种获取channel的方法通过deviceId通过imeiimei是DTU连接服务器最先上报的消息
* 利用imei-ipaddr-deviceId可以发现imei和deviceId的绑定关系
*/
public class OnlineChannels {
public static final OnlineChannels INSTANCE = new OnlineChannels();
// 设备已连接deviceId已上报
private final Map<String, DeviceChannel> dataChannels = new ConcurrentHashMap<>();
private OnlineChannels() {}
public DeviceChannel updateDataChannel(String deviceId, Channel channel, InetSocketAddress address) {
DeviceChannel deviceChannel = dataChannels.get(deviceId);
if(deviceChannel == null){
deviceChannel = new DeviceChannel(deviceId, channel, address);
dataChannels.put(deviceId, deviceChannel);
}
else {
deviceChannel.setChannel(channel);
deviceChannel.setAddress(address);
}
return deviceChannel;
}
public Optional<DeviceChannel> get(String deviceId) {
return Optional.ofNullable(dataChannels.get(deviceId));
}
}

View File

@ -0,0 +1,50 @@
package com.imdroid.ntripproxy.service;
import com.imdroid.common.util.DataTypeUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
@Service
public class UDPClient {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
private DatagramSocket socket;
private InetAddress inetAddress;
@Value("${ntrip.server.host}")
private String host;
@Value("${ntrip.server.port}")
private int port;
@PostConstruct
public void init() {
try {
logger.info("UDP client init "+host + ":" + port);
this.socket = new DatagramSocket();
this.inetAddress = InetAddress.getByName(host);
} catch (Exception e) {
logger.error("初始化udp客户端失败", e);
}
}
public void sendMessage(byte[] data) {
try {
if (logger.isDebugEnabled()) {
logger.debug("推送gga到NTRIP Server:{}", DataTypeUtil.getHexString(data));
}
DatagramPacket packet = new DatagramPacket(data, data.length, inetAddress, port);
socket.send(packet);
} catch (Exception e) {
logger.error("推送ntrip异常:", e);
}
}
}

View File

@ -0,0 +1,60 @@
package com.imdroid.ntripproxy.service;
import com.imdroid.common.util.DataTypeUtil;
import com.imdroid.ntripproxy.executor.BizExecutors;
import com.imdroid.ntripproxy.executor.MessageParser;
import com.imdroid.ntripproxy.message.NtripMessage;
import com.imdroid.ntripproxy.message.UnSupportedMessageException;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.socket.DatagramPacket;
import io.netty.util.ReferenceCountUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
/**
* @author Layton
* @date 2023/2/13 11:47
*/
@ChannelHandler.Sharable
@Component
public class UdpHandler extends ChannelInboundHandlerAdapter {
private static final Logger logger = LoggerFactory.getLogger(UdpHandler.class);
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
DatagramPacket packet = (DatagramPacket) msg;
try {
if (packet.content() == null) {
return;
}
if (logger.isDebugEnabled()) {
byte[] data = new byte[packet.content().readableBytes()];
packet.content().getBytes(0, data);
logger.debug("receive message:" + DataTypeUtil.getHexString(data));
}
// 消息解析
NtripMessage message = MessageParser.instance.parse(packet.content());
if(message.isFromDevice()) {
OnlineChannels.INSTANCE.updateDataChannel(message.getId(), ctx.channel(), packet.sender());
}
BizExecutors.execute(message);
} catch (UnSupportedMessageException e) {
logger.warn("receive un supported message: {}", e.getMessage());
} catch (Exception e) {
logger.error("channel read error: {}", e.toString());
} finally {
ReferenceCountUtil.release(msg);
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
super.exceptionCaught(ctx, cause);
logger.error("Exception caught: {}", cause.toString());
}
}

View File

@ -0,0 +1,60 @@
package com.imdroid.ntripproxy.service;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioDatagramChannel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;
/**
* @author Layton
* @date 2023/2/13 11:47
*/
@Component
public class UdpServer implements ApplicationRunner {
private final Logger logger = LoggerFactory.getLogger(UdpServer.class);
@Value("${ntrip.proxy.port}")
private Integer port;
@Autowired
private UdpHandler udpHandler;
public UdpServer() {
}
@Override
public void run(ApplicationArguments args) throws Exception {
new Thread(this::start0, "ntrip-proxy").start();
}
private void start0() {
EventLoopGroup group = new NioEventLoopGroup();
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(group)
.channel(NioDatagramChannel.class)
.option(ChannelOption.SO_SNDBUF, 1024*1024) //1M缓存考虑1000个基站同时转发
.option(ChannelOption.SO_RCVBUF, 1024*1024)//1M缓存断点续传要大带宽
.handler(udpHandler);
try {
ChannelFuture future = bootstrap.bind(port).sync().channel().closeFuture();
logger.info("ntrip proxy start at port {}", port);
future.await();
} catch (Exception e) {
logger.error("Error starting Imdroid protocol at port {}", port, e);
} finally {
group.shutdownGracefully();
}
}
}

View File

@ -0,0 +1,16 @@
server.port=9910
server.servlet.context-path=/gnss
spring.application.name=ntrip-proxy
spring.application.build=20240606
spring.jackson.dateFormat = yyyy-MM-dd HH:mm:ss
spring.jackson.time-zone = GMT+8
app.format.date = yyyy-MM-dd
app.format.time = HH:mm:ss
app.format.datetime = yyyy-MM-dd HH:mm:ss
ntrip.server.host = 127.0.0.1
ntrip.server.port = 11003
ntrip.proxy.port = 11001

View File

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8" ?>
<configuration scan="true" scanPeriod="30 seconds">
<property name="LOG_HOME" value="/opt/log"/>
<property name="LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n"/>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>${LOG_PATTERN}</pattern>
</encoder>
</appender>
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_HOME}/sideslopertcm.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${LOG_HOME}/sideslopertcm.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<maxFileSize>10MB</maxFileSize>
<maxHistory>20</maxHistory>
</rollingPolicy>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>${LOG_PATTERN}</pattern>
</encoder>
</appender>
<root level="debug">
<appender-ref ref="STDOUT" />
</root>
<logger name="org.springframework" level="warn" />
</configuration>