diff --git a/app/kac-app/src/main/resources/application.yml b/app/kac-app/src/main/resources/application.yml index a488838..eeee4e6 100644 --- a/app/kac-app/src/main/resources/application.yml +++ b/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 \ No newline at end of file + type.descriptor.sql.BasicBinder: TRACE diff --git a/app/kac-socket-app/src/main/java/kr/co/palnet/kac/socket/TestController.java b/app/kac-socket-app/src/main/java/kr/co/palnet/kac/socket/TestController.java new file mode 100644 index 0000000..aa71d95 --- /dev/null +++ b/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"; + } +} diff --git a/app/kac-socket-app/src/main/java/kr/co/palnet/kac/socket/core/codec/DronPayloadDecorder.java b/app/kac-socket-app/src/main/java/kr/co/palnet/kac/socket/core/codec/DronDecoder.java similarity index 71% rename from app/kac-socket-app/src/main/java/kr/co/palnet/kac/socket/core/codec/DronPayloadDecorder.java rename to app/kac-socket-app/src/main/java/kr/co/palnet/kac/socket/core/codec/DronDecoder.java index bbab1e2..ae23f77 100644 --- a/app/kac-socket-app/src/main/java/kr/co/palnet/kac/socket/core/codec/DronPayloadDecorder.java +++ b/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 { +public class DronDecoder extends MessageToMessageDecoder { // private int DATA_LENGTH = 100; private final ObjectMapper objectMapper = ObjectMapperUtils.getObjectMapper(); @@ -20,7 +21,10 @@ public class DronPayloadDecorder extends MessageToMessageDecoder { protected void decode(ChannelHandlerContext ctx, String in, List out) throws Exception { log.info(">>>>> decode <<<<<"); try { - DronPayload payload = objectMapper.readValue(in, DronPayload.class); +// TypeReference typeRef = new TypeReference() { +// }; +// DronRQ payload = objectMapper.readValue(in, typeRef); + DronRQ payload = objectMapper.readValue(in, DronRQ.class); if (payload != null) { out.add(payload); } diff --git a/app/kac-socket-app/src/main/java/kr/co/palnet/kac/socket/core/codec/DronPayloadEncorder.java b/app/kac-socket-app/src/main/java/kr/co/palnet/kac/socket/core/codec/DronEncoder.java similarity index 77% rename from app/kac-socket-app/src/main/java/kr/co/palnet/kac/socket/core/codec/DronPayloadEncorder.java rename to app/kac-socket-app/src/main/java/kr/co/palnet/kac/socket/core/codec/DronEncoder.java index 7648fbd..bbc705a 100644 --- a/app/kac-socket-app/src/main/java/kr/co/palnet/kac/socket/core/codec/DronPayloadEncorder.java +++ b/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 { +public class DronEncoder extends MessageToMessageEncoder { private final ObjectMapper objectMapper = ObjectMapperUtils.getObjectMapper(); @Override - protected void encode(ChannelHandlerContext ctx, DronPayload msg, List out) throws Exception { + protected void encode(ChannelHandlerContext ctx, DronRS msg, List out) throws Exception { log.info(">>>>> encode <<<"); try { String json = objectMapper.writeValueAsString(msg); diff --git a/app/kac-socket-app/src/main/java/kr/co/palnet/kac/socket/core/command/DronCommand.java b/app/kac-socket-app/src/main/java/kr/co/palnet/kac/socket/core/command/DronCommand.java new file mode 100644 index 0000000..e00b1ac --- /dev/null +++ b/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(); +} diff --git a/app/kac-socket-app/src/main/java/kr/co/palnet/kac/socket/core/command/impl/AdsbDronCommandImpl.java b/app/kac-socket-app/src/main/java/kr/co/palnet/kac/socket/core/command/impl/AdsbDronCommandImpl.java new file mode 100644 index 0000000..7e1e7af --- /dev/null +++ b/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 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 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 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("좌표 정보가 존재하지 않습니다."); + } + } + } + + +} diff --git a/app/kac-socket-app/src/main/java/kr/co/palnet/kac/socket/core/command/impl/AntosDronCommandImpl.java b/app/kac-socket-app/src/main/java/kr/co/palnet/kac/socket/core/command/impl/AntosDronCommandImpl.java new file mode 100644 index 0000000..dc8d91a --- /dev/null +++ b/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 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 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 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("좌표 정보가 존재하지 않습니다."); + } + } + } +} diff --git a/app/kac-socket-app/src/main/java/kr/co/palnet/kac/socket/core/command/impl/SandboxDronCommandImpl.java b/app/kac-socket-app/src/main/java/kr/co/palnet/kac/socket/core/command/impl/SandboxDronCommandImpl.java new file mode 100644 index 0000000..b69bcec --- /dev/null +++ b/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 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 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 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("좌표 정보가 존재하지 않습니다."); + } + } + } + +} diff --git a/app/kac-socket-app/src/main/java/kr/co/palnet/kac/socket/core/handler/DronSocketHandler.java b/app/kac-socket-app/src/main/java/kr/co/palnet/kac/socket/core/handler/DronSocketHandler.java index 62fc192..8dbe57d 100644 --- a/app/kac-socket-app/src/main/java/kr/co/palnet/kac/socket/core/handler/DronSocketHandler.java +++ b/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 { +public class DronSocketHandler extends SimpleChannelInboundHandler { - @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 commandMap; + + + public DronSocketHandler(List 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()); } - - } diff --git a/app/kac-socket-app/src/main/java/kr/co/palnet/kac/socket/core/model/ControlDto.java b/app/kac-socket-app/src/main/java/kr/co/palnet/kac/socket/core/model/ControlDto.java new file mode 100644 index 0000000..a086473 --- /dev/null +++ b/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; +} diff --git a/app/kac-socket-app/src/main/java/kr/co/palnet/kac/socket/core/model/DronDTO.java b/app/kac-socket-app/src/main/java/kr/co/palnet/kac/socket/core/model/DronDTO.java new file mode 100644 index 0000000..4c11d32 --- /dev/null +++ b/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 recentPositionHistory; + + // 전체 히스토리 저장 + private List postionHistory; + + // 비정상 상황 식별 코드 + private boolean controlWarnCd; + + // 큐가 Socket서버에 도착한 시간 + private Instant regDt; + // 큐가 Socket서버에 도착한 시간 + private boolean sendUtm; // 불법드론 전송 여부 + +} diff --git a/app/kac-socket-app/src/main/java/kr/co/palnet/kac/socket/core/model/DronHistoryDTO.java b/app/kac-socket-app/src/main/java/kr/co/palnet/kac/socket/core/model/DronHistoryDTO.java new file mode 100644 index 0000000..cf3ad62 --- /dev/null +++ b/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; +} diff --git a/app/kac-socket-app/src/main/java/kr/co/palnet/kac/socket/core/codec/DronPayload.java b/app/kac-socket-app/src/main/java/kr/co/palnet/kac/socket/core/model/DronRQ.java similarity index 68% rename from app/kac-socket-app/src/main/java/kr/co/palnet/kac/socket/core/codec/DronPayload.java rename to app/kac-socket-app/src/main/java/kr/co/palnet/kac/socket/core/model/DronRQ.java index ba9cec0..7f6da6b 100644 --- a/app/kac-socket-app/src/main/java/kr/co/palnet/kac/socket/core/codec/DronPayload.java +++ b/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 body; } diff --git a/app/kac-socket-app/src/main/java/kr/co/palnet/kac/socket/core/model/DronRS.java b/app/kac-socket-app/src/main/java/kr/co/palnet/kac/socket/core/model/DronRS.java new file mode 100644 index 0000000..c0d56f5 --- /dev/null +++ b/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; +} diff --git a/app/kac-socket-app/src/main/java/kr/co/palnet/kac/socket/core/socket/DronChannelInitializer.java b/app/kac-socket-app/src/main/java/kr/co/palnet/kac/socket/core/socket/DronChannelInitializer.java index 793f69e..bcec949 100644 --- a/app/kac-socket-app/src/main/java/kr/co/palnet/kac/socket/core/socket/DronChannelInitializer.java +++ b/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 { -// private final DronSocketHandler dronSocketHandler; + private final DronSocketHandler dronSocketHandler; // 클라이언트 소켓 채널이 생성될 때 호출 @Override @@ -27,14 +27,14 @@ public class DronChannelInitializer extends ChannelInitializer { 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); + } } diff --git a/app/kac-socket-app/src/main/java/kr/co/palnet/kac/socket/core/util/AuthKeyUtil.java b/app/kac-socket-app/src/main/java/kr/co/palnet/kac/socket/core/util/AuthKeyUtil.java new file mode 100644 index 0000000..82e5599 --- /dev/null +++ b/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 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; + } + +} diff --git a/app/kac-socket-app/src/main/java/kr/co/palnet/kac/socket/core/util/ControlCacheUtil.java b/app/kac-socket-app/src/main/java/kr/co/palnet/kac/socket/core/util/ControlCacheUtil.java new file mode 100644 index 0000000..53848f2 --- /dev/null +++ b/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 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); + } + } + } + +} \ No newline at end of file diff --git a/app/kac-socket-app/src/main/java/kr/co/palnet/kac/socket/core/util/DronCacheUtil.java b/app/kac-socket-app/src/main/java/kr/co/palnet/kac/socket/core/util/DronCacheUtil.java new file mode 100644 index 0000000..42e23a9 --- /dev/null +++ b/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> 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> getAll() { + if (dronMap.keySet().isEmpty()) { + return null; + } + return dronMap; + } + + public List 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 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 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()); + } + +} \ No newline at end of file diff --git a/app/kac-socket-app/src/main/java/kr/co/palnet/kac/socket/core/util/DronUtil.java b/app/kac-socket-app/src/main/java/kr/co/palnet/kac/socket/core/util/DronUtil.java new file mode 100644 index 0000000..69153b1 --- /dev/null +++ b/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; + } +} diff --git a/app/kac-socket-app/src/main/java/kr/co/palnet/kac/socket/service/KacAppService.java b/app/kac-socket-app/src/main/java/kr/co/palnet/kac/socket/service/KacAppService.java new file mode 100644 index 0000000..0e6dcd9 --- /dev/null +++ b/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) { + + } +} diff --git a/app/kac-socket-app/src/main/java/kr/co/palnet/kac/socket/service/WebSocketService.java b/app/kac-socket-app/src/main/java/kr/co/palnet/kac/socket/service/WebSocketService.java new file mode 100644 index 0000000..178e488 --- /dev/null +++ b/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) { + + } + +} diff --git a/app/kac-socket-app/src/main/resources/application.yml b/app/kac-socket-app/src/main/resources/application.yml index 991e6cb..950a48e 100644 --- a/app/kac-socket-app/src/main/resources/application.yml +++ b/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 \ No newline at end of file + on-profile: local + + +