Procházet zdrojové kódy

firt commit - database job completed

shjung před 2 roky
rodič
revize
a874057a88
28 změnil soubory, kde provedl 1839 přidání a 158 odebrání
  1. 0 1
      conf/vms-comm-server.pid
  2. 9 0
      pom.xml
  3. 23 0
      src/main/java/com/its/app/utils/Converter.java
  4. 39 0
      src/main/java/com/its/app/utils/Counter.java
  5. 123 0
      src/main/java/com/its/vms/api/controller/VmsControlController.java
  6. 185 0
      src/main/java/com/its/vms/api/dto/CctvControlDto.java
  7. 144 0
      src/main/java/com/its/vms/api/dto/CctvParamControlDto.java
  8. 144 0
      src/main/java/com/its/vms/api/dto/CctvPresetControlDto.java
  9. 88 0
      src/main/java/com/its/vms/api/dto/CctvPtzControlDto.java
  10. 76 0
      src/main/java/com/its/vms/api/dto/CctvStatusDto.java
  11. 157 0
      src/main/java/com/its/vms/api/dto/CctvVarCharControlDto.java
  12. 79 0
      src/main/java/com/its/vms/api/dto/TbCctvCtlrSttsDto.java
  13. 429 0
      src/main/java/com/its/vms/api/service/VmsControlService.java
  14. 32 0
      src/main/java/com/its/vms/api/websocket/ItsWebSocketConfig.java
  15. 67 0
      src/main/java/com/its/vms/api/websocket/ItsWebSocketHandler.java
  16. 18 0
      src/main/java/com/its/vms/api/websocket/ItsWebSocketMessage.java
  17. 35 0
      src/main/java/com/its/vms/api/websocket/ItsWebSocketSession.java
  18. 68 0
      src/main/java/com/its/vms/api/websocket/ItsWebSocketSessionManager.java
  19. 4 4
      src/main/java/com/its/vms/config/SwaggerConfig.java
  20. 12 8
      src/main/java/com/its/vms/domain/VmsFormObject.java
  21. 0 37
      src/main/java/com/its/vms/global/AppRepository.java
  22. 20 8
      src/main/java/com/its/vms/service/VmsManageService.java
  23. 27 38
      src/main/java/com/its/vms/webapp/config/WebSecurityConfig.java
  24. 9 54
      src/main/java/com/its/vms/webapp/controller/WebAppController.java
  25. 1 0
      src/main/java/com/its/vms/xnettcp/vms/protocol/VmsProtocolConst.java
  26. 42 0
      src/main/java/com/its/vms/xnettcp/vms/protocol/enums/eVmsOpCode.java
  27. 7 7
      src/main/webapp/WEB-INF/jsp/controller.jsp
  28. 1 1
      src/main/webapp/WEB-INF/jsp/head.jsp

+ 0 - 1
conf/vms-comm-server.pid

@@ -1 +0,0 @@
-111168

+ 9 - 0
pom.xml

@@ -60,6 +60,15 @@
             <artifactId>spring-boot-starter-test</artifactId>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-websocket</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.fusesource.jansi</groupId>
+            <artifactId>jansi</artifactId>
+            <version>1.8</version>
+        </dependency>
 
         <dependency>
             <groupId>com.jcabi</groupId>

+ 23 - 0
src/main/java/com/its/app/utils/Converter.java

@@ -0,0 +1,23 @@
+package com.its.app.utils;
+
+public class Converter {
+    private Converter() {
+    }
+
+    public static String getSize(double bytes) {
+        long kilo = 1024L;
+        double kb = bytes / 1024.0D;
+        double mb = kb / 1024.0D;
+        double gb = mb / 1024.0D;
+        double tb = gb / 1024.0D;
+        if (tb > 1.0D) {
+            return String.format("%.2f TB", tb);
+        } else if (gb > 1.0D) {
+            return String.format("%.2f GB", gb);
+        } else if (mb > 1.0D) {
+            return String.format("%.2f MB", mb);
+        } else {
+            return kb > 1.0D ? String.format("%.2f KB", kb) : String.format("%.2f Bytes", bytes);
+        }
+    }
+}

+ 39 - 0
src/main/java/com/its/app/utils/Counter.java

@@ -0,0 +1,39 @@
+package com.its.app.utils;
+
+import java.util.concurrent.atomic.AtomicLong;
+
+public class Counter {
+    private AtomicLong counter = new AtomicLong(0L);
+
+    public Counter() {
+    }
+
+    public long reset() {
+        return this.counter.getAndSet(0L);
+    }
+
+    public long reset(long value) {
+        return this.counter.getAndSet(0L);
+    }
+
+    public long increment() {
+        return this.counter.incrementAndGet();
+    }
+
+    public long add(long value) {
+        return this.counter.addAndGet(value);
+    }
+
+    public long decrement() {
+        return this.counter.decrementAndGet();
+    }
+
+    public long get() {
+        return this.counter.get();
+    }
+
+    public String toString() {
+        return Converter.getSize(this.counter.doubleValue());
+    }
+
+}

+ 123 - 0
src/main/java/com/its/vms/api/controller/VmsControlController.java

@@ -0,0 +1,123 @@
+package com.its.vms.api.controller;
+
+import com.its.vms.api.dto.*;
+import com.its.vms.api.service.VmsControlService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import javax.validation.Valid;
+
+@Slf4j
+@Api(tags = "1.VMS-0.제어")
+@Validated
+@RestController
+@RequiredArgsConstructor
+@RequestMapping("/api/vms/control")
+public class VmsControlController {
+
+    private final VmsControlService service;
+
+//    @ApiOperation(value = "TEST", response = CctvStatusDto.CctvStatusNotifyDtoRes.class)
+//    @GetMapping(value = "/status/notify/{id}", produces = {"application/json; charset=utf8"})
+//    public CctvStatusDto.CctvStatusNotifyDtoRes notifyStatus(
+//            @ApiParam(name = "id", value = "제어기번호", example = "1001", required = true)
+//            @PathVariable("id") Long id) {
+//        return this.service.notifyStatus(id);
+//    }
+
+    @ApiOperation(value = "VMS 환경 파라미터 조회", response = CctvParamControlDto.CctvParamValueRes.class)
+    @GetMapping(value = "/param-qry/{id}", produces = {"application/json; charset=utf8"})
+    public CctvParamControlDto.CctvParamValueRes requestParam(
+            @ApiParam(name = "id", value = "제어기번호", example = "1001", required = true)
+            @PathVariable("id") Long id) {
+        return this.service.requestParam(id);
+    }
+
+    @ApiOperation(value = "VMS 환경 파라미터 설정", response = CctvParamControlDto.CctvParamControlRes.class)
+    //@PostMapping(value = "/param-set/{id}", produces = {"application/json; charset=utf8"})
+    @RequestMapping(value = "/param-set/{id}", method = {RequestMethod.GET, RequestMethod.POST})
+    public CctvParamControlDto.CctvParamControlRes controlParam(
+            @ApiParam(name = "id", value = "제어기번호", example = "1001", required = true)
+            @PathVariable("id") Long id,
+            @ApiParam(name = "req", value = "CCTV PARAMETER 설정 정보", example = "[ADMIN]", required = true)
+            @RequestBody @Valid final CctvParamControlDto.CctvParamControlReq req) {
+        return this.service.controlParam(id, req);
+    }
+
+    @ApiOperation(value = "VMS 상태 조회", response = CctvStatusDto.CctvStatusDtoRes.class)
+    @GetMapping(value = "/status/{id}", produces = {"application/json; charset=utf8"})
+    public CctvStatusDto.CctvStatusDtoRes requestStatus(
+            @ApiParam(name = "id", value = "제어기번호", example = "1001", required = true)
+            @PathVariable("id") Long id) {
+        return this.service.requestStatus(id);
+    }
+
+    @ApiOperation(value = "VMS 현재위치정보 조회", response = CctvPresetControlDto.CctvPresetValueRes.class)
+    @GetMapping(value = "/preset-value/{id}", produces = {"application/json; charset=utf8"})
+    public CctvPresetControlDto.CctvPresetValueRes requestPresetValue(
+            @ApiParam(name = "id", value = "제어기번호", example = "1001", required = true)
+            @PathVariable("id") Long id) {
+        return this.service.requestPresetValue(id);
+    }
+
+    @ApiOperation(value = "VMS PTZ 제어", response = CctvPtzControlDto.CctvPtzControlRes.class)
+    //@PostMapping(value = "/ptz/{id}", produces = {"application/json; charset=utf8"})
+    @RequestMapping(value = "/ptz/{id}", method = {RequestMethod.GET, RequestMethod.POST})
+    public CctvPtzControlDto.CctvPtzControlRes controlPtz(
+            @ApiParam(name = "id", value = "제어기번호", example = "1001", required = true)
+            @PathVariable("id") Long id,
+            @ApiParam(name = "req", value = "VMS PTZ 제어 정보", example = "[tilt-up, start, 20, ADMIN]", required = true)
+            @RequestBody @Valid final CctvPtzControlDto.CctvControlPtzReq req) {
+        return this.service.controlPtz(id, req);
+    }
+
+    @ApiOperation(value = "VMS Preset 제어", response = CctvPresetControlDto.CctvPresetControlRes.class)
+    //@PostMapping(value = "/preset/{id}", produces = {"application/json; charset=utf8"})
+    @RequestMapping(value = "/preset/{id}", method = {RequestMethod.GET, RequestMethod.POST})
+    public CctvPresetControlDto.CctvPresetControlRes controlPreset(
+            @ApiParam(name = "id", value = "제어기번호", example = "1001", required = true)
+            @PathVariable("id") Long id,
+            @ApiParam(name = "req", value = "VMS Preset 제어 정보", example = "[preset, call, 1, 0, 0, ADMIN]", required = true)
+            @RequestBody @Valid final CctvPresetControlDto.CctvPresetControlReq req) {
+        return this.service.controlPreset(id, req);
+    }
+
+    @ApiOperation(value = "VMS 가변 문자 설정", response = CctvVarCharControlDto.CctvVarCharControlRes.class)
+    //@PostMapping(value = "/var-char-set/{id}", produces = {"application/json; charset=utf8"})
+    @RequestMapping(value = "/var-char-set/{id}", method = {RequestMethod.GET, RequestMethod.POST})
+    public CctvVarCharControlDto.CctvVarCharControlRes controlVarCharSet(
+            @ApiParam(name = "id", value = "제어기번호", example = "1001", required = true)
+            @PathVariable("id") Long id,
+            @ApiParam(name = "req", value = "VMS 가변문자 설정 정보", example = "[preset, call, 1, 0, 0, ADMIN]", required = true)
+            @RequestBody @Valid final CctvVarCharControlDto.CctvVarCharControlSetReq req) {
+        return this.service.controlVarCharSet(id, req);
+    }
+
+    @ApiOperation(value = "VMS 가변 문자 삭제", response = CctvVarCharControlDto.CctvVarCharControlRes.class)
+    //@PostMapping(value = "/var-char-del/{id}", produces = {"application/json; charset=utf8"})
+    @RequestMapping(value = "/var-char-del/{id}", method = {RequestMethod.GET, RequestMethod.POST})
+    public CctvVarCharControlDto.CctvVarCharControlRes controlVarCharDel(
+            @ApiParam(name = "id", value = "제어기번호", example = "1001", required = true)
+            @PathVariable("id") Long id,
+            @ApiParam(name = "req", value = "VMS 가변문자 삭제 정보", example = "[preset, call, 1, 0, 0, ADMIN]", required = true)
+            @RequestBody @Valid final CctvVarCharControlDto.CctvVarCharControlDelReq req) {
+        return this.service.controlVarCharDel(id, req);
+    }
+
+    @ApiOperation(value = "VMS RESET", response = CctvControlDto.CctvControlResetRes.class)
+    //@PostMapping(value = "/reset/{id}", produces = {"application/json; charset=utf8"})
+    @RequestMapping(value = "/reset/{id}", method = {RequestMethod.GET, RequestMethod.POST})
+    public CctvControlDto.CctvControlResetRes controlReset(
+            @ApiParam(name = "id", value = "제어기번호", example = "1001", required = true)
+            @PathVariable("id") Long id,
+            @ApiParam(name = "req", value = "VMS 제어기 리셋", example = "[0/1, ADMIN]", required = true)
+            @RequestBody @Valid final CctvControlDto.CctvControlResetReq req) {
+        return this.service.controlReset(id, req);
+    }
+
+}

