shjung 3 years ago
parent
commit
6d7361c3ff
31 changed files with 1151 additions and 29 deletions
  1. 22 0
      pom.xml
  2. 23 0
      src/main/java/com/its/app/utils/Converter.java
  3. 39 0
      src/main/java/com/its/app/utils/Counter.java
  4. 10 0
      src/main/java/com/its/cctv/CctvCommServerApplication.java
  5. 63 0
      src/main/java/com/its/cctv/api/controller/CctvControlController.java
  6. 144 0
      src/main/java/com/its/cctv/api/dto/CctvPresetControlDto.java
  7. 88 0
      src/main/java/com/its/cctv/api/dto/CctvPtzControlDto.java
  8. 49 0
      src/main/java/com/its/cctv/api/dto/CctvStatusDto.java
  9. 105 0
      src/main/java/com/its/cctv/api/dto/TbCctvCtlrSttsDto.java
  10. 206 0
      src/main/java/com/its/cctv/api/service/CctvControlService.java
  11. 34 0
      src/main/java/com/its/cctv/api/websocket/ItsWebSocketConfig.java
  12. 68 0
      src/main/java/com/its/cctv/api/websocket/ItsWebSocketHandler.java
  13. 18 0
      src/main/java/com/its/cctv/api/websocket/ItsWebSocketMessage.java
  14. 35 0
      src/main/java/com/its/cctv/api/websocket/ItsWebSocketSession.java
  15. 68 0
      src/main/java/com/its/cctv/api/websocket/ItsWebSocketSessionManager.java
  16. 55 0
      src/main/java/com/its/cctv/config/SwaggerConfig.java
  17. 36 0
      src/main/java/com/its/cctv/entity/CctvPresetValue.java
  18. 3 0
      src/main/java/com/its/cctv/entity/TbCctvCtlr.java
  19. 20 0
      src/main/java/com/its/cctv/entity/TbCctvCtlrStts.java
  20. 2 2
      src/main/java/com/its/cctv/ui/SubUI.java
  21. 14 0
      src/main/java/com/its/cctv/webapp/config/WebSecurityConfig.java
  22. 3 0
      src/main/java/com/its/cctv/xnettcp/cctv/process/Job_PresetQryRes.java
  23. 3 0
      src/main/java/com/its/cctv/xnettcp/cctv/protocol/CctvProtocol.java
  24. 16 0
      src/main/java/com/its/cctv/xnettcp/cctv/protocol/CctvReqPresetDel.java
  25. 5 5
      src/main/java/com/its/cctv/xnettcp/cctv/protocol/CctvReqPresetMove.java
  26. 2 2
      src/main/java/com/its/cctv/xnettcp/cctv/protocol/CctvReqPresetQry.java
  27. 7 7
      src/main/java/com/its/cctv/xnettcp/cctv/protocol/CctvReqPresetSave.java
  28. 3 3
      src/main/java/com/its/cctv/xnettcp/cctv/protocol/CctvReqPresetSel.java
  29. 2 2
      src/main/resources/application.yml
  30. 7 7
      src/main/webapp/WEB-INF/jsp/controller.jsp
  31. 1 1
      src/main/webapp/WEB-INF/jsp/system.jsp

+ 22 - 0
pom.xml

@@ -26,6 +26,10 @@
 
     <properties>
         <java.version>1.8</java.version>
+        <swagger.version>2.9.2</swagger.version>
+        <org.projectlombok.version>1.18.20</org.projectlombok.version>
+        <org.mapstruct.version>1.4.2.Final</org.mapstruct.version>
+        <jackson.version>2.13.1</jackson.version>
         <!-- jar 배포용 -->
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
         <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
@@ -38,6 +42,24 @@
     </properties>
 
     <dependencies>
+        <dependency>
+            <groupId>io.springfox</groupId>
+            <artifactId>springfox-swagger2</artifactId>
+            <version>${swagger.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>io.springfox</groupId>
+            <artifactId>springfox-swagger-ui</artifactId>
+            <version>${swagger.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-validation</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-websocket</artifactId>
+        </dependency>
         <dependency>
             <groupId>org.fusesource.jansi</groupId>
             <artifactId>jansi</artifactId>

+ 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());
+    }
+
+}

+ 10 - 0
src/main/java/com/its/cctv/CctvCommServerApplication.java

