shjung 2 år sedan
förälder
incheckning
a962bfd77b
24 ändrade filer med 372 tillägg och 99 borttagningar
  1. 25 2
      src/main/java/com/its/pis/PisCommServerApplication.java
  2. 70 9
      src/main/java/com/its/pis/entity/TbPisInfr.java
  3. 1 1
      src/main/java/com/its/pis/scheduler/SchedulerTask.java
  4. 1 1
      src/main/java/com/its/pis/service/PisInfrService.java
  5. 2 2
      src/main/java/com/its/pis/ui/MainUI.form
  6. 21 22
      src/main/java/com/its/pis/ui/MainUI.java
  7. 4 4
      src/main/java/com/its/pis/webapp/controller/WebAppController.java
  8. 18 0
      src/main/java/com/its/pis/websocket/C2F/message/C2FMessageRequest.java
  9. 2 3
      src/main/java/com/its/pis/websocket/C2F/message/PrkPlceOprInfo.java
  10. 3 1
      src/main/java/com/its/pis/websocket/C2F/message/PrkPlceSttusInfo.java
  11. 0 3
      src/main/java/com/its/pis/websocket/C2F/message/prk_plce_rl_time/PrkColctDeviceInfo.java
  12. 3 0
      src/main/java/com/its/pis/websocket/C2F/message/prk_plce_rl_time/RtArInfr.java
  13. 4 4
      src/main/java/com/its/pis/websocket/C2F/message/prk_plce_sttus/ArInfr.java
  14. 1 1
      src/main/java/com/its/pis/websocket/C2F/message/prk_plce_sttus/TypedPrkColctDeviceQty.java
  15. 2 0
      src/main/java/com/its/pis/websocket/ItsWebSocketConfig.java
  16. 113 43
      src/main/java/com/its/pis/websocket/ItsWebSocketHandler.java
  17. 33 0
      src/main/java/com/its/pis/websocket/ItsWebSocketHandshakeInterceptor.java
  18. 5 2
      src/main/java/com/its/pis/websocket/ItsWebSocketSession.java
  19. 14 0
      src/main/java/com/its/pis/websocket/common/PisSubscribeIdentifier.java
  20. 16 0
      src/main/java/com/its/pis/websocket/common/PisSubscribeRequest.java
  21. 17 0
      src/main/java/com/its/pis/websocket/common/PisSubscribeResponseAccept.java
  22. 16 0
      src/main/java/com/its/pis/websocket/common/PisSubscribeResponseReject.java
  23. 1 1
      src/main/java/com/its/pis/xnettcp/center/handler/CenterTcpServerInboundHandler.java
  24. BIN
      src/main/resources/application.png

+ 25 - 2
src/main/java/com/its/pis/PisCommServerApplication.java

@@ -23,6 +23,7 @@ import org.springframework.context.ApplicationListener;
 import org.springframework.context.annotation.ComponentScan;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.context.event.ContextClosedEvent;
+import org.springframework.core.io.ClassPathResource;
 import org.springframework.scheduling.annotation.EnableAsync;
 import org.springframework.web.bind.annotation.CrossOrigin;
 
@@ -31,7 +32,9 @@ import java.awt.*;
 import java.awt.event.WindowAdapter;
 import java.awt.event.WindowEvent;
 import java.io.File;
+import java.io.IOException;
 import java.io.PrintStream;
+import java.net.URL;
 
 @Slf4j
 @EnableAsync