+ 185 - 0
src/main/java/com/its/vms/api/dto/CctvControlDto.java

@@ -0,0 +1,185 @@
+package com.its.vms.api.dto;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.*;
+
+import javax.validation.constraints.Positive;
+import javax.validation.constraints.PositiveOrZero;
+import java.io.Serializable;
+
+/**
+ * CCTV 제어기 제어정보 DTO Class
+ */
+@Data
+@Builder
+@ApiModel("CctvControlDto(CCTV 제어 정보")
+public class CctvControlDto implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    @ApiModel("CctvControlPtzReq(CCTV PTZ 제어 정보)")
+    @Getter
+    @Setter
+    @ToString
+    @NoArgsConstructor//(access = AccessLevel.PROTECTED)
+    public static class CctvControlPtzReq {
+
+        @ApiModelProperty("제어 명령(tilt-up, tilt-down, pan-left, pan-right, zoom-in, zoom-out, focus-in, focus-out, up-left, up-right, down-left, down-right)")
+        @JsonProperty("command")
+        private String command;
+
+        @ApiModelProperty("제어 명령 제어(start, stop)")
+        @JsonProperty("action")
+        private String action;
+
+        @ApiModelProperty("제어속도(0~63)")
+        @JsonProperty("speed")
+        @PositiveOrZero
+        private Integer speed;
+
+        @ApiModelProperty("로그인 사용자 ID")
+        @JsonProperty("user_id")
+        private String userId;
+
+        @Builder
+        public CctvControlPtzReq(String command, String action, Integer speed, String user_id) {
+            this.command = command;
+            this.action = action;
+            this.speed = speed;
+            this.userId = user_id;
+        }
+    }
+    @ApiModel("CctvControlPresetReq(CCTV Preset 제어 정보)")
+    @Getter
+    @Setter
+    @ToString
+    @NoArgsConstructor//(access = AccessLevel.PROTECTED)
+    public static class CctvControlPresetReq {
+
+        @ApiModelProperty("제어 명령(preset-default)")
+        @JsonProperty("command")
+        private String command;
+
+        @ApiModelProperty("제어 명령 제어(call, save, delete)")
+        @JsonProperty("action")
+        private String action;
+
+        @ApiModelProperty("프리셋 번호(1~255)")
+        @JsonProperty("no")
+        @Positive
+        private Integer no;
+
+        @ApiModelProperty("속도(0~63), save only")
+        @JsonProperty("speed")
+        @PositiveOrZero
+        private Integer speed;
+
+        @ApiModelProperty("프리셋 타임(0~60 sec), save only")
+        @JsonProperty("time")
+        @PositiveOrZero
+        private Integer time;
+
+        @ApiModelProperty("프리셋 명(영문명만, 20 byte) save only, not use.")
+        @JsonProperty("name")
+        private String name;
+
+        @ApiModelProperty("로그인 사용자 ID")
+        @JsonProperty("user_id")
+        private String userId;
+
+        @Builder
+        public CctvControlPresetReq(String command, String action, Integer no, Integer speed, Integer time, String userId) {
+            this.command = command;
+            this.action = action;
+            this.no = no;
+            this.speed = speed;
+            this.time = time;
+            this.userId = userId;
+        }
+    }
+
+    @ApiModel("CctvControlResetReq(CCTV 제어기 리셋)")
+    @Getter
+    @Setter
+    @ToString
+    @NoArgsConstructor//(access = AccessLevel.PROTECTED)
+    public static class CctvControlResetReq {
+
+        @ApiModelProperty("로그인 사용자 ID")
+        @JsonProperty("user_id")
+        private String userId;
+
+        @ApiModelProperty("RESET 유형(0: SW, 1: HW")
+        @JsonProperty("reset_type")
+        private Integer resetType;
+
+        @Builder
+        public CctvControlResetReq(String user_id, Integer reset_type) {
+            this.userId = user_id;
+            this.resetType = reset_type;
+        }
+    }
+
+    @ApiModel("CctvControlResetRes(CCTV 제어기 리셋 응답)")
+    @Getter
+    @Setter
+    @ToString
+    @NoArgsConstructor//(access = AccessLevel.PROTECTED)
+    public static class CctvControlResetRes {
+
+        @ApiModelProperty("제어결과(0: 성공, 기타: 오류")
+        @JsonProperty("error")
+        private Integer error;
+
+        @ApiModelProperty("제어결과메시지")
+        @JsonProperty("message")
+        private String message;
+
+        public void setResult(Integer error, String message) {
+            this.error = error;
+            this.message = message;
+        }
+        @Builder
+        public CctvControlResetRes(Integer error, String message) {
+            this.error = error;
+            this.message = message;
+        }
+    }
+
+    @ApiModel("CctvControlRes(CCTV Control 응답)")
+    @Getter
+    @Setter
+    @ToString
+    @NoArgsConstructor//(access = AccessLevel.PROTECTED)
+    public static class CctvControlRes {
+
+        @ApiModelProperty("제어 명령(tilt-up, tilt-down, pan-left, pan-right, zoom-in, zoom-out, focus-in, focus-out, up-left, up-right, down-left, down-right), 프리셋제어(call, save, delete)")
+        @JsonProperty("command")
+        private String command;
+
+        @ApiModelProperty("제어 명령 제어(start, stop), 프리셋제어(call, save, delete)")
+        @JsonProperty("action")
+        private String action;
+
+        @ApiModelProperty("제어결과(0: 성공, 기타: 오류")
+        @JsonProperty("error")
+        private Integer error;
+
+        @ApiModelProperty("제어결과메시지")
+        @JsonProperty("message")
+        private String message;
+
+        public void setResult(Integer error, String message) {
+            this.error = error;
+            this.message = message;
+        }
+        @Builder
+        public CctvControlRes(String command, String action, Integer error, String message) {
+            this.command = command;
+            this.action = action;
+            this.error = error;
+            this.message = message;
+        }
+    }
+}

+ 144 - 0
src/main/java/com/its/vms/api/dto/CctvParamControlDto.java

@@ -0,0 +1,144 @@
+package com.its.vms.api.dto;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.*;
+
+import javax.validation.constraints.PositiveOrZero;
+import java.io.Serializable;
+
+/**
+ * CCTV 제어기 프리셋제어 DTO Class
+ */
+@Data
+@Builder
+@ApiModel("CctvParamControlDto(CCTV 파라미터설정 정보")
+public class CctvParamControlDto implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    @ApiModel("CctvParamControlReq(CCTV 파라미터설정 정보)")
+    @Getter
+    @Setter
+    @ToString
+    @NoArgsConstructor//(access = AccessLevel.PROTECTED)
+    public static class CctvParamControlReq {
+
+        @ApiModelProperty("FAN ON 설정 온도")
+        @JsonProperty("fan_on_tmpr")
+        @PositiveOrZero
+        private Integer fanOnTmpr;
+
+        @ApiModelProperty("FAN OFF 설정 온도")
+        @JsonProperty("fan_off_tmpr")
+        @PositiveOrZero
+        private Integer fanOffTmpr;
+
+        @ApiModelProperty("Heater ON 설정 온도")
+        @JsonProperty("hetr_on_tmpr")
+        @PositiveOrZero
+        private Integer hetrOnTmpr;
+
+        @ApiModelProperty("Heater OFF 설정 온도")
+        @JsonProperty("hetr_off_tmpr")
+        @PositiveOrZero
+        private Integer hetrOffTmpr;
+
+        @ApiModelProperty("로그인 사용자 ID")
+        @JsonProperty("user_id")
+        private String userId;
+
+        @Builder
+        public CctvParamControlReq(Integer fan_on_tmpr, Integer fan_off_tmpr, Integer hetr_on_tmpr, Integer hetr_off_tmpr, String user_id) {
+            this.fanOnTmpr = fan_on_tmpr;
+            this.fanOffTmpr = fan_off_tmpr;
+            this.hetrOnTmpr = hetr_on_tmpr;
+            this.hetrOffTmpr = hetr_off_tmpr;
+            this.userId = user_id;
+        }
+    }
+
+    @ApiModel("CctvParamControlRes(CCTV 파라미터설정 응답)")
+    @Getter
+    @Setter
+    @ToString
+    @NoArgsConstructor//(access = AccessLevel.PROTECTED)
+    public static class CctvParamControlRes {
+
+        @ApiModelProperty("제어결과(0: 성공, 기타: 오류")
+        @JsonProperty("error")
+        private Integer error;
+
+        @ApiModelProperty("제어결과메시지")
+        @JsonProperty("message")
+        private String message;
+
+        public void setResult(Integer error, String message) {
+            this.error = error;
+            this.message = message;
+        }
+        @Builder
+        public CctvParamControlRes(Integer error, String message) {
+            this.error = error;
+            this.message = message;
+        }
+    }
+
+    @ApiModel("CctvParamValueRes(CCTV 파라미터설정 정보)")
+    @Getter
+    @Setter
+    @ToString
+    @NoArgsConstructor//(access = AccessLevel.PROTECTED)
+    public static class CctvParamValueRes {
+
+        @ApiModelProperty("제어결과(0: 성공, 기타: 오류")
+        @JsonProperty("error")
+        private Integer error;
+
+        @ApiModelProperty("제어결과메시지")
+        @JsonProperty("message")
+        private String message;
+
+        @ApiModelProperty("FAN ON 설정 온도")
+        @JsonProperty("fan_on_tmpr")
+        @PositiveOrZero
+        private Integer fanOnTmpr;
+
+        @ApiModelProperty("FAN OFF 설정 온도")
+        @JsonProperty("fan_off_tmpr")
+        @PositiveOrZero
+        private Integer fanOffTmpr;
+
+        @ApiModelProperty("Heater ON 설정 온도")
+        @JsonProperty("hetr_on_tmpr")
+        @PositiveOrZero
+        private Integer hetrOnTmpr;
+
+        @ApiModelProperty("Heater OFF 설정 온도")
+        @JsonProperty("hetr_off_tmpr")
+        @PositiveOrZero
+        private Integer hetrOffTmpr;
+
+        public CctvParamValueRes(Integer error, String message) {
+            this.error = error;
+            this.message = message;
+            this.fanOnTmpr = 0;
+            this.fanOffTmpr = 0;
+            this.hetrOnTmpr = 0;
+            this.hetrOffTmpr = 0;
+        }
+
+        public void setResult(Integer error, String message) {
+            this.error = error;
+            this.message = message;
+        }
+
+        public void setValue(Integer fanOnTmpr, Integer fanOffTmpr, Integer hetrOnTmpr, Integer hetrOffTmpr) {
+            this.fanOnTmpr = fanOnTmpr;
+            this.fanOffTmpr = fanOffTmpr;
+            this.hetrOnTmpr = hetrOnTmpr;
+            this.hetrOffTmpr = hetrOffTmpr;
+        }
+    }
+
+}

+ 144 - 0
src/main/java/com/its/vms/api/dto/CctvPresetControlDto.java

