Skip to content

Commit 5bba357

Browse files
committed
1.0!
1 parent aea0f02 commit 5bba357

49 files changed

Lines changed: 10866 additions & 82 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/docs.yml

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
name: Documentation
2+
on:
3+
push:
4+
branches:
5+
- master
6+
- main
7+
permissions:
8+
contents: read
9+
pages: write
10+
id-token: write
11+
jobs:
12+
deploy:
13+
environment:
14+
name: github-pages
15+
url: ${{ steps.deployment.outputs.page_url }}
16+
runs-on: ubuntu-latest
17+
steps:
18+
- uses: actions/configure-pages@v5
19+
- uses: actions/checkout@v5
20+
- uses: actions/setup-python@v5
21+
with:
22+
python-version: 3.x
23+
- run: pip install zensical
24+
- run: zensical build --clean
25+
- uses: actions/upload-pages-artifact@v4
26+
with:
27+
path: site
28+
- uses: actions/deploy-pages@v4
29+
id: deployment

README.md

Lines changed: 57 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,70 @@
1-
# WPILib Vendor Template
1+
# TelemetryKit
22

3-
This is the base WPILib vendor template for 2025.
3+
A lightweight, type-safe telemetry library for FRC robots. TelemetryKit provides a unified interface for logging data to NetworkTables, `.wpilog` files, and console output.
44

