Skip to content

Commit 22e1f04

Browse files
facontidavideDavide Faconticlaude
authored
feat(pj_base): add CameraInfo + OccupancyGridUpdate canonical builtin types (#98)
* feat(pj_base): add CameraInfo + OccupancyGridUpdate canonical builtin types Closes the two remaining canonical-object gaps for the ROS pipeline: every common ROS 2 message now maps onto a builtin type. Both are wired end-to-end (struct + codec + proto + enum/ABI/typeOf + tests + docs) at enum values 14/15, following the existing owned (FrameTransforms) and byte-backed (OccupancyGrid) templates. - CameraInfo (owned): pinhole calibration K/D/R/P + dimensions, packed-double codec. Mirrors sensor_msgs/CameraInfo; binning/ROI omitted (additive later). Correlated to an image topic by name convention; carries no topic linkage. - OccupancyGridUpdate (byte-backed): incremental sub-rectangle patch for an OccupancyGrid. Deliberately no origin/resolution — a stateful consumer pairs it with the base grid (<base>/costmap_updates <-> <base>/costmap) and places it. Signed int32 cell offsets round-trip via the varint bit pattern. Also dedups a duplicate RobotDescription row in the conversion-examples table. Build 100%; ctest 5/5 (camera_info_codec, occupancy_grid_update_codec, builtin_object, abi_layout_sentinels, occupancy_grid_codec). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(pj_base): SceneEntities gains ModelPrimitive + entity deletions Extends the SceneEntities canonical type so it can losslessly represent visualization_msgs/Marker: - ModelPrimitive (mesh asset by url or inline data) on SceneEntity — covers MESH_RESOURCE markers, which previously had no home. - deletions[] of SceneEntityDeletion{MATCHING_ID, ALL} on SceneEntities — lets a snapshot producer express the removal half of a stateful stream (Marker DELETE / DELETEALL) without an empty-entity sentinel. Both mirror fields in Foxglove's SceneEntity/SceneUpdate that the original port omitted. Additive only: new struct fields + proto field numbers (models=14, deletions=2), updated hand-written codec + round-trip tests, docs. No new BuiltinObjectType, so the C ABI and abi_layout_sentinels are unchanged. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(pj_base): align SceneEntities schema with Foxglove field order No consumers exist yet, so renumber freely to match Foxglove's SceneEntity/SceneUpdate 1:1: - SceneEntity: add metadata=6 (new KeyValuePair message) and shift the primitive lists to 7..14 (arrows..models), matching Foxglove exactly. - AxesPrimitive has no Foxglove counterpart; it moves to field 15, after the Foxglove range, as a documented PlotJuggler extension. - SceneEntities: order deletions=1, entities=2 (Foxglove SceneUpdate order). C++ struct member names are unchanged, so producers/consumers that build by name are unaffected; only the wire numbering moves. Codec + round-trip tests updated (metadata coverage added). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(pj_base): preserve repeated D chunks in CameraInfo decode Drop the per-occurrence info.D.clear() in the field-6 handler. readPackedDouble appends, so a distortion-coefficient array split across multiple packed chunks (valid protobuf, e.g. after message merge/concatenation) was losing all but the last chunk. Now matches the DepthImage decoder. (Codex review P2.) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(pj_base): use shared KeyValuePair.proto in SceneEntities SceneEntity.metadata referenced an inline KeyValuePair message duplicated in SceneEntities.proto, but a canonical pj/KeyValuePair.proto already exists (and is imported by the annotation schemas). Import it instead of redefining, so PJ.KeyValuePair has a single definition. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(pj_base): add Log canonical builtin type (kLog=16) A small owned builtin for textual log messages (timestamp, level, message, name), for a log/console panel. Mirrors the core of Foxglove's Log schema and rcl_interfaces/Log; the source-location fields (file, line) are intentionally omitted. Wires kLog=16 through builtin_object_abi.h (C enum), builtin_object.hpp (enum + name/parse/typeOf + include), abi_layout_sentinels + builtin_object tests, and CMake (codec source + round-trip test). New log.hpp / log_codec.* / Log.proto follow the established owned-type pattern; docs + proto README updated. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Davide Faconti <dfaconti@aurynrobotics.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 2f7bc92 commit 22e1f04

26 files changed

Lines changed: 1374 additions & 37 deletions

docs/builtin_type.md

Lines changed: 88 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,8 @@ Builtin objects fall into two serialization families:
8383

8484
| Family | Current types | Storage model | Codec policy |
8585
|--------|---------------|---------------|--------------|
86-
| Byte-backed views | `Image`, `DepthImage`, `PointCloud`, `CompressedPointCloud`, `OccupancyGrid`, `Mesh3D`, `VideoFrame` | Header fields live in the SDK struct; payload bytes live behind `Span<const uint8_t>` plus `BufferAnchor`. | No mandatory canonical codec; preserve zero-copy views over ROS, MCAP, compressed image, point-cloud, or plugin-owned payloads. If conversion is unavoidable, allocate a new payload and anchor it. |
87-
| Owned values | `ImageAnnotations`, `FrameTransforms`, `SceneEntities`, `AssetVideo`, `RobotDescription`; future marker types | SDK structs own their vectors/strings/scalars directly. | Add explicit codecs when canonical bytes are needed. Codecs serialize the owned value to the protobuf-wire payload described by the `.proto` contract, using shared private wire primitives. `RobotDescription` carries source-format text as-is (no canonical codec) — the format hint distinguishes URDF / SDF / MJCF. |
86+
| Byte-backed views | `Image`, `DepthImage`, `PointCloud`, `CompressedPointCloud`, `OccupancyGrid`, `OccupancyGridUpdate`, `Mesh3D`, `VideoFrame` | Header fields live in the SDK struct; payload bytes live behind `Span<const uint8_t>` plus `BufferAnchor`. | No mandatory canonical codec; preserve zero-copy views over ROS, MCAP, compressed image, point-cloud, or plugin-owned payloads. If conversion is unavoidable, allocate a new payload and anchor it. |
87+
| Owned values | `ImageAnnotations`, `FrameTransforms`, `SceneEntities`, `AssetVideo`, `RobotDescription`, `CameraInfo`; future marker types | SDK structs own their vectors/strings/scalars directly. | Add explicit codecs when canonical bytes are needed. Codecs serialize the owned value to the protobuf-wire payload described by the `.proto` contract, using shared private wire primitives. `RobotDescription` carries source-format text as-is (no canonical codec) — the format hint distinguishes URDF / SDF / MJCF. |
8888

8989
Canonical `.proto` files live under `pj_base/proto/pj` and act as the wire
9090
format contract. One file per top-level message, each named after its message
@@ -120,6 +120,9 @@ annotations, frame transforms, or no builtin object.
120120
| `kSceneEntities` | `PJ::sdk::SceneEntities` | Procedural 3D scene primitives (arrows, cubes, lines, text, …). |
121121
| `kAssetVideo` | `PJ::sdk::AssetVideo` | File-backed video reference plus typed playback metadata. |
122122
| `kRobotDescription` | `PJ::sdk::RobotDescription` | Raw URDF/SDF/MJCF text + format hint. |
123+
| `kCameraInfo` | `PJ::sdk::CameraInfo` | Pinhole camera calibration (intrinsics K, distortion D, rectification R, projection P). |
124+
| `kOccupancyGridUpdate` | `PJ::sdk::OccupancyGridUpdate` | Incremental sub-rectangle patch for a previously-published `OccupancyGrid`. |
125+
| `kLog` | `PJ::sdk::Log` | Textual log message (severity level + text + originating name). |
123126

124127
`BuiltinObject` is `std::any`. Producers store a concrete builtin value in it;
125128
consumers recover the concrete type with `std::any_cast<T>(&object)` or ask
@@ -315,6 +318,34 @@ renderer cares about cell-to-world placement, not pixel layout.
315318
`pj_base/builtin/occupancy_grid_codec.hpp` serializes and deserializes this
316319
type using the canonical `PJ.OccupancyGrid` protobuf wire format.
317320

321+
## OccupancyGridUpdate
322+
323+
`OccupancyGridUpdate` is the incremental counterpart to `OccupancyGrid`: a
324+
row-major sub-rectangle patch into a previously-published base grid (ROS
325+
`map_msgs/OccupancyGridUpdate`, e.g. `<base>/costmap_updates`).
326+
327+
It deliberately carries **no** `origin` / `resolution` — a patch is not
328+
independently placeable. A stateful consumer pairs the update with its base
329+
grid (by topic-name convention, `<base>/costmap_updates``<base>/costmap`)
330+
and positions it at the base's `origin + (x, y) * resolution`. This keeps the
331+
producer stateless and cross-topic-blind; all accumulation / placement lives in
332+
the consumer. The patch is a self-contained snapshot at its own timestamp, so it
333+
stores and decodes like any other object (no replay required at decode time).
334+
335+
| Field | Type | Notes |
336+
|-------|------|-------|
337+
| `timestamp_ns` | `Timestamp` | Timestamp of the update. |
338+
| `frame_id` | `std::string` | Must match the base grid's frame. |
339+
| `x` | `int32_t` | Column offset (cells) of the patch top-left into the base grid. |
340+
| `y` | `int32_t` | Row offset (cells) of the patch top-left into the base grid. |
341+
| `width` | `uint32_t` | Patch width in cells. |
342+
| `height` | `uint32_t` | Patch height in cells. |
343+
| `data` | `Span<const uint8_t>` | Row-major signed-8-bit cells; size must equal `width * height`. |
344+
| `anchor` | `BufferAnchor` | Keeps `data` alive when it references shared storage. |
345+
346+
`pj_base/builtin/occupancy_grid_update_codec.hpp` serializes and deserializes
347+
this type using the canonical `PJ.OccupancyGridUpdate` protobuf wire format.
348+
318349
## CompressedPointCloud
319350

320351
`CompressedPointCloud` carries a point cloud delivered in a format-specific
@@ -432,8 +463,8 @@ bundles heterogeneous primitives sharing a `frame_id` and timestamp;
432463

433464
Use `SceneEntities` when the value is procedural 3D scene content
434465
expressible as a small set of primitives: arrows, cubes, spheres,
435-
cylinders, line strips/loops/lists, triangles, text labels, or coordinate
436-
axes glyphs.
466+
cylinders, line strips/loops/lists, triangles, text labels, coordinate
467+
axes glyphs, or model (mesh asset) references.
437468

438469
| Field on `SceneEntity` | Type | Notes |
439470
|------------------------|------|-------|
@@ -442,7 +473,12 @@ axes glyphs.
442473
| `id` | `std::string` | Republishing with the same `(topic, id)` replaces the previous entity. |
443474
| `lifetime_ns` | `int64_t` | `0` means persist until replaced; otherwise expire `lifetime_ns` after `timestamp`. |
444475
| `frame_locked` | `bool` | When true, track `frame_id` as it moves; when false, stamp into the fixed frame at publish time. |
445-
| `arrows` / `cubes` / `spheres` / `cylinders` / `lines` / `triangles` / `texts` / `axes` | `std::vector<…Primitive>` | Heterogeneous primitive lists. |
476+
| `arrows` / `cubes` / `spheres` / `cylinders` / `lines` / `triangles` / `texts` / `axes` / `models` | `std::vector<…Primitive>` | Heterogeneous primitive lists. `models` references a mesh asset by `url` or inline `data`. |
477+
478+
The `SceneEntities` batch also carries `deletions` (`std::vector<SceneEntityDeletion>`):
479+
removal commands that let a snapshot-based producer express the removal half of a
480+
stateful stream (e.g. ROS Marker `DELETE` / `DELETEALL`). A deletion is either
481+
`kMatchingId` (remove the entity with the given `id`) or `kAll` (clear the topic).
446482

447483
Each primitive carries its own `Pose`, geometry-specific size or shape
448484
fields, and color (or per-vertex colors, where applicable). See
@@ -487,6 +523,51 @@ Design notes:
487523
`<robot>`) before emission. Generic `std_msgs/String` payloads on unrelated
488524
topics should not surface as RobotDescription.
489525

