shjung 11 달 전
부모
커밋
409db2d551
26개의 변경된 파일965개의 추가작업 그리고 67개의 파일을 삭제
  1. 2 0
      src/main/java/com/evp/comm/server/dao/mapper/EvpsServiceMapper.java
  2. 16 0
      src/main/java/com/evp/comm/server/dao/mapper/batch/EvpCommServerDao.java
  3. 63 0
      src/main/java/com/evp/comm/server/dto/KafkaEvpsEventDto.java
  4. 113 0
      src/main/java/com/evp/comm/server/dto/KafkaEvpsNodeDto.java
  5. 40 0
      src/main/java/com/evp/comm/server/dto/KafkaEvpsRouteDto.java
  6. 74 0
      src/main/java/com/evp/comm/server/dto/KafkaEvpsServiceDto.java
  7. 66 0
      src/main/java/com/evp/comm/server/dto/KafkaEvpsSignalDto.java
  8. 2 2
      src/main/java/com/evp/comm/server/entity/TbEvpNode.java
  9. 81 0
      src/main/java/com/evp/comm/server/entity/TbEvpPhase.java
  10. 1 1
      src/main/java/com/evp/comm/server/entity/TbEvpService.java
  11. 62 0
      src/main/java/com/evp/comm/server/entity/TbEvpSignal.java
  12. 4 2
      src/main/java/com/evp/comm/server/kafka/KafkaProducerService.java
  13. 2 0
      src/main/java/com/evp/comm/server/process/dbms/DbmsData.java
  14. 10 0
      src/main/java/com/evp/comm/server/process/dbms/DbmsDataProcess.java
  15. 3 1
      src/main/java/com/evp/comm/server/xnet/server/EvpCommServerInitializer.java
  16. 30 1
      src/main/java/com/evp/comm/server/xnet/server/process/protocol/EvpsCommPacket.java
  17. 16 6
      src/main/java/com/evp/comm/server/xnet/server/process/protocol/EvpsProtocolConst.java
  18. 2 1
      src/main/java/com/evp/comm/server/xnet/server/process/protocol/eEvpsOpCode.java
  19. 5 7
      src/main/java/com/evp/comm/server/xnet/server/process/response/EvpsEvent.java
  20. 57 17
      src/main/java/com/evp/comm/server/xnet/server/process/response/EvpsNode.java
  21. 11 19
      src/main/java/com/evp/comm/server/xnet/server/process/response/EvpsService.java
  22. 4 9
      src/main/java/com/evp/comm/server/xnet/server/process/response/EvpsServiceEnd.java
  23. 155 0
      src/main/java/com/evp/comm/server/xnet/server/process/response/EvpsSignal.java
  24. 3 0
      src/main/java/com/evp/comm/server/xnet/server/process/work/DataPacketProcess.java
  25. 141 0
      src/main/resources/mybatis/mapper/EvpsServiceMapper.xml
  26. 2 1
      src/test/java/com/evp/comm/server/EvpCommServerApplicationTests.java

+ 2 - 0
src/main/java/com/evp/comm/server/dao/mapper/EvpsServiceMapper.java

@@ -15,5 +15,7 @@ public interface EvpsServiceMapper {
     int insertEvpEvent(@Param("obj") TbEvpEvent obj);
     int insertEvpRoute(Map<String, Object> obj);
     int insertEvpNode(Map<String, Object> obj);
+    int insertEvpPhase(Map<String, Object> obj);
+    int insertEvpSignal(Map<String, Object> obj);
 
 }

+ 16 - 0
src/main/java/com/evp/comm/server/dao/mapper/batch/EvpCommServerDao.java

@@ -32,4 +32,20 @@ public class EvpCommServerDao extends BatchDaoService {
         log.info("{}.insertEvpNode: ..END. {} EA. {} ms.", this.serviceName, total, elapsed.milliSeconds());
         return total;
     }
+
+    public int insertEvpPhase(List<HashMap<String, Object>> req) {
+        log.info("{}.insertEvpPhase: START. {} EA", this.serviceName, req.size());
+        Elapsed elapsed = new Elapsed();
+        int total = insertBatch("insertEvpPhase", req);
+        log.info("{}.insertEvpPhase: ..END. {} EA. {} ms.", this.serviceName, total, elapsed.milliSeconds());
+        return total;
+    }
+
+    public int insertEvpSignal(List<HashMap<String, Object>> req) {
+        log.info("{}.insertEvpSignal: START. {} EA", this.serviceName, req.size());
+        Elapsed elapsed = new Elapsed();
+        int total = insertBatch("insertEvpSignal", req);
+        log.info("{}.insertEvpSignal: ..END. {} EA. {} ms.", this.serviceName, total, elapsed.milliSeconds());
+        return total;
+    }
 }

+ 63 - 0
src/main/java/com/evp/comm/server/dto/KafkaEvpsEventDto.java

@@ -0,0 +1,63 @@
+package com.evp.comm.server.dto;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+
+import java.util.Date;
+
+/**
+* 긴급차량 이벤트 정보
+* tb_evp_event
+*/
+@Getter
+@NoArgsConstructor
+@AllArgsConstructor
+public class KafkaEvpsEventDto {
+    /**
+    * 수집시각
+    */
+    private Date clctDt;
+    /**
+    * 긴급차량 서비스 ID
+    */
+    private String serviceId;
+    /**
+     * 긴급차량 번호(Not Used)
+     */
+    private String evNo;
+    /**
+    * 이벤트 코드(0:서비스시작, 1:차량위치, 2:서비스종료)
+    */
+    private Integer eventCd;
+    /**
+    * 현재위치 위도
+    */
+    private Double curLat;
+    /**
+    * 현재위치 경로
+    */
+    private Double curLng;
+    /**
+    * 현재 차량 속도
+    */
+    private Integer curSpd;
+    /**
+    * 목적지 남은거리(m)
+    */
+    private Integer remDist;
+
+    public KafkaEvpsEventDto(String serviceId, int eventCd) {
+        this.serviceId = serviceId;
+        this.eventCd = eventCd;
+        init();
+    }
+
+    private void init() {
+        this.curLat = 0.;
+        this.curLng = 0.;
+        this.curSpd = 0;
+        this.remDist = 0;
+        this.evNo = "";
+    }
+}

+ 113 - 0
src/main/java/com/evp/comm/server/dto/KafkaEvpsNodeDto.java

@@ -0,0 +1,113 @@
+package com.evp.comm.server.dto;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+
+import java.util.List;
+
+/**
+* 긴급차량 서비스 교차로 정보
+*/
+@Getter
+@NoArgsConstructor
+@AllArgsConstructor
+public class KafkaEvpsNodeDto {
+    /**
+    * 긴급차량 서비스 ID
+    */
+    private String serviceId;
+    private List<EvpsNodeInfo> nodeList;
+
+    @Getter
+    @NoArgsConstructor
+    @AllArgsConstructor
+    public static class EvpsNodeInfo {
+        /**
+         * 교차로 순서(1,...,N)
+         */
+        private Integer seqNo;
+        /**
+         * 교차로 ID
+         */
+        private Integer nodeId;
+        /**
+         * 교차로명
+         */
+        private String nodeNm;
+        /**
+         * 위치 위도
+         */
+        private double lat;
+        /**
+         * 위치 경로
+         */
+        private double lng;
+
+        private List<EvpsPhaseInfo> phaseList;
+    }
+
+
+    @Getter
+    @NoArgsConstructor
+    @AllArgsConstructor
+    public static class EvpsPhaseInfo {
+        /**
+         * 교차로 순서(1,...,N)
+         */
+        private Integer seqNo;
+        /**
+         * 교차로 ID
+         */
+        private Integer nodeId;
+        /**
+         * 링번호(1:A링, 2:B링)
+         */
+        private Integer ring;
+        /**
+         * 현시번호(1~8)
+         */
+        private Integer phaseNo;
+        /**
+         * 맵 번호(0:일반제, 1~5:시차제, 6:전용맵)
+         */
+        private Integer planClass;
+        /**
+         * 이동류번호(1-16, 17, 18)
+         */
+        private Integer flowNo;
+        /**
+         * 시작점 위치 위도
+         */
+        private Double headLat;
+        /**
+         * 시작점 위치 경로
+         */
+        private Double headLng;
+        /**
+         * 중심점 위치 위도
+         */
+        private Double midLat;
+        /**
+         * 중심점 위치 경로
+         */
+        private Double midLng;
+        /**
+         * 끝점 위치 위도
+         */
+        private Double endLat;
+        /**
+         * 끝점 위치 경로
+         */
+        private Double endLng;
+        /**
+         * 시작점 각도
+         */
+        private Integer headAngle;
+        /**
+         * 종점 각도
+         */
+        private Integer endAngle;
+
+    }
+}