@@ -69,12 +72,32 @@ public class PisCommServerApplication implements CommandLineRunner, ApplicationL
     @Override
     public void run(String... args) throws Exception {
         SwingUtilities.invokeLater(() -> {
-            String pathOfImage = "C:\\DEV\\ITS\\01.WINDOWS\\22.05.GUMI\\JAVA\\pis-comm-server\\src\\main\\resources\\static\\image\\application.png";
             String sysTime = SysUtils.getSysTimeStr();
             //JFrame.setDefaultLookAndFeelDecorated(true);
             JFrame frame = new JFrame("PIS 통신 서버 - [" + sysTime + "]");
             MainUI UI = new MainUI(frame);
-            frame.setIconImage(Toolkit.getDefaultToolkit().getImage(pathOfImage));
+
+            try {
+                ClassPathResource file = new ClassPathResource("static/image/application.png");
+                URL imgURL = file.getURL();
+                frame.setIconImage(new ImageIcon(imgURL).getImage());
+            } catch (IOException e) {
+                log.error("Not found application icon image");
+            }
+//            URL basePath = frame.getClass().getClassLoader().getResource("/static");
+//            log.error("basePath: {}", basePath);
+//            Image image = new ImageIcon(Toolkit.getDefaultToolkit().getClass().getResource("/resources/static/image/application.png")).getImage();
+//frame.setIconImage(image);
+//frame.setIconImage(new ImageIcon("resources/static/image/application.png").getImage());
+//            URL imgURL = frame.getClass().getResource("/static/image/application.png");
+//            log.error("{}", imgURL);
+//            if (imgURL != null) {
+//                frame.setIconImage(new ImageIcon(imgURL).getImage());
+//            }
+//frame.setIconImage(new ImageIcon(Objects.requireNonNull(frame.getClass().getResource("/static/image/application.png"))));
+
+//            String pathOfImage = "C:\\DEV\\ITS\\01.WINDOWS\\22.05.GUMI\\JAVA\\pis-comm-server\\src\\main\\resources\\static\\image\\application.png";
+//            frame.setIconImage(Toolkit.getDefaultToolkit().getImage(pathOfImage));
             frame.setModalExclusionType(Dialog.ModalExclusionType.APPLICATION_EXCLUDE);
             frame.setContentPane(UI.getRootPanel());
             frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

+ 70 - 9
src/main/java/com/its/pis/entity/TbPisInfr.java

@@ -1,13 +1,22 @@
 package com.its.pis.entity;
 
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
 import com.its.app.utils.SysUtils;
 import com.its.pis.domain.NET;
+import com.its.pis.ui.MainUI;
+import com.its.pis.websocket.C2F.message.C2FMessage;
+import com.its.pis.websocket.C2F.message.C2FMessageData;
+import com.its.pis.websocket.C2F.message.C2FMessageRequest;
+import com.its.pis.websocket.ItsWebSocketSession;
 import io.netty.channel.Channel;
 import lombok.Getter;
 import lombok.Setter;
 import lombok.ToString;
 import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.socket.TextMessage;
 
+import java.io.IOException;
 import java.net.InetSocketAddress;
 import java.util.Collections;
 import java.util.HashMap;
@@ -37,7 +46,7 @@ public class TbPisInfr {
 	private boolean                 isDupLogin;
 
 	private TbPisInfrStts 			stts;
-	private Channel 				channel;
+	private ItsWebSocketSession     session;
 	private Channel 				dupChannel;
 	private long          			syncTime;
 	private InetSocketAddress 		cliReq;
@@ -87,24 +96,32 @@ public class TbPisInfr {
 		this.netState   = NET.CLOSED;
 		this.isDupCon   = false;
 		this.isDupLogin = false;
-		this.channel    = null;
+		this.session    = null;
 		this.dupChannel = null;
 		this.cliReq     = null;
 		this.syncTime   = 0;
 	}
 
-	public synchronized void channelOpen(Channel channel) {
+	public synchronized void channelOpen(ItsWebSocketSession session) {
 		this.netState = NET.LOGIN_REQ;
-		this.channel = channel;
+		this.session= session;
 		getStts().initStts(true);
 		setConnectTm();
+		MainUI mainUI = MainUI.getInstance();
+		if (mainUI != null) {
+			mainUI.updateCtlrStts(this);
+		}
 	}
 
-	public synchronized void channelLogin(Channel channel) {
+	public synchronized void channelLogin(ItsWebSocketSession session) {
 		this.netState = NET.LOGINED;
-		this.channel = channel;
+		this.session = session;
 		getStts().initStts(true);
 		setConnectTm();
+		MainUI mainUI = MainUI.getInstance();
+		if (mainUI != null) {
+			mainUI.updateCtlrStts(this);
+		}
 	}
 
 	public synchronized void channelClosed() {
@@ -112,16 +129,60 @@ public class TbPisInfr {
 			setDisConnectTm();
 		}
 		this.netState = NET.CLOSED;
-		this.channel = null;
+		this.session = null;
 		getStts().initStts(false);
+		MainUI mainUI = MainUI.getInstance();
+		if (mainUI != null) {
+			mainUI.updateCtlrStts(this);
+		}
 	}
 
 	public boolean channelClose() {
-		if (getChannel() == null || getNetState() == NET.CLOSED) {
+		if (getSession() == null || getNetState() == NET.CLOSED) {
 			log.error("Close Request: channel not connected: [{}]", this);
 			return false;
 		}
-		//VdsTcpClientIdleHandler.disconnectChannel(getChannel());
+		try {
+			getSession().getSession().close();
+		} catch (IOException e) {
+			throw new RuntimeException(e);
+		}
+		MainUI mainUI = MainUI.getInstance();
+		if (mainUI != null) {
+			mainUI.updateCtlrStts(this);
+		}
+		return true;
+	}
+
+	public boolean requestRlTimeInfo() {
+		if (getSession() == null || getNetState() == NET.CLOSED) {
+			log.error("Reset Request: channel not connected: [{}]", this);
+			return false;
+		}
+
+		C2FMessageRequest request = C2FMessageRequest.builder()
+				.prk_tkn(this.PIS_TOKEN)
+				.event_name("prk_plce_rl_time_request_info")
+				.event_type("request")
+				.build();
+
+		C2FMessage<Object> message = C2FMessage.builder()
+				.command("message")
+				.data(C2FMessageData.builder()
+						.payload(request)
+						.build())
+				.build();
+
+		ObjectMapper mapper = new ObjectMapper();
+		try {
+			String strMessage = mapper.writeValueAsString(message);
+			log.error("{}", strMessage);
+			TextMessage txtMessage = new TextMessage(strMessage);
+			getSession().sendMessage(request.getEvent_name(), txtMessage);
+		} catch (JsonProcessingException e) {
+			throw new RuntimeException(e);
+		}
+
 		return true;
 	}
 

+ 1 - 1
src/main/java/com/its/pis/scheduler/SchedulerTask.java

@@ -24,7 +24,7 @@ public class SchedulerTask {
 
     @Scheduled(cron = "6 * * * * *")  // 1분주기 작업 실행(매분 6초)
     public void UnitSystSchedule() {
-        if (!this.processConfig.isStartSchedule()) {
+        if (this.processConfig.isStartSchedule()) {
             return;
         }
         Elapsed elapsed = new Elapsed();

+ 1 - 1
src/main/java/com/its/pis/service/PisInfrService.java

@@ -171,7 +171,7 @@ public class PisInfrService {
         for (Map.Entry<String, TbPisInfr> e : AppRepository.getInstance().getPisNmbrMap().entrySet()) {
             TbPisInfr pis = e.getValue();
             pis.getStts().setUPDT_DT(UPDT_DT);
-            if (isRun && pis.getNetState() >= NET.LOGIN_REQ && pis.getChannel() != null) {
+            if (isRun && pis.getNetState() >= NET.LOGIN_REQ && pis.getSession() != null) {
                 // 주차정보시스템 통신정상
                 normal++;
                 pis.getStts().setCMNC_STTS_CD("CMS0");

+ 2 - 2
src/main/java/com/its/pis/ui/MainUI.form

@@ -134,13 +134,13 @@
                   <text value="초기화"/>
                 </properties>
               </component>
-              <component id="7c99b" class="javax.swing.JButton" binding="btnReset">
+              <component id="7c99b" class="javax.swing.JButton" binding="btnRlTimeRequest">
                 <constraints>
                   <grid row="0" column="5" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
                 </constraints>
                 <properties>
                   <font name="Malgun Gothic" size="12" style="0"/>
-                  <text value="리셋"/>
+                  <text value="실시간 주차정보 요청"/>
                 </properties>
               </component>
               <component id="a85f9" class="javax.swing.JButton" binding="btnDisconnect">

+ 21 - 22
src/main/java/com/its/pis/ui/MainUI.java

@@ -69,7 +69,7 @@ public class MainUI {
     private JPanel pnlControl;
     private JButton btnImage;
     private JButton btnInitialize;
-    private JButton btnReset;
+    private JButton btnRlTimeRequest;
     private JButton btnDisconnect;
     private JTextField txtName;
     private JTextField txtId;
@@ -196,7 +196,7 @@ public class MainUI {
                 controlController(1);
             }
         });
-        btnReset.addActionListener(new ActionListener() {
+        btnRlTimeRequest.addActionListener(new ActionListener() {
             @Override
             public void actionPerformed(ActionEvent e) {
                 controlController(2);
@@ -223,22 +223,22 @@ public class MainUI {
      */
     public void controlController(int type) {
         if (selObj == null) {
-            JOptionPane.showMessageDialog(getRootPanel(), "제어기가 선택되지 않았습니다. 목록을 더블클릭하여 제어기를 선택하세요.", "제어기 선택", JOptionPane.ERROR_MESSAGE);
+            JOptionPane.showMessageDialog(getRootPanel(), "주차시스템이 선택되지 않았습니다. 목록을 더블클릭하여 주차시스템을 선택하세요.", "주차시스템 선택", JOptionPane.ERROR_MESSAGE);
             return;
         }
         if (selObj.getNetState() == NET.CLOSED) {
-            JOptionPane.showMessageDialog(getRootPanel(), "제어기가 현재 연결이 되어 있지 않습니다.", "제어기 연결 상태", JOptionPane.ERROR_MESSAGE);
+            JOptionPane.showMessageDialog(getRootPanel(), "주차시스템이 현재 연결이 되어 있지 않습니다.", "주차시스템 연결 상태", JOptionPane.ERROR_MESSAGE);
             return;
         }
         String message, title;
         switch (type) {
             case 1:
-                message = "제어기와의 연결을 종료 하시겠습니까?";
-                title = "제어기 연결 종료";
+                message = "주차시스템과 연결을 종료 하시겠습니까?";
+                title = "주차시스템 연결 종료";
                 break;
             case 2:
-                message = "제어기를 리셋 하시겠습니까?";
-                title = "제어기 리셋";
+                message = "주차장 실시간 정보를 요청 하시겠습니까?";
+                title = "주차장 실시간 정보 요청";
                 break;
             case 3:
                 message = "제어기를 초기화 하시겠습니까?";
@@ -261,7 +261,7 @@ public class MainUI {
                 result = selObj.channelClose();
                 break;
             case 2:
-                result = false;//selObj.reset();
+                result = selObj.requestRlTimeInfo();
                 break;
             case 3:
                 result = false;//selObj.initialize();
@@ -292,12 +292,12 @@ public class MainUI {
         CtlrSttsTableModel tableModel = (CtlrSttsTableModel) tblCtlrList.getModel();
         selObj = tableModel.getControllerInfo(row);
         if (selObj != null) {
-            txtId.setText(selObj.getPIS_END_POINT());
+            txtId.setText(selObj.getPIS_NMBR());
             txtName.setText(selObj.getPIS_NM());
 
             subUIController.updateInfo(selObj);
             if (!subUIController.isVisible()) {
-                subUIController.setVisible(true);
+                //subUIController.setVisible(true);
             }
             return true;
         }
@@ -398,18 +398,17 @@ public class MainUI {
 
     public void updateCtlrStts(TbPisInfr obj) {
 
-        //CtlrSttsTableModel ctlrSttsTableModel = (CtlrSttsTableModel)tblCtlrList.getModel();
         for (int ii = 0; ii < ctlrSttsTableModel.getRowCount(); ii++) {
-            if (obj.getPIS_END_POINT().equals(ctlrSttsTableModel.getValueAt(ii, 2).toString())) {
+            if (obj.getPIS_NMBR().equals(ctlrSttsTableModel.getValueAt(ii, 1).toString())) {
                 int modelRow = tblCtlrList.convertRowIndexToModel(ii);
-//                //int viewColumn = tblCtlrList.getSelectedColumn();
-//                int modelColumn = tblCtlrList.convertColumnIndexToModel(4);
-//                Object cell = tblCtlrList.getValueAt(modelRow, modelColumn);
-//                log.error("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX: {}", cell);
                 ctlrSttsTableModel.setValue(obj, ii, modelRow);
                 break;
             }
         }
+        CtlrSttsTableModel tableModel = (CtlrSttsTableModel) tblCtlrList.getModel();
+        if (tableModel != null) {
+            tableModel.fireTableDataChanged();
+        }
     }
 
     {
@@ -491,11 +490,11 @@ public class MainUI {
         if (btnInitializeFont != null) btnInitialize.setFont(btnInitializeFont);
         btnInitialize.setText("초기화");
         pnlControl.add(btnInitialize, new GridConstraints(0, 6, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
-        btnReset = new JButton();
-        Font btnResetFont = this.$$$getFont$$$("Malgun Gothic", Font.PLAIN, 12, btnReset.getFont());
-        if (btnResetFont != null) btnReset.setFont(btnResetFont);
-        btnReset.setText("리셋");
-        pnlControl.add(btnReset, new GridConstraints(0, 5, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
+        btnRlTimeRequest = new JButton();
+        Font btnRlTimeRequestFont = this.$$$getFont$$$("Malgun Gothic", Font.PLAIN, 12, btnRlTimeRequest.getFont());
+        if (btnRlTimeRequestFont != null) btnRlTimeRequest.setFont(btnRlTimeRequestFont);
+        btnRlTimeRequest.setText("실시간 주차정보 요청");
+        pnlControl.add(btnRlTimeRequest, new GridConstraints(0, 5, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
         btnDisconnect = new JButton();
         Font btnDisconnectFont = this.$$$getFont$$$("Malgun Gothic", Font.PLAIN, 12, btnDisconnect.getFont());
         if (btnDisconnectFont != null) btnDisconnect.setFont(btnDisconnectFont);

+ 4 - 4
src/main/java/com/its/pis/webapp/controller/WebAppController.java

@@ -68,7 +68,7 @@ public class WebAppController {
     public String disconnectController(@RequestParam("id") String id) {
         String result = "제어기와의 통신 연결을 종료 하였습니다.";
         TbPisInfr ctlr = AppRepository.getInstance().getPisNmbrMap().get(id);
-        if (ctlr != null && ctlr.getNetState() > NET.CLOSED && ctlr.getChannel() != null) {
+        if (ctlr != null && ctlr.getNetState() > NET.CLOSED && ctlr.getSession() != null) {
 //            VdsTcpClientIdleHandler.disconnectChannel(ctlr.getChannel());
             try {
                 Thread.sleep(1000);
@@ -88,7 +88,7 @@ public class WebAppController {
         String result = "통신 연결 중인 제어기와의 통신 연결을 종료 하였습니다.";
         for (Map.Entry<String, TbPisInfr> e : AppRepository.getInstance().getPisNmbrMap().entrySet()) {
             TbPisInfr ctlr = e.getValue();
-            if (ctlr != null && ctlr.getNetState() > NET.CLOSED && ctlr.getChannel() != null) {
+            if (ctlr != null && ctlr.getNetState() > NET.CLOSED && ctlr.getSession() != null) {
 //                VdsTcpClientIdleHandler.disconnectChannel(ctlr.getChannel());
             }
         }
@@ -127,7 +127,7 @@ public class WebAppController {
     public String resetController(@RequestParam("id") String id) {
         String result = "제어기를 리셋 하였습니다.";
         TbPisInfr ctlr = AppRepository.getInstance().getPisNmbrMap().get(id);
-        if (ctlr != null && ctlr.getNetState() > NET.CLOSED && ctlr.getChannel() != null) {
+        if (ctlr != null && ctlr.getNetState() > NET.CLOSED && ctlr.getSession() != null) {
             /*TcpServerIdleStateHandler.disconnectChannel(ctlr.getChannel());
             try {
                 Thread.sleep(1000);
@@ -147,7 +147,7 @@ public class WebAppController {
         String result = "통신 연결 중인 제어기를 리셋 하였습니다.";
         for (Map.Entry<String, TbPisInfr> e : AppRepository.getInstance().getPisNmbrMap().entrySet()) {
             TbPisInfr ctlr = e.getValue();
-            if (ctlr != null && ctlr.getNetState() > NET.CLOSED && ctlr.getChannel() != null) {
+            if (ctlr != null && ctlr.getNetState() > NET.CLOSED && ctlr.getSession() != null) {
 //                VdsTcpClientIdleHandler.disconnectChannel(ctlr.getChannel());
             }
         }

+ 18 - 0
src/main/java/com/its/pis/websocket/C2F/message/C2FMessageRequest.java

@@ -0,0 +1,18 @@
+package com.its.pis.websocket.C2F.message;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import lombok.Builder;
+import lombok.Data;
+
+@Data
+@Builder
+//@NoArgsConstructor(access = AccessLevel.PRIVATE)
+//@AllArgsConstructor(access = AccessLevel.PRIVATE)
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class C2FMessageRequest {
+
+    private String prk_tkn;
+    private String event_name;
+    private String event_type;
+
+}

+ 2 - 3
src/main/java/com/its/pis/websocket/C2F/message/PrkPlceOprInfo.java

@@ -87,9 +87,8 @@ public class PrkPlceOprInfo extends C2FMessagePayload {
                                                 //    핀테크            결제 시 (00000100)    필수
 
     // 주차요금
-    // TODO
-    //private ParkingChargeDetail parking_charge_detail;
-    private ParkingChargeDetail parking_chrge_detail;
+    private ParkingChargeDetail parking_charge_detail;
+    //private ParkingChargeDetail parking_chrge_detail;
 
     // 주차예약 시행여부
     private Integer parking_resve_opertn_at;    // 사전 주차 예약 서비스 시행 여부 numeric(1,0)

+ 3 - 1
src/main/java/com/its/pis/websocket/C2F/message/PrkPlceSttusInfo.java

@@ -91,7 +91,9 @@ public class PrkPlceSttusInfo extends C2FMessagePayload {
                                                 // 2020년01월01일 (YYYY-MM-DD)	선택
 
     // 관련법령
-    private String relation_law_and_ordinances; // 시스템 운영 및 데이터 수집과 관련된 근거 법령	varchar(50)	공공데이터 개방표준(행정안전부 고시 제2019-61호)	선택
+    // TODO
+    //private String relation_law_and_ordinances; // 시스템 운영 및 데이터 수집과 관련된 근거 법령	varchar(50)	공공데이터 개방표준(행정안전부 고시 제2019-61호)	선택
+    private String relation_law_and_opdinances; // 시스템 운영 및 데이터 수집과 관련된 근거 법령	varchar(50)	공공데이터 개방표준(행정안전부 고시 제2019-61호)	선택
 
     // 이용가능 차량유형
     private String prk_avail_cartype;           // 차량유형별 주차장 이용가능 여부    BIT(7)

+ 0 - 3
src/main/java/com/its/pis/websocket/C2F/message/prk_plce_rl_time/PrkColctDeviceInfo.java

@@ -1,7 +1,6 @@
 package com.its.pis.websocket.C2F.message.prk_plce_rl_time;
 
 import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
-import com.fasterxml.jackson.annotation.JsonProperty;
 import lombok.*;
 
 /**
@@ -15,7 +14,6 @@ import lombok.*;
 public class PrkColctDeviceInfo {
 
     // 주차정보수집장치 ID
-    @JsonProperty("device_ID")
     private String device_id;           // varchar(5) 설치순서에 따라 1부터 오름차순(1∼65535) 선택
     // 주차정보수집장치 종류
     private Integer device_type;        // numeric(1,0)
@@ -26,7 +24,6 @@ public class PrkColctDeviceInfo {
                                         // 4 : 초음파
                                         // 9 : 기타
     // 주차면 ID
-    @JsonProperty("prk_unit_ID")
     private String prk_unit_id;         // varchar(10) 층번호+구역일련번호 예시) 1FA01 1층 A구역 01번 주차면
     // 주차면 유형
     private Integer typed_parking_lots; // numeric(1,0)

+ 3 - 0
src/main/java/com/its/pis/websocket/C2F/message/prk_plce_rl_time/RtArInfr.java

@@ -15,6 +15,9 @@ public class RtArInfr {
 
     // 구역 일련번호
     private String ar_no;               // varchar(10) A01, A02, A03, ...    선택
+    // 층 번호
+    // TODO(문서에는 없고 테스트시에 확인)
+    private String flr_no;              // varchar(10) 지상층(1F, 2F, ...) 지하층(B1, B2, ...)    선택
     // 구역별 주차 구획 수
     private Integer ar_prk_cmprt_co;    // numeric(18,0) 120(면)             선택
 

+ 4 - 4
src/main/java/com/its/pis/websocket/C2F/message/prk_plce_sttus/ArInfr.java

@@ -15,10 +15,10 @@ public class ArInfr {
 
     // 구역 일련번호
     private String ar_no;               // varchar(10) A01, A02, A03, ...       선택
-    // 구역별 주차 구획 수
-    private Integer ar_prk_cmprt_co;    // numeric(18,0) 120(면)             선택
-
     // 층 번호
-    // TODO(문서에는 없고 json 예제에 만 존재)
+    // TODO(문서에는 없고 json 예제에 만 존재, 현장프로토콜에도 존재)
     private String flr_no;              // varchar(10) 지상층(1F, 2F, ...) 지하층(B1, B2, ...)    선택
+
+    // 구역별 주차 구획 수
+    private Integer ar_prk_cmprt_co;    // numeric(18,0) 120(면)             선택
 }

+ 1 - 1
src/main/java/com/its/pis/websocket/C2F/message/prk_plce_sttus/TypedPrkColctDeviceQty.java

@@ -15,7 +15,7 @@ public class TypedPrkColctDeviceQty {
     // 선택
 
     // 영상식
-    private Integer type_imgage;            //  numeric(18,0) 2(대)
+    private Integer type_image;             //  numeric(18,0) 2(대)
     // 지자기
     private Integer type_geomagnetic;       //  numeric(18,0) 5(대)
     // 레이더

+ 2 - 0
src/main/java/com/its/pis/websocket/ItsWebSocketConfig.java

@@ -23,12 +23,14 @@ public class ItsWebSocketConfig implements WebSocketConfigurer {
         registry.addHandler(this.itsWebsocketHandler, ItsWebSocketConfig.WS_ENDPOINT)
                 //.setAllowedOrigins("*")
                 .setAllowedOriginPatterns("*")
+                //.addInterceptors(new ItsWebSocketHandshakeInterceptor()).setAllowedOriginPatterns("*")
                 .withSockJS()
                 //.setSessionCookieNeeded(false)
         ;  // sockjs
 
         registry.addHandler(this.itsWebsocketHandler, ItsWebSocketConfig.WS_ENDPOINT)
                 .setAllowedOriginPatterns("*")
+                //.addInterceptors(new ItsWebSocketHandshakeInterceptor()).setAllowedOriginPatterns("*")
                 //.setAllowedOrigins("*")
         ; // 그냥 websocket 지원
     }

+ 113 - 43
src/main/java/com/its/pis/websocket/ItsWebSocketHandler.java

@@ -3,7 +3,12 @@ package com.its.pis.websocket;
 import com.fasterxml.jackson.core.type.TypeReference;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.its.app.utils.ItsUtils;
+import com.its.pis.entity.TbPisInfr;
+import com.its.pis.global.AppRepository;
 import com.its.pis.websocket.C2F.message.*;
+import com.its.pis.websocket.common.PisSubscribeIdentifier;
+import com.its.pis.websocket.common.PisSubscribeRequest;
+import com.its.pis.websocket.common.PisSubscribeResponseAccept;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.io.FileUtils;
 import org.springframework.stereotype.Controller;
@@ -15,6 +20,7 @@ import org.springframework.web.socket.handler.TextWebSocketHandler;
 
 import java.io.File;
 import java.util.Base64;
+import java.util.List;
 import java.util.Map;
 
 @Slf4j
@@ -41,39 +47,99 @@ public class ItsWebSocketHandler extends TextWebSocketHandler {
     @Override
     public void afterConnectionEstablished(WebSocketSession session) throws Exception {
         // 클라이언트가 연결되었을때 실행
-        log.info("afterConnectionEstablished: {}", session.getHandshakeHeaders().entrySet());
+        String ipAddr = session.getRemoteAddress().getHostString();
+        String token = null;
+        for (Map.Entry<String, List<String>> l : session.getHandshakeHeaders().entrySet()) {
+            if ("sec-websocket-protocol".equals(l.getKey())) {
+                token = l.getValue().get(0);
+                break;
+            }
+        }
+        if (token == null) {
+            log.error("afterConnectionEstablished: {}, token not found, session will be closed", ipAddr);
+            session.close();
+            return;
+        }
 
-        log.info("afterConnectionEstablished: {}, {}, {}, {}", session.getBinaryMessageSizeLimit(), session.getTextMessageSizeLimit(), session.getRemoteAddress().getHostString(), session.getAcceptedProtocol());
-        log.info("afterConnectionEstablished: " + session.getRemoteAddress() + ",  URI: " + session.getUri() + ", UUID: " + session.getId());
-        session.setBinaryMessageSizeLimit(1*1024*1024);
-        session.setTextMessageSizeLimit(1*1024*1024);
-        super.afterConnectionEstablished(session);
+        log.info("afterConnectionEstablished: ipAddr={}, token={}", ipAddr, token);
+
+        for (Map.Entry<String, TbPisInfr> e : AppRepository.getInstance().getPisNmbrMap().entrySet()) {
+            TbPisInfr pis = e.getValue();
+            if (token.equals(pis.getPIS_TOKEN())) {
+                ItsWebSocketSession vo = new ItsWebSocketSession(pis, this, session);
+                pis.channelOpen(vo);
+                ItsWebSocketSessionManager.getInstance().addSession(session, vo);
+
+                session.setBinaryMessageSizeLimit(1*1024*1024);
+                session.setTextMessageSizeLimit(1*1024*1024);
+                super.afterConnectionEstablished(session);
+                log.info("afterConnectionEstablished: connection accept, {}, {}", pis.getPIS_ID(), pis.getPIS_NM());
+                return;
+            }
+        }
 
-        ItsWebSocketSession vo = new ItsWebSocketSession(this, session);
-        ItsWebSocketSessionManager.getInstance().addSession(session, vo);
+        log.error("afterConnectionEstablished: ipAddr={}, token={}, not found token, session will be closed", ipAddr, token);
+        session.close();
     }
 
     //클라이언트가 웹소켓 서버로 메시지를 전송했을 때 실행
     @Override
     protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
-        log.info("handleTextMessage: {}, {}, {}, {}", session.getBinaryMessageSizeLimit(), session.getTextMessageSizeLimit(), session.getRemoteAddress().getHostString(), session.getAcceptedProtocol());
-        log.info("handleTextMessage: " + session.getRemoteAddress() + ",  Uri[" + session.getUri() + "], UUID[" + session.getId() + "]");//, message[" + message.getPayload() + "]");
-        /*List<String> requests = ItsUtils.split(message.getPayload(), ":");
-        if (requests.size() != 2) {
-            log.error("Request data parsing error: {}", requests);
+
+        String payloadMessage = message.getPayload();
+        if (payloadMessage == null || payloadMessage.trim().isEmpty()) {
+            log.error("handleTextMessage: Payload data is empty");
+            return;
+        }
+        log.info("handleTextMessage: Payload, RX] {}", payloadMessage);
+
+        ItsWebSocketSession sessionClient = ItsWebSocketSessionManager.getInstance().getSession(session);
+        if (sessionClient == null) {
+            log.error("handleTextMessage: Request session not found, session will be closed {}", session);
             session.close();
             return;
         }
-*/
-        //C2FMessage<C2FMessagePayload> c2fMessage = this.mapper.readValue(message.getPayload(), C2FMessage.class);
-        C2FMessage<C2FMessagePayload> c2fMessage = this.mapper.readValue(message.getPayload(), new TypeReference<C2FMessage<C2FMessagePayload>>(){});
-        log.error("{}", c2fMessage);
+
+        C2FMessage<C2FMessagePayload> c2fMessage = this.mapper.readValue(payloadMessage, new TypeReference<C2FMessage<C2FMessagePayload>>(){});
+        if (c2fMessage.getCommand() == null) {
+            log.error("handleTextMessage: C2FMessage, {}", c2fMessage);
+            log.error("handleTextMessage: command data not found, session will be closed {}", session);
+            session.close();
+            return;
+        }
+        if ("subscribe".equals(c2fMessage.getCommand())) {
+            log.info("handleTextMessage: RX] subscribe");
+
+            PisSubscribeRequest subscribe = this.mapper.readValue(payloadMessage, PisSubscribeRequest.class);
+            //log.info("handleTextMessage: RX] {}", subscribe);
+            log.info("RX] {}", this.mapper.writeValueAsString(subscribe));
+            TbPisInfr pis = sessionClient.getObj();
+            if (pis == null) {
+                log.error("handleTextMessage: PIS object not found, session will be closed {}", session);
+                session.close();
+                return;
+            }
+            pis.channelLogin(pis.getSession());
+
+            PisSubscribeResponseAccept response = PisSubscribeResponseAccept.builder()
+                    .identifier(PisSubscribeIdentifier.builder()
+                            .channel("ParkingLotChannel").build())
+                    .essntl_info("Not Setting Info")
+                    .type("confirm_subscription").build();
+            String strMessage = this.mapper.writeValueAsString(response);
+            sessionClient.sendMessage(response.getType(), new TextMessage(strMessage));
+            log.info("TX] {}", strMessage);
+            return;
+        }
 
         try {
             String eventName = c2fMessage.getData().getPayload().getEvent_name();
             if ("prk_plce_sttus_info".equals(eventName)) {
-                C2FMessage<PrkPlceSttusInfo> sttusInfo = this.mapper.readValue(message.getPayload(), new TypeReference<C2FMessage<PrkPlceSttusInfo>>(){});
-                log.error("{}", this.mapper.writeValueAsString(sttusInfo));
+                C2FMessage<PrkPlceSttusInfo> sttusInfo = this.mapper.readValue(payloadMessage, new TypeReference<C2FMessage<PrkPlceSttusInfo>>(){});
+                C2FMessage<PrkPlceSttusInfo> sttusTemp = sttusInfo;
+                sttusTemp.getData().getPayload().getPrk_place_image().setPrk_plce_image_data("base64 image data string");
+                log.info("handleTextMessage: RX] prk_plce_sttus_info");
+                log.info("RX] {}", this.mapper.writeValueAsString(sttusTemp));
 
                 if (sttusInfo.getData().getPayload().getPrk_place_image() != null) {
                     int imageType = sttusInfo.getData().getPayload().getPrk_place_image().getPrk_plce_image_type();
@@ -97,36 +163,28 @@ public class ItsWebSocketHandler extends TextWebSocketHandler {
 //                String encodedString = Base64.getEncoder().encodeToString(fileContent);
 
             } else if ("prk_plce_opr_info".equals(eventName)) {
-                C2FMessage<PrkPlceOprInfo> oprInfo = this.mapper.readValue(message.getPayload(), new TypeReference<C2FMessage<PrkPlceOprInfo>>(){});
-                //PrkPlceOprInfo oprInfo = (PrkPlceOprInfo)c2fMessage.getData().getPayload();
-                log.error("{}", this.mapper.writeValueAsString(oprInfo));
-            } else if ("prk_plce_rl_time_info".equals(eventName)) {
-                C2FMessage<PrkPlceRlTimeResponseInfo> rlTimeInfo = this.mapper.readValue(message.getPayload(), new TypeReference<C2FMessage<PrkPlceRlTimeResponseInfo>>(){});
-                //PrkPlceRlTimeInfo rlTimeInfo = (PrkPlceRlTimeInfo)c2fMessage.getData().getPayload();
-                log.error("{}", this.mapper.writeValueAsString(rlTimeInfo));
+                C2FMessage<PrkPlceOprInfo> oprInfo = this.mapper.readValue(payloadMessage, new TypeReference<C2FMessage<PrkPlceOprInfo>>(){});
+                log.info("handleTextMessage: RX] prk_plce_opr_info");
+                log.info("RX] {}", this.mapper.writeValueAsString(oprInfo));
+            } else if ("prk_plce_rl_time_info".equals(eventName) || "prk_plce_rl_time_info_cycle".equals(eventName)) {
+                C2FMessage<PrkPlceRlTimeResponseInfo> rlTimeInfo = this.mapper.readValue(payloadMessage, new TypeReference<C2FMessage<PrkPlceRlTimeResponseInfo>>(){});
+                log.info("handleTextMessage: RX] prk_plce_rl_time_info");
+                log.info("RX] {}", this.mapper.writeValueAsString(rlTimeInfo));
             } else if ("prk_plce_reservation_response_info".equals(eventName)) {
-                C2FMessage<PrkPlceReservationResponseInfo> reservationInfo = this.mapper.readValue(message.getPayload(), new TypeReference<C2FMessage<PrkPlceReservationResponseInfo>>(){});
-                //PrkPlceReservationResponseInfo reservationInfo = (PrkPlceReservationResponseInfo)c2fMessage.getData().getPayload();
-                log.error("{}", this.mapper.writeValueAsString(reservationInfo));
+                C2FMessage<PrkPlceReservationResponseInfo> reservationInfo = this.mapper.readValue(payloadMessage, new TypeReference<C2FMessage<PrkPlceReservationResponseInfo>>(){});
+                log.info("handleTextMessage: RX] prk_plce_reservation_response_info");
+                log.info("RX] {}", this.mapper.writeValueAsString(reservationInfo));
             } else if ("prk_plce_vhcl_location_response_info".equals(eventName)) {
-                C2FMessage<PrkPlceVhclLocationResponseInfo> locationInfo = this.mapper.readValue(message.getPayload(), new TypeReference<C2FMessage<PrkPlceVhclLocationResponseInfo>>(){});
-                //PrkPlceVhclLocationResponseInfo locationInfo = (PrkPlceVhclLocationResponseInfo)c2fMessage.getData().getPayload();
-                log.error("{}", this.mapper.writeValueAsString(locationInfo));
+                C2FMessage<PrkPlceVhclLocationResponseInfo> locationInfo = this.mapper.readValue(payloadMessage, new TypeReference<C2FMessage<PrkPlceVhclLocationResponseInfo>>(){});
+                log.info("handleTextMessage: RX] prk_plce_vhcl_location_response_info");
+                log.info("RX] {}", this.mapper.writeValueAsString(locationInfo));
             } else {
-                log.error("Unknown event name: {}", eventName);
-                log.error("Payload: {}", message.getPayload());
+                log.error("handleTextMessage: RX] Unknown event name, {}", eventName);
+                log.error("RX] Payload, {}", payloadMessage);
             }
         } catch(NullPointerException e) {
-            log.error("Payload data null. {}", message.getPayload());
-        }
-
-        ItsWebSocketSession sessionClient = ItsWebSocketSessionManager.getInstance().getSession(session);
-        if (sessionClient == null) {
-            log.error("Request session not found: {}", session);
-            session.close();
-            return;
+            log.error("handleTextMessage: RX] NullPointerException, Payload data null, {}", payloadMessage);
         }
-        //log.info("Payload: {}", message.getPayload());
     }
 
     //클라이언트 연결을 끊었을 때 실행
@@ -135,6 +193,18 @@ public class ItsWebSocketHandler extends TextWebSocketHandler {
         log.info("afterConnectionClosed: " + session.getRemoteAddress() + ",  URI: " + session.getUri() + ", UUID: " + session.getId());
         ItsWebSocketSessionManager.getInstance().removeSession(session);
         super.afterConnectionClosed(session, status);
+        for (Map.Entry<String, TbPisInfr> e : AppRepository.getInstance().getPisNmbrMap().entrySet()) {
+            TbPisInfr pis = e.getValue();
+            pis.channelClosed();
+        }
     }
 
+    @Override
+    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
+        log.error("handleTransportError: {}", exception.getMessage());
+        if (session.isOpen()) {
+            session.close();
+        }
+        super.handleTransportError(session, exception);
+    }
 }

+ 33 - 0
src/main/java/com/its/pis/websocket/ItsWebSocketHandshakeInterceptor.java

@@ -0,0 +1,33 @@
+package com.its.pis.websocket;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.http.server.ServerHttpRequest;
+import org.springframework.http.server.ServerHttpResponse;
+import org.springframework.http.server.ServletServerHttpRequest;
+import org.springframework.web.socket.WebSocketHandler;
+import org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor;
+
+import java.util.Map;
+
+@Slf4j
+public class ItsWebSocketHandshakeInterceptor extends HttpSessionHandshakeInterceptor {
+    @Override
+    public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response,
+                                   WebSocketHandler wsHandler, Map attributes) throws Exception {
+        ServletServerHttpRequest serverRequest = (ServletServerHttpRequest) request;
+        String token = serverRequest.getServletRequest().getParameter("token");
+        log.error("beforeHandshake: {}", token);
+        log.error("beforeHandshake: {}", serverRequest);
+        return super.beforeHandshake(request, response, wsHandler, attributes);
+    }
+
+
+
+    @Override
+    public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response,
+                               WebSocketHandler wsHandler, Exception ex) {
+        ServletServerHttpRequest serverRequest = (ServletServerHttpRequest) request;
+        log.error("afterHandshake: {}", serverRequest);
+    }
+
+}

+ 5 - 2
src/main/java/com/its/pis/websocket/ItsWebSocketSession.java

@@ -1,5 +1,6 @@
 package com.its.pis.websocket;
 
+import com.its.pis.entity.TbPisInfr;
 import lombok.Data;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.web.socket.TextMessage;
@@ -13,9 +14,11 @@ public class ItsWebSocketSession {
 
     private ItsWebSocketHandler itsWebsocketHandler;
     private WebSocketSession session;
+    private TbPisInfr obj;
     private String groupId;
 
-    public ItsWebSocketSession(ItsWebSocketHandler itsWebsocketHandler, org.springframework.web.socket.WebSocketSession session) {
+    public ItsWebSocketSession(TbPisInfr obj, ItsWebSocketHandler itsWebsocketHandler, org.springframework.web.socket.WebSocketSession session) {
+        this.obj = obj;
         this.itsWebsocketHandler = itsWebsocketHandler;
         this.session = session;
     }
@@ -27,7 +30,7 @@ public class ItsWebSocketSession {
                 try {
                     this.session.sendMessage(message);
                 } catch (IOException e) {
-                    log.error("sendMessage: nodeId: {}, session: {}", command, session);
+                    log.error("sendMessage: command: {}, session: {}", command, session);
                 }
             }
         }

+ 14 - 0
src/main/java/com/its/pis/websocket/common/PisSubscribeIdentifier.java

@@ -0,0 +1,14 @@
+package com.its.pis.websocket.common;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import lombok.*;
+
+@Data
+@Builder
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
+@AllArgsConstructor(access = AccessLevel.PRIVATE)
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class PisSubscribeIdentifier {
+
+    String channel;
+}

+ 16 - 0
src/main/java/com/its/pis/websocket/common/PisSubscribeRequest.java

@@ -0,0 +1,16 @@
+package com.its.pis.websocket.common;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import lombok.*;
+
+@Data
+@Builder
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
+@AllArgsConstructor(access = AccessLevel.PRIVATE)
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class PisSubscribeRequest {
+
+    private String command;
+    private PisSubscribeIdentifier identifier;
+
+}

+ 17 - 0
src/main/java/com/its/pis/websocket/common/PisSubscribeResponseAccept.java

@@ -0,0 +1,17 @@
+package com.its.pis.websocket.common;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import lombok.*;
+
+@Data
+@Builder
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
+@AllArgsConstructor(access = AccessLevel.PRIVATE)
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class PisSubscribeResponseAccept {
+
+    private PisSubscribeIdentifier identifier;
+    String essntl_info;
+    String type;    // "confirm_subscription"
+
+}

+ 16 - 0
src/main/java/com/its/pis/websocket/common/PisSubscribeResponseReject.java

@@ -0,0 +1,16 @@
+package com.its.pis.websocket.common;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import lombok.*;
+
+@Data
+@Builder
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
+@AllArgsConstructor(access = AccessLevel.PRIVATE)
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class PisSubscribeResponseReject {
+
+    private PisSubscribeIdentifier identifier;
+    String type;    // "reject_subscription"
+
+}

+ 1 - 1
src/main/java/com/its/pis/xnettcp/center/handler/CenterTcpServerInboundHandler.java

@@ -54,7 +54,7 @@ public class CenterTcpServerInboundHandler extends ChannelInboundHandlerAdapter
             }
             return;
         }
-        if (obj.getChannel() == null || obj.getNetState() == NET.CLOSED) {
+        if (obj.getSession() == null || obj.getNetState() == NET.CLOSED) {
             log.error("CenterTcpServerInboundHandler: Center Request VDS Not Connect: [{}]", obj);
             ByteBuffer sendBuffer = CenterResProtocol.getResponse(opCode, (byte)(0xFE));
             ChannelFuture f = ctx.channel().writeAndFlush(sendBuffer);

BIN
src/main/resources/application.png