WebSocket Client Driver
Overview
The WebSocket driver is a push model: it connects as a client to an external streaming server (ws / wss) endpoint,
caches incoming messages, and returns the most recent value whenever read() is called on each collection cycle.
| Item | Value |
|---|---|
opc_type | WEBSOCKET |
| Implementation class | plantpulse.driver.protocol.websocket.WebSocketDriver |
| Underlying library | java.net.http.HttpClient.WebSocket (Java 21 runtime standard API) |
| read | ✅ (message cache) |
| write | ✅ (sendText) |
| Security | TLS (wss) — options.tls=true |
Where the HTTP driver receives a push (REST POST) from an external system, the WebSocket driver has the
edge make an outbound connection as a client to receive streaming messages.
OPC Registration Form / Options
| Field | Meaning | Default | Example |
|---|---|---|---|
host / port | WebSocket server address | — | 192.168.10.99 / 8765, stream.example.com / 443 |
options.path | endpoint path | / | /stream/v1, /realtime/tag |
options.tls | Whether to use wss | false | true (wss) / false (ws) |
options.subscribe-message | Message to send immediately after connection (optional) | — | {"op":"subscribe","topic":"line1.tempC"} |
timecycle | read interval (ms) | — | 1000 |
Actual endpoint URL: <scheme>://<host>:<port><path> (scheme is ws / wss)
Tag plc_address Format
| Notation | Meaning |
|---|---|
Empty value or _raw_ | The entire last received message (String) |
JSON top-level key (e.g. tempC) | The value of that key when the message is JSON (converted to String) |
Example) If the server pushes {"tempC":25.7,"humid":40.2}:
plc_address | Result |
|---|---|
_raw_ | {"tempC":25.7,"humid":40.2} |
tempC | 25.7 |
humid | 40.2 |
Operation Flow
- On
connect(), connect to the endpoint withHttpClient.newWebSocketBuilder().buildAsync(...). - (Optional) If
subscribe-messageis present, sendsendTextonce. - Accumulate every text message sent by the server in
onText→ callhandleMessage()when the fragment ends. handleMessage()stores the whole message under the_raw_key. If the message starts with{, it is parsed as JSON and also cached per top-level key.- On each collection cycle the PLC collector calls
read(plc_address)→ cache lookup → returns that value. - When
write(value)is called, the value is sent to the server as-is viasendText.
On onError, connected=false is performed. Automatic reconnection is delegated to the reconnect policy of the upper layer.
curl Registration Example
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"}
]
}'
Common Errors + Resolutions
| Message / Symptom | Cause | Resolution |
|---|---|---|
[WS] connect 실패: ...handshake... | URL / path error, server not running | Verify directly with wscat -c ws://<host>:<port><path> |
[WS] connect 실패: ...timeout... | Handshake not completed within 5 seconds | Check firewall / port / TLS settings |
read() is always an empty string | Server sends no messages / subscribe-message not configured | Check the broadcast flow in the server console. Register the required subscribe payload |
| Per-JSON-key read is empty | Message is not JSON / is nested | Receive with _raw_ and post-process, or use a separate parser. (The current driver extracts top-level only) |
| TLS certificate error | Self-signed certificate | Using a valid cert is recommended in production. As a temporary measure, add cacerts |
Limitations / Future Extensions
- No JSON path support: Extraction at
a.b.cdepth is not implemented. Top-level keys only. If needed, post-process with${VALUE}or wait for a future option. - No built-in automatic reconnection: Depends on the PLC collector's reconnect interval / policy. A dedicated backoff is planned.
- Binary frames not handled: Only text frames (
onText) are processed. Intended mainly for text streaming such as JSON. - per-message-deflate / header authentication: Only the basics of the standard
HttpClient.WebSocketare used. Custom headers will be made configurable later.
References
- Java standard
java.net.http.WebSocketAPI:<https://docs.oracle.com/en/java/javase/17/docs/api/java.net.http/java/net/http/WebSocket.html> - Quick dummy server: Python
websocketslibrary —python -m websockets.server.run :8765