+ 40 - 0
src/main/java/com/evp/comm/server/dto/KafkaEvpsRouteDto.java

@@ -0,0 +1,40 @@
+package com.evp.comm.server.dto;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+
+import java.util.List;
+
+/**
+* 긴급차량 서비스 경로 정보
+*/
+@Getter
+@NoArgsConstructor
+@AllArgsConstructor
+public class KafkaEvpsRouteDto {
+
+    /**
+    * 긴급차량 서비스 ID
+    */
+    private String serviceId;
+    private List<EvpsRouteInfo> routeList;
+
+    @Getter
+    @NoArgsConstructor
+    @AllArgsConstructor
+    public static class EvpsRouteInfo {
+        /**
+         * 경로 순서(1,...,N)
+         */
+        private Integer seqNo;
+        /**
+         * 위치 위도
+         */
+        private double lat;
+        /**
+         * 위치 경로
+         */
+        private double lng;
+    }
+}

+ 74 - 0
src/main/java/com/evp/comm/server/dto/KafkaEvpsServiceDto.java

@@ -0,0 +1,74 @@
+package com.evp.comm.server.dto;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+
+import java.util.Date;
+
+/**
+* 긴급차량 서비스 정보
+*/
+@Getter
+@NoArgsConstructor
+@AllArgsConstructor
+public class KafkaEvpsServiceDto {
+
+    /**
+    * 긴급차량 서비스 ID
+    */
+    private String serviceId;
+    /**
+    * 수집시각
+    */
+    private Date clctDt;
+    /**
+    * 긴급차량 번호
+    */
+    private String evNo;
+    /**
+    * 현재위치 위도
+    */
+    private Double curLat;
+    /**
+    * 현재위치 경로
+    */
+    private Double curLng;
+    /**
+    * 서비스명(목적지명)
+    */
+    private String serviceNm;
+    /**
+    * 도착위치 위도
+    */
+    private Double arrLat;
+    /**
+    * 도착위치 위도
+    */
+    private Double arrLng;
+    /**
+    * 예상도착시간(단위:초)
+    */
+    private Integer arrTm;
+    /**
+    * 차량길이(군집차량길이포함)
+    */
+    private Integer vehLen;
+    /**
+    * 재난번호
+    */
+    private String ocrNo;
+    /**
+    * 재난종별명
+    */
+    private String ocrType;
+    /**
+     * 거리(단위:m)
+     */
+    private Integer serviceDist;
+    /**
+    * 서비스 상태 코드(1:진행중-서비스 진행중,2:정상종료-모든 교차로 제어 및 해제 완료,3:취소-아직 통과하지 않은 교차로 존재,4:센터강제종료-운영자가 서비스를 강제로 종료,5:비정상종료-서비스가 존재하지 않음,6:서비스시작실패-제어대상교차로가 없음,7:비정상종료-앱서버에 에러 발생,8:비정상종료-일정시간 앱에서 위치 및 속도 정보가 오지 않는 경우,9:자동종료-경로이탈,10:자동종료-경로진입 가능시간 초과,11:자동종료-정차가능시간 초과,12:취소-모든 교차로 제어및 해제 완료,13:실패-서비스 제어 요청 실패,14:실패-서비스 가능 교차로가 존재하지 않음,15:자동종료-위치정보 수신 가능 시간 초과)
+    */
+    private Integer statusCd;
+
+}

+ 66 - 0
src/main/java/com/evp/comm/server/dto/KafkaEvpsSignalDto.java

@@ -0,0 +1,66 @@
+package com.evp.comm.server.dto;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+
+import java.util.Date;
+import java.util.List;
+
+/**
+* 긴급차량 서비스 교차로 신호 정보
+*/
+@Getter
+@NoArgsConstructor
+@AllArgsConstructor
+public class KafkaEvpsSignalDto {
+
+    /**
+    * 긴급차량 서비스 ID
+    */
+    private String serviceId;
+    /**
+     * 수집시각
+     */
+    private Date clctDt;
+
+    private List<EvpsSignalInfo> signalList;
+
+    @Getter
+    @NoArgsConstructor
+    @AllArgsConstructor
+    public static class EvpsSignalInfo {
+        /**
+         * 교차로 ID
+         */
+        private Integer nodeId;
+        /**
+         * 교차로 순서(1,...,N)
+         */
+        private Integer seqNo;
+        /**
+         * 목적지 남은거리(m)
+         */
+        private Integer remDist;
+        /**
+         * 교차로운영상태(0:통신이상, 1:정상, 2:점멸, 3:소등, 4:수동진행, 5:현시유지)
+         */
+        private Integer state;
+        /**
+         * 현재 운영중인 맵 번호(0:일반제, 1~5:시차제, 6:전용맵)
+         */
+        private Integer planClass;
+        /**
+         * A링 현시번호(1-8)
+         */
+        private Integer aRingPhase;
+        /**
+         * B링 현시번호(1-8)
+         */
+        private Integer bRingPhase;
+        /**
+         * 유지현시번호(1-8)
+         */
+        private Integer holdPhase;
+    }
+}

+ 2 - 2
src/main/java/com/evp/comm/server/entity/TbEvpNode.java

@@ -36,10 +36,10 @@ public class TbEvpNode implements Serializable {
     /**
     * 위치 위도
     */
-    private double lat;
+    private Double lat;
     /**
     * 위치 경로
     */
-    private double lng;
+    private Double lng;
 
 }

+ 81 - 0
src/main/java/com/evp/comm/server/entity/TbEvpPhase.java

@@ -0,0 +1,81 @@
+package com.evp.comm.server.entity;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+
+/**
+* 긴급차량 서비스 교차로 현시 정보
+* tb_evp_phase
+*/
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class TbEvpPhase implements Serializable {
+
+    /**
+     * 긴급차량 서비스 ID
+     */
+    private String serviceId;
+    /**
+    * 교차로 순서(1,...,N)
+    */
+    private Integer seqNo;
+    /**
+    * 교차로 ID
+    */
+    private Integer nodeId;
+    /**
+    * 링번호(1:A링, 2:B링)
+    */
+    private Integer ring;
+    /**
+    * 현시번호(1~8)
+    */
+    private Integer phaseNo;
+    /**
+    * 맵 번호(0:일반제, 1~5:시차제, 6:전용맵)
+    */
+    private Integer planClass;
+    /**
+    * 이동류번호(1-16, 17, 18)
+    */
+    private Integer flowNo;
+    /**
+    * 시작점 위치 위도
+    */
+    private Double headLat;
+    /**
+    * 시작점 위치 경로
+    */
+    private Double headLng;
+    /**
+    * 중심점 위치 위도
+    */
+    private Double midLat;
+    /**
+    * 중심점 위치 경로
+    */
+    private Double midLng;
+    /**
+    * 끝점 위치 위도
+    */
+    private Double endLat;
+    /**
+    * 끝점 위치 경로
+    */
+    private Double endLng;
+    /**
+    * 시작점 각도
+    */
+    private Integer headAngle;
+    /**
+    * 종점 각도
+    */
+    private Integer endAngle;
+
+}

+ 1 - 1
src/main/java/com/evp/comm/server/entity/TbEvpService.java