@@ -0,0 +1,144 @@
+package com.its.vms.api.dto;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.*;
+
+import javax.validation.constraints.PositiveOrZero;
+import java.io.Serializable;
+
+/**
+ * CCTV 제어기 프리셋제어 DTO Class
+ */
+@Data
+@Builder
+@ApiModel("CctvPresetControlDto(CCTV 프리셋제어 정보")
+public class CctvPresetControlDto implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    @ApiModel("CctvPresetControlReq(CCTV 프리셋제어 정보)")
+    @Getter
+    @Setter
+    @ToString
+    @NoArgsConstructor//(access = AccessLevel.PROTECTED)
+    public static class CctvPresetControlReq {
+
+        @ApiModelProperty("Pan Preset Value")
+        @JsonProperty("pan")
+        @PositiveOrZero
+        private Integer pan;
+
+        @ApiModelProperty("Tilt Preset Value")
+        @JsonProperty("tilt")
+        @PositiveOrZero
+        private Integer tilt;
+
+        @ApiModelProperty("Zoom Preset Value")
+        @JsonProperty("zoom")
+        @PositiveOrZero
+        private Integer zoom;
+
+        @ApiModelProperty("Focus Preset Value")
+        @JsonProperty("focus")
+        @PositiveOrZero
+        private Integer focus;
+
+        @ApiModelProperty("로그인 사용자 ID")
+        @JsonProperty("user_id")
+        private String userId;
+
+        @Builder
+        public CctvPresetControlReq(Integer pan, Integer tilt, Integer zoom, Integer focus, String user_id) {
+            this.pan = pan;
+            this.tilt = tilt;
+            this.zoom = zoom;
+            this.focus = focus;
+            this.userId = user_id;
+        }
+    }
+
+    @ApiModel("CctvPresetControlRes(CCTV 프리셋제어 응답)")
+    @Getter
+    @Setter
+    @ToString
+    @NoArgsConstructor//(access = AccessLevel.PROTECTED)
+    public static class CctvPresetControlRes {
+
+        @ApiModelProperty("제어결과(0: 성공, 기타: 오류")
+        @JsonProperty("error")
+        private Integer error;
+
+        @ApiModelProperty("제어결과메시지")
+        @JsonProperty("message")
+        private String message;
+
+        public void setResult(Integer error, String message) {
+            this.error = error;
+            this.message = message;
+        }
+        @Builder
+        public CctvPresetControlRes(Integer error, String message) {
+            this.error = error;
+            this.message = message;
+        }
+    }
+
+    @ApiModel("CctvPresetValueRes(CCTV 프리셋 상태 정보)")
+    @Getter
+    @Setter
+    @ToString
+    @NoArgsConstructor//(access = AccessLevel.PROTECTED)
+    public static class CctvPresetValueRes {
+
+        @ApiModelProperty("제어결과(0: 성공, 기타: 오류")
+        @JsonProperty("error")
+        private Integer error;
+
+        @ApiModelProperty("제어결과메시지")
+        @JsonProperty("message")
+        private String message;
+
+        @ApiModelProperty("Pan Preset Value")
+        @JsonProperty("pan")
+        @PositiveOrZero
+        private Short pan;
+
+        @ApiModelProperty("Tilt Preset Value")
+        @JsonProperty("tilt")
+        @PositiveOrZero
+        private Short tilt;
+
+        @ApiModelProperty("Zoom Preset Value")
+        @JsonProperty("zoom")
+        @PositiveOrZero
+        private Short zoom;
+
+        @ApiModelProperty("Focus Preset Value")
+        @JsonProperty("focus")
+        @PositiveOrZero
+        private Short focus;
+
+        public CctvPresetValueRes(Integer error, String message) {
+            this.error = error;
+            this.message = message;
+            this.pan = 0;
+            this.tilt = 0;
+            this.zoom = 0;
+            this.focus = 0;
+        }
+
+        public void setResult(Integer error, String message) {
+            this.error = error;
+            this.message = message;
+        }
+
+        public void setValue(Short pan, Short tilt, Short zoom, Short focus) {
+            this.pan = pan;
+            this.tilt = tilt;
+            this.zoom = zoom;
+            this.focus = focus;
+        }
+    }
+
+}

+ 88 - 0
src/main/java/com/its/vms/api/dto/CctvPtzControlDto.java

@@ -0,0 +1,88 @@
+package com.its.vms.api.dto;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.*;
+
+import javax.validation.constraints.PositiveOrZero;
+import java.io.Serializable;
+
+/**
+ * CCTV 제어기 제어정보 DTO Class
+ */
+@Data
+@Builder
+@ApiModel("CctvControlDto(CCTV 제어 정보")
+public class CctvPtzControlDto implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    @ApiModel("CctvControlPtzReq(CCTV PTZ 제어 정보)")
+    @Getter
+    @Setter
+    @ToString
+    @NoArgsConstructor//(access = AccessLevel.PROTECTED)
+    public static class CctvControlPtzReq {
+
+        @ApiModelProperty("제어 명령(tilt-up, tilt-down, pan-left, pan-right, zoom-in, zoom-out, focus-in, focus-out, up-left, up-right, down-left, down-right)")
+        @JsonProperty("command")
+        private String command;
+
+        @ApiModelProperty("제어 명령 제어(start, stop)")
+        @JsonProperty("action")
+        private String action;
+
+        @ApiModelProperty("제어속도(0~63)")
+        @JsonProperty("speed")
+        @PositiveOrZero
+        private Integer speed;
+
+        @ApiModelProperty("로그인 사용자 ID")
+        @JsonProperty("user_id")
+        private String userId;
+
+        @Builder
+        public CctvControlPtzReq(String command, String action, Integer speed, String user_id) {
+            this.command = command;
+            this.action = action;
+            this.speed = speed;
+            this.userId = user_id;
+        }
+    }
+
+    @ApiModel("CctvControlRes(CCTV Control 응답)")
+    @Getter
+    @Setter
+    @ToString
+    @NoArgsConstructor//(access = AccessLevel.PROTECTED)
+    public static class CctvPtzControlRes {
+
+        @ApiModelProperty("제어 명령(tilt-up, tilt-down, pan-left, pan-right, zoom-in, zoom-out, focus-in, focus-out, up-left, up-right, down-left, down-right), 프리셋제어(call, save, delete)")
+        @JsonProperty("command")
+        private String command;
+
+        @ApiModelProperty("제어 명령 제어(start, stop), 프리셋제어(call, save, delete)")
+        @JsonProperty("action")
+        private String action;
+
+        @ApiModelProperty("제어결과(0: 성공, 기타: 오류")
+        @JsonProperty("error")
+        private Integer error;
+
+        @ApiModelProperty("제어결과메시지")
+        @JsonProperty("message")
+        private String message;
+
+        public void setResult(Integer error, String message) {
+            this.error = error;
+            this.message = message;
+        }
+        @Builder
+        public CctvPtzControlRes(String command, String action, Integer error, String message) {
+            this.command = command;
+            this.action = action;
+            this.error = error;
+            this.message = message;
+        }
+    }
+}

+ 76 - 0
src/main/java/com/its/vms/api/dto/CctvStatusDto.java

@@ -0,0 +1,76 @@
+package com.its.vms.api.dto;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.*;
+
+import java.io.Serializable;
+
+/**
+ * CCTV 제어기 상태 DTO Class
+ */
+@Data
+@Builder
+@ApiModel("CctvStatusDto(CCTV 상태 정보")
+public class CctvStatusDto implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    @ApiModel("CctvStatusDtoRes(CCTV 상태 응답)")
+    @Getter
+    @Setter
+    @ToString
+    @NoArgsConstructor//(access = AccessLevel.PROTECTED)
+    public static class CctvStatusDtoRes {
+
+        @ApiModelProperty("제어결과(0: 성공, 기타: 오류")
+        @JsonProperty("error")
+        private Integer error;
+
+        @ApiModelProperty("제어결과메시지")
+        @JsonProperty("message")
+        private String message;
+
+        private TbCctvCtlrSttsDto stts;
+
+        public void setResult(Integer error, String message, TbCctvCtlrSttsDto stts) {
+            this.error = error;
+            this.message = message;
+            this.stts = stts;
+        }
+
+        @Builder
+        public CctvStatusDtoRes(Integer error, String message, TbCctvCtlrSttsDto stts) {
+            this.error = error;
+            this.message = message;
+            this.stts = stts;
+        }
+    }
+
+    @ApiModel("CctvStatusNotifyDtoRes(CCTV 상태 Notify 응답)")
+    @Getter
+    @Setter
+    @ToString
+    @NoArgsConstructor//(access = AccessLevel.PROTECTED)
+    public static class CctvStatusNotifyDtoRes {
+
+        @ApiModelProperty("제어결과(0: 성공, 기타: 오류")
+        @JsonProperty("error")
+        private Integer error;
+
+        @ApiModelProperty("제어결과메시지")
+        @JsonProperty("message")
+        private String message;
+
+        public void setResult(Integer error, String message) {
+            this.error = error;
+            this.message = message;
+        }
+
+        @Builder
+        public CctvStatusNotifyDtoRes(Integer error, String message) {
+            this.error = error;
+            this.message = message;
+        }
+    }
+}

+ 157 - 0
src/main/java/com/its/vms/api/dto/CctvVarCharControlDto.java

@@ -0,0 +1,157 @@
+package com.its.vms.api.dto;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.*;
+
+import javax.validation.constraints.PositiveOrZero;
+import javax.validation.constraints.Size;
+import java.io.Serializable;
+
+/**
+ * CCTV 가변 문자 설정 DTO Class
+ */
+@Data
+@Builder
+@ApiModel("CctvVarCharControlDto(CCTV 가변 문자 설정 정보")
+public class CctvVarCharControlDto implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    @ApiModel("CctvVarCharControlSetReq(CCTV 가변 문자 설정)")
+    @Getter
+    @Setter
+    @ToString
+    @NoArgsConstructor//(access = AccessLevel.PROTECTED)
+    public static class CctvVarCharControlSetReq {
+
+        @ApiModelProperty("Sector No. 0~6")
+        @JsonProperty("sector_no")
+        @PositiveOrZero
+        private Integer sectorNo;
+
+        @ApiModelProperty("Start Pan Value 0~1024")
+        @JsonProperty("start_pan")
+        @PositiveOrZero
+        private Short startPan;
+
+        @ApiModelProperty("End Pan Value 0~1024")
+        @JsonProperty("end_pan")
+        @PositiveOrZero
+        private Short endPan;
+
+        @ApiModelProperty("CH1 Character Size(Small: 0x02, Middle:0x03, Large:0x04)")
+        @JsonProperty("ch1_char_size")
+        @PositiveOrZero
+        private Short ch1CharSize;
+
+        @ApiModelProperty("CH1 Character X Position Value 0~1280")
+        @JsonProperty("ch1_pos_x")
+        @PositiveOrZero
+        private Short ch1PosX;
+
+        @ApiModelProperty("CH1 Character Y Position Value 0~720")
+        @JsonProperty("ch1_pos_y")
+        @PositiveOrZero
+        private Short ch1PosY;
+
+        @ApiModelProperty("CH2 Character Size(Small: 0x02, Middle:0x03, Large:0x04)")
+        @JsonProperty("ch2_char_size")
+        @PositiveOrZero
+        private Short ch2CharSize;
+
+        @ApiModelProperty("CH2 Character X Position Value 0~1280")
+        @JsonProperty("ch2_pos_x")
+        @PositiveOrZero
+        private Short ch2PosX;
+
+        @ApiModelProperty("CH2 Character Y Position Value 0~720")
+        @JsonProperty("ch2_pos_y")
+        @PositiveOrZero
+        private Short ch2PosY;
+
+        @ApiModelProperty("CH1 Character, Max: 16char(kor),32char(eng)")
+        @JsonProperty("ch1_char")
+        @Size(min=1, max=32)
+        private String ch1Char;
+
+        @ApiModelProperty("CH2 Character, Max: 16char(kor),32char(eng)")
+        @JsonProperty("ch2_char")
+        @Size(min=1, max=32)
+        private String ch2Char;
+
+        @ApiModelProperty("로그인 사용자 ID")
+        @JsonProperty("user_id")
+        private String userId;
+
+        @Builder
+        public CctvVarCharControlSetReq(Integer sector_no, Short start_pan, Short end_pan,
+                                        Short ch1_char_size, Short ch1_pos_x, Short ch1_pos_y,
+                                        Short ch2_char_size, Short ch2_pos_x, Short ch2_pos_y,
+                                        String ch1_char, String ch2_char,
+                                        String user_id) {
+            this.sectorNo = sector_no;
+            this.startPan = start_pan;
+            this.endPan = end_pan;
+            this.ch1CharSize = ch1_char_size;
+            this.ch1PosX = ch1_pos_x;
+            this.ch1PosY = ch1_pos_y;
+            this.ch2CharSize = ch2_char_size;
+            this.ch2PosX = ch2_pos_x;
+            this.ch2PosY = ch2_pos_y;
+            this.ch1Char = ch1_char;
+            this.ch2Char = ch2_char;
+            this.userId = user_id;
+        }
+    }
+
+    @ApiModel("CctvVarCharControlDelReq(CCTV 가변 문자 지우기)")
+    @Getter
+    @Setter
+    @ToString
+    @NoArgsConstructor//(access = AccessLevel.PROTECTED)
+    public static class CctvVarCharControlDelReq {
+
+        @ApiModelProperty("Sector No. 0~6")
+        @JsonProperty("sector_no")
+        @PositiveOrZero
+        private Integer sectorNo;
+
+        @ApiModelProperty("로그인 사용자 ID")
+        @JsonProperty("user_id")
+        private String userId;
+
+        @Builder
+        public CctvVarCharControlDelReq(Integer sector_no, String user_id) {
+            this.sectorNo = sector_no;
+            this.userId = user_id;
+        }
+    }
+
+    @ApiModel("CctvVarCharControlRes(CCTV 가변 문자 설정 응답)")
+    @Getter
+    @Setter
+    @ToString
+    @NoArgsConstructor//(access = AccessLevel.PROTECTED)
+    public static class CctvVarCharControlRes {
+
+        @ApiModelProperty("제어결과(0: 성공, 기타: 오류")
+        @JsonProperty("error")
+        private Integer error;
+
+        @ApiModelProperty("제어결과메시지")
+        @JsonProperty("message")
+        private String message;
+
+        public void setResult(Integer error, String message) {
+            this.error = error;
+            this.message = message;
+        }
+        @Builder
+        public CctvVarCharControlRes(Integer error, String message) {
+            this.error = error;
+            this.message = message;
+        }
+    }
+
+}

