Browse Source

feat: dron 구성

feature/socket
지대한 7 months ago
parent
commit
6733144ba1
  1. 4
      app/kac-app/src/main/resources/application.yml
  2. 16
      app/kac-socket-app/src/main/java/kr/co/palnet/kac/socket/TestController.java
  3. 8
      app/kac-socket-app/src/main/java/kr/co/palnet/kac/socket/core/codec/DronDecoder.java
  4. 5
      app/kac-socket-app/src/main/java/kr/co/palnet/kac/socket/core/codec/DronEncoder.java
  5. 8
      app/kac-socket-app/src/main/java/kr/co/palnet/kac/socket/core/command/DronCommand.java
  6. 161
      app/kac-socket-app/src/main/java/kr/co/palnet/kac/socket/core/command/impl/AdsbDronCommandImpl.java
  7. 159
      app/kac-socket-app/src/main/java/kr/co/palnet/kac/socket/core/command/impl/AntosDronCommandImpl.java
  8. 166
      app/kac-socket-app/src/main/java/kr/co/palnet/kac/socket/core/command/impl/SandboxDronCommandImpl.java
  9. 107
      app/kac-socket-app/src/main/java/kr/co/palnet/kac/socket/core/handler/DronSocketHandler.java
  10. 18
      app/kac-socket-app/src/main/java/kr/co/palnet/kac/socket/core/model/ControlDto.java
  11. 97
      app/kac-socket-app/src/main/java/kr/co/palnet/kac/socket/core/model/DronDTO.java
  12. 20
      app/kac-socket-app/src/main/java/kr/co/palnet/kac/socket/core/model/DronHistoryDTO.java
  13. 8
      app/kac-socket-app/src/main/java/kr/co/palnet/kac/socket/core/model/DronRQ.java
  14. 15
      app/kac-socket-app/src/main/java/kr/co/palnet/kac/socket/core/model/DronRS.java
  15. 22
      app/kac-socket-app/src/main/java/kr/co/palnet/kac/socket/core/socket/DronChannelInitializer.java
  16. 59
      app/kac-socket-app/src/main/java/kr/co/palnet/kac/socket/core/util/AuthKeyUtil.java
  17. 53
      app/kac-socket-app/src/main/java/kr/co/palnet/kac/socket/core/util/ControlCacheUtil.java
  18. 84
      app/kac-socket-app/src/main/java/kr/co/palnet/kac/socket/core/util/DronCacheUtil.java
  19. 10
      app/kac-socket-app/src/main/java/kr/co/palnet/kac/socket/core/util/DronUtil.java
  20. 15
      app/kac-socket-app/src/main/java/kr/co/palnet/kac/socket/service/KacAppService.java
  21. 17
      app/kac-socket-app/src/main/java/kr/co/palnet/kac/socket/service/WebSocketService.java
  22. 13
      app/kac-socket-app/src/main/resources/application.yml

4
app/kac-app/src/main/resources/application.yml

@ -7,6 +7,8 @@ spring:
virtual:
enabled: true
server.port: 8000
logging:
level:
kr.co.palnet: DEBUG
@ -20,4 +22,4 @@ logging:
connection : OFF # 커넥션 확인가능
org.hibernate:
SQL: DEBUG
type.descriptor.sql.BasicBinder: TRACE
type.descriptor.sql.BasicBinder: TRACE

16
app/kac-socket-app/src/main/java/kr/co/palnet/kac/socket/TestController.java

@ -0,0 +1,16 @@
package kr.co.palnet.kac.socket;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/test")
public class TestController {
@GetMapping("/ping")
public String test() {
return "pong";
}
}

8
app/kac-socket-app/src/main/java/kr/co/palnet/kac/socket/core/codec/DronPayloadDecorder.java → app/kac-socket-app/src/main/java/kr/co/palnet/kac/socket/core/codec/DronDecoder.java