526+
## CameraInfo
527+
528+
`CameraInfo` carries pinhole camera calibration — intrinsics, distortion,
529+
rectification, and projection — for one camera frame (ROS
530+
`sensor_msgs/CameraInfo`). Consumers use it to draw camera frustums, back-project
531+
depth pixels into 3D, and rectify or overlay onto images.
532+
533+
Like `OccupancyGridUpdate`, it is correlated to its image / depth topic by
534+
topic-name convention (`<ns>/camera_info``<ns>/image_raw`); the object itself
535+
carries no topic linkage. It is an owned value (small matrices and a distortion
536+
vector, no byte blob), so no `BufferAnchor` is needed.
537+
538+
| Field | Type | Notes |
539+
|-------|------|-------|
540+
| `timestamp_ns` | `Timestamp` | Timestamp associated with this calibration. |
541+
| `frame_id` | `std::string` | Camera optical frame. |
542+
| `width` | `uint32_t` | Image width in pixels. |
543+
| `height` | `uint32_t` | Image height in pixels. |
544+
| `distortion_model` | `std::string` | e.g. `plumb_bob`, `rational_polynomial`, `equidistant`; empty when rectified. |
545+
| `D` | `std::vector<double>` | Distortion coefficients; size depends on the model. |
546+
| `K` | `std::array<double, 9>` | 3x3 row-major intrinsics `[fx 0 cx; 0 fy cy; 0 0 1]`. |
547+
| `R` | `std::array<double, 9>` | 3x3 row-major rectification (identity for monocular). |
548+
| `P` | `std::array<double, 12>` | 3x4 row-major projection / camera matrix. |
549+
550+
Sub-window fields (binning, ROI) from `sensor_msgs/CameraInfo` are intentionally
551+
omitted; they are additive later if a consumer needs them.
552+
`pj_base/builtin/camera_info_codec.hpp` serializes and deserializes this type
553+
using the canonical `PJ.CameraInfo` protobuf wire format.
554+
555+
## Log
556+
557+
`Log` is a single textual log message, for a log/console panel. It mirrors the
558+
core of Foxglove's `Log` schema (and `rcl_interfaces/Log` / `rosgraph_msgs/Log`).
559+
560+
| Field on `Log` | Type | Notes |
561+
|----------------|------|-------|
562+
| `timestamp_ns` | `Timestamp` | Time of the log message. |
563+
| `level` | `Log::Level` | `kUnknown`/`kDebug`/`kInfo`/`kWarning`/`kError`/`kFatal` (values match Foxglove). |
564+
| `message` | `std::string` | Log text. |
565+
| `name` | `std::string` | Originating process / node / logger name. |
566+
567+
Foxglove's source-location fields (`file`, `line`) are intentionally omitted.
568+
`pj_base/builtin/log_codec.hpp` serializes and deserializes this type using the
569+
canonical `PJ.Log` protobuf wire format.
570+
490571
## Conversion Examples
491572

