junggilpark преди 1 година
родител
ревизия
4185860a25
променени са 39 файла, в които са добавени 1440 реда и са изтрити 543 реда
  1. 1 1
      conf/tsi-sig-server.pid
  2. 14 2
      src/main/java/com/tsi/sig/server/config/KafkaConsumerConfig.java
  3. 1 1
      src/main/java/com/tsi/sig/server/config/MybatisConfig.java
  4. 6 0
      src/main/java/com/tsi/sig/server/controller/MainController.java
  5. 7 3
      src/main/java/com/tsi/sig/server/mapper/MainMapper.java
  6. 1 1
      src/main/java/com/tsi/sig/server/repository/ApplicationRepository.java
  7. 29 2
      src/main/java/com/tsi/sig/server/service/MainService.java
  8. 32 0
      src/main/java/com/tsi/sig/server/vo/EvpEventVo.java
  9. 8 0
      src/main/java/com/tsi/sig/server/vo/EvpRouteVo.java
  10. 40 1
      src/main/java/com/tsi/sig/server/vo/EvpServiceVo.java
  11. 92 0
      src/main/java/com/tsi/sig/server/vo/EvpSignalVo.java
  12. 1 0
      src/main/java/com/tsi/sig/server/websocket/kafka/AllTopicConsumerThread.java
  13. 4 2
      src/main/resources/application.yml
  14. 263 11
      src/main/resources/mybatis/mapper/main.xml
  15. 229 66
      src/main/resources/static/css/main.css
  16. BIN
      src/main/resources/static/images/drop_down.png
  17. BIN
      src/main/resources/static/images/drop_up.png
  18. 13 0
      src/main/resources/static/images/evp_arr.svg
  19. BIN
      src/main/resources/static/images/evp_legend.png
  20. BIN
      src/main/resources/static/images/logo2.png
  21. BIN
      src/main/resources/static/images/logo3.png
  22. BIN
      src/main/resources/static/images/sig1.png
  23. BIN
      src/main/resources/static/images/sig2.png
  24. BIN
      src/main/resources/static/images/sig3.png
  25. BIN
      src/main/resources/static/images/sig4.png
  26. BIN
      src/main/resources/static/images/sig5.png
  27. BIN
      src/main/resources/static/images/tab01_on.png
  28. BIN
      src/main/resources/static/images/tab01_on_2.png
  29. BIN
      src/main/resources/static/images/tab02_off.png
  30. BIN
      src/main/resources/static/images/tab02_on.png
  31. 1 1
      src/main/resources/static/js/common/common-ajax.js
  32. 1 1
      src/main/resources/static/js/cvib.js
  33. 544 325
      src/main/resources/static/js/map.js
  34. 11 8
      src/main/resources/static/js/signal.js
  35. 23 19
      src/main/resources/static/js/worker.js
  36. 2 42
      src/main/webapp/WEB-INF/jsp/bottomListFrame.jsp
  37. 32 15
      src/main/webapp/WEB-INF/jsp/cvibInfoDetail.jsp
  38. 18 29
      src/main/webapp/WEB-INF/jsp/main.jsp
  39. 67 13
      src/main/webapp/WEB-INF/jsp/treeListFrame.jsp

+ 1 - 1
conf/tsi-sig-server.pid

@@ -1 +1 @@
-30436
+7484

+ 14 - 2
src/main/java/com/tsi/sig/server/config/KafkaConsumerConfig.java

@@ -17,6 +17,8 @@ import org.springframework.kafka.listener.KafkaMessageListenerContainer;
 import org.springframework.stereotype.Component;
 
 import javax.annotation.PostConstruct;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
 import java.util.*;
 import java.util.concurrent.ExecutionException;
 
@@ -29,20 +31,30 @@ public class KafkaConsumerConfig {
     //기존
     //private String groupId = "tsi-sig-server";
     //변경
-    private String groupId = "tsi-sig-server-was";
+    private String groupId = "tsi-sig-was";
     private String bootstrapServers;
     @Value("${all-topic:cvim-raw}")
     private String allTopic;
     public List<Map<String, String>> props = new ArrayList<Map<String, String>>();
 
     @PostConstruct
-    private void init() {
+    private void init() throws UnknownHostException {
 
         log.info("[{}] --------------------", this.getClass().getSimpleName());
         log.info("[{}]          groupId: {}", this.getClass().getSimpleName(), this.groupId);
         log.info("[{}] bootstrapServers: {}", this.getClass().getSimpleName(), this.bootstrapServers);
         log.info("[{}]         allTopic: {}", this.getClass().getSimpleName(), this.allTopic);
         log.info("[{}]            props: {}", this.getClass().getSimpleName(), this.props.toArray());
+        String hostName;
+        try {
+            InetAddress localhost = InetAddress.getLocalHost();
+            hostName = localhost.getHostName();
+           // hostAddress = localhost.getHostAddress();
+        } catch (UnknownHostException e) {
+            hostName = UUID.randomUUID().toString();
+            //hostAddress = "127.0.0.1";
+        }
+        this.groupId = this.groupId + hostName;
     }
 
     public Properties getConsumerProperties() {

+ 1 - 1
src/main/java/com/tsi/sig/server/config/MybatisConfig.java

@@ -44,7 +44,7 @@ public class MybatisConfig {
             throws Exception {
         SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
         factoryBean.setDataSource(dataSource);
-        //factoryBean.setConfigLocation(applicationContext.getResource("classpath:mapper/mapper/mapper-config.xml"));
+        factoryBean.setConfigLocation(applicationContext.getResource("classpath:mybatis/mybatis-config.xml"));
         factoryBean.setMapperLocations(applicationContext.getResources(this.mapperLocations));
         return factoryBean.getObject();
     }

+ 6 - 0
src/main/java/com/tsi/sig/server/controller/MainController.java

@@ -156,4 +156,10 @@ public class MainController {
     public List<EvpRouteVo> getEvpRouteList(@RequestParam HashMap<String,Object> paramMap) throws Exception {
         return mainService.getEvpRouteList(paramMap);
     }
+
+    @RequestMapping(value = "/getEvpHistory.do", method = RequestMethod.POST)
+    @ResponseBody
+    public List<EvpServiceVo> getEvpHistory(@RequestParam HashMap<String,Object> paramMap) throws Exception {
+        return mainService.getEvpHistoryList(paramMap);
+    }
 }

+ 7 - 3
src/main/java/com/tsi/sig/server/mapper/MainMapper.java

@@ -5,6 +5,7 @@ import org.apache.ibatis.annotations.Mapper;
 
 import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 @Mapper
 public interface MainMapper {
@@ -13,8 +14,11 @@ public interface MainMapper {
     List<CvibNodeVO> getCvibNodeInfo(HashMap<String, Object> vo);
     List<CvibNodeVO> getCvibMapNodeList(HashMap<String, Object> vo);
     List<EvpServiceVo> getEvpServiceList();
-    List<EvpRouteVo> getEvpRouteList(HashMap<String, Object> vo);
+    List<EvpServiceVo> getEvpHistoryList(Map<String, Object> vo);
+    List<EvpRouteVo> getEvpRouteList(Map<String, Object> vo);
     List<EvpPhaseVo> getEvpPhaseList(HashMap<String, Object> vo);
-    List<EvpSignalVo> getEvpSignalList(HashMap<String, Object> vo);
-
+    List<EvpSignalVo> getEvpSignalList(Map<String, Object> vo);
+    List<EvpEventVo> getEvpEventList(Map<String, Object> paramMap);
+    List<EvpSignalVo> getEvpSignalCurrList(Map<String, Object> paramMap);
+    List<EvpEventVo> getEvpEventCurrList(Map<String, Object> paramMap);
 }

+ 1 - 1
src/main/java/com/tsi/sig/server/repository/ApplicationRepository.java

@@ -51,7 +51,7 @@ public class ApplicationRepository {
                     newVo.setCommStatus(false);
                 }
                 else if(!vo.commStatus) {
-                    log.warn("=========== 통신 이상...  nodeId : {}",vo.nodeId);
+                   // log.warn("=========== 통신 이상...  nodeId : {}",vo.nodeId);
                 }
 
             }

+ 29 - 2
src/main/java/com/tsi/sig/server/service/MainService.java

@@ -188,10 +188,37 @@ public class MainService {
     }
 
     public List<EvpServiceVo> getEvpServiceList() {
-        return this.mainMapper.getEvpServiceList();
+        List<EvpServiceVo> result = this.mainMapper.getEvpServiceList();
+        //log.info("{}", );
+        if (result.size() > 0) {
+            for (EvpServiceVo vo : result) {
+                Map<String, Object> paramMap = new HashMap<>();
+                paramMap.put("serviceId", vo.getServiceId());
+                vo.setRouteList(this.mainMapper.getEvpRouteList(paramMap));
+                vo.setPhaseList(this.mainMapper.getEvpSignalCurrList(paramMap));
+                vo.setEventList(this.mainMapper.getEvpEventCurrList(paramMap));
+            }
+        }
+        return result;
     }
 
-    public List<EvpRouteVo> getEvpRouteList(HashMap<String, Object> paramMap) {
+    public List<EvpRouteVo> getEvpRouteList(Map<String, Object> paramMap) {
         return this.mainMapper.getEvpRouteList(paramMap);
     }
+
+    public List<EvpSignalVo> getEvpSignalList(Map<String, Object> paramMap) {
+        return this.mainMapper.getEvpSignalList(paramMap);
+    }
+
+    public List<EvpServiceVo> getEvpHistoryList(Map<String, Object> paramMap) {
+        List<EvpServiceVo> result = this.mainMapper.getEvpHistoryList(paramMap);
+        if (result != null && result.size() > 0) {
+            for (EvpServiceVo vo : result) {
+                vo.setRouteList(this.mainMapper.getEvpRouteList(paramMap));
+                vo.setPhaseList(this.mainMapper.getEvpSignalList(paramMap));
+                vo.setEventList(this.mainMapper.getEvpEventList(paramMap));
+            }
+        }
+        return result;
+    }
 }

+ 32 - 0
src/main/java/com/tsi/sig/server/vo/EvpEventVo.java

@@ -0,0 +1,32 @@
+package com.tsi.sig.server.vo;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+@Data
+public class EvpEventVo {
+
+    @JsonProperty("clct_dt")
+    private String clctDt;
+
+    @JsonProperty("service_id")
+    private String serviceId;
+
+    @JsonProperty("event_cd")
+    private Integer eventCd;
+
+    @JsonProperty("event_desc")
+    private String eventDesc;
+
+    @JsonProperty("cur_lat")
+    private Double curLat;
+
+    @JsonProperty("cur_lng")
+    private Double curLng;
+
+    @JsonProperty("cur_spd")
+    private Integer curSpd;
+
+    @JsonProperty("rem_dist")
+    private Long remDist;
+}

+ 8 - 0
src/main/java/com/tsi/sig/server/vo/EvpRouteVo.java

@@ -1,11 +1,19 @@
 package com.tsi.sig.server.vo;
 
+import com.fasterxml.jackson.annotation.JsonProperty;
 import lombok.Data;
 
 @Data
 public class EvpRouteVo {
+    @JsonProperty("service_id")
     private String serviceId;
+
+    @JsonProperty("seq_no")
     private Long seqNo;
+
+    @JsonProperty("lat")
     private double lat;
+
+    @JsonProperty("lng")
     private double lng;
 }

+ 40 - 1
src/main/java/com/tsi/sig/server/vo/EvpServiceVo.java

@@ -1,22 +1,61 @@
 package com.tsi.sig.server.vo;
 
+import com.fasterxml.jackson.annotation.JsonProperty;
 import lombok.Data;
 
+import java.util.List;
+
 @Data
 public class EvpServiceVo {
+    @JsonProperty("service_id")
     private String serviceId;
+
+    @JsonProperty("clct_dt")
     private String clctDt;
+
+    @JsonProperty("ev_no")
     private String evNo;
+
+    @JsonProperty("cur_lat")
     private double curLat;
+
+    @JsonProperty("cur_lng")
     private double curLng;
+
+    @JsonProperty("service_nm")
     private String serviceNm;
+
+    @JsonProperty("arr_lat")
     private double arrLat;
+
+    @JsonProperty("arr_lng")
     private double arrLng;
+
+    @JsonProperty("arr_tm")
     private long arrTm;
+
+    @JsonProperty("veh_len")
     private long vehLen;
+
+    @JsonProperty("ocr_no")
     private String ocrNo;
+
+    @JsonProperty("ocr_type")
     private String ocrType;
+
+    @JsonProperty("service_dist")
     private long serviceDist;
+
+    @JsonProperty("status_cd")
     private int statusCd;
-    private String statusDesc;
+
+    @JsonProperty("route_list")
+    private List<EvpRouteVo> routeList;
+
+    @JsonProperty("phase_list")
+    private List<EvpSignalVo> phaseList;
+
+    @JsonProperty("event_list")
+    private List<EvpEventVo> eventList;
+    //private String statusDesc;
 }

+ 92 - 0
src/main/java/com/tsi/sig/server/vo/EvpSignalVo.java

@@ -1,8 +1,100 @@
 package com.tsi.sig.server.vo;
 
+import com.fasterxml.jackson.annotation.JsonProperty;
 import lombok.Data;
 
 @Data
 public class EvpSignalVo {
+    @JsonProperty("seq_no")
+    private Integer seqNo;
 
+    @JsonProperty("clct_dt")
+    private String clctDt;
+
+    @JsonProperty("service_id")
+    private String serviceId;
+
+    @JsonProperty("node_id")
+    private Long nodeId;
+
+    @JsonProperty("node_nm")
+    private String nodeNm;
+
+    @JsonProperty("rem_dist")
+    private Long remDist;
+
+    @JsonProperty("state")
+    private Integer state;
+
+    @JsonProperty("plan_class")
+    private Integer planClass;
+
+    @JsonProperty("a_ring_phase")
+    private Integer aRingPhase;
+
+    @JsonProperty("b_ring_phase")
+    private Integer bRingPhase;
+
+    @JsonProperty("hold_phase")
+    private Integer holdPhase;
+
+    @JsonProperty("lat")
+    private Double lat;
+
+    @JsonProperty("lng")
+    private Double lng;
+
+    @JsonProperty("a_flow_no")
+    private Integer aFlowNo;
+
+    @JsonProperty("a_head_lat")
+    private Double aHeadLat;
+
+    @JsonProperty("a_head_lng")
+    private Double aHeadLng;
+
+    @JsonProperty("a_head_angle")
+    private Integer aHeadAngle;
+
+    @JsonProperty("a_mid_lat")
+    private Double aMidLat;
+
+    @JsonProperty("a_mid_lng")
+    private Double aMidLng;
+
+    @JsonProperty("a_end_lat")
+    private Double aEndLat;
+
+    @JsonProperty("a_end_lng")
+    private Double aEndLng;
+
+    @JsonProperty("a_end_angle")
+    private Integer aEndAngle;
+
+    @JsonProperty("b_flow_no")
+    private Integer bFlowNo;
+
+    @JsonProperty("b_head_lat")
+    private Double bHeadLat;
+
+    @JsonProperty("b_head_lng")
+    private Double bHeadLng;
+
+    @JsonProperty("b_head_angle")
+    private Integer bHeadAngle;
+
+    @JsonProperty("b_mid_lat")
+    private Double bMidLat;
+
+    @JsonProperty("b_mid_lng")
+    private Double bMidLng;
+
+    @JsonProperty("b_end_lat")
+    private Double bEndLat;
+
+    @JsonProperty("b_end_lng")
+    private Double bEndLng;
+
+    @JsonProperty("b_end_angle")
+    private Integer bEndAngle;
 }

+ 1 - 0
src/main/java/com/tsi/sig/server/websocket/kafka/AllTopicConsumerThread.java

@@ -115,6 +115,7 @@ public class AllTopicConsumerThread implements Runnable {
         if (turns.size() == 0) {
             node.commStatus = false;
         }
+
     }
 
     public List<String> formatPartitions(Collection<TopicPartition> partitions) {

+ 4 - 2
src/main/resources/application.yml

@@ -64,7 +64,7 @@ application:
   kafka:
     consumer:
       #group-id: tsi-sig-server
-      group-id: tsi-sig-server-was
+      group-id: tsi-sig-was
       all-topic: cvim-raw
       #props:
       #  - request.timeout.ms: 100
@@ -99,10 +99,12 @@ spring:
     mybatis:
       #jdbc-url: jdbc:mariadb://59.29.208.150:23307/cvim_db?characterEncoding=UTF-8&serverTimezone=UTC
       #jdbc-url: jdbc:mariadb://61.108.209.105:3306/cvim_db?characterEncoding=UTF-8&serverTimezone=UTC
-      jdbc-url: jdbc:mariadb://10.4.4.20:3306/cvim_db?characterEncoding=UTF-8&serverTimezone=UTC
+      #jdbc-url: jdbc:mariadb://10.4.4.20:3306/cvim_db?characterEncoding=UTF-8&serverTimezone=UTC
+      jdbc-url: jdbc:mariadb://115.91.94.42:13306/cvim_db?characterEncoding=UTF-8&serverTimezone=UTC
 
 application:
   kafka:
     consumer:
       bootstrap-servers: 61.82.138.91:19091,61.82.138.91:19092,61.82.138.91:19093
       #bootstrap-servers: 123.142.27.53:9092,123.142.27.53:9093,123.142.27.53:9094
+

+ 263 - 11
src/main/resources/mybatis/mapper/main.xml

@@ -35,6 +35,42 @@
 						10:자동종료-경로이탈,11:자동종료-경로진입 가능시간 초과,12:자동종료-정차가능시간 초과,13:취소-모든 교차로 제어및 해제 완료,
 						14:실패-서비스 제어 요청 실패,15:실패-서비스 가능 교차로가 존재하지 않음,16:자동종료-위치정보 수신 가능 시간 초과)	-->
 	<select id="getEvpServiceList" resultType="com.tsi.sig.server.vo.EvpServiceVo">
+		SELECT SERVICE_ID,
+			   CLCT_DT,
+			   EV_NO,
+			   CUR_LAT,
+			   CUR_LNG,
+			   SERVICE_NM,
+			   ARR_LAT,
+			   ARR_LNG,
+			   ARR_TM,
+			   VEH_LEN,
+			   OCR_NO,
+			   OCR_TYPE,
+			   SERVICE_DIST,
+			   STATUS_CD
+-- 			   IF(STATUS_CD = 1,  '진행중',
+-- 			   IF(STATUS_CD = 2,  '정상종료',
+-- 			   IF(STATUS_CD = 3,  '취소-아직 통과하지 않은 교차로 존재',
+-- 			   IF(STATUS_CD = 4,  '센터강제종료-운영자가 서비스를 강제 종료',
+-- 			   IF(STATUS_CD = 5,  '비정상종료-서비스가 존재하지 않음',
+-- 			   IF(STATUS_CD = 6,  '서비스 시작 실패',
+-- 			   IF(STATUS_CD = 7,  '비정상정료-앱서버에 에러 발생',
+-- 			   IF(STATUS_CD = 8,  '비정상종료-일정시간 앱에서 위치 및 속도 정보가 오지 않음',
+-- 			   IF(STATUS_CD = 9,  '자동종료-경로이탈',
+-- 			   IF(STATUS_CD = 10, '자동종료-경로진입 가능시간 초과',
+-- 			   IF(STATUS_CD = 11, '자동종료-정차가능시간 초과',
+-- 			   IF(STATUS_CD = 12, '취소',
+-- 			   IF(STATUS_CD = 13, '실패-서비스 제어 요청',
+-- 			   IF(STATUS_CD = 14, '실패-서비스 가능 교차로가 존재하지 않음',
+-- 			   IF(STATUS_CD = 15, '자동종료', '-'
+-- 			   ))))))))))))))) AS STATUS_DESC
+		FROM tb_evp_service
+		WHERE STATUS_CD = 1
+		ORDER BY CLCT_DT
+	</select>
+
+	<select id="getEvpHistoryList" parameterType="java.util.HashMap" resultType="com.tsi.sig.server.vo.EvpServiceVo">
 		SELECT SERVICE_ID,
 			   CLCT_DT,
 			   EV_NO,
@@ -66,16 +102,23 @@
 			   IF(STATUS_CD = 15, '자동종료', '-'
 			   ))))))))))))))) AS STATUS_DESC
 		FROM tb_evp_service
