Skip to main content

Examples

Practical examples of using hdds-ws in web applications.

Basic JavaScript Client

class DdsClient {
constructor(url = 'ws://localhost:9090/ws') {
this.url = url;
this.ws = null;
this.handlers = new Map();
this.connect();
}

connect() {
this.ws = new WebSocket(this.url);

this.ws.onopen = () => {
console.log('Connected to HDDS');
this.onConnect?.();
};

this.ws.onclose = () => {
console.log('Disconnected, reconnecting in 3s...');
setTimeout(() => this.connect(), 3000);
};

this.ws.onmessage = (event) => {
const msg = JSON.parse(event.data);
this.handleMessage(msg);
};
}

handleMessage(msg) {
switch (msg.type) {
case 'data':
const handler = this.handlers.get(msg.topic);
if (handler) handler(msg.sample, msg.info);
break;
case 'error':
console.error(`DDS Error: ${msg.message}`);
break;
}
}

subscribe(topic, callback) {
this.handlers.set(topic, callback);
this.send({ type: 'subscribe', topic });
}

unsubscribe(topic) {
this.handlers.delete(topic);
this.send({ type: 'unsubscribe', topic });
}

publish(topic, data) {
this.send({ type: 'publish', topic, data });
}

send(msg) {
if (this.ws?.readyState === WebSocket.OPEN) {
this.ws.send(JSON.stringify(msg));
}
}
}

// Usage
const dds = new DdsClient();
dds.subscribe('temperature', (sample, info) => {
console.log(`Temperature: ${sample.data}°C (seq: ${info.sequence})`);
});

React Hook

import { useEffect, useState, useCallback, useRef } from 'react';

interface DdsSample {
topic: string;
data: any;
info?: {
sequence?: number;
source_timestamp_ms?: number;
};
}

export function useDds(wsUrl = 'ws://localhost:9090/ws') {
const [connected, setConnected] = useState(false);
const [samples, setSamples] = useState<Map<string, DdsSample>>(new Map());
const wsRef = useRef<WebSocket | null>(null);

useEffect(() => {
const connect = () => {
const ws = new WebSocket(wsUrl);
wsRef.current = ws;

ws.onopen = () => setConnected(true);
ws.onclose = () => {
setConnected(false);
setTimeout(connect, 3000);
};

ws.onmessage = (event) => {
const msg = JSON.parse(event.data);
if (msg.type === 'data') {
setSamples(prev => new Map(prev).set(msg.topic, {
topic: msg.topic,
data: msg.sample,
info: msg.info
}));
}
};
};

connect();
return () => wsRef.current?.close();
}, [wsUrl]);

const subscribe = useCallback((topic: string) => {
wsRef.current?.send(JSON.stringify({ type: 'subscribe', topic }));
}, []);

const publish = useCallback((topic: string, data: any) => {
wsRef.current?.send(JSON.stringify({ type: 'publish', topic, data }));
}, []);

return { connected, samples, subscribe, publish };
}

// Usage in component
function TemperatureDisplay() {
const { connected, samples, subscribe } = useDds();

useEffect(() => {
subscribe('temperature');
}, [subscribe]);

const temp = samples.get('temperature');

return (
<div>
<span>{connected ? '🟢' : '🔴'}</span>
<h2>Temperature: {temp?.data?.data ?? '--'}°C</h2>
</div>
);
}

Vue 3 Composable

import { ref, onMounted, onUnmounted } from 'vue';

export function useDds(wsUrl = 'ws://localhost:9090/ws') {
const connected = ref(false);
const samples = ref(new Map());
let ws: WebSocket | null = null;

const connect = () => {
ws = new WebSocket(wsUrl);

ws.onopen = () => connected.value = true;
ws.onclose = () => {
connected.value = false;
setTimeout(connect, 3000);
};

ws.onmessage = (event) => {
const msg = JSON.parse(event.data);
if (msg.type === 'data') {
samples.value.set(msg.topic, msg.sample);
}
};
};

const subscribe = (topic: string) => {
ws?.send(JSON.stringify({ type: 'subscribe', topic }));
};

const publish = (topic: string, data: any) => {
ws?.send(JSON.stringify({ type: 'publish', topic, data }));
};

onMounted(connect);
onUnmounted(() => ws?.close());

return { connected, samples, subscribe, publish };
}