@ -3,6 +3,7 @@ package kr.co.palnet.kac.socket.core.codec;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageDecoder;
import kr.co.palnet.kac.socket.core.model.DronRQ;
import kr.co.palnet.kac.util.ObjectMapperUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@ -12,7 +13,7 @@ import java.util.List;
@Slf4j
@RequiredArgsConstructor
public class DronPayloadDecorder extends MessageToMessageDecoder<String> {
public class DronDecoder extends MessageToMessageDecoder<String> {
// private int DATA_LENGTH = 100;
private final ObjectMapper objectMapper = ObjectMapperUtils.getObjectMapper();
@ -20,7 +21,10 @@ public class DronPayloadDecorder extends MessageToMessageDecoder<String> {
protected void decode(ChannelHandlerContext ctx, String in, List<Object> out) throws Exception {
log.info(">>>>> decode <<<<<");
try {
DronPayload payload = objectMapper.readValue(in, DronPayload.class);
// TypeReference<DronRQ> typeRef = new TypeReference<DronRQ>() {
// };
// DronRQ payload = objectMapper.readValue(in, typeRef);
DronRQ payload = objectMapper.readValue(in, DronRQ.class);
if (payload != null) {
out.add(payload);
}

5
app/kac-socket-app/src/main/java/kr/co/palnet/kac/socket/core/codec/DronPayloadEncorder.java → app/kac-socket-app/src/main/java/kr/co/palnet/kac/socket/core/codec/DronEncoder.java

@ -3,6 +3,7 @@ package kr.co.palnet.kac.socket.core.codec;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageEncoder;
import kr.co.palnet.kac.socket.core.model.DronRS;
import kr.co.palnet.kac.util.ObjectMapperUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@ -11,12 +12,12 @@ import java.util.List;
@Slf4j
@RequiredArgsConstructor
public class DronPayloadEncorder extends MessageToMessageEncoder<DronPayload> {
public class DronEncoder extends MessageToMessageEncoder<DronRS> {
private final ObjectMapper objectMapper = ObjectMapperUtils.getObjectMapper();
@Override
protected void encode(ChannelHandlerContext ctx, DronPayload msg, List<Object> out) throws Exception {
protected void encode(ChannelHandlerContext ctx, DronRS msg, List<Object> out) throws Exception {
log.info(">>>>> encode <<<");
try {
String json = objectMapper.writeValueAsString(msg);

8
app/kac-socket-app/src/main/java/kr/co/palnet/kac/socket/core/command/DronCommand.java

@ -0,0 +1,8 @@
package kr.co.palnet.kac.socket.core.command;
import kr.co.palnet.kac.socket.core.model.DronRQ;
public interface DronCommand {
void command(final DronRQ rq);
String getCommandName();
}

161
app/kac-socket-app/src/main/java/kr/co/palnet/kac/socket/core/command/impl/AdsbDronCommandImpl.java

@ -0,0 +1,161 @@
package kr.co.palnet.kac.socket.core.command.impl;
import kr.co.palnet.kac.socket.core.model.ControlDto;
import kr.co.palnet.kac.socket.core.model.DronDTO;
import kr.co.palnet.kac.socket.core.model.DronHistoryDTO;
import kr.co.palnet.kac.socket.core.model.DronRQ;
import kr.co.palnet.kac.socket.core.command.DronCommand;
import kr.co.palnet.kac.socket.core.util.ControlCacheUtil;
import kr.co.palnet.kac.socket.core.util.DronCacheUtil;
import kr.co.palnet.kac.socket.core.util.DronUtil;
import kr.co.palnet.kac.socket.service.KacAppService;
import kr.co.palnet.kac.socket.service.WebSocketService;
import kr.co.palnet.kac.util.ObjectMapperUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Instant;
import java.util.*;
@Slf4j
@RequiredArgsConstructor
@Component
public class AdsbDronCommandImpl implements DronCommand {
private final WebSocketService webSocketService;
private final KacAppService kacAppService;
@Value("${app.kac-app.host}")
private String kacAppHost;
@Override
public String getCommandName() {
return "ADS-B";
}
@Override
public void command(DronRQ rq) {
log.info(">>>> AdsbCommandServiceImpl <<<<<");
List<DronDTO> resultList = rq.getBody();
/** 전문 설정 ***/
final String messageType = "LTEM";
final String objectType = "DRON";
/** 데이터 모델링 **/
for (DronDTO dron : resultList) {
// 위,경도 좌표가 0으로 들어오는 것은 무시 처리
if (DronUtil.latlonCheck(dron.getLat(), dron.getLon())) {
dron.setObjectType(objectType);
dron.setMessageType(messageType);
dron.setTerminalId(rq.getTerminalId());
dron.setRegDt(Instant.now());
// 서버 수신 시간 정보
dron.setServerRcvDt(Instant.now());
// 관제 이력 정보
List<DronHistoryDTO> hisList;
DronHistoryDTO history = DronHistoryDTO.builder()
.objectId(dron.getObjectId())
.lat(dron.getLat())
.lon(dron.getLon())
.build();
if (dron.getPostionHistory() != null) {
hisList = dron.getPostionHistory();
} else {
hisList = new ArrayList<>();
}
hisList.add(history);
dron.setPostionHistory(hisList);
// STEP 1. 전에 내부 메모리에서 controlId 조회
ControlCacheUtil controlCacheUtil = ControlCacheUtil.getInstance();
ControlDto dronCacheDTO = controlCacheUtil.get(dron.getObjectId());
if (dronCacheDTO == null) {
// STEP 1. Control ID 발급 -> Application Server Http 통신
try {
// TODO 분리
HttpRequest request = HttpRequest.newBuilder()
.uri(new URI(
kacAppHost + "/api/ctr/cntrl/id/" + dron.getObjectId()
))
.version(HttpClient.Version.HTTP_2)
.GET()
.build();
HttpResponse<String> response = HttpClient
.newHttpClient()
.send(request, HttpResponse.BodyHandlers.ofString());
log.debug("CONTROL ID RESPONSE : {}", response.body());
Map body = ObjectMapperUtils.getObjectMapper().readValue(response.body(), HashMap.class);
Map data = (Map) body.get("data");
dron.setControlId((String) data.get("controlId"));
dron.setTypeCd((String) data.get("typeCd"));
dron.setAreaTrnsYn((String) data.get("areaTrnsYn"));
dron.setControlStartDt(Instant.now());
ControlDto controlDto = new ControlDto();
controlDto.setControlId((String) data.get("controlId"));
controlDto.setTypeCd((String) data.get("typeCd"));
controlDto.setAreaTrnsYn((String) data.get("areaTrnsYn"));
controlDto.setRegTime(System.currentTimeMillis());
ControlDto newDronCacheDTO = ControlDto.builder().build();
controlCacheUtil.set(dron.getObjectId(), newDronCacheDTO);
} catch (IOException e) {
log.error("요청한 URL 정보가 잘못되었습니다.", e.getMessage());
} catch (Exception e) {
e.printStackTrace();
}
} else {
dron.setControlId(dronCacheDTO.getControlId());
dron.setTypeCd("02");
dron.setAreaTrnsYn(dronCacheDTO.getAreaTrnsYn());
dron.setControlWarnCd(dronCacheDTO.isControlWarnCd());
dronCacheDTO.setRegTime(System.currentTimeMillis());
controlCacheUtil.set(dron.getObjectId(), dronCacheDTO);
}
// STEP 2. 이력 생성할 전문 전달 -> DRON의 대한 식별정보만 이력 관리
try {
// 저장 해 놓았다가 한거번에 전송
DronCacheUtil dronCacheUtil = DronCacheUtil.getInstance();
dronCacheUtil.set(dron);
} catch (Exception e) {
log.error("ERROR : {}\n{}", e.getMessage(), e.getStackTrace());
}
// STEP 3. 화면에 표출할 정보 WebSocket 전달
if ("PA".equals(dron.getObjectId().substring(0, 2))) {
webSocketService.sendData(dron);
}
} else {
log.error("좌표 정보가 존재하지 않습니다.");
throw new IllegalArgumentException("좌표 정보가 존재하지 않습니다.");
}
}
}
}

159
app/kac-socket-app/src/main/java/kr/co/palnet/kac/socket/core/command/impl/AntosDronCommandImpl.java

@ -0,0 +1,159 @@
package kr.co.palnet.kac.socket.core.command.impl;
import kr.co.palnet.kac.socket.core.model.ControlDto;
import kr.co.palnet.kac.socket.core.model.DronDTO;
import kr.co.palnet.kac.socket.core.model.DronHistoryDTO;
import kr.co.palnet.kac.socket.core.model.DronRQ;
import kr.co.palnet.kac.socket.core.command.DronCommand;
import kr.co.palnet.kac.socket.core.util.ControlCacheUtil;
import kr.co.palnet.kac.socket.core.util.DronCacheUtil;
import kr.co.palnet.kac.socket.core.util.DronUtil;
import kr.co.palnet.kac.socket.service.KacAppService;
import kr.co.palnet.kac.socket.service.WebSocketService;
import kr.co.palnet.kac.util.ObjectMapperUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Instant;
import java.util.*;
@Slf4j
@RequiredArgsConstructor
@Component
public class AntosDronCommandImpl implements DronCommand {
private final WebSocketService webSocketService;
private final KacAppService kacAppService;
@Value("${app.kac-app.host}")
private String kacAppHost;
@Override
public String getCommandName() {
return "ANTOS";
}
@Override
public void command(DronRQ rq) {
log.info(">>>> AntosCommandServiceImpl <<<<<");
List<DronDTO> resultList = rq.getBody();
/** 전문 설정 ***/
final String messageType = "LTEM";
final String objectType = "DRON";
/** 데이터 모델링 **/
for (DronDTO dron : resultList) {
// 위,경도 좌표가 0으로 들어오는 것은 무시 처리
if (DronUtil.latlonCheck(dron.getLat(), dron.getLon())) {
dron.setObjectType(objectType);
dron.setMessageType(messageType);
dron.setTerminalId(rq.getTerminalId());
dron.setRegDt(Instant.now());
// 서버 수신 시간 정보
dron.setServerRcvDt(Instant.now());
// 관제 이력 정보
List<DronHistoryDTO> hisList;
DronHistoryDTO history = DronHistoryDTO.builder()
.objectId(dron.getObjectId())
.lat(dron.getLat())
.lon(dron.getLon())
.build();
if (dron.getPostionHistory() != null) {
hisList = dron.getPostionHistory();
} else {
hisList = new ArrayList<>();
}
hisList.add(history);
dron.setPostionHistory(hisList);
// STEP 1. 전에 내부 메모리에서 controlId 조회
ControlCacheUtil controlCacheUtil = ControlCacheUtil.getInstance();
ControlDto dronCacheDTO = controlCacheUtil.get(dron.getObjectId());
if (dronCacheDTO == null) {
// STEP 1. Control ID 발급 -> Application Server Http 통신
try {
HttpRequest request = HttpRequest.newBuilder()
.uri(new URI(
kacAppHost + "/api/ctr/cntrl/id/" + dron.getObjectId()
))
.version(HttpClient.Version.HTTP_2)
.GET()
.build();
HttpResponse<String> response = HttpClient
.newHttpClient()
.send(request, HttpResponse.BodyHandlers.ofString());
log.debug("CONTROL ID RESPONSE : {}", response.body());
Map body = ObjectMapperUtils.getObjectMapper().readValue(response.body(), HashMap.class);
Map data = (Map) body.get("data");
dron.setControlId((String) data.get("controlId"));
dron.setTypeCd((String) data.get("typeCd"));
dron.setAreaTrnsYn((String) data.get("areaTrnsYn"));
dron.setControlStartDt(Instant.now());
ControlDto controlDto = new ControlDto();
controlDto.setControlId((String) data.get("controlId"));
controlDto.setTypeCd((String) data.get("typeCd"));
controlDto.setAreaTrnsYn((String) data.get("areaTrnsYn"));
controlDto.setRegTime(System.currentTimeMillis());
ControlDto newDronCacheDTO = ControlDto.builder().build();
controlCacheUtil.set(dron.getObjectId(), newDronCacheDTO);
} catch (IOException e) {
log.error("요청한 URL 정보가 잘못되었습니다.", e.getMessage());
} catch (Exception e) {
e.printStackTrace();
}
} else {
dron.setControlId(dronCacheDTO.getControlId());
dron.setTypeCd("02");
dron.setAreaTrnsYn(dronCacheDTO.getAreaTrnsYn());
dron.setControlWarnCd(dronCacheDTO.isControlWarnCd());
dronCacheDTO.setRegTime(System.currentTimeMillis());
controlCacheUtil.set(dron.getObjectId(), dronCacheDTO);
}
// STEP 2. 이력 생성할 전문 전달 -> DRON의 대한 식별정보만 이력 관리
try {
// 저장 해 놓았다가 한거번에 전송
DronCacheUtil dronCacheUtil = DronCacheUtil.getInstance();
dronCacheUtil.set(dron);
} catch (Exception e) {
log.error("ERROR : {}\n{}", e.getMessage(), e.getStackTrace());
}
// STEP 3. 화면에 표출할 정보 WebSocket 전달
if ("PA".equals(dron.getObjectId().substring(0, 2))) {
webSocketService.sendData(dron);
}
} else {
log.error("좌표 정보가 존재하지 않습니다.");
throw new IllegalArgumentException("좌표 정보가 존재하지 않습니다.");
}
}
}
}

166
app/kac-socket-app/src/main/java/kr/co/palnet/kac/socket/core/command/impl/SandboxDronCommandImpl.java

@ -0,0 +1,166 @@
package kr.co.palnet.kac.socket.core.command.impl;
import kr.co.palnet.kac.socket.core.command.DronCommand;
import kr.co.palnet.kac.socket.core.model.ControlDto;
import kr.co.palnet.kac.socket.core.model.DronDTO;
import kr.co.palnet.kac.socket.core.model.DronHistoryDTO;
import kr.co.palnet.kac.socket.core.model.DronRQ;
import kr.co.palnet.kac.socket.core.util.ControlCacheUtil;
import kr.co.palnet.kac.socket.core.util.DronCacheUtil;
import kr.co.palnet.kac.socket.core.util.DronUtil;
import kr.co.palnet.kac.socket.service.KacAppService;
import kr.co.palnet.kac.socket.service.WebSocketService;
import kr.co.palnet.kac.util.ObjectMapperUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Instant;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Slf4j
@RequiredArgsConstructor
@Component
public class SandboxDronCommandImpl implements DronCommand {
private final WebSocketService webSocketService;
private final KacAppService kacAppService;
@Value("${app.kac-app.host}")
private String kacAppHost;
@Override
public String getCommandName() {
return "SANDBOX";
}
@Override
public void command(DronRQ rq) {
log.info(">>>> SandboxCommandServiceImpl <<<<<");
List<DronDTO> resultList = rq.getBody();
/** 전문 설정 ***/
final String messageType = "LTEM";
final String objectType = "DRON";
/** 데이터 모델링 **/
for (DronDTO dron : resultList) {
// 위,경도 좌표가 0으로 들어오는 것은 무시 처리
if (DronUtil.latlonCheck(dron.getLat(), dron.getLon())) {
dron.setObjectType(objectType);
dron.setMessageType(messageType);
dron.setTerminalId(rq.getTerminalId());
dron.setRegDt(Instant.now());
// 서버 수신 시간 정보
dron.setServerRcvDt(Instant.now());
// 관제 이력 정보
List<DronHistoryDTO> hisList;
DronHistoryDTO history = DronHistoryDTO.builder()
.objectId(dron.getObjectId())
.lat(dron.getLat())
.lon(dron.getLon())
.build();
if (dron.getPostionHistory() != null) {
hisList = dron.getPostionHistory();
} else {
hisList = new ArrayList<>();
}
hisList.add(history);
dron.setPostionHistory(hisList);
Long start = System.currentTimeMillis();
// STEP 1. 전에 내부 메모리에서 controlId 조회
ControlCacheUtil controlCacheUtil = ControlCacheUtil.getInstance();
ControlDto dronCacheDTO = controlCacheUtil.get(dron.getObjectId());
if (dronCacheDTO == null) {
// STEP 1. Control ID 발급 -> Application Server Http 통신
try {
// TODO 분리
HttpRequest request = HttpRequest.newBuilder()
.uri(new URI(
kacAppHost + "/api/ctr/cntrl/id/" + dron.getObjectId()
))
.version(HttpClient.Version.HTTP_2)
.GET()
.build();
HttpResponse<String> response = HttpClient
.newHttpClient()
.send(request, HttpResponse.BodyHandlers.ofString());
log.debug("CONTROL ID RESPONSE : {}", response.body());
Map body = ObjectMapperUtils.getObjectMapper().readValue(response.body(), HashMap.class);
Map data = (Map) body.get("data");
dron.setControlId((String) data.get("controlId"));
dron.setTypeCd((String) data.get("typeCd"));
dron.setAreaTrnsYn((String) data.get("areaTrnsYn"));
dron.setControlStartDt(Instant.now());
ControlDto controlDto = new ControlDto();
controlDto.setControlId((String) data.get("controlId"));
controlDto.setTypeCd((String) data.get("typeCd"));
controlDto.setAreaTrnsYn((String) data.get("areaTrnsYn"));
controlDto.setRegTime(System.currentTimeMillis());
ControlDto newDronCacheDTO = ControlDto.builder().build();
controlCacheUtil.set(dron.getObjectId(), newDronCacheDTO);
} catch (IOException e) {
log.error("요청한 URL 정보가 잘못되었습니다.", e.getMessage());
} catch (Exception e) {
e.printStackTrace();
}
} else {
dron.setControlId(dronCacheDTO.getControlId());
dron.setTypeCd("02");
dron.setAreaTrnsYn(dronCacheDTO.getAreaTrnsYn());
dron.setControlWarnCd(dronCacheDTO.isControlWarnCd());
dronCacheDTO.setRegTime(System.currentTimeMillis());
controlCacheUtil.set(dron.getObjectId(), dronCacheDTO);
}
// STEP 2. 이력 생성할 전문 전달 -> DRON의 대한 식별정보만 이력 관리
try {
// 저장 해 놓았다가 한거번에 전송
DronCacheUtil dronCacheUtil = DronCacheUtil.getInstance();
// dronCacheUtil.set(dron);
} catch (Exception e) {
log.error("ERROR : {}\n{}", e.getMessage(), e.getStackTrace());
}
// STEP 3. 화면에 표출할 정보 WebSocket 전달
// webSocketService.sendData(dron);
Long end = System.currentTimeMillis();
log.info(">>> during time : {}::{}", dron.getObjectId(),end - start);
} else {
log.error("좌표 정보가 존재하지 않습니다.");
throw new IllegalArgumentException("좌표 정보가 존재하지 않습니다.");
}
}
}
}

107
app/kac-socket-app/src/main/java/kr/co/palnet/kac/socket/core/handler/DronSocketHandler.java

@ -1,44 +1,109 @@
package kr.co.palnet.kac.socket.core.handler;
import io.netty.buffer.ByteBuf;
import io.netty.channel.*;
import kr.co.palnet.kac.socket.core.codec.DronPayload;
import lombok.RequiredArgsConstructor;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import kr.co.palnet.kac.socket.core.command.DronCommand;
import kr.co.palnet.kac.socket.core.model.DronRQ;
import kr.co.palnet.kac.socket.core.model.DronRS;
import kr.co.palnet.kac.socket.core.util.AuthKeyUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Slf4j
@RequiredArgsConstructor
@ChannelHandler.Sharable
@Component
public class DronSocketHandler extends SimpleChannelInboundHandler<DronPayload> {
public class DronSocketHandler extends SimpleChannelInboundHandler<DronRQ> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, DronPayload msg) throws Exception {
log.info(">>>> channelRead0 <<<<<");
log.info("Received0 JSON: " + msg.toString());
ctx.writeAndFlush(msg);
private final Map<String, DronCommand> commandMap;
public DronSocketHandler(List<DronCommand> commandList) {
commandMap = new HashMap<>();
for (DronCommand command : commandList) {
commandMap.put(command.getCommandName(), command);
}
}
// 클라이언트와 연결되어 트래픽을 생성할 준비가 되었을 때 호출되는 메소드
//
@Override
public void channelActive(ChannelHandlerContext ctx) {
log.info(">>>> channelActive <<<<<");
String remoteAddress = ctx.channel().remoteAddress().toString();
log.info("Remote Address: " + remoteAddress);
// ctx.writeAndFlush("success");
}
protected void channelRead0(ChannelHandlerContext ctx, DronRQ rq) throws Exception {
log.info(">>>> channelRead0 <<<<<");
// key 검사
if (rq == null) {
ctx.writeAndFlush(DronRS.builder()
.code("-1000")
.message("data is empty.")
.build());
return;
}
if (rq.getCommand() == null || rq.getCommand().isEmpty()) {
ctx.writeAndFlush(DronRS.builder()
.code("-1001")
.message("command is empty.")
.build());
return;
}
if (rq.getAuthKey() == null || rq.getAuthKey().isEmpty()) {
ctx.writeAndFlush(DronRS.builder()
.code("-2000")
.message("auth key is empty.")
.build());
return;
}
if (AuthKeyUtil.getInstance().checkAuthKey(rq.getCommand())) {
ctx.writeAndFlush(DronRS.builder()
.code("-2000")
.message("Invalid auth key.")
.build());
return;
}
log.info(">>> rq : {}", rq);
DronCommand commandService = commandMap.get(rq.getCommand().trim());
if (commandService == null) {
ctx.writeAndFlush(DronRS.builder()
.code("-2100")
.message("this command is not exist.")
.build());
return;
}
try {
commandService.command(rq);
} catch (Exception e) {
ctx.writeAndFlush(DronRS.builder()
.code("-500")
.message("server error - execute command")
.build());
return;
}
ctx.writeAndFlush(DronRS.builder()
.code("0")
.message("success")
.build());
}
// 에러 처리
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
log.info(">>>> exceptionCaught <<<<<");
DronRS rs = DronRS.builder()
.code("-9999")
.message("error - etc")
.build();
ctx.writeAndFlush(rs);
// Close the connection when an exception is raised.
ctx.close();
cause.printStackTrace();
log.error("ERROR: {}\n{}", cause.getMessage(), cause.getStackTrace());
}
}

18
app/kac-socket-app/src/main/java/kr/co/palnet/kac/socket/core/model/ControlDto.java

@ -0,0 +1,18 @@
package kr.co.palnet.kac.socket.core.model;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class ControlDto {
private String controlId;
private String typeCd;
private String areaTrnsYn;
private boolean controlWarnCd;
private Long regTime;
}

97
app/kac-socket-app/src/main/java/kr/co/palnet/kac/socket/core/model/DronDTO.java

@ -0,0 +1,97 @@
package kr.co.palnet.kac.socket.core.model;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.Instant;
import java.time.LocalDateTime;
import java.util.List;
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class DronDTO {
private String typeCd; // 01 : 최초 들어온 데이터 , 99 : 종료 시킬 데이터
private String messageType;
private String terminalId;
@Builder.Default
private Double moveDistance = 0.0;
private String moveDistanceType;
private String controlId; // 처음 위치 데이터가 들어 왔을때 생성 함
private String objectType;
private String objectId;
@Builder.Default
private Double lat = 0.0;
@Builder.Default
private Double lon = 0.0;
private String elevType;
@Builder.Default
private Double elev = 0.0;
private String speedType;
@Builder.Default
private Double speed = 0.0;
@Builder.Default
private Double betteryLevel = 0.0;
@Builder.Default
private Double betteryVoltage = 0.0;
private String dronStatus;
@Builder.Default
private Double heading = 0.0;
private String terminalRcvDt;
private Instant serverRcvDt;
private Instant controlStartDt;
private Instant controlEndDt;
private String areaTrnsYn;
// 환경센서 필드
@Builder.Default
private Double sensorCo = 0.0;
@Builder.Default
private Double sensorSo2 = 0.0;
@Builder.Default
private Double sensorNo2 = 0.0;
@Builder.Default
private Double sensorO3 = 0.0;
@Builder.Default
private Double sensorDust = 0.0;
//최근 5건만 저장
private List<DronHistoryDTO> recentPositionHistory;
// 전체 히스토리 저장
private List<DronHistoryDTO> postionHistory;
// 비정상 상황 식별 코드
private boolean controlWarnCd;
// 큐가 Socket서버에 도착한 시간
private Instant regDt;
// 큐가 Socket서버에 도착한 시간
private boolean sendUtm; // 불법드론 전송 여부
}

20
app/kac-socket-app/src/main/java/kr/co/palnet/kac/socket/core/model/DronHistoryDTO.java

@ -0,0 +1,20 @@
package kr.co.palnet.kac.socket.core.model;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class DronHistoryDTO {
private String objectId;
@Builder.Default
private Double lat = 0.0;
@Builder.Default
private Double lon = 0.0;
}

8
app/kac-socket-app/src/main/java/kr/co/palnet/kac/socket/core/codec/DronPayload.java → app/kac-socket-app/src/main/java/kr/co/palnet/kac/socket/core/model/DronRQ.java

@ -1,15 +1,17 @@
package kr.co.palnet.kac.socket.core.codec;
package kr.co.palnet.kac.socket.core.model;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class DronPayload {
public class DronRQ {
private String authKey;
@ -17,6 +19,6 @@ public class DronPayload {
private String command;
private Object body;
private List<DronDTO> body;
}

15
app/kac-socket-app/src/main/java/kr/co/palnet/kac/socket/core/model/DronRS.java

@ -0,0 +1,15 @@
package kr.co.palnet.kac.socket.core.model;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class DronRS {
private String code;
private String message;
}

22
app/kac-socket-app/src/main/java/kr/co/palnet/kac/socket/core/socket/DronChannelInitializer.java

@ -7,8 +7,8 @@ import io.netty.handler.codec.json.JsonObjectDecoder;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.util.CharsetUtil;
import kr.co.palnet.kac.socket.core.codec.DronPayloadDecorder;
import kr.co.palnet.kac.socket.core.codec.DronPayloadEncorder;
import kr.co.palnet.kac.socket.core.codec.DronDecoder;
import kr.co.palnet.kac.socket.core.codec.DronEncoder;
import kr.co.palnet.kac.socket.core.handler.DronSocketHandler;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@ -19,7 +19,7 @@ import org.springframework.stereotype.Component;
@Component
public class DronChannelInitializer extends ChannelInitializer<SocketChannel> {
// private final DronSocketHandler dronSocketHandler;
private final DronSocketHandler dronSocketHandler;
// 클라이언트 소켓 채널이 생성될 때 호출
@Override
@ -27,14 +27,14 @@ public class DronChannelInitializer extends ChannelInitializer<SocketChannel> {
log.info(">>>>> initChannel <<<<<");
ChannelPipeline pipeline = ch.pipeline();
// decoder는 @Sharable이 안 됨, Bean 객체 주입이 안 되고, 매번 새로운 객체 생성해야 함
DronPayloadDecorder sampleDecoder = new DronPayloadDecorder();
// 뒤이어 처리할 디코더 및 핸들러 추가
pipeline.addLast(new JsonObjectDecoder());
pipeline.addLast(new StringDecoder(CharsetUtil.UTF_8), new StringEncoder(CharsetUtil.UTF_8));
pipeline.addLast(new DronPayloadDecorder(), new DronPayloadEncorder());
pipeline.addLast(new DronSocketHandler());
// pipeline.addLast(dronSocketHandler);
pipeline.addLast("json-decoder", new JsonObjectDecoder());
pipeline.addLast("string-decoder", new StringDecoder(CharsetUtil.UTF_8));
pipeline.addLast("string-encoder", new StringEncoder(CharsetUtil.UTF_8));
pipeline.addLast("dron-decoder", new DronDecoder());
pipeline.addLast("dron-encoder", new DronEncoder());
pipeline.addLast("dron-handler", dronSocketHandler);
}
}

59
app/kac-socket-app/src/main/java/kr/co/palnet/kac/socket/core/util/AuthKeyUtil.java

@ -0,0 +1,59 @@
package kr.co.palnet.kac.socket.core.util;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import java.util.HashSet;
import java.util.Set;
@Slf4j
public class AuthKeyUtil {
private Set<String> keys = new HashSet<>();
private AuthKeyUtil() {
log.debug("AuthKeyUtil : init keys");
initKey();
}
public static AuthKeyUtil getInstance() {
return LazyHolder.INSTANCE;
}
public static class LazyHolder {
private static final AuthKeyUtil INSTANCE = new AuthKeyUtil();
}
private void initKey() {
keys.add("1cc2e08e-0c5c-43b2-8d4d-cddd3de558e3");// 지텔인증키
keys.add("35ea4080-a3f2-4e34-8361-78db06bac6fc");// PAL 인증키
keys.add("4d244c0a-6cf5-4d57-ae48-331a71010c3d");// ADS-B
}
public boolean checkAuthKey(String authKey) {
// keys에서 authKey와 동일한 값이 잇으면 true반환
return keys.contains(authKey);
}
public boolean addKey(String authKey) {
try {
return keys.add(authKey);
} catch (Exception e) {
log.debug("fail add key.");
return false;
}
}
public boolean removeKey(String authKey) {
try {
if (keys.contains(authKey)) {
return keys.remove(authKey);
}
} catch (Exception e) {
log.debug("fail remove key.");
return false;
}
return false;
}
}

53
app/kac-socket-app/src/main/java/kr/co/palnet/kac/socket/core/util/ControlCacheUtil.java

@ -0,0 +1,53 @@
package kr.co.palnet.kac.socket.core.util;
import kr.co.palnet.kac.socket.core.model.ControlDto;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@Slf4j
public class ControlCacheUtil {
private final Map<String, ControlDto> controlMap;
private final int REMOVE_TIME = 1000 * 10;
private ControlCacheUtil() {
controlMap = new ConcurrentHashMap<>();
}
public static ControlCacheUtil getInstance() {
return LazyHolder.INSTANCE;
}
public static class LazyHolder {
private static final ControlCacheUtil INSTANCE = new ControlCacheUtil();
}
public ControlDto get(String objectId) {
return controlMap.get(objectId);
}
public ControlDto set(String objectId, ControlDto control) {
return controlMap.put(objectId, control);
}
public ControlDto remove(String objectId) {
return controlMap.remove(objectId);
}
// TODO scheduler 테스트 필요(현재 Bean등록 안함)
@Scheduled(fixedDelay = 1000 * 10)
public void remove() {
for (String key : controlMap.keySet()) {
ControlDto dronCacheDTO = controlMap.get(key);
long diff = System.currentTimeMillis() - dronCacheDTO.getRegTime();
if(diff > REMOVE_TIME){
remove(key);
}
}
}
}

84
app/kac-socket-app/src/main/java/kr/co/palnet/kac/socket/core/util/DronCacheUtil.java

@ -0,0 +1,84 @@
package kr.co.palnet.kac.socket.core.util;
import kr.co.palnet.kac.socket.core.model.ControlDto;
import kr.co.palnet.kac.socket.core.model.DronDTO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import java.time.Instant;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@Slf4j
public class DronCacheUtil {
private final Map<String, List<DronDTO>> dronMap;
private final int REMOVE_TIME = 1000 * 10;
private DronCacheUtil() {
dronMap = new ConcurrentHashMap<>();
}
public static DronCacheUtil getInstance() {
return LazyHolder.INSTANCE;
}
public static class LazyHolder {
private static final DronCacheUtil INSTANCE = new DronCacheUtil();
}
public Map<String, List<DronDTO>> getAll() {
if (dronMap.keySet().isEmpty()) {
return null;
}
return dronMap;
}
public List<DronDTO> get(String objectId) {
if (objectId == null || objectId.isEmpty()) {
return null;
}
if (dronMap.get(objectId) == null) {
return null;
}
return dronMap.get(objectId);
}
public void set(DronDTO dron) {
if (dron == null || dron.getObjectId() == null || dron.getObjectId().isEmpty()) {
return;
}
List<DronDTO> list = dronMap.get(dron.getObjectId());
if (list == null) {
list = new ArrayList<>();
}
list.add(dron);
dronMap.put(dron.getObjectId(), list);
}
public void removeScheduled() {
log.info("removeScheduled start - dronMap size : {}", dronMap.size());
for (String key : dronMap.keySet()) {
List<DronDTO> list = dronMap.get(key);
if (list == null || list.isEmpty()) {
continue;
}
DronDTO dron = list.get(list.size() - 1);
// 1분 이상된 데이터 삭제
Instant compareTime = Instant.now().minusSeconds(60);
if (compareTime.isAfter(dron.getRegDt())) {
// log.info("remove data - 1 munite over : {}", key);
dronMap.remove(key);
}
}
log.info("removeScheduled end - dronMap size : {}", dronMap.size());
}
}

10
app/kac-socket-app/src/main/java/kr/co/palnet/kac/socket/core/util/DronUtil.java

@ -0,0 +1,10 @@
package kr.co.palnet.kac.socket.core.util;
public class DronUtil {
public static boolean latlonCheck(double lat, double lon) {
if (lat > 32 && lat < 44 && lon > 124 && lon < 133) {
return true;
}
return false;
}
}

15
app/kac-socket-app/src/main/java/kr/co/palnet/kac/socket/service/KacAppService.java

@ -0,0 +1,15 @@
package kr.co.palnet.kac.socket.service;
import kr.co.palnet.kac.socket.core.model.DronDTO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@Slf4j
@RequiredArgsConstructor
@Service
public class KacAppService {
public void sendData(DronDTO dto) {
}
}

17
app/kac-socket-app/src/main/java/kr/co/palnet/kac/socket/service/WebSocketService.java

@ -0,0 +1,17 @@
package kr.co.palnet.kac.socket.service;
import kr.co.palnet.kac.socket.core.model.DronDTO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@Slf4j
@RequiredArgsConstructor
@Service
public class WebSocketService {
public void sendData(DronDTO dto) {
}
}

13
app/kac-socket-app/src/main/resources/application.yml

@ -1,15 +1,24 @@
netty:
socket:
tcp-port: 8090
tcp-port: 8200
boss-count: 1
worker-count: 10
keep-alive: false
backlog: 100
app:
kac-app:
host: http://127.0.0.1:8000
web-socket:
host: http://127.0.0.1:8100
---
spring:
config:
activate:
on-profile: local
on-profile: local

Loading…
Cancel
Save