-		WHERE DATE_FORMAT(CLCT_DT, '%Y-%m-%d') = CURDATE()
+		<if test="serviceId != null and !serviceId.equals('')">
+			WHERE SERVICE_ID = #{serviceId}
+		</if>
+		ORDER BY CLCT_DT
 	</select>
+
 	<select id="getEvpRouteList" parameterType="java.util.HashMap" resultType="com.tsi.sig.server.vo.EvpRouteVo">
 		SELECT SERVICE_ID,
 		       SEQ_NO,
 		       LAT,
 		       LNG
-		  FROM TB_EVP_ROUTE
+		  FROM tb_evp_route
 		WHERE SERVICE_ID = #{serviceId}
+		  AND LAT BETWEEN 33.10000000 AND 38.45000000
+		  AND LNG BETWEEN 125.06666667 AND 131.87222222
 	</select>
+
 	<select id="getEvpPhaseList" parameterType="java.util.HashMap" resultType="com.tsi.sig.server.vo.EvpPhaseVo">
 		SELECT SERVICE_ID,
 			   SEQ_NO,
@@ -96,14 +139,223 @@
 	</select>
 
 	<select id="getEvpSignalList" parameterType="java.util.HashMap" resultType="com.tsi.sig.server.vo.EvpSignalVo">
-		SELECT SERVICE_ID,
-			   SEQ_NO,
-			   NODE_ID,
-			   STATE,
-			   PLAN_CLASS,
-			   A_RING_PHASE,
-			   B_RING_PHASE,
-			   HOLD_PHASE
-		FROM TB_EVP_SIGNAL
+		SELECT
+			A.seq_no,
+			DATE_FORMAT(A.clct_dt, '%Y-%m-%d %h:%i:%s') clct_dt,
+			A.service_id,
+			A.node_id,
+			D.node_nm,
+			A.rem_dist,
+			A.state,
+			A.plan_class,
+			A.a_ring_phase,
+			A.b_ring_phase,
+			A.hold_phase,
+			D.lat,
+			D.lng,
+			B.flow_no a_flow_no,
+			B.head_lat a_head_lat,
+			B.head_lng a_head_lng,
+			B.head_angle a_head_angle,
+			B.mid_lat a_mid_lat,
+			B.mid_lng a_mid_lng,
+			B.end_lat a_end_lat,
+			B.end_lng a_end_lng,
+			B.end_angle a_end_angle,
+			C.flow_no b_flow_no,
+			C.head_lat b_head_lat,
+			C.head_lng b_head_lng,
+			C.head_angle b_head_angle,
+			C.mid_lat b_mid_lat,
+			C.mid_lng b_mid_lng,
+			C.end_lat b_end_lat,
+			C.end_lng b_end_lng,
+			C.end_angle b_end_angle
+		FROM (
+		       select
+					  seq_no,
+					  clct_dt,
+					  service_id,
+					  node_id,
+					  rem_dist,
+					  state,
+					  plan_class,
+					  if (state = 5, hold_phase, a_ring_phase) a_ring_phase,
+					  if (state = 5, hold_phase, b_ring_phase) b_ring_phase,
+					  hold_phase
+			     from tb_evp_signal
+		     ) A,
+			 (select
+				  *
+			  from tb_evp_phase
+			  where ring = 1
+				and head_lat BETWEEN 33.10000000 AND 38.45000000
+				and head_lng BETWEEN 125.06666667 AND 131.87222222
+				and mid_lat BETWEEN 33.10000000 AND 38.45000000
+				and mid_lng BETWEEN 125.06666667 AND 131.87222222
+				and end_lat BETWEEN 33.10000000 AND 38.45000000
+				and end_lng BETWEEN 125.06666667 AND 131.87222222
+			 ) B,
+			 (
+				 select
+					 *
+				 from tb_evp_phase
+				 where ring = 2
+				   and head_lat BETWEEN 33.10000000 AND 38.45000000
+				   and head_lng BETWEEN 125.06666667 AND 131.87222222
+				   and mid_lat BETWEEN 33.10000000 AND 38.45000000
+				   and mid_lng BETWEEN 125.06666667 AND 131.87222222
+				   and end_lat BETWEEN 33.10000000 AND 38.45000000
+				   and end_lng BETWEEN 125.06666667 AND 131.87222222
+			 ) C,
+			 tb_evp_node D
+		where (1=1)
+		  and A.service_id   = #{serviceId}
+		  and A.seq_no 		 = B.seq_no
+		  and A.node_id 	 = B.node_id
+		  and A.service_id 	 = B.service_id
+		  and A.a_ring_phase = B.phase_no
+		  and A.plan_class 	 = B.plan_class
+		  and A.node_id 	 = C.node_id
+		  and A.service_id 	 = C.service_id
+		  and A.b_ring_phase = C.phase_no
+		  and A.seq_no 		 = C.seq_no
+		  and A.plan_class 	 = C.plan_class
+		  and A.service_id 	 = D.service_id
+		  and A.node_id 	 = D.node_id
+		  and A.seq_no 		 = D.seq_no
+		ORDER BY clct_dt, A.seq_no;
+	</select>
+
+	<select id="getEvpSignalCurrList" parameterType="java.util.HashMap" resultType="com.tsi.sig.server.vo.EvpSignalVo">
+		SELECT
+			A.seq_no,
+			DATE_FORMAT(A.clct_dt, '%Y-%m-%d %h:%i:%s') clct_dt,
+			A.service_id,
+			A.node_id,
+			D.node_nm,
+			A.rem_dist,
+			A.state,
+			A.plan_class,
+			A.a_ring_phase,
+			A.b_ring_phase,
+			A.hold_phase,
+			D.lat,
+			D.lng,
+			B.flow_no a_flow_no,
+			B.head_lat a_head_lat,
+			B.head_lng a_head_lng,
+			B.head_angle a_head_angle,
+			B.mid_lat a_mid_lat,
+			B.mid_lng a_mid_lng,
+			B.end_lat a_end_lat,
+			B.end_lng a_end_lng,
+			B.end_angle a_end_angle,
+			C.flow_no b_flow_no,
+			C.head_lat b_head_lat,
+			C.head_lng b_head_lng,
+			C.head_angle b_head_angle,
+			C.mid_lat b_mid_lat,
+			C.mid_lng b_mid_lng,
+			C.end_lat b_end_lat,
+			C.end_lng b_end_lng,
+			C.end_angle b_end_angle
+		FROM (
+				 select
+					 seq_no,
+					 clct_dt,
+					 service_id,
+					 node_id,
+					 rem_dist,
+					 state,
+					 plan_class,
+					 if (state = 5, hold_phase, a_ring_phase) a_ring_phase,
+					 if (state = 5, hold_phase, b_ring_phase) b_ring_phase,
+					 hold_phase
+				 from tb_evp_signal_curr
+			 ) A,
+			 (select
+				  *
+			  from tb_evp_phase
+			  where ring = 1
+				and head_lat BETWEEN 33.10000000 AND 38.45000000
+				and head_lng BETWEEN 125.06666667 AND 131.87222222
+				and mid_lat BETWEEN 33.10000000 AND 38.45000000
+				and mid_lng BETWEEN 125.06666667 AND 131.87222222
+				and end_lat BETWEEN 33.10000000 AND 38.45000000
+				and end_lng BETWEEN 125.06666667 AND 131.87222222
+			 ) B,
+			 (
+				 select
+					 *
+				 from tb_evp_phase
+				 where ring = 2
+				   and head_lat BETWEEN 33.10000000 AND 38.45000000
+				   and head_lng BETWEEN 125.06666667 AND 131.87222222
+				   and mid_lat BETWEEN 33.10000000 AND 38.45000000
+				   and mid_lng BETWEEN 125.06666667 AND 131.87222222
+				   and end_lat BETWEEN 33.10000000 AND 38.45000000
+				   and end_lng BETWEEN 125.06666667 AND 131.87222222
+			 ) C,
+			 tb_evp_node D
+		where (1=1)
+		  and A.service_id   = #{serviceId}
+		  and A.seq_no 		 = B.seq_no
+		  and A.node_id 	 = B.node_id
+		  and A.service_id 	 = B.service_id
+		  and A.a_ring_phase = B.phase_no
+		  and A.plan_class 	 = B.plan_class
+		  and A.node_id 	 = C.node_id
+		  and A.service_id 	 = C.service_id
+		  and A.b_ring_phase = C.phase_no
+		  and A.seq_no 		 = C.seq_no
+		  and A.plan_class 	 = C.plan_class
+		  and A.service_id 	 = D.service_id
+		  and A.node_id 	 = D.node_id
+		  and A.seq_no 		 = D.seq_no
+		ORDER BY clct_dt, A.seq_no;
+	</select>
+
+	<select id="getEvpEventList" parameterType="java.util.HashMap" resultType="com.tsi.sig.server.vo.EvpEventVo">
+		SELECT
+			DATE_FORMAT(clct_dt, '%Y-%m-%d %h:%i:%s') clct_dt,
+			service_id,
+			event_cd,
+			IF(event_cd = 0,  '서비스시작',
+		    IF(event_cd = 1,  '차량위치',
+			IF(event_cd = 2,  '서비스종료', '정보없음'
+			))) AS event_desc,
+			cur_lat,
+			cur_lng,
+			cur_spd,
+			rem_dist
+		FROM
+			tb_evp_event
+		WHERE service_id = #{serviceId}
+		  AND cur_lat BETWEEN 33.10000000 AND 38.45000000
+		  AND cur_lng BETWEEN 125.06666667 AND 131.87222222
+		ORDER BY clct_dt
+	</select>
+
+
+	<select id="getEvpEventCurrList" parameterType="java.util.HashMap" resultType="com.tsi.sig.server.vo.EvpEventVo">
+		SELECT
+			DATE_FORMAT(clct_dt, '%Y-%m-%d %h:%i:%s') clct_dt,
+			service_id,
+			event_cd,
+			IF(event_cd = 0,  '서비스시작',
+		      IF(event_cd = 1,  '차량위치',
+			   IF(event_cd = 2,  '서비스종료', '정보없음'
+				   ))) AS event_desc,
+			cur_lat,
+			cur_lng,
+			cur_spd,
+			rem_dist
+		FROM
+			tb_evp_event_curr
+		WHERE service_id = #{serviceId}
+		  AND cur_lat BETWEEN 33.10000000 AND 38.45000000
+		  AND cur_lng BETWEEN 125.06666667 AND 131.87222222
+		ORDER BY clct_dt
 	</select>
 </mapper>

