mirror of
https://github.com/wpilibsuite/allwpilib
synced 2026-06-30 02:31:44 +00:00
Compare commits
60 Commits
v2025.1.1-
...
v2025.1.1-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
80c391e182 | ||
|
|
70f36cce7e | ||
|
|
564c1f2de2 | ||
|
|
a1b642a402 | ||
|
|
f9b3efb712 | ||
|
|
782459dff4 | ||
|
|
4bca79b9af | ||
|
|
68285dae77 | ||
|
|
5e3dba672a | ||
|
|
e943424609 | ||
|
|
4225b732fd | ||
|
|
39d05ebe7c | ||
|
|
cc41a0ed24 | ||
|
|
d5edb4060d | ||
|
|
e08fdeba21 | ||
|
|
544553a58f | ||
|
|
838c5fbcd7 | ||
|
|
38a239b531 | ||
|
|
9a1b4245fa | ||
|
|
e222efaa01 | ||
|
|
f772bb141d | ||
|
|
278efa6384 | ||
|
|
e876452967 | ||
|
|
5c95966d44 | ||
|
|
882233bede | ||
|
|
1921d019b7 | ||
|
|
c3fc7c829d | ||
|
|
4ce8930342 | ||
|
|
60198c0bec | ||
|
|
54f0e11fc0 | ||
|
|
de82ed434d | ||
|
|
4dd3a36d2e | ||
|
|
92f8c89267 | ||
|
|
1eecaaf337 | ||
|
|
892e062316 | ||
|
|
9807d60566 | ||
|
|
c387a7ecae | ||
|
|
715cbb6b76 | ||
|
|
9d40b993f8 | ||
|
|
92ee5bc523 | ||
|
|
5012ad7499 | ||
|
|
145450b73d | ||
|
|
b91864a5ec | ||
|
|
0941251375 | ||
|
|
7d178615fa | ||
|
|
806d56e564 | ||
|
|
65f3345407 | ||
|
|
5e1c6a84ce | ||
|
|
a0af0fd572 | ||
|
|
f377a9c573 | ||
|
|
62338c7287 | ||
|
|
49e3e4a0be | ||
|
|
59dbfc9c2d | ||
|
|
9607c6c10d | ||
|
|
6ef5b85758 | ||
|
|
b6de7acbdb | ||
|
|
fe28fa1ded | ||
|
|
b7eb9fb8f9 | ||
|
|
1d58c5025e | ||
|
|
b4a8d33486 |
56
.github/labeler.yml
vendored
Normal file
56
.github/labeler.yml
vendored
Normal 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/**
|
||||
2
.github/workflows/cmake.yml
vendored
2
.github/workflows/cmake.yml
vendored
@@ -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
|
||||
|
||||
2
.github/workflows/comment-command.yml
vendored
2
.github/workflows/comment-command.yml
vendored
@@ -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
|
||||
|
||||
6
.github/workflows/documentation.yml
vendored
6
.github/workflows/documentation.yml
vendored
@@ -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 🔑
|
||||
|
||||
45
.github/workflows/gradle.yml
vendored
45
.github/workflows/gradle.yml
vendored
@@ -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
12
.github/workflows/labeler.yml
vendored
Normal 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
|
||||
6
.github/workflows/lint-format.yml
vendored
6
.github/workflows/lint-format.yml
vendored
@@ -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
|
||||
|
||||
2
.github/workflows/sanitizers.yml
vendored
2
.github/workflows/sanitizers.yml
vendored
@@ -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
|
||||
|
||||
6
.github/workflows/sentinel-build.yml
vendored
6
.github/workflows/sentinel-build.yml
vendored
@@ -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
|
||||
|
||||
2
.github/workflows/tools.yml
vendored
2
.github/workflows/tools.yml
vendored
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -9,5 +9,5 @@ repositories {
|
||||
}
|
||||
}
|
||||
dependencies {
|
||||
implementation "edu.wpi.first:native-utils:2025.3.0"
|
||||
implementation "edu.wpi.first:native-utils:2025.9.0"
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ application {
|
||||
mainClass = 'edu.wpi.Main'
|
||||
}
|
||||
|
||||
apply plugin: 'com.github.johnrengelman.shadow'
|
||||
apply plugin: 'com.gradleup.shadow'
|
||||
|
||||
repositories {
|
||||
maven {
|
||||
|
||||
@@ -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
|
||||
```
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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) + ")";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) + ")";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) + ")";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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] }
|
||||
|
||||
@@ -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) + ")";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) + ")";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
});
|
||||
|
||||
@@ -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.
|
||||
@@ -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")
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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. */
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
/**
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
48
upstream_utils/debugging.py
Executable 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()
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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());
|
||||
@@ -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 {
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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";
|
||||
@@ -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 +
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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
|
||||
@@ -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 ++--
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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};
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user