@@ -37,7 +37,9 @@ import java.awt.event.WindowEvent;
 import java.io.File;
 import java.io.IOException;
 import java.io.PrintStream;
+import java.lang.reflect.Field;
 import java.net.URL;
+import java.nio.charset.Charset;
 
 @Slf4j
 @EnableAsync
@@ -50,6 +52,14 @@ public class CctvCommServerApplication implements CommandLineRunner, Application
     private static String applicationName = "cctv-comm-server";
 
     public static void main(String[] args) {
+        System.setProperty("file.encoding","UTF-8");
+        try {
+            Field charset = Charset.class.getDeclaredField("defaultCharset");
+            charset.setAccessible(true);
+            charset.set(null,null);
+        } catch(Exception e){
+        }
+
         File file1 = new File("./conf/" + applicationName + ".pid");
         if (file1.exists()) {
             System.out.println(System.getProperty("Program Already Running....."));

+ 63 - 0
src/main/java/com/its/cctv/api/controller/CctvControlController.java

@@ -0,0 +1,63 @@
+package com.its.cctv.api.controller;
+
+import com.its.cctv.api.dto.CctvPresetControlDto;
+import com.its.cctv.api.dto.CctvPtzControlDto;
+import com.its.cctv.api.dto.CctvStatusDto;
+import com.its.cctv.api.service.CctvControlService;
+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 = "11.CCTV-0.제어")
+@Validated
+@RestController
+@RequiredArgsConstructor
+@RequestMapping("/api/cctv/control")
+public class CctvControlController {
+
+    private final CctvControlService service;
+
+    @ApiOperation(value = "CCTV PTZ 제어", 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 = "CCTV PTZ 제어", 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 = "CCTV PTZ 제어", response = CctvPtzControlDto.CctvPtzControlRes.class)
+    @PostMapping(value = "/ptz/{id}", produces = {"application/json; charset=utf8"})
+    public CctvPtzControlDto.CctvPtzControlRes controlPtz(
+            @ApiParam(name = "id", value = "제어기번호", example = "1001", required = true)
+            @PathVariable("id") Long id,
+            @ApiParam(name = "req", value = "CCTV PTZ 제어 정보", example = "[tilt-up, start, 20, ADMIN]", required = true)
+            @RequestBody @Valid final CctvPtzControlDto.CctvControlPtzReq req) {
+        return this.service.controlPtz(id, req);
+    }
+
+    @ApiOperation(value = "CCTV Preset 제어", response = CctvPresetControlDto.CctvPresetControlRes.class)
+    @PostMapping(value = "/preset/{id}", produces = {"application/json; charset=utf8"})
+    public CctvPresetControlDto.CctvPresetControlRes controlPreset(
+            @ApiParam(name = "id", value = "제어기번호", example = "1001", required = true)
+            @PathVariable("id") Long id,
+            @ApiParam(name = "req", value = "CCTV Preset 제어 정보", example = "[preset, call, 1, 0, 0, ADMIN]", required = true)
+            @RequestBody @Valid final CctvPresetControlDto.CctvPresetControlReq req) {
+        return this.service.controlPreset(id, req);
+    }
+
+}

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

@@ -0,0 +1,144 @@
+package com.its.cctv.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 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;
+
+        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(Integer pan, Integer tilt, Integer zoom, Integer focus) {
+            this.pan = pan;
+            this.tilt = tilt;
+            this.zoom = zoom;
+            this.focus = focus;
+        }
+    }
+
+}

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

@@ -0,0 +1,88 @@
+package com.its.cctv.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;
+        }
+    }
+}

+ 49 - 0
src/main/java/com/its/cctv/api/dto/CctvStatusDto.java

@@ -0,0 +1,49 @@
+package com.its.cctv.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;
+        }
+    }
+}

+ 105 - 0
src/main/java/com/its/cctv/api/dto/TbCctvCtlrSttsDto.java