+ 79 - 0
src/main/java/com/its/vms/api/dto/TbCctvCtlrSttsDto.java

@@ -0,0 +1,79 @@
+package com.its.vms.api.dto;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Builder;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * CCTV 상태 DTO Class
+ */
+@Data
+@Builder
+@ApiModel("TbCctvCtlrSttsDto(CCTV 상태)")
+public class TbCctvCtlrSttsDto implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty("CCTV 관리 번호")  // N NUMBER(10)
+    @JsonProperty("cctv_ctlr_nmbr")
+    private Long cctvCtlrNmbr;
+
+    @ApiModelProperty("갱신 일시")  // Y VARCHAR2(14)
+    @JsonProperty("updt_dt")
+    private String updtDt;
+
+    @ApiModelProperty("통신 상태 코드")  // Y VARCHAR2(7)
+    @JsonProperty("cmnc_stts_cd")
+    private String cmncSttsCd;
+
+    @ApiModelProperty("함체 문 상태 코드('CDS')-앞문/뒷문상태가 모두닫힌경우 닫힘")  // Y VARCHAR2(7)
+    @JsonProperty("cbox_door_stts_cd")
+    private String cboxDoorSttsCd;
+
+    @ApiModelProperty("앞문 상태 코드('CDS')")  // Y VARCHAR2(7)
+    @JsonProperty("front_door_stts_cd")
+    private String frontDoorSttsCd;
+
+    @ApiModelProperty("뒷문 상태 코드('CDS')")  // Y VARCHAR2(7)
+    @JsonProperty("back_door_stts_cd")
+    private String backDoorSttsCd;
+
+    @ApiModelProperty("팬 상태 코드('PAS')")  // Y VARCHAR2(7)
+    @JsonProperty("fan_stts_cd")
+    private String fanSttsCd;
+
+    @ApiModelProperty("히터 상태 코드('HTS')")  // Y VARCHAR2(7)
+    @JsonProperty("hetr_stts_cd")
+    private String hetrSttsCd;
+
+    @ApiModelProperty("Video Input 여부('VDI')")  // Y VARCHAR2(7)
+    @JsonProperty("video_input")
+    private String videoInput;
+
+    @ApiModelProperty("함체 온도")  // Y NUMBER(3)
+    @JsonProperty("cbox_tmpr")
+    private Integer cboxTmpr;
+
+    @ApiModelProperty("함체 습도")  // Y NUMBER(3)
+    @JsonProperty("cbox_hmdt")
+    private Integer cboxHmdt;
+
+    @ApiModelProperty("PAN")  // Y NUMBER(6)
+    @JsonProperty("pan")
+    private Integer pan;
+
+    @ApiModelProperty("TILT")  // Y NUMBER(6)
+    @JsonProperty("tilt")
+    private Integer tilt;
+
+    @ApiModelProperty("확대/축소범위")  // Y NUMBER(6)
+    @JsonProperty("zoom")
+    private Integer zoom;
+
+    @ApiModelProperty("FOCUS")  // Y NUMBER(6)
+    @JsonProperty("focus")
+    private Integer focus;
+}

+ 429 - 0
src/main/java/com/its/vms/api/service/VmsControlService.java

