shjung 2 anni fa
parent
commit
136cf33a45
41 ha cambiato i file con 1962 aggiunte e 219 eliminazioni
  1. 1 1
      src/main/java/com/its/app/utils/ItsUtils.java
  2. 2 0
      src/main/java/com/its/vms/VmsCommServerApplication.java
  3. 14 3
      src/main/java/com/its/vms/config/ApplicationConfig.java
  4. 2 0
      src/main/java/com/its/vms/dao/mapper/VmsFormMapper.java
  5. 3 4
      src/main/java/com/its/vms/domain/VmsConstants.java
  6. 149 0
      src/main/java/com/its/vms/domain/VmsForm.java
  7. 71 0
      src/main/java/com/its/vms/domain/VmsFormManager.java
  8. 112 0
      src/main/java/com/its/vms/domain/VmsFormObject.java
  9. 44 0
      src/main/java/com/its/vms/domain/eVmsFileType.java
  10. 16 16
      src/main/java/com/its/vms/domain/eVmsFormType.java
  11. 19 41
      src/main/java/com/its/vms/dto/TbVmsAtmpDto.java
  12. 2 2
      src/main/java/com/its/vms/dto/TbVmsCtlrDto.java
  13. 21 0
      src/main/java/com/its/vms/dto/TbVmsFontColorDto.java
  14. 2 12
      src/main/java/com/its/vms/dto/TbVmsFontColrDto.java
  15. 21 0
      src/main/java/com/its/vms/dto/TbVmsFormColrDto.java
  16. 6 6
      src/main/java/com/its/vms/dto/TbVmsFormDto.java
  17. 1 2
      src/main/java/com/its/vms/dto/TbVmsFormObjectDto.java
  18. 1 19
      src/main/java/com/its/vms/dto/TbVmsIfscTrafDto.java
  19. 57 0
      src/main/java/com/its/vms/dto/TbVmsParkDto.java
  20. 1 12
      src/main/java/com/its/vms/dto/TbVmsSymbIfscDto.java
  21. 5 5
      src/main/java/com/its/vms/dto/TbVmsSymbLibDto.java
  22. 2 2
      src/main/java/com/its/vms/entity/TbVmsFontColr.java
  23. 6 6
      src/main/java/com/its/vms/entity/TbVmsForm.java
  24. 33 0
      src/main/java/com/its/vms/entity/TbVmsFormColr.java
  25. 1 2
      src/main/java/com/its/vms/entity/TbVmsFormObject.java
  26. 2 1
      src/main/java/com/its/vms/entity/TbVmsIfscTraf.java
  27. 1 0
      src/main/java/com/its/vms/entity/TbVmsSymbIfsc.java
  28. 1 1
      src/main/java/com/its/vms/entity/TbVmsSymbLib.java
  29. 9 0
      src/main/java/com/its/vms/service/AppRepositoryService.java
  30. 3 0
      src/main/java/com/its/vms/service/VmsAtmpService.java
  31. 20 10
      src/main/java/com/its/vms/service/VmsCtlrService.java
  32. 32 7
      src/main/java/com/its/vms/service/VmsFontService.java
  33. 53 14
      src/main/java/com/its/vms/service/VmsFormService.java
  34. 11 4
      src/main/java/com/its/vms/service/VmsIfscService.java
  35. 1188 33
      src/main/java/com/its/vms/service/VmsManageService.java
  36. 3 0
      src/main/java/com/its/vms/service/VmsParkService.java
  37. 13 3
      src/main/java/com/its/vms/service/VmsSymbService.java
  38. 11 1
      src/main/resources/application.yml
  39. 4 5
      src/main/resources/mybatis/mapper/VmsFontMapper.xml
  40. 13 4
      src/main/resources/mybatis/mapper/VmsFormMapper.xml
  41. 6 3
      src/test/java/com/its/app/VmsCommServerApplicationTests.java

+ 1 - 1
src/main/java/com/its/app/utils/ItsUtils.java

@@ -254,7 +254,7 @@ public final class ItsUtils
 			FileUtils.writeByteArrayToFile(new File(filePath), byteArrayData);
 		}
 		catch (IOException e) {
-			log.error("saveByteArrayToFile: IOException");
+			log.error("saveByteArrayToFile: IOException: {}, {}", filePath, e.getMessage());
 			result = false;
 		}
 		return result;

+ 2 - 0
src/main/java/com/its/vms/VmsCommServerApplication.java

@@ -180,7 +180,9 @@ public class VmsCommServerApplication implements CommandLineRunner, ApplicationL
         vmsFormService.loadDb();
 
         VmsManageService vmsManageService = (VmsManageService)AppUtils.getBean(VmsManageService.class);
+        vmsManageService.loadVmsOnOffTime();
         vmsManageService.initVmsDsplPrst();
+        vmsManageService.downloadVmsForm();;
 
         // VMS Communication Service Start.........
         VmsTcpCommServerService vmsTcpCommServerService = (VmsTcpCommServerService)AppUtils.getBean(VmsTcpCommServerService.class);

+ 14 - 3
src/main/java/com/its/vms/config/ApplicationConfig.java

@@ -1,6 +1,7 @@
 package com.its.vms.config;
 
 import com.its.app.utils.SysUtils;
+import com.its.vms.domain.VmsConstants;
 import lombok.Getter;
 import lombok.Setter;
 import lombok.ToString;
