Quickstart example
This example illustrates logging to an MCAP file and a running Foxglove app using the SDK. It logs a scene update with an animated cube which can be viewed in the Foxglove app as described below.
Example code
- Rust
- Python
- C++
use std::ops::Add;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::time::SystemTime;
use foxglove::schemas::{Color, CubePrimitive, SceneEntity, SceneUpdate, Vector3};
use foxglove::{LazyChannel, LazyRawChannel, McapWriter};
const FILE_NAME: &str = "quickstart-rust.mcap";
// Our example logs data on a couple of different topics, so we'll create a
// channel for each. We can use a channel like Channel<SceneUpdate> to log
// Foxglove schemas, or a generic RawChannel to log custom data.
static SCENE: LazyChannel<SceneUpdate> = LazyChannel::new("/scene");
static SIZE: LazyRawChannel = LazyRawChannel::new("/size", "json");
fn main() {
let env = env_logger::Env::default().default_filter_or("debug");
env_logger::init_from_env(env);
let done = Arc::new(AtomicBool::default());
ctrlc::set_handler({
let done = done.clone();
move || {
done.store(true, Ordering::Relaxed);
}
})
.expect("Failed to set SIGINT handler");
// We'll log to both an MCAP file, and to a running Foxglove app via a server.
let mcap = McapWriter::new()
.create_new_buffered_file(FILE_NAME)
.expect("Failed to start mcap writer");
// Start a server to communicate with the Foxglove app. This will run indefinitely, even if
// references are dropped.
foxglove::WebSocketServer::new()
.start_blocking()
.expect("Server failed to start");
while !done.load(Ordering::Relaxed) {
let size = SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_secs_f64()
.sin()
.abs()
.add(1.0);
// Log messages on the channel until interrupted. By default, each message
// is stamped with the current time.
SIZE.log(format!("{{\"size\": {size}}}").as_bytes());
SCENE.log(&SceneUpdate {
deletions: vec![],
entities: vec![SceneEntity {
id: "box".to_string(),
cubes: vec![CubePrimitive {
size: Some(Vector3 {
x: size,
y: size,
z: size,
}),
color: Some(Color {
r: 1.0,
g: 0.0,
b: 0.0,
a: 1.0,
}),
..Default::default()
}],
..Default::default()
}],
});
std::thread::sleep(std::time::Duration::from_millis(33));
}
mcap.close().expect("Failed to close mcap writer");
}
For more examples, see the source examples and reference documentation.
import math
import time
import foxglove
from foxglove.channel import Channel
from foxglove.channels import SceneUpdateChannel
from foxglove.schemas import (
Color,
CubePrimitive,
SceneEntity,
SceneUpdate,
Vector3,
)
foxglove.set_log_level("DEBUG")
# Our example logs data on a couple of different topics, so we'll create a
# channel for each. We can use a channel like SceneUpdateChannel to log
# Foxglove schemas, or a generic Channel to log custom data.
scene_channel = SceneUpdateChannel("/scene")
size_channel = Channel("/size", message_encoding="json")
# We'll log to both an MCAP file, and to a running Foxglove app via a server.
file_name = "quickstart-python.mcap"
writer = foxglove.open_mcap(file_name)
server = foxglove.start_server()
while True:
size = abs(math.sin(time.time())) + 1
# Log messages on both channels until interrupted. By default, each message
# is stamped with the current time.
size_channel.log({"size": size})
scene_channel.log(
SceneUpdate(
entities=[
SceneEntity(
cubes=[
CubePrimitive(
size=Vector3(x=size, y=size, z=size),
color=Color(r=1.0, g=0, b=0, a=1.0),
)
],
),
]
)
)
time.sleep(0.033)
For more examples, see the source examples or examples in documentation.
#include <foxglove/channel.hpp>
#include <foxglove/mcap.hpp>
#include <foxglove/server.hpp>
#include <atomic>
#include <chrono>
#include <csignal>
#include <functional>
#include <iostream>
#include <thread>
using namespace std::chrono_literals;
// This example logs custom data on an "example" topic. Open Foxglove and connect to the running
// server. Then add a Raw Message panel, and choose the "example" topic.
int main(int argc, const char* argv[]) {
static std::function<void()> sigintHandler;
std::signal(SIGINT, [](int) {
if (sigintHandler) {
sigintHandler();
}
});
// We'll log to both an MCAP file, and to a running Foxglove app.
foxglove::McapWriterOptions mcap_options = {};
mcap_options.path = "quickstart-cpp.mcap";
auto writerResult = foxglove::McapWriter::create(mcap_options);
if (!writerResult.has_value()) {
std::cerr << "Failed to create writer: " << foxglove::strerror(writerResult.error()) << '\n';
return 1;
}
auto writer = std::move(writerResult.value());
// Start a server to communicate with the Foxglove app.
foxglove::WebSocketServerOptions ws_options;
ws_options.host = "127.0.0.1";
ws_options.port = 8765;
auto serverResult = foxglove::WebSocketServer::create(std::move(ws_options));
if (!serverResult.has_value()) {
std::cerr << "Failed to create server: " << foxglove::strerror(serverResult.error()) << '\n';
return 1;
}
auto server = std::move(serverResult.value());
std::cerr << "Server listening on port " << server.port() << '\n';
std::atomic_bool done = false;
sigintHandler = [&] {
done = true;
};
// Our example logs custom data on an "example" topic, so we'll create a channel for that.
foxglove::Schema schema;
schema.encoding = "jsonschema";
std::string schemaData = R"({
"type": "object",
"properties": {
"val": { "type": "number" }
}
})";
schema.data = reinterpret_cast<const std::byte*>(schemaData.data());
schema.dataLen = schemaData.size();
auto channelResult = foxglove::Channel::create("example", "json", std::move(schema));
if (!channelResult.has_value()) {
std::cerr << "Failed to create channel: " << foxglove::strerror(channelResult.error()) << '\n';
return 1;
}
auto channel = std::move(channelResult.value());
while (!done) {
// Log messages on the channel until interrupted. By default, each message
// is stamped with the current time.
std::this_thread::sleep_for(33ms);
auto now = std::chrono::system_clock::now().time_since_epoch().count();
std::string msg = "{\"val\": " + std::to_string(now) + "}";
channel.log(reinterpret_cast<const std::byte*>(msg.data()), msg.size());
}
return 0;
}
For more examples, see the source examples.
Running the app
Now that you're running the above example, let's view the live visualization.
- Open the Foxglove app or visit https://app.foxglove.dev/.
- Click "Open connection..." and open a Foxglove WebSocket connection with the default URL.
- Add a 3D panel to your layout.
- Subscribe to the "/scene" topic by toggling its visibility in the panel settings sidebar.
See the Live data documentation for help connecting to the app, and the 3D panel documentation for help configuring the scene.