492573
| Source type | Canonical builtin type | Conversion intent |
@@ -503,7 +584,8 @@ Design notes:
503584
| Detection or tracking message | `ImageAnnotations` | Convert boxes, points, circles, and labels into pixel-space primitives. |
504585
| ROS `tf2_msgs/TFMessage` | `FrameTransforms` | Convert transform batches into named parent/child frame relationships. |
505586
| ROS `std_msgs/String` on `/robot_description` (or matching name) carrying URDF XML | `RobotDescription` | Validate root element matches `format`, then carry the raw text + format hint. No mesh resolution at parse time. |
506-
| ROS `std_msgs/String` on `/robot_description` (or matching name) carrying URDF XML | `RobotDescription` | Validate root element matches `format`, then carry the raw text + format hint. No mesh resolution at parse time. |
587+
| ROS `sensor_msgs/CameraInfo` | `CameraInfo` | Map K / D / R / P plus dimensions; correlate to the image topic by name. Sub-window (binning / ROI) is dropped. |
588+
| ROS `map_msgs/OccupancyGridUpdate` | `OccupancyGridUpdate` | Forward the cell-space patch (`x`/`y`/`width`/`height` + bytes); the consumer pairs it with the base grid and supplies origin/resolution. |
507589

508590
The builtin type is the boundary object. After conversion, consumers should not
509591
need to know which third-party schema produced it.