@@ -39,7 +40,6 @@ public class ApplicationConfig {
 
     // Center Communication Config
     private boolean centerCommEnable = true;
-    private int maxPhase = 10;
     private int listenPort = 30200;
     protected String bindingAddr = "0.0.0.0";
     protected int backlog = 0;
@@ -59,8 +59,19 @@ public class ApplicationConfig {
     private String ftpImageDir;  // FTP Image Directory
 
     private boolean loadDb = true;
+    private int maxDownloadForms = 10;
     private int cngstContCount = 2;
     private int maxCngstForms = 5;
+    private int bottomTrafficMax = 1;
+    private int bottomTrafficCycle = 0;
+    private boolean figureTrafficCenter = true;
+    private boolean textTrafficCenter = true;
+    private String figureTrafGrad1 = "원활";
+    private String figureTrafGrad2 = "지체";
+    private String figureTrafGrad3 = "정체";
+    private String textTrafGrad1 = "소통원활";
+    private String textTrafGrad2 = "지 체";
+    private String textTrafGrad3 = "정 체";
 
     @PostConstruct
     private void init() {
@@ -89,8 +100,8 @@ public class ApplicationConfig {
 
         this.bootingDateTime = SysUtils.getSysTimeStr();
 
-        if (this.maxPhase < 10) this.maxPhase = 10;
-        if (this.maxPhase > 20) this.maxPhase = 20;
+        if (this.maxDownloadForms < VmsConstants.VMS_MIN_DOWNLOAD_FORMS) this.maxDownloadForms = VmsConstants.VMS_MIN_DOWNLOAD_FORMS;
+        if (this.maxDownloadForms > VmsConstants.VMS_MAX_DOWNLOAD_FORMS) this.maxDownloadForms = VmsConstants.VMS_MAX_DOWNLOAD_FORMS;
 
         if (this.ftpHomeDir == null || this.ftpHomeDir.trim().length() == 0) {
             Path filePath = Paths.get(System.getProperty("user.dir"), "ftp");

+ 2 - 0
src/main/java/com/its/vms/dao/mapper/VmsFormMapper.java

@@ -1,6 +1,7 @@
 package com.its.vms.dao.mapper;
 
 import com.its.vms.entity.TbVmsForm;
+import com.its.vms.entity.TbVmsFormColr;
 import com.its.vms.entity.TbVmsFormObject;
 import org.apache.ibatis.annotations.Mapper;
 
@@ -11,4 +12,5 @@ public interface VmsFormMapper {
 
     List<TbVmsForm> selectVmsFormInfo();
     List<TbVmsFormObject> selectVmsFormObjectInfo();
+    List<TbVmsFormColr> selectVmsFormColorInfo();
 }

+ 3 - 4
src/main/java/com/its/vms/domain/VmsConstants.java

@@ -26,12 +26,11 @@ public class VmsConstants {
     public static int VMS_BASE_DEFAULT_ID = 8000;       // 기본 시나리오 ID
     public static int VMS_BASE_MANUAL_ID = 9000;        // 수동 시나리오 ID
 
-    public static int VMS_MAX_FORMS = 20;
-    public static int VMS_MAX_DOWNLOAD = 8;             // 최대 VMS 다운로드
-    public static int VMS_MAX_EVENT = 16;
-    public static int VMS_MAX_SCHEDULE = INT_VMS_MAX_FORM;
+    public static int VMS_MIN_DOWNLOAD_FORMS = 10;      // 한국도로공사 프로토콜 최대 폼 갯수
+    public static int VMS_MAX_DOWNLOAD_FORMS = 20;
 
     public static int VMS_MAX_EVEHICLE = 10;
     public static int VMS_MAX_CNGS_FORM = 5;
+    public static int VMS_MAX_FORM_OBJECTS = 64;        // 최대 VMS 오브젝트 리스트 갯수
 
 }

+ 149 - 0
src/main/java/com/its/vms/domain/VmsForm.java

@@ -0,0 +1,149 @@
+package com.its.vms.domain;
+
+import com.its.vms.dto.TbVmsCtlrDto;
+import com.its.vms.dto.TbVmsFormDto;
+import com.its.vms.dto.TbVmsScheduleDto;
+import lombok.Data;
+
+import java.awt.*;
+import java.awt.image.BufferedImage;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ *  DTO Class
+ */
+@Data
+public class VmsForm implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    private int     scenarioSeq;        // 폼 표출순서
+    private Integer vmsFormId;          // TB_VMS_FORM - VMS FORM ID
+    private int     width;              // 폼넓이
+    private int     height;             // 폼높이
+
+    private int     dsplHh;             // TB_VMS_FORM - 표출시간(초)
+    private int     vmsFormDsplDrctCd;  // TB_VMS_FORM - VMS 메시지 표출 방향 코드
+    private int     vmsFormDsplMthdCd;  // TB_VMS_FORM - VMS 메시지 표출 방법 코드
+    private int     vmsFormTypeCd;      // TB_VMS_FORM - 폼 종류
+    private int     vmsFormColrCd;      // TB_VMS_FORM - 배경색상
+
+    private Integer trfBakImgId;
+
+    private String  vmsSchType;
+    int             vmsSchFormType;
+    private String  symbLibNmbr;
+    private String  strmAddr;
+
+    private boolean success;
+    private boolean svcRes;             // 제공결과
+    private boolean ftpRes;             // 제공결과
+
+    private String  ftpFileName;
+    private String  localFileName;
+
+    // 프로토콜 적용을 위해서
+    private eVmsFileType fileType;      // 0x01 : 이미지 (JPG)
+                                        // 0x02 : 동영상 (AVI)
+                                        // 0x03 : URL(스트리밍)
+                                        // 0x04 : 소통 이미지
+                                        // 0x05 : 표출문구 이미지
+
+    private int     totalCount;
+    private int     objectCount;
+    private BufferedImage formImage;
+    private Graphics2D g2d;
+    private List<VmsFormObject> objects;
+
+    public VmsForm() {
+        this.totalCount = 0;
+        this.objectCount = 0;
+
+        this.success = false;
+        this.svcRes = false;
+        this.ftpRes = false;
+        this.trfBakImgId = 0;
+
+        this.ftpFileName = "";
+        this.localFileName = "";
+        this.formImage = null;
+        this.g2d = null;
+        this.objects = new ArrayList<>();
+    }
+
+    public void clear() {
+        this.svcRes = false;
+        this.ftpRes = false;
+        this.objectCount = 0;
+    }
+
+    public void init(int width, int height) {
+        this.width = width;
+        this.height = height;
+        this.formImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
+        this.g2d = (Graphics2D)formImage.createGraphics();
+        this.g2d.setColor(Color.BLACK);
+        this.g2d.fillRect(0, 0, this.width, this.height);
+    }
+    public void initDownload(TbVmsCtlrDto vmsObj, TbVmsFormDto vmsForm, TbVmsScheduleDto schedule) {
+        setSuccess(false);
+        setSvcRes(false);
+        setFtpRes(false);
+        setFtpFileName(vmsForm.getFtpFileName());
+        setLocalFileName(vmsForm.getLocalFileName());
+        setVmsSchType(schedule.getVmsSchType());
+        setVmsSchFormType(schedule.getVmsSchFormType());
+        setSymbLibNmbr(schedule.getSymbLibNmbr()+"0");
+        setStrmAddr(schedule.getStrmAddr());
+
+        setScenarioSeq(vmsObj.getFormManager().count());
+        setVmsFormId(vmsForm.getVmsFormId());
+        setDsplHh(schedule.getDsplHh());
+        setVmsFormDsplDrctCd(vmsForm.getVmsFormDsplDrctCd());
+        setVmsFormDsplMthdCd(vmsForm.getVmsFormDsplMthdCd());
+        setVmsFormTypeCd(vmsForm.getVmsFormTypeCd());
+        setVmsFormColrCd(vmsForm.getVmsFormColrCd());
+        setWidth(vmsObj.getVmsWidth());
+        setHeight(vmsObj.getVmsHeight());
+    }
+
+    public void formClear(Color bkColor) {
+        if (this.g2d != null) {
+            this.g2d.setColor(bkColor);
+            this.g2d.fillRect(0, 0, this.width, this.height);
+        }
+    }
+
+    public int count() { return this.objectCount; }
+    public int total() { return this.totalCount; }
+
+    public VmsFormObject getItem(int idx) {
+        if (this.objects.size() > idx) {
+            return this.objects.get(idx);
+        }
+        return null;
+    }
+
+    public int addFormObject(VmsFormObject formObj) {
+        this.objects.add(formObj);
+        this.objectCount = this.objects.size();
+        return this.objectCount;
+    }
+
+    public VmsFormObject addFormObject() {
+        VmsFormObject formObj = null;
+        if (this.objectCount < this.totalCount) {
+            formObj = getItem(this.objectCount);
+        }
+        else {
+            formObj = new VmsFormObject();
+            this.objects.add(formObj);
+            this.totalCount++;
+        }
+        this.objectCount++;
+        return formObj;
+    }
+
+}
+

+ 71 - 0
src/main/java/com/its/vms/domain/VmsFormManager.java

@@ -0,0 +1,71 @@
+package com.its.vms.domain;
+
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ *  DTO Class
+ */
+@Data
+public class VmsFormManager implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    private int formWidth;
+    private int formHeight;
+
+    private int totalCount;
+    private int objectCount;
+
+    private List<VmsForm> objects;
+
+    public VmsFormManager(int formWidth, int formHeight) {
+        this.formWidth = formWidth;
+        this.formHeight = formHeight;
+        this.totalCount = 0;
+        this.objectCount = 0;
+        this.objects = new ArrayList<>();
+    }
+
+    public void clear() {
+        this.objectCount = 0;
+        this.objects.forEach(obj -> {
+            obj.clear();
+        });
+    }
+
+    public VmsForm getItem(int idx) {
+        if (this.objects.size() > idx) {
+            return this.objects.get(idx);
+        }
+        return null;
+    }
+
+    public int addForm(VmsForm form) {
+        this.objects.add(form);
+        this.objectCount = this.objects.size();
+        return this.objectCount;
+    }
+
+    public VmsForm addForm() {
+        VmsForm form = null;
+        if (this.objectCount < this.totalCount) {
+            form = getItem(this.objectCount);
+        }
+        else {
+            form = new VmsForm();
+            form.init(this.formWidth, this.formHeight);
+            this.objects.add(form);
+            this.totalCount++;
+        }
+        this.objectCount++;
+        return form;
+    }
+
+    public int count() { return this.objectCount; }
+    public int total() { return this.totalCount; }
+
+}
+

+ 112 - 0
src/main/java/com/its/vms/domain/VmsFormObject.java

@@ -0,0 +1,112 @@
+package com.its.vms.domain;
+
+import com.its.vms.dto.TbVmsFormObjectDto;
+import lombok.Data;
+import lombok.extern.slf4j.Slf4j;
+
+import java.awt.*;
+import java.awt.geom.Rectangle2D;
+import java.awt.image.BufferedImage;
+import java.io.Serializable;
+
+/**
+ *  DTO Class
+ */
+@Slf4j
+@Data
+public class VmsFormObject implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+
+    private int    objectKind;      // 객체종류
+    private int    objectType;      // 객체타입
+    private int    objectSize;      // 객체크기
+    private int    isBlinking;      // 점멸 여부
+    private int    posX;            // 위치 X
+    private int    posY;            // 위치 Y
+    private int    dsplWidth;       // 객체넓이
+    private int    textWidth;       // 객체넓이
+    private int    dsplHeight;      // 객체높이
+    private int    bkColor;         // 객체배경색상
+    private int    fontNameCd;      // 폰트이름
+
+    private String fontName;
+    private int    fontColor;       // 폰트색상
+    private int    fontSize;        // 폰트크기
+    private int    fontBold;        // 폰트굵기
+    private int    textAlign;       // 문자열정열방식
+    private String textData;        // 문자열데이터
+    private String imageId;         // 이미지아이디
+    private String imageType;       // 이미지 타입
+
+    private Long   ifscId;
+    private int    ifscTrafGradCd;
+    private String  trfcFillCd;
+
+    private byte[] imageData;
+    private BufferedImage formImage;
+    private Graphics2D g2d;
+
+    public VmsFormObject() {
+        this.ifscId = 0L;
+        this.formImage = null;
+        this.g2d = null;
+    }
+
+    public void clear() {
+        // Do nothing.
+    }
+
+    public Graphics2D newImage() {
+        return this.g2d;
+    }
+
+    public void setFontInfo(String fontName, TbVmsFormObjectDto obj) {
+        this.ifscTrafGradCd = 0;
+        this.fontName   = fontName;
+        this.objectType = obj.getVmsFormObjectTypeCd();     // NUMBER(3)	N			VMS FORM OBJECT 유형 코드
+        this.fontNameCd = obj.getVmsFontNameCd();           // NUMBER(3)	N			VMS 글꼴 유형 코드
+        this.fontColor  = obj.getVmsFontColrCd();           // NUMBER(3)	N			VMS 글꼴 색상 코드
+        this.fontBold   = obj.getVmsFontBold();             // NUMBER(3)	Y			VMS 글꼴 방식 코드(0:Normal,1:Bold)
+        this.fontSize   = obj.getVmsFontSize();             // NUMBER(3)	Y	0		VMS 글꼴 크기
+        this.textAlign  = obj.getVmsFontAlign();            // NUMBER(1)	Y	0		VMS 표출 문자열정열방식(0:LEFT,1:RIGHT,2:CENTER)
+        this.posX       = obj.getVmsDsplXcrdn();            // NUMBER(5)	Y			VMS 표출 X좌표
+        this.posY       = obj.getVmsDsplYcrdn();            // NUMBER(5)	Y			VMS 표출 Y좌표
+        this.dsplWidth = obj.getVmsDsplWidth();             // NUMBER(5)	Y	0		VMS 표출 넓이
+        this.textWidth  = obj.getVmsDsplWidth();            // NUMBER(5)	Y	0		VMS 표출 넓이
+        this.dsplHeight = obj.getVmsDsplHeight();           // NUMBER(5)	Y	0		VMS 표출 높이
+        this.isBlinking = obj.getVmsDsplBlinking();         // NUMBER(1)	Y	0		VMS 표출 점멸 여부(0:지속, 1:점멸)
+        this.bkColor    = obj.getVmsDsplBkColor();          // NUMBER(1)	Y	0		VMS 표출 배경색상코드
+        this.objectSize = obj.getVmsDsplSize();             // NUMBER(7)	Y	0		VMS 표출 크기(문자:문자길이,이미지:이미지전체크기)
+    }
+
+    public void changeTextPosition() {
+        if (this.textAlign == 0 || "".equals(this.textData)) {
+            // 좌측정렬 또는 표출데이터가 없는 경우
+            return;
+        }
+        log.info("changeTextPosition.before: font {}, {}, x {}, width {}, textWidth {}.", this.fontName, this.textData, this.posX, this.dsplWidth, this.textWidth);
+        int style = this.fontBold == 0 ? Font.PLAIN : Font.BOLD;
+        Font font = new Font(this.fontName, style, this.fontSize);
+        font = font.deriveFont(this.fontSize * 1.35f);
+        FontMetrics metrics = new FontMetrics(font){};
+        Rectangle2D bounds = metrics.getStringBounds(this.textData, null);
+        int textWidth = (int) bounds.getWidth();
+        int correctWidth = this.dsplWidth - textWidth;
+        if (this.textAlign == 1) {
+            // 우측정렬: 좌측에서 글자길이를 뺀다음 만약 음수이면 0으로 설정
+            //this.posX = (this.posX + this.dsplWidth) - textWidth;
+            this.posX += correctWidth;
+        }
+        else {
+            // 가운데정렬: 원래 글자의 중앙값을 얻은 다음에 조정된 글자의 1/2 크기를 뺀다음 음수이면 0으로 설정
+            //this.posX = (this.posX+(this.dsplWidth /2)) - (textWidth/2);
+            this.posX += (correctWidth / 2);
+        }
+        if (this.posX < 0) {
+            this.posX = 0;
+        }
+        log.info("changeTextPosition..after: font {}, {}, x {}, width {}, textWidth {}, {}.", this.fontName, this.textData, this.posX, this.dsplWidth, this.textWidth, textWidth);
+        this.textWidth = this.dsplWidth;
+    }
+}

+ 44 - 0
src/main/java/com/its/vms/domain/eVmsFileType.java

@@ -0,0 +1,44 @@
+package com.its.vms.domain;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public enum eVmsFileType {
+
+    vms_file_image         ((byte)0x01, "0x01 : 이미지 (JPG)"),
+    vms_file_avi           ((byte)0x02, "0x02 : 동영상 (AVI)"),
+    vms_file_stream_url    ((byte)0x03, "0x03 : URL(스트리밍)"),
+    vms_file_traffic_image ((byte)0x04, "0x04 : 소통정보 이미지"),
+    vms_file_dspl_image    ((byte)0x05, "0x05 : 표출문구 이미지");
+
+    private final byte value;
+    private final String string;
+
+    private static final Map<Integer, eVmsFileType> map;
+    static {
+        map = new HashMap<>();
+        for (eVmsFileType e : values()) {
+            map.put(Integer.valueOf(e.value), e);
+        }
+    }
+
+    public static eVmsFileType getValue(int value) {
+        return map.get(value);
+    }
+    public static eVmsFileType getValue(byte value) {
+        int intValue = (value & 0x0F);
+        return map.get(intValue);
+    }
+
+    eVmsFileType(byte value, String string) {
+        this.value  = value;
+        this.string = string;
+    }
+
+    public byte getValue() {
+        return this.value;
+    }
+    public String toString() {
+        return this.string;
+    }
+}

+ 16 - 16
src/main/java/com/its/vms/domain/eVmsFormType.java

@@ -5,22 +5,22 @@ import java.util.Map;
 
 public enum eVmsFormType {
 
-    eFormTp_traf_1   ( 11,  "소통상황(1단)"),
-    eFormTp_traf_2   ( 12,  "소통상황(2단)"),
-    eFormTp_traf_3   ( 13,  "소통상황(3단)"),
-    eFormTp_traf_4   ( 14,  "소통상황(4단)"),
-    eFormTp_figure   ( 15,  "소통상황(도형식배경소통정보)"),
-    eFormTp_congest  ( 16,  "정체소통상황"),
-    eFormTp_incident ( 20,  "돌발문안"),
-    eFormTp_gongsa   ( 30,  "공사/행사문안"),
-    eFormTp_hongbo   ( 40,  "홍보문안"),
-    eFormTp_deture   ( 50,  "우회도로"),
-    eFormTp_safe     ( 60,  "재난안전"),
-    eFormTp_video    ( 70,  "동영상"),
-    eFormTp_stream   ( 80,  "스트리밍영상"),
-    eFormTp_atmp     ( 90,  "대기환경"),
-    eFormTp_park     ( 100, "주차정보"),
-    eFormTp_evehicle ( 110, "긴급차량우선신호");
+    eFormTp_traf_1   ( 11,  "11.소통상황(1단)"),
+    eFormTp_traf_2   ( 12,  "12.소통상황(2단)"),
+    eFormTp_traf_3   ( 13,  "13.소통상황(3단)"),
+    eFormTp_traf_4   ( 14,  "14.소통상황(4단)"),
+    eFormTp_figure   ( 15,  "15.소통상황(도형식배경소통정보)"),
+    eFormTp_congest  ( 16,  "16.정체소통상황"),
+    eFormTp_incident ( 20,  "20.돌발문안"),
+    eFormTp_gongsa   ( 30,  "30.공사/행사문안"),
+    eFormTp_hongbo   ( 40,  "40.홍보문안"),
+    eFormTp_deture   ( 50,  "50.우회도로"),
+    eFormTp_safe     ( 60,  "60.재난안전"),
+    eFormTp_video    ( 70,  "70.동영상"),
+    eFormTp_stream   ( 80,  "80.스트리밍영상"),
+    eFormTp_atmp     ( 90,  "90.대기환경"),
+    eFormTp_park     ( 100, "100.주차정보"),
+    eFormTp_evehicle ( 110, "110.긴급차량우선신호");
 
     private final int value;
     private final String string;

+ 19 - 41
src/main/java/com/its/vms/dto/TbVmsAtmpDto.java

@@ -99,29 +99,29 @@ public class TbVmsAtmpDto implements Serializable {
         obj.setSuccess(this.isSuccess);
     }
 
-    public String getPM10_VAL(boolean AUnit) {
+    public String getPm10ValDesc(String unitGap, boolean AUnit) {
         // 미세먼지
         if (this.isSuccess) {
             if (AUnit)
-                return this.pm10Val + " ㎍/㎥";
+                return this.pm10Val + unitGap + "㎍/㎥";
             else
                 return this.pm10Val;
         }
-        return (AUnit) ? "- ㎍/㎥" : "-";
+        return (AUnit) ? "-" + unitGap + "㎍/㎥" : "-";
     }
 
-    public String getPM25_VAL(boolean AUnit) {
+    public String getPm25ValDesc(String unitGap, boolean AUnit) {
         //초미세먼지
         if (this.isSuccess) {
             if (AUnit)
-                return this.pm25Val + " ㎍/㎥";
+                return this.pm25Val + unitGap + "㎍/㎥";
             else
                 return this.pm25Val;
         }
-        return (AUnit) ? "- ㎍/㎥" : "-";
+        return (AUnit) ? "-" + unitGap + "㎍/㎥" : "-";
     }
 
-    public String getATMP_VAL() {
+    public String getAtmpValDesc() {
         //통합대기환경수치
         if (this.isSuccess) {
             return this.intgAtmpVal;
@@ -129,15 +129,15 @@ public class TbVmsAtmpDto implements Serializable {
         return "-";
     }
 
-    public String GetO3_VAL(boolean AUnit) {
+    public String getO3ValDesc(String unitGap, boolean AUnit) {
         //오존농도
         if (this.isSuccess) {
             if (AUnit)
-                return this.o3Val + " ppm";
+                return this.o3Val + unitGap + "ppm";
             else
                 return this.o3Val;
         }
-        return (AUnit) ? "- ppm" : "-";
+        return (AUnit) ? "-" + unitGap + "ppm" : "-";
     }
 
     public String getGradeDesc(int AGrad) {
@@ -152,27 +152,10 @@ public class TbVmsAtmpDto implements Serializable {
         return "-";
     }
 
-    public String getPM10_GRAD() {
-        return getGradeDesc(ItsUtils.parseIntDef(this.pm101hhGrad, 0));
-    }
-
-    public String getPM25_GRAD() {
-        return getGradeDesc(ItsUtils.parseIntDef(this.pm251hhGrad, 0));
-    }
-
-    public String getATMP_GRAD() {
-        return getGradeDesc(ItsUtils.parseIntDef(this.intgAtmpGrad, 0));
-    }
-
-    public String getO3_GRAD() {
-        return getGradeDesc(ItsUtils.parseIntDef(this.o3Grad, 0));
-    }
-
     public int getGradeColor(int AGrad) {
         if (!this.isSuccess) return 3;//Color.GREEN.getRGB();
 
-        switch(AGrad)
-        {
+        switch(AGrad) {
             case 1: return 2;//밝은녹색(Lime)
             case 2: return 5;//하늘색(Aqua)
             case 3: return 4;//주황색(Orange)
@@ -180,21 +163,16 @@ public class TbVmsAtmpDto implements Serializable {
         }
         return 3;
     }
+    public String getPm10GradDesc() { return getGradeDesc(ItsUtils.parseIntDef(this.pm101hhGrad, 0)); }
+    public String getPm25GradDesc() { return getGradeDesc(ItsUtils.parseIntDef(this.pm251hhGrad, 0)); }
+    public String getAtmpGradDesc() { return getGradeDesc(ItsUtils.parseIntDef(this.intgAtmpGrad, 0)); }
+    public String getO3GradDesc() { return getGradeDesc(ItsUtils.parseIntDef(this.o3Grad, 0)); }
 
-    public int getPM10_CLR() {
-        return getGradeColor(ItsUtils.parseIntDef(this.pm101hhGrad, 0));
-    }
-
-    public int getPM25_CLR() {
-        return getGradeColor(ItsUtils.parseIntDef(this.pm251hhGrad, 0));
-    }
-
-    public int getATMP_CLR() {
+    public int getPm10Color() { return getGradeColor(ItsUtils.parseIntDef(this.pm101hhGrad, 0)); }
+    public int getPm25Color() { return getGradeColor(ItsUtils.parseIntDef(this.pm251hhGrad, 0)); }
+    public int getAtmpColor() {
         return getGradeColor(ItsUtils.parseIntDef(this.intgAtmpGrad, 0));
     }
-
-    public int getO3_CLR() {
-        return getGradeColor(ItsUtils.parseIntDef(this.o3Grad, 0));
-    }
+    public int getO3Color()   { return getGradeColor(ItsUtils.parseIntDef(this.o3Grad, 0)); }
 
 }

+ 2 - 2
src/main/java/com/its/vms/dto/TbVmsCtlrDto.java

@@ -93,7 +93,7 @@ public class TbVmsCtlrDto implements Serializable {
     private boolean evehIngForm;
     private boolean evehEndForm;
 
-    private List<TbVmsFormDto> forms;
+    private VmsFormManager formManager;
 
     /**
      * VMS 메시지 생성 메모리
@@ -139,7 +139,7 @@ public class TbVmsCtlrDto implements Serializable {
         this.userCommands = new ConcurrentHashMap<>();
         this.rltnIfscMap = new ConcurrentHashMap<>();
 
-        this.forms = new ArrayList<>();
+        this.formManager = new VmsFormManager(this.vmsWidth, this.vmsHeight);
         this.evechile = new VmsEvehicle();
         this.schedule = new VmsSchedule();   // 스케쥴 정보
         this.incident = new VmsEvent();      // 돌발정보

+ 21 - 0
src/main/java/com/its/vms/dto/TbVmsFontColorDto.java

@@ -0,0 +1,21 @@
+package com.its.vms.dto;
+
+import lombok.Builder;
+import lombok.Data;
+
+import java.awt.*;
+import java.io.Serializable;
+
+/**
+ *  DTO Class
+ */
+@Data
+@Builder
+public class TbVmsFontColorDto implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    private Integer vmsFontColrCd;
+    private String vmsFontColrNm;
+    private Color vmsFontColor;
+
+}

+ 2 - 12
src/main/java/com/its/vms/dto/TbVmsFontColrDto.java

@@ -1,10 +1,9 @@
 package com.its.vms.dto;
 
-import com.its.vms.entity.TbVmsFontColr;
-import io.swagger.annotations.ApiModel;
 import lombok.Builder;
 import lombok.Data;
 
+import java.awt.*;
 import java.io.Serializable;
 
 /**
@@ -18,15 +17,6 @@ public class TbVmsFontColrDto implements Serializable {
     private Integer vmsFontColrCd;
     private String vmsFontColrNm;
     private Long vmsFontColrVal;
-    private String useYn;
-
-    public TbVmsFontColr toEntity() {
-        return TbVmsFontColr.builder()
-                .vmsFontColrCd(this.vmsFontColrCd)
-                .vmsFontColrNm(this.vmsFontColrNm)
-                .vmsFontColrVal(this.vmsFontColrVal)
-                .useYn(this.useYn)
-                .build();
-    }
+    private Color vmsFontColor;
 
 }

+ 21 - 0
src/main/java/com/its/vms/dto/TbVmsFormColrDto.java

@@ -0,0 +1,21 @@
+package com.its.vms.dto;
+
+import lombok.Builder;
+import lombok.Data;
+
+import java.awt.*;
+import java.io.Serializable;
+
+/**
+ *  DTO Class
+ */
+@Data
+@Builder
+public class TbVmsFormColrDto implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    private Integer vmsFormColrCd;
+    private String vmsFormColrNm;
+    private Color vmsFormColor;
+
+}

+ 6 - 6
src/main/java/com/its/vms/dto/TbVmsFormDto.java

@@ -31,14 +31,14 @@ public class TbVmsFormDto implements Serializable {
 
     private String localFileName;
     private String ftpFileName;
-    private Integer trfBackImgId;
+    private String trfBackImgId;
 
-    private boolean isTrfIfsc;     //도형식 하단 정보제공구간소통정보
-    private boolean isTrfAxis;     //도형식 하단 축소통정보
-    private boolean isTrfFixed;    //도형식 하단에 소통정보가 없을 경우 기본문자 표출여부, 20200515 추가함
-    private boolean isIsNewForm;   //신규이거나 변경된 경우 true
+    private boolean isBottomTrfIfsc;    // 도형식 하단 정보제공구간소통정보
+    private boolean isBottomTrfAxis;    // 도형식 하단 축소통정보
+    private boolean isBottomTrfFixed;   // 도형식 하단에 소통정보가 없을 경우 기본문자 표출여부, 20200515 추가함
+    private boolean isNewForm;          // 신규이거나 변경된 경우 true
 
-    private List<TbVmsFormObjectDto> objs;
+    private List<TbVmsFormObjectDto> objects;
 
     public TbVmsForm toEntity() {
         return TbVmsForm.builder()

+ 1 - 2
src/main/java/com/its/vms/dto/TbVmsFormObjectDto.java

@@ -22,7 +22,6 @@ public class TbVmsFormObjectDto implements Serializable {
     private Integer vmsFontNameCd;
     private Integer vmsFontColrCd;
     private String vmsDsplTxt;
-    private byte[] vmsDsplFigr;
     private Integer vmsDsplXcrdn;
     private Integer vmsDsplYcrdn;
     private String trfcFillCd;
@@ -38,6 +37,7 @@ public class TbVmsFormObjectDto implements Serializable {
     private Long vmsIfscId;
 
     private TbVmsSymbLibDto vmsSymbLib;
+    private byte[] imagData;
 
     public TbVmsFormObject toEntity() {
         return TbVmsFormObject.builder()
@@ -49,7 +49,6 @@ public class TbVmsFormObjectDto implements Serializable {
                 .vmsFontNameCd(this.vmsFontNameCd)
                 .vmsFontColrCd(this.vmsFontColrCd)
                 .vmsDsplTxt(this.vmsDsplTxt)
-                .vmsDsplFigr(this.vmsDsplFigr)
                 .vmsDsplXcrdn(this.vmsDsplXcrdn)
                 .vmsDsplYcrdn(this.vmsDsplYcrdn)
                 .trfcFillCd(this.trfcFillCd)

+ 1 - 19
src/main/java/com/its/vms/dto/TbVmsIfscTrafDto.java

@@ -1,6 +1,5 @@
 package com.its.vms.dto;
 
-import com.its.vms.entity.TbVmsIfscTraf;
 import lombok.AllArgsConstructor;
 import lombok.Builder;
 import lombok.Data;
@@ -31,28 +30,11 @@ public class TbVmsIfscTrafDto implements Serializable {
 
     // 교통정보
     private String prcnDt;
-    private String cmtrGradCd;
+    private Integer cmtrGradCd;
     private Integer sped;
     private Integer trvlHh;
 
     private Integer cngstCnt;
     private String cngstDt;
 
-    public TbVmsIfscTraf toEntity() {
-        return TbVmsIfscTraf.builder()
-                .vmsIfscId(this.vmsIfscId)
-                .vmsIfscNm(this.vmsIfscNm)
-                .dsplStrtNodeNm(this.dsplStrtNodeNm)
-                .dsplEndNodeNm(this.dsplEndNodeNm)
-                .detrId(this.detrId)
-                .roadNm(this.roadNm)
-                .spotNm(this.spotNm)
-                .axisYn(this.axisYn)
-                .prcnDt(this.prcnDt)
-                .cmtrGradCd(this.cmtrGradCd)
-                .sped(this.sped)
-                .trvlHh(this.trvlHh)
-                .build();
-    }
-
 }

+ 57 - 0
src/main/java/com/its/vms/dto/TbVmsParkDto.java

@@ -1,5 +1,6 @@
 package com.its.vms.dto;
 
+import com.its.vms.domain.eVmsColor;
 import lombok.AllArgsConstructor;
 import lombok.Builder;
 import lombok.Data;
@@ -45,4 +46,60 @@ public class TbVmsParkDto implements Serializable {
                 break;
         }
     }
+
+    public int getGradeColorCd()
+    {
+        if (!this.isSuccess) {
+            return eVmsColor.color_amber.getValue();//황색(Yellow)
+        }
+        switch(this.parkingCgsStts) {
+            case 0:
+            case 1:  return eVmsColor.color_green.getValue();   // 밝은녹색(Lime)
+            case 3:  return eVmsColor.color_red.getValue();     // 적색(Red)
+            //case 2:  return eVmsColor.color_amber.getValue();   // 황색(Yellow)
+            default: return eVmsColor.color_amber.getValue();
+        }
+    }
+
+    public String getParkName() {
+        return this.vmsDsplNm;
+    }
+
+    public String getCountText(String ATxt, int ACount) {
+        if (!this.isSuccess) {
+            return "  ";
+        }
+
+        boolean isSpace = ATxt.contains(" ");
+        String sType = "";
+
+        if (ATxt.contains("대")) {
+            sType = "대";
+        }
+        if (ATxt.contains("면")) {
+            sType = "면";
+        }
+
+        if (!"".equals(sType)) {
+            if (isSpace) {
+                return ACount + " " + sType;
+            }
+            return ACount + sType;
+        }
+        return String.valueOf(ACount);
+    }
+    
+    public String getRemainCo(String ATxt) {
+        return getCountText(ATxt, this.remndrPrkCmprtCo);
+    }
+    
+    public String getTotalCo(String ATxt) {
+        return getCountText(ATxt, this.regPrkCmprtCo);
+    }
+    
+    public String getCgsSttsDesc() {
+        return this.parkingCngstSttsDesc;
+    }
+
+
 }

+ 1 - 12
src/main/java/com/its/vms/dto/TbVmsSymbIfscDto.java

@@ -1,6 +1,5 @@
 package com.its.vms.dto;
 
-import com.its.vms.entity.TbVmsSymbIfsc;
 import lombok.Builder;
 import lombok.Data;
 
@@ -21,17 +20,7 @@ public class TbVmsSymbIfscDto implements Serializable {
     private Long vmsIfscId;
     private String updtDt;
 
+    private boolean isValid;
     private boolean isDup;
 
-    public TbVmsSymbIfsc toEntity() {
-        return TbVmsSymbIfsc.builder()
-                .symbLibNmbr(this.symbLibNmbr)
-                .cellId(this.cellId)
-                .posX(this.posX)
-                .posY(this.posY)
-                .vmsIfscId(this.vmsIfscId == null ? 0 : this.vmsIfscId)
-                .updtDt(this.updtDt)
-                .build();
-    }
-
 }

+ 5 - 5
src/main/java/com/its/vms/dto/TbVmsSymbLibDto.java

@@ -30,7 +30,7 @@ public class TbVmsSymbLibDto implements Serializable {
     private byte[] redData;
     private byte[] greenData;
     private String symbExpl;
-    private byte[] imagData;
+    private byte[] imageData;
     private String vmsTypeCd;
     private String symbImagType;
     private String symbFileNm;
@@ -59,7 +59,7 @@ public class TbVmsSymbLibDto implements Serializable {
                 .redData(this.redData)
                 .greenData(this.greenData)
                 .symbExpl(this.symbExpl)
-                .imagData(this.imagData)
+                .imagData(this.imageData)
                 .vmsTypeCd(this.vmsTypeCd)
                 .symbImagType(this.symbImagType)
                 .symbFileNm(this.symbFileNm)
@@ -103,13 +103,13 @@ public class TbVmsSymbLibDto implements Serializable {
     }
 
     public Image getImage() {
-        if (this.imagData == null || this.imagData.length == 0) {
+        if (this.imageData == null || this.imageData.length == 0) {
             log.error("getImage: Image Data null: {}", this.orgSymbLibNmbr);
             return null;
         }
 
         try (
-                ByteArrayInputStream bis = new ByteArrayInputStream(this.imagData);
+                ByteArrayInputStream bis = new ByteArrayInputStream(this.imageData);
         ) {
             return ImageIO.read(bis);
         } catch (IOException e) {
@@ -126,7 +126,7 @@ public class TbVmsSymbLibDto implements Serializable {
                 .redData(this.redData)
                 .greenData(this.greenData)
                 .symbExpl(this.symbExpl)
-                .imagData(this.imagData)
+                .imageData(this.imageData)
                 .vmsTypeCd(this.vmsTypeCd)
                 .symbImagType(this.symbImagType)
                 .symbFileNm(this.symbFileNm)

+ 2 - 2
src/main/java/com/its/vms/entity/TbVmsFontColr.java

@@ -6,6 +6,7 @@ import lombok.Builder;
 import lombok.Getter;
 import lombok.NoArgsConstructor;
 
+import java.awt.*;
 import java.io.Serializable;
 
 /**
@@ -21,14 +22,13 @@ public class TbVmsFontColr implements Serializable {
     private Integer vmsFontColrCd;
     private String vmsFontColrNm;
     private Long vmsFontColrVal;
-    private String useYn;
 
     public TbVmsFontColrDto toDto() {
         return TbVmsFontColrDto.builder()
                 .vmsFontColrCd(this.vmsFontColrCd)
                 .vmsFontColrNm(this.vmsFontColrNm)
                 .vmsFontColrVal(this.vmsFontColrVal)
-                .useYn(this.useYn)
+                .vmsFontColor(new Color(this.vmsFontColrVal & 0xFF, (this.vmsFontColrVal & 0xFF00) >> 8, (this.vmsFontColrVal & 0xFF0000) >> 16))
                 .build();
     }
 }

+ 6 - 6
src/main/java/com/its/vms/entity/TbVmsForm.java

@@ -50,12 +50,12 @@ public class TbVmsForm implements Serializable {
                 .validYn(this.validYn)
                 .localFileName("")
                 .ftpFileName("")
-                .trfBackImgId(0)
-                .isTrfIfsc(false)     //도형식 하단 정보제공구간소통정보
-                .isTrfAxis(false)     //도형식 하단 축소통정보
-                .isTrfFixed(false)    //도형식 하단에 소통정보가 없을 경우 기본문자 표출여부, 20200515 추가함
-                .isIsNewForm(false)   //신규이거나 변경된 경우 true
-                .objs(new ArrayList<>())
+                .trfBackImgId("0")
+                .isBottomTrfIfsc(false)     //도형식 하단 정보제공구간소통정보
+                .isBottomTrfAxis(false)     //도형식 하단 축소통정보
+                .isBottomTrfFixed(false)    //도형식 하단에 소통정보가 없을 경우 기본문자 표출여부, 20200515 추가함
+                .isNewForm(false)   //신규이거나 변경된 경우 true
+                .objects(new ArrayList<>())
                 .build();
     }
 }

+ 33 - 0
src/main/java/com/its/vms/entity/TbVmsFormColr.java

@@ -0,0 +1,33 @@
+package com.its.vms.entity;
+
+import com.its.vms.dto.TbVmsFormColrDto;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.awt.*;
+import java.io.Serializable;
+
+/**
+ *  Entity Class
+ */
+@Data
+@Builder
+@NoArgsConstructor//(access = AccessLevel.PROTECTED)
+@AllArgsConstructor
+public class TbVmsFormColr implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    private Integer vmsFormColrCd;
+    private String vmsFormColrNm;
+    private Integer vmsFormColrVal;
+
+    public TbVmsFormColrDto toDto() {
+        return TbVmsFormColrDto.builder()
+                .vmsFormColrCd(this.vmsFormColrCd)
+                .vmsFormColrNm(this.vmsFormColrNm)
+                .vmsFormColor(new Color(this.vmsFormColrVal & 0xFF, (this.vmsFormColrVal & 0xFF00) >> 8, (this.vmsFormColrVal & 0xFF0000) >> 16))
+                .build();
+    }
+}

+ 1 - 2
src/main/java/com/its/vms/entity/TbVmsFormObject.java

@@ -26,7 +26,6 @@ public class TbVmsFormObject implements Serializable {
     private Integer vmsFontNameCd;
     private Integer vmsFontColrCd;
     private String vmsDsplTxt;
-    private byte[] vmsDsplFigr;
     private Integer vmsDsplXcrdn;
     private Integer vmsDsplYcrdn;
     private String trfcFillCd;
@@ -51,7 +50,6 @@ public class TbVmsFormObject implements Serializable {
                 .vmsFontNameCd(this.vmsFontNameCd)
                 .vmsFontColrCd(this.vmsFontColrCd)
                 .vmsDsplTxt(this.vmsDsplTxt)
-                .vmsDsplFigr(this.vmsDsplFigr)
                 .vmsDsplXcrdn(this.vmsDsplXcrdn)
                 .vmsDsplYcrdn(this.vmsDsplYcrdn)
                 .trfcFillCd(this.trfcFillCd)
@@ -66,6 +64,7 @@ public class TbVmsFormObject implements Serializable {
                 .symbLibNmbr(this.symbLibNmbr)
                 .vmsIfscId(this.vmsIfscId)
                 .vmsSymbLib(null)
+                .imagData(null)
                 .build();
     }
 

+ 2 - 1
src/main/java/com/its/vms/entity/TbVmsIfscTraf.java

@@ -1,5 +1,6 @@
 package com.its.vms.entity;
 
+import com.its.app.utils.ItsUtils;
 import com.its.vms.dto.TbVmsIfscTrafDto;
 import lombok.AllArgsConstructor;
 import lombok.Builder;
@@ -43,7 +44,7 @@ public class TbVmsIfscTraf implements Serializable {
                 .spotNm(this.spotNm)
                 .axisYn(this.axisYn)
                 .prcnDt(this.prcnDt)
-                .cmtrGradCd(this.cmtrGradCd)
+                .cmtrGradCd(ItsUtils.parseIntDef(this.cmtrGradCd, 0))
                 .sped(this.sped)
                 .trvlHh(this.trvlHh)
                 .cngstCnt(0)

+ 1 - 0
src/main/java/com/its/vms/entity/TbVmsSymbIfsc.java

@@ -34,6 +34,7 @@ public class TbVmsSymbIfsc implements Serializable {
                 .vmsIfscId(this.vmsIfscId == null ? 0 : this.vmsIfscId)
                 .updtDt(this.updtDt)
                 .isDup(false)
+                .isValid(true)
                 .build();
     }
 

+ 1 - 1
src/main/java/com/its/vms/entity/TbVmsSymbLib.java

@@ -47,7 +47,7 @@ public class TbVmsSymbLib implements Serializable {
                 .redData(this.redData)
                 .greenData(this.greenData)
                 .symbExpl(this.symbExpl)
-                .imagData(this.imagData)
+                .imageData(this.imagData)
                 .vmsTypeCd(this.vmsTypeCd)
                 .symbImagType(this.symbImagType)
                 .symbFileNm(this.symbFileNm)

+ 9 - 0
src/main/java/com/its/vms/service/AppRepositoryService.java

@@ -20,6 +20,15 @@ public class AppRepositoryService {
     private final ConcurrentHashMap<String, TbVmsCtlrDto> ipAddrMap = new ConcurrentHashMap<>();;
     private final ConcurrentHashMap<Channel, TbVmsCtlrDto> channelMap = new ConcurrentHashMap<>();;
 
+    private boolean isStaticCycle = false;
+
+    public void setStaticCycle(boolean isStaticCycle) {
+        this.isStaticCycle = isStaticCycle;
+    }
+    public boolean isStaticCycle() {
+        return this.isStaticCycle;
+    }
+
     public void initCtlrInfo() {
         try {
             for (Map.Entry<Long, TbVmsCtlrDto> e : this.ctlrMap.entrySet()) {

+ 3 - 0
src/main/java/com/its/vms/service/VmsAtmpService.java

@@ -1,5 +1,6 @@
 package com.its.vms.service;
 
+import com.its.app.utils.Elapsed;
 import com.its.vms.dao.mapper.VmsAtmpMapper;
 import com.its.vms.dto.TbVmsAtmpDto;
 import com.its.vms.entity.TbVmsAtmp;
@@ -30,6 +31,7 @@ public class VmsAtmpService {
     }
 
     public void loadVmsAtmpInfo() {
+        Elapsed elapsed = new Elapsed();
         AtomicInteger nFailed = new AtomicInteger();
         AtomicInteger nSuccess = new AtomicInteger();
         try {
@@ -62,6 +64,7 @@ public class VmsAtmpService {
             // 성공 데이터가 존재하고 실패 데이터가 존재하는 경우 데이터 보정
             correctVmsAtmpInfo();
         }
+        log.info("VmsAtmpService.loadVmsAtmpInfo: {} ms.", elapsed.milliSeconds());
     }
 
     public void correctVmsAtmpInfo() {

+ 20 - 10
src/main/java/com/its/vms/service/VmsCtlrService.java

@@ -1,6 +1,7 @@
 package com.its.vms.service;
 
 import com.its.app.AppUtils;
+import com.its.app.utils.Elapsed;
 import com.its.app.utils.SysUtils;
 import com.its.vms.config.ApplicationConfig;
 import com.its.vms.dao.mapper.VmsCtlrMapper;
@@ -44,6 +45,7 @@ public class VmsCtlrService {
     }
 
     public void loadCtlrInfo() {
+        Elapsed elapsed = new Elapsed();
         this.repoService.initCtlrInfo();
 
         try {
@@ -51,21 +53,26 @@ public class VmsCtlrService {
             log.info("VmsCtlrService.loadCtlrInfo: {} EA", infoList.size());
             for (TbVmsCtlr dto : infoList) {
                 log.info("VmsCtlrService.loadCtlrInfo: {}", dto);
-                TbVmsCtlrDto obj = dto.toDto();
-                if (obj.getCtlrId().trim().equals("")) {
-                    log.error("VmsCtlrService.loadCtlrInfo: Controller Ip Address Error: {}", obj);
+                TbVmsCtlrDto vmsObj = dto.toDto();
+                //TbVmsCtlrDto oldObj = this.repoService.getCtrlMap(vmsObj.getCtlrNmbr());
+                if (vmsObj.getCtlrId().trim().equals("")) {
+                    log.error("VmsCtlrService.loadCtlrInfo: Controller Ip Address Error: {}", vmsObj);
                     continue;
                 }
 
-                this.repoService.putCtrlMap(obj);
-                this.repoService.putIpAddrMap(obj);
-                obj.getStts().initUnknown();
+                this.repoService.putCtrlMap(vmsObj);
+                this.repoService.putIpAddrMap(vmsObj);
+                vmsObj.getStts().initUnknown();
 
-                obj.setLocalFormDir(this.config.getFtpFormDir() + File.separator + obj.getCtlrNmbr());
-                obj.setFtpFormDir(ApplicationConfig.FTP_FORM + File.separator + obj.getCtlrNmbr() + File.separator);
-                obj.setFtpDownload(true);
+                if (vmsObj.getMaxPhaseNum() > this.config.getMaxDownloadForms()) {
+                    log.error("VmsCtlrService.loadCtlrInfo: VMS {} Download Form Size Error: {}/{}", vmsObj.getCtlrNmbr(), vmsObj.getMaxPhaseNum(), this.config.getMaxDownloadForms());
+                    vmsObj.setMaxPhaseNum(this.config.getMaxDownloadForms());
+                }
+                vmsObj.setLocalFormDir(this.config.getFtpFormDir() + File.separator + vmsObj.getCtlrNmbr());
+                vmsObj.setFtpFormDir(ApplicationConfig.FTP_FORM + File.separator + vmsObj.getCtlrNmbr() + File.separator);
+                vmsObj.setFtpDownload(true);
 
-                this.config.makeDirectory(obj.getLocalFormDir(), "VMS " + obj.getCtlrNmbr() + " Directory.");
+                this.config.makeDirectory(vmsObj.getLocalFormDir(), "VMS " + vmsObj.getCtlrNmbr() + " Directory.");
             }
         }
         catch (Exception e) {
@@ -75,9 +82,11 @@ public class VmsCtlrService {
         this.repoService.getCtlrMap().forEach((key, obj) -> {
             log.info("VmsCtlrService.loadCtlrInfo:: {}", obj.toString());
         });
+        log.info("VmsCtlrService.loadCtlrInfo: {} ms.", elapsed.milliSeconds());
     }
 
     public void loadCtlrSttsInfo() {
+        Elapsed elapsed = new Elapsed();
         try {
             List<TbVmsCtlrStts> infoList  = this.mapper.selectCtlrStts();
             log.info("VmsCtlrService.loadCtlrSttsInfo: {} EA", infoList.size());
@@ -93,6 +102,7 @@ public class VmsCtlrService {
         catch (Exception e) {
             log.error("VmsCtlrService.loadCtlrSttsInfo: {}", e.toString());
         }
+        log.info("VmsCtlrService.loadCtlrSttsInfo: {} ms.", elapsed.milliSeconds());
     }
 
     public void updateCtlrStts(boolean isRun, TbVmsCtlrDto AObj) {

+ 32 - 7
src/main/java/com/its/vms/service/VmsFontService.java

@@ -1,5 +1,6 @@
 package com.its.vms.service;
 
+import com.its.app.utils.Elapsed;
 import com.its.vms.dao.mapper.VmsFontMapper;
 import com.its.vms.dto.TbVmsFontColrDto;
 import com.its.vms.dto.TbVmsFontNameDto;
@@ -9,6 +10,7 @@ import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Service;
 
+import java.awt.*;
 import java.util.List;
 import java.util.concurrent.ConcurrentHashMap;
 
@@ -29,30 +31,52 @@ public class VmsFontService {
         return this.colrMap.get(vmsFontColrCd);
     }
 
+    public Color getFontColor(Integer colrCd) {
+        if (colrCd == 0) return Color.BLACK;
+        if (colrCd == 1) return Color.RED;
+        if (colrCd == 2) return Color.GREEN;
+        if (colrCd == 3) return Color.YELLOW;
+        TbVmsFontColrDto obj = this.colrMap.get(colrCd);
+        if (obj != null) {
+            return obj.getVmsFontColor();
+        }
+        return Color.BLACK;
+    }
+
+    public String getFontName(Integer vmsFontNameCd) {
+        TbVmsFontNameDto obj = findFontName(vmsFontNameCd);
+        if (obj == null) {
+            return "맑은 고딕";
+        }
+        return obj.getVmsFontNameNm();
+    }
     public void loadDb() {
         loadFontNameInfo();
         loadFontColorInfo();
     }
 
     public void loadFontNameInfo() {
+        Elapsed elapsed = new Elapsed();
         try {
-            List<TbVmsFontName> infoList  = this.mapper.selectFontName();
-            log.info("VmsFontService.loadFontNameInfo: {} EA", infoList.size());
-            for (TbVmsFontName data : infoList) {
+            List<TbVmsFontName> result  = this.mapper.selectFontName();
+            log.info("VmsFontService.loadFontNameInfo: {} EA", result.size());
+            for (TbVmsFontName data : result) {
                 TbVmsFontNameDto obj = data.toDto();
                 this.nameMap.put(obj.getVmsFontNameCd(), obj);
             }
         }
         catch (Exception e) {
-            log.error("VmsFontService.loadFontNameInfo: {}", e.toString());
+            log.error("VmsFontService.loadFontNameInfo: {}", e.getMessage());
         }
+        log.info("VmsFontService.loadFontNameInfo: {} ms.", elapsed.milliSeconds());
     }
 
     public void loadFontColorInfo() {
+        Elapsed elapsed = new Elapsed();
         try {
-            List<TbVmsFontColr> infoList  = this.mapper.selectFontColr();
-            log.info("VmsFontService.loadFontColorInfo: {} EA", infoList.size());
-            for (TbVmsFontColr dto : infoList) {
+            List<TbVmsFontColr> result  = this.mapper.selectFontColr();
+            log.info("VmsFontService.loadFontColorInfo: {} EA", result.size());
+            for (TbVmsFontColr dto : result) {
                 TbVmsFontColrDto obj = dto.toDto();
                 this.colrMap.put(obj.getVmsFontColrCd(), obj);
             }
@@ -60,6 +84,7 @@ public class VmsFontService {
         catch (Exception e) {
             log.error("VmsFontService.loadFontColorInfo: {}", e.toString());
         }
+        log.info("VmsFontService.loadFontColorInfo: {} ms.", elapsed.milliSeconds());
     }
 
 }

+ 53 - 14
src/main/java/com/its/vms/service/VmsFormService.java

@@ -1,18 +1,22 @@
 package com.its.vms.service;
 
+import com.its.app.utils.Elapsed;
 import com.its.app.utils.ItsUtils;
 import com.its.vms.config.ApplicationConfig;
 import com.its.vms.dao.mapper.VmsFormMapper;
+import com.its.vms.domain.eVmsFormType;
+import com.its.vms.dto.TbVmsFormColrDto;
 import com.its.vms.dto.TbVmsFormDto;
 import com.its.vms.dto.TbVmsFormObjectDto;
 import com.its.vms.dto.TbVmsSymbLibDto;
 import com.its.vms.entity.TbVmsForm;
+import com.its.vms.entity.TbVmsFormColr;
 import com.its.vms.entity.TbVmsFormObject;
-import com.its.vms.domain.eVmsFormType;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Service;
 
+import java.awt.*;
 import java.io.File;
 import java.util.List;
 import java.util.Objects;
@@ -29,21 +33,36 @@ public class VmsFormService {
     private final VmsSymbService vmsSymbService;
 
     private final ConcurrentHashMap<Integer, TbVmsFormDto> dataMap = new ConcurrentHashMap<>();
+    private final ConcurrentHashMap<Integer, TbVmsFormColrDto> colrMap = new ConcurrentHashMap<>();
 
     public TbVmsFormDto find(Integer vmsFormId) {
         return this.dataMap.get(vmsFormId);
     }
 
+    public Color getFormColor(Integer colrCd) {
+        if (colrCd == 0) return Color.BLACK;
+        if (colrCd == 1) return Color.RED;
+        if (colrCd == 2) return Color.GREEN;
+        if (colrCd == 3) return Color.YELLOW;
+        TbVmsFormColrDto obj = this.colrMap.get(colrCd);
+        if (obj != null) {
+            return obj.getVmsFormColor();
+        }
+        return Color.BLACK;
+    }
+
     public void loadDb() {
         loadVmsFormInfo();
         loadVmsFormObjectInfo();
+        loadVmsFormColorInfo();
     }
 
     public void loadVmsFormInfo() {
+        Elapsed elapsed = new Elapsed();
         try {
-            List<TbVmsForm> infoList  = this.mapper.selectVmsFormInfo();
-            log.info("VmsFormService.loadVmsFormInfo: {} EA", infoList.size());
-            infoList.forEach(data -> {
+            List<TbVmsForm> result  = this.mapper.selectVmsFormInfo();
+            log.info("VmsFormService.loadVmsFormInfo: {} EA", result.size());
+            result.forEach(data -> {
                 TbVmsFormDto obj = data.toDto();
                 String updtDt = "";
                 TbVmsFormDto oldObj = this.dataMap.get(obj.getVmsFormId());
@@ -54,7 +73,7 @@ public class VmsFormService {
                 // 정적폼(홍보) 같은 경우 최초 또는 변경되었을 때에만 사용할수 있도록(제어기로 한번만 다운로드 할수 있도록)
                 // 체크 플래그를 두어 확인할수 있도록 한다.
                 if (obj.getVmsFormTypeCd() == eVmsFormType.eFormTp_hongbo.getValue() && !Objects.equals(obj.getUpdtDt(), updtDt)) {
-                    obj.setIsNewForm(true);
+                    obj.setNewForm(true);
                 }
 
                 if (obj.getVmsFormTypeCd() == eVmsFormType.eFormTp_hongbo.getValue() && obj.getVmsFormImag() != null) {
@@ -77,22 +96,24 @@ public class VmsFormService {
         catch (Exception e) {
             log.error("VmsFormService.loadVmsFormInfo: {}", e.toString());
         }
+        log.info("VmsFormService.loadVmsFormInfo: {} ms.", elapsed.milliSeconds());
     }
 
     public void loadVmsFormObjectInfo() {
+        Elapsed elapsed = new Elapsed();
         try {
-            List<TbVmsFormObject> infoList  = this.mapper.selectVmsFormObjectInfo();
-            log.info("VmsFormService.loadVmsFormObjectInfo: {} EA", infoList.size());
-            infoList.forEach(data -> {
+            List<TbVmsFormObject> result  = this.mapper.selectVmsFormObjectInfo();
+            log.info("VmsFormService.loadVmsFormObjectInfo: {} EA", result.size());
+            result.forEach(data -> {
                 TbVmsFormObjectDto obj = data.toDto();
                 TbVmsFormDto formObj = this.dataMap.get(obj.getVmsFormId());
                 if (formObj == null) {
                     log.error("VmsFormService.loadVmsFormObjectInfo: Not Found VMS Form {}", obj.getVmsFormId());
                     return;
                 }
-
+                // log.info("VmsFormService.loadVmsFormObjectInfo: {}, {}", obj.getVmsFormId(), obj.getVmsFormObjectId());
                 // 폼정보에 오브젝트 객체를 추가
-                formObj.getObjs().add(obj);
+                formObj.getObjects().add(obj);
 
                 int objType = obj.getVmsFormObjectTypeCd();
                 if (formObj.getVmsFormTypeCd() == eVmsFormType.eFormTp_figure.getValue()) {
@@ -105,7 +126,7 @@ public class VmsFormService {
                         case  2://,  ,  @이미지
                             if ("F".equals(obj.getTrfcFillCd())) {
                                 //하단고정일 경우 도형식하단에 표출할 소통정보가 없을 경우 표출하는 정보임
-                                formObj.setTrfFixed(true);
+                                formObj.setBottomTrfFixed(true);
                             }
                             break;
                         //////////////////////// 20200515 추가함(E)
@@ -117,13 +138,13 @@ public class VmsFormService {
                         case 16://, '	@통행속도
                         case 17://, '	@소통정보이미지
                         case 18://, '	@지점명
-                            formObj.setTrfIfsc(true);
+                            formObj.setBottomTrfIfsc(true);
                             break;
                         case 93://, '	@축시점명(순환)
                         case 94://, '	@축종점명(순환)
                         case 95://, '	@축소통상황(순환)
                         case 96://, '	@축통행시간(순환)
-                            formObj.setTrfAxis(true);
+                            formObj.setBottomTrfAxis(true);
                             break;
                         default:
                             break;
@@ -132,7 +153,7 @@ public class VmsFormService {
 
                 if (objType == 3) {
                     // 소통정보배경 이미지
-                    formObj.setTrfBackImgId(Integer.valueOf(obj.getSymbLibNmbr()+"0"));
+                    formObj.setTrfBackImgId(obj.getSymbLibNmbr()+"0");
                 }
 
                 if (objType ==   1 || //심볼
@@ -156,5 +177,23 @@ public class VmsFormService {
         catch (Exception e) {
             log.error("VmsFormService.loadVmsFormObjectInfo: {}", e.toString());
         }
+        log.info("VmsFormService.loadVmsFormObjectInfo: {} ms.", elapsed.milliSeconds());
     }
+
+    public void loadVmsFormColorInfo() {
+        Elapsed elapsed = new Elapsed();
+        try {
+            List<TbVmsFormColr> result  = this.mapper.selectVmsFormColorInfo();
+            log.info("VmsFormService.loadVmsFormColorInfo: {} EA", result.size());
+            result.forEach(data -> {
+                TbVmsFormColrDto obj = data.toDto();
+                this.colrMap.put(obj.getVmsFormColrCd(), obj);
+            });
+        }
+        catch (Exception e) {
+            log.error("VmsFormService.loadVmsFormInfo: {}", e.toString());
+        }
+        log.info("VmsFormService.loadVmsFormColorInfo: {} ms.", elapsed.milliSeconds());
+    }
+
 }

+ 11 - 4
src/main/java/com/its/vms/service/VmsIfscService.java

@@ -1,5 +1,6 @@
 package com.its.vms.service;
 
+import com.its.app.utils.Elapsed;
 import com.its.vms.dao.mapper.VmsIfscMapper;
 import com.its.vms.dto.TbVmsIfscTrafDto;
 import com.its.vms.dto.TbVmsRltnIfscDto;
@@ -20,7 +21,6 @@ import java.util.concurrent.ConcurrentHashMap;
 public class VmsIfscService {
 
     private final VmsIfscMapper mapper;
-    private final VmsManageService vmsManageService;
     private final AppRepositoryService repoService;
 
     private final ConcurrentHashMap<Long, TbVmsIfscTrafDto> dataMap = new ConcurrentHashMap<>();
@@ -35,6 +35,7 @@ public class VmsIfscService {
     }
 
     public void loadVmsRltnIfsc() {
+        Elapsed elapsed = new Elapsed();
         this.repoService.getCtlrMap().forEach((nmbr, obj) -> {
             obj.getRltnIfscMap().forEach((ifscId, ifsc) -> {
                 ifsc.setUsed(false);
@@ -53,7 +54,7 @@ public class VmsIfscService {
                         vmsObj.getRltnIfscMap().put(data.getVmsIfscId(), rltnIfsc);
                     }
                     rltnIfsc.setUsed(true);
-                    if (this.vmsManageService.getStaticCycle()) {
+                    if (this.repoService.isStaticCycle()) {
                         // 정주기 데이터 조회 인 경우
                         // 정체판정 정보가 변경되었으면 정체판정횟수를 초기화한다.
                         boolean cngstCnfmYn = "Y".equals(data.getCngstCnfmYn());
@@ -72,11 +73,13 @@ public class VmsIfscService {
         catch (Exception e) {
             log.error("VmsIfscService.loadVmsRltnIfsc: {}", e.toString());
         }
+        log.info("VmsIfscService.loadVmsRltnIfsc: {} ms.", elapsed.milliSeconds());
     }
 
     public void loadVmsIfscTrafInfo() {
-        dataMap.forEach((ifscId, ifsc) -> {
-            ifsc.setCmtrGradCd("0");
+        Elapsed elapsed = new Elapsed();
+        this.dataMap.forEach((ifscId, ifsc) -> {
+            ifsc.setCmtrGradCd(0);
             ifsc.setSped(0);
             ifsc.setTrvlHh(0);
         });
@@ -91,12 +94,16 @@ public class VmsIfscService {
                     obj.setCngstCnt(oldObj.getCngstCnt());
                     obj.setCngstDt(oldObj.getCngstDt());
                 }
+                if (obj.getCmtrGradCd() != 0 && obj.getTrvlHh() == 0) {
+                    obj.setTrvlHh(1);
+                }
                 this.dataMap.put(data.getVmsIfscId(), obj);
             });
         }
         catch (Exception e) {
             log.error("VmsIfscService.loadVmsIfscTrafInfo: {}", e.toString());
         }
+        log.info("VmsIfscService.loadVmsIfscTrafInfo: {} ms.", elapsed.milliSeconds());
     }
 
 }

+ 1188 - 33
src/main/java/com/its/vms/service/VmsManageService.java

@@ -1,6 +1,7 @@
 package com.its.vms.service;
 
 import com.its.app.utils.Elapsed;
+import com.its.app.utils.FloodFill;
 import com.its.app.utils.ItsUtils;
 import com.its.vms.config.ApplicationConfig;
 import com.its.vms.dao.mapper.VmsManageMapper;
@@ -17,8 +18,14 @@ import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Service;
 
+import javax.imageio.ImageIO;
+import java.awt.*;
+import java.awt.image.BufferedImage;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
 import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.Objects;
 
 @Slf4j
@@ -38,18 +45,9 @@ public class VmsManageService {
 
     private final TcpServerDataProcess serverDataProcess;
 
-    private boolean isStaticCycle = false;
-
-    public boolean getStaticCycle() {
-        return this.isStaticCycle;
-    }
-    public void setStaticCycle(boolean isStaticCycle) {
-        this.isStaticCycle = isStaticCycle;
-    }
-
     public void initVmsDsplPrst() {
         HashMap<String, Object> param = new HashMap<>();
-        param.put("maxPhase",  this.config.getMaxPhase()+1);
+        param.put("maxPhase",  this.config.getMaxDownloadForms()+1);
         this.mapper.initVmsDsplPrst(param);
     }
 
@@ -57,6 +55,7 @@ public class VmsManageService {
      * 전광판 On/Off 시각 정보 읽어서 전광판 제어
      */
     public void loadVmsOnOffTime() {
+        Elapsed elapsed = new Elapsed();
         int nCurrTime = ItsUtils.parseIntDef(ItsUtils.getSysHourMin(), 0);
         String sysDay = ItsUtils.getSysDay();
 
@@ -126,6 +125,7 @@ public class VmsManageService {
      * 돌발 및 공사/행사 정보 조회
      */
     public void loadEventOccrInfo() {
+        Elapsed elapsed = new Elapsed();
         List<TbVmsIncd> result = this.mapper.selectVmsIncd();
         result.forEach(data -> {
             TbVmsCtlrDto vmsObj = this.repoService.getCtrlMap(data.getVmsCtlrNmbr());
@@ -134,10 +134,10 @@ public class VmsManageService {
                 return;
             }
 
-            if (!vmsObj.isProvide()) {
-                log.error("VmsManageService.loadEventOccrInfo:VMS {}, Provide {}.", data.getVmsCtlrNmbr(), vmsObj.isProvide());
-                return;
-            }
+//            if (!vmsObj.isProvide()) {
+//                log.error("VmsManageService.loadEventOccrInfo:VMS {}, Provide {}.", data.getVmsCtlrNmbr(), vmsObj.isProvide());
+//                return;
+//            }
 
             if ("VIT1".equals(data.getVmsIncdTypeCd())) {
                 // 사고
@@ -148,6 +148,7 @@ public class VmsManageService {
                 vmsObj.getEvent().add(data.toDto());
             }
         });
+        log.info("VmsManageService.loadEventOccrInfo: {} ms.", elapsed.milliSeconds());
     }
 
     /**
@@ -187,10 +188,247 @@ public class VmsManageService {
         });
     }
 
+    /**
+     * VMS 교통정보 표출 스케쥴 조회
+     */
+    public void loadVmsDsplTrafSchedule(TbVmsCtlrDto vmsObj, TbVmsFormDto vmsForm, TbVmsScheduleDto schedule) {
+        //Elapsed elapsed = new Elapsed();
+        boolean isBottomTraffic = false;
+        Long figureVmsIfscId    = 0L;
+        int figureTrafficCnt    = 0;   //전체 표출할 하단 소통정보 갯수
+        int figureDisplayTm     = schedule.getDsplHh();
+
+        eVmsFormType vmsFormType = eVmsFormType.getValue(vmsForm.getVmsFormTypeCd());
+
+        // 도형식 소통정보 인 경우 해당 도형의 소통정보가 존재하는지 확인
+        if (vmsFormType == eVmsFormType.eFormTp_figure) {
+            // 도형식 소통정보 스케쥴폼
+            TbVmsSymbLibDto backTrafSymb = this.symbService.find(vmsForm.getTrfBackImgId());    // 심벌번호+"0"
+            if (backTrafSymb == null) {
+                log.error("VmsManageService.loadVmsDsplTrafSchedule: VMS {} Back Traffic Form {}, Not Found Traffic Image {}.",
+                        vmsObj.getCtlrNmbr(), vmsForm.getVmsFormId(), vmsForm.getTrfBackImgId());
+                return;
+            }
+
+            boolean isTraffic = false;
+            int missingCnt = 0;
+            for (Map.Entry<Integer, TbVmsSymbIfscDto> e : backTrafSymb.getCellMap().entrySet()) {
+                TbVmsSymbIfscDto cell = e.getValue();
+                if (cell.isDup()) {
+                    continue;
+                }
+                TbVmsIfscTrafDto ifscTraf = this.ifscService.find(cell.getVmsIfscId());
+                if (ifscTraf == null) {
+                    log.error("VmsManageService.loadVmsDsplTrafSchedule: VMS {} Back Traffic Form {}, IFSC Not Found {}, cellId {}.",
+                            vmsObj.getCtlrNmbr(), vmsForm.getVmsFormId(), cell.getVmsIfscId(), cell.getCellId());
+                    missingCnt++;
+                    continue;
+                }
+                if (0 == ifscTraf.getCmtrGradCd()) {
+                    missingCnt++;
+                }
+                else {
+                    if (figureVmsIfscId == 0L) {
+                        figureVmsIfscId = cell.getVmsIfscId();
+                    }
+                    isTraffic = true;
+                }
+            }
+
+            // 민간정보 연계에서 정보가 수집되지 않는 경우 가 종종 있어서 도형식 소통정보를 채우지 못한다.
+            // 결측구간이 3구간 이상이면 도형식 소통정보 표출하지 말자
+            if (!isTraffic || missingCnt >= 3) {
+                log.error("VmsManageService.loadVmsDsplTrafSchedule: VMS {} Back Traffic Form {}, {}, IFSC Missing Traffic {} EA.",
+                        vmsObj.getCtlrNmbr(), vmsForm.getVmsFormId(), isTraffic, missingCnt);
+                return;
+            }
+
+            if (vmsForm.isBottomTrfIfsc()) {
+                // 도형식 하단에 정보제공구간 소통정보 표출하는 경우 다중폼이 생성됨
+                // 제공구간소통정보가 정체, 지체인 경우만 하단 소통정보를 표출함
+                // 도형식 배경셀에 등록된 구간에 대해서 표출하도록함
+                for (Map.Entry<Integer, TbVmsSymbIfscDto> e : backTrafSymb.getCellMap().entrySet()) {
+                    TbVmsSymbIfscDto cell = e.getValue();
+                    if (cell.isDup()) {
+                        continue;
+                    }
+                    TbVmsIfscTrafDto ifscTraf = this.ifscService.find(cell.getVmsIfscId());
+                    if (ifscTraf != null) {
+                        if (3 == ifscTraf.getCmtrGradCd()) {
+                            // 정체구간
+                            isBottomTraffic = true; // 도형식배경소통정보의 셀내의 구간정보 소통정보가 하나라도 있으면 도형식 소통정보를 표출함
+                            figureTrafficCnt++;
+                        }
+                        if (2 == ifscTraf.getCmtrGradCd()) {
+                            // 지체구간
+                            isBottomTraffic = true; // 도형식배경소통정보의 셀내의 구간정보 소통정보가 하나라도 있으면 도형식 소통정보를 표출함
+                            figureTrafficCnt++;
+                        }
+                    }
+                }
+                if (figureVmsIfscId != 0L && !isBottomTraffic) {
+                    // 소통정보는 존재하지만 지체, 정체가 없는 경우 하단 소통정보를 표출못하게 된다.
+                    // 만일 하단 고정문자가 존재하는 경우 하단 고정문자를 표출하면 되지만
+                    // 고정문자가 존재하기 않으면 하단에 표출할 정보가 없으므로 첫번째 구간의 정보를 표출하도록 한다.
+                    if (!vmsForm.isBottomTrfFixed()) {
+                        isBottomTraffic = true; // 첫번째 소통정보가 표출되도록 함
+                        figureTrafficCnt++;
+                    }
+                }
+            }
+
+            if (vmsForm.isBottomTrfAxis()) {
+                // 도형식 소통정보 폼일때 하단에 VMS 축 소통정보를 표출해야 하는 경우 다중폼이 생성됨
+                // 축 소통정보인 경우 모든 축에 대해 소통정보를 표출, 최대 표출설정값이 설정된 경우 설정값만큼만 표출되게 수정(20200515)
+                // rltnIfscMap
+                for (Map.Entry<Long, TbVmsRltnIfscDto> e : vmsObj.getRltnIfscMap().entrySet()) {
+                    TbVmsRltnIfscDto ifsc = e.getValue();
+                    if (!ifsc.isUsed()) {
+                        continue;
+                    }
+                    TbVmsIfscTrafDto ifscTraf = this.ifscService.find(ifsc.getVmsIfscId());
+                    if (!"Y".equals(ifscTraf.getAxisYn())) {
+                        continue;
+                    }
+                    if (0 != ifscTraf.getCmtrGradCd()) {
+                        if (figureVmsIfscId == 0L) {
+                            figureVmsIfscId = ifsc.getVmsIfscId();
+                            isBottomTraffic = true; // 첫번째 소통정보가 표출되도록 함
+                            figureTrafficCnt++;
+                        }
+                    }
+                }
+                if (figureVmsIfscId != 0L && !isBottomTraffic) {
+                    // 하단 축 소통정보가 없는 경우 하단 소통정보를 표출못하게 된다.
+                    // 만일 하단 고정문자가 존재하는 경우 하단 고정문자를 표출하면 되지만
+                    // 고정문자가 존재하기 않으면 하단에 표출할 정보가 없으므로 첫번째 구간의 정보를 표출하도록 한다.
+                    if (!vmsForm.isBottomTrfFixed()) {
+                        isBottomTraffic = true; // 첫번째 소통정보가 표출되도록 함
+                        figureTrafficCnt++;
+                    }
+                }
+            }
+        } // (vmsFormType == eVmsFormType.eFormTp_figure) 도형식 소통정보 폼
+
+        if (isBottomTraffic) {
+            // 도형식 소통정보 표출폼인데 하단에 소통정보를 표출하는 경우.
+
+            // 표출시간 설정
+            if (this.config.getBottomTrafficMax() > 0 && figureTrafficCnt > this.config.getBottomTrafficMax()) {
+                figureTrafficCnt = this.config.getBottomTrafficMax();
+            }
+            if (this.config.getBottomTrafficCycle() > 0) {
+                int dsplTm = figureDisplayTm / figureTrafficCnt;
+                if (dsplTm < this.config.getBottomTrafficCycle()) {
+                    figureDisplayTm = this.config.getBottomTrafficCycle();
+                }
+                else {
+                    figureDisplayTm = dsplTm;
+                }
+            }
+
+            int bottomTrafficCnt = 0;
+            // 도형식 소통정보 스케쥴폼
+            TbVmsSymbLibDto backTrafSymb = this.symbService.find(vmsForm.getTrfBackImgId());    // 심벌번호+"0"
+            if (backTrafSymb == null) {
+                return;
+            }
+            if (vmsForm.isBottomTrfIfsc()) {
+                // 도형식 하단 정보제공구간 소통정보 폼 스케쥴 작성
+                for (Map.Entry<Integer, TbVmsSymbIfscDto> e : backTrafSymb.getCellMap().entrySet()) {
+                    TbVmsSymbIfscDto cell = e.getValue();
+                    if (cell.isDup()) {
+                        continue;
+                    }
+                    TbVmsIfscTrafDto ifscTraf = this.ifscService.find(cell.getVmsIfscId());
+                    if (ifscTraf != null && 3 == ifscTraf.getCmtrGradCd()) {
+                        // 정체 정보 먼저 표출
+                        schedule.setVmsIfscId(cell.getVmsIfscId());
+                        schedule.setFrstVmsIfscId(cell.getVmsIfscId());
+                        schedule.setDsplHh(figureDisplayTm);
+                        vmsObj.getSchedule().add(schedule);
+                        bottomTrafficCnt++;
+                        if (this.config.getBottomTrafficMax() > 0 && bottomTrafficCnt >= this.config.getBottomTrafficMax()) {
+                            // 최건 폼 표출 건수 까지만 스케쥴 작성
+                            break;
+                        }
+                    }
+                }
+                for (Map.Entry<Integer, TbVmsSymbIfscDto> e : backTrafSymb.getCellMap().entrySet()) {
+                    TbVmsSymbIfscDto cell = e.getValue();
+                    if (cell.isDup()) {
+                        continue;
+                    }
+                    TbVmsIfscTrafDto ifscTraf = this.ifscService.find(cell.getVmsIfscId());
+                    if (ifscTraf != null && 2 == ifscTraf.getCmtrGradCd()) {
+                        // 지체 정보 표출
+                        schedule.setVmsIfscId(cell.getVmsIfscId());
+                        schedule.setFrstVmsIfscId(cell.getVmsIfscId());
+                        schedule.setDsplHh(figureDisplayTm);
+                        vmsObj.getSchedule().add(schedule);
+                        bottomTrafficCnt++;
+                        if (this.config.getBottomTrafficMax() > 0 && bottomTrafficCnt >= this.config.getBottomTrafficMax()) {
+                            // 최건 폼 표출 건수 까지만 스케쥴 작성
+                            break;
+                        }
+                    }
+                }
+                if (bottomTrafficCnt == 0 && figureVmsIfscId != 0L) {
+                    schedule.setVmsIfscId(figureVmsIfscId);
+                    schedule.setFrstVmsIfscId(figureVmsIfscId);
+                    schedule.setDsplHh(figureDisplayTm);
+                    vmsObj.getSchedule().add(schedule);
+                    bottomTrafficCnt++;
+                }
+            }
+
+            if (vmsForm.isBottomTrfAxis()) {
+                // 도형식 하단 축 소통정보 순환 메시지 스케쥴 작성
+                for (Map.Entry<Long, TbVmsRltnIfscDto> e : vmsObj.getRltnIfscMap().entrySet()) {
+                    TbVmsRltnIfscDto ifsc = e.getValue();
+                    if (!ifsc.isUsed()) {
+                        continue;
+                    }
+                    TbVmsIfscTrafDto ifscTraf = this.ifscService.find(ifsc.getVmsIfscId());
+                    if (!"Y".equals(ifscTraf.getAxisYn())) {
+                        continue;
+                    }
+                    if (0 == ifscTraf.getCmtrGradCd()) {
+                        continue;
+                    }
+                    schedule.setVmsIfscId(ifsc.getVmsIfscId());
+                    schedule.setDsplHh(figureDisplayTm);
+                    vmsObj.getSchedule().add(schedule);
+                    bottomTrafficCnt++;
+                    if (this.config.getBottomTrafficMax() > 0 && bottomTrafficCnt >= this.config.getBottomTrafficMax()) {
+                        // 최건 폼 표출 건수 까지만 스케쥴 작성
+                        break;
+                    }
+                }
+                if (bottomTrafficCnt == 0 && figureVmsIfscId != 0L) {
+                    schedule.setVmsIfscId(figureVmsIfscId);
+                    schedule.setFrstVmsIfscId(figureVmsIfscId);
+                    schedule.setSecdVmsIfscId(figureVmsIfscId);
+                    schedule.setThirVmsIfscId(figureVmsIfscId);
+                    schedule.setFourVmsIfscId(figureVmsIfscId);
+                    schedule.setDsplHh(figureDisplayTm);
+                    vmsObj.getSchedule().add(schedule);
+                    bottomTrafficCnt++;
+                }
+            }
+        }
+        else {
+            // 도형식 소통정보 단일 표출폼인 경우
+            vmsObj.getSchedule().add(schedule);
+        }
+        //log.info("VmsManageService.loadVmsDsplTrafSchedule: {} ms.", elapsed.milliSeconds());
+    }
+
     /**
      * VMS 표출 스케쥴 정보를 조회
      */
     public void loadVmsDsplSchedule() {
+        Elapsed elapsed = new Elapsed();
         List<TbVmsSchedule> result = this.mapper.selectVmsDsplSchedule();
         result.forEach(data -> {
             TbVmsCtlrDto vmsObj = this.repoService.getCtrlMap(data.getVmsCtlrNmbr());
@@ -225,20 +463,38 @@ public class VmsManageService {
             // eFormTp_traf_2   = 12,  //	소통상황(2단)
             // eFormTp_traf_3   = 13,  //	소통상황(3단)
             // eFormTp_traf_4   = 14,  //	소통상황(4단)
+            boolean isIncidentForm = false;
             if (vmsSchFormType == eVmsScheduleType.eSchTp_traffic && vmsFormType == eVmsFormType.eFormTp_traf_1) {
                 // 교통정보스케쥴이고 폼의 유형이 소통정보(1단) 인경우
                 // 해당 폼이 돌발이나 이벤트가 등록되어 있다면 폼을 생성하지 않는다.
-                // TODO: 로직 다시점검해봐야함.
+                Long frstVmsIfscId = data.getFrstVmsIfscId();
+                if (frstVmsIfscId != null && frstVmsIfscId != 0L) {
+                    for (int ii = 0; ii < vmsObj.getSchedule().getUnits().size(); ii++) {
+                        TbVmsScheduleDto schedule = vmsObj.getSchedule().getUnits().get(ii);
+                        eVmsScheduleType scheduleFormType = eVmsScheduleType.getValue(schedule.getVmsSchFormType());
+                        if (scheduleFormType == eVmsScheduleType.eSchTp_incident || //돌발
+                            scheduleFormType == eVmsScheduleType.eSchTp_gongsa ||   //공사/행사
+                            scheduleFormType == eVmsScheduleType.eSchTp_deture      //우회도로
+                        ) {
+                            if (schedule.getEvent() != null && Objects.equals(schedule.getEvent().getVmsIfscId(), frstVmsIfscId)) {
+                                isIncidentForm = true;
+                                break;
+                            }
+                        }
+                    }
+                }
+            }
+            if (isIncidentForm) {
+                log.error("VmsManageService.loadVmsDsplSchedule: VMS {}, Form {}, IFSC ID {} Incident Occurred.",
+                        vmsObj.getCtlrNmbr(), vmsForm.getVmsFormId(), data.getFrstVmsIfscId());
+                // TODO: 나중에 추가되는 스케쥴에 정보제공구간이 존재하는 경우 체크하지 못함...
+                return;
             }
-
-            boolean isBottomTraffic = false;
-            int FigureDisplayTm     = data.getDsplHh();
-            Long figureVmsIfscId    = 0L;
-            int figureTrafficCnt    = 0;   //전체 표출할 하단 소통정보 갯수
 
             TbVmsScheduleDto schedule = data.toDto();
             switch(vmsSchFormType) {
                 case eSchTp_traffic:    // 교통정보
+                    loadVmsDsplTrafSchedule(vmsObj, vmsForm, schedule);
                     break;
                 case eSchTp_incident:   // 돌발
                     // 돌발 스케줄인 경우 해당 VMS에 발생한 모든 돌발 정보를 표출하도록 스케줄 추가
@@ -324,8 +580,8 @@ public class VmsManageService {
                 case eSchTp_atmp: //9:대기환경
                     Long atmpSttnNmbr = 0L;
                     boolean isAtmp = false;
-                    for (int ii = 0; ii < vmsForm.getObjs().size(); ii++) {
-                        TbVmsFormObjectDto formObj = vmsForm.getObjs().get(ii);
+                    for (int ii = 0; ii < vmsForm.getObjects().size(); ii++) {
+                        TbVmsFormObjectDto formObj = vmsForm.getObjects().get(ii);
                         /**
                          90	401	@관측장소	Y
                          90	402	@PM10	Y
@@ -341,7 +597,7 @@ public class VmsManageService {
                          90	412	@오존등급	Y
                          90	413	@오존등급이미지	Y
                         */
-                        if (formObj.getVmsFormObjectTypeCd() >= 401 && formObj.getVmsFormObjectTypeCd() <= 413) {
+                        if (formObj.geVmsFormObjectectTypeCd() >= 401 && formObj.geVmsFormObjectectTypeCd() <= 413) {
                             atmpSttnNmbr = formObj.getVmsIfscId();
                             TbVmsAtmpDto atmpObj = this.atmpService.find(atmpSttnNmbr);
                             if (atmpObj == null) {
@@ -373,15 +629,15 @@ public class VmsManageService {
                     break;
                 case eSchTp_park: //10:주차정보
                     boolean isParkingForm = false;
-                    for (int ii = 0; ii < vmsForm.getObjs().size(); ii++) {
-                        TbVmsFormObjectDto formObj = vmsForm.getObjs().get(ii);
+                    for (int ii = 0; ii < vmsForm.getObjects().size(); ii++) {
+                        TbVmsFormObjectDto formObj = vmsForm.getObjects().get(ii);
                         /**
                          100	101	@주차장명	N
                          100	102	@주차면수	Y
                          100	103	@주차가능면수	Y
                          100	104	@주차혼잡도	Y
                          */
-                        if (formObj.getVmsFormObjectTypeCd() >= 101 && formObj.getVmsFormObjectTypeCd() <= 104) {
+                        if (formObj.geVmsFormObjectectTypeCd() >= 101 && formObj.geVmsFormObjectectTypeCd() <= 104) {
                             TbVmsParkDto parkObj = this.parkService.find(formObj.getVmsIfscId());
                             if (parkObj == null) {
                                 log.error("VmsManageService.loadVmsDsplSchedule: VMS {}, Parking Form {}, Not Found Parking {}.",
@@ -422,12 +678,14 @@ public class VmsManageService {
                     break;
             }
         });
+        log.info("VmsManageService.loadVmsDsplSchedule: {} ms.", elapsed.milliSeconds());
     }
 
     /**
      * VMS 운영모드 정보 조회
      */
     public void loadVmsOperationMode() {
+        Elapsed elapsed = new Elapsed();
         List<TbVmsOperMode> result = this.mapper.selectVmsOperMode();
         result.forEach(data-> {
             TbVmsCtlrDto vmsObj = this.repoService.getCtrlMap(data.getVmsCtlrNmbr());
@@ -440,13 +698,14 @@ public class VmsManageService {
             }
             String oldOperMode = vmsObj.getOperMode();
             vmsObj.setOperMode(data.getOperMode());
-            vmsObj.setMaxPhaseNum(Math.min(VmsConstants.VMS_MAX_FORMS, data.getVmsMaxPhaseNum()));
+            vmsObj.setMaxPhaseNum(Math.min(this.config.getMaxDownloadForms(), data.getVmsMaxPhaseNum()));
             // VMS 별 정체폼갯수를 제한하는 경우 여기에서 처리하도록 하자
             vmsObj.setMaxPhaseNum(VmsConstants.VMS_MAX_CNGS_FORM);
             if (data.getOperMode().equals(oldOperMode)) {
                 // TODO: 화면 업데이트
             }
         });
+        log.info("VmsManageService.loadVmsOperationMode: {} ms.", elapsed.milliSeconds());
     }
 
     public void initProvide(TbVmsCtlrDto AObj) {
@@ -457,7 +716,7 @@ public class VmsManageService {
         });
     }
 
-    public void clearProvideMode() {
+    public void clearVmsProvideMode() {
         String dsplDt = ItsUtils.getSysTime();
         this.repoService.getCtlrMap().forEach((key, obj) -> {
 
@@ -469,7 +728,7 @@ public class VmsManageService {
 
             obj.getIncident().getUnits().clear();
             obj.getEvent().getUnits().clear();
-            obj.getForms().clear();
+            obj.getFormManager().clear();
 
             obj.setCngstForms(0);
             obj.setMaxCngstForm(this.config.getMaxCngstForms());
@@ -492,18 +751,21 @@ public class VmsManageService {
      * VMS 다운로드 폼 정보 생성
      */
     public void downloadVmsForm() {
-        if (this.isStaticCycle) {
+        if (this.repoService.isStaticCycle()) {
             loadVmsOperationMode(); // 운영모드 조회
             initProvide(null);
         }
 
-        clearProvideMode();
+        clearVmsProvideMode();
         makeVmsScenarioForm();
+        makeVmsProvideForm();
+        makeVmsDatabaseForm();
     }
     /**
      * VMS FORM 데이터를 생성한다.
      */
     public void makeVmsScenarioForm() {
+        Elapsed elapsed = new Elapsed();
         if (this.config.isLoadDb()) {
             loadDb();
         }
@@ -513,13 +775,906 @@ public class VmsManageService {
         loadEventOccrInfo();  // 돌발정보조회
         this.ifscService.loadDb();  // VMS 정보제공구간 교통정보
 
-        if (this.isStaticCycle) {
+        if (this.repoService.isStaticCycle()) {
             // 정주기 가공일때 처리해야 함
             congestJudgment();  // 정체판정
-            this.isStaticCycle = false;
+            this.repoService.setStaticCycle(false);
         }
 
         loadVmsDsplSchedule();
+        log.info("VmsManageService.makeVmsScenarioForm: {} ms.", elapsed.milliSeconds());
+    }
+
+    public void makeVmsProvideForm() {
+        Elapsed elapsed = new Elapsed();
+        this.repoService.getCtlrMap().forEach((ctlrNmbr, vmsObj) -> {
+            if ("Y".equals(vmsObj.getDelYn())) {
+                return;
+            }
+            VmsSchedule scheObj = vmsObj.getSchedule();
+
+            int downLoadForms = Math.min(vmsObj.getMaxPhaseNum(), scheObj.size());
+            vmsObj.getControlMode().setMaxDisplayForm(downLoadForms);
+            log.info("VmsManageService.makeVmsProvideForm: VMS {}, DownloadForms/Schedule/MaxPhase {}/{}/{} EA. FormDir {}, FtpDir {}",
+                    ctlrNmbr, scheObj.size(), downLoadForms, scheObj.size(), vmsObj.getLocalFormDir(), vmsObj.getFtpFormDir());
+
+            this.config.makeDirectory(vmsObj.getLocalFormDir(), "VMS " + vmsObj.getCtlrNmbr() + " Directory.");
+
+            boolean isEVehIngForm = vmsObj.isEvehIngForm() && vmsObj.getEvechile().isOcrr();    // 폼이 존재하고 데이터 발생
+            boolean isEVehEndForm = vmsObj.isEvehEndForm() && vmsObj.getEvechile().isEnd();     // 폼이 존재하고 데이터 발생
+
+            log.info("VmsManageService.makeVmsProvideForm: VMS {}, Schedules {}.", ctlrNmbr, scheObj.size());
+
+            for (int scheduleIdx = 0; scheduleIdx < scheObj.size(); scheduleIdx++) {
+                TbVmsScheduleDto schedule = scheObj.getUnits().get(scheduleIdx);
+                if (vmsObj.isExistCngsForm() && !schedule.isDsplCngsYn()) {
+                    // 해당 제어기에 정체폼이 존재하는 경우 스케쥴 폼이 정체일때 표출하지 않는 경우 폼을 생성하지 않는다.
+                    log.info("VmsManageService.makeVmsProvideForm: VMS {}, Congest continuous {}/{}.", ctlrNmbr, vmsObj.isExistCngsForm(), schedule.isDsplCngsYn());
+                    continue;
+                }
+                TbVmsFormDto vmsForm = this.formService.find(schedule.getVmsFormId());
+                if (vmsForm == null) {
+                    log.error("VmsManageService.makeVmsProvideForm: VMS {}, Not Found Form {}.", ctlrNmbr, schedule.getVmsFormId());
+                    continue;
+                }
+                eVmsScheduleType vmsSchFormType = eVmsScheduleType.getValue(schedule.getVmsSchFormType());
+                if (vmsSchFormType == null) {
+                    log.error("VmsManageService.makeVmsProvideForm: Unknown Form Schedule Type: VMS({}), FormId({}), FormScheduleType({})",
+                            ctlrNmbr, schedule.getVmsFormId(), schedule.getVmsSchFormType());
+                    continue;
+                }
+                eVmsFormType vmsFormType = eVmsFormType.getValue(vmsForm.getVmsFormTypeCd());
+                if (vmsFormType == null) {
+                    log.error("VmsManageService.makeVmsProvideForm: Unknown Form Type: VMS({}), FormId({}), FormScheduleType({})",
+                            ctlrNmbr, schedule.getVmsFormId(), vmsForm.getVmsFormTypeCd());
+                    continue;
+                }
+                log.info("VmsManageService.makeVmsProvideForm: VMS {}, FORM {}, Schedule {}, ScheduleType {}, FormType {}.",
+                        ctlrNmbr, vmsForm.getVmsFormId(), scheduleIdx, vmsSchFormType, vmsFormType);
+
+                if (isEVehIngForm) {
+                    // 긴급차량 진행중 메시지 표출
+                    if (vmsSchFormType != eVmsScheduleType.eSchTp_evehicle || vmsForm.getVmsFormDsplDrctCd() != 0) {
+                        // 긴급차량우선신호 진행중 일때 스케쥴 유형 및 폼유형이 맞지 않으면 리턴
+                        // 단일 메시지로 표출하는 경우
+                        log.info("VmsManageService.makeVmsProvideForm: VMS {}, FORM {}, EVehicle Ing {}. {} DsplDrctCd {}",
+                                ctlrNmbr, vmsForm.getVmsFormId(), isEVehIngForm, vmsSchFormType, vmsForm.getVmsFormDsplDrctCd());
+                        continue;
+                    }
+                } else {
+                    if (vmsSchFormType == eVmsScheduleType.eSchTp_evehicle && vmsForm.getVmsFormDsplDrctCd() == 0) {
+                        log.info("VmsManageService.makeVmsProvideForm: VMS {}, FORM {}, EVehicle Ing {}. {} DsplDrctCd {}",
+                                ctlrNmbr, vmsForm.getVmsFormId(), isEVehIngForm, vmsSchFormType, vmsForm.getVmsFormDsplDrctCd());
+                        continue;
+                    }
+                }
+
+                if (isEVehEndForm) {
+                    // 긴급차량 운영종료 메시지 표출
+                    // 운영종료 메시지는 기타 메시지와 같이 내려보낸다. 단, 표출시간을 60초로 해서 내려보내자.
+//                    if (vmsSchFormType != eVmsScheduleType.eSchTp_evehicle || vmsForm.getVmsFormDsplDrctCd() != 1) {
+//                        // 긴급차량우선신호 운영종료 일때 스케쥴 유형 및 폼유형이 맞지 않으면 리턴
+//                        // 단일메시지로 표출하는 경우
+//                        continue;
+//                    }
+                } else {
+                    if (vmsSchFormType == eVmsScheduleType.eSchTp_evehicle && vmsForm.getVmsFormDsplDrctCd() == 1) {
+                        log.info("VmsManageService.makeVmsProvideForm: VMS {}, FORM {}, EVehicle End {}. {} DsplDrctCd {}",
+                                ctlrNmbr, vmsForm.getVmsFormId(), isEVehEndForm, vmsSchFormType, vmsForm.getVmsFormDsplDrctCd());
+                        continue;
+                    }
+                }
+
+                // 소통정보 표출폼 인 경우 표출할 구간의 소통정보가 존재하지 않는 경우 폼을 생성하지 않도록 한다.
+                int reqTraffic = 0;
+                int setTraffic = 0;
+                switch (vmsFormType) {
+                    case eFormTp_traf_1://   = 11,  //	소통상황(1단)
+                        reqTraffic = 1;
+                        break;
+                    case eFormTp_traf_2://   = 12,  //	소통상황(2단)
+                        reqTraffic = 2;
+                        break;
+                    case eFormTp_traf_3://   = 13,  //	소통상황(3단)
+                        reqTraffic = 3;
+                        break;
+                    case eFormTp_traf_4://   = 14,  //	소통상황(4단)
+                        reqTraffic = 4;
+                        break;
+                    default:
+                        break;
+                }
+                if (reqTraffic > 0) {
+                    int ss;
+                    Long[] vmsIfscId = { schedule.getFrstVmsIfscId(), schedule.getSecdVmsIfscId(), schedule.getThirVmsIfscId(), schedule.getFourVmsIfscId() };
+                    Long[] imgIfscId = { schedule.getFrstImgIfscId(), schedule.getSecdImgIfscId(), schedule.getThirImgIfscId(), schedule.getFourImgIfscId() };
+
+                    for (ss = 0; ss < 4; ss++) {
+                        if (vmsIfscId[ss] == 0L) {
+                            vmsIfscId[ss] = imgIfscId[ss];
+                        }
+                        if (vmsIfscId[ss] != 0L) {
+                            setTraffic++;
+                        }
+                    }
+                    if (reqTraffic > setTraffic) {
+                        //실제 소통정보 폼 갯수보다 할당된 구간이 적은 경우에 해당함
+                        reqTraffic = setTraffic;
+                    }
+                    setTraffic = 0;
+                    for (ss = 0; ss < 4; ss++) {
+                        if (vmsIfscId[ss] == 0L) continue;
+                        TbVmsIfscTrafDto ifscTraf = this.ifscService.find(vmsIfscId[ss]);
+                        if (ifscTraf != null && 0 != ifscTraf.getCmtrGradCd()) {
+                            setTraffic++;
+                        }
+                    }
+                    if (reqTraffic != setTraffic) {
+                        log.error("VmsManageService.makeVmsProvideForm: No Traffic: VMS({}), FormId({}), Req {}, Set {}",
+                                ctlrNmbr, schedule.getVmsFormId(), reqTraffic, setTraffic);
+                        continue;
+                    }
+                } // 1,2,3,4 단 소통정보 표출 폼인 경우
+
+                // 스케줄에 따른 VMS FORM 을 생성한다.
+                VmsForm form = vmsObj.getFormManager().addForm();
+                form.initDownload(vmsObj, vmsForm, schedule);
+                for (int objectIdx = 0; objectIdx < vmsForm.getObjects().size(); objectIdx++) {
+                    if (objectIdx >= VmsConstants.VMS_MAX_FORM_OBJECTS) {
+                        log.error("VmsManageService.makeVmsProvideForm: VMS({}), FormId({}), Form Object count overflow {} EA.",
+                                ctlrNmbr, schedule.getVmsFormId(), vmsForm.getObjects().size());
+                        break;
+                    }
+
+                    TbVmsFormObjectDto obj = vmsForm.getObjects().get(objectIdx);
+                    if (vmsFormType == eVmsFormType.eFormTp_figure) {
+                        // 도형식 배경소통정보 폼인 경우 하단에 표출할 정보가 없는 경우 하단 소통정보 객체는 생성하지 않도록 하자.
+                        switch(obj.geVmsFormObjectectTypeCd()) {
+                            case 0: //문자열
+                            case 1: //심볼
+                            case 2: //이미지
+                                //N:일반(모두표출하는것)
+                                //T:소통정보(소통정보가 있는 경우에만 표출하는것)
+                                //F:하단고정(소통정보가 없는 경우에만 표출하는것)
+                                if (schedule.getVmsIfscId() == 0L) {
+                                    // 표출할 소통정보가 없는 경우 교통정보 표출인 경우 하단정보 생성하지 않음
+                                    if ("T".equals(obj.getTrfcFillCd())) {
+                                        continue;
+                                    }
+                                }
+                                else {
+                                    // 표출할 소통정보가 있는 경우 고정문자 표출인 경우 하단정보 생성하지 않음
+                                    if ("F".equals(obj.getTrfcFillCd())) {
+                                        continue;
+                                    }
+                                }
+
+                                break;
+                            case 11: case 12: case 13: case 14: case 15: case 16: case 17: case 18: case 19:
+                                if (schedule.getVmsIfscId() == 0L) {
+                                    continue; //하단에 표출할 정보제공구간이 없는 경우임
+                                }
+                                if (obj.geVmsFormObjectectTypeCd() == 17) {
+                                    // 하단 소통정보이미지
+                                    schedule.setFrstImgIfscId(schedule.getVmsIfscId());
+                                }
+                                break;
+//                            case  1: // 심볼(소통정보 시점 종점 사이 화살표)
+//                                if ("->".equals(obj.getVmsDsplTxt().trim())) {
+//                                    if (schedule.getVmsIfscId() == 0L) {
+//                                        continue; //하단에 표출할 정보제공구간이 없는 경우임
+//                                    }
+//                                }
+//                                break;
+                            default: break;
+                        }
+                    } // (vmsFormType == eVmsFormType.eFormTp_figure)
+
+                    VmsFormObject formObj = form.addFormObject();
+                    formObj.setIfscId(0L);
+                    formObj.setTrfcFillCd(obj.getTrfcFillCd());
+                    switch(obj.geVmsFormObjectectTypeCd()) {
+                        case  1: //심볼
+                        case  2: //이미지
+                        case  17: //1단,소통정보이미지1
+                        case  27: //2단,소통정보이미지2
+                        case  37: //3단,소통정보이미지3
+                        case  47: //4단,소통정보이미지4
+                        case 167: //@우회소통정보이미지
+                        case 200: //동영상이미지
+                        case 300: //스트리밍영상이미지
+                        case 406: //@통합대기등급 이미지
+                        case 407: //@미세먼지등급 이미지
+                        case 408: //@초미세먼지등급 이미지
+                        case 413: //@오존등급이미지
+                            formObj.setObjectKind(eVmsFormObjectData.graphicID_object_data.getValue());
+                            break;
+                        case  3: //소통정보배경이미지
+                            formObj.setObjectKind(eVmsFormObjectData.graphic_object_data.getValue());
+                            break;
+                        default:
+                            formObj.setObjectKind(eVmsFormObjectData.text_object_data.getValue());
+                            break;
+                    }
+
+                    formObj.setFontInfo(this.fontService.getFontName(obj.getVmsFontNameCd()), obj);
+                    if (obj.geVmsFormObjectectTypeCd() == 201 || obj.geVmsFormObjectectTypeCd() == 301) {
+                        // 201:동영상명칭, 301:스트리밍영상주소
+                        formObj.setTextData(schedule.getStrmAddr());
+                    }
+                    else {
+                        formObj.setTextData(obj.getVmsDsplTxt());
+                    }
+
+                    if (obj.geVmsFormObjectectTypeCd() == 200 || obj.geVmsFormObjectectTypeCd() == 300) {
+                        formObj.setImageId(schedule.getSymbLibNmbr()+"0");
+                    }
+                    else {
+                        formObj.setImageId(obj.getSymbLibNmbr() + "0");
+                    }
+                    formObj.setImageType("0");
+                    if (obj.geVmsFormObjectectTypeCd() == 200) {
+                        // 200:동영상이미지, 300:스트리밍영상이미지
+                        formObj.setPosX(0);                 // NUMBER(5)	Y			VMS 표출 X좌표
+                        formObj.setPosY(0);                 // NUMBER(5)	Y			VMS 표출 Y좌표
+                        formObj.setDsplWidth(form.getWidth());  // NUMBER(5)	Y	0		VMS 표출 넓이
+                        formObj.setDsplHeight(form.getHeight());// NUMBER(5)	Y	0		VMS 표출 높이
+                        TbVmsSymbLibDto symb = this.symbService.find(formObj.getImageId());
+                        if (symb == null) {
+                            log.error("VmsManageService.makeVmsProvideForm: VMS({}), FormId({}), Form Object Video Symbol Not Found {}.",
+                                    ctlrNmbr, schedule.getVmsFormId(), formObj.getImageId());
+                        }
+                        else {
+                            form.setDsplHh(Math.toIntExact(symb.getPlayTm()));
+                        }
+                    }
+                    else {
+                        formObj.setTextData(obj.getVmsDsplTxt());
+                    }
+
+                    if (formObj.getObjectKind() != eVmsFormObjectData.text_object_data.getValue()) {
+                        if (obj.geVmsFormObjectectTypeCd() == 200 || obj.geVmsFormObjectectTypeCd() == 300) {
+                            // 200:동영상이미지, 300:스트리밍영상이미지
+                            TbVmsSymbLibDto symb = this.symbService.find(formObj.getImageId());
+                            if (symb != null) {
+                                formObj.setImageData(symb.getImageData());
+                            }
+                            else {
+                                formObj.setImageData(formObj.getImageData());
+                                log.error("VmsManageService.makeVmsProvideForm: VMS({}), FormId({}), Symbol Not Found {} set form object image data.",
+                                        ctlrNmbr, schedule.getVmsFormId(), formObj.getImageId());
+                            }
+                        }
+                        else if (obj.geVmsFormObjectectTypeCd() == 406 || //@통합대기등급 이미지
+                                 obj.geVmsFormObjectectTypeCd() == 407 || //@미세먼지등급 이미지
+                                 obj.geVmsFormObjectectTypeCd() == 408 || //@초미세먼지등급 이미지
+                                 obj.geVmsFormObjectectTypeCd() == 413 )  //@오존등급이미지
+                        {
+                            TbVmsAtmpDto atmp = this.atmpService.find(obj.getVmsIfscId());
+                            if (atmp != null && atmp.isSuccess()) {
+                                // 관측장소 정보가 존재하는 경우에만 대기환경이미지를 폼에 적용한다.
+                                int atmpGrad = 0;
+                                switch(obj.geVmsFormObjectectTypeCd())
+                                {
+                                    case 406: atmpGrad = ItsUtils.parseIntDef(atmp.getIntgAtmpGrad(), 1); break;
+                                    case 407: atmpGrad = ItsUtils.parseIntDef(atmp.getPm101hhGrad(), 1);  break;
+                                    case 408: atmpGrad = ItsUtils.parseIntDef(atmp.getPm251hhGrad(), 1);  break;
+                                    case 413: atmpGrad = ItsUtils.parseIntDef(atmp.getO3Grad(), 1);       break;
+                                }
+                                if (atmpGrad > 0) {
+                                    atmpGrad--;
+                                }
+                                TbVmsSymbLibDto symb = this.symbService.find(formObj.getImageId());
+                                if (symb != null && symb.getGradSymbLibNmbrList().size() >= atmpGrad) {
+                                    TbVmsSymbLibDto atmpSymb = this.symbService.find(symb.getGradSymbLibNmbrList().get(atmpGrad));
+                                    if (atmpSymb != null) {
+                                        formObj.setImageData(atmpSymb.getImageData());
+                                        formObj.setImageId(symb.getGradSymbLibNmbrList().get(atmpGrad));
+                                    }
+                                    else {
+                                        formObj.setImageData(symb.getImageData());
+                                        formObj.setImageId(formObj.getImageId());
+                                    }
+                                }
+                                else {
+                                    formObj.setImageData(formObj.getImageData());
+                                    log.error("VmsManageService.makeVmsProvideForm: VMS({}), FormId({}), Atmp Symbol Not Found {} set form object image data.",
+                                            ctlrNmbr, schedule.getVmsFormId(), formObj.getImageId());
+                                }
+                            }
+                        }
+                        else {
+                            formObj.setImageData(formObj.getImageData());
+                        }
+                    } // (formObj.getObjectKind() != eVmsFormObjectData.text_object_data.getValue())
+
+                    //폼객체 유형별 Fill In
+                    switch(formObj.getObjectType()) {
+                        case 0:     // 고정문자열
+                        case 201:   // 동영상파일명
+                        case 301:   // 스트리밍영상주소
+                            formObj.changeTextPosition();
+                            break;
+                        case 1:     // 심볼
+                        case 2:     // 파일이미지
+                            // Do nothing
+                            break;
+                        case 17:    // 1단,소통정보이미지1
+                        case 27:    // 2단,소통정보이미지2
+                        case 37:    // 3단,소통정보이미지3
+                        case 47:    // 4단,소통정보이미지4
+                        case 167:   // @우회소통정보이미지
+                            TbVmsSymbLibDto symb = this.symbService.find(formObj.getImageId());
+                            if (symb == null) {
+                                log.error("VmsManageService.makeVmsProvideForm: VMS({}), FormId({}), Not Found Traffic 1,2,3,4,detour Symbol {}.",
+                                        ctlrNmbr, schedule.getVmsFormId(), formObj.getImageId());
+                                continue;
+                            }
+                            formObj.setImageId(formObj.getImageId());
+                            formObj.setPosX(symb.getPosX());
+                            formObj.setPosY(symb.getPosY());
+
+                            switch(formObj.getObjectType()) {
+                                case 167: // @우회소통정보이미지
+                                case 17:  formObj.setIfscId(schedule.getFrstImgIfscId()); break;  // 1단,소통정보이미지1
+                                case 27:  formObj.setIfscId(schedule.getSecdImgIfscId()); break;  // 2단,소통정보이미지2
+                                case 37:  formObj.setIfscId(schedule.getThirImgIfscId()); break;  // 3단,소통정보이미지3
+                                case 47:  formObj.setIfscId(schedule.getFourImgIfscId()); break;  // 4단,소통정보이미지4
+                            }
+                            TbVmsIfscTrafDto ifscTraf = this.ifscService.find(formObj.getIfscId());
+                            if (ifscTraf != null) {
+                                formObj.setIfscTrafGradCd(ifscTraf.getCmtrGradCd());
+                                String trafImagId = symb.getOrgSymbLibNmbr() + String.valueOf(ifscTraf.getCmtrGradCd());
+                                TbVmsSymbLibDto trafSymb = this.symbService.find(trafImagId);
+                                if (trafSymb != null) {
+                                    formObj.setImageId(trafImagId);
+                                    formObj.setImageData(trafSymb.getImageData());
+                                }
+                                else {
+                                    formObj.setImageData(symb.getImageData());
+                                }
+                            }
+                            else {
+                                log.error("VmsManageService.makeVmsProvideForm: VMS({}), FormId({}), Not Found IFSC Traffic 1,2,3,4,detour IFSC {}.",
+                                        ctlrNmbr, schedule.getVmsFormId(), formObj.getIfscId());
+                                formObj.setIfscTrafGradCd(0);
+                                formObj.setImageData(symb.getImageData());
+                            }
+                            break;
+                        case 3:     // 소통정보 배경이미지
+                            TbVmsSymbLibDto bkSymb = this.symbService.find(formObj.getImageId());
+                            if (bkSymb == null) {
+                                log.error("VmsManageService.makeVmsProvideForm: VMS({}), FormId({}), Not Found Bk Traffic Symbol {}.",
+                                        ctlrNmbr, schedule.getVmsFormId(), formObj.getImageId());
+                                continue;
+                            }
+                            formObj.setImageId(formObj.getImageId());
+                            formObj.setPosX(bkSymb.getPosX());
+                            formObj.setPosY(bkSymb.getPosY());
+                            Image imgBmp = bkSymb.getImage();  // 원천이미지를 가지고 온다.
+                            if (imgBmp == null) {
+                                log.error("VmsManageService.makeVmsProvideForm: VMS({}), FormId({}), Image Data Error {}.",
+                                        ctlrNmbr, schedule.getVmsFormId(), formObj.getImageId());
+                                continue;
+                            }
+                            BufferedImage formImage = new BufferedImage(imgBmp.getWidth(null), imgBmp.getHeight(null), BufferedImage.TYPE_INT_RGB);
+                            Graphics2D 	g2d = formImage.createGraphics();
+                            FloodFill floodFill = new FloodFill(imgBmp);
+                            floodFill.setAntialiased(false);
+                            bkSymb.getCellMap().forEach((cellId, cell) -> {
+                                if (!cell.isValid()) {
+                                    return;
+                                }
+                                TbVmsIfscTrafDto cellTraf = this.ifscService.find(cell.getVmsIfscId());
+                                if (cellTraf == null) {
+                                    return;
+                                }
+                                int cmtrGradCd = cellTraf.getCmtrGradCd();
+                                int orgColor = floodFill.getImage().getRGB(cell.getPosX(), cell.getPosY());
+                                int trfColor = VmsSymbService.getGradColor(cmtrGradCd);
+                                if (orgColor != trfColor) {
+                                    floodFill.fill(cell.getPosX(), cell.getPosY(), VmsSymbService.getTrafColor(cmtrGradCd));
+                                }
+                            });
+                            g2d.drawImage(floodFill.getImage(), 0, 0, imgBmp.getWidth(null), imgBmp.getHeight(null), null);
+                            ByteArrayOutputStream out = new ByteArrayOutputStream();
+                            try {
+                                ImageIO.write(formImage, "bmp", out);
+                            } catch (IOException e) {
+                                log.error("VmsManageService.makeVmsProvideForm: Image Convert error: {}", formObj.getImageId());
+                            }
+                            g2d.dispose();
+                            formObj.setImageData(out.toByteArray());
+                            form.setLocalFileName(vmsObj.getLocalFormDir() + scheduleIdx + ".bmp");
+                            ItsUtils.saveByteArrayToFile(form.getLocalFileName(), formObj.getImageData());
+                            break;
+                        case 61:  // 구간명
+                        case 62:  // 시점명
+                        case 63:  // 종점명
+                        case 64:  // 돌발유형
+                        case 65:  // 대응문구
+                        case 66:  // 통제유형 ==> 여기까지 돌발
+                        case 71:  // 발생장소
+                        case 72:  // INCD_TITL
+                        case 73:  // 통제시점명
+                        case 74:  // 통제종점명
+                        case 75:  // 00차로 차단
+                        case 76:  // 00월00일 ~ 00월00일
+                        case 77:  // 대응문구
+                        case 78:  // @통제유형 ==> 여기까지 공사/행사
+                        case 81:  // 돌발종류
+                        case 82:  // 해당도로
+                        case 83:  // 우회도로
+                        case 84:  // 발생장소 ==> 여기까지 돌발
+                            // 돌발문안, 공사/행사문안 임
+                            // TB_VMS_FORM_OBJECT_TYPE 테이블에 유형추가하고 돌발화면에서 필요한 항목 추가, TB_INCD_OCRR_VMS 테이블에 컬럼추가해야함
+                            formObj.setTextData("");
+                            TbVmsIncdDto event = schedule.getEvent();
+                            if (event != null) {
+                                switch(formObj.getObjectType()) {
+                                    case 82: //해당도로
+                                    case 83: //우회도로
+                                    case 61: formObj.setTextData(event.getVmsIfscNm());         break; //@구간명(사용안함)
+                                    case 62: //@시점명
+                                    case 73: formObj.setTextData(event.getStrtLctnNm());        break; //통제시점명
+                                    case 74: //통제종점명
+                                    case 63: formObj.setTextData(event.getEndLctnNm());         break; //@종점명
+                                    case 81: //돌발종류
+                                    case 64: formObj.setTextData(event.getVmsIncdDetlNm());     break; //@돌발유형
+                                    case 66: //@통제유형
+                                    case 78: formObj.setTextData(event.getVmsIncdRstrTypeNm()); break; //@통제유형
+                                    case 65: //@대응문구(사용안함-발생장소로 일단넣음)
+                                    case 77: //대응문구(사용안함-발생장소로 일단넣음)
+                                    case 84: //발생장소
+                                    case 71: formObj.setTextData(event.getOcrrLctnNm());        break; //발생장소
+                                    case 72: formObj.setTextData(event.getIncdTitl());          break; //@공사행사명
+                                    case 75: // 00차로 차단
+                                        int nLane = ItsUtils.parseIntDef(event.getIncdClsrLane(), 0);
+                                        if (nLane == 111111)
+                                            formObj.setTextData("차로 차단");
+                                        else
+                                        if (nLane > 0)
+                                            formObj.setTextData("일부 차단");
+                                        else
+                                            formObj.setTextData(" ");
+                                        break;
+                                    case 76: //00월00일 ~ 00월00일
+                                        try {
+                                            String strtDt = event.getIncdStrtDt();
+                                            String endDt = event.getIncdEndPrarDt();
+                                            String sFrom = strtDt.substring(4,6) + "월" + strtDt.substring(6,8) + "일 " + strtDt.substring(8,10) + "시";
+                                            String sTo = endDt.substring(4,6) + "월" + endDt.substring(6,8) + "일 " + endDt.substring(8,10) + "시";
+                                            formObj.setTextData(sFrom + " ~ " + sTo);
+                                        } catch(Exception e) {}
+                                        break;
+                                }
+                            }
+                            if ("".equals(formObj.getTextData())) {
+                                formObj.setTextData(" ");
+                            }
+                            formObj.changeTextPosition();
+                            break;
+                        case 401: // @관측장소
+                        case 402: // @PM10
+                        case 403: // @PM2.5
+                        case 404: // @PM10등급
+                        case 405: // @PM2.5등급
+                        case 409: // @통합대기등급
+                        case 410: // @통합대기
+                        case 411: // @오존
+                        case 412: // @오존등급
+                            formObj.setTextData(" ");
+                            TbVmsAtmpDto atmp = this.atmpService.find(obj.getVmsIfscId());
+                            if (atmp == null) {
+                                log.error("VmsManageService.makeVmsProvideForm: VMS({}), FormId({}), Not Found Atmp {}.",
+                                        ctlrNmbr, schedule.getVmsFormId(), obj.getVmsIfscId());
+                            }
+                            else {
+                                String unitGap = "";
+                                boolean isUnit = false;
+                                if (formObj.getTextData().contains(" -")) {
+                                    isUnit = true;
+                                    unitGap = " ";
+                                } else if (formObj.getTextData().contains("-")) {
+                                    isUnit = true;
+                                }
+
+                                if (formObj.getObjectType() == 401) {
+                                    formObj.setTextData(atmp.getVmsDispNm());
+                                } else if (formObj.getObjectType() == 402) {
+                                    formObj.setTextData(atmp.getPm10ValDesc(unitGap, isUnit));
+                                } else if (formObj.getObjectType() == 403) {
+                                    formObj.setTextData(atmp.getPm25ValDesc(unitGap, isUnit));
+                                } else if (formObj.getObjectType() == 404) {
+                                    formObj.setTextData(atmp.getPm10GradDesc());
+                                } else if (formObj.getObjectType() == 405) {
+                                    formObj.setTextData(atmp.getPm25GradDesc());
+                                } else if (formObj.getObjectType() == 409) {
+                                    formObj.setTextData(atmp.getAtmpGradDesc());
+                                } else if (formObj.getObjectType() == 410) {
+                                    formObj.setTextData(atmp.getAtmpValDesc());
+                                } else if (formObj.getObjectType() == 411) {
+                                    formObj.setTextData(atmp.getO3ValDesc(unitGap, isUnit));
+                                } else if (formObj.getObjectType() == 412) {
+                                    formObj.setTextData(atmp.getO3GradDesc());
+                                }
+                            }
+                            if ("".equals(formObj.getTextData())) {
+                                formObj.setTextData(" ");
+                            }
+                            formObj.changeTextPosition();
+                            break;
+                        case 101: // @주차장명
+                        case 102: // @주차면수
+                        case 103: // @주차가능면수
+                        case 104: // @주차혼잡도
+                            TbVmsParkDto park = this.parkService.find(obj.getVmsIfscId());
+                            if (park == null) {
+                                formObj.setTextData("-");
+                                formObj.setFontColor(eVmsColor.color_red.getValue());
+
+                                log.error("VmsManageService.makeVmsProvideForm: VMS({}), FormId({}), Not Found Parking {}.",
+                                        ctlrNmbr, schedule.getVmsFormId(), obj.getVmsIfscId());
+                            }
+                            else {
+                                String textFmt = obj.getVmsDsplTxt().replaceAll("@", "");
+                                switch(formObj.getObjectType()) {
+                                    case 101:// || //@주차장명
+                                        formObj.setTextData(park.getParkName());
+                                        break;
+                                    case 102:// || //@주차면수
+                                        formObj.setTextData(park.getTotalCo(textFmt));
+                                        break;
+                                    case 103:// || //@주차가능면수
+                                        formObj.setTextData(park.getRemainCo(textFmt));
+                                        formObj.setFontColor(park.getGradeColorCd());
+                                        break;
+                                    case 104://    //@주차혼잡도
+                                        formObj.setTextData(park.getParkingCngstSttsDesc());
+                                        formObj.setFontColor(park.getGradeColorCd());
+                                        break;
+                                }
+                            }
+                            if ("".equals(formObj.getTextData())) {
+                                formObj.setTextData(" ");
+                            }
+                            formObj.changeTextPosition();
+                            break;
+                        default:
+                            //객체가 가변(소통정보 표출) 인 경우 가변문자를 설정하자....
+                            Long vmsIfscId = 0L;
+                            switch(formObj.getObjectType()) {
+                                case 11: case 12: case 13: case 14: case 15: case 16: case 18: case 19:
+                                    vmsIfscId = schedule.getFrstVmsIfscId();
+                                    if (form.getVmsFormTypeCd() == eVmsFormType.eFormTp_figure.getValue() || form.getVmsFormTypeCd() == eVmsFormType.eFormTp_congest.getValue()) {
+                                        vmsIfscId = schedule.getVmsIfscId();
+                                    }
+                                    break;
+                                case 161: case 162: case 163: case 164: case 165: case 166: case 168:
+                                case 21: case 22: case 23: case 24: case 25: case 26: case 28:
+                                    vmsIfscId = schedule.getSecdVmsIfscId();
+                                    break;
+                                case 31: case 32: case 33: case 34: case 35: case 36: case 38:
+                                    vmsIfscId = schedule.getThirVmsIfscId();
+                                    break;
+                                case 41: case 42: case 43: case 44: case 45: case 46: case 48:
+                                    vmsIfscId = schedule.getFourVmsIfscId();
+                                    break;
+                                case 91: case 92:   //@축통행시간(고정), @축소통상황(고정)
+                                case 93: case 94: case 95: case 96: // @축시점명(순환), @축종점명(순환), @축소통상황(순환), @축통행시간(순환)
+                                    vmsIfscId = schedule.getVmsIfscId();
+                                    break;
+                                default: continue;
+                            }
+                            formObj.setTextData("");
+                            ifscTraf = this.ifscService.find(vmsIfscId);
+                            if (ifscTraf == null) {
+                                log.error("VmsManageService.makeVmsProvideForm: VMS({}), FormId({}), Traffic IFSC Not Found {}.",
+                                        ctlrNmbr, schedule.getVmsFormId(), vmsIfscId);
+                                continue;
+                            }
+                            switch(formObj.getObjectType()) {
+                                case 11: case 21: case 31: case 41: case 161: //구간명
+                                    formObj.setTextData(ifscTraf.getRoadNm());
+                                    break;
+                                case 12: case 22: case 32: case 42: case 93: case 162: //시점명
+                                    formObj.setTextData(ifscTraf.getDsplStrtNodeNm());
+                                    break;
+                                case 13: case 23: case 33: case 43: case 94: case 163: //종점명
+                                    formObj.setTextData(ifscTraf.getDsplEndNodeNm());
+                                    break;
+                                case 14: case 24: case 34: case 44: case 92: case 95: case 164: //소통상황
+                                    formObj.setTextData(trafGradeDesc(form.getVmsFormTypeCd(), ifscTraf.getCmtrGradCd()));
+                                    break;
+                                case 19:    //소통상황(가변)
+                                    String gradText = trafGradeDesc(form.getVmsFormTypeCd(), ifscTraf.getCmtrGradCd());
+                                    if (ifscTraf.getCmtrGradCd() == eTrafficGrade.grade_smoothness.getValue()) {
+                                        gradText = String.format("소통원활  약 %d 분", ifscTraf.getTrvlHh());
+                                    }
+                                    formObj.setTextData(gradText);
+                                    formObj.setFontColor(gradeToColorCode(ifscTraf.getCmtrGradCd()));
+                                    break;
+                                case 15: case 25: case 35: case 45: case 91: case 96: case 165: //통행시간
+                                    if (ifscTraf.getTrvlHh() > 0) {
+                                        String textFmt = obj.getVmsDsplTxt().replace("@", "");
+                                        if (textFmt.contains("0")) {
+                                            textFmt = textFmt.replace("0000", "%4d");
+                                            textFmt = textFmt.replace("000", "%3d");
+                                            textFmt = textFmt.replace("00", "%2d");
+                                            textFmt = textFmt.replace("0", "%d");
+                                            String textData = String.format(textFmt, ifscTraf.getTrvlHh());
+                                            formObj.setTextData(textData);
+                                        }
+                                        else {
+                                            formObj.setTextData(String.valueOf(ifscTraf.getTrvlHh()));
+                                        }
+                                        formObj.setFontColor(gradeToColorCode(ifscTraf.getCmtrGradCd()));
+                                    }
+                                    else
+                                    {
+                                        formObj.setTextData(" ");
+                                        formObj.setFontColor(eVmsColor.color_black.getValue());
+                                    }
+                                    break;
+                                case 16: case 26: case 36: case 46: case 166: //통행속도
+                                    formObj.setTextData(String.valueOf(ifscTraf.getSped()));
+                                    formObj.setFontColor(gradeToColorCode(ifscTraf.getCmtrGradCd()));
+                                    break;
+                                case 17: case 27: case 37: case 47: //방향이미지...???
+                                    break;
+                                case 18: case 28: case 38: case 48: case 168: //지점명
+                                    formObj.setTextData(ifscTraf.getSpotNm());
+                                    break;
+                                default: break;
+                            }
+                            formObj.changeTextPosition();
+                            break;
+                    }
+                } // (ii = 0; ii < vmsForm.getObjects().size()
+            } // (int ii = 0; ii < scheObj.size(); ii++)
+        });
+        log.info("VmsManageService.makeVmsProvideForm: {} ms.", elapsed.milliSeconds());
     }
 
+    /**
+     * 데이터베이스에 저장할 폼 이미지를 생성한다.
+     */
+    public void makeVmsDatabaseForm() {
+        Elapsed elapsed = new Elapsed();
+        this.repoService.getCtlrMap().forEach((ctlrNmbr, vmsObj) -> {
+            if ("Y".equals(vmsObj.getDelYn())) {
+                return;
+            }
+            if (this.config.isFigureTrafficCenter()) {
+                //도형식폼의 하단소통정보위치를 가운데로 정렬하는 경우
+                makeVmsFigureFormPos(vmsObj);
+            }
+            if (this.config.isTextTrafficCenter() && "VMP2".equals(vmsObj.getTypeCd()))    //2단 12열 일 경우 정렬
+            {
+                //1단교통정보의 이정을 가운데로 정렬하는 경우
+                makeVmsTextFormPos(vmsObj);
+            }
+        });
+        log.info("VmsManageService.makeVmsDatabaseForm: {} ms.", elapsed.milliSeconds());
+    }
+
+    public void makeVmsFigureFormPos(TbVmsCtlrDto vmsObj) {
+        int arrowGap = 7;
+        int trafGap = 17;
+        if (vmsObj == null) {
+            return;
+        }
+        int formCnt = vmsObj.getFormManager().count();
+        if (formCnt == 0) {
+            log.error("VmsManagerService.makeVmsFigureFormPos:VMS {}, Form Data Not Found.", vmsObj.getCtlrNmbr());
+            return;
+        }
+        if (formCnt > vmsObj.getMaxPhaseNum()) {
+            log.error("VmsManagerService.makeVmsFigureFormPos:VMS {}, Form Count Over: {}/{} EA.", vmsObj.getCtlrNmbr(), formCnt, vmsObj.getMaxPhaseNum());
+            formCnt = vmsObj.getMaxPhaseNum();
+        }
+
+        int objCnt;
+        int formT;
+        VmsFormObject pArw;
+        VmsFormObject pStr;
+        VmsFormObject pEnd;
+        VmsFormObject pTrf;
+        int totW;
+        int left;
+
+        for (int ii = 0; ii < formCnt; ii++) {
+            VmsForm pForm = vmsObj.getFormManager().getItem(ii);
+            if (pForm.getVmsFormTypeCd() != eVmsFormType.eFormTp_figure.getValue()) {
+                //도형식배경소통정보 폼이 아닌경우 계산하지 않음
+                continue;
+            }
+
+            formT = pForm.getHeight() - 40; //하단소통정보 객체의 최대높이를 설정, 이것보다 작으면 위치조정 하지 않음
+            pArw = null;
+            pStr = null;
+            pEnd = null;
+            pTrf = null;
+
+            //소통상황 시점 -> 종점
+            //시점 -> 종점 소통상황
+            //위의 2가지 유형에 대하여 가운데 정렬
+            objCnt = pForm.count();
+            for (int jj = 0; jj < objCnt; jj++) {
+                VmsFormObject pFormObj = pForm.getItem(jj);
+                if (pFormObj.getPosY() < formT)
+                    continue;
+
+                switch(pFormObj.getObjectType()) {
+                    case  1:    //	심볼	            Y
+                    case  2:    //	이미지	            Y
+                        if ("T".equals(pFormObj.getTrfcFillCd()))
+                            pArw = pFormObj;
+                        break;
+                    case 17:    //	@소통정보이미지	    N
+                        pArw = pFormObj;
+                        break;
+                    case 12:    //	@시점명	            Y
+                    case 93:    //	@축시점명(순환)	    Y
+                    case 13:    //	@종점명	            Y
+                    case 94:    //	@축종점명(순환)	    Y
+                        if (pFormObj.getObjectType() == 12 || pFormObj.getObjectType() == 93)
+                            pStr = pFormObj;
+                        else
+                            pEnd = pFormObj;
+                        break;
+                    case 14:    //	@소통상황	        Y
+                    case 15:    //	@통행시간	        Y
+                    case 16:    //	@통행속도	        N
+                    case 95:    //	@축소통상황(순환)	Y
+                    case 96:    //	@축통행시간(순환)	Y
+                        pTrf = pFormObj;
+                        break;
+                    default: break;
+                }
+            }
+
+            if (pArw != null && pStr != null && pEnd != null && pTrf != null) {
+                int totalWidth = pStr.getTextWidth() + pArw.getTextWidth() + pEnd.getTextWidth() + pTrf.getTextWidth();
+                if (totalWidth > (pForm.getWidth() - arrowGap - arrowGap - trafGap - 10) ) {
+                    arrowGap =  3;
+                    trafGap = 10;
+                }
+                totW = pStr.getTextWidth() + arrowGap + pArw.getTextWidth() + arrowGap + pEnd.getTextWidth() + trafGap + pTrf.getTextWidth();
+                left = (pForm.getWidth() - totW) / 2;
+                if (pStr.getPosX() < pTrf.getPosX()) {
+                    //시점 -> 종점 소통상황
+                    pStr.setPosX(left);
+                    pArw.setPosX(pStr.getPosX() + pStr.getTextWidth() + arrowGap);
+                    pEnd.setPosX(pArw.getPosX() + pArw.getTextWidth() + arrowGap);
+                    pTrf.setPosX(pEnd.getPosX() + pEnd.getTextWidth() + trafGap);
+                }
+                else {
+                    //소통상황 시점 -> 종점
+                    pTrf.setPosX(left);
+                    pStr.setPosX(pTrf.getPosX() + pTrf.getTextWidth() + trafGap);
+                    pArw.setPosX(pStr.getPosX() + pStr.getTextWidth() + arrowGap);
+                    pEnd.setPosX(pArw.getPosX() + pArw.getTextWidth() + arrowGap);
+                }
+            }
+        }
+    }
+
+    public void makeVmsTextFormPos(TbVmsCtlrDto vmsObj) {
+        // 시점 화살표 종점 의 좌표는 폼생성할때 정의된데로 하고
+        // 시점 화살표 종점의 전체 길이를 폼의 넓이로 환산하여 가운데 정렬
+        // 시점(오른쪽정렬) 화살표(의미없고) 종점(왼쪽정렬) 로 폼설정에서 설정하도록 한다.
+        // 시점/화살표/종점의 Y축 좌표도 설정한 데로 표출한다.
+        // 시점/화살표/종점을 폼의 가운데에 정렬하는 것이다.
+        if (vmsObj == null) {
+            return;
+        }
+        int formCnt = vmsObj.getFormManager().count();
+        if (formCnt == 0) {
+            log.error("VmsManagerService.makeVmsFigureFormPos:VMS {}, Form Data Not Found.", vmsObj.getCtlrNmbr());
+            return;
+        }
+        if (formCnt > vmsObj.getMaxPhaseNum()) {
+            log.error("VmsManagerService.makeVmsFigureFormPos:VMS {}, Form Count Over: {}/{} EA.", vmsObj.getCtlrNmbr(), formCnt, vmsObj.getMaxPhaseNum());
+            formCnt = vmsObj.getMaxPhaseNum();
+        }
+
+        int objCnt;
+        int gapL;
+        VmsFormObject pArw;
+        VmsFormObject pStr;
+        VmsFormObject pEnd;
+        int totW;
+        int left;
+
+        for (int ii = 0; ii < formCnt; ii++) {
+            VmsForm pForm = vmsObj.getFormManager().getItem(ii);
+            if (pForm.getVmsFormTypeCd() != eVmsFormType.eFormTp_traf_1.getValue()) {
+                //소통상황(1단) 폼이 아닌경우 계산하지 않음
+                continue;
+            }
+
+            pArw = null;
+            pStr = null;
+            pEnd = null;
+
+            // 시점(12) -> 종점(13)
+            objCnt = pForm.count();
+            for (int jj = 0; jj < objCnt; jj++) {
+                VmsFormObject pFormObj = pForm.getItem(jj);
+                switch(pFormObj.getObjectType()) {
+                    case 12:    //	@시점명	            Y
+                        pStr = pFormObj;
+                        break;
+                    case 13:    //	@종점명	            Y
+                        pEnd = pFormObj;
+                        break;
+                    default: break;
+                }
+            }
+            if (pStr == null || pEnd == null) {
+                continue;
+            }
+            for (int jj = 0; jj < objCnt; jj++) {
+                VmsFormObject pFormObj = pForm.getItem(jj);
+                switch(pFormObj.getObjectType()) {
+                    case  1:    //	@심볼	            Y
+                    case  2:    //	@이미지	            Y
+                    case 17:    //	@소통정보이미지      Y
+                        //if (pFormObj->TRFC_FILL_CD == "T")
+                        if (pFormObj.getPosY() <= (pStr.getPosY()+pStr.getDsplHeight())) {
+                            pArw = pFormObj;
+                        }
+                        break;
+                    default: break;
+                }
+            }
+
+            if (pArw != null && pStr != null && pEnd!= null ) {
+                //시점 화살표 종점 순으로 되어 있어야 한다.
+                int nRange = pStr.getPosY() + pStr.getDsplHeight();
+                if (pArw.getPosY() <= nRange && pEnd.getPosY() <= nRange)
+                {
+                    gapL = pArw.getPosX()- (pStr.getPosX()+ pStr.getTextWidth());   // 시점 화살표 사이의 간격
+                    totW = (pEnd.getPosX()+ pEnd.getTextWidth()) - pStr.getPosX(); // 전체길이
+                    left = (pForm.getWidth() - totW) / 2;
+
+                    pStr.setPosX(left);
+                    pArw.setPosX(pStr.getPosX()+ pStr.getTextWidth() + gapL);
+                    pEnd.setPosX((left + totW) - pEnd.getTextWidth());
+                }
+            }
+        }
+    }
+
+
+    public String trafGradeDesc(int formType, int cmtrGradCd) {
+        if (formType == eVmsFormType.eFormTp_figure.getValue()) {
+            switch(cmtrGradCd) {
+                case 1: return this.config.getFigureTrafGrad1();
+                case 2: return this.config.getFigureTrafGrad2();
+                case 3: return this.config.getFigureTrafGrad3();
+                default: break;
+            }
+        }
+        else {
+            switch(cmtrGradCd) {
+                case 1: return this.config.getTextTrafGrad1();
+                case 2: return this.config.getTextTrafGrad2();
+                case 3: return this.config.getTextTrafGrad3();
+                default: break;
+            }
+        }
+        return " ";
+    }
+
+    public int gradeToColorCode(int cmtrGradCd) {
+        switch(cmtrGradCd) {
+            case 1: return eVmsColor.color_green.getValue();
+            case 2: return eVmsColor.color_amber.getValue();
+            case 3: return eVmsColor.color_red.getValue();
+            default: break;
+        }
+        return eVmsColor.color_green.getValue();
+    }
 }

+ 3 - 0
src/main/java/com/its/vms/service/VmsParkService.java

@@ -1,5 +1,6 @@
 package com.its.vms.service;
 
+import com.its.app.utils.Elapsed;
 import com.its.vms.dao.mapper.VmsParkMapper;
 import com.its.vms.dto.TbVmsParkDto;
 import com.its.vms.entity.TbVmsPark;
@@ -28,6 +29,7 @@ public class VmsParkService {
     }
 
     public void loadVmsParkInfo() {
+        Elapsed elapsed = new Elapsed();
         try {
             List<TbVmsPark> infoList  = this.mapper.selectVmsParkInfo();
             log.info("VmsParkService.loadVmsParkInfo: {} EA", infoList.size());
@@ -43,6 +45,7 @@ public class VmsParkService {
         catch (Exception e) {
             log.error("VmsParkService.loadVmsParkInfo: {}", e.toString());
         }
+        log.info("VmsParkService.loadVmsParkInfo: {} ms.", elapsed.milliSeconds());
     }
 
 }

+ 13 - 3
src/main/java/com/its/vms/service/VmsSymbService.java

@@ -1,5 +1,6 @@
 package com.its.vms.service;
 
+import com.its.app.utils.Elapsed;
 import com.its.app.utils.FloodFill;
 import com.its.app.utils.ItsUtils;
 import com.its.vms.config.ApplicationConfig;
@@ -42,6 +43,7 @@ public class VmsSymbService {
     }
 
     public void loadVmsSymbLib() {
+        Elapsed elapsed = new Elapsed();
         try {
             List<TbVmsSymbLib> infoList  = this.mapper.selectVmsSymbLib();
             log.info("VmsSymbService.loadVmsSymbLib: {} EA", infoList.size());
@@ -118,7 +120,7 @@ public class VmsSymbService {
                             log.error("VmsSymbService.loadVmsSymbLib: Image Convert error: {}", trafSymbLibNmbr);
                         }
                         g2d.dispose();
-                        trfObj.setImagData(out.toByteArray());
+                        trfObj.setImageData(out.toByteArray());
 
                         trfObj.setLocalFileName(this.config.getFtpImageDir() + trfObj.getSymbFileNm());
                         trfObj.setFtpFileName(ApplicationConfig.FTP_IMAGE + File.separator + trfObj.getSymbFileNm()); // Ftp 다운로드 명을 설정
@@ -126,7 +128,12 @@ public class VmsSymbService {
                         saveByteArrayToFile(trfObj, trfUpdtDt);
                     }
                 }
-
+                if ("SBT1".equals(obj.getSymbType())) {
+                    // 소통정보배경
+                    obj.getCellMap().forEach((id, cell) -> {
+                        cell.setValid(false);
+                    });
+                }
                 // 이미지 파일저장
                 if (!"SBT3".equals(obj.getSymbType())) {
                     saveByteArrayToFile(obj, updtDt);
@@ -136,9 +143,11 @@ public class VmsSymbService {
         catch (Exception e) {
             log.error("VmsSymbService.loadVmsSymbLib: {}", e.toString());
         }
+        log.info("VmsSymbService.loadVmsSymbLib: {} ms.", elapsed.milliSeconds());
     }
 
     public void loadVmsSymbCellInfo() {
+        Elapsed elapsed = new Elapsed();
         try {
             List<TbVmsSymbIfsc> infoList  = this.mapper.selectVmsSymbCellInfo();
             log.info("VmsSymbService.loadVmsSymbCellInfo: {} EA", infoList.size());
@@ -162,6 +171,7 @@ public class VmsSymbService {
         catch (Exception e) {
             log.error("VmsSymbService.loadVmsSymbCellInfo: {}", e.getMessage());
         }
+        log.info("VmsSymbService.loadVmsSymbCellInfo: {} ms.", elapsed.milliSeconds());
     }
 
     public void saveByteArrayToFile(TbVmsSymbLibDto obj, String updtDt) {
@@ -171,7 +181,7 @@ public class VmsSymbService {
                 boolean deleted = imagFile.delete();
                 log.info("VmsSymbService.saveImageFile: Old file deleted {}, {}", obj.getLocalFileName(), deleted);
             }
-            ItsUtils.saveByteArrayToFile(obj.getLocalFileName(), obj.getImagData());
+            ItsUtils.saveByteArrayToFile(obj.getLocalFileName(), obj.getImageData());
         }
         obj.setUpdtDt(updtDt);  // 업데이트 시각을 업데이트 한다.
     }

+ 11 - 1
src/main/resources/application.yml

@@ -5,11 +5,21 @@ application:
   user-id: admin
   user-pswd: 1234
   listen-port: 3001
-  max-phase: 12
   ftp-home-dir: C:\DRIVE_E\ANDONG_VMS_FTP
   load-db: true
+  max-download-forms: 10
   cngst-cont-count: 2
   max-cngst-forms: 5
+  bottom-traffic-max: 3
+  bottom-traffic-cycle: 3
+  figure-traffic-center: true
+  text-traffic-center: true
+  figure-traf-grad1: 원활
+  figure-traf-grad2: 지체
+  figure-traf-grad3: 정체
+  text-traf-grad1: 소통원활
+  text-traf-grad2: 지 체
+  text-traf-grad3: 정 체
 
 communication:
   listen-port: 30200

+ 4 - 5
src/main/resources/mybatis/mapper/VmsFontMapper.xml

@@ -5,9 +5,9 @@
 
     <select id="selectFontName" resultType="com.its.vms.entity.TbVmsFontName">
     <![CDATA[
-        select t1.vms_font_name_cd AS VMS_FONT_NAME_CD,
-               t1.vms_font_name_nm AS VMS_FONT_NAME_NM,
-               t1.use_yn           AS USE_YN
+        select t1.vms_font_name_cd AS vmsFontNameCd,
+               t1.vms_font_name_nm AS vmsFontNameNm,
+               t1.use_yn           AS useYn
         from tb_vms_font_name t1
         ]]>
     </select>
@@ -15,8 +15,7 @@
     <![CDATA[
         select t1.vms_font_colr_cd  AS vmsFontColrCd,
                t1.vms_font_colr_nm  AS vmsFontColrNm,
-               t1.vms_font_colr_val AS vmsFontColrVal,
-               t1.use_yn            AS useYn
+               t1.vms_font_colr_val AS vmsFontColrVal
         from tb_vms_font_colr t1
         ]]>
     </select>

+ 13 - 4
src/main/resources/mybatis/mapper/VmsFormMapper.xml

@@ -35,7 +35,6 @@
                t1.vms_font_size           AS vmsFontSize,
                t1.vms_font_align          AS vmsFontAlign,
                t1.vms_dspl_txt            AS vmsDsplTxt,
-               t1.vms_dspl_figr           AS vmsDsplFigr,
                t1.vms_dspl_xcrdn          AS vmsDsplXcrdn,
                t1.vms_dspl_ycrdn          AS vmsDsplYcrdn,
                t1.vms_dspl_width          AS vmsDsplWidth,
@@ -43,13 +42,23 @@
                t1.vms_dspl_blinking       AS vmsDsplBlinking,
                t1.vms_dspl_bkcolor        AS vmsDsplBkColor,
                t1.vms_dspl_size           AS vmsDsplSize,
-               t1.trfc_fill_cd            AS trfcFillCd,
-               t1.symb_lib_nmbr           AS symbLibNmbr,
-               t1.vms_ifsc_id             AS vmsIfscId
+               nvl(t1.trfc_fill_cd, 'N')  AS trfcFillCd,
+               nvl(t1.symb_lib_nmbr, 0)   AS symbLibNmbr,
+               nvl(t1.vms_ifsc_id, 0)     AS vmsIfscId
         from tb_vms_form_object t1, tb_vms_form t2, tb_vms_symb_lib t3
         where t1.vms_form_id = t2.vms_form_id
           and t1.symb_lib_nmbr = t3.symb_lib_nmbr(+)
         order by t1.vms_form_id, t1.vms_form_object_id
         ]]>
     </select>
+
+    <select id="selectVmsFormColorInfo" resultType="com.its.vms.entity.TbVmsFormColr">
+    <![CDATA[
+        select t1.vms_form_colr_cd  AS vmsFormColrCd,
+               t1.vms_form_colr_nm  AS vmsFormColrNm,
+               t1.vms_form_colr_val AS vmsFormColrVal
+        from tb_vms_form_colr t1
+        ]]>
+    </select>
+
 </mapper>

+ 6 - 3
src/test/java/com/its/app/VmsCommServerApplicationTests.java

@@ -1,16 +1,19 @@
 package com.its.app;
 
-import com.its.vms.VmsCommServerApplication;
 import lombok.extern.slf4j.Slf4j;
 import org.junit.jupiter.api.Test;
-import org.springframework.boot.test.context.SpringBootTest;
 
 @Slf4j
-@SpringBootTest(classes = VmsCommServerApplication.class)
+//@SpringBootTest(classes = VmsCommServerApplication.class)
 public class VmsCommServerApplicationTests {
 
     @Test
     void requestObuStatusInfo() {
+        //                 0123 45 67 89 0123
+        String testData = "20230102034000";
+        log.info("{}", testData.length());
+        String sFrom = testData.substring(4,6) + "월" + testData.substring(6,8) + "일 " + testData.substring(8,10) + "시";
+        log.info("{}", sFrom);
     }
 
     @Test