diff --git a/pom.xml b/pom.xml
index d08d8635..4d37485a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -15,6 +15,7 @@
sec-api
sec-beidou-fwd
sec-test-device
+ sec-ntrip-proxy
diff --git a/sec-beidou-fwd/src/main/resources/application.properties b/sec-beidou-fwd/src/main/resources/application.properties
index c4b4cbf7..1a98155e 100644
--- a/sec-beidou-fwd/src/main/resources/application.properties
+++ b/sec-beidou-fwd/src/main/resources/application.properties
@@ -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
\ No newline at end of file
diff --git a/sec-beidou/src/main/java/com/imdroid/beidou/auth/AuthInterceptor.java b/sec-beidou/src/main/java/com/imdroid/beidou/auth/AuthInterceptor.java
index 10e9d1dd..ef362fdb 100644
--- a/sec-beidou/src/main/java/com/imdroid/beidou/auth/AuthInterceptor.java
+++ b/sec-beidou/src/main/java/com/imdroid/beidou/auth/AuthInterceptor.java
@@ -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");
diff --git a/sec-beidou/src/main/java/com/imdroid/beidou/controller/GnssCalcDataController.java b/sec-beidou/src/main/java/com/imdroid/beidou/controller/GnssCalcDataController.java
index a324057b..18ddba37 100644
--- a/sec-beidou/src/main/java/com/imdroid/beidou/controller/GnssCalcDataController.java
+++ b/sec-beidou/src/main/java/com/imdroid/beidou/controller/GnssCalcDataController.java
@@ -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;
}
diff --git a/sec-beidou/src/main/java/com/imdroid/beidou/controller/GnssDeviceController.java b/sec-beidou/src/main/java/com/imdroid/beidou/controller/GnssDeviceController.java
index 681aa2aa..f2455065 100644
--- a/sec-beidou/src/main/java/com/imdroid/beidou/controller/GnssDeviceController.java
+++ b/sec-beidou/src/main/java/com/imdroid/beidou/controller/GnssDeviceController.java
@@ -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);
}
//设备类型
diff --git a/sec-beidou/src/main/java/com/imdroid/beidou/controller/OssFileController.java b/sec-beidou/src/main/java/com/imdroid/beidou/controller/OssFileController.java
index c0daf47c..c81a2192 100644
--- a/sec-beidou/src/main/java/com/imdroid/beidou/controller/OssFileController.java
+++ b/sec-beidou/src/main/java/com/imdroid/beidou/controller/OssFileController.java
@@ -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("设备编号不能为空");
}
diff --git a/sec-beidou/src/main/java/com/imdroid/beidou/service/CommonExcelService.java b/sec-beidou/src/main/java/com/imdroid/beidou/service/CommonExcelService.java
index b6c0250f..85aa475d 100644
--- a/sec-beidou/src/main/java/com/imdroid/beidou/service/CommonExcelService.java
+++ b/sec-beidou/src/main/java/com/imdroid/beidou/service/CommonExcelService.java
@@ -47,7 +47,7 @@ public interface CommonExcelService {
*/
default AbstractWrapper prepareQueryWrapper() {
QueryWrapper 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 {
*/
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 {
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_")) {
diff --git a/sec-beidou/src/main/resources/application.properties b/sec-beidou/src/main/resources/application.properties
index d12929f3..9c6e1b50 100644
--- a/sec-beidou/src/main/resources/application.properties
+++ b/sec-beidou/src/main/resources/application.properties
@@ -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
diff --git a/sec-beidou/src/main/resources/templates/page/gnss_data_calc.html b/sec-beidou/src/main/resources/templates/page/gnss_data_calc.html
index b3c94ec2..5eff1bb2 100644
--- a/sec-beidou/src/main/resources/templates/page/gnss_data_calc.html
+++ b/sec-beidou/src/main/resources/templates/page/gnss_data_calc.html
@@ -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: {
diff --git a/sec-ntrip-proxy/pom.xml b/sec-ntrip-proxy/pom.xml
new file mode 100644
index 00000000..a473aaf6
--- /dev/null
+++ b/sec-ntrip-proxy/pom.xml
@@ -0,0 +1,106 @@
+
+
+ 4.0.0
+
+ com.imdroid
+ security-monitor
+ 1.0-SNAPSHOT
+
+
+ sec-ntrip-proxy
+
+
+ 8
+ 8
+ UTF-8
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+ org.springframework.boot
+ spring-boot-devtools
+ true
+
+
+
+ org.slf4j
+ slf4j-api
+
+
+ ch.qos.logback
+ logback-classic
+
+
+ io.netty
+ netty-all
+ 4.1.78.Final
+
+
+
+ com.imdroid
+ sec-api
+ 1.0-SNAPSHOT
+
+
+ com.imdroid
+ sec-common
+ 1.0-SNAPSHOT
+
+
+
+ org.projectlombok
+ lombok
+ true
+
+
+
+ org.ejml
+ ejml-all
+ 0.41
+
+
+
+
+ ${project.artifactId}
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+ repackage
+
+
+
+
+
+
+
+
+
+ central
+ ali-mirror
+ https://maven.aliyun.com/repository/central
+
+ true
+
+
+ true
+
+
+
+
+
\ No newline at end of file
diff --git a/sec-ntrip-proxy/src/main/java/com/imdroid/ntripproxy/NtripProxyApp.java b/sec-ntrip-proxy/src/main/java/com/imdroid/ntripproxy/NtripProxyApp.java
new file mode 100644
index 00000000..d7e20424
--- /dev/null
+++ b/sec-ntrip-proxy/src/main/java/com/imdroid/ntripproxy/NtripProxyApp.java
@@ -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);
+ }
+}
diff --git a/sec-ntrip-proxy/src/main/java/com/imdroid/ntripproxy/executor/BizExecutors.java b/sec-ntrip-proxy/src/main/java/com/imdroid/ntripproxy/executor/BizExecutors.java
new file mode 100644
index 00000000..db3b5e8c
--- /dev/null
+++ b/sec-ntrip-proxy/src/main/java/com/imdroid/ntripproxy/executor/BizExecutors.java
@@ -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, Object> executors = new HashMap<>();
+
+ private static List> executorList;
+
+ public BizExecutors(List> executorList) {
+ this.executorList = executorList;
+ for (Executor, ?> executor : this.executorList) {
+ System.out.println("executor type:" + executor.getMessageType().getName());
+ executors.put(executor.getMessageType(), executor);
+ }
+ }
+
+
+ public static R execute(Q query) {
+ Executor executor = (Executor)(executors.get(query.getClass()));
+ return executor.execute(query);
+ }
+}
diff --git a/sec-ntrip-proxy/src/main/java/com/imdroid/ntripproxy/executor/D333RtcmMessageExecutor.java b/sec-ntrip-proxy/src/main/java/com/imdroid/ntripproxy/executor/D333RtcmMessageExecutor.java
new file mode 100644
index 00000000..803a21b8
--- /dev/null
+++ b/sec-ntrip-proxy/src/main/java/com/imdroid/ntripproxy/executor/D333RtcmMessageExecutor.java
@@ -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 {
+
+ 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;
+ }
+}
diff --git a/sec-ntrip-proxy/src/main/java/com/imdroid/ntripproxy/executor/D341GgaMessageExecutor.java b/sec-ntrip-proxy/src/main/java/com/imdroid/ntripproxy/executor/D341GgaMessageExecutor.java
new file mode 100644
index 00000000..4b58a570
--- /dev/null
+++ b/sec-ntrip-proxy/src/main/java/com/imdroid/ntripproxy/executor/D341GgaMessageExecutor.java
@@ -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 {
+
+ 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;
+ }
+}
diff --git a/sec-ntrip-proxy/src/main/java/com/imdroid/ntripproxy/executor/Executor.java b/sec-ntrip-proxy/src/main/java/com/imdroid/ntripproxy/executor/Executor.java
new file mode 100644
index 00000000..8aa8e06e
--- /dev/null
+++ b/sec-ntrip-proxy/src/main/java/com/imdroid/ntripproxy/executor/Executor.java
@@ -0,0 +1,12 @@
+package com.imdroid.ntripproxy.executor;
+
+/**
+ * @author Layton
+ * @date 2022/4/8 22:32
+ */
+public interface Executor {
+
+ R execute(Q message);
+
+ Class> getMessageType();
+}
diff --git a/sec-ntrip-proxy/src/main/java/com/imdroid/ntripproxy/executor/MessageParser.java b/sec-ntrip-proxy/src/main/java/com/imdroid/ntripproxy/executor/MessageParser.java
new file mode 100644
index 00000000..ae4289b2
--- /dev/null
+++ b/sec-ntrip-proxy/src/main/java/com/imdroid/ntripproxy/executor/MessageParser.java
@@ -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> 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;
+ }
+}
diff --git a/sec-ntrip-proxy/src/main/java/com/imdroid/ntripproxy/message/D333RtcmMessage.java b/sec-ntrip-proxy/src/main/java/com/imdroid/ntripproxy/message/D333RtcmMessage.java
new file mode 100644
index 00000000..a8c5996c
--- /dev/null
+++ b/sec-ntrip-proxy/src/main/java/com/imdroid/ntripproxy/message/D333RtcmMessage.java
@@ -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));
+ }
+
+}
diff --git a/sec-ntrip-proxy/src/main/java/com/imdroid/ntripproxy/message/D341GgaMessage.java b/sec-ntrip-proxy/src/main/java/com/imdroid/ntripproxy/message/D341GgaMessage.java
new file mode 100644
index 00000000..3db4645d
--- /dev/null
+++ b/sec-ntrip-proxy/src/main/java/com/imdroid/ntripproxy/message/D341GgaMessage.java
@@ -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);
+ }
+
+}
diff --git a/sec-ntrip-proxy/src/main/java/com/imdroid/ntripproxy/message/NtripMessage.java b/sec-ntrip-proxy/src/main/java/com/imdroid/ntripproxy/message/NtripMessage.java
new file mode 100644
index 00000000..80cf7099
--- /dev/null
+++ b/sec-ntrip-proxy/src/main/java/com/imdroid/ntripproxy/message/NtripMessage.java
@@ -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;
+ }
+}
diff --git a/sec-ntrip-proxy/src/main/java/com/imdroid/ntripproxy/message/UnSupportedMessageException.java b/sec-ntrip-proxy/src/main/java/com/imdroid/ntripproxy/message/UnSupportedMessageException.java
new file mode 100644
index 00000000..24102849
--- /dev/null
+++ b/sec-ntrip-proxy/src/main/java/com/imdroid/ntripproxy/message/UnSupportedMessageException.java
@@ -0,0 +1,8 @@
+package com.imdroid.ntripproxy.message;
+
+/**
+ * @author Layton
+ * @date 2023/2/2 20:43
+ */
+public class UnSupportedMessageException extends RuntimeException {
+}
diff --git a/sec-ntrip-proxy/src/main/java/com/imdroid/ntripproxy/service/DeviceChannel.java b/sec-ntrip-proxy/src/main/java/com/imdroid/ntripproxy/service/DeviceChannel.java
new file mode 100644
index 00000000..45c3ee6b
--- /dev/null
+++ b/sec-ntrip-proxy/src/main/java/com/imdroid/ntripproxy/service/DeviceChannel.java
@@ -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));
+ }
+ }
+}
diff --git a/sec-ntrip-proxy/src/main/java/com/imdroid/ntripproxy/service/OnlineChannels.java b/sec-ntrip-proxy/src/main/java/com/imdroid/ntripproxy/service/OnlineChannels.java
new file mode 100644
index 00000000..db3d9ded
--- /dev/null
+++ b/sec-ntrip-proxy/src/main/java/com/imdroid/ntripproxy/service/OnlineChannels.java
@@ -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;通过imei。imei是DTU连接服务器最先上报的消息
+ * 利用imei-ipaddr-deviceId,可以发现imei和deviceId的绑定关系
+ */
+public class OnlineChannels {
+
+ public static final OnlineChannels INSTANCE = new OnlineChannels();
+
+ // 设备已连接,deviceId已上报
+ private final Map 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 get(String deviceId) {
+ return Optional.ofNullable(dataChannels.get(deviceId));
+ }
+
+}
diff --git a/sec-ntrip-proxy/src/main/java/com/imdroid/ntripproxy/service/UDPClient.java b/sec-ntrip-proxy/src/main/java/com/imdroid/ntripproxy/service/UDPClient.java
new file mode 100644
index 00000000..21a5d54f
--- /dev/null
+++ b/sec-ntrip-proxy/src/main/java/com/imdroid/ntripproxy/service/UDPClient.java
@@ -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);
+ }
+ }
+}
diff --git a/sec-ntrip-proxy/src/main/java/com/imdroid/ntripproxy/service/UdpHandler.java b/sec-ntrip-proxy/src/main/java/com/imdroid/ntripproxy/service/UdpHandler.java
new file mode 100644
index 00000000..3b53203c
--- /dev/null
+++ b/sec-ntrip-proxy/src/main/java/com/imdroid/ntripproxy/service/UdpHandler.java
@@ -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());
+ }
+
+}
diff --git a/sec-ntrip-proxy/src/main/java/com/imdroid/ntripproxy/service/UdpServer.java b/sec-ntrip-proxy/src/main/java/com/imdroid/ntripproxy/service/UdpServer.java
new file mode 100644
index 00000000..2f08fdba
--- /dev/null
+++ b/sec-ntrip-proxy/src/main/java/com/imdroid/ntripproxy/service/UdpServer.java
@@ -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();
+ }
+ }
+}
diff --git a/sec-ntrip-proxy/src/main/resources/application.properties b/sec-ntrip-proxy/src/main/resources/application.properties
new file mode 100644
index 00000000..d8b970d1
--- /dev/null
+++ b/sec-ntrip-proxy/src/main/resources/application.properties
@@ -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
\ No newline at end of file
diff --git a/sec-ntrip-proxy/src/main/resources/logback.xml b/sec-ntrip-proxy/src/main/resources/logback.xml
new file mode 100644
index 00000000..1742b9e7
--- /dev/null
+++ b/sec-ntrip-proxy/src/main/resources/logback.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+ ${LOG_PATTERN}
+
+
+
+ ${LOG_HOME}/sideslopertcm.log
+
+ ${LOG_HOME}/sideslopertcm.%d{yyyy-MM-dd}.%i.log
+ 10MB
+ 20
+
+
+
+ ${LOG_PATTERN}
+
+
+
+
+
+
+
+
\ No newline at end of file