Skip to content

Commit 1d814bc

Browse files
authored
Add spec doc, update README (#3)
Import spec from google doc, along with some updates and reformatting
1 parent 8fa1146 commit 1d814bc

File tree

4 files changed

+215
-7
lines changed

4 files changed

+215
-7
lines changed

README.md

+24-5
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,38 @@
1-
## Virtualenv usage
1+
# Foxglove Studio WebSocket protocol libraries
2+
3+
This repository provides a protocol specification and reference implementations enabling [Foxglove Studio](https://github.com/foxglove/studio) to ingest arbitrary “live” streamed data.
4+
5+
The protocol is encoding-agnostic, i.e. it can support Protobuf messages, ROS 1 or 2 messages, etc. (as long as the desired encoding is supported by both client and server).
6+
7+
## Documentation
8+
9+
- [Protocol spec](docs/spec.md)
10+
11+
## Development
12+
13+
### Virtualenv usage
214

315
```
416
python3.8 -m venv venv
517
. venv/bin/activate
6-
pip install -r requirements.txt -r dev-requirements.txt
18+
pip install -r python/requirements.txt -r python/dev-requirements.txt
719
```
820

9-
### h5py installation on M1 Macs
21+
#### h5py installation on M1 Macs
22+
1023
```
1124
brew install hdf5
1225
HDF5_DIR=/opt/homebrew/opt/hdf5 pip install -v --no-build-isolation h5py
1326
```
1427

15-
## Run example server
28+
### Run example server
29+
30+
```
31+
python -m python.src [hdf5 file]
32+
```
33+
34+
### Run example client
1635

1736
```
18-
python -m src /path/to/person.hdf5
37+
yarn workspace @foxglove/ws-protocol example [host] [topic]
1938
```

docs/spec.md

+189
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
# Foxglove Studio WebSocket protocol v1
2+
3+
## Protocol overview
4+
5+
- An application wishing to provide data for streamed consumption by Foxglove Studio hosts a [WebSocket](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API) server.
6+
7+
- The client (Foxglove Studio) will specify supported subprotocols (a standard part of the WebSocket handshake) when establishing the connection. The current version of this document corresponds to subprotocol `foxglove.websocket.v1`. The server must select a subprotocol with which it is compatible for the connection to continue.
8+
9+
- Example client code in JavaScript:
10+
```js
11+
new WebSocket("ws://...", ["foxglove.websocket.v1"]);
12+
```
13+
14+
- Both text and binary messages are used on the WebSocket connection.
15+
16+
- Each text message must be a JSON object having a field called `op` which identifies the type of message. The interpretation of the other fields depends on the opcode.
17+
18+
- Similarly, each binary message starts with a 1-byte opcode identifying the type of message. The interpretation of the remaining bytes depends on the opcode.
19+
20+
- Upon establishing a connection, the server must send clients a Server Info message with a list of supported capabilities.
21+
22+
## Summary of messages
23+
24+
### Sent by server
25+
26+
- [Server Info](#server-info) (json)
27+
- [Status](#status) (json)
28+
- [Advertise](#advertise) (json)
29+
- [Unadvertise](#unadvertise) (json)
30+
- [Message Data](#message-data) (binary)
31+
32+
### Sent by client
33+
34+
- [Subscribe](#subscribe) (json)
35+
- [Unsubscribe](#unsubscribe) (json)
36+
37+
## JSON messages
38+
39+
Each JSON message must be an object containing a field called `op` which identifies the type of message.
40+
41+
### Server Info
42+
43+
- This message is always sent to new clients upon connection.
44+
45+
#### Fields
46+
47+
- `op`: string `"serverInfo"`
48+
- `name`: free-form information about the server which the client may optionally display or use for debugging purposes
49+
- `capabilities`: array of strings (reserved for future expansions to the spec)
50+
51+
#### Example
52+
53+
```json
54+
{
55+
"op": "serverInfo",
56+
"name": "example server",
57+
"capabilities": []
58+
}
59+
```
60+
61+
### Status
62+
63+
- The server may send this message at any time. Client developers may use it for debugging purposes, display it to the end user, or ignore it.
64+
65+
#### Fields
66+
67+
- `op`: string `"status"`
68+
- `level`: 0 (info), 1 (warning), 2 (error)
69+
- `message`: string
70+
71+
#### Example
72+
73+
```json
74+
{
75+
"op": "status",
76+
"level": 0,
77+
"message": "Some info"
78+
}
79+
```
80+
81+
### Advertise
82+
83+
- Informs the client about newly available channels.
84+
- At least one Advertise message is always sent to new clients upon connection.
85+
86+
#### Fields
87+
88+
- `op`: string `"advertise"`
89+
- `channels`: array of:
90+
- `id`: number. The server may reuse ids when channels disappear and reappear, but only if the channel keeps the exact same topic, encoding, schemaName, and schema. Clients will use this unique id to cache schema info and deserialization routines.
91+
- `topic`: string
92+
- `encoding`: string
93+
- `schemaName`: string
94+
- `schema`: string
95+
96+
#### Example
97+
98+
```json
99+
{
100+
"op": "advertise",
101+
"channels": [
102+
{
103+
"id": 1,
104+
"topic": "foo",
105+
"encoding": "protobuf",
106+
"schemaName": "ExampleMsg",
107+
"schema": "ZXhhbXBsZSBkYXRh"
108+
}
109+
]
110+
}
111+
```
112+
113+
### Unadvertise
114+
115+
Informs the client that channels are no longer available.
116+
117+
#### Fields
118+
119+
- `op`: string `"unadvertise"`
120+
- `channelIds`: array of number, corresponding to previous Advertise
121+
122+
#### Example
123+
124+
```json
125+
{
126+
"op": "unadvertise",
127+
"channelIds": [1, 2]
128+
}
129+
```
130+
131+
### Subscribe
132+
133+
- Requests that the server start streaming messages on a given topic (or topics) to the client.
134+
- A client may only have one subscription for each channel at a time.
135+
136+
#### Fields
137+
138+
- `op`: string `"subscribe"`
139+
- `subscriptions`: array of:
140+
- `id`: number chosen by the client. The client may not reuse ids across multiple active subscriptions. The server may ignore subscriptions that attempt to reuse an id (and send an error status message). After unsubscribing, the client may reuse the id.
141+
- `channelId`: number, corresponding to previous Advertise message(s)
142+
143+
#### Example
144+
145+
```json
146+
{
147+
"op": "subscribe",
148+
"subscriptions": [
149+
{ "id": 0, "channelId": 3 },
150+
{ "id": 1, "channelId": 5 }
151+
]
152+
}
153+
```
154+
155+
### Unsubscribe
156+
157+
- Requests that the server stop streaming messages to which the client previously subscribed.
158+
159+
#### Fields
160+
161+
- `op`: string `"subscribe"`
162+
- `subscriptionIds`: array of number, corresponding to previous Subscribe message(s)
163+
164+
#### Example
165+
166+
```json
167+
{
168+
"op": "unsubscribe",
169+
"subscriptionIds": [0, 1]
170+
}
171+
```
172+
173+
## Binary messages
174+
175+
All binary messages must start with a 1-byte opcode identifying the type of message. The interpretation of the remaining bytes depends on the opcode.
176+
177+
All integer types explicitly specified (uint32, uint64, etc.) in this section are encoded with **little-endian** byte order.
178+
179+
### Message Data
180+
181+
- Provides a raw message payload, encoded as specified in the Advertise corresponding to the channel.
182+
- Subscription id must correspond to a Subscribe that was previously sent.
183+
184+
| Bytes | Type | Description |
185+
| --------------- | ------- | ------------------------------- |
186+
| 1 | opcode | 0x01 |
187+
| 4 | uint32 | subscription id |
188+
| 8 | uint64 | receive timestamp (nanoseconds) |
189+
| remaining bytes | uint8[] | message payload |

python/src/server.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ async def _run(self):
8686
self._handle_connection,
8787
self.host,
8888
self.port,
89-
subprotocols=[Subprotocol("x-foxglove-1")],
89+
subprotocols=[Subprotocol("foxglove.websocket.v1")],
9090
)
9191
for sock in server.sockets or []:
9292
self._logger.info("Server listening on %s", sock.getsockname())

typescript/src/FoxgloveClient.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ interface IWebSocket {
5151
}
5252

5353
export default class FoxgloveClient {
54-
static SUPPORTED_SUBPROTOCOL = "x-foxglove-1";
54+
static SUPPORTED_SUBPROTOCOL = "foxglove.websocket.v1";
5555

5656
private emitter = new EventEmitter<EventTypes>();
5757
private ws!: IWebSocket;

0 commit comments

Comments
 (0)