Compare commits

...

109 Commits

Author SHA1 Message Date
amsam0
7ca35e5678 [wpimath] Add limit setters to SlewRateLimiter (#8581)
This is just #7793 with requested changes applied.
2026-02-27 12:53:18 -08:00
Kevin Cooney
613bd88548 [wpilib] Make Preferences Listener not depend on mutable fields (#8607)
The Listener installed by Preferences was referencing m_typePublisher which could be modified by a future call to setNetworkTableInstance(). Instead, reference a local.

Also made Topic.m_handle final, to guarantee that Topic.equals() is thread-safe, and still work after the publisher has been closed.
2026-02-27 12:42:40 -08:00
Stephen Just
e311722637 [ntcore] Handle interrupted save in NetworkServer (#8630)
In NetworkServer::SavePersistent, if the save is interrupted (by robot
power loss, etc), the networktables.json file may be left in an
unhandled state where the file consumed by
NetworkServer::LoadPersistent is not found, but the backup file exists.
In this case, we should attempt to recover the backup file to avoid
losing all persistent data.
2026-02-27 12:39:05 -08:00
sciencewhiz
ae43b8b6dd [cmd] Fix WaitUntilCommand for match time counting down (#8632)
Fixes #8631
Documents that it will return immediately if FMS isn't attached or DS
isn't in practice mode.
Related to change in DS match time behavior that was documented in #8606
2026-02-23 17:11:59 -08:00
sciencewhiz
5ae8ee06dd [build] use local opencv docs element-list (#8633)
Fixes opencv cloudflare blocking gradle javadoc builds
2026-02-23 16:32:28 -08:00
DeltaDizzy
d9eba4bb22 [wpilib] Document zero angle in SingleJointedArmSim (NFC) (#7756)
Fixes #7752

---------

Co-authored-by: sciencewhiz <sciencewhiz@users.noreply.github.com>
Co-authored-by: Tyler Veness <calcmogul@gmail.com>
2026-02-21 14:36:36 -08:00
Jordan Powers
9cd933fa14 [wpiunits] Fix incorrect magnitudes in some MutableMeasure mutations (#8620)
This PR fixes the magnitude units in `MutableMeasure#mut_acc` and
`MutableMeasure#mut_plus`.

Previously, both `mut_acc` and `mut_plus` were setting the base
magnitude using the unit-ed magnitude value. While this would work fine
for base units where `magnitude == baseUnitMagnitude`, this was creating
issues with derived units.

This PR also adds missing tests for the `MutableMeasure` class.
2026-02-20 15:29:53 -08:00
sciencewhiz
77dfad97c6 [ci] Use wpilib docker-run-action fork (#8615) 2026-02-14 11:32:57 -08:00
sciencewhiz
7ac0612397 [hal,wpilib] Update MatchTime docs for teleop and auto mode (NFC) (#8606)
Update Timer and JNI MatchTime functions to latest docs
2026-02-09 17:14:42 -08:00
Tyler Veness
2c5529d714 [wpimath] Speed up pose estimator correction computation (#8574)
In C++, we use a diagonal matrix to avoid an expensive matrix
multiplication. EJML doesn't have a diagonal matrix type, so in Java, we
use a double array and implement the multiplication manually.
2026-02-06 21:47:49 -08:00
Gavin P
7cd3790c7c [wpiunits] Make RPM an alias of RotationsPerMinute (#8595)
Currently the only name for this unit is `RPM`. This caused a bit of
confusion for a couple of my team members when we failed to find an RPM
unit, assuming it would be named `RotationsPerMinute` as is the standard
for almost all other units, such as `RotationsPerSecond`.

No corresponding changes have been made to wpilibc as it seems to
already work this way, with `rpm` being the abbreviation for
`revolutions_per_minute`.
2026-02-06 21:47:08 -08:00
NotTacos
664484306c [glass] Change match times for rebuilt (#8575)
The timer has changed for rebuilt's auto and teleop.
2026-02-06 21:39:12 -08:00
jpokornyiii
0a37317467 [examples] Adding XRP java and cpp examples for Timed Robot (#8599)
Adding an example (one C++ and one Java) for using TimedRobot with the
XRP.
2026-02-06 21:36:35 -08:00
FirstFox3
6b225bb1f1 [wpilib] Fix typo in TimedRobot.getLoopStartTime() docs (#8590)
Fixed a typo in the description of the getLoopStartTime function in both
C++ and Java TimedRobot class.

---------

Co-authored-by: Tyler Veness <calcmogul@gmail.com>
2026-02-05 18:57:49 -07:00
Vasista Vovveti
3d92547d62 [cscore] Fix format specifier on Mac (#8602) 2026-02-05 18:56:52 -07:00
crueter
c89401250f [hal, wpilib] Usage Reporting: QFRCDashboard -> QDash (#8571) 2026-01-15 19:55:55 -07:00
Tyler Veness
8be7720a68 [sysid] Fix crash on partially empty raw data (#8572)
Fixes #8570.
2026-01-15 18:57:38 -07:00
Michael Lesirge
21b5389bbe [wpimath,cmd] Add multi tap boolean stream filter and multi tap trigger modifier (double tap detector) (#8307)
Add a simple tap counting filter for boolean streams. 

The filter activates when the input has risen (transitioned from false
to true, like when a button is tapped) the required number of times
within the time window after the first rising edge. Once activated, the
output remains true as long as the input is true. The tap count resets
when the time window expires or when the input goes false after
activation.

Example usage:
```java
    xbox.a()
      .multiPress(2, 0.2) // Detect a double tap within 0.2 seconds
      .onTrue(Commands.print("Double tapped A button"));
      
     xbox.y()
      .multiPress(2, 0.5) // Detect a double tap within 0.5 seconds
      .whileTrue(Commands.print("Y held after tap").repeatedly());
```

This is not a noise reduction and/or input smoothing filter, but it is
similar in usage to debounce, so I believe it could be considered a
filter, but am open to a better location.

I believe this would be a useful addition, as double/triple tapping a
button is a common control option in games, yet is not often utilized by
newer FRC teams. I believe adding it to WPILib in a standard way will
allow more teams to make the most out of their controls.
2026-01-14 20:22:07 -08:00
Joseph Eng
9e1258440b [wpimath] Fix Rotation3d interpolation and document extrinsic vs intrinsic (#8544)
Documents the extrinsic vs intrinsic semantics of `plus()` and
`minus()`. (`rotateBy()` was documented in [a previous
PR](https://github.com/wpilibsuite/allwpilib/pull/5508))
Fixes usage of `plus()` and `minus()` in `Rotation3d.interpolate()`.
(Fixes #8523)
Fixes incorrect usages of `plus()`, `minus()`, and `rotateBy()`
throughout `Odometry3d`.
Adds explanatory comments for some `plus()`, `minus()`, and `rotateBy()`
operations.
Fixes `TimeInterpolatableBuffer` not using twists for `Pose3d` (this was
just because I happened to notice it, it isn't really related to the PR)

To check all of our usages of `plus()`, `minus()`, and `rotateBy()`, I
marked them as deprecated, checked compile errors from `./gradlew
compileJava`, and then undeprecated them. You can see all of the spots
that showed up (at least on the Java side) by viewing the diff for
241109c.

I wanted to present this alternative to #8526 because the change has its
own quirks, there's little time before kickoff, and there would be no
code-side warning to teams (and mentors) already used to the current
behavior.
2026-01-14 20:16:24 -08:00
Dave Oleksy
812a1b8e1a [hal] Add 2026 REV products for usage reporting (#8567)
Adding REV Easy Swerve and MAXSpline Encoder to usage reporting.

Co-authored-by: Dave Oleksy <dave.oleksy@revrobotics.com>
2026-01-14 20:14:20 -08:00
Kevin-OConnor
18249badc0 Add 2026 game specifics (#8558)
Co-authored-by: Peter Johnson <johnson.peter@gmail.com>
Co-authored-by: Gold856 <117957790+Gold856@users.noreply.github.com>
2026-01-12 19:07:51 -08:00
sciencewhiz
b82d204525 Update vendordep frcYear to 2026 (#8552) 2026-01-06 16:29:45 -08:00
PJ Reiniger
ccb3266753 [upstream_utils] Remove patch that results in building with NDEBUG causing ODR issues (#8540) 2026-01-03 13:31:26 -08:00
sciencewhiz
71a788e20b [docs] Update references to 2026 (#8534) 2026-01-02 08:46:22 -08:00
Charlotte Wilson
f395954d3c [ci] Update Android NDK version to R27D in CMake workflow (#8525)
https://github.com/android/ndk/wiki#release-schedule
2025-12-31 09:08:33 -08:00
Bryce Roethel
ab3af00d07 [wpimath] TrapezoidProfile.State implement StructSerializable (#8499)
Seems like this was missed in #8163
2025-12-21 10:08:32 -06:00
Tyler Veness
1b2f051b4b [docs] Replace instance of PWMSpeedController (#8478) 2025-12-14 08:06:02 -08:00
Benjamin Hall
3dc334c1ee [wpimath] Fix ResetTranslation and ResetRotation in PoseEstimator and PoseEstimator3d causing the robot to teleport (#8285)
Fixes https://github.com/wpilibsuite/allwpilib/issues/8284.

If we have vision updates at the time of the `Reset*` call, we can
correct the translation/rotation of the new odometry pose by adding a
new vision update where:
- `ResetTranslation`: the translation is hard-coded to the new
translation, and the rotation components are set to those of the latest
vision update (prior to clearing the map).
- `ResetRotation`: the rotation is hard-coded to the new rotation, and
the translation components are set to those of the latest vision update
(prior to clearing the map).
2025-12-13 15:53:53 -08:00
Peter Johnson
baa6379267 [wpimath] Add usage reporting for state-space classes (#8453)
- LinearQuadraticRegulator
- Kalman filters
- Pose estimators
- LinearSystemLoop

Fixes #2925.
2025-12-06 09:17:02 -08:00
Peter Johnson
0d1dd84e86 [wpilib] LEDPattern: Add usage reporting (#8452) 2025-12-06 09:16:45 -08:00
Levi
a61866912b [hal] Rename Lumen to Lumyn in usage reporting (#8455) 2025-12-05 19:13:39 -08:00
Peter Johnson
57c40a3dfc [hal] Add more usage reporting constants (#8451)
Fixes #7234 
Fixes #6919
Supports #2925
Supersedes #8212 
Supersedes #7708
2025-12-05 15:20:49 -08:00
Peter Johnson
6f86f533e5 [wpiutil] MemoryBuffer: Fix zero extending size_t warning on Win32 (#8450) 2025-12-05 10:55:28 -08:00
Ryan Blue
ded6790bcd [cmake] Only add wpilibj to generated config if Java is enabled (#8434)
Fixes #8422
2025-11-29 20:44:02 -08:00
Keagan Kautzer
769ce5e9fa [wpiunits] Rename AngularMomentumUnit.mult to per (#8409)
Fixes #8408
2025-11-23 14:40:30 -08:00
Ryan Blue
f6b4ad575b [ci] Pin docker-run-action and remove rm command (#8415)
The fix was made but has not been tagged.
2025-11-22 07:55:44 -08:00
Gold856
7cd0ef5bd9 [docs] Revert "Update readme to say Xcode is required" (#8397)
This reverts commit 49b4b064cf  (#7892).
2025-11-18 17:14:01 -08:00
Warren Reynolds
bd7a88a6d0 [examples] Fix order of Swerve Modules in Odometry Update (#8396)
The order of the Swerve Modules in the m_odometry.Update call needs to
match the order they are defined in the creation of the kDriveKinematics
object.
2025-11-18 17:13:27 -08:00
Benjamin Hall
95cb38e6df [wpimath] Fix ElevatorSim::GetCurrentDraw() in C++ (#8370)
The Kv calculation in C++ was missing a negative sign compared to the Java implementation.
2025-11-13 11:48:43 -07:00
Joseph Eng
b8d6bc2eb1 [wpimath] Scale transforms instead of twists in PoseEstimator (#8333)
The spiraling issue occurs when the vision rotation standard deviation is very high relative to the odometry rotation standard deviation and the vision measurements have a large rotation error. (Scaling the rotation component of a twist without scaling the translation component causes the direction of overall translation to change, leading to spiraling around (either towards or away) the vision measurement instead of moving towards it.) Using a transform instead of a twist avoids this issue.

In general, scaling twist components is more mathematically correct than scaling transform components. However, although twists are correct for modeling uncertainty in an odometry-only pose estimate, they are not correct for the difference between the odometry-only pose estimate and a vision measurement. Since neither twists nor transforms are completely correct (and the pose estimator as a whole is not mathematically correct), but using transforms can guarantee that the pose estimate approaches the vision measurement (instead of potentially spiraling away), they are the least bad option.
2025-11-07 18:07:43 -08:00
Peter Johnson
688535298b [cscore] Add braces to match styleguide (NFC) (#8339) 2025-11-07 18:07:05 -08:00
Thad House
02252b58d7 [build] Update to 2026 Beta 1 (#8337) 2025-11-07 18:06:39 -08:00
Joseph Eng
e207ca4880 [build] Clean up spotbugs excludes (#8332) 2025-11-07 10:07:56 -08:00
Tyler Veness
f4db88da9a [build] Document how to mirror new Doxygen versions (#8327) 2025-11-01 14:45:53 -07:00
Jade
e45aadc851 [sysid] Remove Phoenix5 CANcoder preset (#8316)
Signed-off-by: Jade Turner <spacey-sooty@proton.me>
2025-11-01 09:19:52 -07:00
Dalton Smith
fea6883d98 [wpimath] DCMotor: Add X44 and Minion (#8319) 2025-11-01 09:19:36 -07:00
Tyler Veness
b7fe5ef833 [build] Fix up grammar in docs/build.gradle (#8317) 2025-11-01 09:18:50 -07:00
Tyler Veness
cd237e57d4 [ci] Upgrade to macOS 15 runner image (#8321)
This fixes a compiler bug (rejecting out-of-line definitions of constrained members) newer versions of Sleipnir were encountering.
2025-11-01 09:17:09 -07:00
Murat65536
8b99ad82c3 [wpilib] Add a few unit overloads (#8231)
Co-authored-by: Sam Carlberg <sam@slfc.dev>
Co-authored-by: Joseph Eng <91924258+KangarooKoala@users.noreply.github.com>
Co-authored-by: Tyler Veness <calcmogul@gmail.com>
2025-10-28 20:18:55 -07:00
Thad House
58ba536351 [wpilib] Remove Jaguar (and other) motor controllers (#8298)
https://community.firstinspires.org/2025-robot-rules-preview-for-2026
2025-10-28 20:18:02 -07:00
Tyler Veness
4aef52a117 [ci] Upgrade to wpiformat 2025.36 (#8308)
clang-format 21 made some formatting changes. Since wpiformat's stdlib
task was removed, I removed NOLINT comments for it and removed some
std:: prefixes it added to comments.
2025-10-28 20:17:04 -07:00
Jason Daming
a6a4912a80 [snippets] Add ProfiledPIDController with feedforward snippets (#8280)
Adds snippets demonstrating ProfiledPIDController usage with
SimpleMotorFeedforward using the two-parameter calculate() method
(currentVelocity, nextVelocity).

These snippets will be used in frc-docs to document the recommended
feedforward pattern with ProfiledPIDController.

Co-authored-by: sciencewhiz <sciencewhiz@users.noreply.github.com>
2025-10-27 20:49:16 -06:00
Ryan Blue
873e960e93 [ci] Update tools workflow for 2026 (#8301) 2025-10-25 17:23:12 -07:00
Gold856
4f133c6aa1 [build][ci] Update vcpkg baseline (#8300) 2025-10-25 10:28:39 -07:00
Edan Thomton
35dd61cde5 [build] Fix Eclipse annotation generation in wpilibjExamples (#8295) 2025-10-23 22:29:32 -07:00
Michael Lesirge
9e85f3cf55 [wpimath] Rename 1D copySignPow to match 2D copyDirectionPow (#8286) 2025-10-11 09:24:10 -07:00
Michael Lesirge
2b43541b94 [wpimath] MathUtil: Add 2D variants of applyDeadband and copySignPow (#8057) 2025-10-10 13:44:12 -07:00
arbessette
4c4996e638 [docs] Remove Private Message language (#8202)
Removing private written message for safety of all users and contributors.
2025-10-10 12:43:02 -07:00
Sam Carlberg
cfbd7a5af2 [build] Fix doxygen builds on apple CPUs (#8282)
Caused by the doxygen gradle plugin attempting to download 1.10.0 (presumably its default version) from artifactory because the 1.12.0 config is only applied on x86_64 platforms. Just fixing that isn't enough, however; on mac, the plugin would fail to extract the dmg. We need to fall back to a global installation on the PATH for the plugin to find, preferentially using that instead of a failed attempt to download and extract the dmg.
2025-10-10 12:42:02 -07:00
Tyler Veness
f5990e8b40 [upstream_utils] Fix Eigen tag (#8283)
Upstream no longer seems to have the commit we were pointing to. We'll
just use the tag since that hasn't changed since the official release
announcement.
2025-10-09 21:50:55 -07:00
sciencewhiz
b56b843c8a Update frcYear in vendordeps (#8276) 2025-10-07 22:00:04 -07:00
sciencewhiz
2fb5271cc9 [build] Update native-utils to 2026 (#8277) 2025-10-05 14:22:53 -07:00
Peter Lilley
f1b9be551b [wpiutil] Add reverse/bidirectional iterators to wpi::circular_buffer (#8275)
Use std::reverse_iterator<> to create reverse iterators, make other
iterators bidirectional to allow for this. Added unit tests.
2025-10-03 23:13:55 -07:00
Sam Carlberg
3972b01c51 Add javac plugin for detecting common error cases at compile time (#8196)
Adds a `@NoDiscard` annotation that can be placed on methods to guarantee their return values are used and on types to guarantee that any method returning that type uses the return value.

Methods that call `@NoDiscard`-annotated functions can add a `@SuppressWarnings("NoDiscard")` or `@SuppressWarnings("all")` annotation (or annotation on the class declaring that method) to silence the compiler error warnings.
2025-10-03 17:42:47 -07:00
Joseph Eng
871769c815 [wpimath] Fix units overload resolution (#8267) 2025-10-02 17:36:30 -07:00
Peter Johnson
ca7718cb08 [glass] FMS: Fix reading past end of GSM buffer (#8268) 2025-10-02 17:35:50 -07:00
Joseph Eng
5e7e5306df [wpiutil] Update StructSerializable contract (NFC) (#7441)
Matches ProtobufSerializable.  This is necessary for generic types.
2025-09-30 14:57:42 -06:00
Gold856
6447011bc3 [ci] Consolidate docs jobs (#7910)
We build docs in three different places, which is annoying to deal with, and it means we build docs two more times than necessary. Now, docs are built just once in the main Gradle workflow, with warnings promoted to errors, eliminating the need for the separate job in lint-format.yml. The uploaded docs artifact is then unpacked and commited to the GitHub Pages repo like normal.
2025-09-29 18:02:42 -07:00
Tyler Veness
bb5ee73e46 [build] Build JNI and benchmarks as release by default (#8257)
It doesn't make sense to benchmark debug binaries. Also, wpimath JNI
performance in unit tests is drastically impacted by debug vs release.
2025-09-29 17:47:17 -07:00
Gold856
0277759d44 [wpiunits] Remove redundant if statement and inaccurate comment (#8262) 2025-09-28 16:00:35 -07:00
Gold856
f9899eb73f [wpimath] Fix odd header path in BangBangController (#8261) 2025-09-27 22:48:00 -07:00
Tyler Veness
6b8be313c7 [build] Fix Java 25 builds (#8245)
I'm able to use a local install of Gradle 9.1 that has Java 25 support,
but some plugin upgrades are needed as well.
2025-09-25 21:28:37 -07:00
Tyler Veness
ab53d51c6f Fix or suppress clang-tidy warnings (#8254) 2025-09-25 21:28:04 -07:00
Tyler Veness
5003939b64 [upstream_utils] Recopy Eigen source (#8251)
Upstream slid the tag (again).  Change to the SHA commit ID until things stabilize.
2025-09-24 09:03:16 -06:00
Tyler Veness
ab259c2e89 [build] Fix Gradle 9 archives deprecation warning (#8247)
The deprecation message was:
```
The `archives` configuration added by the `base` plugin has been
deprecated and will be removed in Gradle 10.0.0. Adding artifacts to the
`archives` configuration will now result in a deprecation warning. If
you want the artifact built when running the `assemble` task, you should
add the artifact (or the task that produces it) as a dependency of the
`assemble` task directly.

val specialJar = tasks.register<Jar>("specialJar") {
    archiveBaseName.set("special")
    from("build/special")
}
tasks.named("assemble") {
    dependsOn(specialJar)
}
```
2025-09-22 11:58:14 -06:00
Tyler Veness
8fb5a1985a [upstream_utils] Recopy Eigen source (#8249)
Upstream slid the tag.
2025-09-21 21:58:43 -07:00
sciencewhiz
1e50471d2c [build] Update gradle-jni to 1.2.0 for Gradle 9 support (#8246) 2025-09-21 08:14:37 -07:00
sciencewhiz
c575a23e8e [build] Fix wpical and sysid icons on macOS (#8243)
Fixes #8239
2025-09-20 20:30:44 -07:00
sciencewhiz
4522cca70f [build] Update to develocity plugin (#8242)
Gradle enterprise plugin has been replaced by develocity.
2025-09-20 20:30:05 -07:00
sciencewhiz
850a148aad [build] Fix gradle 9 deprecations in msvc runtime (#8244) 2025-09-20 17:59:29 -07:00
Tyler Veness
0a4e44ea06 [wpimath] Synchronize C++ and Java RK4 docs (NFC) (#8238) 2025-09-20 15:49:41 -07:00
Tyler Veness
a7e7f6912a [upstream_utils] Upgrade to Eigen 5.0.0 (#8240) 2025-09-20 15:49:23 -07:00
Sam Carlberg
ee0a8a1e56 [epilogue] Support logging of protobuf-serializable types (#8229)
For parity with struct-serializable types.

Change struct serialization to only apply to types with a public static final <type> struct field, instead of relying only on the marker interface (which is not always followed). Doing this allows fallthrough to the protobuf handler for types with dynamic structs but static protobuf serializers.
2025-09-20 11:23:22 -07:00
Tyler Veness
3dbdfa1839 [upstream_utils] Upgrade Sleipnir (#8235) 2025-09-20 11:21:06 -07:00
Tyler Veness
ee3d55e848 [upstream_utils] Upgrade Eigen to latest (#8228) 2025-09-19 17:52:48 -06:00
Tyler Veness
dbffe6e8ac [wpimath] Add readme (#8209) 2025-09-08 17:26:22 -07:00
Ryan Blue
2639e0365b [ci] Update sentinel build with Windows FFI changes (#8218) 2025-09-07 21:33:19 -07:00
Ryan Blue
08f11488b0 [ci] Add 2027 development repo to cleanup task (#8217) 2025-09-07 06:16:26 -07:00
Tyler Veness
632749e6f3 [build] Upgrade Maven dependencies (#8173) 2025-09-01 08:13:46 -07:00
Jade
b0829356fa [wpimath] Fix sysid links (NFC) (#8204)
Signed-off-by: Jade Turner <spacey-sooty@proton.me>
2025-09-01 08:11:39 -07:00
Ryan Blue
ed904851eb [ci] Fix CMake Android build caching (#8206)
sccache was enabled but didn't have write credentials
2025-08-31 21:55:13 -07:00
Wispy
2cfd58f119 [commands] Add Subsystem.idle() (#7815) 2025-08-30 22:54:53 -07:00
Sam Carlberg
129cbbe11d [epilogue] Optimize time and memory usage of epilogue backends (#8190) 2025-08-30 20:15:22 -07:00
Ryan Shavell
45db0fd45e [epilogue] Use reflection to access non-public superclass fields (#7996)
Co-authored-by: Sam Carlberg <sam@slfc.dev>
2025-08-30 20:14:41 -07:00
Kevin-OConnor
9fd4ccf95b [hal] Add CAN Mfgrs and Adjust Device Types (#8201) 2025-08-30 11:36:26 -07:00
sciencewhiz
4e6b9706ff [build] Explicitly set Documentation archiveVersion (#8192)
In the recent gradle or gradle dependencies update, the documentation
zips were being published as documentation-version-unspecified, where
the unspecified was coming from archiveVersion. It looks like we use a
different method of setting the version, so make sure that
archiveVersion is empty
2025-08-26 08:09:18 -06:00
Tyler Veness
0d9e850e22 [wpimath] Fix dt type in C++ tests (#8179)
The UKF test was calling `.value()` on an implicit
`units::millisecond_t` type assuming it was `units::second_t`.

I normalized the rest of the dt declarations while I was at it.
2025-08-16 22:51:13 -07:00
sciencewhiz
46a3318324 [build] Fix processstarter publishing (#8171)
Artifacts weren't in OS and architecture subfolders in the zip like all the other
C++ tools
2025-08-11 20:52:10 -07:00
Daniel Chen
f209ecb0cb [wpimath] Add structs for TrapezoidProfile.State and ExponentialProfile.State (#8163) 2025-08-10 11:45:36 -07:00
Iris
78fa67099e [build] Small fixes to build on GCC 15 (#8148)
Co-authored-by: Tyler Veness <calcmogul@gmail.com>
2025-08-09 00:07:41 -07:00
Tyler Veness
9ac7e286f5 [build] Upgrade Gradle plugins (#8166)
I upgraded all plugins I could see except org.ysb33r.doxygen. 2.0 made
breaking changes, and I couldn't figure out how to migrate.

Most of the changes are for suppressing new linter purification rites.
2025-08-08 23:04:02 -07:00
Tyler Veness
5fd9e1e72a [build] Fix Gradle Task.project deprecation warning (#8167)
```
> Task :wpilibcExamples:checkCommands
Script '/home/tav/frc/wpilib/allwpilib/shared/examplecheck.gradle': line 135
Invocation of Task.project at execution time has been deprecated. This will fail with an error in Gradle 10.0. This API is incompatible with the configuration cache, which will become the only mode supported by Gradle in a future release. Consult the upgrading guide for further information: https://docs.gradle.org/8.14.3/userguide/upgrading_version_7.html#task_project
        at examplecheck_4wsg1s37eigy9vs5arzst20ga$_run_closure5$_closure16$_closure17.doCall$original(/home/tav/frc/wpilib/allwpilib/shared/examplecheck.gradle:135)
        (Run with --stacktrace to get the full stack trace of this deprecation warning.)
```

Moving the project access outside the doLast block makes it occur at
confguration time instead.
2025-08-08 22:48:11 -07:00
Tyler Veness
0a0adebd89 [build] Upgrade to Gradle 8.14.3 (#8164)
This fixes local builds with JDK 24.

I fixed deprecation warnings from `./gradlew wrapper --warning-mode all`
as well.
2025-08-08 09:08:34 -06:00
Gold856
2d11946d98 [wpical] Use updated thirdparty-ceres and move resource files (#8151) 2025-08-03 11:41:25 -07:00
Gold856
c42fde5d07 [ci] Make upstream_utils check error with a more clear message (#8153)
Now it links to the README in upstream_utils.
2025-08-03 11:37:40 -07:00
Rain Heuer
b3aeee18c8 [wpimath] Add vector product and squared length operations to Translation2d/3d (#8133)
Adds methods to compute the dot and cross products between Translation2ds and Translation3ds, as well as methods to compute the square of Distance and Norm, which allows avoiding some calls to sqrt in many cases.

Co-authored-by: Tyler Veness <calcmogul@gmail.com>
2025-07-31 21:05:39 -07:00
Tyler Veness
feee88f40d [wpimath] Remove redundant transposes on symmetric matrices (#8131)
This likely won't have a performance impact since it only affects matrix traversal order, but it does simplify the code.
2025-07-31 21:04:55 -07:00
Peter Johnson
0478176e47 [simgui] Add GUI context getter hooks (#8127)
This enables GUI libraries to be linked statically with shared context.
2025-07-30 21:29:24 -07:00
Tyler Veness
e678a338b4 [ci] Upgrade wpiformat (#8124)
See https://github.com/wpilibsuite/styleguide/pull/312
2025-07-30 11:10:12 -06:00
677 changed files with 12392 additions and 4924 deletions

View File

@@ -45,6 +45,8 @@ Checks:
-clang-diagnostic-#warnings,
-clang-diagnostic-pedantic,
clang-analyzer-*,
-clang-analyzer-optin.cplusplus.UninitializedObject,
-clang-analyzer-security.FloatLoopCounter,
cppcoreguidelines-slicing,
google-build-namespaces,
google-explicit-constructor,

View File

@@ -1,6 +1,4 @@
color: false
definitions: [cmake/modules]
line_length: 100
list_expansion: favour-inlining
quiet: false
unsafe: false

View File

@@ -3,7 +3,10 @@
{
"aql": {
"items.find": {
"repo": "wpilib-mvn-development-local",
"$or":[
{ "repo": "wpilib-mvn-development-local" },
{ "repo": "wpilib-mvn-development-2027-local" }
],
"path": { "$nmatch":"*edu/wpi/first/thirdparty*" },
"$or":[
{

View File

@@ -37,7 +37,7 @@ jobs:
build-mac:
name: "Mac"
runs-on: macos-14
runs-on: macOS-15
steps:
- uses: actions/checkout@v4
with: { fetch-depth: 0 }

View File

@@ -30,7 +30,7 @@ jobs:
- uses: nttld/setup-ndk@v1
id: setup-ndk
with:
ndk-version: r27c
ndk-version: r27d
add-to-path: false
- uses: actions/setup-java@v4
@@ -46,6 +46,12 @@ jobs:
- name: configure
run: cmake --preset with-sccache -DCMAKE_BUILD_TYPE=RelWithDebInfo -DWITH_WPILIB=OFF -DWITH_GUI=OFF -DWITH_CSCORE=OFF -DWITH_TESTS=OFF -DWITH_SIMULATION_MODULES=OFF -DWITH_PROTOBUF=OFF -DWITH_JAVA=ON -DBUILD_SHARED_LIBS=ON -DCMAKE_TOOLCHAIN_FILE=${{ steps.setup-ndk.outputs.ndk-path }}/build/cmake/android.toolchain.cmake -DANDROID_ABI="${{ matrix.abi }}" -DANDROID_PLATFORM=android-24
env:
SCCACHE_WEBDAV_USERNAME: ${{ secrets.ARTIFACTORY_USERNAME }}
SCCACHE_WEBDAV_PASSWORD: ${{ secrets.ARTIFACTORY_PASSWORD }}
- name: build
run: cmake --build build-cmake --parallel $(nproc)
env:
SCCACHE_WEBDAV_USERNAME: ${{ secrets.ARTIFACTORY_USERNAME }}
SCCACHE_WEBDAV_PASSWORD: ${{ secrets.ARTIFACTORY_PASSWORD }}

View File

@@ -20,7 +20,7 @@ jobs:
name: Linux
container: wpilib/roborio-cross-ubuntu:2025-22.04
flags: "--preset with-java-and-sccache -DCMAKE_BUILD_TYPE=Release -DWITH_EXAMPLES=ON"
- os: macOS-14
- os: macOS-15
name: macOS
container: ""
env: ""
@@ -59,7 +59,7 @@ jobs:
uses: lukka/run-vcpkg@v11.5
with:
vcpkgDirectory: ${{ runner.workspace }}/vcpkg
vcpkgGitCommitId: 37c3e63a1306562f7f59c4c3c8892ddd50fdf992 # HEAD on 2024-02-24
vcpkgGitCommitId: 74e6536215718009aae747d86d84b78376bf9e09 # HEAD on 2025-10-17
- name: configure
run: cmake ${{ matrix.flags }}

View File

@@ -1,67 +0,0 @@
name: Documentation
on: [push, workflow_dispatch]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
env:
BASE_PATH: allwpilib/docs
jobs:
publish:
name: "Documentation - Publish"
runs-on: ubuntu-22.04
if: github.repository == 'wpilibsuite/allwpilib' && (github.ref == 'refs/heads/main' || (startsWith(github.ref, 'refs/tags/v') && !contains(github.ref, '2027')))
concurrency: ci-docs-publish
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
persist-credentials: false
- uses: gradle/actions/wrapper-validation@v4
- uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: 17
- name: Set environment variables (Development)
run: |
echo "BRANCH=development" >> $GITHUB_ENV
if: github.ref == 'refs/heads/main'
- name: Set environment variables (Tag)
run: |
echo "EXTRA_GRADLE_ARGS=-PreleaseMode" >> $GITHUB_ENV
echo "BRANCH=beta" >> $GITHUB_ENV
if: startsWith(github.ref, 'refs/tags/v') && !contains(github.ref, '2027')
- name: Set environment variables (Release)
run: |
echo "EXTRA_GRADLE_ARGS=-PreleaseMode" >> $GITHUB_ENV
echo "BRANCH=release" >> $GITHUB_ENV
if: startsWith(github.ref, 'refs/tags/v') && !contains(github.ref, 'alpha') && !contains(github.ref, 'beta') && !contains(github.ref, '2027')
- name: Build with Gradle
run: ./gradlew docs:generateJavaDocs docs:doxygen -PbuildServer ${{ env.EXTRA_GRADLE_ARGS }}
- name: Install SSH Client 🔑
uses: webfactory/ssh-agent@v0.9.0
with:
ssh-private-key: ${{ secrets.GH_DEPLOY_KEY }}
- name: Deploy 🚀
uses: JamesIves/github-pages-deploy-action@v4.6.1
with:
ssh-key: true
repository-name: wpilibsuite/wpilibsuite.github.io
branch: allwpilib-${{ env.BRANCH }}
clean: true
single-commit: true
folder: docs/build/docs
- name: Trigger Workflow
uses: actions/github-script@v7
with:
github-token: ${{ secrets.DISPATCH_PAT_TOKEN }}
script: |
github.rest.actions.createWorkflowDispatch({
owner: context.repo.owner,
repo: 'wpilibsuite.github.io',
workflow_id: 'static.yml',
ref: 'main',
})

View File

@@ -31,6 +31,9 @@ def main():
# Replace GCC warning argument with one Clang recognizes
elif arg == "-Wno-maybe-uninitialized":
out_args.append("-Wno-uninitialized")
# Skip GCC-specific warning argument
elif arg == "-Wno-error=restrict":
pass
else:
out_args.append(arg)

View File

@@ -52,11 +52,11 @@ jobs:
run: echo "EXTRA_GRADLE_ARGS=-PreleaseMode" >> $GITHUB_ENV
if: startsWith(github.ref, 'refs/tags/v') && !contains(github.ref, '2027')
- name: Build with Gradle
uses: addnab/docker-run-action@v3
uses: wpilibsuite/docker-run-action@v4
with:
image: ${{ matrix.container }}
options: -v ${{ github.workspace }}:/work -w /work -e ARTIFACTORY_PUBLISH_USERNAME -e ARTIFACTORY_PUBLISH_PASSWORD -e GITHUB_REF -e CI
run: df . && rm -f semicolon_delimited_script && echo $GITHUB_REF && ./gradlew build --build-cache -PbuildServer -PskipJavaFormat ${{ matrix.build-options }} ${{ env.EXTRA_GRADLE_ARGS }}
run: df . && echo $GITHUB_REF && ./gradlew build --build-cache -PbuildServer -PskipJavaFormat ${{ matrix.build-options }} ${{ env.EXTRA_GRADLE_ARGS }}
env:
ARTIFACTORY_PUBLISH_USERNAME: ${{ secrets.ARTIFACTORY_USERNAME }}
ARTIFACTORY_PUBLISH_PASSWORD: ${{ secrets.ARTIFACTORY_PASSWORD }}
@@ -98,7 +98,7 @@ jobs:
build-options: "-PciReleaseOnly -Pbuildwinarm64 -Ponlywindowsarm64"
task: "copyAllOutputs"
outputs: "build/allOutputs"
- os: macOS-14
- os: macOS-15
artifact-name: macOS
architecture: aarch64
task: "build"
@@ -153,7 +153,7 @@ jobs:
if: matrix.os == 'windows-2022'
- name: Check disk free space pre-cleanup (macOS)
run: df -h .
if: matrix.os == 'macOS-14'
if: matrix.os == 'macOS-15'
- name: Cleanup disk space
# CodeQL: 5G
# go: 748M
@@ -162,10 +162,10 @@ jobs:
rm -rf /Users/runner/hostedtoolcache/CodeQL
rm -rf /Users/runner/hostedtoolcache/go
rm -rf /Users/runner/Library/Android
if: matrix.os == 'macOS-14'
if: matrix.os == 'macOS-15'
- name: Check disk free space post-cleanup (macOS)
run: df -h .
if: matrix.os == 'macOS-14'
if: matrix.os == 'macOS-15'
- name: Build with Gradle
run: ./gradlew ${{ matrix.task }} --build-cache -PbuildServer -PskipJavaFormat ${{ matrix.build-options }} ${{ env.EXTRA_GRADLE_ARGS }}
env:
@@ -181,7 +181,7 @@ jobs:
if: matrix.os == 'windows-2022'
- name: Check disk free space (macOS)
run: df -h .
if: matrix.os == 'macOS-14'
if: matrix.os == 'macOS-15'
- uses: actions/upload-artifact@v4
with:
name: ${{ matrix.artifact-name }}
@@ -203,7 +203,7 @@ jobs:
run: echo "EXTRA_GRADLE_ARGS=-PreleaseMode" >> $GITHUB_ENV
if: startsWith(github.ref, 'refs/tags/v') && !contains(github.ref, '2027')
- name: Build with Gradle
run: ./gradlew docs:zipDocs --build-cache -PbuildServer ${{ env.EXTRA_GRADLE_ARGS }}
run: ./gradlew docs:zipDocs --build-cache -PbuildServer -PdocWarningsAsErrors ${{ env.EXTRA_GRADLE_ARGS }}
env:
ARTIFACTORY_PUBLISH_USERNAME: ${{ secrets.ARTIFACTORY_USERNAME }}
ARTIFACTORY_PUBLISH_PASSWORD: ${{ secrets.ARTIFACTORY_PASSWORD }}
@@ -212,6 +212,68 @@ jobs:
name: Documentation
path: docs/build/outputs
publish:
name: "Documentation - Publish"
runs-on: ubuntu-22.04
if: github.repository == 'wpilibsuite/allwpilib' && (github.ref == 'refs/heads/main' || (startsWith(github.ref, 'refs/tags/v') && !contains(github.ref, '2027')))
needs: [build-documentation]
concurrency: ci-docs-publish
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
persist-credentials: false
- name: Download docs artifacts
uses: actions/download-artifact@v4
with:
name: Documentation
- name: Make output directories
run: |
mkdir -p docs/tmp/doxygen/html
mkdir -p docs/tmp/javadoc
- name: Extract docs
run: |
unzip _GROUP_edu_wpi_first_wpilibc_ID_documentation_CLS.zip -d docs/tmp/doxygen/html
unzip _GROUP_edu_wpi_first_wpilibj_ID_documentation_CLS.zip -d docs/tmp/javadoc
- name: Set environment variables (Development)
run: |
echo "BRANCH=development" >> $GITHUB_ENV
if: github.ref == 'refs/heads/main'
- name: Set environment variables (Tag)
run: |
echo "EXTRA_GRADLE_ARGS=-PreleaseMode" >> $GITHUB_ENV
echo "BRANCH=beta" >> $GITHUB_ENV
if: startsWith(github.ref, 'refs/tags/v') && !contains(github.ref, '2027')
- name: Set environment variables (Release)
run: |
echo "EXTRA_GRADLE_ARGS=-PreleaseMode" >> $GITHUB_ENV
echo "BRANCH=release" >> $GITHUB_ENV
if: startsWith(github.ref, 'refs/tags/v') && !contains(github.ref, 'alpha') && !contains(github.ref, 'beta') && !contains(github.ref, '2027')
- name: Install SSH Client 🔑
uses: webfactory/ssh-agent@v0.9.0
with:
ssh-private-key: ${{ secrets.GH_DEPLOY_KEY }}
- name: Deploy 🚀
uses: JamesIves/github-pages-deploy-action@v4.6.1
with:
ssh-key: true
repository-name: wpilibsuite/wpilibsuite.github.io
branch: allwpilib-${{ env.BRANCH }}
clean: true
single-commit: true
folder: docs/tmp
- name: Trigger Workflow
uses: actions/github-script@v7
with:
github-token: ${{ secrets.DISPATCH_PAT_TOKEN }}
script: |
github.rest.actions.createWorkflowDispatch({
owner: context.repo.owner,
repo: 'wpilibsuite.github.io',
workflow_id: 'static.yml',
ref: 'main',
})
combine:
name: Combine
needs: [build-docker, build-host, build-documentation]

View File

@@ -36,7 +36,7 @@ jobs:
- name: Install wpiformat
run: |
python -m venv ${{ runner.temp }}/wpiformat
${{ runner.temp }}/wpiformat/bin/pip3 install wpiformat==2025.33
${{ runner.temp }}/wpiformat/bin/pip3 install wpiformat==2025.36
- name: Run
run: ${{ runner.temp }}/wpiformat/bin/wpiformat
- name: Check output
@@ -78,7 +78,7 @@ jobs:
- name: Install wpiformat
run: |
python -m venv ${{ runner.temp }}/wpiformat
${{ runner.temp }}/wpiformat/bin/pip3 install wpiformat==2025.33
${{ runner.temp }}/wpiformat/bin/pip3 install wpiformat==2025.36
- name: Create compile_commands.json
run: |
./gradlew generateCompileCommands -Ptoolchain-optional-roboRio
@@ -123,18 +123,3 @@ jobs:
echo '' >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY
if: ${{ failure() }}
documentation:
name: "Documentation"
runs-on: ubuntu-22.04
needs: [validation]
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: 21
- name: Build with Gradle
run: ./gradlew docs:zipDocs -PbuildServer -PdocWarningsAsErrors ${{ env.EXTRA_GRADLE_ARGS }}

View File

@@ -53,11 +53,11 @@ jobs:
with:
fetch-depth: 0
- name: Build with Gradle
uses: addnab/docker-run-action@v3
uses: wpilibsuite/docker-run-action@v4
with:
image: ${{ matrix.container }}
options: -v ${{ github.workspace }}:/work -w /work -e GITHUB_REF -e CI
run: df . && rm -f semicolon_delimited_script && echo $GITHUB_REF && ./gradlew build -PbuildServer -PskipJavaFormat ${{ matrix.build-options }}
run: df . && echo $GITHUB_REF && ./gradlew build -PbuildServer -PskipJavaFormat ${{ matrix.build-options }}
- name: Check free disk space
run: df .
- uses: actions/upload-artifact@v4
@@ -97,15 +97,22 @@ jobs:
build-options: "-PciReleaseOnly -Pbuildwinarm64 -Ponlywindowsarm64"
task: "copyAllOutputs"
outputs: "build/allOutputs"
- os: macOS-14
- os: macOS-15
artifact-name: macOS
architecture: aarch64
task: "build"
outputs: "build/allOutputs"
- os: windows-2022
artifact-name: Win32
artifact-name: Win32FFI
architecture: x86
task: ":ntcoreffi:build"
build-options: "-Pntcoreffibuild \"-Dorg.gradle.jvmargs=-Xmx1096m\""
outputs: "ntcoreffi/build/outputs"
- os: windows-2022
artifact-name: Win64FFI
architecture: x64
task: ":ntcoreffi:build"
build-options: "-Pntcoreffibuild -Pbuildwinarm64"
outputs: "ntcoreffi/build/outputs"
name: "Build - ${{ matrix.artifact-name }}"
runs-on: ${{ matrix.os }}
@@ -139,7 +146,7 @@ jobs:
if: matrix.os == 'windows-2022'
- name: Check disk free space pre-cleanup (macOS)
run: df -h .
if: matrix.os == 'macOS-14'
if: matrix.os == 'macOS-15'
- name: Cleanup disk space
# CodeQL: 5G
# go: 748M
@@ -148,10 +155,10 @@ jobs:
rm -rf /Users/runner/hostedtoolcache/CodeQL
rm -rf /Users/runner/hostedtoolcache/go
rm -rf /Users/runner/Library/Android
if: matrix.os == 'macOS-14'
if: matrix.os == 'macOS-15'
- name: Check disk free space post-cleanup (macOS)
run: df -h .
if: matrix.os == 'macOS-14'
if: matrix.os == 'macOS-15'
- name: Build with Gradle
run: ./gradlew ${{ matrix.task }} -PbuildServer -PskipJavaFormat ${{ matrix.build-options }}
- name: Sign Libraries with Developer ID
@@ -163,7 +170,7 @@ jobs:
if: matrix.os == 'windows-2022'
- name: Check disk free space (macOS)
run: df -h .
if: matrix.os == 'macOS-14'
if: matrix.os == 'macOS-15'
- uses: actions/upload-artifact@v4
with:
name: ${{ matrix.artifact-name }}

View File

@@ -7,7 +7,7 @@ concurrency:
cancel-in-progress: true
env:
YEAR: 2025
YEAR: 2026
jobs:
build-artifacts:
@@ -31,11 +31,11 @@ jobs:
fetch-depth: 0
- uses: gradle/actions/wrapper-validation@v4
- name: Build WPILib with Gradle
uses: addnab/docker-run-action@v3
uses: wpilibsuite/docker-run-action@v4
with:
image: wpilib/roborio-cross-ubuntu:2025-22.04
options: -v ${{ github.workspace }}:/work -w /work -e GITHUB_REF -e CI -e DISPLAY
run: df . && rm -f semicolon_delimited_script && ./gradlew :wpilibc:publish :wpilibj:publish :wpilibNewCommands:publish :hal:publish :cameraserver:publish :ntcore:publish :cscore:publish :wpimath:publish :wpinet:publish :wpiutil:publish :apriltag:publish :wpiunits:publish :simulation:halsim_gui:publish :simulation:halsim_ds_socket:publish :simulation:halsim_ws_server:publish :simulation:halsim_ws_client:publish :simulation:halsim_xrp:publish :fieldImages:publish :romiVendordep:publish :xrpVendordep:publish :epilogue-processor:publish :epilogue-runtime:publish :thirdparty:googletest:publish -x test -x Javadoc -x doxygen --build-cache && cp -r /root/releases/maven/development /work
run: df . && ./gradlew :wpilibc:publish :wpilibj:publish :wpilibNewCommands:publish :hal:publish :cameraserver:publish :ntcore:publish :cscore:publish :wpimath:publish :wpinet:publish :wpiutil:publish :apriltag:publish :wpiunits:publish :simulation:halsim_gui:publish :simulation:halsim_ds_socket:publish :simulation:halsim_ws_server:publish :simulation:halsim_ws_client:publish :simulation:halsim_xrp:publish :fieldImages:publish :romiVendordep:publish :xrpVendordep:publish :epilogue-processor:publish :epilogue-runtime:publish :thirdparty:googletest:publish -x test -x Javadoc -x doxygen --build-cache && cp -r /root/releases/maven/development /work
- uses: actions/upload-artifact@v4
with:
name: MavenArtifacts

View File

@@ -141,4 +141,11 @@ jobs:
- name: Add untracked files to index so they count as changes
run: git add -A
- name: Check output
run: git --no-pager diff --exit-code HEAD ':!*.bazel'
run: |
set +e
git --no-pager diff --exit-code HEAD ':!*.bazel'
git_exit_code=$?
if test "$git_exit_code" -ne "0"; then
echo "::error ::upstream_utils check failed. This is usually caused by a bad script or the copied files differing from what the script outputs. You can learn more about using upstream_utils to modify thirdparty libraries at https://github.com/wpilibsuite/allwpilib/blob/main/upstream_utils/README.md"
exit $git_exit_code
fi

View File

@@ -289,6 +289,7 @@ endif()
set(FILENAME_DEP_REPLACE "get_filename_component(SELF_DIR \"$\{CMAKE_CURRENT_LIST_FILE\}\" PATH)")
set(SELF_DIR "$\{SELF_DIR\}")
set(WPIUNITS_DEP_REPLACE_IMPL "find_dependency(wpiunits)")
set(WPIANNOTATIONS_DEP_REPLACE_IMPL "find_dependency(wpiannotations)")
set(WPIUTIL_DEP_REPLACE "find_dependency(wpiutil)")
add_subdirectory(wpiutil)
@@ -308,6 +309,10 @@ if(WITH_WPIMATH)
add_subdirectory(wpimath)
endif()
if(WITH_JAVA)
add_subdirectory(wpiannotations)
endif()
if(WITH_WPIUNITS AND NOT WITH_WPIMATH)
# In case of building wpiunits standalone
set(WPIUNITS_DEP_REPLACE ${WPIUNITS_DEP_REPLACE_IMPL})
@@ -346,7 +351,9 @@ endif()
if(WITH_WPILIB)
set(APRILTAG_DEP_REPLACE "find_dependency(apriltag)")
set(WPILIBC_DEP_REPLACE "find_dependency(wpilibc)")
set(WPILIBJ_DEP_REPLACE "find_dependency(wpilibj)")
if(WITH_JAVA)
set(WPILIBJ_DEP_REPLACE "find_dependency(wpilibj)")
endif()
set(WPILIBNEWCOMMANDS_DEP_REPLACE "find_dependency(wpilibNewCommands)")
add_subdirectory(apriltag)
add_subdirectory(wpilibj)

View File

@@ -56,7 +56,7 @@ the consequences for any action they deem in violation of this Code of Conduct:
**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing
**Consequence**: A warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.

View File

@@ -51,39 +51,6 @@ Have an idea to make WPILib better? Here's some steps to go from idea to impleme
WPILib uses modified Google style guides for both C++ and Java, which can be found in the [styleguide repository](https://github.com/wpilibsuite/styleguide). Autoformatters are available for many popular editors at https://github.com/google/styleguide. Running wpiformat is required for all contributions and is enforced by our continuous integration system.
While the library should be fully formatted according to the styles, additional elements of the style guide were not followed when the library was initially created. All new code should follow the guidelines. If you are looking for some easy ramp-up tasks, finding areas that don't follow the style guide and fixing them is very welcome.
### Math documentation
When writing math expressions in documentation, use https://www.unicodeit.net/ to convert LaTeX to a Unicode equivalent that's easier to read. Not all expressions will translate (e.g., superscripts of superscripts) so focus on making it readable by someone who isn't familiar with LaTeX. If content on multiple lines needs to be aligned in Doxygen/Javadoc comments (e.g., integration/summation limits, matrices packed with square brackets and superscripts for them), put them in @verbatim/@endverbatim blocks in Doxygen or `<pre>` tags in Javadoc so they render with monospace font.
The LaTeX to Unicode conversions can also be done locally via the unicodeit Python package. To install it, execute:
```bash
pip install --user unicodeit
```
Here's example usage:
```bash
$ python -m unicodeit.cli 'x_{k+1} = Ax_k + Bu_k'
xₖ₊₁ = Axₖ + Buₖ
```
On Linux, this process can be streamlined further by adding the following Bash function to your .bashrc (requires `wl-clipboard` on Wayland or `xclip` on X11):
```bash
# Converts LaTeX to Unicode, prints the result, and copies it to the clipboard
uc() {
if [ $WAYLAND_DISPLAY ]; then
python -m unicodeit.cli $@ | tee >(wl-copy -n)
else
python -m unicodeit.cli $@ | tee >(xclip -sel)
fi
}
```
Here's example usage:
```bash
$ uc 'x_{k+1} = Ax_k + Bu_k'
xₖ₊₁ = Axₖ + Buₖ
```
## Submitting Changes
### Pull Request Format

View File

@@ -13,7 +13,7 @@ This article contains instructions on building projects using a development buil
Development builds are the per-commit build hosted every time a commit is pushed to the [allwpilib](https://github.com/wpilibsuite/allwpilib/) repository. These builds are then hosted on [artifactory](https://frcmaven.wpi.edu/artifactory/webapp/#/home).
To build a project using a development build, find the build.gradle file and open it. Then, add the following code below the plugin section and replace YEAR with the year of the development version. It is also necessary to use a 2025 GradleRIO version, ie `2025.1.1-beta-1`
To build a project using a development build, find the build.gradle file and open it. Then, add the following code below the plugin section and replace YEAR with the year of the development version. It is also necessary to use a 2026 GradleRIO version, ie `2026.1.1`
```groovy
wpi.maven.useLocal = false
@@ -28,13 +28,13 @@ Java
```groovy
plugins {
id "java"
id "edu.wpi.first.GradleRIO" version "2025.1.1-beta-1"
id "edu.wpi.first.GradleRIO" version "2026.1.1"
}
wpi.maven.useLocal = false
wpi.maven.useDevelopment = true
wpi.versions.wpilibVersion = '2025.+'
wpi.versions.wpimathVersion = '2025.+'
wpi.versions.wpilibVersion = '2026.+'
wpi.versions.wpimathVersion = '2026.+'
```
C++
@@ -42,13 +42,13 @@ C++
plugins {
id "cpp"
id "google-test-test-suite"
id "edu.wpi.first.GradleRIO" version "2025.1.1-beta-1"
id "edu.wpi.first.GradleRIO" version "2026.1.1"
}
wpi.maven.useLocal = false
wpi.maven.useDevelopment = true
wpi.versions.wpilibVersion = '2025.+'
wpi.versions.wpimathVersion = '2025.+'
wpi.versions.wpilibVersion = '2026.+'
wpi.versions.wpimathVersion = '2026.+'
```
### Development Build Documentation
@@ -64,7 +64,7 @@ Java
```groovy
plugins {
id "java"
id "edu.wpi.first.GradleRIO" version "2025.1.1-beta-1"
id "edu.wpi.first.GradleRIO" version "2026.1.1"
}
wpi.maven.useLocal = false
@@ -78,7 +78,7 @@ C++
plugins {
id "cpp"
id "google-test-test-suite"
id "edu.wpi.first.GradleRIO" version "2025.1.1-beta-1"
id "edu.wpi.first.GradleRIO" version "2026.1.1"
}
wpi.maven.useLocal = false

View File

@@ -1,4 +1,4 @@
Copyright (c) 2009-2025 FIRST and other WPILib contributors
Copyright (c) 2009-2026 FIRST and other WPILib contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without

View File

@@ -50,7 +50,7 @@ Using Gradle makes building WPILib very straightforward. It only has a few depen
- C++ compiler
- On Linux, install GCC 11 or greater
- On Windows, install [Visual Studio Community 2022](https://visualstudio.microsoft.com/vs/community/) and select the C++ programming language during installation (Gradle can't use the build tools for Visual Studio)
- On macOS 13.3 or newer, install Xcode 14 or later (the command-line build tools are insufficient).
- On macOS, install the Xcode command-line build tools via `xcode-select --install`. Xcode 14 or later is required.
- ARM compiler toolchain
- Run `./gradlew installRoboRioToolchain` after cloning this repository
- If the WPILib installer was used, this toolchain is already installed
@@ -59,8 +59,6 @@ Using Gradle makes building WPILib very straightforward. It only has a few depen
On macOS ARM, run `softwareupdate --install-rosetta`. This is necessary to be able to use the macOS x86 roboRIO toolchain on ARM.
On linux, run `sudo apt install gfortran`. This is necessary to be able to build WPIcal on linux platforms.
## Setup
Clone the WPILib repository and follow the instructions above for installing any required tooling. The build process uses versioning information from git. Downloading the source is not sufficient to run the build.

View File

@@ -15,12 +15,12 @@ rules_jvm_external_deps()
load("@rules_jvm_external//:defs.bzl", "maven_install")
maven_artifacts = [
"org.ejml:ejml-simple:0.43.1",
"com.fasterxml.jackson.core:jackson-annotations:2.15.2",
"com.fasterxml.jackson.core:jackson-core:2.15.2",
"com.fasterxml.jackson.core:jackson-databind:2.15.2",
"us.hebi.quickbuf:quickbuf-runtime:1.3.3",
"com.google.code.gson:gson:2.10.1",
"org.ejml:ejml-simple:0.44.0",
"com.fasterxml.jackson.core:jackson-annotations:2.19.2",
"com.fasterxml.jackson.core:jackson-core:2.19.2",
"com.fasterxml.jackson.core:jackson-databind:2.19.2",
"us.hebi.quickbuf:quickbuf-runtime:1.4",
"com.google.code.gson:gson:2.13.1",
]
maven_install(

View File

@@ -152,9 +152,10 @@ public class AprilTagFieldLayout {
var pose =
switch (origin) {
case kBlueAllianceWallRightSide -> Pose3d.kZero;
case kRedAllianceWallRightSide -> new Pose3d(
new Translation3d(m_fieldDimensions.fieldLength, m_fieldDimensions.fieldWidth, 0),
new Rotation3d(0, 0, Math.PI));
case kRedAllianceWallRightSide ->
new Pose3d(
new Translation3d(m_fieldDimensions.fieldLength, m_fieldDimensions.fieldWidth, 0),
new Rotation3d(0, 0, Math.PI));
};
setOrigin(pose);
}

View File

@@ -17,13 +17,17 @@ public enum AprilTagFields {
/** 2025 Reefscape Welded (see TU 12). */
k2025ReefscapeWelded("2025-reefscape-welded.json"),
/** 2025 Reefscape AndyMark (see TU 12). */
k2025ReefscapeAndyMark("2025-reefscape-andymark.json");
k2025ReefscapeAndyMark("2025-reefscape-andymark.json"),
/** 2026 Rebuilt Welded. */
k2026RebuiltWelded("2026-rebuilt-welded.json"),
/** 2026 Rebuilt AndyMark. */
k2026RebuiltAndymark("2026-rebuilt-andymark.json");
/** Base resource directory. */
public static final String kBaseResourceDir = "/edu/wpi/first/apriltag/";
/** Alias to the current game. */
public static final AprilTagFields kDefaultField = k2025ReefscapeWelded;
public static final AprilTagFields kDefaultField = k2026RebuiltWelded;
/** Resource filename. */
public final String m_resourceFile;

View File

@@ -135,6 +135,8 @@ std::string_view GetResource_2023_chargedup_json();
std::string_view GetResource_2024_crescendo_json();
std::string_view GetResource_2025_reefscape_welded_json();
std::string_view GetResource_2025_reefscape_andymark_json();
std::string_view GetResource_2026_rebuilt_welded_json();
std::string_view GetResource_2026_rebuilt_andymark_json();
} // namespace frc
@@ -156,6 +158,12 @@ AprilTagFieldLayout AprilTagFieldLayout::LoadField(AprilTagField field) {
case AprilTagField::k2025ReefscapeAndyMark:
fieldString = GetResource_2025_reefscape_andymark_json();
break;
case AprilTagField::k2026RebuiltWelded:
fieldString = GetResource_2026_rebuilt_welded_json();
break;
case AprilTagField::k2026RebuiltAndyMark:
fieldString = GetResource_2026_rebuilt_andymark_json();
break;
case AprilTagField::kNumFields:
throw std::invalid_argument("Invalid Field");
}

View File

@@ -24,8 +24,12 @@ enum class AprilTagField {
k2025ReefscapeAndyMark,
/// 2025 Reefscape Welded (see TU12).
k2025ReefscapeWelded,
/// 2026 Rebuilt Andymark.
k2026RebuiltAndyMark,
/// 2026 Rebuilt Welded.
k2026RebuiltWelded,
/// Alias to the current game.
kDefaultField = k2025ReefscapeWelded,
kDefaultField = k2026RebuiltWelded,
// This is a placeholder for denoting the last supported field. This should
// always be the last entry in the enum and should not be used by users

View File

@@ -0,0 +1,33 @@
ID,X,Y,Z,Z-Rotation,X-Rotation
1,467.085,291.791,35,180,0
2,468.559,182.077,44.25,90,0
3,444.797,172.321,44.25,180,0
4,444.797,158.321,44.25,180,0
5,468.559,134.565,44.25,270,0
6,467.085,24.851,35,180,0
7,470.034,24.851,35,0,0
8,482.559,134.565,44.25,270,0
9,492.329,144.321,44.25,0,0
10,492.329,158.321,44.25,0,0
11,482.559,182.077,44.25,90,0
12,470.034,291.791,35,0,0
13,649.58,291.02,21.75,180,0
14,649.58,274.02,21.75,180,0
15,649.566,169.783,21.75,180,0
16,649.566,152.783,21.75,180,0
17,183.034,24.851,35,0,0
18,181.559,134.565,44.25,270,0
19,205.321,144.321,44.25,0,0
20,205.321,158.321,44.25,0,0
21,181.559,182.077,44.25,90,0
22,183.034,291.791,35,0,0
23,180.085,291.791,35,180,0
24,167.559,182.077,44.25,90,0
25,157.79,172.321,44.25,180,0
26,157.79,158.321,44.25,180,0
27,167.559,134.565,44.25,270,0
28,180.085,24.851,35,180,0
29,0.539,25.621,21.75,0,0
30,0.539,42.621,21.75,0,0
31,0.553,146.858,21.75,0,0
32,0.553,163.858,21.75,0,0
1 ID X Y Z Z-Rotation X-Rotation
2 1 467.085 291.791 35 180 0
3 2 468.559 182.077 44.25 90 0
4 3 444.797 172.321 44.25 180 0
5 4 444.797 158.321 44.25 180 0
6 5 468.559 134.565 44.25 270 0
7 6 467.085 24.851 35 180 0
8 7 470.034 24.851 35 0 0
9 8 482.559 134.565 44.25 270 0
10 9 492.329 144.321 44.25 0 0
11 10 492.329 158.321 44.25 0 0
12 11 482.559 182.077 44.25 90 0
13 12 470.034 291.791 35 0 0
14 13 649.58 291.02 21.75 180 0
15 14 649.58 274.02 21.75 180 0
16 15 649.566 169.783 21.75 180 0
17 16 649.566 152.783 21.75 180 0
18 17 183.034 24.851 35 0 0
19 18 181.559 134.565 44.25 270 0
20 19 205.321 144.321 44.25 0 0
21 20 205.321 158.321 44.25 0 0
22 21 181.559 182.077 44.25 90 0
23 22 183.034 291.791 35 0 0
24 23 180.085 291.791 35 180 0
25 24 167.559 182.077 44.25 90 0
26 25 157.79 172.321 44.25 180 0
27 26 157.79 158.321 44.25 180 0
28 27 167.559 134.565 44.25 270 0
29 28 180.085 24.851 35 180 0
30 29 0.539 25.621 21.75 0 0
31 30 0.539 42.621 21.75 0 0
32 31 0.553 146.858 21.75 0 0
33 32 0.553 163.858 21.75 0 0

View File

@@ -0,0 +1,584 @@
{
"tags": [
{
"ID": 1,
"pose": {
"translation": {
"x": 11.863959,
"y": 7.411491399999999,
"z": 0.889
},
"rotation": {
"quaternion": {
"W": 6.123233995736766e-17,
"X": 0.0,
"Y": 0.0,
"Z": 1.0
}
}
}
},
{
"ID": 2,
"pose": {
"translation": {
"x": 11.9013986,
"y": 4.6247558,
"z": 1.12395
},
"rotation": {
"quaternion": {
"W": 0.7071067811865476,
"X": 0.0,
"Y": 0.0,
"Z": 0.7071067811865476
}
}
}
},
{
"ID": 3,
"pose": {
"translation": {
"x": 11.2978438,
"y": 4.3769534,
"z": 1.12395
},
"rotation": {
"quaternion": {
"W": 6.123233995736766e-17,
"X": 0.0,
"Y": 0.0,
"Z": 1.0
}
}
}
},
{
"ID": 4,
"pose": {
"translation": {
"x": 11.2978438,
"y": 4.0213534,
"z": 1.12395
},
"rotation": {
"quaternion": {
"W": 6.123233995736766e-17,
"X": 0.0,
"Y": 0.0,
"Z": 1.0
}
}
}
},
{
"ID": 5,
"pose": {
"translation": {
"x": 11.9013986,
"y": 3.417951,
"z": 1.12395
},
"rotation": {
"quaternion": {
"W": -0.7071067811865475,
"X": -0.0,
"Y": 0.0,
"Z": 0.7071067811865476
}
}
}
},
{
"ID": 6,
"pose": {
"translation": {
"x": 11.863959,
"y": 0.6312154,
"z": 0.889
},
"rotation": {
"quaternion": {
"W": 6.123233995736766e-17,
"X": 0.0,
"Y": 0.0,
"Z": 1.0
}
}
}
},
{
"ID": 7,
"pose": {
"translation": {
"x": 11.9388636,
"y": 0.6312154,
"z": 0.889
},
"rotation": {
"quaternion": {
"W": 1.0,
"X": 0.0,
"Y": 0.0,
"Z": 0.0
}
}
}
},
{
"ID": 8,
"pose": {
"translation": {
"x": 12.2569986,
"y": 3.417951,
"z": 1.12395
},
"rotation": {
"quaternion": {
"W": -0.7071067811865475,
"X": -0.0,
"Y": 0.0,
"Z": 0.7071067811865476
}
}
}
},
{
"ID": 9,
"pose": {
"translation": {
"x": 12.5051566,
"y": 3.6657534,
"z": 1.12395
},
"rotation": {
"quaternion": {
"W": 1.0,
"X": 0.0,
"Y": 0.0,
"Z": 0.0
}
}
}
},
{
"ID": 10,
"pose": {
"translation": {
"x": 12.5051566,
"y": 4.0213534,
"z": 1.12395
},
"rotation": {
"quaternion": {
"W": 1.0,
"X": 0.0,
"Y": 0.0,
"Z": 0.0
}
}
}
},
{
"ID": 11,
"pose": {
"translation": {
"x": 12.2569986,
"y": 4.6247558,
"z": 1.12395
},
"rotation": {
"quaternion": {
"W": 0.7071067811865476,
"X": 0.0,
"Y": 0.0,
"Z": 0.7071067811865476
}
}
}
},
{
"ID": 12,
"pose": {
"translation": {
"x": 11.9388636,
"y": 7.411491399999999,
"z": 0.889
},
"rotation": {
"quaternion": {
"W": 1.0,
"X": 0.0,
"Y": 0.0,
"Z": 0.0
}
}
}
},
{
"ID": 13,
"pose": {
"translation": {
"x": 16.499332,
"y": 7.391907999999999,
"z": 0.55245
},
"rotation": {
"quaternion": {
"W": 6.123233995736766e-17,
"X": 0.0,
"Y": 0.0,
"Z": 1.0
}
}
}
},
{
"ID": 14,
"pose": {
"translation": {
"x": 16.499332,
"y": 6.960107999999999,
"z": 0.55245
},
"rotation": {
"quaternion": {
"W": 6.123233995736766e-17,
"X": 0.0,
"Y": 0.0,
"Z": 1.0
}
}
}
},
{
"ID": 15,
"pose": {
"translation": {
"x": 16.4989764,
"y": 4.3124882,
"z": 0.55245
},
"rotation": {
"quaternion": {
"W": 6.123233995736766e-17,
"X": 0.0,
"Y": 0.0,
"Z": 1.0
}
}
}
},
{
"ID": 16,
"pose": {
"translation": {
"x": 16.4989764,
"y": 3.8806881999999994,
"z": 0.55245
},
"rotation": {
"quaternion": {
"W": 6.123233995736766e-17,
"X": 0.0,
"Y": 0.0,
"Z": 1.0
}
}
}
},
{
"ID": 17,
"pose": {
"translation": {
"x": 4.6490636,
"y": 0.6312154,
"z": 0.889
},
"rotation": {
"quaternion": {
"W": 1.0,
"X": 0.0,
"Y": 0.0,
"Z": 0.0
}
}
}
},
{
"ID": 18,
"pose": {
"translation": {
"x": 4.6115986,
"y": 3.417951,
"z": 1.12395
},
"rotation": {
"quaternion": {
"W": -0.7071067811865475,
"X": -0.0,
"Y": 0.0,
"Z": 0.7071067811865476
}
}
}
},
{
"ID": 19,
"pose": {
"translation": {
"x": 5.2151534,
"y": 3.6657534,
"z": 1.12395
},
"rotation": {
"quaternion": {
"W": 1.0,
"X": 0.0,
"Y": 0.0,
"Z": 0.0
}
}
}
},
{
"ID": 20,
"pose": {
"translation": {
"x": 5.2151534,
"y": 4.0213534,
"z": 1.12395
},
"rotation": {
"quaternion": {
"W": 1.0,
"X": 0.0,
"Y": 0.0,
"Z": 0.0
}
}
}
},
{
"ID": 21,
"pose": {
"translation": {
"x": 4.6115986,
"y": 4.6247558,
"z": 1.12395
},
"rotation": {
"quaternion": {
"W": 0.7071067811865476,
"X": 0.0,
"Y": 0.0,
"Z": 0.7071067811865476
}
}
}
},
{
"ID": 22,
"pose": {
"translation": {
"x": 4.6490636,
"y": 7.411491399999999,
"z": 0.889
},
"rotation": {
"quaternion": {
"W": 1.0,
"X": 0.0,
"Y": 0.0,
"Z": 0.0
}
}
}
},
{
"ID": 23,
"pose": {
"translation": {
"x": 4.574159,
"y": 7.411491399999999,
"z": 0.889
},
"rotation": {
"quaternion": {
"W": 6.123233995736766e-17,
"X": 0.0,
"Y": 0.0,
"Z": 1.0
}
}
}
},
{
"ID": 24,
"pose": {
"translation": {
"x": 4.2559986,
"y": 4.6247558,
"z": 1.12395
},
"rotation": {
"quaternion": {
"W": 0.7071067811865476,
"X": 0.0,
"Y": 0.0,
"Z": 0.7071067811865476
}
}
}
},
{
"ID": 25,
"pose": {
"translation": {
"x": 4.007866,
"y": 4.3769534,
"z": 1.12395
},
"rotation": {
"quaternion": {
"W": 6.123233995736766e-17,
"X": 0.0,
"Y": 0.0,
"Z": 1.0
}
}
}
},
{
"ID": 26,
"pose": {
"translation": {
"x": 4.007866,
"y": 4.0213534,
"z": 1.12395
},
"rotation": {
"quaternion": {
"W": 6.123233995736766e-17,
"X": 0.0,
"Y": 0.0,
"Z": 1.0
}
}
}
},
{
"ID": 27,
"pose": {
"translation": {
"x": 4.2559986,
"y": 3.417951,
"z": 1.12395
},
"rotation": {
"quaternion": {
"W": -0.7071067811865475,
"X": -0.0,
"Y": 0.0,
"Z": 0.7071067811865476
}
}
}
},
{
"ID": 28,
"pose": {
"translation": {
"x": 4.574159,
"y": 0.6312154,
"z": 0.889
},
"rotation": {
"quaternion": {
"W": 6.123233995736766e-17,
"X": 0.0,
"Y": 0.0,
"Z": 1.0
}
}
}
},
{
"ID": 29,
"pose": {
"translation": {
"x": 0.0136906,
"y": 0.6507734,
"z": 0.55245
},
"rotation": {
"quaternion": {
"W": 1.0,
"X": 0.0,
"Y": 0.0,
"Z": 0.0
}
}
}
},
{
"ID": 30,
"pose": {
"translation": {
"x": 0.0136906,
"y": 1.0825734,
"z": 0.55245
},
"rotation": {
"quaternion": {
"W": 1.0,
"X": 0.0,
"Y": 0.0,
"Z": 0.0
}
}
}
},
{
"ID": 31,
"pose": {
"translation": {
"x": 0.0140462,
"y": 3.7301932,
"z": 0.55245
},
"rotation": {
"quaternion": {
"W": 1.0,
"X": 0.0,
"Y": 0.0,
"Z": 0.0
}
}
}
},
{
"ID": 32,
"pose": {
"translation": {
"x": 0.0140462,
"y": 4.1619931999999995,
"z": 0.55245
},
"rotation": {
"quaternion": {
"W": 1.0,
"X": 0.0,
"Y": 0.0,
"Z": 0.0
}
}
}
}
],
"field": {
"length": 16.518,
"width": 8.043
}
}

View File

@@ -0,0 +1,33 @@
ID,X,Y,Z,Z-Rotation,X-Rotation
1,467.637,292.314,35,180,0
2,469.111,182.6,44.25,90,0
3,445.349,172.844,44.25,180,0
4,445.349,158.844,44.25,180,0
5,469.111,135.088,44.25,270,0
6,467.637,25.374,35,180,0
7,470.586,25.374,35,0,0
8,483.111,135.088,44.25,270,0
9,492.881,144.844,44.25,0,0
10,492.881,158.844,44.25,0,0
11,483.111,182.6,44.25,90,0
12,470.586,292.314,35,0,0
13,650.918,291.469,21.75,180,0
14,650.918,274.469,21.75,180,0
15,650.904,170.219,21.75,180,0
16,650.904,153.219,21.75,180,0
17,183.586,25.374,35,0,0
18,182.111,135.088,44.25,270,0
19,205.873,144.844,44.25,0,0
20,205.873,158.844,44.25,0,0
21,182.111,182.6,44.25,90,0
22,183.586,292.314,35,0,0
23,180.637,292.314,35,180,0
24,168.111,182.6,44.25,90,0
25,158.341,172.844,44.25,180,0
26,158.341,158.844,44.25,180,0
27,168.111,135.088,44.25,270,0
28,180.637,25.374,35,180,0
29,0.305,26.219,21.75,0,0
30,0.305,43.219,21.75,0,0
31,0.318,147.469,21.75,0,0
32,0.318,164.469,21.75,0,0
1 ID X Y Z Z-Rotation X-Rotation
2 1 467.637 292.314 35 180 0
3 2 469.111 182.6 44.25 90 0
4 3 445.349 172.844 44.25 180 0
5 4 445.349 158.844 44.25 180 0
6 5 469.111 135.088 44.25 270 0
7 6 467.637 25.374 35 180 0
8 7 470.586 25.374 35 0 0
9 8 483.111 135.088 44.25 270 0
10 9 492.881 144.844 44.25 0 0
11 10 492.881 158.844 44.25 0 0
12 11 483.111 182.6 44.25 90 0
13 12 470.586 292.314 35 0 0
14 13 650.918 291.469 21.75 180 0
15 14 650.918 274.469 21.75 180 0
16 15 650.904 170.219 21.75 180 0
17 16 650.904 153.219 21.75 180 0
18 17 183.586 25.374 35 0 0
19 18 182.111 135.088 44.25 270 0
20 19 205.873 144.844 44.25 0 0
21 20 205.873 158.844 44.25 0 0
22 21 182.111 182.6 44.25 90 0
23 22 183.586 292.314 35 0 0
24 23 180.637 292.314 35 180 0
25 24 168.111 182.6 44.25 90 0
26 25 158.341 172.844 44.25 180 0
27 26 158.341 158.844 44.25 180 0
28 27 168.111 135.088 44.25 270 0
29 28 180.637 25.374 35 180 0
30 29 0.305 26.219 21.75 0 0
31 30 0.305 43.219 21.75 0 0
32 31 0.318 147.469 21.75 0 0
33 32 0.318 164.469 21.75 0 0

View File

@@ -0,0 +1,584 @@
{
"tags": [
{
"ID": 1,
"pose": {
"translation": {
"x": 11.8779798,
"y": 7.4247756,
"z": 0.889
},
"rotation": {
"quaternion": {
"W": 6.123233995736766e-17,
"X": 0.0,
"Y": 0.0,
"Z": 1.0
}
}
}
},
{
"ID": 2,
"pose": {
"translation": {
"x": 11.9154194,
"y": 4.638039999999999,
"z": 1.12395
},
"rotation": {
"quaternion": {
"W": 0.7071067811865476,
"X": 0.0,
"Y": 0.0,
"Z": 0.7071067811865476
}
}
}
},
{
"ID": 3,
"pose": {
"translation": {
"x": 11.3118646,
"y": 4.3902376,
"z": 1.12395
},
"rotation": {
"quaternion": {
"W": 6.123233995736766e-17,
"X": 0.0,
"Y": 0.0,
"Z": 1.0
}
}
}
},
{
"ID": 4,
"pose": {
"translation": {
"x": 11.3118646,
"y": 4.0346376,
"z": 1.12395
},
"rotation": {
"quaternion": {
"W": 6.123233995736766e-17,
"X": 0.0,
"Y": 0.0,
"Z": 1.0
}
}
}
},
{
"ID": 5,
"pose": {
"translation": {
"x": 11.9154194,
"y": 3.4312351999999997,
"z": 1.12395
},
"rotation": {
"quaternion": {
"W": -0.7071067811865475,
"X": -0.0,
"Y": 0.0,
"Z": 0.7071067811865476
}
}
}
},
{
"ID": 6,
"pose": {
"translation": {
"x": 11.8779798,
"y": 0.6444996,
"z": 0.889
},
"rotation": {
"quaternion": {
"W": 6.123233995736766e-17,
"X": 0.0,
"Y": 0.0,
"Z": 1.0
}
}
}
},
{
"ID": 7,
"pose": {
"translation": {
"x": 11.9528844,
"y": 0.6444996,
"z": 0.889
},
"rotation": {
"quaternion": {
"W": 1.0,
"X": 0.0,
"Y": 0.0,
"Z": 0.0
}
}
}
},
{
"ID": 8,
"pose": {
"translation": {
"x": 12.2710194,
"y": 3.4312351999999997,
"z": 1.12395
},
"rotation": {
"quaternion": {
"W": -0.7071067811865475,
"X": -0.0,
"Y": 0.0,
"Z": 0.7071067811865476
}
}
}
},
{
"ID": 9,
"pose": {
"translation": {
"x": 12.519177399999998,
"y": 3.6790375999999996,
"z": 1.12395
},
"rotation": {
"quaternion": {
"W": 1.0,
"X": 0.0,
"Y": 0.0,
"Z": 0.0
}
}
}
},
{
"ID": 10,
"pose": {
"translation": {
"x": 12.519177399999998,
"y": 4.0346376,
"z": 1.12395
},
"rotation": {
"quaternion": {
"W": 1.0,
"X": 0.0,
"Y": 0.0,
"Z": 0.0
}
}
}
},
{
"ID": 11,
"pose": {
"translation": {
"x": 12.2710194,
"y": 4.638039999999999,
"z": 1.12395
},
"rotation": {
"quaternion": {
"W": 0.7071067811865476,
"X": 0.0,
"Y": 0.0,
"Z": 0.7071067811865476
}
}
}
},
{
"ID": 12,
"pose": {
"translation": {
"x": 11.9528844,
"y": 7.4247756,
"z": 0.889
},
"rotation": {
"quaternion": {
"W": 1.0,
"X": 0.0,
"Y": 0.0,
"Z": 0.0
}
}
}
},
{
"ID": 13,
"pose": {
"translation": {
"x": 16.5333172,
"y": 7.4033126,
"z": 0.55245
},
"rotation": {
"quaternion": {
"W": 6.123233995736766e-17,
"X": 0.0,
"Y": 0.0,
"Z": 1.0
}
}
}
},
{
"ID": 14,
"pose": {
"translation": {
"x": 16.5333172,
"y": 6.9715126,
"z": 0.55245
},
"rotation": {
"quaternion": {
"W": 6.123233995736766e-17,
"X": 0.0,
"Y": 0.0,
"Z": 1.0
}
}
}
},
{
"ID": 15,
"pose": {
"translation": {
"x": 16.5329616,
"y": 4.3235626,
"z": 0.55245
},
"rotation": {
"quaternion": {
"W": 6.123233995736766e-17,
"X": 0.0,
"Y": 0.0,
"Z": 1.0
}
}
}
},
{
"ID": 16,
"pose": {
"translation": {
"x": 16.5329616,
"y": 3.8917626,
"z": 0.55245
},
"rotation": {
"quaternion": {
"W": 6.123233995736766e-17,
"X": 0.0,
"Y": 0.0,
"Z": 1.0
}
}
}
},
{
"ID": 17,
"pose": {
"translation": {
"x": 4.6630844,
"y": 0.6444996,
"z": 0.889
},
"rotation": {
"quaternion": {
"W": 1.0,
"X": 0.0,
"Y": 0.0,
"Z": 0.0
}
}
}
},
{
"ID": 18,
"pose": {
"translation": {
"x": 4.6256194,
"y": 3.4312351999999997,
"z": 1.12395
},
"rotation": {
"quaternion": {
"W": -0.7071067811865475,
"X": -0.0,
"Y": 0.0,
"Z": 0.7071067811865476
}
}
}
},
{
"ID": 19,
"pose": {
"translation": {
"x": 5.229174199999999,
"y": 3.6790375999999996,
"z": 1.12395
},
"rotation": {
"quaternion": {
"W": 1.0,
"X": 0.0,
"Y": 0.0,
"Z": 0.0
}
}
}
},
{
"ID": 20,
"pose": {
"translation": {
"x": 5.229174199999999,
"y": 4.0346376,
"z": 1.12395
},
"rotation": {
"quaternion": {
"W": 1.0,
"X": 0.0,
"Y": 0.0,
"Z": 0.0
}
}
}
},
{
"ID": 21,
"pose": {
"translation": {
"x": 4.6256194,
"y": 4.638039999999999,
"z": 1.12395
},
"rotation": {
"quaternion": {
"W": 0.7071067811865476,
"X": 0.0,
"Y": 0.0,
"Z": 0.7071067811865476
}
}
}
},
{
"ID": 22,
"pose": {
"translation": {
"x": 4.6630844,
"y": 7.4247756,
"z": 0.889
},
"rotation": {
"quaternion": {
"W": 1.0,
"X": 0.0,
"Y": 0.0,
"Z": 0.0
}
}
}
},
{
"ID": 23,
"pose": {
"translation": {
"x": 4.5881798,
"y": 7.4247756,
"z": 0.889
},
"rotation": {
"quaternion": {
"W": 6.123233995736766e-17,
"X": 0.0,
"Y": 0.0,
"Z": 1.0
}
}
}
},
{
"ID": 24,
"pose": {
"translation": {
"x": 4.2700194,
"y": 4.638039999999999,
"z": 1.12395
},
"rotation": {
"quaternion": {
"W": 0.7071067811865476,
"X": 0.0,
"Y": 0.0,
"Z": 0.7071067811865476
}
}
}
},
{
"ID": 25,
"pose": {
"translation": {
"x": 4.0218614,
"y": 4.3902376,
"z": 1.12395
},
"rotation": {
"quaternion": {
"W": 6.123233995736766e-17,
"X": 0.0,
"Y": 0.0,
"Z": 1.0
}
}
}
},
{
"ID": 26,
"pose": {
"translation": {
"x": 4.0218614,
"y": 4.0346376,
"z": 1.12395
},
"rotation": {
"quaternion": {
"W": 6.123233995736766e-17,
"X": 0.0,
"Y": 0.0,
"Z": 1.0
}
}
}
},
{
"ID": 27,
"pose": {
"translation": {
"x": 4.2700194,
"y": 3.4312351999999997,
"z": 1.12395
},
"rotation": {
"quaternion": {
"W": -0.7071067811865475,
"X": -0.0,
"Y": 0.0,
"Z": 0.7071067811865476
}
}
}
},
{
"ID": 28,
"pose": {
"translation": {
"x": 4.5881798,
"y": 0.6444996,
"z": 0.889
},
"rotation": {
"quaternion": {
"W": 6.123233995736766e-17,
"X": 0.0,
"Y": 0.0,
"Z": 1.0
}
}
}
},
{
"ID": 29,
"pose": {
"translation": {
"x": 0.0077469999999999995,
"y": 0.6659626,
"z": 0.55245
},
"rotation": {
"quaternion": {
"W": 1.0,
"X": 0.0,
"Y": 0.0,
"Z": 0.0
}
}
}
},
{
"ID": 30,
"pose": {
"translation": {
"x": 0.0077469999999999995,
"y": 1.0977626,
"z": 0.55245
},
"rotation": {
"quaternion": {
"W": 1.0,
"X": 0.0,
"Y": 0.0,
"Z": 0.0
}
}
}
},
{
"ID": 31,
"pose": {
"translation": {
"x": 0.0080772,
"y": 3.7457125999999996,
"z": 0.55245
},
"rotation": {
"quaternion": {
"W": 1.0,
"X": 0.0,
"Y": 0.0,
"Z": 0.0
}
}
}
},
{
"ID": 32,
"pose": {
"translation": {
"x": 0.0080772,
"y": 4.1775126,
"z": 0.55245
},
"rotation": {
"quaternion": {
"W": 1.0,
"X": 0.0,
"Y": 0.0,
"Z": 0.0
}
}
}
}
],
"field": {
"length": 16.541,
"width": 8.069
}
}

View File

@@ -24,7 +24,6 @@ import org.opencv.core.Mat;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;
@SuppressWarnings("PMD.MutableStaticState")
class AprilTagDetectorTest {
@SuppressWarnings("MemberName")
AprilTagDetector detector;

View File

@@ -181,7 +181,11 @@ tasks.register('deployStatic') {
model {
components {
benchmarkCpp(NativeExecutableSpec) {
targetBuildTypes 'debug'
if (project.hasProperty('ciDebugOnly')) {
targetBuildTypes 'debug'
} else {
targetBuildTypes 'release'
}
sources {
cpp {
source {
@@ -235,7 +239,11 @@ model {
}
}
benchmarkCppStatic(NativeExecutableSpec) {
targetBuildTypes 'debug'
if (project.hasProperty('ciDebugOnly')) {
targetBuildTypes 'debug'
} else {
targetBuildTypes 'release'
}
nativeUtils.excludeBinariesFromStrip(it)
sources {
cpp {

View File

@@ -22,6 +22,7 @@ void BM_Transform(benchmark::State& state) {
auto transform = pose2 - pose1;
return units::math::hypot(transform.X(), transform.Y()).value();
}};
// NOLINTNEXTLINE(clang-analyzer-deadcode.DeadStores)
for (auto _ : state) {
traveler.Solve(poses, iterations);
}
@@ -33,6 +34,7 @@ void BM_Twist(benchmark::State& state) {
auto twist = pose1.Log(pose2);
return units::math::hypot(twist.dx, twist.dy).value();
}};
// NOLINTNEXTLINE(clang-analyzer-deadcode.DeadStores)
for (auto _ : state) {
traveler.Solve(poses, iterations);
}

View File

@@ -13,14 +13,14 @@ plugins {
id 'edu.wpi.first.wpilib.versioning.WPILibVersioningPlugin' version '2023.0.1'
id 'edu.wpi.first.wpilib.repositories.WPILibRepositoriesPlugin' version '2020.2'
id 'edu.wpi.first.NativeUtils' apply false
id 'edu.wpi.first.GradleJni' version '1.1.0'
id 'edu.wpi.first.GradleJni' version '1.2.0'
id 'edu.wpi.first.GradleVsCode'
id 'idea'
id 'visual-studio'
id 'net.ltgt.errorprone' version '3.1.0' apply false
id 'com.gradleup.shadow' version '8.3.4' apply false
id 'com.diffplug.spotless' version '6.20.0' apply false
id 'com.github.spotbugs' version '6.0.2' apply false
id 'net.ltgt.errorprone' version '4.3.0' apply false
id 'com.gradleup.shadow' version '9.1.0' apply false
id 'com.diffplug.spotless' version '8.0.0' apply false
id 'com.github.spotbugs' version '6.4.2' apply false
}
wpilibVersioning.buildServerMode = project.hasProperty('buildServer')
@@ -39,11 +39,11 @@ allprojects {
}
}
buildScan {
termsOfServiceUrl = 'https://gradle.com/terms-of-service'
termsOfServiceAgree = 'yes'
publishAlways()
develocity {
buildScan {
termsOfUseUrl = "https://gradle.com/help/legal-terms-of-use"
termsOfUseAgree = "yes"
}
}
import com.github.spotbugs.snom.Effort
@@ -81,7 +81,7 @@ task libraryBuild() {}
build.dependsOn outputVersions
task copyAllOutputs(type: Copy) {
destinationDir outputsFolder
destinationDir = outputsFolder
}
build.dependsOn copyAllOutputs
@@ -170,5 +170,5 @@ ext.getCurrentArch = {
}
wrapper {
gradleVersion = '8.11'
gradleVersion = '8.14.3'
}

View File

@@ -5,9 +5,9 @@ repositories {
url = 'https://frcmaven.wpi.edu/artifactory/ex-gradle'
}
mavenCentral()
url "https://plugins.gradle.org/m2/"
url = "https://plugins.gradle.org/m2/"
}
}
dependencies {
implementation "edu.wpi.first:native-utils:2025.9.1"
implementation "edu.wpi.first:native-utils:2026.0.0"
}

View File

@@ -31,7 +31,7 @@ repositories {
}
dependencies {
implementation 'com.google.code.gson:gson:2.10.1'
implementation 'com.google.code.gson:gson:2.13.1'
implementation project(':wpiutil')
implementation project(':wpinet')

View File

@@ -46,7 +46,7 @@ public final class CameraServer {
private static final String kPublishName = "/CameraPublisher";
private static final class PropertyPublisher implements AutoCloseable {
@SuppressWarnings({"PMD.MissingBreakInSwitch", "PMD.ImplicitSwitchFallThrough", "fallthrough"})
@SuppressWarnings("fallthrough")
PropertyPublisher(NetworkTable table, VideoEvent event) {
String name;
String infoName;
@@ -66,7 +66,7 @@ public final class CameraServer {
break;
case kEnum:
m_choicesTopic = table.getStringArrayTopic(infoName + "/choices");
// fall through
// fallthrough
case kInteger:
m_integerValueEntry = table.getIntegerTopic(name).getEntry(0);
m_minPublisher = table.getIntegerTopic(infoName + "/min").publish();

View File

@@ -13,6 +13,7 @@ import org.opencv.core.Mat;
* @see VisionRunner
* @see VisionThread
*/
@FunctionalInterface
public interface VisionPipeline {
/**
* Processes the image input and sets the result objects. Implementations should make these

View File

@@ -5,7 +5,6 @@
#include <opencv2/core/core.hpp>
#include <wpi/print.h>
#include "cscore.h"
#include "cscore_cv.h"
int main() {

View File

@@ -68,8 +68,9 @@ public class CvSource extends ImageSource {
case 2 -> PixelFormat.kYUYV; // 2 channels is assumed YUYV
case 3 -> PixelFormat.kBGR; // 3 channels is assumed BGR
case 4 -> PixelFormat.kBGRA; // 4 channels is assumed BGRA
default -> throw new VideoException(
"Unable to get pixel format for " + channels + " channels");
default ->
throw new VideoException(
"Unable to get pixel format for " + channels + " channels");
};
putFrame(finalImage, format, true);

View File

@@ -773,8 +773,7 @@ void MjpegServerImpl::ConnThread::SendStream(wpi::raw_socket_ostream& os) {
lastFrameTime = thisFrameTime;
double timestamp = lastFrameTime / 1000000.0;
header.clear();
oss << "\r\n--" BOUNDARY "\r\n"
<< "Content-Type: image/jpeg\r\n";
oss << "\r\n--" BOUNDARY "\r\n" << "Content-Type: image/jpeg\r\n";
wpi::print(oss, "Content-Length: {}\r\n", size);
wpi::print(oss, "X-Timestamp: {}\r\n", timestamp);
oss << "\r\n";

View File

@@ -44,7 +44,7 @@ static O* ConvertToC(std::vector<I>&& in, int* count) {
// retain vector at end of returned array
alignas(T) unsigned char buf[sizeof(T)];
new (buf) T(std::move(in));
std::memcpy(out + size * sizeof(O), buf, sizeof(T));
std::memcpy(out + size, buf, sizeof(T));
return out;
}
@@ -392,7 +392,7 @@ void CS_FreeEvents(CS_Event* arr, int count) {
// destroy vector saved at end of array
using T = std::vector<cs::RawEvent>;
alignas(T) unsigned char buf[sizeof(T)];
std::memcpy(buf, arr + count * sizeof(CS_Event), sizeof(T));
std::memcpy(buf, arr + count, sizeof(T));
reinterpret_cast<T*>(buf)->~T();
std::free(arr);

View File

@@ -484,7 +484,7 @@ const propertyInfo_t propertyInfo[] =
UVCERROR("USB control interface not found");
break;
default:
UVCERROR("ControlRequest failed (KR=sys:sub:code) = {:02Xh}:{:03Xh}:{:04Xh}",
UVCERROR("ControlRequest failed (KR=sys:sub:code) = {:02X}h:{:03X}h:{:04X}h",
sys, sub, code);
break;
}

View File

@@ -255,8 +255,9 @@ bool UsbCameraImpl::CheckDeviceChange(WPARAM wParam, DEV_BROADCAST_HDR* pHdr,
}
void UsbCameraImpl::DeviceDisconnect() {
if (m_connectVerbose)
if (m_connectVerbose) {
SINFO("Disconnected from {}", m_path);
}
m_sourceReader.Reset();
m_mediaSource.Reset();
if (m_imageCallback) {
@@ -655,8 +656,9 @@ void UsbCameraImpl::DeviceCacheProperty(
rawProp->valueStr = perProp->valueStr; // copy
} else {
// Read current raw value and set percentage from it
if (!rawProp->DeviceGet(lock, pProcAmp))
if (!rawProp->DeviceGet(lock, pProcAmp)) {
SWARNING("failed to get property {}", rawProp->name);
}
if (perProp) {
perProp->SetValue(RawToPercentage(*rawProp, rawProp->value));
@@ -666,8 +668,9 @@ void UsbCameraImpl::DeviceCacheProperty(
// Set value on device if user-configured
if (rawProp->valueSet) {
if (!rawProp->DeviceSet(lock, pProcAmp))
if (!rawProp->DeviceSet(lock, pProcAmp)) {
SWARNING("failed to set property {}", rawProp->name);
}
}
// Update pointers since we released the lock
@@ -707,8 +710,9 @@ void UsbCameraImpl::DeviceCacheProperty(
}
NotifyPropertyCreated(*rawIndex, *rawPropPtr);
if (perPropPtr && perIndex)
if (perPropPtr && perIndex) {
NotifyPropertyCreated(*perIndex, *perPropPtr);
}
}
CS_StatusValue UsbCameraImpl::DeviceProcessCommand(

View File

@@ -31,7 +31,7 @@ model {
// Create the ZIP.
def task = project.tasks.create("copyDataLogToolExecutable" + binary.targetPlatform.operatingSystem.name + binary.targetPlatform.architecture.name, Zip) {
description("Copies the DataLogTool executable to the outputs directory.")
description = "Copies the DataLogTool executable to the outputs directory."
destinationDirectory = outputsFolder
archiveBaseName = zipBaseName
@@ -117,7 +117,7 @@ model {
artifactId = baseArtifactId
groupId = artifactGroupId
version wpilibVersioning.version.get()
version = wpilibVersioning.version.get()
}
}
}

View File

@@ -1,6 +1,6 @@
plugins {
id 'java'
id "org.ysb33r.doxygen" version "1.0.4"
id "org.ysb33r.doxygen" version "2.0.0"
}
evaluationDependsOn(':apriltag')
@@ -9,6 +9,7 @@ evaluationDependsOn(':cscore')
evaluationDependsOn(':epilogue-runtime')
evaluationDependsOn(':hal')
evaluationDependsOn(':ntcore')
evaluationDependsOn(':wpiannotations')
evaluationDependsOn(':wpilibNewCommands')
evaluationDependsOn(':wpilibc')
evaluationDependsOn(':wpilibj')
@@ -46,28 +47,41 @@ cppProjectZips.add(project(':romiVendordep').cppHeadersZip)
cppProjectZips.add(project(':xrpVendordep').cppHeadersZip)
doxygen {
// Doxygen binaries are only provided for x86_64 platforms
// Other platforms will need to provide doxygen via their system
// See below maven and https://doxygen.nl/download.html for provided binaries
// Ensure theme.css (from https://github.com/jothepro/doxygen-awesome-css) is compatible with
// doxygen version when updating
// Doxygen binaries are only provided for x86_64 platforms. Other platforms
// will need to use a local Doxygen install.
//
// executeByVersion() fetches Doxygen binaries from
// https://frcmaven.wpi.edu/ui/native/generic-release-mirror/doxygen/, which
// is a mirror of binaries from https://doxygen.nl/download.html.
//
// To mirror a new Doxygen version, retrigger the GitHub Actions workflow in
// https://github.com/wpilibsuite/doxygen-mirror with the desired version
// number as an input.
//
// Ensure theme.css (from https://github.com/jothepro/doxygen-awesome-css)
// is compatible with Doxygen version when updating.
executables {
doxygen {
// Note: has no effect if not on an x86_64 platform - you need to
// have a global install available on your PATH for the Doxygen
// plugin to run.
executableByVersion('1.12.0')
String arch = System.getProperty("os.arch");
if (arch.equals("x86_64") || arch.equals("amd64")) {
executables {
doxygen {
executableByVersion('1.12.0')
String arch = System.getProperty("os.arch");
if (!(arch.equals("x86_64") || arch.equals("amd64"))) {
// Search for a local Doxygen install
executableBySearchPath('doxygen')
}
}
}
}
doxygen {
template 'Doxyfile'
doxygen.sourceSets.main {
template = 'Doxyfile'
cppProjectZips.each {
dependsOn it
source it.source
doxygenDox.dependsOn it
sources it.source
it.ext.includeDirs.each {
cppIncludeRoots.add(it.absolutePath)
}
@@ -77,66 +91,19 @@ doxygen {
// apriltag
exclude 'apriltag_pose.h'
// Eigen
exclude 'Eigen/**'
exclude 'unsupported/**'
// LLVM
exclude 'wpi/AlignOf.h'
exclude 'wpi/Casting.h'
exclude 'wpi/Chrono.h'
exclude 'wpi/Compiler.h'
exclude 'wpi/ConvertUTF.h'
exclude 'wpi/DenseMap.h'
exclude 'wpi/DenseMapInfo.h'
exclude 'wpi/Endian.h'
exclude 'wpi/EpochTracker.h'
exclude 'wpi/Errc.h'
exclude 'wpi/Errno.h'
exclude 'wpi/ErrorHandling.h'
exclude 'wpi/bit.h'
exclude 'wpi/fs.h'
exclude 'wpi/FunctionExtras.h'
exclude 'wpi/function_ref.h'
exclude 'wpi/Hashing.h'
exclude 'wpi/iterator.h'
exclude 'wpi/iterator_range.h'
exclude 'wpi/ManagedStatic.h'
exclude 'wpi/MapVector.h'
exclude 'wpi/MathExtras.h'
exclude 'wpi/MemAlloc.h'
exclude 'wpi/PointerIntPair.h'
exclude 'wpi/PointerLikeTypeTraits.h'
exclude 'wpi/PointerUnion.h'
exclude 'wpi/raw_os_ostream.h'
exclude 'wpi/raw_ostream.h'
exclude 'wpi/SmallPtrSet.h'
exclude 'wpi/SmallSet.h'
exclude 'wpi/SmallString.h'
exclude 'wpi/SmallVector.h'
exclude 'wpi/StringExtras.h'
exclude 'wpi/StringMap.h'
exclude 'wpi/SwapByteOrder.h'
exclude 'wpi/type_traits.h'
exclude 'wpi/VersionTuple.h'
exclude 'wpi/WindowsError.h'
// fmtlib
exclude 'fmt/**'
// libuv
exclude 'uv.h'
exclude 'uv/**'
exclude 'wpinet/uv/**'
// json
exclude 'wpi/adl_serializer.h'
exclude 'wpi/byte_container_with_subtype.h'
exclude 'wpi/detail/**'
exclude 'wpi/json.h'
exclude 'wpi/json_fwd.h'
exclude 'wpi/ordered_map.h'
exclude 'wpi/thirdparty/**'
// mpack
exclude 'wpi/mpack.h'
@@ -168,8 +135,9 @@ doxygen {
tasks.register("zipCppDocs", Zip) {
archiveBaseName = zipBaseNameCpp
archiveVersion = ""
destinationDirectory = outputsFolder
dependsOn doxygen
dependsOn doxygenDox
from ("$buildDir/docs/doxygen/html")
into '/'
}
@@ -177,14 +145,16 @@ tasks.register("zipCppDocs", Zip) {
// Java
configurations {
javaSource {
transitive false
transitive = false
}
}
task generateJavaDocs(type: Javadoc) {
classpath += project(":wpilibj").sourceSets.main.compileClasspath
options.links("https://docs.oracle.com/en/java/javase/17/docs/api/")
options.links("https://docs.opencv.org/4.x/javadoc/")
// workaround for opencv site blocking javadoc tool. If the link is changed,
// docs/opencv/element-list must be redownloaded
options.linksOffline("https://docs.opencv.org/4.10.0/javadoc/", "opencv")
options.addStringOption("tag", "pre:a:Pre-Condition")
options.addBooleanOption("Xdoclint/package:" +
// TODO: v Document these, then remove them from the list
@@ -206,6 +176,7 @@ task generateJavaDocs(type: Javadoc) {
"-edu.wpi.first.math.system.plant.proto," +
"-edu.wpi.first.math.system.plant.struct," +
"-edu.wpi.first.math.trajectory.proto," +
"-edu.wpi.first.math.trajectory.struct," +
// The .measure package contains generated source files for which automatic javadoc
// generation is very difficult to do meaningfully.
"-edu.wpi.first.units.measure", true)
@@ -219,6 +190,7 @@ task generateJavaDocs(type: Javadoc) {
source project(':epilogue-runtime').sourceSets.main.java
source project(':hal').sourceSets.main.java
source project(':ntcore').sourceSets.main.java
source project(':wpiannotations').sourceSets.main.java
source project(':wpilibNewCommands').sourceSets.main.java
source project(':wpilibj').sourceSets.main.java
source project(':wpimath').sourceSets.main.java
@@ -246,6 +218,7 @@ task generateJavaDocs(type: Javadoc) {
tasks.register("zipJavaDocs", Zip) {
archiveBaseName = zipBaseNameJava
archiveVersion = ""
destinationDirectory = outputsFolder
dependsOn generateJavaDocs
from ("$buildDir/docs/javadoc")
@@ -265,15 +238,15 @@ publishing {
artifact zipJavaDocs
artifactId = "${baseArtifactIdJava}"
groupId artifactGroupIdJava
version wpilibVersioning.version.get()
groupId = artifactGroupIdJava
version = wpilibVersioning.version.get()
}
cpp(MavenPublication) {
artifact zipCppDocs
artifactId = "${baseArtifactIdCpp}"
groupId artifactGroupIdCpp
version wpilibVersioning.version.get()
groupId = artifactGroupIdCpp
version = wpilibVersioning.version.get()
}
}
}

29
docs/opencv/element-list Normal file
View File

@@ -0,0 +1,29 @@
org.opencv.aruco
org.opencv.bgsegm
org.opencv.bioinspired
org.opencv.calib3d
org.opencv.core
org.opencv.dnn
org.opencv.dnn_superres
org.opencv.face
org.opencv.features2d
org.opencv.highgui
org.opencv.img_hash
org.opencv.imgcodecs
org.opencv.imgproc
org.opencv.ml
org.opencv.objdetect
org.opencv.osgi
org.opencv.phase_unwrapping
org.opencv.photo
org.opencv.plot
org.opencv.structured_light
org.opencv.text
org.opencv.tracking
org.opencv.utils
org.opencv.video
org.opencv.videoio
org.opencv.wechat_qrcode
org.opencv.xfeatures2d
org.opencv.ximgproc
org.opencv.xphoto

View File

@@ -112,7 +112,8 @@ public class AnnotationProcessor extends AbstractProcessor {
new MeasureHandler(processingEnv),
new PrimitiveHandler(processingEnv),
new SupplierHandler(processingEnv),
new StructHandler(processingEnv), // prioritize struct over sendable
new StructHandler(processingEnv), // prioritize struct over sendable and protobuf
new ProtobufHandler(processingEnv), // then protobuf
new SendableHandler(processingEnv));
m_epiloguerGenerator = new EpilogueGenerator(processingEnv, customLoggers);

View File

@@ -6,6 +6,7 @@ package edu.wpi.first.epilogue.processor;
import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.ArrayType;
import javax.lang.model.type.PrimitiveType;
import javax.lang.model.type.TypeMirror;
@@ -52,7 +53,7 @@ public class ArrayHandler extends ElementHandler {
}
@Override
public String logInvocation(Element element) {
public String logInvocation(Element element, TypeElement loggedClass) {
var dataType = dataType(element);
// known to be an array type (assuming isLoggable is checked first); this is a safe cast
@@ -63,13 +64,17 @@ public class ArrayHandler extends ElementHandler {
return "backend.log(\""
+ loggedName(element)
+ "\", "
+ elementAccess(element)
+ elementAccess(element, loggedClass)
+ ", "
+ m_structHandler.structAccess(componentType)
+ ")";
} else {
// Primitive or string array
return "backend.log(\"" + loggedName(element) + "\", " + elementAccess(element) + ")";
return "backend.log(\""
+ loggedName(element)
+ "\", "
+ elementAccess(element, loggedClass)
+ ")";
}
}
}

View File

@@ -6,6 +6,7 @@ package edu.wpi.first.epilogue.processor;
import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeMirror;
@@ -38,7 +39,7 @@ public class CollectionHandler extends ElementHandler {
}
@Override
public String logInvocation(Element element) {
public String logInvocation(Element element, TypeElement loggedClass) {
var dataType = dataType(element);
var componentType = ((DeclaredType) dataType).getTypeArguments().get(0);
@@ -46,12 +47,16 @@ public class CollectionHandler extends ElementHandler {
return "backend.log(\""
+ loggedName(element)
+ "\", "
+ elementAccess(element)
+ elementAccess(element, loggedClass)
+ ", "
+ m_structHandler.structAccess(componentType)
+ ")";
} else {
return "backend.log(\"" + loggedName(element) + "\", " + elementAccess(element) + ")";
return "backend.log(\""
+ loggedName(element)
+ "\", "
+ elementAccess(element, loggedClass)
+ ")";
}
}
}

View File

@@ -7,6 +7,7 @@ package edu.wpi.first.epilogue.processor;
import java.util.Map;
import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeMirror;
@@ -27,7 +28,7 @@ public class ConfiguredLoggerHandler extends ElementHandler {
}
@Override
public String logInvocation(Element element) {
public String logInvocation(Element element, TypeElement loggedClass) {
var dataType = dataType(element);
var loggerType =
m_customLoggers.entrySet().stream()
@@ -44,7 +45,7 @@ public class ConfiguredLoggerHandler extends ElementHandler {
+ ".tryUpdate(backend.getNested(\""
+ loggedName(element)
+ "\"), "
+ elementAccess(element)
+ elementAccess(element, loggedClass)
+ ", Epilogue.getConfig().errorHandler)";
}
}

View File

@@ -117,9 +117,9 @@ public abstract class ElementHandler {
* @param element the element to generate the access for
* @return the generated access snippet
*/
public String elementAccess(Element element) {
public String elementAccess(Element element, TypeElement loggedClass) {
if (element instanceof VariableElement field) {
return fieldAccess(field);
return fieldAccess(field, loggedClass);
} else if (element instanceof ExecutableElement method) {
return methodAccess(method);
} else {
@@ -127,8 +127,20 @@ public abstract class ElementHandler {
}
}
private static String fieldAccess(VariableElement field) {
if (!field.getModifiers().contains(Modifier.PUBLIC)) {
private static String fieldAccess(VariableElement field, TypeElement loggedClass) {
var mods = field.getModifiers();
// To be directly accessible, the field needs to be:
// - public; or
// - protected or package-private, and declared by a superclass in the same package
// However, we can't cleanly access package information, so we'll always emit a VarHandle
// for any field declared in a superclass unless it's public and we know we can read it.
boolean isVarHandle =
field.getEnclosingElement().equals(loggedClass)
? mods.contains(Modifier.PRIVATE)
: !mods.contains(Modifier.PUBLIC);
if (isVarHandle) {
// ((com.example.Foo) $fooField.get(object))
// Extra parentheses so cast evaluates before appended methods
// (e.g. when appending .getAsDouble())
@@ -136,7 +148,7 @@ public abstract class ElementHandler {
if (type.getKind() == TypeKind.TYPEVAR) {
type = ((TypeVariable) type).getUpperBound();
}
return "((" + type.toString() + ") $" + field.getSimpleName() + ".get(object))";
return "((" + type.toString() + ") " + LoggerGenerator.varHandleName(field) + ".get(object))";
} else {
// object.fooField
return "object." + field.getSimpleName();
@@ -171,5 +183,5 @@ public abstract class ElementHandler {
* @param element the field or method element to generate the logger call for
* @return the generated log invocation
*/
public abstract String logInvocation(Element element);
public abstract String logInvocation(Element element, TypeElement loggedClass);
}

View File

@@ -6,6 +6,7 @@ package edu.wpi.first.epilogue.processor;
import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.TypeMirror;
public class EnumHandler extends ElementHandler {
@@ -27,7 +28,11 @@ public class EnumHandler extends ElementHandler {
}
@Override
public String logInvocation(Element element) {
return "backend.log(\"" + loggedName(element) + "\", " + elementAccess(element) + ")";
public String logInvocation(Element element, TypeElement loggedClass) {
return "backend.log(\""
+ loggedName(element)
+ "\", "
+ elementAccess(element, loggedClass)
+ ")";
}
}

View File

@@ -39,7 +39,7 @@ public class LoggableHandler extends ElementHandler {
}
@Override
public String logInvocation(Element element) {
public String logInvocation(Element element, TypeElement loggedClass) {
TypeMirror dataType = dataType(element);
var declaredType =
m_processingEnv
@@ -61,7 +61,7 @@ public class LoggableHandler extends ElementHandler {
// If there are no known loggable subtypes, return just the single logger call
if (size == 1) {
return generateLoggerCall(element, declaredType, elementAccess(element));
return generateLoggerCall(element, declaredType, elementAccess(element, loggedClass));
}
// Otherwise, generate an if-else chain to compare the element with its known loggable subtypes
@@ -73,7 +73,7 @@ public class LoggableHandler extends ElementHandler {
StringBuilder builder = new StringBuilder();
// Cache the value in a variable so it's only read once
builder.append("var %s = %s;\n".formatted(varName, elementAccess(element)));
builder.append("var %s = %s;\n".formatted(varName, elementAccess(element, loggedClass)));
for (int i = 0; i < size; i++) {
TypeElement type = loggableSubtypes.get(i);

View File

@@ -18,9 +18,11 @@ import java.io.PrintWriter;
import java.lang.annotation.Annotation;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Deque;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -185,7 +187,21 @@ public class LoggerGenerator {
var loggerFile = m_processingEnv.getFiler().createSourceFile(loggerClassName);
var varHandleFields =
loggableFields.stream().filter(e -> !e.getModifiers().contains(Modifier.PUBLIC)).toList();
loggableFields.stream()
.filter(
e -> {
if (e.getEnclosingElement().equals(clazz)) {
// The generated logger is in the same package as the logged class, so the
// only fields it can't read are private ones.
return e.getModifiers().contains(Modifier.PRIVATE);
} else {
// Logging from a superclass. Can only read public fields, unless the superclass
// is in the same package, in which case protected and package-private fields
// are also readable.
return !e.getModifiers().contains(Modifier.PUBLIC);
}
})
.toList();
boolean requiresVarHandles = !varHandleFields.isEmpty();
try (var out = new PrintWriter(loggerFile.openWriter())) {
@@ -214,41 +230,67 @@ public class LoggerGenerator {
+ "> {");
if (requiresVarHandles) {
for (var varHandleField : varHandleFields) {
for (var privateField : varHandleFields) {
// This field needs a VarHandle to access.
// Cache it in the class to avoid lookups
out.println(" private static final VarHandle $" + varHandleField.getSimpleName() + ";");
out.printf(
" // Accesses private or superclass field %s.%s%n",
privateField.getEnclosingElement(), privateField.getSimpleName());
out.printf(" private static final VarHandle %s;%n", varHandleName(privateField));
}
out.println();
}
var classReference = simpleClassName + ".class";
// Static initializer block to load VarHandles and reflection fields
if (requiresVarHandles) {
out.println(" static {");
out.println(" try {");
out.println(
" var lookup = MethodHandles.privateLookupIn("
+ classReference
+ ", MethodHandles.lookup());");
for (var varHandleField : varHandleFields) {
var fieldName = varHandleField.getSimpleName();
out.println(
" $"
+ fieldName
+ " = lookup.findVarHandle("
+ classReference
+ ", \""
+ fieldName
+ "\", "
+ m_processingEnv.getTypeUtils().erasure(varHandleField.asType())
+ ".class);");
}
out.println(" try {");
out.println(" var rootLookup = MethodHandles.lookup();");
// Group private fields by class, then generate a private lookup for each class
// and a VarHandle for each field using that lookup. Sorting and then collecting into a
// LinkedHashMap gives deterministic output ordering (mostly for tests, which check exact
// file contents, but also results in less churn when regenerating files for users who like
// to read the generated logger classes).
//
// This lets us read private fields from superclasses.
Map<Element, List<VariableElement>> privateFieldsByClass =
varHandleFields.stream()
.sorted(Comparator.comparing(e -> e.getSimpleName().toString()))
.collect(
Collectors.groupingBy(
VariableElement::getEnclosingElement,
LinkedHashMap::new,
Collectors.toList()));
privateFieldsByClass.forEach(
(enclosingClass, fields) -> {
String className = enclosingClass.toString();
String lookupName = "lookup$$" + className.replace(".", "_");
out.printf(
" var %s = MethodHandles.privateLookupIn(%s.class, rootLookup);%n",
lookupName, className);
for (var field : fields) {
var fieldname = field.getSimpleName();
out.printf(
" %s = %s.findVarHandle(%s.class, \"%s\", %s.class);%n",
varHandleName(field),
lookupName,
className,
fieldname,
m_processingEnv.getTypeUtils().erasure(field.asType()));
}
});
out.println(" } catch (ReflectiveOperationException e) {");
out.println(
" throw new RuntimeException("
+ "\"[EPILOGUE] Could not load private fields for logging!\", e);");
out.println(" }");
out.println(" }");
out.println();
}
@@ -300,7 +342,7 @@ public class LoggerGenerator {
// to be logged. For example, the sendable handler consumes all sendable types
// but does not log commands or subsystems, to prevent excessive warnings about
// unloggable commands.
var logInvocation = h.logInvocation(loggableElement);
var logInvocation = h.logInvocation(loggableElement, clazz);
if (logInvocation != null) {
out.println(logInvocation.indent(6).stripTrailing() + ";");
}
@@ -315,6 +357,18 @@ public class LoggerGenerator {
}
}
/**
* Generates the name of a VarHandle for access to the given field. The VarHandle variable's name
* is guaranteed to be unique.
*
* @param field The field to generate a VarHandle for
* @return The name of the generated VarHandle variable
*/
public static String varHandleName(VariableElement field) {
return "$%s_%s"
.formatted(field.getEnclosingElement().toString().replace(".", "_"), field.getSimpleName());
}
private void collectLoggables(
TypeElement clazz, List<VariableElement> fields, List<ExecutableElement> methods) {
var config = clazz.getAnnotation(Logged.class);

View File

@@ -6,6 +6,7 @@ package edu.wpi.first.epilogue.processor;
import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.TypeMirror;
public class MeasureHandler extends ElementHandler {
@@ -30,8 +31,12 @@ public class MeasureHandler extends ElementHandler {
}
@Override
public String logInvocation(Element element) {
public String logInvocation(Element element, TypeElement loggedClass) {
// EpilogueBackend has builtin support for logging measures
return "backend.log(\"" + loggedName(element) + "\", " + elementAccess(element) + ")";
return "backend.log(\""
+ loggedName(element)
+ "\", "
+ elementAccess(element, loggedClass)
+ ")";
}
}

View File

@@ -16,6 +16,7 @@ import static javax.lang.model.type.TypeKind.SHORT;
import java.util.Set;
import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.TypeMirror;
public class PrimitiveHandler extends ElementHandler {
@@ -35,7 +36,11 @@ public class PrimitiveHandler extends ElementHandler {
}
@Override
public String logInvocation(Element element) {
return "backend.log(\"" + loggedName(element) + "\", " + elementAccess(element) + ")";
public String logInvocation(Element element, TypeElement loggedClass) {
return "backend.log(\""
+ loggedName(element)
+ "\", "
+ elementAccess(element, loggedClass)
+ ")";
}
}

View File

@@ -0,0 +1,102 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.first.epilogue.processor;
import java.util.Set;
import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.Element;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
/**
* Supports protobuf serializable types. Protobuf-serializable types are loggable if they have a
* public static final {@code proto} field of a type that inherits from {@code Protobuf}.
*/
public class ProtobufHandler extends ElementHandler {
private final TypeMirror m_serializable;
private final TypeElement m_protobufType;
private final Types m_typeUtils;
private final Elements m_elementUtils;
protected ProtobufHandler(ProcessingEnvironment processingEnv) {
super(processingEnv);
m_serializable =
processingEnv
.getElementUtils()
.getTypeElement("edu.wpi.first.util.protobuf.ProtobufSerializable")
.asType();
m_protobufType =
processingEnv.getElementUtils().getTypeElement("edu.wpi.first.util.protobuf.Protobuf");
m_typeUtils = processingEnv.getTypeUtils();
m_elementUtils = processingEnv.getElementUtils();
}
@Override
public boolean isLoggable(Element element) {
return isLoggableType(dataType(element));
}
/**
* Checks if a type is protobuf-serializable: implements the ProtobufSerializable marker interface
* and has a `public static final proto` field of a type that inherits from Protobuf with a
* compatible generic type bound.
*
* @param type The type to check
* @return true if the type is protobuf-serializable, false otherwise
*/
public boolean isLoggableType(TypeMirror type) {
var serializableType = m_typeUtils.erasure(type);
var typeElement = m_elementUtils.getTypeElement(serializableType.toString());
if (typeElement == null) {
return false;
}
// eg `Protobuf<Rotation2d, ?>` instead of the raw `Protobuf` type. The message type doesn't
// really matter here; we can leave it as a wildcard.
var sharpProtobufType =
m_typeUtils.getDeclaredType(
m_protobufType,
typeElement.asType(), // the serializable type
m_typeUtils.getWildcardType(
m_elementUtils.getTypeElement("us.hebi.quickbuf.ProtoMessage").asType(), null));
boolean hasProto =
typeElement.getEnclosedElements().stream()
.filter(e -> e instanceof VariableElement)
.map(e -> (VariableElement) e)
.anyMatch(
field -> {
var nameMatch = field.getSimpleName().contentEquals("proto");
var modifiersMatch =
field
.getModifiers()
.containsAll(Set.of(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL));
var typeMatch =
m_typeUtils.isAssignable(
m_typeUtils.erasure(field.asType()), sharpProtobufType);
return nameMatch && modifiersMatch && typeMatch;
});
return m_typeUtils.isAssignable(type, m_serializable) && hasProto;
}
public String protoAccess(TypeMirror serializableType) {
var className = m_typeUtils.erasure(serializableType).toString();
return className + ".proto";
}
@Override
public String logInvocation(Element element, TypeElement loggedClass) {
return "backend.log(\"%s\", %s, %s)"
.formatted(
loggedName(element),
elementAccess(element, loggedClass),
protoAccess(dataType(element)));
}
}

View File

@@ -44,7 +44,7 @@ public class SendableHandler extends ElementHandler {
}
@Override
public String logInvocation(Element element) {
public String logInvocation(Element element, TypeElement loggedClass) {
var dataType = dataType(element);
// Do not log commands or subsystems via their sendable implementations
@@ -66,7 +66,7 @@ public class SendableHandler extends ElementHandler {
return "logSendable(backend.getNested(\""
+ loggedName(element)
+ "\"), "
+ elementAccess(element)
+ elementAccess(element, loggedClass)
+ ")";
}
}

View File

@@ -4,14 +4,26 @@
package edu.wpi.first.epilogue.processor;
import java.util.Set;
import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.Element;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.ArrayType;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
/**
* Supports struct serializable types. Struct-serializable types are loggable if they have a public
* static final {@code struct} field of a type that inherits from {@code Struct}.
*/
public class StructHandler extends ElementHandler {
private final TypeMirror m_serializable;
private final TypeElement m_structType;
private final Types m_typeUtils;
private final Elements m_elementUtils;
protected StructHandler(ProcessingEnvironment processingEnv) {
super(processingEnv);
@@ -20,16 +32,57 @@ public class StructHandler extends ElementHandler {
.getElementUtils()
.getTypeElement("edu.wpi.first.util.struct.StructSerializable")
.asType();
m_structType =
processingEnv.getElementUtils().getTypeElement("edu.wpi.first.util.struct.Struct");
m_typeUtils = processingEnv.getTypeUtils();
m_elementUtils = processingEnv.getElementUtils();
}
@Override
public boolean isLoggable(Element element) {
return m_typeUtils.isAssignable(dataType(element), m_serializable);
return isLoggableType(dataType(element));
}
/**
* Checks if a type is struct-serializable: implements the StructSerializable marker interface and
* has a `public static final struct` field of a type that inherits from Struct with a compatible
* generic type bound.
*
* @param type The type to check
* @return true if the type is struct-serializable, false otherwise
*/
public boolean isLoggableType(TypeMirror type) {
return m_typeUtils.isAssignable(type, m_serializable);
TypeMirror serializableType;
if (type instanceof ArrayType arr) {
serializableType = arr.getComponentType();
} else {
serializableType = m_typeUtils.erasure(type);
}
var typeElement = m_elementUtils.getTypeElement(serializableType.toString());
if (typeElement == null) {
return false;
}
// eg `Struct<Rotation2d>` instead of the raw `Struct` type
var sharpStructType = m_typeUtils.getDeclaredType(m_structType, typeElement.asType());
boolean hasStruct =
typeElement.getEnclosedElements().stream()
.filter(e -> e instanceof VariableElement)
.map(e -> (VariableElement) e)
.anyMatch(
field -> {
var nameMatch = field.getSimpleName().contentEquals("struct");
var modifiersMatch =
field
.getModifiers()
.containsAll(Set.of(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL));
var typeMatch =
m_typeUtils.isAssignable(
m_typeUtils.erasure(field.asType()), sharpStructType);
return nameMatch && modifiersMatch && typeMatch;
});
return m_typeUtils.isAssignable(type, m_serializable) && hasStruct;
}
public String structAccess(TypeMirror serializableType) {
@@ -38,11 +91,11 @@ public class StructHandler extends ElementHandler {
}
@Override
public String logInvocation(Element element) {
public String logInvocation(Element element, TypeElement loggedClass) {
return "backend.log(\""
+ loggedName(element)
+ "\", "
+ elementAccess(element)
+ elementAccess(element, loggedClass)
+ ", "
+ structAccess(dataType(element))
+ ")";

View File

@@ -6,6 +6,7 @@ package edu.wpi.first.epilogue.processor;
import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.TypeMirror;
public class SupplierHandler extends ElementHandler {
@@ -42,15 +43,19 @@ public class SupplierHandler extends ElementHandler {
}
@Override
public String logInvocation(Element element) {
return "backend.log(\"" + loggedName(element) + "\", " + elementAccess(element) + ")";
public String logInvocation(Element element, TypeElement loggedClass) {
return "backend.log(\""
+ loggedName(element)
+ "\", "
+ elementAccess(element, loggedClass)
+ ")";
}
@Override
public String elementAccess(Element element) {
public String elementAccess(Element element, TypeElement loggedClass) {
var typeUtils = m_processingEnv.getTypeUtils();
var dataType = dataType(element);
String base = super.elementAccess(element);
String base = super.elementAccess(element, loggedClass);
if (typeUtils.isAssignable(dataType, m_booleanSupplier)) {
return base + ".getAsBoolean()";

View File

@@ -8,5 +8,6 @@ java_library(
"//ntcore:networktables-java",
"//wpiunits",
"//wpiutil:wpiutil-java",
"@maven//:us_hebi_quickbuf_quickbuf_runtime",
],
)

View File

@@ -13,4 +13,5 @@ dependencies {
api(project(':ntcore'))
api(project(':wpiutil'))
api(project(':wpiunits'))
testImplementation(project(':wpimath')) // for convenient protobuf types
}

View File

@@ -106,14 +106,13 @@ public abstract class ClassSpecificLogger<T> {
return;
}
var builder =
m_sendables.computeIfAbsent(
sendable,
s -> {
var b = new LogBackedSendableBuilder(backend);
s.initSendable(b);
return b;
});
builder.update();
if (m_sendables.containsKey(sendable)) {
m_sendables.get(sendable).update();
} else {
var builder = new LogBackedSendableBuilder(backend);
sendable.initSendable(builder);
m_sendables.put(sendable, builder);
builder.update();
}
}
}

View File

@@ -6,8 +6,10 @@ package edu.wpi.first.epilogue.logging;
import edu.wpi.first.units.Measure;
import edu.wpi.first.units.Unit;
import edu.wpi.first.util.protobuf.Protobuf;
import edu.wpi.first.util.struct.Struct;
import java.util.Collection;
import us.hebi.quickbuf.ProtoMessage;
/** A backend is a generic interface for Epilogue to log discrete data points. */
public interface EpilogueBackend {
@@ -193,6 +195,17 @@ public interface EpilogueBackend {
log(identifier, array, struct);
}
/**
* Logs a protobuf-serializable object.
*
* @param identifier the identifier of the data point
* @param value the value of the data point
* @param proto the protobuf to use to serialize the data
* @param <P> the protobuf-serializable type
* @param <M> the protobuf message type
*/
<P, M extends ProtoMessage<M>> void log(String identifier, P value, Protobuf<P, M> proto);
/**
* Logs a measurement's value in terms of its base unit.
*

View File

@@ -16,21 +16,28 @@ import edu.wpi.first.util.datalog.FloatArrayLogEntry;
import edu.wpi.first.util.datalog.FloatLogEntry;
import edu.wpi.first.util.datalog.IntegerArrayLogEntry;
import edu.wpi.first.util.datalog.IntegerLogEntry;
import edu.wpi.first.util.datalog.ProtobufLogEntry;
import edu.wpi.first.util.datalog.RawLogEntry;
import edu.wpi.first.util.datalog.StringArrayLogEntry;
import edu.wpi.first.util.datalog.StringLogEntry;
import edu.wpi.first.util.datalog.StructArrayLogEntry;
import edu.wpi.first.util.datalog.StructLogEntry;
import edu.wpi.first.util.protobuf.Protobuf;
import edu.wpi.first.util.struct.Struct;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.function.BiFunction;
import us.hebi.quickbuf.ProtoMessage;
/** A backend implementation that saves information to a WPILib {@link DataLog} file on disk. */
public class FileBackend implements EpilogueBackend {
private final DataLog m_dataLog;
private final Map<String, DataLogEntry> m_entries = new HashMap<>();
private final Map<String, NestedBackend> m_subLoggers = new HashMap<>();
private final Set<Struct<?>> m_seenSchemas = new HashSet<>();
private final Set<Protobuf<?, ?>> m_seenProtos = new HashSet<>();
/**
* Creates a new file-based backend.
@@ -43,7 +50,13 @@ public class FileBackend implements EpilogueBackend {
@Override
public EpilogueBackend getNested(String path) {
return m_subLoggers.computeIfAbsent(path, k -> new NestedBackend(k, this));
if (!m_subLoggers.containsKey(path)) {
var nested = new NestedBackend(path, this);
m_subLoggers.put(path, nested);
return nested;
}
return m_subLoggers.get(path);
}
@SuppressWarnings("unchecked")
@@ -131,14 +144,45 @@ public class FileBackend implements EpilogueBackend {
@Override
@SuppressWarnings("unchecked")
public <S> void log(String identifier, S value, Struct<S> struct) {
m_dataLog.addSchema(struct);
getEntry(identifier, (log, k) -> StructLogEntry.create(log, k, struct)).append(value);
// DataLog.addSchema has checks that we're able to skip, avoiding allocations
if (m_seenSchemas.add(struct)) {
m_dataLog.addSchema(struct);
}
if (!m_entries.containsKey(identifier)) {
m_entries.put(identifier, StructLogEntry.create(m_dataLog, identifier, struct));
}
((StructLogEntry<S>) m_entries.get(identifier)).append(value);
}
@Override
@SuppressWarnings("unchecked")
public <S> void log(String identifier, S[] value, Struct<S> struct) {
m_dataLog.addSchema(struct);
getEntry(identifier, (log, k) -> StructArrayLogEntry.create(log, k, struct)).append(value);
// DataLog.addSchema has checks that we're able to skip, avoiding allocations
if (m_seenSchemas.add(struct)) {
m_dataLog.addSchema(struct);
}
if (!m_entries.containsKey(identifier)) {
m_entries.put(identifier, StructArrayLogEntry.create(m_dataLog, identifier, struct));
}
((StructArrayLogEntry<S>) m_entries.get(identifier)).append(value);
}
@Override
@SuppressWarnings("unchecked")
public <P, M extends ProtoMessage<M>> void log(String identifier, P value, Protobuf<P, M> proto) {
// DataLog.addSchema has checks that we're able to skip, avoiding allocations
if (m_seenProtos.add(proto)) {
m_dataLog.addSchema(proto);
}
if (!m_entries.containsKey(identifier)) {
m_entries.put(identifier, ProtobufLogEntry.create(m_dataLog, identifier, proto));
}
((ProtobufLogEntry<P>) m_entries.get(identifier)).append(value);
}
}

View File

@@ -4,11 +4,13 @@
package edu.wpi.first.epilogue.logging;
import edu.wpi.first.util.protobuf.Protobuf;
import edu.wpi.first.util.struct.Struct;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import us.hebi.quickbuf.ProtoMessage;
/**
* A backend implementation that only logs data when it changes. Useful for keeping bandwidth and
@@ -40,7 +42,13 @@ public class LazyBackend implements EpilogueBackend {
@Override
public EpilogueBackend getNested(String path) {
return m_subLoggers.computeIfAbsent(path, k -> new NestedBackend(k, this));
if (!m_subLoggers.containsKey(path)) {
var nested = new NestedBackend(path, this);
m_subLoggers.put(path, nested);
return nested;
}
return m_subLoggers.get(path);
}
@Override
@@ -237,4 +245,17 @@ public class LazyBackend implements EpilogueBackend {
m_previousValues.put(identifier, value.clone());
m_backend.log(identifier, value, struct);
}
@Override
public <P, M extends ProtoMessage<M>> void log(String identifier, P value, Protobuf<P, M> proto) {
var previous = m_previousValues.get(identifier);
if (Objects.equals(previous, value)) {
// no change
return;
}
m_previousValues.put(identifier, value);
m_backend.log(identifier, value, proto);
}
}

View File

@@ -19,7 +19,6 @@ import java.util.function.LongSupplier;
import java.util.function.Supplier;
/** A sendable builder implementation that sends data to a {@link EpilogueBackend}. */
@SuppressWarnings("PMD.CouplingBetweenObjects") // most methods simply delegate to the backend
public class LogBackedSendableBuilder implements SendableBuilder {
private final EpilogueBackend m_backend;
private final Collection<Runnable> m_updates = new ArrayList<>();

View File

@@ -4,10 +4,12 @@
package edu.wpi.first.epilogue.logging;
import edu.wpi.first.util.protobuf.Protobuf;
import edu.wpi.first.util.struct.Struct;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import us.hebi.quickbuf.ProtoMessage;
/**
* A backend implementation that delegates to other backends. Helpful for simultaneous logging to
@@ -24,7 +26,13 @@ public class MultiBackend implements EpilogueBackend {
@Override
public EpilogueBackend getNested(String path) {
return m_nestedBackends.computeIfAbsent(path, k -> new NestedBackend(k, this));
if (!m_nestedBackends.containsKey(path)) {
var nested = new NestedBackend(path, this);
m_nestedBackends.put(path, nested);
return nested;
}
return m_nestedBackends.get(path);
}
@Override
@@ -131,4 +139,11 @@ public class MultiBackend implements EpilogueBackend {
backend.log(identifier, value, struct);
}
}
@Override
public <P, M extends ProtoMessage<M>> void log(String identifier, P value, Protobuf<P, M> proto) {
for (EpilogueBackend backend : m_backends) {
backend.log(identifier, value, proto);
}
}
}

View File

@@ -13,15 +13,21 @@ import edu.wpi.first.networktables.FloatPublisher;
import edu.wpi.first.networktables.IntegerArrayPublisher;
import edu.wpi.first.networktables.IntegerPublisher;
import edu.wpi.first.networktables.NetworkTableInstance;
import edu.wpi.first.networktables.ProtobufPublisher;
import edu.wpi.first.networktables.Publisher;
import edu.wpi.first.networktables.RawPublisher;
import edu.wpi.first.networktables.StringArrayPublisher;
import edu.wpi.first.networktables.StringPublisher;
import edu.wpi.first.networktables.StructArrayPublisher;
import edu.wpi.first.networktables.StructPublisher;
import edu.wpi.first.util.protobuf.Protobuf;
import edu.wpi.first.util.struct.Struct;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import us.hebi.quickbuf.ProtoMessage;
/**
* A backend implementation that sends data over network tables. Be careful when using this, since
@@ -32,61 +38,82 @@ public class NTEpilogueBackend implements EpilogueBackend {
private final Map<String, Publisher> m_publishers = new HashMap<>();
private final Map<String, NestedBackend> m_nestedBackends = new HashMap<>();
private final Set<Struct<?>> m_seenSchemas = new HashSet<>();
private final Set<Protobuf<?, ?>> m_seenProtos = new HashSet<>();
private final Function<String, IntegerPublisher> m_createIntPublisher;
private final Function<String, FloatPublisher> m_createFloatPublisher;
private final Function<String, DoublePublisher> m_createDoublePublisher;
private final Function<String, BooleanPublisher> m_createBooleanPublisher;
private final Function<String, RawPublisher> m_createRawPublisher;
private final Function<String, IntegerArrayPublisher> m_createIntegerArrayPublisher;
private final Function<String, FloatArrayPublisher> m_createFloatArrayPublisher;
private final Function<String, DoubleArrayPublisher> m_createDoubleArrayPublisher;
private final Function<String, BooleanArrayPublisher> m_createBooleanArrayPublisher;
private final Function<String, StringPublisher> m_createStringPublisher;
private final Function<String, StringArrayPublisher> m_createStringArrayPublisher;
/**
* Creates a logging backend that sends information to NetworkTables.
*
* @param nt the NetworkTable instance to use to send data to
*/
@SuppressWarnings("unchecked")
public NTEpilogueBackend(NetworkTableInstance nt) {
this.m_nt = nt;
m_createIntPublisher = identifier -> m_nt.getIntegerTopic(identifier).publish();
m_createFloatPublisher = identifier -> m_nt.getFloatTopic(identifier).publish();
m_createDoublePublisher = identifier -> m_nt.getDoubleTopic(identifier).publish();
m_createBooleanPublisher = identifier -> m_nt.getBooleanTopic(identifier).publish();
m_createRawPublisher = identifier -> m_nt.getRawTopic(identifier).publish("raw");
m_createIntegerArrayPublisher = identifier -> m_nt.getIntegerArrayTopic(identifier).publish();
m_createFloatArrayPublisher = identifier -> m_nt.getFloatArrayTopic(identifier).publish();
m_createDoubleArrayPublisher = identifier -> m_nt.getDoubleArrayTopic(identifier).publish();
m_createBooleanArrayPublisher = identifier -> m_nt.getBooleanArrayTopic(identifier).publish();
m_createStringPublisher = identifier -> m_nt.getStringTopic(identifier).publish();
m_createStringArrayPublisher = identifier -> m_nt.getStringArrayTopic(identifier).publish();
}
@Override
public EpilogueBackend getNested(String path) {
return m_nestedBackends.computeIfAbsent(path, k -> new NestedBackend(k, this));
if (!m_nestedBackends.containsKey(path)) {
var nested = new NestedBackend(path, this);
m_nestedBackends.put(path, nested);
return nested;
}
return m_nestedBackends.get(path);
}
@Override
public void log(String identifier, int value) {
((IntegerPublisher)
m_publishers.computeIfAbsent(identifier, k -> m_nt.getIntegerTopic(k).publish()))
.set(value);
((IntegerPublisher) m_publishers.computeIfAbsent(identifier, m_createIntPublisher)).set(value);
}
@Override
public void log(String identifier, long value) {
((IntegerPublisher)
m_publishers.computeIfAbsent(identifier, k -> m_nt.getIntegerTopic(k).publish()))
.set(value);
((IntegerPublisher) m_publishers.computeIfAbsent(identifier, m_createIntPublisher)).set(value);
}
@Override
public void log(String identifier, float value) {
((FloatPublisher)
m_publishers.computeIfAbsent(identifier, k -> m_nt.getFloatTopic(k).publish()))
.set(value);
((FloatPublisher) m_publishers.computeIfAbsent(identifier, m_createFloatPublisher)).set(value);
}
@Override
public void log(String identifier, double value) {
((DoublePublisher)
m_publishers.computeIfAbsent(identifier, k -> m_nt.getDoubleTopic(k).publish()))
((DoublePublisher) m_publishers.computeIfAbsent(identifier, m_createDoublePublisher))
.set(value);
}
@Override
public void log(String identifier, boolean value) {
((BooleanPublisher)
m_publishers.computeIfAbsent(identifier, k -> m_nt.getBooleanTopic(k).publish()))
((BooleanPublisher) m_publishers.computeIfAbsent(identifier, m_createBooleanPublisher))
.set(value);
}
@Override
public void log(String identifier, byte[] value) {
((RawPublisher)
m_publishers.computeIfAbsent(identifier, k -> m_nt.getRawTopic(k).publish("raw")))
.set(value);
((RawPublisher) m_publishers.computeIfAbsent(identifier, m_createRawPublisher)).set(value);
}
@Override
@@ -100,68 +127,96 @@ public class NTEpilogueBackend implements EpilogueBackend {
}
((IntegerArrayPublisher)
m_publishers.computeIfAbsent(identifier, k -> m_nt.getIntegerArrayTopic(k).publish()))
m_publishers.computeIfAbsent(identifier, m_createIntegerArrayPublisher))
.set(widened);
}
@Override
public void log(String identifier, long[] value) {
((IntegerArrayPublisher)
m_publishers.computeIfAbsent(identifier, k -> m_nt.getIntegerArrayTopic(k).publish()))
m_publishers.computeIfAbsent(identifier, m_createIntegerArrayPublisher))
.set(value);
}
@Override
public void log(String identifier, float[] value) {
((FloatArrayPublisher)
m_publishers.computeIfAbsent(identifier, k -> m_nt.getFloatArrayTopic(k).publish()))
((FloatArrayPublisher) m_publishers.computeIfAbsent(identifier, m_createFloatArrayPublisher))
.set(value);
}
@Override
public void log(String identifier, double[] value) {
((DoubleArrayPublisher)
m_publishers.computeIfAbsent(identifier, k -> m_nt.getDoubleArrayTopic(k).publish()))
((DoubleArrayPublisher) m_publishers.computeIfAbsent(identifier, m_createDoubleArrayPublisher))
.set(value);
}
@Override
public void log(String identifier, boolean[] value) {
((BooleanArrayPublisher)
m_publishers.computeIfAbsent(identifier, k -> m_nt.getBooleanArrayTopic(k).publish()))
m_publishers.computeIfAbsent(identifier, m_createBooleanArrayPublisher))
.set(value);
}
@Override
public void log(String identifier, String value) {
((StringPublisher)
m_publishers.computeIfAbsent(identifier, k -> m_nt.getStringTopic(k).publish()))
((StringPublisher) m_publishers.computeIfAbsent(identifier, m_createStringPublisher))
.set(value);
}
@Override
public void log(String identifier, String[] value) {
((StringArrayPublisher)
m_publishers.computeIfAbsent(identifier, k -> m_nt.getStringArrayTopic(k).publish()))
((StringArrayPublisher) m_publishers.computeIfAbsent(identifier, m_createStringArrayPublisher))
.set(value);
}
@Override
@SuppressWarnings("unchecked")
public <S> void log(String identifier, S value, Struct<S> struct) {
m_nt.addSchema(struct);
((StructPublisher<S>)
m_publishers.computeIfAbsent(identifier, k -> m_nt.getStructTopic(k, struct).publish()))
.set(value);
// NetworkTableInstance.addSchema has checks that we're able to skip, avoiding allocations
if (m_seenSchemas.add(struct)) {
m_nt.addSchema(struct);
}
if (m_publishers.containsKey(identifier)) {
((StructPublisher<S>) m_publishers.get(identifier)).set(value);
} else {
StructPublisher<S> publisher = m_nt.getStructTopic(identifier, struct).publish();
m_publishers.put(identifier, publisher);
publisher.set(value);
}
}
@Override
@SuppressWarnings("unchecked")
public <S> void log(String identifier, S[] value, Struct<S> struct) {
m_nt.addSchema(struct);
((StructArrayPublisher<S>)
m_publishers.computeIfAbsent(
identifier, k -> m_nt.getStructArrayTopic(k, struct).publish()))
.set(value);
// NetworkTableInstance.addSchema has checks that we're able to skip, avoiding allocations
if (m_seenSchemas.add(struct)) {
m_nt.addSchema(struct);
}
if (m_publishers.containsKey(identifier)) {
((StructArrayPublisher<S>) m_publishers.get(identifier)).set(value);
} else {
StructArrayPublisher<S> publisher = m_nt.getStructArrayTopic(identifier, struct).publish();
m_publishers.put(identifier, publisher);
publisher.set(value);
}
}
@Override
@SuppressWarnings("unchecked")
public <P, M extends ProtoMessage<M>> void log(String identifier, P value, Protobuf<P, M> proto) {
// NetworkTableInstance.addSchema has checks that we're able to skip, avoiding allocations
if (m_seenProtos.add(proto)) {
m_nt.addSchema(proto);
}
if (m_publishers.containsKey(identifier)) {
((ProtobufPublisher<P>) m_publishers.get(identifier)).set(value);
} else {
ProtobufPublisher<P> publisher = m_nt.getProtobufTopic(identifier, proto).publish();
m_publishers.put(identifier, publisher);
publisher.set(value);
}
}
}

View File

@@ -4,9 +4,11 @@
package edu.wpi.first.epilogue.logging;
import edu.wpi.first.util.protobuf.Protobuf;
import edu.wpi.first.util.struct.Struct;
import java.util.HashMap;
import java.util.Map;
import us.hebi.quickbuf.ProtoMessage;
/**
* A backend that logs to an underlying backend, prepending all logged data with a specific prefix.
@@ -17,6 +19,15 @@ public class NestedBackend implements EpilogueBackend {
private final EpilogueBackend m_impl;
private final Map<String, NestedBackend> m_nestedBackends = new HashMap<>();
// String concatenation can be expensive, especially for deeply nested hierarchies with many
// logged fields. For example, logging a hypothetical Robot.elevator.io.getHeight() would result
// in "/Robot/" + "elevator/" + "io/" + "getHeight"; three concatenations and string and byte
// array allocations that need to be cleaned up by the GC. Caching the results means those
// allocations only occur once, resulting in no GC (the strings are always referenced in the
// cache), and minimal time costs (the String object caches its own hash code, so all we do is an
// O(1) table lookup per concatenation)
private final Map<String, String> m_prefixedIdentifiers = new HashMap<>();
/**
* Creates a new nested backed underneath another backend.
*
@@ -33,83 +44,114 @@ public class NestedBackend implements EpilogueBackend {
this.m_impl = impl;
}
/**
* Fast lookup to avoid redundant `m_prefix + identifier` concatenations. If the identifier has
* not been seen before, we compute the concatenation and cache the result for later invocations
* to read. This avoids redundantly recomputing the same concatenations every loop and
* significantly cuts down on the CPU and memory overhead of the Epilogue library.
*
* @param identifier The identifier to prepend with {@link #m_prefix}.
* @return The concatenated string.
*/
private String withPrefix(String identifier) {
// Using computeIfAbsent would result in a new lambda object allocation on every call
if (m_prefixedIdentifiers.containsKey(identifier)) {
return m_prefixedIdentifiers.get(identifier);
}
String result = m_prefix + identifier;
m_prefixedIdentifiers.put(identifier, result);
return result;
}
@Override
public EpilogueBackend getNested(String path) {
return m_nestedBackends.computeIfAbsent(path, k -> new NestedBackend(k, this));
if (!m_nestedBackends.containsKey(path)) {
var nested = new NestedBackend(path, this);
m_nestedBackends.put(path, nested);
return nested;
}
return m_nestedBackends.get(path);
}
@Override
public void log(String identifier, int value) {
m_impl.log(m_prefix + identifier, value);
m_impl.log(withPrefix(identifier), value);
}
@Override
public void log(String identifier, long value) {
m_impl.log(m_prefix + identifier, value);
m_impl.log(withPrefix(identifier), value);
}
@Override
public void log(String identifier, float value) {
m_impl.log(m_prefix + identifier, value);
m_impl.log(withPrefix(identifier), value);
}
@Override
public void log(String identifier, double value) {
m_impl.log(m_prefix + identifier, value);
m_impl.log(withPrefix(identifier), value);
}
@Override
public void log(String identifier, boolean value) {
m_impl.log(m_prefix + identifier, value);
m_impl.log(withPrefix(identifier), value);
}
@Override
public void log(String identifier, byte[] value) {
m_impl.log(m_prefix + identifier, value);
m_impl.log(withPrefix(identifier), value);
}
@Override
public void log(String identifier, int[] value) {
m_impl.log(m_prefix + identifier, value);
m_impl.log(withPrefix(identifier), value);
}
@Override
public void log(String identifier, long[] value) {
m_impl.log(m_prefix + identifier, value);
m_impl.log(withPrefix(identifier), value);
}
@Override
public void log(String identifier, float[] value) {
m_impl.log(m_prefix + identifier, value);
m_impl.log(withPrefix(identifier), value);
}
@Override
public void log(String identifier, double[] value) {
m_impl.log(m_prefix + identifier, value);
m_impl.log(withPrefix(identifier), value);
}
@Override
public void log(String identifier, boolean[] value) {
m_impl.log(m_prefix + identifier, value);
m_impl.log(withPrefix(identifier), value);
}
@Override
public void log(String identifier, String value) {
m_impl.log(m_prefix + identifier, value);
m_impl.log(withPrefix(identifier), value);
}
@Override
public void log(String identifier, String[] value) {
m_impl.log(m_prefix + identifier, value);
m_impl.log(withPrefix(identifier), value);
}
@Override
public <S> void log(String identifier, S value, Struct<S> struct) {
m_impl.log(m_prefix + identifier, value, struct);
m_impl.log(withPrefix(identifier), value, struct);
}
@Override
public <S> void log(String identifier, S[] value, Struct<S> struct) {
m_impl.log(m_prefix + identifier, value, struct);
m_impl.log(withPrefix(identifier), value, struct);
}
@Override
public <P, M extends ProtoMessage<M>> void log(String identifier, P value, Protobuf<P, M> proto) {
m_impl.log(m_prefix + identifier, value, proto);
}
}

View File

@@ -4,7 +4,9 @@
package edu.wpi.first.epilogue.logging;
import edu.wpi.first.util.protobuf.Protobuf;
import edu.wpi.first.util.struct.Struct;
import us.hebi.quickbuf.ProtoMessage;
/** Null backend implementation that logs nothing. */
public class NullBackend implements EpilogueBackend {
@@ -62,4 +64,8 @@ public class NullBackend implements EpilogueBackend {
@Override
public <S> void log(String identifier, S[] value, Struct<S> struct) {}
@Override
public <P, M extends ProtoMessage<M>> void log(
String identifier, P value, Protobuf<P, M> proto) {}
}

View File

@@ -8,6 +8,7 @@ import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertSame;
import edu.wpi.first.math.geometry.Rotation2d;
import java.util.List;
import org.junit.jupiter.api.Test;
@@ -185,4 +186,17 @@ class LazyBackendTest {
assertArrayEquals(
new byte[] {0x01, 0x00, 0x00, 0x00}, (byte[]) backend.getEntries().get(1).value());
}
@Test
void lazyProtobuf() {
var backend = new TestBackend();
var lazy = new LazyBackend(backend);
var rotation = Rotation2d.kZero;
lazy.log("rotation", rotation, Rotation2d.proto);
assertEquals(1, backend.getEntries().size());
var entry = backend.getEntries().get(0);
assertEquals("rotation", entry.identifier());
assertArrayEquals(new byte[] {9, 0, 0, 0, 0, 0, 0, 0, 0}, (byte[]) entry.value());
}
}

View File

@@ -0,0 +1,180 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.first.epilogue.logging;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertSame;
import org.junit.jupiter.api.Test;
class NestedBackendTest {
@Test
void prefixesAppliedAndNested() {
var root = new TestBackend();
var nested = new NestedBackend("/Robot", root);
nested.log("int", 1);
nested.log("string", "hello");
var arm = nested.getNested("arm");
arm.log("position", 2.0);
arm.log("enabled", true);
assertEquals(4, root.getEntries().size());
assertEquals("/Robot/int", root.getEntries().get(0).identifier());
assertEquals(1, root.getEntries().get(0).value());
assertEquals("/Robot/string", root.getEntries().get(1).identifier());
assertEquals("hello", root.getEntries().get(1).value());
assertEquals("/Robot/arm/position", root.getEntries().get(2).identifier());
assertEquals(2.0, root.getEntries().get(2).value());
assertEquals("/Robot/arm/enabled", root.getEntries().get(3).identifier());
assertEquals(true, root.getEntries().get(3).value());
}
@Test
void handlesTrailingSlashOnPrefix() {
var root = new TestBackend();
var a = new NestedBackend("/Robot", root);
var b = new NestedBackend("/Robot/", root);
a.log("x", 1);
b.log("y", 2);
assertEquals("/Robot/x", root.getEntries().get(0).identifier());
assertEquals("/Robot/y", root.getEntries().get(1).identifier());
}
@Test
void getNestedIsCached() {
var root = new TestBackend();
var nested = new NestedBackend("/Robot", root);
var arm1 = nested.getNested("arm");
var arm2 = nested.getNested("arm");
assertSame(arm1, arm2);
}
@Test
void usesPrefixedIdentifierCacheForSameField() {
var root = new TestBackend();
var nested = new NestedBackend("/Robot", root);
// Same field logged multiple times - identifier object should be the same (cached)
// We use assertSame to check that the references are identical
nested.log("x", 0);
nested.log("x", 1);
String id0 = root.getEntries().get(0).identifier();
String id1 = root.getEntries().get(1).identifier();
assertSame(
id0,
id1,
"Identifier %s (id: %d) was not reused (new id: %d)"
.formatted(id0, System.identityHashCode(id0), System.identityHashCode(id1)));
// Also verify through a nested backend path
var arm = nested.getNested("arm");
arm.log("position", 0.0);
arm.log("position", 1.0);
String id2 = root.getEntries().get(2).identifier();
String id3 = root.getEntries().get(3).identifier();
assertSame(
id2,
id3,
"Identifier %s (id: %d) was not reused (new id: %d)"
.formatted(id2, System.identityHashCode(id2), System.identityHashCode(id3)));
// Sanity check actual full values
assertEquals("/Robot/x", id0);
assertEquals("/Robot/arm/position", id2);
}
@Test
void logsAllOverloads() {
var root = new TestBackend();
var nested = new NestedBackend("/Robot", root);
// Scalars
nested.log("int", 1);
nested.log("long", 2L);
nested.log("float", 3.0f);
nested.log("double", 4.0);
nested.log("boolean", true);
nested.log("string", "hello");
// Arrays
nested.log("bytes", new byte[] {1, 2});
nested.log("ints", new int[] {3, 4});
nested.log("longs", new long[] {5L, 6L});
nested.log("floats", new float[] {7.0f, 8.0f});
nested.log("doubles", new double[] {9.0, 10.0});
nested.log("booleans", new boolean[] {true, false});
nested.log("strings", new String[] {"x", "y"});
// Structs
nested.log("customStruct", new CustomStruct(7), CustomStruct.struct);
nested.log(
"customStructs",
new CustomStruct[] {new CustomStruct(0), new CustomStruct(1)},
CustomStruct.struct);
var entries = root.getEntries();
int idx = 0;
// Scalars
assertEquals(new TestBackend.LogEntry<>("/Robot/int", 1), entries.get(idx++));
assertEquals(new TestBackend.LogEntry<>("/Robot/long", 2L), entries.get(idx++));
assertEquals(new TestBackend.LogEntry<>("/Robot/float", 3.0f), entries.get(idx++));
assertEquals(new TestBackend.LogEntry<>("/Robot/double", 4.0), entries.get(idx++));
assertEquals(new TestBackend.LogEntry<>("/Robot/boolean", true), entries.get(idx++));
assertEquals(new TestBackend.LogEntry<>("/Robot/string", "hello"), entries.get(idx++));
// Arrays
assertEquals("/Robot/bytes", entries.get(idx).identifier());
assertArrayEquals(new byte[] {1, 2}, (byte[]) entries.get(idx++).value());
assertEquals("/Robot/ints", entries.get(idx).identifier());
assertArrayEquals(new int[] {3, 4}, (int[]) entries.get(idx++).value());
assertEquals("/Robot/longs", entries.get(idx).identifier());
assertArrayEquals(new long[] {5L, 6L}, (long[]) entries.get(idx++).value());
assertEquals("/Robot/floats", entries.get(idx).identifier());
assertArrayEquals(new float[] {7.0f, 8.0f}, (float[]) entries.get(idx++).value());
assertEquals("/Robot/doubles", entries.get(idx).identifier());
assertArrayEquals(new double[] {9.0, 10.0}, (double[]) entries.get(idx++).value());
assertEquals("/Robot/booleans", entries.get(idx).identifier());
assertArrayEquals(new boolean[] {true, false}, (boolean[]) entries.get(idx++).value());
assertEquals("/Robot/strings", entries.get(idx).identifier());
assertArrayEquals(new String[] {"x", "y"}, (String[]) entries.get(idx++).value());
// Structs are serialized to bytes
assertEquals("/Robot/customStruct", entries.get(idx).identifier());
assertArrayEquals(new byte[] {0x07, 0x00, 0x00, 0x00}, (byte[]) entries.get(idx++).value());
assertEquals("/Robot/customStructs", entries.get(idx).identifier());
// two int32 values, little-endian
assertArrayEquals(
new byte[] {
0x00, 0x00, 0x00, 0x00, // 0 (first element)
0x01, 0x00, 0x00, 0x00, // 1 (second element)
0x00, 0x00, 0x00, 0x00, // 0 (empty space allocated by StructBuffer)
0x00, 0x00, 0x00, 0x00 // 0 (empty space allocated by StructBuffer)
},
(byte[]) entries.get(idx++).value());
// Ensure we covered all calls
assertEquals(idx, entries.size());
}
}

View File

@@ -4,12 +4,14 @@
package edu.wpi.first.epilogue.logging;
import edu.wpi.first.util.protobuf.Protobuf;
import edu.wpi.first.util.struct.Struct;
import edu.wpi.first.util.struct.StructBuffer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import us.hebi.quickbuf.ProtoMessage;
@SuppressWarnings("PMD.TestClassWithoutTestCases") // This is not a test class!
public class TestBackend implements EpilogueBackend {
@@ -114,4 +116,12 @@ public class TestBackend implements EpilogueBackend {
m_entries.add(new LogEntry<>(identifier, serialized));
}
@Override
public <P, M extends ProtoMessage<M>> void log(String identifier, P value, Protobuf<P, M> proto) {
var msg = proto.createMessage();
proto.pack(msg, value);
var serialized = msg.toByteArray();
m_entries.add(new LogEntry<>(identifier, serialized));
}
}

View File

@@ -63,8 +63,8 @@ model {
artifact cppSourcesZip
artifactId = "${baseArtifactId}-cpp"
groupId artifactGroupId
version wpilibVersioning.version.get()
groupId = artifactGroupId
version = wpilibVersioning.version.get()
}
}
}

View File

@@ -17,12 +17,13 @@ public enum Fields {
k2022RapidReact("2022-rapidreact.json"),
k2023ChargedUp("2023-chargedup.json"),
k2024Crescendo("2024-crescendo.json"),
k2025Reefscape("2025-reefscape.json");
k2025Reefscape("2025-reefscape.json"),
k2026Rebuilt("2026-rebuilt.json");
public static final String kBaseResourceDir = "/edu/wpi/first/fields/";
/** Alias to the current game. */
public static final Fields kDefaultField = k2025Reefscape;
public static final Fields kDefaultField = k2026Rebuilt;
public final String m_resourceFile;

View File

@@ -17,10 +17,12 @@
#include "fields/2023-chargedup.h"
#include "fields/2024-crescendo.h"
#include "fields/2025-reefscape.h"
#include "fields/2026-rebuilt.h"
using namespace fields;
static const Field kFields[] = {
{"2026 Rebuilt", GetResource_2026_rebuilt_json, GetResource_2026_field_png},
{"2025 Reefscape", GetResource_2025_reefscape_json,
GetResource_2025_field_png},
{"2024 Crescendo", GetResource_2024_crescendo_json,

View File

@@ -0,0 +1,12 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#pragma once
#include <string_view>
namespace fields {
std::string_view GetResource_2026_rebuilt_json();
std::string_view GetResource_2026_field_png();
} // namespace fields

Binary file not shown.

After

Width:  |  Height:  |  Size: 742 KiB

View File

@@ -0,0 +1,19 @@
{
"game": "Rebuilt",
"field-image": "2026-field.png",
"field-corners": {
"top-left": [
245,
118
],
"bottom-right": [
3942,
1914
]
},
"field-size": [
54.269,
26.474
],
"field-unit": "foot"
}

View File

@@ -86,7 +86,7 @@ model {
// Create the ZIP.
def task = project.tasks.create("copyGlassExecutable" + binary.targetPlatform.operatingSystem.name + binary.targetPlatform.architecture.name, Zip) {
description("Copies the Glass executable to the outputs directory.")
description = "Copies the Glass executable to the outputs directory."
destinationDirectory = outputsFolder
archiveBaseName = zipBaseName
@@ -175,7 +175,7 @@ model {
artifactId = baseArtifactId
groupId = artifactGroupId
version wpilibVersioning.version.get()
version = wpilibVersioning.version.get()
}
libglass(MavenPublication) {
libGlassTaskList.each { artifact it }
@@ -185,7 +185,7 @@ model {
artifactId = libBaseArtifactId
groupId = libArtifactGroupId
version wpilibVersioning.version.get()
version = wpilibVersioning.version.get()
}
libglassnt(MavenPublication) {
libGlassntTaskList.each { artifact it }
@@ -195,7 +195,7 @@ model {
artifactId = libntBaseArtifactId
groupId = libntArtifactGroupId
version wpilibVersioning.version.get()
version = wpilibVersioning.version.get()
}
}
}

View File

@@ -71,11 +71,11 @@ void glass::DisplayFMS(FMSModel* model, bool editableDsAttached) {
}
ImGui::SameLine();
if (ImGui::Button("Auto") && !enabled) {
model->SetMatchTime(15.0);
model->SetMatchTime(20.0);
}
ImGui::SameLine();
if (ImGui::Button("Teleop") && !enabled) {
model->SetMatchTime(135.0);
model->SetMatchTime(140.0);
}
}
@@ -148,11 +148,14 @@ void glass::DisplayFMSReadOnly(FMSModel* model) {
ImGui::TextUnformatted("?");
}
}
wpi::SmallString<64> gameSpecificMessageBuf;
std::string_view gameSpecificMessage =
model->GetGameSpecificMessage(gameSpecificMessageBuf);
ImGui::Text("Game Specific: %s", exists ? gameSpecificMessage.data() : "?");
if (exists) {
wpi::SmallString<64> gsmBuf;
std::string_view gsm = model->GetGameSpecificMessage(gsmBuf);
ImGui::Text("Game Specific: %.*s", static_cast<int>(gsm.size()),
gsm.data());
} else {
ImGui::TextUnformatted("Game Specific: ?");
}
if (!exists) {
ImGui::PopStyleColor();

View File

@@ -343,7 +343,7 @@ static bool InputPose(frc::Pose2d* pose) {
}
FieldInfo::FieldInfo(Storage& storage)
: m_builtin{storage.GetString("builtin", "2025 Reefscape")},
: m_builtin{storage.GetString("builtin", "2026 Rebuilt")},
m_filename{storage.GetString("image")},
m_width{storage.GetFloat("width", kDefaultWidth.to<float>())},
m_height{storage.GetFloat("height", kDefaultHeight.to<float>())},

View File

@@ -4,11 +4,8 @@
#include "glass/other/PIDController.h"
#include <string>
#include <imgui.h>
#include "glass/Context.h"
#include "glass/DataSource.h"
using namespace glass;
@@ -34,8 +31,8 @@ void glass::DisplayPIDController(PIDControllerModel* m) {
[flag](const char* name, double* v,
std::function<void(double)> callback) {
ImGui::SetNextItemWidth(ImGui::GetFontSize() * 4);
if (ImGui::InputScalar(name, ImGuiDataType_Double, v, NULL, NULL,
"%.3f", flag)) {
if (ImGui::InputScalar(name, ImGuiDataType_Double, v, nullptr,
nullptr, "%.3f", flag)) {
callback(*v);
}
};

View File

@@ -4,11 +4,8 @@
#include "glass/other/ProfiledPIDController.h"
#include <string>
#include <imgui.h>
#include "glass/Context.h"
#include "glass/DataSource.h"
using namespace glass;
@@ -34,8 +31,8 @@ void glass::DisplayProfiledPIDController(ProfiledPIDControllerModel* m) {
[flag](const char* name, double* v,
std::function<void(double)> callback) {
ImGui::SetNextItemWidth(ImGui::GetFontSize() * 4);
if (ImGui::InputScalar(name, ImGuiDataType_Double, v, NULL, NULL,
"%.3f", flag)) {
if (ImGui::InputScalar(name, ImGuiDataType_Double, v, nullptr,
nullptr, "%.3f", flag)) {
callback(*v);
}
};

Binary file not shown.

View File

@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME

9
gradlew vendored
View File

@@ -86,8 +86,7 @@ done
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s
' "$PWD" ) || exit
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
@@ -115,7 +114,7 @@ case "$( uname )" in #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
CLASSPATH="\\\"\\\""
# Determine the Java command to use to start the JVM.
@@ -206,7 +205,7 @@ fi
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command:
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# and any embedded shellness will be escaped.
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
# treated as '${Hostname}' itself on the command line.
@@ -214,7 +213,7 @@ DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
"$@"
# Stop when "xargs" is not available.

4
gradlew.bat vendored
View File

@@ -70,11 +70,11 @@ goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
set CLASSPATH=
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
:end
@rem End local scope for the variables with windows NT shell

View File

@@ -17,6 +17,7 @@ kFramework_AdvantageKit = 7
kFramework_MagicBot = 8
kFramework_KitBotTraditional = 9
kFramework_KitBotInline = 10
kFramework_Everybot = 11
kRobotDrive_ArcadeStandard = 1
kRobotDrive_ArcadeButtonSpin = 2
kRobotDrive_ArcadeRatioCurve = 3
@@ -35,6 +36,7 @@ kRobotDriveSwerve_YAGSL = 15
kRobotDriveSwerve_CTRE = 16
kRobotDriveSwerve_MaxSwerve = 17
kRobotDriveSwerve_AdvantageKit = 18
kRobotDriveSwerve_EasySwerve = 19
kDriverStationCIO_Analog = 1
kDriverStationCIO_DigitalIn = 2
kDriverStationCIO_DigitalOut = 3
@@ -68,7 +70,7 @@ kDashboard_Shuffleboard = 4
kDashboard_Elastic = 5
kDashboard_LabVIEW = 6
kDashboard_AdvantageScope = 7
kDashboard_QFRCDashboard = 8
kDashboard_QDash = 8
kDashboard_FRCWebComponents = 9
kDataLogLocation_Onboard = 1
kDataLogLocation_USB = 2

View File

@@ -125,3 +125,13 @@ kResourceType_ThriftyNova = 123
kResourceType_RevServoHub = 124
kResourceType_PWFSEN36005 = 125
kResourceType_LaserShark = 126
kResourceType_YAMS = 127
kResourceType_LEDPattern = 128
kResourceType_LinearQuadraticRegulator = 129
kResourceType_KalmanFilter = 130
kResourceType_PoseEstimator = 131
kResourceType_PoseEstimator3d = 132
kResourceType_LinearSystemLoop = 133
kResourceType_LumynLabs_ConnectorX = 134
kResourceType_LumynLabs_ConnectorXAnimate = 135
kResourceType_RevMAXSplineEncoder = 136

View File

@@ -273,6 +273,26 @@ public final class FRCNetComm {
public static final int kResourceType_PWFSEN36005 = 125;
/** kResourceType_LaserShark = 126. */
public static final int kResourceType_LaserShark = 126;
/** kResourceType_YAMS = 127. */
public static final int kResourceType_YAMS = 127;
/** kResourceType_LEDPattern = 128. */
public static final int kResourceType_LEDPattern = 128;
/** kResourceType_LinearQuadraticRegulator = 129. */
public static final int kResourceType_LinearQuadraticRegulator = 129;
/** kResourceType_KalmanFilter = 130. */
public static final int kResourceType_KalmanFilter = 130;
/** kResourceType_PoseEstimator = 131. */
public static final int kResourceType_PoseEstimator = 131;
/** kResourceType_PoseEstimator3d = 132. */
public static final int kResourceType_PoseEstimator3d = 132;
/** kResourceType_LinearSystemLoop = 133. */
public static final int kResourceType_LinearSystemLoop = 133;
/** kResourceType_LumynLabs_ConnectorX = 134. */
public static final int kResourceType_LumynLabs_ConnectorX = 134;
/** kResourceType_LumynLabs_ConnectorXAnimate = 135. */
public static final int kResourceType_LumynLabs_ConnectorXAnimate = 135;
/** kResourceType_RevMAXSplineEncoder = 136. */
public static final int kResourceType_RevMAXSplineEncoder = 136;
}
/**
@@ -321,6 +341,8 @@ public final class FRCNetComm {
public static final int kFramework_KitBotTraditional = 9;
/** kFramework_KitBotInline = 10. */
public static final int kFramework_KitBotInline = 10;
/** kFramework_Everybot = 11. */
public static final int kFramework_Everybot = 11;
/** kRobotDrive_ArcadeStandard = 1. */
public static final int kRobotDrive_ArcadeStandard = 1;
/** kRobotDrive_ArcadeButtonSpin = 2. */
@@ -357,6 +379,8 @@ public final class FRCNetComm {
public static final int kRobotDriveSwerve_MaxSwerve = 17;
/** kRobotDriveSwerve_AdvantageKit = 18. */
public static final int kRobotDriveSwerve_AdvantageKit = 18;
/** kRobotDriveSwerve_EasySwerve = 19. */
public static final int kRobotDriveSwerve_EasySwerve = 19;
/** kDriverStationCIO_Analog = 1. */
public static final int kDriverStationCIO_Analog = 1;
/** kDriverStationCIO_DigitalIn = 2. */
@@ -423,8 +447,8 @@ public final class FRCNetComm {
public static final int kDashboard_LabVIEW = 6;
/** kDashboard_AdvantageScope = 7. */
public static final int kDashboard_AdvantageScope = 7;
/** kDashboard_QFRCDashboard = 8. */
public static final int kDashboard_QFRCDashboard = 8;
/** kDashboard_QDash = 8. */
public static final int kDashboard_QDash = 8;
/** kDashboard_FRCWebComponents = 9. */
public static final int kDashboard_FRCWebComponents = 9;
/** kDataLogLocation_Onboard = 1. */

View File

@@ -178,6 +178,16 @@ namespace HALUsageReporting {
kResourceType_RevServoHub = 124,
kResourceType_PWFSEN36005 = 125,
kResourceType_LaserShark = 126,
kResourceType_YAMS = 127,
kResourceType_LEDPattern = 128,
kResourceType_LinearQuadraticRegulator = 129,
kResourceType_KalmanFilter = 130,
kResourceType_PoseEstimator = 131,
kResourceType_PoseEstimator3d = 132,
kResourceType_LinearSystemLoop = 133,
kResourceType_LumynLabs_ConnectorX = 134,
kResourceType_LumynLabs_ConnectorXAnimate = 135,
kResourceType_RevMAXSplineEncoder = 136,
};
enum tInstances : int32_t {
kLanguage_LabVIEW = 1,
@@ -199,6 +209,7 @@ namespace HALUsageReporting {
kFramework_MagicBot = 8,
kFramework_KitBotTraditional = 9,
kFramework_KitBotInline = 10,
kFramework_Everybot = 11,
kRobotDrive_ArcadeStandard = 1,
kRobotDrive_ArcadeButtonSpin = 2,
kRobotDrive_ArcadeRatioCurve = 3,
@@ -217,6 +228,7 @@ namespace HALUsageReporting {
kRobotDriveSwerve_CTRE = 16,
kRobotDriveSwerve_MaxSwerve = 17,
kRobotDriveSwerve_AdvantageKit = 18,
kRobotDriveSwerve_EasySwerve = 19,
kDriverStationCIO_Analog = 1,
kDriverStationCIO_DigitalIn = 2,
kDriverStationCIO_DigitalOut = 3,
@@ -250,7 +262,7 @@ namespace HALUsageReporting {
kDashboard_Elastic = 5,
kDashboard_LabVIEW = 6,
kDashboard_AdvantageScope = 7,
kDashboard_QFRCDashboard = 8,
kDashboard_QDash = 8,
kDashboard_FRCWebComponents = 9,
kDataLogLocation_Onboard = 1,
kDataLogLocation_USB = 2,

View File

@@ -147,6 +147,16 @@ typedef enum
kResourceType_RevServoHub = 124,
kResourceType_PWFSEN36005 = 125,
kResourceType_LaserShark = 126,
kResourceType_YAMS = 127,
kResourceType_LEDPattern = 128,
kResourceType_LinearQuadraticRegulator = 129,
kResourceType_KalmanFilter = 130,
kResourceType_PoseEstimator = 131,
kResourceType_PoseEstimator3d = 132,
kResourceType_LinearSystemLoop = 133,
kResourceType_LumynLabs_ConnectorX = 134,
kResourceType_LumynLabs_ConnectorXAnimate = 135,
kResourceType_RevMAXSplineEncoder = 136,
// kResourceType_MaximumID = 255,
} tResourceType;
@@ -172,6 +182,7 @@ typedef enum
kFramework_MagicBot = 8,
kFramework_KitBotTraditional = 9,
kFramework_KitBotInline = 10,
kFramework_Everybot = 11,
kRobotDrive_ArcadeStandard = 1,
kRobotDrive_ArcadeButtonSpin = 2,
kRobotDrive_ArcadeRatioCurve = 3,
@@ -190,6 +201,7 @@ typedef enum
kRobotDriveSwerve_CTRE = 16,
kRobotDriveSwerve_MaxSwerve = 17,
kRobotDriveSwerve_AdvantageKit = 18,
kRobotDriveSwerve_EasySwerve = 19,
kDriverStationCIO_Analog = 1,
kDriverStationCIO_DigitalIn = 2,
kDriverStationCIO_DigitalOut = 3,
@@ -223,7 +235,7 @@ typedef enum
kDashboard_Elastic = 5,
kDashboard_LabVIEW = 6,
kDashboard_AdvantageScope = 7,
kDashboard_QFRCDashboard = 8,
kDashboard_QDash = 8,
kDashboard_FRCWebComponents = 9,
kDataLogLocation_Onboard = 1,
kDataLogLocation_USB = 2,

View File

@@ -15,19 +15,19 @@ package edu.wpi.first.hal;
public class AnalogJNI extends JNIWrapper {
/**
* <i>native declaration : AthenaJava\target\native\include\HAL\Analog.h:58</i><br>
* enum values
* enum values.
*/
public interface AnalogTriggerType {
/** <i>native declaration : AthenaJava\target\native\include\HAL\Analog.h:54</i> */
/** <i>native declaration : AthenaJava\target\native\include\HAL\Analog.h:54</i>. */
int kInWindow = 0;
/** <i>native declaration : AthenaJava\target\native\include\HAL\Analog.h:55</i> */
/** <i>native declaration : AthenaJava\target\native\include\HAL\Analog.h:55</i>. */
int kState = 1;
/** <i>native declaration : AthenaJava\target\native\include\HAL\Analog.h:56</i> */
/** <i>native declaration : AthenaJava\target\native\include\HAL\Analog.h:56</i>. */
int kRisingPulse = 2;
/** <i>native declaration : AthenaJava\target\native\include\HAL\Analog.h:57</i> */
/** <i>native declaration : AthenaJava\target\native\include\HAL\Analog.h:57</i>. */
int kFallingPulse = 3;
}

View File

@@ -35,10 +35,10 @@ public final class CANAPITypes {
kGyroSensor(4),
/** Accelerometer. */
kAccelerometer(5),
/** Ultrasonic sensor. */
kUltrasonicSensor(6),
/** Gear tooth sensor. */
kGearToothSensor(7),
/** Distance sensor. */
kDistanceSensor(6),
/** Encoder. */
kEncoder(7),
/** Power distribution. */
kPowerDistribution(8),
/** Pneumatics. */
@@ -49,11 +49,13 @@ public final class CANAPITypes {
kIOBreakout(11),
/** Servo Controller. */
kServoController(12),
/** Color Sensor. */
kColorSensor(13),
/** Firmware update. */
kFirmwareUpdate(31);
/** The device type ID. */
@SuppressWarnings("PMD.MemberName")
@SuppressWarnings("MemberName")
public final int id;
CANDeviceType(int id) {
@@ -105,10 +107,18 @@ public final class CANAPITypes {
/** AndyMark. */
kAndyMark(15),
/** Vivid-Hosting. */
kVividHosting(16);
kVividHosting(16),
/** Vertos Robotics. */
kVertosRobotics(17),
/** SWYFT Robotics. */
kSWYFTRobotics(18),
/** Lumyn Labs. */
kLumynLabs(19),
/** Brushland Labs. */
kBrushlandLabs(20);
/** The manufacturer ID. */
@SuppressWarnings("PMD.MemberName")
@SuppressWarnings("MemberName")
public final int id;
CANManufacturer(int id) {

View File

@@ -308,16 +308,19 @@ public class DriverStationJNI extends JNIWrapper {
public static native int getJoystickAxisType(byte joystickNum, byte axis);
/**
* Returns the approximate match time.
* Return the approximate match time. The FMS does not send an official match time to the robots,
* but does send an approximate match time. The value will count down the time remaining in the
* current period (auto or teleop). Warning: This is not an official time (so it cannot be used to
* dispute ref calls or guarantee that a function will trigger before the match ends).
*
* <p>The FMS does not send an official match time to the robots, but does send an approximate
* match time. The value will count down the time remaining in the current period (auto or
* teleop).
* <p>When connected to the real field, this number only changes in full integer increments, and
* always counts down.
*
* <p>Warning: This is not an official time (so it cannot be used to dispute ref calls or
* guarantee that a function will trigger before the match ends).
* <p>When the DS is in practice mode, this number is a floating point number, and counts down.
*
* <p>The Practice Match function of the DS approximates the behavior seen on the field.
* <p>When the DS is in teleop or autonomous mode, this number returns -1.0.
*
* <p>Simulation matches DS behavior without an FMS connected.
*
* @return time remaining in current match period (auto or teleop)
* @see "HAL_GetMatchTime"

View File

@@ -125,8 +125,8 @@ public class PowerDistributionFaults {
case 21 -> Channel21BreakerFault;
case 22 -> Channel22BreakerFault;
case 23 -> Channel23BreakerFault;
default -> throw new IndexOutOfBoundsException(
"Power distribution fault channel out of bounds!");
default ->
throw new IndexOutOfBoundsException("Power distribution fault channel out of bounds!");
};
}

View File

@@ -134,8 +134,8 @@ public class PowerDistributionStickyFaults {
case 21 -> Channel21BreakerFault;
case 22 -> Channel22BreakerFault;
case 23 -> Channel23BreakerFault;
default -> throw new IndexOutOfBoundsException(
"Power distribution fault channel out of bounds!");
default ->
throw new IndexOutOfBoundsException("Power distribution fault channel out of bounds!");
};
}

View File

@@ -32,15 +32,14 @@ public final class CANExceptionFactory {
case NIRioStatus.kRioStatusSuccess -> {
// Everything is ok... don't throw.
}
case ERR_CANSessionMux_InvalidBuffer,
NIRioStatus.kRIOStatusBufferInvalidSize -> throw new CANInvalidBufferException();
case ERR_CANSessionMux_MessageNotFound,
NIRioStatus.kRIOStatusOperationTimedOut -> throw new CANMessageNotFoundException();
case ERR_CANSessionMux_NotAllowed,
NIRioStatus.kRIOStatusFeatureNotSupported -> throw new CANMessageNotAllowedException(
"MessageID = " + messageID);
case ERR_CANSessionMux_NotInitialized,
NIRioStatus.kRIOStatusResourceNotInitialized -> throw new CANNotInitializedException();
case ERR_CANSessionMux_InvalidBuffer, NIRioStatus.kRIOStatusBufferInvalidSize ->
throw new CANInvalidBufferException();
case ERR_CANSessionMux_MessageNotFound, NIRioStatus.kRIOStatusOperationTimedOut ->
throw new CANMessageNotFoundException();
case ERR_CANSessionMux_NotAllowed, NIRioStatus.kRIOStatusFeatureNotSupported ->
throw new CANMessageNotAllowedException("MessageID = " + messageID);
case ERR_CANSessionMux_NotInitialized, NIRioStatus.kRIOStatusResourceNotInitialized ->
throw new CANNotInitializedException();
default -> throw new UncleanStatusException("Fatal status code detected: " + status);
}
}

Some files were not shown because too many files have changed in this diff Show More