@@ -0,0 +1,105 @@
+package com.its.cctv.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;
+
+    // Code Description Field
+    @ApiModelProperty("통신 상태 코드 설명")
+    @JsonProperty("cmnc_stts_desc")    // CMNC_STTS_CD
+    private String cmncSttsDesc;
+
+    @ApiModelProperty("함체 문 상태 코드('CDS')-앞문/뒷문상태가 모두닫힌경우 닫힘 설명")
+    @JsonProperty("cbox_door_stts_desc")    // CBOX_DOOR_STTS_CD
+    private String cboxDoorSttsDesc;
+
+    @ApiModelProperty("앞문 상태 코드('CDS') 설명")
+    @JsonProperty("front_door_stts_desc")    // FRONT_DOOR_STTS_CD
+    private String frontDoorSttsDesc;
+
+    @ApiModelProperty("뒷문 상태 코드('CDS') 설명")
+    @JsonProperty("back_door_stts_desc")    // BACK_DOOR_STTS_CD
+    private String backDoorSttsDesc;
+
+    @ApiModelProperty("팬 상태 코드('PAS') 설명")
+    @JsonProperty("fan_stts_desc")    // FAN_STTS_CD
+    private String fanSttsDesc;
+
+    @ApiModelProperty("히터 상태 코드('HTS') 설명")
+    @JsonProperty("hetr_stts_desc")    // HETR_STTS_CD
+    private String hetrSttsDesc;
+
+}

+ 206 - 0
src/main/java/com/its/cctv/api/service/CctvControlService.java

@@ -0,0 +1,206 @@
+package com.its.cctv.api.service;
+
+import com.its.app.utils.ByteUtils;
+import com.its.cctv.api.dto.CctvPresetControlDto;
+import com.its.cctv.api.dto.CctvPtzControlDto;
+import com.its.cctv.api.dto.CctvStatusDto;
+import com.its.cctv.domain.NET;
+import com.its.cctv.entity.TbCctvCtlr;
+import com.its.cctv.global.AppRepository;
+import com.its.cctv.xnettcp.cctv.protocol.CctvReqPresetMove;
+import com.its.cctv.xnettcp.cctv.protocol.CctvReqPresetQry;
+import com.its.cctv.xnettcp.cctv.protocol.CctvReqPtzCtrl;
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+import lombok.Setter;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang.StringUtils;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.PostConstruct;
+import java.nio.ByteBuffer;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.NoSuchElementException;
+
+@Slf4j
+@RequiredArgsConstructor
+@Service
+public class CctvControlService {
+
+    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 TbCctvCtlr requireOne(Long id) throws NoSuchElementException {
+        return AppRepository.getInstance().getCtlrMap().get(String.valueOf(id));
+    }
+
+    /**
+     * CCTV 상태정보 요청 처리
+     * @param id
+     * @return
+     */
+    public CctvStatusDto.CctvStatusDtoRes requestStatus(Long id) {
+        TbCctvCtlr cctv = this.requireOne(id);
+        if (cctv == null) {
+            return new CctvStatusDto.CctvStatusDtoRes(1, "알수없는 CCTV 관리번호 입니다.", null);
+        }
+        if (cctv.getNetState() != NET.LOGINED) {
+            return new CctvStatusDto.CctvStatusDtoRes(2, "CCTV 통신연결이 되어 있지 않습니다.", null);
+        }
+
+        CctvStatusDto.CctvStatusDtoRes result = new CctvStatusDto.CctvStatusDtoRes(0, "success", cctv.getStts().toDto());
+        return result;
+    }
+
+    /**
+     * CCTV PTZ 컨트롤
+     * @param id
+     * @param req
+     * @return
+     */
+    public CctvPtzControlDto.CctvPtzControlRes controlPtz(Long id, CctvPtzControlDto.CctvControlPtzReq req) {
+        log.info("{}", req);
+        TbCctvCtlr cctv = this.requireOne(id);
+        if (cctv == null) {
+            return new CctvPtzControlDto.CctvPtzControlRes(req.getCommand(), req.getAction(), 1, "알수없는 CCTV 관리번호 입니다.");
+        }
+        if (cctv.getNetState() != NET.LOGINED) {
+            return new CctvPtzControlDto.CctvPtzControlRes(req.getCommand(), req.getAction(), 2, "CCTV 통신연결이 되어 있지 않습니다.");
+        }
+
+        ControlCmd command = this.ptzControlMap.get(req.getCommand());
+        if (command == null) {
+            return new CctvPtzControlDto.CctvPtzControlRes(req.getCommand(), req.getAction(), 3, "CCTV 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(cctv.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 (cctv.sendData(sendBuffer, 0, "cctv_StateReq")) {
+                log.info("{} send success.", cmdDesc);
+            } else {
+                result.setResult(4, "CCTV PTZ 시작 명령 전송이 실패하였습니다.");
+                log.error("{} send failed.", cmdDesc);
+            }
+        } else {
+            // stop
+            CctvReqPtzCtrl pkt = new CctvReqPtzCtrl(cctv.getAddress());
+            ByteBuffer sendBuffer = pkt.getByteBuffer();
+            if (cctv.sendData(sendBuffer, 0, "cctv_StateReq")) {
+                log.info("{} STOP send success.", cmdDesc);
+            } else {
+                result.setResult(4, "CCTV PTZ 정지 명령 전송이 실패하였습니다.");
+                log.error("{} STOP send failed.", cmdDesc);
+            }
+        }
+        return result;
+    }
+
+    /**
+     * CCTV Preset 컨트롤
+     * @param id
+     * @param req
+     * @return
+     */
+    public CctvPresetControlDto.CctvPresetControlRes controlPreset(Long id, CctvPresetControlDto.CctvPresetControlReq req) {
+        log.info("{}", req);
+        TbCctvCtlr cctv = this.requireOne(id);
+        if (cctv == null) {
+            return new CctvPresetControlDto.CctvPresetControlRes(1, "unknown cctv type");
+        }
+        if (cctv.getNetState() != NET.LOGINED) {
+            return new CctvPresetControlDto.CctvPresetControlRes(2, "CCTV 통신연결이 되어 있지 않습니다.");
+        }
+
+        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.");
+        } else {
+            result.setResult(4, "CCTV PRESET 명령 전송이 실패하였습니다.");
+            log.error("PRESET send failed.");
+        }
+        return result;
+    }
+
+    public CctvPresetControlDto.CctvPresetValueRes requestPresetValue(Long id) {
+        log.info("{}", id);
+        TbCctvCtlr cctv = this.requireOne(id);
+        if (cctv == null) {
+            return new CctvPresetControlDto.CctvPresetValueRes(1, "unknown cctv type");
+        }
+        if (cctv.getNetState() != NET.LOGINED) {
+            return new CctvPresetControlDto.CctvPresetValueRes(2, "CCTV 통신연결이 되어 있지 않습니다.");
+        }
+
+        CctvPresetControlDto.CctvPresetValueRes result = new CctvPresetControlDto.CctvPresetValueRes(0, "success");
+        CctvReqPresetQry pkt = new CctvReqPresetQry(cctv.getAddress());
+        pkt.setValue((byte)0x00);
+        ByteBuffer sendBuffer = pkt.getByteBuffer();
+        if (cctv.sendData(sendBuffer, 0, "cctv_PresetQry")) {
+            log.info("PRESET QRY send success.");
+        } else {
+            result.setResult(4, "CCTV PRESET 값조회 명령 전송이 실패하였습니다.");
+            log.error("PRESET QRY send failed.");
+        }
+        return result;
+    }
+
+}