@@ -0,0 +1,429 @@
+package com.its.vms.api.service;
+
+import com.its.vms.api.dto.*;
+import com.its.vms.domain.NET;
+import com.its.vms.dto.TbVmsCtlrDto;
+import com.its.vms.service.AppRepositoryService;
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+import lombok.Setter;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.PostConstruct;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.NoSuchElementException;
+
+@Slf4j
+@RequiredArgsConstructor
+@Service
+public class VmsControlService {
+    
+    private final AppRepositoryService repoService;
+
+    Map<String, ControlCmd> ptzControlMap = new HashMap<>();
+
+    @PostConstruct
+    void init() {
+        this.ptzControlMap.put("zoom-in",    new ControlCmd((byte)0, (byte)0, (byte)1, (byte)0, (byte)0,    (byte)0,    (byte)0xFE, (byte)0,    "Zoom In"));
+        this.ptzControlMap.put("zoom-out",   new ControlCmd((byte)0, (byte)0, (byte)2, (byte)0, (byte)0,    (byte)0,    (byte)0xFE, (byte)0,    "Zoom Out"));
+        this.ptzControlMap.put("focus-in",   new ControlCmd((byte)0, (byte)0, (byte)0, (byte)1, (byte)0,    (byte)0,    (byte)0,    (byte)0xFE, "Focus Near"));
+        this.ptzControlMap.put("focus-out",  new ControlCmd((byte)0, (byte)0, (byte)0, (byte)2, (byte)0,    (byte)0,    (byte)0,    (byte)0xFE, "Focus Far"));
+        this.ptzControlMap.put("pan-left",   new ControlCmd((byte)1, (byte)0, (byte)0, (byte)0, (byte)0xFE, (byte)0,    (byte)0,    (byte)0,    "Pan Left"));
+        this.ptzControlMap.put("pan-right",  new ControlCmd((byte)2, (byte)0, (byte)0, (byte)0, (byte)0xFE, (byte)0,    (byte)0,    (byte)0,    "Pan Right"));
+        this.ptzControlMap.put("tilt-up",    new ControlCmd((byte)0, (byte)1, (byte)0, (byte)0, (byte)0,    (byte)0xFE, (byte)0,    (byte)0,    "Tilt Up"));
+        this.ptzControlMap.put("tilt-down",  new ControlCmd((byte)0, (byte)2, (byte)0, (byte)0, (byte)0,    (byte)0xFE, (byte)0,    (byte)0,    "Tilt Down"));
+        this.ptzControlMap.put("up-left",    new ControlCmd((byte)1, (byte)1, (byte)0, (byte)0, (byte)0xFE, (byte)0xFE, (byte)0,    (byte)0,    "Pan Tilt Left Up"));
+        this.ptzControlMap.put("up-right",   new ControlCmd((byte)2, (byte)1, (byte)0, (byte)0, (byte)0xFE, (byte)0xFE, (byte)0,    (byte)0,    "Pan Tilt Right Up"));
+        this.ptzControlMap.put("down-left",  new ControlCmd((byte)1, (byte)2, (byte)0, (byte)0, (byte)0xFE, (byte)0xFE, (byte)0,    (byte)0,    "Pan Tilt Left Down"));
+        this.ptzControlMap.put("down-right", new ControlCmd((byte)2, (byte)2, (byte)0, (byte)0, (byte)0xFE, (byte)0xFE, (byte)0,    (byte)0,    "Pan Tilt Right Down"));
+    }
+
+    @Getter
+    @Setter
+    public static class ControlCmd {
+        byte pan;
+        byte tilt;
+        byte zoom;
+        byte focus;
+        byte panSped;
+        byte tiltSped;
+        byte zoomSped;
+        byte focusSped;
+        String cmdDesc;
+        public ControlCmd(byte pan, byte tilt, byte zoom, byte focus, byte panSped, byte tiltSped, byte zoomSped, byte focusSped, String cmdDesc) {
+            this.pan = pan;
+            this.tilt = tilt;
+            this.zoom = zoom;
+            this.focus = focus;
+            this.panSped = panSped;
+            this.tiltSped = tiltSped;
+            this.zoomSped = zoomSped;
+            this.focusSped = focusSped;
+            this.cmdDesc = cmdDesc;
+        }
+    }
+
+    // 데이터 1건 조회, 없으면 exception
+    private TbVmsCtlrDto requireOne(Long id) throws NoSuchElementException {
+        return this.repoService.getCtrlMap(id);
+    }
+
+    /**
+     * VMS 상태정보 요청 처리
+     * @param id
+     * @return
+     */
+    public CctvStatusDto.CctvStatusDtoRes requestStatus(Long id) {
+        log.info("requestStatus: {}", id);
+        TbVmsCtlrDto obj = this.requireOne(id);
+        if (obj == null) {
+            return new CctvStatusDto.CctvStatusDtoRes(1, "알수없는 VMS 관리번호 입니다.", null);
+        }
+        if (obj.getNetState() != NET.LOGINED) {
+            return new CctvStatusDto.CctvStatusDtoRes(2, "VMS 제어기와 통신연결이 되어 있지 않습니다.", null);
+        }
+
+        return new CctvStatusDto.CctvStatusDtoRes(0, "success", null);//obj.getStts().toDto());
+    }
+
+    /**
+     * VMS PTZ 컨트롤
+     * @param id
+     * @param req
+     * @return
+     */
+    public CctvPtzControlDto.CctvPtzControlRes controlPtz(Long id, CctvPtzControlDto.CctvControlPtzReq req) {
+        log.info("controlPtz: {}, {}", id, req);
+        TbVmsCtlrDto obj = this.requireOne(id);
+        if (obj == null) {
+            return new CctvPtzControlDto.CctvPtzControlRes(req.getCommand(), req.getAction(), 1, "알수없는 VMS 관리번호 입니다.");
+        }
+        if (obj.getNetState() != NET.LOGINED) {
+            return new CctvPtzControlDto.CctvPtzControlRes(req.getCommand(), req.getAction(), 2, "VMS 제어기와 통신연결이 되어 있지 않습니다.");
+        }
+
+        ControlCmd command = this.ptzControlMap.get(req.getCommand());
+        if (command == null) {
+            return new CctvPtzControlDto.CctvPtzControlRes(req.getCommand(), req.getAction(), 3, "VMS PTZ 명령이 잘못 되었습니다.");
+        }
+
+        CctvPtzControlDto.CctvPtzControlRes result = new CctvPtzControlDto.CctvPtzControlRes(req.getCommand(), req.getAction(), 0, "success");
+        String cmdDesc = command.getCmdDesc();
+//        if (StringUtils.equalsIgnoreCase("start", req.getAction())) {
+//            byte speed = req.getSpeed().byteValue();
+//            byte pan = command.getPan();
+//            byte tilt = command.getTilt();
+//            byte zoom = command.getZoom();
+//            byte focus = command.getFocus();
+//            byte panSped = (byte)Math.min(command.getPanSped(), speed);
+//            byte tiltSped = (byte)Math.min(command.getTiltSped(), speed);
+//            byte zoomSped = (byte)Math.min(command.getZoomSped(), speed);
+//            byte focusSped = (byte)Math.min(command.getFocusSped(), speed);
+//            CctvReqPtzCtrl pkt = new CctvReqPtzCtrl(obj.getAddress());
+//            byte ptz = pkt.setPtzValue((byte) pan, (byte) tilt, (byte) zoom, (byte) focus, (byte) panSped, (byte) tiltSped, (byte) zoomSped, (byte) focusSped);
+//            //log.info("[{}]. PTZ Control: " + ByteUtils.byteToBitString(ptz));
+//            ByteBuffer sendBuffer = pkt.getByteBuffer();
+//            if (obj.sendData(sendBuffer, 0, "cctv_PtzStart")) {
+//                log.info("[{}]. START send success. {}", obj.getCCTV_CTLR_ID(), cmdDesc);
+//            } else {
+//                result.setResult(4, "CCTV PTZ 시작 명령 전송이 실패하였습니다.");
+//                log.error("[{}]. START send failed. {}", obj.getCCTV_CTLR_ID(), cmdDesc);
+//            }
+//        } else {
+//            // stop
+//            CctvReqPtzCtrl pkt = new CctvReqPtzCtrl(obj.getAddress());
+//            ByteBuffer sendBuffer = pkt.getByteBuffer();
+//            if (obj.sendData(sendBuffer, 0, "cctv_PtzStop")) {
+//                log.info("[{}]. STOP send success. {}", obj.getCCTV_CTLR_ID(), cmdDesc);
+//            } else {
+//                result.setResult(4, "CCTV PTZ 정지 명령 전송이 실패하였습니다.");
+//                log.error("[{}]. STOP send failed. {}", obj.getCCTV_CTLR_ID(), cmdDesc);
+//            }
+//        }
+        return result;
+    }
+
+    /**
+     * VMS Preset 컨트롤
+     * @param id
+     * @param req
+     * @return
+     */
+    public CctvPresetControlDto.CctvPresetControlRes controlPreset(Long id, CctvPresetControlDto.CctvPresetControlReq req) {
+        log.info("controlPreset: {}, {}", id, req);
+        TbVmsCtlrDto cctv = this.requireOne(id);
+        if (cctv == null) {
+            return new CctvPresetControlDto.CctvPresetControlRes(1, "VMS 제어기 정보를 찾을 수 없습니다.");
+        }
+        if (cctv.getNetState() != NET.LOGINED) {
+            return new CctvPresetControlDto.CctvPresetControlRes(2, "VMS 제어기와 통신연결이 되어 있지 않습니다.");
+        }
+
+        CctvPresetControlDto.CctvPresetControlRes result = new CctvPresetControlDto.CctvPresetControlRes(0, "success");
+//        CctvReqPresetMove pkt = new CctvReqPresetMove(cctv.getAddress());
+//        pkt.setValue(req.getPan().shortValue(), req.getTilt().shortValue(), req.getZoom().shortValue(), req.getFocus().shortValue());
+//        ByteBuffer sendBuffer = pkt.getByteBuffer();
+//        if (cctv.sendData(sendBuffer, 0, "cctv_PresetMove")) {
+//            log.info("[{}]. PRESET send success.", cctv.getCCTV_CTLR_ID());
+//        } else {
+//            result.setResult(4, "CCTV PRESET 명령 전송이 실패하였습니다.");
+//            log.error("[{}]. PRESET send failed.", cctv.getCCTV_CTLR_ID());
+//        }
+        return result;
+    }
+
+    /**
+     * VMS 파라미터정보 설정
+     * @param id
+     * @param req
+     * @return
+     */
+    public CctvParamControlDto.CctvParamControlRes controlParam(Long id, CctvParamControlDto.CctvParamControlReq req) {
+        log.info("controlParam: {}, {}", id, req);
+        TbVmsCtlrDto cctv = this.requireOne(id);
+        if (cctv == null) {
+            return new CctvParamControlDto.CctvParamControlRes(1, "VMS 제어기 정보를 찾을 수 없습니다.");
+        }
+        if (cctv.getNetState() != NET.LOGINED) {
+            return new CctvParamControlDto.CctvParamControlRes(2, "VMS 제어기와 통신연결이 되어 있지 않습니다.");
+        }
+
+        CctvParamControlDto.CctvParamControlRes result = new CctvParamControlDto.CctvParamControlRes(0, "success");
+//        CctvReqParamSet pkt = new CctvReqParamSet(cctv.getAddress());
+//        pkt.setValue(req.getFanOnTmpr().byteValue(), req.getFanOffTmpr().byteValue(), req.getHetrOnTmpr().byteValue(), req.getHetrOffTmpr().byteValue());
+//        ByteBuffer sendBuffer = pkt.getByteBuffer();
+//        if (cctv.sendData(sendBuffer, 0, "cctv_ParamSet")) {
+//            log.info("[{}]. PARAMETER SET send success.", cctv.getCCTV_CTLR_ID());
+//        } else {
+//            result.setResult(4, "CCTV PARAMETER 명령 전송이 실패하였습니다.");
+//            log.error("[{}]. PARAMETER SET send failed.", cctv.getCCTV_CTLR_ID());
+//        }
+        return result;
+    }
+
+    /**
+     * VMS 파라미터 정보 조회
+     * @param id
+     * @return
+     */
+    public CctvParamControlDto.CctvParamValueRes requestParam(Long id) {
+        log.info("requestParam: {}", id);
+        TbVmsCtlrDto cctv = this.requireOne(id);
+        if (cctv == null) {
+            return new CctvParamControlDto.CctvParamValueRes(1, "VMS 제어기 정보를 찾을 수 없습니다.");
+        }
+        if (cctv.getNetState() != NET.LOGINED) {
+            return new CctvParamControlDto.CctvParamValueRes(2, "VMS 제어기와 통신연결이 되어 있지 않습니다.");
+        }
+
+        CctvParamControlDto.CctvParamValueRes result = new CctvParamControlDto.CctvParamValueRes(0, "success");
+
+//        synchronized (cctv.getParamVal()) {
+//            try {
+//                cctv.getParamVal().setRequest(true);
+//                cctv.getParamVal().setCompleted(false);
+//                ByteBuffer sendBuffer = cctv.getReqParam().getByteBuffer();
+//                if (cctv.sendData(sendBuffer, 0, "cctv_ParamReq")) {
+//                    log.info("[{}]. PARAMETER QRY send success.", cctv.getCCTV_CTLR_ID());
+//                    cctv.getParamVal().wait(5000);
+//                    if (cctv.getParamVal().isCompleted()) {
+//                        result.setValue(cctv.getParamVal().getFanOnTmpr(), cctv.getParamVal().getFanOffTmpr(), cctv.getParamVal().getHetrOnTmpr(), cctv.getParamVal().getHetrOffTmpr());
+//                    } else {
+//                        result.setResult(5, "CCTV PARAMETER 값조회 명령 중 타임아웃이 발생하였습니다.");
+//                    }
+//                } else {
+//                    result.setResult(4, "CCTV PARAMETER 값조회 명령 전송이 실패하였습니다.");
+//                    log.error("[{}]. PARAMETER QRY send failed.", cctv.getCCTV_CTLR_ID());
+//                }
+//            } catch (InterruptedException e) {
+//                result.setResult(9, "CCTV PARAMETER 값조회 명령 전송 중 오류가 발생하였습니다.");
+//                log.error("[{}]. PARAMETER QRY send failed. InterruptedException.", cctv.getCCTV_CTLR_ID());
+//            }
+//            cctv.getParamVal().setRequest(false);
+//        }
+        return result;
+    }
+
+    /**
+     * VMS 프리셋 현재위치정보 요청
+     * @param id
+     * @return
+     */
+    public CctvPresetControlDto.CctvPresetValueRes requestPresetValue(Long id) {
+        log.info("requestPresetValue: {}", id);
+        TbVmsCtlrDto cctv = this.requireOne(id);
+        if (cctv == null) {
+            return new CctvPresetControlDto.CctvPresetValueRes(1, "VMS 제어기 정보를 찾을 수 없습니다.");
+        }
+        if (cctv.getNetState() != NET.LOGINED) {
+            return new CctvPresetControlDto.CctvPresetValueRes(2, "VMS 제어기와 통신연결이 되어 있지 않습니다.");
+        }
+
+        CctvPresetControlDto.CctvPresetValueRes result = new CctvPresetControlDto.CctvPresetValueRes(0, "success");
+//        synchronized (cctv.getPsetVal()) {
+//            try {
+//                cctv.getPsetVal().setCompleted(false);
+//                cctv.getPsetVal().setRequest(true);
+////                CctvReqPresetQry pkt = new CctvReqPresetQry(cctv.getAddress());
+////                pkt.setValue((byte)0x00);
+////                ByteBuffer sendBuffer = pkt.getByteBuffer();
+////                if (cctv.sendData(sendBuffer, 0, "cctv_PresetQry")) {
+//                ByteBuffer sendBuffer = cctv.getReqState().getByteBuffer();
+//                if (cctv.sendData(sendBuffer, 0, "cctv_PresetQry")) {
+//                    log.info("[{}]. PRESET QRY send success.", cctv.getCCTV_CTLR_ID());
+//                    cctv.getPsetVal().wait(5000);
+//                    if (cctv.getPsetVal().isCompleted()) {
+//                        result.setValue(cctv.getPsetVal().getPan(), cctv.getPsetVal().getTilt(), cctv.getPsetVal().getZoom(), cctv.getPsetVal().getFocus());
+//                    } else {
+//                        result.setResult(5, "CCTV PRESET 값조회 명령 중 타임아웃이 발생하였습니다.");
+//                    }
+//                } else {
+//                    result.setResult(4, "CCTV PRESET 값조회 명령 전송이 실패하였습니다.");
+//                    log.error("[{}]. PRESET QRY send failed.", cctv.getCCTV_CTLR_ID());
+//                }
+//            } catch (InterruptedException e) {
+//                result.setResult(6, "CCTV PRESET 값조회 명령 전송 중 오류가 발생하였습니다.");
+//                log.error("[{}]. PRESET QRY send failed. InterruptedException.", cctv.getCCTV_CTLR_ID());
+//            }
+//            cctv.getPsetVal().setRequest(false);
+//        }
+        return result;
+    }
+
+    /**
+     * Object Notify TEST
+     * @param id
+     * @return
+     */
+    public CctvStatusDto.CctvStatusNotifyDtoRes notifyStatus(Long id) {
+        log.info("notifyStatus: {}", id);
+        TbVmsCtlrDto cctv = this.requireOne(id);
+        if (cctv == null) {
+            return new CctvStatusDto.CctvStatusNotifyDtoRes(1, "VMS 제어기 정보를 찾을 수 없습니다.");
+        }
+        if (cctv.getNetState() != NET.LOGINED) {
+            return new CctvStatusDto.CctvStatusNotifyDtoRes(2, "VMS 제어기와 통신연결이 되어 있지 않습니다.");
+        }
+
+        CctvStatusDto.CctvStatusNotifyDtoRes result = new CctvStatusDto.CctvStatusNotifyDtoRes(0, "success");
+//        synchronized (cctv.getPsetVal()) {
+//            try {
+//                cctv.getPsetVal().notifyAll();
+//            }
+//            catch(Exception e) {
+//                log.error("{}, {}", cctv.getPsetVal(), e.getMessage());
+//            }
+//        }
+        return result;
+    }
+
+    /**
+     * VMS 가변문자 설정
+     * @param id
+     * @param req
+     * @return
+     */
+    public CctvVarCharControlDto.CctvVarCharControlRes controlVarCharSet(Long id, CctvVarCharControlDto.CctvVarCharControlSetReq req) {
+        log.info("controlVarCharSet: {}, {}", id, req);
+        TbVmsCtlrDto cctv = this.requireOne(id);
+        if (cctv == null) {
+            return new CctvVarCharControlDto.CctvVarCharControlRes(1, "VMS 제어기 정보를 찾을 수 없습니다.");
+        }
+        if (cctv.getNetState() != NET.LOGINED) {
+            return new CctvVarCharControlDto.CctvVarCharControlRes(2, "VMS 제어기와 통신연결이 되어 있지 않습니다.");
+        }
+
+        CctvVarCharControlDto.CctvVarCharControlRes result = new CctvVarCharControlDto.CctvVarCharControlRes(0, "success");
+//        CctvReqCharVar pkt = new CctvReqCharVar(cctv.getAddress());
+//        if (!pkt.setValue(
+//                req.getSectorNo().byteValue(),
+//                req.getStartPan(), req.getEndPan(),
+//                req.getCh1CharSize().byteValue(), req.getCh1PosX(), req.getCh2PosY(),
+//                req.getCh2CharSize().byteValue(), req.getCh2PosX(), req.getCh2PosY(),
+//                req.getCh1Char(), req.getCh2Char()
+//                )) {
+//            return new CctvVarCharControlDto.CctvVarCharControlRes(3, "CCTV 가변문자 설정값에 오류가 있습니다.");
+//        }
+//
+//        ByteBuffer sendBuffer = pkt.getByteBuffer();
+//        if (cctv.sendData(sendBuffer, 0, "cctv_CharVarSet")) {
+//            log.info("[{}]. CHAR-VAR-SET send success.", cctv.getCCTV_CTLR_ID());
+//        } else {
+//            result.setResult(4, "CCTV CHAR-VAR-SET 명령 전송이 실패하였습니다.");
+//            log.error("[{}]. CHAR-VAR-SET send failed.", cctv.getCCTV_CTLR_ID());
+//        }
+        return result;
+    }
+
+    /**
+     * VMS 가변문자 삭제
+     * @param id
+     * @param req
+     * @return
+     */
+    public CctvVarCharControlDto.CctvVarCharControlRes controlVarCharDel(Long id, CctvVarCharControlDto.CctvVarCharControlDelReq req) {
+        log.info("controlVarCharDel: {}, {}", id, req);
+        TbVmsCtlrDto cctv = this.requireOne(id);
+        if (cctv == null) {
+            return new CctvVarCharControlDto.CctvVarCharControlRes(1, "VMS 제어기 정보를 찾을 수 없습니다.");
+        }
+        if (cctv.getNetState() != NET.LOGINED) {
+            return new CctvVarCharControlDto.CctvVarCharControlRes(2, "VMS 제어기와 통신연결이 되어 있지 않습니다.");
+        }
+
+        CctvVarCharControlDto.CctvVarCharControlRes result = new CctvVarCharControlDto.CctvVarCharControlRes(0, "success");
+//        CctvReqCharDel pkt = new CctvReqCharDel(cctv.getAddress());
+//        pkt.setValue(req.getSectorNo().byteValue());
+//
+//        ByteBuffer sendBuffer = pkt.getByteBuffer();
+//        if (cctv.sendData(sendBuffer, 0, "cctv_CharVarDel")) {
+//            log.info("[{}]. CHAR-VAR-DEL send success.", cctv.getCCTV_CTLR_ID());
+//        } else {
+//            result.setResult(4, "CCTV CHAR-VAR-DEL 명령 전송이 실패하였습니다.");
+//            log.error("[{}]. CHAR-VAR-DEL send failed.", cctv.getCCTV_CTLR_ID());
+//        }
+        return result;
+    }
+
+    /**
+     * VMS 제어기 리셋
+     * @param id
+     * @param req
+     * @return
+     */
+    public CctvControlDto.CctvControlResetRes controlReset(Long id, CctvControlDto.CctvControlResetReq req) {
+        log.info("controlReset: {}, {}", id, req);
+        TbVmsCtlrDto cctv = this.requireOne(id);
+        if (cctv == null) {
+            return new CctvControlDto.CctvControlResetRes(1, "VMS 제어기 정보를 찾을 수 없습니다.");
+        }
+        if (cctv.getNetState() != NET.LOGINED) {
+            return new CctvControlDto.CctvControlResetRes(2, "VMS 제어기와 통신연결이 되어 있지 않습니다.");
+        }
+
+        CctvControlDto.CctvControlResetRes result = new CctvControlDto.CctvControlResetRes(0, "success");
+//        if (0 == req.getResetType()) {
+//            // SW Reset
+//            if (!cctv.resetSw()) {
+//                result.setResult(4, "CCTV 제어기 SW 리셋 명령 전송에 실패하였습니다.");
+//            } else {
+//                log.info("[{}]. SW Reset send success.", cctv.getCCTV_CTLR_ID());
+//            }
+//        }
+//        else if (1 == req.getResetType()) {
+//            // HW Reset
+//            if (!cctv.resetHw()) {
+//                result.setResult(5, "CCTV 제어기 HW 리셋 명령 전송에 실패하였습니다.");
+//            } else {
+//                log.info("[{}]. HW Reset send success.", cctv.getCCTV_CTLR_ID());
+//            }
+//        }
+//        else {
+//            result.setResult(3, "CCTV 리셋 정보가 잘못 되었습니다.");
+//        }
+        return result;
+    }
+
+}

