Compare commits

...

5 Commits

  1. 22
      pom.xml
  2. 21
      src/main/java/com/palnet/Application.java
  3. 38
      src/main/java/com/palnet/SocketApplication.java
  4. 158
      src/main/java/com/palnet/comn/collection/GPCollection.java
  5. 2
      src/main/java/com/palnet/comn/model/GPModel.java
  6. 2
      src/main/java/com/palnet/comn/utils/ControlCacheUtils.java
  7. 22
      src/main/java/com/palnet/process/message/Receiver.java
  8. 86
      src/main/java/com/palnet/process/message/config/MessageConfig.java
  9. 71
      src/main/java/com/palnet/process/message/producer/MessageProducer.java
  10. 93
      src/main/java/com/palnet/server/command/SocketCommand.java
  11. 137
      src/main/java/com/palnet/server/handler/SocketHandler.java
  12. 132
      src/main/java/com/palnet/server/task/server/service/TaskServerService.java
  13. 109
      src/main/java/com/palnet/server/task/wb/service/TaskWbService.java
  14. 203
      src/main/resources/application.yml

22
pom.xml

@ -17,10 +17,6 @@
<java.version>11</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
@ -36,11 +32,15 @@
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.amqp</groupId>
<artifactId>spring-rabbit-test</artifactId>
<scope>test</scope>
</dependency>
<!-- <dependency>-->
<!-- <groupId>org.springframework.boot</groupId>-->
<!-- <artifactId>spring-boot-starter-amqp</artifactId>-->
<!-- </dependency>-->
<!-- <dependency>-->
<!-- <groupId>org.springframework.amqp</groupId>-->
<!-- <artifactId>spring-rabbit-test</artifactId>-->
<!-- <scope>test</scope>-->
<!-- </dependency>-->
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
@ -50,6 +50,10 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
</dependencies>
<build>

21
src/main/java/com/palnet/Application.java

@ -1,21 +0,0 @@
package com.palnet;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.annotation.EnableScheduling;
@EnableScheduling
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@Bean
public ObjectMapper objectMapper() {
return new ObjectMapper();
}
}

38
src/main/java/com/palnet/SocketApplication.java

@ -0,0 +1,38 @@
package com.palnet;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.Executor;
@EnableAsync
@EnableScheduling
@SpringBootApplication
public class SocketApplication {
public static void main(String[] args) {
SpringApplication.run(SocketApplication.class, args);
}
@Bean
public ObjectMapper objectMapper() {
return new ObjectMapper();
}
@Bean("asyncExecutor")
public Executor asyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(1); // 기본 스레드 수
executor.setMaxPoolSize(10); // 최대 스레드 수
executor.setQueueCapacity(50); // Max 스레드가 동작하는 경우 대기하는 Queue
executor.setThreadNamePrefix("Async-");
executor.initialize();
return executor;
}
}

158
src/main/java/com/palnet/comn/collection/GPCollection.java