+ 34 - 0
src/main/java/com/its/cctv/api/websocket/ItsWebSocketConfig.java

@@ -0,0 +1,34 @@
+package com.its.cctv.api.websocket;
+
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.socket.config.annotation.EnableWebSocket;
+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 지원
+    }
+
+}

+ 68 - 0
src/main/java/com/its/cctv/api/websocket/ItsWebSocketHandler.java

@@ -0,0 +1,68 @@
+package com.its.cctv.api.websocket;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Controller;
+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/cctv/api/websocket/ItsWebSocketMessage.java

@@ -0,0 +1,18 @@
+package com.its.cctv.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/cctv/api/websocket/ItsWebSocketSession.java

@@ -0,0 +1,35 @@
+package com.its.cctv.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/cctv/api/websocket/ItsWebSocketSessionManager.java

@@ -0,0 +1,68 @@
+package com.its.cctv.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);
+        });
+    }
+}

+ 55 - 0
src/main/java/com/its/cctv/config/SwaggerConfig.java

@@ -0,0 +1,55 @@
+package com.its.cctv.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.HttpHeaders;
+import springfox.documentation.builders.ApiInfoBuilder;
+import springfox.documentation.builders.ParameterBuilder;
+import springfox.documentation.builders.PathSelectors;
+import springfox.documentation.builders.RequestHandlerSelectors;
+import springfox.documentation.schema.ModelRef;
+import springfox.documentation.service.ApiInfo;
+import springfox.documentation.service.Parameter;
+import springfox.documentation.spi.DocumentationType;
+import springfox.documentation.spring.web.plugins.Docket;
+import springfox.documentation.swagger2.annotations.EnableSwagger2;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Configuration
+@EnableSwagger2
+public class SwaggerConfig {
+
+    @Bean
+    public Docket commonApi() {
+        Parameter parameterBuilder = new ParameterBuilder()
+                .name(HttpHeaders.AUTHORIZATION)
+                .description("Access Token")
+                .modelRef(new ModelRef("string"))
+                .parameterType("header")
+                .required(false)
+                .build();
+
+        List<Parameter> globalParameters = new ArrayList<>();
+        globalParameters.add(parameterBuilder);
+        return new Docket(DocumentationType.SWAGGER_2)
+                .globalOperationParameters(globalParameters)
+                .groupName("cctv-api")
+                .apiInfo(this.apiInfo())
+                .select()
+                .apis(RequestHandlerSelectors.basePackage("com.its.cctv.api.controller"))
+                .paths(PathSelectors.any())
+                //.paths(PathSelectors.ant("/api/**"))
+                .build();
+    }
+
+    private ApiInfo apiInfo() {
+        return new ApiInfoBuilder()
+                .title("CCTV Control API")
+                .description("CCTV Communication Server API")
+                .version("0.0.1")
+                .build();
+    }
+
+}