pj_base/CMakeLists.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,16 @@
44

55
add_library(pj_base STATIC
66
src/builtin/asset_video_codec.cpp
7+
src/builtin/camera_info_codec.cpp
78
src/builtin/compressed_point_cloud_codec.cpp
89
src/builtin/depth_image_codec.cpp
910
src/builtin/frame_transforms_codec.cpp
1011
src/builtin/image_annotations_codec.cpp
1112
src/builtin/image_codec.cpp
13+
src/builtin/log_codec.cpp
1214
src/builtin/mesh3d_codec.cpp
1315
src/builtin/occupancy_grid_codec.cpp
16+
src/builtin/occupancy_grid_update_codec.cpp
1417
src/builtin/point_cloud_codec.cpp
1518
src/builtin/scene_entities_codec.cpp
1619
src/builtin/video_frame_codec.cpp
@@ -83,6 +86,9 @@ if(PJ_BUILD_TESTS)
8386
tests/point_cloud_codec_test.cpp
8487
tests/compressed_point_cloud_codec_test.cpp
8588
tests/occupancy_grid_codec_test.cpp
89+
tests/occupancy_grid_update_codec_test.cpp
90+
tests/camera_info_codec_test.cpp
91+
tests/log_codec_test.cpp
8692
tests/mesh3d_codec_test.cpp
8793
tests/video_frame_codec_test.cpp
8894
tests/scene_entities_codec_test.cpp

pj_base/include/pj_base/builtin/builtin_object.hpp

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,16 @@
2727
#include <string_view>
2828

2929
#include "pj_base/builtin/asset_video.hpp"
30+
#include "pj_base/builtin/camera_info.hpp"
3031
#include "pj_base/builtin/compressed_point_cloud.hpp"
3132
#include "pj_base/builtin/depth_image.hpp"
3233
#include "pj_base/builtin/frame_transforms.hpp"
3334
#include "pj_base/builtin/image.hpp"
3435
#include "pj_base/builtin/image_annotations.hpp"
36+
#include "pj_base/builtin/log.hpp"
3537
#include "pj_base/builtin/mesh3d.hpp"
3638
#include "pj_base/builtin/occupancy_grid.hpp"
39+
#include "pj_base/builtin/occupancy_grid_update.hpp"
3740
#include "pj_base/builtin/point_cloud.hpp"
3841
#include "pj_base/builtin/robot_description.hpp"
3942
#include "pj_base/builtin/scene_entities.hpp"
@@ -56,6 +59,9 @@ enum class BuiltinObjectType : uint16_t {
5659
kSceneEntities = 11, ///< sdk::SceneEntities — procedural 3D scene primitives.
5760
kAssetVideo = 12, ///< sdk::AssetVideo — file-backed video reference + playback metadata.
5861
kRobotDescription = 13, ///< sdk::RobotDescription — raw URDF/SDF/MJCF text + format hint.
62+
kCameraInfo = 14, ///< sdk::CameraInfo — pinhole camera calibration (K/D/R/P).
63+
kOccupancyGridUpdate = 15, ///< sdk::OccupancyGridUpdate — incremental sub-rectangle patch for an OccupancyGrid.
64+
kLog = 16, ///< sdk::Log — textual log message (level + text + name).
5965
};
6066

6167
/// A-priori classification of a schema. Currently carries only the type;
@@ -94,6 +100,12 @@ struct SchemaClassification {
94100
return "kAssetVideo";
95101
case BuiltinObjectType::kRobotDescription:
96102
return "kRobotDescription";
103+
case BuiltinObjectType::kCameraInfo:
104+
return "kCameraInfo";
105+
case BuiltinObjectType::kOccupancyGridUpdate:
106+
return "kOccupancyGridUpdate";
107+
case BuiltinObjectType::kLog:
108+
return "kLog";
97109
}
98110
return "kNone";
99111
}
@@ -140,6 +152,15 @@ struct SchemaClassification {
140152
if (s == "kRobotDescription") {
141153
return BuiltinObjectType::kRobotDescription;
142154
}
155+
if (s == "kCameraInfo") {
156+
return BuiltinObjectType::kCameraInfo;
157+
}
158+
if (s == "kOccupancyGridUpdate") {
159+
return BuiltinObjectType::kOccupancyGridUpdate;
160+
}
161+
if (s == "kLog") {
162+
return BuiltinObjectType::kLog;
163+
}
143164
return std::nullopt;
144165
}
145166