+ 229 - 66
src/main/resources/static/css/main.css

@@ -19,6 +19,9 @@ body, html {
     min-width: 640px;
     overflow: hidden;
 }
+body {
+    background-color: #212121;
+}
 
 a {
     text-decoration: none;
@@ -90,7 +93,7 @@ table {
     /* border-right: 1px solid #bbb;
     border-top: 1px solid #bbb;
     border-left: 1px solid #bbb; */
-    border: 1px solid #bbb;
+    border: 1px solid #595959;
     margin: 5px 0 0 332px;
 }
 
@@ -168,7 +171,7 @@ table {
 .crossLoadMap {
     width: 40%;
     height: 450px;
-    border: 1px solid #bbb;
+    border: 1px solid #595959;
 
     z-index: 1;
     float: left;
@@ -178,8 +181,7 @@ table {
 .crossLoadInfo {
     width: 58.9%;
     height: 450px;
-    border: 1px solid #bbb;
-
+    border: 1px solid #595959;
     z-index: 1;
     float: left;
 }
@@ -404,7 +406,7 @@ table {
     height: 24px;
     width: 430px;
     position: absolute;
-    left: 10px;
+    left: 40px;
     top: 3px;
 }
 
@@ -1100,13 +1102,18 @@ div.popState dl.jaywork dd strong.rblue {color:#607cd4; text-align:right;}
 .headMenu {
     width: 100%;
     height: 61px;
-    background-color: #fff;
-    /*border: 1px solid #bbb;*/
+    background-color: #000000;
+    border: 1px solid #595959;
 }
 
 .headMenu h1 {
     float: left;
-    width: 329px
+    width: 329px;
+    height: 100%;
+    box-sizing: border-box;
+    display: flex;
+    align-items: center;
+    padding-left: 10px;
 }
 
 .headMenu ul li {
@@ -1145,15 +1152,15 @@ div.popState dl.jaywork dd strong.rblue {color:#607cd4; text-align:right;}
     height: calc(100% - 75%);
     overflow: hidden;
     border: 0;
-    border-bottom: 1px solid #bbb;
-    border-left: 1px solid #bbb;
+    border-bottom: 1px solid #595959;
+    border-left: 1px solid #595959;
     z-index: 50
 }
 
 .leftMenu {
-    height: calc(100% - 10px);
+    height: calc(100% - 8px);
     background-color: #fff;
-    border: 1px solid #bbb
+    border: 1px solid #595959;
 }
 
 .leftMenu h1 {
@@ -1163,7 +1170,8 @@ div.popState dl.jaywork dd strong.rblue {color:#607cd4; text-align:right;}
 
 .leftMenu .tabs {
     height: 30px;
-    border-bottom: 2px solid #1e65c5;
+    border-bottom: 2px solid #3396ff;
+    background-color: #000000;
 }
 
 .leftMenu .tabs li {
@@ -1173,19 +1181,26 @@ div.popState dl.jaywork dd strong.rblue {color:#607cd4; text-align:right;}
 /* 트리리스트 */
 
 /* .leftMenu .iframeTreeList{width:95%;height:calc(100% - 45px);overflow:auto;padding:5px} */
+.leftMenu .historyBox,
 .leftMenu .searchBox {
     width: 100%;
     height: 40px;
-    border-bottom: 1px solid #dddddd;
+    border-bottom: 1px solid #595959;
     display: flex;
     gap: 5px;
     justify-content: center;
     align-items: center;
+    background-color: black;
+}
+
+.leftMenu .historyBox {
+    display: none;
 }
+.leftMenu .historyBox .search-button,
 .leftMenu .searchBox .search-button {
-    padding: 6px;
+    padding: 4px;
     border: 1px solid #eeeeee;
-    background-color: #1e65c5;
+    background-color: #3396ff;
     color: white;
     width: 50px;
     display: flex;
@@ -1206,22 +1221,29 @@ div.popState dl.jaywork dd strong.rblue {color:#607cd4; text-align:right;}
 }
 
 .leftMenu #intTree {
-    height: calc(100% - 82px);
+    height: calc(100% - 83px);
     overflow: auto;
     padding: 5px
 }
 
-.leftMenu #groupTree {
+.leftMenu #historyList {
     display: none;
     height: calc(100% - 45px);
     overflow: auto;
-    padding: 5px
+    padding: 5px;
+    background-color: #212121;
+    box-sizing: border-box;
+    width: 100%;
+}
+
+.leftMenu #historyList table {
+    width: 100%;
 }
 
 .leftMenu .intSearchMenu {
     height: 70px;
     padding: 10px 0 0 20px;
-    border-bottom: 1px solid #bbb
+    border-bottom: 1px solid #595959
 }
 
 .leftMenu .intSearchMenu label {
@@ -1283,8 +1305,8 @@ div.popState dl.jaywork dd strong.rblue {color:#607cd4; text-align:right;}
 }
 
 .leftMenu .intList .intListMenu {
-    border-top: 2px solid #444;
-    border-bottom: 1px solid #bababa;
+    border-top: 2px solid #595959;
+    border-bottom: 1px solid #595959;
     font-weight: bold;
     color: #2d2d2d;
     height: 30px;
@@ -1357,14 +1379,18 @@ div.popState dl.jaywork dd strong.rblue {color:#607cd4; text-align:right;}
 
 .wideMap {
     position: absolute;
-    top: 40%;
+    /*top: 40%;*/
+    top: 10px;
+    left: -10px;
     cursor: pointer;
-    z-index: 10
+    z-index: 10;
+    rotate: 90deg;
+
 }
 
 .upDownMap {
     position: absolute;
-    left: 45%;
+    right: 0;
     bottom: 0px;
     cursor: pointer;
     z-index: 10
@@ -1395,28 +1421,37 @@ div.popState dl.jaywork dd strong.rblue {color:#607cd4; text-align:right;}
 
 .bottomMenu .bottomHead table {
     width: 100%;
+    border-collapse: separate;
+    border-spacing: 0px;
 }
 
-.bottomMenu .bottomHead table thead {
+.bottomMenu .bottomHead table thead,
+.bottomMenu .bottomBody table thead {
     background-color: #dfe8ef
 }
 
+.bottomMenu .bottomBody table th,
 .bottomMenu .bottomHead table th {
     height: 20px;
     padding: 5px;
     text-align: center;
     font-weight: bold;
-    color: #2d2d2d;
-    background: url(/images/table_bg.png) repeat-x;
+    color: #b2b2b2;
+    /*background: url(/images/table_bg.png) repeat-x;*/
+    background-color: black;
     line-height: 20px;
-    border-top: 1px solid #bababa;
-    border-bottom: 1px solid #bababa;
-    border-right: 1px solid #bababa
+    /*border-top: 1px solid #595959;*/
+    border-bottom: 1px solid #595959;
+    border-right: 1px solid #595959;
+    position: sticky;
+    top: 0;
+    left: 0;
 }
 
 .bottomMenu .bottomBody {
-    max-height: calc(100% - 40px);
-    overflow-y: scroll
+    max-height: 100%;
+    /*overflow-y: scroll*/
+    overflow: auto;
 }
 
 .bottomMenu .bottomBody table {
@@ -1430,15 +1465,19 @@ div.popState dl.jaywork dd strong.rblue {color:#607cd4; text-align:right;}
 .bottomMenu .bottomBody table td {
     padding: 5px;
     text-align: center;
-    color: #515151;
-    border-bottom: 1px solid #bababa;
-    border-right: 1px solid #bababa;
+    /*color: #515151;*/
+    /*border-bottom: 1px solid #bababa;*/
+    /*border-right: 1px solid #bababa;*/
+    color: #b2b2b2;
+    border-bottom: 1px solid #595959;
+    border-right: 1px solid #595959;
 }
 
 .bottomMenu .bottomBody table a {
     /*display: inline-block;*/
     width: 100%;
-    height: 100%
+    height: 100%;
+    color: #b2b2b2;
 }
 
 .leftPopMenu {
@@ -1453,7 +1492,7 @@ div.popState dl.jaywork dd strong.rblue {color:#607cd4; text-align:right;}
 
 .leftPopMenu .intResrvPlan {
     height: 300px;
-    border: 1px solid #bbb
+    border: 1px solid #595959
 }
 
 .leftPopMenu .intResrvPlan p, .leftPopMenu .holydayPlan p, .bottomPopMenu P {
@@ -1530,7 +1569,7 @@ div.popState dl.jaywork dd strong.rblue {color:#607cd4; text-align:right;}
 .leftPopMenu .holydayPlan {
     height: 488px;
     margin-top: 5px;
-    border: 1px solid #bbb
+    border: 1px solid #595959
 }
 
 .leftPopMenu .holydayPlan .holydayPlanCon {
@@ -1561,7 +1600,7 @@ div.popState dl.jaywork dd strong.rblue {color:#607cd4; text-align:right;}
     position: absolute;
     top: 295px;
     height: 538px;
-    border: 1px solid #bbb;
+    border: 1px solid #595959;
     margin-left: 5px;
     margin-top: 2px;
     width: 908px
@@ -1718,7 +1757,12 @@ div.popState dl.jaywork dd strong.rblue {color:#607cd4; text-align:right;}
     width: 280px;
     text-align: center;
     padding-left: 55px;
-    font-size: 15px
+    font-size: 15px;
+    color: #b2b2b2;
+}
+
+#regionIntNm {
+    color: #b2b2b2;
 }
 
 .phaseInfoTop .tabs .simulation {
@@ -1861,7 +1905,7 @@ div.popState dl.jaywork dd strong.rblue {color:#607cd4; text-align:right;}
 .signalMapDiv {
     height: 798px;
     margin: 5px;
-    border: 1px solid #bbb
+    border: 1px solid #595959
 }
 
 .signalMapDiv .signalMapNo ul li {
@@ -2191,7 +2235,7 @@ div.popState dl.jaywork dd strong.rblue {color:#607cd4; text-align:right;}
 
 .centerContainer .listCon, .centerContainer .updateCon, .centerContainer .insertCon {
     margin: 5px;
-    border: 1px solid #bbb
+    border: 1px solid #595959
 }
 
 .centerContainer p {
@@ -2297,7 +2341,7 @@ div.popState dl.jaywork dd strong.rblue {color:#607cd4; text-align:right;}
 .eventContainer .listCon {
     height: 98.5%;
     margin: 5px;
-    border: 1px solid #bbb
+    border: 1px solid #595959
 }
 
 .eventContainer p {
@@ -2343,7 +2387,7 @@ div.popState dl.jaywork dd strong.rblue {color:#607cd4; text-align:right;}
     width: 99%;
     border-spacing: 0px;
     margin: 0 5px;
-    border: 1px solid #bbb
+    border: 1px solid #595959
 }
 
 .eventContainer .listCon table thead {
@@ -2356,7 +2400,7 @@ div.popState dl.jaywork dd strong.rblue {color:#607cd4; text-align:right;}
     font-weight: bold;
     color: #2d2d2d;
     background: url(/images/table_bg.png) repeat-x;
-    border: 1px solid #bbb
+    border: 1px solid #595959
 }
 
 .eventContainer .listCon table tbody td {
@@ -2994,7 +3038,7 @@ cursor: pointer;border: 1px solid #ebebeb;border-bottom-color: #e2e2e2;border-ra
 .treeLoading {
     display: none;
     position: absolute;
-    top: 50px;
+    top: 80px;
     left: 45%
 }
 
@@ -3028,7 +3072,7 @@ cursor: pointer;border: 1px solid #ebebeb;border-bottom-color: #e2e2e2;border-ra
 
 .board-write th {
     background: #CCC;
-    border-bottom: 1px solid #bbb;
+    border-bottom: 1px solid #595959;
 }
 
 .board-write td {
@@ -3088,6 +3132,13 @@ cursor: pointer;border: 1px solid #ebebeb;border-bottom-color: #e2e2e2;border-ra
     font-size: 15px
 }
 
+#cvibBottomInfo > tr:nth-child(odd) {
+    background-color: #292828;
+}
+
+#cvibBottomInfo > tr:nth-child(even) {
+    background-color: #3c3c3c;
+}
 
 .bottomInfo {
     height: 100%;
@@ -3101,14 +3152,17 @@ cursor: pointer;border: 1px solid #ebebeb;border-bottom-color: #e2e2e2;border-ra
 }
 
 .bottomInfo table thead {
-    background-color: #f4f4f4
+    background-color: #292828;
+}
+#bottomInfoBottom tr,
+#bottomInfoTop tr td{
+    background-color: #3c3c3c;
 }
-
 .bottomInfo table tr th, .bottomInfo table tr td {
-    border: 1px solid #bbb;
+    border: 1px solid #595959;
     line-height: 30px;
     text-align: center;
-    color: #2d2d2d
+    color: #b2b2b2
 }
 
 .bottomInfo table tr th {
@@ -3135,25 +3189,43 @@ cursor: pointer;border: 1px solid #ebebeb;border-bottom-color: #e2e2e2;border-ra
 .bottomInfo .bottomInfoBottom .bottomInfoHead::-webkit-scrollbar {
     width: 17px;
     background-color: #eee;
-    border-top: 1px solid #bbb;
-    border-right: 1px solid #bbb;
-    border-bottom: 1px solid #bbb;
+    border-top: 1px solid #595959;
+    border-right: 1px solid #595959;
+    border-bottom: 1px solid #595959;
 }
 
 .bottomInfo .bottomInfoBottom .bottomInfoBody {
-    max-height: 370px;
-    overflow-y: scroll;
-    border-bottom: 1px solid #bbb
+    max-height: 439px;
+    overflow: auto;
+    border-bottom: 1px solid #595959
+}
+.bottomInfo .bottomInfoBottom .bottomInfoBody table{
+    border-collapse: separate;
+    border-spacing: 0;
 }
 
+.bottomInfo .bottomInfoBottom .bottomInfoBody thead {
+    position: sticky;
+    top: 0;
+    left: 0;
+}
+
+/*.bottomInfo .bottomInfoBottom .bottomInfoBody {*/
+/*    max-height: 370px;*/
+/*    overflow-y: scroll;*/
+/*    border-bottom: 1px solid #595959*/
+/*}*/
+
 .bottomInfo .bottomInfoBottom .bottomInfoBody::-webkit-scrollbar {
-    width: 17px;
-    border-right: 1px solid #bbb;
+    width: 10px;
+    border-right: 1px solid #595959;
     background-color: #eee;
+    border-radius: 5px;
 }
 
 .bottomInfo .bottomInfoBottom .bottomInfoBody::-webkit-scrollbar-thumb {
-    background-color: #bbb;
+    background-color: #595959;
+    border-radius: 5px;
 }
 
 .bottomInfo .bottomInfoBottom .bottomInfoBody table tbody tr:nth-child(1) td {
@@ -3161,15 +3233,17 @@ cursor: pointer;border: 1px solid #ebebeb;border-bottom-color: #e2e2e2;border-ra
 }
 
 .bottomInfo .bottomInfoBottom .bottomInfoBody .cvibLightsBack_1 {
-    background-color: #ff0000
+    background-color: #ff0000;
+    color: white;
 }
 
 .bottomInfo .bottomInfoBottom .bottomInfoBody .cvibLightsBack_2 {
-    background-color: #ffff00
+    background-color: #ffff00;
 }
 
 .bottomInfo .bottomInfoBottom .bottomInfoBody .cvibLightsBack_3 {
-    background-color: #008000
+    background-color: #008000;
+    color: white;
 }
 
 .bottomInfo .bottomEvent {
@@ -3243,7 +3317,7 @@ cursor: pointer;border: 1px solid #ebebeb;border-bottom-color: #e2e2e2;border-ra
         width: 100%;
 
         height: 450px;
-        border: 1px solid #bbb;
+        border: 1px solid #595959;
 
         z-index: 1;
         float: left;
@@ -3253,9 +3327,98 @@ cursor: pointer;border: 1px solid #ebebeb;border-bottom-color: #e2e2e2;border-ra
     .crossLoadInfo {
         width: 100%;
         height: 450px;
-        border: 1px solid #bbb;
+        border: 1px solid #595959;
 
         z-index: 1;
         float: left;
     }
+}
+/*:root {*/
+/*    --map-tiles-filter: brightness(0.6) invert(1) contrast(3) hue-rotate(200deg) saturate(0.3) brightness(0.7);*/
+/*}*/
+/*#map img{*/
+#map img{
+   /*filter : brightness(0.7);*/
+    /*opacity: 0.9 !important;*/
+    /*filter: contrast(0.7);*/
+    filter: brightness(0.6) invert(0.8) contrast(2) hue-rotate(180deg) saturate(0) brightness(0.7);
+    /*filter: brightness(0.6) invert(1) contrast(4) hue-rotate(180deg) saturate(0) brightness(0.7);*/
+    /*filter: invert(1) saturate(0.3) brightness(0.7);*/
+    /*filter: invert(100%) hue-rotate(180deg) brightness(120%) contrast(72.5%);*/
+    /*-webkit-filter: invert(100%) hue-rotate(180deg) brightness(120%) contrast(72.5%);*/
+    /*-moz-filter: invert(100%) hue-rotate(180deg) brightness(120%) contrast(72.5%);*/
+    /*transition: 0.2s ease-out;*/
+}
+
+#start, #end {
+    width: 105px;
+    height: 25px;
+}
+/*#map button, */
+#map > div:nth-child(7),
+/*#map > div:nth-child(8),*/
+#searchText,
+#start,
+#end,
+#map .mapToggle img,
+img[src="images/tab01_on.png"],
+img[src="images/tab01_off.png"],
+img[src="images/tab02_on.png"],
+img[src="images/tab02_off.png"],
+img[src="images/logout.png"]{
+    filter: brightness(0.6) invert(1) contrast(4) hue-rotate(180deg) saturate(0.3) brightness(0.7);
+}
+
+#map .wideMap img,
+#map .upDownMap img,
+#map img[src="/images/evp_arr.svg"],#map img[src="images/TrafficLight25.png"],
+#map img[src="/images/car.svg"],
+#map img[src="images/sig1.png"],
+#map img[src="images/sig2.png"],
+#map img[src="images/sig3.png"],
+#map img[src="images/sig4.png"],
+#map img[src="images/sig5.png"],
+#map img[src="/images/CvibLight_0_0.gif"],
+#map img[src="/images/CvibLight_0_9.gif"],
+#map img[src="/images/CvibLight_1_1.gif"],
+#map img[src="/images/CvibLight_1_2.gif"],
+#map img[src="/images/CvibLight_1_3.gif"],
+#map img[src="/images/CvibLight_1_6.gif"],
+#map img[src="/images/CvibLight_1_9.gif"],
+#map img[src="/images/CvibLight_2_1.gif"],
+#map img[src="/images/CvibLight_2_2.gif"],
+#map img[src="/images/CvibLight_2_3.gif"],
+#map img[src="/images/CvibLight_2_9.gif"],
+#map img[src="/images/CvibLight_3_1.gif"],
+#map img[src="/images/CvibLight_3_2.gif"],
+#map img[src="/images/CvibLight_3_3.gif"],
+#map img[src="/images/CvibLight_3_9.gif"],
+#map img[src="/images/CvibLight_5_0.gif"],
+#map img[src="/images/CvibLight_5_9.gif"],
+#map img[src="/images/CvibLight_9_1.gif"],
+#map img[src="/images/CvibLight_9_2.gif"],
+#map img[src="/images/CvibLight_9_3.gif"],
+#map img[src="/images/CvibLight_9_4.gif"],
+#map img[src="http://t1.kakaocdn.net/localimg/localimages/07/mapapidoc/roadview_wk.png"]
+/*#map .mapToggle img,*/
+/*#map img[src="images/arrow_left.png"],*/
+/*#map img[src="images/arrow_down.png"]*/
+{
+    filter: none !important;
+}
+
+.jstree-children > li:nth-child(odd) > div{
+    background-color: #292828;
+}
+
+.jstree-children > li:nth-child(even) > div{
+    background-color: #3c3c3c;
+}
+
+.jstree-node{
+    color: #b2b2b2;
+}
+
+#intTree{
+    background-color: #212121;
 }

BIN
src/main/resources/static/images/drop_down.png


BIN
src/main/resources/static/images/drop_up.png


+ 13 - 0
src/main/resources/static/images/evp_arr.svg

@@ -0,0 +1,13 @@
+<svg width="32" height="39" viewBox="0 0 32 39" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g id="ic_&#235;&#143;&#132;&#236;&#176;&#169;">
+<g id="Group 16760">
+<path id="Vector" d="M30.25 15.8333C30.25 26.9166 16 36.4166 16 36.4166C16 36.4166 1.75 26.9166 1.75 15.8333C1.75 12.0539 3.25133 8.42937 5.92373 5.75698C8.59612 3.08459 12.2207 1.58325 16 1.58325C19.7793 1.58325 23.4039 3.08459 26.0763 5.75698C28.7487 8.42937 30.25 12.0539 30.25 15.8333Z" fill="#3D3D3D"/>
+<path id="Vector_2" d="M32 15.9545C32 28.3636 16 39 16 39C16 39 0 28.3636 0 15.9545C6.32325e-08 11.7231 1.68571 7.66504 4.68629 4.67298C7.68687 1.68092 11.7565 0 16 0C20.2435 0 24.3131 1.68092 27.3137 4.67298C30.3143 7.66504 32 11.7231 32 15.9545Z" fill="white"/>
+<path id="Vector_3" d="M30.25 15.8333C30.25 26.9166 16 36.4166 16 36.4166C16 36.4166 1.75 26.9166 1.75 15.8333C1.75 12.0539 3.25133 8.42937 5.92373 5.75698C8.59612 3.08459 12.2207 1.58325 16 1.58325C19.7793 1.58325 23.4039 3.08459 26.0763 5.75698C28.7487 8.42937 30.25 12.0539 30.25 15.8333Z" fill="#F54C66"/>
+</g>
+<g id="&#235;&#143;&#132;&#236;&#176;&#169;">
+<path d="M14.6406 17.3672H11.5469V19.6406H15.707V20.8594H5.94531V19.6406H10.0586V17.3672H7.09375V12.1641H14.5586V13.3594H8.59375V16.1836H14.6406V17.3672Z" fill="white"/>
+<path d="M20.2422 13.7109C20.2461 14.0938 20.3379 14.4629 20.5176 14.8184C20.6973 15.1738 20.9746 15.4941 21.3496 15.7793C21.7246 16.0605 22.1914 16.2734 22.75 16.418L22.0586 17.6016C21.4688 17.4414 20.959 17.1973 20.5293 16.8691C20.1035 16.5371 19.7656 16.1445 19.5156 15.6914C19.2734 16.1914 18.9375 16.6211 18.5078 16.9805C18.0781 17.3359 17.5586 17.6016 16.9492 17.7773L16.2344 16.6055C16.7891 16.4531 17.2539 16.2285 17.6289 15.9316C18.0078 15.6309 18.2891 15.2891 18.4727 14.9062C18.6602 14.5195 18.7539 14.1211 18.7539 13.7109H16.6094V12.5273H18.7539V11.3789H20.2656V12.5273H22.3984V13.7109H20.2422ZM17.5938 18.3867H24.7773V22.0898H23.2773V19.582H17.5938V18.3867ZM23.2773 11.4141H24.7773V14.0391H26.2188V15.293H24.7773V17.9531H23.2773V11.4141Z" fill="white"/>
+</g>
+</g>
+</svg>

BIN
src/main/resources/static/images/evp_legend.png


BIN
src/main/resources/static/images/logo2.png


BIN
src/main/resources/static/images/logo3.png


BIN
src/main/resources/static/images/sig1.png


BIN
src/main/resources/static/images/sig2.png


BIN
src/main/resources/static/images/sig3.png


BIN
src/main/resources/static/images/sig4.png


BIN
src/main/resources/static/images/sig5.png


BIN
src/main/resources/static/images/tab01_on.png


BIN
src/main/resources/static/images/tab01_on_2.png


BIN
src/main/resources/static/images/tab02_off.png


BIN
src/main/resources/static/images/tab02_on.png


+ 1 - 1
src/main/resources/static/js/common/common-ajax.js

@@ -1 +1 @@
-/**
 * @param {Object} url
 * @param {Object} param
 * @param {Object} callback
 */
function requestService(url, param, callback, async) {
    /*
     * 스프링시큐리티 csrf 토큰 에러때문에  ajax 통신시 해더에 포함해줘야한다.
     */

    if (async) {

        async = true;
    }

    const token = $("meta[name='_csrf']").attr("content");
    const header = $("meta[name='_csrf_header']").attr("content");
    $.ajax({
        url: _WEB_CONTEXT_NAME + "/" + url
        , data: encodeURI(param)
        , cache: false
        , async: async
        , type: 'POST'
        , success: callback
        , error: whenError
        , beforeSend: function (xhr) {
            xhr.setRequestHeader(header, token);
        }
    });
}

/**
 *
 * @param url
 * @param formName
 * @param callback
 * @param async
 */
function requestServiceForm(url, formName, callback, async) {

    if (async) {

        async = true;
    }

    $("#" + formName).ajaxSubmit({

        url: _WEB_CONTEXT_NAME + "/" + url
        , data: $(this).serialize()
        , cache: false
        , async: async
        , type: 'POST'
        , success: callback
        , error: whenError

    });
}

function whenError() {

    console.log('ajax error');

}
+/**
 * @param {Object} url
 * @param {Object} param
 * @param {Object} callback
 */
function requestService(url, param, callback, async) {
    /*
     * 스프링시큐리티 csrf 토큰 에러때문에  ajax 통신시 해더에 포함해줘야한다.
     */

    if (async) {

        async = true;
    }

    const token = $("meta[name='_csrf']").attr("content");
    const header = $("meta[name='_csrf_header']").attr("content");
    $.ajax({
        url: _WEB_CONTEXT_NAME + "/" + url
        , data: param
        , cache: false
        , async: async
        , type: 'POST'
        , success: callback
        , error: whenError
        , beforeSend: function (xhr) {
            xhr.setRequestHeader(header, token);
        }
    });
}

/**
 *
 * @param url
 * @param formName
 * @param callback
 * @param async
 */
function requestServiceForm(url, formName, callback, async) {

    if (async) {

        async = true;
    }

    $("#" + formName).ajaxSubmit({

        url: _WEB_CONTEXT_NAME + "/" + url
        , data: $(this).serialize()
        , cache: false
        , async: async
        , type: 'POST'
        , success: callback
        , error: whenError

    });
}

function whenError() {

    console.log('ajax error');

}

+ 1 - 1
src/main/resources/static/js/cvib.js

@@ -61,7 +61,7 @@ CvibInfo.prototype = {
             center: new kakao.maps.LatLng(this.centerPos.Lat, this.centerPos.Lng),
             radius: 30,
             strokeWeight: 0.3,
-            strokeColor: '#000000',
+            strokeColor: '#eeeeee',
             strokeStyle: 'solid',
             zIndex: 10,
             map : map

+ 544 - 325
src/main/resources/static/js/map.js

@@ -25,6 +25,7 @@ var searchInfoWindow = []   //장소 이름 인포윈도우
 var imsMarker = [];
 var troubleMarker = [];
 var linkLineArr = [];
+var _EmergencyMap = new Map();
 
 var IMS_VIEW_LEVEL = 8;	    //돌발 7
 
@@ -34,25 +35,19 @@ var roadviewMarker = null;
 
 var _ToggleSiginfo = false;
 
+const stateMap = new Map();
+stateMap.set(0, '#FF0000');
+stateMap.set(1, '#00FF00');
+stateMap.set(2, '#FFFF00');
+stateMap.set(3, '#808080');
+stateMap.set(4, '#CC6600');
+stateMap.set(5, '#9933FF');
+
 
 $(document).ready(function () {
     init();
 });
 
-let sigData =
-    {
-        service_id : 1,
-        seq_no : 1,
-        node_id : 1,
-        ring : 1,
-        head_lat : 126.5169020,
-        head_lng : 33.2475970,
-        mid_lat : 126.5170416,
-        mid_lng : 33.2475840,
-        end_lat : 126.5170395,
-        end_lng : 33.2474713
-    }
-
 
 
 function init() {
@@ -77,12 +72,13 @@ function init() {
     };
 
     map = new kakao.maps.Map(mapContainer, mapOption); // 지도생성
-   // map.setMaxLevel(11);
+
+    // map.setMaxLevel(11);
     // 지도와 스카이뷰 지도타입 컨트롤을 생성
-    var mapTypeControl = new kakao.maps.MapTypeControl();
+    //var mapTypeControl = new kakao.maps.MapTypeControl();
     // 지도에 컨트롤을 추가해야 지도위에 표시됩니다
     // kakao.maps.ControlPosition은 컨트롤이 표시될 위치를 정의하는데 TOPRIGHT는 오른쪽 위를 의미합니다
-    map.addControl(mapTypeControl, kakao.maps.ControlPosition.TOPRIGHT);
+    //map.addControl(mapTypeControl, kakao.maps.ControlPosition.TOPRIGHT);
     // 지도 확대 축소 줌 컨트롤을 생성
     var zoomControl = new kakao.maps.ZoomControl();
     map.addControl(zoomControl, kakao.maps.ControlPosition.RIGHT);
@@ -101,12 +97,12 @@ function init() {
     });*/
 
     //getSignalInfo();
-    // const worker = new Worker('/js/worker.js');
-    // worker.postMessage({message:"worker START"});
-    // worker.onmessage = (ev)=>{
-    //     const data = JSON.parse(ev.data);
-    //     console.log(data);
-    // }
+    const worker = new Worker('/js/worker.js');
+    worker.postMessage({message:"worker START"});
+    worker.onmessage = (ev)=>{
+        const data = JSON.parse(ev.data);
+        drawEmergencyRoad(data);
+    }
 
 }
 
@@ -151,258 +147,247 @@ function getKakaoPosition(y, x) {
     return new kakao.maps.LatLng(y, x);
 }
 
-let testData = [
-    {
-        service_id : 1,
-        lat : 126.5240617,
-        lng : 33.2471400,
-        seq_no : 1,
-    },
-    {
-        service_id : 1,
-        lat : 126.5229991,
-        lng : 33.2471900,
-        seq_no : 2,
-    },
-    {
-        service_id : 1,
-        lat : 126.5210876,
-        lng : 33.2474622,
-        seq_no : 3,
-    },{
-        service_id : 1,
-        lat : 126.5191443,
-        lng : 33.2476711,
-        seq_no : 4,
-    },
-    {
-        service_id : 1,
-        lat : 126.5167390,
-        lng : 33.2479593,
-        seq_no : 5,
-    },
-    {
-        service_id : 1,
-        lat : 126.5141087,
-        lng : 33.2482105,
-        seq_no : 7,
-    },
-    {
-        service_id : 2,
-        lat : 126.5141180,
-        lng : 33.2479716,
-        seq_no : 1,
-    },
-    {
-        service_id : 2,
-        lat : 126.5141180,
-        lng : 33.2479716,
-        seq_no : 2,
-    },
-    {
-        service_id : 2,
-        lat : 126.5154815,
-        lng : 33.2478417,
-        seq_no : 3,
-    },
-    {
-        service_id : 2,
-        lat : 126.5169955,
-        lng : 33.2476673,
-        seq_no : 4,
-    },
-    {
-        service_id : 2,
-        lat : 126.5187242,
-        lng : 33.2474756,
-        seq_no : 5,
-    },
-    {
-        service_id : 2,
-        lat : 126.5204636,
-        lng : 33.2472750,
-        seq_no : 6,
-    },
-    {
-        service_id : 2,
-        lat : 126.5223642,
-        lng : 33.2470298,
-        seq_no : 7,
-    },{
-        service_id : 2,
-        lat : 126.5240712,
-        lng : 33.2468741,
-        seq_no : 8,
-    },
-]
-
-function emergencyEvent() {
-
+function emergencyHistory() {
+    const param = {serviceId : '1832024101614555100'};
+     requestService('getEvpHistory.do', param, emergencyHistoryCallBack, true);
 }
 
-function test() {
-    if (testData && testData.length) {
-        testData.sort((a,b)=>{
-            return a.service_id > b.service_id ? 1 : a.service_id < b.service_id ?  -1 : a.seq_no > b.seq_no ? 1 : a.seq_no === b.seq_no ? 0 : -1
-        });
-        const objMap = new Map();
-        for (let data of testData) {
-            if (!objMap.get(data.service_id)) {
-                objMap.set(data.service_id, {service_id : data.service_id, coordinateArr : []});
-            }
-            objMap.get(data.service_id).coordinateArr.push({
-              lng : data.lng,
-              lat : data.lat,
-            })
-        }
-        objMap.forEach((obj)=>{
-            const road = new emergencyRoad(obj);
-            road.init();
-            road.drawPolyLine();
-            road.setBound();
+const historyMap = new Map();
+function emergencyHistoryCallBack(data) {
+    if (data && data.length) {
+
+        data.forEach((obj)=>{
+            historyMap.set(obj.service_id, obj);
         })
-    }
-}
 
-function emergencySignal(data){
-    if ( data && data.length > 0 ) {
-        data.sort((a,b)=>{
-            return a.service_id > b.service_id ? 1 : a.service_id < b.service_id ?  -1 : a.seq_no > b.seq_no ? 1 : a.seq_no === b.seq_no ? 0 : -1
+        const selected = historyMap.get('1832024101614555100');
+        const {route_list, phase_list, event_list} = selected;
+        const phaseMap = new Map();
+
+        const dateMap = new Map();
+
+        event_list.forEach((eventObj)=>{
+            dateMap.set(eventObj.clct_dt, new Map());
+            dateMap.get(eventObj.clct_dt).set('event', eventObj);
+            dateMap.get(eventObj.clct_dt).set('sig', []);
         });
-        let lineMap = new Map();
-        const bounds = new kakao.maps.LatLngBounds();
-        bounds.extend(getKakaoPosition(33.2471400, 126.5240617));
-        bounds.extend(getKakaoPosition(33.2482105,126.5141087));
-        map.setBounds(bounds);
-        const markerMap = new Map();
-        for (let obj of data) {
-            const position = getKakaoPosition(obj.lng, obj.lat);
-            if (!lineMap.get(obj.service_id)) {
-                lineMap.set(obj.service_id, []);
-                let markImage = new kakao.maps.MarkerImage(
-                    '/images/car.svg',
-                    new kakao.maps.Size(32, 39), {
-                         offset: new kakao.maps.Point(16, 39)
-                    });
 
+        phase_list.forEach((obj)=>{
+            if (!dateMap.get(obj.clct_dt)) {
+                dateMap.set(obj.clct_dt, new Map());
+                dateMap.get(obj.clct_dt).set('event', null);
+                dateMap.get(obj.clct_dt).set('sig', []);
+            }
+            dateMap.get(obj.clct_dt).get('sig').push(obj);
+        });
 
-                const marker = new kakao.maps.Marker({
-                    image: markImage,
-                    position: position,
-                });
-                markerMap.set(obj.service_id, marker);
+        let beforeSig;
+        let beforeEvent;
+        const dateArr = Array.from(dateMap.keys()).sort();
+
+
+        let road = null;
+        let car = null;
+        let sig = new Map();
+        let arrive = null;
+
+        for (let date of dateArr) {
+            const objMap = dateMap.get(date);
+            if (objMap.get('event') === null) {
+                if (beforeEvent === undefined) {
+                    let idx = dateArr.findIndex(key => dateMap.get(key).get('event') !== null);
+                    let key = dateArr[idx];
+                    beforeEvent = dateMap.get(key).get('event');
+                }
+                dateMap.get(date).set('event', beforeEvent);
+            }
+            else {
+                beforeEvent = objMap.get('event');
+            }
+
+            if (objMap.get('sig') === null) {
+                if (beforeSig === undefined) {
+                    for (let key of dateArr) {
+                        if (dateMap.get(key).get('sig').size() !== 0) {
+                            beforeSig = dateMap.get(key).get('sig');
+                            break;
+                        }
+                    }
+                }
+                dateMap.get(date).set('sig', beforeSig);
             }
+            else {
+                beforeSig = objMap.get('sig');
+            }
+        }
+        const sigArr = dateMap.get(dateArr[0]).get('sig');
+        const eventObj = dateMap.get(dateArr[0]).get('event');
+
 
-            lineMap.get(obj.service_id).push(position);
+
+        if (route_list && route_list.length) {
+           road = new EmergencyRoadObj(route_list);
+           road.setBound();
         }
-        let polyMap = new Map();
-        lineMap.forEach((position, key)=>{
-            let backLine = new kakao.maps.Polyline({
-                path: position, // 선을 구성하는 좌표 배열입니다 클릭한 위치를 넣어줍니다
-                strokeWeight: 10, // 선의 두께입니다
-                strokeColor: '#ffffff', // 선의 색깔입니다
-                strokeOpacity: 1, // 선의 불투명도입니다 0에서 1 사이값이며 0에 가까울수록 투명합니다
-                strokeStyle: 'solid', // 선의 스타일입니다
-                zIndex: 1,
-            });
 
-            // 선이 그려지고 있을 때 마우스 움직임에 따라 선이 그려질 위치를 표시할 선을 생성합니다
-            let polyline = new kakao.maps.Polyline({
-                strokeWeight: 8, // 선의 두께입니다
-                path: position,
-                strokeColor: '#3386ff', // 선의 색깔입니다
-                strokeOpacity: 1, // 선의 불투명도입니다 0에서 1 사이값이며 0에 가까울수록 투명합니다
-                strokeStyle: 'solid', // 선의 스타일입니다
-                zIndex: 2,
-            });
-            backLine.setMap(map);
-            polyline.setMap(map);
-            polyMap.set(key, [backLine, polyline]);
+
+        sigArr.forEach((obj)=>{
+            sig.set(obj.service_id + '_' + obj.seq_no, new EmergencySig(obj));
         });
 
-        markerMap.forEach((marker, key)=>{
-            marker.setMap(map);
-            if (lineMap.get(key)) {
-                const arr = lineMap.get(key);
-                let cnt = 0;
-                const timer = setInterval(()=>{
-                    if (arr[cnt]) {
-                        marker.setPosition(arr[cnt++])
-                    }
-                    else {
-                        marker.setMap(null);
-                        const polyArr = polyMap.get(key);
-                        if (polyArr && polyArr.length === 2) {
-                            polyArr[0].setMap(null);
-                            polyArr[1].setMap(null);
-                        }
-                        clearInterval(timer);
-                    }
-                }, 1000)
+
+        if (selected.arr_lat && selected.arr_lng) {
+            arrive = new EmergencyMarker(getKakaoPosition(selected.arr_lat, selected.arr_lng), '/images/evp_arr.svg');
+        }
+        car = new EmergencyMarker(getKakaoPosition(eventObj.cur_lat, eventObj.cur_lng), '/images/car.svg');
+
+        let cnt = 1;
+        let timer = setInterval(()=>{
+            if (cnt === dateArr.length) {
+                return clearInterval(timer);
             }
-        })
+            const objMap = dateMap.get(dateArr[cnt]);
+            setEmergencyCurr(car, sig, road, arrive, objMap);
+            cnt++;
+        }, 500);
 
     }
 }
 
-function drawEmergencyRoad(serviceId) {
-    requestService('/getEvpList.do', {serviceId : serviceId}, (data)=>{
-        data.sort((a,b)=>{
-            return a.service_id > b.service_id ? 1 : a.service_id < b.service_id ?  -1 : a.seq_no > b.seq_no ? 1 : a.seq_no === b.seq_no ? 0 : -1
-        });
-        let lineMap = new Map();
-        const bounds = new kakao.maps.LatLngBounds();
-        bounds.extend(getKakaoPosition(33.2471400, 126.5240617));
-        bounds.extend(getKakaoPosition(33.2482105,126.5141087));
-        map.setBounds(bounds);
-        const markerMap = new Map();
-        for (let obj of data) {
-            const position = getKakaoPosition(obj.lng, obj.lat);
-            if (!lineMap.get(obj.service_id)) {
-                lineMap.set(obj.service_id, []);
-                let markImage = new kakao.maps.MarkerImage(
-                    '/images/car.svg',
-                    new kakao.maps.Size(32, 39), {
-                        //마커의 좌표에 해당하는 이미지의 위치를 설정합니다.
-                        //이미지의 모양에 따라 값은 다를 수 있으나, 보통 width/2, height를 주면 좌표에 이미지의 하단 중앙이 올라가게 됩니다.
-                        offset: new kakao.maps.Point(16, 39)
-                    });
+function setEmergencyCurr(car, sigMap, road, arrive, dataMap) {
+    const sigArr   = dataMap.get('sig');
+    const eventObj = dataMap.get('event')
+    const eventCd  = eventObj.event_cd;
+    if (eventCd === 1) {
+        const {cur_lat, cur_lng} = eventObj;
+        car.moveMarker(getKakaoPosition(cur_lat, cur_lng));
+    }
+    else if (eventCd === 2) {
+        car.hide();
+        sigArr.forEach((obj)=>{obj.hide()});
+        arrive.hide();
+        road.hide();
+        return;
+    }
+
+    if (sigArr && sigArr.length > 0) {
+        for (let sig of sigArr) {
+            const sigMarker = sigMap.get(sig.service_id + '_' + sig.seq_no);
+            if (sigMarker) {
+                if (!sigMarker.a_ring && !sigMarker.b_ring) {
+                    sigMarker.createRing(sig);
+                }
+                else {
+                    const {a_head_lat, a_head_lng, a_mid_lat, a_mid_lng, a_end_lat, a_end_lng,
+                        b_head_lat, b_head_lng, b_mid_lat, b_mid_lng, b_end_lat, b_end_lng} = sig;
+                        const aPosition = [
+                            getKakaoPosition(a_head_lat, a_head_lng),
+                            getKakaoPosition(a_mid_lat, a_mid_lng),
+                            getKakaoPosition(a_end_lat, a_end_lng),
+                        ]
+                        const bPosition = [
+                            getKakaoPosition(b_head_lat, b_head_lng),
+                            getKakaoPosition(b_mid_lat, b_mid_lng),
+                            getKakaoPosition(b_end_lat, b_end_lng),
+                        ]
+
+                        // console.log(aPosition, bPosition);
+                        sigMarker.a_ring.setPath(aPosition);
+                        sigMarker.b_ring.setPath(bPosition);
+                }
+                sigMarker.setState(sig.state);
+            }
+        }
+    }
 
+}
+
+function drawEmergencyRoad(data) {
+    if (data && data.length) {
+        data.forEach((obj)=>{
+            const beforeObj = _EmergencyMap.get(obj.service_id);
+            if (!beforeObj) {
+                const emgObj = new EmergencyObj(obj);
+                if (_EmergencyMap.size === 0) {
+                    emgObj.setBound();
+                }
 
-                const marker = new kakao.maps.Marker({
-                    image: markImage,
-                    position: position,
+                _EmergencyMap.set(obj.service_id, emgObj);
+            }
+            else {
+                beforeObj.car.moveMarker(getKakaoPosition(obj.event_list[0].cur_lat, obj.event_list[0].cur_lng));
+                obj.phase_list.forEach((sigObj)=>{
+                    const beforeSig = beforeObj.sig.get(sigObj.service_id + '_' + sigObj.seq_no);
+                    if (beforeSig) {
+                        const {a_head_lat, a_head_lng, a_mid_lat, a_mid_lng, a_end_lat, a_end_lng,
+                            b_head_lat, b_head_lng, b_mid_lat, b_mid_lng, b_end_lat, b_end_lng, state} = sigObj
+                        beforeSig.setState(state);
+                        if (beforeSig.a_ring) {
+                            const a_head = getKakaoPosition(a_head_lat, a_head_lng);
+                            const a_mid = getKakaoPosition(a_mid_lat, a_mid_lng);
+                            const a_end = getKakaoPosition(a_end_lat, a_end_lng);
+                            beforeSig.a_ring.setPath([a_head, a_mid, a_end]);
+                        }
+
+                        if (beforeSig.b_ring) {
+                            const b_head = getKakaoPosition(b_head_lat, b_head_lng);
+                            const b_mid = getKakaoPosition(b_mid_lat, b_mid_lng);
+                            const b_end = getKakaoPosition(b_end_lat, b_end_lng);
+                            beforeSig.b_ring.setPath([b_head, b_mid, b_end]);
+                        }
+
+                        if (beforeSig.a_ring === null && beforeSig.b_ring === null) {
+                            beforeSig.createRing(sigObj);
+                        }
+                    }
                 });
-                markerMap.set(obj.service_id, marker);
             }
+        });
 
-            lineMap.get(obj.service_id).push(position);
+        if (_EmergencyMap.size !== data.length) {
+            const keyArr = Array.from(_EmergencyMap.keys());
+            for (let key of keyArr) {
+                let cnt = 0;
+                data.forEach((obj) => {
+                    if (obj.service_id === key) {
+                        cnt++;
+                    }
+                })
+                if (cnt === 0) {
+                    const sigObj = _EmergencyMap.get(key).sig;
+                    sigMap.delete(sigObj.lat + '_' + sigObj.lng);
+                    _EmergencyMap.get(key).clear();
+                    _EmergencyMap.delete(key);
+                }
+            }
         }
-        let polyMap = new Map();
-        lineMap.forEach((position, key)=>{
-            let backLine = new kakao.maps.Polyline({
-                path: position, // 선을 구성하는 좌표 배열입니다 클릭한 위치를 넣어줍니다
-                strokeWeight: 10, // 선의 두께입니다
-                strokeColor: '#ffffff', // 선의 색깔입니다
-                strokeOpacity: 1, // 선의 불투명도입니다 0에서 1 사이값이며 0에 가까울수록 투명합니다
-                strokeStyle: 'solid', // 선의 스타일입니다
-                zIndex: 1,
+    }
+    else {
+        if (_EmergencyMap.size > 0) {
+            _EmergencyMap.forEach((obj)=>{
+                obj.clear();
             });
+            _EmergencyMap.clear();
+        }
+        // clearEmgMarker();
+    }
+}
 
-            // 선이 그려지고 있을 때 마우스 움직임에 따라 선이 그려질 위치를 표시할 선을 생성합니다
-            let polyline = new kakao.maps.Polyline({
-                strokeWeight: 8, // 선의 두께입니다
-                path: position,
-                strokeColor: '#3386ff', // 선의 색깔입니다
-                strokeOpacity: 1, // 선의 불투명도입니다 0에서 1 사이값이며 0에 가까울수록 투명합니다
-                strokeStyle: 'solid', // 선의 스타일입니다
-                zIndex: 2,
-            });
-            backLine.setMap(map);
-            polyline.setMap(map);
-            polyMap.set(key, [backLine, polyline]);
+function clearEmgMarker() {
+    if (_EmergencyMap.size > 0) {
+        _EmergencyMap.forEach((obj)=>{
+            obj.get('marker').setMap(null);
+            obj.get('line')[0].setMap(null);
+            obj.get('line')[1].setMap(null);
+            const circleArr = obj.get('circle');
+            if (circleArr && circleArr.length) {
+                for(let circle of circleArr) {
+                    circle.setMap(null);
+                }
+            }
         });
-    })
+        _EmergencyMap.clear();
+    }
 }
 
 function moveMarker(position, marker) {
@@ -419,7 +404,7 @@ function customOverlayFnc(intNm, xCoord, yCoord) {
         customOverlay = new kakao.maps.CustomOverlay({
             position: position,
             content: content,
-            yAnchor: 1,
+            // yAnchor: 1,
             zIndex: 100
         });
         // 커스텀 오버레이를 지도에 표시합니다
@@ -857,6 +842,7 @@ function getDrawRoad() {
 
                 // 마우스로 클릭한 위치입니다
                 var clickPosition = mouseEvent.latLng;
+                console.log(clickPosition);
                 // 지도 클릭이벤트가 발생했는데 선을 그리고있는 상태가 아니면
                 if (!drawingFlag) {
                     //거리재기초기화
@@ -1571,82 +1557,84 @@ function moveToPosition() {
     mapZoomBound();
 }
 
-class emergencyRoad{
-    constructor({service_id, coordinateArr}) {
+
+class EmergencyObj{
+    constructor({ arr_lat, arr_lng, arr_tm, clct_dt,
+                    cur_lat, cur_lng, ev_no, ocr_no, ocr_type,
+                    phase_list, route_list, service_dist,
+                    service_id, service_nm, status_cd, veh_len, event_list }) {
+        this.arr_lat = arr_lat;
+        this.arr_lng = arr_lng;
+        this.arr_tm = arr_tm;
+        this.clct_dt = clct_dt;
+        this.cur_lat = cur_lat;
+        this.cur_lng = cur_lng;
+        this.ev_no = ev_no;
+        this.ocr_no = ocr_no;
+        this.ocr_type = ocr_type;
+        this.phase_list = phase_list;
+        this.route_list = route_list;
+        this.service_dist = service_dist;
         this.service_id = service_id;
-        this.coordinateArr = coordinateArr;
-        this.backPoly = null;
-        this.polyLine = null;
+        this.service_nm = service_nm;
+        this.status_cd = status_cd;
+        this.veh_len = veh_len;
+        this.road = null;
+        this.sig = new Map();
+        this.car = null;
+        this.arrive = null;
+        this.event_list = event_list;
+        this.init();
     }
 
     init() {
-        if (this.coordinateArr && this.coordinateArr.length > 0) {
-            let lineArr = [];
-            for (let coordinate of this.coordinateArr) {
-                lineArr.push(getKakaoPosition(coordinate.lng, coordinate.lat));
-            }
-            this.backPoly = new kakao.maps.Polyline({
-                path: lineArr, // 선을 구성하는 좌표 배열입니다 클릭한 위치를 넣어줍니다
-                strokeWeight: 10, // 선의 두께입니다
-                strokeColor: '#ffffff', // 선의 색깔입니다
-                strokeOpacity: 1, // 선의 불투명도입니다 0에서 1 사이값이며 0에 가까울수록 투명합니다
-                strokeStyle: 'solid', // 선의 스타일입니다
-                zIndex: 1,
-            });
-
-            // 선이 그려지고 있을 때 마우스 움직임에 따라 선이 그려질 위치를 표시할 선을 생성합니다
-            this.polyLine = new kakao.maps.Polyline({
-                strokeWeight: 8, // 선의 두께입니다
-                path: lineArr,
-                strokeColor: '#3386ff', // 선의 색깔입니다
-                strokeOpacity: 1, // 선의 불투명도입니다 0에서 1 사이값이며 0에 가까울수록 투명합니다
-                strokeStyle: 'solid', // 선의 스타일입니다
-                zIndex: 2,
-            });
+        this.road = new EmergencyRoadObj(this.route_list);
+        if (this.phase_list && this.phase_list.length) {
+            this.phase_list.forEach((obj)=>{
+                const sig = new EmergencySig(obj);
+                this.sig.set(obj.service_id + '_' + obj.seq_no, sig);
+            })
         }
+        this.car = new EmergencyMarker(getKakaoPosition(this.event_list[0].cur_lat, this.event_list[0].cur_lng), '/images/car.svg');
+        this.arrive = new EmergencyMarker(getKakaoPosition(this.arr_lat, this.arr_lng), '/images/evp_arr.svg');
     }
 
-    drawPolyLine() {
-        if (this.polyLine && this.backPoly) {
-            this.polyLine.setMap(map);
-            this.backPoly.setMap(map);
+    clear() {
+        if (this.road) {
+            this.road.hide();
         }
-    }
 
-    hidePolyLine() {
-        if (this.polyLine && this.backPoly) {
-            this.polyLine.setMap(null);
-            this.backPoly.setMap(null);
+        if (this.sig && this.sig.length) {
+            this.sig.forEach((obj)=>{
+                obj.hide();
+            })
+        }
+
+        if (this.car) {
+            this.car.hide();
+        }
+
+        if (this.arrive) {
+            this.arrive.hide();
         }
     }
 
     setBound() {
         const bounds = new kakao.maps.LatLngBounds();
-        const lastIdx = this.coordinateArr.length - 1;
-        bounds.extend(getKakaoPosition( this.coordinateArr[0].lng,  this.coordinateArr[0].lat));
-        bounds.extend(getKakaoPosition(this.coordinateArr[lastIdx].lng,  this.coordinateArr[lastIdx].lat));
+        bounds.extend(getKakaoPosition(this.cur_lat, this.cur_lng));
+        bounds.extend(getKakaoPosition(this.arr_lat, this.arr_lng));
         map.setBounds(bounds);
     }
 }
 
-const stateMap = new Map();
-stateMap.set(0, '#FF0000');
-stateMap.set(1, '#00FF00');
-stateMap.set(2, '#FFFF00');
-stateMap.set(3, '#808080');
-stateMap.set(4, '#CC6600');
-stateMap.set(5, '#9933FF');
-
 // 1~5 레발 까지만 보임
 // 통신 이상 : 0 (#FF0000, 빨강), 정상 : 1 (#00FF00, 초록), 점멸 : 2 (#FFFF00, 노랑), 소등 : 3 (#808080, 회색), 수동진행 : 4 (#CC6600, 갈색), 현시 유지 : 5 (#9933FF, 보라)
 class Ring{
-    constructor({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, state}) {
+    constructor({service_id, node_id, ring, phase_no, flow_no, head_lat, head_lng, mid_lat, mid_lng, end_lat, end_lng, head_angle, end_angle, state}) {
         this.service_id = service_id;
-        this.seq_no = seq_no;
         this.node_id = node_id;
         this.ring = ring;
         this.phase_no = phase_no;
-        this.plan_class = plan_class;
         this.flow_no = flow_no;
         this.head_lat = head_lat;
         this.head_lng = head_lng;
@@ -1657,6 +1645,7 @@ class Ring{
         this.head_angle = head_angle;
         this.end_angle = end_angle;
         this.state = state;
+        this.init();
     }
 
     init() {
@@ -1679,13 +1668,13 @@ class Ring{
             });
     }
 
-    drawLine() {
+    show() {
         if (this.polyLine) {
             this.polyLine.setMap(map);
         }
     }
 
-    hideLine() {
+    hide() {
         if (this.polyLine) {
             this.polyLine.setMap(null);
         }
@@ -1702,17 +1691,30 @@ class Ring{
             this.polyLine.setOptions(option);
         }
     }
+
+    setState(color) {
+        if (this.polyLine) {
+            this.polyLine.setOptions({
+                strokeColor : color,
+            });
+        }
+    }
 }
 
-class Sig{
-    constructor({clct_dt, service_id, node_id, node_nm, lat, lng, seq_no, rem_dist, state, plan_class, a_ring_phase, b_ring_phase, hold_phase}) {
+const sigMap = new Map();
+
+class EmergencySig{
+    constructor({clct_dt, service_id, node_id, node_nm, lat, lng, rem_dist, state, plan_class, a_ring_phase, b_ring_phase,
+                    hold_phase, a_flow_no, a_head_angle, a_end_angle, a_head_lat, a_head_lng, a_mid_lat, a_mid_lng, a_end_lat,
+                    a_end_lng, b_flow_no, b_head_angle, b_end_angle, b_head_lat, b_head_lng, b_mid_lat, b_mid_lng, b_end_lat,
+                    b_end_lng, seq_no}) {
+        this.seq_no = seq_no;
         this.clct_dt = clct_dt;
         this.service_id = service_id;
         this.node_id = node_id;
         this.node_nm = node_nm;
         this.lat = lat;
         this.lng = lng;
-        this.seq_no = seq_no;
         this.rem_dist = rem_dist;
         this.state = state;
         this.plan_class = plan_class;
@@ -1721,7 +1723,26 @@ class Sig{
         this.hold_phase = hold_phase;
         this.a_ring = null;
         this.b_ring = null;
-        this.marker = null;
+        this.circle = null;
+        this.a_flow_no = a_flow_no;
+        this.b_flow_no = b_flow_no;
+        this.a_head_angle = a_head_angle;
+        this.b_head_angle = b_head_angle;
+        this.a_end_angle = a_end_angle;
+        this.b_end_angle = b_end_angle;
+        this.a_head_lat = a_head_lat;
+        this.a_head_lng = a_head_lng;
+        this.b_head_lat = b_head_lat;
+        this.b_head_lng = b_head_lng;
+        this.a_mid_lat = a_mid_lat;
+        this.a_mid_lng = a_mid_lng;
+        this.b_mid_lat = b_mid_lat;
+        this.b_mid_lng = b_mid_lng;
+        this.a_end_lat = a_end_lat;
+        this.a_end_lng = a_end_lng;
+        this.b_end_lat = b_end_lat;
+        this.b_end_lng = b_end_lng;
+        this.init();
     }
 
     init() {
@@ -1729,40 +1750,238 @@ class Sig{
         if (!isNaN(Number(this.state))) {
             stateColor = stateMap.get(Number(this.state));
         }
-        this.marker = new kakao.maps.Circle({
-            center : getKakaoPosition(this.lng, this.lat),  // 원의 중심좌표 입니다
-            radius: 50, // 미터 단위의 원의 반지름입니다
-            strokeWeight: 5, // 선의 두께입니다
-            strokeColor: stateColor, // 선의 색깔입니다
-            strokeOpacity: 1, // 선의 불투명도 입니다 1에서 0 사이의 값이며 0에 가까울수록 투명합니다
-            strokeStyle: 'solid', // 선의 스타일 입니다
-            fillColor: stateColor, // 채우기 색깔입니다
-            fillOpacity: 0.7, // 채우기 불투명도 입니다
-            zIndex: 1
-        });
+        const position = getKakaoPosition(this.lat, this.lng);
+        if (!sigMap.get(this.lat + '_' + this.lng)) {
+            this.circle = new kakao.maps.Circle({
+                center : position,  // 원의 중심좌표 입니다
+                radius: 60, // 미터 단위의 원의 반지름입니다
+                strokeWeight: 5, // 선의 두께입니다
+                strokeColor: stateColor, // 선의 색깔입니다
+                strokeOpacity: 1, // 선의 불투명도 입니다 1에서 0 사이의 값이며 0에 가까울수록 투명합니다
+                strokeStyle: 'solid', // 선의 스타일 입니다
+                fillColor: stateColor, // 채우기 색깔입니다
+                fillOpacity: 0.3, // 채우기 불투명도 입니다
+                zIndex: 2,
+                name : '신호현시',
+            });
+            sigMap.set(this.lat + '_' + this.lng, this.circle);
+        }
+
+        this.createRing(this);
+
+        //if (map.getLevel() < 5) {
+        // this.circle.setMap(map);
+        // this.a_ring.show();
+        // this.b_ring.show();
+        this.show();
+        //}
+    }
+
+    createRing(data) {
+        const stateArr = [1,2,4,5];
+        if (stateArr.includes(data.state)) {
+            this.a_ring = new Ring({
+                service_id : data.service_id,
+                node_id : data.node_id,
+                ring : 1,
+                phase_no : data.a_ring_phase,
+                flow_no : data.a_flow_no,
+                head_lat : data.a_head_lat,
+                head_lng : data.a_head_lng,
+                mid_lat : data.a_mid_lat,
+                mid_lng : data.a_mid_lng,
+                end_lag : data.a_end_lat,
+                end_lng : data.a_end_lng,
+                head_angle : data.a_head_angle,
+                end_angle : data.a_end_angle,
+                state : data.state
+            });
+
+            this.b_ring = new Ring({
+                service_id : data.service_id,
+                node_id    : data.node_id,
+                ring       : 2,
+                phase_no   : data.b_ring_phase,
+                flow_no    : data.b_flow_no,
+                head_lat   : data.b_head_lat,
+                head_lng   : data.b_head_lng,
+                mid_lat    : data.b_mid_lat,
+                mid_lng    : data.b_mid_lng,
+                end_lag    : data.b_end_lat,
+                end_lng    : data.b_end_lng,
+                head_angle : data.b_head_angle,
+                end_angle  : data.b_end_angle,
+                state      : data.state
+            });
+
+            this.a_ring.show();
+            this.b_ring.show();
+        }
     }
 
-    drawMarker() {
-        this.marker.setMap(map);
+    getStateColor(state) {
+        let stateColor = '#FF0000';
+        if (!isNaN(Number(state))) {
+            stateColor = stateMap.get(Number(state));
+        }
+        return stateColor;
+    }
+
+    setState(state) {
+        const stateColor = this.getStateColor(state);
+        if (this.circle) {
+            this.circle.setOptions({
+                strokeColor: stateColor,
+                fillColor : stateColor,
+            });
+        }
+
         if (this.a_ring) {
-            this.a_ring.setMap(map);
+            this.a_ring.setState(stateColor);
         }
+
         if (this.b_ring) {
-            this.b_ring.setMap(map);
+            this.b_ring.setState(stateColor);
         }
     }
+    show() {
+        if (this.circle) {
+            this.circle.setMap(map);
+        }
 
-    hideMarker() {
-        this.marker.setMap(null);
         if (this.a_ring) {
-            this.a_ring.setMap(null);
+            this.a_ring.show();
         }
+
         if (this.b_ring) {
-            this.b_ring.setMap(null);
+            this.b_ring.show();
+        }
+    }
+
+    hide() {
+        if (this.circle) {
+            this.circle.setMap(null);
+        }
+
+        if (this.a_ring) {
+            this.a_ring.hide();
+        }
+        if (this.b_ring) {
+            this.b_ring.hide();
         }
     }
 
     setCenter() {
         map.setCenter(getKakaoPosition(this.lng, this.lat));
     }
+}
+
+
+class EmergencyRoadObj{
+    constructor(pathArr) {
+        this.pathArr = pathArr;
+        this.backPolyLine = null;
+        this.polyLine = null;
+        this.kakaoPathArr = [];
+        this.init();
+    }
+
+    init() {
+
+        if (this.pathArr && this.pathArr.length) {
+            this.pathArr.forEach((obj)=>{
+                this.kakaoPathArr.push(getKakaoPosition(obj.lat, obj.lng));
+            });
+
+            this.backPolyLine = new kakao.maps.Polyline({
+                path: this.kakaoPathArr, // 선을 구성하는 좌표 배열입니다 클릭한 위치를 넣어줍니다
+                strokeWeight: 10, // 선의 두께입니다
+                strokeColor: '#ffffff', // 선의 색깔입니다
+                strokeOpacity: 1, // 선의 불투명도입니다 0에서 1 사이값이며 0에 가까울수록 투명합니다
+                strokeStyle: 'solid', // 선의 스타일입니다
+                zIndex: 1,
+            });
+
+            // 선이 그려지고 있을 때 마우스 움직임에 따라 선이 그려질 위치를 표시할 선을 생성합니다
+            this.polyLine = new kakao.maps.Polyline({
+                strokeWeight: 8, // 선의 두께입니다
+                path: this.kakaoPathArr,
+                strokeColor: '#3386ff', // 선의 색깔입니다
+                strokeOpacity: 1, // 선의 불투명도입니다 0에서 1 사이값이며 0에 가까울수록 투명합니다
+                strokeStyle: 'solid', // 선의 스타일입니다
+                zIndex: 2,
+            });
+
+            this.show();
+        }
+    }
+
+    show() {
+        if (this.backPolyLine) {
+            this.backPolyLine.setMap(map);
+        }
+        if (this.polyLine) {
+            this.polyLine.setMap(map);
+        }
+    }
+
+    hide() {
+        if (this.backPolyLine) {
+            this.backPolyLine.setMap(null);
+        }
+        if (this.polyLine) {
+            this.polyLine.setMap(null);
+        }
+    }
+
+    setBound() {
+        const bounds = new kakao.maps.LatLngBounds();
+        bounds.extend(this.kakaoPathArr[0]);
+        bounds.extend(this.kakaoPathArr[this.pathArr.length - 1]);
+        map.setBounds(bounds);
+    }
+}
+
+class EmergencyMarker{
+    constructor(position, img) {
+        this.position = position;
+        this.marker = null;
+        this.imgSrc = img;
+        this.init();
+    }
+
+    init() {
+        const imgSrc = this.imgSrc;
+        let markImage = new kakao.maps.MarkerImage(
+            imgSrc,
+            new kakao.maps.Size(32, 39), {
+                 offset: new kakao.maps.Point(16, 39)
+            });
+
+        const position = this.position;
+        this.marker = new kakao.maps.Marker({
+            image: markImage,
+            position: position,
+            zIndex: 11
+        });
+        this.show();
+    }
+
+    show() {
+        if (this.marker) {
+            this.marker.setMap(map);
+        }
+    }
+
+    hide() {
+        if (this.marker) {
+            this.marker.setMap(null);
+        }
+    }
+
+    moveMarker(position) {
+        if (this.marker) {
+            this.marker.setPosition(position);
+        }
+    }
 }

+ 11 - 8
src/main/resources/static/js/signal.js

@@ -261,20 +261,23 @@ Signal.prototype = {
         this.listDrawSignal = [];
         var markerSize = 0;
         if (_Level >= 5) {
-            markerSize = 15;
+            markerSize = 10;
         } else if (_Level >= 3) {
-            markerSize = 25;
+            markerSize = 20;
         } else {
-            markerSize = 35;
+            markerSize = 20;
         }
-
+        let offset = markerSize / 2;
         var icon = new kakao.maps.MarkerImage(
-            'images/TrafficLight25.png',
-            new kakao.maps.Size(markerSize, markerSize),
+            // 'images/TrafficLight25.png',
+            'images/sig5.png',
+            new kakao.maps.Size(markerSize * 3, markerSize),
             {
 
-                alt: "신호현시"
-            }
+                alt: "신호현시",
+                offset: new kakao.maps.Point(offset * 3, offset)
+            },
+
         );
 
         this.mapCircle = new kakao.maps.Marker({

+ 23 - 19
src/main/resources/static/js/worker.js

@@ -12,29 +12,33 @@ self.onmessage = (e)=> {
         if (timer) clearTimeout(timer);
 
         const xhr = new XMLHttpRequest();
-        xhr.onreadystatechange = (e)=> {
-            if(xhr.readyState === 4){
-                if(xhr.status === 200){
-                    const resultData = xhr.responseText;
-                    console.log(resultData);
-                    if (resultData) {
-                        postMessage(resultData);
-                    }
-                    let end  = new Date().getTime();
-                    let between  = end - start;
-                    if (between >= 1000) {
-                        between = 1000;
-                    }
 
-                    timer = setTimeout(()=>{
-                        callEvpServiceList();
-                    }, time - between)
-                }
-            }
-        }
         xhr.open('POST', '/getEvpServiceList.do', true);
         xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
         xhr.responseType = 'text';
+        xhr.send()  //요청 전송
+        xhr.onload = () => {
+            //정상 코드 200
+            if(xhr.status === 200) {
+                const res = xhr.response;
+                if (res) {
+                    postMessage(res);
+                }
+                let end  = new Date().getTime();
+                let between  = end - start;
+                if (between >= 1000) {
+                    between = 1000;
+                }
+
+                timer = setTimeout(()=>{
+                    callEvpServiceList();
+                }, time - between)
+                //postMessage(res);
+            } else {
+                //에러발생
+                console.error(xhr.status, xhr.statusText); //응답상태와 응답 메시지 출력
+            }
+        }
 
     }
 }

+ 2 - 42
src/main/webapp/WEB-INF/jsp/bottomListFrame.jsp

@@ -15,43 +15,8 @@
 </head>
 <body class="sang">
 <div class="bottomMenu" id="bottomMenu">
-    <div class="networkType">
-        <!-- <a href="javascript:showOnlineList()" title="유선"><img src="/images/online_on.png" id="onlineToggle" alt="" /></a> -->
-        <!-- <a href="javascript:showOfflineList()" title="무선"><img src="/images/offline_on.png" id="offlineToggle" alt="" /></a> -->
-    </div>
-    <!-- <div class="bottomHead" id="bottomHead">
-        <table id="bottomHeadTable">
-            <thead>
-                <tr>
-                    <th style="width:4%">센터명</th>
-                    <th style="width:5%">그룹번호</th>
-                    <th style="width:5%">제어기번호</th>
-                    <th style="width:12%">교차로명</th>
-                    <th style="width:8%">수집일시</th>
-                    <th style="width:5%">통신여부</th>
-                    <th style="width:8%">운영모드</th>
-                    <th style="width:6%">시차제모드</th>
-                    <th style="width:6%">A링 현시값</th>
-                    <th style="width:6%">B링 현시값</th>
-                    <th style="width:4%">소등</th>
-                    <th style="width:4%">점멸</th>
-                    <th style="width:4%">수동</th>
-                    <th style="width:7%">주기카운트(초)</th>
-                    <th style="width:5%">주기(초)</th>
-                    <th style="width:5%">맵번호</th>
-                    <th style="width:6%">PPC제어</th>
-                </tr>
-            </thead>
-        </table>
-        <span style="width:20px"></span>
-    </div>
-    <div class="bottomBody" id="bottomBody">
-        <table id="bottomBodyTable">
-            <tbody id="bottomInfo"></tbody>
-        </table>
-    </div> -->
-    <div class="bottomHead" id="cvibBottomHead">
-        <table id="cvbiBottomHeadTable">
+    <div class="bottomBody" id="cvibBottomBody">
+        <table id="cvibBottomBodyTable">
             <thead>
             <tr>
                 <th style="width:5%">센터명</th>
@@ -72,11 +37,6 @@
                 <th style="width:10%">교차로시각</th>
             </tr>
             </thead>
-        </table>
-        <span style="width:20px"></span>
-    </div>
-    <div class="bottomBody" id="cvibBottomBody">
-        <table id="cvibBottomBodyTable">
             <tbody id="cvibBottomInfo"></tbody>
         </table>
     </div>

+ 32 - 15
src/main/webapp/WEB-INF/jsp/cvibInfoDetail.jsp

@@ -465,7 +465,38 @@
 
             <div class="bottomInfo">
                 <div class="bottomInfoBottom">
-                    <div class="bottomInfoHead">
+<%--                    <div class="bottomInfoHead">--%>
+<%--                        <table>--%>
+<%--                            <colgroup>--%>
+<%--                                <col width="7%">--%>
+<%--                                <col width="8%">--%>
+<%--                                <col width="9%">--%>
+<%--                                <col width="9%">--%>
+<%--                                <col width="10%">--%>
+<%--                                <col width="6%">--%>
+<%--                                <col width="10%">--%>
+<%--                                <col width="10%">--%>
+<%--                            </colgroup>--%>
+<%--                            <thead>--%>
+<%--                            <tr>--%>
+<%--                                <td colspan="2">방향정보</td>--%>
+<%--                                <td colspan="4">출력정보</td>--%>
+<%--                                <td colspan="2">기타</td>--%>
+<%--                            </tr>--%>
+<%--                            <tr>--%>
+<%--                                <td>보행자</td>--%>
+<%--                                <td>방향코드</td>--%>
+<%--                                <td>시간신뢰성</td>--%>
+<%--                                <td>출력형태</td>--%>
+<%--                                <td>신호등상태</td>--%>
+<%--                                <td>비보호</td>--%>
+<%--                                <td>잔여시간</td>--%>
+<%--                                <td>표출시간</td>--%>
+<%--                            </tr>--%>
+<%--                            </thead>--%>
+<%--                        </table>--%>
+<%--                    </div>--%>
+                    <div class="bottomInfoBody">
                         <table>
                             <colgroup>
                                 <col width="7%">
@@ -494,20 +525,6 @@
                                 <td>표출시간</td>
                             </tr>
                             </thead>
-                        </table>
-                    </div>
-                    <div class="bottomInfoBody">
-                        <table>
-                            <colgroup>
-                                <col width="7%">
-                                <col width="8%">
-                                <col width="9%">
-                                <col width="9%">
-                                <col width="10%">
-                                <col width="6%">
-                                <col width="10%">
-                                <col width="10%">
-                            </colgroup>
                             <tbody id="bottomInfoBottom"></tbody>
                         </table>
                     </div>

+ 18 - 29
src/main/webapp/WEB-INF/jsp/main.jsp

@@ -67,42 +67,21 @@
 <input type="hidden" name="modemIp" id="modemIp" value=""/>
 
 <div class="headMenu">
-    <h1><a href="main.do" title="로고"><img src="images/logo.png" alt="로고" style="width: 250px;margin: 3px;"></a></h1>
+    <h1><a href="main.do" title="로고"><img src="images/logo3.png" alt="로고" style="width: 310px;margin: 3px;"></a></h1>
     <h2 style="width: calc(100% - 340px); display: flex; justify-content: flex-end; height: 100%; align-items: center;">
         <a href="logout.do" title="로그아웃"><img src="images/logout.png" alt="로그아웃"></a>
     </h2>
 
     <ul>
-        <!--
-	    <li class="toggleBtn">
-	    	<a href="javascript:void(0)" class="ims"><img src="${contextRoot}/images/btn_04.png" id="ims" class="road_off" alt="돌발정보"></a>
-	    </li>
-	     -->
-      <%--
-      <c:if test="${userType == 'SA' }">
-            <li class="toggleBtn">
-                <a href="javascript:void(0)" class="center"><img src="${contextRoot}/images/btn_02.png" id="center"
-                                                                 class="road_off" alt="신호센터"></a>
-            </li>
-            <li class="toggleBtn">
-                <a href="javascript:void(0)" class="event"><img src="${contextRoot}/images/btn_03.png" id="event"
-                                                                class="road_off" alt="로그관리"></a>
-            </li>
-            <li class="toggleBtn">
-                <a href="javascript:void(0)" class="addSignal"><img src="${contextRoot}/images/btn_05.png"
-                                                                    id="addSignal" class="road_off" alt="정보관리"></a>
-            </li>
-        </c:if>
-        --%>
+
     </ul>
 </div>
-<%--<div class="hline" id="hline"></div>--%>
 <div id="container" class="container">
     <iframe src="/getTreeListFrame.do" id="iframeTreeList" class="iframeTreeList"></iframe>
     <div id="mapWrapper">
         <div id="map" class="map">
-            <div class="wideMap"><img alt="지도확대축소" src="images/arrow_left.png"/></div>
-            <div class="upDownMap"><img alt="지도확대축소" src="images/arrow_down.png"/></div>
+            <div class="wideMap"><img alt="지도확대축소" src="images/drop_down.png" width="45" height="25"/></div>
+            <div class="upDownMap"><img alt="지도확대축소" src="images/drop_down.png" width="45" height="25"/></div>
             <!-- 검색값-->
             <div class="bg_white" id="menu_wrap">
                 <div class="option">
@@ -160,6 +139,12 @@
                             src="${contextRoot}/images/refresh.png"
                             alt="새로고침"/></a>
                     </li>
+<%--                    <li class="toggleBtn" style="margin-left:5px; float:left;"><a href="javascript:emergencyHistory();"--%>
+<%--                                                                                  class="refresh"><img--%>
+<%--                            class="refresh"--%>
+<%--                            src="${contextRoot}/images/refresh.png"--%>
+<%--                            alt="테스트"/></a>--%>
+<%--                    </li>--%>
                 </ul>
             </div>
             <!-- <img src="images/bg_traffic04.png" alt="범례" class="bumImage" /> -->
@@ -382,7 +367,8 @@
     $('.wideMap').click(function () {
         const isRoadView = $('.road_on')[0];
         if (wideFlag === false) {
-            $('.wideMap img').attr('src', '/images/arrow_right.png');
+            // $('.wideMap img').attr('src', '/images/arrow_right.png');
+            $('.wideMap img').attr('src', '/images/drop_up.png');
             $('.iframeTreeList').hide();
             const widthVal = isRoadView ? 'calc(60% - 10px)' : 'calc(100% - 10px)';
             $('#mapWrapper').css({
@@ -398,7 +384,8 @@
                 $('.mylocationMob').css('left', '25px');
             wideFlag = true;
         } else if (wideFlag === true) {
-            $('.wideMap img').attr('src', '/images/arrow_left.png');
+            // $('.wideMap img').attr('src', '/images/arrow_left.png');
+            $('.wideMap img').attr('src', '/images/drop_down.png');
             $('.iframeTreeList').show();
             const widthVal = isRoadView ? 'calc(60% - 338px)' : 'calc(100% - 338px)';
             $('#mapWrapper').css({
@@ -420,7 +407,8 @@
     $('.upDownMap').click(function () {
         if (upDownFlag == false) {
             //확대
-            $('.upDownMap img').attr('src', 'images/arrow_up.png');
+            // $('.upDownMap img').attr('src', 'images/arrow_up.png');
+            $('.upDownMap img').attr('src', 'images/drop_up.png');
             $('.iframeBottomList').hide();
             $('#mapWrapper').css('height', 'calc(100% - 12px)');
             if (UserAgent.match(/iPhone|iPod|Android|Windows CE|BlackBerry|Symbian|Windows Phone|webOS|Opera Mini|Opera Mobi|POLARIS|IEMobile|lgtelecom|nokia|SonyEricsson/i) != null || UserAgent.match(/LG|SAMSUNG|Samsung/) != null)
@@ -428,7 +416,8 @@
             upDownFlag = true;
         } else if (upDownFlag == true) {
             //축소
-            $('.upDownMap img').attr('src', 'images/arrow_down.png').slide;
+            // $('.upDownMap img').attr('src', 'images/arrow_down.png').slide;
+            $('.upDownMap img').attr('src', 'images/drop_down.png').slide;
             $('.iframeBottomList').show();
             $('#mapWrapper').css('height', 'calc(75% - 10px)');
             if (UserAgent.match(/iPhone|iPod|Android|Windows CE|BlackBerry|Symbian|Windows Phone|webOS|Opera Mini|Opera Mobi|POLARIS|IEMobile|lgtelecom|nokia|SonyEricsson/i) != null || UserAgent.match(/LG|SAMSUNG|Samsung/) != null)

+ 67 - 13
src/main/webapp/WEB-INF/jsp/treeListFrame.jsp

@@ -16,15 +16,32 @@
 <div class="leftMenu">
     <ul class="tabs">
         <li><a href="javascript:goIntMenu()" class="tab" id="tab1"><img src="images/tab01_on.png" alt="교차로메뉴"/></a></li>
-        <!-- <li><a href="javascript:goGroupMenu()" class="tab" id="tab2"><img src="images/tab02_off.png" alt="신호그룹메뉴" /></a></li> -->
+        <li><a href="javascript:goEvpMenu()" class="tab" id="tab2"><img src="images/tab02_off.png" alt="긴급차량우선신호메뉴"/></a></li>
+        <div style="position: absolute; left: 95px; top: 5px; display: none;"><img src="images/map/btn_alert_A.gif" style="width: 22px"></div>
     </ul>
     <div id="onOffTree"></div>
     <div class="searchBox">
         <input type="text" id="searchText" placeholder="검색어를 입력해주세요" autocomplete="false" onkeyup="searchTreeData(event)">
         <div class="search-button" onclick="searchTreeData()">검색</div>
     </div>
+    <div class="historyBox">
+        <input type="date" id="start" autocomplete="false">
+        <input type="date" id="end" autocomplete="false">
+        <div class="search-button" onclick="searchHistoryData()">검색</div>
+    </div>
     <div id="intTree"></div>
-    <div id="groupTree"></div>
+    <div id="historyList">
+        <table>
+            <thead>
+                <tr>
+                    <th>발생시각</th>
+                    <th>서비스 ID</th>
+                </tr>
+            </thead>
+            <tbody class="history-table">
+            </tbody>
+        </table>
+    </div>
     <div class="treeLoading"><img src="/css/themes/classic/throbber.gif"/></div>
 </div>
 <script type="text/javascript" src="${contextRoot }/js/common/moment.js"></script>
@@ -53,7 +70,7 @@
     var offlineIntStatusData;
     let _regionMap = new Map();
     var _searchText = "";
-
+    let timeout = null;
     function setAddrMap(data) {
 
         _regionMap.clear();
@@ -78,8 +95,7 @@
 
     setTreeList();
     getIntTreeList();
-
-    var timeout = setInterval(controllerOnOffFuc, 1000);
+    controllerOnOffFuc();
 
     function searchTreeData(el) {
         if (el && el.code !== "Enter") {
@@ -100,8 +116,10 @@
     * 신호 제어기 상태 정보
     * 1 : 정상, 2: 교차로 시각 오류 0: 마지막통신 시간 3초이상 지연시
      */
-    function controllerOnOffFuc() {
 
+    function controllerOnOffFuc() {
+        if (timeout) clearTimeout(timeout);
+        const start = new Date();
         var addrArr = getAddrSi();
         for(var ll = 0; ll < addrArr.length; ll++) {
 
@@ -111,6 +129,7 @@
             var preDate = moment(now.getTime()).add("-30", "s").format("YYYY-MM-DD HH:mm:ss");
             var nxtDate = moment(now.getTime()).add("30", "s").format("YYYY-MM-DD HH:mm:ss");
 
+           // console.log( window.parent._signalInfoArr);
             window.parent._signalInfoArr.forEach(function (el, idx) {
                 if (el.addr1 == addrArr[ll]) {
                     ctnlCount++;
@@ -144,7 +163,7 @@
                         // } //
                     }
 
-                    var commOnOffImg = 'background-image:url("images/intStatus_' + el.status + '.png");background-position:center center';
+                    // var commOnOffImg = 'background-image:url("images/intStatus_' + el.status + '.png");background-position:center center';
                     /*if (el.status == 1) {
                         commOnOffImg = 'background-image:url("images/intStatusOn.png");background-position:center center';
                     } else if(el.status == 2) {
@@ -154,12 +173,21 @@
                         //errorCount++;
                         commOnOffImg = 'background-image:url("images/intStatusNull.png");background-position:center center';
                     }*/
-                    $('#' + el.nodeId + ' .jstree-anchor .jstree-icon').attr('style', commOnOffImg);
+                    //$('#' + el.nodeId + ' .jstree-anchor .jstree-icon').attr('style', commOnOffImg);
+                    $('#' + el.nodeId + ' .jstree-anchor .jstree-icon').css({backgroundImage: 'url("images/intStatus_' + el.status + '.png")',
+                        backgroundPosition: 'center center'});
                 }
             });
             $('#intErrCnt_'+getEngAddr(addrArr[ll])).text(errorCount);
             $('#intTotal_'+getEngAddr(addrArr[ll])).text(ctnlCount);
         }
+        const end = new Date();
+        let diff = end.getTime() - start.getTime();
+        if (diff >= 1000) {
+            diff = 1000;
+        }
+
+        timeout = setTimeout(()=>controllerOnOffFuc(), 1000 - diff);
     }
 
     function setTreeList() {
@@ -293,7 +321,7 @@
 
         //TODO: 20230221
         setAddrMap(data);
-        window.parent._signalInfoArr = [];
+        window.parent._signalInfoArr.length = 0;
         for (var i = 0; i < data.length; i++) {
             if (data[i].addr1 == null || data[i].addr1 == '-') {
                 continue;
@@ -316,8 +344,11 @@
 
             var engAddr = getEngAddr(addr1);
 
-            nameCntOn = '&nbsp;<strong class="centerNm_' + engAddr + '">' + addr1 + '</strong>&nbsp;( 전체 : <span class="intTotal" id="intTotal_'+engAddr+'" style="color:#0000FF"></span> / 이상 : '+
-					  '<span class="intErrCnt" id="intErrCnt_'+engAddr+'" style="color:#FF0000"></span> )';
+            nameCntOn = '&nbsp;<strong class="centerNm_' + engAddr + '">' + addr1 +
+                      // '</strong>&nbsp;( 전체 : <span class="intTotal" id="intTotal_'+engAddr+'" style="color:#0000FF"></span> / 이상 : '+
+                      '</strong>&nbsp;( 전체 : <span class="intTotal" id="intTotal_'+engAddr+'" style="color:#3396ff"></span> / 이상 : '+
+					  // '<span class="intErrCnt" id="intErrCnt_'+engAddr+'" style="color:#FF0000"></span> )';
+					  '<span class="intErrCnt" id="intErrCnt_'+engAddr+'" style="color:#ef6060"></span> )';
 
             //guTitleOff = nameCntOn + '-' + addr2 + '&nbsp;';
 
@@ -394,10 +425,10 @@
 
             if (parentNd == '#') //시도 선택
             {
-                //console.log("시도 선택했음");
+                console.log("시도 선택했음");
             } else if (parentNd != '#' && children.length > 0) //구 선택
             {
-                //console.log("구 선택했음");
+                console.log("구 선택했음");
             } else {
                 var intNm = data.node.data.intNm;
                 var lng = data.node.data.lng;
@@ -562,6 +593,29 @@
         }
     }
 
+
+    function goIntMenu() {
+        $('#tab1 img').attr('src','images/tab01_on.png');
+        $('#tab2 img').attr('src','images/tab02_off.png');
+
+        // $('.treeLoading').hide();
+        $('#historyList').hide();
+        $('#intTree').show();
+        $('.searchBox').css('display', 'flex');
+        $('.historyBox').css('display', 'none');
+
+    }
+
+    function goEvpMenu() {
+        $('#tab1 img').attr('src','images/tab01_off.png');
+        $('#tab2 img').attr('src','images/tab02_on.png');
+
+        // $('.treeLoading').show();
+        $('#historyList').show();
+        $('#intTree').hide();
+        $('.searchBox').css('display', 'none');
+        $('.historyBox').css('display', 'flex');
+    }
 </script>
 </body>
 </html>