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 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