@@ -189,6 +210,15 @@ using BuiltinObject = std::any;
189210
if (t == typeid(RobotDescription)) {
190211
return BuiltinObjectType::kRobotDescription;
191212
}
213+
if (t == typeid(CameraInfo)) {
214+
return BuiltinObjectType::kCameraInfo;
215+
}
216+
if (t == typeid(OccupancyGridUpdate)) {
217+
return BuiltinObjectType::kOccupancyGridUpdate;
218+
}
219+
if (t == typeid(Log)) {
220+
return BuiltinObjectType::kLog;
221+
}
192222
return BuiltinObjectType::kNone;
193223
}
194224

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/**
2+
* @file camera_info.hpp
3+
* @brief Pinhole camera calibration (intrinsics + distortion) for a camera frame.
4+
*
5+
* CameraInfo is a small owned builtin: it carries calibration matrices and
6+
* distortion coefficients directly, with no byte blob, so no BufferAnchor is
7+
* needed. It is correlated to an image / depth topic by convention — a consumer
8+
* pairs `<ns>/camera_info` with `<ns>/image_raw` — so the canonical object
9+
* itself carries no topic linkage. Sub-window fields (binning, ROI) are
10+
* intentionally omitted; they are additive later if a consumer needs them.
11+
*/
12+
// Copyright 2026 Davide Faconti
13+
// SPDX-License-Identifier: Apache-2.0
14+
15+
#pragma once
16+
17+
#include <array>
18+
#include <cstdint>
19+
#include <string>
20+
#include <vector>
21+
22+
#include "pj_base/types.hpp"
23+
24+
namespace PJ {
25+
namespace sdk {
26+
27+
/// Pinhole camera calibration. Mirrors the calibration payload of
28+
/// sensor_msgs/CameraInfo (K/D/R/P + distortion model), expressed as canonical
29+
/// PJ vocabulary rather than a ROS-specific type. Matrices are row-major.
30+
struct CameraInfo {
31+
Timestamp timestamp_ns = 0;
32+
std::string frame_id; ///< Camera optical frame.
33+
uint32_t width = 0; ///< Image width in pixels.
34+
uint32_t height = 0; ///< Image height in pixels.
35+
std::string distortion_model; ///< e.g. "plumb_bob", "rational_polynomial", "equidistant".
36+
std::vector<double> D; ///< Distortion coefficients; size depends on the model.
37+
std::array<double, 9> K{}; ///< 3x3 intrinsics [fx 0 cx; 0 fy cy; 0 0 1].
38+
std::array<double, 9> R{}; ///< 3x3 rectification (identity for monocular).
39+
std::array<double, 12> P{}; ///< 3x4 projection / camera matrix.
40+
41+
bool operator==(const CameraInfo&) const = default;
42+
};
43+
44+
} // namespace sdk
45+
} // namespace PJ
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#pragma once
2+
// Copyright 2026 Davide Faconti
3+
// SPDX-License-Identifier: Apache-2.0
4+
5+
#include <cstddef>
6+
#include <cstdint>
7+
#include <string_view>
8+
#include <vector>
9+
10+
#include "pj_base/builtin/camera_info.hpp"
11+
#include "pj_base/expected.hpp"
12+
13+
namespace PJ {
14+
15+
inline constexpr std::string_view kSchemaCameraInfo = "PJ.CameraInfo";
16+
17+
/// Serializes sdk::CameraInfo to canonical PJ.CameraInfo wire bytes
18+
/// (see pj_base/proto/pj/CameraInfo.proto).
19+
[[nodiscard]] std::vector<uint8_t> serializeCameraInfo(const sdk::CameraInfo& info);
20+
21+
/// Decodes canonical PJ.CameraInfo wire bytes into an owned sdk::CameraInfo.
22+
[[nodiscard]] Expected<sdk::CameraInfo> deserializeCameraInfo(const uint8_t* data, size_t size);
23+
24+
} // namespace PJ
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/**
2+
* @file log.hpp
3+
* @brief A single textual log message (level + text + originating name).
4+
*
5+
* Log is a small owned builtin — no byte blob, no BufferAnchor. It mirrors the
6+
* core of Foxglove's Log schema (and rcl_interfaces/Log / rosgraph_msgs/Log),
7+
* expressed as canonical PJ vocabulary. The Foxglove source-location fields
8+
* (`file`, `line`) are intentionally omitted.
9+
*/
10+
// Copyright 2026 Davide Faconti
11+
// SPDX-License-Identifier: Apache-2.0
12+
13+
#pragma once
14+
15+
#include <cstdint>
16+
#include <string>
17+
18+
#include "pj_base/types.hpp"
19+
20+
namespace PJ {
21+
namespace sdk {
22+
23+
/// A textual log message at a point in time.
24+
struct Log {
25+
/// Severity level. Values match Foxglove's Log.Level.
26+
enum class Level : uint8_t {
27+
kUnknown = 0,
28+
kDebug = 1,
29+
kInfo = 2,
30+
kWarning = 3,
31+
kError = 4,
32+
kFatal = 5,
33+
};
34+
35+
Timestamp timestamp_ns = 0;
36+
Level level = Level::kUnknown;
37+
std::string message; ///< Log text.
38+
std::string name; ///< Originating process / node / logger name.
39+
40+
bool operator==(const Log&) const = default;
41+
};
42+
43+
} // namespace sdk
44+
} // namespace PJ
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#pragma once
2+
// Copyright 2026 Davide Faconti
3+
// SPDX-License-Identifier: Apache-2.0
4+
5+
#include <cstddef>
6+
#include <cstdint>
7+
#include <string_view>
8+
#include <vector>
9+
10+
#include "pj_base/builtin/log.hpp"
11+
#include "pj_base/expected.hpp"
12+
13+
namespace PJ {
14+
15+
inline constexpr std::string_view kSchemaLog = "PJ.Log";
16+
17+
/// Serializes sdk::Log to canonical PJ.Log wire bytes
18+
/// (see pj_base/proto/pj/Log.proto).
19+
[[nodiscard]] std::vector<uint8_t> serializeLog(const sdk::Log& log);
20+
21+
/// Decodes canonical PJ.Log wire bytes into an owned sdk::Log.
22+
[[nodiscard]] Expected<sdk::Log> deserializeLog(const uint8_t* data, size_t size);
23+
24+
} // namespace PJ

0 commit comments

Comments
 (0)