본문으로 건너뛰기

WebSocket 클라이언트 드라이버

개요

WebSocket 드라이버는 외부 streaming server (ws / wss) 의 endpoint 에 클라이언트로 접속해 들어오는 메시지를 캐시하고, 수집 주기마다 read() 하면 가장 최근 값을 반환하는 push 모델이다.

항목
opc_typeWEBSOCKET
구현 클래스plantpulse.driver.protocol.websocket.WebSocketDriver
기반 라이브러리java.net.http.HttpClient.WebSocket (Java 21 runtime 표준 API)
read✅ (메시지 캐시)
write✅ (sendText)
보안TLS (wss) — options.tls=true

HTTP 드라이버가 외부 시스템의 push (REST POST) 를 받는 모델이라면, WebSocket 드라이버는 edge 가 클라이언트로 outbound 접속 해 streaming 메시지를 받는 모델이다.


OPC 등록 폼 / 옵션

필드의미기본값예시
host / portWebSocket 서버 주소192.168.10.99 / 8765, stream.example.com / 443
options.pathendpoint path//stream/v1, /realtime/tag
options.tlswss 사용 여부falsetrue (wss) / false (ws)
options.subscribe-message연결 직후 송신할 메시지 (옵션){"op":"subscribe","topic":"line1.tempC"}
timecycleread 주기 (ms)1000

실제 endpoint URL: <scheme>://<host>:<port><path> (scheme 은 ws / wss)


태그 plc_address 형식

표기의미
빈 값 또는 _raw_마지막 수신 메시지 전체 (String)
JSON top-level 키 (예: tempC)메시지가 JSON 일 때 해당 키의 값 (String 으로 변환)

예) 서버가 {"tempC":25.7,"humid":40.2} 를 push 하면:

plc_address결과
_raw_{"tempC":25.7,"humid":40.2}
tempC25.7
humid40.2

동작 흐름

  1. connect()HttpClient.newWebSocketBuilder().buildAsync(...) 로 endpoint 에 접속.
  2. (옵션) subscribe-message 가 있으면 한 번 sendText 송신.
  3. 서버가 보내는 모든 텍스트 메시지를 onText 에서 누적 → fragment 종료 시 handleMessage() 호출.
  4. handleMessage() 는 메시지 전체를 _raw_ 키에 저장. 메시지가 { 로 시작하면 JSON parsing 후 top-level 키별로도 캐시.
  5. 수집 주기마다 PLC collector 가 read(plc_address) 호출 → 캐시 lookup → 그 값 반환.
  6. write(value) 호출 시 sendText 로 그대로 server 에 전송.

onError 발생 시 connected=false 처리. 자동 재연결은 상위 layer 의 reconnect 정책에 위임.


curl 등록 예시

curl -X POST http://<edge-host>/api/v1/opc \
-H "Content-Type: application/json" \
-d '{
"opc_id": "OPC_WS_LINE1",
"opc_type": "WEBSOCKET",
"opc_name": "Line1 WebSocket Stream",
"opc_agent_ip": "192.168.10.99",
"opc_agent_port": "8765",
"site_id": "SITE_00001",
"auto_collect": true,
"timecycle": 1000,
"options": {
"path": "/stream/v1",
"tls": "false",
"subscribe-message": "{\"op\":\"subscribe\",\"topic\":\"line1\"}"
},
"tag_list": [
{"tag_id":"OPC_WS_LINE1_T01","tag_name":"TempC","plc_address":"tempC","data_type":"Float"},
{"tag_id":"OPC_WS_LINE1_T02","tag_name":"Humid","plc_address":"humid","data_type":"Float"},
{"tag_id":"OPC_WS_LINE1_T03","tag_name":"Raw", "plc_address":"_raw_","data_type":"String"}
]
}'

흔한 에러 + 해결

메시지 / 증상원인해결
[WS] connect 실패: ...handshake...URL / path 오류, 서버 미동작wscat -c ws://<host>:<port><path> 로 직접 검증
[WS] connect 실패: ...timeout...5 초 안에 핸드쉐이크 미완료방화벽 / 포트 / TLS 설정 점검
read() 가 늘 빈 문자열서버가 메시지를 안 보냄 / subscribe-message 미설정서버 콘솔에서 broadcast 흐름 확인. 필요한 subscribe payload 등록
JSON 키별 read 가 비어있음메시지가 JSON 이 아님 / 중첩 (nested)_raw_ 로 받은 후 후처리 또는 별도 parser. (현재 driver 는 top-level 만 추출)
TLS 인증서 오류자체서명 (self-signed)운영 환경에서 valid cert 사용 권장. 임시 시 cacerts 추가

한계 / 추후 확장

  • JSON path 미지원: 현재 a.b.c 깊이 추출은 미구현. top-level key 만. 필요 시 ${VALUE} 후처리 또는 추후 옵션 도입.
  • 자동 재연결 미내장: PLC collector 의 reconnect 주기 / 정책에 의존. 별도 백오프는 향후.
  • binary frame 미처리: text frame (onText) 만 처리. JSON 등 텍스트 streaming 위주.
  • per-message-deflate / 헤더 인증: 표준 HttpClient.WebSocket 의 기본만 사용. 커스텀 헤더는 추후 옵션화 예정.

참고

  • Java 표준 java.net.http.WebSocket API: <https://docs.oracle.com/en/java/javase/17/docs/api/java.net.http/java/net/http/WebSocket.html>
  • 빠른 dummy server: Python websockets 라이브러리 — python -m websockets.server.run :8765