Working with the WebSocket Server
The SDK integrates with the Foxglove app using a WebSocket server.
For an introduction to basic logging with the WebSocket server, see the earlier example. This guide describes more of the features that the SDK and app support.
Connecting
By default, the server listens on 127.0.0.1
, port 8765
, which matches the app's default connection string. In the app, choose "Open connection" from the dashboard to connect to a server — see Connecting to data for details.
Here, we'll start a server in the SDK with the default options.
- Rust
- Python
- C++
let server = foxglove::WebSocketServer::new()
.start_blocking()
.expect("Server failed to start");
The examples here use .start_blocking()
, but if you're in an async context (such as a tokio.rs runtime),
then you can use start().await
instead.
import foxglove
server = foxglove.start_server()
#include <foxglove/server.hpp>
foxglove::WebSocketServerOptions ws_options;
auto server_result = foxglove::WebSocketServer::create(std::move(ws_options));
if (!server_result.has_value()) {
std::cerr << "Failed to create server: " << foxglove::strerror(server_result.error()) << '\n';
return 1;
}
auto server = std::move(server_result.value());
You can change the host or port (and other options) when creating the server. If port is 0
, an
available port will be automatically selected.
- Rust
- Python
- C++
let server = foxglove::WebSocketServer::new()
.bind("127.0.0.1", 0)
.start_blocking()
.expect("Server failed to start");
For more options, see the WebSocketServer
reference
import foxglove
server = foxglove.start_server(host="192.168.0.101", port=0)
For more options, see the start_server
reference
#include <foxglove/server.hpp>
foxglove::WebSocketServerOptions ws_options;
ws_options.host = "192.168.0.101";
ws_options.port = 0;
auto server_result = foxglove::WebSocketServer::create(std::move(ws_options));
if (!server_result.has_value()) {
std::cerr << "Failed to create server: " << foxglove::strerror(server_result.error()) << '\n';
return 1;
}
auto server = std::move(server_result.value());
For more options, see the WebSocketServerOptions
reference
Sessions
A session describes an instance of a running WebSocket server. The server provides the connected app with a session ID, which allows the app to know whether it is connecting to a new server, or re-connecting to an existing one. You can provide a session ID explicitly when starting the server; by default, it will generate one based on the current time.
Clearing a session
During playback, you may need to reset the visualization state. For example, if you're streaming data from multiple simulation runs, you'll want to clear out data from previous runs. You can do this by clearing the running session.
- Rust
- Python
- C++
server.clear_session(None);
server.clear_session()
auto error = server.clearSession();
if (error != foxglove::FoxgloveError::Ok) {
std::cerr << "Failed to clear session: " << foxglove::strerror(error) << '\n';
}
When clearing the session, you can optionally provide a new session ID to use instead of the default time-based ID.
Status Messages
You can publish a status message to the app, which will be displayed in the Problems panel. Use this to communicate warnings and errors within the app.
- Rust
- Python
- C++
server.publish_status(Status::error("Error!"));
If you provide a status ID when publishing, you can later remove the status from the Problems panel by calling remove_status
.
from foxglove.websocket import StatusLevel
server.publish_status("Error!", StatusLevel.Error)
If you provide a status ID when publishing, you can later remove the status from the Problems panel by calling remove_status
.
server.publishStatus(foxglove::WebSocketServerStatusLevel::Error, "Error!")
If you provide a status ID when publishing, you can later remove the status from the Problems panel by calling removeStatus
.
Capabilities
You can augment the SDK's WebSocket server with additional capabilities by implementing interfaces which unlock more features in the app.
The following sections describe the additional capabilities you can build into your server.
Advertising capabilities
In some cases, as noted below, you'll have to specifically advertise these capabilities to the app so that it
knows when they're available. To do this, provide one or more capabilities when constructing your server. In this
example, we augment the Connecting example above to advertise Time
and ClientPublish
capabilities.
- Rust
- Python
- C++
use foxglove::websocket::Capability;
let server = foxglove::WebSocketServer::new()
.capabilities([Capability::Time, Capability::ClientPublish])
.start_blocking();
from foxglove import start_server
from foxglove.websocket import Capability
server = start_server(capabilities=[Capability.Time, Capability.ClientPublish])
foxglove::WebSocketServerOptions options;
options.capabilities = {foxglove::WebSocketServerCapabilities::Time &
foxglove::WebSocketServerCapabilities::ClientPublish};
auto server = foxglove::WebSocketServer::create(std::move(options)).value();
// Error checking omitted for brevity
Handling messages from the app
The Publish panel can be used send messages to the server from the app.
In addition to advertising ClientPublish
support, you'll declare the encodings you support and implement at least one callback function to receive the messages.
- Rust
- Python
- C++
This example uses serde_json
to parse messages from the client:
cargo add serde_json
use foxglove::websocket::{Capability, Client, ClientChannel, ServerListener};
struct ExampleCallbackHandler;
impl ServerListener for ExampleCallbackHandler {
fn on_message_data(&self, client: Client, channel: &ClientChannel, message: &[u8]) {
let json: serde_json::Value =
serde_json::from_slice(message).expect("Failed to parse message");
println!(
"Client {} published to channel {}: {json}",
client.id(),
channel.id
);
}
}
let server = foxglove::WebSocketServer::new()
.capabilities([Capability::ClientPublish])
.supported_encodings(["json"])
.listener(Arc::new(ExampleCallbackHandler))
.start_blocking();
The ServerListener
interface provides ways to track advertisements and subscriptions. For
more detail, see the client-publish example.
from foxglove import start_server
from foxglove.websocket import (Capability, ServerListener)
class ExampleListener(ServerListener):
def on_message_data(
self,
client: Client,
client_channel_id: int,
data: bytes,
) -> None:
logging.info(f"Message from client {client.id} on channel {client_channel_id}")
logging.info(f"Data: {data!r}")
listener = ExampleListener()
server = start_server(
capabilities=[Capability.ClientPublish],
supported_encodings=["json"],
server_listener=listener,
)
The ServerListener
interface provides ways to track advertisements and subscriptions. For
more detail, see the live-visualization example.
#include <cstddef>
foxglove::WebSocketServerOptions options;
options.capabilities = {foxglove::WebSocketServerCapabilities::ClientPublish};
options.supported_encodings = {"json"};
options.callbacks.onMessageData =
[&](uint32_t client_id [[maybe_unused]],
uint32_t client_channel_id [[maybe_unused]], const std::byte *data,
size_t data_len) {
std::cout << "Received message from client " << client_id
<< " on channel " << client_channel_id << std::endl;
};
auto server = foxglove::WebSocketServer::create(std::move(options)).value();
Broadcasting time
You can broadcast timestamps (nanoseconds since epoch) from the server to inform many panels in the app of the server time, in cases where message timestamps disagree with wall time.
You must add the Time
capability to your server. See advertising capabilities.
- Rust
- Python
- C++
server.broadcast_time(42);
server.broadcast_time(42)
server.broadcastTime(42);