+ 32 - 0
src/main/java/com/its/vms/api/websocket/ItsWebSocketConfig.java

@@ -0,0 +1,32 @@
+package com.its.vms.api.websocket;
+
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
+import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
+
+@Slf4j
+@RequiredArgsConstructor
+//@Configuration
+//@EnableWebSocket
+public class ItsWebSocketConfig implements WebSocketConfigurer {
+
+    private final ItsWebSocketHandler itsWebsocketHandler;
+
+    @Override
+    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
+
+        registry.addHandler(this.itsWebsocketHandler, "/ws/messages.do")
+                //.setAllowedOrigins("*")
+                .setAllowedOriginPatterns("*")
+                .withSockJS()
+                //.setSessionCookieNeeded(false)
+        ;  // sockjs
+
+        registry.addHandler(this.itsWebsocketHandler, "/ws/messages.do")
+                .setAllowedOriginPatterns("*")
+                //.setAllowedOrigins("*")
+        ; // 그냥 websocket 지원
+    }
+
+}

+ 67 - 0
src/main/java/com/its/vms/api/websocket/ItsWebSocketHandler.java

@@ -0,0 +1,67 @@
+package com.its.vms.api.websocket;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.socket.CloseStatus;
+import org.springframework.web.socket.TextMessage;
+import org.springframework.web.socket.WebSocketSession;
+import org.springframework.web.socket.handler.TextWebSocketHandler;
+
+import java.util.Map;
+
+@Slf4j
+//@Controller
+@RequestMapping("/ws/messages.do")
+public class ItsWebSocketHandler extends TextWebSocketHandler {
+
+    public ItsWebSocketHandler() {
+
+        Runtime.getRuntime().addShutdownHook(new Thread(() -> {
+            for (Map.Entry<org.springframework.web.socket.WebSocketSession, ItsWebSocketSession> obj : ItsWebSocketSessionManager.getInstance().getMap().entrySet()) {
+                if (obj.getValue() != null) {
+                }
+            }
+        }));
+
+        log.info("WebsocketHandler() START");
+    }
+
+    @Override
+    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
+        // 클라이언트가 연결되었을때 실행
+        log.info("afterConnectionEstablished: " + session.getRemoteAddress() + ",  URI: " + session.getUri() + ", UUID: " + session.getId());
+        super.afterConnectionEstablished(session);
+
+        ItsWebSocketSession vo = new ItsWebSocketSession(this, session);
+        ItsWebSocketSessionManager.getInstance().addSession(session, vo);
+    }
+
+    //클라이언트가 웹소켓 서버로 메시지를 전송했을 때 실행
+    @Override
+    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
+        log.info("handleTextMessage: " + session.getRemoteAddress() + ",  Uri[" + session.getUri() + "], UUID[" + session.getId() + "], message[" + message.getPayload() + "]");
+        /*List<String> requests = ItsUtils.split(message.getPayload(), ":");
+        if (requests.size() != 2) {
+            log.error("Request data parsing error: {}", requests);
+            session.close();
+            return;
+        }
+*/
+        ItsWebSocketSession sessionClient = ItsWebSocketSessionManager.getInstance().getSession(session);
+        if (sessionClient == null) {
+            log.error("Request session not found: {}", session);
+            session.close();
+            return;
+        }
+        log.info("Payload: {}", message.getPayload());
+    }
+
+    //클라이언트 연결을 끊었을 때 실행
+    @Override
+    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
+        log.info("afterConnectionClosed: " + session.getRemoteAddress() + ",  URI: " + session.getUri() + ", UUID: " + session.getId());
+        ItsWebSocketSessionManager.getInstance().removeSession(session);
+        super.afterConnectionClosed(session, status);
+    }
+
+}

+ 18 - 0
src/main/java/com/its/vms/api/websocket/ItsWebSocketMessage.java

@@ -0,0 +1,18 @@
+package com.its.vms.api.websocket;
+
+import lombok.Data;
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+@Data
+public class ItsWebSocketMessage {
+
+    private String command;
+    private Object data;
+
+    public ItsWebSocketMessage(String command, Object data) {
+        this.command = command;
+        this.data = data;
+    }
+
+}

+ 35 - 0
src/main/java/com/its/vms/api/websocket/ItsWebSocketSession.java

@@ -0,0 +1,35 @@
+package com.its.vms.api.websocket;
+
+import lombok.Data;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.socket.TextMessage;
+import org.springframework.web.socket.WebSocketSession;
+
+import java.io.IOException;
+
+@Slf4j
+@Data
+public class ItsWebSocketSession {
+
+    private ItsWebSocketHandler itsWebsocketHandler;
+    private WebSocketSession session;
+    private String groupId;
+
+    public ItsWebSocketSession(ItsWebSocketHandler itsWebsocketHandler, org.springframework.web.socket.WebSocketSession session) {
+        this.itsWebsocketHandler = itsWebsocketHandler;
+        this.session = session;
+    }
+
+    public void sendMessage(String command, TextMessage message) {
+
+        if (this.session != null && this.session.isOpen()) {
+            synchronized (this.session) {
+                try {
+                    this.session.sendMessage(message);
+                } catch (IOException e) {
+                    log.error("sendMessage: nodeId: {}, session: {}", command, session);
+                }
+            }
+        }
+    }
+}

+ 68 - 0
src/main/java/com/its/vms/api/websocket/ItsWebSocketSessionManager.java

@@ -0,0 +1,68 @@
+package com.its.vms.api.websocket;
+
+import com.its.app.utils.Counter;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.socket.TextMessage;
+import org.springframework.web.socket.WebSocketSession;
+
+import java.util.concurrent.ConcurrentHashMap;
+
+@Slf4j
+public class ItsWebSocketSessionManager {
+
+    private static ItsWebSocketSessionManager _instance = null;
+
+    private volatile boolean serverRun;
+    private Counter sessions;
+    private ConcurrentHashMap<WebSocketSession, ItsWebSocketSession> sessionMap;
+
+    public static ItsWebSocketSessionManager getInstance() {
+        if (_instance == null) {
+            synchronized (ItsWebSocketSessionManager.class) {
+                if (_instance == null)
+                    _instance = new ItsWebSocketSessionManager();
+            }
+        }
+        return _instance;
+    }
+
+    private ItsWebSocketSessionManager() {
+        this.sessions = new Counter();
+        this.sessionMap = new ConcurrentHashMap<>();
+    }
+
+    public ConcurrentHashMap<WebSocketSession, ItsWebSocketSession> getMap() {
+        return this.sessionMap;
+    }
+
+    public int add() {
+        return (int) this.sessions.increment();
+    }
+    public int remove() {
+        return (int) this.sessions.decrement();
+    }
+    public int get() {
+        return (int) this.sessions.get();
+    }
+
+    public void addSession(WebSocketSession session, ItsWebSocketSession vo) {
+        this.sessionMap.put(session, vo);
+    }
+    public void removeSession(WebSocketSession session) {
+        this.sessionMap.remove(session);
+    }
+    public ItsWebSocketSession getSession(WebSocketSession session) {
+        return this.sessionMap.get(session);
+    }
+
+    public void reportSession() {
+        log.info("Sessions: {}", this.sessionMap.toString());
+    }
+
+    public void sendBroadcastMessage(String command, TextMessage message) {
+        this.sessionMap.forEach((key, value) -> {
+            log.info("{}, {}, {}", command, message, key);
+            value.sendMessage(command, message);
+        });
+    }
+}

