Compare commits

...

60 Commits

Author SHA1 Message Date
Peter Johnson
80c391e182 [wpinet] WebServer: Unescape URI (#7552)
Also provide Content-Disposition filename header in response.

This fixes e.g. filenames with spaces in them.
2024-12-15 12:28:08 -08:00
Joseph Eng
70f36cce7e [commands] Extract common trigger binding logic (#7550)
This makes the logic clearer in the actual binding methods and will hopefully make it less annoying to make changes such as allowing control over initial edges.

Also changes Java to use previous and current to match C++.
2024-12-14 23:13:41 -08:00
Peter Johnson
564c1f2de2 [wpinet] WebServer: Fix Windows (#7551)
The order of evaluation of parameters is not defined; on Windows,
the std::move was executed before the GetBuffer().
2024-12-14 23:07:48 -08:00
Peter Johnson
a1b642a402 [wpinet] Add simple web server (#7527)
Also add EscapeHTML to HttpUtil.
2024-12-14 11:51:21 -08:00
Falon
f9b3efb712 [wpiunits] Refactor MomentOfInertiaUnit constructor to use MomentOfInertiaUnit instead of a Per<> (#7546) 2024-12-14 11:26:26 -08:00
Thad House
782459dff4 [wpiutil] Make runtime loader exception message slightly better (#7536) 2024-12-14 11:25:27 -08:00
Peter Johnson
4bca79b9af [wpimath] Revert "ChassisSpeeds fromRelative and discretize methods made instance methods (#7115)" (#7549)
This reverts commit a3b12b3bd9.
2024-12-14 10:42:49 -08:00
Daniel Chen
68285dae77 [commands] Add withDeadline modifier (#7299)
Co-authored-by: Ryan Blue <ryanzblue@gmail.com>
2024-12-13 18:30:02 -07:00
Daniel Chen
5e3dba672a [wpiutil] Record/Enum struct generation fix (#7538)
ProceduralStructGenerator's genRecord and genEnum were package-private, and only extractClassStruct was made public.
However, this package private visibility rendered them unable to be used by the rest of wpilib(and advanced users).

Here, ProceduralStructGenerator is split into 2 classes: StructGenerator(which generates structs) and StructFetcher(the new namespace for extractClassStruct). In addition, genRecord and genEnum have been made public methods.
2024-12-13 12:25:38 -07:00
Ryan Blue
e943424609 [build] Update shadow plugin (#7540) 2024-12-12 19:19:14 -08:00
Joseph Eng
4225b732fd Remove unnecessary boxing (#7539)
* Remove unnecessary boxing
Also remove unnecessary warning suppression

* Use more idiomatic functional interfaces in NumericalIntegration
2024-12-12 19:18:40 -08:00
Carl Hauser
39d05ebe7c [rtns] Capture exitCode from ssh_channel_get_exit_status (#7541) 2024-12-12 19:18:02 -08:00
Wispy
cc41a0ed24 [wpilib] Replace MecanumDriveMotorVoltages with a functional interface (#6760) 2024-12-08 17:05:06 -08:00
Tyler Veness
d5edb4060d [upstream_utils] Upgrade Sleipnir (#7512)
It now uses SQP for problems without inequality constraints, which is
faster.

main:
```
[ RUN      ] Ellipse2dTest.DistanceToPoint
0.203 ms
[       OK ] Ellipse2dTest.DistanceToPoint (0 ms)
[ RUN      ] Ellipse2dTest.FindNearestPoint
0.019 ms
[       OK ] Ellipse2dTest.FindNearestPoint (0 ms)
```

upgrade:
```
[ RUN      ] Ellipse2dTest.DistanceToPoint
0.197 ms
[       OK ] Ellipse2dTest.DistanceToPoint (0 ms)
[ RUN      ] Ellipse2dTest.FindNearestPoint
0.015 ms
[       OK ] Ellipse2dTest.FindNearestPoint (0 ms)
```
2024-12-07 23:02:39 -08:00
Tyler Veness
e08fdeba21 [wpimath] Rename FindNearestPoint() to Nearest() (#7513)
This is easier to type and follows the naming of Pose2d::Nearest().

Since Ellipse2d and Rectangle2d were added for the 2025 season, we don't
need to add deprecation notices.
2024-12-07 23:01:18 -08:00
Wispy
544553a58f [developerRobot] Add Epilogue and wpiunits to developerRobot (#7510) 2024-12-07 23:00:49 -08:00
Peter Johnson
838c5fbcd7 [ci] Fix labeler yaml (#7523) 2024-12-07 21:37:49 -08:00
Peter Johnson
38a239b531 [ci] Add labeler action to auto-label PRs (#7520) 2024-12-07 21:15:49 -08:00
Thad House
9a1b4245fa [build] Restore Windows constexpr mutex define on wpiutil (#7515) 2024-12-07 18:55:06 -08:00
Tyler Veness
e222efaa01 [wpimath] Add affine transformation constructors and getters to geometry API (#7430)
Fixes #7429.
2024-12-07 15:49:17 -08:00
shueja
f772bb141d [epilogue] Add extra parentheses around cast when using varhandle (#7428)
* [epilogue] Add extra parentheses around cast when using varhandle

* Fixed tests and added one for private suppliers

* Fix tests

* Formatting fixes

* Update epilogue-processor/src/test/java/edu/wpi/first/epilogue/processor/AnnotationProcessorTest.java

Co-authored-by: Gold856 <117957790+Gold856@users.noreply.github.com>

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Gold856 <117957790+Gold856@users.noreply.github.com>
Co-authored-by: Sam Carlberg <sam@slfc.dev>
2024-12-07 16:09:35 -05:00
Tyler Veness
278efa6384 [upstream_utils] Remove Sleipnir patches no longer needed with GCC 11 (#7491) 2024-12-07 12:36:25 -08:00
Tyler Veness
e876452967 [ci] Upgrade to wpiformat 2024.50 (#7506) 2024-12-07 11:40:24 -08:00
Sam Carlberg
5c95966d44 Fix epilogue loading optional types from the newcommands vendordep (#7505) 2024-12-07 13:56:05 -05:00
Tyler Veness
882233bede [wpimath] Improve SimpleMotorFeedforward argument names and deprecation message (#7489)
Also roll back SimpleMotorFeedforward unit API until 2027.
2024-12-06 22:32:40 -08:00
Peter Johnson
1921d019b7 [build] Update arm builds to bookworm (#7501)
Also bumps native-utils to 2025.9.0.
And upgrades OpenCV to -3; fixes some enum conversion deprecation warnings.
2024-12-06 22:19:00 -08:00
Peter Johnson
c3fc7c829d [build] Upgrade OpenCV to 4.10.0 (#7500) 2024-12-06 15:41:40 -08:00
Ryan Blue
4ce8930342 [developerRobot] Add instructions for developerRobot java sim (NFC) (#7498) 2024-12-06 00:24:26 -08:00
Daniel Chen
60198c0bec [epilogue] Improved opt-in logging support (#7437) 2024-12-03 20:30:55 -08:00
Gold856
54f0e11fc0 [build] Update OpenCV to 2025-4.8.0-2 (#7476) 2024-12-03 20:30:03 -08:00
sciencewhiz
de82ed434d [hal] Add usage reporting for TOF sensors (#7470) 2024-12-01 17:02:20 -08:00
Thad House
4dd3a36d2e [ci] Switch lint task to use base ubuntu image (#7471) 2024-12-01 17:01:19 -08:00
sciencewhiz
92f8c89267 [hal] Add usage reporting for 2025 legal motor controllers (#7467) 2024-11-30 22:25:04 -08:00
Thad House
1eecaaf337 [build] Update OpenCV to 2025 (#7468) 2024-11-30 22:21:41 -08:00
sciencewhiz
892e062316 [hal,wpilib,wpimath] Add Usage Reporting for Choreo and PathWeaver (#7464) 2024-11-30 20:33:09 -08:00
sciencewhiz
9807d60566 [hal,wpilib] Change Power Distribution usage reporting to Instances (#7465)
LabVIEW doesn't appear to report PDP. This should reduce the number of
Unknowns in the parsed UsageReporting.
2024-11-30 20:32:21 -08:00
Thad House
c387a7ecae [build] Update to 2025 compilers (#7462) 2024-11-30 10:07:53 -08:00
Peter Johnson
715cbb6b76 [sim] GUI: Update FMS widget when real DS is connected (#7456) 2024-11-30 09:49:09 -08:00
Ryan Blue
9d40b993f8 [wpiutil] Fix HasNestedStruct docs (NFC) (#7459) 2024-11-30 07:44:19 -08:00
Sam Carlberg
92ee5bc523 [epilogue] Add usage reporting (#7461) 2024-11-30 09:50:34 -05:00
Peter Johnson
5012ad7499 [epilogue] Fix missed EpilogueBackend renames (#7458) 2024-11-30 00:34:41 -08:00
Peter Johnson
145450b73d [ci] Disable processing of 2027 tags (#7457) 2024-11-29 23:51:27 -08:00
Nicholas Armstrong
b91864a5ec [wpilib] Fix acceleration getter for DCMotorSim (#7449) 2024-11-29 22:15:00 -08:00
sciencewhiz
0941251375 [wpilib] Add usage reporting for loggers (#7450) 2024-11-29 22:13:31 -08:00
Sam Carlberg
7d178615fa [epilogue] Allow custom loggers for generic types (#7452)
Support custom loggers for generic types
Improve error messaging for custom loggers with generic type arguments
Consistently start all epilogue processor prints with "[EPILOGUE]"
2024-11-29 22:11:46 -08:00
Sam Carlberg
806d56e564 [epilogue] Rename DataLogger to EpilogueBackend for clarity (#7453)
Update documentation and internal names correspondingly
2024-11-29 22:10:51 -08:00
Tyler Veness
65f3345407 [upstream_utils] Suppress protobuf warnings on GCC 12 too (#7451) 2024-11-29 18:19:59 -08:00
Sam Carlberg
5e1c6a84ce [wpilibj, wpilibc] Fix LED patterns not offsetting reads (#6948)
Was causing bugs when combined with patterns that need to read back from the buffer (eg masks and overlays)

Co-authored-by: Joseph Eng <s-engjo@bsd405.org>
2024-11-28 21:25:54 -08:00
Tyler Veness
a0af0fd572 [wpimath] Remove redundant internal DARE function (#7442) 2024-11-28 21:24:13 -08:00
Kavin Muralikrishnan
f377a9c573 [examples] Fix SysId example references to shooter subsystem (#7392) 2024-11-27 23:04:53 -08:00
Peter Johnson
62338c7287 [sim] Fix DS GUI System Joysticks window auto-hiding (#7431) 2024-11-27 23:03:55 -08:00
sciencewhiz
49e3e4a0be [wpiunits] Fix deprecation javadoc for units negate (#7436)
deprecated javadoc tags aren't inherited
2024-11-27 23:03:23 -08:00
Joseph Eng
59dbfc9c2d [wpimath] Improve C++ SimpleMotorFeedforward unit type support (#7440)
Allow using non-base types
Allow using angles for serde
2024-11-27 23:02:31 -08:00
Sam Carlberg
9607c6c10d [epilogue] Fix epilogue generating incorrect packages for inner classes (#7439) 2024-11-27 23:02:00 -08:00
sciencewhiz
6ef5b85758 [wpiunits] Restore and deprecate divide (#7438)
It was changed to div in #7387, but 2024 used divide.
2024-11-27 23:01:26 -08:00
Peter Johnson
b6de7acbdb [sim] GUI: Don't show Window menu if it has no contents (#7432) 2024-11-25 17:25:55 -08:00
Thad House
fe28fa1ded [wpilibj] Fix ADIS16470 Gyro (#7434) 2024-11-25 17:23:32 -08:00
Tyler Veness
b7eb9fb8f9 [upstream_utils] Add std::is_debugger_present() shim (#7423) 2024-11-22 09:17:23 -08:00
oh-yes-0-fps
1d58c5025e [wpilibj] Add procedural struct generator for enums and records (#7149) 2024-11-21 20:48:11 -08:00
Thad House
b4a8d33486 [ntcoreffi] Use static runtime for ntcoreffi (#7422)
This avoids requiring users of this library to keep up to date with the latest MSVC runtimes.
2024-11-21 16:14:12 -07:00
264 changed files with 14868 additions and 3399 deletions

56
.github/labeler.yml vendored Normal file
View File

@@ -0,0 +1,56 @@
'2027':
- base-branch: '2027'
'component: apriltag':
- changed-files:
- any-glob-to-any-file: apriltag/**
'component: command-based':
- changed-files:
- any-glob-to-any-file: wpilibNewCommands/**
'component: cscore':
- changed-files:
- any-glob-to-any-file: cscore/**
'component: datalogtool':
- changed-files:
- any-glob-to-any-file: datalogtool/**
'component: epilogue':
- changed-files:
- any-glob-to-any-file: epilogue-*/**
'component: examples':
- changed-files:
- any-glob-to-any-file: wpilib*Examples/**
'component: glass':
- changed-files:
- any-glob-to-any-file: glass/**
'component: hal':
- changed-files:
- any-glob-to-any-file: hal/**
'component: ntcore':
- changed-files:
- any-glob-to-any-file: ntcore/**
'component: outlineviewer':
- changed-files:
- any-glob-to-any-file: outlineviewer/**
'component: sysid':
- changed-files:
- any-glob-to-any-file: sysid/**
'component: teamnumbersetter':
- changed-files:
- any-glob-to-any-file: roborioteamnumbersetter/**
'component: wpilibc':
- changed-files:
- any-glob-to-any-file: wpilibc/**
'component: wpilibj':
- changed-files:
- any-glob-to-any-file: wpilibj/**
'component: wpimath':
- changed-files:
- any-glob-to-any-file: wpimath/**
'component: wpinet':
- changed-files:
- any-glob-to-any-file: wpinet/**
'component: wpiunits':
- changed-files:
- any-glob-to-any-file: wpiunits/**
'component: wpiutil':
- changed-files:
- any-glob-to-any-file: wpiutil/**

View File

@@ -18,7 +18,7 @@ jobs:
include:
- os: ubuntu-22.04
name: Linux
container: wpilib/roborio-cross-ubuntu:2024-22.04
container: wpilib/roborio-cross-ubuntu:2025-22.04
flags: "--preset with-java-and-sccache -DCMAKE_BUILD_TYPE=Release -DWITH_EXAMPLES=ON"
- os: macOS-14
name: macOS

View File

@@ -43,7 +43,7 @@ jobs:
distribution: 'temurin'
java-version: 17
- name: Install wpiformat
run: pip3 install wpiformat==2024.45
run: pip3 install wpiformat==2024.50
- name: Run wpiformat
run: wpiformat
- name: Run spotlessApply

View File

@@ -13,7 +13,7 @@ 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'))
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
@@ -32,12 +32,12 @@ jobs:
run: |
echo "EXTRA_GRADLE_ARGS=-PreleaseMode" >> $GITHUB_ENV
echo "BRANCH=beta" >> $GITHUB_ENV
if: startsWith(github.ref, 'refs/tags/v')
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')
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 🔑

View File

@@ -12,13 +12,13 @@ jobs:
fail-fast: false
matrix:
include:
- container: wpilib/roborio-cross-ubuntu:2024-22.04
- container: wpilib/roborio-cross-ubuntu:2025-22.04
artifact-name: Athena
build-options: "-Ponlylinuxathena"
- container: wpilib/raspbian-cross-ubuntu:bullseye-22.04
- container: wpilib/raspbian-cross-ubuntu:bookworm-22.04
artifact-name: Arm32
build-options: "-Ponlylinuxarm32"
- container: wpilib/aarch64-cross-ubuntu:bullseye-22.04
- container: wpilib/aarch64-cross-ubuntu:bookworm-22.04
artifact-name: Arm64
build-options: "-Ponlylinuxarm64"
- container: wpilib/ubuntu-base:22.04
@@ -42,7 +42,7 @@ jobs:
fetch-depth: 0
- name: Set release environment variable
run: echo "EXTRA_GRADLE_ARGS=-PreleaseMode" >> $GITHUB_ENV
if: startsWith(github.ref, 'refs/tags/v')
if: startsWith(github.ref, 'refs/tags/v') && !contains(github.ref, '2027')
- name: Build with Gradle
uses: addnab/docker-run-action@v3
with:
@@ -96,9 +96,16 @@ jobs:
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 }}
@@ -119,16 +126,16 @@ jobs:
keychain-password: ${{ secrets.APPLE_KEYCHAIN_PASSWORD }}
if: |
matrix.artifact-name == 'macOS' && (github.repository == 'wpilibsuite/allwpilib' &&
(github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v')))
(github.ref == 'refs/heads/main' || (startsWith(github.ref, 'refs/tags/v') && !contains(github.ref, '2027'))))
- name: Set Keychain Lock Timeout
run: security set-keychain-settings -lut 21600
if: |
matrix.artifact-name == 'macOS' && (github.repository == 'wpilibsuite/allwpilib' &&
(github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v')))
(github.ref == 'refs/heads/main' || (startsWith(github.ref, 'refs/tags/v') && !contains(github.ref, '2027'))))
- name: Set release environment variable
run: echo "EXTRA_GRADLE_ARGS=-PreleaseMode" >> $GITHUB_ENV
shell: bash
if: startsWith(github.ref, 'refs/tags/v')
if: startsWith(github.ref, 'refs/tags/v') && !contains(github.ref, '2027')
- name: Set Java Heap Size
run: sed -i 's/-Xmx2g/-Xmx1g/g' gradle.properties
if: matrix.artifact-name == 'Win32'
@@ -159,7 +166,7 @@ jobs:
run: ./gradlew copyAllOutputs --build-cache -PbuildServer -PskipJavaFormat -PdeveloperID=${{ secrets.APPLE_DEVELOPER_ID }} ${{ matrix.build-options }} ${{ env.EXTRA_GRADLE_ARGS }}
if: |
matrix.artifact-name == 'macOS' && (github.repository == 'wpilibsuite/allwpilib' &&
(github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v')))
(github.ref == 'refs/heads/main' || (startsWith(github.ref, 'refs/tags/v') && !contains(github.ref, '2027'))))
- name: Check disk free space (Windows)
run: wmic logicaldisk get caption, freespace
if: matrix.os == 'windows-2022'
@@ -184,7 +191,7 @@ jobs:
java-version: 17
- name: Set release environment variable
run: echo "EXTRA_GRADLE_ARGS=-PreleaseMode" >> $GITHUB_ENV
if: startsWith(github.ref, 'refs/tags/v')
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 }}
env:
@@ -203,7 +210,7 @@ jobs:
- name: Free Disk Space
if: |
github.repository == 'wpilibsuite/allwpilib' &&
(github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v'))
(github.ref == 'refs/heads/main' || (startsWith(github.ref, 'refs/tags/v') && !contains(github.ref, '2027')))
uses: jlumbroso/free-disk-space@main
with:
tool-cache: false
@@ -216,31 +223,31 @@ jobs:
- uses: actions/checkout@v4
if: |
github.repository == 'wpilibsuite/allwpilib' &&
(github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v'))
(github.ref == 'refs/heads/main' || (startsWith(github.ref, 'refs/tags/v') && !contains(github.ref, '2027')))
with:
repository: wpilibsuite/build-tools
- uses: actions/download-artifact@v4
if: |
github.repository == 'wpilibsuite/allwpilib' &&
(github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v'))
(github.ref == 'refs/heads/main' || (startsWith(github.ref, 'refs/tags/v') && !contains(github.ref, '2027')))
with:
path: combiner/products/build/allOutputs
- name: Flatten Artifacts
if: |
github.repository == 'wpilibsuite/allwpilib' &&
(github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v'))
(github.ref == 'refs/heads/main' || (startsWith(github.ref, 'refs/tags/v') && !contains(github.ref, '2027')))
run: rsync -a --delete combiner/products/build/allOutputs/*/* combiner/products/build/allOutputs/
- name: Check version number exists
if: |
github.repository == 'wpilibsuite/allwpilib' &&
(github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v'))
(github.ref == 'refs/heads/main' || (startsWith(github.ref, 'refs/tags/v') && !contains(github.ref, '2027')))
run: |
cat combiner/products/build/allOutputs/version.txt
test -s combiner/products/build/allOutputs/version.txt
- uses: actions/setup-java@v4
if: |
github.repository == 'wpilibsuite/allwpilib' &&
(github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v'))
(github.ref == 'refs/heads/main' || (startsWith(github.ref, 'refs/tags/v') && !contains(github.ref, '2027')))
with:
distribution: 'temurin'
java-version: 17
@@ -256,7 +263,7 @@ jobs:
- name: Combine (Release)
if: |
github.repository == 'wpilibsuite/allwpilib' &&
startsWith(github.ref, 'refs/tags/v')
startsWith(github.ref, 'refs/tags/v') && !contains(github.ref, '2027')
run: cd combiner && ./gradlew publish -Pallwpilib -PreleaseRepoPublish
env:
RUN_AZURE_ARTIFACTORY_RELEASE: "TRUE"
@@ -265,7 +272,7 @@ jobs:
- uses: actions/upload-artifact@v4
if: |
github.repository == 'wpilibsuite/allwpilib' &&
(github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v'))
(github.ref == 'refs/heads/main' || (startsWith(github.ref, 'refs/tags/v') && !contains(github.ref, '2027')))
with:
name: Maven
path: ~/releases
@@ -281,7 +288,7 @@ jobs:
- uses: peter-evans/repository-dispatch@v3
if: |
github.repository == 'wpilibsuite/allwpilib' &&
startsWith(github.ref, 'refs/tags/v')
startsWith(github.ref, 'refs/tags/v') && !contains(github.ref, '2027')
with:
token: ${{ secrets.TOOL_REPO_ACCESS_TOKEN }}
repository: wpilibsuite/${{ matrix.repo }}

12
.github/workflows/labeler.yml vendored Normal file
View File

@@ -0,0 +1,12 @@
name: "Pull Request Labeler"
on:
- pull_request_target
jobs:
labeler:
permissions:
contents: read
pull-requests: write
runs-on: ubuntu-latest
steps:
- uses: actions/labeler@v5

View File

@@ -27,7 +27,7 @@ jobs:
with:
python-version: '3.12'
- name: Install wpiformat
run: pip3 install wpiformat==2024.45
run: pip3 install wpiformat==2024.50
- name: Run
run: wpiformat
- name: Check output
@@ -51,7 +51,7 @@ jobs:
tidy:
name: "clang-tidy"
runs-on: ubuntu-22.04
container: wpilib/roborio-cross-ubuntu:2024-22.04
container: wpilib/ubuntu-base:22.04
steps:
- uses: actions/checkout@v4
with:
@@ -66,7 +66,7 @@ jobs:
with:
python-version: '3.12'
- name: Install wpiformat
run: pip3 install wpiformat==2024.45
run: pip3 install wpiformat==2024.50
- name: Create compile_commands.json
run: |
./gradlew generateCompileCommands -Ptoolchain-optional-roboRio

View File

@@ -30,7 +30,7 @@ jobs:
ctest-flags: ""
name: "${{ matrix.name }}"
runs-on: ubuntu-22.04
container: wpilib/roborio-cross-ubuntu:2024-22.04
container: wpilib/roborio-cross-ubuntu:2025-22.04
steps:
- name: Install Dependencies
run: sudo apt-get update && sudo apt-get install -y libopencv-dev libopencv4.5-java clang-14 libprotobuf-dev protobuf-compiler ninja-build

View File

@@ -16,13 +16,13 @@ jobs:
fail-fast: false
matrix:
include:
- container: wpilib/roborio-cross-ubuntu:2024-22.04
- container: wpilib/roborio-cross-ubuntu:2025-22.04
artifact-name: Athena
build-options: "-Ponlylinuxathena"
- container: wpilib/raspbian-cross-ubuntu:bullseye-22.04
- container: wpilib/raspbian-cross-ubuntu:bookworm-22.04
artifact-name: Arm32
build-options: "-Ponlylinuxarm32"
- container: wpilib/aarch64-cross-ubuntu:bullseye-22.04
- container: wpilib/aarch64-cross-ubuntu:bookworm-22.04
artifact-name: Arm64
build-options: "-Ponlylinuxarm64"
- container: wpilib/ubuntu-base:22.04

View File

@@ -32,7 +32,7 @@ jobs:
- name: Build WPILib with Gradle
uses: addnab/docker-run-action@v3
with:
image: wpilib/roborio-cross-ubuntu:2024-22.04
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 :fieldImages: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

View File

@@ -43,6 +43,7 @@ Portable File Dialogs wpigui/src/main/native/include/portable-file-dialogs.h
V8 export-template wpiutil/src/main/native/include/wpi/SymbolExports.h
GCEM wpimath/src/main/native/thirdparty/gcem/include/
Sleipnir wpimath/src/main/native/thirdparty/sleipnir
Debugging wpiutil/src/main/native/thirdparty/debugging
==============================================================================
Google Test License
@@ -1224,3 +1225,29 @@ Redistribution and use in source and binary forms, with or without modification,
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
=================
Debugging License
=================
MIT License
Copyright (c) 2021-2022 René Ferdinand Rivera Morell
Copyright (c) 2018 Isabella Muerte
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -18,7 +18,7 @@ plugins {
id 'idea'
id 'visual-studio'
id 'net.ltgt.errorprone' version '3.1.0' apply false
id 'com.github.johnrengelman.shadow' version '8.1.1' 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
}

View File

@@ -9,5 +9,5 @@ repositories {
}
}
dependencies {
implementation "edu.wpi.first:native-utils:2025.3.0"
implementation "edu.wpi.first:native-utils:2025.9.0"
}

View File

@@ -22,7 +22,7 @@ application {
mainClass = 'edu.wpi.Main'
}
apply plugin: 'com.github.johnrengelman.shadow'
apply plugin: 'com.gradleup.shadow'
repositories {
maven {

View File

@@ -9,7 +9,12 @@ This command builds everything.
## Simulation
This command runs the C++ subproject on desktop.
This command runs the Java project on desktop.
```bash
./gradlew developerRobot:run
```
This command runs the C++ project on desktop.
```bash
./gradlew developerRobot:runCpp
```

View File

@@ -35,7 +35,7 @@ application {
mainClass = 'frc.robot.Main'
}
apply plugin: 'com.github.johnrengelman.shadow'
apply plugin: 'com.gradleup.shadow'
repositories {
maven {
@@ -54,6 +54,9 @@ dependencies {
implementation project(':cameraserver')
implementation project(':wpilibNewCommands')
implementation project(':apriltag')
implementation project(':wpiunits')
implementation project(':epilogue-runtime')
annotationProcessor project(':epilogue-processor')
}
tasks.withType(com.github.spotbugs.snom.SpotBugsTask).configureEach {

View File

@@ -11,9 +11,12 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
@@ -86,19 +89,19 @@ public class AnnotationProcessor extends AbstractProcessor {
.getMessager()
.printMessage(
Diagnostic.Kind.ERROR,
"Custom logger classes should have a @CustomLoggerFor annotation",
"[EPILOGUE] Custom logger classes should have a @CustomLoggerFor annotation",
e);
});
var loggedTypes = getLoggedTypes(roundEnv);
// Handlers are declared in order of priority. If an element could be logged in more than one
// way (eg a class implements both Sendable and StructSerializable), the order of the handlers
// in this list will determine how it gets logged.
m_handlers =
List.of(
new LoggableHandler(
processingEnv,
roundEnv.getElementsAnnotatedWith(
Logged.class)), // prioritize epilogue logging over Sendable
processingEnv, loggedTypes), // prioritize epilogue logging over Sendable
new ConfiguredLoggerHandler(
processingEnv, customLoggers), // then customized logging configs
new ArrayHandler(processingEnv),
@@ -118,12 +121,39 @@ public class AnnotationProcessor extends AbstractProcessor {
.findAny()
.ifPresent(
epilogue -> {
processEpilogue(roundEnv, epilogue);
processEpilogue(roundEnv, epilogue, loggedTypes);
});
return false;
}
/**
* Gets the set of all loggable types in the compilation unit. A type is considered loggable if it
* is directly annotated with {@code @Logged} or contains a field or method with a {@code @Logged}
* annotation.
*
* @param roundEnv the compilation round environment
* @return the set of all loggable types
*/
private Set<TypeElement> getLoggedTypes(RoundEnvironment roundEnv) {
// Fetches everything annotated with @Logged; classes, methods, values, etc.
var annotatedElements = roundEnv.getElementsAnnotatedWith(Logged.class);
return Stream.concat(
// 1. All type elements (classes, interfaces, or enums) with the @Logged annotation
annotatedElements.stream()
.filter(e -> e instanceof TypeElement)
.map(e -> (TypeElement) e),
// 2. All type elements containing a field or method with the @Logged annotation
annotatedElements.stream()
.filter(e -> e instanceof VariableElement || e instanceof ExecutableElement)
.map(Element::getEnclosingElement)
.filter(e -> e instanceof TypeElement)
.map(e -> (TypeElement) e))
.sorted(Comparator.comparing(e -> e.getSimpleName().toString()))
.collect(
Collectors.toCollection(LinkedHashSet::new)); // Collect to a set to avoid duplicates
}
private boolean validateFields(Set<? extends Element> annotatedElements) {
var fields =
annotatedElements.stream()
@@ -284,7 +314,7 @@ public class AnnotationProcessor extends AbstractProcessor {
.getMessager()
.printMessage(
Diagnostic.Kind.ERROR,
"Logger classes must have a public no-argument constructor",
"[EPILOGUE] Logger classes must have a public no-argument constructor",
annotatedElement);
continue;
}
@@ -306,7 +336,17 @@ public class AnnotationProcessor extends AbstractProcessor {
.getMessager()
.printMessage(
Diagnostic.Kind.ERROR,
"Multiple custom loggers detected for type " + targetType,
"[EPILOGUE] Multiple custom loggers detected for type " + targetType,
annotatedElement);
continue;
}
if (annotatedElement instanceof TypeElement t && !t.getTypeParameters().isEmpty()) {
processingEnv
.getMessager()
.printMessage(
Diagnostic.Kind.ERROR,
"[EPILOGUE] Custom logger classes cannot take generic type arguments",
annotatedElement);
continue;
}
@@ -318,7 +358,7 @@ public class AnnotationProcessor extends AbstractProcessor {
.getMessager()
.printMessage(
Diagnostic.Kind.ERROR,
"Not a subclass of ClassSpecificLogger<" + targetType + ">",
"[EPILOGUE] Not a subclass of ClassSpecificLogger<" + targetType + ">",
annotatedElement);
continue;
}
@@ -330,7 +370,8 @@ public class AnnotationProcessor extends AbstractProcessor {
return customLoggers;
}
private void processEpilogue(RoundEnvironment roundEnv, TypeElement epilogueAnnotation) {
private void processEpilogue(
RoundEnvironment roundEnv, TypeElement epilogueAnnotation, Set<TypeElement> loggedTypes) {
var annotatedElements = roundEnv.getElementsAnnotatedWith(epilogueAnnotation);
List<String> loggerClassNames = new ArrayList<>();
@@ -348,12 +389,7 @@ public class AnnotationProcessor extends AbstractProcessor {
return;
}
var classes =
annotatedElements.stream()
.filter(e -> e instanceof TypeElement)
.map(e -> (TypeElement) e)
.toList();
for (TypeElement clazz : classes) {
for (TypeElement clazz : loggedTypes) {
try {
warnOfNonLoggableElements(clazz);
m_loggerGenerator.writeLoggerFile(clazz);
@@ -368,7 +404,7 @@ public class AnnotationProcessor extends AbstractProcessor {
.getMessager()
.printMessage(
Diagnostic.Kind.ERROR,
"Could not write logger file for " + clazz.getQualifiedName(),
"[EPILOGUE] Could not write logger file for " + clazz.getQualifiedName(),
clazz);
e.printStackTrace(System.err);
}
@@ -381,7 +417,7 @@ public class AnnotationProcessor extends AbstractProcessor {
private void warnOfNonLoggableElements(TypeElement clazz) {
var config = clazz.getAnnotation(Logged.class);
if (config.strategy() == Logged.Strategy.OPT_IN) {
if (config == null || config.strategy() == Logged.Strategy.OPT_IN) {
// field and method validations will have already checked everything
return;
}

View File

@@ -60,7 +60,7 @@ public class ArrayHandler extends ElementHandler {
if (m_structHandler.isLoggableType(componentType)) {
// Struct arrays need to pass in the struct serializer
return "dataLogger.log(\""
return "backend.log(\""
+ loggedName(element)
+ "\", "
+ elementAccess(element)
@@ -69,7 +69,7 @@ public class ArrayHandler extends ElementHandler {
+ ")";
} else {
// Primitive or string array
return "dataLogger.log(\"" + loggedName(element) + "\", " + elementAccess(element) + ")";
return "backend.log(\"" + loggedName(element) + "\", " + elementAccess(element) + ")";
}
}
}

View File

@@ -43,7 +43,7 @@ public class CollectionHandler extends ElementHandler {
var componentType = ((DeclaredType) dataType).getTypeArguments().get(0);
if (m_structHandler.isLoggableType(componentType)) {
return "dataLogger.log(\""
return "backend.log(\""
+ loggedName(element)
+ "\", "
+ elementAccess(element)
@@ -51,7 +51,7 @@ public class CollectionHandler extends ElementHandler {
+ m_structHandler.structAccess(componentType)
+ ")";
} else {
return "dataLogger.log(\"" + loggedName(element) + "\", " + elementAccess(element) + ")";
return "backend.log(\"" + loggedName(element) + "\", " + elementAccess(element) + ")";
}
}
}

View File

@@ -22,17 +22,26 @@ public class ConfiguredLoggerHandler extends ElementHandler {
@Override
public boolean isLoggable(Element element) {
return m_customLoggers.containsKey(dataType(element));
return m_customLoggers.keySet().stream()
.anyMatch(m -> m_processingEnv.getTypeUtils().isAssignable(dataType(element), m));
}
@Override
public String logInvocation(Element element) {
var dataType = dataType(element);
var loggerType = m_customLoggers.get(dataType);
var loggerType =
m_customLoggers.entrySet().stream()
.filter(
e -> {
return m_processingEnv.getTypeUtils().isAssignable(dataType, e.getKey());
})
.findFirst()
.orElseThrow()
.getValue();
return "Epilogue."
+ StringUtils.lowerCamelCase(loggerType.asElement().getSimpleName())
+ ".tryUpdate(dataLogger.getSubLogger(\""
+ ".tryUpdate(backend.getNested(\""
+ loggedName(element)
+ "\"), "
+ elementAccess(element)

View File

@@ -6,7 +6,7 @@ package edu.wpi.first.epilogue.processor;
import edu.wpi.first.epilogue.Logged;
import edu.wpi.first.epilogue.logging.ClassSpecificLogger;
import edu.wpi.first.epilogue.logging.DataLogger;
import edu.wpi.first.epilogue.logging.EpilogueBackend;
import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
@@ -127,8 +127,10 @@ public abstract class ElementHandler {
private static String fieldAccess(VariableElement field) {
if (field.getModifiers().contains(Modifier.PRIVATE)) {
// (com.example.Foo) $fooField.get(object)
return "(" + field.asType() + ") $" + field.getSimpleName() + ".get(object)";
// ((com.example.Foo) $fooField.get(object))
// Extra parentheses so cast evaluates before appended methods
// (e.g. when appending .getAsDouble())
return "((" + field.asType() + ") $" + field.getSimpleName() + ".get(object))";
} else {
// object.fooField
return "object." + field.getSimpleName();
@@ -157,7 +159,7 @@ public abstract class ElementHandler {
/**
* Generates a code snippet to place in a generated logger file to log the value of a field or
* method. Log invocations are placed in a generated implementation of {@link
* ClassSpecificLogger#update(DataLogger, Object)}, with access to the data logger and logged
* ClassSpecificLogger#update(EpilogueBackend, Object)}, with access to the backend and logged
* object passed to the method call.
*
* @param element the field or method element to generate the logger call for

View File

@@ -28,6 +28,6 @@ public class EnumHandler extends ElementHandler {
@Override
public String logInvocation(Element element) {
return "dataLogger.log(\"" + loggedName(element) + "\", " + elementAccess(element) + ")";
return "backend.log(\"" + loggedName(element) + "\", " + elementAccess(element) + ")";
}
}

View File

@@ -55,6 +55,10 @@ public class EpilogueGenerator {
out.println("import static edu.wpi.first.units.Units.Seconds;");
out.println();
out.println("import edu.wpi.first.hal.FRCNetComm;");
out.println("import edu.wpi.first.hal.HAL;");
out.println();
loggerClassNames.stream()
.sorted()
.forEach(
@@ -80,6 +84,18 @@ public class EpilogueGenerator {
out.println();
out.println("public final class Epilogue {");
// Usage reporting
out.println(
"""
static {
HAL.report(
FRCNetComm.tResourceType.kResourceType_LoggingFramework,
FRCNetComm.tInstances.kLoggingFramework_Epilogue
);
}
""");
out.println(
" private static final EpilogueConfiguration config = new EpilogueConfiguration();");
out.println();
@@ -154,9 +170,9 @@ public class EpilogueGenerator {
out.println(
" "
+ StringUtils.loggerFieldName(mainRobotClass)
+ ".tryUpdate(config.dataLogger.getSubLogger(config.root), robot, config.errorHandler);");
+ ".tryUpdate(config.backend.getNested(config.root), robot, config.errorHandler);");
out.println(
" config.dataLogger.log(\"Epilogue/Stats/Last Run\", (System.nanoTime() - start) / 1e6);");
" config.backend.log(\"Epilogue/Stats/Last Run\", (System.nanoTime() - start) / 1e6);");
out.println(" }");
out.println();

View File

@@ -15,7 +15,6 @@ import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
@@ -35,10 +34,8 @@ public class LoggableHandler extends ElementHandler {
@Override
public boolean isLoggable(Element element) {
var dataType = dataType(element);
return dataType.getAnnotation(Logged.class) != null
|| dataType instanceof DeclaredType decl
&& decl.asElement().getAnnotation(Logged.class) != null;
return m_processingEnv.getTypeUtils().asElement(dataType(element)) instanceof TypeElement t
&& m_loggedTypes.contains(t);
}
@Override
@@ -161,7 +158,7 @@ public class LoggableHandler extends ElementHandler {
}
private String generateLoggerCall(Element element, TypeElement type, String elementReference) {
return ("Epilogue.%s.tryUpdate(dataLogger.getSubLogger(\"%s\"), %s, "
return ("Epilogue.%s.tryUpdate(backend.getNested(\"%s\"), %s, "
+ "Epilogue.getConfig().errorHandler)")
.formatted(StringUtils.loggerFieldName(type), loggedName(element), elementReference);
}

View File

@@ -15,7 +15,10 @@ import edu.wpi.first.epilogue.Logged;
import edu.wpi.first.epilogue.NotLogged;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.annotation.Annotation;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.List;
@@ -29,6 +32,7 @@ import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.Name;
import javax.lang.model.element.PackageElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.tools.Diagnostic;
@@ -39,6 +43,33 @@ public class LoggerGenerator {
LoggerGenerator::isBuiltInJavaMethod;
private final ProcessingEnvironment m_processingEnv;
private final List<ElementHandler> m_handlers;
private final Logged m_defaultConfig =
new Logged() {
@Override
public Class<? extends Annotation> annotationType() {
return Logged.class;
}
@Override
public String name() {
return "";
}
@Override
public Strategy strategy() {
return Strategy.OPT_IN;
}
@Override
public Importance importance() {
return Importance.DEBUG;
}
@Override
public Naming defaultNaming() {
return Naming.USE_CODE_NAME;
}
};
public LoggerGenerator(ProcessingEnvironment processingEnv, List<ElementHandler> handlers) {
this.m_processingEnv = processingEnv;
@@ -73,6 +104,9 @@ public class LoggerGenerator {
*/
public void writeLoggerFile(TypeElement clazz) throws IOException {
var config = clazz.getAnnotation(Logged.class);
if (config == null) {
config = m_defaultConfig;
}
boolean requireExplicitOptIn = config.strategy() == Logged.Strategy.OPT_IN;
Predicate<Element> notSkipped = LoggerGenerator::isNotSkipped;
@@ -150,36 +184,28 @@ public class LoggerGenerator {
}
});
writeLoggerFile(clazz.getQualifiedName().toString(), config, fieldsToLog, methodsToLog);
writeLoggerFile(clazz, config, fieldsToLog, methodsToLog);
}
private void writeLoggerFile(
String className,
TypeElement clazz,
Logged classConfig,
List<VariableElement> loggableFields,
List<ExecutableElement> loggableMethods)
throws IOException {
String packageName = null;
int lastDot = className.lastIndexOf('.');
if (lastDot > 0) {
packageName = className.substring(0, lastDot);
// Walk nesting levels, to support inner classes
Deque<String> nesting = new ArrayDeque<>();
Element enclosing = clazz.getEnclosingElement();
while (!(enclosing instanceof PackageElement p)) {
nesting.addFirst(enclosing.getSimpleName().toString());
enclosing = enclosing.getEnclosingElement();
}
String packageName = p.getQualifiedName().toString();
nesting.addLast(clazz.getSimpleName().toString());
String simpleClassName = String.join(".", nesting);
String simpleClassName = StringUtils.simpleName(className);
String loggerClassName = className + "Logger";
String loggerSimpleClassName = loggerClassName.substring(lastDot + 1);
// Use the name on the class config to set the generated logger names
// This helps to avoid naming conflicts
if (!classConfig.name().isBlank()) {
loggerSimpleClassName =
StringUtils.capitalize(classConfig.name().replaceAll(" ", "")) + "Logger";
if (lastDot > 0) {
loggerClassName = packageName + "." + loggerSimpleClassName;
} else {
loggerClassName = loggerSimpleClassName;
}
}
String loggerClassName = StringUtils.loggerClassName(clazz);
String loggerSimpleClassName = StringUtils.simpleName(loggerClassName);
var loggerFile = m_processingEnv.getFiler().createSourceFile(loggerClassName);
@@ -197,7 +223,7 @@ public class LoggerGenerator {
out.println("import edu.wpi.first.epilogue.Logged;");
out.println("import edu.wpi.first.epilogue.Epilogue;");
out.println("import edu.wpi.first.epilogue.logging.ClassSpecificLogger;");
out.println("import edu.wpi.first.epilogue.logging.DataLogger;");
out.println("import edu.wpi.first.epilogue.logging.EpilogueBackend;");
if (requiresVarHandles) {
out.println("import java.lang.invoke.MethodHandles;");
out.println("import java.lang.invoke.VarHandle;");
@@ -220,13 +246,13 @@ public class LoggerGenerator {
}
out.println();
var clazz = simpleClassName + ".class";
var classReference = simpleClassName + ".class";
out.println(" static {");
out.println(" try {");
out.println(
" var lookup = MethodHandles.privateLookupIn("
+ clazz
+ classReference
+ ", MethodHandles.lookup());");
for (var privateField : privateFields) {
@@ -235,7 +261,7 @@ public class LoggerGenerator {
" $"
+ fieldName
+ " = lookup.findVarHandle("
+ clazz
+ classReference
+ ", \""
+ fieldName
+ "\", "
@@ -258,9 +284,10 @@ public class LoggerGenerator {
out.println();
// @Override
// public void update(DataLogger dataLogger, Foo object) {
// public void update(EpilogueBackend backend, Foo object) {
out.println(" @Override");
out.println(" public void update(DataLogger dataLogger, " + simpleClassName + " object) {");
out.println(
" public void update(EpilogueBackend backend, " + simpleClassName + " object) {");
// Build a map of importance levels to the fields logged at those levels
// e.g. { DEBUG: [fieldA, fieldB], INFO: [fieldC], CRITICAL: [fieldD, fieldE, fieldF] }

View File

@@ -31,7 +31,7 @@ public class MeasureHandler extends ElementHandler {
@Override
public String logInvocation(Element element) {
// DataLogger has builtin support for logging measures
return "dataLogger.log(\"" + loggedName(element) + "\", " + elementAccess(element) + ")";
// EpilogueBackend has builtin support for logging measures
return "backend.log(\"" + loggedName(element) + "\", " + elementAccess(element) + ")";
}
}

View File

@@ -36,6 +36,6 @@ public class PrimitiveHandler extends ElementHandler {
@Override
public String logInvocation(Element element) {
return "dataLogger.log(\"" + loggedName(element) + "\", " + elementAccess(element) + ")";
return "backend.log(\"" + loggedName(element) + "\", " + elementAccess(element) + ")";
}
}

View File

@@ -4,24 +4,32 @@
package edu.wpi.first.epilogue.processor;
import java.util.Optional;
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 SendableHandler extends ElementHandler {
private final TypeMirror m_sendableType;
private final TypeMirror m_commandType;
private final TypeMirror m_subsystemType;
private final Optional<TypeMirror> m_sendableType;
private final Optional<TypeMirror> m_commandType;
private final Optional<TypeMirror> m_subsystemType;
protected SendableHandler(ProcessingEnvironment processingEnv) {
super(processingEnv);
m_sendableType =
lookupTypeElement(processingEnv, "edu.wpi.first.util.sendable.Sendable").asType();
Optional.ofNullable(
lookupTypeElement(processingEnv, "edu.wpi.first.util.sendable.Sendable"))
.map(TypeElement::asType);
m_commandType =
lookupTypeElement(processingEnv, "edu.wpi.first.wpilibj2.command.Command").asType();
Optional.ofNullable(
lookupTypeElement(processingEnv, "edu.wpi.first.wpilibj2.command.Command"))
.map(TypeElement::asType);
m_subsystemType =
lookupTypeElement(processingEnv, "edu.wpi.first.wpilibj2.command.SubsystemBase").asType();
Optional.ofNullable(
lookupTypeElement(processingEnv, "edu.wpi.first.wpilibj2.command.SubsystemBase"))
.map(TypeElement::asType);
}
@Override
@@ -30,24 +38,32 @@ public class SendableHandler extends ElementHandler {
// Accept any sendable type. However, the log invocation will return null
// for sendable types that should not be logged (commands, subsystems)
return m_processingEnv.getTypeUtils().isAssignable(dataType, m_sendableType);
return m_sendableType
.map(t -> m_processingEnv.getTypeUtils().isAssignable(dataType, t))
.orElse(false);
}
@Override
public String logInvocation(Element element) {
var dataType = dataType(element);
if (m_processingEnv.getTypeUtils().isAssignable(dataType, m_commandType)
|| m_processingEnv.getTypeUtils().isAssignable(dataType, m_subsystemType)) {
// Do not log commands or subsystems via their sendable implementations
// We accept all sendable objects to prevent them from being reported as not loggable,
// but their sendable implementations do not include helpful information.
// Users are free to provide custom logging implementations for commands, and tag their
// subsystems with @Logged to log their contents automatically
// Do not log commands or subsystems via their sendable implementations
// We accept all sendable objects to prevent them from being reported as not loggable,
// but their sendable implementations do not include helpful information.
// Users are free to provide custom logging implementations for commands, and tag their
// subsystems with @Logged to log their contents automatically
if (m_commandType
.map(t -> m_processingEnv.getTypeUtils().isAssignable(dataType, t))
.orElse(false)) {
return null;
}
if (m_subsystemType
.map(t -> m_processingEnv.getTypeUtils().isAssignable(dataType, t))
.orElse(false)) {
return null;
}
return "logSendable(dataLogger.getSubLogger(\""
return "logSendable(backend.getNested(\""
+ loggedName(element)
+ "\"), "
+ elementAccess(element)

View File

@@ -5,9 +5,13 @@
package edu.wpi.first.epilogue.processor;
import edu.wpi.first.epilogue.Logged;
import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.Deque;
import java.util.List;
import java.util.stream.Collectors;
import javax.lang.model.element.Element;
import javax.lang.model.element.PackageElement;
import javax.lang.model.element.TypeElement;
public final class StringUtils {
@@ -108,34 +112,24 @@ public final class StringUtils {
*/
public static String loggerClassName(TypeElement clazz) {
var config = clazz.getAnnotation(Logged.class);
var className = clazz.getQualifiedName().toString();
String packageName;
int lastDot = className.lastIndexOf('.');
if (lastDot <= 0) {
packageName = null;
Deque<String> nesting = new ArrayDeque<>();
Element enclosing = clazz.getEnclosingElement();
while (!(enclosing instanceof PackageElement p)) {
nesting.addFirst(enclosing.getSimpleName().toString());
enclosing = enclosing.getEnclosingElement();
}
nesting.addLast(clazz.getSimpleName().toString());
String packageName = p.getQualifiedName().toString();
String className;
if (config == null || config.name().isEmpty()) {
className = String.join("$", nesting);
} else {
packageName = className.substring(0, lastDot);
className = capitalize(config.name()).replaceAll(" ", "");
}
String loggerClassName;
// Use the name on the class config to set the generated logger names
// This helps to avoid naming conflicts
if (config.name().isBlank()) {
loggerClassName = className + "Logger";
} else {
String cleaned = config.name().replaceAll(" ", "");
var loggerSimpleClassName = StringUtils.capitalize(cleaned) + "Logger";
if (packageName != null) {
loggerClassName = packageName + "." + loggerSimpleClassName;
} else {
loggerClassName = loggerSimpleClassName;
}
}
return loggerClassName;
return packageName + "." + className + "Logger";
}
/**

View File

@@ -39,7 +39,7 @@ public class StructHandler extends ElementHandler {
@Override
public String logInvocation(Element element) {
return "dataLogger.log(\""
return "backend.log(\""
+ loggedName(element)
+ "\", "
+ elementAccess(element)

View File

@@ -43,7 +43,7 @@ public class SupplierHandler extends ElementHandler {
@Override
public String logInvocation(Element element) {
return "dataLogger.log(\"" + loggedName(element) + "\", " + elementAccess(element) + ")";
return "backend.log(\"" + loggedName(element) + "\", " + elementAccess(element) + ")";
}
@Override

View File

@@ -33,9 +33,19 @@ class EpilogueGeneratorTest {
import static edu.wpi.first.units.Units.Seconds;
import edu.wpi.first.hal.FRCNetComm;
import edu.wpi.first.hal.HAL;
import edu.wpi.first.epilogue.ExampleLogger;
public final class Epilogue {
static {
HAL.report(
FRCNetComm.tResourceType.kResourceType_LoggingFramework,
FRCNetComm.tInstances.kLoggingFramework_Epilogue
);
}
private static final EpilogueConfiguration config = new EpilogueConfiguration();
public static final ExampleLogger exampleLogger = new ExampleLogger();
@@ -82,9 +92,19 @@ class EpilogueGeneratorTest {
import static edu.wpi.first.units.Units.Seconds;
import edu.wpi.first.hal.FRCNetComm;
import edu.wpi.first.hal.HAL;
import edu.wpi.first.epilogue.ExampleLogger;
public final class Epilogue {
static {
HAL.report(
FRCNetComm.tResourceType.kResourceType_LoggingFramework,
FRCNetComm.tInstances.kLoggingFramework_Epilogue
);
}
private static final EpilogueConfiguration config = new EpilogueConfiguration();
public static final ExampleLogger exampleLogger = new ExampleLogger();
@@ -126,9 +146,19 @@ class EpilogueGeneratorTest {
import static edu.wpi.first.units.Units.Seconds;
import edu.wpi.first.hal.FRCNetComm;
import edu.wpi.first.hal.HAL;
import edu.wpi.first.epilogue.ExampleLogger;
public final class Epilogue {
static {
HAL.report(
FRCNetComm.tResourceType.kResourceType_LoggingFramework,
FRCNetComm.tInstances.kLoggingFramework_Epilogue
);
}
private static final EpilogueConfiguration config = new EpilogueConfiguration();
public static final ExampleLogger exampleLogger = new ExampleLogger();
@@ -155,8 +185,8 @@ class EpilogueGeneratorTest {
*/
public static void update(edu.wpi.first.epilogue.Example robot) {
long start = System.nanoTime();
exampleLogger.tryUpdate(config.dataLogger.getSubLogger(config.root), robot, config.errorHandler);
config.dataLogger.log(\"Epilogue/Stats/Last Run\", (System.nanoTime() - start) / 1e6);
exampleLogger.tryUpdate(config.backend.getNested(config.root), robot, config.errorHandler);
config.backend.log(\"Epilogue/Stats/Last Run\", (System.nanoTime() - start) / 1e6);
}
/**
@@ -204,10 +234,20 @@ class EpilogueGeneratorTest {
import static edu.wpi.first.units.Units.Seconds;
import edu.wpi.first.hal.FRCNetComm;
import edu.wpi.first.hal.HAL;
import edu.wpi.first.epilogue.AlphaBotLogger;
import edu.wpi.first.epilogue.BetaBotLogger;
public final class Epilogue {
static {
HAL.report(
FRCNetComm.tResourceType.kResourceType_LoggingFramework,
FRCNetComm.tInstances.kLoggingFramework_Epilogue
);
}
private static final EpilogueConfiguration config = new EpilogueConfiguration();
public static final AlphaBotLogger alphaBotLogger = new AlphaBotLogger();
@@ -235,8 +275,8 @@ class EpilogueGeneratorTest {
*/
public static void update(edu.wpi.first.epilogue.AlphaBot robot) {
long start = System.nanoTime();
alphaBotLogger.tryUpdate(config.dataLogger.getSubLogger(config.root), robot, config.errorHandler);
config.dataLogger.log(\"Epilogue/Stats/Last Run\", (System.nanoTime() - start) / 1e6);
alphaBotLogger.tryUpdate(config.backend.getNested(config.root), robot, config.errorHandler);
config.backend.log(\"Epilogue/Stats/Last Run\", (System.nanoTime() - start) / 1e6);
}
/**
@@ -267,8 +307,8 @@ class EpilogueGeneratorTest {
*/
public static void update(edu.wpi.first.epilogue.BetaBot robot) {
long start = System.nanoTime();
betaBotLogger.tryUpdate(config.dataLogger.getSubLogger(config.root), robot, config.errorHandler);
config.dataLogger.log(\"Epilogue/Stats/Last Run\", (System.nanoTime() - start) / 1e6);
betaBotLogger.tryUpdate(config.backend.getNested(config.root), robot, config.errorHandler);
config.backend.log(\"Epilogue/Stats/Last Run\", (System.nanoTime() - start) / 1e6);
}
/**
@@ -314,7 +354,7 @@ class EpilogueGeneratorTest {
public CustomLogger() { super(A.class); }
@Override
public void update(DataLogger logger, A object) {} // implementation is irrelevant
public void update(EpilogueBackend backend, A object) {} // implementation is irrelevant
}
@Logged
@@ -331,10 +371,20 @@ class EpilogueGeneratorTest {
import static edu.wpi.first.units.Units.Seconds;
import edu.wpi.first.hal.FRCNetComm;
import edu.wpi.first.hal.HAL;
import edu.wpi.first.epilogue.ExampleLogger;
import edu.wpi.first.epilogue.CustomLogger;
public final class Epilogue {
static {
HAL.report(
FRCNetComm.tResourceType.kResourceType_LoggingFramework,
FRCNetComm.tInstances.kLoggingFramework_Epilogue
);
}
private static final EpilogueConfiguration config = new EpilogueConfiguration();
public static final ExampleLogger exampleLogger = new ExampleLogger();

View File

@@ -4,8 +4,8 @@
package edu.wpi.first.epilogue;
import edu.wpi.first.epilogue.logging.DataLogger;
import edu.wpi.first.epilogue.logging.NTDataLogger;
import edu.wpi.first.epilogue.logging.EpilogueBackend;
import edu.wpi.first.epilogue.logging.NTEpilogueBackend;
import edu.wpi.first.epilogue.logging.errors.ErrorHandler;
import edu.wpi.first.epilogue.logging.errors.ErrorPrinter;
import edu.wpi.first.networktables.NetworkTableInstance;
@@ -18,11 +18,11 @@ import edu.wpi.first.units.measure.Time;
@SuppressWarnings("checkstyle:MemberName")
public class EpilogueConfiguration {
/**
* The data logger implementation for Epilogue to use. By default, this will log data directly to
* The backend implementation for Epilogue to use. By default, this will log data directly to
* NetworkTables. NetworkTable data can be mirrored to a log file on disk by calling {@code
* DataLogManager.start()} in your {@code robotInit} method.
*/
public DataLogger dataLogger = new NTDataLogger(NetworkTableInstance.getDefault());
public EpilogueBackend backend = new NTEpilogueBackend(NetworkTableInstance.getDefault());
/**
* The period Epilogue will log at. By default this is the period that the robot runs at. This is

View File

@@ -42,26 +42,26 @@ public abstract class ClassSpecificLogger<T> {
/**
* Updates an object's fields in a data log.
*
* @param dataLogger the logger to update
* @param backend the backend to update
* @param object the object to update in the log
*/
protected abstract void update(DataLogger dataLogger, T object);
protected abstract void update(EpilogueBackend backend, T object);
/**
* Attempts to update the data log. Will do nothing if the logger is {@link #disable() disabled}.
*
* @param dataLogger the logger to log data to
* @param backend the backend to log data to
* @param object the data object to log
* @param errorHandler the handler to use if logging raised an exception
*/
@SuppressWarnings("PMD.AvoidCatchingGenericException")
public final void tryUpdate(DataLogger dataLogger, T object, ErrorHandler errorHandler) {
public final void tryUpdate(EpilogueBackend backend, T object, ErrorHandler errorHandler) {
if (m_disabled) {
return;
}
try {
update(dataLogger, object);
update(backend, object);
} catch (Exception e) {
errorHandler.handle(e, this);
}
@@ -98,10 +98,10 @@ public abstract class ClassSpecificLogger<T> {
/**
* Logs a sendable type.
*
* @param dataLogger the logger to log data into
* @param backend the backend to log data into
* @param sendable the sendable object to log
*/
protected void logSendable(DataLogger dataLogger, Sendable sendable) {
protected void logSendable(EpilogueBackend backend, Sendable sendable) {
if (sendable == null) {
return;
}
@@ -110,7 +110,7 @@ public abstract class ClassSpecificLogger<T> {
m_sendables.computeIfAbsent(
sendable,
s -> {
var b = new LogBackedSendableBuilder(dataLogger);
var b = new LogBackedSendableBuilder(backend);
s.initSendable(b);
return b;
});

View File

@@ -9,40 +9,40 @@ import edu.wpi.first.units.Unit;
import edu.wpi.first.util.struct.Struct;
import java.util.Collection;
/** A data logger is a generic interface for logging discrete data points. */
public interface DataLogger {
/** A backend is a generic interface for Epilogue to log discrete data points. */
public interface EpilogueBackend {
/**
* Creates a data logger that logs to multiple backends at once. Data reads will still only occur
* once; data is passed to all composed loggers at once.
* Creates a backend that logs to multiple backends at once. Data reads will still only occur
* once; data is passed to all composed backends at once.
*
* @param loggers the loggers to compose together
* @return the multi logger
* @param backends the backends to compose together
* @return the multi backend
*/
static DataLogger multi(DataLogger... loggers) {
return new MultiLogger(loggers);
static EpilogueBackend multi(EpilogueBackend... backends) {
return new MultiBackend(backends);
}
/**
* Creates a lazy version of this logger. A lazy logger will only log data to a field when its
* Creates a lazy version of this backend. A lazy backend will only log data to a field when its
* value changes, which can help keep file size and bandwidth usage in check. However, there is an
* additional CPU and memory overhead associated with tracking the current value of every logged
* entry. The most surefire way to reduce CPU and memory usage associated with logging is to log
* fewer things - which can be done by opting out of logging unnecessary data or increasing the
* minimum logged importance level in the Epilogue configuration.
*
* @return the lazy logger
* @return the lazy backend
*/
default DataLogger lazy() {
return new LazyLogger(this);
default EpilogueBackend lazy() {
return new LazyBackend(this);
}
/**
* Gets a logger that can be used to log nested data underneath a specific path.
* Gets a backend that can be used to log nested data underneath a specific path.
*
* @param path the path to use for logging nested data under
* @return the sub logger
* @return the nested backend
*/
DataLogger getSubLogger(String path);
EpilogueBackend getNested(String path);
/**
* Logs a 32-bit integer data point.

View File

@@ -26,24 +26,24 @@ import java.util.HashMap;
import java.util.Map;
import java.util.function.BiFunction;
/** A data logger implementation that saves information to a WPILib {@link DataLog} file on disk. */
public class FileLogger implements DataLogger {
/** 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, SubLogger> m_subLoggers = new HashMap<>();
private final Map<String, NestedBackend> m_subLoggers = new HashMap<>();
/**
* Creates a new file logger.
* Creates a new file-based backend.
*
* @param dataLog the data log to save data to
*/
public FileLogger(DataLog dataLog) {
this.m_dataLog = requireNonNullParam(dataLog, "dataLog", "FileLogger");
public FileBackend(DataLog dataLog) {
this.m_dataLog = requireNonNullParam(dataLog, "dataLog", "FileBackend");
}
@Override
public DataLogger getSubLogger(String path) {
return m_subLoggers.computeIfAbsent(path, k -> new SubLogger(k, this));
public EpilogueBackend getNested(String path) {
return m_subLoggers.computeIfAbsent(path, k -> new NestedBackend(k, this));
}
@SuppressWarnings("unchecked")

View File

@@ -11,36 +11,36 @@ import java.util.Map;
import java.util.Objects;
/**
* A data logger implementation that only logs data when it changes. Useful for keeping bandwidth
* and file sizes down. However, because it still needs to check that data has changed, it cannot
* avoid expensive sensor reads.
* A backend implementation that only logs data when it changes. Useful for keeping bandwidth and
* file sizes down. However, because it still needs to check that data has changed, it cannot avoid
* expensive sensor reads.
*/
public class LazyLogger implements DataLogger {
private final DataLogger m_logger;
public class LazyBackend implements EpilogueBackend {
private final EpilogueBackend m_backend;
// Keep a record of the most recent value written to each entry
// Note that this may duplicate a lot of data, and will box primitives.
private final Map<String, Object> m_previousValues = new HashMap<>();
private final Map<String, SubLogger> m_subLoggers = new HashMap<>();
private final Map<String, NestedBackend> m_subLoggers = new HashMap<>();
/**
* Creates a new lazy logger wrapper around another logger.
* Creates a new lazy backend wrapper around another backend.
*
* @param logger the logger to delegate to
* @param backend the backend to delegate to
*/
public LazyLogger(DataLogger logger) {
this.m_logger = logger;
public LazyBackend(EpilogueBackend backend) {
this.m_backend = backend;
}
@Override
public DataLogger lazy() {
public EpilogueBackend lazy() {
// Already lazy, don't need to wrap it again
return this;
}
@Override
public DataLogger getSubLogger(String path) {
return m_subLoggers.computeIfAbsent(path, k -> new SubLogger(k, this));
public EpilogueBackend getNested(String path) {
return m_subLoggers.computeIfAbsent(path, k -> new NestedBackend(k, this));
}
@Override
@@ -53,7 +53,7 @@ public class LazyLogger implements DataLogger {
}
m_previousValues.put(identifier, value);
m_logger.log(identifier, value);
m_backend.log(identifier, value);
}
@Override
@@ -66,7 +66,7 @@ public class LazyLogger implements DataLogger {
}
m_previousValues.put(identifier, value);
m_logger.log(identifier, value);
m_backend.log(identifier, value);
}
@Override
@@ -79,7 +79,7 @@ public class LazyLogger implements DataLogger {
}
m_previousValues.put(identifier, value);
m_logger.log(identifier, value);
m_backend.log(identifier, value);
}
@Override
@@ -92,7 +92,7 @@ public class LazyLogger implements DataLogger {
}
m_previousValues.put(identifier, value);
m_logger.log(identifier, value);
m_backend.log(identifier, value);
}
@Override
@@ -105,7 +105,7 @@ public class LazyLogger implements DataLogger {
}
m_previousValues.put(identifier, value);
m_logger.log(identifier, value);
m_backend.log(identifier, value);
}
@Override
@@ -118,7 +118,7 @@ public class LazyLogger implements DataLogger {
}
m_previousValues.put(identifier, value);
m_logger.log(identifier, value);
m_backend.log(identifier, value);
}
@Override
@@ -131,7 +131,7 @@ public class LazyLogger implements DataLogger {
}
m_previousValues.put(identifier, value);
m_logger.log(identifier, value);
m_backend.log(identifier, value);
}
@Override
@@ -144,7 +144,7 @@ public class LazyLogger implements DataLogger {
}
m_previousValues.put(identifier, value);
m_logger.log(identifier, value);
m_backend.log(identifier, value);
}
@Override
@@ -157,7 +157,7 @@ public class LazyLogger implements DataLogger {
}
m_previousValues.put(identifier, value);
m_logger.log(identifier, value);
m_backend.log(identifier, value);
}
@Override
@@ -170,7 +170,7 @@ public class LazyLogger implements DataLogger {
}
m_previousValues.put(identifier, value);
m_logger.log(identifier, value);
m_backend.log(identifier, value);
}
@Override
@@ -183,7 +183,7 @@ public class LazyLogger implements DataLogger {
}
m_previousValues.put(identifier, value);
m_logger.log(identifier, value);
m_backend.log(identifier, value);
}
@Override
@@ -196,7 +196,7 @@ public class LazyLogger implements DataLogger {
}
m_previousValues.put(identifier, value);
m_logger.log(identifier, value);
m_backend.log(identifier, value);
}
@Override
@@ -209,7 +209,7 @@ public class LazyLogger implements DataLogger {
}
m_previousValues.put(identifier, value);
m_logger.log(identifier, value);
m_backend.log(identifier, value);
}
@Override
@@ -222,7 +222,7 @@ public class LazyLogger implements DataLogger {
}
m_previousValues.put(identifier, value);
m_logger.log(identifier, value, struct);
m_backend.log(identifier, value, struct);
}
@Override
@@ -235,6 +235,6 @@ public class LazyLogger implements DataLogger {
}
m_previousValues.put(identifier, value);
m_logger.log(identifier, value, struct);
m_backend.log(identifier, value, struct);
}
}

View File

@@ -18,24 +18,24 @@ import java.util.function.LongConsumer;
import java.util.function.LongSupplier;
import java.util.function.Supplier;
/** A sendable builder implementation that sends data to a {@link DataLogger}. */
@SuppressWarnings("PMD.CouplingBetweenObjects") // most methods simply delegate to the logger
/** 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 DataLogger m_logger;
private final EpilogueBackend m_backend;
private final Collection<Runnable> m_updates = new ArrayList<>();
/**
* Creates a new sendable builder that delegates writes to an underlying data logger.
* Creates a new sendable builder that delegates writes to an underlying backend.
*
* @param logger the data logger to write the sendable data to
* @param backend the backend to write the sendable data to
*/
public LogBackedSendableBuilder(DataLogger logger) {
this.m_logger = logger;
public LogBackedSendableBuilder(EpilogueBackend backend) {
this.m_backend = backend;
}
@Override
public void setSmartDashboardType(String type) {
m_logger.log(".type", type);
m_backend.log(".type", type);
}
@Override
@@ -50,132 +50,132 @@ public class LogBackedSendableBuilder implements SendableBuilder {
@Override
public void addBooleanProperty(String key, BooleanSupplier getter, BooleanConsumer setter) {
m_updates.add(() -> m_logger.log(key, getter.getAsBoolean()));
m_updates.add(() -> m_backend.log(key, getter.getAsBoolean()));
}
@Override
public void publishConstBoolean(String key, boolean value) {
m_logger.log(key, value);
m_backend.log(key, value);
}
@Override
public void addIntegerProperty(String key, LongSupplier getter, LongConsumer setter) {
m_updates.add(() -> m_logger.log(key, getter.getAsLong()));
m_updates.add(() -> m_backend.log(key, getter.getAsLong()));
}
@Override
public void publishConstInteger(String key, long value) {
m_logger.log(key, value);
m_backend.log(key, value);
}
@Override
public void addFloatProperty(String key, FloatSupplier getter, FloatConsumer setter) {
m_updates.add(() -> m_logger.log(key, getter.getAsFloat()));
m_updates.add(() -> m_backend.log(key, getter.getAsFloat()));
}
@Override
public void publishConstFloat(String key, float value) {
m_logger.log(key, value);
m_backend.log(key, value);
}
@Override
public void addDoubleProperty(String key, DoubleSupplier getter, DoubleConsumer setter) {
m_updates.add(() -> m_logger.log(key, getter.getAsDouble()));
m_updates.add(() -> m_backend.log(key, getter.getAsDouble()));
}
@Override
public void publishConstDouble(String key, double value) {
m_logger.log(key, value);
m_backend.log(key, value);
}
@Override
public void addStringProperty(String key, Supplier<String> getter, Consumer<String> setter) {
if (getter != null) {
m_updates.add(() -> m_logger.log(key, getter.get()));
m_updates.add(() -> m_backend.log(key, getter.get()));
}
}
@Override
public void publishConstString(String key, String value) {
m_logger.log(key, value);
m_backend.log(key, value);
}
@Override
public void addBooleanArrayProperty(
String key, Supplier<boolean[]> getter, Consumer<boolean[]> setter) {
if (getter != null) {
m_updates.add(() -> m_logger.log(key, getter.get()));
m_updates.add(() -> m_backend.log(key, getter.get()));
}
}
@Override
public void publishConstBooleanArray(String key, boolean[] value) {
m_logger.log(key, value);
m_backend.log(key, value);
}
@Override
public void addIntegerArrayProperty(
String key, Supplier<long[]> getter, Consumer<long[]> setter) {
if (getter != null) {
m_updates.add(() -> m_logger.log(key, getter.get()));
m_updates.add(() -> m_backend.log(key, getter.get()));
}
}
@Override
public void publishConstIntegerArray(String key, long[] value) {
m_logger.log(key, value);
m_backend.log(key, value);
}
@Override
public void addFloatArrayProperty(
String key, Supplier<float[]> getter, Consumer<float[]> setter) {
if (getter != null) {
m_updates.add(() -> m_logger.log(key, getter.get()));
m_updates.add(() -> m_backend.log(key, getter.get()));
}
}
@Override
public void publishConstFloatArray(String key, float[] value) {
m_logger.log(key, value);
m_backend.log(key, value);
}
@Override
public void addDoubleArrayProperty(
String key, Supplier<double[]> getter, Consumer<double[]> setter) {
if (getter != null) {
m_updates.add(() -> m_logger.log(key, getter.get()));
m_updates.add(() -> m_backend.log(key, getter.get()));
}
}
@Override
public void publishConstDoubleArray(String key, double[] value) {
m_logger.log(key, value);
m_backend.log(key, value);
}
@Override
public void addStringArrayProperty(
String key, Supplier<String[]> getter, Consumer<String[]> setter) {
if (getter != null) {
m_updates.add(() -> m_logger.log(key, getter.get()));
m_updates.add(() -> m_backend.log(key, getter.get()));
}
}
@Override
public void publishConstStringArray(String key, String[] value) {
m_logger.log(key, value);
m_backend.log(key, value);
}
@Override
public void addRawProperty(
String key, String typeString, Supplier<byte[]> getter, Consumer<byte[]> setter) {
if (getter != null) {
m_updates.add(() -> m_logger.log(key, getter.get()));
m_updates.add(() -> m_backend.log(key, getter.get()));
}
}
@Override
public void publishConstRaw(String key, String typeString, byte[] value) {
m_logger.log(key, value);
m_backend.log(key, value);
}
@Override

View File

@@ -0,0 +1,134 @@
// 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 edu.wpi.first.util.struct.Struct;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* A backend implementation that delegates to other backends. Helpful for simultaneous logging to
* multiple data stores at once.
*/
public class MultiBackend implements EpilogueBackend {
private final List<EpilogueBackend> m_backends;
private final Map<String, NestedBackend> m_nestedBackends = new HashMap<>();
// Use EpilogueBackend.multi(...) instead of instantiation directly
MultiBackend(EpilogueBackend... backends) {
this.m_backends = List.of(backends);
}
@Override
public EpilogueBackend getNested(String path) {
return m_nestedBackends.computeIfAbsent(path, k -> new NestedBackend(k, this));
}
@Override
public void log(String identifier, int value) {
for (EpilogueBackend backend : m_backends) {
backend.log(identifier, value);
}
}
@Override
public void log(String identifier, long value) {
for (EpilogueBackend backend : m_backends) {
backend.log(identifier, value);
}
}
@Override
public void log(String identifier, float value) {
for (EpilogueBackend backend : m_backends) {
backend.log(identifier, value);
}
}
@Override
public void log(String identifier, double value) {
for (EpilogueBackend backend : m_backends) {
backend.log(identifier, value);
}
}
@Override
public void log(String identifier, boolean value) {
for (EpilogueBackend backend : m_backends) {
backend.log(identifier, value);
}
}
@Override
public void log(String identifier, byte[] value) {
for (EpilogueBackend backend : m_backends) {
backend.log(identifier, value);
}
}
@Override
public void log(String identifier, int[] value) {
for (EpilogueBackend backend : m_backends) {
backend.log(identifier, value);
}
}
@Override
public void log(String identifier, long[] value) {
for (EpilogueBackend backend : m_backends) {
backend.log(identifier, value);
}
}
@Override
public void log(String identifier, float[] value) {
for (EpilogueBackend backend : m_backends) {
backend.log(identifier, value);
}
}
@Override
public void log(String identifier, double[] value) {
for (EpilogueBackend backend : m_backends) {
backend.log(identifier, value);
}
}
@Override
public void log(String identifier, boolean[] value) {
for (EpilogueBackend backend : m_backends) {
backend.log(identifier, value);
}
}
@Override
public void log(String identifier, String value) {
for (EpilogueBackend backend : m_backends) {
backend.log(identifier, value);
}
}
@Override
public void log(String identifier, String[] value) {
for (EpilogueBackend backend : m_backends) {
backend.log(identifier, value);
}
}
@Override
public <S> void log(String identifier, S value, Struct<S> struct) {
for (EpilogueBackend backend : m_backends) {
backend.log(identifier, value, struct);
}
}
@Override
public <S> void log(String identifier, S[] value, Struct<S> struct) {
for (EpilogueBackend backend : m_backends) {
backend.log(identifier, value, struct);
}
}
}

View File

@@ -1,134 +0,0 @@
// 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 edu.wpi.first.util.struct.Struct;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* A data logger implementation that delegates to other loggers. Helpful for simultaneous logging to
* multiple data stores at once.
*/
public class MultiLogger implements DataLogger {
private final List<DataLogger> m_loggers;
private final Map<String, SubLogger> m_subLoggers = new HashMap<>();
// Use DataLogger.multi(...) instead of instantiation directly
MultiLogger(DataLogger... loggers) {
this.m_loggers = List.of(loggers);
}
@Override
public DataLogger getSubLogger(String path) {
return m_subLoggers.computeIfAbsent(path, k -> new SubLogger(k, this));
}
@Override
public void log(String identifier, int value) {
for (DataLogger logger : m_loggers) {
logger.log(identifier, value);
}
}
@Override
public void log(String identifier, long value) {
for (DataLogger logger : m_loggers) {
logger.log(identifier, value);
}
}
@Override
public void log(String identifier, float value) {
for (DataLogger logger : m_loggers) {
logger.log(identifier, value);
}
}
@Override
public void log(String identifier, double value) {
for (DataLogger logger : m_loggers) {
logger.log(identifier, value);
}
}
@Override
public void log(String identifier, boolean value) {
for (DataLogger logger : m_loggers) {
logger.log(identifier, value);
}
}
@Override
public void log(String identifier, byte[] value) {
for (DataLogger logger : m_loggers) {
logger.log(identifier, value);
}
}
@Override
public void log(String identifier, int[] value) {
for (DataLogger logger : m_loggers) {
logger.log(identifier, value);
}
}
@Override
public void log(String identifier, long[] value) {
for (DataLogger logger : m_loggers) {
logger.log(identifier, value);
}
}
@Override
public void log(String identifier, float[] value) {
for (DataLogger logger : m_loggers) {
logger.log(identifier, value);
}
}
@Override
public void log(String identifier, double[] value) {
for (DataLogger logger : m_loggers) {
logger.log(identifier, value);
}
}
@Override
public void log(String identifier, boolean[] value) {
for (DataLogger logger : m_loggers) {
logger.log(identifier, value);
}
}
@Override
public void log(String identifier, String value) {
for (DataLogger logger : m_loggers) {
logger.log(identifier, value);
}
}
@Override
public void log(String identifier, String[] value) {
for (DataLogger logger : m_loggers) {
logger.log(identifier, value);
}
}
@Override
public <S> void log(String identifier, S value, Struct<S> struct) {
for (DataLogger logger : m_loggers) {
logger.log(identifier, value, struct);
}
}
@Override
public <S> void log(String identifier, S[] value, Struct<S> struct) {
for (DataLogger logger : m_loggers) {
logger.log(identifier, value, struct);
}
}
}

View File

@@ -24,27 +24,27 @@ import java.util.HashMap;
import java.util.Map;
/**
* A data logger implementation that sends data over network tables. Be careful when using this,
* since sending too much data may cause bandwidth or CPU starvation.
* A backend implementation that sends data over network tables. Be careful when using this, since
* sending too much data may cause bandwidth or CPU starvation.
*/
public class NTDataLogger implements DataLogger {
public class NTEpilogueBackend implements EpilogueBackend {
private final NetworkTableInstance m_nt;
private final Map<String, Publisher> m_publishers = new HashMap<>();
private final Map<String, SubLogger> m_subLoggers = new HashMap<>();
private final Map<String, NestedBackend> m_nestedBackends = new HashMap<>();
/**
* Creates a data logger that sends information to NetworkTables.
* Creates a logging backend that sends information to NetworkTables.
*
* @param nt the NetworkTable instance to use to send data to
*/
public NTDataLogger(NetworkTableInstance nt) {
public NTEpilogueBackend(NetworkTableInstance nt) {
this.m_nt = nt;
}
@Override
public DataLogger getSubLogger(String path) {
return m_subLoggers.computeIfAbsent(path, k -> new SubLogger(k, this));
public EpilogueBackend getNested(String path) {
return m_nestedBackends.computeIfAbsent(path, k -> new NestedBackend(k, this));
}
@Override

View File

@@ -9,21 +9,21 @@ import java.util.HashMap;
import java.util.Map;
/**
* A data logger that logs to an underlying logger, prepending all logged data with a specific
* prefix. Useful for logging nested data structures.
* A backend that logs to an underlying backend, prepending all logged data with a specific prefix.
* Useful for logging nested data structures.
*/
public class SubLogger implements DataLogger {
public class NestedBackend implements EpilogueBackend {
private final String m_prefix;
private final DataLogger m_impl;
private final Map<String, SubLogger> m_subLoggers = new HashMap<>();
private final EpilogueBackend m_impl;
private final Map<String, NestedBackend> m_nestedBackends = new HashMap<>();
/**
* Creates a new sublogger underneath another logger.
* Creates a new nested backed underneath another backend.
*
* @param prefix the prefix to append to all data logged in the sublogger
* @param impl the data logger to log to
* @param prefix the prefix to append to all data logged in the nested backend
* @param impl the backend to log to
*/
public SubLogger(String prefix, DataLogger impl) {
public NestedBackend(String prefix, EpilogueBackend impl) {
// Add a trailing slash if not already present
if (prefix.endsWith("/")) {
this.m_prefix = prefix;
@@ -34,8 +34,8 @@ public class SubLogger implements DataLogger {
}
@Override
public DataLogger getSubLogger(String path) {
return m_subLoggers.computeIfAbsent(path, k -> new SubLogger(k, this));
public EpilogueBackend getNested(String path) {
return m_nestedBackends.computeIfAbsent(path, k -> new NestedBackend(k, this));
}
@Override

View File

@@ -6,14 +6,14 @@ package edu.wpi.first.epilogue.logging;
import edu.wpi.first.util.struct.Struct;
/** Null data logger implementation that logs nothing. */
public class NullLogger implements DataLogger {
/** Null backend implementation that logs nothing. */
public class NullBackend implements EpilogueBackend {
/** Default constructor. */
public NullLogger() {}
public NullBackend() {}
@Override
public DataLogger getSubLogger(String path) {
// Since a sublogger would still log nothing and has no state, we can just return the same
public EpilogueBackend getNested(String path) {
// Since a nested backend would still log nothing and has no state, we can just return the same
// null-logging implementation
return this;
}

View File

@@ -19,10 +19,10 @@ class ClassSpecificLoggerTest {
}
@Override
protected void update(DataLogger dataLogger, Point2d object) {
dataLogger.log("x", object.x);
dataLogger.log("y", object.y);
dataLogger.log("dim", object.dim);
protected void update(EpilogueBackend backend, Point2d object) {
backend.log("x", object.x);
backend.log("y", object.y);
backend.log("dim", object.dim);
}
}
}
@@ -31,14 +31,14 @@ class ClassSpecificLoggerTest {
void testReadPrivate() {
var point = new Point2d(1, 4, 2);
var logger = new Point2d.Logger();
var dataLog = new TestLogger();
logger.update(dataLog.getSubLogger("Point"), point);
var dataLog = new TestBackend();
logger.update(dataLog.getNested("Point"), point);
assertEquals(
List.of(
new TestLogger.LogEntry<>("Point/x", 1.0),
new TestLogger.LogEntry<>("Point/y", 4.0),
new TestLogger.LogEntry<>("Point/dim", 2)),
new TestBackend.LogEntry<>("Point/x", 1.0),
new TestBackend.LogEntry<>("Point/y", 4.0),
new TestBackend.LogEntry<>("Point/dim", 2)),
dataLog.getEntries());
}
}

View File

@@ -10,36 +10,36 @@ import static org.junit.jupiter.api.Assertions.assertSame;
import java.util.List;
import org.junit.jupiter.api.Test;
class LazyLoggerTest {
class LazyBackendTest {
@Test
void lazyOfLazyReturnsSelf() {
var lazy = new LazyLogger(new NullLogger());
var lazy = new LazyBackend(new NullBackend());
assertSame(lazy, lazy.lazy());
}
@Test
void lazyInt() {
var logger = new TestLogger();
var lazy = new LazyLogger(logger);
var backend = new TestBackend();
var lazy = new LazyBackend(backend);
{
// First time logging to "int" should go through
lazy.log("int", 0);
assertEquals(List.of(new TestLogger.LogEntry<>("int", 0)), logger.getEntries());
assertEquals(List.of(new TestBackend.LogEntry<>("int", 0)), backend.getEntries());
}
{
// Logging the current value shouldn't go through
lazy.log("int", 0);
assertEquals(List.of(new TestLogger.LogEntry<>("int", 0)), logger.getEntries());
assertEquals(List.of(new TestBackend.LogEntry<>("int", 0)), backend.getEntries());
}
{
// Logging a new value should go through
lazy.log("int", 1);
assertEquals(
List.of(new TestLogger.LogEntry<>("int", 0), new TestLogger.LogEntry<>("int", 1)),
logger.getEntries());
List.of(new TestBackend.LogEntry<>("int", 0), new TestBackend.LogEntry<>("int", 1)),
backend.getEntries());
}
{
@@ -47,10 +47,10 @@ class LazyLoggerTest {
lazy.log("int", 0);
assertEquals(
List.of(
new TestLogger.LogEntry<>("int", 0),
new TestLogger.LogEntry<>("int", 1),
new TestLogger.LogEntry<>("int", 0)),
logger.getEntries());
new TestBackend.LogEntry<>("int", 0),
new TestBackend.LogEntry<>("int", 1),
new TestBackend.LogEntry<>("int", 0)),
backend.getEntries());
}
}
}

View File

@@ -12,10 +12,10 @@ import java.util.List;
import java.util.Map;
@SuppressWarnings("PMD.TestClassWithoutTestCases") // This is not a test class!
public class TestLogger implements DataLogger {
public class TestBackend implements EpilogueBackend {
public record LogEntry<T>(String identifier, T value) {}
private final Map<String, SubLogger> m_subLoggers = new HashMap<>();
private final Map<String, NestedBackend> m_nestedBackends = new HashMap<>();
private final List<LogEntry<?>> m_entries = new ArrayList<>();
@@ -24,8 +24,8 @@ public class TestLogger implements DataLogger {
}
@Override
public DataLogger getSubLogger(String path) {
return m_subLoggers.computeIfAbsent(path, k -> new SubLogger(k, this));
public EpilogueBackend getNested(String path) {
return m_nestedBackends.computeIfAbsent(path, k -> new NestedBackend(k, this));
}
@Override

View File

@@ -121,6 +121,11 @@ class WindowManager {
*/
void EraseWindows() { m_windows.clear(); }
/**
* Get window count.
*/
size_t GetNumWindows() const { return m_windows.size(); }
protected:
/**
* Removes existing window (by index)

View File

@@ -68,3 +68,13 @@ kDashboard_LabVIEW = 6
kDashboard_AdvantageScope = 7
kDashboard_QFRCDashboard = 8
kDashboard_FRCWebComponents = 9
kDataLogLocation_Onboard = 1
kDataLogLocation_USB = 2
kLoggingFramework_Other = 1
kLoggingFramework_Epilogue = 2
kLoggingFramework_Monologue = 3
kLoggingFramework_AdvantageKit = 4
kLoggingFramework_DogLog = 5
kPDP_CTRE = 1
kPDP_REV = 2
kPDP_Unknown = 3

View File

@@ -115,3 +115,12 @@ kResourceType_Redux_future5 = 113
kResourceType_RevSparkFlexCAN = 114
kResourceType_RevSparkFlexPWM = 115
kResourceType_BangBangController = 116
kResourceType_DataLogManager = 117
kResourceType_LoggingFramework = 118
kResourceType_ChoreoTrajectory = 119
kResourceType_ChoreoTrigger = 120
kResourceType_PathWeaverTrajectory = 121
kResourceType_Koors40 = 122
kResourceType_ThriftyNova = 123
kResourceType_PWFSEN36005 = 124
kResourceType_LaserShark = 125

View File

@@ -253,6 +253,24 @@ public final class FRCNetComm {
public static final int kResourceType_RevSparkFlexPWM = 115;
/** kResourceType_BangBangController = 116. */
public static final int kResourceType_BangBangController = 116;
/** kResourceType_DataLogManager = 117. */
public static final int kResourceType_DataLogManager = 117;
/** kResourceType_LoggingFramework = 118. */
public static final int kResourceType_LoggingFramework = 118;
/** kResourceType_ChoreoTrajectory = 119. */
public static final int kResourceType_ChoreoTrajectory = 119;
/** kResourceType_ChoreoTrigger = 120. */
public static final int kResourceType_ChoreoTrigger = 120;
/** kResourceType_PathWeaverTrajectory = 121. */
public static final int kResourceType_PathWeaverTrajectory = 121;
/** kResourceType_Koors40 = 122. */
public static final int kResourceType_Koors40 = 122;
/** kResourceType_ThriftyNova = 123. */
public static final int kResourceType_ThriftyNova = 123;
/** kResourceType_PWFSEN36005 = 124. */
public static final int kResourceType_PWFSEN36005 = 124;
/** kResourceType_LaserShark = 125. */
public static final int kResourceType_LaserShark = 125;
}
/**
@@ -403,6 +421,26 @@ public final class FRCNetComm {
public static final int kDashboard_QFRCDashboard = 8;
/** kDashboard_FRCWebComponents = 9. */
public static final int kDashboard_FRCWebComponents = 9;
/** kDataLogLocation_Onboard = 1. */
public static final int kDataLogLocation_Onboard = 1;
/** kDataLogLocation_USB = 2. */
public static final int kDataLogLocation_USB = 2;
/** kLoggingFramework_Other = 1. */
public static final int kLoggingFramework_Other = 1;
/** kLoggingFramework_Epilogue = 2. */
public static final int kLoggingFramework_Epilogue = 2;
/** kLoggingFramework_Monologue = 3. */
public static final int kLoggingFramework_Monologue = 3;
/** kLoggingFramework_AdvantageKit = 4. */
public static final int kLoggingFramework_AdvantageKit = 4;
/** kLoggingFramework_DogLog = 5. */
public static final int kLoggingFramework_DogLog = 5;
/** kPDP_CTRE = 1. */
public static final int kPDP_CTRE = 1;
/** kPDP_REV = 2. */
public static final int kPDP_REV = 2;
/** kPDP_Unknown = 3. */
public static final int kPDP_Unknown = 3;
}
/** Utility class. */

View File

@@ -168,6 +168,15 @@ namespace HALUsageReporting {
kResourceType_RevSparkFlexCAN = 114,
kResourceType_RevSparkFlexPWM = 115,
kResourceType_BangBangController = 116,
kResourceType_DataLogManager = 117,
kResourceType_LoggingFramework = 118,
kResourceType_ChoreoTrajectory = 119,
kResourceType_ChoreoTrigger = 120,
kResourceType_PathWeaverTrajectory = 121,
kResourceType_Koors40 = 122,
kResourceType_ThriftyNova = 123,
kResourceType_PWFSEN36005 = 124,
kResourceType_LaserShark = 125,
};
enum tInstances : int32_t {
kLanguage_LabVIEW = 1,
@@ -240,6 +249,16 @@ namespace HALUsageReporting {
kDashboard_AdvantageScope = 7,
kDashboard_QFRCDashboard = 8,
kDashboard_FRCWebComponents = 9,
kDataLogLocation_Onboard = 1,
kDataLogLocation_USB = 2,
kLoggingFramework_Other = 1,
kLoggingFramework_Epilogue = 2,
kLoggingFramework_Monologue = 3,
kLoggingFramework_AdvantageKit = 4,
kLoggingFramework_DogLog = 5,
kPDP_CTRE = 1,
kPDP_REV = 2,
kPDP_Unknown = 3,
};
}
#endif

View File

@@ -137,6 +137,15 @@ typedef enum
kResourceType_RevSparkFlexCAN = 114,
kResourceType_RevSparkFlexPWM = 115,
kResourceType_BangBangController = 116,
kResourceType_DataLogManager = 117,
kResourceType_LoggingFramework = 118,
kResourceType_ChoreoTrajectory = 119,
kResourceType_ChoreoTrigger = 120,
kResourceType_PathWeaverTrajectory = 121,
kResourceType_Koors40 = 122,
kResourceType_ThriftyNova = 123,
kResourceType_PWFSEN36005 = 124,
kResourceType_LaserShark = 125,
// kResourceType_MaximumID = 255,
} tResourceType;
@@ -213,6 +222,16 @@ typedef enum
kDashboard_AdvantageScope = 7,
kDashboard_QFRCDashboard = 8,
kDashboard_FRCWebComponents = 9,
kDataLogLocation_Onboard = 1,
kDataLogLocation_USB = 2,
kLoggingFramework_Other = 1,
kLoggingFramework_Epilogue = 2,
kLoggingFramework_Monologue = 3,
kLoggingFramework_AdvantageKit = 4,
kLoggingFramework_DogLog = 5,
kPDP_CTRE = 1,
kPDP_REV = 2,
kPDP_Unknown = 3,
} tInstances;
/**

View File

@@ -1,3 +1,5 @@
import org.gradle.internal.os.OperatingSystem
plugins {
id 'c'
id 'cpp'
@@ -119,6 +121,10 @@ task cppHeadersZip(type: Zip) {
}
}
if (OperatingSystem.current().isWindows() && !project.hasProperty('ntcoreffibuild')) {
return
}
build.dependsOn cppHeadersZip
addTaskToCopyAllOutputs(cppHeadersZip)

View File

@@ -146,7 +146,7 @@ std::string SshSession::ExecuteResult(std::string_view cmd, int* exitStatus) {
#if LIBSSH_VERSION_MAJOR == 0 && LIBSSH_VERSION_MINOR >= 11
ssh_channel_get_exit_state(channel, &exitCode, nullptr, nullptr);
#else
ssh_channel_get_exit_status(channel);
exitCode = ssh_channel_get_exit_status(channel);
#endif
INFO("{} {}", exitCode, cmd);

View File

@@ -13,9 +13,9 @@ nativeUtils.withCrossLinuxArm64()
nativeUtils {
wpi {
configureDependencies {
opencvYear = "frc2024"
opencvYear = "frc2025"
niLibVersion = "2025.0.0"
opencvVersion = "4.8.0-4"
opencvVersion = "4.10.0-3"
}
}
}
@@ -41,6 +41,23 @@ nativeUtils.platformConfigs.each {
nativeUtils.platformConfigs.linuxathena.linker.args.add("-Wl,--fatal-warnings")
if (project.hasProperty('ntcoreffibuild')) {
// On windows, for ntcoreffi, use static runtime
nativeUtils.platformConfigs.each {
if (it.name.contains('windows')) {
it.cCompiler.releaseArgs.remove('/MD')
it.cppCompiler.releaseArgs.remove('/MD')
it.cCompiler.debugArgs.remove('/MDd')
it.cppCompiler.debugArgs.remove('/MDd')
it.cCompiler.releaseArgs.add('/MT')
it.cppCompiler.releaseArgs.add('/MT')
it.cCompiler.debugArgs.add('/MTd')
it.cppCompiler.debugArgs.add('/MTd')
}
}
}
model {
components {
all {

View File

@@ -1,4 +1,4 @@
def opencvVersion = '4.8.0-4'
def opencvVersion = '4.10.0-3'
if (project.hasProperty('useCpp') && project.useCpp) {
model {
@@ -22,12 +22,12 @@ if (project.hasProperty('useCpp') && project.useCpp) {
if (project.hasProperty('useJava') && project.useJava) {
dependencies {
implementation "edu.wpi.first.thirdparty.frc2024.opencv:opencv-java:${opencvVersion}"
implementation "edu.wpi.first.thirdparty.frc2025.opencv:opencv-java:${opencvVersion}"
if (!project.hasProperty('skipDev') || !project.skipDev) {
devImplementation "edu.wpi.first.thirdparty.frc2024.opencv:opencv-java:${opencvVersion}"
devImplementation "edu.wpi.first.thirdparty.frc2025.opencv:opencv-java:${opencvVersion}"
}
if (project.hasProperty('useDocumentation') && project.useDocumentation) {
javaSource "edu.wpi.first.thirdparty.frc2024.opencv:opencv-java:${opencvVersion}:sources"
javaSource "edu.wpi.first.thirdparty.frc2025.opencv:opencv-java:${opencvVersion}:sources"
}
}
}

View File

@@ -1043,16 +1043,17 @@ static void DriverStationExecute() {
bool disableDS = IsDSDisabled();
if (disableDS && !prevDisableDS) {
if (auto win = HALSimGui::manager->GetWindow("System Joysticks")) {
if (auto win = DriverStationGui::dsManager->GetWindow("System Joysticks")) {
win->SetVisibility(glass::Window::kDisabled);
}
} else if (!disableDS && prevDisableDS) {
if (auto win = HALSimGui::manager->GetWindow("System Joysticks")) {
if (auto win = DriverStationGui::dsManager->GetWindow("System Joysticks")) {
win->SetVisibility(glass::Window::kShow);
}
}
prevDisableDS = disableDS;
if (disableDS) {
gFMSModel->Update();
return;
}

View File

@@ -148,7 +148,7 @@ __declspec(dllexport)
gPlotProvider->DisplayMenu();
ImGui::EndMenu();
}
if (ImGui::BeginMenu("Window")) {
if (HALSimGui::manager->GetNumWindows() > 0 && ImGui::BeginMenu("Window")) {
HALSimGui::manager->DisplayMenu();
ImGui::EndMenu();
}

48
upstream_utils/debugging.py Executable file
View File

@@ -0,0 +1,48 @@
#!/usr/bin/env python3
import os
import shutil
from upstream_utils import Lib, walk_cwd_and_copy_if
def copy_upstream_src(wpilib_root):
wpiutil = os.path.join(wpilib_root, "wpiutil")
# Delete old install
for d in [
"src/main/native/thirdparty/debugging/src",
"src/main/native/thirdparty/debugging/include",
]:
shutil.rmtree(os.path.join(wpiutil, d), ignore_errors=True)
# Copy debugging files into allwpilib
filenames = walk_cwd_and_copy_if(
lambda dp, f: dp.startswith(os.path.join(".", "src"))
or dp.startswith(os.path.join(".", "include")),
os.path.join(wpiutil, "src/main/native/thirdparty/debugging"),
)
for filename in filenames:
with open(filename) as f:
content = f.read()
# Rename namespace from stdx to wpi
content = content.replace("namespace stdx", "namespace wpi")
with open(filename, "w") as f:
f.write(content)
def main():
name = "debugging"
url = "https://github.com/grafikrobot/debugging"
# master on 2024-11-21
tag = "c510133c44894b93afbb5be55275bfb88163a2cb"
expected = Lib(name, url, tag, copy_upstream_src)
expected.main()
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,35 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Tyler Veness <calcmogul@gmail.com>
Date: Thu, 21 Nov 2024 17:51:15 -0800
Subject: [PATCH 1/4] Guard [[gnu::flatten]] attribute
---
include/debugging.hpp | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)
diff --git a/include/debugging.hpp b/include/debugging.hpp
index 70ba724a2b6522a774931af7d7be2cee9408567a..25014a9fc65d06052089058feea7566462c01d60 100644
--- a/include/debugging.hpp
+++ b/include/debugging.hpp
@@ -7,13 +7,19 @@ namespace stdx {
bool is_debugger_present() noexcept;
-[[gnu::flatten]] inline void breakpoint() noexcept
+#if defined(__GNUC__) && !defined(__clang__)
+[[gnu::flatten]]
+#endif
+inline void breakpoint() noexcept
{
psnip_trap();
}
-[[gnu::flatten]] inline void breakpoint_if_debugging() noexcept
+#if defined(__GNUC__) && !defined(__clang__)
+[[gnu::flatten]]
+#endif
+inline void breakpoint_if_debugging() noexcept
{
if (is_debugger_present()) breakpoint();
}

View File

@@ -0,0 +1,36 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Tyler Veness <calcmogul@gmail.com>
Date: Thu, 21 Nov 2024 17:23:48 -0800
Subject: [PATCH 2/4] Remove debugger_query argument from Windows and macOS
---
src/macos.cxx | 2 +-
src/windows.cxx | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/macos.cxx b/src/macos.cxx
index bbcf6f2eec9ea479a2bea0ff06b454dc81b5d356..85dbb5f45d89680e39b4847a9aa2d5472c824f2a 100644
--- a/src/macos.cxx
+++ b/src/macos.cxx
@@ -13,7 +13,7 @@ auto exc = std::array<T, EXC_TYPES_COUNT> { {} };
namespace stdx {
-bool is_debugger_present(debugger_query q) noexcept
+bool is_debugger_present() noexcept
{
mach_msg_type_number_t count {};
auto masks = exc<exception_mask_t>;
diff --git a/src/windows.cxx b/src/windows.cxx
index eec576f415d52f63d2658012546ead2e691d7415..45d98eb27c5182de7ad11291925275fb4fdb54fb 100644
--- a/src/windows.cxx
+++ b/src/windows.cxx
@@ -9,7 +9,7 @@
namespace stdx {
-bool is_debugger_present(debugger_query q) noexcept
+bool is_debugger_present() noexcept
{
return ::IsDebuggerPresent();
}

View File

@@ -0,0 +1,22 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Tyler Veness <calcmogul@gmail.com>
Date: Thu, 21 Nov 2024 18:09:37 -0800
Subject: [PATCH 3/4] Fix exception mask type typo on macOS
---
src/macos.cxx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/macos.cxx b/src/macos.cxx
index 85dbb5f45d89680e39b4847a9aa2d5472c824f2a..2c68064742bc7883a08551b88cd5dbb9a1f38100 100644
--- a/src/macos.cxx
+++ b/src/macos.cxx
@@ -20,7 +20,7 @@ bool is_debugger_present() noexcept
auto ports = exc<mach_port_t>;
auto behaviors = exc<exception_behavior_t>;
auto flavors = exc<thread_state_flavor_t>;
- exception_mast_t mask
+ exception_mask_t mask
= EXC_MASK_ALL & ~(EXC_MASK_RESOURCE | EXC_MASK_GUARD);
kern_return_t result = task_get_exception_ports(mach_task_self(), mask,
masks.data(), &count, ports.data(), behaviors.data(), flavors.data());

View File

@@ -0,0 +1,21 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Tyler Veness <calcmogul@gmail.com>
Date: Thu, 21 Nov 2024 18:49:53 -0800
Subject: [PATCH 4/4] Remove NOMINMAX macro from Windows
---
src/windows.cxx | 1 -
1 file changed, 1 deletion(-)
diff --git a/src/windows.cxx b/src/windows.cxx
index 45d98eb27c5182de7ad11291925275fb4fdb54fb..d20ae438ef9b2de8830c6df099f0476aba395de5 100644
--- a/src/windows.cxx
+++ b/src/windows.cxx
@@ -4,7 +4,6 @@
# include <debugging.hpp>
# define WIN32_LEAN_AND_MEAN
-# define NOMINMAX
# include <Windows.h>
namespace stdx {

View File

@@ -9,42 +9,42 @@ Subject: [PATCH 12/13] Suppress stringop-overflow warning false positives
2 files changed, 14 insertions(+)
diff --git a/src/google/protobuf/io/coded_stream.h b/src/google/protobuf/io/coded_stream.h
index 6c0dd4ab4099d1d748957af8bfc5f8c59c2aa3d6..a102cec8ea0b56926f63cf9ece205c634cb6d528 100644
index 6c0dd4ab4099d1d748957af8bfc5f8c59c2aa3d6..f43b7ee044673b33410a3ebec6a501ff434d061d 100644
--- a/src/google/protobuf/io/coded_stream.h
+++ b/src/google/protobuf/io/coded_stream.h
@@ -681,7 +681,14 @@ class PROTOBUF_EXPORT EpsCopyOutputStream {
if (PROTOBUF_PREDICT_FALSE(end_ - ptr < static_cast<int>(size))) {
return WriteRawFallback(data, size, ptr);
}
+#if __GNUC__ >= 13
+#if __GNUC__ >= 12
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wstringop-overflow="
+#endif // __GNUC__ >= 13
+#endif // __GNUC__ >= 12
std::memcpy(ptr, data, size);
+#if __GNUC__ >= 13
+#if __GNUC__ >= 12
+#pragma GCC diagnostic pop
+#endif // __GNUC__ >= 13
+#endif // __GNUC__ >= 12
return ptr + size;
}
// Writes the buffer specified by data, size to the stream. Possibly by
diff --git a/src/google/protobuf/unknown_field_set.cc b/src/google/protobuf/unknown_field_set.cc
index 74c358e9a22c5475bfaef6c5ac63b05fc61b7074..c0587350b309839f3b8b99506d0417a9fd91b06d 100644
index 74c358e9a22c5475bfaef6c5ac63b05fc61b7074..5f6f7a1a298321e562112fed576a7086bd57643c 100644
--- a/src/google/protobuf/unknown_field_set.cc
+++ b/src/google/protobuf/unknown_field_set.cc
@@ -96,9 +96,16 @@ void UnknownFieldSet::MergeFromAndDestroy(UnknownFieldSet* other) {
if (fields_.empty()) {
fields_ = std::move(other->fields_);
} else {
+#if __GNUC__ >= 13
+#if __GNUC__ >= 12
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wstringop-overflow="
+#endif // __GNUC__ >= 13
+#endif // __GNUC__ >= 12
fields_.insert(fields_.end(),
std::make_move_iterator(other->fields_.begin()),
std::make_move_iterator(other->fields_.end()));
+#if __GNUC__ >= 13
+#if __GNUC__ >= 12
+#pragma GCC diagnostic pop
+#endif // __GNUC__ >= 13
+#endif // __GNUC__ >= 12
}
other->fields_.clear();
}

View File

@@ -48,8 +48,8 @@ def copy_upstream_src(wpilib_root):
def main():
name = "sleipnir"
url = "https://github.com/SleipnirGroup/Sleipnir"
# main on 2024-09-18
tag = "8bbce85252bc351c5aacb0de9f50fa31b8b9e1ae"
# main on 2024-12-07
tag = "01206ab17d741f4c45a7faeb56b8a5442df1681c"
sleipnir = Lib(name, url, tag, copy_upstream_src)
sleipnir.main()

View File

@@ -1,638 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Tyler Veness <calcmogul@gmail.com>
Date: Wed, 24 Apr 2024 15:56:06 -0700
Subject: [PATCH 1/5] Remove "using enum" declarations
---
include/sleipnir/autodiff/Expression.hpp | 161 +++++++-----------
.../optimization/SolverExitCondition.hpp | 22 ++-
2 files changed, 73 insertions(+), 110 deletions(-)
diff --git a/include/sleipnir/autodiff/Expression.hpp b/include/sleipnir/autodiff/Expression.hpp
index dd53755ccae88e3975d1b5e6b13ac464bd4efcce..51070613e82cdf5e4105519f39632deb5d2bf19e 100644
--- a/include/sleipnir/autodiff/Expression.hpp
+++ b/include/sleipnir/autodiff/Expression.hpp
@@ -203,8 +203,6 @@ struct SLEIPNIR_DLLEXPORT Expression {
*/
friend SLEIPNIR_DLLEXPORT ExpressionPtr operator*(const ExpressionPtr& lhs,
const ExpressionPtr& rhs) {
- using enum ExpressionType;
-
// Prune expression
if (lhs->IsConstant(0.0)) {
// Return zero
@@ -219,20 +217,22 @@ struct SLEIPNIR_DLLEXPORT Expression {
}
// Evaluate constant
- if (lhs->type == kConstant && rhs->type == kConstant) {
+ if (lhs->type == ExpressionType::kConstant &&
+ rhs->type == ExpressionType::kConstant) {
return MakeExpressionPtr(lhs->value * rhs->value);
}
// Evaluate expression type
ExpressionType type;
- if (lhs->type == kConstant) {
+ if (lhs->type == ExpressionType::kConstant) {
type = rhs->type;
- } else if (rhs->type == kConstant) {
+ } else if (rhs->type == ExpressionType::kConstant) {
type = lhs->type;
- } else if (lhs->type == kLinear && rhs->type == kLinear) {
- type = kQuadratic;
+ } else if (lhs->type == ExpressionType::kLinear &&
+ rhs->type == ExpressionType::kLinear) {
+ type = ExpressionType::kQuadratic;
} else {
- type = kNonlinear;
+ type = ExpressionType::kNonlinear;
}
return MakeExpressionPtr(
@@ -258,8 +258,6 @@ struct SLEIPNIR_DLLEXPORT Expression {
*/
friend SLEIPNIR_DLLEXPORT ExpressionPtr operator/(const ExpressionPtr& lhs,
const ExpressionPtr& rhs) {
- using enum ExpressionType;
-
// Prune expression
if (lhs->IsConstant(0.0)) {
// Return zero
@@ -269,16 +267,17 @@ struct SLEIPNIR_DLLEXPORT Expression {
}
// Evaluate constant
- if (lhs->type == kConstant && rhs->type == kConstant) {
+ if (lhs->type == ExpressionType::kConstant &&
+ rhs->type == ExpressionType::kConstant) {
return MakeExpressionPtr(lhs->value / rhs->value);
}
// Evaluate expression type
ExpressionType type;
- if (rhs->type == kConstant) {
+ if (rhs->type == ExpressionType::kConstant) {
type = lhs->type;
} else {
- type = kNonlinear;
+ type = ExpressionType::kNonlinear;
}
return MakeExpressionPtr(
@@ -306,8 +305,6 @@ struct SLEIPNIR_DLLEXPORT Expression {
*/
friend SLEIPNIR_DLLEXPORT ExpressionPtr operator+(const ExpressionPtr& lhs,
const ExpressionPtr& rhs) {
- using enum ExpressionType;
-
// Prune expression
if (lhs == nullptr || lhs->IsConstant(0.0)) {
return rhs;
@@ -316,7 +313,8 @@ struct SLEIPNIR_DLLEXPORT Expression {
}
// Evaluate constant
- if (lhs->type == kConstant && rhs->type == kConstant) {
+ if (lhs->type == ExpressionType::kConstant &&
+ rhs->type == ExpressionType::kConstant) {
return MakeExpressionPtr(lhs->value + rhs->value);
}
@@ -340,8 +338,6 @@ struct SLEIPNIR_DLLEXPORT Expression {
*/
friend SLEIPNIR_DLLEXPORT ExpressionPtr operator-(const ExpressionPtr& lhs,
const ExpressionPtr& rhs) {
- using enum ExpressionType;
-
// Prune expression
if (lhs->IsConstant(0.0)) {
if (rhs->IsConstant(0.0)) {
@@ -355,7 +351,8 @@ struct SLEIPNIR_DLLEXPORT Expression {
}
// Evaluate constant
- if (lhs->type == kConstant && rhs->type == kConstant) {
+ if (lhs->type == ExpressionType::kConstant &&
+ rhs->type == ExpressionType::kConstant) {
return MakeExpressionPtr(lhs->value - rhs->value);
}
@@ -377,8 +374,6 @@ struct SLEIPNIR_DLLEXPORT Expression {
* @param lhs Operand of unary minus.
*/
friend SLEIPNIR_DLLEXPORT ExpressionPtr operator-(const ExpressionPtr& lhs) {
- using enum ExpressionType;
-
// Prune expression
if (lhs->IsConstant(0.0)) {
// Return zero
@@ -386,7 +381,7 @@ struct SLEIPNIR_DLLEXPORT Expression {
}
// Evaluate constant
- if (lhs->type == kConstant) {
+ if (lhs->type == ExpressionType::kConstant) {
return MakeExpressionPtr(-lhs->value);
}
@@ -469,8 +464,6 @@ inline constexpr void IntrusiveSharedPtrDecRefCount(Expression* expr) {
*/
SLEIPNIR_DLLEXPORT inline ExpressionPtr abs( // NOLINT
const ExpressionPtr& x) {
- using enum ExpressionType;
-
// Prune expression
if (x->IsConstant(0.0)) {
// Return zero
@@ -478,12 +471,12 @@ SLEIPNIR_DLLEXPORT inline ExpressionPtr abs( // NOLINT
}
// Evaluate constant
- if (x->type == kConstant) {
+ if (x->type == ExpressionType::kConstant) {
return MakeExpressionPtr(std::abs(x->value));
}
return MakeExpressionPtr(
- kNonlinear, [](double x, double) { return std::abs(x); },
+ ExpressionType::kNonlinear, [](double x, double) { return std::abs(x); },
[](double x, double, double parentAdjoint) {
if (x < 0.0) {
return -parentAdjoint;
@@ -514,20 +507,18 @@ SLEIPNIR_DLLEXPORT inline ExpressionPtr abs( // NOLINT
*/
SLEIPNIR_DLLEXPORT inline ExpressionPtr acos( // NOLINT
const ExpressionPtr& x) {
- using enum ExpressionType;
-
// Prune expression
if (x->IsConstant(0.0)) {
return MakeExpressionPtr(std::numbers::pi / 2.0);
}
// Evaluate constant
- if (x->type == kConstant) {
+ if (x->type == ExpressionType::kConstant) {
return MakeExpressionPtr(std::acos(x->value));
}
return MakeExpressionPtr(
- kNonlinear, [](double x, double) { return std::acos(x); },
+ ExpressionType::kNonlinear, [](double x, double) { return std::acos(x); },
[](double x, double, double parentAdjoint) {
return -parentAdjoint / std::sqrt(1.0 - x * x);
},
@@ -546,8 +537,6 @@ SLEIPNIR_DLLEXPORT inline ExpressionPtr acos( // NOLINT
*/
SLEIPNIR_DLLEXPORT inline ExpressionPtr asin( // NOLINT
const ExpressionPtr& x) {
- using enum ExpressionType;
-
// Prune expression
if (x->IsConstant(0.0)) {
// Return zero
@@ -555,12 +544,12 @@ SLEIPNIR_DLLEXPORT inline ExpressionPtr asin( // NOLINT
}
// Evaluate constant
- if (x->type == kConstant) {
+ if (x->type == ExpressionType::kConstant) {
return MakeExpressionPtr(std::asin(x->value));
}
return MakeExpressionPtr(
- kNonlinear, [](double x, double) { return std::asin(x); },
+ ExpressionType::kNonlinear, [](double x, double) { return std::asin(x); },
[](double x, double, double parentAdjoint) {
return parentAdjoint / std::sqrt(1.0 - x * x);
},
@@ -579,8 +568,6 @@ SLEIPNIR_DLLEXPORT inline ExpressionPtr asin( // NOLINT
*/
SLEIPNIR_DLLEXPORT inline ExpressionPtr atan( // NOLINT
const ExpressionPtr& x) {
- using enum ExpressionType;
-
// Prune expression
if (x->IsConstant(0.0)) {
// Return zero
@@ -588,12 +575,12 @@ SLEIPNIR_DLLEXPORT inline ExpressionPtr atan( // NOLINT
}
// Evaluate constant
- if (x->type == kConstant) {
+ if (x->type == ExpressionType::kConstant) {
return MakeExpressionPtr(std::atan(x->value));
}
return MakeExpressionPtr(
- kNonlinear, [](double x, double) { return std::atan(x); },
+ ExpressionType::kNonlinear, [](double x, double) { return std::atan(x); },
[](double x, double, double parentAdjoint) {
return parentAdjoint / (1.0 + x * x);
},
@@ -612,8 +599,6 @@ SLEIPNIR_DLLEXPORT inline ExpressionPtr atan( // NOLINT
*/
SLEIPNIR_DLLEXPORT inline ExpressionPtr atan2( // NOLINT
const ExpressionPtr& y, const ExpressionPtr& x) {
- using enum ExpressionType;
-
// Prune expression
if (y->IsConstant(0.0)) {
// Return zero
@@ -623,12 +608,14 @@ SLEIPNIR_DLLEXPORT inline ExpressionPtr atan2( // NOLINT
}
// Evaluate constant
- if (y->type == kConstant && x->type == kConstant) {
+ if (y->type == ExpressionType::kConstant &&
+ x->type == ExpressionType::kConstant) {
return MakeExpressionPtr(std::atan2(y->value, x->value));
}
return MakeExpressionPtr(
- kNonlinear, [](double y, double x) { return std::atan2(y, x); },
+ ExpressionType::kNonlinear,
+ [](double y, double x) { return std::atan2(y, x); },
[](double y, double x, double parentAdjoint) {
return parentAdjoint * x / (y * y + x * x);
},
@@ -653,20 +640,18 @@ SLEIPNIR_DLLEXPORT inline ExpressionPtr atan2( // NOLINT
*/
SLEIPNIR_DLLEXPORT inline ExpressionPtr cos( // NOLINT
const ExpressionPtr& x) {
- using enum ExpressionType;
-
// Prune expression
if (x->IsConstant(0.0)) {
return MakeExpressionPtr(1.0);
}
// Evaluate constant
- if (x->type == kConstant) {
+ if (x->type == ExpressionType::kConstant) {
return MakeExpressionPtr(std::cos(x->value));
}
return MakeExpressionPtr(
- kNonlinear, [](double x, double) { return std::cos(x); },
+ ExpressionType::kNonlinear, [](double x, double) { return std::cos(x); },
[](double x, double, double parentAdjoint) {
return -parentAdjoint * std::sin(x);
},
@@ -684,20 +669,18 @@ SLEIPNIR_DLLEXPORT inline ExpressionPtr cos( // NOLINT
*/
SLEIPNIR_DLLEXPORT inline ExpressionPtr cosh( // NOLINT
const ExpressionPtr& x) {
- using enum ExpressionType;
-
// Prune expression
if (x->IsConstant(0.0)) {
return MakeExpressionPtr(1.0);
}
// Evaluate constant
- if (x->type == kConstant) {
+ if (x->type == ExpressionType::kConstant) {
return MakeExpressionPtr(std::cosh(x->value));
}
return MakeExpressionPtr(
- kNonlinear, [](double x, double) { return std::cosh(x); },
+ ExpressionType::kNonlinear, [](double x, double) { return std::cosh(x); },
[](double x, double, double parentAdjoint) {
return parentAdjoint * std::sinh(x);
},
@@ -715,8 +698,6 @@ SLEIPNIR_DLLEXPORT inline ExpressionPtr cosh( // NOLINT
*/
SLEIPNIR_DLLEXPORT inline ExpressionPtr erf( // NOLINT
const ExpressionPtr& x) {
- using enum ExpressionType;
-
// Prune expression
if (x->IsConstant(0.0)) {
// Return zero
@@ -724,12 +705,12 @@ SLEIPNIR_DLLEXPORT inline ExpressionPtr erf( // NOLINT
}
// Evaluate constant
- if (x->type == kConstant) {
+ if (x->type == ExpressionType::kConstant) {
return MakeExpressionPtr(std::erf(x->value));
}
return MakeExpressionPtr(
- kNonlinear, [](double x, double) { return std::erf(x); },
+ ExpressionType::kNonlinear, [](double x, double) { return std::erf(x); },
[](double x, double, double parentAdjoint) {
return parentAdjoint * 2.0 * std::numbers::inv_sqrtpi *
std::exp(-x * x);
@@ -750,20 +731,18 @@ SLEIPNIR_DLLEXPORT inline ExpressionPtr erf( // NOLINT
*/
SLEIPNIR_DLLEXPORT inline ExpressionPtr exp( // NOLINT
const ExpressionPtr& x) {
- using enum ExpressionType;
-
// Prune expression
if (x->IsConstant(0.0)) {
return MakeExpressionPtr(1.0);
}
// Evaluate constant
- if (x->type == kConstant) {
+ if (x->type == ExpressionType::kConstant) {
return MakeExpressionPtr(std::exp(x->value));
}
return MakeExpressionPtr(
- kNonlinear, [](double x, double) { return std::exp(x); },
+ ExpressionType::kNonlinear, [](double x, double) { return std::exp(x); },
[](double x, double, double parentAdjoint) {
return parentAdjoint * std::exp(x);
},
@@ -782,8 +761,6 @@ SLEIPNIR_DLLEXPORT inline ExpressionPtr exp( // NOLINT
*/
SLEIPNIR_DLLEXPORT inline ExpressionPtr hypot( // NOLINT
const ExpressionPtr& x, const ExpressionPtr& y) {
- using enum ExpressionType;
-
// Prune expression
if (x->IsConstant(0.0)) {
return y;
@@ -792,12 +769,14 @@ SLEIPNIR_DLLEXPORT inline ExpressionPtr hypot( // NOLINT
}
// Evaluate constant
- if (x->type == kConstant && y->type == kConstant) {
+ if (x->type == ExpressionType::kConstant &&
+ y->type == ExpressionType::kConstant) {
return MakeExpressionPtr(std::hypot(x->value, y->value));
}
return MakeExpressionPtr(
- kNonlinear, [](double x, double y) { return std::hypot(x, y); },
+ ExpressionType::kNonlinear,
+ [](double x, double y) { return std::hypot(x, y); },
[](double x, double y, double parentAdjoint) {
return parentAdjoint * x / std::hypot(x, y);
},
@@ -822,8 +801,6 @@ SLEIPNIR_DLLEXPORT inline ExpressionPtr hypot( // NOLINT
*/
SLEIPNIR_DLLEXPORT inline ExpressionPtr log( // NOLINT
const ExpressionPtr& x) {
- using enum ExpressionType;
-
// Prune expression
if (x->IsConstant(0.0)) {
// Return zero
@@ -831,12 +808,12 @@ SLEIPNIR_DLLEXPORT inline ExpressionPtr log( // NOLINT
}
// Evaluate constant
- if (x->type == kConstant) {
+ if (x->type == ExpressionType::kConstant) {
return MakeExpressionPtr(std::log(x->value));
}
return MakeExpressionPtr(
- kNonlinear, [](double x, double) { return std::log(x); },
+ ExpressionType::kNonlinear, [](double x, double) { return std::log(x); },
[](double x, double, double parentAdjoint) { return parentAdjoint / x; },
[](const ExpressionPtr& x, const ExpressionPtr&,
const ExpressionPtr& parentAdjoint) { return parentAdjoint / x; },
@@ -850,8 +827,6 @@ SLEIPNIR_DLLEXPORT inline ExpressionPtr log( // NOLINT
*/
SLEIPNIR_DLLEXPORT inline ExpressionPtr log10( // NOLINT
const ExpressionPtr& x) {
- using enum ExpressionType;
-
// Prune expression
if (x->IsConstant(0.0)) {
// Return zero
@@ -859,12 +834,13 @@ SLEIPNIR_DLLEXPORT inline ExpressionPtr log10( // NOLINT
}
// Evaluate constant
- if (x->type == kConstant) {
+ if (x->type == ExpressionType::kConstant) {
return MakeExpressionPtr(std::log10(x->value));
}
return MakeExpressionPtr(
- kNonlinear, [](double x, double) { return std::log10(x); },
+ ExpressionType::kNonlinear,
+ [](double x, double) { return std::log10(x); },
[](double x, double, double parentAdjoint) {
return parentAdjoint / (std::numbers::ln10 * x);
},
@@ -883,8 +859,6 @@ SLEIPNIR_DLLEXPORT inline ExpressionPtr log10( // NOLINT
*/
SLEIPNIR_DLLEXPORT inline ExpressionPtr pow( // NOLINT
const ExpressionPtr& base, const ExpressionPtr& power) {
- using enum ExpressionType;
-
// Prune expression
if (base->IsConstant(0.0)) {
// Return zero
@@ -899,12 +873,15 @@ SLEIPNIR_DLLEXPORT inline ExpressionPtr pow( // NOLINT
}
// Evaluate constant
- if (base->type == kConstant && power->type == kConstant) {
+ if (base->type == ExpressionType::kConstant &&
+ power->type == ExpressionType::kConstant) {
return MakeExpressionPtr(std::pow(base->value, power->value));
}
return MakeExpressionPtr(
- base->type == kLinear && power->IsConstant(2.0) ? kQuadratic : kNonlinear,
+ base->type == ExpressionType::kLinear && power->IsConstant(2.0)
+ ? ExpressionType::kQuadratic
+ : ExpressionType::kNonlinear,
[](double base, double power) { return std::pow(base, power); },
[](double base, double power, double parentAdjoint) {
return parentAdjoint * std::pow(base, power - 1) * power;
@@ -945,10 +922,8 @@ SLEIPNIR_DLLEXPORT inline ExpressionPtr pow( // NOLINT
* @param x The argument.
*/
SLEIPNIR_DLLEXPORT inline ExpressionPtr sign(const ExpressionPtr& x) {
- using enum ExpressionType;
-
// Evaluate constant
- if (x->type == kConstant) {
+ if (x->type == ExpressionType::kConstant) {
if (x->value < 0.0) {
return MakeExpressionPtr(-1.0);
} else if (x->value == 0.0) {
@@ -960,7 +935,7 @@ SLEIPNIR_DLLEXPORT inline ExpressionPtr sign(const ExpressionPtr& x) {
}
return MakeExpressionPtr(
- kNonlinear,
+ ExpressionType::kNonlinear,
[](double x, double) {
if (x < 0.0) {
return -1.0;
@@ -985,8 +960,6 @@ SLEIPNIR_DLLEXPORT inline ExpressionPtr sign(const ExpressionPtr& x) {
*/
SLEIPNIR_DLLEXPORT inline ExpressionPtr sin( // NOLINT
const ExpressionPtr& x) {
- using enum ExpressionType;
-
// Prune expression
if (x->IsConstant(0.0)) {
// Return zero
@@ -994,12 +967,12 @@ SLEIPNIR_DLLEXPORT inline ExpressionPtr sin( // NOLINT
}
// Evaluate constant
- if (x->type == kConstant) {
+ if (x->type == ExpressionType::kConstant) {
return MakeExpressionPtr(std::sin(x->value));
}
return MakeExpressionPtr(
- kNonlinear, [](double x, double) { return std::sin(x); },
+ ExpressionType::kNonlinear, [](double x, double) { return std::sin(x); },
[](double x, double, double parentAdjoint) {
return parentAdjoint * std::cos(x);
},
@@ -1016,8 +989,6 @@ SLEIPNIR_DLLEXPORT inline ExpressionPtr sin( // NOLINT
* @param x The argument.
*/
SLEIPNIR_DLLEXPORT inline ExpressionPtr sinh(const ExpressionPtr& x) {
- using enum ExpressionType;
-
// Prune expression
if (x->IsConstant(0.0)) {
// Return zero
@@ -1025,12 +996,12 @@ SLEIPNIR_DLLEXPORT inline ExpressionPtr sinh(const ExpressionPtr& x) {
}
// Evaluate constant
- if (x->type == kConstant) {
+ if (x->type == ExpressionType::kConstant) {
return MakeExpressionPtr(std::sinh(x->value));
}
return MakeExpressionPtr(
- kNonlinear, [](double x, double) { return std::sinh(x); },
+ ExpressionType::kNonlinear, [](double x, double) { return std::sinh(x); },
[](double x, double, double parentAdjoint) {
return parentAdjoint * std::cosh(x);
},
@@ -1048,10 +1019,8 @@ SLEIPNIR_DLLEXPORT inline ExpressionPtr sinh(const ExpressionPtr& x) {
*/
SLEIPNIR_DLLEXPORT inline ExpressionPtr sqrt( // NOLINT
const ExpressionPtr& x) {
- using enum ExpressionType;
-
// Evaluate constant
- if (x->type == kConstant) {
+ if (x->type == ExpressionType::kConstant) {
if (x->value == 0.0) {
// Return zero
return x;
@@ -1063,7 +1032,7 @@ SLEIPNIR_DLLEXPORT inline ExpressionPtr sqrt( // NOLINT
}
return MakeExpressionPtr(
- kNonlinear, [](double x, double) { return std::sqrt(x); },
+ ExpressionType::kNonlinear, [](double x, double) { return std::sqrt(x); },
[](double x, double, double parentAdjoint) {
return parentAdjoint / (2.0 * std::sqrt(x));
},
@@ -1082,8 +1051,6 @@ SLEIPNIR_DLLEXPORT inline ExpressionPtr sqrt( // NOLINT
*/
SLEIPNIR_DLLEXPORT inline ExpressionPtr tan( // NOLINT
const ExpressionPtr& x) {
- using enum ExpressionType;
-
// Prune expression
if (x->IsConstant(0.0)) {
// Return zero
@@ -1091,12 +1058,12 @@ SLEIPNIR_DLLEXPORT inline ExpressionPtr tan( // NOLINT
}
// Evaluate constant
- if (x->type == kConstant) {
+ if (x->type == ExpressionType::kConstant) {
return MakeExpressionPtr(std::tan(x->value));
}
return MakeExpressionPtr(
- kNonlinear, [](double x, double) { return std::tan(x); },
+ ExpressionType::kNonlinear, [](double x, double) { return std::tan(x); },
[](double x, double, double parentAdjoint) {
return parentAdjoint / (std::cos(x) * std::cos(x));
},
@@ -1114,8 +1081,6 @@ SLEIPNIR_DLLEXPORT inline ExpressionPtr tan( // NOLINT
* @param x The argument.
*/
SLEIPNIR_DLLEXPORT inline ExpressionPtr tanh(const ExpressionPtr& x) {
- using enum ExpressionType;
-
// Prune expression
if (x->IsConstant(0.0)) {
// Return zero
@@ -1123,12 +1088,12 @@ SLEIPNIR_DLLEXPORT inline ExpressionPtr tanh(const ExpressionPtr& x) {
}
// Evaluate constant
- if (x->type == kConstant) {
+ if (x->type == ExpressionType::kConstant) {
return MakeExpressionPtr(std::tanh(x->value));
}
return MakeExpressionPtr(
- kNonlinear, [](double x, double) { return std::tanh(x); },
+ ExpressionType::kNonlinear, [](double x, double) { return std::tanh(x); },
[](double x, double, double parentAdjoint) {
return parentAdjoint / (std::cosh(x) * std::cosh(x));
},
diff --git a/include/sleipnir/optimization/SolverExitCondition.hpp b/include/sleipnir/optimization/SolverExitCondition.hpp
index 7d1445297e33e3c62bcdf9d03eebeaad20af9a1c..734cd3d127327e8ce01e1a42fe74ccc81fea1f90 100644
--- a/include/sleipnir/optimization/SolverExitCondition.hpp
+++ b/include/sleipnir/optimization/SolverExitCondition.hpp
@@ -46,31 +46,29 @@ enum class SolverExitCondition : int8_t {
*/
SLEIPNIR_DLLEXPORT constexpr std::string_view ToMessage(
const SolverExitCondition& exitCondition) {
- using enum SolverExitCondition;
-
switch (exitCondition) {
- case kSuccess:
+ case SolverExitCondition::kSuccess:
return "solved to desired tolerance";
- case kSolvedToAcceptableTolerance:
+ case SolverExitCondition::kSolvedToAcceptableTolerance:
return "solved to acceptable tolerance";
- case kCallbackRequestedStop:
+ case SolverExitCondition::kCallbackRequestedStop:
return "callback requested stop";
- case kTooFewDOFs:
+ case SolverExitCondition::kTooFewDOFs:
return "problem has too few degrees of freedom";
- case kLocallyInfeasible:
+ case SolverExitCondition::kLocallyInfeasible:
return "problem is locally infeasible";
- case kFeasibilityRestorationFailed:
+ case SolverExitCondition::kFeasibilityRestorationFailed:
return "solver failed to reach the desired tolerance, and feasibility "
"restoration failed to converge";
- case kNonfiniteInitialCostOrConstraints:
+ case SolverExitCondition::kNonfiniteInitialCostOrConstraints:
return "solver encountered nonfinite initial cost or constraints and "
"gave up";
- case kDivergingIterates:
+ case SolverExitCondition::kDivergingIterates:
return "solver encountered diverging primal iterates xₖ and/or sₖ and "
"gave up";
- case kMaxIterationsExceeded:
+ case SolverExitCondition::kMaxIterationsExceeded:
return "solution returned after maximum iterations exceeded";
- case kTimeout:
+ case SolverExitCondition::kTimeout:
return "solution returned after maximum wall clock time exceeded";
default:
return "unknown";

View File

@@ -1,7 +1,7 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Tyler Veness <calcmogul@gmail.com>
Date: Wed, 29 May 2024 16:29:55 -0700
Subject: [PATCH 2/5] Use fmtlib
Subject: [PATCH 1/3] Use fmtlib
---
include/.styleguide | 1 +

View File

@@ -1,25 +1,26 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Tyler Veness <calcmogul@gmail.com>
Date: Sun, 16 Jun 2024 12:08:49 -0700
Subject: [PATCH 4/5] Use wpi::SmallVector
Subject: [PATCH 2/3] Use wpi::SmallVector
---
include/.styleguide | 1 +
include/sleipnir/autodiff/Expression.hpp | 5 +++--
include/sleipnir/autodiff/ExpressionGraph.hpp | 15 ++++++++-------
include/sleipnir/autodiff/Hessian.hpp | 4 ++--
include/sleipnir/autodiff/Jacobian.hpp | 10 +++++-----
include/sleipnir/autodiff/Variable.hpp | 10 +++++-----
include/sleipnir/autodiff/VariableMatrix.hpp | 4 ++--
include/sleipnir/optimization/Multistart.hpp | 7 ++++---
.../sleipnir/optimization/OptimizationProblem.hpp | 8 ++++----
include/sleipnir/util/Pool.hpp | 7 ++++---
include/sleipnir/util/Spy.hpp | 4 ++--
src/.styleguide | 1 +
src/optimization/solver/InteriorPoint.cpp | 4 ++--
.../solver/util/FeasibilityRestoration.hpp | 10 +++++-----
src/optimization/solver/util/Filter.hpp | 4 ++--
15 files changed, 50 insertions(+), 44 deletions(-)
include/.styleguide | 1 +
include/sleipnir/autodiff/Expression.hpp | 13 +++++++------
include/sleipnir/autodiff/ExpressionGraph.hpp | 15 ++++++++-------
include/sleipnir/autodiff/Hessian.hpp | 4 ++--
include/sleipnir/autodiff/Jacobian.hpp | 10 +++++-----
include/sleipnir/autodiff/Variable.hpp | 10 +++++-----
include/sleipnir/autodiff/VariableMatrix.hpp | 4 ++--
include/sleipnir/optimization/Multistart.hpp | 7 ++++---
.../optimization/OptimizationProblem.hpp | 8 ++++----
include/sleipnir/util/Pool.hpp | 7 ++++---
include/sleipnir/util/Spy.hpp | 4 ++--
src/.styleguide | 1 +
src/optimization/solver/InteriorPoint.cpp | 4 ++--
src/optimization/solver/SQP.cpp | 4 ++--
.../solver/util/FeasibilityRestoration.hpp | 18 +++++++++---------
src/optimization/solver/util/Filter.hpp | 4 ++--
16 files changed, 60 insertions(+), 54 deletions(-)
diff --git a/include/.styleguide b/include/.styleguide
index 6a7f8ed28f9cb037c9746a7e0ef5e110481d9825..efa36cee1fb593ae810032340c64f1854fbbc523 100644
@@ -32,7 +33,7 @@ index 6a7f8ed28f9cb037c9746a7e0ef5e110481d9825..efa36cee1fb593ae810032340c64f185
+ ^wpi/
}
diff --git a/include/sleipnir/autodiff/Expression.hpp b/include/sleipnir/autodiff/Expression.hpp
index dff8e2a6ef24413e3e6356bf0ec57286e50654cf..bdbeb4730223f88cfdc0c424a8f7b852b34742f7 100644
index dd53755ccae88e3975d1b5e6b13ac464bd4efcce..ef9a15bf69d8cae6b2196513b72ec4b359cc8752 100644
--- a/include/sleipnir/autodiff/Expression.hpp
+++ b/include/sleipnir/autodiff/Expression.hpp
@@ -11,11 +11,12 @@
@@ -49,7 +50,33 @@ index dff8e2a6ef24413e3e6356bf0ec57286e50654cf..bdbeb4730223f88cfdc0c424a8f7b852
namespace sleipnir::detail {
@@ -427,7 +428,7 @@ inline void IntrusiveSharedPtrDecRefCount(Expression* expr) {
@@ -29,8 +30,8 @@ inline constexpr bool kUsePoolAllocator = true;
struct SLEIPNIR_DLLEXPORT Expression;
-inline constexpr void IntrusiveSharedPtrIncRefCount(Expression* expr);
-inline constexpr void IntrusiveSharedPtrDecRefCount(Expression* expr);
+inline void IntrusiveSharedPtrIncRefCount(Expression* expr);
+inline void IntrusiveSharedPtrDecRefCount(Expression* expr);
/**
* Typedef for intrusive shared pointer to Expression.
@@ -418,7 +419,7 @@ SLEIPNIR_DLLEXPORT inline ExpressionPtr sqrt(const ExpressionPtr& x);
*
* @param expr The shared pointer's managed object.
*/
-inline constexpr void IntrusiveSharedPtrIncRefCount(Expression* expr) {
+inline void IntrusiveSharedPtrIncRefCount(Expression* expr) {
++expr->refCount;
}
@@ -427,12 +428,12 @@ inline constexpr void IntrusiveSharedPtrIncRefCount(Expression* expr) {
*
* @param expr The shared pointer's managed object.
*/
-inline constexpr void IntrusiveSharedPtrDecRefCount(Expression* expr) {
+inline void IntrusiveSharedPtrDecRefCount(Expression* expr) {
// If a deeply nested tree is being deallocated all at once, calling the
// Expression destructor when expr's refcount reaches zero can cause a stack
// overflow. Instead, we iterate over its children to decrement their
// refcounts and deallocate them.
@@ -308,7 +335,7 @@ index 8055713a2492a9c8473f047a2fb9fe7ca57753c3..09b1b2f3bf33c35ae0aeddb9b5d47c0d
for (auto& future : futures) {
diff --git a/include/sleipnir/optimization/OptimizationProblem.hpp b/include/sleipnir/optimization/OptimizationProblem.hpp
index 28b2943f5842229335e79eae14abbda6ff5b7000..e812fa5e3454903d4dfb8572539ebf1b506bdf11 100644
index 569dcdee507881ceb412585ca811927072552c15..66883fed98ad087010fb153bd91effce6e047928 100644
--- a/include/sleipnir/optimization/OptimizationProblem.hpp
+++ b/include/sleipnir/optimization/OptimizationProblem.hpp
@@ -11,6 +11,7 @@
@@ -319,15 +346,15 @@ index 28b2943f5842229335e79eae14abbda6ff5b7000..e812fa5e3454903d4dfb8572539ebf1b
#include "sleipnir/autodiff/Variable.hpp"
#include "sleipnir/autodiff/VariableMatrix.hpp"
@@ -21,7 +22,6 @@
#include "sleipnir/optimization/solver/InteriorPoint.hpp"
@@ -22,7 +23,6 @@
#include "sleipnir/optimization/solver/SQP.hpp"
#include "sleipnir/util/Print.hpp"
#include "sleipnir/util/SymbolExports.hpp"
-#include "sleipnir/util/small_vector.hpp"
namespace sleipnir {
@@ -358,16 +358,16 @@ class SLEIPNIR_DLLEXPORT OptimizationProblem {
@@ -364,16 +364,16 @@ class SLEIPNIR_DLLEXPORT OptimizationProblem {
private:
// The list of decision variables, which are the root of the problem's
// expression tree
@@ -408,7 +435,7 @@ index f3b2f0cf9e60b3a86b9654ff2b381f9c48734ff6..ad739cea6dce6f6cb586f538d1d30b92
+ ^wpi/
}
diff --git a/src/optimization/solver/InteriorPoint.cpp b/src/optimization/solver/InteriorPoint.cpp
index dcd8b56dc08516b80f89550c43cb7002745b93d8..892d2dd20f7fe92c2c91518a4ecb487425643585 100644
index a09d9866d05731c8ce53122b3d1a850803883df4..d3981c59d163927e3e5ba602c3323f6e1429c475 100644
--- a/src/optimization/solver/InteriorPoint.cpp
+++ b/src/optimization/solver/InteriorPoint.cpp
@@ -9,6 +9,7 @@
@@ -436,8 +463,37 @@ index dcd8b56dc08516b80f89550c43cb7002745b93d8..892d2dd20f7fe92c2c91518a4ecb4874
RegularizedLDLT solver;
diff --git a/src/optimization/solver/SQP.cpp b/src/optimization/solver/SQP.cpp
index 77b9632e1da37361c995a8579d1d83a2756d6d88..662abc2fb6e311767b0fbb3a61121ce78549a3f6 100644
--- a/src/optimization/solver/SQP.cpp
+++ b/src/optimization/solver/SQP.cpp
@@ -9,6 +9,7 @@
#include <limits>
#include <Eigen/SparseCholesky>
+#include <wpi/SmallVector.h>
#include "optimization/RegularizedLDLT.hpp"
#include "optimization/solver/util/ErrorEstimate.hpp"
@@ -22,7 +23,6 @@
#include "sleipnir/optimization/SolverExitCondition.hpp"
#include "sleipnir/util/Print.hpp"
#include "sleipnir/util/Spy.hpp"
-#include "sleipnir/util/small_vector.hpp"
#include "util/ScopeExit.hpp"
#include "util/ToMilliseconds.hpp"
@@ -155,7 +155,7 @@ void SQP(std::span<Variable> decisionVariables,
Filter filter{f};
// Kept outside the loop so its storage can be reused
- small_vector<Eigen::Triplet<double>> triplets;
+ wpi::SmallVector<Eigen::Triplet<double>> triplets;
RegularizedLDLT solver;
diff --git a/src/optimization/solver/util/FeasibilityRestoration.hpp b/src/optimization/solver/util/FeasibilityRestoration.hpp
index c324ef093cc7dc8ce93af2cba337042a65b28475..0f13676aea0e80549ef1ef43e4972d5498acaa18 100644
index feefe137adf0832b094a36d61201b15962138ded..79b5d99ae27de6049ba098888a965291e6b677fa 100644
--- a/src/optimization/solver/util/FeasibilityRestoration.hpp
+++ b/src/optimization/solver/util/FeasibilityRestoration.hpp
@@ -8,6 +8,7 @@
@@ -456,16 +512,16 @@ index c324ef093cc7dc8ce93af2cba337042a65b28475..0f13676aea0e80549ef1ef43e4972d54
namespace sleipnir {
@@ -65,7 +65,7 @@ inline void FeasibilityRestoration(
@@ -57,7 +57,7 @@ inline void FeasibilityRestoration(
constexpr double ρ = 1000.0;
double μ = config.tolerance / 10.0;
- small_vector<Variable> fr_decisionVariables;
+ wpi::SmallVector<Variable> fr_decisionVariables;
fr_decisionVariables.reserve(decisionVariables.size() +
2 * equalityConstraints.size() +
2 * inequalityConstraints.size());
@@ -81,7 +81,7 @@ inline void FeasibilityRestoration(
2 * equalityConstraints.size());
@@ -70,7 +70,7 @@ inline void FeasibilityRestoration(
fr_decisionVariables.emplace_back();
}
@@ -474,7 +530,7 @@ index c324ef093cc7dc8ce93af2cba337042a65b28475..0f13676aea0e80549ef1ef43e4972d54
VariableMatrix xAD{std::span{it, it + decisionVariables.size()}};
it += decisionVariables.size();
@@ -157,7 +157,7 @@ inline void FeasibilityRestoration(
@@ -128,7 +128,7 @@ inline void FeasibilityRestoration(
}
// cₑ(x) - pₑ + nₑ = 0
@@ -483,7 +539,43 @@ index c324ef093cc7dc8ce93af2cba337042a65b28475..0f13676aea0e80549ef1ef43e4972d54
fr_equalityConstraints.assign(equalityConstraints.begin(),
equalityConstraints.end());
for (size_t row = 0; row < fr_equalityConstraints.size(); ++row) {
@@ -166,7 +166,7 @@ inline void FeasibilityRestoration(
@@ -137,7 +137,7 @@ inline void FeasibilityRestoration(
}
// cᵢ(x) - s - pᵢ + nᵢ = 0
- small_vector<Variable> fr_inequalityConstraints;
+ wpi::SmallVector<Variable> fr_inequalityConstraints;
// pₑ ≥ 0
std::copy(p_e.begin(), p_e.end(),
@@ -227,7 +227,7 @@ inline void FeasibilityRestoration(
constexpr double ρ = 1000.0;
- small_vector<Variable> fr_decisionVariables;
+ wpi::SmallVector<Variable> fr_decisionVariables;
fr_decisionVariables.reserve(decisionVariables.size() +
2 * equalityConstraints.size() +
2 * inequalityConstraints.size());
@@ -243,7 +243,7 @@ inline void FeasibilityRestoration(
fr_decisionVariables.emplace_back();
}
- auto it = fr_decisionVariables.cbegin();
+ auto it = fr_decisionVariables.begin();
VariableMatrix xAD{std::span{it, it + decisionVariables.size()}};
it += decisionVariables.size();
@@ -319,7 +319,7 @@ inline void FeasibilityRestoration(
}
// cₑ(x) - pₑ + nₑ = 0
- small_vector<Variable> fr_equalityConstraints;
+ wpi::SmallVector<Variable> fr_equalityConstraints;
fr_equalityConstraints.assign(equalityConstraints.begin(),
equalityConstraints.end());
for (size_t row = 0; row < fr_equalityConstraints.size(); ++row) {
@@ -328,7 +328,7 @@ inline void FeasibilityRestoration(
}
// cᵢ(x) - s - pᵢ + nᵢ = 0
@@ -493,7 +585,7 @@ index c324ef093cc7dc8ce93af2cba337042a65b28475..0f13676aea0e80549ef1ef43e4972d54
inequalityConstraints.end());
for (size_t row = 0; row < fr_inequalityConstraints.size(); ++row) {
diff --git a/src/optimization/solver/util/Filter.hpp b/src/optimization/solver/util/Filter.hpp
index 3fbb849edc4a6b3336f94b5af9e018a37b07b123..02bdb5a8db5c80dd86d17ea4421ec564d7e0a2c7 100644
index f19236928c59623bc0a3ce87b659f0756997f821..0c14efd7b8afa6cef398f5a7d383c54dbf64ec68 100644
--- a/src/optimization/solver/util/Filter.hpp
+++ b/src/optimization/solver/util/Filter.hpp
@@ -8,9 +8,9 @@
@@ -505,12 +597,12 @@ index 3fbb849edc4a6b3336f94b5af9e018a37b07b123..02bdb5a8db5c80dd86d17ea4421ec564
#include "sleipnir/autodiff/Variable.hpp"
-#include "sleipnir/util/small_vector.hpp"
namespace sleipnir {
// See docs/algorithms.md#Works_cited for citation definitions.
@@ -177,7 +177,7 @@ class Filter {
@@ -182,7 +182,7 @@ class Filter {
private:
Variable* m_f = nullptr;
double m_μ = 0.0;
- small_vector<FilterEntry> m_filter;
+ wpi::SmallVector<FilterEntry> m_filter;
};

View File

@@ -1,42 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Tyler Veness <calcmogul@gmail.com>
Date: Mon, 20 May 2024 09:01:54 -0700
Subject: [PATCH 3/5] Remove unsupported constexpr
---
include/sleipnir/autodiff/Expression.hpp | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/include/sleipnir/autodiff/Expression.hpp b/include/sleipnir/autodiff/Expression.hpp
index 51070613e82cdf5e4105519f39632deb5d2bf19e..dff8e2a6ef24413e3e6356bf0ec57286e50654cf 100644
--- a/include/sleipnir/autodiff/Expression.hpp
+++ b/include/sleipnir/autodiff/Expression.hpp
@@ -29,8 +29,8 @@ inline constexpr bool kUsePoolAllocator = true;
struct SLEIPNIR_DLLEXPORT Expression;
-inline constexpr void IntrusiveSharedPtrIncRefCount(Expression* expr);
-inline constexpr void IntrusiveSharedPtrDecRefCount(Expression* expr);
+inline void IntrusiveSharedPtrIncRefCount(Expression* expr);
+inline void IntrusiveSharedPtrDecRefCount(Expression* expr);
/**
* Typedef for intrusive shared pointer to Expression.
@@ -413,7 +413,7 @@ SLEIPNIR_DLLEXPORT inline ExpressionPtr sqrt(const ExpressionPtr& x);
*
* @param expr The shared pointer's managed object.
*/
-inline constexpr void IntrusiveSharedPtrIncRefCount(Expression* expr) {
+inline void IntrusiveSharedPtrIncRefCount(Expression* expr) {
++expr->refCount;
}
@@ -422,7 +422,7 @@ inline constexpr void IntrusiveSharedPtrIncRefCount(Expression* expr) {
*
* @param expr The shared pointer's managed object.
*/
-inline constexpr void IntrusiveSharedPtrDecRefCount(Expression* expr) {
+inline void IntrusiveSharedPtrDecRefCount(Expression* expr) {
// If a deeply nested tree is being deallocated all at once, calling the
// Expression destructor when expr's refcount reaches zero can cause a stack
// overflow. Instead, we iterate over its children to decrement their

View File

@@ -1,7 +1,7 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Tyler Veness <calcmogul@gmail.com>
Date: Wed, 26 Jun 2024 12:13:33 -0700
Subject: [PATCH 5/5] Suppress clang-tidy false positives
Subject: [PATCH 3/3] Suppress clang-tidy false positives
---
include/sleipnir/autodiff/Variable.hpp | 4 ++--

View File

@@ -55,9 +55,10 @@ def copy_to(files, root, rename_c_to_cpp=False):
for f in files:
dest_file = os.path.join(root, f)
# Rename .cc file to .cpp
if dest_file.endswith(".cc"):
# Rename .cc or .cxx file to .cpp
if dest_file.endswith(".cc") or dest_file.endswith(".cxx"):
dest_file = os.path.splitext(dest_file)[0] + ".cpp"
if rename_c_to_cpp and dest_file.endswith(".c"):
dest_file = os.path.splitext(dest_file)[0] + ".cpp"

View File

@@ -287,6 +287,24 @@ public abstract class Command implements Sendable {
return group;
}
/**
* Creates a new command that runs this command and the deadline in parallel, finishing (and
* interrupting this command) when the deadline finishes.
*
* <p>Note: This decorator works by adding this command to a composition. The command the
* decorator was called on cannot be scheduled independently or be added to a different
* composition (namely, decorators), unless it is manually cleared from the list of composed
* commands with {@link CommandScheduler#removeComposedCommand(Command)}. The command composition
* returned from this method can be further decorated without issue.
*
* @param deadline the deadline of the command group
* @return the decorated command
* @see Command#deadlineFor
*/
public ParallelDeadlineGroup withDeadline(Command deadline) {
return new ParallelDeadlineGroup(deadline, this);
}
/**
* Decorates this command with a set of commands to run parallel to it, ending when the calling
* command ends and interrupting all the others. Often more convenient/less-verbose than
@@ -321,6 +339,7 @@ public abstract class Command implements Sendable {
* @param parallel the commands to run in parallel. Note the parallel commands will be interrupted
* when the deadline command ends
* @return the decorated command
* @see Command#withDeadline
*/
public ParallelDeadlineGroup deadlineFor(Command... parallel) {
return new ParallelDeadlineGroup(this, parallel);

View File

@@ -4,8 +4,6 @@
package edu.wpi.first.wpilibj2.command;
import static edu.wpi.first.units.Units.MetersPerSecond;
import static edu.wpi.first.units.Units.Volts;
import static edu.wpi.first.util.ErrorMessages.requireNonNullParam;
import edu.wpi.first.math.controller.HolonomicDriveController;
@@ -19,7 +17,6 @@ import edu.wpi.first.math.kinematics.MecanumDriveKinematics;
import edu.wpi.first.math.kinematics.MecanumDriveMotorVoltages;
import edu.wpi.first.math.kinematics.MecanumDriveWheelSpeeds;
import edu.wpi.first.math.trajectory.Trajectory;
import edu.wpi.first.units.measure.MutLinearVelocity;
import edu.wpi.first.wpilibj.Timer;
import java.util.function.Consumer;
import java.util.function.Supplier;
@@ -41,6 +38,7 @@ import java.util.function.Supplier;
*
* <p>This class is provided by the NewCommands VendorDep
*/
@SuppressWarnings("removal")
public class MecanumControllerCommand extends Command {
private final Timer m_timer = new Timer();
private final boolean m_usePID;
@@ -56,16 +54,12 @@ public class MecanumControllerCommand extends Command {
private final PIDController m_frontRightController;
private final PIDController m_rearRightController;
private final Supplier<MecanumDriveWheelSpeeds> m_currentWheelSpeeds;
private final Consumer<MecanumDriveMotorVoltages> m_outputDriveVoltages;
private final MecanumVoltagesConsumer m_outputDriveVoltages;
private final Consumer<MecanumDriveWheelSpeeds> m_outputWheelSpeeds;
private final MutLinearVelocity m_prevFrontLeftSpeedSetpoint = MetersPerSecond.mutable(0);
private final MutLinearVelocity m_prevRearLeftSpeedSetpoint = MetersPerSecond.mutable(0);
private final MutLinearVelocity m_prevFrontRightSpeedSetpoint = MetersPerSecond.mutable(0);
private final MutLinearVelocity m_prevRearRightSpeedSetpoint = MetersPerSecond.mutable(0);
private final MutLinearVelocity m_frontLeftSpeedSetpoint = MetersPerSecond.mutable(0);
private final MutLinearVelocity m_rearLeftSpeedSetpoint = MetersPerSecond.mutable(0);
private final MutLinearVelocity m_frontRightSpeedSetpoint = MetersPerSecond.mutable(0);
private final MutLinearVelocity m_rearRightSpeedSetpoint = MetersPerSecond.mutable(0);
private double m_prevFrontLeftSpeedSetpoint; // m/s
private double m_prevRearLeftSpeedSetpoint; // m/s
private double m_prevFrontRightSpeedSetpoint; // m/s
private double m_prevRearRightSpeedSetpoint; // m/s
/**
* Constructs a new MecanumControllerCommand that when executed will follow the provided
@@ -91,8 +85,7 @@ public class MecanumControllerCommand extends Command {
* @param frontRightController The front right wheel velocity PID.
* @param rearRightController The rear right wheel velocity PID.
* @param currentWheelSpeeds A MecanumDriveWheelSpeeds object containing the current wheel speeds.
* @param outputDriveVoltages A MecanumDriveMotorVoltages object containing the output motor
* voltages.
* @param outputDriveVoltages A MecanumVoltagesConsumer that consumes voltages of mecanum motors.
* @param requirements The subsystems to require.
*/
@SuppressWarnings("this-escape")
@@ -111,7 +104,7 @@ public class MecanumControllerCommand extends Command {
PIDController frontRightController,
PIDController rearRightController,
Supplier<MecanumDriveWheelSpeeds> currentWheelSpeeds,
Consumer<MecanumDriveMotorVoltages> outputDriveVoltages,
MecanumVoltagesConsumer outputDriveVoltages,
Subsystem... requirements) {
m_trajectory = requireNonNullParam(trajectory, "trajectory", "MecanumControllerCommand");
m_pose = requireNonNullParam(pose, "pose", "MecanumControllerCommand");
@@ -152,6 +145,139 @@ public class MecanumControllerCommand extends Command {
addRequirements(requirements);
}
/**
* Constructs a new MecanumControllerCommand that when executed will follow the provided
* trajectory. PID control and feedforward are handled internally. Outputs are scaled from -12 to
* 12 as a voltage output to the motor.
*
* <p>Note: The controllers will *not* set the outputVolts to zero upon completion of the path
* this is left to the user, since it is not appropriate for paths with nonstationary endstates.
*
* @param trajectory The trajectory to follow.
* @param pose A function that supplies the robot pose - use one of the odometry classes to
* provide this.
* @param feedforward The feedforward to use for the drivetrain.
* @param kinematics The kinematics for the robot drivetrain.
* @param xController The Trajectory Tracker PID controller for the robot's x position.
* @param yController The Trajectory Tracker PID controller for the robot's y position.
* @param thetaController The Trajectory Tracker PID controller for angle for the robot.
* @param desiredRotation The angle that the robot should be facing. This is sampled at each time
* step.
* @param maxWheelVelocityMetersPerSecond The maximum velocity of a drivetrain wheel.
* @param frontLeftController The front left wheel velocity PID.
* @param rearLeftController The rear left wheel velocity PID.
* @param frontRightController The front right wheel velocity PID.
* @param rearRightController The rear right wheel velocity PID.
* @param currentWheelSpeeds A MecanumDriveWheelSpeeds object containing the current wheel speeds.
* @param outputDriveVoltages A MecanumDriveMotorVoltages object containing the output motor
* voltages.
* @param requirements The subsystems to require.
*/
@Deprecated(since = "2025", forRemoval = true)
public MecanumControllerCommand(
Trajectory trajectory,
Supplier<Pose2d> pose,
SimpleMotorFeedforward feedforward,
MecanumDriveKinematics kinematics,
PIDController xController,
PIDController yController,
ProfiledPIDController thetaController,
Supplier<Rotation2d> desiredRotation,
double maxWheelVelocityMetersPerSecond,
PIDController frontLeftController,
PIDController rearLeftController,
PIDController frontRightController,
PIDController rearRightController,
Supplier<MecanumDriveWheelSpeeds> currentWheelSpeeds,
Consumer<MecanumDriveMotorVoltages> outputDriveVoltages,
Subsystem... requirements) {
this(
trajectory,
pose,
feedforward,
kinematics,
xController,
yController,
thetaController,
desiredRotation,
maxWheelVelocityMetersPerSecond,
frontLeftController,
rearLeftController,
frontRightController,
rearRightController,
currentWheelSpeeds,
(frontLeft, frontRight, rearLeft, rearRight) ->
outputDriveVoltages.accept(
new MecanumDriveMotorVoltages(frontLeft, frontRight, rearLeft, rearRight)),
requirements);
}
/**
* Constructs a new MecanumControllerCommand that when executed will follow the provided
* trajectory. PID control and feedforward are handled internally. Outputs are scaled from -12 to
* 12 as a voltage output to the motor.
*
* <p>Note: The controllers will *not* set the outputVolts to zero upon completion of the path
* this is left to the user, since it is not appropriate for paths with nonstationary endstates.
*
* <p>Note 2: The final rotation of the robot will be set to the rotation of the final pose in the
* trajectory. The robot will not follow the rotations from the poses at each timestep. If
* alternate rotation behavior is desired, the other constructor with a supplier for rotation
* should be used.
*
* @param trajectory The trajectory to follow.
* @param pose A function that supplies the robot pose - use one of the odometry classes to
* provide this.
* @param feedforward The feedforward to use for the drivetrain.
* @param kinematics The kinematics for the robot drivetrain.
* @param xController The Trajectory Tracker PID controller for the robot's x position.
* @param yController The Trajectory Tracker PID controller for the robot's y position.
* @param thetaController The Trajectory Tracker PID controller for angle for the robot.
* @param maxWheelVelocityMetersPerSecond The maximum velocity of a drivetrain wheel.
* @param frontLeftController The front left wheel velocity PID.
* @param rearLeftController The rear left wheel velocity PID.
* @param frontRightController The front right wheel velocity PID.
* @param rearRightController The rear right wheel velocity PID.
* @param currentWheelSpeeds A MecanumDriveWheelSpeeds object containing the current wheel speeds.
* @param outputDriveVoltages A MecanumVoltagesConsumer that consumes voltages of mecanum motors.
* @param requirements The subsystems to require.
*/
public MecanumControllerCommand(
Trajectory trajectory,
Supplier<Pose2d> pose,
SimpleMotorFeedforward feedforward,
MecanumDriveKinematics kinematics,
PIDController xController,
PIDController yController,
ProfiledPIDController thetaController,
double maxWheelVelocityMetersPerSecond,
PIDController frontLeftController,
PIDController rearLeftController,
PIDController frontRightController,
PIDController rearRightController,
Supplier<MecanumDriveWheelSpeeds> currentWheelSpeeds,
MecanumVoltagesConsumer outputDriveVoltages,
Subsystem... requirements) {
this(
trajectory,
pose,
feedforward,
kinematics,
xController,
yController,
thetaController,
() ->
trajectory.getStates().get(trajectory.getStates().size() - 1).poseMeters.getRotation(),
maxWheelVelocityMetersPerSecond,
frontLeftController,
rearLeftController,
frontRightController,
rearRightController,
currentWheelSpeeds,
outputDriveVoltages,
requirements);
}
/**
* Constructs a new MecanumControllerCommand that when executed will follow the provided
* trajectory. PID control and feedforward are handled internally. Outputs are scaled from -12 to
@@ -183,6 +309,7 @@ public class MecanumControllerCommand extends Command {
* voltages.
* @param requirements The subsystems to require.
*/
@Deprecated(since = "2025", forRemoval = true)
public MecanumControllerCommand(
Trajectory trajectory,
Supplier<Pose2d> pose,
@@ -207,15 +334,15 @@ public class MecanumControllerCommand extends Command {
xController,
yController,
thetaController,
() ->
trajectory.getStates().get(trajectory.getStates().size() - 1).poseMeters.getRotation(),
maxWheelVelocityMetersPerSecond,
frontLeftController,
rearLeftController,
frontRightController,
rearRightController,
currentWheelSpeeds,
outputDriveVoltages,
(frontLeft, frontRight, rearLeft, rearRight) ->
outputDriveVoltages.accept(
new MecanumDriveMotorVoltages(frontLeft, frontRight, rearLeft, rearRight)),
requirements);
}
@@ -343,10 +470,10 @@ public class MecanumControllerCommand extends Command {
MecanumDriveWheelSpeeds prevSpeeds =
m_kinematics.toWheelSpeeds(new ChassisSpeeds(initialXVelocity, initialYVelocity, 0.0));
m_prevFrontLeftSpeedSetpoint.mut_setMagnitude(prevSpeeds.frontLeftMetersPerSecond);
m_prevRearLeftSpeedSetpoint.mut_setMagnitude(prevSpeeds.rearLeftMetersPerSecond);
m_prevFrontRightSpeedSetpoint.mut_setMagnitude(prevSpeeds.frontRightMetersPerSecond);
m_prevRearRightSpeedSetpoint.mut_setMagnitude(prevSpeeds.rearRightMetersPerSecond);
m_prevFrontLeftSpeedSetpoint = prevSpeeds.frontLeftMetersPerSecond;
m_prevRearLeftSpeedSetpoint = prevSpeeds.rearLeftMetersPerSecond;
m_prevFrontRightSpeedSetpoint = prevSpeeds.frontRightMetersPerSecond;
m_prevRearRightSpeedSetpoint = prevSpeeds.rearRightMetersPerSecond;
m_timer.restart();
}
@@ -363,10 +490,10 @@ public class MecanumControllerCommand extends Command {
targetWheelSpeeds.desaturate(m_maxWheelVelocityMetersPerSecond);
m_frontLeftSpeedSetpoint.mut_setMagnitude(targetWheelSpeeds.frontLeftMetersPerSecond);
m_rearLeftSpeedSetpoint.mut_setMagnitude(targetWheelSpeeds.rearLeftMetersPerSecond);
m_frontRightSpeedSetpoint.mut_setMagnitude(targetWheelSpeeds.frontRightMetersPerSecond);
m_rearRightSpeedSetpoint.mut_setMagnitude(targetWheelSpeeds.rearRightMetersPerSecond);
double frontLeftSpeedSetpoint = targetWheelSpeeds.frontLeftMetersPerSecond;
double rearLeftSpeedSetpoint = targetWheelSpeeds.rearLeftMetersPerSecond;
double frontRightSpeedSetpoint = targetWheelSpeeds.frontRightMetersPerSecond;
double rearRightSpeedSetpoint = targetWheelSpeeds.rearRightMetersPerSecond;
double frontLeftOutput;
double rearLeftOutput;
@@ -375,54 +502,50 @@ public class MecanumControllerCommand extends Command {
if (m_usePID) {
final double frontLeftFeedforward =
m_feedforward.calculate(m_prevFrontLeftSpeedSetpoint, m_frontLeftSpeedSetpoint).in(Volts);
m_feedforward.calculateWithVelocities(
m_prevFrontLeftSpeedSetpoint, frontLeftSpeedSetpoint);
final double rearLeftFeedforward =
m_feedforward.calculate(m_prevRearLeftSpeedSetpoint, m_rearLeftSpeedSetpoint).in(Volts);
m_feedforward.calculateWithVelocities(m_prevRearLeftSpeedSetpoint, rearLeftSpeedSetpoint);
final double frontRightFeedforward =
m_feedforward
.calculate(m_prevFrontRightSpeedSetpoint, m_frontRightSpeedSetpoint)
.in(Volts);
m_feedforward.calculateWithVelocities(
m_prevFrontRightSpeedSetpoint, frontRightSpeedSetpoint);
final double rearRightFeedforward =
m_feedforward.calculate(m_prevRearRightSpeedSetpoint, m_rearRightSpeedSetpoint).in(Volts);
m_feedforward.calculateWithVelocities(
m_prevRearRightSpeedSetpoint, rearRightSpeedSetpoint);
frontLeftOutput =
frontLeftFeedforward
+ m_frontLeftController.calculate(
m_currentWheelSpeeds.get().frontLeftMetersPerSecond,
m_frontLeftSpeedSetpoint.in(MetersPerSecond));
m_currentWheelSpeeds.get().frontLeftMetersPerSecond, frontLeftSpeedSetpoint);
rearLeftOutput =
rearLeftFeedforward
+ m_rearLeftController.calculate(
m_currentWheelSpeeds.get().rearLeftMetersPerSecond,
m_rearLeftSpeedSetpoint.in(MetersPerSecond));
m_currentWheelSpeeds.get().rearLeftMetersPerSecond, rearLeftSpeedSetpoint);
frontRightOutput =
frontRightFeedforward
+ m_frontRightController.calculate(
m_currentWheelSpeeds.get().frontRightMetersPerSecond,
m_frontRightSpeedSetpoint.in(MetersPerSecond));
m_currentWheelSpeeds.get().frontRightMetersPerSecond, frontRightSpeedSetpoint);
rearRightOutput =
rearRightFeedforward
+ m_rearRightController.calculate(
m_currentWheelSpeeds.get().rearRightMetersPerSecond,
m_rearRightSpeedSetpoint.in(MetersPerSecond));
m_currentWheelSpeeds.get().rearRightMetersPerSecond, rearRightSpeedSetpoint);
m_outputDriveVoltages.accept(
new MecanumDriveMotorVoltages(
frontLeftOutput, frontRightOutput, rearLeftOutput, rearRightOutput));
frontLeftOutput, frontRightOutput, rearLeftOutput, rearRightOutput);
} else {
m_outputWheelSpeeds.accept(
new MecanumDriveWheelSpeeds(
m_frontLeftSpeedSetpoint.in(MetersPerSecond),
m_frontRightSpeedSetpoint.in(MetersPerSecond),
m_rearLeftSpeedSetpoint.in(MetersPerSecond),
m_rearRightSpeedSetpoint.in(MetersPerSecond)));
frontLeftSpeedSetpoint,
frontRightSpeedSetpoint,
rearLeftSpeedSetpoint,
rearRightSpeedSetpoint));
}
}
@@ -435,4 +558,22 @@ public class MecanumControllerCommand extends Command {
public boolean isFinished() {
return m_timer.hasElapsed(m_trajectory.getTotalTimeSeconds());
}
/** A consumer to represent an operation on the voltages of a mecanum drive. */
@FunctionalInterface
public interface MecanumVoltagesConsumer {
/**
* Accepts the voltages to perform some operation with them.
*
* @param frontLeftVoltage The voltage of the front left motor.
* @param frontRightVoltage The voltage of the front right motor.
* @param rearLeftVoltage The voltage of the rear left motor.
* @param rearRightVoltage The voltage of the rear left motor.
*/
void accept(
double frontLeftVoltage,
double frontRightVoltage,
double rearLeftVoltage,
double rearRightVoltage);
}
}

View File

@@ -4,8 +4,6 @@
package edu.wpi.first.wpilibj2.command;
import static edu.wpi.first.units.Units.MetersPerSecond;
import static edu.wpi.first.units.Units.Volts;
import static edu.wpi.first.util.ErrorMessages.requireNonNullParam;
import edu.wpi.first.math.controller.PIDController;
@@ -16,7 +14,6 @@ import edu.wpi.first.math.kinematics.ChassisSpeeds;
import edu.wpi.first.math.kinematics.DifferentialDriveKinematics;
import edu.wpi.first.math.kinematics.DifferentialDriveWheelSpeeds;
import edu.wpi.first.math.trajectory.Trajectory;
import edu.wpi.first.units.measure.MutLinearVelocity;
import edu.wpi.first.util.sendable.SendableBuilder;
import edu.wpi.first.wpilibj.Timer;
import java.util.function.BiConsumer;
@@ -49,10 +46,8 @@ public class RamseteCommand extends Command {
private final PIDController m_rightController;
private final BiConsumer<Double, Double> m_output;
private DifferentialDriveWheelSpeeds m_prevSpeeds = new DifferentialDriveWheelSpeeds();
private final MutLinearVelocity m_prevLeftSpeedSetpoint = MetersPerSecond.mutable(0);
private final MutLinearVelocity m_prevRightSpeedSetpoint = MetersPerSecond.mutable(0);
private final MutLinearVelocity m_leftSpeedSetpoint = MetersPerSecond.mutable(0);
private final MutLinearVelocity m_rightSpeedSetpoint = MetersPerSecond.mutable(0);
private double m_prevLeftSpeedSetpoint; // m/s
private double m_prevRightSpeedSetpoint; // m/s
private double m_prevTime;
/**
@@ -156,8 +151,8 @@ public class RamseteCommand extends Command {
initialState.velocityMetersPerSecond,
0,
initialState.curvatureRadPerMeter * initialState.velocityMetersPerSecond));
m_prevLeftSpeedSetpoint.mut_setMagnitude(m_prevSpeeds.leftMetersPerSecond);
m_prevRightSpeedSetpoint.mut_setMagnitude(m_prevSpeeds.rightMetersPerSecond);
m_prevLeftSpeedSetpoint = m_prevSpeeds.leftMetersPerSecond;
m_prevRightSpeedSetpoint = m_prevSpeeds.rightMetersPerSecond;
m_timer.restart();
if (m_usePID) {
m_leftController.reset();
@@ -179,21 +174,18 @@ public class RamseteCommand extends Command {
m_kinematics.toWheelSpeeds(
m_follower.calculate(m_pose.get(), m_trajectory.sample(curTime)));
var leftSpeedSetpoint = targetWheelSpeeds.leftMetersPerSecond;
var rightSpeedSetpoint = targetWheelSpeeds.rightMetersPerSecond;
m_leftSpeedSetpoint.mut_setMagnitude(targetWheelSpeeds.leftMetersPerSecond);
m_rightSpeedSetpoint.mut_setMagnitude(targetWheelSpeeds.rightMetersPerSecond);
double leftSpeedSetpoint = targetWheelSpeeds.leftMetersPerSecond;
double rightSpeedSetpoint = targetWheelSpeeds.rightMetersPerSecond;
double leftOutput;
double rightOutput;
if (m_usePID) {
double leftFeedforward =
m_feedforward.calculate(m_prevLeftSpeedSetpoint, m_leftSpeedSetpoint).in(Volts);
m_feedforward.calculateWithVelocities(m_prevLeftSpeedSetpoint, leftSpeedSetpoint);
double rightFeedforward =
m_feedforward.calculate(m_prevRightSpeedSetpoint, m_rightSpeedSetpoint).in(Volts);
m_feedforward.calculateWithVelocities(m_prevRightSpeedSetpoint, rightSpeedSetpoint);
leftOutput =
leftFeedforward

View File

@@ -24,6 +24,18 @@ import java.util.function.BooleanSupplier;
* <p>This class is provided by the NewCommands VendorDep
*/
public class Trigger implements BooleanSupplier {
/** Functional interface for the body of a trigger binding. */
@FunctionalInterface
private interface BindingBody {
/**
* Executes the body of the binding.
*
* @param previous The previous state of the condition.
* @param current The current state of the condition.
*/
void run(boolean previous, boolean current);
}
private final BooleanSupplier m_condition;
private final EventLoop m_loop;
@@ -49,6 +61,27 @@ public class Trigger implements BooleanSupplier {
this(CommandScheduler.getInstance().getDefaultButtonLoop(), condition);
}
/**
* Adds a binding to the EventLoop.
*
* @param body The body of the binding to add.
*/
private void addBinding(BindingBody body) {
m_loop.bind(
new Runnable() {
private boolean m_previous = m_condition.getAsBoolean();
@Override
public void run() {
boolean current = m_condition.getAsBoolean();
body.run(m_previous, current);
m_previous = current;
}
});
}
/**
* Starts the command when the condition changes.
*
@@ -57,19 +90,10 @@ public class Trigger implements BooleanSupplier {
*/
public Trigger onChange(Command command) {
requireNonNullParam(command, "command", "onChange");
m_loop.bind(
new Runnable() {
private boolean m_pressedLast = m_condition.getAsBoolean();
@Override
public void run() {
boolean pressed = m_condition.getAsBoolean();
if (m_pressedLast != pressed) {
command.schedule();
}
m_pressedLast = pressed;
addBinding(
(previous, current) -> {
if (previous != current) {
command.schedule();
}
});
return this;
@@ -83,19 +107,10 @@ public class Trigger implements BooleanSupplier {
*/
public Trigger onTrue(Command command) {
requireNonNullParam(command, "command", "onTrue");
m_loop.bind(
new Runnable() {
private boolean m_pressedLast = m_condition.getAsBoolean();
@Override
public void run() {
boolean pressed = m_condition.getAsBoolean();
if (!m_pressedLast && pressed) {
command.schedule();
}
m_pressedLast = pressed;
addBinding(
(previous, current) -> {
if (!previous && current) {
command.schedule();
}
});
return this;
@@ -109,19 +124,10 @@ public class Trigger implements BooleanSupplier {
*/
public Trigger onFalse(Command command) {
requireNonNullParam(command, "command", "onFalse");
m_loop.bind(
new Runnable() {
private boolean m_pressedLast = m_condition.getAsBoolean();
@Override
public void run() {
boolean pressed = m_condition.getAsBoolean();
if (m_pressedLast && !pressed) {
command.schedule();
}
m_pressedLast = pressed;
addBinding(
(previous, current) -> {
if (previous && !current) {
command.schedule();
}
});
return this;
@@ -139,21 +145,12 @@ public class Trigger implements BooleanSupplier {
*/
public Trigger whileTrue(Command command) {
requireNonNullParam(command, "command", "whileTrue");
m_loop.bind(
new Runnable() {
private boolean m_pressedLast = m_condition.getAsBoolean();
@Override
public void run() {
boolean pressed = m_condition.getAsBoolean();
if (!m_pressedLast && pressed) {
command.schedule();
} else if (m_pressedLast && !pressed) {
command.cancel();
}
m_pressedLast = pressed;
addBinding(
(previous, current) -> {
if (!previous && current) {
command.schedule();
} else if (previous && !current) {
command.cancel();
}
});
return this;
@@ -171,21 +168,12 @@ public class Trigger implements BooleanSupplier {
*/
public Trigger whileFalse(Command command) {
requireNonNullParam(command, "command", "whileFalse");
m_loop.bind(
new Runnable() {
private boolean m_pressedLast = m_condition.getAsBoolean();
@Override
public void run() {
boolean pressed = m_condition.getAsBoolean();
if (m_pressedLast && !pressed) {
command.schedule();
} else if (!m_pressedLast && pressed) {
command.cancel();
}
m_pressedLast = pressed;
addBinding(
(previous, current) -> {
if (previous && !current) {
command.schedule();
} else if (!previous && current) {
command.cancel();
}
});
return this;
@@ -199,23 +187,14 @@ public class Trigger implements BooleanSupplier {
*/
public Trigger toggleOnTrue(Command command) {
requireNonNullParam(command, "command", "toggleOnTrue");
m_loop.bind(
new Runnable() {
private boolean m_pressedLast = m_condition.getAsBoolean();
@Override
public void run() {
boolean pressed = m_condition.getAsBoolean();
if (!m_pressedLast && pressed) {
if (command.isScheduled()) {
command.cancel();
} else {
command.schedule();
}
addBinding(
(previous, current) -> {
if (!previous && current) {
if (command.isScheduled()) {
command.cancel();
} else {
command.schedule();
}
m_pressedLast = pressed;
}
});
return this;
@@ -229,23 +208,14 @@ public class Trigger implements BooleanSupplier {
*/
public Trigger toggleOnFalse(Command command) {
requireNonNullParam(command, "command", "toggleOnFalse");
m_loop.bind(
new Runnable() {
private boolean m_pressedLast = m_condition.getAsBoolean();
@Override
public void run() {
boolean pressed = m_condition.getAsBoolean();
if (m_pressedLast && !pressed) {
if (command.isScheduled()) {
command.cancel();
} else {
command.schedule();
}
addBinding(
(previous, current) -> {
if (previous && !current) {
if (command.isScheduled()) {
command.cancel();
} else {
command.schedule();
}
m_pressedLast = pressed;
}
});
return this;

View File

@@ -121,6 +121,10 @@ CommandPtr Command::OnlyIf(std::function<bool()> condition) && {
return std::move(*this).ToPtr().OnlyIf(std::move(condition));
}
CommandPtr Command::WithDeadline(CommandPtr&& deadline) && {
return std::move(*this).ToPtr().WithDeadline(std::move(deadline));
}
CommandPtr Command::DeadlineFor(CommandPtr&& parallel) && {
return std::move(*this).ToPtr().DeadlineFor(std::move(parallel));
}

View File

@@ -168,6 +168,15 @@ CommandPtr CommandPtr::OnlyIf(std::function<bool()> condition) && {
return std::move(*this).Unless(std::not_fn(std::move(condition)));
}
CommandPtr CommandPtr::WithDeadline(CommandPtr&& deadline) && {
AssertValid();
std::vector<std::unique_ptr<Command>> vec;
vec.emplace_back(std::move(m_ptr));
m_ptr = std::make_unique<ParallelDeadlineGroup>(std::move(deadline).Unwrap(),
std::move(vec));
return std::move(*this);
}
CommandPtr CommandPtr::DeadlineWith(CommandPtr&& parallel) && {
AssertValid();
std::vector<std::unique_ptr<Command>> vec;

View File

@@ -15,159 +15,117 @@ using namespace frc2;
Trigger::Trigger(const Trigger& other) = default;
void Trigger::AddBinding(wpi::unique_function<void(bool, bool)>&& body) {
m_loop->Bind([condition = m_condition, previous = m_condition(),
body = std::move(body)]() mutable {
bool current = condition();
body(previous, current);
previous = current;
});
}
Trigger Trigger::OnChange(Command* command) {
m_loop->Bind(
[condition = m_condition, previous = m_condition(), command]() mutable {
bool current = condition();
if (previous != current) {
command->Schedule();
}
previous = current;
});
AddBinding([command](bool previous, bool current) {
if (previous != current) {
command->Schedule();
}
});
return *this;
}
Trigger Trigger::OnChange(CommandPtr&& command) {
m_loop->Bind([condition = m_condition, previous = m_condition(),
command = std::move(command)]() mutable {
bool current = condition();
AddBinding([command = std::move(command)](bool previous, bool current) {
if (previous != current) {
command.Schedule();
}
previous = current;
});
return *this;
}
Trigger Trigger::OnTrue(Command* command) {
m_loop->Bind(
[condition = m_condition, previous = m_condition(), command]() mutable {
bool current = condition();
if (!previous && current) {
command->Schedule();
}
previous = current;
});
AddBinding([command](bool previous, bool current) {
if (!previous && current) {
command->Schedule();
}
});
return *this;
}
Trigger Trigger::OnTrue(CommandPtr&& command) {
m_loop->Bind([condition = m_condition, previous = m_condition(),
command = std::move(command)]() mutable {
bool current = condition();
AddBinding([command = std::move(command)](bool previous, bool current) {
if (!previous && current) {
command.Schedule();
}
previous = current;
});
return *this;
}
Trigger Trigger::OnFalse(Command* command) {
m_loop->Bind(
[condition = m_condition, previous = m_condition(), command]() mutable {
bool current = condition();
if (previous && !current) {
command->Schedule();
}
previous = current;
});
AddBinding([command](bool previous, bool current) {
if (previous && !current) {
command->Schedule();
}
});
return *this;
}
Trigger Trigger::OnFalse(CommandPtr&& command) {
m_loop->Bind([condition = m_condition, previous = m_condition(),
command = std::move(command)]() mutable {
bool current = condition();
AddBinding([command = std::move(command)](bool previous, bool current) {
if (previous && !current) {
command.Schedule();
}
previous = current;
});
return *this;
}
Trigger Trigger::WhileTrue(Command* command) {
m_loop->Bind(
[condition = m_condition, previous = m_condition(), command]() mutable {
bool current = condition();
if (!previous && current) {
command->Schedule();
} else if (previous && !current) {
command->Cancel();
}
previous = current;
});
AddBinding([command](bool previous, bool current) {
if (!previous && current) {
command->Schedule();
} else if (previous && !current) {
command->Cancel();
}
});
return *this;
}
Trigger Trigger::WhileTrue(CommandPtr&& command) {
m_loop->Bind([condition = m_condition, previous = m_condition(),
command = std::move(command)]() mutable {
bool current = condition();
AddBinding([command = std::move(command)](bool previous, bool current) {
if (!previous && current) {
command.Schedule();
} else if (previous && !current) {
command.Cancel();
}
previous = current;
});
return *this;
}
Trigger Trigger::WhileFalse(Command* command) {
m_loop->Bind(
[condition = m_condition, previous = m_condition(), command]() mutable {
bool current = condition();
if (previous && !current) {
command->Schedule();
} else if (!previous && current) {
command->Cancel();
}
previous = current;
});
AddBinding([command](bool previous, bool current) {
if (previous && !current) {
command->Schedule();
} else if (!previous && current) {
command->Cancel();
}
});
return *this;
}
Trigger Trigger::WhileFalse(CommandPtr&& command) {
m_loop->Bind([condition = m_condition, previous = m_condition(),
command = std::move(command)]() mutable {
bool current = condition();
AddBinding([command = std::move(command)](bool previous, bool current) {
if (!previous && current) {
command.Schedule();
} else if (previous && !current) {
command.Cancel();
}
previous = current;
});
return *this;
}
Trigger Trigger::ToggleOnTrue(Command* command) {
m_loop->Bind([condition = m_condition, previous = m_condition(),
command = command]() mutable {
bool current = condition();
AddBinding([command](bool previous, bool current) {
if (!previous && current) {
if (command->IsScheduled()) {
command->Cancel();
@@ -175,17 +133,12 @@ Trigger Trigger::ToggleOnTrue(Command* command) {
command->Schedule();
}
}
previous = current;
});
return *this;
}
Trigger Trigger::ToggleOnTrue(CommandPtr&& command) {
m_loop->Bind([condition = m_condition, previous = m_condition(),
command = std::move(command)]() mutable {
bool current = condition();
AddBinding([command = std::move(command)](bool previous, bool current) {
if (!previous && current) {
if (command.IsScheduled()) {
command.Cancel();
@@ -193,17 +146,12 @@ Trigger Trigger::ToggleOnTrue(CommandPtr&& command) {
command.Schedule();
}
}
previous = current;
});
return *this;
}
Trigger Trigger::ToggleOnFalse(Command* command) {
m_loop->Bind([condition = m_condition, previous = m_condition(),
command = command]() mutable {
bool current = condition();
AddBinding([command](bool previous, bool current) {
if (previous && !current) {
if (command->IsScheduled()) {
command->Cancel();
@@ -211,17 +159,12 @@ Trigger Trigger::ToggleOnFalse(Command* command) {
command->Schedule();
}
}
previous = current;
});
return *this;
}
Trigger Trigger::ToggleOnFalse(CommandPtr&& command) {
m_loop->Bind([condition = m_condition, previous = m_condition(),
command = std::move(command)]() mutable {
bool current = condition();
AddBinding([command = std::move(command)](bool previous, bool current) {
if (previous && !current) {
if (command.IsScheduled()) {
command.Cancel();
@@ -229,8 +172,6 @@ Trigger Trigger::ToggleOnFalse(CommandPtr&& command) {
command.Schedule();
}
}
previous = current;
});
return *this;
}

View File

@@ -309,6 +309,16 @@ class Command : public wpi::Sendable, public wpi::SendableHelper<Command> {
[[nodiscard]]
CommandPtr OnlyIf(std::function<bool()> condition) &&;
/**
* Creates a new command that runs this command and the deadline in parallel,
* finishing (and interrupting this command) when the deadline finishes.
*
* @param deadline the deadline of the command group
* @return the decorated command
* @see DeadlineFor
*/
CommandPtr WithDeadline(CommandPtr&& deadline) &&;
/**
* Decorates this command with a set of commands to run parallel to it, ending
* when the calling command ends and interrupting all the others. Often more
@@ -318,9 +328,11 @@ class Command : public wpi::Sendable, public wpi::SendableHelper<Command> {
* @param parallel the commands to run in parallel. Note the parallel commands
* will be interupted when the deadline command ends
* @return the decorated command
* @see WithDeadline
*/
[[nodiscard]]
CommandPtr DeadlineFor(CommandPtr&& parallel) &&;
/**
* Decorates this command with a set of commands to run parallel to it, ending
* when the last command ends. Often more convenient/less-verbose than

View File

@@ -182,6 +182,16 @@ class CommandPtr final {
[[nodiscard]]
CommandPtr OnlyIf(std::function<bool()> condition) &&;
/**
* Creates a new command that runs this command and the deadline in parallel,
* finishing (and interrupting this command) when the deadline finishes.
*
* @param deadline the deadline of the command group
* @return the decorated command
* @see DeadlineFor
*/
CommandPtr WithDeadline(CommandPtr&& deadline) &&;
/**
* Decorates this command with a set of commands to run parallel to it, ending
* when the calling command ends and interrupting all the others. Often more

View File

@@ -11,6 +11,7 @@
#include <frc/event/EventLoop.h>
#include <frc/filter/Debouncer.h>
#include <units/time.h>
#include <wpi/FunctionExtras.h>
#include "frc2/command/Command.h"
#include "frc2/command/CommandScheduler.h"
@@ -291,6 +292,13 @@ class Trigger {
bool Get() const;
private:
/**
* Adds a binding to the EventLoop.
*
* @param body The body of the binding to add.
*/
void AddBinding(wpi::unique_function<void(bool, bool)>&& body);
frc::EventLoop* m_loop;
std::function<bool()> m_condition;
};

View File

@@ -271,6 +271,57 @@ class CommandDecoratorTest extends CommandTestBase {
}
}
@Test
void withDeadlineTest() {
try (CommandScheduler scheduler = new CommandScheduler()) {
AtomicBoolean finish = new AtomicBoolean(false);
Command endsBeforeGroup = Commands.none().withDeadline(Commands.waitUntil(finish::get));
scheduler.schedule(endsBeforeGroup);
scheduler.run();
assertTrue(scheduler.isScheduled(endsBeforeGroup));
finish.set(true);
scheduler.run();
assertFalse(scheduler.isScheduled(endsBeforeGroup));
finish.set(false);
Command endsAfterGroup = Commands.idle().withDeadline(Commands.waitUntil(finish::get));
scheduler.schedule(endsAfterGroup);
scheduler.run();
assertTrue(scheduler.isScheduled(endsAfterGroup));
finish.set(true);
scheduler.run();
assertFalse(scheduler.isScheduled(endsAfterGroup));
}
}
@Test
void withDeadlineOrderTest() {
try (CommandScheduler scheduler = new CommandScheduler()) {
AtomicBoolean dictatorHasRun = new AtomicBoolean(false);
AtomicBoolean dictatorWasPolled = new AtomicBoolean(false);
Command dictator =
new FunctionalCommand(
() -> {},
() -> dictatorHasRun.set(true),
interrupted -> {},
() -> {
dictatorWasPolled.set(true);
return true;
});
Command other =
Commands.run(
() ->
assertAll(
() -> assertTrue(dictatorHasRun.get()),
() -> assertTrue(dictatorWasPolled.get())));
Command group = other.withDeadline(dictator);
scheduler.schedule(group);
scheduler.run();
assertAll(() -> assertTrue(dictatorHasRun.get()), () -> assertTrue(dictatorWasPolled.get()));
}
}
@Test
void alongWithTest() {
try (CommandScheduler scheduler = new CommandScheduler()) {

View File

@@ -221,6 +221,27 @@ TEST_F(CommandDecoratorTest, DeadlineFor) {
EXPECT_FALSE(scheduler.IsScheduled(group));
}
TEST_F(CommandDecoratorTest, WithDeadline) {
CommandScheduler scheduler = GetScheduler();
bool finish = false;
auto dictator = WaitUntilCommand([&finish] { return finish; });
auto endsAfter = WaitUntilCommand([] { return false; });
auto group = std::move(endsAfter).WithDeadline(std::move(dictator).ToPtr());
scheduler.Schedule(group);
scheduler.Run();
EXPECT_TRUE(scheduler.IsScheduled(group));
finish = true;
scheduler.Run();
EXPECT_FALSE(scheduler.IsScheduled(group));
}
TEST_F(CommandDecoratorTest, AlongWith) {
CommandScheduler scheduler = GetScheduler();
@@ -283,6 +304,33 @@ TEST_F(CommandDecoratorTest, DeadlineForOrder) {
EXPECT_TRUE(dictatorWasPolled);
}
TEST_F(CommandDecoratorTest, WithDeadlineOrder) {
CommandScheduler scheduler = GetScheduler();
bool dictatorHasRun = false;
bool dictatorWasPolled = false;
auto dictator =
FunctionalCommand([] {}, [&dictatorHasRun] { dictatorHasRun = true; },
[](bool interrupted) {},
[&dictatorWasPolled] {
dictatorWasPolled = true;
return true;
});
auto other = RunCommand([&dictatorHasRun, &dictatorWasPolled] {
EXPECT_TRUE(dictatorHasRun);
EXPECT_TRUE(dictatorWasPolled);
});
auto group = std::move(other).WithDeadline(std::move(dictator).ToPtr());
scheduler.Schedule(group);
scheduler.Run();
EXPECT_TRUE(dictatorHasRun);
EXPECT_TRUE(dictatorWasPolled);
}
TEST_F(CommandDecoratorTest, AlongWithOrder) {
CommandScheduler scheduler = GetScheduler();

View File

@@ -13,6 +13,7 @@
#include <vector>
#include <fmt/chrono.h>
#include <hal/FRCUsageReporting.h>
#include <networktables/NetworkTableInstance.h>
#include <wpi/DataLog.h>
#include <wpi/DataLogBackgroundWriter.h>
@@ -78,6 +79,8 @@ static std::string MakeLogDir(std::string_view dir) {
(s.permissions() & fs::perms::others_write) != fs::perms::none) {
fs::create_directory("/u/logs", ec);
return "/u/logs";
HAL_Report(HALUsageReporting::kResourceType_DataLogManager,
HALUsageReporting::kDataLogLocation_USB);
}
if (RobotBase::GetRuntimeType() == kRoboRIO) {
FRC_ReportWarning(
@@ -85,6 +88,8 @@ static std::string MakeLogDir(std::string_view dir) {
"not recommended! Plug in a FAT32 formatted flash drive!");
}
fs::create_directory("/home/lvuser/logs", ec);
HAL_Report(HALUsageReporting::kResourceType_DataLogManager,
HALUsageReporting::kDataLogLocation_Onboard);
return "/home/lvuser/logs";
#else
std::string logDir = filesystem::GetOperatingDirectory() + "/logs";

View File

@@ -18,33 +18,46 @@
using namespace frc;
LEDPattern::LEDPattern(LEDPatternFn impl) : m_impl(std::move(impl)) {}
LEDPattern::LEDPattern(std::function<void(frc::LEDPattern::LEDReader,
std::function<void(int, frc::Color)>)>
impl)
: m_impl(std::move(impl)) {}
void LEDPattern::ApplyTo(LEDPattern::LEDReader reader,
std::function<void(int, frc::Color)> writer) const {
m_impl(reader, writer);
}
void LEDPattern::ApplyTo(std::span<AddressableLED::LEDData> data,
LEDWriterFn writer) const {
m_impl(data, writer);
std::function<void(int, frc::Color)> writer) const {
ApplyTo(LEDPattern::LEDReader{[=](size_t i) { return data[i]; }, data.size()},
writer);
}
void LEDPattern::ApplyTo(std::span<AddressableLED::LEDData> data) const {
ApplyTo(data, [&](int index, Color color) { data[index].SetLED(color); });
}
LEDPattern LEDPattern::Reversed() {
return LEDPattern{[self = *this](auto data, auto writer) {
self.ApplyTo(data, [&](int i, Color color) {
writer((data.size() - 1) - i, color);
});
LEDPattern LEDPattern::MapIndex(
std::function<size_t(size_t, size_t)> indexMapper) {
return LEDPattern{[self = *this, indexMapper](auto data, auto writer) {
size_t bufLen = data.size();
self.ApplyTo(
LEDPattern::LEDReader{
[=](auto i) { return data[indexMapper(bufLen, i)]; }, bufLen},
[&](int i, Color color) { writer(indexMapper(bufLen, i), color); });
}};
}
LEDPattern LEDPattern::Reversed() {
return MapIndex([](size_t bufLen, size_t i) { return bufLen - 1 - i; });
}
LEDPattern LEDPattern::OffsetBy(int offset) {
return LEDPattern{[=, self = *this](auto data, auto writer) {
self.ApplyTo(data, [&data, &writer, offset](int i, Color color) {
int shiftedIndex =
frc::FloorMod(i + offset, static_cast<int>(data.size()));
writer(shiftedIndex, color);
});
}};
return MapIndex([offset](size_t bufLen, size_t i) {
return frc::FloorMod(static_cast<int>(i) + offset,
static_cast<int>(bufLen));
});
}
LEDPattern LEDPattern::ScrollAtRelativeSpeed(units::hertz_t velocity) {
@@ -53,8 +66,7 @@ LEDPattern LEDPattern::ScrollAtRelativeSpeed(units::hertz_t velocity) {
// Invert and multiply by 1,000,000 to get microseconds
double periodMicros = 1e6 / velocity.value();
return LEDPattern{[=, self = *this](auto data, auto writer) {
auto bufLen = data.size();
return MapIndex([=](size_t bufLen, size_t i) {
auto now = wpi::Now();
// index should move by (bufLen) / (period)
@@ -62,12 +74,9 @@ LEDPattern LEDPattern::ScrollAtRelativeSpeed(units::hertz_t velocity) {
(now % static_cast<int64_t>(std::floor(periodMicros))) / periodMicros;
int offset = static_cast<int>(std::floor(t * bufLen));
self.ApplyTo(data, [=](int i, Color color) {
// floorMod so if the offset is negative, we still get positive outputs
int shiftedIndex = frc::FloorMod(i + offset, static_cast<int>(bufLen));
writer(shiftedIndex, color);
});
}};
return frc::FloorMod(static_cast<int>(i) + offset,
static_cast<int>(bufLen));
});
}
LEDPattern LEDPattern::ScrollAtAbsoluteSpeed(
@@ -77,8 +86,7 @@ LEDPattern LEDPattern::ScrollAtAbsoluteSpeed(
auto microsPerLed =
static_cast<int64_t>(std::floor((ledSpacing / velocity).value() * 1e6));
return LEDPattern{[=, self = *this](auto data, auto writer) {
auto bufLen = data.size();
return MapIndex([=](size_t bufLen, size_t i) {
auto now = wpi::Now();
// every step in time that's a multiple of microsPerLED will increment
@@ -87,13 +95,9 @@ LEDPattern LEDPattern::ScrollAtAbsoluteSpeed(
// offset values for negative velocities
auto offset = static_cast<int64_t>(now) / microsPerLed;
self.ApplyTo(data, [=, &writer](int i, Color color) {
// FloorMod so if the offset is negative, we still get positive outputs
int shiftedIndex = frc::FloorMod(i + offset, static_cast<int>(bufLen));
writer(shiftedIndex, color);
});
}};
return frc::FloorMod(static_cast<int>(i) + offset,
static_cast<int>(bufLen));
});
}
LEDPattern LEDPattern::Blink(units::second_t onTime, units::second_t offTime) {

View File

@@ -39,7 +39,14 @@ PowerDistribution::PowerDistribution() {
m_module = HAL_GetPowerDistributionModuleNumber(m_handle, &status);
FRC_ReportError(status, "Module {}", m_module);
HAL_Report(HALUsageReporting::kResourceType_PDP, m_module + 1);
if (HAL_GetPowerDistributionType(m_handle, &status) ==
HAL_PowerDistributionType::HAL_PowerDistributionType_kCTRE) {
HAL_Report(HALUsageReporting::kResourceType_PDP,
HALUsageReporting::kPDP_CTRE);
} else {
HAL_Report(HALUsageReporting::kResourceType_PDP,
HALUsageReporting::kPDP_REV);
}
wpi::SendableRegistry::AddLW(this, "PowerDistribution", m_module);
}
@@ -54,7 +61,13 @@ PowerDistribution::PowerDistribution(int module, ModuleType moduleType) {
m_module = HAL_GetPowerDistributionModuleNumber(m_handle, &status);
FRC_ReportError(status, "Module {}", module);
HAL_Report(HALUsageReporting::kResourceType_PDP, m_module + 1);
if (moduleType == ModuleType::kCTRE) {
HAL_Report(HALUsageReporting::kResourceType_PDP,
HALUsageReporting::kPDP_CTRE);
} else {
HAL_Report(HALUsageReporting::kResourceType_PDP,
HALUsageReporting::kPDP_REV);
}
wpi::SendableRegistry::AddLW(this, "PowerDistribution", m_module);
}

View File

@@ -52,7 +52,7 @@ units::radians_per_second_t DCMotorSim::GetAngularVelocity() const {
units::radians_per_second_squared_t DCMotorSim::GetAngularAcceleration() const {
return units::radians_per_second_squared_t{
(m_plant.A() * m_x + m_plant.B() * m_u)(0, 0)};
(m_plant.A() * m_x + m_plant.B() * m_u)(1, 0)};
}
units::newton_meter_t DCMotorSim::GetTorque() const {

View File

@@ -142,6 +142,10 @@ class WPILibMathShared : public wpi::math::MathShared {
case wpi::math::MathUsageId::kController_BangBangController:
HAL_Report(HALUsageReporting::kResourceType_BangBangController, count);
break;
case wpi::math::MathUsageId::kTrajectory_PathWeaver:
HAL_Report(HALUsageReporting::kResourceType_PathWeaverTrajectory,
count);
break;
}
}

View File

@@ -18,21 +18,36 @@
namespace frc {
/**
* Sets the LED at the given index to the given color.
*/
using LEDWriterFn = std::function<void(int, frc::Color)>;
/**
* Accepts a data buffer (1st argument) and a callback (2nd argument) for
* writing data.
*/
using LEDPatternFn =
std::function<void(std::span<frc::AddressableLED::LEDData>, LEDWriterFn)>;
class LEDPattern {
public:
explicit LEDPattern(LEDPatternFn impl);
/**
* A wrapper around a length and an arbitrary reader function that accepts an
* LED index and returns data for the LED at that index. This configuration
* allows us to abstract over different container types without templating.
*/
class LEDReader {
public:
LEDReader(std::function<frc::AddressableLED::LEDData(int)> impl,
size_t size)
: m_impl{std::move(impl)}, m_size{size} {}
frc::AddressableLED::LEDData operator[](size_t index) const {
return m_impl(index);
}
size_t size() const { return m_size; }
private:
std::function<frc::AddressableLED::LEDData(int)> m_impl;
size_t m_size;
};
explicit LEDPattern(std::function<void(frc::LEDPattern::LEDReader,
std::function<void(int, frc::Color)>)>
impl);
void ApplyTo(LEDReader reader,
std::function<void(int, frc::Color)> writer) const;
/**
* Writes the pattern to an LED buffer. Dynamic animations should be called
@@ -48,7 +63,7 @@ class LEDPattern {
* @param writer data writer for setting new LED colors on the LED strip
*/
void ApplyTo(std::span<frc::AddressableLED::LEDData> data,
LEDWriterFn writer) const;
std::function<void(int, frc::Color)> writer) const;
/**
* Writes the pattern to an LED buffer. Dynamic animations should be called
@@ -64,6 +79,15 @@ class LEDPattern {
*/
void ApplyTo(std::span<frc::AddressableLED::LEDData> data) const;
/**
* Creates a pattern with remapped indices.
*
* @param indexMapper the index mapper
* @return the mapped pattern
*/
[[nodiscard]]
LEDPattern MapIndex(std::function<size_t(size_t, size_t)> indexMapper);
/**
* Creates a pattern that displays this one in reverse. Scrolling patterns
* will scroll in the opposite direction (but at the same speed). It will
@@ -373,6 +397,8 @@ class LEDPattern {
static LEDPattern Rainbow(int saturation, int value);
private:
LEDPatternFn m_impl;
std::function<void(frc::LEDPattern::LEDReader,
std::function<void(int, frc::Color)>)>
m_impl;
};
} // namespace frc

View File

@@ -813,6 +813,220 @@ TEST(LEDPatternTest, ClippingBrightness) {
AssertIndexColor(buffer, 0, Color::kWhite);
}
TEST(LEDPatternTest, ReverseMask) {
std::array<AddressableLED::LEDData, 8> buffer;
std::array<std::pair<double, Color>, 4> colorSteps{
std::pair{0.0, Color::kRed}, std::pair{0.25, Color::kBlue},
std::pair{0.5, Color::kYellow}, std::pair{0.75, Color::kGreen}};
std::array<std::pair<double, Color>, 2> maskSteps{
std::pair{0, Color::kWhite}, std::pair{0.5, Color::kBlack}};
auto pattern = LEDPattern::Steps(colorSteps)
.Mask(LEDPattern::Steps(maskSteps))
.Reversed();
pattern.ApplyTo(buffer);
AssertIndexColor(buffer, 7, Color::kRed);
AssertIndexColor(buffer, 6, Color::kRed);
AssertIndexColor(buffer, 5, Color::kBlue);
AssertIndexColor(buffer, 4, Color::kBlue);
AssertIndexColor(buffer, 3, Color::kBlack);
AssertIndexColor(buffer, 2, Color::kBlack);
AssertIndexColor(buffer, 1, Color::kBlack);
AssertIndexColor(buffer, 0, Color::kBlack);
}
TEST(LEDPatternTest, OffsetMask) {
std::array<AddressableLED::LEDData, 8> buffer;
std::array<std::pair<double, Color>, 4> colorSteps{
std::pair{0.0, Color::kRed}, std::pair{0.25, Color::kBlue},
std::pair{0.5, Color::kYellow}, std::pair{0.75, Color::kGreen}};
std::array<std::pair<double, Color>, 2> maskSteps{
std::pair{0, Color::kWhite}, std::pair{0.5, Color::kBlack}};
auto pattern = LEDPattern::Steps(colorSteps)
.Mask(LEDPattern::Steps(maskSteps))
.OffsetBy(4);
pattern.ApplyTo(buffer);
AssertIndexColor(buffer, 0, Color::kBlack);
AssertIndexColor(buffer, 1, Color::kBlack);
AssertIndexColor(buffer, 2, Color::kBlack);
AssertIndexColor(buffer, 3, Color::kBlack);
AssertIndexColor(buffer, 4, Color::kRed);
AssertIndexColor(buffer, 5, Color::kRed);
AssertIndexColor(buffer, 6, Color::kBlue);
AssertIndexColor(buffer, 7, Color::kBlue);
}
TEST(LEDPatternTest, RelativeScrollingMask) {
std::array<AddressableLED::LEDData, 8> buffer;
std::array<std::pair<double, Color>, 4> colorSteps{
std::pair{0.0, Color::kRed}, std::pair{0.25, Color::kBlue},
std::pair{0.5, Color::kYellow}, std::pair{0.75, Color::kGreen}};
std::array<std::pair<double, Color>, 2> maskSteps{
std::pair{0, Color::kWhite}, std::pair{0.5, Color::kBlack}};
auto pattern = LEDPattern::Steps(colorSteps)
.Mask(LEDPattern::Steps(maskSteps))
.ScrollAtRelativeSpeed(units::hertz_t{1e6 / 8.0});
pattern.ApplyTo(buffer);
static uint64_t now = 0ull;
WPI_SetNowImpl([] { return now; });
{
now = 0ull; // start
SCOPED_TRACE(fmt::format("Time {}", now));
pattern.ApplyTo(buffer);
AssertIndexColor(buffer, 0, Color::kRed);
AssertIndexColor(buffer, 1, Color::kRed);
AssertIndexColor(buffer, 2, Color::kBlue);
AssertIndexColor(buffer, 3, Color::kBlue);
AssertIndexColor(buffer, 4, Color::kBlack);
AssertIndexColor(buffer, 5, Color::kBlack);
AssertIndexColor(buffer, 6, Color::kBlack);
AssertIndexColor(buffer, 7, Color::kBlack);
}
{
now = 1ull;
SCOPED_TRACE(fmt::format("Time {}", now));
pattern.ApplyTo(buffer);
AssertIndexColor(buffer, 0, Color::kBlack);
AssertIndexColor(buffer, 1, Color::kRed);
AssertIndexColor(buffer, 2, Color::kRed);
AssertIndexColor(buffer, 3, Color::kBlue);
AssertIndexColor(buffer, 4, Color::kBlue);
AssertIndexColor(buffer, 5, Color::kBlack);
AssertIndexColor(buffer, 6, Color::kBlack);
AssertIndexColor(buffer, 7, Color::kBlack);
}
{
now = 2ull;
SCOPED_TRACE(fmt::format("Time {}", now));
pattern.ApplyTo(buffer);
AssertIndexColor(buffer, 0, Color::kBlack);
AssertIndexColor(buffer, 1, Color::kBlack);
AssertIndexColor(buffer, 2, Color::kRed);
AssertIndexColor(buffer, 3, Color::kRed);
AssertIndexColor(buffer, 4, Color::kBlue);
AssertIndexColor(buffer, 5, Color::kBlue);
AssertIndexColor(buffer, 6, Color::kBlack);
AssertIndexColor(buffer, 7, Color::kBlack);
}
{
now = 3ull;
SCOPED_TRACE(fmt::format("Time {}", now));
pattern.ApplyTo(buffer);
AssertIndexColor(buffer, 0, Color::kBlack);
AssertIndexColor(buffer, 1, Color::kBlack);
AssertIndexColor(buffer, 2, Color::kBlack);
AssertIndexColor(buffer, 3, Color::kRed);
AssertIndexColor(buffer, 4, Color::kRed);
AssertIndexColor(buffer, 5, Color::kBlue);
AssertIndexColor(buffer, 6, Color::kBlue);
AssertIndexColor(buffer, 7, Color::kBlack);
}
WPI_SetNowImpl(nullptr); // cleanup
}
TEST(LEDPatternTest, AbsoluteScrollingMask) {
std::array<AddressableLED::LEDData, 8> buffer;
std::array<std::pair<double, Color>, 4> colorSteps{
std::pair{0.0, Color::kRed}, std::pair{0.25, Color::kBlue},
std::pair{0.5, Color::kYellow}, std::pair{0.75, Color::kGreen}};
std::array<std::pair<double, Color>, 2> maskSteps{
std::pair{0, Color::kWhite}, std::pair{0.5, Color::kBlack}};
auto pattern = LEDPattern::Steps(colorSteps)
.Mask(LEDPattern::Steps(maskSteps))
.ScrollAtAbsoluteSpeed(1_mps, 1_m);
pattern.ApplyTo(buffer);
static uint64_t now = 0ull;
WPI_SetNowImpl([] { return now; });
{
now = 0ull; // start
SCOPED_TRACE(fmt::format("Time {}", now));
pattern.ApplyTo(buffer);
AssertIndexColor(buffer, 0, Color::kRed);
AssertIndexColor(buffer, 1, Color::kRed);
AssertIndexColor(buffer, 2, Color::kBlue);
AssertIndexColor(buffer, 3, Color::kBlue);
AssertIndexColor(buffer, 4, Color::kBlack);
AssertIndexColor(buffer, 5, Color::kBlack);
AssertIndexColor(buffer, 6, Color::kBlack);
AssertIndexColor(buffer, 7, Color::kBlack);
}
{
now = 1000000ull;
SCOPED_TRACE(fmt::format("Time {}", now));
pattern.ApplyTo(buffer);
AssertIndexColor(buffer, 0, Color::kBlack);
AssertIndexColor(buffer, 1, Color::kRed);
AssertIndexColor(buffer, 2, Color::kRed);
AssertIndexColor(buffer, 3, Color::kBlue);
AssertIndexColor(buffer, 4, Color::kBlue);
AssertIndexColor(buffer, 5, Color::kBlack);
AssertIndexColor(buffer, 6, Color::kBlack);
AssertIndexColor(buffer, 7, Color::kBlack);
}
{
now = 2000000ull;
SCOPED_TRACE(fmt::format("Time {}", now));
pattern.ApplyTo(buffer);
AssertIndexColor(buffer, 0, Color::kBlack);
AssertIndexColor(buffer, 1, Color::kBlack);
AssertIndexColor(buffer, 2, Color::kRed);
AssertIndexColor(buffer, 3, Color::kRed);
AssertIndexColor(buffer, 4, Color::kBlue);
AssertIndexColor(buffer, 5, Color::kBlue);
AssertIndexColor(buffer, 6, Color::kBlack);
AssertIndexColor(buffer, 7, Color::kBlack);
}
{
now = 3000000ull;
SCOPED_TRACE(fmt::format("Time {}", now));
pattern.ApplyTo(buffer);
AssertIndexColor(buffer, 0, Color::kBlack);
AssertIndexColor(buffer, 1, Color::kBlack);
AssertIndexColor(buffer, 2, Color::kBlack);
AssertIndexColor(buffer, 3, Color::kRed);
AssertIndexColor(buffer, 4, Color::kRed);
AssertIndexColor(buffer, 5, Color::kBlue);
AssertIndexColor(buffer, 6, Color::kBlue);
AssertIndexColor(buffer, 7, Color::kBlack);
}
WPI_SetNowImpl(nullptr); // cleanup
}
void AssertIndexColor(std::span<AddressableLED::LEDData> data, int index,
Color color) {
frc::Color8Bit color8bit{color};

View File

@@ -426,6 +426,10 @@ public class ADIS16470_IMU implements AutoCloseable, Sendable {
return m_connected;
}
private static int toUShort(int upper, int lower) {
return ((upper & 0xFF) << 8) + (lower & 0xFF);
}
private static int toShort(int upper, int lower) {
return (short) (((upper & 0xFF) << 8) + (lower & 0xFF));
}
@@ -609,7 +613,7 @@ public class ADIS16470_IMU implements AutoCloseable, Sendable {
m_spi.write(buf, 2);
m_spi.read(false, buf, 2);
return (buf[0] << 8) & buf[1];
return toUShort(buf[0], buf[1]);
}
private void writeRegister(int reg, int val) {

View File

@@ -4,6 +4,9 @@
package edu.wpi.first.wpilibj;
import edu.wpi.first.hal.FRCNetComm.tInstances;
import edu.wpi.first.hal.FRCNetComm.tResourceType;
import edu.wpi.first.hal.HAL;
import edu.wpi.first.networktables.NetworkTableInstance;
import edu.wpi.first.util.FileLogger;
import edu.wpi.first.util.WPIUtilJNI;
@@ -244,6 +247,7 @@ public final class DataLogManager {
if (!new File("/u/logs").mkdir()) {
// ignored
}
HAL.report(tResourceType.kResourceType_DataLogManager, tInstances.kDataLogLocation_USB);
return "/u/logs";
}
} catch (IOException ex) {
@@ -258,6 +262,7 @@ public final class DataLogManager {
if (!new File("/home/lvuser/logs").mkdir()) {
// ignored
}
HAL.report(tResourceType.kResourceType_DataLogManager, tInstances.kDataLogLocation_Onboard);
return "/home/lvuser/logs";
}
String logDir = Filesystem.getOperatingDirectory().getAbsolutePath() + "/logs";

View File

@@ -93,6 +93,19 @@ import java.util.function.DoubleSupplier;
*/
@FunctionalInterface
public interface LEDPattern {
/** A functional interface for index mapping functions. */
@FunctionalInterface
interface IndexMapper {
/**
* Maps the index.
*
* @param bufLen Length of the buffer
* @param index The index to map
* @return The mapped index
*/
int apply(int bufLen, int index);
}
/**
* Writes the pattern to an LED buffer. Dynamic animations should be called periodically (such as
* with a command or with a periodic method) to refresh the buffer over time.
@@ -129,6 +142,41 @@ public interface LEDPattern {
applyTo(readWriter, readWriter);
}
/**
* Creates a pattern with remapped indices.
*
* @param indexMapper the index mapper
* @return the mapped pattern
*/
default LEDPattern mapIndex(IndexMapper indexMapper) {
return (reader, writer) -> {
int bufLen = reader.getLength();
applyTo(
new LEDReader() {
@Override
public int getLength() {
return reader.getLength();
}
@Override
public int getRed(int index) {
return reader.getRed(indexMapper.apply(bufLen, index));
}
@Override
public int getGreen(int index) {
return reader.getGreen(indexMapper.apply(bufLen, index));
}
@Override
public int getBlue(int index) {
return reader.getBlue(indexMapper.apply(bufLen, index));
}
},
(i, r, g, b) -> writer.setRGB(indexMapper.apply(bufLen, i), r, g, b));
};
}
/**
* Creates a pattern that displays this one in reverse. Scrolling patterns will scroll in the
* opposite direction (but at the same speed). It will treat the end of an LED strip as the start,
@@ -143,10 +191,7 @@ public interface LEDPattern {
* @see AddressableLEDBufferView#reversed()
*/
default LEDPattern reversed() {
return (reader, writer) -> {
int bufLen = reader.getLength();
applyTo(reader, (i, r, g, b) -> writer.setRGB((bufLen - 1) - i, r, g, b));
};
return mapIndex((length, index) -> length - 1 - index);
}
/**
@@ -157,15 +202,7 @@ public interface LEDPattern {
* @return the offset pattern
*/
default LEDPattern offsetBy(int offset) {
return (reader, writer) -> {
int bufLen = reader.getLength();
applyTo(
reader,
(i, r, g, b) -> {
int shiftedIndex = Math.floorMod(i + offset, bufLen);
writer.setRGB(shiftedIndex, r, g, b);
});
};
return mapIndex((length, index) -> Math.floorMod(index + offset, length));
}
/**
@@ -189,23 +226,16 @@ public interface LEDPattern {
default LEDPattern scrollAtRelativeSpeed(Frequency velocity) {
final double periodMicros = velocity.asPeriod().in(Microseconds);
return (reader, writer) -> {
int bufLen = reader.getLength();
long now = RobotController.getTime();
return mapIndex(
(bufLen, index) -> {
long now = RobotController.getTime();
// index should move by (buf.length) / (period)
double t = (now % (long) periodMicros) / periodMicros;
int offset = (int) (t * bufLen);
// index should move by (buf.length) / (period)
double t = (now % (long) periodMicros) / periodMicros;
int offset = (int) (t * bufLen);
applyTo(
reader,
(i, r, g, b) -> {
// floorMod so if the offset is negative, we still get positive outputs
int shiftedIndex = Math.floorMod(i + offset, bufLen);
writer.setRGB(shiftedIndex, r, g, b);
});
};
return Math.floorMod(index + offset, bufLen);
});
}
/**
@@ -240,22 +270,16 @@ public interface LEDPattern {
var metersPerMicro = velocity.in(Meters.per(Microsecond));
var microsPerLED = (int) (ledSpacing.in(Meters) / metersPerMicro);
return (reader, writer) -> {
int bufLen = reader.getLength();
long now = RobotController.getTime();
return mapIndex(
(bufLen, index) -> {
long now = RobotController.getTime();
// every step in time that's a multiple of microsPerLED will increment the offset by 1
var offset = now / microsPerLED;
// every step in time that's a multiple of microsPerLED will increment the offset by 1
var offset = (int) (now / microsPerLED);
applyTo(
reader,
(i, r, g, b) -> {
// floorMod so if the offset is negative, we still get positive outputs
int shiftedIndex = Math.floorMod(i + offset, bufLen);
writer.setRGB(shiftedIndex, r, g, b);
});
};
// floorMod so if the offset is negative, we still get positive outputs
return Math.floorMod(index + offset, bufLen);
});
}
/**

View File

@@ -96,8 +96,7 @@ public class PneumaticHub implements PneumaticsBase {
private static DataStore getForModule(int module) {
synchronized (m_handleLock) {
Integer moduleBoxed = module;
DataStore pcm = m_handleMap.get(moduleBoxed);
DataStore pcm = m_handleMap.get(module);
if (pcm == null) {
pcm = new DataStore(module);
}

View File

@@ -47,8 +47,7 @@ public class PneumaticsControlModule implements PneumaticsBase {
private static DataStore getForModule(int module) {
synchronized (m_handleLock) {
Integer moduleBoxed = module;
DataStore pcm = m_handleMap.get(moduleBoxed);
DataStore pcm = m_handleMap.get(module);
if (pcm == null) {
pcm = new DataStore(module);
}

View File

@@ -4,6 +4,7 @@
package edu.wpi.first.wpilibj;
import edu.wpi.first.hal.FRCNetComm.tInstances;
import edu.wpi.first.hal.FRCNetComm.tResourceType;
import edu.wpi.first.hal.HAL;
import edu.wpi.first.hal.PowerDistributionFaults;
@@ -51,7 +52,11 @@ public class PowerDistribution implements Sendable, AutoCloseable {
m_handle = PowerDistributionJNI.initialize(module, moduleType.value);
m_module = PowerDistributionJNI.getModuleNumber(m_handle);
HAL.report(tResourceType.kResourceType_PDP, m_module + 1);
if (moduleType == ModuleType.kCTRE) {
HAL.report(tResourceType.kResourceType_PDP, tInstances.kPDP_CTRE);
} else {
HAL.report(tResourceType.kResourceType_PDP, tInstances.kPDP_REV);
}
SendableRegistry.addLW(this, "PowerDistribution", m_module);
}
@@ -65,7 +70,12 @@ public class PowerDistribution implements Sendable, AutoCloseable {
m_handle = PowerDistributionJNI.initialize(kDefaultModule, PowerDistributionJNI.AUTOMATIC_TYPE);
m_module = PowerDistributionJNI.getModuleNumber(m_handle);
HAL.report(tResourceType.kResourceType_PDP, m_module + 1);
if (PowerDistributionJNI.getType(m_handle) == PowerDistributionJNI.CTRE_TYPE) {
HAL.report(tResourceType.kResourceType_PDP, tInstances.kPDP_CTRE);
} else {
HAL.report(tResourceType.kResourceType_PDP, tInstances.kPDP_REV);
}
SendableRegistry.addLW(this, "PowerDistribution", m_module);
}

View File

@@ -117,6 +117,8 @@ public abstract class RobotBase implements AutoCloseable {
tResourceType.kResourceType_ProfiledPIDController, count);
case kController_BangBangController -> HAL.report(
tResourceType.kResourceType_BangBangController, count);
case kTrajectory_PathWeaver -> HAL.report(
tResourceType.kResourceType_PathWeaverTrajectory, count);
default -> {
// NOP
}

View File

@@ -184,7 +184,7 @@ public class DCMotorSim extends LinearSystemSim<N2, N1, N2> {
*/
public double getAngularAccelerationRadPerSecSq() {
var acceleration = (m_plant.getA().times(m_x)).plus(m_plant.getB().times(m_u));
return acceleration.get(0, 0);
return acceleration.get(1, 0);
}
/**

View File

@@ -5,6 +5,7 @@
package edu.wpi.first.wpilibj;
import static edu.wpi.first.units.Units.Centimeters;
import static edu.wpi.first.units.Units.Meters;
import static edu.wpi.first.units.Units.MetersPerSecond;
import static edu.wpi.first.units.Units.Microsecond;
import static edu.wpi.first.units.Units.Microseconds;
@@ -15,6 +16,7 @@ import static edu.wpi.first.wpilibj.LEDPattern.GradientType.kContinuous;
import static edu.wpi.first.wpilibj.LEDPattern.GradientType.kDiscontinuous;
import static edu.wpi.first.wpilibj.util.Color.kBlack;
import static edu.wpi.first.wpilibj.util.Color.kBlue;
import static edu.wpi.first.wpilibj.util.Color.kGreen;
import static edu.wpi.first.wpilibj.util.Color.kLime;
import static edu.wpi.first.wpilibj.util.Color.kMagenta;
import static edu.wpi.first.wpilibj.util.Color.kMidnightBlue;
@@ -815,6 +817,176 @@ class LEDPatternTest {
assertColorEquals(kWhite, buffer.getLED(0));
}
@Test
void reverseMask() {
var pattern =
LEDPattern.steps(Map.of(0, kRed, 0.25, kBlue, 0.5, kYellow, 0.75, kGreen))
.mask(LEDPattern.steps(Map.of(0, kWhite, 0.5, kBlack)))
.reversed();
var buffer = new AddressableLEDBuffer(8);
pattern.applyTo(buffer);
assertColorEquals(kRed, buffer.getLED(7));
assertColorEquals(kRed, buffer.getLED(6));
assertColorEquals(kBlue, buffer.getLED(5));
assertColorEquals(kBlue, buffer.getLED(4));
assertColorEquals(kBlack, buffer.getLED(3));
assertColorEquals(kBlack, buffer.getLED(2));
assertColorEquals(kBlack, buffer.getLED(1));
assertColorEquals(kBlack, buffer.getLED(0));
}
@Test
void offsetMask() {
var pattern =
LEDPattern.steps(Map.of(0, kRed, 0.25, kBlue, 0.5, kYellow, 0.75, kGreen))
.mask(LEDPattern.steps(Map.of(0, kWhite, 0.5, kBlack)))
.offsetBy(4);
var buffer = new AddressableLEDBuffer(8);
pattern.applyTo(buffer);
assertColorEquals(kBlack, buffer.getLED(0));
assertColorEquals(kBlack, buffer.getLED(1));
assertColorEquals(kBlack, buffer.getLED(2));
assertColorEquals(kBlack, buffer.getLED(3));
assertColorEquals(kRed, buffer.getLED(4));
assertColorEquals(kRed, buffer.getLED(5));
assertColorEquals(kBlue, buffer.getLED(6));
assertColorEquals(kBlue, buffer.getLED(7));
}
@Test
void relativeScrollingMask() {
// [red, red, blue, blue, yellow, yellow, green, green]
// under a mask of first 50% on, last 50% off
// [red, red, blue, blue, black, black, black, black]
// all scrolling at 1 LED per microsecond
var pattern =
LEDPattern.steps(Map.of(0, kRed, 0.25, kBlue, 0.5, kYellow, 0.75, kGreen))
.mask(LEDPattern.steps(Map.of(0, kWhite, 0.5, kBlack)))
.scrollAtRelativeSpeed(Percent.per(Microsecond).of(12.5));
var buffer = new AddressableLEDBuffer(8);
{
m_mockTime = 0; // start
pattern.applyTo(buffer);
assertColorEquals(kRed, buffer.getLED(0));
assertColorEquals(kRed, buffer.getLED(1));
assertColorEquals(kBlue, buffer.getLED(2));
assertColorEquals(kBlue, buffer.getLED(3));
assertColorEquals(kBlack, buffer.getLED(4));
assertColorEquals(kBlack, buffer.getLED(5));
assertColorEquals(kBlack, buffer.getLED(6));
assertColorEquals(kBlack, buffer.getLED(7));
}
{
m_mockTime = 1;
pattern.applyTo(buffer);
assertColorEquals(kBlack, buffer.getLED(0));
assertColorEquals(kRed, buffer.getLED(1));
assertColorEquals(kRed, buffer.getLED(2));
assertColorEquals(kBlue, buffer.getLED(3));
assertColorEquals(kBlue, buffer.getLED(4));
assertColorEquals(kBlack, buffer.getLED(5));
assertColorEquals(kBlack, buffer.getLED(6));
assertColorEquals(kBlack, buffer.getLED(7));
}
{
m_mockTime = 2;
pattern.applyTo(buffer);
assertColorEquals(kBlack, buffer.getLED(0));
assertColorEquals(kBlack, buffer.getLED(1));
assertColorEquals(kRed, buffer.getLED(2));
assertColorEquals(kRed, buffer.getLED(3));
assertColorEquals(kBlue, buffer.getLED(4));
assertColorEquals(kBlue, buffer.getLED(5));
assertColorEquals(kBlack, buffer.getLED(6));
assertColorEquals(kBlack, buffer.getLED(7));
}
{
m_mockTime = 3;
pattern.applyTo(buffer);
assertColorEquals(kBlack, buffer.getLED(0));
assertColorEquals(kBlack, buffer.getLED(1));
assertColorEquals(kBlack, buffer.getLED(2));
assertColorEquals(kRed, buffer.getLED(3));
assertColorEquals(kRed, buffer.getLED(4));
assertColorEquals(kBlue, buffer.getLED(5));
assertColorEquals(kBlue, buffer.getLED(6));
assertColorEquals(kBlack, buffer.getLED(7));
}
}
@Test
void absoluteScrollingMask() {
// [red, red, blue, blue, yellow, yellow, green, green]
// under a mask of first 50% on, last 50% off
// [red, red, blue, blue, black, black, black, black]
// all scrolling at 1 LED per microsecond
var pattern =
LEDPattern.steps(Map.of(0, kRed, 0.25, kBlue, 0.5, kYellow, 0.75, kGreen))
.mask(LEDPattern.steps(Map.of(0, kWhite, 0.5, kBlack)))
.scrollAtAbsoluteSpeed(Meters.per(Microsecond).of(1), Meters.one());
var buffer = new AddressableLEDBuffer(8);
{
m_mockTime = 0; // start
pattern.applyTo(buffer);
assertColorEquals(kRed, buffer.getLED(0));
assertColorEquals(kRed, buffer.getLED(1));
assertColorEquals(kBlue, buffer.getLED(2));
assertColorEquals(kBlue, buffer.getLED(3));
assertColorEquals(kBlack, buffer.getLED(4));
assertColorEquals(kBlack, buffer.getLED(5));
assertColorEquals(kBlack, buffer.getLED(6));
assertColorEquals(kBlack, buffer.getLED(7));
}
{
m_mockTime = 1;
pattern.applyTo(buffer);
assertColorEquals(kBlack, buffer.getLED(0));
assertColorEquals(kRed, buffer.getLED(1));
assertColorEquals(kRed, buffer.getLED(2));
assertColorEquals(kBlue, buffer.getLED(3));
assertColorEquals(kBlue, buffer.getLED(4));
assertColorEquals(kBlack, buffer.getLED(5));
assertColorEquals(kBlack, buffer.getLED(6));
assertColorEquals(kBlack, buffer.getLED(7));
}
{
m_mockTime = 2;
pattern.applyTo(buffer);
assertColorEquals(kBlack, buffer.getLED(0));
assertColorEquals(kBlack, buffer.getLED(1));
assertColorEquals(kRed, buffer.getLED(2));
assertColorEquals(kRed, buffer.getLED(3));
assertColorEquals(kBlue, buffer.getLED(4));
assertColorEquals(kBlue, buffer.getLED(5));
assertColorEquals(kBlack, buffer.getLED(6));
assertColorEquals(kBlack, buffer.getLED(7));
}
{
m_mockTime = 3;
pattern.applyTo(buffer);
assertColorEquals(kBlack, buffer.getLED(0));
assertColorEquals(kBlack, buffer.getLED(1));
assertColorEquals(kBlack, buffer.getLED(2));
assertColorEquals(kRed, buffer.getLED(3));
assertColorEquals(kRed, buffer.getLED(4));
assertColorEquals(kBlue, buffer.getLED(5));
assertColorEquals(kBlue, buffer.getLED(6));
assertColorEquals(kBlack, buffer.getLED(7));
}
}
void assertColorEquals(Color expected, Color actual) {
assertEquals(new Color8Bit(expected), new Color8Bit(actual));
}

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