WebSocket 클라이언트 드라이버
개요
WebSocket 드라이버는 외부 streaming server (ws / wss) 의 endpoint 에 클라이언트로 접속해
들어오는 메시지를 캐시하고, 수집 주기마다 read() 하면 가장 최근 값을 반환하는 push 모델이다.
| 항목 | 값 |
|---|---|
opc_type | WEBSOCKET |
| 구현 클래스 | 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 / port | WebSocket 서버 주소 | — | 192.168.10.99 / 8765, stream.example.com / 443 |
options.path | endpoint path | / | /stream/v1, /realtime/tag |
options.tls | wss 사용 여부 | false | true (wss) / false (ws) |
options.subscribe-message | 연결 직후 송신할 메시지 (옵션) | — | {"op":"subscribe","topic":"line1.tempC"} |
timecycle | read 주기 (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} |
tempC | 25.7 |
humid | 40.2 |
동작 흐름
connect()시HttpClient.newWebSocketBuilder().buildAsync(...)로 endpoint 에 접속.- (옵션)
subscribe-message가 있으면 한 번sendText송신. - 서버가 보내는 모든 텍스트 메시지를
onText에서 누적 → fragment 종료 시handleMessage()호출. handleMessage()는 메시지 전체를_raw_키에 저장. 메시지가{로 시작하면 JSON parsing 후 top-level 키별로도 캐시.- 수집 주기마다 PLC collector 가
read(plc_address)호출 → 캐시 lookup → 그 값 반환. 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.WebSocketAPI:<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