+ 4 - 4
src/main/java/com/its/vms/config/SwaggerConfig.java

@@ -35,10 +35,10 @@ public class SwaggerConfig {
         globalParameters.add(parameterBuilder);
         return new Docket(DocumentationType.SWAGGER_2)
                 .globalOperationParameters(globalParameters)
-                .groupName("rse-api")
+                .groupName("vms-api")
                 .apiInfo(this.apiInfo())
                 .select()
-                .apis(RequestHandlerSelectors.basePackage("com.its.dsrc.api.controller"))
+                .apis(RequestHandlerSelectors.basePackage("com.its.vms.api.controller"))
                 .paths(PathSelectors.any())
                 //.paths(PathSelectors.ant("/api/**"))
                 .build();
@@ -46,8 +46,8 @@ public class SwaggerConfig {
 
     private ApiInfo apiInfo() {
         return new ApiInfoBuilder()
-                .title("RSE(DSRC) Control API")
-                .description("RSE(DSRC) Communication Server API")
+                .title("VMS Control API")
+                .description("VMS Communication Server API")
                 .version("0.0.1")
                 .build();
     }

+ 12 - 8
src/main/java/com/its/vms/domain/VmsFormObject.java

@@ -86,6 +86,13 @@ public class VmsFormObject implements Serializable {
             // 표출데이터가 없는 경우
             return;
         }
+// 문자열정열방식
+//        typedef enum en_text_align
+//        {
+//            text_align_left,    /* 0:LEFT */
+//            text_align_right,   /* 1:RIGHT */
+//            text_align_center,  /* 2:CENTER */
+//        } EN_TEXT_ALIGN;
 
         int style = this.fontBold == 0 ? Font.PLAIN : Font.BOLD;
         Font font = new Font(this.fontName, style, this.fontSize);
@@ -101,16 +108,13 @@ public class VmsFormObject implements Serializable {
         int correctWidth = this.dsplWidth - textWidth;
         if (this.textAlign == 1) {
             // 우측정렬: 좌측에서 글자길이를 뺀다음 만약 음수이면 0으로 설정
-            //this.posX = (this.posX + this.dsplWidth) - textWidth;
-            this.posX += correctWidth;
+            this.posX = (this.posX + this.dsplWidth) - textWidth;
+            //this.posX += correctWidth;
         }
-        else {
+        else if (this.textAlign == 2) {
             // 가운데정렬: 원래 글자의 중앙값을 얻은 다음에 조정된 글자의 1/2 크기를 뺀다음 음수이면 0으로 설정
-            //this.posX = (this.posX+(this.dsplWidth /2)) - (textWidth/2);
-            this.posX += (correctWidth / 2);
-        }
-        if ("좋음".equals(this.textData)) {
-            log.error("XXXXXX: {}, {}, {}", this.textAlign, this.posX, this.posY);
+            this.posX = (this.posX+(this.dsplWidth /2)) - (textWidth/2);
+            //this.posX += (correctWidth / 2);
         }
 
         // 좌측정령은 정렬할 필요가 없음, 단 가변문자인 경우 문자길이를 다시 계산해주어야 함....

+ 0 - 37
src/main/java/com/its/vms/global/AppRepository.java

@@ -1,37 +0,0 @@
-package com.its.vms.global;
-
-import com.its.vms.dto.TbVmsCtlrDto;
-import io.netty.channel.Channel;
-import lombok.Getter;
-import lombok.Setter;
-import lombok.extern.slf4j.Slf4j;
-
-import java.util.concurrent.ConcurrentHashMap;
-
-@Getter
-@Setter
-@Slf4j
-public class AppRepository {
-    private static AppRepository _instance = null;
-
-    public ConcurrentHashMap<Long, TbVmsCtlrDto> ctlrMap = null;
-    public ConcurrentHashMap<String, TbVmsCtlrDto> ctlrIpMap = null;
-    public ConcurrentHashMap<Channel, TbVmsCtlrDto> ctlrChannelMap = null;
-
-//    public static AppRepository getInstance() {
-//        if (_instance == null) {
-//            synchronized (AppRepository.class) {
-//                if (_instance == null)
-//                    _instance = new AppRepository();
-//            }
-//        }
-//        return _instance;
-//    }
-
-    public AppRepository() {
-        this.ctlrMap = new ConcurrentHashMap<>();
-        this.ctlrIpMap = new ConcurrentHashMap<>();
-        this.ctlrChannelMap = new ConcurrentHashMap<>();
-    }
-
-}

+ 20 - 8
src/main/java/com/its/vms/service/VmsManageService.java

@@ -1256,32 +1256,39 @@ public class VmsManageService {
                                     isUnit = true;
                                 }
 
+                                boolean isSetFontColor = true;
+                                int fontGradeColorCd = 0;
                                 if (formObj.getObjectType() == 401) {
+                                    isSetFontColor = false;
                                     formObj.setTextData(atmp.getVmsDispNm());
                                 } else if (formObj.getObjectType() == 402) {
                                     formObj.setTextData(atmp.getPm10ValDesc(unitGap, isUnit));
-                                    formObj.setFontColor(atmp.getPm10Color());
+                                    fontGradeColorCd = atmp.getPm10Color();
                                 } else if (formObj.getObjectType() == 403) {
                                     formObj.setTextData(atmp.getPm25ValDesc(unitGap, isUnit));
-                                    formObj.setFontColor(atmp.getPm25Color());
+                                    fontGradeColorCd = atmp.getPm25Color();
                                 } else if (formObj.getObjectType() == 404) {
                                     formObj.setTextData(atmp.getPm10GradDesc());
-                                    formObj.setFontColor(atmp.getPm10Color());
+                                    fontGradeColorCd = atmp.getPm10Color();
                                 } else if (formObj.getObjectType() == 405) {
                                     formObj.setTextData(atmp.getPm25GradDesc());
-                                    formObj.setFontColor(atmp.getPm25Color());
+                                    fontGradeColorCd = atmp.getPm25Color();
                                 } else if (formObj.getObjectType() == 409) {
                                     formObj.setTextData(atmp.getAtmpGradDesc());
-                                    formObj.setFontColor(atmp.getAtmpColor());
+                                    fontGradeColorCd = atmp.getAtmpColor();
                                 } else if (formObj.getObjectType() == 410) {
                                     formObj.setTextData(atmp.getAtmpValDesc());
-                                    formObj.setFontColor(atmp.getAtmpColor());
+                                    fontGradeColorCd = atmp.getAtmpColor();
                                 } else if (formObj.getObjectType() == 411) {
                                     formObj.setTextData(atmp.getO3ValDesc(unitGap, isUnit));
-                                    formObj.setFontColor(atmp.getO3Color());
+                                    fontGradeColorCd = atmp.getO3Color();
                                 } else if (formObj.getObjectType() == 412) {
                                     formObj.setTextData(atmp.getO3GradDesc());
-                                    formObj.setFontColor(atmp.getO3Color());
+                                    fontGradeColorCd = atmp.getO3Color();
+                                }
+                                if (isSetFontColor && formObj.getFontColor() != 10) {
+                                    // 엘이디 고장난게 있어서 흰색으로 표출해야 해서 폰트색상이 흰색인 경우 그냥 흰색으로 표출하는 것으로.....
+                                    formObj.setFontColor(fontGradeColorCd);
                                 }
                             }
                             if ("".equals(formObj.getTextData())) {
@@ -1370,6 +1377,11 @@ public class VmsManageService {
                                     break;
                                 case 14: case 24: case 34: case 44: case 92: case 95: case 164: //소통상황
                                     formObj.setTextData(trafGradeDesc(form.getVmsFormTypeCd(), ifscTraf.getCmtrGradCd()));
+                                    // 도형식 하단쪽에 엘이디 고장난게 있어서 흰색으로 표출해야 해서
+                                    // 폰트색상이 흰색인 경우 그냥 흰색으로 표출하는 것으로.....
+                                    if (formObj.getFontColor() != 10) {
+                                        formObj.setFontColor(gradeToColorCode(ifscTraf.getCmtrGradCd()));
+                                    }
                                     break;
                                 case 19:    //소통상황(가변)
                                     String gradText = trafGradeDesc(form.getVmsFormTypeCd(), ifscTraf.getCmtrGradCd());

+ 27 - 38
src/main/java/com/its/vms/webapp/config/WebSecurityConfig.java

@@ -5,7 +5,6 @@ import com.its.vms.webapp.service.UserService;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.http.HttpMethod;
-import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
 import org.springframework.security.config.annotation.web.builders.HttpSecurity;
 import org.springframework.security.config.annotation.web.builders.WebSecurity;
 import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
@@ -13,7 +12,9 @@ import org.springframework.security.config.annotation.web.configuration.WebSecur
 import org.springframework.security.config.http.SessionCreationPolicy;
 import org.springframework.security.core.session.SessionRegistry;
 import org.springframework.security.core.session.SessionRegistryImpl;
-import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
+import org.springframework.web.cors.CorsConfiguration;
+import org.springframework.web.cors.CorsConfigurationSource;
+import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
 
 import javax.servlet.http.HttpSessionListener;
 
@@ -29,7 +30,7 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
     @Override
     public void configure(WebSecurity web) throws Exception {
         web.ignoring().antMatchers("/favicon.ico");
-        web.ignoring().antMatchers(new String[]{"/css/**", "/js/**", "/img/**", "/lib/**"});
+        web.ignoring().antMatchers("/css/**", "/js/**", "/img/**", "/lib/**");
 
         web.ignoring().antMatchers(HttpMethod.GET, "/api/**");  // GET Method 는 모두 통과
         web.ignoring().antMatchers(HttpMethod.POST, "/api/**");  // GET Method 는 모두 통과
@@ -40,49 +41,37 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
     @Override
     protected void configure(HttpSecurity http) throws Exception {
 
-        http.authorizeRequests()
+        http
+                .httpBasic().disable()
+                .cors().configurationSource(corsConfigurationSource())
+                .and()
+                .csrf().disable()
+                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
+                .and()
+                .authorizeRequests()
                 // SWAGGER 권한 설정
                 .antMatchers("/swagger-ui.html", "/swagger/**", "/swagger-resources/**", "/webjars/**", "/v2/api-docs").permitAll()
                 // 웹소켓 권한 설정하지
                 .antMatchers("/ws/**").permitAll()
                 .antMatchers("/api/**").permitAll()
-                .antMatchers(new String[]{"/index"})
-                .hasRole("ADMIN")
-                .antMatchers(new String[]{"/**"})
-                .hasRole("ADMIN")
-                .and()
-                .formLogin()
-                .loginPage("/login")
-                .defaultSuccessUrl("/index")
-                .failureUrl("/denied")
-                .permitAll()
-                .and()
-                .logout()
-                .logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
-                .logoutSuccessUrl("/login").invalidateHttpSession(true)
-                .deleteCookies()
-                .and()
-                .exceptionHandling()
-                .accessDeniedPage("/denied");
+                .anyRequest().permitAll()
+        ;
+    }
 
-        http
-                .csrf()
-                .disable();
+    // CORS 허용 적용
+    @Bean
+    public CorsConfigurationSource corsConfigurationSource() {
+        CorsConfiguration corsConfig = new CorsConfiguration();
 
-        http
-                .sessionManagement()
-                .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
-                .invalidSessionUrl("/login")
-                .sessionFixation()
-                .migrateSession()
-                .maximumSessions(5)
-                .maxSessionsPreventsLogin(true)
-                .expiredUrl("/login")
-                .sessionRegistry(this.sessionRegistry());
-    }
+        corsConfig.setAllowCredentials(true);       // cross origin 으로부터 인증을 위한 쿠키 정보를 받을지 여부
+        corsConfig.addAllowedOriginPattern("*");    // addAllowedOrigin("*") 대신 사용, 허용할 origin 정보, Arrays.asList("http://localhost:8080")
+        corsConfig.addAllowedHeader("*");
+        corsConfig.addAllowedMethod("*");           // 허용할 http methods. Arrays.asList("GET", "POST")
+
+        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
+        source.registerCorsConfiguration("/**", corsConfig);
 
-    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
-        auth.userDetailsService(this.userService);
+        return source;
     }
 
     @Bean

+ 9 - 54
src/main/java/com/its/vms/webapp/controller/WebAppController.java

@@ -36,19 +36,17 @@ public class WebAppController {
     @RequestMapping(value = {"/controller"})
     public String controller(Model model, HttpServletRequest request) {
         String result = "controller";
-        SortedMap<Long, TbVmsCtlrDto> ctlrMap = new TreeMap<>();
-        //for (Map.Entry<Long, TbVmsCtlr> e : AppRepository.getInstance().getCtlrMap().entrySet()) {
+        SortedMap<Long, TbVmsCtlrDto> ctlrMap = new TreeMap<>();        //for (Map.Entry<Long, TbVmsCtlr> e : AppRepository.getInstance().getCtlrMap().entrySet()) {
         for (Map.Entry<Long, TbVmsCtlrDto> e : this.repoService.getCtlrMap().entrySet()) {
             TbVmsCtlrDto obj = e.getValue();
             if (obj.getNetState() == NET.CLOSED) {
-//                if (obj.getChannel() != null || obj.getLogin() != null) {
-//                    obj.setNetState(NET.LOGINED);
-//                }
+                if (obj.getChannel() != null) {
+                    obj.setNetState(NET.LOGINED);
+                }
             }
             ctlrMap.put(obj.getVmsCtlrNmbr(), obj);
         }
         model.addAttribute("ServerConfig", this.applicationConfig);
-        //model.addAttribute("list", AppRepository.getInstance().getCtlrMap().entrySet());
         model.addAttribute("list", ctlrMap);
         model.addAttribute("ServerTime", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
         return result;
@@ -58,7 +56,6 @@ public class WebAppController {
     @ResponseBody
     public ConcurrentHashMap<Long, TbVmsCtlrDto> getControllerInfo() {
         ConcurrentHashMap<Long, TbVmsCtlrDto> ctlrMap = new ConcurrentHashMap<>();
-        //for (Map.Entry<Long, TbVmsCtlr> e : AppRepository.getInstance().getCtlrMap().entrySet()) {
         for (Map.Entry<Long, TbVmsCtlrDto> e : this.repoService.getCtlrMap().entrySet()) {
             TbVmsCtlrDto obj = e.getValue();
             ctlrMap.put(obj.getVmsCtlrNmbr(), obj);
@@ -70,20 +67,9 @@ public class WebAppController {
     @ResponseBody
     public String disconnectController(@RequestParam("id") Long id) {
         String result = "제어기와의 통신 연결을 종료 하였습니다.";
-        //TbVmsCtlr ctlr = AppRepository.getInstance().getCtlrMap().get(id);
         TbVmsCtlrDto ctlr = this.repoService.getCtrlMap(id);
         if (ctlr != null && ctlr.getNetState() > NET.CLOSED && ctlr.getChannel() != null) {
-            if (ctlr.getNetState() == NET.LOGIN_REQ) {
-                VmsServerIdleStateHandler.disconnectChannel(ctlr, ctlr.getChannel());
-            }
-            else {
-                VmsServerIdleStateHandler.disconnectChannel(ctlr, ctlr.getChannel());
-            }
-            try {
-                Thread.sleep(1000);
-            } catch (InterruptedException e) {
-                e.printStackTrace();
-            }
+            VmsServerIdleStateHandler.disconnectChannel(ctlr, ctlr.getChannel());
         }
         else {
             result = "제어기는 현재 연결되어 있지 않습니다.";
@@ -95,24 +81,12 @@ public class WebAppController {
     @ResponseBody
     public String allDisconnectController() {
         String result = "통신 연결 중인 제어기와의 통신 연결을 종료 하였습니다.";
-        //for (Map.Entry<Long, TbVmsCtlr> e : AppRepository.getInstance().getCtlrMap().entrySet()) {
         for (Map.Entry<Long, TbVmsCtlrDto> e : this.repoService.getCtlrMap().entrySet()) {
             TbVmsCtlrDto ctlr = e.getValue();
-            //if (ctlr != null && ctlr.getNetState() > NET.CLOSED && ctlr.getChannel() != null) {
-            if (ctlr != null && ctlr.getChannel() != null) {
-                if (ctlr.getNetState() == NET.LOGIN_REQ) {
-                    VmsServerIdleStateHandler.disconnectChannel(ctlr, ctlr.getChannel());
-                }
-                else {
-                    VmsServerIdleStateHandler.disconnectChannel(ctlr, ctlr.getChannel());
-                }
+            if (ctlr.getChannel() != null) {
+                VmsServerIdleStateHandler.disconnectChannel(ctlr, ctlr.getChannel());
             }
         }
-        try {
-            Thread.sleep(1000);
-        } catch (InterruptedException e) {
-            e.printStackTrace();
-        }
         return result;
     }
 
@@ -120,7 +94,6 @@ public class WebAppController {
     @ResponseBody
     public String resetConnCount(@RequestParam("id") Long id) {
         String result = "통신 접속횟수를 초기화 하였습니다.";
-        //TbVmsCtlr ctlr = AppRepository.getInstance().getCtlrMap().get(id);
         TbVmsCtlrDto ctlr = this.repoService.getCtrlMap(id);
         if (ctlr != null) {
             ctlr.resetConnectCount();
@@ -132,7 +105,6 @@ public class WebAppController {
     @ResponseBody
     public String allResetConnCount() {
         String result = "모든 제어기의 통신 접속횟수를 초기화 하였습니다.";
-        //for (Map.Entry<Long, TbVmsCtlr> e : AppRepository.getInstance().getCtlrMap().entrySet()) {
         for (Map.Entry<Long, TbVmsCtlrDto> e : this.repoService.getCtlrMap().entrySet()) {
             TbVmsCtlrDto ctlr = e.getValue();
             ctlr.resetConnectCount();
@@ -144,15 +116,9 @@ public class WebAppController {
     @ResponseBody
     public String resetController(@RequestParam("id") Long id) {
         String result = "제어기를 리셋 하였습니다.";
-        //TbVmsCtlr ctlr = AppRepository.getInstance().getCtlrMap().get(id);
         TbVmsCtlrDto ctlr = this.repoService.getCtrlMap(id);
         if (ctlr != null && ctlr.getNetState() > NET.CLOSED && ctlr.getChannel() != null) {
-//            TcpServerIdleStateHandler.disconnectChannel(ctlr.getChannel());
-//            try {
-//                Thread.sleep(1000);
-//            } catch (InterruptedException e) {
-//                e.printStackTrace();
-//            }
+            VmsServerIdleStateHandler.disconnectChannel(ctlr, ctlr.getChannel());
         }
         else {
             result = "제어기는 현재 연결되어 있지 않습니다.";
@@ -164,23 +130,12 @@ public class WebAppController {
     @ResponseBody
     public String allResetController(@RequestParam("id") String id) {
         String result = "통신 연결 중인 제어기를 리셋 하였습니다.";
-        //for (Map.Entry<Long, TbVmsCtlr> e : AppRepository.getInstance().getCtlrMap().entrySet()) {
         for (Map.Entry<Long, TbVmsCtlrDto> e : this.repoService.getCtlrMap().entrySet()) {
             TbVmsCtlrDto ctlr = e.getValue();
             if (ctlr != null && ctlr.getNetState() > NET.CLOSED && ctlr.getChannel() != null) {
-                if (ctlr.getNetState() == NET.LOGIN_REQ) {
-                    VmsServerIdleStateHandler.disconnectChannel(ctlr, ctlr.getChannel());
-                }
-                else {
-                    VmsServerIdleStateHandler.disconnectChannel(ctlr, ctlr.getChannel());
-                }
+                VmsServerIdleStateHandler.disconnectChannel(ctlr, ctlr.getChannel());
             }
         }
-        try {
-            Thread.sleep(1000);
-        } catch (InterruptedException e) {
-            e.printStackTrace();
-        }
         return result;
     }
 

+ 1 - 0
src/main/java/com/its/vms/xnettcp/vms/protocol/VmsProtocolConst.java

@@ -11,4 +11,5 @@ public class VmsProtocolConst {
     public static byte P_FILE_TYPE_STREAM  = (byte)0x03;
     public static byte P_FILE_TYPE_TRAFFIC = (byte)0x04;
     public static byte P_FILE_TYPE_TEXT    = (byte)0x05;
+
 }

+ 42 - 0
src/main/java/com/its/vms/xnettcp/vms/protocol/enums/eVmsOpCode.java

@@ -0,0 +1,42 @@
+package com.its.vms.xnettcp.vms.protocol.enums;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public enum eVmsOpCode {
+
+    vms_req_control_signboard  ((byte)0x01, "signboard control"),
+    vms_req_control_initialize ((byte)0x02, "initialize(reset)"),
+    vms_req_control_luminance  ((byte)0x06, "luminance control");
+
+    private final byte value;
+    private final String string;
+
+    private static final Map<Integer, eVmsOpCode> map;
+    static {
+        map = new HashMap<>();
+        for (eVmsOpCode e : values()) {
+            map.put(Integer.valueOf(e.value), e);
+        }
+    }
+
+    public static eVmsOpCode getValue(int value) {
+        return map.get(value);
+    }
+    public static eVmsOpCode getValue(byte value) {
+        int intValue = (value & 0x0F);
+        return map.get(intValue);
+    }
+
+    eVmsOpCode(byte value, String string) {
+        this.value  = value;
+        this.string = string;
+    }
+
+    public byte getValue() {
+        return this.value;
+    }
+    public String toString() {
+        return this.string;
+    }
+}

+ 7 - 7
src/main/webapp/WEB-INF/jsp/controller.jsp

@@ -30,10 +30,10 @@
                         </thead>
                         <c:forEach var="entry" items="${list}" varStatus="status">
                             <tr>
-                                <td align="center">${entry.value.RSE_CTLR_NMBR}</td>
-                                <td align="center">${entry.value.RSE_CTLR_ID}</td>
-                                <td>${entry.value.RSE_NM}</td>
-                                <td>&nbsp;${entry.value.RSE_CTLR_IP}&nbsp;</td>
+                                <td align="center">${entry.value.vmsCtlrNmbr}</td>
+                                <td align="center">${entry.value.ctlrId}</td>
+                                <td>${entry.value.name}</td>
+                                <td>&nbsp;${entry.value.ctlrIp}&nbsp;</td>
                                 <td align="center">
                                     <c:choose>
                                         <c:when test="${entry.value.netState eq '0'}">
@@ -48,9 +48,9 @@
                                 <td align="center">&nbsp;${entry.value.disConnectTm}&nbsp;</td>
                                 <td align="center">${entry.value.connectCount}</td>
                                 <td align="center">
-                                    &nbsp;<a href=javascript:disconnectController('${entry.value.RSE_CTLR_NMBR}')>연결해제</a>&nbsp;&nbsp;
-                                    <!--<a href=javascript:resetController('${entry.value.RSE_CTLR_NMBR}')>제어기리셋</a>&nbsp;&nbsp;-->
-                                    <a href=javascript:resetConnCount('${entry.value.RSE_CTLR_NMBR}')>접속횟수초기화</a>&nbsp;</td>
+                                    &nbsp;<a href=javascript:disconnectController('${entry.value.vmsCtlrNmbr}')>연결해제</a>&nbsp;&nbsp;
+                                    <!--<a href=javascript:resetController('${entry.value.vmsCtlrNmbr}')>제어기리셋</a>&nbsp;&nbsp;-->
+                                    <a href=javascript:resetConnCount('${entry.value.vmsCtlrNmbr}')>접속횟수초기화</a>&nbsp;</td>
                             </tr>
                         </c:forEach>
                     </table>

+ 1 - 1
src/main/webapp/WEB-INF/jsp/head.jsp

@@ -7,4 +7,4 @@
 <script type="text/javascript" src="/js/jquery-2.2.4.min.js"></script>
 <script type="text/javascript" src="/js/ajax.js"></script>
 <script type="text/javascript" src="/js/main.js"></script>
-<script type="text/javascript" src="/js/login.js"></script>
+<%--<script type="text/javascript" src="/js/login.js"></script>--%>