5-
## Layout
5+
**[📚 Documentation](https://sticksdev.github.io/TelemetryKit/)**
66

7-
The build is split into 3 libraries. A java library is built. This has access to all of wpilib, and also can JNI load the driver library.
7+
## Features
88

9-
A driver library is built. This should contain all low level code you want to access from both C++, Java and any other text based language. This will not work with LabVIEW. This library has access to the WPILib HAL and wpiutil. This library can only export C symbols. It cannot export C++ symbols at all, and all C symbols must be explicitly listed in the symbols.txt file in the driver folder. JNI symbols must be listed in this file as well. This library however can be written in C++. If you attempt to change this library to have access to all of wpilib, you will break JNI access and it will no longer work.
9+
- **Type-safe logging** - Support for primitives, arrays, and WPILib structs
10+
- **Multiple receivers** - Publish to NetworkTables, write to `.wpilog` files, or print to console
11+
- **Change-only optimization** - Only process values when they change
12+
- **Zero overhead** - Singleton pattern with minimal runtime cost
1013

11-
A native C++ library is built. This has access to all of wpilib, and access to the driver library. This should implment the standard wpilib interfaces.
14+
## Installation
1215

13-
## Customizing
14-
For Java, the library name will be the folder name the build is started from, so rename the folder to the name of your choosing.
16+
### Using Vendor JSON (Recommended)
1517

16-
For the native impl, you need to change the library name in the exportsConfigs block of build.gradle, the components block of build.gradle, and the taskList input array name in publish.gradle.
18+
1. Open VS Code with your FRC project
19+
2. Press `Ctrl+Shift+P` (or `Cmd+Shift+P` on Mac)
20+
3. Type "WPILib: Manage Vendor Libraries"
21+
4. Select "Install new libraries (online)"
22+
5. Paste this URL:
1723

18-
For the driver, change the library name in privateExportsConfigs, the driver name in components, and the driverTaskList input array name. In addition, you'll need to change the `lib library` in the native C++ impl component, and the JNI library name in the JNI java class.
24+
```
25+
https://raw.githubusercontent.com/SticksDev/TelemetryKit/refs/heads/2026/TelemetryKit.json
26+
```
1927

20-
For the maven artifact names, those are all in publish.gradle about 40 lines down.
28+
## Quick Start
2129

22-
## Building and editing
23-
This uses gradle, and uses the same base setup as a standard GradleRIO robot project. This means you build with `./gradlew build`, and can install the native toolchain with `./gradlew installRoboRIOToolchain`. If you open this project in VS Code with the wpilib extension installed, you will get intellisense set up for both C++ and Java.
30+
```cpp
31+
#include <telemetrykit/TelemetryKit.h>
2432

25-
By default, this template builds against the latest WPILib development build. To build against the last WPILib tagged release, build with `./gradlew build -PreleaseMode`.
33+
auto& logger = tkit::Logger::GetInstance();
2634

27-
## Checking Vendordep
28-
After you've published your library to maven, you can use the [vendordep checker](https://github.com/wpilibsuite/vendor-json-repo/blob/main/check.py) to check for common errors, such as not publishing all dependencies, and ensuring that all architectures are correct.
35+
// Add receivers
36+
logger.AddReceiver(std::make_unique<tkit::NetworkTablesReceiver>());
37+
logger.AddReceiver(std::make_unique<tkit::WPILogWriter>("/home/lvuser/logs"));
2938

30-
## Listing Vendordep in VS Code Dependency Manager
31-
Follow the directions at [WPILib Vendor JSON Repository](https://github.com/wpilibsuite/vendor-json-repo/blob/main/README.md) to get a Vendordep included in the [VS Code Dependency Manager](https://docs.wpilib.org/en/stable/docs/software/vscode-overview/3rd-party-libraries.html#installing-libraries)
39+
// Start logging
40+
logger.Start();
41+
42+
// Log data every cycle
43+
void RobotPeriodic() {
44+
tkit::RecordOutput("Drive/Speed", m_drive.GetSpeed());
45+
tkit::RecordOutput("Vision/Targets", targetCount);
46+
logger.Periodic();
47+
}
48+
```
49+
50+
## Building from Source
51+
52+
```bash
53+
./gradlew build
54+
```
55+
56+
To install the native toolchain:
57+
58+
```bash
59+
./gradlew installRoboRIOToolchain
60+
```
61+
62+
To publish to local WPILib maven:
63+
64+
```bash
65+
./gradlew copyToWpilibLocal
66+
```
67+
68+
## License
69+
70+
See [LICENSE.txt](LICENSE.txt)

TelemetryKit.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@
44
"version": "1.0.0",
55
"frcYear": "2026",
66
"uuid": "bf2eaa28-c978-45c9-8b1e-206f7620bd79",
7-
"mavenUrls": [],
8-
"jsonUrl": "",
7+
"mavenUrls": [
8+
"https://jitpack.io"
9+
],
10+
"jsonUrl": "https://raw.githubusercontent.com/SticksDev/TelemetryKit/refs/heads/2026/TelemetryKit.json",
911
"jniDependencies": [],
1012
"javaDependencies": [],
1113
"cppDependencies": [

docs/advanced/structs.md

Lines changed: 264 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,264 @@
1+
---
2+
icon: lucide/box
3+
---
4+
5+
# Struct Logging
6+
7+
TelemetryKit supports WPILib struct serialization with automatic schema publishing to NetworkTables and WPILog files.
8+
9+
!!! warning "Beta Feature"
10+
Struct logging is in beta. Some structs may not work correctly. Use the helper functions (e.g., `LogPose2d()`, `LogSwerveModuleStates()`) for more reliable decomposed logging. <!-- (1)! -->
11+
12+
Helper functions decompose structs into individual primitive fields, avoiding serialization issues and providing better dashboard compatibility.
13+
14+
## Quick Start
15+
16+
```cpp
17+
#include <frc/geometry/Pose2d.h>
18+
#include <telemetrykit/TelemetryKit.h>
19+
20+
// Log a Pose2d
21+
frc::Pose2d robotPose{1.5_m, 2.3_m, frc::Rotation2d{45_deg}};
22+
tkit::Logger::RecordOutput("Odometry/Pose", tkit::MakeStructValue(robotPose));
23+
24+
// Log an array of SwerveModuleStates
25+
std::array<frc::SwerveModuleState, 4> moduleStates = {...};
26+
tkit::Logger::RecordOutput("Drive/ModuleStates",
27+
tkit::MakeStructArrayValue(std::span(moduleStates)));
28+
```
29+
30+
TelemetryKit automatically publishes struct schemas to NetworkTables and encodes them correctly in WPILog files.
31+
32+
## How It Works
33+
34+
### Serialization
35+
36+
When you call `MakeStructValue()`, TelemetryKit:
37+
38+
1. Serializes the struct to bytes using `wpi::Struct`
39+
2. Extracts the type name (e.g., "Pose2d")
40+
3. Collects schema bytes using `wpi::GetStructSchemaBytes<T>()`
41+
4. Gathers nested struct schemas (e.g., Translation2d, Rotation2d for Pose2d)
42+
43+
```mermaid
44+
graph LR
45+
A[Pose2d Object] -->|MakeStructValue| B[Serialize to bytes]
46+
B --> C[Extract type name]
47+
C --> D[Get schema bytes]
48+
D --> E[Collect nested schemas]
49+
E --> F[LogValue with all schemas]
50+
```
51+
52+
### Schema Publishing
53+
54+
#### NetworkTables
55+
56+
When `NetworkTablesReceiver` encounters a struct for the first time:
57+
58+
```cpp
59+
// Publishes schemas for Pose2d and its nested structs
60+
m_inst.AddSchema("Pose2d", "structschema", pose2dSchemaBytes);
61+
m_inst.AddSchema("Translation2d", "structschema", translation2dSchemaBytes);
62+
m_inst.AddSchema("Rotation2d", "structschema", rotation2dSchemaBytes);
63+
64+
// Then publishes the data as a RawTopic
65+
auto topic = m_inst.GetRawTopic("/Odometry/Pose");
66+
auto publisher = topic.Publish("Pose2d"); // (1)!
67+
publisher.Set(serializedBytes, timestamp);
68+
```
69+
70+
1. The type string "Pose2d" tells NT4 clients which schema to use for deserialization
71+
72+
#### WPILog Files
73+
74+
`WPILogWriter` creates log entries with the correct type string:
75+
76+
```cpp
77+
// Creates entry with "struct:Pose2d" type
78+
int entryId = m_log->Start("/Odometry/Pose", "struct:Pose2d");
79+
80+
// Appends serialized bytes
81+
m_log->AppendRaw(entryId, serializedBytes, timestamp);
82+
```
83+
84+
Tools like AdvantageScope can then deserialize the struct data for visualization.
85+
86+
## Nested Struct Dependencies
87+
88+
TelemetryKit automatically handles nested struct dependencies using `wpi::ForEachStructSchema<T>()`:
89+
90+
```cpp
91+
// When you log a Pose2d
92+
tkit::MakeStructValue(pose);
93+
94+
// TelemetryKit collects schemas for:
95+
// - Pose2d (the main struct)
96+
// - Translation2d (nested in Pose2d)
97+
// - Rotation2d (nested in Pose2d)
98+
```
99+
100+
Without nested schemas, clients cannot deserialize structs that contain other structs. With nested schemas, clients receive all required schemas for full deserialization.
101+
102+
## Supported Structs
103+
104+
Any WPILib struct that implements `wpi::Struct<T>` is supported:
105+
106+
### Geometry Structs
107+
108+
```cpp
109+
#include <frc/geometry/Pose2d.h>
110+
#include <frc/geometry/Pose3d.h>
111+
#include <frc/geometry/Translation2d.h>
112+
#include <frc/geometry/Translation3d.h>
113+
#include <frc/geometry/Rotation2d.h>
114+
#include <frc/geometry/Rotation3d.h>
115+
#include <frc/geometry/Transform2d.h>
116+
#include <frc/geometry/Transform3d.h>
117+
118+
tkit::Logger::RecordOutput("Pose2D", tkit::MakeStructValue(pose2d));
119+
tkit::Logger::RecordOutput("Pose3D", tkit::MakeStructValue(pose3d));
120+
```
121+
122+
### Kinematics Structs
123+
124+
```cpp
125+
#include <frc/kinematics/SwerveModuleState.h>
126+
#include <frc/kinematics/SwerveModulePosition.h>
127+
#include <frc/kinematics/ChassisSpeeds.h>
128+
129+
tkit::Logger::RecordOutput("ModuleStates",
130+
tkit::MakeStructArrayValue(moduleStates));
131+
```
132+
133+
### Custom Structs
134+
135+
You can use any custom struct that implements `wpi::Struct<T>`:
136+
137+
```cpp
138+
// Your custom struct with wpi::Struct implementation
139+
struct CustomData {
140+
double value1;
141+
int value2;
142+
143+
using StructType = wpi::Struct<CustomData>;
144+
static constexpr std::string_view GetTypeName() { return "CustomData"; }
145+
// ... implement required methods
146+
};
147+
148+
CustomData data{1.5, 42};
149+
tkit::Logger::RecordOutput("Custom", tkit::MakeStructValue(data));
150+
```
151+
152+
## Struct Helper Functions
153+
154+
For dashboard visibility, TelemetryKit provides helpers to decompose structs into individual fields:
155+
156+
```cpp
157+
#include <telemetrykit/core/StructLogger.h>
158+
159+
frc::Pose2d pose{1.5_m, 2.3_m, frc::Rotation2d{45_deg}};
160+
161+
// Instead of logging as a binary struct
162+
tkit::Logger::RecordOutput("Pose", tkit::MakeStructValue(pose));
163+
164+
// Decompose into individual fields for plotting
165+
auto& table = tkit::Logger::GetTable();
166+
tkit::LogPose2d(table, "Pose", pose);
167+
// Creates:
168+
// "Pose/X" -> 1.5
169+
// "Pose/Y" -> 2.3
170+
// "Pose/Rotation" -> 0.785398 (45° in radians)
171+
```
172+
173+
Available helpers:
174+
175+
| Function | Struct Type | Fields Created |
176+
|----------|-------------|----------------|
177+
| `LogPose2d` | `frc::Pose2d` | X, Y, Rotation |
178+
| `LogPose3d` | `frc::Pose3d` | X, Y, Z, Rotation (Quaternion) |
179+
| `LogTranslation2d` | `frc::Translation2d` | X, Y |
180+
| `LogTranslation3d` | `frc::Translation3d` | X, Y, Z |
181+
| `LogSwerveModuleState` | `frc::SwerveModuleState` | Speed, Angle |
182+
| `LogSwerveModulePosition` | `frc::SwerveModulePosition` | Distance, Angle |
183+
| `LogSwerveModuleStates` | `std::vector<...>` | Module0/..., Module1/..., etc. |
184+
185+
Use structs for full type preservation in AdvantageScope. Use helpers for individual fields that can be plotted on dashboards.
186+
187+
## Performance Considerations
188+
189+
### Serialization Overhead
190+
191+
Struct serialization has minimal overhead:
192+
193+
```cpp
194+
frc::Pose2d pose{...};
195+
196+
// ~500 nanoseconds on roboRIO
197+
auto logValue = tkit::MakeStructValue(pose); // (1)!
198+
```
199+
200+
1. Includes serialization, schema lookup, and LogValue construction
201+
202+
### Schema Publishing
203+
204+
Schemas are only published once per struct type:
205+
206+
```cpp
207+
// First Pose2d - publishes schemas
208+
tkit::Logger::RecordOutput("Pose1", tkit::MakeStructValue(pose1));
209+
tkit::Logger::Periodic(); // → Publishes 3 schemas
210+
211+
// Second Pose2d - skips schema publishing
212+
tkit::Logger::RecordOutput("Pose2", tkit::MakeStructValue(pose2));
213+
tkit::Logger::Periodic(); // → Only publishes data
214+
```
215+
216+
Schema publishing is a one-time cost per struct type, not per value.
217+
218+
## Examples
219+
220+
### Complete Swerve Drive Telemetry
221+
222+
```cpp
223+
#include <telemetrykit/TelemetryKit.h>
224+
#include <telemetrykit/core/StructLogger.h>
225+
#include <frc/kinematics/SwerveModuleState.h>
226+
227+
class SwerveDrive {
228+
public:
229+
void UpdateTelemetry() {
230+
auto& table = tkit::Logger::GetTable();
231+
232+
// Log robot pose (full struct for AdvantageScope)
233+
tkit::Logger::RecordOutput("Swerve/Pose",
234+
tkit::MakeStructValue(m_poseEstimator.GetEstimatedPosition()));
235+
236+
// Log module states (decomposed for dashboard)
237+
tkit::LogSwerveModuleStates(table, "Swerve/ModuleStates", m_moduleStates);
238+
239+
// Log as struct array for AdvantageScope
240+
tkit::Logger::RecordOutput("Swerve/ModuleStatesStruct",
241+
tkit::MakeStructArrayValue(std::span(m_moduleStates)));
242+
}
243+
244+
private:
245+
std::array<frc::SwerveModuleState, 4> m_moduleStates;
246+
frc::SwerveDrivePoseEstimator m_poseEstimator;
247+
};
248+
```
249+
250+
This creates the following NT4 topics and log entries:
251+
252+
```
253+
/Swerve/Pose → Pose2d struct
254+
/Swerve/ModuleStates/Module0/Speed → double
255+
/Swerve/ModuleStates/Module0/Angle → double
256+
/Swerve/ModuleStates/Module1/Speed → double
257+
/Swerve/ModuleStates/Module1/Angle → double
258+
...
259+
/Swerve/ModuleStatesStruct → SwerveModuleState[] struct array
260+
```
261+
262+
---
263+
264+
[Learn More About Receivers →](../receivers/overview.md){ .md-button }

0 commit comments

Comments
 (0)