@ -1,86 +1,106 @@
package com.palnet.comn.collection;
import com.palnet.comn.utils.ContextUtils;
import com.palnet.comn.utils.DateUtils;
import com.palnet.comn.utils.JsonUtils;
import com.palnet.process.message.producer.MessageProducer;
import com.palnet.comn.model.GPDatabaseModel;
import com.palnet.comn.model.GPHistoryModel;
import com.palnet.comn.model.GPModel;
import io.netty.channel.AbstractChannel;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.nio.charset.StandardCharsets;
import java.time.Instant;
import java.util.*;
/**
* 현재 움직이는 물체/비행체에 대한 정보를 전송하기 위해 저장하는 Collection
* @author kang
*
* @author kang
*/
@Slf4j
@Component
public class GPCollection {
private MessageProducer messageProducer;
public GPCollection() {
this.messageProducer = (MessageProducer) ContextUtils.getBean("messageProducer");
}
// 받은 데이터 정보를 데이터구조에 맞게 저장 한다.
// 최초에 데이터를 수신한 경우 관제 ID , 관제시작 시간을 셋팅한다.
public void putData(List<GPModel> paramData) {
List<GPHistoryModel> historyList;
for(GPModel data : paramData) {
data.setServerRcvDt(DateUtils.getCurrentTime()); //서버에서 받은 시간 넣기
// History Coordinates Settings
GPHistoryModel historyModel = new GPHistoryModel();
historyModel.setObjectId(data.getObjectId());
historyModel.setLat(data.getLat());
historyModel.setLng(data.getLng());
if (data.getPostionHistory() != null) {
historyList = data.getPostionHistory();
} else {
historyList = new ArrayList<>();
}
historyList.add(historyModel);
data.setPostionHistory(historyList);
/* Message Queue Server 전달 */
messageProducer.sendControlMessage(data);
try {
Socket socket = new Socket();
SocketAddress address = new InetSocketAddress("192.168.0.26", 4355);
socket.connect(address);
String gpsJson = JsonUtils.toJson(data);
OutputStream outputStream = socket.getOutputStream();
outputStream.write(gpsJson.getBytes(StandardCharsets.UTF_8));
outputStream.flush();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
private static Map<String, List<GPModel>> gpMap = Collections.synchronizedMap(new HashMap<>()); // 누적 GPModel key-objectId
public static void init() {
gpMap = Collections.synchronizedMap(new HashMap<>());
}
public static void init(String objectId) {
if (objectId == null || objectId.isEmpty()) {
return;
}
gpMap.remove(objectId);
}
public static Map<String, List<GPModel>> getAll() {
if (gpMap.keySet().size() < 1) {
return null;
}
return gpMap;
}
public static List<GPModel> get(String objectId) {
if (objectId == null || objectId.isEmpty()) {
return null;
}
if (gpMap.get(objectId) == null) {
return null;
}
return gpMap.get(objectId);
}
public static void set(GPModel model) {
if (model == null || model.getObjectId() == null || model.getObjectId().isEmpty()) {
return;
}
List<GPModel> list = gpMap.get(model.getObjectId());
if (list == null) {
list = new ArrayList<>();
}
list.add(model);
gpMap.put(model.getObjectId(), list);
}
public static List<GPModel> send(String objectId) {
if (objectId == null || objectId.isEmpty()) {
return null;
}
if (!gpMap.containsKey(objectId)) {
return null;
}
List<GPModel> list = gpMap.get(objectId);
gpMap.remove(objectId);
return list;
}
public static Map<String, List<GPModel>> sendAll() {
log.info("sendAll start - GPMap size : {}", gpMap.size());
if(gpMap.keySet().size() < 1) {
return null;
}
Map<String, List<GPModel>> map = gpMap;
GPCollection.init();
log.info("sendAll end - GPMap size : {}/{}", gpMap.size(), map.size());
return map;
}
// 1분마다 데이터 삭제
@Scheduled(fixedDelay = 1000 * 60)
public void removeSchedule() {
log.info("removeSchedule start - GPMap size : {}", gpMap.size());
for (String key : gpMap.keySet()) {
List<GPModel> list = gpMap.get(key);
if (list == null || list.size() == 0) {
continue;
}
GPModel model = list.get(list.size() - 1);
// 1분 이상된 데이터 삭제
Instant compareTime = Instant.now().minusSeconds(60);
if (compareTime.isAfter(model.getRegDt())) {
log.info("remove data - 1 munite over : {}", key);
gpMap.remove(key);
}
}
log.info("removeSchedule end - GPMap size : {}", gpMap.size());
}
}

2
src/main/java/com/palnet/comn/model/GPModel.java

@ -2,6 +2,7 @@ package com.palnet.comn.model;
import lombok.Data;
import java.time.Instant;
import java.util.List;
@Data
@ -68,5 +69,6 @@ public class GPModel {
// 비정상 상황 식별 코드
private boolean controlWarnCd;
private Instant regDt;
}

2
src/main/java/com/palnet/comn/utils/ControlCacheUtils.java

@ -6,8 +6,6 @@ import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import javax.naming.ldap.Control;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

22
src/main/java/com/palnet/process/message/Receiver.java

@ -1,22 +0,0 @@
package com.palnet.process.message;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.util.concurrent.CountDownLatch;
@Component
@Slf4j
public class Receiver {
private CountDownLatch latch = new CountDownLatch(1);
public void receiveMessage(String message) {
log.info("Received <" + message);
latch.countDown();
}
public CountDownLatch getLatch() {
return latch;
}
}

86
src/main/java/com/palnet/process/message/config/MessageConfig.java

@ -1,86 +0,0 @@
package com.palnet.process.message.config;
import com.palnet.process.message.Receiver;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
@Configuration
@Slf4j
public class MessageConfig {
private final Environment env;
public MessageConfig(Environment env) {
this.env = env;
}
@Bean
public Jackson2JsonMessageConverter converter() {
return new Jackson2JsonMessageConverter();
}
@Bean
public DirectExchange appDroneExchange() {
return new DirectExchange(env.getProperty("message.app.exchange-name"));
}
@Bean
public DirectExchange websocketDroneExchange() {
return new DirectExchange(env.getProperty("message.websocket.exchange-name"));
}
@Bean
public Queue appDroneQueue() {
return new Queue(env.getProperty("message.app.queue-name"), false);
}
@Bean
public Queue websocketDroneQueue() {
return new Queue(env.getProperty("message.websocket.queue-name"), false);
}
@Bean
public Binding appDroneBinding(Queue appDroneQueue, DirectExchange appDroneExchange) {
return BindingBuilder.bind(appDroneQueue)
.to(appDroneExchange)
.with(env.getProperty("message.app.routing-key"));
}
@Bean
public Binding websocketDroneBinding(Queue websocketDroneQueue, DirectExchange websocketDroneExchange) {
return BindingBuilder.bind(websocketDroneQueue)
.to(websocketDroneExchange)
.with(env.getProperty("message.websocket.routing-key"));
}
@Bean
public CachingConnectionFactory cachingConnectionFactory() {
CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
connectionFactory.setHost(env.getProperty("spring.rabbitmq.host"));
connectionFactory.setPort(Integer.parseInt(env.getProperty("spring.rabbitmq.port")));
connectionFactory.setUsername(env.getProperty("spring.rabbitmq.username"));
connectionFactory.setPassword(env.getProperty("spring.rabbitmq.password"));
return connectionFactory;
}
@Bean
public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory, Jackson2JsonMessageConverter converter) {
RabbitTemplate rabbitTemplate = new RabbitTemplate();
rabbitTemplate.setConnectionFactory(connectionFactory);
rabbitTemplate.setMessageConverter(converter);
return rabbitTemplate;
}
}

71
src/main/java/com/palnet/process/message/producer/MessageProducer.java

@ -1,71 +0,0 @@
package com.palnet.process.message.producer;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.palnet.comn.model.GPModel;
import com.palnet.process.message.Receiver;
import com.rabbitmq.client.Channel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
@Component
@Slf4j
public class MessageProducer {
private final RabbitTemplate rabbitTemplate;
private final Environment env;
private final ObjectMapper objectMapper;
private final Receiver receiver;
public MessageProducer(RabbitTemplate rabbitTemplate,
Environment env,
ObjectMapper objectMapper,
Receiver receiver) {
this.rabbitTemplate = rabbitTemplate;
this.env = env;
this.objectMapper = objectMapper;
this.receiver = receiver;
}
public void sendControlHistoryMessage(GPModel model) {
String json = "";
try {
json = objectMapper.writeValueAsString(model);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
// log.info("[APP MESSAGE] HISTORY DATA : {}" ,model);
rabbitTemplate.convertAndSend(
Objects.requireNonNull(env.getProperty("message.app.exchange-name")),
Objects.requireNonNull(env.getProperty("message.app.routing-key")),
json
);
}
public void sendControlMessage(GPModel model) {
String json = "";
try {
json = objectMapper.writeValueAsString(model);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
log.info("Socket Payload : {}", json);
rabbitTemplate.convertAndSend(
Objects.requireNonNull(env.getProperty("message.websocket.exchange-name")),
Objects.requireNonNull(env.getProperty("message.websocket.routing-key")),
json
);
}
}

93
src/main/java/com/palnet/server/command/SocketCommand.java

@ -8,27 +8,36 @@ import com.palnet.comn.utils.ContextUtils;
import com.palnet.comn.utils.ControlCacheUtils;
import com.palnet.comn.utils.DateUtils;
import com.palnet.comn.utils.JsonUtils;
import com.palnet.process.message.producer.MessageProducer;
import com.palnet.server.codec.SocketPayload;
import com.palnet.server.task.server.service.TaskServerService;
import com.palnet.server.task.wb.service.TaskWbService;
import com.sun.tools.javac.Main;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.env.Environment;
import org.springframework.core.task.TaskRejectedException;
import java.io.IOException;
import java.net.*;
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.time.LocalDateTime;
import java.util.*;
public class SocketCommand {
private Logger logger = LoggerFactory.getLogger(getClass());
private MessageProducer messageProducer;
// private MessageProducer messageProducer;
private TaskWbService taskWbService;
private TaskServerService taskServerService;
private Environment env;
public SocketCommand() {
this.messageProducer = (MessageProducer) ContextUtils.getBean("messageProducer");
// this.messageProducer = (MessageProducer) ContextUtils.getBean("messageProducer");
this.taskWbService = (TaskWbService) ContextUtils.getBean("taskWbService");
this.taskServerService = (TaskServerService) ContextUtils.getBean("taskServerService");
this.env = (Environment) ContextUtils.getBean("environment");
}
@ -38,20 +47,20 @@ public class SocketCommand {
* @param lat , lon
*/
public boolean latlonCheck(double lat, double lon) {
boolean Check = false;
if(lat > 32 && lat < 44 && lon > 124 && lon < 133) {
Check = true;
}
return Check;
boolean Check = false;
if (lat > 32 && lat < 44 && lon > 124 && lon < 133) {
Check = true;
}
return Check;
}
/**
* 들어온 데이터를 collection넣는 작업을 한다.
*
* @param payload
*/
public void sandBoxCommand(final SocketPayload payload) {
List<LinkedHashMap<?, ?>> resultList = (ArrayList) payload.getBody();
@ -62,7 +71,7 @@ public class SocketCommand {
/** 데이터 모델링 **/
for (LinkedHashMap<?, ?> obj : resultList) {
// 위,경도 좌표가 0으로 들어오는 것은 무시 처리
if (this.latlonCheck((double)obj.get("lat"), (double)obj.get("lon"))) {
if (this.latlonCheck((double) obj.get("lat"), (double) obj.get("lon"))) {
GPModel model = new GPModel();
model.setObjectType(objectType);
@ -73,6 +82,7 @@ public class SocketCommand {
model.setSpeedType((String) obj.get("speedType"));
model.setElevType((String) obj.get("elevType"));
model.setDronStatus((String) obj.get("dronStatus"));
model.setRegDt(Instant.now());
if (obj.get("lat") != null) model.setLat(Double.valueOf(obj.get("lat").toString()));
if (obj.get("lon") != null) model.setLng(Double.valueOf(obj.get("lon").toString()));
@ -109,7 +119,7 @@ public class SocketCommand {
try {
HttpRequest request = HttpRequest.newBuilder()
.uri(new URI(
env.getProperty("app.host") + "api/ctr/cntrl/id/" + model.getObjectId()
env.getProperty("app.host") + "/api/ctr/cntrl/id/" + model.getObjectId()
))
.version(HttpClient.Version.HTTP_2)
.GET()
@ -134,7 +144,7 @@ public class SocketCommand {
ccm.setTypeCd((String) data.get("typeCd"));
ccm.setAreaTrnsYn((String) data.get("areaTrnsYn"));
ccm.setRegTime(System.currentTimeMillis());
ControlCacheUtils.setControl(model.getObjectId(),ccm);
ControlCacheUtils.setControl(model.getObjectId(), ccm);
} catch (IOException e) {
logger.error("요청한 URL 정보가 잘못되었습니다.", e.getMessage());
@ -148,18 +158,30 @@ public class SocketCommand {
model.setAreaTrnsYn(control.getAreaTrnsYn());
model.setControlWarnCd(control.isControlWarnCd());
control.setRegTime(System.currentTimeMillis());
ControlCacheUtils.setControl(model.getObjectId(),control);
ControlCacheUtils.setControl(model.getObjectId(), control);
}
Long end = System.currentTimeMillis();
// Long end = System.currentTimeMillis();
// logger.info(">>> during time : {}::{}", model.getObjectId(),end - start);
// STEP 2. 이력 생성할 전문 전달 -> DRON의 대한 식별정보만 이력 관리
if (model.getObjectId().indexOf("PA") > -1) {
messageProducer.sendControlHistoryMessage(model);
// messageProducer.sendControlHistoryMessage(model);
try {
taskWbService.sendDataWebClient(model);
} catch (Exception e) {
logger.error("ERROR : {}\n{}", e.getMessage(), e.getStackTrace());
}
}
// STEP 3. 화면에 표출할 정보 WebSocket 전달
messageProducer.sendControlMessage(model);
// messageProducer.sendControlMessage(model);
try {
// taskServerService.sendData(model);
// taskServerService.sendDataWebClient(model);
GPCollection.set(model);
} catch (Exception e) {
logger.error("ERROR : {}\n{}", e.getMessage(), e.getStackTrace());
}
} else {
logger.error("좌표 정보가 존재하지 않습니다.");
@ -178,7 +200,7 @@ public class SocketCommand {
/** 데이터 모델링 **/
for (LinkedHashMap<?, ?> obj : resultList) {
// 위,경도 좌표가 0으로 들어오는 것은 무시 처리
if (this.latlonCheck((double)obj.get("lat"), (double)obj.get("lon"))) {
if (this.latlonCheck((double) obj.get("lat"), (double) obj.get("lon"))) {
GPModel model = new GPModel();
model.setObjectType(objectType);
@ -225,7 +247,7 @@ public class SocketCommand {
try {
HttpRequest request = HttpRequest.newBuilder()
.uri(new URI(
env.getProperty("app.host") + "api/ctr/cntrl/id/" + model.getObjectId()
env.getProperty("app.host") + "/api/ctr/cntrl/id/" + model.getObjectId()
))
.version(HttpClient.Version.HTTP_2)
.GET()
@ -250,7 +272,7 @@ public class SocketCommand {
ccm.setTypeCd((String) data.get("typeCd"));
ccm.setAreaTrnsYn((String) data.get("areaTrnsYn"));
ccm.setRegTime(System.currentTimeMillis());
ControlCacheUtils.setControl(model.getObjectId(),ccm);
ControlCacheUtils.setControl(model.getObjectId(), ccm);
} catch (IOException e) {
logger.error("요청한 URL 정보가 잘못되었습니다.", e.getMessage());
@ -264,16 +286,18 @@ public class SocketCommand {
model.setAreaTrnsYn(control.getAreaTrnsYn());
model.setControlWarnCd(control.isControlWarnCd());
control.setRegTime(System.currentTimeMillis());
ControlCacheUtils.setControl(model.getObjectId(),control);
ControlCacheUtils.setControl(model.getObjectId(), control);
}
// STEP 2. 이력 생성할 전문 전달 -> DRON의 대한 식별정보만 이력 관리
if ("PA".equals(model.getObjectId().substring(0, 2))) {
messageProducer.sendControlHistoryMessage(model);
// messageProducer.sendControlHistoryMessage(model);
taskWbService.sendData(model);
}
// STEP 3. 화면에 표출할 정보 WebSocket 전달
messageProducer.sendControlMessage(model);
// messageProducer.sendControlMessage(model);
taskServerService.sendData(model);
} else {
logger.error("좌표 정보가 존재하지 않습니다.");
@ -281,7 +305,7 @@ public class SocketCommand {
}
}
}
public void ANTOSCommand(final SocketPayload payload) {
List<LinkedHashMap<?, ?>> resultList = (ArrayList) payload.getBody();
@ -292,7 +316,7 @@ public class SocketCommand {
/** 데이터 모델링 **/
for (LinkedHashMap<?, ?> obj : resultList) {
// 위,경도 좌표가 0으로 들어오는 것은 무시 처리
if (this.latlonCheck((double)obj.get("lat"), (double)obj.get("lon"))) {
if (this.latlonCheck((double) obj.get("lat"), (double) obj.get("lon"))) {
GPModel model = new GPModel();
model.setObjectType(objectType);
@ -339,7 +363,7 @@ public class SocketCommand {
try {
HttpRequest request = HttpRequest.newBuilder()
.uri(new URI(
env.getProperty("app.host") + "api/ctr/cntrl/id/" + model.getObjectId()
env.getProperty("app.host") + "/api/ctr/cntrl/id/" + model.getObjectId()
))
.version(HttpClient.Version.HTTP_2)
.GET()
@ -364,7 +388,7 @@ public class SocketCommand {
ccm.setTypeCd((String) data.get("typeCd"));
ccm.setAreaTrnsYn((String) data.get("areaTrnsYn"));
ccm.setRegTime(System.currentTimeMillis());
ControlCacheUtils.setControl(model.getObjectId(),ccm);
ControlCacheUtils.setControl(model.getObjectId(), ccm);
} catch (IOException e) {
logger.error("요청한 URL 정보가 잘못되었습니다.", e.getMessage());
@ -378,21 +402,26 @@ public class SocketCommand {
model.setAreaTrnsYn(control.getAreaTrnsYn());
model.setControlWarnCd(control.isControlWarnCd());
control.setRegTime(System.currentTimeMillis());
ControlCacheUtils.setControl(model.getObjectId(),control);
ControlCacheUtils.setControl(model.getObjectId(), control);
}
// STEP 2. 이력 생성할 전문 전달 -> DRON의 대한 식별정보만 이력 관리
if ("PA".equals(model.getObjectId().substring(0, 2))) {
messageProducer.sendControlHistoryMessage(model);
// messageProducer.sendControlHistoryMessage(model);
taskWbService.sendData(model);
}
// STEP 3. 화면에 표출할 정보 WebSocket 전달
messageProducer.sendControlMessage(model);
// messageProducer.sendControlMessage(model);
taskServerService.sendData(model);
} else {
logger.error("좌표 정보가 존재하지 않습니다.");
throw new IllegalArgumentException("좌표 정보가 존재하지 않습니다.");
}
}
}
}

137
src/main/java/com/palnet/server/handler/SocketHandler.java

@ -5,87 +5,82 @@ import com.palnet.comn.utils.JsonUtils;
import com.palnet.server.codec.SocketPayload;
import com.palnet.server.codec.SocketPayloadResponse;
import com.palnet.server.command.SocketCommand;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;
public class SocketHandler extends SimpleChannelInboundHandler<SocketPayload> {
private Logger logger = LoggerFactory.getLogger(getClass());
private SocketCommand command = new SocketCommand();
private AuthCollection auth = new AuthCollection();
private SocketPayloadResponse res = new SocketPayloadResponse();
private Logger logger = LoggerFactory.getLogger(getClass());
private SocketCommand command = new SocketCommand();
private AuthCollection auth = new AuthCollection();
private SocketPayloadResponse res = new SocketPayloadResponse();
@Override
public void channelActive(ChannelHandlerContext ctx) {
auth.reloadAuthkey(); // 채널 활성화 되면 인증키 정보를 다시 불러옴
logger.debug("==================== [SocketHandler channelActive ] ==================== ");
auth.reloadAuthkey(); // 채널 활성화 되면 인증키 정보를 다시 불러옴
logger.debug("==================== [SocketHandler channelActive ] ==================== ");
ctx.writeAndFlush("Server Connection");
}
@Override
protected void channelRead0(ChannelHandlerContext ctx, SocketPayload payload){
try {
logger.debug("==================== [SocketHandler channelRead0 ] ==================== ");
if(payload.getAuthKey().isEmpty()) {
res.setRspCode("-2000");
res.setRspMessage("auth key is empty !");
} else if(!auth.checkAuthkey(payload.getAuthKey().trim())) {
res.setRspCode("-2000");
res.setRspMessage("Invalid auth key !");
} else {
logger.info("====> SOCKET BODY : {}", JsonUtils.toJson(payload));
switch (payload.getCommand().trim()) {
case "SANDBOX":
command.sandBoxCommand(payload);
break;
case "ADS-B":
command.ADSBCommand(payload);
case "ANTOS":
command.ANTOSCommand(payload);
break;
default:
break;
}
res.setRspCode("0");
res.setRspMessage("SUCCESS");
}
} catch(Exception e) {
res.setRspCode("-9999");
res.setRspMessage("Etc error");
e.printStackTrace();
} finally {
logger.debug("res >>>" + JsonUtils.toJson(res));
ctx.writeAndFlush(res);
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
logger.info("=========== [exceptionCaught ] ====================");
res.setRspCode("-9999");
res.setRspMessage("Etc error");
ctx.writeAndFlush(res);
cause.printStackTrace();
ctx.close();
}
@Override
protected void channelRead0(ChannelHandlerContext ctx, SocketPayload payload) {
try {
logger.debug("==================== [SocketHandler channelRead0 ] ==================== ");
if (payload.getAuthKey().isEmpty()) {
res.setRspCode("-2000");
res.setRspMessage("auth key is empty !");
} else if (!auth.checkAuthkey(payload.getAuthKey().trim())) {
res.setRspCode("-2000");
res.setRspMessage("Invalid auth key !");
} else {
logger.info("====> SOCKET BODY : {}", JsonUtils.toJson(payload));
switch (payload.getCommand().trim()) {
case "SANDBOX":
command.sandBoxCommand(payload);
break;
case "ADS-B":
command.ADSBCommand(payload);
case "ANTOS":
command.ANTOSCommand(payload);
break;
default:
break;
}
res.setRspCode("0");
res.setRspMessage("SUCCESS");
}
} catch (Exception e) {
res.setRspCode("-9999");
res.setRspMessage("Etc error");
e.printStackTrace();
} finally {
logger.debug("res >>>" + JsonUtils.toJson(res));
ctx.writeAndFlush(res);
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
logger.info("=========== [exceptionCaught ] ====================");
res.setRspCode("-9999");
res.setRspMessage("Etc error");
ctx.writeAndFlush(res);
logger.error("ERROR : {}\n{}", cause.getMessage(), cause.getStackTrace());
ctx.close();
}
}

132
src/main/java/com/palnet/server/task/server/service/TaskServerService.java

@ -0,0 +1,132 @@
package com.palnet.server.task.server.service;
import com.palnet.comn.collection.GPCollection;
import com.palnet.comn.model.GPModel;
import com.palnet.comn.utils.JsonUtils;
import io.netty.channel.ChannelOption;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
/**
* packageName : com.palnet.server.task.wb.service
* fileName : TaskWbService
* author : dhji
* date : 2023-08-28(028)
* description :
* ===========================================================
* DATE AUTHOR NOTE
* -----------------------------------------------------------
* 2023-08-28(028) dhji 최초 생성
*/
@Slf4j
@RequiredArgsConstructor
@Service
public class TaskServerService {
@Value("${app.host}")
private String APP_HOST;
private final String APP_SEND_ASYNC_URI = "/api/server/receiver/async";
private final String APP_SEND_URI = "/api/server/receiver";
private final String APP_SEND_ALL_URI = "/api/server/receiver/all";
public void sendData(GPModel model) {
HttpRequest request = null;
try {
request = HttpRequest.newBuilder()
.uri(new URI(
APP_HOST + APP_SEND_URI
))
.version(HttpClient.Version.HTTP_2)
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(JsonUtils.toJson(model)))
.build();
HttpResponse<String> response = HttpClient
.newHttpClient()
.send(request, HttpResponse.BodyHandlers.ofString());
} catch (URISyntaxException | InterruptedException | IOException e) {
log.error("요청한 URL 정보가 잘못되었습니다.", e.getMessage());
} catch (Exception e) {
log.error("ERROR : {}\n{}", e.getMessage(), e.getStackTrace());
}
// log.info("websocket send message : {}", JsonUtils.toJson(model));
}
@Async("asyncExecutor")
public CompletableFuture<Void> sendDataAsync(GPModel model) {
HttpRequest request = null;
try {
request = HttpRequest.newBuilder()
.uri(new URI(
APP_HOST + APP_SEND_ASYNC_URI
))
.version(HttpClient.Version.HTTP_2)
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(JsonUtils.toJson(model)))
.build();
HttpResponse<String> response = HttpClient
.newHttpClient()
.send(request, HttpResponse.BodyHandlers.ofString());
} catch (URISyntaxException | InterruptedException | IOException e) {
log.error("요청한 URL 정보가 잘못되었습니다.", e.getMessage());
} catch (Exception e) {
log.error("ERROR : {}\n{}", e.getMessage(), e.getStackTrace());
}
// log.info("websocket send message : {}", JsonUtils.toJson(model));
return CompletableFuture.completedFuture(null);
}
public void sendDataWebClient(GPModel model) {
WebClient client = WebClient.builder()
.baseUrl(APP_HOST)
.defaultHeader("Content-Type", "application/json")
.build();
client.post()
.uri(APP_SEND_URI)
.body(Mono.just(model), GPModel.class)
.retrieve()
.bodyToMono(Void.class).subscribe();
}
@Scheduled(fixedDelay = 1000 * 20)
public void sendDataAllWebClient() {
Map<String, List<GPModel>> all = GPCollection.sendAll();
if (all == null) {
return;
}
WebClient client = WebClient.builder()
.baseUrl(APP_HOST)
.defaultHeader("Content-Type", "application/json")
.build();
client.post()
.uri(APP_SEND_ALL_URI)
.body(Mono.just(all), Map.class)
.retrieve()
.bodyToMono(Void.class).subscribe();
}
}

109
src/main/java/com/palnet/server/task/wb/service/TaskWbService.java

@ -0,0 +1,109 @@
package com.palnet.server.task.wb.service;
import com.palnet.comn.model.GPModel;
import com.palnet.comn.utils.JsonUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;
import reactor.netty.http.client.HttpClientRequest;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
import java.util.concurrent.CompletableFuture;
/**
* packageName : com.palnet.server.task.wb.service
* fileName : TaskWbService
* author : dhji
* date : 2023-08-28(028)
* description :
* ===========================================================
* DATE AUTHOR NOTE
* -----------------------------------------------------------
* 2023-08-28(028) dhji 최초 생성
*/
@Slf4j
@RequiredArgsConstructor
@Service
public class TaskWbService {
@Value("${web-socket.host}")
private String WS_HOST;
private final String WS_SEND_ASYNC_URI = "/api/ws/receiver/async";
private final String WS_SEND_URI = "/api/ws/receiver";
@Async("asyncExecutor")
public CompletableFuture<Void> sendDataAsync(GPModel model) {
// log.info(">>> wb sendData model :: {}", model);
HttpRequest request = null;
try {
request = HttpRequest.newBuilder()
.uri(new URI(
WS_HOST + WS_SEND_ASYNC_URI
))
.version(HttpClient.Version.HTTP_2)
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(JsonUtils.toJson(model)))
.build();
HttpResponse<String> response = HttpClient
.newHttpClient()
.send(request, HttpResponse.BodyHandlers.ofString());
} catch (URISyntaxException | InterruptedException | IOException e) {
log.error("요청한 URL 정보가 잘못되었습니다.", e.getMessage());
} catch (Exception e) {
log.error("ERROR : {}\n{}", e.getMessage(), e.getStackTrace());
}
// log.info("websocket send message : {}", JsonUtils.toJson(model));
return CompletableFuture.completedFuture(null);
}
public void sendData(GPModel model) {
// log.info(">>> wb sendData model :: {}", model);
HttpRequest request = null;
try {
request = HttpRequest.newBuilder()
.uri(new URI(
WS_HOST + WS_SEND_URI
))
.version(HttpClient.Version.HTTP_2)
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(JsonUtils.toJson(model)))
.build();
HttpResponse<String> response = HttpClient
.newHttpClient()
.send(request, HttpResponse.BodyHandlers.ofString());
} catch (URISyntaxException | InterruptedException | IOException e) {
log.error("요청한 URL 정보가 잘못되었습니다.", e.getMessage());
} catch (Exception e) {
log.error("ERROR : {}\n{}", e.getMessage(), e.getStackTrace());
}
// log.info("websocket send message : {}", JsonUtils.toJson(model));
}
public void sendDataWebClient(GPModel model){
WebClient client = WebClient.builder()
.baseUrl(WS_HOST)
.defaultHeader("Content-Type", "application/json")
.build();
client.post()
.uri(WS_SEND_URI)
.body(Mono.just(model), GPModel.class)
.retrieve()
.bodyToMono(Void.class)
.subscribe();
}
}

203
src/main/resources/application.yml

@ -2,12 +2,12 @@ spring:
config:
activate:
on-profile: local
rabbitmq:
host: 192.168.0.26
port: 5672
username: palnet
password: palnet!234
virtual-host: /
# rabbitmq:
# host: 192.168.0.26
# port: 5672
# username: palnet
# password: palnet!234
# virtual-host: /
netty:
task:
@ -24,27 +24,29 @@ netty:
server:
port: 8182
message:
app:
queue-name: app.drone.queue
exchange-name: app.drone.exchange
routing-key: app.drone.routing.#
websocket:
queue-name: websocket.drone.queue
exchange-name: websocket.drone.exchange
routing-key: websocket.drone.routing.#
#message:
# app:
# queue-name: app.drone.queue
# exchange-name: app.drone.exchange
# routing-key: app.drone.routing.#
# websocket:
# queue-name: websocket.drone.queue
# exchange-name: websocket.drone.exchange
# routing-key: websocket.drone.routing.#
app:
host: http://127.0.0.1:8080/
management:
endpoints:
web:
base-path: /management
path-mapping:
health: health_check
exposure:
include: health, info
host: http://127.0.0.1:8080
web-socket:
host: http://127.0.0.1:8181
#management:
# endpoints:
# web:
# base-path: /management
# path-mapping:
# health: health_check
# exposure:
# include: health, info
---
@ -52,11 +54,11 @@ spring:
config:
activate:
on-profile: dev
rabbitmq:
host: 192.168.0.26
port: 5672
username: guest
password: guest
# rabbitmq:
# host: 192.168.0.26
# port: 5672
# username: guest
# password: guest
netty:
task:
@ -70,27 +72,29 @@ netty:
server:
port: 8182
message:
app:
queue-name: app.drone.queue
exchange-name: app.drone.exchange
routing-key: app.drone.routing.#
websocket:
queue-name: websocket.drone.queue
exchange-name: websocket.drone.exchange
routing-key: websocket.drone.routing.#
#message:
# app:
# queue-name: app.drone.queue
# exchange-name: app.drone.exchange
# routing-key: app.drone.routing.#
# websocket:
# queue-name: websocket.drone.queue
# exchange-name: websocket.drone.exchange
# routing-key: websocket.drone.routing.#
app:
host: http://211.253.38.218:8080/
management:
endpoints:
web:
base-path: /management
path-mapping:
health: health_check
exposure:
include: health, info
host: http://211.253.38.218:8080
web-socket:
host: http://211.253.38.218:8081
#management:
# endpoints:
# web:
# base-path: /management
# path-mapping:
# health: health_check
# exposure:
# include: health, info
---
@ -98,11 +102,11 @@ spring:
config:
activate:
on-profile: prod
rabbitmq:
host: 211.253.38.218
port: 5672
username: palnet
password: palnet1234
# rabbitmq:
# host: 211.253.38.218
# port: 5672
# username: palnet
# password: palnet1234
netty:
task:
@ -119,15 +123,15 @@ netty:
server:
port: 8182
message:
app:
queue-name: app.drone.queue
exchange-name: app.drone.exchange
routing-key: app.drone.routing.#
websocket:
queue-name: websocket.drone.queue
exchange-name: websocket.drone.exchange
routing-key: websocket.drone.routing.#
#message:
# app:
# queue-name: app.drone.queue
# exchange-name: app.drone.exchange
# routing-key: app.drone.routing.#
# websocket:
# queue-name: websocket.drone.queue
# exchange-name: websocket.drone.exchange
# routing-key: websocket.drone.routing.#
logging:
pattern:
@ -139,16 +143,19 @@ logging:
com.palnet: info
app:
host: http://211.253.38.218:8080/
host: http://211.253.38.218:8080
web-socket:
host: http://211.253.38.218:8081
management:
endpoints:
web:
base-path: /management
path-mapping:
health: health_check
exposure:
include: health, info
#management:
# endpoints:
# web:
# base-path: /management
# path-mapping:
# health: health_check
# exposure:
# include: health, info
---
@ -156,11 +163,11 @@ spring:
config:
activate:
on-profile: prod2
rabbitmq:
host: 211.253.38.218
port: 5672
username: palnet
password: palnet1234
# rabbitmq:
# host: 211.253.38.218
# port: 5672
# username: palnet
# password: palnet1234
netty:
task:
@ -177,15 +184,15 @@ netty:
server:
port: 8182
message:
app:
queue-name: app.drone.queue
exchange-name: app.drone.exchange
routing-key: app.drone.routing.#
websocket:
queue-name: websocket.drone.queue
exchange-name: websocket.drone.exchange
routing-key: websocket.drone.routing.#
#message:
# app:
# queue-name: app.drone.queue
# exchange-name: app.drone.exchange
# routing-key: app.drone.routing.#
# websocket:
# queue-name: websocket.drone.queue
# exchange-name: websocket.drone.exchange
# routing-key: websocket.drone.routing.#
logging:
pattern:
@ -197,14 +204,16 @@ logging:
com.palnet: info
app:
host: http://211.253.38.218:8080/
management:
endpoints:
web:
base-path: /management
path-mapping:
health: health_check
exposure:
include: health, info
host: http://211.253.38.218:8080
web-socket:
host: http://211.253.38.218:8081
#management:
# endpoints:
# web:
# base-path: /management
# path-mapping:
# health: health_check
# exposure:
# include: health, info
Loading…
Cancel
Save