Real-Time Dashboard

Complete example of a sensor monitoring dashboard:

<!DOCTYPE html>
<html>
<head>
<title>Sensor Dashboard</title>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<style>
body {
font-family: sans-serif;
background: #1a1a2e;
color: #eee;
padding: 20px;
}
.grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 20px;
}
.card {
background: #16213e;
border-radius: 12px;
padding: 20px;
}
.value {
font-size: 3rem;
font-weight: bold;
color: #00d4ff;
}
</style>
</head>
<body>
<h1>🌡️ DDS Sensor Dashboard</h1>
<div class="grid">
<div class="card">
<h3>Temperature</h3>
<div class="value" id="temp">--</div>
<canvas id="tempChart"></canvas>
</div>
<div class="card">
<h3>Humidity</h3>
<div class="value" id="humidity">--</div>
</div>
</div>

<script>
const ctx = document.getElementById('tempChart').getContext('2d');
const chart = new Chart(ctx, {
type: 'line',
data: {
labels: [],
datasets: [{
label: 'Temperature',
data: [],
borderColor: '#00d4ff',
tension: 0.4
}]
},
options: {
scales: { y: { beginAtZero: false } },
animation: false
}
});

const ws = new WebSocket('ws://localhost:9090/ws');

ws.onopen = () => {
ws.send(JSON.stringify({ type: 'subscribe', topic: 'temperature' }));
ws.send(JSON.stringify({ type: 'subscribe', topic: 'humidity' }));
};

ws.onmessage = (event) => {
const msg = JSON.parse(event.data);
if (msg.type !== 'data') return;

const value = msg.sample.data || msg.sample.value || JSON.stringify(msg.sample);

if (msg.topic === 'temperature') {
document.getElementById('temp').textContent = value + '°C';

// Update chart
chart.data.labels.push(new Date().toLocaleTimeString());
chart.data.datasets[0].data.push(parseFloat(value));
if (chart.data.labels.length > 20) {
chart.data.labels.shift();
chart.data.datasets[0].data.shift();
}
chart.update();
}

if (msg.topic === 'humidity') {
document.getElementById('humidity').textContent = value + '%';
}
};
</script>
</body>
</html>

Integration with ROS 2

Monitor and interact with ROS 2 nodes via the WebSocket bridge:

const ws = new WebSocket('ws://localhost:9090/ws');

ws.onopen = () => {
// Subscribe to ROS 2 topics
ws.send(JSON.stringify({ type: 'subscribe', topic: 'rt/chatter' }));
ws.send(JSON.stringify({ type: 'subscribe', topic: 'rt/robot_state' }));
};

ws.onmessage = (event) => {
const msg = JSON.parse(event.data);

if (msg.type === 'data') {
// ROS 2 std_msgs/String is decoded automatically
if (msg.topic === 'rt/chatter') {
console.log('Chatter:', msg.sample.data);
}
}
};

// Publish to ROS 2 topic
function sendCommand(cmd) {
ws.send(JSON.stringify({
type: 'publish',
topic: 'rt/cmd_vel',
data: {
linear: { x: cmd.speed, y: 0, z: 0 },
angular: { x: 0, y: 0, z: cmd.turn }
}
}));
}

Python WebSocket Client

import asyncio
import json
import websockets

async def dds_client():
uri = "ws://localhost:9090/ws"

async with websockets.connect(uri) as ws:
# Subscribe to topic
await ws.send(json.dumps({
"type": "subscribe",
"topic": "temperature"
}))

# Receive messages
async for message in ws:
msg = json.loads(message)

if msg["type"] == "data":
print(f"[{msg['topic']}] {msg['sample']}")

elif msg["type"] == "welcome":
print(f"Connected to domain {msg['domain']}")

# Run
asyncio.run(dds_client())

Deployment with Nginx

Proxy WebSocket connections through Nginx:

upstream hdds_ws {
server 127.0.0.1:9090;
}

server {
listen 443 ssl;
server_name dds.example.com;

location /ws {
proxy_pass http://hdds_ws/ws;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_read_timeout 86400;
}

location / {
proxy_pass http://hdds_ws/;
}
}

Then start hdds-ws bound to localhost only:

hdds-ws --bind 127.0.0.1 --port 9090