+ 36 - 0
src/main/java/com/its/cctv/entity/CctvPresetValue.java

@@ -0,0 +1,36 @@
+package com.its.cctv.entity;
+
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+import lombok.extern.slf4j.Slf4j;
+
+import java.io.Serializable;
+
+@Slf4j
+@Getter
+@Setter
+@ToString
+public class CctvPresetValue implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    private Short pan;
+    private Short tilt;
+    private Short zoom;
+    private Short focus;
+
+    public void init() {
+        this.pan = 0;
+        this.tilt = 0;
+        this.zoom = 0;
+        this.focus = 0;
+    }
+
+    public void setValue(Short pan, Short tilt, Short zoom, Short focus) {
+        this.pan = pan;
+        this.tilt = tilt;
+        this.zoom = zoom;
+        this.focus = focus;
+    }
+
+}

+ 3 - 0
src/main/java/com/its/cctv/entity/TbCctvCtlr.java

@@ -43,6 +43,7 @@ public class TbCctvCtlr {
 	private boolean           isDupLogin;
 	private String            dstIpAddr;
 	private TbCctvCtlrStts    stts;
+	private CctvPresetValue   psetVal;
 	private Channel           channel;
 	private Channel           dupChannel;
 	private long              syncTime;
@@ -64,6 +65,8 @@ public class TbCctvCtlr {
 
 	public TbCctvCtlr() {
 		this.stts = new TbCctvCtlrStts();
+		this.psetVal = new CctvPresetValue();
+		this.psetVal.init();
 
 		this.connectCount = 0;
 		this.connectTm = "";

+ 20 - 0
src/main/java/com/its/cctv/entity/TbCctvCtlrStts.java

@@ -1,6 +1,7 @@
 package com.its.cctv.entity;
 
 import com.its.app.utils.SysUtils;
+import com.its.cctv.api.dto.TbCctvCtlrSttsDto;
 import lombok.Getter;
 import lombok.Setter;
 import lombok.ToString;
@@ -97,4 +98,23 @@ public class TbCctvCtlrStts {
 		return (short)CBOX_TMPR;
 	}
 
+	public TbCctvCtlrSttsDto toDto() {
+		return TbCctvCtlrSttsDto.builder()
+				.cctvCtlrNmbr(Long.valueOf(this.CCTV_CTLR_NMBR))
+				.updtDt(this.UPDT_DT)
+				.cmncSttsCd(this.CMNC_STTS_CD)
+				.cboxDoorSttsCd(this.CBOX_DOOR_STTS_CD)
+				.frontDoorSttsCd(this.FRONT_DOOR_STTS_CD)
+				.backDoorSttsCd(this.BACK_DOOR_STTS_CD)
+				.fanSttsCd(this.FAN_STTS_CD)
+				.hetrSttsCd(this.HETR_STTS_CD)
+				.videoInput(this.VIDEO_INPUT)
+				.cboxTmpr(this.CBOX_TMPR)
+				.cboxHmdt(this.CBOX_HMDT)
+				.pan(this.PAN)
+				.tilt(this.TILT)
+				.zoom(this.ZOOM)
+				.focus(this.FOCUS)
+				.build();
+	}
 }

+ 2 - 2
src/main/java/com/its/cctv/ui/SubUI.java

@@ -291,7 +291,7 @@ public abstract class SubUI {
 
         CctvReqPtzCtrl pkt = new CctvReqPtzCtrl(this.selObj.getAddress());
         ByteBuffer sendBuffer = pkt.getByteBuffer();
-        if (this.selObj.sendData(sendBuffer, 0, "cctv_StateReq")) {
+        if (this.selObj.sendData(sendBuffer, 0, "cctv_PtzStop")) {
             logging("STOP Command send success.");
         } else {
             logging("STOP Command send failed.");
@@ -321,7 +321,7 @@ public abstract class SubUI {
         byte ptz = pkt.setPtzValue((byte) pan, (byte) tilt, (byte) zoom, (byte) focus, (byte) panSped, (byte) tiltSped, (byte) zoomSped, (byte) focusSped);
         logging("PTZ Control: " + ByteUtils.byteToBitString(ptz));
         ByteBuffer sendBuffer = pkt.getByteBuffer();
-        if (this.selObj.sendData(sendBuffer, 0, "cctv_StateReq")) {
+        if (this.selObj.sendData(sendBuffer, 0, "cctv_PtzStart")) {
             logging(cmd + " send success.");
         } else {
             logging(cmd + " send failed.");

+ 14 - 0
src/main/java/com/its/cctv/webapp/config/WebSecurityConfig.java

@@ -4,6 +4,7 @@ import com.its.cctv.webapp.security.SessionListener;
 import com.its.cctv.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;
@@ -25,13 +26,26 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
         this.userService = userService;
     }
 
+    @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(HttpMethod.GET, "/api/**");  // GET Method 는 모두 통과
+        web.ignoring().antMatchers(HttpMethod.POST, "/api/**");  // GET Method 는 모두 통과
+        web.ignoring().antMatchers(HttpMethod.PUT, "/api/**");  // GET Method 는 모두 통과
+        web.ignoring().antMatchers(HttpMethod.DELETE, "/api/**");  // GET Method 는 모두 통과
     }
 
+    @Override
     protected void configure(HttpSecurity http) throws Exception {
 
         http.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[]{"/**"})

+ 3 - 0
src/main/java/com/its/cctv/xnettcp/cctv/process/Job_PresetQryRes.java

@@ -28,11 +28,14 @@ public class Job_PresetQryRes implements JobProtocol {
 			return 0;
 		}
 
+		// TODO: 프리셋 정보 요청을 운영단말에서 한 경우 이므로 운영단말로 전송해야 한다.
 		ByteBuffer buffer = ByteBuffer.wrap(body);
 		short pan = buffer.getShort();
 		short tilt = buffer.getShort();
 		short zoom  = buffer.getShort();
 		short focus = buffer.getShort();
+		obj.getPsetVal().setValue(pan, tilt, zoom, focus);
+		obj.getPsetVal().notifyAll();
 
 		MainUI mainUI = MainUI.getInstance();
 		if (mainUI != null) {

+ 3 - 0
src/main/java/com/its/cctv/xnettcp/cctv/protocol/CctvProtocol.java

@@ -67,11 +67,14 @@ public class CctvProtocol {
     public static final byte cctv_ParamSet   = 0x23;    // 환경 Parameter 설정              - 개입제어  - Fan/Heater 동작 설정
     public static final byte cctv_ParamReq   = 0x24;    // 환경 Parameter 요구              - 개입제어  - 동작 설정값 요청
     public static final byte cctv_PtzCtrl    = 0x30;    // 촬상부 제어(P/T, Lens제어)       - 개입제어  - P/T/Z/F Go/Stop
+
     public static final byte cctv_PresetMove = 0x31;    // Preset Value 이동                - 개입제어  - P/T/Z/F Value로 이동
+
     public static final byte cctv_PresetSav  = 0x33;    // Preset Value 입력                - 개입제어  - P/T/Z/F Value를 입력
     public static final byte cctv_PresetSel  = 0x34;    // Preset Value 선택                - 개입제어  - P/T/Z/F Value를 선택
     public static final byte cctv_PresetDel  = 0x35;    // Preset Value 삭제                - 개입제어  - P/T/Z/F Value를 삭제
     public static final byte cctv_PresetQry  = 0x36;    // Preset Value 조회                - 개입제어  - P/T/Z/F Value를 조회
+
     public static final byte cctv_CharFixSet = 0x41;    // 고정문자 표출 환경 설정          - 개입제어  - -
     public static final byte cctv_CharVarSet = 0x42;    // 가변문자 표출 환경 설정          - 개입제어  - -
     public static final byte cctv_CharVarDel = 0x43;    // 가변(설정) 문자 지우기           - 개입제어  - -

+ 16 - 0
src/main/java/com/its/cctv/xnettcp/cctv/protocol/CctvReqPresetDel.java

@@ -0,0 +1,16 @@
+package com.its.cctv.xnettcp.cctv.protocol;
+
+/**
+ * Preset 삭제
+ */
+public class CctvReqPresetDel extends CctvReqFramePacket {
+
+    public CctvReqPresetDel(short groupNo) {
+        super(groupNo, CctvProtocol.cctv_PresetDel);
+        this.body = new byte[1];
+    }
+
+    public void setValue(byte presetNo) {
+        this.body[0] = presetNo;    // Preset No(1~10)
+    }
+}

+ 5 - 5
src/main/java/com/its/cctv/xnettcp/cctv/protocol/CctvReqPresetMove.java

@@ -3,7 +3,7 @@ package com.its.cctv.xnettcp.cctv.protocol;
 import java.nio.ByteBuffer;
 
 /**
- * PTZ 제어
+ * Preset 이동
  */
 public class CctvReqPresetMove extends CctvReqFramePacket {
 
@@ -17,10 +17,10 @@ public class CctvReqPresetMove extends CctvReqFramePacket {
     public void setValue(short pan, short tilt, short zoom, short focus) {
         ByteBuffer buffer = ByteBuffer.allocate(this.packetSize);
         buffer.order(CctvProtocol.byteOrder);
-        buffer.putShort(pan);
-        buffer.putShort(tilt);
-        buffer.putShort(zoom);
-        buffer.putShort(focus);
+        buffer.putShort(pan);       // 0: Don't move, Pan Preset Value
+        buffer.putShort(tilt);      // 0: Don't move, Tilt Preset Value
+        buffer.putShort(zoom);      // 0: Don't move, Zoom Preset Value
+        buffer.putShort(focus);     // 0: Don't move, Focus Preset Value
         buffer.get(this.body, 0, this.body.length);
     }
 }

+ 2 - 2
src/main/java/com/its/cctv/xnettcp/cctv/protocol/CctvReqPresetQry.java

@@ -1,12 +1,12 @@
 package com.its.cctv.xnettcp.cctv.protocol;
 
 /**
- * PTZ 제어
+ * Preset 값 조회
  */
 public class CctvReqPresetQry extends CctvReqFramePacket {
 
     public CctvReqPresetQry(short groupNo) {
-        super(groupNo, CctvProtocol.cctv_PresetMove);
+        super(groupNo, CctvProtocol.cctv_PresetQry);
         this.body = new byte[1];
     }
 

+ 7 - 7
src/main/java/com/its/cctv/xnettcp/cctv/protocol/CctvReqPresetSave.java

@@ -3,25 +3,25 @@ package com.its.cctv.xnettcp.cctv.protocol;
 import java.nio.ByteBuffer;
 
 /**
- * PTZ 제어
+ * Preset 제어
  */
 public class CctvReqPresetSave extends CctvReqFramePacket {
 
     private final int packetSize = 9;
 
     public CctvReqPresetSave(short groupNo) {
-        super(groupNo, CctvProtocol.cctv_PresetMove);
+        super(groupNo, CctvProtocol.cctv_PresetSav);
         this.body = new byte[this.packetSize];
     }
 
     public void setValue(short pan, short tilt, short zoom, short focus, byte presetNo) {
         ByteBuffer buffer = ByteBuffer.allocate(this.packetSize);
         buffer.order(CctvProtocol.byteOrder);
-        buffer.putShort(pan);
-        buffer.putShort(tilt);
-        buffer.putShort(zoom);
-        buffer.putShort(focus);
-        buffer.put(presetNo);
+        buffer.putShort(pan);       // Pan Preset Value
+        buffer.putShort(tilt);      // Tilt Preset Value
+        buffer.putShort(zoom);      // Zoom Preset Value
+        buffer.putShort(focus);     // Focus Preset Value
+        buffer.put(presetNo);       // Preset No
         buffer.get(this.body, 0, this.body.length);
     }
 }

+ 3 - 3
src/main/java/com/its/cctv/xnettcp/cctv/protocol/CctvReqPresetSel.java

@@ -1,16 +1,16 @@
 package com.its.cctv.xnettcp.cctv.protocol;
 
 /**
- * PTZ 제어
+ * Preset 선택
  */
 public class CctvReqPresetSel extends CctvReqFramePacket {
 
     public CctvReqPresetSel(short groupNo) {
-        super(groupNo, CctvProtocol.cctv_PresetMove);
+        super(groupNo, CctvProtocol.cctv_PresetSel);
         this.body = new byte[1];
     }
 
     public void setValue(byte presetNo) {
-        this.body[0] = presetNo;
+        this.body[0] = presetNo;    // Preset No(1~10)
     }
 }

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

@@ -12,7 +12,7 @@ running:
   connect-timeout: 5
 
 server:
-  port: 9879
+  port: 8903
   shutdown: graceful
   error:
     whitelabel:
@@ -29,7 +29,7 @@ spring:
   profiles:
     active: dev
   main:
-    web-application-type: none
+    #web-application-type: none
     log-startup-info: true
     banner-mode: off
   mvc:

+ 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.VDS_CTLR_NMBR}</td>
-                                <td align="center">${entry.value.VDS_CTLR_ID}</td>
-                                <td>${entry.value.VDS_NM}</td>
-                                <td>&nbsp;${entry.value.VDS_CTLR_IP}&nbsp;</td>
+                                <td align="center">${entry.value.CCTV_CTLR_NMBR}</td>
+                                <td align="center">${entry.value.CCTV_CTLR_ID}</td>
+                                <td>${entry.value.CCTV_NM}</td>
+                                <td>&nbsp;${entry.value.CCTV_CTLR_IP}&nbsp;</td>
                                 <td align="center">
                                     <c:choose>
                                         <c:when test="${entry.value.netState eq '0'}">
@@ -51,9 +51,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.VDS_CTLR_NMBR}')>연결해제</a>&nbsp;&nbsp;
-                                    <!--<a href=javascript:resetController('${entry.value.VDS_CTLR_NMBR}')>제어기리셋</a>&nbsp;&nbsp;-->
-                                    <a href=javascript:resetConnCount('${entry.value.VDS_CTLR_NMBR}')>접속횟수초기화</a>&nbsp;</td>
+                                    &nbsp;<a href=javascript:disconnectController('${entry.value.CCTV_CTLR_NMBR}')>연결해제</a>&nbsp;&nbsp;
+                                    <!--<a href=javascript:resetController('${entry.value.CCTV_CTLR_NMBR}')>제어기리셋</a>&nbsp;&nbsp;-->
+                                    <a href=javascript:resetConnCount('${entry.value.CCTV_CTLR_NMBR}')>접속횟수초기화</a>&nbsp;</td>
                             </tr>
                         </c:forEach>
                     </table>

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

@@ -15,7 +15,7 @@
                 <fieldset><legend> System Information </legend>
                     <table border="0" cellpadding="1" cellspacing="0" style="font-weight: normal;font-family:tahoma;font-size:12pt;">
                         <tr><td align="right" width="200">          system id : </td> <td><input type="text" value="${ServerConfig.id}" disabled /></td></tr>
-                        <tr><td align="right" width="200">center binding port : </td> <td><input type="text" value="${ServerConfig.centerBindPort}" disabled /></td></tr>
+                        <tr><td align="right" width="200">center binding port : </td> <td><input type="text" value="${ServerConfig.listenPort}" disabled /></td></tr>
                         <tr><td align="right" width="200">       booting time : </td> <td><input type="text" value="${ServerConfig.bootingDateTime}"   disabled /></td></tr>
                     </table>
                 </fieldset>