mirror of
https://github.com/wpilibsuite/allwpilib
synced 2026-06-23 01:21:42 +00:00
Compare commits
109 Commits
v2027.0.0-
...
v2026.2.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7ca35e5678 | ||
|
|
613bd88548 | ||
|
|
e311722637 | ||
|
|
ae43b8b6dd | ||
|
|
5ae8ee06dd | ||
|
|
d9eba4bb22 | ||
|
|
9cd933fa14 | ||
|
|
77dfad97c6 | ||
|
|
7ac0612397 | ||
|
|
2c5529d714 | ||
|
|
7cd3790c7c | ||
|
|
664484306c | ||
|
|
0a37317467 | ||
|
|
6b225bb1f1 | ||
|
|
3d92547d62 | ||
|
|
c89401250f | ||
|
|
8be7720a68 | ||
|
|
21b5389bbe | ||
|
|
9e1258440b | ||
|
|
812a1b8e1a | ||
|
|
18249badc0 | ||
|
|
b82d204525 | ||
|
|
ccb3266753 | ||
|
|
71a788e20b | ||
|
|
f395954d3c | ||
|
|
ab3af00d07 | ||
|
|
1b2f051b4b | ||
|
|
3dc334c1ee | ||
|
|
baa6379267 | ||
|
|
0d1dd84e86 | ||
|
|
a61866912b | ||
|
|
57c40a3dfc | ||
|
|
6f86f533e5 | ||
|
|
ded6790bcd | ||
|
|
769ce5e9fa | ||
|
|
f6b4ad575b | ||
|
|
7cd0ef5bd9 | ||
|
|
bd7a88a6d0 | ||
|
|
95cb38e6df | ||
|
|
b8d6bc2eb1 | ||
|
|
688535298b | ||
|
|
02252b58d7 | ||
|
|
e207ca4880 | ||
|
|
f4db88da9a | ||
|
|
e45aadc851 | ||
|
|
fea6883d98 | ||
|
|
b7fe5ef833 | ||
|
|
cd237e57d4 | ||
|
|
8b99ad82c3 | ||
|
|
58ba536351 | ||
|
|
4aef52a117 | ||
|
|
a6a4912a80 | ||
|
|
873e960e93 | ||
|
|
4f133c6aa1 | ||
|
|
35dd61cde5 | ||
|
|
9e85f3cf55 | ||
|
|
2b43541b94 | ||
|
|
4c4996e638 | ||
|
|
cfbd7a5af2 | ||
|
|
f5990e8b40 | ||
|
|
b56b843c8a | ||
|
|
2fb5271cc9 | ||
|
|
f1b9be551b | ||
|
|
3972b01c51 | ||
|
|
871769c815 | ||
|
|
ca7718cb08 | ||
|
|
5e7e5306df | ||
|
|
6447011bc3 | ||
|
|
bb5ee73e46 | ||
|
|
0277759d44 | ||
|
|
f9899eb73f | ||
|
|
6b8be313c7 | ||
|
|
ab53d51c6f | ||
|
|
5003939b64 | ||
|
|
ab259c2e89 | ||
|
|
8fb5a1985a | ||
|
|
1e50471d2c | ||
|
|
c575a23e8e | ||
|
|
4522cca70f | ||
|
|
850a148aad | ||
|
|
0a4e44ea06 | ||
|
|
a7e7f6912a | ||
|
|
ee0a8a1e56 | ||
|
|
3dbdfa1839 | ||
|
|
ee3d55e848 | ||
|
|
dbffe6e8ac | ||
|
|
2639e0365b | ||
|
|
08f11488b0 | ||
|
|
632749e6f3 | ||
|
|
b0829356fa | ||
|
|
ed904851eb | ||
|
|
2cfd58f119 | ||
|
|
129cbbe11d | ||
|
|
45db0fd45e | ||
|
|
9fd4ccf95b | ||
|
|
4e6b9706ff | ||
|
|
0d9e850e22 | ||
|
|
46a3318324 | ||
|
|
f209ecb0cb | ||
|
|
78fa67099e | ||
|
|
9ac7e286f5 | ||
|
|
5fd9e1e72a | ||
|
|
0a0adebd89 | ||
|
|
2d11946d98 | ||
|
|
c42fde5d07 | ||
|
|
b3aeee18c8 | ||
|
|
feee88f40d | ||
|
|
0478176e47 | ||
|
|
e678a338b4 |
@@ -45,6 +45,8 @@ Checks:
|
||||
-clang-diagnostic-#warnings,
|
||||
-clang-diagnostic-pedantic,
|
||||
clang-analyzer-*,
|
||||
-clang-analyzer-optin.cplusplus.UninitializedObject,
|
||||
-clang-analyzer-security.FloatLoopCounter,
|
||||
cppcoreguidelines-slicing,
|
||||
google-build-namespaces,
|
||||
google-explicit-constructor,
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
color: false
|
||||
definitions: [cmake/modules]
|
||||
line_length: 100
|
||||
list_expansion: favour-inlining
|
||||
quiet: false
|
||||
unsafe: false
|
||||
|
||||
@@ -3,7 +3,10 @@
|
||||
{
|
||||
"aql": {
|
||||
"items.find": {
|
||||
"repo": "wpilib-mvn-development-local",
|
||||
"$or":[
|
||||
{ "repo": "wpilib-mvn-development-local" },
|
||||
{ "repo": "wpilib-mvn-development-2027-local" }
|
||||
],
|
||||
"path": { "$nmatch":"*edu/wpi/first/thirdparty*" },
|
||||
"$or":[
|
||||
{
|
||||
|
||||
2
.github/workflows/bazel.yml
vendored
2
.github/workflows/bazel.yml
vendored
@@ -37,7 +37,7 @@ jobs:
|
||||
|
||||
build-mac:
|
||||
name: "Mac"
|
||||
runs-on: macos-14
|
||||
runs-on: macOS-15
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with: { fetch-depth: 0 }
|
||||
|
||||
8
.github/workflows/cmake-android.yml
vendored
8
.github/workflows/cmake-android.yml
vendored
@@ -30,7 +30,7 @@ jobs:
|
||||
- uses: nttld/setup-ndk@v1
|
||||
id: setup-ndk
|
||||
with:
|
||||
ndk-version: r27c
|
||||
ndk-version: r27d
|
||||
add-to-path: false
|
||||
|
||||
- uses: actions/setup-java@v4
|
||||
@@ -46,6 +46,12 @@ jobs:
|
||||
|
||||
- name: configure
|
||||
run: cmake --preset with-sccache -DCMAKE_BUILD_TYPE=RelWithDebInfo -DWITH_WPILIB=OFF -DWITH_GUI=OFF -DWITH_CSCORE=OFF -DWITH_TESTS=OFF -DWITH_SIMULATION_MODULES=OFF -DWITH_PROTOBUF=OFF -DWITH_JAVA=ON -DBUILD_SHARED_LIBS=ON -DCMAKE_TOOLCHAIN_FILE=${{ steps.setup-ndk.outputs.ndk-path }}/build/cmake/android.toolchain.cmake -DANDROID_ABI="${{ matrix.abi }}" -DANDROID_PLATFORM=android-24
|
||||
env:
|
||||
SCCACHE_WEBDAV_USERNAME: ${{ secrets.ARTIFACTORY_USERNAME }}
|
||||
SCCACHE_WEBDAV_PASSWORD: ${{ secrets.ARTIFACTORY_PASSWORD }}
|
||||
|
||||
- name: build
|
||||
run: cmake --build build-cmake --parallel $(nproc)
|
||||
env:
|
||||
SCCACHE_WEBDAV_USERNAME: ${{ secrets.ARTIFACTORY_USERNAME }}
|
||||
SCCACHE_WEBDAV_PASSWORD: ${{ secrets.ARTIFACTORY_PASSWORD }}
|
||||
|
||||
4
.github/workflows/cmake.yml
vendored
4
.github/workflows/cmake.yml
vendored
@@ -20,7 +20,7 @@ jobs:
|
||||
name: Linux
|
||||
container: wpilib/roborio-cross-ubuntu:2025-22.04
|
||||
flags: "--preset with-java-and-sccache -DCMAKE_BUILD_TYPE=Release -DWITH_EXAMPLES=ON"
|
||||
- os: macOS-14
|
||||
- os: macOS-15
|
||||
name: macOS
|
||||
container: ""
|
||||
env: ""
|
||||
@@ -59,7 +59,7 @@ jobs:
|
||||
uses: lukka/run-vcpkg@v11.5
|
||||
with:
|
||||
vcpkgDirectory: ${{ runner.workspace }}/vcpkg
|
||||
vcpkgGitCommitId: 37c3e63a1306562f7f59c4c3c8892ddd50fdf992 # HEAD on 2024-02-24
|
||||
vcpkgGitCommitId: 74e6536215718009aae747d86d84b78376bf9e09 # HEAD on 2025-10-17
|
||||
|
||||
- name: configure
|
||||
run: cmake ${{ matrix.flags }}
|
||||
|
||||
67
.github/workflows/documentation.yml
vendored
67
.github/workflows/documentation.yml
vendored
@@ -1,67 +0,0 @@
|
||||
name: Documentation
|
||||
|
||||
on: [push, workflow_dispatch]
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
BASE_PATH: allwpilib/docs
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
name: "Documentation - Publish"
|
||||
runs-on: ubuntu-22.04
|
||||
if: github.repository == 'wpilibsuite/allwpilib' && (github.ref == 'refs/heads/main' || (startsWith(github.ref, 'refs/tags/v') && !contains(github.ref, '2027')))
|
||||
concurrency: ci-docs-publish
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
persist-credentials: false
|
||||
- uses: gradle/actions/wrapper-validation@v4
|
||||
- uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: 17
|
||||
- name: Set environment variables (Development)
|
||||
run: |
|
||||
echo "BRANCH=development" >> $GITHUB_ENV
|
||||
if: github.ref == 'refs/heads/main'
|
||||
- name: Set environment variables (Tag)
|
||||
run: |
|
||||
echo "EXTRA_GRADLE_ARGS=-PreleaseMode" >> $GITHUB_ENV
|
||||
echo "BRANCH=beta" >> $GITHUB_ENV
|
||||
if: startsWith(github.ref, 'refs/tags/v') && !contains(github.ref, '2027')
|
||||
- name: Set environment variables (Release)
|
||||
run: |
|
||||
echo "EXTRA_GRADLE_ARGS=-PreleaseMode" >> $GITHUB_ENV
|
||||
echo "BRANCH=release" >> $GITHUB_ENV
|
||||
if: startsWith(github.ref, 'refs/tags/v') && !contains(github.ref, 'alpha') && !contains(github.ref, 'beta') && !contains(github.ref, '2027')
|
||||
- name: Build with Gradle
|
||||
run: ./gradlew docs:generateJavaDocs docs:doxygen -PbuildServer ${{ env.EXTRA_GRADLE_ARGS }}
|
||||
- name: Install SSH Client 🔑
|
||||
uses: webfactory/ssh-agent@v0.9.0
|
||||
with:
|
||||
ssh-private-key: ${{ secrets.GH_DEPLOY_KEY }}
|
||||
- name: Deploy 🚀
|
||||
uses: JamesIves/github-pages-deploy-action@v4.6.1
|
||||
with:
|
||||
ssh-key: true
|
||||
repository-name: wpilibsuite/wpilibsuite.github.io
|
||||
branch: allwpilib-${{ env.BRANCH }}
|
||||
clean: true
|
||||
single-commit: true
|
||||
folder: docs/build/docs
|
||||
- name: Trigger Workflow
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
github-token: ${{ secrets.DISPATCH_PAT_TOKEN }}
|
||||
script: |
|
||||
github.rest.actions.createWorkflowDispatch({
|
||||
owner: context.repo.owner,
|
||||
repo: 'wpilibsuite.github.io',
|
||||
workflow_id: 'static.yml',
|
||||
ref: 'main',
|
||||
})
|
||||
3
.github/workflows/fix_compile_commands.py
vendored
3
.github/workflows/fix_compile_commands.py
vendored
@@ -31,6 +31,9 @@ def main():
|
||||
# Replace GCC warning argument with one Clang recognizes
|
||||
elif arg == "-Wno-maybe-uninitialized":
|
||||
out_args.append("-Wno-uninitialized")
|
||||
# Skip GCC-specific warning argument
|
||||
elif arg == "-Wno-error=restrict":
|
||||
pass
|
||||
else:
|
||||
out_args.append(arg)
|
||||
|
||||
|
||||
78
.github/workflows/gradle.yml
vendored
78
.github/workflows/gradle.yml
vendored
@@ -52,11 +52,11 @@ jobs:
|
||||
run: echo "EXTRA_GRADLE_ARGS=-PreleaseMode" >> $GITHUB_ENV
|
||||
if: startsWith(github.ref, 'refs/tags/v') && !contains(github.ref, '2027')
|
||||
- name: Build with Gradle
|
||||
uses: addnab/docker-run-action@v3
|
||||
uses: wpilibsuite/docker-run-action@v4
|
||||
with:
|
||||
image: ${{ matrix.container }}
|
||||
options: -v ${{ github.workspace }}:/work -w /work -e ARTIFACTORY_PUBLISH_USERNAME -e ARTIFACTORY_PUBLISH_PASSWORD -e GITHUB_REF -e CI
|
||||
run: df . && rm -f semicolon_delimited_script && echo $GITHUB_REF && ./gradlew build --build-cache -PbuildServer -PskipJavaFormat ${{ matrix.build-options }} ${{ env.EXTRA_GRADLE_ARGS }}
|
||||
run: df . && echo $GITHUB_REF && ./gradlew build --build-cache -PbuildServer -PskipJavaFormat ${{ matrix.build-options }} ${{ env.EXTRA_GRADLE_ARGS }}
|
||||
env:
|
||||
ARTIFACTORY_PUBLISH_USERNAME: ${{ secrets.ARTIFACTORY_USERNAME }}
|
||||
ARTIFACTORY_PUBLISH_PASSWORD: ${{ secrets.ARTIFACTORY_PASSWORD }}
|
||||
@@ -98,7 +98,7 @@ jobs:
|
||||
build-options: "-PciReleaseOnly -Pbuildwinarm64 -Ponlywindowsarm64"
|
||||
task: "copyAllOutputs"
|
||||
outputs: "build/allOutputs"
|
||||
- os: macOS-14
|
||||
- os: macOS-15
|
||||
artifact-name: macOS
|
||||
architecture: aarch64
|
||||
task: "build"
|
||||
@@ -153,7 +153,7 @@ jobs:
|
||||
if: matrix.os == 'windows-2022'
|
||||
- name: Check disk free space pre-cleanup (macOS)
|
||||
run: df -h .
|
||||
if: matrix.os == 'macOS-14'
|
||||
if: matrix.os == 'macOS-15'
|
||||
- name: Cleanup disk space
|
||||
# CodeQL: 5G
|
||||
# go: 748M
|
||||
@@ -162,10 +162,10 @@ jobs:
|
||||
rm -rf /Users/runner/hostedtoolcache/CodeQL
|
||||
rm -rf /Users/runner/hostedtoolcache/go
|
||||
rm -rf /Users/runner/Library/Android
|
||||
if: matrix.os == 'macOS-14'
|
||||
if: matrix.os == 'macOS-15'
|
||||
- name: Check disk free space post-cleanup (macOS)
|
||||
run: df -h .
|
||||
if: matrix.os == 'macOS-14'
|
||||
if: matrix.os == 'macOS-15'
|
||||
- name: Build with Gradle
|
||||
run: ./gradlew ${{ matrix.task }} --build-cache -PbuildServer -PskipJavaFormat ${{ matrix.build-options }} ${{ env.EXTRA_GRADLE_ARGS }}
|
||||
env:
|
||||
@@ -181,7 +181,7 @@ jobs:
|
||||
if: matrix.os == 'windows-2022'
|
||||
- name: Check disk free space (macOS)
|
||||
run: df -h .
|
||||
if: matrix.os == 'macOS-14'
|
||||
if: matrix.os == 'macOS-15'
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ matrix.artifact-name }}
|
||||
@@ -203,7 +203,7 @@ jobs:
|
||||
run: echo "EXTRA_GRADLE_ARGS=-PreleaseMode" >> $GITHUB_ENV
|
||||
if: startsWith(github.ref, 'refs/tags/v') && !contains(github.ref, '2027')
|
||||
- name: Build with Gradle
|
||||
run: ./gradlew docs:zipDocs --build-cache -PbuildServer ${{ env.EXTRA_GRADLE_ARGS }}
|
||||
run: ./gradlew docs:zipDocs --build-cache -PbuildServer -PdocWarningsAsErrors ${{ env.EXTRA_GRADLE_ARGS }}
|
||||
env:
|
||||
ARTIFACTORY_PUBLISH_USERNAME: ${{ secrets.ARTIFACTORY_USERNAME }}
|
||||
ARTIFACTORY_PUBLISH_PASSWORD: ${{ secrets.ARTIFACTORY_PASSWORD }}
|
||||
@@ -212,6 +212,68 @@ jobs:
|
||||
name: Documentation
|
||||
path: docs/build/outputs
|
||||
|
||||
publish:
|
||||
name: "Documentation - Publish"
|
||||
runs-on: ubuntu-22.04
|
||||
if: github.repository == 'wpilibsuite/allwpilib' && (github.ref == 'refs/heads/main' || (startsWith(github.ref, 'refs/tags/v') && !contains(github.ref, '2027')))
|
||||
needs: [build-documentation]
|
||||
concurrency: ci-docs-publish
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
persist-credentials: false
|
||||
- name: Download docs artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: Documentation
|
||||
- name: Make output directories
|
||||
run: |
|
||||
mkdir -p docs/tmp/doxygen/html
|
||||
mkdir -p docs/tmp/javadoc
|
||||
- name: Extract docs
|
||||
run: |
|
||||
unzip _GROUP_edu_wpi_first_wpilibc_ID_documentation_CLS.zip -d docs/tmp/doxygen/html
|
||||
unzip _GROUP_edu_wpi_first_wpilibj_ID_documentation_CLS.zip -d docs/tmp/javadoc
|
||||
- name: Set environment variables (Development)
|
||||
run: |
|
||||
echo "BRANCH=development" >> $GITHUB_ENV
|
||||
if: github.ref == 'refs/heads/main'
|
||||
- name: Set environment variables (Tag)
|
||||
run: |
|
||||
echo "EXTRA_GRADLE_ARGS=-PreleaseMode" >> $GITHUB_ENV
|
||||
echo "BRANCH=beta" >> $GITHUB_ENV
|
||||
if: startsWith(github.ref, 'refs/tags/v') && !contains(github.ref, '2027')
|
||||
- name: Set environment variables (Release)
|
||||
run: |
|
||||
echo "EXTRA_GRADLE_ARGS=-PreleaseMode" >> $GITHUB_ENV
|
||||
echo "BRANCH=release" >> $GITHUB_ENV
|
||||
if: startsWith(github.ref, 'refs/tags/v') && !contains(github.ref, 'alpha') && !contains(github.ref, 'beta') && !contains(github.ref, '2027')
|
||||
- name: Install SSH Client 🔑
|
||||
uses: webfactory/ssh-agent@v0.9.0
|
||||
with:
|
||||
ssh-private-key: ${{ secrets.GH_DEPLOY_KEY }}
|
||||
- name: Deploy 🚀
|
||||
uses: JamesIves/github-pages-deploy-action@v4.6.1
|
||||
with:
|
||||
ssh-key: true
|
||||
repository-name: wpilibsuite/wpilibsuite.github.io
|
||||
branch: allwpilib-${{ env.BRANCH }}
|
||||
clean: true
|
||||
single-commit: true
|
||||
folder: docs/tmp
|
||||
- name: Trigger Workflow
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
github-token: ${{ secrets.DISPATCH_PAT_TOKEN }}
|
||||
script: |
|
||||
github.rest.actions.createWorkflowDispatch({
|
||||
owner: context.repo.owner,
|
||||
repo: 'wpilibsuite.github.io',
|
||||
workflow_id: 'static.yml',
|
||||
ref: 'main',
|
||||
})
|
||||
|
||||
combine:
|
||||
name: Combine
|
||||
needs: [build-docker, build-host, build-documentation]
|
||||
|
||||
19
.github/workflows/lint-format.yml
vendored
19
.github/workflows/lint-format.yml
vendored
@@ -36,7 +36,7 @@ jobs:
|
||||
- name: Install wpiformat
|
||||
run: |
|
||||
python -m venv ${{ runner.temp }}/wpiformat
|
||||
${{ runner.temp }}/wpiformat/bin/pip3 install wpiformat==2025.33
|
||||
${{ runner.temp }}/wpiformat/bin/pip3 install wpiformat==2025.36
|
||||
- name: Run
|
||||
run: ${{ runner.temp }}/wpiformat/bin/wpiformat
|
||||
- name: Check output
|
||||
@@ -78,7 +78,7 @@ jobs:
|
||||
- name: Install wpiformat
|
||||
run: |
|
||||
python -m venv ${{ runner.temp }}/wpiformat
|
||||
${{ runner.temp }}/wpiformat/bin/pip3 install wpiformat==2025.33
|
||||
${{ runner.temp }}/wpiformat/bin/pip3 install wpiformat==2025.36
|
||||
- name: Create compile_commands.json
|
||||
run: |
|
||||
./gradlew generateCompileCommands -Ptoolchain-optional-roboRio
|
||||
@@ -123,18 +123,3 @@ jobs:
|
||||
echo '' >> $GITHUB_STEP_SUMMARY
|
||||
echo '```' >> $GITHUB_STEP_SUMMARY
|
||||
if: ${{ failure() }}
|
||||
|
||||
documentation:
|
||||
name: "Documentation"
|
||||
runs-on: ubuntu-22.04
|
||||
needs: [validation]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: 21
|
||||
- name: Build with Gradle
|
||||
run: ./gradlew docs:zipDocs -PbuildServer -PdocWarningsAsErrors ${{ env.EXTRA_GRADLE_ARGS }}
|
||||
|
||||
23
.github/workflows/sentinel-build.yml
vendored
23
.github/workflows/sentinel-build.yml
vendored
@@ -53,11 +53,11 @@ jobs:
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Build with Gradle
|
||||
uses: addnab/docker-run-action@v3
|
||||
uses: wpilibsuite/docker-run-action@v4
|
||||
with:
|
||||
image: ${{ matrix.container }}
|
||||
options: -v ${{ github.workspace }}:/work -w /work -e GITHUB_REF -e CI
|
||||
run: df . && rm -f semicolon_delimited_script && echo $GITHUB_REF && ./gradlew build -PbuildServer -PskipJavaFormat ${{ matrix.build-options }}
|
||||
run: df . && echo $GITHUB_REF && ./gradlew build -PbuildServer -PskipJavaFormat ${{ matrix.build-options }}
|
||||
- name: Check free disk space
|
||||
run: df .
|
||||
- uses: actions/upload-artifact@v4
|
||||
@@ -97,15 +97,22 @@ jobs:
|
||||
build-options: "-PciReleaseOnly -Pbuildwinarm64 -Ponlywindowsarm64"
|
||||
task: "copyAllOutputs"
|
||||
outputs: "build/allOutputs"
|
||||
- os: macOS-14
|
||||
- os: macOS-15
|
||||
artifact-name: macOS
|
||||
architecture: aarch64
|
||||
task: "build"
|
||||
outputs: "build/allOutputs"
|
||||
- os: windows-2022
|
||||
artifact-name: Win32
|
||||
artifact-name: Win32FFI
|
||||
architecture: x86
|
||||
task: ":ntcoreffi:build"
|
||||
build-options: "-Pntcoreffibuild \"-Dorg.gradle.jvmargs=-Xmx1096m\""
|
||||
outputs: "ntcoreffi/build/outputs"
|
||||
- os: windows-2022
|
||||
artifact-name: Win64FFI
|
||||
architecture: x64
|
||||
task: ":ntcoreffi:build"
|
||||
build-options: "-Pntcoreffibuild -Pbuildwinarm64"
|
||||
outputs: "ntcoreffi/build/outputs"
|
||||
name: "Build - ${{ matrix.artifact-name }}"
|
||||
runs-on: ${{ matrix.os }}
|
||||
@@ -139,7 +146,7 @@ jobs:
|
||||
if: matrix.os == 'windows-2022'
|
||||
- name: Check disk free space pre-cleanup (macOS)
|
||||
run: df -h .
|
||||
if: matrix.os == 'macOS-14'
|
||||
if: matrix.os == 'macOS-15'
|
||||
- name: Cleanup disk space
|
||||
# CodeQL: 5G
|
||||
# go: 748M
|
||||
@@ -148,10 +155,10 @@ jobs:
|
||||
rm -rf /Users/runner/hostedtoolcache/CodeQL
|
||||
rm -rf /Users/runner/hostedtoolcache/go
|
||||
rm -rf /Users/runner/Library/Android
|
||||
if: matrix.os == 'macOS-14'
|
||||
if: matrix.os == 'macOS-15'
|
||||
- name: Check disk free space post-cleanup (macOS)
|
||||
run: df -h .
|
||||
if: matrix.os == 'macOS-14'
|
||||
if: matrix.os == 'macOS-15'
|
||||
- name: Build with Gradle
|
||||
run: ./gradlew ${{ matrix.task }} -PbuildServer -PskipJavaFormat ${{ matrix.build-options }}
|
||||
- name: Sign Libraries with Developer ID
|
||||
@@ -163,7 +170,7 @@ jobs:
|
||||
if: matrix.os == 'windows-2022'
|
||||
- name: Check disk free space (macOS)
|
||||
run: df -h .
|
||||
if: matrix.os == 'macOS-14'
|
||||
if: matrix.os == 'macOS-15'
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ matrix.artifact-name }}
|
||||
|
||||
6
.github/workflows/tools.yml
vendored
6
.github/workflows/tools.yml
vendored
@@ -7,7 +7,7 @@ concurrency:
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
YEAR: 2025
|
||||
YEAR: 2026
|
||||
|
||||
jobs:
|
||||
build-artifacts:
|
||||
@@ -31,11 +31,11 @@ jobs:
|
||||
fetch-depth: 0
|
||||
- uses: gradle/actions/wrapper-validation@v4
|
||||
- name: Build WPILib with Gradle
|
||||
uses: addnab/docker-run-action@v3
|
||||
uses: wpilibsuite/docker-run-action@v4
|
||||
with:
|
||||
image: wpilib/roborio-cross-ubuntu:2025-22.04
|
||||
options: -v ${{ github.workspace }}:/work -w /work -e GITHUB_REF -e CI -e DISPLAY
|
||||
run: df . && rm -f semicolon_delimited_script && ./gradlew :wpilibc:publish :wpilibj:publish :wpilibNewCommands:publish :hal:publish :cameraserver:publish :ntcore:publish :cscore:publish :wpimath:publish :wpinet:publish :wpiutil:publish :apriltag:publish :wpiunits:publish :simulation:halsim_gui:publish :simulation:halsim_ds_socket:publish :simulation:halsim_ws_server:publish :simulation:halsim_ws_client:publish :simulation:halsim_xrp:publish :fieldImages:publish :romiVendordep:publish :xrpVendordep:publish :epilogue-processor:publish :epilogue-runtime:publish :thirdparty:googletest:publish -x test -x Javadoc -x doxygen --build-cache && cp -r /root/releases/maven/development /work
|
||||
run: df . && ./gradlew :wpilibc:publish :wpilibj:publish :wpilibNewCommands:publish :hal:publish :cameraserver:publish :ntcore:publish :cscore:publish :wpimath:publish :wpinet:publish :wpiutil:publish :apriltag:publish :wpiunits:publish :simulation:halsim_gui:publish :simulation:halsim_ds_socket:publish :simulation:halsim_ws_server:publish :simulation:halsim_ws_client:publish :simulation:halsim_xrp:publish :fieldImages:publish :romiVendordep:publish :xrpVendordep:publish :epilogue-processor:publish :epilogue-runtime:publish :thirdparty:googletest:publish -x test -x Javadoc -x doxygen --build-cache && cp -r /root/releases/maven/development /work
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: MavenArtifacts
|
||||
|
||||
9
.github/workflows/upstream-utils.yml
vendored
9
.github/workflows/upstream-utils.yml
vendored
@@ -141,4 +141,11 @@ jobs:
|
||||
- name: Add untracked files to index so they count as changes
|
||||
run: git add -A
|
||||
- name: Check output
|
||||
run: git --no-pager diff --exit-code HEAD ':!*.bazel'
|
||||
run: |
|
||||
set +e
|
||||
git --no-pager diff --exit-code HEAD ':!*.bazel'
|
||||
git_exit_code=$?
|
||||
if test "$git_exit_code" -ne "0"; then
|
||||
echo "::error ::upstream_utils check failed. This is usually caused by a bad script or the copied files differing from what the script outputs. You can learn more about using upstream_utils to modify thirdparty libraries at https://github.com/wpilibsuite/allwpilib/blob/main/upstream_utils/README.md"
|
||||
exit $git_exit_code
|
||||
fi
|
||||
|
||||
@@ -289,6 +289,7 @@ endif()
|
||||
set(FILENAME_DEP_REPLACE "get_filename_component(SELF_DIR \"$\{CMAKE_CURRENT_LIST_FILE\}\" PATH)")
|
||||
set(SELF_DIR "$\{SELF_DIR\}")
|
||||
set(WPIUNITS_DEP_REPLACE_IMPL "find_dependency(wpiunits)")
|
||||
set(WPIANNOTATIONS_DEP_REPLACE_IMPL "find_dependency(wpiannotations)")
|
||||
set(WPIUTIL_DEP_REPLACE "find_dependency(wpiutil)")
|
||||
add_subdirectory(wpiutil)
|
||||
|
||||
@@ -308,6 +309,10 @@ if(WITH_WPIMATH)
|
||||
add_subdirectory(wpimath)
|
||||
endif()
|
||||
|
||||
if(WITH_JAVA)
|
||||
add_subdirectory(wpiannotations)
|
||||
endif()
|
||||
|
||||
if(WITH_WPIUNITS AND NOT WITH_WPIMATH)
|
||||
# In case of building wpiunits standalone
|
||||
set(WPIUNITS_DEP_REPLACE ${WPIUNITS_DEP_REPLACE_IMPL})
|
||||
@@ -346,7 +351,9 @@ endif()
|
||||
if(WITH_WPILIB)
|
||||
set(APRILTAG_DEP_REPLACE "find_dependency(apriltag)")
|
||||
set(WPILIBC_DEP_REPLACE "find_dependency(wpilibc)")
|
||||
set(WPILIBJ_DEP_REPLACE "find_dependency(wpilibj)")
|
||||
if(WITH_JAVA)
|
||||
set(WPILIBJ_DEP_REPLACE "find_dependency(wpilibj)")
|
||||
endif()
|
||||
set(WPILIBNEWCOMMANDS_DEP_REPLACE "find_dependency(wpilibNewCommands)")
|
||||
add_subdirectory(apriltag)
|
||||
add_subdirectory(wpilibj)
|
||||
|
||||
@@ -56,7 +56,7 @@ the consequences for any action they deem in violation of this Code of Conduct:
|
||||
**Community Impact**: Use of inappropriate language or other behavior deemed
|
||||
unprofessional or unwelcome in the community.
|
||||
|
||||
**Consequence**: A private, written warning from community leaders, providing
|
||||
**Consequence**: A warning from community leaders, providing
|
||||
clarity around the nature of the violation and an explanation of why the
|
||||
behavior was inappropriate. A public apology may be requested.
|
||||
|
||||
|
||||
@@ -51,39 +51,6 @@ Have an idea to make WPILib better? Here's some steps to go from idea to impleme
|
||||
WPILib uses modified Google style guides for both C++ and Java, which can be found in the [styleguide repository](https://github.com/wpilibsuite/styleguide). Autoformatters are available for many popular editors at https://github.com/google/styleguide. Running wpiformat is required for all contributions and is enforced by our continuous integration system.
|
||||
While the library should be fully formatted according to the styles, additional elements of the style guide were not followed when the library was initially created. All new code should follow the guidelines. If you are looking for some easy ramp-up tasks, finding areas that don't follow the style guide and fixing them is very welcome.
|
||||
|
||||
### Math documentation
|
||||
|
||||
When writing math expressions in documentation, use https://www.unicodeit.net/ to convert LaTeX to a Unicode equivalent that's easier to read. Not all expressions will translate (e.g., superscripts of superscripts) so focus on making it readable by someone who isn't familiar with LaTeX. If content on multiple lines needs to be aligned in Doxygen/Javadoc comments (e.g., integration/summation limits, matrices packed with square brackets and superscripts for them), put them in @verbatim/@endverbatim blocks in Doxygen or `<pre>` tags in Javadoc so they render with monospace font.
|
||||
|
||||
The LaTeX to Unicode conversions can also be done locally via the unicodeit Python package. To install it, execute:
|
||||
```bash
|
||||
pip install --user unicodeit
|
||||
```
|
||||
|
||||
Here's example usage:
|
||||
```bash
|
||||
$ python -m unicodeit.cli 'x_{k+1} = Ax_k + Bu_k'
|
||||
xₖ₊₁ = Axₖ + Buₖ
|
||||
```
|
||||
|
||||
On Linux, this process can be streamlined further by adding the following Bash function to your .bashrc (requires `wl-clipboard` on Wayland or `xclip` on X11):
|
||||
```bash
|
||||
# Converts LaTeX to Unicode, prints the result, and copies it to the clipboard
|
||||
uc() {
|
||||
if [ $WAYLAND_DISPLAY ]; then
|
||||
python -m unicodeit.cli $@ | tee >(wl-copy -n)
|
||||
else
|
||||
python -m unicodeit.cli $@ | tee >(xclip -sel)
|
||||
fi
|
||||
}
|
||||
```
|
||||
|
||||
Here's example usage:
|
||||
```bash
|
||||
$ uc 'x_{k+1} = Ax_k + Bu_k'
|
||||
xₖ₊₁ = Axₖ + Buₖ
|
||||
```
|
||||
|
||||
## Submitting Changes
|
||||
|
||||
### Pull Request Format
|
||||
|
||||
@@ -13,7 +13,7 @@ This article contains instructions on building projects using a development buil
|
||||
|
||||
Development builds are the per-commit build hosted every time a commit is pushed to the [allwpilib](https://github.com/wpilibsuite/allwpilib/) repository. These builds are then hosted on [artifactory](https://frcmaven.wpi.edu/artifactory/webapp/#/home).
|
||||
|
||||
To build a project using a development build, find the build.gradle file and open it. Then, add the following code below the plugin section and replace YEAR with the year of the development version. It is also necessary to use a 2025 GradleRIO version, ie `2025.1.1-beta-1`
|
||||
To build a project using a development build, find the build.gradle file and open it. Then, add the following code below the plugin section and replace YEAR with the year of the development version. It is also necessary to use a 2026 GradleRIO version, ie `2026.1.1`
|
||||
|
||||
```groovy
|
||||
wpi.maven.useLocal = false
|
||||
@@ -28,13 +28,13 @@ Java
|
||||
```groovy
|
||||
plugins {
|
||||
id "java"
|
||||
id "edu.wpi.first.GradleRIO" version "2025.1.1-beta-1"
|
||||
id "edu.wpi.first.GradleRIO" version "2026.1.1"
|
||||
}
|
||||
|
||||
wpi.maven.useLocal = false
|
||||
wpi.maven.useDevelopment = true
|
||||
wpi.versions.wpilibVersion = '2025.+'
|
||||
wpi.versions.wpimathVersion = '2025.+'
|
||||
wpi.versions.wpilibVersion = '2026.+'
|
||||
wpi.versions.wpimathVersion = '2026.+'
|
||||
```
|
||||
|
||||
C++
|
||||
@@ -42,13 +42,13 @@ C++
|
||||
plugins {
|
||||
id "cpp"
|
||||
id "google-test-test-suite"
|
||||
id "edu.wpi.first.GradleRIO" version "2025.1.1-beta-1"
|
||||
id "edu.wpi.first.GradleRIO" version "2026.1.1"
|
||||
}
|
||||
|
||||
wpi.maven.useLocal = false
|
||||
wpi.maven.useDevelopment = true
|
||||
wpi.versions.wpilibVersion = '2025.+'
|
||||
wpi.versions.wpimathVersion = '2025.+'
|
||||
wpi.versions.wpilibVersion = '2026.+'
|
||||
wpi.versions.wpimathVersion = '2026.+'
|
||||
```
|
||||
|
||||
### Development Build Documentation
|
||||
@@ -64,7 +64,7 @@ Java
|
||||
```groovy
|
||||
plugins {
|
||||
id "java"
|
||||
id "edu.wpi.first.GradleRIO" version "2025.1.1-beta-1"
|
||||
id "edu.wpi.first.GradleRIO" version "2026.1.1"
|
||||
}
|
||||
|
||||
wpi.maven.useLocal = false
|
||||
@@ -78,7 +78,7 @@ C++
|
||||
plugins {
|
||||
id "cpp"
|
||||
id "google-test-test-suite"
|
||||
id "edu.wpi.first.GradleRIO" version "2025.1.1-beta-1"
|
||||
id "edu.wpi.first.GradleRIO" version "2026.1.1"
|
||||
}
|
||||
|
||||
wpi.maven.useLocal = false
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Copyright (c) 2009-2025 FIRST and other WPILib contributors
|
||||
Copyright (c) 2009-2026 FIRST and other WPILib contributors
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
|
||||
@@ -50,7 +50,7 @@ Using Gradle makes building WPILib very straightforward. It only has a few depen
|
||||
- C++ compiler
|
||||
- On Linux, install GCC 11 or greater
|
||||
- On Windows, install [Visual Studio Community 2022](https://visualstudio.microsoft.com/vs/community/) and select the C++ programming language during installation (Gradle can't use the build tools for Visual Studio)
|
||||
- On macOS 13.3 or newer, install Xcode 14 or later (the command-line build tools are insufficient).
|
||||
- On macOS, install the Xcode command-line build tools via `xcode-select --install`. Xcode 14 or later is required.
|
||||
- ARM compiler toolchain
|
||||
- Run `./gradlew installRoboRioToolchain` after cloning this repository
|
||||
- If the WPILib installer was used, this toolchain is already installed
|
||||
@@ -59,8 +59,6 @@ Using Gradle makes building WPILib very straightforward. It only has a few depen
|
||||
|
||||
On macOS ARM, run `softwareupdate --install-rosetta`. This is necessary to be able to use the macOS x86 roboRIO toolchain on ARM.
|
||||
|
||||
On linux, run `sudo apt install gfortran`. This is necessary to be able to build WPIcal on linux platforms.
|
||||
|
||||
## Setup
|
||||
|
||||
Clone the WPILib repository and follow the instructions above for installing any required tooling. The build process uses versioning information from git. Downloading the source is not sufficient to run the build.
|
||||
|
||||
12
WORKSPACE
12
WORKSPACE
@@ -15,12 +15,12 @@ rules_jvm_external_deps()
|
||||
load("@rules_jvm_external//:defs.bzl", "maven_install")
|
||||
|
||||
maven_artifacts = [
|
||||
"org.ejml:ejml-simple:0.43.1",
|
||||
"com.fasterxml.jackson.core:jackson-annotations:2.15.2",
|
||||
"com.fasterxml.jackson.core:jackson-core:2.15.2",
|
||||
"com.fasterxml.jackson.core:jackson-databind:2.15.2",
|
||||
"us.hebi.quickbuf:quickbuf-runtime:1.3.3",
|
||||
"com.google.code.gson:gson:2.10.1",
|
||||
"org.ejml:ejml-simple:0.44.0",
|
||||
"com.fasterxml.jackson.core:jackson-annotations:2.19.2",
|
||||
"com.fasterxml.jackson.core:jackson-core:2.19.2",
|
||||
"com.fasterxml.jackson.core:jackson-databind:2.19.2",
|
||||
"us.hebi.quickbuf:quickbuf-runtime:1.4",
|
||||
"com.google.code.gson:gson:2.13.1",
|
||||
]
|
||||
|
||||
maven_install(
|
||||
|
||||
@@ -152,9 +152,10 @@ public class AprilTagFieldLayout {
|
||||
var pose =
|
||||
switch (origin) {
|
||||
case kBlueAllianceWallRightSide -> Pose3d.kZero;
|
||||
case kRedAllianceWallRightSide -> new Pose3d(
|
||||
new Translation3d(m_fieldDimensions.fieldLength, m_fieldDimensions.fieldWidth, 0),
|
||||
new Rotation3d(0, 0, Math.PI));
|
||||
case kRedAllianceWallRightSide ->
|
||||
new Pose3d(
|
||||
new Translation3d(m_fieldDimensions.fieldLength, m_fieldDimensions.fieldWidth, 0),
|
||||
new Rotation3d(0, 0, Math.PI));
|
||||
};
|
||||
setOrigin(pose);
|
||||
}
|
||||
|
||||
@@ -17,13 +17,17 @@ public enum AprilTagFields {
|
||||
/** 2025 Reefscape Welded (see TU 12). */
|
||||
k2025ReefscapeWelded("2025-reefscape-welded.json"),
|
||||
/** 2025 Reefscape AndyMark (see TU 12). */
|
||||
k2025ReefscapeAndyMark("2025-reefscape-andymark.json");
|
||||
k2025ReefscapeAndyMark("2025-reefscape-andymark.json"),
|
||||
/** 2026 Rebuilt Welded. */
|
||||
k2026RebuiltWelded("2026-rebuilt-welded.json"),
|
||||
/** 2026 Rebuilt AndyMark. */
|
||||
k2026RebuiltAndymark("2026-rebuilt-andymark.json");
|
||||
|
||||
/** Base resource directory. */
|
||||
public static final String kBaseResourceDir = "/edu/wpi/first/apriltag/";
|
||||
|
||||
/** Alias to the current game. */
|
||||
public static final AprilTagFields kDefaultField = k2025ReefscapeWelded;
|
||||
public static final AprilTagFields kDefaultField = k2026RebuiltWelded;
|
||||
|
||||
/** Resource filename. */
|
||||
public final String m_resourceFile;
|
||||
|
||||
@@ -135,6 +135,8 @@ std::string_view GetResource_2023_chargedup_json();
|
||||
std::string_view GetResource_2024_crescendo_json();
|
||||
std::string_view GetResource_2025_reefscape_welded_json();
|
||||
std::string_view GetResource_2025_reefscape_andymark_json();
|
||||
std::string_view GetResource_2026_rebuilt_welded_json();
|
||||
std::string_view GetResource_2026_rebuilt_andymark_json();
|
||||
|
||||
} // namespace frc
|
||||
|
||||
@@ -156,6 +158,12 @@ AprilTagFieldLayout AprilTagFieldLayout::LoadField(AprilTagField field) {
|
||||
case AprilTagField::k2025ReefscapeAndyMark:
|
||||
fieldString = GetResource_2025_reefscape_andymark_json();
|
||||
break;
|
||||
case AprilTagField::k2026RebuiltWelded:
|
||||
fieldString = GetResource_2026_rebuilt_welded_json();
|
||||
break;
|
||||
case AprilTagField::k2026RebuiltAndyMark:
|
||||
fieldString = GetResource_2026_rebuilt_andymark_json();
|
||||
break;
|
||||
case AprilTagField::kNumFields:
|
||||
throw std::invalid_argument("Invalid Field");
|
||||
}
|
||||
|
||||
@@ -24,8 +24,12 @@ enum class AprilTagField {
|
||||
k2025ReefscapeAndyMark,
|
||||
/// 2025 Reefscape Welded (see TU12).
|
||||
k2025ReefscapeWelded,
|
||||
/// 2026 Rebuilt Andymark.
|
||||
k2026RebuiltAndyMark,
|
||||
/// 2026 Rebuilt Welded.
|
||||
k2026RebuiltWelded,
|
||||
/// Alias to the current game.
|
||||
kDefaultField = k2025ReefscapeWelded,
|
||||
kDefaultField = k2026RebuiltWelded,
|
||||
|
||||
// This is a placeholder for denoting the last supported field. This should
|
||||
// always be the last entry in the enum and should not be used by users
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
ID,X,Y,Z,Z-Rotation,X-Rotation
|
||||
1,467.085,291.791,35,180,0
|
||||
2,468.559,182.077,44.25,90,0
|
||||
3,444.797,172.321,44.25,180,0
|
||||
4,444.797,158.321,44.25,180,0
|
||||
5,468.559,134.565,44.25,270,0
|
||||
6,467.085,24.851,35,180,0
|
||||
7,470.034,24.851,35,0,0
|
||||
8,482.559,134.565,44.25,270,0
|
||||
9,492.329,144.321,44.25,0,0
|
||||
10,492.329,158.321,44.25,0,0
|
||||
11,482.559,182.077,44.25,90,0
|
||||
12,470.034,291.791,35,0,0
|
||||
13,649.58,291.02,21.75,180,0
|
||||
14,649.58,274.02,21.75,180,0
|
||||
15,649.566,169.783,21.75,180,0
|
||||
16,649.566,152.783,21.75,180,0
|
||||
17,183.034,24.851,35,0,0
|
||||
18,181.559,134.565,44.25,270,0
|
||||
19,205.321,144.321,44.25,0,0
|
||||
20,205.321,158.321,44.25,0,0
|
||||
21,181.559,182.077,44.25,90,0
|
||||
22,183.034,291.791,35,0,0
|
||||
23,180.085,291.791,35,180,0
|
||||
24,167.559,182.077,44.25,90,0
|
||||
25,157.79,172.321,44.25,180,0
|
||||
26,157.79,158.321,44.25,180,0
|
||||
27,167.559,134.565,44.25,270,0
|
||||
28,180.085,24.851,35,180,0
|
||||
29,0.539,25.621,21.75,0,0
|
||||
30,0.539,42.621,21.75,0,0
|
||||
31,0.553,146.858,21.75,0,0
|
||||
32,0.553,163.858,21.75,0,0
|
||||
|
@@ -0,0 +1,584 @@
|
||||
{
|
||||
"tags": [
|
||||
{
|
||||
"ID": 1,
|
||||
"pose": {
|
||||
"translation": {
|
||||
"x": 11.863959,
|
||||
"y": 7.411491399999999,
|
||||
"z": 0.889
|
||||
},
|
||||
"rotation": {
|
||||
"quaternion": {
|
||||
"W": 6.123233995736766e-17,
|
||||
"X": 0.0,
|
||||
"Y": 0.0,
|
||||
"Z": 1.0
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": 2,
|
||||
"pose": {
|
||||
"translation": {
|
||||
"x": 11.9013986,
|
||||
"y": 4.6247558,
|
||||
"z": 1.12395
|
||||
},
|
||||
"rotation": {
|
||||
"quaternion": {
|
||||
"W": 0.7071067811865476,
|
||||
"X": 0.0,
|
||||
"Y": 0.0,
|
||||
"Z": 0.7071067811865476
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": 3,
|
||||
"pose": {
|
||||
"translation": {
|
||||
"x": 11.2978438,
|
||||
"y": 4.3769534,
|
||||
"z": 1.12395
|
||||
},
|
||||
"rotation": {
|
||||
"quaternion": {
|
||||
"W": 6.123233995736766e-17,
|
||||
"X": 0.0,
|
||||
"Y": 0.0,
|
||||
"Z": 1.0
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": 4,
|
||||
"pose": {
|
||||
"translation": {
|
||||
"x": 11.2978438,
|
||||
"y": 4.0213534,
|
||||
"z": 1.12395
|
||||
},
|
||||
"rotation": {
|
||||
"quaternion": {
|
||||
"W": 6.123233995736766e-17,
|
||||
"X": 0.0,
|
||||
"Y": 0.0,
|
||||
"Z": 1.0
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": 5,
|
||||
"pose": {
|
||||
"translation": {
|
||||
"x": 11.9013986,
|
||||
"y": 3.417951,
|
||||
"z": 1.12395
|
||||
},
|
||||
"rotation": {
|
||||
"quaternion": {
|
||||
"W": -0.7071067811865475,
|
||||
"X": -0.0,
|
||||
"Y": 0.0,
|
||||
"Z": 0.7071067811865476
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": 6,
|
||||
"pose": {
|
||||
"translation": {
|
||||
"x": 11.863959,
|
||||
"y": 0.6312154,
|
||||
"z": 0.889
|
||||
},
|
||||
"rotation": {
|
||||
"quaternion": {
|
||||
"W": 6.123233995736766e-17,
|
||||
"X": 0.0,
|
||||
"Y": 0.0,
|
||||
"Z": 1.0
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": 7,
|
||||
"pose": {
|
||||
"translation": {
|
||||
"x": 11.9388636,
|
||||
"y": 0.6312154,
|
||||
"z": 0.889
|
||||
},
|
||||
"rotation": {
|
||||
"quaternion": {
|
||||
"W": 1.0,
|
||||
"X": 0.0,
|
||||
"Y": 0.0,
|
||||
"Z": 0.0
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": 8,
|
||||
"pose": {
|
||||
"translation": {
|
||||
"x": 12.2569986,
|
||||
"y": 3.417951,
|
||||
"z": 1.12395
|
||||
},
|
||||
"rotation": {
|
||||
"quaternion": {
|
||||
"W": -0.7071067811865475,
|
||||
"X": -0.0,
|
||||
"Y": 0.0,
|
||||
"Z": 0.7071067811865476
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": 9,
|
||||
"pose": {
|
||||
"translation": {
|
||||
"x": 12.5051566,
|
||||
"y": 3.6657534,
|
||||
"z": 1.12395
|
||||
},
|
||||
"rotation": {
|
||||
"quaternion": {
|
||||
"W": 1.0,
|
||||
"X": 0.0,
|
||||
"Y": 0.0,
|
||||
"Z": 0.0
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": 10,
|
||||
"pose": {
|
||||
"translation": {
|
||||
"x": 12.5051566,
|
||||
"y": 4.0213534,
|
||||
"z": 1.12395
|
||||
},
|
||||
"rotation": {
|
||||
"quaternion": {
|
||||
"W": 1.0,
|
||||
"X": 0.0,
|
||||
"Y": 0.0,
|
||||
"Z": 0.0
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": 11,
|
||||
"pose": {
|
||||
"translation": {
|
||||
"x": 12.2569986,
|
||||
"y": 4.6247558,
|
||||
"z": 1.12395
|
||||
},
|
||||
"rotation": {
|
||||
"quaternion": {
|
||||
"W": 0.7071067811865476,
|
||||
"X": 0.0,
|
||||
"Y": 0.0,
|
||||
"Z": 0.7071067811865476
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": 12,
|
||||
"pose": {
|
||||
"translation": {
|
||||
"x": 11.9388636,
|
||||
"y": 7.411491399999999,
|
||||
"z": 0.889
|
||||
},
|
||||
"rotation": {
|
||||
"quaternion": {
|
||||
"W": 1.0,
|
||||
"X": 0.0,
|
||||
"Y": 0.0,
|
||||
"Z": 0.0
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": 13,
|
||||
"pose": {
|
||||
"translation": {
|
||||
"x": 16.499332,
|
||||
"y": 7.391907999999999,
|
||||
"z": 0.55245
|
||||
},
|
||||
"rotation": {
|
||||
"quaternion": {
|
||||
"W": 6.123233995736766e-17,
|
||||
"X": 0.0,
|
||||
"Y": 0.0,
|
||||
"Z": 1.0
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": 14,
|
||||
"pose": {
|
||||
"translation": {
|
||||
"x": 16.499332,
|
||||
"y": 6.960107999999999,
|
||||
"z": 0.55245
|
||||
},
|
||||
"rotation": {
|
||||
"quaternion": {
|
||||
"W": 6.123233995736766e-17,
|
||||
"X": 0.0,
|
||||
"Y": 0.0,
|
||||
"Z": 1.0
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": 15,
|
||||
"pose": {
|
||||
"translation": {
|
||||
"x": 16.4989764,
|
||||
"y": 4.3124882,
|
||||
"z": 0.55245
|
||||
},
|
||||
"rotation": {
|
||||
"quaternion": {
|
||||
"W": 6.123233995736766e-17,
|
||||
"X": 0.0,
|
||||
"Y": 0.0,
|
||||
"Z": 1.0
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": 16,
|
||||
"pose": {
|
||||
"translation": {
|
||||
"x": 16.4989764,
|
||||
"y": 3.8806881999999994,
|
||||
"z": 0.55245
|
||||
},
|
||||
"rotation": {
|
||||
"quaternion": {
|
||||
"W": 6.123233995736766e-17,
|
||||
"X": 0.0,
|
||||
"Y": 0.0,
|
||||
"Z": 1.0
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": 17,
|
||||
"pose": {
|
||||
"translation": {
|
||||
"x": 4.6490636,
|
||||
"y": 0.6312154,
|
||||
"z": 0.889
|
||||
},
|
||||
"rotation": {
|
||||
"quaternion": {
|
||||
"W": 1.0,
|
||||
"X": 0.0,
|
||||
"Y": 0.0,
|
||||
"Z": 0.0
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": 18,
|
||||
"pose": {
|
||||
"translation": {
|
||||
"x": 4.6115986,
|
||||
"y": 3.417951,
|
||||
"z": 1.12395
|
||||
},
|
||||
"rotation": {
|
||||
"quaternion": {
|
||||
"W": -0.7071067811865475,
|
||||
"X": -0.0,
|
||||
"Y": 0.0,
|
||||
"Z": 0.7071067811865476
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": 19,
|
||||
"pose": {
|
||||
"translation": {
|
||||
"x": 5.2151534,
|
||||
"y": 3.6657534,
|
||||
"z": 1.12395
|
||||
},
|
||||
"rotation": {
|
||||
"quaternion": {
|
||||
"W": 1.0,
|
||||
"X": 0.0,
|
||||
"Y": 0.0,
|
||||
"Z": 0.0
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": 20,
|
||||
"pose": {
|
||||
"translation": {
|
||||
"x": 5.2151534,
|
||||
"y": 4.0213534,
|
||||
"z": 1.12395
|
||||
},
|
||||
"rotation": {
|
||||
"quaternion": {
|
||||
"W": 1.0,
|
||||
"X": 0.0,
|
||||
"Y": 0.0,
|
||||
"Z": 0.0
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": 21,
|
||||
"pose": {
|
||||
"translation": {
|
||||
"x": 4.6115986,
|
||||
"y": 4.6247558,
|
||||
"z": 1.12395
|
||||
},
|
||||
"rotation": {
|
||||
"quaternion": {
|
||||
"W": 0.7071067811865476,
|
||||
"X": 0.0,
|
||||
"Y": 0.0,
|
||||
"Z": 0.7071067811865476
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": 22,
|
||||
"pose": {
|
||||
"translation": {
|
||||
"x": 4.6490636,
|
||||
"y": 7.411491399999999,
|
||||
"z": 0.889
|
||||
},
|
||||
"rotation": {
|
||||
"quaternion": {
|
||||
"W": 1.0,
|
||||
"X": 0.0,
|
||||
"Y": 0.0,
|
||||
"Z": 0.0
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": 23,
|
||||
"pose": {
|
||||
"translation": {
|
||||
"x": 4.574159,
|
||||
"y": 7.411491399999999,
|
||||
"z": 0.889
|
||||
},
|
||||
"rotation": {
|
||||
"quaternion": {
|
||||
"W": 6.123233995736766e-17,
|
||||
"X": 0.0,
|
||||
"Y": 0.0,
|
||||
"Z": 1.0
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": 24,
|
||||
"pose": {
|
||||
"translation": {
|
||||
"x": 4.2559986,
|
||||
"y": 4.6247558,
|
||||
"z": 1.12395
|
||||
},
|
||||
"rotation": {
|
||||
"quaternion": {
|
||||
"W": 0.7071067811865476,
|
||||
"X": 0.0,
|
||||
"Y": 0.0,
|
||||
"Z": 0.7071067811865476
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": 25,
|
||||
"pose": {
|
||||
"translation": {
|
||||
"x": 4.007866,
|
||||
"y": 4.3769534,
|
||||
"z": 1.12395
|
||||
},
|
||||
"rotation": {
|
||||
"quaternion": {
|
||||
"W": 6.123233995736766e-17,
|
||||
"X": 0.0,
|
||||
"Y": 0.0,
|
||||
"Z": 1.0
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": 26,
|
||||
"pose": {
|
||||
"translation": {
|
||||
"x": 4.007866,
|
||||
"y": 4.0213534,
|
||||
"z": 1.12395
|
||||
},
|
||||
"rotation": {
|
||||
"quaternion": {
|
||||
"W": 6.123233995736766e-17,
|
||||
"X": 0.0,
|
||||
"Y": 0.0,
|
||||
"Z": 1.0
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": 27,
|
||||
"pose": {
|
||||
"translation": {
|
||||
"x": 4.2559986,
|
||||
"y": 3.417951,
|
||||
"z": 1.12395
|
||||
},
|
||||
"rotation": {
|
||||
"quaternion": {
|
||||
"W": -0.7071067811865475,
|
||||
"X": -0.0,
|
||||
"Y": 0.0,
|
||||
"Z": 0.7071067811865476
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": 28,
|
||||
"pose": {
|
||||
"translation": {
|
||||
"x": 4.574159,
|
||||
"y": 0.6312154,
|
||||
"z": 0.889
|
||||
},
|
||||
"rotation": {
|
||||
"quaternion": {
|
||||
"W": 6.123233995736766e-17,
|
||||
"X": 0.0,
|
||||
"Y": 0.0,
|
||||
"Z": 1.0
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": 29,
|
||||
"pose": {
|
||||
"translation": {
|
||||
"x": 0.0136906,
|
||||
"y": 0.6507734,
|
||||
"z": 0.55245
|
||||
},
|
||||
"rotation": {
|
||||
"quaternion": {
|
||||
"W": 1.0,
|
||||
"X": 0.0,
|
||||
"Y": 0.0,
|
||||
"Z": 0.0
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": 30,
|
||||
"pose": {
|
||||
"translation": {
|
||||
"x": 0.0136906,
|
||||
"y": 1.0825734,
|
||||
"z": 0.55245
|
||||
},
|
||||
"rotation": {
|
||||
"quaternion": {
|
||||
"W": 1.0,
|
||||
"X": 0.0,
|
||||
"Y": 0.0,
|
||||
"Z": 0.0
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": 31,
|
||||
"pose": {
|
||||
"translation": {
|
||||
"x": 0.0140462,
|
||||
"y": 3.7301932,
|
||||
"z": 0.55245
|
||||
},
|
||||
"rotation": {
|
||||
"quaternion": {
|
||||
"W": 1.0,
|
||||
"X": 0.0,
|
||||
"Y": 0.0,
|
||||
"Z": 0.0
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": 32,
|
||||
"pose": {
|
||||
"translation": {
|
||||
"x": 0.0140462,
|
||||
"y": 4.1619931999999995,
|
||||
"z": 0.55245
|
||||
},
|
||||
"rotation": {
|
||||
"quaternion": {
|
||||
"W": 1.0,
|
||||
"X": 0.0,
|
||||
"Y": 0.0,
|
||||
"Z": 0.0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"field": {
|
||||
"length": 16.518,
|
||||
"width": 8.043
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
ID,X,Y,Z,Z-Rotation,X-Rotation
|
||||
1,467.637,292.314,35,180,0
|
||||
2,469.111,182.6,44.25,90,0
|
||||
3,445.349,172.844,44.25,180,0
|
||||
4,445.349,158.844,44.25,180,0
|
||||
5,469.111,135.088,44.25,270,0
|
||||
6,467.637,25.374,35,180,0
|
||||
7,470.586,25.374,35,0,0
|
||||
8,483.111,135.088,44.25,270,0
|
||||
9,492.881,144.844,44.25,0,0
|
||||
10,492.881,158.844,44.25,0,0
|
||||
11,483.111,182.6,44.25,90,0
|
||||
12,470.586,292.314,35,0,0
|
||||
13,650.918,291.469,21.75,180,0
|
||||
14,650.918,274.469,21.75,180,0
|
||||
15,650.904,170.219,21.75,180,0
|
||||
16,650.904,153.219,21.75,180,0
|
||||
17,183.586,25.374,35,0,0
|
||||
18,182.111,135.088,44.25,270,0
|
||||
19,205.873,144.844,44.25,0,0
|
||||
20,205.873,158.844,44.25,0,0
|
||||
21,182.111,182.6,44.25,90,0
|
||||
22,183.586,292.314,35,0,0
|
||||
23,180.637,292.314,35,180,0
|
||||
24,168.111,182.6,44.25,90,0
|
||||
25,158.341,172.844,44.25,180,0
|
||||
26,158.341,158.844,44.25,180,0
|
||||
27,168.111,135.088,44.25,270,0
|
||||
28,180.637,25.374,35,180,0
|
||||
29,0.305,26.219,21.75,0,0
|
||||
30,0.305,43.219,21.75,0,0
|
||||
31,0.318,147.469,21.75,0,0
|
||||
32,0.318,164.469,21.75,0,0
|
||||
|
@@ -0,0 +1,584 @@
|
||||
{
|
||||
"tags": [
|
||||
{
|
||||
"ID": 1,
|
||||
"pose": {
|
||||
"translation": {
|
||||
"x": 11.8779798,
|
||||
"y": 7.4247756,
|
||||
"z": 0.889
|
||||
},
|
||||
"rotation": {
|
||||
"quaternion": {
|
||||
"W": 6.123233995736766e-17,
|
||||
"X": 0.0,
|
||||
"Y": 0.0,
|
||||
"Z": 1.0
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": 2,
|
||||
"pose": {
|
||||
"translation": {
|
||||
"x": 11.9154194,
|
||||
"y": 4.638039999999999,
|
||||
"z": 1.12395
|
||||
},
|
||||
"rotation": {
|
||||
"quaternion": {
|
||||
"W": 0.7071067811865476,
|
||||
"X": 0.0,
|
||||
"Y": 0.0,
|
||||
"Z": 0.7071067811865476
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": 3,
|
||||
"pose": {
|
||||
"translation": {
|
||||
"x": 11.3118646,
|
||||
"y": 4.3902376,
|
||||
"z": 1.12395
|
||||
},
|
||||
"rotation": {
|
||||
"quaternion": {
|
||||
"W": 6.123233995736766e-17,
|
||||
"X": 0.0,
|
||||
"Y": 0.0,
|
||||
"Z": 1.0
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": 4,
|
||||
"pose": {
|
||||
"translation": {
|
||||
"x": 11.3118646,
|
||||
"y": 4.0346376,
|
||||
"z": 1.12395
|
||||
},
|
||||
"rotation": {
|
||||
"quaternion": {
|
||||
"W": 6.123233995736766e-17,
|
||||
"X": 0.0,
|
||||
"Y": 0.0,
|
||||
"Z": 1.0
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": 5,
|
||||
"pose": {
|
||||
"translation": {
|
||||
"x": 11.9154194,
|
||||
"y": 3.4312351999999997,
|
||||
"z": 1.12395
|
||||
},
|
||||
"rotation": {
|
||||
"quaternion": {
|
||||
"W": -0.7071067811865475,
|
||||
"X": -0.0,
|
||||
"Y": 0.0,
|
||||
"Z": 0.7071067811865476
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": 6,
|
||||
"pose": {
|
||||
"translation": {
|
||||
"x": 11.8779798,
|
||||
"y": 0.6444996,
|
||||
"z": 0.889
|
||||
},
|
||||
"rotation": {
|
||||
"quaternion": {
|
||||
"W": 6.123233995736766e-17,
|
||||
"X": 0.0,
|
||||
"Y": 0.0,
|
||||
"Z": 1.0
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": 7,
|
||||
"pose": {
|
||||
"translation": {
|
||||
"x": 11.9528844,
|
||||
"y": 0.6444996,
|
||||
"z": 0.889
|
||||
},
|
||||
"rotation": {
|
||||
"quaternion": {
|
||||
"W": 1.0,
|
||||
"X": 0.0,
|
||||
"Y": 0.0,
|
||||
"Z": 0.0
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": 8,
|
||||
"pose": {
|
||||
"translation": {
|
||||
"x": 12.2710194,
|
||||
"y": 3.4312351999999997,
|
||||
"z": 1.12395
|
||||
},
|
||||
"rotation": {
|
||||
"quaternion": {
|
||||
"W": -0.7071067811865475,
|
||||
"X": -0.0,
|
||||
"Y": 0.0,
|
||||
"Z": 0.7071067811865476
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": 9,
|
||||
"pose": {
|
||||
"translation": {
|
||||
"x": 12.519177399999998,
|
||||
"y": 3.6790375999999996,
|
||||
"z": 1.12395
|
||||
},
|
||||
"rotation": {
|
||||
"quaternion": {
|
||||
"W": 1.0,
|
||||
"X": 0.0,
|
||||
"Y": 0.0,
|
||||
"Z": 0.0
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": 10,
|
||||
"pose": {
|
||||
"translation": {
|
||||
"x": 12.519177399999998,
|
||||
"y": 4.0346376,
|
||||
"z": 1.12395
|
||||
},
|
||||
"rotation": {
|
||||
"quaternion": {
|
||||
"W": 1.0,
|
||||
"X": 0.0,
|
||||
"Y": 0.0,
|
||||
"Z": 0.0
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": 11,
|
||||
"pose": {
|
||||
"translation": {
|
||||
"x": 12.2710194,
|
||||
"y": 4.638039999999999,
|
||||
"z": 1.12395
|
||||
},
|
||||
"rotation": {
|
||||
"quaternion": {
|
||||
"W": 0.7071067811865476,
|
||||
"X": 0.0,
|
||||
"Y": 0.0,
|
||||
"Z": 0.7071067811865476
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": 12,
|
||||
"pose": {
|
||||
"translation": {
|
||||
"x": 11.9528844,
|
||||
"y": 7.4247756,
|
||||
"z": 0.889
|
||||
},
|
||||
"rotation": {
|
||||
"quaternion": {
|
||||
"W": 1.0,
|
||||
"X": 0.0,
|
||||
"Y": 0.0,
|
||||
"Z": 0.0
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": 13,
|
||||
"pose": {
|
||||
"translation": {
|
||||
"x": 16.5333172,
|
||||
"y": 7.4033126,
|
||||
"z": 0.55245
|
||||
},
|
||||
"rotation": {
|
||||
"quaternion": {
|
||||
"W": 6.123233995736766e-17,
|
||||
"X": 0.0,
|
||||
"Y": 0.0,
|
||||
"Z": 1.0
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": 14,
|
||||
"pose": {
|
||||
"translation": {
|
||||
"x": 16.5333172,
|
||||
"y": 6.9715126,
|
||||
"z": 0.55245
|
||||
},
|
||||
"rotation": {
|
||||
"quaternion": {
|
||||
"W": 6.123233995736766e-17,
|
||||
"X": 0.0,
|
||||
"Y": 0.0,
|
||||
"Z": 1.0
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": 15,
|
||||
"pose": {
|
||||
"translation": {
|
||||
"x": 16.5329616,
|
||||
"y": 4.3235626,
|
||||
"z": 0.55245
|
||||
},
|
||||
"rotation": {
|
||||
"quaternion": {
|
||||
"W": 6.123233995736766e-17,
|
||||
"X": 0.0,
|
||||
"Y": 0.0,
|
||||
"Z": 1.0
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": 16,
|
||||
"pose": {
|
||||
"translation": {
|
||||
"x": 16.5329616,
|
||||
"y": 3.8917626,
|
||||
"z": 0.55245
|
||||
},
|
||||
"rotation": {
|
||||
"quaternion": {
|
||||
"W": 6.123233995736766e-17,
|
||||
"X": 0.0,
|
||||
"Y": 0.0,
|
||||
"Z": 1.0
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": 17,
|
||||
"pose": {
|
||||
"translation": {
|
||||
"x": 4.6630844,
|
||||
"y": 0.6444996,
|
||||
"z": 0.889
|
||||
},
|
||||
"rotation": {
|
||||
"quaternion": {
|
||||
"W": 1.0,
|
||||
"X": 0.0,
|
||||
"Y": 0.0,
|
||||
"Z": 0.0
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": 18,
|
||||
"pose": {
|
||||
"translation": {
|
||||
"x": 4.6256194,
|
||||
"y": 3.4312351999999997,
|
||||
"z": 1.12395
|
||||
},
|
||||
"rotation": {
|
||||
"quaternion": {
|
||||
"W": -0.7071067811865475,
|
||||
"X": -0.0,
|
||||
"Y": 0.0,
|
||||
"Z": 0.7071067811865476
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": 19,
|
||||
"pose": {
|
||||
"translation": {
|
||||
"x": 5.229174199999999,
|
||||
"y": 3.6790375999999996,
|
||||
"z": 1.12395
|
||||
},
|
||||
"rotation": {
|
||||
"quaternion": {
|
||||
"W": 1.0,
|
||||
"X": 0.0,
|
||||
"Y": 0.0,
|
||||
"Z": 0.0
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": 20,
|
||||
"pose": {
|
||||
"translation": {
|
||||
"x": 5.229174199999999,
|
||||
"y": 4.0346376,
|
||||
"z": 1.12395
|
||||
},
|
||||
"rotation": {
|
||||
"quaternion": {
|
||||
"W": 1.0,
|
||||
"X": 0.0,
|
||||
"Y": 0.0,
|
||||
"Z": 0.0
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": 21,
|
||||
"pose": {
|
||||
"translation": {
|
||||
"x": 4.6256194,
|
||||
"y": 4.638039999999999,
|
||||
"z": 1.12395
|
||||
},
|
||||
"rotation": {
|
||||
"quaternion": {
|
||||
"W": 0.7071067811865476,
|
||||
"X": 0.0,
|
||||
"Y": 0.0,
|
||||
"Z": 0.7071067811865476
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": 22,
|
||||
"pose": {
|
||||
"translation": {
|
||||
"x": 4.6630844,
|
||||
"y": 7.4247756,
|
||||
"z": 0.889
|
||||
},
|
||||
"rotation": {
|
||||
"quaternion": {
|
||||
"W": 1.0,
|
||||
"X": 0.0,
|
||||
"Y": 0.0,
|
||||
"Z": 0.0
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": 23,
|
||||
"pose": {
|
||||
"translation": {
|
||||
"x": 4.5881798,
|
||||
"y": 7.4247756,
|
||||
"z": 0.889
|
||||
},
|
||||
"rotation": {
|
||||
"quaternion": {
|
||||
"W": 6.123233995736766e-17,
|
||||
"X": 0.0,
|
||||
"Y": 0.0,
|
||||
"Z": 1.0
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": 24,
|
||||
"pose": {
|
||||
"translation": {
|
||||
"x": 4.2700194,
|
||||
"y": 4.638039999999999,
|
||||
"z": 1.12395
|
||||
},
|
||||
"rotation": {
|
||||
"quaternion": {
|
||||
"W": 0.7071067811865476,
|
||||
"X": 0.0,
|
||||
"Y": 0.0,
|
||||
"Z": 0.7071067811865476
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": 25,
|
||||
"pose": {
|
||||
"translation": {
|
||||
"x": 4.0218614,
|
||||
"y": 4.3902376,
|
||||
"z": 1.12395
|
||||
},
|
||||
"rotation": {
|
||||
"quaternion": {
|
||||
"W": 6.123233995736766e-17,
|
||||
"X": 0.0,
|
||||
"Y": 0.0,
|
||||
"Z": 1.0
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": 26,
|
||||
"pose": {
|
||||
"translation": {
|
||||
"x": 4.0218614,
|
||||
"y": 4.0346376,
|
||||
"z": 1.12395
|
||||
},
|
||||
"rotation": {
|
||||
"quaternion": {
|
||||
"W": 6.123233995736766e-17,
|
||||
"X": 0.0,
|
||||
"Y": 0.0,
|
||||
"Z": 1.0
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": 27,
|
||||
"pose": {
|
||||
"translation": {
|
||||
"x": 4.2700194,
|
||||
"y": 3.4312351999999997,
|
||||
"z": 1.12395
|
||||
},
|
||||
"rotation": {
|
||||
"quaternion": {
|
||||
"W": -0.7071067811865475,
|
||||
"X": -0.0,
|
||||
"Y": 0.0,
|
||||
"Z": 0.7071067811865476
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": 28,
|
||||
"pose": {
|
||||
"translation": {
|
||||
"x": 4.5881798,
|
||||
"y": 0.6444996,
|
||||
"z": 0.889
|
||||
},
|
||||
"rotation": {
|
||||
"quaternion": {
|
||||
"W": 6.123233995736766e-17,
|
||||
"X": 0.0,
|
||||
"Y": 0.0,
|
||||
"Z": 1.0
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": 29,
|
||||
"pose": {
|
||||
"translation": {
|
||||
"x": 0.0077469999999999995,
|
||||
"y": 0.6659626,
|
||||
"z": 0.55245
|
||||
},
|
||||
"rotation": {
|
||||
"quaternion": {
|
||||
"W": 1.0,
|
||||
"X": 0.0,
|
||||
"Y": 0.0,
|
||||
"Z": 0.0
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": 30,
|
||||
"pose": {
|
||||
"translation": {
|
||||
"x": 0.0077469999999999995,
|
||||
"y": 1.0977626,
|
||||
"z": 0.55245
|
||||
},
|
||||
"rotation": {
|
||||
"quaternion": {
|
||||
"W": 1.0,
|
||||
"X": 0.0,
|
||||
"Y": 0.0,
|
||||
"Z": 0.0
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": 31,
|
||||
"pose": {
|
||||
"translation": {
|
||||
"x": 0.0080772,
|
||||
"y": 3.7457125999999996,
|
||||
"z": 0.55245
|
||||
},
|
||||
"rotation": {
|
||||
"quaternion": {
|
||||
"W": 1.0,
|
||||
"X": 0.0,
|
||||
"Y": 0.0,
|
||||
"Z": 0.0
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": 32,
|
||||
"pose": {
|
||||
"translation": {
|
||||
"x": 0.0080772,
|
||||
"y": 4.1775126,
|
||||
"z": 0.55245
|
||||
},
|
||||
"rotation": {
|
||||
"quaternion": {
|
||||
"W": 1.0,
|
||||
"X": 0.0,
|
||||
"Y": 0.0,
|
||||
"Z": 0.0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"field": {
|
||||
"length": 16.541,
|
||||
"width": 8.069
|
||||
}
|
||||
}
|
||||
@@ -24,7 +24,6 @@ import org.opencv.core.Mat;
|
||||
import org.opencv.imgcodecs.Imgcodecs;
|
||||
import org.opencv.imgproc.Imgproc;
|
||||
|
||||
@SuppressWarnings("PMD.MutableStaticState")
|
||||
class AprilTagDetectorTest {
|
||||
@SuppressWarnings("MemberName")
|
||||
AprilTagDetector detector;
|
||||
|
||||
@@ -181,7 +181,11 @@ tasks.register('deployStatic') {
|
||||
model {
|
||||
components {
|
||||
benchmarkCpp(NativeExecutableSpec) {
|
||||
targetBuildTypes 'debug'
|
||||
if (project.hasProperty('ciDebugOnly')) {
|
||||
targetBuildTypes 'debug'
|
||||
} else {
|
||||
targetBuildTypes 'release'
|
||||
}
|
||||
sources {
|
||||
cpp {
|
||||
source {
|
||||
@@ -235,7 +239,11 @@ model {
|
||||
}
|
||||
}
|
||||
benchmarkCppStatic(NativeExecutableSpec) {
|
||||
targetBuildTypes 'debug'
|
||||
if (project.hasProperty('ciDebugOnly')) {
|
||||
targetBuildTypes 'debug'
|
||||
} else {
|
||||
targetBuildTypes 'release'
|
||||
}
|
||||
nativeUtils.excludeBinariesFromStrip(it)
|
||||
sources {
|
||||
cpp {
|
||||
|
||||
@@ -22,6 +22,7 @@ void BM_Transform(benchmark::State& state) {
|
||||
auto transform = pose2 - pose1;
|
||||
return units::math::hypot(transform.X(), transform.Y()).value();
|
||||
}};
|
||||
// NOLINTNEXTLINE(clang-analyzer-deadcode.DeadStores)
|
||||
for (auto _ : state) {
|
||||
traveler.Solve(poses, iterations);
|
||||
}
|
||||
@@ -33,6 +34,7 @@ void BM_Twist(benchmark::State& state) {
|
||||
auto twist = pose1.Log(pose2);
|
||||
return units::math::hypot(twist.dx, twist.dy).value();
|
||||
}};
|
||||
// NOLINTNEXTLINE(clang-analyzer-deadcode.DeadStores)
|
||||
for (auto _ : state) {
|
||||
traveler.Solve(poses, iterations);
|
||||
}
|
||||
|
||||
24
build.gradle
24
build.gradle
@@ -13,14 +13,14 @@ plugins {
|
||||
id 'edu.wpi.first.wpilib.versioning.WPILibVersioningPlugin' version '2023.0.1'
|
||||
id 'edu.wpi.first.wpilib.repositories.WPILibRepositoriesPlugin' version '2020.2'
|
||||
id 'edu.wpi.first.NativeUtils' apply false
|
||||
id 'edu.wpi.first.GradleJni' version '1.1.0'
|
||||
id 'edu.wpi.first.GradleJni' version '1.2.0'
|
||||
id 'edu.wpi.first.GradleVsCode'
|
||||
id 'idea'
|
||||
id 'visual-studio'
|
||||
id 'net.ltgt.errorprone' version '3.1.0' apply false
|
||||
id 'com.gradleup.shadow' version '8.3.4' apply false
|
||||
id 'com.diffplug.spotless' version '6.20.0' apply false
|
||||
id 'com.github.spotbugs' version '6.0.2' apply false
|
||||
id 'net.ltgt.errorprone' version '4.3.0' apply false
|
||||
id 'com.gradleup.shadow' version '9.1.0' apply false
|
||||
id 'com.diffplug.spotless' version '8.0.0' apply false
|
||||
id 'com.github.spotbugs' version '6.4.2' apply false
|
||||
}
|
||||
|
||||
wpilibVersioning.buildServerMode = project.hasProperty('buildServer')
|
||||
@@ -39,11 +39,11 @@ allprojects {
|
||||
}
|
||||
}
|
||||
|
||||
buildScan {
|
||||
termsOfServiceUrl = 'https://gradle.com/terms-of-service'
|
||||
termsOfServiceAgree = 'yes'
|
||||
|
||||
publishAlways()
|
||||
develocity {
|
||||
buildScan {
|
||||
termsOfUseUrl = "https://gradle.com/help/legal-terms-of-use"
|
||||
termsOfUseAgree = "yes"
|
||||
}
|
||||
}
|
||||
|
||||
import com.github.spotbugs.snom.Effort
|
||||
@@ -81,7 +81,7 @@ task libraryBuild() {}
|
||||
build.dependsOn outputVersions
|
||||
|
||||
task copyAllOutputs(type: Copy) {
|
||||
destinationDir outputsFolder
|
||||
destinationDir = outputsFolder
|
||||
}
|
||||
|
||||
build.dependsOn copyAllOutputs
|
||||
@@ -170,5 +170,5 @@ ext.getCurrentArch = {
|
||||
}
|
||||
|
||||
wrapper {
|
||||
gradleVersion = '8.11'
|
||||
gradleVersion = '8.14.3'
|
||||
}
|
||||
|
||||
@@ -5,9 +5,9 @@ repositories {
|
||||
url = 'https://frcmaven.wpi.edu/artifactory/ex-gradle'
|
||||
}
|
||||
mavenCentral()
|
||||
url "https://plugins.gradle.org/m2/"
|
||||
url = "https://plugins.gradle.org/m2/"
|
||||
}
|
||||
}
|
||||
dependencies {
|
||||
implementation "edu.wpi.first:native-utils:2025.9.1"
|
||||
implementation "edu.wpi.first:native-utils:2026.0.0"
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ repositories {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'com.google.code.gson:gson:2.10.1'
|
||||
implementation 'com.google.code.gson:gson:2.13.1'
|
||||
|
||||
implementation project(':wpiutil')
|
||||
implementation project(':wpinet')
|
||||
|
||||
@@ -46,7 +46,7 @@ public final class CameraServer {
|
||||
private static final String kPublishName = "/CameraPublisher";
|
||||
|
||||
private static final class PropertyPublisher implements AutoCloseable {
|
||||
@SuppressWarnings({"PMD.MissingBreakInSwitch", "PMD.ImplicitSwitchFallThrough", "fallthrough"})
|
||||
@SuppressWarnings("fallthrough")
|
||||
PropertyPublisher(NetworkTable table, VideoEvent event) {
|
||||
String name;
|
||||
String infoName;
|
||||
@@ -66,7 +66,7 @@ public final class CameraServer {
|
||||
break;
|
||||
case kEnum:
|
||||
m_choicesTopic = table.getStringArrayTopic(infoName + "/choices");
|
||||
// fall through
|
||||
// fallthrough
|
||||
case kInteger:
|
||||
m_integerValueEntry = table.getIntegerTopic(name).getEntry(0);
|
||||
m_minPublisher = table.getIntegerTopic(infoName + "/min").publish();
|
||||
|
||||
@@ -13,6 +13,7 @@ import org.opencv.core.Mat;
|
||||
* @see VisionRunner
|
||||
* @see VisionThread
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface VisionPipeline {
|
||||
/**
|
||||
* Processes the image input and sets the result objects. Implementations should make these
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
#include <opencv2/core/core.hpp>
|
||||
#include <wpi/print.h>
|
||||
|
||||
#include "cscore.h"
|
||||
#include "cscore_cv.h"
|
||||
|
||||
int main() {
|
||||
|
||||
@@ -68,8 +68,9 @@ public class CvSource extends ImageSource {
|
||||
case 2 -> PixelFormat.kYUYV; // 2 channels is assumed YUYV
|
||||
case 3 -> PixelFormat.kBGR; // 3 channels is assumed BGR
|
||||
case 4 -> PixelFormat.kBGRA; // 4 channels is assumed BGRA
|
||||
default -> throw new VideoException(
|
||||
"Unable to get pixel format for " + channels + " channels");
|
||||
default ->
|
||||
throw new VideoException(
|
||||
"Unable to get pixel format for " + channels + " channels");
|
||||
};
|
||||
|
||||
putFrame(finalImage, format, true);
|
||||
|
||||
@@ -773,8 +773,7 @@ void MjpegServerImpl::ConnThread::SendStream(wpi::raw_socket_ostream& os) {
|
||||
lastFrameTime = thisFrameTime;
|
||||
double timestamp = lastFrameTime / 1000000.0;
|
||||
header.clear();
|
||||
oss << "\r\n--" BOUNDARY "\r\n"
|
||||
<< "Content-Type: image/jpeg\r\n";
|
||||
oss << "\r\n--" BOUNDARY "\r\n" << "Content-Type: image/jpeg\r\n";
|
||||
wpi::print(oss, "Content-Length: {}\r\n", size);
|
||||
wpi::print(oss, "X-Timestamp: {}\r\n", timestamp);
|
||||
oss << "\r\n";
|
||||
|
||||
@@ -44,7 +44,7 @@ static O* ConvertToC(std::vector<I>&& in, int* count) {
|
||||
// retain vector at end of returned array
|
||||
alignas(T) unsigned char buf[sizeof(T)];
|
||||
new (buf) T(std::move(in));
|
||||
std::memcpy(out + size * sizeof(O), buf, sizeof(T));
|
||||
std::memcpy(out + size, buf, sizeof(T));
|
||||
|
||||
return out;
|
||||
}
|
||||
@@ -392,7 +392,7 @@ void CS_FreeEvents(CS_Event* arr, int count) {
|
||||
// destroy vector saved at end of array
|
||||
using T = std::vector<cs::RawEvent>;
|
||||
alignas(T) unsigned char buf[sizeof(T)];
|
||||
std::memcpy(buf, arr + count * sizeof(CS_Event), sizeof(T));
|
||||
std::memcpy(buf, arr + count, sizeof(T));
|
||||
reinterpret_cast<T*>(buf)->~T();
|
||||
|
||||
std::free(arr);
|
||||
|
||||
@@ -484,7 +484,7 @@ const propertyInfo_t propertyInfo[] =
|
||||
UVCERROR("USB control interface not found");
|
||||
break;
|
||||
default:
|
||||
UVCERROR("ControlRequest failed (KR=sys:sub:code) = {:02Xh}:{:03Xh}:{:04Xh}",
|
||||
UVCERROR("ControlRequest failed (KR=sys:sub:code) = {:02X}h:{:03X}h:{:04X}h",
|
||||
sys, sub, code);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -255,8 +255,9 @@ bool UsbCameraImpl::CheckDeviceChange(WPARAM wParam, DEV_BROADCAST_HDR* pHdr,
|
||||
}
|
||||
|
||||
void UsbCameraImpl::DeviceDisconnect() {
|
||||
if (m_connectVerbose)
|
||||
if (m_connectVerbose) {
|
||||
SINFO("Disconnected from {}", m_path);
|
||||
}
|
||||
m_sourceReader.Reset();
|
||||
m_mediaSource.Reset();
|
||||
if (m_imageCallback) {
|
||||
@@ -655,8 +656,9 @@ void UsbCameraImpl::DeviceCacheProperty(
|
||||
rawProp->valueStr = perProp->valueStr; // copy
|
||||
} else {
|
||||
// Read current raw value and set percentage from it
|
||||
if (!rawProp->DeviceGet(lock, pProcAmp))
|
||||
if (!rawProp->DeviceGet(lock, pProcAmp)) {
|
||||
SWARNING("failed to get property {}", rawProp->name);
|
||||
}
|
||||
|
||||
if (perProp) {
|
||||
perProp->SetValue(RawToPercentage(*rawProp, rawProp->value));
|
||||
@@ -666,8 +668,9 @@ void UsbCameraImpl::DeviceCacheProperty(
|
||||
|
||||
// Set value on device if user-configured
|
||||
if (rawProp->valueSet) {
|
||||
if (!rawProp->DeviceSet(lock, pProcAmp))
|
||||
if (!rawProp->DeviceSet(lock, pProcAmp)) {
|
||||
SWARNING("failed to set property {}", rawProp->name);
|
||||
}
|
||||
}
|
||||
|
||||
// Update pointers since we released the lock
|
||||
@@ -707,8 +710,9 @@ void UsbCameraImpl::DeviceCacheProperty(
|
||||
}
|
||||
|
||||
NotifyPropertyCreated(*rawIndex, *rawPropPtr);
|
||||
if (perPropPtr && perIndex)
|
||||
if (perPropPtr && perIndex) {
|
||||
NotifyPropertyCreated(*perIndex, *perPropPtr);
|
||||
}
|
||||
}
|
||||
|
||||
CS_StatusValue UsbCameraImpl::DeviceProcessCommand(
|
||||
|
||||
@@ -31,7 +31,7 @@ model {
|
||||
|
||||
// Create the ZIP.
|
||||
def task = project.tasks.create("copyDataLogToolExecutable" + binary.targetPlatform.operatingSystem.name + binary.targetPlatform.architecture.name, Zip) {
|
||||
description("Copies the DataLogTool executable to the outputs directory.")
|
||||
description = "Copies the DataLogTool executable to the outputs directory."
|
||||
destinationDirectory = outputsFolder
|
||||
|
||||
archiveBaseName = zipBaseName
|
||||
@@ -117,7 +117,7 @@ model {
|
||||
|
||||
artifactId = baseArtifactId
|
||||
groupId = artifactGroupId
|
||||
version wpilibVersioning.version.get()
|
||||
version = wpilibVersioning.version.get()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
plugins {
|
||||
id 'java'
|
||||
id "org.ysb33r.doxygen" version "1.0.4"
|
||||
id "org.ysb33r.doxygen" version "2.0.0"
|
||||
}
|
||||
|
||||
evaluationDependsOn(':apriltag')
|
||||
@@ -9,6 +9,7 @@ evaluationDependsOn(':cscore')
|
||||
evaluationDependsOn(':epilogue-runtime')
|
||||
evaluationDependsOn(':hal')
|
||||
evaluationDependsOn(':ntcore')
|
||||
evaluationDependsOn(':wpiannotations')
|
||||
evaluationDependsOn(':wpilibNewCommands')
|
||||
evaluationDependsOn(':wpilibc')
|
||||
evaluationDependsOn(':wpilibj')
|
||||
@@ -46,28 +47,41 @@ cppProjectZips.add(project(':romiVendordep').cppHeadersZip)
|
||||
cppProjectZips.add(project(':xrpVendordep').cppHeadersZip)
|
||||
|
||||
doxygen {
|
||||
// Doxygen binaries are only provided for x86_64 platforms
|
||||
// Other platforms will need to provide doxygen via their system
|
||||
// See below maven and https://doxygen.nl/download.html for provided binaries
|
||||
// Ensure theme.css (from https://github.com/jothepro/doxygen-awesome-css) is compatible with
|
||||
// doxygen version when updating
|
||||
// Doxygen binaries are only provided for x86_64 platforms. Other platforms
|
||||
// will need to use a local Doxygen install.
|
||||
//
|
||||
// executeByVersion() fetches Doxygen binaries from
|
||||
// https://frcmaven.wpi.edu/ui/native/generic-release-mirror/doxygen/, which
|
||||
// is a mirror of binaries from https://doxygen.nl/download.html.
|
||||
//
|
||||
// To mirror a new Doxygen version, retrigger the GitHub Actions workflow in
|
||||
// https://github.com/wpilibsuite/doxygen-mirror with the desired version
|
||||
// number as an input.
|
||||
//
|
||||
// Ensure theme.css (from https://github.com/jothepro/doxygen-awesome-css)
|
||||
// is compatible with Doxygen version when updating.
|
||||
executables {
|
||||
doxygen {
|
||||
// Note: has no effect if not on an x86_64 platform - you need to
|
||||
// have a global install available on your PATH for the Doxygen
|
||||
// plugin to run.
|
||||
executableByVersion('1.12.0')
|
||||
|
||||
String arch = System.getProperty("os.arch");
|
||||
if (arch.equals("x86_64") || arch.equals("amd64")) {
|
||||
executables {
|
||||
doxygen {
|
||||
executableByVersion('1.12.0')
|
||||
String arch = System.getProperty("os.arch");
|
||||
if (!(arch.equals("x86_64") || arch.equals("amd64"))) {
|
||||
// Search for a local Doxygen install
|
||||
executableBySearchPath('doxygen')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
doxygen {
|
||||
template 'Doxyfile'
|
||||
doxygen.sourceSets.main {
|
||||
template = 'Doxyfile'
|
||||
|
||||
cppProjectZips.each {
|
||||
dependsOn it
|
||||
source it.source
|
||||
doxygenDox.dependsOn it
|
||||
sources it.source
|
||||
it.ext.includeDirs.each {
|
||||
cppIncludeRoots.add(it.absolutePath)
|
||||
}
|
||||
@@ -77,66 +91,19 @@ doxygen {
|
||||
// apriltag
|
||||
exclude 'apriltag_pose.h'
|
||||
|
||||
// Eigen
|
||||
exclude 'Eigen/**'
|
||||
exclude 'unsupported/**'
|
||||
|
||||
// LLVM
|
||||
exclude 'wpi/AlignOf.h'
|
||||
exclude 'wpi/Casting.h'
|
||||
exclude 'wpi/Chrono.h'
|
||||
exclude 'wpi/Compiler.h'
|
||||
exclude 'wpi/ConvertUTF.h'
|
||||
exclude 'wpi/DenseMap.h'
|
||||
exclude 'wpi/DenseMapInfo.h'
|
||||
exclude 'wpi/Endian.h'
|
||||
exclude 'wpi/EpochTracker.h'
|
||||
exclude 'wpi/Errc.h'
|
||||
exclude 'wpi/Errno.h'
|
||||
exclude 'wpi/ErrorHandling.h'
|
||||
exclude 'wpi/bit.h'
|
||||
exclude 'wpi/fs.h'
|
||||
exclude 'wpi/FunctionExtras.h'
|
||||
exclude 'wpi/function_ref.h'
|
||||
exclude 'wpi/Hashing.h'
|
||||
exclude 'wpi/iterator.h'
|
||||
exclude 'wpi/iterator_range.h'
|
||||
exclude 'wpi/ManagedStatic.h'
|
||||
exclude 'wpi/MapVector.h'
|
||||
exclude 'wpi/MathExtras.h'
|
||||
exclude 'wpi/MemAlloc.h'
|
||||
exclude 'wpi/PointerIntPair.h'
|
||||
exclude 'wpi/PointerLikeTypeTraits.h'
|
||||
exclude 'wpi/PointerUnion.h'
|
||||
exclude 'wpi/raw_os_ostream.h'
|
||||
exclude 'wpi/raw_ostream.h'
|
||||
exclude 'wpi/SmallPtrSet.h'
|
||||
exclude 'wpi/SmallSet.h'
|
||||
exclude 'wpi/SmallString.h'
|
||||
exclude 'wpi/SmallVector.h'
|
||||
exclude 'wpi/StringExtras.h'
|
||||
exclude 'wpi/StringMap.h'
|
||||
exclude 'wpi/SwapByteOrder.h'
|
||||
exclude 'wpi/type_traits.h'
|
||||
exclude 'wpi/VersionTuple.h'
|
||||
exclude 'wpi/WindowsError.h'
|
||||
|
||||
// fmtlib
|
||||
exclude 'fmt/**'
|
||||
|
||||
// libuv
|
||||
exclude 'uv.h'
|
||||
exclude 'uv/**'
|
||||
exclude 'wpinet/uv/**'
|
||||
|
||||
// json
|
||||
exclude 'wpi/adl_serializer.h'
|
||||
exclude 'wpi/byte_container_with_subtype.h'
|
||||
exclude 'wpi/detail/**'
|
||||
exclude 'wpi/json.h'
|
||||
exclude 'wpi/json_fwd.h'
|
||||
exclude 'wpi/ordered_map.h'
|
||||
exclude 'wpi/thirdparty/**'
|
||||
|
||||
// mpack
|
||||
exclude 'wpi/mpack.h'
|
||||
@@ -168,8 +135,9 @@ doxygen {
|
||||
|
||||
tasks.register("zipCppDocs", Zip) {
|
||||
archiveBaseName = zipBaseNameCpp
|
||||
archiveVersion = ""
|
||||
destinationDirectory = outputsFolder
|
||||
dependsOn doxygen
|
||||
dependsOn doxygenDox
|
||||
from ("$buildDir/docs/doxygen/html")
|
||||
into '/'
|
||||
}
|
||||
@@ -177,14 +145,16 @@ tasks.register("zipCppDocs", Zip) {
|
||||
// Java
|
||||
configurations {
|
||||
javaSource {
|
||||
transitive false
|
||||
transitive = false
|
||||
}
|
||||
}
|
||||
|
||||
task generateJavaDocs(type: Javadoc) {
|
||||
classpath += project(":wpilibj").sourceSets.main.compileClasspath
|
||||
options.links("https://docs.oracle.com/en/java/javase/17/docs/api/")
|
||||
options.links("https://docs.opencv.org/4.x/javadoc/")
|
||||
// workaround for opencv site blocking javadoc tool. If the link is changed,
|
||||
// docs/opencv/element-list must be redownloaded
|
||||
options.linksOffline("https://docs.opencv.org/4.10.0/javadoc/", "opencv")
|
||||
options.addStringOption("tag", "pre:a:Pre-Condition")
|
||||
options.addBooleanOption("Xdoclint/package:" +
|
||||
// TODO: v Document these, then remove them from the list
|
||||
@@ -206,6 +176,7 @@ task generateJavaDocs(type: Javadoc) {
|
||||
"-edu.wpi.first.math.system.plant.proto," +
|
||||
"-edu.wpi.first.math.system.plant.struct," +
|
||||
"-edu.wpi.first.math.trajectory.proto," +
|
||||
"-edu.wpi.first.math.trajectory.struct," +
|
||||
// The .measure package contains generated source files for which automatic javadoc
|
||||
// generation is very difficult to do meaningfully.
|
||||
"-edu.wpi.first.units.measure", true)
|
||||
@@ -219,6 +190,7 @@ task generateJavaDocs(type: Javadoc) {
|
||||
source project(':epilogue-runtime').sourceSets.main.java
|
||||
source project(':hal').sourceSets.main.java
|
||||
source project(':ntcore').sourceSets.main.java
|
||||
source project(':wpiannotations').sourceSets.main.java
|
||||
source project(':wpilibNewCommands').sourceSets.main.java
|
||||
source project(':wpilibj').sourceSets.main.java
|
||||
source project(':wpimath').sourceSets.main.java
|
||||
@@ -246,6 +218,7 @@ task generateJavaDocs(type: Javadoc) {
|
||||
|
||||
tasks.register("zipJavaDocs", Zip) {
|
||||
archiveBaseName = zipBaseNameJava
|
||||
archiveVersion = ""
|
||||
destinationDirectory = outputsFolder
|
||||
dependsOn generateJavaDocs
|
||||
from ("$buildDir/docs/javadoc")
|
||||
@@ -265,15 +238,15 @@ publishing {
|
||||
artifact zipJavaDocs
|
||||
|
||||
artifactId = "${baseArtifactIdJava}"
|
||||
groupId artifactGroupIdJava
|
||||
version wpilibVersioning.version.get()
|
||||
groupId = artifactGroupIdJava
|
||||
version = wpilibVersioning.version.get()
|
||||
}
|
||||
cpp(MavenPublication) {
|
||||
artifact zipCppDocs
|
||||
|
||||
artifactId = "${baseArtifactIdCpp}"
|
||||
groupId artifactGroupIdCpp
|
||||
version wpilibVersioning.version.get()
|
||||
groupId = artifactGroupIdCpp
|
||||
version = wpilibVersioning.version.get()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
29
docs/opencv/element-list
Normal file
29
docs/opencv/element-list
Normal file
@@ -0,0 +1,29 @@
|
||||
org.opencv.aruco
|
||||
org.opencv.bgsegm
|
||||
org.opencv.bioinspired
|
||||
org.opencv.calib3d
|
||||
org.opencv.core
|
||||
org.opencv.dnn
|
||||
org.opencv.dnn_superres
|
||||
org.opencv.face
|
||||
org.opencv.features2d
|
||||
org.opencv.highgui
|
||||
org.opencv.img_hash
|
||||
org.opencv.imgcodecs
|
||||
org.opencv.imgproc
|
||||
org.opencv.ml
|
||||
org.opencv.objdetect
|
||||
org.opencv.osgi
|
||||
org.opencv.phase_unwrapping
|
||||
org.opencv.photo
|
||||
org.opencv.plot
|
||||
org.opencv.structured_light
|
||||
org.opencv.text
|
||||
org.opencv.tracking
|
||||
org.opencv.utils
|
||||
org.opencv.video
|
||||
org.opencv.videoio
|
||||
org.opencv.wechat_qrcode
|
||||
org.opencv.xfeatures2d
|
||||
org.opencv.ximgproc
|
||||
org.opencv.xphoto
|
||||
@@ -112,7 +112,8 @@ public class AnnotationProcessor extends AbstractProcessor {
|
||||
new MeasureHandler(processingEnv),
|
||||
new PrimitiveHandler(processingEnv),
|
||||
new SupplierHandler(processingEnv),
|
||||
new StructHandler(processingEnv), // prioritize struct over sendable
|
||||
new StructHandler(processingEnv), // prioritize struct over sendable and protobuf
|
||||
new ProtobufHandler(processingEnv), // then protobuf
|
||||
new SendableHandler(processingEnv));
|
||||
|
||||
m_epiloguerGenerator = new EpilogueGenerator(processingEnv, customLoggers);
|
||||
|
||||
@@ -6,6 +6,7 @@ package edu.wpi.first.epilogue.processor;
|
||||
|
||||
import javax.annotation.processing.ProcessingEnvironment;
|
||||
import javax.lang.model.element.Element;
|
||||
import javax.lang.model.element.TypeElement;
|
||||
import javax.lang.model.type.ArrayType;
|
||||
import javax.lang.model.type.PrimitiveType;
|
||||
import javax.lang.model.type.TypeMirror;
|
||||
@@ -52,7 +53,7 @@ public class ArrayHandler extends ElementHandler {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String logInvocation(Element element) {
|
||||
public String logInvocation(Element element, TypeElement loggedClass) {
|
||||
var dataType = dataType(element);
|
||||
|
||||
// known to be an array type (assuming isLoggable is checked first); this is a safe cast
|
||||
@@ -63,13 +64,17 @@ public class ArrayHandler extends ElementHandler {
|
||||
return "backend.log(\""
|
||||
+ loggedName(element)
|
||||
+ "\", "
|
||||
+ elementAccess(element)
|
||||
+ elementAccess(element, loggedClass)
|
||||
+ ", "
|
||||
+ m_structHandler.structAccess(componentType)
|
||||
+ ")";
|
||||
} else {
|
||||
// Primitive or string array
|
||||
return "backend.log(\"" + loggedName(element) + "\", " + elementAccess(element) + ")";
|
||||
return "backend.log(\""
|
||||
+ loggedName(element)
|
||||
+ "\", "
|
||||
+ elementAccess(element, loggedClass)
|
||||
+ ")";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ package edu.wpi.first.epilogue.processor;
|
||||
|
||||
import javax.annotation.processing.ProcessingEnvironment;
|
||||
import javax.lang.model.element.Element;
|
||||
import javax.lang.model.element.TypeElement;
|
||||
import javax.lang.model.type.DeclaredType;
|
||||
import javax.lang.model.type.TypeMirror;
|
||||
|
||||
@@ -38,7 +39,7 @@ public class CollectionHandler extends ElementHandler {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String logInvocation(Element element) {
|
||||
public String logInvocation(Element element, TypeElement loggedClass) {
|
||||
var dataType = dataType(element);
|
||||
var componentType = ((DeclaredType) dataType).getTypeArguments().get(0);
|
||||
|
||||
@@ -46,12 +47,16 @@ public class CollectionHandler extends ElementHandler {
|
||||
return "backend.log(\""
|
||||
+ loggedName(element)
|
||||
+ "\", "
|
||||
+ elementAccess(element)
|
||||
+ elementAccess(element, loggedClass)
|
||||
+ ", "
|
||||
+ m_structHandler.structAccess(componentType)
|
||||
+ ")";
|
||||
} else {
|
||||
return "backend.log(\"" + loggedName(element) + "\", " + elementAccess(element) + ")";
|
||||
return "backend.log(\""
|
||||
+ loggedName(element)
|
||||
+ "\", "
|
||||
+ elementAccess(element, loggedClass)
|
||||
+ ")";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ package edu.wpi.first.epilogue.processor;
|
||||
import java.util.Map;
|
||||
import javax.annotation.processing.ProcessingEnvironment;
|
||||
import javax.lang.model.element.Element;
|
||||
import javax.lang.model.element.TypeElement;
|
||||
import javax.lang.model.type.DeclaredType;
|
||||
import javax.lang.model.type.TypeMirror;
|
||||
|
||||
@@ -27,7 +28,7 @@ public class ConfiguredLoggerHandler extends ElementHandler {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String logInvocation(Element element) {
|
||||
public String logInvocation(Element element, TypeElement loggedClass) {
|
||||
var dataType = dataType(element);
|
||||
var loggerType =
|
||||
m_customLoggers.entrySet().stream()
|
||||
@@ -44,7 +45,7 @@ public class ConfiguredLoggerHandler extends ElementHandler {
|
||||
+ ".tryUpdate(backend.getNested(\""
|
||||
+ loggedName(element)
|
||||
+ "\"), "
|
||||
+ elementAccess(element)
|
||||
+ elementAccess(element, loggedClass)
|
||||
+ ", Epilogue.getConfig().errorHandler)";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,9 +117,9 @@ public abstract class ElementHandler {
|
||||
* @param element the element to generate the access for
|
||||
* @return the generated access snippet
|
||||
*/
|
||||
public String elementAccess(Element element) {
|
||||
public String elementAccess(Element element, TypeElement loggedClass) {
|
||||
if (element instanceof VariableElement field) {
|
||||
return fieldAccess(field);
|
||||
return fieldAccess(field, loggedClass);
|
||||
} else if (element instanceof ExecutableElement method) {
|
||||
return methodAccess(method);
|
||||
} else {
|
||||
@@ -127,8 +127,20 @@ public abstract class ElementHandler {
|
||||
}
|
||||
}
|
||||
|
||||
private static String fieldAccess(VariableElement field) {
|
||||
if (!field.getModifiers().contains(Modifier.PUBLIC)) {
|
||||
private static String fieldAccess(VariableElement field, TypeElement loggedClass) {
|
||||
var mods = field.getModifiers();
|
||||
|
||||
// To be directly accessible, the field needs to be:
|
||||
// - public; or
|
||||
// - protected or package-private, and declared by a superclass in the same package
|
||||
// However, we can't cleanly access package information, so we'll always emit a VarHandle
|
||||
// for any field declared in a superclass unless it's public and we know we can read it.
|
||||
boolean isVarHandle =
|
||||
field.getEnclosingElement().equals(loggedClass)
|
||||
? mods.contains(Modifier.PRIVATE)
|
||||
: !mods.contains(Modifier.PUBLIC);
|
||||
|
||||
if (isVarHandle) {
|
||||
// ((com.example.Foo) $fooField.get(object))
|
||||
// Extra parentheses so cast evaluates before appended methods
|
||||
// (e.g. when appending .getAsDouble())
|
||||
@@ -136,7 +148,7 @@ public abstract class ElementHandler {
|
||||
if (type.getKind() == TypeKind.TYPEVAR) {
|
||||
type = ((TypeVariable) type).getUpperBound();
|
||||
}
|
||||
return "((" + type.toString() + ") $" + field.getSimpleName() + ".get(object))";
|
||||
return "((" + type.toString() + ") " + LoggerGenerator.varHandleName(field) + ".get(object))";
|
||||
} else {
|
||||
// object.fooField
|
||||
return "object." + field.getSimpleName();
|
||||
@@ -171,5 +183,5 @@ public abstract class ElementHandler {
|
||||
* @param element the field or method element to generate the logger call for
|
||||
* @return the generated log invocation
|
||||
*/
|
||||
public abstract String logInvocation(Element element);
|
||||
public abstract String logInvocation(Element element, TypeElement loggedClass);
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ package edu.wpi.first.epilogue.processor;
|
||||
|
||||
import javax.annotation.processing.ProcessingEnvironment;
|
||||
import javax.lang.model.element.Element;
|
||||
import javax.lang.model.element.TypeElement;
|
||||
import javax.lang.model.type.TypeMirror;
|
||||
|
||||
public class EnumHandler extends ElementHandler {
|
||||
@@ -27,7 +28,11 @@ public class EnumHandler extends ElementHandler {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String logInvocation(Element element) {
|
||||
return "backend.log(\"" + loggedName(element) + "\", " + elementAccess(element) + ")";
|
||||
public String logInvocation(Element element, TypeElement loggedClass) {
|
||||
return "backend.log(\""
|
||||
+ loggedName(element)
|
||||
+ "\", "
|
||||
+ elementAccess(element, loggedClass)
|
||||
+ ")";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ public class LoggableHandler extends ElementHandler {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String logInvocation(Element element) {
|
||||
public String logInvocation(Element element, TypeElement loggedClass) {
|
||||
TypeMirror dataType = dataType(element);
|
||||
var declaredType =
|
||||
m_processingEnv
|
||||
@@ -61,7 +61,7 @@ public class LoggableHandler extends ElementHandler {
|
||||
|
||||
// If there are no known loggable subtypes, return just the single logger call
|
||||
if (size == 1) {
|
||||
return generateLoggerCall(element, declaredType, elementAccess(element));
|
||||
return generateLoggerCall(element, declaredType, elementAccess(element, loggedClass));
|
||||
}
|
||||
|
||||
// Otherwise, generate an if-else chain to compare the element with its known loggable subtypes
|
||||
@@ -73,7 +73,7 @@ public class LoggableHandler extends ElementHandler {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
// Cache the value in a variable so it's only read once
|
||||
builder.append("var %s = %s;\n".formatted(varName, elementAccess(element)));
|
||||
builder.append("var %s = %s;\n".formatted(varName, elementAccess(element, loggedClass)));
|
||||
|
||||
for (int i = 0; i < size; i++) {
|
||||
TypeElement type = loggableSubtypes.get(i);
|
||||
|
||||
@@ -18,9 +18,11 @@ import java.io.PrintWriter;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.Deque;
|
||||
import java.util.EnumMap;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
@@ -185,7 +187,21 @@ public class LoggerGenerator {
|
||||
var loggerFile = m_processingEnv.getFiler().createSourceFile(loggerClassName);
|
||||
|
||||
var varHandleFields =
|
||||
loggableFields.stream().filter(e -> !e.getModifiers().contains(Modifier.PUBLIC)).toList();
|
||||
loggableFields.stream()
|
||||
.filter(
|
||||
e -> {
|
||||
if (e.getEnclosingElement().equals(clazz)) {
|
||||
// The generated logger is in the same package as the logged class, so the
|
||||
// only fields it can't read are private ones.
|
||||
return e.getModifiers().contains(Modifier.PRIVATE);
|
||||
} else {
|
||||
// Logging from a superclass. Can only read public fields, unless the superclass
|
||||
// is in the same package, in which case protected and package-private fields
|
||||
// are also readable.
|
||||
return !e.getModifiers().contains(Modifier.PUBLIC);
|
||||
}
|
||||
})
|
||||
.toList();
|
||||
boolean requiresVarHandles = !varHandleFields.isEmpty();
|
||||
|
||||
try (var out = new PrintWriter(loggerFile.openWriter())) {
|
||||
@@ -214,41 +230,67 @@ public class LoggerGenerator {
|
||||
+ "> {");
|
||||
|
||||
if (requiresVarHandles) {
|
||||
for (var varHandleField : varHandleFields) {
|
||||
for (var privateField : varHandleFields) {
|
||||
// This field needs a VarHandle to access.
|
||||
// Cache it in the class to avoid lookups
|
||||
out.println(" private static final VarHandle $" + varHandleField.getSimpleName() + ";");
|
||||
out.printf(
|
||||
" // Accesses private or superclass field %s.%s%n",
|
||||
privateField.getEnclosingElement(), privateField.getSimpleName());
|
||||
out.printf(" private static final VarHandle %s;%n", varHandleName(privateField));
|
||||
}
|
||||
out.println();
|
||||
}
|
||||
|
||||
var classReference = simpleClassName + ".class";
|
||||
|
||||
// Static initializer block to load VarHandles and reflection fields
|
||||
if (requiresVarHandles) {
|
||||
out.println(" static {");
|
||||
out.println(" try {");
|
||||
out.println(
|
||||
" var lookup = MethodHandles.privateLookupIn("
|
||||
+ classReference
|
||||
+ ", MethodHandles.lookup());");
|
||||
|
||||
for (var varHandleField : varHandleFields) {
|
||||
var fieldName = varHandleField.getSimpleName();
|
||||
out.println(
|
||||
" $"
|
||||
+ fieldName
|
||||
+ " = lookup.findVarHandle("
|
||||
+ classReference
|
||||
+ ", \""
|
||||
+ fieldName
|
||||
+ "\", "
|
||||
+ m_processingEnv.getTypeUtils().erasure(varHandleField.asType())
|
||||
+ ".class);");
|
||||
}
|
||||
out.println(" try {");
|
||||
|
||||
out.println(" var rootLookup = MethodHandles.lookup();");
|
||||
|
||||
// Group private fields by class, then generate a private lookup for each class
|
||||
// and a VarHandle for each field using that lookup. Sorting and then collecting into a
|
||||
// LinkedHashMap gives deterministic output ordering (mostly for tests, which check exact
|
||||
// file contents, but also results in less churn when regenerating files for users who like
|
||||
// to read the generated logger classes).
|
||||
//
|
||||
// This lets us read private fields from superclasses.
|
||||
Map<Element, List<VariableElement>> privateFieldsByClass =
|
||||
varHandleFields.stream()
|
||||
.sorted(Comparator.comparing(e -> e.getSimpleName().toString()))
|
||||
.collect(
|
||||
Collectors.groupingBy(
|
||||
VariableElement::getEnclosingElement,
|
||||
LinkedHashMap::new,
|
||||
Collectors.toList()));
|
||||
|
||||
privateFieldsByClass.forEach(
|
||||
(enclosingClass, fields) -> {
|
||||
String className = enclosingClass.toString();
|
||||
String lookupName = "lookup$$" + className.replace(".", "_");
|
||||
out.printf(
|
||||
" var %s = MethodHandles.privateLookupIn(%s.class, rootLookup);%n",
|
||||
lookupName, className);
|
||||
|
||||
for (var field : fields) {
|
||||
var fieldname = field.getSimpleName();
|
||||
out.printf(
|
||||
" %s = %s.findVarHandle(%s.class, \"%s\", %s.class);%n",
|
||||
varHandleName(field),
|
||||
lookupName,
|
||||
className,
|
||||
fieldname,
|
||||
m_processingEnv.getTypeUtils().erasure(field.asType()));
|
||||
}
|
||||
});
|
||||
|
||||
out.println(" } catch (ReflectiveOperationException e) {");
|
||||
out.println(
|
||||
" throw new RuntimeException("
|
||||
+ "\"[EPILOGUE] Could not load private fields for logging!\", e);");
|
||||
out.println(" }");
|
||||
|
||||
out.println(" }");
|
||||
out.println();
|
||||
}
|
||||
@@ -300,7 +342,7 @@ public class LoggerGenerator {
|
||||
// to be logged. For example, the sendable handler consumes all sendable types
|
||||
// but does not log commands or subsystems, to prevent excessive warnings about
|
||||
// unloggable commands.
|
||||
var logInvocation = h.logInvocation(loggableElement);
|
||||
var logInvocation = h.logInvocation(loggableElement, clazz);
|
||||
if (logInvocation != null) {
|
||||
out.println(logInvocation.indent(6).stripTrailing() + ";");
|
||||
}
|
||||
@@ -315,6 +357,18 @@ public class LoggerGenerator {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the name of a VarHandle for access to the given field. The VarHandle variable's name
|
||||
* is guaranteed to be unique.
|
||||
*
|
||||
* @param field The field to generate a VarHandle for
|
||||
* @return The name of the generated VarHandle variable
|
||||
*/
|
||||
public static String varHandleName(VariableElement field) {
|
||||
return "$%s_%s"
|
||||
.formatted(field.getEnclosingElement().toString().replace(".", "_"), field.getSimpleName());
|
||||
}
|
||||
|
||||
private void collectLoggables(
|
||||
TypeElement clazz, List<VariableElement> fields, List<ExecutableElement> methods) {
|
||||
var config = clazz.getAnnotation(Logged.class);
|
||||
|
||||
@@ -6,6 +6,7 @@ package edu.wpi.first.epilogue.processor;
|
||||
|
||||
import javax.annotation.processing.ProcessingEnvironment;
|
||||
import javax.lang.model.element.Element;
|
||||
import javax.lang.model.element.TypeElement;
|
||||
import javax.lang.model.type.TypeMirror;
|
||||
|
||||
public class MeasureHandler extends ElementHandler {
|
||||
@@ -30,8 +31,12 @@ public class MeasureHandler extends ElementHandler {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String logInvocation(Element element) {
|
||||
public String logInvocation(Element element, TypeElement loggedClass) {
|
||||
// EpilogueBackend has builtin support for logging measures
|
||||
return "backend.log(\"" + loggedName(element) + "\", " + elementAccess(element) + ")";
|
||||
return "backend.log(\""
|
||||
+ loggedName(element)
|
||||
+ "\", "
|
||||
+ elementAccess(element, loggedClass)
|
||||
+ ")";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ import static javax.lang.model.type.TypeKind.SHORT;
|
||||
import java.util.Set;
|
||||
import javax.annotation.processing.ProcessingEnvironment;
|
||||
import javax.lang.model.element.Element;
|
||||
import javax.lang.model.element.TypeElement;
|
||||
import javax.lang.model.type.TypeMirror;
|
||||
|
||||
public class PrimitiveHandler extends ElementHandler {
|
||||
@@ -35,7 +36,11 @@ public class PrimitiveHandler extends ElementHandler {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String logInvocation(Element element) {
|
||||
return "backend.log(\"" + loggedName(element) + "\", " + elementAccess(element) + ")";
|
||||
public String logInvocation(Element element, TypeElement loggedClass) {
|
||||
return "backend.log(\""
|
||||
+ loggedName(element)
|
||||
+ "\", "
|
||||
+ elementAccess(element, loggedClass)
|
||||
+ ")";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
package edu.wpi.first.epilogue.processor;
|
||||
|
||||
import java.util.Set;
|
||||
import javax.annotation.processing.ProcessingEnvironment;
|
||||
import javax.lang.model.element.Element;
|
||||
import javax.lang.model.element.Modifier;
|
||||
import javax.lang.model.element.TypeElement;
|
||||
import javax.lang.model.element.VariableElement;
|
||||
import javax.lang.model.type.TypeMirror;
|
||||
import javax.lang.model.util.Elements;
|
||||
import javax.lang.model.util.Types;
|
||||
|
||||
/**
|
||||
* Supports protobuf serializable types. Protobuf-serializable types are loggable if they have a
|
||||
* public static final {@code proto} field of a type that inherits from {@code Protobuf}.
|
||||
*/
|
||||
public class ProtobufHandler extends ElementHandler {
|
||||
private final TypeMirror m_serializable;
|
||||
private final TypeElement m_protobufType;
|
||||
private final Types m_typeUtils;
|
||||
private final Elements m_elementUtils;
|
||||
|
||||
protected ProtobufHandler(ProcessingEnvironment processingEnv) {
|
||||
super(processingEnv);
|
||||
|
||||
m_serializable =
|
||||
processingEnv
|
||||
.getElementUtils()
|
||||
.getTypeElement("edu.wpi.first.util.protobuf.ProtobufSerializable")
|
||||
.asType();
|
||||
m_protobufType =
|
||||
processingEnv.getElementUtils().getTypeElement("edu.wpi.first.util.protobuf.Protobuf");
|
||||
m_typeUtils = processingEnv.getTypeUtils();
|
||||
m_elementUtils = processingEnv.getElementUtils();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLoggable(Element element) {
|
||||
return isLoggableType(dataType(element));
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a type is protobuf-serializable: implements the ProtobufSerializable marker interface
|
||||
* and has a `public static final proto` field of a type that inherits from Protobuf with a
|
||||
* compatible generic type bound.
|
||||
*
|
||||
* @param type The type to check
|
||||
* @return true if the type is protobuf-serializable, false otherwise
|
||||
*/
|
||||
public boolean isLoggableType(TypeMirror type) {
|
||||
var serializableType = m_typeUtils.erasure(type);
|
||||
var typeElement = m_elementUtils.getTypeElement(serializableType.toString());
|
||||
if (typeElement == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// eg `Protobuf<Rotation2d, ?>` instead of the raw `Protobuf` type. The message type doesn't
|
||||
// really matter here; we can leave it as a wildcard.
|
||||
var sharpProtobufType =
|
||||
m_typeUtils.getDeclaredType(
|
||||
m_protobufType,
|
||||
typeElement.asType(), // the serializable type
|
||||
m_typeUtils.getWildcardType(
|
||||
m_elementUtils.getTypeElement("us.hebi.quickbuf.ProtoMessage").asType(), null));
|
||||
|
||||
boolean hasProto =
|
||||
typeElement.getEnclosedElements().stream()
|
||||
.filter(e -> e instanceof VariableElement)
|
||||
.map(e -> (VariableElement) e)
|
||||
.anyMatch(
|
||||
field -> {
|
||||
var nameMatch = field.getSimpleName().contentEquals("proto");
|
||||
var modifiersMatch =
|
||||
field
|
||||
.getModifiers()
|
||||
.containsAll(Set.of(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL));
|
||||
var typeMatch =
|
||||
m_typeUtils.isAssignable(
|
||||
m_typeUtils.erasure(field.asType()), sharpProtobufType);
|
||||
return nameMatch && modifiersMatch && typeMatch;
|
||||
});
|
||||
return m_typeUtils.isAssignable(type, m_serializable) && hasProto;
|
||||
}
|
||||
|
||||
public String protoAccess(TypeMirror serializableType) {
|
||||
var className = m_typeUtils.erasure(serializableType).toString();
|
||||
return className + ".proto";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String logInvocation(Element element, TypeElement loggedClass) {
|
||||
return "backend.log(\"%s\", %s, %s)"
|
||||
.formatted(
|
||||
loggedName(element),
|
||||
elementAccess(element, loggedClass),
|
||||
protoAccess(dataType(element)));
|
||||
}
|
||||
}
|
||||
@@ -44,7 +44,7 @@ public class SendableHandler extends ElementHandler {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String logInvocation(Element element) {
|
||||
public String logInvocation(Element element, TypeElement loggedClass) {
|
||||
var dataType = dataType(element);
|
||||
|
||||
// Do not log commands or subsystems via their sendable implementations
|
||||
@@ -66,7 +66,7 @@ public class SendableHandler extends ElementHandler {
|
||||
return "logSendable(backend.getNested(\""
|
||||
+ loggedName(element)
|
||||
+ "\"), "
|
||||
+ elementAccess(element)
|
||||
+ elementAccess(element, loggedClass)
|
||||
+ ")";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,14 +4,26 @@
|
||||
|
||||
package edu.wpi.first.epilogue.processor;
|
||||
|
||||
import java.util.Set;
|
||||
import javax.annotation.processing.ProcessingEnvironment;
|
||||
import javax.lang.model.element.Element;
|
||||
import javax.lang.model.element.Modifier;
|
||||
import javax.lang.model.element.TypeElement;
|
||||
import javax.lang.model.element.VariableElement;
|
||||
import javax.lang.model.type.ArrayType;
|
||||
import javax.lang.model.type.TypeMirror;
|
||||
import javax.lang.model.util.Elements;
|
||||
import javax.lang.model.util.Types;
|
||||
|
||||
/**
|
||||
* Supports struct serializable types. Struct-serializable types are loggable if they have a public
|
||||
* static final {@code struct} field of a type that inherits from {@code Struct}.
|
||||
*/
|
||||
public class StructHandler extends ElementHandler {
|
||||
private final TypeMirror m_serializable;
|
||||
private final TypeElement m_structType;
|
||||
private final Types m_typeUtils;
|
||||
private final Elements m_elementUtils;
|
||||
|
||||
protected StructHandler(ProcessingEnvironment processingEnv) {
|
||||
super(processingEnv);
|
||||
@@ -20,16 +32,57 @@ public class StructHandler extends ElementHandler {
|
||||
.getElementUtils()
|
||||
.getTypeElement("edu.wpi.first.util.struct.StructSerializable")
|
||||
.asType();
|
||||
m_structType =
|
||||
processingEnv.getElementUtils().getTypeElement("edu.wpi.first.util.struct.Struct");
|
||||
m_typeUtils = processingEnv.getTypeUtils();
|
||||
m_elementUtils = processingEnv.getElementUtils();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLoggable(Element element) {
|
||||
return m_typeUtils.isAssignable(dataType(element), m_serializable);
|
||||
return isLoggableType(dataType(element));
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a type is struct-serializable: implements the StructSerializable marker interface and
|
||||
* has a `public static final struct` field of a type that inherits from Struct with a compatible
|
||||
* generic type bound.
|
||||
*
|
||||
* @param type The type to check
|
||||
* @return true if the type is struct-serializable, false otherwise
|
||||
*/
|
||||
public boolean isLoggableType(TypeMirror type) {
|
||||
return m_typeUtils.isAssignable(type, m_serializable);
|
||||
TypeMirror serializableType;
|
||||
if (type instanceof ArrayType arr) {
|
||||
serializableType = arr.getComponentType();
|
||||
} else {
|
||||
serializableType = m_typeUtils.erasure(type);
|
||||
}
|
||||
var typeElement = m_elementUtils.getTypeElement(serializableType.toString());
|
||||
if (typeElement == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// eg `Struct<Rotation2d>` instead of the raw `Struct` type
|
||||
var sharpStructType = m_typeUtils.getDeclaredType(m_structType, typeElement.asType());
|
||||
|
||||
boolean hasStruct =
|
||||
typeElement.getEnclosedElements().stream()
|
||||
.filter(e -> e instanceof VariableElement)
|
||||
.map(e -> (VariableElement) e)
|
||||
.anyMatch(
|
||||
field -> {
|
||||
var nameMatch = field.getSimpleName().contentEquals("struct");
|
||||
var modifiersMatch =
|
||||
field
|
||||
.getModifiers()
|
||||
.containsAll(Set.of(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL));
|
||||
var typeMatch =
|
||||
m_typeUtils.isAssignable(
|
||||
m_typeUtils.erasure(field.asType()), sharpStructType);
|
||||
return nameMatch && modifiersMatch && typeMatch;
|
||||
});
|
||||
return m_typeUtils.isAssignable(type, m_serializable) && hasStruct;
|
||||
}
|
||||
|
||||
public String structAccess(TypeMirror serializableType) {
|
||||
@@ -38,11 +91,11 @@ public class StructHandler extends ElementHandler {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String logInvocation(Element element) {
|
||||
public String logInvocation(Element element, TypeElement loggedClass) {
|
||||
return "backend.log(\""
|
||||
+ loggedName(element)
|
||||
+ "\", "
|
||||
+ elementAccess(element)
|
||||
+ elementAccess(element, loggedClass)
|
||||
+ ", "
|
||||
+ structAccess(dataType(element))
|
||||
+ ")";
|
||||
|
||||
@@ -6,6 +6,7 @@ package edu.wpi.first.epilogue.processor;
|
||||
|
||||
import javax.annotation.processing.ProcessingEnvironment;
|
||||
import javax.lang.model.element.Element;
|
||||
import javax.lang.model.element.TypeElement;
|
||||
import javax.lang.model.type.TypeMirror;
|
||||
|
||||
public class SupplierHandler extends ElementHandler {
|
||||
@@ -42,15 +43,19 @@ public class SupplierHandler extends ElementHandler {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String logInvocation(Element element) {
|
||||
return "backend.log(\"" + loggedName(element) + "\", " + elementAccess(element) + ")";
|
||||
public String logInvocation(Element element, TypeElement loggedClass) {
|
||||
return "backend.log(\""
|
||||
+ loggedName(element)
|
||||
+ "\", "
|
||||
+ elementAccess(element, loggedClass)
|
||||
+ ")";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String elementAccess(Element element) {
|
||||
public String elementAccess(Element element, TypeElement loggedClass) {
|
||||
var typeUtils = m_processingEnv.getTypeUtils();
|
||||
var dataType = dataType(element);
|
||||
String base = super.elementAccess(element);
|
||||
String base = super.elementAccess(element, loggedClass);
|
||||
|
||||
if (typeUtils.isAssignable(dataType, m_booleanSupplier)) {
|
||||
return base + ".getAsBoolean()";
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -8,5 +8,6 @@ java_library(
|
||||
"//ntcore:networktables-java",
|
||||
"//wpiunits",
|
||||
"//wpiutil:wpiutil-java",
|
||||
"@maven//:us_hebi_quickbuf_quickbuf_runtime",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -13,4 +13,5 @@ dependencies {
|
||||
api(project(':ntcore'))
|
||||
api(project(':wpiutil'))
|
||||
api(project(':wpiunits'))
|
||||
testImplementation(project(':wpimath')) // for convenient protobuf types
|
||||
}
|
||||
|
||||
@@ -106,14 +106,13 @@ public abstract class ClassSpecificLogger<T> {
|
||||
return;
|
||||
}
|
||||
|
||||
var builder =
|
||||
m_sendables.computeIfAbsent(
|
||||
sendable,
|
||||
s -> {
|
||||
var b = new LogBackedSendableBuilder(backend);
|
||||
s.initSendable(b);
|
||||
return b;
|
||||
});
|
||||
builder.update();
|
||||
if (m_sendables.containsKey(sendable)) {
|
||||
m_sendables.get(sendable).update();
|
||||
} else {
|
||||
var builder = new LogBackedSendableBuilder(backend);
|
||||
sendable.initSendable(builder);
|
||||
m_sendables.put(sendable, builder);
|
||||
builder.update();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,8 +6,10 @@ package edu.wpi.first.epilogue.logging;
|
||||
|
||||
import edu.wpi.first.units.Measure;
|
||||
import edu.wpi.first.units.Unit;
|
||||
import edu.wpi.first.util.protobuf.Protobuf;
|
||||
import edu.wpi.first.util.struct.Struct;
|
||||
import java.util.Collection;
|
||||
import us.hebi.quickbuf.ProtoMessage;
|
||||
|
||||
/** A backend is a generic interface for Epilogue to log discrete data points. */
|
||||
public interface EpilogueBackend {
|
||||
@@ -193,6 +195,17 @@ public interface EpilogueBackend {
|
||||
log(identifier, array, struct);
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs a protobuf-serializable object.
|
||||
*
|
||||
* @param identifier the identifier of the data point
|
||||
* @param value the value of the data point
|
||||
* @param proto the protobuf to use to serialize the data
|
||||
* @param <P> the protobuf-serializable type
|
||||
* @param <M> the protobuf message type
|
||||
*/
|
||||
<P, M extends ProtoMessage<M>> void log(String identifier, P value, Protobuf<P, M> proto);
|
||||
|
||||
/**
|
||||
* Logs a measurement's value in terms of its base unit.
|
||||
*
|
||||
|
||||
@@ -16,21 +16,28 @@ import edu.wpi.first.util.datalog.FloatArrayLogEntry;
|
||||
import edu.wpi.first.util.datalog.FloatLogEntry;
|
||||
import edu.wpi.first.util.datalog.IntegerArrayLogEntry;
|
||||
import edu.wpi.first.util.datalog.IntegerLogEntry;
|
||||
import edu.wpi.first.util.datalog.ProtobufLogEntry;
|
||||
import edu.wpi.first.util.datalog.RawLogEntry;
|
||||
import edu.wpi.first.util.datalog.StringArrayLogEntry;
|
||||
import edu.wpi.first.util.datalog.StringLogEntry;
|
||||
import edu.wpi.first.util.datalog.StructArrayLogEntry;
|
||||
import edu.wpi.first.util.datalog.StructLogEntry;
|
||||
import edu.wpi.first.util.protobuf.Protobuf;
|
||||
import edu.wpi.first.util.struct.Struct;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.BiFunction;
|
||||
import us.hebi.quickbuf.ProtoMessage;
|
||||
|
||||
/** A backend implementation that saves information to a WPILib {@link DataLog} file on disk. */
|
||||
public class FileBackend implements EpilogueBackend {
|
||||
private final DataLog m_dataLog;
|
||||
private final Map<String, DataLogEntry> m_entries = new HashMap<>();
|
||||
private final Map<String, NestedBackend> m_subLoggers = new HashMap<>();
|
||||
private final Set<Struct<?>> m_seenSchemas = new HashSet<>();
|
||||
private final Set<Protobuf<?, ?>> m_seenProtos = new HashSet<>();
|
||||
|
||||
/**
|
||||
* Creates a new file-based backend.
|
||||
@@ -43,7 +50,13 @@ public class FileBackend implements EpilogueBackend {
|
||||
|
||||
@Override
|
||||
public EpilogueBackend getNested(String path) {
|
||||
return m_subLoggers.computeIfAbsent(path, k -> new NestedBackend(k, this));
|
||||
if (!m_subLoggers.containsKey(path)) {
|
||||
var nested = new NestedBackend(path, this);
|
||||
m_subLoggers.put(path, nested);
|
||||
return nested;
|
||||
}
|
||||
|
||||
return m_subLoggers.get(path);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@@ -131,14 +144,45 @@ public class FileBackend implements EpilogueBackend {
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <S> void log(String identifier, S value, Struct<S> struct) {
|
||||
m_dataLog.addSchema(struct);
|
||||
getEntry(identifier, (log, k) -> StructLogEntry.create(log, k, struct)).append(value);
|
||||
// DataLog.addSchema has checks that we're able to skip, avoiding allocations
|
||||
if (m_seenSchemas.add(struct)) {
|
||||
m_dataLog.addSchema(struct);
|
||||
}
|
||||
|
||||
if (!m_entries.containsKey(identifier)) {
|
||||
m_entries.put(identifier, StructLogEntry.create(m_dataLog, identifier, struct));
|
||||
}
|
||||
|
||||
((StructLogEntry<S>) m_entries.get(identifier)).append(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <S> void log(String identifier, S[] value, Struct<S> struct) {
|
||||
m_dataLog.addSchema(struct);
|
||||
getEntry(identifier, (log, k) -> StructArrayLogEntry.create(log, k, struct)).append(value);
|
||||
// DataLog.addSchema has checks that we're able to skip, avoiding allocations
|
||||
if (m_seenSchemas.add(struct)) {
|
||||
m_dataLog.addSchema(struct);
|
||||
}
|
||||
|
||||
if (!m_entries.containsKey(identifier)) {
|
||||
m_entries.put(identifier, StructArrayLogEntry.create(m_dataLog, identifier, struct));
|
||||
}
|
||||
|
||||
((StructArrayLogEntry<S>) m_entries.get(identifier)).append(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <P, M extends ProtoMessage<M>> void log(String identifier, P value, Protobuf<P, M> proto) {
|
||||
// DataLog.addSchema has checks that we're able to skip, avoiding allocations
|
||||
if (m_seenProtos.add(proto)) {
|
||||
m_dataLog.addSchema(proto);
|
||||
}
|
||||
|
||||
if (!m_entries.containsKey(identifier)) {
|
||||
m_entries.put(identifier, ProtobufLogEntry.create(m_dataLog, identifier, proto));
|
||||
}
|
||||
|
||||
((ProtobufLogEntry<P>) m_entries.get(identifier)).append(value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,11 +4,13 @@
|
||||
|
||||
package edu.wpi.first.epilogue.logging;
|
||||
|
||||
import edu.wpi.first.util.protobuf.Protobuf;
|
||||
import edu.wpi.first.util.struct.Struct;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import us.hebi.quickbuf.ProtoMessage;
|
||||
|
||||
/**
|
||||
* A backend implementation that only logs data when it changes. Useful for keeping bandwidth and
|
||||
@@ -40,7 +42,13 @@ public class LazyBackend implements EpilogueBackend {
|
||||
|
||||
@Override
|
||||
public EpilogueBackend getNested(String path) {
|
||||
return m_subLoggers.computeIfAbsent(path, k -> new NestedBackend(k, this));
|
||||
if (!m_subLoggers.containsKey(path)) {
|
||||
var nested = new NestedBackend(path, this);
|
||||
m_subLoggers.put(path, nested);
|
||||
return nested;
|
||||
}
|
||||
|
||||
return m_subLoggers.get(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -237,4 +245,17 @@ public class LazyBackend implements EpilogueBackend {
|
||||
m_previousValues.put(identifier, value.clone());
|
||||
m_backend.log(identifier, value, struct);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <P, M extends ProtoMessage<M>> void log(String identifier, P value, Protobuf<P, M> proto) {
|
||||
var previous = m_previousValues.get(identifier);
|
||||
|
||||
if (Objects.equals(previous, value)) {
|
||||
// no change
|
||||
return;
|
||||
}
|
||||
|
||||
m_previousValues.put(identifier, value);
|
||||
m_backend.log(identifier, value, proto);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@ import java.util.function.LongSupplier;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/** A sendable builder implementation that sends data to a {@link EpilogueBackend}. */
|
||||
@SuppressWarnings("PMD.CouplingBetweenObjects") // most methods simply delegate to the backend
|
||||
public class LogBackedSendableBuilder implements SendableBuilder {
|
||||
private final EpilogueBackend m_backend;
|
||||
private final Collection<Runnable> m_updates = new ArrayList<>();
|
||||
|
||||
@@ -4,10 +4,12 @@
|
||||
|
||||
package edu.wpi.first.epilogue.logging;
|
||||
|
||||
import edu.wpi.first.util.protobuf.Protobuf;
|
||||
import edu.wpi.first.util.struct.Struct;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import us.hebi.quickbuf.ProtoMessage;
|
||||
|
||||
/**
|
||||
* A backend implementation that delegates to other backends. Helpful for simultaneous logging to
|
||||
@@ -24,7 +26,13 @@ public class MultiBackend implements EpilogueBackend {
|
||||
|
||||
@Override
|
||||
public EpilogueBackend getNested(String path) {
|
||||
return m_nestedBackends.computeIfAbsent(path, k -> new NestedBackend(k, this));
|
||||
if (!m_nestedBackends.containsKey(path)) {
|
||||
var nested = new NestedBackend(path, this);
|
||||
m_nestedBackends.put(path, nested);
|
||||
return nested;
|
||||
}
|
||||
|
||||
return m_nestedBackends.get(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -131,4 +139,11 @@ public class MultiBackend implements EpilogueBackend {
|
||||
backend.log(identifier, value, struct);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <P, M extends ProtoMessage<M>> void log(String identifier, P value, Protobuf<P, M> proto) {
|
||||
for (EpilogueBackend backend : m_backends) {
|
||||
backend.log(identifier, value, proto);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,15 +13,21 @@ import edu.wpi.first.networktables.FloatPublisher;
|
||||
import edu.wpi.first.networktables.IntegerArrayPublisher;
|
||||
import edu.wpi.first.networktables.IntegerPublisher;
|
||||
import edu.wpi.first.networktables.NetworkTableInstance;
|
||||
import edu.wpi.first.networktables.ProtobufPublisher;
|
||||
import edu.wpi.first.networktables.Publisher;
|
||||
import edu.wpi.first.networktables.RawPublisher;
|
||||
import edu.wpi.first.networktables.StringArrayPublisher;
|
||||
import edu.wpi.first.networktables.StringPublisher;
|
||||
import edu.wpi.first.networktables.StructArrayPublisher;
|
||||
import edu.wpi.first.networktables.StructPublisher;
|
||||
import edu.wpi.first.util.protobuf.Protobuf;
|
||||
import edu.wpi.first.util.struct.Struct;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import us.hebi.quickbuf.ProtoMessage;
|
||||
|
||||
/**
|
||||
* A backend implementation that sends data over network tables. Be careful when using this, since
|
||||
@@ -32,61 +38,82 @@ public class NTEpilogueBackend implements EpilogueBackend {
|
||||
|
||||
private final Map<String, Publisher> m_publishers = new HashMap<>();
|
||||
private final Map<String, NestedBackend> m_nestedBackends = new HashMap<>();
|
||||
private final Set<Struct<?>> m_seenSchemas = new HashSet<>();
|
||||
private final Set<Protobuf<?, ?>> m_seenProtos = new HashSet<>();
|
||||
private final Function<String, IntegerPublisher> m_createIntPublisher;
|
||||
private final Function<String, FloatPublisher> m_createFloatPublisher;
|
||||
private final Function<String, DoublePublisher> m_createDoublePublisher;
|
||||
private final Function<String, BooleanPublisher> m_createBooleanPublisher;
|
||||
private final Function<String, RawPublisher> m_createRawPublisher;
|
||||
private final Function<String, IntegerArrayPublisher> m_createIntegerArrayPublisher;
|
||||
private final Function<String, FloatArrayPublisher> m_createFloatArrayPublisher;
|
||||
private final Function<String, DoubleArrayPublisher> m_createDoubleArrayPublisher;
|
||||
private final Function<String, BooleanArrayPublisher> m_createBooleanArrayPublisher;
|
||||
private final Function<String, StringPublisher> m_createStringPublisher;
|
||||
private final Function<String, StringArrayPublisher> m_createStringArrayPublisher;
|
||||
|
||||
/**
|
||||
* Creates a logging backend that sends information to NetworkTables.
|
||||
*
|
||||
* @param nt the NetworkTable instance to use to send data to
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public NTEpilogueBackend(NetworkTableInstance nt) {
|
||||
this.m_nt = nt;
|
||||
m_createIntPublisher = identifier -> m_nt.getIntegerTopic(identifier).publish();
|
||||
m_createFloatPublisher = identifier -> m_nt.getFloatTopic(identifier).publish();
|
||||
m_createDoublePublisher = identifier -> m_nt.getDoubleTopic(identifier).publish();
|
||||
m_createBooleanPublisher = identifier -> m_nt.getBooleanTopic(identifier).publish();
|
||||
m_createRawPublisher = identifier -> m_nt.getRawTopic(identifier).publish("raw");
|
||||
m_createIntegerArrayPublisher = identifier -> m_nt.getIntegerArrayTopic(identifier).publish();
|
||||
m_createFloatArrayPublisher = identifier -> m_nt.getFloatArrayTopic(identifier).publish();
|
||||
m_createDoubleArrayPublisher = identifier -> m_nt.getDoubleArrayTopic(identifier).publish();
|
||||
m_createBooleanArrayPublisher = identifier -> m_nt.getBooleanArrayTopic(identifier).publish();
|
||||
m_createStringPublisher = identifier -> m_nt.getStringTopic(identifier).publish();
|
||||
m_createStringArrayPublisher = identifier -> m_nt.getStringArrayTopic(identifier).publish();
|
||||
}
|
||||
|
||||
@Override
|
||||
public EpilogueBackend getNested(String path) {
|
||||
return m_nestedBackends.computeIfAbsent(path, k -> new NestedBackend(k, this));
|
||||
if (!m_nestedBackends.containsKey(path)) {
|
||||
var nested = new NestedBackend(path, this);
|
||||
m_nestedBackends.put(path, nested);
|
||||
return nested;
|
||||
}
|
||||
|
||||
return m_nestedBackends.get(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void log(String identifier, int value) {
|
||||
((IntegerPublisher)
|
||||
m_publishers.computeIfAbsent(identifier, k -> m_nt.getIntegerTopic(k).publish()))
|
||||
.set(value);
|
||||
((IntegerPublisher) m_publishers.computeIfAbsent(identifier, m_createIntPublisher)).set(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void log(String identifier, long value) {
|
||||
((IntegerPublisher)
|
||||
m_publishers.computeIfAbsent(identifier, k -> m_nt.getIntegerTopic(k).publish()))
|
||||
.set(value);
|
||||
((IntegerPublisher) m_publishers.computeIfAbsent(identifier, m_createIntPublisher)).set(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void log(String identifier, float value) {
|
||||
((FloatPublisher)
|
||||
m_publishers.computeIfAbsent(identifier, k -> m_nt.getFloatTopic(k).publish()))
|
||||
.set(value);
|
||||
((FloatPublisher) m_publishers.computeIfAbsent(identifier, m_createFloatPublisher)).set(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void log(String identifier, double value) {
|
||||
((DoublePublisher)
|
||||
m_publishers.computeIfAbsent(identifier, k -> m_nt.getDoubleTopic(k).publish()))
|
||||
((DoublePublisher) m_publishers.computeIfAbsent(identifier, m_createDoublePublisher))
|
||||
.set(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void log(String identifier, boolean value) {
|
||||
((BooleanPublisher)
|
||||
m_publishers.computeIfAbsent(identifier, k -> m_nt.getBooleanTopic(k).publish()))
|
||||
((BooleanPublisher) m_publishers.computeIfAbsent(identifier, m_createBooleanPublisher))
|
||||
.set(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void log(String identifier, byte[] value) {
|
||||
((RawPublisher)
|
||||
m_publishers.computeIfAbsent(identifier, k -> m_nt.getRawTopic(k).publish("raw")))
|
||||
.set(value);
|
||||
((RawPublisher) m_publishers.computeIfAbsent(identifier, m_createRawPublisher)).set(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -100,68 +127,96 @@ public class NTEpilogueBackend implements EpilogueBackend {
|
||||
}
|
||||
|
||||
((IntegerArrayPublisher)
|
||||
m_publishers.computeIfAbsent(identifier, k -> m_nt.getIntegerArrayTopic(k).publish()))
|
||||
m_publishers.computeIfAbsent(identifier, m_createIntegerArrayPublisher))
|
||||
.set(widened);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void log(String identifier, long[] value) {
|
||||
((IntegerArrayPublisher)
|
||||
m_publishers.computeIfAbsent(identifier, k -> m_nt.getIntegerArrayTopic(k).publish()))
|
||||
m_publishers.computeIfAbsent(identifier, m_createIntegerArrayPublisher))
|
||||
.set(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void log(String identifier, float[] value) {
|
||||
((FloatArrayPublisher)
|
||||
m_publishers.computeIfAbsent(identifier, k -> m_nt.getFloatArrayTopic(k).publish()))
|
||||
((FloatArrayPublisher) m_publishers.computeIfAbsent(identifier, m_createFloatArrayPublisher))
|
||||
.set(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void log(String identifier, double[] value) {
|
||||
((DoubleArrayPublisher)
|
||||
m_publishers.computeIfAbsent(identifier, k -> m_nt.getDoubleArrayTopic(k).publish()))
|
||||
((DoubleArrayPublisher) m_publishers.computeIfAbsent(identifier, m_createDoubleArrayPublisher))
|
||||
.set(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void log(String identifier, boolean[] value) {
|
||||
((BooleanArrayPublisher)
|
||||
m_publishers.computeIfAbsent(identifier, k -> m_nt.getBooleanArrayTopic(k).publish()))
|
||||
m_publishers.computeIfAbsent(identifier, m_createBooleanArrayPublisher))
|
||||
.set(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void log(String identifier, String value) {
|
||||
((StringPublisher)
|
||||
m_publishers.computeIfAbsent(identifier, k -> m_nt.getStringTopic(k).publish()))
|
||||
((StringPublisher) m_publishers.computeIfAbsent(identifier, m_createStringPublisher))
|
||||
.set(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void log(String identifier, String[] value) {
|
||||
((StringArrayPublisher)
|
||||
m_publishers.computeIfAbsent(identifier, k -> m_nt.getStringArrayTopic(k).publish()))
|
||||
((StringArrayPublisher) m_publishers.computeIfAbsent(identifier, m_createStringArrayPublisher))
|
||||
.set(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <S> void log(String identifier, S value, Struct<S> struct) {
|
||||
m_nt.addSchema(struct);
|
||||
((StructPublisher<S>)
|
||||
m_publishers.computeIfAbsent(identifier, k -> m_nt.getStructTopic(k, struct).publish()))
|
||||
.set(value);
|
||||
// NetworkTableInstance.addSchema has checks that we're able to skip, avoiding allocations
|
||||
if (m_seenSchemas.add(struct)) {
|
||||
m_nt.addSchema(struct);
|
||||
}
|
||||
|
||||
if (m_publishers.containsKey(identifier)) {
|
||||
((StructPublisher<S>) m_publishers.get(identifier)).set(value);
|
||||
} else {
|
||||
StructPublisher<S> publisher = m_nt.getStructTopic(identifier, struct).publish();
|
||||
m_publishers.put(identifier, publisher);
|
||||
publisher.set(value);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <S> void log(String identifier, S[] value, Struct<S> struct) {
|
||||
m_nt.addSchema(struct);
|
||||
((StructArrayPublisher<S>)
|
||||
m_publishers.computeIfAbsent(
|
||||
identifier, k -> m_nt.getStructArrayTopic(k, struct).publish()))
|
||||
.set(value);
|
||||
// NetworkTableInstance.addSchema has checks that we're able to skip, avoiding allocations
|
||||
if (m_seenSchemas.add(struct)) {
|
||||
m_nt.addSchema(struct);
|
||||
}
|
||||
|
||||
if (m_publishers.containsKey(identifier)) {
|
||||
((StructArrayPublisher<S>) m_publishers.get(identifier)).set(value);
|
||||
} else {
|
||||
StructArrayPublisher<S> publisher = m_nt.getStructArrayTopic(identifier, struct).publish();
|
||||
m_publishers.put(identifier, publisher);
|
||||
publisher.set(value);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <P, M extends ProtoMessage<M>> void log(String identifier, P value, Protobuf<P, M> proto) {
|
||||
// NetworkTableInstance.addSchema has checks that we're able to skip, avoiding allocations
|
||||
if (m_seenProtos.add(proto)) {
|
||||
m_nt.addSchema(proto);
|
||||
}
|
||||
|
||||
if (m_publishers.containsKey(identifier)) {
|
||||
((ProtobufPublisher<P>) m_publishers.get(identifier)).set(value);
|
||||
} else {
|
||||
ProtobufPublisher<P> publisher = m_nt.getProtobufTopic(identifier, proto).publish();
|
||||
m_publishers.put(identifier, publisher);
|
||||
publisher.set(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,9 +4,11 @@
|
||||
|
||||
package edu.wpi.first.epilogue.logging;
|
||||
|
||||
import edu.wpi.first.util.protobuf.Protobuf;
|
||||
import edu.wpi.first.util.struct.Struct;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import us.hebi.quickbuf.ProtoMessage;
|
||||
|
||||
/**
|
||||
* A backend that logs to an underlying backend, prepending all logged data with a specific prefix.
|
||||
@@ -17,6 +19,15 @@ public class NestedBackend implements EpilogueBackend {
|
||||
private final EpilogueBackend m_impl;
|
||||
private final Map<String, NestedBackend> m_nestedBackends = new HashMap<>();
|
||||
|
||||
// String concatenation can be expensive, especially for deeply nested hierarchies with many
|
||||
// logged fields. For example, logging a hypothetical Robot.elevator.io.getHeight() would result
|
||||
// in "/Robot/" + "elevator/" + "io/" + "getHeight"; three concatenations and string and byte
|
||||
// array allocations that need to be cleaned up by the GC. Caching the results means those
|
||||
// allocations only occur once, resulting in no GC (the strings are always referenced in the
|
||||
// cache), and minimal time costs (the String object caches its own hash code, so all we do is an
|
||||
// O(1) table lookup per concatenation)
|
||||
private final Map<String, String> m_prefixedIdentifiers = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Creates a new nested backed underneath another backend.
|
||||
*
|
||||
@@ -33,83 +44,114 @@ public class NestedBackend implements EpilogueBackend {
|
||||
this.m_impl = impl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fast lookup to avoid redundant `m_prefix + identifier` concatenations. If the identifier has
|
||||
* not been seen before, we compute the concatenation and cache the result for later invocations
|
||||
* to read. This avoids redundantly recomputing the same concatenations every loop and
|
||||
* significantly cuts down on the CPU and memory overhead of the Epilogue library.
|
||||
*
|
||||
* @param identifier The identifier to prepend with {@link #m_prefix}.
|
||||
* @return The concatenated string.
|
||||
*/
|
||||
private String withPrefix(String identifier) {
|
||||
// Using computeIfAbsent would result in a new lambda object allocation on every call
|
||||
if (m_prefixedIdentifiers.containsKey(identifier)) {
|
||||
return m_prefixedIdentifiers.get(identifier);
|
||||
}
|
||||
|
||||
String result = m_prefix + identifier;
|
||||
m_prefixedIdentifiers.put(identifier, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EpilogueBackend getNested(String path) {
|
||||
return m_nestedBackends.computeIfAbsent(path, k -> new NestedBackend(k, this));
|
||||
if (!m_nestedBackends.containsKey(path)) {
|
||||
var nested = new NestedBackend(path, this);
|
||||
m_nestedBackends.put(path, nested);
|
||||
return nested;
|
||||
}
|
||||
|
||||
return m_nestedBackends.get(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void log(String identifier, int value) {
|
||||
m_impl.log(m_prefix + identifier, value);
|
||||
m_impl.log(withPrefix(identifier), value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void log(String identifier, long value) {
|
||||
m_impl.log(m_prefix + identifier, value);
|
||||
m_impl.log(withPrefix(identifier), value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void log(String identifier, float value) {
|
||||
m_impl.log(m_prefix + identifier, value);
|
||||
m_impl.log(withPrefix(identifier), value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void log(String identifier, double value) {
|
||||
m_impl.log(m_prefix + identifier, value);
|
||||
m_impl.log(withPrefix(identifier), value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void log(String identifier, boolean value) {
|
||||
m_impl.log(m_prefix + identifier, value);
|
||||
m_impl.log(withPrefix(identifier), value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void log(String identifier, byte[] value) {
|
||||
m_impl.log(m_prefix + identifier, value);
|
||||
m_impl.log(withPrefix(identifier), value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void log(String identifier, int[] value) {
|
||||
m_impl.log(m_prefix + identifier, value);
|
||||
m_impl.log(withPrefix(identifier), value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void log(String identifier, long[] value) {
|
||||
m_impl.log(m_prefix + identifier, value);
|
||||
m_impl.log(withPrefix(identifier), value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void log(String identifier, float[] value) {
|
||||
m_impl.log(m_prefix + identifier, value);
|
||||
m_impl.log(withPrefix(identifier), value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void log(String identifier, double[] value) {
|
||||
m_impl.log(m_prefix + identifier, value);
|
||||
m_impl.log(withPrefix(identifier), value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void log(String identifier, boolean[] value) {
|
||||
m_impl.log(m_prefix + identifier, value);
|
||||
m_impl.log(withPrefix(identifier), value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void log(String identifier, String value) {
|
||||
m_impl.log(m_prefix + identifier, value);
|
||||
m_impl.log(withPrefix(identifier), value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void log(String identifier, String[] value) {
|
||||
m_impl.log(m_prefix + identifier, value);
|
||||
m_impl.log(withPrefix(identifier), value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <S> void log(String identifier, S value, Struct<S> struct) {
|
||||
m_impl.log(m_prefix + identifier, value, struct);
|
||||
m_impl.log(withPrefix(identifier), value, struct);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <S> void log(String identifier, S[] value, Struct<S> struct) {
|
||||
m_impl.log(m_prefix + identifier, value, struct);
|
||||
m_impl.log(withPrefix(identifier), value, struct);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <P, M extends ProtoMessage<M>> void log(String identifier, P value, Protobuf<P, M> proto) {
|
||||
m_impl.log(m_prefix + identifier, value, proto);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,9 @@
|
||||
|
||||
package edu.wpi.first.epilogue.logging;
|
||||
|
||||
import edu.wpi.first.util.protobuf.Protobuf;
|
||||
import edu.wpi.first.util.struct.Struct;
|
||||
import us.hebi.quickbuf.ProtoMessage;
|
||||
|
||||
/** Null backend implementation that logs nothing. */
|
||||
public class NullBackend implements EpilogueBackend {
|
||||
@@ -62,4 +64,8 @@ public class NullBackend implements EpilogueBackend {
|
||||
|
||||
@Override
|
||||
public <S> void log(String identifier, S[] value, Struct<S> struct) {}
|
||||
|
||||
@Override
|
||||
public <P, M extends ProtoMessage<M>> void log(
|
||||
String identifier, P value, Protobuf<P, M> proto) {}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertSame;
|
||||
|
||||
import edu.wpi.first.math.geometry.Rotation2d;
|
||||
import java.util.List;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
@@ -185,4 +186,17 @@ class LazyBackendTest {
|
||||
assertArrayEquals(
|
||||
new byte[] {0x01, 0x00, 0x00, 0x00}, (byte[]) backend.getEntries().get(1).value());
|
||||
}
|
||||
|
||||
@Test
|
||||
void lazyProtobuf() {
|
||||
var backend = new TestBackend();
|
||||
var lazy = new LazyBackend(backend);
|
||||
|
||||
var rotation = Rotation2d.kZero;
|
||||
lazy.log("rotation", rotation, Rotation2d.proto);
|
||||
assertEquals(1, backend.getEntries().size());
|
||||
var entry = backend.getEntries().get(0);
|
||||
assertEquals("rotation", entry.identifier());
|
||||
assertArrayEquals(new byte[] {9, 0, 0, 0, 0, 0, 0, 0, 0}, (byte[]) entry.value());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,180 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
package edu.wpi.first.epilogue.logging;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertSame;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class NestedBackendTest {
|
||||
@Test
|
||||
void prefixesAppliedAndNested() {
|
||||
var root = new TestBackend();
|
||||
var nested = new NestedBackend("/Robot", root);
|
||||
|
||||
nested.log("int", 1);
|
||||
nested.log("string", "hello");
|
||||
|
||||
var arm = nested.getNested("arm");
|
||||
arm.log("position", 2.0);
|
||||
arm.log("enabled", true);
|
||||
|
||||
assertEquals(4, root.getEntries().size());
|
||||
assertEquals("/Robot/int", root.getEntries().get(0).identifier());
|
||||
assertEquals(1, root.getEntries().get(0).value());
|
||||
|
||||
assertEquals("/Robot/string", root.getEntries().get(1).identifier());
|
||||
assertEquals("hello", root.getEntries().get(1).value());
|
||||
|
||||
assertEquals("/Robot/arm/position", root.getEntries().get(2).identifier());
|
||||
assertEquals(2.0, root.getEntries().get(2).value());
|
||||
|
||||
assertEquals("/Robot/arm/enabled", root.getEntries().get(3).identifier());
|
||||
assertEquals(true, root.getEntries().get(3).value());
|
||||
}
|
||||
|
||||
@Test
|
||||
void handlesTrailingSlashOnPrefix() {
|
||||
var root = new TestBackend();
|
||||
var a = new NestedBackend("/Robot", root);
|
||||
var b = new NestedBackend("/Robot/", root);
|
||||
|
||||
a.log("x", 1);
|
||||
b.log("y", 2);
|
||||
|
||||
assertEquals("/Robot/x", root.getEntries().get(0).identifier());
|
||||
assertEquals("/Robot/y", root.getEntries().get(1).identifier());
|
||||
}
|
||||
|
||||
@Test
|
||||
void getNestedIsCached() {
|
||||
var root = new TestBackend();
|
||||
var nested = new NestedBackend("/Robot", root);
|
||||
|
||||
var arm1 = nested.getNested("arm");
|
||||
var arm2 = nested.getNested("arm");
|
||||
|
||||
assertSame(arm1, arm2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void usesPrefixedIdentifierCacheForSameField() {
|
||||
var root = new TestBackend();
|
||||
var nested = new NestedBackend("/Robot", root);
|
||||
|
||||
// Same field logged multiple times - identifier object should be the same (cached)
|
||||
// We use assertSame to check that the references are identical
|
||||
nested.log("x", 0);
|
||||
nested.log("x", 1);
|
||||
|
||||
String id0 = root.getEntries().get(0).identifier();
|
||||
String id1 = root.getEntries().get(1).identifier();
|
||||
assertSame(
|
||||
id0,
|
||||
id1,
|
||||
"Identifier %s (id: %d) was not reused (new id: %d)"
|
||||
.formatted(id0, System.identityHashCode(id0), System.identityHashCode(id1)));
|
||||
|
||||
// Also verify through a nested backend path
|
||||
var arm = nested.getNested("arm");
|
||||
arm.log("position", 0.0);
|
||||
arm.log("position", 1.0);
|
||||
|
||||
String id2 = root.getEntries().get(2).identifier();
|
||||
String id3 = root.getEntries().get(3).identifier();
|
||||
assertSame(
|
||||
id2,
|
||||
id3,
|
||||
"Identifier %s (id: %d) was not reused (new id: %d)"
|
||||
.formatted(id2, System.identityHashCode(id2), System.identityHashCode(id3)));
|
||||
|
||||
// Sanity check actual full values
|
||||
assertEquals("/Robot/x", id0);
|
||||
assertEquals("/Robot/arm/position", id2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void logsAllOverloads() {
|
||||
var root = new TestBackend();
|
||||
var nested = new NestedBackend("/Robot", root);
|
||||
|
||||
// Scalars
|
||||
nested.log("int", 1);
|
||||
nested.log("long", 2L);
|
||||
nested.log("float", 3.0f);
|
||||
nested.log("double", 4.0);
|
||||
nested.log("boolean", true);
|
||||
nested.log("string", "hello");
|
||||
|
||||
// Arrays
|
||||
nested.log("bytes", new byte[] {1, 2});
|
||||
nested.log("ints", new int[] {3, 4});
|
||||
nested.log("longs", new long[] {5L, 6L});
|
||||
nested.log("floats", new float[] {7.0f, 8.0f});
|
||||
nested.log("doubles", new double[] {9.0, 10.0});
|
||||
nested.log("booleans", new boolean[] {true, false});
|
||||
nested.log("strings", new String[] {"x", "y"});
|
||||
|
||||
// Structs
|
||||
nested.log("customStruct", new CustomStruct(7), CustomStruct.struct);
|
||||
nested.log(
|
||||
"customStructs",
|
||||
new CustomStruct[] {new CustomStruct(0), new CustomStruct(1)},
|
||||
CustomStruct.struct);
|
||||
|
||||
var entries = root.getEntries();
|
||||
int idx = 0;
|
||||
|
||||
// Scalars
|
||||
assertEquals(new TestBackend.LogEntry<>("/Robot/int", 1), entries.get(idx++));
|
||||
assertEquals(new TestBackend.LogEntry<>("/Robot/long", 2L), entries.get(idx++));
|
||||
assertEquals(new TestBackend.LogEntry<>("/Robot/float", 3.0f), entries.get(idx++));
|
||||
assertEquals(new TestBackend.LogEntry<>("/Robot/double", 4.0), entries.get(idx++));
|
||||
assertEquals(new TestBackend.LogEntry<>("/Robot/boolean", true), entries.get(idx++));
|
||||
assertEquals(new TestBackend.LogEntry<>("/Robot/string", "hello"), entries.get(idx++));
|
||||
|
||||
// Arrays
|
||||
assertEquals("/Robot/bytes", entries.get(idx).identifier());
|
||||
assertArrayEquals(new byte[] {1, 2}, (byte[]) entries.get(idx++).value());
|
||||
|
||||
assertEquals("/Robot/ints", entries.get(idx).identifier());
|
||||
assertArrayEquals(new int[] {3, 4}, (int[]) entries.get(idx++).value());
|
||||
|
||||
assertEquals("/Robot/longs", entries.get(idx).identifier());
|
||||
assertArrayEquals(new long[] {5L, 6L}, (long[]) entries.get(idx++).value());
|
||||
|
||||
assertEquals("/Robot/floats", entries.get(idx).identifier());
|
||||
assertArrayEquals(new float[] {7.0f, 8.0f}, (float[]) entries.get(idx++).value());
|
||||
|
||||
assertEquals("/Robot/doubles", entries.get(idx).identifier());
|
||||
assertArrayEquals(new double[] {9.0, 10.0}, (double[]) entries.get(idx++).value());
|
||||
|
||||
assertEquals("/Robot/booleans", entries.get(idx).identifier());
|
||||
assertArrayEquals(new boolean[] {true, false}, (boolean[]) entries.get(idx++).value());
|
||||
|
||||
assertEquals("/Robot/strings", entries.get(idx).identifier());
|
||||
assertArrayEquals(new String[] {"x", "y"}, (String[]) entries.get(idx++).value());
|
||||
|
||||
// Structs are serialized to bytes
|
||||
assertEquals("/Robot/customStruct", entries.get(idx).identifier());
|
||||
assertArrayEquals(new byte[] {0x07, 0x00, 0x00, 0x00}, (byte[]) entries.get(idx++).value());
|
||||
|
||||
assertEquals("/Robot/customStructs", entries.get(idx).identifier());
|
||||
// two int32 values, little-endian
|
||||
assertArrayEquals(
|
||||
new byte[] {
|
||||
0x00, 0x00, 0x00, 0x00, // 0 (first element)
|
||||
0x01, 0x00, 0x00, 0x00, // 1 (second element)
|
||||
0x00, 0x00, 0x00, 0x00, // 0 (empty space allocated by StructBuffer)
|
||||
0x00, 0x00, 0x00, 0x00 // 0 (empty space allocated by StructBuffer)
|
||||
},
|
||||
(byte[]) entries.get(idx++).value());
|
||||
|
||||
// Ensure we covered all calls
|
||||
assertEquals(idx, entries.size());
|
||||
}
|
||||
}
|
||||
@@ -4,12 +4,14 @@
|
||||
|
||||
package edu.wpi.first.epilogue.logging;
|
||||
|
||||
import edu.wpi.first.util.protobuf.Protobuf;
|
||||
import edu.wpi.first.util.struct.Struct;
|
||||
import edu.wpi.first.util.struct.StructBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import us.hebi.quickbuf.ProtoMessage;
|
||||
|
||||
@SuppressWarnings("PMD.TestClassWithoutTestCases") // This is not a test class!
|
||||
public class TestBackend implements EpilogueBackend {
|
||||
@@ -114,4 +116,12 @@ public class TestBackend implements EpilogueBackend {
|
||||
|
||||
m_entries.add(new LogEntry<>(identifier, serialized));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <P, M extends ProtoMessage<M>> void log(String identifier, P value, Protobuf<P, M> proto) {
|
||||
var msg = proto.createMessage();
|
||||
proto.pack(msg, value);
|
||||
var serialized = msg.toByteArray();
|
||||
m_entries.add(new LogEntry<>(identifier, serialized));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,8 +63,8 @@ model {
|
||||
artifact cppSourcesZip
|
||||
|
||||
artifactId = "${baseArtifactId}-cpp"
|
||||
groupId artifactGroupId
|
||||
version wpilibVersioning.version.get()
|
||||
groupId = artifactGroupId
|
||||
version = wpilibVersioning.version.get()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,12 +17,13 @@ public enum Fields {
|
||||
k2022RapidReact("2022-rapidreact.json"),
|
||||
k2023ChargedUp("2023-chargedup.json"),
|
||||
k2024Crescendo("2024-crescendo.json"),
|
||||
k2025Reefscape("2025-reefscape.json");
|
||||
k2025Reefscape("2025-reefscape.json"),
|
||||
k2026Rebuilt("2026-rebuilt.json");
|
||||
|
||||
public static final String kBaseResourceDir = "/edu/wpi/first/fields/";
|
||||
|
||||
/** Alias to the current game. */
|
||||
public static final Fields kDefaultField = k2025Reefscape;
|
||||
public static final Fields kDefaultField = k2026Rebuilt;
|
||||
|
||||
public final String m_resourceFile;
|
||||
|
||||
|
||||
@@ -17,10 +17,12 @@
|
||||
#include "fields/2023-chargedup.h"
|
||||
#include "fields/2024-crescendo.h"
|
||||
#include "fields/2025-reefscape.h"
|
||||
#include "fields/2026-rebuilt.h"
|
||||
|
||||
using namespace fields;
|
||||
|
||||
static const Field kFields[] = {
|
||||
{"2026 Rebuilt", GetResource_2026_rebuilt_json, GetResource_2026_field_png},
|
||||
{"2025 Reefscape", GetResource_2025_reefscape_json,
|
||||
GetResource_2025_field_png},
|
||||
{"2024 Crescendo", GetResource_2024_crescendo_json,
|
||||
|
||||
12
fieldImages/src/main/native/include/fields/2026-rebuilt.h
Normal file
12
fieldImages/src/main/native/include/fields/2026-rebuilt.h
Normal file
@@ -0,0 +1,12 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string_view>
|
||||
|
||||
namespace fields {
|
||||
std::string_view GetResource_2026_rebuilt_json();
|
||||
std::string_view GetResource_2026_field_png();
|
||||
} // namespace fields
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 742 KiB |
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"game": "Rebuilt",
|
||||
"field-image": "2026-field.png",
|
||||
"field-corners": {
|
||||
"top-left": [
|
||||
245,
|
||||
118
|
||||
],
|
||||
"bottom-right": [
|
||||
3942,
|
||||
1914
|
||||
]
|
||||
},
|
||||
"field-size": [
|
||||
54.269,
|
||||
26.474
|
||||
],
|
||||
"field-unit": "foot"
|
||||
}
|
||||
@@ -86,7 +86,7 @@ model {
|
||||
|
||||
// Create the ZIP.
|
||||
def task = project.tasks.create("copyGlassExecutable" + binary.targetPlatform.operatingSystem.name + binary.targetPlatform.architecture.name, Zip) {
|
||||
description("Copies the Glass executable to the outputs directory.")
|
||||
description = "Copies the Glass executable to the outputs directory."
|
||||
destinationDirectory = outputsFolder
|
||||
|
||||
archiveBaseName = zipBaseName
|
||||
@@ -175,7 +175,7 @@ model {
|
||||
|
||||
artifactId = baseArtifactId
|
||||
groupId = artifactGroupId
|
||||
version wpilibVersioning.version.get()
|
||||
version = wpilibVersioning.version.get()
|
||||
}
|
||||
libglass(MavenPublication) {
|
||||
libGlassTaskList.each { artifact it }
|
||||
@@ -185,7 +185,7 @@ model {
|
||||
|
||||
artifactId = libBaseArtifactId
|
||||
groupId = libArtifactGroupId
|
||||
version wpilibVersioning.version.get()
|
||||
version = wpilibVersioning.version.get()
|
||||
}
|
||||
libglassnt(MavenPublication) {
|
||||
libGlassntTaskList.each { artifact it }
|
||||
@@ -195,7 +195,7 @@ model {
|
||||
|
||||
artifactId = libntBaseArtifactId
|
||||
groupId = libntArtifactGroupId
|
||||
version wpilibVersioning.version.get()
|
||||
version = wpilibVersioning.version.get()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,11 +71,11 @@ void glass::DisplayFMS(FMSModel* model, bool editableDsAttached) {
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Auto") && !enabled) {
|
||||
model->SetMatchTime(15.0);
|
||||
model->SetMatchTime(20.0);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Teleop") && !enabled) {
|
||||
model->SetMatchTime(135.0);
|
||||
model->SetMatchTime(140.0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -148,11 +148,14 @@ void glass::DisplayFMSReadOnly(FMSModel* model) {
|
||||
ImGui::TextUnformatted("?");
|
||||
}
|
||||
}
|
||||
|
||||
wpi::SmallString<64> gameSpecificMessageBuf;
|
||||
std::string_view gameSpecificMessage =
|
||||
model->GetGameSpecificMessage(gameSpecificMessageBuf);
|
||||
ImGui::Text("Game Specific: %s", exists ? gameSpecificMessage.data() : "?");
|
||||
if (exists) {
|
||||
wpi::SmallString<64> gsmBuf;
|
||||
std::string_view gsm = model->GetGameSpecificMessage(gsmBuf);
|
||||
ImGui::Text("Game Specific: %.*s", static_cast<int>(gsm.size()),
|
||||
gsm.data());
|
||||
} else {
|
||||
ImGui::TextUnformatted("Game Specific: ?");
|
||||
}
|
||||
|
||||
if (!exists) {
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
@@ -343,7 +343,7 @@ static bool InputPose(frc::Pose2d* pose) {
|
||||
}
|
||||
|
||||
FieldInfo::FieldInfo(Storage& storage)
|
||||
: m_builtin{storage.GetString("builtin", "2025 Reefscape")},
|
||||
: m_builtin{storage.GetString("builtin", "2026 Rebuilt")},
|
||||
m_filename{storage.GetString("image")},
|
||||
m_width{storage.GetFloat("width", kDefaultWidth.to<float>())},
|
||||
m_height{storage.GetFloat("height", kDefaultHeight.to<float>())},
|
||||
|
||||
@@ -4,11 +4,8 @@
|
||||
|
||||
#include "glass/other/PIDController.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <imgui.h>
|
||||
|
||||
#include "glass/Context.h"
|
||||
#include "glass/DataSource.h"
|
||||
|
||||
using namespace glass;
|
||||
@@ -34,8 +31,8 @@ void glass::DisplayPIDController(PIDControllerModel* m) {
|
||||
[flag](const char* name, double* v,
|
||||
std::function<void(double)> callback) {
|
||||
ImGui::SetNextItemWidth(ImGui::GetFontSize() * 4);
|
||||
if (ImGui::InputScalar(name, ImGuiDataType_Double, v, NULL, NULL,
|
||||
"%.3f", flag)) {
|
||||
if (ImGui::InputScalar(name, ImGuiDataType_Double, v, nullptr,
|
||||
nullptr, "%.3f", flag)) {
|
||||
callback(*v);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -4,11 +4,8 @@
|
||||
|
||||
#include "glass/other/ProfiledPIDController.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <imgui.h>
|
||||
|
||||
#include "glass/Context.h"
|
||||
#include "glass/DataSource.h"
|
||||
|
||||
using namespace glass;
|
||||
@@ -34,8 +31,8 @@ void glass::DisplayProfiledPIDController(ProfiledPIDControllerModel* m) {
|
||||
[flag](const char* name, double* v,
|
||||
std::function<void(double)> callback) {
|
||||
ImGui::SetNextItemWidth(ImGui::GetFontSize() * 4);
|
||||
if (ImGui::InputScalar(name, ImGuiDataType_Double, v, NULL, NULL,
|
||||
"%.3f", flag)) {
|
||||
if (ImGui::InputScalar(name, ImGuiDataType_Double, v, nullptr,
|
||||
nullptr, "%.3f", flag)) {
|
||||
callback(*v);
|
||||
}
|
||||
};
|
||||
|
||||
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,6 +1,6 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
||||
9
gradlew
vendored
9
gradlew
vendored
@@ -86,8 +86,7 @@ done
|
||||
# shellcheck disable=SC2034
|
||||
APP_BASE_NAME=${0##*/}
|
||||
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s
|
||||
' "$PWD" ) || exit
|
||||
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD=maximum
|
||||
@@ -115,7 +114,7 @@ case "$( uname )" in #(
|
||||
NONSTOP* ) nonstop=true ;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
CLASSPATH="\\\"\\\""
|
||||
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
@@ -206,7 +205,7 @@ fi
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Collect all arguments for the java command:
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
||||
# and any embedded shellness will be escaped.
|
||||
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
|
||||
# treated as '${Hostname}' itself on the command line.
|
||||
@@ -214,7 +213,7 @@ DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
set -- \
|
||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||
-classpath "$CLASSPATH" \
|
||||
org.gradle.wrapper.GradleWrapperMain \
|
||||
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
|
||||
"$@"
|
||||
|
||||
# Stop when "xargs" is not available.
|
||||
|
||||
4
gradlew.bat
vendored
4
gradlew.bat
vendored
@@ -70,11 +70,11 @@ goto fail
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
set CLASSPATH=
|
||||
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
|
||||
@@ -17,6 +17,7 @@ kFramework_AdvantageKit = 7
|
||||
kFramework_MagicBot = 8
|
||||
kFramework_KitBotTraditional = 9
|
||||
kFramework_KitBotInline = 10
|
||||
kFramework_Everybot = 11
|
||||
kRobotDrive_ArcadeStandard = 1
|
||||
kRobotDrive_ArcadeButtonSpin = 2
|
||||
kRobotDrive_ArcadeRatioCurve = 3
|
||||
@@ -35,6 +36,7 @@ kRobotDriveSwerve_YAGSL = 15
|
||||
kRobotDriveSwerve_CTRE = 16
|
||||
kRobotDriveSwerve_MaxSwerve = 17
|
||||
kRobotDriveSwerve_AdvantageKit = 18
|
||||
kRobotDriveSwerve_EasySwerve = 19
|
||||
kDriverStationCIO_Analog = 1
|
||||
kDriverStationCIO_DigitalIn = 2
|
||||
kDriverStationCIO_DigitalOut = 3
|
||||
@@ -68,7 +70,7 @@ kDashboard_Shuffleboard = 4
|
||||
kDashboard_Elastic = 5
|
||||
kDashboard_LabVIEW = 6
|
||||
kDashboard_AdvantageScope = 7
|
||||
kDashboard_QFRCDashboard = 8
|
||||
kDashboard_QDash = 8
|
||||
kDashboard_FRCWebComponents = 9
|
||||
kDataLogLocation_Onboard = 1
|
||||
kDataLogLocation_USB = 2
|
||||
|
||||
@@ -125,3 +125,13 @@ kResourceType_ThriftyNova = 123
|
||||
kResourceType_RevServoHub = 124
|
||||
kResourceType_PWFSEN36005 = 125
|
||||
kResourceType_LaserShark = 126
|
||||
kResourceType_YAMS = 127
|
||||
kResourceType_LEDPattern = 128
|
||||
kResourceType_LinearQuadraticRegulator = 129
|
||||
kResourceType_KalmanFilter = 130
|
||||
kResourceType_PoseEstimator = 131
|
||||
kResourceType_PoseEstimator3d = 132
|
||||
kResourceType_LinearSystemLoop = 133
|
||||
kResourceType_LumynLabs_ConnectorX = 134
|
||||
kResourceType_LumynLabs_ConnectorXAnimate = 135
|
||||
kResourceType_RevMAXSplineEncoder = 136
|
||||
|
||||
@@ -273,6 +273,26 @@ public final class FRCNetComm {
|
||||
public static final int kResourceType_PWFSEN36005 = 125;
|
||||
/** kResourceType_LaserShark = 126. */
|
||||
public static final int kResourceType_LaserShark = 126;
|
||||
/** kResourceType_YAMS = 127. */
|
||||
public static final int kResourceType_YAMS = 127;
|
||||
/** kResourceType_LEDPattern = 128. */
|
||||
public static final int kResourceType_LEDPattern = 128;
|
||||
/** kResourceType_LinearQuadraticRegulator = 129. */
|
||||
public static final int kResourceType_LinearQuadraticRegulator = 129;
|
||||
/** kResourceType_KalmanFilter = 130. */
|
||||
public static final int kResourceType_KalmanFilter = 130;
|
||||
/** kResourceType_PoseEstimator = 131. */
|
||||
public static final int kResourceType_PoseEstimator = 131;
|
||||
/** kResourceType_PoseEstimator3d = 132. */
|
||||
public static final int kResourceType_PoseEstimator3d = 132;
|
||||
/** kResourceType_LinearSystemLoop = 133. */
|
||||
public static final int kResourceType_LinearSystemLoop = 133;
|
||||
/** kResourceType_LumynLabs_ConnectorX = 134. */
|
||||
public static final int kResourceType_LumynLabs_ConnectorX = 134;
|
||||
/** kResourceType_LumynLabs_ConnectorXAnimate = 135. */
|
||||
public static final int kResourceType_LumynLabs_ConnectorXAnimate = 135;
|
||||
/** kResourceType_RevMAXSplineEncoder = 136. */
|
||||
public static final int kResourceType_RevMAXSplineEncoder = 136;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -321,6 +341,8 @@ public final class FRCNetComm {
|
||||
public static final int kFramework_KitBotTraditional = 9;
|
||||
/** kFramework_KitBotInline = 10. */
|
||||
public static final int kFramework_KitBotInline = 10;
|
||||
/** kFramework_Everybot = 11. */
|
||||
public static final int kFramework_Everybot = 11;
|
||||
/** kRobotDrive_ArcadeStandard = 1. */
|
||||
public static final int kRobotDrive_ArcadeStandard = 1;
|
||||
/** kRobotDrive_ArcadeButtonSpin = 2. */
|
||||
@@ -357,6 +379,8 @@ public final class FRCNetComm {
|
||||
public static final int kRobotDriveSwerve_MaxSwerve = 17;
|
||||
/** kRobotDriveSwerve_AdvantageKit = 18. */
|
||||
public static final int kRobotDriveSwerve_AdvantageKit = 18;
|
||||
/** kRobotDriveSwerve_EasySwerve = 19. */
|
||||
public static final int kRobotDriveSwerve_EasySwerve = 19;
|
||||
/** kDriverStationCIO_Analog = 1. */
|
||||
public static final int kDriverStationCIO_Analog = 1;
|
||||
/** kDriverStationCIO_DigitalIn = 2. */
|
||||
@@ -423,8 +447,8 @@ public final class FRCNetComm {
|
||||
public static final int kDashboard_LabVIEW = 6;
|
||||
/** kDashboard_AdvantageScope = 7. */
|
||||
public static final int kDashboard_AdvantageScope = 7;
|
||||
/** kDashboard_QFRCDashboard = 8. */
|
||||
public static final int kDashboard_QFRCDashboard = 8;
|
||||
/** kDashboard_QDash = 8. */
|
||||
public static final int kDashboard_QDash = 8;
|
||||
/** kDashboard_FRCWebComponents = 9. */
|
||||
public static final int kDashboard_FRCWebComponents = 9;
|
||||
/** kDataLogLocation_Onboard = 1. */
|
||||
|
||||
@@ -178,6 +178,16 @@ namespace HALUsageReporting {
|
||||
kResourceType_RevServoHub = 124,
|
||||
kResourceType_PWFSEN36005 = 125,
|
||||
kResourceType_LaserShark = 126,
|
||||
kResourceType_YAMS = 127,
|
||||
kResourceType_LEDPattern = 128,
|
||||
kResourceType_LinearQuadraticRegulator = 129,
|
||||
kResourceType_KalmanFilter = 130,
|
||||
kResourceType_PoseEstimator = 131,
|
||||
kResourceType_PoseEstimator3d = 132,
|
||||
kResourceType_LinearSystemLoop = 133,
|
||||
kResourceType_LumynLabs_ConnectorX = 134,
|
||||
kResourceType_LumynLabs_ConnectorXAnimate = 135,
|
||||
kResourceType_RevMAXSplineEncoder = 136,
|
||||
};
|
||||
enum tInstances : int32_t {
|
||||
kLanguage_LabVIEW = 1,
|
||||
@@ -199,6 +209,7 @@ namespace HALUsageReporting {
|
||||
kFramework_MagicBot = 8,
|
||||
kFramework_KitBotTraditional = 9,
|
||||
kFramework_KitBotInline = 10,
|
||||
kFramework_Everybot = 11,
|
||||
kRobotDrive_ArcadeStandard = 1,
|
||||
kRobotDrive_ArcadeButtonSpin = 2,
|
||||
kRobotDrive_ArcadeRatioCurve = 3,
|
||||
@@ -217,6 +228,7 @@ namespace HALUsageReporting {
|
||||
kRobotDriveSwerve_CTRE = 16,
|
||||
kRobotDriveSwerve_MaxSwerve = 17,
|
||||
kRobotDriveSwerve_AdvantageKit = 18,
|
||||
kRobotDriveSwerve_EasySwerve = 19,
|
||||
kDriverStationCIO_Analog = 1,
|
||||
kDriverStationCIO_DigitalIn = 2,
|
||||
kDriverStationCIO_DigitalOut = 3,
|
||||
@@ -250,7 +262,7 @@ namespace HALUsageReporting {
|
||||
kDashboard_Elastic = 5,
|
||||
kDashboard_LabVIEW = 6,
|
||||
kDashboard_AdvantageScope = 7,
|
||||
kDashboard_QFRCDashboard = 8,
|
||||
kDashboard_QDash = 8,
|
||||
kDashboard_FRCWebComponents = 9,
|
||||
kDataLogLocation_Onboard = 1,
|
||||
kDataLogLocation_USB = 2,
|
||||
|
||||
@@ -147,6 +147,16 @@ typedef enum
|
||||
kResourceType_RevServoHub = 124,
|
||||
kResourceType_PWFSEN36005 = 125,
|
||||
kResourceType_LaserShark = 126,
|
||||
kResourceType_YAMS = 127,
|
||||
kResourceType_LEDPattern = 128,
|
||||
kResourceType_LinearQuadraticRegulator = 129,
|
||||
kResourceType_KalmanFilter = 130,
|
||||
kResourceType_PoseEstimator = 131,
|
||||
kResourceType_PoseEstimator3d = 132,
|
||||
kResourceType_LinearSystemLoop = 133,
|
||||
kResourceType_LumynLabs_ConnectorX = 134,
|
||||
kResourceType_LumynLabs_ConnectorXAnimate = 135,
|
||||
kResourceType_RevMAXSplineEncoder = 136,
|
||||
|
||||
// kResourceType_MaximumID = 255,
|
||||
} tResourceType;
|
||||
@@ -172,6 +182,7 @@ typedef enum
|
||||
kFramework_MagicBot = 8,
|
||||
kFramework_KitBotTraditional = 9,
|
||||
kFramework_KitBotInline = 10,
|
||||
kFramework_Everybot = 11,
|
||||
kRobotDrive_ArcadeStandard = 1,
|
||||
kRobotDrive_ArcadeButtonSpin = 2,
|
||||
kRobotDrive_ArcadeRatioCurve = 3,
|
||||
@@ -190,6 +201,7 @@ typedef enum
|
||||
kRobotDriveSwerve_CTRE = 16,
|
||||
kRobotDriveSwerve_MaxSwerve = 17,
|
||||
kRobotDriveSwerve_AdvantageKit = 18,
|
||||
kRobotDriveSwerve_EasySwerve = 19,
|
||||
kDriverStationCIO_Analog = 1,
|
||||
kDriverStationCIO_DigitalIn = 2,
|
||||
kDriverStationCIO_DigitalOut = 3,
|
||||
@@ -223,7 +235,7 @@ typedef enum
|
||||
kDashboard_Elastic = 5,
|
||||
kDashboard_LabVIEW = 6,
|
||||
kDashboard_AdvantageScope = 7,
|
||||
kDashboard_QFRCDashboard = 8,
|
||||
kDashboard_QDash = 8,
|
||||
kDashboard_FRCWebComponents = 9,
|
||||
kDataLogLocation_Onboard = 1,
|
||||
kDataLogLocation_USB = 2,
|
||||
|
||||
@@ -15,19 +15,19 @@ package edu.wpi.first.hal;
|
||||
public class AnalogJNI extends JNIWrapper {
|
||||
/**
|
||||
* <i>native declaration : AthenaJava\target\native\include\HAL\Analog.h:58</i><br>
|
||||
* enum values
|
||||
* enum values.
|
||||
*/
|
||||
public interface AnalogTriggerType {
|
||||
/** <i>native declaration : AthenaJava\target\native\include\HAL\Analog.h:54</i> */
|
||||
/** <i>native declaration : AthenaJava\target\native\include\HAL\Analog.h:54</i>. */
|
||||
int kInWindow = 0;
|
||||
|
||||
/** <i>native declaration : AthenaJava\target\native\include\HAL\Analog.h:55</i> */
|
||||
/** <i>native declaration : AthenaJava\target\native\include\HAL\Analog.h:55</i>. */
|
||||
int kState = 1;
|
||||
|
||||
/** <i>native declaration : AthenaJava\target\native\include\HAL\Analog.h:56</i> */
|
||||
/** <i>native declaration : AthenaJava\target\native\include\HAL\Analog.h:56</i>. */
|
||||
int kRisingPulse = 2;
|
||||
|
||||
/** <i>native declaration : AthenaJava\target\native\include\HAL\Analog.h:57</i> */
|
||||
/** <i>native declaration : AthenaJava\target\native\include\HAL\Analog.h:57</i>. */
|
||||
int kFallingPulse = 3;
|
||||
}
|
||||
|
||||
|
||||
@@ -35,10 +35,10 @@ public final class CANAPITypes {
|
||||
kGyroSensor(4),
|
||||
/** Accelerometer. */
|
||||
kAccelerometer(5),
|
||||
/** Ultrasonic sensor. */
|
||||
kUltrasonicSensor(6),
|
||||
/** Gear tooth sensor. */
|
||||
kGearToothSensor(7),
|
||||
/** Distance sensor. */
|
||||
kDistanceSensor(6),
|
||||
/** Encoder. */
|
||||
kEncoder(7),
|
||||
/** Power distribution. */
|
||||
kPowerDistribution(8),
|
||||
/** Pneumatics. */
|
||||
@@ -49,11 +49,13 @@ public final class CANAPITypes {
|
||||
kIOBreakout(11),
|
||||
/** Servo Controller. */
|
||||
kServoController(12),
|
||||
/** Color Sensor. */
|
||||
kColorSensor(13),
|
||||
/** Firmware update. */
|
||||
kFirmwareUpdate(31);
|
||||
|
||||
/** The device type ID. */
|
||||
@SuppressWarnings("PMD.MemberName")
|
||||
@SuppressWarnings("MemberName")
|
||||
public final int id;
|
||||
|
||||
CANDeviceType(int id) {
|
||||
@@ -105,10 +107,18 @@ public final class CANAPITypes {
|
||||
/** AndyMark. */
|
||||
kAndyMark(15),
|
||||
/** Vivid-Hosting. */
|
||||
kVividHosting(16);
|
||||
kVividHosting(16),
|
||||
/** Vertos Robotics. */
|
||||
kVertosRobotics(17),
|
||||
/** SWYFT Robotics. */
|
||||
kSWYFTRobotics(18),
|
||||
/** Lumyn Labs. */
|
||||
kLumynLabs(19),
|
||||
/** Brushland Labs. */
|
||||
kBrushlandLabs(20);
|
||||
|
||||
/** The manufacturer ID. */
|
||||
@SuppressWarnings("PMD.MemberName")
|
||||
@SuppressWarnings("MemberName")
|
||||
public final int id;
|
||||
|
||||
CANManufacturer(int id) {
|
||||
|
||||
@@ -308,16 +308,19 @@ public class DriverStationJNI extends JNIWrapper {
|
||||
public static native int getJoystickAxisType(byte joystickNum, byte axis);
|
||||
|
||||
/**
|
||||
* Returns the approximate match time.
|
||||
* Return the approximate match time. The FMS does not send an official match time to the robots,
|
||||
* but does send an approximate match time. The value will count down the time remaining in the
|
||||
* current period (auto or teleop). Warning: This is not an official time (so it cannot be used to
|
||||
* dispute ref calls or guarantee that a function will trigger before the match ends).
|
||||
*
|
||||
* <p>The FMS does not send an official match time to the robots, but does send an approximate
|
||||
* match time. The value will count down the time remaining in the current period (auto or
|
||||
* teleop).
|
||||
* <p>When connected to the real field, this number only changes in full integer increments, and
|
||||
* always counts down.
|
||||
*
|
||||
* <p>Warning: This is not an official time (so it cannot be used to dispute ref calls or
|
||||
* guarantee that a function will trigger before the match ends).
|
||||
* <p>When the DS is in practice mode, this number is a floating point number, and counts down.
|
||||
*
|
||||
* <p>The Practice Match function of the DS approximates the behavior seen on the field.
|
||||
* <p>When the DS is in teleop or autonomous mode, this number returns -1.0.
|
||||
*
|
||||
* <p>Simulation matches DS behavior without an FMS connected.
|
||||
*
|
||||
* @return time remaining in current match period (auto or teleop)
|
||||
* @see "HAL_GetMatchTime"
|
||||
|
||||
@@ -125,8 +125,8 @@ public class PowerDistributionFaults {
|
||||
case 21 -> Channel21BreakerFault;
|
||||
case 22 -> Channel22BreakerFault;
|
||||
case 23 -> Channel23BreakerFault;
|
||||
default -> throw new IndexOutOfBoundsException(
|
||||
"Power distribution fault channel out of bounds!");
|
||||
default ->
|
||||
throw new IndexOutOfBoundsException("Power distribution fault channel out of bounds!");
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -134,8 +134,8 @@ public class PowerDistributionStickyFaults {
|
||||
case 21 -> Channel21BreakerFault;
|
||||
case 22 -> Channel22BreakerFault;
|
||||
case 23 -> Channel23BreakerFault;
|
||||
default -> throw new IndexOutOfBoundsException(
|
||||
"Power distribution fault channel out of bounds!");
|
||||
default ->
|
||||
throw new IndexOutOfBoundsException("Power distribution fault channel out of bounds!");
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -32,15 +32,14 @@ public final class CANExceptionFactory {
|
||||
case NIRioStatus.kRioStatusSuccess -> {
|
||||
// Everything is ok... don't throw.
|
||||
}
|
||||
case ERR_CANSessionMux_InvalidBuffer,
|
||||
NIRioStatus.kRIOStatusBufferInvalidSize -> throw new CANInvalidBufferException();
|
||||
case ERR_CANSessionMux_MessageNotFound,
|
||||
NIRioStatus.kRIOStatusOperationTimedOut -> throw new CANMessageNotFoundException();
|
||||
case ERR_CANSessionMux_NotAllowed,
|
||||
NIRioStatus.kRIOStatusFeatureNotSupported -> throw new CANMessageNotAllowedException(
|
||||
"MessageID = " + messageID);
|
||||
case ERR_CANSessionMux_NotInitialized,
|
||||
NIRioStatus.kRIOStatusResourceNotInitialized -> throw new CANNotInitializedException();
|
||||
case ERR_CANSessionMux_InvalidBuffer, NIRioStatus.kRIOStatusBufferInvalidSize ->
|
||||
throw new CANInvalidBufferException();
|
||||
case ERR_CANSessionMux_MessageNotFound, NIRioStatus.kRIOStatusOperationTimedOut ->
|
||||
throw new CANMessageNotFoundException();
|
||||
case ERR_CANSessionMux_NotAllowed, NIRioStatus.kRIOStatusFeatureNotSupported ->
|
||||
throw new CANMessageNotAllowedException("MessageID = " + messageID);
|
||||
case ERR_CANSessionMux_NotInitialized, NIRioStatus.kRIOStatusResourceNotInitialized ->
|
||||
throw new CANNotInitializedException();
|
||||
default -> throw new UncleanStatusException("Fatal status code detected: " + status);
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user