@@ -73,7 +73,7 @@ public class TbEvpService implements Serializable {
      */
     private Integer serviceDist;
     /**
-    * 서비스 상태 코드(1:진행중-서비스 진행중,2:정상종료-모든 교차로 제어 및 해제 완료,3:취소-아직 통과하지 않은 교차로 존재,4:센터강제종료-운영자가 서비스를 강제로 종료,5:비정상종료-서비스가 존재하지 않음,6:서비스시작실패-제어대상교차로가 없음,8:비정상종료-앱서버에 에러 발생,9:비정상종료-일정시간 앱에서 위치 및 속도 정보가 오지 않는 경우,10:자동종료-경로이탈,11:자동종료-경로진입 가능시간 초과,12:자동종료-정차가능시간 초과,13:취소-모든 교차로 제어및 해제 완료,14:실패-서비스 제어 요청 실패,15:실패-서비스 가능 교차로가 존재하지 않음,16:자동종료-위치정보 수신 가능 시간 초과)
+    * 서비스 상태 코드(1:진행중-서비스 진행중,2:정상종료-모든 교차로 제어 및 해제 완료,3:취소-아직 통과하지 않은 교차로 존재,4:센터강제종료-운영자가 서비스를 강제로 종료,5:비정상종료-서비스가 존재하지 않음,6:서비스시작실패-제어대상교차로가 없음,7:비정상종료-앱서버에 에러 발생,8:비정상종료-일정시간 앱에서 위치 및 속도 정보가 오지 않는 경우,9:자동종료-경로이탈,10:자동종료-경로진입 가능시간 초과,11:자동종료-정차가능시간 초과,12:취소-모든 교차로 제어및 해제 완료,13:실패-서비스 제어 요청 실패,14:실패-서비스 가능 교차로가 존재하지 않음,15:자동종료-위치정보 수신 가능 시간 초과)
     */
     private Integer statusCd;
 

+ 62 - 0
src/main/java/com/evp/comm/server/entity/TbEvpSignal.java

@@ -0,0 +1,62 @@
+package com.evp.comm.server.entity;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+* 긴급차량 서비스 교차로 신호 정보
+* tb_evp_signal
+*/
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class TbEvpSignal implements Serializable {
+
+    /**
+    * 수집시각
+    */
+    private Date clctDt;
+    /**
+    * 긴급차량 서비스 ID
+    */
+    private String serviceId;
+    /**
+    * 교차로 ID
+    */
+    private Integer nodeId;
+    /**
+    * 교차로 순서(1,...,N)
+    */
+    private Integer seqNo;
+    /**
+    * 목적지 남은거리(m)
+    */
+    private Integer remDist;
+    /**
+    * 교차로운영상태(0:통신이상, 1:정상, 2:점멸, 3:소등, 4:수동진행, 5:현시유지)
+    */
+    private Integer state;
+    /**
+    * 현재 운영중인 맵 번호(0:일반제, 1~5:시차제, 6:전용맵)
+    */
+    private Integer planClass;
+    /**
+    * A링 현시번호(1-8)
+    */
+    private Integer aRingPhase;
+    /**
+    * B링 현시번호(1-8)
+    */
+    private Integer bRingPhase;
+    /**
+    * 유지현시번호(1-8)
+    */
+    private Integer holdPhase;
+
+}

+ 4 - 2
src/main/java/com/evp/comm/server/kafka/KafkaProducerService.java

@@ -75,6 +75,7 @@ public class KafkaProducerService {
         this.pingProducer = KafkaProducerFactory.createProducerTemplate(props);
         this.pingProducer.setDefaultTopic(this.config.getPingTopic());
     }
+
     public void shutdown() {
         try {
             if (this.nodeProducer != null) {
@@ -162,8 +163,9 @@ public class KafkaProducerService {
                 log.error("Error while producing message to topic: {}, {}", recordMetadata, e.toString());
             }
             else {
-                String message = String.format("sent message to topic:%s partition:%s  offset:%s", recordMetadata.topic(), recordMetadata.partition(), recordMetadata.offset());
-                System.out.println(message);
+                String message = String.format("sent message to topic:%s partition:%s  offset:%s",
+                        recordMetadata.topic(), recordMetadata.partition(), recordMetadata.offset());
+               log.error(message);
             }
         }
     }

+ 2 - 0
src/main/java/com/evp/comm/server/process/dbms/DbmsData.java

@@ -18,6 +18,8 @@ public class DbmsData {
     public static final int DBMS_DATA_INS_EVENT= 12;
     public static final int DBMS_DATA_INS_ROUTE = 13;
     public static final int DBMS_DATA_INS_NODE = 14;
+    public static final int DBMS_DATA_INS_PHASE = 15;
+    public static final int DBMS_DATA_INS_SIGNAL = 16;
 
 
     private int          type;

+ 10 - 0
src/main/java/com/evp/comm/server/process/dbms/DbmsDataProcess.java

@@ -105,6 +105,16 @@ public class DbmsDataProcess {
                     result = this.evpCommServerDao.insertEvpNode(nodeList);
                     break;
 
+                case DbmsData.DBMS_DATA_INS_PHASE:
+                    List<HashMap<String, Object>> phaseList = (List<HashMap<String, Object>>)data.getData();
+                    result = this.evpCommServerDao.insertEvpPhase(phaseList);
+                    break;
+
+                case DbmsData.DBMS_DATA_INS_SIGNAL:
+                    List<HashMap<String, Object>> signalList = (List<HashMap<String, Object>>)data.getData();
+                    result = this.evpCommServerDao.insertEvpSignal(signalList);
+                    break;
+
 
                 case DbmsData.DBMS_DATA_CENTER_STTS_UPDATE:
                     TbRegionCenterComm updStts = (TbRegionCenterComm) data.getData();

+ 3 - 1
src/main/java/com/evp/comm/server/xnet/server/EvpCommServerInitializer.java

@@ -12,6 +12,8 @@ import io.netty.channel.Channel;
 import io.netty.channel.ChannelInitializer;
 import io.netty.channel.ChannelPipeline;
 import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
+import io.netty.handler.logging.LogLevel;
+import io.netty.handler.logging.LoggingHandler;
 import io.netty.handler.timeout.IdleStateHandler;
 import lombok.extern.slf4j.Slf4j;
 
@@ -56,7 +58,7 @@ public class EvpCommServerInitializer extends ChannelInitializer<Channel> {
 
         IdleStateHandler idleStateHandler = new IdleStateHandler(this.config.getReaderIdleTimeSeconds(), 0, 0);
         ChannelPipeline pipeline = channel.pipeline();
-//        pipeline.addLast(new LoggingHandler(LogLevel.ERROR));
+        pipeline.addLast(new LoggingHandler(LogLevel.INFO));
         pipeline.addLast("idleStateHandler", idleStateHandler);
         pipeline.addLast("frameDecoder", new LengthFieldBasedFrameDecoder(
                 EvpsProtocolConst.EVPS_MAX_PACKET *2,

+ 30 - 1
src/main/java/com/evp/comm/server/xnet/server/process/protocol/EvpsCommPacket.java

@@ -7,6 +7,13 @@ import lombok.Setter;
 import lombok.ToString;
 
 import java.io.Serializable;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.Date;
 
 @Getter
 @Setter
@@ -52,11 +59,28 @@ public class EvpsCommPacket implements Serializable {
     protected short hour;
     protected short min;
     protected short sec;
+    protected byte[] serviceIdArr;
     protected int dataLength;
     protected String commDate;
+    protected String serviceId;
 
     protected boolean isValid;
 
+    public Date getPacketTime() {
+        try {
+            DateTimeFormatter inputFormatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
+            LocalDateTime localDateTime = LocalDateTime.parse(this.commDate, inputFormatter);
+            ZoneId koreaZoneId = ZoneId.of("Asia/Seoul");
+            ZonedDateTime koreaZonedDateTime = localDateTime.atZone(koreaZoneId);
+
+            return Date.from(koreaZonedDateTime.toInstant());
+        }
+        catch (Exception e) {
+            Calendar cal = Calendar.getInstance();
+            return cal.getTime();
+        }
+    }
+
     private void decode() {
         if (this.frameLength < EvpsProtocolConst.EVPS_HEAD_SIZE) {
             this.stx1       = EvpsProtocolConst.EVPS_NULL;
@@ -71,9 +95,10 @@ public class EvpsCommPacket implements Serializable {
             this.hour       = 0;
             this.min        = 0;
             this.sec        = 0;
+            this.serviceIdArr  = new byte[EvpsProtocolConst.EVPS_SERVICE_ID_SIZE];
             this.dataLength = 0;
-
             this.commDate = "20000101010000";
+            this.serviceId = "";
             this.isValid = false;
             return;
         }
@@ -92,8 +117,12 @@ public class EvpsCommPacket implements Serializable {
         this.hour       = (short)(this.headBuffer[idx++] & 0xFF);
         this.min        = (short)(this.headBuffer[idx++] & 0xFF);
         this.sec        = (short)(this.headBuffer[idx++] & 0xFF);
+        this.serviceIdArr = Arrays.copyOfRange(this.headBuffer, idx, EvpsProtocolConst.EVPS_SERVICE_ID_SIZE);
+        idx += EvpsProtocolConst.EVPS_SERVICE_ID_SIZE;
         this.dataLength = ((this.headBuffer[idx++] & 0xFF) << 8) | (this.headBuffer[idx] & 0xFF);
+//        this.dataLength = ((this.headBuffer[idx++] & 0xFF) << 24) | ((this.headBuffer[idx++] & 0xFF) << 16) | ((this.headBuffer[idx++] & 0xFF) << 8) | (this.headBuffer[idx++] & 0xFF);
         this.commDate = String.format("%4d%02d%02d%02d%02d%02d", this.year, this.month, this.day, this.hour, this.min, this.sec);
+        this.serviceId = new String(this.serviceIdArr).trim();
         this.isValid = true;
     }
 

+ 16 - 6
src/main/java/com/evp/comm/server/xnet/server/process/protocol/EvpsProtocolConst.java

@@ -16,14 +16,16 @@ public class EvpsProtocolConst {
      *              시(0~23)                                                 1
      *              분(0~59)                                                 1
      *              초(0~59)                                                 1
+     * 서비스 ID    ID구성 : 행정구역코드(3)+년+월+일+시+분+초+순번(0~99)
+     *              지자체센터에서 생성 이후 사용함                          19
      * DATA LENGTH  현재 프레임 데이터의 길이(0~8174)
      *              프레임의 최대길이가 8192바이트이므로 헤더18 바이트를
      *              제외한 8174바이트가 DATA 의 최대 길이임.                 2
      * ****************************************************************************************************************/
 
     public static final int EVPS_MAX_PACKET = 8192;                 /* 한번에 전송하는 최대 패킷 크기, 8192 */
-    public static final int EVPS_HEAD_SIZE = 18;                    /* 프로토콜 헤더 크기 16 byte */
-    public static final int EVPS_PACKET_LENGTH_FIELD_OFFSET = 16;   /* Netty Fixed Packet Length position */
+    public static final int EVPS_HEAD_SIZE = 37;                    /* 프로토콜 헤더 크기 37 byte */
+    public static final int EVPS_PACKET_LENGTH_FIELD_OFFSET = 36;   /* Netty Fixed Packet Length position */
     public static final int EVPS_PACKET_LENGTH_FIELD_SIZE = 2;      /* Netty Fixed Packet Length field size */
     public static final int EVPS_PACKET_LENGTH_ADJUSTMENTS = 0;     /* Length Field 이후의 데이터 나머지 크기 */
 
@@ -31,10 +33,18 @@ public class EvpsProtocolConst {
     public static final byte EVPS_STX2 = 'E';  /* STX1 */
     public static final byte EVPS_NULL = (byte)0x00;
 
-    public static final int MIN_SERVICE_DATA_SIZE = 199;
-    public static final int MIN_NODE_DATA_SIZE = 21;     // (19+2);
-    public static final int EVENT_DATA_SIZE = 45;        // (19+10+4+4+4+4);
-    public static final int EVENT_SERVICE_END_SIZE = 23; // (19+4);
+    public static final int MIN_SERVICE_DATA_SIZE = 180;
+    public static final int SERVICE_DATA_ROUTE_SIZE = 8;    // (lat: 4 + lng: 4)
+
+    public static final int MIN_NODE_DATA_SIZE = 2;         // (nodeCount: 2);
+    public static final int MIN_NODE_DATA_NODE_SIZE = 114;  // (nodeCount: 2);
+    public static final int NODE_DATA_PHASE_SIZE = 32;
+
+    public static final int EVENT_DATA_SIZE = 26;           // (10+4+4+4+4);
+
+    public static final int MIN_SIGNAL_DATA_SIZE = 2;         // (nodeCount: 2);
+    public static final int SIGNAL_DATA_SIGNAL_SIZE = 13;
+    public static final int EVENT_SERVICE_END_SIZE = 4;     // (reason: 4);
 
     public static final int EVPS_SERVICE_ID_SIZE = 19;
     public static final int EVPS_SERVICE_NAME_SIZE = 100;

+ 2 - 1
src/main/java/com/evp/comm/server/xnet/server/process/protocol/eEvpsOpCode.java

@@ -8,8 +8,9 @@ import java.util.Map;
 public enum eEvpsOpCode {
 
     EVPS_SERVICE     ( (byte)0x14, "긴급차량우선신호 발생에 따른 출발지-목적지 사이의 경로정보"),
-    EVPS_NODE        ( (byte)0x15, "경로 내의 교차로 정보"),
+    EVPS_NODE        ( (byte)0x15, "경로 내의 교차로/현시구성 정보"),
     EVPS_EVENT       ( (byte)0x16, "긴급차량의 위치와 속도 정보"),
+    EVPS_SIGNAL      ( (byte)0x17, "긴급차량의 위치에서 진행방향의 각 교차로까지의 남은거리 및 현시현황"),
     EVPS_SERVICE_END ( (byte)0x18, "긴급차량 서비스 종료"); // 사용 안함(예비)
 
     @Getter

+ 5 - 7
src/main/java/com/evp/comm/server/xnet/server/process/response/EvpsEvent.java

@@ -13,7 +13,6 @@ import org.slf4j.MDC;
 import java.util.Arrays;
 
 /*
-serviceId       서비스ID          String      19
 evNo            차량번호          String      10
 currentLat      현재위치의 위도   Integer      4   ( DD.DDDDDD x 1000000 )
 currentLng      현재위치의 경도   Integer      4   ( DD.DDDDDD x 1000000 )
@@ -36,7 +35,7 @@ public class EvpsEvent implements EvpsCommResponse {
             MDC.put("id", packet.getCenter().getLogKey());
             log.info("[{}], EvpsEvent.response.", packet.getCenter().getLogKey());
 
-            return makeData(packet.getCenter(), packet.getPacket().getBuffer());
+            return makeData(packet, packet.getPacket().getBuffer());
         }
         catch (Exception e) {
             log.error("[{}], EvpsEvent.response: Exception. will be closed. {}", packet.getCenter().getLogKey(), e.getMessage());
@@ -49,7 +48,8 @@ public class EvpsEvent implements EvpsCommResponse {
         return result;
     }
 
-    private boolean makeData(EvpsCenter center, byte[] buffer) {
+    private boolean makeData(RecvPacketDto packet, byte[] buffer) {
+        EvpsCenter center = packet.getCenter();
         if (buffer == null || buffer.length != EvpsProtocolConst.EVENT_DATA_SIZE) {
             log.error("[{}], EvpsEvent.response: Data Length Error: Required data length({}), Cur({}). will be closed.",
                     center.getLogKey(), EvpsProtocolConst.EVENT_DATA_SIZE, buffer == null ? 0 : buffer.length);
@@ -57,9 +57,6 @@ public class EvpsEvent implements EvpsCommResponse {
         }
 
         int idx = 0;
-        // 서비스ID
-        byte[] serviceIdArr = Arrays.copyOfRange(buffer, idx, idx+EvpsProtocolConst.EVPS_SERVICE_ID_SIZE);
-        idx += EvpsProtocolConst.EVPS_SERVICE_ID_SIZE;
         // 차량번호
         byte[] vehNoArr = Arrays.copyOfRange(buffer, idx, idx+EvpsProtocolConst.EVPS_VEHICLE_NO_SIZE);
         idx += EvpsProtocolConst.EVPS_VEHICLE_NO_SIZE;
@@ -73,7 +70,8 @@ public class EvpsEvent implements EvpsCommResponse {
         int distance   = ((buffer[idx++] & 0xFF) << 24) | ((buffer[idx++] & 0xFF) << 16) | ((buffer[idx++] & 0xFF) << 8) | (buffer[idx++] & 0xFF);
 
         // 이벤트 정보 입력(차량 이동)
-        TbEvpEvent event = new TbEvpEvent(new String(serviceIdArr).trim(), TbEvpEvent.EVPS_EVENT_VEHICLE_MOVE);
+        String serviceId = packet.getPacket().getServiceId();
+        TbEvpEvent event = new TbEvpEvent(serviceId, TbEvpEvent.EVPS_EVENT_VEHICLE_MOVE);
         event.setEvNo(new String(vehNoArr).trim());
         event.setCurLat(currentLat/EvpsProtocolConst.EVPS_GEO_CORRECT);
         event.setCurLng(currentLng/EvpsProtocolConst.EVPS_GEO_CORRECT);

+ 57 - 17
src/main/java/com/evp/comm/server/xnet/server/process/response/EvpsNode.java

@@ -16,7 +16,6 @@ import java.util.HashMap;
 import java.util.List;
 
 /*
-serviceId       서비스ID        String      19
 lcList          교차로 갯수     Integer     2
     intLcNo1    교차로 번호1    Integer     4       국토부 표준 NODE ID
     intName1    교차로명1       String      100     교차로명
@@ -41,7 +40,7 @@ public class EvpsNode implements EvpsCommResponse {
 
             if (packet.getPacket().getCurrent() == 1 && packet.getPacket().getTotal() == 1) {
                 // 모든 정보가 하나의 프레임으로 전송된 경우
-                return makeData(packet.getCenter(), packet.getPacket().getBuffer());
+                return makeData(packet, packet.getPacket().getBuffer());
             }
 
             // 패킷이 분할되어 전송된 경우
@@ -50,8 +49,7 @@ public class EvpsNode implements EvpsCommResponse {
             if (packet.getPacket().getCurrent() == 1) {
                 // 첫번째 프레임
                 // sequence 에 대항하는 서비스 정보를 메모리에 저장하고 서비스 ID에 대한 메모리 정보를 얻는다.
-                byte[] serviceIdArr = Arrays.copyOfRange(packet.getPacket().getBuffer(), 0, EvpsProtocolConst.EVPS_SERVICE_ID_SIZE);
-                String serviceId = new String(serviceIdArr).trim();
+                String serviceId = packet.getPacket().getServiceId();
                 EvpsSequenceDto sequenceDto = new EvpsSequenceDto();
                 sequenceDto.setSequence(sequence);
                 sequenceDto.setServiceId(serviceId);
@@ -84,7 +82,7 @@ public class EvpsNode implements EvpsCommResponse {
                         packet.getCenter().getLogKey(), serviceDto.getServiceId(), sequence, packet.getPacket().getCurrent(), packet.getPacket().getTotal());
 
                 // 저장한 프레임 전체를 이용해서 데이터 생성
-                result = makeData(packet.getCenter(), serviceDto.getNodeBuffer().array());
+                result = makeData(packet, serviceDto.getNodeBuffer().array());
 
                 // SequenceMap 에서 SequenceDto 를 제거한다.
                 packet.getCenter().getSequenceMap().remove(sequence);
@@ -101,32 +99,30 @@ public class EvpsNode implements EvpsCommResponse {
         return result;
     }
 
-    private boolean makeData(EvpsCenter center, byte[] buffer) {
+    private boolean makeData(RecvPacketDto packet, byte[] buffer) {
+        EvpsCenter center = packet.getCenter();
         if (buffer == null || buffer.length < EvpsProtocolConst.MIN_NODE_DATA_SIZE) {
             log.error("[{}], EvpsNode.response: Data Length Error: Required minimum data length({}), Cur({}). will be closed.",
                     center.getLogKey(), EvpsProtocolConst.MIN_NODE_DATA_SIZE, buffer == null ? 0: buffer.length);
             return false;
         }
 
-        // 서비스ID
-        int idx = 0;
-        byte[] serviceIdArr = Arrays.copyOfRange(buffer, idx, idx+EvpsProtocolConst.EVPS_SERVICE_ID_SIZE);
-        idx += EvpsProtocolConst.EVPS_SERVICE_ID_SIZE;
-        String serviceId = new String(serviceIdArr).trim();
+        String serviceId = packet.getPacket().getServiceId();
 
+        int idx = 0;
         // 교차로수(2 Byte)
         int nodeCount = ((buffer[idx++] & 0xFF) << 8) | (buffer[idx++] & 0xFF);
-        int reqNodeSize = nodeCount * 112;  // nodeId(4), name(100), lat(4), lng(4)
-        int reqDataSize = EvpsProtocolConst.MIN_NODE_DATA_SIZE + reqNodeSize;
-        if (buffer.length != reqDataSize) {
+        int reqDataSize = EvpsProtocolConst.MIN_NODE_DATA_NODE_SIZE * nodeCount;
+        if ((buffer.length - idx) < reqDataSize) {
             log.error("[{}], EvpsNode.response: Data Length Error: Required data length({}), Cur({}). will be closed.",
-                    center.getLogKey(), reqDataSize, buffer.length);
+                    center.getLogKey(), reqDataSize, (buffer.length-idx));
             return false;
         }
 
         List<HashMap<String, Object>> lists = new ArrayList<>();
+        List<HashMap<String, Object>> phaseLists = new ArrayList<>();
         for (int ii = 0; ii < nodeCount; ii++) {
-            int nodeId       = ((buffer[idx++] & 0xFF) << 24) | ((buffer[idx++] & 0xFF) << 16) | ((buffer[idx++] & 0xFF) << 8) | (buffer[idx++] & 0xFF);
+            long nodeId      = ((long) (buffer[idx++] & 0xFF) << 24) | ((buffer[idx++] & 0xFF) << 16) | ((buffer[idx++] & 0xFF) << 8) | (buffer[idx++] & 0xFF);
             byte[] nodeNmArr = Arrays.copyOfRange(buffer, idx, idx+EvpsProtocolConst.EVPS_NODE_NAME_SIZE);
             idx += EvpsProtocolConst.EVPS_NODE_NAME_SIZE;
             int currentLat   = ((buffer[idx++] & 0xFF) << 24) | ((buffer[idx++] & 0xFF) << 16) | ((buffer[idx++] & 0xFF) << 8) | (buffer[idx++] & 0xFF);
@@ -141,13 +137,57 @@ public class EvpsNode implements EvpsCommResponse {
             param.put("LNG",         currentLng/EvpsProtocolConst.EVPS_GEO_CORRECT);
 
             lists.add(param);
-        }
 
+            int phaseCount = ((buffer[idx++] & 0xFF) << 8) | (buffer[idx++] & 0xFF);
+            int reqPhaseDataSize = EvpsProtocolConst.NODE_DATA_PHASE_SIZE * phaseCount;
+            if ((buffer.length - idx) < reqPhaseDataSize) {
+                log.error("[{}], EvpsNode.response: Phase Data Length Error: Required data length({}), Cur({}). will be closed.",
+                        center.getLogKey(), reqPhaseDataSize, (buffer.length-idx));
+                return false;
+            }
+            for (int jj = 0; jj < phaseCount; jj++) {
+                int ring      = buffer[idx++] & 0xFF;
+                int phaseNo   = buffer[idx++] & 0xFF;
+                int planClass = buffer[idx++] & 0xFF;
+                int flowNo    = buffer[idx++] & 0xFF;
+                int headLat   = ((buffer[idx++] & 0xFF) << 24) | ((buffer[idx++] & 0xFF) << 16) | ((buffer[idx++] & 0xFF) << 8) | (buffer[idx++] & 0xFF);
+                int headLng   = ((buffer[idx++] & 0xFF) << 24) | ((buffer[idx++] & 0xFF) << 16) | ((buffer[idx++] & 0xFF) << 8) | (buffer[idx++] & 0xFF);
+                int midLat    = ((buffer[idx++] & 0xFF) << 24) | ((buffer[idx++] & 0xFF) << 16) | ((buffer[idx++] & 0xFF) << 8) | (buffer[idx++] & 0xFF);
+                int midLng    = ((buffer[idx++] & 0xFF) << 24) | ((buffer[idx++] & 0xFF) << 16) | ((buffer[idx++] & 0xFF) << 8) | (buffer[idx++] & 0xFF);
+                int endLat    = ((buffer[idx++] & 0xFF) << 24) | ((buffer[idx++] & 0xFF) << 16) | ((buffer[idx++] & 0xFF) << 8) | (buffer[idx++] & 0xFF);
+                int endLng    = ((buffer[idx++] & 0xFF) << 24) | ((buffer[idx++] & 0xFF) << 16) | ((buffer[idx++] & 0xFF) << 8) | (buffer[idx++] & 0xFF);
+                int headAngle = ((buffer[idx++] & 0xFF) << 8) | (buffer[idx++] & 0xFF);
+                int endAngle  = ((buffer[idx++] & 0xFF) << 8) | (buffer[idx++] & 0xFF);
+
+                HashMap<String, Object> phaseParam = new HashMap<>();
+                param.put("SERVICE_ID",  serviceId);
+                param.put("SEQ_NO",      jj+1);
+                param.put("NODE_ID",     nodeId);
+
+                param.put("RING",       ring);
+                param.put("PHASE_NO",   phaseNo);
+                param.put("PLAN_CLASS", planClass);
+                param.put("FLOW_NO",    flowNo);
+                param.put("HEAD_LAT",   headLat/EvpsProtocolConst.EVPS_GEO_CORRECT);
+                param.put("HEAD_LNG",   headLng/EvpsProtocolConst.EVPS_GEO_CORRECT);
+                param.put("MID_LAT",    midLat/EvpsProtocolConst.EVPS_GEO_CORRECT);
+                param.put("MID_LNG",    midLng/EvpsProtocolConst.EVPS_GEO_CORRECT);
+                param.put("END_LAT",    endLat/EvpsProtocolConst.EVPS_GEO_CORRECT);
+                param.put("END_LNG",    endLng/EvpsProtocolConst.EVPS_GEO_CORRECT);
+                param.put("HEAD_ANGLE", headAngle);
+                param.put("END_ANGLE",  endAngle);
+
+                phaseLists.add(phaseParam);
+            }
+        }
         log.info("[{}], EvpsNode.response: Service Node List {} EA.", center.getLogKey(), lists.size());
 
         if (!lists.isEmpty()) {
             this.dbmsDataProcess.add(new DbmsData(DbmsData.DBMS_DATA_INS_NODE, center, lists));
         }
+        if (!phaseLists.isEmpty()) {
+            this.dbmsDataProcess.add(new DbmsData(DbmsData.DBMS_DATA_INS_PHASE, center, phaseLists));
+        }
         return true;
     }
 }

+ 11 - 19
src/main/java/com/evp/comm/server/xnet/server/process/response/EvpsService.java

@@ -18,7 +18,6 @@ import java.util.HashMap;
 import java.util.List;
 
 /*
-serviceId       서비스ID        String      19
 evNo            차량번호        String      10
 currentLat      현재위치LAT     Integer     4       ( DD.DDDDDD x 1000000 )
 currentLng      현재위치LNG     Integer     4       ( DD.DDDDDD x 1000000 )
@@ -53,7 +52,7 @@ public class EvpsService implements EvpsCommResponse {
 
             if (packet.getPacket().getCurrent() == 1 && packet.getPacket().getTotal() == 1) {
                 // 모든 정보가 하나의 프레임으로 전송된 경우
-                return makeData(packet.getCenter(), packet.getPacket().getBuffer(), true);
+                return makeData(packet, packet.getPacket().getBuffer(), true);
             }
 
             // 패킷이 분할되어 전송된 경우
@@ -62,8 +61,7 @@ public class EvpsService implements EvpsCommResponse {
             if (packet.getPacket().getCurrent() == 1) {
                 // 첫번째 프레임
                 // sequence 에 대항하는 서비스 정보를 메모리에 저장하고 서비스 ID에 대한 메모리 정보를 얻는다.
-                byte[] serviceIdArr = Arrays.copyOfRange(packet.getPacket().getBuffer(), 0, EvpsProtocolConst.EVPS_SERVICE_ID_SIZE);
-                String serviceId = new String(serviceIdArr).trim();
+                String serviceId = packet.getPacket().getServiceId();
                 EvpsSequenceDto sequenceDto = new EvpsSequenceDto();
                 sequenceDto.setSequence(sequence);
                 sequenceDto.setServiceId(serviceId);
@@ -96,7 +94,7 @@ public class EvpsService implements EvpsCommResponse {
                         packet.getCenter().getLogKey(), serviceDto.getServiceId(), sequence, packet.getPacket().getCurrent(), packet.getPacket().getTotal());
 
                 // 저장한 프레임 전체를 이용해서 데이터 생성
-                result = makeData(packet.getCenter(), serviceDto.getNodeBuffer().array(), false);
+                result = makeData(packet, serviceDto.getNodeBuffer().array(), false);
 
                 // SequenceMap 에서 SequenceDto 를 제거한다.
                 packet.getCenter().getSequenceMap().remove(sequence);
@@ -113,7 +111,8 @@ public class EvpsService implements EvpsCommResponse {
         return result;
     }
 
-    private boolean makeData(EvpsCenter center, byte[] buffer, boolean oneFrame) {
+    private boolean makeData(RecvPacketDto packet, byte[] buffer, boolean oneFrame) {
+        EvpsCenter center = packet.getCenter();
         if (buffer == null || buffer.length < EvpsProtocolConst.MIN_SERVICE_DATA_SIZE) {
             log.error("[{}], EvpsService.response: Data Length Error: Required minimum data length({}), Cur({}). will be closed.",
                     center.getLogKey(), EvpsProtocolConst.MIN_SERVICE_DATA_SIZE, buffer == null ? 0 : buffer.length);
@@ -123,9 +122,6 @@ public class EvpsService implements EvpsCommResponse {
         // 만일 분할 된 패킷인 경우 패킷을 메노리에 저장하고 리턴
         int idx = 0;
 
-        // 서비스ID
-        byte[] serviceIdArr = Arrays.copyOfRange(buffer, idx, idx+EvpsProtocolConst.EVPS_SERVICE_ID_SIZE);
-        idx += EvpsProtocolConst.EVPS_SERVICE_ID_SIZE;
         // 차량번호
         byte[] vehNoArr = Arrays.copyOfRange(buffer, idx, idx+EvpsProtocolConst.EVPS_VEHICLE_NO_SIZE);
         idx += EvpsProtocolConst.EVPS_VEHICLE_NO_SIZE;
@@ -155,7 +151,7 @@ public class EvpsService implements EvpsCommResponse {
 
         // 서비스 시작 정보 입력
         TbEvpService service = new TbEvpService();
-        service.setServiceId(new String(serviceIdArr).trim());
+        service.setServiceId(packet.getPacket().getServiceId());
         service.setEvNo(new String(vehNoArr).trim());
         service.setCurLat(currentLat/EvpsProtocolConst.EVPS_GEO_CORRECT);
         service.setCurLng(currentLng/EvpsProtocolConst.EVPS_GEO_CORRECT);
@@ -173,6 +169,7 @@ public class EvpsService implements EvpsCommResponse {
         serviceDto.setService(service);
 
         if (oneFrame) {
+            service.setClctDt(packet.getPacket().getPacketTime());
             ApplicationRepository.serviceMap.put(service.getServiceId(), serviceDto);
 
             this.dbmsDataProcess.add(new DbmsData(DbmsData.DBMS_DATA_INS_SERVICE, center, service));
@@ -188,11 +185,10 @@ public class EvpsService implements EvpsCommResponse {
         }
 
         int routeCount = ((buffer[idx++] & 0xFF) << 8) | (buffer[idx++] & 0xFF);
-        int reqRouteSize = routeCount * 8;  // lat(4), lng(4)
-        int reqDataSize = EvpsProtocolConst.EVPS_HEAD_SIZE + EvpsProtocolConst.MIN_SERVICE_DATA_SIZE + reqRouteSize;
-        if (buffer.length != reqDataSize) {
+        int reqDataSize = EvpsProtocolConst.SERVICE_DATA_ROUTE_SIZE * routeCount;
+        if ((buffer.length - idx) != reqDataSize) {
             log.error("[{}], EvpsService.response: Data Length Error: Required data length({}), Cur({}). will be closed.",
-                    center.getLogKey(), reqDataSize, buffer.length);
+                    center.getLogKey(), reqDataSize, (buffer.length-idx));
             return false;
         }
 
@@ -212,14 +208,10 @@ public class EvpsService implements EvpsCommResponse {
 
             lists.add(param);
         }
-        if (!lists.isEmpty()) {
-            this.dbmsDataProcess.add(new DbmsData(DbmsData.DBMS_DATA_INS_ROUTE, center, lists));
-        }
-
         log.info("[{}], EvpsService.response: Service Route List {} EA.", center.getLogKey(), lists.size());
 
         if (!lists.isEmpty()) {
-            this.dbmsDataProcess.add(new DbmsData(DbmsData.DBMS_DATA_INS_NODE, center, lists));
+            this.dbmsDataProcess.add(new DbmsData(DbmsData.DBMS_DATA_INS_ROUTE, center, lists));
         }
         return true;
     }

+ 4 - 9
src/main/java/com/evp/comm/server/xnet/server/process/response/EvpsServiceEnd.java

@@ -11,10 +11,7 @@ import com.evp.comm.server.xnet.server.process.protocol.EvpsProtocolConst;
 import lombok.extern.slf4j.Slf4j;
 import org.slf4j.MDC;
 
-import java.util.Arrays;
-
 /*
-serviceId       서비스ID        String      19
 reason          종료코드        Integer     4
     1: 진행중 - 서비스 진행중
     2: 정상종료 - 모든 교차로 제어 및 해제 완료
@@ -48,7 +45,7 @@ public class EvpsServiceEnd implements EvpsCommResponse {
             MDC.put("id", packet.getCenter().getLogKey());
             log.info("[{}], EvpsServiceEnd.response.", packet.getCenter().getLogKey());
 
-            return makeData(packet.getCenter(), packet.getPacket().getBuffer());
+            return makeData(packet, packet.getPacket().getBuffer());
         }
         catch (Exception e) {
             log.error("[{}], EvpsServiceEnd.response: Exception. will be closed. {}", packet.getCenter().getLogKey(), e.getMessage());
@@ -61,7 +58,8 @@ public class EvpsServiceEnd implements EvpsCommResponse {
         return result;
     }
 
-    private boolean makeData(EvpsCenter center, byte[] buffer) {
+    private boolean makeData(RecvPacketDto packet, byte[] buffer) {
+        EvpsCenter center = packet.getCenter();
         if (buffer == null || buffer.length != EvpsProtocolConst.EVENT_SERVICE_END_SIZE) {
             log.error("[{}], EvpsServiceEnd.response: Data Length Error: Required data length({}), Cur({}). will be closed.",
                     center.getLogKey(), EvpsProtocolConst.EVENT_SERVICE_END_SIZE, buffer == null ? 0 : buffer.length);
@@ -69,15 +67,12 @@ public class EvpsServiceEnd implements EvpsCommResponse {
         }
 
         int idx = 0;
-        // 서비스ID
-        byte[] serviceIdArr = Arrays.copyOfRange(buffer, idx, idx+EvpsProtocolConst.EVPS_SERVICE_ID_SIZE);
-        idx += EvpsProtocolConst.EVPS_SERVICE_ID_SIZE;
         // 종료코드
         int reason = ((buffer[idx++] & 0xFF) << 24) | ((buffer[idx++] & 0xFF) << 16) | ((buffer[idx++] & 0xFF) << 8) | (buffer[idx++] & 0xFF);
 
         // 서비스 종료 정보 업데이트
         TbEvpService service = new TbEvpService();
-        service.setServiceId(new String(serviceIdArr).trim());
+        service.setServiceId(packet.getPacket().getServiceId());
         service.setStatusCd(reason);
         this.dbmsDataProcess.add(new DbmsData(DbmsData.DBMS_DATA_UPD_SERVICE, center, service));
 

+ 155 - 0
src/main/java/com/evp/comm/server/xnet/server/process/response/EvpsSignal.java

@@ -0,0 +1,155 @@
+package com.evp.comm.server.xnet.server.process.response;
+
+import com.evp.comm.server.dto.EvpsCenter;
+import com.evp.comm.server.dto.EvpsSequenceDto;
+import com.evp.comm.server.dto.EvpsServiceDto;
+import com.evp.comm.server.process.dbms.DbmsData;
+import com.evp.comm.server.process.dbms.DbmsDataProcess;
+import com.evp.comm.server.repository.ApplicationRepository;
+import com.evp.comm.server.xnet.server.process.protocol.EvpsProtocolConst;
+import lombok.extern.slf4j.Slf4j;
+import org.slf4j.MDC;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+/*
+signalList      교차로현황 리스트      SHORT       2
+    intLcNo     교차로 번호            INTEGER     4
+    remainDist  교차로 남은거리        INTEGER     4
+    state       교차로운영상태         BYTE        1
+    plan        현재 운영중인 맵 번호  BYTE        1
+    ARingPhase  A링 현시번호           BYTE        1
+    BRingPhase  B링 현시번호           BYTE        1
+    holdPhase   유지현시번호           BYTE        1
+ */
+
+@Slf4j
+public class EvpsSignal implements EvpsCommResponse {
+    private final DbmsDataProcess dbmsDataProcess;
+
+    public EvpsSignal(DbmsDataProcess dbmsDataProcess) {
+        this.dbmsDataProcess = dbmsDataProcess;
+    }
+
+    @Override
+    public boolean response(RecvPacketDto packet) {
+        boolean result = true;
+        try {
+            MDC.put("id", packet.getCenter().getLogKey());
+            log.info("[{}], EvpsSignal.response.", packet.getCenter().getLogKey());
+
+            if (packet.getPacket().getCurrent() == 1 && packet.getPacket().getTotal() == 1) {
+                // 모든 정보가 하나의 프레임으로 전송된 경우
+                return makeData(packet, packet.getPacket().getBuffer());
+            }
+
+            // 패킷이 분할되어 전송된 경우
+            EvpsServiceDto serviceDto = null;
+            int sequence = packet.getPacket().getSequence();
+            if (packet.getPacket().getCurrent() == 1) {
+                // 첫번째 프레임
+                // sequence 에 대항하는 서비스 정보를 메모리에 저장하고 서비스 ID에 대한 메모리 정보를 얻는다.
+                String serviceId = packet.getPacket().getServiceId();
+                EvpsSequenceDto sequenceDto = new EvpsSequenceDto();
+                sequenceDto.setSequence(sequence);
+                sequenceDto.setServiceId(serviceId);
+                packet.getCenter().getSequenceMap().put(sequence, sequenceDto); // SequenceMap 에 SequenceDto 를 저장한다.
+                // 서비스 정보를 메모리에서 얻어온다.
+                serviceDto = ApplicationRepository.serviceMap.get(serviceId);
+            }
+            else {
+                // 분할된 프레임
+                // sequence 에 대한 정보를 메모리에서 구하고 얻어온 서비스 ID로 서비스 정보를 메모리에서 얻어온다.
+                EvpsSequenceDto sequenceDto = packet.getCenter().getSequenceMap().get(sequence);
+                if (sequenceDto != null) {
+                    // 서비스 정보를 메모리에서 얻어온다.
+                    serviceDto = ApplicationRepository.serviceMap.get(sequenceDto.getServiceId());
+                }
+            }
+
+            if (serviceDto == null) {
+                // 서비스 정보를 메모리에서 얻어 오지 못한 경우.
+                log.error("[{}], EvpsSignal.response: Not Found Service Information. Sequence: {}, Curr: ({}), Tot: ({})",
+                        packet.getCenter().getLogKey(), sequence, packet.getPacket().getCurrent(), packet.getPacket().getTotal());
+                return false;
+            }
+
+            // 서비스 메모리에 교차로 정보 패킷을 순차적으로 적재
+            serviceDto.addNodeBuffer(packet.getPacket().getBuffer());
+            if (packet.getPacket().getCurrent() == packet.getPacket().getTotal()) {
+                // 분할된 프레임을 모두 받았음. 즉 마지막 프레임을 수신한 경우 데이터 처리
+                log.info("[{}], EvpsSignal.response: Recv All Frames. Service Id: {}, Sequence: {}, Curr: ({}), Tot: ({})",
+                        packet.getCenter().getLogKey(), serviceDto.getServiceId(), sequence, packet.getPacket().getCurrent(), packet.getPacket().getTotal());
+
+                // 저장한 프레임 전체를 이용해서 데이터 생성
+                result = makeData(packet, serviceDto.getNodeBuffer().array());
+
+                // SequenceMap 에서 SequenceDto 를 제거한다.
+                packet.getCenter().getSequenceMap().remove(sequence);
+            }
+        }
+        catch (Exception e) {
+            log.error("[{}], EvpsSignal.response: Exception. will be closed. {}", packet.getCenter().getLogKey(), e.getMessage());
+            result = false;
+        }
+        finally {
+            MDC.remove(packet.getCenter().getLogKey());
+            MDC.clear();
+        }
+        return result;
+    }
+
+    private boolean makeData(RecvPacketDto packet, byte[] buffer) {
+        EvpsCenter center = packet.getCenter();
+        if (buffer == null || buffer.length < EvpsProtocolConst.MIN_SIGNAL_DATA_SIZE) {
+            log.error("[{}], EvpsSignal.response: Data Length Error: Required minimum data length({}), Cur({}). will be closed.",
+                    center.getLogKey(), EvpsProtocolConst.MIN_NODE_DATA_SIZE, buffer == null ? 0: buffer.length);
+            return false;
+        }
+
+        String serviceId = packet.getPacket().getServiceId();
+
+        int idx = 0;
+        // 교차로수(2 Byte)
+        int nodeCount = ((buffer[idx++] & 0xFF) << 8) | (buffer[idx++] & 0xFF);
+        int reqDataSize = EvpsProtocolConst.SIGNAL_DATA_SIGNAL_SIZE * nodeCount;
+        if ((buffer.length - idx) < reqDataSize) {
+            log.error("[{}], EvpsSignal.response: Data Length Error: Required data length({}), Cur({}). will be closed.",
+                    center.getLogKey(), reqDataSize, (buffer.length-idx));
+            return false;
+        }
+
+        List<HashMap<String, Object>> lists = new ArrayList<>();
+        for (int ii = 0; ii < nodeCount; ii++) {
+            long nodeId     = ((long) (buffer[idx++] & 0xFF) << 24) | ((buffer[idx++] & 0xFF) << 16) | ((buffer[idx++] & 0xFF) << 8) | (buffer[idx++] & 0xFF);
+            int remDist     = ((buffer[idx++] & 0xFF) << 24) | ((buffer[idx++] & 0xFF) << 16) | ((buffer[idx++] & 0xFF) << 8) | (buffer[idx++] & 0xFF);
+            int state       = buffer[idx++] & 0xFF;
+            int planClass   = buffer[idx++] & 0xFF;
+            int aRingPhase  = buffer[idx++] & 0xFF;
+            int bRingPhase  = buffer[idx++] & 0xFF;
+            int holdPhase   = buffer[idx++] & 0xFF;
+
+            HashMap<String, Object> param = new HashMap<>();
+            param.put("SERVICE_ID",  serviceId);
+            param.put("SEQ_NO",      ii+1);
+            param.put("NODE_ID",     nodeId);
+
+            param.put("REM_DIST",     remDist);
+            param.put("STATE",        state);
+            param.put("PLAN_CLASS",   planClass);
+            param.put("A_RING_PHASE", aRingPhase);
+            param.put("B_RING_PHASE", bRingPhase);
+            param.put("HOLD_PHASE",   holdPhase);
+
+            lists.add(param);
+        }
+        log.info("[{}], EvpsSignal.response: Service Signal List {} EA.", center.getLogKey(), lists.size());
+
+        if (!lists.isEmpty()) {
+            this.dbmsDataProcess.add(new DbmsData(DbmsData.DBMS_DATA_INS_SIGNAL, center, lists));
+        }
+        return true;
+    }
+}

+ 3 - 0
src/main/java/com/evp/comm/server/xnet/server/process/work/DataPacketProcess.java

@@ -68,6 +68,9 @@ public class DataPacketProcess {
                 case EVPS_NODE:
                     response = new EvpsNode(this.dbmsDataProcess);
                     break;
+                case EVPS_SIGNAL:
+                    response = new EvpsSignal(this.dbmsDataProcess);
+                    break;
                 case EVPS_EVENT:
                     response = new EvpsEvent(this.dbmsDataProcess);
                     break;

+ 141 - 0
src/main/resources/mybatis/mapper/EvpsServiceMapper.xml

@@ -99,4 +99,145 @@
         ]]>
     </insert>
 
+    <insert id="insertEvpPhase" parameterType="java.util.Map">
+    <![CDATA[
+        INSERT INTO tb_evp_phase (service_id,
+                                  seq_no,
+                                  node_id,
+                                  ring,
+                                  phase_no,
+                                  plan_class,
+                                  flow_no,
+                                  head_lat,
+                                  head_lng,
+                                  mid_lat,
+                                  mid_lng,
+                                  end_lat,
+                                  end_lng,
+                                  head_angle,
+                                  end_angle)
+        VALUES ( #{SERVICE_ID},
+                 #{SEQ_NO},
+                 #{NODE_ID},
+                 #{RING},
+                 #{PHASE_NO},
+                 #{PLAN_CLASS},
+                 #{FLOW_NO},
+                 #{HEAD_LAT},
+                 #{HEAD_LNG},
+                 #{MID_LAT},
+                 #{MID_LNG},
+                 #{END_LAT},
+                 #{END_LNG},
+                 #{HEAD_ANGLE},
+                 #{END_ANGLE}
+               )
+        ]]>
+    </insert>
+
+    <insert id="insertEvpSignal" parameterType="java.util.Map">
+    <![CDATA[
+        INSERT INTO tb_evp_signal (clct_dt,
+                                   service_id,
+                                   node_id,
+                                   seq_no,
+                                   rem_dist,
+                                   state,
+                                   plan_class,
+                                   a_ring_phase,
+                                   b_ring_phase,
+                                   hold_phase)
+        VALUES ( current_timestamp(),
+                 #{SERVICE_ID},
+                 #{NODE_ID},
+                 #{SEQ_NO},
+                 #{REM_DIST},
+                 #{STATE},
+                 #{PLAN_CLASS},
+                 #{A_RING_PHASE},
+                 #{B_RING_PHASE},
+                 #{HOLD_PHASE}
+               )
+        ]]>
+    </insert>
+
+    <insert id="insertEvpSignal2" parameterType="java.util.Map">
+    <![CDATA[
+        INSERT INTO tb_evp_signal (service_id,
+                                   node_id,
+                                   seq_no,
+                                   clct_dt,
+                                   rem_dist,
+                                   state,
+                                   plan_class,
+                                   a_ring_phase,
+                                   b_ring_phase,
+                                   hold_phase)
+        VALUES ( #{SERVICE_ID},
+                 #{NODE_ID},
+                 #{SEQ_NO},
+                 current_timestamp(),
+                 #{REM_DIST},
+                 #{STATE},
+                 #{PLAN_CLASS},
+                 #{A_RING_PHASE},
+                 #{B_RING_PHASE},
+                 #{HOLD_PHASE}
+               )
+        ON DUPLICATE KEY UPDATE
+             clct_dt       = current_timestamp(),
+             rem_dist      = #{REM_DIST},
+             state         = #{STATE},
+             plan_class    = #{PLAN_CLASS},
+             a_ring_phase  = #{A_RING_PHASE},
+             b_ring_phase  = #{B_RING_PHASE},
+             hold_phase    = #{HOLD_PHASE}
+    ]]>
+    </insert>
+
+<!--    <update id="updateEvpSignalOracle" parameterType="java.util.Map">-->
+<!--    <![CDATA[-->
+<!--        MERGE INTO tb_evp_signal L-->
+<!--            USING(SELECT #{SERVICE_ID} AS service_id,-->
+<!--                         #{SEQ_NO} AS seq_no,-->
+<!--                         #{NODE_ID} AS node_id,-->
+<!--                         #{REM_DIST} AS rem_dist,-->
+<!--                         #{STATE} AS state,-->
+<!--                         #{PLAN_CLASS} AS plan_class,-->
+<!--                         #{A_RING_PHASE} AS a_ring_phase,-->
+<!--                         #{B_RING_PHASE} AS b_ring_phase,-->
+<!--                         #{HOLD_PHASE} AS hold_phase-->
+<!--                  FROM dual A) M-->
+<!--            ON (L.service_id = M.service_id AND A.node_id = B.node_id)-->
+<!--            WHEN MATCHED THEN-->
+<!--                UPDATE SET-->
+<!--                    L.rem_dist      = M.rem_dist,-->
+<!--                    L.state         = M.state,-->
+<!--                    L.plan_class    = M.plan_class,-->
+<!--                    L.a_ring_phase  = M.a_ring_phase,-->
+<!--                    L.b_ring_phase  = M.b_ring_phase,-->
+<!--                    L.hold_phase    = M.hold_phase-->
+<!--            WHEN NOT MATCHED THEN-->
+<!--                INSERT (service_id,-->
+<!--                        seq_no,-->
+<!--                        node_id,-->
+<!--                        rem_dist,-->
+<!--                        state,-->
+<!--                        plan_class,-->
+<!--                        a_ring_phase,-->
+<!--                        b_ring_phase,-->
+<!--                        hold_phase)-->
+<!--                    VALUES (M.service_id,-->
+<!--                            M.seq_no,-->
+<!--                            M.node_id,-->
+<!--                            M.rem_dist,-->
+<!--                            M.state,-->
+<!--                            M.plan_class,-->
+<!--                            M.a_ring_phase,-->
+<!--                            M.b_ring_phase,-->
+<!--                            M.hold_phase)-->
+
+<!--        ]]>-->
+<!--    </update>-->
+
 </mapper>

+ 2 - 1
src/test/java/com/evp/comm/server/EvpCommServerApplicationTests.java

@@ -9,6 +9,7 @@ import io.netty.buffer.ByteBuf;
 import io.netty.buffer.Unpooled;
 import lombok.extern.slf4j.Slf4j;
 import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.context.SpringBootTest;
 
 import java.net.InetAddress;
 import java.net.UnknownHostException;
@@ -18,7 +19,7 @@ import java.util.concurrent.ThreadPoolExecutor;
 import java.util.concurrent.TimeUnit;
 
 @Slf4j
-//@SpringBootTest
+@SpringBootTest
 public class EvpCommServerApplicationTests {
 
     public static final EvpsCenter rCenter = EvpsCenter.builder()